From 1372848511cb896b80b51ed1a3e9606bd9816631 Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Fri, 10 Feb 2023 11:50:34 +0100 Subject: New upstream version 1.42.0 --- src/c-siphash/src/c-siphash.c | 110 +- src/c-siphash/src/c-siphash.h | 26 +- src/c-stdaux/src/c-stdaux-generic.h | 510 ++++ src/c-stdaux/src/c-stdaux-gnuc.h | 435 ++++ src/c-stdaux/src/c-stdaux-unix.h | 102 + src/c-stdaux/src/c-stdaux.h | 792 +------ src/core/NetworkManagerUtils.c | 73 +- src/core/devices/adsl/nm-atm-manager.c | 3 +- src/core/devices/adsl/nm-device-adsl.c | 12 +- src/core/devices/adsl/nm-device-adsl.h | 3 +- src/core/devices/bluetooth/nm-bluez-manager.c | 5 +- src/core/devices/bluetooth/nm-bluez-manager.h | 2 +- src/core/devices/bluetooth/nm-device-bt.h | 2 +- src/core/devices/nm-device-6lowpan.c | 39 +- src/core/devices/nm-device-6lowpan.h | 2 +- src/core/devices/nm-device-bond.c | 140 +- src/core/devices/nm-device-bond.h | 3 +- src/core/devices/nm-device-bridge.c | 11 +- src/core/devices/nm-device-bridge.h | 2 +- src/core/devices/nm-device-dummy.c | 2 +- src/core/devices/nm-device-dummy.h | 2 +- src/core/devices/nm-device-ethernet.c | 15 +- src/core/devices/nm-device-ethernet.h | 2 +- src/core/devices/nm-device-factory.c | 1 + src/core/devices/nm-device-factory.h | 5 +- src/core/devices/nm-device-generic.h | 2 +- src/core/devices/nm-device-infiniband.c | 8 +- src/core/devices/nm-device-infiniband.h | 2 +- src/core/devices/nm-device-ip-tunnel.c | 277 ++- src/core/devices/nm-device-ip-tunnel.h | 3 +- src/core/devices/nm-device-logging.h | 13 +- src/core/devices/nm-device-loopback.c | 166 ++ src/core/devices/nm-device-loopback.h | 27 + src/core/devices/nm-device-macsec.c | 39 +- src/core/devices/nm-device-macsec.h | 2 +- src/core/devices/nm-device-macvlan.c | 39 +- src/core/devices/nm-device-macvlan.h | 2 +- src/core/devices/nm-device-ppp.c | 2 +- src/core/devices/nm-device-ppp.h | 2 +- src/core/devices/nm-device-tun.c | 2 +- src/core/devices/nm-device-tun.h | 2 +- src/core/devices/nm-device-utils.c | 42 +- src/core/devices/nm-device-veth.c | 2 +- src/core/devices/nm-device-veth.h | 3 +- src/core/devices/nm-device-vlan.c | 58 +- src/core/devices/nm-device-vlan.h | 3 +- src/core/devices/nm-device-vrf.c | 2 +- src/core/devices/nm-device-vrf.h | 2 +- src/core/devices/nm-device-vxlan.c | 30 +- src/core/devices/nm-device-vxlan.h | 2 +- src/core/devices/nm-device-wireguard.c | 25 +- src/core/devices/nm-device-wireguard.h | 2 +- src/core/devices/nm-device-wpan.c | 2 +- src/core/devices/nm-device-wpan.h | 3 +- src/core/devices/nm-device.c | 353 +-- src/core/devices/nm-device.h | 28 +- src/core/devices/nm-lldp-listener.c | 332 +-- src/core/devices/ovs/nm-device-ovs-bridge.c | 16 +- src/core/devices/ovs/nm-device-ovs-bridge.h | 2 +- src/core/devices/ovs/nm-device-ovs-interface.h | 2 +- src/core/devices/ovs/nm-device-ovs-port.h | 2 +- src/core/devices/ovs/nm-ovs-factory.c | 3 +- src/core/devices/ovs/nm-ovsdb.c | 581 +++-- src/core/devices/ovs/nm-ovsdb.h | 18 +- src/core/devices/team/nm-device-team.c | 62 +- src/core/devices/team/nm-device-team.h | 3 +- src/core/devices/team/nm-team-factory.c | 2 +- src/core/devices/tests/test-lldp.c | 16 +- src/core/devices/wifi/nm-device-iwd-p2p.h | 2 +- src/core/devices/wifi/nm-device-iwd.c | 466 +++- src/core/devices/wifi/nm-device-iwd.h | 4 +- src/core/devices/wifi/nm-device-olpc-mesh.h | 2 +- src/core/devices/wifi/nm-device-wifi-p2p.c | 1 - src/core/devices/wifi/nm-device-wifi-p2p.h | 2 +- src/core/devices/wifi/nm-device-wifi.c | 49 +- src/core/devices/wifi/nm-device-wifi.h | 3 +- src/core/devices/wifi/nm-iwd-manager.c | 261 +- src/core/devices/wifi/nm-iwd-manager.h | 7 +- src/core/devices/wifi/nm-wifi-ap.h | 2 +- src/core/devices/wifi/nm-wifi-factory.c | 2 +- src/core/devices/wifi/nm-wifi-p2p-peer.h | 2 +- src/core/devices/wifi/nm-wifi-utils.c | 175 +- src/core/devices/wwan/nm-device-modem.c | 24 + src/core/devices/wwan/nm-device-modem.h | 2 +- src/core/devices/wwan/nm-modem-broadband.c | 13 +- src/core/devices/wwan/nm-modem-broadband.h | 2 +- src/core/devices/wwan/nm-modem-manager.h | 2 +- src/core/devices/wwan/nm-modem-ofono.c | 740 ++++-- src/core/devices/wwan/nm-modem-ofono.h | 5 +- src/core/devices/wwan/nm-modem.c | 10 +- src/core/devices/wwan/nm-modem.h | 2 +- src/core/devices/wwan/nm-wwan-factory.c | 2 +- src/core/dhcp/nm-dhcp-client.c | 229 +- src/core/dhcp/nm-dhcp-client.h | 23 +- src/core/dhcp/nm-dhcp-dhclient.c | 2 +- src/core/dhcp/nm-dhcp-dhcpcanon.c | 2 +- src/core/dhcp/nm-dhcp-dhcpcd.c | 3 +- src/core/dhcp/nm-dhcp-listener.h | 2 +- src/core/dhcp/nm-dhcp-manager.h | 2 +- src/core/dhcp/nm-dhcp-nettools.c | 60 +- src/core/dhcp/nm-dhcp-options.c | 4 +- src/core/dhcp/nm-dhcp-systemd.c | 12 +- src/core/dhcp/nm-dhcp-utils.c | 30 +- src/core/dhcp/tests/test-dhcp-utils.c | 41 +- src/core/dns/nm-dns-dnsmasq.c | 34 +- src/core/dns/nm-dns-dnsmasq.h | 3 +- src/core/dns/nm-dns-manager.c | 128 +- src/core/dns/nm-dns-manager.h | 12 +- src/core/dns/nm-dns-plugin.h | 2 +- src/core/dns/nm-dns-systemd-resolved.c | 216 +- src/core/dns/nm-dns-systemd-resolved.h | 2 +- src/core/dnsmasq/nm-dnsmasq-manager.c | 12 +- src/core/dnsmasq/nm-dnsmasq-manager.h | 2 +- src/core/dnsmasq/nm-dnsmasq-utils.c | 6 +- src/core/main-utils.c | 31 +- src/core/meson.build | 5 + src/core/ndisc/nm-fake-ndisc.c | 8 +- src/core/ndisc/nm-fake-ndisc.h | 2 +- src/core/ndisc/nm-lndp-ndisc.c | 16 +- src/core/ndisc/nm-lndp-ndisc.h | 2 +- src/core/ndisc/nm-ndisc.c | 189 +- src/core/ndisc/nm-ndisc.h | 10 +- src/core/nm-act-request.h | 3 +- src/core/nm-active-connection.c | 32 +- src/core/nm-active-connection.h | 2 +- src/core/nm-audit-manager.h | 2 +- src/core/nm-auth-manager.c | 2 +- src/core/nm-auth-manager.h | 2 +- src/core/nm-bond-manager.c | 967 ++++++++ src/core/nm-bond-manager.h | 32 + src/core/nm-checkpoint.c | 2 +- src/core/nm-checkpoint.h | 2 +- src/core/nm-config-data.c | 19 +- src/core/nm-config-data.h | 3 +- src/core/nm-config.c | 30 +- src/core/nm-config.h | 2 +- src/core/nm-connectivity.c | 8 +- src/core/nm-connectivity.h | 2 +- src/core/nm-core-utils.c | 36 +- src/core/nm-core-utils.h | 2 +- src/core/nm-dbus-manager.c | 2 +- src/core/nm-dbus-manager.h | 3 +- src/core/nm-dbus-object.h | 3 +- src/core/nm-dbus-utils.c | 2 +- src/core/nm-default-daemon.h | 1 + src/core/nm-dhcp-config.c | 4 +- src/core/nm-dhcp-config.h | 3 +- src/core/nm-dispatcher.c | 22 +- src/core/nm-dispatcher.h | 3 +- src/core/nm-firewall-utils.c | 252 +- src/core/nm-firewall-utils.h | 6 + src/core/nm-firewalld-manager.h | 2 +- src/core/nm-hostname-manager.c | 83 +- src/core/nm-hostname-manager.h | 18 +- src/core/nm-ip-config.c | 178 +- src/core/nm-ip-config.h | 4 +- src/core/nm-keep-alive.h | 2 +- src/core/nm-l3-config-data.c | 282 ++- src/core/nm-l3-config-data.h | 17 +- src/core/nm-l3-ipv4ll.c | 46 +- src/core/nm-l3-ipv4ll.h | 2 +- src/core/nm-l3-ipv6ll.c | 15 +- src/core/nm-l3cfg.c | 287 ++- src/core/nm-l3cfg.h | 14 +- src/core/nm-manager.c | 109 +- src/core/nm-manager.h | 3 +- src/core/nm-netns.c | 595 ++++- src/core/nm-netns.h | 10 +- src/core/nm-pacrunner-manager.c | 6 +- src/core/nm-pacrunner-manager.h | 2 +- src/core/nm-policy.c | 75 +- src/core/nm-policy.h | 2 +- src/core/nm-rfkill-manager.h | 2 +- src/core/nm-session-monitor.h | 2 +- src/core/nm-sleep-monitor.c | 31 +- src/core/nm-sleep-monitor.h | 3 +- src/core/nm-test-utils-core.h | 24 +- src/core/platform/nm-fake-platform.c | 47 +- src/core/platform/nm-fake-platform.h | 2 +- src/core/platform/tests/meson.build | 2 +- src/core/platform/tests/monitor.c | 165 +- src/core/platform/tests/test-address.c | 6 +- src/core/platform/tests/test-cleanup.c | 4 +- src/core/platform/tests/test-common.c | 985 +++++++- src/core/platform/tests/test-common.h | 190 +- src/core/platform/tests/test-link.c | 275 ++- src/core/platform/tests/test-platform-general.c | 932 ++++---- src/core/platform/tests/test-route.c | 397 +++- src/core/platform/tests/test-tc.c | 8 +- src/core/ppp/nm-ppp-manager.c | 5 +- src/core/ppp/nm-ppp-mgr.c | 2 +- src/core/settings/nm-agent-manager.c | 6 +- src/core/settings/nm-agent-manager.h | 2 +- src/core/settings/nm-secret-agent.h | 2 +- src/core/settings/nm-settings-connection.h | 2 +- src/core/settings/nm-settings-plugin.h | 2 +- src/core/settings/nm-settings-storage.h | 2 +- src/core/settings/nm-settings.c | 4 +- src/core/settings/nm-settings.h | 2 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h | 2 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 92 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-storage.h | 2 +- .../settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c | 15 +- .../settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h | 2 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 22 +- .../ifcfg-Vlan_test-vlan-interface.cexpected | 1 + .../ifcfg-rh/tests/network-scripts/ifcfg-netmask-1 | 3 + .../network-scripts/ifcfg-netmask-1.cexpected | 3 + .../network-scripts/ifcfg-test-vlan-interface | 1 + .../network-scripts/ifcfg-test-wired-static-routes | 2 +- .../network-scripts/route-test-wired-static-routes | 2 +- .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 41 +- .../plugins/ifupdown/nms-ifupdown-parser.c | 6 +- .../plugins/ifupdown/nms-ifupdown-plugin.h | 2 +- .../settings/plugins/keyfile/nms-keyfile-plugin.h | 2 +- .../settings/plugins/keyfile/nms-keyfile-storage.h | 2 +- .../keyfile/tests/keyfiles/Test_Wired_Connection | 2 +- .../plugins/keyfile/tests/test-keyfile-settings.c | 4 +- src/core/supplicant/nm-supplicant-config.c | 32 +- src/core/supplicant/nm-supplicant-config.h | 2 +- src/core/supplicant/nm-supplicant-interface.c | 4 +- src/core/supplicant/nm-supplicant-interface.h | 2 +- src/core/supplicant/nm-supplicant-manager.c | 4 +- src/core/supplicant/nm-supplicant-manager.h | 2 +- .../supplicant/nm-supplicant-settings-verify.c | 18 +- src/core/tests/config/global-dns-good.conf | 13 + src/core/tests/config/global-dns-options.conf | 5 + src/core/tests/config/nm-test-device.h | 3 +- src/core/tests/config/test-config.c | 22 +- src/core/tests/test-core-with-expect.c | 28 +- src/core/tests/test-core.c | 31 +- src/core/tests/test-l3cfg.c | 37 +- src/core/tests/test-systemd.c | 16 - src/core/vpn/nm-vpn-connection.c | 81 +- src/core/vpn/nm-vpn-connection.h | 2 +- src/core/vpn/nm-vpn-manager.h | 3 +- src/libnm-base/nm-base.h | 14 + src/libnm-base/nm-ethtool-base.c | 12 +- src/libnm-base/nm-ethtool-utils-base.h | 2 +- src/libnm-client-aux-extern/nm-libnm-aux.c | 61 + src/libnm-client-aux-extern/nm-libnm-aux.h | 4 + src/libnm-client-impl/libnm.ver | 41 + src/libnm-client-impl/meson.build | 1 + src/libnm-client-impl/nm-client.c | 441 +++- src/libnm-client-impl/nm-conn-utils.c | 22 +- src/libnm-client-impl/nm-default-libnm.h | 1 + src/libnm-client-impl/nm-device-6lowpan.c | 2 +- src/libnm-client-impl/nm-device-dummy.c | 3 - src/libnm-client-impl/nm-device-loopback.c | 81 + src/libnm-client-impl/nm-device-macsec.c | 2 +- src/libnm-client-impl/nm-device-veth.c | 2 +- src/libnm-client-impl/nm-device-vxlan.c | 4 +- src/libnm-client-impl/nm-device-wifi-p2p.c | 2 +- src/libnm-client-impl/nm-device.c | 6 +- src/libnm-client-impl/nm-dhcp-config.c | 2 +- src/libnm-client-impl/nm-dhcp4-config.h | 2 +- src/libnm-client-impl/nm-dhcp6-config.h | 2 +- src/libnm-client-impl/nm-dns-manager.c | 2 +- src/libnm-client-impl/nm-ip-config.c | 4 +- src/libnm-client-impl/nm-ip4-config.h | 2 +- src/libnm-client-impl/nm-ip6-config.h | 2 +- src/libnm-client-impl/nm-libnm-utils.c | 163 +- src/libnm-client-impl/nm-libnm-utils.h | 14 +- src/libnm-client-impl/nm-object.c | 4 +- src/libnm-client-impl/nm-property-infos-dbus.xml | 53 - .../nm-property-infos-ifcfg-rh.xml | 53 - .../nm-property-infos-keyfile.xml | 53 - src/libnm-client-impl/nm-property-infos-nmcli.xml | 588 ----- src/libnm-client-impl/nm-remote-connection.c | 2 +- src/libnm-client-impl/nm-secret-agent-old.c | 8 +- src/libnm-client-impl/nm-settings-docs-gir.xml | 404 ---- src/libnm-client-impl/tests/test-libnm.c | 2 +- src/libnm-client-impl/tests/test-nm-client.c | 406 +++- src/libnm-client-public/NetworkManager.h | 5 + src/libnm-client-public/meson.build | 1 + src/libnm-client-public/nm-autoptr.h | 2 + src/libnm-client-public/nm-client.h | 36 +- src/libnm-client-public/nm-device-6lowpan.h | 2 +- src/libnm-client-public/nm-device-dummy.h | 2 +- src/libnm-client-public/nm-device-loopback.h | 41 + src/libnm-client-public/nm-device-macsec.h | 4 +- src/libnm-client-public/nm-device-macvlan.h | 2 +- src/libnm-client-public/nm-device-ovs-bridge.h | 1 + src/libnm-client-public/nm-device-ovs-port.h | 1 + src/libnm-client-public/nm-device-team.h | 5 +- src/libnm-client-public/nm-device-tun.h | 2 +- src/libnm-client-public/nm-device-veth.h | 2 +- src/libnm-client-public/nm-device-vxlan.h | 6 +- src/libnm-client-public/nm-device-wifi-p2p.h | 4 +- src/libnm-client-public/nm-ethtool-utils.h | 2 +- src/libnm-core-aux-extern/nm-dispatcher-api.h | 1 + src/libnm-core-aux-intern/nm-auth-subject.h | 2 +- src/libnm-core-aux-intern/nm-libnm-core-utils.c | 223 +- src/libnm-core-aux-intern/nm-libnm-core-utils.h | 34 + .../gen-metadata-nm-settings-libnm-core.c | 160 ++ .../gen-metadata-nm-settings-libnm-core.xml.in | 2492 ++++++++++++++++++++ src/libnm-core-impl/meson.build | 52 +- src/libnm-core-impl/nm-connection.c | 162 +- src/libnm-core-impl/nm-dbus-utils.c | 259 -- src/libnm-core-impl/nm-default-libnm-core.h | 1 + src/libnm-core-impl/nm-keyfile.c | 203 +- src/libnm-core-impl/nm-meta-setting-base-impl.c | 162 +- src/libnm-core-impl/nm-setting-6lowpan.c | 4 +- src/libnm-core-impl/nm-setting-8021x.c | 133 +- src/libnm-core-impl/nm-setting-adsl.c | 4 +- src/libnm-core-impl/nm-setting-bond.c | 49 +- src/libnm-core-impl/nm-setting-bridge.c | 39 +- src/libnm-core-impl/nm-setting-cdma.c | 4 +- src/libnm-core-impl/nm-setting-connection.c | 26 +- src/libnm-core-impl/nm-setting-ethtool.c | 8 +- src/libnm-core-impl/nm-setting-gsm.c | 13 +- src/libnm-core-impl/nm-setting-hostname.c | 4 +- src/libnm-core-impl/nm-setting-infiniband.c | 3 +- src/libnm-core-impl/nm-setting-ip-config.c | 488 ++-- src/libnm-core-impl/nm-setting-ip-tunnel.c | 65 +- src/libnm-core-impl/nm-setting-ip4-config.c | 191 +- src/libnm-core-impl/nm-setting-ip6-config.c | 116 +- src/libnm-core-impl/nm-setting-loopback.c | 212 ++ src/libnm-core-impl/nm-setting-macsec.c | 4 +- src/libnm-core-impl/nm-setting-ovs-bridge.c | 2 +- src/libnm-core-impl/nm-setting-ovs-dpdk.c | 116 +- src/libnm-core-impl/nm-setting-ovs-external-ids.c | 122 +- src/libnm-core-impl/nm-setting-ovs-interface.c | 42 +- src/libnm-core-impl/nm-setting-ovs-other-config.c | 403 ++++ src/libnm-core-impl/nm-setting-ovs-port.c | 364 ++- src/libnm-core-impl/nm-setting-pppoe.c | 4 +- src/libnm-core-impl/nm-setting-private.h | 115 +- src/libnm-core-impl/nm-setting-serial.c | 3 +- src/libnm-core-impl/nm-setting-sriov.c | 2 +- src/libnm-core-impl/nm-setting-tc-config.c | 39 +- src/libnm-core-impl/nm-setting-team.c | 7 +- src/libnm-core-impl/nm-setting-vlan.c | 68 +- src/libnm-core-impl/nm-setting-vpn.c | 8 +- src/libnm-core-impl/nm-setting-vxlan.c | 4 +- src/libnm-core-impl/nm-setting-wimax.c | 6 +- src/libnm-core-impl/nm-setting-wired.c | 38 +- src/libnm-core-impl/nm-setting-wireguard.c | 36 +- src/libnm-core-impl/nm-setting-wireless-security.c | 16 +- src/libnm-core-impl/nm-setting-wireless.c | 57 +- src/libnm-core-impl/nm-setting-wpan.c | 12 +- src/libnm-core-impl/nm-setting.c | 397 +++- src/libnm-core-impl/nm-simple-connection.c | 14 +- src/libnm-core-impl/nm-team-utils.c | 4 +- src/libnm-core-impl/nm-utils-private.h | 6 +- src/libnm-core-impl/nm-utils.c | 259 +- src/libnm-core-impl/nm-vpn-plugin-info.c | 12 +- src/libnm-core-impl/tests/test-general.c | 575 ++++- src/libnm-core-impl/tests/test-setting.c | 170 +- src/libnm-core-intern/nm-core-internal.h | 78 +- src/libnm-core-intern/nm-meta-setting-base-impl.h | 2 + src/libnm-core-public/meson.build | 2 + src/libnm-core-public/nm-connection.h | 1 - src/libnm-core-public/nm-core-types.h | 2 + src/libnm-core-public/nm-dbus-interface.h | 23 +- src/libnm-core-public/nm-dbus-types.xml | 27 +- src/libnm-core-public/nm-setting-6lowpan.h | 4 +- src/libnm-core-public/nm-setting-8021x.h | 24 +- src/libnm-core-public/nm-setting-bond.h | 1 + src/libnm-core-public/nm-setting-connection.h | 2 +- src/libnm-core-public/nm-setting-hostname.h | 2 +- src/libnm-core-public/nm-setting-ip-config.h | 6 +- src/libnm-core-public/nm-setting-ip-tunnel.h | 7 +- src/libnm-core-public/nm-setting-ip4-config.h | 2 +- src/libnm-core-public/nm-setting-loopback.h | 44 + src/libnm-core-public/nm-setting-ovs-bridge.h | 2 +- src/libnm-core-public/nm-setting-ovs-dpdk.h | 10 +- src/libnm-core-public/nm-setting-ovs-interface.h | 5 + .../nm-setting-ovs-other-config.h | 63 + src/libnm-core-public/nm-setting-ovs-port.h | 15 + src/libnm-core-public/nm-setting-sriov.h | 2 +- src/libnm-core-public/nm-setting-tc-config.h | 4 +- src/libnm-core-public/nm-setting-vlan.h | 3 + src/libnm-core-public/nm-setting-vpn.h | 2 +- src/libnm-core-public/nm-setting-wpan.h | 12 +- src/libnm-core-public/nm-setting.h | 19 + src/libnm-core-public/nm-utils.h | 12 +- src/libnm-core-public/nm-version-macros.h | 73 +- src/libnm-core-public/nm-version-macros.h.in | 69 +- src/libnm-core-public/nm-version.h | 14 +- src/libnm-core-public/nm-vpn-plugin-info.h | 2 +- src/libnm-crypto/nm-crypto-nss.c | 13 +- src/libnm-glib-aux/meson.build | 2 + src/libnm-glib-aux/nm-dbus-aux.c | 232 +- src/libnm-glib-aux/nm-dbus-aux.h | 46 + src/libnm-glib-aux/nm-dedup-multi.c | 15 +- src/libnm-glib-aux/nm-dedup-multi.h | 20 +- src/libnm-glib-aux/nm-default-glib.h | 1 + src/libnm-glib-aux/nm-glib.h | 52 +- src/libnm-glib-aux/nm-hash-utils.c | 2 +- src/libnm-glib-aux/nm-hash-utils.h | 57 +- src/libnm-glib-aux/nm-inet-utils.c | 529 +++++ src/libnm-glib-aux/nm-inet-utils.h | 389 +++ src/libnm-glib-aux/nm-io-utils.c | 52 +- src/libnm-glib-aux/nm-io-utils.h | 45 + src/libnm-glib-aux/nm-macros-internal.h | 236 +- src/libnm-glib-aux/nm-prioq.c | 323 +++ src/libnm-glib-aux/nm-prioq.h | 71 + src/libnm-glib-aux/nm-ref-string.c | 3 +- src/libnm-glib-aux/nm-ref-string.h | 19 +- src/libnm-glib-aux/nm-secret-utils.c | 16 +- src/libnm-glib-aux/nm-shared-utils.c | 675 +----- src/libnm-glib-aux/nm-shared-utils.h | 692 ++---- src/libnm-glib-aux/nm-str-buf.h | 23 +- src/libnm-glib-aux/nm-test-utils.h | 256 +- src/libnm-glib-aux/nm-time-utils.c | 52 +- src/libnm-glib-aux/nm-time-utils.h | 36 +- src/libnm-glib-aux/nm-uuid.c | 92 +- src/libnm-glib-aux/nm-uuid.h | 21 +- src/libnm-glib-aux/tests/test-shared-general.c | 207 +- src/libnm-lldp/meson.build | 18 + src/libnm-lldp/nm-lldp-neighbor.c | 842 +++++++ src/libnm-lldp/nm-lldp-neighbor.h | 85 + src/libnm-lldp/nm-lldp-network.c | 74 + src/libnm-lldp/nm-lldp-network.h | 9 + src/libnm-lldp/nm-lldp-rx-internal.h | 55 + src/libnm-lldp/nm-lldp-rx.c | 469 ++++ src/libnm-lldp/nm-lldp-rx.h | 124 + src/libnm-lldp/nm-lldp.h | 110 + src/libnm-platform/meson.build | 2 + src/libnm-platform/nm-linux-platform.c | 588 +++-- src/libnm-platform/nm-linux-platform.h | 9 +- src/libnm-platform/nm-netlink.c | 7 +- src/libnm-platform/nm-netlink.h | 9 +- src/libnm-platform/nm-platform.c | 1895 +++++++-------- src/libnm-platform/nm-platform.h | 634 ++--- src/libnm-platform/nmp-base.c | 68 + src/libnm-platform/nmp-base.h | 25 + src/libnm-platform/nmp-global-tracker.c | 6 +- src/libnm-platform/nmp-netns.c | 56 +- src/libnm-platform/nmp-netns.h | 2 +- src/libnm-platform/nmp-object.c | 680 ++++-- src/libnm-platform/nmp-object.h | 85 +- src/libnm-platform/nmp-plobj.c | 637 +++++ src/libnm-platform/nmp-plobj.h | 296 +++ src/libnm-platform/tests/test-nm-platform.c | 113 +- src/libnm-platform/wifi/nm-wifi-utils-nl80211.h | 2 +- src/libnm-platform/wifi/nm-wifi-utils-wext.c | 2 +- src/libnm-platform/wifi/nm-wifi-utils-wext.h | 2 +- src/libnm-platform/wifi/nm-wifi-utils.h | 2 +- src/libnm-platform/wpan/nm-wpan-utils.h | 2 +- src/libnm-std-aux/nm-default-std.h | 2 +- src/libnm-std-aux/nm-std-aux.h | 384 ++- src/libnm-systemd-core/meson.build | 3 - src/libnm-systemd-core/nm-sd.h | 1 - .../src/libsystemd-network/dhcp6-option.c | 198 +- .../src/libsystemd-network/dhcp6-option.h | 12 +- .../src/libsystemd-network/dhcp6-protocol.c | 11 + .../src/libsystemd-network/dhcp6-protocol.h | 1 + .../src/libsystemd-network/lldp-neighbor.c | 798 ------- .../src/libsystemd-network/lldp-neighbor.h | 92 - .../src/libsystemd-network/lldp-network.c | 72 - .../src/libsystemd-network/lldp-network.h | 6 - .../src/libsystemd-network/lldp-rx-internal.h | 48 - .../src/libsystemd-network/sd-dhcp6-client.c | 160 +- .../src/libsystemd-network/sd-dhcp6-lease.c | 4 +- .../src/libsystemd-network/sd-lldp-rx.c | 528 ----- .../src/libsystemd/sd-event/event-source.h | 1 + .../src/libsystemd/sd-event/sd-event.c | 134 +- src/libnm-systemd-core/src/systemd/sd-event.h | 3 + src/libnm-systemd-core/src/systemd/sd-lldp-rx.h | 109 - src/libnm-systemd-core/src/systemd/sd-lldp.h | 123 - .../sd-adapt-shared/user-util.h | 3 - src/libnm-systemd-shared/src/basic/cgroup-util.h | 1 + src/libnm-systemd-shared/src/basic/env-file.c | 4 +- src/libnm-systemd-shared/src/basic/env-util.c | 12 + src/libnm-systemd-shared/src/basic/env-util.h | 2 + src/libnm-systemd-shared/src/basic/errno-util.h | 31 +- src/libnm-systemd-shared/src/basic/fd-util.c | 19 +- src/libnm-systemd-shared/src/basic/fd-util.h | 3 +- src/libnm-systemd-shared/src/basic/fs-util.c | 77 +- src/libnm-systemd-shared/src/basic/fs-util.h | 22 +- src/libnm-systemd-shared/src/basic/glyph-util.c | 2 + src/libnm-systemd-shared/src/basic/glyph-util.h | 1 + src/libnm-systemd-shared/src/basic/hashmap.c | 9 +- src/libnm-systemd-shared/src/basic/in-addr-util.c | 53 +- src/libnm-systemd-shared/src/basic/in-addr-util.h | 2 + src/libnm-systemd-shared/src/basic/io-util.c | 6 +- src/libnm-systemd-shared/src/basic/log.h | 39 + src/libnm-systemd-shared/src/basic/missing_stat.h | 2 + src/libnm-systemd-shared/src/basic/parse-util.c | 68 +- src/libnm-systemd-shared/src/basic/parse-util.h | 6 +- src/libnm-systemd-shared/src/basic/path-util.c | 135 +- src/libnm-systemd-shared/src/basic/path-util.h | 7 +- src/libnm-systemd-shared/src/basic/process-util.c | 2 +- src/libnm-systemd-shared/src/basic/random-util.c | 15 +- src/libnm-systemd-shared/src/basic/ratelimit.c | 7 + src/libnm-systemd-shared/src/basic/ratelimit.h | 3 +- src/libnm-systemd-shared/src/basic/stat-util.c | 26 + src/libnm-systemd-shared/src/basic/stat-util.h | 3 + src/libnm-systemd-shared/src/basic/stdio-util.h | 14 +- src/libnm-systemd-shared/src/basic/string-util.c | 15 +- src/libnm-systemd-shared/src/basic/string-util.h | 9 +- src/libnm-systemd-shared/src/basic/strv.c | 27 +- src/libnm-systemd-shared/src/basic/strv.h | 2 +- src/libnm-systemd-shared/src/basic/time-util.c | 3 +- src/libnm-systemd-shared/src/basic/time-util.h | 9 +- src/libnm-systemd-shared/src/basic/tmpfile-util.c | 129 +- src/libnm-systemd-shared/src/basic/user-util.h | 150 ++ .../src/fundamental/macro-fundamental.h | 19 +- src/libnm-systemd-shared/src/fundamental/sha256.c | 16 +- src/libnm-systemd-shared/src/fundamental/sha256.h | 6 +- src/libnmc-base/nm-polkit-listener.h | 2 +- src/libnmc-base/nm-secret-agent-simple.h | 2 +- src/libnmc-base/nm-vpn-helpers.c | 14 +- src/libnmc-setting/meson.build | 5 +- src/libnmc-setting/nm-meta-setting-access.c | 2 +- src/libnmc-setting/nm-meta-setting-base-impl.c | 162 +- src/libnmc-setting/nm-meta-setting-base-impl.h | 2 + src/libnmc-setting/nm-meta-setting-desc.c | 540 +++-- src/libnmc-setting/nm-meta-setting-desc.h | 17 + src/libnmc-setting/settings-docs-input.xml | 938 -------- src/libnmc-setting/settings-docs.h | 36 +- src/libnmc-setting/settings-docs.h.in | 36 +- src/libnmt-newt/nmt-newt-button-box.h | 2 +- src/libnmt-newt/nmt-newt-button.h | 2 +- src/libnmt-newt/nmt-newt-checkbox.h | 2 +- src/libnmt-newt/nmt-newt-component.h | 2 +- src/libnmt-newt/nmt-newt-container.h | 2 +- src/libnmt-newt/nmt-newt-entry-numeric.h | 2 +- src/libnmt-newt/nmt-newt-entry.h | 3 +- src/libnmt-newt/nmt-newt-form.c | 26 +- src/libnmt-newt/nmt-newt-form.h | 2 +- src/libnmt-newt/nmt-newt-grid.c | 14 +- src/libnmt-newt/nmt-newt-grid.h | 2 +- src/libnmt-newt/nmt-newt-label.h | 3 +- src/libnmt-newt/nmt-newt-listbox.h | 2 +- src/libnmt-newt/nmt-newt-popup.c | 10 +- src/libnmt-newt/nmt-newt-popup.h | 3 +- src/libnmt-newt/nmt-newt-section.h | 2 +- src/libnmt-newt/nmt-newt-separator.h | 2 +- src/libnmt-newt/nmt-newt-stack.c | 6 +- src/libnmt-newt/nmt-newt-stack.h | 3 +- src/libnmt-newt/nmt-newt-textbox.h | 2 +- src/libnmt-newt/nmt-newt-toggle-button.h | 2 +- src/libnmt-newt/nmt-newt-widget.h | 2 +- src/meson.build | 1 + src/nm-cloud-setup/main.c | 34 +- src/nm-cloud-setup/nm-cloud-setup-utils.c | 4 +- src/nm-cloud-setup/nm-http-client.h | 3 +- src/nm-cloud-setup/nmcs-provider-aliyun.c | 26 +- src/nm-cloud-setup/nmcs-provider-aliyun.h | 2 +- src/nm-cloud-setup/nmcs-provider-azure.c | 6 +- src/nm-cloud-setup/nmcs-provider-azure.h | 2 +- src/nm-cloud-setup/nmcs-provider-ec2.c | 12 +- src/nm-cloud-setup/nmcs-provider-ec2.h | 2 +- src/nm-cloud-setup/nmcs-provider-gcp.h | 2 +- src/nm-cloud-setup/nmcs-provider.h | 2 +- src/nm-compat-headers/linux/if_addr.h | 16 + src/nm-dispatcher/nm-dispatcher.c | 32 +- src/nm-initrd-generator/nm-initrd-generator.c | 20 +- src/nm-initrd-generator/nm-initrd-generator.h | 4 +- src/nm-initrd-generator/nmi-cmdline-reader.c | 26 +- src/nm-initrd-generator/nmi-dt-reader.c | 4 +- src/nm-initrd-generator/nmi-ibft-reader.c | 35 +- .../tests/test-cmdline-reader.c | 13 +- src/nm-online/nm-online.c | 10 +- src/nmcli/agent.c | 40 +- src/nmcli/common.c | 104 +- src/nmcli/connections.c | 1139 ++++----- src/nmcli/connections.h | 2 +- src/nmcli/devices.c | 444 ++-- src/nmcli/devices.h | 2 +- src/nmcli/gen-metadata-nm-settings-nmcli.c | 71 + src/nmcli/gen-metadata-nm-settings-nmcli.xml.in | 1183 ++++++++++ src/nmcli/general.c | 300 ++- src/nmcli/generate-docs-nm-settings-nmcli.c | 71 - src/nmcli/generate-docs-nm-settings-nmcli.xml | 1160 --------- src/nmcli/generate-docs-nm-settings-nmcli.xml.in | 1160 --------- src/nmcli/meson.build | 23 +- src/nmcli/nmcli.c | 22 +- src/nmcli/polkit-agent.c | 9 +- src/nmcli/settings.c | 39 +- src/nmcli/utils.c | 164 +- src/nmcli/utils.h | 44 +- src/nmtui/meson.build | 2 + src/nmtui/nm-editor-bindings.c | 95 +- src/nmtui/nm-editor-bindings.h | 11 + src/nmtui/nm-editor-utils.c | 8 + src/nmtui/nmt-8021x-fields.c | 693 ++++++ src/nmtui/nmt-8021x-fields.h | 29 + src/nmtui/nmt-address-list.h | 2 +- src/nmtui/nmt-connect-connection-list.c | 1 + src/nmtui/nmt-connect-connection-list.h | 6 +- src/nmtui/nmt-device-entry.h | 2 +- src/nmtui/nmt-edit-connection-list.h | 2 +- src/nmtui/nmt-editor-grid.c | 12 +- src/nmtui/nmt-editor-grid.h | 2 +- src/nmtui/nmt-editor-page-device.h | 2 +- src/nmtui/nmt-editor-page.h | 2 +- src/nmtui/nmt-editor-section.h | 2 +- src/nmtui/nmt-editor.c | 3 + src/nmtui/nmt-editor.h | 2 +- src/nmtui/nmt-ip-entry.c | 4 +- src/nmtui/nmt-ip-entry.h | 2 +- src/nmtui/nmt-mac-entry.h | 2 +- src/nmtui/nmt-mtu-entry.h | 2 +- src/nmtui/nmt-page-bond-port.h | 2 +- src/nmtui/nmt-page-bond.h | 2 +- src/nmtui/nmt-page-bridge-port.h | 2 +- src/nmtui/nmt-page-bridge.h | 2 +- src/nmtui/nmt-page-dsl.h | 2 +- src/nmtui/nmt-page-ethernet.c | 90 +- src/nmtui/nmt-page-ethernet.h | 13 +- src/nmtui/nmt-page-infiniband.h | 2 +- src/nmtui/nmt-page-ip-tunnel.h | 2 +- src/nmtui/nmt-page-ip4.h | 2 +- src/nmtui/nmt-page-ip6.h | 2 +- src/nmtui/nmt-page-macsec.c | 196 ++ src/nmtui/nmt-page-macsec.h | 28 + src/nmtui/nmt-page-ppp.h | 2 +- src/nmtui/nmt-page-team-port.h | 2 +- src/nmtui/nmt-page-team.h | 2 +- src/nmtui/nmt-page-vlan.h | 2 +- src/nmtui/nmt-page-wifi.c | 50 +- src/nmtui/nmt-page-wifi.h | 2 +- src/nmtui/nmt-page-wireguard.h | 2 +- src/nmtui/nmt-password-dialog.c | 2 - src/nmtui/nmt-password-dialog.h | 2 +- src/nmtui/nmt-password-fields.c | 130 +- src/nmtui/nmt-password-fields.h | 7 +- src/nmtui/nmt-route-editor.h | 2 +- src/nmtui/nmt-route-entry.h | 2 +- src/nmtui/nmt-route-table.h | 2 +- src/nmtui/nmt-slave-list.h | 3 +- src/nmtui/nmt-widget-list.h | 2 +- src/nmtui/nmt-wireguard-peer-editor.h | 2 +- src/nmtui/nmt-wireguard-peer-list.h | 2 +- src/nmtui/nmtui-connect.c | 11 +- src/nmtui/nmtui-edit.c | 11 +- src/nmtui/nmtui-hostname.c | 2 +- src/nmtui/nmtui.c | 7 +- src/tests/client/meson.build | 4 + .../test-client.check-on-disk/test_003.expected | 708 +++--- .../test-client.check-on-disk/test_004.expected | 900 ++++--- src/tests/client/test-client.py | 328 ++- src/tests/client/test-client.sh | 61 +- 636 files changed, 33133 insertions(+), 18470 deletions(-) create mode 100644 src/c-stdaux/src/c-stdaux-generic.h create mode 100644 src/c-stdaux/src/c-stdaux-gnuc.h create mode 100644 src/c-stdaux/src/c-stdaux-unix.h create mode 100644 src/core/devices/nm-device-loopback.c create mode 100644 src/core/devices/nm-device-loopback.h create mode 100644 src/core/nm-bond-manager.c create mode 100644 src/core/nm-bond-manager.h create mode 100644 src/core/tests/config/global-dns-good.conf create mode 100644 src/core/tests/config/global-dns-options.conf create mode 100644 src/libnm-client-impl/nm-device-loopback.c delete mode 100644 src/libnm-client-impl/nm-property-infos-dbus.xml delete mode 100644 src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml delete mode 100644 src/libnm-client-impl/nm-property-infos-keyfile.xml delete mode 100644 src/libnm-client-impl/nm-property-infos-nmcli.xml delete mode 100644 src/libnm-client-impl/nm-settings-docs-gir.xml create mode 100644 src/libnm-client-public/nm-device-loopback.h create mode 100644 src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.c create mode 100644 src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in delete mode 100644 src/libnm-core-impl/nm-dbus-utils.c create mode 100644 src/libnm-core-impl/nm-setting-loopback.c create mode 100644 src/libnm-core-impl/nm-setting-ovs-other-config.c create mode 100644 src/libnm-core-public/nm-setting-loopback.h create mode 100644 src/libnm-core-public/nm-setting-ovs-other-config.h create mode 100644 src/libnm-glib-aux/nm-inet-utils.c create mode 100644 src/libnm-glib-aux/nm-inet-utils.h create mode 100644 src/libnm-glib-aux/nm-prioq.c create mode 100644 src/libnm-glib-aux/nm-prioq.h create mode 100644 src/libnm-lldp/meson.build create mode 100644 src/libnm-lldp/nm-lldp-neighbor.c create mode 100644 src/libnm-lldp/nm-lldp-neighbor.h create mode 100644 src/libnm-lldp/nm-lldp-network.c create mode 100644 src/libnm-lldp/nm-lldp-network.h create mode 100644 src/libnm-lldp/nm-lldp-rx-internal.h create mode 100644 src/libnm-lldp/nm-lldp-rx.c create mode 100644 src/libnm-lldp/nm-lldp-rx.h create mode 100644 src/libnm-lldp/nm-lldp.h create mode 100644 src/libnm-platform/nmp-base.c create mode 100644 src/libnm-platform/nmp-plobj.c create mode 100644 src/libnm-platform/nmp-plobj.h delete mode 100644 src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c delete mode 100644 src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.h delete mode 100644 src/libnm-systemd-core/src/libsystemd-network/lldp-network.c delete mode 100644 src/libnm-systemd-core/src/libsystemd-network/lldp-network.h delete mode 100644 src/libnm-systemd-core/src/libsystemd-network/lldp-rx-internal.h delete mode 100644 src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c delete mode 100644 src/libnm-systemd-core/src/systemd/sd-lldp-rx.h delete mode 100644 src/libnm-systemd-core/src/systemd/sd-lldp.h delete mode 100644 src/libnm-systemd-shared/sd-adapt-shared/user-util.h create mode 100644 src/libnm-systemd-shared/src/basic/user-util.h delete mode 100644 src/libnmc-setting/settings-docs-input.xml create mode 100644 src/nm-compat-headers/linux/if_addr.h create mode 100644 src/nmcli/gen-metadata-nm-settings-nmcli.c create mode 100644 src/nmcli/gen-metadata-nm-settings-nmcli.xml.in delete mode 100644 src/nmcli/generate-docs-nm-settings-nmcli.c delete mode 100644 src/nmcli/generate-docs-nm-settings-nmcli.xml delete mode 100644 src/nmcli/generate-docs-nm-settings-nmcli.xml.in create mode 100644 src/nmtui/nmt-8021x-fields.c create mode 100644 src/nmtui/nmt-8021x-fields.h create mode 100644 src/nmtui/nmt-page-macsec.c create mode 100644 src/nmtui/nmt-page-macsec.h (limited to 'src') diff --git a/src/c-siphash/src/c-siphash.c b/src/c-siphash/src/c-siphash.c index 720e403a..b92af658 100644 --- a/src/c-siphash/src/c-siphash.c +++ b/src/c-siphash/src/c-siphash.c @@ -8,7 +8,7 @@ * * So far, only SipHash24 is implemented, since there was no need for other * parameters. However, adjusted c_siphash_append_X() and - * C_siphash_finalize_Y() can be easily provided, if required. + * c_siphash_finalize_Y() can be easily provided, if required. */ #include @@ -88,23 +88,8 @@ _c_public_ void c_siphash_init(CSipHash *state, const uint8_t seed[16]) { }; } -/** - * c_siphash_append() - hash stream of data - * @state: context object - * @bytes: array of input bytes - * @n_bytes: number of input bytes - * - * This feeds an array of bytes into the SipHash state machine. This is a - * streaming-capable API. That is, the resulting hash is the same, regardless - * of the way you chunk the input. - * This function simply feeds the given bytes into the SipHash state machine. - * It does not produce a final hash. You can call this function many times to - * append more data. To retrieve the final hash, call c_siphash_finalize(). - * - * Note that this implementation works best when used with chunk-sizes of - * multiples of 64bit (8-bytes). This is not a requirement, though. - */ -_c_public_ void c_siphash_append(CSipHash *state, const uint8_t *bytes, size_t n_bytes) { + +static inline _c_always_inline_ void c_siphash_append_N(CSipHash *state, const uint8_t *bytes, size_t n_bytes, unsigned N) { const uint8_t *end = bytes + n_bytes; size_t left = state->n_bytes & 7; uint64_t m; @@ -123,8 +108,8 @@ _c_public_ void c_siphash_append(CSipHash *state, const uint8_t *bytes, size_t n return; state->v3 ^= state->padding; - c_siphash_sipround(state); - c_siphash_sipround(state); + for (unsigned i = 0; i < N; i++) + c_siphash_sipround(state); state->v0 ^= state->padding; state->padding = 0; @@ -141,8 +126,8 @@ _c_public_ void c_siphash_append(CSipHash *state, const uint8_t *bytes, size_t n m = c_siphash_read_le64(bytes); state->v3 ^= m; - c_siphash_sipround(state); - c_siphash_sipround(state); + for (unsigned i = 0; i < N; i++) + c_siphash_sipround(state); state->v0 ^= m; } @@ -179,6 +164,50 @@ _c_public_ void c_siphash_append(CSipHash *state, const uint8_t *bytes, size_t n } } +static inline _c_always_inline_ uint64_t c_siphash_finalize_NM(CSipHash *state, unsigned N, unsigned M) { + uint64_t b; + + b = state->padding | (((uint64_t) state->n_bytes) << 56); + + state->v3 ^= b; + for (unsigned i = 0; i < N; i++) + c_siphash_sipround(state); + state->v0 ^= b; + + state->v2 ^= 0xff; + + for (unsigned i = 0; i < M; i++) + c_siphash_sipround(state); + + return state->v0 ^ state->v1 ^ state->v2 ^ state->v3; +} + +/** + * c_siphash_append() - hash stream of data + * @state: context object + * @bytes: array of input bytes + * @n_bytes: number of input bytes + * + * This feeds an array of bytes into the SipHash state machine. This is a + * streaming-capable API. That is, the resulting hash is the same, regardless + * of the way you chunk the input. + * This function simply feeds the given bytes into the SipHash state machine. + * It does not produce a final hash. You can call this function many times to + * append more data. To retrieve the final hash, call c_siphash_finalize(). + * + * Note that this implementation works best when used with chunk-sizes of + * multiples of 64bit (8-bytes). This is not a requirement, though. + * + * This uses the SipHash-2-4 variant. + */ +_c_public_ void c_siphash_append(CSipHash *state, const uint8_t *bytes, size_t n_bytes) { + c_siphash_append_N(state, bytes, n_bytes, 2); +} + +_c_public_ void c_siphash_append_13(CSipHash *state, const uint8_t *bytes, size_t n_bytes) { + c_siphash_append_N(state, bytes, n_bytes, 1); +} + /** * c_siphash_finalize() - finalize hash * @state: context object @@ -192,26 +221,16 @@ _c_public_ void c_siphash_append(CSipHash *state, const uint8_t *bytes, size_t n * the object, anymore, you can release it any time. There is no need to * destroy the object explicitly. * + * This uses the SipHash-2-4 variant. + * * Return: 64bit hash value */ _c_public_ uint64_t c_siphash_finalize(CSipHash *state) { - uint64_t b; - - b = state->padding | (((uint64_t) state->n_bytes) << 56); - - state->v3 ^= b; - c_siphash_sipround(state); - c_siphash_sipround(state); - state->v0 ^= b; - - state->v2 ^= 0xff; - - c_siphash_sipround(state); - c_siphash_sipround(state); - c_siphash_sipround(state); - c_siphash_sipround(state); + return c_siphash_finalize_NM(state, 2, 4); +} - return state->v0 ^ state->v1 ^ state->v2 ^ state->v3; +_c_public_ uint64_t c_siphash_finalize_13(CSipHash *state) { + return c_siphash_finalize_NM(state, 1, 3); } /** @@ -233,13 +252,24 @@ _c_public_ uint64_t c_siphash_finalize(CSipHash *state) { * Unlike the streaming API, this is a one-shot call suitable for any data that * is available in-memory at the same time. * + * This uses the SipHash-2-4 variant. + * * Return: 64bit hash value */ _c_public_ uint64_t c_siphash_hash(const uint8_t seed[16], const uint8_t *bytes, size_t n_bytes) { CSipHash state; c_siphash_init(&state, seed); - c_siphash_append(&state, bytes, n_bytes); + c_siphash_append_24(&state, bytes, n_bytes); + + return c_siphash_finalize_24(&state); +} + +_c_public_ uint64_t c_siphash_hash_13(const uint8_t seed[16], const uint8_t *bytes, size_t n_bytes) { + CSipHash state; + + c_siphash_init(&state, seed); + c_siphash_append_13(&state, bytes, n_bytes); - return c_siphash_finalize(&state); + return c_siphash_finalize_13(&state); } diff --git a/src/c-siphash/src/c-siphash.h b/src/c-siphash/src/c-siphash.h index c0cfc1e0..24fc3f27 100644 --- a/src/c-siphash/src/c-siphash.h +++ b/src/c-siphash/src/c-siphash.h @@ -47,14 +47,36 @@ struct CSipHash { size_t n_bytes; }; -#define C_SIPHASH_NULL {} +#define C_SIPHASH_NULL {0} void c_siphash_init(CSipHash *state, const uint8_t seed[16]); + +/* siphash-2-4 (default for historical reasons) */ + void c_siphash_append(CSipHash *state, const uint8_t *bytes, size_t n_bytes); uint64_t c_siphash_finalize(CSipHash *state); - uint64_t c_siphash_hash(const uint8_t seed[16], const uint8_t *bytes, size_t n_bytes); +/* siphash-1-3 */ + +void c_siphash_append_13(CSipHash *state, const uint8_t *bytes, size_t n_bytes); +uint64_t c_siphash_finalize_13(CSipHash *state); +uint64_t c_siphash_hash_13(const uint8_t seed[16], const uint8_t *bytes, size_t n_bytes); + +/* inline helpers */ + +static inline void c_siphash_append_24(CSipHash *state, const uint8_t *bytes, size_t n_bytes) { + c_siphash_append(state, bytes, n_bytes); +} + +static inline uint64_t c_siphash_finalize_24(CSipHash *state) { + return c_siphash_finalize(state); +} + +static inline uint64_t c_siphash_hash_24(const uint8_t seed[16], const uint8_t *bytes, size_t n_bytes) { + return c_siphash_hash(seed, bytes, n_bytes); +} + #ifdef __cplusplus } #endif diff --git a/src/c-stdaux/src/c-stdaux-generic.h b/src/c-stdaux/src/c-stdaux-generic.h new file mode 100644 index 00000000..4848617a --- /dev/null +++ b/src/c-stdaux/src/c-stdaux-generic.h @@ -0,0 +1,510 @@ +#pragma once + +/* + * c-stdaux-generic: Generic auxiliary macros and functions + * + * This header contains all generic features of c-stdaux.h, which are not + * specific to a target platform. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Documented alongside target properties. */ +#define C_MODULE_GENERIC 1 + +/** + * DOC: Target Properties + * + * Since multiple target compilers and systems are supported, c-stdaux + * exports a set of symbols that identify the target of the current compilation. + * The following pre-processor constants are defined (and evaluate to ``1``) if + * the current compilation targets the specific system. Note that multiple + * constants might be defined at the same time if compatibility to multiple + * targets is available. + * + * - ``C_COMPILER_CLANG``: The compiling software is compatible to the CLang + * LLVM Compiler. + * - ``C_COMPILER_DOCS``: The compilation is part of generating documentation. + * - ``C_COMPILER_GNUC``: The compiling software is compatible to the GNU C + * Compiler. + * - ``C_COMPILER_MSVC``: The compiling software is compatible to Microsoft + * Visual Studio (use ``_MSC_VER`` to check for specific version support). + * - ``C_OS_LINUX``: The target system is compatible to Linux. + * - ``C_OS_MACOS``: The target system is compatible to Apple MacOS. + * - ``C_OS_WINDOWS``: The target system is compatible to Microsoft Windows. + * - ``C_MODULE_GENERIC``: The `*-generic.h` module was included. + * - ``C_MODULE_GNUC``: The `*-gnuc.h` module was included. + * - ``C_MODULE_UNIX``: The `*-unix.h` module was included. + * + * Note that other exported symbols might depend on one of these constants to + * be set in order to be exposed. See the documentation of each symbol for + * details. Furthermore, if stub implementations do not violate the guarantees + * of a symbol, they will be provided for targets that do not provide the + * necessary infrastructure (e.g., ``_c_likely_()`` is a no-op on MSVC). + */ +/**/ + +#if defined(__clang__) +# define C_COMPILER_CLANG 1 +#endif + +/* #define C_COMPILER_DOCS 1 */ + +#if defined(__GNUC__) +# define C_COMPILER_GNUC 1 +#endif + +#if defined(_MSC_VER) +# define C_COMPILER_MSVC 1 +#endif + +#if defined(__linux__) +# define C_OS_LINUX 1 +#endif + +#if defined(__MACH__) && defined(__APPLE__) +# define C_OS_MACOS 1 +#endif + +#if defined(_WIN32) || defined(_WIN64) +# define C_OS_WINDOWS 1 +#endif + +/** + * DOC: Guaranteed STD-C Includes + * + * c-stdaux includes a set of C Standard Library headers. All those includes + * are guaranteed and part of the API. See the actual header for a + * comprehensive list. + */ +/**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * DOC: Generic Compiler Intrinsics + * + * This section provides access to compiler extensions and intrinsics which are + * either portable or have generic fallbacks. + */ +/**/ + +/** + * _c_always_inline_() - Always-inline attribute + * + * Annotate a symbol to be inlined more aggressively. On GNUC targets this is + * an alias for ``__attribute__((__always_inline__))``. On MSVC targets this is + * and alias for ``__forceinline``. On other systems, this is a no-op. + */ +#define _c_always_inline_ _c_internal_always_inline_ +#if defined(C_COMPILER_GNUC) +# define _c_internal_always_inline_ __attribute__((__always_inline__)) +#elif defined(C_COMPILER_MSVC) +# define _c_internal_always_inline_ __forceinline +#else +# define _c_internal_always_inline_ +#endif + +/** + * _c_boolean_expr_() - Evaluate a boolean expression + * @_x: Expression to evaluate + * + * Evaluate the given expression and convert the result to 1 or 0. In most + * cases this is equivalent to ``(!!(_x))``. However, for given compilers this + * avoids the parentheses to improve diagnostics with ``-Wparentheses``. + * + * Outside of macros, this has no added value. + * + * Return: Evaluates to the value of ``!!_x``. + */ +#define _c_boolean_expr_(_x) _c_internal_boolean_expr_(__COUNTER__, _x) +#if defined(C_COMPILER_GNUC) && __GNUC__ > 4 +# define _c_internal_boolean_expr_(_uniq, _x) \ + __builtin_choose_expr( \ + __builtin_constant_p(_x), \ + (!!(_x)), \ + (__extension__ ({ \ + int C_VAR(b, _uniq); \ + \ + /* \ + * Avoid any extra parentheses around the evaluation of \ + * `_x` to allow `-Wparentheses` to warn about use of \ + * `x = ...` and instead suggest `(x = ...)` or \ + * `x == ...`. \ + */ \ + \ + if (_x) \ + C_VAR(b, _uniq) = 1; \ + else \ + C_VAR(b, _uniq) = 0; \ + \ + C_VAR(b, _uniq); \ + }))) +#else +# define _c_internal_boolean_expr_(_uniq, _x) (!!(_x)) +#endif + +/** + * _c_likely_() - Likely attribute + * @_x: Expression to evaluate + * + * Alias for ``__builtin_expect(!!(_x), 1)``. + * + * Return: The expression ``!!_x`` is evaluated and returned. + */ +#define _c_likely_(_x) _c_internal_likely_(_x) +#if defined(C_COMPILER_GNUC) +# define _c_internal_likely_(_x) (__builtin_expect(_c_boolean_expr_(_x), 1)) +#else +# define _c_internal_likely_(_x) (_c_boolean_expr_(_x)) +#endif + +/** + * _c_public_() - Public attribute + * + * Mark a symbol definition as public, to be exported by the linker. On + * GNUC-compatible systems, this is an alias for + * ``__attribute__((__visibility__("default")))``. On all other systems, this + * is a no-op. + * + * Note that this explicitly does not resolve to ``__declspec(dllexport)`` on + * MSVC targets, since that would require knowing whether to compile for export + * or inport and whether to compile for static or dynamic linking. Instead, + * the ``_c_public_`` attribute is meant to be used unconditionally on + * definition only. For MSVC exports, we recommend module definition files. + */ +#define _c_public_ _c_internal_public_ +#if defined(C_COMPILER_GNUC) +# define _c_internal_public_ __attribute__((__visibility__("default"))) +#else +# define _c_internal_public_ +#endif + +/** + * _c_unlikely_() - Unlikely attribute + * @_x: Expression to evaluate + * + * Alias for ``__builtin_expect(!!(_x), 0)``. + * + * Return: The expression ``!!_x`` is evaluated and returned. + */ +#define _c_unlikely_(_x) _c_internal_unlikely_(_x) +#if defined(C_COMPILER_GNUC) +# define _c_internal_unlikely_(_x) (__builtin_expect(_c_boolean_expr_(_x), 0)) +#else +# define _c_internal_unlikely_(_x) (_c_boolean_expr_(_x)) +#endif + +/** + * DOC: Generic Utility Macros + * + * A set of utility macros which is portable to all supported platforms or has + * generic fallback variants. + */ +/**/ + +/** + * C_STRINGIFY() - Stringify a token, but evaluate it first + * @_x: Token to evaluate and stringify + * + * Return: Evaluates to a constant string literal + */ +#define C_STRINGIFY(_x) C_INTERNAL_STRINGIFY(_x) +#define C_INTERNAL_STRINGIFY(_x) #_x + +/** + * C_CONCATENATE() - Concatenate two tokens, but evaluate them first + * @_x: First token + * @_y: Second token + * + * Return: Evaluates to a constant identifier + */ +#define C_CONCATENATE(_x, _y) C_INTERNAL_CONCATENATE(_x, _y) +#define C_INTERNAL_CONCATENATE(_x, _y) _x ## _y + +/** + * C_EXPAND() - Expand a tuple to a series of its values + * @_x: Tuple to expand + * + * Return: Evaluates to the expanded tuple + */ +#define C_EXPAND(_x) C_INTERNAL_EXPAND _x +#define C_INTERNAL_EXPAND(...) __VA_ARGS__ + +/** + * C_VAR() - Generate unique variable name + * @_x: Name of variable, optional + * @_uniq: Unique prefix, usually provided by ``__COUNTER__``, optional + * + * This macro shall be used to generate unique variable names, that will not be + * shadowed by recursive macro invocations. It is effectively a + * :c:macro:`C_CONCATENATE` of both arguments, but also provides a globally + * separated prefix and makes the code better readable. + * + * The second argument is optional. If not given, ``__LINE__`` is implied, and + * as such the macro will generate the same identifier if used multiple times + * on the same code-line (or within a macro). This should be used if recursive + * calls into the macro are not expected. In fact, no argument is necessary in + * this case, as a mere ``C_VAR`` will evaluate to a valid variable name. + * + * This helper may be used by macro implementations that might reasonable well + * be called in a stacked fasion, like: + * + * .. code-block:: c + * + * c_max(foo, c_max(bar, baz)) + * + * Such a stacked call of :c:macro:`c_max()` might cause compiler warnings of + * shadowed variables in the definition of :c:macro:`c_max()`. By using + * ``C_VAR()``, such warnings can be silenced as each evaluation of + * :c:macro:`c_max()` uses unique variable names. + * + * Return: This evaluates to a constant identifier. + */ +#define C_VAR(...) C_INTERNAL_VAR(__VA_ARGS__, 2, 1) +#define C_INTERNAL_VAR(_x, _uniq, _num, ...) C_VAR ## _num (_x, _uniq) +#define C_VAR1(_x, _unused) C_VAR2(_x, C_CONCATENATE(line, __LINE__)) +#define C_VAR2(_x, _uniq) C_CONCATENATE(c_internal_var_unique_, C_CONCATENATE(_uniq, _x)) + +/** + * DOC: Generic Standard Library Utilities + * + * The C Standard Library lacks some crucial and basic support functions. This + * section describes the set of helpers provided as extension to the standard + * library. + */ +/**/ + +/** + * c_assert() - Runtime assertions + * @_x: Result of an expression + * + * This function behaves like the standard ``assert(3)`` macro. That is, if + * ``NDEBUG`` is defined, it is a no-op. In all other cases it will assert that + * the result of the passed expression is true. + * + * Unlike the standard ``assert(3)`` macro, this function always evaluates its + * argument. This means side-effects will always be evaluated! However, if the + * macro is used with constant expressions, the compiler will be able to + * optimize it away. + */ +#define c_assert(_x) ( \ + _c_likely_(_x) \ + ? assert(true && #_x) \ + : assert(false && #_x) \ + ) + +/** + * c_errno() - Return valid errno + * + * This helper should be used to silence warnings if you know ``errno`` is valid + * (ie., ``errno`` is greater than 0). Instead of ``return -errno;``, use + * ``return -c_errno();`` It will suppress bogus warnings in case the compiler + * assumes ``errno`` might be 0 (or smaller than 0) and thus the caller's + * error-handling might not be triggered. + * + * This helper should be avoided whenever possible. However, occasionally we + * really want to silence warnings (especially with static/inline functions). In + * those cases, the compiler usually cannot deduce that some error paths are + * guaranteed to be taken. Hence, making the return value explicit allows it to + * better optimize the code. + * + * Note that you really should never use this helper to work around broken libc + * calls or syscalls, not setting 'errno' correctly. + * + * Return: Positive error code is returned. + */ +static inline int c_errno(void) { + return _c_likely_(errno > 0) ? errno : ENOTRECOVERABLE; +} + +/** + * c_memset() - Fill memory region with constant byte + * @p: Pointer to memory region, if non-empty + * @c: Value to fill with + * @n: Size of the memory region in bytes + * + * This function works like ``memset(3)`` if ``n`` is non-zero. If ``n`` is + * zero, this function is a no-op. Therefore, unlike ``memset(3)`` it is safe + * to call this function with ``NULL`` as ``p`` if ``n`` is 0. + * + * Return: ``p`` is returned. + */ +static inline void *c_memset(void *p, int c, size_t n) { + if (n > 0) + memset(p, c, n); + return p; +} + +/** + * c_memzero() - Clear memory area + * @p: Pointer to memory region, if non-empty + * @n: Size of the memory region in bytes + * + * Clear a memory area to 0. If the memory area is empty, this is a no-op. + * Similar to ``c_memset()``, this function allows ``p`` to be ``NULL`` if the + * area is empty. + * + * Return: ``p`` is returned. + */ +static inline void *c_memzero(void *p, size_t n) { + return c_memset(p, 0, n); +} + +/** + * c_memcpy() - Copy memory area + * @dst: Pointer to target area + * @src: Pointer to source area + * @n: Length of area to copy + * + * Copy the memory of size ``n`` from ``src`` to ``dst``, just as ``memcpy(3)`` + * does, except this function allows either to be ``NULL`` if ``n`` is zero. In + * the latter case, the operation is a no-op. + * + * Return: ``p`` is returned. + */ +static inline void *c_memcpy(void *dst, const void *src, size_t n) { + if (n > 0) + memcpy(dst, src, n); + return dst; +} + +/** + * c_memcmp() - Compare memory areas + * @s1: Pointer to one area + * @s2: Pointer to other area + * @n: Length of area to compare + * + * Compare the memory of size ``n`` of ``s1`` and ``s2``, just as ``memcmp(3)`` + * does, except this function allows either to be ``NULL`` if ``n`` is zero. + * + * Return: Comparison result for ordering is returned. + */ +static inline int c_memcmp(const void *s1, const void *s2, size_t n) { + if (n > 0) + return memcmp(s1, s2, n); + return 0; +} + +/** + * DOC: Generic Destructors + * + * A set of destructors is provided which extends standard library destructors + * to adhere to some adjuvant rules. In particular, they return an invalid + * value of the particular object, rather than void. This allows direct + * assignment to any member-field and/or variable they are defined in, like: + * + * .. code-block:: c + * + * foo = c_free(foo); + * foo->bar = c_fclose(foo->bar); + * + * Furthermore, all those destructors can be safely called with the "INVALID" + * value as argument, and they will be a no-op. + */ +/**/ + +/** + * c_free() - Destructor-wrapper for free() + * @p: Value to pass to destructor, or NULL + * + * Wrapper around ``free()``, but always returns ``NULL``. + * + * Return: NULL is returned. + */ +static inline void *c_free(void *p) { + free(p); + return NULL; +} + +/** + * c_fclose() - Destructor-wrapper for fclose() + * @f: File handle to pass to destructor, or NULL + * + * Wrapper around ``fclose()``, but a no-op if ``NULL`` is passed. Always + * returns ``NULL``. + * + * Return: NULL is returned. + */ +static inline FILE *c_fclose(FILE *f) { + if (f) + fclose(f); + return NULL; +} + +/** + * DOC: Generic Cleanup Helpers + * + * A set of helpers that aid in creating functions suitable for use with + * :c:macro:`_c_cleanup_()`. Furthermore, a collection of predefined cleanup + * functions of a set of standard library objects ready for use with + * :c:macro:`_c_cleanup_()`. + * Those cleanup helpers are always suffixed with a ``p``. + * + * The helpers that are provided are: + * + * - ``c_freep()``: Wrapper around :c:func:`c_free()`. + * - ``c_fclosep()``: Wrapper around :c:func:`c_fclose()`. + */ +/**/ + +/** + * C_DEFINE_CLEANUP() - Define cleanup helper + * @_type: Type of object to cleanup + * @_func: Destructor of the respective type + * + * Define a C static inline function that takes a single argument of type + * `_type` and calls `_func` on it, if its dereferenced value of its argument + * evaluates to true. Otherwise, it is a no-op. + * + * This macro allows for very simple and fast creation of cleanup helpers for + * use with ``_c_cleanup_()``, based on any destructor and type you provide to + * it. + */ +#define C_DEFINE_CLEANUP(_type, _func) \ + static inline void _func ## p(_type *p) { \ + if (*p) \ + _func(*p); \ + } struct c_internal_trailing_semicolon + +/** + * C_DEFINE_DIRECT_CLEANUP() - Define direct cleanup helper + * @_type: Type of object to cleanup + * @_func: Destructor of the respective type + * + * This works like :c:macro:`C_DEFINE_CLEANUP()` but does not check the + * dereferenced value of its argument. It always unconditionally invokes the + * destructor. + */ +#define C_DEFINE_DIRECT_CLEANUP(_type, _func) \ + static inline void _func ## p(_type *p) { \ + _func(*p); \ + } struct c_internal_trailing_semicolon + +static inline void c_freep(void *p) { + /* + * `foobar **` does not coerce to `void **`, so we need `void *` as + * argument type, and then we dereference manually. + */ + c_free(*(void **)p); +} + +C_DEFINE_CLEANUP(FILE *, c_fclose); + +#ifdef __cplusplus +} +#endif diff --git a/src/c-stdaux/src/c-stdaux-gnuc.h b/src/c-stdaux/src/c-stdaux-gnuc.h new file mode 100644 index 00000000..df323efd --- /dev/null +++ b/src/c-stdaux/src/c-stdaux-gnuc.h @@ -0,0 +1,435 @@ +#pragma once + +/* + * c-stdaux-gnuc: GNUC-specific auxiliary macros and functions + * + * This header contains all GNUC-specific features of c-stdaux.h, usually only + * available when compiled via GCC or clang. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Documented alongside target properties. */ +#define C_MODULE_GNUC 1 + +/** + * DOC: GNUC Compiler Attributes + * + * The GCC compiler uses the ``__attribute__((__xyz__()))`` syntax to annotate + * language entities with special attributes. Aliases are provided by this + * header which map one-to-one to the respective compiler attributes. + * + * These attributes are not supported by all compilers, but are always provided + * by this header. They are pre-processor macros and do not affect the + * compilation, unless used. Note that most compilers support these, not just + * GCC. + */ +/**/ + +/** + * _c_cleanup_() - Cleanup attribute + * @_x: Cleanup function to use + * + * Alias for ``__attribute__((__cleanup__(_x)))``. + */ +#define _c_cleanup_(_x) __attribute__((__cleanup__(_x))) + +/** + * _c_const_() - Const attribute + * + * Alias for ``__attribute__((__const__))``. + */ +#define _c_const_ __attribute__((__const__)) + +/** + * _c_deprecated_() - Deprecated attribute + * + * Alias for ``__attribute__((__deprecated__))``. + */ +#define _c_deprecated_ __attribute__((__deprecated__)) + +/** + * _c_hidden_() - Hidden attribute + * + * Alias for ``__attribute__((__visibility__("hidden")))``. + */ +#define _c_hidden_ __attribute__((__visibility__("hidden"))) + +/** + * _c_packed_() - Packed attribute + * + * Alias for ``__attribute__((__packed__))``. + */ +#define _c_packed_ __attribute__((__packed__)) + +/** + * _c_printf_() - Printf attribute + * @_a: Format expression argument index + * @_b: First format-parameter argument index + * + * Alias for ``__attribute__((__format__(printf, _a, _b)))``. + */ +#define _c_printf_(_a, _b) __attribute__((__format__(printf, _a, _b))) + +/** + * _c_pure_() - Pure attribute + * + * Alias for ``__attribute__((__pure__))``. + */ +#define _c_pure_ __attribute__((__pure__)) + +/** + * _c_sentinel_() - Sentinel attribute + * + * Alias for ``__attribute__((__sentinel__))``. + */ +#define _c_sentinel_ __attribute__((__sentinel__)) + +/** + * _c_unused_() - Unused attribute + * + * Alias for ``__attribute__((__unused__))``. + */ +#define _c_unused_ __attribute__((__unused__)) + +/** + * DOC: GNUC-Specific Utility Macros + * + * A set of utility macros is provided which aids in creating safe macros + * suitable for use in other pre-processor statements as well as in C + * expressions. + */ +/**/ + +/** + * C_EXPR_ASSERT() - Create expression with assertion + * @_expr: Expression to evaluate to + * @_assertion: Arbitrary assertion + * @_message: Message associated with the assertion + * + * This macro simply evaluates to ``_expr``. That is, it can be used in any + * context that expects an expression like ``_expr``. Additionally, it takes an + * assertion as ``_assertion`` and evaluates it through ``_Static_assert()``, + * using ``_message`` as debug message. + * + * The ``_Static_assert()`` builtin of C11 is defined as statement and thus + * cannot be used in expressions. This macro circumvents this restriction. + * + * Return: Evaluates to ``_expr``. + */ +#define C_EXPR_ASSERT(_expr, _assertion, _message) C_INTERNAL_EXPR_ASSERT((_expr), (_assertion), _message) +#if defined(__COVERITY__) // Coverity cannot const-fold __builtin_choose_expr() +# define C_INTERNAL_EXPR_ASSERT(_expr, _assertion, _message) (_expr) +#else +# define C_INTERNAL_EXPR_ASSERT(_expr, _assertion, _message) \ + /* indentation and line-split to get better diagnostics */ \ + (__builtin_choose_expr( \ + !!(1 + 0 * sizeof( \ + struct { \ +_Static_assert(_assertion, _message); \ + } \ + )), \ + (_expr), \ + ((void)0) \ + )) +#endif + +/** + * C_CC_MACRO1() - Provide safe environment to a macro + * @_call: Macro to call + * @_x1: First argument + * @...: Further arguments to forward unmodified to ``_call`` + * + * This function simplifies the implementation of macros. Whenever you + * implement a macro, provide the internal macro name as ``_call`` and its + * argument as ``_x1``. Inside of your internal macro, you... + * + * - are safe against multiple evaluation errors, since ``C_CC_MACRO1`` + * will store the initial parameters in temporary variables. + * + * - support constant folding, as ``C_CC_MACRO1`` takes care to invoke your + * macro with the original values, if they are compile-time constant. + * + * - have unique variable names for recursive callers and will not run into + * variable-shadowing-warnings accidentally. + * + * - have properly typed arguments as ``C_CC_MACRO1`` stores the original + * arguments in an ``__auto_type`` temporary variable. + * + * Return: Result of ``_call`` is returned. + */ +#define C_CC_MACRO1(_call, _x1, ...) C_INTERNAL_CC_MACRO1(_call, __COUNTER__, (_x1), ## __VA_ARGS__) +#define C_INTERNAL_CC_MACRO1(_call, _x1q, _x1, ...) \ + __builtin_choose_expr( \ + __builtin_constant_p(_x1), \ + _call(_x1, ## __VA_ARGS__), \ + __extension__ ({ \ + const __auto_type C_VAR(X1, _x1q) = (_x1); \ + _call(C_VAR(X1, _x1q), ## __VA_ARGS__); \ + })) + +/** + * C_CC_MACRO2() - Provide safe environment to a macro + * @_call: Macro to call + * @_x1: First argument + * @_x2: Second argument + * @...: Further arguments to forward unmodified to ``_call`` + * + * This is the 2-argument equivalent of :c:macro:`C_CC_MACRO1()`. + * + * Return: Result of ``_call`` is returned. + */ +#define C_CC_MACRO2(_call, _x1, _x2, ...) C_INTERNAL_CC_MACRO2(_call, __COUNTER__, (_x1), __COUNTER__, (_x2), ## __VA_ARGS__) +#define C_INTERNAL_CC_MACRO2(_call, _x1q, _x1, _x2q, _x2, ...) \ + __builtin_choose_expr( \ + (__builtin_constant_p(_x1) && __builtin_constant_p(_x2)), \ + _call((_x1), (_x2), ## __VA_ARGS__), \ + __extension__ ({ \ + const __auto_type C_VAR(X1, _x1q) = (_x1); \ + const __auto_type C_VAR(X2, _x2q) = (_x2); \ + _call(C_VAR(X1, _x1q), C_VAR(X2, _x2q), ## __VA_ARGS__); \ + })) + +/** + * C_CC_MACRO3() - Provide safe environment to a macro + * @_call: Macro to call + * @_x1: First argument + * @_x2: Second argument + * @_x3: Third argument + * @...: Further arguments to forward unmodified to ``_call`` + * + * This is the 3-argument equivalent of :c:macro:`C_CC_MACRO1()`. + * + * Return: Result of ``_call`` is returned. + */ +#define C_CC_MACRO3(_call, _x1, _x2, _x3, ...) C_INTERNAL_CC_MACRO3(_call, __COUNTER__, (_x1), __COUNTER__, (_x2), __COUNTER__, (_x3), ## __VA_ARGS__) +#define C_INTERNAL_CC_MACRO3(_call, _x1q, _x1, _x2q, _x2, _x3q, _x3, ...) \ + __builtin_choose_expr( \ + (__builtin_constant_p(_x1) && __builtin_constant_p(_x2) && __builtin_constant_p(_x3)), \ + _call((_x1), (_x2), (_x3), ## __VA_ARGS__), \ + __extension__ ({ \ + const __auto_type C_VAR(X1, _x1q) = (_x1); \ + const __auto_type C_VAR(X2, _x2q) = (_x2); \ + const __auto_type C_VAR(X3, _x3q) = (_x3); \ + _call(C_VAR(X1, _x1q), C_VAR(X2, _x2q), C_VAR(X3, _x3q), ## __VA_ARGS__); \ + })) + +/** + * DOC: Standard Library Utilities + * + * The C Standard Library lacks some crucial and basic support functions. This + * section describes the set of helpers provided as extension to the standard + * library. + */ +/**/ + +/** + * C_ARRAY_SIZE() - Calculate number of array elements at compile time + * @_x: Array to calculate size of + * + * Return: Evaluates to a constant integer expression. + */ +#define C_ARRAY_SIZE(_x) \ + C_EXPR_ASSERT(sizeof(_x) / sizeof((_x)[0]), \ + /* \ + * Verify that `_x' is an array, not a pointer. Rely on \ + * `&_x[0]' degrading arrays to pointers. \ + */ \ + !__builtin_types_compatible_p( \ + __typeof__(_x), \ + __typeof__(&(*(__typeof__(_x)*)0)[0]) \ + ), \ + "C_ARRAY_SIZE() called with non-array argument" \ + ) + +/** + * C_DECIMAL_MAX() - Calculate maximum length of a decimal representation + * @_type: Integer variable/type + * + * This calculates the bytes required for the decimal representation of an + * integer of the given type. It accounts for a possible +/- prefix, but it + * does *NOT* include the trailing terminating zero byte. + * + * Return: Evaluates to a constant integer expression + */ +#define C_DECIMAL_MAX(_arg) \ + (_Generic((__typeof__(_arg)){ 0 }, \ + char: C_INTERNAL_DECIMAL_MAX(sizeof(char)), \ + signed char: C_INTERNAL_DECIMAL_MAX(sizeof(signed char)), \ + unsigned char: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned char)), \ + signed short: C_INTERNAL_DECIMAL_MAX(sizeof(signed short)), \ + unsigned short: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned short)), \ + signed int: C_INTERNAL_DECIMAL_MAX(sizeof(signed int)), \ + unsigned int: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned int)), \ + signed long: C_INTERNAL_DECIMAL_MAX(sizeof(signed long)), \ + unsigned long: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned long)), \ + signed long long: C_INTERNAL_DECIMAL_MAX(sizeof(signed long long)), \ + unsigned long long: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned long long)))) +#define C_INTERNAL_DECIMAL_MAX(_bytes) \ + C_EXPR_ASSERT( \ + 1 + ((_bytes) <= 1 ? 3 : \ + (_bytes) <= 2 ? 5 : \ + (_bytes) <= 4 ? 10 : \ + 20), \ + (_bytes) <= 8, \ + "Invalid use of C_INTERNAL_DECIMAL_MAX()" \ + ) + +/** + * c_container_of() - Cast a member of a structure out to the containing type + * @_ptr: Pointer to the member or NULL + * @_type: Type of the container struct this is embedded in + * @_member: Name of the member within the struct + * + * This uses ``offsetof(3)`` to turn a pointer to a structure-member into a + * pointer to the surrounding structure. + * + * Return: Pointer to the surrounding object. + */ +#define c_container_of(_ptr, _type, _member) C_CC_MACRO1(C_CONTAINER_OF, (_ptr), _type, _member) +#define C_CONTAINER_OF(_ptr, _type, _member) \ + C_EXPR_ASSERT( \ + (_ptr ? (_type*)c_internal_container_of((void *)_ptr, offsetof(_type, _member)) : NULL), \ + __builtin_types_compatible_p( \ + __typeof__(*(_ptr)), \ + __typeof__(((_type){})._member) \ + ) || __builtin_types_compatible_p( \ + __typeof__(_ptr), \ + __typeof__(NULL) \ + ), \ + "Invalid use of C_CONTAINER_OF()" \ + ) +static inline void *c_internal_container_of(void *ptr, size_t offset) { + /* + * Arithmetic on NULL is UB, even if in dead-code. Hide it in a proper + * C function, so the macro never emits it as code. + */ + return (char *)ptr - offset; +} + +/** + * c_max() - Compute maximum of two values + * @_a: Value A + * @_b: Value B + * + * Calculate the maximum of both passed values. Both arguments are evaluated + * exactly once, under all circumstances. Furthermore, if both values are + * constant expressions, the result will be constant as well. + * + * The comparison of their values is performed with the types given by the + * caller. It is the caller's responsibility to convert them to suitable types + * if necessary. + * + * Return: Maximum of both values is returned. + */ +#define c_max(_a, _b) C_CC_MACRO2(C_MAX, (_a), (_b)) +#define C_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) + +/** + * c_min() - Compute minimum of two values + * @_a: Value A + * @_b: Value B + * + * Calculate the minimum of both passed values. Both arguments are evaluated + * exactly once, under all circumstances. Furthermore, if both values are + * constant expressions, the result will be constant as well. + * + * The comparison of their values is performed with the types given by the + * caller. It is the caller's responsibility to convert them to suitable types + * if necessary. + * + * Return: Minimum of both values is returned. + */ +#define c_min(_a, _b) C_CC_MACRO2(C_MIN, (_a), (_b)) +#define C_MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b)) + +/** + * c_less_by() - Calculate clamped difference of two values + * @_a: Minuend + * @_b: Subtrahend + * + * Calculate ``_a - _b``, but clamp the result to 0. Both arguments are + * evaluated exactly once, under all circumstances. Furthermore, if both values + * are constant expressions, the result will be constant as well. + * + * The comparison of their values is performed with the types given by the + * caller. It is the caller's responsibility to convert them to suitable types + * if necessary. + * + * Return: This computes ``_a - _b``, if ``_a > _b``. Otherwise, 0 is returned. + */ +#define c_less_by(_a, _b) C_CC_MACRO2(C_LESS_BY, (_a), (_b)) +#define C_LESS_BY(_a, _b) ((_a) > (_b) ? (_a) - (_b) : 0) + +/** + * c_clamp() - Clamp value to lower and upper boundary + * @_x: Value to clamp + * @_low: Lower boundary + * @_high: Higher boundary + * + * This clamps ``_x`` to the lower and higher bounds given as ``_low`` and + * ``_high``. All arguments are evaluated exactly once, and yield a constant + * expression if all arguments are constant as well. + * + * The comparison of their values is performed with the types given by the + * caller. It is the caller's responsibility to convert them to suitable types + * if necessary. + * + * Return: Clamped integer value. + */ +#define c_clamp(_x, _low, _high) C_CC_MACRO3(C_CLAMP, (_x), (_low), (_high)) +#define C_CLAMP(_x, _low, _high) ((_x) > (_high) ? (_high) : (_x) < (_low) ? (_low) : (_x)) + +/** + * c_div_round_up() - Calculate integer quotient but round up + * @_x: Dividend + * @_y: Divisor + * + * Calculates ``x / y`` but rounds up the result to the next integer. All + * arguments are evaluated exactly once, and yield a constant expression if all + * arguments are constant. + * + * **Note:** + * ``(x + y - 1) / y`` suffers from an integer overflow, even though the + * computation should be possible in the given type. Therefore, we use + * ``x / y + !!(x % y)``. Note that on most CPUs a division returns both the + * quotient and the remainder, so both should be equally fast. Furthermore, if + * the divisor is a power of two, the compiler will optimize it, anyway. + * + * The operationsare performed with the types given by the caller. It is the + * caller's responsibility to convert the arguments to suitable types if + * necessary. + * + * Return: The quotient is returned. + */ +#define c_div_round_up(_x, _y) C_CC_MACRO2(C_DIV_ROUND_UP, (_x), (_y)) +#define C_DIV_ROUND_UP(_x, _y) ((_x) / (_y) + !!((_x) % (_y))) + +/** + * c_align_to() - Align value to a multiple + * @_val: Value to align + * @_to: Align to multiple of this + * + * This aligns ``_val`` to a multiple of ``_to``. If ``_val`` is already a + * multiple of ``_to``, ``_val`` is returned unchanged. This function operates + * within the boundaries of the type of ``_val`` and ``_to``. Make sure to cast + * them if needed. + * + * The arguments of this macro are evaluated exactly once. If both arguments + * are a constant expression, this also yields a constant return value. + * + * Note that ``_to`` must be a power of 2, otherwise the behavior will not + * match expectations. + * + * Return: ``_val`` aligned to a multiple of ``_to``. + */ +#define c_align_to(_val, _to) C_CC_MACRO2(C_ALIGN_TO, (_val), (_to)) +#define C_ALIGN_TO(_val, _to) (((_val) + (_to) - 1) & ~((_to) - 1)) + +#ifdef __cplusplus +} +#endif diff --git a/src/c-stdaux/src/c-stdaux-unix.h b/src/c-stdaux/src/c-stdaux-unix.h new file mode 100644 index 00000000..b3699ac6 --- /dev/null +++ b/src/c-stdaux/src/c-stdaux-unix.h @@ -0,0 +1,102 @@ +#pragma once + +/* + * c-stdaux-unix: Unix-specific auxiliary macros and functions + * + * This header contains all unix-specific features of c-stdaux.h, usually only + * available on unix-like platforms. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Documented alongside target properties. */ +#define C_MODULE_UNIX 1 + +/** + * DOC: Guaranteed Unix Includes + * + * c-stdaux-unix includes a set of Unix headers. All those includes are + * guaranteed and part of the API. See the actual header for a comprehensive + * list. + */ +/**/ + +#include +#include +#include +#include +#include + +/** + * DOC: Common Unix Destructors + * + * A set of destructors is provided which extends standard library destructors + * to adhere to some adjuvant rules. In particular, they return an invalid + * value of the particular object, rather than void. This allows direct + * assignment to any member-field and/or variable they are defined in, like: + * + * .. code-block:: c + * + * foo->bar = c_close(foo->bar); + * + * Furthermore, all those destructors can be safely called with the "INVALID" + * value as argument, and they will be a no-op. + */ +/**/ + +/** + * c_close() - Destructor-wrapper for close() + * @fd: File-descriptor to pass to destructor, or negative value + * + * Wrapper around ``close()``, but a no-op if a negative value is provided. + * Always returns ``-1``. + * + * Return: -1 is returned. + */ +static inline int c_close(int fd) { + if (fd >= 0) + close(fd); + return -1; +} + +/** + * c_closedir() - Destructor-wrapper for closedir() + * @d: Directory handle to pass to destructor, or NULL + * + * Wrapper around ``closedir()``, but a no-op if ``NULL`` is passed. Always + * returns ``NULL``. + * + * Return: NULL is returned. + */ +static inline DIR *c_closedir(DIR *d) { + if (d) + closedir(d); + return NULL; +} + +/** + * DOC: Common Cleanup Helpers + * + * A set of helpers that aid in creating functions suitable for use with + * :c:macro:`_c_cleanup_()`. Furthermore, a collection of predefined cleanup + * functions of a set of standard library objects ready for use with + * :c:macro:`_c_cleanup_()`. + * Those cleanup helpers are always suffixed with a ``p``. + * + * The helpers that are provided are: + * + * - ``c_closep()``: Wrapper around :c:func:`c_close()`. + * - ``c_closedirp()``: Wrapper around :c:func:`c_closedir()`. + */ +/**/ + +C_DEFINE_DIRECT_CLEANUP(int, c_close); +C_DEFINE_CLEANUP(DIR *, c_closedir); + +#ifdef __cplusplus +} +#endif diff --git a/src/c-stdaux/src/c-stdaux.h b/src/c-stdaux/src/c-stdaux.h index 80db7f9a..0eccb20f 100644 --- a/src/c-stdaux/src/c-stdaux.h +++ b/src/c-stdaux/src/c-stdaux.h @@ -40,795 +40,15 @@ extern "C" { */ /**/ -/** - * DOC: Guaranteed Includes - * - * The ``c-stdaux.h`` header includes a set of C Standard Library headers as - * well as UNIX headers. All those includes are guaranteed and part of the API. - * See the actual header for a comprehensive list. - */ -/**/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * DOC: Compiler Attributes - * - * The GCC compiler uses the ``__attribute__((__xyz__()))`` syntax to annotate - * language entities with special attributes. Aliases are provided by this - * header which map one-to-one to the respective compiler attributes. - * - * These attributes are not supported by all compilers, but are always provided - * by this header. They are pre-processor macros and do not affect the - * compilation, unless used. Note that most compilers support these, not just - * GCC. - */ -/**/ - -/** - * _c_cleanup_() - Cleanup attribute - * @_x: Cleanup function to use - * - * Alias for ``__attribute__((__cleanup__(_x)))``. - */ -#define _c_cleanup_(_x) __attribute__((__cleanup__(_x))) - -/** - * _c_const_() - Const attribute - * - * Alias for ``__attribute__((__const__))``. - */ -#define _c_const_ __attribute__((__const__)) - -/** - * _c_deprecated_() - Deprecated attribute - * - * Alias for ``__attribute__((__deprecated__))``. - */ -#define _c_deprecated_ __attribute__((__deprecated__)) - -/** - * _c_hidden_() - Hidden attribute - * - * Alias for ``__attribute__((__visibility__("hidden")))``. - */ -#define _c_hidden_ __attribute__((__visibility__("hidden"))) - -/** - * _c_packed_() - Packed attribute - * - * Alias for ``__attribute__((__packed__))``. - */ -#define _c_packed_ __attribute__((__packed__)) - -/** - * _c_printf_() - Printf attribute - * @_a: Format expression argument index - * @_b: First format-parameter argument index - * - * Alias for ``__attribute__((__format__(printf, _a, _b)))``. - */ -#define _c_printf_(_a, _b) __attribute__((__format__(printf, _a, _b))) - -/** - * _c_public_() - Public attribute - * - * Alias for ``__attribute__((__visibility__("default")))``. - */ -#define _c_public_ __attribute__((__visibility__("default"))) - -/** - * _c_pure_() - Pure attribute - * - * Alias for ``__attribute__((__pure__))``. - */ -#define _c_pure_ __attribute__((__pure__)) - -/** - * _c_sentinel_() - Sentinel attribute - * - * Alias for ``__attribute__((__sentinel__))``. - */ -#define _c_sentinel_ __attribute__((__sentinel__)) - -/** - * _c_unused_() - Unused attribute - * - * Alias for ``__attribute__((__unused__))``. - */ -#define _c_unused_ __attribute__((__unused__)) - -/** - * DOC: Compiler Intrinsics - * - * Aliases for common compiler extensions and intrinsics are provided similar - * to the compiler attributes. They are pure preprocessor aliases and do not - * affect compilation unless used. - */ -/**/ - -/** - * _c_likely_() - Likely attribute - * @_x: Expression to evaluate - * - * Alias for ``__builtin_expect(!!(_x), 1)``. - * - * Return: The expression ``_x`` is evaluated and returned. - */ -#define _c_likely_(_x) (__builtin_expect(!!(_x), 1)) - -/** - * _c_unlikely_() - Unlikely attribute - * @_x: Expression to evaluate - * - * Alias for ``__builtin_expect(!!(_x), 0)``. - * - * Return: The expression ``_x`` is evaluated and returned. - */ -#define _c_unlikely_(_x) (__builtin_expect(!!(_x), 0)) +#include -/** - * DOC: Utility Macros - * - * A set of utility macros is provided which aids in creating safe macros - * suitable for use in other pre-processor statements as well as in C - * expressions. - */ -/**/ - -/** - * C_EXPR_ASSERT() - Create expression with assertion - * @_expr: Expression to evaluate to - * @_assertion: Arbitrary assertion - * @_message: Message associated with the assertion - * - * This macro simply evaluates to ``_expr``. That is, it can be used in any - * context that expects an expression like ``_expr``. Additionally, it takes an - * assertion as ``_assertion`` and evaluates it through ``_Static_assert()``, - * using ``_message`` as debug message. - * - * The ``_Static_assert()`` builtin of C11 is defined as statement and thus - * cannot be used in expressions. This macro circumvents this restriction. - * - * Return: Evaluates to ``_expr``. - */ -#define C_EXPR_ASSERT(_expr, _assertion, _message) C_INTERNAL_EXPR_ASSERT((_expr), (_assertion), _message) -#if defined(__COVERITY__) // Coverity cannot const-fold __builtin_choose_expr() -# define C_INTERNAL_EXPR_ASSERT(_expr, _assertion, _message) (_expr) -#else -# define C_INTERNAL_EXPR_ASSERT(_expr, _assertion, _message) \ - /* indentation and line-split to get better diagnostics */ \ - (__builtin_choose_expr( \ - !!(1 + 0 * sizeof( \ - struct { \ -_Static_assert(_assertion, _message); \ - } \ - )), \ - (_expr), \ - ((void)0) \ - )) +#if defined(C_COMPILER_GNUC) +# include #endif -/** - * C_STRINGIFY() - Stringify a token, but evaluate it first - * @_x: Token to evaluate and stringify - * - * Return: Evaluates to a constant string literal - */ -#define C_STRINGIFY(_x) C_INTERNAL_STRINGIFY(_x) -#define C_INTERNAL_STRINGIFY(_x) #_x - -/** - * C_CONCATENATE() - Concatenate two tokens, but evaluate them first - * @_x: First token - * @_y: Second token - * - * Return: Evaluates to a constant identifier - */ -#define C_CONCATENATE(_x, _y) C_INTERNAL_CONCATENATE(_x, _y) -#define C_INTERNAL_CONCATENATE(_x, _y) _x ## _y - -/** - * C_EXPAND() - Expand a tuple to a series of its values - * @_x: Tuple to expand - * - * Return: Evaluates to the expanded tuple - */ -#define C_EXPAND(_x) C_INTERNAL_EXPAND _x -#define C_INTERNAL_EXPAND(...) __VA_ARGS__ - -/** - * C_VAR() - Generate unique variable name - * @_x: Name of variable, optional - * @_uniq: Unique prefix, usually provided by ``__COUNTER__``, optional - * - * This macro shall be used to generate unique variable names, that will not be - * shadowed by recursive macro invocations. It is effectively a - * :c:macro:`C_CONCATENATE` of both arguments, but also provides a globally - * separated prefix and makes the code better readable. - * - * The second argument is optional. If not given, ``__LINE__`` is implied, and - * as such the macro will generate the same identifier if used multiple times - * on the same code-line (or within a macro). This should be used if recursive - * calls into the macro are not expected. In fact, no argument is necessary in - * this case, as a mere ``C_VAR`` will evaluate to a valid variable name. - * - * This helper may be used by macro implementations that might reasonable well - * be called in a stacked fasion, like: - * - * .. code-block:: c - * - * c_max(foo, c_max(bar, baz)) - * - * Such a stacked call of :c:macro:`c_max()` might cause compiler warnings of - * shadowed variables in the definition of :c:macro:`c_max()`. By using - * ``C_VAR()``, such warnings can be silenced as each evaluation of - * :c:macro:`c_max()` uses unique variable names. - * - * Return: This evaluates to a constant identifier. - */ -#define C_VAR(...) C_INTERNAL_VAR(__VA_ARGS__, 2, 1) -#define C_INTERNAL_VAR(_x, _uniq, _num, ...) C_VAR ## _num (_x, _uniq) -#define C_VAR1(_x, _unused) C_VAR2(_x, C_CONCATENATE(line, __LINE__)) -#define C_VAR2(_x, _uniq) C_CONCATENATE(c_internal_var_unique_, C_CONCATENATE(_uniq, _x)) - -/** - * C_CC_MACRO1() - Provide safe environment to a macro - * @_call: Macro to call - * @_x1: First argument - * @...: Further arguments to forward unmodified to ``_call`` - * - * This function simplifies the implementation of macros. Whenever you - * implement a macro, provide the internal macro name as ``_call`` and its - * argument as ``_x1``. Inside of your internal macro, you... - * - * - are safe against multiple evaluation errors, since ``C_CC_MACRO1`` - * will store the initial parameters in temporary variables. - * - * - support constant folding, as ``C_CC_MACRO1`` takes care to invoke your - * macro with the original values, if they are compile-time constant. - * - * - have unique variable names for recursive callers and will not run into - * variable-shadowing-warnings accidentally. - * - * - have properly typed arguments as ``C_CC_MACRO1`` stores the original - * arguments in an ``__auto_type`` temporary variable. - * - * Return: Result of ``_call`` is returned. - */ -#define C_CC_MACRO1(_call, _x1, ...) C_INTERNAL_CC_MACRO1(_call, __COUNTER__, (_x1), ## __VA_ARGS__) -#define C_INTERNAL_CC_MACRO1(_call, _x1q, _x1, ...) \ - __builtin_choose_expr( \ - __builtin_constant_p(_x1), \ - _call(_x1, ## __VA_ARGS__), \ - __extension__ ({ \ - const __auto_type C_VAR(X1, _x1q) = (_x1); \ - _call(C_VAR(X1, _x1q), ## __VA_ARGS__); \ - })) - -/** - * C_CC_MACRO2() - Provide safe environment to a macro - * @_call: Macro to call - * @_x1: First argument - * @_x2: Second argument - * @...: Further arguments to forward unmodified to ``_call`` - * - * This is the 2-argument equivalent of :c:macro:`C_CC_MACRO1()`. - * - * Return: Result of ``_call`` is returned. - */ -#define C_CC_MACRO2(_call, _x1, _x2, ...) C_INTERNAL_CC_MACRO2(_call, __COUNTER__, (_x1), __COUNTER__, (_x2), ## __VA_ARGS__) -#define C_INTERNAL_CC_MACRO2(_call, _x1q, _x1, _x2q, _x2, ...) \ - __builtin_choose_expr( \ - (__builtin_constant_p(_x1) && __builtin_constant_p(_x2)), \ - _call((_x1), (_x2), ## __VA_ARGS__), \ - __extension__ ({ \ - const __auto_type C_VAR(X1, _x1q) = (_x1); \ - const __auto_type C_VAR(X2, _x2q) = (_x2); \ - _call(C_VAR(X1, _x1q), C_VAR(X2, _x2q), ## __VA_ARGS__); \ - })) - -/** - * C_CC_MACRO3() - Provide safe environment to a macro - * @_call: Macro to call - * @_x1: First argument - * @_x2: Second argument - * @_x3: Third argument - * @...: Further arguments to forward unmodified to ``_call`` - * - * This is the 3-argument equivalent of :c:macro:`C_CC_MACRO1()`. - * - * Return: Result of ``_call`` is returned. - */ -#define C_CC_MACRO3(_call, _x1, _x2, _x3, ...) C_INTERNAL_CC_MACRO3(_call, __COUNTER__, (_x1), __COUNTER__, (_x2), __COUNTER__, (_x3), ## __VA_ARGS__) -#define C_INTERNAL_CC_MACRO3(_call, _x1q, _x1, _x2q, _x2, _x3q, _x3, ...) \ - __builtin_choose_expr( \ - (__builtin_constant_p(_x1) && __builtin_constant_p(_x2) && __builtin_constant_p(_x3)), \ - _call((_x1), (_x2), (_x3), ## __VA_ARGS__), \ - __extension__ ({ \ - const __auto_type C_VAR(X1, _x1q) = (_x1); \ - const __auto_type C_VAR(X2, _x2q) = (_x2); \ - const __auto_type C_VAR(X3, _x3q) = (_x3); \ - _call(C_VAR(X1, _x1q), C_VAR(X2, _x2q), C_VAR(X3, _x3q), ## __VA_ARGS__); \ - })) - -/** - * DOC: Standard Library Utilities - * - * The C Standard Library lacks some crucial and basic support functions. This - * section describes the set of helpers provided as extension to the standard - * library. - */ -/**/ - -/** - * C_ARRAY_SIZE() - Calculate number of array elements at compile time - * @_x: Array to calculate size of - * - * Return: Evaluates to a constant integer expression. - */ -#define C_ARRAY_SIZE(_x) \ - C_EXPR_ASSERT(sizeof(_x) / sizeof((_x)[0]), \ - /* \ - * Verify that `_x' is an array, not a pointer. Rely on \ - * `&_x[0]' degrading arrays to pointers. \ - */ \ - !__builtin_types_compatible_p( \ - __typeof__(_x), \ - __typeof__(&(*(__typeof__(_x)*)0)[0]) \ - ), \ - "C_ARRAY_SIZE() called with non-array argument" \ - ) - -/** - * C_DECIMAL_MAX() - Calculate maximum length of a decimal representation - * @_type: Integer variable/type - * - * This calculates the bytes required for the decimal representation of an - * integer of the given type. It accounts for a possible +/- prefix, but it - * does *NOT* include the trailing terminating zero byte. - * - * Return: Evaluates to a constant integer expression - */ -#define C_DECIMAL_MAX(_arg) \ - (_Generic((__typeof__(_arg)){ 0 }, \ - char: C_INTERNAL_DECIMAL_MAX(sizeof(char)), \ - signed char: C_INTERNAL_DECIMAL_MAX(sizeof(signed char)), \ - unsigned char: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned char)), \ - signed short: C_INTERNAL_DECIMAL_MAX(sizeof(signed short)), \ - unsigned short: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned short)), \ - signed int: C_INTERNAL_DECIMAL_MAX(sizeof(signed int)), \ - unsigned int: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned int)), \ - signed long: C_INTERNAL_DECIMAL_MAX(sizeof(signed long)), \ - unsigned long: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned long)), \ - signed long long: C_INTERNAL_DECIMAL_MAX(sizeof(signed long long)), \ - unsigned long long: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned long long)))) -#define C_INTERNAL_DECIMAL_MAX(_bytes) \ - C_EXPR_ASSERT( \ - 1 + ((_bytes) <= 1 ? 3 : \ - (_bytes) <= 2 ? 5 : \ - (_bytes) <= 4 ? 10 : \ - 20), \ - (_bytes) <= 8, \ - "Invalid use of C_INTERNAL_DECIMAL_MAX()" \ - ) - -/** - * c_container_of() - Cast a member of a structure out to the containing type - * @_ptr: Pointer to the member or NULL - * @_type: Type of the container struct this is embedded in - * @_member: Name of the member within the struct - * - * This uses ``offsetof(3)`` to turn a pointer to a structure-member into a - * pointer to the surrounding structure. - * - * Return: Pointer to the surrounding object. - */ -#define c_container_of(_ptr, _type, _member) C_CC_MACRO1(C_CONTAINER_OF, (_ptr), _type, _member) -#define C_CONTAINER_OF(_ptr, _type, _member) \ - C_EXPR_ASSERT( \ - (_ptr ? (_type*)c_internal_container_of((void *)_ptr, offsetof(_type, _member)) : NULL), \ - __builtin_types_compatible_p( \ - __typeof__(*(_ptr)), \ - __typeof__(((_type){})._member) \ - ) || __builtin_types_compatible_p( \ - __typeof__(_ptr), \ - __typeof__(NULL) \ - ), \ - "Invalid use of C_CONTAINER_OF()" \ - ) -static inline void *c_internal_container_of(void *ptr, size_t offset) { - /* - * Arithmetic on NULL is UB, even if in dead-code. Hide it in a proper - * C function, so the macro never emits it as code. - */ - return (char *)ptr - offset; -} - -/** - * c_max() - Compute maximum of two values - * @_a: Value A - * @_b: Value B - * - * Calculate the maximum of both passed values. Both arguments are evaluated - * exactly once, under all circumstances. Furthermore, if both values are - * constant expressions, the result will be constant as well. - * - * The comparison of their values is performed with the types given by the - * caller. It is the caller's responsibility to convert them to suitable types - * if necessary. - * - * Return: Maximum of both values is returned. - */ -#define c_max(_a, _b) C_CC_MACRO2(C_MAX, (_a), (_b)) -#define C_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) - -/** - * c_min() - Compute minimum of two values - * @_a: Value A - * @_b: Value B - * - * Calculate the minimum of both passed values. Both arguments are evaluated - * exactly once, under all circumstances. Furthermore, if both values are - * constant expressions, the result will be constant as well. - * - * The comparison of their values is performed with the types given by the - * caller. It is the caller's responsibility to convert them to suitable types - * if necessary. - * - * Return: Minimum of both values is returned. - */ -#define c_min(_a, _b) C_CC_MACRO2(C_MIN, (_a), (_b)) -#define C_MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b)) - -/** - * c_less_by() - Calculate clamped difference of two values - * @_a: Minuend - * @_b: Subtrahend - * - * Calculate ``_a - _b``, but clamp the result to 0. Both arguments are - * evaluated exactly once, under all circumstances. Furthermore, if both values - * are constant expressions, the result will be constant as well. - * - * The comparison of their values is performed with the types given by the - * caller. It is the caller's responsibility to convert them to suitable types - * if necessary. - * - * Return: This computes ``_a - _b``, if ``_a > _b``. Otherwise, 0 is returned. - */ -#define c_less_by(_a, _b) C_CC_MACRO2(C_LESS_BY, (_a), (_b)) -#define C_LESS_BY(_a, _b) ((_a) > (_b) ? (_a) - (_b) : 0) - -/** - * c_clamp() - Clamp value to lower and upper boundary - * @_x: Value to clamp - * @_low: Lower boundary - * @_high: Higher boundary - * - * This clamps ``_x`` to the lower and higher bounds given as ``_low`` and - * ``_high``. All arguments are evaluated exactly once, and yield a constant - * expression if all arguments are constant as well. - * - * The comparison of their values is performed with the types given by the - * caller. It is the caller's responsibility to convert them to suitable types - * if necessary. - * - * Return: Clamped integer value. - */ -#define c_clamp(_x, _low, _high) C_CC_MACRO3(C_CLAMP, (_x), (_low), (_high)) -#define C_CLAMP(_x, _low, _high) ((_x) > (_high) ? (_high) : (_x) < (_low) ? (_low) : (_x)) - -/** - * c_div_round_up() - Calculate integer quotient but round up - * @_x: Dividend - * @_y: Divisor - * - * Calculates ``x / y`` but rounds up the result to the next integer. All - * arguments are evaluated exactly once, and yield a constant expression if all - * arguments are constant. - * - * **Note:** - * ``(x + y - 1) / y`` suffers from an integer overflow, even though the - * computation should be possible in the given type. Therefore, we use - * ``x / y + !!(x % y)``. Note that on most CPUs a division returns both the - * quotient and the remainder, so both should be equally fast. Furthermore, if - * the divisor is a power of two, the compiler will optimize it, anyway. - * - * The operationsare performed with the types given by the caller. It is the - * caller's responsibility to convert the arguments to suitable types if - * necessary. - * - * Return: The quotient is returned. - */ -#define c_div_round_up(_x, _y) C_CC_MACRO2(C_DIV_ROUND_UP, (_x), (_y)) -#define C_DIV_ROUND_UP(_x, _y) ((_x) / (_y) + !!((_x) % (_y))) - -/** - * c_align_to() - Align value to a multiple - * @_val: Value to align - * @_to: Align to multiple of this - * - * This aligns ``_val`` to a multiple of ``_to``. If ``_val`` is already a - * multiple of ``_to``, ``_val`` is returned unchanged. This function operates - * within the boundaries of the type of ``_val`` and ``_to``. Make sure to cast - * them if needed. - * - * The arguments of this macro are evaluated exactly once. If both arguments - * are a constant expression, this also yields a constant return value. - * - * Note that ``_to`` must be a power of 2, otherwise the behavior will not - * match expectations. - * - * Return: ``_val`` aligned to a multiple of ``_to``. - */ -#define c_align_to(_val, _to) C_CC_MACRO2(C_ALIGN_TO, (_val), (_to)) -#define C_ALIGN_TO(_val, _to) (((_val) + (_to) - 1) & ~((_to) - 1)) - -/** - * c_assert() - Runtime assertions - * @expr_result: Result of an expression - * - * This function behaves like the standard ``assert(3)`` macro. That is, if - * ``NDEBUG`` is defined, it is a no-op. In all other cases it will assert that - * the result of the passed expression is true. - * - * Unlike the standard ``assert(3)`` macro, this function always evaluates its - * argument. This means side-effects will always be evaluated! However, if the - * macro is used with constant expressions, the compiler will be able to - * optimize it away. - */ -#define c_assert(_x) ({ \ - const _c_unused_ bool c_assert_result = (_x); \ - assert(c_assert_result && #_x); \ - }) - -/** - * c_errno() - Return valid errno - * - * This helper should be used to shut up gcc if you know ``errno`` is valid - * (ie., ``errno`` is greater than 0). Instead of ``return -errno;``, use - * ``return -c_errno();`` It will suppress bogus gcc warnings in case it - * assumes ``errno`` might be 0 (or smaller than 0) and thus the caller's - * error-handling might not be triggered. - * - * This helper should be avoided whenever possible. However, occasionally we - * really want to shut up gcc (especially with static/inline functions). In - * those cases, gcc usually cannot deduce that some error paths are guaranteed - * to be taken. Hence, making the return value explicit allows gcc to better - * optimize the code. - * - * Note that you really should never use this helper to work around broken libc - * calls or syscalls, not setting 'errno' correctly. - * - * Return: Positive error code is returned. - */ -static inline int c_errno(void) { - return _c_likely_(errno > 0) ? errno : ENOTRECOVERABLE; -} - -/** - * c_memset() - Fill memory region with constant byte - * @p: Pointer to memory region, if non-empty - * @c: Value to fill with - * @n: Size of the memory region in bytes - * - * This function works like ``memset(3)`` if ``n`` is non-zero. If ``n`` is - * zero, this function is a no-op. Therefore, unlike ``memset(3)`` it is safe - * to call this function with ``NULL`` as ``p`` if ``n`` is 0. - * - * Return: ``p`` is returned. - */ -static inline void *c_memset(void *p, int c, size_t n) { - if (n > 0) - memset(p, c, n); - return p; -} - -/** - * c_memzero() - Clear memory area - * @p: Pointer to memory region, if non-empty - * @n: Size of the memory region in bytes - * - * Clear a memory area to 0. If the memory area is empty, this is a no-op. - * Similar to ``c_memset()``, this function allows ``p`` to be ``NULL`` if the - * area is empty. - * - * Return: ``p`` is returned. - */ -static inline void *c_memzero(void *p, size_t n) { - return c_memset(p, 0, n); -} - -/** - * c_memcpy() - Copy memory area - * @dst: Pointer to target area - * @src: Pointer to source area - * @n: Length of area to copy - * - * Copy the memory of size ``n`` from ``src`` to ``dst``, just as ``memcpy(3)`` - * does, except this function allows either to be ``NULL`` if ``n`` is zero. In - * the latter case, the operation is a no-op. - * - * Return: ``p`` is returned. - */ -static inline void *c_memcpy(void *dst, const void *src, size_t n) { - if (n > 0) - memcpy(dst, src, n); - return dst; -} - -/** - * c_memcmp() - Compare memory areas - * @s1: Pointer to one area - * @s2: Pointer to other area - * @n: Length of area to compare - * - * Compare the memory of size ``n`` of ``s1`` and ``s2``, just as ``memcmp(3)`` - * does, except this function allows either to be ``NULL`` if ``n`` is zero. - * - * Return: Comparison result for ordering is returned. - */ -static inline int c_memcmp(const void *s1, const void *s2, size_t n) { - if (n > 0) - return memcmp(s1, s2, n); - return 0; -} - -/** - * DOC: Common Destructors - * - * A set of destructors is provided which extends standard library destructors - * to adhere to some adjuvant rules. In particular, they return an invalid - * value of the particular object, rather than void. This allows direct - * assignment to any member-field and/or variable they are defined in, like: - * - * .. code-block:: c - * - * foo = c_free(foo); - * foo->bar = c_close(foo->bar); - * - * Furthermore, all those destructors can be safely called with the "INVALID" - * value as argument, and they will be a no-op. - */ -/**/ - -/** - * c_free() - Destructor-wrapper for free() - * @p: Value to pass to destructor, or NULL - * - * Wrapper around ``free()``, but always returns ``NULL``. - * - * Return: NULL is returned. - */ -static inline void *c_free(void *p) { - free(p); - return NULL; -} - -/** - * c_close() - Destructor-wrapper for close() - * @fd: File-descriptor to pass to destructor, or negative value - * - * Wrapper around ``close()``, but a no-op if a negative value is provided. - * Always returns ``-1``. - * - * Return: -1 is returned. - */ -static inline int c_close(int fd) { - if (fd >= 0) - close(fd); - return -1; -} - -/** - * c_fclose() - Destructor-wrapper for fclose() - * @f: File handle to pass to destructor, or NULL - * - * Wrapper around ``fclose()``, but a no-op if ``NULL`` is passed. Always - * returns ``NULL``. - * - * Return: NULL is returned. - */ -static inline FILE *c_fclose(FILE *f) { - if (f) - fclose(f); - return NULL; -} - -/** - * c_closedir() - Destructor-wrapper for closedir) - * @d: Directory handle to pass to destructor, or NULL - * - * Wrapper around ``closedir()``, but a no-op if ``NULL`` is passed. Always - * returns ``NULL``. - * - * Return: NULL is returned. - */ -static inline DIR *c_closedir(DIR *d) { - if (d) - closedir(d); - return NULL; -} - -/** - * DOC: Common Cleanup Helpers - * - * A set of helpers that aid in creating functions suitable for use with - * :c:macro:`_c_cleanup_()`. Furthermore, a collection of predefined cleanup - * functions of a set of standard library objects ready for use with - * :c:macro:`_c_cleanup_()`. - * Those cleanup helpers are always suffixed with a ``p``. - * - * The helpers that are provided are: - * - * - ``c_freep()``: Wrapper around :c:func:`c_free()`. - * - ``c_closep()``: Wrapper around :c:func:`c_close()`. - * - ``c_fclosep()``: Wrapper around :c:func:`c_fclose()`. - * - ``c_closedirp()``: Wrapper around :c:func:`c_closedir()`. - */ -/**/ - -/** - * C_DEFINE_CLEANUP() - Define cleanup helper - * @_type: Type of object to cleanup - * @_func: Destructor of the respective type - * - * Define a C static inline function that takes a single argument of type - * `_type` and calls `_func` on it, if its dereferenced value of its argument - * evaluates to true. Otherwise, it is a no-op. - * - * This macro allows for very simple and fast creation of cleanup helpers for - * use with ``_c_cleanup_()``, based on any destructor and type you provide to - * it. - */ -#define C_DEFINE_CLEANUP(_type, _func) \ - static inline void _func ## p(_type *p) { \ - if (*p) \ - _func(*p); \ - } struct c_internal_trailing_semicolon - -/** - * C_DEFINE_DIRECT_CLEANUP() - Define direct cleanup helper - * @_type: Type of object to cleanup - * @_func: Destructor of the respective type - * - * This works like :c:macro:`C_DEFINE_CLEANUP()` but does not check the - * dereferenced value of its argument. It always unconditionally invokes the - * destructor. - */ -#define C_DEFINE_DIRECT_CLEANUP(_type, _func) \ - static inline void _func ## p(_type *p) { \ - _func(*p); \ - } struct c_internal_trailing_semicolon - -static inline void c_freep(void *p) { - /* - * `foobar **` does not coerce to `void **`, so we need `void *` as - * argument type, and then we dereference manually. - */ - c_free(*(void **)p); -} - -C_DEFINE_DIRECT_CLEANUP(int, c_close); -C_DEFINE_CLEANUP(FILE *, c_fclose); -C_DEFINE_CLEANUP(DIR *, c_closedir); +#if defined(C_OS_LINUX) || defined(C_OS_MACOS) +# include +#endif #ifdef __cplusplus } diff --git a/src/core/NetworkManagerUtils.c b/src/core/NetworkManagerUtils.c index f5b7666b..6f4c60f8 100644 --- a/src/core/NetworkManagerUtils.c +++ b/src/core/NetworkManagerUtils.c @@ -419,8 +419,8 @@ route_compare(NMIPRoute *route1, NMIPRoute *route2, gint64 default_metric) nm_assert_not_reached(); if (!inet_pton(family, nm_ip_route_get_dest(route2), &a2)) nm_assert_not_reached(); - nm_utils_ipx_address_clear_host_address(family, &a1, NULL, plen); - nm_utils_ipx_address_clear_host_address(family, &a2, NULL, plen); + nm_ip_addr_clear_host_address(family, &a1, NULL, plen); + nm_ip_addr_clear_host_address(family, &a2, NULL, plen); NM_CMP_DIRECT_MEMCMP(&a1, &a2, nm_utils_addr_family_to_size(family)); return 0; @@ -1382,11 +1382,16 @@ nm_utils_ip_route_attribute_to_platform(int addr_family, GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_TOS, r4->tos, BYTE, byte, 0); GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_SCOPE, scope, BYTE, byte, RT_SCOPE_NOWHERE); + GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_WEIGHT, r4->weight, UINT32, uint32, 0); r4->scope_inv = nm_platform_route_scope_inv(scope); } + /* Note that for IPv4 routes in kernel, the onlink flag can be set for + * each next hop separately (rtnh_flags). Not for NetworkManager. We can + * only merge routes as ECMP routes (when setting a weight) if they all + * share the same onlink flag. See NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID. + * That simplifies the code. */ GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_ONLINK, onlink, BOOLEAN, boolean, FALSE); - r->r_rtm_flags = ((onlink) ? (unsigned) RTNH_F_ONLINK : 0u); GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_ADVMSS, r->mss, UINT32, uint32, 0); @@ -1418,11 +1423,11 @@ nm_utils_ip_route_attribute_to_platform(int addr_family, && g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)) { int prefix; - if (nm_utils_parse_inaddr_prefix_bin(addr_family, - g_variant_get_string(variant, NULL), - NULL, - &addr, - &prefix)) { + if (nm_inet_parse_with_prefix_bin(addr_family, + g_variant_get_string(variant, NULL), + NULL, + &addr, + &prefix)) { if (prefix < 0) prefix = 128; r6->src = addr.addr6; @@ -1444,7 +1449,7 @@ nm_utils_ip_addresses_to_dbus(int addr_family, const int IS_IPv4 = NM_IS_IPv4(addr_family); GVariantBuilder builder_data; GVariantBuilder builder_legacy; - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + char addr_str[NM_INET_ADDRSTRLEN]; NMDedupMultiIter iter; const NMPObject *obj; guint i; @@ -1479,8 +1484,7 @@ nm_utils_ip_addresses_to_dbus(int addr_family, &addr_builder, "{sv}", "address", - g_variant_new_string( - nm_utils_inet_ntop(addr_family, address->ax.address_ptr, addr_str))); + g_variant_new_string(nm_inet_ntop(addr_family, address->ax.address_ptr, addr_str))); g_variant_builder_add(&addr_builder, "{sv}", @@ -1497,11 +1501,10 @@ nm_utils_ip_addresses_to_dbus(int addr_family, p = &address->a6.peer_address; } if (p) { - g_variant_builder_add( - &addr_builder, - "{sv}", - "peer", - g_variant_new_string(nm_utils_inet_ntop(addr_family, p, addr_str))); + g_variant_builder_add(&addr_builder, + "{sv}", + "peer", + g_variant_new_string(nm_inet_ntop(addr_family, p, addr_str))); } if (IS_IPv4) { @@ -1559,7 +1562,7 @@ nm_utils_ip_routes_to_dbus(int addr_family, const NMPObject *obj; GVariantBuilder builder_data; GVariantBuilder builder_legacy; - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + char addr_str[NM_INET_ADDRSTRLEN]; nm_assert_addr_family(addr_family); @@ -1580,14 +1583,12 @@ nm_utils_ip_routes_to_dbus(int addr_family, nm_assert(r); nm_assert(r->rx.plen <= 8u * nm_utils_addr_family_to_size(addr_family)); nm_assert(!IS_IPv4 - || r->r4.network - == nm_utils_ip4_address_clear_host_address(r->r4.network, r->r4.plen)); - nm_assert( - IS_IPv4 - || (memcmp(&r->r6.network, - nm_utils_ip6_address_clear_host_address(&n, &r->r6.network, r->r6.plen), - sizeof(n)) - == 0)); + || r->r4.network == nm_ip4_addr_clear_host_address(r->r4.network, r->r4.plen)); + nm_assert(IS_IPv4 + || (memcmp(&r->r6.network, + nm_ip6_addr_clear_host_address(&n, &r->r6.network, r->r6.plen), + sizeof(n)) + == 0)); if (r->rx.type_coerced != nm_platform_route_type_coerce(RTN_UNICAST)) continue; @@ -1602,7 +1603,7 @@ nm_utils_ip_routes_to_dbus(int addr_family, &route_builder, "{sv}", "dest", - g_variant_new_string(nm_utils_inet_ntop(addr_family, r->rx.network_ptr, addr_str))); + g_variant_new_string(nm_inet_ntop(addr_family, r->rx.network_ptr, addr_str))); g_variant_builder_add(&route_builder, "{sv}", @@ -1615,7 +1616,7 @@ nm_utils_ip_routes_to_dbus(int addr_family, &route_builder, "{sv}", "next-hop", - g_variant_new_string(nm_utils_inet_ntop(addr_family, gateway, addr_str))); + g_variant_new_string(nm_inet_ntop(addr_family, gateway, addr_str))); } g_variant_builder_add(&route_builder, @@ -1680,7 +1681,7 @@ nm_utils_platform_capture_ip_setting(NMPlatform *platform, NMDedupMultiIter iter; const NMPObject *obj; const char *method = NULL; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; const NMPlatformIPXRoute *best_default_route = NULL; s_ip = @@ -1782,13 +1783,13 @@ nm_utils_platform_capture_ip_setting(NMPlatform *platform, } if (best_default_route && nm_setting_ip_config_get_num_addresses(s_ip) > 0) { - g_object_set(s_ip, - NM_SETTING_IP_CONFIG_GATEWAY, - nm_utils_inet_ntop( - addr_family, + g_object_set( + s_ip, + NM_SETTING_IP_CONFIG_GATEWAY, + nm_inet_ntop(addr_family, nm_platform_ip_route_get_gateway(addr_family, &best_default_route->rx), sbuf), - NULL); + NULL); } return NM_SETTING(g_steal_pointer(&s_ip)); @@ -1839,7 +1840,7 @@ nm_platform_setup(NMPlatform *instance) * Returns: (transfer none): The #NMPlatform singleton reference. */ NMPlatform * -nm_platform_get() +nm_platform_get(void) { g_assert(singleton_instance); @@ -1851,11 +1852,11 @@ nm_platform_get() void nm_linux_platform_setup(void) { - nm_platform_setup(nm_linux_platform_new(FALSE, FALSE, FALSE)); + nm_platform_setup(nm_linux_platform_new(NULL, FALSE, FALSE, FALSE)); } void nm_linux_platform_setup_with_tc_cache(void) { - nm_platform_setup(nm_linux_platform_new(FALSE, FALSE, TRUE)); + nm_platform_setup(nm_linux_platform_new(NULL, FALSE, FALSE, TRUE)); } diff --git a/src/core/devices/adsl/nm-atm-manager.c b/src/core/devices/adsl/nm-atm-manager.c index cc8d37e6..f3c0d367 100644 --- a/src/core/devices/adsl/nm-atm-manager.c +++ b/src/core/devices/adsl/nm-atm-manager.c @@ -17,7 +17,8 @@ /*****************************************************************************/ #define NM_TYPE_ATM_MANAGER (nm_atm_manager_get_type()) -#define NM_ATM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_ATM_MANAGER, NMAtmManager)) +#define NM_ATM_MANAGER(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_ATM_MANAGER, NMAtmManager)) #define NM_ATM_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_ATM_MANAGER, NMAtmManagerClass)) #define NM_IS_ATM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_ATM_MANAGER)) diff --git a/src/core/devices/adsl/nm-device-adsl.c b/src/core/devices/adsl/nm-device-adsl.c index 79705c28..fcd16b1c 100644 --- a/src/core/devices/adsl/nm-device-adsl.c +++ b/src/core/devices/adsl/nm-device-adsl.c @@ -214,12 +214,12 @@ error: } static void -link_changed_cb(NMPlatform *platform, - int obj_type_i, - int ifindex, - NMPlatformLink *info, - int change_type_i, - NMDeviceAdsl *self) +link_changed_cb(NMPlatform *platform, + int obj_type_i, + int ifindex, + const NMPlatformLink *info, + int change_type_i, + NMDeviceAdsl *self) { const NMPlatformSignalChangeType change_type = change_type_i; diff --git a/src/core/devices/adsl/nm-device-adsl.h b/src/core/devices/adsl/nm-device-adsl.h index c5c18901..287a1401 100644 --- a/src/core/devices/adsl/nm-device-adsl.h +++ b/src/core/devices/adsl/nm-device-adsl.h @@ -10,7 +10,8 @@ #include "devices/nm-device.h" #define NM_TYPE_DEVICE_ADSL (nm_device_adsl_get_type()) -#define NM_DEVICE_ADSL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_ADSL, NMDeviceAdsl)) +#define NM_DEVICE_ADSL(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_ADSL, NMDeviceAdsl)) #define NM_DEVICE_ADSL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_ADSL, NMDeviceAdslClass)) #define NM_IS_DEVICE_ADSL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_ADSL)) diff --git a/src/core/devices/bluetooth/nm-bluez-manager.c b/src/core/devices/bluetooth/nm-bluez-manager.c index 6bc00faf..ac26ede8 100644 --- a/src/core/devices/bluetooth/nm-bluez-manager.c +++ b/src/core/devices/bluetooth/nm-bluez-manager.c @@ -1010,8 +1010,9 @@ _network_server_get_bluez_manager(const NMBtVTableNetworkServer *vtable_network_ { NMBluezManager *self; - self = (NMBluezManager *) (((char *) vtable_network_server) - - G_STRUCT_OFFSET(NMBluezManager, _priv.vtable_network_server)); + self = NM_CAST_ALIGN(NMBluezManager, + (((char *) vtable_network_server) + - G_STRUCT_OFFSET(NMBluezManager, _priv.vtable_network_server))); g_return_val_if_fail(NM_IS_BLUEZ_MANAGER(self), NULL); diff --git a/src/core/devices/bluetooth/nm-bluez-manager.h b/src/core/devices/bluetooth/nm-bluez-manager.h index 299f5c3b..7c823c64 100644 --- a/src/core/devices/bluetooth/nm-bluez-manager.h +++ b/src/core/devices/bluetooth/nm-bluez-manager.h @@ -8,7 +8,7 @@ #define NM_TYPE_BLUEZ_MANAGER (nm_bluez_manager_get_type()) #define NM_BLUEZ_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManager)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManager)) #define NM_BLUEZ_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass)) #define NM_IS_BLUEZ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_BLUEZ_MANAGER)) diff --git a/src/core/devices/bluetooth/nm-device-bt.h b/src/core/devices/bluetooth/nm-device-bt.h index 59457fba..bf17e91f 100644 --- a/src/core/devices/bluetooth/nm-device-bt.h +++ b/src/core/devices/bluetooth/nm-device-bt.h @@ -9,7 +9,7 @@ #include "devices/nm-device.h" #define NM_TYPE_DEVICE_BT (nm_device_bt_get_type()) -#define NM_DEVICE_BT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_BT, NMDeviceBt)) +#define NM_DEVICE_BT(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_BT, NMDeviceBt)) #define NM_DEVICE_BT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_BT, NMDeviceBtClass)) #define NM_IS_DEVICE_BT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_BT)) diff --git a/src/core/devices/nm-device-6lowpan.c b/src/core/devices/nm-device-6lowpan.c index d6543356..870a1c14 100644 --- a/src/core/devices/nm-device-6lowpan.c +++ b/src/core/devices/nm-device-6lowpan.c @@ -21,7 +21,6 @@ /*****************************************************************************/ typedef struct { - gulong parent_state_id; } NMDevice6LowpanPrivate; struct _NMDevice6Lowpan { @@ -40,21 +39,6 @@ G_DEFINE_TYPE(NMDevice6Lowpan, nm_device_6lowpan, NM_TYPE_DEVICE) /*****************************************************************************/ -static void -parent_state_changed(NMDevice *parent, - NMDeviceState new_state, - NMDeviceState old_state, - NMDeviceStateReason reason, - gpointer user_data) -{ - NMDevice6Lowpan *self = NM_DEVICE_6LOWPAN(user_data); - - nm_device_set_unmanaged_by_flags(NM_DEVICE(self), - NM_UNMANAGED_PARENT, - !nm_device_get_managed(parent, FALSE), - reason); -} - static void parent_changed_notify(NMDevice *device, int old_ifindex, @@ -62,30 +46,9 @@ parent_changed_notify(NMDevice *device, int new_ifindex, NMDevice *new_parent) { - NMDevice6Lowpan *self = NM_DEVICE_6LOWPAN(device); - NMDevice6LowpanPrivate *priv = NM_DEVICE_6LOWPAN_GET_PRIVATE(self); - NM_DEVICE_CLASS(nm_device_6lowpan_parent_class) ->parent_changed_notify(device, old_ifindex, old_parent, new_ifindex, new_parent); - /* note that @self doesn't have to clear @parent_state_id on dispose, - * because NMDevice's dispose() will unset the parent, which in turn calls - * parent_changed_notify(). */ - nm_clear_g_signal_handler(old_parent, &priv->parent_state_id); - - if (new_parent) { - priv->parent_state_id = g_signal_connect(new_parent, - NM_DEVICE_STATE_CHANGED, - G_CALLBACK(parent_state_changed), - device); - - /* Set parent-dependent unmanaged flag */ - nm_device_set_unmanaged_by_flags(device, - NM_UNMANAGED_PARENT, - !nm_device_get_managed(new_parent, FALSE), - NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED); - } - if (new_ifindex > 0) { /* Recheck availability now that the parent has changed */ nm_device_queue_recheck_available(device, @@ -272,7 +235,7 @@ nm_device_6lowpan_class_init(NMDevice6LowpanClass *klass) #define NM_TYPE_6LOWPAN_DEVICE_FACTORY (nm_6lowpan_device_factory_get_type()) #define NM_6LOWPAN_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_6LOWPAN_DEVICE_FACTORY, NM6LowpanDeviceFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_6LOWPAN_DEVICE_FACTORY, NM6LowpanDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-6lowpan.h b/src/core/devices/nm-device-6lowpan.h index 34ea3c8d..bc6f77c4 100644 --- a/src/core/devices/nm-device-6lowpan.h +++ b/src/core/devices/nm-device-6lowpan.h @@ -10,7 +10,7 @@ #define NM_TYPE_DEVICE_6LOWPAN (nm_device_6lowpan_get_type()) #define NM_DEVICE_6LOWPAN(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_6LOWPAN, NMDevice6Lowpan)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_6LOWPAN, NMDevice6Lowpan)) #define NM_DEVICE_6LOWPAN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_6LOWPAN, NMDevice6LowpanClass)) #define NM_IS_DEVICE_6LOWPAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_6LOWPAN)) diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c index 10765b60..0b0697e3 100644 --- a/src/core/devices/nm-device-bond.c +++ b/src/core/devices/nm-device-bond.c @@ -20,6 +20,7 @@ #include "libnm-core-intern/nm-core-internal.h" #include "nm-manager.h" #include "nm-setting-bond-port.h" +#include "nm-bond-manager.h" #define _NMLOG_DEVICE_TYPE NMDeviceBond #include "nm-device-logging.h" @@ -59,7 +60,8 @@ /*****************************************************************************/ struct _NMDeviceBond { - NMDevice parent; + NMDevice parent; + NMBondManager *bond_manager; }; struct _NMDeviceBondClass { @@ -178,7 +180,9 @@ update_connection(NMDevice *device, NMConnection *connection) gs_free char *value = NULL; char *p; - if (NM_IN_STRSET(option, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE)) + if (NM_IN_STRSET(option, + NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, + NM_SETTING_BOND_OPTION_BALANCE_SLB)) continue; value = @@ -224,9 +228,10 @@ controller_update_port_connection(NMDevice *self, GError **error) { NMSettingBondPort *s_port; - int ifindex_port = nm_device_get_ifindex(port); - uint queue_id = NM_BOND_PORT_QUEUE_ID_DEF; - gs_free char *queue_id_str = NULL; + int ifindex_port = nm_device_get_ifindex(port); + NMConnection *applied_connection = nm_device_get_applied_connection(self); + uint queue_id = NM_BOND_PORT_QUEUE_ID_DEF; + gs_free char *queue_id_str = NULL; g_return_val_if_fail(ifindex_port > 0, FALSE); @@ -243,7 +248,7 @@ controller_update_port_connection(NMDevice *self, g_object_set(nm_connection_get_setting_connection(connection), NM_SETTING_CONNECTION_MASTER, - nm_device_get_iface(self), + nm_connection_get_uuid(applied_connection), NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME, NULL); @@ -274,10 +279,10 @@ set_arp_targets(NMDevice *device, const char *cur_arp_ip_target, const char *new in_addr_t a4; s = new_strv[i]; - if (nm_utils_parse_inaddr_bin(AF_INET, s, NULL, &a4)) { + if (nm_inet_parse_bin(AF_INET, s, NULL, &a4)) { char sbuf[INET_ADDRSTRLEN]; - _nm_utils_inet4_ntop(a4, sbuf); + nm_inet4_ntop(a4, sbuf); if (!nm_streq(s, sbuf)) { if (!free_list) free_list = g_ptr_array_new_with_free_func(g_free); @@ -368,7 +373,7 @@ _bond_arp_ip_target_to_platform(const char *value, in_addr_t out[static NM_BOND_ for (i = 0; ip[i]; i++) { if (added > NM_BOND_MAX_ARP_TARGETS - 1) break; - if (!nm_utils_parse_inaddr_bin(AF_INET, ip[i], NULL, &in_a)) + if (!nm_inet_parse_bin(AF_INET, ip[i], NULL, &in_a)) nm_assert_not_reached(); /* verify() already validated the IP addresses */ out[added++] = in_a; @@ -460,10 +465,97 @@ _platform_lnk_bond_init_from_setting(NMSettingBond *s_bond, NMPlatformLnkBond *p props->tlb_dynamic_lb_has = NM_IN_SET(props->mode, NM_BOND_MODE_TLB, NM_BOND_MODE_ALB); } +static void +_balance_slb_cb(NMBondManager *bond_manager, NMBondManagerEventType event_type, gpointer user_data) +{ + NMDevice *device = user_data; + NMDeviceBond *self = NM_DEVICE_BOND(device); + + nm_assert(NM_IS_DEVICE_BOND(self)); + nm_assert(self->bond_manager == bond_manager); + + switch (event_type) { + case NM_BOND_MANAGER_EVENT_TYPE_STATE: + switch (nm_bond_manager_get_state(bond_manager)) { + case NM_OPTION_BOOL_FALSE: + if (nm_device_get_state(device) <= NM_DEVICE_STATE_ACTIVATED) { + _LOGD(LOGD_BOND, "balance-slb: failed"); + nm_device_state_changed(device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_CONFIG_FAILED); + } + return; + case NM_OPTION_BOOL_TRUE: + if (nm_device_get_state(device) <= NM_DEVICE_STATE_ACTIVATED + && nm_device_devip_get_state(device, AF_UNSPEC) <= NM_DEVICE_IP_STATE_PENDING) { + nm_device_devip_set_state(device, AF_UNSPEC, NM_DEVICE_IP_STATE_READY, NULL); + } + return; + case NM_OPTION_BOOL_DEFAULT: + if (nm_device_get_state(device) <= NM_DEVICE_STATE_ACTIVATED + && nm_device_devip_get_state(device, AF_UNSPEC) == NM_DEVICE_IP_STATE_READY) { + /* We are again busy. We can also go back to "pending" from "ready". + * If ip-config state is not yet complete, this will further delay it. + * Otherwise, it should have no effect. */ + nm_device_devip_set_state(device, AF_UNSPEC, NM_DEVICE_IP_STATE_PENDING, NULL); + } + return; + } + nm_assert_not_reached(); + return; + } + + nm_assert_not_reached(); +} + +static void +_balance_slb_setup(NMDeviceBond *self, NMConnection *connection) +{ + int ifindex = nm_device_get_ifindex(NM_DEVICE(self)); + gboolean balance_slb = FALSE; + const char *uuid; + NMSettingBond *s_bond; + + if (ifindex > 0 && connection && (s_bond = nm_connection_get_setting_bond(connection))) + balance_slb = _v_intbool(s_bond, NM_SETTING_BOND_OPTION_BALANCE_SLB); + + if (!balance_slb) { + if (nm_clear_pointer(&self->bond_manager, nm_bond_manager_destroy)) { + _LOGD(LOGD_BOND, "balance-slb: stopped"); + nm_device_devip_set_state(NM_DEVICE(self), AF_UNSPEC, NM_DEVICE_IP_STATE_NONE, NULL); + } + return; + } + + uuid = nm_connection_get_uuid(connection); + + if (self->bond_manager) { + if (nm_bond_manager_get_ifindex(self->bond_manager) == ifindex + && nm_streq0(nm_bond_manager_get_connection_uuid(self->bond_manager), uuid)) { + _LOGD(LOGD_BOND, "balance-slb: reapply"); + nm_bond_manager_reapply(self->bond_manager); + return; + } + nm_clear_pointer(&self->bond_manager, nm_bond_manager_destroy); + _LOGD(LOGD_BOND, "balance-slb: restart"); + } + + _LOGD(LOGD_BOND, "balance-slb: start"); + if (nm_device_devip_get_state(NM_DEVICE(self), AF_UNSPEC) < NM_DEVICE_IP_STATE_PENDING) + nm_device_devip_set_state(NM_DEVICE(self), AF_UNSPEC, NM_DEVICE_IP_STATE_PENDING, NULL); + self->bond_manager = nm_bond_manager_new(nm_device_get_platform(NM_DEVICE(self)), + ifindex, + uuid, + _balance_slb_cb, + self); + nm_assert(nm_bond_manager_get_state(self->bond_manager) == NM_OPTION_BOOL_DEFAULT); +} + static NMActStageReturn act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) { - NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; + NMDeviceBond *self = NM_DEVICE_BOND(device); + NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; NMConnection *connection; NMSettingBond *s_bond; NMPlatformLnkBond props; @@ -476,6 +568,14 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) s_bond = nm_connection_get_setting_bond(connection); g_return_val_if_fail(s_bond, NM_ACT_STAGE_RETURN_FAILURE); + if (nm_device_sys_iface_state_is_external(device)) + return NM_ACT_STAGE_RETURN_SUCCESS; + + _balance_slb_setup(self, connection); + + if (nm_device_sys_iface_state_is_external_or_assume(device)) + return NM_ACT_STAGE_RETURN_SUCCESS; + _platform_lnk_bond_init_from_setting(s_bond, &props); /* Interface must be down to set bond options */ @@ -684,7 +784,7 @@ can_reapply_change(NMDevice *device, const char *name = *option_list; /* We support changes to these */ - if (NM_IN_STRSET(name, OPTIONS_REAPPLY_FULL)) + if (NM_IN_STRSET(name, OPTIONS_REAPPLY_FULL, NM_SETTING_BOND_OPTION_BALANCE_SLB)) continue; /* Reject any other changes */ @@ -730,6 +830,16 @@ reapply_connection(NMDevice *device, NMConnection *con_old, NMConnection *con_ne set_bond_arp_ip_targets(device, s_bond); set_bond_attrs_or_default(device, s_bond, NM_MAKE_STRV(OPTIONS_REAPPLY_SUBSET)); + + _balance_slb_setup(self, con_new); +} + +static void +deactivate(NMDevice *device) +{ + NMDeviceBond *self = NM_DEVICE_BOND(device); + + _balance_slb_setup(self, NULL); } /*****************************************************************************/ @@ -768,20 +878,22 @@ nm_device_bond_class_init(NMDeviceBondClass *klass) device_class->update_connection = update_connection; device_class->master_update_slave_connection = controller_update_port_connection; - device_class->create_and_realize = create_and_realize; - device_class->act_stage1_prepare = act_stage1_prepare; + device_class->create_and_realize = create_and_realize; + device_class->act_stage1_prepare = act_stage1_prepare; + device_class->act_stage1_prepare_also_for_external_or_assume = TRUE; device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired; device_class->attach_port = attach_port; device_class->detach_port = detach_port; device_class->can_reapply_change = can_reapply_change; device_class->reapply_connection = reapply_connection; + device_class->deactivate = deactivate; } /*****************************************************************************/ #define NM_TYPE_BOND_DEVICE_FACTORY (nm_bond_device_factory_get_type()) #define NM_BOND_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_BOND_DEVICE_FACTORY, NMBondDeviceFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_BOND_DEVICE_FACTORY, NMBondDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-bond.h b/src/core/devices/nm-device-bond.h index 19301793..083189bb 100644 --- a/src/core/devices/nm-device-bond.h +++ b/src/core/devices/nm-device-bond.h @@ -9,7 +9,8 @@ #include "nm-device.h" #define NM_TYPE_DEVICE_BOND (nm_device_bond_get_type()) -#define NM_DEVICE_BOND(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_BOND, NMDeviceBond)) +#define NM_DEVICE_BOND(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_BOND, NMDeviceBond)) #define NM_DEVICE_BOND_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_BOND, NMDeviceBondClass)) #define NM_IS_DEVICE_BOND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_BOND)) diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c index 31cf361e..c5ce34c2 100644 --- a/src/core/devices/nm-device-bridge.c +++ b/src/core/devices/nm-device-bridge.c @@ -679,9 +679,10 @@ master_update_slave_connection(NMDevice *device, NMDeviceBridge *self = NM_DEVICE_BRIDGE(device); NMSettingConnection *s_con; NMSettingBridgePort *s_port; - int ifindex_slave = nm_device_get_ifindex(slave); - const char *iface = nm_device_get_iface(device); - const Option *option; + int ifindex_slave = nm_device_get_ifindex(slave); + NMConnection *applied_connection = nm_device_get_applied_connection(device); + + const Option *option; g_return_val_if_fail(ifindex_slave > 0, FALSE); @@ -717,7 +718,7 @@ master_update_slave_connection(NMDevice *device, g_object_set(s_con, NM_SETTING_CONNECTION_MASTER, - iface, + nm_connection_get_uuid(applied_connection), NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, NULL); @@ -1270,7 +1271,7 @@ nm_device_bridge_class_init(NMDeviceBridgeClass *klass) #define NM_TYPE_BRIDGE_DEVICE_FACTORY (nm_bridge_device_factory_get_type()) #define NM_BRIDGE_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_BRIDGE_DEVICE_FACTORY, NMBridgeDeviceFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_BRIDGE_DEVICE_FACTORY, NMBridgeDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-bridge.h b/src/core/devices/nm-device-bridge.h index 32437947..6d9f1614 100644 --- a/src/core/devices/nm-device-bridge.h +++ b/src/core/devices/nm-device-bridge.h @@ -10,7 +10,7 @@ #define NM_TYPE_DEVICE_BRIDGE (nm_device_bridge_get_type()) #define NM_DEVICE_BRIDGE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_BRIDGE, NMDeviceBridge)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_BRIDGE, NMDeviceBridge)) #define NM_DEVICE_BRIDGE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_BRIDGE, NMDeviceBridgeClass)) #define NM_IS_DEVICE_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_BRIDGE)) diff --git a/src/core/devices/nm-device-dummy.c b/src/core/devices/nm-device-dummy.c index de701a0d..36a0ee6f 100644 --- a/src/core/devices/nm-device-dummy.c +++ b/src/core/devices/nm-device-dummy.c @@ -136,7 +136,7 @@ nm_device_dummy_class_init(NMDeviceDummyClass *klass) #define NM_TYPE_DUMMY_DEVICE_FACTORY (nm_dummy_device_factory_get_type()) #define NM_DUMMY_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DUMMY_DEVICE_FACTORY, NMDummyDeviceFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DUMMY_DEVICE_FACTORY, NMDummyDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-dummy.h b/src/core/devices/nm-device-dummy.h index 2845b4eb..9e550887 100644 --- a/src/core/devices/nm-device-dummy.h +++ b/src/core/devices/nm-device-dummy.h @@ -10,7 +10,7 @@ #define NM_TYPE_DEVICE_DUMMY (nm_device_dummy_get_type()) #define NM_DEVICE_DUMMY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_DUMMY, NMDeviceDummy)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_DUMMY, NMDeviceDummy)) #define NM_DEVICE_DUMMY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_DUMMY, NMDeviceDummyClass)) #define NM_IS_DEVICE_DUMMY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_DUMMY)) diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c index 4788cdc6..97cf84a1 100644 --- a/src/core/devices/nm-device-ethernet.c +++ b/src/core/devices/nm-device-ethernet.c @@ -1735,11 +1735,10 @@ new_default_connection(NMDevice *self) /* Create a stable UUID. The UUID is also the Network_ID for stable-privacy addr-gen-mode, * thus when it changes we will also generate different IPv6 addresses. */ - uuid = nm_uuid_generate_from_strings("default-wired", - nm_utils_machine_id_str(), - defname, - perm_hw_addr ?: iface, - NULL); + uuid = nm_uuid_generate_from_strings_old("default-wired", + nm_utils_machine_id_str(), + defname, + perm_hw_addr ?: iface); g_object_set(setting, NM_SETTING_CONNECTION_ID, @@ -2114,8 +2113,10 @@ nm_device_ethernet_class_init(NMDeviceEthernetClass *klass) /*****************************************************************************/ #define NM_TYPE_ETHERNET_DEVICE_FACTORY (nm_ethernet_device_factory_get_type()) -#define NM_ETHERNET_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_ETHERNET_DEVICE_FACTORY, NMEthernetDeviceFactory)) +#define NM_ETHERNET_DEVICE_FACTORY(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), \ + NM_TYPE_ETHERNET_DEVICE_FACTORY, \ + NMEthernetDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-ethernet.h b/src/core/devices/nm-device-ethernet.h index 6e134d71..6c4acb82 100644 --- a/src/core/devices/nm-device-ethernet.h +++ b/src/core/devices/nm-device-ethernet.h @@ -11,7 +11,7 @@ #define NM_TYPE_DEVICE_ETHERNET (nm_device_ethernet_get_type()) #define NM_DEVICE_ETHERNET(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernet)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernet)) #define NM_DEVICE_ETHERNET_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass)) #define NM_IS_DEVICE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_ETHERNET)) diff --git a/src/core/devices/nm-device-factory.c b/src/core/devices/nm-device-factory.c index 41e9480f..2f0ae2e8 100644 --- a/src/core/devices/nm-device-factory.c +++ b/src/core/devices/nm-device-factory.c @@ -398,6 +398,7 @@ nm_device_factory_manager_load_factories(NMDeviceFactoryManagerFactoryFunc callb _ADD_INTERNAL(nm_ethernet_device_factory_get_type); _ADD_INTERNAL(nm_infiniband_device_factory_get_type); _ADD_INTERNAL(nm_ip_tunnel_device_factory_get_type); + _ADD_INTERNAL(nm_loopback_device_factory_get_type); _ADD_INTERNAL(nm_macsec_device_factory_get_type); _ADD_INTERNAL(nm_macvlan_device_factory_get_type); _ADD_INTERNAL(nm_ppp_device_factory_get_type); diff --git a/src/core/devices/nm-device-factory.h b/src/core/devices/nm-device-factory.h index 51d64767..ac5ae05f 100644 --- a/src/core/devices/nm-device-factory.h +++ b/src/core/devices/nm-device-factory.h @@ -17,7 +17,7 @@ #define NM_TYPE_DEVICE_FACTORY (nm_device_factory_get_type()) #define NM_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_FACTORY, NMDeviceFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_FACTORY, NMDeviceFactory)) #define NM_DEVICE_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_FACTORY, NMDeviceFactoryClass)) #define NM_IS_DEVICE_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_FACTORY)) @@ -208,7 +208,8 @@ NMDevice *nm_device_factory_create_device(NMDeviceFactory *factory, \ NM_DEVICE_FACTORY_DECLARE_TYPES(st_code) \ \ - static void nm_##lower##_device_factory_init(NM##mixed##DeviceFactory *self) {} \ + static void nm_##lower##_device_factory_init(NM##mixed##DeviceFactory *self) \ + {} \ \ static void nm_##lower##_device_factory_class_init(NM##mixed##DeviceFactoryClass *klass) \ { \ diff --git a/src/core/devices/nm-device-generic.h b/src/core/devices/nm-device-generic.h index 48c43523..f06a5bdc 100644 --- a/src/core/devices/nm-device-generic.h +++ b/src/core/devices/nm-device-generic.h @@ -10,7 +10,7 @@ #define NM_TYPE_DEVICE_GENERIC (nm_device_generic_get_type()) #define NM_DEVICE_GENERIC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_GENERIC, NMDeviceGeneric)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_GENERIC, NMDeviceGeneric)) #define NM_DEVICE_GENERIC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_GENERIC, NMDeviceGenericClass)) #define NM_IS_DEVICE_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_GENERIC)) diff --git a/src/core/devices/nm-device-infiniband.c b/src/core/devices/nm-device-infiniband.c index 6b6aef86..d025931e 100644 --- a/src/core/devices/nm-device-infiniband.c +++ b/src/core/devices/nm-device-infiniband.c @@ -403,10 +403,10 @@ nm_device_infiniband_class_init(NMDeviceInfinibandClass *klass) /*****************************************************************************/ #define NM_TYPE_INFINIBAND_DEVICE_FACTORY (nm_infiniband_device_factory_get_type()) -#define NM_INFINIBAND_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), \ - NM_TYPE_INFINIBAND_DEVICE_FACTORY, \ - NMInfinibandDeviceFactory)) +#define NM_INFINIBAND_DEVICE_FACTORY(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), \ + NM_TYPE_INFINIBAND_DEVICE_FACTORY, \ + NMInfinibandDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-infiniband.h b/src/core/devices/nm-device-infiniband.h index 69994a2d..5c915dd4 100644 --- a/src/core/devices/nm-device-infiniband.h +++ b/src/core/devices/nm-device-infiniband.h @@ -10,7 +10,7 @@ #define NM_TYPE_DEVICE_INFINIBAND (nm_device_infiniband_get_type()) #define NM_DEVICE_INFINIBAND(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfiniband)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfiniband)) #define NM_DEVICE_INFINIBAND_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfinibandClass)) #define NM_IS_DEVICE_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_INFINIBAND)) diff --git a/src/core/devices/nm-device-ip-tunnel.c b/src/core/devices/nm-device-ip-tunnel.c index 02680dd7..5e5ba632 100644 --- a/src/core/devices/nm-device-ip-tunnel.c +++ b/src/core/devices/nm-device-ip-tunnel.c @@ -39,6 +39,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDeviceIPTunnel, PROP_OUTPUT_KEY, PROP_ENCAPSULATION_LIMIT, PROP_FLOW_LABEL, + PROP_FWMARK, PROP_FLAGS, ); typedef struct { @@ -53,6 +54,7 @@ typedef struct { char *output_key; guint8 encap_limit; guint32 flow_label; + guint32 fwmark; NMIPTunnelFlags flags; } NMDeviceIPTunnelPrivate; @@ -107,12 +109,12 @@ address_equal_pp(int addr_family, const char *a, const char *b) nm_assert_addr_family(addr_family); if (a) { - if (!nm_utils_parse_inaddr_bin(addr_family, a, NULL, &addr_a_val)) + if (!nm_inet_parse_bin(addr_family, a, NULL, &addr_a_val)) nm_assert_not_reached(); addr_a = &addr_a_val; } if (b) { - if (!nm_utils_parse_inaddr_bin(addr_family, b, NULL, &addr_b_val)) + if (!nm_inet_parse_bin(addr_family, b, NULL, &addr_b_val)) nm_assert_not_reached(); addr_b = &addr_b_val; } @@ -125,7 +127,7 @@ address_set(int addr_family, char **p_addr, const NMIPAddr *addr_new) { nm_assert_addr_family(addr_family); nm_assert(p_addr); - nm_assert(!*p_addr || nm_utils_ipaddr_is_normalized(addr_family, *p_addr)); + nm_assert(!*p_addr || nm_inet_is_normalized(addr_family, *p_addr)); if (!addr_new || nm_ip_addr_is_null(addr_family, addr_new)) { if (nm_clear_g_free(p_addr)) @@ -136,7 +138,7 @@ address_set(int addr_family, char **p_addr, const NMIPAddr *addr_new) if (*p_addr) { NMIPAddr addr_val; - if (!nm_utils_parse_inaddr_bin(addr_family, *p_addr, NULL, &addr_val)) + if (!nm_inet_parse_bin(addr_family, *p_addr, NULL, &addr_val)) nm_assert_not_reached(); if (nm_ip_addr_equal(addr_family, &addr_val, addr_new)) @@ -145,7 +147,7 @@ address_set(int addr_family, char **p_addr, const NMIPAddr *addr_new) g_free(*p_addr); } - *p_addr = nm_utils_inet_ntop_dup(addr_family, addr_new); + *p_addr = nm_inet_ntop_dup(addr_family, addr_new); return TRUE; } @@ -162,8 +164,10 @@ update_properties_from_ifindex(NMDevice *device, int ifindex) guint8 encap_limit = 0; gboolean pmtud = FALSE; guint32 flow_label = 0; + guint32 fwmark = 0; NMIPTunnelFlags flags = NM_IP_TUNNEL_FLAG_NONE; - char *key; + gs_free char *input_key = NULL; + gs_free char *output_key = NULL; if (ifindex <= 0) { clear: @@ -207,35 +211,10 @@ clear: tos = lnk->tos; pmtud = lnk->path_mtu_discovery; - if (NM_FLAGS_HAS(lnk->input_flags, NM_GRE_KEY)) { - key = g_strdup_printf("%u", lnk->input_key); - if (g_strcmp0(priv->input_key, key)) { - g_free(priv->input_key); - priv->input_key = key; - _notify(self, PROP_INPUT_KEY); - } else - g_free(key); - } else { - if (priv->input_key) { - nm_clear_g_free(&priv->input_key); - _notify(self, PROP_INPUT_KEY); - } - } - - if (NM_FLAGS_HAS(lnk->output_flags, NM_GRE_KEY)) { - key = g_strdup_printf("%u", lnk->output_key); - if (g_strcmp0(priv->output_key, key)) { - g_free(priv->output_key); - priv->output_key = key; - _notify(self, PROP_OUTPUT_KEY); - } else - g_free(key); - } else { - if (priv->output_key) { - nm_clear_g_free(&priv->output_key); - _notify(self, PROP_OUTPUT_KEY); - } - } + if (NM_FLAGS_HAS(lnk->input_flags, NM_GRE_KEY)) + input_key = g_strdup_printf("%u", lnk->input_key); + if (NM_FLAGS_HAS(lnk->output_flags, NM_GRE_KEY)) + output_key = g_strdup_printf("%u", lnk->output_key); } else if (priv->mode == NM_IP_TUNNEL_MODE_SIT) { const NMPlatformLnkSit *lnk; @@ -296,36 +275,45 @@ clear: flags = ip6tnl_flags_plat_to_setting(lnk->flags); if (NM_IN_SET(priv->mode, NM_IP_TUNNEL_MODE_IP6GRE, NM_IP_TUNNEL_MODE_IP6GRETAP)) { - if (NM_FLAGS_HAS(lnk->input_flags, NM_GRE_KEY)) { - key = g_strdup_printf("%u", lnk->input_key); - if (g_strcmp0(priv->input_key, key)) { - g_free(priv->input_key); - priv->input_key = key; - _notify(self, PROP_INPUT_KEY); - } else - g_free(key); - } else { - if (priv->input_key) { - nm_clear_g_free(&priv->input_key); - _notify(self, PROP_INPUT_KEY); - } - } + if (NM_FLAGS_HAS(lnk->input_flags, NM_GRE_KEY)) + input_key = g_strdup_printf("%u", lnk->input_key); + if (NM_FLAGS_HAS(lnk->output_flags, NM_GRE_KEY)) + output_key = g_strdup_printf("%u", lnk->output_key); + } + } else if (priv->mode == NM_IP_TUNNEL_MODE_VTI) { + const NMPlatformLnkVti *lnk; - if (NM_FLAGS_HAS(lnk->output_flags, NM_GRE_KEY)) { - key = g_strdup_printf("%u", lnk->output_key); - if (g_strcmp0(priv->output_key, key)) { - g_free(priv->output_key); - priv->output_key = key; - _notify(self, PROP_OUTPUT_KEY); - } else - g_free(key); - } else { - if (priv->output_key) { - nm_clear_g_free(&priv->output_key); - _notify(self, PROP_OUTPUT_KEY); - } - } + lnk = nm_platform_link_get_lnk_vti(nm_device_get_platform(device), ifindex, NULL); + if (!lnk) { + _LOGW(LOGD_PLATFORM, "could not read %s properties", "vti"); + goto clear; + } + + parent_ifindex = lnk->parent_ifindex; + local.addr4 = lnk->local; + remote.addr4 = lnk->remote; + fwmark = lnk->fwmark; + if (lnk->ikey) + input_key = g_strdup_printf("%u", lnk->ikey); + if (lnk->okey) + input_key = g_strdup_printf("%u", lnk->okey); + } else if (priv->mode == NM_IP_TUNNEL_MODE_VTI6) { + const NMPlatformLnkVti6 *lnk; + + lnk = nm_platform_link_get_lnk_vti6(nm_device_get_platform(device), ifindex, NULL); + if (!lnk) { + _LOGW(LOGD_PLATFORM, "could not read %s properties", "vti6"); + goto clear; } + + parent_ifindex = lnk->parent_ifindex; + local.addr6 = lnk->local; + remote.addr6 = lnk->remote; + fwmark = lnk->fwmark; + if (lnk->ikey) + input_key = g_strdup_printf("%u", lnk->ikey); + if (lnk->okey) + input_key = g_strdup_printf("%u", lnk->okey); } else g_return_if_reached(); @@ -337,7 +325,6 @@ clear: _notify(self, PROP_REMOTE); out: - if (priv->ttl != ttl) { priv->ttl = ttl; _notify(self, PROP_TTL); @@ -363,6 +350,23 @@ out: _notify(self, PROP_FLOW_LABEL); } + if (!nm_streq0(priv->input_key, input_key)) { + g_free(priv->input_key); + priv->input_key = g_steal_pointer(&input_key); + _notify(self, PROP_INPUT_KEY); + } + + if (!nm_streq0(priv->output_key, output_key)) { + g_free(priv->output_key); + priv->output_key = g_steal_pointer(&output_key); + _notify(self, PROP_OUTPUT_KEY); + } + + if (priv->fwmark != fwmark) { + priv->fwmark = fwmark; + _notify(self, PROP_FWMARK); + } + if (priv->flags != flags) { priv->flags = flags; _notify(self, PROP_FLAGS); @@ -467,11 +471,17 @@ update_connection(NMDevice *device, NMConnection *connection) NULL); } + if (nm_setting_ip_tunnel_get_fwmark(s_ip_tunnel) != priv->fwmark) { + g_object_set(G_OBJECT(s_ip_tunnel), NM_SETTING_IP_TUNNEL_FWMARK, priv->fwmark, NULL); + } + if (NM_IN_SET(priv->mode, NM_IP_TUNNEL_MODE_GRE, NM_IP_TUNNEL_MODE_GRETAP, NM_IP_TUNNEL_MODE_IP6GRE, - NM_IP_TUNNEL_MODE_IP6GRETAP)) { + NM_IP_TUNNEL_MODE_IP6GRETAP, + NM_IP_TUNNEL_MODE_VTI, + NM_IP_TUNNEL_MODE_VTI6)) { if (g_strcmp0(nm_setting_ip_tunnel_get_input_key(s_ip_tunnel), priv->input_key)) { g_object_set(G_OBJECT(s_ip_tunnel), NM_SETTING_IP_TUNNEL_INPUT_KEY, @@ -493,6 +503,7 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError * NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL(device); NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE(self); NMSettingIPTunnel *s_ip_tunnel; + NMIPTunnelMode mode; const char *parent; if (!NM_DEVICE_CLASS(nm_device_ip_tunnel_parent_class) @@ -500,8 +511,9 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError * return FALSE; s_ip_tunnel = nm_connection_get_setting_ip_tunnel(connection); + mode = nm_setting_ip_tunnel_get_mode(s_ip_tunnel); - if (nm_setting_ip_tunnel_get_mode(s_ip_tunnel) != priv->mode) { + if (mode != priv->mode) { nm_utils_error_set_literal(error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, "incompatible IP tunnel mode"); @@ -536,14 +548,16 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError * return FALSE; } - if (nm_setting_ip_tunnel_get_ttl(s_ip_tunnel) != priv->ttl) { + if (!NM_IN_SET(mode, NM_IP_TUNNEL_MODE_VTI, NM_IP_TUNNEL_MODE_VTI6) + && nm_setting_ip_tunnel_get_ttl(s_ip_tunnel) != priv->ttl) { nm_utils_error_set_literal(error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, "TTL of IP tunnel mismatches"); return FALSE; } - if (nm_setting_ip_tunnel_get_tos(s_ip_tunnel) != priv->tos) { + if (!NM_IN_SET(mode, NM_IP_TUNNEL_MODE_VTI, NM_IP_TUNNEL_MODE_VTI6) + && nm_setting_ip_tunnel_get_tos(s_ip_tunnel) != priv->tos) { nm_utils_error_set_literal(error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, "TOS of IP tunnel mismatches"); @@ -551,11 +565,14 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError * } if (priv->addr_family == AF_INET) { - if (nm_setting_ip_tunnel_get_path_mtu_discovery(s_ip_tunnel) - != priv->path_mtu_discovery) { - nm_utils_error_set_literal(error, - NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "MTU discovery setting of IP tunnel mismatches"); + if (!NM_IN_SET(mode, NM_IP_TUNNEL_MODE_VTI, NM_IP_TUNNEL_MODE_VTI6) + && nm_setting_ip_tunnel_get_path_mtu_discovery(s_ip_tunnel) + != priv->path_mtu_discovery) { + nm_utils_error_set(error, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "MTU discovery setting of IP tunnel mismatches: %d vs %d", + nm_setting_ip_tunnel_get_path_mtu_discovery(s_ip_tunnel), + priv->path_mtu_discovery); return FALSE; } } else { @@ -605,6 +622,10 @@ platform_link_to_tunnel_mode(const NMPlatformLink *link) return NM_IP_TUNNEL_MODE_IPIP; case NM_LINK_TYPE_SIT: return NM_IP_TUNNEL_MODE_SIT; + case NM_LINK_TYPE_VTI: + return NM_IP_TUNNEL_MODE_VTI; + case NM_LINK_TYPE_VTI6: + return NM_IP_TUNNEL_MODE_VTI6; default: g_return_val_if_reached(NM_IP_TUNNEL_MODE_UNKNOWN); } @@ -630,7 +651,9 @@ tunnel_mode_to_link_type(NMIPTunnelMode tunnel_mode) case NM_IP_TUNNEL_MODE_SIT: return NM_LINK_TYPE_SIT; case NM_IP_TUNNEL_MODE_VTI: + return NM_LINK_TYPE_VTI; case NM_IP_TUNNEL_MODE_VTI6: + return NM_LINK_TYPE_VTI6; case NM_IP_TUNNEL_MODE_ISATAP: return NM_LINK_TYPE_UNKNOWN; case NM_IP_TUNNEL_MODE_UNKNOWN: @@ -654,6 +677,8 @@ create_and_realize(NMDevice *device, NMPlatformLnkSit lnk_sit = {}; NMPlatformLnkIpIp lnk_ipip = {}; NMPlatformLnkIp6Tnl lnk_ip6tnl = {}; + NMPlatformLnkVti lnk_vti = {}; + NMPlatformLnkVti6 lnk_vti6 = {}; const char *str; gint64 val; NMIPTunnelMode mode; @@ -871,6 +896,81 @@ create_and_realize(NMDevice *device, return FALSE; } break; + case NM_IP_TUNNEL_MODE_VTI: + if (parent) + lnk_vti.parent_ifindex = nm_device_get_ifindex(parent); + + str = nm_setting_ip_tunnel_get_local(s_ip_tunnel); + if (str) + inet_pton(AF_INET, str, &lnk_vti.local); + + str = nm_setting_ip_tunnel_get_remote(s_ip_tunnel); + nm_assert(str); + inet_pton(AF_INET, str, &lnk_vti.remote); + + lnk_vti.ikey = _nm_utils_ascii_str_to_int64(nm_setting_ip_tunnel_get_input_key(s_ip_tunnel), + 10, + 0, + G_MAXUINT32, + 0); + lnk_vti.okey = + _nm_utils_ascii_str_to_int64(nm_setting_ip_tunnel_get_output_key(s_ip_tunnel), + 10, + 0, + G_MAXUINT32, + 0); + lnk_vti.fwmark = nm_setting_ip_tunnel_get_fwmark(s_ip_tunnel); + + r = nm_platform_link_vti_add(nm_device_get_platform(device), iface, &lnk_vti, out_plink); + if (r < 0) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_CREATION_FAILED, + "Failed to create VTI interface '%s' for '%s': %s", + iface, + nm_connection_get_id(connection), + nm_strerror(r)); + return FALSE; + } + break; + case NM_IP_TUNNEL_MODE_VTI6: + if (parent) + lnk_vti6.parent_ifindex = nm_device_get_ifindex(parent); + + str = nm_setting_ip_tunnel_get_local(s_ip_tunnel); + if (str) + inet_pton(AF_INET6, str, &lnk_vti6.local); + + str = nm_setting_ip_tunnel_get_remote(s_ip_tunnel); + nm_assert(str); + inet_pton(AF_INET6, str, &lnk_vti6.remote); + + lnk_vti6.ikey = + _nm_utils_ascii_str_to_int64(nm_setting_ip_tunnel_get_input_key(s_ip_tunnel), + 10, + 0, + G_MAXUINT32, + 0); + lnk_vti6.okey = + _nm_utils_ascii_str_to_int64(nm_setting_ip_tunnel_get_output_key(s_ip_tunnel), + 10, + 0, + G_MAXUINT32, + 0); + lnk_vti6.fwmark = nm_setting_ip_tunnel_get_fwmark(s_ip_tunnel); + + r = nm_platform_link_vti6_add(nm_device_get_platform(device), iface, &lnk_vti6, out_plink); + if (r < 0) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_CREATION_FAILED, + "Failed to create VTI6 interface '%s' for '%s': %s", + iface, + nm_connection_get_id(connection), + nm_strerror(r)); + return FALSE; + } + break; default: g_set_error(error, NM_DEVICE_ERROR, @@ -986,6 +1086,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) case PROP_FLAGS: g_value_set_uint(value, priv->flags); break; + case PROP_FWMARK: + g_value_set_uint(value, priv->fwmark); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -1021,7 +1124,8 @@ constructed(GObject *object) NM_IP_TUNNEL_MODE_IPIP6, NM_IP_TUNNEL_MODE_IP6IP6, NM_IP_TUNNEL_MODE_IP6GRE, - NM_IP_TUNNEL_MODE_IP6GRETAP)) + NM_IP_TUNNEL_MODE_IP6GRETAP, + NM_IP_TUNNEL_MODE_VTI6)) priv->addr_family = AF_INET6; else priv->addr_family = AF_INET; @@ -1070,6 +1174,9 @@ static const NMDBusInterfaceInfoExtended interface_info_device_ip_tunnel = { NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("FlowLabel", "u", NM_DEVICE_IP_TUNNEL_FLOW_LABEL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("FwMark", + "u", + NM_DEVICE_IP_TUNNEL_FWMARK), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Flags", "u", NM_DEVICE_IP_TUNNEL_FLAGS), ), ), @@ -1097,7 +1204,9 @@ nm_device_ip_tunnel_class_init(NMDeviceIPTunnelClass *klass) NM_LINK_TYPE_IP6GRE, NM_LINK_TYPE_IP6GRETAP, NM_LINK_TYPE_IPIP, - NM_LINK_TYPE_SIT); + NM_LINK_TYPE_SIT, + NM_LINK_TYPE_VTI, + NM_LINK_TYPE_VTI6); device_class->act_stage1_prepare = act_stage1_prepare; device_class->link_changed = link_changed; @@ -1192,14 +1301,24 @@ nm_device_ip_tunnel_class_init(NMDeviceIPTunnelClass *klass) 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_FWMARK] = g_param_spec_uint(NM_DEVICE_IP_TUNNEL_FWMARK, + "", + "", + 0, + G_MAXUINT32, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); } /*****************************************************************************/ #define NM_TYPE_IP_TUNNEL_DEVICE_FACTORY (nm_ip_tunnel_device_factory_get_type()) -#define NM_IP_TUNNEL_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IP_TUNNEL_DEVICE_FACTORY, NMIPTunnelDeviceFactory)) +#define NM_IP_TUNNEL_DEVICE_FACTORY(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), \ + NM_TYPE_IP_TUNNEL_DEVICE_FACTORY, \ + NMIPTunnelDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, @@ -1282,7 +1401,9 @@ NM_DEVICE_FACTORY_DEFINE_INTERNAL( NM_LINK_TYPE_IPIP, NM_LINK_TYPE_IP6TNL, NM_LINK_TYPE_IP6GRE, - NM_LINK_TYPE_IP6GRETAP) + NM_LINK_TYPE_IP6GRETAP, + NM_LINK_TYPE_VTI, + NM_LINK_TYPE_VTI6) NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(NM_SETTING_IP_TUNNEL_SETTING_NAME), factory_class->create_device = create_device; factory_class->get_connection_parent = get_connection_parent; diff --git a/src/core/devices/nm-device-ip-tunnel.h b/src/core/devices/nm-device-ip-tunnel.h index 0a941cc2..1d7b7381 100644 --- a/src/core/devices/nm-device-ip-tunnel.h +++ b/src/core/devices/nm-device-ip-tunnel.h @@ -11,7 +11,7 @@ #define NM_TYPE_DEVICE_IP_TUNNEL (nm_device_ip_tunnel_get_type()) #define NM_DEVICE_IP_TUNNEL(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_IP_TUNNEL, NMDeviceIPTunnel)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_IP_TUNNEL, NMDeviceIPTunnel)) #define NM_DEVICE_IP_TUNNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_IP_TUNNEL, NMDeviceIPTunnelClass)) #define NM_IS_DEVICE_IP_TUNNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_IP_TUNNEL)) @@ -31,6 +31,7 @@ #define NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT "encapsulation-limit" #define NM_DEVICE_IP_TUNNEL_FLOW_LABEL "flow-label" #define NM_DEVICE_IP_TUNNEL_FLAGS "flags" +#define NM_DEVICE_IP_TUNNEL_FWMARK "fwmark" typedef struct _NMDeviceIPTunnel NMDeviceIPTunnel; typedef struct _NMDeviceIPTunnelClass NMDeviceIPTunnelClass; diff --git a/src/core/devices/nm-device-logging.h b/src/core/devices/nm-device-logging.h index c2602189..ffc90c2d 100644 --- a/src/core/devices/nm-device-logging.h +++ b/src/core/devices/nm-device-logging.h @@ -11,15 +11,12 @@ #if !_NM_CC_SUPPORT_GENERIC #define _NM_DEVICE_CAST(self) ((NMDevice *) (self)) #elif !defined(_NMLOG_DEVICE_TYPE) -#define _NM_DEVICE_CAST(self) \ - _Generic((self), NMDevice * : ((NMDevice *) (self)), NMDevice *const : ((NMDevice *) (self))) +#define _NM_DEVICE_CAST(self) _Generic((self), NMDevice * : ((NMDevice *) (self))) #else -#define _NM_DEVICE_CAST(self) \ - _Generic((self), \ - _NMLOG_DEVICE_TYPE * : ((NMDevice *) (self)), \ - _NMLOG_DEVICE_TYPE * const: ((NMDevice *) (self)), \ - NMDevice * : ((NMDevice *) (self)), \ - NMDevice * const: ((NMDevice *) (self))) +#define _NM_DEVICE_CAST(self) \ + _Generic((self), _NMLOG_DEVICE_TYPE * \ + : ((NMDevice *) (self)), NMDevice * \ + : ((NMDevice *) (self))) #endif #undef _NMLOG_ENABLED diff --git a/src/core/devices/nm-device-loopback.c b/src/core/devices/nm-device-loopback.c new file mode 100644 index 00000000..ec72aa96 --- /dev/null +++ b/src/core/devices/nm-device-loopback.c @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" +#include "nm-device-loopback.h" +#include "nm-device-private.h" +#include "libnm-platform/nm-platform.h" +#include "nm-device-factory.h" +#include "nm-setting-loopback.h" +#include "libnm-core-aux-intern/nm-libnm-core-utils.h" + +#define _NMLOG_DEVICE_TYPE NMDeviceLoopback +#include "nm-device-logging.h" + +/*****************************************************************************/ + +struct _NMDeviceLoopback { + NMDevice parent; +}; + +struct _NMDeviceLoopbackClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceLoopback, nm_device_loopback, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static NMDeviceCapabilities +get_generic_capabilities(NMDevice *dev) +{ + /* In many aspects, loopback is a software device. Still, don't return + * NM_DEVICE_CAP_IS_SOFTWARE here, because we cannot delete nor create + * such an interface. */ + return NM_DEVICE_CAP_NONE; +} + +static guint32 +get_configured_mtu(NMDevice *device, NMDeviceMtuSource *out_source, gboolean *out_force) +{ + return nm_device_get_configured_mtu_from_connection(device, + NM_TYPE_SETTING_LOOPBACK, + out_source); +} + +static const char * +get_auto_ip_config_method(NMDevice *device, int addr_family) +{ + return NM_IS_IPv4(addr_family) ? NM_SETTING_IP4_CONFIG_METHOD_MANUAL + : NM_SETTING_IP6_CONFIG_METHOD_MANUAL; +} + +static gboolean +complete_connection(NMDevice *device, + NMConnection *connection, + const char *specific_object, + NMConnection *const *existing_connections, + GError **error) +{ + nm_utils_complete_generic_with_params(nm_device_get_platform(device), + connection, + NM_SETTING_LOOPBACK_SETTING_NAME, + existing_connections, + NULL, + _("Loopback connection"), + NULL, + nm_device_get_ip_iface(device)); + + _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_LOOPBACK); + + return TRUE; +} + +static void +update_connection(NMDevice *device, NMConnection *connection) +{ + _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_LOOPBACK); +} + +static gboolean +can_reapply_change(NMDevice *device, + const char *setting_name, + NMSetting *s_old, + NMSetting *s_new, + GHashTable *diffs, + GError **error) +{ + if (!nm_streq(setting_name, NM_SETTING_LOOPBACK_SETTING_NAME)) { + return NM_DEVICE_CLASS(nm_device_loopback_parent_class) + ->can_reapply_change(device, setting_name, s_old, s_new, diffs, error); + } + + return nm_device_hash_check_invalid_keys(diffs, + NM_SETTING_LOOPBACK_SETTING_NAME, + error, + NM_SETTING_LOOPBACK_MTU); +} + +/*****************************************************************************/ + +static const NMDBusInterfaceInfoExtended interface_info_device_loopback = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(NM_DBUS_INTERFACE_DEVICE_LOOPBACK, ), +}; + +static void +nm_device_loopback_init(NMDeviceLoopback *self) +{} + +static void +nm_device_loopback_class_init(NMDeviceLoopbackClass *klass) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass); + + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_device_loopback); + + device_class->connection_type_supported = NM_SETTING_LOOPBACK_SETTING_NAME; + device_class->connection_type_check_compatible = NM_SETTING_LOOPBACK_SETTING_NAME; + device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_LOOPBACK); + + device_class->complete_connection = complete_connection; + device_class->get_generic_capabilities = get_generic_capabilities; + device_class->update_connection = update_connection; + device_class->act_stage1_prepare_set_hwaddr_ethernet = TRUE; + device_class->get_auto_ip_config_method = get_auto_ip_config_method; + device_class->get_configured_mtu = get_configured_mtu; + device_class->allow_autoconnect_on_external = TRUE; + device_class->can_reapply_change = can_reapply_change; +} + +/*****************************************************************************/ + +#define NM_TYPE_LOOPBACK_DEVICE_FACTORY (nm_loopback_device_factory_get_type()) +#define NM_LOOPBACK_DEVICE_FACTORY(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), \ + NM_TYPE_LOOPBACK_DEVICE_FACTORY, \ + NMLoopbackDeviceFactory)) + +static NMDevice * +create_device(NMDeviceFactory *factory, + const char *iface, + const NMPlatformLink *plink, + NMConnection *connection, + gboolean *out_ignore) +{ + return g_object_new(NM_TYPE_DEVICE_LOOPBACK, + NM_DEVICE_IFACE, + iface, + NM_DEVICE_TYPE_DESC, + "Loopback", + NM_DEVICE_DEVICE_TYPE, + NM_DEVICE_TYPE_LOOPBACK, + NM_DEVICE_LINK_TYPE, + NM_LINK_TYPE_LOOPBACK, + NULL); +} + +NM_DEVICE_FACTORY_DEFINE_INTERNAL( + LOOPBACK, + Loopback, + loopback, + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES(NM_LINK_TYPE_LOOPBACK) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(NM_SETTING_LOOPBACK_SETTING_NAME), + factory_class->create_device = create_device;); diff --git a/src/core/devices/nm-device-loopback.h b/src/core/devices/nm-device-loopback.h new file mode 100644 index 00000000..fc62a94b --- /dev/null +++ b/src/core/devices/nm-device-loopback.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_DEVICE_LOOPBACK_H__ +#define __NETWORKMANAGER_DEVICE_LOOPBACK_H__ + +#include "nm-device-generic.h" + +#define NM_TYPE_DEVICE_LOOPBACK (nm_device_loopback_get_type()) +#define NM_DEVICE_LOOPBACK(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_LOOPBACK, NMDeviceLoopback)) +#define NM_DEVICE_LOOPBACK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_LOOPBACK, NMDeviceLoopbackClass)) +#define NM_IS_DEVICE_LOOPBACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_LOOPBACK)) +#define NM_IS_DEVICE_LOOPBACK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DEVICE_LOOPBACK)) +#define NM_DEVICE_LOOPBACK_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DEVICE_LOOPBACK, NMDeviceLoopbackClass)) + +typedef struct _NMDeviceLoopback NMDeviceLoopback; +typedef struct _NMDeviceLoopbackClass NMDeviceLoopbackClass; + +GType nm_device_loopback_get_type(void); + +#endif /* __NETWORKMANAGER_DEVICE_LOOPBACK_H__ */ diff --git a/src/core/devices/nm-device-macsec.c b/src/core/devices/nm-device-macsec.c index 5b1f5fdc..130708bb 100644 --- a/src/core/devices/nm-device-macsec.c +++ b/src/core/devices/nm-device-macsec.c @@ -45,7 +45,6 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDeviceMacsec, typedef struct { NMPlatformLnkMacsec props; - gulong parent_state_id; gulong parent_mtu_id; struct { @@ -91,25 +90,6 @@ static NM_UTILS_LOOKUP_STR_DEFINE(validation_mode_to_string, NM_UTILS_LOOKUP_STR_ITEM(1, "check"), NM_UTILS_LOOKUP_STR_ITEM(2, "strict"), ); -static void -parent_state_changed(NMDevice *parent, - NMDeviceState new_state, - NMDeviceState old_state, - NMDeviceStateReason reason, - gpointer user_data) -{ - NMDeviceMacsec *self = NM_DEVICE_MACSEC(user_data); - - /* We'll react to our own carrier state notifications. Ignore the parent's. */ - if (nm_device_state_reason_check(reason) == NM_DEVICE_STATE_REASON_CARRIER) - return; - - nm_device_set_unmanaged_by_flags(NM_DEVICE(self), - NM_UNMANAGED_PARENT, - !nm_device_get_managed(parent, FALSE), - reason); -} - static void parent_mtu_maybe_changed(NMDevice *parent, GParamSpec *pspec, gpointer user_data) { @@ -132,27 +112,13 @@ parent_changed_notify(NMDevice *device, NM_DEVICE_CLASS(nm_device_macsec_parent_class) ->parent_changed_notify(device, old_ifindex, old_parent, new_ifindex, new_parent); - /* note that @self doesn't have to clear @parent_state_id on dispose, - * because NMDevice's dispose() will unset the parent, which in turn calls - * parent_changed_notify(). */ - nm_clear_g_signal_handler(old_parent, &priv->parent_state_id); nm_clear_g_signal_handler(old_parent, &priv->parent_mtu_id); if (new_parent) { - priv->parent_state_id = g_signal_connect(new_parent, - NM_DEVICE_STATE_CHANGED, - G_CALLBACK(parent_state_changed), - device); - priv->parent_mtu_id = g_signal_connect(new_parent, + priv->parent_mtu_id = g_signal_connect(new_parent, "notify::" NM_DEVICE_MTU, G_CALLBACK(parent_mtu_maybe_changed), device); - - /* Set parent-dependent unmanaged flag */ - nm_device_set_unmanaged_by_flags(device, - NM_UNMANAGED_PARENT, - !nm_device_get_managed(new_parent, FALSE), - NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED); } /* Recheck availability now that the parent has changed */ @@ -862,7 +828,6 @@ dispose(GObject *object) G_OBJECT_CLASS(nm_device_macsec_parent_class)->dispose(object); - nm_assert(NM_DEVICE_MACSEC_GET_PRIVATE(self)->parent_state_id == 0); nm_assert(NM_DEVICE_MACSEC_GET_PRIVATE(self)->parent_mtu_id == 0); } @@ -1011,7 +976,7 @@ nm_device_macsec_class_init(NMDeviceMacsecClass *klass) #define NM_TYPE_MACSEC_DEVICE_FACTORY (nm_macsec_device_factory_get_type()) #define NM_MACSEC_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MACSEC_DEVICE_FACTORY, NMMacsecDeviceFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MACSEC_DEVICE_FACTORY, NMMacsecDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-macsec.h b/src/core/devices/nm-device-macsec.h index e91fe51c..fefb773c 100644 --- a/src/core/devices/nm-device-macsec.h +++ b/src/core/devices/nm-device-macsec.h @@ -10,7 +10,7 @@ #define NM_TYPE_DEVICE_MACSEC (nm_device_macsec_get_type()) #define NM_DEVICE_MACSEC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_MACSEC, NMDeviceMacsec)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_MACSEC, NMDeviceMacsec)) #define NM_DEVICE_MACSEC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_MACSEC, NMDeviceMacsecClass)) #define NM_IS_DEVICE_MACSEC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_MACSEC)) diff --git a/src/core/devices/nm-device-macvlan.c b/src/core/devices/nm-device-macvlan.c index 40ecc5fd..3f57bfb1 100644 --- a/src/core/devices/nm-device-macvlan.c +++ b/src/core/devices/nm-device-macvlan.c @@ -29,7 +29,6 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDeviceMacvlan, PROP_MODE, PROP_NO_PROMISC, PROP_TAP, ); typedef struct { - gulong parent_state_id; gulong parent_mtu_id; NMPlatformLnkMacvlan props; } NMDeviceMacvlanPrivate; @@ -102,25 +101,6 @@ platform_mode_to_string(guint mode) /*****************************************************************************/ -static void -parent_state_changed(NMDevice *parent, - NMDeviceState new_state, - NMDeviceState old_state, - NMDeviceStateReason reason, - gpointer user_data) -{ - NMDeviceMacvlan *self = NM_DEVICE_MACVLAN(user_data); - - /* We'll react to our own carrier state notifications. Ignore the parent's. */ - if (nm_device_state_reason_check(reason) == NM_DEVICE_STATE_REASON_CARRIER) - return; - - nm_device_set_unmanaged_by_flags(NM_DEVICE(self), - NM_UNMANAGED_PARENT, - !nm_device_get_managed(parent, FALSE), - reason); -} - static void parent_mtu_maybe_changed(NMDevice *parent, GParamSpec *pspec, gpointer user_data) { @@ -143,27 +123,13 @@ parent_changed_notify(NMDevice *device, NM_DEVICE_CLASS(nm_device_macvlan_parent_class) ->parent_changed_notify(device, old_ifindex, old_parent, new_ifindex, new_parent); - /* note that @self doesn't have to clear @parent_state_id on dispose, - * because NMDevice's dispose() will unset the parent, which in turn calls - * parent_changed_notify(). */ - nm_clear_g_signal_handler(old_parent, &priv->parent_state_id); nm_clear_g_signal_handler(old_parent, &priv->parent_mtu_id); if (new_parent) { - priv->parent_state_id = g_signal_connect(new_parent, - NM_DEVICE_STATE_CHANGED, - G_CALLBACK(parent_state_changed), - device); - priv->parent_mtu_id = g_signal_connect(new_parent, + priv->parent_mtu_id = g_signal_connect(new_parent, "notify::" NM_DEVICE_MTU, G_CALLBACK(parent_mtu_maybe_changed), device); - - /* Set parent-dependent unmanaged flag */ - nm_device_set_unmanaged_by_flags(device, - NM_UNMANAGED_PARENT, - !nm_device_get_managed(new_parent, FALSE), - NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED); } if (new_ifindex > 0) { @@ -490,7 +456,6 @@ dispose(GObject *object) { G_OBJECT_CLASS(nm_device_macvlan_parent_class)->dispose(object); - nm_assert(NM_DEVICE_MACVLAN_GET_PRIVATE(object)->parent_state_id == 0); nm_assert(NM_DEVICE_MACVLAN_GET_PRIVATE(object)->parent_mtu_id == 0); } #endif @@ -566,7 +531,7 @@ nm_device_macvlan_class_init(NMDeviceMacvlanClass *klass) #define NM_TYPE_MACVLAN_DEVICE_FACTORY (nm_macvlan_device_factory_get_type()) #define NM_MACVLAN_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MACVLAN_DEVICE_FACTORY, NMMacvlanDeviceFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MACVLAN_DEVICE_FACTORY, NMMacvlanDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-macvlan.h b/src/core/devices/nm-device-macvlan.h index 109a2bcd..86ac79c7 100644 --- a/src/core/devices/nm-device-macvlan.h +++ b/src/core/devices/nm-device-macvlan.h @@ -10,7 +10,7 @@ #define NM_TYPE_DEVICE_MACVLAN (nm_device_macvlan_get_type()) #define NM_DEVICE_MACVLAN(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_MACVLAN, NMDeviceMacvlan)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_MACVLAN, NMDeviceMacvlan)) #define NM_DEVICE_MACVLAN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_MACVLAN, NMDeviceMacvlanClass)) #define NM_IS_DEVICE_MACVLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_MACVLAN)) diff --git a/src/core/devices/nm-device-ppp.c b/src/core/devices/nm-device-ppp.c index 6615b65e..1860df3c 100644 --- a/src/core/devices/nm-device-ppp.c +++ b/src/core/devices/nm-device-ppp.c @@ -337,7 +337,7 @@ nm_device_ppp_class_init(NMDevicePppClass *klass) #define NM_TYPE_PPP_DEVICE_FACTORY (nm_ppp_device_factory_get_type()) #define NM_PPP_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PPP_DEVICE_FACTORY, NMPppDeviceFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PPP_DEVICE_FACTORY, NMPppDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-ppp.h b/src/core/devices/nm-device-ppp.h index 24c119ab..0e8ca54a 100644 --- a/src/core/devices/nm-device-ppp.h +++ b/src/core/devices/nm-device-ppp.h @@ -7,7 +7,7 @@ #define __NETWORKMANAGER_DEVICE_PPP_H__ #define NM_TYPE_DEVICE_PPP (nm_device_ppp_get_type()) -#define NM_DEVICE_PPP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_PPP, NMDevicePpp)) +#define NM_DEVICE_PPP(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_PPP, NMDevicePpp)) #define NM_DEVICE_PPP_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_PPP, NMDevicePppClass)) #define NM_IS_DEVICE_PPP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_PPP)) diff --git a/src/core/devices/nm-device-tun.c b/src/core/devices/nm-device-tun.c index e623e9c0..cbea7d7d 100644 --- a/src/core/devices/nm-device-tun.c +++ b/src/core/devices/nm-device-tun.c @@ -527,7 +527,7 @@ nm_device_tun_class_init(NMDeviceTunClass *klass) #define NM_TYPE_TUN_DEVICE_FACTORY (nm_tun_device_factory_get_type()) #define NM_TUN_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_TUN_DEVICE_FACTORY, NMTunDeviceFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_TUN_DEVICE_FACTORY, NMTunDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-tun.h b/src/core/devices/nm-device-tun.h index 84497ea0..866d6c4f 100644 --- a/src/core/devices/nm-device-tun.h +++ b/src/core/devices/nm-device-tun.h @@ -9,7 +9,7 @@ #include "nm-device-generic.h" #define NM_TYPE_DEVICE_TUN (nm_device_tun_get_type()) -#define NM_DEVICE_TUN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_TUN, NMDeviceTun)) +#define NM_DEVICE_TUN(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_TUN, NMDeviceTun)) #define NM_DEVICE_TUN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_TUN, NMDeviceTunClass)) #define NM_IS_DEVICE_TUN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_TUN)) diff --git a/src/core/devices/nm-device-utils.c b/src/core/devices/nm-device-utils.c index 170922eb..2bf24ae6 100644 --- a/src/core/devices/nm-device-utils.c +++ b/src/core/devices/nm-device-utils.c @@ -171,25 +171,25 @@ typedef struct { #define _NMLOG_PREFIX_NAME "resolve-addr" #define _NMLOG_DOMAIN LOGD_CORE -#define _NMLOG2(level, info, ...) \ - G_STMT_START \ - { \ - if (nm_logging_enabled((level), (_NMLOG_DOMAIN))) { \ - ResolveAddrInfo *_info = (info); \ - char _addr_str[NM_UTILS_INET_ADDRSTRLEN]; \ - \ - _nm_log((level), \ - (_NMLOG_DOMAIN), \ - 0, \ - NULL, \ - NULL, \ - _NMLOG_PREFIX_NAME "[" NM_HASH_OBFUSCATE_PTR_FMT \ - ",%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - NM_HASH_OBFUSCATE_PTR(_info), \ - nm_utils_inet_ntop(_info->addr_family, &_info->address, _addr_str) \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ +#define _NMLOG2(level, info, ...) \ + G_STMT_START \ + { \ + if (nm_logging_enabled((level), (_NMLOG_DOMAIN))) { \ + ResolveAddrInfo *_info = (info); \ + char _addr_str[NM_INET_ADDRSTRLEN]; \ + \ + _nm_log((level), \ + (_NMLOG_DOMAIN), \ + 0, \ + NULL, \ + NULL, \ + _NMLOG_PREFIX_NAME "[" NM_HASH_OBFUSCATE_PTR_FMT \ + ",%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + NM_HASH_OBFUSCATE_PTR(_info), \ + nm_inet_ntop(_info->addr_family, &_info->address, _addr_str) \ + _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } \ G_STMT_END static void @@ -234,9 +234,9 @@ resolve_addr_helper_cb(GObject *source, GAsyncResult *result, gpointer user_data static void resolve_addr_spawn_helper(ResolveAddrInfo *info) { - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + char addr_str[NM_INET_ADDRSTRLEN]; - nm_utils_inet_ntop(info->addr_family, &info->address, addr_str); + nm_inet_ntop(info->addr_family, &info->address, addr_str); _LOG2D(info, "start lookup via nm-daemon-helper"); nm_utils_spawn_helper(NM_MAKE_STRV("resolve-address", addr_str), g_task_get_cancellable(info->task), diff --git a/src/core/devices/nm-device-veth.c b/src/core/devices/nm-device-veth.c index 17115d33..c4b9e234 100644 --- a/src/core/devices/nm-device-veth.c +++ b/src/core/devices/nm-device-veth.c @@ -215,7 +215,7 @@ nm_device_veth_class_init(NMDeviceVethClass *klass) #define NM_TYPE_VETH_DEVICE_FACTORY (nm_veth_device_factory_get_type()) #define NM_VETH_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_VETH_DEVICE_FACTORY, NMVethDeviceFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_VETH_DEVICE_FACTORY, NMVethDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-veth.h b/src/core/devices/nm-device-veth.h index d43a0a4b..39182363 100644 --- a/src/core/devices/nm-device-veth.h +++ b/src/core/devices/nm-device-veth.h @@ -9,7 +9,8 @@ #include "nm-device-ethernet.h" #define NM_TYPE_DEVICE_VETH (nm_device_veth_get_type()) -#define NM_DEVICE_VETH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_VETH, NMDeviceVeth)) +#define NM_DEVICE_VETH(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_VETH, NMDeviceVeth)) #define NM_DEVICE_VETH_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_VETH, NMDeviceVethClass)) #define NM_IS_DEVICE_VETH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_VETH)) diff --git a/src/core/devices/nm-device-vlan.c b/src/core/devices/nm-device-vlan.c index 0c101c70..feb011db 100644 --- a/src/core/devices/nm-device-vlan.c +++ b/src/core/devices/nm-device-vlan.c @@ -8,6 +8,7 @@ #include "nm-device-vlan.h" #include +#include #include "nm-manager.h" #include "nm-utils.h" @@ -31,7 +32,6 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDeviceVlan, PROP_VLAN_ID, ); typedef struct { - gulong parent_state_id; gulong parent_hwaddr_id; gulong parent_mtu_id; guint vlan_id; @@ -53,25 +53,6 @@ G_DEFINE_TYPE(NMDeviceVlan, nm_device_vlan, NM_TYPE_DEVICE) /*****************************************************************************/ -static void -parent_state_changed(NMDevice *parent, - NMDeviceState new_state, - NMDeviceState old_state, - NMDeviceStateReason reason, - gpointer user_data) -{ - NMDeviceVlan *self = NM_DEVICE_VLAN(user_data); - - /* We'll react to our own carrier state notifications. Ignore the parent's. */ - if (nm_device_state_reason_check(reason) == NM_DEVICE_STATE_REASON_CARRIER) - return; - - nm_device_set_unmanaged_by_flags(NM_DEVICE(self), - NM_UNMANAGED_PARENT, - !nm_device_get_managed(parent, FALSE), - reason); -} - static void parent_mtu_maybe_changed(NMDevice *parent, GParamSpec *pspec, gpointer user_data) { @@ -132,19 +113,10 @@ parent_changed_notify(NMDevice *device, NM_DEVICE_CLASS(nm_device_vlan_parent_class) ->parent_changed_notify(device, old_ifindex, old_parent, new_ifindex, new_parent); - /* note that @self doesn't have to clear @parent_state_id on dispose, - * because NMDevice's dispose() will unset the parent, which in turn calls - * parent_changed_notify(). */ - nm_clear_g_signal_handler(old_parent, &priv->parent_state_id); nm_clear_g_signal_handler(old_parent, &priv->parent_hwaddr_id); nm_clear_g_signal_handler(old_parent, &priv->parent_mtu_id); if (new_parent) { - priv->parent_state_id = g_signal_connect(new_parent, - NM_DEVICE_STATE_CHANGED, - G_CALLBACK(parent_state_changed), - device); - priv->parent_hwaddr_id = g_signal_connect(new_parent, "notify::" NM_DEVICE_HW_ADDRESS, G_CALLBACK(parent_hwaddr_maybe_changed), @@ -156,12 +128,6 @@ parent_changed_notify(NMDevice *device, G_CALLBACK(parent_mtu_maybe_changed), device); parent_mtu_maybe_changed(new_parent, NULL, self); - - /* Set parent-dependent unmanaged flag */ - nm_device_set_unmanaged_by_flags(device, - NM_UNMANAGED_PARENT, - !nm_device_get_managed(new_parent, FALSE), - NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED); } /* Recheck availability now that the parent has changed */ @@ -227,6 +193,8 @@ create_and_realize(NMDevice *device, int parent_ifindex; guint vlan_id; int r; + const char *protocol_str; + guint16 protocol = ETH_P_8021Q; s_vlan = nm_connection_get_setting_vlan(connection); g_assert(s_vlan); @@ -262,11 +230,22 @@ create_and_realize(NMDevice *device, vlan_id = nm_setting_vlan_get_id(s_vlan); + protocol_str = nm_setting_vlan_get_protocol(s_vlan); + if (protocol_str) { + if (nm_streq(protocol_str, "802.1ad")) + protocol = ETH_P_8021AD; + else + nm_assert(nm_streq(protocol_str, "802.1Q")); + } + r = nm_platform_link_vlan_add(nm_device_get_platform(device), iface, parent_ifindex, - vlan_id, - nm_setting_vlan_get_flags(s_vlan), + &((NMPlatformLnkVlan){ + .id = vlan_id, + .flags = nm_setting_vlan_get_flags(s_vlan), + .protocol = protocol, + }), out_plink); if (r < 0) { g_set_error(error, @@ -473,6 +452,9 @@ update_connection(NMDevice *device, NMConnection *connection) _nm_setting_vlan_set_priorities(s_vlan, NM_VLAN_INGRESS_MAP, NULL, 0); _nm_setting_vlan_set_priorities(s_vlan, NM_VLAN_EGRESS_MAP, NULL, 0); } + + if (polnk && polnk->lnk_vlan.protocol == ETH_P_8021AD) + g_object_set(s_vlan, NM_SETTING_VLAN_PROTOCOL, "802.1ad", NULL); } static NMActStageReturn @@ -592,7 +574,7 @@ nm_device_vlan_class_init(NMDeviceVlanClass *klass) #define NM_TYPE_VLAN_DEVICE_FACTORY (nm_vlan_device_factory_get_type()) #define NM_VLAN_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_VLAN_DEVICE_FACTORY, NMVlanDeviceFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_VLAN_DEVICE_FACTORY, NMVlanDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-vlan.h b/src/core/devices/nm-device-vlan.h index 5270706f..06fef5cf 100644 --- a/src/core/devices/nm-device-vlan.h +++ b/src/core/devices/nm-device-vlan.h @@ -9,7 +9,8 @@ #include "nm-device.h" #define NM_TYPE_DEVICE_VLAN (nm_device_vlan_get_type()) -#define NM_DEVICE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_VLAN, NMDeviceVlan)) +#define NM_DEVICE_VLAN(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_VLAN, NMDeviceVlan)) #define NM_DEVICE_VLAN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_VLAN, NMDeviceVlanClass)) #define NM_IS_DEVICE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_VLAN)) diff --git a/src/core/devices/nm-device-vrf.c b/src/core/devices/nm-device-vrf.c index 91eb195a..ad31f3a2 100644 --- a/src/core/devices/nm-device-vrf.c +++ b/src/core/devices/nm-device-vrf.c @@ -347,7 +347,7 @@ nm_device_vrf_class_init(NMDeviceVrfClass *klass) #define NM_TYPE_VRF_DEVICE_FACTORY (nm_vrf_device_factory_get_type()) #define NM_VRF_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_VRF_DEVICE_FACTORY, NMVrfDeviceFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_VRF_DEVICE_FACTORY, NMVrfDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-vrf.h b/src/core/devices/nm-device-vrf.h index 5169041c..c6580fb8 100644 --- a/src/core/devices/nm-device-vrf.h +++ b/src/core/devices/nm-device-vrf.h @@ -6,7 +6,7 @@ #include "nm-device-generic.h" #define NM_TYPE_DEVICE_VRF (nm_device_vrf_get_type()) -#define NM_DEVICE_VRF(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_VRF, NMDeviceVrf)) +#define NM_DEVICE_VRF(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_VRF, NMDeviceVrf)) #define NM_DEVICE_VRF_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_VRF, NMDeviceVrfClass)) #define NM_IS_DEVICE_VRF(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_VRF)) diff --git a/src/core/devices/nm-device-vxlan.c b/src/core/devices/nm-device-vxlan.c index bf7c94c0..44a7be33 100644 --- a/src/core/devices/nm-device-vxlan.c +++ b/src/core/devices/nm-device-vxlan.c @@ -174,15 +174,15 @@ create_and_realize(NMDevice *device, str = nm_setting_vxlan_get_local(s_vxlan); if (str) { - if (!nm_utils_parse_inaddr_bin(AF_INET, str, NULL, &props.local) - && !nm_utils_parse_inaddr_bin(AF_INET6, str, NULL, &props.local6)) + if (!nm_inet_parse_bin(AF_INET, str, NULL, &props.local) + && !nm_inet_parse_bin(AF_INET6, str, NULL, &props.local6)) return FALSE; } str = nm_setting_vxlan_get_remote(s_vxlan); if (str) { - if (!nm_utils_parse_inaddr_bin(AF_INET, str, NULL, &props.group) - && !nm_utils_parse_inaddr_bin(AF_INET6, str, NULL, &props.group6)) + if (!nm_inet_parse_bin(AF_INET, str, NULL, &props.group) + && !nm_inet_parse_bin(AF_INET6, str, NULL, &props.group6)) return FALSE; } @@ -223,7 +223,7 @@ address_matches(const char *candidate, in_addr_t addr4, struct in6_addr *addr6) if (!candidate) return addr4 == 0u && IN6_IS_ADDR_UNSPECIFIED(addr6); - if (!nm_utils_parse_inaddr_bin(AF_UNSPEC, candidate, &addr_family, &candidate_addr)) + if (!nm_inet_parse_bin(AF_UNSPEC, candidate, &addr_family, &candidate_addr)) return FALSE; if (!nm_ip_addr_equal(addr_family, @@ -401,7 +401,7 @@ update_connection(NMDevice *device, NMConnection *connection) { NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE(device); NMSettingVxlan *s_vxlan = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_VXLAN); - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; if (priv->props.id != nm_setting_vxlan_get_id(s_vxlan)) g_object_set(G_OBJECT(s_vxlan), NM_SETTING_VXLAN_ID, priv->props.id, NULL); @@ -417,12 +417,12 @@ update_connection(NMDevice *device, NMConnection *connection) if (priv->props.group) { g_object_set(s_vxlan, NM_SETTING_VXLAN_REMOTE, - _nm_utils_inet4_ntop(priv->props.group, sbuf), + nm_inet4_ntop(priv->props.group, sbuf), NULL); } else { g_object_set(s_vxlan, NM_SETTING_VXLAN_REMOTE, - _nm_utils_inet6_ntop(&priv->props.group6, sbuf), + nm_inet6_ntop(&priv->props.group6, sbuf), NULL); } } @@ -433,12 +433,12 @@ update_connection(NMDevice *device, NMConnection *connection) if (priv->props.local) { g_object_set(s_vxlan, NM_SETTING_VXLAN_LOCAL, - _nm_utils_inet4_ntop(priv->props.local, sbuf), + nm_inet4_ntop(priv->props.local, sbuf), NULL); } else if (memcmp(&priv->props.local6, &in6addr_any, sizeof(in6addr_any))) { g_object_set(s_vxlan, NM_SETTING_VXLAN_LOCAL, - _nm_utils_inet6_ntop(&priv->props.local6, sbuf), + nm_inet6_ntop(&priv->props.local6, sbuf), NULL); } } @@ -510,15 +510,15 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) break; case PROP_GROUP: if (priv->props.group) - g_value_take_string(value, nm_utils_inet4_ntop_dup(priv->props.group)); + g_value_take_string(value, nm_inet4_ntop_dup(priv->props.group)); else if (!IN6_IS_ADDR_UNSPECIFIED(&priv->props.group6)) - g_value_take_string(value, nm_utils_inet6_ntop_dup(&priv->props.group6)); + g_value_take_string(value, nm_inet6_ntop_dup(&priv->props.group6)); break; case PROP_LOCAL: if (priv->props.local) - g_value_take_string(value, nm_utils_inet4_ntop_dup(priv->props.local)); + g_value_take_string(value, nm_inet4_ntop_dup(priv->props.local)); else if (!IN6_IS_ADDR_UNSPECIFIED(&priv->props.local6)) - g_value_take_string(value, nm_utils_inet6_ntop_dup(&priv->props.local6)); + g_value_take_string(value, nm_inet6_ntop_dup(&priv->props.local6)); break; case PROP_TOS: g_value_set_uchar(value, priv->props.tos); @@ -741,7 +741,7 @@ nm_device_vxlan_class_init(NMDeviceVxlanClass *klass) #define NM_TYPE_VXLAN_DEVICE_FACTORY (nm_vxlan_device_factory_get_type()) #define NM_VXLAN_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_VXLAN_DEVICE_FACTORY, NMVxlanDeviceFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_VXLAN_DEVICE_FACTORY, NMVxlanDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-vxlan.h b/src/core/devices/nm-device-vxlan.h index 4c4165e5..e53ae82e 100644 --- a/src/core/devices/nm-device-vxlan.h +++ b/src/core/devices/nm-device-vxlan.h @@ -10,7 +10,7 @@ #define NM_TYPE_DEVICE_VXLAN (nm_device_vxlan_get_type()) #define NM_DEVICE_VXLAN(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_VXLAN, NMDeviceVxlan)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_VXLAN, NMDeviceVxlan)) #define NM_DEVICE_VXLAN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_VXLAN, NMDeviceVxlanClass)) #define NM_IS_DEVICE_VXLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_VXLAN)) diff --git a/src/core/devices/nm-device-wireguard.c b/src/core/devices/nm-device-wireguard.c index 179c2a22..db02033d 100644 --- a/src/core/devices/nm-device-wireguard.c +++ b/src/core/devices/nm-device-wireguard.c @@ -219,7 +219,7 @@ _auto_default_route_get_enabled(NMSettingWireGuard *s_wg, aip = nm_wireguard_peer_get_allowed_ip(peer, j, &valid); if (!valid) continue; - if (!nm_utils_parse_inaddr_prefix_bin(AF_UNSPEC, aip, &addr_family, NULL, &prefix)) + if (!nm_inet_parse_with_prefix_bin(AF_UNSPEC, aip, &addr_family, NULL, &prefix)) continue; if (prefix != 0) continue; @@ -1152,11 +1152,11 @@ _peers_get_platform_list(NMDeviceWireGuardPrivate *priv, aip = nm_wireguard_peer_get_allowed_ip(peer_data->peer, i_aip, &valid); if (!valid - || !nm_utils_parse_inaddr_prefix_bin(AF_UNSPEC, - aip, - &addr_family, - &addrbin, - &prefix)) { + || !nm_inet_parse_with_prefix_bin(AF_UNSPEC, + aip, + &addr_family, + &addrbin, + &prefix)) { /* the address is really not expected to be invalid, because then * the connection would not verify. Anyway, silently skip it. */ continue; @@ -1197,7 +1197,7 @@ skip: nm_assert(plp->_construct_idx_start < plp->_construct_idx_end); l = plp->_construct_idx_end - plp->_construct_idx_start; plp->allowed_ips = - &g_array_index(allowed_ips, NMPWireGuardAllowedIP, plp->_construct_idx_start); + &nm_g_array_index(allowed_ips, NMPWireGuardAllowedIP, plp->_construct_idx_start); plp->allowed_ips_len = l; } } @@ -1701,8 +1701,7 @@ _get_dev2_ip_config(NMDeviceWireGuard *self, int addr_family) aip = nm_wireguard_peer_get_allowed_ip(peer, j, &valid); - if (!valid - || !nm_utils_parse_inaddr_prefix_bin(addr_family, aip, NULL, &addrbin, &prefix)) + if (!valid || !nm_inet_parse_with_prefix_bin(addr_family, aip, NULL, &addrbin, &prefix)) continue; if (prefix < 0) @@ -1722,7 +1721,7 @@ _get_dev2_ip_config(NMDeviceWireGuard *self, int addr_family) NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES); } - nm_utils_ipx_address_clear_host_address(addr_family, &addrbin, NULL, prefix); + nm_ip_addr_clear_host_address(addr_family, &addrbin, NULL, prefix); rtable_coerced = route_table_coerced; @@ -2062,8 +2061,10 @@ nm_device_wireguard_class_init(NMDeviceWireGuardClass *klass) /*************************************************************/ #define NM_TYPE_WIREGUARD_DEVICE_FACTORY (nm_wireguard_device_factory_get_type()) -#define NM_WIREGUARD_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIREGUARD_DEVICE_FACTORY, NMWireGuardDeviceFactory)) +#define NM_WIREGUARD_DEVICE_FACTORY(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), \ + NM_TYPE_WIREGUARD_DEVICE_FACTORY, \ + NMWireGuardDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-wireguard.h b/src/core/devices/nm-device-wireguard.h index 7e18bdba..8fb8f8cf 100644 --- a/src/core/devices/nm-device-wireguard.h +++ b/src/core/devices/nm-device-wireguard.h @@ -10,7 +10,7 @@ #define NM_TYPE_DEVICE_WIREGUARD (nm_device_wireguard_get_type()) #define NM_DEVICE_WIREGUARD(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_WIREGUARD, NMDeviceWireGuard)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_WIREGUARD, NMDeviceWireGuard)) #define NM_DEVICE_WIREGUARD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_WIREGUARD, NMDeviceWireGuardClass)) #define NM_IS_DEVICE_WIREGUARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_WIREGUARD)) diff --git a/src/core/devices/nm-device-wpan.c b/src/core/devices/nm-device-wpan.c index ecb5ee23..98356ccf 100644 --- a/src/core/devices/nm-device-wpan.c +++ b/src/core/devices/nm-device-wpan.c @@ -221,7 +221,7 @@ nm_device_wpan_class_init(NMDeviceWpanClass *klass) #define NM_TYPE_WPAN_DEVICE_FACTORY (nm_wpan_device_factory_get_type()) #define NM_WPAN_DEVICE_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WPAN_DEVICE_FACTORY, NMWpanDeviceFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WPAN_DEVICE_FACTORY, NMWpanDeviceFactory)) static NMDevice * create_device(NMDeviceFactory *factory, diff --git a/src/core/devices/nm-device-wpan.h b/src/core/devices/nm-device-wpan.h index 969929ae..39cf4656 100644 --- a/src/core/devices/nm-device-wpan.h +++ b/src/core/devices/nm-device-wpan.h @@ -7,7 +7,8 @@ #define __NETWORKMANAGER_DEVICE_WPAN_H__ #define NM_TYPE_DEVICE_WPAN (nm_device_wpan_get_type()) -#define NM_DEVICE_WPAN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_WPAN, NMDeviceWpan)) +#define NM_DEVICE_WPAN(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_WPAN, NMDeviceWpan)) #define NM_DEVICE_WPAN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_WPAN, NMDeviceWpanClass)) #define NM_IS_DEVICE_WPAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_WPAN)) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index c4f0f9a2..04478614 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include "nm-compat-headers/linux/if_addr.h" #include #include #include @@ -59,6 +59,7 @@ #include "settings/nm-settings.h" #include "nm-setting-ethtool.h" #include "nm-setting-ovs-external-ids.h" +#include "nm-setting-ovs-other-config.h" #include "nm-setting-user.h" #include "nm-auth-utils.h" #include "nm-keep-alive.h" @@ -77,6 +78,7 @@ #include "nm-device-generic.h" #include "nm-device-bridge.h" +#include "nm-device-loopback.h" #include "nm-device-vlan.h" #include "nm-device-vrf.h" #include "nm-device-wireguard.h" @@ -110,6 +112,7 @@ typedef enum { CLEANUP_TYPE_KEEP, CLEANUP_TYPE_REMOVED, CLEANUP_TYPE_DECONFIGURE, + CLEANUP_TYPE_KEEP_REAPPLY, } CleanupType; typedef enum _nm_packed { @@ -848,7 +851,7 @@ static void _dev_ipshared4_spawn_dnsmasq(NMDevice *self); static void _dev_ipshared6_start(NMDevice *self); static void -_cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gboolean from_reapply); +_cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gboolean preserve_dhcp); static void concheck_update_state(NMDevice *self, int addr_family, @@ -1583,6 +1586,9 @@ _prop_get_ipv4_link_local(NMDevice *self) if (!s_ip4) return NM_SETTING_IP4_LL_DISABLED; + if (NM_IS_DEVICE_LOOPBACK(self)) + return NM_SETTING_IP4_LL_DISABLED; + link_local = nm_setting_ip4_config_get_link_local(s_ip4); if (link_local == NM_SETTING_IP4_LL_DEFAULT) { @@ -2736,12 +2742,6 @@ _ethtool_state_set(NMDevice *self) /*****************************************************************************/ -static gboolean -is_loopback(NMDevice *self) -{ - return NM_IS_DEVICE_GENERIC(self) && NM_DEVICE_GET_PRIVATE(self)->ifindex == 1; -} - gboolean nm_device_is_vpn(NMDevice *self) { @@ -3578,7 +3578,7 @@ _dev_ip_state_check_async(NMDevice *self, int addr_family) } static void -_dev_ip_state_cleanup(NMDevice *self, int addr_family, gboolean from_reapply) +_dev_ip_state_cleanup(NMDevice *self, int addr_family, gboolean keep_reapply) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); int IS_IPv4; @@ -3586,7 +3586,7 @@ _dev_ip_state_cleanup(NMDevice *self, int addr_family, gboolean from_reapply) if (addr_family == AF_UNSPEC) { _dev_ip_state_set_state(self, addr_family, - from_reapply ? NM_DEVICE_IP_STATE_PENDING : NM_DEVICE_IP_STATE_NONE, + keep_reapply ? NM_DEVICE_IP_STATE_PENDING : NM_DEVICE_IP_STATE_NONE, "ip-state-clear"); return; } @@ -3597,7 +3597,7 @@ _dev_ip_state_cleanup(NMDevice *self, int addr_family, gboolean from_reapply) nm_clear_g_source_inst(&priv->ip_data_x[IS_IPv4].req_timeout_source); _dev_ip_state_set_state(self, addr_family, - from_reapply ? NM_DEVICE_IP_STATE_PENDING : NM_DEVICE_IP_STATE_NONE, + keep_reapply ? NM_DEVICE_IP_STATE_PENDING : NM_DEVICE_IP_STATE_NONE, "ip-state-clear"); priv->ip_data_x[IS_IPv4].wait_for_carrier = FALSE; priv->ip_data_x[IS_IPv4].wait_for_ports = FALSE; @@ -4129,18 +4129,30 @@ _dev_l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, N /* Check if AC6 addresses completed DAD */ if (NM_FLAGS_ANY(notify_data->platform_change_on_idle.obj_type_flags, nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP6_ADDRESS)) - && priv->ipac6_data.state == NM_DEVICE_IP_STATE_PENDING && priv->ipac6_data.l3cd - && nm_l3cfg_check_ready(l3cfg, - priv->ipac6_data.l3cd, - AF_INET6, - NM_L3CFG_CHECK_READY_FLAGS_IP6_DAD_READY, - NULL)) { - if (nm_l3cfg_has_temp_not_available_obj(priv->l3cfg, AF_INET6)) - _dev_l3_cfg_commit(self, FALSE); - - nm_clear_l3cd(&priv->ipac6_data.l3cd); - _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_READY); - _dev_ip_state_check_async(self, AF_INET6); + && priv->ipac6_data.state == NM_DEVICE_IP_STATE_PENDING && priv->ipac6_data.l3cd) { + gs_unref_array GArray *conflicts = NULL; + gboolean ready; + + ready = nm_l3cfg_check_ready(l3cfg, + priv->ipac6_data.l3cd, + AF_INET6, + NM_L3CFG_CHECK_READY_FLAGS_IP6_DAD_READY, + &conflicts); + if (conflicts) { + /* nm_ndisc_dad_failed() will emit a new "NDisc:config-received" + * signal; _dev_ipac6_ndisc_config_changed() will be called + * synchronously to update the current state and schedule a commit. */ + nm_ndisc_dad_failed(priv->ipac6_data.ndisc, conflicts, TRUE); + } else if (ready) { + if (nm_l3cfg_has_temp_not_available_obj(priv->l3cfg, AF_INET6)) + _dev_l3_cfg_commit(self, FALSE); + + nm_clear_l3cd(&priv->ipac6_data.l3cd); + _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_READY); + _dev_ip_state_check_async(self, AF_INET6); + } else { + /* wait */ + } } _dev_ipmanual_check_ready(self); @@ -5005,6 +5017,9 @@ nm_device_get_route_metric_default(NMDeviceType device_type) */ switch (device_type) { + case NM_DEVICE_TYPE_LOOPBACK: + return 30; + /* 50 is also used for VPN plugins (NM_VPN_ROUTE_METRIC_DEFAULT). * * Note that returning 50 from this function means that this device-type is @@ -5425,7 +5440,7 @@ concheck_is_possible(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (!nm_device_is_real(self) || is_loopback(self)) + if (!nm_device_is_real(self) || NM_IS_DEVICE_LOOPBACK(self)) return FALSE; /* we enable periodic checks for every device state (except UNKNOWN). Especially with @@ -6203,7 +6218,8 @@ nm_device_master_release_slave(NMDevice *self, NMDevicePrivate *priv; NMDevicePrivate *slave_priv; SlaveInfo *info; - gs_unref_object NMDevice *self_free = NULL; + gs_unref_object NMDevice *self_free = NULL; + gs_unref_object NMDevice *slave_free = NULL; g_return_if_fail(NM_DEVICE(self)); g_return_if_fail(NM_DEVICE(slave)); @@ -6246,14 +6262,15 @@ nm_device_master_release_slave(NMDevice *self, /* keep both alive until the end of the function. * Transfers ownership from slave_priv->master. */ - self_free = self; + nm_assert(self == slave_priv->master); + self_free = g_steal_pointer(&slave_priv->master); - c_list_unlink(&info->lst_slave); - slave_priv->master = NULL; + nm_assert(slave == info->slave); + slave_free = g_steal_pointer(&info->slave); + c_list_unlink(&info->lst_slave); g_signal_handler_disconnect(slave, info->watch_id); - g_object_unref(slave); - g_slice_free(SlaveInfo, info); + nm_g_slice_free(info); if (c_list_is_empty(&priv->slaves)) { _active_connection_set_state_flags_full(self, @@ -6577,27 +6594,30 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink) NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); } - if (master && NM_DEVICE_GET_CLASS(master)->attach_port) { - nm_device_master_add_slave(master, self, FALSE); + if (master) { + if (NM_DEVICE_GET_CLASS(master)->attach_port) { + nm_device_master_add_slave(master, self, FALSE); + } else { + _LOGD(LOGD_DEVICE, + "enslaved to non-master-type device %s; ignoring", + nm_device_get_iface(master)); + } goto out; } - if (master) { - _LOGD(LOGD_DEVICE, - "enslaved to non-master-type device %s; ignoring", - nm_device_get_iface(master)); - } else { + if (plink->master) { _LOGD(LOGD_DEVICE, "enslaved to unknown device %d (%s%s%s)", plink->master, NM_PRINT_FMT_QUOTED(plink_master, "\"", plink_master->name, "\"", "??")); + if (!priv->ifindex_changed_id) { + priv->ifindex_changed_id = g_signal_connect(nm_device_get_manager(self), + NM_MANAGER_DEVICE_IFINDEX_CHANGED, + G_CALLBACK(device_ifindex_changed_cb), + self); + } } - if (!priv->ifindex_changed_id) { - priv->ifindex_changed_id = g_signal_connect(nm_device_get_manager(self), - NM_MANAGER_DEVICE_IFINDEX_CHANGED, - G_CALLBACK(device_ifindex_changed_cb), - self); - } + return; out: @@ -6609,6 +6629,8 @@ device_ifindex_changed_cb(NMManager *manager, NMDevice *device_changed, NMDevice { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + g_return_if_fail(priv->master_ifindex > 0); + if (priv->master_ifindex != nm_device_get_ifindex(device_changed)) return; @@ -7465,11 +7487,6 @@ realize_start_setup(NMDevice *self, NM_UNMANAGED_EXTERNAL_DOWN, _dev_unmanaged_is_external_down(self, TRUE)); - /* Unmanaged the loopback device with an explicit NM_UNMANAGED_BY_TYPE flag. - * Later we might want to manage 'lo' too. Currently, that doesn't work because - * NetworkManager might down the interface or remove the 127.0.0.1 address. */ - nm_device_set_unmanaged_flags(self, NM_UNMANAGED_BY_TYPE, is_loopback(self)); - nm_device_set_unmanaged_by_user_udev(self); nm_device_set_unmanaged_by_user_conf(self); @@ -7659,8 +7676,7 @@ nm_device_unrealize(NMDevice *self, gboolean remove_resources, GError **error) nm_device_set_unmanaged_flags(self, NM_UNMANAGED_PLATFORM_INIT, TRUE); nm_device_set_unmanaged_flags(self, - NM_UNMANAGED_PARENT | NM_UNMANAGED_BY_TYPE - | NM_UNMANAGED_USER_UDEV | NM_UNMANAGED_USER_EXPLICIT + NM_UNMANAGED_USER_UDEV | NM_UNMANAGED_USER_EXPLICIT | NM_UNMANAGED_EXTERNAL_DOWN | NM_UNMANAGED_IS_SLAVE, NM_UNMAN_FLAG_OP_FORGET); @@ -8585,7 +8601,7 @@ nm_device_generate_connection(NMDevice *self, pllink = nm_platform_link_get(nm_device_get_platform(self), priv->ifindex); if (pllink && pllink->inet6_token.id) { - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; g_object_set(s_ip6, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, @@ -8776,6 +8792,14 @@ check_connection_compatible(NMDevice *self, NMConnection *connection, GError **e return FALSE; } + if (!nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV) + && nm_connection_get_setting(connection, NM_TYPE_SETTING_SRIOV)) { + nm_utils_error_set_literal(error, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "device does not support SR-IOV"); + return FALSE; + } + conn_iface = nm_manager_get_connection_iface(NM_MANAGER_GET, connection, NULL, NULL, &local); /* We always need a interface name for virtual devices, but for @@ -9359,14 +9383,17 @@ activate_stage1_device_prepare(NMDevice *self) nm_device_state_changed(self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); if (priv->stage1_sriov_state != NM_DEVICE_STAGE_STATE_COMPLETED) { - NMSettingSriov *s_sriov; + NMSettingSriov *s_sriov = NULL; if (nm_device_sys_iface_state_is_external_or_assume(self)) { /* pass */ - } else if (priv->stage1_sriov_state == NM_DEVICE_STAGE_STATE_PENDING) + } else if (priv->stage1_sriov_state == NM_DEVICE_STAGE_STATE_PENDING) { return; - else if (priv->ifindex > 0 && nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV) - && (s_sriov = nm_device_get_applied_setting(self, NM_TYPE_SETTING_SRIOV))) { + } else if (priv->ifindex > 0) { + s_sriov = nm_device_get_applied_setting(self, NM_TYPE_SETTING_SRIOV); + } + + if (s_sriov) { nm_auto_freev NMPlatformVF **plat_vfs = NULL; gs_free_error GError *error = NULL; NMSriovVF *vf; @@ -9374,6 +9401,8 @@ activate_stage1_device_prepare(NMDevice *self) guint num; guint i; + nm_assert(nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV)); + autoprobe = nm_setting_sriov_get_autoprobe_drivers(s_sriov); if (autoprobe == NM_TERNARY_DEFAULT) { autoprobe = nm_config_data_get_connection_default_int64( @@ -10058,7 +10087,7 @@ _dev_ipmanual_check_ready(NMDevice *self) gboolean has_carrier; NML3CfgCheckReadyFlags flags; gboolean ready; - gboolean acd_used = FALSE; + gs_unref_array GArray *conflicts = NULL; int IS_IPv4; if (priv->ipmanual_data.state_4 != NM_DEVICE_IP_STATE_PENDING @@ -10101,8 +10130,8 @@ _dev_ipmanual_check_ready(NMDevice *self) priv->l3cds[L3_CONFIG_DATA_TYPE_MANUALIP].d, addr_family, flags, - &acd_used); - if (acd_used) { + &conflicts); + if (conflicts) { _dev_ipmanual_set_state(self, addr_family, NM_DEVICE_IP_STATE_FAILED); _dev_ip_state_check_async(self, AF_UNSPEC); } else if (ready) { @@ -10434,6 +10463,7 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family) .request_broadcast = request_broadcast, .acd_timeout_msec = _prop_get_ipv4_dad_timeout(self), }, + .previous_lease = priv->l3cds[L3_CONFIG_DATA_TYPE_DHCP_X(IS_IPv4)].d, }; priv->ipdhcp_data_4.client = @@ -10484,16 +10514,16 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family) G_CALLBACK(_dev_ipdhcpx_notify), self); - /* FIXME(l3cfg:dhcp:previous-lease): take the NML3ConfigData from the previous lease (if any) - * and pass it on to NMDhcpClient. This is a fake lease that we use initially (until - * NMDhcpClient got a real lease). Note that NMDhcpClient needs to check whether the - * lease already expired. */ - + /* Take the NML3ConfigData from the previous lease (if any) that was passed to the NMDhcpClient. + * This may be the old lease only used during the duration of a reapply until we get the + * new lease. */ previous_lease = nm_dhcp_client_get_lease(priv->ipdhcp_data_x[IS_IPv4].client); + if (!priv->ipdhcp_data_x[IS_IPv4].config) { priv->ipdhcp_data_x[IS_IPv4].config = nm_dhcp_config_new(addr_family, previous_lease); _notify(self, PROP_DHCPX_CONFIG(IS_IPv4)); } + if (previous_lease) { nm_dhcp_config_set_lease(priv->ipdhcp_data_x[IS_IPv4].config, previous_lease); _dev_l3_register_l3cds_set_one_full(self, @@ -10747,14 +10777,14 @@ nm_device_copy_ip6_dns_config(NMDevice *self, NMDevice *from_device) l3cd_src = priv_src->l3cds[L3_CONFIG_DATA_TYPE_AC_6].d; } if (l3cd_src) { - const char *const *strvarr; - const struct in6_addr *addrs; - guint n; - guint i; + const char *const *strvarr; + const char *const *addrs; + guint n; + guint i; addrs = nm_l3_config_data_get_nameservers(l3cd_src, AF_INET6, &n); for (i = 0; i < n; i++) - nm_l3_config_data_add_nameserver(l3cd, AF_INET6, &addrs[i]); + nm_l3_config_data_add_nameserver(l3cd, AF_INET6, addrs[i]); strvarr = nm_l3_config_data_get_searches(l3cd_src, AF_INET6, &n); for (i = 0; i < n; i++) @@ -10806,7 +10836,8 @@ _dev_ipll6_set_llstate(NMDevice *self, NML3IPv6LLState llstate, const struct in6 || (!priv->ipll_data_6.v6.ipv6ll && NM_IN_SET(priv->ipll_data_6.v6.llstate, NM_L3_IPV6LL_STATE_NONE, - NM_L3_IPV6LL_STATE_DEFUNCT))); + NM_L3_IPV6LL_STATE_DEFUNCT, + NM_L3_IPV6LL_STATE_READY))); switch (priv->ipll_data_6.v6.llstate) { case NM_L3_IPV6LL_STATE_NONE: @@ -10844,7 +10875,7 @@ _dev_ipll6_set_llstate(NMDevice *self, NML3IPv6LLState llstate, const struct in6 } if (changed) { - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; _LOGT_ipll(AF_INET6, "set state %s (was %s, llstate=%s, lladdr=%s)", @@ -10853,7 +10884,7 @@ _dev_ipll6_set_llstate(NMDevice *self, NML3IPv6LLState llstate, const struct in6 nm_l3_ipv6ll_state_to_string(priv->ipll_data_6.v6.llstate), nm_ip_addr_is_null(AF_INET6, &priv->ipll_data_6.v6.lladdr) ? "(none)" - : _nm_utils_inet6_ntop(&priv->ipll_data_6.v6.lladdr, sbuf)); + : nm_inet6_ntop(&priv->ipll_data_6.v6.lladdr, sbuf)); } if (changed) @@ -10889,6 +10920,11 @@ _dev_ipll6_start(NMDevice *self) if (priv->ipll_data_6.v6.ipv6ll) return; + if (NM_IS_DEVICE_LOOPBACK(self)) { + _dev_ipll6_set_llstate(self, NM_L3_IPV6LL_STATE_READY, NULL); + return; + } + if (!priv->l3cfg) { _LOGD(LOGD_IP6, "linklocal6: no IP link for IPv6"); goto out_fail; @@ -11000,6 +11036,10 @@ nm_device_get_configured_mtu_from_connection(NMDevice *self, if (setting) mtu = nm_setting_wireguard_get_mtu(NM_SETTING_WIREGUARD(setting)); global_property_name = NM_CON_DEFAULT("wireguard.mtu"); + } else if (setting_type == NM_TYPE_SETTING_LOOPBACK) { + if (setting) + mtu = nm_setting_loopback_get_mtu(NM_SETTING_LOOPBACK(setting)); + global_property_name = NM_CON_DEFAULT("loopback.mtu"); } else g_return_val_if_reached(0); @@ -11411,10 +11451,14 @@ _dev_ipac6_ndisc_config_changed(NMNDisc *ndisc, const NML3ConfigData *l3cd, NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - gboolean ready = TRUE; - NMDedupMultiIter iter; - const NMPObject *obj; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gboolean ready; + + /* The ndisc configuration changes when we receive a new RA or + * when a lifetime expires; but also when DAD fails for a + * SLAAC address and we need to regenerate new stable-privacy + * addresses. In all these cases we update the AC6 configuration, + * schedule a commit and update the AC state. */ _dev_ipac6_grace_period_start(self, 0, TRUE); @@ -11425,22 +11469,11 @@ _dev_ipac6_ndisc_config_changed(NMNDisc *ndisc, FALSE); nm_clear_l3cd(&priv->ipac6_data.l3cd); - - /* wait that addresses are committed to platform and - * become non-tentative before declaring AC6 is ready.*/ - nm_l3_config_data_iter_obj_for_each (&iter, l3cd, &obj, NMP_OBJECT_TYPE_IP6_ADDRESS) { - const NMPlatformIP6Address *addr = NMP_OBJECT_CAST_IP6_ADDRESS(obj); - const NMPlatformIP6Address *plat_addr; - - plat_addr = nm_platform_ip6_address_get(nm_device_get_platform(self), - nm_device_get_ip_ifindex(self), - &addr->address); - if (!plat_addr || (plat_addr->n_ifa_flags & IFA_F_TENTATIVE)) { - ready = FALSE; - break; - } - } - + ready = nm_l3cfg_check_ready(priv->l3cfg, + l3cd, + AF_INET6, + NM_L3CFG_CHECK_READY_FLAGS_IP6_DAD_READY, + NULL); if (ready) { _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_READY); } else { @@ -12078,13 +12111,34 @@ activate_stage3_ip_config(NMDevice *self) ifindex = nm_device_get_ip_ifindex(self); + ipv4_method = nm_device_get_effective_ip_config_method(self, AF_INET); + if (nm_streq(ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { + /* "auto" usually means DHCPv4 or autoconf6, but it doesn't have to be. Subclasses + * can overwrite it. For example, you cannot run DHCPv4 on PPP/WireGuard links. */ + ipv4_method = klass->get_ip_method_auto(self, AF_INET); + } + + ipv6_method = nm_device_get_effective_ip_config_method(self, AF_INET6); + + if (nm_streq(ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { + ipv6_method = klass->get_ip_method_auto(self, AF_INET6); + } + if (priv->ip_data_4.do_reapply) { _LOGD_ip(AF_INET, "reapply..."); - _cleanup_ip_pre(self, AF_INET, CLEANUP_TYPE_DECONFIGURE, TRUE); + priv->ip_data_4.do_reapply = FALSE; + _cleanup_ip_pre(self, + AF_INET, + CLEANUP_TYPE_KEEP_REAPPLY, + nm_streq(ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)); } if (priv->ip_data_6.do_reapply) { _LOGD_ip(AF_INET6, "reapply..."); - _cleanup_ip_pre(self, AF_INET6, CLEANUP_TYPE_DECONFIGURE, TRUE); + priv->ip_data_6.do_reapply = FALSE; + _cleanup_ip_pre(self, + AF_INET6, + CLEANUP_TYPE_KEEP_REAPPLY, + nm_streq(ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)); } /* Add the interface to the specified firewall zone */ @@ -12136,18 +12190,6 @@ activate_stage3_ip_config(NMDevice *self) * let's do it! */ _commit_mtu(self); - ipv4_method = nm_device_get_effective_ip_config_method(self, AF_INET); - if (nm_streq(ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { - /* "auto" usually means DHCPv4 or autoconf6, but it doesn't have to be. Subclasses - * can overwrite it. For example, you cannot run DHCPv4 on PPP/WireGuard links. */ - ipv4_method = klass->get_ip_method_auto(self, AF_INET); - } - - ipv6_method = nm_device_get_effective_ip_config_method(self, AF_INET6); - if (nm_streq(ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { - ipv6_method = klass->get_ip_method_auto(self, AF_INET6); - } - if (!nm_device_sys_iface_state_is_external(self) && (!klass->ready_for_ip_config || klass->ready_for_ip_config(self, TRUE))) { if (priv->ipmanual_data.state_6 == NM_DEVICE_IP_STATE_NONE @@ -12274,16 +12316,28 @@ _dev_ipshared4_new_l3cd(NMDevice *self, NMConnection *connection, NMPlatformIP4A static gboolean _dev_ipshared4_init(NMDevice *self) { - static const char *const modules[] = {"ip_tables", - "iptable_nat", - "nf_nat_ftp", - "nf_nat_irc", - "nf_nat_sip", - "nf_nat_tftp", - "nf_nat_pptp", - "nf_nat_h323"}; - int errsv; - guint i; + static const char *const modules_iptables[] = {"ip_tables", "iptable_nat"}; + static const char *const modules_nftables[] = + {"nf_nat_ftp", "nf_nat_irc", "nf_nat_sip", "nf_nat_tftp", "nf_nat_pptp", "nf_nat_h323"}; + int errsv; + guint i; + + switch (nm_firewall_utils_get_backend()) { + case NM_FIREWALL_BACKEND_IPTABLES: + for (i = 0; i < G_N_ELEMENTS(modules_iptables); i++) + nmp_utils_modprobe(NULL, FALSE, modules_iptables[i], NULL); + break; + case NM_FIREWALL_BACKEND_NFTABLES: + for (i = 0; i < G_N_ELEMENTS(modules_nftables); i++) + nmp_utils_modprobe(NULL, FALSE, modules_nftables[i], NULL); + break; + case NM_FIREWALL_BACKEND_NONE: + /* do not modify network settings like ip forwarding */ + return TRUE; + default: + nm_assert_not_reached(); + break; + } if (nm_platform_sysctl_get_int32(nm_device_get_platform(self), NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_forward"), @@ -12312,9 +12366,6 @@ _dev_ipshared4_init(NMDevice *self) nm_strerror_native(errsv)); } - for (i = 0; i < G_N_ELEMENTS(modules); i++) - nmp_utils_modprobe(NULL, FALSE, modules[i], NULL); - return TRUE; } @@ -12591,17 +12642,18 @@ delete_on_deactivate_check_and_schedule(NMDevice *self) } static void -_cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gboolean from_reapply) +_cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gboolean preserve_dhcp) { - const int IS_IPv4 = NM_IS_IPv4(addr_family); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gboolean keep_reapply = (cleanup_type == CLEANUP_TYPE_KEEP_REAPPLY); _dev_ipsharedx_cleanup(self, addr_family); _dev_ipdev_cleanup(self, AF_UNSPEC); _dev_ipdev_cleanup(self, addr_family); - _dev_ipdhcpx_cleanup(self, addr_family, TRUE, FALSE); + _dev_ipdhcpx_cleanup(self, addr_family, !preserve_dhcp || !keep_reapply, FALSE); if (!IS_IPv4) _dev_ipac6_cleanup(self); @@ -12613,8 +12665,8 @@ _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gbool nm_clear_g_signal_handler(nm_manager_get_dns_manager(priv->manager), &priv->ip_data.dnsmgr_update_pending_signal_id); - _dev_ip_state_cleanup(self, AF_UNSPEC, from_reapply); - _dev_ip_state_cleanup(self, addr_family, from_reapply); + _dev_ip_state_cleanup(self, AF_UNSPEC, keep_reapply); + _dev_ip_state_cleanup(self, addr_family, keep_reapply); } gboolean @@ -12762,7 +12814,9 @@ can_reapply_change(NMDevice *self, goto out_fail; } - if (nm_streq(setting_name, NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME) + if (NM_IN_STRSET(setting_name, + NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME, + NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME) && NM_DEVICE_GET_CLASS(self)->can_reapply_change_ovs_external_ids) { /* TODO: this means, you cannot reapply changes to the external-ids for * OVS system interfaces. */ @@ -12984,6 +13038,10 @@ check_and_reapply_connection(NMDevice *self, if (priv->state >= NM_DEVICE_STATE_ACTIVATED) nm_device_update_metered(self); + /* Notify dispatcher when re-applied */ + _LOGD(LOGD_DEVICE, "Notifying re-apply complete"); + nm_dispatcher_call_device(NM_DISPATCHER_ACTION_REAPPLY, self, NULL, NULL, NULL, NULL); + return TRUE; } @@ -13889,7 +13947,7 @@ nm_device_start_ip_check(NMDevice *self) NMSettingConnection *s_con; guint timeout = 0; const char *ping_binary = NULL; - char buf[NM_UTILS_INET_ADDRSTRLEN]; + char buf[NM_INET_ADDRSTRLEN]; NMLogDomain log_domain = LOGD_IP4; /* Shouldn't be any active ping here, since IP_CHECK happens after the @@ -13920,14 +13978,14 @@ nm_device_start_ip_check(NMDevice *self) } else if (priv->ip_data_4.state == NM_DEVICE_IP_STATE_READY) { gw = nm_l3_config_data_get_best_default_route(l3cd, AF_INET); if (gw) { - _nm_utils_inet4_ntop(NMP_OBJECT_CAST_IP4_ROUTE(gw)->gateway, buf); + nm_inet4_ntop(NMP_OBJECT_CAST_IP4_ROUTE(gw)->gateway, buf); ping_binary = nm_utils_find_helper("ping", "/usr/bin/ping", NULL); log_domain = LOGD_IP4; } } else if (priv->ip_data_6.state == NM_DEVICE_IP_STATE_READY) { gw = nm_l3_config_data_get_best_default_route(l3cd, AF_INET6); if (gw) { - _nm_utils_inet6_ntop(&NMP_OBJECT_CAST_IP6_ROUTE(gw)->gateway, buf); + nm_inet6_ntop(&NMP_OBJECT_CAST_IP6_ROUTE(gw)->gateway, buf); ping_binary = nm_utils_find_helper("ping6", "/usr/bin/ping6", NULL); log_domain = LOGD_IP6; } @@ -14151,8 +14209,6 @@ NM_UTILS_FLAGS2STR_DEFINE(nm_unmanaged_flags2str, NMUnmanagedFlags, NM_UTILS_FLAGS2STR(NM_UNMANAGED_SLEEPING, "sleeping"), NM_UTILS_FLAGS2STR(NM_UNMANAGED_QUITTING, "quitting"), - NM_UTILS_FLAGS2STR(NM_UNMANAGED_PARENT, "parent"), - NM_UTILS_FLAGS2STR(NM_UNMANAGED_BY_TYPE, "by-type"), NM_UTILS_FLAGS2STR(NM_UNMANAGED_PLATFORM_INIT, "platform-init"), NM_UTILS_FLAGS2STR(NM_UNMANAGED_USER_EXPLICIT, "user-explicit"), NM_UTILS_FLAGS2STR(NM_UNMANAGED_BY_DEFAULT, "by-default"), @@ -14847,9 +14903,10 @@ _nm_device_check_connection_available(NMDevice *self, } else { if (!nm_device_get_managed(self, TRUE)) { /* device is strictly unmanaged by authoritative unmanaged reasons. */ - nm_utils_error_set_literal(error, - NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE, - "device is strictly unmanaged"); + nm_utils_error_set_literal( + error, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_STRICTLY_UNMANAGED_DEVICE, + "device is strictly unmanaged"); return FALSE; } if (!NM_FLAGS_HAS(flags, @@ -16138,7 +16195,7 @@ nm_device_queue_state(NMDevice *self, NMDeviceState state, NMDeviceStateReason r /* We should only ever have one delayed state transition at a time */ if (priv->queued_state.id) { - _LOGW(LOGD_DEVICE, + _LOGD(LOGD_DEVICE, "queue-state[%s, reason:%s, id:%u]: %s", nm_device_state_to_string(priv->queued_state.state), nm_device_state_reason_to_string_a(priv->queued_state.reason), @@ -17127,6 +17184,12 @@ nm_device_clear_dns_lookup_data(NMDevice *self) nm_clear_pointer(&priv->hostname_resolver_x[i], _hostname_resolver_free); } +gboolean +nm_device_get_allow_autoconnect_on_external(NMDevice *self) +{ + return NM_DEVICE_GET_CLASS(self)->allow_autoconnect_on_external; +} + static GInetAddress * get_address_for_hostname_dns_lookup(NMDevice *self, int addr_family) { @@ -17151,10 +17214,12 @@ get_address_for_hostname_dns_lookup(NMDevice *self, int addr_family) if (head_entry) { c_list_for_each_entry (iter, &head_entry->lst_entries_head, lst_entries) { - const NMPlatformIPAddress *addr = NMP_OBJECT_CAST_IP_ADDRESS(iter->obj); + const NMPlatformIPXAddress *addr = NMP_OBJECT_CAST_IPX_ADDRESS(iter->obj); if (IS_IPv4) { - return g_inet_address_new_from_bytes(addr->address_ptr, G_SOCKET_FAMILY_IPV4); + if (nm_ip4_addr_is_loopback(addr->a4.address)) + continue; + return g_inet_address_new_from_bytes(addr->ax.address_ptr, G_SOCKET_FAMILY_IPV4); } /* For IPv6 prefer, in order: @@ -17163,15 +17228,19 @@ get_address_for_hostname_dns_lookup(NMDevice *self, int addr_family) * - link-local */ - if (!IN6_IS_ADDR_LINKLOCAL(addr->address_ptr)) { - if (!(addr->n_ifa_flags & IFA_F_DEPRECATED)) { - return g_inet_address_new_from_bytes(addr->address_ptr, G_SOCKET_FAMILY_IPV6); + if (IN6_ARE_ADDR_EQUAL(&addr->a6.address, &in6addr_loopback)) + continue; + + if (!IN6_IS_ADDR_LINKLOCAL(addr->ax.address_ptr)) { + if (!(addr->ax.n_ifa_flags & IFA_F_DEPRECATED)) { + return g_inet_address_new_from_bytes(addr->ax.address_ptr, + G_SOCKET_FAMILY_IPV6); } - addr6_nonll = addr->address_ptr; + addr6_nonll = addr->ax.address_ptr; continue; } - addr6_ll = addr->address_ptr; + addr6_ll = addr->ax.address_ptr; } if (addr6_nonll || addr6_ll) @@ -17578,7 +17647,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps nm_assert(priv->type == NM_DEVICE_TYPE_UNKNOWN); priv->type = g_value_get_uint(value); nm_assert(priv->type > NM_DEVICE_TYPE_UNKNOWN); - nm_assert(priv->type <= NM_DEVICE_TYPE_VRF); + nm_assert(priv->type <= NM_DEVICE_TYPE_LOOPBACK); break; case PROP_LINK_TYPE: /* construct-only */ diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h index de850e68..f54457d1 100644 --- a/src/core/devices/nm-device.h +++ b/src/core/devices/nm-device.h @@ -91,7 +91,7 @@ #define NM_DEVICE_INTERFACE_FLAGS "interface-flags" #define NM_TYPE_DEVICE (nm_device_get_type()) -#define NM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE, NMDevice)) +#define NM_DEVICE(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE, NMDevice)) #define NM_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE, NMDeviceClass)) #define NM_IS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE)) #define NM_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DEVICE)) @@ -208,6 +208,8 @@ typedef struct _NMDeviceClass { bool can_reapply_change_ovs_external_ids : 1; + bool allow_autoconnect_on_external : 1; + NMRfkillType rfkill_type : 4; void (*state_changed)(NMDevice *device, @@ -561,8 +563,6 @@ void nm_device_copy_ip6_dns_config(NMDevice *self, NMDevice *from_device); * @NM_UNMANAGED_NONE: placeholder value * @NM_UNMANAGED_SLEEPING: %TRUE when unmanaged because NM is sleeping. * @NM_UNMANAGED_QUITTING: %TRUE when unmanaged because NM is shutting down. - * @NM_UNMANAGED_PARENT: %TRUE when unmanaged due to parent device being unmanaged - * @NM_UNMANAGED_BY_TYPE: %TRUE for unmanaging device by type, like loopback. * @NM_UNMANAGED_PLATFORM_INIT: %TRUE when unmanaged because platform link not * yet initialized. Unrealized device are also unmanaged for this reason. * @NM_UNMANAGED_USER_EXPLICIT: %TRUE when unmanaged by explicit user decision @@ -592,21 +592,19 @@ typedef enum { * the device cannot be managed. */ NM_UNMANAGED_SLEEPING = (1LL << 0), NM_UNMANAGED_QUITTING = (1LL << 1), - NM_UNMANAGED_PARENT = (1LL << 2), - NM_UNMANAGED_BY_TYPE = (1LL << 3), - NM_UNMANAGED_PLATFORM_INIT = (1LL << 4), - NM_UNMANAGED_USER_EXPLICIT = (1LL << 5), - NM_UNMANAGED_USER_SETTINGS = (1LL << 6), + NM_UNMANAGED_PLATFORM_INIT = (1LL << 2), + NM_UNMANAGED_USER_EXPLICIT = (1LL << 3), + NM_UNMANAGED_USER_SETTINGS = (1LL << 4), /* These flags can be non-effective and be overwritten * by other flags. */ - NM_UNMANAGED_BY_DEFAULT = (1LL << 7), - NM_UNMANAGED_USER_CONF = (1LL << 8), - NM_UNMANAGED_USER_UDEV = (1LL << 9), - NM_UNMANAGED_EXTERNAL_DOWN = (1LL << 10), - NM_UNMANAGED_IS_SLAVE = (1LL << 11), + NM_UNMANAGED_BY_DEFAULT = (1LL << 5), + NM_UNMANAGED_USER_CONF = (1LL << 6), + NM_UNMANAGED_USER_UDEV = (1LL << 7), + NM_UNMANAGED_EXTERNAL_DOWN = (1LL << 8), + NM_UNMANAGED_IS_SLAVE = (1LL << 9), - NM_UNMANAGED_ALL = ((1LL << 12) - 1), + NM_UNMANAGED_ALL = ((1LL << 10) - 1), } NMUnmanagedFlags; typedef enum { @@ -821,4 +819,6 @@ nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean void nm_device_clear_dns_lookup_data(NMDevice *self); +gboolean nm_device_get_allow_autoconnect_on_external(NMDevice *self); + #endif /* __NETWORKMANAGER_DEVICE_H__ */ diff --git a/src/core/devices/nm-lldp-listener.c b/src/core/devices/nm-lldp-listener.c index 137bf8fb..ac7e97f0 100644 --- a/src/core/devices/nm-lldp-listener.c +++ b/src/core/devices/nm-lldp-listener.c @@ -15,24 +15,20 @@ #include "libnm-std-aux/unaligned.h" #include "libnm-platform/nm-platform.h" #include "libnm-glib-aux/nm-c-list.h" +#include "libnm-lldp/nm-lldp-rx.h" #include "nm-utils.h" -#include "libnm-systemd-core/nm-sd.h" - #define MAX_NEIGHBORS 128 #define MIN_UPDATE_INTERVAL_NSEC (2 * NM_UTILS_NSEC_PER_SEC) -#define LLDP_MAC_NEAREST_BRIDGE \ - (&((struct ether_addr){.ether_addr_octet = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e}})) -#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE \ - (&((struct ether_addr){.ether_addr_octet = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x03}})) -#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE \ - (&((struct ether_addr){.ether_addr_octet = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00}})) +#define LLDP_MAC_NEAREST_BRIDGE (&NM_ETHER_ADDR_INIT(0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e)) +#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE (&NM_ETHER_ADDR_INIT(0x01, 0x80, 0xc2, 0x00, 0x00, 0x03)) +#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE (&NM_ETHER_ADDR_INIT(0x01, 0x80, 0xc2, 0x00, 0x00, 0x00)) /*****************************************************************************/ struct _NMLldpListener { - sd_lldp_rx *lldp_handle; + NMLldpRX *lldp_rx; GHashTable *lldp_neighbors; GVariant *variant; @@ -49,12 +45,12 @@ struct _NMLldpListener { /*****************************************************************************/ typedef struct { - GVariant *variant; - sd_lldp_neighbor *neighbor_sd; - char *chassis_id; - char *port_id; - guint8 chassis_id_type; - guint8 port_id_type; + GVariant *variant; + NMLldpNeighbor *neighbor_nm; + char *chassis_id; + char *port_id; + guint8 chassis_id_type; + guint8 port_id_type; } LldpNeighbor; /*****************************************************************************/ @@ -100,7 +96,7 @@ lldp_neighbor_get_raw(LldpNeighbor *neigh, const guint8 **out_raw_data, gsize *o nm_assert(neigh); - r = sd_lldp_neighbor_get_raw(neigh->neighbor_sd, &raw_data, &raw_len); + r = nm_lldp_neighbor_get_raw(neigh->neighbor_nm, &raw_data, &raw_len); nm_assert(r >= 0); nm_assert(raw_data); @@ -111,24 +107,24 @@ lldp_neighbor_get_raw(LldpNeighbor *neigh, const guint8 **out_raw_data, gsize *o } static gboolean -lldp_neighbor_id_get(struct sd_lldp_neighbor *neighbor_sd, - guint8 *out_chassis_id_type, - const guint8 **out_chassis_id, - gsize *out_chassis_id_len, - guint8 *out_port_id_type, - const guint8 **out_port_id, - gsize *out_port_id_len) +lldp_neighbor_id_get(NMLldpNeighbor *neighbor_nm, + guint8 *out_chassis_id_type, + const guint8 **out_chassis_id, + gsize *out_chassis_id_len, + guint8 *out_port_id_type, + const guint8 **out_port_id, + gsize *out_port_id_len) { int r; - r = sd_lldp_neighbor_get_chassis_id(neighbor_sd, + r = nm_lldp_neighbor_get_chassis_id(neighbor_nm, out_chassis_id_type, (gconstpointer *) out_chassis_id, out_chassis_id_len); if (r < 0) return FALSE; - r = sd_lldp_neighbor_get_port_id(neighbor_sd, + r = nm_lldp_neighbor_get_port_id(neighbor_nm, out_port_id_type, (gconstpointer *) out_port_id, out_port_id_len); @@ -139,82 +135,16 @@ lldp_neighbor_id_get(struct sd_lldp_neighbor *neighbor_sd, } static guint -lldp_neighbor_id_hash(gconstpointer ptr) +lldp_neighbor_id_hash(const LldpNeighbor *neigh) { - const LldpNeighbor *neigh = ptr; - guint8 chassis_id_type; - guint8 port_id_type; - const guint8 *chassis_id; - const guint8 *port_id; - gsize chassis_id_len; - gsize port_id_len; - NMHashState h; - - if (!lldp_neighbor_id_get(neigh->neighbor_sd, - &chassis_id_type, - &chassis_id, - &chassis_id_len, - &port_id_type, - &port_id, - &port_id_len)) { - nm_assert_not_reached(); - return 0; - } - - nm_hash_init(&h, 23423423u); - nm_hash_update_vals(&h, chassis_id_len, port_id_len, chassis_id_type, port_id_type); - nm_hash_update(&h, chassis_id, chassis_id_len); - nm_hash_update(&h, port_id, port_id_len); - return nm_hash_complete(&h); + return nm_lldp_neighbor_id_hash(nm_lldp_neighbor_get_id(neigh->neighbor_nm)); } static int lldp_neighbor_id_cmp(const LldpNeighbor *a, const LldpNeighbor *b) { - guint8 a_chassis_id_type; - guint8 b_chassis_id_type; - guint8 a_port_id_type; - guint8 b_port_id_type; - const guint8 *a_chassis_id; - const guint8 *b_chassis_id; - const guint8 *a_port_id; - const guint8 *b_port_id; - gsize a_chassis_id_len; - gsize b_chassis_id_len; - gsize a_port_id_len; - gsize b_port_id_len; - - NM_CMP_SELF(a, b); - - if (!lldp_neighbor_id_get(a->neighbor_sd, - &a_chassis_id_type, - &a_chassis_id, - &a_chassis_id_len, - &a_port_id_type, - &a_port_id, - &a_port_id_len)) { - nm_assert_not_reached(); - return FALSE; - } - - if (!lldp_neighbor_id_get(b->neighbor_sd, - &b_chassis_id_type, - &b_chassis_id, - &b_chassis_id_len, - &b_port_id_type, - &b_port_id, - &b_port_id_len)) { - nm_assert_not_reached(); - return FALSE; - } - - NM_CMP_DIRECT(a_chassis_id_type, b_chassis_id_type); - NM_CMP_DIRECT(a_port_id_type, b_port_id_type); - NM_CMP_DIRECT(a_chassis_id_len, b_chassis_id_len); - NM_CMP_DIRECT(a_port_id_len, b_port_id_len); - NM_CMP_DIRECT_MEMCMP(a_chassis_id, b_chassis_id, a_chassis_id_len); - NM_CMP_DIRECT_MEMCMP(a_port_id, b_port_id, a_port_id_len); - return 0; + return nm_lldp_neighbor_id_cmp(nm_lldp_neighbor_get_id(a->neighbor_nm), + nm_lldp_neighbor_get_id(b->neighbor_nm)); } static int @@ -225,7 +155,7 @@ lldp_neighbor_id_cmp_p(gconstpointer a, gconstpointer b, gpointer user_data) } static gboolean -lldp_neighbor_id_equal(gconstpointer a, gconstpointer b) +lldp_neighbor_id_equal(const LldpNeighbor *a, const LldpNeighbor *b) { return lldp_neighbor_id_cmp(a, b) == 0; } @@ -239,7 +169,7 @@ lldp_neighbor_free(LldpNeighbor *neighbor) g_free(neighbor->chassis_id); g_free(neighbor->port_id); nm_g_variant_unref(neighbor->variant); - sd_lldp_neighbor_unref(neighbor->neighbor_sd); + nm_lldp_neighbor_unref(neighbor->neighbor_nm); nm_g_slice_free(neighbor); } @@ -257,7 +187,7 @@ lldp_neighbor_equal(LldpNeighbor *a, LldpNeighbor *b) gsize raw_len_a; gsize raw_len_b; - if (a->neighbor_sd == b->neighbor_sd) + if (a->neighbor_nm == b->neighbor_nm) return TRUE; lldp_neighbor_get_raw(a, &raw_data_a, &raw_len_a); @@ -293,7 +223,7 @@ parse_management_address_tlv(const uint8_t *data, gsize len) if (len < 11) return NULL; - nm_assert((data[0] >> 1) == SD_LLDP_TYPE_MGMT_ADDRESS); + nm_assert((data[0] >> 1) == NM_LLDP_TYPE_MGMT_ADDRESS); nm_assert((((data[0] & 1) << 8) + data[1]) + 2 == len); data += 2; @@ -360,7 +290,7 @@ format_network_address(const guint8 *data, gsize sz) } else return NULL; - return nm_utils_inet_ntop_dup(family, &a); + return nm_inet_ntop_dup(family, &a); } static const char * @@ -402,7 +332,7 @@ format_string_cp(const guint8 *data, gsize len, gboolean allow_trim) } static LldpNeighbor * -lldp_neighbor_new(sd_lldp_neighbor *neighbor_sd) +lldp_neighbor_new(NMLldpNeighbor *neighbor_nm) { LldpNeighbor *neigh; guint8 chassis_id_type; @@ -414,7 +344,7 @@ lldp_neighbor_new(sd_lldp_neighbor *neighbor_sd) gs_free char *s_chassis_id = NULL; gs_free char *s_port_id = NULL; - if (!lldp_neighbor_id_get(neighbor_sd, + if (!lldp_neighbor_id_get(neighbor_nm, &chassis_id_type, &chassis_id, &chassis_id_len, @@ -424,17 +354,17 @@ lldp_neighbor_new(sd_lldp_neighbor *neighbor_sd) return NULL; switch (chassis_id_type) { - case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT: - case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS: - case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT: - case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME: - case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED: + case NM_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT: + case NM_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS: + case NM_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT: + case NM_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME: + case NM_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED: s_chassis_id = format_string_cp(chassis_id, chassis_id_len, FALSE); break; - case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS: + case NM_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS: s_chassis_id = nm_utils_hwaddr_ntoa(chassis_id, chassis_id_len); break; - case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS: + case NM_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS: s_chassis_id = format_network_address(chassis_id, chassis_id_len); break; } @@ -446,16 +376,16 @@ lldp_neighbor_new(sd_lldp_neighbor *neighbor_sd) } switch (port_id_type) { - case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS: - case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT: - case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME: - case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED: + case NM_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS: + case NM_LLDP_PORT_SUBTYPE_PORT_COMPONENT: + case NM_LLDP_PORT_SUBTYPE_INTERFACE_NAME: + case NM_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED: s_port_id = format_string_cp(port_id, port_id_len, FALSE); break; - case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS: + case NM_LLDP_PORT_SUBTYPE_MAC_ADDRESS: s_port_id = nm_utils_hwaddr_ntoa(port_id, port_id_len); break; - case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS: + case NM_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS: s_port_id = format_network_address(port_id, port_id_len); break; } @@ -468,7 +398,7 @@ lldp_neighbor_new(sd_lldp_neighbor *neighbor_sd) neigh = g_slice_new(LldpNeighbor); *neigh = (LldpNeighbor){ - .neighbor_sd = sd_lldp_neighbor_ref(neighbor_sd), + .neighbor_nm = nm_lldp_neighbor_ref(neighbor_nm), .chassis_id_type = chassis_id_type, .chassis_id = g_steal_pointer(&s_chassis_id), .port_id_type = port_id_type, @@ -480,15 +410,15 @@ lldp_neighbor_new(sd_lldp_neighbor *neighbor_sd) static GVariant * lldp_neighbor_to_variant(LldpNeighbor *neigh) { - struct ether_addr destination_address; - GVariantBuilder builder; - const char *str; - const guint8 *raw_data; - gsize raw_len; - uint16_t u16; - uint8_t *data8; - gsize len; - int r; + NMEtherAddr destination_address; + GVariantBuilder builder; + const char *str; + const guint8 *raw_data; + gsize raw_len; + uint16_t u16; + uint8_t *data8; + gsize len; + int r; if (neigh->variant) return neigh->variant; @@ -505,33 +435,33 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh) nm_g_variant_builder_add_sv_uint32(&builder, NM_LLDP_ATTR_PORT_ID_TYPE, neigh->port_id_type); nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_PORT_ID, neigh->port_id); - r = sd_lldp_neighbor_get_destination_address(neigh->neighbor_sd, &destination_address); + r = nm_lldp_neighbor_get_destination_address(neigh->neighbor_nm, &destination_address); if (r < 0) str = NULL; - else if (nm_utils_ether_addr_equal(&destination_address, LLDP_MAC_NEAREST_BRIDGE)) + else if (nm_ether_addr_equal(&destination_address, LLDP_MAC_NEAREST_BRIDGE)) str = NM_LLDP_DEST_NEAREST_BRIDGE; - else if (nm_utils_ether_addr_equal(&destination_address, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE)) + else if (nm_ether_addr_equal(&destination_address, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE)) str = NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE; - else if (nm_utils_ether_addr_equal(&destination_address, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE)) + else if (nm_ether_addr_equal(&destination_address, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE)) str = NM_LLDP_DEST_NEAREST_CUSTOMER_BRIDGE; else str = NULL; if (str) nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_DESTINATION, str); - if (sd_lldp_neighbor_get_port_description(neigh->neighbor_sd, &str) == 0) + if (nm_lldp_neighbor_get_port_description(neigh->neighbor_nm, &str) == 0) nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_PORT_DESCRIPTION, str); - if (sd_lldp_neighbor_get_system_name(neigh->neighbor_sd, &str) == 0) + if (nm_lldp_neighbor_get_system_name(neigh->neighbor_nm, &str) == 0) nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_SYSTEM_NAME, str); - if (sd_lldp_neighbor_get_system_description(neigh->neighbor_sd, &str) == 0) + if (nm_lldp_neighbor_get_system_description(neigh->neighbor_nm, &str) == 0) nm_g_variant_builder_add_sv_str(&builder, NM_LLDP_ATTR_SYSTEM_DESCRIPTION, str); - if (sd_lldp_neighbor_get_system_capabilities(neigh->neighbor_sd, &u16) == 0) + if (nm_lldp_neighbor_get_system_capabilities(neigh->neighbor_nm, &u16) == 0) nm_g_variant_builder_add_sv_uint32(&builder, NM_LLDP_ATTR_SYSTEM_CAPABILITIES, u16); - r = sd_lldp_neighbor_tlv_rewind(neigh->neighbor_sd); + r = nm_lldp_neighbor_tlv_rewind(neigh->neighbor_nm); if (r < 0) nm_assert_not_reached(); else { @@ -556,14 +486,14 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh) guint8 type; guint8 subtype; - if (sd_lldp_neighbor_tlv_get_type(neigh->neighbor_sd, &type) < 0) + if (nm_lldp_neighbor_tlv_get_type(neigh->neighbor_nm, &type) < 0) continue; - if (sd_lldp_neighbor_tlv_get_raw(neigh->neighbor_sd, (void *) &data8, &len) < 0) + if (nm_lldp_neighbor_tlv_get_raw(neigh->neighbor_nm, (void *) &data8, &len) < 0) continue; switch (type) { - case SD_LLDP_TYPE_MGMT_ADDRESS: + case NM_LLDP_TYPE_MGMT_ADDRESS: tmp_variant = parse_management_address_tlv(data8, len); if (tmp_variant) { if (!v_management_addresses_has) { @@ -573,13 +503,13 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh) g_variant_builder_add_value(&v_management_addresses, tmp_variant); } continue; - case SD_LLDP_TYPE_PRIVATE: + case NM_LLDP_TYPE_PRIVATE: break; default: continue; } - r = sd_lldp_neighbor_tlv_get_oui(neigh->neighbor_sd, oui, &subtype); + r = nm_lldp_neighbor_tlv_get_oui(neigh->neighbor_nm, oui, &subtype); if (r < 0) { if (r == -ENXIO) continue; @@ -609,15 +539,15 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh) data8 += 6; len -= 6; - if (memcmp(oui, SD_LLDP_OUI_802_1, sizeof(oui)) == 0) { + if (memcmp(oui, NM_LLDP_OUI_802_1, sizeof(oui)) == 0) { switch (subtype) { - case SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID: + case NM_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID: if (len != 2) continue; if (!v_ieee_802_1_pvid) v_ieee_802_1_pvid = g_variant_new_uint32(unaligned_read_be16(data8)); break; - case SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID: + case NM_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID: if (len != 3) continue; if (!v_ieee_802_1_ppvid) { @@ -633,7 +563,7 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh) g_variant_builder_add_value(&v_ieee_802_1_ppvids, g_variant_builder_end(&tmp_builder)); break; - case SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME: + case NM_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME: { gs_free char *name_to_free = NULL; const char *name; @@ -669,9 +599,9 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh) default: continue; } - } else if (memcmp(oui, SD_LLDP_OUI_802_3, sizeof(oui)) == 0) { + } else if (memcmp(oui, NM_LLDP_OUI_802_3, sizeof(oui)) == 0) { switch (subtype) { - case SD_LLDP_OUI_802_3_SUBTYPE_MAC_PHY_CONFIG_STATUS: + case NM_LLDP_OUI_802_3_SUBTYPE_MAC_PHY_CONFIG_STATUS: if (len != 5) continue; @@ -687,7 +617,7 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh) v_ieee_802_3_mac_phy_conf = g_variant_builder_end(&tmp_builder); } break; - case SD_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI: + case NM_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI: if (len != 3) continue; @@ -703,7 +633,7 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh) v_ieee_802_3_power_via_mdi = g_variant_builder_end(&tmp_builder); } break; - case SD_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE: + case NM_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE: if (len != 2) continue; if (!v_ieee_802_3_max_frame_size) @@ -711,9 +641,9 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh) g_variant_new_uint32(unaligned_read_be16(data8)); break; } - } else if (memcmp(oui, SD_LLDP_OUI_IANA, sizeof(oui)) == 0) { + } else if (memcmp(oui, NM_LLDP_OUI_IANA, sizeof(oui)) == 0) { switch (subtype) { - case SD_LLDP_OUI_IANA_SUBTYPE_MUD: + case NM_LLDP_OUI_IANA_SUBTYPE_MUD: if (!v_mud_url) { gs_free char *s_free = NULL; const char *s; @@ -725,7 +655,7 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh) break; } } - } while (sd_lldp_neighbor_tlv_next(neigh->neighbor_sd) > 0); + } while (nm_lldp_neighbor_tlv_next(neigh->neighbor_nm) > 0); if (v_management_addresses_has) nm_g_variant_builder_add_sv(&builder, @@ -777,18 +707,17 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh) GVariant * nmtst_lldp_parse_from_raw(const guint8 *raw_data, gsize raw_len) { - nm_auto(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *neighbor_sd = NULL; - nm_auto(lldp_neighbor_freep) LldpNeighbor *neigh = NULL; - GVariant *variant; - int r; + nm_auto(nm_lldp_neighbor_unrefp) NMLldpNeighbor *neighbor_nm = NULL; + nm_auto(lldp_neighbor_freep) LldpNeighbor *neigh = NULL; + GVariant *variant; g_assert(raw_data); g_assert(raw_len > 0); - r = sd_lldp_neighbor_from_raw(&neighbor_sd, raw_data, raw_len); - g_assert(r >= 0); + neighbor_nm = nm_lldp_neighbor_new_from_raw(raw_data, raw_len); + g_assert(neighbor_nm); - neigh = lldp_neighbor_new(neighbor_sd); + neigh = lldp_neighbor_new(neighbor_nm); g_assert(neigh); variant = lldp_neighbor_to_variant(neigh); @@ -843,18 +772,18 @@ data_changed_schedule(NMLldpListener *self) } static void -process_lldp_neighbor(NMLldpListener *self, sd_lldp_neighbor *neighbor_sd, gboolean remove) +process_lldp_neighbor(NMLldpListener *self, NMLldpNeighbor *neighbor_nm, gboolean remove) { nm_auto(lldp_neighbor_freep) LldpNeighbor *neigh = NULL; LldpNeighbor *neigh_old; nm_assert(self); - nm_assert(self->lldp_handle); + nm_assert(self->lldp_rx); nm_assert(self->lldp_neighbors); - g_return_if_fail(neighbor_sd); + g_return_if_fail(neighbor_nm); - neigh = lldp_neighbor_new(neighbor_sd); + neigh = lldp_neighbor_new(neighbor_nm); if (!neigh) { _LOGT("process: failed to parse neighbor"); return; @@ -884,14 +813,17 @@ handle_changed: } static void -lldp_event_handler(sd_lldp_rx *lldp, sd_lldp_rx_event_t event, sd_lldp_neighbor *n, void *userdata) +lldp_event_handler(NMLldpRX *lldp, NMLldpRXEvent event, NMLldpNeighbor *n, void *user_data) { - process_lldp_neighbor(userdata, + NMLldpListener *self = user_data; + + _LOGD("event: %s", nm_lldp_rx_event_to_string(event)); + process_lldp_neighbor(self, n, !NM_IN_SET(event, - SD_LLDP_RX_EVENT_ADDED, - SD_LLDP_RX_EVENT_UPDATED, - SD_LLDP_RX_EVENT_REFRESHED)); + NM_LLDP_RX_EVENT_ADDED, + NM_LLDP_RX_EVENT_UPDATED, + NM_LLDP_RX_EVENT_REFRESHED)); } /*****************************************************************************/ @@ -935,35 +867,14 @@ nm_lldp_listener_new(int ifindex, gpointer notify_user_data, GError **error) { - NMLldpListener *self = NULL; - sd_lldp_rx *lldp_handle; - int r; + NMLldpListener *self = NULL; + nm_auto(nm_lldp_rx_unrefp) NMLldpRX *lldp_rx = NULL; + int r; g_return_val_if_fail(ifindex > 0, FALSE); g_return_val_if_fail(!error || !*error, FALSE); g_return_val_if_fail(notify_callback, FALSE); - r = sd_lldp_rx_new(&lldp_handle); - if (r < 0) { - g_set_error_literal(error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_FAILED, - "initialization failed"); - return FALSE; - } - - r = sd_lldp_rx_set_ifindex(lldp_handle, ifindex); - if (r < 0) { - g_set_error_literal(error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_FAILED, - "failed setting ifindex"); - goto fail_handle; - } - - r = sd_lldp_rx_set_neighbors_max(lldp_handle, MAX_NEIGHBORS); - nm_assert(r == 0); - self = g_slice_new(NMLldpListener); *self = (NMLldpListener){ .ifindex = ifindex, @@ -971,39 +882,33 @@ nm_lldp_listener_new(int ifindex, .notify_user_data = notify_user_data, }; - r = sd_lldp_rx_set_callback(lldp_handle, lldp_event_handler, self); - if (r < 0) { - g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "set callback failed"); - goto fail_handle; - } + nm_assert(nm_g_main_context_is_thread_default(g_main_context_default())); - r = sd_lldp_rx_attach_event(lldp_handle, NULL, 0); - if (r < 0) { - g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "attach event failed"); - goto fail_attached; - } + lldp_rx = nm_lldp_rx_new(&((NMLldpRXConfig){ + .ifindex = ifindex, + .neighbors_max = MAX_NEIGHBORS, + .callback = lldp_event_handler, + .userdata = self, + })); - r = sd_lldp_rx_start(lldp_handle); + r = nm_lldp_rx_start(lldp_rx); if (r < 0) { g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "start failed"); - goto fail_attached; + goto fail; } - self->lldp_neighbors = g_hash_table_new_full(lldp_neighbor_id_hash, - lldp_neighbor_id_equal, + self->lldp_neighbors = g_hash_table_new_full((GHashFunc) lldp_neighbor_id_hash, + (GEqualFunc) lldp_neighbor_id_equal, (GDestroyNotify) lldp_neighbor_free, NULL); - self->lldp_handle = lldp_handle; + + self->lldp_rx = g_steal_pointer(&lldp_rx); _LOGD("start lldp listener"); return self; -fail_attached: - sd_lldp_rx_detach_event(lldp_handle); -fail_handle: - if (self) - nm_g_slice_free(self); - sd_lldp_rx_unref(lldp_handle); +fail: + nm_g_slice_free(self); return NULL; } @@ -1012,9 +917,8 @@ nm_lldp_listener_destroy(NMLldpListener *self) { g_return_if_fail(self); - sd_lldp_rx_stop(self->lldp_handle); - sd_lldp_rx_detach_event(self->lldp_handle); - sd_lldp_rx_unref(self->lldp_handle); + nm_lldp_rx_stop(self->lldp_rx); + nm_lldp_rx_unref(self->lldp_rx); nm_clear_g_source_inst(&self->ratelimit_source); diff --git a/src/core/devices/ovs/nm-device-ovs-bridge.c b/src/core/devices/ovs/nm-device-ovs-bridge.c index 048afb02..7b319af3 100644 --- a/src/core/devices/ovs/nm-device-ovs-bridge.c +++ b/src/core/devices/ovs/nm-device-ovs-bridge.c @@ -16,6 +16,7 @@ #include "nm-setting-connection.h" #include "nm-setting-ovs-bridge.h" #include "nm-setting-ovs-external-ids.h" +#include "nm-setting-ovs-other-config.h" #include "libnm-core-intern/nm-core-internal.h" #define _NMLOG_DEVICE_TYPE NMDeviceOvsBridge @@ -124,13 +125,14 @@ nm_device_ovs_reapply_connection(NMDevice *self, NMConnection *con_old, NMConnec device_type = NM_DEVICE_TYPE_OVS_BRIDGE; } - nm_ovsdb_set_external_ids( - nm_ovsdb_get(), - device_type, - nm_device_get_ip_iface(self), - nm_connection_get_uuid(con_new), - _nm_connection_get_setting(con_old, NM_TYPE_SETTING_OVS_EXTERNAL_IDS), - _nm_connection_get_setting(con_new, NM_TYPE_SETTING_OVS_EXTERNAL_IDS)); + nm_ovsdb_set_reapply(nm_ovsdb_get(), + device_type, + nm_device_get_ip_iface(self), + nm_connection_get_uuid(con_new), + _nm_connection_get_setting(con_old, NM_TYPE_SETTING_OVS_EXTERNAL_IDS), + _nm_connection_get_setting(con_new, NM_TYPE_SETTING_OVS_EXTERNAL_IDS), + _nm_connection_get_setting(con_old, NM_TYPE_SETTING_OVS_OTHER_CONFIG), + _nm_connection_get_setting(con_new, NM_TYPE_SETTING_OVS_OTHER_CONFIG)); } /*****************************************************************************/ diff --git a/src/core/devices/ovs/nm-device-ovs-bridge.h b/src/core/devices/ovs/nm-device-ovs-bridge.h index 2b893343..51cc685d 100644 --- a/src/core/devices/ovs/nm-device-ovs-bridge.h +++ b/src/core/devices/ovs/nm-device-ovs-bridge.h @@ -8,7 +8,7 @@ #define NM_TYPE_DEVICE_OVS_BRIDGE (nm_device_ovs_bridge_get_type()) #define NM_DEVICE_OVS_BRIDGE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridge)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridge)) #define NM_DEVICE_OVS_BRIDGE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridgeClass)) #define NM_IS_DEVICE_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_OVS_BRIDGE)) diff --git a/src/core/devices/ovs/nm-device-ovs-interface.h b/src/core/devices/ovs/nm-device-ovs-interface.h index 03b0e9a4..b8c951d2 100644 --- a/src/core/devices/ovs/nm-device-ovs-interface.h +++ b/src/core/devices/ovs/nm-device-ovs-interface.h @@ -8,7 +8,7 @@ #define NM_TYPE_DEVICE_OVS_INTERFACE (nm_device_ovs_interface_get_type()) #define NM_DEVICE_OVS_INTERFACE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterface)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterface)) #define NM_DEVICE_OVS_INTERFACE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterfaceClass)) #define NM_IS_DEVICE_OVS_INTERFACE(obj) \ diff --git a/src/core/devices/ovs/nm-device-ovs-port.h b/src/core/devices/ovs/nm-device-ovs-port.h index f4516c01..87446fa4 100644 --- a/src/core/devices/ovs/nm-device-ovs-port.h +++ b/src/core/devices/ovs/nm-device-ovs-port.h @@ -8,7 +8,7 @@ #define NM_TYPE_DEVICE_OVS_PORT (nm_device_ovs_port_get_type()) #define NM_DEVICE_OVS_PORT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPort)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPort)) #define NM_DEVICE_OVS_PORT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPortClass)) #define NM_IS_DEVICE_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_OVS_PORT)) diff --git a/src/core/devices/ovs/nm-ovs-factory.c b/src/core/devices/ovs/nm-ovs-factory.c index ff2c7858..50023778 100644 --- a/src/core/devices/ovs/nm-ovs-factory.c +++ b/src/core/devices/ovs/nm-ovs-factory.c @@ -27,7 +27,8 @@ typedef struct { } NMOvsFactoryClass; #define NM_TYPE_OVS_FACTORY (nm_ovs_factory_get_type()) -#define NM_OVS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_OVS_FACTORY, NMOvsFactory)) +#define NM_OVS_FACTORY(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_OVS_FACTORY, NMOvsFactory)) #define NM_OVS_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_OVS_FACTORY, NMOvsFactoryClass)) #define NM_IS_OVS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_OVS_FACTORY)) diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c index 0d688396..85b7953f 100644 --- a/src/core/devices/ovs/nm-ovsdb.c +++ b/src/core/devices/ovs/nm-ovsdb.c @@ -17,6 +17,7 @@ #include "devices/nm-device.h" #include "nm-manager.h" #include "nm-setting-ovs-external-ids.h" +#include "nm-setting-ovs-other-config.h" #include "nm-priv-helper-call.h" #include "libnm-platform/nm-platform.h" @@ -24,18 +25,26 @@ #define OVSDB_MAX_FAILURES 3 +#define OTHER_CONFIG_HWADDR "hwaddr" + /*****************************************************************************/ #if JANSSON_VERSION_HEX < 0x020400 #warning "requires at least libjansson 2.4" #endif +typedef enum { + STRDICT_TYPE_EXTERNAL_IDS, + STRDICT_TYPE_OTHER_CONFIG, +} StrdictType; + typedef struct { char *port_uuid; char *name; char *connection_uuid; GPtrArray *interfaces; /* interface uuids */ GArray *external_ids; + GArray *other_config; } OpenvswitchPort; typedef struct { @@ -44,6 +53,7 @@ typedef struct { char *connection_uuid; GPtrArray *ports; /* port uuids */ GArray *external_ids; + GArray *other_config; } OpenvswitchBridge; typedef struct { @@ -52,6 +62,7 @@ typedef struct { char *type; char *connection_uuid; GArray *external_ids; + GArray *other_config; } OpenvswitchInterface; /*****************************************************************************/ @@ -66,7 +77,7 @@ typedef enum { OVSDB_ADD_INTERFACE, OVSDB_DEL_INTERFACE, OVSDB_SET_INTERFACE_MTU, - OVSDB_SET_EXTERNAL_IDS, + OVSDB_SET_REAPPLY, } OvsdbCommand; #define CALL_ID_UNSPEC G_MAXUINT64 @@ -92,9 +103,11 @@ typedef union { NMDeviceType device_type; char *ifname; char *connection_uuid; - GHashTable *exid_old; - GHashTable *exid_new; - } set_external_ids; + GHashTable *external_ids_old; + GHashTable *external_ids_new; + GHashTable *other_config_old; + GHashTable *other_config_new; + } set_reapply; } OvsdbMethodPayload; typedef struct { @@ -223,20 +236,24 @@ static void cleanup_check_ready(NMOvsdb *self); }, \ })) -#define OVSDB_METHOD_PAYLOAD_SET_EXTERNAL_IDS(xdevice_type, \ - xifname, \ - xconnection_uuid, \ - xexid_old, \ - xexid_new) \ - (&((const OvsdbMethodPayload){ \ - .set_external_ids = \ - { \ - .device_type = xdevice_type, \ - .ifname = (char *) NM_CONSTCAST(char, (xifname)), \ - .connection_uuid = (char *) NM_CONSTCAST(char, (xconnection_uuid)), \ - .exid_old = (xexid_old), \ - .exid_new = (xexid_new), \ - }, \ +#define OVSDB_METHOD_PAYLOAD_SET_REAPPLY(xdevice_type, \ + xifname, \ + xconnection_uuid, \ + xexternal_ids_old, \ + xexternal_ids_new, \ + xother_config_old, \ + xother_config_new) \ + (&((const OvsdbMethodPayload){ \ + .set_reapply = \ + { \ + .device_type = xdevice_type, \ + .ifname = (char *) NM_CONSTCAST(char, (xifname)), \ + .connection_uuid = (char *) NM_CONSTCAST(char, (xconnection_uuid)), \ + .external_ids_old = (xexternal_ids_old), \ + .external_ids_new = (xexternal_ids_new), \ + .other_config_old = (xother_config_old), \ + .other_config_new = (xother_config_new), \ + }, \ })) /*****************************************************************************/ @@ -291,11 +308,13 @@ _call_complete(OvsdbMethodCall *call, json_t *response, GError *error) case OVSDB_SET_INTERFACE_MTU: nm_clear_g_free(&call->payload.set_interface_mtu.ifname); break; - case OVSDB_SET_EXTERNAL_IDS: - nm_clear_g_free(&call->payload.set_external_ids.ifname); - nm_clear_g_free(&call->payload.set_external_ids.connection_uuid); - nm_clear_pointer(&call->payload.set_external_ids.exid_old, g_hash_table_destroy); - nm_clear_pointer(&call->payload.set_external_ids.exid_new, g_hash_table_destroy); + case OVSDB_SET_REAPPLY: + nm_clear_g_free(&call->payload.set_reapply.ifname); + nm_clear_g_free(&call->payload.set_reapply.connection_uuid); + nm_clear_pointer(&call->payload.set_reapply.external_ids_old, g_hash_table_destroy); + nm_clear_pointer(&call->payload.set_reapply.external_ids_new, g_hash_table_destroy); + nm_clear_pointer(&call->payload.set_reapply.other_config_old, g_hash_table_destroy); + nm_clear_pointer(&call->payload.set_reapply.other_config_new, g_hash_table_destroy); break; } @@ -312,6 +331,7 @@ _free_bridge(OpenvswitchBridge *ovs_bridge) g_free(ovs_bridge->connection_uuid); g_ptr_array_free(ovs_bridge->ports, TRUE); nm_g_array_unref(ovs_bridge->external_ids); + nm_g_array_unref(ovs_bridge->other_config); nm_g_slice_free(ovs_bridge); } @@ -323,6 +343,7 @@ _free_port(OpenvswitchPort *ovs_port) g_free(ovs_port->connection_uuid); g_ptr_array_free(ovs_port->interfaces, TRUE); nm_g_array_unref(ovs_port->external_ids); + nm_g_array_unref(ovs_port->other_config); nm_g_slice_free(ovs_port); } @@ -334,6 +355,7 @@ _free_interface(OpenvswitchInterface *ovs_interface) g_free(ovs_interface->connection_uuid); g_free(ovs_interface->type); nm_g_array_unref(ovs_interface->external_ids); + nm_g_array_unref(ovs_interface->other_config); nm_g_slice_free(ovs_interface); } @@ -447,19 +469,22 @@ ovsdb_call_method(NMOvsdb *self, call->payload.set_interface_mtu.ifname, call->payload.set_interface_mtu.mtu); break; - case OVSDB_SET_EXTERNAL_IDS: - call->payload.set_external_ids.device_type = payload->set_external_ids.device_type; - call->payload.set_external_ids.ifname = g_strdup(payload->set_external_ids.ifname); - call->payload.set_external_ids.connection_uuid = - g_strdup(payload->set_external_ids.connection_uuid); - call->payload.set_external_ids.exid_old = - nm_g_hash_table_ref(payload->set_external_ids.exid_old); - call->payload.set_external_ids.exid_new = - nm_g_hash_table_ref(payload->set_external_ids.exid_new); + case OVSDB_SET_REAPPLY: + call->payload.set_reapply.device_type = payload->set_reapply.device_type; + call->payload.set_reapply.ifname = g_strdup(payload->set_reapply.ifname); + call->payload.set_reapply.connection_uuid = g_strdup(payload->set_reapply.connection_uuid); + call->payload.set_reapply.external_ids_old = + nm_g_hash_table_ref(payload->set_reapply.external_ids_old); + call->payload.set_reapply.external_ids_new = + nm_g_hash_table_ref(payload->set_reapply.external_ids_new); + call->payload.set_reapply.other_config_old = + nm_g_hash_table_ref(payload->set_reapply.other_config_old); + call->payload.set_reapply.other_config_new = + nm_g_hash_table_ref(payload->set_reapply.other_config_new); _LOGT_call(call, - "new: set-external-ids con-uuid=%s, interface=%s", - call->payload.set_external_ids.connection_uuid, - call->payload.set_external_ids.ifname); + "new: set external-ids/other-config con-uuid=%s, interface=%s", + call->payload.set_reapply.connection_uuid, + call->payload.set_reapply.ifname); break; } @@ -604,12 +629,12 @@ _set_bridge_mac(json_t *params, const char *ifname, const char *mac) "other_config", "delete", "set", - "hwaddr", + OTHER_CONFIG_HWADDR, "other_config", "insert", "map", - "hwaddr", + OTHER_CONFIG_HWADDR, mac, "where", @@ -677,92 +702,155 @@ _set_port_interfaces(json_t *params, const char *ifname, json_t *new_interfaces) } static json_t * -_j_create_external_ids_array_new(NMConnection *connection) +_j_create_strdict_new(NMConnection *connection, + StrdictType strdict_type, + const char *other_config_hwaddr) { + NMSettingOvsOtherConfig *s_other_config = NULL; + NMSettingOvsExternalIDs *s_external_ids = NULL; json_t *array; - const char *const *external_ids = NULL; - guint n_external_ids = 0; + const char *const *strv = NULL; + guint n_strv = 0; guint i; const char *uuid; - NMSettingOvsExternalIDs *s_exid; nm_assert(NM_IS_CONNECTION(connection)); + nm_assert(NM_IN_SET(strdict_type, STRDICT_TYPE_EXTERNAL_IDS, STRDICT_TYPE_OTHER_CONFIG)); array = json_array(); - uuid = nm_connection_get_uuid(connection); - nm_assert(uuid); - json_array_append_new(array, json_pack("[s, s]", NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, uuid)); + if (strdict_type == STRDICT_TYPE_EXTERNAL_IDS) { + uuid = nm_connection_get_uuid(connection); + nm_assert(uuid); + json_array_append_new(array, + json_pack("[s, s]", NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, uuid)); + } else { + if (other_config_hwaddr) { + json_array_append_new(array, + json_pack("[s, s]", OTHER_CONFIG_HWADDR, other_config_hwaddr)); + } + } + + if (strdict_type == STRDICT_TYPE_EXTERNAL_IDS) { + s_external_ids = _nm_connection_get_setting(connection, NM_TYPE_SETTING_OVS_EXTERNAL_IDS); + if (s_external_ids) + strv = nm_setting_ovs_external_ids_get_data_keys(s_external_ids, &n_strv); + } else { + s_other_config = _nm_connection_get_setting(connection, NM_TYPE_SETTING_OVS_OTHER_CONFIG); + if (s_other_config) + strv = nm_setting_ovs_other_config_get_data_keys(s_other_config, &n_strv); + } + + for (i = 0; i < n_strv; i++) { + const char *k = strv[i]; - s_exid = _nm_connection_get_setting(connection, NM_TYPE_SETTING_OVS_EXTERNAL_IDS); - if (s_exid) - external_ids = nm_setting_ovs_external_ids_get_data_keys(s_exid, &n_external_ids); - for (i = 0; i < n_external_ids; i++) { - const char *k = external_ids[i]; + if (strdict_type == STRDICT_TYPE_OTHER_CONFIG && other_config_hwaddr + && nm_streq(k, OTHER_CONFIG_HWADDR)) { + /* "hwaddr" is explicitly overwritten. */ + continue; + } json_array_append_new( array, - json_pack("[s, s]", k, nm_setting_ovs_external_ids_get_data(s_exid, k))); + json_pack("[s, s]", + k, + strdict_type == STRDICT_TYPE_EXTERNAL_IDS + ? nm_setting_ovs_external_ids_get_data(s_external_ids, k) + : nm_setting_ovs_other_config_get_data(s_other_config, k))); } return json_pack("[s, o]", "map", array); } -static json_t * -_j_create_external_ids_array_update(const char *connection_uuid, - GHashTable *exid_old, - GHashTable *exid_new) +static void +_j_create_strv_array_update(json_t *mutations, + StrdictType strdict_type, + const char *connection_uuid, + GHashTable *hash_old, + GHashTable *hash_new) { GHashTableIter iter; - json_t *mutations; json_t *array; const char *key; const char *val; - nm_assert(connection_uuid); + /* This is called during reapply. We accept reapplying all settings, + * except other_config:hwaddr. That one cannot change and is specially + * handled below. The reason is that we knew the correct "hwaddr" during + * _j_create_strdict_new(), but we don't do now. At least not easily, + * and it's not clear that reapply of the MAC address is really useful. */ - mutations = json_array(); + nm_assert((!!connection_uuid) == (strdict_type == STRDICT_TYPE_EXTERNAL_IDS)); + nm_assert(NM_IN_SET(strdict_type, STRDICT_TYPE_EXTERNAL_IDS, STRDICT_TYPE_OTHER_CONFIG)); - array = json_array(); - if (exid_old) { - g_hash_table_iter_init(&iter, exid_old); + array = NULL; + if (hash_old) { + g_hash_table_iter_init(&iter, hash_old); while (g_hash_table_iter_next(&iter, (gpointer *) &key, NULL)) { + if (strdict_type == STRDICT_TYPE_OTHER_CONFIG && nm_streq(key, OTHER_CONFIG_HWADDR)) + continue; + if (!array) + array = json_array(); json_array_append_new(array, json_string(key)); } } - if (exid_new) { - g_hash_table_iter_init(&iter, exid_new); + if (hash_new) { + g_hash_table_iter_init(&iter, hash_new); while (g_hash_table_iter_next(&iter, (gpointer *) &key, NULL)) { - if (nm_g_hash_table_contains(exid_old, key)) + if (strdict_type == STRDICT_TYPE_OTHER_CONFIG && nm_streq(key, OTHER_CONFIG_HWADDR)) continue; + if (nm_g_hash_table_contains(hash_old, key)) + continue; + if (!array) + array = json_array(); json_array_append_new(array, json_string(key)); } } - if (!nm_g_hash_table_contains(exid_old, NM_OVS_EXTERNAL_ID_NM_PREFIX) - && !nm_g_hash_table_contains(exid_new, NM_OVS_EXTERNAL_ID_NM_PREFIX)) { - json_array_append_new(array, json_string(NM_OVS_EXTERNAL_ID_NM_PREFIX)); + if (strdict_type == STRDICT_TYPE_EXTERNAL_IDS) { + if (!nm_g_hash_table_contains(hash_old, NM_OVS_EXTERNAL_ID_NM_PREFIX) + && !nm_g_hash_table_contains(hash_new, NM_OVS_EXTERNAL_ID_NM_PREFIX)) { + if (!array) + array = json_array(); + json_array_append_new(array, json_string(NM_OVS_EXTERNAL_ID_NM_PREFIX)); + } + } + if (array) { + json_array_append_new( + mutations, + json_pack("[s, s, [s, o]]", + strdict_type == STRDICT_TYPE_EXTERNAL_IDS ? "external_ids" : "other_config", + "delete", + "set", + array)); } - json_array_append_new(mutations, - json_pack("[s, s, [s, o]]", "external_ids", "delete", "set", array)); array = json_array(); - json_array_append_new( - array, - json_pack("[s, s]", NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, connection_uuid)); - - if (exid_new) { - g_hash_table_iter_init(&iter, exid_new); + if (strdict_type == STRDICT_TYPE_EXTERNAL_IDS) { + json_array_append_new( + array, + json_pack("[s, s]", NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID, connection_uuid)); + } + if (hash_new) { + g_hash_table_iter_init(&iter, hash_new); while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) { - if (NM_STR_HAS_PREFIX(key, NM_OVS_EXTERNAL_ID_NM_PREFIX)) + if (strdict_type == STRDICT_TYPE_EXTERNAL_IDS) { + if (NM_STR_HAS_PREFIX(key, NM_OVS_EXTERNAL_ID_NM_PREFIX)) + continue; + } + if (strdict_type == STRDICT_TYPE_OTHER_CONFIG && nm_streq(key, OTHER_CONFIG_HWADDR)) continue; json_array_append_new(array, json_pack("[s, s]", key, val)); } } - json_array_append_new(mutations, - json_pack("[s, s, [s, o]]", "external_ids", "insert", "map", array)); - return mutations; + json_array_append_new( + mutations, + json_pack("[s, s, [s, o]]", + strdict_type == STRDICT_TYPE_EXTERNAL_IDS ? "external_ids" : "other_config", + "insert", + "map", + array)); } /** @@ -779,18 +867,19 @@ _insert_interface(json_t *params, const char *type = NULL; NMSettingOvsInterface *s_ovs_iface; NMSettingOvsDpdk *s_ovs_dpdk; - const char *devargs; - guint32 n_rxq; char sbuf[64]; json_t *dpdk_array; NMSettingOvsPatch *s_ovs_patch; json_t *options = json_array(); json_t *row; - guint32 mtu = 0; + guint32 mtu = 0; + guint32 ofport_request = 0; s_ovs_iface = nm_connection_get_setting_ovs_interface(interface); - if (s_ovs_iface) - type = nm_setting_ovs_interface_get_interface_type(s_ovs_iface); + if (s_ovs_iface) { + type = nm_setting_ovs_interface_get_interface_type(s_ovs_iface); + ofport_request = nm_setting_ovs_interface_get_ofport_request(s_ovs_iface); + } if (nm_streq0(type, "internal")) { NMSettingWired *s_wired; @@ -808,8 +897,15 @@ _insert_interface(json_t *params, s_ovs_patch = nm_connection_get_setting_ovs_patch(interface); if (s_ovs_dpdk) { - devargs = nm_setting_ovs_dpdk_get_devargs(s_ovs_dpdk); - n_rxq = nm_setting_ovs_dpdk_get_n_rxq(s_ovs_dpdk); + const char *devargs; + guint32 n_rxq; + guint32 n_rxq_desc; + guint32 n_txq_desc; + + devargs = nm_setting_ovs_dpdk_get_devargs(s_ovs_dpdk); + n_rxq = nm_setting_ovs_dpdk_get_n_rxq(s_ovs_dpdk); + n_rxq_desc = nm_setting_ovs_dpdk_get_n_rxq_desc(s_ovs_dpdk); + n_txq_desc = nm_setting_ovs_dpdk_get_n_txq_desc(s_ovs_dpdk); dpdk_array = json_array(); @@ -820,6 +916,16 @@ _insert_interface(json_t *params, json_array_append_new(dpdk_array, json_pack("[s,s]", "n_rxq", nm_sprintf_buf(sbuf, "%u", n_rxq))); } + if (n_rxq_desc != 0) { + json_array_append_new( + dpdk_array, + json_pack("[s,s]", "n_rxq_desc", nm_sprintf_buf(sbuf, "%u", n_rxq_desc))); + } + if (n_txq_desc != 0) { + json_array_append_new( + dpdk_array, + json_pack("[s,s]", "n_txq_desc", nm_sprintf_buf(sbuf, "%u", n_txq_desc))); + } json_array_append_new(options, dpdk_array); @@ -831,7 +937,7 @@ _insert_interface(json_t *params, json_array_append_new(options, json_array()); } - row = json_pack("{s:s, s:s, s:o, s:o}", + row = json_pack("{s:s, s:s, s:o, s:o, s:o}", "name", nm_connection_get_interface_name(interface), "type", @@ -839,7 +945,9 @@ _insert_interface(json_t *params, "options", options, "external_ids", - _j_create_external_ids_array_new(interface)); + _j_create_strdict_new(interface, STRDICT_TYPE_EXTERNAL_IDS, NULL), + "other_config", + _j_create_strdict_new(interface, STRDICT_TYPE_OTHER_CONFIG, NULL)); if (cloned_mac) json_object_set_new(row, "mac", json_string(cloned_mac)); @@ -847,6 +955,9 @@ _insert_interface(json_t *params, if (mtu != 0) json_object_set_new(row, "mtu_request", json_integer(mtu)); + if (ofport_request != 0) + json_object_set_new(row, "ofport_request", json_integer(ofport_request)); + json_array_append_new(params, json_pack("{s:s, s:s, s:o, s:s}", "op", @@ -869,6 +980,7 @@ _insert_port(json_t *params, NMConnection *port, json_t *new_interfaces) { NMSettingOvsPort *s_ovs_port; const char *vlan_mode = NULL; + json_t *trunks = NULL; guint tag = 0; const char *lacp = NULL; const char *bond_mode = NULL; @@ -881,18 +993,34 @@ _insert_port(json_t *params, NMConnection *port, json_t *new_interfaces) row = json_object(); if (s_ovs_port) { + const GPtrArray *ranges; + guint i; + guint64 start; + guint64 end; + vlan_mode = nm_setting_ovs_port_get_vlan_mode(s_ovs_port); tag = nm_setting_ovs_port_get_tag(s_ovs_port); lacp = nm_setting_ovs_port_get_lacp(s_ovs_port); bond_mode = nm_setting_ovs_port_get_bond_mode(s_ovs_port); bond_updelay = nm_setting_ovs_port_get_bond_updelay(s_ovs_port); bond_downdelay = nm_setting_ovs_port_get_bond_downdelay(s_ovs_port); + + ranges = _nm_setting_ovs_port_get_trunks_arr(s_ovs_port); + for (i = 0; i < ranges->len; i++) { + if (!trunks) + trunks = json_array(); + nm_range_get_range(ranges->pdata[i], &start, &end); + for (; start <= end; start++) + json_array_append_new(trunks, json_integer(start)); + } } if (vlan_mode) json_object_set_new(row, "vlan_mode", json_string(vlan_mode)); if (tag) json_object_set_new(row, "tag", json_integer(tag)); + if (trunks) + json_object_set_new(row, "trunks", json_pack("[s, o]", "set", trunks)); if (lacp) json_object_set_new(row, "lacp", json_string(lacp)); if (bond_mode) @@ -904,7 +1032,12 @@ _insert_port(json_t *params, NMConnection *port, json_t *new_interfaces) json_object_set_new(row, "name", json_string(nm_connection_get_interface_name(port))); json_object_set_new(row, "interfaces", json_pack("[s, O]", "set", new_interfaces)); - json_object_set_new(row, "external_ids", _j_create_external_ids_array_new(port)); + json_object_set_new(row, + "external_ids", + _j_create_strdict_new(port, STRDICT_TYPE_EXTERNAL_IDS, NULL)); + json_object_set_new(row, + "other_config", + _j_create_strdict_new(port, STRDICT_TYPE_OTHER_CONFIG, NULL)); /* Create a new one. */ json_array_append_new(params, @@ -964,13 +1097,12 @@ _insert_bridge(json_t *params, json_object_set_new(row, "name", json_string(nm_connection_get_interface_name(bridge))); json_object_set_new(row, "ports", json_pack("[s, O]", "set", new_ports)); - json_object_set_new(row, "external_ids", _j_create_external_ids_array_new(bridge)); - - if (cloned_mac) { - json_object_set_new(row, - "other_config", - json_pack("[s, [[s, s]]]", "map", "hwaddr", cloned_mac)); - } + json_object_set_new(row, + "external_ids", + _j_create_strdict_new(bridge, STRDICT_TYPE_EXTERNAL_IDS, NULL)); + json_object_set_new(row, + "other_config", + _j_create_strdict_new(bridge, STRDICT_TYPE_OTHER_CONFIG, cloned_mac)); /* Create a new one. */ json_array_append_new(params, @@ -1332,9 +1464,9 @@ ovsdb_next_command(NMOvsdb *self) switch (call->command) { case OVSDB_MONITOR: msg = json_pack("{s:I, s:s, s:[s, n, {" - " s:[{s:[s, s, s]}]," - " s:[{s:[s, s, s]}]," " s:[{s:[s, s, s, s]}]," + " s:[{s:[s, s, s, s]}]," + " s:[{s:[s, s, s, s, s]}]," " s:[{s:[]}]" "}]}", "id", @@ -1348,16 +1480,19 @@ ovsdb_next_command(NMOvsdb *self) "name", "ports", "external_ids", + "other_config", "Port", "columns", "name", "interfaces", "external_ids", + "other_config", "Interface", "columns", "name", "type", "external_ids", + "other_config", "error", "Open_vSwitch", "columns"); @@ -1398,24 +1533,38 @@ ovsdb_next_command(NMOvsdb *self) "==", call->payload.set_interface_mtu.ifname)); break; - case OVSDB_SET_EXTERNAL_IDS: + case OVSDB_SET_REAPPLY: + { + json_t *mutations; + + mutations = json_array(); + + _j_create_strv_array_update(mutations, + STRDICT_TYPE_EXTERNAL_IDS, + call->payload.set_reapply.connection_uuid, + call->payload.set_reapply.external_ids_old, + call->payload.set_reapply.external_ids_new); + _j_create_strv_array_update(mutations, + STRDICT_TYPE_OTHER_CONFIG, + NULL, + call->payload.set_reapply.other_config_old, + call->payload.set_reapply.other_config_new); + json_array_append_new( params, json_pack("{s:s, s:s, s:o, s:[[s, s, s]]}", "op", "mutate", "table", - _device_type_to_table(call->payload.set_external_ids.device_type), + _device_type_to_table(call->payload.set_reapply.device_type), "mutations", - _j_create_external_ids_array_update( - call->payload.set_external_ids.connection_uuid, - call->payload.set_external_ids.exid_old, - call->payload.set_external_ids.exid_new), + mutations, "where", "name", "==", - call->payload.set_external_ids.ifname)); + call->payload.set_reapply.ifname)); break; + } default: nm_assert_not_reached(); @@ -1500,19 +1649,18 @@ _uuids_to_array(const json_t *items) } static void -_external_ids_extract(json_t *external_ids, GArray **out_array, const char **out_connection_uuid) +_strdict_extract(json_t *strdict, GArray **out_array) { json_t *array; json_t *value; gsize index; nm_assert(out_array && !*out_array); - nm_assert(!out_connection_uuid || !*out_connection_uuid); - if (!nm_streq0("map", json_string_value(json_array_get(external_ids, 0)))) + if (!nm_streq0("map", json_string_value(json_array_get(strdict, 0)))) return; - array = json_array_get(external_ids, 1); + array = json_array_get(strdict, 1); json_array_foreach (array, index, value) { const char *key = json_string_value(json_array_get(value, 0)); @@ -1533,16 +1681,26 @@ _external_ids_extract(json_t *external_ids, GArray **out_array, const char **out .name = g_strdup(key), .value_str = g_strdup(val), }; - - if (out_connection_uuid && nm_streq(v->name, NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID)) { - *out_connection_uuid = v->value_str; - out_connection_uuid = NULL; - } } } +static const char * +_strdict_find_key(GArray *array, const char *key) +{ + gssize idx; + + idx = nm_utils_named_value_list_find(nm_g_array_first_p(array, NMUtilsNamedValue), + nm_g_array_len(array), + key, + FALSE); + if (idx < 0) + return NULL; + + return nm_g_array_index(array, NMUtilsNamedValue, idx).value_str; +} + static gboolean -_external_ids_equal(const GArray *arr1, const GArray *arr2) +_strdict_equals(const GArray *arr1, const GArray *arr2) { guint n; guint i; @@ -1552,19 +1710,19 @@ _external_ids_equal(const GArray *arr1, const GArray *arr2) if (n != nm_g_array_len(arr2)) return FALSE; for (i = 0; i < n; i++) { - const NMUtilsNamedValue *n1 = &g_array_index(arr1, NMUtilsNamedValue, i); - const NMUtilsNamedValue *n2 = &g_array_index(arr2, NMUtilsNamedValue, i); + const NMUtilsNamedValue *n1 = &nm_g_array_index(arr1, NMUtilsNamedValue, i); + const NMUtilsNamedValue *n2 = &nm_g_array_index(arr2, NMUtilsNamedValue, i); - if (!nm_streq0(n1->name, n2->name)) + if (!nm_streq(n1->name, n2->name)) return FALSE; - if (!nm_streq0(n1->value_str, n2->value_str)) + if (!nm_streq(n1->value_str, n2->value_str)) return FALSE; } return TRUE; } static char * -_external_ids_to_string(const GArray *arr) +_strdict_to_string(const GArray *arr) { NMStrBuf strbuf; guint i; @@ -1575,7 +1733,7 @@ _external_ids_to_string(const GArray *arr) strbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_104, FALSE); nm_str_buf_append(&strbuf, "["); for (i = 0; i < arr->len; i++) { - const NMUtilsNamedValue *n = &g_array_index(arr, NMUtilsNamedValue, i); + const NMUtilsNamedValue *n = &nm_g_array_index(arr, NMUtilsNamedValue, i); if (i > 0) nm_str_buf_append_c(&strbuf, ','); @@ -1605,8 +1763,9 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) json_t *interface = NULL; json_t *items; json_t *external_ids; + json_t *other_config; json_error_t json_error = { - 0, + 0, }; void *iter; const char *name; @@ -1644,12 +1803,13 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) json_object_foreach (interface, key, value) { OpenvswitchInterface *ovs_interface; gs_unref_array GArray *external_ids_arr = NULL; + gs_unref_array GArray *other_config_arr = NULL; const char *connection_uuid = NULL; json_t *error = NULL; int r; r = json_unpack(value, - "{s:{s:s, s:s, s?:o, s:o}}", + "{s:{s:s, s:s, s?:o, s:o, s:o}}", "new", "name", &name, @@ -1658,7 +1818,9 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) "error", &error, "external_ids", - &external_ids); + &external_ids, + "other_config", + &other_config); if (r != 0) { gpointer unused; @@ -1701,7 +1863,10 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) nm_clear_pointer(&ovs_interface, _free_interface); } - _external_ids_extract(external_ids, &external_ids_arr, &connection_uuid); + _strdict_extract(external_ids, &external_ids_arr); + connection_uuid = + _strdict_find_key(external_ids_arr, NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID); + _strdict_extract(other_config, &other_config_arr); if (ovs_interface) { gboolean changed = FALSE; @@ -1710,14 +1875,20 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) changed |= nm_strdup_reset(&ovs_interface->type, type); changed |= nm_strdup_reset(&ovs_interface->connection_uuid, connection_uuid); - if (!_external_ids_equal(ovs_interface->external_ids, external_ids_arr)) { + if (!_strdict_equals(ovs_interface->external_ids, external_ids_arr)) { NM_SWAP(&ovs_interface->external_ids, &external_ids_arr); changed = TRUE; } + if (!_strdict_equals(ovs_interface->other_config, other_config_arr)) { + NM_SWAP(&ovs_interface->other_config, &other_config_arr); + changed = TRUE; + } if (changed) { - gs_free char *strtmp = NULL; + gs_free char *strtmp1 = NULL; + gs_free char *strtmp2 = NULL; - _LOGT("obj[iface:%s]: changed an '%s' interface: %s%s%s, external-ids=%s", + _LOGT("obj[iface:%s]: changed an '%s' interface: %s%s%s, external-ids=%s, " + "other-config=%s", key, type, ovs_interface->name, @@ -1725,10 +1896,12 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) ", ", ovs_interface->connection_uuid, ""), - (strtmp = _external_ids_to_string(ovs_interface->external_ids))); + (strtmp1 = _strdict_to_string(ovs_interface->external_ids)), + (strtmp2 = _strdict_to_string(ovs_interface->other_config))); } } else { - gs_free char *strtmp = NULL; + gs_free char *strtmp1 = NULL; + gs_free char *strtmp2 = NULL; ovs_interface = g_slice_new(OpenvswitchInterface); *ovs_interface = (OpenvswitchInterface){ @@ -1737,17 +1910,20 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) .type = g_strdup(type), .connection_uuid = g_strdup(connection_uuid), .external_ids = g_steal_pointer(&external_ids_arr), + .other_config = g_steal_pointer(&other_config_arr), }; g_hash_table_add(priv->interfaces, ovs_interface); - _LOGT("obj[iface:%s]: added an '%s' interface: %s%s%s, external-ids=%s", - key, - ovs_interface->type, - ovs_interface->name, - NM_PRINT_FMT_QUOTED2(ovs_interface->connection_uuid, - ", ", - ovs_interface->connection_uuid, - ""), - (strtmp = _external_ids_to_string(ovs_interface->external_ids))); + _LOGT( + "obj[iface:%s]: added an '%s' interface: %s%s%s, external-ids=%s, other-config=%s", + key, + ovs_interface->type, + ovs_interface->name, + NM_PRINT_FMT_QUOTED2(ovs_interface->connection_uuid, + ", ", + ovs_interface->connection_uuid, + ""), + (strtmp1 = _strdict_to_string(ovs_interface->external_ids)), + (strtmp2 = _strdict_to_string(ovs_interface->other_config))); _signal_emit_device_added(self, ovs_interface->name, NM_DEVICE_TYPE_OVS_INTERFACE, @@ -1768,16 +1944,19 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) gs_unref_ptrarray GPtrArray *interfaces = NULL; OpenvswitchPort *ovs_port; gs_unref_array GArray *external_ids_arr = NULL; + gs_unref_array GArray *other_config_arr = NULL; const char *connection_uuid = NULL; int r; r = json_unpack(value, - "{s:{s:s, s:o, s:o}}", + "{s:{s:s, s:o, s:o, s:o}}", "new", "name", &name, "external_ids", &external_ids, + "other_config", + &other_config, "interfaces", &items); if (r != 0) { @@ -1811,7 +1990,11 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) nm_clear_pointer(&ovs_port, _free_port); } - _external_ids_extract(external_ids, &external_ids_arr, &connection_uuid); + _strdict_extract(external_ids, &external_ids_arr); + connection_uuid = + _strdict_find_key(external_ids_arr, NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID); + _strdict_extract(other_config, &other_config_arr); + interfaces = _uuids_to_array(items); if (ovs_port) { @@ -1825,24 +2008,31 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) NM_SWAP(&ovs_port->interfaces, &interfaces); changed = TRUE; } - if (!_external_ids_equal(ovs_port->external_ids, external_ids_arr)) { + if (!_strdict_equals(ovs_port->external_ids, external_ids_arr)) { NM_SWAP(&ovs_port->external_ids, &external_ids_arr); changed = TRUE; } + if (!_strdict_equals(ovs_port->other_config, other_config_arr)) { + NM_SWAP(&ovs_port->other_config, &other_config_arr); + changed = TRUE; + } if (changed) { - gs_free char *strtmp = NULL; + gs_free char *strtmp1 = NULL; + gs_free char *strtmp2 = NULL; - _LOGT("obj[port:%s]: changed a port: %s%s%s, external-ids=%s", + _LOGT("obj[port:%s]: changed a port: %s%s%s, external-ids=%s, other-config=%s", key, ovs_port->name, NM_PRINT_FMT_QUOTED2(ovs_port->connection_uuid, ", ", ovs_port->connection_uuid, ""), - (strtmp = _external_ids_to_string(ovs_port->external_ids))); + (strtmp1 = _strdict_to_string(ovs_port->external_ids)), + (strtmp2 = _strdict_to_string(ovs_port->other_config))); } } else { - gs_free char *strtmp = NULL; + gs_free char *strtmp1 = NULL; + gs_free char *strtmp2 = NULL; ovs_port = g_slice_new(OpenvswitchPort); *ovs_port = (OpenvswitchPort){ @@ -1851,16 +2041,18 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) .connection_uuid = g_strdup(connection_uuid), .interfaces = g_steal_pointer(&interfaces), .external_ids = g_steal_pointer(&external_ids_arr), + .other_config = g_steal_pointer(&other_config_arr), }; g_hash_table_add(priv->ports, ovs_port); - _LOGT("obj[port:%s]: added a port: %s%s%s, external-ids=%s", + _LOGT("obj[port:%s]: added a port: %s%s%s, external-ids=%s, other-config=%s", key, ovs_port->name, NM_PRINT_FMT_QUOTED2(ovs_port->connection_uuid, ", ", ovs_port->connection_uuid, ""), - (strtmp = _external_ids_to_string(ovs_port->external_ids))); + (strtmp1 = _strdict_to_string(ovs_port->external_ids)), + (strtmp2 = _strdict_to_string(ovs_port->other_config))); _signal_emit_device_added(self, ovs_port->name, NM_DEVICE_TYPE_OVS_PORT, NULL); } } @@ -1869,16 +2061,19 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) gs_unref_ptrarray GPtrArray *ports = NULL; OpenvswitchBridge *ovs_bridge; gs_unref_array GArray *external_ids_arr = NULL; + gs_unref_array GArray *other_config_arr = NULL; const char *connection_uuid = NULL; int r; r = json_unpack(value, - "{s:{s:s, s:o, s:o}}", + "{s:{s:s, s:o, s:o, s:o}}", "new", "name", &name, "external_ids", &external_ids, + "other_config", + &other_config, "ports", &items); @@ -1916,7 +2111,11 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) nm_clear_pointer(&ovs_bridge, _free_bridge); } - _external_ids_extract(external_ids, &external_ids_arr, &connection_uuid); + _strdict_extract(external_ids, &external_ids_arr); + connection_uuid = + _strdict_find_key(external_ids_arr, NM_OVS_EXTERNAL_ID_NM_CONNECTION_UUID); + _strdict_extract(other_config, &other_config_arr); + ports = _uuids_to_array(items); if (ovs_bridge) { @@ -1930,24 +2129,31 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) NM_SWAP(&ovs_bridge->ports, &ports); changed = TRUE; } - if (!_external_ids_equal(ovs_bridge->external_ids, external_ids_arr)) { + if (!_strdict_equals(ovs_bridge->external_ids, external_ids_arr)) { NM_SWAP(&ovs_bridge->external_ids, &external_ids_arr); changed = TRUE; } + if (!_strdict_equals(ovs_bridge->other_config, other_config_arr)) { + NM_SWAP(&ovs_bridge->other_config, &other_config_arr); + changed = TRUE; + } if (changed) { - gs_free char *strtmp = NULL; + gs_free char *strtmp1 = NULL; + gs_free char *strtmp2 = NULL; - _LOGT("obj[bridge:%s]: changed a bridge: %s%s%s, external-ids=%s", + _LOGT("obj[bridge:%s]: changed a bridge: %s%s%s, external-ids=%s, other-config=%s", key, ovs_bridge->name, NM_PRINT_FMT_QUOTED2(ovs_bridge->connection_uuid, ", ", ovs_bridge->connection_uuid, ""), - (strtmp = _external_ids_to_string(ovs_bridge->external_ids))); + (strtmp1 = _strdict_to_string(ovs_bridge->external_ids)), + (strtmp2 = _strdict_to_string(ovs_bridge->external_ids))); } } else { - gs_free char *strtmp = NULL; + gs_free char *strtmp1 = NULL; + gs_free char *strtmp2 = NULL; ovs_bridge = g_slice_new(OpenvswitchBridge); *ovs_bridge = (OpenvswitchBridge){ @@ -1956,16 +2162,18 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) .connection_uuid = g_strdup(connection_uuid), .ports = g_steal_pointer(&ports), .external_ids = g_steal_pointer(&external_ids_arr), + .other_config = g_steal_pointer(&other_config_arr), }; g_hash_table_add(priv->bridges, ovs_bridge); - _LOGT("obj[bridge:%s]: added a bridge: %s%s%s, external-ids=%s", + _LOGT("obj[bridge:%s]: added a bridge: %s%s%s, external-ids=%s, other-config=%s", key, ovs_bridge->name, NM_PRINT_FMT_QUOTED2(ovs_bridge->connection_uuid, ", ", ovs_bridge->connection_uuid, ""), - (strtmp = _external_ids_to_string(ovs_bridge->external_ids))); + (strtmp1 = _strdict_to_string(ovs_bridge->external_ids)), + (strtmp2 = _strdict_to_string(ovs_bridge->other_config))); _signal_emit_device_added(self, ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE, NULL); } } @@ -2007,7 +2215,7 @@ ovsdb_got_msg(NMOvsdb *self, json_t *msg) { NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); json_error_t json_error = { - 0, + 0, }; json_t *json_id = NULL; json_int_t id = (json_int_t) -1; @@ -2149,7 +2357,7 @@ ovsdb_read_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) gssize size; json_t *msg; json_error_t json_error = { - 0, + 0, }; size = g_input_stream_read_finish(stream, res, &error); @@ -2703,31 +2911,50 @@ nm_ovsdb_set_interface_mtu(NMOvsdb *self, } void -nm_ovsdb_set_external_ids(NMOvsdb *self, - NMDeviceType device_type, - const char *ifname, - const char *connection_uuid, - NMSettingOvsExternalIDs *s_exid_old, - NMSettingOvsExternalIDs *s_exid_new) +nm_ovsdb_set_reapply(NMOvsdb *self, + NMDeviceType device_type, + const char *ifname, + const char *connection_uuid, + NMSettingOvsExternalIDs *s_external_ids_old, + NMSettingOvsExternalIDs *s_external_ids_new, + NMSettingOvsOtherConfig *s_other_config_old, + NMSettingOvsOtherConfig *s_other_config_new) { - gs_unref_hashtable GHashTable *exid_old = NULL; - gs_unref_hashtable GHashTable *exid_new = NULL; - - exid_old = - s_exid_old ? nm_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_exid_old)) : NULL; - exid_new = - s_exid_new ? nm_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_exid_new)) : NULL; + gs_unref_hashtable GHashTable *external_ids_old = NULL; + gs_unref_hashtable GHashTable *external_ids_new = NULL; + gs_unref_hashtable GHashTable *other_config_old = NULL; + gs_unref_hashtable GHashTable *other_config_new = NULL; + + external_ids_old = + s_external_ids_old + ? nm_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_external_ids_old)) + : NULL; + external_ids_new = + s_external_ids_new + ? nm_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_external_ids_new)) + : NULL; + + other_config_old = + s_other_config_old + ? nm_strdict_clone(_nm_setting_ovs_other_config_get_data(s_other_config_old)) + : NULL; + other_config_new = + s_other_config_new + ? nm_strdict_clone(_nm_setting_ovs_other_config_get_data(s_other_config_new)) + : NULL; ovsdb_call_method(self, NULL, NULL, FALSE, - OVSDB_SET_EXTERNAL_IDS, - OVSDB_METHOD_PAYLOAD_SET_EXTERNAL_IDS(device_type, - ifname, - connection_uuid, - exid_old, - exid_new)); + OVSDB_SET_REAPPLY, + OVSDB_METHOD_PAYLOAD_SET_REAPPLY(device_type, + ifname, + connection_uuid, + external_ids_old, + external_ids_new, + other_config_old, + other_config_new)); } /*****************************************************************************/ diff --git a/src/core/devices/ovs/nm-ovsdb.h b/src/core/devices/ovs/nm-ovsdb.h index a50db45a..a022ff00 100644 --- a/src/core/devices/ovs/nm-ovsdb.h +++ b/src/core/devices/ovs/nm-ovsdb.h @@ -7,7 +7,7 @@ #define __NETWORKMANAGER_OVSDB_H__ #define NM_TYPE_OVSDB (nm_ovsdb_get_type()) -#define NM_OVSDB(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_OVSDB, NMOvsdb)) +#define NM_OVSDB(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_OVSDB, NMOvsdb)) #define NM_OVSDB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_OVSDB, NMOvsdbClass)) #define NM_IS_OVSDB(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_OVSDB)) #define NM_IS_OVSDB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_OVSDB)) @@ -47,14 +47,14 @@ void nm_ovsdb_set_interface_mtu(NMOvsdb *self, NMOvsdbCallback callback, gpointer user_data); -struct _NMSettingOvsExternalIDs; - -void nm_ovsdb_set_external_ids(NMOvsdb *self, - NMDeviceType device_type, - const char *ifname, - const char *connection_uuid, - struct _NMSettingOvsExternalIDs *s_exid_old, - struct _NMSettingOvsExternalIDs *s_exid_new); +void nm_ovsdb_set_reapply(NMOvsdb *self, + NMDeviceType device_type, + const char *ifname, + const char *connection_uuid, + NMSettingOvsExternalIDs *s_external_ids_old, + NMSettingOvsExternalIDs *s_external_ids_new, + NMSettingOvsOtherConfig *s_other_config_old, + NMSettingOvsOtherConfig *s_other_config_new); gboolean nm_ovsdb_is_ready(NMOvsdb *self); diff --git a/src/core/devices/team/nm-device-team.c b/src/core/devices/team/nm-device-team.c index 9eca008a..3e3fe52b 100644 --- a/src/core/devices/team/nm-device-team.c +++ b/src/core/devices/team/nm-device-team.c @@ -144,6 +144,7 @@ _update_port_config(NMDeviceTeam *self, const char *port_iface, const char *sani NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); int err; + _LOGT(LOGD_TEAM, "setting port config: %s", sanitized_config); err = teamdctl_port_config_update_raw(priv->tdc, port_iface, sanitized_config); if (err != 0) { _LOGE(LOGD_TEAM, "failed to update config for port %s (err=%d)", port_iface, err); @@ -154,23 +155,20 @@ _update_port_config(NMDeviceTeam *self, const char *port_iface, const char *sani } static gboolean -ensure_teamd_connection(NMDevice *device) +ensure_teamd_connection(NMDevice *device, GError **error) { - NMDeviceTeam *self = NM_DEVICE_TEAM(device); - NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); - gs_free_error GError *error = NULL; - const char *port_iface; - const char *port_config; - GHashTableIter iter; + NMDeviceTeam *self = NM_DEVICE_TEAM(device); + NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); + const char *port_iface; + const char *port_config; + GHashTableIter iter; if (priv->tdc) return TRUE; - priv->tdc = _tdc_connect_new(self, nm_device_get_iface(device), &error); - if (!priv->tdc) { - _LOGE(LOGD_TEAM, "failed to connect to teamd: %s", error->message); + priv->tdc = _tdc_connect_new(self, nm_device_get_iface(device), error); + if (!priv->tdc) return FALSE; - } g_hash_table_iter_init(&iter, priv->port_configs); while (g_hash_table_iter_next(&iter, (gpointer *) &port_iface, (gpointer *) &port_config)) @@ -229,10 +227,17 @@ update_connection(NMDevice *device, NMConnection *connection) NMSettingTeam *s_team = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_TEAM); NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); struct teamdctl *tdc = priv->tdc; + GError *error = NULL; /* Read the configuration only if not already set */ - if (!priv->config && ensure_teamd_connection(device)) - teamd_read_config(self); + if (!priv->config) { + if (ensure_teamd_connection(device, &error)) { + teamd_read_config(self); + } else { + _LOGD(LOGD_TEAM, "could not connect to teamd: %s", error->message); + g_clear_error(&error); + } + } /* Restore previous tdc state */ if (priv->tdc && !tdc) { @@ -258,9 +263,10 @@ master_update_slave_connection(NMDevice *device, gs_free_error GError *connect_error = NULL; int err = 0; struct teamdctl *tdc; - const char *team_port_config = NULL; - const char *iface = nm_device_get_iface(device); - const char *iface_slave = nm_device_get_iface(slave); + const char *team_port_config = NULL; + const char *iface = nm_device_get_iface(device); + const char *iface_slave = nm_device_get_iface(slave); + NMConnection *applied_connection = nm_device_get_applied_connection(device); tdc = _tdc_connect_new(self, iface, &connect_error); if (!tdc) { @@ -299,7 +305,7 @@ master_update_slave_connection(NMDevice *device, g_object_set(nm_connection_get_setting_connection(connection), NM_SETTING_CONNECTION_MASTER, - iface, + nm_connection_get_uuid(applied_connection), NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME, NULL); @@ -397,6 +403,7 @@ teamd_ready(NMDeviceTeam *self) NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); NMDevice *device = NM_DEVICE(self); gboolean success; + GError *error = NULL; if (priv->kill_in_progress) { /* If we are currently killing teamd, we are not @@ -410,7 +417,11 @@ teamd_ready(NMDeviceTeam *self) * immediately. But if we are, and grabbing it failed, fail the * device activation. */ - success = ensure_teamd_connection(device); + success = ensure_teamd_connection(device, &error); + if (!success) { + _LOGW(LOGD_TEAM, "could not connect to teamd: %s", error->message); + g_clear_error(&error); + } if (nm_device_get_state(device) != NM_DEVICE_STATE_PREPARE || priv->stage1_state != NM_DEVICE_STAGE_STATE_PENDING) @@ -759,18 +770,20 @@ teamd_start(NMDeviceTeam *self) static NMActStageReturn act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) { - NMDeviceTeam *self = NM_DEVICE_TEAM(device); - NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); - gs_free_error GError *error = NULL; - NMSettingTeam *s_team; - const char *cfg; + NMDeviceTeam *self = NM_DEVICE_TEAM(device); + NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); + GError *error = NULL; + NMSettingTeam *s_team; + const char *cfg; if (nm_device_sys_iface_state_is_external(device)) return NM_ACT_STAGE_RETURN_SUCCESS; if (nm_device_sys_iface_state_is_external_or_assume(device)) { - if (ensure_teamd_connection(device)) + if (ensure_teamd_connection(device, &error)) return NM_ACT_STAGE_RETURN_SUCCESS; + _LOGD(LOGD_TEAM, "could not connect to teamd: %s", error->message); + g_clear_error(&error); } s_team = nm_device_get_applied_setting(device, NM_TYPE_SETTING_TEAM); @@ -803,6 +816,7 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) _LOGW(LOGD_TEAM, "existing teamd config mismatch; failed to kill existing teamd: %s", error->message); + g_clear_error(&error); NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED); return NM_ACT_STAGE_RETURN_FAILURE; } diff --git a/src/core/devices/team/nm-device-team.h b/src/core/devices/team/nm-device-team.h index 6f5cff93..e2a54c6e 100644 --- a/src/core/devices/team/nm-device-team.h +++ b/src/core/devices/team/nm-device-team.h @@ -9,7 +9,8 @@ #include "devices/nm-device.h" #define NM_TYPE_DEVICE_TEAM (nm_device_team_get_type()) -#define NM_DEVICE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_TEAM, NMDeviceTeam)) +#define NM_DEVICE_TEAM(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_TEAM, NMDeviceTeam)) #define NM_DEVICE_TEAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_TEAM, NMDeviceTeamClass)) #define NM_IS_DEVICE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_TEAM)) diff --git a/src/core/devices/team/nm-team-factory.c b/src/core/devices/team/nm-team-factory.c index b446cbd0..68ea914e 100644 --- a/src/core/devices/team/nm-team-factory.c +++ b/src/core/devices/team/nm-team-factory.c @@ -17,7 +17,7 @@ #define NM_TYPE_TEAM_FACTORY (nm_team_factory_get_type()) #define NM_TEAM_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_TEAM_FACTORY, NMTeamFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_TEAM_FACTORY, NMTeamFactory)) #define NM_TEAM_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_TEAM_FACTORY, NMTeamFactoryClass)) #define NM_IS_TEAM_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_TEAM_FACTORY)) diff --git a/src/core/devices/tests/test-lldp.c b/src/core/devices/tests/test-lldp.c index 048410c1..4cd602f7 100644 --- a/src/core/devices/tests/test-lldp.c +++ b/src/core/devices/tests/test-lldp.c @@ -12,9 +12,8 @@ #include #include +#include "libnm-lldp/nm-lldp.h" #include "devices/nm-lldp-listener.h" -#include "libnm-systemd-core/nm-sd.h" - #include "platform/tests/test-common.h" #include "nm-test-utils-core.h" @@ -191,9 +190,9 @@ _test_recv_data0_check_do(GMainLoop *loop, NMLldpListener *listener, const TestR g_assert_cmpint(g_variant_n_children(neighbors), ==, 1); neighbor = get_lldp_neighbor(neighbors, - SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, + NM_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, "00:01:02:03:04:05", - SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME, + NM_LLDP_PORT_SUBTYPE_INTERFACE_NAME, "1/3"); g_assert(neighbor); g_assert_cmpint(g_variant_n_children(neighbor), ==, 1 + 4 + 4); @@ -547,9 +546,9 @@ _test_recv_data1_check(GMainLoop *loop, NMLldpListener *listener, TestRecvCallba g_assert_cmpint(g_variant_n_children(neighbors), ==, 1); neighbor = get_lldp_neighbor(neighbors, - SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, + NM_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, "00:01:30:F9:AD:A0", - SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME, + NM_LLDP_PORT_SUBTYPE_INTERFACE_NAME, "1/1"); g_assert(neighbor); g_assert_cmpint(g_variant_n_children(neighbor), ==, 1 + 4 + 16); @@ -874,7 +873,6 @@ test_recv(TestRecvFixture *fixture, gconstpointer user_data) TestRecvCallbackInfo info = {}; gsize i_frames; GError *error = NULL; - guint sd_id; if (fixture->ifindex == 0) { g_test_skip("Tun device not available"); @@ -884,8 +882,7 @@ test_recv(TestRecvFixture *fixture, gconstpointer user_data) listener = nm_lldp_listener_new(fixture->ifindex, lldp_neighbors_changed, &info, &error); nmtst_assert_success(listener, error); - loop = g_main_loop_new(NULL, FALSE); - sd_id = nm_sd_event_attach_default(); + loop = g_main_loop_new(NULL, FALSE); for (i_frames = 0; i_frames < data->frames_len; i_frames++) { const TestRecvFrame *f = data->frames[i_frames]; @@ -902,7 +899,6 @@ test_recv(TestRecvFixture *fixture, gconstpointer user_data) nm_clear_pointer(&listener, nm_lldp_listener_destroy); - nm_clear_g_source(&sd_id); nm_clear_pointer(&loop, g_main_loop_unref); } diff --git a/src/core/devices/wifi/nm-device-iwd-p2p.h b/src/core/devices/wifi/nm-device-iwd-p2p.h index 9ce353c0..98cf3762 100644 --- a/src/core/devices/wifi/nm-device-iwd-p2p.h +++ b/src/core/devices/wifi/nm-device-iwd-p2p.h @@ -11,7 +11,7 @@ #define NM_TYPE_DEVICE_IWD_P2P (nm_device_iwd_p2p_get_type()) #define NM_DEVICE_IWD_P2P(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_IWD_P2P, NMDeviceIwdP2P)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_IWD_P2P, NMDeviceIwdP2P)) #define NM_DEVICE_IWD_P2P_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_IWD_P2P, NMDeviceIwdP2PClass)) #define NM_IS_DEVICE_IWD_P2P(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_IWD_P2P)) diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c index ab37cbec..e03227cd 100644 --- a/src/core/devices/wifi/nm-device-iwd.c +++ b/src/core/devices/wifi/nm-device-iwd.c @@ -8,6 +8,7 @@ #include "nm-device-iwd.h" #include +#include #include "devices/nm-device-private.h" #include "devices/nm-device.h" @@ -32,6 +33,7 @@ #include "supplicant/nm-supplicant-types.h" #include "nm-auth-utils.h" #include "nm-manager.h" +#include "nm-l3-config-data.h" #define _NMLOG_DEVICE_TYPE NMDeviceIwd #include "devices/nm-device-logging.h" @@ -70,6 +72,7 @@ typedef struct { bool secrets_failed : 1; bool networks_requested : 1; bool networks_changed : 1; + bool assuming : 1; gint64 last_scan; uint32_t ap_id; guint32 rate; @@ -77,6 +80,16 @@ typedef struct { GDBusMethodInvocation *pending_agent_request; NMActiveConnection *assumed_ac; guint assumed_ac_timeout; + + union { + struct { + const NML3ConfigData *pending_l3cd_6; + const NML3ConfigData *pending_l3cd_4; + }; + const NML3ConfigData *pending_l3cd_x[2]; + }; + + NMIwdManager *manager; } NMDeviceIwdPrivate; struct _NMDeviceIwd { @@ -289,6 +302,7 @@ insert_ap_from_network(NMDeviceIwd *self, gint64 last_seen_msec, int16_t signal) { + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE(self); gs_unref_object GDBusProxy *network_proxy = NULL; nm_auto_ref_string NMRefString *bss_path = nm_ref_string_new(path); NMWifiAP *ap; @@ -299,7 +313,7 @@ insert_ap_from_network(NMDeviceIwd *self, } network_proxy = - nm_iwd_manager_get_dbus_interface(nm_iwd_manager_get(), path, NM_IWD_NETWORK_INTERFACE); + nm_iwd_manager_get_dbus_interface(priv->manager, path, NM_IWD_NETWORK_INTERFACE); ap = ap_from_network(self, network_proxy, bss_path, last_seen_msec, signal); if (!ap) @@ -525,6 +539,9 @@ cleanup_association_attempt(NMDeviceIwd *self, gboolean disconnect) if (disconnect && priv->dbus_station_proxy) send_disconnect(self); + + nm_clear_l3cd(&priv->pending_l3cd_6); + nm_clear_l3cd(&priv->pending_l3cd_4); } static void @@ -581,6 +598,10 @@ deactivate(NMDevice *device) if (!priv->dbus_obj) return; + /* Don't cause IWD to break the connection being assumed */ + if (priv->assuming) + return; + if (priv->dbus_station_proxy) { gs_unref_variant GVariant *value = g_dbus_proxy_get_cached_property(priv->dbus_station_proxy, "State"); @@ -673,7 +694,7 @@ deactivate_async(NMDevice *device, } static gboolean -is_connection_known_network(NMConnection *connection) +is_connection_known_network(NMIwdManager *manager, NMConnection *connection) { NMIwdNetworkSecurity security; gs_free char *ssid = NULL; @@ -681,17 +702,17 @@ is_connection_known_network(NMConnection *connection) if (!nm_wifi_connection_get_iwd_ssid_and_security(connection, &ssid, &security)) return FALSE; - return nm_iwd_manager_is_known_network(nm_iwd_manager_get(), ssid, security); + return nm_iwd_manager_is_known_network(manager, ssid, security); } static gboolean -is_ap_known_network(NMWifiAP *ap) +is_ap_known_network(NMIwdManager *manager, NMWifiAP *ap) { gs_unref_object GDBusProxy *network_proxy = NULL; gs_unref_variant GVariant *known_network = NULL; network_proxy = - nm_iwd_manager_get_dbus_interface(nm_iwd_manager_get(), + nm_iwd_manager_get_dbus_interface(manager, nm_ref_string_get_str(nm_wifi_ap_get_supplicant_path(ap)), NM_IWD_NETWORK_INTERFACE); if (!network_proxy) @@ -794,7 +815,8 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError * * thus are Known Networks. */ if (security == NM_IWD_NETWORK_SECURITY_8021X) { - if (!is_connection_known_network(connection)) { + if (!is_connection_known_network(priv->manager, connection) + && !nm_iwd_manager_is_recently_mirrored(priv->manager, ssid)) { nm_utils_error_set_literal(error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE, "802.1x connections must have IWD provisioning files"); @@ -927,7 +949,9 @@ check_connection_available(NMDevice *device, */ if (nm_wifi_connection_get_iwd_ssid_and_security(connection, NULL, &security) && security == NM_IWD_NETWORK_SECURITY_8021X) { - if (!is_ap_known_network(ap)) { + if (!is_ap_known_network(priv->manager, ap) + && !nm_iwd_manager_is_recently_mirrored(priv->manager, + nm_setting_wireless_get_ssid(s_wifi))) { nm_utils_error_set_literal( error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, @@ -2044,7 +2068,7 @@ assume_connection(NMDeviceIwd *self, NMWifiAP *ap) * becomes "managed" only when ACTIVATED but for IWD it's really * managed when IP_CONFIG starts. */ - sett_conn = nm_iwd_manager_get_ap_mirror_connection(nm_iwd_manager_get(), ap); + sett_conn = nm_iwd_manager_get_ap_mirror_connection(priv->manager, ap); if (!sett_conn) goto error; @@ -2217,7 +2241,8 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) * for a first-time connection to a hidden network. If a hidden network is * a Known Network it should still have been in the AP list. */ - if (!nm_setting_wireless_get_hidden(s_wireless) || is_connection_known_network(connection)) + if (!nm_setting_wireless_get_hidden(s_wireless) + || is_connection_known_network(priv->manager, connection)) return NM_ACT_STAGE_RETURN_FAILURE; add_new: @@ -2270,6 +2295,18 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) goto out_fail; } + /* With priv->iwd_autoconnect we have to let IWD handle retries for + * infrastructure networks. IWD will not necessarily retry the same + * network after a failure but it will likely go into an autoconnect + * mode and we don't want to try to override the logic. We don't need + * to reset the retry count so we set no timeout. + */ + if (priv->iwd_autoconnect) { + NMSettingsConnection *sett_conn = nm_act_request_get_settings_connection(req); + + nm_settings_connection_autoconnect_retries_set(sett_conn, 0); + } + /* With priv->iwd_autoconnect, if we're assuming a connection because * of a state change to "connecting", signal stage 2 is still running. * If "connected" or "roaming", we can go right to the IP_CONFIG state @@ -2310,7 +2347,9 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) * fail, for other combinations we will let the Connect call fail * or ask us for any missing secrets through the Agent. */ - if (nm_connection_get_setting_802_1x(connection) && !is_ap_known_network(ap)) { + if (nm_connection_get_setting_802_1x(connection) && !is_ap_known_network(priv->manager, ap) + && !nm_iwd_manager_is_recently_mirrored(priv->manager, + nm_setting_wireless_get_ssid(s_wireless))) { _LOGI(LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) access point '%s' has 802.1x security but is not configured " "in IWD.", @@ -2351,7 +2390,7 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) } network_proxy = nm_iwd_manager_get_dbus_interface( - nm_iwd_manager_get(), + priv->manager, nm_ref_string_get_str(nm_wifi_ap_get_supplicant_path(ap)), NM_IWD_NETWORK_INTERFACE); if (!network_proxy) { @@ -2421,6 +2460,38 @@ out_fail: return NM_ACT_STAGE_RETURN_FAILURE; } +static void +act_stage3_ip_config(NMDevice *device, int addr_family) +{ + NMDeviceIwd *self = NM_DEVICE_IWD(device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + + if (!nm_iwd_manager_get_netconfig_enabled(nm_iwd_manager_get())) { + NMDeviceClass *device_class = NM_DEVICE_CLASS(nm_device_iwd_parent_class); + + if (device_class->act_stage3_ip_config) + device_class->act_stage3_ip_config(device, addr_family); + + return; + } + + if (!priv->pending_l3cd_x[IS_IPv4]) + return; + + nm_device_devip_set_state(device, + addr_family, + NM_DEVICE_IP_STATE_READY, + priv->pending_l3cd_x[IS_IPv4]); + nm_clear_l3cd(&priv->pending_l3cd_x[IS_IPv4]); +} + +static gboolean +ready_for_ip_config(NMDevice *device, gboolean is_manual) +{ + return !nm_iwd_manager_get_netconfig_enabled(nm_iwd_manager_get()); +} + static guint32 get_configured_mtu(NMDevice *device, NMDeviceMtuSource *out_source, gboolean *out_force) { @@ -2719,12 +2790,20 @@ state_changed(NMDeviceIwd *self, const char *new_state) "IWD is connecting to the wrong AP, %s activation", switch_ap ? "replacing" : "aborting"); cleanup_association_attempt(self, !switch_ap); + + if (!switch_ap) { + nm_device_state_changed(device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + return; + } + + priv->assuming = TRUE; /* Don't send Station.Disconnect() */ nm_device_state_changed(device, - NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); - - if (switch_ap) - assume_connection(self, ap); + priv->assuming = FALSE; + assume_connection(self, ap); return; } @@ -3101,7 +3180,7 @@ nm_device_iwd_set_dbus_object(NMDeviceIwd *self, GDBusObject *object) goto error; } - adapter_proxy = nm_iwd_manager_get_dbus_interface(nm_iwd_manager_get(), + adapter_proxy = nm_iwd_manager_get_dbus_interface(priv->manager, g_variant_get_string(value, NULL), NM_IWD_WIPHY_INTERFACE); if (!adapter_proxy) { @@ -3278,7 +3357,7 @@ nm_device_iwd_agent_query(NMDeviceIwd *self, GDBusMethodInvocation *invocation) * it. IWD only queries us if it has not saved the PSK (e.g. by policy) * or a previous attempt has failed with current secrets so it wants a * fresh value. It doesn't know about agent-owned secrets so whenever - * possible and the PSK is saved and not asked from NM. However if this + * possible, the PSK is saved and not asked from NM. However if this * is a new connection it may include all of the needed settings already * so allow using these, too. Connection timestamp is set after * activation or after first activation failure (to 0). @@ -3368,6 +3447,341 @@ nm_device_iwd_network_add_remove(NMDeviceIwd *self, GDBusProxy *network, bool ad } } +static const NML3ConfigData * +nm_device_iwd_parse_netconfig(NMDeviceIwd *self, int addr_family, GVariantIter *config_iter) +{ + NMDevice *device = NM_DEVICE(self); + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + const char *key; + GVariant *value; + NMIPConfigSource source = NM_IP_CONFIG_SOURCE_UNKNOWN; + nm_auto_free_variant_iter GVariantIter *address_iter = NULL; + nm_auto_free_variant_iter GVariantIter *route_iter = NULL; + nm_auto_free_variant_iter GVariantIter *dns_iter = NULL; + nm_auto_free_variant_iter GVariantIter *domain_iter = NULL; + NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT; + const char *str_value; + GVariantIter *entry_iter; + uint32_t timestamp = nm_utils_get_monotonic_timestamp_sec(); + + while (g_variant_iter_next(config_iter, "{&sv}", &key, &value)) { + _nm_unused gs_unref_variant GVariant *value_free = value; + + if (nm_streq(key, "Method")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) + || source != NM_IP_CONFIG_SOURCE_UNKNOWN) + goto param_error; + + str_value = g_variant_get_string(value, NULL); + if (nm_streq(str_value, "static")) + source = NM_IP_CONFIG_SOURCE_USER; + else if (nm_streq(str_value, "auto")) + /* Use SOURCE_DHCP as shorthand for the various autoconfiguration protocols */ + source = NM_IP_CONFIG_SOURCE_DHCP; + else + _LOGW(LOGD_WIFI, "iwd_parse_netconfig: Uknown Method value \"%s\"", str_value); + } + + if (nm_streq(key, "Addresses")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}")) || address_iter) + goto param_error; + + g_variant_get(value, "aa{sv}", &address_iter); + } + + if (nm_streq(key, "Routes")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}")) || route_iter) + goto param_error; + + g_variant_get(value, "aa{sv}", &route_iter); + } + + if (nm_streq(key, "DomainNameServers")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING_ARRAY) || dns_iter) + goto param_error; + + g_variant_get(value, "as", &dns_iter); + } + + if (nm_streq(key, "DomainNames")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING_ARRAY) || domain_iter) + goto param_error; + + g_variant_get(value, "as", &domain_iter); + } + + if (nm_streq(key, "MDNS")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) + || mdns != NM_SETTING_CONNECTION_MDNS_DEFAULT) + goto param_error; + + str_value = g_variant_get_string(value, NULL); + if (nm_streq(str_value, "yes")) + mdns = NM_SETTING_CONNECTION_MDNS_YES; + else if (nm_streq(str_value, "no")) + mdns = NM_SETTING_CONNECTION_MDNS_NO; + else if (nm_streq(str_value, "resolve")) + mdns = NM_SETTING_CONNECTION_MDNS_RESOLVE; + else + _LOGW(LOGD_WIFI, "iwd_parse_netconfig: Uknown MDNS value \"%s\"", str_value); + } + } + + if (!address_iter || !route_iter) + goto param_error; + + l3cd = nm_l3_config_data_new(nm_device_get_multi_index(device), + nm_device_get_ip_ifindex(device), + source); + + while (g_variant_iter_next(address_iter, "a{sv}", &entry_iter)) { + _nm_unused nm_auto_free_variant_iter GVariantIter *entry_iter_free = entry_iter; + const char *addr_str = NULL; + NMIPAddr addr_bin; + uint8_t plen = 0; + const char *bcast_str = NULL; + NMIPAddr bcast_bin = {}; + uint32_t valid_lifetime = NM_PLATFORM_LIFETIME_PERMANENT; + uint32_t preferred_lifetime = NM_PLATFORM_LIFETIME_PERMANENT; + NMPlatformIPXAddress a; + + while (g_variant_iter_next(entry_iter, "{&sv}", &key, &value)) { + _nm_unused gs_unref_variant GVariant *value_free = value; + + if (nm_streq(key, "Address")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) || addr_str) + goto param_error; + + addr_str = g_variant_get_string(value, NULL); + if (inet_pton(addr_family, addr_str, &addr_bin) != 1) + goto param_error; + } + + if (nm_streq(key, "PrefixLength")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BYTE)) + goto param_error; + + plen = g_variant_get_byte(value); + } + + if (nm_streq(key, "Broadcast")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) || bcast_str) + goto param_error; + + bcast_str = g_variant_get_string(value, NULL); + if (inet_pton(addr_family, bcast_str, &bcast_bin) != 1) + goto param_error; + } + + if (nm_streq(key, "ValidLifetime")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32) + || valid_lifetime != NM_PLATFORM_LIFETIME_PERMANENT) + goto param_error; + + valid_lifetime = g_variant_get_uint32(value); + } + + if (nm_streq(key, "PreferredLifetime")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32) + || preferred_lifetime != NM_PLATFORM_LIFETIME_PERMANENT) + goto param_error; + + preferred_lifetime = g_variant_get_uint32(value); + } + } + + if (addr_str == NULL) + goto param_error; + + if (valid_lifetime != NM_PLATFORM_LIFETIME_PERMANENT + && preferred_lifetime == NM_PLATFORM_LIFETIME_PERMANENT) + preferred_lifetime = valid_lifetime; + + if (addr_family == AF_INET) { + a.a4 = (NMPlatformIP4Address){ + .address = addr_bin.addr4, + .peer_address = addr_bin.addr4, + .plen = plen, + .timestamp = (valid_lifetime != NM_PLATFORM_LIFETIME_PERMANENT) ? timestamp : 0, + .lifetime = valid_lifetime, + .preferred = preferred_lifetime, + .addr_source = source, + .use_ip4_broadcast_address = (bcast_str != NULL), + .broadcast_address = bcast_bin.addr4, + }; + } else { + a.a6 = (NMPlatformIP6Address){ + .address = addr_bin.addr6, + .plen = 128, + .timestamp = (valid_lifetime != NM_PLATFORM_LIFETIME_PERMANENT) ? timestamp : 0, + .lifetime = valid_lifetime, + .preferred = preferred_lifetime, + .addr_source = source, + }; + } + + nm_l3_config_data_add_address(l3cd, addr_family, NULL, &a.ax); + } + + while (g_variant_iter_next(route_iter, "a{sv}", &entry_iter)) { + _nm_unused nm_auto_free_variant_iter GVariantIter *entry_iter_free = entry_iter; + const char *dst_addr_str = NULL; + NMIPAddr dst_addr_bin = {}; + uint8_t dst_plen = 0; + const char *router_str = NULL; + NMIPAddr router_bin = {}; + const char *pref_src_str = NULL; + NMIPAddr pref_src_bin = {}; + _nm_unused uint32_t lifetime = NM_PLATFORM_LIFETIME_PERMANENT; + _nm_unused uint32_t priority = 0; + uint8_t preference = 0; + uint32_t mtu = 0; + NMPlatformIPXRoute r; + + while (g_variant_iter_next(entry_iter, "{&sv}", &key, &value)) { + _nm_unused gs_unref_variant GVariant *value_free = value; + + if (nm_streq(key, "Destination")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE("(sy)")) || dst_addr_str) + goto param_error; + + g_variant_get(value, "(&sy)", &dst_addr_str, &dst_plen); + if (inet_pton(addr_family, dst_addr_str, &dst_addr_bin) != 1) + goto param_error; + } + + if (nm_streq(key, "Router")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) || router_str) + goto param_error; + + router_str = g_variant_get_string(value, NULL); + if (inet_pton(addr_family, router_str, &router_bin) != 1) + goto param_error; + } + + if (nm_streq(key, "PreferredSource")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) || pref_src_str) + goto param_error; + + pref_src_str = g_variant_get_string(value, NULL); + if (inet_pton(addr_family, pref_src_str, &pref_src_bin) != 1) + goto param_error; + } + + if (nm_streq(key, "Lifetime")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32) + || lifetime != NM_PLATFORM_LIFETIME_PERMANENT) + goto param_error; + + lifetime = g_variant_get_uint32(value); + } + + if (nm_streq(key, "Priority")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32)) + goto param_error; + + priority = g_variant_get_uint32(value); + } + + if (nm_streq(key, "Preference")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BYTE)) + goto param_error; + + preference = g_variant_get_byte(value); + } + + if (nm_streq(key, "MTU")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32)) + goto param_error; + + mtu = g_variant_get_uint32(value); + } + } + + if (addr_family == AF_INET) { + r.r4 = (NMPlatformIP4Route){ + .network = dst_addr_str ? dst_addr_bin.addr4 : 0, + .plen = dst_addr_str ? dst_plen : 0, + .gateway = router_str ? router_bin.addr4 : 0, + .pref_src = pref_src_str ? pref_src_bin.addr4 : 0, + .scope_inv = + nm_platform_route_scope_inv(router_str ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK), + }; + } else { + r.r6 = (NMPlatformIP6Route){ + .network = dst_addr_str ? dst_addr_bin.addr6 : nm_ip_addr_zero.addr6, + .plen = dst_addr_str ? dst_plen : 0, + .gateway = router_str ? router_bin.addr6 : nm_ip_addr_zero.addr6, + .pref_src = pref_src_str ? pref_src_bin.addr6 : nm_ip_addr_zero.addr6, + .rt_pref = preference, + .r_rtm_flags = RTNH_F_ONLINK, + }; + } + + r.rx.metric_any = TRUE; + r.rx.mtu = mtu; + r.rx.rt_source = source; + r.rx.type_coerced = nm_platform_route_type_coerce(RTN_UNICAST); + r.rx.table_any = TRUE; + + /* TODO: set the lifetime */ + nm_l3_config_data_add_route(l3cd, addr_family, NULL, &r.rx); + } + + if (dns_iter) { + while (g_variant_iter_next(dns_iter, "&s", &str_value)) { + NMIPAddr dns_bin; + + if (inet_pton(addr_family, str_value, &dns_bin) != 1) + goto param_error; + + nm_l3_config_data_add_nameserver_detail(l3cd, addr_family, &dns_bin, NULL); + nm_l3_config_data_set_dns_priority(l3cd, addr_family, NM_DNS_PRIORITY_DEFAULT_NORMAL); + } + } + + if (domain_iter) { + while (g_variant_iter_next(domain_iter, "&s", &str_value)) + nm_l3_config_data_add_search(l3cd, addr_family, str_value); + } + + if (mdns != NM_SETTING_CONNECTION_MDNS_DEFAULT) + nm_l3_config_data_set_mdns(l3cd, mdns); + + return nm_l3_config_data_ref_and_seal(l3cd); + +param_error: + return NULL; +} + +bool +nm_device_iwd_set_netconfig(NMDeviceIwd *self, int addr_family, GVariantIter *config_iter) +{ + NMDevice *device = NM_DEVICE(self); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE(self); + const NML3ConfigData *l3cd; + NMDeviceState state = nm_device_get_state(device); + + if (state < NM_DEVICE_STATE_CONFIG || state > NM_DEVICE_STATE_ACTIVATED) + return FALSE; + + l3cd = nm_device_iwd_parse_netconfig(self, addr_family, config_iter); + if (!l3cd) { + _LOGE(LOGD_WIFI, "Malformed netconfig DBus structure"); + return FALSE; + } + + if (state == NM_DEVICE_STATE_CONFIG) { + nm_l3_config_data_unref(priv->pending_l3cd_x[NM_IS_IPv4(addr_family)]); + priv->pending_l3cd_x[NM_IS_IPv4(addr_family)] = l3cd; + } else { + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, l3cd); + nm_l3_config_data_unref(l3cd); + } + + return TRUE; +} + static void autoconnect_changed(NMDevice *device, GParamSpec *pspec, NMDeviceIwd *self) { @@ -3411,7 +3825,7 @@ nm_device_iwd_init(NMDeviceIwd *self) g_signal_connect(self, "notify::" NM_DEVICE_AUTOCONNECT, G_CALLBACK(autoconnect_changed), self); /* Make sure the manager is running */ - (void) nm_iwd_manager_get(); + priv->manager = g_object_ref(nm_iwd_manager_get()); } NMDevice * @@ -3443,6 +3857,8 @@ dispose(GObject *object) G_OBJECT_CLASS(nm_device_iwd_parent_class)->dispose(object); nm_assert(c_list_is_empty(&priv->aps_lst_head)); + + g_clear_object(&priv->manager); } static void @@ -3472,12 +3888,14 @@ nm_device_iwd_class_init(NMDeviceIwdClass *klass) device_class->set_enabled = set_enabled; device_class->get_type_description = get_type_description; - device_class->act_stage1_prepare = act_stage1_prepare; - device_class->act_stage2_config = act_stage2_config; - device_class->get_configured_mtu = get_configured_mtu; - device_class->deactivate = deactivate; - device_class->deactivate_async = deactivate_async; - device_class->can_reapply_change = can_reapply_change; + device_class->act_stage1_prepare = act_stage1_prepare; + device_class->act_stage2_config = act_stage2_config; + device_class->act_stage3_ip_config = act_stage3_ip_config; + device_class->ready_for_ip_config = ready_for_ip_config; + device_class->get_configured_mtu = get_configured_mtu; + device_class->deactivate = deactivate; + device_class->deactivate_async = deactivate_async; + device_class->can_reapply_change = can_reapply_change; /* Stage 1 needed only for the set_current_ap() call. Stage 2 is * needed if we're assuming a connection still in the "connecting" diff --git a/src/core/devices/wifi/nm-device-iwd.h b/src/core/devices/wifi/nm-device-iwd.h index bb136bf6..8449f7f6 100644 --- a/src/core/devices/wifi/nm-device-iwd.h +++ b/src/core/devices/wifi/nm-device-iwd.h @@ -11,7 +11,7 @@ #include "nm-device-wifi.h" #define NM_TYPE_DEVICE_IWD (nm_device_iwd_get_type()) -#define NM_DEVICE_IWD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_IWD, NMDeviceIwd)) +#define NM_DEVICE_IWD(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_IWD, NMDeviceIwd)) #define NM_DEVICE_IWD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_IWD, NMDeviceIwdClass)) #define NM_IS_DEVICE_IWD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_IWD)) @@ -46,4 +46,6 @@ void _nm_device_iwd_request_scan(NMDeviceIwd *self, void nm_device_iwd_network_add_remove(NMDeviceIwd *device, GDBusProxy *network, bool add); +bool nm_device_iwd_set_netconfig(NMDeviceIwd *device, int addr_family, GVariantIter *config_iter); + #endif /* __NETWORKMANAGER_DEVICE_IWD_H__ */ diff --git a/src/core/devices/wifi/nm-device-olpc-mesh.h b/src/core/devices/wifi/nm-device-olpc-mesh.h index 79b7fd5d..af3abbc0 100644 --- a/src/core/devices/wifi/nm-device-olpc-mesh.h +++ b/src/core/devices/wifi/nm-device-olpc-mesh.h @@ -15,7 +15,7 @@ #define NM_TYPE_DEVICE_OLPC_MESH (nm_device_olpc_mesh_get_type()) #define NM_DEVICE_OLPC_MESH(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMesh)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMesh)) #define NM_DEVICE_OLPC_MESH_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass)) #define NM_IS_DEVICE_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_OLPC_MESH)) diff --git a/src/core/devices/wifi/nm-device-wifi-p2p.c b/src/core/devices/wifi/nm-device-wifi-p2p.c index 4576af95..6c0a4d5e 100644 --- a/src/core/devices/wifi/nm-device-wifi-p2p.c +++ b/src/core/devices/wifi/nm-device-wifi-p2p.c @@ -22,7 +22,6 @@ #include "nm-act-request.h" #include "nm-l3-config-data.h" #include "nm-manager.h" -#include "nm-manager.h" #include "nm-setting-wifi-p2p.h" #include "nm-utils.h" #include "nm-wifi-p2p-peer.h" diff --git a/src/core/devices/wifi/nm-device-wifi-p2p.h b/src/core/devices/wifi/nm-device-wifi-p2p.h index d1aadd8e..08780464 100644 --- a/src/core/devices/wifi/nm-device-wifi-p2p.h +++ b/src/core/devices/wifi/nm-device-wifi-p2p.h @@ -11,7 +11,7 @@ #define NM_TYPE_DEVICE_WIFI_P2P (nm_device_wifi_p2p_get_type()) #define NM_DEVICE_WIFI_P2P(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_WIFI_P2P, NMDeviceWifiP2P)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_WIFI_P2P, NMDeviceWifiP2P)) #define NM_DEVICE_WIFI_P2P_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_WIFI_P2P, NMDeviceWifiP2PClass)) #define NM_IS_DEVICE_WIFI_P2P(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_WIFI_P2P)) diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c index 8af974d0..171743d0 100644 --- a/src/core/devices/wifi/nm-device-wifi.c +++ b/src/core/devices/wifi/nm-device-wifi.c @@ -99,13 +99,13 @@ typedef struct { NMSupplMgrCreateIfaceHandle *sup_create_handle; NMSupplicantInterface *sup_iface; + GSource *scan_kickoff_timeout_source; + gint64 scan_last_complete_msec; gint64 scan_periodic_next_msec; gint64 scan_last_request_started_at_msec; - guint scan_kickoff_timeout_id; - guint ap_dump_id; guint periodic_update_id; @@ -508,7 +508,10 @@ _scan_notify_allowed(NMDeviceWifi *self, NMTernary do_kickoff) /* something prohibits scanning. */ } else if (NM_IN_SET(priv->mode, _NM_802_11_MODE_ADHOC, _NM_802_11_MODE_AP)) { /* Don't scan when a an AP or Ad-Hoc connection is active as it will - * disrupt connected clients or peers. */ + * disrupt connected clients or peers. + * + * Explicit scans are allowed however. */ + explicit_allowed = TRUE; } else if (NM_IN_SET(state, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_FAILED)) { /* Can always scan when disconnected */ explicit_allowed = TRUE; @@ -659,7 +662,7 @@ supplicant_interface_release(NMDeviceWifi *self) NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE); - nm_clear_g_source(&priv->scan_kickoff_timeout_id); + nm_clear_g_source_inst(&priv->scan_kickoff_timeout_source); nm_clear_g_source_inst(&priv->scan_request_delay_source); nm_clear_g_cancellable(&priv->scan_request_cancellable); @@ -1718,9 +1721,9 @@ _scan_kickoff_timeout_cb(gpointer user_data) NMDeviceWifi *self = user_data; NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(self); - priv->scan_kickoff_timeout_id = 0; + nm_clear_g_source_inst(&priv->scan_kickoff_timeout_source); _scan_kickoff(self); - return G_SOURCE_REMOVE; + return G_SOURCE_CONTINUE; } static void @@ -1763,13 +1766,13 @@ _scan_kickoff(NMDeviceWifi *self) / 1000), (int) ((priv->scan_last_request_started_at_msec + ratelimit_duration_msec - now_msec) % 1000), - !priv->scan_kickoff_timeout_id ? ", schedule timeout" : ""); - if (!priv->scan_kickoff_timeout_id + !priv->scan_kickoff_timeout_source ? ", schedule timeout" : ""); + if (!priv->scan_kickoff_timeout_source && (priv->scan_explicit_allowed || priv->scan_periodic_allowed)) { - priv->scan_kickoff_timeout_id = g_timeout_add(priv->scan_last_request_started_at_msec - + ratelimit_duration_msec - now_msec, - _scan_kickoff_timeout_cb, - self); + priv->scan_kickoff_timeout_source = nm_g_timeout_add_source( + priv->scan_last_request_started_at_msec + ratelimit_duration_msec - now_msec, + _scan_kickoff_timeout_cb, + self); } return; } @@ -1786,14 +1789,16 @@ _scan_kickoff(NMDeviceWifi *self) _LOGT_scan("kickoff: don't scan (rate limited for another %d.%03d sec after previous scan)", timeout_msec / 1000, timeout_msec % 1000); - nm_clear_g_source(&priv->scan_kickoff_timeout_id); - priv->scan_kickoff_timeout_id = g_timeout_add(timeout_msec, _scan_kickoff_timeout_cb, self); + nm_clear_g_source_inst(&priv->scan_kickoff_timeout_source); + priv->scan_kickoff_timeout_source = + nm_g_timeout_add_source(timeout_msec, _scan_kickoff_timeout_cb, self); return; } if (priv->scan_explicit_requested) { if (!priv->scan_explicit_allowed) { - _LOGT_scan("kickoff: don't scan (explicit scan requested but not allowed)"); + _LOGT_scan( + "kickoff: don't scan (explicit scan requested but not allowed at the moment)"); return; } priv->scan_explicit_requested = FALSE; @@ -1803,7 +1808,7 @@ _scan_kickoff(NMDeviceWifi *self) _LOGT_scan("kickoff: don't scan (periodic scan currently not allowed)"); priv->scan_periodic_next_msec = 0; priv->scan_periodic_interval_sec = 0; - nm_clear_g_source(&priv->scan_kickoff_timeout_id); + nm_clear_g_source_inst(&priv->scan_kickoff_timeout_source); return; } @@ -1813,12 +1818,12 @@ _scan_kickoff(NMDeviceWifi *self) _LOGT_scan("kickoff: don't scan (periodic scan waiting for another %d.%03d sec%s)", (int) ((priv->scan_periodic_next_msec - now_msec) / 1000), (int) ((priv->scan_periodic_next_msec - now_msec) % 1000), - !priv->scan_kickoff_timeout_id ? ", schedule timeout" : ""); - if (!priv->scan_kickoff_timeout_id) { - priv->scan_kickoff_timeout_id = - g_timeout_add_seconds((priv->scan_periodic_next_msec - now_msec + 999) / 1000, - _scan_kickoff_timeout_cb, - self); + !priv->scan_kickoff_timeout_source ? ", schedule timeout" : ""); + if (!priv->scan_kickoff_timeout_source) { + priv->scan_kickoff_timeout_source = nm_g_timeout_add_seconds_source( + (priv->scan_periodic_next_msec - now_msec + 999) / 1000, + _scan_kickoff_timeout_cb, + self); } return; } diff --git a/src/core/devices/wifi/nm-device-wifi.h b/src/core/devices/wifi/nm-device-wifi.h index 3a85192e..17893a1b 100644 --- a/src/core/devices/wifi/nm-device-wifi.h +++ b/src/core/devices/wifi/nm-device-wifi.h @@ -10,7 +10,8 @@ #include "devices/nm-device.h" #define NM_TYPE_DEVICE_WIFI (nm_device_wifi_get_type()) -#define NM_DEVICE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_WIFI, NMDeviceWifi)) +#define NM_DEVICE_WIFI(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_WIFI, NMDeviceWifi)) #define NM_DEVICE_WIFI_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass)) #define NM_IS_DEVICE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_WIFI)) diff --git a/src/core/devices/wifi/nm-iwd-manager.c b/src/core/devices/wifi/nm-iwd-manager.c index 2e0d51e5..0d4daf21 100644 --- a/src/core/devices/wifi/nm-iwd-manager.c +++ b/src/core/devices/wifi/nm-iwd-manager.c @@ -46,6 +46,11 @@ typedef struct { const KnownNetworkId *id; } KnownNetworkData; +typedef struct { + GBytes *ssid; + gint64 timestamp; +} RecentlyMirroredData; + typedef struct { NMManager *manager; NMSettings *settings; @@ -53,7 +58,7 @@ typedef struct { gboolean running; GDBusObjectManager *object_manager; guint agent_id; - char *agent_path; + guint netconfig_agent_id; GHashTable *known_networks; NMDeviceIwd *last_agent_call_device; char *last_state_dir; @@ -62,6 +67,7 @@ typedef struct { GHashTable *p2p_devices; NMIwdWfdInfo wfd_info; guint wfd_use_count; + GSList *recently_mirrored; } NMIwdManagerPrivate; struct _NMIwdManager { @@ -271,6 +277,77 @@ return_error: "Secrets not available for this connection"); } +static void +netconfig_agent_dbus_method_cb(GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + NMIwdManager *self = user_data; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self); + gs_free char *name_owner = NULL; + const char *device_path; + gs_unref_object GDBusInterface *device_obj = NULL; + NMDevice *device; + nm_auto_free_variant_iter GVariantIter *config_iter = NULL; + const char *ifname; + int addr_family; + + /* Be paranoid and check the sender address */ + name_owner = g_dbus_object_manager_client_get_name_owner( + G_DBUS_OBJECT_MANAGER_CLIENT(priv->object_manager)); + if (!nm_streq0(name_owner, sender)) + goto return_error; + + if (nm_streq(method_name, "ConfigureIPv4")) + addr_family = AF_INET; + else if (nm_streq(method_name, "ConfigureIPv6")) + addr_family = AF_INET6; + else + goto return_error; + + g_variant_get(parameters, "(&oa{sv})", &device_path, &config_iter); + + device_obj = g_dbus_object_manager_get_interface(priv->object_manager, + device_path, + NM_IWD_DEVICE_INTERFACE); + if (!device_obj) { + _LOGE("netconfig-agent-request: unable to find the device object"); + goto return_error; + } + + ifname = get_property_string_or_null(G_DBUS_PROXY(device_obj), "Name"); + if (!ifname) { + _LOGD("Name not cached for device at %s", device_path); + goto return_error; + } + + device = nm_manager_get_device(priv->manager, ifname, NM_DEVICE_TYPE_WIFI); + if (!device || !NM_IS_DEVICE_IWD(device)) { + _LOGD("NM device %s is not an IWD-managed device", ifname); + goto return_error; + } + + if (nm_device_iwd_set_netconfig(NM_DEVICE_IWD(device), addr_family, config_iter)) { + g_dbus_method_invocation_return_value(invocation, g_variant_new("()")); + return; + } + + _LOGD("netconfig-agent-request: device %s did not handle the IWD Netconfig Agent request", + ifname); + +return_error: + /* IWD doesn't look at the specific error */ + g_dbus_method_invocation_return_error_literal(invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "Couldn't set netconfig data"); +} + static const GDBusInterfaceInfo iwd_agent_iface_info = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT( "net.connman.iwd.Agent", .methods = NM_DEFINE_GDBUS_METHOD_INFOS( @@ -296,36 +373,55 @@ static const GDBusInterfaceInfo iwd_agent_iface_info = NM_DEFINE_GDBUS_INTERFACE .in_args = NM_DEFINE_GDBUS_ARG_INFOS( NM_DEFINE_GDBUS_ARG_INFO("reason", "s"), ), ), ), ); +static const GDBusInterfaceInfo iwd_netconfig_agent_iface_info = + NM_DEFINE_GDBUS_INTERFACE_INFO_INIT( + "net.connman.iwd.NetworkConfigurationAgent", + .methods = NM_DEFINE_GDBUS_METHOD_INFOS( + NM_DEFINE_GDBUS_METHOD_INFO("ConfigureIPv4", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS( + NM_DEFINE_GDBUS_ARG_INFO("device", "o"), + NM_DEFINE_GDBUS_ARG_INFO("config", "a{sv}"), ), ), + NM_DEFINE_GDBUS_METHOD_INFO("ConfigureIPv6", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS( + NM_DEFINE_GDBUS_ARG_INFO("device", "o"), + NM_DEFINE_GDBUS_ARG_INFO("config", "a{sv}"), ), ), ), ); + static guint -iwd_agent_export(GDBusConnection *connection, gpointer user_data, char **agent_path, GError **error) +iwd_agent_export(GDBusConnection *connection, gpointer user_data, GError **error) { static const GDBusInterfaceVTable vtable = { .method_call = agent_dbus_method_cb, }; - char path[50]; - unsigned int rnd; - guint id; - nm_random_get_bytes(&rnd, sizeof(rnd)); - - nm_sprintf_buf(path, "/agent/%u", rnd); + return g_dbus_connection_register_object( + connection, + NM_IWD_AGENT_PATH, + NM_UNCONST_PTR(GDBusInterfaceInfo, &iwd_agent_iface_info), + &vtable, + user_data, + NULL, + error); +} - id = - g_dbus_connection_register_object(connection, - path, - NM_UNCONST_PTR(GDBusInterfaceInfo, &iwd_agent_iface_info), - &vtable, - user_data, - NULL, - error); +static guint +iwd_netconfig_agent_export(GDBusConnection *connection, gpointer user_data, GError **error) +{ + static const GDBusInterfaceVTable vtable = { + .method_call = netconfig_agent_dbus_method_cb, + }; - if (id) - *agent_path = g_strdup(path); - return id; + return g_dbus_connection_register_object( + connection, + NM_IWD_AGENT_PATH, + NM_UNCONST_PTR(GDBusInterfaceInfo, &iwd_netconfig_agent_iface_info), + &vtable, + user_data, + NULL, + error); } static void -register_agent(NMIwdManager *self) +register_agent(NMIwdManager *self, const char *method) { NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self); GDBusInterface *agent_manager; @@ -334,14 +430,14 @@ register_agent(NMIwdManager *self) "/net/connman/iwd", /* IWD 1.0+ */ NM_IWD_AGENT_MANAGER_INTERFACE); if (!agent_manager) { - _LOGE("unable to register the IWD Agent: PSK/8021x Wi-Fi networks may not work"); + _LOGE("unable to register the IWD Agent"); return; } /* Register our agent */ g_dbus_proxy_call(G_DBUS_PROXY(agent_manager), - "RegisterAgent", - g_variant_new("(o)", priv->agent_path), + method, + g_variant_new("(o)", NM_IWD_AGENT_PATH), G_DBUS_CALL_FLAGS_NONE, -1, NULL, @@ -353,6 +449,70 @@ register_agent(NMIwdManager *self) /*****************************************************************************/ +static void +recently_mirrored_data_free(void *data) +{ + RecentlyMirroredData *rmd = data; + + g_bytes_unref(rmd->ssid); + g_free(rmd); +} + +/* When we mirror an 802.1x connection to an IWD config file, and there's an + * AP in range with matching SSID, that connection should become available + * for activation. In IWD terms when an 802.1x network becomes a Known + * Network, it can be connected to using the .Connect D-Bus method. + * + * However there's a delay between writing the IWD config file and receiving + * the InterfaceAdded event for the Known Network so we don't immediately + * find out that the network can now be used. If an NM client creates a + * new connection for an 802.1x AP and tries to activate it immediately, + * NMDeviceIWD will not allow it to because it doesn't know the network is + * known yet. To work around this, we save the SSIDs of 802.1x connections + * we recently mirrored to IWD config files, for 2 seconds, and we treat + * them as Known Networks in that period since in theory activations should + * succeed. + */ +bool +nm_iwd_manager_is_recently_mirrored(NMIwdManager *self, const GBytes *ssid) +{ + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self); + gint64 now = nm_utils_get_monotonic_timestamp_nsec(); + GSList *iter; + RecentlyMirroredData *rmd; + + /* Drop entries older than 2 seconds */ + while (priv->recently_mirrored) { + rmd = priv->recently_mirrored->data; + if (now < rmd->timestamp + 2000000000) + break; + + priv->recently_mirrored = g_slist_remove(priv->recently_mirrored, rmd); + recently_mirrored_data_free(rmd); + } + + for (iter = priv->recently_mirrored; iter; iter = iter->next) { + rmd = iter->data; + if (g_bytes_equal(ssid, rmd->ssid)) + return TRUE; + } + + return FALSE; +} + +static void +save_mirrored(NMIwdManager *self, GBytes *ssid) +{ + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self); + RecentlyMirroredData *rmd = g_malloc(sizeof(RecentlyMirroredData)); + + rmd->ssid = g_bytes_ref(ssid); + rmd->timestamp = nm_utils_get_monotonic_timestamp_nsec(); + priv->recently_mirrored = g_slist_append(priv->recently_mirrored, rmd); +} + +/*****************************************************************************/ + static KnownNetworkId * known_network_id_new(const char *name, NMIwdNetworkSecurity security) { @@ -721,6 +881,9 @@ sett_conn_changed(NMSettingsConnection *sett_conn, "iwd: changed Wi-Fi connection %s mirrored as IWD profile %s", nm_settings_connection_get_id(sett_conn), full_path); + + if (security == NM_IWD_NETWORK_SECURITY_8021X) + save_mirrored(nm_iwd_manager_get(), ssid); } /* Look up an existing NMSettingsConnection for a network that has been @@ -1283,6 +1446,7 @@ connection_added(NMSettings *settings, NMSettingsConnection *sett_conn, gpointer gs_free_error GError *error = NULL; nm_auto_unref_keyfile GKeyFile *iwd_config = NULL; NMSettingsConnectionIntFlags flags; + NMIwdNetworkSecurity security; if (!nm_streq(nm_settings_connection_get_connection_type(sett_conn), "802-11-wireless")) return; @@ -1338,6 +1502,12 @@ connection_added(NMSettings *settings, NMSettingsConnection *sett_conn, gpointer _LOGD("New Wi-Fi connection %s mirrored as IWD profile %s", nm_settings_connection_get_id(sett_conn), full_path); + + if (nm_wifi_connection_get_iwd_ssid_and_security(conn, NULL, &security) + && security == NM_IWD_NETWORK_SECURITY_8021X) { + NMSettingWireless *s_wifi = nm_connection_get_setting_wireless(conn); + save_mirrored(nm_iwd_manager_get(), nm_setting_wireless_get_ssid(s_wifi)); + } } static gboolean @@ -1355,26 +1525,33 @@ _om_has_name_owner(GDBusObjectManager *object_manager) static void release_object_manager(NMIwdManager *self) { - NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self); + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self); + GDBusConnection *agent_connection; + GDBusObjectManagerClient *omc; if (!priv->object_manager) return; g_signal_handlers_disconnect_by_data(priv->object_manager, self); - if (priv->agent_id) { - GDBusConnection *agent_connection; - GDBusObjectManagerClient *omc = G_DBUS_OBJECT_MANAGER_CLIENT(priv->object_manager); - - agent_connection = g_dbus_object_manager_client_get_connection(omc); + omc = G_DBUS_OBJECT_MANAGER_CLIENT(priv->object_manager); + agent_connection = g_dbus_object_manager_client_get_connection(omc); - /* We're is called when we're shutting down (i.e. our DBus connection - * is being closed, and IWD will detect this) or IWD was stopped so - * in either case calling UnregisterAgent will not do anything. - */ + /* We're called when we're shutting down (i.e. our DBus connection + * is being closed, and IWD will detect this) or IWD was stopped so + * in either case calling UnregisterAgent will not do anything. + * Just unregister the agent interfaces. The agents are on the same + * object (same path) but it seems g_dbus_connection_unregister_object() + * should be called for each interface on the object separately. + */ + if (priv->agent_id) { g_dbus_connection_unregister_object(agent_connection, priv->agent_id); priv->agent_id = 0; - nm_clear_g_free(&priv->agent_path); + } + + if (priv->netconfig_agent_id) { + g_dbus_connection_unregister_object(agent_connection, priv->netconfig_agent_id); + priv->netconfig_agent_id = 0; } g_clear_object(&priv->object_manager); @@ -1589,6 +1766,10 @@ next: } g_variant_iter_free(properties_iter); + + /* Register the netconfig agent only once we know netconfig is enabled */ + if (nm_iwd_manager_get_netconfig_enabled(self) && priv->netconfig_agent_id) + register_agent(self, "RegisterNetworkConfigurationAgent"); } static void @@ -1620,13 +1801,19 @@ got_object_manager(GObject *object, GAsyncResult *result, gpointer user_data) connection = g_dbus_object_manager_client_get_connection(G_DBUS_OBJECT_MANAGER_CLIENT(object_manager)); - priv->agent_id = iwd_agent_export(connection, self, &priv->agent_path, &error); + priv->agent_id = iwd_agent_export(connection, self, &error); if (!priv->agent_id) { _LOGE("failed to export the IWD Agent: PSK/8021x Wi-Fi networks may not work: %s", error->message); g_clear_error(&error); } + priv->netconfig_agent_id = iwd_netconfig_agent_export(connection, self, &error); + if (!priv->netconfig_agent_id) { + _LOGE("failed to export the IWD Netconfig Agent: %s", error->message); + g_clear_error(&error); + } + if (_om_has_name_owner(object_manager)) { GList *objects, *iter; gs_unref_object GDBusInterface *daemon = NULL; @@ -1654,7 +1841,7 @@ got_object_manager(GObject *object, GAsyncResult *result, gpointer user_data) g_list_free_full(objects, g_object_unref); if (priv->agent_id) - register_agent(self); + register_agent(self, "RegisterAgent"); priv->netconfig_enabled = false; /* Assume false until GetInfo() results come in */ @@ -1952,6 +2139,8 @@ dispose(GObject *object) g_hash_table_unref(nm_steal_pointer(&priv->p2p_devices)); + g_slist_free_full(nm_steal_pointer(&priv->recently_mirrored), recently_mirrored_data_free); + G_OBJECT_CLASS(nm_iwd_manager_parent_class)->dispose(object); } diff --git a/src/core/devices/wifi/nm-iwd-manager.h b/src/core/devices/wifi/nm-iwd-manager.h index 02cd6bba..80123c9a 100644 --- a/src/core/devices/wifi/nm-iwd-manager.h +++ b/src/core/devices/wifi/nm-iwd-manager.h @@ -30,8 +30,11 @@ #define NM_IWD_P2P_SERVICE_MANAGER_INTERFACE "net.connman.iwd.p2p.ServiceManager" #define NM_IWD_P2P_WFD_INTERFACE "net.connman.iwd.p2p.Display" +#define NM_IWD_AGENT_PATH NM_DBUS_PATH "/iwd/agent" + #define NM_TYPE_IWD_MANAGER (nm_iwd_manager_get_type()) -#define NM_IWD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IWD_MANAGER, NMIwdManager)) +#define NM_IWD_MANAGER(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IWD_MANAGER, NMIwdManager)) #define NM_IWD_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_IWD_MANAGER, NMIwdManagerClass)) #define NM_IS_IWD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_IWD_MANAGER)) @@ -63,4 +66,6 @@ gboolean nm_iwd_manager_check_wfd_info_compatible(NMIwdManager *self, const NMIw gboolean nm_iwd_manager_register_wfd(NMIwdManager *self, const NMIwdWfdInfo *wfd_info); void nm_iwd_manager_unregister_wfd(NMIwdManager *self); +bool nm_iwd_manager_is_recently_mirrored(NMIwdManager *self, const GBytes *ssid); + #endif /* __NETWORKMANAGER_IWD_MANAGER_H__ */ diff --git a/src/core/devices/wifi/nm-wifi-ap.h b/src/core/devices/wifi/nm-wifi-ap.h index c0422900..93dc8edf 100644 --- a/src/core/devices/wifi/nm-wifi-ap.h +++ b/src/core/devices/wifi/nm-wifi-ap.h @@ -13,7 +13,7 @@ #include "libnm-base/nm-base.h" #define NM_TYPE_WIFI_AP (nm_wifi_ap_get_type()) -#define NM_WIFI_AP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_AP, NMWifiAP)) +#define NM_WIFI_AP(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_AP, NMWifiAP)) #define NM_WIFI_AP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_AP, NMWifiAPClass)) #define NM_IS_WIFI_AP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WIFI_AP)) #define NM_IS_WIFI_AP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_WIFI_AP)) diff --git a/src/core/devices/wifi/nm-wifi-factory.c b/src/core/devices/wifi/nm-wifi-factory.c index 5e354c6b..19880a90 100644 --- a/src/core/devices/wifi/nm-wifi-factory.c +++ b/src/core/devices/wifi/nm-wifi-factory.c @@ -24,7 +24,7 @@ #define NM_TYPE_WIFI_FACTORY (nm_wifi_factory_get_type()) #define NM_WIFI_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_FACTORY, NMWifiFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_FACTORY, NMWifiFactory)) #define NM_WIFI_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_FACTORY, NMWifiFactoryClass)) #define NM_IS_WIFI_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WIFI_FACTORY)) diff --git a/src/core/devices/wifi/nm-wifi-p2p-peer.h b/src/core/devices/wifi/nm-wifi-p2p-peer.h index 5124d1de..b0abeb37 100644 --- a/src/core/devices/wifi/nm-wifi-p2p-peer.h +++ b/src/core/devices/wifi/nm-wifi-p2p-peer.h @@ -12,7 +12,7 @@ #define NM_TYPE_WIFI_P2P_PEER (nm_wifi_p2p_peer_get_type()) #define NM_WIFI_P2P_PEER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_P2P_PEER, NMWifiP2PPeer)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_P2P_PEER, NMWifiP2PPeer)) #define NM_WIFI_P2P_PEER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_P2P_PEER, NMWifiP2PPeerClass)) #define NM_IS_WIFI_P2P_PEER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WIFI_P2P_PEER)) diff --git a/src/core/devices/wifi/nm-wifi-utils.c b/src/core/devices/wifi/nm-wifi-utils.c index 1a8204b0..8a8d062f 100644 --- a/src/core/devices/wifi/nm-wifi-utils.c +++ b/src/core/devices/wifi/nm-wifi-utils.c @@ -11,6 +11,7 @@ #include #include +#include "libnm-glib-aux/nm-str-buf.h" #include "nm-utils.h" #include "libnm-core-intern/nm-core-internal.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h" @@ -812,13 +813,12 @@ nm_wifi_utils_complete_connection(GBytes *ap_ssid, * setting. Since there's so much configuration required for it, there's * no way it can be automatically completed. */ - } else if (nm_streq0(key_mgmt, "wpa-psk") - || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE - && (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK - || ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK))) { + } else if (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE + && (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK + || ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) { g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, - "wpa-psk", + nm_streq0(key_mgmt, "sae") ? "sae" : "wpa-psk", NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", NULL); @@ -828,7 +828,7 @@ nm_wifi_utils_complete_connection(GBytes *ap_ssid, || NM_FLAGS_ANY(ap_rsn_flags, NM_802_11_AP_SEC_KEY_MGMT_OWE | NM_802_11_AP_SEC_KEY_MGMT_OWE_TM)) { g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "owe", NULL); - } else if (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK + } else if (nm_streq0(key_mgmt, "wpa-psk") || ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK || ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) { g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, @@ -1004,7 +1004,7 @@ psk_setting_to_iwd_config(GKeyFile *file, NMSettingWirelessSecurity *s_wsec, GEr if (NM_FLAGS_ANY(psk_flags, SECRETS_DONT_STORE_FLAGS)) { nm_log_info( LOGD_WIFI, - "IWD network config is being created wihout the PSK but IWD will save the PSK on " + "IWD network config is being created without the PSK but IWD will save the PSK on " "successful activation not honoring the psk-flags property"); } return TRUE; @@ -1549,10 +1549,16 @@ eap_setting_to_iwd_config(GKeyFile *file, NMSetting8021x *s_8021x, GError **erro } static gboolean -ip4_config_to_iwd_config(GKeyFile *file, NMSettingIPConfig *s_ip, GError **error) +ip_config_to_iwd_config(int addr_family, GKeyFile *file, NMSettingIPConfig *s_ip, GError **error) { - guint num; - struct in_addr ip; + const int IS_IPv4 = NM_IS_IPv4(addr_family); + nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_488, FALSE); + NMIPAddress *addr; + guint num; + guint i; + char buf[NM_INET_ADDRSTRLEN + 10]; + const char *kf_group = IS_IPv4 ? "IPv4" : "IPv6"; + const char *gw; /* These settings are not acutally used unless global * [General].EnableNetworkConfiguration is true, which we don't support. @@ -1564,15 +1570,26 @@ ip4_config_to_iwd_config(GKeyFile *file, NMSettingIPConfig *s_ip, GError **error if (!s_ip) return TRUE; + nm_assert(NM_IS_IPv4(addr_family) ? NM_IS_SETTING_IP4_CONFIG(s_ip) + : NM_IS_SETTING_IP6_CONFIG(s_ip)); + num = nm_setting_ip_config_get_num_dns(s_ip); if (num) { - nm_auto_free_gstring GString *s = g_string_sized_new(128); - guint i; - + nm_str_buf_reset(&strbuf); for (i = 0; i < num; i++) { - if (s->len) - g_string_append_c(s, ' '); - g_string_append(s, nm_setting_ip_config_get_dns(s_ip, i)); + char sbuf[NM_INET_ADDRSTRLEN]; + NMIPAddr a; + + if (!nm_utils_dnsname_parse_assert(addr_family, + nm_setting_ip_config_get_dns(s_ip, i), + NULL, + &a, + NULL)) + continue; + + if (strbuf.len > 0) + nm_str_buf_append_c(&strbuf, ' '); + nm_str_buf_append(&strbuf, nm_inet_ntop(addr_family, &a, sbuf)); } /* It doesn't matter whether we add the DNS under [IPv4] or [IPv6] * except that with method=auto the list will override the @@ -1581,97 +1598,61 @@ ip4_config_to_iwd_config(GKeyFile *file, NMSettingIPConfig *s_ip, GError **error * Note ignore-auto-dns=false isn't supported, this list always * overrides the DHCP DNSes. */ - g_key_file_set_string(file, "IPv4", "DNS", s->str); + g_key_file_set_string(file, kf_group, "DNS", nm_str_buf_get_str(&strbuf)); } - if (!nm_streq0(nm_setting_ip_config_get_method(s_ip), NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) - return TRUE; - - num = nm_setting_ip_config_get_num_addresses(s_ip); - if (num) { - NMIPAddress *addr = nm_setting_ip_config_get_address(s_ip, 0); - guint prefix = nm_ip_address_get_prefix(addr); - in_addr_t netmask = _nm_utils_ip4_prefix_to_netmask(prefix); - char buf[INET_ADDRSTRLEN]; - - nm_ip_address_get_address_binary(addr, &ip); - g_key_file_set_string(file, "IPv4", "Address", nm_ip_address_get_address(addr)); - g_key_file_set_string(file, "IPv4", "Netmask", _nm_utils_inet4_ntop(netmask, buf)); - } else { - inet_pton(AF_INET, "10.42.0.100", &ip); - g_key_file_set_string(file, "IPv4", "Address", "10.42.0.100"); + if (!IS_IPv4) { + if (!NM_IN_STRSET(nm_setting_ip_config_get_method(s_ip), + NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_DHCP, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) + return TRUE; + g_key_file_set_boolean(file, kf_group, "Enabled", TRUE); } - if (nm_setting_ip_config_get_gateway(s_ip)) { - g_key_file_set_string(file, "IPv4", "Gateway", nm_setting_ip_config_get_gateway(s_ip)); - } else { - uint32_t val; - char buf[INET_ADDRSTRLEN]; - - /* IWD won't enable static IP unless both Address and Gateway are - * set so generate a gateway address if not known. - */ - val = (ntohl(ip.s_addr) & 0xfffffff0) + 1; - if (val == ntohl(ip.s_addr)) - val += 1; - g_key_file_set_string(file, "IPv4", "Gateway", _nm_utils_inet4_ntop(htonl(val), buf)); - } - - return TRUE; -} - -static gboolean -ip6_config_to_iwd_config(GKeyFile *file, NMSettingIPConfig *s_ip, GError **error) -{ - guint num; - NMIPAddress *addr; - char buf[INET6_ADDRSTRLEN + 10]; - - if (!s_ip) + num = nm_setting_ip_config_get_num_addresses(s_ip); + if (num == 0) return TRUE; - num = nm_setting_ip_config_get_num_dns(s_ip); - if (num) { - nm_auto_free_gstring GString *s = g_string_sized_new(128); - guint i; + addr = nm_setting_ip_config_get_address(s_ip, 0); + gw = nm_setting_ip_config_get_gateway(s_ip); - for (i = 0; i < num; i++) { - if (s->len) - g_string_append_c(s, ' '); - g_string_append(s, nm_setting_ip_config_get_dns(s_ip, i)); - } - g_key_file_set_string(file, "IPv6", "DNS", s->str); - } + if (IS_IPv4) { + in_addr_t ip; - if (!NM_IN_STRSET(nm_setting_ip_config_get_method(s_ip), - NM_SETTING_IP6_CONFIG_METHOD_AUTO, - NM_SETTING_IP6_CONFIG_METHOD_DHCP, - NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) - return TRUE; + nm_ip_address_get_address_binary(addr, &ip); - g_key_file_set_boolean(file, "IPv6", "Enabled", TRUE); + g_key_file_set_string(file, kf_group, "Address", nm_ip_address_get_address(addr)); + g_key_file_set_string( + file, + kf_group, + "Netmask", + nm_inet4_ntop(nm_ip4_addr_netmask_from_prefix(nm_ip_address_get_prefix(addr)), buf)); - if (!nm_streq0(nm_setting_ip_config_get_method(s_ip), NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) - return TRUE; + if (!gw) { + guint32 val; - if (!nm_setting_ip_config_get_num_addresses(s_ip)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - "IP address required for IPv6 manual config"); - return FALSE; + /* IWD won't enable static IP unless both Address and Gateway are + * set so generate a gateway address if not known. + */ + val = (ntohl(ip) & 0xfffffff0) + 1; + if (val == ntohl(ip)) + val += 1; + gw = nm_inet4_ntop(htonl(val), buf); + } + g_key_file_set_string(file, kf_group, "Gateway", gw); + } else { + g_key_file_set_string(file, + kf_group, + "Address", + nm_sprintf_buf(buf, + "%s/%u", + nm_ip_address_get_address(addr), + nm_ip_address_get_prefix(addr))); + if (gw) + g_key_file_set_string(file, kf_group, "Gateway", gw); } - addr = nm_setting_ip_config_get_address(s_ip, 0); - g_key_file_set_string(file, - "IPv6", - "Address", - nm_sprintf_buf(buf, - "%s/%u", - nm_ip_address_get_address(addr), - nm_ip_address_get_prefix(addr))); - if (nm_setting_ip_config_get_gateway(s_ip)) - g_key_file_set_string(file, "IPv6", "Gateway", nm_setting_ip_config_get_gateway(s_ip)); return TRUE; } @@ -1759,13 +1740,15 @@ nm_wifi_utils_connection_to_iwd_config(NMConnection *connection, else if (cloned_mac_addr && nm_utils_hwaddr_valid(cloned_mac_addr, ETH_ALEN)) g_key_file_set_string(file, "Settings", "AddressOverride", cloned_mac_addr); - if (!ip4_config_to_iwd_config( + if (!ip_config_to_iwd_config( + AF_INET, file, NM_SETTING_IP_CONFIG(nm_connection_get_setting_ip4_config(connection)), error)) return NULL; - if (!ip6_config_to_iwd_config( + if (!ip_config_to_iwd_config( + AF_INET6, file, NM_SETTING_IP_CONFIG(nm_connection_get_setting_ip6_config(connection)), error)) diff --git a/src/core/devices/wwan/nm-device-modem.c b/src/core/devices/wwan/nm-device-modem.c index e0209aff..b83120f9 100644 --- a/src/core/devices/wwan/nm-device-modem.c +++ b/src/core/devices/wwan/nm-device-modem.c @@ -273,6 +273,15 @@ modem_state_cb(NMModem *modem, int new_state_i, int old_state_i, gpointer user_d /* Called when the ModemManager modem enabled state is changed externally * to NetworkManager (eg something using MM's D-Bus API directly). */ + + if (!NM_MODEM_GET_CLASS(priv->modem)->set_mm_enabled) { + /* We cannot re-enable this modem, thus device becomes unavailable. */ + nm_device_state_changed(device, + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_USER_REQUESTED); + return; + } + if (nm_device_is_activating(device) || dev_state == NM_DEVICE_STATE_ACTIVATED) { /* user-initiated action, hence DISCONNECTED not FAILED */ nm_device_state_changed(device, @@ -435,6 +444,13 @@ check_connection_available(NMDevice *device, return FALSE; } + if (!NM_MODEM_GET_CLASS(priv->modem)->set_mm_enabled && state <= NM_MODEM_STATE_DISABLING) { + nm_utils_error_set_literal(error, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "modem is disabled and NM cannot enable it"); + return FALSE; + } + if (state == NM_MODEM_STATE_LOCKED) { if (!nm_connection_get_setting_gsm(connection)) { nm_utils_error_set_literal(error, @@ -599,6 +615,11 @@ set_enabled(NMDevice *device, gboolean enabled) if (enabled == FALSE) { nm_device_state_changed(device, NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_NONE); + } else { + /* It's possible that the modem is enabled outside of NM. Need to recheck. */ + nm_device_queue_recheck_available(device, + NM_DEVICE_STATE_REASON_MODEM_AVAILABLE, + NM_DEVICE_STATE_REASON_MODEM_FAILED); } } @@ -617,6 +638,9 @@ is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags) if (modem_state <= NM_MODEM_STATE_INITIALIZING) return FALSE; + if (!NM_MODEM_GET_CLASS(priv->modem)->set_mm_enabled && modem_state <= NM_MODEM_STATE_DISABLING) + return FALSE; + return TRUE; } diff --git a/src/core/devices/wwan/nm-device-modem.h b/src/core/devices/wwan/nm-device-modem.h index f171d76f..317a5bde 100644 --- a/src/core/devices/wwan/nm-device-modem.h +++ b/src/core/devices/wwan/nm-device-modem.h @@ -11,7 +11,7 @@ #define NM_TYPE_DEVICE_MODEM (nm_device_modem_get_type()) #define NM_DEVICE_MODEM(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_MODEM, NMDeviceModem)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_MODEM, NMDeviceModem)) #define NM_DEVICE_MODEM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_MODEM, NMDeviceModemClass)) #define NM_IS_DEVICE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_MODEM)) diff --git a/src/core/devices/wwan/nm-modem-broadband.c b/src/core/devices/wwan/nm-modem-broadband.c index db4fb29f..f0907c46 100644 --- a/src/core/devices/wwan/nm-modem-broadband.c +++ b/src/core/devices/wwan/nm-modem-broadband.c @@ -636,7 +636,7 @@ connect_context_step(NMModemBroadband *self) if (ctx->ip_types_i < ctx->ip_types->len) { NMModemIPType current; - current = g_array_index(ctx->ip_types, NMModemIPType, ctx->ip_types_i); + current = nm_g_array_index(ctx->ip_types, NMModemIPType, ctx->ip_types_i); if (current == NM_MODEM_IP_TYPE_IPV4) mm_simple_connect_properties_set_ip_type(ctx->connect_properties, @@ -993,7 +993,7 @@ stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_metho /* Fully fail if invalid IP address retrieved */ address_string = mm_bearer_ip_config_get_address(self->_priv.ipv4_config); if (!address_string - || !nm_utils_parse_inaddr_bin(AF_INET, address_string, NULL, &address_network)) { + || !nm_inet_parse_bin(AF_INET, address_string, NULL, &address_network)) { g_set_error(&error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, @@ -1005,7 +1005,7 @@ stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_metho /* Missing gateway not a hard failure */ gw_string = mm_bearer_ip_config_get_gateway(self->_priv.ipv4_config); - if (gw_string && !nm_utils_parse_inaddr_bin(AF_INET, gw_string, NULL, &gw)) { + if (gw_string && !nm_inet_parse_bin(AF_INET, gw_string, NULL, &gw)) { g_set_error(&error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, @@ -1057,9 +1057,8 @@ stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_metho dns = mm_bearer_ip_config_get_dns(self->_priv.ipv4_config); for (i = 0; dns && dns[i]; i++) { - if (nm_utils_parse_inaddr_bin(AF_INET, dns[i], NULL, &address_network) - && address_network > 0) { - nm_l3_config_data_add_nameserver(l3cd, AF_INET, &address_network); + if (nm_inet_parse_bin(AF_INET, dns[i], NULL, &address_network) && address_network > 0) { + nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET, &address_network, NULL); _LOGI(" DNS %s", dns[i]); } } @@ -1174,7 +1173,7 @@ stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_metho struct in6_addr addr; if (inet_pton(AF_INET6, dns[i], &addr)) { - nm_l3_config_data_add_nameserver(l3cd, AF_INET6, &addr); + nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET6, &addr, NULL); _LOGI(" DNS %s", dns[i]); } } diff --git a/src/core/devices/wwan/nm-modem-broadband.h b/src/core/devices/wwan/nm-modem-broadband.h index 627fe25a..e4467310 100644 --- a/src/core/devices/wwan/nm-modem-broadband.h +++ b/src/core/devices/wwan/nm-modem-broadband.h @@ -10,7 +10,7 @@ #define NM_TYPE_MODEM_BROADBAND (nm_modem_broadband_get_type()) #define NM_MODEM_BROADBAND(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MODEM_BROADBAND, NMModemBroadband)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MODEM_BROADBAND, NMModemBroadband)) #define NM_MODEM_BROADBAND_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_MODEM_BROADBAND, NMModemBroadbandClass)) #define NM_IS_MODEM_BROADBAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_MODEM_BROADBAND)) diff --git a/src/core/devices/wwan/nm-modem-manager.h b/src/core/devices/wwan/nm-modem-manager.h index e89a032b..a9339ffc 100644 --- a/src/core/devices/wwan/nm-modem-manager.h +++ b/src/core/devices/wwan/nm-modem-manager.h @@ -12,7 +12,7 @@ #define NM_TYPE_MODEM_MANAGER (nm_modem_manager_get_type()) #define NM_MODEM_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MODEM_MANAGER, NMModemManager)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MODEM_MANAGER, NMModemManager)) #define NM_MODEM_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_MODEM_MANAGER, NMModemManagerClass)) #define NM_IS_MODEM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_MODEM_MANAGER)) diff --git a/src/core/devices/wwan/nm-modem-ofono.c b/src/core/devices/wwan/nm-modem-ofono.c index fc27c3fd..b3745621 100644 --- a/src/core/devices/wwan/nm-modem-ofono.c +++ b/src/core/devices/wwan/nm-modem-ofono.c @@ -7,8 +7,12 @@ #include "nm-modem-ofono.h" +#include "libnm-glib-aux/nm-dbus-aux.h" #include "libnm-core-intern/nm-core-internal.h" +#include "libnm-glib-aux/nm-uuid.h" #include "devices/nm-device-private.h" +#include "nm-setting-gsm.h" +#include "settings/nm-settings.h" #include "nm-modem.h" #include "libnm-platform/nm-platform.h" #include "nm-l3-config-data.h" @@ -26,28 +30,45 @@ /*****************************************************************************/ +typedef struct { + NMModemOfono *self; + char *name; + char *type; + gboolean preferred; + GDBusProxy *proxy; +} OfonoContextData; + typedef struct { GHashTable *connect_properties; + GHashTable *connections; + GHashTable *contexts; GDBusProxy *modem_proxy; GDBusProxy *connman_proxy; - GDBusProxy *context_proxy; GDBusProxy *sim_proxy; GCancellable *modem_proxy_cancellable; GCancellable *connman_proxy_cancellable; - GCancellable *context_proxy_cancellable; + GCancellable *connect_cancellable; GCancellable *sim_proxy_cancellable; GError *property_error; - char *context_path; char *imsi; gboolean modem_online; + gboolean connman_powered; gboolean gprs_attached; NML3ConfigData *l3cd_4; + NMSettings *settings; + + guint n_context_proxy_pending; + + /* unowned; reference held by 'contexts' above. */ + OfonoContextData *current_octx; + + GSource *deferred_connection_timeout_source; } NMModemOfonoPrivate; struct _NMModemOfono { @@ -96,6 +117,21 @@ G_DEFINE_TYPE(NMModemOfono, nm_modem_ofono, NM_TYPE_MODEM) /*****************************************************************************/ +/* + * Deterministic UUID is used to pair imsi+context with exported connection + * (via a pair of hash tables). + */ + +static char * +_generate_uuid(const char *imsi, const char *object_path) +{ + return nm_uuid_generate_from_strings( + NM_UUID_TYPE_VERSION5, + &NM_UUID_INIT(b5, 6b, ad, f5, ef, 9d, 4a, 21, a8, e7, 7d, db, 69, bb, 6b, ee), + imsi, + object_path); +} + static void get_capabilities(NMModem *_self, NMDeviceModemCapabilities *modem_caps, @@ -106,6 +142,8 @@ get_capabilities(NMModem *_self, *current_caps = NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS; } +static void do_context_activate(NMModemOfono *self); + static void update_modem_state(NMModemOfono *self) { @@ -114,13 +152,16 @@ update_modem_state(NMModemOfono *self) NMModemState new_state = NM_MODEM_STATE_DISABLED; const char *reason = NULL; - _LOGI("'Attached': %s 'Online': %s 'IMSI': %s", + _LOGI("'Attached': %s 'Online': %s 'Powered': %s 'IMSI': %s", priv->gprs_attached ? "true" : "false", priv->modem_online ? "true" : "false", + priv->connman_powered ? "true" : "false", priv->imsi); if (priv->modem_online == FALSE) { reason = "modem 'Online=false'"; + } else if (priv->connman_powered == FALSE) { + reason = "ConnectionManager 'Powered=false'"; } else if (priv->imsi == NULL && state != NM_MODEM_STATE_ENABLING) { reason = "modem not ready"; } else if (priv->gprs_attached == FALSE) { @@ -131,8 +172,27 @@ update_modem_state(NMModemOfono *self) reason = "modem ready"; } - if (state != new_state) + if (state != new_state) { + if (new_state == NM_MODEM_STATE_DISABLED && priv->deferred_connection_timeout_source) { + /* + * Do this before set_state(), because we want the final device state + * to be "unavailable", not "failed" or "disconnected". + */ + _LOGI("canceling deferred context activation"); + + nm_clear_g_source_inst(&priv->deferred_connection_timeout_source); + nm_modem_emit_prepare_result(NM_MODEM(self), FALSE, NM_DEVICE_STATE_REASON_MODEM_BUSY); + } + nm_modem_set_state(NM_MODEM(self), new_state, reason); + + if (new_state == NM_MODEM_STATE_REGISTERED && priv->deferred_connection_timeout_source) { + _LOGI("resuming deferred context activation"); + + nm_clear_g_source_inst(&priv->deferred_connection_timeout_source); + do_context_activate(self); + } + } } /* Disconnect */ @@ -213,7 +273,8 @@ disconnect(NMModem *modem, ctx->callback = callback; ctx->callback_user_data = user_data; - if (state != NM_MODEM_STATE_CONNECTED || g_cancellable_is_cancelled(cancellable)) { + if (state != NM_MODEM_STATE_CONNECTED || g_cancellable_is_cancelled(cancellable) + || priv->current_octx == NULL) { nm_utils_invoke_on_idle(cancellable, disconnect_context_complete_on_idle, ctx); return; } @@ -222,7 +283,7 @@ disconnect(NMModem *modem, NM_MODEM_STATE_DISCONNECTING, nm_modem_state_to_string(NM_MODEM_STATE_DISCONNECTING)); - g_dbus_proxy_call(priv->context_proxy, + g_dbus_proxy_call(priv->current_octx->proxy, "SetProperty", g_variant_new("(sv)", "Active", g_variant_new("b", warn)), G_DBUS_CALL_FLAGS_NONE, @@ -230,6 +291,8 @@ disconnect(NMModem *modem, ctx->cancellable, disconnect_done, ctx); + + priv->current_octx = NULL; } static void @@ -251,7 +314,7 @@ check_connection_compatible_with_modem(NMModem *modem, NMConnection *connection, { NMModemOfono *self = NM_MODEM_OFONO(modem); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); - const char *id; + const char *uuid; if (!_nm_connection_check_main_setting(connection, NM_SETTING_GSM_SETTING_NAME, NULL)) { nm_utils_error_set(error, @@ -268,19 +331,12 @@ check_connection_compatible_with_modem(NMModem *modem, NMConnection *connection, return FALSE; } - id = nm_connection_get_id(connection); - - if (!strstr(id, "/context")) { - nm_utils_error_set_literal(error, - NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "the connection ID has no context"); - return FALSE; - } + uuid = nm_connection_get_uuid(connection); - if (!strstr(id, priv->imsi)) { + if (!g_hash_table_contains(priv->contexts, uuid)) { nm_utils_error_set_literal(error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "the connection ID does not contain the IMSI"); + "connection ID does not match known contexts"); return FALSE; } @@ -398,11 +454,11 @@ _sim_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) priv->sim_proxy = proxy; /* Watch for custom ofono PropertyChanged signals */ - _nm_dbus_signal_connect(priv->sim_proxy, - "PropertyChanged", - G_VARIANT_TYPE("(sv)"), - G_CALLBACK(sim_property_changed), - self); + _nm_dbus_proxy_signal_connect(priv->sim_proxy, + "PropertyChanged", + G_VARIANT_TYPE("(sv)"), + G_CALLBACK(sim_property_changed), + self); g_dbus_proxy_call(priv->sim_proxy, "GetProperties", @@ -467,6 +523,19 @@ handle_connman_property(GDBusProxy *proxy, const char *property, GVariant *v, gp old_attached ? "true" : "false", attached ? "true" : "false"); + update_modem_state(self); + } + } else if (nm_streq(property, "Powered") && VARIANT_IS_OF_TYPE_BOOLEAN(v)) { + gboolean powered = g_variant_get_boolean(v); + gboolean old_powered = priv->connman_powered; + + _LOGD("Powered: %s", powered ? "True" : "False"); + + if (old_powered != powered) { + priv->connman_powered = powered; + + _LOGI("Powered %s -> %s", old_powered ? "true" : "false", powered ? "true" : "false"); + update_modem_state(self); } } @@ -484,7 +553,6 @@ static void connman_get_properties_done(GObject *source, GAsyncResult *result, gpointer user_data) { NMModemOfono *self; - NMModemOfonoPrivate *priv; gs_free_error GError *error = NULL; gs_unref_variant GVariant *v_properties = NULL; gs_unref_variant GVariant *v_dict = NULL; @@ -498,9 +566,6 @@ connman_get_properties_done(GObject *source, GAsyncResult *result, gpointer user return; self = NM_MODEM_OFONO(user_data); - priv = NM_MODEM_OFONO_GET_PRIVATE(self); - - g_clear_object(&priv->connman_proxy_cancellable); if (!v_properties) { g_dbus_error_strip_remote_error(error); @@ -525,6 +590,366 @@ connman_get_properties_done(GObject *source, GAsyncResult *result, gpointer user } } +static void +ofono_context_data_free(OfonoContextData *octx) +{ + g_free(octx->name); + g_free(octx->type); + + if (octx->proxy) { + g_signal_handlers_disconnect_by_data(octx->proxy, octx); + g_object_unref(octx->proxy); + } + + g_slice_free(OfonoContextData, octx); +} + +static void +add_or_update_connection(NMModemOfono *self, const char *context_name, const char *uuid) +{ + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + gs_unref_object NMConnection *connection = NULL; + NMSetting *setting; + NMSettingsConnection *sett_conn; + gs_free_error GError *error = NULL; + + /* + * See first if we have an existing connection (from previous or current + * run of NM) that we can update in-place. + */ + + sett_conn = nm_settings_get_connection_by_uuid(priv->settings, uuid); + if (sett_conn + && !NM_FLAGS_HAS(nm_settings_connection_get_flags(sett_conn), + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) { + /* + * Either we have a coliding connection, or our connection has been + * modified by user. For the latter case it's safe to leave it alone; + * the UUID will still let it connect. However, for the first case, + * we can't really do anything about it unless we re-write the way we + * track connection <-> context, which I (Ratchanan) don't want to do + * right now... + */ + return; + } + + connection = nm_simple_connection_new(); + setting = nm_setting_connection_new(); + g_object_set(setting, + NM_SETTING_CONNECTION_ID, + context_name, + NM_SETTING_CONNECTION_UUID, + uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_GSM_SETTING_NAME, + NULL); + nm_connection_add_setting(connection, setting); + + setting = nm_setting_gsm_new(); + + /* + * oFono should already know how to handle placing the call, but NM + * insists on having a number. Pass the usual *99#. + */ + g_object_set(setting, NM_SETTING_GSM_NUMBER, "*99#", NULL); + nm_connection_add_setting(connection, setting); + + if (!nm_connection_normalize(connection, NULL, NULL, &error)) { + nm_log_err(LOGD_MB, + "ofono: could not not generate a connection for %s: %s", + context_name, + error->message); + return; + } + + if (!sett_conn) { + nm_settings_add_connection(priv->settings, + NULL, /* plugin */ + connection, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, + NM_SETTINGS_CONNECTION_ADD_REASON_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, + &sett_conn, + &error); + } else { + nm_settings_update_connection(priv->settings, + sett_conn, + NULL, /* plugin_name */ + connection, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE, + /* log_context_name */ "ofono", + &error); + } + + if (!sett_conn) { + nm_log_warn(LOGD_MB, + "ofono: could not add or update new connection for '%s' (%s): %s", + context_name, + uuid, + error->message); + return; + } + + g_hash_table_insert(priv->connections, g_strdup(uuid), g_object_ref(sett_conn)); +} + +/* + * Used when we decide a connection is not needed. Rationale being that the + * connection could have been modified by user since it's created by us, + * after which we don't want to delete, but we don't want to track it either. + */ + +static void +untrack_connection_and_delete_if_generated(NMModemOfono *self, char const *uuid) +{ + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + NMSettingsConnection *sett_conn; + + sett_conn = g_hash_table_lookup(priv->connections, uuid); + if (!sett_conn) + return; + + if (NM_FLAGS_HAS(nm_settings_connection_get_flags(sett_conn), + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) + nm_settings_connection_delete(sett_conn, FALSE); + + g_hash_table_remove(priv->connections, uuid); +} + +static void +update_connection_list(NMModemOfono *self) +{ + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + GHashTableIter iter; + char *uuid; + OfonoContextData *octx; + OfonoContextData *octx_preferred = NULL; + + _LOGI("(re-)checking which context needs a connection"); + + g_hash_table_iter_init(&iter, priv->contexts); + while (g_hash_table_iter_next(&iter, (gpointer *) &uuid, (gpointer *) &octx)) { + if (octx->preferred) { + octx_preferred = octx; + break; + } + } + + g_hash_table_iter_init(&iter, priv->contexts); + while (g_hash_table_iter_next(&iter, (gpointer *) &uuid, (gpointer *) &octx)) { + gboolean connection_should_exist = + (!octx_preferred || octx_preferred == octx) + && (nm_streq(octx->type, "internet") || nm_streq(octx->type, "internet+mms")); + gboolean connection_exists = g_hash_table_contains(priv->connections, uuid); + + if (connection_should_exist && !connection_exists) { + _LOGI("creating connection for %s%s", + octx_preferred ? "preferred context " : "", + g_dbus_proxy_get_object_path(octx->proxy)); + add_or_update_connection(self, octx->name, uuid); + } else if (!connection_should_exist && connection_exists) { + _LOGI("removing connection for %s", g_dbus_proxy_get_object_path(octx->proxy)); + untrack_connection_and_delete_if_generated(self, uuid); + + /* priv->current_octx is deliberately not cleared here because the + * disconnection chain happens in another main loop iteration. + */ + } + } +} + +static void handle_settings(NMModemOfono *self, GVariant *v_dict); + +static void +context_property_changed(GDBusProxy *proxy, const char *property, GVariant *v, gpointer user_data) +{ + OfonoContextData *octx = user_data; + NMModemOfono *self = octx->self; + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + gs_unref_variant GVariant *v_inner = g_variant_get_child_value(v, 0); + + if (!v_inner) { + _LOGW("ofono: (%s): error handling PropertyChanged signal", + nm_modem_get_uid(NM_MODEM(self))); + return; + } + + if (nm_streq(property, "Name")) { + gs_free char *uuid = NULL; + + g_return_if_fail(g_variant_is_of_type(v_inner, G_VARIANT_TYPE_STRING)); + g_free(octx->name); + octx->name = g_variant_dup_string(v_inner, /* &length */ NULL); + + uuid = _generate_uuid(priv->imsi, g_dbus_proxy_get_object_path(proxy)); + if (g_hash_table_contains(priv->connections, uuid)) + add_or_update_connection(self, octx->name, uuid); + } else if (nm_streq(property, "Type")) { + g_return_if_fail(g_variant_is_of_type(v_inner, G_VARIANT_TYPE_STRING)); + g_free(octx->type); + octx->type = g_variant_dup_string(v_inner, /* &length */ NULL); + + update_connection_list(self); + } else if (nm_streq(property, "Preferred")) { + g_return_if_fail(g_variant_is_of_type(v_inner, G_VARIANT_TYPE_BOOLEAN)); + octx->preferred = g_variant_get_boolean(v_inner); + + update_connection_list(self); + } else if (nm_streq(property, "Settings") && priv->current_octx == octx) { + g_return_if_fail(g_variant_is_of_type(v_inner, G_VARIANT_TYPE_VARDICT)); + handle_settings(self, v_inner); + } +} + +static void +_context_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + OfonoContextData *octx = user_data; + NMModemOfono *self; + NMModemOfonoPrivate *priv; + gs_free_error GError *error = NULL; + GDBusProxy *proxy; + char *uuid; + + proxy = g_dbus_proxy_new_for_bus_finish(result, &error); + if (!proxy && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + ofono_context_data_free(octx); + return; + } + + self = octx->self; + priv = NM_MODEM_OFONO_GET_PRIVATE(self); + + if (!proxy) { + _LOGW("failed to create ConnectionContext proxy: %s", error->message); + ofono_context_data_free(octx); + return; + } + + _LOGD("recieved proxy for %s", g_dbus_proxy_get_object_path(proxy)); + octx->proxy = proxy; + + _nm_dbus_proxy_signal_connect(proxy, + "PropertyChanged", + G_VARIANT_TYPE("(sv)"), + G_CALLBACK(context_property_changed), + octx); + + uuid = _generate_uuid(priv->imsi, g_dbus_proxy_get_object_path(proxy)); + g_hash_table_insert(priv->contexts, uuid, octx); + priv->n_context_proxy_pending--; + + if (priv->n_context_proxy_pending == 0) + update_connection_list(self); +} + +static void +connman_context_removed(GDBusProxy *proxy, const char *object_path, gpointer user_data) +{ + NMModemOfono *self = NM_MODEM_OFONO(user_data); + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + gs_free char *uuid = NULL; + OfonoContextData *octx; + + /* look up the connction, and if we have connection disconnect and remove it */ + uuid = _generate_uuid(priv->imsi, object_path); + untrack_connection_and_delete_if_generated(self, uuid); + + octx = g_hash_table_lookup(priv->contexts, uuid); + if (octx) { + gboolean preferred = octx->preferred; + + if (octx == priv->current_octx) + priv->current_octx = NULL; + + g_hash_table_remove(priv->contexts, uuid); + + if (preferred) + update_connection_list(self); + } +} + +static void +connman_context_added(GDBusProxy *proxy, const char *object_path, GVariant *v, gpointer user_data) +{ + NMModemOfono *self = user_data; + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + OfonoContextData *octx; + gs_unref_variant GVariant *v_context_type = NULL; + gs_unref_variant GVariant *v_context_name = NULL; + gs_unref_variant GVariant *v_context_preferred = NULL; + + nm_log_info(LOGD_MB, "ofono: processing context %s", object_path); + + v_context_name = g_variant_lookup_value(v, "Name", G_VARIANT_TYPE_STRING); + v_context_type = g_variant_lookup_value(v, "Type", G_VARIANT_TYPE_STRING); + if (!v_context_name || !v_context_type) { + nm_log_err(LOGD_MB, "ofono: context dictionary is missing required key(s)."); + return; + } + + /* Preferred property exists in some oFono fork only (mostly Ubuntu Touch's). */ + v_context_preferred = g_variant_lookup_value(v, "Preferred", G_VARIANT_TYPE_BOOLEAN); + + octx = g_slice_new0(OfonoContextData); + octx->self = self; + octx->name = g_variant_dup_string(v_context_name, NULL); + octx->type = g_variant_dup_string(v_context_type, NULL); + octx->preferred = v_context_preferred && g_variant_get_boolean(v_context_preferred); + + priv->n_context_proxy_pending++; + g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES + | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + NULL, /* GDBusInterfaceInfo */ + OFONO_DBUS_SERVICE, + object_path, + OFONO_DBUS_INTERFACE_CONNECTION_CONTEXT, + priv->connman_proxy_cancellable, + _context_proxy_new_cb, + octx); +} + +static void +connman_get_contexts_done(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMModemOfono *self; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *v_contexts = NULL; + gs_unref_variant GVariant *v_objects = NULL; + gs_unref_variant GVariant *v = NULL; + GVariantIter i; + const char *object_path; + + v_contexts = _nm_dbus_proxy_call_finish(G_DBUS_PROXY(source), + result, + G_VARIANT_TYPE("(a(oa{sv}))"), + &error); + if (!v_contexts && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = user_data; + + if (!v_contexts) { + g_dbus_error_strip_remote_error(error); + _LOGW("Error getting list of contexts: %s", error->message); + return; + } + + nm_log_info(LOGD_MB, "ofono: printing %s", g_variant_get_type_string(v_contexts)); + + v_objects = g_variant_get_child_value(v_contexts, 0); + + g_variant_iter_init(&i, v_objects); + while (g_variant_iter_loop(&i, "(&o@a{sv})", &object_path, &v)) + connman_context_added(NULL, object_path, v, self); +} + static void _connman_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) { @@ -548,11 +973,23 @@ _connman_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) priv->connman_proxy = proxy; - _nm_dbus_signal_connect(priv->connman_proxy, - "PropertyChanged", - G_VARIANT_TYPE("(sv)"), - G_CALLBACK(connman_property_changed), - self); + _nm_dbus_proxy_signal_connect(priv->connman_proxy, + "PropertyChanged", + G_VARIANT_TYPE("(sv)"), + G_CALLBACK(connman_property_changed), + self); + + _nm_dbus_proxy_signal_connect(priv->connman_proxy, + "ContextAdded", + G_VARIANT_TYPE("(oa{sv})"), + G_CALLBACK(connman_context_added), + self); + + _nm_dbus_proxy_signal_connect(priv->connman_proxy, + "ContextRemoved", + G_VARIANT_TYPE("(o)"), + G_CALLBACK(connman_context_removed), + self); g_dbus_proxy_call(priv->connman_proxy, "GetProperties", @@ -562,6 +999,15 @@ _connman_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) priv->connman_proxy_cancellable, connman_get_properties_done, self); + + g_dbus_proxy_call(priv->connman_proxy, + "GetContexts", + NULL, + G_DBUS_CALL_FLAGS_NONE, + 20000, + priv->connman_proxy_cancellable, + connman_get_contexts_done, + self); } static void @@ -572,6 +1018,9 @@ handle_connman_iface(NMModemOfono *self, gboolean found) _LOGD("ConnectionManager interface %sfound", found ? "" : "not "); if (!found && (priv->connman_proxy || priv->connman_proxy_cancellable)) { + GHashTableIter iter; + NMSettingsConnection *conn; + _LOGI("ConnectionManager interface disappeared"); nm_clear_g_cancellable(&priv->connman_proxy_cancellable); if (priv->connman_proxy) { @@ -584,6 +1033,18 @@ handle_connman_iface(NMModemOfono *self, gboolean found) */ priv->gprs_attached = FALSE; + g_hash_table_iter_init(&iter, priv->connections); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &conn)) { + if (NM_FLAGS_HAS(nm_settings_connection_get_flags(conn), + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) { + nm_settings_connection_delete(conn, FALSE); + } + } + + priv->current_octx = NULL; + g_hash_table_remove_all(priv->connections); + g_hash_table_remove_all(priv->contexts); + update_modem_state(self); } else if (found && (!priv->connman_proxy && !priv->connman_proxy_cancellable)) { _LOGI("found new ConnectionManager interface"); @@ -716,7 +1177,7 @@ stage1_prepare_done(GObject *source, GAsyncResult *result, gpointer user_data) self = NM_MODEM_OFONO(user_data); priv = NM_MODEM_OFONO_GET_PRIVATE(self); - g_clear_object(&priv->context_proxy_cancellable); + g_clear_object(&priv->connect_cancellable); nm_clear_pointer(&priv->connect_properties, g_hash_table_destroy); @@ -730,9 +1191,8 @@ stage1_prepare_done(GObject *source, GAsyncResult *result, gpointer user_data) } static void -handle_settings(GVariant *v_dict, gpointer user_data) +handle_settings(NMModemOfono *self, GVariant *v_dict) { - NMModemOfono *self = NM_MODEM_OFONO(user_data); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatformIP4Address address; @@ -744,14 +1204,20 @@ handle_settings(GVariant *v_dict, gpointer user_data) int ifindex; GError *error = NULL; - //_LOGD("PropertyChanged: %s", property); - /* * TODO: might be a good idea and re-factor this to mimic bluez-device, * ie. have this function just check the key, and call a sub-func to * handle the action. */ + if (nm_modem_get_state(NM_MODEM(self)) < NM_MODEM_STATE_REGISTERED) { + /* + * Connection definitely isn't happening. Avoid trigering bogus + * failure which would put device in a wrong state. + */ + return; + } + _LOGI("IPv4 static Settings:"); if (!g_variant_lookup(v_dict, "Interface", "&s", &interface)) { @@ -786,7 +1252,7 @@ handle_settings(GVariant *v_dict, gpointer user_data) _LOGW("Settings 'Address' missing"); goto out; } - if (!s || !nm_utils_parse_inaddr_bin(AF_INET, s, NULL, &address_network)) { + if (!s || !nm_inet_parse_bin(AF_INET, s, NULL, &address_network)) { _LOGW("can't convert 'Address' %s to addr", s ?: ""); goto out; } @@ -801,11 +1267,11 @@ handle_settings(GVariant *v_dict, gpointer user_data) _LOGW("Settings 'Netmask' missing"); goto out; } - if (!s || !nm_utils_parse_inaddr_bin(AF_INET, s, NULL, &address_network)) { + if (!s || !nm_inet_parse_bin(AF_INET, s, NULL, &address_network)) { _LOGW("invalid 'Netmask': %s", s ?: ""); goto out; } - address.plen = _nm_utils_ip4_netmask_to_prefix(address_network); + address.plen = nm_ip4_addr_netmask_to_prefix(address_network); _LOGI("Address: %s", nm_platform_ip4_address_to_string(&address, sbuf, sizeof(sbuf))); nm_l3_config_data_add_address_4(priv->l3cd_4, &address); @@ -814,7 +1280,7 @@ handle_settings(GVariant *v_dict, gpointer user_data) _LOGW("Settings 'Gateway' missing"); goto out; } - if (!nm_utils_parse_inaddr_bin(AF_INET, s, NULL, &gateway_network)) { + if (!nm_inet_parse_bin(AF_INET, s, NULL, &gateway_network)) { _LOGW("invalid 'Gateway': %s", s); goto out; } @@ -841,14 +1307,13 @@ handle_settings(GVariant *v_dict, gpointer user_data) gsize i; for (i = 0; array[i]; i++) { - if (!nm_utils_parse_inaddr_bin(AF_INET, array[i], NULL, &address_network) - || !address_network) { + if (!nm_inet_parse_bin(AF_INET, array[i], NULL, &address_network) || !address_network) { _LOGW("invalid NameServer: %s", array[i]); continue; } any_good = TRUE; _LOGI("DNS: %s", array[i]); - nm_l3_config_data_add_nameserver(priv->l3cd_4, AF_INET, &address_network); + nm_l3_config_data_add_nameserver_detail(priv->l3cd_4, AF_INET, &address_network, NULL); } if (!any_good) { _LOGW("Settings: 'DomainNameServers': none specified"); @@ -858,7 +1323,7 @@ handle_settings(GVariant *v_dict, gpointer user_data) if (g_variant_lookup(v_dict, "MessageProxy", "&s", &s)) { _LOGI("MessageProxy: %s", s); - if (s && nm_utils_parse_inaddr_bin(AF_INET, s, NULL, &address_network)) { + if (s && nm_inet_parse_bin(AF_INET, s, NULL, &address_network)) { const NMPlatformIP4Route mms_route = { .network = address_network, .plen = 32, @@ -892,28 +1357,6 @@ out: } } -static void -context_property_changed(GDBusProxy *proxy, const char *property, GVariant *v, gpointer user_data) -{ - NMModemOfono *self = NM_MODEM_OFONO(user_data); - gs_unref_variant GVariant *v_dict = NULL; - - _LOGD("PropertyChanged: %s", property); - - if (g_strcmp0(property, "Settings") != 0) - return; - - v_dict = g_variant_get_child_value(v, 0); - if (!v_dict) { - _LOGW("ofono: (%s): error getting IPv4 Settings", nm_modem_get_uid(NM_MODEM(self))); - return; - } - - g_assert(g_variant_is_of_type(v_dict, G_VARIANT_TYPE_VARDICT)); - - handle_settings(v_dict, user_data); -} - static void stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_method) { @@ -948,17 +1391,13 @@ out: static void context_properties_cb(GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { - NMModemOfono *self; - NMModemOfonoPrivate *priv; + NMModemOfono *self = user_data; gs_free_error GError *error = NULL; gs_unref_variant GVariant *properties = NULL; gs_unref_variant GVariant *settings = NULL; gs_unref_variant GVariant *v_dict = NULL; gboolean active; - self = NM_MODEM_OFONO(user_data); - priv = NM_MODEM_OFONO_GET_PRIVATE(self); - properties = g_dbus_proxy_call_finish(proxy, result, &error); if (!properties) { @@ -978,13 +1417,6 @@ context_properties_cb(GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat goto error; } - /* Watch for custom ofono PropertyChanged signals */ - _nm_dbus_signal_connect(priv->context_proxy, - "PropertyChanged", - G_VARIANT_TYPE("(sv)"), - G_CALLBACK(context_property_changed), - self); - if (active) { _LOGD("ofono: connection is already Active"); @@ -994,9 +1426,9 @@ context_properties_cb(GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat goto error; } - handle_settings(settings, user_data); + handle_settings(self, settings); } else { - g_dbus_proxy_call(priv->context_proxy, + g_dbus_proxy_call(proxy, "SetProperty", g_variant_new("(sv)", "Active", g_variant_new("b", TRUE)), G_DBUS_CALL_FLAGS_NONE, @@ -1012,36 +1444,15 @@ error: } static void -context_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) +do_context_activate(NMModemOfono *self) { - NMModemOfono *self; - NMModemOfonoPrivate *priv; - gs_free_error GError *error = NULL; - GDBusProxy *proxy; - - proxy = g_dbus_proxy_new_for_bus_finish(result, &error); - if (!proxy || g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); - self = NM_MODEM_OFONO(user_data); - priv = NM_MODEM_OFONO_GET_PRIVATE(self); + g_return_if_fail(NM_IS_MODEM_OFONO(self)); - if (!proxy) { - _LOGE("failed to create ofono ConnectionContext DBus proxy: %s", error->message); - g_clear_object(&priv->context_proxy_cancellable); - nm_modem_emit_prepare_result(NM_MODEM(self), FALSE, NM_DEVICE_STATE_REASON_MODEM_BUSY); - return; - } + nm_clear_g_cancellable(&priv->connect_cancellable); - priv->context_proxy = proxy; - - if (!priv->gprs_attached) { - g_clear_object(&priv->context_proxy_cancellable); - nm_modem_emit_prepare_result(NM_MODEM(self), - FALSE, - NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER); - return; - } + priv->connect_cancellable = g_cancellable_new(); /* We have an old copy of the settings from a previous activation, * clear it so that we can gate getting the IP config from oFono @@ -1051,39 +1462,16 @@ context_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) /* We need to directly query ConnectionContextinteface to get the current * property values */ - g_dbus_proxy_call(priv->context_proxy, + g_dbus_proxy_call(priv->current_octx->proxy, "GetProperties", NULL, G_DBUS_CALL_FLAGS_NONE, 20000, - NULL, + priv->connect_cancellable, (GAsyncReadyCallback) context_properties_cb, self); } -static void -do_context_activate(NMModemOfono *self) -{ - NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); - - g_return_if_fail(NM_IS_MODEM_OFONO(self)); - - nm_clear_g_cancellable(&priv->context_proxy_cancellable); - g_clear_object(&priv->context_proxy); - - priv->context_proxy_cancellable = g_cancellable_new(); - - g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, - NULL, - OFONO_DBUS_SERVICE, - priv->context_path, - OFONO_DBUS_INTERFACE_CONNECTION_CONTEXT, - priv->context_proxy_cancellable, - context_proxy_new_cb, - self); -} - static GHashTable * create_connect_properties(NMConnection *connection) { @@ -1109,6 +1497,20 @@ create_connect_properties(NMConnection *connection) return properties; } +static gboolean +wait_for_signal_timeout(gpointer user_data) +{ + NMModemOfono *self = NM_MODEM_OFONO(user_data); + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + + nm_modem_emit_prepare_result(NM_MODEM(self), + FALSE, + NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT); + + nm_clear_g_source_inst(&priv->deferred_connection_timeout_source); + return G_SOURCE_REMOVE; +} + static NMActStageReturn modem_act_stage1_prepare(NMModem *modem, NMConnection *connection, @@ -1116,20 +1518,10 @@ modem_act_stage1_prepare(NMModem *modem, { NMModemOfono *self = NM_MODEM_OFONO(modem); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); - const char *context_id; - char **id = NULL; + const char *uuid = nm_connection_get_uuid(connection); + OfonoContextData *octx = g_hash_table_lookup(priv->contexts, uuid); - context_id = nm_connection_get_id(connection); - id = g_strsplit(context_id, "/", 0); - g_return_val_if_fail(id[2], NM_ACT_STAGE_RETURN_FAILURE); - - _LOGD("trying %s %s", id[1], id[2]); - - g_free(priv->context_path); - priv->context_path = g_strdup_printf("%s/%s", nm_modem_get_path(modem), id[2]); - g_strfreev(id); - - if (!priv->context_path) { + if (!octx) { NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_GSM_APN_FAILED); return NM_ACT_STAGE_RETURN_FAILURE; } @@ -1139,11 +1531,19 @@ modem_act_stage1_prepare(NMModem *modem, priv->connect_properties = create_connect_properties(connection); - _LOGI("activating context %s", priv->context_path); + _LOGI("activating context %s", g_dbus_proxy_get_object_path(octx->proxy)); + priv->current_octx = octx; update_modem_state(self); if (nm_modem_get_state(modem) == NM_MODEM_STATE_REGISTERED) { do_context_activate(self); + } else if (nm_modem_get_state(modem) == NM_MODEM_STATE_SEARCHING) { + _LOGI("activation deferred while modem is searching for signal."); + + nm_clear_g_source_inst(&priv->deferred_connection_timeout_source); + priv->deferred_connection_timeout_source = + nm_g_timeout_add_seconds_source(60 /* seconds */, wait_for_signal_timeout, self); + /* update_modem_state() will advance the next step. */ } else { _LOGW("could not activate context: modem is not registered."); NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER); @@ -1176,11 +1576,11 @@ modem_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) priv->modem_proxy = proxy; - _nm_dbus_signal_connect(priv->modem_proxy, - "PropertyChanged", - G_VARIANT_TYPE("(sv)"), - G_CALLBACK(modem_property_changed), - self); + _nm_dbus_proxy_signal_connect(priv->modem_proxy, + "PropertyChanged", + G_VARIANT_TYPE("(sv)"), + G_CALLBACK(modem_property_changed), + self); g_dbus_proxy_call(priv->modem_proxy, "GetProperties", @@ -1196,7 +1596,17 @@ modem_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) static void nm_modem_ofono_init(NMModemOfono *self) -{} +{ + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + + priv->modem_proxy_cancellable = g_cancellable_new(); + priv->connections = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_object_unref); + priv->contexts = g_hash_table_new_full(nm_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) ofono_context_data_free); + priv->settings = g_object_ref(NM_SETTINGS_GET); +} static void constructed(GObject *object) @@ -1204,8 +1614,6 @@ constructed(GObject *object) NMModemOfono *self = NM_MODEM_OFONO(object); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); - priv->modem_proxy_cancellable = g_cancellable_new(); - g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, @@ -1257,7 +1665,7 @@ dispose(GObject *object) nm_clear_g_cancellable(&priv->modem_proxy_cancellable); nm_clear_g_cancellable(&priv->connman_proxy_cancellable); - nm_clear_g_cancellable(&priv->context_proxy_cancellable); + nm_clear_g_cancellable(&priv->connect_cancellable); nm_clear_g_cancellable(&priv->sim_proxy_cancellable); if (priv->connect_properties) { @@ -1265,6 +1673,16 @@ dispose(GObject *object) priv->connect_properties = NULL; } + if (priv->connections) { + g_hash_table_destroy(priv->connections); + priv->connections = NULL; + } + + if (priv->contexts) { + g_hash_table_destroy(priv->contexts); + priv->contexts = NULL; + } + nm_clear_l3cd(&priv->l3cd_4); if (priv->modem_proxy) { @@ -1277,19 +1695,21 @@ dispose(GObject *object) g_clear_object(&priv->connman_proxy); } - if (priv->context_proxy) { - g_signal_handlers_disconnect_by_data(priv->context_proxy, self); - g_clear_object(&priv->context_proxy); - } - if (priv->sim_proxy) { g_signal_handlers_disconnect_by_data(priv->sim_proxy, self); g_clear_object(&priv->sim_proxy); } + if (priv->settings) { + g_signal_handlers_disconnect_by_data(priv->settings, self); + g_clear_object(&priv->settings); + } + g_free(priv->imsi); priv->imsi = NULL; + nm_clear_g_source_inst(&priv->deferred_connection_timeout_source); + G_OBJECT_CLASS(nm_modem_ofono_parent_class)->dispose(object); } diff --git a/src/core/devices/wwan/nm-modem-ofono.h b/src/core/devices/wwan/nm-modem-ofono.h index ebe21023..02ba49ad 100644 --- a/src/core/devices/wwan/nm-modem-ofono.h +++ b/src/core/devices/wwan/nm-modem-ofono.h @@ -8,8 +8,9 @@ #include "nm-modem.h" -#define NM_TYPE_MODEM_OFONO (nm_modem_ofono_get_type()) -#define NM_MODEM_OFONO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MODEM_OFONO, NMModemOfono)) +#define NM_TYPE_MODEM_OFONO (nm_modem_ofono_get_type()) +#define NM_MODEM_OFONO(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MODEM_OFONO, NMModemOfono)) #define NM_IS_MODEM_OFONO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_MODEM_OFONO)) #define NM_MODEM_OFONO_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_MODEM_OFONO, NMModemOfonoClass)) diff --git a/src/core/devices/wwan/nm-modem.c b/src/core/devices/wwan/nm-modem.c index a5ee8ce8..0159d351 100644 --- a/src/core/devices/wwan/nm-modem.c +++ b/src/core/devices/wwan/nm-modem.c @@ -353,6 +353,12 @@ nm_modem_set_mm_enabled(NMModem *self, gboolean enabled) NMModemPrivate *priv = NM_MODEM_GET_PRIVATE(self); NMModemState prev_state = priv->state; + /* Not all modem classes support set_mm_enabled */ + if (!NM_MODEM_GET_CLASS(self)->set_mm_enabled) { + _LOGD("cannot enable modem: not implemented"); + return; + } + if (enabled && priv->state >= NM_MODEM_STATE_ENABLING) { _LOGD("cannot enable modem: already enabled"); return; @@ -375,9 +381,7 @@ nm_modem_set_mm_enabled(NMModem *self, gboolean enabled) return; } - /* Not all modem classes support set_mm_enabled */ - if (NM_MODEM_GET_CLASS(self)->set_mm_enabled) - NM_MODEM_GET_CLASS(self)->set_mm_enabled(self, enabled); + NM_MODEM_GET_CLASS(self)->set_mm_enabled(self, enabled); /* Pre-empt the state change signal */ nm_modem_set_state(self, diff --git a/src/core/devices/wwan/nm-modem.h b/src/core/devices/wwan/nm-modem.h index ca541073..ec001102 100644 --- a/src/core/devices/wwan/nm-modem.h +++ b/src/core/devices/wwan/nm-modem.h @@ -11,7 +11,7 @@ #include "devices/nm-device.h" #define NM_TYPE_MODEM (nm_modem_get_type()) -#define NM_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MODEM, NMModem)) +#define NM_MODEM(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MODEM, NMModem)) #define NM_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_MODEM, NMModemClass)) #define NM_IS_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_MODEM)) #define NM_IS_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_MODEM)) diff --git a/src/core/devices/wwan/nm-wwan-factory.c b/src/core/devices/wwan/nm-wwan-factory.c index 169407c7..1c6d351f 100644 --- a/src/core/devices/wwan/nm-wwan-factory.c +++ b/src/core/devices/wwan/nm-wwan-factory.c @@ -18,7 +18,7 @@ #define NM_TYPE_WWAN_FACTORY (nm_wwan_factory_get_type()) #define NM_WWAN_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WWAN_FACTORY, NMWwanFactory)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WWAN_FACTORY, NMWwanFactory)) #define NM_WWAN_FACTORY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WWAN_FACTORY, NMWwanFactoryClass)) #define NM_IS_WWAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WWAN_FACTORY)) diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c index 805b42d2..600cb930 100644 --- a/src/core/dhcp/nm-dhcp-client.c +++ b/src/core/dhcp/nm-dhcp-client.c @@ -84,6 +84,7 @@ typedef struct _NMDhcpClientPrivate { * and is set from l3cd_next. */ const NML3ConfigData *l3cd_curr; + GSource *previous_lease_timeout_source; GSource *no_lease_timeout_source; GSource *watch_source; GBytes *effective_client_id; @@ -269,6 +270,12 @@ nm_dhcp_client_create_options_dict(NMDhcpClient *self, gboolean static_keys) return options; } +const NML3ConfigData * +nm_dhcp_client_get_lease(NMDhcpClient *self) +{ + return NM_DHCP_CLIENT_GET_PRIVATE(self)->l3cd_curr; +} + /*****************************************************************************/ gboolean @@ -301,11 +308,16 @@ nm_dhcp_client_set_effective_client_id(NMDhcpClient *self, GBytes *client_id) /*****************************************************************************/ static void -_emit_notify(NMDhcpClient *self, const NMDhcpClientNotifyData *notify_data) +_emit_notify_data(NMDhcpClient *self, const NMDhcpClientNotifyData *notify_data) { g_signal_emit(G_OBJECT(self), signals[SIGNAL_NOTIFY], 0, notify_data); } +#define _emit_notify(self, _notify_type, ...) \ + _emit_notify_data( \ + (self), \ + &((const NMDhcpClientNotifyData){.notify_type = (_notify_type), __VA_ARGS__})) + /*****************************************************************************/ static void @@ -392,12 +404,7 @@ _no_lease_timeout(gpointer user_data) NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); nm_clear_g_source_inst(&priv->no_lease_timeout_source); - - _emit_notify(self, - &((NMDhcpClientNotifyData){ - .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT, - })); - + _emit_notify(self, NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT); return G_SOURCE_CONTINUE; } @@ -473,7 +480,7 @@ _acd_complete_on_idle_cb(gpointer user_data) } #define _acd_reglist_data_get(priv, idx) \ - nm_g_array_index_p((priv)->v4.acd.reglist, AcdRegListData, (idx)) + (&nm_g_array_index((priv)->v4.acd.reglist, AcdRegListData, (idx))) static guint _acd_reglist_data_find(NMDhcpClientPrivate *priv, in_addr_t addr_needle) @@ -503,10 +510,10 @@ _acd_reglist_data_remove(NMDhcpClient *self, guint idx, gboolean do_log) reglist_data = _acd_reglist_data_get(priv, idx); if (do_log) { - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; _LOGD("acd: drop check for address %s (l3cd " NM_HASH_OBFUSCATE_PTR_FMT ")", - _nm_utils_inet4_ntop(reglist_data->addr, sbuf_addr), + nm_inet4_ntop(reglist_data->addr, sbuf_addr), NM_HASH_OBFUSCATE_PTR(reglist_data->l3cd)); } @@ -585,7 +592,7 @@ static void _acd_check_lease(NMDhcpClient *self, NMOptionBool *out_acd_state) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; in_addr_t addr; gboolean addr_changed = FALSE; guint idx; @@ -624,7 +631,7 @@ _acd_check_lease(NMDhcpClient *self, NMOptionBool *out_acd_state) _LOGD("acd: %s check for address %s (timeout %u msec, l3cd " NM_HASH_OBFUSCATE_PTR_FMT ")", addr_changed ? "add" : "update", - _nm_utils_inet4_ntop(addr, sbuf_addr), + nm_inet4_ntop(addr, sbuf_addr), priv->config.v4.acd_timeout_msec, NM_HASH_OBFUSCATE_PTR(priv->l3cd_next)); @@ -711,7 +718,7 @@ gboolean _nm_dhcp_client_accept_offer(NMDhcpClient *self, gconstpointer p_yiaddr) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; NMIPAddr yiaddr; const NML3AcdAddrInfo *acd_info; @@ -744,7 +751,7 @@ _nm_dhcp_client_accept_offer(NMDhcpClient *self, gconstpointer p_yiaddr) return TRUE; _LOGD("offered lease rejected: address %s failed ACD check", - _nm_utils_inet4_ntop(yiaddr.addr4, sbuf_addr)); + nm_inet4_ntop(yiaddr.addr4, sbuf_addr)); return FALSE; } @@ -863,6 +870,9 @@ _nm_dhcp_client_notify(NMDhcpClient *self, return; } + if (priv->l3cd_next) + nm_clear_g_source_inst(&priv->previous_lease_timeout_source); + nm_l3_config_data_reset(&priv->l3cd_curr, priv->l3cd_next); if (client_event_type == NM_DHCP_CLIENT_EVENT_TYPE_BOUND && priv->l3cd_curr @@ -883,18 +893,12 @@ _nm_dhcp_client_notify(NMDhcpClient *self, l3_cfg_notify_check_connected(self); - { - const NMDhcpClientNotifyData notify_data = { - .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE, - .lease_update = - { - .l3cd = priv->l3cd_curr, - .accepted = !priv->l3cfg_notify.wait_dhcp_commit, - }, - }; - - _emit_notify(self, ¬ify_data); - } + _emit_notify(self, + NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE, + .lease_update = { + .l3cd = priv->l3cd_curr, + .accepted = !priv->l3cfg_notify.wait_dhcp_commit, + }); } static void @@ -1010,12 +1014,10 @@ ipv6_lladdr_timeout(gpointer user_data) nm_clear_g_source_inst(&priv->v6.lladdr_timeout_source); - _emit_notify( - self, - &((NMDhcpClientNotifyData){ - .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, - .it_looks_bad.reason = "timeout reached while waiting for an IPv6 link-local address", - })); + _emit_notify(self, + NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, + .it_looks_bad.reason = + "timeout reached while waiting for an IPv6 link-local address"); return G_SOURCE_CONTINUE; } @@ -1027,12 +1029,9 @@ ipv6_dad_timeout(gpointer user_data) nm_clear_g_source_inst(&priv->v6.dad_timeout_source); - _emit_notify( - self, - &((NMDhcpClientNotifyData){ - .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, - .it_looks_bad.reason = "timeout reached while waiting for IPv6 DAD to complete", - })); + _emit_notify(self, + NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, + .it_looks_bad.reason = "timeout reached while waiting for IPv6 DAD to complete"); return G_SOURCE_CONTINUE; } @@ -1068,35 +1067,33 @@ ipv6_lladdr_find(NMDhcpClient *self) static void ipv6_tentative_addr_check(NMDhcpClient *self, GPtrArray **tentative, - GPtrArray **missing, + GPtrArray **dadfailed, const NMPlatformIP6Address **valid) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); NMDedupMultiIter iter; const NMPlatformIP6Address *addr; - NML3Cfg *l3cfg = priv->config.l3cfg; + NML3Cfg *l3cfg = priv->config.l3cfg; + NMPlatform *platform = nm_l3cfg_get_platform(l3cfg); + int ifindex = nm_l3cfg_get_ifindex(l3cfg); /* For each address in the lease, check whether it's tentative - * in platform. */ + * or dad-failed in platform. */ nm_l3_config_data_iter_ip6_address_for_each (&iter, priv->l3cd_curr, &addr) { const NMPlatformIP6Address *pladdr; - NMPObject needle; - - nmp_object_stackinit_id_ip6_address(&needle, nm_l3cfg_get_ifindex(l3cfg), &addr->address); - pladdr = NMP_OBJECT_CAST_IP6_ADDRESS(nm_platform_lookup_obj(nm_l3cfg_get_platform(l3cfg), - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - &needle)); - if (!pladdr) { - /* address removed: we assume that's because DAD failed */ - if (missing) { - if (!*missing) - *missing = g_ptr_array_new(); - g_ptr_array_add(*missing, (gpointer) addr); + + pladdr = nm_platform_ip6_address_get(platform, ifindex, &addr->address); + if ((pladdr && NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_DADFAILED)) + || (!pladdr && nm_platform_ip6_dadfailed_check(platform, ifindex, &addr->address))) { + if (dadfailed) { + if (!*dadfailed) + *dadfailed = g_ptr_array_new(); + g_ptr_array_add(*dadfailed, (gpointer) addr); } continue; } - if (NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_TENTATIVE) + if (pladdr && NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_TENTATIVE) && !NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_OPTIMISTIC)) { if (tentative) { if (!*tentative) @@ -1105,6 +1102,9 @@ ipv6_tentative_addr_check(NMDhcpClient *self, } } + /* Here the address is non-tentative or it was removed externally by the user. + * In both cases it has completed DAD. + */ NM_SET_OUT(valid, addr); } } @@ -1113,7 +1113,7 @@ static void l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcpClient *self) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; nm_assert(l3cfg == priv->config.l3cfg); @@ -1133,10 +1133,8 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp if (!NM_DHCP_CLIENT_GET_CLASS(self)->ip6_start(self, &addr->address, &error)) { _emit_notify(self, - &((NMDhcpClientNotifyData){ - .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, - .it_looks_bad.reason = error->message, - })); + NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, + .it_looks_bad.reason = error->message); } } } @@ -1144,13 +1142,13 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE && priv->l3cfg_notify.wait_ipv6_dad) { gs_unref_ptrarray GPtrArray *tentative = NULL; - gs_unref_ptrarray GPtrArray *missing = NULL; + gs_unref_ptrarray GPtrArray *dadfailed = NULL; const NMPlatformIP6Address *valid = NULL; char str[NM_UTILS_TO_STRING_BUFFER_SIZE]; guint i; gs_free_error GError *error = NULL; - ipv6_tentative_addr_check(self, &tentative, &missing, &valid); + ipv6_tentative_addr_check(self, &tentative, &dadfailed, &valid); if (tentative) { for (i = 0; i < tentative->len; i++) { _LOGD("still waiting DAD for address: %s", @@ -1163,10 +1161,10 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp nm_clear_g_source_inst(&priv->v6.dad_timeout_source); l3_cfg_notify_check_connected(self); - if (missing) { - for (i = 0; i < missing->len; i++) { + if (dadfailed) { + for (i = 0; i < dadfailed->len; i++) { _LOGE("DAD failed for address: %s", - nm_platform_ip6_address_to_string(missing->pdata[i], str, sizeof(str))); + nm_platform_ip6_address_to_string(dadfailed->pdata[i], str, sizeof(str))); } } @@ -1176,22 +1174,19 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp if (_dhcp_client_accept(self, priv->l3cd_curr, &error)) { _emit_notify(self, - &((NMDhcpClientNotifyData){ - .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE, - .lease_update = { - .l3cd = priv->l3cd_curr, - .accepted = TRUE, - }})); + NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE, + .lease_update = { + .l3cd = priv->l3cd_curr, + .accepted = TRUE, + }); } else { gs_free char *reason = g_strdup_printf("error accepting lease: %s", error->message); _LOGD("accept failed: %s", error->message); _emit_notify(self, - &((NMDhcpClientNotifyData){ - .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, - .it_looks_bad.reason = reason, - })); + NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, + .it_looks_bad.reason = reason); } } else { _LOGD("decline the lease"); @@ -1266,20 +1261,17 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp _LOGD("accept failed: %s", error->message); _emit_notify(self, - &((NMDhcpClientNotifyData){ - .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, - .it_looks_bad.reason = reason, - })); + NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, + .it_looks_bad.reason = reason, ); goto wait_dhcp_commit_done; } - _emit_notify( - self, - &((NMDhcpClientNotifyData){.notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE, - .lease_update = { - .l3cd = priv->l3cd_curr, - .accepted = TRUE, - }})); + _emit_notify(self, + NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE, + .lease_update = { + .l3cd = priv->l3cd_curr, + .accepted = TRUE, + }); } } wait_dhcp_commit_done: @@ -1318,7 +1310,7 @@ wait_dhcp_commit_done: if (acd_state != NM_OPTION_BOOL_DEFAULT) { _LOGD("acd: acd %s for %s", acd_state ? "ready" : "conflict", - _nm_utils_inet4_ntop(priv->v4.acd.addr, sbuf_addr)); + nm_inet4_ntop(priv->v4.acd.addr, sbuf_addr)); nm_l3cfg_commit_type_clear(priv->config.l3cfg, &priv->v4.acd.l3cfg_commit_handle); priv->v4.acd.state = acd_state; priv->v4.acd.done_source = nm_g_idle_add_source(_acd_complete_on_idle_cb, self); @@ -1327,6 +1319,19 @@ wait_dhcp_commit_done: } } +static gboolean +_previous_lease_timeout_cb(gpointer user_data) +{ + NMDhcpClient *self = user_data; + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + nm_clear_g_source_inst(&priv->previous_lease_timeout_source); + + _nm_dhcp_client_notify(self, NM_DHCP_CLIENT_EVENT_TYPE_TIMEOUT, NULL); + + return G_SOURCE_CONTINUE; +} + gboolean nm_dhcp_client_start(NMDhcpClient *self, GError **error) { @@ -1358,6 +1363,23 @@ nm_dhcp_client_start(NMDhcpClient *self, GError **error) _no_lease_timeout_schedule(self); + if (priv->config.previous_lease) { + /* We got passed a previous lease (during a reapply). For a few seconds, we + * will pretend that this is current lease. */ + priv->l3cd_curr = g_steal_pointer(&priv->config.previous_lease); + + /* Schedule a timeout for when we give up using this lease. Note + * that then we will emit a NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE event + * and the lease is gone. Note that NMDevice ignores that and will + * keep using the lease. + * + * At the same time, we have _no_lease_timeout_schedule() ticking, when + * that expires, we will emit a NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT + * signal, which causes NMDevice to clear the lease. */ + priv->previous_lease_timeout_source = + nm_g_timeout_add_seconds_source(15, _previous_lease_timeout_cb, self); + } + if (IS_IPv4) return NM_DHCP_CLIENT_GET_CLASS(self)->ip4_start(self, error); @@ -1438,6 +1460,8 @@ nm_dhcp_client_stop(NMDhcpClient *self, gboolean release) if (priv->is_stopped) return; + nm_clear_g_source_inst(&priv->previous_lease_timeout_source); + priv->is_stopped = TRUE; if (priv->invocation) { @@ -1597,15 +1621,11 @@ maybe_add_option(NMDhcpClient *self, GHashTable *hash, const char *key, GVariant void nm_dhcp_client_emit_ipv6_prefix_delegated(NMDhcpClient *self, const NMPlatformIP6Address *prefix) { - const NMDhcpClientNotifyData notify_data = { - .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED, - .prefix_delegated = - { - .prefix = prefix, - }, - }; - - _emit_notify(self, ¬ify_data); + _emit_notify(self, + NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED, + .prefix_delegated = { + .prefix = prefix, + }); } gboolean @@ -1621,7 +1641,7 @@ nm_dhcp_client_handle_event(gpointer unused, nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; NMDhcpClientEventType client_event_type; NMPlatformIP6Address prefix = { - 0, + 0, }; g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE); @@ -1751,14 +1771,14 @@ nm_dhcp_client_server_id_is_rejected(NMDhcpClient *self, gconstpointer addr) in_addr_t mask; int r_prefix; - if (!nm_utils_parse_inaddr_prefix_bin(AF_INET, - priv->config.reject_servers[i], - NULL, - &r_addr, - &r_prefix)) + if (!nm_inet_parse_with_prefix_bin(AF_INET, + priv->config.reject_servers[i], + NULL, + &r_addr, + &r_prefix)) nm_assert_not_reached(); - mask = _nm_utils_ip4_prefix_to_netmask(r_prefix < 0 ? 32 : r_prefix); + mask = nm_ip4_addr_netmask_from_prefix(r_prefix < 0 ? 32 : r_prefix); if ((addr4 & mask) == (r_addr & mask)) return TRUE; } @@ -1783,6 +1803,8 @@ config_init(NMDhcpClientConfig *config, const NMDhcpClientConfig *src) g_object_ref(config->l3cfg); + nm_l3_config_data_ref_and_seal(config->previous_lease); + nm_g_bytes_ref(config->hwaddr); nm_g_bytes_ref(config->bcast_hwaddr); nm_g_bytes_ref(config->vendor_class_identifier); @@ -1843,6 +1865,8 @@ config_clear(NMDhcpClientConfig *config) { g_object_unref(config->l3cfg); + nm_clear_l3cd(&config->previous_lease); + nm_clear_pointer(&config->hwaddr, g_bytes_unref); nm_clear_pointer(&config->bcast_hwaddr, g_bytes_unref); nm_clear_pointer(&config->vendor_class_identifier, g_bytes_unref); @@ -1919,6 +1943,7 @@ dispose(GObject *object) watch_cleanup(self); + nm_clear_g_source_inst(&priv->previous_lease_timeout_source); nm_clear_g_source_inst(&priv->no_lease_timeout_source); if (!NM_IS_IPv4(priv->config.addr_family)) { diff --git a/src/core/dhcp/nm-dhcp-client.h b/src/core/dhcp/nm-dhcp-client.h index 6f403b61..f43770a0 100644 --- a/src/core/dhcp/nm-dhcp-client.h +++ b/src/core/dhcp/nm-dhcp-client.h @@ -14,7 +14,8 @@ #define NM_DHCP_TIMEOUT_INFINITY ((guint32) G_MAXINT32) #define NM_TYPE_DHCP_CLIENT (nm_dhcp_client_get_type()) -#define NM_DHCP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClient)) +#define NM_DHCP_CLIENT(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClient)) #define NM_DHCP_CLIENT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass)) #define NM_IS_DHCP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_CLIENT)) @@ -91,9 +92,8 @@ typedef struct { * NMDhcpClient is supposed to run. */ NML3Cfg *l3cfg; - /* FIXME(l3cfg:dhcp:previous-lease): most parameters of NMDhcpClient are immutable, - * so to change them (during reapply), we need to create and start - * a new NMDhcpClient instance. + /* Most parameters of NMDhcpClient are immutable, so to change them (during + * reapply), we need to create and start a new NMDhcpClient instance. * * However, while the restart happens, we want to stick to the previous * lease (if any). Allow the caller to provide such a previous lease, @@ -120,7 +120,7 @@ typedef struct { const char *uuid; /* Set to reduce the number of broadcast packets when the - * anycast hardware address of the DHCP service is known. */ + * anycast hardware address of the DHCP service is known. */ const char *anycast_address; /* The hostname or FQDN to send. */ @@ -224,18 +224,7 @@ const NMDhcpClientConfig *nm_dhcp_client_get_config(NMDhcpClient *self); pid_t nm_dhcp_client_get_pid(NMDhcpClient *self); -static inline const NML3ConfigData * -nm_dhcp_client_get_lease(NMDhcpClient *self) -{ - /* FIXME(l3cfg:dhcp:previous-lease): this function returns the currently - * valid, exposed lease. - * - * Note that NMDhcpClient should accept as construct argument a *previous* lease, - * and (if that lease is still valid), pretend that it's good to use. The point is - * so that during reapply we keep using the current address, until a new lease - * was received. */ - return NULL; -} +const NML3ConfigData *nm_dhcp_client_get_lease(NMDhcpClient *self); void nm_dhcp_client_stop(NMDhcpClient *self, gboolean release); diff --git a/src/core/dhcp/nm-dhcp-dhclient.c b/src/core/dhcp/nm-dhcp-dhclient.c index e4f40d7c..35b2fb2e 100644 --- a/src/core/dhcp/nm-dhcp-dhclient.c +++ b/src/core/dhcp/nm-dhcp-dhclient.c @@ -43,7 +43,7 @@ _addr_family_to_path_part(int addr_family) #define NM_TYPE_DHCP_DHCLIENT (nm_dhcp_dhclient_get_type()) #define NM_DHCP_DHCLIENT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclient)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclient)) #define NM_DHCP_DHCLIENT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclientClass)) #define NM_IS_DHCP_DHCLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_DHCLIENT)) diff --git a/src/core/dhcp/nm-dhcp-dhcpcanon.c b/src/core/dhcp/nm-dhcp-dhcpcanon.c index 87e43fa8..cd42b692 100644 --- a/src/core/dhcp/nm-dhcp-dhcpcanon.c +++ b/src/core/dhcp/nm-dhcp-dhcpcanon.c @@ -18,7 +18,7 @@ #define NM_TYPE_DHCP_DHCPCANON (nm_dhcp_dhcpcanon_get_type()) #define NM_DHCP_DHCPCANON(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCPCANON, NMDhcpDhcpcanon)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCPCANON, NMDhcpDhcpcanon)) #define NM_DHCP_DHCPCANON_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_DHCPCANON, NMDhcpDhcpcanonClass)) #define NM_IS_DHCP_DHCPCANON(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_DHCPCANON)) diff --git a/src/core/dhcp/nm-dhcp-dhcpcd.c b/src/core/dhcp/nm-dhcp-dhcpcd.c index 280d9ce6..8d2f928b 100644 --- a/src/core/dhcp/nm-dhcp-dhcpcd.c +++ b/src/core/dhcp/nm-dhcp-dhcpcd.c @@ -23,7 +23,8 @@ /*****************************************************************************/ #define NM_TYPE_DHCP_DHCPCD (nm_dhcp_dhcpcd_get_type()) -#define NM_DHCP_DHCPCD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCPCD, NMDhcpDhcpcd)) +#define NM_DHCP_DHCPCD(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCPCD, NMDhcpDhcpcd)) #define NM_DHCP_DHCPCD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_DHCPCD, NMDhcpDhcpcdClass)) #define NM_IS_DHCP_DHCPCD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_DHCPCD)) diff --git a/src/core/dhcp/nm-dhcp-listener.h b/src/core/dhcp/nm-dhcp-listener.h index 5f3c952c..0818b371 100644 --- a/src/core/dhcp/nm-dhcp-listener.h +++ b/src/core/dhcp/nm-dhcp-listener.h @@ -8,7 +8,7 @@ #define NM_TYPE_DHCP_LISTENER (nm_dhcp_listener_get_type()) #define NM_DHCP_LISTENER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_LISTENER, NMDhcpListener)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_LISTENER, NMDhcpListener)) #define NM_IS_DHCP_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_LISTENER)) #define NM_DHCP_LISTENER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DHCP_LISTENER, NMDhcpListenerClass)) diff --git a/src/core/dhcp/nm-dhcp-manager.h b/src/core/dhcp/nm-dhcp-manager.h index bbd68236..980a26c9 100644 --- a/src/core/dhcp/nm-dhcp-manager.h +++ b/src/core/dhcp/nm-dhcp-manager.h @@ -12,7 +12,7 @@ #define NM_TYPE_DHCP_MANAGER (nm_dhcp_manager_get_type()) #define NM_DHCP_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_MANAGER, NMDhcpManager)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_MANAGER, NMDhcpManager)) #define NM_DHCP_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_MANAGER, NMDhcpManagerClass)) #define NM_IS_DHCP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_MANAGER)) diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c index df88362e..9cdfd9aa 100644 --- a/src/core/dhcp/nm-dhcp-nettools.c +++ b/src/core/dhcp/nm-dhcp-nettools.c @@ -36,7 +36,7 @@ #define NM_TYPE_DHCP_NETTOOLS (nm_dhcp_nettools_get_type()) #define NM_DHCP_NETTOOLS(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_NETTOOLS, NMDhcpNettools)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_NETTOOLS, NMDhcpNettools)) #define NM_DHCP_NETTOOLS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_NETTOOLS, NMDhcpNettoolsClass)) #define NM_IS_DHCP_NETTOOLS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_NETTOOLS)) @@ -143,12 +143,12 @@ lease_option_consume_route(const uint8_t **datap, if (!nm_dhcp_lease_data_consume_in_addr(&data, &n_data, &dest)) return FALSE; - plen = _nm_utils_ip4_get_default_prefix0(dest); + plen = nm_ip4_addr_get_default_prefix0(dest); if (plen == 0) return FALSE; } - dest = nm_utils_ip4_address_clear_host_address(dest, plen); + dest = nm_ip4_addr_clear_host_address(dest, plen); if (!nm_dhcp_lease_data_consume_in_addr(&data, &n_data, &gateway)) return FALSE; @@ -239,16 +239,16 @@ lease_parse_address(NMDhcpNettools *self /* for logging context only */, r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_SUBNET_MASK, &l_data, &l_data_len); if (r == N_DHCP4_E_UNSET) { - char str1[NM_UTILS_INET_ADDRSTRLEN]; - char str2[NM_UTILS_INET_ADDRSTRLEN]; + char str1[NM_INET_ADDRSTRLEN]; + char str2[NM_INET_ADDRSTRLEN]; /* Some DHCP servers may not set the subnet-mask (issue#1037). * Do the same as the dhclient plugin and use a default. */ - a_plen = _nm_utils_ip4_get_default_prefix(a_address.s_addr); - a_netmask = _nm_utils_ip4_prefix_to_netmask(a_plen); + a_plen = nm_ip4_addr_get_default_prefix(a_address.s_addr); + a_netmask = nm_ip4_addr_netmask_from_prefix(a_plen); _LOGT("missing subnet mask (option 1). Guess %s based on IP address %s", - _nm_utils_inet4_ntop(a_netmask, str1), - _nm_utils_inet4_ntop(a_address.s_addr, str2)); + nm_inet4_ntop(a_netmask, str1), + nm_inet4_ntop(a_address.s_addr, str2)); } else { if (r != 0 || !nm_dhcp_lease_data_parse_in_addr(l_data, @@ -261,8 +261,8 @@ lease_parse_address(NMDhcpNettools *self /* for logging context only */, "could not get netmask from lease"); return FALSE; } - a_plen = _nm_utils_ip4_netmask_to_prefix(a_netmask); - a_netmask = _nm_utils_ip4_prefix_to_netmask(a_plen); + a_plen = nm_ip4_addr_netmask_to_prefix(a_netmask); + a_netmask = nm_ip4_addr_netmask_from_prefix(a_plen); } nm_dhcp_option_add_option_in_addr(options, @@ -335,25 +335,25 @@ lease_parse_address_list(NDhcp4ClientLease *lease, nm_str_buf_reset(sbuf); for (; l_data_len > 0; l_data_len -= 4, l_data += 4) { - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + char addr_str[NM_INET_ADDRSTRLEN]; const in_addr_t addr = unaligned_read_ne32(l_data); nm_str_buf_append_required_delimiter(sbuf, ' '); - nm_str_buf_append(sbuf, _nm_utils_inet4_ntop(addr, addr_str)); + nm_str_buf_append(sbuf, nm_inet4_ntop(addr, addr_str)); switch (option) { case NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER: - if (addr == 0 || nm_utils_ip4_address_is_loopback(addr)) { + if (addr == 0 || nm_ip4_addr_is_loopback(addr)) { /* Skip localhost addresses, like also networkd does. * See https://github.com/systemd/systemd/issues/4524. */ nm_dhcp_lease_log_invalid_option(iface, AF_INET, option, "address %s is ignored", - _nm_utils_inet4_ntop(addr, addr_str)); + nm_inet4_ntop(addr, addr_str)); continue; } - nm_l3_config_data_add_nameserver(l3cd, AF_INET, &addr); + nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET, &addr, NULL); break; case NM_DHCP_OPTION_DHCP4_NIS_SERVERS: nm_l3_config_data_add_nis_server(l3cd, addr); @@ -378,8 +378,8 @@ lease_parse_routes(NDhcp4ClientLease *lease, GHashTable *options, NMStrBuf *sbuf) { - char dest_str[NM_UTILS_INET_ADDRSTRLEN]; - char gateway_str[NM_UTILS_INET_ADDRSTRLEN]; + char dest_str[NM_INET_ADDRSTRLEN]; + char gateway_str[NM_INET_ADDRSTRLEN]; in_addr_t dest; in_addr_t gateway; uint8_t plen; @@ -412,8 +412,8 @@ lease_parse_routes(NDhcp4ClientLease *lease, nm_str_buf_reset(sbuf); while (lease_option_consume_route(&l_data, &l_data_len, TRUE, &dest, &plen, &gateway)) { - _nm_utils_inet4_ntop(dest, dest_str); - _nm_utils_inet4_ntop(gateway, gateway_str); + nm_inet4_ntop(dest, dest_str); + nm_inet4_ntop(gateway, gateway_str); nm_str_buf_append_required_delimiter(sbuf, ' '); nm_str_buf_append_printf(sbuf, "%s/%d %s", dest_str, (int) plen, gateway_str); @@ -454,8 +454,8 @@ lease_parse_routes(NDhcp4ClientLease *lease, nm_str_buf_reset(sbuf); while (lease_option_consume_route(&l_data, &l_data_len, FALSE, &dest, &plen, &gateway)) { - _nm_utils_inet4_ntop(dest, dest_str); - _nm_utils_inet4_ntop(gateway, gateway_str); + nm_inet4_ntop(dest, dest_str); + nm_inet4_ntop(gateway, gateway_str); nm_str_buf_append_required_delimiter(sbuf, ' '); nm_str_buf_append_printf(sbuf, "%s/%d %s", dest_str, (int) plen, gateway_str); @@ -501,7 +501,7 @@ lease_parse_routes(NDhcp4ClientLease *lease, while (nm_dhcp_lease_data_consume_in_addr(&l_data, &l_data_len, &gateway)) { nm_str_buf_append_required_delimiter(sbuf, ' '); - nm_str_buf_append(sbuf, _nm_utils_inet4_ntop(gateway, gateway_str)); + nm_str_buf_append(sbuf, nm_inet4_ntop(gateway, gateway_str)); if (gateway == 0) { /* silently skip 0.0.0.0 */ @@ -868,7 +868,7 @@ lease_save(NMDhcpNettools *self, NDhcp4ClientLease *lease, const char *lease_fil { struct in_addr a_address; nm_auto_str_buf NMStrBuf sbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_104, FALSE); - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + char addr_str[NM_INET_ADDRSTRLEN]; gs_free_error GError *error = NULL; nm_assert(lease); @@ -879,9 +879,7 @@ lease_save(NMDhcpNettools *self, NDhcp4ClientLease *lease, const char *lease_fil return; nm_str_buf_append(&sbuf, "# This is private data. Do not parse.\n"); - nm_str_buf_append_printf(&sbuf, - "ADDRESS=%s\n", - _nm_utils_inet4_ntop(a_address.s_addr, addr_str)); + nm_str_buf_append_printf(&sbuf, "ADDRESS=%s\n", nm_inet4_ntop(a_address.s_addr, addr_str)); if (!g_file_set_contents(lease_file, nm_str_buf_get_str_unsafe(&sbuf), sbuf.len, &error)) _LOGW("error saving lease to %s: %s", lease_file, error->message); @@ -966,7 +964,7 @@ dhcp4_event_handle(NMDhcpNettools *self, NDhcp4ClientEvent *event) if (nm_dhcp_client_server_id_is_rejected(NM_DHCP_CLIENT(self), &server_id)) { _LOGD("server-id %s is in the reject-list, ignoring", - nm_utils_inet_ntop(AF_INET, &server_id, addr_str)); + nm_inet_ntop(AF_INET, &server_id, addr_str)); return; } @@ -976,8 +974,8 @@ dhcp4_event_handle(NMDhcpNettools *self, NDhcp4ClientEvent *event) } _LOGT("selecting offered lease from %s for %s", - _nm_utils_inet4_ntop(server_id.s_addr, addr_str), - _nm_utils_inet4_ntop(yiaddr.s_addr, addr_str2)); + nm_inet4_ntop(server_id.s_addr, addr_str), + nm_inet4_ntop(yiaddr.s_addr, addr_str2)); r = n_dhcp4_client_lease_select(event->offer.lease); @@ -1340,7 +1338,7 @@ ip4_start(NMDhcpClient *client, GError **error) NULL); nm_parse_env_file(contents, "ADDRESS", &s_addr); if (s_addr) - nm_utils_parse_inaddr_bin(AF_INET, s_addr, NULL, &last_addr); + nm_inet_parse_bin(AF_INET, s_addr, NULL, &last_addr); } if (last_addr.s_addr) { diff --git a/src/core/dhcp/nm-dhcp-options.c b/src/core/dhcp/nm-dhcp-options.c index a5bb06d1..d95fe016 100644 --- a/src/core/dhcp/nm-dhcp-options.c +++ b/src/core/dhcp/nm-dhcp-options.c @@ -438,9 +438,9 @@ nm_dhcp_option_add_option_in_addr(GHashTable *options, guint option, in_addr_t value) { - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; - nm_dhcp_option_add_option(options, addr_family, option, _nm_utils_inet4_ntop(value, sbuf)); + nm_dhcp_option_add_option(options, addr_family, option, nm_inet4_ntop(value, sbuf)); } void diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c index 7ce15d30..10990822 100644 --- a/src/core/dhcp/nm-dhcp-systemd.c +++ b/src/core/dhcp/nm-dhcp-systemd.c @@ -30,7 +30,7 @@ #define NM_TYPE_DHCP_SYSTEMD (nm_dhcp_systemd_get_type()) #define NM_DHCP_SYSTEMD(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_SYSTEMD, NMDhcpSystemd)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_SYSTEMD, NMDhcpSystemd)) #define NM_DHCP_SYSTEMD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_SYSTEMD, NMDhcpSystemdClass)) #define NM_IS_DHCP_SYSTEMD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_SYSTEMD)) @@ -74,7 +74,7 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro gs_unref_hashtable GHashTable *options = NULL; struct in6_addr tmp_addr; const struct in6_addr *dns; - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + char addr_str[NM_INET_ADDRSTRLEN]; char **domains; char **ntp_fqdns; const struct in6_addr *ntp_addrs; @@ -107,7 +107,7 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro nm_l3_config_data_add_address_6(l3cd, &address); - _nm_utils_inet6_ntop(&tmp_addr, addr_str); + nm_inet6_ntop(&tmp_addr, addr_str); g_string_append(nm_gstring_add_space_delimiter(str), addr_str); has_any_addresses = TRUE; @@ -133,9 +133,9 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro if (num > 0) { nm_gstring_prepare(&str); for (i = 0; i < num; i++) { - _nm_utils_inet6_ntop(&dns[i], addr_str); + nm_inet6_ntop(&dns[i], addr_str); g_string_append(nm_gstring_add_space_delimiter(str), addr_str); - nm_l3_config_data_add_nameserver(l3cd, AF_INET6, &dns[i]); + nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET6, &dns[i], NULL); } nm_dhcp_option_add_option(options, AF_INET6, NM_DHCP_OPTION_DHCP6_DNS_SERVERS, str->str); } @@ -170,7 +170,7 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro num = sd_dhcp6_lease_get_ntp_addrs(lease, &ntp_addrs); if (num > 0) { for (i = 0; i < num; i++) { - _nm_utils_inet6_ntop(&ntp_addrs[i], addr_str); + nm_inet6_ntop(&ntp_addrs[i], addr_str); g_string_append(nm_gstring_add_space_delimiter(str), addr_str); } } diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c index 1bffb3c4..ca1c0482 100644 --- a/src/core/dhcp/nm-dhcp-utils.c +++ b/src/core/dhcp/nm-dhcp-utils.c @@ -94,7 +94,7 @@ ip4_process_dhcpcd_rfc3442_routes(const char *iface, l3cd, &((const NMPlatformIP4Route){ .rt_source = NM_IP_CONFIG_SOURCE_DHCP, - .network = nm_utils_ip4_address_clear_host_address(rt_addr, rt_cidr), + .network = nm_ip4_addr_clear_host_address(rt_addr, rt_cidr), .plen = rt_cidr, .gateway = rt_route, .pref_src = address, @@ -139,7 +139,7 @@ process_dhclient_rfc3442_route(const char *const **p_octets, NMPlatformIP4Route str_addr = g_strjoin(".", addr[0], addr[1], addr[2], addr[3], NULL); if (inet_pton(AF_INET, str_addr, &tmp_addr) <= 0) return FALSE; - v_network = nm_utils_ip4_address_clear_host_address(tmp_addr, v_plen); + v_network = nm_ip4_addr_clear_host_address(tmp_addr, v_plen); } next_hop = g_strjoin(".", o[0], o[1], o[2], o[3], NULL); @@ -203,9 +203,9 @@ ip4_process_dhclient_rfc3442_routes(const char *iface, _LOG2I(LOGD_DHCP4, iface, " classless static route %s/%d gw %s", - _nm_utils_inet4_ntop(route.network, b1), + nm_inet4_ntop(route.network, b1), route.plen, - _nm_utils_inet4_ntop(route.gateway, b2)); + nm_inet4_ntop(route.gateway, b2)); } } @@ -324,8 +324,8 @@ process_classful_routes(const char *iface, * The Static Routes option (option 33) does not provide a subnet mask * for each route - it is assumed that the subnet mask is implicit in * whatever network number is specified in each route entry */ - route.plen = _nm_utils_ip4_get_default_prefix(rt_addr); - if (rt_addr & ~_nm_utils_ip4_prefix_to_netmask(route.plen)) { + route.plen = nm_ip4_addr_get_default_prefix(rt_addr); + if (rt_addr & ~nm_ip4_addr_netmask_from_prefix(route.plen)) { /* RFC 943: target not "this network"; using host routing */ route.plen = 32; } @@ -337,7 +337,7 @@ process_classful_routes(const char *iface, route.metric_any = TRUE; route.metric = 0; - route.network = nm_utils_ip4_address_clear_host_address(route.network, route.plen); + route.network = nm_ip4_addr_clear_host_address(route.network, route.plen); nm_l3_config_data_add_route_4(l3cd, &route); @@ -400,7 +400,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, gboolean gateway_has = FALSE; guint32 gateway = 0; guint8 plen = 0; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; guint32 now; g_return_val_if_fail(options != NULL, NULL); @@ -414,7 +414,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, }; str = g_hash_table_lookup(options, "ip_address"); - if (!str || !nm_utils_parse_inaddr_bin(AF_INET, str, NULL, &addr)) + if (!str || !nm_inet_parse_bin(AF_INET, str, NULL, &addr)) return NULL; if (addr == INADDR_ANY) return NULL; @@ -423,11 +423,11 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, str = g_hash_table_lookup(options, "subnet_mask"); if (str && (inet_pton(AF_INET, str, &tmp_addr) > 0)) { - plen = _nm_utils_ip4_netmask_to_prefix(tmp_addr); + plen = nm_ip4_addr_netmask_to_prefix(tmp_addr); _LOG2I(LOGD_DHCP4, iface, " plen %d (%s)", plen, str); } else { /* Get default netmask for the IP according to appropriate class. */ - plen = _nm_utils_ip4_get_default_prefix(addr); + plen = nm_ip4_addr_get_default_prefix(addr); _LOG2I(LOGD_DHCP4, iface, " plen %d (default)", plen); } @@ -440,7 +440,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, process_classful_routes(iface, options, l3cd, address.address); if (gateway) { - _LOG2I(LOGD_DHCP4, iface, " gateway %s", _nm_utils_inet4_ntop(gateway, sbuf)); + _LOG2I(LOGD_DHCP4, iface, " gateway %s", nm_inet4_ntop(gateway, sbuf)); gateway_has = TRUE; } else { /* If the gateway wasn't provided as a classless static route with a @@ -499,7 +499,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, for (s = dns; dns && *s; s++) { if (inet_pton(AF_INET, *s, &tmp_addr) > 0) { if (tmp_addr) { - nm_l3_config_data_add_nameserver(l3cd, AF_INET, &tmp_addr); + nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET, &tmp_addr, NULL); _LOG2I(LOGD_DHCP4, iface, " nameserver '%s'", *s); } } else @@ -593,7 +593,7 @@ nm_dhcp_utils_ip6_prefix_from_options(GHashTable *options) { gs_strfreev char **split_addr = NULL; NMPlatformIP6Address address = { - 0, + 0, }; struct in6_addr tmp_addr; char *str = NULL; @@ -704,7 +704,7 @@ nm_dhcp_utils_ip6_config_from_options(NMDedupMultiIndex *multi_idx, for (s = dns; dns && *s; s++) { if (inet_pton(AF_INET6, *s, &tmp_addr) > 0) { if (!IN6_IS_ADDR_UNSPECIFIED(&tmp_addr)) { - nm_l3_config_data_add_nameserver(l3cd, AF_INET6, &tmp_addr); + nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET6, &tmp_addr, NULL); _LOG2I(LOGD_DHCP6, iface, " nameserver '%s'", *s); } } else diff --git a/src/core/dhcp/tests/test-dhcp-utils.c b/src/core/dhcp/tests/test-dhcp-utils.c index 7597f516..1c6d6302 100644 --- a/src/core/dhcp/tests/test-dhcp-utils.c +++ b/src/core/dhcp/tests/test-dhcp-utils.c @@ -88,7 +88,6 @@ test_generic_options(void) const char *expected_route2_dest = "100.99.88.56"; const char *expected_route2_gw = "10.1.1.1"; const char *const *strarr; - const in_addr_t *ia_arr; guint u; options = fill_table(generic_options, NULL); @@ -115,10 +114,10 @@ test_generic_options(void) g_assert_cmpstr(strarr[0], ==, expected_search1); g_assert_cmpstr(strarr[1], ==, expected_search2); - ia_arr = nm_l3_config_data_get_nameservers(l3cd, AF_INET, &u); + strarr = nm_l3_config_data_get_nameservers(l3cd, AF_INET, &u); g_assert_cmpint(u, ==, 2); - nmtst_assert_ip4_address(ia_arr[0], expected_dns1); - nmtst_assert_ip4_address(ia_arr[1], expected_dns2); + g_assert_cmpstr(strarr[0], ==, expected_dns1); + g_assert_cmpstr(strarr[1], ==, expected_dns2); g_assert_cmpint(nm_l3_config_data_get_num_routes(l3cd, AF_INET), ==, 3); @@ -285,7 +284,7 @@ test_classless_static_routes_1(void) const char *expected_route2_dest = "10.0.0.0"; const char *expected_route2_gw = "10.17.66.41"; static const Option data[] = { - /* dhclient custom format */ + /* dhclient custom format */ {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 8 10 10 17 66 41"}, {NULL, NULL}}; @@ -310,7 +309,7 @@ test_classless_static_routes_2(void) const char *expected_route2_dest = "10.0.0.0"; const char *expected_route2_gw = "10.17.66.41"; static const Option data[] = { - /* dhcpcd format */ + /* dhcpcd format */ {"classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41"}, {NULL, NULL}}; @@ -336,7 +335,7 @@ test_fedora_dhclient_classless_static_routes(void) const char *expected_route2_gw = "10.34.255.6"; const char *expected_gateway = "192.168.0.113"; static const Option data[] = { - /* Fedora dhclient format */ + /* Fedora dhclient format */ {"classless_static_routes", "0 192.168.0.113 25.129.210.177.132 192.168.0.113 7.2 10.34.255.6"}, {NULL, NULL}}; @@ -362,7 +361,7 @@ test_dhclient_invalid_classless_routes_1(void) const char *expected_route1_dest = "192.168.10.0"; const char *expected_route1_gw = "192.168.1.1"; static const Option data[] = { - /* dhclient format */ + /* dhclient format */ {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 45 10 17 66 41"}, {NULL, NULL}}; @@ -389,7 +388,7 @@ test_dhcpcd_invalid_classless_routes_1(void) const char *expected_route2_dest = "100.99.88.56"; const char *expected_route2_gw = "10.1.1.1"; static const Option data[] = { - /* dhcpcd format */ + /* dhcpcd format */ {"classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.adfadf/44 10.17.66.41"}, {NULL, NULL}}; @@ -419,8 +418,8 @@ test_dhclient_invalid_classless_routes_2(void) const char *expected_route2_dest = "100.99.88.56"; const char *expected_route2_gw = "10.1.1.1"; static const Option data[] = { - {"rfc3442_classless_static_routes", "45 10 17 66 41 24 192 168 10 192 168 1 1"}, - {NULL, NULL}}; + {"rfc3442_classless_static_routes", "45 10 17 66 41 24 192 168 10 192 168 1 1"}, + {NULL, NULL}}; options = fill_table(generic_options, NULL); options = fill_table(data, options); @@ -448,8 +447,8 @@ test_dhcpcd_invalid_classless_routes_2(void) const char *expected_route2_dest = "100.99.88.56"; const char *expected_route2_gw = "10.1.1.1"; static const Option data[] = { - {"classless_static_routes", "10.0.adfadf/44 10.17.66.41 192.168.10.0/24 192.168.1.1"}, - {NULL, NULL}}; + {"classless_static_routes", "10.0.adfadf/44 10.17.66.41 192.168.10.0/24 192.168.1.1"}, + {NULL, NULL}}; options = fill_table(generic_options, NULL); options = fill_table(data, options); @@ -477,8 +476,8 @@ test_dhclient_invalid_classless_routes_3(void) const char *expected_route1_dest = "192.168.10.0"; const char *expected_route1_gw = "192.168.1.1"; static const Option data[] = { - {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 32 128 10 17 66 41"}, - {NULL, NULL}}; + {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 32 128 10 17 66 41"}, + {NULL, NULL}}; options = fill_table(generic_options, NULL); options = fill_table(data, options); @@ -501,8 +500,8 @@ test_dhcpcd_invalid_classless_routes_3(void) const char *expected_route1_dest = "192.168.10.0"; const char *expected_route1_gw = "192.168.1.1"; static Option data[] = { - {"classless_static_routes", "192.168.10.0/24 192.168.1.1 128/32 10.17.66.41"}, - {NULL, NULL}}; + {"classless_static_routes", "192.168.10.0/24 192.168.1.1 128/32 10.17.66.41"}, + {NULL, NULL}}; options = fill_table(generic_options, NULL); options = fill_table(data, options); @@ -526,8 +525,8 @@ test_dhclient_gw_in_classless_routes(void) const char *expected_route1_gw = "192.168.1.1"; const char *expected_gateway = "192.2.3.4"; static Option data[] = { - {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 0 192 2 3 4"}, - {NULL, NULL}}; + {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 0 192 2 3 4"}, + {NULL, NULL}}; options = fill_table(generic_options, NULL); options = fill_table(data, options); @@ -550,8 +549,8 @@ test_dhcpcd_gw_in_classless_routes(void) const char *expected_route1_gw = "192.168.1.1"; const char *expected_gateway = "192.2.3.4"; static Option data[] = { - {"classless_static_routes", "192.168.10.0/24 192.168.1.1 0.0.0.0/0 192.2.3.4"}, - {NULL, NULL}}; + {"classless_static_routes", "192.168.10.0/24 192.168.1.1 0.0.0.0/0 192.2.3.4"}, + {NULL, NULL}}; options = fill_table(generic_options, NULL); options = fill_table(data, options); diff --git a/src/core/dns/nm-dns-dnsmasq.c b/src/core/dns/nm-dns-dnsmasq.c index cc7c03a2..8ae1bb96 100644 --- a/src/core/dns/nm-dns-dnsmasq.c +++ b/src/core/dns/nm-dns-dnsmasq.c @@ -801,13 +801,13 @@ add_dnsmasq_nameserver(NMDnsDnsmasq *self, g_variant_builder_close(servers); } -#define IP_ADDR_TO_STRING_BUFLEN (NM_UTILS_INET_ADDRSTRLEN + 1 + IFNAMSIZ) +#define IP_ADDR_TO_STRING_BUFLEN (NM_INET_ADDRSTRLEN + 1 + IFNAMSIZ) static const char * ip_addr_to_string(int addr_family, gconstpointer addr, const char *iface, char *out_buf) { int n_written; - char buf2[NM_UTILS_INET_ADDRSTRLEN]; + char buf2[NM_INET_ADDRSTRLEN]; const char *separator; nm_assert_addr_family(addr_family); @@ -815,13 +815,13 @@ ip_addr_to_string(int addr_family, gconstpointer addr, const char *iface, char * nm_assert(out_buf); if (addr_family == AF_INET) { - nm_utils_inet_ntop(addr_family, addr, buf2); + nm_inet_ntop(addr_family, addr, buf2); separator = "@"; } else { if (IN6_IS_ADDR_V4MAPPED(addr)) - _nm_utils_inet4_ntop(((const struct in6_addr *) addr)->s6_addr32[3], buf2); + nm_inet4_ntop(((const struct in6_addr *) addr)->s6_addr32[3], buf2); else - _nm_utils_inet6_ntop(addr, buf2); + nm_inet6_ntop(addr, buf2); /* Need to scope link-local addresses with %. Before dnsmasq 2.58, * only '@' was supported as delimiter. Since 2.58, '@' and '%' are * supported. Due to a bug, since 2.73 only '%' works properly as "server" @@ -868,22 +868,24 @@ add_global_config(NMDnsDnsmasq *self, static void add_ip_config(NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsConfigIPData *ip_data) { - const char *iface; - const char *domain; - char ip_addr_to_string_buf[IP_ADDR_TO_STRING_BUFLEN]; - gconstpointer nameservers; - guint num; - guint i; - guint j; + const char *iface; + const char *domain; + char ip_addr_to_string_buf[IP_ADDR_TO_STRING_BUFLEN]; + const char *const *strarr; + guint num; + guint i; + guint j; iface = nm_platform_link_get_name(NM_PLATFORM_GET, ip_data->data->ifindex); - nameservers = nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &num); + strarr = nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &num); for (i = 0; i < num; i++) { - gconstpointer addr; + NMIPAddr a; - addr = nm_ip_addr_from_packed_array(ip_data->addr_family, nameservers, i); - ip_addr_to_string(ip_data->addr_family, addr, iface, ip_addr_to_string_buf); + if (!nm_utils_dnsname_parse_assert(ip_data->addr_family, strarr[i], NULL, &a, NULL)) + continue; + + ip_addr_to_string(ip_data->addr_family, &a, iface, ip_addr_to_string_buf); if (!ip_data->domains.has_default_route_explicit && ip_data->domains.has_default_route) add_dnsmasq_nameserver(self, servers, ip_addr_to_string_buf, NULL); diff --git a/src/core/dns/nm-dns-dnsmasq.h b/src/core/dns/nm-dns-dnsmasq.h index 98761d3d..c2f2a115 100644 --- a/src/core/dns/nm-dns-dnsmasq.h +++ b/src/core/dns/nm-dns-dnsmasq.h @@ -10,7 +10,8 @@ #include "nm-dns-manager.h" #define NM_TYPE_DNS_DNSMASQ (nm_dns_dnsmasq_get_type()) -#define NM_DNS_DNSMASQ(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasq)) +#define NM_DNS_DNSMASQ(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasq)) #define NM_DNS_DNSMASQ_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasqClass)) #define NM_IS_DNS_DNSMASQ(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DNS_DNSMASQ)) diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c index edb8ce0f..6ee2e816 100644 --- a/src/core/dns/nm-dns-manager.c +++ b/src/core/dns/nm-dns-manager.c @@ -552,29 +552,29 @@ add_dns_domains(GPtrArray *array, static void merge_one_l3cd(NMResolvConfData *rc, int addr_family, int ifindex, const NML3ConfigData *l3cd) { - char buf[NM_UTILS_INET_ADDRSTRLEN + 50]; + char buf[NM_INET_ADDRSTRLEN + 50]; gboolean has_trust_ad; guint num_nameservers; guint num; guint i; - gconstpointer nameservers; - const char *const *strv; + const char *const *strarr; nm_assert(ifindex == nm_l3_config_data_get_ifindex(l3cd)); - nameservers = nm_l3_config_data_get_nameservers(l3cd, addr_family, &num_nameservers); + strarr = nm_l3_config_data_get_nameservers(l3cd, addr_family, &num_nameservers); for (i = 0; i < num_nameservers; i++) { - const NMIPAddr *addr; + NMIPAddr a; - addr = nm_ip_addr_from_packed_array(addr_family, nameservers, i); + if (!nm_utils_dnsname_parse_assert(addr_family, strarr[i], NULL, &a, NULL)) + continue; if (addr_family == AF_INET) - nm_utils_inet_ntop(addr_family, addr, buf); - else if (IN6_IS_ADDR_V4MAPPED(addr)) - _nm_utils_inet4_ntop(addr->addr6.s6_addr32[3], buf); + nm_inet_ntop(addr_family, &a, buf); + else if (IN6_IS_ADDR_V4MAPPED(&a)) + nm_inet4_ntop(a.addr6.s6_addr32[3], buf); else { - _nm_utils_inet6_ntop(&addr->addr6, buf); - if (IN6_IS_ADDR_LINKLOCAL(addr)) { + nm_inet6_ntop(&a.addr6, buf); + if (IN6_IS_ADDR_LINKLOCAL(&a)) { const char *ifname; ifname = nm_platform_link_get_name(NM_PLATFORM_GET, ifindex); @@ -591,9 +591,9 @@ merge_one_l3cd(NMResolvConfData *rc, int addr_family, int ifindex, const NML3Con add_dns_domains(rc->searches, addr_family, l3cd, FALSE, TRUE); has_trust_ad = FALSE; - strv = nm_l3_config_data_get_dns_options(l3cd, addr_family, &num); + strarr = nm_l3_config_data_get_dns_options(l3cd, addr_family, &num); for (i = 0; i < num; i++) { - const char *option = strv[i]; + const char *option = strarr[i]; if (nm_streq(option, NM_SETTING_DNS_OPTION_TRUST_AD)) { has_trust_ad = TRUE; @@ -619,7 +619,7 @@ merge_one_l3cd(NMResolvConfData *rc, int addr_family, int ifindex, const NML3Con nis_servers = nm_l3_config_data_get_nis_servers(l3cd, &num); for (i = 0; i < num; i++) - add_string_item(rc->nis_servers, _nm_utils_inet4_ntop(nis_servers[i], buf), TRUE); + add_string_item(rc->nis_servers, nm_inet4_ntop(nis_servers[i], buf), TRUE); if ((nis_domain = nm_l3_config_data_get_nis_domain(l3cd))) { /* FIXME: handle multiple domains */ @@ -1201,14 +1201,19 @@ compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[ if (global) nm_global_dns_config_update_checksum(global, sum); - else { + + if (!global || !nm_global_dns_config_lookup_domain(global, "*")) { const CList *head; /* FIXME(ip-config-checksum): this relies on the fact that an IP * configuration without DNS parameters gives a zero checksum. */ head = _mgr_get_ip_data_lst_head(self); - c_list_for_each_entry (ip_data, head, ip_data_lst) - nm_ip_config_dns_hash(ip_data->l3cd, sum, ip_data->addr_family); + c_list_for_each_entry (ip_data, head, ip_data_lst) { + nm_l3_config_data_hash_dns(ip_data->l3cd, + sum, + ip_data->addr_family, + ip_data->ip_config_type); + } } nm_utils_checksum_get_digest_len(sum, buffer, HASH_LEN); @@ -1244,13 +1249,15 @@ merge_global_dns_config(NMResolvConfData *rc, NMGlobalDnsConfig *global_conf) } default_domain = nm_global_dns_config_lookup_domain(global_conf, "*"); - nm_assert(default_domain); + if (!default_domain) + return TRUE; servers = nm_global_dns_domain_get_servers(default_domain); - if (servers) { - for (i = 0; servers[i]; i++) - add_string_item(rc->nameservers, servers[i], TRUE); - } + if (!servers) + return TRUE; + + for (i = 0; servers[i]; i++) + add_string_item(rc->nameservers, servers[i], TRUE); return TRUE; } @@ -1258,19 +1265,21 @@ merge_global_dns_config(NMResolvConfData *rc, NMGlobalDnsConfig *global_conf) static const char * get_nameserver_list(int addr_family, const NML3ConfigData *l3cd, NMStrBuf *tmp_strbuf) { - char buf[NM_UTILS_INET_ADDRSTRLEN]; - guint num; - guint i; - gconstpointer nameservers; + char buf[NM_INET_ADDRSTRLEN]; + guint num; + guint i; + const char *const *strarr; nm_str_buf_reset(tmp_strbuf); - nameservers = nm_l3_config_data_get_nameservers(l3cd, addr_family, &num); + strarr = nm_l3_config_data_get_nameservers(l3cd, addr_family, &num); for (i = 0; i < num; i++) { - const NMIPAddr *addr; + NMIPAddr a; + + if (!nm_utils_dnsname_parse_assert(addr_family, strarr[i], NULL, &a, NULL)) + continue; - addr = nm_ip_addr_from_packed_array(addr_family, nameservers, i); - nm_utils_inet_ntop(addr_family, addr->addr_ptr, buf); + nm_inet_ntop(addr_family, &a, buf); if (i > 0) nm_str_buf_append_c(tmp_strbuf, ' '); nm_str_buf_append(tmp_strbuf, buf); @@ -1311,7 +1320,8 @@ _collect_resolv_conf_data(NMDnsManager *self, if (global_config) merge_global_dns_config(&rc, global_config); - else { + + if (!global_config || !nm_global_dns_config_lookup_domain(global_config, "*")) { nm_auto_str_buf NMStrBuf tmp_strbuf = NM_STR_BUF_INIT(0, FALSE); int first_prio = 0; const NMDnsConfigIPData *ip_data; @@ -2090,8 +2100,7 @@ nm_dns_manager_set_hostname(NMDnsManager *self, const char *hostname, gboolean s /* Certain hostnames we don't want to include in resolv.conf 'searches' */ if (hostname && nm_utils_is_specific_hostname(hostname) - && !g_str_has_suffix(hostname, ".in-addr.arpa") - && !nm_utils_ipaddr_is_valid(AF_UNSPEC, hostname)) { + && !g_str_has_suffix(hostname, ".in-addr.arpa") && !nm_inet_is_valid(AF_UNSPEC, hostname)) { domain = strchr(hostname, '.'); if (domain) { domain++; @@ -2555,14 +2564,12 @@ config_changed_cb(NMConfig *config, } } -static GVariant * -_get_global_config_variant(NMGlobalDnsConfig *global) +static void +_get_global_config_variant(GVariantBuilder *builder, NMGlobalDnsConfig *global) { NMGlobalDnsDomain *domain; - GVariantBuilder builder; guint i, num; - g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); num = nm_global_dns_config_get_num_domains(global); for (i = 0; i < num; i++) { GVariantBuilder conf_builder; @@ -2598,10 +2605,8 @@ _get_global_config_variant(NMGlobalDnsConfig *global) "priority", g_variant_new_int32(NM_DNS_PRIORITY_DEFAULT_NORMAL)); - g_variant_builder_add(&builder, "a{sv}", &conf_builder); + g_variant_builder_add(builder, "a{sv}", &conf_builder); } - - return g_variant_ref_sink(g_variant_builder_end(&builder)); } static GVariant * @@ -2618,28 +2623,25 @@ _get_config_variant(NMDnsManager *self) if (priv->config_variant) return priv->config_variant; - global_config = nm_config_data_get_global_dns_config(nm_config_get_data(priv->config)); - if (global_config) { - priv->config_variant = _get_global_config_variant(global_config); - _LOGT("current configuration: %s", (str = g_variant_print(priv->config_variant, TRUE))); - return priv->config_variant; - } - g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); + global_config = nm_config_data_get_global_dns_config(nm_config_get_data(priv->config)); + if (global_config) + _get_global_config_variant(&builder, global_config); + head = _mgr_get_ip_data_lst_head(self); c_list_for_each_entry (ip_data, head, ip_data_lst) { - GVariantBuilder entry_builder; - GVariantBuilder strv_builder; - guint num; - guint num_domains; - guint num_searches; - guint i; - char buf[NM_UTILS_INET_ADDRSTRLEN]; - const char *ifname; - gconstpointer nameservers; - - nameservers = nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &num); + GVariantBuilder entry_builder; + GVariantBuilder strv_builder; + guint num; + guint num_domains; + guint num_searches; + guint i; + char buf[NM_INET_ADDRSTRLEN]; + const char *ifname; + const char *const *strarr; + + strarr = nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &num); if (num == 0) continue; @@ -2647,12 +2649,12 @@ _get_config_variant(NMDnsManager *self) g_variant_builder_init(&strv_builder, G_VARIANT_TYPE("as")); for (i = 0; i < num; i++) { - const NMIPAddr *addr; + NMIPAddr a; + + if (!nm_utils_dnsname_parse_assert(ip_data->addr_family, strarr[i], NULL, &a, NULL)) + continue; - addr = nm_ip_addr_from_packed_array(ip_data->addr_family, nameservers, i); - g_variant_builder_add(&strv_builder, - "s", - nm_utils_inet_ntop(ip_data->addr_family, addr, buf)); + g_variant_builder_add(&strv_builder, "s", nm_inet_ntop(ip_data->addr_family, &a, buf)); } g_variant_builder_add(&entry_builder, "{sv}", diff --git a/src/core/dns/nm-dns-manager.h b/src/core/dns/nm-dns-manager.h index 210f9f6c..42f9dec5 100644 --- a/src/core/dns/nm-dns-manager.h +++ b/src/core/dns/nm-dns-manager.h @@ -12,16 +12,6 @@ #include "nm-setting-connection.h" #include "nm-dns-plugin.h" -typedef enum { - NM_DNS_IP_CONFIG_TYPE_REMOVED = -1, - - NM_DNS_IP_CONFIG_TYPE_DEFAULT = 0, - NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE, - NM_DNS_IP_CONFIG_TYPE_VPN, -} NMDnsIPConfigType; - -/*****************************************************************************/ - struct _NMDnsConfigData; struct _NMDnsManager; @@ -71,7 +61,7 @@ typedef struct _NMDnsConfigData { /*****************************************************************************/ #define NM_TYPE_DNS_MANAGER (nm_dns_manager_get_type()) -#define NM_DNS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_DNS_MANAGER, NMDnsManager)) +#define NM_DNS_MANAGER(o) (_NM_G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_DNS_MANAGER, NMDnsManager)) #define NM_DNS_MANAGER_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_DNS_MANAGER, NMDnsManagerClass)) #define NM_IS_DNS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), NM_TYPE_DNS_MANAGER)) diff --git a/src/core/dns/nm-dns-plugin.h b/src/core/dns/nm-dns-plugin.h index 24d6083b..364f15ce 100644 --- a/src/core/dns/nm-dns-plugin.h +++ b/src/core/dns/nm-dns-plugin.h @@ -11,7 +11,7 @@ #include "nm-config-data.h" #define NM_TYPE_DNS_PLUGIN (nm_dns_plugin_get_type()) -#define NM_DNS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_PLUGIN, NMDnsPlugin)) +#define NM_DNS_PLUGIN(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_PLUGIN, NMDnsPlugin)) #define NM_DNS_PLUGIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DNS_PLUGIN, NMDnsPluginClass)) #define NM_IS_DNS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DNS_PLUGIN)) diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c index e0b94647..24c7c774 100644 --- a/src/core/dns/nm-dns-systemd-resolved.c +++ b/src/core/dns/nm-dns-systemd-resolved.c @@ -36,6 +36,7 @@ /* define a variable, so that we can compare the operation with pointer equality. */ static const char *const DBUS_OP_SET_LINK_DEFAULT_ROUTE = "SetLinkDefaultRoute"; static const char *const DBUS_OP_SET_LINK_DNS_OVER_TLS = "SetLinkDNSOverTLS"; +static const char *const DBUS_OP_SET_LINK_DNS_EX = "SetLinkDNSEx"; /*****************************************************************************/ @@ -90,12 +91,11 @@ typedef struct { bool dbus_initied : 1; bool send_updates_waiting : 1; bool update_pending : 1; - /* These two variables ensure that the log is not spammed with - * API (not) supported messages. - * They can be removed when no distro uses systemd-resolved < v240 anymore - */ - NMTernary has_link_default_route : 3; - NMTernary has_link_dns_over_tls : 3; + + /* Detect support for the respective D-Bus API. */ + NMTernary has_set_link_default_route : 3; + NMTernary has_set_link_dns_over_tls : 3; + NMTernary has_set_link_dns_ex : 3; } NMDnsSystemdResolvedPrivate; struct _NMDnsSystemdResolved { @@ -148,6 +148,8 @@ static void _resolve_complete_error(NMDnsSystemdResolvedResolveHandle *handle, G static void _resolve_start(NMDnsSystemdResolved *self, NMDnsSystemdResolvedResolveHandle *handle); +static void send_updates(NMDnsSystemdResolved *self); + /*****************************************************************************/ static gboolean @@ -276,6 +278,7 @@ call_done(GObject *source, GAsyncResult *r, gpointer user_data) NMLogLevel log_level; const char *operation; int ifindex; + gboolean reconfigure = FALSE; request_item = user_data; self = request_item->self; @@ -290,30 +293,52 @@ call_done(GObject *source, GAsyncResult *r, gpointer user_data) goto out_dec_pending; if (v) { - if (operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE - && priv->has_link_default_route == NM_TERNARY_DEFAULT) { - priv->has_link_default_route = NM_TERNARY_TRUE; - _LOGD("systemd-resolved support for SetLinkDefaultRoute(): API supported"); - } - if (operation == DBUS_OP_SET_LINK_DNS_OVER_TLS - && priv->has_link_dns_over_tls == NM_TERNARY_DEFAULT) { - priv->has_link_dns_over_tls = NM_TERNARY_TRUE; - _LOGD("systemd-resolved support for SetLinkDNSOverTLS(): API supported"); + if (operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE) { + if (priv->has_set_link_default_route == NM_TERNARY_DEFAULT) { + priv->has_set_link_default_route = NM_TERNARY_TRUE; + _LOGD("systemd-resolved support for SetLinkDefaultRoute(): API supported"); + } + } else if (operation == DBUS_OP_SET_LINK_DNS_OVER_TLS) { + if (priv->has_set_link_dns_over_tls == NM_TERNARY_DEFAULT) { + priv->has_set_link_dns_over_tls = NM_TERNARY_TRUE; + _LOGD("systemd-resolved support for SetLinkDNSOverTLS(): API supported"); + } + } else if (operation == DBUS_OP_SET_LINK_DNS_EX) { + if (priv->has_set_link_dns_ex == NM_TERNARY_DEFAULT) { + priv->has_set_link_dns_ex = NM_TERNARY_TRUE; + _LOGD("systemd-resolved support for SetLinkDNSEx(): API supported"); + } } priv->send_updates_warn_ratelimited = FALSE; goto out_dec_pending; } if (nm_g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { - if (priv->has_link_default_route == NM_TERNARY_DEFAULT - && operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE) { - priv->has_link_default_route = NM_TERNARY_FALSE; - _LOGD("systemd-resolved support for SetLinkDefaultRoute(): API not supported"); - } - if (priv->has_link_dns_over_tls == NM_TERNARY_DEFAULT - && operation == DBUS_OP_SET_LINK_DNS_OVER_TLS) { - priv->has_link_dns_over_tls = NM_TERNARY_FALSE; - _LOGD("systemd-resolved support for SetLinkDNSOverTLS(): API not supported"); + if (operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE) { + if (priv->has_set_link_default_route == NM_TERNARY_DEFAULT) { + priv->has_set_link_default_route = NM_TERNARY_FALSE; + _LOGD("systemd-resolved support for SetLinkDefaultRoute(): API not supported"); + } + } else if (operation == DBUS_OP_SET_LINK_DNS_OVER_TLS) { + if (priv->has_set_link_dns_over_tls == NM_TERNARY_DEFAULT) { + priv->has_set_link_dns_over_tls = NM_TERNARY_FALSE; + _LOGD("systemd-resolved support for SetLinkDNSOverTLS(): API not supported"); + } + } else if (operation == DBUS_OP_SET_LINK_DNS_EX) { + if (priv->has_set_link_dns_ex == NM_TERNARY_DEFAULT) { + priv->has_set_link_dns_ex = NM_TERNARY_FALSE; + _LOGD("systemd-resolved support for SetLinkDNSEx(): API not supported"); + + _LOGW("systemd-resolved does not support SetLinkDNSEx API (v246). " + "Cannot set DoT server name (SNI)"); + + /* We need to reconfigure with the SetLinkDNS fallback. + * + * In the other cases above, there is no need to reconfigure anything. + * We won't retry SetLinkDefaultRoute/SetLinkDNSOverTLS anymore, but there + * is nothing else we can do about that. */ + reconfigure = TRUE; + } } goto out_dec_pending; } @@ -335,21 +360,29 @@ out_dec_pending: * we must wrap up fast, and not hang an undefined amount time. */ g_object_unref(self); } + + if (reconfigure) { + priv->send_updates_waiting = TRUE; + send_updates(self); + } } static gboolean update_add_ip_config(NMDnsSystemdResolved *self, + const NMDnsConfigIPData *ip_data, GVariantBuilder *dns, + GVariantBuilder *dns_ex, GVariantBuilder *domains, - const NMDnsConfigIPData *ip_data) + gboolean *out_require_dns_ex) { - gsize addr_size; - guint n; - guint i; - gboolean is_routing; - const char *domain; - gboolean has_config = FALSE; - gconstpointer nameservers; + NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + gsize addr_size; + guint n; + guint i; + gboolean is_routing; + const char *domain; + gboolean has_config = FALSE; + const char *const *strarr; addr_size = nm_utils_addr_family_to_size(ip_data->addr_family); @@ -361,29 +394,52 @@ update_add_ip_config(NMDnsSystemdResolved *self, return FALSE; } - nameservers = nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &n); + strarr = nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &n); for (i = 0; i < n; i++) { - g_variant_builder_open(dns, G_VARIANT_TYPE("(iay)")); - g_variant_builder_add(dns, "i", ip_data->addr_family); - g_variant_builder_add_value( - dns, - nm_g_variant_new_ay(nm_ip_addr_from_packed_array(ip_data->addr_family, nameservers, i), - addr_size)); - g_variant_builder_close(dns); - has_config = TRUE; - } + const char *server_name; + NMIPAddr a; + + if (!nm_utils_dnsname_parse_assert(ip_data->addr_family, strarr[i], NULL, &a, &server_name)) + continue; - if (!ip_data->domains.has_default_route_explicit - && ip_data->domains.has_default_route_exclusive) { - g_variant_builder_add(domains, "(sb)", ".", TRUE); + if (server_name) { + NM_SET_OUT(out_require_dns_ex, TRUE); + if (priv->has_set_link_dns_ex == FALSE) { + /* The caller won't care about this result anymore. We can skip setting it. */ + dns = NULL; + } + } + + if (dns_ex) { + g_variant_builder_open(dns_ex, G_VARIANT_TYPE("(iayqs)")); + g_variant_builder_add(dns_ex, "i", ip_data->addr_family); + g_variant_builder_add_value(dns_ex, nm_g_variant_new_ay((gconstpointer) &a, addr_size)); + g_variant_builder_add(dns_ex, "q", 0); + g_variant_builder_add(dns_ex, "s", server_name ?: ""); + g_variant_builder_close(dns_ex); + } + if (dns) { + g_variant_builder_open(dns, G_VARIANT_TYPE("(iay)")); + g_variant_builder_add(dns, "i", ip_data->addr_family); + g_variant_builder_add_value(dns, nm_g_variant_new_ay((gconstpointer) &a, addr_size)); + g_variant_builder_close(dns); + } has_config = TRUE; } - if (ip_data->domains.search) { - for (i = 0; ip_data->domains.search[i]; i++) { - domain = nm_utils_parse_dns_domain(ip_data->domains.search[i], &is_routing); - g_variant_builder_add(domains, "(sb)", domain[0] ? domain : ".", is_routing); + + if (!has_config || domains) { + if (!ip_data->domains.has_default_route_explicit + && ip_data->domains.has_default_route_exclusive) { + g_variant_builder_add(domains, "(sb)", ".", TRUE); has_config = TRUE; } + if (ip_data->domains.search) { + for (i = 0; ip_data->domains.search[i]; i++) { + domain = nm_utils_parse_dns_domain(ip_data->domains.search[i], &is_routing); + g_variant_builder_add(domains, "(sb)", domain[0] ? domain : ".", is_routing); + has_config = TRUE; + } + } } return has_config; @@ -406,7 +462,9 @@ free_pending_updates(NMDnsSystemdResolved *self) static gboolean prepare_one_interface(NMDnsSystemdResolved *self, const InterfaceConfig *ic) { + NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); GVariantBuilder dns; + GVariantBuilder dns_ex; GVariantBuilder domains; NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT; NMSettingConnectionLlmnr llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT; @@ -417,6 +475,9 @@ prepare_one_interface(NMDnsSystemdResolved *self, const InterfaceConfig *ic) gboolean has_config = FALSE; gboolean has_default_route = FALSE; guint i; + gboolean require_dns_ex = FALSE; + + nm_assert(ic->ifindex != NM_LOOPBACK_IFINDEX); g_variant_builder_init(&dns, G_VARIANT_TYPE("(ia(iay))")); g_variant_builder_add(&dns, "i", ic->ifindex); @@ -430,7 +491,7 @@ prepare_one_interface(NMDnsSystemdResolved *self, const InterfaceConfig *ic) for (i = 0; i < ic->ip_data_list->len; i++) { const NMDnsConfigIPData *ip_data = ic->ip_data_list->pdata[i]; - if (update_add_ip_config(self, &dns, &domains, ip_data)) + if (update_add_ip_config(self, ip_data, &dns, NULL, &domains, &require_dns_ex)) has_config = TRUE; if (ip_data->domains.has_default_route) @@ -448,6 +509,23 @@ prepare_one_interface(NMDnsSystemdResolved *self, const InterfaceConfig *ic) g_variant_builder_close(&dns); g_variant_builder_close(&domains); + if (!require_dns_ex) { + /* No need to use the new API. SetLinkDNS() is sufficient. */ + } else if (!priv->has_set_link_dns_ex) { + /* API to set server name is not supported. Nothing we can do. */ + require_dns_ex = FALSE; + } else { + g_variant_builder_init(&dns_ex, G_VARIANT_TYPE("(ia(iayqs))")); + g_variant_builder_add(&dns_ex, "i", ic->ifindex); + g_variant_builder_open(&dns_ex, G_VARIANT_TYPE("a(iayqs)")); + for (i = 0; i < ic->ip_data_list->len; i++) { + const NMDnsConfigIPData *ip_data = ic->ip_data_list->pdata[i]; + + update_add_ip_config(self, ip_data, NULL, &dns_ex, NULL, NULL); + } + g_variant_builder_close(&dns_ex); + } + switch (mdns) { case NM_SETTING_CONNECTION_MDNS_NO: mdns_arg = "no"; @@ -513,7 +591,14 @@ prepare_one_interface(NMDnsSystemdResolved *self, const InterfaceConfig *ic) "SetLinkLLMNR", ic->ifindex, g_variant_new("(is)", ic->ifindex, llmnr_arg ?: "")); - _request_item_append(self, "SetLinkDNS", ic->ifindex, g_variant_builder_end(&dns)); + if (require_dns_ex) { + _request_item_append(self, + DBUS_OP_SET_LINK_DNS_EX, + ic->ifindex, + g_variant_builder_end(&dns_ex)); + g_variant_builder_clear(&dns); + } else + _request_item_append(self, "SetLinkDNS", ic->ifindex, g_variant_builder_end(&dns)); _request_item_append(self, DBUS_OP_SET_LINK_DNS_OVER_TLS, ic->ifindex, @@ -631,9 +716,9 @@ send_updates(NMDnsSystemdResolved *self) gs_free char *ss = NULL; if ((request_item->operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE - && priv->has_link_default_route == NM_TERNARY_FALSE) + && priv->has_set_link_default_route == NM_TERNARY_FALSE) || (request_item->operation == DBUS_OP_SET_LINK_DNS_OVER_TLS - && priv->has_link_dns_over_tls == NM_TERNARY_FALSE)) { + && priv->has_set_link_dns_over_tls == NM_TERNARY_FALSE)) { /* The "SetLinkDefaultRoute" API is only supported since v240. * The "SetLinkDNSOverTLS" API is only supported since v239. * We detected whether they are supported, and skip the calls. There @@ -708,6 +793,13 @@ update(NMDnsPlugin *plugin, nm_assert(ifindex == nm_l3_config_data_get_ifindex(ip_data->l3cd)); + if (ifindex == NM_LOOPBACK_IFINDEX) { + /* systemd-resolved API is per-link, and loopback is not supported. + * Unclear what to do about DNS configuration on loopback. Just skip + * it here. */ + continue; + } + ic = g_hash_table_lookup(interfaces, GINT_TO_POINTER(ifindex)); if (!ic) { ic = g_slice_new(InterfaceConfig); @@ -759,7 +851,7 @@ update(NMDnsPlugin *plugin, if (dirty_array) { g_array_sort_with_data(dirty_array, nm_cmp_int2ptr_p_with_data, NULL); for (i = 0; i < dirty_array->len; i++) { - int ifindex = g_array_index(dirty_array, int, i); + int ifindex = nm_g_array_index(dirty_array, int, i); InterfaceConfig ic; _LOGT("clear previously configured ifindex %d", ifindex); @@ -798,8 +890,9 @@ name_owner_changed(NMDnsSystemdResolved *self, const char *owner) priv->try_start_blocked = FALSE; priv->send_updates_waiting = TRUE; } else { - priv->has_link_default_route = NM_TERNARY_DEFAULT; - priv->has_link_dns_over_tls = NM_TERNARY_DEFAULT; + priv->has_set_link_default_route = NM_TERNARY_DEFAULT; + priv->has_set_link_dns_over_tls = NM_TERNARY_DEFAULT; + priv->has_set_link_dns_ex = NM_TERNARY_DEFAULT; } send_updates(self); @@ -968,7 +1061,7 @@ _resolve_handle_call_cb(GObject *source, GAsyncResult *result, gpointer user_dat g_variant_iter_free(v_names_iter); _resolve_complete(handle, - &g_array_index(v_names, NMDnsSystemdResolvedAddressResult, 0), + nm_g_array_first_p(v_names, NMDnsSystemdResolvedAddressResult), v_names->len, v_flags, NULL); @@ -1066,7 +1159,7 @@ nm_dns_systemd_resolved_resolve_address(NMDnsSystemdResolved { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); NMDnsSystemdResolvedResolveHandle *handle; - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + char addr_str[NM_INET_ADDRSTRLEN]; g_return_val_if_fail(NM_IS_DNS_SYSTEMD_RESOLVED(self), NULL); nm_assert_addr_family(addr_family); @@ -1092,7 +1185,7 @@ nm_dns_systemd_resolved_resolve_address(NMDnsSystemdResolved _LOG2T(handle, "resolve-address(ifindex=%d, %s, flags=%" G_GINT64_MODIFIER "x): new request", handle->r_address.ifindex, - nm_utils_inet_ntop(handle->r_address.addr_family, &handle->r_address.addr, addr_str), + nm_inet_ntop(handle->r_address.addr_family, &handle->r_address.addr, addr_str), handle->r_address.flags); _resolve_start(self, handle); @@ -1158,8 +1251,9 @@ nm_dns_systemd_resolved_init(NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - priv->has_link_default_route = NM_TERNARY_DEFAULT; - priv->has_link_dns_over_tls = NM_TERNARY_DEFAULT; + priv->has_set_link_default_route = NM_TERNARY_DEFAULT; + priv->has_set_link_dns_over_tls = NM_TERNARY_DEFAULT; + priv->has_set_link_dns_ex = NM_TERNARY_DEFAULT; c_list_init(&priv->request_queue_lst_head); c_list_init(&priv->handle_lst_head); diff --git a/src/core/dns/nm-dns-systemd-resolved.h b/src/core/dns/nm-dns-systemd-resolved.h index 02bc524c..0563a70f 100644 --- a/src/core/dns/nm-dns-systemd-resolved.h +++ b/src/core/dns/nm-dns-systemd-resolved.h @@ -12,7 +12,7 @@ #define NM_TYPE_DNS_SYSTEMD_RESOLVED (nm_dns_systemd_resolved_get_type()) #define NM_DNS_SYSTEMD_RESOLVED(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_SYSTEMD_RESOLVED, NMDnsSystemdResolved)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_SYSTEMD_RESOLVED, NMDnsSystemdResolved)) #define NM_DNS_SYSTEMD_RESOLVED_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DNS_SYSTEMD_RESOLVED, NMDnsSystemdResolvedClass)) #define NM_IS_DNS_SYSTEMD_RESOLVED(obj) \ diff --git a/src/core/dnsmasq/nm-dnsmasq-manager.c b/src/core/dnsmasq/nm-dnsmasq-manager.c index be463da3..d245d5d3 100644 --- a/src/core/dnsmasq/nm-dnsmasq-manager.c +++ b/src/core/dnsmasq/nm-dnsmasq-manager.c @@ -104,7 +104,6 @@ create_dm_cmd_line(const char *iface, gs_free char *error_desc = NULL; const char *dm_binary; const NMPlatformIP4Address *listen_address; - const in_addr_t *ipv4arr; const char *const *strarr; guint n; guint i; @@ -147,7 +146,7 @@ create_dm_cmd_line(const char *iface, */ nm_strv_ptrarray_add_string_dup(cmd, "--strict-order"); - _nm_utils_inet4_ntop(listen_address->address, listen_address_s); + nm_inet4_ntop(listen_address->address, listen_address_s); nm_strv_ptrarray_add_string_concat(cmd, "--listen-address=", listen_address_s); @@ -163,13 +162,18 @@ create_dm_cmd_line(const char *iface, nm_strv_ptrarray_add_string_concat(cmd, "--dhcp-option=option:router,", listen_address_s); } - ipv4arr = nm_l3_config_data_get_nameservers(l3cd, AF_INET, &n); + strarr = nm_l3_config_data_get_nameservers(l3cd, AF_INET, &n); if (n > 0) { nm_gstring_prepare(&s); g_string_append(s, "--dhcp-option=option:dns-server"); for (i = 0; i < n; i++) { + in_addr_t a; + + if (!nm_utils_dnsname_parse_assert(AF_INET, strarr[i], NULL, &a, NULL)) + continue; + g_string_append_c(s, ','); - g_string_append(s, _nm_utils_inet4_ntop(ipv4arr[i], sbuf_addr)); + g_string_append(s, nm_inet4_ntop(a, sbuf_addr)); } nm_strv_ptrarray_take_gstring(cmd, &s); } diff --git a/src/core/dnsmasq/nm-dnsmasq-manager.h b/src/core/dnsmasq/nm-dnsmasq-manager.h index 7fd4199e..2f10a9e8 100644 --- a/src/core/dnsmasq/nm-dnsmasq-manager.h +++ b/src/core/dnsmasq/nm-dnsmasq-manager.h @@ -8,7 +8,7 @@ #define NM_TYPE_DNSMASQ_MANAGER (nm_dnsmasq_manager_get_type()) #define NM_DNSMASQ_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNSMASQ_MANAGER, NMDnsMasqManager)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNSMASQ_MANAGER, NMDnsMasqManager)) #define NM_DNSMASQ_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DNSMASQ_MANAGER, NMDnsMasqManagerClass)) #define NM_IS_DNSMASQ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DNSMASQ_MANAGER)) diff --git a/src/core/dnsmasq/nm-dnsmasq-utils.c b/src/core/dnsmasq/nm-dnsmasq-utils.c index 7f799878..69424d74 100644 --- a/src/core/dnsmasq/nm-dnsmasq-utils.c +++ b/src/core/dnsmasq/nm-dnsmasq-utils.c @@ -50,7 +50,7 @@ nm_dnsmasq_utils_get_range(const NMPlatformIP4Address *addr, prefix = 24; } - netmask = _nm_utils_ip4_prefix_to_netmask(prefix); + netmask = nm_ip4_addr_netmask_from_prefix(prefix); /* treat addresses in host-order from here on. */ netmask = ntohl(netmask); @@ -96,8 +96,8 @@ nm_dnsmasq_utils_get_range(const NMPlatformIP4Address *addr, first = htonl(first); last = htonl(last); - _nm_utils_inet4_ntop(first, out_first); - _nm_utils_inet4_ntop(last, out_last); + nm_inet4_ntop(first, out_first); + nm_inet4_ntop(last, out_last); return TRUE; } diff --git a/src/core/main-utils.c b/src/core/main-utils.c index 444e122e..c6fa05c0 100644 --- a/src/core/main-utils.c +++ b/src/core/main-utils.c @@ -18,6 +18,7 @@ #include "main-utils.h" #include "NetworkManagerUtils.h" #include "nm-config.h" +#include "libnm-glib-aux/nm-io-utils.h" static gboolean sighup_handler(gpointer user_data) @@ -76,34 +77,20 @@ nm_main_utils_setup_signals(GMainLoop *main_loop) gboolean nm_main_utils_write_pidfile(const char *pidfile) { - char pid[16]; - int fd; - int errsv; - gboolean success = FALSE; + gs_free_error GError *error = NULL; + char pid[16]; - if ((fd = open(pidfile, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 00644)) < 0) { - errsv = errno; - fprintf(stderr, _("Opening %s failed: %s\n"), pidfile, nm_strerror_native(errsv)); + nm_sprintf_buf(pid, "%lld", (long long) getpid()); + if (!nm_utils_file_set_contents(pidfile, pid, -1, 00644, NULL, NULL, &error)) { + fprintf(stderr, _("Writing to %s failed: %s\n"), pidfile, error->message); return FALSE; } - g_snprintf(pid, sizeof(pid), "%d", getpid()); - if (write(fd, pid, strlen(pid)) < 0) { - errsv = errno; - fprintf(stderr, _("Writing to %s failed: %s\n"), pidfile, nm_strerror_native(errsv)); - } else - success = TRUE; - - if (nm_close(fd)) { - errsv = errno; - fprintf(stderr, _("Closing %s failed: %s\n"), pidfile, nm_strerror_native(errsv)); - } - - return success; + return TRUE; } void -nm_main_utils_ensure_statedir() +nm_main_utils_ensure_statedir(void) { gs_free char *parent = NULL; int errsv; @@ -129,7 +116,7 @@ nm_main_utils_ensure_statedir() } void -nm_main_utils_ensure_rundir() +nm_main_utils_ensure_rundir(void) { int errsv; diff --git a/src/core/meson.build b/src/core/meson.build index f3359ad0..6c1d463e 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -53,6 +53,7 @@ libNetworkManagerBase = static_library( 'nm-l3-ipv4ll.c', 'nm-l3-ipv6ll.c', 'nm-l3cfg.c', + 'nm-bond-manager.c', 'nm-ip-config.c', ), dependencies: [ @@ -103,6 +104,7 @@ libNetworkManager = static_library( 'devices/nm-device-generic.c', 'devices/nm-device-infiniband.c', 'devices/nm-device-ip-tunnel.c', + 'devices/nm-device-loopback.c', 'devices/nm-device-macsec.c', 'devices/nm-device-macvlan.c', 'devices/nm-device-ppp.c', @@ -204,6 +206,7 @@ if enable_tests libnm_core_aux_intern, libnm_core_impl, libnm_crypto, + libnm_lldp, libnm_platform, libnm_base, libnm_systemd_shared, @@ -255,6 +258,7 @@ NetworkManager_all_sym = executable( libnm_core_aux_intern, libnm_core_impl, libnm_crypto, + libnm_lldp, libnm_platform, libnm_base, libnm_log_core, @@ -295,6 +299,7 @@ NetworkManager = executable( libnm_core_aux_intern, libnm_core_impl, libnm_crypto, + libnm_lldp, libnm_platform, libnm_base, libnm_udev_aux, diff --git a/src/core/ndisc/nm-fake-ndisc.c b/src/core/ndisc/nm-fake-ndisc.c index 59bf0c1b..f305e771 100644 --- a/src/core/ndisc/nm-fake-ndisc.c +++ b/src/core/ndisc/nm-fake-ndisc.c @@ -247,14 +247,14 @@ receive_ra(gpointer user_data) } for (i = 0; i < ra->gateways->len; i++) { - const NMNDiscGateway *item = &g_array_index(ra->gateways, NMNDiscGateway, i); + const NMNDiscGateway *item = &nm_g_array_index(ra->gateways, NMNDiscGateway, i); if (nm_ndisc_add_gateway(ndisc, item, now_msec)) changed |= NM_NDISC_CONFIG_GATEWAYS; } for (i = 0; i < ra->prefixes->len; i++) { - FakePrefix *item = &g_array_index(ra->prefixes, FakePrefix, i); + FakePrefix *item = &nm_g_array_index(ra->prefixes, FakePrefix, i); const NMNDiscRoute route = { .network = item->network, .plen = item->plen, @@ -282,14 +282,14 @@ receive_ra(gpointer user_data) } for (i = 0; i < ra->dns_servers->len; i++) { - const NMNDiscDNSServer *item = &g_array_index(ra->dns_servers, NMNDiscDNSServer, i); + const NMNDiscDNSServer *item = &nm_g_array_index(ra->dns_servers, NMNDiscDNSServer, i); if (nm_ndisc_add_dns_server(ndisc, item, now_msec)) changed |= NM_NDISC_CONFIG_DNS_SERVERS; } for (i = 0; i < ra->dns_domains->len; i++) { - const NMNDiscDNSDomain *item = &g_array_index(ra->dns_domains, NMNDiscDNSDomain, i); + const NMNDiscDNSDomain *item = &nm_g_array_index(ra->dns_domains, NMNDiscDNSDomain, i); if (nm_ndisc_add_dns_domain(ndisc, item, now_msec)) changed |= NM_NDISC_CONFIG_DNS_DOMAINS; diff --git a/src/core/ndisc/nm-fake-ndisc.h b/src/core/ndisc/nm-fake-ndisc.h index 91c2c21c..a4a29d7c 100644 --- a/src/core/ndisc/nm-fake-ndisc.h +++ b/src/core/ndisc/nm-fake-ndisc.h @@ -9,7 +9,7 @@ #include "nm-ndisc.h" #define NM_TYPE_FAKE_NDISC (nm_fake_ndisc_get_type()) -#define NM_FAKE_NDISC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_FAKE_NDISC, NMFakeNDisc)) +#define NM_FAKE_NDISC(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_FAKE_NDISC, NMFakeNDisc)) #define NM_FAKE_NDISC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_FAKE_NDISC, NMFakeNDiscClass)) #define NM_IS_FAKE_NDISC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_FAKE_NDISC)) diff --git a/src/core/ndisc/nm-lndp-ndisc.c b/src/core/ndisc/nm-lndp-ndisc.c index 313756a6..63dde5a5 100644 --- a/src/core/ndisc/nm-lndp-ndisc.c +++ b/src/core/ndisc/nm-lndp-ndisc.c @@ -202,9 +202,7 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) r_plen = ndp_msg_opt_prefix_len(msg, offset); if (r_plen == 0 || r_plen > 128) continue; - nm_utils_ip6_address_clear_host_address(&r_network, - ndp_msg_opt_prefix(msg, offset), - r_plen); + nm_ip6_addr_clear_host_address(&r_network, ndp_msg_opt_prefix(msg, offset), r_plen); if (IN6_IS_ADDR_UNSPECIFIED(&r_network) || IN6_IS_ADDR_LINKLOCAL(&r_network)) continue; @@ -213,6 +211,7 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) const NMNDiscRoute route = { .network = r_network, .plen = r_plen, + .on_link = TRUE, .expiry_msec = _nm_ndisc_lifetime_to_expiry(now_msec, ndp_msg_opt_prefix_valid_time(msg, offset)), @@ -244,15 +243,14 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) if (plen == 0 || plen > 128) continue; - nm_utils_ip6_address_clear_host_address(&network, - ndp_msg_opt_route_prefix(msg, offset), - plen); + nm_ip6_addr_clear_host_address(&network, ndp_msg_opt_route_prefix(msg, offset), plen); { const NMNDiscRoute route = { .network = network, .gateway = gateway_addr, .plen = plen, + .on_link = FALSE, .expiry_msec = _nm_ndisc_lifetime_to_expiry(now_msec, ndp_msg_opt_route_lifetime(msg, offset)), .preference = _route_preference_coerce(ndp_msg_opt_route_preference(msg, offset)), @@ -417,7 +415,7 @@ send_ra(NMNDisc *ndisc, GError **error) /* The device let us know about all addresses that the device got * whose prefixes are suitable for delegating. Let's announce them. */ for (i = 0; i < rdata->addresses->len; i++) { - const NMNDiscAddress *address = &g_array_index(rdata->addresses, NMNDiscAddress, i); + const NMNDiscAddress *address = &nm_g_array_index(rdata->addresses, NMNDiscAddress, i); struct nd_opt_prefix_info *prefix; prefix = _ndp_msg_add_option(msg, sizeof(*prefix)); @@ -462,7 +460,7 @@ send_ra(NMNDisc *ndisc, GError **error) for (i = 0; i < rdata->dns_servers->len; i++) { const NMNDiscDNSServer *dns_server = - &g_array_index(rdata->dns_servers, NMNDiscDNSServer, i); + &nm_g_array_index(rdata->dns_servers, NMNDiscDNSServer, i); option->addrs[i] = dns_server->address; } @@ -478,7 +476,7 @@ dns_servers_done: for (i = 0; i < rdata->dns_domains->len; i++) { const NMNDiscDNSDomain *dns_domain = - &g_array_index(rdata->dns_domains, NMNDiscDNSDomain, i); + &nm_g_array_index(rdata->dns_domains, NMNDiscDNSDomain, i); const char *domain = dns_domain->domain; gsize domain_l; gsize n_reserved; diff --git a/src/core/ndisc/nm-lndp-ndisc.h b/src/core/ndisc/nm-lndp-ndisc.h index 711a95e1..62b7b217 100644 --- a/src/core/ndisc/nm-lndp-ndisc.h +++ b/src/core/ndisc/nm-lndp-ndisc.h @@ -10,7 +10,7 @@ #include "nm-core-utils.h" #define NM_TYPE_LNDP_NDISC (nm_lndp_ndisc_get_type()) -#define NM_LNDP_NDISC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_LNDP_NDISC, NMLndpNDisc)) +#define NM_LNDP_NDISC(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_LNDP_NDISC, NMLndpNDisc)) #define NM_LNDP_NDISC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_LNDP_NDISC, NMLndpNDiscClass)) #define NM_IS_LNDP_NDISC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_LNDP_NDISC)) diff --git a/src/core/ndisc/nm-ndisc.c b/src/core/ndisc/nm-ndisc.c index 9a6038d4..39a4df48 100644 --- a/src/core/ndisc/nm-ndisc.c +++ b/src/core/ndisc/nm-ndisc.c @@ -13,6 +13,7 @@ #include "libnm-platform/nm-platform-utils.h" #include "libnm-platform/nm-platform.h" #include "libnm-platform/nmp-netns.h" +#include "libnm-core-aux-intern/nm-libnm-core-utils.h" #include "nm-l3-config-data.h" #include "nm-l3cfg.h" #include "nm-ndisc-private.h" @@ -160,8 +161,9 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex *multi_idx, .table_any = TRUE, .table_coerced = 0, .metric_any = TRUE, - .metric = 0, - .rt_pref = ndisc_route->preference, + /* Non-on_link routes get a small penalty */ + .metric = ndisc_route->duplicate && !ndisc_route->on_link ? 5 : 0, + .rt_pref = ndisc_route->preference, }; nm_assert((NMIcmpv6RouterPref) r.rt_pref == ndisc_route->preference); @@ -186,8 +188,12 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex *multi_idx, } } - for (i = 0; i < rdata->dns_servers_n; i++) - nm_l3_config_data_add_nameserver(l3cd, AF_INET6, &rdata->dns_servers[i].address); + for (i = 0; i < rdata->dns_servers_n; i++) { + nm_l3_config_data_add_nameserver_detail(l3cd, + AF_INET6, + &rdata->dns_servers[i].address, + NULL); + } for (i = 0; i < rdata->dns_domains_n; i++) nm_l3_config_data_add_search(l3cd, AF_INET6, rdata->dns_domains[i].domain); @@ -324,11 +330,11 @@ _ASSERT_data_gateways(const NMNDiscDataInternal *data) return; for (i = 0; i < data->gateways->len; i++) { - const NMNDiscGateway *item = &g_array_index(data->gateways, NMNDiscGateway, i); + const NMNDiscGateway *item = &nm_g_array_index(data->gateways, NMNDiscGateway, i); nm_assert(!IN6_IS_ADDR_UNSPECIFIED(&item->address)); for (j = 0; j < i; j++) { - const NMNDiscGateway *item2 = &g_array_index(data->gateways, NMNDiscGateway, j); + const NMNDiscGateway *item2 = &nm_g_array_index(data->gateways, NMNDiscGateway, j); nm_assert(!IN6_ARE_ADDR_EQUAL(&item->address, &item2->address)); } @@ -344,12 +350,47 @@ _ASSERT_data_gateways(const NMNDiscDataInternal *data) } /*****************************************************************************/ +static bool +is_duplicate_route(const NMNDiscRoute *r0, const NMNDiscRoute *r1) +{ + return IN6_ARE_ADDR_EQUAL(&r0->network, &r1->network) && r0->plen == r1->plen; +} + +static void +_data_complete_prepare_routes(GArray *routes) +{ + guint i, j; + + for (i = 0; i < routes->len; i++) { + NMNDiscRoute *r0 = &nm_g_array_index(routes, NMNDiscRoute, i); + + r0->duplicate = FALSE; + } + for (i = 0; i < routes->len; i++) { + NMNDiscRoute *r0 = &nm_g_array_index(routes, NMNDiscRoute, i); + + for (j = i + 1; j < routes->len; j++) { + NMNDiscRoute *r1 = &nm_g_array_index(routes, NMNDiscRoute, j); + + if (!is_duplicate_route(r0, r1)) + continue; + + r0->duplicate = TRUE; + r1->duplicate = TRUE; + + /* Maybe after index j, there is yet another duplicate. But we + * will find that later, when i becomes j. */ + break; + } + } +} static const NMNDiscData * _data_complete(NMNDiscDataInternal *data) { _ASSERT_data_gateways(data); + _data_complete_prepare_routes(data->routes); #define _SET(data, field) \ G_STMT_START \ { \ @@ -402,7 +443,7 @@ nm_ndisc_add_gateway(NMNDisc *ndisc, const NMNDiscGateway *new_item, gint64 now_ guint insert_idx = G_MAXUINT; for (i = 0; i < rdata->gateways->len;) { - NMNDiscGateway *item = &g_array_index(rdata->gateways, NMNDiscGateway, i); + NMNDiscGateway *item = &nm_g_array_index(rdata->gateways, NMNDiscGateway, i); if (IN6_ARE_ADDR_EQUAL(&item->address, &new_item->address)) { if (new_item->expiry_msec <= now_msec) { @@ -476,12 +517,12 @@ complete_address(NMNDisc *ndisc, NMNDiscAddress *addr) priv->config.network_id, addr->dad_counter++, &error)) { - _LOGW("complete-address: failed to generate an stable-privacy address: %s", + _LOGW("complete-address: failed to generate a stable-privacy address: %s", error->message); g_clear_error(&error); return FALSE; } - _LOGD("complete-address: using an stable-privacy address"); + _LOGD("complete-address: using a stable-privacy address"); return TRUE; } @@ -496,7 +537,7 @@ complete_address(NMNDisc *ndisc, NMNDiscAddress *addr) return TRUE; } - _LOGW("complete-address: can't generate a new_item EUI-64 address"); + _LOGW("complete-address: can't generate a new EUI-64 address"); return FALSE; } @@ -519,7 +560,7 @@ nm_ndisc_add_address(NMNDisc *ndisc, nm_assert((!!from_ra) == (now_msec > 0)); for (i = 0; i < rdata->addresses->len; i++) { - NMNDiscAddress *item = &g_array_index(rdata->addresses, NMNDiscAddress, i); + NMNDiscAddress *item = &nm_g_array_index(rdata->addresses, NMNDiscAddress, i); if (from_ra) { /* RFC4862 5.5.3.d, we find an existing address with the same prefix. @@ -651,10 +692,19 @@ nm_ndisc_add_route(NMNDisc *ndisc, const NMNDiscRoute *new_item, gint64 now_msec rdata = &priv->rdata; for (i = 0; i < rdata->routes->len;) { - NMNDiscRoute *item = &g_array_index(rdata->routes, NMNDiscRoute, i); - - if (IN6_ARE_ADDR_EQUAL(&item->network, &new_item->network) - && item->plen == new_item->plen) { + NMNDiscRoute *item = &nm_g_array_index(rdata->routes, NMNDiscRoute, i); + + /* + * It is possible that two entries in rdata->routes have + * the same prefix as well as the same prefix length. + * One of them, however, refers to the on-link prefix, + * and the other one to a route from the route information field. + * Moreover, they might have different route preferences. + * Hence, if both routes differ in the on-link flag, + * comparison is aborted, and both routes are added. + */ + if (IN6_ARE_ADDR_EQUAL(&item->network, &new_item->network) && item->plen == new_item->plen + && item->on_link == new_item->on_link) { if (new_item->expiry_msec <= now_msec) { g_array_remove_index(rdata->routes, i); return TRUE; @@ -707,7 +757,7 @@ nm_ndisc_add_dns_server(NMNDisc *ndisc, const NMNDiscDNSServer *new_item, gint64 rdata = &priv->rdata; for (i = 0; i < rdata->dns_servers->len; i++) { - NMNDiscDNSServer *item = &g_array_index(rdata->dns_servers, NMNDiscDNSServer, i); + NMNDiscDNSServer *item = &nm_g_array_index(rdata->dns_servers, NMNDiscDNSServer, i); if (IN6_ARE_ADDR_EQUAL(&item->address, &new_item->address)) { if (new_item->expiry_msec <= now_msec) { @@ -746,7 +796,7 @@ nm_ndisc_add_dns_domain(NMNDisc *ndisc, const NMNDiscDNSDomain *new_item, gint64 rdata = &priv->rdata; for (i = 0; i < rdata->dns_domains->len; i++) { - item = &g_array_index(rdata->dns_domains, NMNDiscDNSDomain, i); + item = &nm_g_array_index(rdata->dns_domains, NMNDiscDNSDomain, i); if (nm_streq(item->domain, new_item->domain)) { if (new_item->expiry_msec <= now_msec) { @@ -989,14 +1039,13 @@ announce_router_solicited(NMNDisc *ndisc) void nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd) { - gboolean changed = FALSE; - const struct in6_addr *in6arr; - const char *const *strvarr; - NMDedupMultiIter iter; - const NMPObject *obj; - guint len; - guint i; - gint32 fake_now = NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000; + gboolean changed = FALSE; + const char *const *strvarr; + NMDedupMultiIter iter; + const NMPObject *obj; + guint len; + guint i; + gint32 fake_now = NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000; nm_assert(NM_IS_NDISC(ndisc)); nm_assert(nm_ndisc_get_node_type(ndisc) == NM_NDISC_NODE_TYPE_ROUTER); @@ -1035,15 +1084,19 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd) changed = TRUE; } - in6arr = NULL; - len = 0; + strvarr = NULL; + len = 0; if (l3cd) - in6arr = nm_l3_config_data_get_nameservers(l3cd, AF_INET6, &len); + strvarr = nm_l3_config_data_get_nameservers(l3cd, AF_INET6, &len); for (i = 0; i < len; i++) { + struct in6_addr a; NMNDiscDNSServer n; + if (!nm_utils_dnsname_parse_assert(AF_INET6, strvarr[i], NULL, &a, NULL)) + continue; + n = (NMNDiscDNSServer){ - .address = in6arr[i], + .address = a, .expiry_msec = _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, NM_NDISC_ROUTER_LIFETIME), }; @@ -1221,28 +1274,35 @@ nm_ndisc_stop(NMNDisc *ndisc) } NMNDiscConfigMap -nm_ndisc_dad_failed(NMNDisc *ndisc, const struct in6_addr *address, gboolean emit_changed_signal) +nm_ndisc_dad_failed(NMNDisc *ndisc, GArray *addresses, gboolean emit_changed_signal) { NMNDiscDataInternal *rdata; guint i; + guint j; gboolean changed = FALSE; + g_return_val_if_fail(addresses, NM_NDISC_CONFIG_NONE); + rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata; - for (i = 0; i < rdata->addresses->len;) { - NMNDiscAddress *item = &g_array_index(rdata->addresses, NMNDiscAddress, i); + for (i = 0; i < addresses->len; i++) { + const struct in6_addr *addr = &nm_g_array_index(addresses, struct in6_addr, i); - if (IN6_ARE_ADDR_EQUAL(&item->address, address)) { - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + for (j = 0; j < rdata->addresses->len;) { + NMNDiscAddress *item = &nm_g_array_index(rdata->addresses, NMNDiscAddress, j); - _LOGD("DAD failed for discovered address %s", _nm_utils_inet6_ntop(address, sbuf)); - changed = TRUE; - if (!complete_address(ndisc, item)) { - g_array_remove_index(rdata->addresses, i); - continue; + if (IN6_ARE_ADDR_EQUAL(&item->address, addr)) { + char sbuf[NM_INET_ADDRSTRLEN]; + + _LOGI("DAD failed for discovered address %s", nm_inet6_ntop(addr, sbuf)); + changed = TRUE; + if (!complete_address(ndisc, item)) { + g_array_remove_index(rdata->addresses, j); + continue; + } } + j++; } - i++; } if (emit_changed_signal && changed) @@ -1278,7 +1338,7 @@ _config_changed_log(NMNDisc *ndisc, NMNDiscConfigMap changed) NMNDiscDataInternal *rdata; guint i; char changedstr[CONFIG_MAP_MAX_STR]; - char addrstr[NM_UTILS_INET_ADDRSTRLEN]; + char addrstr[NM_INET_ADDRSTRLEN]; char str_pref[35]; char str_exp[100]; gint64 now_msec; @@ -1303,42 +1363,42 @@ _config_changed_log(NMNDisc *ndisc, NMNDiscConfigMap changed) _LOGD(" retrans timer : %u", (guint) rdata->public.retrans_timer_ms); for (i = 0; i < rdata->gateways->len; i++) { - const NMNDiscGateway *gateway = &g_array_index(rdata->gateways, NMNDiscGateway, i); + const NMNDiscGateway *gateway = &nm_g_array_index(rdata->gateways, NMNDiscGateway, i); _LOGD(" gateway %s pref %s exp %s", - _nm_utils_inet6_ntop(&gateway->address, addrstr), + nm_inet6_ntop(&gateway->address, addrstr), nm_icmpv6_router_pref_to_string(gateway->preference, str_pref, sizeof(str_pref)), get_exp(str_exp, now_msec, gateway)); } for (i = 0; i < rdata->addresses->len; i++) { - const NMNDiscAddress *address = &g_array_index(rdata->addresses, NMNDiscAddress, i); + const NMNDiscAddress *address = &nm_g_array_index(rdata->addresses, NMNDiscAddress, i); _LOGD(" address %s exp %s", - _nm_utils_inet6_ntop(&address->address, addrstr), + nm_inet6_ntop(&address->address, addrstr), get_exp(str_exp, now_msec, address)); } for (i = 0; i < rdata->routes->len; i++) { - const NMNDiscRoute *route = &g_array_index(rdata->routes, NMNDiscRoute, i); - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + const NMNDiscRoute *route = &nm_g_array_index(rdata->routes, NMNDiscRoute, i); + char sbuf[NM_INET_ADDRSTRLEN]; _LOGD(" route %s/%u via %s pref %s exp %s", - _nm_utils_inet6_ntop(&route->network, addrstr), + nm_inet6_ntop(&route->network, addrstr), (guint) route->plen, - _nm_utils_inet6_ntop(&route->gateway, sbuf), + nm_inet6_ntop(&route->gateway, sbuf), nm_icmpv6_router_pref_to_string(route->preference, str_pref, sizeof(str_pref)), get_exp(str_exp, now_msec, route)); } for (i = 0; i < rdata->dns_servers->len; i++) { const NMNDiscDNSServer *dns_server = - &g_array_index(rdata->dns_servers, NMNDiscDNSServer, i); + &nm_g_array_index(rdata->dns_servers, NMNDiscDNSServer, i); _LOGD(" dns_server %s exp %s", - _nm_utils_inet6_ntop(&dns_server->address, addrstr), + nm_inet6_ntop(&dns_server->address, addrstr), get_exp(str_exp, now_msec, dns_server)); } for (i = 0; i < rdata->dns_domains->len; i++) { const NMNDiscDNSDomain *dns_domain = - &g_array_index(rdata->dns_domains, NMNDiscDNSDomain, i); + &nm_g_array_index(rdata->dns_domains, NMNDiscDNSDomain, i); _LOGD(" dns_domain %s exp %s", dns_domain->domain, get_exp(str_exp, now_msec, dns_domain)); } @@ -1370,7 +1430,7 @@ clean_gateways(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint6 if (rdata->gateways->len == 0) return; - arr = &g_array_index(rdata->gateways, NMNDiscGateway, 0); + arr = &nm_g_array_first(rdata->gateways, NMNDiscGateway); for (i = 0, j = 0; i < rdata->gateways->len; i++) { if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec)) @@ -1403,7 +1463,7 @@ clean_addresses(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint if (rdata->addresses->len == 0) return; - arr = &g_array_index(rdata->addresses, NMNDiscAddress, 0); + arr = &nm_g_array_first(rdata->addresses, NMNDiscAddress); for (i = 0, j = 0; i < rdata->addresses->len; i++) { if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec)) @@ -1433,7 +1493,7 @@ clean_routes(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64 if (rdata->routes->len == 0) return; - arr = &g_array_index(rdata->routes, NMNDiscRoute, 0); + arr = &nm_g_array_first(rdata->routes, NMNDiscRoute); for (i = 0, j = 0; i < rdata->routes->len; i++) { if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec)) @@ -1463,7 +1523,7 @@ clean_dns_servers(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gi if (rdata->dns_servers->len == 0) return; - arr = &g_array_index(rdata->dns_servers, NMNDiscDNSServer, 0); + arr = &nm_g_array_first(rdata->dns_servers, NMNDiscDNSServer); for (i = 0, j = 0; i < rdata->dns_servers->len; i++) { if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec)) @@ -1493,7 +1553,7 @@ clean_dns_domains(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gi if (rdata->dns_domains->len == 0) return; - arr = &g_array_index(rdata->dns_domains, NMNDiscDNSDomain, 0); + arr = &nm_g_array_first(rdata->dns_domains, NMNDiscDNSDomain); for (i = 0, j = 0; i < rdata->dns_domains->len; i++) { if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec)) @@ -1602,34 +1662,35 @@ calc_pre_expiry_rs_msec(NMNDisc *ndisc) _calc_pre_expiry_rs_msec_worker( &expiry_msec, priv->last_rs_msec, - g_array_index(rdata->gateways, NMNDiscGateway, i).expiry_msec); + nm_g_array_index(rdata->gateways, NMNDiscGateway, i).expiry_msec); } for (i = 0; i < rdata->addresses->len; i++) { _calc_pre_expiry_rs_msec_worker( &expiry_msec, priv->last_rs_msec, - g_array_index(rdata->addresses, NMNDiscAddress, i).expiry_msec); + nm_g_array_index(rdata->addresses, NMNDiscAddress, i).expiry_msec); } for (i = 0; i < rdata->routes->len; i++) { - _calc_pre_expiry_rs_msec_worker(&expiry_msec, - priv->last_rs_msec, - g_array_index(rdata->routes, NMNDiscRoute, i).expiry_msec); + _calc_pre_expiry_rs_msec_worker( + &expiry_msec, + priv->last_rs_msec, + nm_g_array_index(rdata->routes, NMNDiscRoute, i).expiry_msec); } for (i = 0; i < rdata->dns_servers->len; i++) { _calc_pre_expiry_rs_msec_worker( &expiry_msec, priv->last_rs_msec, - g_array_index(rdata->dns_servers, NMNDiscDNSServer, i).expiry_msec); + nm_g_array_index(rdata->dns_servers, NMNDiscDNSServer, i).expiry_msec); } for (i = 0; i < rdata->dns_domains->len; i++) { _calc_pre_expiry_rs_msec_worker( &expiry_msec, priv->last_rs_msec, - g_array_index(rdata->dns_domains, NMNDiscDNSDomain, i).expiry_msec); + nm_g_array_index(rdata->dns_domains, NMNDiscDNSDomain, i).expiry_msec); } return expiry_msec - solicit_retransmit_time_jitter(NM_NDISC_PRE_EXPIRY_TIME_MSEC); diff --git a/src/core/ndisc/nm-ndisc.h b/src/core/ndisc/nm-ndisc.h index 079ec037..8f1a12a2 100644 --- a/src/core/ndisc/nm-ndisc.h +++ b/src/core/ndisc/nm-ndisc.h @@ -8,7 +8,7 @@ #include #include -#include +#include "nm-compat-headers/linux/if_addr.h" #include "nm-setting-ip6-config.h" #include "NetworkManagerUtils.h" @@ -20,7 +20,7 @@ #define NM_RA_TIMEOUT_INFINITY ((guint32) G_MAXINT32) #define NM_TYPE_NDISC (nm_ndisc_get_type()) -#define NM_NDISC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_NDISC, NMNDisc)) +#define NM_NDISC(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_NDISC, NMNDisc)) #define NM_NDISC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_NDISC, NMNDiscClass)) #define NM_IS_NDISC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_NDISC)) #define NM_IS_NDISC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_NDISC)) @@ -115,6 +115,8 @@ typedef struct _NMNDiscRoute { gint64 expiry_msec; NMIcmpv6RouterPref preference; guint8 plen; + bool on_link : 1; + bool duplicate : 1; } NMNDiscRoute; typedef struct { @@ -228,9 +230,9 @@ NMNDiscNodeType nm_ndisc_get_node_type(NMNDisc *self); gboolean nm_ndisc_set_iid(NMNDisc *ndisc, const NMUtilsIPv6IfaceId iid, gboolean is_token); void nm_ndisc_start(NMNDisc *ndisc); void nm_ndisc_stop(NMNDisc *ndisc); +void nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd); NMNDiscConfigMap -nm_ndisc_dad_failed(NMNDisc *ndisc, const struct in6_addr *address, gboolean emit_changed_signal); -void nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd); +nm_ndisc_dad_failed(NMNDisc *ndisc, GArray *addresses, gboolean emit_changed_signal); NMPlatform *nm_ndisc_get_platform(NMNDisc *self); NMPNetns *nm_ndisc_netns_get(NMNDisc *self); diff --git a/src/core/nm-act-request.h b/src/core/nm-act-request.h index 9075d5b7..4c85735f 100644 --- a/src/core/nm-act-request.h +++ b/src/core/nm-act-request.h @@ -10,7 +10,8 @@ #include "nm-active-connection.h" #define NM_TYPE_ACT_REQUEST (nm_act_request_get_type()) -#define NM_ACT_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_ACT_REQUEST, NMActRequest)) +#define NM_ACT_REQUEST(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_ACT_REQUEST, NMActRequest)) #define NM_ACT_REQUEST_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_ACT_REQUEST, NMActRequestClass)) #define NM_IS_ACT_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_ACT_REQUEST)) diff --git a/src/core/nm-active-connection.c b/src/core/nm-active-connection.c index 49568f7c..4488d909 100644 --- a/src/core/nm-active-connection.c +++ b/src/core/nm-active-connection.c @@ -116,22 +116,22 @@ static void auth_complete(NMActiveConnection *self, gboolean result, const char #define _NMLOG_DOMAIN LOGD_DEVICE #define _NMLOG_PREFIX_NAME "active-connection" -#define _NMLOG(level, ...) \ - G_STMT_START \ - { \ - char _sbuf[64]; \ - NMActiveConnectionPrivate *_priv = self ? NM_ACTIVE_CONNECTION_GET_PRIVATE(self) : NULL; \ - \ - nm_log((level), \ - _NMLOG_DOMAIN, \ - (_priv && _priv->device) ? nm_device_get_iface(_priv->device) : NULL, \ - (_priv && _priv->applied_connection) \ - ? nm_connection_get_uuid(_priv->applied_connection) \ - : NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - self ? nm_sprintf_buf(_sbuf, "[%p]", self) : "" _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ +#define _NMLOG(level, ...) \ + G_STMT_START \ + { \ + char _sbuf[64]; \ + NMActiveConnectionPrivate *_priv = self ? NM_ACTIVE_CONNECTION_GET_PRIVATE(self) : NULL; \ + \ + nm_log((level), \ + _NMLOG_DOMAIN, \ + (_priv && _priv->device) ? nm_device_get_iface(_priv->device) : NULL, \ + (_priv && _priv->applied_connection) \ + ? nm_connection_get_uuid(_priv->applied_connection) \ + : NULL, \ + "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + NM_HASH_OBFUSCATE_PTR_STR(self, _sbuf) _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ G_STMT_END /*****************************************************************************/ diff --git a/src/core/nm-active-connection.h b/src/core/nm-active-connection.h index 28d4246e..a2353a65 100644 --- a/src/core/nm-active-connection.h +++ b/src/core/nm-active-connection.h @@ -12,7 +12,7 @@ #define NM_TYPE_ACTIVE_CONNECTION (nm_active_connection_get_type()) #define NM_ACTIVE_CONNECTION(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnection)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnection)) #define NM_ACTIVE_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnectionClass)) #define NM_IS_ACTIVE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_ACTIVE_CONNECTION)) diff --git a/src/core/nm-audit-manager.h b/src/core/nm-audit-manager.h index 3a7ea7c0..47ab8b76 100644 --- a/src/core/nm-audit-manager.h +++ b/src/core/nm-audit-manager.h @@ -12,7 +12,7 @@ #define NM_TYPE_AUDIT_MANAGER (nm_audit_manager_get_type()) #define NM_AUDIT_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_AUDIT_MANAGER, NMAuditManager)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_AUDIT_MANAGER, NMAuditManager)) #define NM_AUDIT_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_AUDIT_MANAGER, NMAuditManagerClass)) #define NM_IS_AUDIT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_AUDIT_MANAGER)) diff --git a/src/core/nm-auth-manager.c b/src/core/nm-auth-manager.c index 0f7e0c0d..242f0055 100644 --- a/src/core/nm-auth-manager.c +++ b/src/core/nm-auth-manager.c @@ -524,7 +524,7 @@ _name_owner_get_cb(const char *name_owner, GError *error, gpointer user_data) /*****************************************************************************/ NMAuthManager * -nm_auth_manager_get() +nm_auth_manager_get(void) { g_return_val_if_fail(singleton_instance, NULL); diff --git a/src/core/nm-auth-manager.h b/src/core/nm-auth-manager.h index db6237d8..4ebad11b 100644 --- a/src/core/nm-auth-manager.h +++ b/src/core/nm-auth-manager.h @@ -40,7 +40,7 @@ nm_auth_call_result_eval(gboolean is_authorized, gboolean is_challenge, GError * #define NM_TYPE_AUTH_MANAGER (nm_auth_manager_get_type()) #define NM_AUTH_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_AUTH_MANAGER, NMAuthManager)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_AUTH_MANAGER, NMAuthManager)) #define NM_AUTH_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_AUTH_MANAGER, NMAuthManagerClass)) #define NM_IS_AUTH_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_AUTH_MANAGER)) diff --git a/src/core/nm-bond-manager.c b/src/core/nm-bond-manager.c new file mode 100644 index 00000000..2d15b0b5 --- /dev/null +++ b/src/core/nm-bond-manager.c @@ -0,0 +1,967 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "src/core/nm-default-daemon.h" + +#include "nm-bond-manager.h" + +#include + +#include "NetworkManagerUtils.h" +#include "libnm-core-aux-intern/nm-libnm-core-utils.h" +#include "libnm-glib-aux/nm-str-buf.h" +#include "libnm-platform/nm-platform.h" +#include "libnm-platform/nmp-object.h" +#include "nm-firewall-utils.h" + +/*****************************************************************************/ + +typedef enum _nm_packed { + REGISTRATION_STATE_NONE, + REGISTRATION_STATE_UPPING, + REGISTRATION_STATE_UP, + REGISTRATION_STATE_DOWNING, +} RegistrationState; + +struct _NMBondManager { + NMPlatform *platform; + + NMBondManagerCallback callback; + gpointer user_data; + + /* This is only used for structured logging. */ + char *connection_uuid; + + GSource *reconfigure_on_idle_source; + + /* During _reconfigure_check() we remember all ifindexes that are part + * of the current SLB bond. This is used during _link_changed_cb() to + * figure out whether a change on the interface might be relevant to + * trigger a _reconfigure_check() on idle. */ + GHashTable *previous_ifindexes; + + /* We need to keep track of active members that we configured in NFT. + * That is, because on update we use "add && flush" to reset the table, + * however that leaves empty chains around. If we previously had an active + * member, a chain for it was created that we need to clean up. + * + * Before every NFT call we use this to generate the list of members that + * are to be cleaned up. Thereby also adding the new active-memebers to + * the list. When the NFT calls returns with success, we can prune the + * now deleted member/chain. */ + GHashTable *previous_members; + + GCancellable *cancellable; + + struct { + char *bond_ifname_curr; + char *bond_ifname_next; + const char **active_members_curr; + const char **active_members_next; + } dat; + + gulong link_changed_id; + int ifindex; + RegistrationState reg_state; + bool destroyed : 1; + + /* Whether we noticed some changes that require us to _reconfigure_check(). + * Note that while a NFT call is pending, we postpone the check. */ + bool reconfigure_check : 1; + + /* Whether a `nft` call is in progress. Usually this corresponds to + * having a cancellable, however, we may also cancel and clear the + * cancellable while the call is still in progress. */ + bool nft_in_progress : 1; + + /* Whether the last NFT invocation was good. If not, we may have + * an invalid state. Actually unused, so far because it's not + * clear what to do about failure to configure NFT (aside logging + * a warning). */ + bool nft_good : 1; + + /* The overall state. DEFAULT means that an update is pending. + * FALSE means that the last "nft" command failed. + * TRUE means that the last "nft" command was good. */ + NMOptionBool state : 3; +}; + +#define NM_IS_BOND_MANAGER(self) \ + ({ \ + const NMBondManager *_self = (self); \ + \ + (_self && NM_IS_PLATFORM(_self->platform)); \ + }) + +/*****************************************************************************/ + +static void _nft_call(NMBondManager *self, + gboolean up, + const char *bond_ifname, + const char *const *bond_ifnames_down, + const char *const *active_members); + +static void _bond_manager_destroy(NMBondManager *self); + +static void _reconfigure_check(NMBondManager *self, gboolean reapply); + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_DEVICE +#define _NMLOG_PREFIX_NAME "mlag" +#define _NMLOG(level, ...) \ + G_STMT_START \ + { \ + const NMLogLevel _level = (level); \ + \ + if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) { \ + NMBondManager *const _self = (self); \ + const char *_ifname = nm_platform_link_get_name(_self->platform, _self->ifindex); \ + char _sbuf[30]; \ + \ + _nm_log(_level, \ + _NMLOG_DOMAIN, \ + 0, \ + _ifname, \ + _self->connection_uuid, \ + "%s[" NM_HASH_OBFUSCATE_PTR_FMT ", %s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + NM_HASH_OBFUSCATE_PTR(_self), \ + (_ifname ?: nm_sprintf_buf(_sbuf, "(%d)", _self->ifindex)) \ + _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } \ + G_STMT_END + +static const char * +_log_info(NMStrBuf *strbuf, + const char *bond_ifname, + const char *const *active_members, + const char *const *previous_members) +{ + gsize i; + + nm_str_buf_reset(strbuf); + + if (!bond_ifname) + nm_str_buf_append(strbuf, "(disabled)"); + else { + nm_str_buf_append_printf(strbuf, "(enabled, \"%s\"", bond_ifname); + + for (i = 0; active_members && active_members[i]; i++) { + if (i == 0) + nm_str_buf_append(strbuf, ", active-members=[ \""); + else + nm_str_buf_append(strbuf, "\", \""); + nm_str_buf_append(strbuf, active_members[i]); + } + if (i > 0) + nm_str_buf_append(strbuf, "\" ]"); + + for (i = 0; previous_members && previous_members[i]; i++) { + nm_assert(!nm_strv_contains(active_members, -1, previous_members[i])); + if (i == 0) + nm_str_buf_append(strbuf, ", previous-members=[ \""); + else + nm_str_buf_append(strbuf, "\", \""); + nm_str_buf_append(strbuf, previous_members[i]); + } + if (i > 0) + nm_str_buf_append(strbuf, "\" ]"); + + nm_str_buf_append(strbuf, ")"); + } + + return nm_str_buf_get_str(strbuf); +} + +/*****************************************************************************/ + +static gboolean +_nm_assert_self_(NMBondManager *self) +{ + nm_assert(self); + nm_assert(NM_IS_PLATFORM(self->platform)); + nm_assert(!self->cancellable || G_IS_CANCELLABLE(self->cancellable)); + nm_assert(!self->cancellable || !g_cancellable_is_cancelled(self->cancellable)); + nm_assert(!self->dat.active_members_curr || self->dat.bond_ifname_curr); + nm_assert(!self->dat.active_members_next || self->dat.bond_ifname_next); + nm_assert(!self->cancellable || self->nft_in_progress); + nm_assert(!self->reconfigure_on_idle_source || self->reconfigure_check); + nm_assert(!self->nft_in_progress || !self->reconfigure_on_idle_source); + + nm_assert(!self->dat.active_members_curr || self->dat.bond_ifname_curr[0]); + nm_assert(!self->dat.active_members_next || self->dat.bond_ifname_next[0]); + + nm_assert(!self->destroyed || !self->dat.bond_ifname_next); + nm_assert(!self->destroyed + || NM_IN_SET((RegistrationState) self->reg_state, + REGISTRATION_STATE_UPPING, + REGISTRATION_STATE_DOWNING)); + + switch (self->reg_state) { + case REGISTRATION_STATE_NONE: + nm_assert(!self->nft_in_progress); + nm_assert(!self->cancellable); + nm_assert(!self->dat.bond_ifname_curr); + nm_assert(!self->dat.bond_ifname_next); + break; + case REGISTRATION_STATE_UPPING: + nm_assert(self->nft_in_progress); + nm_assert(self->dat.bond_ifname_curr); + break; + case REGISTRATION_STATE_UP: + nm_assert(!self->nft_in_progress); + nm_assert(!self->cancellable); + nm_assert(self->dat.bond_ifname_curr); + nm_assert(!self->dat.bond_ifname_next); + break; + case REGISTRATION_STATE_DOWNING: + nm_assert(self->nft_in_progress); + nm_assert(self->dat.bond_ifname_curr); + break; + default: + nm_assert_not_reached(); + break; + } + + return TRUE; +} + +#define _nm_assert_self(self) nm_assert(_nm_assert_self_(self)) + +/*****************************************************************************/ + +static void +_callback_invoke(NMBondManager *self, NMBondManagerEventType event_type) +{ + if (!self->callback) + return; + + self->callback(self, event_type, self->user_data); +} + +static void +_notify_state_change(NMBondManager *self) +{ + NMOptionBool state; + + if (self->nft_in_progress) + state = NM_OPTION_BOOL_DEFAULT; + else + state = !!self->nft_good; + + if (state == self->state) + return; + + self->state = state; + _callback_invoke(self, NM_BOND_MANAGER_EVENT_TYPE_STATE); +} + +/*****************************************************************************/ + +static void +_nft_call_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_232, FALSE); + NMBondManager *self; + gpointer ptr_up; + gs_free const char **previous_members = NULL; + gs_free_error GError *error = NULL; + + nm_utils_user_data_unpack(user_data, &self, &ptr_up, &previous_members); + + _nm_assert_self(self); + + self->nft_in_progress = FALSE; + + nm_firewall_nft_call_finish(result, &error); + + if (!error) { + gsize i; + + /* On success, we can forget about our previous members that we successfully + * deleted. */ + if (!GPOINTER_TO_INT(ptr_up)) { + /* We successfully deleted the NFT table. Forget all previous members. */ + g_hash_table_remove_all(self->previous_members); + } else if (previous_members) { + /* These previous members are now forgotten for good. */ + for (i = 0; previous_members[i]; i++) + g_hash_table_remove(self->previous_members, previous_members[i]); + } + } else { + /* If all our NFT calls keep failing, we never actually prune entries from + * self->previous_members. That is a problem, however, under normal operation + * NFT calls should not continuously fail, and we would have a small fixed + * number of active-members. */ + } + + nm_clear_g_cancellable(&self->cancellable); + + if (nm_utils_error_is_cancelled(error)) { + switch (self->reg_state) { + case REGISTRATION_STATE_NONE: + case REGISTRATION_STATE_UP: + case REGISTRATION_STATE_DOWNING: + /* It is not expected that we cancel anything in this state. */ + nm_assert_not_reached(); + goto out; + case REGISTRATION_STATE_UPPING: + nm_assert(self->dat.bond_ifname_curr); + /* We cancelled while upping. We need to issue another down, + * to make sure the data is gone. */ + if (!self->dat.bond_ifname_next) { + /* There is no other name to configure. We just need to down + * the current one. */ + _LOGT("reconfigure: configuration cancelled, deconfigure %s", + self->dat.bond_ifname_curr); + _nft_call(self, FALSE, self->dat.bond_ifname_curr, NULL, NULL); + self->reg_state = REGISTRATION_STATE_DOWNING; + goto out; + } + /* There is already another configuration. UPPING again. */ + _LOGT("reconfigure: configuration cancelled, configure %s", + _log_info(&strbuf, + self->dat.bond_ifname_next, + self->dat.active_members_next, + NULL)); + _nft_call(self, + TRUE, + self->dat.bond_ifname_next, + NM_MAKE_STRV(self->dat.bond_ifname_curr), + self->dat.active_members_next); + self->reg_state = REGISTRATION_STATE_UPPING; + nm_clear_g_free(&self->dat.bond_ifname_curr); + nm_clear_g_free(&self->dat.active_members_curr); + self->dat.bond_ifname_curr = g_steal_pointer(&self->dat.bond_ifname_next); + self->dat.active_members_curr = g_steal_pointer(&self->dat.active_members_next); + goto out; + } + nm_assert_not_reached(); + goto out; + } + + if (error) { + self->nft_good = FALSE; + } else { + /* Technically, if a previous downing failed, we cannot know that + * we were able to fix this bug a successful run now. That is, because + * if the interface got renamed, and the downing for the previous + * interface name failed, we leak that table and the success now doesn't + * fix that. + * + * That is a bug, but probably not severe because: + * - interfaces are not supposed to be renamed. + * - if this NFT command succeed, we expect that also the previous downings worked. + * + * The problem here is only that nft_good might lie and indicate + * no problem. However, when a downing fails, we anyway leak the table already + * and the bad thing happend. We cannot fix if `nft` command fails. + */ + self->nft_good = TRUE; + } + + switch (self->reg_state) { + case REGISTRATION_STATE_NONE: + case REGISTRATION_STATE_UP: + /* Unexpected to get a callback completion in these states. */ + nm_assert_not_reached(); + goto out; + case REGISTRATION_STATE_UPPING: + nm_assert(!self->dat.bond_ifname_next); + if (error) { + /* Unclear what to do about this error. Just log about it, nothing else. */ + _LOGW("reconfigure: nft configuration for balance-slb failed: %s", error->message); + } else + _LOGT("reconfigure: configuration completed"); + self->reg_state = REGISTRATION_STATE_UP; + goto out; + case REGISTRATION_STATE_DOWNING: + nm_assert(self->dat.bond_ifname_curr); + if (!self->dat.bond_ifname_next) { + if (error) { + /* Unclear what to do about this error. Just log about it, nothing else. */ + _LOGW("reconfigure: nft deconfiguration for balance-slb failed: %s", + error->message); + } else + _LOGT("reconfigure: deconfiguration completed"); + nm_clear_g_free(&self->dat.bond_ifname_curr); + nm_clear_g_free(&self->dat.active_members_curr); + self->reg_state = REGISTRATION_STATE_NONE; + + if (self->destroyed) { + _bond_manager_destroy(self); + return; + } + + goto out; + } + if (error) { + /* Unclear what to do about this error. Just log about it, nothing else. */ + _LOGW("reconfigure: nft deconfiguration failed before restart: %s", error->message); + } else + _LOGT("reconfigure: deconfiguration completed before restart"); + _nft_call(self, + TRUE, + self->dat.bond_ifname_next, + NM_MAKE_STRV(self->dat.bond_ifname_curr), + self->dat.active_members_next); + nm_clear_g_free(&self->dat.bond_ifname_curr); + nm_clear_g_free(&self->dat.active_members_curr); + self->dat.bond_ifname_curr = g_steal_pointer(&self->dat.bond_ifname_next); + self->dat.active_members_curr = g_steal_pointer(&self->dat.active_members_next); + self->reg_state = REGISTRATION_STATE_UPPING; + goto out; + } + + nm_assert_not_reached(); + +out: + if (self->reconfigure_check) { + if (self->destroyed) + nm_assert_not_reached(); + else if (!self->nft_in_progress) { + nm_assert(!self->reconfigure_on_idle_source); + _reconfigure_check(self, FALSE); + } + } + + _notify_state_change(self); +} + +static void +_nft_call(NMBondManager *self, + gboolean up, + const char *bond_ifname, + const char *const *bond_ifnames_down, + const char *const *active_members) +{ + gs_unref_bytes GBytes *stdin_buf = NULL; + gs_free const char *const *previous_members_strv = NULL; + + if (up) { + gs_unref_ptrarray GPtrArray *arr = NULL; + GHashTableIter iter; + const char *n; + gsize i; + + /* We need to track the active-members that we add, because, when we update the + * NFT table without the member from previously, we use "add && flush", which + * leaves empty chains for the previous members around. We need to cleanup those + * chains, hence the need to track which members we ever added. + * + * Before making an UP call, we add the newly configured active_members to the list + * of previous_members. All the while, passing a list of previous_members_strv + * which we currently no longer configure. + * + * Only when the call succeeds (in _nft_call_cb()), we will forget about previously added + * members. This is done by passing the list of members that we are forgetting now + * on to the callback below. */ + + /* Get the list of previous members that are no longer in the current + * active list. */ + g_hash_table_iter_init(&iter, self->previous_members); + while (g_hash_table_iter_next(&iter, (gpointer *) &n, NULL)) { + if (nm_strv_contains(active_members, -1, n)) + continue; + if (!arr) + arr = g_ptr_array_new(); + g_ptr_array_add(arr, (gpointer) n); + } + if (arr) { + nm_strv_sort((const char **) arr->pdata, arr->len); + previous_members_strv = nm_strv_dup_packed((const char *const *) arr->pdata, arr->len); + } + + /* The now active member also get tracked as previous members for the future. */ + if (active_members) { + for (i = 0; active_members[i]; i++) + g_hash_table_add(self->previous_members, g_strdup(active_members[i])); + } + } + + stdin_buf = nm_firewall_nft_stdio_mlag(up, + bond_ifname, + bond_ifnames_down, + active_members, + previous_members_strv); + + nm_clear_g_cancellable(&self->cancellable); + self->cancellable = g_cancellable_new(); + + nm_shutdown_wait_obj_register_cancellable(self->cancellable, "nft-mlag"); + + if (_LOGT_ENABLED()) { + if (up) { + nm_auto_str_buf NMStrBuf strbuf = + NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_232, FALSE); + + _LOGT("reconfigure: call nft: %s", + _log_info(&strbuf, bond_ifname, active_members, previous_members_strv)); + } else + _LOGT("reconfigure: call nft: disable on \"%s\"", bond_ifname); + } + + self->nft_in_progress = TRUE; + + if (self->reconfigure_check) + nm_clear_g_source_inst(&self->reconfigure_on_idle_source); + + nm_firewall_nft_call(stdin_buf, + self->cancellable, + _nft_call_cb, + nm_utils_user_data_pack(self, + GINT_TO_POINTER(up), + g_steal_pointer(&previous_members_strv))); +} + +/*****************************************************************************/ + +static void +_reconfigure_do(NMBondManager *self, + gboolean reapply, + const char *bond_ifname, + const char **active_members_take) +{ + nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_232, FALSE); + gs_free const char **active_members = g_steal_pointer(&active_members_take); + + _nm_assert_self(self); + nm_assert(!active_members || bond_ifname); + nm_assert(!active_members || active_members[0]); + + /* The difficulty of all of this is "state". In particular, since we make the nft call + * async, we need to handle all the possible cases, how an update event can invalidate + * a currently pending call. */ + + switch (self->reg_state) { + case REGISTRATION_STATE_NONE: + nm_assert(!self->dat.bond_ifname_curr); + nm_assert(!self->dat.active_members_curr); + nm_assert(!self->dat.bond_ifname_next); + nm_assert(!self->dat.active_members_next); + nm_assert(!self->cancellable); + nm_assert(!self->nft_in_progress); + + if (!bond_ifname) { + /* No configuration done. Nothing to do. */ + goto out; + } + + _LOGT("reconfigure: start configuring (%s)", + _log_info(&strbuf, bond_ifname, active_members, NULL)); + self->dat.bond_ifname_curr = g_strdup(bond_ifname); + self->dat.active_members_curr = nm_strv_dup_packed(active_members, -1); + _nft_call(self, TRUE, self->dat.bond_ifname_curr, NULL, self->dat.active_members_curr); + self->reg_state = REGISTRATION_STATE_UPPING; + goto out; + case REGISTRATION_STATE_UPPING: + nm_assert(self->dat.bond_ifname_curr); + nm_assert(self->nft_in_progress); + + /* We are UPPING, we cancel the pending operation and will + * handle the rest when the callback completes. */ + if (!bond_ifname) { + if (self->cancellable || self->dat.bond_ifname_next) + _LOGT("reconfigure: aborting configuring"); + nm_clear_g_free(&self->dat.bond_ifname_next); + nm_clear_g_free(&self->dat.active_members_next); + nm_clear_g_cancellable(&self->cancellable); + goto out; + } + if (!reapply && self->cancellable && nm_streq0(bond_ifname, self->dat.bond_ifname_curr) + && nm_strv_equal(active_members, self->dat.active_members_curr)) { + /* Nothing to do. We are already upping this setup. */ + nm_assert(!self->dat.bond_ifname_next); + nm_assert(!self->dat.active_members_next); + goto out; + } + if (!reapply && !self->cancellable && nm_streq0(bond_ifname, self->dat.bond_ifname_next) + && nm_strv_equal(active_members, self->dat.active_members_next)) { + /* We already cancelled the current upping, and have scheduled another + * (identical) run. Nothing to do. */ + goto out; + } + _LOGT("reconfigure: abort configuring to configure %s", + _log_info(&strbuf, bond_ifname, active_members, NULL)); + nm_clear_g_free(&self->dat.bond_ifname_next); + nm_clear_g_free(&self->dat.active_members_next); + self->dat.bond_ifname_next = g_strdup(bond_ifname); + self->dat.active_members_next = nm_strv_dup_packed(active_members, -1); + nm_clear_g_cancellable(&self->cancellable); + goto out; + case REGISTRATION_STATE_UP: + nm_assert(self->dat.bond_ifname_curr); + nm_assert(!self->dat.bond_ifname_next); + nm_assert(!self->dat.active_members_next); + nm_assert(!self->cancellable); + nm_assert(!self->nft_in_progress); + + if (!bond_ifname) { + _LOGT("reconfigure: deconfigure to disable"); + _nft_call(self, FALSE, self->dat.bond_ifname_curr, NULL, NULL); + self->reg_state = REGISTRATION_STATE_DOWNING; + goto out; + } + if (!reapply && nm_streq0(bond_ifname, self->dat.bond_ifname_curr) + && nm_strv_equal(active_members, self->dat.active_members_curr)) { + /* Nothing to do. The current configuration is already active. */ + goto out; + } + _LOGT("reconfigure: configure, update to %s", + _log_info(&strbuf, bond_ifname, active_members, NULL)); + _nft_call(self, + TRUE, + bond_ifname, + NM_MAKE_STRV(self->dat.bond_ifname_curr), + active_members); + self->reg_state = REGISTRATION_STATE_UPPING; + nm_clear_g_free(&self->dat.bond_ifname_curr); + nm_clear_g_free(&self->dat.active_members_curr); + self->dat.bond_ifname_curr = g_strdup(bond_ifname); + self->dat.active_members_curr = nm_strv_dup_packed(active_members, -1); + goto out; + case REGISTRATION_STATE_DOWNING: + nm_assert(self->dat.bond_ifname_curr); + nm_assert(self->nft_in_progress); + + /* we are already DOWNING. It suffices to clear the scheduled "next" + * config and wait, and reset the "next" configuration. */ + if (nm_streq0(bond_ifname, self->dat.bond_ifname_next) + && nm_strv_equal(active_members, self->dat.active_members_next)) { + /* Nothing to do. */ + goto out; + } + _LOGT("reconfigure: deconfiguring and waiting for %s", + _log_info(&strbuf, bond_ifname, active_members, NULL)); + nm_clear_g_free(&self->dat.bond_ifname_next); + nm_clear_g_free(&self->dat.active_members_next); + if (bond_ifname) { + self->dat.bond_ifname_next = g_strdup(bond_ifname); + self->dat.active_members_next = nm_strv_dup_packed(active_members, -1); + } + goto out; + } + nm_assert_not_reached(); + +out: + _notify_state_change(self); +} + +static void +_reconfigure_check(NMBondManager *self, gboolean reapply) +{ + const NMPlatformLink *plink_ctrl; + const NMPlatformLink *plink_port; + const NMPlatformLnkBond *plnkbond_ctrl; + NMDedupMultiIter pliter; + const NMDedupMultiHeadEntry *pl_links_head_entry; + const char *active_members_lst_stack[16]; + gs_free const char **active_members_lst_heap = NULL; + const char **active_members_lst = active_members_lst_stack; + gsize active_members_alloc = G_N_ELEMENTS(active_members_lst_stack); + gsize active_members_n = 0; + gs_free const char **active_members_result = NULL; + const char *bond_ifname = NULL; + + _nm_assert_self(self); + nm_assert(!self->destroyed); + + self->reconfigure_check = FALSE; + nm_clear_g_source_inst(&self->reconfigure_on_idle_source); + + g_hash_table_remove_all(self->previous_ifindexes); + + plnkbond_ctrl = nm_platform_link_get_lnk_bond(self->platform, self->ifindex, &plink_ctrl); + + /* We only do bonding-slb MLAG handling if our ifindex is a bond with + * mode=balance-xor && xmit_hash_policy=vlan+srcmac. */ + if (!plnkbond_ctrl) + goto out; + if (!plink_ctrl) + goto out; + if (plink_ctrl->type != NM_LINK_TYPE_BOND) + goto out; + if (plnkbond_ctrl->mode != NM_BOND_MODE_XOR) + goto out; + if (plnkbond_ctrl->xmit_hash_policy != NM_BOND_XMIT_HASH_POLICY_VLAN_SRCMAC) + goto out; + + /* Find all the connected ports that are IFF_RUNNING. */ + pl_links_head_entry = nm_platform_lookup_obj_type(self->platform, NMP_OBJECT_TYPE_LINK); + nmp_cache_iter_for_each_link (&pliter, pl_links_head_entry, &plink_port) { + if (plink_port->master != self->ifindex) + continue; + if (!NM_FLAGS_HAS(plink_port->n_ifi_flags, IFF_RUNNING)) + continue; + + g_hash_table_add(self->previous_ifindexes, GINT_TO_POINTER(plink_port->ifindex)); + + if (active_members_n == active_members_alloc) { + active_members_alloc *= 2; + active_members_lst_heap = + g_renew(const char *, active_members_lst_heap, active_members_alloc); + if (active_members_lst == active_members_lst_stack) { + memcpy(active_members_lst_heap, + active_members_lst_stack, + sizeof(const char *) * active_members_n); + } + active_members_lst = active_members_lst_heap; + } + + active_members_lst[active_members_n++] = plink_port->name; + } + + if (active_members_n > 0) { + gsize i; + gsize j; + + /* We sort the active members by name */ + g_qsort_with_data(active_members_lst, + active_members_n, + sizeof(const char *), + nm_strcmp_p_with_data, + NULL); + + /* There really shouldn't be any duplicates. Nonetheless, check + * and drop them. They must be unique, because nm_firewall_nft_stdio_mlag() + * relies on that. */ + for (j = 1, i = 1; i < active_members_n; i++) { + if (nm_streq(active_members_lst[j - 1], active_members_lst[i])) { + /* Repeated. Skip. */ + continue; + } + if (j != i) + active_members_lst[j] = active_members_lst[i]; + j++; + } + active_members_n = j; + + active_members_result = g_new(const char *, active_members_n + 1u); + j = 0; + + if (self->dat.active_members_curr) { + /* We configured a list earlier. We want to preserve the sort order + * from before. Prefer entries that we already had, in their previous + * order. */ + for (i = 0; self->dat.active_members_curr[i]; i++) { + gssize idx; + + /* We cannot use binary search, because we steal the elements we found + * already. Hence this is O(n^2). We could use binary search if we would + * not modify active_members_lst, but then we would need to remember + * somehow which elements are already consumed. */ + idx = nm_strv_find_first(active_members_lst, + active_members_n, + self->dat.active_members_curr[i]); + if (idx >= 0) + active_members_result[j++] = g_steal_pointer(&active_members_lst[idx]); + } + } + + /* append the remaining entries, which are sorted by name. */ + for (i = 0; i < active_members_n; i++) { + if (active_members_lst[i]) + active_members_result[j++] = active_members_lst[i]; + } + + nm_assert(j == active_members_n); + active_members_result[j] = NULL; + } + + bond_ifname = plink_ctrl->name; + +out: + _reconfigure_do(self, reapply, bond_ifname, g_steal_pointer(&active_members_result)); +} + +static gboolean +_reconfigure_check_on_idle_cb(gpointer user_data) +{ + NMBondManager *self = user_data; + + nm_assert(!self->nft_in_progress); + _reconfigure_check(self, FALSE); + return G_SOURCE_CONTINUE; +} + +/*****************************************************************************/ + +static void +_link_changed_cb(NMPlatform *platform, + int obj_type_i, + int ifindex, + const NMPlatformLink *plink, + int change_type_i, + NMBondManager *self) +{ + if (self->reconfigure_check) { + /* Recheck already scheduled. */ + return; + } + + if (self->destroyed) { + /* We should not get another event at this point. Anyway, ignore. */ + return; + } + + if (ifindex == self->ifindex) + goto schedule; + + if (plink->master == self->ifindex) + goto schedule; + + if (g_hash_table_contains(self->previous_ifindexes, GINT_TO_POINTER(ifindex))) + goto schedule; + + /* This event is not relevant. Skip. */ + return; + +schedule: + self->reconfigure_check = TRUE; + if (!self->nft_in_progress) { + self->reconfigure_on_idle_source = + nm_g_idle_add_source(_reconfigure_check_on_idle_cb, self); + } +} + +/*****************************************************************************/ + +void +nm_bond_manager_reapply(NMBondManager *self) +{ + _reconfigure_check(self, TRUE); +} + +/*****************************************************************************/ + +int +nm_bond_manager_get_ifindex(NMBondManager *self) +{ + nm_assert(NM_IS_BOND_MANAGER(self)); + + return self->ifindex; +} + +const char * +nm_bond_manager_get_connection_uuid(NMBondManager *self) +{ + nm_assert(NM_IS_BOND_MANAGER(self)); + + return self->connection_uuid; +} + +NMOptionBool +nm_bond_manager_get_state(NMBondManager *self) +{ + nm_assert(NM_IS_BOND_MANAGER(self)); + + return self->state; +} + +/*****************************************************************************/ + +NMBondManager * +nm_bond_manager_new(struct _NMPlatform *platform, + int ifindex, + const char *connection_uuid, + NMBondManagerCallback callback, + gpointer user_data) +{ + NMBondManager *self; + + nm_assert(NM_IS_PLATFORM(platform)); + nm_assert(ifindex > 0); + + self = g_slice_new(NMBondManager); + *self = (NMBondManager){ + .platform = g_object_ref(platform), + .ifindex = ifindex, + .reg_state = REGISTRATION_STATE_NONE, + .destroyed = FALSE, + .nft_good = TRUE, + .callback = callback, + .user_data = user_data, + .previous_ifindexes = g_hash_table_new(nm_direct_hash, NULL), + .previous_members = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, NULL), + .connection_uuid = g_strdup(connection_uuid), + .state = NM_OPTION_BOOL_DEFAULT, + }; + + self->link_changed_id = g_signal_connect(self->platform, + NM_PLATFORM_SIGNAL_LINK_CHANGED, + G_CALLBACK(_link_changed_cb), + self); + + _LOGT("new balance-slb (MLAG) manager for interface %d", self->ifindex); + + _reconfigure_check(self, TRUE); + + return self; +} + +void +nm_bond_manager_destroy(NMBondManager *self) +{ + g_return_if_fail(self); + g_return_if_fail(!self->destroyed); + + self->destroyed = TRUE; + + self->callback = NULL; + self->user_data = NULL; + + nm_clear_g_signal_handler(self->platform, &self->link_changed_id); + + nm_clear_g_source_inst(&self->reconfigure_on_idle_source); + self->reconfigure_check = FALSE; + + nm_clear_g_free(&self->dat.bond_ifname_next); + nm_clear_g_free(&self->dat.active_members_next); + + switch (self->reg_state) { + case REGISTRATION_STATE_NONE: + break; + case REGISTRATION_STATE_UPPING: + /* We still have some nfts registered. We need to wrap them up. */ + _LOGT("destroying but deconfigure pending configuration first"); + nm_clear_g_free(&self->dat.bond_ifname_next); + nm_clear_g_free(&self->dat.active_members_next); + nm_clear_g_cancellable(&self->cancellable); + return; + case REGISTRATION_STATE_UP: + _LOGT("destroying but deconfigure first"); + _nft_call(self, FALSE, self->dat.bond_ifname_curr, NULL, NULL); + self->reg_state = REGISTRATION_STATE_DOWNING; + return; + case REGISTRATION_STATE_DOWNING: + _LOGT("destroying but wait for deconfiguring"); + return; + } + + _bond_manager_destroy(self); +} + +static void +_bond_manager_destroy(NMBondManager *self) +{ + _LOGT("destroyed"); + + nm_assert(self); + nm_assert(self->destroyed); + nm_assert(self->reg_state == REGISTRATION_STATE_NONE); + nm_assert(self->link_changed_id == 0); + nm_assert(!self->cancellable); + nm_assert(!self->dat.bond_ifname_curr); + nm_assert(!self->dat.active_members_curr); + nm_assert(!self->reconfigure_on_idle_source); + + nm_clear_g_free(&self->dat.bond_ifname_next); + nm_clear_g_free(&self->dat.active_members_next); + + g_object_unref(self->platform); + g_hash_table_unref(self->previous_ifindexes); + g_hash_table_unref(self->previous_members); + g_free(self->connection_uuid); + nm_g_slice_free(self); +} diff --git a/src/core/nm-bond-manager.h b/src/core/nm-bond-manager.h new file mode 100644 index 00000000..92a89f0b --- /dev/null +++ b/src/core/nm-bond-manager.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef __NM_BOND_MANAGER_H__ +#define __NM_BOND_MANAGER_H__ + +typedef struct _NMBondManager NMBondManager; + +struct _NMPlatform; + +typedef enum { + NM_BOND_MANAGER_EVENT_TYPE_STATE, +} NMBondManagerEventType; + +typedef void (*NMBondManagerCallback)(NMBondManager *self, + NMBondManagerEventType event_type, + gpointer user_data); + +NMBondManager *nm_bond_manager_new(struct _NMPlatform *platform, + int ifindex, + const char *connection_uuid, + NMBondManagerCallback callback, + gpointer user_data); + +void nm_bond_manager_reapply(NMBondManager *self); + +void nm_bond_manager_destroy(NMBondManager *self); + +int nm_bond_manager_get_ifindex(NMBondManager *self); +const char *nm_bond_manager_get_connection_uuid(NMBondManager *self); +NMOptionBool nm_bond_manager_get_state(NMBondManager *self); + +#endif /* __NM_BOND_MANAGER_H__ */ diff --git a/src/core/nm-checkpoint.c b/src/core/nm-checkpoint.c index 15667332..cd0e17fa 100644 --- a/src/core/nm-checkpoint.c +++ b/src/core/nm-checkpoint.c @@ -416,10 +416,10 @@ activate: } } else { /* The device was initially disconnected, deactivate any existing connection */ - _LOGD("rollback: disconnecting device"); if (nm_device_get_state(device) > NM_DEVICE_STATE_DISCONNECTED && nm_device_get_state(device) < NM_DEVICE_STATE_DEACTIVATING) { + _LOGD("rollback: disconnecting device"); nm_device_state_changed(device, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_USER_REQUESTED); diff --git a/src/core/nm-checkpoint.h b/src/core/nm-checkpoint.h index db33b6e7..b396acfe 100644 --- a/src/core/nm-checkpoint.h +++ b/src/core/nm-checkpoint.h @@ -10,7 +10,7 @@ #include "nm-dbus-interface.h" #define NM_TYPE_CHECKPOINT (nm_checkpoint_get_type()) -#define NM_CHECKPOINT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_CHECKPOINT, NMCheckpoint)) +#define NM_CHECKPOINT(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_CHECKPOINT, NMCheckpoint)) #define NM_CHECKPOINT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_CHECKPOINT, NMCheckpointClass)) #define NM_IS_CHECKPOINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_CHECKPOINT)) diff --git a/src/core/nm-config-data.c b/src/core/nm-config-data.c index 753e3fb5..c6ab998f 100644 --- a/src/core/nm-config-data.c +++ b/src/core/nm-config-data.c @@ -1125,10 +1125,13 @@ load_global_dns(GKeyFile *keyfile, gboolean internal) gboolean default_found = FALSE; char **strv; - group = - internal ? NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS : NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS; - domain_prefix = internal ? NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN - : NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN; + if (internal) { + group = NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS; + domain_prefix = NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN; + } else { + group = NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS; + domain_prefix = NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN; + } domain_prefix_len = strlen(domain_prefix); if (!nm_config_keyfile_has_global_dns_config(keyfile, internal)) @@ -1191,8 +1194,7 @@ load_global_dns(GKeyFile *keyfile, gboolean internal) if (strv) { nm_strv_cleanup(strv, TRUE, TRUE, TRUE); for (i = 0, j = 0; strv[i]; i++) { - if (nm_utils_ipaddr_is_valid(AF_INET, strv[i]) - || nm_utils_ipaddr_is_valid(AF_INET6, strv[i])) + if (nm_inet_is_valid(AF_INET, strv[i]) || nm_inet_is_valid(AF_INET6, strv[i])) strv[j++] = strv[i]; else g_free(strv[i]); @@ -1231,7 +1233,7 @@ load_global_dns(GKeyFile *keyfile, gboolean internal) default_found = TRUE; } - if (!default_found) { + if (!default_found && g_hash_table_size(dns_config->domains)) { nm_log_dbg(LOGD_CORE, "%s global DNS configuration is missing default domain, ignore it", internal ? "internal" : "user"); @@ -1328,8 +1330,7 @@ global_dns_domain_from_dbus(char *name, GVariant *variant) strv = g_variant_dup_strv(val, NULL); nm_strv_cleanup(strv, TRUE, TRUE, TRUE); for (i = 0, j = 0; strv && strv[i]; i++) { - if (nm_utils_ipaddr_is_valid(AF_INET, strv[i]) - || nm_utils_ipaddr_is_valid(AF_INET6, strv[i])) + if (nm_inet_is_valid(AF_INET, strv[i]) || nm_inet_is_valid(AF_INET6, strv[i])) strv[j++] = strv[i]; else g_free(strv[i]); diff --git a/src/core/nm-config-data.h b/src/core/nm-config-data.h index fdc7e1fc..e3dc90dd 100644 --- a/src/core/nm-config-data.h +++ b/src/core/nm-config-data.h @@ -31,7 +31,8 @@ typedef enum { /*****************************************************************************/ #define NM_TYPE_CONFIG_DATA (nm_config_data_get_type()) -#define NM_CONFIG_DATA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_CONFIG_DATA, NMConfigData)) +#define NM_CONFIG_DATA(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_CONFIG_DATA, NMConfigData)) #define NM_CONFIG_DATA_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_CONFIG_DATA, NMConfigDataClass)) #define NM_IS_CONFIG_DATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_CONFIG_DATA)) diff --git a/src/core/nm-config.c b/src/core/nm-config.c index 62e4e7ea..b7445362 100644 --- a/src/core/nm-config.c +++ b/src/core/nm-config.c @@ -575,63 +575,63 @@ nm_config_cmd_line_options_add_to_entries(NMConfigCmdLineOptions *cli, GOptionCo { GOptionGroup *group; GOptionEntry config_options[] = { - {"config", + {"config", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_main_file, N_("Config file location"), DEFAULT_CONFIG_MAIN_FILE}, - {"config-dir", + {"config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_dir, N_("Config directory location"), DEFAULT_CONFIG_DIR}, - {"system-config-dir", + {"system-config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->system_config_dir, N_("System config directory location"), DEFAULT_SYSTEM_CONFIG_DIR}, - {"intern-config", + {"intern-config", 0, 0, G_OPTION_ARG_FILENAME, &cli->intern_config_file, N_("Internal config file location"), DEFAULT_INTERN_CONFIG_FILE}, - {"state-file", + {"state-file", 0, 0, G_OPTION_ARG_FILENAME, &cli->state_file, N_("State file location"), DEFAULT_STATE_FILE}, - {"no-auto-default", + {"no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli->no_auto_default_file, N_("State file for no-auto-default devices"), DEFAULT_NO_AUTO_DEFAULT_FILE}, - {"plugins", + {"plugins", 0, 0, G_OPTION_ARG_STRING, &cli->plugins, N_("List of plugins separated by ','"), NM_CONFIG_DEFAULT_MAIN_PLUGINS}, - {"configure-and-quit", + {"configure-and-quit", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_configure_and_quit, N_("Quit after initial configuration"), NULL}, - {"debug", + {"debug", 'd', 0, G_OPTION_ARG_NONE, @@ -639,29 +639,29 @@ nm_config_cmd_line_options_add_to_entries(NMConfigCmdLineOptions *cli, GOptionCo N_("Don't become a daemon, and log to stderr"), NULL}, - /* These three are hidden for now, and should eventually just go away. */ - {"connectivity-uri", + /* These three are hidden for now, and should eventually just go away. */ + {"connectivity-uri", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_uri, N_("An http(s) address for checking internet connectivity"), "http://example.com"}, - {"connectivity-interval", + {"connectivity-interval", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &cli->connectivity_interval, N_("The interval between connectivity checks (in seconds)"), G_STRINGIFY(NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL)}, - {"connectivity-response", + {"connectivity-response", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_response, N_("The expected start of the response"), NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE}, - {0}, + {0}, }; g_return_if_fail(opt_ctx); @@ -680,7 +680,7 @@ nm_config_cmd_line_options_add_to_entries(NMConfigCmdLineOptions *cli, GOptionCo /*****************************************************************************/ GKeyFile * -nm_config_create_keyfile() +nm_config_create_keyfile(void) { GKeyFile *keyfile; diff --git a/src/core/nm-config.h b/src/core/nm-config.h index 0dd159f4..d56770d0 100644 --- a/src/core/nm-config.h +++ b/src/core/nm-config.h @@ -12,7 +12,7 @@ #include "libnm-base/nm-config-base.h" #define NM_TYPE_CONFIG (nm_config_get_type()) -#define NM_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_CONFIG, NMConfig)) +#define NM_CONFIG(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_CONFIG, NMConfig)) #define NM_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_CONFIG, NMConfigClass)) #define NM_IS_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_CONFIG)) #define NM_IS_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_CONFIG)) diff --git a/src/core/nm-connectivity.c b/src/core/nm-connectivity.c index c0e77e35..aaf9d810 100644 --- a/src/core/nm-connectivity.c +++ b/src/core/nm-connectivity.c @@ -810,7 +810,7 @@ system_resolver_resolve_cb(GObject *source_object, GAsyncResult *res, gpointer u for (iter = list; iter; iter = iter->next) { GInetAddress *a = iter->data; - char str_addr[NM_UTILS_INET_ADDRSTRLEN]; + char str_addr[NM_INET_ADDRSTRLEN]; int addr_family; switch (g_inet_address_get_family(a)) { @@ -835,7 +835,7 @@ system_resolver_resolve_cb(GObject *source_object, GAsyncResult *res, gpointer u nm_str_buf_append_c(&strbuf_hosts, ','); nm_str_buf_append(&strbuf_hosts, - nm_utils_inet_ntop(addr_family, g_inet_address_to_bytes(a), str_addr)); + nm_inet_ntop(addr_family, g_inet_address_to_bytes(a), str_addr)); next:; } @@ -910,7 +910,7 @@ systemd_resolved_resolve_cb(GObject *object, GAsyncResult *res, gpointer user_da for (i = 0; i < no_addresses; i++) { gs_unref_variant GVariant *address = NULL; - char str_addr[NM_UTILS_INET_ADDRSTRLEN]; + char str_addr[NM_INET_ADDRSTRLEN]; const guchar *address_buf; g_variant_get_child(addresses, i, "(ii@ay)", &ifindex, &addr_family, &address); @@ -933,7 +933,7 @@ systemd_resolved_resolve_cb(GObject *object, GAsyncResult *res, gpointer user_da } else nm_str_buf_append_c(&strbuf_hosts, ','); - nm_str_buf_append(&strbuf_hosts, nm_utils_inet_ntop(addr_family, address_buf, str_addr)); + nm_str_buf_append(&strbuf_hosts, nm_inet_ntop(addr_family, address_buf, str_addr)); } if (strbuf_hosts.len == 0) { _LOG2D("systemd-resolve returned no usable IPv%c addresses", diff --git a/src/core/nm-connectivity.h b/src/core/nm-connectivity.h index 7b57a112..d9ea95c2 100644 --- a/src/core/nm-connectivity.h +++ b/src/core/nm-connectivity.h @@ -32,7 +32,7 @@ nm_connectivity_state_cmp(NMConnectivityState a, NMConnectivityState b) #define NM_TYPE_CONNECTIVITY (nm_connectivity_get_type()) #define NM_CONNECTIVITY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_CONNECTIVITY, NMConnectivity)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_CONNECTIVITY, NMConnectivity)) #define NM_CONNECTIVITY_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_CONNECTIVITY, NMConnectivityClass)) #define NM_IS_CONNECTIVITY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_CONNECTIVITY)) diff --git a/src/core/nm-core-utils.c b/src/core/nm-core-utils.c index 4aad9414..9448ba7b 100644 --- a/src/core/nm-core-utils.c +++ b/src/core/nm-core-utils.c @@ -58,7 +58,7 @@ G_STATIC_ASSERT(sizeof(NMUtilsTestFlags) <= sizeof(int)); static int _nm_utils_testing = 0; gboolean -nm_utils_get_testing_initialized() +nm_utils_get_testing_initialized(void) { NMUtilsTestFlags flags; @@ -69,7 +69,7 @@ nm_utils_get_testing_initialized() } NMUtilsTestFlags -nm_utils_get_testing() +nm_utils_get_testing(void) { NMUtilsTestFlags flags; @@ -2333,14 +2333,14 @@ nm_utils_log_connection_diff(NMConnection *connection, for (i = 0; i < sorted_hashes->len; i++) { LogConnectionSettingData *setting_data = - &g_array_index(sorted_hashes, LogConnectionSettingData, i); + &nm_g_array_index(sorted_hashes, LogConnectionSettingData, i); _log_connection_sort_names(setting_data, sorted_names); print_setting_header = TRUE; for (j = 0; j < sorted_names->len; j++) { char *str_conn, *str_diff; LogConnectionSettingItem *item = - &g_array_index(sorted_names, LogConnectionSettingItem, j); + &nm_g_array_index(sorted_names, LogConnectionSettingItem, j); str_conn = (item->diff_result & NM_SETTING_DIFF_RESULT_IN_A) ? _log_connection_get_property(setting_data->setting, item->item_name) @@ -2717,11 +2717,17 @@ _host_id_hash_v2(const guint8 *seed_arr, const UuidData *machine_id_data; char slen[100]; - /* - (stat -c '%s' /var/lib/NetworkManager/secret_key; - echo -n ' '; - cat /var/lib/NetworkManager/secret_key; - cat /etc/machine-id | tr -d '\n' | sed -n 's/[a-f0-9-]/\0/pg') | sha256sum + /* The following snippet generates the same (binary) host-id: + + ( + stat -c '%s' /var/lib/NetworkManager/secret_key | tr -d '\n'; + echo -n ' '; + cat /var/lib/NetworkManager/secret_key; + cat /etc/machine-id | tr -d '\n' | sed -n 's/[a-f0-9-]/\0/pg' + ) \ + | sha256sum \ + | awk '{print $1}' \ + | xxd -r -p */ nm_sprintf_buf(slen, "%" G_GSIZE_FORMAT " ", seed_len); @@ -3013,7 +3019,7 @@ nmtst_utils_host_id_pop(void) nm_log_dbg(LOGD_CORE, "nmtst: host-id pop"); - h = &g_array_index(nmtst_host_id_stack, HostIdData, nmtst_host_id_stack->len - 1); + h = &nm_g_array_index(nmtst_host_id_stack, HostIdData, nmtst_host_id_stack->len - 1); g_free((char *) h->host_id); g_array_set_size(nmtst_host_id_stack, nmtst_host_id_stack->len - 1u); @@ -3022,7 +3028,7 @@ nmtst_utils_host_id_pop(void) &host_id_static, h, nmtst_host_id_stack->len == 0u ? nmtst_host_id_static_0 - : nm_g_array_last(nmtst_host_id_stack, HostIdData))) + : &nm_g_array_last(nmtst_host_id_stack, HostIdData))) g_assert_not_reached(); } @@ -3864,7 +3870,7 @@ nm_utils_dhcp_client_id_duid(guint32 iaid, const guint8 *duid, gsize duid_len) guint8 type; guint32 iaid; guint8 duid[]; - } * client_id; + } *client_id; gsize total_size; /* the @duid must include the 16 bit duid-type and the data (of max 128 bytes). */ @@ -3914,7 +3920,7 @@ nm_utils_dhcp_client_id_systemd_node_specific_full(guint32 iaid, } en; }; } duid; - } * client_id; + } *client_id; guint64 u64; g_return_val_if_fail(machine_id, NULL); @@ -4193,7 +4199,7 @@ nm_utils_get_reverse_dns_domains_ip_6(const struct in6_addr *ip, guint8 plen, GP return; memcpy(&addr, ip, sizeof(struct in6_addr)); - nm_utils_ip6_address_clear_host_address(&addr, NULL, plen); + nm_ip6_addr_clear_host_address(&addr, NULL, plen); /* Number of nibbles to include in domains */ nibbles = (plen - 1) / 4 + 1; @@ -4338,7 +4344,7 @@ skip: result = g_new(char *, paths->len + 1); for (i = 0; i < paths->len; i++) - result[i] = g_array_index(paths, struct plugin_info, i).path; + result[i] = nm_g_array_index(paths, struct plugin_info, i).path; result[i] = NULL; g_array_free(paths, TRUE); diff --git a/src/core/nm-core-utils.h b/src/core/nm-core-utils.h index 483af077..bc936496 100644 --- a/src/core/nm-core-utils.h +++ b/src/core/nm-core-utils.h @@ -112,7 +112,7 @@ nm_hash_update_in6addr_prefix(NMHashState *h, const struct in6_addr *addr, guint nm_assert(addr); - nm_utils_ip6_address_clear_host_address(&a, addr, plen); + nm_ip6_addr_clear_host_address(&a, addr, plen); /* we don't hash plen itself. The caller may want to do that.*/ nm_hash_update_in6addr(h, &a); } diff --git a/src/core/nm-dbus-manager.c b/src/core/nm-dbus-manager.c index 7fcbf6ca..af7de8c4 100644 --- a/src/core/nm-dbus-manager.c +++ b/src/core/nm-dbus-manager.c @@ -1077,7 +1077,7 @@ nm_dbus_manager_lookup_object(NMDBusManager *self, const char *path) if (!ptr) return NULL; - obj = (NMDBusObject *) (((char *) ptr) - G_STRUCT_OFFSET(NMDBusObject, internal)); + obj = NM_CAST_ALIGN(NMDBusObject, (((char *) ptr) - G_STRUCT_OFFSET(NMDBusObject, internal))); nm_assert(NM_IS_DBUS_OBJECT(obj)); return obj; } diff --git a/src/core/nm-dbus-manager.h b/src/core/nm-dbus-manager.h index d977f99f..b68161db 100644 --- a/src/core/nm-dbus-manager.h +++ b/src/core/nm-dbus-manager.h @@ -10,7 +10,8 @@ #include "nm-dbus-utils.h" #define NM_TYPE_DBUS_MANAGER (nm_dbus_manager_get_type()) -#define NM_DBUS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_DBUS_MANAGER, NMDBusManager)) +#define NM_DBUS_MANAGER(o) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_DBUS_MANAGER, NMDBusManager)) #define NM_DBUS_MANAGER_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_DBUS_MANAGER, NMDBusManagerClass)) #define NM_IS_DBUS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), NM_TYPE_DBUS_MANAGER)) diff --git a/src/core/nm-dbus-object.h b/src/core/nm-dbus-object.h index df22bd92..2a0ff0ee 100644 --- a/src/core/nm-dbus-object.h +++ b/src/core/nm-dbus-object.h @@ -46,7 +46,8 @@ typedef struct { /*****************************************************************************/ #define NM_TYPE_DBUS_OBJECT (nm_dbus_object_get_type()) -#define NM_DBUS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DBUS_OBJECT, NMDBusObject)) +#define NM_DBUS_OBJECT(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DBUS_OBJECT, NMDBusObject)) #define NM_DBUS_OBJECT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DBUS_OBJECT, NMDBusObjectClass)) #define NM_IS_DBUS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DBUS_OBJECT)) diff --git a/src/core/nm-dbus-utils.c b/src/core/nm-dbus-utils.c index 096b647b..05eca84f 100644 --- a/src/core/nm-dbus-utils.c +++ b/src/core/nm-dbus-utils.c @@ -166,7 +166,7 @@ nm_dbus_utils_get_paths_for_clist(const CList *lst_head, i = 0; strv = g_new(const char *, n + 1); c_list_for_each (iter, lst_head) { - NMDBusObject *obj = (NMDBusObject *) (((const char *) iter) - member_offset); + NMDBusObject *obj = NM_CAST_ALIGN(NMDBusObject, (((char *) iter) - member_offset)); path = nm_dbus_object_get_path(obj); if (!path) { diff --git a/src/core/nm-default-daemon.h b/src/core/nm-default-daemon.h index 57d4c6af..f8270ef4 100644 --- a/src/core/nm-default-daemon.h +++ b/src/core/nm-default-daemon.h @@ -18,6 +18,7 @@ #include "nm-version.h" #include "nm-core-types.h" +#include "nm-errors.h" #include "nm-types.h" #include "libnm-log-core/nm-logging.h" diff --git a/src/core/nm-dhcp-config.c b/src/core/nm-dhcp-config.c index 394a3a49..fca1dcdd 100644 --- a/src/core/nm-dhcp-config.c +++ b/src/core/nm-dhcp-config.c @@ -18,7 +18,7 @@ #define NM_TYPE_DHCP4_CONFIG (nm_dhcp4_config_get_type()) #define NM_DHCP4_CONFIG(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP4_CONFIG, NMDhcp4Config)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP4_CONFIG, NMDhcp4Config)) #define NM_DHCP4_CONFIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP4_CONFIG, NMDhcp4ConfigClass)) #define NM_IS_DHCP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP4_CONFIG)) @@ -33,7 +33,7 @@ static GType nm_dhcp4_config_get_type(void); #define NM_TYPE_DHCP6_CONFIG (nm_dhcp6_config_get_type()) #define NM_DHCP6_CONFIG(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP6_CONFIG, NMDhcp6Config)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP6_CONFIG, NMDhcp6Config)) #define NM_DHCP6_CONFIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP6_CONFIG, NMDhcp6ConfigClass)) #define NM_IS_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP6_CONFIG)) diff --git a/src/core/nm-dhcp-config.h b/src/core/nm-dhcp-config.h index cdca4ef1..e372ec81 100644 --- a/src/core/nm-dhcp-config.h +++ b/src/core/nm-dhcp-config.h @@ -9,7 +9,8 @@ /*****************************************************************************/ #define NM_TYPE_DHCP_CONFIG (nm_dhcp_config_get_type()) -#define NM_DHCP_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_CONFIG, NMDhcpConfig)) +#define NM_DHCP_CONFIG(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_CONFIG, NMDhcpConfig)) #define NM_DHCP_CONFIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_CONFIG, NMDhcpConfigClass)) #define NM_IS_DHCP_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_CONFIG)) diff --git a/src/core/nm-dispatcher.c b/src/core/nm-dispatcher.c index d3529ee4..01a63826 100644 --- a/src/core/nm-dispatcher.c +++ b/src/core/nm-dispatcher.c @@ -8,6 +8,7 @@ #include "nm-dispatcher.h" +#include "libnm-glib-aux/nm-dbus-aux.h" #include "libnm-core-aux-extern/nm-dispatcher-api.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" @@ -179,7 +180,6 @@ dump_ip_to_props(const NML3ConfigData *l3cd, int addr_family, GVariantBuilder *b const NMPObject *default_route; const char *const *strarr; const in_addr_t *ip4arr; - gconstpointer iparr; if (IS_IPv4) g_variant_builder_init(&int_builder, G_VARIANT_TYPE("aau")); @@ -223,14 +223,17 @@ dump_ip_to_props(const NML3ConfigData *l3cd, int addr_family, GVariantBuilder *b g_variant_builder_init(&int_builder, G_VARIANT_TYPE("au")); else g_variant_builder_init(&int_builder, G_VARIANT_TYPE("aay")); - iparr = nm_l3_config_data_get_nameservers(l3cd, addr_family, &n); + strarr = nm_l3_config_data_get_nameservers(l3cd, addr_family, &n); for (i = 0; i < n; i++) { + NMIPAddr a; + + if (!nm_utils_dnsname_parse_assert(addr_family, strarr[i], NULL, &a, NULL)) + continue; + if (IS_IPv4) - g_variant_builder_add(&int_builder, "u", ((const in_addr_t *) iparr)[i]); - else { - var1 = nm_g_variant_new_ay_in6addr(&(((const struct in6_addr *) iparr)[i])); - g_variant_builder_add(&int_builder, "@ay", var1); - } + g_variant_builder_add(&int_builder, "u", &a); + else + g_variant_builder_add(&int_builder, "@ay", nm_g_variant_new_ay_in6addr(&a.addr6)); } g_variant_builder_add(builder, "{sv}", "nameservers", g_variant_builder_end(&int_builder)); @@ -426,7 +429,7 @@ dispatcher_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) if (!ret) { NMLogLevel log_level = LOGL_DEBUG; - if (_nm_dbus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed")) { + if (nm_dbus_error_is(error, "org.freedesktop.systemd1.LoadFailed")) { g_dbus_error_strip_remote_error(error); log_level = LOGL_WARN; } @@ -465,7 +468,8 @@ static const char *action_table[] = {[NM_DISPATCHER_ACTION_HOSTNAME] = NMD_ [NM_DISPATCHER_ACTION_DHCP_CHANGE_4] = NMD_ACTION_DHCP4_CHANGE, [NM_DISPATCHER_ACTION_DHCP_CHANGE_6] = NMD_ACTION_DHCP6_CHANGE, [NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE] = - NMD_ACTION_CONNECTIVITY_CHANGE}; + NMD_ACTION_CONNECTIVITY_CHANGE, + [NM_DISPATCHER_ACTION_REAPPLY] = NMD_ACTION_REAPPLY}; static const char * action_to_string(NMDispatcherAction action) diff --git a/src/core/nm-dispatcher.h b/src/core/nm-dispatcher.h index 73e0599a..30716399 100644 --- a/src/core/nm-dispatcher.h +++ b/src/core/nm-dispatcher.h @@ -21,7 +21,8 @@ typedef enum { NM_DISPATCHER_ACTION_VPN_DOWN, NM_DISPATCHER_ACTION_DHCP_CHANGE_4, NM_DISPATCHER_ACTION_DHCP_CHANGE_6, - NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE + NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE, + NM_DISPATCHER_ACTION_REAPPLY, } NMDispatcherAction; #define NM_DISPATCHER_ACTION_DHCP_CHANGE_X(IS_IPv4) \ diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index 4bcde85f..f231583a 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -39,6 +39,71 @@ static const struct { /*****************************************************************************/ +static const char * +_nft_ifname_valid(const char *str) +{ + gsize i; + + /* `nft -f -` takes certain strings, like "device $IFNAME", but + * those strings are from a limited character set. Check that + * @str is valid according to those rules. + * + * src/scanner.l: + * digit [0-9] + * letter [a-zA-Z] + * string ({letter}|[_.])({letter}|{digit}|[/\-_\.])* + **/ + + if (!str || !str[0]) + return NULL; + + for (i = 0; str[i]; i++) { + switch (str[i]) { + case 'a' ... 'z': + case 'A' ... 'Z': + case '_': + case '.': + continue; + case '0' ... '9': + case '/': + case '-': + if (i == 0) + return NULL; + continue; + default: + return NULL; + } + } + if (i >= NMP_IFNAMSIZ) + return NULL; + + return str; +} + +static const char * +_strbuf_set_sanitized(NMStrBuf *strbuf, const char *prefix, const char *str_to_sanitize) +{ + nm_str_buf_reset(strbuf); + + if (prefix) + nm_str_buf_append(strbuf, prefix); + + for (; str_to_sanitize[0] != '\0'; str_to_sanitize++) { + const char ch = str_to_sanitize[0]; + + if (g_ascii_isalpha(ch) || g_ascii_isdigit(ch)) { + nm_str_buf_append_c(strbuf, ch); + continue; + } + nm_str_buf_append_c(strbuf, '_'); + nm_str_buf_append_c_hex(strbuf, ch, FALSE); + } + + return nm_str_buf_get_str(strbuf); +} + +/*****************************************************************************/ + #define _SHARE_IPTABLES_SUBNET_TO_STR_LEN (INET_ADDRSTRLEN + 1 + 2 + 1) static const char * @@ -50,12 +115,12 @@ _share_iptables_subnet_to_str(char buf[static _SHARE_IPTABLES_SUBNET_TO_STR in_addr_t netmask; int l; - netmask = _nm_utils_ip4_prefix_to_netmask(plen); + netmask = nm_ip4_addr_netmask_from_prefix(plen); l = g_snprintf(buf, _SHARE_IPTABLES_SUBNET_TO_STR_LEN, "%s/%u", - _nm_utils_inet4_ntop(addr & netmask, buf_addr), + nm_inet4_ntop(addr & netmask, buf_addr), plen); nm_assert(l < _SHARE_IPTABLES_SUBNET_TO_STR_LEN); return buf; @@ -693,6 +758,189 @@ _fw_nft_set_shared_construct(gboolean up, const char *ip_iface, in_addr_t addr, /*****************************************************************************/ +GBytes * +nm_firewall_nft_stdio_mlag(gboolean up, + const char *bond_ifname, + const char *const *bond_ifnames_down, + const char *const *active_members, + const char *const *previous_members) +{ + nm_auto_str_buf NMStrBuf strbuf_table_name = + NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_32, FALSE); + nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE); + const char *table_name; + gsize i; + + if (NM_MORE_ASSERTS > 10 && active_members) { + /* No duplicates. We make certain assumptions here, and we don't + * want to check that there are no duplicates. The caller must take + * care of this. */ + for (i = 0; active_members[i]; i++) + nm_assert(!nm_strv_contains(&active_members[i + 1], -1, active_members[i])); + } + + /* If an interface gets renamed, we need to update the nft tables. Since one nft + * invocation is atomic, it is reasonable to drop the previous tables(s) at the + * same time when creating the new one. */ + for (; bond_ifnames_down && bond_ifnames_down[0]; bond_ifnames_down++) { + if (nm_streq(bond_ifname, bond_ifnames_down[0])) + continue; + table_name = _strbuf_set_sanitized(&strbuf_table_name, "nm-mlag-", bond_ifnames_down[0]); + _fw_nft_append_cmd_table(&strbuf, "netdev", table_name, FALSE); + } + + table_name = _strbuf_set_sanitized(&strbuf_table_name, "nm-mlag-", bond_ifname); + + _fw_nft_append_cmd_table(&strbuf, "netdev", table_name, up); + + if (up) { + nm_auto_str_buf NMStrBuf strbuf_1 = + NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_232, FALSE); + const gsize n_active_members = NM_PTRARRAY_LEN(active_members); + + if (!_nft_ifname_valid(bond_ifname)) { + /* We cannot meaningfully express this interface name. Ignore all chains + * and only create an empty table. */ + goto out; + } + + for (; previous_members && previous_members[0]; previous_members++) { + const char *previous_member = previous_members[0]; + const char *chain_name; + + /* The caller already ensures that the previous member is not part of the new + * active members. Avoid the overhead of checking, and assert against that. */ + nm_assert(!nm_strv_contains(active_members, n_active_members, previous_member)); + + if (!_nft_ifname_valid(previous_member)) + continue; + + chain_name = _strbuf_set_sanitized(&strbuf_1, "rx-drop-bc-mc-", previous_member); + + /* We want atomically update our table, however, we don't want to delete + * and recreate it, because then the sets get lost (which we don't want). + * + * Instead, we only "add && flush" the table, which removes all rules from + * the chain. However, as our active-members change, we want to delete + * the obsolete chains too. + * + * nft has no way to delete all chains in a table, we have to name + * them one by one. So we keep track of active members that we had + * in the past, and which are now no longer in use. For those previous + * members we delete the chains (again, with the "add && delete" dance + * to avoid failure deleting a non-existing chain (in case our tracking + * is wrong or somebody else modified the table in the meantime). + * + * We need to track the previous members, because we don't want to first + * ask nft which chains exist. Doing that would be cumbersome as we would + * have to do one async program invocation and parse stdout. */ + _append(&strbuf, + "add chain netdev %s %s {" + " type filter hook ingress device %s priority filter; " + "}", + table_name, + chain_name, + previous_member); + _append(&strbuf, "delete chain netdev %s %s", table_name, chain_name); + } + + /* OVS SLB rule 1 + * + * "Open vSwitch avoids packet duplication by accepting multicast and broadcast + * packets on only the active member, and dropping multicast and broadcast + * packets on all other members." + * + * primary is first member, we drop on all others */ + for (i = 0; i < n_active_members; i++) { + const char *active_member = active_members[i]; + const char *chain_name; + + if (!_nft_ifname_valid(active_member)) + continue; + + chain_name = _strbuf_set_sanitized(&strbuf_1, "rx-drop-bc-mc-", active_member); + + _append(&strbuf, + "add chain netdev %s %s {" + " type filter hook ingress device %s priority filter; " + "}", + table_name, + chain_name, + active_member); + + if (i == 0) { + _append(&strbuf, "delete chain netdev %s %s", table_name, chain_name); + continue; + } + + _append(&strbuf, + "add rule netdev %s %s pkttype {" + " broadcast, multicast " + "} counter drop", + table_name, + chain_name); + } + + /* OVS SLB rule 2 + * + * "Open vSwitch deals with this case by dropping packets received on any SLB + * bonded link that have a source MAC+VLAN that has been learned on any other + * port." + */ + _append(&strbuf, + "add set netdev %s macset-tagged {" + " typeof ether saddr . vlan id; flags timeout; " + "}", + table_name); + _append(&strbuf, + "add set netdev %s macset-untagged {" + " typeof ether saddr; flags timeout;" + "}", + table_name); + + _append(&strbuf, + "add chain netdev %s tx-snoop-source-mac {" + " type filter hook egress device %s priority filter; " + "}", + table_name, + bond_ifname); + _append(&strbuf, + "add rule netdev %s tx-snoop-source-mac set update ether saddr . vlan id" + " timeout 5s @macset-tagged counter return" + "", /* tagged */ + table_name); + _append(&strbuf, + "add rule netdev %s tx-snoop-source-mac set update ether saddr" + " timeout 5s @macset-untagged counter" + "", /* untagged*/ + table_name); + + _append(&strbuf, + "add chain netdev %s rx-drop-looped-packets {" + " type filter hook ingress device %s priority filter; " + "}", + table_name, + bond_ifname); + _append(&strbuf, + "add rule netdev %s rx-drop-looped-packets ether saddr . vlan id" + " @macset-tagged counter drop", + table_name); + _append(&strbuf, + "add rule netdev %s rx-drop-looped-packets ether type vlan counter return" + "", /* avoid looking up tagged packets in untagged table */ + table_name); + _append(&strbuf, + "add rule netdev %s rx-drop-looped-packets ether saddr @macset-untagged" + " counter drop", + table_name); + } + +out: + return nm_str_buf_finalize_to_gbytes(&strbuf); +} + +/*****************************************************************************/ + struct _NMFirewallConfig { char *ip_iface; in_addr_t addr; diff --git a/src/core/nm-firewall-utils.h b/src/core/nm-firewall-utils.h index 9d883fea..ca138ccf 100644 --- a/src/core/nm-firewall-utils.h +++ b/src/core/nm-firewall-utils.h @@ -35,4 +35,10 @@ void nm_firewall_nft_call(GBytes *stdin_buf, gboolean nm_firewall_nft_call_finish(GAsyncResult *result, GError **error); +GBytes *nm_firewall_nft_stdio_mlag(gboolean up, + const char *bond_ifname, + const char *const *bond_ifnames_down, + const char *const *active_members, + const char *const *previous_members); + #endif /* __NM_FIREWALL_UTILS_H__ */ diff --git a/src/core/nm-firewalld-manager.h b/src/core/nm-firewalld-manager.h index a7e71486..1e8f9a42 100644 --- a/src/core/nm-firewalld-manager.h +++ b/src/core/nm-firewalld-manager.h @@ -8,7 +8,7 @@ #define NM_TYPE_FIREWALLD_MANAGER (nm_firewalld_manager_get_type()) #define NM_FIREWALLD_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_FIREWALLD_MANAGER, NMFirewalldManager)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_FIREWALLD_MANAGER, NMFirewalldManager)) #define NM_FIREWALLD_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_FIREWALLD_MANAGER, NMFirewalldManagerClass)) #define NM_IS_FIREWALLD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_FIREWALLD_MANAGER)) diff --git a/src/core/nm-hostname-manager.c b/src/core/nm-hostname-manager.c index ba0c6c9d..b036590f 100644 --- a/src/core/nm-hostname-manager.c +++ b/src/core/nm-hostname-manager.c @@ -99,53 +99,32 @@ _file_monitor_new(const char *path) /*****************************************************************************/ -#if defined(HOSTNAME_PERSIST_GENTOO) static char * -read_hostname_gentoo(const char *path) +read_hostname(const char *path, gboolean is_gentoo) { - gs_free char *contents = NULL; - gs_strfreev char **all_lines = NULL; - const char *tmp; - guint i; + gs_free char *contents = NULL; + gs_free const char **all_lines = NULL; + const char *tmp; + gsize i; if (!g_file_get_contents(path, &contents, NULL, NULL)) return NULL; - all_lines = g_strsplit(contents, "\n", 0); - for (i = 0; all_lines[i]; i++) { - g_strstrip(all_lines[i]); - if (all_lines[i][0] == '#' || all_lines[i][0] == '\0') - continue; - if (g_str_has_prefix(all_lines[i], "hostname=")) { - tmp = &all_lines[i][NM_STRLEN("hostname=")]; - return g_shell_unquote(tmp, NULL); + all_lines = nm_strsplit_set_full(contents, "\n", NM_STRSPLIT_SET_FLAGS_STRSTRIP); + for (i = 0; (tmp = all_lines[i]); i++) { + if (is_gentoo) { + if (!NM_STR_HAS_PREFIX(tmp, "hostname=")) + continue; + tmp = &tmp[NM_STRLEN("hostname=")]; + } else { + if (tmp[0] == '#') + continue; } + nm_assert(tmp && tmp[0] != '\0'); + return g_shell_unquote(tmp, NULL); } return NULL; } -#endif - -#if defined(HOSTNAME_PERSIST_SLACKWARE) -static char * -read_hostname_slackware(const char *path) -{ - gs_free char *contents = NULL; - gs_strfreev char **all_lines = NULL; - guint i = 0; - - if (!g_file_get_contents(path, &contents, NULL, NULL)) - return NULL; - - all_lines = g_strsplit(contents, "\n", 0); - for (i = 0; all_lines[i]; i++) { - g_strstrip(all_lines[i]); - if (all_lines[i][0] == '#' || all_lines[i][0] == '\0') - continue; - return g_shell_unquote(&all_lines[i][0], NULL); - } - return NULL; -} -#endif #if defined(HOSTNAME_PERSIST_SUSE) static gboolean @@ -237,10 +216,11 @@ _set_hostname_read_file(NMHostnameManager *self) #endif #if defined(HOSTNAME_PERSIST_GENTOO) - hostname = read_hostname_gentoo(HOSTNAME_FILE); + hostname = read_hostname(HOSTNAME_FILE, TRUE); #elif defined(HOSTNAME_PERSIST_SLACKWARE) - hostname = read_hostname_slackware(HOSTNAME_FILE); + hostname = read_hostname(HOSTNAME_FILE, FALSE); #else + (void) read_hostname; if (g_file_get_contents(HOSTNAME_FILE, &hostname, NULL, NULL)) g_strchomp(hostname); #endif @@ -433,11 +413,11 @@ _write_hostname_on_idle_cb(gpointer user_data, GCancellable *cancellable) } void -nm_hostname_manager_write_hostname(NMHostnameManager *self, - const char *hostname, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +nm_hostname_manager_set_static_hostname(NMHostnameManager *self, + const char *hostname, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { NMHostnameManagerPrivate *priv; GTask *task; @@ -446,8 +426,11 @@ nm_hostname_manager_write_hostname(NMHostnameManager *self, priv = NM_HOSTNAME_MANAGER_GET_PRIVATE(self); - task = - nm_g_task_new(self, cancellable, nm_hostname_manager_write_hostname, callback, user_data); + task = nm_g_task_new(self, + cancellable, + nm_hostname_manager_set_static_hostname, + callback, + user_data); g_task_set_task_data(task, g_strdup(hostname), g_free); @@ -467,12 +450,12 @@ nm_hostname_manager_write_hostname(NMHostnameManager *self, } gboolean -nm_hostname_manager_write_hostname_finish(NMHostnameManager *self, - GAsyncResult *result, - GError **error) +nm_hostname_manager_set_static_hostname_finish(NMHostnameManager *self, + GAsyncResult *result, + GError **error) { g_return_val_if_fail(NM_IS_HOSTNAME_MANAGER(self), FALSE); - g_return_val_if_fail(nm_g_task_is_valid(result, self, nm_hostname_manager_write_hostname), + g_return_val_if_fail(nm_g_task_is_valid(result, self, nm_hostname_manager_set_static_hostname), FALSE); return g_task_propagate_boolean(G_TASK(result), error); diff --git a/src/core/nm-hostname-manager.h b/src/core/nm-hostname-manager.h index 31596819..54818da3 100644 --- a/src/core/nm-hostname-manager.h +++ b/src/core/nm-hostname-manager.h @@ -12,7 +12,7 @@ #define NM_TYPE_HOSTNAME_MANAGER (nm_hostname_manager_get_type()) #define NM_HOSTNAME_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_HOSTNAME_MANAGER, NMHostnameManager)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_HOSTNAME_MANAGER, NMHostnameManager)) #define NM_HOSTNAME_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_HOSTNAME_MANAGER, NMHostnameManagerClass)) #define NM_IS_HOSTNAME_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_HOSTNAME_MANAGER)) @@ -36,15 +36,15 @@ NMHostnameManager *nm_hostname_manager_get(void); const char *nm_hostname_manager_get_static_hostname(NMHostnameManager *self); -void nm_hostname_manager_write_hostname(NMHostnameManager *self, - const char *hostname, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); +void nm_hostname_manager_set_static_hostname(NMHostnameManager *self, + const char *hostname, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); -gboolean nm_hostname_manager_write_hostname_finish(NMHostnameManager *self, - GAsyncResult *result, - GError **error); +gboolean nm_hostname_manager_set_static_hostname_finish(NMHostnameManager *self, + GAsyncResult *result, + GError **error); void nm_hostname_manager_set_transient_hostname(NMHostnameManager *self, const char *hostname, diff --git a/src/core/nm-ip-config.c b/src/core/nm-ip-config.c index b191b54b..f87840cc 100644 --- a/src/core/nm-ip-config.c +++ b/src/core/nm-ip-config.c @@ -10,6 +10,7 @@ #include +#include "libnm-core-aux-intern/nm-libnm-core-utils.h" #include "nm-l3cfg.h" #include "NetworkManagerUtils.h" @@ -90,7 +91,7 @@ get_property_ip(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec NMIPConfig *self = NM_IP_CONFIG(object); NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); const int addr_family = nm_ip_config_get_addr_family(self); - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; const char *const *strv; guint len; int v_i; @@ -108,7 +109,7 @@ get_property_ip(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec NMP_OBJECT_CAST_IP_ROUTE(priv->v_gateway.best_default_route)); g_value_set_variant( value, - g_variant_new_string(nm_utils_inet_ntop(addr_family, gateway, sbuf_addr))); + g_variant_new_string(nm_inet_ntop(addr_family, gateway, sbuf_addr))); } else g_value_set_variant(value, nm_g_variant_singleton_s_empty()); break; @@ -333,9 +334,10 @@ get_property_ip4(GObject *object, guint prop_id, GValue *value, GParamSpec *pspe { NMIPConfig *self = NM_IP_CONFIG(object); NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + char addr_str[NM_INET_ADDRSTRLEN]; GVariantBuilder builder; const in_addr_t *addrs; + const char *const *strarr; guint len; guint i; @@ -347,28 +349,37 @@ get_property_ip4(GObject *object, guint prop_id, GValue *value, GParamSpec *pspe g_value_set_variant(value, priv->v_routes); break; case PROP_IP4_NAMESERVERS: - addrs = nm_l3_config_data_get_nameservers(priv->l3cd, AF_INET, &len); - g_value_set_variant(value, - (len == 0) ? nm_g_variant_singleton_au() - : nm_g_variant_new_au(addrs, len)); - break; case PROP_IP4_NAMESERVER_DATA: - addrs = nm_l3_config_data_get_nameservers(priv->l3cd, AF_INET, &len); - if (len == 0) - g_value_set_variant(value, nm_g_variant_singleton_aaLsvI()); - else { - g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); + strarr = nm_l3_config_data_get_nameservers(priv->l3cd, AF_INET, &len); + if (len == 0) { + g_value_set_variant(value, + (prop_id == PROP_IP4_NAMESERVERS) + ? nm_g_variant_singleton_au() + : nm_g_variant_singleton_aaLsvI()); + } else { + if (prop_id == PROP_IP4_NAMESERVERS) + g_variant_builder_init(&builder, G_VARIANT_TYPE("au")); + else + g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); for (i = 0; i < len; i++) { - GVariantBuilder nested_builder; + in_addr_t a; - _nm_utils_inet4_ntop(addrs[i], addr_str); + if (!nm_utils_dnsname_parse_assert(AF_INET, strarr[i], NULL, &a, NULL)) + continue; - g_variant_builder_init(&nested_builder, G_VARIANT_TYPE("a{sv}")); - g_variant_builder_add(&nested_builder, - "{sv}", - "address", - g_variant_new_string(addr_str)); - g_variant_builder_add(&builder, "a{sv}", &nested_builder); + if (prop_id == PROP_IP4_NAMESERVERS) + g_variant_builder_add(&builder, "u", &a); + else { + GVariantBuilder nested_builder; + + nm_inet4_ntop(a, addr_str); + g_variant_builder_init(&nested_builder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&nested_builder, + "{sv}", + "address", + g_variant_new_string(addr_str)); + g_variant_builder_add(&builder, "a{sv}", &nested_builder); + } } g_value_take_variant(value, g_variant_builder_end(&builder)); @@ -387,7 +398,7 @@ get_property_ip4(GObject *object, guint prop_id, GValue *value, GParamSpec *pspe else { g_variant_builder_init(&builder, G_VARIANT_TYPE("as")); for (i = 0; i < len; i++) - g_variant_builder_add(&builder, "s", _nm_utils_inet4_ntop(addrs[i], addr_str)); + g_variant_builder_add(&builder, "s", nm_inet4_ntop(addrs[i], addr_str)); g_value_take_variant(value, g_variant_builder_end(&builder)); } break; @@ -498,77 +509,6 @@ nm_ip4_config_class_init(NMIP4ConfigClass *klass) g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST_ip4, obj_properties_ip4); } -void -nm_ip_config_dns_hash(const NML3ConfigData *l3cd, GChecksum *sum, int addr_family) -{ - guint i; - int val; - const char *const *nameservers; - const in_addr_t *wins; - const char *const *domains; - const char *const *searches; - const char *const *options; - guint num_nameservers; - guint num_wins; - guint num_domains; - guint num_searches; - guint num_options; - - g_return_if_fail(l3cd); - g_return_if_fail(sum); - - nameservers = nm_l3_config_data_get_nameservers(l3cd, addr_family, &num_nameservers); - for (i = 0; i < num_nameservers; i++) { - g_checksum_update(sum, - nm_ip_addr_from_packed_array(addr_family, nameservers, i), - nm_utils_addr_family_to_size(addr_family)); - } - - if (addr_family == AF_INET) { - wins = nm_l3_config_data_get_wins(l3cd, &num_wins); - for (i = 0; i < num_wins; i++) - g_checksum_update(sum, (guint8 *) &wins[i], 4); - } - - domains = nm_l3_config_data_get_domains(l3cd, addr_family, &num_domains); - for (i = 0; i < num_domains; i++) { - g_checksum_update(sum, (const guint8 *) domains[i], strlen(domains[i])); - } - - searches = nm_l3_config_data_get_searches(l3cd, addr_family, &num_searches); - for (i = 0; i < num_searches; i++) { - g_checksum_update(sum, (const guint8 *) searches[i], strlen(searches[i])); - } - - options = nm_l3_config_data_get_dns_options(l3cd, addr_family, &num_options); - for (i = 0; i < num_options; i++) { - g_checksum_update(sum, (const guint8 *) options[i], strlen(options[i])); - } - - val = nm_l3_config_data_get_mdns(l3cd); - if (val != NM_SETTING_CONNECTION_MDNS_DEFAULT) - g_checksum_update(sum, (const guint8 *) &val, sizeof(val)); - - val = nm_l3_config_data_get_llmnr(l3cd); - if (val != NM_SETTING_CONNECTION_LLMNR_DEFAULT) - g_checksum_update(sum, (const guint8 *) &val, sizeof(val)); - - val = nm_l3_config_data_get_dns_over_tls(l3cd); - if (val != NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT) - g_checksum_update(sum, (const guint8 *) &val, sizeof(val)); - - /* FIXME(ip-config-checksum): the DNS priority should be considered relevant - * and added into the checksum as well, but this can't be done right now - * because in the DNS manager we rely on the fact that an empty - * configuration (i.e. just created) has a zero checksum. This is needed to - * avoid rewriting resolv.conf when there is no change. - * - * The DNS priority initial value depends on the connection type (VPN or - * not), so it's a bit difficult to add it to checksum maintaining the - * assumption of checksum(empty)=0 - */ -} - /*****************************************************************************/ /* public */ @@ -630,12 +570,12 @@ static const NMDBusInterfaceInfoExtended interface_info_ip6_config = { static void get_property_ip6(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - NMIPConfig *self = NM_IP_CONFIG(object); - NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); - GVariantBuilder builder; - guint len; - guint i; - const struct in6_addr *addrs; + NMIPConfig *self = NM_IP_CONFIG(object); + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + GVariantBuilder builder; + guint len; + guint i; + const char *const *strarr; switch (prop_id) { case PROP_IP6_ADDRESSES: @@ -645,13 +585,19 @@ get_property_ip6(GObject *object, guint prop_id, GValue *value, GParamSpec *pspe g_value_set_variant(value, priv->v_routes); break; case PROP_IP6_NAMESERVERS: - addrs = nm_l3_config_data_get_nameservers(priv->l3cd, AF_INET6, &len); + strarr = nm_l3_config_data_get_nameservers(priv->l3cd, AF_INET6, &len); if (len == 0) g_value_set_variant(value, nm_g_variant_singleton_aay()); else { g_variant_builder_init(&builder, G_VARIANT_TYPE("aay")); - for (i = 0; i < len; i++) - g_variant_builder_add(&builder, "@ay", nm_g_variant_new_ay_in6addr(&addrs[i])); + for (i = 0; i < len; i++) { + struct in6_addr a; + + if (!nm_utils_dnsname_parse_assert(AF_INET6, strarr[i], NULL, &a, NULL)) + continue; + + g_variant_builder_add(&builder, "@ay", nm_g_variant_new_ay_in6addr(&a)); + } g_value_take_variant(value, g_variant_builder_end(&builder)); } break; @@ -736,8 +682,8 @@ _handle_l3cd_changed(NMIPConfig *self, const NML3ConfigData *l3cd) nm_auto_unref_l3cd const NML3ConfigData *l3cd_old = NULL; GParamSpec *changed_params[8]; guint n_changed_params = 0; - const char *const *strv; - const char *const *strv_old; + const char *const *strarr; + const char *const *strarr_old; gconstpointer addrs; gconstpointer addrs_old; guint len; @@ -748,9 +694,9 @@ _handle_l3cd_changed(NMIPConfig *self, const NML3ConfigData *l3cd) l3cd_old = g_steal_pointer(&priv->l3cd); priv->l3cd = nm_l3_config_data_ref(l3cd); - addrs_old = nm_l3_config_data_get_nameservers(l3cd_old, addr_family, &len_old); - addrs = nm_l3_config_data_get_nameservers(priv->l3cd, addr_family, &len); - if (!nm_memeq_n(addrs_old, len_old, addrs, len, nm_utils_addr_family_to_size(addr_family))) { + strarr_old = nm_l3_config_data_get_nameservers(l3cd_old, addr_family, &len_old); + strarr = nm_l3_config_data_get_nameservers(priv->l3cd, addr_family, &len); + if (!nm_strv_equal_n(strarr_old, len_old, strarr, len)) { if (IS_IPv4) { changed_params[n_changed_params++] = obj_properties_ip4[PROP_IP4_NAMESERVER_DATA]; changed_params[n_changed_params++] = obj_properties_ip4[PROP_IP4_NAMESERVERS]; @@ -758,14 +704,14 @@ _handle_l3cd_changed(NMIPConfig *self, const NML3ConfigData *l3cd) changed_params[n_changed_params++] = obj_properties_ip6[PROP_IP6_NAMESERVERS]; } - strv_old = nm_l3_config_data_get_domains(l3cd_old, addr_family, &len_old); - strv = nm_l3_config_data_get_domains(priv->l3cd, addr_family, &len); - if (!nm_strv_equal_n(strv, len, strv_old, len_old)) + strarr_old = nm_l3_config_data_get_domains(l3cd_old, addr_family, &len_old); + strarr = nm_l3_config_data_get_domains(priv->l3cd, addr_family, &len); + if (!nm_strv_equal_n(strarr, len, strarr_old, len_old)) changed_params[n_changed_params++] = obj_properties_ip[PROP_IP_DOMAINS]; - strv_old = nm_l3_config_data_get_searches(l3cd_old, addr_family, &len_old); - strv = nm_l3_config_data_get_searches(priv->l3cd, addr_family, &len); - if (!nm_strv_equal_n(strv, len, strv_old, len_old)) + strarr_old = nm_l3_config_data_get_searches(l3cd_old, addr_family, &len_old); + strarr = nm_l3_config_data_get_searches(priv->l3cd, addr_family, &len); + if (!nm_strv_equal_n(strarr, len, strarr_old, len_old)) changed_params[n_changed_params++] = obj_properties_ip[PROP_IP_SEARCHES]; v_i_old = nm_l3_config_data_get_dns_priority_or_default(l3cd_old, addr_family); @@ -773,9 +719,9 @@ _handle_l3cd_changed(NMIPConfig *self, const NML3ConfigData *l3cd) if (v_i != v_i_old) changed_params[n_changed_params++] = obj_properties_ip[PROP_IP_DNS_PRIORITY]; - strv_old = nm_l3_config_data_get_dns_options(l3cd_old, addr_family, &len); - strv = nm_l3_config_data_get_dns_options(priv->l3cd, addr_family, &len); - if (!nm_strv_equal_n(strv, len, strv_old, len_old)) + strarr_old = nm_l3_config_data_get_dns_options(l3cd_old, addr_family, &len); + strarr = nm_l3_config_data_get_dns_options(priv->l3cd, addr_family, &len); + if (!nm_strv_equal_n(strarr, len, strarr_old, len_old)) changed_params[n_changed_params++] = obj_properties_ip[PROP_IP_DNS_OPTIONS]; if (IS_IPv4) { diff --git a/src/core/nm-ip-config.h b/src/core/nm-ip-config.h index 68103cf7..0dcea83b 100644 --- a/src/core/nm-ip-config.h +++ b/src/core/nm-ip-config.h @@ -12,7 +12,7 @@ /*****************************************************************************/ #define NM_TYPE_IP_CONFIG (nm_ip_config_get_type()) -#define NM_IP_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IP_CONFIG, NMIPConfig)) +#define NM_IP_CONFIG(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IP_CONFIG, NMIPConfig)) #define NM_IP_CONFIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_IP_CONFIG, NMIPConfigClass)) #define NM_IS_IP_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_IP_CONFIG)) @@ -51,8 +51,6 @@ NMIPConfig *nm_ip_config_new(int addr_family, NML3Cfg *l3cfg); void nm_ip_config_take_and_unexport_on_idle(NMIPConfig *self_take); -void nm_ip_config_dns_hash(const NML3ConfigData *l3cd, GChecksum *sum, int addr_family); - /*****************************************************************************/ static inline NML3Cfg * diff --git a/src/core/nm-keep-alive.h b/src/core/nm-keep-alive.h index 7737c17a..14378d25 100644 --- a/src/core/nm-keep-alive.h +++ b/src/core/nm-keep-alive.h @@ -7,7 +7,7 @@ #define __NETWORKMANAGER_KEEP_ALIVE_H__ #define NM_TYPE_KEEP_ALIVE (nm_keep_alive_get_type()) -#define NM_KEEP_ALIVE(o) (G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_KEEP_ALIVE, NMKeepAlive)) +#define NM_KEEP_ALIVE(o) (_NM_G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_KEEP_ALIVE, NMKeepAlive)) #define NM_KEEP_ALIVE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_KEEP_ALIVE, NMKeepAliveClass)) #define NM_KEEP_ALIVE_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS((o), NM_TYPE_KEEP_ALIVE, NMKeepAliveClass)) diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c index bcd0b62a..17bb9db8 100644 --- a/src/core/nm-l3-config-data.c +++ b/src/core/nm-l3-config-data.c @@ -5,7 +5,7 @@ #include "nm-l3-config-data.h" #include -#include +#include "nm-compat-headers/linux/if_addr.h" #include #include "libnm-core-intern/nm-core-internal.h" @@ -67,10 +67,10 @@ struct _NML3ConfigData { union { struct { - GArray *nameservers_6; - GArray *nameservers_4; + GPtrArray *nameservers_6; + GPtrArray *nameservers_4; }; - GArray *nameservers_x[2]; + GPtrArray *nameservers_x[2]; }; union { @@ -269,17 +269,6 @@ _garray_inaddr_get(GArray *arr, guint *out_len) return arr->data; } -static gconstpointer -_garray_inaddr_at(GArray *arr, gboolean IS_IPv4, guint idx) -{ - nm_assert(arr); - nm_assert(idx < arr->len); - - if (IS_IPv4) - return &g_array_index(arr, in_addr_t, idx); - return &g_array_index(arr, struct in6_addr, idx); -} - static gboolean _garray_inaddr_add(GArray **p_arr, int addr_family, gconstpointer addr) { @@ -352,7 +341,7 @@ nm_l3_config_data_log(const NML3ConfigData *self, NMLogDomain log_domain) { char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; int IS_IPv4; guint i; @@ -480,13 +469,11 @@ nm_l3_config_data_log(const NML3ConfigData *self, self->dns_priority_x[IS_IPv4]); } - for (i = 0; i < nm_g_array_len(self->nameservers_x[IS_IPv4]); i++) { + for (i = 0; i < nm_g_ptr_array_len(self->nameservers_x[IS_IPv4]); i++) { _L("nameserver%c[%u]: %s", nm_utils_addr_family_to_char(addr_family), i, - nm_utils_inet_ntop(addr_family, - _garray_inaddr_at(self->nameservers_x[IS_IPv4], IS_IPv4, i), - sbuf_addr)); + (char *) self->nameservers_x[IS_IPv4]->pdata[i]); } for (i = 0; i < nm_g_ptr_array_len(self->domains_x[IS_IPv4]); i++) { @@ -514,12 +501,12 @@ nm_l3_config_data_log(const NML3ConfigData *self, for (i = 0; i < nm_g_array_len(self->wins); i++) { _L("wins[%u]: %s", i, - _nm_utils_inet4_ntop(g_array_index(self->wins, in_addr_t, i), sbuf_addr)); + nm_inet4_ntop(nm_g_array_index(self->wins, in_addr_t, i), sbuf_addr)); } for (i = 0; i < nm_g_array_len(self->nis_servers); i++) { _L("nis-server[%u]: %s", i, - _nm_utils_inet4_ntop(g_array_index(self->nis_servers, in_addr_t, i), sbuf_addr)); + nm_inet4_ntop(nm_g_array_index(self->nis_servers, in_addr_t, i), sbuf_addr)); } if (self->nis_domain) _L("nis-domain: %s", self->nis_domain->str); @@ -621,8 +608,7 @@ nm_l3_config_data_log(const NML3ConfigData *self, static gboolean _route_valid_4(const NMPlatformIP4Route *r) { - return r && r->plen <= 32 - && r->network == nm_utils_ip4_address_clear_host_address(r->network, r->plen); + return r && r->plen <= 32 && r->network == nm_ip4_addr_clear_host_address(r->network, r->plen); } static gboolean @@ -632,7 +618,7 @@ _route_valid_6(const NMPlatformIP6Route *r) return r && r->plen <= 128 && (memcmp(&r->network, - nm_utils_ip6_address_clear_host_address(&n, &r->network, r->plen), + nm_ip6_addr_clear_host_address(&n, &r->network, r->plen), sizeof(n)) == 0); } @@ -793,8 +779,8 @@ nm_l3_config_data_unref(const NML3ConfigData *self) nm_clear_pointer(&mutable->dhcp_lease_4, nm_dhcp_lease_unref); nm_clear_pointer(&mutable->dhcp_lease_6, nm_dhcp_lease_unref); - nm_clear_pointer(&mutable->nameservers_4, g_array_unref); - nm_clear_pointer(&mutable->nameservers_6, g_array_unref); + nm_clear_pointer(&mutable->nameservers_4, g_ptr_array_unref); + nm_clear_pointer(&mutable->nameservers_6, g_ptr_array_unref); nm_clear_pointer(&mutable->domains_4, g_ptr_array_unref); nm_clear_pointer(&mutable->domains_6, g_ptr_array_unref); @@ -1422,7 +1408,7 @@ _check_and_add_domain(GPtrArray **p_arr, const char *domain) return TRUE; } -gconstpointer +const char *const * nm_l3_config_data_get_nameservers(const NML3ConfigData *self, int addr_family, guint *out_len) { nm_assert(!self || _NM_IS_L3_CONFIG_DATA(self, TRUE)); @@ -1434,33 +1420,68 @@ nm_l3_config_data_get_nameservers(const NML3ConfigData *self, int addr_family, g return NULL; } - return _garray_inaddr_get(self->nameservers_x[NM_IS_IPv4(addr_family)], out_len); + return nm_strv_ptrarray_get_unsafe(self->nameservers_x[NM_IS_IPv4(addr_family)], out_len); } gboolean -nm_l3_config_data_add_nameserver(NML3ConfigData *self, - int addr_family, - gconstpointer /* (const NMIPAddr *) */ nameserver) +nm_l3_config_data_add_nameserver(NML3ConfigData *self, int addr_family, const char *nameserver) { + GPtrArray **p_arr; + nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE)); nm_assert_addr_family(addr_family); nm_assert(nameserver); - return _garray_inaddr_add(&self->nameservers_x[NM_IS_IPv4(addr_family)], - addr_family, - nameserver); + if (NM_MORE_ASSERTS > 5) { + gs_free char *s_free = NULL; + + nm_assert( + nm_streq0(nm_utils_dnsname_normalize(addr_family, nameserver, &s_free), nameserver)); + } + + p_arr = &self->nameservers_x[NM_IS_IPv4(addr_family)]; + + if (nm_strv_ptrarray_contains(*p_arr, nameserver)) + return FALSE; + + nm_strv_ptrarray_add_string_dup(nm_strv_ptrarray_ensure(p_arr), nameserver); + return TRUE; +} + +gboolean +nm_l3_config_data_add_nameserver_detail(NML3ConfigData *self, + int addr_family, + gconstpointer addr_bin, + const char *server_name) +{ + gs_free char *s_free = NULL; + char *s; + gsize l; + + nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE)); + nm_assert_addr_family(addr_family); + nm_assert(addr_bin); + + l = (NM_INET_ADDRSTRLEN + 2u) + (server_name ? strlen(server_name) : 0u); + + s = nm_malloc_maybe_a(300, l, &s_free); + + if (!nm_utils_dnsname_construct(addr_family, addr_bin, server_name, s, l)) + nm_assert_not_reached(); + + return nm_l3_config_data_add_nameserver(self, addr_family, s); } gboolean nm_l3_config_data_clear_nameservers(NML3ConfigData *self, int addr_family) { - gs_unref_array GArray *old = NULL; + gs_unref_ptrarray GPtrArray *old = NULL; nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE)); nm_assert_addr_family(addr_family); old = g_steal_pointer(&self->nameservers_x[NM_IS_IPv4(addr_family)]); - return (nm_g_array_len(old) > 0); + return (nm_g_ptr_array_len(old) > 0); } const in_addr_t * @@ -2185,8 +2206,7 @@ _dedup_multi_index_cmp(const NML3ConfigData *a, NM_CMP_DIRECT(ra.metric, rb.metric); NM_CMP_DIRECT(ra.plen, rb.plen); - NM_CMP_RETURN_DIRECT( - nm_utils_ip4_address_same_prefix_cmp(ra.network, rb.network, ra.plen)); + NM_CMP_RETURN_DIRECT(nm_ip4_addr_same_prefix_cmp(ra.network, rb.network, ra.plen)); break; } case NMP_OBJECT_TYPE_IP6_ROUTE: @@ -2197,7 +2217,7 @@ _dedup_multi_index_cmp(const NML3ConfigData *a, NM_CMP_DIRECT(ra.metric, rb.metric); NM_CMP_DIRECT(ra.plen, rb.plen); NM_CMP_RETURN_DIRECT( - nm_utils_ip6_address_same_prefix_cmp(&ra.network, &rb.network, ra.plen)); + nm_ip6_addr_same_prefix_cmp(&ra.network, &rb.network, ra.plen)); break; } default: @@ -2274,7 +2294,6 @@ nm_l3_config_data_cmp_full(const NML3ConfigData *a, } for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { - const int addr_family = IS_IPv4 ? AF_INET : AF_INET6; const NMPObject *def_route_a = a->best_default_route_x[IS_IPv4]; const NMPObject *def_route_b = b->best_default_route_x[IS_IPv4]; @@ -2298,8 +2317,7 @@ nm_l3_config_data_cmp_full(const NML3ConfigData *a, NM_CMP_DIRECT(ra.metric, rb.metric); NM_CMP_DIRECT(ra.plen, rb.plen); - NM_CMP_RETURN_DIRECT( - nm_utils_ip4_address_same_prefix_cmp(ra.network, rb.network, ra.plen)); + NM_CMP_RETURN_DIRECT(nm_ip4_addr_same_prefix_cmp(ra.network, rb.network, ra.plen)); } else { NMPlatformIP6Route ra = def_route_a->ip6_route; NMPlatformIP6Route rb = def_route_b->ip6_route; @@ -2307,16 +2325,15 @@ nm_l3_config_data_cmp_full(const NML3ConfigData *a, NM_CMP_DIRECT(ra.metric, rb.metric); NM_CMP_DIRECT(ra.plen, rb.plen); NM_CMP_RETURN_DIRECT( - nm_utils_ip6_address_same_prefix_cmp(&ra.network, &rb.network, ra.plen)); + nm_ip6_addr_same_prefix_cmp(&ra.network, &rb.network, ra.plen)); } } if (NM_FLAGS_HAS(flags, NM_L3_CONFIG_CMP_FLAGS_DNS)) { const NML3ConfigDatFlags FLAG = NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY(IS_IPv4); - NM_CMP_RETURN(_garray_inaddr_cmp(a->nameservers_x[IS_IPv4], - b->nameservers_x[IS_IPv4], - addr_family)); + NM_CMP_RETURN( + nm_strv_ptrarray_cmp(a->nameservers_x[IS_IPv4], b->nameservers_x[IS_IPv4])); NM_CMP_RETURN(nm_strv_ptrarray_cmp(a->domains_x[IS_IPv4], b->domains_x[IS_IPv4])); NM_CMP_RETURN(nm_strv_ptrarray_cmp(a->searches_x[IS_IPv4], b->searches_x[IS_IPv4])); NM_CMP_RETURN( @@ -2391,10 +2408,7 @@ nm_l3_config_data_cmp_full(const NML3ConfigData *a, /*****************************************************************************/ static const NMPObject * -_data_get_direct_route_for_host(const NML3ConfigData *self, - int addr_family, - gconstpointer host, - guint32 route_table) +_data_get_direct_route_for_host(const NML3ConfigData *self, int addr_family, gconstpointer host) { const int IS_IPv4 = NM_IS_IPv4(addr_family); const NMPObject *best_route_obj = NULL; @@ -2422,13 +2436,7 @@ _data_get_direct_route_for_host(const NML3ConfigData *self, if (best_route && best_route->rx.plen > item->rx.plen) continue; - if (nm_platform_route_table_uncoerce(item->rx.table_coerced, TRUE) != route_table) - continue; - - if (!nm_utils_ip_address_same_prefix(addr_family, - host, - item->rx.network_ptr, - item->rx.plen)) + if (!nm_ip_addr_same_prefix(addr_family, host, item->rx.network_ptr, item->rx.plen)) continue; if (best_route && best_route->rx.metric <= item->rx.metric) @@ -2478,9 +2486,9 @@ nm_l3_config_data_get_blacklisted_ip4_routes(const NML3ConfigData *self, gboolea if (my_addr->plen == 0) continue; - network_4 = nm_utils_ip4_address_clear_host_address(my_addr->peer_address, my_addr->plen); + network_4 = nm_ip4_addr_clear_host_address(my_addr->peer_address, my_addr->plen); - if (nm_utils_ip4_address_is_zeronet(network_4)) { + if (nm_ip4_addr_is_zeronet(network_4)) { /* Kernel doesn't add device-routes for destinations that * start with 0.x.y.z. Skip them. */ continue; @@ -2555,12 +2563,10 @@ nm_l3_config_data_add_dependent_onlink_routes(NML3ConfigData *self, int addr_fam if (nm_ip_addr_is_null(addr_family, p_gateway)) continue; - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route_src) - || _data_get_direct_route_for_host( - self, - addr_family, - p_gateway, - nm_platform_route_table_uncoerce(route_src->rx.table_coerced, TRUE))) + if (NM_FLAGS_HAS(route_src->rx.r_rtm_flags, (unsigned) RTNH_F_ONLINK)) + continue; + + if (_data_get_direct_route_for_host(self, addr_family, p_gateway)) continue; new_route = nmp_object_clone(obj_src, FALSE); @@ -2672,9 +2678,9 @@ nm_l3_config_data_add_dependent_device_routes(NML3ConfigData *self, a6 = &addr_src->a6.peer_address; plen = 128; } else { - a6 = nm_utils_ip6_address_clear_host_address(&a6_stack, - &addr_src->a6.address, - addr_src->a6.plen); + a6 = nm_ip6_addr_clear_host_address(&a6_stack, + &addr_src->a6.address, + addr_src->a6.plen); plen = addr_src->a6.plen; } @@ -2819,12 +2825,12 @@ _init_from_connection_ip(NML3ConfigData *self, int addr_family, NMConnection *co plen = nm_ip_route_get_prefix(s_route); - nm_utils_ipx_address_clear_host_address(addr_family, &network_bin, &network_bin, plen); + nm_ip_addr_clear_host_address(addr_family, &network_bin, &network_bin, plen); if (IS_IPv4) { r.r4 = (NMPlatformIP4Route){ .network = network_bin.addr4, - .plen = nm_ip_route_get_prefix(s_route), + .plen = plen, .gateway = next_hop_bin.addr4, .metric_any = metric_any, .metric = metric, @@ -2834,7 +2840,7 @@ _init_from_connection_ip(NML3ConfigData *self, int addr_family, NMConnection *co } else { r.r6 = (NMPlatformIP6Route){ .network = network_bin.addr6, - .plen = nm_ip_route_get_prefix(s_route), + .plen = plen, .gateway = next_hop_bin.addr6, .metric_any = metric_any, .metric = metric, @@ -2845,19 +2851,28 @@ _init_from_connection_ip(NML3ConfigData *self, int addr_family, NMConnection *co nm_utils_ip_route_attribute_to_platform(addr_family, s_route, &r.rx, -1); + if (IS_IPv4 && r.r4.gateway == 0) { + /* As far as kernel is concerned, an IPv4 without gateway cannot have + * the onlink flag set, we need to clear it. If we wouldn't clear it, + * then the address we would add in kernel would be entirely different than + * the one we create here (because the "onlink" flag is part of the + * identifier of a route, see nm_platform_ip4_route_cmp()). + * + * Note however that for ECMP routes we currently can only merge routes + * that agree in their onlink flag. So a route without gateway cannot + * merge with an onlink route that has a gateway. That needs fixing, + * by not treating the onlink flag as for the entire route, but allowing + * to merge ECMP routes with different onlink flag. And first, we need + * to track the onlink flag for the nexthop (NMPlatformIP4RtNextHop). */ + r.r4.r_rtm_flags &= ~((unsigned) RTNH_F_ONLINK); + } + nm_l3_config_data_add_route(self, addr_family, NULL, &r.rx); } nnameservers = nm_setting_ip_config_get_num_dns(s_ip); - for (i = 0; i < nnameservers; i++) { - const char *s; - NMIPAddr ip; - - s = nm_setting_ip_config_get_dns(s_ip, i); - if (!nm_utils_parse_inaddr_bin(addr_family, s, NULL, &ip)) - continue; - nm_l3_config_data_add_nameserver(self, addr_family, &ip); - } + for (i = 0; i < nnameservers; i++) + nm_l3_config_data_add_nameserver(self, addr_family, nm_setting_ip_config_get_dns(s_ip, i)); nsearches = nm_setting_ip_config_get_num_dns_searches(s_ip); for (i = 0; i < nsearches; i++) { @@ -3022,6 +3037,107 @@ nm_l3_config_data_new_from_platform(NMDedupMultiIndex *multi_idx, /*****************************************************************************/ +void +nm_l3_config_data_hash_dns(const NML3ConfigData *l3cd, + GChecksum *sum, + int addr_family, + NMDnsIPConfigType dns_ip_config_type) +{ + guint i; + int val; + const char *const *strarr; + const in_addr_t *wins; + const char *const *domains; + const char *const *searches; + const char *const *options; + guint num_nameservers; + guint num_wins; + guint num_domains; + guint num_searches; + guint num_options; + gboolean empty = TRUE; + + g_return_if_fail(l3cd); + g_return_if_fail(sum); + + strarr = nm_l3_config_data_get_nameservers(l3cd, addr_family, &num_nameservers); + for (i = 0; i < num_nameservers; i++) { + g_checksum_update(sum, (gpointer) strarr[i], strlen(strarr[i])); + empty = FALSE; + } + + if (addr_family == AF_INET) { + wins = nm_l3_config_data_get_wins(l3cd, &num_wins); + for (i = 0; i < num_wins; i++) { + g_checksum_update(sum, (guint8 *) &wins[i], 4); + empty = FALSE; + } + } + + domains = nm_l3_config_data_get_domains(l3cd, addr_family, &num_domains); + for (i = 0; i < num_domains; i++) { + g_checksum_update(sum, (const guint8 *) domains[i], strlen(domains[i])); + empty = FALSE; + } + + searches = nm_l3_config_data_get_searches(l3cd, addr_family, &num_searches); + for (i = 0; i < num_searches; i++) { + g_checksum_update(sum, (const guint8 *) searches[i], strlen(searches[i])); + empty = FALSE; + } + + options = nm_l3_config_data_get_dns_options(l3cd, addr_family, &num_options); + for (i = 0; i < num_options; i++) { + g_checksum_update(sum, (const guint8 *) options[i], strlen(options[i])); + empty = FALSE; + } + + val = nm_l3_config_data_get_mdns(l3cd); + if (val != NM_SETTING_CONNECTION_MDNS_DEFAULT) { + g_checksum_update(sum, (const guint8 *) &val, sizeof(val)); + empty = FALSE; + } + + val = nm_l3_config_data_get_llmnr(l3cd); + if (val != NM_SETTING_CONNECTION_LLMNR_DEFAULT) { + g_checksum_update(sum, (const guint8 *) &val, sizeof(val)); + empty = FALSE; + } + + val = nm_l3_config_data_get_dns_over_tls(l3cd); + if (val != NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT) { + g_checksum_update(sum, (const guint8 *) &val, sizeof(val)); + empty = FALSE; + } + + if (!empty) { + int prio = 0; + + /* In the DNS manager we rely on the fact that an empty (i.e. without + * any name server, domain, option, etc. parameters) configuration + * has a zero checksum. This is needed to avoid rewriting resolv.conf + * when not needed. Since the dns-type and the priority are always + * present, hash them only when the rest of configuration is not empty. + */ + g_checksum_update(sum, (const guint8 *) &dns_ip_config_type, sizeof(dns_ip_config_type)); + nm_l3_config_data_get_dns_priority(l3cd, addr_family, &prio); + g_checksum_update(sum, (const guint8 *) &prio, sizeof(prio)); + } + + /* FIXME(ip-config-checksum): the DNS priority should be considered relevant + * and added into the checksum as well, but this can't be done right now + * because in the DNS manager we rely on the fact that an empty + * configuration (i.e. just created) has a zero checksum. This is needed to + * avoid rewriting resolv.conf when there is no change. + * + * The DNS priority initial value depends on the connection type (VPN or + * not), so it's a bit difficult to add it to checksum maintaining the + * assumption of checksum(empty)=0 + */ +} + +/*****************************************************************************/ + void nm_l3_config_data_merge(NML3ConfigData *self, const NML3ConfigData *src, @@ -3212,9 +3328,7 @@ nm_l3_config_data_merge(NML3ConfigData *self, } if (!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS)) - _garray_inaddr_merge(&self->nameservers_x[IS_IPv4], - src->nameservers_x[IS_IPv4], - addr_family); + _strv_ptrarray_merge(&self->nameservers_x[IS_IPv4], src->nameservers_x[IS_IPv4]); if (!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS)) _strv_ptrarray_merge(&self->domains_x[IS_IPv4], src->domains_x[IS_IPv4]); diff --git a/src/core/nm-l3-config-data.h b/src/core/nm-l3-config-data.h index ca42d4a1..bfab04d9 100644 --- a/src/core/nm-l3-config-data.h +++ b/src/core/nm-l3-config-data.h @@ -495,12 +495,16 @@ const in_addr_t *nm_l3_config_data_get_wins(const NML3ConfigData *self, guint *o gboolean nm_l3_config_data_add_wins(NML3ConfigData *self, in_addr_t wins); -gconstpointer +const char *const * nm_l3_config_data_get_nameservers(const NML3ConfigData *self, int addr_family, guint *out_len); -gboolean nm_l3_config_data_add_nameserver(NML3ConfigData *self, - int addr_family, - gconstpointer /* (const NMIPAddr *) */ nameserver); +gboolean +nm_l3_config_data_add_nameserver(NML3ConfigData *self, int addr_family, const char *nameserver); + +gboolean nm_l3_config_data_add_nameserver_detail(NML3ConfigData *self, + int addr_family, + gconstpointer addr_bin, + const char *server_name); gboolean nm_l3_config_data_clear_nameservers(NML3ConfigData *self, int addr_family); @@ -600,4 +604,9 @@ nmtst_l3_config_data_get_best_gateway(const NML3ConfigData *self, int addr_famil return nm_platform_ip_route_get_gateway(addr_family, NMP_OBJECT_CAST_IP_ROUTE(rt)); } +void nm_l3_config_data_hash_dns(const NML3ConfigData *l3cd, + GChecksum *sum, + int addr_family, + NMDnsIPConfigType dns_ip_config_type); + #endif /* __NM_L3_CONFIG_DATA_H__ */ diff --git a/src/core/nm-l3-ipv4ll.c b/src/core/nm-l3-ipv4ll.c index 3fc1e165..ec871bef 100644 --- a/src/core/nm-l3-ipv4ll.c +++ b/src/core/nm-l3-ipv4ll.c @@ -104,17 +104,17 @@ NM_UTILS_ENUM2STR_DEFINE(nm_l3_ipv4ll_state_to_string, /*****************************************************************************/ -#define _ASSERT(self) \ - G_STMT_START \ - { \ - NML3IPv4LL *const _self = (self); \ - \ - nm_assert(NM_IS_L3_IPV4LL(_self)); \ - if (NM_MORE_ASSERTS > 5) { \ - nm_assert(_self->addr == 0u || nm_utils_ip4_address_is_link_local(_self->addr)); \ - nm_assert(!_self->l3cd || NM_IS_L3_CONFIG_DATA(_self->l3cd)); \ - } \ - } \ +#define _ASSERT(self) \ + G_STMT_START \ + { \ + NML3IPv4LL *const _self = (self); \ + \ + nm_assert(NM_IS_L3_IPV4LL(_self)); \ + if (NM_MORE_ASSERTS > 5) { \ + nm_assert(_self->addr == 0u || nm_ip4_addr_is_link_local(_self->addr)); \ + nm_assert(!_self->l3cd || NM_IS_L3_CONFIG_DATA(_self->l3cd)); \ + } \ + } \ G_STMT_END /*****************************************************************************/ @@ -290,7 +290,7 @@ _ip4_address_is_link_local(const NMPlatformIP4Address *a) { nm_assert(a); - return nm_utils_ip4_address_is_link_local(a->address) && a->plen == ADDR_IPV4LL_PREFIX_LEN + return nm_ip4_addr_is_link_local(a->address) && a->plen == ADDR_IPV4LL_PREFIX_LEN && a->address == a->peer_address; } @@ -322,7 +322,7 @@ _l3cd_config_create(int ifindex, in_addr_t addr, NMDedupMultiIndex *multi_idx) { nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; - nm_assert(nm_utils_ip4_address_is_link_local(addr)); + nm_assert(nm_ip4_addr_is_link_local(addr)); nm_assert(ifindex > 0); nm_assert(multi_idx); @@ -382,7 +382,7 @@ static void _ipv4ll_addrgen(NML3IPv4LL *self, gboolean generate_new_addr) { CSipHash state; - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; gboolean seed_changed = FALSE; in_addr_t addr_new; guint64 h; @@ -459,7 +459,7 @@ _ipv4ll_addrgen(NML3IPv4LL *self, gboolean generate_new_addr) if (!seed_changed && !generate_new_addr) { /* neither did the caller request a new address, nor was the seed changed. The current * address is still to be used. */ - nm_assert(nm_utils_ip4_address_is_link_local(self->addr)); + nm_assert(nm_ip4_addr_is_link_local(self->addr)); return; } @@ -492,9 +492,9 @@ gen_addr: if (self->addr == addr_new || NM_IN_SET(ntohl(addr_new) & 0x0000FF00u, 0x0000u, 0xFF00u)) goto gen_addr; - nm_assert(nm_utils_ip4_address_is_link_local(addr_new)); + nm_assert(nm_ip4_addr_is_link_local(addr_new)); - _LOGT("addr-gen: set address %s", _nm_utils_inet4_ntop(addr_new, sbuf_addr)); + _LOGT("addr-gen: set address %s", nm_inet4_ntop(addr_new, sbuf_addr)); self->addr = addr_new; } @@ -549,7 +549,7 @@ static void _l3cd_config_add(NML3IPv4LL *self) { nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; gboolean changed; _ASSERT(self); @@ -571,7 +571,7 @@ _l3cd_config_add(NML3IPv4LL *self) self->l3cd_timeout_msec_changed = FALSE; _LOGT("add l3cd config with %s (acd-timeout %u msec%s)", - _nm_utils_inet4_ntop(self->addr, sbuf_addr), + nm_inet4_ntop(self->addr, sbuf_addr), self->reg_timeout_msec, changed ? "" : " changed"); @@ -639,7 +639,7 @@ _ipv4ll_platform_ip4_address_lookup(NML3IPv4LL *self, in_addr_t addr) if (addr == 0u) return NULL; - nm_assert(nm_utils_ip4_address_is_link_local(addr)); + nm_assert(nm_ip4_addr_is_link_local(addr)); pladdr = nm_platform_ip4_address_get(nm_l3_ipv4ll_get_platform(self), nm_l3_ipv4ll_get_ifindex(self), @@ -658,7 +658,7 @@ _ipv4ll_l3cfg_get_acd_addr_info(NML3IPv4LL *self, in_addr_t addr) if (addr == 0u) return NULL; - nm_assert(nm_utils_ip4_address_is_link_local(addr)); + nm_assert(nm_ip4_addr_is_link_local(addr)); return nm_l3cfg_get_acd_addr_info(self->l3cfg, addr); } @@ -766,7 +766,7 @@ _ipv4ll_set_timed_out_update(NML3IPv4LL *self, TimedOutState new_state) static gboolean _ipv4ll_set_state(NML3IPv4LL *self, NML3IPv4LLState state) { - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; char sbuf100[100]; if (self->state == state) @@ -775,7 +775,7 @@ _ipv4ll_set_state(NML3IPv4LL *self, NML3IPv4LLState state) self->notify_on_idle = TRUE; _LOGT("state: set state %s (addr=%s)", nm_l3_ipv4ll_state_to_string(state, sbuf100, sizeof(sbuf100)), - _nm_utils_inet4_ntop(self->addr, sbuf_addr)); + nm_inet4_ntop(self->addr, sbuf_addr)); return TRUE; } diff --git a/src/core/nm-l3-ipv4ll.h b/src/core/nm-l3-ipv4ll.h index b39762a8..af74ce08 100644 --- a/src/core/nm-l3-ipv4ll.h +++ b/src/core/nm-l3-ipv4ll.h @@ -45,7 +45,7 @@ NM_IS_L3_IPV4LL(const NML3IPv4LL *self) { nm_assert(!self || (NM_IS_L3CFG(*((NML3Cfg **) self)) - && (*((int *) (((char *) self) + sizeof(gpointer)))) > 0)); + && *(NM_CAST_ALIGN(int, (((char *) self) + sizeof(gpointer)))) > 0)); return !!self; } diff --git a/src/core/nm-l3-ipv6ll.c b/src/core/nm-l3-ipv6ll.c index 2640c075..2e2a6a0a 100644 --- a/src/core/nm-l3-ipv6ll.c +++ b/src/core/nm-l3-ipv6ll.c @@ -4,7 +4,7 @@ #include "nm-l3-ipv6ll.h" -#include +#include "nm-compat-headers/linux/if_addr.h" #include "nm-core-utils.h" @@ -216,7 +216,7 @@ _emit_changed_on_idle_cb(gpointer user_data) _LOGT("emit changed signal (state=%s%s%s)", nm_l3_ipv6ll_state_to_string(state), lladdr ? ", " : "", - lladdr ? _nm_utils_inet6_ntop(lladdr, sbuf) : ""); + lladdr ? nm_inet6_ntop(lladdr, sbuf) : ""); self->notify_fcn(self, state, lladdr, self->user_data); @@ -525,14 +525,14 @@ _check(NML3IPv6LL *self) if (_pladdr_is_ll_tentative(pladdr)) { if (_set_cur_lladdr_obj(self, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, pladdr)) { _LOGT("changed: waiting for address %s to complete DAD", - _nm_utils_inet6_ntop(&self->cur_lladdr, sbuf)); + nm_inet6_ntop(&self->cur_lladdr, sbuf)); _lladdr_handle_changed(self); } return; } if (_set_cur_lladdr_obj(self, NM_L3_IPV6LL_STATE_READY, pladdr)) { - _LOGT("changed: address %s is ready", _nm_utils_inet6_ntop(&self->cur_lladdr, sbuf)); + _LOGT("changed: address %s is ready", nm_inet6_ntop(&self->cur_lladdr, sbuf)); _lladdr_handle_changed(self); } return; @@ -545,9 +545,9 @@ _check(NML3IPv6LL *self) NM_IN_SET(self->state, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, NM_L3_IPV6LL_STATE_READY)); if (self->state == NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS) _LOGT("changed: address %s did not complete DAD", - _nm_utils_inet6_ntop(&self->cur_lladdr, sbuf)); + nm_inet6_ntop(&self->cur_lladdr, sbuf)); else { - _LOGT("changed: address %s is gone", _nm_utils_inet6_ntop(&self->cur_lladdr, sbuf)); + _LOGT("changed: address %s is gone", nm_inet6_ntop(&self->cur_lladdr, sbuf)); } /* reset the state here, so that we are sure that the following @@ -579,8 +579,7 @@ _check(NML3IPv6LL *self) * If that does not happen within timeout, we assume that this address failed DAD. */ self->wait_for_addr_source = nm_g_timeout_add_source(2000, _wait_for_addr_timeout_cb, self); if (_set_cur_lladdr_bin(self, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, &lladdr)) { - _LOGT("changed: starting DAD for address %s", - _nm_utils_inet6_ntop(&self->cur_lladdr, sbuf)); + _LOGT("changed: starting DAD for address %s", nm_inet6_ntop(&self->cur_lladdr, sbuf)); _lladdr_handle_changed(self); } return; diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index 8fa1e07d..a49654fe 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -7,7 +7,7 @@ #include "libnm-std-aux/nm-linux-compat.h" #include -#include +#include "nm-compat-headers/linux/if_addr.h" #include #include @@ -358,16 +358,16 @@ G_DEFINE_TYPE(NML3Cfg, nm_l3cfg, G_TYPE_OBJECT) } \ G_STMT_END -#define _LOGT_acd(acd_data, ...) \ - G_STMT_START \ - { \ - char _sbuf_acd[NM_UTILS_INET_ADDRSTRLEN]; \ - \ - _LOGT("acd[%s, %s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _nm_utils_inet4_ntop((acd_data)->info.addr, _sbuf_acd), \ - _l3_acd_addr_state_to_string((acd_data)->info.state) \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ +#define _LOGT_acd(acd_data, ...) \ + G_STMT_START \ + { \ + char _sbuf_acd[NM_INET_ADDRSTRLEN]; \ + \ + _LOGT("acd[%s, %s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + nm_inet4_ntop((acd_data)->info.addr, _sbuf_acd), \ + _l3_acd_addr_state_to_string((acd_data)->info.state) \ + _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ G_STMT_END /*****************************************************************************/ @@ -548,7 +548,7 @@ _l3_config_notify_data_to_string(const NML3ConfigNotifyData *notify_data, char *sbuf, gsize sbuf_size) { - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; char sbuf100[100]; char sbufobf[NM_HASH_OBFUSCATE_PTR_STR_BUF_SIZE]; char *s = sbuf; @@ -577,7 +577,7 @@ _l3_config_notify_data_to_string(const NML3ConfigNotifyData *notify_data, nm_strbuf_append(&s, &l, ", addr=%s, state=%s", - _nm_utils_inet4_ntop(notify_data->acd_event.info.addr, sbuf_addr), + nm_inet4_ntop(notify_data->acd_event.info.addr, sbuf_addr), _l3_acd_addr_state_to_string(notify_data->acd_event.info.state)); break; case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE: @@ -603,7 +603,7 @@ _l3_config_notify_data_to_string(const NML3ConfigNotifyData *notify_data, &l, ", ipv4ll=" NM_HASH_OBFUSCATE_PTR_FMT "%s%s, state=%s", NM_HASH_OBFUSCATE_PTR(notify_data->ipv4ll_event.ipv4ll), - NM_PRINT_FMT_QUOTED2(addr4 != 0, ", addr=", _nm_utils_inet4_ntop(addr4, sbuf_addr), ""), + NM_PRINT_FMT_QUOTED2(addr4 != 0, ", addr=", nm_inet4_ntop(addr4, sbuf_addr), ""), nm_l3_ipv4ll_state_to_string(nm_l3_ipv4ll_get_state(notify_data->ipv4ll_event.ipv4ll), sbuf100, sizeof(sbuf100))); @@ -1143,7 +1143,7 @@ _commit_collect_routes(NML3Cfg *self, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)); if (!head_entry) - return; + goto loop_done; c_list_for_each_entry (entry, &head_entry->lst_entries_head, lst_entries) { const NMPObject *obj = entry->obj; @@ -1152,6 +1152,14 @@ _commit_collect_routes(NML3Cfg *self, if (_obj_is_route_nodev(obj)) r = routes_nodev; else { + nm_assert(NMP_OBJECT_CAST_IP_ROUTE(obj)->ifindex == self->priv.ifindex); + + if (IS_IPv4 && NMP_OBJECT_CAST_IP4_ROUTE(obj)->weight > 0) { + /* This route needs to be registered as ECMP route. */ + nm_netns_ip_route_ecmp_register(self->priv.netns, self, obj); + continue; + } + if (!_obj_states_sync_filter(self, obj, commit_type)) continue; r = routes; @@ -1162,6 +1170,36 @@ _commit_collect_routes(NML3Cfg *self, g_ptr_array_add(*r, (gpointer) nmp_object_ref(obj)); } + +loop_done: + + if (IS_IPv4) { + gs_unref_ptrarray GPtrArray *singlehop_routes = NULL; + guint i; + + /* NMNetns will merge the routes. The ones that found a merge partner are true multihop + * routes, with potentially a next hop on different interfaces. The routes + * that didn't find a merge partner are returned in "singlehop_routes". */ + nm_netns_ip_route_ecmp_commit(self->priv.netns, + self, + &singlehop_routes, + NM_IN_SET(commit_type, NM_L3_CFG_COMMIT_TYPE_REAPPLY)); + + if (singlehop_routes) { + for (i = 0; i < singlehop_routes->len; i++) { + const NMPObject *obj = singlehop_routes->pdata[i]; + + if (!_obj_states_sync_filter(self, obj, commit_type)) + continue; + + if (!*routes) + *routes = + g_ptr_array_new_with_free_func((GDestroyNotify) nm_dedup_multi_obj_unref); + + g_ptr_array_add(*routes, (gpointer) nmp_object_ref(obj)); + } + } + } } static void @@ -1430,7 +1468,8 @@ _acd_data_free(AcdData *acd_data) } static guint -_acd_data_collect_tracks_data(const AcdData *acd_data, +_acd_data_collect_tracks_data(NML3Cfg *self, + const AcdData *acd_data, NMTernary dirty_selector, guint32 *out_best_acd_timeout_msec, NML3AcdDefendType *out_best_acd_defend_type) @@ -1466,6 +1505,12 @@ _acd_data_collect_tracks_data(const AcdData *acd_data, nm_assert(n == 0 || best_acd_defend_type > _NM_L3_ACD_DEFEND_TYPE_NONE); nm_assert(best_acd_defend_type <= NM_L3_ACD_DEFEND_TYPE_ALWAYS); + if (self->priv.ifindex == NM_LOOPBACK_IFINDEX) { + /* On loopback interface, ACD makes no sense. We always force the + * timeout to zero, which means no ACD. */ + best_acd_timeout_msec = 0; + } + NM_SET_OUT(out_best_acd_timeout_msec, n > 0 ? best_acd_timeout_msec : 0u); NM_SET_OUT(out_best_acd_defend_type, best_acd_defend_type); return n; @@ -2230,7 +2275,7 @@ _l3_acd_data_state_change(NML3Cfg *self, gint64 now_msec; const char *log_reason; char sbuf256[256]; - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; if (!p_now_msec) { now_msec = 0; @@ -2313,7 +2358,8 @@ _l3_acd_data_state_change(NML3Cfg *self, return; handle_init: - if (_acd_data_collect_tracks_data(acd_data, + if (_acd_data_collect_tracks_data(self, + acd_data, NM_TERNARY_FALSE, &acd_timeout_msec, &acd_defend_type) @@ -2362,7 +2408,8 @@ handle_init: /* we just did a commit of the IP configuration and now visit all ACD states * and kick off the necessary actions... */ - if (_acd_data_collect_tracks_data(acd_data, + if (_acd_data_collect_tracks_data(self, + acd_data, NM_TERNARY_TRUE, &acd_timeout_msec, &acd_defend_type) @@ -2465,7 +2512,8 @@ handle_init: /* after a timeout, re-probe the address. This only happens if the caller * does not deconfigure the address after USED/CONFLICT. But in that case, * we eventually want to retry. */ - if (_acd_data_collect_tracks_data(acd_data, + if (_acd_data_collect_tracks_data(self, + acd_data, NM_TERNARY_TRUE, &acd_timeout_msec, &acd_defend_type) @@ -2550,7 +2598,7 @@ handle_init: if (!_l3_acd_data_defendconflict_warning_ratelimited(acd_data, p_now_msec)) { _LOGI("IPv4 address %s is used on network connected to interface %d%s%s%s from " "host %s", - _nm_utils_inet4_ntop(acd_data->info.addr, sbuf_addr), + nm_inet4_ntop(acd_data->info.addr, sbuf_addr), self->priv.ifindex, NM_PRINT_FMT_QUOTED(self->priv.plobj_next, " (", @@ -2565,7 +2613,7 @@ handle_init: nm_assert(acd_data->info.state == NM_L3_ACD_ADDR_STATE_DEFENDING); _LOGT_acd(acd_data, "address %s defended from %s", - _nm_utils_inet4_ntop(acd_data->info.addr, sbuf_addr), + nm_inet4_ntop(acd_data->info.addr, sbuf_addr), nm_ether_addr_to_string_a(sender_addr)); /* we just log an info message. Nothing else to do. */ return; @@ -2575,7 +2623,7 @@ handle_init: _LOGT_acd(acd_data, "address conflict for %s detected with %s", - _nm_utils_inet4_ntop(acd_data->info.addr, sbuf_addr), + nm_inet4_ntop(acd_data->info.addr, sbuf_addr), nm_ether_addr_to_string_a(sender_addr)); if (!_l3_acd_data_defendconflict_warning_ratelimited(acd_data, p_now_msec)) { @@ -2587,7 +2635,7 @@ handle_init: NMP_OBJECT_CAST_LINK(self->priv.plobj_next)->name, ")", ""), - _nm_utils_inet4_ntop(acd_data->info.addr, sbuf_addr), + nm_inet4_ntop(acd_data->info.addr, sbuf_addr), nm_ether_addr_to_string_a(sender_addr)); } @@ -2990,68 +3038,103 @@ nm_l3cfg_check_ready(NML3Cfg *self, const NML3ConfigData *l3cd, int addr_family, NML3CfgCheckReadyFlags flags, - gboolean *acd_used) + GArray **conflicts) { NMDedupMultiIter iter; const NMPObject *obj; + gboolean ready = TRUE; nm_assert(NM_IS_L3CFG(self)); - nm_assert_addr_family_or_unspec(addr_family); - - NM_SET_OUT(acd_used, FALSE); + nm_assert_addr_family(addr_family); + nm_assert(!conflicts || !*conflicts); if (!l3cd) return TRUE; - if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET) - && NM_FLAGS_HAS(flags, NM_L3CFG_CHECK_READY_FLAGS_IP4_ACD_READY)) { - gboolean pending = FALSE; - - nm_l3_config_data_iter_obj_for_each (&iter, l3cd, &obj, NMP_OBJECT_TYPE_IP4_ADDRESS) { - const NML3AcdAddrInfo *addr_info; - - addr_info = nm_l3cfg_get_acd_addr_info(self, NMP_OBJECT_CAST_IP4_ADDRESS(obj)->address); - if (!addr_info) { - /* We don't track the this address? That's odd. Not ready. */ - pending = TRUE; - } else { - if (addr_info->state <= NM_L3_ACD_ADDR_STATE_PROBING) { - /* Still probing. Not ready. */ - pending = TRUE; - } else if (addr_info->state == NM_L3_ACD_ADDR_STATE_USED) { - NM_SET_OUT(acd_used, TRUE); + if (addr_family == AF_INET) { + if (NM_FLAGS_HAS(flags, NM_L3CFG_CHECK_READY_FLAGS_IP4_ACD_READY)) { + nm_l3_config_data_iter_obj_for_each (&iter, l3cd, &obj, NMP_OBJECT_TYPE_IP4_ADDRESS) { + const NML3AcdAddrInfo *addr_info; + + addr_info = + nm_l3cfg_get_acd_addr_info(self, NMP_OBJECT_CAST_IP4_ADDRESS(obj)->address); + if (!addr_info) { + /* We don't track the this address? That's odd. Not ready. */ + ready = FALSE; + } else { + if (addr_info->state <= NM_L3_ACD_ADDR_STATE_PROBING) { + /* Still probing. Not ready. */ + ready = FALSE; + } else if (addr_info->state == NM_L3_ACD_ADDR_STATE_USED) { + if (conflicts) { + if (!*conflicts) + *conflicts = g_array_new(FALSE, + FALSE, + nm_utils_addr_family_to_size(addr_family)); + g_array_append_val(*conflicts, addr_info->addr); + } + } } + /* we only care that we don't have ACD still pending. Otherwise we are ready, + * including if we have no addr_info about this address or the address is in use. */ } - /* we only care that we don't have ACD still pending. Otherwise we are ready, - * including if we have no addr_info about this address or the address is in use. */ } - if (pending) - return FALSE; + return ready; } - if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6) - && NM_FLAGS_HAS(flags, NM_L3CFG_CHECK_READY_FLAGS_IP6_DAD_READY)) { + /* AF_INET6 */ + if (NM_FLAGS_HAS(flags, NM_L3CFG_CHECK_READY_FLAGS_IP6_DAD_READY)) { nm_l3_config_data_iter_obj_for_each (&iter, l3cd, &obj, NMP_OBJECT_TYPE_IP6_ADDRESS) { ObjStateData *obj_state; + gboolean dadfailed = FALSE; obj_state = g_hash_table_lookup(self->priv.p->obj_state_hash, &obj); if (!obj_state) { /* Hm, we don't track this object? That is odd. Not ready. */ - return FALSE; + ready = FALSE; + continue; } if (!obj_state->os_nm_configured && !obj_state->os_plobj) { /* We didn't (yet) configure this address and it also is not in platform. * Not ready. */ - return FALSE; + ready = FALSE; + continue; + } + + if (obj_state->os_plobj + && NM_FLAGS_HAS(NMP_OBJECT_CAST_IP6_ADDRESS(obj_state->os_plobj)->n_ifa_flags, + IFA_F_DADFAILED)) { + /* The address is still present with DADFAILED flag. */ + dadfailed = TRUE; + } else if (obj_state->os_nm_configured && !obj_state->os_plobj + && nm_platform_ip6_dadfailed_check( + self->priv.platform, + self->priv.ifindex, + &NMP_OBJECT_CAST_IP6_ADDRESS(obj_state->obj)->address)) { + /* We configured the address and kernel removed it with DADFAILED flag. */ + dadfailed = TRUE; + } + + if (dadfailed) { + if (conflicts) { + if (!*conflicts) { + *conflicts = + g_array_new(FALSE, FALSE, nm_utils_addr_family_to_size(addr_family)); + } + g_array_append_val(*conflicts, + NMP_OBJECT_CAST_IP6_ADDRESS(obj_state->obj)->address); + } + continue; } if (obj_state->os_plobj && NM_FLAGS_HAS(NMP_OBJECT_CAST_IP6_ADDRESS(obj_state->os_plobj)->n_ifa_flags, IFA_F_TENTATIVE)) { /* The address is configured in kernel, but still tentative. Not ready. */ - return FALSE; + ready = FALSE; + continue; } /* This address is ready. Even if it is not (not anymore) configured in kernel (as @@ -3060,7 +3143,7 @@ nm_l3cfg_check_ready(NML3Cfg *self, } } - return TRUE; + return ready; } /*****************************************************************************/ @@ -3144,7 +3227,7 @@ nm_l3cfg_commit_on_idle_is_scheduled(NML3Cfg *self) /*****************************************************************************/ #define _l3_config_datas_at(l3_config_datas, idx) \ - (&g_array_index((l3_config_datas), L3ConfigData, (idx))) + (&nm_g_array_index((l3_config_datas), L3ConfigData, (idx))) static gssize _l3_config_datas_find_next(GArray *l3_config_datas, @@ -3395,8 +3478,10 @@ nm_l3cfg_add_config(NML3Cfg *self, nm_assert(l3_config_data->acd_defend_type_confdata == acd_defend_type); - if (changed) + if (changed) { _l3_changed_configs_set_dirty(self); + nm_l3cfg_commit_on_idle_schedule(self, NM_L3_CFG_COMMIT_TYPE_AUTO); + } return changed; } @@ -3440,6 +3525,9 @@ _l3cfg_remove_config(NML3Cfg *self, } } + if (changed) + nm_l3cfg_commit_on_idle_schedule(self, NM_L3_CFG_COMMIT_TYPE_AUTO); + if (self->priv.p->l3_config_datas->len == 0) { nm_assert(changed); nm_clear_pointer(&self->priv.p->l3_config_datas, g_array_unref); @@ -3713,6 +3801,38 @@ _l3cfg_update_combined_config(NML3Cfg *self, &hook_data); } + if (self->priv.ifindex == NM_LOOPBACK_IFINDEX) { + NMPlatformIPXAddress ax; + NMPlatformIPXRoute rx; + + if (!nm_l3_config_data_lookup_address_4(l3cd, + NM_IPV4LO_ADDR1, + NM_IPV4LO_PREFIXLEN, + NM_IPV4LO_ADDR1)) { + nm_l3_config_data_add_address_4( + l3cd, + nm_platform_ip4_address_init_loopback_addr1(&ax.a4)); + } + if (!nm_l3_config_data_lookup_address_6(l3cd, &in6addr_loopback)) { + nm_l3_config_data_add_address_6(l3cd, + nm_platform_ip6_address_init_loopback(&ax.a6)); + } + + rx.r4 = (NMPlatformIP4Route){ + .ifindex = NM_LOOPBACK_IFINDEX, + .rt_source = NM_IP_CONFIG_SOURCE_KERNEL, + .network = NM_IPV4LO_ADDR1, + .plen = NM_IPV4LO_PREFIXLEN, + .table_coerced = nm_platform_route_table_coerce(RT_TABLE_LOCAL), + .scope_inv = nm_platform_route_scope_inv(RT_SCOPE_HOST), + .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), + .pref_src = NM_IPV4LO_ADDR1, + }; + nm_platform_ip_route_normalize(AF_INET, &rx.rx); + if (!nm_l3_config_data_lookup_route(l3cd, AF_INET, &rx.rx)) { + nm_l3_config_data_add_route_4(l3cd, &rx.r4); + } + } for (i = 0; i < l3_config_datas_len; i++) { const L3ConfigData *l3cd_data = l3_config_datas_arr[i]; int IS_IPv4; @@ -4371,9 +4491,9 @@ _l3_commit_mptcp_af(NML3Cfg *self, default: if (IS_IPv4) { /* We take all addresses, including rfc1918 private addresses - * (nm_utils_ip_is_site_local()). */ + * (nm_ip_addr_is_site_local()). */ } else { - if (nm_utils_ip6_is_ula(&addr->a6.address)) { + if (nm_ip6_addr_is_ula(&addr->a6.address)) { /* Exclude unique local IPv6 addresses fc00::/7. */ goto skip_addr; } else { @@ -4470,7 +4590,7 @@ _l3_commit_one(NML3Cfg *self, gs_unref_ptrarray GPtrArray *addresses_prune = NULL; gs_unref_ptrarray GPtrArray *routes_prune = NULL; gs_unref_ptrarray GPtrArray *routes_temporary_not_available_arr = NULL; - NMIPRouteTableSyncMode route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE; + NMIPRouteTableSyncMode route_table_sync; gboolean final_failure_for_temporary_not_available = FALSE; char sbuf_commit_type[50]; gboolean success = TRUE; @@ -4487,15 +4607,15 @@ _l3_commit_one(NML3Cfg *self, nm_utils_addr_family_to_char(addr_family), _l3_cfg_commit_type_to_string(commit_type, sbuf_commit_type, sizeof(sbuf_commit_type))); - if (self->priv.p->combined_l3cd_commited) { - addresses = _commit_collect_addresses(self, addr_family, commit_type); + addresses = _commit_collect_addresses(self, addr_family, commit_type); - _commit_collect_routes(self, addr_family, commit_type, &routes, &routes_nodev); + _commit_collect_routes(self, addr_family, commit_type, &routes, &routes_nodev); - route_table_sync = - nm_l3_config_data_get_route_table_sync(self->priv.p->combined_l3cd_commited, - addr_family); - } + route_table_sync = + self->priv.p->combined_l3cd_commited + ? nm_l3_config_data_get_route_table_sync(self->priv.p->combined_l3cd_commited, + addr_family) + : NM_IP_ROUTE_TABLE_SYNC_MODE_NONE; if (!IS_IPv4) { _l3_commit_ip6_privacy(self, commit_type); @@ -4552,6 +4672,23 @@ _l3_commit_one(NML3Cfg *self, } } + if (self->priv.ifindex == NM_LOOPBACK_IFINDEX) { + if (!addresses) { + NMPlatformIPXAddress ax; + + addresses = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); + if (IS_IPv4) { + g_ptr_array_add( + addresses, + nmp_object_new(NMP_OBJECT_TYPE_IP4_ADDRESS, + nm_platform_ip4_address_init_loopback_addr1(&ax.a4))); + } else { + g_ptr_array_add(addresses, + nmp_object_new(NMP_OBJECT_TYPE_IP6_ADDRESS, + nm_platform_ip6_address_init_loopback(&ax.a6))); + } + } + } /* FIXME(l3cfg): need to honor and set nm_l3_config_data_get_ndisc_*(). */ /* FIXME(l3cfg): need to honor and set nm_l3_config_data_get_mtu(). */ @@ -4559,7 +4696,10 @@ _l3_commit_one(NML3Cfg *self, addr_family, self->priv.ifindex, addresses, - addresses_prune); + addresses_prune, + self->priv.ifindex == NM_LOOPBACK_IFINDEX + ? NMP_IP_ADDRESS_SYNC_FLAGS_NONE + : NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE); _nodev_routes_sync(self, addr_family, commit_type, routes_nodev); @@ -4810,6 +4950,9 @@ nm_l3cfg_commit_type_register(NML3Cfg *self, c_list_link_tail(&self->priv.p->commit_type_lst_head, &handle->commit_type_lst); ret = handle; + + nm_l3cfg_commit_on_idle_schedule(self, NM_L3_CFG_COMMIT_TYPE_AUTO); + out: _LOGT("commit type register (type \"%s\", source \"%s\", existing " NM_HASH_OBFUSCATE_PTR_FMT ") -> " NM_HASH_OBFUSCATE_PTR_FMT "", @@ -4832,6 +4975,8 @@ nm_l3cfg_commit_type_unregister(NML3Cfg *self, NML3CfgCommitTypeHandle *handle) _LOGT("commit type unregister " NM_HASH_OBFUSCATE_PTR_FMT "", NM_HASH_OBFUSCATE_PTR(handle)); + nm_l3cfg_commit_on_idle_schedule(self, NM_L3_CFG_COMMIT_TYPE_AUTO); + c_list_unlink_stale(&handle->commit_type_lst); if (c_list_is_empty(&self->priv.p->commit_type_lst_head)) g_object_unref(self); @@ -5024,6 +5169,9 @@ nm_l3cfg_init(NML3Cfg *self) c_list_init(&self->priv.p->blocked_lst_head_4); c_list_init(&self->priv.p->blocked_lst_head_6); + c_list_init(&self->internal_netns.signal_pending_lst); + c_list_init(&self->internal_netns.ecmp_track_ifindex_lst_head); + self->priv.p->obj_state_hash = g_hash_table_new_full(nmp_object_indirect_id_hash, nmp_object_indirect_id_equal, _obj_state_data_free, @@ -5066,6 +5214,9 @@ finalize(GObject *object) NML3Cfg *self = NM_L3CFG(object); gboolean changed; + nm_assert(c_list_is_empty(&self->internal_netns.signal_pending_lst)); + nm_assert(c_list_is_empty(&self->internal_netns.ecmp_track_ifindex_lst_head)); + nm_assert(!self->priv.p->ipconfig_4); nm_assert(!self->priv.p->ipconfig_6); diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h index e0257623..9d622b4a 100644 --- a/src/core/nm-l3cfg.h +++ b/src/core/nm-l3cfg.h @@ -13,7 +13,7 @@ #define NM_ACD_TIMEOUT_MAX_MSEC 30000u #define NM_TYPE_L3CFG (nm_l3cfg_get_type()) -#define NM_L3CFG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_L3CFG, NML3Cfg)) +#define NM_L3CFG(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_L3CFG, NML3Cfg)) #define NM_L3CFG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_L3CFG, NML3CfgClass)) #define NM_IS_L3CFG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_L3CFG)) #define NM_IS_L3CFG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_L3CFG)) @@ -209,6 +209,16 @@ struct _NML3Cfg { const NMPObject *plobj_next; int ifindex; } priv; + + /* NML3Cfg strongly cooperates with NMNetns. The latter is + * the one that creates and manages (also) the lifetime of the + * NML3Cfg instance. We track some per-l3cfg-data that is only + * relevant to NMNetns here. */ + struct { + guint32 signal_pending_obj_type_flags; + CList signal_pending_lst; + CList ecmp_track_ifindex_lst_head; + } internal_netns; }; typedef struct _NML3CfgClass NML3CfgClass; @@ -400,7 +410,7 @@ gboolean nm_l3cfg_check_ready(NML3Cfg *self, const NML3ConfigData *l3cd, int addr_family, NML3CfgCheckReadyFlags flags, - gboolean *acd_used); + GArray **conflicts); gboolean nm_l3cfg_has_temp_not_available_obj(NML3Cfg *self, int addr_family); diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c index 822df7ad..66547294 100644 --- a/src/core/nm-manager.c +++ b/src/core/nm-manager.c @@ -19,6 +19,7 @@ #include "NetworkManagerUtils.h" #include "devices/nm-device-factory.h" #include "devices/nm-device-generic.h" +#include "devices/nm-device-loopback.h" #include "devices/nm-device.h" #include "dns/nm-dns-manager.h" #include "dhcp/nm-dhcp-manager.h" @@ -114,6 +115,7 @@ static guint signals[LAST_SIGNAL] = {0}; NM_GOBJECT_PROPERTIES_DEFINE(NMManager, PROP_VERSION, + PROP_VERSION_INFO, PROP_CAPABILITIES, PROP_STATE, PROP_STARTUP, @@ -419,6 +421,31 @@ static NM_CACHED_QUARK_FCN("autoconnect-root", autoconnect_root_quark); /*****************************************************************************/ +static GVariant * +_version_info_get(void) +{ + const guint32 arr[] = { + NM_VERSION, + }; + + /* The array contains as first element NM_VERSION, which can be + * used to numerically compare the version (see also NM_ENCODE_VERSION, + * nm_utils_version(), nm_encode_version() and nm_decode_version(). + * + * The following elements of the array are a bitfield of capabilities. + * These capabilities should only depend on compile-time abilities + * (unlike NM_MANAGER_CAPABILITIES, NMCapability). The supported values + * are from NMVersionInfoCapability enum. This way to expose capabilities + * is more cumbersome but more efficient compared to NM_MANAGER_CAPABILITIES. + * As such, it is cheap to add capabilities for something, where you would + * avoid it as NM_MANAGER_CAPABILITIES due to the overhead. + */ + + return nm_g_variant_new_au(arr, G_N_ELEMENTS(arr)); +} + +/*****************************************************************************/ + static gboolean _connection_is_vpn(NMConnection *connection) { @@ -632,11 +659,8 @@ static guint _device_route_metric_data_by_ifindex_hash(gconstpointer p) { const DeviceRouteMetricData *data = p; - NMHashState h; - nm_hash_init(&h, 1030338191); - nm_hash_update_vals(&h, data->ifindex); - return nm_hash_complete(&h); + return nm_hash_val(1030338191, data->ifindex); } static gboolean @@ -1464,8 +1488,11 @@ find_best_device_state(NMManager *manager) NMActiveConnection *ac; c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) { - NMActiveConnectionState ac_state = nm_active_connection_get_state(ac); + NMActiveConnectionState ac_state; + if (NM_IS_DEVICE_LOOPBACK(nm_active_connection_get_device(ac))) + continue; + ac_state = nm_active_connection_get_state(ac); switch (ac_state) { case NM_ACTIVE_CONNECTION_STATE_ACTIVATED: if (nm_active_connection_get_default(ac, AF_UNSPEC)) { @@ -1890,8 +1917,11 @@ find_parent_device_for_connection(NMManager *self, * with some known device. */ c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { - /* Unmanaged devices are not compatible with any connection */ - if (!nm_device_get_managed(candidate, FALSE)) + /* For a realized device, check that it's managed; otherwise it's not + * compatible with any connection. If the device is unrealized then + * the managed state is meaningless. + */ + if (nm_device_is_real(candidate) && !nm_device_get_managed(candidate, FALSE)) continue; if (nm_device_get_settings_connection(candidate) == parent_connection) @@ -1949,7 +1979,7 @@ nm_manager_get_connection_iface(NMManager *self, g_set_error(error, NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, + NM_MANAGER_ERROR_MISSING_PLUGIN, "NetworkManager plugin for '%s' unavailable", nm_connection_get_connection_type(connection)); return NULL; @@ -3083,12 +3113,11 @@ recheck_assume_connection(NMManager *self, NMDevice *device) g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); if (!nm_device_get_managed(device, FALSE)) { - /* If the device is unmanaged by NM_UNMANAGED_PLATFORM_INIT or NM_UNMANAGED_PARENT, + /* If the device is unmanaged by NM_UNMANAGED_PLATFORM_INIT, * don't reset the state now but wait until it becomes managed. */ - if (nm_device_get_unmanaged_flags(device, NM_UNMANAGED_ALL) - & ~(NM_UNMANAGED_PLATFORM_INIT | NM_UNMANAGED_PARENT)) + if (nm_device_get_unmanaged_flags(device, NM_UNMANAGED_ALL) & ~NM_UNMANAGED_PLATFORM_INIT) nm_device_assume_state_reset(device); - _LOG2D(LOGD_DEVICE, device, "assume: don't assume because %s", "not managed"); + _LOG2D(LOGD_DEVICE, device, "assume: don't assume because device is not managed"); return FALSE; } @@ -3341,6 +3370,9 @@ _get_best_connectivity(NMManager *self, int addr_family) NMConnectivityState state; gint64 metric; + if (NM_IS_DEVICE_LOOPBACK(dev)) + continue; + r = nm_device_get_best_default_route(dev, addr_family); if (r) metric = NMP_OBJECT_CAST_IP_ROUTE(r)->metric; @@ -3410,10 +3442,9 @@ _device_realize_finish(NMManager *self, NMDevice *device, const NMPlatformLink * nm_device_realize_finish(device, plink); if (!nm_device_get_managed(device, FALSE)) { - /* If the device is unmanaged by NM_UNMANAGED_PLATFORM_INIT or NM_UNMANAGED_PARENT, + /* If the device is unmanaged by NM_UNMANAGED_PLATFORM_INIT, * don't reset the state now but wait until it becomes managed. */ - if (nm_device_get_unmanaged_flags(device, NM_UNMANAGED_ALL) - & ~(NM_UNMANAGED_PLATFORM_INIT | NM_UNMANAGED_PARENT)) + if (nm_device_get_unmanaged_flags(device, NM_UNMANAGED_ALL) & ~NM_UNMANAGED_PLATFORM_INIT) nm_device_assume_state_reset(device); return; } @@ -4608,11 +4639,9 @@ ensure_master_active_connection(NMManager *self, NULL)) continue; - if (!nm_device_is_software(candidate)) { - master_state = nm_device_get_state(candidate); - if (nm_device_is_real(candidate) && master_state != NM_DEVICE_STATE_DISCONNECTED) - continue; - } + if (nm_device_is_real(candidate) + && nm_device_get_state(candidate) != NM_DEVICE_STATE_DISCONNECTED) + continue; master_ac = nm_manager_activate_connection( self, @@ -4715,8 +4744,10 @@ find_slaves(NMManager *manager, } nm_assert(n_slaves < n_all_connections); - slaves[n_slaves].connection = candidate, slaves[n_slaves].device = slave_device, - n_slaves++; + slaves[n_slaves++] = (SlaveConnectionInfo){ + .connection = candidate, + .device = slave_device, + }; if (slave_device) g_hash_table_add(devices, slave_device); @@ -6612,7 +6643,7 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed) } /* Give the connections a chance to recreate the virtual devices. - * We've torn them down on sleep. */ + * We've torn them down on sleep. */ connections_changed(self); } @@ -7089,10 +7120,6 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde ifindex = nm_device_get_ip_ifindex(device); if (ifindex <= 0) return FALSE; - if (ifindex == 1) { - /* ignore loopback */ - return FALSE; - } if (!nm_platform_link_get(priv->platform, ifindex)) return FALSE; @@ -7904,12 +7931,12 @@ nm_manager_set_capability(NMManager *self, NMCapability cap) priv = NM_MANAGER_GET_PRIVATE(self); - idx = nm_utils_array_find_binary_search(&g_array_index(priv->capabilities, guint32, 0), - sizeof(guint32), - priv->capabilities->len, - &cap_i, - nm_cmp_uint32_p_with_data, - NULL); + idx = nm_array_find_bsearch(nm_g_array_first_p(priv->capabilities, guint32), + priv->capabilities->len, + sizeof(guint32), + &cap_i, + nm_cmp_uint32_p_with_data, + NULL); if (idx >= 0) return; @@ -8132,9 +8159,12 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) case PROP_VERSION: g_value_set_string(value, VERSION); break; + case PROP_VERSION_INFO: + g_value_set_variant(value, _version_info_get()); + break; case PROP_CAPABILITIES: g_value_set_variant(value, - nm_g_variant_new_au((const guint32 *) priv->capabilities->data, + nm_g_variant_new_au(nm_g_array_first_p(priv->capabilities, guint32), priv->capabilities->len)); break; case PROP_STATE: @@ -8645,6 +8675,9 @@ static const NMDBusInterfaceInfoExtended interface_info_manager = { NM_MANAGER_ACTIVATING_CONNECTION), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Startup", "b", NM_MANAGER_STARTUP), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Version", "s", NM_MANAGER_VERSION), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("VersionInfo", + "au", + NM_MANAGER_VERSION_INFO), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Capabilities", "au", NM_MANAGER_CAPABILITIES), @@ -8703,6 +8736,14 @@ nm_manager_class_init(NMManagerClass *manager_class) NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_VERSION_INFO] = + g_param_spec_variant(NM_MANAGER_VERSION_INFO, + "", + "", + G_VARIANT_TYPE("au"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_CAPABILITIES] = g_param_spec_variant(NM_MANAGER_CAPABILITIES, "", diff --git a/src/core/nm-manager.h b/src/core/nm-manager.h index fcb00227..0cbbcbf0 100644 --- a/src/core/nm-manager.h +++ b/src/core/nm-manager.h @@ -12,7 +12,7 @@ #include "nm-dbus-manager.h" #define NM_TYPE_MANAGER (nm_manager_get_type()) -#define NM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MANAGER, NMManager)) +#define NM_MANAGER(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MANAGER, NMManager)) #define NM_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_MANAGER, NMManagerClass)) #define NM_IS_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_MANAGER)) #define NM_IS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_MANAGER)) @@ -20,6 +20,7 @@ (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_MANAGER, NMManagerClass)) #define NM_MANAGER_VERSION "version" +#define NM_MANAGER_VERSION_INFO "version-info" #define NM_MANAGER_CAPABILITIES "capabilities" #define NM_MANAGER_STATE "state" #define NM_MANAGER_STARTUP "startup" diff --git a/src/core/nm-netns.c b/src/core/nm-netns.c index 859dbb21..12ca8508 100644 --- a/src/core/nm-netns.c +++ b/src/core/nm-netns.c @@ -7,6 +7,8 @@ #include "nm-netns.h" +#include + #include "libnm-glib-aux/nm-dedup-multi.h" #include "libnm-glib-aux/nm-c-list.h" @@ -16,6 +18,7 @@ #include "libnm-platform/nm-platform.h" #include "libnm-platform/nmp-netns.h" #include "libnm-platform/nmp-global-tracker.h" +#include "libnm-std-aux/c-list-util.h" /*****************************************************************************/ @@ -28,6 +31,8 @@ typedef struct { NMPGlobalTracker *global_tracker; GHashTable *l3cfgs; GHashTable *shared_ips; + GHashTable *ecmp_track_by_obj; + GHashTable *ecmp_track_by_ecmpid; CList l3cfg_signal_pending_lst_head; GSource *signal_pending_idle_source; } NMNetnsPrivate; @@ -67,6 +72,165 @@ NM_DEFINE_SINGLETON_GETTER(NMNetns, nm_netns_get, NM_TYPE_NETNS); /*****************************************************************************/ +#define nm_assert_l3cfg(self, l3cfg) \ + G_STMT_START \ + { \ + NMNetns *_self = (self); \ + NML3Cfg *_l3cfg = (l3cfg); \ + \ + nm_assert(NM_IS_NETNS(self)); \ + nm_assert(NM_IS_L3CFG(_l3cfg)); \ + if (NM_MORE_ASSERTS > 5) \ + nm_assert(_l3cfg == nm_netns_l3cfg_get(_self, nm_l3cfg_get_ifindex(_l3cfg))); \ + } \ + G_STMT_END + +/*****************************************************************************/ + +typedef struct { + const NMPObject *representative_obj; + const NMPObject *merged_obj; + CList ecmpid_lst_head; + bool needs_update : 1; + bool already_visited : 1; +} EcmpTrackEcmpid; + +typedef struct { + const NMPObject *obj; + + NML3Cfg *l3cfg; + EcmpTrackEcmpid *parent_track_ecmpid; + + CList ifindex_lst; + CList ecmpid_lst; + + /* Calling nm_netns_ip_route_ecmp_register() will ensure that the tracked + * entry is non-dirty. This can be used to remove stale entries. */ + bool dirty : 1; + + /* This flag is set during nm_netns_ip_route_ecmp_register(), when first tracking the + * route. It is cleared on the next nm_netns_ip_route_ecmp_commit(). It thus only + * exists for a short time, to know during a commit that the route is new and + * we need to do something special. */ + bool is_new : 1; + + /* The entry is ready to be configured. This exists, because the nexthop of + * a route must be reachable directly (being onlink). That is, we may need + * to add a direct, single-hop route to the gateway, which is done by + * the NML3Cfg of that interface. Since the NML3Cfg calls nm_netns_ip_route_ecmp_commit() + * and only adds the direct route afterwards, the ECMP route may not be ready + * right away, but only upon seeing the entry a second time. */ + bool is_ready : 1; +} EcmpTrackObj; + +static int +_ecmp_track_sort_lst_cmp(const CList *a, const CList *b, const void *user_data) +{ + EcmpTrackObj *track_obj_a = c_list_entry(a, EcmpTrackObj, ecmpid_lst); + EcmpTrackObj *track_obj_b = c_list_entry(b, EcmpTrackObj, ecmpid_lst); + const NMPlatformIP4Route *route_a = NMP_OBJECT_CAST_IP4_ROUTE(track_obj_a->obj); + const NMPlatformIP4Route *route_b = NMP_OBJECT_CAST_IP4_ROUTE(track_obj_b->obj); + + nm_assert(route_a->ifindex > 0); + nm_assert(route_a->n_nexthops <= 1); + nm_assert(route_b->ifindex > 0); + nm_assert(route_b->n_nexthops <= 1); + + NM_CMP_FIELD(route_a, route_b, ifindex); + NM_CMP_FIELD(route_b, route_a, weight); + NM_CMP_DIRECT(htonl(route_a->gateway), htonl(route_b->gateway)); + + return nm_assert_unreachable_val( + nm_platform_ip4_route_cmp(route_a, route_b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)); +} + +static gboolean +_ecmp_track_init_merged_obj(EcmpTrackEcmpid *track_ecmpid, const NMPObject **out_obj_del) +{ + EcmpTrackObj *track_obj; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + gsize n_nexthops; + gsize i; + + nm_assert(track_ecmpid); + nm_assert(!c_list_is_empty(&track_ecmpid->ecmpid_lst_head)); + nm_assert(track_ecmpid->representative_obj + == c_list_first_entry(&track_ecmpid->ecmpid_lst_head, EcmpTrackObj, ecmpid_lst)->obj); + nm_assert(out_obj_del && !*out_obj_del); + + if (!track_ecmpid->needs_update) { + /* Already up to date. Nothing to do. */ + return FALSE; + } + + track_ecmpid->needs_update = FALSE; + + n_nexthops = c_list_length(&track_ecmpid->ecmpid_lst_head); + + if (n_nexthops == 1) { + /* There is only a single entry. There is nothing to merge, just set + * the first entry. */ + obj_new = nmp_object_ref(track_ecmpid->representative_obj); + goto out; + } + + /* We want that the nexthop list is deterministic. We thus sort the list and update + * the representative_obj. */ + c_list_sort(&track_ecmpid->ecmpid_lst_head, _ecmp_track_sort_lst_cmp, NULL); + nmp_object_ref_set( + &track_ecmpid->representative_obj, + c_list_first_entry(&track_ecmpid->ecmpid_lst_head, EcmpTrackObj, ecmpid_lst)->obj); + + obj_new = nmp_object_clone(track_ecmpid->representative_obj, FALSE); + + nm_assert(obj_new->ip4_route.n_nexthops <= 1); + nm_assert(!obj_new->_ip4_route.extra_nexthops); + + /* Note that there actually cannot be duplicate (ifindex,gateway,weight) tuples, because + * NML3Cfg uses NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID to track the routes, and track_ecmpid + * groups them further by NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID. The comparison for + * ECMP_ID is a strict superset of ID, hence there are no dupliated. + * + * Also, kernel wouldn't care if there were duplicate nexthops anyway. + * + * This means, it's gonna be simple. We sorted the single-hop routes by next-hop, + * now just create a plain list of the nexthops (no check for duplciates, etc). */ + + ((NMPObject *) obj_new)->ip4_route.n_nexthops = n_nexthops; + ((NMPObject *) obj_new)->_ip4_route.extra_nexthops = + g_new(NMPlatformIP4RtNextHop, n_nexthops - 1u); + + i = 0; + c_list_for_each_entry (track_obj, &track_ecmpid->ecmpid_lst_head, ecmpid_lst) { + if (i > 0) { + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE(track_obj->obj); + NMPlatformIP4RtNextHop *nh = (gpointer) &obj_new->_ip4_route.extra_nexthops[i - 1]; + + *nh = (NMPlatformIP4RtNextHop){ + .ifindex = r->ifindex, + .gateway = r->gateway, + .weight = r->weight, + }; + } + i++; + } + +out: + nm_assert(obj_new); + if (nmp_object_equal(track_ecmpid->merged_obj, obj_new)) + /* the objects are equal but the update was needed, for example if the + * routes were removed from kernel but not from our tracking + * dictionaries and therefore we tried to register them again. */ + return TRUE; + + if (track_ecmpid->merged_obj) + *out_obj_del = g_steal_pointer(&track_ecmpid->merged_obj); + track_ecmpid->merged_obj = g_steal_pointer(&obj_new); + return TRUE; +} + +/*****************************************************************************/ + NMPNetns * nm_netns_get_platform_netns(NMNetns *self) { @@ -93,21 +257,67 @@ nm_netns_get_multi_idx(NMNetns *self) /*****************************************************************************/ -typedef struct { - int ifindex; - guint32 signal_pending_obj_type_flags; - NML3Cfg *l3cfg; - CList signal_pending_lst; -} L3CfgData; +static guint +_ecmp_routes_by_ecmpid_hash(gconstpointer ptr) +{ + const NMPObject *const *p_obj = ptr; + + return nm_platform_ip4_route_hash(NMP_OBJECT_CAST_IP4_ROUTE(*p_obj), + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID); +} + +static int +_ecmp_routes_by_ecmpid_equal(gconstpointer ptr_a, gconstpointer ptr_b) +{ + const NMPObject *const *p_obj_a = ptr_a; + const NMPObject *const *p_obj_b = ptr_b; + + return nm_platform_ip4_route_cmp(NMP_OBJECT_CAST_IP4_ROUTE(*p_obj_a), + NMP_OBJECT_CAST_IP4_ROUTE(*p_obj_b), + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID) + == 0; +} static void -_l3cfg_data_free(gpointer ptr) +_ecmp_routes_by_ecmpid_free(gpointer ptr) { - L3CfgData *l3cfg_data = ptr; + EcmpTrackEcmpid *track_ecmpid = ptr; + + c_list_unlink_stale(&track_ecmpid->ecmpid_lst_head); + nmp_object_unref(track_ecmpid->representative_obj); + nmp_object_unref(track_ecmpid->merged_obj); + nm_g_slice_free(track_ecmpid); +} - c_list_unlink_stale(&l3cfg_data->signal_pending_lst); +static void +_ecmp_routes_by_obj_free(gpointer ptr) +{ + EcmpTrackObj *track_obj = ptr; - nm_g_slice_free(l3cfg_data); + c_list_unlink_stale(&track_obj->ifindex_lst); + c_list_unlink_stale(&track_obj->ecmpid_lst); + nmp_object_unref(track_obj->obj); + nm_g_slice_free(track_obj); +} + +/*****************************************************************************/ + +static NML3Cfg * +_l3cfg_hashed_to_l3cfg(gpointer ptr) +{ + gpointer l3cfg; + + l3cfg = &(((char *) ptr)[-G_STRUCT_OFFSET(NML3Cfg, priv.ifindex)]); + nm_assert(NM_IS_L3CFG(l3cfg)); + return l3cfg; +} + +static void +_l3cfg_hashed_free(gpointer ptr) +{ + NML3Cfg *l3cfg = _l3cfg_hashed_to_l3cfg(ptr); + + c_list_unlink(&l3cfg->internal_netns.signal_pending_lst); } static void @@ -128,58 +338,48 @@ _l3cfg_weak_notify(gpointer data, GObject *where_the_object_was) NML3Cfg * nm_netns_l3cfg_get(NMNetns *self, int ifindex) { - NMNetnsPrivate *priv; - L3CfgData *l3cfg_data; - - g_return_val_if_fail(NM_IS_NETNS(self), NULL); - g_return_val_if_fail(ifindex > 0, NULL); - - priv = NM_NETNS_GET_PRIVATE(self); + NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE(self); + gpointer ptr; - l3cfg_data = g_hash_table_lookup(priv->l3cfgs, &ifindex); + nm_assert(ifindex > 0); - return l3cfg_data ? l3cfg_data->l3cfg : NULL; + ptr = g_hash_table_lookup(priv->l3cfgs, &ifindex); + return ptr ? _l3cfg_hashed_to_l3cfg(ptr) : NULL; } NML3Cfg * nm_netns_l3cfg_acquire(NMNetns *self, int ifindex) { NMNetnsPrivate *priv; - L3CfgData *l3cfg_data; + NML3Cfg *l3cfg; g_return_val_if_fail(NM_IS_NETNS(self), NULL); g_return_val_if_fail(ifindex > 0, NULL); priv = NM_NETNS_GET_PRIVATE(self); - l3cfg_data = g_hash_table_lookup(priv->l3cfgs, &ifindex); - - if (l3cfg_data) { + l3cfg = nm_netns_l3cfg_get(self, ifindex); + if (l3cfg) { nm_log_trace(LOGD_CORE, "l3cfg[" NM_HASH_OBFUSCATE_PTR_FMT ",ifindex=%d] %s", - NM_HASH_OBFUSCATE_PTR(l3cfg_data->l3cfg), + NM_HASH_OBFUSCATE_PTR(l3cfg), ifindex, "referenced"); - return g_object_ref(l3cfg_data->l3cfg); + return g_object_ref(l3cfg); } - l3cfg_data = g_slice_new(L3CfgData); - *l3cfg_data = (L3CfgData){ - .ifindex = ifindex, - .l3cfg = nm_l3cfg_new(self, ifindex), - .signal_pending_lst = C_LIST_INIT(l3cfg_data->signal_pending_lst), - }; + l3cfg = nm_l3cfg_new(self, ifindex); - if (!g_hash_table_add(priv->l3cfgs, l3cfg_data)) + if (!g_hash_table_add(priv->l3cfgs, &l3cfg->priv.ifindex)) nm_assert_not_reached(); if (NM_UNLIKELY(g_hash_table_size(priv->l3cfgs) == 1)) g_object_ref(self); - g_object_weak_ref(G_OBJECT(l3cfg_data->l3cfg), _l3cfg_weak_notify, self); + g_object_weak_ref(G_OBJECT(l3cfg), _l3cfg_weak_notify, self); /* Transfer ownership! We keep only a weak ref. */ - return l3cfg_data->l3cfg; + return l3cfg; } /*****************************************************************************/ @@ -189,7 +389,7 @@ _platform_signal_on_idle_cb(gpointer user_data) { gs_unref_object NMNetns *self = g_object_ref(NM_NETNS(user_data)); NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE(self); - L3CfgData *l3cfg_data; + NML3Cfg *l3cfg; CList work_list; nm_clear_g_source_inst(&priv->signal_pending_idle_source); @@ -205,12 +405,12 @@ _platform_signal_on_idle_cb(gpointer user_data) c_list_init(&work_list); c_list_splice(&work_list, &priv->l3cfg_signal_pending_lst_head); - while ((l3cfg_data = c_list_first_entry(&work_list, L3CfgData, signal_pending_lst))) { - nm_assert(NM_IS_L3CFG(l3cfg_data->l3cfg)); - c_list_unlink(&l3cfg_data->signal_pending_lst); + while ((l3cfg = c_list_first_entry(&work_list, NML3Cfg, internal_netns.signal_pending_lst))) { + nm_assert(NM_IS_L3CFG(l3cfg)); + c_list_unlink(&l3cfg->internal_netns.signal_pending_lst); _nm_l3cfg_notify_platform_change_on_idle( - l3cfg_data->l3cfg, - nm_steal_int(&l3cfg_data->signal_pending_obj_type_flags)); + l3cfg, + nm_steal_int(&l3cfg->internal_netns.signal_pending_obj_type_flags)); } return G_SOURCE_CONTINUE; @@ -228,24 +428,28 @@ _platform_signal_cb(NMPlatform *platform, NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE(self); const NMPObjectType obj_type = obj_type_i; const NMPlatformSignalChangeType change_type = change_type_i; - L3CfgData *l3cfg_data; + NML3Cfg *l3cfg; - l3cfg_data = g_hash_table_lookup(priv->l3cfgs, &ifindex); - if (!l3cfg_data) + if (ifindex <= 0) { + /* platform signal callback could be triggered by nodev routes, skip them */ + return; + } + + l3cfg = nm_netns_l3cfg_get(self, ifindex); + if (!l3cfg) return; - l3cfg_data->signal_pending_obj_type_flags |= nmp_object_type_to_flags(obj_type); + l3cfg->internal_netns.signal_pending_obj_type_flags |= nmp_object_type_to_flags(obj_type); - if (c_list_is_empty(&l3cfg_data->signal_pending_lst)) { - c_list_link_tail(&priv->l3cfg_signal_pending_lst_head, &l3cfg_data->signal_pending_lst); + if (c_list_is_empty(&l3cfg->internal_netns.signal_pending_lst)) { + c_list_link_tail(&priv->l3cfg_signal_pending_lst_head, + &l3cfg->internal_netns.signal_pending_lst); if (!priv->signal_pending_idle_source) priv->signal_pending_idle_source = nm_g_idle_add_source(_platform_signal_on_idle_cb, self); } - _nm_l3cfg_notify_platform_change(l3cfg_data->l3cfg, - change_type, - NMP_OBJECT_UP_CAST(platform_object)); + _nm_l3cfg_notify_platform_change(l3cfg, change_type, NMP_OBJECT_UP_CAST(platform_object)); } /*****************************************************************************/ @@ -257,7 +461,7 @@ nm_netns_shared_ip_reserve(NMNetns *self) NMNetnsSharedIPHandle *handle; const in_addr_t addr_start = ntohl(0x0a2a0001u); /* 10.42.0.1 */ in_addr_t addr; - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; /* Find an unused address in the 10.42.x.x range */ @@ -287,10 +491,10 @@ nm_netns_shared_ip_reserve(NMNetns *self) if (count > 0xFFu) { if (handle->_ref_count == 1) { _LOGE("shared-ip4: ran out of shared IP addresses. Reuse %s/24", - _nm_utils_inet4_ntop(handle->addr, sbuf_addr)); + nm_inet4_ntop(handle->addr, sbuf_addr)); } else { _LOGD("shared-ip4: reserved IP address range %s/24 (duplicate)", - _nm_utils_inet4_ntop(handle->addr, sbuf_addr)); + nm_inet4_ntop(handle->addr, sbuf_addr)); } handle->_ref_count++; return handle; @@ -307,8 +511,7 @@ nm_netns_shared_ip_reserve(NMNetns *self) g_hash_table_add(priv->shared_ips, handle); - _LOGD("shared-ip4: reserved IP address range %s/24", - _nm_utils_inet4_ntop(handle->addr, sbuf_addr)); + _LOGD("shared-ip4: reserved IP address range %s/24", nm_inet4_ntop(handle->addr, sbuf_addr)); return handle; } @@ -317,7 +520,7 @@ nm_netns_shared_ip_release(NMNetnsSharedIPHandle *handle) { NMNetns *self; NMNetnsPrivate *priv; - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; g_return_if_fail(handle); @@ -334,7 +537,7 @@ nm_netns_shared_ip_release(NMNetnsSharedIPHandle *handle) nm_assert(handle->addr == ntohl(0x0A2AFF01u)); /* 10.42.255.1 */ handle->_ref_count--; _LOGD("shared-ip4: release IP address range %s/24 (%d more references held)", - _nm_utils_inet4_ntop(handle->addr, sbuf_addr), + nm_inet4_ntop(handle->addr, sbuf_addr), handle->_ref_count); return; } @@ -347,8 +550,7 @@ nm_netns_shared_ip_release(NMNetnsSharedIPHandle *handle) g_object_unref(self); } - _LOGD("shared-ip4: release IP address range %s/24", - _nm_utils_inet4_ntop(handle->addr, sbuf_addr)); + _LOGD("shared-ip4: release IP address range %s/24", nm_inet4_ntop(handle->addr, sbuf_addr)); handle->_self = NULL; nm_g_slice_free(handle); @@ -356,6 +558,270 @@ nm_netns_shared_ip_release(NMNetnsSharedIPHandle *handle) /*****************************************************************************/ +void +nm_netns_ip_route_ecmp_register(NMNetns *self, NML3Cfg *l3cfg, const NMPObject *obj) +{ + NMNetnsPrivate *priv; + EcmpTrackObj *track_obj; + const NMPlatformIP4Route *route; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + + nm_assert_l3cfg(self, l3cfg); + + route = NMP_OBJECT_CAST_IP4_ROUTE(obj); + + nm_assert(route->ifindex > 0); + nm_assert(route->ifindex == nm_l3cfg_get_ifindex(l3cfg)); + nm_assert(route->n_nexthops <= 1); + + priv = NM_NETNS_GET_PRIVATE(self); + + track_obj = g_hash_table_lookup(priv->ecmp_track_by_obj, &obj); + + if (NM_MORE_ASSERTS > 10) { + EcmpTrackObj *track_obj2; + gboolean found = FALSE; + + c_list_for_each_entry (track_obj2, + &l3cfg->internal_netns.ecmp_track_ifindex_lst_head, + ifindex_lst) { + if (track_obj2->obj == obj) { + found = TRUE; + break; + } + } + + nm_assert((!!track_obj) == found); + } + + if (!track_obj) { + EcmpTrackEcmpid *track_ecmpid; + + track_ecmpid = g_hash_table_lookup(priv->ecmp_track_by_ecmpid, &obj); + if (!track_ecmpid) { + track_ecmpid = g_slice_new(EcmpTrackEcmpid); + *track_ecmpid = (EcmpTrackEcmpid){ + .representative_obj = nmp_object_ref(obj), + .merged_obj = NULL, + .ecmpid_lst_head = C_LIST_INIT(track_ecmpid->ecmpid_lst_head), + .needs_update = TRUE, + }; + g_hash_table_add(priv->ecmp_track_by_ecmpid, track_ecmpid); + } else + track_ecmpid->needs_update = TRUE; + + track_obj = g_slice_new(EcmpTrackObj); + *track_obj = (EcmpTrackObj){ + .obj = nmp_object_ref(obj), + .l3cfg = l3cfg, + .parent_track_ecmpid = track_ecmpid, + .dirty = FALSE, + .is_new = TRUE, + .is_ready = FALSE, + }; + + g_hash_table_add(priv->ecmp_track_by_obj, track_obj); + c_list_link_tail(&l3cfg->internal_netns.ecmp_track_ifindex_lst_head, + &track_obj->ifindex_lst); + c_list_link_tail(&track_ecmpid->ecmpid_lst_head, &track_obj->ecmpid_lst); + + _LOGT( + "ecmp-route: track %s", + nmp_object_to_string(track_obj->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); + } else { + track_obj->dirty = FALSE; + track_obj->parent_track_ecmpid->needs_update = TRUE; + } +} + +void +nm_netns_ip_route_ecmp_commit(NMNetns *self, + NML3Cfg *l3cfg, + GPtrArray **out_singlehop_routes, + gboolean is_reapply) +{ + NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE(self); + EcmpTrackObj *track_obj; + EcmpTrackObj *track_obj_safe; + EcmpTrackEcmpid *track_ecmpid; + const NMPObject *route_obj; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + gboolean already_notified = FALSE; + + nm_assert_l3cfg(self, l3cfg); + + _LOGT("ecmp-route: committing IPv4 ECMP routes"); + + /* First, delete all dirty entries, and mark the survivors as dirty, so that on the + * next update they must be touched again. */ + c_list_for_each_entry_safe (track_obj, + track_obj_safe, + &l3cfg->internal_netns.ecmp_track_ifindex_lst_head, + ifindex_lst) { + track_ecmpid = track_obj->parent_track_ecmpid; + track_ecmpid->already_visited = FALSE; + + nm_assert(g_hash_table_lookup(priv->ecmp_track_by_ecmpid, track_ecmpid) == track_ecmpid); + nm_assert(g_hash_table_lookup(priv->ecmp_track_by_obj, track_obj) == track_obj); + nm_assert(c_list_contains(&track_ecmpid->ecmpid_lst_head, &track_obj->ecmpid_lst)); + nm_assert(track_obj->l3cfg == l3cfg); + + if (!track_obj->dirty) { + /* This one is still in used. Keep it, but mark dirty, so that on the + * next update cycle, it needs to be touched again or will be deleted. */ + track_obj->dirty = TRUE; + if (is_reapply) { + track_obj->is_new = TRUE; + track_obj->is_ready = FALSE; + } + if (track_obj->is_new) { + const NMPlatformIP4Route *route = + NMP_OBJECT_CAST_IP4_ROUTE(track_ecmpid->merged_obj); + + /* This is a new route entry that was just added. Upon first + * addition, the route is not yet ready for configuration, + * because we need to make sure that the gateway is reachable + * via an onlink route. The calling l3cfg will configure that + * route, but only after returning from this function. So we + * need to go through one more commit. + * + * We also need to make sure that we are called back right + * after l3cfg configured that route. We achieve that by + * scheduling another idle commit on "l3cfg". */ + track_obj->is_new = FALSE; + if (route + && (route->gateway == 0 + || NM_FLAGS_HAS(route->r_rtm_flags, (unsigned) RTNH_F_ONLINK))) { + /* This route is onlink. We don't need to configure an onlink route + * to the gateway, and the route is immediately ready for configuration. */ + track_obj->is_ready = TRUE; + } else if (c_list_length_is(&track_ecmpid->ecmpid_lst_head, 1)) { + /* This route has no merge partner and ends up being a + * single hop route. It will be returned and configured by + * the calling "l3cfg". + * + * Unlike for multi-hop routes, we don't need to be called + * again after the onlink route was added. We are done, and + * don't need to schedule an idle commit. */ + track_obj->is_ready = TRUE; + } else { + /* This is a new route which has a gateway. We need for the "l3cfg" + * to first configure the onlink route. It's not yet ready for configuration. + * + * Instead, schedule an idle commit to make sure we get called back + * again, and then (upon seeing the entry the second time) the onlink + * route is already configured and we will be ready. */ + if (!already_notified) { + /* Some micro optimization with already_notified to avoid calling + * schedule unnecessarily. */ + already_notified = TRUE; + nm_l3cfg_commit_on_idle_schedule(l3cfg, NM_L3_CFG_COMMIT_TYPE_AUTO); + } + } + } else { + /* We see this entry the second time (or more) so it's ready. */ + track_obj->is_ready = TRUE; + } + continue; + } + + /* This entry can be dropped. */ + if (!g_hash_table_remove(priv->ecmp_track_by_obj, track_obj)) + nm_assert_not_reached(); + + if (c_list_is_empty(&track_ecmpid->ecmpid_lst_head)) { + if (track_ecmpid->merged_obj) { + if (NMP_OBJECT_CAST_IP4_ROUTE(track_ecmpid->merged_obj)->n_nexthops > 1) + nm_platform_object_delete(priv->platform, track_ecmpid->merged_obj); + } + g_hash_table_remove(priv->ecmp_track_by_ecmpid, track_ecmpid); + + continue; + } + + /* We need to update the representative obj. */ + nmp_object_ref_set( + &track_ecmpid->representative_obj, + c_list_first_entry(&track_ecmpid->ecmpid_lst_head, EcmpTrackObj, ecmpid_lst)->obj); + track_ecmpid->needs_update = TRUE; + } + + /* Now, we need to iterate again over all objects, and regenerate the merged_obj. */ + c_list_for_each_entry (track_obj, + &l3cfg->internal_netns.ecmp_track_ifindex_lst_head, + ifindex_lst) { + const NMPlatformIP4Route *route; + EcmpTrackObj *track_obj2; + nm_auto_nmpobj const NMPObject *obj_del = NULL; + gboolean changed; + gboolean all_is_ready; + + track_ecmpid = track_obj->parent_track_ecmpid; + if (track_ecmpid->already_visited) { + /* We already visited this ecmpid in the same loop. We can skip, otherwise + * we might add the same route twice. */ + continue; + } + track_ecmpid->already_visited = TRUE; + + all_is_ready = TRUE; + c_list_for_each_entry (track_obj2, &track_ecmpid->ecmpid_lst_head, ecmpid_lst) { + if (!track_obj2->is_ready) { + all_is_ready = FALSE; + break; + } + } + if (!all_is_ready) { + /* Here we might have a merged_obj already which can have the wrong + * setting e.g the wrong nexthops. We leave them for the moment and + * then we reconfigure it when this entry is ready. */ + continue; + } + + changed = _ecmp_track_init_merged_obj(track_obj->parent_track_ecmpid, &obj_del); + + nm_assert(!obj_del || changed); + + route_obj = track_ecmpid->merged_obj; + route = NMP_OBJECT_CAST_IP4_ROUTE(route_obj); + + if (obj_del) { + if (NMP_OBJECT_CAST_IP4_ROUTE(obj_del)->n_nexthops > 1) + nm_platform_object_delete(priv->platform, obj_del); + else if (track_obj->l3cfg != l3cfg) + nm_l3cfg_commit_on_idle_schedule(track_obj->l3cfg, NM_L3_CFG_COMMIT_TYPE_AUTO); + } + + if (route->n_nexthops <= 1) { + /* This is a single hop route. Return it to the caller. */ + if (!*out_singlehop_routes) { + /* Note that the returned array does not own a reference. This + * function has only one caller, and for that caller, it's just + * fine that the result is not additionally kept alive. */ + *out_singlehop_routes = + g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); + } + g_ptr_array_add(*out_singlehop_routes, (gpointer) nmp_object_ref(route_obj)); + if (changed) { + _LOGT("ecmp-route: single-hop %s", + nmp_object_to_string(route_obj, + NMP_OBJECT_TO_STRING_PUBLIC, + sbuf, + sizeof(sbuf))); + } + continue; + } + + if (changed || is_reapply) { + _LOGT("ecmp-route: multi-hop %s", + nmp_object_to_string(route_obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); + nm_platform_ip_route_add(priv->platform, NMP_NLM_FLAG_APPEND, route_obj); + } + } +} + +/*****************************************************************************/ + static void set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { @@ -385,6 +851,14 @@ nm_netns_init(NMNetns *self) priv->_self_signal_user_data = self; c_list_init(&priv->l3cfg_signal_pending_lst_head); + + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(EcmpTrackObj, obj) == 0); + priv->ecmp_track_by_obj = + g_hash_table_new_full(nm_pdirect_hash, nm_pdirect_equal, _ecmp_routes_by_obj_free, NULL); + priv->ecmp_track_by_ecmpid = g_hash_table_new_full(_ecmp_routes_by_ecmpid_hash, + _ecmp_routes_by_ecmpid_equal, + _ecmp_routes_by_ecmpid_free, + NULL); } static void @@ -396,7 +870,7 @@ constructed(GObject *object) if (!priv->platform) g_return_if_reached(); - priv->l3cfgs = g_hash_table_new_full(nm_pint_hash, nm_pint_equal, _l3cfg_data_free, NULL); + priv->l3cfgs = g_hash_table_new_full(nm_pint_hash, nm_pint_equal, _l3cfg_hashed_free, NULL); priv->platform_netns = nm_platform_netns_get(priv->platform); @@ -464,6 +938,9 @@ dispose(GObject *object) nm_assert(c_list_is_empty(&priv->l3cfg_signal_pending_lst_head)); nm_assert(!priv->shared_ips); + nm_clear_pointer(&priv->ecmp_track_by_obj, g_hash_table_destroy); + nm_clear_pointer(&priv->ecmp_track_by_ecmpid, g_hash_table_destroy); + nm_clear_g_source_inst(&priv->signal_pending_idle_source); if (priv->platform) diff --git a/src/core/nm-netns.h b/src/core/nm-netns.h index 17c1c637..84a78f83 100644 --- a/src/core/nm-netns.h +++ b/src/core/nm-netns.h @@ -9,7 +9,7 @@ #include "libnm-platform/nmp-base.h" #define NM_TYPE_NETNS (nm_netns_get_type()) -#define NM_NETNS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_NETNS, NMNetns)) +#define NM_NETNS(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_NETNS, NMNetns)) #define NM_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_NETNS, NMNetnsClass)) #define NM_IS_NETNS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_NETNS)) #define NM_IS_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_NETNS)) @@ -51,4 +51,12 @@ NMNetnsSharedIPHandle *nm_netns_shared_ip_reserve(NMNetns *self); void nm_netns_shared_ip_release(NMNetnsSharedIPHandle *handle); +/*****************************************************************************/ + +void nm_netns_ip_route_ecmp_register(NMNetns *self, NML3Cfg *l3cfg, const NMPObject *obj); +void nm_netns_ip_route_ecmp_commit(NMNetns *self, + NML3Cfg *l3cfg, + GPtrArray **routes, + gboolean is_reapply); + #endif /* __NM_NETNS_H__ */ diff --git a/src/core/nm-pacrunner-manager.c b/src/core/nm-pacrunner-manager.c index 86a40632..80db755f 100644 --- a/src/core/nm-pacrunner-manager.c +++ b/src/core/nm-pacrunner-manager.c @@ -129,7 +129,7 @@ get_ip_domains(GPtrArray *domains, const NML3ConfigData *l3cd, int addr_family) char *cidr; guint num; guint i; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; const NMPlatformIPAddress *address; const NMPlatformIPRoute *route; const char *const *strv; @@ -144,7 +144,7 @@ get_ip_domains(GPtrArray *domains, const NML3ConfigData *l3cd, int addr_family) nm_l3_config_data_iter_ip_address_for_each (&ipconf_iter, l3cd, addr_family, &address) { cidr = g_strdup_printf("%s/%u", - nm_utils_inet_ntop(addr_family, address->address_ptr, sbuf), + nm_inet_ntop(addr_family, address->address_ptr, sbuf), address->plen); g_ptr_array_add(domains, cidr); } @@ -153,7 +153,7 @@ get_ip_domains(GPtrArray *domains, const NML3ConfigData *l3cd, int addr_family) if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route)) continue; cidr = g_strdup_printf("%s/%u", - nm_utils_inet_ntop(addr_family, route->network_ptr, sbuf), + nm_inet_ntop(addr_family, route->network_ptr, sbuf), route->plen); g_ptr_array_add(domains, cidr); } diff --git a/src/core/nm-pacrunner-manager.h b/src/core/nm-pacrunner-manager.h index 3f16908f..b434349a 100644 --- a/src/core/nm-pacrunner-manager.h +++ b/src/core/nm-pacrunner-manager.h @@ -9,7 +9,7 @@ #define NM_TYPE_PACRUNNER_MANAGER (nm_pacrunner_manager_get_type()) #define NM_PACRUNNER_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PACRUNNER_MANAGER, NMPacrunnerManager)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PACRUNNER_MANAGER, NMPacrunnerManager)) #define NM_PACRUNNER_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_PACRUNNER_MANAGER, NMPacrunnerManagerClass)) #define NM_IS_PACRUNNER_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_PACRUNNER_MANAGER)) diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c index 0c07c29e..4192808a 100644 --- a/src/core/nm-policy.c +++ b/src/core/nm-policy.c @@ -108,9 +108,10 @@ _PRIV_TO_SELF(NMPolicyPrivate *priv) nm_assert(priv); - self = (NMPolicy *) (((char *) priv) - G_STRUCT_OFFSET(NMPolicy, _priv)); + self = NM_CAST_ALIGN(NMPolicy, (((char *) priv) - G_STRUCT_OFFSET(NMPolicy, _priv))); nm_assert(NM_IS_POLICY(self)); + return self; } @@ -167,11 +168,11 @@ static void clear_ip6_prefix_delegation(gpointer data) { IP6PrefixDelegation *delegation = data; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; _LOGD(LOGD_IP6, "ipv6-pd: undelegating prefix %s/%d", - _nm_utils_inet6_ntop(&delegation->prefix.address, sbuf), + nm_inet6_ntop(&delegation->prefix.address, sbuf), delegation->prefix.plen); g_hash_table_foreach(delegation->subnets, _clear_ip6_subnet, NULL); @@ -187,7 +188,7 @@ expire_ip6_delegations(NMPolicy *self) guint i; for (i = 0; i < priv->ip6_prefix_delegations->len; i++) { - delegation = &g_array_index(priv->ip6_prefix_delegations, IP6PrefixDelegation, i); + delegation = &nm_g_array_index(priv->ip6_prefix_delegations, IP6PrefixDelegation, i); if (delegation->prefix.timestamp + delegation->prefix.lifetime < now) g_array_remove_index_fast(priv->ip6_prefix_delegations, i); } @@ -204,7 +205,7 @@ ip6_subnet_from_delegation(IP6PrefixDelegation *delegation, NMDevice *device) { NMPlatformIP6Address *subnet; int ifindex = nm_device_get_ifindex(device); - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; subnet = g_hash_table_lookup(delegation->subnets, GINT_TO_POINTER(ifindex)); if (!subnet) { @@ -212,7 +213,7 @@ ip6_subnet_from_delegation(IP6PrefixDelegation *delegation, NMDevice *device) if (delegation->next_subnet >= (1 << (64 - delegation->prefix.plen))) { _LOGD(LOGD_IP6, "ipv6-pd: no more prefixes in %s/%d", - _nm_utils_inet6_ntop(&delegation->prefix.address, sbuf), + nm_inet6_ntop(&delegation->prefix.address, sbuf), delegation->prefix.plen); return FALSE; } @@ -241,7 +242,7 @@ ip6_subnet_from_delegation(IP6PrefixDelegation *delegation, NMDevice *device) _LOGD(LOGD_IP6, "ipv6-pd: %s allocated from a /%d prefix on %s", - _nm_utils_inet6_ntop(&subnet->address, sbuf), + nm_inet6_ntop(&subnet->address, sbuf), delegation->prefix.plen, nm_device_get_iface(device)); @@ -267,7 +268,7 @@ ip6_subnet_from_device(NMPolicy *self, NMDevice *from_device, NMDevice *device) expire_ip6_delegations(self); for (i = 0; i < priv->ip6_prefix_delegations->len; i++) { - delegation = &g_array_index(priv->ip6_prefix_delegations, IP6PrefixDelegation, i); + delegation = &nm_g_array_index(priv->ip6_prefix_delegations, IP6PrefixDelegation, i); if (delegation->device != from_device) continue; @@ -295,7 +296,7 @@ ip6_remove_device_prefix_delegations(NMPolicy *self, NMDevice *device) guint i; for (i = 0; i < priv->ip6_prefix_delegations->len; i++) { - delegation = &g_array_index(priv->ip6_prefix_delegations, IP6PrefixDelegation, i); + delegation = &nm_g_array_index(priv->ip6_prefix_delegations, IP6PrefixDelegation, i); if (delegation->device == device) g_array_remove_index_fast(priv->ip6_prefix_delegations, i); } @@ -312,11 +313,11 @@ device_ip6_prefix_delegated(NMDevice *device, guint i; const CList *tmp_list; NMActiveConnection *ac; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; _LOGI(LOGD_IP6, "ipv6-pd: received a prefix %s/%d from %s", - _nm_utils_inet6_ntop(&prefix->address, sbuf), + nm_inet6_ntop(&prefix->address, sbuf), prefix->plen, nm_device_get_iface(device)); @@ -324,16 +325,15 @@ device_ip6_prefix_delegated(NMDevice *device, for (i = 0; i < priv->ip6_prefix_delegations->len; i++) { /* Look for an already known prefix to update. */ - delegation = &g_array_index(priv->ip6_prefix_delegations, IP6PrefixDelegation, i); + delegation = &nm_g_array_index(priv->ip6_prefix_delegations, IP6PrefixDelegation, i); if (IN6_ARE_ADDR_EQUAL(&delegation->prefix.address, &prefix->address)) break; } if (i == priv->ip6_prefix_delegations->len) { /* Allocate a delegation for new prefix. */ - g_array_set_size(priv->ip6_prefix_delegations, i + 1); - delegation = &g_array_index(priv->ip6_prefix_delegations, IP6PrefixDelegation, i); - delegation->subnets = g_hash_table_new(nm_direct_hash, NULL); + delegation = nm_g_array_append_new(priv->ip6_prefix_delegations, IP6PrefixDelegation); + delegation->subnets = g_hash_table_new(nm_direct_hash, NULL); delegation->next_subnet = 0; } @@ -458,7 +458,7 @@ get_best_active_connection(NMPolicy *self, int addr_family, gboolean fully_activ } static gboolean -all_devices_not_active(NMPolicy *self) +any_devices_active(NMPolicy *self) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self); const CList *tmp_lst; @@ -468,12 +468,13 @@ all_devices_not_active(NMPolicy *self) NMDeviceState state; state = nm_device_get_state(device); - if (state <= NM_DEVICE_STATE_DISCONNECTED || state >= NM_DEVICE_STATE_DEACTIVATING) { + if (state <= NM_DEVICE_STATE_DISCONNECTED || state >= NM_DEVICE_STATE_DEACTIVATING) continue; - } - return FALSE; + if (nm_device_sys_iface_state_is_external(device)) + continue; + return TRUE; } - return TRUE; + return FALSE; } #define FALLBACK_HOSTNAME4 "localhost.localdomain" @@ -607,7 +608,7 @@ _set_hostname(NMPolicy *self, const char *new_hostname, const char *msg) priv->updating_dns = TRUE; nm_dns_manager_set_hostname(priv->dns_manager, priv->cur_hostname_full, - all_devices_not_active(self)); + !any_devices_active(self)); priv->updating_dns = FALSE; } @@ -738,6 +739,9 @@ build_device_hostname_infos(NMPolicy *self) if (!device) continue; + if (nm_device_sys_iface_state_is_external(device)) + continue; + only_from_default = device_get_hostname_property_boolean(device, NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT); @@ -770,10 +774,10 @@ build_device_hostname_infos(NMPolicy *self) g_array_sort(array, device_hostname_info_compare); - info0 = &g_array_index(array, DeviceHostnameInfo, 0); + info0 = &nm_g_array_first(array, DeviceHostnameInfo); if (info0->priority < 0) { for (i = 1; i < array->len; i++) { - const DeviceHostnameInfo *info = &g_array_index(array, DeviceHostnameInfo, i); + const DeviceHostnameInfo *info = &nm_g_array_index(array, DeviceHostnameInfo, i); if (info->priority > info0->priority) { g_array_set_size(array, i); @@ -878,7 +882,7 @@ update_system_hostname(NMPolicy *self, const char *msg) if (infos && _LOGT_ENABLED(LOGD_DNS)) { _LOGT(LOGD_DNS, "device hostname info:"); for (i = 0; i < infos->len; i++) { - info = &g_array_index(infos, DeviceHostnameInfo, i); + info = &nm_g_array_index(infos, DeviceHostnameInfo, i); _LOGT(LOGD_DNS, " - prio:%5d ipv%c%s %s %s dev:%s", info->priority, @@ -891,7 +895,7 @@ update_system_hostname(NMPolicy *self, const char *msg) } for (i = 0; infos && i < infos->len; i++) { - info = &g_array_index(infos, DeviceHostnameInfo, i); + info = &nm_g_array_index(infos, DeviceHostnameInfo, i); addr_family = info->IS_IPv4 ? AF_INET : AF_INET6; g_signal_handlers_disconnect_by_func(info->device, device_dns_lookup_done, self); g_signal_handlers_disconnect_by_func(info->device, device_carrier_changed, priv); @@ -1361,8 +1365,16 @@ auto_activate_device(NMPolicy *self, NMDevice *device) // but another connection now overrides the current one for that device, // deactivate the device and activate the new connection instead of just // bailing if the device is already active - if (nm_device_get_act_request(device)) - return; + if (nm_device_get_act_request(device)) { + if (nm_device_sys_iface_state_is_external(device) + && nm_device_get_allow_autoconnect_on_external(device)) { + /* this is an external activation, and we allow autoconnecting on + * top of that. + * + * pass. */ + } else + return; + } if (!nm_device_autoconnect_allowed(device)) return; @@ -1689,8 +1701,13 @@ schedule_activate_check(NMPolicy *self, NMDevice *device) return; nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) { - if (nm_active_connection_get_device(ac) == device) - return; + if (nm_active_connection_get_device(ac) == device) { + if (nm_device_sys_iface_state_is_external(device) + && nm_device_get_allow_autoconnect_on_external(device)) { + /* pass */ + } else + return; + } } nm_device_add_pending_action(device, NM_PENDING_ACTION_AUTOACTIVATE, TRUE); diff --git a/src/core/nm-policy.h b/src/core/nm-policy.h index fa216db3..f2b98dbd 100644 --- a/src/core/nm-policy.h +++ b/src/core/nm-policy.h @@ -8,7 +8,7 @@ #define __NETWORKMANAGER_POLICY_H__ #define NM_TYPE_POLICY (nm_policy_get_type()) -#define NM_POLICY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_POLICY, NMPolicy)) +#define NM_POLICY(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_POLICY, NMPolicy)) #define NM_POLICY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_POLICY, NMPolicyClass)) #define NM_IS_POLICY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_POLICY)) #define NM_IS_POLICY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_POLICY)) diff --git a/src/core/nm-rfkill-manager.h b/src/core/nm-rfkill-manager.h index 88d0421a..818ab4b2 100644 --- a/src/core/nm-rfkill-manager.h +++ b/src/core/nm-rfkill-manager.h @@ -37,7 +37,7 @@ const char *nm_rfkill_type_to_string(NMRfkillType rtype); #define NM_TYPE_RFKILL_MANAGER (nm_rfkill_manager_get_type()) #define NM_RFKILL_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_RFKILL_MANAGER, NMRfkillManager)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_RFKILL_MANAGER, NMRfkillManager)) #define NM_RFKILL_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_RFKILL_MANAGER, NMRfkillManagerClass)) #define NM_IS_RFKILL_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_RFKILL_MANAGER)) diff --git a/src/core/nm-session-monitor.h b/src/core/nm-session-monitor.h index 63a3136b..5b7c3f91 100644 --- a/src/core/nm-session-monitor.h +++ b/src/core/nm-session-monitor.h @@ -10,7 +10,7 @@ #define NM_TYPE_SESSION_MONITOR (nm_session_monitor_get_type()) #define NM_SESSION_MONITOR(o) \ - (G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_SESSION_MONITOR, NMSessionMonitor)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_SESSION_MONITOR, NMSessionMonitor)) #define NM_SESSION_MONITOR_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_SESSION_MONITOR, NMSessionMonitorClass)) #define NM_SESSION_MONITOR_GET_CLASS(o) \ diff --git a/src/core/nm-sleep-monitor.c b/src/core/nm-sleep-monitor.c index a7ea496d..66ea2f6c 100644 --- a/src/core/nm-sleep-monitor.c +++ b/src/core/nm-sleep-monitor.c @@ -11,6 +11,7 @@ #include #include +#include "libnm-glib-aux/nm-dbus-aux.h" #include "libnm-core-intern/nm-core-internal.h" #include "NetworkManagerUtils.h" @@ -294,24 +295,24 @@ on_proxy_acquired(GObject *object, GAsyncResult *res, NMSleepMonitor *self) g_clear_object(&self->cancellable); #if USE_UPOWER - self->sig_id_1 = _nm_dbus_signal_connect(self->proxy, - "Sleeping", - NULL, - G_CALLBACK(upower_sleeping_cb), - self); - self->sig_id_2 = _nm_dbus_signal_connect(self->proxy, - "Resuming", - NULL, - G_CALLBACK(upower_resuming_cb), - self); + self->sig_id_1 = _nm_dbus_proxy_signal_connect(self->proxy, + "Sleeping", + NULL, + G_CALLBACK(upower_sleeping_cb), + self); + self->sig_id_2 = _nm_dbus_proxy_signal_connect(self->proxy, + "Resuming", + NULL, + G_CALLBACK(upower_resuming_cb), + self); #else self->sig_id_1 = g_signal_connect(self->proxy, "notify::g-name-owner", G_CALLBACK(name_owner_cb), self); - self->sig_id_2 = _nm_dbus_signal_connect(self->proxy, - "PrepareForSleep", - G_VARIANT_TYPE("(b)"), - G_CALLBACK(prepare_for_sleep_cb), - self); + self->sig_id_2 = _nm_dbus_proxy_signal_connect(self->proxy, + "PrepareForSleep", + G_VARIANT_TYPE("(b)"), + G_CALLBACK(prepare_for_sleep_cb), + self); { gs_free char *owner = NULL; diff --git a/src/core/nm-sleep-monitor.h b/src/core/nm-sleep-monitor.h index 7d45065b..0b7708db 100644 --- a/src/core/nm-sleep-monitor.h +++ b/src/core/nm-sleep-monitor.h @@ -8,7 +8,8 @@ #define __NETWORKMANAGER_SLEEP_MONITOR_H__ #define NM_TYPE_SLEEP_MONITOR (nm_sleep_monitor_get_type()) -#define NM_SLEEP_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_SLEEP_MONITOR, NMSleepMonitor)) +#define NM_SLEEP_MONITOR(o) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_SLEEP_MONITOR, NMSleepMonitor)) #define NM_SLEEP_MONITOR_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_SLEEP_MONITOR, NMSleepMonitorClass)) #define NM_SLEEP_MONITOR_GET_CLASS(o) \ diff --git a/src/core/nm-test-utils-core.h b/src/core/nm-test-utils-core.h index 467f9bb2..e341d62f 100644 --- a/src/core/nm-test-utils-core.h +++ b/src/core/nm-test-utils-core.h @@ -72,8 +72,8 @@ nmtst_platform_ip6_address(const char *address, const char *peer_address, guint g_assert(plen <= 128); memset(&addr, 0, sizeof(addr)); - addr.address = *nmtst_inet6_from_string(address); - addr.peer_address = *nmtst_inet6_from_string(peer_address); + addr.address = nmtst_inet6_from_string(address); + addr.peer_address = nmtst_inet6_from_string(peer_address); addr.plen = plen; return &addr; @@ -148,10 +148,10 @@ nmtst_platform_ip6_route(const char *network, guint plen, const char *gateway, c nm_assert(plen <= 128); memset(&route, 0, sizeof(route)); - route.network = *nmtst_inet6_from_string(network); + route.network = nmtst_inet6_from_string(network); route.plen = plen; - route.gateway = *nmtst_inet6_from_string(gateway); - route.pref_src = *nmtst_inet6_from_string(pref_src); + route.gateway = nmtst_inet6_from_string(gateway); + route.pref_src = nmtst_inet6_from_string(pref_src); return &route; } @@ -178,8 +178,9 @@ nmtst_platform_ip6_route_full(const char *network, static inline int _nmtst_platform_ip4_routes_equal_sort(gconstpointer a, gconstpointer b, gpointer user_data) { - return nm_platform_ip4_route_cmp_full((const NMPlatformIP4Route *) a, - (const NMPlatformIP4Route *) b); + return nm_platform_ip4_route_cmp((const NMPlatformIP4Route *) a, + (const NMPlatformIP4Route *) b, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); } static inline void @@ -210,7 +211,7 @@ nmtst_platform_ip4_routes_equal(const NMPlatformIP4Route *a, } for (i = 0; i < len; i++) { - if (nm_platform_ip4_route_cmp_full(&a[i], &b[i]) != 0) { + if (nm_platform_ip4_route_cmp(&a[i], &b[i], NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) != 0) { char buf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; @@ -248,8 +249,9 @@ nmtst_platform_ip4_routes_equal_aptr(const NMPObject *const *a, static inline int _nmtst_platform_ip6_routes_equal_sort(gconstpointer a, gconstpointer b, gpointer user_data) { - return nm_platform_ip6_route_cmp_full((const NMPlatformIP6Route *) a, - (const NMPlatformIP6Route *) b); + return nm_platform_ip6_route_cmp((const NMPlatformIP6Route *) a, + (const NMPlatformIP6Route *) b, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); } static inline void @@ -280,7 +282,7 @@ nmtst_platform_ip6_routes_equal(const NMPlatformIP6Route *a, } for (i = 0; i < len; i++) { - if (nm_platform_ip6_route_cmp_full(&a[i], &b[i]) != 0) { + if (nm_platform_ip6_route_cmp(&a[i], &b[i], NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) != 0) { char buf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; diff --git a/src/core/platform/nm-fake-platform.c b/src/core/platform/nm-fake-platform.c index a1ca5434..c92d9aef 100644 --- a/src/core/platform/nm-fake-platform.c +++ b/src/core/platform/nm-fake-platform.c @@ -170,7 +170,7 @@ link_get(NMPlatform *platform, int ifindex) if (idx >= priv->links->len) goto not_found; - device = &g_array_index(priv->links, NMFakePlatformLink, idx); + device = &nm_g_array_index(priv->links, NMFakePlatformLink, idx); if (!device->obj) goto not_found; @@ -234,8 +234,7 @@ link_add_pre(NMPlatform *platform, g_assert(!name || strlen(name) < IFNAMSIZ); - g_array_set_size(priv->links, priv->links->len + 1); - device = &g_array_index(priv->links, NMFakePlatformLink, priv->links->len - 1); + device = nm_g_array_append_new(priv->links, NMFakePlatformLink); ifindex = priv->links->len; memset(device, 0, sizeof(*device)); @@ -272,7 +271,7 @@ link_add_pre(NMPlatform *platform, g_assert(address_len == 0); device->obj = o; - device->ip6_lladdr = *nmtst_inet6_from_string(ip6_lladdr); + device->ip6_lladdr = nmtst_inet6_from_string(ip6_lladdr); return device; } @@ -980,7 +979,7 @@ ipx_address_delete(NMPlatform *platform, || (plen && address->plen != *plen) || (peer_addr && (((peer_addr_i ^ address->peer_address) - & _nm_utils_ip4_prefix_to_netmask(address->plen)) + & nm_ip4_addr_netmask_from_prefix(address->plen)) != 0))) continue; } else { @@ -1093,10 +1092,7 @@ object_delete(NMPlatform *platform, const NMPObject *obj) } static int -ip_route_add(NMPlatform *platform, - NMPNlmFlags flags, - int addr_family, - const NMPlatformIPRoute *route) +ip_route_add(NMPlatform *platform, NMPNlmFlags flags, NMPObject *obj_stack) { NMDedupMultiIter iter; nm_auto_nmpobj NMPObject *obj = NULL; @@ -1110,23 +1106,29 @@ ip_route_add(NMPlatform *platform, NMPlatformIPRoute *r = NULL; NMPlatformIP4Route *r4 = NULL; NMPlatformIP6Route *r6 = NULL; + int addr_family; gboolean has_same_weak_id; gboolean only_dirty; guint16 nlmsgflags; - g_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); + g_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_stack), + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + + addr_family = NMP_OBJECT_GET_ADDR_FAMILY(obj_stack); flags = NM_FLAGS_UNSET(flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE); /* currently, only replace is implemented. */ g_assert(flags == NMP_NLM_FLAG_REPLACE); - obj = nmp_object_new(addr_family == AF_INET ? NMP_OBJECT_TYPE_IP4_ROUTE - : NMP_OBJECT_TYPE_IP6_ROUTE, - (const NMPlatformObject *) route); - r = NMP_OBJECT_CAST_IP_ROUTE(obj); + if (NMP_OBJECT_GET_TYPE(obj_stack) == NMP_OBJECT_TYPE_IP4_ROUTE + && obj_stack->ip4_route.n_nexthops == 0 && obj_stack->ip4_route.ifindex > 0) + obj_stack->ip4_route.n_nexthops = 1; - nm_platform_ip_route_normalize(addr_family, r); + obj = nmp_object_clone(obj_stack, FALSE); + + r = NMP_OBJECT_CAST_IP_ROUTE(obj); switch (addr_family) { case AF_INET: @@ -1151,8 +1153,8 @@ ip_route_add(NMPlatform *platform, &o) { if (addr_family == AF_INET) { const NMPlatformIP4Route *item = NMP_OBJECT_CAST_IP4_ROUTE(o); - guint32 n = nm_utils_ip4_address_clear_host_address(item->network, item->plen); - guint32 g = nm_utils_ip4_address_clear_host_address(r4->gateway, item->plen); + guint32 n = nm_ip4_addr_clear_host_address(item->network, item->plen); + guint32 g = nm_ip4_addr_clear_host_address(r4->gateway, item->plen); if (r->ifindex == item->ifindex && n == g) { has_route_to_gw = TRUE; @@ -1162,21 +1164,21 @@ ip_route_add(NMPlatform *platform, const NMPlatformIP6Route *item = NMP_OBJECT_CAST_IP6_ROUTE(o); if (r->ifindex == item->ifindex - && nm_utils_ip6_address_same_prefix(&r6->gateway, &item->network, item->plen)) { + && nm_ip6_addr_same_prefix(&r6->gateway, &item->network, item->plen)) { has_route_to_gw = TRUE; break; } } } if (!has_route_to_gw) { - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; if (addr_family == AF_INET) { nm_log_warn( LOGD_PLATFORM, "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable", r->ifindex, - _nm_utils_inet4_ntop(r4->network, sbuf), + nm_inet4_ntop(r4->network, sbuf), r->plen, r->metric); } else { @@ -1184,7 +1186,7 @@ ip_route_add(NMPlatform *platform, LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", r->ifindex, - _nm_utils_inet6_ntop(&r6->network, sbuf), + nm_inet6_ntop(&r6->network, sbuf), r->plen, r->metric); } @@ -1230,6 +1232,7 @@ ip_route_add(NMPlatform *platform, obj, FALSE, nlmsgflags, + TRUE, &obj_old, &obj_new, &obj_replace, @@ -1292,7 +1295,7 @@ finalize(GObject *object) g_hash_table_unref(priv->options); for (i = 0; i < priv->links->len; i++) { - NMFakePlatformLink *device = &g_array_index(priv->links, NMFakePlatformLink, i); + NMFakePlatformLink *device = &nm_g_array_index(priv->links, NMFakePlatformLink, i); nm_clear_pointer(&device->obj, nmp_object_unref); } diff --git a/src/core/platform/nm-fake-platform.h b/src/core/platform/nm-fake-platform.h index 8f8571fa..88fd3243 100644 --- a/src/core/platform/nm-fake-platform.h +++ b/src/core/platform/nm-fake-platform.h @@ -10,7 +10,7 @@ #define NM_TYPE_FAKE_PLATFORM (nm_fake_platform_get_type()) #define NM_FAKE_PLATFORM(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_FAKE_PLATFORM, NMFakePlatform)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_FAKE_PLATFORM, NMFakePlatform)) #define NM_FAKE_PLATFORM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_FAKE_PLATFORM, NMFakePlatformClass)) #define NM_IS_FAKE_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_FAKE_PLATFORM)) diff --git a/src/core/platform/tests/meson.build b/src/core/platform/tests/meson.build index 8824ed2f..252c6840 100644 --- a/src/core/platform/tests/meson.build +++ b/src/core/platform/tests/meson.build @@ -13,7 +13,7 @@ test_units = [ ['test-nmp-object', 'test-nmp-object.c', test_c_flags, default_test_timeout], ['test-platform-general', 'test-platform-general.c', test_c_flags, default_test_timeout], ['test-route-fake', 'test-route.c', test_fake_c_flags, default_test_timeout], - ['test-route-linux', 'test-route.c', test_linux_c_flags, default_test_timeout], + ['test-route-linux', 'test-route.c', test_linux_c_flags, 900], ['test-tc-linux', 'test-tc.c', test_linux_c_flags, default_test_timeout], ] diff --git a/src/core/platform/tests/monitor.c b/src/core/platform/tests/monitor.c index 8d0e0107..c83192bb 100644 --- a/src/core/platform/tests/monitor.c +++ b/src/core/platform/tests/monitor.c @@ -8,6 +8,8 @@ #include #include +#include "libnm-glib-aux/nm-str-buf.h" +#include "libnm-glib-aux/nm-io-utils.h" #include "libnm-platform/nm-linux-platform.h" #include "libnm-platform/nmp-object.h" @@ -17,23 +19,45 @@ NMTST_DEFINE(); static struct { gboolean persist; + char *state_file; } global_opt = { .persist = TRUE, }; +const char *const *const ip_argv_rule6 = NM_MAKE_STRV("ip", "-d", "-6", "rule"); + +static const struct { + NMPObjectType obj_type; + const char *const *ip_argv; +} dump_obj_types[] = { + {NMP_OBJECT_TYPE_LINK, NM_MAKE_STRV("ip", "-d", "link")}, + {NMP_OBJECT_TYPE_IP4_ADDRESS, NM_MAKE_STRV("ip", "-d", "-4", "address")}, + {NMP_OBJECT_TYPE_IP6_ADDRESS, NM_MAKE_STRV("ip", "-d", "-6", "address")}, + {NMP_OBJECT_TYPE_IP4_ROUTE, NM_MAKE_STRV("ip", "-d", "-4", "route")}, + {NMP_OBJECT_TYPE_IP6_ROUTE, NM_MAKE_STRV("ip", "-d", "-6", "route")}, + {NMP_OBJECT_TYPE_ROUTING_RULE, NM_MAKE_STRV("ip", "-d", "-4", "rule")}, +}; + static gboolean read_argv(int *argc, char ***argv) { GOptionContext *context; GOptionEntry options[] = { - {"no-persist", + {"no-persist", 'P', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &global_opt.persist, "Exit after processing netlink messages", NULL}, - {0}, + {"state-file", + 'S', + 0, + G_OPTION_ARG_FILENAME, + &global_opt.state_file, + "Dump the platform cache to this file", + "FILE"}, + {0}, }; gs_free_error GError *error = NULL; @@ -63,6 +87,123 @@ mptcp_addr_dump(NMPlatform *platform) /*****************************************************************************/ +static void +_dump_state(NMPlatform *platform, const char *state_file) +{ + nm_auto_str_buf NMStrBuf sbuf = NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_488, FALSE); + nm_auto_unref_gdatetime GDateTime *time_datetime = NULL; + gs_free char *time_str = NULL; + int i_obj_type; + + if (!state_file) + return; + + time_datetime = g_date_time_new_now_local(); + time_str = g_date_time_format(time_datetime, "%C%y-%m-%dT%H:%M:%S.%f"); + + nm_log_dbg(LOGD_PLATFORM, "dump to file \"%s\", at %s", state_file, time_str); + + nm_str_buf_append_printf(&sbuf, "time: %s\n", time_str); + nm_str_buf_append_printf(&sbuf, "pid: %lld\n", (long long) getpid()); + + for (i_obj_type = 0; i_obj_type < (int) G_N_ELEMENTS(dump_obj_types); i_obj_type++) { + NMPObjectType obj_type = dump_obj_types[i_obj_type].obj_type; + const char *const *ip_argv = dump_obj_types[i_obj_type].ip_argv; + const NMDedupMultiHeadEntry *pl_head_entry; + NMDedupMultiIter pl_iter; + const NMPObject *obj; + guint i; + char buf1[1000]; + + pl_head_entry = nm_platform_lookup_obj_type(platform, obj_type); + + nm_str_buf_append_printf(&sbuf, + "\n%s: %u\n", + nmp_class_from_type(obj_type)->obj_type_name, + pl_head_entry ? pl_head_entry->len : 0u); + + i = 0; + nmp_cache_iter_for_each (&pl_iter, pl_head_entry, &obj) { + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, buf1, sizeof(buf1)); + nm_str_buf_append_printf(&sbuf, + "%s[%u]: %s\n", + nmp_class_from_type(obj_type)->obj_type_name, + i, + buf1); + g_assert(strlen(buf1) < sizeof(buf1) - 1u); + i++; + } + +ip_again: + if (ip_argv) { + gs_free_error GError *error = NULL; + gs_free char *ip_argv_ss = NULL; + gs_free char *s_stdout = NULL; + gs_free char *s_stderr = NULL; + int exit_code; + + g_spawn_sync(NULL, + (char **) ip_argv, + NULL, + G_SPAWN_SEARCH_PATH, + NULL, + NULL, + &s_stdout, + &s_stderr, + &exit_code, + &error); + + nm_str_buf_append_printf(&sbuf, + "%s: call %s: ", + nmp_class_from_type(obj_type)->obj_type_name, + ip_argv_ss = g_strjoinv(" ", (char **) ip_argv)); + if (error) { + nm_str_buf_append_printf(&sbuf, "FAILED: %s\n", error->message); + } else if (WIFEXITED(exit_code) && WEXITSTATUS(exit_code) == 0) + nm_str_buf_append_printf(&sbuf, "SUCCESS\n"); + else { + nm_str_buf_append_printf( + &sbuf, + "ERROR: %s\n", + nm_utils_get_process_exit_status_desc_buf(exit_code, buf1, sizeof(buf1))); + } + if (!nm_str_is_empty(s_stdout)) + nm_str_buf_append_printf(&sbuf, "STDOUT>\n%s<\n", s_stdout); + if (!nm_str_is_empty(s_stderr)) + nm_str_buf_append_printf(&sbuf, "STDERR>\n%s<\n", s_stderr); + + if (obj_type == NMP_OBJECT_TYPE_ROUTING_RULE + && ip_argv == dump_obj_types[i_obj_type].ip_argv) { + ip_argv = ip_argv_rule6; + goto ip_again; + } + } + } + + nm_utils_file_set_contents(state_file, + nm_str_buf_get_str_unsafe(&sbuf), + sbuf.len, + 00644, + NULL, + NULL, + NULL); + + nm_log_dbg(LOGD_PLATFORM, "dump to file complete"); +} + +static void +_dump_state_platform_cb(NMPlatform *platform, + int obj_type_i, + int ifindex, + gconstpointer platform_object, + int change_type_i, + gpointer unused_user_data) +{ + _dump_state(platform, global_opt.state_file); +} + +/*****************************************************************************/ + int main(int argc, char **argv) { @@ -82,12 +223,32 @@ main(int argc, char **argv) nm_linux_platform_setup(); + if (global_opt.state_file) { + int i_obj_type; + + for (i_obj_type = 0; i_obj_type < (int) G_N_ELEMENTS(dump_obj_types); i_obj_type++) { + NMPObjectType obj_type = dump_obj_types[i_obj_type].obj_type; + + g_signal_connect(NM_PLATFORM_GET, + nmp_class_from_type(obj_type)->signal_type, + G_CALLBACK(_dump_state_platform_cb), + NULL); + } + } + mptcp_addr_dump(NM_PLATFORM_GET); + _dump_state(NM_PLATFORM_GET, global_opt.state_file); + if (global_opt.persist) g_main_loop_run(loop); g_main_loop_unref(loop); + g_signal_handlers_disconnect_by_func(NM_PLATFORM_GET, + G_CALLBACK(_dump_state_platform_cb), + NULL); + g_object_unref(NM_PLATFORM_GET); + return EXIT_SUCCESS; } diff --git a/src/core/platform/tests/test-address.c b/src/core/platform/tests/test-address.c index 700891e8..0bce00c4 100644 --- a/src/core/platform/tests/test-address.c +++ b/src/core/platform/tests/test-address.c @@ -118,7 +118,7 @@ test_ip4_address_general(void) addresses = nmtstp_platform_ip4_address_get_all(NM_PLATFORM_GET, ifindex); g_assert(addresses); g_assert_cmpint(addresses->len, ==, 1); - address = &g_array_index(addresses, NMPlatformIP4Address, 0); + address = &nm_g_array_first(addresses, NMPlatformIP4Address); g_assert_cmpint(address->ifindex, ==, ifindex); g_assert_cmphex(address->address, ==, addr); g_assert_cmphex(address->peer_address, ==, addr); @@ -193,7 +193,7 @@ test_ip6_address_general(void) addresses = nmtstp_platform_ip6_address_get_all(NM_PLATFORM_GET, ifindex); g_assert(addresses); g_assert_cmpint(addresses->len, ==, 1); - address = &g_array_index(addresses, NMPlatformIP6Address, 0); + address = &nm_g_array_first(addresses, NMPlatformIP6Address); g_assert_cmpint(address->ifindex, ==, ifindex); g_assert(!memcmp(&address->address, &addr, sizeof(addr))); g_assert_cmpint(address->plen, ==, IP6_PLEN); @@ -453,7 +453,7 @@ _nmtstp_init_tests(int *argc, char ***argv) void _nmtstp_setup_tests(void) { -#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, FALSE) +#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, 1, FALSE) add_test_func("/address/ipv4/general", test_ip4_address_general); add_test_func("/address/ipv6/general", test_ip6_address_general); diff --git a/src/core/platform/tests/test-cleanup.c b/src/core/platform/tests/test-cleanup.c index 2198e921..139a0280 100644 --- a/src/core/platform/tests/test-cleanup.c +++ b/src/core/platform/tests/test-cleanup.c @@ -63,7 +63,7 @@ test_cleanup_internal(void) nm_platform_process_events(NM_PLATFORM_GET); } addrs = nmtstp_platform_ip6_address_get_all(NM_PLATFORM_GET, ifindex); - if (addrs->len == 1 && (a = &g_array_index(addrs, NMPlatformIP6Address, 0)) + if (addrs->len == 1 && (a = &nm_g_array_first(addrs, NMPlatformIP6Address)) && IN6_IS_ADDR_LINKLOCAL(&a->address)) break; }); @@ -73,7 +73,7 @@ test_cleanup_internal(void) addr4, plen4, addr4, - nm_platform_ip4_broadcast_address_create(addr4, plen4), + nm_ip4_addr_get_broadcast_address(addr4, plen4), lifetime, preferred, 0, diff --git a/src/core/platform/tests/test-common.c b/src/core/platform/tests/test-common.c index b5bb1232..a37982fc 100644 --- a/src/core/platform/tests/test-common.c +++ b/src/core/platform/tests/test-common.c @@ -15,6 +15,7 @@ #include #include "n-acd/src/n-acd.h" +#include "libnm-platform/nm-platform-utils.h" #define SIGNAL_DATA_FMT "'%s-%s' ifindex %d%s%s%s (%d times received)" #define SIGNAL_DATA_ARG(data) \ @@ -22,8 +23,60 @@ (data)->ifname ? " ifname '" : "", (data)->ifname ?: "", (data)->ifname ? "'" : "", \ (data)->received_count -int NMTSTP_ENV1_IFINDEX = -1; -int NMTSTP_ENV1_EX = -1; +int NMTSTP_ENV1_IFINDEXES[]; + +const char *const NMTSTP_ENV1_DEVICE_NAME[] = { + "nm-test-device0", + "nm-test-device1", +}; + +int NMTSTP_ENV1_EX = -1; + +/*****************************************************************************/ + +typedef struct { + const char *module_name; + + NMLinkType iftype; + + /* These modules create additional interfaces, like + * "gre0" for "ip_gre" module. + * + * - actually some modules create multiple interfaces, like "ip_gre" + * creating "gre0", "gretap0", "erspan0". + * - if already an interface with such name exist, kernel would create + * names like "gre1" or "gretap1". We don't care for that, because + * we run in our own namespace and we control which interfaces are there. + * We wouldn't create an interface with such a conflicting name. + * + * Anyway. This is the name of *one* of the interfaces that the module would + * create. */ + const char *ifname; + + /* This only gets set, if iftype is NM_LINK_TYPE_UNKNOWN. It corresponds to + * NMPlatformLink.kind. */ + const char *ifkind; + +} IPTunnelModInfo; + +#define INF(_module_name, _iftype, _ifname, ...) \ + { \ + .module_name = ""_module_name, .iftype = _iftype, .ifname = ""_ifname, __VA_ARGS__ \ + } + +static const IPTunnelModInfo ip_tunnel_mod_infos[] = { + INF("ip_gre", NM_LINK_TYPE_GRE, "gre0"), + INF("ip_gre", NM_LINK_TYPE_GRETAP, "gretap0"), + INF("ip_gre", NM_LINK_TYPE_UNKNOWN, "erspan0", .ifkind = "erspan"), + INF("ipip", NM_LINK_TYPE_IPIP, "tunl0"), + INF("ip6_tunnel", NM_LINK_TYPE_IP6TNL, "ip6tnl0"), + INF("ip6_gre", NM_LINK_TYPE_IP6GRE, "ip6gre0"), + INF("sit", NM_LINK_TYPE_SIT, "sit0"), + INF("ip_vti", NM_LINK_TYPE_VTI, "ip_vti0"), + INF("ip6_vti", NM_LINK_TYPE_VTI6, "ip6_vti0"), +}; + +#undef INF /*****************************************************************************/ @@ -218,13 +271,13 @@ _nmtstp_platform_ip_addresses_assert(const char *filename, } if (!addr_bin->found) { - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; g_error("%s:%d: IPv%c address %s was not found on ifindex %d", filename, lineno, nm_utils_addr_family_to_char(addr_bin->addr_family), - nm_utils_inet_ntop(addr_bin->addr_family, &addr_bin->addr, sbuf), + nm_inet_ntop(addr_bin->addr_family, &addr_bin->addr, sbuf), ifindex); } } @@ -583,7 +636,7 @@ _nmtstp_assert_ip4_route_exists(const char *file, r = _ip4_route_get(platform, ifindex, network, plen, metric, tos, &c); if (c != c_exists && c_exists != -1) { - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("[%s:%u] %s(): The ip4 route %s/%d metric %u tos %u shall exist %u times, but " @@ -591,7 +644,7 @@ _nmtstp_assert_ip4_route_exists(const char *file, file, line, func, - _nm_utils_inet4_ntop(network, sbuf), + nm_inet4_ntop(network, sbuf), plen, metric, tos, @@ -683,8 +736,8 @@ _nmtstp_assert_ip6_route_exists(const char *file, r = _ip6_route_get(platform, ifindex, network, plen, metric, src, src_plen, &c); if (c != c_exists && c_exists != -1) { - char s_src[NM_UTILS_INET_ADDRSTRLEN]; - char s_network[NM_UTILS_INET_ADDRSTRLEN]; + char s_src[NM_INET_ADDRSTRLEN]; + char s_network[NM_INET_ADDRSTRLEN]; NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("[%s:%u] %s(): The ip6 route %s/%d metric %u src %s/%d shall exist %u times, but " @@ -692,10 +745,10 @@ _nmtstp_assert_ip6_route_exists(const char *file, file, line, func, - _nm_utils_inet6_ntop(network, s_network), + nm_inet6_ntop(network, s_network), plen, metric, - _nm_utils_inet6_ntop(src, s_src), + nm_inet6_ntop(src, s_src), src_plen, c_exists, c); @@ -739,6 +792,642 @@ nmtstp_run_command(const char *format, ...) /*****************************************************************************/ +static int +_assert_platform_sort_objs(gconstpointer ptr_a, gconstpointer ptr_b) +{ + const NMPObject *a = *((const NMPObject *const *) ptr_a); + const NMPObject *b = *((const NMPObject *const *) ptr_b); + + g_assert(NMP_OBJECT_IS_VALID(a)); + g_assert(NMP_OBJECT_IS_VALID(b)); + g_assert(NMP_OBJECT_GET_TYPE(a) == NMP_OBJECT_GET_TYPE(b)); + + NM_CMP_RETURN(nmp_object_id_cmp(a, b)); + g_assert_not_reached(); + return 0; +} + +static void +_assert_platform_printarr(NMPObjectType obj_type, GPtrArray *arr1, GPtrArray *arr2) +{ + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + guint i; + + _LOGT("compare arrays of %s. In cache %u entries, fetched %u entries", + NMP_OBJECT_TYPE_NAME(obj_type), + nm_g_ptr_array_len(arr1), + nm_g_ptr_array_len(arr2)); + + for (i = 0; i < nm_g_ptr_array_len(arr1); i++) { + _LOGT("cache[%u] %s", + i, + nmp_object_to_string(arr1->pdata[i], NMP_OBJECT_TO_STRING_ALL, sbuf, sizeof(sbuf))); + } + for (i = 0; i < nm_g_ptr_array_len(arr2); i++) { + _LOGT("fetch[%u] %s", + i, + nmp_object_to_string(arr2->pdata[i], NMP_OBJECT_TO_STRING_ALL, sbuf, sizeof(sbuf))); + } + + switch (obj_type) { + case NMP_OBJECT_TYPE_LINK: + nmtstp_run_command("ip -d link"); + break; + case NMP_OBJECT_TYPE_IP4_ADDRESS: + nmtstp_run_command("ip -d -4 address"); + break; + case NMP_OBJECT_TYPE_IP6_ADDRESS: + nmtstp_run_command("ip -d -6 address"); + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + nmtstp_run_command("ip -d -4 route show table all"); + break; + case NMP_OBJECT_TYPE_IP6_ROUTE: + nmtstp_run_command("ip -d -6 route show table all"); + break; + default: + g_assert_not_reached(); + break; + } +} + +static gboolean +_assert_platform_normalize_all(GPtrArray *arr) +{ + guint i; + gboolean normalized = FALSE; + + for (i = 0; i < nm_g_ptr_array_len(arr); i++) { + const NMPObject **ptr = (gpointer) &arr->pdata[i]; + nm_auto_nmpobj NMPObject *new = NULL; + gboolean skip = FALSE; + + switch (NMP_OBJECT_GET_TYPE(*ptr)) { + case NMP_OBJECT_TYPE_LINK: + { + const NMPlatformLink *link = NMP_OBJECT_CAST_LINK(*ptr); + + if (nmtstp_link_is_iptunnel_special(link)) { + /* These are special interfaces for the ip tunnel modules, like + * "gre0" created by the "ip_gre" module. + * + * These interfaces can appear at any moment, when the module + * gets loaded (by anybody on the host). We might want to avoid + * that by calling nmtstp_ensure_module(), but it's worse. + * Kernel does not send correct RTM_NEWLINK events when those + * interfaces get created. So the cache content based on the + * events will differ from a new load from a dump. + * + * We need to ignore those interfaces. */ + skip = TRUE; + } else if (link->type == NM_LINK_TYPE_UNKNOWN) { + /* The link type is not detected. This might be a generated + * interface like nmtstp_link_is_iptunnel_special(), but for + * kernel modules that we don't know about. Ignore them too. */ + skip = TRUE; + } + + if (!skip) { + new = nmp_object_clone(*ptr, FALSE); + new->link.rx_packets = 0; + new->link.rx_bytes = 0; + new->link.tx_packets = 0; + new->link.tx_bytes = 0; + } + if (nmp_object_ref_set(ptr, new)) + normalized = TRUE; + break; + } + default: + break; + } + } + + while (g_ptr_array_remove(arr, NULL)) { + /* Remove NULL values. */ + normalized = TRUE; + } + + return normalized; +} + +static gboolean +_assert_platform_compare_arr(NMPObjectType obj_type, + const char *detail_type, + GPtrArray *arr1, + GPtrArray *arr2, + gboolean normalized, + gboolean share_multi_idx, + gboolean do_assert) +{ + const NMPClass *obj_class = nmp_class_from_type(obj_type); + char sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; + char sbuf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; + int idx; + int idx_pointer_comp = -1; + +#define _fail_msg(do_assert, ...) \ + G_STMT_START \ + { \ + if (do_assert) { \ + g_error(__VA_ARGS__); \ + } else { \ + _LOGW(__VA_ARGS__); \ + return FALSE; \ + } \ + } \ + G_STMT_END + + for (idx = 0; TRUE; idx++) { + if (nm_g_ptr_array_len(arr1) == idx && nm_g_ptr_array_len(arr2) == idx) + break; + if (idx >= nm_g_ptr_array_len(arr1)) { + _assert_platform_printarr(obj_type, arr1, arr2); + _fail_msg(do_assert, + "Comparing %s (%s) for platform fails. Platform now shows entry #%u which is " + "not in the cache but expected %s", + obj_class->obj_type_name, + detail_type, + idx, + nmp_object_to_string(arr2->pdata[idx], + NMP_OBJECT_TO_STRING_ALL, + sbuf1, + sizeof(sbuf1))); + } + if (idx >= nm_g_ptr_array_len(arr2)) { + _assert_platform_printarr(obj_type, arr1, arr2); + _fail_msg( + do_assert, + "Comparing %s (%s) for platform fails. Platform has no more entry #%u which is " + "still in the cache as %s", + obj_class->obj_type_name, + detail_type, + idx, + nmp_object_to_string(arr1->pdata[idx], + NMP_OBJECT_TO_STRING_ALL, + sbuf1, + sizeof(sbuf1))); + } + if (!nmp_object_equal(arr1->pdata[idx], arr2->pdata[idx])) { + _assert_platform_printarr(obj_type, arr1, arr2); + _fail_msg(do_assert, + "Comparing %s (%s) for platform fails. Platform entry #%u is now %s but in " + "cache is %s", + obj_class->obj_type_name, + detail_type, + idx, + nmp_object_to_string(arr2->pdata[idx], + NMP_OBJECT_TO_STRING_ALL, + sbuf1, + sizeof(sbuf1)), + nmp_object_to_string(arr1->pdata[idx], + NMP_OBJECT_TO_STRING_ALL, + sbuf2, + sizeof(sbuf2))); + } + + if (!normalized && (share_multi_idx != (arr1->pdata[idx] == arr2->pdata[idx])) + && idx_pointer_comp == -1) + idx_pointer_comp = idx; + } + + if (idx_pointer_comp != -1) { + _assert_platform_printarr(obj_type, arr1, arr2); + _fail_msg(do_assert, + "Comparing %s (%s) for platform fails for pointer comparison. Platform entry " + "#%u is now %s but in cache is %s", + obj_class->obj_type_name, + detail_type, + idx_pointer_comp, + nmp_object_to_string(arr2->pdata[idx_pointer_comp], + NMP_OBJECT_TO_STRING_ALL, + sbuf1, + sizeof(sbuf1)), + nmp_object_to_string(arr1->pdata[idx_pointer_comp], + NMP_OBJECT_TO_STRING_ALL, + sbuf2, + sizeof(sbuf2))); + } + + return TRUE; +} + +/*****************************************************************************/ + +gboolean +nmtstp_link_is_iptunnel_special(const NMPlatformLink *link) +{ + int i; + + g_assert(link); + + /* These interfaces are autogenerated when loading the ip tunnel + * modules. For example, loading "ip_gre" results in interfaces + * "gre0", "gretap0", "erspan0". + * + * Actually, if the interface names are already taken ("gre0" already + * exists), it will create "gre1" and so on. We don't care about that, + * because in our test's netns that is not happening. */ + + for (i = 0; i < (int) G_N_ELEMENTS(ip_tunnel_mod_infos); i++) { + const IPTunnelModInfo *module_info = &ip_tunnel_mod_infos[i]; + + if (module_info->iftype != link->type) + continue; + if (!nm_streq(module_info->ifname, link->name)) + continue; + if (module_info->ifkind && !nm_streq(module_info->ifkind, link->kind)) + continue; + + return TRUE; + } + + return FALSE; +} + +/*****************************************************************************/ + +gboolean +nmtstp_ensure_module(const char *module_name) +{ + /* using iproute2 seems to fail sometimes? Force use of platform code. */ + const int EX = 0; + gs_free char *test_ifname = NULL; + const NMPlatformLink *link; + static int module_state[G_N_ELEMENTS(ip_tunnel_mod_infos)] = {0}; + int i_module_info; + const IPTunnelModInfo *module_info = NULL; + int i; + int ifindex; + gboolean result; + + if (!module_name) { + result = TRUE; + for (i = 0; i < (int) G_N_ELEMENTS(ip_tunnel_mod_infos); i++) { + if (!nmtstp_ensure_module(ip_tunnel_mod_infos[i].module_name)) + result = FALSE; + } + return result; + } + + for (i_module_info = 0; i_module_info < (int) G_N_ELEMENTS(ip_tunnel_mod_infos); + i_module_info++) { + if (nm_streq(module_name, ip_tunnel_mod_infos[i_module_info].module_name)) { + module_info = &ip_tunnel_mod_infos[i_module_info]; + break; + } + } + if (!module_info) + g_error("%s:%d: Module name \"%s\" not implemented!", __FILE__, __LINE__, module_name); + + test_ifname = g_strdup_printf("nm-mod-%s", module_info->ifname); + g_assert(nm_utils_ifname_valid_kernel(test_ifname, NULL)); + +again: + i = g_atomic_int_get(&module_state[i_module_info]); + if (i != 0) + return i > 0; + + /* When tunnel modules get loaded, then interfaces like "gre0", "gretap0" + * and "erspan0" (for "ip_gre" module) appear. For other modules, the interfaces + * are named differently. Of course, unless those interface name are already taken, + * in which case it will create "gre1", etc). So ugly. + * + * Anyway. as we run unit tests in parallel (`make check -j`), another + * test might just load the module just now, which results in the creation of + * those interfaces in our current namespace. That can break the test. + */ + + link = nmtstp_link_get_typed(NM_PLATFORM_GET, 0, test_ifname, module_info->iftype); + g_assert(!link); + + link = nmtstp_link_get_typed(NM_PLATFORM_GET, 0, module_info->ifname, module_info->iftype); + if (link) { + g_assert(nmtstp_link_is_iptunnel_special(link)); + /* An interface with this name exists. While technically this could not be the interface + * generated by the kernel module, in our test netns we can assume that it is. This is + * good enough. */ + result = TRUE; + goto out; + } + + /* Try to load the module. It probably won't work, because we don't have permissions. + * Ignore any failure. */ + nmp_utils_modprobe(NULL, TRUE, module_info->module_name, NULL); + + if (nm_streq(module_name, "ip_gre")) { + link = nmtstp_link_gre_add(NULL, + EX, + test_ifname, + &((const NMPlatformLnkGre){ + .local = nmtst_inet4_from_string("192.168.233.204"), + .remote = nmtst_inet4_from_string("172.168.10.25"), + .parent_ifindex = 0, + .ttl = 174, + .tos = 37, + .path_mtu_discovery = TRUE, + })); + } else if (nm_streq(module_name, "ipip")) { + link = nmtstp_link_ipip_add(NULL, + EX, + test_ifname, + &((const NMPlatformLnkIpIp){ + .local = nmtst_inet4_from_string("1.2.3.4"), + .remote = nmtst_inet4_from_string("5.6.7.8"), + .parent_ifindex = 0, + .tos = 32, + .path_mtu_discovery = FALSE, + })); + } else if (nm_streq(module_name, "ip6_tunnel")) { + link = nmtstp_link_ip6tnl_add(NULL, + EX, + test_ifname, + &((const NMPlatformLnkIp6Tnl){ + .local = nmtst_inet6_from_string("fd01::15"), + .remote = nmtst_inet6_from_string("fd01::16"), + .tclass = 20, + .encap_limit = 6, + .flow_label = 1337, + .proto = IPPROTO_IPV6, + })); + } else if (nm_streq(module_name, "ip6_gre")) { + link = nmtstp_link_ip6gre_add(NULL, + EX, + test_ifname, + &((const NMPlatformLnkIp6Tnl){ + .local = nmtst_inet6_from_string("fd01::42"), + .remote = nmtst_inet6_from_string("fd01::aaaa"), + .tclass = 21, + .flow_label = 1338, + .is_gre = TRUE, + })); + } else if (nm_streq(module_name, "sit")) { + link = nmtstp_link_sit_add(NULL, + EX, + test_ifname, + &((const NMPlatformLnkSit){ + .local = nmtst_inet4_from_string("192.168.200.1"), + .remote = nmtst_inet4_from_string("172.25.100.14"), + .ttl = 0, + .tos = 31, + .path_mtu_discovery = FALSE, + })); + } else if (nm_streq(module_name, "ip_vti")) { + link = nmtstp_link_vti_add(NULL, + EX, + test_ifname, + &((const NMPlatformLnkVti){ + .local = nmtst_inet4_from_string("192.168.212.204"), + .remote = nmtst_inet4_from_string("172.168.11.25"), + .ikey = 12, + .okey = 13, + })); + } else if (nm_streq(module_name, "ip6_vti")) { + link = nmtstp_link_vti6_add(NULL, + EX, + test_ifname, + &((const NMPlatformLnkVti6){ + .local = nmtst_inet6_from_string("fd01::1"), + .remote = nmtst_inet6_from_string("fd02::2"), + .ikey = 13, + .okey = 14, + })); + } else + g_error("%s:%d: Module name \"%s\" not implemented!", __FILE__, __LINE__, module_name); + + if (!link) { + /* We might be unable to add the interface, if the kernel module does not exist. + * Be graceful about that. */ + ifindex = 0; + g_assert(!nmtstp_link_get_typed(NM_PLATFORM_GET, 0, test_ifname, module_info->iftype)); + g_assert( + !nmtstp_link_get_typed(NM_PLATFORM_GET, 0, module_info->ifname, module_info->iftype)); + } else { + ifindex = link->ifindex; + + g_assert(link); + g_assert( + link + == nmtstp_link_get_typed(NM_PLATFORM_GET, ifindex, test_ifname, module_info->iftype)); + + nmtstp_link_delete(NULL, -1, link->ifindex, test_ifname, TRUE); + + link = nmtstp_link_get_typed(NM_PLATFORM_GET, 0, module_info->ifname, module_info->iftype); + g_assert(nmtstp_link_is_iptunnel_special(link)); + } + + result = ifindex > 0 ? 1 : -1; + +out: + if (!g_atomic_int_compare_and_exchange(&module_state[i_module_info], 0, result)) + goto again; + + if (!result) { + /* The function aims to be graceful about missing kernel modules. */ + + /* g_error("Failure to ensure module \"%s\"", module_name); */ + } + + return result; +} + +gboolean +nmtstp_check_platform_full(NMPlatform *platform, guint32 obj_type_flags, gboolean do_assert) +{ + static const NMPObjectType obj_types[] = { + NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE, + NMP_OBJECT_TYPE_LINK, + }; + gboolean obj_type_flags_all = (obj_type_flags == 0u); + gs_unref_object NMPlatform *platform2 = NULL; + int i_obj_types; + gboolean share_multi_idx = nmtst_get_rand_bool(); + + /* This test creates a new NMLinuxPlatform instance. This will fill + * the cache with a new dump. + * + * Then it compares the content with @platform and checks that they + * agree. This tests that @platform cache is consistent, as it was + * updated based on netlink events. */ + + g_assert(NM_IS_LINUX_PLATFORM(platform)); + + _LOGD("assert-platform: start"); + + nm_platform_process_events(platform); + + platform2 = nm_linux_platform_new(share_multi_idx ? nm_platform_get_multi_idx(platform) : NULL, + TRUE, + nmtst_get_rand_bool(), + nmtst_get_rand_bool()); + g_assert(NM_IS_LINUX_PLATFORM(platform2)); + + for (i_obj_types = 0; i_obj_types < (int) G_N_ELEMENTS(obj_types); i_obj_types++) { + const NMPObjectType obj_type = obj_types[i_obj_types]; + const guint32 i_obj_type_flags = nmp_object_type_to_flags(obj_type); + gs_unref_ptrarray GPtrArray *arr1 = NULL; + gs_unref_ptrarray GPtrArray *arr2 = NULL; + NMPLookup lookup; + gboolean check_unordered = TRUE; + guint idx; + gboolean normalized; + + if (!obj_type_flags_all) { + if (!NM_FLAGS_ANY(obj_type_flags, i_obj_type_flags)) + continue; + obj_type_flags = NM_FLAGS_UNSET(obj_type_flags, i_obj_type_flags); + } + + nmp_lookup_init_obj_type(&lookup, obj_type); + + arr1 = nm_platform_lookup_clone(platform, &lookup, NULL, NULL) ?: g_ptr_array_new(); + arr2 = nm_platform_lookup_clone(platform2, &lookup, NULL, NULL) ?: g_ptr_array_new(); + + normalized = _assert_platform_normalize_all(arr1); + normalized = _assert_platform_normalize_all(arr2); + + if (check_unordered) { + /* We need to sort the two lists. */ + g_ptr_array_sort(arr1, _assert_platform_sort_objs); + g_ptr_array_sort(arr2, _assert_platform_sort_objs); + } + + if (!_assert_platform_compare_arr(obj_type, + "main", + arr1, + arr2, + normalized, + share_multi_idx, + do_assert)) + return FALSE; + + if (NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)) { + /* For routes, the WEAK_ID needs to be sorted and match the expected order. Check that. */ + g_assert(!normalized); + for (idx = 0; idx < nm_g_ptr_array_len(arr1); idx++) { + const NMPObject *obj1 = arr1->pdata[idx]; + const NMPObject *obj2 = arr2->pdata[idx]; + gs_unref_ptrarray GPtrArray *arr1b = NULL; + gs_unref_ptrarray GPtrArray *arr2b = NULL; + gs_unref_ptrarray GPtrArray *arr1b_sorted = NULL; + gs_unref_ptrarray GPtrArray *arr2b_sorted = NULL; + guint found_obj1 = 0; + guint found_obj2 = 0; + guint i; + + nmp_lookup_init_route_by_weak_id(&lookup, obj1); + arr1b = + nm_platform_lookup_clone(platform, &lookup, NULL, NULL) ?: g_ptr_array_new(); + g_assert_cmpint(arr1b->len, >, 0u); + + nmp_lookup_init_route_by_weak_id(&lookup, obj2); + arr2b = + nm_platform_lookup_clone(platform2, &lookup, NULL, NULL) ?: g_ptr_array_new(); + g_assert_cmpint(arr2b->len, ==, arr1b->len); + + /* First check that the lists agree, if we sort them. The list of + * weak-ids was supposed to honor the sort order from `ip route show`, + * but as that is not the case (see blow), first check whether at + * least the same routes are in the list (with wrong sort order). */ + arr1b_sorted = nm_g_ptr_array_new_clone(arr1b, NULL, NULL, NULL); + arr2b_sorted = nm_g_ptr_array_new_clone(arr2b, NULL, NULL, NULL); + g_ptr_array_sort(arr1b_sorted, _assert_platform_sort_objs); + g_ptr_array_sort(arr2b_sorted, _assert_platform_sort_objs); + if (!_assert_platform_compare_arr(obj_type, + "weak-id-sorted", + arr1b_sorted, + arr2b_sorted, + normalized, + share_multi_idx, + do_assert)) + return FALSE; + + if (obj_type == NMP_OBJECT_TYPE_IP6_ROUTE) { + /* For IPv6, the weak-ids are actually not sorted correctly. + * This is because IPv6 multihop/ECMP routes get split into + * multiple objects, and we don't get this right. + * + * This may be a bug. But we probably don't rely on this + * anymore, because the weak-id were used to find which + * route got replaced with `NLM_F_REPLACE`, but that anyway + * doesn't work. We now always request a new dump. */ + } else if (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) { + /* For IPv4, it also does not reliably always work. This may + * be a bug we want to fix. For now, ignore the check. + * + * a) Kernel can wrongly allow to configure the same route twice. + * That means, the same route is visible in `ip route` output, + * meaning, it would be added twice to the platform cache. + * At least due to that problem, may the weak-id not be properly sorted. + * See https://bugzilla.redhat.com/show_bug.cgi?id=2165720 which is + * a bug of kernel allowing to configure the exact same route twice. + * + * b) See https://bugzilla.redhat.com/show_bug.cgi?id=2162315 which is + * a bug where kernel does allow to configure single-hop routes that differ by + * their next-hop weight, but on the netlink API those routes look the same. + * + * Due to a) and b), the platform cache may contain only one instance + * of a route, which is visible more than once in `ip route` output. + * This merging of different routes causes problems, and it also means + * that the RTM_NEWROUTE events are wrongly interpreted and the weak-id + * is not properly sorted. + */ + } else { + /* Assert that also the original, not-sorted lists agree. */ + if (!_assert_platform_compare_arr(obj_type, + "weak-id", + arr1b, + arr2b, + normalized, + share_multi_idx, + do_assert)) + return FALSE; + } + + for (i = 0; i < arr1b->len; i++) { + if (arr1b->pdata[i] == obj1) + found_obj1++; + if (arr2b->pdata[i] == obj2) + found_obj2++; + } + + g_assert_cmpint(found_obj1, ==, 1u); + g_assert_cmpint(found_obj2, ==, 1u); + } + } + } + + g_clear_object(&platform2); + + _LOGD("assert-platform: done"); + + g_assert_cmpint(obj_type_flags, ==, 0u); + + return TRUE; +} + +void +nmtstp_check_platform(NMPlatform *platform, guint32 obj_type_flags) +{ + if (!nmtstp_check_platform_full(platform, obj_type_flags, FALSE)) { + /* It's unclear why this failure sometimes happens. It happens + * on gitlab-ci on Ubuntu/Debian(??). + * + * Retrying shortly after seems to avoid it. */ + g_usleep(20 * 1000); + nm_platform_process_events(platform); + nmtstp_run_command("ip route"); + nm_platform_process_events(platform); + + nmtstp_check_platform_full(platform, obj_type_flags, TRUE); + } +} + +/*****************************************************************************/ + typedef struct { GMainLoop *loop; guint signal_counts; @@ -1036,7 +1725,7 @@ nmtstp_ip_address_assert_lifetime(const NMPlatformIPAddress *addr, static void _ip_address_add(NMPlatform *platform, - gboolean external_command, + int external_command, gboolean is_v4, int ifindex, const NMIPAddr *address, @@ -1058,8 +1747,8 @@ _ip_address_add(NMPlatform *platform, gs_free char *s_valid = NULL; gs_free char *s_preferred = NULL; gs_free char *s_label = NULL; - char b1[NM_UTILS_INET_ADDRSTRLEN]; - char b2[NM_UTILS_INET_ADDRSTRLEN]; + char b1[NM_INET_ADDRSTRLEN]; + char b2[NM_INET_ADDRSTRLEN]; ifname = nm_platform_link_get_name(platform, ifindex); g_assert(ifname); @@ -1072,18 +1761,18 @@ _ip_address_add(NMPlatform *platform, s_label = g_strdup_printf("%s:%s", ifname, label); if (is_v4) { - char s_peer[NM_UTILS_INET_ADDRSTRLEN + 50]; + char s_peer[NM_INET_ADDRSTRLEN + 50]; g_assert(flags == 0); if (peer_address->addr4 != address->addr4 || nmtst_get_rand_uint32() % 2) { /* If the peer is the same as the local address, we can omit it. The result should be identical */ - nm_sprintf_buf(s_peer, " peer %s", _nm_utils_inet4_ntop(peer_address->addr4, b2)); + nm_sprintf_buf(s_peer, " peer %s", nm_inet4_ntop(peer_address->addr4, b2)); } else s_peer[0] = '\0'; nmtstp_run_command_check("ip address change %s%s/%d dev %s%s%s%s", - _nm_utils_inet4_ntop(address->addr4, b1), + nm_inet4_ntop(address->addr4, b1), s_peer, plen, ifname, @@ -1096,10 +1785,10 @@ _ip_address_add(NMPlatform *platform, /* flags not implemented (yet) */ g_assert(flags == 0); nmtstp_run_command_check("ip address change %s%s%s/%d dev %s%s%s%s", - _nm_utils_inet6_ntop(&address->addr6, b1), + nm_inet6_ntop(&address->addr6, b1), !IN6_IS_ADDR_UNSPECIFIED(&peer_address->addr6) ? " peer " : "", !IN6_IS_ADDR_UNSPECIFIED(&peer_address->addr6) - ? _nm_utils_inet6_ntop(&peer_address->addr6, b2) + ? nm_inet6_ntop(&peer_address->addr6, b2) : "", plen, ifname, @@ -1188,7 +1877,7 @@ _ip_address_add(NMPlatform *platform, void nmtstp_ip4_address_add(NMPlatform *platform, - gboolean external_command, + int external_command, int ifindex, in_addr_t address, int plen, @@ -1213,7 +1902,7 @@ nmtstp_ip4_address_add(NMPlatform *platform, void nmtstp_ip6_address_add(NMPlatform *platform, - gboolean external_command, + int external_command, int ifindex, struct in6_addr address, int plen, @@ -1257,8 +1946,8 @@ nmtstp_ip4_route_add(NMPlatform *platform, route.metric = metric; route.mss = mss; - g_assert( - NMTST_NM_ERR_SUCCESS(nm_platform_ip4_route_add(platform, NMP_NLM_FLAG_REPLACE, &route))); + g_assert(NMTST_NM_ERR_SUCCESS( + nm_platform_ip4_route_add(platform, NMP_NLM_FLAG_REPLACE, &route, NULL))); } void @@ -1291,7 +1980,7 @@ nmtstp_ip6_route_add(NMPlatform *platform, static void _ip_address_del(NMPlatform *platform, - gboolean external_command, + int external_command, gboolean is_v4, int ifindex, const NMIPAddr *address, @@ -1306,8 +1995,8 @@ _ip_address_del(NMPlatform *platform, if (external_command) { const char *ifname; - char b1[NM_UTILS_INET_ADDRSTRLEN]; - char b2[NM_UTILS_INET_ADDRSTRLEN]; + char b1[NM_INET_ADDRSTRLEN]; + char b2[NM_INET_ADDRSTRLEN]; int success; gboolean had_address; @@ -1325,18 +2014,17 @@ _ip_address_del(NMPlatform *platform, had_address = !!nm_platform_ip6_address_get(platform, ifindex, &address->addr6); if (is_v4) { - success = nmtstp_run_command("ip address delete %s%s%s/%d dev %s", - _nm_utils_inet4_ntop(address->addr4, b1), - peer_address->addr4 != address->addr4 ? " peer " : "", - peer_address->addr4 != address->addr4 - ? _nm_utils_inet4_ntop(peer_address->addr4, b2) - : "", - plen, - ifname); + success = nmtstp_run_command( + "ip address delete %s%s%s/%d dev %s", + nm_inet4_ntop(address->addr4, b1), + peer_address->addr4 != address->addr4 ? " peer " : "", + peer_address->addr4 != address->addr4 ? nm_inet4_ntop(peer_address->addr4, b2) : "", + plen, + ifname); } else { g_assert(!peer_address); success = nmtstp_run_command("ip address delete %s/%d dev %s", - _nm_utils_inet6_ntop(&address->addr6, b1), + nm_inet6_ntop(&address->addr6, b1), plen, ifname); } @@ -1391,7 +2079,7 @@ _ip_address_del(NMPlatform *platform, void nmtstp_ip4_address_del(NMPlatform *platform, - gboolean external_command, + int external_command, int ifindex, in_addr_t address, int plen, @@ -1408,7 +2096,7 @@ nmtstp_ip4_address_del(NMPlatform *platform, void nmtstp_ip6_address_del(NMPlatform *platform, - gboolean external_command, + int external_command, int ifindex, struct in6_addr address, int plen) @@ -1478,7 +2166,7 @@ nmtstp_link_bridge_normalize_jiffies_time(const NMPlatformLnkBridge *requested, const NMPlatformLink * nmtstp_link_bridge_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkBridge *lnk) { @@ -1633,10 +2321,7 @@ nmtstp_link_bridge_add(NMPlatform *platform, return pllink; } const NMPlatformLink * -nmtstp_link_veth_add(NMPlatform *platform, - gboolean external_command, - const char *name, - const char *peer) +nmtstp_link_veth_add(NMPlatform *platform, int external_command, const char *name, const char *peer) { const NMPlatformLink *pllink = NULL; gboolean success = FALSE; @@ -1681,7 +2366,7 @@ again: } const NMPlatformLink * -nmtstp_link_dummy_add(NMPlatform *platform, gboolean external_command, const char *name) +nmtstp_link_dummy_add(NMPlatform *platform, int external_command, const char *name) { const NMPlatformLink *pllink = NULL; gboolean success; @@ -1706,7 +2391,7 @@ nmtstp_link_dummy_add(NMPlatform *platform, gboolean external_command, const cha const NMPlatformLink * nmtstp_link_gre_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkGre *lnk) { @@ -1734,13 +2419,13 @@ nmtstp_link_gre_add(NMPlatform *platform, obj = lnk->is_tap ? "link" : "tunnel"; type = lnk->is_tap ? "type gretap" : "mode gre"; - success = !nmtstp_run_command("ip %s add %s %s %s local %s remote %s ttl %u tos %02x %s", + success = !nmtstp_run_command("ip %s add %s %s%s%s local %s remote %s ttl %u tos %02x %s", obj, name, type, - dev ?: "", - _nm_utils_inet4_ntop(lnk->local, b1), - _nm_utils_inet4_ntop(lnk->remote, b2), + NM_PRINT_FMT_QUOTED2(dev, " ", dev, ""), + nm_inet4_ntop(lnk->local, b1), + nm_inet4_ntop(lnk->remote, b2), lnk->ttl, lnk->tos, lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); @@ -1757,14 +2442,14 @@ nmtstp_link_gre_add(NMPlatform *platform, const NMPlatformLink * nmtstp_link_ip6tnl_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkIp6Tnl *lnk) { const NMPlatformLink *pllink = NULL; gboolean success; - char b1[NM_UTILS_INET_ADDRSTRLEN]; - char b2[NM_UTILS_INET_ADDRSTRLEN]; + char b1[NM_INET_ADDRSTRLEN]; + char b2[NM_INET_ADDRSTRLEN]; char encap[20]; char tclass[20]; gboolean encap_ignore; @@ -1800,13 +2485,13 @@ nmtstp_link_ip6tnl_add(NMPlatform *platform, tclass_inherit = NM_FLAGS_HAS(lnk->flags, IP6_TNL_F_USE_ORIG_TCLASS); success = !nmtstp_run_command( - "ip -6 tunnel add %s mode %s %s local %s remote %s ttl %u tclass %s encaplimit %s " + "ip -6 tunnel add %s mode %s%s%s local %s remote %s ttl %u tclass %s encaplimit %s " "flowlabel %x", name, mode, - dev, - _nm_utils_inet6_ntop(&lnk->local, b1), - _nm_utils_inet6_ntop(&lnk->remote, b2), + NM_PRINT_FMT_QUOTED2(dev, " ", dev, ""), + nm_inet6_ntop(&lnk->local, b1), + nm_inet6_ntop(&lnk->remote, b2), lnk->ttl, tclass_inherit ? "inherit" : nm_sprintf_buf(tclass, "%02x", lnk->tclass), encap_ignore ? "none" : nm_sprintf_buf(encap, "%u", lnk->encap_limit), @@ -1823,14 +2508,14 @@ nmtstp_link_ip6tnl_add(NMPlatform *platform, const NMPlatformLink * nmtstp_link_ip6gre_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkIp6Tnl *lnk) { const NMPlatformLink *pllink = NULL; gboolean success; - char b1[NM_UTILS_INET_ADDRSTRLEN]; - char b2[NM_UTILS_INET_ADDRSTRLEN]; + char b1[NM_INET_ADDRSTRLEN]; + char b2[NM_INET_ADDRSTRLEN]; char tclass[20]; gboolean tclass_inherit; @@ -1851,12 +2536,12 @@ nmtstp_link_ip6gre_add(NMPlatform *platform, tclass_inherit = NM_FLAGS_HAS(lnk->flags, IP6_TNL_F_USE_ORIG_TCLASS); success = !nmtstp_run_command( - "ip link add %s type %s %s local %s remote %s ttl %u tclass %s flowlabel %x", + "ip link add %s type %s%s%s local %s remote %s ttl %u tclass %s flowlabel %x", name, lnk->is_tap ? "ip6gretap" : "ip6gre", - dev, - _nm_utils_inet6_ntop(&lnk->local, b1), - _nm_utils_inet6_ntop(&lnk->remote, b2), + NM_PRINT_FMT_QUOTED2(dev, " ", dev, ""), + nm_inet6_ntop(&lnk->local, b1), + nm_inet6_ntop(&lnk->remote, b2), lnk->ttl, tclass_inherit ? "inherit" : nm_sprintf_buf(tclass, "%02x", lnk->tclass), lnk->flow_label); @@ -1882,7 +2567,7 @@ nmtstp_link_ip6gre_add(NMPlatform *platform, const NMPlatformLink * nmtstp_link_ipip_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkIpIp *lnk) { @@ -1905,11 +2590,11 @@ nmtstp_link_ipip_add(NMPlatform *platform, g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex)); success = !nmtstp_run_command( - "ip tunnel add %s mode ipip %s local %s remote %s ttl %u tos %02x %s", + "ip tunnel add %s mode ipip%s%s local %s remote %s ttl %u tos %02x %s", name, - dev, - _nm_utils_inet4_ntop(lnk->local, b1), - _nm_utils_inet4_ntop(lnk->remote, b2), + NM_PRINT_FMT_QUOTED2(dev, " ", dev, ""), + nm_inet4_ntop(lnk->local, b1), + nm_inet4_ntop(lnk->remote, b2), lnk->ttl, lnk->tos, lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); @@ -1925,7 +2610,7 @@ nmtstp_link_ipip_add(NMPlatform *platform, const NMPlatformLink * nmtstp_link_macvlan_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, int parent, const NMPlatformLnkMacvlan *lnk) @@ -1945,10 +2630,10 @@ nmtstp_link_macvlan_add(NMPlatform *platform, if (external_command) { const char *dev; char *modes[] = { - [MACVLAN_MODE_BRIDGE] = "bridge", - [MACVLAN_MODE_VEPA] = "vepa", - [MACVLAN_MODE_PRIVATE] = "private", - [MACVLAN_MODE_PASSTHRU] = "passthru", + [MACVLAN_MODE_BRIDGE] = "bridge", + [MACVLAN_MODE_VEPA] = "vepa", + [MACVLAN_MODE_PRIVATE] = "private", + [MACVLAN_MODE_PASSTHRU] = "passthru", }; dev = nm_platform_link_get_name(platform, parent); @@ -1974,7 +2659,7 @@ nmtstp_link_macvlan_add(NMPlatform *platform, const NMPlatformLink * nmtstp_link_sit_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkSit *lnk) { @@ -2004,8 +2689,8 @@ nmtstp_link_sit_add(NMPlatform *platform, !nmtstp_run_command("ip tunnel add %s mode sit%s local %s remote %s ttl %u tos %02x %s", name, dev, - _nm_utils_inet4_ntop(lnk->local, b1), - _nm_utils_inet4_ntop(lnk->remote, b2), + nm_inet4_ntop(lnk->local, b1), + nm_inet4_ntop(lnk->remote, b2), lnk->ttl, lnk->tos, lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); @@ -2021,7 +2706,7 @@ nmtstp_link_sit_add(NMPlatform *platform, const NMPlatformLink * nmtstp_link_tun_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkTun *lnk, int *out_fd) @@ -2085,9 +2770,135 @@ nmtstp_link_tun_add(NMPlatform *platform, } const NMPlatformLink * -nmtstp_link_vrf_add(NMPlatform *platform, +nmtstp_link_vlan_add(NMPlatform *platform, + int external_command, + const char *name, + int parent, + const NMPlatformLnkVlan *lnk) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + + g_assert(nm_utils_ifname_valid_kernel(name, NULL)); + + external_command = nmtstp_run_command_check_external(external_command); + + _init_platform(&platform, external_command); + + if (external_command) { + const char *dev; + + dev = nm_platform_link_get_name(platform, parent); + g_assert(dev); + g_assert(NM_IN_SET(lnk->protocol, ETH_P_8021Q, ETH_P_8021AD)); + + success = !nmtstp_run_command( + "ip link add name %s link %s type vlan id %hu protocol %s%s%s%s%s", + name, + dev, + lnk->id, + lnk->protocol == ETH_P_8021Q ? "802.1Q" : "802.1ad", + !(lnk->flags & _NM_VLAN_FLAG_REORDER_HEADERS) ? " reorder_hdr off" : "", + (lnk->flags & _NM_VLAN_FLAG_GVRP) ? " gvrp on" : "", + (lnk->flags & _NM_VLAN_FLAG_MVRP) ? " mvrp on" : "", + (lnk->flags & _NM_VLAN_FLAG_LOOSE_BINDING) ? " loose_binding on" : ""); + if (success) + pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_VLAN, 100); + } else + success = + NMTST_NM_ERR_SUCCESS(nm_platform_link_vlan_add(platform, name, parent, lnk, &pllink)); + + _assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_VLAN); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_vti_add(NMPlatform *platform, gboolean external_command, const char *name, + const NMPlatformLnkVti *lnk) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + char b1[INET_ADDRSTRLEN]; + char b2[INET_ADDRSTRLEN]; + + g_assert(nm_utils_ifname_valid_kernel(name, NULL)); + external_command = nmtstp_run_command_check_external(external_command); + _init_platform(&platform, external_command); + g_assert(lnk->fwmark == 0); + + if (external_command) { + gs_free char *dev = NULL; + + if (lnk->parent_ifindex) + dev = + g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex)); + + success = + !nmtstp_run_command("ip link add %s type vti %s local %s remote %s ikey %u okey %u", + name, + dev ?: "", + nm_inet4_ntop(lnk->local, b1), + nm_inet4_ntop(lnk->remote, b2), + lnk->ikey, + lnk->okey); + if (success) + pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_VTI, 100); + } else + success = NMTST_NM_ERR_SUCCESS(nm_platform_link_vti_add(platform, name, lnk, &pllink)); + + _assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_VTI); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_vti6_add(NMPlatform *platform, + gboolean external_command, + const char *name, + const NMPlatformLnkVti6 *lnk) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + char b1[INET6_ADDRSTRLEN]; + char b2[INET6_ADDRSTRLEN]; + + g_assert(nm_utils_ifname_valid_kernel(name, NULL)); + external_command = nmtstp_run_command_check_external(external_command); + _init_platform(&platform, external_command); + g_assert(lnk->fwmark == 0); + + if (external_command) { + gs_free char *dev = NULL; + + if (lnk->parent_ifindex) + dev = + g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex)); + + success = + !nmtstp_run_command("ip link add %s type vti6 %s local %s remote %s ikey %u okey %u", + name, + dev ?: "", + nm_inet6_ntop(&lnk->local, b1), + nm_inet6_ntop(&lnk->remote, b2), + lnk->ikey, + lnk->okey); + if (success) + pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_VTI6, 100); + } else + success = NMTST_NM_ERR_SUCCESS(nm_platform_link_vti6_add(platform, name, lnk, &pllink)); + + _assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_VTI6); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_vrf_add(NMPlatform *platform, + int external_command, + const char *name, const NMPlatformLnkVrf *lnk, gboolean *out_not_supported) { @@ -2123,7 +2934,7 @@ nmtstp_link_vrf_add(NMPlatform *platform, const NMPlatformLink * nmtstp_link_vxlan_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkVxlan *lnk) { @@ -2139,24 +2950,24 @@ nmtstp_link_vxlan_add(NMPlatform *platform, if (external_command) { gs_free char *dev = NULL; - char local[NM_UTILS_INET_ADDRSTRLEN]; - char group[NM_UTILS_INET_ADDRSTRLEN]; + char local[NM_INET_ADDRSTRLEN]; + char group[NM_INET_ADDRSTRLEN]; if (lnk->parent_ifindex) dev = g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex)); if (lnk->local) - _nm_utils_inet4_ntop(lnk->local, local); + nm_inet4_ntop(lnk->local, local); else if (memcmp(&lnk->local6, &in6addr_any, sizeof(in6addr_any))) - _nm_utils_inet6_ntop(&lnk->local6, local); + nm_inet6_ntop(&lnk->local6, local); else local[0] = '\0'; if (lnk->group) - _nm_utils_inet4_ntop(lnk->group, group); + nm_inet4_ntop(lnk->group, group); else if (memcmp(&lnk->group6, &in6addr_any, sizeof(in6addr_any))) - _nm_utils_inet6_ntop(&lnk->group6, group); + nm_inet6_ntop(&lnk->group6, group); else group[0] = '\0'; @@ -2239,7 +3050,7 @@ nmtstp_link_get(NMPlatform *platform, int ifindex, const char *name) void nmtstp_link_delete(NMPlatform *platform, - gboolean external_command, + int external_command, int ifindex, const char *name, gboolean require_exist) @@ -2291,7 +3102,7 @@ nmtstp_link_delete(NMPlatform *platform, /*****************************************************************************/ void -nmtstp_link_set_updown(NMPlatform *platform, gboolean external_command, int ifindex, gboolean up) +nmtstp_link_set_updown(NMPlatform *platform, int external_command, int ifindex, gboolean up) { const NMPlatformLink *plink; gint64 end_time; @@ -2739,7 +3550,7 @@ nmtstp_acd_defender_new(int ifindex, in_addr_t ip_addr, const NMEtherAddr *mac_a NAcdProbe *probe = NULL; int fd; int r; - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; g_assert_cmpint(ifindex, >, 0); g_assert(mac_addr); @@ -2780,7 +3591,7 @@ nmtstp_acd_defender_new(int ifindex, in_addr_t ip_addr, const NMEtherAddr *mac_a NM_HASH_OBFUSCATE_PTR(defender), ifindex, NM_ETHER_ADDR_FORMAT_VAL(mac_addr), - _nm_utils_inet4_ntop(ip_addr, sbuf_addr)); + nm_inet4_ntop(ip_addr, sbuf_addr)); n_acd_probe_set_userdata(defender->probe, defender); diff --git a/src/core/platform/tests/test-common.h b/src/core/platform/tests/test-common.h index da30b0ed..2802d8fd 100644 --- a/src/core/platform/tests/test-common.h +++ b/src/core/platform/tests/test-common.h @@ -18,8 +18,6 @@ #include "nm-test-utils-core.h" -#define DEVICE_NAME "nm-test-device" - /*****************************************************************************/ #define nmtstp_normalize_jiffies_time(requested_value, kernel_value) \ @@ -141,6 +139,13 @@ int nmtstp_run_command(const char *format, ...) _nm_printf(1, 2); /*****************************************************************************/ +gboolean +nmtstp_check_platform_full(NMPlatform *platform, guint32 obj_type_flags, gboolean do_assert); + +void nmtstp_check_platform(NMPlatform *platform, guint32 obj_type_flags); + +/*****************************************************************************/ + guint nmtstp_wait_for_signal(NMPlatform *platform, gint64 timeout_msec); guint nmtstp_wait_for_signal_until(NMPlatform *platform, gint64 until_ms); const NMPlatformLink *nmtstp_wait_for_link(NMPlatform *platform, @@ -261,7 +266,7 @@ void nmtstp_ip_address_assert_lifetime(const NMPlatformIPAddress *addr, guint32 expected_preferred); void nmtstp_ip4_address_add(NMPlatform *platform, - gboolean external_command, + int external_command, int ifindex, in_addr_t address, int plen, @@ -271,7 +276,7 @@ void nmtstp_ip4_address_add(NMPlatform *platform, guint32 flags, const char *label); void nmtstp_ip6_address_add(NMPlatform *platform, - gboolean external_command, + int external_command, int ifindex, struct in6_addr address, int plen, @@ -280,13 +285,13 @@ void nmtstp_ip6_address_add(NMPlatform *platform, guint32 preferred, guint32 flags); void nmtstp_ip4_address_del(NMPlatform *platform, - gboolean external_command, + int external_command, int ifindex, in_addr_t address, int plen, in_addr_t peer_address); void nmtstp_ip6_address_del(NMPlatform *platform, - gboolean external_command, + int external_command, int ifindex, struct in6_addr address, int plen); @@ -452,8 +457,7 @@ const NMPlatformLink *nmtstp_link_get(NMPlatform *platform, int ifindex, const c gboolean nmtstp_kernel_support_get(NMPlatformKernelSupportType type); -void -nmtstp_link_set_updown(NMPlatform *platform, gboolean external_command, int ifindex, gboolean up); +void nmtstp_link_set_updown(NMPlatform *platform, int external_command, int ifindex, gboolean up); const NMPlatformLnkBridge * nmtstp_link_bridge_normalize_jiffies_time(const NMPlatformLnkBridge *requested, @@ -461,61 +465,78 @@ nmtstp_link_bridge_normalize_jiffies_time(const NMPlatformLnkBridge *requested, NMPlatformLnkBridge *dst); const NMPlatformLink *nmtstp_link_bridge_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkBridge *lnk); const NMPlatformLink *nmtstp_link_veth_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const char *peer); const NMPlatformLink * -nmtstp_link_dummy_add(NMPlatform *platform, gboolean external_command, const char *name); +nmtstp_link_dummy_add(NMPlatform *platform, int external_command, const char *name); const NMPlatformLink *nmtstp_link_gre_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkGre *lnk); const NMPlatformLink *nmtstp_link_ip6tnl_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkIp6Tnl *lnk); const NMPlatformLink *nmtstp_link_ip6gre_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkIp6Tnl *lnk); const NMPlatformLink *nmtstp_link_ipip_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkIpIp *lnk); const NMPlatformLink *nmtstp_link_macvlan_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, int parent, const NMPlatformLnkMacvlan *lnk); const NMPlatformLink *nmtstp_link_sit_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkSit *lnk); const NMPlatformLink *nmtstp_link_tun_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkTun *lnk, int *out_fd); -const NMPlatformLink *nmtstp_link_vrf_add(NMPlatform *platform, +const NMPlatformLink *nmtstp_link_vlan_add(NMPlatform *platform, + int external_command, + const char *name, + int parent, + const NMPlatformLnkVlan *lnk); +const NMPlatformLink *nmtstp_link_vti_add(NMPlatform *platform, gboolean external_command, const char *name, + const NMPlatformLnkVti *lnk); +const NMPlatformLink *nmtstp_link_vti6_add(NMPlatform *platform, + gboolean external_command, + const char *name, + const NMPlatformLnkVti6 *lnk); +const NMPlatformLink *nmtstp_link_vrf_add(NMPlatform *platform, + int external_command, + const char *name, const NMPlatformLnkVrf *lnk, gboolean *out_not_supported); const NMPlatformLink *nmtstp_link_vxlan_add(NMPlatform *platform, - gboolean external_command, + int external_command, const char *name, const NMPlatformLnkVxlan *lnk); void nmtstp_link_delete(NMPlatform *platform, - gboolean external_command, + int external_command, int ifindex, const char *name, gboolean require_exist); +gboolean nmtstp_link_is_iptunnel_special(const NMPlatformLink *link); + +gboolean nmtstp_ensure_module(const char *module_name); + /*****************************************************************************/ #define nmtst_object_new_mptcp_addr(...) \ @@ -523,36 +544,47 @@ void nmtstp_link_delete(NMPlatform *platform, /*****************************************************************************/ -extern int NMTSTP_ENV1_IFINDEX; +extern int NMTSTP_ENV1_IFINDEXES[2]; +extern const char *const NMTSTP_ENV1_DEVICE_NAME[G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES)]; + +#define DEVICE_NAME "nm-test-device0" +#define NMTSTP_ENV1_IFINDEX (NMTSTP_ENV1_IFINDEXES[0]) + extern int NMTSTP_ENV1_EX; static inline void _nmtstp_env1_wrapper_setup(const NmtstTestData *test_data) { - int *p_ifindex; + int *p_ifindexes; + gpointer p_n_ifaces; gpointer p_ifup; - - nmtst_test_data_unpack(test_data, &p_ifindex, NULL, NULL, NULL, &p_ifup); - - g_assert(p_ifindex && *p_ifindex == -1); + int n_ifaces; + int i; _LOGT("TEST[%s]: setup", test_data->testpath); - nmtstp_link_delete(NM_PLATFORM_GET, -1, -1, DEVICE_NAME, FALSE); + nmtst_test_data_unpack(test_data, &p_ifindexes, &p_n_ifaces, NULL, NULL, NULL, &p_ifup); - g_assert(NMTST_NM_ERR_SUCCESS(nm_platform_link_dummy_add(NM_PLATFORM_GET, DEVICE_NAME, NULL))); + n_ifaces = GPOINTER_TO_UINT(p_n_ifaces); + g_assert_cmpint(n_ifaces, >=, 1); + g_assert_cmpint(n_ifaces, <=, (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES)); - *p_ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - g_assert_cmpint(*p_ifindex, >, 0); - g_assert_cmpint(NMTSTP_ENV1_IFINDEX, ==, -1); + for (i = 0; i < (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES); i++) { + g_assert_cmpint(NMTSTP_ENV1_IFINDEXES[i], ==, 0); + g_assert_cmpint(p_ifindexes[i], ==, 0); + } - if (GPOINTER_TO_INT(p_ifup)) - g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, *p_ifindex, IFF_UP, TRUE) >= 0); + for (i = 0; i < n_ifaces; i++) { + p_ifindexes[i] = nmtstp_link_dummy_add(NULL, -1, NMTSTP_ENV1_DEVICE_NAME[i])->ifindex; + if (GPOINTER_TO_INT(p_ifup)) + nmtstp_link_set_updown(NULL, -1, p_ifindexes[i], TRUE); + } nm_platform_process_events(NM_PLATFORM_GET); - NMTSTP_ENV1_IFINDEX = *p_ifindex; - NMTSTP_ENV1_EX = nmtstp_run_command_check_external_global(); + for (i = 0; i < n_ifaces; i++) + NMTSTP_ENV1_IFINDEXES[i] = p_ifindexes[i]; + NMTSTP_ENV1_EX = nmtstp_run_command_check_external_global(); } static inline void @@ -563,7 +595,7 @@ _nmtstp_env1_wrapper_run(gconstpointer user_data) GTestFunc test_func; gconstpointer d; - nmtst_test_data_unpack(test_data, NULL, &test_func, &test_func_data, &d, NULL); + nmtst_test_data_unpack(test_data, NULL, NULL, &test_func, &test_func_data, &d, NULL); _LOGT("TEST[%s]: run", test_data->testpath); if (test_func) @@ -575,55 +607,73 @@ _nmtstp_env1_wrapper_run(gconstpointer user_data) static inline void _nmtstp_env1_wrapper_teardown(const NmtstTestData *test_data) { - int *p_ifindex; + int *p_ifindexes; + gpointer p_n_ifaces; + int n_ifaces; + int i; - nmtst_test_data_unpack(test_data, &p_ifindex, NULL, NULL, NULL, NULL); + _LOGT("TEST[%s]: teardown", test_data->testpath); - g_assert_cmpint(NMTSTP_ENV1_IFINDEX, ==, *p_ifindex); - NMTSTP_ENV1_IFINDEX = -1; + nmtst_test_data_unpack(test_data, &p_ifindexes, &p_n_ifaces, NULL, NULL, NULL, NULL); - _LOGT("TEST[%s]: teardown", test_data->testpath); + n_ifaces = GPOINTER_TO_UINT(p_n_ifaces); + g_assert_cmpint(n_ifaces, >=, 1); + g_assert_cmpint(n_ifaces, <=, (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES)); + + for (i = 0; i < (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES); i++) { + if (i < n_ifaces) + g_assert_cmpint(p_ifindexes[i], >, 0); + else + g_assert_cmpint(p_ifindexes[i], ==, 0); + g_assert_cmpint(NMTSTP_ENV1_IFINDEXES[i], ==, p_ifindexes[i]); + NMTSTP_ENV1_IFINDEXES[i] = 0; + } - g_assert_cmpint(*p_ifindex, ==, nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME)); - g_assert(nm_platform_link_delete(NM_PLATFORM_GET, *p_ifindex)); + for (i = 0; i < n_ifaces; i++) + nmtstp_link_delete(NULL, -1, p_ifindexes[i], NMTSTP_ENV1_DEVICE_NAME[i], TRUE); nm_platform_process_events(NM_PLATFORM_GET); _LOGT("TEST[%s]: finished", test_data->testpath); - *p_ifindex = -1; + for (i = 0; i < (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES); i++) + p_ifindexes[i] = 0; } /* add test function, that set's up a particular environment, consisting * of a dummy device with ifindex NMTSTP_ENV1_IFINDEX. */ -#define _nmtstp_env1_add_test_func_full(testpath, test_func, test_data_func, arg, ifup) \ - nmtst_add_test_func_full(testpath, \ - _nmtstp_env1_wrapper_run, \ - _nmtstp_env1_wrapper_setup, \ - _nmtstp_env1_wrapper_teardown, \ - ({ \ - static int _ifindex = -1; \ - &_ifindex; \ - }), \ - ({ \ - GTestFunc _test_func = (test_func); \ - _test_func; \ - }), \ - ({ \ - GTestDataFunc _test_func = (test_data_func); \ - _test_func; \ - }), \ - (arg), \ - ({ \ - gboolean _ifup = (ifup); \ - GINT_TO_POINTER(_ifup); \ +#define _nmtstp_env1_add_test_func_full(testpath, test_func, test_data_func, arg, n_ifaces, ifup) \ + nmtst_add_test_func_full(testpath, \ + _nmtstp_env1_wrapper_run, \ + _nmtstp_env1_wrapper_setup, \ + _nmtstp_env1_wrapper_teardown, \ + ({ \ + static int _ifindexes[G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES)] = {0}; \ + _ifindexes; \ + }), \ + ({ \ + guint _n_ifaces = (n_ifaces); \ + GUINT_TO_POINTER(_n_ifaces); \ + }), \ + ({ \ + GTestFunc _test_func = (test_func); \ + _test_func; \ + }), \ + ({ \ + GTestDataFunc _test_func = (test_data_func); \ + _test_func; \ + }), \ + (arg), \ + ({ \ + gboolean _ifup = (ifup); \ + GINT_TO_POINTER(!!_ifup); \ })) -#define nmtstp_env1_add_test_func_data(testpath, test_func, arg, ifup) \ - _nmtstp_env1_add_test_func_full(testpath, NULL, test_func, arg, ifup) +#define nmtstp_env1_add_test_func_data(testpath, test_func, arg, n_ifaces, ifup) \ + _nmtstp_env1_add_test_func_full(testpath, NULL, test_func, arg, n_ifaces, ifup) -#define nmtstp_env1_add_test_func(testpath, test_func, ifup) \ - _nmtstp_env1_add_test_func_full(testpath, test_func, NULL, NULL, ifup) +#define nmtstp_env1_add_test_func(testpath, test_func, n_ifaces, ifup) \ + _nmtstp_env1_add_test_func_full(testpath, test_func, NULL, NULL, n_ifaces, ifup) /*****************************************************************************/ diff --git a/src/core/platform/tests/test-link.c b/src/core/platform/tests/test-link.c index b72bcb65..cc1ec6d1 100644 --- a/src/core/platform/tests/test-link.c +++ b/src/core/platform/tests/test-link.c @@ -161,8 +161,14 @@ software_add(NMLinkType link_type, const char *name) accept_signals(parent_changed, 1, 2); free_signal(parent_changed); - return NMTST_NM_ERR_SUCCESS( - nm_platform_link_vlan_add(NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL)); + return NMTST_NM_ERR_SUCCESS(nm_platform_link_vlan_add(NM_PLATFORM_GET, + name, + parent_ifindex, + &((NMPlatformLnkVlan){ + .id = VLAN_ID, + .protocol = ETH_P_8021Q, + }), + NULL)); } } default: @@ -1183,7 +1189,7 @@ _test_wireguard_change(NMPlatform *platform, int ifindex, int test_mode) for (i = 0; i < num_peers; i++) { NMPWireGuardPeer peer; - char s_addr[NM_UTILS_INET_ADDRSTRLEN]; + char s_addr[NM_INET_ADDRSTRLEN]; NMSockAddrUnion endpoint; guint i_allowed_ips, n_allowed_ips; NMPWireGuardAllowedIP *allowed_ips; @@ -1203,8 +1209,8 @@ _test_wireguard_change(NMPlatform *platform, int ifindex, int test_mode) .in6 = { .sin6_family = AF_INET6, - .sin6_addr = *nmtst_inet6_from_string( - nm_sprintf_buf(s_addr, "a:b:c:e::1:%d", i)), + .sin6_addr = + nmtst_inet6_from_string(nm_sprintf_buf(s_addr, "a:b:c:e::1:%d", i)), .sin6_port = htons(16000 + i), }, }; @@ -1225,7 +1231,7 @@ _test_wireguard_change(NMPlatform *platform, int ifindex, int test_mode) nm_sprintf_buf(s_addr, "10.%u.%u.0", i, i_allowed_ips)); aip->mask = 32 - (i_allowed_ips % 8); } else { - aip->addr.addr6 = *nmtst_inet6_from_string( + aip->addr.addr6 = nmtst_inet6_from_string( nm_sprintf_buf(s_addr, "a:d:f:%02x:%02x::", i, i_allowed_ips)); aip->mask = 128 - (i_allowed_ips % 10); } @@ -1250,7 +1256,7 @@ _test_wireguard_change(NMPlatform *platform, int ifindex, int test_mode) r = nm_platform_link_wireguard_change(platform, ifindex, &lnk_wireguard, - (const NMPWireGuardPeer *) peers->data, + nm_g_array_first_p(peers, const NMPWireGuardPeer), NULL, peers->len, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY @@ -1280,8 +1286,11 @@ test_software_detect(gconstpointer user_data) const gboolean ext = test_data->external_command; NMPlatformLnkBridge lnk_bridge = {}; NMPlatformLnkTun lnk_tun; - NMPlatformLnkGre lnk_gre = {}; - nm_auto_close int tun_fd = -1; + NMPlatformLnkGre lnk_gre = {}; + NMPlatformLnkVti lnk_vti = {}; + NMPlatformLnkVti6 lnk_vti6 = {}; + nm_auto_close int tun_fd = -1; + gboolean module_loaded; nmtstp_run_command_check("ip link add %s type dummy", PARENT_NAME); ifindex_parent = @@ -1296,24 +1305,24 @@ test_software_detect(gconstpointer user_data) lnk_bridge.ageing_time = 2200; lnk_bridge.stp_state = TRUE; lnk_bridge.priority = 22; - lnk_bridge.vlan_protocol = 0x8100; + lnk_bridge.vlan_protocol = ETH_P_8021Q; lnk_bridge.vlan_stats_enabled = nmtstp_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED) ? TRUE : FALSE; - lnk_bridge.group_fwd_mask = 8; - lnk_bridge.group_addr = (NMEtherAddr){{0x01, 0x80, 0xC2, 0x00, 0x00, 0x08}}; - lnk_bridge.mcast_snooping = TRUE; - lnk_bridge.mcast_router = 1; - lnk_bridge.mcast_query_use_ifaddr = TRUE; - lnk_bridge.mcast_querier = TRUE; - lnk_bridge.mcast_hash_max = 1024; - lnk_bridge.mcast_last_member_count = 2; - lnk_bridge.mcast_startup_query_count = 3; - lnk_bridge.mcast_last_member_interval = 5000; - lnk_bridge.mcast_membership_interval = 25000; - lnk_bridge.mcast_querier_interval = 26000; - lnk_bridge.mcast_query_interval = 12000; + lnk_bridge.group_fwd_mask = 8; + lnk_bridge.group_addr = NM_ETHER_ADDR_INIT(0x01, 0x80, 0xC2, 0x00, 0x00, 0x08); + lnk_bridge.mcast_snooping = TRUE; + lnk_bridge.mcast_router = 1; + lnk_bridge.mcast_query_use_ifaddr = TRUE; + lnk_bridge.mcast_querier = TRUE; + lnk_bridge.mcast_hash_max = 1024; + lnk_bridge.mcast_last_member_count = 2; + lnk_bridge.mcast_startup_query_count = 3; + lnk_bridge.mcast_last_member_interval = 5000; + lnk_bridge.mcast_membership_interval = 25000; + lnk_bridge.mcast_querier_interval = 26000; + lnk_bridge.mcast_query_interval = 12000; lnk_bridge.mcast_query_response_interval = 5200; lnk_bridge.mcast_startup_query_interval = 3000; @@ -1322,8 +1331,7 @@ test_software_detect(gconstpointer user_data) break; case NM_LINK_TYPE_GRE: - { - gboolean gracefully_skip = FALSE; + module_loaded = nmtstp_ensure_module("ip_gre"); lnk_gre.local = nmtst_inet4_from_string("192.168.233.204"); lnk_gre.remote = nmtst_inet4_from_string("172.168.10.25"); @@ -1332,13 +1340,8 @@ test_software_detect(gconstpointer user_data) lnk_gre.tos = 37; lnk_gre.path_mtu_discovery = TRUE; - if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "gre0")) { - /* Seems that the ip_gre module is not loaded... try to load it. */ - gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip_gre", NULL) != 0; - } - if (!nmtstp_link_gre_add(NULL, ext, DEVICE_NAME, &lnk_gre)) { - if (gracefully_skip) { + if (!module_loaded) { g_test_skip( "Cannot create gre tunnel because of missing ip_gre module (modprobe ip_gre)"); goto out_delete_parent; @@ -1346,10 +1349,9 @@ test_software_detect(gconstpointer user_data) g_error("Failed adding GRE tunnel"); } break; - } + case NM_LINK_TYPE_GRETAP: - { - gboolean gracefully_skip = FALSE; + module_loaded = nmtstp_ensure_module("ip_gre"); lnk_gre.local = nmtst_inet4_from_string("192.168.1.133"); lnk_gre.remote = nmtst_inet4_from_string("172.168.101.2"); @@ -1359,13 +1361,8 @@ test_software_detect(gconstpointer user_data) lnk_gre.path_mtu_discovery = FALSE; lnk_gre.is_tap = TRUE; - if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "gretap0")) { - /* Seems that the ip_gre module is not loaded... try to load it. */ - gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip_gre", NULL) != 0; - } - if (!nmtstp_link_gre_add(NULL, ext, DEVICE_NAME, &lnk_gre)) { - if (gracefully_skip) { + if (!module_loaded) { g_test_skip("Cannot create gretap tunnel because of missing ip_gre module " "(modprobe ip_gre)"); goto out_delete_parent; @@ -1373,16 +1370,12 @@ test_software_detect(gconstpointer user_data) g_error("Failed adding GRETAP tunnel"); } break; - } + case NM_LINK_TYPE_IPIP: { - NMPlatformLnkIpIp lnk_ipip = {}; - gboolean gracefully_skip = FALSE; + NMPlatformLnkIpIp lnk_ipip = {}; - if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "tunl0")) { - /* Seems that the ipip module is not loaded... try to load it. */ - gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ipip", NULL) != 0; - } + module_loaded = nmtstp_ensure_module("ipip"); lnk_ipip.local = nmtst_inet4_from_string("1.2.3.4"); lnk_ipip.remote = nmtst_inet4_from_string("5.6.7.8"); @@ -1391,7 +1384,7 @@ test_software_detect(gconstpointer user_data) lnk_ipip.path_mtu_discovery = FALSE; if (!nmtstp_link_ipip_add(NULL, ext, DEVICE_NAME, &lnk_ipip)) { - if (gracefully_skip) { + if (!module_loaded) { g_test_skip( "Cannot create ipip tunnel because of missing ipip module (modprobe ipip)"); goto out_delete_parent; @@ -1400,20 +1393,17 @@ test_software_detect(gconstpointer user_data) } break; } + case NM_LINK_TYPE_IP6TNL: { - NMPlatformLnkIp6Tnl lnk_ip6tnl = {}; - gboolean gracefully_skip = FALSE; + NMPlatformLnkIp6Tnl lnk_ip6tnl = {}; - if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "ip6tnl0")) { - /* Seems that the ip6_tunnel module is not loaded... try to load it. */ - gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip6_tunnel", NULL) != 0; - } + module_loaded = nmtstp_ensure_module("ip6_tunnel"); switch (test_data->test_mode) { case 0: - lnk_ip6tnl.local = *nmtst_inet6_from_string("fd01::15"); - lnk_ip6tnl.remote = *nmtst_inet6_from_string("fd01::16"); + lnk_ip6tnl.local = nmtst_inet6_from_string("fd01::15"); + lnk_ip6tnl.remote = nmtst_inet6_from_string("fd01::16"); lnk_ip6tnl.parent_ifindex = ifindex_parent; lnk_ip6tnl.tclass = 20; lnk_ip6tnl.encap_limit = 6; @@ -1421,8 +1411,8 @@ test_software_detect(gconstpointer user_data) lnk_ip6tnl.proto = IPPROTO_IPV6; break; case 1: - lnk_ip6tnl.local = *nmtst_inet6_from_string("fd01::17"); - lnk_ip6tnl.remote = *nmtst_inet6_from_string("fd01::18"); + lnk_ip6tnl.local = nmtst_inet6_from_string("fd01::17"); + lnk_ip6tnl.remote = nmtst_inet6_from_string("fd01::18"); lnk_ip6tnl.parent_ifindex = ifindex_parent; lnk_ip6tnl.tclass = 0; lnk_ip6tnl.encap_limit = 0; @@ -1433,7 +1423,7 @@ test_software_detect(gconstpointer user_data) } if (!nmtstp_link_ip6tnl_add(NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) { - if (gracefully_skip) { + if (!module_loaded) { g_test_skip("Cannot create ip6tnl tunnel because of missing ip6_tunnel module " "(modprobe ip6_tunnel)"); goto out_delete_parent; @@ -1442,25 +1432,22 @@ test_software_detect(gconstpointer user_data) } break; } + case NM_LINK_TYPE_IP6GRE: { - NMPlatformLnkIp6Tnl lnk_ip6tnl = {}; - gboolean gracefully_skip = FALSE; + NMPlatformLnkIp6Tnl lnk_ip6tnl = {}; - if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "ip6gre0")) { - /* Seems that the ip6_tunnel module is not loaded... try to load it. */ - gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip6_gre", NULL) != 0; - } + module_loaded = nmtstp_ensure_module("ip6_gre"); - lnk_ip6tnl.local = *nmtst_inet6_from_string("fd01::42"); - lnk_ip6tnl.remote = *nmtst_inet6_from_string("fd01::aaaa"); + lnk_ip6tnl.local = nmtst_inet6_from_string("fd01::42"); + lnk_ip6tnl.remote = nmtst_inet6_from_string("fd01::aaaa"); lnk_ip6tnl.parent_ifindex = ifindex_parent; lnk_ip6tnl.tclass = 21; lnk_ip6tnl.flow_label = 1338; lnk_ip6tnl.is_gre = TRUE; if (!nmtstp_link_ip6gre_add(NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) { - if (gracefully_skip) { + if (!module_loaded) { g_test_skip("Cannot create ip6gre tunnel because of missing ip6_gre module " "(modprobe ip6_gre)"); goto out_delete_parent; @@ -1469,18 +1456,15 @@ test_software_detect(gconstpointer user_data) } break; } + case NM_LINK_TYPE_IP6GRETAP: { - NMPlatformLnkIp6Tnl lnk_ip6tnl = {}; - gboolean gracefully_skip = FALSE; + NMPlatformLnkIp6Tnl lnk_ip6tnl = {}; - if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "ip6gre0")) { - /* Seems that the ip6_tunnel module is not loaded... try to load it. */ - gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip6_gre", NULL) != 0; - } + module_loaded = nmtstp_ensure_module("ip6_gre"); - lnk_ip6tnl.local = *nmtst_inet6_from_string("fe80::abcd"); - lnk_ip6tnl.remote = *nmtst_inet6_from_string("fc01::bbbb"); + lnk_ip6tnl.local = nmtst_inet6_from_string("fe80::abcd"); + lnk_ip6tnl.remote = nmtst_inet6_from_string("fc01::bbbb"); lnk_ip6tnl.parent_ifindex = ifindex_parent; lnk_ip6tnl.ttl = 10; lnk_ip6tnl.tclass = 22; @@ -1489,7 +1473,7 @@ test_software_detect(gconstpointer user_data) lnk_ip6tnl.is_tap = TRUE; if (!nmtstp_link_ip6gre_add(NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) { - if (gracefully_skip) { + if (!module_loaded) { g_test_skip("Cannot create ip6gretap tunnel because of missing ip6_gre module " "(modprobe ip6_gre)"); goto out_delete_parent; @@ -1498,6 +1482,7 @@ test_software_detect(gconstpointer user_data) } break; } + case NM_LINK_TYPE_MACVLAN: { NMPlatformLnkMacvlan lnk_macvlan = {}; @@ -1531,6 +1516,7 @@ test_software_detect(gconstpointer user_data) g_error("Failed adding MACVLAN interface"); break; } + case NM_LINK_TYPE_MACVTAP: { NMPlatformLnkMacvlan lnk_macvtap = {}; @@ -1543,10 +1529,12 @@ test_software_detect(gconstpointer user_data) g_error("Failed adding MACVTAP interface"); break; } + case NM_LINK_TYPE_SIT: { - NMPlatformLnkSit lnk_sit = {}; - gboolean gracefully_skip = FALSE; + NMPlatformLnkSit lnk_sit = {}; + + module_loaded = nmtstp_ensure_module("sit"); lnk_sit.local = nmtst_inet4_from_string("192.168.200.1"); lnk_sit.remote = nmtst_inet4_from_string("172.25.100.14"); @@ -1555,13 +1543,8 @@ test_software_detect(gconstpointer user_data) lnk_sit.tos = 31; lnk_sit.path_mtu_discovery = FALSE; - if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "sit0")) { - /* Seems that the sit module is not loaded... try to load it. */ - gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "sit", NULL) != 0; - } - if (!nmtstp_link_sit_add(NULL, ext, DEVICE_NAME, &lnk_sit)) { - if (gracefully_skip) { + if (!module_loaded) { g_test_skip( "Cannot create sit tunnel because of missing sit module (modprobe sit)"); goto out_delete_parent; @@ -1570,11 +1553,31 @@ test_software_detect(gconstpointer user_data) } break; } + case NM_LINK_TYPE_VLAN: - nmtstp_run_command_check("ip link add name %s link %s type vlan id 1242", - DEVICE_NAME, - PARENT_NAME); + { + NMPlatformLnkVlan lnk_vlan = {}; + + switch (test_data->test_mode) { + case 0: + lnk_vlan.id = 1242; + lnk_vlan.protocol = ETH_P_8021Q; + lnk_vlan.flags = _NM_VLAN_FLAG_REORDER_HEADERS; + break; + case 1: + lnk_vlan.id = 4094; + lnk_vlan.protocol = ETH_P_8021AD; + lnk_vlan.flags = _NM_VLAN_FLAG_GVRP | _NM_VLAN_FLAG_MVRP; + break; + default: + nm_assert_not_reached(); + } + + if (!nmtstp_link_vlan_add(NULL, ext, DEVICE_NAME, ifindex_parent, &lnk_vlan)) + g_error("Failed adding VLAN interface"); break; + } + case NM_LINK_TYPE_VRF: { NMPlatformLnkVrf lnk_vrf = {}; @@ -1591,6 +1594,45 @@ test_software_detect(gconstpointer user_data) } break; } + + case NM_LINK_TYPE_VTI: + module_loaded = nmtstp_ensure_module("ip_vti"); + + lnk_vti.local = nmtst_inet4_from_string("192.168.212.204"); + lnk_vti.remote = nmtst_inet4_from_string("172.168.11.25"); + lnk_vti.parent_ifindex = ifindex_parent; + lnk_vti.ikey = 12; + lnk_vti.okey = 13; + + if (!nmtstp_link_vti_add(NULL, ext, DEVICE_NAME, &lnk_vti)) { + if (!module_loaded) { + g_test_skip( + "Cannot create vti tunnel because of missing vti module (modprobe ip_vti)"); + goto out_delete_parent; + } + g_error("Failed adding VTI tunnel"); + } + break; + + case NM_LINK_TYPE_VTI6: + module_loaded = nmtstp_ensure_module("ip6_vti"); + + lnk_vti6.local = nmtst_inet6_from_string("fd01::1"); + lnk_vti6.remote = nmtst_inet6_from_string("fd02::2"); + lnk_vti6.parent_ifindex = ifindex_parent; + lnk_vti6.ikey = 13; + lnk_vti6.okey = 14; + + if (!nmtstp_link_vti6_add(NULL, ext, DEVICE_NAME, &lnk_vti6)) { + if (!module_loaded) { + g_test_skip( + "Cannot create vti6 tunnel because of missing vti module (modprobe ip_vti)"); + goto out_delete_parent; + } + g_error("Failed adding VTI6 tunnel"); + } + break; + case NM_LINK_TYPE_VXLAN: { NMPlatformLnkVxlan lnk_vxlan = {}; @@ -1608,8 +1650,8 @@ test_software_detect(gconstpointer user_data) case 1: lnk_vxlan.parent_ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, PARENT_NAME); lnk_vxlan.id = 11214423; - lnk_vxlan.local6 = *nmtst_inet6_from_string("1:2:3:4:334:23::23"); - lnk_vxlan.group6 = *nmtst_inet6_from_string("ff0e::115"); + lnk_vxlan.local6 = nmtst_inet6_from_string("1:2:3:4:334:23::23"); + lnk_vxlan.group6 = nmtst_inet6_from_string("ff0e::115"); lnk_vxlan.ttl = 32; lnk_vxlan.dst_port = 57412; lnk_vxlan.src_port_min = 1000; @@ -1622,6 +1664,7 @@ test_software_detect(gconstpointer user_data) g_assert(nmtstp_link_vxlan_add(NULL, ext, DEVICE_NAME, &lnk_vxlan)); break; } + case NM_LINK_TYPE_TUN: { gboolean owner_valid = nmtst_get_rand_bool(); @@ -1656,6 +1699,7 @@ test_software_detect(gconstpointer user_data) (!lnk_tun.persist || nmtst_get_rand_bool()) ? &tun_fd : NULL)); break; } + case NM_LINK_TYPE_WIREGUARD: { const NMPlatformLink *link; @@ -1727,7 +1771,7 @@ test_software_detect(gconstpointer user_data) g_assert_cmpint(plnk->ageing_time, ==, lnk_bridge_norm->ageing_time); g_assert_cmpint(plnk->stp_state, ==, TRUE); g_assert_cmpint(plnk->priority, ==, 22); - g_assert_cmpint(plnk->vlan_protocol, ==, 0x8100); + g_assert_cmpint(plnk->vlan_protocol, ==, ETH_P_8021Q); g_assert_cmpint(plnk->vlan_stats_enabled, ==, lnk_bridge_norm->vlan_stats_enabled); g_assert_cmpint(plnk->group_fwd_mask, ==, 8); g_assert_cmpint(plnk->mcast_snooping, ==, TRUE); @@ -1910,7 +1954,19 @@ test_software_detect(gconstpointer user_data) const NMPlatformLnkVlan *plnk = &lnk->lnk_vlan; g_assert(plnk == nm_platform_link_get_lnk_vlan(NM_PLATFORM_GET, ifindex, NULL)); - g_assert_cmpint(plnk->id, ==, 1242); + + switch (test_data->test_mode) { + case 0: + g_assert_cmpint(plnk->id, ==, 1242); + g_assert_cmpint(plnk->protocol, ==, ETH_P_8021Q); + g_assert_cmpint(plnk->flags, ==, _NM_VLAN_FLAG_REORDER_HEADERS); + break; + case 1: + g_assert_cmpint(plnk->id, ==, 4094); + g_assert_cmpint(plnk->protocol, ==, ETH_P_8021AD); + g_assert_cmpint(plnk->flags, ==, _NM_VLAN_FLAG_GVRP | _NM_VLAN_FLAG_MVRP); + break; + } break; } case NM_LINK_TYPE_VRF: @@ -1921,6 +1977,22 @@ test_software_detect(gconstpointer user_data) g_assert_cmpint(plnk->table, ==, 9876); break; } + case NM_LINK_TYPE_VTI: + { + const NMPlatformLnkVti *plnk = &lnk->lnk_vti; + + g_assert(plnk == nm_platform_link_get_lnk_vti(NM_PLATFORM_GET, ifindex, NULL)); + g_assert(nm_platform_lnk_vti_cmp(plnk, &lnk_vti) == 0); + break; + } + case NM_LINK_TYPE_VTI6: + { + const NMPlatformLnkVti6 *plnk = &lnk->lnk_vti6; + + g_assert(plnk == nm_platform_link_get_lnk_vti6(NM_PLATFORM_GET, ifindex, NULL)); + g_assert(nm_platform_lnk_vti6_cmp(plnk, &lnk_vti6) == 0); + break; + } case NM_LINK_TYPE_VXLAN: { const NMPlatformLnkVxlan *plnk = &lnk->lnk_vxlan; @@ -2511,7 +2583,7 @@ test_create_many_links_do(guint n_devices) if (EX == 2) nmtstp_run_command_check("ip link delete %s", name); else - nmtstp_link_delete(NULL, EX, g_array_index(ifindexes, int, i), name, TRUE); + nmtstp_link_delete(NULL, EX, nm_g_array_index(ifindexes, int, i), name, TRUE); } _LOGI(">>> process events after deleting devices..."); @@ -2754,7 +2826,7 @@ _test_netns_create_platform(void) netns = nmp_netns_new(); g_assert(NMP_IS_NETNS(netns)); - platform = nm_linux_platform_new(TRUE, TRUE, TRUE); + platform = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE); g_assert(NM_IS_LINUX_PLATFORM(platform)); nmp_netns_pop(netns); @@ -2843,7 +2915,7 @@ test_netns_general(gpointer fixture, gconstpointer test_data) if (_check_sysctl_skip()) return; - platform_1 = nm_linux_platform_new(TRUE, TRUE, TRUE); + platform_1 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE); platform_2 = _test_netns_create_platform(); /* add some dummy devices. The "other-*" devices are there to bump the ifindex */ @@ -2971,7 +3043,7 @@ test_netns_set_netns(gpointer fixture, gconstpointer test_data) if (_test_netns_check_skip()) return; - platforms[0] = platform_0 = nm_linux_platform_new(TRUE, TRUE, TRUE); + platforms[0] = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE); platforms[1] = platform_1 = _test_netns_create_platform(); platforms[2] = platform_2 = _test_netns_create_platform(); @@ -3070,7 +3142,7 @@ test_netns_push(gpointer fixture, gconstpointer test_data) if (_check_sysctl_skip()) return; - pl[0].platform = platform_0 = nm_linux_platform_new(TRUE, TRUE, TRUE); + pl[0].platform = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE); pl[1].platform = platform_1 = _test_netns_create_platform(); pl[2].platform = platform_2 = _test_netns_create_platform(); @@ -3217,7 +3289,7 @@ test_netns_bind_to_path(gpointer fixture, gconstpointer test_data) if (_test_netns_check_skip()) return; - platforms[0] = platform_0 = nm_linux_platform_new(TRUE, TRUE, TRUE); + platforms[0] = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE); platforms[1] = platform_1 = _test_netns_create_platform(); platforms[2] = platform_2 = _test_netns_create_platform(); @@ -3382,7 +3454,7 @@ test_sysctl_netns_switch(void) if (_test_netns_check_skip()) return; - platforms[0] = platform_0 = nm_linux_platform_new(TRUE, TRUE, TRUE); + platforms[0] = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE); platforms[1] = platform_1 = _test_netns_create_platform(); platforms[2] = platform_2 = _test_netns_create_platform(); PL = platforms[nmtst_get_rand_uint32() % 3]; @@ -3823,7 +3895,10 @@ _nmtstp_setup_tests(void) test_software_detect_add("/link/software/detect/macvtap", NM_LINK_TYPE_MACVTAP, 0); test_software_detect_add("/link/software/detect/sit", NM_LINK_TYPE_SIT, 0); test_software_detect_add("/link/software/detect/tun", NM_LINK_TYPE_TUN, 0); - test_software_detect_add("/link/software/detect/vlan", NM_LINK_TYPE_VLAN, 0); + test_software_detect_add("/link/software/detect/vlan/0", NM_LINK_TYPE_VLAN, 0); + test_software_detect_add("/link/software/detect/vlan/1", NM_LINK_TYPE_VLAN, 1); + test_software_detect_add("/link/software/detect/vti", NM_LINK_TYPE_VTI, 0); + test_software_detect_add("/link/software/detect/vti6", NM_LINK_TYPE_VTI6, 0); test_software_detect_add("/link/software/detect/vrf", NM_LINK_TYPE_VRF, 0); test_software_detect_add("/link/software/detect/vxlan/0", NM_LINK_TYPE_VXLAN, 0); test_software_detect_add("/link/software/detect/vxlan/1", NM_LINK_TYPE_VXLAN, 1); diff --git a/src/core/platform/tests/test-platform-general.c b/src/core/platform/tests/test-platform-general.c index c18886c9..cebd44f6 100644 --- a/src/core/platform/tests/test-platform-general.c +++ b/src/core/platform/tests/test-platform-general.c @@ -31,7 +31,7 @@ test_init_linux_platform(void) { gs_unref_object NMPlatform *platform = NULL; - platform = nm_linux_platform_new(TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT, TRUE); + platform = nm_linux_platform_new(NULL, TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT, TRUE); } /*****************************************************************************/ @@ -42,7 +42,7 @@ test_link_get_all(void) gs_unref_object NMPlatform *platform = NULL; gs_unref_ptrarray GPtrArray *links = NULL; - platform = nm_linux_platform_new(TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT, TRUE); + platform = nm_linux_platform_new(NULL, TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT, TRUE); links = nm_platform_link_get_all(platform, TRUE); } @@ -101,456 +101,486 @@ static void test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data) { static const char *const EXPECTED_BUFFER[3] = { - [0] = ("5b1aea34648cabfec7c3523f76cf1ce34ca17a9a32f3f0f218074e48000000cb504e03d53e1124c5" - "0065aeb2e6fbf952902383028e3b47f2111d891d17a47631c169f11ff9119c40b468b66331bca67a" - "034f43340d02683c7545097aea4a849f060ddf57004a5be9b10123e6e4f15ecd1dff045298cfee06" - "b7f0318a03000000061db3dd7fde25654a4059d565dbc8a91e014457000000db56189572f32f4af5" - "00d785d5c1b5aad487c192f08bd7a09bdd2144439c88b02a0300000004b0595daf37deb499996bfb" - "667f072dec035d9c000000f4409bcfb3c167090e00d90a8543961b2ccd47724a3c460ba85183f4c5" - "a7afe28303000000d05ff720b16c73761a3fcb675219061ab402c7940000006a883c146972decb8b" - "00dac8f5e6d66df46ea139107754dee62c36d2fc6aae660b03000000fc1fb23d24438387def0f4c6" - "544e4b275d037146000000d349adc17647176b7c00d856e3b883efebc09dd9d61966b7ae7412041d" - "e5fe7d360300000066a3ae3939762df33a2d55060c78d551cb0310fd00000043b9aca07b6e4925dd" - "00943a272c171ed15abbbe1cd911db7b56e8f3fe6f0e9e6903000000ec2e2924348352d715cf9d41" - "1ea012e5300294b6000000c528ab20075efee38d006f8ee4ccd2f50c30706cae3fdcc2f0ee3d5e26" - "dac12ebd03000000667224dae775e5ccd141aca2ff0f57676702648100000086b62f22ca33478e1f" - "001715df7fc6a902edae579e2e10c7f7a22ba0340436242c03000000ee3fbc164632d4f28aa28c08" - "f80dd50c6702dfb400000023a28fa772a19cdf6d00c173f6078a806d329c9b008bbfc9723107f3f5" - "bcfe97e1030000007ad742d3d7320a4f880cf47f5dd0cf69cb0d840f0000007749509cb6752b2cee" - "00b7e7736a0afc9879ea40e69710fc9f6e8e99cdabf85f0c0300000008d33a2ea67776d7f88d69c8" - "c7e3b2d3c900f05400000021abb42316c533d9c90088189c471a1ff2f0ce3ff66e782110125ae2ed" - "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f6002f061d45d864d09f4ecf17625f82d" - "00c748288ae21e371051852c972ea7954079884a6c0fc09c03000000809829af4a9df58dbfef186d" - "416f3a1ef102d10f000000675203d82a3c84c4eb274ed18687780281d47a32f2235376239daeaacb" - "3cae864b0300000091921681c2162b9bfa40546cdb544e44df037c270000009a051bb2ee8192ac0c" - "fa3abc1ea0ab3e7d75a2f42b50c6a363401323780422ae3f03000000c43bd794c697895e5bfe47c6" - "f53a54e01b021d89000000d3e02d2ec587fc330003781b0f294a2b73c4421398f4de67e9cee64b38" - "ae8cb60b0300000038d856a40c67c3d4afa9c8649da90bd2ff02f2560000000bd7d5f14870c6d18d" - "6242b356955e1a13d1111b067acbce3e3bcdf382bb0ec36f0300000064a2827b7cdd38d0314c178e" - "925b568ae512c4e9000000e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13" - "36674cde0300000054cfe8f42bf0e9381ba2c4396de60f032b00f34de2959871c0d4c0d4eb720c4a" - "e3c9472e5f2da5d9c5cfd9f2343b21362c19a09269121aec030000000fcc00ac2daa755e5292cd32" - "dc096cd5d802c5cf3351ee1cfcc2056cc1ff8e51bbce426cfa4b861cc78592be7b14e7ba9c15acb8" - "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa70031205000000f39b098b43cd68be6e" - "3f81268193fd637e585287dc6ca972a2cfb18b2bf6989d2d030000006ff7add5df2c93e05459507b" - "5d8c6a0f460554d2000000a8c67b2e48b47003c81785d3ffdbd9a617857ae8126b028c0e3e295354" - "bc51e540030000006c3d048a92d3adc69a84eb47622400207700416a00000087fbd7e2f7dea0077a" - "e9c0c0d9c386e9c6e6a2cbfa10ee58bdc75183607c52a52d030000004e8e8bc17c01ac63eca0c1cf" - "bb204132fb0213e4000000c2303dd1d65c7b2cffeca6d1adaccae11f8045a80f855ebf3b772eb2b1" - "bae87d60030000005d31e5b2de252167c1c91b3a36ba0c700e118477000000b913a353edc66efc06" - "8829c0e66ad0143a0554efb46b382ab0404cdf027a899f2d030000009bf5a5d06776e66a9d80ab13" - "2e1ac921eb01adbb0000002f561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b" - "daae7d6303000000c0982b283aa9986700b2a2b3ed257b8b041df48f000000cac72105b327335a25" - "a364d1015ffac03089f45539e1b0178462156cd6c4e1966803000000b3121cd5ef51e696c816290d" - "baee0e77260a82e1530ff5397c9125f59577d71c4b258a005116d11354edff62ceaa458fc75a91c4" - "d03bc90503000000eb760e3f004a2efc8ffc96b89b1bc8f415034e77be46bae5a1b3cae76665a268" - "abfe8a41d149f074a378b881153ece68ade15cec7625ca4a03000000e682f80f94315c45ef817264" - "c89a736ed51dd637b077ddad3108be69f9b97d05c917ad6b10e693bb6e26f2ba90c8e909e85e20e5" - "2af145480300000004265f6c72923e22594f3f79e208ed2e2e020771db46a54cadb245eafde6b025" - "67f814de39dd8f88152a58454d9ca9d20f45dfa700154d12020000009edfa7a74c1c83c18e47935e" - "8354846e3403386a000000d94064f6b021281e230094ae20ac26c7d41fd050bf8d85055bcfe3756c" - "98c39e7c02000000efc57ff447036b9551adcb22fd50793f79103c7d0000006e759d0c90bc512e13" - "00cef5e4a27985bd8d1e69c83dc21056b973972c6855676c02000000017c9148da5892bb4951c3a7" - "ed55689d3305ce7d3fd48469845f0c233dbdd2a90035df1f1782dee88487fd0db5971b46b7a350f9" - "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c01a56dfdcfc1155f236c8b9c79a620" - "00660bbf024b03ff0a8e27c405e6424457393c6f74993ade02000000e62112898dab2dade2ab2fc9" - "c56a7c86be0296228ec3f2f439d949b22b9c161c00b745cebdd06352cb2c354434819f4b248eb2b8" - "6297dfa702000000fd0d53bb6215de4bb6f6f3d031a790287e06e9c18bb836c0a25ea71f5daaed4d" - "00eb2ebebe3c432fec7d3abc4ccf30d3aaaf02a46e91b91502000000be5b534e0a6527f67485ab35" - "aca0c7ee4102338500000063e66e3a23d6732578acd14320aefd4503189f7630248e501c9052c22b" - "e3ff1a5f02000000b4425486c619147eb0216050ed7afd741000e83600000011f9c797c0c0ec468e" - "9de5a9caf493472201e48d163106cc397446a33fa349e1cc02000000b879a45176740744fcb6a035" - "339d504c9719d80d000000df765b4d9a130257dc5ff7f03a9518862bc9a89192c1c8be55281a59d1" - "bcd7be0702000000e45aaf61814f703b40125e6baf4648cbaa1f7b86000000dafe7d6ec4ac1ea051" - "010911915ddb05f2a84e27c18a06e2c9cb29efdfb56e03e0010000002f70249eefc67bce716c856e" - "3973dc42a1033be9000000efb1f14d985d5bbe6c00085f3d6176f78a19bb8df1804c122fce5078c1" - "57d6249b010000007dc7f17f666969e45c3baf7a119a65ee0b01fa9e000000f3de6dc8dfcf2a3ad3" - "00cc0fa3b314f695bd82e9ea8891ed0a3d1b91ca4f1b134801000000449a774929e86716ec852d59" - "f3d17232ed026ad600000068843a9aef34983882004f24a17dba1ee7e143fd52599364e13468f80f" - "fc1519de010000002545666937c22a3b6e9686dcc405cbee5000c2520000001f7a0165fe20744edb" - "00a04f146e4c699341b95f41d9c1e67743efb54e54270e46010000006a894e387625da376feecb80" - "ac245409c003cc2700000067179bff0644399ae70064ed0432d599eaa660ac824b7fae389861419c" - "9f3c2c8801000000f51f44e644d7cf1d06b2b115d882549e62002fba5b1cdec48d10bbb13b8313d8" - "002a24d34fc812bd2f8a59d96d064126666befbb81ae55f0010000000aa20624fbab244735d67c61" - "283031667b0274a103e0b1b153222aa590a2312e00f1a35900459582f4ef43c780908872746e39ef" - "536c75bb01000000720da9e07ac998b5690807d52617369ee1004ca500000081af5fd7ceacdca10f" - "00d7b351a36b178aabc78a4a0d80684ee5dbf45bc7a4c65601000000cd625009a40aeb62a42b6a62" - "548e3c38b9002002000000fbfaf1809becd2506300a6da29b2e94d3ab51fe0e9af98b180a36035fb" - "f05d7308010000009d82c856ba942455050f63f8282f34644402acf5000000a54b7892f487afa98c" - "0021c19c69d9942f460bcea75481f25e307d0de959747cf201000000ab2dadc5a39411fd4ff1116d" - "478987316a0e3fc2000000cfc6ebe434a7ae8ff040483e310819e3b10db116431ec6f7691539ce41" - "d514573f01000000f6932f6932d1d5f5d0ce2cf6bf7a70ba5100a1620000001b11d9172cf84a0347" - "8b82cdd8883b0f3fd6ed84d6c959e553b887edcdf90e54b501000000a779f37c30d2c5053c40ecae" - "210aeb0934012a247000b96d06992b6a58acd3995b9663d2df48f333ef467092818aa77d6732b678" - "cf844ef501000000cd743f59bd14c89b5cfd5ce6e82e785415032b7000000043e0f79ab6902dde53" - "36b3ec7127e19f254ac9588319558041fc62ed564153e174010000004d89c0bb5408c1b7d17fb084" - "d9e1825fa602e1af0000004b7f9e85ff73e050a2cd89fd276e08cb887464b9344ec3127c750fabdd" - "9985d62701000000e491cbbbf9443fd6a77eb3c5bf64b671d6068dbf0000008306c83bc026977911" - "4bdade862c224f6f36506ebd455e679cef369bb8fc2b445701000000496ad01bf007eacb0a28aec8" - "68282510b61e91ea6480e356def85b75827b26e8a0bef8675b29a197b7b3cceaf5f1bb12c535256a" - "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e1700b60000006dae02dfa0c26e7c3a" - "00603bd35e7fa0f02f2f3313d8469d09a92409c0f257b044000000006a4c619f9037d7a754655354" - "c47cf0f5510167700000001bef820fe02931f2d30075049150b8d10ab700cc3a7cf51be0403b654b" - "10613fc90000000016a31dc2e46a234558817151b38fc9c909038d71000000d761fdf274fc5dca8c" - "00bd1871ae17bb4515856092b4d9e89b676761b2eecc89f600000000d635edea8ffe423d01883918" - "039249f39800b37e00000053b2a16e643722d9b700c0d8a41fb7c8307f06b4b29088f20d9ac676d5" - "4d4647f5000000000f88e3759a08e7a56663861eea1bf42ccd0572f200000057ca82ac5180101be4" - "005cef468ea086ea9aaafdc388811ec787bc45db50da49bf00000000d94beb259f9f0251becc9879" - "07879cca681ec7bb000000df248b49953d136cac00054b019402a4c2167721673f2b02b3256ead1f" - "02e9b69000000000eacce745ea213cb1a84035b632ef3be6071fafa202187470e9da55709427c226" - "0024d9a086ed27184b443181a5edc511c1507a142fea9bc10000000062fb81e733cff620ca7feefe" - "1933631e8e03f6d90000002f6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196" - "cdb3455c00000000c66af76e80f062ea1a7e0b7be218d06753001b04000000bb9f2b8a1a8982c42f" - "53700a3437c5156e072b2f2fb337c9cfe07c34ddb83579b80000000072437f9b6245de39ec9e71ee" - "4b951507be002b340000000f895d5f24037d233302ad3b82d639272e4a46eadfbd2146bf8cfdb205" - "ebfdcec30000000067a982be8a934f852cc3d4d82bc0ec7303129f8f00000087965a316af2abb982" - "b001ec16901f55f960c55c22f32099a8020bea2a25a5927300000000c4458d70911714f1819aa46f" - "be143934c900ae6dec0fb124f264af67eed7c9a8c38b6b8ee2614344bea73223e59bf0193432e9fa" - "b4d54acb00000000a417f3da01d99dae5f190096cc582cabd51fd15a0000003edc5aa115165eb914" - "d8fbe9a4fdb3d117d76b0de5416f95e7bb75d72f4cc43c5100000000c2aac1d67a909b297c703915" - "312e9c3c5a03c40b20ea99d5ab90135fa636af4464d210a256be75e0509c4477d19ac64b2e69220d" - "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ec17fb112087b97cfcebbaeb0c565359" - "23f14874042a8aff1d1f8e5ec3cc13cc36bbe3c9b2cd3dbd00000000e958e48d2ab8b0313e7f8933" - "e315130fa9034c8288f83f6e70b1646d8026e9f1704f1b16286ba2dabc7ef0820c91ed33ef60a8b5" - "70ccc567df7a49b8a4c73cf5ffa4ffc8a4bab14874afdf54e60fe816a9fe07ca0675e09818e9bbec" - "0018c59068baf76ab08940f6fc3bbdc7f6090f7512ea3b9503000000fb95e8332fdfff658483a2d0" - "39a7bf148e01481ea9fec460e89f7abdb682380a00eae374835b4a49a2b980b6aba92da6409969aa" - "fd338f35030000006e8cc34573654ea6624f138ef9531cd9360002e4a9fe989477a363d55ad47b8a" - "11090df8da40d88d40a6d75bc404156225b7eedeb95c593103000000dbe9962069e66142e2275334" - "4fda8a3e0b001cf7a9fedd68269b53eccb44a831714acfb58c54dda9ac3bb1b1d43e6505621b9a7f" - "1e0f515603000000f57609453df7803357ae79989870ccec3d000ad9a9fee016663fe6ee0228c1da" - "5f5b481b9f05a16768cb3bc873b04d937a6ed8f7d7ac5f9903000000822a272bec4501a1e27acfee" - "7a8588ffd5016caea9fee59560fad65d67c624f285d7174177a47579dda0b6ead1e1b34901eaedd5" - "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a011480a9fec42cac4ef3ad378ffec7" - "085a62475588456e4ead7cbda620a3abae816e345d68265d020000009b7a559be3acf4fc36fa6b51" - "3cf09e8b99009462a9fe10acfce165e332a623840004e5bab49185ad9b7d0070a36dd51323b2c542" - "dedea2030200000002a1f8e6f5657f3f9e3eb807cbad76451002edb8a9fe628a70456d79f25d5895" - "0057fa60d9279fb2b8fcbac6abea74f7a6df7979b3980deb020000006c1de913df1424ce3ee1d76f" - "c1ea76e98c00fc78a9feb53e27c21963f5b4cb2454830c68e44ea74b4f2b83f9b3bee14d861a4c9e" - "bc74179402000000438a72e12ae0436a2495a609675344f7e200a5eba9fec85f0a1742f70abe95c5" - "0345132a61eda239d9d083c3bf085387046ef8a3b0916e5d020000004a503a712350acbd48411f0d" - "c15d2f0f4903d345a9fe06954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8" - "9a6ce5d902000000a6a5ee19d6f63fe9cbdd01f7246f13b2050524a2a9fe5ef7a029c896b4132bd8" - "95072cfeffe9d6812231bb02f922259b173c333c1dce2f83010000003a3efc240b9977e166bee737" - "fe73670c4301644ca9fe59b4cd20eb7dd3d38785008ffad45e0dfe491d1ba96742c7b5e02b227122" - "a9a89f8701000000fcdf6d9be94030b34774d1d7dddedd989901f627a9fe965a87041331b2834bcf" - "00b4e3ce848518c4e3f6cbf25e5e1b990fc46767e36f90d401000000fd41fe47684b370b6ec6584d" - "64496089570368eaa9feae91c819bb068196d59900de3246e43f5e7945aaf95e2ffa3a11b9ef1b21" - "74e7860401000000cd7db68b9ae586dacdb4070c50320427c4033d03a9fef22c636360d696be085a" - "007c499dd538d34574c038404923e75d0e09e2fcaefd545b010000007ced4f8b362956cd46646a0e" - "222160e5f700bb29a9feed37a2123c5b6a813387e99bb834a19da86c64fefab2548759d313a5b92d" - "c896c9a7010000000a5f6abd850b06e3d9662795ce2cc1cee01702aba9feaaa218dc091e9aa1477f" - "6fdc9830d7fbc95829a8838314dff34d24c332219a1b163a000000000e2f4f911a0dd8c052ed6b13" - "e2815a1a37002bdda9fef1018b587eed72bb58f000201f17bd99fcf72909ac9e7a55299e9bb4fd53" - "7666c05300000000ffa8e89871fb9510b900b2061d0c334b81022dd1a9fe4b0f9a60e0e289f08db3" - "00bafc4e095a3230fde69eec3af2e6d0bd68ab722cb749e2000000000eb4f73e75039fcf251b32fc" - "79685b05dd02aa9ba9fed2e40edb4d758fb554158ae5c7c00d58fbcc58fc4c2fd0d44ff1e51c3515" - "40b26f6600000000badfad0fe8fa8f3c1aad2a82530b7447d902f401a9fe2f4c329f997184c9d257" - "8e4cf9c48147507e1cd59ea1ae6da48b1eba6d16672d2fa500000000e276c5661a64192db44ce7af" - "5eb888e9eb03bb04a9fe943b101e1f553215abf8fbdf17c3641e447514ca565fb9b68c08a5e4351e" - "a2f5680800000000faa100fdfdd68d34be184524a6bdc8789c035178a9fe5bcc8da98489c55b8c12" - "87e6f7bb020ffdb0e612098610e52bb2a16a40089f3c39f200000000de745cb25851429b3bbfb501" - "68dfd04edf033542a9fe5f574c5cf8107ba282f4f983821918f93e742d08f0550b55726f2292362e"), - [1] = ("54270e46020000006a894e387625da376feecb80ac245409c058cc271d9c2f67179bff0644399ae7" - "9c64ed0432d599eaa660ac824b7fae389861419c7a899f2d010000009bf5a5d06776e66a9d80ab13" - "2e1ac921eb04adbb229df32e561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b" - "c4e1966800000000b3121cd5ef51e696c816290dbaee0e77264c82e1530ff5397c9125f59577d71c" - "4b258a005116d11354edff62ceaa458fc75a91c425a5927300000000c4458d70911714f1819aa46f" - "be143934c915ae6dec0fb124f264af67eed7c9a8c38b6b8ee2614344bea73223e59bf0193432e9fa" - "4cc43c5103000000c2aac1d67a909b297c703915312e9c3c5a64c40b20ea99d5ab90135fa636af44" - "64d210a256be75e0509c4477d19ac64b2e69220d12ea3b9501000000fb95e8332fdfff658483a2d0" - "39a7bf148e4c481e0bccc460e89f7abdb682380a62eae374835b4a49a2b980b6aba92da6409969aa" - "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ec5ffb112087b97cfcebbaeb0c565359" - "23f14874042a8aff1d1f8e5ec3cc13cc36bbe3c9bb0ec36f0000000064a2827b7cdd38d0314c178e" - "925b568ae566c4e9fefb24e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13" - "02e9b69000000000eacce745ea213cb1a84035b632ef3be60755afa2fed87470e9da55709427c226" - "d324d9a086ed27184b443181a5edc511c1507a142fea9bc10300000062fb81e733cff620ca7feefe" - "1933631e8e18f6d9fed62d2c6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196" - "9985d62700000000e491cbbbf9443fd6a77eb3c5bf64b671d6098dbffeff9b8306c83bc026977911" - "4bdade862c224f6f36506ebd455e679cef369bb8d514573f03000000f6932f6932d1d5f5d0ce2cf6" - "bf7a70ba5111a162fe92ec1b11d9172cf84a03478b82cdd8883b0f3fd6ed84d6c959e553b887edcd" - "6297dfa700000000fd0d53bb6215de4bb6f6f3d031a790287e7be9c1feb836c0a25ea71f5daaed4d" - "99eb2ebebe3c432fec7d3abc4ccf30d3aaaf02a41e0f515601000000f57609453df7803357ae7998" - "9870ccec3d4f0ad9000000000000000000000000000000019f05a16768cb3bc873b04d937a6ed8f7" - "ae8cb60b0300000038d856a40c67c3d4afa9c8649da90bd2ff4cf256000000000000000000000000" - "00000001955e1a13d1111b067acbce3e3bcdf38200154d12010000009edfa7a74c1c83c18e47935e" - "8354846e346e386a00000000000000000000000000000001ac26c7d41fd050bf8d85055bcfe3756c" - "a2f56808092069affaa100fdfdd68d34be184524a6bdc8789c575178000000000000000000000000" - "00000000020ffdb0e612098610e52bb2a16a40086f0e9e6903000000ec2e2924348352d715cf9d41" - "1ea012e5305a94b600000000000000000000000000000000ccd2f50c30706cae3fdcc2f0ee3d5e26" - "81ae55f0030000000aa20624fbab244735d67c61283031667b2274a1000000000000000000000000" - "0000000000459582f4ef43c780908872746e39efaefd545b020000007ced4f8b362956cd46646a0e" - "222160e5f749bb2900000000000000000000000000000000a19da86c64fefab2548759d313a5b92d" - "f05d7308020000009d82c856ba942455050f63f8282f34644466acf5000000000000000000000000" - "0000000069d9942f460bcea75481f25e307d0de96e91b91501000000be5b534e0a6527f67485ab35" - "aca0c7ee4167338500000000000000000000000000000000aefd4503189f7630248e501c9052c22b" - "5d68265d010000009b7a559be3acf4fc36fa6b513cf09e8b99729462000000000000000000000000" - "00000000b49185ad9b7d0070a36dd51323b2c542e5fe7d360100000066a3ae3939762df33a2d5506" - "0c78d551cb7410fd000000000000000000000000000000002c171ed15abbbe1cd911db7b56e8f3fe" - "9a6ce5d900000000a6a5ee19d6f63fe9cbdd01f7246f13b2055624a2000000000000000000000000" - "00000000ffe9d6812231bb02f922259b173c333cbae87d60000000005d31e5b2de252167c1c91b3a" - "36ba0c700e538477000000000000000000000000000000006ad0143a0554efb46b382ab0404cdf02" - "7c52a52d030000004e8e8bc17c01ac63eca0c1cfbb204132fb0813e4000000000000000000000000" - "00000000accae11f8045a80f855ebf3b772eb2b1d03bc90503000000eb760e3f004a2efc8ffc96b8" - "9b1bc8f415174e7700000000000000000000000000000000d149f074a378b881153ece68ade15cec" - "7666c05303000000ffa8e89871fb9510b900b2061d0c334b816a2dd1000000000000000000000000" - "00000000095a3230fde69eec3af2e6d0bd68ab7210613fc90200000016a31dc2e46a234558817151" - "b38fc9c9093c8d7100000000000000000000000000000000ae17bb4515856092b4d9e89b676761b2" - "57d6249b020000007dc7f17f666969e45c3baf7a119a65ee0b08fa9e000000000000000000000000" - "00000000b314f695bd82e9ea8891ed0a3d1b91cab56e03e0010000002f70249eefc67bce716c856e" - "3973dc42a1003be9000000000000000000000000000000006176f78a19bb8df1804c122fce5078c1" - "eecc89f601000000d635edea8ffe423d01883918039249f3984ab37e000000000000000000000000" - "000000001fb7c8307f06b4b29088f20d9ac676d5f90e54b501000000a779f37c30d2c5053c40ecae" - "210aeb0934392a2400000000000000000000000000000000df48f333ef467092818aa77d6732b678" - "bcd7be0700000000e45aaf61814f703b40125e6baf4648cbaa557b86000000000000000000000000" - "000000005ddb05f2a84e27c18a06e2c9cb29efdf50da49bf00000000d94beb259f9f0251becc9879" - "07879cca6857c7bb000000000000000000000000000000009402a4c2167721673f2b02b3256ead1f" - "a349e1cc00000000b879a45176740744fcb6a035339d504c972bd80d000000000000000000000000" - "000000009518862bc9a89192c1c8be55281a59d1ebfdcec30000000067a982be8a934f852cc3d4d8" - "2bc0ec7303579f8f00000000000000000000000000000000901f55f960c55c22f32099a8020bea2a" - "5b1aea3401000000c7c3523f76cf1ce34ca17a9a32f3f0f2187c4e48000000000000000000000000" - "00000002e6fbf952902383028e3b47f2111d891d536c75bb01000000720da9e07ac998b5690807d5" - "2617369ee16d4ca500000000000000000000ffff47d7b351a36b178aabc78a4a0d80684ee5dbf45b" - "17a4763102000000f9119c40b468b66331bca67a034f43340d2c683c00000000000000000000ffff" - "b24a5be9b10123e6e4f15ecd1dff045298cfee069c88b02a0300000004b0595daf37deb499996bfb" - "667f072dec755d9cdc8a11f4409bcfb3c167090e99d90a8543961b2ccd47724a3c460ba85183f4c5" - "a7afe28302000000d05ff720b16c73761a3fcb675219061ab466c79489b2cc6a883c146972decb8b" - "5adac8f5e6d66df46ea139107754dee62c36d2fcc7a4c65601000000cd625009a40aeb62a42b6a62" - "548e3c38b96620022753c5fbfaf1809becd2506315a6da29b2e94d3ab51fe0e9af98b180a36035fb" - "e36f90d401000000fd41fe47684b370b6ec6584d64496089574568ead4d1ae91c819bb068196d599" - "efde3246e43f5e7945aaf95e2ffa3a11b9ef1b21cdb3455c00000000c66af76e80f062ea1a7e0b7b" - "e218d067532d1b0487b8c3b99f2b8a1a8982c42f53700a3437c5156e072b2f2fb337c9cfe07c34dd" - "c896c9a7000000000a5f6abd850b06e3d9662795ce2cc1cee03202aba042aaa218dc091e9aa1477f" - "6fdc9830d7fbc95829a8838314dff34d24c332214d4647f5000000000f88e3759a08e7a56663861e" - "ea1bf42ccd1172f2ad063857ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec787bc45db" - "7625ca4a00000000e682f80f94315c45ef817264c89a736ed580d637b077ddad3108be69f9b97d05" - "c917ad6b10e693bb6e26f2ba90c8e909e85e20e5b0916e5d030000004a503a712350acbd48411f0d" - "c15d2f0f4952d345599706954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8" - "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa700b120523bd98f29b098b43cd68be6e" - "3f81268193fd637e585287dc6ca972a2cfb18b2b3cae864b0200000091921681c2162b9bfa40546c" - "db544e44df587c2790d3119a051bb2ee8192ac0cfa3abc1ea0ab3e7d75a2f42b50c6a36340132378" + [0] = ("8d1e69c83dc21056b973972c17a47631c169f11ff9119c40b407b6630000007a034f43340d01683c" + "0045097aea4a849f060ddf57b24a5be9b10123e6e4f15ecd9c88b02a06d0dc5504b0595daf37deb4" + "99996bfb667f072dec025d9cdc8a11f4409bcfb3c167090e00d90a8543961b2ccd47724a3c460ba8" + "5183f4c5daae7d63ecc8fb1103000000fcebbaeb0c56535923f14874042a8aff1d018e5e000000cd" + "36bbe3c9bb0ec36f00e007bb64a2827b7cdd38d0314c178e925b568ae56bc4e92931f2d303000000" + "50b8d10ab700cc3a7cf51be0403b654ba2026808092069affaa100fdfdd68d3400184524a6bdc878" + "9cf851782d895bcc8da98489c55b8c1230d2c50503000000210aeb0934cb2a247000b96d06992b6a" + "5803d399000000d2df48f333ef467092008aa77d6732b678cf844ef5a943825fcd743f59bd14c89b" + "df6f628a03000000f25d5895fb57fa60d9279fb2b8fcbac6ab0274f70000007bf6989d2d6a909e98" + "00f7add5df2c93e05459507b5d8c6a0f466554d2ae4d52a89427c2260300000086ed27184b443181" + "a5edc511c1507a141e03515600000033f57609453df7803300ae79989870ccec3def0ad92749e016" + "663fe6ee0228c1da5f5b481b0300000068cb3bc873b04d937a6ed8f7bc51e5406603d0980000008b" + "92d3adc69a84eb47002400207799416af1f0a086fbd7e2f7dea0077ae9c0c0d9f264af6703000000" + "c38b6b8ee2614344bea73223e59bf0193402e9fa0000002d8ec7e4b89bf5a5d00076e66a9d80ab13" + "2e1ac921eb76adbb229df32e561fa80aabf85f0c0300000008d33a2ea67776d7f88d69c8c7e3b2d3" + "c902f05400000020abb42316c533d9c90088189c471a1ff2f0ce3ff66e782110125ae2ed74993ade" + "507e308d030000008dab2dade2ab2fc9c56a7c86be5f96228e02f2f4000000b32b9c161c36b745ce" + "00d06352cb2c354434819f4b248eb2b8e3ff1a5ffc386da337c27c15030000007e5c53e755c90fa0" + "9e9653ddcb1b14d2750d8a130000001734b49e955cbcf46d006a1818053da43ec5085d1df74d1e28" + "7271092897e26251de485b1303000000957c22721583c453d15ade4386ffd00b3600858900000013" + "f09e601d88d4cb0c004d15c69e89f94ef7b30486591030639eb0e99f7f53702f3c84c4eb03000000" + "87780281d47a32f2235376239daeaacb3c02864b000000ae91921681c2162b9bfa40546cdb544e44" + "df2d7c2790d3119a051bb2ee8192ac0cfa3abc1e0300000075a2f42b50c6a36340132378b95c5931" + "3b03ba640000002069e66142e22753344fda8a3e0b841cf76391dd68269b53eccb44a831714acfb5" + "c442139803000000cee64b38b56e03e0f121014a2f70249eef027bce0000006f3973dc42a1003be9" + "4f89d8eeb1f14d985d5bbe6c10085f3d6176f78a19bb8df1449a774903000000ec852d59f3d17232" + "edd86ad6c3103a6884029aef00000082594f24a17dba1ee7e143fd52599364e13468f80fd514573f" + "b572671cf6932f6909f4ecf103000000a1c748288ae21e371051852c972ea7954012884a00000045" + "fd13a6826a4c619f9037d7a754655354c47cf0f551e96770a754ac19ef820fe0eafb1ac403000000" + "3aa9986700b2a2b3ed257b8b0489f48f0500c8cac72105b327335a25a364d1015ffac03089f45539" + "e1b0178462156cd6b83579b8da27345a4d9ca9d20300000074e786047d8683facd7db68b9ae586da" + "cd03070c00000027c4dd3d031e33f22c636360d696be085aad7c499dd538d34574c038404923e75d" + "36506ebd03000000ef369bb8ebfdcec3f8c8fa6867a982be8a054f85000000d82bc0ec7303f99f8f" + "e8d07c84965a316af2abb982b001ec16901f55f960c55c22c896c9a7030000000a5f6abd850b06e3" + "d9662795ce2cc1cee00002ab000000a318dc091e9aa1477f6fdc9830d7fbc95829a8838314dff34d" + "24c332219a1b163a8483a2d0030000008ed3481e0bccc460e89f7abdb682380a6202e3740000004b" + "a2b980b6aba92da6409969aabae87d60e00473755d31e5b2de252167c1c91b3a891715df03000000" + "edae579e2e10c7f7a22ba0340436242cbe11248c000000174632d4f28aa28c08f80dd50c6712dfb4" + "d184e820a28fa772a19cdf6dd1c173f6bc7ef08203000000ef60a8b540b26f66761d1f13badfad0f" + "e8038f3c1aad2a82530b7447d9c2f401012f2f4c329f997184c9d2578e4cf9c48147507e1cd59ea1" + "6e8e99cd03000000644f0e8ab3121cd5ef51e696c816290dba010e77000000e1530ff5397c9125f5" + "9577d71c4b258a005116d11354edff62ceaa458fc75a91c45cfd5ce603000000152f2b70eaef7443" + "e0f79ab6902dde53361dec71000000254ac9588319558041fc62ed564153e17478f8c3344d89c0bb" + "5408c1b7d17fb084b16c7376030000005219061ab4c5c79489b2cc6a883c1469721dcb8b5adac8f5" + "e6d66df46ea139107754dee62c36d2fc70ccc567df7a49b8a4c73cf5ffa4ffc8acdca10f03000000" + "a36b178aabc78a4a0d80684ee5dbf45bba0210fb9733cc24904f288262e6ea77a22fa5f863eaeefa" + "701d120523bd98f29b098b43cd68be6e37686b5e030000006215de4bb6f6f3d031a790287e45e9c1" + "8b0236c0a25ea71f5daaed4d99eb2ebebe3c432fec7d3abc4ccf30d3aaaf02a4f05d7308b67f91bf" + "0408423e03000000886dc91e2e5b0f0b65843fc961f7ad18010a3c67d9714f19f1e41c073164d93b" + "1171719c7f81773302d007c983566139c695e0f4b661ee6b3cf09e8b02000000e79c10acfce165e3" + "32a62384ec04e5bab40285ad00000071a36dd51323b2c54200154d12f86c260a9edfa7a74c1c83c1" + "8e47935e8354846e27c219630200000054830c68e44ea74b4f2b83f9b3bee14d86034c9e000000f6" + "abba4a32d635edea00fe423d01883918039249f398f9b37ea0914651b2a16e642c19a09202000000" + "92defa453a3efc240b9977e166bee737fe10670c0000004c323b59b4cd20eb7d00d38785c88ffad4" + "5e0dfe491d1ba96742c7b5e02b227122fcd32a7f0200000023a5223a2fb2c0764e08db62a3db07d0" + "900260b922a7b4a1565e63d3b610c9df00e32703b0ed3a7d0f8b13cd736aaa72021a381fb50e2c42" + "54270e46020000006a894e387625da376feecb80ac245409c005cc271d9c2f67179bff0644399ae7" + "0064ed0432d599eaa660ac824b7fae389861419c50da49bf8c3b371502000000710c030690f5f18e" + "a125dbf7d7e93bd65c01a56dfdcfc1155f236c8b9c79a62000660bbf024b03ff0a8e27c405e64244" + "57393c6fd7ac5f99544e4b2702000000a7f810d249adc17647176b7c07d856e3b806efebc09dd9d6" + "1966b7ae7412041d00fe7d361f2d5dfd66a3ae3939762df33a2d55060c78d551fcb6a03502000000" + "9726d80d9c2d89df765b4d9a130257dc5f19f03a0000002bc9a89192c1c8be55281a59d1fd338f35" + "7e75f8ce6e8cc34573654ea6624f138e8c45eb5002000000562dcd24a27c8212127da516b1091d1d" + "4f0008170000000d32d823b0617718eb0f3195e2fe917423d314ae73572ff808b373f5b193d661ed" + "e45aaf610200000040125e6baf4648cbaaa37b861e6d46dafe1f6ec400000053010911915ddb05f2" + "a84e27c18a06e2c9cb29efdf672d2fa57fc85ebbe276c566804c122f0100000059747cf23a1349c8" + "ab2dadc5a39411fd4f03116d000000316a553fc28cff0bcd00ebe434a7ae8ff040483e310819e3b1" + "0db116431ec6f76949ba9be3010000000c67c3d4afa9c8649da90bd2fff2f2568101870b0000004b" + "70c6d18d6242b356005e1a13d1111b067acbce3e3bcdf3824f1b134847ff26ba32d1d5f501000000" + "bf7a70ba5193a162bc92ec1b11d9172cf8020347000000d8883b0f3fd6ed84d60059e553b887edcd" + "61c2f5d2f381f843b809d259e499db7d2b092583010000009f9f0251becc987907879cca68fec7bb" + "bb008edf000000973d136caca5054b010002a4c2167721673f2b02b3256ead1f2fea9bc12f0cfca1" + "62fb81e701000000ca7feefe1933631e8e69f6d9d6962d2c6e0304f50000009e47150b9b14010469" + "003acb72bb89182f931121966e91b9153b36bc60be5b534e1dff04520100000036674cdefb57b9ae" + "54cfe8f42bf0e9381b00c4396de60f032bc7f34de295987100d4c0d4eb720c4ae3c9472e5f2da5d9" + "c5cfd9f2343b21363f81268101000000585287dc6ca972a2cfb18b2b2cb749e2e002544d0eb4f73e" + "75039fcf251b32fc00685b05ddd3aa9ba511d2e40edb4d758fb554158ae5c7c0fde69eec01000000" + "bd68ab722af14548b29572e504265f6c72003e2200000079e208ed2e2ebb07710046a54cadb245ea" + "fde6b02567f814de39dd8f88152a5845a4bab14801000000e6aae816430607ca0675e09818e9bbec" + "5900c5900000006ab08940f6fc3bbdc700090f756aae660b3b02239efc1fb23d24438387def0f4c6" + "cbb110fd01000000b9aca07b6e4925dd27943a272c171ed15a02be1c0000007b56e8f3fededea203" + "00a8042602a1f8e6f5657f3f9e3eb807cbad76451055edb81539ce410100000018238487a417f3da" + "01d99dae5f190096cc0e2cab0000005b2363d13edc5aa115165eb914d8fbe9a4fdb3d117d76b0de5" + "416f95e7bb75d72fc8f1853b010000000e2b00b667b7b16dae02dfa0c26e7c3add003bd3000000f1" + "2f2f3313d8469d09a92409c0b7f0318a575a4f8e061db3dd7fde25654a4059d5d8a4c5cf01000000" + "fcc2056cc1ff8e51bbce426cfa4b861cc70192be7b14e7ba9c15acb881ae55f0f512dc1c0aa20624" + "fbab244735d67c61283031667b2d74a103e0b1b10100000090a2312e17f1a35900459582f4ef43c7" + "80038872000000efa9a89f87189b1d0afcdf6d9be94030b34774d1d7dddedd9899f0f627be16965a" + "8704133101000000d9b4e3ce848518c4e3f6cbf25e5e1b990f026767000000bba23eb66e720da9e0" + "7ac998b5690807d52617369ee1af4ca5a6a95581af5fd7ce732d803e010000001a0dd8c052ed6b13" + "e2815a1a37a52bddd106f101000000ed72bb58f0e9201f17bd99fcf72909ac9e7a55299e9bb4fd53" + "bc7417940fcffe3fb60291ea01000000def85b75827b26e8a0bef8675b29a197b71ecceaf5f1bb12" + "c535256ab0916e5d58099ffc4a503a712350acbd48411f0dc15d2f0f49dad34565dbc8a900000000" + "b550c5db56189572f32f4af595d785d5c117aad4000000f08bd7a09bdd214443008cd9bf66ec305a" + "225a0c71b2ce78bb16104c8eaf18c56587e6f7bb00000000e612098610e52bb2a16a4008aefd545b" + "7a019db50000008b362956cd46646a0e002160e5f769bb295766ed37a2123c5b6a813387e99bb834" + "b7a350f900000000fa7db4532545666937c22a3b6e9686dcc403cbee000000529ed8901d7a0165fe" + "00744edb6ba04f146e4c699341b95f41d9c1e67743efb54e4fc812bd000000006d064126666befbb" + "98c39e7cc282208fef007ff40000009751adcb22fd50793f007a3c7dadd1c86e759d0c90bc512e13" + "7dcef5e4a27985bd348352d7000000001ea012e5307294b6e346dac428ab20075e05e38d000000e6" + "ccd2f50c30706cae00dcc2f0ee3d5e2612ea3b95e1d2a5d0fb95e8332fdfff650a6527f600000000" + "aca0c7ee4197338581cafd63e66e3a23d61e257800000020aefd4503189f7630008e501c9052c22b" + "9f3c39f24b0572b1de745cb25851429bf32099a8000000004d4647f53b1921af0f88e3759a08e7a5" + "661f861eea1bf42ccd3c72f2ad0638570082ac5180101be4f85cef468ea086ea9aaafdc388811ec7" + "3bbfb50100000000df6b3542f4bb5f574c5cf8107ba282f4f9038219000000772d08f0550b55726f" + "2292362e5d68265d9f98c82d9b7a559be3acf4fc36fa6b513722d9b7000000001fb7c8307f06b4b2" + "9088f20d9ac676d55b00ea34000000fec7c3523f76cf1ce34ca17a9a32f3f0f218424e48836dd1cb" + "504e03d53e1124c5d9e1825f00000000284a024b7f9e85ff73e050a2cd89fd276e00cb8800000037" + "4ec3127c750fabdd69121aec129cdc690fcc00ac2daa755e5292cd32dc096cd5438a72e100000000" + "2495a609675344f7e2e3a5ebaec3c85f0a1242f7000000c70345132a61eda239d9d083c3bf085387" + "046ef8a36f0e9e6982d15956ec2e2924ae6da48b00000000bcfe97e1761e12457ad742d3d7320a4f" + "8800f47f5dd0cf69cb22840f09b3fe7749509cb6752b2cee30b7e7736a0afc9879ea40e69710fc9f" + "57d6249b000000007dc7f17f666969e45c3baf7a119a65ee0b1ffa9e000000f2de6dc8dfcf2a3ad3" + "bbcc0fa3b314f695bd82e9ea8891ed0a3d1b91caae8cb60bab90135f0000000064d210a256be75e0" + "509c4477d19ac64b2e03220d6c0fc09c25d6534c809829af4a9df58dbfef186d416f3a1ef170d10f" + "2c932e655203d82a87bb24e500000000e9cf9f6df9615189f78ee815b2781ea55c1755876b6a3f13" + "922b6cb8360feaac1d5c302df59dd32a677e5a3af4fe297f608df061d45d864db442548600000000" + "b0216050ed7afd741024e8365ea1aa11f90397c0c0ec468e9de5a9caf493472201e48d163106cc39" + "7446a33fdc335c8b152c507a0a3c8e7cc67b2e48b47003c81785d3ffdbd9a617857ae8126b028c0e" + "3e0f5354a9feb690c628d048eacce74500213cb1a84035b632ef3be6070dafa202187470e9da5570" + "40e55b8b03000000c0cfb2b4386bec092fa5757ecde9348bda012ebda9fea6aa667224dae775e5cc" + "0041aca2ff0f576767d3648102b61886b62f22ca33478e1ff9531cd903000000d241989477a363d5" + "5ad47b8a11090df8da00d88da9fed75bc404156225b7eedefc2b44574b15e2ae496ad01bf007eacb" + "0a28aec8682825108c54dda903000000d43e6505621b9a7f0422ae3fc8979ee0c400d794a9fe895e" + "5bfe47c6f53a54e01b0c1d89414d94d0e02d2ec587fc330003781b0f294a2b7387bc45db03000000" + "36bd39a44e8e8bc17c01ac63eca0c1cfbb004132a9fe13e4862573c2303dd1d65c7b2cffeca6d1ad" + "accae11f8045a80f855ebf3b772eb2b1182c028703000000ec4501a1e27acfee7a8588ffd5a06cae" + "4201e595a9fed65d67c624f285d7174177a47579dda0b6ead1e1b34901eaedd5bcd7be07d78e6222" + "d611d7b103000000f9f23b5f1570a4648a0ff80dd9a37a82e10120aaa9fe258532449deb7afb217d" + "58c0bb20de6f36d664873c5be82c5361358730bcde43782d87b8c3b9020000008982c42f53700a34" + "37c5156e072b2f2fb300c9cfa9fe34ddb3980deb14ab7b5a001de913df1424ce3ee1d76fc1ea76e9" + "8c9dfc78997ab53ec386e9c60200000010ee58bdc75183609985d627b6a4b1cbe402cbbba9fe3fd6" + "a77eb3c5bf64b67100f18dbf463f9b8306c83bc0269779114bdade862c224f6fdd65aeb202000000" + "902383028e3b47f2111d891d9f3c2c88cd007619a9fe44e644d7cf1d06b2b115d882549e62c12fba" + "5b1cdec48d10bbb13b8313d8cd2a24d3be46bae5020000006665a268abfe8a41d149f074a378b881" + "1500ce68a9fe5cec25a59273e7519f34c4458d70911714f1819aa46fbe143934c933ae6dec0fb124" + "59970695020000000d3c723b91adb0da7c4aa7e7eb5a15bcde035fb9a9fe1fd84cc43c510385b9a4" + "c2aac1d67a909b297c703915312e9c3c5a06c40b20ea99d5078a806d020000008bbfc9723107f3f5" + "b2cd3dbddc74e26de905e48da9feb0313e7f8933e315130fa9a84c8288f83f6e70b1646d8026e9f1" + "704f1b16286ba2da72437f9b01000000ec9e71ee4b951507beb42b3473cef00e89015f24a9fe2333" + "02ad3b82d639272e0046eadfbd2146bf8cfdb205f90e54b58a6ee136a779f37c0d58fbcc01000000" + "d0d44ff1e51c35157666c05348e6f507ff01e898a9fe9510b900b2061d0c334b009f2dd1cdf64b0f" + "9a60e0e289f08db3e4bafc4e095a3230b4d9e89b010000006855676c277cf1bd017c9148da5892bb" + "4903c3a7a9fe689d3391ce7d3fd48469005f0c233dbdd2a97835df1f1782dee88487fd0db5971b46" + "9d82c85601000000050f63f8282f3464443cacf59c6379a44b0392f4a9fea98cb121c19c69d9942f" + "000bcea75481f25e307d0de9a7afe283bd2f22ead05ff72045aaf95e01000000b9ef1b2110613fc9" + "f90adac616a31dc2e4002345a9fe7151b38fc9c909cf8d71e64b0bd761fdf274fc5dca8ce1bd1871" + "ae17bb451585609236ba0c700100000040318db913a353edc66efc068829c0e66a17143aa9feefb4" + "6b382ab0404cdf029a6ce5d906837dbca6a5ee19d6f63fe9cbdd01f7246f13b2050424a200000000" + "a029c896b4132bd895072cfeffe9d6812200bb02a9fe259b173c333cd03bc90500b899afeb760e3f" + "004a2efc8ffc96b89b1bc8f415bd4e77c7a4c65600000000cd625009a40aeb62a42b6a62548e3c38" + "b9022002a9fec5fbfaf1809becd2506300a6da29b2e94d3ab51fe0e9af98b180a36035fb6297dfa7" + "0e09e2fc00000000cc807cdce682f80f94315c45ef817264c802736ea9fed637b077ddad3108be69" + "f9b97d05c917ad6b10e693bb6e26f2ba90c8e909e85e20e591bf821c00000000c37ff4283a631480" + "91acc42cac4ef3ad3702fec7a9fe62475588456e4ead7cbda620a3abae816e34cba1d74f18d1c4ed" + "b99c0e49bcb946f71a64192d000000005eb888e9eb37bb046686943b101e1f553203abf8a9fe17c3" + "641e447514ca565fb9b68c08a5e4351ea349e1ccf662e058b879a45176740744a19da86c00000000" + "548759d313a5b92de36f90d4bc8b496afd03fe47a9fe370b6ec6584d64496089570968ead4d1ae91" + "c819bb068196d599efde3246e43f5e7934f0386a000000004064f6b021281e235f94ae20ac26c7d4" + "1f0350bfa9fe055bcfe3756ccdb3455c60ca7b11c66af76e80f062ea1a7e0b7be218d067530e1b04"), + [1] = ("4d9ca9d20300000074e786047d8683facd7db68b9ae586dacd0b070c50320427c4dd3d031e33f22c" + "636360d696be085aad7c499dd538d34574c038404923e75d30d2c50503000000210aeb0934cb2a24" + "7000b96d06992b6a5875d3995b9663d2df48f333ef467092818aa77d6732b678cf844ef5a943825f" + "cd743f59bd14c89bdf6f628a02000000f25d5895fb57fa60d9279fb2b8fcbac6ab6674f7a6df7979" + "f6989d2d6a909e986ff7add5df2c93e05459507b5d8c6a0f466554d2ae4d52a8de485b1301000000" + "957c22721583c453d15ade4386ffd00b36518589be395312f09e601d88d4cb0c2f4d15c69e89f94e" + "f7b30486591030639eb0e99f7f53702fa4bab14801000000e6aae816430607ca0675e09818e9bbec" + "5966c59068baf76ab08940f6fc3bbdc7f6090f756aae660b3b02239efc1fb23d24438387def0f4c6" + "36ba0c700000000040318db913a353edc66efc068829c0e66a32143a0554efb46b382ab0404cdf02" + "9a6ce5d906837dbca6a5ee19d6f63fe9cbdd01f7246f13b254270e46000000006a894e387625da37" + "6feecb80ac245409c06bcc271d9c2f67179bff0644399ae79c64ed0432d599eaa660ac824b7fae38" + "9861419c50da49bf5cfd5ce600000000152f2b70eaef7443e0f79ab6902dde53362fec7127e19f25" + "4ac9588319558041fc62ed564153e17478f8c3344d89c0bb5408c1b7d17fb084b16c737600000000" + "5219061ab4c5c79489b2cc6a883c14697280cb8b5adac8f5e6d66df46ea139107754dee62c36d2fc" + "70ccc567df7a49b8a4c73cf5ffa4ffc83722d9b7000000001fb7c8307f06b4b29088f20d9ac676d5" + "5b2dea34648cabfec7c3523f76cf1ce34ca17a9a32f3f0f218424e48836dd1cb504e03d53e1124c5" + "87bb24e500000000e9cf9f6df9615189f78ee815b2781ea55c5f55876b6a3f13922b6cb8360feaac" + "1d5c302df59dd32a677e5a3af4fe297f608df061d45d864d348352d7000000001ea012e5307294b6" + "e346dac428ab20075e11e38dd16f8ee4ccd2f50c30706cae3fdcc2f0ee3d5e2612ea3b95e1d2a5d0" + "fb95e8332fdfff65fa3abc1e0200000075a2f42b50c6a36340132378b95c59313b58ba64dbe99620" + "69e66142e22753344fda8a3e0b841cf76391dd68269b53eccb44a831714acfb5f32099a800000000" + "4d4647f53b1921af0f88e3759a08e7a56655861efedbf42ccd3c72f2ad063857ca82ac5180101be4" + "f85cef468ea086ea9aaafdc388811ec7c442139803000000cee64b38b56e03e0f121014a2f70249e" + "ef217bcefeec856e3973dc42a1003be94f89d8eeb1f14d985d5bbe6c10085f3d6176f78a19bb8df1" + "09f4ecf100000000a1c748288ae21e371051852c972ea7954066884afed7b044fd13a6826a4c619f" + "9037d7a754655354c47cf0f551e96770a754ac19ef820fe0732d803e000000001a0dd8c052ed6b13" + "e2815a1a37a52bddd109f101fed87eed72bb58f0e9201f17bd99fcf72909ac9e7a55299e9bb4fd53" + "bc7417940fcffe3feafb1ac4020000003aa9986700b2a2b3ed257b8b0489f48f055ac8cafea105b3" + "27335a25a364d1015ffac03089f45539e1b0178462156cd6b83579b8da27345a544e4b2700000000" + "a7f810d249adc17647176b7c07d856e3b87befebfe9dd9d61966b7ae7412041de5fe7d361f2d5dfd" + "66a3ae3939762df33a2d55060c78d551dd65aeb202000000902383028e3b47f2111d891d9f3c2c88" + "cd1676190000000000000000000000000000000162c12fba5b1cdec48d10bbb13b8313d8cd2a24d3" + "27c219630100000054830c68e44ea74b4f2b83f9b3bee14d866e4c9e000000000000000000000000" + "0000000101883918039249f398f9b37ea0914651b2a16e64a19da86c64fefab2548759d313a5b92d" + "e36f90d4bc8b496afd57fe4700000000000000000000000000000000d4d1ae91c819bb068196d599" + "efde3246e43f5e79507e308d030000008dab2dade2ab2fc9c56a7c86be5f96228e05f2f400000000" + "000000000000000000000000cb2c354434819f4b248eb2b8e3ff1a5ffc386da38483a2d003000000" + "8ed3481e0bccc460e89f7abdb682380a6208e37400000000000000000000000000000000bae87d60" + "e00473755d31e5b2de252167c1c91b3a87e6f7bb03000000e612098610e52bb2a16a4008aefd545b" + "7a219db500000000000000000000000000000000f769bb295766ed37a2123c5b6a813387e99bb834" + "bc7ef08203000000ef60a8b540b26f66761d1f13badfad0fe8178f3c000000000000000000000000" + "00000000329f997184c9d2578e4cf9c48147507e1cd59ea13f81268103000000585287dc6ca972a2" + "cfb18b2b2cb749e2e022544d00000000000000000000000000000000ddd3aa9ba511d2e40edb4d75" + "8fb554158ae5c7c09d82c85602000000050f63f8282f3464443cacf59c6379a44b8092f400000000" + "0000000000000000000000005481f25e307d0de9a7afe283bd2f22ead05ff72045aaf95e02000000" + "b9ef1b2110613fc9f90adac616a31dc2e44923450000000000000000000000000000000061fdf274" + "fc5dca8ce1bd1871ae17bb4515856092cbb110fd02000000b9aca07b6e4925dd27943a272c171ed1" + "5a66be1c0000000000000000000000000000000002a1f8e6f5657f3f9e3eb807cbad76451055edb8" + "5f5b481b0100000068cb3bc873b04d937a6ed8f7bc51e5406674d098000000000000000000000000" + "000000007799416af1f0a086fbd7e2f7dea0077ae9c0c0d9ecc8fb1101000000fcebbaeb0c565359" + "23f14874042a8aff1d008e5e0000000000000000000000000000000064a2827b7cdd38d0314c178e" + "925b568ae56bc4e9438a72e1000000002495a609675344f7e2e3a5ebaec3c85f0a5742f700000000" + "000000000000000000000000bf085387046ef8a36f0e9e6982d15956ec2e2924891715df00000000" + "edae579e2e10c7f7a22ba0340436242cbe53248c000000000000000000000000000000006712dfb4" + "d184e820a28fa772a19cdf6dd1c173f6e45aaf610000000040125e6baf4648cbaaa37b861e6d46da" + "fe556ec4000000000000000000000000000000008a06e2c9cb29efdf672d2fa57fc85ebbe276c566" + "8c45eb5003000000562dcd24a27c8212127da516b1091d1d4f5f0817000000000000000000000000" + "00000000fe917423d314ae73572ff808b373f5b193d661edbe46bae5030000006665a268abfe8a41" + "d149f074a378b8811526ce6800000000000000000000000000000000911714f1819aa46fbe143934" + "c933ae6dec0fb124fcd32a7f0300000023a5223a2fb2c0764e08db62a3db07d0907360b900000000" + "000000000000000000000000b0ed3a7d0f8b13cd736aaa72021a381fb50e2c421dff045203000000" + "36674cdefb57b9ae54cfe8f42bf0e9381b04c43900000000000000000000000000000000eb720c4a" + "e3c9472e5f2da5d9c5cfd9f2343b21363c84c4eb0200000087780281d47a32f2235376239daeaacb" + "3c54864b00000000000000000000000000000000db544e44df2d7c2790d3119a051bb2ee8192ac0c" + "804c122f0100000059747cf23a1349c8ab2dadc5a39411fd4f60116d000000000000000000000000" + "00000000a7ae8ff040483e310819e3b10db116431ec6f76987b8c3b9010000008982c42f53700a34" + "37c5156e072b2f2fb372c9cf00000000000000000000000000000000df1424ce3ee1d76fc1ea76e9" + "8c9dfc78997ab53ef9531cd901000000d241989477a363d55ad47b8a11090df8da18d88d00000000" + "0000000000000000000000004b15e2ae496ad01bf007eacb0a28aec86828251065dbc8a900000000" + "b550c5db56189572f32f4af595d785d5c10eaad40000000000000000000000000000000066ec305a" + "225a0c71b2ce78bb16104c8eaf18c56557d6249b000000007dc7f17f666969e45c3baf7a119a65ee" + "0b34fa9e00000000000000000000000000000000b314f695bd82e9ea8891ed0a3d1b91caae8cb60b" + "fcb6a035000000009726d80d9c2d89df765b4d9a130257dc5f2bf03a000000000000000000000000" + "00000000fd338f357e75f8ce6e8cc34573654ea6624f138e91bf821c03000000c37ff4283a631480" + "91acc42cac4ef3ad373bfec700000000000000000000000000000002ae816e34cba1d74f18d1c4ed" + "b99c0e49bcb946f734f0386a010000004064f6b021281e235f94ae20ac26c7d41f2a50bf00000000" + "000000000000000000000002c66af76e80f062ea1a7e0b7be218d067530e1b04c386e9c601000000" + "10ee58bdc75183609985d627b6a4b1cbe413cbbb00000000000000000000000000000002463f9b83" + "06c83bc0269779114bdade862c224f6f1a64192d010000005eb888e9eb37bb046686943b101e1f55" + "3273abf800000000000000000000000000000002a5e4351ea349e1ccf662e058b879a45176740744" + "32d1d5f503000000bf7a70ba5193a162bc92ec1b11d9172cf83a0347000000000000000000000000" + "00000002b887edcd61c2f5d2f381f843b809d259e499db7dfde69eec01000000bd68ab722af14548" + "b29572e504265f6c726d3e2200000000000000000000ffffdb46a54cadb245eafde6b02567f814de" + "39dd8f88152a584562fb81e702000000ca7feefe1933631e8e69f6d9d6962d2c6e5804f57239c09c" + "47150b9b14010469823acb72bb89182f931121966e91b9153b36bc60be5b534e6e8e99cd01000000" + "644f0e8ab3121cd5ef51e696c816290dba040e7726d082e1530ff5397c9125f59577d71c4b258a00" + "5116d11354edff62ceaa458fc75a91c4b4d9e89b010000006855676c277cf1bd017c9148da5892bb" + "4945c3a7ed55689d3391ce7d3fd48469845f0c233dbdd2a97835df1f1782dee88487fd0db5971b46" + "2c19a0920000000092defa453a3efc240b9977e166bee737fe1c670c439a644c323b59b4cd20eb7d" + "d3d38785c88ffad45e0dfe491d1ba96742c7b5e02b227122ab90135f0300000064d210a256be75e0" + "509c4477d19ac64b2e64220d6c0fc09c25d6534c809829af4a9df58dbfef186d416f3a1ef170d10f" + "2c932e655203d82a59970695030000000d3c723b91adb0da7c4aa7e7eb5a15bcde525fb98b841fd8" + "4cc43c510385b9a4c2aac1d67a909b297c703915312e9c3c5a06c40b20ea99d58c3b371502000000" + "710c030690f5f18ea125dbf7d7e93bd65c03a56dfdcfc1155f236c8b9c79a6205f660bbf024b03ff" + "0a8e27c405e6424457393c6fd7ac5f9940e55b8b01000000c0cfb2b4386bec092fa5757ecde9348b" + "da4c2ebd4f78a6aa667224dae775e5ccd141aca2ff0f576767d3648102b61886b62f22ca33478e1f" + "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c954f054e93f8120abb42316c533d9c9" + "4d88189c471a1ff2f0ce3ff66e782110125ae2ed74993adeae6da48b00000000bcfe97e1761e1245" + "7ad742d3d7320a4f8815f47f5dd0cf69cb22840f09b3fe7749509cb6752b2cee30b7e7736a0afc98" + "79ea40e69710fc9f0408423e00000000886dc91e2e5b0f0b65843fc961f7ad18014c3c67d9714f19" + "f1e41c073164d93b1171719c7f81773302d007c983566139c695e0f4b661ee6bc896c9a703000000" + "0a5f6abd850b06e3d9662795ce2cc1cee00802abfec2aaa218dc091e9aa1477f6fdc9830d7fbc958" + "29a8838314dff34d24c332219a1b163a3bbfb50103000000df6b3542f4bb5f574c5cf8107ba282f4" + "f9188219fef93e742d08f0550b55726f2292362e5d68265d9f98c82d9b7a559be3acf4fc36fa6b51" + "c8f1853b030000000e2b00b667b7b16dae02dfa0c26e7c3add113bd3febfa0f02f2f3313d8469d09" + "a92409c0b7f0318a575a4f8e061db3dd7fde25654a4059d5b442548601000000b0216050ed7afd74" + "1024e8365ea1aa11f92797c0feac468e9de5a9caf493472201e48d163106cc397446a33fdc335c8b" + "152c507a0a3c8e7cacdca10f01000000a36b178aabc78a4a0d80684ee5dbf45bba3010fbfeb3cc24" + "904f288262e6ea77a22fa5f863eaeefa701d120523bd98f29b098b43cd68be6e37c27c1500000000" + "7e5c53e755c90fa09e9653ddcb1b14d2757f8a13feac3c1734b49e955cbcf46d256a1818053da43e" + "c5085d1df74d1e287271092897e2625103e0b1b10100000090a2312e17f1a35900459582f4ef43c7" + "807b887200000000000000000000000000000001e94030b34774d1d7dddedd9899f0f627be16965a" + "87bc45db0100000036bd39a44e8e8bc17c01ac63eca0c1cfbb4f4132000000000000000000000000" + "00000001eca6d1adaccae11f8045a80f855ebf3b772eb2b1449a774903000000ec852d59f3d17232" + "edd86ad6c3103a68844c9aef00000000000000000000000000000001599364e13468f80fd514573f" + "b572671cf6932f69d9e1825f03000000284a024b7f9e85ff73e050a2cd89fd276e0acb8800000000" + "000000000000000000000001129cdc690fcc00ac2daa755e5292cd32dc096cd572437f9b02000000" + "ec9e71ee4b951507beb42b3473cef00e896b5f2400000000000000000000000000000001bd2146bf" + "8cfdb205f90e54b58a6ee136a779f37c2931f2d30300000050b8d10ab700cc3a7cf51be0403b654b" + "a242680800000000000000000000000000000000a6bdc8789cf851782d895bcc8da98489c55b8c12" + "37686b5e030000006215de4bb6f6f3d031a790287e45e9c18b7136c0000000000000000000000000" + "00000000ec7d3abc4ccf30d3aaaf02a4f05d7308b67f91bfc7a4c65603000000cd625009a40aeb62" + "a42b6a62548e3c38b96a200200000000000000000000000000000000b2e94d3ab51fe0e9af98b180" + "a36035fb6297dfa78704133102000000d9b4e3ce848518c4e3f6cbf25e5e1b990f13676700000000" + "000000000000000000000000690807d52617369ee1af4ca5a6a95581af5fd7ceb7a350f902000000" + "fa7db4532545666937c22a3b6e9686dcc43ccbee000000000000000000000000000000006ba04f14" + "6e4c699341b95f41d9c1e67743efb54e3cf09e8b01000000e79c10acfce165e332a62384ec04e5ba" + "b46785ad00000000000000000000000000000000f86c260a9edfa7a74c1c83c18e47935e8354846e" + "4fc812bd010000006d064126666befbb98c39e7cc282208fef4a7ff4000000000000000000000000" + "00000000add1c86e759d0c90bc512e137dcef5e4a27985bdd8a4c5cf01000000fcc2056cc1ff8e51" + "bbce426cfa4b861cc73992be000000000000000000000000000000000aa20624fbab244735d67c61" + "283031667b2d74a10d58fbcc01000000d0d44ff1e51c35157666c05348e6f507ff72e89800000000" + "000000000000000000000000cdf64b0f9a60e0e289f08db3e4bafc4e095a32302b09258301000000" + "9f9f0251becc987907879cca68fec7bbbb4d8edf0000000000000000000000000000000016772167" + "3f2b02b3256ead1f2fea9bc12f0cfca1078a806d000000008bbfc9723107f3f5b2cd3dbddc74e26d" + "e956e48d0000000000000000000000000000000088f83f6e70b1646d8026e9f1704f1b16286ba2da" + "0a6527f600000000aca0c7ee4197338581cafd63e66e3a23d6572578000000000000000000000000" + "000000009052c22b9f3c39f24b0572b1de745cb25851429b8c54dda900000000d43e6505621b9a7f" + "0422ae3fc8979ee0c400d79400000000000000000000000000000000414d94d0e02d2ec587fc3300" + "03781b0f294a2b73050424a203000000a029c896b4132bd895072cfeffe9d6812232bb0200000000" + "000000000000000000000000eb760e3f004a2efc8ffc96b89b1bc8f415bd4e77d611d7b103000000" + "f9f23b5f1570a4648a0ff80dd9a37a82e10c20aa00000000000000000000000000000000de6f36d6" + "64873c5be82c5361358730bcde43782df264af6703000000c38b6b8ee2614344bea73223e59bf019" + "345ae9fa000000000000000000000000000000009d80ab132e1ac921eb76adbb229df32e561fa80a" + "49ba9be3020000000c67c3d4afa9c8649da90bd2fff2f2568108870b000000000000000000000000" + "00000000d1111b067acbce3e3bcdf3824f1b134847ff26ba0e09e2fc01000000cc807cdce682f80f" + "94315c45ef817264c83c736e00000000000000000000000000000000c917ad6b10e693bb6e26f2ba" + "90c8e909e85e20e5182c028701000000ec4501a1e27acfee7a8588ffd5a06cae4228e59500000000" + "000000000000000000000000dda0b6ead1e1b34901eaedd5bcd7be07d78e62229427c22601000000" + "86ed27184b443181a5edc511c1507a141e4a5156000000000000000000000000000000009870ccec" + "3def0ad92749e016663fe6ee0228c1da1539ce410000000018238487a417f3da01d99dae5f190096" + "cc292cab00000000000000000000000000000000d8fbe9a4fdb3d117d76b0de5416f95e7bb75d72f" + "b60291ea00000000def85b75827b26e8a0bef8675b29a197b71bccea000000000000000000000000" + "000000004a503a712350acbd48411f0dc15d2f0f49dad3458d1e69c801000000b973972c17a47631" + "c169f11ff9119c40b47cb66300000000000000000000000000000002ea4a849f060ddf57b24a5be9" + "b10123e6e4f15ecdc67b2e48000000001785d3ffdbd9a617857ae8126b028c0e3e4b535400000000" + "000000000000000000000002a84035b632ef3be6070dafa202187470e9da55709c88b02a02000000" + "04b0595daf37deb499996bfb667f072dec2c5d9c00000000000000000000ffff99d90a8543961b2c" + "cd47724a3c460ba85183f4c5daae7d6336506ebd00000000ef369bb8ebfdcec3f8c8fa6867a982be" + "8a534f8500000000000000000000ffffe8d07c84965a316af2abb982b001ec16901f55f960c55c22"), + [2] = ("df6f628a02000000f25d5895fb57fa60d9279fb2b8fcbac6ab6674f7a6df7979f6989d2d6a909e98" + "6ff7add5df2c93e05459507b5d8c6a0f466554d2ae4d52a8fa3abc1e0200000075a2f42b50c6a363" + "40132378b95c59313b58ba64dbe9962069e66142e22753344fda8a3e0b841cf76391dd68269b53ec" + "cb44a831714acfb554270e46000000006a894e387625da376feecb80ac245409c06bcc271d9c2f67" + "179bff0644399ae79c64ed0432d599eaa660ac824b7fae389861419c50da49bfb16c737600000000" + "5219061ab4c5c79489b2cc6a883c14697280cb8b5adac8f5e6d66df46ea139107754dee62c36d2fc" + "70ccc567df7a49b8a4c73cf5ffa4ffc83722d9b7000000001fb7c8307f06b4b29088f20d9ac676d5" + "5b2dea34648cabfec7c3523f76cf1ce34ca17a9a32f3f0f218424e48836dd1cb504e03d53e1124c5" + "87bb24e500000000e9cf9f6df9615189f78ee815b2781ea55c5f55876b6a3f13922b6cb8360feaac" + "1d5c302df59dd32a677e5a3af4fe297f608df061d45d864d4d9ca9d20300000074e786047d8683fa" + "cd7db68b9ae586dacd0b070c50320427c4dd3d031e33f22c636360d696be085aad7c499dd538d345" + "74c038404923e75d30d2c50503000000210aeb0934cb2a247000b96d06992b6a5875d3995b9663d2" + "df48f333ef467092818aa77d6732b678cf844ef5a943825fcd743f59bd14c89bde485b1301000000" + "957c22721583c453d15ade4386ffd00b36518589be395312f09e601d88d4cb0c2f4d15c69e89f94e" + "f7b30486591030639eb0e99f7f53702fa4bab14801000000e6aae816430607ca0675e09818e9bbec" + "5966c59068baf76ab08940f6fc3bbdc7f6090f756aae660b3b02239efc1fb23d24438387def0f4c6" + "36ba0c700000000040318db913a353edc66efc068829c0e66a32143a0554efb46b382ab0404cdf02" + "9a6ce5d906837dbca6a5ee19d6f63fe9cbdd01f7246f13b25cfd5ce600000000152f2b70eaef7443" + "e0f79ab6902dde53362fec7127e19f254ac9588319558041fc62ed564153e17478f8c3344d89c0bb" + "5408c1b7d17fb084348352d7000000001ea012e5307294b6e346dac428ab20075e11e38dd16f8ee4" + "ccd2f50c30706cae3fdcc2f0ee3d5e2612ea3b95e1d2a5d0fb95e8332fdfff65c442139803000000" + "cee64b38b56e03e0f121014a2f70249eef217bcefeec856e3973dc42a1003be94f89d8eeb1f14d98" + "5d5bbe6c10085f3d6176f78a19bb8df109f4ecf100000000a1c748288ae21e371051852c972ea795" + "4066884afed7b044fd13a6826a4c619f9037d7a754655354c47cf0f551e96770a754ac19ef820fe0" + "f32099a8000000004d4647f53b1921af0f88e3759a08e7a56655861efedbf42ccd3c72f2ad063857" + "ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec7732d803e000000001a0dd8c052ed6b13" + "e2815a1a37a52bddd109f101fed87eed72bb58f0e9201f17bd99fcf72909ac9e7a55299e9bb4fd53" + "bc7417940fcffe3f544e4b2700000000a7f810d249adc17647176b7c07d856e3b87befebfe9dd9d6" + "1966b7ae7412041de5fe7d361f2d5dfd66a3ae3939762df33a2d55060c78d551eafb1ac402000000" + "3aa9986700b2a2b3ed257b8b0489f48f055ac8cafea105b327335a25a364d1015ffac03089f45539" + "e1b0178462156cd6b83579b8da27345add65aeb202000000902383028e3b47f2111d891d9f3c2c88" + "cd1676190000000000000000000000000000000162c12fba5b1cdec48d10bbb13b8313d8cd2a24d3" + "27c219630100000054830c68e44ea74b4f2b83f9b3bee14d866e4c9e000000000000000000000000" + "0000000101883918039249f398f9b37ea0914651b2a16e648483a2d0030000008ed3481e0bccc460" + "e89f7abdb682380a6208e37400000000000000000000000000000000bae87d60e00473755d31e5b2" + "de252167c1c91b3a8c45eb5003000000562dcd24a27c8212127da516b1091d1d4f5f081700000000" + "000000000000000000000000fe917423d314ae73572ff808b373f5b193d661edbe46bae503000000" + "6665a268abfe8a41d149f074a378b8811526ce6800000000000000000000000000000000911714f1" + "819aa46fbe143934c933ae6dec0fb124fcd32a7f0300000023a5223a2fb2c0764e08db62a3db07d0" + "907360b900000000000000000000000000000000b0ed3a7d0f8b13cd736aaa72021a381fb50e2c42" + "1dff04520300000036674cdefb57b9ae54cfe8f42bf0e9381b04c439000000000000000000000000" + "00000000eb720c4ae3c9472e5f2da5d9c5cfd9f2343b21363c84c4eb0200000087780281d47a32f2" + "235376239daeaacb3c54864b00000000000000000000000000000000db544e44df2d7c2790d3119a" + "051bb2ee8192ac0c804c122f0100000059747cf23a1349c8ab2dadc5a39411fd4f60116d00000000" + "000000000000000000000000a7ae8ff040483e310819e3b10db116431ec6f76987b8c3b901000000" + "8982c42f53700a3437c5156e072b2f2fb372c9cf00000000000000000000000000000000df1424ce" + "3ee1d76fc1ea76e98c9dfc78997ab53eecc8fb1101000000fcebbaeb0c56535923f14874042a8aff" + "1d008e5e0000000000000000000000000000000064a2827b7cdd38d0314c178e925b568ae56bc4e9" + "f9531cd901000000d241989477a363d55ad47b8a11090df8da18d88d000000000000000000000000" + "000000004b15e2ae496ad01bf007eacb0a28aec86828251065dbc8a900000000b550c5db56189572" + "f32f4af595d785d5c10eaad40000000000000000000000000000000066ec305a225a0c71b2ce78bb" + "16104c8eaf18c56557d6249b000000007dc7f17f666969e45c3baf7a119a65ee0b34fa9e00000000" + "000000000000000000000000b314f695bd82e9ea8891ed0a3d1b91caae8cb60b891715df00000000" + "edae579e2e10c7f7a22ba0340436242cbe53248c000000000000000000000000000000006712dfb4" + "d184e820a28fa772a19cdf6dd1c173f6fcb6a035000000009726d80d9c2d89df765b4d9a130257dc" + "5f2bf03a00000000000000000000000000000000fd338f357e75f8ce6e8cc34573654ea6624f138e" + "a19da86c64fefab2548759d313a5b92de36f90d4bc8b496afd57fe47000000000000000000000000" + "00000000d4d1ae91c819bb068196d599efde3246e43f5e79507e308d030000008dab2dade2ab2fc9" + "c56a7c86be5f96228e05f2f400000000000000000000000000000000cb2c354434819f4b248eb2b8" + "e3ff1a5ffc386da387e6f7bb03000000e612098610e52bb2a16a4008aefd545b7a219db500000000" + "000000000000000000000000f769bb295766ed37a2123c5b6a813387e99bb834bc7ef08203000000" + "ef60a8b540b26f66761d1f13badfad0fe8178f3c00000000000000000000000000000000329f9971" + "84c9d2578e4cf9c48147507e1cd59ea13f81268103000000585287dc6ca972a2cfb18b2b2cb749e2" + "e022544d00000000000000000000000000000000ddd3aa9ba511d2e40edb4d758fb554158ae5c7c0" + "9d82c85602000000050f63f8282f3464443cacf59c6379a44b8092f4000000000000000000000000" + "000000005481f25e307d0de9a7afe283bd2f22ead05ff72045aaf95e02000000b9ef1b2110613fc9" + "f90adac616a31dc2e44923450000000000000000000000000000000061fdf274fc5dca8ce1bd1871" + "ae17bb4515856092cbb110fd02000000b9aca07b6e4925dd27943a272c171ed15a66be1c00000000" + "00000000000000000000000002a1f8e6f5657f3f9e3eb807cbad76451055edb85f5b481b01000000" + "68cb3bc873b04d937a6ed8f7bc51e5406674d098000000000000000000000000000000007799416a" + "f1f0a086fbd7e2f7dea0077ae9c0c0d9438a72e1000000002495a609675344f7e2e3a5ebaec3c85f" + "0a5742f700000000000000000000000000000000bf085387046ef8a36f0e9e6982d15956ec2e2924" + "e45aaf610000000040125e6baf4648cbaaa37b861e6d46dafe556ec4000000000000000000000000" + "000000008a06e2c9cb29efdf672d2fa57fc85ebbe276c56691bf821c03000000c37ff4283a631480" + "91acc42cac4ef3ad373bfec700000000000000000000000000000002ae816e34cba1d74f18d1c4ed" + "b99c0e49bcb946f732d1d5f503000000bf7a70ba5193a162bc92ec1b11d9172cf83a034700000000" + "000000000000000000000002b887edcd61c2f5d2f381f843b809d259e499db7d34f0386a01000000" + "4064f6b021281e235f94ae20ac26c7d41f2a50bf00000000000000000000000000000002c66af76e" + "80f062ea1a7e0b7be218d067530e1b04c386e9c60100000010ee58bdc75183609985d627b6a4b1cb" + "e413cbbb00000000000000000000000000000002463f9b8306c83bc0269779114bdade862c224f6f" + "1a64192d010000005eb888e9eb37bb046686943b101e1f553273abf8000000000000000000000000" + "00000002a5e4351ea349e1ccf662e058b879a45176740744fde69eec01000000bd68ab722af14548" + "b29572e504265f6c726d3e2200000000000000000000ffffdb46a54cadb245eafde6b02567f814de" + "39dd8f88152a5845ab90135f0300000064d210a256be75e0509c4477d19ac64b2e64220d6c0fc09c" + "25d6534c809829af4a9df58dbfef186d416f3a1ef170d10f2c932e655203d82a5997069503000000" + "0d3c723b91adb0da7c4aa7e7eb5a15bcde525fb98b841fd84cc43c510385b9a4c2aac1d67a909b29" + "7c703915312e9c3c5a06c40b20ea99d562fb81e702000000ca7feefe1933631e8e69f6d9d6962d2c" + "6e5804f57239c09c47150b9b14010469823acb72bb89182f931121966e91b9153b36bc60be5b534e" "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c03a56dfdcfc1155f236c8b9c79a620" - "5f660bbf024b03ff0a8e27c405e6424457393c6fdac12ebd01000000667224dae775e5ccd141aca2" - "ff0f57676754648102b61886b62f22ca33478e1f891715df7fc6a902edae579e2e10c7f7a22ba034" - "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c951f054e93f8120abb42316c533d9c9" - "4d88189c471a1ff2f0ce3ff66e782110125ae2eddaae7d6300000000c0982b283aa9986700b2a2b3" - "ed257b8b042ff48f053ec8cac72105b327335a25a364d1015ffac03089f45539e1b0178462156cd6" - "6855676c00000000017c9148da5892bb4951c3a7ed55689d336bce7d3fd48469845f0c233dbdd2a9" - "7835df1f1782dee88487fd0db5971b46b7a350f998c39e7c00000000efc57ff447036b9551adcb22" - "fd50793f791c3c7dadd1c86e759d0c90bc512e137dcef5e4a27985bd8d1e69c83dc21056b973972c" - "bc51e540030000006c3d048a92d3adc69a84eb47622400207708416afef0a086fbd7e2f7dea0077a" - "e9c0c0d9c386e9c6e6a2cbfa10ee58bdc75183600422ae3f03000000c43bd794c697895e5bfe47c6" - "f53a54e01b211d89fecd94d0e02d2ec587fc330003781b0f294a2b73c4421398f4de67e9cee64b38" - "69121aec010000000fcc00ac2daa755e5292cd32dc096cd5d830c5cffe91ee1cfcc2056cc1ff8e51" - "bbce426cfa4b861cc78592be7b14e7ba9c15acb836674cde0200000054cfe8f42bf0e9381ba2c439" - "6de60f032b5af34dfe959871c0d4c0d4eb720c4ae3c9472e5f2da5d9c5cfd9f2343b21362c19a092" - "b2cd3dbd01000000e958e48d2ab8b0313e7f8933e315130fa9274c82feb83f6e70b1646d8026e9f1" - "704f1b16286ba2dabc7ef0820c91ed33ef60a8b5bcfe97e1000000007ad742d3d7320a4f880cf47f" - "5dd0cf69cb7f840ffeb3fe7749509cb6752b2cee30b7e7736a0afc9879ea40e69710fc9f6e8e99cd" - "1dce2f83020000003a3efc240b9977e166bee737fe73670c436b644c000000000000000000000000" - "000000015e0dfe491d1ba96742c7b5e02b227122cf844ef501000000cd743f59bd14c89b5cfd5ce6" - "e82e7854157b2b700000000000000000000000000000000127e19f254ac9588319558041fc62ed56" - "b83579b80300000072437f9b6245de39ec9e71ee4b951507be0a2b34000000000000000000000000" - "00000001d639272e4a46eadfbd2146bf8cfdb205b3980deb020000006c1de913df1424ce3ee1d76f" - "c1ea76e98c16fc7800000000000000000000000000000001e44ea74b4f2b83f9b3bee14d861a4c9e" - "0436242c03000000ee3fbc164632d4f28aa28c08f80dd50c6705dfb4000000000000000000000000" - "00000000078a806d329c9b008bbfc9723107f3f52af145480300000004265f6c72923e22594f3f79" - "e208ed2e2e7107710000000000000000000000000000000039dd8f88152a58454d9ca9d20f45dfa7" - "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f6042f061000000000000000000000000" - "000000008ae21e371051852c972ea7954079884a9a1b163a030000000e2f4f911a0dd8c052ed6b13" - "e2815a1a37322bdd00000000000000000000000000000000bd99fcf72909ac9e7a55299e9bb4fd53" - "9f3c2c8803000000f51f44e644d7cf1d06b2b115d882549e62042fba000000000000000000000000" - "000000004fc812bd2f8a59d96d064126666befbbf257b044030000006a4c619f9037d7a754655354" - "c47cf0f5512167700000000000000000000000000000000050b8d10ab700cc3a7cf51be0403b654b" - "74e7860402000000cd7db68b9ae586dacdb4070c50320427c4803d03000000000000000000000000" - "00000000d538d34574c038404923e75d0e09e2fcfc1519de010000002545666937c22a3b6e9686dc" - "c405cbee504dc252000000000000000000000000000000006e4c699341b95f41d9c1e67743efb54e" - "6aae660b01000000fc1fb23d24438387def0f4c6544e4b275d4a7146000000000000000000000000" - "00000000b883efebc09dd9d61966b7ae7412041dd7ac5f9901000000822a272bec4501a1e27acfee" - "7a8588ffd5286cae0000000000000000000000000000000077a47579dda0b6ead1e1b34901eaedd5" - "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e0e00b6000000000000000000000000" - "000000005e7fa0f02f2f3313d8469d09a92409c0b4d54acb00000000a417f3da01d99dae5f190096" - "cc582cabd534d15a00000000000000000000000000000000fdb3d117d76b0de5416f95e7bb75d72f" - "fc2b445700000000496ad01bf007eacb0a28aec868282510b61b91ea000000000000000000000000" - "000000005b29a197b7b3cceaf5f1bb12c535256a74993ade03000000e62112898dab2dade2ab2fc9" - "c56a7c86be73962200000000000000000000000000000000bdd06352cb2c354434819f4b248eb2b8" - "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a0c1480000000000000000000000000" - "000000005588456e4ead7cbda620a3abae816e34e3ff1a5f03000000b4425486c619147eb0216050" - "ed7afd74105fe83600000000000000000000000000000000f493472201e48d163106cc397446a33f" - "bc74179403000000438a72e12ae0436a2495a609675344f7e226a5eb000000000000000000000000" - "0000000061eda239d9d083c3bf085387046ef8a34153e174020000004d89c0bb5408c1b7d17fb084" - "d9e1825fa613e1af000000000000000000000000000000006e08cb887464b9344ec3127c750fabdd" - "6c0fc09c02000000809829af4a9df58dbfef186d416f3a1ef154d10f000000000000000000000000" - "0000000087780281d47a32f2235376239daeaacba9a89f8701000000fcdf6d9be94030b34774d1d7" - "dddedd989972f62700000000000000000000000000000000848518c4e3f6cbf25e5e1b990fc46767" - "fd338f35010000006e8cc34573654ea6624f138ef9531cd9361802e4000000000000000000000000" - "00000000da40d88d40a6d75bc404156225b7eede2cb749e2010000000eb4f73e75039fcf251b32fc" - "79685b05dd3caa9b000000000000000000000000000000000d58fbcc58fc4c2fd0d44ff1e51c3515" - "b7f0318a01000000061db3dd7fde25654a4059d565dbc8a91e004457000000000000000000000000" - "00000000c1b5aad487c192f08bd7a09bdd21444359747cf200000000ab2dadc5a39411fd4ff1116d" - "478987316a293fc2000000000000000000000000000000000819e3b10db116431ec6f7691539ce41" - "b95c593100000000dbe9962069e66142e22753344fda8a3e0b001cf7000000000000000000000000" - "000000008c54dda9ac3bb1b1d43e6505621b9a7f672d2fa501000000e276c5661a64192db44ce7af" - "5eb888e9eb73bb0400000000000000000000000000000002641e447514ca565fb9b68c08a5e4351e" - "9f3c39f201000000de745cb25851429b3bbfb50168dfd04edf2a3542000000000000000000000000" - "0000000218f93e742d08f0550b55726f2292362e70ccc56700000000a4c73cf5ffa4ffc8a4bab148" - "74afdf54e64be8160000000000000000000000000000000268baf76ab08940f6fc3bbdc7f6090f75" - "4f1b134803000000449a774929e86716ec852d59f3d17232ed3a6ad6000000000000000000000000" - "000000027dba1ee7e143fd52599364e13468f80f40b26f6603000000badfad0fe8fa8f3c1aad2a82" - "530b7447d93bf401000000000000000000000000000000028147507e1cd59ea1ae6da48b1eba6d16" - "dedea2030100000002a1f8e6f5657f3f9e3eb807cbad76451013edb8000000000000000000000000" - "00000002d9279fb2b8fcbac6abea74f7a6df7979f6989d2d000000006ff7add5df2c93e05459507b" - "5d8c6a0f465354d200000000000000000000ffff1785d3ffdbd9a617857ae8126b028c0e3e295354"), - [2] = ("4cc43c5103000000c2aac1d67a909b297c703915312e9c3c5a64c40b20ea99d5ab90135fa636af44" - "64d210a256be75e0509c4477d19ac64b2e69220d54270e46020000006a894e387625da376feecb80" - "ac245409c058cc271d9c2f67179bff0644399ae79c64ed0432d599eaa660ac824b7fae389861419c" - "12ea3b9501000000fb95e8332fdfff658483a2d039a7bf148e4c481e0bccc460e89f7abdb682380a" - "62eae374835b4a49a2b980b6aba92da6409969aa7a899f2d010000009bf5a5d06776e66a9d80ab13" - "2e1ac921eb04adbb229df32e561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b" - "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ec5ffb112087b97cfcebbaeb0c565359" - "23f14874042a8aff1d1f8e5ec3cc13cc36bbe3c9c4e1966800000000b3121cd5ef51e696c816290d" - "baee0e77264c82e1530ff5397c9125f59577d71c4b258a005116d11354edff62ceaa458fc75a91c4" - "25a5927300000000c4458d70911714f1819aa46fbe143934c915ae6dec0fb124f264af67eed7c9a8" - "c38b6b8ee2614344bea73223e59bf0193432e9fa2fea9bc10300000062fb81e733cff620ca7feefe" - "1933631e8e18f6d9fed62d2c6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196" - "9985d62700000000e491cbbbf9443fd6a77eb3c5bf64b671d6098dbffeff9b8306c83bc026977911" - "4bdade862c224f6f36506ebd455e679cef369bb8bb0ec36f0000000064a2827b7cdd38d0314c178e" - "925b568ae566c4e9fefb24e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13" - "02e9b69000000000eacce745ea213cb1a84035b632ef3be60755afa2fed87470e9da55709427c226" - "d324d9a086ed27184b443181a5edc511c1507a14d514573f03000000f6932f6932d1d5f5d0ce2cf6" - "bf7a70ba5111a162fe92ec1b11d9172cf84a03478b82cdd8883b0f3fd6ed84d6c959e553b887edcd" - "6297dfa700000000fd0d53bb6215de4bb6f6f3d031a790287e7be9c1feb836c0a25ea71f5daaed4d" - "99eb2ebebe3c432fec7d3abc4ccf30d3aaaf02a4ae8cb60b0300000038d856a40c67c3d4afa9c864" - "9da90bd2ff4cf25600000000000000000000000000000001955e1a13d1111b067acbce3e3bcdf382" - "00154d12010000009edfa7a74c1c83c18e47935e8354846e346e386a000000000000000000000000" - "00000001ac26c7d41fd050bf8d85055bcfe3756c1e0f515601000000f57609453df7803357ae7998" - "9870ccec3d4f0ad9000000000000000000000000000000019f05a16768cb3bc873b04d937a6ed8f7" - "7c52a52d030000004e8e8bc17c01ac63eca0c1cfbb204132fb0813e4000000000000000000000000" - "00000000accae11f8045a80f855ebf3b772eb2b1d03bc90503000000eb760e3f004a2efc8ffc96b8" - "9b1bc8f415174e7700000000000000000000000000000000d149f074a378b881153ece68ade15cec" - "6f0e9e6903000000ec2e2924348352d715cf9d411ea012e5305a94b6000000000000000000000000" - "00000000ccd2f50c30706cae3fdcc2f0ee3d5e267666c05303000000ffa8e89871fb9510b900b206" - "1d0c334b816a2dd100000000000000000000000000000000095a3230fde69eec3af2e6d0bd68ab72" - "81ae55f0030000000aa20624fbab244735d67c61283031667b2274a1000000000000000000000000" - "0000000000459582f4ef43c780908872746e39ef10613fc90200000016a31dc2e46a234558817151" - "b38fc9c9093c8d7100000000000000000000000000000000ae17bb4515856092b4d9e89b676761b2" - "57d6249b020000007dc7f17f666969e45c3baf7a119a65ee0b08fa9e000000000000000000000000" - "00000000b314f695bd82e9ea8891ed0a3d1b91ca6e91b91501000000be5b534e0a6527f67485ab35" - "aca0c7ee4167338500000000000000000000000000000000aefd4503189f7630248e501c9052c22b" - "5d68265d010000009b7a559be3acf4fc36fa6b513cf09e8b99729462000000000000000000000000" - "00000000b49185ad9b7d0070a36dd51323b2c542b56e03e0010000002f70249eefc67bce716c856e" - "3973dc42a1003be9000000000000000000000000000000006176f78a19bb8df1804c122fce5078c1" - "e5fe7d360100000066a3ae3939762df33a2d55060c78d551cb7410fd000000000000000000000000" - "000000002c171ed15abbbe1cd911db7b56e8f3feeecc89f601000000d635edea8ffe423d01883918" - "039249f3984ab37e000000000000000000000000000000001fb7c8307f06b4b29088f20d9ac676d5" - "f90e54b501000000a779f37c30d2c5053c40ecae210aeb0934392a24000000000000000000000000" - "00000000df48f333ef467092818aa77d6732b678bcd7be0700000000e45aaf61814f703b40125e6b" - "af4648cbaa557b86000000000000000000000000000000005ddb05f2a84e27c18a06e2c9cb29efdf" - "50da49bf00000000d94beb259f9f0251becc987907879cca6857c7bb000000000000000000000000" - "000000009402a4c2167721673f2b02b3256ead1fa349e1cc00000000b879a45176740744fcb6a035" - "339d504c972bd80d000000000000000000000000000000009518862bc9a89192c1c8be55281a59d1" - "bae87d60000000005d31e5b2de252167c1c91b3a36ba0c700e538477000000000000000000000000" - "000000006ad0143a0554efb46b382ab0404cdf02ebfdcec30000000067a982be8a934f852cc3d4d8" - "2bc0ec7303579f8f00000000000000000000000000000000901f55f960c55c22f32099a8020bea2a" - "a2f56808092069affaa100fdfdd68d34be184524a6bdc8789c575178000000000000000000000000" - "00000000020ffdb0e612098610e52bb2a16a4008aefd545b020000007ced4f8b362956cd46646a0e" - "222160e5f749bb2900000000000000000000000000000000a19da86c64fefab2548759d313a5b92d" - "f05d7308020000009d82c856ba942455050f63f8282f34644466acf5000000000000000000000000" - "0000000069d9942f460bcea75481f25e307d0de99a6ce5d900000000a6a5ee19d6f63fe9cbdd01f7" - "246f13b2055624a200000000000000000000000000000000ffe9d6812231bb02f922259b173c333c" - "5b1aea3401000000c7c3523f76cf1ce34ca17a9a32f3f0f2187c4e48000000000000000000000000" - "00000002e6fbf952902383028e3b47f2111d891d17a4763102000000f9119c40b468b66331bca67a" - "034f43340d2c683c00000000000000000000ffffb24a5be9b10123e6e4f15ecd1dff045298cfee06" - "536c75bb01000000720da9e07ac998b5690807d52617369ee16d4ca500000000000000000000ffff" - "47d7b351a36b178aabc78a4a0d80684ee5dbf45bb0916e5d030000004a503a712350acbd48411f0d" - "c15d2f0f4952d345599706954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8" - "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa700b120523bd98f29b098b43cd68be6e" - "3f81268193fd637e585287dc6ca972a2cfb18b2b3cae864b0200000091921681c2162b9bfa40546c" - "db544e44df587c2790d3119a051bb2ee8192ac0cfa3abc1ea0ab3e7d75a2f42b50c6a36340132378" - "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c03a56dfdcfc1155f236c8b9c79a620" - "5f660bbf024b03ff0a8e27c405e6424457393c6fdac12ebd01000000667224dae775e5ccd141aca2" - "ff0f57676754648102b61886b62f22ca33478e1f891715df7fc6a902edae579e2e10c7f7a22ba034" - "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c951f054e93f8120abb42316c533d9c9" - "4d88189c471a1ff2f0ce3ff66e782110125ae2edc7a4c65601000000cd625009a40aeb62a42b6a62" - "548e3c38b96620022753c5fbfaf1809becd2506315a6da29b2e94d3ab51fe0e9af98b180a36035fb" - "daae7d6300000000c0982b283aa9986700b2a2b3ed257b8b042ff48f053ec8cac72105b327335a25" - "a364d1015ffac03089f45539e1b0178462156cd66855676c00000000017c9148da5892bb4951c3a7" - "ed55689d336bce7d3fd48469845f0c233dbdd2a97835df1f1782dee88487fd0db5971b46b7a350f9" - "cdb3455c00000000c66af76e80f062ea1a7e0b7be218d067532d1b0487b8c3b99f2b8a1a8982c42f" - "53700a3437c5156e072b2f2fb337c9cfe07c34ddc896c9a7000000000a5f6abd850b06e3d9662795" - "ce2cc1cee03202aba042aaa218dc091e9aa1477f6fdc9830d7fbc95829a8838314dff34d24c33221" - "98c39e7c00000000efc57ff447036b9551adcb22fd50793f791c3c7dadd1c86e759d0c90bc512e13" - "7dcef5e4a27985bd8d1e69c83dc21056b973972c7625ca4a00000000e682f80f94315c45ef817264" - "c89a736ed580d637b077ddad3108be69f9b97d05c917ad6b10e693bb6e26f2ba90c8e909e85e20e5" - "9c88b02a0300000004b0595daf37deb499996bfb667f072dec755d9cdc8a11f4409bcfb3c167090e" - "99d90a8543961b2ccd47724a3c460ba85183f4c5a7afe28302000000d05ff720b16c73761a3fcb67" - "5219061ab466c79489b2cc6a883c146972decb8b5adac8f5e6d66df46ea139107754dee62c36d2fc" - "e36f90d401000000fd41fe47684b370b6ec6584d64496089574568ead4d1ae91c819bb068196d599" - "efde3246e43f5e7945aaf95e2ffa3a11b9ef1b214d4647f5000000000f88e3759a08e7a56663861e" - "ea1bf42ccd1172f2ad063857ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec787bc45db" - "0422ae3f03000000c43bd794c697895e5bfe47c6f53a54e01b211d89fecd94d0e02d2ec587fc3300" - "03781b0f294a2b73c4421398f4de67e9cee64b38bc51e540030000006c3d048a92d3adc69a84eb47" - "622400207708416afef0a086fbd7e2f7dea0077ae9c0c0d9c386e9c6e6a2cbfa10ee58bdc7518360" - "36674cde0200000054cfe8f42bf0e9381ba2c4396de60f032b5af34dfe959871c0d4c0d4eb720c4a" - "e3c9472e5f2da5d9c5cfd9f2343b21362c19a092b2cd3dbd01000000e958e48d2ab8b0313e7f8933" - "e315130fa9274c82feb83f6e70b1646d8026e9f1704f1b16286ba2dabc7ef0820c91ed33ef60a8b5" - "bcfe97e1000000007ad742d3d7320a4f880cf47f5dd0cf69cb7f840ffeb3fe7749509cb6752b2cee" - "30b7e7736a0afc9879ea40e69710fc9f6e8e99cd69121aec010000000fcc00ac2daa755e5292cd32" - "dc096cd5d830c5cffe91ee1cfcc2056cc1ff8e51bbce426cfa4b861cc78592be7b14e7ba9c15acb8" - "b83579b80300000072437f9b6245de39ec9e71ee4b951507be0a2b34000000000000000000000000" - "00000001d639272e4a46eadfbd2146bf8cfdb205b3980deb020000006c1de913df1424ce3ee1d76f" - "c1ea76e98c16fc7800000000000000000000000000000001e44ea74b4f2b83f9b3bee14d861a4c9e" - "cf844ef501000000cd743f59bd14c89b5cfd5ce6e82e7854157b2b70000000000000000000000000" - "0000000127e19f254ac9588319558041fc62ed561dce2f83020000003a3efc240b9977e166bee737" - "fe73670c436b644c000000000000000000000000000000015e0dfe491d1ba96742c7b5e02b227122" - "2af145480300000004265f6c72923e22594f3f79e208ed2e2e710771000000000000000000000000" - "0000000039dd8f88152a58454d9ca9d20f45dfa774993ade03000000e62112898dab2dade2ab2fc9" - "c56a7c86be73962200000000000000000000000000000000bdd06352cb2c354434819f4b248eb2b8" - "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f6042f061000000000000000000000000" - "000000008ae21e371051852c972ea7954079884a9f3c2c8803000000f51f44e644d7cf1d06b2b115" - "d882549e62042fba000000000000000000000000000000004fc812bd2f8a59d96d064126666befbb" - "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a0c1480000000000000000000000000" - "000000005588456e4ead7cbda620a3abae816e34e3ff1a5f03000000b4425486c619147eb0216050" - "ed7afd74105fe83600000000000000000000000000000000f493472201e48d163106cc397446a33f" - "f257b044030000006a4c619f9037d7a754655354c47cf0f551216770000000000000000000000000" - "0000000050b8d10ab700cc3a7cf51be0403b654bbc74179403000000438a72e12ae0436a2495a609" - "675344f7e226a5eb0000000000000000000000000000000061eda239d9d083c3bf085387046ef8a3" - "4153e174020000004d89c0bb5408c1b7d17fb084d9e1825fa613e1af000000000000000000000000" - "000000006e08cb887464b9344ec3127c750fabdd6c0fc09c02000000809829af4a9df58dbfef186d" - "416f3a1ef154d10f0000000000000000000000000000000087780281d47a32f2235376239daeaacb" - "74e7860402000000cd7db68b9ae586dacdb4070c50320427c4803d03000000000000000000000000" - "00000000d538d34574c038404923e75d0e09e2fca9a89f8701000000fcdf6d9be94030b34774d1d7" - "dddedd989972f62700000000000000000000000000000000848518c4e3f6cbf25e5e1b990fc46767" - "fd338f35010000006e8cc34573654ea6624f138ef9531cd9361802e4000000000000000000000000" - "00000000da40d88d40a6d75bc404156225b7eede2cb749e2010000000eb4f73e75039fcf251b32fc" - "79685b05dd3caa9b000000000000000000000000000000000d58fbcc58fc4c2fd0d44ff1e51c3515" - "6aae660b01000000fc1fb23d24438387def0f4c6544e4b275d4a7146000000000000000000000000" - "00000000b883efebc09dd9d61966b7ae7412041db7f0318a01000000061db3dd7fde25654a4059d5" - "65dbc8a91e00445700000000000000000000000000000000c1b5aad487c192f08bd7a09bdd214443" - "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e0e00b6000000000000000000000000" - "000000005e7fa0f02f2f3313d8469d09a92409c0b4d54acb00000000a417f3da01d99dae5f190096" - "cc582cabd534d15a00000000000000000000000000000000fdb3d117d76b0de5416f95e7bb75d72f" - "59747cf200000000ab2dadc5a39411fd4ff1116d478987316a293fc2000000000000000000000000" - "000000000819e3b10db116431ec6f7691539ce41b95c593100000000dbe9962069e66142e2275334" - "4fda8a3e0b001cf7000000000000000000000000000000008c54dda9ac3bb1b1d43e6505621b9a7f" - "fc2b445700000000496ad01bf007eacb0a28aec868282510b61b91ea000000000000000000000000" - "000000005b29a197b7b3cceaf5f1bb12c535256a0436242c03000000ee3fbc164632d4f28aa28c08" - "f80dd50c6705dfb400000000000000000000000000000000078a806d329c9b008bbfc9723107f3f5" - "9a1b163a030000000e2f4f911a0dd8c052ed6b13e2815a1a37322bdd000000000000000000000000" - "00000000bd99fcf72909ac9e7a55299e9bb4fd53fc1519de010000002545666937c22a3b6e9686dc" - "c405cbee504dc252000000000000000000000000000000006e4c699341b95f41d9c1e67743efb54e" - "d7ac5f9901000000822a272bec4501a1e27acfee7a8588ffd5286cae000000000000000000000000" - "0000000077a47579dda0b6ead1e1b34901eaedd54f1b134803000000449a774929e86716ec852d59" - "f3d17232ed3a6ad6000000000000000000000000000000027dba1ee7e143fd52599364e13468f80f" - "40b26f6603000000badfad0fe8fa8f3c1aad2a82530b7447d93bf401000000000000000000000000" - "000000028147507e1cd59ea1ae6da48b1eba6d16dedea2030100000002a1f8e6f5657f3f9e3eb807" - "cbad76451013edb800000000000000000000000000000002d9279fb2b8fcbac6abea74f7a6df7979" - "672d2fa501000000e276c5661a64192db44ce7af5eb888e9eb73bb04000000000000000000000000" - "00000002641e447514ca565fb9b68c08a5e4351e9f3c39f201000000de745cb25851429b3bbfb501" - "68dfd04edf2a35420000000000000000000000000000000218f93e742d08f0550b55726f2292362e" - "70ccc56700000000a4c73cf5ffa4ffc8a4bab14874afdf54e64be816000000000000000000000000" - "0000000268baf76ab08940f6fc3bbdc7f6090f75f6989d2d000000006ff7add5df2c93e05459507b" - "5d8c6a0f465354d200000000000000000000ffff1785d3ffdbd9a617857ae8126b028c0e3e295354"), + "5f660bbf024b03ff0a8e27c405e6424457393c6fd7ac5f996e8e99cd01000000644f0e8ab3121cd5" + "ef51e696c816290dba040e7726d082e1530ff5397c9125f59577d71c4b258a005116d11354edff62" + "ceaa458fc75a91c440e55b8b01000000c0cfb2b4386bec092fa5757ecde9348bda4c2ebd4f78a6aa" + "667224dae775e5ccd141aca2ff0f576767d3648102b61886b62f22ca33478e1fabf85f0c01000000" + "08d33a2ea67776d7f88d69c8c7e3b2d3c954f054e93f8120abb42316c533d9c94d88189c471a1ff2" + "f0ce3ff66e782110125ae2ed74993adeb4d9e89b010000006855676c277cf1bd017c9148da5892bb" + "4945c3a7ed55689d3391ce7d3fd48469845f0c233dbdd2a97835df1f1782dee88487fd0db5971b46" + "ae6da48b00000000bcfe97e1761e12457ad742d3d7320a4f8815f47f5dd0cf69cb22840f09b3fe77" + "49509cb6752b2cee30b7e7736a0afc9879ea40e69710fc9f2c19a0920000000092defa453a3efc24" + "0b9977e166bee737fe1c670c439a644c323b59b4cd20eb7dd3d38785c88ffad45e0dfe491d1ba967" + "42c7b5e02b2271220408423e00000000886dc91e2e5b0f0b65843fc961f7ad18014c3c67d9714f19" + "f1e41c073164d93b1171719c7f81773302d007c983566139c695e0f4b661ee6bc896c9a703000000" + "0a5f6abd850b06e3d9662795ce2cc1cee00802abfec2aaa218dc091e9aa1477f6fdc9830d7fbc958" + "29a8838314dff34d24c332219a1b163a3bbfb50103000000df6b3542f4bb5f574c5cf8107ba282f4" + "f9188219fef93e742d08f0550b55726f2292362e5d68265d9f98c82d9b7a559be3acf4fc36fa6b51" + "c8f1853b030000000e2b00b667b7b16dae02dfa0c26e7c3add113bd3febfa0f02f2f3313d8469d09" + "a92409c0b7f0318a575a4f8e061db3dd7fde25654a4059d5acdca10f01000000a36b178aabc78a4a" + "0d80684ee5dbf45bba3010fbfeb3cc24904f288262e6ea77a22fa5f863eaeefa701d120523bd98f2" + "9b098b43cd68be6e37c27c15000000007e5c53e755c90fa09e9653ddcb1b14d2757f8a13feac3c17" + "34b49e955cbcf46d256a1818053da43ec5085d1df74d1e287271092897e26251b442548601000000" + "b0216050ed7afd741024e8365ea1aa11f92797c0feac468e9de5a9caf493472201e48d163106cc39" + "7446a33fdc335c8b152c507a0a3c8e7c449a774903000000ec852d59f3d17232edd86ad6c3103a68" + "844c9aef00000000000000000000000000000001599364e13468f80fd514573fb572671cf6932f69" + "d9e1825f03000000284a024b7f9e85ff73e050a2cd89fd276e0acb88000000000000000000000000" + "00000001129cdc690fcc00ac2daa755e5292cd32dc096cd572437f9b02000000ec9e71ee4b951507" + "beb42b3473cef00e896b5f2400000000000000000000000000000001bd2146bf8cfdb205f90e54b5" + "8a6ee136a779f37c87bc45db0100000036bd39a44e8e8bc17c01ac63eca0c1cfbb4f413200000000" + "000000000000000000000001eca6d1adaccae11f8045a80f855ebf3b772eb2b103e0b1b101000000" + "90a2312e17f1a35900459582f4ef43c7807b887200000000000000000000000000000001e94030b3" + "4774d1d7dddedd9899f0f627be16965a050424a203000000a029c896b4132bd895072cfeffe9d681" + "2232bb0200000000000000000000000000000000eb760e3f004a2efc8ffc96b89b1bc8f415bd4e77" + "d611d7b103000000f9f23b5f1570a4648a0ff80dd9a37a82e10c20aa000000000000000000000000" + "00000000de6f36d664873c5be82c5361358730bcde43782df264af6703000000c38b6b8ee2614344" + "bea73223e59bf019345ae9fa000000000000000000000000000000009d80ab132e1ac921eb76adbb" + "229df32e561fa80ac7a4c65603000000cd625009a40aeb62a42b6a62548e3c38b96a200200000000" + "000000000000000000000000b2e94d3ab51fe0e9af98b180a36035fb6297dfa749ba9be302000000" + "0c67c3d4afa9c8649da90bd2fff2f2568108870b00000000000000000000000000000000d1111b06" + "7acbce3e3bcdf3824f1b134847ff26ba8704133102000000d9b4e3ce848518c4e3f6cbf25e5e1b99" + "0f13676700000000000000000000000000000000690807d52617369ee1af4ca5a6a95581af5fd7ce" + "b7a350f902000000fa7db4532545666937c22a3b6e9686dcc43ccbee000000000000000000000000" + "000000006ba04f146e4c699341b95f41d9c1e67743efb54e0e09e2fc01000000cc807cdce682f80f" + "94315c45ef817264c83c736e00000000000000000000000000000000c917ad6b10e693bb6e26f2ba" + "90c8e909e85e20e5182c028701000000ec4501a1e27acfee7a8588ffd5a06cae4228e59500000000" + "000000000000000000000000dda0b6ead1e1b34901eaedd5bcd7be07d78e62223cf09e8b01000000" + "e79c10acfce165e332a62384ec04e5bab46785ad00000000000000000000000000000000f86c260a" + "9edfa7a74c1c83c18e47935e8354846e4fc812bd010000006d064126666befbb98c39e7cc282208f" + "ef4a7ff400000000000000000000000000000000add1c86e759d0c90bc512e137dcef5e4a27985bd" + "9427c2260100000086ed27184b443181a5edc511c1507a141e4a5156000000000000000000000000" + "000000009870ccec3def0ad92749e016663fe6ee0228c1da0d58fbcc01000000d0d44ff1e51c3515" + "7666c05348e6f507ff72e89800000000000000000000000000000000cdf64b0f9a60e0e289f08db3" + "e4bafc4e095a32302b092583010000009f9f0251becc987907879cca68fec7bbbb4d8edf00000000" + "000000000000000000000000167721673f2b02b3256ead1f2fea9bc12f0cfca1078a806d00000000" + "8bbfc9723107f3f5b2cd3dbddc74e26de956e48d0000000000000000000000000000000088f83f6e" + "70b1646d8026e9f1704f1b16286ba2da0a6527f600000000aca0c7ee4197338581cafd63e66e3a23" + "d6572578000000000000000000000000000000009052c22b9f3c39f24b0572b1de745cb25851429b" + "1539ce410000000018238487a417f3da01d99dae5f190096cc292cab000000000000000000000000" + "00000000d8fbe9a4fdb3d117d76b0de5416f95e7bb75d72fb60291ea00000000def85b75827b26e8" + "a0bef8675b29a197b71bccea000000000000000000000000000000004a503a712350acbd48411f0d" + "c15d2f0f49dad3452931f2d30300000050b8d10ab700cc3a7cf51be0403b654ba242680800000000" + "000000000000000000000000a6bdc8789cf851782d895bcc8da98489c55b8c1237686b5e03000000" + "6215de4bb6f6f3d031a790287e45e9c18b7136c000000000000000000000000000000000ec7d3abc" + "4ccf30d3aaaf02a4f05d7308b67f91bfd8a4c5cf01000000fcc2056cc1ff8e51bbce426cfa4b861c" + "c73992be000000000000000000000000000000000aa20624fbab244735d67c61283031667b2d74a1" + "8c54dda900000000d43e6505621b9a7f0422ae3fc8979ee0c400d794000000000000000000000000" + "00000000414d94d0e02d2ec587fc330003781b0f294a2b738d1e69c801000000b973972c17a47631" + "c169f11ff9119c40b47cb66300000000000000000000000000000002ea4a849f060ddf57b24a5be9" + "b10123e6e4f15ecdc67b2e48000000001785d3ffdbd9a617857ae8126b028c0e3e4b535400000000" + "000000000000000000000002a84035b632ef3be6070dafa202187470e9da557036506ebd00000000" + "ef369bb8ebfdcec3f8c8fa6867a982be8a534f8500000000000000000000ffffe8d07c84965a316a" + "f2abb982b001ec16901f55f960c55c229c88b02a0200000004b0595daf37deb499996bfb667f072d" + "ec2c5d9c00000000000000000000ffff99d90a8543961b2ccd47724a3c460ba85183f4c5daae7d63"), }; const int TEST_DATA_I = GPOINTER_TO_INT(test_data); const int addr_family = (TEST_DATA_I == 0 ? AF_INET : AF_INET6); diff --git a/src/core/platform/tests/test-route.c b/src/core/platform/tests/test-route.c index 148ed792..bd8fdc27 100644 --- a/src/core/platform/tests/test-route.c +++ b/src/core/platform/tests/test-route.c @@ -27,7 +27,7 @@ _wait_for_ipv4_addr_device_route(NMPlatform *platform, /* Wait that the addresses gets a device-route. After adding a address, * the device route is not added immediately. It takes a moment... */ - addr = nm_utils_ip4_address_clear_host_address(addr, plen); + addr = nm_ip4_addr_clear_host_address(addr, plen); NMTST_WAIT_ASSERT(400, { NMDedupMultiIter iter; NMPLookup lookup; @@ -41,7 +41,7 @@ _wait_for_ipv4_addr_device_route(NMPlatform *platform, &o) { const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE(o); - if (r->plen == plen && addr == nm_utils_ip4_address_clear_host_address(r->network, plen) + if (r->plen == plen && addr == nm_ip4_addr_clear_host_address(r->network, plen) && r->metric == 0 && r->scope_inv == nm_platform_route_scope_inv(RT_SCOPE_LINK) && r->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) return; @@ -333,30 +333,33 @@ test_ip4_route(void) /* Test route listing */ routes = nmtstp_ip4_route_get_all(NM_PLATFORM_GET, ifindex); memset(rts, 0, sizeof(rts)); - rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[0].network = gateway; - rts[0].plen = 32; - rts[0].ifindex = ifindex; - rts[0].gateway = INADDR_ANY; - rts[0].metric = metric; - rts[0].mss = mss; - rts[0].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_LINK); - rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[1].network = network; - rts[1].plen = plen; - rts[1].ifindex = ifindex; - rts[1].gateway = gateway; - rts[1].metric = metric; - rts[1].mss = mss; - rts[1].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE); - rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[2].network = 0; - rts[2].plen = 0; - rts[2].ifindex = ifindex; - rts[2].gateway = gateway; - rts[2].metric = metric; - rts[2].mss = mss; - rts[2].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE); + rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); + rts[0].network = gateway; + rts[0].plen = 32; + rts[0].ifindex = ifindex; + rts[0].gateway = INADDR_ANY; + rts[0].metric = metric; + rts[0].mss = mss; + rts[0].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_LINK); + rts[0].n_nexthops = 1; + rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); + rts[1].network = network; + rts[1].plen = plen; + rts[1].ifindex = ifindex; + rts[1].gateway = gateway; + rts[1].metric = metric; + rts[1].mss = mss; + rts[1].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE); + rts[1].n_nexthops = 1; + rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); + rts[2].network = 0; + rts[2].plen = 0; + rts[2].ifindex = ifindex; + rts[2].gateway = gateway; + rts[2].metric = metric; + rts[2].mss = mss; + rts[2].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE); + rts[2].n_nexthops = 1; g_assert_cmpint(routes->len, ==, 3); nmtst_platform_ip4_routes_equal_aptr((const NMPObject *const *) routes->pdata, rts, @@ -635,21 +638,22 @@ test_ip4_route_options(gconstpointer test_data) switch (TEST_IDX) { case 1: rts_add[rts_n++] = ((NMPlatformIP4Route){ - .ifindex = IFINDEX, - .rt_source = NM_IP_CONFIG_SOURCE_USER, - .network = nmtst_inet4_from_string("172.16.1.0"), - .plen = 24, - .metric = 20, - .tos = 0x28, - .window = 10000, - .cwnd = 16, - .initcwnd = 30, - .initrwnd = 50, - .mtu = 1350, - .lock_cwnd = TRUE, - .mss = 1300, - .quickack = TRUE, - .rto_min = 1000, + .ifindex = IFINDEX, + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string("172.16.1.0"), + .plen = 24, + .metric = 20, + .tos = 0x28, + .window = 10000, + .cwnd = 16, + .initcwnd = 30, + .initrwnd = 50, + .mtu = 1350, + .lock_cwnd = TRUE, + .mss = 1300, + .quickack = TRUE, + .rto_min = 1000, + .n_nexthops = 1, }); break; case 2: @@ -663,12 +667,13 @@ test_ip4_route_options(gconstpointer test_data) .n_ifa_flags = 0, }); rts_add[rts_n++] = ((NMPlatformIP4Route){ - .ifindex = IFINDEX, - .rt_source = NM_IP_CONFIG_SOURCE_USER, - .network = nmtst_inet4_from_string("172.17.1.0"), - .gateway = nmtst_inet4_from_string("172.16.1.1"), - .plen = 24, - .metric = 20, + .ifindex = IFINDEX, + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string("172.17.1.0"), + .gateway = nmtst_inet4_from_string("172.16.1.1"), + .plen = 24, + .metric = 20, + .n_nexthops = 1, }); rts_add[rts_n++] = ((NMPlatformIP4Route){ .ifindex = IFINDEX, @@ -678,6 +683,7 @@ test_ip4_route_options(gconstpointer test_data) .r_rtm_flags = RTNH_F_ONLINK, .plen = 24, .metric = 20, + .n_nexthops = 1, }); break; default: @@ -691,24 +697,23 @@ test_ip4_route_options(gconstpointer test_data) const NMPlatformIP4Address *a = &addr[i]; g_assert(a->ifindex == IFINDEX); - g_assert(nm_platform_ip4_address_add( - NM_PLATFORM_GET, - a->ifindex, - a->address, - a->plen, - a->peer_address, - nm_platform_ip4_broadcast_address_create(a->address, a->plen), - a->lifetime, - a->preferred, - a->n_ifa_flags, - a->label)); + g_assert(nm_platform_ip4_address_add(NM_PLATFORM_GET, + a->ifindex, + a->address, + a->plen, + a->peer_address, + nm_ip4_addr_get_broadcast_address(a->address, a->plen), + a->lifetime, + a->preferred, + a->n_ifa_flags, + a->label)); if (a->peer_address == a->address) _wait_for_ipv4_addr_device_route(NM_PLATFORM_GET, 200, a->ifindex, a->address, a->plen); } for (i = 0; i < rts_n; i++) g_assert(NMTST_NM_ERR_SUCCESS( - nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i]))); + nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i], NULL))); for (i = 0; i < rts_n; i++) { rts_cmp[i] = rts_add[i]; @@ -747,7 +752,7 @@ test_ip6_route_get(void) nmtstp_wait_for_signal(NM_PLATFORM_GET, 10); if (nmtstp_ip6_route_get(NM_PLATFORM_GET, ifindex, - nmtst_inet6_from_string("fd01:abcd::"), + nmtst_inet6_from_string_p("fd01:abcd::"), 64, NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6, NULL, @@ -755,7 +760,7 @@ test_ip6_route_get(void) break; }); - a = nmtst_inet6_from_string("fd01:abcd::42"); + a = nmtst_inet6_from_string_p("fd01:abcd::42"); result = nm_platform_ip_route_get(NM_PLATFORM_GET, AF_INET6, a, @@ -797,7 +802,7 @@ test_ip6_route_options(gconstpointer test_data) rts_add[rts_n++] = ((NMPlatformIP6Route){ .ifindex = IFINDEX, .rt_source = NM_IP_CONFIG_SOURCE_USER, - .network = *nmtst_inet6_from_string("2001:db8:a:b:0:0:0:0"), + .network = nmtst_inet6_from_string("2001:db8:a:b:0:0:0:0"), .plen = 64, .gateway = in6addr_any, .metric = 1024, @@ -812,7 +817,7 @@ test_ip6_route_options(gconstpointer test_data) case 2: addr[addr_n++] = ((NMPlatformIP6Address){ .ifindex = IFINDEX, - .address = *nmtst_inet6_from_string("2000::2"), + .address = nmtst_inet6_from_string("2000::2"), .plen = 128, .peer_address = in6addr_any, .lifetime = NM_PLATFORM_LIFETIME_PERMANENT, @@ -822,17 +827,17 @@ test_ip6_route_options(gconstpointer test_data) rts_add[rts_n++] = ((NMPlatformIP6Route){ .ifindex = IFINDEX, .rt_source = NM_IP_CONFIG_SOURCE_USER, - .network = *nmtst_inet6_from_string("1010::1"), + .network = nmtst_inet6_from_string("1010::1"), .plen = 128, .gateway = in6addr_any, .metric = 256, - .pref_src = *nmtst_inet6_from_string("2000::2"), + .pref_src = nmtst_inet6_from_string("2000::2"), }); break; case 3: addr[addr_n++] = ((NMPlatformIP6Address){ .ifindex = IFINDEX, - .address = *nmtst_inet6_from_string("2001:db8:8086::5"), + .address = nmtst_inet6_from_string("2001:db8:8086::5"), .plen = 128, .peer_address = in6addr_any, .lifetime = NM_PLATFORM_LIFETIME_PERMANENT, @@ -842,7 +847,7 @@ test_ip6_route_options(gconstpointer test_data) rts_add[rts_n++] = ((NMPlatformIP6Route){ .ifindex = IFINDEX, .rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string("2001:db8:8086::"), + .network = nmtst_inet6_from_string("2001:db8:8086::"), .plen = 110, .metric = 10021, .mss = 0, @@ -850,9 +855,9 @@ test_ip6_route_options(gconstpointer test_data) rts_add[rts_n++] = ((NMPlatformIP6Route){ .ifindex = IFINDEX, .rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string("2001:db8:abad:c0de::"), + .network = nmtst_inet6_from_string("2001:db8:abad:c0de::"), .plen = 64, - .gateway = *nmtst_inet6_from_string("2001:db8:8086::1"), + .gateway = nmtst_inet6_from_string("2001:db8:8086::1"), .metric = 21, .mss = 0, }); @@ -918,7 +923,7 @@ test_ip(gconstpointer test_data) const NMPlatformLink *l; char ifname[IFNAMSIZ]; char ifname2[IFNAMSIZ]; - char s1[NM_UTILS_INET_ADDRSTRLEN]; + char s1[NM_INET_ADDRSTRLEN]; NMPlatform *platform = NM_PLATFORM_GET; const int EX_ = -1; struct { @@ -983,8 +988,8 @@ again_find_idx: order_idx[order_len++] = idx; r->ifindex = iface_data[idx].ifindex; - g_assert( - NMTST_NM_ERR_SUCCESS(nm_platform_ip4_route_add(platform, NMP_NLM_FLAG_APPEND, r))); + g_assert(NMTST_NM_ERR_SUCCESS( + nm_platform_ip4_route_add(platform, NMP_NLM_FLAG_APPEND, r, NULL))); } else { i = nmtst_get_rand_uint32() % order_len; idx = order_idx[i]; @@ -1171,7 +1176,7 @@ _rule_check_kernel_support(NMPlatform *platform, int attribute) .addr_family = AF_INET, .priority = PROBE_PRORITY, .uid_range = - { + { .start = 0, .end = 0, }, @@ -1278,7 +1283,7 @@ _rule_create_random(NMPlatform *platform) guint32 p; int addr_size; guint i; - char saddr[NM_UTILS_INET_ADDRSTRLEN]; + char saddr[NM_INET_ADDRSTRLEN]; static struct { guint32 uid; guint32 euid; @@ -1331,7 +1336,7 @@ _rule_create_random(NMPlatform *platform) p_addr->addr4 = nmtst_inet4_from_string(nm_sprintf_buf(saddr, "192.192.5.%u", (~p) % 256u)); else - p_addr->addr6 = *nmtst_inet6_from_string( + p_addr->addr6 = nmtst_inet6_from_string( nm_sprintf_buf(saddr, "1:2:3:4::f:%02x", (~p) % 256u)); } else if ((p % 3u) == 1) nmtst_rand_buf(NULL, p_addr, addr_size); @@ -1931,7 +1936,7 @@ test_blackhole(gconstpointer test_data) nm_platform_ip_route_normalize(addr_family, &rr.rx); if (IS_IPv4) - r = nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_APPEND, &rr.r4); + r = nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_APPEND, &rr.r4, NULL); else r = nm_platform_ip6_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_APPEND, &rr.r6); @@ -2155,6 +2160,229 @@ test_mptcp(gconstpointer test_data) /*****************************************************************************/ +static void +_ensure_onlink_routes(void) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS(NMTSTP_ENV1_DEVICE_NAME) && NMTSTP_ENV1_DEVICE_NAME[i]; i++) { + nmtstp_run_command("ip route append 7.7.7.0/24 dev %s%s", + NMTSTP_ENV1_DEVICE_NAME[i], + nmtst_is_debug() ? "" : " &>/dev/null"); + nmtstp_run_command("ip route append 7:7:7::/64 dev %s%s", + NMTSTP_ENV1_DEVICE_NAME[i], + nmtst_is_debug() ? "" : " &>/dev/null"); + } +} + +static void +test_cache_consistency_routes(gconstpointer test_data) +{ + const int TEST_IDX = GPOINTER_TO_INT(test_data); + NMPlatform *platform = NM_PLATFORM_GET; + gboolean is_test_quick = nmtst_test_quick(); + const int N_RUN = is_test_quick ? 50 : 500; + int i_run; + gs_unref_ptrarray GPtrArray *keeper = g_ptr_array_new_with_free_func(g_free); + + _ensure_onlink_routes(); + + for (i_run = 0; i_run < N_RUN; i_run++) { + const char *extra_options[100]; + gsize n_extra_options = 0; + gs_free char *extra_options_str = NULL; + int i_if; + int ifindex; + const char *ifname; + int IS_IPv4; + const char *op; + const char *prefix; + const char *s; + const char *route_type; + int i; + int n; + char addr_family_char[2] = {'6', '4'}; + + g_ptr_array_set_size(keeper, 0); + + switch (TEST_IDX) { + case 1: + IS_IPv4 = TRUE; + break; + case 2: + IS_IPv4 = FALSE; + break; + default: + IS_IPv4 = nmtst_get_rand_bool(); + break; + } + + i_if = nmtst_get_rand_uint32() % 2; + op = nmtst_rand_select_str("flush", "add", "change", "append", "prepend", "replace"); + + ifindex = NMTSTP_ENV1_IFINDEXES[i_if]; + ifname = NMTSTP_ENV1_DEVICE_NAME[i_if]; + + g_assert_cmpint(ifindex, ==, nm_platform_link_get_ifindex(platform, ifname)); + + if (nm_streq(op, "flush")) { + if (!nmtst_get_rand_one_case_in(10)) { + /* flush more seldom. */ + continue; + } + nmtstp_run_command("ip -%c route flush dev %s" + " table %s" /* table */ + "%s" /* redirect */ + "", + addr_family_char[IS_IPv4], + ifname, + nmtst_rand_select_str("main", "10222", "10223", "all"), + nmtst_is_debug() ? "" : " &>/dev/null"); + _ensure_onlink_routes(); + goto done; + } + + route_type = nmtst_get_rand_one_case_in(4) + ? nmtst_rand_select_str("unicast", "blackhole", "local", "broadcast") + : NULL; + + if (NM_IN_STRSET(route_type, "blackhole")) { + ifindex = 0; + ifname = NULL; + } + + if (IS_IPv4) { + prefix = nmtst_rand_select_str("192.168.4.0/24", + "192.168.5.0/24", + "192.168.5.5/32", + "default"); + } else { + prefix = + nmtst_rand_select_str("a:b:c:d::/64", "a:b:c:e::/64", "a:b:c:f::/64", "default"); + } + + s = nmtst_rand_select_str(NULL, "kernel", "bird"); + if (s) { + if (nmtst_get_rand_bool()) { + s = nm_streq(s, "kernel") ? nmtst_rand_select_str("boot", "static", "ra") + : nmtst_rand_select_str("zebra", "bird"); + } + extra_options[n_extra_options++] = "proto"; + extra_options[n_extra_options++] = s; + } + + s = nmtst_rand_select_str(NULL, "10", "20"); + if (s) { + extra_options[n_extra_options++] = "metric"; + extra_options[n_extra_options++] = s; + } + + s = nmtst_rand_select_str(NULL, "10222", "10223"); + if (s) { + extra_options[n_extra_options++] = "table"; + extra_options[n_extra_options++] = s; + } + + if (!IS_IPv4 && NM_IN_STRSET(op, "add", "change", "append", "prepend", "replace")) { + /* kernel has a bug with append/prepend of IPv6 routes with next-hops. + * This leads to wrong notification messages, wrong merging of multi-hop + * routes and cache inconsistency in NMPlatform. + * + * https://bugzilla.redhat.com/show_bug.cgi?id=2161994 + * + * For now, disable the test case to make the unit test not fail. + * + * While being a kernel bug, it leads to cache inconsistency in NMPlatform, + * which is a problem for NetworkManager. I don't see how we can detect + * this problem to trigger a refresh. */ + } else if (ifname && nmtst_get_rand_one_case_in(3)) { + n = (nmtst_get_rand_uint32() % 4) + 1; + for (i = 0; i < n; i++) { + extra_options[n_extra_options++] = "nexthop"; + extra_options[n_extra_options++] = "via"; + if (IS_IPv4) { + extra_options[n_extra_options++] = + nmtst_keeper_printf(&keeper, "7.7.7.%d", i + 1); + } else { + extra_options[n_extra_options++] = + nmtst_keeper_printf(&keeper, "7:7:7:7::%d", i + 1); + } + extra_options[n_extra_options++] = "dev"; + extra_options[n_extra_options++] = NMTSTP_ENV1_DEVICE_NAME[nmtst_get_rand_bool()]; + if (IS_IPv4 && i == 0) { + /* For IPv4, there is a problem if we configure a route with + * only one next-hop and a weight. In that case, kernel allows + * to add duplicates (that only differ by weight), but on netlink + * the weight is not exposed, so the routes look identical and + * are deduplicated by the hash. + * See https://bugzilla.redhat.com/show_bug.cgi?id=2162315 + * + * This needs a kernel fix. Workaround that issue here, otherwise the test + * will randomly fail. */ + } else if (nmtst_get_rand_one_case_in(3)) { + extra_options[n_extra_options++] = "weight"; + extra_options[n_extra_options++] = "5"; + } + } + + ifname = NULL; + ifindex = 0; + } + + g_assert_cmpint(n_extra_options, <, G_N_ELEMENTS(extra_options)); + extra_options[n_extra_options] = NULL; + + if (nmtst_is_debug()) + nmtstp_run_command("ip -%c -d route show table all", addr_family_char[IS_IPv4]); + + /* We ignore errors. The reason is that operations like "change" might fail if + * the route doesn't exist. That's fine for our test. We just do randomly things + * and some of them will stick. */ + nmtstp_run_command( + "ip -%c route " + "%s" /* op */ + "%s%s" /* route_type */ + " %s" /* prefix */ + "%s%s" /* ifname */ + "%s%s" /* extra_options */ + "%s" /* redirect */ + "", + addr_family_char[IS_IPv4], + op, + NM_PRINT_FMT_QUOTED2(route_type, " ", route_type, ""), + prefix, + NM_PRINT_FMT_QUOTED2(ifname, " dev ", ifname, ""), + NM_PRINT_FMT_QUOTED2(extra_options[0], + " ", + (extra_options_str = g_strjoinv(" ", (char **) extra_options)), + ""), + nmtst_is_debug() ? "" : " &>/dev/null"); + + if (nmtst_is_debug()) + nmtstp_run_command("ip -%c -d route show table all", addr_family_char[IS_IPv4]); +done: + nm_platform_process_events(platform); + + if (!is_test_quick || (i_run + 1 == N_RUN) || nmtst_get_rand_one_case_in(5)) { + nmtstp_check_platform( + platform, + nmtst_get_rand_one_case_in(5) + ? 0u + : nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4))); + } + } + + if (is_test_quick) { + gs_free char *msg = NULL; + + msg = g_strdup_printf("Ran a quick version of test %s (try NMTST_DEBUG=slow)", + nmtst_test_get_path()); + g_test_skip(msg); + } +} + +/*****************************************************************************/ + NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; void @@ -2166,9 +2394,11 @@ _nmtstp_init_tests(int *argc, char ***argv) void _nmtstp_setup_tests(void) { -#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, TRUE) +#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, 1, TRUE) #define add_test_func_data(testpath, test_func, arg) \ - nmtstp_env1_add_test_func_data(testpath, test_func, arg, TRUE) + nmtstp_env1_add_test_func_data(testpath, test_func, arg, 1, TRUE) +#define add_test_func_data_with_if2(testpath, test_func, arg) \ + nmtstp_env1_add_test_func_data(testpath, test_func, arg, 2, TRUE) add_test_func("/route/ip4", test_ip4_route); add_test_func("/route/ip6", test_ip6_route); @@ -2201,4 +2431,15 @@ _nmtstp_setup_tests(void) add_test_func_data("/route/mptcp/1", test_mptcp, GINT_TO_POINTER(1)); add_test_func_data("/route/mptcp/2", test_mptcp, GINT_TO_POINTER(2)); } + if (nmtstp_is_root_test()) { + add_test_func_data_with_if2("/route/test_cache_consistency_routes/1", + test_cache_consistency_routes, + GINT_TO_POINTER(1)); + add_test_func_data_with_if2("/route/test_cache_consistency_routes/2", + test_cache_consistency_routes, + GINT_TO_POINTER(2)); + add_test_func_data_with_if2("/route/test_cache_consistency_routes/3", + test_cache_consistency_routes, + GINT_TO_POINTER(3)); + } } diff --git a/src/core/platform/tests/test-tc.c b/src/core/platform/tests/test-tc.c index 6a2019c8..832fbea6 100644 --- a/src/core/platform/tests/test-tc.c +++ b/src/core/platform/tests/test-tc.c @@ -214,8 +214,8 @@ _nmtstp_init_tests(int *argc, char ***argv) void _nmtstp_setup_tests(void) { - nmtstp_env1_add_test_func("/link/qdisc/1", test_qdisc1, TRUE); - nmtstp_env1_add_test_func("/link/qdisc/fq_codel", test_qdisc_fq_codel, TRUE); - nmtstp_env1_add_test_func("/link/qdisc/sfq", test_qdisc_sfq, TRUE); - nmtstp_env1_add_test_func("/link/qdisc/tbf", test_qdisc_tbf, TRUE); + nmtstp_env1_add_test_func("/link/qdisc/1", test_qdisc1, 1, TRUE); + nmtstp_env1_add_test_func("/link/qdisc/fq_codel", test_qdisc_fq_codel, 1, TRUE); + nmtstp_env1_add_test_func("/link/qdisc/sfq", test_qdisc_sfq, 1, TRUE); + nmtstp_env1_add_test_func("/link/qdisc/tbf", test_qdisc_tbf, 1, TRUE); } diff --git a/src/core/ppp/nm-ppp-manager.c b/src/core/ppp/nm-ppp-manager.c index 003a9806..14cd5731 100644 --- a/src/core/ppp/nm-ppp-manager.c +++ b/src/core/ppp/nm-ppp-manager.c @@ -66,7 +66,8 @@ static NM_CACHED_QUARK_FCN("ppp-manager-secret-tries", ppp_manager_secret_tries_ /*****************************************************************************/ #define NM_TYPE_PPP_MANAGER (nm_ppp_manager_get_type()) -#define NM_PPP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PPP_MANAGER, NMPPPManager)) +#define NM_PPP_MANAGER(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PPP_MANAGER, NMPPPManager)) #define NM_PPP_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_PPP_MANAGER, NMPPPManagerClass)) #define NM_IS_PPP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_PPP_MANAGER)) @@ -582,7 +583,7 @@ impl_ppp_manager_set_ip4_config(NMDBusObject *obj, if (g_variant_lookup(config_dict, NM_PPP_IP4_CONFIG_DNS, "au", &iter)) { while (g_variant_iter_next(iter, "u", &u32)) - nm_l3_config_data_add_nameserver(l3cd, AF_INET, &u32); + nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET, &u32, NULL); g_variant_iter_free(iter); } diff --git a/src/core/ppp/nm-ppp-mgr.c b/src/core/ppp/nm-ppp-mgr.c index 31c173a3..bebffaf3 100644 --- a/src/core/ppp/nm-ppp-mgr.c +++ b/src/core/ppp/nm-ppp-mgr.c @@ -68,7 +68,7 @@ NM_UTILS_LOOKUP_STR_DEFINE(nm_ppp_mgr_callback_type_to_string, #define SELF_TO_USERDATA(self) (&(self)->idle_start) #define SELF_FROM_USERDATA(user_data) \ - ((NMPppMgr *) (((const char *) (user_data)) - G_STRUCT_OFFSET(NMPppMgr, idle_start))) + NM_CAST_ALIGN(NMPppMgr, (((char *) (user_data)) - G_STRUCT_OFFSET(NMPppMgr, idle_start))) /*****************************************************************************/ diff --git a/src/core/settings/nm-agent-manager.c b/src/core/settings/nm-agent-manager.c index 295378bc..0a120649 100644 --- a/src/core/settings/nm-agent-manager.c +++ b/src/core/settings/nm-agent-manager.c @@ -1172,8 +1172,10 @@ _con_get_try_complete_early(Request *req) } /* Do we have everything we need? */ if (NM_FLAGS_HAS(req->con.get.flags, NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM) - || ((nm_connection_need_secrets(tmp, NULL) == NULL) - && !NM_FLAGS_HAS(req->con.get.flags, NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW))) { + || (NM_FLAGS_HAS(req->con.get.flags, NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW) + && !nm_connection_need_secrets_for_rerequest(tmp)) + || (!NM_FLAGS_HAS(req->con.get.flags, NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW) + && !nm_connection_need_secrets(tmp, NULL))) { _LOGD(NULL, "(" LOG_REQ_FMT ") system settings secrets sufficient", LOG_REQ_ARG(req)); /* Got everything, we're done */ diff --git a/src/core/settings/nm-agent-manager.h b/src/core/settings/nm-agent-manager.h index cae8e8e0..e5c6e31c 100644 --- a/src/core/settings/nm-agent-manager.h +++ b/src/core/settings/nm-agent-manager.h @@ -13,7 +13,7 @@ #define NM_TYPE_AGENT_MANAGER (nm_agent_manager_get_type()) #define NM_AGENT_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_AGENT_MANAGER, NMAgentManager)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_AGENT_MANAGER, NMAgentManager)) #define NM_AGENT_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_AGENT_MANAGER, NMAgentManagerClass)) #define NM_IS_AGENT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_AGENT_MANAGER)) diff --git a/src/core/settings/nm-secret-agent.h b/src/core/settings/nm-secret-agent.h index 14458b38..eb8dd382 100644 --- a/src/core/settings/nm-secret-agent.h +++ b/src/core/settings/nm-secret-agent.h @@ -12,7 +12,7 @@ #define NM_TYPE_SECRET_AGENT (nm_secret_agent_get_type()) #define NM_SECRET_AGENT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SECRET_AGENT, NMSecretAgent)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SECRET_AGENT, NMSecretAgent)) #define NM_SECRET_AGENT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SECRET_AGENT, NMSecretAgentClass)) #define NM_IS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SECRET_AGENT)) diff --git a/src/core/settings/nm-settings-connection.h b/src/core/settings/nm-settings-connection.h index 893b0d7b..47599c3d 100644 --- a/src/core/settings/nm-settings-connection.h +++ b/src/core/settings/nm-settings-connection.h @@ -122,7 +122,7 @@ typedef enum { #define NM_TYPE_SETTINGS_CONNECTION (nm_settings_connection_get_type()) #define NM_SETTINGS_CONNECTION(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnection)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnection)) #define NM_SETTINGS_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnectionClass)) #define NM_IS_SETTINGS_CONNECTION(obj) \ diff --git a/src/core/settings/nm-settings-plugin.h b/src/core/settings/nm-settings-plugin.h index 198447c0..c9d1265f 100644 --- a/src/core/settings/nm-settings-plugin.h +++ b/src/core/settings/nm-settings-plugin.h @@ -26,7 +26,7 @@ typedef struct { #define NM_TYPE_SETTINGS_PLUGIN (nm_settings_plugin_get_type()) #define NM_SETTINGS_PLUGIN(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTINGS_PLUGIN, NMSettingsPlugin)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTINGS_PLUGIN, NMSettingsPlugin)) #define NM_SETTINGS_PLUGIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SETTINGS_PLUGIN, NMSettingsPluginClass)) #define NM_IS_SETTINGS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SETTINGS_PLUGIN)) diff --git a/src/core/settings/nm-settings-storage.h b/src/core/settings/nm-settings-storage.h index 198b0e8b..65750152 100644 --- a/src/core/settings/nm-settings-storage.h +++ b/src/core/settings/nm-settings-storage.h @@ -12,7 +12,7 @@ #define NM_TYPE_SETTINGS_STORAGE (nm_settings_storage_get_type()) #define NM_SETTINGS_STORAGE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTINGS_STORAGE, NMSettingsStorage)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTINGS_STORAGE, NMSettingsStorage)) #define NM_SETTINGS_STORAGE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SETTINGS_STORAGE, NMSettingsStorageClass)) #define NM_IS_SETTINGS_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SETTINGS_STORAGE)) diff --git a/src/core/settings/nm-settings.c b/src/core/settings/nm-settings.c index b7d846c6..63476c3c 100644 --- a/src/core/settings/nm-settings.c +++ b/src/core/settings/nm-settings.c @@ -3479,7 +3479,7 @@ _save_hostname_write_cb(GObject *source, GAsyncResult *result, gpointer user_dat nm_utils_user_data_unpack(user_data, &self, &context, &auth_subject, &hostname, &cancellable); - nm_hostname_manager_write_hostname_finish(NM_HOSTNAME_MANAGER(source), result, &error); + nm_hostname_manager_set_static_hostname_finish(NM_HOSTNAME_MANAGER(source), result, &error); nm_audit_log_control_op(NM_AUDIT_OP_HOSTNAME_SAVE, hostname ?: "", @@ -3544,7 +3544,7 @@ _save_hostname_pk_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointe (gpointer *) &priv->shutdown_cancellable); } - nm_hostname_manager_write_hostname( + nm_hostname_manager_set_static_hostname( priv->hostname_manager, hostname, priv->shutdown_cancellable, diff --git a/src/core/settings/nm-settings.h b/src/core/settings/nm-settings.h index 8747d1dc..aba3c565 100644 --- a/src/core/settings/nm-settings.h +++ b/src/core/settings/nm-settings.h @@ -15,7 +15,7 @@ #include "nm-settings-connection.h" #define NM_TYPE_SETTINGS (nm_settings_get_type()) -#define NM_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTINGS, NMSettings)) +#define NM_SETTINGS(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTINGS, NMSettings)) #define NM_SETTINGS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SETTINGS, NMSettingsClass)) #define NM_IS_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SETTINGS)) diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h index dc1a01c8..49e04291 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h @@ -10,7 +10,7 @@ #define NMS_TYPE_IFCFG_RH_PLUGIN (nms_ifcfg_rh_plugin_get_type()) #define NMS_IFCFG_RH_PLUGIN(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPlugin)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPlugin)) #define NMS_IFCFG_RH_PLUGIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPluginClass)) #define NMS_IS_IFCFG_RH_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMS_TYPE_IFCFG_RH_PLUGIN)) diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 4d8e7bd6..0eeead8a 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -739,7 +739,7 @@ read_full_ip4_address(shvarFile *ifcfg, int prefix = 0; gboolean has_key; guint32 a; - char inet_buf[NM_UTILS_INET_ADDRSTRLEN]; + char inet_buf[NM_INET_ADDRSTRLEN]; g_return_val_if_fail(which >= -1, FALSE); g_return_val_if_fail(ifcfg != NULL, FALSE); @@ -761,7 +761,7 @@ read_full_ip4_address(shvarFile *ifcfg, if (!read_ip4_address(ifcfg, numbered_tag(tag, "GATEWAY", which), &has_key, &a, error)) return FALSE; if (has_key) - *out_gateway = nm_utils_inet4_ntop_dup(a); + *out_gateway = nm_inet4_ntop_dup(a); } /* Prefix */ @@ -782,16 +782,16 @@ read_full_ip4_address(shvarFile *ifcfg, if (!read_ip4_address(ifcfg, numbered_tag(tag, "NETMASK", which), &has_key, &a, error)) return FALSE; if (has_key) - prefix = _nm_utils_ip4_netmask_to_prefix(a); + prefix = nm_ip4_addr_netmask_to_prefix(a); else { if (base_addr) prefix = nm_ip_address_get_prefix(base_addr); else { /* Try to autodetermine the prefix for the address' class */ - prefix = _nm_utils_ip4_get_default_prefix(ipaddr); + prefix = nm_ip4_addr_get_default_prefix(ipaddr); PARSE_WARNING("missing %s, assuming %s/%d", prefix_tag, - _nm_utils_inet4_ntop(ipaddr, inet_buf), + nm_inet4_ntop(ipaddr, inet_buf), prefix); } } @@ -887,6 +887,7 @@ enum { PARSE_LINE_ATTR_ROUTE_ADVMSS, PARSE_LINE_ATTR_ROUTE_RTO_MIN, PARSE_LINE_ATTR_ROUTE_QUICKACK, + PARSE_LINE_ATTR_ROUTE_WEIGHT, /* iproute2 arguments that only matter when parsing the file. */ PARSE_LINE_ATTR_ROUTE_TO, @@ -966,6 +967,12 @@ parse_route_line(const char *line, .int_base_16 = TRUE, .ignore = PARSE_LINE_AF_FLAG_FOR_IPV6, }, + [PARSE_LINE_ATTR_ROUTE_WEIGHT] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_WEIGHT, + .type = PARSE_LINE_TYPE_UINT32, + .disabled = PARSE_LINE_AF_FLAG_FOR_IPV6, + }, [PARSE_LINE_ATTR_ROUTE_SCOPE] = { .key = NM_IP_ROUTE_ATTRIBUTE_SCOPE, @@ -1282,7 +1289,7 @@ parse_line_type_addr_with_prefix: int prefix = -1; if (p_info->type == PARSE_LINE_TYPE_ADDR) { - if (!nm_utils_parse_inaddr_bin(addr_family, s, NULL, &p_data->v.addr.addr)) { + if (!nm_inet_parse_bin(addr_family, s, NULL, &p_data->v.addr.addr)) { if (p_info == &parse_infos[PARSE_LINE_ATTR_ROUTE_VIA] && nm_streq(s, "(null)")) { /* Due to a bug, would older versions of NM write "via (null)" @@ -1313,11 +1320,11 @@ parse_line_type_addr_with_prefix: if (p_info == &parse_infos[PARSE_LINE_ATTR_ROUTE_TO] && nm_streq(s, "default")) { memset(&p_data->v.addr.addr, 0, sizeof(p_data->v.addr.addr)); prefix = 0; - } else if (!nm_utils_parse_inaddr_prefix_bin(addr_family, - s, - NULL, - &p_data->v.addr.addr, - &prefix)) { + } else if (!nm_inet_parse_with_prefix_bin(addr_family, + s, + NULL, + &p_data->v.addr.addr, + &prefix)) { g_set_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -1468,7 +1475,7 @@ read_one_ip4_route(shvarFile *ifcfg, guint32 which, NMIPRoute **out_route, GErro const char *v; gs_free char *value = NULL; gint64 prefix, metric; - char inet_buf[NM_UTILS_INET_ADDRSTRLEN]; + char inet_buf[NM_INET_ADDRSTRLEN]; g_return_val_if_fail(ifcfg != NULL, FALSE); g_return_val_if_fail(out_route && !*out_route, FALSE); @@ -1496,14 +1503,14 @@ read_one_ip4_route(shvarFile *ifcfg, guint32 which, NMIPRoute **out_route, GErro error)) return FALSE; if (has_key) { - prefix = _nm_utils_ip4_netmask_to_prefix(netmask); - if (netmask != _nm_utils_ip4_prefix_to_netmask(prefix)) { + prefix = nm_ip4_addr_netmask_to_prefix(netmask); + if (netmask != nm_ip4_addr_netmask_from_prefix(prefix)) { g_set_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Invalid IP4 netmask '%s' \"%s\"", netmask_tag, - _nm_utils_inet4_ntop(netmask, inet_buf)); + nm_inet4_ntop(netmask, inet_buf)); return FALSE; } } else { @@ -1667,7 +1674,7 @@ parse_full_ip6_address(shvarFile *ifcfg, nm_assert(out_address && !*out_address); nm_assert(!error || !*error); - if (!nm_utils_parse_inaddr_prefix_bin(AF_INET6, addr_str, NULL, &addr_bin, &prefix)) { + if (!nm_inet_parse_with_prefix_bin(AF_INET6, addr_str, NULL, &addr_bin, &prefix)) { g_set_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -1935,6 +1942,8 @@ make_ip4_setting(shvarFile *ifcfg, (guint) route_table, NM_SETTING_IP4_CONFIG_LINK_LOCAL, ipv4_link_local, + NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW, + svGetValueTernary(ifcfg, "IPV4_AUTO_ROUTE_EXT_GW"), NULL); if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) @@ -2027,7 +2036,7 @@ make_ip4_setting(shvarFile *ifcfg, "because the connection has no static addresses", f); } else - gateway = nm_utils_inet4_ntop_dup(a); + gateway = nm_inet4_ntop_dup(a); } } } @@ -2047,7 +2056,9 @@ make_ip4_setting(shvarFile *ifcfg, * Pick up just IPv4 addresses (IPv6 addresses are taken by make_ip6_setting()) */ for (i = 1; i < 10000; i++) { - char tag[256]; + int af; + NMIPAddr ip; + char tag[256]; numbered_tag(tag, "DNS", i); nm_clear_g_free(&value); @@ -2055,18 +2066,18 @@ make_ip4_setting(shvarFile *ifcfg, if (!v) break; - if (nm_utils_ipaddr_is_valid(AF_INET, v)) { - if (!nm_setting_ip_config_add_dns(s_ip4, v)) - PARSE_WARNING("duplicate DNS server %s", tag); - } else if (nm_utils_ipaddr_is_valid(AF_INET6, v)) { - /* Ignore IPv6 addresses */ - } else { + if (!nm_utils_dnsname_parse(AF_UNSPEC, v, &af, &ip, NULL)) { g_set_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Invalid DNS server address '%s'", v); return NULL; + } else if (af == AF_INET) { + if (!nm_setting_ip_config_add_dns(s_ip4, v)) + PARSE_WARNING("duplicate DNS server %s", tag); + } else { + /* Ignore IPv6 addresses */ } } @@ -2446,6 +2457,8 @@ make_ip6_setting(shvarFile *ifcfg, shvarFile *network_ifcfg, gboolean routes_rea (guint) route_table, NM_SETTING_IP6_CONFIG_IP6_PRIVACY, ip6_privacy_val, + NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW, + svGetValueTernary(ifcfg, "IPV6_AUTO_ROUTE_EXT_GW"), NULL); /* Don't bother to read IP, DNS and routes when IPv6 is disabled */ @@ -2546,7 +2559,7 @@ make_ip6_setting(shvarFile *ifcfg, shvarFile *network_ifcfg, gboolean routes_rea if ((ptr = strchr(v, '%')) != NULL) *ptr = '\0'; /* remove %interface prefix if present */ - if (!nm_utils_ipaddr_is_valid(AF_INET6, v)) { + if (!nm_inet_is_valid(AF_INET6, v)) { if (!is_disabled) { g_set_error(error, NM_SETTINGS_ERROR, @@ -2586,7 +2599,9 @@ make_ip6_setting(shvarFile *ifcfg, shvarFile *network_ifcfg, gboolean routes_rea * Pick up just IPv6 addresses (IPv4 addresses are taken by make_ip4_setting()) */ for (i = 1; i < 10000; i++) { - char tag[256]; + int af; + NMIPAddr ip; + char tag[256]; numbered_tag(tag, "DNS", i); nm_clear_g_free(&value); @@ -2594,16 +2609,7 @@ make_ip6_setting(shvarFile *ifcfg, shvarFile *network_ifcfg, gboolean routes_rea if (!v) break; - if (nm_utils_ipaddr_is_valid(AF_INET6, v)) { - if (is_disabled) { - PARSE_WARNING("ignore DNS server addresses with method disabled/ignore"); - break; - } - if (!nm_setting_ip_config_add_dns(s_ip6, v)) - PARSE_WARNING("duplicate DNS server %s", tag); - } else if (nm_utils_ipaddr_is_valid(AF_INET, v)) { - /* Ignore IPv4 addresses */ - } else { + if (!nm_utils_dnsname_parse(AF_UNSPEC, v, &af, &ip, NULL)) { if (is_disabled) continue; g_set_error(error, @@ -2612,6 +2618,15 @@ make_ip6_setting(shvarFile *ifcfg, shvarFile *network_ifcfg, gboolean routes_rea "Invalid DNS server address '%s'", v); return NULL; + } else if (af == AF_INET6) { + if (is_disabled) { + PARSE_WARNING("ignore DNS server addresses with method disabled/ignore"); + break; + } + if (!nm_setting_ip_config_add_dns(s_ip6, v)) + PARSE_WARNING("duplicate DNS server %s", tag); + } else { + /* Ignore IPv4 addresses */ } } @@ -6227,6 +6242,11 @@ make_vlan_setting(shvarFile *ifcfg, const char *file, GError **error) } g_object_set(s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL); + v = svGetValueStr(ifcfg, "VLAN_PROTOCOL", &value); + if (v) { + g_object_set(s_vlan, NM_SETTING_VLAN_PROTOCOL, v, NULL); + } + vlan_flags |= NM_VLAN_FLAG_REORDER_HEADERS; gvrp = svGetValueBoolean(ifcfg, "GVRP", -1); diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h index 2ab6beb6..737c9614 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h @@ -13,7 +13,7 @@ #define NMS_TYPE_IFCFG_RH_STORAGE (nms_ifcfg_rh_storage_get_type()) #define NMS_IFCFG_RH_STORAGE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorage)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorage)) #define NMS_IFCFG_RH_STORAGE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorageClass)) #define NMS_IS_IFCFG_RH_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMS_TYPE_IFCFG_RH_STORAGE)) diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c index e1ef8174..85a0eb26 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c @@ -957,6 +957,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = { _KEY_TYPE("IEEE_8021X_SUBJECT_MATCH", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IEEE_8021X_SYSTEM_CA_CERTS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPADDR", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("IPV4_AUTO_ROUTE_EXT_GW", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV4_DHCP_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV4_DNS_PRIORITY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV4_FAILURE_FATAL", NMS_IFCFG_KEY_TYPE_IS_PLAIN), @@ -971,6 +972,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = { _KEY_TYPE("IPV6TUNNELIPV4", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV6_ADDR_GEN_MODE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV6_AUTOCONF", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_AUTO_ROUTE_EXT_GW", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV6_DEFAULTDEV", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV6_DEFAULTGW", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IPV6_DEFROUTE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), @@ -1061,6 +1063,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = { _KEY_TYPE("VLAN_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("VLAN_ID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("VLAN_INGRESS_PRIORITY_MAP", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("VLAN_PROTOCOL", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("VRF", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("VRF_UUID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("WAIT_ACTIVATION_DELAY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), @@ -1080,12 +1083,12 @@ nms_ifcfg_well_known_key_find_info(const char *key, gssize *out_idx) G_STATIC_ASSERT(G_STRUCT_OFFSET(NMSIfcfgKeyTypeInfo, key_name) == 0); - idx = nm_utils_array_find_binary_search(nms_ifcfg_well_known_keys, - sizeof(nms_ifcfg_well_known_keys[0]), - G_N_ELEMENTS(nms_ifcfg_well_known_keys), - &key, - nm_strcmp_p_with_data, - NULL); + idx = nm_array_find_bsearch(nms_ifcfg_well_known_keys, + G_N_ELEMENTS(nms_ifcfg_well_known_keys), + sizeof(nms_ifcfg_well_known_keys[0]), + &key, + nm_strcmp_p_with_data, + NULL); NM_SET_OUT(out_idx, idx); if (idx < 0) return NULL; diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h index d1f8dbad..31064274 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h @@ -33,7 +33,7 @@ typedef struct { NMSIfcfgKeyTypeFlags key_flags; } NMSIfcfgKeyTypeInfo; -extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[256]; +extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[259]; const NMSIfcfgKeyTypeInfo *nms_ifcfg_well_known_key_find_info(const char *key, gssize *out_idx); diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index e8948c3d..2ea097fd 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -1453,6 +1453,7 @@ write_vlan_setting(NMConnection *connection, shvarFile *ifcfg, gboolean *wired, svSetValueStr(ifcfg, "TYPE", TYPE_VLAN); svSetValueStr(ifcfg, "PHYSDEV", nm_setting_vlan_get_parent(s_vlan)); svSetValueInt64(ifcfg, "VLAN_ID", nm_setting_vlan_get_id(s_vlan)); + svSetValueStr(ifcfg, "VLAN_PROTOCOL", nm_setting_vlan_get_protocol(s_vlan)); vlan_flags = nm_setting_vlan_get_flags(s_vlan); svSetValueBoolean(ifcfg, "REORDER_HDR", NM_FLAGS_HAS(vlan_flags, NM_VLAN_FLAG_REORDER_HEADERS)); @@ -2359,13 +2360,14 @@ get_route_attributes_string(NMIPRoute *route, int family) /* we also have a corresponding attribute with the numeric value. The * lock setting is handled above. */ } - } else if (nm_streq(names[i], NM_IP_ROUTE_ATTRIBUTE_SCOPE)) { + } else if (NM_IN_STRSET(names[i], NM_IP_ROUTE_ATTRIBUTE_SCOPE)) { g_string_append_printf(str, "%s %u", names[i], (unsigned) g_variant_get_byte(attr)); } else if (nm_streq(names[i], NM_IP_ROUTE_ATTRIBUTE_TOS)) { g_string_append_printf(str, "%s 0x%02x", names[i], (unsigned) g_variant_get_byte(attr)); } else if (NM_IN_STRSET(names[i], + NM_IP_ROUTE_ATTRIBUTE_RTO_MIN, NM_IP_ROUTE_ATTRIBUTE_TABLE, - NM_IP_ROUTE_ATTRIBUTE_RTO_MIN)) { + NM_IP_ROUTE_ATTRIBUTE_WEIGHT)) { g_string_append_printf(str, "%s %u", names[i], (unsigned) g_variant_get_uint32(attr)); } else if (nm_streq(names[i], NM_IP_ROUTE_ATTRIBUTE_QUICKACK)) { g_string_append_printf(str, "%s %u", names[i], (unsigned) g_variant_get_boolean(attr)); @@ -2416,8 +2418,8 @@ write_route_file_svformat(const char *filename, NMSettingIPConfig *s_ip4) svSetValueStr(routefile, addr_key, nm_ip_route_get_dest(route)); - netmask = _nm_utils_ip4_prefix_to_netmask(nm_ip_route_get_prefix(route)); - svSetValueStr(routefile, netmask_key, _nm_utils_inet4_ntop(netmask, buf)); + netmask = nm_ip4_addr_netmask_from_prefix(nm_ip_route_get_prefix(route)); + svSetValueStr(routefile, netmask_key, nm_inet4_ntop(netmask, buf)); svSetValueStr(routefile, gw_key, nm_ip_route_get_next_hop(route)); @@ -2826,9 +2828,7 @@ write_ip4_setting(NMConnection *connection, if (has_netmask) { char buf[INET_ADDRSTRLEN]; - svSetValueStr(ifcfg, - tag, - _nm_utils_inet4_ntop(_nm_utils_ip4_prefix_to_netmask(prefix), buf)); + svSetValueStr(ifcfg, tag, nm_inet4_ntop(nm_ip4_addr_netmask_from_prefix(prefix), buf)); } n++; @@ -2937,6 +2937,10 @@ write_ip4_setting(NMConnection *connection, } svSetValueStr(ifcfg, "DHCP_REJECT_SERVERS", str->str); } + + svSetValueTernary(ifcfg, + "IPV4_AUTO_ROUTE_EXT_GW", + nm_setting_ip_config_get_auto_route_ext_gw(s_ip4)); } static void @@ -3197,6 +3201,10 @@ write_ip6_setting(NMConnection *connection, shvarFile *ifcfg, GString **out_rout write_res_options(ifcfg, s_ip6, "IPV6_RES_OPTIONS"); NM_SET_OUT(out_route6_content, write_route_file(s_ip6)); + + svSetValueTernary(ifcfg, + "IPV6_AUTO_ROUTE_EXT_GW", + nm_setting_ip_config_get_auto_route_ext_gw(s_ip6)); } static void diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected index 6ad151aa..6373da90 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected @@ -2,6 +2,7 @@ VLAN=yes TYPE=Vlan PHYSDEV=eth9 VLAN_ID=43 +VLAN_PROTOCOL=802.1ad REORDER_HDR=yes GVRP=yes VLAN_FLAGS=LOOSE_BINDING diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1 index ecb36c37..5e40362e 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1 +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1 @@ -1,4 +1,7 @@ DNS1="192.0.2.1" +DNS2="192.0.2.2#adfs.afddsaf" +DNS3="1::2" +DNS4="1::3#dfdf.er" IPADDR="102.0.2.2" GATEWAY="192.0.2.1" NETMASK="255.254.0.0" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1.cexpected index 87493ac2..d0eeeb99 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1.cexpected +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1.cexpected @@ -1,4 +1,7 @@ DNS1=192.0.2.1 +DNS2=192.0.2.2#adfs.afddsaf +DNS3=1::2 +DNS4=1::3#dfdf.er IPADDR=102.0.2.2 GATEWAY=192.0.2.1 NETMASK=255.254.0.0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-interface b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-interface index d8d9193f..38fe4069 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-interface +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-interface @@ -5,6 +5,7 @@ PHYSDEV=eth9 VLAN_FLAGS=GVRP,LOOSE_BINDING VLAN_INGRESS_PRIORITY_MAP=0:1,2:5 VLAN_EGRESS_PRIORITY_MAP=12:3,14:7,3:1 +VLAN_PROTOCOL=802.1ad ONBOOT=yes BOOTPROTO=static IPADDR=192.168.43.149 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-routes b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-routes index 7faf49bd..fac9a5a2 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-routes +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-routes @@ -9,7 +9,7 @@ IPV6INIT=no MTU=1492 NM_CONTROLLED=yes DNS1=4.2.2.1 -DNS2=4.2.2.2 +DNS2=4.2.2.2#dns.name IPADDR=192.168.1.5 NETMASK=255.255.255.0 GATEWAY=192.168.1.1 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes index 717ad1ac..728e8e96 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes @@ -6,7 +6,7 @@ ADDRESS1=44.55.66.77 NETMASK1=255.255.255.255 GATEWAY1=192.168.1.7 METRIC1=3 -OPTIONS1="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 window 30000 scope 10 initcwnd lock 13 initrwnd 14 rto_min 300 advmss 1300 quickack 1" +OPTIONS1="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 window 30000 scope 10 initcwnd lock 13 initrwnd 14 rto_min 300 advmss 1300 quickack 1 weight 5" ADDRESS2=44.55.66.78 NETMASK2=255.255.255.255 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 886a605f..40ff7c67 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -461,6 +461,7 @@ test_read_netmask_1(void) gs_free char *content = NULL; NMSettingConnection *s_con; NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; NMIPAddress *ip4_addr; const char *FILENAME = TEST_IFCFG_DIR "/ifcfg-netmask-1"; @@ -470,12 +471,20 @@ test_read_netmask_1(void) g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System netmask-1"); s_ip4 = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_IP4_CONFIG); - g_assert_cmpuint(nm_setting_ip_config_get_num_dns(s_ip4), ==, 1); + g_assert_cmpuint(nm_setting_ip_config_get_num_dns(s_ip4), ==, 2); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 0), ==, "192.0.2.1"); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 1), ==, "192.0.2.2#adfs.afddsaf"); + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 0); g_assert(ip4_addr); g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "102.0.2.2"); g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 15); + s_ip6 = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_IP6_CONFIG); + g_assert_cmpuint(nm_setting_ip_config_get_num_dns(s_ip6), ==, 2); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip6, 0), ==, "1::2"); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip6, 1), ==, "1::3#dfdf.er"); + nmtst_assert_connection_verifies_without_normalization(connection); content = nmtst_file_get_contents(FILENAME); @@ -1356,6 +1365,7 @@ test_read_wired_static_routes(void) nmtst_assert_route_attribute_byte(ip4_route, NM_IP_ROUTE_ATTRIBUTE_TOS, 0x28); nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, 30000); nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 12); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_WEIGHT, 5); nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITCWND, 13); nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITRWND, 14); nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 9000); @@ -1390,6 +1400,10 @@ test_read_wired_static_routes(void) g_assert_cmpint(nm_ip_route_get_prefix(ip4_route), ==, 32); nmtst_assert_route_attribute_string(ip4_route, NM_IP_ROUTE_ATTRIBUTE_TYPE, "local"); nmtst_assert_route_attribute_byte(ip4_route, NM_IP_ROUTE_ATTRIBUTE_SCOPE, 254); + + g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip4), ==, 2); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 0), ==, "4.2.2.1"); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 1), ==, "4.2.2.2#dns.name"); } static void @@ -2036,11 +2050,11 @@ test_read_wired_aliases_good(gconstpointer test_data) NULL}; const char *expected_address_3[] = {"192.168.1.5", "192.168.1.6", NULL}; const char *expected_label_0[] = { - NULL, - "aliasem0:1", - "aliasem0:2", - "aliasem0:99", - NULL, + NULL, + "aliasem0:1", + "aliasem0:2", + "aliasem0:99", + NULL, }; const char *expected_label_3[] = { NULL, @@ -2082,7 +2096,7 @@ test_read_wired_aliases_good(gconstpointer test_data) g_assert(ip4_addr != NULL); addr = nm_ip_address_get_address(ip4_addr); - g_assert(nm_utils_ipaddr_is_valid(AF_INET, addr)); + g_assert(nm_inet_is_valid(AF_INET, addr)); for (j = 0; j < expected_num_addresses; j++) { if (!g_strcmp0(addr, expected_address[j])) @@ -4266,6 +4280,7 @@ static void test_write_wired_static(void) { nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *route4file = NULL; nmtst_auto_unlinkfile char *route6file = NULL; gs_unref_object NMConnection *connection = NULL; gs_unref_object NMConnection *reread = NULL; @@ -4275,6 +4290,7 @@ test_write_wired_static(void) NMSettingIPConfig *s_ip6, *reread_s_ip6; NMIPAddress *addr; NMIPAddress *addr6; + NMIPRoute *route4; NMIPRoute *route6; GError *error = NULL; @@ -4379,6 +4395,13 @@ test_write_wired_static(void) nm_setting_ip_config_add_route(s_ip6, route6); nm_ip_route_unref(route6); + route4 = nm_ip_route_new(AF_INET, "1.1.1.1", 24, "1.2.3.4", 99, &error); + g_assert_no_error(error); + nm_ip_route_set_attribute(route4, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32(100)); + nm_ip_route_set_attribute(route4, NM_IP_ROUTE_ATTRIBUTE_WEIGHT, g_variant_new_uint32(5)); + nm_setting_ip_config_add_route(s_ip4, route4); + nm_ip_route_unref(route4); + /* DNS servers */ nm_setting_ip_config_add_dns(s_ip6, "fade:0102:0103::face"); nm_setting_ip_config_add_dns(s_ip6, "cafe:ffff:eeee:dddd:cccc:bbbb:aaaa:feed"); @@ -4390,7 +4413,9 @@ test_write_wired_static(void) nmtst_assert_connection_verifies(connection); _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + route6file = utils_get_route6_path(testfile); + route4file = utils_get_route_path(testfile); reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); @@ -7754,6 +7779,7 @@ test_read_vlan_interface(void) g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth9"); g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 43); + g_assert_cmpstr(nm_setting_vlan_get_protocol(s_vlan), ==, "802.1ad"); g_assert_cmpint(nm_setting_vlan_get_flags(s_vlan), ==, NM_VLAN_FLAG_GVRP | NM_VLAN_FLAG_LOOSE_BINDING | NM_VLAN_FLAG_REORDER_HEADERS); @@ -7930,6 +7956,7 @@ test_read_vlan_flags_1(void) g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth9"); g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 44); + g_assert_cmpstr(nm_setting_vlan_get_protocol(s_vlan), ==, NULL); g_assert_cmpint(nm_setting_vlan_get_flags(s_vlan), ==, NM_VLAN_FLAG_LOOSE_BINDING | NM_VLAN_FLAG_REORDER_HEADERS); diff --git a/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c index a1d6781b..3bdd78c9 100644 --- a/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c +++ b/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c @@ -451,7 +451,7 @@ update_ip4_setting_from_if_block(NMConnection *connection, if_block *block, GErr netmask_v); return FALSE; } else { - netmask_int = _nm_utils_ip4_netmask_to_prefix(tmp_mask); + netmask_int = nm_ip4_addr_netmask_to_prefix(tmp_mask); } } @@ -470,7 +470,7 @@ update_ip4_setting_from_if_block(NMConnection *connection, if_block *block, GErr /* gateway */ gateway_v = ifparser_getkey(block, "gateway"); if (gateway_v) { - if (!nm_utils_ipaddr_is_valid(AF_INET, gateway_v)) { + if (!nm_inet_is_valid(AF_INET, gateway_v)) { g_set_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -579,7 +579,7 @@ update_ip6_setting_from_if_block(NMConnection *connection, if_block *block, GErr gateway_v = ifparser_getkey(block, "gateway"); if (gateway_v) { - if (!nm_utils_ipaddr_is_valid(AF_INET6, gateway_v)) { + if (!nm_inet_is_valid(AF_INET6, gateway_v)) { g_set_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, diff --git a/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.h b/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.h index edf1b317..570bf2a3 100644 --- a/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.h +++ b/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.h @@ -11,7 +11,7 @@ #define NMS_TYPE_IFUPDOWN_PLUGIN (nms_ifupdown_plugin_get_type()) #define NMS_IFUPDOWN_PLUGIN(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPlugin)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPlugin)) #define NMS_IFUPDOWN_PLUGIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPluginClass)) #define NMS_IS_IFUPDOWN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMS_TYPE_IFUPDOWN_PLUGIN)) diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-plugin.h b/src/core/settings/plugins/keyfile/nms-keyfile-plugin.h index 19e4122d..03a47526 100644 --- a/src/core/settings/plugins/keyfile/nms-keyfile-plugin.h +++ b/src/core/settings/plugins/keyfile/nms-keyfile-plugin.h @@ -14,7 +14,7 @@ #define NMS_TYPE_KEYFILE_PLUGIN (nms_keyfile_plugin_get_type()) #define NMS_KEYFILE_PLUGIN(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NMS_TYPE_KEYFILE_PLUGIN, NMSKeyfilePlugin)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMS_TYPE_KEYFILE_PLUGIN, NMSKeyfilePlugin)) #define NMS_KEYFILE_PLUGIN_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NMS_TYPE_KEYFILE_PLUGIN, NMSKeyfilePluginClass)) #define NMS_IS_KEYFILE_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMS_TYPE_KEYFILE_PLUGIN)) diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-storage.h b/src/core/settings/plugins/keyfile/nms-keyfile-storage.h index 8343cb2b..66521618 100644 --- a/src/core/settings/plugins/keyfile/nms-keyfile-storage.h +++ b/src/core/settings/plugins/keyfile/nms-keyfile-storage.h @@ -14,7 +14,7 @@ #define NMS_TYPE_KEYFILE_STORAGE (nms_keyfile_storage_get_type()) #define NMS_KEYFILE_STORAGE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorage)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorage)) #define NMS_KEYFILE_STORAGE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorageClass)) #define NMS_IS_KEYFILE_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMS_TYPE_KEYFILE_STORAGE)) diff --git a/src/core/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection b/src/core/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection index f9ccc003..eb3754a2 100644 --- a/src/core/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection +++ b/src/core/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection @@ -36,7 +36,7 @@ routes9=1.1.1.9/19,0.0.0.0,0 route10=1.1.1.10/21,,0 routes10=1.1.1.10/20,,0 routes11=1.1.1.11/21,,21 -routes11_options=cwnd=10,lock-cwnd=true,mtu=1430,src=7.7.7.7,type=unicast +routes11_options=cwnd=10,lock-cwnd=true,mtu=1430,src=7.7.7.7,type=unicast,weight=5 routes12=1.2.3.4/32 routes12_options=type=local address30=1.2.3.30/24 diff --git a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c index 47be3a5e..83019bab 100644 --- a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c +++ b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c @@ -311,6 +311,7 @@ test_read_valid_wired_connection(void) nmtst_assert_route_attribute_uint32(route, NM_IP_ROUTE_ATTRIBUTE_CWND, 10); nmtst_assert_route_attribute_uint32(route, NM_IP_ROUTE_ATTRIBUTE_MTU, 1430); + nmtst_assert_route_attribute_uint32(route, NM_IP_ROUTE_ATTRIBUTE_WEIGHT, 5); nmtst_assert_route_attribute_boolean(route, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, TRUE); nmtst_assert_route_attribute_string(route, NM_IP_ROUTE_ATTRIBUTE_SRC, "7.7.7.7"); nmtst_assert_route_attribute_string(route, NM_IP_ROUTE_ATTRIBUTE_TYPE, "unicast"); @@ -495,6 +496,7 @@ test_write_wired_connection(void) g_assert_no_error(error); nm_ip_route_set_attribute(rt, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32(10)); nm_ip_route_set_attribute(rt, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32(1492)); + nm_ip_route_set_attribute(rt, NM_IP_ROUTE_ATTRIBUTE_WEIGHT, g_variant_new_uint32(5)); nm_ip_route_set_attribute(rt, NM_IP_ROUTE_ATTRIBUTE_SRC, g_variant_new_string("1.2.3.4")); g_assert(nm_setting_ip_config_add_route(s_ip4, rt)); nm_ip_route_unref(rt); @@ -2298,7 +2300,7 @@ test_read_missing_id_uuid(void) const char *FILENAME = TEST_KEYFILES_DIR "/Test_Missing_ID_UUID"; const char F[] = "keyfile\0" TEST_KEYFILES_DIR "/Test_Missing_ID_UUID"; - expected_uuid = nm_uuid_generate_from_strings("keyfile", FILENAME, NULL); + expected_uuid = nm_uuid_generate_from_strings_old("keyfile", FILENAME); expected_uuid2 = nm_uuid_generate_from_string_str(F, sizeof(F), NM_UUID_TYPE_VERSION3, &nm_uuid_ns_1); diff --git a/src/core/supplicant/nm-supplicant-config.c b/src/core/supplicant/nm-supplicant-config.c index bd48ed92..c63a0058 100644 --- a/src/core/supplicant/nm-supplicant-config.c +++ b/src/core/supplicant/nm-supplicant-config.c @@ -153,16 +153,16 @@ nm_supplicant_config_add_option_with_type(NMSupplicantConfig *self, return FALSE; } - opt = g_slice_new0(ConfigOption); - opt->value = g_malloc(len + 1); - memcpy(opt->value, value, len); - opt->value[len] = '\0'; - - opt->len = len; - opt->type = type; + opt = g_slice_new(ConfigOption); + *opt = (ConfigOption){ + .value = nm_memdup_nul(value, len), + .len = len, + .type = type, + }; { char buf[255]; + memset(&buf[0], 0, sizeof(buf)); memcpy(&buf[0], opt->value, opt->len > 254 ? 254 : opt->len); nm_log_info(LOGD_SUPPLICANT, @@ -1379,12 +1379,24 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self, } phase1_auth_flags = nm_setting_802_1x_get_phase1_auth_flags(setting); - if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE)) + if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_ENABLE)) + g_string_append_printf(phase1, "%stls_disable_tlsv1_0=0", (phase1->len ? " " : "")); + else if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE)) g_string_append_printf(phase1, "%stls_disable_tlsv1_0=1", (phase1->len ? " " : "")); - if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE)) + if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_ENABLE)) + g_string_append_printf(phase1, "%stls_disable_tlsv1_1=0", (phase1->len ? " " : "")); + else if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE)) g_string_append_printf(phase1, "%stls_disable_tlsv1_1=1", (phase1->len ? " " : "")); - if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_DISABLE)) + if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_ENABLE)) + g_string_append_printf(phase1, "%stls_disable_tlsv1_2=0", (phase1->len ? " " : "")); + else if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_DISABLE)) g_string_append_printf(phase1, "%stls_disable_tlsv1_2=1", (phase1->len ? " " : "")); + if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_3_ENABLE)) + g_string_append_printf(phase1, "%stls_disable_tlsv1_3=0", (phase1->len ? " " : "")); + else if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_3_DISABLE)) + g_string_append_printf(phase1, "%stls_disable_tlsv1_3=1", (phase1->len ? " " : "")); + if (NM_FLAGS_HAS(phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_DISABLE_TIME_CHECKS)) + g_string_append_printf(phase1, "%stls_disable_time_checks=1", (phase1->len ? " " : "")); if (phase1->len) { if (!add_string_val(self, phase1->str, "phase1", FALSE, NULL, error)) { diff --git a/src/core/supplicant/nm-supplicant-config.h b/src/core/supplicant/nm-supplicant-config.h index ee7f4dc8..063f7f55 100644 --- a/src/core/supplicant/nm-supplicant-config.h +++ b/src/core/supplicant/nm-supplicant-config.h @@ -16,7 +16,7 @@ #define NM_TYPE_SUPPLICANT_CONFIG (nm_supplicant_config_get_type()) #define NM_SUPPLICANT_CONFIG(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SUPPLICANT_CONFIG, NMSupplicantConfig)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SUPPLICANT_CONFIG, NMSupplicantConfig)) #define NM_SUPPLICANT_CONFIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SUPPLICANT_CONFIG, NMSupplicantConfigClass)) #define NM_IS_SUPPLICANT_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SUPPLICANT_CONFIG)) diff --git a/src/core/supplicant/nm-supplicant-interface.c b/src/core/supplicant/nm-supplicant-interface.c index fa8b4e37..18525724 100644 --- a/src/core/supplicant/nm-supplicant-interface.c +++ b/src/core/supplicant/nm-supplicant-interface.c @@ -2647,7 +2647,7 @@ scan_request_cb(GObject *source, GAsyncResult *result, gpointer user_data) _LOGD("request-scan: request cancelled"); else { if (error) { - if (_nm_dbus_error_has_name(error, "fi.w1.wpa_supplicant1.Interface.ScanError")) + if (nm_dbus_error_is(error, "fi.w1.wpa_supplicant1.Interface.ScanError")) _LOGD("request-scan: could not get scan request result: %s", error->message); else { g_dbus_error_strip_remote_error(error); @@ -3174,7 +3174,7 @@ _signal_handle(NMSupplicantInterface *self, _set_p2p_assigned_addr(iface, addr_data, - _nm_utils_ip4_netmask_to_prefix(netmask)); + nm_ip4_addr_netmask_to_prefix(netmask)); } else { _LOGW("P2P: GroupStarted signaled invalid IP Address information"); } diff --git a/src/core/supplicant/nm-supplicant-interface.h b/src/core/supplicant/nm-supplicant-interface.h index 5ccb455c..b8d9b013 100644 --- a/src/core/supplicant/nm-supplicant-interface.h +++ b/src/core/supplicant/nm-supplicant-interface.h @@ -58,7 +58,7 @@ typedef enum { #define NM_TYPE_SUPPLICANT_INTERFACE (nm_supplicant_interface_get_type()) #define NM_SUPPLICANT_INTERFACE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterface)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterface)) #define NM_SUPPLICANT_INTERFACE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterfaceClass)) #define NM_IS_SUPPLICANT_INTERFACE(obj) \ diff --git a/src/core/supplicant/nm-supplicant-manager.c b/src/core/supplicant/nm-supplicant-manager.c index 2ec7db23..f6927500 100644 --- a/src/core/supplicant/nm-supplicant-manager.c +++ b/src/core/supplicant/nm-supplicant-manager.c @@ -450,7 +450,7 @@ _create_iface_dbus_call_get_interface_cb(GObject *source, GAsyncResult *result, char ifname[NMP_IFNAMSIZ]; if (handle->create_iface_try_count < CREATE_IFACE_TRY_COUNT_MAX - && _nm_dbus_error_has_name(error, NM_WPAS_ERROR_UNKNOWN_IFACE) + && nm_dbus_error_is(error, NM_WPAS_ERROR_UNKNOWN_IFACE) && nm_platform_if_indextoname(NM_PLATFORM_GET, handle->ifindex, ifname)) { /* Before, supplicant told us the interface existed. Was there a race? * Try again. */ @@ -500,7 +500,7 @@ _create_iface_dbus_call_create_interface_cb(GObject *source, nm_assert(handle->self); TRUE; }) - && _nm_dbus_error_has_name(error, NM_WPAS_ERROR_EXISTS_ERROR) + && nm_dbus_error_is(error, NM_WPAS_ERROR_EXISTS_ERROR) && nm_platform_if_indextoname(NM_PLATFORM_GET, handle->ifindex, ifname)) { self = handle->self; _LOGT("create-iface[" NM_HASH_OBFUSCATE_PTR_FMT diff --git a/src/core/supplicant/nm-supplicant-manager.h b/src/core/supplicant/nm-supplicant-manager.h index ab163b0d..b3284b70 100644 --- a/src/core/supplicant/nm-supplicant-manager.h +++ b/src/core/supplicant/nm-supplicant-manager.h @@ -12,7 +12,7 @@ #define NM_TYPE_SUPPLICANT_MANAGER (nm_supplicant_manager_get_type()) #define NM_SUPPLICANT_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManager)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManager)) #define NM_SUPPLICANT_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManagerClass)) #define NM_IS_SUPPLICANT_MANAGER(obj) \ diff --git a/src/core/supplicant/nm-supplicant-settings-verify.c b/src/core/supplicant/nm-supplicant-settings-verify.c index 9881c552..fc40bade 100644 --- a/src/core/supplicant/nm-supplicant-settings-verify.c +++ b/src/core/supplicant/nm-supplicant-settings-verify.c @@ -113,7 +113,11 @@ static const struct Opt opt_table[] = { "tls_disable_tlsv1_1=0", "tls_disable_tlsv1_1=1", "tls_disable_tlsv1_2=0", - "tls_disable_tlsv1_2=1", )), + "tls_disable_tlsv1_2=1", + "tls_disable_tlsv1_3=0", + "tls_disable_tlsv1_3=1", + "tls_disable_time_checks=0", + "tls_disable_time_checks=1", )), OPT_KEYWORD("phase2", NM_MAKE_STRV("auth=PAP", "auth=CHAP", @@ -277,12 +281,12 @@ nm_supplicant_settings_verify_setting(const char *key, const char *value, const } } - opt_idx = nm_utils_array_find_binary_search(opt_table, - sizeof(opt_table[0]), - G_N_ELEMENTS(opt_table), - &key, - nm_strcmp_p_with_data, - NULL); + opt_idx = nm_array_find_bsearch(opt_table, + G_N_ELEMENTS(opt_table), + sizeof(opt_table[0]), + &key, + nm_strcmp_p_with_data, + NULL); if (opt_idx < 0) { if (nm_streq(key, "mode")) { if (len != 1) diff --git a/src/core/tests/config/global-dns-good.conf b/src/core/tests/config/global-dns-good.conf new file mode 100644 index 00000000..6265a611 --- /dev/null +++ b/src/core/tests/config/global-dns-good.conf @@ -0,0 +1,13 @@ +# Good configuration, since there is a default domain section + +[global-dns] +searches=foo.com +options=timeout:5 + +[global-dns-domain-*] +servers=4.5.6.7 +options=myoption1 + +[global-dns-domain-test.com] +servers=1.2.3.4 +options=myoption2 diff --git a/src/core/tests/config/global-dns-options.conf b/src/core/tests/config/global-dns-options.conf new file mode 100644 index 00000000..0be17735 --- /dev/null +++ b/src/core/tests/config/global-dns-options.conf @@ -0,0 +1,5 @@ +# Good configuration, since there is no domain section + +[global-dns] +searches=foo.com +options=timeout:5 diff --git a/src/core/tests/config/nm-test-device.h b/src/core/tests/config/nm-test-device.h index 638177a0..48a53bf4 100644 --- a/src/core/tests/config/nm-test-device.h +++ b/src/core/tests/config/nm-test-device.h @@ -9,7 +9,8 @@ #include "devices/nm-device.h" #define NM_TYPE_TEST_DEVICE (nm_test_device_get_type()) -#define NM_TEST_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_TEST_DEVICE, NMTestDevice)) +#define NM_TEST_DEVICE(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_TEST_DEVICE, NMTestDevice)) #define NM_TEST_DEVICE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_TEST_DEVICE, NMTestDeviceClass)) #define NM_IS_TEST_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_TEST_DEVICE)) diff --git a/src/core/tests/config/test-config.c b/src/core/tests/config/test-config.c index 17a66b06..054b9003 100644 --- a/src/core/tests/config/test-config.c +++ b/src/core/tests/config/test-config.c @@ -370,7 +370,21 @@ test_config_global_dns(void) g_object_unref(config); - /* Check that a file without a default domain section gives a NULL configuration */ + /* Check that a file with a default domain section gives a good configuration */ + config = + setup_config(NULL, TEST_DIR "/global-dns-good.conf", "", NULL, "/no/such/dir", "", NULL); + dns = nm_config_data_get_global_dns_config(nm_config_get_data_orig(config)); + g_assert(dns); + g_object_unref(config); + + /* Check that a file with options but no domains gives a good configuration */ + config = + setup_config(NULL, TEST_DIR "/global-dns-options.conf", "", NULL, "/no/such/dir", "", NULL); + dns = nm_config_data_get_global_dns_config(nm_config_get_data_orig(config)); + g_assert(dns); + g_object_unref(config); + + /* Check that a file with a domain domain, but without a default one gives a NULL configuration */ config = setup_config(NULL, TEST_DIR "/global-dns-invalid.conf", "", NULL, "/no/such/dir", "", NULL); dns = nm_config_data_get_global_dns_config(nm_config_get_data_orig(config)); @@ -1103,9 +1117,9 @@ test_config_set_values(void) const char *CONFIG_USER = BUILD_DIR "/test-set-values-user.conf"; const char *CONFIG_INTERN = BUILD_DIR "/test-set-values-intern.conf"; const char *atomic_section_prefixes[] = { - "atomic-prefix-1.", - "atomic-prefix-2.", - NULL, + "atomic-prefix-1.", + "atomic-prefix-2.", + NULL, }; g_assert(g_file_set_contents(CONFIG_USER, "", 0, NULL)); diff --git a/src/core/tests/test-core-with-expect.c b/src/core/tests/test-core-with-expect.c index de510c76..b05144ac 100644 --- a/src/core/tests/test-core-with-expect.c +++ b/src/core/tests/test-core-with-expect.c @@ -227,11 +227,11 @@ do_test_nm_utils_kill_child(void) { GLogLevelFlags fatal_mask; char *argv_watchdog[] = { - "bash", - "-c", - "sleep 4; " - "kill -KILL 0; #watchdog for #" TEST_TOKEN, - NULL, + "bash", + "-c", + "sleep 4; " + "kill -KILL 0; #watchdog for #" TEST_TOKEN, + NULL, }; char *argv1[] = { "bash", @@ -282,7 +282,7 @@ do_test_nm_utils_kill_child(void) pid4a = test_nm_utils_kill_child_spawn(argv4, TRUE); /* give processes time to start (and potentially block signals) ... */ - g_usleep(G_USEC_PER_SEC / 10); + g_usleep(G_USEC_PER_SEC / 5); fatal_mask = g_log_set_always_fatal(G_LOG_FATAL_MASK); @@ -338,12 +338,12 @@ do_test_nm_utils_kill_child(void) "(No child process*, 10) after sending no signal (0)"); test_nm_utils_kill_child_sync_do("test-s-3-2", pid3s, 0, 0, FALSE, NULL); - NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-4' (*): waiting up to 1 milliseconds for " + NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-4' (*): waiting up to 50 milliseconds for " "process to terminate normally after sending SIGTERM (15)..."); NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-4' (*): sending SIGKILL..."); NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-4' (*): after sending SIGTERM (15) and " "SIGKILL, process * exited by signal 9 (* usec elapsed)"); - test_nm_utils_kill_child_sync_do("test-s-4", pid4s, SIGTERM, 1, TRUE, &expected_signal_KILL); + test_nm_utils_kill_child_sync_do("test-s-4", pid4s, SIGTERM, 50, TRUE, &expected_signal_KILL); NMTST_EXPECT_NM_DEBUG("kill child process 'test-a-1-1' (*): wait for process to terminate " "after sending SIGTERM (15) (send SIGKILL in 3000 milliseconds)..."); @@ -528,27 +528,27 @@ test_nm_utils_array_remove_at_indexes(void) _remove_at_indexes_init_random_idx(idx, i_len, i_idx_len); g_array_set_size(array, i_len); for (i = 0; i < i_len; i++) - g_array_index(array, gssize, i) = i; + nm_g_array_index(array, gssize, i) = i; - nm_utils_array_remove_at_indexes(array, &g_array_index(idx, guint, 0), i_idx_len); + nm_utils_array_remove_at_indexes(array, nm_g_array_first_p(idx, guint), i_idx_len); g_hash_table_remove_all(unique); /* ensure that all the indexes are still unique */ for (i = 0; i < array->len; i++) - g_hash_table_add(unique, GUINT_TO_POINTER(g_array_index(array, gssize, i))); + g_hash_table_add(unique, GUINT_TO_POINTER(nm_g_array_index(array, gssize, i))); g_assert_cmpint(g_hash_table_size(unique), ==, array->len); for (i = 0; i < idx->len; i++) - g_hash_table_add(unique, GUINT_TO_POINTER(g_array_index(idx, guint, i))); + g_hash_table_add(unique, GUINT_TO_POINTER(nm_g_array_index(idx, guint, i))); g_assert_cmpint(g_hash_table_size(unique), ==, i_len); /* ensure proper sort order in array */ for (i = 0; i < array->len; i++) { - gssize i1 = g_array_index(array, gssize, i); + gssize i1 = nm_g_array_index(array, gssize, i); g_assert(i1 >= 0 && i1 < i_len); if (i > 0) { - gsize i0 = g_array_index(array, gssize, i - 1); + gsize i0 = nm_g_array_index(array, gssize, i - 1); g_assert_cmpint(i0, <, i1); } } diff --git a/src/core/tests/test-core.c b/src/core/tests/test-core.c index c260788e..887803bf 100644 --- a/src/core/tests/test-core.c +++ b/src/core/tests/test-core.c @@ -40,7 +40,7 @@ test_config_h(void) /*****************************************************************************/ -/* Reference implementation for nm_utils_ip6_address_clear_host_address. +/* Reference implementation for nm_ip6_addr_clear_host_address. * Taken originally from set_address_masked(), src/ndisc/nm-lndp-ndisc.c **/ static void @@ -95,14 +95,14 @@ test_nm_utils_ip6_address_clear_host_address(void) _randomize_in6_addr(&addr1, r); _randomize_in6_addr(&addr2, r); addr1 = addr_src; - nm_utils_ip6_address_clear_host_address(&addr2, &addr1, plen); + nm_ip6_addr_clear_host_address(&addr2, &addr1, plen); g_assert_cmpint(memcmp(&addr1, &addr_src, sizeof(struct in6_addr)), ==, 0); g_assert_cmpint(memcmp(&addr2, &addr_ref, sizeof(struct in6_addr)), ==, 0); /* test for self assignment/inplace update. */ _randomize_in6_addr(&addr1, r); addr1 = addr_src; - nm_utils_ip6_address_clear_host_address(&addr1, &addr1, plen); + nm_ip6_addr_clear_host_address(&addr1, &addr1, plen); g_assert_cmpint(memcmp(&addr1, &addr_ref, sizeof(struct in6_addr)), ==, 0); } } @@ -152,14 +152,14 @@ test_logging_error(void) static void _test_same_prefix(const char *a1, const char *a2, guint8 plen) { - struct in6_addr a = *nmtst_inet6_from_string(a1); - struct in6_addr b = *nmtst_inet6_from_string(a2); + struct in6_addr a = nmtst_inet6_from_string(a1); + struct in6_addr b = nmtst_inet6_from_string(a2); - g_assert(nm_utils_ip6_address_same_prefix(&a, &b, plen)); + g_assert(nm_ip6_addr_same_prefix(&a, &b, plen)); } static void -test_nm_utils_ip6_address_same_prefix(void) +test_nm_ip_addr_same_prefix(void) { guint n, i; const guint N = 100; @@ -202,7 +202,7 @@ again_plen: } else b = a; - result = nm_utils_ip6_address_same_prefix(&a.val, &b.val, plen); + result = nm_ip6_addr_same_prefix(&a.val, &b.val, plen); g_assert(result == is_same); g_assert(NM_IN_SET(result, TRUE, FALSE)); } @@ -214,12 +214,12 @@ again_plen: plen = nmtst_get_rand_uint32() % 129; memset(addrmask.ptr, 0xFF, sizeof(addrmask)); - nm_utils_ip6_address_clear_host_address(&addrmask.val, &addrmask.val, plen); + nm_ip6_addr_clear_host_address(&addrmask.val, &addrmask.val, plen); for (i = 0; i < sizeof(a); i++) b.ptr[i] = (a.ptr[i] & addrmask.ptr[i]) | (b.ptr[i] & ~addrmask.ptr[i]); - g_assert(nm_utils_ip6_address_same_prefix(&a.val, &b.val, plen) == TRUE); + g_assert(nm_ip6_addr_same_prefix(&a.val, &b.val, plen) == TRUE); } /* test#3 */ @@ -234,10 +234,10 @@ again_plen: continue; memset(addrmask.ptr, 0xFF, sizeof(addrmask)); - nm_utils_ip6_address_clear_host_address(&addrmask.val, &addrmask.val, plen); + nm_ip6_addr_clear_host_address(&addrmask.val, &addrmask.val, plen); memset(addrmask_bit.ptr, 0xFF, sizeof(addrmask_bit)); - nm_utils_ip6_address_clear_host_address(&addrmask_bit.val, &addrmask_bit.val, plen - 1); + nm_ip6_addr_clear_host_address(&addrmask_bit.val, &addrmask_bit.val, plen - 1); for (i = 0; i < sizeof(a); i++) b.ptr[i] = (a.ptr[i] & addrmask.ptr[i]) | (b.ptr[i] & ~addrmask.ptr[i]); @@ -254,7 +254,7 @@ again_plen: } g_assert(reached); - g_assert(nm_utils_ip6_address_same_prefix(&a.val, &b.val, plen) == FALSE); + g_assert(nm_ip6_addr_same_prefix(&a.val, &b.val, plen) == FALSE); } /* test#4 */ @@ -2594,10 +2594,9 @@ main(int argc, char **argv) g_test_add_func("/general/nm_strbuf_append", test_nm_utils_strbuf_append); - g_test_add_func("/general/nm_utils_ip6_address_clear_host_address", + g_test_add_func("/general/nm_ip6_addr_clear_host_address", test_nm_utils_ip6_address_clear_host_address); - g_test_add_func("/general/nm_utils_ip6_address_same_prefix", - test_nm_utils_ip6_address_same_prefix); + g_test_add_func("/general/nm_ip6_addr_same_prefix", test_nm_ip_addr_same_prefix); g_test_add_func("/general/nm_utils_log_connection_diff", test_nm_utils_log_connection_diff); g_test_add_func("/general/nm_utils_sysctl_ip_conf_path", test_nm_utils_sysctl_ip_conf_path); diff --git a/src/core/tests/test-l3cfg.c b/src/core/tests/test-l3cfg.c index a9ac4004..962b2eec 100644 --- a/src/core/tests/test-l3cfg.c +++ b/src/core/tests/test-l3cfg.c @@ -2,7 +2,7 @@ #include "src/core/nm-default-daemon.h" -#include +#include "nm-compat-headers/linux/if_addr.h" #include "nm-l3cfg.h" #include "nm-l3-ipv4ll.h" @@ -339,6 +339,10 @@ test_l3cfg(gconstpointer test_data) _LOGD("test start (/l3cfg/%d)", TEST_IDX); + /* FIXME: https://bugzilla.redhat.com/show_bug.cgi?id=2165141 */ + g_test_skip("Skip: this test is currently known to fail (rhbz#2165141)"); + return; + if (nmtst_test_quick()) { gs_free char *msg = g_strdup_printf("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n", @@ -418,7 +422,7 @@ test_l3cfg(gconstpointer test_data) nm_l3_config_data_add_address_6( l3cd, - NM_PLATFORM_IP6_ADDRESS_INIT(.address = *nmtst_inet6_from_string("1:2:3:4::45"), + NM_PLATFORM_IP6_ADDRESS_INIT(.address = nmtst_inet6_from_string("1:2:3:4::45"), .plen = 64, )); if (nmtst_get_rand_one_case_in(2)) @@ -572,7 +576,7 @@ _test_l3_ipv4ll_signal_notify(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, TestL3IPv4LLData *tdata) { - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; g_assert(NM_IS_L3CFG(l3cfg)); g_assert(tdata); @@ -593,9 +597,9 @@ _test_l3_ipv4ll_signal_notify(NML3Cfg *l3cfg, if (tdata->f->test_idx == 2 && nmtst_get_rand_bool()) { tdata->addr_commit++; tdata->addr_commit_addr = nm_l3_ipv4ll_get_addr(tdata->l3ipv4ll); - g_assert(nm_utils_ip4_address_is_link_local(tdata->addr_commit_addr)); + g_assert(nm_ip4_addr_is_link_local(tdata->addr_commit_addr)); _LOGT("add address %s that passed ACD", - _nm_utils_inet4_ntop(tdata->addr_commit_addr, sbuf_addr)); + nm_inet4_ntop(tdata->addr_commit_addr, sbuf_addr)); if (!nm_l3cfg_add_config(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll), TEST_L3_IPV4LL_TAG(tdata, 1), nmtst_get_rand_bool(), @@ -630,9 +634,9 @@ _test_l3_ipv4ll_signal_notify(NML3Cfg *l3cfg, if (tdata->addr_commit > 0) { g_assert_cmpint(tdata->addr_commit, ==, 1); tdata->addr_commit--; - g_assert(nm_utils_ip4_address_is_link_local(tdata->addr_commit_addr)); + g_assert(nm_ip4_addr_is_link_local(tdata->addr_commit_addr)); _LOGT("remove address %s that previously passed ACD", - _nm_utils_inet4_ntop(tdata->addr_commit_addr, sbuf_addr)); + nm_inet4_ntop(tdata->addr_commit_addr, sbuf_addr)); if (!nm_l3cfg_remove_config_all(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll), TEST_L3_IPV4LL_TAG(tdata, 1))) g_assert_not_reached(); @@ -663,7 +667,7 @@ test_l3_ipv4ll(gconstpointer test_data) gint64 start_time_msec; gint64 total_poll_time_msec; nm_auto_remove_l3ipv4ll_registration NML3IPv4LLRegistration *l3ipv4ll_reg = NULL; - char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf_addr[NM_INET_ADDRSTRLEN]; _LOGD("test start (/l3-ipv4ll/%d)", TEST_IDX); @@ -733,12 +737,12 @@ test_l3_ipv4ll(gconstpointer test_data) if (tdata->f->test_idx == 2 && nmtst_get_rand_one_case_in(3)) { in_addr_t a = nm_l3_ipv4ll_get_addr(l3ipv4ll); - g_assert(nm_utils_ip4_address_is_link_local(a)); + g_assert(nm_ip4_addr_is_link_local(a)); _test_l3_ipv4ll_maybe_add_addr_4(tdata, tdata->f->ifindex1, 2, &tdata->add_conflict_done, - _nm_utils_inet4_ntop(a, sbuf_addr)); + nm_inet4_ntop(a, sbuf_addr)); g_assert_cmpint(tdata->f->hwaddr1.len, ==, sizeof(NMEtherAddr)); acd_defender_2 = nmtstp_acd_defender_new(tdata->f->ifindex1, a, &tdata->f->hwaddr1.ether_addr); @@ -768,12 +772,11 @@ test_l3_ipv4ll(gconstpointer test_data) if (tdata->addr_commit == 1 && !tdata->add_conflict_checked) { tdata->add_conflict_checked = TRUE; - _test_l3_ipv4ll_maybe_add_addr_4( - tdata, - tdata->f->ifindex1, - 2, - &tdata->add_conflict_done, - _nm_utils_inet4_ntop(tdata->addr_commit_addr, sbuf_addr)); + _test_l3_ipv4ll_maybe_add_addr_4(tdata, + tdata->f->ifindex1, + 2, + &tdata->add_conflict_done, + nm_inet4_ntop(tdata->addr_commit_addr, sbuf_addr)); if (tdata->add_conflict_done) total_poll_time_msec += L3IPV4LL_ACD_TIMEOUT_MSEC / 2; g_assert_cmpint(tdata->f->hwaddr1.len, ==, sizeof(NMEtherAddr)); @@ -986,7 +989,7 @@ test_l3_ipv6ll(gconstpointer test_data) nmtstp_ip6_address_add(tdata->f->platform, -1, tdata->f->ifindex1, - *nmtst_inet6_from_string(_LLADDR_TEST1), + nmtst_inet6_from_string(_LLADDR_TEST1), 64, in6addr_any, NM_PLATFORM_LIFETIME_PERMANENT, diff --git a/src/core/tests/test-systemd.c b/src/core/tests/test-systemd.c index cf31ec33..1b0b7f65 100644 --- a/src/core/tests/test-systemd.c +++ b/src/core/tests/test-systemd.c @@ -12,21 +12,6 @@ /*****************************************************************************/ -static void -test_lldp_create(void) -{ - sd_lldp_rx *lldp = NULL; - int r; - - r = sd_lldp_rx_new(&lldp); - g_assert(r == 0); - g_assert(lldp); - - sd_lldp_rx_unref(lldp); -} - -/*****************************************************************************/ - typedef struct { GMainLoop *mainloop; sd_event_source *event_source; @@ -105,7 +90,6 @@ main(int argc, char **argv) { nmtst_init(&argc, &argv, TRUE); - g_test_add_func("/systemd/lldp/create", test_lldp_create); g_test_add_func("/systemd/sd-event", test_sd_event); return g_test_run(); diff --git a/src/core/vpn/nm-vpn-connection.c b/src/core/vpn/nm-vpn-connection.c index bbb73550..4fb926a5 100644 --- a/src/core/vpn/nm-vpn-connection.c +++ b/src/core/vpn/nm-vpn-connection.c @@ -107,14 +107,15 @@ typedef struct { NMIPAddr gw_internal; NMIPAddr gw_external; - /* Whether this address family is enabled. If not, then we won't have a l3cd instance, - * but the activation for this address family is still complete. */ + /* Whether VPN auto-configuration is enabled in the connection profile for + * this address family. */ + bool method_auto : 1; + + /* Whether VPN auto-configuration is enabled, in the connection profile AND + * in the configuration reported by the VPN. If not, then we won't have a + * l3cd instance, but the activation for this address family is still + * complete. */ bool enabled : 1; - - /* Whether this address family is ready. This means we received the IP configuration. - * Usually this implies we also have a corresponding l3cd, but that might not be the - * case if this address family is disabled. */ - bool conf_ready : 1; } IPData; typedef struct { @@ -1217,7 +1218,7 @@ _parent_device_l3cd_add_gateway_route(NML3ConfigData *l3cd, if (!nm_ip_addr_is_null(addr_family, gw)) { nm_ip_addr_set(addr_family, &parent_gw, gw); has_parent_gw = TRUE; - } else if (nm_utils_ip_is_site_local(addr_family, vpn_gw)) + } else if (nm_ip_addr_is_site_local(addr_family, vpn_gw)) has_parent_gw = TRUE; else if ((obj = nm_device_get_best_default_route(parent_device, addr_family)) && nm_ip_addr_is_null( @@ -1296,6 +1297,17 @@ _l3cfg_l3cd_gw_extern_update(NMVpnConnection *self) changed = FALSE; for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + const int addr_family = IS_IPv4 ? AF_INET : AF_INET6; + NMSettingIPConfig *s_ip; + + s_ip = nm_connection_get_setting_ip_config(_get_applied_connection(self), addr_family); + if (s_ip && nm_setting_ip_config_get_auto_route_ext_gw(s_ip) == NM_TERNARY_FALSE) { + _LOGD("IPv%c route to the external gateway have been deactivated via auto-route-ext-gw " + "setting", + nm_utils_addr_family_to_char(addr_family)); + continue; + } + if (_parent_device_l3cd_add_gateway_route( l3cd, IS_IPv4 ? AF_INET : AF_INET6, @@ -1870,9 +1882,16 @@ _dbus_signal_config_cb(NMVpnConnection *self, GVariant *dict) else priv->ip_data_6.enabled = FALSE; - _LOGD("config: reply received (IPv4:%s, IPv6:%s)", + _LOGD("config: reply received (IPv4:%s(%s), IPv6:%s(%s))", priv->ip_data_4.enabled ? "on" : "off", - priv->ip_data_6.enabled ? "on" : "off"); + priv->ip_data_4.method_auto ? "auto" : "disabled", + priv->ip_data_4.enabled ? "on" : "off", + priv->ip_data_6.method_auto ? "auto" : "disabled"); + + if (!priv->ip_data_4.method_auto) + priv->ip_data_4.enabled = FALSE; + if (!priv->ip_data_6.method_auto) + priv->ip_data_6.enabled = FALSE; if (priv->vpn_state == STATE_CONNECT) _set_vpn_state(self, STATE_IP_CONFIG_GET, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); @@ -1936,7 +1955,8 @@ _dbus_signal_ip_config_cb(NMVpnConnection *self, int addr_family, GVariant *dict return; } - priv->ip_data_4.enabled = TRUE; + if (priv->ip_data_4.method_auto) + priv->ip_data_4.enabled = TRUE; priv->ip_data_6.enabled = FALSE; } } else { @@ -1953,6 +1973,11 @@ _dbus_signal_ip_config_cb(NMVpnConnection *self, int addr_family, GVariant *dict _set_vpn_state(self, STATE_IP_CONFIG_GET, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); } + if (!priv->ip_data_x[IS_IPv4].enabled) { + _check_complete(self, TRUE); + return; + } + ip_ifindex = nm_vpn_connection_get_ip_ifindex(self, TRUE); if (ip_ifindex <= 0) g_return_if_reached(); @@ -2011,14 +2036,14 @@ _dbus_signal_ip_config_cb(NMVpnConnection *self, int addr_family, GVariant *dict if (IS_IPv4) { if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_DNS, "au", &var_iter)) { while (g_variant_iter_next(var_iter, "u", &u32)) - nm_l3_config_data_add_nameserver(l3cd, addr_family, &u32); + nm_l3_config_data_add_nameserver_detail(l3cd, addr_family, &u32, NULL); g_variant_iter_free(var_iter); } } else { if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_DNS, "aay", &var_iter)) { while (g_variant_iter_next(var_iter, "@ay", &v)) { if (nm_ip_addr_set_from_variant(AF_INET6, &v_addr, v, NULL)) - nm_l3_config_data_add_nameserver(l3cd, addr_family, &v_addr); + nm_l3_config_data_add_nameserver_detail(l3cd, addr_family, &v_addr, NULL); g_variant_unref(v); } g_variant_iter_free(var_iter); @@ -2104,9 +2129,8 @@ _dbus_signal_ip_config_cb(NMVpnConnection *self, int addr_family, GVariant *dict if (plen > 32) break; - route.r4.plen = plen; - route.r4.network = - nm_utils_ip4_address_clear_host_address(route.r4.network, plen); + route.r4.plen = plen; + route.r4.network = nm_ip4_addr_clear_host_address(route.r4.network, plen); if (priv->ip_data_4.gw_external.addr4 && route.r4.network == priv->ip_data_4.gw_external.addr4 @@ -2146,9 +2170,7 @@ _dbus_signal_ip_config_cb(NMVpnConnection *self, int addr_family, GVariant *dict nm_ip_addr_set_from_variant(AF_INET6, &route.r6.gateway, next_hop, NULL); - nm_utils_ip6_address_clear_host_address(&route.r6.network, - &route.r6.network, - route.r6.plen); + nm_ip6_addr_clear_host_address(&route.r6.network, &route.r6.network, route.r6.plen); if (!IN6_IS_ADDR_UNSPECIFIED(&priv->ip_data_6.gw_external.addr6) && IN6_ARE_ADDR_EQUAL(&route.r6.network, &priv->ip_data_6.gw_external.addr6) @@ -2761,6 +2783,11 @@ nm_vpn_connection_activate(NMVpnConnection *self, NMVpnPluginInfo *plugin_info) _LOGI("starting %s", nm_vpn_plugin_info_get_name(plugin_info)); + priv->ip_data_4.method_auto = nm_streq0(nm_utils_get_ip_config_method(connection, AF_INET), + NM_SETTING_IP4_CONFIG_METHOD_AUTO); + priv->ip_data_6.method_auto = nm_streq0(nm_utils_get_ip_config_method(connection, AF_INET6), + NM_SETTING_IP6_CONFIG_METHOD_AUTO); + priv->connection_can_persist = nm_setting_vpn_get_persistent(s_vpn); priv->plugin_info = g_object_ref(plugin_info); @@ -2841,17 +2868,11 @@ device_changed(NMActiveConnection *active, NMDevice *new_device, NMDevice *old_d _l3cfg_clear(self, l3cfg_old); priv->ifindex_dev = ifindex; - if (ifindex > 0) { - priv->l3cfg_dev = nm_netns_l3cfg_acquire(priv->netns, ifindex); - g_signal_connect(priv->l3cfg_dev, - NM_L3CFG_SIGNAL_NOTIFY, - G_CALLBACK(_l3cfg_notify_cb), - self); - priv->l3cfg_commit_type_dev = nm_l3cfg_commit_type_register(priv->l3cfg_dev, - NM_L3_CFG_COMMIT_TYPE_UPDATE, - NULL, - "vpn"); - } + + priv->l3cfg_dev = nm_netns_l3cfg_acquire(priv->netns, ifindex); + g_signal_connect(priv->l3cfg_dev, NM_L3CFG_SIGNAL_NOTIFY, G_CALLBACK(_l3cfg_notify_cb), self); + priv->l3cfg_commit_type_dev = + nm_l3cfg_commit_type_register(priv->l3cfg_dev, NM_L3_CFG_COMMIT_TYPE_UPDATE, NULL, "vpn"); if (_l3cfg_l3cd_gw_extern_update(self)) nm_l3cfg_commit_on_idle_schedule(priv->l3cfg_dev, NM_L3_CFG_COMMIT_TYPE_AUTO); diff --git a/src/core/vpn/nm-vpn-connection.h b/src/core/vpn/nm-vpn-connection.h index 51e2e500..f9c38322 100644 --- a/src/core/vpn/nm-vpn-connection.h +++ b/src/core/vpn/nm-vpn-connection.h @@ -15,7 +15,7 @@ #define NM_TYPE_VPN_CONNECTION (nm_vpn_connection_get_type()) #define NM_VPN_CONNECTION(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_VPN_CONNECTION, NMVpnConnection)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_VPN_CONNECTION, NMVpnConnection)) #define NM_VPN_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_VPN_CONNECTION, NMVpnConnectionClass)) #define NM_IS_VPN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_VPN_CONNECTION)) diff --git a/src/core/vpn/nm-vpn-manager.h b/src/core/vpn/nm-vpn-manager.h index 41ecb8d2..cd691dd1 100644 --- a/src/core/vpn/nm-vpn-manager.h +++ b/src/core/vpn/nm-vpn-manager.h @@ -10,7 +10,8 @@ #include "nm-vpn-connection.h" #define NM_TYPE_VPN_MANAGER (nm_vpn_manager_get_type()) -#define NM_VPN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_VPN_MANAGER, NMVpnManager)) +#define NM_VPN_MANAGER(obj) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_VPN_MANAGER, NMVpnManager)) #define NM_VPN_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_VPN_MANAGER, NMVpnManagerClass)) #define NM_IS_VPN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_VPN_MANAGER)) diff --git a/src/libnm-base/nm-base.h b/src/libnm-base/nm-base.h index ff062a18..74e8142f 100644 --- a/src/libnm-base/nm-base.h +++ b/src/libnm-base/nm-base.h @@ -393,6 +393,12 @@ typedef struct { #define NM_BOND_PORT_QUEUE_ID_DEF 0 +/****************************************************************************/ + +/* ifindex generation is per-net namespace, and loopback is always the first + * device in the network namespace, thus any loopback device should get ifindex 1. */ +#define NM_LOOPBACK_IFINDEX 1 + /*****************************************************************************/ /* NM_CRYPTO_ERROR is part of public API in libnm (implemented in libnm-core). @@ -409,4 +415,12 @@ typedef struct { #define _NM_CRYPTO_ERROR _nm_crypto_error_quark() GQuark _nm_crypto_error_quark(void); +typedef enum { + NM_DNS_IP_CONFIG_TYPE_REMOVED = -1, + + NM_DNS_IP_CONFIG_TYPE_DEFAULT = 0, + NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE, + NM_DNS_IP_CONFIG_TYPE_VPN, +} NMDnsIPConfigType; + #endif /* __NM_LIBNM_BASE_H__ */ diff --git a/src/libnm-base/nm-ethtool-base.c b/src/libnm-base/nm-ethtool-base.c index a75df8a0..c31cff6f 100644 --- a/src/libnm-base/nm-ethtool-base.c +++ b/src/libnm-base/nm-ethtool-base.c @@ -271,12 +271,12 @@ nm_ethtool_data_get_by_optname(const char *optname) _ASSERT_data(); - idx = nm_utils_array_find_binary_search(_by_name, - sizeof(_by_name[0]), - _NM_ETHTOOL_ID_NUM, - optname, - _by_name_cmp, - NULL); + idx = nm_array_find_bsearch(_by_name, + _NM_ETHTOOL_ID_NUM, + sizeof(_by_name[0]), + optname, + _by_name_cmp, + NULL); return (idx < 0) ? NULL : nm_ethtool_data[_by_name[idx]]; } diff --git a/src/libnm-base/nm-ethtool-utils-base.h b/src/libnm-base/nm-ethtool-utils-base.h index 3656ed3f..fa9c3071 100644 --- a/src/libnm-base/nm-ethtool-utils-base.h +++ b/src/libnm-base/nm-ethtool-utils-base.h @@ -3,7 +3,7 @@ * Copyright (C) 2018 Red Hat, Inc. */ -#ifndef __NM_ETHTOOL_UTILS_H__ /* lgtm[cpp/duplicate-include-guard] */ +#ifndef __NM_ETHTOOL_UTILS_H__ #define __NM_ETHTOOL_UTILS_H__ G_BEGIN_DECLS diff --git a/src/libnm-client-aux-extern/nm-libnm-aux.c b/src/libnm-client-aux-extern/nm-libnm-aux.c index 573eed1d..82a75de6 100644 --- a/src/libnm-client-aux-extern/nm-libnm-aux.c +++ b/src/libnm-client-aux-extern/nm-libnm-aux.c @@ -135,3 +135,64 @@ nmc_client_new_waitsync(GCancellable *cancellable, } return TRUE; } + +/*****************************************************************************/ + +guint32 +nmc_client_has_version_info_v(NMClient *nmc) +{ + const guint32 *ver; + gsize len; + + ver = nm_client_get_version_info(nmc, &len); + if (len < 1) + return 0; + return ver[0]; +} + +gboolean +nmc_client_has_version_info_capability(NMClient *nmc, NMVersionInfoCapability capability) +{ + const guint32 *ver; + gsize len; + gsize idx; + gsize idx_hi; + gsize idx_lo; + + ver = nm_client_get_version_info(nmc, &len); + + if (len < 2) + return FALSE; + + len--; + ver++; + + idx = (gsize) capability; + if (idx >= G_MAXSIZE - 31u) + return FALSE; + + idx_hi = ((idx + 31u) / 32u); + idx_lo = (idx % 32u); + + if (idx_hi > len) + return FALSE; + + return NM_FLAGS_ANY(ver[idx_hi], (1ull << idx_lo)); +} + +gboolean +nmc_client_has_capability(NMClient *nmc, NMCapability capability) +{ + const guint32 *caps; + gsize len; + gsize i; + + caps = nm_client_get_capabilities(nmc, &len); + + for (i = 0; i < len; i++) { + if (caps[i] == capability) + return TRUE; + } + + return FALSE; +} diff --git a/src/libnm-client-aux-extern/nm-libnm-aux.h b/src/libnm-client-aux-extern/nm-libnm-aux.h index 44344fd0..ae949cc5 100644 --- a/src/libnm-client-aux-extern/nm-libnm-aux.h +++ b/src/libnm-client-aux-extern/nm-libnm-aux.h @@ -21,4 +21,8 @@ gboolean nmc_client_new_waitsync(GCancellable *cancellable, const char *first_property_name, ...); +guint32 nmc_client_has_version_info_v(NMClient *nmc); +gboolean nmc_client_has_version_info_capability(NMClient *nmc, NMVersionInfoCapability capability); +gboolean nmc_client_has_capability(NMClient *nmc, NMCapability capability); + #endif /* __NM_LIBNM_AUX_H__ */ diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver index 2478defa..c373c823 100644 --- a/src/libnm-client-impl/libnm.ver +++ b/src/libnm-client-impl/libnm.ver @@ -1878,3 +1878,44 @@ global: nm_utils_ip_routes_to_variant; nm_vpn_plugin_info_supports_multiple; } libnm_1_40_0; + +libnm_1_42_0 { +global: + nm_client_get_version_info; + nm_client_wait_shutdown; + nm_client_wait_shutdown_finish; + nm_device_loopback_get_type; + nm_device_reapply_flags_get_type; + nm_range_cmp; + nm_range_from_str; + nm_range_get_range; + nm_range_get_type; + nm_range_new; + nm_range_ref; + nm_range_to_str; + nm_range_unref; + nm_setting_ip_config_get_auto_route_ext_gw; + nm_setting_ip_config_get_dhcp_iaid; + nm_setting_ip_config_get_dhcp_iaid; + nm_setting_ip_tunnel_get_fwmark; + nm_setting_loopback_get_mtu; + nm_setting_loopback_get_type; + nm_setting_loopback_new; + nm_setting_ovs_dpdk_get_n_rxq_desc; + nm_setting_ovs_dpdk_get_n_txq_desc; + nm_setting_ovs_interface_get_ofport_request; + nm_setting_ovs_other_config_get_data; + nm_setting_ovs_other_config_get_data_keys; + nm_setting_ovs_other_config_get_type; + nm_setting_ovs_other_config_new; + nm_setting_ovs_other_config_set_data; + nm_setting_ovs_port_add_trunk; + nm_setting_ovs_port_clear_trunks; + nm_setting_ovs_port_get_num_trunks; + nm_setting_ovs_port_get_trunk; + nm_setting_ovs_port_remove_trunk; + nm_setting_ovs_port_remove_trunk_by_value; + nm_setting_vlan_get_protocol; + nm_utils_ensure_gtypes; + nm_version_info_capability_get_type; +} libnm_1_40_0; diff --git a/src/libnm-client-impl/meson.build b/src/libnm-client-impl/meson.build index d72ed545..143126c5 100644 --- a/src/libnm-client-impl/meson.build +++ b/src/libnm-client-impl/meson.build @@ -17,6 +17,7 @@ libnm_client_impl_sources = files( 'nm-device-generic.c', 'nm-device-infiniband.c', 'nm-device-ip-tunnel.c', + 'nm-device-loopback.c', 'nm-device-macsec.c', 'nm-device-macvlan.c', 'nm-device-modem.c', diff --git a/src/libnm-client-impl/nm-client.c b/src/libnm-client-impl/nm-client.c index 871c5466..44a96994 100644 --- a/src/libnm-client-impl/nm-client.c +++ b/src/libnm-client-impl/nm-client.c @@ -31,6 +31,7 @@ #include "nm-device-generic.h" #include "nm-device-infiniband.h" #include "nm-device-ip-tunnel.h" +#include "nm-device-loopback.h" #include "nm-device-macsec.h" #include "nm-device-macvlan.h" #include "nm-device-modem.h" @@ -216,6 +217,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMClient, PROP_DNS_RC_MANAGER, PROP_DNS_CONFIGURATION, PROP_CHECKPOINTS, + PROP_VERSION_INFO, PROP_CAPABILITIES, PROP_PERMISSIONS_STATE, ); @@ -287,7 +289,7 @@ typedef struct { guint dbsid_nm_vpn_connection_state_changed; guint dbsid_nm_check_permissions; - NMClientInstanceFlags instance_flags : 3; + NMClientInstanceFlags instance_flags : 5; NMTernary permissions_state : 3; @@ -304,7 +306,9 @@ typedef struct { char *connectivity_check_uri; char *version; guint32 *capabilities_arr; + guint32 *version_info_arr; gsize capabilities_len; + gsize version_info_len; guint32 connectivity; guint32 state; guint32 metered; @@ -1253,9 +1257,9 @@ nml_dbus_object_get_property_location(NMLDBusObject *dbobj, { char *target_c; - target_c = (char *) dbobj->nmobj; + target_c = ((gpointer) dbobj->nmobj); if (meta_iface->base_struct_offset > 0) - target_c = *((gpointer *) (&target_c[meta_iface->base_struct_offset])); + target_c = *NM_CAST_ALIGN(gpointer, &target_c[meta_iface->base_struct_offset]); return &target_c[meta_property->prop_struct_offset]; } @@ -2630,7 +2634,16 @@ _obj_handle_dbus_iface_changes(NMClient *self, if (is_removed) { for (i_prop = 0; i_prop < db_iface_data->dbus_iface.meta->n_dbus_properties; i_prop++) { - _obj_handle_dbus_prop_changes(self, dbobj, db_iface_data, i_prop, NULL); + const GVariantType *dbus_type = + db_iface_data->dbus_iface.meta->dbus_properties[i_prop].dbus_type; + + /* Unset properties that can potentially contain objects, to release them, + * but keep the rest around, because it might still make sense to know what + * they were (e.g. when a device has been removed we'd like know what interface + * name it had, or keep the state to avoid spurious state change into UNKNOWN). */ + if (g_variant_type_is_array(dbus_type) + || g_variant_type_equal(dbus_type, G_VARIANT_TYPE_OBJECT_PATH)) + _obj_handle_dbus_prop_changes(self, dbobj, db_iface_data, i_prop, NULL); } } else { while ((db_prop_data = c_list_first_entry(&db_iface_data->changed_prop_lst_head, @@ -6290,6 +6303,57 @@ nm_client_get_capabilities(NMClient *client, gsize *length) return priv->nm.capabilities_arr; } +/** + * nm_client_get_version_info: + * @client: the #NMClient instance + * @length: (out): the number of returned capabilities. + * + * If available, the first element in the array is NM_VERSION which + * encodes the daemon version as "(major << 16 | minor << 8 | micro)". + * The following elements are a bitfield of %NMVersionInfoCapabilities + * that indicate that the daemon supports a certain capability. + * + * Returns: (transfer none) (array length=length): the + * list of capabilities reported by the server or %NULL + * if the capabilities are unknown. + * + * Since: 1.42 + */ +const guint32 * +nm_client_get_version_info(NMClient *client, gsize *length) +{ + NMClientPrivate *priv; + + g_return_val_if_fail(NM_IS_CLIENT(client), NULL); + g_return_val_if_fail(length, NULL); + + priv = NM_CLIENT_GET_PRIVATE(client); + + *length = priv->nm.version_info_len; + return priv->nm.version_info_arr; +} + +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_nm_au(guint32 **p_arr, gsize *p_len, GVariant *value) +{ + nm_clear_g_free(p_arr); + *p_len = 0; + + if (value) { + const guint32 *arr; + gsize len; + + arr = g_variant_get_fixed_array(value, &len, sizeof(guint32)); + *p_len = len; + *p_arr = g_new(guint32, len + 1); + if (len > 0) + memcpy(*p_arr, arr, len * sizeof(guint32)); + (*p_arr)[len] = 0; + } + + return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; +} + static NMLDBusNotifyUpdatePropFlags _notify_update_prop_nm_capabilities(NMClient *self, NMLDBusObject *dbobj, @@ -6301,22 +6365,21 @@ _notify_update_prop_nm_capabilities(NMClient *self, nm_assert(G_OBJECT(self) == dbobj->nmobj); - nm_clear_g_free(&priv->nm.capabilities_arr); - priv->nm.capabilities_len = 0; + return _notify_update_prop_nm_au(&priv->nm.capabilities_arr, &priv->nm.capabilities_len, value); +} - if (value) { - const guint32 *arr; - gsize len; +static NMLDBusNotifyUpdatePropFlags +_notify_update_prop_nm_version_info(NMClient *self, + NMLDBusObject *dbobj, + const NMLDBusMetaIface *meta_iface, + guint dbus_property_idx, + GVariant *value) +{ + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); - arr = g_variant_get_fixed_array(value, &len, sizeof(guint32)); - priv->nm.capabilities_len = len; - priv->nm.capabilities_arr = g_new(guint32, len + 1); - if (len > 0) - memcpy(priv->nm.capabilities_arr, arr, len * sizeof(guint32)); - priv->nm.capabilities_arr[len] = 0; - } + nm_assert(G_OBJECT(self) == dbobj->nmobj); - return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY; + return _notify_update_prop_nm_au(&priv->nm.version_info_arr, &priv->nm.version_info_len, value); } /*****************************************************************************/ @@ -7279,7 +7342,8 @@ nml_cleanup_context_busy_watcher_on_idle(GObject *context_busy_watcher_take, GMa static void _init_start_complete(NMClient *self, GError *error_take) { - NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); + gs_unref_object NMClient *self_keep_alive = g_object_ref(self); + NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self); NML_NMCLIENT_LOG_D( self, @@ -7287,6 +7351,11 @@ _init_start_complete(NMClient *self, GError *error_take) priv->init_data->is_sync ? "sync" : "async", NM_PRINT_FMT_QUOTED(error_take, "error: ", error_take->message, "", "success")); + priv->instance_flags |= (error_take ? NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_BAD + : NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_GOOD); + + _notify(self, PROP_INSTANCE_FLAGS); + nml_init_data_return(g_steal_pointer(&priv->init_data), error_take); } @@ -7525,13 +7594,15 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) case PROP_CHECKPOINTS: g_value_take_boxed(value, _nm_utils_copy_object_array(nm_client_get_checkpoints(self))); break; + case PROP_VERSION_INFO: case PROP_CAPABILITIES: { const guint32 *arr; GArray *out; gsize len; - arr = nm_client_get_capabilities(self, &len); + arr = (prop_id == PROP_VERSION_INFO) ? nm_client_get_version_info(self, &len) + : nm_client_get_capabilities(self, &len); if (arr) { out = g_array_new(TRUE, FALSE, sizeof(guint32)); g_array_append_vals(out, arr, len); @@ -7587,8 +7658,18 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps /* construct */ v_uint = g_value_get_uint(value); - g_return_if_fail(!NM_FLAGS_ANY(v_uint, ~((guint) NM_CLIENT_INSTANCE_FLAGS_ALL))); - v_uint &= ((guint) NM_CLIENT_INSTANCE_FLAGS_ALL); + + /* Silently ignore "initialized-{good,bad}" flags. They are only set internally + * and cannot be change by the user. However, accept the caller to set them, + * so that + * nmc.props.instance_flags = nmc.props.instance_flags | NM.ClientInstanceFlags.NO_AUTO_FETCH_PERMISSIONS + * works. */ + v_uint &= ~((guint) (NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_GOOD + | NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_BAD)); + + g_return_if_fail(!NM_FLAGS_ANY(v_uint, ~((guint) NM_CLIENT_INSTANCE_FLAGS_ALL_WRITABLE))); + + v_uint &= ((guint) NM_CLIENT_INSTANCE_FLAGS_ALL_WRITABLE); if (!priv->instance_flags_constructed) { priv->instance_flags_constructed = TRUE; @@ -8078,6 +8159,10 @@ const NMLDBusMetaIface _nml_dbus_meta_iface_nm = NML_DBUS_META_IFACE_INIT_PROP( NML_DBUS_META_PROPERTY_INIT_B("Startup", PROP_STARTUP, NMClient, _priv.nm.startup), NML_DBUS_META_PROPERTY_INIT_U("State", PROP_STATE, NMClient, _priv.nm.state), NML_DBUS_META_PROPERTY_INIT_S("Version", PROP_VERSION, NMClient, _priv.nm.version), + NML_DBUS_META_PROPERTY_INIT_FCN("VersionInfo", + PROP_VERSION_INFO, + "au", + _notify_update_prop_nm_version_info, ), NML_DBUS_META_PROPERTY_INIT_IGNORE("WimaxEnabled", "b"), NML_DBUS_META_PROPERTY_INIT_IGNORE("WimaxHardwareEnabled", "b"), NML_DBUS_META_PROPERTY_INIT_B("WirelessEnabled", @@ -8177,6 +8262,9 @@ nm_client_class_init(NMClientClass *client_class) * property to know whether permissions are ready. Note that permissions are only fetched * when NMClient has a D-Bus name owner. * + * The flags %NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_GOOD and %NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_BAD + * cannot be set, however they will be returned by the getter after initialization completes. + * * Since: 1.24 */ obj_properties[PROP_INSTANCE_FLAGS] = g_param_spec_uint( @@ -8213,6 +8301,24 @@ nm_client_class_init(NMClientClass *client_class) NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + /** + * NMClient:version-info: (type GArray(guint32)) + * + * Expose version info and capabilities of NetworkManager. If non-empty, + * the first element is NM_VERSION, which encodes the version of the + * daemon as "(major << 16 | minor << 8 | micro)". The following elements + * is a bitfields of %NMVersionInfoCapabilities. If a bit is set, then + * the running NetworkManager has the respective capability. + * + * Since: 1.42 + */ + obj_properties[PROP_VERSION_INFO] = + g_param_spec_boxed(NM_CLIENT_VERSION_INFO, + "", + "", + G_TYPE_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + /** * NMClient:state: * @@ -8815,6 +8921,299 @@ nm_client_async_initable_iface_init(GAsyncInitableIface *iface) iface->init_finish = init_finish; } +/*****************************************************************************/ + +typedef struct { + GCancellable *cancellable; + GSource *integration_source; + GTask *task; + GSource *idle_source; + + /* A weakref to nm_client_get_context_busy_watcher() */ + GWeakRef weak_ref; + + gulong cancellable_id; + + guint64 log_ptr; + + int result; +} WaitShutdownData; + +G_LOCK_DEFINE_STATIC(wait_shutdown_mutex); + +static NM_CACHED_QUARK_FCN("nm.client.wait-shutdown", _wait_shutdown_get_quark); + +static void +_wait_shutdown_data_free(gpointer user_data) +{ + WaitShutdownData *data = user_data; + + nm_g_slice_free(data); +} + +static gboolean +_wait_shutdown_idle_cb(gpointer user_data) +{ + WaitShutdownData *data = user_data; + gs_unref_object GTask *task = NULL; + int result; + + nm_clear_g_source_inst(&data->idle_source); + + task = g_steal_pointer(&data->task); + + result = g_atomic_int_get(&data->result); + nm_assert(NM_IN_SET(result, 0, 1)); + + NML_DBUS_LOG(_NML_NMCLIENT_LOG_LEVEL_COERCE(NML_DBUS_LOG_LEVEL_TRACE), + "nmclient[" NM_HASH_OBFUSCATE_PTR_FMT + "]: wait-shutdown (" NM_HASH_OBFUSCATE_PTR_FMT ")" + "%s", + data->log_ptr, + NM_HASH_OBFUSCATE_PTR(data), + !result ? " cancelled" : " completed"); + + if (!result) { + GError *error = NULL; + + nm_utils_error_set_cancelled(&error, FALSE, NULL); + g_task_return_error(task, error); + } else + g_task_return_boolean(task, TRUE); + + return G_SOURCE_CONTINUE; +} + +static void +_wait_shutdown_data_clear(WaitShutdownData *data, gboolean result) +{ + gs_unref_object GObject *context_busy_watcher = NULL; + + if (!g_atomic_int_compare_and_exchange(&data->result, -1, !!result)) { + /* There was a race and the result is already provided. Nothing to + * do, except, if "result" indicates cancellation (FALSE), set data->result + * to FALSE to. Aside that, the completion is already in progress. */ + if (!result) + g_atomic_int_compare_and_exchange(&data->result, TRUE, FALSE); + return; + } + + nm_clear_g_signal_handler(data->cancellable, &data->cancellable_id); + nm_clear_g_source_inst(&data->integration_source); + g_clear_object(&data->cancellable); + + if (!result) { + /* This was a cancellation. We likely still have the qdata tracked. + * We need to remove it. */ + + context_busy_watcher = g_weak_ref_get(&data->weak_ref); + if (context_busy_watcher) { + GPtrArray *qdata_arr; + + G_LOCK(wait_shutdown_mutex); + qdata_arr = g_object_get_qdata(context_busy_watcher, _wait_shutdown_get_quark()); + if (qdata_arr && g_ptr_array_remove_fast(qdata_arr, data)) { + /* data->task had an additional reference, we return it now. */ + g_object_unref(data->task); + } + G_UNLOCK(wait_shutdown_mutex); + } + } + + g_weak_ref_clear(&data->weak_ref); + + /* We don't complete right away, instead always schedule an idle action + * on the caller's context. */ + data->idle_source = nm_g_source_attach( + nm_g_idle_source_new(G_PRIORITY_DEFAULT_IDLE, _wait_shutdown_idle_cb, data, NULL), + g_task_get_context(data->task)); +} + +static void +_wait_shutdown_qdata_cb(gpointer user_data) +{ + gs_unref_ptrarray GPtrArray *qdata_arr = user_data; + + while (qdata_arr->len > 0) { + WaitShutdownData *data; + + data = g_ptr_array_remove_index_fast(qdata_arr, qdata_arr->len - 1); + _wait_shutdown_data_clear(data, TRUE); + + /* data->task had an additional reference, we return it now. */ + g_object_unref(data->task); + } +} + +static void +_wait_shutdown_cancelled_cb(GCancellable *cancellable, gpointer user_data) +{ + _wait_shutdown_data_clear(g_task_get_task_data(user_data), FALSE); +} + +/** + * nm_client_wait_shutdown: + * @client: the #NMClient to shutdown. + * @integrate_maincontext: whether to hook the client's maincontext + * in the current thread default. Otherwise, you must ensure + * that the client's maincontext gets iterated so that it can complete. + * By integrating the maincontext in the current thread default, you + * may instead only iterate the latter. + * @cancellable: (allow-none): the #GCancellable to abort the shutdown. + * @callback: (nullable): a #GAsyncReadyCallback to call when the request + * is satisfied or %NULL if you don't care about the result of the + * method invocation. + * @user_data: the data to pass to @callback + * + * The way to stop #NMClient is by unrefing it. That will cancel all + * internally pending async operations. However, as async operations in + * NMClient use GTask, hence they cannot complete right away. Instead, + * their (internal) result callback still needs to be dispatched by iterating + * the client's main context. + * + * You thus cannot stop iterating the client's main context until + * everything is wrapped up. nm_client_get_context_busy_watcher() + * helps to watch how long that will be. + * + * This function automates that waiting. Like all glib async operations + * this honors the current g_main_context_get_thread_default(). + * + * In any case, to complete the shutdown, nm_client_get_main_context() + * must be iterated. If the current g_main_context_get_thread_default() is + * the same as nm_client_get_main_context(), then @integrate_maincontext + * is ignored. In that case, the caller is required to iterate the context + * for shutdown to complete. Otherwise, if g_main_context_get_thread_default() + * differs from nm_client_get_main_context() and @integrate_maincontext + * is %FALSE, the caller must make sure that both contexts are iterated + * until completion. Otherwise, if @integrate_maincontext is %TRUE, then + * nm_client_get_main_context() will be integrated in g_main_context_get_thread_default(). + * This means, the caller gives nm_client_get_main_context() up until the waiting + * completes, the function will acquire the context and hook it into + * g_main_context_get_thread_default(). + * It is a bug to request @integrate_maincontext while having nm_client_get_main_context() + * acquired or iterated otherwise because a context can only be acquired once + * at a time. + * + * Shutdown can only complete after all references to @client were released. + * + * It is possible to call this function multiple times for the same client. + * But note that with @integrate_maincontext the client's context is acquired, + * which can only be done once at a time. + * + * It is permissible to start waiting before the objects is fully initialized. + * + * The function really allows two separate things. To get a notification (callback) when + * shutdown is complete, and to integrate the client's context in another context. + * The latter case is useful if the client has a separate context and you hand it + * over to another GMainContext to wrap up. + * + * The main use is to have a NMClient and a separate GMainContext on a worker + * thread. When being done, you can hand over the cleanup of the context + * to g_main_context_default(), assuming that the main thread iterates + * the default context. In that case, you don't need to care about passing + * a callback to know when shutdown completed. + * + * Since: 1.42 + */ +void +nm_client_wait_shutdown(NMClient *client, + gboolean integrate_maincontext, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMClientPrivate *priv; + WaitShutdownData *data; + gs_unref_object GTask *task = NULL; + GQuark quark = _wait_shutdown_get_quark(); + GPtrArray *qdata_arr; + GSource *integration_source = NULL; + + g_return_if_fail(NM_IS_CLIENT(client)); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + + priv = NM_CLIENT_GET_PRIVATE(client); + + task = nm_g_task_new(NULL, cancellable, nm_client_wait_shutdown, callback, user_data); + + if (integrate_maincontext && g_task_get_context(task) != priv->main_context) { + integration_source = nm_utils_g_main_context_create_integrate_source(priv->main_context); + g_return_if_fail(integration_source); + g_source_attach(integration_source, g_task_get_context(task)); + } + + data = g_slice_new(WaitShutdownData); + *data = (WaitShutdownData){ + .cancellable = nm_g_object_ref(cancellable), + .task = g_object_ref(task), + .result = -1, + .integration_source = integration_source, + .log_ptr = NM_HASH_OBFUSCATE_PTR(client), + }; + /* The "data" itself stays alive as long as the task lives. That's important, because + * the callbacks _wait_shutdown_weak_ref_cb and _wait_shutdown_cancelled_cb + * rely on accessing "data", which must live long enough. */ + g_task_set_task_data(task, data, _wait_shutdown_data_free); + + g_weak_ref_init(&data->weak_ref, priv->context_busy_watcher); + + NML_DBUS_LOG(_NML_NMCLIENT_LOG_LEVEL_COERCE(NML_DBUS_LOG_LEVEL_TRACE), + "nmclient[" NM_HASH_OBFUSCATE_PTR_FMT + "]: wait-shutdown (" NM_HASH_OBFUSCATE_PTR_FMT ")" + "%s", + data->log_ptr, + NM_HASH_OBFUSCATE_PTR(data), + integration_source ? " (integrated main source)" : ""); + + /* I don't think g_object_weak_ref() + GWeakRef can actually be used in + * a race-free way here, because g_object_weak_ref() has no GDestroyNotify + * and cannot keep task alive to avoid races. Instead, implement a weak pointer + * notification via the qdata. + * + * Yes, getting cancellation thread-safe is rather complicated here! I think + * the code is correct though, and you can cancel the operation from + * any thread without races. */ + G_LOCK(wait_shutdown_mutex); + qdata_arr = g_object_get_qdata(priv->context_busy_watcher, quark); + if (!qdata_arr) { + qdata_arr = g_ptr_array_new(); + g_object_set_qdata_full(priv->context_busy_watcher, + quark, + qdata_arr, + _wait_shutdown_qdata_cb); + } + /* data->task gets an additional reference, take it now. */ + g_object_ref(data->task); + g_ptr_array_add(qdata_arr, data); + G_UNLOCK(wait_shutdown_mutex); + + if (data->cancellable) { + /* Take an additional reference on task. */ + data->cancellable_id = g_cancellable_connect(data->cancellable, + G_CALLBACK(_wait_shutdown_cancelled_cb), + g_object_ref(task), + g_object_unref); + } +} + +/** + * nm_client_wait_shutdown_finish: + * @result: a #GAsyncResult obtained from the #GAsyncReadyCallback passed to nm_client_wait_shutdown() + * @error: return location for error or %NULL + * + * Returns: %TRUE if waiting is complete successfully. In that case, all resources of the + * nmclient are wrapped up and released. This can only fail by user cancellation. + * + * Since: 1.42 + */ +gboolean +nm_client_wait_shutdown_finish(GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(nm_g_task_is_valid(result, NULL, nm_client_wait_shutdown), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + /***************************************************************************** * Backported symbols. Usually, new API is only added in new major versions * of NetworkManager (that is, on "master" branch). Sometimes however, we might diff --git a/src/libnm-client-impl/nm-conn-utils.c b/src/libnm-client-impl/nm-conn-utils.c index 5d04cd32..223c18e6 100644 --- a/src/libnm-client-impl/nm-conn-utils.c +++ b/src/libnm-client-impl/nm-conn-utils.c @@ -14,6 +14,13 @@ #include "libnm-glib-aux/nm-secret-utils.h" #include "nm-simple-connection.h" +/** + * SECTION:nm-conn-utils + * @short_description: Connection utilities + * + * Extra connection functionality. + */ + static gboolean _wg_complete_peer(GPtrArray **p_peers, NMWireGuardPeer *peer_take, @@ -271,11 +278,11 @@ nm_conn_wireguard_import(const char *filename, GError **error) int addr_family; int prefix_len; - if (!nm_utils_parse_inaddr_prefix_bin(AF_UNSPEC, - value_word, - &addr_family, - &addr_bin, - &prefix_len)) + if (!nm_inet_parse_with_prefix_bin(AF_UNSPEC, + value_word, + &addr_family, + &addr_bin, + &prefix_len)) goto fail_invalid_value; p_data_addr = (addr_family == AF_INET) ? &data_addr_v4 : &data_addr_v6; @@ -315,13 +322,12 @@ nm_conn_wireguard_import(const char *filename, GError **error) NMIPAddr addr_bin; int addr_family; - if (nm_utils_parse_inaddr_bin(AF_UNSPEC, value_word, &addr_family, &addr_bin)) { + if (nm_inet_parse_bin(AF_UNSPEC, value_word, &addr_family, &addr_bin)) { p_data_dns = (addr_family == AF_INET) ? &data_dns_v4 : &data_dns_v6; if (!*p_data_dns) *p_data_dns = g_ptr_array_new_with_free_func(g_free); - g_ptr_array_add(*p_data_dns, - nm_utils_inet_ntop_dup(addr_family, &addr_bin)); + g_ptr_array_add(*p_data_dns, nm_inet_ntop_dup(addr_family, &addr_bin)); continue; } diff --git a/src/libnm-client-impl/nm-default-libnm.h b/src/libnm-client-impl/nm-default-libnm.h index 859766a3..b9248e2b 100644 --- a/src/libnm-client-impl/nm-default-libnm.h +++ b/src/libnm-client-impl/nm-default-libnm.h @@ -27,6 +27,7 @@ #include "nm-client.h" #include "nm-vpn-connection.h" #include "nm-libnm-utils.h" +#include "nm-errors.h" /*****************************************************************************/ diff --git a/src/libnm-client-impl/nm-device-6lowpan.c b/src/libnm-client-impl/nm-device-6lowpan.c index fc1f59ba..4e649a6b 100644 --- a/src/libnm-client-impl/nm-device-6lowpan.c +++ b/src/libnm-client-impl/nm-device-6lowpan.c @@ -39,7 +39,7 @@ G_DEFINE_TYPE(NMDevice6Lowpan, nm_device_6lowpan, NM_TYPE_DEVICE) * * Returns: (transfer none): the device's parent device * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ NMDevice * nm_device_6lowpan_get_parent(NMDevice6Lowpan *device) diff --git a/src/libnm-client-impl/nm-device-dummy.c b/src/libnm-client-impl/nm-device-dummy.c index d56f6cda..a8fec6ff 100644 --- a/src/libnm-client-impl/nm-device-dummy.c +++ b/src/libnm-client-impl/nm-device-dummy.c @@ -23,9 +23,6 @@ struct _NMDeviceDummyClass { G_DEFINE_TYPE(NMDeviceDummy, nm_device_dummy, NM_TYPE_DEVICE) -#define NM_DEVICE_DUMMY_GET_PRIVATE(self) \ - _NM_GET_PRIVATE(self, NMDeviceDummy, NM_IS_DEVICE_DUMMY, NMObject, NMDevice) - /*****************************************************************************/ /** diff --git a/src/libnm-client-impl/nm-device-loopback.c b/src/libnm-client-impl/nm-device-loopback.c new file mode 100644 index 00000000..9163f38d --- /dev/null +++ b/src/libnm-client-impl/nm-device-loopback.c @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-device-loopback.h" + +#include "nm-object-private.h" +#include "nm-setting-loopback.h" +#include "nm-setting-connection.h" + +/*****************************************************************************/ + +struct _NMDeviceLoopback { + NMDevice parent; +}; + +struct _NMDeviceLoopbackClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMDeviceLoopback, nm_device_loopback, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static gboolean +connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +{ + const char *iface_name; + + if (!NM_DEVICE_CLASS(nm_device_loopback_parent_class) + ->connection_compatible(device, connection, error)) + return FALSE; + + if (!nm_connection_is_type(connection, NM_SETTING_LOOPBACK_SETTING_NAME)) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a loopback connection.")); + return FALSE; + } + + iface_name = nm_connection_get_interface_name(connection); + if (!iface_name) { + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection did not specify an interface name.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type(NMDevice *device) +{ + return NM_TYPE_SETTING_LOOPBACK; +} + +/*****************************************************************************/ + +static void +nm_device_loopback_init(NMDeviceLoopback *device) +{} + +const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_loopback = + NML_DBUS_META_IFACE_INIT(NM_DBUS_INTERFACE_DEVICE_LOOPBACK, + nm_device_loopback_get_type, + NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, ); + +static void +nm_device_loopback_class_init(NMDeviceLoopbackClass *loopback_class) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS(loopback_class); + + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; +} diff --git a/src/libnm-client-impl/nm-device-macsec.c b/src/libnm-client-impl/nm-device-macsec.c index 763faa6c..fc076402 100644 --- a/src/libnm-client-impl/nm-device-macsec.c +++ b/src/libnm-client-impl/nm-device-macsec.c @@ -65,7 +65,7 @@ G_DEFINE_TYPE(NMDeviceMacsec, nm_device_macsec, NM_TYPE_DEVICE) * * Returns: (transfer none): the device's parent device * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ NMDevice * nm_device_macsec_get_parent(NMDeviceMacsec *device) diff --git a/src/libnm-client-impl/nm-device-veth.c b/src/libnm-client-impl/nm-device-veth.c index 0d4537ed..9c825455 100644 --- a/src/libnm-client-impl/nm-device-veth.c +++ b/src/libnm-client-impl/nm-device-veth.c @@ -44,7 +44,7 @@ G_DEFINE_TYPE(NMDeviceVeth, nm_device_veth, NM_TYPE_DEVICE_ETHERNET) * * Returns: (transfer none): the device's peer device * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ NMDevice * nm_device_veth_get_peer(NMDeviceVeth *device) diff --git a/src/libnm-client-impl/nm-device-vxlan.c b/src/libnm-client-impl/nm-device-vxlan.c index 13c20536..f3027a52 100644 --- a/src/libnm-client-impl/nm-device-vxlan.c +++ b/src/libnm-client-impl/nm-device-vxlan.c @@ -99,7 +99,7 @@ nm_device_vxlan_get_hw_address(NMDeviceVxlan *device) * This property is not implemented yet, and the function always returns * FALSE. * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ gboolean nm_device_vxlan_get_carrier(NMDeviceVxlan *device) @@ -325,7 +325,7 @@ nm_device_vxlan_get_proxy(NMDeviceVxlan *device) * * Returns: whether route short circuit is turned on * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ gboolean nm_device_vxlan_get_rsc(NMDeviceVxlan *device) diff --git a/src/libnm-client-impl/nm-device-wifi-p2p.c b/src/libnm-client-impl/nm-device-wifi-p2p.c index 3d539111..2b3b82f8 100644 --- a/src/libnm-client-impl/nm-device-wifi-p2p.c +++ b/src/libnm-client-impl/nm-device-wifi-p2p.c @@ -100,7 +100,7 @@ nm_device_wifi_p2p_get_peers(NMDeviceWifiP2P *device) * * Returns: (transfer none): the peer or %NULL if none is found. * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ NMWifiP2PPeer * nm_device_wifi_p2p_get_peer_by_path(NMDeviceWifiP2P *device, const char *path) diff --git a/src/libnm-client-impl/nm-device.c b/src/libnm-client-impl/nm-device.c index 0e971d39..d5ecee48 100644 --- a/src/libnm-client-impl/nm-device.c +++ b/src/libnm-client-impl/nm-device.c @@ -312,6 +312,7 @@ coerce_type(NMDeviceType type) case NM_DEVICE_TYPE_WIREGUARD: case NM_DEVICE_TYPE_WIFI_P2P: case NM_DEVICE_TYPE_VRF: + case NM_DEVICE_TYPE_LOOPBACK: return type; } return NM_DEVICE_TYPE_UNKNOWN; @@ -1811,6 +1812,8 @@ get_type_name(NMDevice *device) return _("Wi-Fi P2P"); case NM_DEVICE_TYPE_VRF: return _("VRF"); + case NM_DEVICE_TYPE_LOOPBACK: + return _("Loopback"); case NM_DEVICE_TYPE_GENERIC: case NM_DEVICE_TYPE_UNUSED1: case NM_DEVICE_TYPE_UNUSED2: @@ -2979,7 +2982,8 @@ NM_IS_LLDP_NEIGHBOR(const NMLldpNeighbor *self) * Note that #NMLldpNeighbor has no public API for mutating * an instance. Also, libnm will not internally mutate a * once exposed object. They are guaranteed to be immutable. - * Since 1.32, ref-counting is thread-safe. + * + * Since 1.32, ref-counting of #NMLldpNeighbor is thread-safe. * * This function is not useful, as there is no public API to * actually modify the (empty) instance. diff --git a/src/libnm-client-impl/nm-dhcp-config.c b/src/libnm-client-impl/nm-dhcp-config.c index 1f908491..9730a649 100644 --- a/src/libnm-client-impl/nm-dhcp-config.c +++ b/src/libnm-client-impl/nm-dhcp-config.c @@ -146,7 +146,7 @@ nm_dhcp_config_class_init(NMDhcpConfigClass *config_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** - * NMDhcpConfig:options: (type GHashTable(utf8,utf8)): + * NMDhcpConfig:options: (type GHashTable(utf8,utf8)) * * The #GHashTable containing options of the configuration. **/ diff --git a/src/libnm-client-impl/nm-dhcp4-config.h b/src/libnm-client-impl/nm-dhcp4-config.h index 135720f0..ab400393 100644 --- a/src/libnm-client-impl/nm-dhcp4-config.h +++ b/src/libnm-client-impl/nm-dhcp4-config.h @@ -14,7 +14,7 @@ #define NM_TYPE_DHCP4_CONFIG (nm_dhcp4_config_get_type()) #define NM_DHCP4_CONFIG(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP4_CONFIG, NMDhcp4Config)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP4_CONFIG, NMDhcp4Config)) #define NM_DHCP4_CONFIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP4_CONFIG, NMDhcp4ConfigClass)) #define NM_IS_DHCP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP4_CONFIG)) diff --git a/src/libnm-client-impl/nm-dhcp6-config.h b/src/libnm-client-impl/nm-dhcp6-config.h index adaa5050..49334275 100644 --- a/src/libnm-client-impl/nm-dhcp6-config.h +++ b/src/libnm-client-impl/nm-dhcp6-config.h @@ -14,7 +14,7 @@ #define NM_TYPE_DHCP6_CONFIG (nm_dhcp6_config_get_type()) #define NM_DHCP6_CONFIG(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP6_CONFIG, NMDhcp6Config)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP6_CONFIG, NMDhcp6Config)) #define NM_DHCP6_CONFIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP6_CONFIG, NMDhcp6ConfigClass)) #define NM_IS_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP6_CONFIG)) diff --git a/src/libnm-client-impl/nm-dns-manager.c b/src/libnm-client-impl/nm-dns-manager.c index 1fdcdd9b..38514177 100644 --- a/src/libnm-client-impl/nm-dns-manager.c +++ b/src/libnm-client-impl/nm-dns-manager.c @@ -20,7 +20,7 @@ G_DEFINE_BOXED_TYPE(NMDnsEntry, nm_dns_entry, nm_dns_entry_dup, nm_dns_entry_unref) -struct NMDnsEntry { +struct _NMDnsEntry { guint refcount; char *interface; diff --git a/src/libnm-client-impl/nm-ip-config.c b/src/libnm-client-impl/nm-ip-config.c index 2307ce2c..9aa2e778 100644 --- a/src/libnm-client-impl/nm-ip-config.c +++ b/src/libnm-client-impl/nm-ip-config.c @@ -175,9 +175,7 @@ _notify_update_prop_nameservers(NMClient *client, if (!g_variant_is_of_type(val, G_VARIANT_TYPE_STRING)) goto next; - if (!nm_utils_parse_inaddr(AF_INET, - g_variant_get_string(val, NULL), - &val_str)) + if (!nm_inet_parse_str(AF_INET, g_variant_get_string(val, NULL), &val_str)) goto next; if (!arr) arr = g_ptr_array_new(); diff --git a/src/libnm-client-impl/nm-ip4-config.h b/src/libnm-client-impl/nm-ip4-config.h index db82a0a5..3c4b128e 100644 --- a/src/libnm-client-impl/nm-ip4-config.h +++ b/src/libnm-client-impl/nm-ip4-config.h @@ -14,7 +14,7 @@ #include "nm-ip-config.h" #define NM_TYPE_IP4_CONFIG (nm_ip4_config_get_type()) -#define NM_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IP4_CONFIG, NMIP4Config)) +#define NM_IP4_CONFIG(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IP4_CONFIG, NMIP4Config)) #define NM_IP4_CONFIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass)) #define NM_IS_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_IP4_CONFIG)) diff --git a/src/libnm-client-impl/nm-ip6-config.h b/src/libnm-client-impl/nm-ip6-config.h index 94386691..b44cd34b 100644 --- a/src/libnm-client-impl/nm-ip6-config.h +++ b/src/libnm-client-impl/nm-ip6-config.h @@ -14,7 +14,7 @@ #include "nm-ip-config.h" #define NM_TYPE_IP6_CONFIG (nm_ip6_config_get_type()) -#define NM_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IP6_CONFIG, NMIP6Config)) +#define NM_IP6_CONFIG(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_IP6_CONFIG, NMIP6Config)) #define NM_IP6_CONFIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass)) #define NM_IS_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_IP6_CONFIG)) diff --git a/src/libnm-client-impl/nm-libnm-utils.c b/src/libnm-client-impl/nm-libnm-utils.c index 6a7c155e..398c6ebc 100644 --- a/src/libnm-client-impl/nm-libnm-utils.c +++ b/src/libnm-client-impl/nm-libnm-utils.c @@ -16,7 +16,13 @@ /*****************************************************************************/ -volatile int _nml_dbus_log_level = 0; +#define LOG_FILE_FD_UNSET -3 +#define LOG_FILE_FD_NONE -2 +#define LOG_FILE_FD_DEFUNCT -1 + +volatile int _nml_dbus_log_level = 0; +const char *_nml_dbus_log_file = NULL; +int _nml_dbus_log_file_fd = LOG_FILE_FD_UNSET; int _nml_dbus_log_level_init(void) @@ -40,6 +46,97 @@ _nml_dbus_log_level_init(void) return l; } +static const char * +_nml_dbus_log_file_init(void) +{ + const char *s; + + s = g_getenv("LIBNM_CLIENT_DEBUG_FILE"); + if (nm_str_not_empty(s)) { + if (strstr(s, "%p")) { + gs_strfreev char **tokens = NULL; + char pid_str[100]; + + tokens = g_strsplit(s, "%p", -1); + nm_sprintf_buf(pid_str, "%lld", (long long) getpid()); + s = nm_str_realloc(g_strjoinv(pid_str, tokens)); + } else + s = g_strdup(s); + } else + s = ""; + + if (!g_atomic_pointer_compare_and_exchange(&_nml_dbus_log_file, NULL, s)) { + if (s[0] != '\0') + g_free((gpointer) s); + s = g_atomic_pointer_get(&_nml_dbus_log_file); + } + + return s; +} + +#define nml_dbus_log_file() \ + ({ \ + const char *_l; \ + \ + _l = g_atomic_pointer_get(&_nml_dbus_log_file); \ + if (G_UNLIKELY(!_l)) \ + _l = _nml_dbus_log_file_init(); \ + \ + _l[0] ? _l : NULL; \ + }) + +static int +_nml_dbus_log_file_fd_init(void) +{ + static GMutex mutex; + NM_G_MUTEX_LOCKED(&mutex); + int fd; + + fd = g_atomic_int_get(&_nml_dbus_log_file_fd); + if (fd == LOG_FILE_FD_UNSET) { + const char *name; + + name = nml_dbus_log_file(); + if (!name) + fd = LOG_FILE_FD_NONE; + else { + fd = open(name, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, 0600); + if (fd < 0) + fd = LOG_FILE_FD_DEFUNCT; + } + g_atomic_int_set(&_nml_dbus_log_file_fd, fd); + } + + return fd; +} + +#define nml_dbus_log_file_fd() \ + ({ \ + int _fd2; \ + \ + _fd2 = g_atomic_int_get(&_nml_dbus_log_file_fd); \ + if (G_UNLIKELY(_fd2 == LOG_FILE_FD_UNSET)) \ + _fd2 = _nml_dbus_log_file_fd_init(); \ + \ + nm_assert(NM_IN_SET(_fd2, LOG_FILE_FD_NONE, LOG_FILE_FD_DEFUNCT) || _fd2 >= 0); \ + _fd2; \ + }) + +#define _log_printf(use_stdout, ...) \ + G_STMT_START \ + { \ + const int _fd = nml_dbus_log_file_fd(); \ + \ + if (_fd != LOG_FILE_FD_NONE) { \ + if (_fd >= 0) \ + dprintf(_fd, __VA_ARGS__); \ + } else if (use_stdout) \ + g_print(__VA_ARGS__); \ + else \ + g_printerr(__VA_ARGS__); \ + } \ + G_STMT_END + void _nml_dbus_log(NMLDBusLogLevel level, gboolean use_stdout, const char *fmt, ...) { @@ -94,21 +191,13 @@ _nml_dbus_log(NMLDBusLogLevel level, gboolean use_stdout, const char *fmt, ...) pid = getpid(); - if (use_stdout) { - g_print("libnm-dbus[%lld]: %s[%" G_GINT64_FORMAT ".%05" G_GINT64_FORMAT "] %s\n", + _log_printf(use_stdout, + "libnm-dbus[%lld]: %s[%" G_GINT64_FORMAT ".%05" G_GINT64_FORMAT "] %s\n", (long long) pid, prefix, ts / NM_UTILS_NSEC_PER_SEC, (ts / (NM_UTILS_NSEC_PER_SEC / 100000)) % 100000, msg); - } else { - g_printerr("libnm-dbus[%lld]: %s[%" G_GINT64_FORMAT ".%05" G_GINT64_FORMAT "] %s\n", - (long long) pid, - prefix, - ts / NM_UTILS_NSEC_PER_SEC, - (ts / (NM_UTILS_NSEC_PER_SEC / 100000)) % 100000, - msg); - } } /*****************************************************************************/ @@ -693,6 +782,7 @@ const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[] = { &_nml_dbus_meta_iface_nm_device_generic, &_nml_dbus_meta_iface_nm_device_iptunnel, &_nml_dbus_meta_iface_nm_device_infiniband, + &_nml_dbus_meta_iface_nm_device_loopback, &_nml_dbus_meta_iface_nm_device_lowpan, &_nml_dbus_meta_iface_nm_device_macsec, &_nml_dbus_meta_iface_nm_device_macvlan, @@ -752,11 +842,11 @@ nml_dbus_meta_iface_get(const char *dbus_iface_name) if (NM_STR_HAS_PREFIX(dbus_iface_name, COMMON_PREFIX)) { /* optimize, that in fact all our interfaces have the same prefix. */ - idx = nm_utils_ptrarray_find_binary_search((gconstpointer *) _nml_dbus_meta_ifaces, - G_N_ELEMENTS(_nml_dbus_meta_ifaces), - &dbus_iface_name[NM_STRLEN(COMMON_PREFIX)], - _strcmp_common_prefix, - NULL); + idx = nm_ptrarray_find_bsearch((gconstpointer *) _nml_dbus_meta_ifaces, + G_N_ELEMENTS(_nml_dbus_meta_ifaces), + &dbus_iface_name[NM_STRLEN(COMMON_PREFIX)], + _strcmp_common_prefix, + NULL); } else return NULL; @@ -775,12 +865,12 @@ nml_dbus_meta_property_get(const NMLDBusMetaIface *meta_iface, nm_assert(meta_iface); nm_assert(dbus_property_name); - idx = nm_utils_array_find_binary_search(meta_iface->dbus_properties, - sizeof(meta_iface->dbus_properties[0]), - meta_iface->n_dbus_properties, - &dbus_property_name, - nm_strcmp_p_with_data, - NULL); + idx = nm_array_find_bsearch(meta_iface->dbus_properties, + meta_iface->n_dbus_properties, + sizeof(meta_iface->dbus_properties[0]), + &dbus_property_name, + nm_strcmp_p_with_data, + NULL); if (idx < 0) { NM_SET_OUT(out_idx, meta_iface->n_dbus_properties); return NULL; @@ -883,8 +973,10 @@ nm_utils_g_param_spec_is_default(const GParamSpec *pspec) /** * nm_utils_print: * @output_mode: if 1 it uses g_print(). If 2, it uses g_printerr(). - * If 0, it uses either g_print() or g_printerr(), depending - * on LIBNM_CLIENT_DEBUG (and the "stdout" flag). + * If 0, it uses the same output as internal libnm debug logging + * does. That is, depending on LIBNM_CLIENT_DEBUG's "stdout" flag + * it uses g_print() or g_printerr() and if LIBNM_CLIENT_DEBUG_FILE is + * set, it writes the output to file instead * @msg: the message to print. The function does not append * a trailing newline. * @@ -895,6 +987,11 @@ nm_utils_g_param_spec_is_default(const GParamSpec *pspec) * with these functions (it implements additional buffering). By * using nm_utils_print(), the same logging mechanisms can be used. * + * Also, libnm honors LIBNM_CLIENT_DEBUG_FILE environment. If this + * is set to a filename pattern (accepting "%p" for the process ID), + * then the debug log is written to that file instead. With @output_mode + * zero, the same location will be written. Since: 1.44. + * * Since: 1.30 */ void @@ -904,17 +1001,21 @@ nm_utils_print(int output_mode, const char *msg) g_return_if_fail(msg); - if (output_mode == 0) { + switch (output_mode) { + case 0: nml_dbus_log_enabled_full(NML_DBUS_LOG_LEVEL_ANY, &use_stdout); - output_mode = use_stdout ? 1 : 2; - } - - if (output_mode == 1) + _log_printf(use_stdout, "%s", msg); + break; + case 1: g_print("%s", msg); - else if (output_mode == 2) + break; + case 2: g_printerr("%s", msg); - else + break; + default: g_return_if_reached(); + break; + } } /*****************************************************************************/ diff --git a/src/libnm-client-impl/nm-libnm-utils.h b/src/libnm-client-impl/nm-libnm-utils.h index 9d9cfa25..a60706bd 100644 --- a/src/libnm-client-impl/nm-libnm-utils.h +++ b/src/libnm-client-impl/nm-libnm-utils.h @@ -227,7 +227,16 @@ typedef enum { /*****************************************************************************/ -#define NM_CLIENT_INSTANCE_FLAGS_ALL ((NMClientInstanceFlags) 0x1) +#define NM_CLIENT_INSTANCE_FLAGS_ALL \ + ((NMClientInstanceFlags) (NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS \ + | NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_GOOD \ + | NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_BAD)) + +#define NM_CLIENT_INSTANCE_FLAGS_ALL_WRITABLE \ + ((NMClientInstanceFlags) (NM_CLIENT_INSTANCE_FLAGS_ALL \ + & ~(( \ + NMClientInstanceFlags) (NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_GOOD \ + | NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_BAD)))) typedef struct { GType (*get_o_type_fcn)(void); @@ -556,7 +565,7 @@ struct _NMLDBusMetaIface { NML_DBUS_META_IFACE_OBJ_PROPERTIES(), \ ##__VA_ARGS__) -extern const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[44]; +extern const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[45]; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_accesspoint; @@ -572,6 +581,7 @@ extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_dummy; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_generic; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_infiniband; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_iptunnel; +extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_loopback; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_lowpan; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_macsec; extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_macvlan; diff --git a/src/libnm-client-impl/nm-object.c b/src/libnm-client-impl/nm-object.c index 60b01ca8..add498e1 100644 --- a/src/libnm-client-impl/nm-object.c +++ b/src/libnm-client-impl/nm-object.c @@ -43,10 +43,10 @@ _nm_object_get_private(NMObjectClass *klass, NMObject *self, guint16 extra_offse nm_assert(klass->priv_ptr_offset > 0); - ptr = (char *) self; + ptr = ((gpointer) self); ptr += klass->priv_ptr_offset; if (klass->priv_ptr_indirect) - ptr = *((gpointer *) ptr); + ptr = *NM_CAST_ALIGN(gpointer, ptr); return ptr + extra_offset; } diff --git a/src/libnm-client-impl/nm-property-infos-dbus.xml b/src/libnm-client-impl/nm-property-infos-dbus.xml deleted file mode 100644 index f875cf34..00000000 --- a/src/libnm-client-impl/nm-property-infos-dbus.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml b/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml deleted file mode 100644 index b0ad2d21..00000000 --- a/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/libnm-client-impl/nm-property-infos-keyfile.xml b/src/libnm-client-impl/nm-property-infos-keyfile.xml deleted file mode 100644 index c6021c1c..00000000 --- a/src/libnm-client-impl/nm-property-infos-keyfile.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/libnm-client-impl/nm-property-infos-nmcli.xml b/src/libnm-client-impl/nm-property-infos-nmcli.xml deleted file mode 100644 index 63d187d3..00000000 --- a/src/libnm-client-impl/nm-property-infos-nmcli.xml +++ /dev/null @@ -1,588 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -A list of IPv4 destination addresses, prefix length, optional IPv4 -next hop addresses, optional route metric, optional attribute. The valid syntax is: -"ip[/prefix] [next-hop] [metric] [attribute=val]...[,ip[/prefix]...]". -For example "192.0.2.0/24 10.1.1.1 77, 198.51.100.0/24". - - -Various attributes are supported: - - -"advmss" - an unsigned 32 bit integer. - - -"cwnd" - an unsigned 32 bit integer. - - -"initcwnd" - an unsigned 32 bit integer. - - -"initrwnd" - an unsigned 32 bit integer. - - -"lock-advmss" - a boolean value. - - -"lock-cwnd" - a boolean value. - - -"lock-initcwnd" - a boolean value. - - -"lock-initrwnd" - a boolean value. - - -"lock-mtu" - a boolean value. - - -"lock-window" - a boolean value. - - -"mtu" - an unsigned 32 bit integer. - - -"onlink" - a boolean value. - - -"quickack" - a boolean value. - - -"rto_min" - an unsigned 32 bit integer. -The value is in milliseconds. - - -"scope" - an unsigned 8 bit integer. IPv4 only. - - -"src" - an IPv4 address. - - -"table" - an unsigned 32 bit integer. The default depends on ipv4.route-table. - - -"tos" - an unsigned 8 bit integer. IPv4 only. - - -"type" - one of unicast, local, blackhole, -unavailable, prohibit, throw. -The default is unicast. - - -"window" - an unsigned 32 bit integer. - - - - -For details see also `man ip-route`. - - -A comma separated list of routing rules for policy routing. The format -is based on ip rule add syntax and mostly compatible. -One difference is that routing rules in NetworkManager always need a -fixed priority. - - -Example: priority 5 from 192.167.4.0/24 table 45 - - - -A list of IPv6 destination addresses, prefix length, optional IPv6 -next hop addresses, optional route metric, optional attribute. The valid syntax is: -"ip[/prefix] [next-hop] [metric] [attribute=val]...[,ip[/prefix]...]". - - -Various attributes are supported: - - -"advmss" - an unsigned 32 bit integer. - - -"cwnd" - an unsigned 32 bit integer. - - -"from" - an IPv6 address with optional prefix. IPv6 only. - - -"initcwnd" - an unsigned 32 bit integer. - - -"initrwnd" - an unsigned 32 bit integer. - - -"lock-advmss" - a boolean value. - - -"lock-cwnd" - a boolean value. - - -"lock-initcwnd" - a boolean value. - - -"lock-initrwnd" - a boolean value. - - -"lock-mtu" - a boolean value. - - -"lock-window" - a boolean value. - - -"mtu" - an unsigned 32 bit integer. - - -"onlink" - a boolean value. - - -"quickack" - a boolean value. - - -"rto_min" - an unsigned 32 bit integer. -The value is in milliseconds. - - -"src" - an IPv6 address. - - -"table" - an unsigned 32 bit integer. The default depends on ipv6.route-table. - - -"type" - one of unicast, local, blackhole, -unavailable, prohibit, throw. -The default is unicast. - - -"window" - an unsigned 32 bit integer. - - - - -For details see also `man ip-route`. - - -A comma separated list of routing rules for policy routing. The format -is based on ip rule add syntax and mostly compatible. -One difference is that routing rules in NetworkManager always need a -fixed priority. - - -Example: priority 5 from 1:2:3::5/128 table 45 - - - - - - - - - - - - - - - - - - -Array of TC queueing disciplines. qdisc is a basic block in the -Linux traffic control subsystem - - -Each qdisc can be specified by the following attributes: - - - -handle HANDLE - - -specifies the qdisc handle. A qdisc, which potentially can have children, gets -assigned a major number, called a 'handle', leaving the minor number namespace -available for classes. The handle is expressed as '10:'. It is customary to -explicitly assign a handle to qdiscs expected to have children. - - - - -parent HANDLE - - -specifies the handle of the parent qdisc the current qdisc must be -attached to. - - - - -root - - -specifies that the qdisc is attached to the root of device. - - - - -KIND - - -this is the qdisc kind. NetworkManager currently supports the -following kinds: fq_codel, sfq, tbf. Each qdisc kind has a -different set of parameters, described below. There are also some -kinds like pfifo, pfifo_fast, prio supported by NetworkManager -but their parameters are not supported by NetworkManager. - - - - - -Parameters for 'fq_codel': - - - -limit U32 - - -the hard limit on the real queue size. When this limit is -reached, incoming packets are dropped. Default is 10240 packets. - - - - -memory_limit U32 - - -sets a limit on the total number of bytes that can be queued in -this FQ-CoDel instance. The lower of the packet limit of the -limit parameter and the memory limit will be enforced. Default is -32 MB. - - - - -flows U32 - - -the number of flows into which the incoming packets are -classified. Due to the stochastic nature of hashing, multiple -flows may end up being hashed into the same slot. Newer flows -have priority over older ones. This parameter can be set only at -load time since memory has to be allocated for the hash table. -Default value is 1024. - - - - -target U32 - - -the acceptable minimum standing/persistent queue delay. This minimum -delay is identified by tracking the local minimum queue delay that packets -experience. The unit of measurement is microsecond(us). Default value is 5ms. - - - - -interval U32 - - -used to ensure that the measured minimum delay does not become too stale. -The minimum delay must be experienced in the last epoch of length .B -interval. It should be set on the order of the worst-case RTT -through the bottleneck to give endpoints sufficient time to -react. Default value is 100ms. - - - - -quantum U32 - - -the number of bytes used as 'deficit' in the fair queuing -algorithm. Default is set to 1514 bytes which corresponds to the -Ethernet MTU plus the hardware header length of 14 bytes. - - - - -ecn BOOL - - -can be used to mark packets instead of dropping them. ecn is turned -on by default. - - - - -ce_threshold U32 - - -sets a threshold above which all packets are marked with ECN -Congestion Experienced. This is useful for DCTCP-style congestion -control algorithms that require marking at very shallow queueing -thresholds. - - - - - -Parameters for 'sfq': - - - -divisor U32 - - -can be used to set a different hash table size, available -from kernel 2.6.39 onwards. The specified divisor must be -a power of two and cannot be larger than 65536. Default -value: 1024. - - - - -limit U32 - - -Upper limit of the SFQ. Can be used to reduce the default -length of 127 packets. - - - - -depth U32 - - -Limit of packets per flow. Default to -127 and can be lowered. - - - - -perturb_period U32 - - -Interval in seconds for queue algorithm perturbation. -Defaults to 0, which means that no perturbation occurs. Do -not set too low for each perturbation may cause some -packet reordering or losses. Advised value: 60 This value -has no effect when external flow classification is used. -Its better to increase divisor value to lower risk of hash -collisions. - - - - -quantum U32 - - -Amount of bytes a flow is allowed to dequeue during a -round of the round robin process. Defaults to the MTU of -the interface which is also the advised value and the -minimum value. - - - - -flows U32 - - -Default value is 127. - - - - - -Parameters for 'tbf': - - - -rate U64 - - -Bandwidth or rate. These parameters accept a floating -point number, possibly followed by either a unit (both SI -and IEC units supported), or a float followed by a percent -character to specify the rate as a percentage of the -device's speed. - - - - -burst U32 - - -Also known as buffer or maxburst. Size of the bucket, in -bytes. This is the maximum amount of bytes that tokens can -be available for instantaneously. In general, larger -shaping rates require a larger buffer. For 10mbit/s on -Intel, you need at least 10kbyte buffer if you want to -reach your configured rate! - - -If your buffer is too small, packets may be dropped -because more tokens arrive per timer tick than fit in your -bucket. The minimum buffer size can be calculated by -dividing the rate by HZ. - - -Token usage calculations are performed using a table which -by default has a resolution of 8 packets. This resolution -can be changed by specifying the cell size with the burst. -For example, to specify a 6000 byte buffer with a 16 byte -cell size, set a burst of 6000/16. You will probably never -have to set this. Must be an integral power of 2. - - - - -limit U32 - - -Limit is the number of bytes that can be queued waiting -for tokens to become available. - - - - -latency U32 - - -specifies the maximum amount of time a packet can -sit in the TBF. The latency calculation takes into account -the size of the bucket, the rate and possibly the peakrate -(if set). The latency and limit are mutually exclusive. - - - - - -Array of TC traffic filters. Traffic control can manage the packet content during -classification by using filters. - - -Each tfilters can be specified by the following attributes: - - - -handle HANDLE - - -specifies the tfilters handle. A filter is used by a classful qdisc to determine in which class -a packet will be enqueued. It is important to notice that filters reside within qdiscs. Therefore, -see qdiscs handle for detailed information. - - - - -parent HANDLE - - -specifies the handle of the parent qdisc the current qdisc must be -attached to. - - - - -root - - -specifies that the qdisc is attached to the root of device. - - - - -KIND - - -this is the tfilters kind. NetworkManager currently supports -following kinds: mirred, simple. Each filter kind has a -different set of actions, described below. There are also some -other kinds like matchall, basic, u32 supported by NetworkManager. - - - - - -Actions for 'mirred': - - - -egress bool - - -Define whether the packet should exit from the interface. - - - - -ingress bool - - -Define whether the packet should come into the interface. - - - - -mirror bool - - -Define whether the packet should be copied to the destination space. - - - - -redirect bool - - -Define whether the packet should be moved to the destination space. - - - - - -Action for 'simple': - - - -sdata char[32] - - -The actual string to print. - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/libnm-client-impl/nm-remote-connection.c b/src/libnm-client-impl/nm-remote-connection.c index b10d3d55..fe7e0712 100644 --- a/src/libnm-client-impl/nm-remote-connection.c +++ b/src/libnm-client-impl/nm-remote-connection.c @@ -698,7 +698,7 @@ static void register_client(NMObject *nmobj, NMClient *client, NMLDBusObject *dbobj) { NM_OBJECT_CLASS(nm_remote_connection_parent_class)->register_client(nmobj, client, dbobj); - nm_connection_set_path(NM_CONNECTION(nmobj), dbobj->dbus_path->str); + _nm_connection_set_path_rstr(NM_CONNECTION(nmobj), dbobj->dbus_path); _nm_client_get_settings_call(client, dbobj); } diff --git a/src/libnm-client-impl/nm-secret-agent-old.c b/src/libnm-client-impl/nm-secret-agent-old.c index 93475ed3..93b6048f 100644 --- a/src/libnm-client-impl/nm-secret-agent-old.c +++ b/src/libnm-client-impl/nm-secret-agent-old.c @@ -818,7 +818,7 @@ nm_secret_agent_old_register_async(NMSecretAgentOld *self, cancelled_id = g_cancellable_connect(cancellable, G_CALLBACK(_register_cancelled_cb), task, NULL); if (cancelled_id != 0) { - g_task_set_task_data(task, g_memdup(&cancelled_id, sizeof(cancelled_id)), g_free); + g_task_set_task_data(task, nm_memdup(&cancelled_id, sizeof(cancelled_id)), g_free); } } } @@ -964,7 +964,7 @@ nm_secret_agent_old_unregister_finish(NMSecretAgentOld *self, GAsyncResult *resu /*****************************************************************************/ /** - * nm_secret_agent_old_get_secrets: (virtual get_secrets): + * nm_secret_agent_old_get_secrets: (virtual get_secrets) * @self: a #NMSecretAgentOld * @connection: the #NMConnection for which we're asked secrets * @setting_name: the name of the secret setting @@ -1007,7 +1007,7 @@ nm_secret_agent_old_get_secrets(NMSecretAgentOld *self, } /** - * nm_secret_agent_old_save_secrets: (virtual save_secrets): + * nm_secret_agent_old_save_secrets: (virtual save_secrets) * @self: a #NMSecretAgentOld * @connection: a #NMConnection * @callback: (scope async): a callback, to be invoked when the operation is done @@ -1034,7 +1034,7 @@ nm_secret_agent_old_save_secrets(NMSecretAgentOld *self, } /** - * nm_secret_agent_old_delete_secrets: (virtual delete_secrets): + * nm_secret_agent_old_delete_secrets: (virtual delete_secrets) * @self: a #NMSecretAgentOld * @connection: a #NMConnection * @callback: (scope async): a callback, to be invoked when the operation is done diff --git a/src/libnm-client-impl/nm-settings-docs-gir.xml b/src/libnm-client-impl/nm-settings-docs-gir.xml deleted file mode 100644 index 78dc852c..00000000 --- a/src/libnm-client-impl/nm-settings-docs-gir.xml +++ /dev/null @@ -1,404 +0,0 @@ - - The number of retries for the authentication. Zero means to try indefinitely; -1 means to use a global default. If the global default is not set, the authentication retries for 3 times before failing the connection. - - Currently, this only applies to 802-1x authentication. The number of retries for the authentication. Zero means to try indefinitely; -1 means to use a global default. If the global default is not set, the authentication retries for 3 times before failing the connection. Currently, this only applies to 802-1x authentication. Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. - - Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. - - Note that autoconnect is not implemented for VPN profiles. See "secondaries" as an alternative to automatically connect VPN profiles. - - If multiple profiles are ready to autoconnect on the same device, the one with the better "connection.autoconnect-priority" is chosen. If the priorities are equal, then the most recently connected profile is activated. If the profiles were not connected earlier or their "connection.timestamp" is identical, the choice is undefined. - - Depending on "connection.multi-connect", a profile can (auto)connect only once at a time or multiple times. Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See "secondaries" as an alternative to automatically connect VPN profiles. If multiple profiles are ready to autoconnect on the same device, the one with the better "connection.autoconnect-priority" is chosen. If the priorities are equal, then the most recently connected profile is activated. If the profiles were not connected earlier or their "connection.timestamp" is identical, the choice is undefined. Depending on "connection.multi-connect", a profile can (auto)connect only once at a time or multiple times. The autoconnect priority in range -999 to 999. If the connection is set to autoconnect, connections with higher priority will be preferred. The higher number means higher priority. Defaults to 0. Note that this property only matters if there are more than one candidate profile to select for autoconnect. In case of equal priority, the profile used most recently is chosen. The autoconnect priority in range -999 to 999. If the connection is set to autoconnect, connections with higher priority will be preferred. The higher number means higher priority. Defaults to 0. Note that this property only matters if there are more than one candidate profile to select for autoconnect. In case of equal priority, the profile used most recently is chosen. The number of times a connection should be tried when autoactivating before giving up. Zero means forever, -1 means the global default (4 times if not overridden). Setting this to 1 means to try activation only once before blocking autoconnect. Note that after a timeout, NetworkManager will try to autoconnect again. The number of times a connection should be tried when autoactivating before giving up. Zero means forever, -1 means the global default (4 times if not overridden). Setting this to 1 means to try activation only once before blocking autoconnect. Note that after a timeout, NetworkManager will try to autoconnect again. Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties "autoconnect", "autoconnect-priority" and "autoconnect-retries" are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0. Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties "autoconnect", "autoconnect-priority" and "autoconnect-retries" are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0. Whether DNSOverTls (dns-over-tls) is enabled for the connection. DNSOverTls is a technology which uses TLS to encrypt dns traffic. - - The permitted values are: "yes" (2) use DNSOverTls and disabled fallback, "opportunistic" (1) use DNSOverTls but allow fallback to unencrypted resolution, "no" (0) don't ever use DNSOverTls. If unspecified "default" depends on the plugin used. Systemd-resolved uses global setting. - - This feature requires a plugin which supports DNSOverTls. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved. Whether DNSOverTls (dns-over-tls) is enabled for the connection. DNSOverTls is a technology which uses TLS to encrypt dns traffic. The permitted values are: "yes" (2) use DNSOverTls and disabled fallback, "opportunistic" (1) use DNSOverTls but allow fallback to unencrypted resolution, "no" (0) don't ever use DNSOverTls. If unspecified "default" depends on the plugin used. Systemd-resolved uses global setting. This feature requires a plugin which supports DNSOverTls. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved. If greater than zero, delay success of IP addressing until either the timeout is reached, or an IP gateway replies to a ping. If greater than zero, delay success of IP addressing until either the timeout is reached, or an IP gateway replies to a ping. A human readable unique identifier for the connection, like "Work Wi-Fi" or "T-Mobile 3G". A human readable unique identifier for the connection, like "Work Wi-Fi" or "T-Mobile 3G". The name of the network interface this connection is bound to. If not set, then the connection can be attached to any interface of the appropriate type (subject to restrictions imposed by other settings). - - For software devices this specifies the name of the created device. - - For connection types where interface names cannot easily be made persistent (e.g. mobile broadband or USB Ethernet), this property should not be used. Setting this property restricts the interfaces a connection can be used with, and if interface names change or are reordered the connection may be applied to the wrong interface. The name of the network interface this connection is bound to. If not set, then the connection can be attached to any interface of the appropriate type (subject to restrictions imposed by other settings). For software devices this specifies the name of the created device. For connection types where interface names cannot easily be made persistent (e.g. mobile broadband or USB Ethernet), this property should not be used. Setting this property restricts the interfaces a connection can be used with, and if interface names change or are reordered the connection may be applied to the wrong interface. Whether LLDP is enabled for the connection. Whether LLDP is enabled for the connection. Whether Link-Local Multicast Name Resolution (LLMNR) is enabled for the connection. LLMNR is a protocol based on the Domain Name System (DNS) packet format that allows both IPv4 and IPv6 hosts to perform name resolution for hosts on the same local link. - - The permitted values are: "yes" (2) register hostname and resolving for the connection, "no" (0) disable LLMNR for the interface, "resolve" (1) do not register hostname but allow resolving of LLMNR host names If unspecified, "default" ultimately depends on the DNS plugin (which for systemd-resolved currently means "yes"). - - This feature requires a plugin which supports LLMNR. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved. Whether Link-Local Multicast Name Resolution (LLMNR) is enabled for the connection. LLMNR is a protocol based on the Domain Name System (DNS) packet format that allows both IPv4 and IPv6 hosts to perform name resolution for hosts on the same local link. The permitted values are: "yes" (2) register hostname and resolving for the connection, "no" (0) disable LLMNR for the interface, "resolve" (1) do not register hostname but allow resolving of LLMNR host names If unspecified, "default" ultimately depends on the DNS plugin (which for systemd-resolved currently means "yes"). This feature requires a plugin which supports LLMNR. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved. Interface name of the master device or UUID of the master connection. Interface name of the master device or UUID of the master connection. Whether mDNS is enabled for the connection. - - The permitted values are: "yes" (2) register hostname and resolving for the connection, "no" (0) disable mDNS for the interface, "resolve" (1) do not register hostname but allow resolving of mDNS host names and "default" (-1) to allow lookup of a global default in NetworkManager.conf. If unspecified, "default" ultimately depends on the DNS plugin (which for systemd-resolved currently means "no"). - - This feature requires a plugin which supports mDNS. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved. Whether mDNS is enabled for the connection. The permitted values are: "yes" (2) register hostname and resolving for the connection, "no" (0) disable mDNS for the interface, "resolve" (1) do not register hostname but allow resolving of mDNS host names and "default" (-1) to allow lookup of a global default in NetworkManager.conf. If unspecified, "default" ultimately depends on the DNS plugin (which for systemd-resolved currently means "no"). This feature requires a plugin which supports mDNS. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved. Whether the connection is metered. - - When updating this property on a currently activated connection, the change takes effect immediately. Whether the connection is metered. When updating this property on a currently activated connection, the change takes effect immediately. Whether to configure MPTCP endpoints and the address flags. If MPTCP is enabled in NetworkManager, it will configure the addresses of the interface as MPTCP endpoints. Note that IPv4 loopback addresses (127.0.0.0/8), IPv4 link local addresses (169.254.0.0/16), the IPv6 loopback address (::1), IPv6 link local addresses (fe80::/10), IPv6 unique local addresses (ULA, fc00::/7) and IPv6 privacy extension addresses (rfc3041, ipv6.ip6-privacy) will be excluded from being configured as endpoints. - - If "disabled" (0x1), MPTCP handling for the interface is disabled and no endpoints are registered. - - The "enabled" (0x2) flag means that MPTCP handling is enabled. This flag can also be implied from the presence of other flags. - - Even when enabled, MPTCP handling will by default still be disabled unless "/proc/sys/net/mptcp/enabled" sysctl is on. NetworkManager does not change the sysctl and this is up to the administrator or distribution. To configure endpoints even if the sysctl is disabled, "also-without-sysctl" (0x4) flag can be used. In that case, NetworkManager doesn't look at the sysctl and configures endpoints regardless. - - Even when enabled, NetworkManager will only configure MPTCP endpoints for a certain address family, if there is a unicast default route (0.0.0.0/0 or ::/0) in the main routing table. The flag "also-without-default-route" (0x8) can override that. - - When MPTCP handling is enabled then endpoints are configured with the specified address flags "signal" (0x10), "subflow" (0x20), "backup" (0x40), "fullmesh" (0x80). See ip-mptcp(8) manual for additional information about the flags. - - If the flags are zero (0x0), the global connection default from NetworkManager.conf is honored. If still unspecified, the fallback is "enabled,subflow". Note that this means that MPTCP is by default done depending on the "/proc/sys/net/mptcp/enabled" sysctl. - - NetworkManager does not change the MPTCP limits nor enable MPTCP via "/proc/sys/net/mptcp/enabled". That is a host configuration which the admin can change via sysctl and ip-mptcp. - - Strict reverse path filtering (rp_filter) breaks many MPTCP use cases, so when MPTCP handling for IPv4 addresses on the interface is enabled, NetworkManager would loosen the strict reverse path filtering (1) to the loose setting (2). Whether to configure MPTCP endpoints and the address flags. If MPTCP is enabled in NetworkManager, it will configure the addresses of the interface as MPTCP endpoints. Note that IPv4 loopback addresses (127.0.0.0/8), IPv4 link local addresses (169.254.0.0/16), the IPv6 loopback address (::1), IPv6 link local addresses (fe80::/10), IPv6 unique local addresses (ULA, fc00::/7) and IPv6 privacy extension addresses (rfc3041, ipv6.ip6-privacy) will be excluded from being configured as endpoints. If "disabled" (0x1), MPTCP handling for the interface is disabled and no endpoints are registered. The "enabled" (0x2) flag means that MPTCP handling is enabled. This flag can also be implied from the presence of other flags. Even when enabled, MPTCP handling will by default still be disabled unless "/proc/sys/net/mptcp/enabled" sysctl is on. NetworkManager does not change the sysctl and this is up to the administrator or distribution. To configure endpoints even if the sysctl is disabled, "also-without-sysctl" (0x4) flag can be used. In that case, NetworkManager doesn't look at the sysctl and configures endpoints regardless. Even when enabled, NetworkManager will only configure MPTCP endpoints for a certain address family, if there is a unicast default route (0.0.0.0/0 or ::/0) in the main routing table. The flag "also-without-default-route" (0x8) can override that. When MPTCP handling is enabled then endpoints are configured with the specified address flags "signal" (0x10), "subflow" (0x20), "backup" (0x40), "fullmesh" (0x80). See ip-mptcp(8) manual for additional information about the flags. If the flags are zero (0x0), the global connection default from NetworkManager.conf is honored. If still unspecified, the fallback is "enabled,subflow". Note that this means that MPTCP is by default done depending on the "/proc/sys/net/mptcp/enabled" sysctl. NetworkManager does not change the MPTCP limits nor enable MPTCP via "/proc/sys/net/mptcp/enabled". That is a host configuration which the admin can change via sysctl and ip-mptcp. Strict reverse path filtering (rp_filter) breaks many MPTCP use cases, so when MPTCP handling for IPv4 addresses on the interface is enabled, NetworkManager would loosen the strict reverse path filtering (1) to the loose setting (2). If configured, set to a Manufacturer Usage Description (MUD) URL that points to manufacturer-recommended network policies for IoT devices. It is transmitted as a DHCPv4 or DHCPv6 option. The value must be a valid URL starting with "https://". - - The special value "none" is allowed to indicate that no MUD URL is used. - - If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the ultimate default is "none". If configured, set to a Manufacturer Usage Description (MUD) URL that points to manufacturer-recommended network policies for IoT devices. It is transmitted as a DHCPv4 or DHCPv6 option. The value must be a valid URL starting with "https://". The special value "none" is allowed to indicate that no MUD URL is used. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the ultimate default is "none". Specifies whether the profile can be active multiple times at a particular moment. The value is of type NMConnectionMultiConnect. Specifies whether the profile can be active multiple times at a particular moment. The value is of type NMConnectionMultiConnect. An array of strings defining what access a given user has to this connection. If this is NULL or empty, all users are allowed to access this connection; otherwise users are allowed if and only if they are in this list. When this is not empty, the connection can be active only when one of the specified users is logged into an active session. Each entry is of the form "[type]:[id]:[reserved]"; for example, "user:dcbw:blah". - - At this time only the "user" [type] is allowed. Any other values are ignored and reserved for future use. [id] is the username that this permission refers to, which may not contain the ":" character. Any [reserved] information present must be ignored and is reserved for future use. All of [type], [id], and [reserved] must be valid UTF-8. An array of strings defining what access a given user has to this connection. If this is NULL or empty, all users are allowed to access this connection; otherwise users are allowed if and only if they are in this list. When this is not empty, the connection can be active only when one of the specified users is logged into an active session. Each entry is of the form "[type]:[id]:[reserved]"; for example, "user:dcbw:blah". At this time only the "user" [type] is allowed. Any other values are ignored and reserved for future use. [id] is the username that this permission refers to, which may not contain the ":" character. Any [reserved] information present must be ignored and is reserved for future use. All of [type], [id], and [reserved] must be valid UTF-8. FALSE if the connection can be modified using the provided settings service's D-Bus interface with the right privileges, or TRUE if the connection is read-only and cannot be modified. FALSE if the connection can be modified using the provided settings service's D-Bus interface with the right privileges, or TRUE if the connection is read-only and cannot be modified. List of connection UUIDs that should be activated when the base connection itself is activated. Currently, only VPN connections are supported. List of connection UUIDs that should be activated when the base connection itself is activated. Currently, only VPN connections are supported. Setting name of the device type of this slave's master connection (eg, "bond"), or NULL if this connection is not a slave. Setting name of the device type of this slave's master connection (eg, "bond"), or NULL if this connection is not a slave. This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. - - The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. - - Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual about the secret-key and the host identity. - - The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are "${CONNECTION}", "${DEVICE}", "${MAC}", "${BOOT}", "${RANDOM}". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that "${DEVICE}" corresponds to the interface name of the device and "${MAC}" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as "$$". For example, set it to "${CONNECTION}-${BOOT}-${DEVICE}" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. - - If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to "${CONNECTION}" and uses a unique, fixed ID for the connection. This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual about the secret-key and the host identity. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are "${CONNECTION}", "${DEVICE}", "${MAC}", "${BOOT}", "${RANDOM}". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that "${DEVICE}" corresponds to the interface name of the device and "${MAC}" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as "$$". For example, set it to "${CONNECTION}-${BOOT}-${DEVICE}" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to "${CONNECTION}" and uses a unique, fixed ID for the connection. The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. - - NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved). The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved). Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, "802-3-ethernet" or "802-11-wireless" or "bluetooth", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, "vpn" or "bridge", etc). Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, "802-3-ethernet" or "802-11-wireless" or "bluetooth", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, "vpn" or "bridge", etc). A universally unique identifier for the connection, for example generated with libuuid. It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network. For example, it should not be changed when the "id" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or "type" property changes. - - The UUID must be in the format "2815492f-7e56-435e-b2e9-246bd7cdc664" (ie, contains only hexadecimal characters and "-"). A universally unique identifier for the connection, for example generated with libuuid. It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network. For example, it should not be changed when the "id" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or "type" property changes. The UUID must be in the format "2815492f-7e56-435e-b2e9-246bd7cdc664" (ie, contains only hexadecimal characters and "-"). Time in milliseconds to wait for connection to be considered activated. The wait will start after the pre-up dispatcher event. - - The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time. Time in milliseconds to wait for connection to be considered activated. The wait will start after the pre-up dispatcher event. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time. Timeout in milliseconds to wait for device at startup. During boot, devices may take a while to be detected by the driver. This property will cause to delay NetworkManager-wait-online.service and nm-online to give the device a chance to appear. This works by waiting for the given timeout until a compatible device for the profile is available and managed. - - The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time. Timeout in milliseconds to wait for device at startup. During boot, devices may take a while to be detected by the driver. This property will cause to delay NetworkManager-wait-online.service and nm-online to give the device a chance to appear. This works by waiting for the given timeout until a compatible device for the profile is available and managed. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time. The trust level of a the connection. Free form case-insensitive string (for example "Home", "Work", "Public"). NULL or unspecified zone means the connection will be placed in the default zone as defined by the firewall. - - When updating this property on a currently activated connection, the change takes effect immediately. The trust level of a the connection. Free form case-insensitive string (for example "Home", "Work", "Public"). NULL or unspecified zone means the connection will be placed in the default zone as defined by the firewall. When updating this property on a currently activated connection, the change takes effect immediately. If given, specifies the parent interface name or parent connection UUID from which this 6LowPAN interface should be created. If given, specifies the parent interface name or parent connection UUID from which this 6LowPAN interface should be created. List of strings to be matched against the altSubjectName of the certificate presented by the authentication server. If the list is empty, no verification of the server certificate's altSubjectName is performed. List of strings to be matched against the altSubjectName of the certificate presented by the authentication server. If the list is empty, no verification of the server certificate's altSubjectName is performed. Anonymous identity string for EAP authentication methods. Used as the unencrypted identity with EAP types that support different tunneled identity like EAP-TTLS. Anonymous identity string for EAP authentication methods. Used as the unencrypted identity with EAP types that support different tunneled identity like EAP-TTLS. A timeout for the authentication. Zero means the global default; if the global default is not set, the authentication timeout is 25 seconds. A timeout for the authentication. Zero means the global default; if the global default is not set, the authentication timeout is 25 seconds. Contains the CA certificate if used by the EAP method specified in the "eap" property. - - Certificate data is specified using a "scheme"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. - - Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory. Contains the CA certificate if used by the EAP method specified in the "eap" property. Certificate data is specified using a "scheme"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory. The password used to access the CA certificate stored in "ca-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. The password used to access the CA certificate stored in "ca-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. Flags indicating how to handle the "ca-cert-password" property. Flags indicating how to handle the "ca-cert-password" property. UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the "ca-cert" property. - - If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored. UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the "ca-cert" property. If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored. Contains the client certificate if used by the EAP method specified in the "eap" property. - - Certificate data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. Contains the client certificate if used by the EAP method specified in the "eap" property. Certificate data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. The password used to access the client certificate stored in "client-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. The password used to access the client certificate stored in "client-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. Flags indicating how to handle the "client-cert-password" property. Flags indicating how to handle the "client-cert-password" property. Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a ";" delimited list. Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a ";" delimited list. Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a ";" delimited list. Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a ";" delimited list. The allowed EAP method to be used when authenticating to the network with 802.1x. Valid methods are: "leap", "md5", "tls", "peap", "ttls", "pwd", and "fast". Each method requires different configuration using the properties of this setting; refer to wpa_supplicant documentation for the allowed combinations. The allowed EAP method to be used when authenticating to the network with 802.1x. Valid methods are: "leap", "md5", "tls", "peap", "ttls", "pwd", and "fast". Each method requires different configuration using the properties of this setting; refer to wpa_supplicant documentation for the allowed combinations. Identity string for EAP authentication methods. Often the user's user or login name. Identity string for EAP authentication methods. Often the user's user or login name. Whether the 802.1X authentication is optional. If TRUE, the activation will continue even after a timeout or an authentication failure. Setting the property to TRUE is currently allowed only for Ethernet connections. If set to FALSE, the activation can continue only after a successful authentication. Whether the 802.1X authentication is optional. If TRUE, the activation will continue even after a timeout or an authentication failure. Setting the property to TRUE is currently allowed only for Ethernet connections. If set to FALSE, the activation can continue only after a successful authentication. UTF-8 encoded file path containing PAC for EAP-FAST. UTF-8 encoded file path containing PAC for EAP-FAST. UTF-8 encoded password used for EAP authentication methods. If both the "password" property and the "password-raw" property are specified, "password" is preferred. UTF-8 encoded password used for EAP authentication methods. If both the "password" property and the "password-raw" property are specified, "password" is preferred. Flags indicating how to handle the "password" property. Flags indicating how to handle the "password" property. Password used for EAP authentication methods, given as a byte array to allow passwords in other encodings than UTF-8 to be used. If both the "password" property and the "password-raw" property are specified, "password" is preferred. Password used for EAP authentication methods, given as a byte array to allow passwords in other encodings than UTF-8 to be used. If both the "password" property and the "password-raw" property are specified, "password" is preferred. Flags indicating how to handle the "password-raw" property. Flags indicating how to handle the "password-raw" property. Specifies authentication flags to use in "phase 1" outer authentication using NMSetting8021xAuthFlags options. The individual TLS versions can be explicitly disabled. If a certain TLS disable flag is not set, it is up to the supplicant to allow or forbid it. The TLS options map to tls_disable_tlsv1_x settings. See the wpa_supplicant documentation for more details. Specifies authentication flags to use in "phase 1" outer authentication using NMSetting8021xAuthFlags options. The individual TLS versions can be explicitly disabled. If a certain TLS disable flag is not set, it is up to the supplicant to allow or forbid it. The TLS options map to tls_disable_tlsv1_x settings. See the wpa_supplicant documentation for more details. Enables or disables in-line provisioning of EAP-FAST credentials when FAST is specified as the EAP method in the "eap" property. Recognized values are "0" (disabled), "1" (allow unauthenticated provisioning), "2" (allow authenticated provisioning), and "3" (allow both authenticated and unauthenticated provisioning). See the wpa_supplicant documentation for more details. Enables or disables in-line provisioning of EAP-FAST credentials when FAST is specified as the EAP method in the "eap" property. Recognized values are "0" (disabled), "1" (allow unauthenticated provisioning), "2" (allow authenticated provisioning), and "3" (allow both authenticated and unauthenticated provisioning). See the wpa_supplicant documentation for more details. Forces use of the new PEAP label during key derivation. Some RADIUS servers may require forcing the new PEAP label to interoperate with PEAPv1. Set to "1" to force use of the new PEAP label. See the wpa_supplicant documentation for more details. Forces use of the new PEAP label during key derivation. Some RADIUS servers may require forcing the new PEAP label to interoperate with PEAPv1. Set to "1" to force use of the new PEAP label. See the wpa_supplicant documentation for more details. Forces which PEAP version is used when PEAP is set as the EAP method in the "eap" property. When unset, the version reported by the server will be used. Sometimes when using older RADIUS servers, it is necessary to force the client to use a particular PEAP version. To do so, this property may be set to "0" or "1" to force that specific PEAP version. Forces which PEAP version is used when PEAP is set as the EAP method in the "eap" property. When unset, the version reported by the server will be used. Sometimes when using older RADIUS servers, it is necessary to force the client to use a particular PEAP version. To do so, this property may be set to "0" or "1" to force that specific PEAP version. List of strings to be matched against the altSubjectName of the certificate presented by the authentication server during the inner "phase 2" authentication. If the list is empty, no verification of the server certificate's altSubjectName is performed. List of strings to be matched against the altSubjectName of the certificate presented by the authentication server during the inner "phase 2" authentication. If the list is empty, no verification of the server certificate's altSubjectName is performed. Specifies the allowed "phase 2" inner authentication method when an EAP method that uses an inner TLS tunnel is specified in the "eap" property. For TTLS this property selects one of the supported non-EAP inner methods: "pap", "chap", "mschap", "mschapv2" while "phase2-autheap" selects an EAP inner method. For PEAP this selects an inner EAP method, one of: "gtc", "otp", "md5" and "tls". Each "phase 2" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details. Both "phase2-auth" and "phase2-autheap" cannot be specified. Specifies the allowed "phase 2" inner authentication method when an EAP method that uses an inner TLS tunnel is specified in the "eap" property. For TTLS this property selects one of the supported non-EAP inner methods: "pap", "chap", "mschap", "mschapv2" while "phase2-autheap" selects an EAP inner method. For PEAP this selects an inner EAP method, one of: "gtc", "otp", "md5" and "tls". Each "phase 2" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details. Both "phase2-auth" and "phase2-autheap" cannot be specified. Specifies the allowed "phase 2" inner EAP-based authentication method when TTLS is specified in the "eap" property. Recognized EAP-based "phase 2" methods are "md5", "mschapv2", "otp", "gtc", and "tls". Each "phase 2" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details. Specifies the allowed "phase 2" inner EAP-based authentication method when TTLS is specified in the "eap" property. Recognized EAP-based "phase 2" methods are "md5", "mschapv2", "otp", "gtc", and "tls". Each "phase 2" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details. Contains the "phase 2" CA certificate if used by the EAP method specified in the "phase2-auth" or "phase2-autheap" properties. - - Certificate data is specified using a "scheme"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. - - Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory. Contains the "phase 2" CA certificate if used by the EAP method specified in the "phase2-auth" or "phase2-autheap" properties. Certificate data is specified using a "scheme"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory. The password used to access the "phase2" CA certificate stored in "phase2-ca-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. The password used to access the "phase2" CA certificate stored in "phase2-ca-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. Flags indicating how to handle the "phase2-ca-cert-password" property. Flags indicating how to handle the "phase2-ca-cert-password" property. UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the "phase2-ca-cert" property. - - If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored. UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the "phase2-ca-cert" property. If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored. Contains the "phase 2" client certificate if used by the EAP method specified in the "phase2-auth" or "phase2-autheap" properties. - - Certificate data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Contains the "phase 2" client certificate if used by the EAP method specified in the "phase2-auth" or "phase2-autheap" properties. Certificate data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. The password used to access the "phase2" client certificate stored in "phase2-client-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. The password used to access the "phase2" client certificate stored in "phase2-client-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. Flags indicating how to handle the "phase2-client-cert-password" property. Flags indicating how to handle the "phase2-client-cert-password" property. Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner "phase 2" authentication. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a ";" delimited list. Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner "phase 2" authentication. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a ";" delimited list. Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner "phase 2" authentication. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a ";" delimited list. Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner "phase 2" authentication. If a matching dNSName is found, this constraint is met. If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a ";" delimited list. Contains the "phase 2" inner private key when the "phase2-auth" or "phase2-autheap" property is set to "tls". - - Key data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the "phase2-private-key-password" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte, and as with the blob scheme the "phase2-private-key-password" property must be set to the password used to decode the PKCS#12 private key and certificate. Contains the "phase 2" inner private key when the "phase2-auth" or "phase2-autheap" property is set to "tls". Key data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the "phase2-private-key-password" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte, and as with the blob scheme the "phase2-private-key-password" property must be set to the password used to decode the PKCS#12 private key and certificate. The password used to decrypt the "phase 2" private key specified in the "phase2-private-key" property when the private key either uses the path scheme, or is a PKCS#12 format key. The password used to decrypt the "phase 2" private key specified in the "phase2-private-key" property when the private key either uses the path scheme, or is a PKCS#12 format key. Flags indicating how to handle the "phase2-private-key-password" property. Flags indicating how to handle the "phase2-private-key-password" property. Substring to be matched against the subject of the certificate presented by the authentication server during the inner "phase 2" authentication. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:phase2-domain-suffix-match. Substring to be matched against the subject of the certificate presented by the authentication server during the inner "phase 2" authentication. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:phase2-domain-suffix-match. PIN used for EAP authentication methods. PIN used for EAP authentication methods. Flags indicating how to handle the "pin" property. Flags indicating how to handle the "pin" property. Contains the private key when the "eap" property is set to "tls". - - Key data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the "private-key-password" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte, and as with the blob scheme the "private-key-password" property must be set to the password used to decode the PKCS#12 private key and certificate. - - WARNING: "private-key" is not a "secret" property, and thus unencrypted private key data using the BLOB scheme may be readable by unprivileged users. Private keys should always be encrypted with a private key password to prevent unauthorized access to unencrypted private key data. Contains the private key when the "eap" property is set to "tls". Key data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the "private-key-password" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte, and as with the blob scheme the "private-key-password" property must be set to the password used to decode the PKCS#12 private key and certificate. WARNING: "private-key" is not a "secret" property, and thus unencrypted private key data using the BLOB scheme may be readable by unprivileged users. Private keys should always be encrypted with a private key password to prevent unauthorized access to unencrypted private key data. The password used to decrypt the private key specified in the "private-key" property when the private key either uses the path scheme, or if the private key is a PKCS#12 format key. The password used to decrypt the private key specified in the "private-key" property when the private key either uses the path scheme, or if the private key is a PKCS#12 format key. Flags indicating how to handle the "private-key-password" property. Flags indicating how to handle the "private-key-password" property. Substring to be matched against the subject of the certificate presented by the authentication server. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:domain-suffix-match. Substring to be matched against the subject of the certificate presented by the authentication server. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:domain-suffix-match. When TRUE, overrides the "ca-path" and "phase2-ca-path" properties using the system CA directory specified at configure time with the --system-ca-path switch. The certificates in this directory are added to the verification chain in addition to any certificates specified by the "ca-cert" and "phase2-ca-cert" properties. If the path provided with --system-ca-path is rather a file name (bundle of trusted CA certificates), it overrides "ca-cert" and "phase2-ca-cert" properties instead (sets ca_cert/ca_cert2 options for wpa_supplicant). When TRUE, overrides the "ca-path" and "phase2-ca-path" properties using the system CA directory specified at configure time with the --system-ca-path switch. The certificates in this directory are added to the verification chain in addition to any certificates specified by the "ca-cert" and "phase2-ca-cert" properties. If the path provided with --system-ca-path is rather a file name (bundle of trusted CA certificates), it overrides "ca-cert" and "phase2-ca-cert" properties instead (sets ca_cert/ca_cert2 options for wpa_supplicant). Encapsulation of ADSL connection. Can be "vcmux" or "llc". Encapsulation of ADSL connection. Can be "vcmux" or "llc". Password used to authenticate with the ADSL service. Password used to authenticate with the ADSL service. Flags indicating how to handle the "password" property. Flags indicating how to handle the "password" property. ADSL connection protocol. Can be "pppoa", "pppoe" or "ipoatm". ADSL connection protocol. Can be "pppoa", "pppoe" or "ipoatm". Username used to authenticate with the ADSL service. Username used to authenticate with the ADSL service. VCI of ADSL connection VCI of ADSL connection VPI of ADSL connection VPI of ADSL connection The Bluetooth address of the device. The Bluetooth address of the device. Either "dun" for Dial-Up Networking connections or "panu" for Personal Area Networking connections to devices supporting the NAP profile. Either "dun" for Dial-Up Networking connections or "panu" for Personal Area Networking connections to devices supporting the NAP profile. Dictionary of key/value pairs of bonding options. Both keys and values must be strings. Option names must contain only alphanumeric characters (ie, [a-zA-Z0-9]). Dictionary of key/value pairs of bonding options. Both keys and values must be strings. Option names must contain only alphanumeric characters (ie, [a-zA-Z0-9]). The queue ID of this bond port. The maximum value of queue ID is the number of TX queues currently active in device. The queue ID of this bond port. The maximum value of queue ID is the number of TX queues currently active in device. The Ethernet MAC address aging time, in seconds. The Ethernet MAC address aging time, in seconds. The Spanning Tree Protocol (STP) forwarding delay, in seconds. The Spanning Tree Protocol (STP) forwarding delay, in seconds. If specified, The MAC address of the multicast group this bridge uses for STP. - - The address must be a link-local address in standard Ethernet MAC address format, ie an address of the form 01:80:C2:00:00:0X, with X in [0, 4..F]. If not specified the default value is 01:80:C2:00:00:00. If specified, The MAC address of the multicast group this bridge uses for STP. The address must be a link-local address in standard Ethernet MAC address format, ie an address of the form 01:80:C2:00:00:0X, with X in [0, 4..F]. If not specified the default value is 01:80:C2:00:00:00. A mask of group addresses to forward. Usually, group addresses in the range from 01:80:C2:00:00:00 to 01:80:C2:00:00:0F are not forwarded according to standards. This property is a mask of 16 bits, each corresponding to a group address in that range that must be forwarded. The mask can't have bits 0, 1 or 2 set because they are used for STP, MAC pause frames and LACP. A mask of group addresses to forward. Usually, group addresses in the range from 01:80:C2:00:00:00 to 01:80:C2:00:00:0F are not forwarded according to standards. This property is a mask of 16 bits, each corresponding to a group address in that range that must be forwarded. The mask can't have bits 0, 1 or 2 set because they are used for STP, MAC pause frames and LACP. The Spanning Tree Protocol (STP) hello time, in seconds. The Spanning Tree Protocol (STP) hello time, in seconds. If specified, the MAC address of bridge. When creating a new bridge, this MAC address will be set. - - If this field is left unspecified, the "ethernet.cloned-mac-address" is referred instead to generate the initial MAC address. Note that setting "ethernet.cloned-mac-address" anyway overwrites the MAC address of the bridge later while activating the bridge. Hence, this property is deprecated. Deprecated: 1 If specified, the MAC address of bridge. When creating a new bridge, this MAC address will be set. If this field is left unspecified, the "ethernet.cloned-mac-address" is referred instead to generate the initial MAC address. Note that setting "ethernet.cloned-mac-address" anyway overwrites the MAC address of the bridge later while activating the bridge. Hence, this property is deprecated. Deprecated: 1 The Spanning Tree Protocol (STP) maximum message age, in seconds. The Spanning Tree Protocol (STP) maximum message age, in seconds. Set maximum size of multicast hash table (value must be a power of 2). Set maximum size of multicast hash table (value must be a power of 2). Set the number of queries the bridge will send before stopping forwarding a multicast group after a "leave" message has been received. Set the number of queries the bridge will send before stopping forwarding a multicast group after a "leave" message has been received. Set interval (in deciseconds) between queries to find remaining members of a group, after a "leave" message is received. Set interval (in deciseconds) between queries to find remaining members of a group, after a "leave" message is received. Set delay (in deciseconds) after which the bridge will leave a group, if no membership reports for this group are received. Set delay (in deciseconds) after which the bridge will leave a group, if no membership reports for this group are received. Enable or disable sending of multicast queries by the bridge. If not specified the option is disabled. Enable or disable sending of multicast queries by the bridge. If not specified the option is disabled. If no queries are seen after this delay (in deciseconds) has passed, the bridge will start to send its own queries. If no queries are seen after this delay (in deciseconds) has passed, the bridge will start to send its own queries. Interval (in deciseconds) between queries sent by the bridge after the end of the startup phase. Interval (in deciseconds) between queries sent by the bridge after the end of the startup phase. Set the Max Response Time/Max Response Delay (in deciseconds) for IGMP/MLD queries sent by the bridge. Set the Max Response Time/Max Response Delay (in deciseconds) for IGMP/MLD queries sent by the bridge. If enabled the bridge's own IP address is used as the source address for IGMP queries otherwise the default of 0.0.0.0 is used. If enabled the bridge's own IP address is used as the source address for IGMP queries otherwise the default of 0.0.0.0 is used. Sets bridge's multicast router. Multicast-snooping must be enabled for this option to work. - - Supported values are: 'auto', 'disabled', 'enabled' to which kernel assigns the numbers 1, 0, and 2, respectively. If not specified the default value is 'auto' (1). Sets bridge's multicast router. Multicast-snooping must be enabled for this option to work. Supported values are: 'auto', 'disabled', 'enabled' to which kernel assigns the numbers 1, 0, and 2, respectively. If not specified the default value is 'auto' (1). Controls whether IGMP snooping is enabled for this bridge. Note that if snooping was automatically disabled due to hash collisions, the system may refuse to enable the feature until the collisions are resolved. Controls whether IGMP snooping is enabled for this bridge. Note that if snooping was automatically disabled due to hash collisions, the system may refuse to enable the feature until the collisions are resolved. Set the number of IGMP queries to send during startup phase. Set the number of IGMP queries to send during startup phase. Sets the time (in deciseconds) between queries sent out at startup to determine membership information. Sets the time (in deciseconds) between queries sent out at startup to determine membership information. Sets the Spanning Tree Protocol (STP) priority for this bridge. Lower values are "better"; the lowest priority bridge will be elected the root bridge. Sets the Spanning Tree Protocol (STP) priority for this bridge. Lower values are "better"; the lowest priority bridge will be elected the root bridge. Controls whether Spanning Tree Protocol (STP) is enabled for this bridge. Controls whether Spanning Tree Protocol (STP) is enabled for this bridge. The default PVID for the ports of the bridge, that is the VLAN id assigned to incoming untagged frames. The default PVID for the ports of the bridge, that is the VLAN id assigned to incoming untagged frames. Control whether VLAN filtering is enabled on the bridge. Control whether VLAN filtering is enabled on the bridge. If specified, the protocol used for VLAN filtering. - - Supported values are: '802.1Q', '802.1ad'. If not specified the default value is '802.1Q'. If specified, the protocol used for VLAN filtering. Supported values are: '802.1Q', '802.1ad'. If not specified the default value is '802.1Q'. Controls whether per-VLAN stats accounting is enabled. Controls whether per-VLAN stats accounting is enabled. Array of bridge VLAN objects. In addition to the VLANs specified here, the bridge will also have the default-pvid VLAN configured by the bridge.vlan-default-pvid property. - - In nmcli the VLAN list can be specified with the following syntax: - - $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... - - where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash. Array of bridge VLAN objects. In addition to the VLANs specified here, the bridge will also have the default-pvid VLAN configured by the bridge.vlan-default-pvid property. In nmcli the VLAN list can be specified with the following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash. Enables or disables "hairpin mode" for the port, which allows frames to be sent back out through the port the frame was received on. Enables or disables "hairpin mode" for the port, which allows frames to be sent back out through the port the frame was received on. The Spanning Tree Protocol (STP) port cost for destinations via this port. The Spanning Tree Protocol (STP) port cost for destinations via this port. The Spanning Tree Protocol (STP) priority of this bridge port. The Spanning Tree Protocol (STP) priority of this bridge port. Array of bridge VLAN objects. In addition to the VLANs specified here, the port will also have the default-pvid VLAN configured on the bridge by the bridge.vlan-default-pvid property. - - In nmcli the VLAN list can be specified with the following syntax: - - $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... - - where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash. Array of bridge VLAN objects. In addition to the VLANs specified here, the port will also have the default-pvid VLAN configured on the bridge by the bridge.vlan-default-pvid property. In nmcli the VLAN list can be specified with the following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames. The number to dial to establish the connection to the CDMA-based mobile broadband network, if any. If not specified, the default number (#777) is used when required. The number to dial to establish the connection to the CDMA-based mobile broadband network, if any. If not specified, the default number (#777) is used when required. The password used to authenticate with the network, if required. Many providers do not require a password, or accept any password. But if a password is required, it is specified here. The password used to authenticate with the network, if required. Many providers do not require a password, or accept any password. But if a password is required, it is specified here. Flags indicating how to handle the "password" property. Flags indicating how to handle the "password" property. The username used to authenticate with the network, if required. Many providers do not require a username, or accept any username. But if a username is required, it is specified here. The username used to authenticate with the network, if required. Many providers do not require a username, or accept any username. But if a username is required, it is specified here. Specifies the NMSettingDcbFlags for the DCB FCoE application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). Specifies the NMSettingDcbFlags for the DCB FCoE application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). The FCoE controller mode; either "fabric" or "vn2vn". - - Since 1.34, NULL is the default and means "fabric". Before 1.34, NULL was rejected as invalid and the default was "fabric". The FCoE controller mode; either "fabric" or "vn2vn". Since 1.34, NULL is the default and means "fabric". Before 1.34, NULL was rejected as invalid and the default was "fabric". The highest User Priority (0 - 7) which FCoE frames should use, or -1 for default priority. Only used when the "app-fcoe-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag. The highest User Priority (0 - 7) which FCoE frames should use, or -1 for default priority. Only used when the "app-fcoe-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag. Specifies the NMSettingDcbFlags for the DCB FIP application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). Specifies the NMSettingDcbFlags for the DCB FIP application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). The highest User Priority (0 - 7) which FIP frames should use, or -1 for default priority. Only used when the "app-fip-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag. The highest User Priority (0 - 7) which FIP frames should use, or -1 for default priority. Only used when the "app-fip-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag. Specifies the NMSettingDcbFlags for the DCB iSCSI application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). Specifies the NMSettingDcbFlags for the DCB iSCSI application. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). The highest User Priority (0 - 7) which iSCSI frames should use, or -1 for default priority. Only used when the "app-iscsi-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag. The highest User Priority (0 - 7) which iSCSI frames should use, or -1 for default priority. Only used when the "app-iscsi-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag. An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the percentage of bandwidth of the priority's assigned group that the priority may use. The sum of all percentages for priorities which belong to the same group must total 100 percents. An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the percentage of bandwidth of the priority's assigned group that the priority may use. The sum of all percentages for priorities which belong to the same group must total 100 percents. An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the corresponding priority should transmit priority pause. An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the corresponding priority should transmit priority pause. Specifies the NMSettingDcbFlags for DCB Priority Flow Control (PFC). Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). Specifies the NMSettingDcbFlags for DCB Priority Flow Control (PFC). Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). An array of 8 uint values, where the array index corresponds to the Priority Group ID (0 - 7) and the value indicates the percentage of link bandwidth allocated to that group. Allowed values are 0 - 100, and the sum of all values must total 100 percents. An array of 8 uint values, where the array index corresponds to the Priority Group ID (0 - 7) and the value indicates the percentage of link bandwidth allocated to that group. Allowed values are 0 - 100, and the sum of all values must total 100 percents. Specifies the NMSettingDcbFlags for DCB Priority Groups. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). Specifies the NMSettingDcbFlags for DCB Priority Groups. Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the Priority Group ID. Allowed Priority Group ID values are 0 - 7 or 15 for the unrestricted group. An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the Priority Group ID. Allowed Priority Group ID values are 0 - 7 or 15 for the unrestricted group. An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the priority may use all of the bandwidth allocated to its assigned group. An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the priority may use all of the bandwidth allocated to its assigned group. An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the traffic class (0 - 7) to which the priority is mapped. An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the traffic class (0 - 7) to which the priority is mapped. The GPRS Access Point Name specifying the APN used when establishing a data session with the GSM-based network. The APN often determines how the user will be billed for their network usage and whether the user has access to the Internet or just a provider-specific walled-garden, so it is important to use the correct APN for the user's mobile broadband plan. The APN may only be composed of the characters a-z, 0-9, ., and - per GSM 03.60 Section 14.9. The GPRS Access Point Name specifying the APN used when establishing a data session with the GSM-based network. The APN often determines how the user will be billed for their network usage and whether the user has access to the Internet or just a provider-specific walled-garden, so it is important to use the correct APN for the user's mobile broadband plan. The APN may only be composed of the characters a-z, 0-9, ., and - per GSM 03.60 Section 14.9. When TRUE, the settings such as APN, username, or password will default to values that match the network the modem will register to in the Mobile Broadband Provider database. When TRUE, the settings such as APN, username, or password will default to values that match the network the modem will register to in the Mobile Broadband Provider database. The device unique identifier (as given by the WWAN management service) which this connection applies to. If given, the connection will only apply to the specified device. The device unique identifier (as given by the WWAN management service) which this connection applies to. If given, the connection will only apply to the specified device. When TRUE, only connections to the home network will be allowed. Connections to roaming networks will not be made. When TRUE, only connections to the home network will be allowed. Connections to roaming networks will not be made. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames. The Network ID (GSM LAI format, ie MCC-MNC) to force specific network registration. If the Network ID is specified, NetworkManager will attempt to force the device to register only on the specified network. This can be used to ensure that the device does not roam when direct roaming control of the device is not otherwise possible. The Network ID (GSM LAI format, ie MCC-MNC) to force specific network registration. If the Network ID is specified, NetworkManager will attempt to force the device to register only on the specified network. This can be used to ensure that the device does not roam when direct roaming control of the device is not otherwise possible. Legacy setting that used to help establishing PPP data sessions for GSM-based modems. Deprecated: 1 Legacy setting that used to help establishing PPP data sessions for GSM-based modems. Deprecated: 1 The password used to authenticate with the network, if required. Many providers do not require a password, or accept any password. But if a password is required, it is specified here. The password used to authenticate with the network, if required. Many providers do not require a password, or accept any password. But if a password is required, it is specified here. Flags indicating how to handle the "password" property. Flags indicating how to handle the "password" property. If the SIM is locked with a PIN it must be unlocked before any other operations are requested. Specify the PIN here to allow operation of the device. If the SIM is locked with a PIN it must be unlocked before any other operations are requested. Specify the PIN here to allow operation of the device. Flags indicating how to handle the "pin" property. Flags indicating how to handle the "pin" property. The SIM card unique identifier (as given by the WWAN management service) which this connection applies to. If given, the connection will apply to any device also allowed by "device-id" which contains a SIM card matching the given identifier. The SIM card unique identifier (as given by the WWAN management service) which this connection applies to. If given, the connection will apply to any device also allowed by "device-id" which contains a SIM card matching the given identifier. A MCC/MNC string like "310260" or "21601" identifying the specific mobile network operator which this connection applies to. If given, the connection will apply to any device also allowed by "device-id" and "sim-id" which contains a SIM card provisioned by the given operator. A MCC/MNC string like "310260" or "21601" identifying the specific mobile network operator which this connection applies to. If given, the connection will apply to any device also allowed by "device-id" and "sim-id" which contains a SIM card provisioned by the given operator. The username used to authenticate with the network, if required. Many providers do not require a username, or accept any username. But if a username is required, it is specified here. The username used to authenticate with the network, if required. Many providers do not require a username, or accept any username. But if a username is required, it is specified here. Whether the system hostname can be determined from DHCP on this connection. - - When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1). Whether the system hostname can be determined from DHCP on this connection. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1). Whether the system hostname can be determined from reverse DNS lookup of addresses on this device. - - When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1). Whether the system hostname can be determined from reverse DNS lookup of addresses on this device. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1). If set to NM_TERNARY_TRUE (1), NetworkManager attempts to get the hostname via DHCPv4/DHCPv6 or reverse DNS lookup on this device only when the device has the default route for the given address family (IPv4/IPv6). - - If set to NM_TERNARY_FALSE (0), the hostname can be set from this device even if it doesn't have the default route. - - When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_FALSE (0). If set to NM_TERNARY_TRUE (1), NetworkManager attempts to get the hostname via DHCPv4/DHCPv6 or reverse DNS lookup on this device only when the device has the default route for the given address family (IPv4/IPv6). If set to NM_TERNARY_FALSE (0), the hostname can be set from this device even if it doesn't have the default route. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_FALSE (0). The relative priority of this connection to determine the system hostname. A lower numerical value is better (higher priority). A connection with higher priority is considered before connections with lower priority. - - If the value is zero, it can be overridden by a global value from NetworkManager configuration. If the property doesn't have a value in the global configuration, the value is assumed to be 100. - - Negative values have the special effect of excluding other connections with a greater numerical priority value; so in presence of at least one negative priority, only connections with the lowest priority value will be used to determine the hostname. The relative priority of this connection to determine the system hostname. A lower numerical value is better (higher priority). A connection with higher priority is considered before connections with lower priority. If the value is zero, it can be overridden by a global value from NetworkManager configuration. If the property doesn't have a value in the global configuration, the value is assumed to be 100. Negative values have the special effect of excluding other connections with a greater numerical priority value; so in presence of at least one negative priority, only connections with the lowest priority value will be used to determine the hostname. If specified, this connection will only apply to the IPoIB device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing). If specified, this connection will only apply to the IPoIB device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing). If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames. The InfiniBand P_Key to use for this device. A value of -1 means to use the default P_Key (aka "the P_Key at index 0"). Otherwise, it is a 16-bit unsigned integer, whose high bit is set if it is a "full membership" P_Key. The InfiniBand P_Key to use for this device. A value of -1 means to use the default P_Key (aka "the P_Key at index 0"). Otherwise, it is a 16-bit unsigned integer, whose high bit is set if it is a "full membership" P_Key. The interface name of the parent device of this device. Normally NULL, but if the "p_key" property is set, then you must specify the base device by setting either this property or "mac-address". The interface name of the parent device of this device. Normally NULL, but if the "p_key" property is set, then you must specify the base device by setting either this property or "mac-address". The IP-over-InfiniBand transport mode. Either "datagram" or "connected". The IP-over-InfiniBand transport mode. Either "datagram" or "connected". Array of IP addresses. Array of IP addresses. Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. - - The property is currently implemented only for IPv4. Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4. A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string ('aa:bb:cc') it is interpreted as a binary client ID, in which case the first byte is assumed to be the 'type' field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the 'type' field is set to 0. - - The special values "mac" and "perm-mac" are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. - - The special value "ipv6-duid" uses the DUID from "ipv6.dhcp-duid" property as an RFC4361-compliant client identifier. As IAID it uses "ipv4.dhcp-iaid" and falls back to "ipv6.dhcp-iaid" if unset. - - The special value "duid" generates a RFC4361-compliant client identifier based on "ipv4.dhcp-iaid" and uses a DUID generated by hashing /etc/machine-id. - - The special value "stable" is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the "${DEVICE}" or "${MAC}" specifier to get a per-device key. - - If unset, a globally configured default is used. If still unset, the default depends on the DHCP plugin. A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string ('aa:bb:cc') it is interpreted as a binary client ID, in which case the first byte is assumed to be the 'type' field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the 'type' field is set to 0. The special values "mac" and "perm-mac" are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. The special value "ipv6-duid" uses the DUID from "ipv6.dhcp-duid" property as an RFC4361-compliant client identifier. As IAID it uses "ipv4.dhcp-iaid" and falls back to "ipv6.dhcp-iaid" if unset. The special value "duid" generates a RFC4361-compliant client identifier based on "ipv4.dhcp-iaid" and uses a DUID generated by hashing /etc/machine-id. The special value "stable" is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the "${DEVICE}" or "${MAC}" specifier to get a per-device key. If unset, a globally configured default is used. If still unset, the default depends on the DHCP plugin. If the "dhcp-send-hostname" property is TRUE, then the specified FQDN will be sent to the DHCP server when acquiring a lease. This property and "dhcp-hostname" are mutually exclusive and cannot be set at the same time. If the "dhcp-send-hostname" property is TRUE, then the specified FQDN will be sent to the DHCP server when acquiring a lease. This property and "dhcp-hostname" are mutually exclusive and cannot be set at the same time. If the "dhcp-send-hostname" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and "dhcp-fqdn" are mutually exclusive and cannot be set at the same time. If the "dhcp-send-hostname" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and "dhcp-fqdn" are mutually exclusive and cannot be set at the same time. Flags for the DHCP hostname and FQDN. - - Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4). When no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the standard FQDN flags are set in the request: NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6. - - When this property is set to the default value NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags described above are sent in the DHCP requests. Flags for the DHCP hostname and FQDN. Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4). When no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the standard FQDN flags are set in the request: NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6. When this property is set to the default value NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags described above are sent in the DHCP requests. A string containing the "Identity Association Identifier" (IAID) used by the DHCP client. The property is a 32-bit decimal value or a special value among "mac", "perm-mac", "ifname" and "stable". When set to "mac" (or "perm-mac"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to "ifname", the IAID is computed by hashing the interface name. The special value "stable" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be "ifname". Note that at the moment this property is ignored for IPv6 by dhclient, which always derives the IAID from the MAC address. A string containing the "Identity Association Identifier" (IAID) used by the DHCP client. The property is a 32-bit decimal value or a special value among "mac", "perm-mac", "ifname" and "stable". When set to "mac" (or "perm-mac"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to "ifname", the IAID is computed by hashing the interface name. The special value "stable" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be "ifname". Note that at the moment this property is ignored for IPv6 by dhclient, which always derives the IAID from the MAC address. Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. - - For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. "192.168.122.0/24"). - - This property is currently not implemented for DHCPv6. Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. "192.168.122.0/24"). This property is currently not implemented for DHCPv6. If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer. If the "dhcp-hostname" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent. If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer. If the "dhcp-hostname" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent. A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). - - Set to 2147483647 (MAXINT32) for infinity. A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity. The Vendor Class Identifier DHCP option (60). Special characters in the data string may be escaped using C-style escapes, nevertheless this property cannot contain nul bytes. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the DHCP option is not sent to the server. The Vendor Class Identifier DHCP option (60). Special characters in the data string may be escaped using C-style escapes, nevertheless this property cannot contain nul bytes. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the DHCP option is not sent to the server. Array of IP addresses of DNS servers. Array of IP addresses of DNS servers. Array of DNS options as described in man 5 resolv.conf. - - NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties. - - The currently supported options are "attempts", "debug", "edns0", "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names", "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single-request", "single-request-reopen", "timeout", "trust-ad", "use-vc". - - The "trust-ad" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have "trust-ad" enabled. - - When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then "edns0" and "trust-ad" are automatically added. Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties. The currently supported options are "attempts", "debug", "edns0", "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names", "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single-request", "single-request-reopen", "timeout", "trust-ad", "use-vc". The "trust-ad" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have "trust-ad" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then "edns0" and "trust-ad" are automatically added. DNS servers priority. - - The relative priority for DNS servers specified by this setting. A lower numerical value is better (higher priority). - - Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. - - Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. - - Note that the priority is to order DNS settings for multiple active connections. It does not disambiguate multiple DNS servers within the same connection profile. - - When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. - - When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the "rotate" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. - - When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain. Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually). When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins. If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured. DNS servers priority. The relative priority for DNS servers specified by this setting. A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections. It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the "rotate" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain. Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually). When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins. If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured. List of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. - - When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting. - - When set on a profile that also enabled DHCP, the DNS search list received automatically (option 119 for DHCPv4 and option 24 for DHCPv6) gets merged with the manual list. This can be prevented by setting "ignore-auto-dns". Note that if no DNS searches are configured, the fallback will be derived from the domain from DHCP (option 15). List of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting. When set on a profile that also enabled DHCP, the DNS search list received automatically (option 119 for DHCPv4 and option 24 for DHCPv6) gets merged with the manual list. This can be prevented by setting "ignore-auto-dns". Note that if no DNS searches are configured, the fallback will be derived from the domain from DHCP (option 15). The gateway associated with this configuration. This is only meaningful if "addresses" is also set. - - Setting the gateway causes NetworkManager to configure a standard default route with the gateway as next hop. This is ignored if "never-default" is set. An alternative is to configure the default route explicitly with a manual route and /0 as prefix length. - - Note that the gateway usually conflicts with routing that NetworkManager configures for WireGuard interfaces, so usually it should not be set in that case. See "ip4-auto-default-route". The gateway associated with this configuration. This is only meaningful if "addresses" is also set. Setting the gateway causes NetworkManager to configure a standard default route with the gateway as next hop. This is ignored if "never-default" is set. An alternative is to configure the default route explicitly with a manual route and /0 as prefix length. Note that the gateway usually conflicts with routing that NetworkManager configures for WireGuard interfaces, so usually it should not be set in that case. See "ip4-auto-default-route". When "method" is set to "auto" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the "dns" and "dns-search" properties, if any, are used. When "method" is set to "auto" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the "dns" and "dns-search" properties, if any, are used. When "method" is set to "auto" and this property to TRUE, automatically configured routes are ignored and only routes specified in the "routes" property, if any, are used. When "method" is set to "auto" and this property to TRUE, automatically configured routes are ignored and only routes specified in the "routes" property, if any, are used. Enable and disable the IPv4 link-local configuration independently of the ipv4.method configuration. This allows a link-local address (169.254.x.y/16) to be obtained in addition to other addresses, such as those manually configured or obtained from a DHCP server. - - When set to "auto", the value is dependent on "ipv4.method". When set to "default", it honors the global connection default, before falling back to "auto". Note that if "ipv4.method" is "disabled", then link local addressing is always disabled too. The default is "default". Enable and disable the IPv4 link-local configuration independently of the ipv4.method configuration. This allows a link-local address (169.254.x.y/16) to be obtained in addition to other addresses, such as those manually configured or obtained from a DHCP server. When set to "auto", the value is dependent on "ipv4.method". When set to "default", it honors the global connection default, before falling back to "auto". Note that if "ipv4.method" is "disabled", then link local addressing is always disabled too. The default is "default". If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out. Note that at least one IP configuration must succeed or overall network configuration will still fail. For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully. If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out. Note that at least one IP configuration must succeed or overall network configuration will still fail. For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully. IP configuration method. - - NMSettingIP4Config and NMSettingIP6Config both support "disabled", "auto", "manual", and "link-local". See the subclass-specific documentation for other values. - - In general, for the "auto" method, properties such as "dns" and "routes" specify information that is added on to the information returned from automatic configuration. The "ignore-auto-routes" and "ignore-auto-dns" properties modify this behavior. - - For methods that imply no upstream network, such as "shared" or "link-local", these properties must be empty. - - For IPv4 method "shared", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared. IP configuration method. NMSettingIP4Config and NMSettingIP6Config both support "disabled", "auto", "manual", and "link-local". See the subclass-specific documentation for other values. In general, for the "auto" method, properties such as "dns" and "routes" specify information that is added on to the information returned from automatic configuration. The "ignore-auto-routes" and "ignore-auto-dns" properties modify this behavior. For methods that imply no upstream network, such as "shared" or "link-local", these properties must be empty. For IPv4 method "shared", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared. If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager. If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager. The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. - - This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. - - Note that if "may-fail" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. - - A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero). The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. Note that if "may-fail" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero). The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric. The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric. Enable policy routing (source routing) and set the routing table used when adding routes. - - This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. - - If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. - - Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager. Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager. Array of IP routes. Array of IP routes. Configure method for creating the address for use with RFC4862 IPv6 Stateless Address Autoconfiguration. The permitted values are: NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 (0), NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY (1). NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT_OR_EUI64 (2) or NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT (3). - - If the property is set to EUI64, the addresses will be generated using the interface tokens derived from hardware address. This makes the host part of the address to stay constant, making it possible to track host's presence when it changes networks. The address changes when the interface hardware is replaced. - - The value of stable-privacy enables use of cryptographically secure hash of a secret host-specific key along with the connection's stable-id and the network address as specified by RFC7217. This makes it impossible to use the address track host's presence, and makes the address stable when the network interface hardware is replaced. - - The special values "default" and "default-or-eui64" will fallback to the global connection default in as documented in NetworkManager.conf(5) manual. If the global default is not specified, the fallback value is "stable-privacy" or "eui64", respectively. - - For libnm, the property defaults to "default" since 1.40. Previously it defaulted to "stable-privacy". On D-Bus, the absence of an addr-gen-mode setting equals "default". For keyfile plugin, the absence of the setting on disk means "default-or-eui64" so that the property doesn't change on upgrade from older versions. - - Note that this setting is distinct from the Privacy Extensions as configured by "ip6-privacy" property and it does not affect the temporary addresses configured with this option. Configure method for creating the address for use with RFC4862 IPv6 Stateless Address Autoconfiguration. The permitted values are: NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 (0), NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY (1). NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT_OR_EUI64 (2) or NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT (3). If the property is set to EUI64, the addresses will be generated using the interface tokens derived from hardware address. This makes the host part of the address to stay constant, making it possible to track host's presence when it changes networks. The address changes when the interface hardware is replaced. The value of stable-privacy enables use of cryptographically secure hash of a secret host-specific key along with the connection's stable-id and the network address as specified by RFC7217. This makes it impossible to use the address track host's presence, and makes the address stable when the network interface hardware is replaced. The special values "default" and "default-or-eui64" will fallback to the global connection default in as documented in NetworkManager.conf(5) manual. If the global default is not specified, the fallback value is "stable-privacy" or "eui64", respectively. For libnm, the property defaults to "default" since 1.40. Previously it defaulted to "stable-privacy". On D-Bus, the absence of an addr-gen-mode setting equals "default". For keyfile plugin, the absence of the setting on disk means "default-or-eui64" so that the property doesn't change on upgrade from older versions. Note that this setting is distinct from the Privacy Extensions as configured by "ip6-privacy" property and it does not affect the temporary addresses configured with this option. Array of IP addresses. Array of IP addresses. Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. - - The property is currently implemented only for IPv4. Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network. If an address conflict is detected, the activation will fail. A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero). A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4. A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp client to identify itself to DHCPv6 servers (RFC 3315). The DUID is carried in the Client Identifier option. If the property is a hex string ('aa:bb:cc') it is interpreted as a binary DUID and filled as an opaque value in the Client Identifier option. - - The special value "lease" will retrieve the DUID previously used from the lease file belonging to the connection. If no DUID is found and "dhclient" is the configured dhcp client, the DUID is searched in the system-wide dhclient lease file. If still no DUID is found, or another dhcp client is used, a global and permanent DUID-UUID (RFC 6355) will be generated based on the machine-id. - - The special values "llt" and "ll" will generate a DUID of type LLT or LL (see RFC 3315) based on the current MAC address of the device. In order to try providing a stable DUID-LLT, the time field will contain a constant timestamp that is used globally (for all profiles) and persisted to disk. - - The special values "stable-llt", "stable-ll" and "stable-uuid" will generate a DUID of the corresponding type, derived from the connection's stable-id and a per-host unique key. You may want to include the "${DEVICE}" or "${MAC}" specifier in the stable-id, in case this profile gets activated on multiple devices. So, the link-layer address of "stable-ll" and "stable-llt" will be a generated address derived from the stable id. The DUID-LLT time value in the "stable-llt" option will be picked among a static timespan of three years (the upper bound of the interval is the same constant timestamp used in "llt"). - - When the property is unset, the global value provided for "ipv6.dhcp-duid" is used. If no global value is provided, the default "lease" value is assumed. A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp client to identify itself to DHCPv6 servers (RFC 3315). The DUID is carried in the Client Identifier option. If the property is a hex string ('aa:bb:cc') it is interpreted as a binary DUID and filled as an opaque value in the Client Identifier option. The special value "lease" will retrieve the DUID previously used from the lease file belonging to the connection. If no DUID is found and "dhclient" is the configured dhcp client, the DUID is searched in the system-wide dhclient lease file. If still no DUID is found, or another dhcp client is used, a global and permanent DUID-UUID (RFC 6355) will be generated based on the machine-id. The special values "llt" and "ll" will generate a DUID of type LLT or LL (see RFC 3315) based on the current MAC address of the device. In order to try providing a stable DUID-LLT, the time field will contain a constant timestamp that is used globally (for all profiles) and persisted to disk. The special values "stable-llt", "stable-ll" and "stable-uuid" will generate a DUID of the corresponding type, derived from the connection's stable-id and a per-host unique key. You may want to include the "${DEVICE}" or "${MAC}" specifier in the stable-id, in case this profile gets activated on multiple devices. So, the link-layer address of "stable-ll" and "stable-llt" will be a generated address derived from the stable id. The DUID-LLT time value in the "stable-llt" option will be picked among a static timespan of three years (the upper bound of the interval is the same constant timestamp used in "llt"). When the property is unset, the global value provided for "ipv6.dhcp-duid" is used. If no global value is provided, the default "lease" value is assumed. If the "dhcp-send-hostname" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and "dhcp-fqdn" are mutually exclusive and cannot be set at the same time. If the "dhcp-send-hostname" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and "dhcp-fqdn" are mutually exclusive and cannot be set at the same time. Flags for the DHCP hostname and FQDN. - - Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4). When no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the standard FQDN flags are set in the request: NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6. - - When this property is set to the default value NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags described above are sent in the DHCP requests. Flags for the DHCP hostname and FQDN. Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4). When no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the standard FQDN flags are set in the request: NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6. When this property is set to the default value NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags described above are sent in the DHCP requests. A string containing the "Identity Association Identifier" (IAID) used by the DHCP client. The property is a 32-bit decimal value or a special value among "mac", "perm-mac", "ifname" and "stable". When set to "mac" (or "perm-mac"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to "ifname", the IAID is computed by hashing the interface name. The special value "stable" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be "ifname". Note that at the moment this property is ignored for IPv6 by dhclient, which always derives the IAID from the MAC address. A string containing the "Identity Association Identifier" (IAID) used by the DHCP client. The property is a 32-bit decimal value or a special value among "mac", "perm-mac", "ifname" and "stable". When set to "mac" (or "perm-mac"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to "ifname", the IAID is computed by hashing the interface name. The special value "stable" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be "ifname". Note that at the moment this property is ignored for IPv6 by dhclient, which always derives the IAID from the MAC address. Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. - - For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. "192.168.122.0/24"). - - This property is currently not implemented for DHCPv6. Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. "192.168.122.0/24"). This property is currently not implemented for DHCPv6. If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer. If the "dhcp-hostname" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent. If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer. If the "dhcp-hostname" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent. A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). - - Set to 2147483647 (MAXINT32) for infinity. A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity. Array of IP addresses of DNS servers. Array of IP addresses of DNS servers. Array of DNS options as described in man 5 resolv.conf. - - NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties. - - The currently supported options are "attempts", "debug", "edns0", "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names", "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single-request", "single-request-reopen", "timeout", "trust-ad", "use-vc". - - The "trust-ad" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have "trust-ad" enabled. - - When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then "edns0" and "trust-ad" are automatically added. Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties. The currently supported options are "attempts", "debug", "edns0", "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names", "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single-request", "single-request-reopen", "timeout", "trust-ad", "use-vc". The "trust-ad" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have "trust-ad" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then "edns0" and "trust-ad" are automatically added. DNS servers priority. - - The relative priority for DNS servers specified by this setting. A lower numerical value is better (higher priority). - - Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. - - Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. - - Note that the priority is to order DNS settings for multiple active connections. It does not disambiguate multiple DNS servers within the same connection profile. - - When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. - - When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the "rotate" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. - - When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain. Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually). When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins. If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured. DNS servers priority. The relative priority for DNS servers specified by this setting. A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections. It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the "rotate" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain. Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually). When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins. If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured. List of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. - - When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting. - - When set on a profile that also enabled DHCP, the DNS search list received automatically (option 119 for DHCPv4 and option 24 for DHCPv6) gets merged with the manual list. This can be prevented by setting "ignore-auto-dns". Note that if no DNS searches are configured, the fallback will be derived from the domain from DHCP (option 15). List of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting. When set on a profile that also enabled DHCP, the DNS search list received automatically (option 119 for DHCPv4 and option 24 for DHCPv6) gets merged with the manual list. This can be prevented by setting "ignore-auto-dns". Note that if no DNS searches are configured, the fallback will be derived from the domain from DHCP (option 15). The gateway associated with this configuration. This is only meaningful if "addresses" is also set. - - Setting the gateway causes NetworkManager to configure a standard default route with the gateway as next hop. This is ignored if "never-default" is set. An alternative is to configure the default route explicitly with a manual route and /0 as prefix length. - - Note that the gateway usually conflicts with routing that NetworkManager configures for WireGuard interfaces, so usually it should not be set in that case. See "ip4-auto-default-route". The gateway associated with this configuration. This is only meaningful if "addresses" is also set. Setting the gateway causes NetworkManager to configure a standard default route with the gateway as next hop. This is ignored if "never-default" is set. An alternative is to configure the default route explicitly with a manual route and /0 as prefix length. Note that the gateway usually conflicts with routing that NetworkManager configures for WireGuard interfaces, so usually it should not be set in that case. See "ip4-auto-default-route". When "method" is set to "auto" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the "dns" and "dns-search" properties, if any, are used. When "method" is set to "auto" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the "dns" and "dns-search" properties, if any, are used. When "method" is set to "auto" and this property to TRUE, automatically configured routes are ignored and only routes specified in the "routes" property, if any, are used. When "method" is set to "auto" and this property to TRUE, automatically configured routes are ignored and only routes specified in the "routes" property, if any, are used. Configure IPv6 Privacy Extensions for SLAAC, described in RFC4941. If enabled, it makes the kernel generate a temporary IPv6 address in addition to the public one generated from MAC address via modified EUI-64. This enhances privacy, but could cause problems in some applications, on the other hand. The permitted values are: -1: unknown, 0: disabled, 1: enabled (prefer public address), 2: enabled (prefer temporary addresses). - - Having a per-connection setting set to "-1" (unknown) means fallback to global configuration "ipv6.ip6-privacy". - - If also global configuration is unspecified or set to "-1", fallback to read "/proc/sys/net/ipv6/conf/default/use_tempaddr". - - Note that this setting is distinct from the Stable Privacy addresses that can be enabled with the "addr-gen-mode" property's "stable-privacy" setting as another way of avoiding host tracking with IPv6 addresses. Configure IPv6 Privacy Extensions for SLAAC, described in RFC4941. If enabled, it makes the kernel generate a temporary IPv6 address in addition to the public one generated from MAC address via modified EUI-64. This enhances privacy, but could cause problems in some applications, on the other hand. The permitted values are: -1: unknown, 0: disabled, 1: enabled (prefer public address), 2: enabled (prefer temporary addresses). Having a per-connection setting set to "-1" (unknown) means fallback to global configuration "ipv6.ip6-privacy". If also global configuration is unspecified or set to "-1", fallback to read "/proc/sys/net/ipv6/conf/default/use_tempaddr". Note that this setting is distinct from the Stable Privacy addresses that can be enabled with the "addr-gen-mode" property's "stable-privacy" setting as another way of avoiding host tracking with IPv6 addresses. If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out. Note that at least one IP configuration must succeed or overall network configuration will still fail. For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully. If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out. Note that at least one IP configuration must succeed or overall network configuration will still fail. For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully. IP configuration method. - - NMSettingIP4Config and NMSettingIP6Config both support "disabled", "auto", "manual", and "link-local". See the subclass-specific documentation for other values. - - In general, for the "auto" method, properties such as "dns" and "routes" specify information that is added on to the information returned from automatic configuration. The "ignore-auto-routes" and "ignore-auto-dns" properties modify this behavior. - - For methods that imply no upstream network, such as "shared" or "link-local", these properties must be empty. - - For IPv4 method "shared", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared. IP configuration method. NMSettingIP4Config and NMSettingIP6Config both support "disabled", "auto", "manual", and "link-local". See the subclass-specific documentation for other values. In general, for the "auto" method, properties such as "dns" and "routes" specify information that is added on to the information returned from automatic configuration. The "ignore-auto-routes" and "ignore-auto-dns" properties modify this behavior. For methods that imply no upstream network, such as "shared" or "link-local", these properties must be empty. For IPv4 method "shared", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared. Maximum transmission unit size, in bytes. If zero (the default), the MTU is set automatically from router advertisements or is left equal to the link-layer MTU. If greater than the link-layer MTU, or greater than zero but less than the minimum IPv6 MTU of 1280, this value has no effect. Maximum transmission unit size, in bytes. If zero (the default), the MTU is set automatically from router advertisements or is left equal to the link-layer MTU. If greater than the link-layer MTU, or greater than zero but less than the minimum IPv6 MTU of 1280, this value has no effect. If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager. If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager. A timeout for waiting Router Advertisements in seconds. If zero (the default), a globally configured default is used. If still unspecified, the timeout depends on the sysctl settings of the device. - - Set to 2147483647 (MAXINT32) for infinity. A timeout for waiting Router Advertisements in seconds. If zero (the default), a globally configured default is used. If still unspecified, the timeout depends on the sysctl settings of the device. Set to 2147483647 (MAXINT32) for infinity. The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. - - This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. - - Note that if "may-fail" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. - - A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero). The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. Note that if "may-fail" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero). The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric. The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric. Enable policy routing (source routing) and set the routing table used when adding routes. - - This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. - - If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. - - Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager. Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager. Array of IP routes. Array of IP routes. Configure the token for draft-chown-6man-tokenised-ipv6-identifiers-02 IPv6 tokenized interface identifiers. Useful with eui64 addr-gen-mode. Configure the token for draft-chown-6man-tokenised-ipv6-identifiers-02 IPv6 tokenized interface identifiers. Useful with eui64 addr-gen-mode. How many additional levels of encapsulation are permitted to be prepended to packets. This property applies only to IPv6 tunnels. How many additional levels of encapsulation are permitted to be prepended to packets. This property applies only to IPv6 tunnels. Tunnel flags. Currently, the following values are supported: NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT (0x1), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS (0x2), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL (0x4), NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV (0x8), NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY (0x10), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK (0x20). They are valid only for IPv6 tunnels. Tunnel flags. Currently, the following values are supported: NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT (0x1), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS (0x2), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL (0x4), NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV (0x8), NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY (0x10), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK (0x20). They are valid only for IPv6 tunnels. The flow label to assign to tunnel packets. This property applies only to IPv6 tunnels. The flow label to assign to tunnel packets. This property applies only to IPv6 tunnels. The key used for tunnel input packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used. The key used for tunnel input packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used. The local endpoint of the tunnel; the value can be empty, otherwise it must contain an IPv4 or IPv6 address. The local endpoint of the tunnel; the value can be empty, otherwise it must contain an IPv4 or IPv6 address. The tunneling mode, for example NM_IP_TUNNEL_MODE_IPIP (1) or NM_IP_TUNNEL_MODE_GRE (2). The tunneling mode, for example NM_IP_TUNNEL_MODE_IPIP (1) or NM_IP_TUNNEL_MODE_GRE (2). If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments. The key used for tunnel output packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used. The key used for tunnel output packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used. If given, specifies the parent interface name or parent connection UUID the new device will be bound to so that tunneled packets will only be routed via that interface. If given, specifies the parent interface name or parent connection UUID the new device will be bound to so that tunneled packets will only be routed via that interface. Whether to enable Path MTU Discovery on this tunnel. Whether to enable Path MTU Discovery on this tunnel. The remote endpoint of the tunnel; the value must contain an IPv4 or IPv6 address. The remote endpoint of the tunnel; the value must contain an IPv4 or IPv6 address. The type of service (IPv4) or traffic class (IPv6) field to be set on tunneled packets. The type of service (IPv4) or traffic class (IPv6) field to be set on tunneled packets. The TTL to assign to tunneled packets. 0 is a special value meaning that packets inherit the TTL value. The TTL to assign to tunneled packets. 0 is a special value meaning that packets inherit the TTL value. Whether the transmitted traffic must be encrypted. Whether the transmitted traffic must be encrypted. The pre-shared CAK (Connectivity Association Key) for MACsec Key Agreement. Must be a string of 32 hexadecimal characters. The pre-shared CAK (Connectivity Association Key) for MACsec Key Agreement. Must be a string of 32 hexadecimal characters. Flags indicating how to handle the "mka-cak" property. Flags indicating how to handle the "mka-cak" property. The pre-shared CKN (Connectivity-association Key Name) for MACsec Key Agreement. Must be a string of hexadecimal characters with a even length between 2 and 64. The pre-shared CKN (Connectivity-association Key Name) for MACsec Key Agreement. Must be a string of hexadecimal characters with a even length between 2 and 64. Specifies how the CAK (Connectivity Association Key) for MKA (MACsec Key Agreement) is obtained. Specifies how the CAK (Connectivity Association Key) for MKA (MACsec Key Agreement) is obtained. If given, specifies the parent interface name or parent connection UUID from which this MACSEC interface should be created. If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property. If given, specifies the parent interface name or parent connection UUID from which this MACSEC interface should be created. If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property. The port component of the SCI (Secure Channel Identifier), between 1 and 65534. The port component of the SCI (Secure Channel Identifier), between 1 and 65534. Specifies whether the SCI (Secure Channel Identifier) is included in every packet. Specifies whether the SCI (Secure Channel Identifier) is included in every packet. Specifies the validation mode for incoming frames. Specifies the validation mode for incoming frames. The macvlan mode, which specifies the communication mechanism between multiple macvlans on the same lower device. The macvlan mode, which specifies the communication mechanism between multiple macvlans on the same lower device. If given, specifies the parent interface name or parent connection UUID from which this MAC-VLAN interface should be created. If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property. If given, specifies the parent interface name or parent connection UUID from which this MAC-VLAN interface should be created. If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property. Whether the interface should be put in promiscuous mode. Whether the interface should be put in promiscuous mode. Whether the interface should be a MACVTAP. Whether the interface should be a MACVTAP. A list of driver names to match. Each element is a shell wildcard pattern. - - See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the pattern. A list of driver names to match. Each element is a shell wildcard pattern. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the pattern. A list of interface names to match. Each element is a shell wildcard pattern. - - An element can be prefixed with a pipe symbol (|) or an ampersand (&). The former means that the element is optional and the latter means that it is mandatory. If there are any optional elements, than the match evaluates to true if at least one of the optional element matches (logical OR). If there are any mandatory elements, then they all must match (logical AND). By default, an element is optional. This means that an element "foo" behaves the same as "|foo". An element can also be inverted with exclamation mark (!) between the pipe symbol (or the ampersand) and before the pattern. Note that "!foo" is a shortcut for the mandatory match "&!foo". Finally, a backslash can be used at the beginning of the element (after the optional special characters) to escape the start of the pattern. For example, "&\\!a" is an mandatory match for literally "!a". A list of interface names to match. Each element is a shell wildcard pattern. An element can be prefixed with a pipe symbol (|) or an ampersand (&). The former means that the element is optional and the latter means that it is mandatory. If there are any optional elements, than the match evaluates to true if at least one of the optional element matches (logical OR). If there are any mandatory elements, then they all must match (logical AND). By default, an element is optional. This means that an element "foo" behaves the same as "|foo". An element can also be inverted with exclamation mark (!) between the pipe symbol (or the ampersand) and before the pattern. Note that "!foo" is a shortcut for the mandatory match "&!foo". Finally, a backslash can be used at the beginning of the element (after the optional special characters) to escape the start of the pattern. For example, "&\\!a" is an mandatory match for literally "!a". A list of kernel command line arguments to match. This may be used to check whether a specific kernel command line option is set (or unset, if prefixed with the exclamation mark). The argument must either be a single word, or an assignment (i.e. two words, joined by "="). In the former case the kernel command line is searched for the word appearing as is, or as left hand side of an assignment. In the latter case, the exact assignment is looked for with right and left hand side matching. Wildcard patterns are not supported. - - See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the match. A list of kernel command line arguments to match. This may be used to check whether a specific kernel command line option is set (or unset, if prefixed with the exclamation mark). The argument must either be a single word, or an assignment (i.e. two words, joined by "="). In the former case the kernel command line is searched for the word appearing as is, or as left hand side of an assignment. In the latter case, the exact assignment is looked for with right and left hand side matching. Wildcard patterns are not supported. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the match. A list of paths to match against the ID_PATH udev property of devices. ID_PATH represents the topological persistent path of a device. It typically contains a subsystem string (pci, usb, platform, etc.) and a subsystem-specific identifier. - - For PCI devices the path has the form "pci-$domain:$bus:$device.$function", where each variable is an hexadecimal value; for example "pci-0000:0a:00.0". - - The path of a device can be obtained with "udevadm info /sys/class/net/$dev | grep ID_PATH=" or by looking at the "path" property exported by NetworkManager ("nmcli -f general.path device show $dev"). - - Each element of the list is a shell wildcard pattern. - - See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the pattern. A list of paths to match against the ID_PATH udev property of devices. ID_PATH represents the topological persistent path of a device. It typically contains a subsystem string (pci, usb, platform, etc.) and a subsystem-specific identifier. For PCI devices the path has the form "pci-$domain:$bus:$device.$function", where each variable is an hexadecimal value; for example "pci-0000:0a:00.0". The path of a device can be obtained with "udevadm info /sys/class/net/$dev | grep ID_PATH=" or by looking at the "path" property exported by NetworkManager ("nmcli -f general.path device show $dev"). Each element of the list is a shell wildcard pattern. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the pattern. Channel on which the mesh network to join is located. Channel on which the mesh network to join is located. Anycast DHCP MAC address used when requesting an IP address via DHCP. The specific anycast address used determines which DHCP server class answers the request. - - This is currently only implemented by dhclient DHCP plugin. Anycast DHCP MAC address used when requesting an IP address via DHCP. The specific anycast address used determines which DHCP server class answers the request. This is currently only implemented by dhclient DHCP plugin. SSID of the mesh network to join. SSID of the mesh network to join. The data path type. One of "system", "netdev" or empty. The data path type. One of "system", "netdev" or empty. The bridge failure mode. One of "secure", "standalone" or empty. The bridge failure mode. One of "secure", "standalone" or empty. Enable or disable multicast snooping. Enable or disable multicast snooping. Enable or disable RSTP. Enable or disable RSTP. Enable or disable STP. Enable or disable STP. Open vSwitch DPDK device arguments. Open vSwitch DPDK device arguments. Open vSwitch DPDK number of rx queues. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures one queue. Open vSwitch DPDK number of rx queues. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures one queue. A dictionary of key/value pairs with exernal-ids for OVS. A dictionary of key/value pairs with exernal-ids for OVS. The interface type. Either "internal", "system", "patch", "dpdk", or empty. The interface type. Either "internal", "system", "patch", "dpdk", or empty. Specifies the name of the interface for the other side of the patch. The patch on the other side must also set this interface as peer. Specifies the name of the interface for the other side of the patch. The patch on the other side must also set this interface as peer. The time port must be inactive in order to be considered down. The time port must be inactive in order to be considered down. Bonding mode. One of "active-backup", "balance-slb", or "balance-tcp". Bonding mode. One of "active-backup", "balance-slb", or "balance-tcp". The time port must be active before it starts forwarding traffic. The time port must be active before it starts forwarding traffic. LACP mode. One of "active", "off", or "passive". LACP mode. One of "active", "off", or "passive". The VLAN tag in the range 0-4095. The VLAN tag in the range 0-4095. The VLAN mode. One of "access", "native-tagged", "native-untagged", "trunk" or unset. The VLAN mode. One of "access", "native-tagged", "native-untagged", "trunk" or unset. If non-zero, instruct pppd to set the serial port to the specified baudrate. This value should normally be left as 0 to automatically choose the speed. If non-zero, instruct pppd to set the serial port to the specified baudrate. This value should normally be left as 0 to automatically choose the speed. If TRUE, specify that pppd should set the serial port to use hardware flow control with RTS and CTS signals. This value should normally be set to FALSE. If TRUE, specify that pppd should set the serial port to use hardware flow control with RTS and CTS signals. This value should normally be set to FALSE. If non-zero, instruct pppd to presume the connection to the peer has failed if the specified number of LCP echo-requests go unanswered by the peer. The "lcp-echo-interval" property must also be set to a non-zero value if this property is used. If non-zero, instruct pppd to presume the connection to the peer has failed if the specified number of LCP echo-requests go unanswered by the peer. The "lcp-echo-interval" property must also be set to a non-zero value if this property is used. If non-zero, instruct pppd to send an LCP echo-request frame to the peer every n seconds (where n is the specified value). Note that some PPP peers will respond to echo requests and some will not, and it is not possible to autodetect this. If non-zero, instruct pppd to send an LCP echo-request frame to the peer every n seconds (where n is the specified value). Note that some PPP peers will respond to echo requests and some will not, and it is not possible to autodetect this. If TRUE, stateful MPPE is used. See pppd documentation for more information on stateful MPPE. If TRUE, stateful MPPE is used. See pppd documentation for more information on stateful MPPE. If non-zero, instruct pppd to request that the peer send packets no larger than the specified size. If non-zero, the MRU should be between 128 and 16384. If non-zero, instruct pppd to request that the peer send packets no larger than the specified size. If non-zero, the MRU should be between 128 and 16384. If non-zero, instruct pppd to send packets no larger than the specified size. If non-zero, instruct pppd to send packets no larger than the specified size. If TRUE, Van Jacobsen TCP header compression will not be requested. If TRUE, Van Jacobsen TCP header compression will not be requested. If TRUE, do not require the other side (usually the PPP server) to authenticate itself to the client. If FALSE, require authentication from the remote side. In almost all cases, this should be TRUE. If TRUE, do not require the other side (usually the PPP server) to authenticate itself to the client. If FALSE, require authentication from the remote side. In almost all cases, this should be TRUE. If TRUE, BSD compression will not be requested. If TRUE, BSD compression will not be requested. If TRUE, "deflate" compression will not be requested. If TRUE, "deflate" compression will not be requested. If TRUE, the CHAP authentication method will not be used. If TRUE, the CHAP authentication method will not be used. If TRUE, the EAP authentication method will not be used. If TRUE, the EAP authentication method will not be used. If TRUE, the MSCHAP authentication method will not be used. If TRUE, the MSCHAP authentication method will not be used. If TRUE, the MSCHAPv2 authentication method will not be used. If TRUE, the MSCHAPv2 authentication method will not be used. If TRUE, the PAP authentication method will not be used. If TRUE, the PAP authentication method will not be used. If TRUE, MPPE (Microsoft Point-to-Point Encryption) will be required for the PPP session. If either 64-bit or 128-bit MPPE is not available the session will fail. Note that MPPE is not used on mobile broadband connections. If TRUE, MPPE (Microsoft Point-to-Point Encryption) will be required for the PPP session. If either 64-bit or 128-bit MPPE is not available the session will fail. Note that MPPE is not used on mobile broadband connections. If TRUE, 128-bit MPPE (Microsoft Point-to-Point Encryption) will be required for the PPP session, and the "require-mppe" property must also be set to TRUE. If 128-bit MPPE is not available the session will fail. If TRUE, 128-bit MPPE (Microsoft Point-to-Point Encryption) will be required for the PPP session, and the "require-mppe" property must also be set to TRUE. If 128-bit MPPE is not available the session will fail. If given, specifies the parent interface name on which this PPPoE connection should be created. If this property is not specified, the connection is activated on the interface specified in "interface-name" of NMSettingConnection. If given, specifies the parent interface name on which this PPPoE connection should be created. If this property is not specified, the connection is activated on the interface specified in "interface-name" of NMSettingConnection. Password used to authenticate with the PPPoE service. Password used to authenticate with the PPPoE service. Flags indicating how to handle the "password" property. Flags indicating how to handle the "password" property. If specified, instruct PPPoE to only initiate sessions with access concentrators that provide the specified service. For most providers, this should be left blank. It is only required if there are multiple access concentrators or a specific service is known to be required. If specified, instruct PPPoE to only initiate sessions with access concentrators that provide the specified service. For most providers, this should be left blank. It is only required if there are multiple access concentrators or a specific service is known to be required. Username used to authenticate with the PPPoE service. Username used to authenticate with the PPPoE service. Whether the proxy configuration is for browser only. Whether the proxy configuration is for browser only. Method for proxy configuration, Default is NM_SETTING_PROXY_METHOD_NONE (0) Method for proxy configuration, Default is NM_SETTING_PROXY_METHOD_NONE (0) PAC script for the connection. This is an UTF-8 encoded javascript code that defines a FindProxyForURL() function. PAC script for the connection. This is an UTF-8 encoded javascript code that defines a FindProxyForURL() function. PAC URL for obtaining PAC file. PAC URL for obtaining PAC file. Speed to use for communication over the serial port. Note that this value usually has no effect for mobile broadband modems as they generally ignore speed settings and use the highest available speed. Speed to use for communication over the serial port. Note that this value usually has no effect for mobile broadband modems as they generally ignore speed settings and use the highest available speed. Byte-width of the serial communication. The 8 in "8n1" for example. Byte-width of the serial communication. The 8 in "8n1" for example. Parity setting of the serial port. Parity setting of the serial port. Time to delay between each byte sent to the modem, in microseconds. Time to delay between each byte sent to the modem, in microseconds. Number of stop bits for communication on the serial port. Either 1 or 2. The 1 in "8n1" for example. Number of stop bits for communication on the serial port. Either 1 or 2. The 1 in "8n1" for example. Whether to autoprobe virtual functions by a compatible driver. - - If set to NM_TERNARY_TRUE (1), the kernel will try to bind VFs to a compatible driver and if this succeeds a new network interface will be instantiated for each VF. - - If set to NM_TERNARY_FALSE (0), VFs will not be claimed and no network interfaces will be created for them. - - When set to NM_TERNARY_DEFAULT (-1), the global default is used; in case the global default is unspecified it is assumed to be NM_TERNARY_TRUE (1). Whether to autoprobe virtual functions by a compatible driver. If set to NM_TERNARY_TRUE (1), the kernel will try to bind VFs to a compatible driver and if this succeeds a new network interface will be instantiated for each VF. If set to NM_TERNARY_FALSE (0), VFs will not be claimed and no network interfaces will be created for them. When set to NM_TERNARY_DEFAULT (-1), the global default is used; in case the global default is unspecified it is assumed to be NM_TERNARY_TRUE (1). The total number of virtual functions to create. - - Note that when the sriov setting is present NetworkManager enforces the number of virtual functions on the interface (also when it is zero) during activation and resets it upon deactivation. To prevent any changes to SR-IOV parameters don't add a sriov setting to the connection. The total number of virtual functions to create. Note that when the sriov setting is present NetworkManager enforces the number of virtual functions on the interface (also when it is zero) during activation and resets it upon deactivation. To prevent any changes to SR-IOV parameters don't add a sriov setting to the connection. Array of virtual function descriptors. - - Each VF descriptor is a dictionary mapping attribute names to GVariant values. The 'index' entry is mandatory for each VF. - - When represented as string a VF is in the form: - - "INDEX [ATTR=VALUE[ ATTR=VALUE]...]". - - for example: - - "2 mac=00:11:22:33:44:55 spoof-check=true". - - Multiple VFs can be specified using a comma as separator. Currently, the following attributes are supported: mac, spoof-check, trust, min-tx-rate, max-tx-rate, vlans. - - The "vlans" attribute is represented as a semicolon-separated list of VLAN descriptors, where each descriptor has the form - - "ID[.PRIORITY[.PROTO]]". - - PROTO can be either 'q' for 802.1Q (the default) or 'ad' for 802.1ad. Array of virtual function descriptors. Each VF descriptor is a dictionary mapping attribute names to GVariant values. The 'index' entry is mandatory for each VF. When represented as string a VF is in the form: "INDEX [ATTR=VALUE[ ATTR=VALUE]...]". for example: "2 mac=00:11:22:33:44:55 spoof-check=true". Multiple VFs can be specified using a comma as separator. Currently, the following attributes are supported: mac, spoof-check, trust, min-tx-rate, max-tx-rate, vlans. The "vlans" attribute is represented as a semicolon-separated list of VLAN descriptors, where each descriptor has the form "ID[.PRIORITY[.PROTO]]". PROTO can be either 'q' for 802.1Q (the default) or 'ad' for 802.1ad. Array of TC queueing disciplines. - - When the "tc" setting is present, qdiscs from this property are applied upon activation. If the property is empty, all qdiscs are removed and the device will only have the default qdisc assigned by kernel according to the "net.core.default_qdisc" sysctl. - - If the "tc" setting is not present, NetworkManager doesn't touch the qdiscs present on the interface. Array of TC queueing disciplines. When the "tc" setting is present, qdiscs from this property are applied upon activation. If the property is empty, all qdiscs are removed and the device will only have the default qdisc assigned by kernel according to the "net.core.default_qdisc" sysctl. If the "tc" setting is not present, NetworkManager doesn't touch the qdiscs present on the interface. Array of TC traffic filters. - - When the "tc" setting is present, filters from this property are applied upon activation. If the property is empty, NetworkManager removes all the filters. - - If the "tc" setting is not present, NetworkManager doesn't touch the filters present on the interface. Array of TC traffic filters. When the "tc" setting is present, filters from this property are applied upon activation. If the property is empty, NetworkManager removes all the filters. If the "tc" setting is not present, NetworkManager doesn't touch the filters present on the interface. The JSON configuration for the team network interface. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details. The JSON configuration for the team network interface. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details. Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are: ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details. Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are: ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details. Corresponds to the teamd mcast_rejoin.count. Corresponds to the teamd mcast_rejoin.count. Corresponds to the teamd mcast_rejoin.interval. Corresponds to the teamd mcast_rejoin.interval. Corresponds to the teamd notify_peers.count. Corresponds to the teamd notify_peers.count. Corresponds to the teamd notify_peers.interval. Corresponds to the teamd notify_peers.interval. Corresponds to the teamd runner.name. Permitted values are: "roundrobin", "broadcast", "activebackup", "loadbalance", "lacp", "random". Corresponds to the teamd runner.name. Permitted values are: "roundrobin", "broadcast", "activebackup", "loadbalance", "lacp", "random". Corresponds to the teamd runner.active. Corresponds to the teamd runner.active. Corresponds to the teamd runner.agg_select_policy. Corresponds to the teamd runner.agg_select_policy. Corresponds to the teamd runner.fast_rate. Corresponds to the teamd runner.fast_rate. Corresponds to the teamd runner.hwaddr_policy. Corresponds to the teamd runner.hwaddr_policy. Corresponds to the teamd runner.min_ports. Corresponds to the teamd runner.min_ports. Corresponds to the teamd runner.sys_prio. Corresponds to the teamd runner.sys_prio. Corresponds to the teamd runner.tx_balancer.name. Corresponds to the teamd runner.tx_balancer.name. Corresponds to the teamd runner.tx_balancer.interval. Corresponds to the teamd runner.tx_balancer.interval. Corresponds to the teamd runner.tx_hash. Corresponds to the teamd runner.tx_hash. The JSON configuration for the team port. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details. The JSON configuration for the team port. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details. Corresponds to the teamd ports.PORTIFNAME.lacp_key. Corresponds to the teamd ports.PORTIFNAME.lacp_key. Corresponds to the teamd ports.PORTIFNAME.lacp_prio. Corresponds to the teamd ports.PORTIFNAME.lacp_prio. Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are: ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details. Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are: ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details. Corresponds to the teamd ports.PORTIFNAME.prio. Corresponds to the teamd ports.PORTIFNAME.prio. Corresponds to the teamd ports.PORTIFNAME.queue_id. When set to -1 means the parameter is skipped from the json config. Corresponds to the teamd ports.PORTIFNAME.queue_id. When set to -1 means the parameter is skipped from the json config. Corresponds to the teamd ports.PORTIFNAME.sticky. Corresponds to the teamd ports.PORTIFNAME.sticky. The group ID which will own the device. If set to NULL everyone will be able to use the device. The group ID which will own the device. If set to NULL everyone will be able to use the device. The operating mode of the virtual device. Allowed values are NM_SETTING_TUN_MODE_TUN (1) to create a layer 3 device and NM_SETTING_TUN_MODE_TAP (2) to create an Ethernet-like layer 2 one. The operating mode of the virtual device. Allowed values are NM_SETTING_TUN_MODE_TUN (1) to create a layer 3 device and NM_SETTING_TUN_MODE_TAP (2) to create an Ethernet-like layer 2 one. If the property is set to TRUE, the interface will support multiple file descriptors (queues) to parallelize packet sending or receiving. Otherwise, the interface will only support a single queue. If the property is set to TRUE, the interface will support multiple file descriptors (queues) to parallelize packet sending or receiving. Otherwise, the interface will only support a single queue. The user ID which will own the device. If set to NULL everyone will be able to use the device. The user ID which will own the device. If set to NULL everyone will be able to use the device. If TRUE the interface will prepend a 4 byte header describing the physical interface to the packets. If TRUE the interface will prepend a 4 byte header describing the physical interface to the packets. If TRUE the IFF_VNET_HDR the tunnel packets will include a virtio network header. If TRUE the IFF_VNET_HDR the tunnel packets will include a virtio network header. A dictionary of key/value pairs with user data. This data is ignored by NetworkManager and can be used at the users discretion. The keys only support a strict ascii format, but the values can be arbitrary UTF8 strings up to a certain length. A dictionary of key/value pairs with user data. This data is ignored by NetworkManager and can be used at the users discretion. The keys only support a strict ascii format, but the values can be arbitrary UTF8 strings up to a certain length. This property specifies the peer interface name of the veth. This property is mandatory. This property specifies the peer interface name of the veth. This property is mandatory. For outgoing packets, a list of mappings from Linux SKB priorities to 802.1p priorities. The mapping is given in the format "from:to" where both "from" and "to" are unsigned integers, ie "7:3". For outgoing packets, a list of mappings from Linux SKB priorities to 802.1p priorities. The mapping is given in the format "from:to" where both "from" and "to" are unsigned integers, ie "7:3". One or more flags which control the behavior and features of the VLAN interface. Flags include NM_VLAN_FLAG_REORDER_HEADERS (0x1) (reordering of output packet headers), NM_VLAN_FLAG_GVRP (0x2) (use of the GVRP protocol), and NM_VLAN_FLAG_LOOSE_BINDING (0x4) (loose binding of the interface to its master device's operating state). NM_VLAN_FLAG_MVRP (0x8) (use of the MVRP protocol). - - The default value of this property is NM_VLAN_FLAG_REORDER_HEADERS, but it used to be 0. To preserve backward compatibility, the default-value in the D-Bus API continues to be 0 and a missing property on D-Bus is still considered as 0. One or more flags which control the behavior and features of the VLAN interface. Flags include NM_VLAN_FLAG_REORDER_HEADERS (0x1) (reordering of output packet headers), NM_VLAN_FLAG_GVRP (0x2) (use of the GVRP protocol), and NM_VLAN_FLAG_LOOSE_BINDING (0x4) (loose binding of the interface to its master device's operating state). NM_VLAN_FLAG_MVRP (0x8) (use of the MVRP protocol). The default value of this property is NM_VLAN_FLAG_REORDER_HEADERS, but it used to be 0. To preserve backward compatibility, the default-value in the D-Bus API continues to be 0 and a missing property on D-Bus is still considered as 0. The VLAN identifier that the interface created by this connection should be assigned. The valid range is from 0 to 4094, without the reserved id 4095. The VLAN identifier that the interface created by this connection should be assigned. The valid range is from 0 to 4094, without the reserved id 4095. For incoming packets, a list of mappings from 802.1p priorities to Linux SKB priorities. The mapping is given in the format "from:to" where both "from" and "to" are unsigned integers, ie "7:3". For incoming packets, a list of mappings from 802.1p priorities to Linux SKB priorities. The mapping is given in the format "from:to" where both "from" and "to" are unsigned integers, ie "7:3". If given, specifies the parent interface name or parent connection UUID from which this VLAN interface should be created. If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property. If given, specifies the parent interface name or parent connection UUID from which this VLAN interface should be created. If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property. Dictionary of key/value pairs of VPN plugin specific data. Both keys and values must be strings. Dictionary of key/value pairs of VPN plugin specific data. Both keys and values must be strings. If the VPN service supports persistence, and this property is TRUE, the VPN will attempt to stay connected across link changes and outages, until explicitly disconnected. If the VPN service supports persistence, and this property is TRUE, the VPN will attempt to stay connected across link changes and outages, until explicitly disconnected. Dictionary of key/value pairs of VPN plugin specific secrets like passwords or private keys. Both keys and values must be strings. Dictionary of key/value pairs of VPN plugin specific secrets like passwords or private keys. Both keys and values must be strings. D-Bus service name of the VPN plugin that this setting uses to connect to its network. i.e. org.freedesktop.NetworkManager.vpnc for the vpnc plugin. D-Bus service name of the VPN plugin that this setting uses to connect to its network. i.e. org.freedesktop.NetworkManager.vpnc for the vpnc plugin. Timeout for the VPN service to establish the connection. Some services may take quite a long time to connect. Value of 0 means a default timeout, which is 60 seconds (unless overridden by vpn.timeout in configuration file). Values greater than zero mean timeout in seconds. Timeout for the VPN service to establish the connection. Some services may take quite a long time to connect. Value of 0 means a default timeout, which is 60 seconds (unless overridden by vpn.timeout in configuration file). Values greater than zero mean timeout in seconds. If the VPN connection requires a user name for authentication, that name should be provided here. If the connection is available to more than one user, and the VPN requires each user to supply a different name, then leave this property empty. If this property is empty, NetworkManager will automatically supply the username of the user which requested the VPN connection. If the VPN connection requires a user name for authentication, that name should be provided here. If the connection is available to more than one user, and the VPN requires each user to supply a different name, then leave this property empty. If this property is empty, NetworkManager will automatically supply the username of the user which requested the VPN connection. The routing table for this VRF. The routing table for this VRF. Specifies the lifetime in seconds of FDB entries learnt by the kernel. Specifies the lifetime in seconds of FDB entries learnt by the kernel. Specifies the UDP destination port to communicate to the remote VXLAN tunnel endpoint. Specifies the UDP destination port to communicate to the remote VXLAN tunnel endpoint. Specifies the VXLAN Network Identifier (or VXLAN Segment Identifier) to use. Specifies the VXLAN Network Identifier (or VXLAN Segment Identifier) to use. Specifies whether netlink LL ADDR miss notifications are generated. Specifies whether netlink LL ADDR miss notifications are generated. Specifies whether netlink IP ADDR miss notifications are generated. Specifies whether netlink IP ADDR miss notifications are generated. Specifies whether unknown source link layer addresses and IP addresses are entered into the VXLAN device forwarding database. Specifies whether unknown source link layer addresses and IP addresses are entered into the VXLAN device forwarding database. Specifies the maximum number of FDB entries. A value of zero means that the kernel will store unlimited entries. Specifies the maximum number of FDB entries. A value of zero means that the kernel will store unlimited entries. If given, specifies the source IP address to use in outgoing packets. If given, specifies the source IP address to use in outgoing packets. If given, specifies the parent interface name or parent connection UUID. If given, specifies the parent interface name or parent connection UUID. Specifies whether ARP proxy is turned on. Specifies whether ARP proxy is turned on. Specifies the unicast destination IP address to use in outgoing packets when the destination link layer address is not known in the VXLAN device forwarding database, or the multicast IP address to join. Specifies the unicast destination IP address to use in outgoing packets when the destination link layer address is not known in the VXLAN device forwarding database, or the multicast IP address to join. Specifies whether route short circuit is turned on. Specifies whether route short circuit is turned on. Specifies the maximum UDP source port to communicate to the remote VXLAN tunnel endpoint. Specifies the maximum UDP source port to communicate to the remote VXLAN tunnel endpoint. Specifies the minimum UDP source port to communicate to the remote VXLAN tunnel endpoint. Specifies the minimum UDP source port to communicate to the remote VXLAN tunnel endpoint. Specifies the TOS value to use in outgoing packets. Specifies the TOS value to use in outgoing packets. Specifies the time-to-live value to use in outgoing packets. Specifies the time-to-live value to use in outgoing packets. The P2P device that should be connected to. Currently, this is the only way to create or join a group. The P2P device that should be connected to. Currently, this is the only way to create or join a group. The Wi-Fi Display (WFD) Information Elements (IEs) to set. - - Wi-Fi Display requires a protocol specific information element to be set in certain Wi-Fi frames. These can be specified here for the purpose of establishing a connection. This setting is only useful when implementing a Wi-Fi Display client. The Wi-Fi Display (WFD) Information Elements (IEs) to set. Wi-Fi Display requires a protocol specific information element to be set in certain Wi-Fi frames. These can be specified here for the purpose of establishing a connection. This setting is only useful when implementing a Wi-Fi Display client. Flags indicating which mode of WPS is to be used. - - There's little point in changing the default setting as NetworkManager will automatically determine the best method to use. Flags indicating which mode of WPS is to be used. There's little point in changing the default setting as NetworkManager will automatically determine the best method to use. If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing). Deprecated: 1 If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing). Deprecated: 1 Network Service Provider (NSP) name of the WiMAX network this connection should use. Deprecated: 1 Network Service Provider (NSP) name of the WiMAX network this connection should use. Deprecated: 1 When TRUE, setup the interface to accept packets for all MAC addresses. This is enabling the kernel interface flag IFF_PROMISC. When FALSE, the interface will only accept the packets with the interface destination mac address or broadcast. When TRUE, setup the interface to accept packets for all MAC addresses. This is enabling the kernel interface flag IFF_PROMISC. When FALSE, the interface will only accept the packets with the interface destination mac address or broadcast. When TRUE, enforce auto-negotiation of speed and duplex mode. If "speed" and "duplex" properties are both specified, only that single mode will be advertised and accepted during the link auto-negotiation process: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabits modes, as in these cases link negotiation is mandatory. When FALSE, "speed" and "duplex" properties should be both set or link configuration will be skipped. When TRUE, enforce auto-negotiation of speed and duplex mode. If "speed" and "duplex" properties are both specified, only that single mode will be advertised and accepted during the link auto-negotiation process: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabits modes, as in these cases link negotiation is mandatory. When FALSE, "speed" and "duplex" properties should be both set or link configuration will be skipped. If specified, request that the device use this MAC address instead. This is known as MAC cloning or spoofing. - - Beside explicitly specifying a MAC address, the special values "preserve", "permanent", "random" and "stable" are supported. "preserve" means not to touch the MAC address on activation. "permanent" means to use the permanent hardware address if the device has one (otherwise this is treated as "preserve"). "random" creates a random MAC address on each connect. "stable" creates a hashed MAC address based on connection.stable-id and a machine dependent key. - - If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to "preserve" (older versions of NetworkManager may use a different default value). - - On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated "cloned-mac-address". If specified, request that the device use this MAC address instead. This is known as MAC cloning or spoofing. Beside explicitly specifying a MAC address, the special values "preserve", "permanent", "random" and "stable" are supported. "preserve" means not to touch the MAC address on activation. "permanent" means to use the permanent hardware address if the device has one (otherwise this is treated as "preserve"). "random" creates a random MAC address on each connect. "stable" creates a hashed MAC address based on connection.stable-id and a machine dependent key. If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to "preserve" (older versions of NetworkManager may use a different default value). On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated "cloned-mac-address". When a value is set, either "half" or "full", configures the device to use the specified duplex mode. If "auto-negotiate" is "yes" the specified duplex mode will be the only one advertised during link negotiation: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabits modes, as in these cases link negotiation is mandatory. If the value is unset (the default), the link configuration will be either skipped (if "auto-negotiate" is "no", the default) or will be auto-negotiated (if "auto-negotiate" is "yes") and the local device will advertise all the supported duplex modes. Must be set together with the "speed" property if specified. Before specifying a duplex mode be sure your device supports it. When a value is set, either "half" or "full", configures the device to use the specified duplex mode. If "auto-negotiate" is "yes" the specified duplex mode will be the only one advertised during link negotiation: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabits modes, as in these cases link negotiation is mandatory. If the value is unset (the default), the link configuration will be either skipped (if "auto-negotiate" is "no", the default) or will be auto-negotiated (if "auto-negotiate" is "yes") and the local device will advertise all the supported duplex modes. Must be set together with the "speed" property if specified. Before specifying a duplex mode be sure your device supports it. With "cloned-mac-address" setting "random" or "stable", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address. - - If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address. - - If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the "random" or "stable" algorithm. - - If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled globally-administered, burned-in MAC address. - - If the value contains more than one additional MAC addresses, one of them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" will create a fully scrambled MAC address, randomly locally or globally administered. With "cloned-mac-address" setting "random" or "stable", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address. If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address. If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the "random" or "stable" algorithm. If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled globally-administered, burned-in MAC address. If the value contains more than one additional MAC addresses, one of them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" will create a fully scrambled MAC address, randomly locally or globally administered. If specified, this connection will only apply to the Ethernet device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing). If specified, this connection will only apply to the Ethernet device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing). If specified, this connection will never apply to the Ethernet device whose permanent MAC address matches an address in the list. Each MAC address is in the standard hex-digits-and-colons notation (00:11:22:33:44:55). If specified, this connection will never apply to the Ethernet device whose permanent MAC address matches an address in the list. Each MAC address is in the standard hex-digits-and-colons notation (00:11:22:33:44:55). If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames. Specific port type to use if the device supports multiple attachment methods. One of "tp" (Twisted Pair), "aui" (Attachment Unit Interface), "bnc" (Thin Ethernet) or "mii" (Media Independent Interface). If the device supports only one port type, this setting is ignored. Specific port type to use if the device supports multiple attachment methods. One of "tp" (Twisted Pair), "aui" (Attachment Unit Interface), "bnc" (Thin Ethernet) or "mii" (Media Independent Interface). If the device supports only one port type, this setting is ignored. s390 network device type; one of "qeth", "lcs", or "ctc", representing the different types of virtual network devices available on s390 systems. s390 network device type; one of "qeth", "lcs", or "ctc", representing the different types of virtual network devices available on s390 systems. Dictionary of key/value pairs of s390-specific device options. Both keys and values must be strings. Allowed keys include "portno", "layer2", "portname", "protocol", among others. Key names must contain only alphanumeric characters (ie, [a-zA-Z0-9]). - - Currently, NetworkManager itself does nothing with this information. However, s390utils ships a udev rule which parses this information and applies it to the interface. Dictionary of key/value pairs of s390-specific device options. Both keys and values must be strings. Allowed keys include "portno", "layer2", "portname", "protocol", among others. Key names must contain only alphanumeric characters (ie, [a-zA-Z0-9]). Currently, NetworkManager itself does nothing with this information. However, s390utils ships a udev rule which parses this information and applies it to the interface. Identifies specific subchannels that this network device uses for communication with z/VM or s390 host. Like the "mac-address" property for non-z/VM devices, this property can be used to ensure this connection only applies to the network device that uses these subchannels. The list should contain exactly 3 strings, and each string may only be composed of hexadecimal characters and the period (.) character. Identifies specific subchannels that this network device uses for communication with z/VM or s390 host. Like the "mac-address" property for non-z/VM devices, this property can be used to ensure this connection only applies to the network device that uses these subchannels. The list should contain exactly 3 strings, and each string may only be composed of hexadecimal characters and the period (.) character. When a value greater than 0 is set, configures the device to use the specified speed. If "auto-negotiate" is "yes" the specified speed will be the only one advertised during link negotiation: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabit speeds, as in this case link negotiation is mandatory. If the value is unset (0, the default), the link configuration will be either skipped (if "auto-negotiate" is "no", the default) or will be auto-negotiated (if "auto-negotiate" is "yes") and the local device will advertise all the supported speeds. In Mbit/s, ie 100 == 100Mbit/s. Must be set together with the "duplex" property when non-zero. Before specifying a speed value be sure your device supports it. When a value greater than 0 is set, configures the device to use the specified speed. If "auto-negotiate" is "yes" the specified speed will be the only one advertised during link negotiation: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabit speeds, as in this case link negotiation is mandatory. If the value is unset (0, the default), the link configuration will be either skipped (if "auto-negotiate" is "no", the default) or will be auto-negotiated (if "auto-negotiate" is "yes") and the local device will advertise all the supported speeds. In Mbit/s, ie 100 == 100Mbit/s. Must be set together with the "duplex" property when non-zero. Before specifying a speed value be sure your device supports it. The NMSettingWiredWakeOnLan options to enable. Not all devices support all options. May be any combination of NM_SETTING_WIRED_WAKE_ON_LAN_PHY (0x2), NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST (0x4), NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST (0x8), NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST (0x10), NM_SETTING_WIRED_WAKE_ON_LAN_ARP (0x20), NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC (0x40) or the special values NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT (0x1) (to use global settings) and NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE (0x8000) (to disable management of Wake-on-LAN in NetworkManager). The NMSettingWiredWakeOnLan options to enable. Not all devices support all options. May be any combination of NM_SETTING_WIRED_WAKE_ON_LAN_PHY (0x2), NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST (0x4), NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST (0x8), NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST (0x10), NM_SETTING_WIRED_WAKE_ON_LAN_ARP (0x20), NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC (0x40) or the special values NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT (0x1) (to use global settings) and NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE (0x8000) (to disable management of Wake-on-LAN in NetworkManager). If specified, the password used with magic-packet-based Wake-on-LAN, represented as an Ethernet MAC address. If NULL, no password will be required. If specified, the password used with magic-packet-based Wake-on-LAN, represented as an Ethernet MAC address. If NULL, no password will be required. The use of fwmark is optional and is by default off. Setting it to 0 disables it. Otherwise, it is a 32-bit fwmark for outgoing packets. - - Note that "ip4-auto-default-route" or "ip6-auto-default-route" enabled, implies to automatically choose a fwmark. The use of fwmark is optional and is by default off. Setting it to 0 disables it. Otherwise, it is a 32-bit fwmark for outgoing packets. Note that "ip4-auto-default-route" or "ip6-auto-default-route" enabled, implies to automatically choose a fwmark. Whether to enable special handling of the IPv4 default route. If enabled, the IPv4 default route from wireguard.peer-routes will be placed to a dedicated routing-table and two policy routing rules will be added. The fwmark number is also used as routing-table for the default-route, and if fwmark is zero, an unused fwmark/table is chosen automatically. This corresponds to what wg-quick does with Table=auto and what WireGuard calls "Improved Rule-based Routing". - - Note that for this automatism to work, you usually don't want to set ipv4.gateway, because that will result in a conflicting default route. - - Leaving this at the default will enable this option automatically if ipv4.never-default is not set and there are any peers that use a default-route as allowed-ips. Since this automatism only makes sense if you also have a peer with an /0 allowed-ips, it is usually not necessary to enable this explicitly. However, you can disable it if you want to configure your own routing and rules. Whether to enable special handling of the IPv4 default route. If enabled, the IPv4 default route from wireguard.peer-routes will be placed to a dedicated routing-table and two policy routing rules will be added. The fwmark number is also used as routing-table for the default-route, and if fwmark is zero, an unused fwmark/table is chosen automatically. This corresponds to what wg-quick does with Table=auto and what WireGuard calls "Improved Rule-based Routing". Note that for this automatism to work, you usually don't want to set ipv4.gateway, because that will result in a conflicting default route. Leaving this at the default will enable this option automatically if ipv4.never-default is not set and there are any peers that use a default-route as allowed-ips. Since this automatism only makes sense if you also have a peer with an /0 allowed-ips, it is usually not necessary to enable this explicitly. However, you can disable it if you want to configure your own routing and rules. Like ip4-auto-default-route, but for the IPv6 default route. Like ip4-auto-default-route, but for the IPv6 default route. The listen-port. If listen-port is not specified, the port will be chosen randomly when the interface comes up. The listen-port. If listen-port is not specified, the port will be chosen randomly when the interface comes up. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments. - - If zero a default MTU is used. Note that contrary to wg-quick's MTU setting, this does not take into account the current routes at the time of activation. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments. If zero a default MTU is used. Note that contrary to wg-quick's MTU setting, this does not take into account the current routes at the time of activation. Whether to automatically add routes for the AllowedIPs ranges of the peers. If TRUE (the default), NetworkManager will automatically add routes in the routing tables according to ipv4.route-table and ipv6.route-table. Usually you want this automatism enabled. If FALSE, no such routes are added automatically. In this case, the user may want to configure static routes in ipv4.routes and ipv6.routes, respectively. - - Note that if the peer's AllowedIPs is "0.0.0.0/0" or "::/0" and the profile's ipv4.never-default or ipv6.never-default setting is enabled, the peer route for this peer won't be added automatically. Whether to automatically add routes for the AllowedIPs ranges of the peers. If TRUE (the default), NetworkManager will automatically add routes in the routing tables according to ipv4.route-table and ipv6.route-table. Usually you want this automatism enabled. If FALSE, no such routes are added automatically. In this case, the user may want to configure static routes in ipv4.routes and ipv6.routes, respectively. Note that if the peer's AllowedIPs is "0.0.0.0/0" or "::/0" and the profile's ipv4.never-default or ipv6.never-default setting is enabled, the peer route for this peer won't be added automatically. The 256 bit private-key in base64 encoding. The 256 bit private-key in base64 encoding. Flags indicating how to handle the "private-key" property. Flags indicating how to handle the "private-key" property. Configures AP isolation, which prevents communication between wireless devices connected to this AP. This property can be set to a value different from NM_TERNARY_DEFAULT (-1) only when the interface is configured in AP mode. - - If set to NM_TERNARY_TRUE (1), devices are not able to communicate with each other. This increases security because it protects devices against attacks from other clients in the network. At the same time, it prevents devices to access resources on the same wireless networks as file shares, printers, etc. - - If set to NM_TERNARY_FALSE (0), devices can talk to each other. - - When set to NM_TERNARY_DEFAULT (-1), the global default is used; in case the global default is unspecified it is assumed to be NM_TERNARY_FALSE (0). Configures AP isolation, which prevents communication between wireless devices connected to this AP. This property can be set to a value different from NM_TERNARY_DEFAULT (-1) only when the interface is configured in AP mode. If set to NM_TERNARY_TRUE (1), devices are not able to communicate with each other. This increases security because it protects devices against attacks from other clients in the network. At the same time, it prevents devices to access resources on the same wireless networks as file shares, printers, etc. If set to NM_TERNARY_FALSE (0), devices can talk to each other. When set to NM_TERNARY_DEFAULT (-1), the global default is used; in case the global default is unspecified it is assumed to be NM_TERNARY_FALSE (0). 802.11 frequency band of the network. One of "a" for 5GHz 802.11a or "bg" for 2.4GHz 802.11. This will lock associations to the Wi-Fi network to the specific band, i.e. if "a" is specified, the device will not associate with the same network in the 2.4GHz band even if the network's settings are compatible. This setting depends on specific driver capability and may not work with all drivers. 802.11 frequency band of the network. One of "a" for 5GHz 802.11a or "bg" for 2.4GHz 802.11. This will lock associations to the Wi-Fi network to the specific band, i.e. if "a" is specified, the device will not associate with the same network in the 2.4GHz band even if the network's settings are compatible. This setting depends on specific driver capability and may not work with all drivers. If specified, directs the device to only associate with the given access point. This capability is highly driver dependent and not supported by all devices. Note: this property does not control the BSSID used when creating an Ad-Hoc network and is unlikely to in the future. - - Locking a client profile to a certain BSSID will prevent roaming and also disable background scanning. That can be useful, if there is only one access point for the SSID. If specified, directs the device to only associate with the given access point. This capability is highly driver dependent and not supported by all devices. Note: this property does not control the BSSID used when creating an Ad-Hoc network and is unlikely to in the future. Locking a client profile to a certain BSSID will prevent roaming and also disable background scanning. That can be useful, if there is only one access point for the SSID. Wireless channel to use for the Wi-Fi connection. The device will only join (or create for Ad-Hoc networks) a Wi-Fi network on the specified channel. Because channel numbers overlap between bands, this property also requires the "band" property to be set. Wireless channel to use for the Wi-Fi connection. The device will only join (or create for Ad-Hoc networks) a Wi-Fi network on the specified channel. Because channel numbers overlap between bands, this property also requires the "band" property to be set. If specified, request that the device use this MAC address instead. This is known as MAC cloning or spoofing. - - Beside explicitly specifying a MAC address, the special values "preserve", "permanent", "random" and "stable" are supported. "preserve" means not to touch the MAC address on activation. "permanent" means to use the permanent hardware address of the device. "random" creates a random MAC address on each connect. "stable" creates a hashed MAC address based on connection.stable-id and a machine dependent key. - - If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to "preserve" (older versions of NetworkManager may use a different default value). - - On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated "cloned-mac-address". If specified, request that the device use this MAC address instead. This is known as MAC cloning or spoofing. Beside explicitly specifying a MAC address, the special values "preserve", "permanent", "random" and "stable" are supported. "preserve" means not to touch the MAC address on activation. "permanent" means to use the permanent hardware address of the device. "random" creates a random MAC address on each connect. "stable" creates a hashed MAC address based on connection.stable-id and a machine dependent key. If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to "preserve" (older versions of NetworkManager may use a different default value). On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated "cloned-mac-address". With "cloned-mac-address" setting "random" or "stable", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address. - - If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address. - - If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the "random" or "stable" algorithm. - - If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled globally-administered, burned-in MAC address. - - If the value contains more than one additional MAC addresses, one of them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" will create a fully scrambled MAC address, randomly locally or globally administered. With "cloned-mac-address" setting "random" or "stable", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address. If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address. If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the "random" or "stable" algorithm. If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled globally-administered, burned-in MAC address. If the value contains more than one additional MAC addresses, one of them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" will create a fully scrambled MAC address, randomly locally or globally administered. If TRUE, indicates that the network is a non-broadcasting network that hides its SSID. This works both in infrastructure and AP mode. - - In infrastructure mode, various workarounds are used for a more reliable discovery of hidden networks, such as probe-scanning the SSID. However, these workarounds expose inherent insecurities with hidden SSID networks, and thus hidden SSID networks should be used with caution. - - In AP mode, the created network does not broadcast its SSID. - - Note that marking the network as hidden may be a privacy issue for you (in infrastructure mode) or client stations (in AP mode), as the explicit probe-scans are distinctly recognizable on the air. If TRUE, indicates that the network is a non-broadcasting network that hides its SSID. This works both in infrastructure and AP mode. In infrastructure mode, various workarounds are used for a more reliable discovery of hidden networks, such as probe-scanning the SSID. However, these workarounds expose inherent insecurities with hidden SSID networks, and thus hidden SSID networks should be used with caution. In AP mode, the created network does not broadcast its SSID. Note that marking the network as hidden may be a privacy issue for you (in infrastructure mode) or client stations (in AP mode), as the explicit probe-scans are distinctly recognizable on the air. If specified, this connection will only apply to the Wi-Fi device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing). If specified, this connection will only apply to the Wi-Fi device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing). A list of permanent MAC addresses of Wi-Fi devices to which this connection should never apply. Each MAC address should be given in the standard hex-digits-and-colons notation (eg "00:11:22:33:44:55"). A list of permanent MAC addresses of Wi-Fi devices to which this connection should never apply. Each MAC address should be given in the standard hex-digits-and-colons notation (eg "00:11:22:33:44:55"). One of NM_SETTING_MAC_RANDOMIZATION_DEFAULT (0) (never randomize unless the user has set a global default to randomize and the supplicant supports randomization), NM_SETTING_MAC_RANDOMIZATION_NEVER (1) (never randomize the MAC address), or NM_SETTING_MAC_RANDOMIZATION_ALWAYS (2) (always randomize the MAC address). This property is deprecated for 'cloned-mac-address'. Deprecated: 1 One of NM_SETTING_MAC_RANDOMIZATION_DEFAULT (0) (never randomize unless the user has set a global default to randomize and the supplicant supports randomization), NM_SETTING_MAC_RANDOMIZATION_NEVER (1) (never randomize the MAC address), or NM_SETTING_MAC_RANDOMIZATION_ALWAYS (2) (always randomize the MAC address). This property is deprecated for 'cloned-mac-address'. Deprecated: 1 Wi-Fi network mode; one of "infrastructure", "mesh", "adhoc" or "ap". If blank, infrastructure is assumed. Wi-Fi network mode; one of "infrastructure", "mesh", "adhoc" or "ap". If blank, infrastructure is assumed. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames. One of NM_SETTING_WIRELESS_POWERSAVE_DISABLE (2) (disable Wi-Fi power saving), NM_SETTING_WIRELESS_POWERSAVE_ENABLE (3) (enable Wi-Fi power saving), NM_SETTING_WIRELESS_POWERSAVE_IGNORE (1) (don't touch currently configure setting) or NM_SETTING_WIRELESS_POWERSAVE_DEFAULT (0) (use the globally configured value). All other values are reserved. One of NM_SETTING_WIRELESS_POWERSAVE_DISABLE (2) (disable Wi-Fi power saving), NM_SETTING_WIRELESS_POWERSAVE_ENABLE (3) (enable Wi-Fi power saving), NM_SETTING_WIRELESS_POWERSAVE_IGNORE (1) (don't touch currently configure setting) or NM_SETTING_WIRELESS_POWERSAVE_DEFAULT (0) (use the globally configured value). All other values are reserved. If non-zero, directs the device to only use the specified bitrate for communication with the access point. Units are in Kb/s, ie 5500 = 5.5 Mbit/s. This property is highly driver dependent and not all devices support setting a static bitrate. If non-zero, directs the device to only use the specified bitrate for communication with the access point. Units are in Kb/s, ie 5500 = 5.5 Mbit/s. This property is highly driver dependent and not all devices support setting a static bitrate. A list of BSSIDs (each BSSID formatted as a MAC address like "00:11:22:33:44:55") that have been detected as part of the Wi-Fi network. NetworkManager internally tracks previously seen BSSIDs. The property is only meant for reading and reflects the BSSID list of NetworkManager. The changes you make to this property will not be preserved. A list of BSSIDs (each BSSID formatted as a MAC address like "00:11:22:33:44:55") that have been detected as part of the Wi-Fi network. NetworkManager internally tracks previously seen BSSIDs. The property is only meant for reading and reflects the BSSID list of NetworkManager. The changes you make to this property will not be preserved. SSID of the Wi-Fi network. Must be specified. SSID of the Wi-Fi network. Must be specified. If non-zero, directs the device to use the specified transmit power. Units are dBm. This property is highly driver dependent and not all devices support setting a static transmit power. If non-zero, directs the device to use the specified transmit power. Units are dBm. This property is highly driver dependent and not all devices support setting a static transmit power. The NMSettingWirelessWakeOnWLan options to enable. Not all devices support all options. May be any combination of NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY (0x2), NM_SETTING_WIRELESS_WAKE_ON_WLAN_DISCONNECT (0x4), NM_SETTING_WIRELESS_WAKE_ON_WLAN_MAGIC (0x8), NM_SETTING_WIRELESS_WAKE_ON_WLAN_GTK_REKEY_FAILURE (0x10), NM_SETTING_WIRELESS_WAKE_ON_WLAN_EAP_IDENTITY_REQUEST (0x20), NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE (0x40), NM_SETTING_WIRELESS_WAKE_ON_WLAN_RFKILL_RELEASE (0x80), NM_SETTING_WIRELESS_WAKE_ON_WLAN_TCP (0x100) or the special values NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT (0x1) (to use global settings) and NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE (0x8000) (to disable management of Wake-on-LAN in NetworkManager). The NMSettingWirelessWakeOnWLan options to enable. Not all devices support all options. May be any combination of NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY (0x2), NM_SETTING_WIRELESS_WAKE_ON_WLAN_DISCONNECT (0x4), NM_SETTING_WIRELESS_WAKE_ON_WLAN_MAGIC (0x8), NM_SETTING_WIRELESS_WAKE_ON_WLAN_GTK_REKEY_FAILURE (0x10), NM_SETTING_WIRELESS_WAKE_ON_WLAN_EAP_IDENTITY_REQUEST (0x20), NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE (0x40), NM_SETTING_WIRELESS_WAKE_ON_WLAN_RFKILL_RELEASE (0x80), NM_SETTING_WIRELESS_WAKE_ON_WLAN_TCP (0x100) or the special values NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT (0x1) (to use global settings) and NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE (0x8000) (to disable management of Wake-on-LAN in NetworkManager). When WEP is used (ie, key-mgmt = "none" or "ieee8021x") indicate the 802.11 authentication algorithm required by the AP here. One of "open" for Open System, "shared" for Shared Key, or "leap" for Cisco LEAP. When using Cisco LEAP (ie, key-mgmt = "ieee8021x" and auth-alg = "leap") the "leap-username" and "leap-password" properties must be specified. When WEP is used (ie, key-mgmt = "none" or "ieee8021x") indicate the 802.11 authentication algorithm required by the AP here. One of "open" for Open System, "shared" for Shared Key, or "leap" for Cisco LEAP. When using Cisco LEAP (ie, key-mgmt = "ieee8021x" and auth-alg = "leap") the "leap-username" and "leap-password" properties must be specified. Indicates whether Fast Initial Link Setup (802.11ai) must be enabled for the connection. One of NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) (use global default value), NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE (1) (disable FILS), NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL (2) (enable FILS if the supplicant and the access point support it) or NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED (3) (enable FILS and fail if not supported). When set to NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) and no global default is set, FILS will be optionally enabled. Indicates whether Fast Initial Link Setup (802.11ai) must be enabled for the connection. One of NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) (use global default value), NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE (1) (disable FILS), NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL (2) (enable FILS if the supplicant and the access point support it) or NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED (3) (enable FILS and fail if not supported). When set to NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) and no global default is set, FILS will be optionally enabled. A list of group/broadcast encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list. For maximum compatibility leave this property empty. Each list element may be one of "wep40", "wep104", "tkip", or "ccmp". A list of group/broadcast encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list. For maximum compatibility leave this property empty. Each list element may be one of "wep40", "wep104", "tkip", or "ccmp". Key management used for the connection. One of "none" (WEP or no password protection), "ieee8021x" (Dynamic WEP), "owe" (Opportunistic Wireless Encryption), "wpa-psk" (WPA2 + WPA3 personal), "sae" (WPA3 personal only), "wpa-eap" (WPA2 + WPA3 enterprise) or "wpa-eap-suite-b-192" (WPA3 enterprise only). - - This property must be set for any Wi-Fi connection that uses security. Key management used for the connection. One of "none" (WEP or no password protection), "ieee8021x" (Dynamic WEP), "owe" (Opportunistic Wireless Encryption), "wpa-psk" (WPA2 + WPA3 personal), "sae" (WPA3 personal only), "wpa-eap" (WPA2 + WPA3 enterprise) or "wpa-eap-suite-b-192" (WPA3 enterprise only). This property must be set for any Wi-Fi connection that uses security. The login password for legacy LEAP connections (ie, key-mgmt = "ieee8021x" and auth-alg = "leap"). The login password for legacy LEAP connections (ie, key-mgmt = "ieee8021x" and auth-alg = "leap"). Flags indicating how to handle the "leap-password" property. Flags indicating how to handle the "leap-password" property. The login username for legacy LEAP connections (ie, key-mgmt = "ieee8021x" and auth-alg = "leap"). The login username for legacy LEAP connections (ie, key-mgmt = "ieee8021x" and auth-alg = "leap"). A list of pairwise encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list. For maximum compatibility leave this property empty. Each list element may be one of "tkip" or "ccmp". A list of pairwise encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list. For maximum compatibility leave this property empty. Each list element may be one of "tkip" or "ccmp". Indicates whether Protected Management Frames (802.11w) must be enabled for the connection. One of NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) (use global default value), NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE (1) (disable PMF), NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL (2) (enable PMF if the supplicant and the access point support it) or NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED (3) (enable PMF and fail if not supported). When set to NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) and no global default is set, PMF will be optionally enabled. Indicates whether Protected Management Frames (802.11w) must be enabled for the connection. One of NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) (use global default value), NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE (1) (disable PMF), NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL (2) (enable PMF if the supplicant and the access point support it) or NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED (3) (enable PMF and fail if not supported). When set to NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) and no global default is set, PMF will be optionally enabled. List of strings specifying the allowed WPA protocol versions to use. Each element may be one "wpa" (allow WPA) or "rsn" (allow WPA2/RSN). If not specified, both WPA and RSN connections are allowed. List of strings specifying the allowed WPA protocol versions to use. Each element may be one "wpa" (allow WPA) or "rsn" (allow WPA2/RSN). If not specified, both WPA and RSN connections are allowed. Pre-Shared-Key for WPA networks. For WPA-PSK, it's either an ASCII passphrase of 8 to 63 characters that is (as specified in the 802.11i standard) hashed to derive the actual key, or the key in form of 64 hexadecimal character. The WPA3-Personal networks use a passphrase of any length for SAE authentication. Pre-Shared-Key for WPA networks. For WPA-PSK, it's either an ASCII passphrase of 8 to 63 characters that is (as specified in the 802.11i standard) hashed to derive the actual key, or the key in form of 64 hexadecimal character. The WPA3-Personal networks use a passphrase of any length for SAE authentication. Flags indicating how to handle the "psk" property. Flags indicating how to handle the "psk" property. Flags indicating how to handle the "wep-key0", "wep-key1", "wep-key2", and "wep-key3" properties. Flags indicating how to handle the "wep-key0", "wep-key1", "wep-key2", and "wep-key3" properties. Controls the interpretation of WEP keys. Allowed values are NM_WEP_KEY_TYPE_KEY (1), in which case the key is either a 10- or 26-character hexadecimal string, or a 5- or 13-character ASCII password; or NM_WEP_KEY_TYPE_PASSPHRASE (2), in which case the passphrase is provided as a string and will be hashed using the de-facto MD5 method to derive the actual WEP key. Controls the interpretation of WEP keys. Allowed values are NM_WEP_KEY_TYPE_KEY (1), in which case the key is either a 10- or 26-character hexadecimal string, or a 5- or 13-character ASCII password; or NM_WEP_KEY_TYPE_PASSPHRASE (2), in which case the passphrase is provided as a string and will be hashed using the de-facto MD5 method to derive the actual WEP key. Index 0 WEP key. This is the WEP key used in most networks. See the "wep-key-type" property for a description of how this key is interpreted. Index 0 WEP key. This is the WEP key used in most networks. See the "wep-key-type" property for a description of how this key is interpreted. Index 1 WEP key. This WEP index is not used by most networks. See the "wep-key-type" property for a description of how this key is interpreted. Index 1 WEP key. This WEP index is not used by most networks. See the "wep-key-type" property for a description of how this key is interpreted. Index 2 WEP key. This WEP index is not used by most networks. See the "wep-key-type" property for a description of how this key is interpreted. Index 2 WEP key. This WEP index is not used by most networks. See the "wep-key-type" property for a description of how this key is interpreted. Index 3 WEP key. This WEP index is not used by most networks. See the "wep-key-type" property for a description of how this key is interpreted. Index 3 WEP key. This WEP index is not used by most networks. See the "wep-key-type" property for a description of how this key is interpreted. When static WEP is used (ie, key-mgmt = "none") and a non-default WEP key index is used by the AP, put that WEP key index here. Valid values are 0 (default key) through 3. Note that some consumer access points (like the Linksys WRT54G) number the keys 1 - 4. When static WEP is used (ie, key-mgmt = "none") and a non-default WEP key index is used by the AP, put that WEP key index here. Valid values are 0 (default key) through 3. Note that some consumer access points (like the Linksys WRT54G) number the keys 1 - 4. Flags indicating which mode of WPS is to be used if any. - - There's little point in changing the default setting as NetworkManager will automatically determine whether it's feasible to start WPS enrollment from the Access Point capabilities. - - WPS can be disabled by setting this property to a value of 1. Flags indicating which mode of WPS is to be used if any. There's little point in changing the default setting as NetworkManager will automatically determine whether it's feasible to start WPS enrollment from the Access Point capabilities. WPS can be disabled by setting this property to a value of 1. IEEE 802.15.4 channel. A positive integer or -1, meaning "do not set, use whatever the device is already set to". IEEE 802.15.4 channel. A positive integer or -1, meaning "do not set, use whatever the device is already set to". If specified, this connection will only apply to the IEEE 802.15.4 (WPAN) MAC layer device whose permanent MAC address matches. If specified, this connection will only apply to the IEEE 802.15.4 (WPAN) MAC layer device whose permanent MAC address matches. IEEE 802.15.4 channel page. A positive integer or -1, meaning "do not set, use whatever the device is already set to". IEEE 802.15.4 channel page. A positive integer or -1, meaning "do not set, use whatever the device is already set to". IEEE 802.15.4 Personal Area Network (PAN) identifier. IEEE 802.15.4 Personal Area Network (PAN) identifier. Short IEEE 802.15.4 address to be used within a restricted environment. Short IEEE 802.15.4 address to be used within a restricted environment. \ No newline at end of file diff --git a/src/libnm-client-impl/tests/test-libnm.c b/src/libnm-client-impl/tests/test-libnm.c index 1810e139..456f1855 100644 --- a/src/libnm-client-impl/tests/test-libnm.c +++ b/src/libnm-client-impl/tests/test-libnm.c @@ -3099,7 +3099,7 @@ check_dbus_properties: const struct { const char *dbus_type; GType default_gtype; - } * p_expected_type, *p_expected_type_2, + } *p_expected_type, *p_expected_type_2, expected_types[] = { {"b", G_TYPE_BOOLEAN}, {"q", G_TYPE_UINT}, {"y", G_TYPE_UCHAR}, {"i", G_TYPE_INT}, diff --git a/src/libnm-client-impl/tests/test-nm-client.c b/src/libnm-client-impl/tests/test-nm-client.c index a43e98cd..216e3e42 100644 --- a/src/libnm-client-impl/tests/test-nm-client.c +++ b/src/libnm-client-impl/tests/test-nm-client.c @@ -8,7 +8,10 @@ #include #include +#include "libnm-client-impl/nm-dbus-helpers.h" + #include "libnm-client-test/nm-test-libnm-utils.h" +#include "libnm-glib-aux/nm-time-utils.h" static struct { GMainLoop *loop; @@ -495,7 +498,8 @@ nm_running_changed(GObject *client, GParamSpec *pspec, gpointer user_data) { int *running_changed = user_data; - (*running_changed)++; + if (running_changed) + (*running_changed)++; g_main_loop_quit(gl.loop); } @@ -788,50 +792,15 @@ activate_cb(GObject *object, GAsyncResult *result, gpointer user_data) g_main_loop_quit(info->loop); } -static void -_dev_eth0_1_state_changed_cb(NMDevice *device, - NMDeviceState new_state, - NMDeviceState old_state, - NMDeviceStateReason reason, - int *p_count_call) +static NMClient * +_activate_virtual(NMTstcServiceInfo *sinfo) { - const GPtrArray *arr; - - g_assert(p_count_call); - g_assert_cmpint(*p_count_call, ==, 0); - - (*p_count_call)++; - - g_assert(NM_IS_DEVICE_VLAN(device)); - - g_assert_cmpint(old_state, >=, NM_DEVICE_STATE_PREPARE); - g_assert_cmpint(old_state, <=, NM_DEVICE_STATE_ACTIVATED); - g_assert_cmpint(new_state, ==, NM_DEVICE_STATE_UNKNOWN); - - arr = nm_device_get_available_connections(device); - g_assert(arr); - g_assert_cmpint(arr->len, ==, 0); - - g_assert(!nm_device_get_active_connection(device)); -} - -static void -test_activate_virtual(void) -{ - nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; - gs_unref_object NMClient *client = NULL; - NMConnection *conn; - NMSettingConnection *s_con; - NMSettingVlan *s_vlan; - TestACInfo info = {gl.loop, NULL, 0}; - TestConnectionInfo conn_info = {gl.loop, NULL}; - - if (nmtst_test_skip_slow()) - return; - - sinfo = nmtstc_service_init(); - if (!nmtstc_service_available(sinfo)) - return; + NMClient *client = NULL; + NMConnection *conn; + NMSettingConnection *s_con; + NMSettingVlan *s_vlan; + TestACInfo info = {gl.loop, NULL, 0}; + TestConnectionInfo conn_info = {gl.loop, NULL}; client = nmtstc_client_new(TRUE); @@ -878,55 +847,89 @@ test_activate_virtual(void) nm_clear_g_signal_handler(info.device, &info.ac_signal_id); } - if (nmtst_get_rand_bool()) { - /* OK, enough for this run. Let's see whether we can tear down - * successfully at this point. */ + return client; +} + +static void +test_activate_virtual(void) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + + if (nmtst_test_skip_slow()) return; - } - { - NMDevice *dev_eth0_1; - NMActiveConnection *ac; - const GPtrArray *arr; - gulong sig_id; - int call_count = 0; - gboolean take_ref = nmtst_get_rand_bool(); + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; - /* ensure we got all the necessary events in place. */ - nmtst_main_loop_run(gl.loop, 50); + g_object_unref(_activate_virtual(sinfo)); +} - dev_eth0_1 = nm_client_get_device_by_iface(client, "eth0.1"); - g_assert(NM_IS_DEVICE_VLAN(dev_eth0_1)); - if (take_ref) - g_object_ref(dev_eth0_1); +static void +_client_dev_removed(NMClient *client, NMDevice *device, int *p_count_call) +{ + (*p_count_call)++; +} - arr = nm_device_get_available_connections(dev_eth0_1); - g_assert(arr); - g_assert_cmpint(arr->len, ==, 1); +static void +test_activate_virtual_teardown(gconstpointer user_data) +{ + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + gs_unref_object NMClient *client = NULL; + NMDevice *dev_eth0_1; + NMActiveConnection *ac; + const GPtrArray *arr; + int call_count = 0; + gboolean take_ref = nmtst_get_rand_bool(); + gboolean teardown_service = GPOINTER_TO_INT(user_data); - ac = nm_device_get_active_connection(dev_eth0_1); - g_assert(NM_IS_ACTIVE_CONNECTION(ac)); + if (nmtst_test_skip_slow()) + return; - sig_id = g_signal_connect(dev_eth0_1, - "state-changed", - G_CALLBACK(_dev_eth0_1_state_changed_cb), - &call_count); + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; - g_clear_object(&client); + client = _activate_virtual(sinfo); - g_assert_cmpint(call_count, ==, 1); + /* ensure we got all the necessary events in place. */ + nmtst_main_loop_run(gl.loop, 50); - if (take_ref) { - arr = nm_device_get_available_connections(dev_eth0_1); - g_assert(arr); - g_assert_cmpint(arr->len, ==, 0); + dev_eth0_1 = nm_client_get_device_by_iface(client, "eth0.1"); + g_assert(NM_IS_DEVICE_VLAN(dev_eth0_1)); + if (take_ref) + g_object_ref(dev_eth0_1); - g_assert(!nm_device_get_active_connection(dev_eth0_1)); + arr = nm_device_get_available_connections(dev_eth0_1); + g_assert(arr); + g_assert_cmpint(arr->len, ==, 1); - nm_clear_g_signal_handler(dev_eth0_1, &sig_id); + ac = nm_device_get_active_connection(dev_eth0_1); + g_assert(NM_IS_ACTIVE_CONNECTION(ac)); - g_object_unref(dev_eth0_1); - } + g_signal_connect(client, "device-removed", G_CALLBACK(_client_dev_removed), &call_count); + + if (teardown_service) { + g_signal_connect(client, + "notify::" NM_CLIENT_NM_RUNNING, + G_CALLBACK(nm_running_changed), + NULL); + nm_clear_pointer(&sinfo, nmtstc_service_cleanup); + nmtst_main_loop_run(gl.loop, 1000); + g_assert_cmpint(call_count, ==, 2); + } else { + g_clear_object(&client); + g_assert_cmpint(call_count, ==, 0); + } + + if (take_ref) { + arr = nm_device_get_available_connections(dev_eth0_1); + g_assert(arr); + g_assert_cmpint(arr->len, ==, 0); + + g_assert(!nm_device_get_active_connection(dev_eth0_1)); + + g_object_unref(dev_eth0_1); } } @@ -1349,6 +1352,240 @@ test_connection_invalid(void) /*****************************************************************************/ +typedef struct { + NMClient *nmc; + NMOptionBool completed; +} WaitShutdownNmcInitData; + +static void +_wait_shutdown_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + gs_free_error GError *error = NULL; + gboolean b; + guint *p_pending_ops = user_data; + + g_assert(!source); + + if (nmtst_get_rand_bool()) { + b = nm_client_wait_shutdown_finish(result, &error); + g_assert(b == !error); + } + + g_assert_cmpint(*p_pending_ops, >, 0); + (*p_pending_ops)--; +} + +static void +_wait_shutdown_nmc_init_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + WaitShutdownNmcInitData *data = user_data; + gs_free_error GError *error = NULL; + gboolean b; + + g_assert(data->nmc == NM_CLIENT(source)); + g_assert(data->completed == NM_OPTION_BOOL_DEFAULT); + + b = g_async_initable_init_finish(G_ASYNC_INITABLE(source), result, &error); + nmtst_assert_success(b, error); + g_assert(b == TRUE); + + g_assert(NM_FLAGS_HAS(nm_client_get_instance_flags(data->nmc), + NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_GOOD)); + + data->completed = TRUE; +} + +static void +test_client_wait_shutdown(void) +{ + gs_unref_ptrarray GPtrArray *contexts = + g_ptr_array_new_with_free_func((GDestroyNotify) g_main_context_unref); + nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = NULL; + int i_run; + const int N_RUN = 50; + gs_free_error GError *error = NULL; + gs_unref_object GDBusConnection *dbus_connection = NULL; + guint pending_ops = 0; + int i; + gint64 until_msec; + + /* The test creates NMClient instances in various ways (with different + * context of g_main_context_default()) and with sync/asnyc/no initialization. + * + * Then it calls nm_client_wait_shutdown() and checks that all callbacks + * got invoked. + * + * You can also manually bump N_RUN and convince yourself that there are no + * leaks. + */ + + sinfo = nmtstc_service_init(); + if (!nmtstc_service_available(sinfo)) + return; + + /* Have 5 contexts for creating clients (+ the default one). */ + g_ptr_array_add(contexts, g_main_context_ref(g_main_context_default())); + for (i = 0; i < 5; i++) + g_ptr_array_add(contexts, g_main_context_new()); + + dbus_connection = g_bus_get_sync(_nm_dbus_bus_type(), NULL, &error); + nmtst_assert_success(dbus_connection, error); + + for (i_run = 0; i_run < N_RUN; i_run++) { + gs_unref_object GCancellable *init_cancellable = g_cancellable_new(); + gs_unref_object NMClient *nmc = NULL; + nm_auto_pop_gmaincontext GMainContext *client_context = NULL; + gboolean b; + gboolean context_integrated = FALSE; + gs_unref_object GCancellable *cancellable_1 = NULL; + GMainContext *ctx; + + /* Choose a random context for the client. */ + ctx = contexts->pdata[nmtst_get_rand_uint32() % contexts->len]; + if (ctx == g_main_context_default()) { + /* don't use the main context explicitly. */ + } else if (!g_main_context_acquire(ctx)) { + /* the context is in use, we cannot use it. */ + } else { + g_main_context_release(ctx); + client_context = g_main_context_ref(ctx); + g_main_context_push_thread_default(client_context); + } + + nmc = g_object_new( + NM_TYPE_CLIENT, + NM_CLIENT_INSTANCE_FLAGS, + nmtst_get_rand_one_case_in(5) ? NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS : 0, + NM_CLIENT_DBUS_CONNECTION, + nmtst_get_rand_one_case_in(3) ? NULL : dbus_connection, + NULL); + + if (nmtst_get_rand_bool()) { + /* randomly already pop the context before initializing nmc. */ + nm_clear_pointer(&client_context, nm_g_main_context_pop_and_unref); + } + + if (nmtst_get_rand_bool()) { + nm_auto_pop_gmaincontext GMainContext *context = + nm_g_main_context_push_thread_default(NULL); + + /* It is also allowed to start waiting before initialization started. Randomly do that. */ + pending_ops++; + nm_client_wait_shutdown( + nmc, + nmtst_true_once(&context_integrated, nmtst_get_rand_bool()), + (cancellable_1 = nmtst_get_rand_bool() ? g_cancellable_new() : NULL), + _wait_shutdown_cb, + &pending_ops); + } + + /* randomly do some initialization... */ + switch (nmtst_get_rand_uint32() % 4) { + case 0: + /* Don't start initialization. */ + break; + case 1: + /* Do sync initialization. */ + b = g_initable_init(G_INITABLE(nmc), + nmtst_get_rand_bool() ? init_cancellable : NULL, + &error); + nmtst_assert_success(b, error); + break; + case 2: + /* Start async initialization in the background, but don't wait for it complete. */ + g_async_initable_init_async(G_ASYNC_INITABLE(nmc), + G_PRIORITY_DEFAULT, + nmtst_get_rand_bool() ? init_cancellable : NULL, + NULL, + NULL); + break; + default: + { + /* Do async initialization, and iterate the main context until done. */ + WaitShutdownNmcInitData data = { + .completed = NM_OPTION_BOOL_DEFAULT, + .nmc = nmc, + }; + + g_async_initable_init_async(G_ASYNC_INITABLE(nmc), + G_PRIORITY_DEFAULT, + nmtst_get_rand_bool() ? init_cancellable : NULL, + _wait_shutdown_nmc_init_cb, + &data); + + while (data.completed != TRUE) { + g_main_context_iteration(context_integrated ? g_main_context_default() + : nm_client_get_main_context(nmc), + TRUE); + } + break; + } + } + + if (!context_integrated && nmtst_get_rand_bool()) { + nm_clear_g_cancellable(&cancellable_1); + } + + if (nmtst_get_rand_bool()) + nm_clear_pointer(&client_context, nm_g_main_context_pop_and_unref); + + { + gs_unref_object GCancellable *cancellable = NULL; + + pending_ops++; + nm_client_wait_shutdown( + nmc, + nmtst_true_once(&context_integrated, nmtst_get_rand_bool()), + (cancellable = nmtst_get_rand_bool() ? g_cancellable_new() : NULL), + _wait_shutdown_cb, + &pending_ops); + } + + if (nmtst_get_rand_bool()) + g_clear_object(&nmc); + + for (i = 0; i < (int) contexts->len; i++) { + GMainContext *context = contexts->pdata[i]; + + if (nmtst_get_rand_bool()) { + /* iterate next time. */ + continue; + } + + if (!g_main_context_acquire(context)) { + /* It's integrated, skip. */ + continue; + } + g_main_context_release(context); + + while (g_main_context_iteration(context, FALSE)) { + /* pass */ + } + } + } + + until_msec = nm_utils_get_monotonic_timestamp_msec() + 10000; + while (pending_ops > 0 && nm_utils_get_monotonic_timestamp_msec() < until_msec) { + for (i = contexts->len; i > 0; i--) { + GMainContext *context = contexts->pdata[i - 1]; + + if (!g_main_context_acquire(context)) { + /* integrated, skip. */ + continue; + } + g_main_context_release(context); + + while (g_main_context_iteration(context, FALSE)) { + /* pass */ + } + } + } + + g_assert_cmpint(pending_ops, ==, 0); +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -1366,9 +1603,16 @@ main(int argc, char **argv) g_test_add_func("/libnm/devices-array", test_devices_array); g_test_add_func("/libnm/client-nm-running", test_client_nm_running); g_test_add_func("/libnm/active-connections", test_active_connections); - g_test_add_func("/libnm/activate-virtual", test_activate_virtual); + g_test_add_func("/libnm/activate-virtual/without-teardown", test_activate_virtual); + g_test_add_data_func("/libnm/activate-virtual/with-teardown/service", + GINT_TO_POINTER(true), + test_activate_virtual_teardown); + g_test_add_data_func("/libnm/activate-virtual/with-teardown/client", + GINT_TO_POINTER(false), + test_activate_virtual_teardown); g_test_add_func("/libnm/device-connection-compatibility", test_device_connection_compatibility); g_test_add_func("/libnm/connection/invalid", test_connection_invalid); + g_test_add_func("/libnm/test_client_wait_shutdown", test_client_wait_shutdown); return g_test_run(); } diff --git a/src/libnm-client-public/NetworkManager.h b/src/libnm-client-public/NetworkManager.h index 1cd94d11..da661db9 100644 --- a/src/libnm-client-public/NetworkManager.h +++ b/src/libnm-client-public/NetworkManager.h @@ -15,6 +15,8 @@ #include "nm-core-enum-types.h" +#include "nm-errors.h" + #include "nm-connection.h" #include "nm-simple-connection.h" #include "nm-keyfile.h" @@ -42,6 +44,7 @@ #include "nm-setting-ip6-config.h" #include "nm-setting-ip-config.h" #include "nm-setting-ip-tunnel.h" +#include "nm-setting-loopback.h" #include "nm-setting-macsec.h" #include "nm-setting-macvlan.h" #include "nm-setting-match.h" @@ -109,6 +112,7 @@ #include "nm-device-generic.h" #include "nm-device-infiniband.h" #include "nm-device-ip-tunnel.h" +#include "nm-device-loopback.h" #include "nm-device-macsec.h" #include "nm-device-macvlan.h" #include "nm-device-modem.h" @@ -120,6 +124,7 @@ #include "nm-device-tun.h" #include "nm-device-veth.h" #include "nm-device-vlan.h" +#include "nm-device-vrf.h" #include "nm-device-vxlan.h" #include "nm-device-wifi.h" #include "nm-device-wifi-p2p.h" diff --git a/src/libnm-client-public/meson.build b/src/libnm-client-public/meson.build index 5232bcb4..bac7e457 100644 --- a/src/libnm-client-public/meson.build +++ b/src/libnm-client-public/meson.build @@ -20,6 +20,7 @@ libnm_client_headers = files( 'nm-device-generic.h', 'nm-device-infiniband.h', 'nm-device-ip-tunnel.h', + 'nm-device-loopback.h', 'nm-device-macsec.h', 'nm-device-macvlan.h', 'nm-device-modem.h', diff --git a/src/libnm-client-public/nm-autoptr.h b/src/libnm-client-public/nm-autoptr.h index 6c34946c..31ae2cce 100644 --- a/src/libnm-client-public/nm-autoptr.h +++ b/src/libnm-client-public/nm-autoptr.h @@ -43,6 +43,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceEthernet, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceGeneric, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceIPTunnel, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceInfiniband, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceLoopback, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceMacsec, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceMacvlan, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceModem, g_object_unref) @@ -82,6 +83,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingIP6Config, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingIPConfig, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingIPTunnel, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingInfiniband, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingLoopback, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingMacsec, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingMacvlan, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingMatch, g_object_unref) diff --git a/src/libnm-client-public/nm-client.h b/src/libnm-client-public/nm-client.h index da5e509e..9d54827e 100644 --- a/src/libnm-client-public/nm-client.h +++ b/src/libnm-client-public/nm-client.h @@ -22,12 +22,21 @@ G_BEGIN_DECLS * can be disabled. You can toggle this flag to enable and disable automatic * fetching of the permissions. Watch also nm_client_get_permissions_state() * to know whether the permissions are up to date. + * @NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_GOOD: as #NMClient is an GInitable + * and GAsyncInitable, nm_client_get_instance_flags() returns this flag + * once initialization completed with success. This flag cannot be set + * as NM_CLIENT_INSTANCE_FLAGS property. Since: 1.42. + * @NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_BAD: like @NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_GOOD + * indicates that the instance completed initialization with failure. In that + * case the instance is unusable. Since: 1.42. * * Since: 1.24 */ typedef enum /*< flags >*/ { NM_CLIENT_INSTANCE_FLAGS_NONE = 0, - NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS = 1, + NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS = 0x1, + NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_GOOD = 0x2, + NM_CLIENT_INSTANCE_FLAGS_INITIALIZED_BAD = 0x4, } NMClientInstanceFlags; #define NM_TYPE_CLIENT (nm_client_get_type()) @@ -38,6 +47,7 @@ typedef enum /*< flags >*/ { #define NM_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_CLIENT, NMClientClass)) #define NM_CLIENT_VERSION "version" +#define NM_CLIENT_VERSION_INFO "version-info" #define NM_CLIENT_STATE "state" #define NM_CLIENT_STARTUP "startup" #define NM_CLIENT_NM_RUNNING "nm-running" @@ -125,7 +135,7 @@ GQuark nm_client_error_quark(void); * * Since: 1.6 */ -typedef struct NMDnsEntry NMDnsEntry; +typedef struct _NMDnsEntry NMDnsEntry; NM_AVAILABLE_IN_1_6 GType nm_dns_entry_get_type(void); @@ -178,9 +188,13 @@ NM_AVAILABLE_IN_1_22 const char *nm_client_get_dbus_name_owner(NMClient *client); const char *nm_client_get_version(NMClient *client); -NMState nm_client_get_state(NMClient *client); -gboolean nm_client_get_startup(NMClient *client); -gboolean nm_client_get_nm_running(NMClient *client); + +NM_AVAILABLE_IN_1_42 +const guint32 *nm_client_get_version_info(NMClient *client, gsize *length); + +NMState nm_client_get_state(NMClient *client); +gboolean nm_client_get_startup(NMClient *client); +gboolean nm_client_get_nm_running(NMClient *client); NMObject *nm_client_get_object_by_path(NMClient *client, const char *dbus_path); @@ -504,6 +518,18 @@ gboolean nm_utils_file_is_certificate(const char *filename); gboolean nm_utils_file_is_private_key(const char *filename, gboolean *out_encrypted); gboolean nm_utils_file_is_pkcs12(const char *filename); +/*****************************************************************************/ + +NM_AVAILABLE_IN_1_42 +void nm_client_wait_shutdown(NMClient *client, + gboolean integrate_maincontext, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +NM_AVAILABLE_IN_1_42 +gboolean nm_client_wait_shutdown_finish(GAsyncResult *result, GError **error); + G_END_DECLS #endif /* __NM_CLIENT_H__ */ diff --git a/src/libnm-client-public/nm-device-6lowpan.h b/src/libnm-client-public/nm-device-6lowpan.h index 7feba237..62bb2203 100644 --- a/src/libnm-client-public/nm-device-6lowpan.h +++ b/src/libnm-client-public/nm-device-6lowpan.h @@ -38,7 +38,7 @@ typedef struct _NMDevice6LowpanClass NMDevice6LowpanClass; NM_AVAILABLE_IN_1_14 GType nm_device_6lowpan_get_type(void); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 NMDevice *nm_device_6lowpan_get_parent(NMDevice6Lowpan *device); G_END_DECLS diff --git a/src/libnm-client-public/nm-device-dummy.h b/src/libnm-client-public/nm-device-dummy.h index 0e6c6fe6..f869d0f6 100644 --- a/src/libnm-client-public/nm-device-dummy.h +++ b/src/libnm-client-public/nm-device-dummy.h @@ -36,8 +36,8 @@ typedef struct _NMDeviceDummyClass NMDeviceDummyClass; GType nm_device_dummy_get_type(void); -NM_AVAILABLE_IN_1_10 NM_DEPRECATED_IN_1_24_FOR(nm_device_get_hw_address) +NM_AVAILABLE_IN_1_10 const char *nm_device_dummy_get_hw_address(NMDeviceDummy *device); G_END_DECLS diff --git a/src/libnm-client-public/nm-device-loopback.h b/src/libnm-client-public/nm-device-loopback.h new file mode 100644 index 00000000..ae5dcc47 --- /dev/null +++ b/src/libnm-client-public/nm-device-loopback.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022 Red Hat, Inc. + */ + +#ifndef __NM_DEVICE_LOOPBACK_H__ +#define __NM_DEVICE_LOOPBACK_H__ + +#if !defined(__NETWORKMANAGER_H_INSIDE__) && !defined(NETWORKMANAGER_COMPILATION) +#error "Only can be included directly." +#endif + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_LOOPBACK (nm_device_loopback_get_type()) +#define NM_DEVICE_LOOPBACK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_LOOPBACK, NMDeviceLoopback)) +#define NM_DEVICE_LOOPBACK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_LOOPBACK, NMDeviceLoopbackClass)) +#define NM_IS_DEVICE_LOOPBACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_LOOPBACK)) +#define NM_IS_DEVICE_LOOPBACK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DEVICE_LOOPBACK)) +#define NM_DEVICE_LOOPBACK_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DEVICE_LOOPBACK, NMDeviceLoopbackClass)) + +/** + * NMDeviceLoopback: + * + * Since: 1.42 + */ +typedef struct _NMDeviceLoopback NMDeviceLoopback; +typedef struct _NMDeviceLoopbackClass NMDeviceLoopbackClass; + +NM_AVAILABLE_IN_1_42 +GType nm_device_loopback_get_type(void); + +G_END_DECLS + +#endif /* __NM_DEVICE_LOOPBACK_H__ */ diff --git a/src/libnm-client-public/nm-device-macsec.h b/src/libnm-client-public/nm-device-macsec.h index f4f6f03d..307233d2 100644 --- a/src/libnm-client-public/nm-device-macsec.h +++ b/src/libnm-client-public/nm-device-macsec.h @@ -50,11 +50,11 @@ typedef struct _NMDeviceMacsecClass NMDeviceMacsecClass; NM_AVAILABLE_IN_1_6 GType nm_device_macsec_get_type(void); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 NMDevice *nm_device_macsec_get_parent(NMDeviceMacsec *device); -NM_AVAILABLE_IN_1_6 NM_DEPRECATED_IN_1_24_FOR(nm_device_get_hw_address) +NM_AVAILABLE_IN_1_6 const char *nm_device_macsec_get_hw_address(NMDeviceMacsec *device); NM_AVAILABLE_IN_1_6 diff --git a/src/libnm-client-public/nm-device-macvlan.h b/src/libnm-client-public/nm-device-macvlan.h index 85292069..432a7206 100644 --- a/src/libnm-client-public/nm-device-macvlan.h +++ b/src/libnm-client-public/nm-device-macvlan.h @@ -50,8 +50,8 @@ gboolean nm_device_macvlan_get_no_promisc(NMDeviceMacvlan *device); NM_AVAILABLE_IN_1_2 gboolean nm_device_macvlan_get_tap(NMDeviceMacvlan *device); -NM_AVAILABLE_IN_1_2 NM_DEPRECATED_IN_1_24_FOR(nm_device_get_hw_address) +NM_AVAILABLE_IN_1_2 const char *nm_device_macvlan_get_hw_address(NMDeviceMacvlan *device); G_END_DECLS diff --git a/src/libnm-client-public/nm-device-ovs-bridge.h b/src/libnm-client-public/nm-device-ovs-bridge.h index a6c9ff0b..a2540114 100644 --- a/src/libnm-client-public/nm-device-ovs-bridge.h +++ b/src/libnm-client-public/nm-device-ovs-bridge.h @@ -38,6 +38,7 @@ typedef struct _NMDeviceOvsBridgeClass NMDeviceOvsBridgeClass; NM_AVAILABLE_IN_1_10 GType nm_device_ovs_bridge_get_type(void); +NM_DEPRECATED_IN_1_34_FOR(nm_device_get_ports) NM_AVAILABLE_IN_1_14 const GPtrArray *nm_device_ovs_bridge_get_slaves(NMDeviceOvsBridge *device); diff --git a/src/libnm-client-public/nm-device-ovs-port.h b/src/libnm-client-public/nm-device-ovs-port.h index 8014ed91..41c61cfa 100644 --- a/src/libnm-client-public/nm-device-ovs-port.h +++ b/src/libnm-client-public/nm-device-ovs-port.h @@ -38,6 +38,7 @@ typedef struct _NMDeviceOvsPortClass NMDeviceOvsPortClass; NM_AVAILABLE_IN_1_10 GType nm_device_ovs_port_get_type(void); +NM_DEPRECATED_IN_1_34_FOR(nm_device_get_ports) NM_AVAILABLE_IN_1_14 const GPtrArray *nm_device_ovs_port_get_slaves(NMDeviceOvsPort *device); diff --git a/src/libnm-client-public/nm-device-team.h b/src/libnm-client-public/nm-device-team.h index 406949a1..f3f48ff6 100644 --- a/src/libnm-client-public/nm-device-team.h +++ b/src/libnm-client-public/nm-device-team.h @@ -39,8 +39,11 @@ GType nm_device_team_get_type(void); NM_DEPRECATED_IN_1_24_FOR(nm_device_get_hw_address) const char *nm_device_team_get_hw_address(NMDeviceTeam *device); -gboolean nm_device_team_get_carrier(NMDeviceTeam *device); +gboolean nm_device_team_get_carrier(NMDeviceTeam *device); + +NM_DEPRECATED_IN_1_34_FOR(nm_device_get_ports) const GPtrArray *nm_device_team_get_slaves(NMDeviceTeam *device); + NM_AVAILABLE_IN_1_4 const char *nm_device_team_get_config(NMDeviceTeam *device); diff --git a/src/libnm-client-public/nm-device-tun.h b/src/libnm-client-public/nm-device-tun.h index 42690358..97b01d5f 100644 --- a/src/libnm-client-public/nm-device-tun.h +++ b/src/libnm-client-public/nm-device-tun.h @@ -42,8 +42,8 @@ typedef struct _NMDeviceTunClass NMDeviceTunClass; NM_AVAILABLE_IN_1_2 GType nm_device_tun_get_type(void); -NM_AVAILABLE_IN_1_2 NM_DEPRECATED_IN_1_24_FOR(nm_device_get_hw_address) +NM_AVAILABLE_IN_1_2 const char *nm_device_tun_get_hw_address(NMDeviceTun *device); NM_AVAILABLE_IN_1_2 diff --git a/src/libnm-client-public/nm-device-veth.h b/src/libnm-client-public/nm-device-veth.h index 9de27595..623420cd 100644 --- a/src/libnm-client-public/nm-device-veth.h +++ b/src/libnm-client-public/nm-device-veth.h @@ -36,7 +36,7 @@ typedef struct _NMDeviceVethClass NMDeviceVethClass; NM_AVAILABLE_IN_1_30 GType nm_device_veth_get_type(void); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 NMDevice *nm_device_veth_get_peer(NMDeviceVeth *device); G_END_DECLS diff --git a/src/libnm-client-public/nm-device-vxlan.h b/src/libnm-client-public/nm-device-vxlan.h index fa7511e1..2024801f 100644 --- a/src/libnm-client-public/nm-device-vxlan.h +++ b/src/libnm-client-public/nm-device-vxlan.h @@ -54,11 +54,11 @@ typedef struct _NMDeviceVxlanClass NMDeviceVxlanClass; NM_AVAILABLE_IN_1_2 GType nm_device_vxlan_get_type(void); -NM_AVAILABLE_IN_1_2 NM_DEPRECATED_IN_1_24_FOR(nm_device_get_hw_address) +NM_AVAILABLE_IN_1_2 const char *nm_device_vxlan_get_hw_address(NMDeviceVxlan *device); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 gboolean nm_device_vxlan_get_carrier(NMDeviceVxlan *device); NM_AVAILABLE_IN_1_2 NMDevice *nm_device_vxlan_get_parent(NMDeviceVxlan *device); @@ -86,7 +86,7 @@ NM_AVAILABLE_IN_1_2 guint nm_device_vxlan_get_limit(NMDeviceVxlan *device); NM_AVAILABLE_IN_1_2 gboolean nm_device_vxlan_get_proxy(NMDeviceVxlan *device); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 gboolean nm_device_vxlan_get_rsc(NMDeviceVxlan *device); NM_AVAILABLE_IN_1_2 gboolean nm_device_vxlan_get_l2miss(NMDeviceVxlan *device); diff --git a/src/libnm-client-public/nm-device-wifi-p2p.h b/src/libnm-client-public/nm-device-wifi-p2p.h index 31add1e3..44b59ccf 100644 --- a/src/libnm-client-public/nm-device-wifi-p2p.h +++ b/src/libnm-client-public/nm-device-wifi-p2p.h @@ -40,11 +40,11 @@ typedef struct _NMDeviceWifiP2PClass NMDeviceWifiP2PClass; NM_AVAILABLE_IN_1_16 GType nm_device_wifi_p2p_get_type(void); -NM_AVAILABLE_IN_1_16 NM_DEPRECATED_IN_1_24_FOR(nm_device_get_hw_address) +NM_AVAILABLE_IN_1_16 const char *nm_device_wifi_p2p_get_hw_address(NMDeviceWifiP2P *device); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 NMWifiP2PPeer *nm_device_wifi_p2p_get_peer_by_path(NMDeviceWifiP2P *device, const char *path); NM_AVAILABLE_IN_1_16 diff --git a/src/libnm-client-public/nm-ethtool-utils.h b/src/libnm-client-public/nm-ethtool-utils.h index 3656ed3f..fa9c3071 100644 --- a/src/libnm-client-public/nm-ethtool-utils.h +++ b/src/libnm-client-public/nm-ethtool-utils.h @@ -3,7 +3,7 @@ * Copyright (C) 2018 Red Hat, Inc. */ -#ifndef __NM_ETHTOOL_UTILS_H__ /* lgtm[cpp/duplicate-include-guard] */ +#ifndef __NM_ETHTOOL_UTILS_H__ #define __NM_ETHTOOL_UTILS_H__ G_BEGIN_DECLS diff --git a/src/libnm-core-aux-extern/nm-dispatcher-api.h b/src/libnm-core-aux-extern/nm-dispatcher-api.h index 7776c84f..56da5fba 100644 --- a/src/libnm-core-aux-extern/nm-dispatcher-api.h +++ b/src/libnm-core-aux-extern/nm-dispatcher-api.h @@ -33,6 +33,7 @@ #define NMD_ACTION_DHCP4_CHANGE "dhcp4-change" #define NMD_ACTION_DHCP6_CHANGE "dhcp6-change" #define NMD_ACTION_CONNECTIVITY_CHANGE "connectivity-change" +#define NMD_ACTION_REAPPLY "reapply" typedef enum { DISPATCH_RESULT_UNKNOWN = 0, diff --git a/src/libnm-core-aux-intern/nm-auth-subject.h b/src/libnm-core-aux-intern/nm-auth-subject.h index 2c9c4f71..eb153cc5 100644 --- a/src/libnm-core-aux-intern/nm-auth-subject.h +++ b/src/libnm-core-aux-intern/nm-auth-subject.h @@ -8,7 +8,7 @@ #define NM_TYPE_AUTH_SUBJECT (nm_auth_subject_get_type()) #define NM_AUTH_SUBJECT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_AUTH_SUBJECT, NMAuthSubject)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_AUTH_SUBJECT, NMAuthSubject)) #define NM_AUTH_SUBJECT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_AUTH_SUBJECT, NMAuthSubjectClass)) #define NM_IS_AUTH_SUBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_AUTH_SUBJECT)) diff --git a/src/libnm-core-aux-intern/nm-libnm-core-utils.c b/src/libnm-core-aux-intern/nm-libnm-core-utils.c index 1febe5df..c4bfd2bd 100644 --- a/src/libnm-core-aux-intern/nm-libnm-core-utils.c +++ b/src/libnm-core-aux-intern/nm-libnm-core-utils.c @@ -349,12 +349,12 @@ nm_auth_permission_from_string(const char *str) if (!NM_STR_HAS_PREFIX(str, AUTH_PERMISSION_PREFIX)) return NM_CLIENT_PERMISSION_NONE; - idx = nm_utils_array_find_binary_search(nm_auth_permission_sorted, - sizeof(nm_auth_permission_sorted[0]), - G_N_ELEMENTS(nm_auth_permission_sorted), - &str[NM_STRLEN(AUTH_PERMISSION_PREFIX)], - _nm_auth_permission_from_string_cmp, - NULL); + idx = nm_array_find_bsearch(nm_auth_permission_sorted, + G_N_ELEMENTS(nm_auth_permission_sorted), + sizeof(nm_auth_permission_sorted[0]), + &str[NM_STRLEN(AUTH_PERMISSION_PREFIX)], + _nm_auth_permission_from_string_cmp, + NULL); if (idx < 0) return NM_CLIENT_PERMISSION_NONE; return nm_auth_permission_sorted[idx]; @@ -520,3 +520,214 @@ nm_mptcp_flags_normalize(NMMptcpFlags flags) return flags; } + +/*****************************************************************************/ + +gboolean +nm_utils_dnsname_parse(int addr_family, + const char *dns, + int *out_addr_family, + gpointer /* (NMIPAddr **) */ out_addr, + const char **out_servername) +{ + gs_free char *dns_heap = NULL; + const char *s; + NMIPAddr addr; + + nm_assert_addr_family_or_unspec(addr_family); + nm_assert(!out_addr || out_addr_family || NM_IN_SET(addr_family, AF_INET, AF_INET6)); + + if (!dns) + return FALSE; + + s = strchr(dns, '#'); + + if (s) { + dns = nm_strndup_a(200, dns, s - dns, &dns_heap); + s++; + } + + if (s && s[0] == '\0') { + /* "ADDR#" empty DoT SNI name is not allowed. */ + return FALSE; + } + + if (!nm_inet_parse_bin(addr_family, dns, &addr_family, out_addr ? &addr : NULL)) + return FALSE; + + NM_SET_OUT(out_addr_family, addr_family); + if (out_addr) + nm_ip_addr_set(addr_family, out_addr, &addr); + NM_SET_OUT(out_servername, s); + return TRUE; +} + +const char * +nm_utils_dnsname_construct(int addr_family, + gconstpointer /* (const NMIPAddr *) */ addr, + const char *server_name, + char *result, + gsize result_len) +{ + char sbuf[NM_INET_ADDRSTRLEN]; + gsize l; + int d; + + nm_assert_addr_family(addr_family); + nm_assert(addr); + nm_assert(!server_name || !nm_str_is_empty(server_name)); + + nm_inet_ntop(addr_family, addr, sbuf); + + if (!server_name) { + l = g_strlcpy(result, sbuf, result_len); + } else { + d = g_snprintf(result, result_len, "%s#%s", sbuf, server_name); + nm_assert(d >= 0); + l = (gsize) d; + } + + return l < result_len ? result : NULL; +} + +const char * +nm_utils_dnsname_normalize(int addr_family, const char *dns, char **out_free) +{ + char sbuf[NM_INET_ADDRSTRLEN]; + const char *server_name; + char *s; + NMIPAddr a; + gsize l; + + nm_assert_addr_family_or_unspec(addr_family); + nm_assert(dns); + nm_assert(out_free && !*out_free); + + if (!nm_utils_dnsname_parse(addr_family, dns, &addr_family, &a, &server_name)) + return NULL; + + nm_inet_ntop(addr_family, &a, sbuf); + + l = strlen(sbuf); + + /* In the vast majority of cases, the name is in fact normalized. Check + * whether it is, and don't duplicate the string. */ + if (strncmp(dns, sbuf, l) == 0) { + if (server_name) { + if (dns[l] == '#' && nm_streq(&dns[l + 1], server_name)) + return dns; + } else { + if (dns[l] == '\0') + return dns; + } + } + + if (!server_name) + s = g_strdup(sbuf); + else + s = g_strconcat(sbuf, "#", server_name, NULL); + + *out_free = s; + return s; +} + +/*****************************************************************************/ + +/** + * nm_setting_ovs_other_config_check_key: + * @key: (allow-none): the key to check + * @error: a #GError, %NULL to ignore. + * + * Checks whether @key is a valid key for OVS' other-config. + * This means, the key cannot be %NULL, not too large and valid ASCII. + * Also, only digits and numbers are allowed with a few special + * characters. + * + * Returns: %TRUE if @key is a valid user data key. + */ +gboolean +nm_setting_ovs_other_config_check_key(const char *key, GError **error) +{ + gsize len; + + g_return_val_if_fail(!error || !*error, FALSE); + + if (!key || !key[0]) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("missing key")); + return FALSE; + } + len = strlen(key); + if (len > 255u) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("key is too long")); + return FALSE; + } + if (!g_utf8_validate(key, len, NULL)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("key must be UTF8")); + return FALSE; + } + if (!NM_STRCHAR_ALL(key, ch, nm_ascii_is_regular_char(ch))) { + /* Probably OVS is more forgiving about what makes a valid key for + * an other-key. However, we are strict (at least, for now). */ + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("key contains invalid characters")); + return FALSE; + } + + return TRUE; +} + +/** + * nm_setting_ovs_other_config_check_val: + * @val: (allow-none): the value to check + * @error: a #GError, %NULL to ignore. + * + * Checks whether @val is a valid user data value. This means, + * value is not %NULL, not too large and valid UTF-8. + * + * Returns: %TRUE if @val is a valid user data value. + */ +gboolean +nm_setting_ovs_other_config_check_val(const char *val, GError **error) +{ + gsize len; + + g_return_val_if_fail(!error || !*error, FALSE); + + if (!val) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("value is missing")); + return FALSE; + } + + len = strlen(val); + if (len > (2u * 1024u)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("value is too large")); + return FALSE; + } + + if (!g_utf8_validate(val, len, NULL)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("value is not valid UTF8")); + return FALSE; + } + + return TRUE; +} diff --git a/src/libnm-core-aux-intern/nm-libnm-core-utils.h b/src/libnm-core-aux-intern/nm-libnm-core-utils.h index 0208bfdb..18af1e1a 100644 --- a/src/libnm-core-aux-intern/nm-libnm-core-utils.h +++ b/src/libnm-core-aux-intern/nm-libnm-core-utils.h @@ -279,4 +279,38 @@ gpointer _nm_connection_new_setting(NMConnection *connection, GType gtype); NMMptcpFlags nm_mptcp_flags_normalize(NMMptcpFlags flags); +/*****************************************************************************/ + +gboolean nm_utils_dnsname_parse(int addr_family, + const char *dns, + int *out_addr_family, + gpointer /* (NMIPAddr **) */ out_addr, + const char **out_servername); + +#define nm_utils_dnsname_parse_assert(addr_family, dns, out_addr_family, out_addr, out_servername) \ + ({ \ + gboolean _good; \ + \ + _good = nm_utils_dnsname_parse((addr_family), \ + (dns), \ + (out_addr_family), \ + (out_addr), \ + (out_servername)); \ + nm_assert(_good); \ + _good; \ + }) + +const char *nm_utils_dnsname_construct(int addr_family, + gconstpointer /* (const NMIPAddr *) */ addr, + const char *server_name, + char *result, + gsize result_len); + +const char *nm_utils_dnsname_normalize(int addr_family, const char *dns, char **out_free); + +/*****************************************************************************/ + +gboolean nm_setting_ovs_other_config_check_key(const char *key, GError **error); +gboolean nm_setting_ovs_other_config_check_val(const char *val, GError **error); + #endif /* __NM_LIBNM_SHARED_UTILS_H__ */ diff --git a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.c b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.c new file mode 100644 index 00000000..7e25e510 --- /dev/null +++ b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.c @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "libnm-core-impl/nm-default-libnm-core.h" + +#include "libnm-glib-aux/nm-str-buf.h" +#include "libnm-core-intern/nm-meta-setting-base.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "libnm-base/nm-ethtool-base.h" + +#include "libnm-core-public/nm-setting-ethtool.h" + +#define INDENT 4 + +static const char * +_xml_escape_attr(NMStrBuf *sbuf, const char *value) +{ + gs_free char *s = NULL; + + nm_str_buf_reset(sbuf); + s = g_markup_escape_text(value, -1); + nm_str_buf_append_c(sbuf, '"'); + nm_str_buf_append(sbuf, s); + nm_str_buf_append_c(sbuf, '"'); + return nm_str_buf_get_str(sbuf); +} + +static const char * +_indent_level(guint num_spaces) +{ + static const char spaces[] = " "; + + nm_assert(num_spaces < G_N_ELEMENTS(spaces)); + return &spaces[G_N_ELEMENTS(spaces) - num_spaces - 1]; +} + +int +main(int argc, char *argv[]) +{ + nm_auto_str_buf NMStrBuf sbuf1 = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE); + const NMSettInfoSetting *sett_info_settings = nmtst_sett_info_settings(); + NMMetaSettingType meta_type; + + g_print("\n"); + g_print("\n"); + for (meta_type = 0; meta_type < _NM_META_SETTING_TYPE_NUM; meta_type++) { + const NMSettInfoSetting *sis = &sett_info_settings[meta_type]; + const NMMetaSettingInfo *msi = &nm_meta_setting_infos[meta_type]; + nm_auto_unref_gtypeclass NMSettingClass *klass = NULL; + guint prop_idx; + GType gtype; + + gtype = msi->get_setting_gtype(); + klass = g_type_class_ref(gtype); + + g_print("%ssetting_name)); + g_print("\n%sgtype=%s", + _indent_level(INDENT + 9), + _xml_escape_attr(&sbuf1, g_type_name(gtype))); + g_print("\n%s>\n", _indent_level(INDENT + 9)); + + for (prop_idx = 0; prop_idx < sis->property_infos_len; prop_idx++) { + const NMSettInfoProperty *sip = &sis->property_infos[prop_idx]; + + if (nm_streq(sip->name, NM_SETTING_NAME)) + continue; + + g_print("%sname)); + if (sip->is_deprecated) + g_print("\n%sis-deprecated=\"1\"", _indent_level(2 * INDENT + 10)); + if (sip->param_spec && NM_FLAGS_HAS(sip->param_spec->flags, NM_SETTING_PARAM_SECRET)) { + g_print("\n%sis-secret=\"1\"", _indent_level(2 * INDENT + 10)); + } + if (sip->param_spec + && G_PARAM_SPEC_VALUE_TYPE(sip->param_spec) == NM_TYPE_SETTING_SECRET_FLAGS) { + g_print("\n%sis-secret-flags=\"1\"", _indent_level(2 * INDENT + 10)); + } + if (sip->property_type->dbus_type) { + g_print("\n%sdbus-type=%s", + _indent_level(2 * INDENT + 10), + _xml_escape_attr(&sbuf1, (const char *) sip->property_type->dbus_type)); + } + if (sip->dbus_deprecated) { + nm_assert(sip->property_type->dbus_type); + g_print("\n%sdbus-deprecated=\"1\"", _indent_level(2 * INDENT + 10)); + } + if (sip->param_spec) { + nm_assert(nm_streq(sip->name, sip->param_spec->name)); + g_print("\n%sgprop-type=%s", + _indent_level(2 * INDENT + 10), + _xml_escape_attr(&sbuf1, + g_type_name(G_PARAM_SPEC_VALUE_TYPE(sip->param_spec)))); + } + g_print("\n%s/>\n", _indent_level(2 * INDENT + 10)); + } + + if (nm_streq(msi->setting_name, NM_SETTING_ETHTOOL_SETTING_NAME)) { + NMEthtoolID ethtool_id; + + /* NMSettingEthtool's properties are "gendata" options. They are implemented differently. */ + for (ethtool_id = _NM_ETHTOOL_ID_FIRST; ethtool_id <= _NM_ETHTOOL_ID_LAST; + ethtool_id++) { + g_print("%soptname)); + g_print( + "\n%sdbus-type=%s", + _indent_level(2 * INDENT + 10), + _xml_escape_attr(&sbuf1, + (const char *) nm_ethtool_id_get_variant_type(ethtool_id))); + g_print("\n%sis-setting-option=\"1\"", _indent_level(2 * INDENT + 10)); + g_print("\n%s/>\n", _indent_level(2 * INDENT + 10)); + } + } + + g_print("%s\n", _indent_level(INDENT)); + } + g_print("\n"); + return 0; +} diff --git a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in new file mode 100644 index 00000000..3b210b16 --- /dev/null +++ b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in @@ -0,0 +1,2492 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libnm-core-impl/meson.build b/src/libnm-core-impl/meson.build index 83c29085..6408ae17 100644 --- a/src/libnm-core-impl/meson.build +++ b/src/libnm-core-impl/meson.build @@ -24,6 +24,7 @@ libnm_core_settings_sources = files( 'nm-setting-ip-tunnel.c', 'nm-setting-ip4-config.c', 'nm-setting-ip6-config.c', + 'nm-setting-loopback.c', 'nm-setting-macsec.c', 'nm-setting-macvlan.c', 'nm-setting-match.c', @@ -32,6 +33,7 @@ libnm_core_settings_sources = files( 'nm-setting-ovs-dpdk.c', 'nm-setting-ovs-external-ids.c', 'nm-setting-ovs-interface.c', + 'nm-setting-ovs-other-config.c', 'nm-setting-ovs-patch.c', 'nm-setting-ovs-port.c', 'nm-setting-ppp.c', @@ -60,7 +62,6 @@ libnm_core_settings_sources = files( libnm_core_impl_sources = files( 'nm-connection.c', - 'nm-dbus-utils.c', 'nm-errors.c', 'nm-keyfile-utils.c', 'nm-keyfile.c', @@ -88,3 +89,52 @@ libnm_core_impl = static_library( uuid_dep, ], ) + +############################################################################### + +gen_metadata_nm_settings_libnm_core = executable( + 'gen-metadata-nm-settings-libnm-core', + files( + 'gen-metadata-nm-settings-libnm-core.c', + ), + dependencies: [ + libnm_core_public_dep, + ], + link_with: [ + libnm_core_aux_intern, + libnm_core_impl, + libnm_crypto, + libnm_base, + libnm_systemd_shared, + libnm_log_null, + libnm_glib_aux, + libnm_std_aux, + libc_siphash, + ], + link_args: ldflags_linker_script_binary, + link_depends: linker_script_binary, +) + +if enable_docs + gen_metadata_nm_settings_libnm_core_xml = custom_target( + 'gen-metadata-nm-settings-libnm-core.xml', + output: 'gen-metadata-nm-settings-libnm-core.xml', + command: [ gen_metadata_nm_settings_libnm_core ], + capture: true, + ) + + test( + 'check-local-gen-metadata-nm-settings-libnm-core', + find_program(join_paths(source_root, 'tools', 'check-compare-generated.sh')), + args: [ + source_root + '/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in', + gen_metadata_nm_settings_libnm_core_xml, + ], + ) +else + gen_metadata_nm_settings_libnm_core_xml = configure_file( + input: 'gen-metadata-nm-settings-libnm-core.xml.in', + output: '@BASENAME@', + configuration: configuration_data(), + ) +endif diff --git a/src/libnm-core-impl/nm-connection.c b/src/libnm-core-impl/nm-connection.c index 2f5bf357..3a9eda0e 100644 --- a/src/libnm-core-impl/nm-connection.c +++ b/src/libnm-core-impl/nm-connection.c @@ -11,6 +11,7 @@ #include #include "libnm-glib-aux/nm-uuid.h" +#include "libnm-glib-aux/nm-ref-string.h" #include "nm-connection-private.h" #include "nm-utils.h" #include "nm-setting-private.h" @@ -54,39 +55,12 @@ static gboolean _nm_connection_clear_settings(NMConnection *connection, NMConnec /*****************************************************************************/ -#undef NM_IS_SIMPLE_CONNECTION -#define NM_IS_SIMPLE_CONNECTION(self) \ - ({ \ - gconstpointer _self1 = (self); \ - gboolean _result; \ - \ - _result = \ - (_self1 \ - && (((GTypeInstance *) _self1)->g_class == _nm_simple_connection_class_instance)); \ - \ - nm_assert(_result == G_TYPE_CHECK_INSTANCE_TYPE(_self1, NM_TYPE_SIMPLE_CONNECTION)); \ - \ - _result; \ - }) - -#undef NM_IS_CONNECTION -#define NM_IS_CONNECTION(self) \ - ({ \ - gconstpointer _self0 = (self); \ - \ - (_self0 \ - && (NM_IS_SIMPLE_CONNECTION(_self0) \ - || G_TYPE_CHECK_INSTANCE_TYPE(_self0, NM_TYPE_CONNECTION))); \ - }) - -/*****************************************************************************/ - void _nm_connection_private_clear(NMConnectionPrivate *priv) { if (priv->self) { _nm_connection_clear_settings(priv->self, priv); - nm_clear_g_free(&priv->path); + nm_clear_pointer(&priv->path, nm_ref_string_unref); priv->self = NULL; } } @@ -124,19 +98,19 @@ _nm_connection_get_private_from_qdata(NMConnection *connection) return priv; } -#define NM_CONNECTION_GET_PRIVATE(connection) \ - ({ \ - NMConnection *_connection = (connection); \ - NMConnectionPrivate *_priv; \ - \ - if (G_LIKELY(NM_IS_SIMPLE_CONNECTION(_connection))) \ - _priv = (gpointer) (&(((char *) _connection)[_nm_simple_connection_private_offset])); \ - else \ - _priv = _nm_connection_get_private_from_qdata(_connection); \ - \ - nm_assert(_priv && _priv->self == _connection); \ - \ - _priv; \ +#define NM_CONNECTION_GET_PRIVATE(connection) \ + ({ \ + NMConnection *_connection = (connection); \ + NMConnectionPrivate *_priv; \ + \ + if (G_LIKELY(NM_IS_SIMPLE_CONNECTION(_connection))) \ + _priv = _NM_SIMPLE_CONNECTION_GET_CONNECTION_PRIVATE(_connection); \ + else \ + _priv = _nm_connection_get_private_from_qdata(_connection); \ + \ + nm_assert(_priv && _priv->self == _connection); \ + \ + _priv; \ }) /*****************************************************************************/ @@ -1308,11 +1282,11 @@ _normalize_ip_config(NMConnection *self, GHashTable *parameters) && nm_setting_ip6_config_get_addr_gen_mode((NMSettingIP6Config *) s_ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) { struct in6_addr i6_token; - char normalized[NM_UTILS_INET_ADDRSTRLEN]; + char normalized[NM_INET_ADDRSTRLEN]; if (inet_pton(AF_INET6, token, &i6_token) == 1 && _nm_utils_inet6_is_token(&i6_token)) { - _nm_utils_inet6_ntop(&i6_token, normalized); + nm_inet6_ntop(&i6_token, normalized); if (g_strcmp0(token, normalized)) { g_object_set(s_ip6, NM_SETTING_IP6_CONFIG_TOKEN, normalized, NULL); changed = TRUE; @@ -1623,6 +1597,18 @@ _normalize_bridge_port_vlan_order(NMConnection *self) return _nm_setting_bridge_port_sort_vlans(s_port); } +static gboolean +_normalize_ovs_port_trunks(NMConnection *self) +{ + NMSettingOvsPort *s_ovs_port; + + s_ovs_port = nm_connection_get_setting_ovs_port(self); + if (!s_ovs_port) + return FALSE; + + return _nm_setting_ovs_port_sort_trunks(s_ovs_port); +} + static gboolean _normalize_gsm_auto_config(NMConnection *self) { @@ -2015,6 +2001,7 @@ _connection_normalize(NMConnection *connection, was_modified |= _normalize_bridge_port_vlan_order(connection); was_modified |= _normalize_gsm_auto_config(connection); was_modified |= _normalize_802_1x_empty_strings(connection); + was_modified |= _normalize_ovs_port_trunks(connection); was_modified = !!was_modified; @@ -2361,35 +2348,15 @@ nm_connection_update_secrets(NMConnection *connection, return success; } -/** - * nm_connection_need_secrets: - * @connection: the #NMConnection - * @hints: (out) (element-type utf8) (allow-none) (transfer container): - * the address of a pointer to a #GPtrArray, initialized to %NULL, which on - * return points to an allocated #GPtrArray containing the property names of - * secrets of the #NMSetting which may be required; the caller owns the array - * and must free the array itself with g_ptr_array_free(), but not free its - * elements - * - * Returns the name of the first setting object in the connection which would - * need secrets to make a successful connection. The returned hints are only - * intended as a guide to what secrets may be required, because in some - * circumstances, there is no way to conclusively determine exactly which - * secrets are needed. - * - * Returns: the setting name of the #NMSetting object which has invalid or - * missing secrets - **/ -const char * -nm_connection_need_secrets(NMConnection *connection, GPtrArray **hints) +static const char * +_need_secrets(NMConnection *connection, gboolean check_rerequest, GPtrArray **hints) { NMSetting *setting_before = NULL; NMConnectionPrivate *priv; int i; - g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL); - if (hints) - g_return_val_if_fail(*hints == NULL, NULL); + nm_assert(NM_IS_CONNECTION(connection)); + nm_assert(!hints || !*hints); priv = NM_CONNECTION_GET_PRIVATE(connection); @@ -2405,7 +2372,7 @@ nm_connection_need_secrets(NMConnection *connection, GPtrArray **hints) nm_assert(!setting_before || _nm_setting_compare_priority(setting_before, setting) <= 0); setting_before = setting; - secrets = _nm_setting_need_secrets(setting); + secrets = _nm_setting_need_secrets(setting, check_rerequest); if (!secrets) continue; @@ -2420,6 +2387,48 @@ nm_connection_need_secrets(NMConnection *connection, GPtrArray **hints) return NULL; } +/** + * nm_connection_need_secrets: + * @connection: the #NMConnection + * @hints: (out) (element-type utf8) (allow-none) (transfer container): + * the address of a pointer to a #GPtrArray, initialized to %NULL, which on + * return points to an allocated #GPtrArray containing the property names of + * secrets of the #NMSetting which may be required; the caller owns the array + * and must free the array itself with g_ptr_array_free(), but not free its + * elements + * + * Returns the name of the first setting object in the connection which would + * need secrets to make a successful connection. The returned hints are only + * intended as a guide to what secrets may be required, because in some + * circumstances, there is no way to conclusively determine exactly which + * secrets are needed. + * + * Returns: the setting name of the #NMSetting object which has invalid or + * missing secrets + **/ +const char * +nm_connection_need_secrets(NMConnection *connection, GPtrArray **hints) +{ + g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL); + g_return_val_if_fail(!hints || !*hints, NULL); + + return _need_secrets(connection, FALSE, hints); +} + +/** + * nm_connection_need_secrets_for_rerequest: + * @connection: the #NMConnection + * + * Returns TRUE if some secret needs to be re-requested + **/ +gboolean +nm_connection_need_secrets_for_rerequest(NMConnection *connection) +{ + g_return_val_if_fail(NM_IS_CONNECTION(connection), FALSE); + + return !!_need_secrets(connection, TRUE, NULL); +} + /** * nm_connection_clear_secrets: * @connection: the #NMConnection @@ -2942,14 +2951,15 @@ nm_connection_dump(NMConnection *connection) void nm_connection_set_path(NMConnection *connection, const char *path) { - NMConnectionPrivate *priv; - g_return_if_fail(NM_IS_CONNECTION(connection)); - priv = NM_CONNECTION_GET_PRIVATE(connection); + nm_ref_string_reset_str(&NM_CONNECTION_GET_PRIVATE(connection)->path, path); +} - g_free(priv->path); - priv->path = g_strdup(path); +void +_nm_connection_set_path_rstr(NMConnection *connection, NMRefString *path) +{ + nm_ref_string_reset(&NM_CONNECTION_GET_PRIVATE(connection)->path, path); } /** @@ -2966,6 +2976,12 @@ nm_connection_get_path(NMConnection *connection) { g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL); + return nm_ref_string_get_str(NM_CONNECTION_GET_PRIVATE(connection)->path); +} + +NMRefString * +_nm_connection_get_path_rstr(NMConnection *connection) +{ return NM_CONNECTION_GET_PRIVATE(connection)->path; } diff --git a/src/libnm-core-impl/nm-dbus-utils.c b/src/libnm-core-impl/nm-dbus-utils.c deleted file mode 100644 index c9443ff9..00000000 --- a/src/libnm-core-impl/nm-dbus-utils.c +++ /dev/null @@ -1,259 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#include "libnm-core-impl/nm-default-libnm-core.h" - -#include "libnm-core-intern/nm-core-internal.h" - -typedef struct { - char *signal_name; - const GVariantType *signature; -} NMDBusSignalData; - -static void -dbus_signal_data_free(gpointer data, GClosure *closure) -{ - NMDBusSignalData *sd = data; - - g_free(sd->signal_name); - g_slice_free(NMDBusSignalData, sd); -} - -static void -dbus_signal_meta_marshal(GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data) -{ - NMDBusSignalData *sd = marshal_data; - const char *signal_name; - GVariant *parameters, *param; - GValue *closure_params; - gsize n_params, i; - - g_return_if_fail(n_param_values == 4); - - signal_name = g_value_get_string(¶m_values[2]); - parameters = g_value_get_variant(¶m_values[3]); - - if (strcmp(signal_name, sd->signal_name) != 0) - return; - - if (sd->signature) { - if (!g_variant_is_of_type(parameters, sd->signature)) { - g_warning("%p: got signal '%s' but parameters were of type '%s', not '%s'", - g_value_get_object(¶m_values[0]), - signal_name, - g_variant_get_type_string(parameters), - g_variant_type_peek_string(sd->signature)); - return; - } - - n_params = g_variant_n_children(parameters) + 1; - } else - n_params = 1; - - closure_params = g_new0(GValue, n_params); - g_value_init(&closure_params[0], G_TYPE_OBJECT); - g_value_copy(¶m_values[0], &closure_params[0]); - - for (i = 1; i < n_params; i++) { - param = g_variant_get_child_value(parameters, i - 1); - if (g_variant_is_of_type(param, G_VARIANT_TYPE("ay")) - || g_variant_is_of_type(param, G_VARIANT_TYPE("aay"))) { - /* g_dbus_gvariant_to_gvalue() thinks 'ay' means "non-UTF-8 NUL-terminated string" */ - g_value_init(&closure_params[i], G_TYPE_VARIANT); - g_value_set_variant(&closure_params[i], param); - } else - g_dbus_gvariant_to_gvalue(param, &closure_params[i]); - g_variant_unref(param); - } - - g_cclosure_marshal_generic(closure, NULL, n_params, closure_params, invocation_hint, NULL); - - for (i = 0; i < n_params; i++) - g_value_unset(&closure_params[i]); - g_free(closure_params); -} - -/** - * _nm_dbus_signal_connect_data: - * @proxy: a #GDBusProxy - * @signal_name: the D-Bus signal to connect to - * @signature: (allow-none): the signal's type signature (must be a tuple) - * @c_handler: the signal handler function - * @data: (allow-none): data to pass to @c_handler - * @destroy_data: (allow-none): closure destroy notify for @data - * @connect_flags: connection flags - * - * Connects to the D-Bus signal @signal_name on @proxy. @c_handler must be a - * void function whose first argument is a #GDBusProxy, followed by arguments - * for each element of @signature, ending with a #gpointer argument for @data. - * - * The argument types in @c_handler correspond to the types output by - * g_dbus_gvariant_to_gvalue(), except for 'ay' and 'aay'. In particular: - * - both 16-bit and 32-bit integers are passed as #int/#guint - * - 'as' values are passed as #GStrv (char **) - * - all other array, tuple, and dict types are passed as #GVariant - * - * If @signature is %NULL, then the signal's parameters will be ignored, and - * @c_handler should take only the #GDBusProxy and #gpointer arguments. - * - * Returns: the signal handler ID, which can be used with - * g_signal_handler_remove(). Beware that because of the way the signal is - * connected, you will not be able to remove it with - * g_signal_handlers_disconnect_by_func(), although - * g_signal_handlers_disconnect_by_data() will work correctly. - */ -gulong -_nm_dbus_signal_connect_data(GDBusProxy *proxy, - const char *signal_name, - const GVariantType *signature, - GCallback c_handler, - gpointer data, - GClosureNotify destroy_data, - GConnectFlags connect_flags) -{ - NMDBusSignalData *sd; - GClosure *closure; - gboolean swapped = !!(connect_flags & G_CONNECT_SWAPPED); - gboolean after = !!(connect_flags & G_CONNECT_AFTER); - - g_return_val_if_fail(G_IS_DBUS_PROXY(proxy), 0); - g_return_val_if_fail(signal_name != NULL, 0); - g_return_val_if_fail(signature == NULL || g_variant_type_is_tuple(signature), 0); - g_return_val_if_fail(c_handler != NULL, 0); - - sd = g_slice_new(NMDBusSignalData); - sd->signal_name = g_strdup(signal_name); - sd->signature = signature; - - closure = (swapped ? g_cclosure_new_swap : g_cclosure_new)(c_handler, data, destroy_data); - g_closure_set_marshal(closure, g_cclosure_marshal_generic); - g_closure_set_meta_marshal(closure, sd, dbus_signal_meta_marshal); - g_closure_add_finalize_notifier(closure, sd, dbus_signal_data_free); - - return g_signal_connect_closure(proxy, "g-signal", closure, after); -} - -/** - * _nm_dbus_signal_connect: - * @proxy: a #GDBusProxy - * @signal_name: the D-Bus signal to connect to - * @signature: the signal's type signature (must be a tuple) - * @c_handler: the signal handler function - * @data: (allow-none): data to pass to @c_handler - * - * Simplified version of _nm_dbus_signal_connect_data() with fewer arguments. - * - * Returns: the signal handler ID, as with _nm_signal_connect_data(). - */ - -/** - * _nm_dbus_typecheck_response: - * @response: the #GVariant response to check. - * @reply_type: the expected reply type. It may be %NULL to perform no - * checking. - * @error: (allow-none): the error in case the @reply_type does not match. - * - * Returns: %TRUE, if @response is of the expected @reply_type. - */ -gboolean -_nm_dbus_typecheck_response(GVariant *response, const GVariantType *reply_type, GError **error) -{ - g_return_val_if_fail(response, FALSE); - - if (!reply_type) - return TRUE; - if (g_variant_is_of_type(response, reply_type)) - return TRUE; - - /* This is the same error code that g_dbus_connection_call() returns if - * @reply_type doesn't match. - */ - g_set_error(error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("Method returned type '%s', but expected '%s'"), - g_variant_get_type_string(response), - g_variant_type_peek_string(reply_type)); - return FALSE; -} - -/** - * _nm_dbus_proxy_call_finish: - * @proxy: A #GDBusProxy. - * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to - * g_dbus_proxy_call(). - * @reply_type: (allow-none): the expected type of the reply, or %NULL - * @error: Return location for error or %NULL. - * - * Finishes an operation started with g_dbus_proxy_call(), as with - * g_dbus_proxy_call_finish(), except thatif @reply_type is non-%NULL, then it - * will also check that the response matches that type signature, and return - * an error if not. - * - * Returns: %NULL if @error is set. Otherwise, a #GVariant tuple with - * return values. Free with g_variant_unref(). - */ -GVariant * -_nm_dbus_proxy_call_finish(GDBusProxy *proxy, - GAsyncResult *res, - const GVariantType *reply_type, - GError **error) -{ - GVariant *variant; - - variant = g_dbus_proxy_call_finish(proxy, res, error); - if (variant && !_nm_dbus_typecheck_response(variant, reply_type, error)) - nm_clear_pointer(&variant, g_variant_unref); - return variant; -} - -GVariant * -_nm_dbus_connection_call_finish(GDBusConnection *dbus_connection, - GAsyncResult *result, - const GVariantType *reply_type, - GError **error) -{ - GVariant *variant; - - variant = g_dbus_connection_call_finish(dbus_connection, result, error); - if (variant && !_nm_dbus_typecheck_response(variant, reply_type, error)) - nm_clear_pointer(&variant, g_variant_unref); - return variant; -} - -/** - * _nm_dbus_error_has_name: - * @error: (allow-none): a #GError, or %NULL - * @dbus_error_name: a D-Bus error name - * - * Checks if @error is set and corresponds to the D-Bus error @dbus_error_name. - * - * This should only be used for "foreign" D-Bus errors (eg, errors - * from BlueZ or wpa_supplicant). All NetworkManager D-Bus errors - * should be properly mapped by gdbus to one of the domains/codes in - * nm-errors.h. - * - * Returns: %TRUE or %FALSE - */ -gboolean -_nm_dbus_error_has_name(GError *error, const char *dbus_error_name) -{ - gboolean has_name = FALSE; - - if (error && g_dbus_error_is_remote_error(error)) { - char *error_name; - - error_name = g_dbus_error_get_remote_error(error); - has_name = !g_strcmp0(error_name, dbus_error_name); - g_free(error_name); - } - - return has_name; -} diff --git a/src/libnm-core-impl/nm-default-libnm-core.h b/src/libnm-core-impl/nm-default-libnm-core.h index 0f27f82d..013df67d 100644 --- a/src/libnm-core-impl/nm-default-libnm-core.h +++ b/src/libnm-core-impl/nm-default-libnm-core.h @@ -16,6 +16,7 @@ /*****************************************************************************/ #include "nm-version.h" +#include "nm-errors.h" /*****************************************************************************/ diff --git a/src/libnm-core-impl/nm-keyfile.c b/src/libnm-core-impl/nm-keyfile.c index 6ee68b5c..9a6ffc6f 100644 --- a/src/libnm-core-impl/nm-keyfile.c +++ b/src/libnm-core-impl/nm-keyfile.c @@ -28,12 +28,14 @@ #include "nm-setting-private.h" #include "nm-setting-user.h" #include "nm-setting-ovs-external-ids.h" +#include "nm-setting-ovs-other-config.h" #include "libnm-core-intern/nm-keyfile-utils.h" #define ETHERNET_S390_OPTIONS_GROUP_NAME "ethernet-s390-options" -#define OVS_EXTERNAL_IDS_DATA_PREFIX "data." +/* used for "ovs-external-ids.data" and "ovs-other-config.data". */ +#define STRDICT_DATA_PREFIX "data." /*****************************************************************************/ @@ -397,7 +399,7 @@ build_route(KeyfileReaderInfo *info, /* Next hop */ if (gateway_str && gateway_str[0]) { - if (!nm_utils_ipaddr_is_valid(family, gateway_str)) { + if (!nm_inet_is_valid(family, gateway_str)) { /* Try workaround for routes written by broken keyfile writer. * Due to bug bgo#719851, an older version of writer would have * written "a:b:c:d::/plen,metric" if the gateway was ::, instead @@ -1060,23 +1062,29 @@ ip_routing_rule_parser_full(KeyfileReaderInfo *info, } static void -_parser_full_ovs_external_ids_data(KeyfileReaderInfo *info, - const NMMetaSettingInfo *setting_info, - const NMSettInfoProperty *property_info, - const ParseInfoProperty *pip, - NMSetting *setting) +_parser_full_strdict_data(KeyfileReaderInfo *info, + const NMMetaSettingInfo *setting_info, + const NMSettInfoProperty *property_info, + const ParseInfoProperty *pip, + NMSetting *setting) { - const char *setting_name = NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME; - gs_strfreev char **keys = NULL; + gs_strfreev char **keys = NULL; gsize n_keys; gsize i; + gboolean is_exid; - nm_assert(NM_IS_SETTING_OVS_EXTERNAL_IDS(setting)); - nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA)); - nm_assert(nm_streq(setting_name, setting_info->setting_name)); - nm_assert(nm_streq(setting_name, nm_setting_get_name(setting))); + if (NM_IS_SETTING_OVS_EXTERNAL_IDS(setting)) { + nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA)); + is_exid = TRUE; + } else { + nm_assert(NM_IS_SETTING_OVS_OTHER_CONFIG(setting)); + nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_OTHER_CONFIG_DATA)); + is_exid = FALSE; + } - keys = nm_keyfile_plugin_kf_get_keys(info->keyfile, setting_name, &n_keys, NULL); + nm_assert(nm_streq(setting_info->setting_name, nm_setting_get_name(setting))); + + keys = nm_keyfile_plugin_kf_get_keys(info->keyfile, setting_info->setting_name, &n_keys, NULL); for (i = 0; i < n_keys; i++) { const char *key = keys[i]; @@ -1084,16 +1092,20 @@ _parser_full_ovs_external_ids_data(KeyfileReaderInfo *info, gs_free char *value = NULL; const char *name; - if (!NM_STR_HAS_PREFIX(key, OVS_EXTERNAL_IDS_DATA_PREFIX)) + if (!NM_STR_HAS_PREFIX(key, STRDICT_DATA_PREFIX)) continue; - value = nm_keyfile_plugin_kf_get_string(info->keyfile, setting_name, key, NULL); + value = + nm_keyfile_plugin_kf_get_string(info->keyfile, setting_info->setting_name, key, NULL); if (!value) continue; - name = &key[NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)]; + name = &key[NM_STRLEN(STRDICT_DATA_PREFIX)]; name = nm_keyfile_key_decode(name, &name_to_free); - nm_setting_ovs_external_ids_set_data(NM_SETTING_OVS_EXTERNAL_IDS(setting), name, value); + if (is_exid) + nm_setting_ovs_external_ids_set_data(NM_SETTING_OVS_EXTERNAL_IDS(setting), name, value); + else + nm_setting_ovs_other_config_set_data(NM_SETTING_OVS_OTHER_CONFIG(setting), name, value); } } @@ -1102,7 +1114,9 @@ ip_dns_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) { int addr_family; gs_strfreev char **list = NULL; - gsize i, n, length; + gsize length; + gsize n; + gsize i; nm_assert(NM_IS_SETTING_IP4_CONFIG(setting) || NM_IS_SETTING_IP6_CONFIG(setting)); @@ -1115,13 +1129,10 @@ ip_dns_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) if (length == 0) return; - addr_family = NM_IS_SETTING_IP4_CONFIG(setting) ? AF_INET : AF_INET6; - - n = 0; - for (i = 0; i < length; i++) { - NMIPAddr addr; + addr_family = NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting); - if (inet_pton(addr_family, list[i], &addr) <= 0) { + for (i = 0, n = 0; i < length; i++) { + if (!nm_utils_dnsname_parse(addr_family, list[i], NULL, NULL, NULL)) { if (!read_handle_warn(info, key, key, @@ -2008,6 +2019,44 @@ bridge_vlan_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) g_object_set(setting, key, vlans, NULL); } +static void +range_list_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) +{ + gs_unref_ptrarray GPtrArray *ranges = NULL; + gs_free char *value = NULL; + gs_free const char **strv = NULL; + const char *const *iter; + GError *local = NULL; + NMRange *range; + + value = nm_keyfile_plugin_kf_get_string(info->keyfile, nm_setting_get_name(setting), key, NULL); + if (!value || !value[0]) + return; + + ranges = g_ptr_array_new_with_free_func((GDestroyNotify) nm_range_unref); + + strv = nm_utils_escaped_tokens_split(value, ","); + if (strv) { + for (iter = strv; *iter; iter++) { + range = nm_range_from_str(*iter, &local); + if (!range) { + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + "invalid range: %s", + local->message); + g_clear_error(&local); + continue; + } + g_ptr_array_add(ranges, range); + } + } + + if (ranges->len > 0) + g_object_set(setting, key, ranges, NULL); +} + static void qdisc_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) { @@ -2173,7 +2222,7 @@ write_array_of_uint(GKeyFile *file, NMSetting *setting, const char *key, const G nm_keyfile_plugin_kf_set_integer_list_uint(file, nm_setting_get_name(setting), key, - (const guint *) array->data, + &nm_g_array_first(array, const guint), array->len); } @@ -2345,6 +2394,33 @@ bridge_vlan_writer(KeyfileWriterInfo *info, } } +static void +range_list_writer(KeyfileWriterInfo *info, NMSetting *setting, const char *key, const GValue *value) +{ + GPtrArray *ranges; + + ranges = g_value_get_boxed(value); + if (ranges && ranges->len > 0) { + const guint string_initial_size = ranges->len * 10u; + nm_auto_str_buf NMStrBuf string = NM_STR_BUF_INIT(string_initial_size, FALSE); + guint i; + + for (i = 0; i < ranges->len; i++) { + gs_free char *range_str = NULL; + + range_str = nm_range_to_str(ranges->pdata[i]); + if (i > 0) + nm_str_buf_append_c(&string, ','); + nm_utils_escaped_tokens_escape_strbuf_assert(range_str, ",", &string); + } + + nm_keyfile_plugin_kf_set_string(info->keyfile, + nm_setting_get_name(setting), + key, + nm_str_buf_get_str(&string)); + } +} + static void wired_s390_options_parser_full(KeyfileReaderInfo *info, const NMMetaSettingInfo *setting_info, @@ -2505,24 +2581,32 @@ tfilter_writer(KeyfileWriterInfo *info, NMSetting *setting, const char *key, con } static void -_writer_full_ovs_external_ids_data(KeyfileWriterInfo *info, - const NMMetaSettingInfo *setting_info, - const NMSettInfoProperty *property_info, - const ParseInfoProperty *pip, - NMSetting *setting) +_writer_full_strdict_data(KeyfileWriterInfo *info, + const NMMetaSettingInfo *setting_info, + const NMSettInfoProperty *property_info, + const ParseInfoProperty *pip, + NMSetting *setting) { GHashTable *hash; NMUtilsNamedValue data_static[300u / sizeof(NMUtilsNamedValue)]; gs_free NMUtilsNamedValue *data_free = NULL; const NMUtilsNamedValue *data; guint data_len; - char full_key_static[NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX) + 300u]; + char full_key_static[NM_STRLEN(STRDICT_DATA_PREFIX) + 300u]; guint i; + gboolean is_exid; - nm_assert(NM_IS_SETTING_OVS_EXTERNAL_IDS(setting)); - nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA)); + if (NM_IS_SETTING_OVS_EXTERNAL_IDS(setting)) { + nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA)); + is_exid = TRUE; + } else { + nm_assert(NM_IS_SETTING_OVS_OTHER_CONFIG(setting)); + nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_OTHER_CONFIG_DATA)); + is_exid = FALSE; + } - hash = _nm_setting_ovs_external_ids_get_data(NM_SETTING_OVS_EXTERNAL_IDS(setting)); + hash = is_exid ? _nm_setting_ovs_external_ids_get_data(NM_SETTING_OVS_EXTERNAL_IDS(setting)) + : _nm_setting_ovs_other_config_get_data(NM_SETTING_OVS_OTHER_CONFIG(setting)); if (!hash) return; @@ -2530,7 +2614,7 @@ _writer_full_ovs_external_ids_data(KeyfileWriterInfo *info, if (data_len == 0) return; - memcpy(full_key_static, OVS_EXTERNAL_IDS_DATA_PREFIX, NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)); + memcpy(full_key_static, STRDICT_DATA_PREFIX, NM_STRLEN(STRDICT_DATA_PREFIX)); for (i = 0; i < data_len; i++) { const char *key = data[i].name; @@ -2544,15 +2628,16 @@ _writer_full_ovs_external_ids_data(KeyfileWriterInfo *info, escaped_key = nm_keyfile_key_encode(key, &escaped_key_to_free); len = strlen(escaped_key) + 1u; - if (len >= G_N_ELEMENTS(full_key_static) - NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)) { - full_key_free = g_new(char, NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX) + len); + if (len >= G_N_ELEMENTS(full_key_static) - NM_STRLEN(STRDICT_DATA_PREFIX)) { + full_key_free = g_new(char, NM_STRLEN(STRDICT_DATA_PREFIX) + len); full_key = full_key_free; - memcpy(full_key, OVS_EXTERNAL_IDS_DATA_PREFIX, NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)); + memcpy(full_key, STRDICT_DATA_PREFIX, NM_STRLEN(STRDICT_DATA_PREFIX)); } - memcpy(&full_key[NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)], escaped_key, len); + memcpy(&full_key[NM_STRLEN(STRDICT_DATA_PREFIX)], escaped_key, len); nm_keyfile_plugin_kf_set_string(info->keyfile, - NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME, + is_exid ? NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME + : NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME, full_key, val); } @@ -2934,6 +3019,12 @@ static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = { .parser_no_check_key = TRUE, .parser = bridge_vlan_parser, .writer = bridge_vlan_writer, ), ), ), + PARSE_INFO_SETTING( + NM_META_SETTING_TYPE_OVS_PORT, + PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_OVS_PORT_TRUNKS, + .parser_no_check_key = TRUE, + .parser = range_list_parser, + .writer = range_list_writer, ), ), ), PARSE_INFO_SETTING( NM_META_SETTING_TYPE_BRIDGE_PORT, PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_BRIDGE_PORT_VLANS, @@ -3007,10 +3098,18 @@ static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = { NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_OVS_EXTERNAL_IDS_DATA, .parser_no_check_key = TRUE, - .parser_full = _parser_full_ovs_external_ids_data, - .writer_full = _writer_full_ovs_external_ids_data, - .has_parser_full = TRUE, - .has_writer_full = TRUE, ), ), ), + .parser_full = _parser_full_strdict_data, + .writer_full = _writer_full_strdict_data, + .has_parser_full = TRUE, + .has_writer_full = TRUE, ), ), ), + PARSE_INFO_SETTING( + NM_META_SETTING_TYPE_OVS_OTHER_CONFIG, + PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_OVS_OTHER_CONFIG_DATA, + .parser_no_check_key = TRUE, + .parser_full = _parser_full_strdict_data, + .writer_full = _writer_full_strdict_data, + .has_parser_full = TRUE, + .has_writer_full = TRUE, ), ), ), PARSE_INFO_SETTING(NM_META_SETTING_TYPE_SERIAL, PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_SERIAL_PARITY, .parser = parity_parser, ), ), ), @@ -3188,11 +3287,11 @@ _parse_info_find(NMSetting *setting, G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(ParseInfoProperty, property_name) == 0); - idx = nm_utils_ptrarray_find_binary_search((gconstpointer *) pis->properties, - NM_PTRARRAY_LEN(pis->properties), - &property_name, - nm_strcmp_p_with_data, - NULL); + idx = nm_ptrarray_find_bsearch((gconstpointer *) pis->properties, + NM_PTRARRAY_LEN(pis->properties), + &property_name, + nm_strcmp_p_with_data, + NULL); if (idx >= 0) pip = pis->properties[idx]; } @@ -3692,7 +3791,7 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) gsize i; for (i = 0; i < n_sa; i++) { - if (!nm_utils_parse_inaddr_prefix_bin(AF_UNSPEC, sa[i], NULL, NULL, NULL)) { + if (!nm_inet_parse_with_prefix_bin(AF_UNSPEC, sa[i], NULL, NULL, NULL)) { has_error = TRUE; continue; } @@ -3800,7 +3899,7 @@ nm_keyfile_read_ensure_uuid(NMConnection *connection, const char *fallback_uuid_ if (nm_setting_connection_get_uuid(s_con)) return FALSE; - hashed_uuid = nm_uuid_generate_from_strings("keyfile", fallback_uuid_seed, NULL); + hashed_uuid = nm_uuid_generate_from_strings_old("keyfile", fallback_uuid_seed); g_object_set(s_con, NM_SETTING_CONNECTION_UUID, hashed_uuid, NULL); return TRUE; } diff --git a/src/libnm-core-impl/nm-meta-setting-base-impl.c b/src/libnm-core-impl/nm-meta-setting-base-impl.c index 69daa76c..19082671 100644 --- a/src/libnm-core-impl/nm-meta-setting-base-impl.c +++ b/src/libnm-core-impl/nm-meta-setting-base-impl.c @@ -3,6 +3,8 @@ * Copyright (C) 2017 - 2018 Red Hat, Inc. */ +#define NM_WANT_NM_ARRAY_FIND_BSEARCH_INLINE + #include "libnm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-meta-setting-base.h" @@ -32,14 +34,16 @@ #include "nm-setting-ip-tunnel.h" #include "nm-setting-ip4-config.h" #include "nm-setting-ip6-config.h" +#include "nm-setting-loopback.h" #include "nm-setting-macsec.h" #include "nm-setting-macvlan.h" #include "nm-setting-match.h" #include "nm-setting-olpc-mesh.h" #include "nm-setting-ovs-bridge.h" -#include "nm-setting-ovs-interface.h" #include "nm-setting-ovs-dpdk.h" #include "nm-setting-ovs-external-ids.h" +#include "nm-setting-ovs-interface.h" +#include "nm-setting-ovs-other-config.h" #include "nm-setting-ovs-patch.h" #include "nm-setting-ovs-port.h" #include "nm-setting-ppp.h" @@ -358,6 +362,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = { .setting_name = NM_SETTING_IP_TUNNEL_SETTING_NAME, .get_setting_gtype = nm_setting_ip_tunnel_get_type, }, + [NM_META_SETTING_TYPE_LOOPBACK] = + { + .meta_type = NM_META_SETTING_TYPE_LOOPBACK, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_LOOPBACK_SETTING_NAME, + .get_setting_gtype = nm_setting_loopback_get_type, + }, [NM_META_SETTING_TYPE_MACSEC] = { .meta_type = NM_META_SETTING_TYPE_MACSEC, @@ -400,6 +411,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = { .setting_name = NM_SETTING_OVS_DPDK_SETTING_NAME, .get_setting_gtype = nm_setting_ovs_dpdk_get_type, }, + [NM_META_SETTING_TYPE_OVS_OTHER_CONFIG] = + { + .meta_type = NM_META_SETTING_TYPE_OVS_OTHER_CONFIG, + .setting_priority = NM_SETTING_PRIORITY_AUX, + .setting_name = NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME, + .get_setting_gtype = nm_setting_ovs_other_config_get_type, + }, [NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS] = { .meta_type = NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, @@ -608,6 +626,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = { NM_META_SETTING_TYPE_GSM, NM_META_SETTING_TYPE_INFINIBAND, NM_META_SETTING_TYPE_IP_TUNNEL, + NM_META_SETTING_TYPE_LOOPBACK, NM_META_SETTING_TYPE_MACSEC, NM_META_SETTING_TYPE_MACVLAN, NM_META_SETTING_TYPE_OVS_BRIDGE, @@ -643,6 +662,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = { NM_META_SETTING_TYPE_ETHTOOL, NM_META_SETTING_TYPE_MATCH, NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, + NM_META_SETTING_TYPE_OVS_OTHER_CONFIG, NM_META_SETTING_TYPE_PPP, NM_META_SETTING_TYPE_PPPOE, NM_META_SETTING_TYPE_TEAM_PORT, @@ -693,29 +713,31 @@ nm_meta_setting_infos_by_name(const char *name) } G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMMetaSettingInfo, setting_name) == 0); - idx = nm_utils_array_find_binary_search(nm_meta_setting_infos, - sizeof(NMMetaSettingInfo), - _NM_META_SETTING_TYPE_NUM, - &name, - nm_strcmp_p_with_data, - NULL); + idx = nm_array_find_bsearch(nm_meta_setting_infos, + _NM_META_SETTING_TYPE_NUM, + sizeof(NMMetaSettingInfo), + &name, + nm_strcmp_p_with_data, + NULL); return idx >= 0 ? &nm_meta_setting_infos[idx] : NULL; } -const NMMetaSettingInfo * -nm_meta_setting_infos_by_gtype(GType gtype) -{ +/*****************************************************************************/ + #if _NM_META_SETTING_BASE_IMPL_LIBNM +static const NMMetaSettingInfo * +_infos_by_gtype_from_class(GType gtype) +{ nm_auto_unref_gtypeclass GTypeClass *gtypeclass_unref = NULL; GTypeClass *gtypeclass; NMSettingClass *klass; if (!g_type_is_a(gtype, NM_TYPE_SETTING)) - goto out_none; + return NULL; gtypeclass = g_type_class_peek(gtype); - if (!gtypeclass) + if (G_UNLIKELY(!gtypeclass)) gtypeclass = gtypeclass_unref = g_type_class_ref(gtype); nm_assert(NM_IS_SETTING_CLASS(gtypeclass)); @@ -723,38 +745,112 @@ nm_meta_setting_infos_by_gtype(GType gtype) klass = (NMSettingClass *) gtypeclass; if (!klass->setting_info) - goto out_none; + return NULL; nm_assert(klass->setting_info->get_setting_gtype); nm_assert(klass->setting_info->get_setting_gtype() == gtype); - return klass->setting_info; +} +#endif -out_none: - - if (NM_MORE_ASSERTS > 10) { - int i; - - /* this might hint to a bug, but it would be expected for NM_TYPE_SETTING - * and NM_TYPE_SETTING_IP_CONFIG. - * - * Assert that we didn't lookup for a gtype, which we would expect to find. - * An assertion failure here, hints to a bug in nm_setting_*_class_init(). - */ - for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) - nm_assert(nm_meta_setting_infos[i].get_setting_gtype() != gtype); - } - - return NULL; -#else - guint i; +static const NMMetaSettingInfo * +_infos_by_gtype_search(GType gtype) +{ + int i; - for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) { + for (i = 0; i < (int) _NM_META_SETTING_TYPE_NUM; i++) { if (nm_meta_setting_infos[i].get_setting_gtype() == gtype) return &nm_meta_setting_infos[i]; } return NULL; +} + +typedef struct { + GType gtype; + const NMMetaSettingInfo *setting_info; +} LookupData; + +_nm_always_inline static inline int +_lookup_data_cmp(gconstpointer ptr_a, gconstpointer ptr_b, gpointer user_data) +{ + const GType *const a = ptr_a; + const GType *const b = ptr_b; + + nm_assert(a); + nm_assert(b); + nm_assert(a != b); + + NM_CMP_DIRECT(*a, *b); + return 0; +} + +static const NMMetaSettingInfo * +_infos_by_gtype_binary_search(GType gtype) +{ + static LookupData static_array[_NM_META_SETTING_TYPE_NUM]; + static const LookupData *static_ptr = NULL; + const LookupData *ptr; + gssize idx; + +again: + ptr = g_atomic_pointer_get(&static_ptr); + if (G_UNLIKELY(!ptr)) { + static gsize g_lock = 0; + int i; + + if (!g_once_init_enter(&g_lock)) + goto again; + + for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) { + const NMMetaSettingInfo *m = &nm_meta_setting_infos[i]; + + static_array[i] = (LookupData){ + .gtype = m->get_setting_gtype(), + .setting_info = m, + }; + } + + g_qsort_with_data(static_array, + _NM_META_SETTING_TYPE_NUM, + sizeof(static_array[0]), + _lookup_data_cmp, + NULL); + + ptr = static_array; + g_atomic_pointer_set(&static_ptr, ptr); + + g_once_init_leave(&g_lock, 1); + } + + idx = nm_array_find_bsearch_inline(ptr, + _NM_META_SETTING_TYPE_NUM, + sizeof(ptr[0]), + >ype, + _lookup_data_cmp, + NULL); + if (idx < 0) + return NULL; + + return ptr[idx].setting_info; +} + +const NMMetaSettingInfo * +nm_meta_setting_infos_by_gtype(GType gtype) +{ + const NMMetaSettingInfo *setting_info; + +#if _NM_META_SETTING_BASE_IMPL_LIBNM + setting_info = _infos_by_gtype_from_class(gtype); +#else + setting_info = _infos_by_gtype_binary_search(gtype); #endif + + if (NM_MORE_ASSERTS > 20) { + nm_assert(setting_info == _infos_by_gtype_search(gtype)); + nm_assert(setting_info == _infos_by_gtype_binary_search(gtype)); + } + + return setting_info; } /*****************************************************************************/ diff --git a/src/libnm-core-impl/nm-setting-6lowpan.c b/src/libnm-core-impl/nm-setting-6lowpan.c index 21b3dbb9..e0b5d650 100644 --- a/src/libnm-core-impl/nm-setting-6lowpan.c +++ b/src/libnm-core-impl/nm-setting-6lowpan.c @@ -53,7 +53,7 @@ G_DEFINE_TYPE(NMSetting6Lowpan, nm_setting_6lowpan, NM_TYPE_SETTING) * * Returns: the #NMSetting6Lowpan:parent property of the setting * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ const char * nm_setting_6lowpan_get_parent(NMSetting6Lowpan *setting) @@ -141,7 +141,7 @@ nm_setting_6lowpan_init(NMSetting6Lowpan *setting) * * Returns: (transfer full): the new empty #NMSetting6Lowpan object * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ NMSetting * nm_setting_6lowpan_new(void) diff --git a/src/libnm-core-impl/nm-setting-8021x.c b/src/libnm-core-impl/nm-setting-8021x.c index fd9a0961..1bc4de9d 100644 --- a/src/libnm-core-impl/nm-setting-8021x.c +++ b/src/libnm-core-impl/nm-setting-8021x.c @@ -67,7 +67,10 @@ _crypto_format_to_ck(NMCryptoFileFormat format) /*****************************************************************************/ -typedef void (*EAPMethodNeedSecretsFunc)(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2); +typedef void (*EAPMethodNeedSecretsFunc)(NMSetting8021x *self, + GPtrArray *secrets, + gboolean phase2, + gboolean check_rerequest); typedef gboolean (*EAPMethodValidateFunc)(NMSetting8021x *self, gboolean phase2, GError **error); @@ -1601,7 +1604,7 @@ nm_setting_802_1x_get_phase2_ca_cert_path(NMSetting8021x *setting) * nm_setting_802_1x_get_phase2_ca_cert_blob() and * nm_setting_802_1x_get_phase2_ca_cert_path(). * - * Currently, it's limited to PKCS#11 URIs ('pkcs11' scheme as defined by RFC + * Currently, it's limited to PKCS#11 URIs ('pkcs11' scheme as defined by RFC * 7512), but may be extended to other schemes in future (such as 'file' URIs * for local files and 'data' URIs for inline certificate data). * @@ -1937,7 +1940,7 @@ nm_setting_802_1x_get_phase2_client_cert_path(NMSetting8021x *setting) * nm_setting_802_1x_get_phase2_ca_cert_blob() and * nm_setting_802_1x_get_phase2_ca_cert_path(). * - * Currently, it's limited to PKCS#11 URIs ('pkcs11' scheme as defined by RFC + * Currently, it's limited to PKCS#11 URIs ('pkcs11' scheme as defined by RFC * 7512), but may be extended to other schemes in future (such as 'file' URIs * for local files and 'data' URIs for inline certificate data). * @@ -2173,7 +2176,7 @@ nm_setting_802_1x_get_private_key_path(NMSetting8021x *setting) * nm_setting_802_1x_get_private_key_blob() and * nm_setting_802_1x_get_private_key_path(). * - * Currently, it's limited to PKCS#11 URIs ('pkcs11' scheme as defined by RFC + * Currently, it's limited to PKCS#11 URIs ('pkcs11' scheme as defined by RFC * 7512), but may be extended to other schemes in future (such as 'file' URIs * for local files and 'data' URIs for inline certificate data). * @@ -2376,7 +2379,7 @@ nm_setting_802_1x_get_phase2_private_key_path(NMSetting8021x *setting) * nm_setting_802_1x_get_phase2_private_key_blob() and * nm_setting_802_1x_get_phase2_private_key_path(). * - * Currently, it's limited to PKCS#11 URIs ('pkcs11' scheme as defined by RFC + * Currently, it's limited to PKCS#11 URIs ('pkcs11' scheme as defined by RFC * 7512), but may be extended to other schemes in future (such as 'file' URIs * for local files and 'data' URIs for inline certificate data). * @@ -2500,32 +2503,45 @@ nm_setting_802_1x_get_optional(NMSetting8021x *setting) /*****************************************************************************/ static void -need_secrets_password(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2) +need_secrets_password(NMSetting8021x *self, + GPtrArray *secrets, + gboolean phase2, + gboolean check_rerequest) { NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); - if (nm_str_is_empty(priv->password) - && (!priv->password_raw || !g_bytes_get_size(priv->password_raw))) { + if (check_rerequest + || (nm_str_is_empty(priv->password) + && (!priv->password_raw || !g_bytes_get_size(priv->password_raw)))) { g_ptr_array_add(secrets, NM_SETTING_802_1X_PASSWORD); g_ptr_array_add(secrets, NM_SETTING_802_1X_PASSWORD_RAW); } } static void -need_secrets_sim(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2) +need_secrets_sim(NMSetting8021x *self, + GPtrArray *secrets, + gboolean phase2, + gboolean check_rerequest) { NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); - if (nm_str_is_empty(priv->pin)) + if (check_rerequest || nm_str_is_empty(priv->pin)) g_ptr_array_add(secrets, NM_SETTING_802_1X_PIN); } static void -need_secrets_tls(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2) +need_secrets_tls(NMSetting8021x *self, + GPtrArray *secrets, + gboolean phase2, + gboolean check_rerequest) { NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); NMSetting8021xCKScheme scheme; + /* If check_rerequest is TRUE do not return secrets, unless missing. + * This secret cannot be wrong. */ + if (!NM_FLAGS_HAS(phase2 ? priv->phase2_private_key_password_flags : priv->private_key_password_flags, NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { @@ -2719,7 +2735,10 @@ verify_ttls(NMSetting8021x *self, gboolean phase2, GError **error) } static void -need_secrets_phase2(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2) +need_secrets_phase2(NMSetting8021x *self, + GPtrArray *secrets, + gboolean phase2, + gboolean check_rerequest) { NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); char *method = NULL; @@ -2740,7 +2759,7 @@ need_secrets_phase2(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2) if (!eap_methods_table[i].ns_func) continue; if (nm_streq(eap_methods_table[i].method, method)) { - (*eap_methods_table[i].ns_func)(self, secrets, TRUE); + (*eap_methods_table[i].ns_func)(self, secrets, TRUE, check_rerequest); break; } } @@ -2885,10 +2904,35 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } if (NM_FLAGS_ANY(priv->phase1_auth_flags, ~((guint32) NM_SETTING_802_1X_AUTH_FLAGS_ALL))) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("invalid auth flags")); + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("invalid auth flags: '%d' contains unknown flags"), + priv->phase1_auth_flags); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_PHASE1_AUTH_FLAGS); + return FALSE; + } + + if (NM_FLAGS_ALL(priv->phase1_auth_flags, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_ENABLE + | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE) + || NM_FLAGS_ALL(priv->phase1_auth_flags, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_ENABLE + | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE) + || NM_FLAGS_ALL(priv->phase1_auth_flags, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_ENABLE + | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_DISABLE) + || NM_FLAGS_ALL(priv->phase1_auth_flags, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_3_ENABLE + | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_3_DISABLE)) { + g_set_error_literal( + error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("invalid auth flags: both enable and disable are set for the same TLS version")); g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, @@ -3030,7 +3074,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) /*****************************************************************************/ static GPtrArray * -need_secrets(NMSetting *setting) +need_secrets(NMSetting *setting, gboolean check_rerequest) { NMSetting8021x *self = NM_SETTING_802_1X(setting); NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); @@ -3049,7 +3093,7 @@ need_secrets(NMSetting *setting) if (eap_methods_table[i].ns_func == NULL) continue; if (!strcmp(eap_methods_table[i].method, method)) { - (*eap_methods_table[i].ns_func)(self, secrets, FALSE); + (*eap_methods_table[i].ns_func)(self, secrets, FALSE, check_rerequest); /* Only break out of the outer loop if this EAP method * needed secrets. @@ -3296,9 +3340,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass) * * Since: 1.8 **/ - /* ---ifcfg-rh--- - * ---end--- - */ _nm_setting_property_define_direct_string(properties_override, obj_properties, NM_SETTING_802_1X_CA_CERT_PASSWORD, @@ -3314,9 +3355,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass) * * Since: 1.8 **/ - /* ---ifcfg-rh--- - * ---end--- - */ _nm_setting_property_define_direct_secret_flags(properties_override, obj_properties, NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS, @@ -3353,9 +3391,10 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass) * * Substring to be matched against the subject of the certificate presented * by the authentication server. When unset, no verification of the - * authentication server certificate's subject is performed. This property - * provides little security, if any, and its use is deprecated in favor of - * NMSetting8021x:domain-suffix-match. + * authentication server certificate's subject is performed. This property + * provides little security, if any, and should not be used. + * + * Deprecated: 1.2: Use #NMSetting8021x:phase2-domain-suffix-match instead. **/ /* ---ifcfg-rh--- * property: subject-match @@ -3370,7 +3409,8 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass) PROP_SUBJECT_MATCH, NM_SETTING_PARAM_NONE, NMSetting8021xPrivate, - subject_match); + subject_match, + .is_deprecated = TRUE, ); /** * NMSetting8021x:altsubject-matches: @@ -3486,9 +3526,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass) * * Since: 1.8 **/ - /* ---ifcfg-rh--- - * ---end--- - */ _nm_setting_property_define_direct_string(properties_override, obj_properties, NM_SETTING_802_1X_CLIENT_CERT_PASSWORD, @@ -3504,9 +3541,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass) * * Since: 1.8 **/ - /* ---ifcfg-rh--- - * ---end--- - */ _nm_setting_property_define_direct_secret_flags(properties_override, obj_properties, NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS, @@ -3594,9 +3628,10 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass) * * Specifies authentication flags to use in "phase 1" outer * authentication using #NMSetting8021xAuthFlags options. - * The individual TLS versions can be explicitly disabled. If a certain - * TLS disable flag is not set, it is up to the supplicant to allow - * or forbid it. The TLS options map to tls_disable_tlsv1_x settings. + * The individual TLS versions can be explicitly disabled. TLS time checks + * can be also disabled. If a certain TLS disable flag is not + * set, it is up to the supplicant to allow or forbid it. The TLS options + * map to tls_disable_tlsv1_x and tls_disable_time_checks settings. * See the wpa_supplicant documentation for more details. * * Since: 1.8 @@ -3718,9 +3753,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass) * * Since: 1.8 **/ - /* ---ifcfg-rh--- - * ---end--- - */ _nm_setting_property_define_direct_string(properties_override, obj_properties, NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD, @@ -3736,9 +3768,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass) * * Since: 1.8 **/ - /* ---ifcfg-rh--- - * ---end--- - */ _nm_setting_property_define_direct_secret_flags(properties_override, obj_properties, NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS, @@ -3776,9 +3805,10 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass) * Substring to be matched against the subject of the certificate presented * by the authentication server during the inner "phase 2" * authentication. When unset, no verification of the authentication server - * certificate's subject is performed. This property provides little security, - * if any, and its use is deprecated in favor of - * NMSetting8021x:phase2-domain-suffix-match. + * certificate's subject is performed. This property provides little security, + * if any, and should not be used. + * + * Deprecated: 1.2: Use #NMSetting8021x:phase2-domain-suffix-match instead. **/ /* ---ifcfg-rh--- * property: phase2-subject-match @@ -3793,7 +3823,8 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass) PROP_PHASE2_SUBJECT_MATCH, NM_SETTING_PARAM_NONE, NMSetting8021xPrivate, - phase2_subject_match); + phase2_subject_match, + .is_deprecated = TRUE, ); /** * NMSetting8021x:phase2-altsubject-matches: @@ -3913,9 +3944,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass) * * Since: 1.8 **/ - /* ---ifcfg-rh--- - * ---end--- - */ _nm_setting_property_define_direct_string(properties_override, obj_properties, NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD, @@ -3931,9 +3959,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass) * * Since: 1.8 **/ - /* ---ifcfg-rh--- - * ---end--- - */ _nm_setting_property_define_direct_secret_flags( properties_override, obj_properties, diff --git a/src/libnm-core-impl/nm-setting-adsl.c b/src/libnm-core-impl/nm-setting-adsl.c index 88606b69..ca291389 100644 --- a/src/libnm-core-impl/nm-setting-adsl.c +++ b/src/libnm-core-impl/nm-setting-adsl.c @@ -225,12 +225,12 @@ verify_secrets(NMSetting *setting, NMConnection *connection, GError **error) } static GPtrArray * -need_secrets(NMSetting *setting) +need_secrets(NMSetting *setting, gboolean check_rerequest) { NMSettingAdslPrivate *priv = NM_SETTING_ADSL_GET_PRIVATE(setting); GPtrArray *secrets = NULL; - if (priv->password && *priv->password) + if (!check_rerequest && priv->password && *priv->password) return NULL; if (!(priv->password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { diff --git a/src/libnm-core-impl/nm-setting-bond.c b/src/libnm-core-impl/nm-setting-bond.c index 18b6fefb..54030e0b 100644 --- a/src/libnm-core-impl/nm-setting-bond.c +++ b/src/libnm-core-impl/nm-setting-bond.c @@ -70,6 +70,7 @@ static const char *const valid_options_lst[] = { NM_SETTING_BOND_OPTION_ARP_INTERVAL, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, NM_SETTING_BOND_OPTION_ARP_VALIDATE, + NM_SETTING_BOND_OPTION_BALANCE_SLB, NM_SETTING_BOND_OPTION_PRIMARY, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, NM_SETTING_BOND_OPTION_FAIL_OVER_MAC, @@ -195,6 +196,7 @@ static NM_UTILS_STRING_TABLE_LOOKUP_STRUCT_DEFINE( {NM_SETTING_BOND_OPTION_ARP_IP_TARGET, {"", NM_BOND_OPTION_TYPE_IP}}, {NM_SETTING_BOND_OPTION_ARP_VALIDATE, {"none", NM_BOND_OPTION_TYPE_BOTH, 0, 6, _option_default_strv_arp_validate}}, + {NM_SETTING_BOND_OPTION_BALANCE_SLB, {"0", NM_BOND_OPTION_TYPE_INT, 0, 1}}, {NM_SETTING_BOND_OPTION_DOWNDELAY, {"0", NM_BOND_OPTION_TYPE_INT, 0, G_MAXINT}}, {NM_SETTING_BOND_OPTION_FAIL_OVER_MAC, {"none", NM_BOND_OPTION_TYPE_BOTH, 0, 2, _option_default_strv_fail_over_mac}}, @@ -344,6 +346,17 @@ _bond_get_option_normalized(NMSettingBond *self, const char *option, gboolean ge value = _bond_get_option(self, NM_SETTING_BOND_OPTION_PRIMARY); if (!value) value = _bond_get_option(self, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE); + } else if (nm_streq(option, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY)) { + if (_nm_utils_ascii_str_to_int64( + _bond_get_option(self, NM_SETTING_BOND_OPTION_BALANCE_SLB), + 10, + 0, + 1, + -1) + == 1) { + /* balance-slb implies vlan+srcmac */ + return "5"; + } } else value = _bond_get_option(self, option); @@ -506,7 +519,7 @@ validate_ip(const char *name, const char *value, GError **error) return FALSE; } for (i = 0; addrs[i]; i++) { - if (!nm_utils_parse_inaddr_bin(AF_INET, addrs[i], NULL, NULL)) { + if (!nm_inet_parse_bin(AF_INET, addrs[i], NULL, NULL)) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -588,7 +601,7 @@ handle_error: /** * nm_setting_bond_validate_option: * @name: the name of the option to validate - * @value (allow-none): the value of the option to validate. + * @value: (allow-none): the value of the option to validate. * * Checks whether @name is a valid bond option and @value is a valid value for * the @name. If @value is %NULL, the function only validates the option name. @@ -840,6 +853,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) const char *arp_ip_target = NULL; const char *lacp_rate; const char *primary; + const char *s; NMBondMode bond_mode; guint i; const NMUtilsNamedValue *n; @@ -1067,6 +1081,32 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } + s = _bond_get_option(self, NM_SETTING_BOND_OPTION_BALANCE_SLB); + if (s && _atoi(s) > 0) { + if (bond_mode != NM_BOND_MODE_XOR) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("%s requires bond mode \"%s\""), + NM_SETTING_BOND_OPTION_BALANCE_SLB, + "balance-xor"); + g_prefix_error(error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + s = _bond_get_option(self, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY); + if (s + && _nm_setting_bond_xmit_hash_policy_from_string(s) + != NM_BOND_XMIT_HASH_POLICY_VLAN_SRCMAC) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("%s requires xmit_hash_policy \"vlan+srcmac\""), + NM_SETTING_BOND_OPTION_BALANCE_SLB); + g_prefix_error(error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + return FALSE; + } + } + if (!_nm_connection_verify_required_interface_name(connection, error)) return FALSE; @@ -1233,7 +1273,7 @@ nm_setting_bond_class_init(NMSettingBondClass *klass) setting_class->verify = verify; /** - * NMSettingBond:options: (type GHashTable(utf8,utf8)): + * NMSettingBond:options: (type GHashTable(utf8,utf8)) * * Dictionary of key/value pairs of bonding options. Both keys and values * must be strings. Option names must contain only alphanumeric characters @@ -1273,7 +1313,8 @@ nm_setting_bond_class_init(NMSettingBondClass *klass) */ _nm_properties_override_dbus(properties_override, "interface-name", - &nm_sett_info_propert_type_deprecated_interface_name); + &nm_sett_info_propert_type_deprecated_interface_name, + .dbus_deprecated = TRUE, ); g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); diff --git a/src/libnm-core-impl/nm-setting-bridge.c b/src/libnm-core-impl/nm-setting-bridge.c index 89611147..c1a2621d 100644 --- a/src/libnm-core-impl/nm-setting-bridge.c +++ b/src/libnm-core-impl/nm-setting-bridge.c @@ -109,7 +109,7 @@ G_DEFINE_TYPE(NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING) G_DEFINE_BOXED_TYPE(NMBridgeVlan, nm_bridge_vlan, _nm_bridge_vlan_dup, nm_bridge_vlan_unref) struct _NMBridgeVlan { - guint refcount; + int refcount; guint16 vid_start; guint16 vid_end; bool untagged : 1; @@ -132,6 +132,8 @@ NM_IS_BRIDGE_VLAN(const NMBridgeVlan *self, gboolean also_sealed) * Setting @vid_end to 0 is equivalent to setting it to @vid_start * and creates a single-id VLAN. * + * Since 1.42, ref-counting of #NMBridgeVlan is thread-safe. + * * Returns: (transfer full): the new #NMBridgeVlan object. * * Since: 1.18 @@ -148,11 +150,12 @@ nm_bridge_vlan_new(guint16 vid_start, guint16 vid_end) g_return_val_if_fail(vid_end <= NM_BRIDGE_VLAN_VID_MAX, NULL); g_return_val_if_fail(vid_start <= vid_end, NULL); - vlan = g_slice_new0(NMBridgeVlan); - vlan->refcount = 1; - vlan->vid_start = vid_start; - vlan->vid_end = vid_end; - + vlan = g_slice_new(NMBridgeVlan); + *vlan = (NMBridgeVlan){ + .refcount = 1, + .vid_start = vid_start, + .vid_end = vid_end, + }; return vlan; } @@ -164,6 +167,8 @@ nm_bridge_vlan_new(guint16 vid_start, guint16 vid_end) * * Returns: the input argument @vlan object. * + * Since 1.42, ref-counting of #NMBridgeVlan is thread-safe. + * * Since: 1.18 **/ NMBridgeVlan * @@ -171,9 +176,9 @@ nm_bridge_vlan_ref(NMBridgeVlan *vlan) { g_return_val_if_fail(NM_IS_BRIDGE_VLAN(vlan, TRUE), NULL); - nm_assert(vlan->refcount < G_MAXUINT); + nm_assert(vlan->refcount < G_MAXINT); - vlan->refcount++; + g_atomic_int_inc(&vlan->refcount); return vlan; } @@ -184,6 +189,8 @@ nm_bridge_vlan_ref(NMBridgeVlan *vlan) * Decreases the reference count of the object. If the reference count * reaches zero the object will be destroyed. * + * Since 1.42, ref-counting of #NMBridgeVlan is thread-safe. + * * Since: 1.18 **/ void @@ -191,7 +198,7 @@ nm_bridge_vlan_unref(NMBridgeVlan *vlan) { g_return_if_fail(NM_IS_BRIDGE_VLAN(vlan, TRUE)); - if (--vlan->refcount == 0) + if (g_atomic_int_dec_and_test(&vlan->refcount)) g_slice_free(NMBridgeVlan, vlan); } @@ -1426,10 +1433,9 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass) * If this field is left unspecified, the "ethernet.cloned-mac-address" is * referred instead to generate the initial MAC address. Note that setting * "ethernet.cloned-mac-address" anyway overwrites the MAC address of - * the bridge later while activating the bridge. Hence, this property - * is deprecated. + * the bridge later while activating the bridge. * - * Deprecated: 1.12: Use the ethernet.cloned-mac-address property instead. + * Deprecated: 1.12: Use the #NMSettingWired:cloned-mac-address property instead. **/ /* ---keyfile--- * property: mac-address @@ -1439,7 +1445,8 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass) * example: mac-address=00:22:68:12:79:A2 * mac-address=0;34;104;18;121;162; * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: mac-address * variable: BRIDGE_MACADDR(+) * description: MAC address of the bridge. Note that this requires a recent @@ -1454,7 +1461,8 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass) NM_SETTING_PARAM_INFERRABLE, NMSettingBridge, _priv.mac_address, - .direct_set_string_mac_address_len = ETH_ALEN); + .direct_set_string_mac_address_len = ETH_ALEN, + .is_deprecated = TRUE, ); /** * NMSettingBridge:stp: @@ -1748,7 +1756,8 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass) */ _nm_properties_override_dbus(properties_override, "interface-name", - &nm_sett_info_propert_type_deprecated_interface_name); + &nm_sett_info_propert_type_deprecated_interface_name, + .dbus_deprecated = TRUE, ); /** * NMSettingBridge:group-address: diff --git a/src/libnm-core-impl/nm-setting-cdma.c b/src/libnm-core-impl/nm-setting-cdma.c index 46f29f96..c32af6b7 100644 --- a/src/libnm-core-impl/nm-setting-cdma.c +++ b/src/libnm-core-impl/nm-setting-cdma.c @@ -174,12 +174,12 @@ verify_secrets(NMSetting *setting, NMConnection *connection, GError **error) } static GPtrArray * -need_secrets(NMSetting *setting) +need_secrets(NMSetting *setting, gboolean check_rerequest) { NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE(setting); GPtrArray *secrets = NULL; - if (!nm_str_is_empty(priv->password)) + if (!check_rerequest && !nm_str_is_empty(priv->password)) return NULL; if (priv->username) { diff --git a/src/libnm-core-impl/nm-setting-connection.c b/src/libnm-core-impl/nm-setting-connection.c index bc14c767..0ec36a89 100644 --- a/src/libnm-core-impl/nm-setting-connection.c +++ b/src/libnm-core-impl/nm-setting-connection.c @@ -339,7 +339,7 @@ nm_setting_connection_get_permission(NMSettingConnection *setting, g_return_val_if_fail(idx < nm_g_array_len(priv->permissions), FALSE); - permission = &g_array_index(priv->permissions, Permission, idx); + permission = &nm_g_array_index(priv->permissions, Permission, idx); switch (permission->ptype) { case PERM_TYPE_USER: NM_SET_OUT(out_ptype, NM_SETTINGS_CONNECTION_PERMISSION_USER); @@ -384,7 +384,7 @@ nm_setting_connection_permissions_user_allowed(NMSettingConnection *setting, con } for (i = 0; i < priv->permissions->len; i++) { - const Permission *permission = &g_array_index(priv->permissions, Permission, i); + const Permission *permission = &nm_g_array_index(priv->permissions, Permission, i); if (permission->ptype == PERM_TYPE_USER && nm_streq(permission->item, uname)) return TRUE; @@ -440,7 +440,7 @@ nm_setting_connection_add_permission(NMSettingConnection *setting, } for (i = 0; i < priv->permissions->len; i++) { - const Permission *permission = &g_array_index(priv->permissions, Permission, i); + const Permission *permission = &nm_g_array_index(priv->permissions, Permission, i); if (permission->ptype == PERM_TYPE_USER && nm_streq(permission->item, pitem)) return TRUE; @@ -511,7 +511,7 @@ nm_setting_connection_remove_permission_by_value(NMSettingConnection *setting, priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting); if (priv->permissions) { for (i = 0; i < priv->permissions->len; i++) { - const Permission *permission = &g_array_index(priv->permissions, Permission, i); + const Permission *permission = &nm_g_array_index(priv->permissions, Permission, i); if (permission->ptype == PERM_TYPE_USER && nm_streq(permission->item, pitem)) { g_array_remove_index(priv->permissions, i); @@ -1023,7 +1023,7 @@ nm_setting_connection_get_dns_over_tls(NMSettingConnection *setting) * * Returns: the #NMSettingConnection:mptcp-flags property of the setting. * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ NMMptcpFlags nm_setting_connection_get_mptcp_flags(NMSettingConnection *setting) @@ -1490,7 +1490,7 @@ after_interface_name: guint i; for (i = 0; i < priv->permissions->len; i++) { - const Permission *permissions = &g_array_index(priv->permissions, Permission, i); + const Permission *permissions = &nm_g_array_index(priv->permissions, Permission, i); if (permissions->ptype != PERM_TYPE_USER) { g_set_error_literal(error, @@ -1709,7 +1709,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) strv = g_new(char *, l + 1u); for (i = 0; i < l; i++) - strv[i] = _permission_to_string(&g_array_index(priv->permissions, Permission, i)); + strv[i] = _permission_to_string(&nm_g_array_index(priv->permissions, Permission, i)); strv[i] = NULL; g_value_take_boxed(value, strv); @@ -1848,6 +1848,18 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass) * be generated by nm_utils_uuid_generate() or * nm_uuid_generate_from_string_str(). **/ + /* ---nmcli--- + * property: uuid + * format: a valid RFC4122 universally unique identifier (UUID). + * description: The connection.uuid is the real identifier of a profile. + * It cannot change and it must be unique. It is therefore often best + * to refer to a profile by UUID, for example with `nmcli connection up uuid $UUID`. + * + * The UUID cannot be changed, except in offline mode. In that case, + * the special values "new", "generate" and "" are allowed to generate + * a new random UUID. + * ---end--- + */ /* ---ifcfg-rh--- * property: uuid * variable: UUID(+) diff --git a/src/libnm-core-impl/nm-setting-ethtool.c b/src/libnm-core-impl/nm-setting-ethtool.c index 1db6c335..d5199823 100644 --- a/src/libnm-core-impl/nm-setting-ethtool.c +++ b/src/libnm-core-impl/nm-setting-ethtool.c @@ -29,7 +29,7 @@ * * Checks whether @optname is a valid option name for an offload feature. * - * %Returns: %TRUE, if @optname is valid + * Returns: %TRUE, if @optname is valid * * Note that nm_ethtool_optname_is_feature() was first added to the libnm header files * in 1.14.0 but forgot to actually add to the library. This happened belatedly in 1.20.0 and @@ -49,7 +49,7 @@ nm_ethtool_optname_is_feature(const char *optname) * * Checks whether @optname is a valid option name for a coalesce setting. * - * %Returns: %TRUE, if @optname is valid + * Returns: %TRUE, if @optname is valid * * Since: 1.26 */ @@ -65,7 +65,7 @@ nm_ethtool_optname_is_coalesce(const char *optname) * * Checks whether @optname is a valid option name for a ring setting. * - * %Returns: %TRUE, if @optname is valid + * Returns: %TRUE, if @optname is valid * * Since: 1.26 */ @@ -81,7 +81,7 @@ nm_ethtool_optname_is_ring(const char *optname) * * Checks whether @optname is a valid option name for a pause setting. * - * %Returns: %TRUE, if @optname is valid + * Returns: %TRUE, if @optname is valid * * Since: 1.32 */ diff --git a/src/libnm-core-impl/nm-setting-gsm.c b/src/libnm-core-impl/nm-setting-gsm.c index 15460ccc..3fe5dfa1 100644 --- a/src/libnm-core-impl/nm-setting-gsm.c +++ b/src/libnm-core-impl/nm-setting-gsm.c @@ -460,12 +460,12 @@ verify_secrets(NMSetting *setting, NMConnection *connection, GError **error) } static GPtrArray * -need_secrets(NMSetting *setting) +need_secrets(NMSetting *setting, gboolean check_rerequest) { NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE(setting); GPtrArray *secrets = NULL; - if (priv->password && *priv->password) + if (!check_rerequest && priv->password && *priv->password) return NULL; if (priv->username) { @@ -545,7 +545,8 @@ nm_setting_gsm_class_init(NMSettingGsmClass *klass) PROP_NUMBER, NM_SETTING_PARAM_NONE, NMSettingGsmPrivate, - number); + number, + .is_deprecated = TRUE, ); /** * NMSettingGsm:username: @@ -744,10 +745,12 @@ nm_setting_gsm_class_init(NMSettingGsmClass *klass) /* Ignore incoming deprecated properties */ _nm_properties_override_dbus(properties_override, "allowed-bands", - &nm_sett_info_propert_type_deprecated_ignore_u); + &nm_sett_info_propert_type_deprecated_ignore_u, + .dbus_deprecated = TRUE, ); _nm_properties_override_dbus(properties_override, "network-type", - &nm_sett_info_propert_type_deprecated_ignore_i); + &nm_sett_info_propert_type_deprecated_ignore_i, + .dbus_deprecated = TRUE, ); g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); diff --git a/src/libnm-core-impl/nm-setting-hostname.c b/src/libnm-core-impl/nm-setting-hostname.c index 8a5e50be..399f41a8 100644 --- a/src/libnm-core-impl/nm-setting-hostname.c +++ b/src/libnm-core-impl/nm-setting-hostname.c @@ -134,7 +134,7 @@ nm_setting_hostname_init(NMSettingHostname *setting) * * Returns: (transfer full): the new empty #NMSettingHostname object * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ NMSetting * nm_setting_hostname_new(void) @@ -233,7 +233,7 @@ nm_setting_hostname_class_init(NMSettingHostnameClass *klass) * Since: 1.30 **/ /* ---ifcfg-rh--- - * property: from-dhcp + * property: from-dns-lookup * variable: HOSTNAME_FROM_DNS_LOOKUP(+) * default: missing variable means global default or 1 * description: whether the system hostname can be determined from reverse diff --git a/src/libnm-core-impl/nm-setting-infiniband.c b/src/libnm-core-impl/nm-setting-infiniband.c index 787b838b..410f1f06 100644 --- a/src/libnm-core-impl/nm-setting-infiniband.c +++ b/src/libnm-core-impl/nm-setting-infiniband.c @@ -378,7 +378,8 @@ nm_setting_infiniband_class_init(NMSettingInfinibandClass *klass) * or semicolon separated list of 20 decimal bytes (obsolete) * example: mac-address= 80:00:00:6d:fe:80:00:00:00:00:00:00:00:02:55:00:70:33:cf:01 * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: mac-address * variable: HWADDR * description: IBoIP 20-byte hardware address of the device (in traditional diff --git a/src/libnm-core-impl/nm-setting-ip-config.c b/src/libnm-core-impl/nm-setting-ip-config.c index 73f98ab1..3a31848e 100644 --- a/src/libnm-core-impl/nm-setting-ip-config.c +++ b/src/libnm-core-impl/nm-setting-ip-config.c @@ -67,7 +67,7 @@ canonicalize_ip_binary(int family, const NMIPAddr *ip, gboolean null_any) if (null_any && nm_ip_addr_is_null(family, ip)) return NULL; - return nm_utils_inet_ntop_dup(family, ip); + return nm_inet_ntop_dup(family, ip); } static gboolean @@ -80,7 +80,7 @@ valid_ip(int family, const char *ip, NMIPAddr *addr, GError **error) family == AF_INET ? _("Missing IPv4 address") : _("Missing IPv6 address")); return FALSE; } - if (!nm_utils_parse_inaddr_bin(family, ip, NULL, addr)) { + if (!nm_inet_parse_bin(family, ip, NULL, addr)) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, @@ -210,7 +210,7 @@ nm_ip_address_new_binary(int family, gconstpointer addr, guint prefix, GError ** *address = (NMIPAddress){ .refcount = 1, .family = family, - .address = nm_utils_inet_ntop_dup(family, addr), + .address = nm_inet_ntop_dup(family, addr), .prefix = prefix, }; @@ -420,7 +420,7 @@ nm_ip_address_set_address(NMIPAddress *address, const char *addr) if (!valid_ip(address->family, addr, &addr_bin, NULL)) { g_return_if_fail(addr != NULL); - g_return_if_fail(nm_utils_ipaddr_is_valid(address->family, addr)); + g_return_if_fail(nm_inet_is_valid(address->family, addr)); nm_assert_not_reached(); } @@ -462,7 +462,7 @@ nm_ip_address_set_address_binary(NMIPAddress *address, gconstpointer addr) g_return_if_fail(addr != NULL); g_free(address->address); - address->address = nm_utils_inet_ntop_dup(address->family, addr); + address->address = nm_inet_ntop_dup(address->family, addr); } /** @@ -885,7 +885,7 @@ nm_ip_route_get_dest(NMIPRoute *route) * Sets the destination property of this route object. * * @dest must be a valid address of @route's family. If you aren't sure you - * have a valid address, use nm_utils_ipaddr_is_valid() to check it. + * have a valid address, use nm_inet_is_valid() to check it. **/ void nm_ip_route_set_dest(NMIPRoute *route, const char *dest) @@ -895,7 +895,7 @@ nm_ip_route_set_dest(NMIPRoute *route, const char *dest) g_return_if_fail(route != NULL); if (!valid_ip(route->family, dest, &dest_bin, NULL)) { - g_return_if_fail(nm_utils_ipaddr_is_valid(route->family, dest)); + g_return_if_fail(nm_inet_is_valid(route->family, dest)); nm_assert_not_reached(); } @@ -937,7 +937,7 @@ nm_ip_route_set_dest_binary(NMIPRoute *route, gconstpointer dest) g_return_if_fail(dest != NULL); g_free(route->dest); - route->dest = nm_utils_inet_ntop_dup(route->family, dest); + route->dest = nm_inet_ntop_dup(route->family, dest); } /** @@ -1010,7 +1010,7 @@ nm_ip_route_set_next_hop(NMIPRoute *route, const char *next_hop) g_return_if_fail(route != NULL); if (next_hop && !valid_ip(route->family, next_hop, &next_hop_bin, NULL)) { - g_return_if_fail(!next_hop || nm_utils_ipaddr_is_valid(route->family, next_hop)); + g_return_if_fail(!next_hop || nm_inet_is_valid(route->family, next_hop)); nm_assert_not_reached(); } @@ -1278,6 +1278,10 @@ static const NMVariantAttributeSpec *const ip_route_attribute_spec[] = { .v4 = TRUE, .v6 = TRUE, .type_detail = 'T', ), + NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_WEIGHT, + G_VARIANT_TYPE_UINT32, + .v4 = TRUE, + .type_detail = 'w'), NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_WINDOW, G_VARIANT_TYPE_UINT32, .v4 = TRUE, @@ -1299,8 +1303,9 @@ nm_ip_route_get_variant_attribute_spec(void) } typedef struct { - int type; - int scope; + int type; + int scope; + gint16 weight; } IPRouteAttrParseData; static gboolean @@ -1313,6 +1318,7 @@ _ip_route_attribute_validate(const char *name, { const NMVariantAttributeSpec *spec; const char *string; + guint32 u32; nm_assert(name); nm_assert(value); @@ -1354,7 +1360,7 @@ _ip_route_attribute_validate(const char *name, switch (spec->type_detail) { case 'a': /* IP address */ string = g_variant_get_string(value, NULL); - if (!nm_utils_ipaddr_is_valid(family, string)) { + if (!nm_inet_is_valid(family, string)) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, @@ -1386,7 +1392,7 @@ _ip_route_attribute_validate(const char *name, return FALSE; } } - if (!nm_utils_ipaddr_is_valid(family, addr)) { + if (!nm_inet_is_valid(family, addr)) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, @@ -1426,6 +1432,18 @@ _ip_route_attribute_validate(const char *name, if (parse_data) parse_data->scope = g_variant_get_byte(value); break; + case 'w': /* weight */ + u32 = g_variant_get_uint32(value); + if (u32 > 256) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("route weight cannot be larger than 256")); + return FALSE; + } + if (parse_data) + parse_data->weight = (guint16) u32; + break; case '\0': break; default: @@ -1475,8 +1493,9 @@ _nm_ip_route_attribute_validate_all(const NMIPRoute *route, GError **error) guint attrs_len; guint i; IPRouteAttrParseData parse_data = { - .type = RTN_UNICAST, - .scope = -1, + .type = RTN_UNICAST, + .scope = -1, + .weight = 0, }; g_return_val_if_fail(route, FALSE); @@ -1525,6 +1544,17 @@ _nm_ip_route_attribute_validate_all(const NMIPRoute *route, GError **error) break; } + if (parse_data.weight > 0) { + if (parse_data.type != RTN_UNICAST) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("a %s route cannot have a ECMP multi-hop \"weight\""), + nm_net_aux_rtnl_rtntype_n2a(parse_data.type)); + return FALSE; + } + } + return TRUE; } @@ -1537,7 +1567,7 @@ struct NMIPRoutingRule { char *to_str; char *iifname; char *oifname; - guint ref_count; + int ref_count; guint32 priority; guint32 table; gint32 suppress_prefixlength; @@ -1636,9 +1666,11 @@ nm_ip_routing_rule_new(int addr_family) * nm_ip_routing_rule_new_clone: * @rule: the #NMIPRoutingRule to clone. * + * Since 1.42, ref-counting of #NMIPRoutingRule is thread-safe. + * * Returns: (transfer full): a newly created rule instance with * the same settings as @rule. Note that the instance will - * always be unsealred. + * always be unsealed. * * Since: 1.18 */ @@ -1704,11 +1736,12 @@ nm_ip_routing_rule_new_clone(const NMIPRoutingRule *rule) * @self: (allow-none): the #NMIPRoutingRule instance * * Increases the reference count of the instance. - * This is not thread-safe. * * Returns: (transfer full): the @self argument with incremented * reference count. * + * Since 1.42, ref-counting of #NMIPRoutingRule is thread-safe. + * * Since: 1.18 */ NMIPRoutingRule * @@ -1719,8 +1752,9 @@ nm_ip_routing_rule_ref(NMIPRoutingRule *self) g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL); - nm_assert(self->ref_count < G_MAXUINT); - self->ref_count++; + nm_assert(self->ref_count < G_MAXINT); + + g_atomic_int_inc(&self->ref_count); return self; } @@ -1730,7 +1764,8 @@ nm_ip_routing_rule_ref(NMIPRoutingRule *self) * * Decreases the reference count of the instance and destroys * the instance if the reference count reaches zero. - * This is not thread-safe. + * + * Since 1.42, ref-counting of #NMIPRoutingRule is thread-safe. * * Since: 1.18 */ @@ -1742,7 +1777,7 @@ nm_ip_routing_rule_unref(NMIPRoutingRule *self) g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE)); - if (--self->ref_count > 0) + if (!g_atomic_int_dec_and_test(&self->ref_count)) return; g_free(self->from_str); @@ -1913,7 +1948,7 @@ nm_ip_routing_rule_get_from(const NMIPRoutingRule *self) if (!self->from_str) { nm_assert(self->from_valid); ((NMIPRoutingRule *) self)->from_str = - nm_utils_inet_ntop_dup(_ip_routing_rule_get_addr_family(self), &self->from_bin); + nm_inet_ntop_dup(_ip_routing_rule_get_addr_family(self), &self->from_bin); } return self->from_str; } @@ -1970,12 +2005,10 @@ nm_ip_routing_rule_set_from(NMIPRoutingRule *self, const char *from, guint8 len) } nm_clear_g_free(&self->from_str); - self->from_has = TRUE; - self->from_len = len; - self->from_valid = nm_utils_parse_inaddr_bin(_ip_routing_rule_get_addr_family(self), - from, - NULL, - &self->from_bin); + self->from_has = TRUE; + self->from_len = len; + self->from_valid = + nm_inet_parse_bin(_ip_routing_rule_get_addr_family(self), from, NULL, &self->from_bin); if (!self->from_valid) self->from_str = g_strdup(from); } @@ -2015,7 +2048,7 @@ nm_ip_routing_rule_get_to(const NMIPRoutingRule *self) if (!self->to_str) { nm_assert(self->to_valid); ((NMIPRoutingRule *) self)->to_str = - nm_utils_inet_ntop_dup(_ip_routing_rule_get_addr_family(self), &self->to_bin); + nm_inet_ntop_dup(_ip_routing_rule_get_addr_family(self), &self->to_bin); } return self->to_str; } @@ -2076,7 +2109,7 @@ nm_ip_routing_rule_set_to(NMIPRoutingRule *self, const char *to, guint8 len) self->to_has = TRUE; self->to_len = len; self->to_valid = - nm_utils_parse_inaddr_bin(_ip_routing_rule_get_addr_family(self), to, NULL, &self->to_bin); + nm_inet_parse_bin(_ip_routing_rule_get_addr_family(self), to, NULL, &self->to_bin); if (!self->to_valid) self->to_str = g_strdup(to); } @@ -2938,12 +2971,12 @@ _rr_dbus_attr_from_name(const char *name) } } - idx = nm_utils_array_find_binary_search(rr_dbus_data, - sizeof(rr_dbus_data[0]), - _RR_DBUS_ATTR_NUM, - &name, - nm_strcmp_p_with_data, - NULL); + idx = nm_array_find_bsearch(rr_dbus_data, + _RR_DBUS_ATTR_NUM, + sizeof(rr_dbus_data[0]), + &name, + nm_strcmp_p_with_data, + NULL); if (idx < 0) return _RR_DBUS_ATTR_NUM; return idx; @@ -3169,7 +3202,7 @@ GVariant * nm_ip_routing_rule_to_dbus(const NMIPRoutingRule *self) { GVariantBuilder builder; - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + char addr_str[NM_INET_ADDRSTRLEN]; g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL); @@ -3222,9 +3255,9 @@ nm_ip_routing_rule_to_dbus(const NMIPRoutingRule *self) &builder, RR_DBUS_ATTR_FROM, g_variant_new_string(self->from_str - ?: nm_utils_inet_ntop(_ip_routing_rule_get_addr_family(self), - &self->from_bin, - addr_str))); + ?: nm_inet_ntop(_ip_routing_rule_get_addr_family(self), + &self->from_bin, + addr_str))); _rr_to_dbus_add(&builder, RR_DBUS_ATTR_FROM_LEN, g_variant_new_byte(self->from_len)); } @@ -3233,9 +3266,9 @@ nm_ip_routing_rule_to_dbus(const NMIPRoutingRule *self) &builder, RR_DBUS_ATTR_TO, g_variant_new_string(self->to_str - ?: nm_utils_inet_ntop(_ip_routing_rule_get_addr_family(self), - &self->to_bin, - addr_str))); + ?: nm_inet_ntop(_ip_routing_rule_get_addr_family(self), + &self->to_bin, + addr_str))); _rr_to_dbus_add(&builder, RR_DBUS_ATTR_TO_LEN, g_variant_new_byte(self->to_len)); } @@ -3634,11 +3667,11 @@ next_words_consumed: } if (!NM_IN_STRSET(word_from, NULL, "all")) { - if (!nm_utils_parse_inaddr_prefix_bin(addr_family, - word_from, - &addr_family, - &val_from, - &val_from_len)) { + if (!nm_inet_parse_with_prefix_bin(addr_family, + word_from, + &addr_family, + &val_from, + &val_from_len)) { g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, @@ -3650,11 +3683,11 @@ next_words_consumed: } if (!NM_IN_STRSET(word_to, NULL, "all")) { - if (!nm_utils_parse_inaddr_prefix_bin(addr_family, - word_to, - &addr_family, - &val_to, - &val_to_len)) { + if (!nm_inet_parse_with_prefix_bin(addr_family, + word_to, + &addr_family, + &val_to, + &val_to_len)) { g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, @@ -3753,7 +3786,7 @@ _rr_string_append_inet_addr(NMStrBuf *str, const NMIPAddr *addr_bin, guint8 addr_len) { - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + char addr_str[NM_INET_ADDRSTRLEN]; if (addr_len == 0) { if (required) { @@ -3768,7 +3801,7 @@ _rr_string_append_inet_addr(NMStrBuf *str, nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(str, ' '), "%s %s", is_from ? "from" : "to", - nm_utils_inet_ntop(addr_family, addr_bin, addr_str)); + nm_inet_ntop(addr_family, addr_bin, addr_str)); if (addr_len != nm_utils_addr_family_to_size(addr_family) * 8) { nm_str_buf_append_printf(str, "/%u", addr_len); } @@ -3793,8 +3826,8 @@ nm_ip_routing_rule_to_string(const NMIPRoutingRule *self, GHashTable *extra_args, GError **error) { - int addr_family; - NMStrBuf str; + int addr_family; + nm_auto_str_buf NMStrBuf str = NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_488, FALSE); g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL); @@ -3832,8 +3865,6 @@ nm_ip_routing_rule_to_string(const NMIPRoutingRule *self, } } - str = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_32, FALSE); - if (self->priority_has) { nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(&str, ' '), "priority %u", @@ -3940,7 +3971,7 @@ nm_ip_routing_rule_to_string(const NMIPRoutingRule *self, nm_net_aux_rtnl_rtntype_n2a_maybe_buf(self->action, sbuf)); } - return nm_str_buf_finalize(&str, NULL); + return nm_str_buf_dup_str(&str); } /*****************************************************************************/ @@ -3967,7 +3998,8 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingIPConfig, PROP_DHCP_TIMEOUT, PROP_REQUIRED_TIMEOUT, PROP_DHCP_IAID, - PROP_DHCP_REJECT_SERVERS, ); + PROP_DHCP_REJECT_SERVERS, + PROP_AUTO_ROUTE_EXT_GW, ); G_DEFINE_ABSTRACT_TYPE(NMSettingIPConfig, nm_setting_ip_config, NM_TYPE_SETTING) @@ -3991,9 +4023,6 @@ _NM_SETTING_IP_CONFIG_GET_PRIVATE(NMSettingIPConfig *self) /*****************************************************************************/ -#define NM_SETTING_IP_CONFIG_GET_FAMILY(setting) \ - (NM_IS_SETTING_IP4_CONFIG(setting) ? AF_INET : AF_INET6) - /** * nm_setting_ip_config_get_method: * @setting: the #NMSettingIPConfig @@ -4021,7 +4050,7 @@ nm_setting_ip_config_get_num_dns(NMSettingIPConfig *setting) { g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), 0); - return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dns->len; + return nm_g_ptr_array_len(NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dns); } /** @@ -4039,11 +4068,35 @@ nm_setting_ip_config_get_dns(NMSettingIPConfig *setting, int idx) g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NULL); priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting); - g_return_val_if_fail(idx >= 0 && idx < priv->dns->len, NULL); + g_return_val_if_fail(idx >= 0 && ((guint) idx) < nm_g_ptr_array_len(priv->dns), NULL); return priv->dns->pdata[idx]; } +static gboolean +_ip_config_add_dns(NMSettingIPConfig *setting, const char *dns) +{ + NMSettingIPConfigPrivate *priv; + gs_free char *s_free = NULL; + const char *s; + + nm_assert(NM_IS_SETTING_IP_CONFIG(setting)); + nm_assert(dns); + + priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting); + + s = nm_utils_dnsname_normalize(NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), dns, &s_free); + if (!s) + s = dns; + + if (nm_strv_ptrarray_contains(priv->dns, s)) + return FALSE; + + nm_strv_ptrarray_add_string_take(nm_strv_ptrarray_ensure(&priv->dns), + g_steal_pointer(&s_free) ?: g_strdup(s)); + return TRUE; +} + /** * nm_setting_ip_config_add_dns: * @setting: the #NMSettingIPConfig @@ -4053,36 +4106,20 @@ nm_setting_ip_config_get_dns(NMSettingIPConfig *setting, int idx) * * Returns: %TRUE if the DNS server was added; %FALSE if the server was already * known + * + * Before 1.42, setting @dns to an invalid string was treated as user-error. + * Now, also invalid DNS values can be set, but will be rejected later during + * nm_connection_verify(). **/ gboolean nm_setting_ip_config_add_dns(NMSettingIPConfig *setting, const char *dns) { - NMSettingIPConfigPrivate *priv; - int addr_family; - NMIPAddr dns_bin; - char dns_canonical[NM_UTILS_INET_ADDRSTRLEN]; - guint i; - g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE); + g_return_val_if_fail(dns, FALSE); - addr_family = NM_SETTING_IP_CONFIG_GET_FAMILY(setting); - - if (!valid_ip(addr_family, dns, &dns_bin, NULL)) { - g_return_val_if_fail(dns != NULL, FALSE); - g_return_val_if_fail(nm_utils_ipaddr_is_valid(addr_family, dns), FALSE); - nm_assert_not_reached(); - } - - priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting); - - nm_utils_inet_ntop(addr_family, &dns_bin, dns_canonical); - - for (i = 0; i < priv->dns->len; i++) { - if (nm_streq(dns_canonical, priv->dns->pdata[i])) - return FALSE; - } + if (!_ip_config_add_dns(setting, dns)) + return FALSE; - g_ptr_array_add(priv->dns, g_strdup(dns_canonical)); _notify(setting, PROP_DNS); return TRUE; } @@ -4102,7 +4139,8 @@ nm_setting_ip_config_remove_dns(NMSettingIPConfig *setting, int idx) g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting)); priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting); - g_return_if_fail(idx >= 0 && idx < priv->dns->len); + + g_return_if_fail(idx >= 0 && ((guint) idx) < nm_g_ptr_array_len(priv->dns)); g_ptr_array_remove_index(priv->dns, idx); _notify(setting, PROP_DNS); @@ -4116,38 +4154,39 @@ nm_setting_ip_config_remove_dns(NMSettingIPConfig *setting, int idx) * Removes the DNS server @dns. * * Returns: %TRUE if the DNS server was found and removed; %FALSE if it was not. + * + * Before 1.42, setting @dns to an invalid string was treated as user-error. **/ gboolean nm_setting_ip_config_remove_dns_by_value(NMSettingIPConfig *setting, const char *dns) { NMSettingIPConfigPrivate *priv; - int addr_family; - NMIPAddr dns_bin; - char dns_canonical[NM_UTILS_INET_ADDRSTRLEN]; - guint i; + gssize idx; g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE); - - addr_family = NM_SETTING_IP_CONFIG_GET_FAMILY(setting); - - if (!valid_ip(addr_family, dns, &dns_bin, NULL)) { - g_return_val_if_fail(dns != NULL, FALSE); - g_return_val_if_fail(nm_utils_ipaddr_is_valid(addr_family, dns), FALSE); - nm_assert_not_reached(); - } + g_return_val_if_fail(dns, FALSE); priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting); - nm_utils_inet_ntop(addr_family, &dns_bin, dns_canonical); + /* "priv->dns" can only contain normalized or invalid values. Expect that + * "dns" is normalized already, so lookup first for that string. Only + * if that fails, fallback to normalize "dns". */ + idx = nm_strv_ptrarray_find_first(priv->dns, dns); + if (idx < 0) { + gs_free char *s_free = NULL; + const char *s; - for (i = 0; i < priv->dns->len; i++) { - if (nm_streq(dns_canonical, priv->dns->pdata[i])) { - g_ptr_array_remove_index(priv->dns, i); - _notify(setting, PROP_DNS); - return TRUE; - } + s = nm_utils_dnsname_normalize(NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), dns, &s_free); + if (s && !nm_streq(dns, s)) + idx = nm_strv_ptrarray_find_first(priv->dns, dns); } - return FALSE; + + if (idx < 0) + return FALSE; + + g_ptr_array_remove_index(priv->dns, idx); + _notify(setting, PROP_DNS); + return TRUE; } /** @@ -4165,7 +4204,7 @@ nm_setting_ip_config_clear_dns(NMSettingIPConfig *setting) priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting); - if (priv->dns->len != 0) { + if (nm_g_ptr_array_len(priv->dns) != 0) { g_ptr_array_set_size(priv->dns, 0); _notify(setting, PROP_DNS); } @@ -4610,7 +4649,7 @@ nm_setting_ip_config_add_address(NMSettingIPConfig *setting, NMIPAddress *addres g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE); g_return_val_if_fail(address != NULL, FALSE); - g_return_val_if_fail(address->family == NM_SETTING_IP_CONFIG_GET_FAMILY(setting), FALSE); + g_return_val_if_fail(address->family == NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), FALSE); priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting); for (i = 0; i < priv->addresses->len; i++) { @@ -4767,7 +4806,7 @@ nm_setting_ip_config_add_route(NMSettingIPConfig *setting, NMIPRoute *route) g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE); g_return_val_if_fail(route != NULL, FALSE); - g_return_val_if_fail(route->family == NM_SETTING_IP_CONFIG_GET_FAMILY(setting), FALSE); + g_return_val_if_fail(route->family == NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), FALSE); priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting); for (i = 0; i < priv->routes->len; i++) { @@ -4963,7 +5002,7 @@ nm_setting_ip_config_add_routing_rule(NMSettingIPConfig *setting, NMIPRoutingRul g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting)); g_return_if_fail(NM_IS_IP_ROUTING_RULE(routing_rule, TRUE)); g_return_if_fail(_ip_routing_rule_get_addr_family(routing_rule) - == NM_SETTING_IP_CONFIG_GET_FAMILY(setting)); + == NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting)); priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting); @@ -5021,7 +5060,7 @@ nm_setting_ip_config_clear_routing_rules(NMSettingIPConfig *setting) } static GVariant * -_routing_rules_dbus_only_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +routing_rules_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { NMSettingIPConfig *self = NM_SETTING_IP_CONFIG(setting); NMSettingIPConfigPrivate *priv; @@ -5052,7 +5091,7 @@ _routing_rules_dbus_only_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) } static gboolean -_routing_rules_dbus_only_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +routing_rules_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { GVariantIter iter_rules; GVariant *rule_var; @@ -5295,7 +5334,7 @@ nm_setting_ip_config_get_required_timeout(NMSettingIPConfig *setting) * * Returns: the configured DHCP IAID (Identity Association Identifier) * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ const char * nm_setting_ip_config_get_dhcp_iaid(NMSettingIPConfig *setting) @@ -5390,6 +5429,22 @@ nm_setting_ip_config_clear_dhcp_reject_servers(NMSettingIPConfig *setting) } } +/** + * nm_setting_ip_config_get_auto_route_ext_gw: + * @setting: the #NMSettingIPConfig + * + * Returns: the #NMSettingIPConfig:auto-route-ext-gw property of the setting + * + * Since: 1.42 + **/ +NMTernary +nm_setting_ip_config_get_auto_route_ext_gw(NMSettingIPConfig *setting) +{ + g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NM_TERNARY_DEFAULT); + + return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->auto_route_ext_gw; +} + static gboolean verify_label(const char *label) { @@ -5442,20 +5497,26 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } /* Validate DNS */ - for (i = 0; i < priv->dns->len; i++) { - const char *dns = priv->dns->pdata[i]; - - if (!nm_utils_ipaddr_is_valid(NM_SETTING_IP_CONFIG_GET_FAMILY(setting), dns)) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("%d. DNS server address is invalid"), - (int) (i + 1)); - g_prefix_error(error, - "%s.%s: ", - nm_setting_get_name(setting), - NM_SETTING_IP_CONFIG_DNS); - return FALSE; + if (priv->dns) { + for (i = 0; i < priv->dns->len; i++) { + const char *dns = priv->dns->pdata[i]; + + if (!nm_utils_dnsname_parse(NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), + dns, + NULL, + NULL, + NULL)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("%u. DNS server address is invalid"), + (i + 1u)); + g_prefix_error(error, + "%s.%s: ", + nm_setting_get_name(setting), + NM_SETTING_IP_CONFIG_DNS); + return FALSE; + } } } @@ -5464,7 +5525,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) NMIPAddress *addr = (NMIPAddress *) priv->addresses->pdata[i]; GVariant *label; - if (nm_ip_address_get_family(addr) != NM_SETTING_IP_CONFIG_GET_FAMILY(setting)) { + if (nm_ip_address_get_family(addr) != NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting)) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -5521,7 +5582,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (!nm_utils_ipaddr_is_valid(NM_SETTING_IP_CONFIG_GET_FAMILY(setting), priv->gateway)) { + if (!nm_inet_is_valid(NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), priv->gateway)) { g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -5539,7 +5600,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) gs_free_error GError *local = NULL; NMIPRoute *route = (NMIPRoute *) priv->routes->pdata[i]; - if (nm_ip_route_get_family(route) != NM_SETTING_IP_CONFIG_GET_FAMILY(setting)) { + if (nm_ip_route_get_family(route) != NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting)) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -5572,7 +5633,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) gs_free_error GError *local = NULL; if (_ip_routing_rule_get_addr_family(rule) - != NM_SETTING_IP_CONFIG_GET_FAMILY(setting)) { + != NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting)) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -5629,7 +5690,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) if (priv->dhcp_hostname_flags != (NMDhcpHostnameFlags) priv->dhcp_hostname_flags || !_nm_utils_validate_dhcp_hostname_flags(priv->dhcp_hostname_flags, - NM_SETTING_IP_CONFIG_GET_FAMILY(setting), + NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), error)) { g_prefix_error(error, "%s.%s: ", @@ -5640,7 +5701,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) /* Validate reject servers */ if (priv->dhcp_reject_servers && priv->dhcp_reject_servers->len != 0) { - if (NM_SETTING_IP_CONFIG_GET_FAMILY(setting) != AF_INET) { + if (NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting) != AF_INET) { g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -5653,16 +5714,16 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } for (i = 0; i < priv->dhcp_reject_servers->len; i++) { - if (!nm_utils_parse_inaddr_prefix( - NM_SETTING_IP_CONFIG_GET_FAMILY(setting), - g_array_index(priv->dhcp_reject_servers, const char *, i), + if (!nm_inet_parse_with_prefix_str( + NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), + nm_g_array_index(priv->dhcp_reject_servers, const char *, i), NULL, NULL)) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, _("'%s' is not a valid IP or subnet"), - g_array_index(priv->dhcp_reject_servers, const char *, i)); + nm_g_array_index(priv->dhcp_reject_servers, const char *, i)); g_prefix_error(error, "%s.%s: ", nm_setting_get_name(setting), @@ -5736,6 +5797,20 @@ _nm_setting_ip_config_compare_fcn_routes(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm return TRUE; } +NMTernary +_nm_setting_ip_config_compare_fcn_dns(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil) +{ + if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)) + return NM_TERNARY_DEFAULT; + + if (!set_b) + return TRUE; + + return (nm_strv_ptrarray_cmp(NM_SETTING_IP_CONFIG_GET_PRIVATE(set_a)->dns, + NM_SETTING_IP_CONFIG_GET_PRIVATE(set_b)->dns) + == 0); +} + static NMTernary compare_fcn_routing_rules(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil) { @@ -5838,6 +5913,35 @@ _nm_setting_property_from_dbus_fcn_direct_ip_config_gateway( error); } +static GVariant * +dns_data_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +{ + GPtrArray *arr; + + if (!_nm_connection_serialize_non_secret(flags)) + return NULL; + + arr = _nm_setting_ip_config_get_dns_array(NM_SETTING_IP_CONFIG(setting)); + if (nm_g_ptr_array_len(arr) == 0) + return NULL; + return g_variant_new_strv((const char *const *) arr->pdata, arr->len); +} + +static gboolean +dns_data_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +{ + gs_free const char **strv = NULL; + + if (_nm_setting_use_legacy_property(setting, connection_dict, "dns", "dns-data")) { + *out_is_modified = FALSE; + return TRUE; + } + + strv = g_variant_get_strv(value, NULL); + g_object_set(setting, NM_SETTING_IP_CONFIG_DNS, strv, NULL); + return TRUE; +} + GArray * _nm_sett_info_property_override_create_array_ip_config(int addr_family) { @@ -5880,16 +5984,21 @@ _nm_sett_info_property_override_create_array_ip_config(int addr_family) /* ---dbus--- * property: routing-rules * format: array of 'a{sv}' - * description: Array of dictionaries for routing rules. + * description: Array of dictionaries for routing rules. Each routing rule + * supports the following options: action (y), dport-end (q), + * dport-start (q), family (i), from (s), from-len (y), fwmark (u), + * fwmask (u), iifname (s), invert (b), ipproto (s), oifname (s), + * priority (u), sport-end (q), sport-start (q), supress-prefixlength (i), + * table (u), to (s), tos (y), to-len (y), range-end (u), range-start (u). * ---end--- */ _nm_properties_override_dbus( properties_override, NM_SETTING_IP_CONFIG_ROUTING_RULES, NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"), - .to_dbus_fcn = _routing_rules_dbus_only_synth, + .to_dbus_fcn = routing_rules_to_dbus, .compare_fcn = compare_fcn_routing_rules, - .from_dbus_fcn = _routing_rules_dbus_only_set, )); + .from_dbus_fcn = routing_rules_from_dbus, )); _nm_properties_override_gobj( properties_override, @@ -5919,6 +6028,21 @@ _nm_sett_info_property_override_create_array_ip_config(int addr_family) .direct_offset = NM_STRUCT_OFFSET_ENSURE_TYPE(bool, NMSettingIPConfigPrivate, ignore_auto_dns)); + /* ---dbus--- + * property: dns-data + * format: array of strings + * description: Array of DNS name servers. This replaces the deprecated + * "dns" property. Each name server can also contain a DoT server name. + * ---end--- + */ + _nm_properties_override_dbus( + properties_override, + "dns-data", + NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("as"), + .to_dbus_fcn = dns_data_to_dbus, + .from_dbus_fcn = dns_data_from_dbus, + .compare_fcn = _nm_setting_property_compare_fcn_ignore, )); + _nm_properties_override_gobj( properties_override, obj_properties[PROP_DNS_PRIORITY], @@ -5974,6 +6098,13 @@ _nm_sett_info_property_override_create_array_ip_config(int addr_family) &nm_sett_info_propert_type_direct_boolean, .direct_offset = NM_STRUCT_OFFSET_ENSURE_TYPE(bool, NMSettingIPConfigPrivate, may_fail)); + _nm_properties_override_gobj( + properties_override, + obj_properties[PROP_AUTO_ROUTE_EXT_GW], + &nm_sett_info_propert_type_direct_enum, + .direct_offset = + NM_STRUCT_OFFSET_ENSURE_TYPE(int, NMSettingIPConfigPrivate, auto_route_ext_gw)); + return properties_override; } @@ -6028,9 +6159,17 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps switch (prop_id) { case PROP_DNS: - g_ptr_array_unref(priv->dns); - priv->dns = nm_strv_to_ptrarray(g_value_get_boxed(value)); + { + gs_unref_ptrarray GPtrArray *dns_old = NULL; + + dns_old = g_steal_pointer(&priv->dns); + strv = g_value_get_boxed(value); + if (strv) { + for (i = 0; strv[i]; i++) + _ip_config_add_dns(setting, strv[i]); + } break; + } case PROP_DNS_SEARCH: g_ptr_array_unref(priv->dns_search); priv->dns_search = nm_strv_to_ptrarray(g_value_get_boxed(value)); @@ -6082,7 +6221,6 @@ _nm_setting_ip_config_private_init(gpointer self, NMSettingIPConfigPrivate *priv { nm_assert(NM_IS_SETTING_IP_CONFIG(self)); - priv->dns = g_ptr_array_new_with_free_func(g_free); priv->dns_search = g_ptr_array_new_with_free_func(g_free); priv->addresses = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_address_unref); priv->routes = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_route_unref); @@ -6100,7 +6238,7 @@ finalize(GObject *object) NMSettingIPConfig *self = NM_SETTING_IP_CONFIG(object); NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(self); - g_ptr_array_unref(priv->dns); + nm_g_ptr_array_unref(priv->dns); g_ptr_array_unref(priv->dns_search); nm_g_ptr_array_unref(priv->dns_options); g_ptr_array_unref(priv->addresses); @@ -6159,12 +6297,18 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass) * NMSettingIPConfig:dns: * * Array of IP addresses of DNS servers. + * + * For DoT (DNS over TLS), the SNI server name can be specified by appending + * "#example.com" to the IP address of the DNS server. This currently only has + * effect when using systemd-resolved. **/ - obj_properties[PROP_DNS] = g_param_spec_boxed(NM_SETTING_IP_CONFIG_DNS, - "", - "", - G_TYPE_STRV, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_DNS] = + g_param_spec_boxed(NM_SETTING_IP_CONFIG_DNS, + "", + "", + G_TYPE_STRV, + /* On D-Bus, "dns" is deprecated for "dns-data". */ + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * NMSettingIPConfig:dns-search: @@ -6295,12 +6439,9 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass) "", "", G_TYPE_PTR_ARRAY, - /* "addresses" is a legacy D-Bus property, because the - * "addresses" GObject property normally gets set from - * the "address-data" D-Bus property... - */ - G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | NM_SETTING_PARAM_LEGACY - | G_PARAM_STATIC_STRINGS); + /* On D-Bus, "addresses" is deprecated for "address-data". */ + G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE + | NM_SETTING_PARAM_UNUSED1 | G_PARAM_STATIC_STRINGS); /** * NMSettingIPConfig:gateway: @@ -6322,6 +6463,8 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass) "", "", NULL, + /* On D-Bus, the legacy property "addresses" contains the gateway. + * This was replaced by "address-data" and "gateway". */ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS); /** @@ -6334,9 +6477,9 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass) "", "", G_TYPE_PTR_ARRAY, - G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | - /* See :addresses above Re: LEGACY */ - NM_SETTING_PARAM_LEGACY | G_PARAM_STATIC_STRINGS); + /* On D-Bus, "routes" is deprecated for "route-data". */ + G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE + | NM_SETTING_PARAM_UNUSED1 | G_PARAM_STATIC_STRINGS); /** * NMSettingIPConfig:route-metric: @@ -6635,5 +6778,24 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass) G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + /** + * NMSettingIPConfig:auto-route-ext-gw: + * + * VPN connections will default to add the route automatically unless this + * setting is set to %FALSE. + * + * For other connection types, adding such an automatic route is currently + * not supported and setting this to %TRUE has no effect. + * + * Since: 1.42 + */ + obj_properties[PROP_AUTO_ROUTE_EXT_GW] = + g_param_spec_enum(NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW, + "", + "", + NM_TYPE_TERNARY, + NM_TERNARY_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); } diff --git a/src/libnm-core-impl/nm-setting-ip-tunnel.c b/src/libnm-core-impl/nm-setting-ip-tunnel.c index 73416813..7fb8b017 100644 --- a/src/libnm-core-impl/nm-setting-ip-tunnel.c +++ b/src/libnm-core-impl/nm-setting-ip-tunnel.c @@ -28,6 +28,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PARENT, PROP_OUTPUT_KEY, PROP_ENCAPSULATION_LIMIT, PROP_FLOW_LABEL, + PROP_FWMARK, PROP_MTU, PROP_FLAGS, ); @@ -41,6 +42,7 @@ typedef struct { guint32 tos; guint32 encapsulation_limit; guint32 flow_label; + guint32 fwmark; guint32 mode; guint32 mtu; guint32 flags; @@ -240,7 +242,7 @@ nm_setting_ip_tunnel_get_output_key(NMSettingIPTunnel *setting) * * Returns: the encapsulation limit value * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ guint nm_setting_ip_tunnel_get_encapsulation_limit(NMSettingIPTunnel *setting) @@ -258,7 +260,7 @@ nm_setting_ip_tunnel_get_encapsulation_limit(NMSettingIPTunnel *setting) * * Returns: the flow label value * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ guint nm_setting_ip_tunnel_get_flow_label(NMSettingIPTunnel *setting) @@ -268,6 +270,24 @@ nm_setting_ip_tunnel_get_flow_label(NMSettingIPTunnel *setting) return NM_SETTING_IP_TUNNEL_GET_PRIVATE(setting)->flow_label; } +/** + * nm_setting_ip_tunnel_get_fwmark: + * @setting: the #NMSettingIPTunnel + * + * Returns the #NMSettingIPTunnel:fwmark property of the setting. + * + * Returns: the fwmark value + * + * Since: 1.42 + **/ +guint32 +nm_setting_ip_tunnel_get_fwmark(NMSettingIPTunnel *setting) +{ + g_return_val_if_fail(NM_IS_SETTING_IP_TUNNEL(setting), 0); + + return NM_SETTING_IP_TUNNEL_GET_PRIVATE(setting)->fwmark; +} + /** * nm_setting_ip_tunnel_get_mtu: * @setting: the #NMSettingIPTunnel @@ -366,7 +386,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->local && !nm_utils_ipaddr_is_valid(family, priv->local)) { + if (priv->local && !nm_inet_is_valid(family, priv->local)) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -392,7 +412,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (!nm_utils_ipaddr_is_valid(family, priv->remote)) { + if (!nm_inet_is_valid(family, priv->remote)) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -411,11 +431,13 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) NM_IP_TUNNEL_MODE_GRE, NM_IP_TUNNEL_MODE_GRETAP, NM_IP_TUNNEL_MODE_IP6GRE, - NM_IP_TUNNEL_MODE_IP6GRETAP)) { + NM_IP_TUNNEL_MODE_IP6GRETAP, + NM_IP_TUNNEL_MODE_VTI, + NM_IP_TUNNEL_MODE_VTI6)) { g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("tunnel keys can only be specified for GRE tunnels")); + _("tunnel keys can only be specified for GRE and VTI tunnels")); return FALSE; } } @@ -484,6 +506,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } + if (priv->fwmark && !NM_IN_SET(priv->mode, NM_IP_TUNNEL_MODE_VTI, NM_IP_TUNNEL_MODE_VTI6)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("can be set only on VTI tunnels")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_IP_TUNNEL_SETTING_NAME, + NM_SETTING_IP_TUNNEL_FWMARK); + return FALSE; + } + if (nm_connection_get_setting_wired(connection) && !_nm_ip_tunnel_mode_is_layer2(priv->mode)) { g_set_error(error, NM_CONNECTION_ERROR, @@ -727,6 +761,25 @@ nm_setting_ip_tunnel_class_init(NMSettingIPTunnelClass *klass) NMSettingIPTunnelPrivate, flow_label); + /** + * NMSettingIPTunnel:fwmark: + * + * The fwmark value to assign to tunnel packets. This property can be set + * to a non zero value only on VTI and VTI6 tunnels. + * + * Since: 1.42 + **/ + _nm_setting_property_define_direct_uint32(properties_override, + obj_properties, + NM_SETTING_IP_TUNNEL_FWMARK, + PROP_FWMARK, + 0, + G_MAXUINT32, + 0, + NM_SETTING_PARAM_INFERRABLE, + NMSettingIPTunnelPrivate, + fwmark); + /** * NMSettingIPTunnel:mtu: * diff --git a/src/libnm-core-impl/nm-setting-ip4-config.c b/src/libnm-core-impl/nm-setting-ip4-config.c index d991152c..f847918e 100644 --- a/src/libnm-core-impl/nm-setting-ip4-config.c +++ b/src/libnm-core-impl/nm-setting-ip4-config.c @@ -138,7 +138,7 @@ nm_setting_ip4_config_get_dhcp_vendor_class_identifier(NMSettingIP4Config *setti * * Returns: the link-local configuration * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ NMSettingIP4LinkLocal nm_setting_ip4_config_get_link_local(NMSettingIP4Config *setting) @@ -388,21 +388,29 @@ ip4_dns_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) GPtrArray *dns; dns = _nm_setting_ip_config_get_dns_array(NM_SETTING_IP_CONFIG(setting)); - if (nm_g_ptr_array_len(dns) == 0) return NULL; - return _nm_utils_ip4_dns_to_variant((const char *const *) dns->pdata, dns->len); + return nm_utils_dns_to_variant(AF_INET, (const char *const *) dns->pdata, dns->len); } -static void -ip4_dns_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_GPROP_FCN_ARGS _nm_nil) +static gboolean +ip4_dns_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { - g_value_take_boxed(to, nm_utils_ip4_dns_from_variant(from)); + gs_strfreev char **strv = NULL; + + if (!_nm_setting_use_legacy_property(setting, connection_dict, "dns", "dns-data")) { + *out_is_modified = FALSE; + return TRUE; + } + + strv = nm_utils_ip4_dns_from_variant(value); + g_object_set(setting, NM_SETTING_IP_CONFIG_DNS, strv, NULL); + return TRUE; } static GVariant * -ip4_addresses_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +ip4_addresses_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { gs_unref_ptrarray GPtrArray *addrs = NULL; const char *gateway; @@ -413,12 +421,13 @@ ip4_addresses_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) } static gboolean -ip4_addresses_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +ip4_addresses_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { - GPtrArray *addrs; - GVariant *s_ip4; - char **labels, *gateway = NULL; - int i; + gs_unref_ptrarray GPtrArray *addrs = NULL; + gs_unref_variant GVariant *s_ip4 = NULL; + gs_free const char **labels = NULL; + gs_free char *gateway = NULL; + guint i; /* FIXME: properly handle errors */ @@ -432,15 +441,15 @@ ip4_addresses_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) s_ip4 = g_variant_lookup_value(connection_dict, NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_VARIANT_TYPE_SETTING); - if (g_variant_lookup(s_ip4, "address-labels", "^as", &labels)) { - for (i = 0; i < addrs->len && labels[i]; i++) - if (*labels[i]) + if (g_variant_lookup(s_ip4, "address-labels", "^a&s", &labels)) { + for (i = 0; i < addrs->len && labels[i]; i++) { + if (*labels[i]) { nm_ip_address_set_attribute(addrs->pdata[i], NM_IP_ADDRESS_ATTRIBUTE_LABEL, g_variant_new_string(labels[i])); - g_strfreev(labels); + } + } } - g_variant_unref(s_ip4); g_object_set(setting, NM_SETTING_IP_CONFIG_ADDRESSES, @@ -448,52 +457,50 @@ ip4_addresses_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) NM_SETTING_IP_CONFIG_GATEWAY, gateway, NULL); - g_ptr_array_unref(addrs); - g_free(gateway); return TRUE; } static GVariant * -ip4_address_labels_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +ip4_address_labels_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { NMSettingIPConfig *s_ip = NM_SETTING_IP_CONFIG(setting); gboolean have_labels = FALSE; - GPtrArray *labels; - GVariant *ret; - int num_addrs, i; + gs_free GVariant **labels_free = NULL; + GVariant *s_empty; + GVariant **labels; + guint num_addrs; + guint i; if (!_nm_connection_serialize_non_secret(flags)) return NULL; num_addrs = nm_setting_ip_config_get_num_addresses(s_ip); + if (num_addrs == 0) + return NULL; + + labels = nm_malloc_maybe_a(500, sizeof(gpointer) * num_addrs, &labels_free); + + s_empty = nm_g_variant_singleton_s_empty(); + for (i = 0; i < num_addrs; i++) { NMIPAddress *addr = nm_setting_ip_config_get_address(s_ip, i); GVariant *label = nm_ip_address_get_attribute(addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL); if (label) { have_labels = TRUE; - break; - } + labels[i] = label; + } else + labels[i] = s_empty; } + if (!have_labels) return NULL; - labels = g_ptr_array_sized_new(num_addrs); - for (i = 0; i < num_addrs; i++) { - NMIPAddress *addr = nm_setting_ip_config_get_address(s_ip, i); - GVariant *label = nm_ip_address_get_attribute(addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL); - - g_ptr_array_add(labels, (char *) (label ? g_variant_get_string(label, NULL) : "")); - } - - ret = g_variant_new_strv((const char *const *) labels->pdata, labels->len); - g_ptr_array_unref(labels); - - return ret; + return g_variant_new_array(G_VARIANT_TYPE_STRING, labels, num_addrs); } static GVariant * -ip4_address_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +ip4_address_data_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { gs_unref_ptrarray GPtrArray *addrs = NULL; @@ -505,9 +512,9 @@ ip4_address_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) } static gboolean -ip4_address_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +ip4_address_data_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { - GPtrArray *addrs; + gs_unref_ptrarray GPtrArray *addrs = NULL; /* FIXME: properly handle errors */ @@ -519,12 +526,11 @@ ip4_address_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) addrs = nm_utils_ip_addresses_from_variant(value, AF_INET); g_object_set(setting, NM_SETTING_IP_CONFIG_ADDRESSES, addrs, NULL); - g_ptr_array_unref(addrs); return TRUE; } static GVariant * -ip4_routes_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +ip4_routes_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { gs_unref_ptrarray GPtrArray *routes = NULL; @@ -533,9 +539,9 @@ ip4_routes_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) } static gboolean -ip4_routes_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +ip4_routes_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { - GPtrArray *routes; + gs_unref_ptrarray GPtrArray *routes = NULL; /* FIXME: properly handle errors */ @@ -546,12 +552,11 @@ ip4_routes_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) routes = nm_utils_ip4_routes_from_variant(value); g_object_set(setting, property_info->name, routes, NULL); - g_ptr_array_unref(routes); return TRUE; } static GVariant * -ip4_route_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +ip4_route_data_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { gs_unref_ptrarray GPtrArray *routes = NULL; @@ -563,9 +568,9 @@ ip4_route_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) } static gboolean -ip4_route_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +ip4_route_data_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { - GPtrArray *routes; + gs_unref_ptrarray GPtrArray *routes = NULL; /* FIXME: properly handle errors */ @@ -577,7 +582,6 @@ ip4_route_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) routes = nm_utils_ip_routes_from_variant(value, AF_INET); g_object_set(setting, NM_SETTING_IP_CONFIG_ROUTES, routes, NULL); - g_ptr_array_unref(routes); return TRUE; } @@ -620,13 +624,15 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) setting_class->verify = verify; setting_ip_config_class->private_offset = g_type_class_get_instance_private_offset(klass); + setting_ip_config_class->is_ipv4 = TRUE; + setting_ip_config_class->addr_family = AF_INET; /* ---ifcfg-rh--- * property: method * variable: BOOTPROTO - * format: string - * values: none, dhcp (bootp), static, ibft, autoip, shared - * default: none + * format: string + * values: none, dhcp (bootp), static, ibft, autoip, shared + * default: none * description: Method used for IPv4 protocol configuration. * ---end--- */ @@ -637,10 +643,11 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) * description: List of DNS servers. * example: dns=1.2.3.4;8.8.8.8;8.8.4.4; * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: dns * variable: DNS1, DNS2, ... - * format: string + * format: string * description: List of DNS servers. Even if NetworkManager supports many DNS * servers, initscripts and resolver only care about the first three, usually. * example: DNS1=1.2.3.4 DNS2=10.0.0.254 DNS3=8.8.8.8 @@ -650,7 +657,7 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) /* ---ifcfg-rh--- * property: dns-search * variable: DOMAIN - * format: string (space-separated domains) + * format: string (space-separated domains) * description: List of DNS search domains. * ---end--- */ @@ -662,7 +669,8 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) * description: List of static IP addresses. * example: address1=192.168.100.100/24 address2=10.1.1.5/24 * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: addresses * variable: IPADDR, PREFIX (NETMASK), IPADDR1, PREFIX1 (NETMASK1), ... * description: List of static IP addresses. @@ -677,7 +685,8 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) * description: Gateway IP addresses as a string. * example: gateway=192.168.100.1 * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: gateway * variable: GATEWAY * description: Gateway IP address. @@ -693,7 +702,8 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) * example: route1=8.8.8.0/24,10.1.1.1,77 * route2=7.7.0.0/16 * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: routes * variable: ADDRESS1, NETMASK1, GATEWAY1, METRIC1, OPTIONS1, ... * description: List of static routes. They are not stored in ifcfg-* file, @@ -787,6 +797,17 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) * ---end--- */ + /* ---ifcfg-rh--- + * property: auto-route-ext-gw + * variable: IPV4_AUTO_ROUTE_EXT_GW(+) + * default: yes + * description: VPN connections will default to add the route automatically unless this + * setting is set to %FALSE. + * For other connection types, adding such an automatic route is currently + * not supported and setting this to %TRUE has no effect. + * ---end--- + */ + /** * NMSettingIP4Config:dhcp-client-id: * @@ -860,6 +881,7 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) * variable: DHCP_HOSTNAME_FLAGS * description: flags for the DHCP hostname and FQDN properties * example: DHCP_HOSTNAME_FLAGS=5 + * ---end--- */ /** @@ -961,11 +983,11 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) properties_override, g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_DNS), NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("au"), - .compare_fcn = _nm_setting_property_compare_fcn_default, - .to_dbus_fcn = ip4_dns_to_dbus, - .typdata_from_dbus.gprop_fcn = ip4_dns_from_dbus, - .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop, - .from_dbus_is_full = TRUE), ); + .compare_fcn = _nm_setting_ip_config_compare_fcn_dns, + .to_dbus_fcn = ip4_dns_to_dbus, + .from_dbus_fcn = ip4_dns_from_dbus, ), + .to_dbus_only_in_manager_process = TRUE, + .dbus_deprecated = TRUE, ); /* ---dbus--- * property: addresses @@ -995,15 +1017,19 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) properties_override, g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_ADDRESSES), NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aau"), - .to_dbus_fcn = ip4_addresses_get, + .to_dbus_fcn = ip4_addresses_to_dbus, .compare_fcn = _nm_setting_ip_config_compare_fcn_addresses, - .from_dbus_fcn = ip4_addresses_set, )); + .from_dbus_fcn = ip4_addresses_from_dbus, ), + .to_dbus_only_in_manager_process = TRUE, + .dbus_deprecated = TRUE, ); _nm_properties_override_dbus( properties_override, "address-labels", NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING_ARRAY, - .to_dbus_fcn = ip4_address_labels_get, - .compare_fcn = _nm_setting_property_compare_fcn_ignore, )); + .to_dbus_fcn = ip4_address_labels_to_dbus, + .compare_fcn = _nm_setting_property_compare_fcn_ignore, + /* from_dbus() is handled by ip4_addresses_from_dbus(). */), + .dbus_deprecated = TRUE, ); /* ---dbus--- * property: address-data @@ -1018,9 +1044,9 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) properties_override, "address-data", NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"), - .to_dbus_fcn = ip4_address_data_get, + .to_dbus_fcn = ip4_address_data_to_dbus, .compare_fcn = _nm_setting_property_compare_fcn_ignore, - .from_dbus_fcn = ip4_address_data_set, )); + .from_dbus_fcn = ip4_address_data_from_dbus, )); /* ---dbus--- * property: routes @@ -1090,7 +1116,11 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) * "mtu" - an unsigned 32 bit integer. * * - * "onlink" - a boolean value. + * "onlink" - a boolean value. The onlink flag + * is ignored for IPv4 routes without a gateway. That also means, + * with a positive "weight" the route cannot merge with ECMP routes + * which are onlink and have a gateway. + * * * * "quickack" - a boolean value. @@ -1117,6 +1147,17 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) * The default is unicast. * * + * "weight" - an unsigned 32 bit integer + * ranging from 0 to 256. A non-zero weight indicates that the IPv4 + * route is an ECMP IPv4 route. NetworkManager will automatically + * merge compatible ECMP routes into multi-hop routes. Setting to + * zero or omitting the attribute configures single hop routes that + * won't get merged. If the route finds no merge partner, it is + * configured as single hop route. Note that in + * NetworkManager, currently all nexthops of a ECMP route must share + * the same "onlink" flag in order to be mergable. + * + * * "window" - an unsigned 32 bit integer. * * @@ -1130,9 +1171,11 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) properties_override, g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_ROUTES), NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aau"), - .to_dbus_fcn = ip4_routes_get, + .to_dbus_fcn = ip4_routes_to_dbus, .compare_fcn = _nm_setting_ip_config_compare_fcn_routes, - .from_dbus_fcn = ip4_routes_set, )); + .from_dbus_fcn = ip4_routes_from_dbus, ), + .to_dbus_only_in_manager_process = TRUE, + .dbus_deprecated = TRUE, ); /* ---dbus--- * property: route-data @@ -1151,9 +1194,9 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) properties_override, "route-data", NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"), - .to_dbus_fcn = ip4_route_data_get, + .to_dbus_fcn = ip4_route_data_to_dbus, .compare_fcn = _nm_setting_property_compare_fcn_ignore, - .from_dbus_fcn = ip4_route_data_set, )); + .from_dbus_fcn = ip4_route_data_from_dbus, )); /* ---nmcli--- * property: routing-rules diff --git a/src/libnm-core-impl/nm-setting-ip6-config.c b/src/libnm-core-impl/nm-setting-ip6-config.c index 94794d1e..43e2e3c7 100644 --- a/src/libnm-core-impl/nm-setting-ip6-config.c +++ b/src/libnm-core-impl/nm-setting-ip6-config.c @@ -305,7 +305,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) if (priv->token) { if (priv->addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) { struct in6_addr i6_token; - char s_token[NM_UTILS_INET_ADDRSTRLEN]; + char s_token[NM_INET_ADDRSTRLEN]; if (inet_pton(AF_INET6, priv->token, &i6_token) != 1 || !_nm_utils_inet6_is_token(&i6_token)) { @@ -320,7 +320,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (g_strcmp0(priv->token, _nm_utils_inet6_ntop(&i6_token, s_token))) + if (g_strcmp0(priv->token, nm_inet6_ntop(&i6_token, s_token))) token_needs_normalization = TRUE; } else { g_set_error_literal(error, @@ -389,21 +389,29 @@ ip6_dns_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) GPtrArray *dns; dns = _nm_setting_ip_config_get_dns_array(NM_SETTING_IP_CONFIG(setting)); - if (nm_g_ptr_array_len(dns) == 0) return NULL; - return _nm_utils_ip6_dns_to_variant((const char *const *) dns->pdata, dns->len); + return nm_utils_dns_to_variant(AF_INET6, (const char *const *) dns->pdata, dns->len); } -static void -ip6_dns_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_GPROP_FCN_ARGS _nm_nil) +static gboolean +ip6_dns_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { - g_value_take_boxed(to, nm_utils_ip6_dns_from_variant(from)); + gs_strfreev char **strv = NULL; + + if (!_nm_setting_use_legacy_property(setting, connection_dict, "dns", "dns-data")) { + *out_is_modified = FALSE; + return TRUE; + } + + strv = nm_utils_ip6_dns_from_variant(value); + g_object_set(setting, NM_SETTING_IP_CONFIG_DNS, strv, NULL); + return TRUE; } static GVariant * -ip6_addresses_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +ip6_addresses_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { gs_unref_ptrarray GPtrArray *addrs = NULL; const char *gateway; @@ -414,10 +422,10 @@ ip6_addresses_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) } static gboolean -ip6_addresses_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +ip6_addresses_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { - GPtrArray *addrs; - char *gateway = NULL; + gs_unref_ptrarray GPtrArray *addrs = NULL; + gs_free char *gateway = NULL; if (!_nm_setting_use_legacy_property(setting, connection_dict, "addresses", "address-data")) { *out_is_modified = FALSE; @@ -432,13 +440,11 @@ ip6_addresses_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) NM_SETTING_IP_CONFIG_GATEWAY, gateway, NULL); - g_ptr_array_unref(addrs); - g_free(gateway); return TRUE; } static GVariant * -ip6_address_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +ip6_address_data_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { gs_unref_ptrarray GPtrArray *addrs = NULL; @@ -450,9 +456,9 @@ ip6_address_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) } static gboolean -ip6_address_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +ip6_address_data_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { - GPtrArray *addrs; + gs_unref_ptrarray GPtrArray *addrs = NULL; /* Ignore 'address-data' if we're going to process 'addresses' */ if (_nm_setting_use_legacy_property(setting, connection_dict, "addresses", "address-data")) { @@ -462,12 +468,11 @@ ip6_address_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) addrs = nm_utils_ip_addresses_from_variant(value, AF_INET6); g_object_set(setting, NM_SETTING_IP_CONFIG_ADDRESSES, addrs, NULL); - g_ptr_array_unref(addrs); return TRUE; } static GVariant * -ip6_routes_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +ip6_routes_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { gs_unref_ptrarray GPtrArray *routes = NULL; @@ -476,9 +481,9 @@ ip6_routes_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) } static gboolean -ip6_routes_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +ip6_routes_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { - GPtrArray *routes; + gs_unref_ptrarray GPtrArray *routes = NULL; if (!_nm_setting_use_legacy_property(setting, connection_dict, "routes", "route-data")) { *out_is_modified = FALSE; @@ -487,12 +492,11 @@ ip6_routes_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) routes = nm_utils_ip6_routes_from_variant(value); g_object_set(setting, property_info->name, routes, NULL); - g_ptr_array_unref(routes); return TRUE; } static GVariant * -ip6_route_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +ip6_route_data_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { gs_unref_ptrarray GPtrArray *routes = NULL; @@ -504,9 +508,9 @@ ip6_route_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) } static gboolean -ip6_route_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +ip6_route_data_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { - GPtrArray *routes; + gs_unref_ptrarray GPtrArray *routes = NULL; /* Ignore 'route-data' if we're going to process 'routes' */ if (_nm_setting_use_legacy_property(setting, connection_dict, "routes", "route-data")) { @@ -516,7 +520,6 @@ ip6_route_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) routes = nm_utils_ip_routes_from_variant(value, AF_INET6); g_object_set(setting, NM_SETTING_IP_CONFIG_ROUTES, routes, NULL); - g_ptr_array_unref(routes); return TRUE; } @@ -559,11 +562,13 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) setting_class->verify = verify; setting_ip_config_class->private_offset = g_type_class_get_instance_private_offset(klass); + setting_ip_config_class->is_ipv4 = FALSE; + setting_ip_config_class->addr_family = AF_INET6; /* ---ifcfg-rh--- * property: method * variable: IPV6INIT, IPV6FORWARDING, IPV6_AUTOCONF, DHCPV6C, IPV6_DISABLED - * default: IPV6INIT=yes; IPV6FORWARDING=no; IPV6_AUTOCONF=!IPV6FORWARDING, DHCPV6=no + * default: IPV6INIT=yes; IPV6FORWARDING=no; IPV6_AUTOCONF=!IPV6FORWARDING, DHCPV6=no * description: Method used for IPv6 protocol configuration. * ignore ~ IPV6INIT=no; auto ~ IPV6_AUTOCONF=yes; dhcp ~ IPV6_AUTOCONF=no and DHCPV6C=yes; * disabled ~ IPV6_DISABLED=yes @@ -576,10 +581,11 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) * description: List of DNS servers. * example: dns=2001:4860:4860::8888;2001:4860:4860::8844; * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: dns * variable: DNS1, DNS2, ... - * format: string + * format: string * description: List of DNS servers. NetworkManager uses the variables both * for IPv4 and IPv6. * ---end--- @@ -588,7 +594,7 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) /* ---ifcfg-rh--- * property: dns-search * variable: IPV6_DOMAIN(+) - * format: string (space-separated domains) + * format: string (space-separated domains) * description: List of DNS search domains. * ---end--- */ @@ -600,7 +606,8 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) * description: List of static IP addresses. * example: address1=abbe::cafe/96 address2=2001::1234 * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: addresses * variable: IPV6ADDR, IPV6ADDR_SECONDARIES * description: List of static IP addresses. @@ -616,7 +623,8 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) * description: Gateway IP addresses as a string. * example: gateway=abbe::1 * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: gateway * variable: IPV6_DEFAULTGW * description: Gateway IP address. @@ -631,7 +639,8 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) * description: List of IP routes. * example: route1=2001:4860:4860::/64,2620:52:0:2219:222:68ff:fe11:5403 * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: routes * variable: (none) * description: List of static routes. They are not stored in ifcfg-* file, @@ -732,6 +741,17 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) * ---end--- */ + /* ---ifcfg-rh--- + * property: auto-route-ext-gw + * variable: IPV6_AUTO_ROUTE_EXT_GW(+) + * default: yes + * description: VPN connections will default to add the route automatically unless this + * setting is set to %FALSE. + * For other connection types, adding such an automatic route is currently + * not supported and setting this to %TRUE has no effect. + * ---end--- + */ + /** * NMSettingIP6Config:ip6-privacy: * @@ -869,7 +889,7 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) * Since: 1.24 **/ /* ---ifcfg-rh--- - * property: dhcp-timeout + * property: ra-timeout * variable: IPV6_RA_TIMEOUT(+) * description: A timeout for waiting Router Advertisements in seconds. * example: IPV6_RA_TIMEOUT=10 @@ -972,11 +992,11 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) properties_override, g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_DNS), NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aay"), - .compare_fcn = _nm_setting_property_compare_fcn_default, - .to_dbus_fcn = ip6_dns_to_dbus, - .typdata_from_dbus.gprop_fcn = ip6_dns_from_dbus, - .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop, - .from_dbus_is_full = TRUE)); + .compare_fcn = _nm_setting_ip_config_compare_fcn_dns, + .to_dbus_fcn = ip6_dns_to_dbus, + .from_dbus_fcn = ip6_dns_from_dbus, ), + .to_dbus_only_in_manager_process = TRUE, + .dbus_deprecated = TRUE); /* ---dbus--- * property: addresses @@ -1006,9 +1026,11 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) properties_override, g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_ADDRESSES), NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("a(ayuay)"), - .to_dbus_fcn = ip6_addresses_get, + .to_dbus_fcn = ip6_addresses_to_dbus, .compare_fcn = _nm_setting_ip_config_compare_fcn_addresses, - .from_dbus_fcn = ip6_addresses_set, )); + .from_dbus_fcn = ip6_addresses_from_dbus, ), + .to_dbus_only_in_manager_process = TRUE, + .dbus_deprecated = TRUE, ); /* ---dbus--- * property: address-data @@ -1023,9 +1045,9 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) properties_override, "address-data", NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"), - .to_dbus_fcn = ip6_address_data_get, + .to_dbus_fcn = ip6_address_data_to_dbus, .compare_fcn = _nm_setting_property_compare_fcn_ignore, - .from_dbus_fcn = ip6_address_data_set, )); + .from_dbus_fcn = ip6_address_data_from_dbus, )); /* ---dbus--- * property: routes @@ -1124,9 +1146,11 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) properties_override, g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_ROUTES), NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("a(ayuayu)"), - .to_dbus_fcn = ip6_routes_get, + .to_dbus_fcn = ip6_routes_to_dbus, .compare_fcn = _nm_setting_ip_config_compare_fcn_routes, - .from_dbus_fcn = ip6_routes_set, )); + .from_dbus_fcn = ip6_routes_from_dbus, ), + .to_dbus_only_in_manager_process = TRUE, + .dbus_deprecated = TRUE, ); /* ---dbus--- * property: route-data @@ -1145,9 +1169,9 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) properties_override, "route-data", NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"), - .to_dbus_fcn = ip6_route_data_get, + .to_dbus_fcn = ip6_route_data_to_dbus, .compare_fcn = _nm_setting_property_compare_fcn_ignore, - .from_dbus_fcn = ip6_route_data_set, )); + .from_dbus_fcn = ip6_route_data_from_dbus, )); /* ---nmcli--- * property: routing-rules diff --git a/src/libnm-core-impl/nm-setting-loopback.c b/src/libnm-core-impl/nm-setting-loopback.c new file mode 100644 index 00000000..1451b411 --- /dev/null +++ b/src/libnm-core-impl/nm-setting-loopback.c @@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022 Red Hat, Inc. + */ + +#include "libnm-core-impl/nm-default-libnm-core.h" + +#include "nm-setting-loopback.h" + +#include "nm-connection-private.h" +#include "nm-setting-connection.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-loopback + * @short_description: Describes connection properties for loopback interfaces + * + * The #NMSettingLoopback object is a #NMSetting subclass that describes properties + * necessary for connection to loopback devices + **/ + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMSettingLoopback, PROP_MTU, ); + +typedef struct { + guint32 mtu; +} NMSettingLoopbackPrivate; + +/** + * NMSettingLoopback: + * + * Loopback Link Settings + * + * Since: 1.42 + */ +struct _NMSettingLoopback { + NMSetting parent; + NMSettingLoopbackPrivate _priv; +}; + +struct _NMSettingLoopbackClass { + NMSettingClass parent; +}; + +#define NM_SETTING_LOOPBACK_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMSettingLoopback, NM_IS_SETTING_LOOPBACK) + +G_DEFINE_TYPE(NMSettingLoopback, nm_setting_loopback, NM_TYPE_SETTING) + +/*****************************************************************************/ + +/** + * nm_setting_loopback_get_mtu: + * @setting: the #NMSettingLoopback + * + * Returns: the #NMSettingLoopback:mtu property of the setting + * + * Since: 1.42 + **/ +guint32 +nm_setting_loopback_get_mtu(NMSettingLoopback *setting) +{ + g_return_val_if_fail(NM_IS_SETTING_LOOPBACK(setting), 0); + + return NM_SETTING_LOOPBACK_GET_PRIVATE(setting)->mtu; +} + +static gboolean +verify(NMSetting *setting, NMConnection *connection, GError **error) +{ + if (connection) { + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + NMSettingConnection *s_con; + const char *method; + + if ((s_ip4 = nm_connection_get_setting_ip4_config(connection))) { + if ((method = nm_setting_ip_config_get_method(s_ip4)) + && !NM_IN_STRSET(method, + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("ipv4 method \"%s\" is not supported for loopback"), + method); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP_CONFIG_METHOD); + return FALSE; + } + if (!NM_IN_SET(nm_setting_ip4_config_get_link_local(NM_SETTING_IP4_CONFIG(s_ip4)), + NM_SETTING_IP4_LL_DEFAULT, + NM_SETTING_IP4_LL_AUTO, + NM_SETTING_IP4_LL_DISABLED)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("ipv4.link-local cannot be enabled for loopback")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP4_CONFIG_LINK_LOCAL); + return FALSE; + } + } + if ((s_ip6 = nm_connection_get_setting_ip6_config(connection))) { + if ((method = nm_setting_ip_config_get_method(s_ip6)) + && !NM_IN_STRSET(method, + NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("ipv6 method \"%s\" is not supported for loopback"), + method); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_IP_CONFIG_METHOD); + return FALSE; + } + } + + if ((s_con = nm_connection_get_setting_connection(connection))) { + if (nm_setting_connection_get_slave_type(s_con) + || nm_setting_connection_get_master(s_con)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("a loopback profile cannot be a port")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_CONNECTION_SETTING_NAME, + nm_setting_connection_get_slave_type(s_con) + ? NM_SETTING_CONNECTION_SLAVE_TYPE + : NM_SETTING_CONNECTION_MASTER); + return FALSE; + } + } + } + return TRUE; +} + +/*****************************************************************************/ + +static void +nm_setting_loopback_init(NMSettingLoopback *setting) +{} + +/** + * nm_setting_loopback_new: + * + * Creates a new #NMSettingLoopback object with default values. + * + * Returns: (transfer full): the new empty #NMSettingLoopback object + * + * Since: 1.42 + **/ +NMSetting * +nm_setting_loopback_new(void) +{ + return g_object_new(NM_TYPE_SETTING_LOOPBACK, NULL); +} + +static void +nm_setting_loopback_class_init(NMSettingLoopbackClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + NMSettingClass *setting_class = NM_SETTING_CLASS(klass); + GArray *properties_override = _nm_sett_info_property_override_create_array(); + + object_class->get_property = _nm_setting_property_get_property_direct; + object_class->set_property = _nm_setting_property_set_property_direct; + + setting_class->verify = verify; + + /** + * NMSettingLoopback:mtu: + * + * If non-zero, only transmit packets of the specified size or smaller, + * breaking larger packets up into multiple Ethernet frames. + * + * Since: 1.42 + **/ + /* ---ifcfg-rh--- + * property: mtu + * variable: MTU + * description: MTU of the interface. + * ---end--- + */ + _nm_setting_property_define_direct_uint32(properties_override, + obj_properties, + NM_SETTING_LOOPBACK_MTU, + PROP_MTU, + 0, + G_MAXUINT32, + 0, + NM_SETTING_PARAM_FUZZY_IGNORE, + NMSettingLoopback, + _priv.mtu); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + _nm_setting_class_commit(setting_class, + NM_META_SETTING_TYPE_LOOPBACK, + NULL, + properties_override, + 0); +} diff --git a/src/libnm-core-impl/nm-setting-macsec.c b/src/libnm-core-impl/nm-setting-macsec.c index 1463fd92..bf48f049 100644 --- a/src/libnm-core-impl/nm-setting-macsec.c +++ b/src/libnm-core-impl/nm-setting-macsec.c @@ -215,13 +215,13 @@ nm_setting_macsec_get_send_sci(NMSettingMacsec *setting) } static GPtrArray * -need_secrets(NMSetting *setting) +need_secrets(NMSetting *setting, gboolean check_rerequest) { NMSettingMacsecPrivate *priv = NM_SETTING_MACSEC_GET_PRIVATE(setting); GPtrArray *secrets = NULL; if (priv->mode == NM_SETTING_MACSEC_MODE_PSK) { - if (!priv->mka_cak + if ((check_rerequest || !priv->mka_cak) && !NM_FLAGS_HAS(priv->mka_cak_flags, NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { secrets = g_ptr_array_sized_new(1); g_ptr_array_add(secrets, NM_SETTING_MACSEC_MKA_CAK); diff --git a/src/libnm-core-impl/nm-setting-ovs-bridge.c b/src/libnm-core-impl/nm-setting-ovs-bridge.c index 7dc9fda4..8e850157 100644 --- a/src/libnm-core-impl/nm-setting-ovs-bridge.c +++ b/src/libnm-core-impl/nm-setting-ovs-bridge.c @@ -120,7 +120,7 @@ nm_setting_ovs_bridge_get_stp_enable(NMSettingOvsBridge *self) * * Returns: the #NMSettingOvsBridge:datapath_type property of the setting * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ const char * nm_setting_ovs_bridge_get_datapath_type(NMSettingOvsBridge *self) diff --git a/src/libnm-core-impl/nm-setting-ovs-dpdk.c b/src/libnm-core-impl/nm-setting-ovs-dpdk.c index e26f918b..87e9a3e8 100644 --- a/src/libnm-core-impl/nm-setting-ovs-dpdk.c +++ b/src/libnm-core-impl/nm-setting-ovs-dpdk.c @@ -22,7 +22,7 @@ /*****************************************************************************/ -NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_DEVARGS, PROP_N_RXQ, ); +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_DEVARGS, PROP_N_RXQ, PROP_N_RXQ_DESC, PROP_N_TXQ_DESC, ); /** * NMSettingOvsDpdk: @@ -34,6 +34,8 @@ struct _NMSettingOvsDpdk { char *devargs; guint32 n_rxq; + guint32 n_rxq_desc; + guint32 n_txq_desc; }; struct _NMSettingOvsDpdkClass { @@ -76,6 +78,72 @@ nm_setting_ovs_dpdk_get_n_rxq(NMSettingOvsDpdk *self) return self->n_rxq; } +/** + * nm_setting_ovs_dpdk_get_n_rxq_desc: + * @self: the #NMSettingOvsDpdk + * + * Returns: the #NMSettingOvsDpdk:n-rxq-desc property of the setting + * + * Since: 1.42 + **/ +guint32 +nm_setting_ovs_dpdk_get_n_rxq_desc(NMSettingOvsDpdk *self) +{ + g_return_val_if_fail(NM_IS_SETTING_OVS_DPDK(self), 0); + + return self->n_rxq_desc; +} + +/** + * nm_setting_ovs_dpdk_get_n_txq_desc: + * @self: the #NMSettingOvsDpdk + * + * Returns: the #NMSettingOvsDpdk:n-txq-desc property of the setting + * + * Since: 1.42 + **/ +guint32 +nm_setting_ovs_dpdk_get_n_txq_desc(NMSettingOvsDpdk *self) +{ + g_return_val_if_fail(NM_IS_SETTING_OVS_DPDK(self), 0); + + return self->n_txq_desc; +} + +/*****************************************************************************/ + +static gboolean +verify(NMSetting *setting, NMConnection *connection, GError **error) +{ + NMSettingOvsDpdk *self = NM_SETTING_OVS_DPDK(setting); + + if (self->n_rxq_desc != 0 && !nm_utils_is_power_of_two(self->n_rxq_desc)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("must be a power of two")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_OVS_DPDK_SETTING_NAME, + NM_SETTING_OVS_DPDK_N_RXQ_DESC); + return FALSE; + } + + if (self->n_txq_desc != 0 && !nm_utils_is_power_of_two(self->n_txq_desc)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("must be a power of two")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_OVS_DPDK_SETTING_NAME, + NM_SETTING_OVS_DPDK_N_TXQ_DESC); + return FALSE; + } + + return TRUE; +} + /*****************************************************************************/ static void @@ -107,6 +175,8 @@ nm_setting_ovs_dpdk_class_init(NMSettingOvsDpdkClass *klass) object_class->get_property = _nm_setting_property_get_property_direct; object_class->set_property = _nm_setting_property_set_property_direct; + setting_class->verify = verify; + /** * NMSettingOvsDpdk:devargs: * @@ -142,6 +212,50 @@ nm_setting_ovs_dpdk_class_init(NMSettingOvsDpdkClass *klass) NMSettingOvsDpdk, n_rxq); + /** + * NMSettingOvsDpdk:n-rxq-desc: + * + * The rx queue size (number of rx descriptors) for DPDK ports. + * Must be zero or a power of 2 between 1 and 4096, and supported + * by the hardware. Defaults to zero which means to leave the + * parameter in OVS unspecified and effectively configures 2048 + * descriptors. + * + * Since: 1.42 + **/ + _nm_setting_property_define_direct_uint32(properties_override, + obj_properties, + NM_SETTING_OVS_DPDK_N_RXQ_DESC, + PROP_N_RXQ_DESC, + 0, + 4096, + 0, + NM_SETTING_PARAM_INFERRABLE, + NMSettingOvsDpdk, + n_rxq_desc); + + /** + * NMSettingOvsDpdk:n-txq-desc: + * + * The tx queue size (number of tx descriptors) for DPDK ports. + * Must be zero or a power of 2 between 1 and 4096, and supported + * by the hardware. Defaults to zero which means to leave the + * parameter in OVS unspecified and effectively configures 2048 + * descriptors. + * + * Since: 1.42 + **/ + _nm_setting_property_define_direct_uint32(properties_override, + obj_properties, + NM_SETTING_OVS_DPDK_N_TXQ_DESC, + PROP_N_TXQ_DESC, + 0, + 4096, + 0, + NM_SETTING_PARAM_INFERRABLE, + NMSettingOvsDpdk, + n_txq_desc); + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); _nm_setting_class_commit(setting_class, diff --git a/src/libnm-core-impl/nm-setting-ovs-external-ids.c b/src/libnm-core-impl/nm-setting-ovs-external-ids.c index f4e31f81..54684380 100644 --- a/src/libnm-core-impl/nm-setting-ovs-external-ids.c +++ b/src/libnm-core-impl/nm-setting-ovs-external-ids.c @@ -10,6 +10,7 @@ #include "nm-setting-private.h" #include "nm-utils-private.h" #include "nm-connection-private.h" +#include "nm-setting-ovs-other-config.h" #define MAX_NUM_KEYS 256 @@ -53,15 +54,6 @@ G_DEFINE_TYPE(NMSettingOvsExternalIDs, nm_setting_ovs_external_ids, NM_TYPE_SETT /*****************************************************************************/ -static gboolean -_exid_key_char_is_regular(char ch) -{ - /* allow words of printable characters, plus some - * special characters, for example to support base64 encoding. */ - return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') - || NM_IN_SET(ch, '-', '_', '+', '/', '=', '.'); -} - /** * nm_setting_ovs_external_ids_check_key: * @key: (allow-none): the key to check @@ -105,7 +97,7 @@ nm_setting_ovs_external_ids_check_key(const char *key, GError **error) _("key must be UTF8")); return FALSE; } - if (!NM_STRCHAR_ALL(key, ch, _exid_key_char_is_regular(ch))) { + if (!NM_STRCHAR_ALL(key, ch, nm_ascii_is_regular_char(ch))) { /* Probably OVS is more forgiving about what makes a valid key for * an external-id. However, we are strict (at least, for now). */ g_set_error_literal(error, @@ -127,6 +119,56 @@ nm_setting_ovs_external_ids_check_key(const char *key, GError **error) return TRUE; } +gboolean +_nm_setting_ovs_verify_connection_type(GType gtype, NMConnection *connection, GError **error) +{ + NMSettingConnection *s_con; + const char *type; + const char *slave_type; + + nm_assert(!connection || NM_IS_CONNECTION(connection)); + nm_assert(NM_IN_SET(gtype, NM_TYPE_SETTING_OVS_EXTERNAL_IDS, NM_TYPE_SETTING_OVS_OTHER_CONFIG)); + nm_assert(!error || !*error); + + if (!connection) { + /* We don't know. It's valid. */ + return TRUE; + } + + type = nm_connection_get_connection_type(connection); + if (!type) { + NMSetting *s_base; + + s_base = _nm_connection_find_base_type_setting(connection); + if (s_base) + type = nm_setting_get_name(s_base); + } + if (NM_IN_STRSET(type, + NM_SETTING_OVS_BRIDGE_SETTING_NAME, + NM_SETTING_OVS_PORT_SETTING_NAME, + NM_SETTING_OVS_INTERFACE_SETTING_NAME)) + return TRUE; + + if ((s_con = nm_connection_get_setting_connection(connection)) + && _nm_connection_detect_slave_type_full(s_con, + connection, + &slave_type, + NULL, + NULL, + NULL, + NULL) + && nm_streq0(slave_type, NM_SETTING_OVS_PORT_SETTING_NAME)) + return TRUE; + + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("OVS %s can only be added to a profile of type OVS " + "bridge/port/interface or to OVS system interface"), + gtype == NM_TYPE_SETTING_OVS_EXTERNAL_IDS ? "external-ids" : "other-config"); + return FALSE; +} + /** * nm_setting_ovs_external_ids_check_val: * @val: (allow-none): the value to check @@ -204,6 +246,8 @@ nm_setting_ovs_external_ids_get_data_keys(NMSettingOvsExternalIDs *setting, guin NMSettingOvsExternalIDs *self = setting; NMSettingOvsExternalIDsPrivate *priv; + NM_SET_OUT(out_len, 0); + g_return_val_if_fail(NM_IS_SETTING_OVS_EXTERNAL_IDS(self), NULL); priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self); @@ -302,12 +346,16 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) if (priv->data) { gs_free_error GError *local = NULL; - GHashTableIter iter; - const char *key; - const char *val; + const char *const *keys; + guint len; + guint i; + + keys = nm_setting_ovs_external_ids_get_data_keys(self, &len); + + for (i = 0; i < len; i++) { + const char *key = keys[i]; + const char *val = g_hash_table_lookup(priv->data, key); - g_hash_table_iter_init(&iter, priv->data); - while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) { if (!nm_setting_ovs_external_ids_check_key(key, &local)) { g_set_error(error, NM_CONNECTION_ERROR, @@ -336,7 +384,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("maximum number of user data entries reached (%u instead of %u)"), + _("maximum number of entries reached (%u instead of %u)"), g_hash_table_size(priv->data), (unsigned) MAX_NUM_KEYS); g_prefix_error(error, @@ -346,44 +394,10 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (connection) { - NMSettingConnection *s_con; - const char *type; - const char *slave_type; - - type = nm_connection_get_connection_type(connection); - if (!type) { - NMSetting *s_base; - - s_base = _nm_connection_find_base_type_setting(connection); - if (s_base) - type = nm_setting_get_name(s_base); - } - if (NM_IN_STRSET(type, - NM_SETTING_OVS_BRIDGE_SETTING_NAME, - NM_SETTING_OVS_PORT_SETTING_NAME, - NM_SETTING_OVS_INTERFACE_SETTING_NAME)) - goto connection_type_is_good; - - if ((s_con = nm_connection_get_setting_connection(connection)) - && _nm_connection_detect_slave_type_full(s_con, - connection, - &slave_type, - NULL, - NULL, - NULL, - NULL) - && nm_streq0(slave_type, NM_SETTING_OVS_PORT_SETTING_NAME)) - goto connection_type_is_good; - - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("OVS external IDs can only be added to a profile of type OVS " - "bridge/port/interface or to OVS system interface")); + if (!_nm_setting_ovs_verify_connection_type(NM_TYPE_SETTING_OVS_EXTERNAL_IDS, + connection, + error)) return FALSE; - } -connection_type_is_good: return TRUE; } @@ -519,7 +533,7 @@ nm_setting_ovs_external_ids_class_init(NMSettingOvsExternalIDsClass *klass) /** * NMSettingOvsExternalIDs:data: (type GHashTable(utf8,utf8)) * - * A dictionary of key/value pairs with exernal-ids for OVS. + * A dictionary of key/value pairs with external-ids for OVS. * * Since: 1.30 **/ diff --git a/src/libnm-core-impl/nm-setting-ovs-interface.c b/src/libnm-core-impl/nm-setting-ovs-interface.c index 6554f520..34e66480 100644 --- a/src/libnm-core-impl/nm-setting-ovs-interface.c +++ b/src/libnm-core-impl/nm-setting-ovs-interface.c @@ -21,7 +21,7 @@ /*****************************************************************************/ -NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_TYPE, ); +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_TYPE, PROP_OFPORT_REQUEST, ); /** * NMSettingOvsInterface: @@ -31,7 +31,8 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_TYPE, ); struct _NMSettingOvsInterface { NMSetting parent; - char *type; + char *type; + guint32 ofport_request; }; struct _NMSettingOvsInterfaceClass { @@ -58,6 +59,22 @@ nm_setting_ovs_interface_get_interface_type(NMSettingOvsInterface *self) return self->type; } +/** + * nm_setting_ovs_interface_get_ofport_request: + * @self: the #NMSettingOvsInterface + * + * Returns: id of the preassigned ovs port + * + * Since: 1.42 + **/ +guint32 +nm_setting_ovs_interface_get_ofport_request(NMSettingOvsInterface *self) +{ + g_return_val_if_fail(NM_IS_SETTING_OVS_INTERFACE(self), 0); + + return self->ofport_request; +} + /*****************************************************************************/ int @@ -378,6 +395,27 @@ nm_setting_ovs_interface_class_init(NMSettingOvsInterfaceClass *klass) NM_SETTING_PARAM_INFERRABLE, NMSettingOvsInterface, type); + /** + * NMSettingOvsInterface:ofport-request: + * + * Open vSwitch openflow port number. + * Defaults to zero which means that port number will not be specified + * and it will be chosen randomly by ovs. OpenFlow ports are the network interfaces + * for passing packets between OpenFlow processing and the rest of the network. + * OpenFlow switches connect logically to each other via their OpenFlow ports. + * + * Since: 1.42 + **/ + _nm_setting_property_define_direct_uint32(properties_override, + obj_properties, + NM_SETTING_OVS_INTERFACE_OFPORT_REQUEST, + PROP_OFPORT_REQUEST, + 0, + 65279, + 0, + NM_SETTING_PARAM_INFERRABLE, + NMSettingOvsInterface, + ofport_request); g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); diff --git a/src/libnm-core-impl/nm-setting-ovs-other-config.c b/src/libnm-core-impl/nm-setting-ovs-other-config.c new file mode 100644 index 00000000..ca46ae95 --- /dev/null +++ b/src/libnm-core-impl/nm-setting-ovs-other-config.c @@ -0,0 +1,403 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 - 2020, 2022 Red Hat, Inc. + */ + +#include "libnm-core-impl/nm-default-libnm-core.h" + +#include "nm-setting-ovs-other-config.h" + +#include "nm-setting-private.h" +#include "nm-utils-private.h" +#include "nm-connection-private.h" + +#define MAX_NUM_KEYS 256 + +/*****************************************************************************/ + +/** + * SECTION:nm-setting-ovs-other-config + * @short_description: Other-config settings for OVS + * + * The #NMSettingOvsOtherConfig object is a #NMSetting subclass that allows to + * configure other_config settings for OVS. See also "other_config" in the + * "ovs-vswitchd.conf.db" manual for the keys that OVS supports. + **/ + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE(NMSettingOvsOtherConfig, PROP_DATA, ); + +typedef struct { + GHashTable *data; + const char **data_keys; +} NMSettingOvsOtherConfigPrivate; + +/** + * NMSettingOvsOtherConfig: + * + * OVS Other Config Settings + * + * Since: 1.42 + */ +struct _NMSettingOvsOtherConfig { + NMSetting parent; + NMSettingOvsOtherConfigPrivate _priv; +}; + +struct _NMSettingOvsOtherConfigClass { + NMSettingClass parent; +}; + +G_DEFINE_TYPE(NMSettingOvsOtherConfig, nm_setting_ovs_other_config, NM_TYPE_SETTING) + +#define NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMSettingOvsOtherConfig, NM_IS_SETTING_OVS_OTHER_CONFIG) + +/*****************************************************************************/ + +static GHashTable * +_create_data_hash(void) +{ + return g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); +} + +GHashTable * +_nm_setting_ovs_other_config_get_data(NMSettingOvsOtherConfig *self) +{ + return NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self)->data; +} + +/** + * nm_setting_ovs_other_config_get_data_keys: + * @setting: the #NMSettingOvsOtherConfig + * @out_len: (out): the length of the returned array + * + * Returns: (array length=out_len) (transfer none): a + * %NULL-terminated array containing each key from the table. + * + * Since: 1.42 + **/ +const char *const * +nm_setting_ovs_other_config_get_data_keys(NMSettingOvsOtherConfig *setting, guint *out_len) +{ + NMSettingOvsOtherConfig *self = setting; + NMSettingOvsOtherConfigPrivate *priv; + + NM_SET_OUT(out_len, 0); + + g_return_val_if_fail(NM_IS_SETTING_OVS_OTHER_CONFIG(self), NULL); + + priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + if (priv->data_keys) { + NM_SET_OUT(out_len, g_hash_table_size(priv->data)); + return priv->data_keys; + } + + priv->data_keys = nm_strdict_get_keys(priv->data, TRUE, out_len); + + /* don't return %NULL, but hijack the @data_keys fields as a pseudo + * empty strv array. */ + return priv->data_keys ?: ((const char **) &priv->data_keys); +} + +/*****************************************************************************/ + +/** + * nm_setting_ovs_other_config_get_data: + * @setting: the #NMSettingOvsOtherConfig instance + * @key: the other-config to lookup + * + * Since: 1.42 + * + * Returns: (transfer none): the value associated with @key or %NULL if no such + * value exists. + */ +const char * +nm_setting_ovs_other_config_get_data(NMSettingOvsOtherConfig *setting, const char *key) +{ + NMSettingOvsOtherConfig *self = setting; + NMSettingOvsOtherConfigPrivate *priv; + + g_return_val_if_fail(NM_IS_SETTING_OVS_OTHER_CONFIG(self), NULL); + g_return_val_if_fail(key, NULL); + + priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + if (!priv->data) + return NULL; + + return g_hash_table_lookup(priv->data, key); +} + +/** + * nm_setting_ovs_other_config_set_data: + * @setting: the #NMSettingOvsOtherConfig instance + * @key: the key to set + * @val: (allow-none): the value to set or %NULL to clear a key. + * + * Since: 1.42 + */ +void +nm_setting_ovs_other_config_set_data(NMSettingOvsOtherConfig *setting, + const char *key, + const char *val) +{ + NMSettingOvsOtherConfig *self = setting; + NMSettingOvsOtherConfigPrivate *priv; + + g_return_if_fail(NM_IS_SETTING_OVS_OTHER_CONFIG(self)); + + priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + if (!val) { + if (priv->data && g_hash_table_remove(priv->data, key)) + goto out_changed; + return; + } + + if (priv->data) { + const char *val2; + + if (g_hash_table_lookup_extended(priv->data, key, NULL, (gpointer *) &val2)) { + if (nm_streq(val, val2)) + return; + } + } else + priv->data = _create_data_hash(); + + g_hash_table_insert(priv->data, g_strdup(key), g_strdup(val)); + +out_changed: + nm_clear_g_free(&priv->data_keys); + _notify(self, PROP_DATA); +} + +/*****************************************************************************/ + +static gboolean +verify(NMSetting *setting, NMConnection *connection, GError **error) +{ + NMSettingOvsOtherConfig *self = NM_SETTING_OVS_OTHER_CONFIG(setting); + NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + if (priv->data) { + gs_free_error GError *local = NULL; + const char *const *keys; + guint len; + guint i; + + keys = nm_setting_ovs_other_config_get_data_keys(self, &len); + + for (i = 0; i < len; i++) { + const char *key = keys[i]; + const char *val = g_hash_table_lookup(priv->data, key); + + if (!nm_setting_ovs_other_config_check_key(key, &local)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("invalid key \"%s\": %s"), + key, + local->message); + } else if (!nm_setting_ovs_other_config_check_val(val, &local)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("invalid value for \"%s\": %s"), + key, + local->message); + } else + continue; + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME, + NM_SETTING_OVS_OTHER_CONFIG_DATA); + return FALSE; + } + } + + if (priv->data && g_hash_table_size(priv->data) > MAX_NUM_KEYS) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("maximum number of entries reached (%u instead of %u)"), + g_hash_table_size(priv->data), + (unsigned) MAX_NUM_KEYS); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME, + NM_SETTING_OVS_OTHER_CONFIG_DATA); + return FALSE; + } + + if (!_nm_setting_ovs_verify_connection_type(NM_TYPE_SETTING_OVS_OTHER_CONFIG, + connection, + error)) + return FALSE; + + return TRUE; +} + +static NMTernary +compare_fcn_data(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil) +{ + NMSettingOvsOtherConfigPrivate *priv; + NMSettingOvsOtherConfigPrivate *pri2; + + if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)) + return NM_TERNARY_DEFAULT; + + if (!set_b) + return TRUE; + + priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(NM_SETTING_OVS_OTHER_CONFIG(set_a)); + pri2 = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(NM_SETTING_OVS_OTHER_CONFIG(set_b)); + return nm_utils_hashtable_equal(priv->data, pri2->data, TRUE, g_str_equal); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMSettingOvsOtherConfig *self = NM_SETTING_OVS_OTHER_CONFIG(object); + NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + GHashTableIter iter; + GHashTable *data; + const char *key; + const char *val; + + switch (prop_id) { + case PROP_DATA: + data = _create_data_hash(); + if (priv->data) { + g_hash_table_iter_init(&iter, priv->data); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) + g_hash_table_insert(data, g_strdup(key), g_strdup(val)); + } + g_value_take_boxed(value, data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMSettingOvsOtherConfig *self = NM_SETTING_OVS_OTHER_CONFIG(object); + NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + switch (prop_id) { + case PROP_DATA: + { + gs_unref_hashtable GHashTable *old = NULL; + GHashTableIter iter; + GHashTable *data; + const char *key; + const char *val; + + nm_clear_g_free(&priv->data_keys); + + old = g_steal_pointer(&priv->data); + + data = g_value_get_boxed(value); + if (nm_g_hash_table_size(data) <= 0) + return; + + priv->data = _create_data_hash(); + g_hash_table_iter_init(&iter, data); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) + g_hash_table_insert(priv->data, g_strdup(key), g_strdup(val)); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_setting_ovs_other_config_init(NMSettingOvsOtherConfig *self) +{} + +/** + * nm_setting_ovs_other_config_new: + * + * Creates a new #NMSettingOvsOtherConfig object with default values. + * + * Returns: (transfer full) (type NMSettingOvsOtherConfig): the new empty + * #NMSettingOvsOtherConfig object + * + * Since: 1.42 + */ +NMSetting * +nm_setting_ovs_other_config_new(void) +{ + return g_object_new(NM_TYPE_SETTING_OVS_OTHER_CONFIG, NULL); +} + +static void +finalize(GObject *object) +{ + NMSettingOvsOtherConfig *self = NM_SETTING_OVS_OTHER_CONFIG(object); + NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self); + + g_free(priv->data_keys); + if (priv->data) + g_hash_table_unref(priv->data); + + G_OBJECT_CLASS(nm_setting_ovs_other_config_parent_class)->finalize(object); +} + +static void +nm_setting_ovs_other_config_class_init(NMSettingOvsOtherConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + NMSettingClass *setting_class = NM_SETTING_CLASS(klass); + GArray *properties_override = _nm_sett_info_property_override_create_array(); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + setting_class->verify = verify; + + /** + * NMSettingOvsOtherConfig:data: (type GHashTable(utf8,utf8)) + * + * A dictionary of key/value pairs with other_config settings for OVS. + * See also "other_config" in the "ovs-vswitchd.conf.db" manual for the keys + * that OVS supports. + * + * Since: 1.42 + **/ + obj_properties[PROP_DATA] = g_param_spec_boxed(NM_SETTING_OVS_OTHER_CONFIG_DATA, + "", + "", + G_TYPE_HASH_TABLE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + _nm_properties_override_gobj( + properties_override, + obj_properties[PROP_DATA], + NM_SETT_INFO_PROPERT_TYPE_GPROP(NM_G_VARIANT_TYPE("a{ss}"), + .typdata_from_dbus.gprop_fcn = _nm_utils_strdict_from_dbus, + .typdata_to_dbus.gprop_type = + NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_STRDICT, + .compare_fcn = compare_fcn_data, + .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop, + .from_dbus_is_full = TRUE)); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + _nm_setting_class_commit(setting_class, + NM_META_SETTING_TYPE_OVS_OTHER_CONFIG, + NULL, + properties_override, + 0); +} diff --git a/src/libnm-core-impl/nm-setting-ovs-port.c b/src/libnm-core-impl/nm-setting-ovs-port.c index ab9c0a11..190f1e5d 100644 --- a/src/libnm-core-impl/nm-setting-ovs-port.c +++ b/src/libnm-core-impl/nm-setting-ovs-port.c @@ -21,12 +21,14 @@ /*****************************************************************************/ -NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_VLAN_MODE, - PROP_TAG, - PROP_LACP, - PROP_BOND_MODE, - PROP_BOND_UPDELAY, - PROP_BOND_DOWNDELAY, ); +NM_GOBJECT_PROPERTIES_DEFINE(NMSettingOvsPort, + PROP_VLAN_MODE, + PROP_TAG, + PROP_TRUNKS, + PROP_LACP, + PROP_BOND_MODE, + PROP_BOND_UPDELAY, + PROP_BOND_DOWNDELAY, ); /** * NMSettingOvsPort: @@ -36,12 +38,13 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_VLAN_MODE, struct _NMSettingOvsPort { NMSetting parent; - char *vlan_mode; - char *lacp; - char *bond_mode; - guint32 tag; - guint32 bond_updelay; - guint32 bond_downdelay; + GPtrArray *trunks; + char *vlan_mode; + char *lacp; + char *bond_mode; + guint32 tag; + guint32 bond_updelay; + guint32 bond_downdelay; }; struct _NMSettingOvsPortClass { @@ -84,6 +87,143 @@ nm_setting_ovs_port_get_tag(NMSettingOvsPort *self) return self->tag; } +/*****************************************************************************/ + +/** + * nm_setting_ovs_port_add_trunk: + * @setting: the #NMSettingOvsPort + * @trunk: the trunk to add + * + * Appends a new trunk range to the setting. + * This takes a reference to @trunk. + * + * Since: 1.42 + **/ +void +nm_setting_ovs_port_add_trunk(NMSettingOvsPort *self, NMRange *trunk) +{ + g_return_if_fail(NM_IS_SETTING_OVS_PORT(self)); + g_return_if_fail(trunk); + + g_ptr_array_add(self->trunks, nm_range_ref(trunk)); + _notify(self, PROP_TRUNKS); +} + +/** + * nm_setting_ovs_port_get_num_trunks: + * @setting: the #NMSettingOvsPort + * + * Returns: the number of trunk ranges + * + * Since: 1.42 + **/ +guint +nm_setting_ovs_port_get_num_trunks(NMSettingOvsPort *self) +{ + g_return_val_if_fail(NM_IS_SETTING_OVS_PORT(self), 0); + + return self->trunks->len; +} + +/** + * nm_setting_ovs_port_get_trunk: + * @setting: the #NMSettingOvsPort + * @idx: index number of the trunk range to return + * + * Returns: (transfer none): the trunk range at index @idx + * + * Since: 1.42 + **/ +NMRange * +nm_setting_ovs_port_get_trunk(NMSettingOvsPort *self, guint idx) +{ + g_return_val_if_fail(NM_IS_SETTING_OVS_PORT(self), NULL); + + g_return_val_if_fail(idx < self->trunks->len, NULL); + + return self->trunks->pdata[idx]; +} + +/** + * nm_setting_ovs_port_remove_trunk: + * @setting: the #NMSettingOvsPort + * @idx: index number of the trunk range. + * + * Removes the trunk range at index @idx. + * + * Since: 1.42 + **/ +void +nm_setting_ovs_port_remove_trunk(NMSettingOvsPort *self, guint idx) +{ + g_return_if_fail(NM_IS_SETTING_OVS_PORT(self)); + + g_return_if_fail(idx < self->trunks->len); + + g_ptr_array_remove_index(self->trunks, idx); + _notify(self, PROP_TRUNKS); +} + +/** + * nm_setting_ovs_port_remove_trunk_by_value: + * @setting: the #NMSettingOvsPort + * @start: the trunk range start index + * @end: the trunk range end index + * + * Remove the trunk range with range @start to @end. + * + * Returns: %TRUE if the trunk range was found and removed; %FALSE otherwise + * + * Since: 1.42 + **/ +gboolean +nm_setting_ovs_port_remove_trunk_by_value(NMSettingOvsPort *self, guint start, guint end) +{ + NMRange *trunk; + guint i; + + g_return_val_if_fail(NM_IS_SETTING_OVS_PORT(self), FALSE); + + for (i = 0; i < self->trunks->len; i++) { + trunk = (NMRange *) self->trunks->pdata[i]; + if (trunk->start == start && trunk->end == end) { + g_ptr_array_remove_index(self->trunks, i); + _notify(self, PROP_TRUNKS); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_ovs_port_clear_trunks: + * @setting: the #NMSettingOvsPort + * + * Removes all configured trunk ranges. + * + * Since: 1.42 + **/ +void +nm_setting_ovs_port_clear_trunks(NMSettingOvsPort *self) +{ + g_return_if_fail(NM_IS_SETTING_OVS_PORT(self)); + + if (self->trunks->len != 0) { + g_ptr_array_set_size(self->trunks, 0); + _notify(self, PROP_TRUNKS); + } +} + +const GPtrArray * +_nm_setting_ovs_port_get_trunks_arr(NMSettingOvsPort *self) +{ + g_return_val_if_fail(NM_IS_SETTING_OVS_PORT(self), NULL); + + return self->trunks; +} + +/*****************************************************************************/ + /** * nm_setting_ovs_port_get_lacp: * @self: the #NMSettingOvsPort @@ -150,6 +290,107 @@ nm_setting_ovs_port_get_bond_downdelay(NMSettingOvsPort *self) /*****************************************************************************/ +static int +range_cmp(gconstpointer a, gconstpointer b) +{ + const NMRange *range_a = *(const NMRange **) a; + const NMRange *range_b = *(const NMRange **) b; + + return nm_range_cmp(range_a, range_b); +} + +gboolean +_nm_setting_ovs_port_sort_trunks(NMSettingOvsPort *self) +{ + gboolean need_sort = FALSE; + guint i; + + for (i = 1; i < self->trunks->len; i++) { + NMRange *range_prev = self->trunks->pdata[i - 1]; + NMRange *range = self->trunks->pdata[i]; + + if (nm_range_cmp(range_prev, range) > 0) { + need_sort = TRUE; + break; + } + } + + if (need_sort) { + g_ptr_array_sort(self->trunks, range_cmp); + _notify(self, PROP_TRUNKS); + } + + return need_sort; +} + +static gboolean +verify_trunks(GPtrArray *ranges, GError **error) +{ + gs_unref_hashtable GHashTable *h = NULL; + NMRange *range; + guint i; + guint vlan; + + if (!ranges) + return TRUE; + + h = g_hash_table_new(nm_direct_hash, NULL); + + for (i = 0; i < ranges->len; i++) { + range = ranges->pdata[i]; + nm_assert(range->start <= range->end); + + if (range->start > 4095 || range->end > 4095) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("VLANs must be between 0 and 4095")); + return FALSE; + } + + for (vlan = range->start; vlan <= range->end; vlan++) { + if (!nm_g_hash_table_add(h, GUINT_TO_POINTER(vlan))) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("duplicate VLAN %u"), + vlan); + return FALSE; + } + } + } + + return TRUE; +} + +static gboolean +verify_trunks_normalizable(GPtrArray *ranges, GError **error) +{ + guint i; + + nm_assert(verify_trunks(ranges, NULL)); + + if (!ranges || ranges->len <= 1) + return TRUE; + + for (i = 1; i < ranges->len; i++) { + NMRange *range_prev = ranges->pdata[i - 1]; + NMRange *range = ranges->pdata[i]; + + if (nm_range_cmp(range_prev, range) > 0) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("VLANs %u and %u are not sorted in ascending order"), + (guint) range_prev->start, + (guint) range->start); + return FALSE; + } + } + + return TRUE; +} + static int verify(NMSetting *setting, NMConnection *connection, GError **error) { @@ -208,6 +449,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) "native-tagged", "native-untagged", "trunk", + "dot1q-tunnel", NULL)) { g_set_error(error, NM_CONNECTION_ERROR, @@ -257,14 +499,68 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } + if (!verify_trunks(self->trunks, error)) { + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_OVS_PORT_SETTING_NAME, + NM_SETTING_OVS_PORT_TRUNKS); + return FALSE; + } + + if (!verify_trunks_normalizable(self->trunks, error)) { + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_OVS_PORT_SETTING_NAME, + NM_SETTING_OVS_PORT_TRUNKS); + return NM_SETTING_VERIFY_NORMALIZABLE; + } + return TRUE; } /*****************************************************************************/ +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMSettingOvsPort *self = NM_SETTING_OVS_PORT(object); + + switch (prop_id) { + case PROP_TRUNKS: + g_value_take_boxed(value, + _nm_utils_copy_array(self->trunks, + (NMUtilsCopyFunc) nm_range_ref, + (GDestroyNotify) nm_range_unref)); + break; + default: + _nm_setting_property_get_property_direct(object, prop_id, value, pspec); + break; + } +} + +static void +set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMSettingOvsPort *self = NM_SETTING_OVS_PORT(object); + + switch (prop_id) { + case PROP_TRUNKS: + g_ptr_array_unref(self->trunks); + self->trunks = _nm_utils_copy_array(g_value_get_boxed(value), + (NMUtilsCopyFunc) nm_range_ref, + (GDestroyNotify) nm_range_unref); + break; + default: + _nm_setting_property_set_property_direct(object, prop_id, value, pspec); + break; + } +} + static void nm_setting_ovs_port_init(NMSettingOvsPort *self) -{} +{ + self->trunks = g_ptr_array_new_with_free_func((GDestroyNotify) nm_range_unref); +} /** * nm_setting_ovs_port_new: @@ -281,6 +577,16 @@ nm_setting_ovs_port_new(void) return g_object_new(NM_TYPE_SETTING_OVS_PORT, NULL); } +static void +finalize(GObject *object) +{ + NMSettingOvsPort *self = NM_SETTING_OVS_PORT(object); + + g_ptr_array_unref(self->trunks); + + G_OBJECT_CLASS(nm_setting_ovs_port_parent_class)->finalize(object); +} + static void nm_setting_ovs_port_class_init(NMSettingOvsPortClass *klass) { @@ -288,8 +594,9 @@ nm_setting_ovs_port_class_init(NMSettingOvsPortClass *klass) NMSettingClass *setting_class = NM_SETTING_CLASS(klass); GArray *properties_override = _nm_sett_info_property_override_create_array(); - object_class->get_property = _nm_setting_property_get_property_direct; - object_class->set_property = _nm_setting_property_set_property_direct; + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; setting_class->verify = verify; @@ -297,7 +604,7 @@ nm_setting_ovs_port_class_init(NMSettingOvsPortClass *klass) * NMSettingOvsPort:vlan-mode: * * The VLAN mode. One of "access", "native-tagged", "native-untagged", - * "trunk" or unset. + * "trunk", "dot1q-tunnel" or unset. * * Since: 1.10 **/ @@ -327,6 +634,31 @@ nm_setting_ovs_port_class_init(NMSettingOvsPortClass *klass) NMSettingOvsPort, tag); + /** + * NMSettingOvsPort:trunks: (type GPtrArray(NMRange)) + * + * A list of VLAN ranges that this port trunks. + * + * The property is valid only for ports with mode "trunk", + * "native-tagged", or "native-untagged port". + * If it is empty, the port trunks all VLANs. + * + * Since: 1.42 + **/ + obj_properties[PROP_TRUNKS] = g_param_spec_boxed(NM_SETTING_OVS_PORT_TRUNKS, + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE + | G_PARAM_STATIC_STRINGS); + _nm_properties_override_gobj( + properties_override, + obj_properties[PROP_TRUNKS], + NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"), + .to_dbus_fcn = _nm_utils_ranges_to_dbus, + .compare_fcn = _nm_utils_ranges_cmp, + .from_dbus_fcn = _nm_utils_ranges_from_dbus)); + /** * NMSettingOvsPort:lacp: * diff --git a/src/libnm-core-impl/nm-setting-pppoe.c b/src/libnm-core-impl/nm-setting-pppoe.c index c4a9a89c..5c4f3c28 100644 --- a/src/libnm-core-impl/nm-setting-pppoe.c +++ b/src/libnm-core-impl/nm-setting-pppoe.c @@ -178,12 +178,12 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } static GPtrArray * -need_secrets(NMSetting *setting) +need_secrets(NMSetting *setting, gboolean check_rerequest) { NMSettingPppoePrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE(setting); GPtrArray *secrets = NULL; - if (priv->password) + if (!check_rerequest && priv->password) return NULL; if (!(priv->password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { diff --git a/src/libnm-core-impl/nm-setting-private.h b/src/libnm-core-impl/nm-setting-private.h index 38b233e9..26a31b1e 100644 --- a/src/libnm-core-impl/nm-setting-private.h +++ b/src/libnm-core-impl/nm-setting-private.h @@ -13,24 +13,69 @@ #include "nm-setting.h" #include "nm-setting-bridge.h" #include "nm-connection.h" +#include "nm-simple-connection.h" #include "nm-core-enum-types.h" #include "libnm-core-intern/nm-core-internal.h" /*****************************************************************************/ +struct _NMRefString; + typedef struct { NMConnection *self; NMSetting *settings[_NM_META_SETTING_TYPE_NUM]; /* D-Bus path of the connection, if any */ - char *path; + struct _NMRefString *path; } NMConnectionPrivate; extern GTypeClass *_nm_simple_connection_class_instance; extern int _nm_simple_connection_private_offset; +#undef NM_IS_SIMPLE_CONNECTION +#define NM_IS_SIMPLE_CONNECTION(self) \ + ({ \ + gconstpointer _self1 = (self); \ + gboolean _result; \ + \ + _result = \ + (_self1 \ + && (((GTypeInstance *) _self1)->g_class == _nm_simple_connection_class_instance)); \ + \ + nm_assert(_result == G_TYPE_CHECK_INSTANCE_TYPE(_self1, NM_TYPE_SIMPLE_CONNECTION)); \ + \ + _result; \ + }) + +#undef NM_IS_CONNECTION +#define NM_IS_CONNECTION(self) \ + ({ \ + gconstpointer _self0 = (self); \ + \ + (_self0 \ + && (NM_IS_SIMPLE_CONNECTION(_self0) \ + || G_TYPE_CHECK_INSTANCE_TYPE(_self0, NM_TYPE_CONNECTION))); \ + }) + +#define _NM_SIMPLE_CONNECTION_GET_CONNECTION_PRIVATE(connection) \ + ({ \ + gpointer _connection_1 = (connection); \ + NMConnectionPrivate *_priv_1; \ + \ + nm_assert(NM_IS_SIMPLE_CONNECTION(_connection_1)); \ + \ + _priv_1 = (void *) (&(((char *) _connection_1)[_nm_simple_connection_private_offset])); \ + \ + nm_assert(_priv_1 \ + == G_TYPE_INSTANCE_GET_PRIVATE(_connection_1, \ + NM_TYPE_SIMPLE_CONNECTION, \ + NMConnectionPrivate)); \ + \ + _priv_1; \ + }) + void _nm_connection_private_clear(NMConnectionPrivate *priv); /*****************************************************************************/ @@ -55,7 +100,7 @@ struct _NMSettingClass { gboolean (*verify_secrets)(NMSetting *setting, NMConnection *connection, GError **error); - GPtrArray *(*need_secrets)(NMSetting *setting); + GPtrArray *(*need_secrets)(NMSetting *setting, gboolean check_rerequest); int (*update_one_secret)(NMSetting *setting, const char *key, GVariant *value, GError **error); @@ -120,11 +165,23 @@ struct _NMSettingIPConfigClass { NMSettingClass parent; /* In the past, this struct was public API. Preserve ABI! */ + union { - gpointer _dummy; + gpointer _dummy1; int private_offset; }; - gpointer padding[7]; + + union { + gpointer _dummy2; + gint8 addr_family; + }; + + union { + gpointer _dummy3; + bool is_ipv4; + }; + + gpointer padding[5]; }; typedef struct { @@ -140,6 +197,7 @@ typedef struct { char *dhcp_hostname; char *dhcp_iaid; gint64 route_metric; + int auto_route_ext_gw; gint32 required_timeout; gint32 dad_timeout; gint32 dhcp_timeout; @@ -155,6 +213,11 @@ typedef struct { void _nm_setting_ip_config_private_init(gpointer self, NMSettingIPConfigPrivate *priv); +#define NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting) \ + (NM_SETTING_IP_CONFIG_GET_CLASS(setting)->addr_family) + +#define NM_SETTING_IP_CONFIG_IS_IPv4(setting) (NM_SETTING_IP_CONFIG_GET_CLASS(setting)->is_ipv4) + /*****************************************************************************/ NMSettingPriority _nm_setting_get_base_type_priority(NMSetting *setting); @@ -240,6 +303,14 @@ typedef struct { /*****************************************************************************/ +struct _NMRange { + int refcount; + guint64 start; + guint64 end; +}; + +/*****************************************************************************/ + #define NM_SETTING_PARAM_NONE 0 /* The property of the #NMSetting should be considered during comparisons that @@ -256,8 +327,9 @@ typedef struct { */ #define NM_SETTING_PARAM_INFERRABLE (1 << (4 + G_PARAM_USER_SHIFT)) -/* This is a legacy property, which clients should not send to the daemon. */ -#define NM_SETTING_PARAM_LEGACY (1 << (5 + G_PARAM_USER_SHIFT)) +/* This flag has no meaning (anymore). It's only kept, because we used it + * on some older versions of libnm. */ +#define NM_SETTING_PARAM_UNUSED1 (1 << (5 + G_PARAM_USER_SHIFT)) /* When a connection is active and gets modified, usually the change * to the settings-connection does not propagate automatically to the @@ -442,10 +514,14 @@ _nm_properties_override(GArray *properties_override, const NMSettInfoProperty *p .property_type = (p_property_type), \ __VA_ARGS__)) -#define _nm_properties_override_dbus(properties_override, p_name, p_property_type) \ - _nm_properties_override( \ - (properties_override), \ - NM_SETT_INFO_PROPERTY(.name = ("" p_name ""), .property_type = (p_property_type), )) +#define _nm_properties_override_dbus(properties_override, \ + p_name, \ + p_property_type, \ + ... /* extra NMSettInfoProperty fields */) \ + _nm_properties_override((properties_override), \ + NM_SETT_INFO_PROPERTY(.name = ("" p_name ""), \ + .property_type = (p_property_type), \ + __VA_ARGS__)) /*****************************************************************************/ @@ -984,7 +1060,7 @@ gboolean _nm_setting_use_legacy_property(NMSetting *setting, const char *legacy_property, const char *new_property); -GPtrArray *_nm_setting_need_secrets(NMSetting *setting); +GPtrArray *_nm_setting_need_secrets(NMSetting *setting, gboolean check_rerequest); gboolean _nm_setting_should_compare_secret_property(NMSetting *setting, NMSetting *other, @@ -998,15 +1074,26 @@ gboolean _nm_utils_bridge_vlans_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS GVariant *_nm_utils_bridge_vlans_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil); +gboolean _nm_utils_ranges_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil); + +NMTernary _nm_utils_ranges_cmp(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil); + +GVariant *_nm_utils_ranges_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil); + NMTernary _nm_setting_ip_config_compare_fcn_addresses(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil); NMTernary _nm_setting_ip_config_compare_fcn_routes(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil); -gboolean _nm_utils_hwaddr_cloned_not_set(_NM_SETT_INFO_PROP_MISSING_FROM_DBUS_FCN_ARGS _nm_nil); +NMTernary _nm_setting_ip_config_compare_fcn_dns(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil); -GVariant *_nm_utils_hwaddr_cloned_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil); +gboolean _nm_sett_info_prop_missing_from_dbus_fcn_cloned_mac_address( + _NM_SETT_INFO_PROP_MISSING_FROM_DBUS_FCN_ARGS _nm_nil); + +GVariant * +_nm_sett_info_prop_to_dbus_fcn_cloned_mac_address(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil); -gboolean _nm_utils_hwaddr_cloned_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil); +gboolean +_nm_sett_info_prop_from_dbus_fcn_cloned_mac_address(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil); /*****************************************************************************/ diff --git a/src/libnm-core-impl/nm-setting-serial.c b/src/libnm-core-impl/nm-setting-serial.c index a8201876..6fe3e487 100644 --- a/src/libnm-core-impl/nm-setting-serial.c +++ b/src/libnm-core-impl/nm-setting-serial.c @@ -278,7 +278,8 @@ nm_setting_serial_class_init(NMSettingSerialClass *klass) * 111 ('o') for odd, or 110 ('n') for none. * example: parity=n * ---end--- - * ---dbus--- + */ + /* ---dbus--- * property: parity * format: byte * description: The connection parity: 69 (ASCII 'E') for even parity, diff --git a/src/libnm-core-impl/nm-setting-sriov.c b/src/libnm-core-impl/nm-setting-sriov.c index f9bee21e..091cc0c6 100644 --- a/src/libnm-core-impl/nm-setting-sriov.c +++ b/src/libnm-core-impl/nm-setting-sriov.c @@ -372,7 +372,7 @@ const NMVariantAttributeSpec *const _nm_sriov_vf_attribute_spec[] = { * * Returns: %TRUE if the attribute is valid, %FALSE otherwise * - * Since: 1.42, 1.40.4 + * Since: 1.42 */ gboolean nm_sriov_vf_attribute_validate(const char *name, GVariant *value, gboolean *known, GError **error) diff --git a/src/libnm-core-impl/nm-setting-tc-config.c b/src/libnm-core-impl/nm-setting-tc-config.c index 529bbca1..80135201 100644 --- a/src/libnm-core-impl/nm-setting-tc-config.c +++ b/src/libnm-core-impl/nm-setting-tc-config.c @@ -935,7 +935,7 @@ nm_tc_tfilter_get_parent(NMTCTfilter *tfilter) * * Returns: the action associated with a traffic filter. * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ NMTCAction * nm_tc_tfilter_get_action(NMTCTfilter *tfilter) @@ -956,7 +956,7 @@ nm_tc_tfilter_get_action(NMTCTfilter *tfilter) * * Sets the action associated with a traffic filter. * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ void nm_tc_tfilter_set_action(NMTCTfilter *tfilter, NMTCAction *action) @@ -1475,7 +1475,7 @@ next: } static GVariant * -tc_qdiscs_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +qdiscs_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { gs_unref_ptrarray GPtrArray *qdiscs = NULL; @@ -1484,7 +1484,7 @@ tc_qdiscs_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) } static gboolean -tc_qdiscs_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +qdiscs_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { gs_unref_ptrarray GPtrArray *qdiscs = NULL; @@ -1661,7 +1661,7 @@ next: } static GVariant * -tc_tfilters_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +tfilters_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { gs_unref_ptrarray GPtrArray *tfilters = NULL; @@ -1670,7 +1670,7 @@ tc_tfilters_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) } static gboolean -tc_tfilters_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +tfilters_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { gs_unref_ptrarray GPtrArray *tfilters = NULL; @@ -1944,8 +1944,8 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass) * * can be used to set a different hash table size, available * from kernel 2.6.39 onwards. The specified divisor must be - * a power of two and cannot be larger than 65536. Default - * value: 1024. + * a power of two and cannot be larger than 65536. + * Default value: 1024. * * * @@ -2083,12 +2083,13 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass) G_TYPE_PTR_ARRAY, G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS); - _nm_properties_override_gobj(properties_override, - obj_properties[PROP_QDISCS], - NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"), - .to_dbus_fcn = tc_qdiscs_get, - .compare_fcn = compare_fcn_qdiscs, - .from_dbus_fcn = tc_qdiscs_set, )); + _nm_properties_override_gobj( + properties_override, + obj_properties[PROP_QDISCS], + NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"), + .to_dbus_fcn = qdiscs_to_dbus, + .compare_fcn = compare_fcn_qdiscs, + .from_dbus_fcn = qdiscs_from_dbus, )); /** * NMSettingTCConfig:tfilters: (type GPtrArray(NMTCTfilter)) @@ -2107,8 +2108,8 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass) * format: GPtrArray(NMTCTfilter) * description-docbook: * - * Array of TC traffic filters. Traffic control can manage the packet content during - * classification by using filters. + * Array of TC traffic filters. Traffic control can manage the packet content during + * classification by using filters. * * * Each tfilters can be specified by the following attributes: @@ -2206,7 +2207,7 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass) * ---end--- **/ /* ---ifcfg-rh--- - * property: qdiscs + * property: tfilters * variable: FILTER1(+), FILTER2(+), ..., TC_COMMIT(+) * description: Traffic filters to set on the interface. When no * QDISC1, QDISC2, ..., FILTER1, FILTER2, ... keys are present, @@ -2225,9 +2226,9 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass) properties_override, obj_properties[PROP_TFILTERS], NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"), - .to_dbus_fcn = tc_tfilters_get, + .to_dbus_fcn = tfilters_to_dbus, .compare_fcn = compare_fcn_tfilter, - .from_dbus_fcn = tc_tfilters_set, )); + .from_dbus_fcn = tfilters_from_dbus, )); g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); diff --git a/src/libnm-core-impl/nm-setting-team.c b/src/libnm-core-impl/nm-setting-team.c index 3eeeb2c3..e6e375e7 100644 --- a/src/libnm-core-impl/nm-setting-team.c +++ b/src/libnm-core-impl/nm-setting-team.c @@ -375,6 +375,8 @@ _nm_team_link_watcher_ref(NMTeamLinkWatcher *watcher) * * Increases the reference count of the object. * + * Since 1.20, ref-counting of #NMTeamLinkWatcher is thread-safe. + * * Since: 1.12 **/ void @@ -390,6 +392,8 @@ nm_team_link_watcher_ref(NMTeamLinkWatcher *watcher) * Decreases the reference count of the object. If the reference count * reaches zero, the object will be destroyed. * + * Since 1.20, ref-counting of #NMTeamLinkWatcher is thread-safe. + * * Since: 1.12 **/ void @@ -1829,7 +1833,8 @@ nm_setting_team_class_init(NMSettingTeamClass *klass) */ _nm_properties_override_dbus(properties_override, "interface-name", - &nm_sett_info_propert_type_deprecated_interface_name); + &nm_sett_info_propert_type_deprecated_interface_name, + .dbus_deprecated = TRUE); g_object_class_install_properties(object_class, G_N_ELEMENTS(obj_properties), obj_properties); diff --git a/src/libnm-core-impl/nm-setting-vlan.c b/src/libnm-core-impl/nm-setting-vlan.c index 5f50acdd..696799d8 100644 --- a/src/libnm-core-impl/nm-setting-vlan.c +++ b/src/libnm-core-impl/nm-setting-vlan.c @@ -30,6 +30,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingVlan, PROP_PARENT, PROP_ID, PROP_FLAGS, + PROP_PROTOCOL, PROP_INGRESS_PRIORITY_MAP, PROP_EGRESS_PRIORITY_MAP, ); @@ -37,6 +38,7 @@ typedef struct { GSList *ingress_priority_map; GSList *egress_priority_map; char *parent; + char *protocol; guint32 id; guint32 flags; } NMSettingVlanPrivate; @@ -81,6 +83,22 @@ nm_setting_vlan_get_parent(NMSettingVlan *setting) return NM_SETTING_VLAN_GET_PRIVATE(setting)->parent; } +/** + * nm_setting_vlan_get_protocol: + * @setting: the #NMSettingVlan + * + * Since: 1.42 + * + * Returns: the #NMSettingVlan:protocol property of the setting + **/ +const char * +nm_setting_vlan_get_protocol(NMSettingVlan *setting) +{ + g_return_val_if_fail(NM_IS_SETTING_VLAN(setting), NULL); + + return NM_SETTING_VLAN_GET_PRIVATE(setting)->protocol; +} + /** * nm_setting_vlan_get_id: * @setting: the #NMSettingVlan @@ -662,6 +680,16 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } + if (!NM_IN_STRSET(priv->protocol, NULL, "802.1Q", "802.1ad")) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("invalid VLAN protocol %s: must be '802.1Q' or '802.1ad'"), + priv->protocol); + g_prefix_error(error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_PROTOCOL); + return FALSE; + } + if (connection && !s_wired) { /* technically, a VLAN setting does not require an ethernet setting. However, * the ifcfg-rh reader always adds a ethernet setting when reading a vlan setting. @@ -678,13 +706,13 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } static GVariant * -_override_flags_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +vlan_flags_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { return g_variant_new_uint32(nm_setting_vlan_get_flags((NMSettingVlan *) setting)); } static gboolean -_override_flags_not_set(_NM_SETT_INFO_PROP_MISSING_FROM_DBUS_FCN_ARGS _nm_nil) +vlan_flags_missing_from_dbus(_NM_SETT_INFO_PROP_MISSING_FROM_DBUS_FCN_ARGS _nm_nil) { /* we changed the default value for FLAGS. When an older client * doesn't serialize the property, we assume it is the old default. */ @@ -909,12 +937,38 @@ nm_setting_vlan_class_init(NMSettingVlanClass *klass) properties_override, obj_properties[PROP_FLAGS], NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_UINT32, - .to_dbus_fcn = _override_flags_get, + .to_dbus_fcn = vlan_flags_to_dbus, .compare_fcn = _nm_setting_property_compare_fcn_default, - .missing_from_dbus_fcn = _override_flags_not_set, + .missing_from_dbus_fcn = vlan_flags_missing_from_dbus, .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop, .from_dbus_is_full = TRUE)); + /** + * NMSettingVlan:protocol: + * + * Specifies the VLAN protocol to use for encapsulation. + * + * Supported values are: '802.1Q', '802.1ad'. If not specified the default + * value is '802.1Q'. + * + * Since: 1.42 + **/ + /* ---ifcfg-rh--- + * property: protocol + * variable: VLAN_PROTOCOL + * description: VLAN protocol. + * example: VLAN_PROTOCOL="802.1ad" + * ---end--- + */ + _nm_setting_property_define_direct_string(properties_override, + obj_properties, + NM_SETTING_VLAN_PROTOCOL, + PROP_PROTOCOL, + NM_SETTING_PARAM_INFERRABLE, + NMSettingVlanPrivate, + protocol, + .direct_string_is_refstr = TRUE); + /** * NMSettingVlan:ingress-priority-map: * @@ -965,7 +1019,8 @@ nm_setting_vlan_class_init(NMSettingVlanClass *klass) * but VLAN id from DEVICE takes precedence over VLAN_ID. * example: PHYSDEV=eth0, VLAN_ID=12; or DEVICE=eth0.12 * ---end--- - * ---dbus--- + */ + /* ---dbus--- * property: interface-name * format: string * description: Deprecated in favor of connection.interface-name, but can @@ -975,7 +1030,8 @@ nm_setting_vlan_class_init(NMSettingVlanClass *klass) */ _nm_properties_override_dbus(properties_override, "interface-name", - &nm_sett_info_propert_type_deprecated_interface_name); + &nm_sett_info_propert_type_deprecated_interface_name, + .dbus_deprecated = TRUE, ); g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); diff --git a/src/libnm-core-impl/nm-setting-vpn.c b/src/libnm-core-impl/nm-setting-vpn.c index 7e6f18dd..4ff98908 100644 --- a/src/libnm-core-impl/nm-setting-vpn.c +++ b/src/libnm-core-impl/nm-setting-vpn.c @@ -148,7 +148,7 @@ nm_setting_vpn_get_user_name(NMSettingVpn *setting) * * Returns: the #NMSettingVpn:persistent property of the setting * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ gboolean nm_setting_vpn_get_persistent(NMSettingVpn *setting) @@ -803,7 +803,7 @@ set_secret_flags(NMSetting *setting, } static GPtrArray * -need_secrets(NMSetting *setting) +need_secrets(NMSetting *setting, gboolean check_rerequest) { /* Assume that VPN connections need secrets since they almost always will */ return g_ptr_array_sized_new(1); @@ -1132,7 +1132,7 @@ nm_setting_vpn_class_init(NMSettingVpnClass *klass) persistent); /** - * NMSettingVpn:data: (type GHashTable(utf8,utf8)): + * NMSettingVpn:data: (type GHashTable(utf8,utf8)) * * Dictionary of key/value pairs of VPN plugin specific data. Both keys and * values must be strings. @@ -1155,7 +1155,7 @@ nm_setting_vpn_class_init(NMSettingVpnClass *klass) &nm_sett_info_propert_type_strdict); /** - * NMSettingVpn:secrets: (type GHashTable(utf8,utf8)): + * NMSettingVpn:secrets: (type GHashTable(utf8,utf8)) * * Dictionary of key/value pairs of VPN plugin specific secrets like * passwords or private keys. Both keys and values must be strings. diff --git a/src/libnm-core-impl/nm-setting-vxlan.c b/src/libnm-core-impl/nm-setting-vxlan.c index f617a073..3fb4c48d 100644 --- a/src/libnm-core-impl/nm-setting-vxlan.c +++ b/src/libnm-core-impl/nm-setting-vxlan.c @@ -344,9 +344,9 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) gboolean remote_is_valid = TRUE; gboolean local_is_valid = TRUE; - if (priv->remote && !nm_utils_parse_inaddr_bin(addr_family, priv->remote, &addr_family, NULL)) + if (priv->remote && !nm_inet_parse_bin(addr_family, priv->remote, &addr_family, NULL)) remote_is_valid = FALSE; - if (priv->local && !nm_utils_parse_inaddr_bin(addr_family, priv->local, &addr_family, NULL)) + if (priv->local && !nm_inet_parse_bin(addr_family, priv->local, &addr_family, NULL)) local_is_valid = FALSE; if (!remote_is_valid) { diff --git a/src/libnm-core-impl/nm-setting-wimax.c b/src/libnm-core-impl/nm-setting-wimax.c index 1e2b51c3..c33ad3ea 100644 --- a/src/libnm-core-impl/nm-setting-wimax.c +++ b/src/libnm-core-impl/nm-setting-wimax.c @@ -178,7 +178,8 @@ nm_setting_wimax_class_init(NMSettingWimaxClass *klass) PROP_NETWORK_NAME, NM_SETTING_PARAM_NONE, NMSettingWimaxPrivate, - network_name); + network_name, + .is_deprecated = TRUE, ); /** * NMSettingWimax:mac-address: @@ -196,7 +197,8 @@ nm_setting_wimax_class_init(NMSettingWimaxClass *klass) NM_SETTING_PARAM_NONE, NMSettingWimaxPrivate, mac_address, - .direct_set_string_mac_address_len = ETH_ALEN); + .direct_set_string_mac_address_len = ETH_ALEN, + .is_deprecated = TRUE, ); g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); diff --git a/src/libnm-core-impl/nm-setting-wired.c b/src/libnm-core-impl/nm-setting-wired.c index 830b2c73..3acd5d97 100644 --- a/src/libnm-core-impl/nm-setting-wired.c +++ b/src/libnm-core-impl/nm-setting-wired.c @@ -292,7 +292,7 @@ nm_setting_wired_get_mac_address_blacklist(NMSettingWired *setting) g_return_val_if_fail(NM_IS_SETTING_WIRED(setting), NULL); priv = NM_SETTING_WIRED_GET_PRIVATE(setting); - return (const char *const *) priv->mac_address_blacklist->data; + return nm_g_array_data(priv->mac_address_blacklist); } /** @@ -327,7 +327,7 @@ nm_setting_wired_get_mac_blacklist_item(NMSettingWired *setting, guint32 idx) priv = NM_SETTING_WIRED_GET_PRIVATE(setting); g_return_val_if_fail(idx <= priv->mac_address_blacklist->len, NULL); - return g_array_index(priv->mac_address_blacklist, const char *, idx); + return nm_g_array_index(priv->mac_address_blacklist, const char *, idx); } /** @@ -355,7 +355,7 @@ nm_setting_wired_add_mac_blacklist_item(NMSettingWired *setting, const char *mac priv = NM_SETTING_WIRED_GET_PRIVATE(setting); for (i = 0; i < priv->mac_address_blacklist->len; i++) { - candidate = g_array_index(priv->mac_address_blacklist, char *, i); + candidate = nm_g_array_index(priv->mac_address_blacklist, char *, i); if (nm_utils_hwaddr_matches(mac, -1, candidate, -1)) return FALSE; } @@ -409,7 +409,7 @@ nm_setting_wired_remove_mac_blacklist_item_by_value(NMSettingWired *setting, con priv = NM_SETTING_WIRED_GET_PRIVATE(setting); for (i = 0; i < priv->mac_address_blacklist->len; i++) { - candidate = g_array_index(priv->mac_address_blacklist, char *, i); + candidate = nm_g_array_index(priv->mac_address_blacklist, char *, i); if (!nm_utils_hwaddr_matches(mac, -1, candidate, -1)) { g_array_remove_index(priv->mac_address_blacklist, i); _notify(setting, PROP_MAC_ADDRESS_BLACKLIST); @@ -810,7 +810,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } for (i = 0; i < priv->mac_address_blacklist->len; i++) { - const char *mac = g_array_index(priv->mac_address_blacklist, const char *, i); + const char *mac = nm_g_array_index(priv->mac_address_blacklist, const char *, i); if (!nm_utils_hwaddr_valid(mac, ETH_ALEN)) { g_set_error(error, @@ -1007,7 +1007,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) g_value_set_string(value, nm_setting_wired_get_cloned_mac_address(setting)); break; case PROP_MAC_ADDRESS_BLACKLIST: - g_value_set_boxed(value, (char **) priv->mac_address_blacklist->data); + g_value_set_boxed(value, nm_g_array_data(priv->mac_address_blacklist)); break; case PROP_S390_SUBCHANNELS: g_value_set_boxed(value, priv->s390_subchannels); @@ -1311,7 +1311,8 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass) * (e.g. 00:22:68:12:79:A2), or semicolon separated list of 6 bytes (obsolete) * (e.g. 0;34;104;18;121;162) * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: mac-address * variable: HWADDR * description: Hardware address of the device in traditional hex-digits-and-colons @@ -1359,13 +1360,15 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass) * (e.g. 00:22:68:12:79:B2), or semicolon separated list of 6 bytes (obsolete) * (e.g. 0;34;104;18;121;178). * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: cloned-mac-address * variable: MACADDR * description: Cloned (spoofed) MAC address in traditional hex-digits-and-colons * notation (e.g. 00:22:68:14:5A:99). * ---end--- - * ---dbus--- + */ + /* ---dbus--- * property: cloned-mac-address * format: byte array * description: This D-Bus field is deprecated in favor of "assigned-mac-address" @@ -1382,11 +1385,13 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass) _nm_properties_override_gobj( properties_override, obj_properties[PROP_CLONED_MAC_ADDRESS], - NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_BYTESTRING, - .compare_fcn = compare_fcn_cloned_mac_address, - .to_dbus_fcn = _nm_utils_hwaddr_cloned_get, - .from_dbus_fcn = _nm_utils_hwaddr_cloned_set, - .missing_from_dbus_fcn = _nm_utils_hwaddr_cloned_not_set, )); + NM_SETT_INFO_PROPERT_TYPE_DBUS( + G_VARIANT_TYPE_BYTESTRING, + .compare_fcn = compare_fcn_cloned_mac_address, + .to_dbus_fcn = _nm_sett_info_prop_to_dbus_fcn_cloned_mac_address, + .from_dbus_fcn = _nm_sett_info_prop_from_dbus_fcn_cloned_mac_address, + .missing_from_dbus_fcn = _nm_sett_info_prop_missing_from_dbus_fcn_cloned_mac_address, ), + .dbus_deprecated = TRUE); /* ---dbus--- * property: assigned-mac-address @@ -1465,7 +1470,8 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass) * description: MAC address blacklist. * example: mac-address-blacklist= 00:22:68:12:79:A6;00:22:68:12:79:78 * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: mac-address-blacklist * variable: HWADDR_BLACKLIST(+) * description: It denies usage of the connection for any device whose address @@ -1551,7 +1557,7 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass) s390_nettype); /** - * NMSettingWired:s390-options: (type GHashTable(utf8,utf8)): + * NMSettingWired:s390-options: (type GHashTable(utf8,utf8)) * * Dictionary of key/value pairs of s390-specific device options. Both keys * and values must be strings. Allowed keys include "portno", "layer2", diff --git a/src/libnm-core-impl/nm-setting-wireguard.c b/src/libnm-core-impl/nm-setting-wireguard.c index 599ded3c..a981187f 100644 --- a/src/libnm-core-impl/nm-setting-wireguard.c +++ b/src/libnm-core-impl/nm-setting-wireguard.c @@ -50,7 +50,7 @@ struct _NMWireGuardPeer { char *public_key; char *preshared_key; GPtrArray *allowed_ips; - guint refcount; + int refcount; NMSettingSecretFlags preshared_key_flags; guint16 persistent_keepalive; bool public_key_valid : 1; @@ -127,11 +127,11 @@ nm_wireguard_peer_new_clone(const NMWireGuardPeer *self, gboolean with_secrets) * nm_wireguard_peer_ref: * @self: (allow-none): the #NMWireGuardPeer instance * - * This is not thread-safe. - * * Returns: returns the input argument @self after incrementing * the reference count. * + * Since 1.42, ref-counting of #NMWireGuardPeer is thread-safe. + * * Since: 1.16 */ NMWireGuardPeer * @@ -142,9 +142,9 @@ nm_wireguard_peer_ref(NMWireGuardPeer *self) g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), NULL); - nm_assert(self->refcount < G_MAXUINT); + nm_assert(self->refcount < G_MAXINT); - self->refcount++; + g_atomic_int_inc(&self->refcount); return self; } @@ -155,7 +155,7 @@ nm_wireguard_peer_ref(NMWireGuardPeer *self) * Drop a reference to @self. If the last reference is dropped, * the instance is freed and all associate data released. * - * This is not thread-safe. + * Since 1.42, ref-counting of #NMWireGuardPeer is thread-safe. * * Since: 1.16 */ @@ -167,7 +167,7 @@ nm_wireguard_peer_unref(NMWireGuardPeer *self) g_return_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE)); - if (--self->refcount > 0) + if (!g_atomic_int_dec_and_test(&self->refcount)) return; nm_sock_addr_endpoint_unref(self->endpoint); @@ -628,18 +628,18 @@ _peer_append_allowed_ip(NMWireGuardPeer *self, const char *allowed_ip, gboolean /* normalize the address (if it is valid. Otherwise, take it * as-is (it will render the instance invalid). */ - if (!nm_utils_parse_inaddr_prefix_bin(AF_UNSPEC, allowed_ip, &addr_family, &addrbin, &prefix)) { + if (!nm_inet_parse_with_prefix_bin(AF_UNSPEC, allowed_ip, &addr_family, &addrbin, &prefix)) { if (!accept_invalid) return FALSE; /* mark the entry as invalid by having a "X" prefix. */ str = g_strconcat(ALLOWED_IP_INVALID_X_STR, allowed_ip, NULL); is_valid = FALSE; } else { - char addrstr[NM_UTILS_INET_ADDRSTRLEN]; + char addrstr[NM_INET_ADDRSTRLEN]; nm_assert_addr_family(addr_family); - nm_utils_inet_ntop(addr_family, &addrbin, addrstr); + nm_inet_ntop(addr_family, &addrbin, addrstr); if (prefix >= 0) str = g_strdup_printf("%s/%d", addrstr, prefix); else @@ -1460,7 +1460,7 @@ nm_setting_wireguard_clear_peers(NMSettingWireGuard *self) /*****************************************************************************/ static GVariant * -_peers_dbus_only_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +peers_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { NMSettingWireGuard *self = NM_SETTING_WIREGUARD(setting); NMSettingWireGuardPrivate *priv; @@ -1558,7 +1558,7 @@ _peers_dbus_only_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) } static gboolean -_peers_dbus_only_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +peers_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { GVariantIter iter_peers; GVariant *peer_var; @@ -1800,13 +1800,13 @@ verify_secrets(NMSetting *setting, NMConnection *connection, GError **error) } static GPtrArray * -need_secrets(NMSetting *setting) +need_secrets(NMSetting *setting, gboolean check_rerequest) { NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE(setting); GPtrArray *secrets = NULL; guint i; - if (!priv->private_key_valid) { + if (check_rerequest || !priv->private_key_valid) { secrets = g_ptr_array_new_full(1, g_free); g_ptr_array_add(secrets, g_strdup(NM_SETTING_WIREGUARD_PRIVATE_KEY)); } @@ -1857,9 +1857,7 @@ clear_secrets(const NMSettInfoSetting *sett_info, if (j++ < 5) { /* we use alloca() inside a loop here, but it is guarded to happen at most * a few times. */ - name = peers_psk_get_secret_name_a(/* lgtm [cpp/alloca-in-loop] */ - peer->public_key, - &name_free); + name = peers_psk_get_secret_name_a(peer->public_key, &name_free); } else { name_free = peers_psk_get_secret_name_dup(peer->public_key); name = name_free; @@ -2528,9 +2526,9 @@ nm_setting_wireguard_class_init(NMSettingWireGuardClass *klass) properties_override, NM_SETTING_WIREGUARD_PEERS, NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"), - .to_dbus_fcn = _peers_dbus_only_synth, + .to_dbus_fcn = peers_to_dbus, .compare_fcn = compare_fcn_peers, - .from_dbus_fcn = _peers_dbus_only_set, )); + .from_dbus_fcn = peers_from_dbus, )); g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); diff --git a/src/libnm-core-impl/nm-setting-wireless-security.c b/src/libnm-core-impl/nm-setting-wireless-security.c index ebefd504..a7d12df4 100644 --- a/src/libnm-core-impl/nm-setting-wireless-security.c +++ b/src/libnm-core-impl/nm-setting-wireless-security.c @@ -815,7 +815,7 @@ nm_setting_wireless_security_get_fils(NMSettingWirelessSecurity *setting) } static GPtrArray * -need_secrets(NMSetting *setting) +need_secrets(NMSetting *setting, gboolean check_rerequest) { NMSettingWirelessSecurity *self = NM_SETTING_WIRELESS_SECURITY(setting); NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE(self); @@ -828,22 +828,22 @@ need_secrets(NMSetting *setting) /* Static WEP */ if (strcmp(priv->key_mgmt, "none") == 0) { if ((priv->wep_tx_keyidx == 0) - && !nm_utils_wep_key_valid(priv->wep_key0, priv->wep_key_type)) { + && (check_rerequest || !nm_utils_wep_key_valid(priv->wep_key0, priv->wep_key_type))) { g_ptr_array_add(secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0); return secrets; } if ((priv->wep_tx_keyidx == 1) - && !nm_utils_wep_key_valid(priv->wep_key1, priv->wep_key_type)) { + && (check_rerequest || !nm_utils_wep_key_valid(priv->wep_key1, priv->wep_key_type))) { g_ptr_array_add(secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1); return secrets; } if ((priv->wep_tx_keyidx == 2) - && !nm_utils_wep_key_valid(priv->wep_key2, priv->wep_key_type)) { + && (check_rerequest || !nm_utils_wep_key_valid(priv->wep_key2, priv->wep_key_type))) { g_ptr_array_add(secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2); return secrets; } if ((priv->wep_tx_keyidx == 3) - && !nm_utils_wep_key_valid(priv->wep_key3, priv->wep_key_type)) { + && (check_rerequest || !nm_utils_wep_key_valid(priv->wep_key3, priv->wep_key_type))) { g_ptr_array_add(secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3); return secrets; } @@ -852,7 +852,7 @@ need_secrets(NMSetting *setting) /* WPA-PSK infrastructure */ if (strcmp(priv->key_mgmt, "wpa-psk") == 0) { - if (!nm_utils_wpa_psk_valid(priv->psk)) { + if (check_rerequest || !nm_utils_wpa_psk_valid(priv->psk)) { g_ptr_array_add(secrets, NM_SETTING_WIRELESS_SECURITY_PSK); return secrets; } @@ -861,7 +861,7 @@ need_secrets(NMSetting *setting) /* SAE, used in MESH and WPA3-Personal */ if (strcmp(priv->key_mgmt, "sae") == 0) { - if (!priv->psk || !*priv->psk) { + if (check_rerequest || !priv->psk || !*priv->psk) { g_ptr_array_add(secrets, NM_SETTING_WIRELESS_SECURITY_PSK); return secrets; } @@ -870,7 +870,7 @@ need_secrets(NMSetting *setting) /* LEAP */ if (priv->auth_alg && !strcmp(priv->auth_alg, "leap") && !strcmp(priv->key_mgmt, "ieee8021x")) { - if (!priv->leap_password || !*priv->leap_password) { + if (check_rerequest || !priv->leap_password || !*priv->leap_password) { g_ptr_array_add(secrets, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD); return secrets; } diff --git a/src/libnm-core-impl/nm-setting-wireless.c b/src/libnm-core-impl/nm-setting-wireless.c index cf391598..33e16d4b 100644 --- a/src/libnm-core-impl/nm-setting-wireless.c +++ b/src/libnm-core-impl/nm-setting-wireless.c @@ -473,7 +473,7 @@ nm_setting_wireless_get_mac_address_blacklist(NMSettingWireless *setting) g_return_val_if_fail(NM_IS_SETTING_WIRELESS(setting), NULL); priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting); - return (const char *const *) priv->mac_address_blacklist->data; + return nm_g_array_data(priv->mac_address_blacklist); } /** @@ -508,7 +508,7 @@ nm_setting_wireless_get_mac_blacklist_item(NMSettingWireless *setting, guint32 i priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting); g_return_val_if_fail(idx <= priv->mac_address_blacklist->len, NULL); - return g_array_index(priv->mac_address_blacklist, const char *, idx); + return nm_g_array_index(priv->mac_address_blacklist, const char *, idx); } /** @@ -536,7 +536,7 @@ nm_setting_wireless_add_mac_blacklist_item(NMSettingWireless *setting, const cha priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting); for (i = 0; i < priv->mac_address_blacklist->len; i++) { - candidate = g_array_index(priv->mac_address_blacklist, char *, i); + candidate = nm_g_array_index(priv->mac_address_blacklist, char *, i); if (nm_utils_hwaddr_matches(mac, -1, candidate, -1)) return FALSE; } @@ -590,7 +590,7 @@ nm_setting_wireless_remove_mac_blacklist_item_by_value(NMSettingWireless *settin priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting); for (i = 0; i < priv->mac_address_blacklist->len; i++) { - candidate = g_array_index(priv->mac_address_blacklist, char *, i); + candidate = nm_g_array_index(priv->mac_address_blacklist, char *, i); if (!nm_utils_hwaddr_matches(mac, -1, candidate, -1)) { g_array_remove_index(priv->mac_address_blacklist, i); _notify(setting, PROP_MAC_ADDRESS_BLACKLIST); @@ -988,7 +988,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } for (i = 0; i < priv->mac_address_blacklist->len; i++) { - const char *mac = g_array_index(priv->mac_address_blacklist, const char *, i); + const char *mac = nm_g_array_index(priv->mac_address_blacklist, const char *, i); if (!nm_utils_hwaddr_valid(mac, ETH_ALEN)) { g_set_error(error, @@ -1123,7 +1123,7 @@ compare_fcn_seen_bssids(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil) /*****************************************************************************/ static GVariant * -nm_setting_wireless_get_security(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +security_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { if (!_nm_connection_serialize_non_secret(flags)) return NULL; @@ -1174,7 +1174,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) g_value_set_string(value, nm_setting_wireless_get_cloned_mac_address(setting)); break; case PROP_MAC_ADDRESS_BLACKLIST: - g_value_set_boxed(value, (char **) priv->mac_address_blacklist->data); + g_value_set_boxed(value, nm_g_array_data(priv->mac_address_blacklist)); break; case PROP_SEEN_BSSIDS: g_value_take_boxed( @@ -1311,7 +1311,8 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass) * description: SSID of Wi-Fi network. * example: ssid=Quick Net * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: ssid * variable: ESSID * description: SSID of Wi-Fi network. @@ -1493,7 +1494,8 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass) * (e.g. 00:22:68:12:79:A2), or semicolon separated list of 6 bytes (obsolete) * (e.g. 0;34;104;18;121;162). * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: mac-address * variable: HWADDR * description: Hardware address of the device in traditional hex-digits-and-colons @@ -1540,13 +1542,15 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass) * (e.g. 00:22:68:12:79:B2), or semicolon separated list of 6 bytes (obsolete) * (e.g. 0;34;104;18;121;178). * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: cloned-mac-address * variable: MACADDR * description: Cloned (spoofed) MAC address in traditional hex-digits-and-colons * notation (e.g. 00:22:68:14:5A:99). * ---end--- - * ---dbus--- + */ + /* ---dbus--- * property: cloned-mac-address * format: byte array * description: This D-Bus field is deprecated in favor of "assigned-mac-address" @@ -1563,11 +1567,13 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass) _nm_properties_override_gobj( properties_override, obj_properties[PROP_CLONED_MAC_ADDRESS], - NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_BYTESTRING, - .compare_fcn = compare_fcn_cloned_mac_address, - .to_dbus_fcn = _nm_utils_hwaddr_cloned_get, - .from_dbus_fcn = _nm_utils_hwaddr_cloned_set, - .missing_from_dbus_fcn = _nm_utils_hwaddr_cloned_not_set, )); + NM_SETT_INFO_PROPERT_TYPE_DBUS( + G_VARIANT_TYPE_BYTESTRING, + .compare_fcn = compare_fcn_cloned_mac_address, + .to_dbus_fcn = _nm_sett_info_prop_to_dbus_fcn_cloned_mac_address, + .from_dbus_fcn = _nm_sett_info_prop_from_dbus_fcn_cloned_mac_address, + .missing_from_dbus_fcn = _nm_sett_info_prop_missing_from_dbus_fcn_cloned_mac_address, ), + .dbus_deprecated = TRUE, ); /* ---dbus--- * property: assigned-mac-address @@ -1645,7 +1651,8 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass) * description: MAC address blacklist. * example: mac-address-blacklist= 00:22:68:12:79:A6;00:22:68:12:79:78 * ---end--- - * ---ifcfg-rh--- + */ + /* ---ifcfg-rh--- * property: mac-address-blacklist * variable: HWADDR_BLACKLIST(+) * description: It denies usage of the connection for any device whose address @@ -1782,11 +1789,10 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass) * the user has set a global default to randomize and the supplicant * supports randomization), %NM_SETTING_MAC_RANDOMIZATION_NEVER (never * randomize the MAC address), or %NM_SETTING_MAC_RANDOMIZATION_ALWAYS - * (always randomize the MAC address). This property is deprecated for - * 'cloned-mac-address'. + * (always randomize the MAC address). * * Since: 1.2 - * Deprecated: 1.4: Deprecated by NMSettingWireless:cloned-mac-address property. + * Deprecated: 1.4: Use the #NMSettingWireless:cloned-mac-address property instead. **/ /* ---ifcfg-rh--- * property: mac-address-randomization @@ -1805,7 +1811,8 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass) NM_SETTING_MAC_RANDOMIZATION_DEFAULT, NM_SETTING_PARAM_NONE, NMSettingWirelessPrivate, - mac_address_randomization); + mac_address_randomization, + .is_deprecated = TRUE, ); /* Compatibility for deprecated property */ /* ---ifcfg-rh--- @@ -1813,7 +1820,8 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass) * variable: (none) * description: This property is deprecated and not handled by ifcfg-rh-plugin. * ---end--- - * ---dbus--- + */ + /* ---dbus--- * property: security * description: This property is deprecated, but can be set to the value * '802-11-wireless-security' when a wireless security setting is also @@ -1825,8 +1833,9 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass) properties_override, "security", NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING, - .to_dbus_fcn = nm_setting_wireless_get_security, - .compare_fcn = _nm_setting_property_compare_fcn_ignore, )); + .to_dbus_fcn = security_to_dbus, + .compare_fcn = _nm_setting_property_compare_fcn_ignore, ), + .dbus_deprecated = TRUE, ); /** * NMSettingWireless:wake-on-wlan: diff --git a/src/libnm-core-impl/nm-setting-wpan.c b/src/libnm-core-impl/nm-setting-wpan.c index a3e56ded..bc84b01e 100644 --- a/src/libnm-core-impl/nm-setting-wpan.c +++ b/src/libnm-core-impl/nm-setting-wpan.c @@ -77,7 +77,7 @@ G_DEFINE_TYPE(NMSettingWpan, nm_setting_wpan, NM_TYPE_SETTING) * * Returns: the #NMSettingWpan:mac-address property of the setting * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ const char * nm_setting_wpan_get_mac_address(NMSettingWpan *setting) @@ -93,7 +93,7 @@ nm_setting_wpan_get_mac_address(NMSettingWpan *setting) * * Returns: the #NMSettingWpan:pan-id property of the setting * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ guint16 nm_setting_wpan_get_pan_id(NMSettingWpan *setting) @@ -109,7 +109,7 @@ nm_setting_wpan_get_pan_id(NMSettingWpan *setting) * * Returns: the #NMSettingWpan:short-address property of the setting * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ guint16 nm_setting_wpan_get_short_address(NMSettingWpan *setting) @@ -125,7 +125,7 @@ nm_setting_wpan_get_short_address(NMSettingWpan *setting) * * Returns: the #NMSettingWpan:page property of the setting * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ gint16 nm_setting_wpan_get_page(NMSettingWpan *setting) @@ -141,7 +141,7 @@ nm_setting_wpan_get_page(NMSettingWpan *setting) * * Returns: the #NMSettingWpan:channel property of the setting * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ gint16 nm_setting_wpan_get_channel(NMSettingWpan *setting) @@ -213,7 +213,7 @@ nm_setting_wpan_init(NMSettingWpan *setting) * * Returns: (transfer full): the new empty #NMSettingWpan object * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ NMSetting * nm_setting_wpan_new(void) diff --git a/src/libnm-core-impl/nm-setting.c b/src/libnm-core-impl/nm-setting.c index b6f72137..5a46a1b4 100644 --- a/src/libnm-core-impl/nm-setting.c +++ b/src/libnm-core-impl/nm-setting.c @@ -331,7 +331,7 @@ _nm_setting_class_commit(NMSettingClass *setting_class, override_len = properties_override->len; for (i = 0; i < override_len; i++) { - NMSettInfoProperty *p = &g_array_index(properties_override, NMSettInfoProperty, i); + NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i); nm_assert((!!p->name) != (!!p->param_spec)); @@ -346,14 +346,14 @@ _nm_setting_class_commit(NMSettingClass *setting_class, #if NM_MORE_ASSERTS > 10 /* assert that properties_override is constructed consistently. */ for (i = 0; i < override_len; i++) { - const NMSettInfoProperty *p = &g_array_index(properties_override, NMSettInfoProperty, i); + const NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i); gboolean found = FALSE; guint k; - nm_assert( - !_nm_sett_info_property_find_in_array((NMSettInfoProperty *) properties_override->data, - i, - p->name)); + nm_assert(!_nm_sett_info_property_find_in_array( + nm_g_array_index_p(properties_override, NMSettInfoProperty, 0), + i, + p->name)); for (k = 0; k < n_property_specs; k++) { if (!nm_streq(property_specs[k]->name, p->name)) continue; @@ -369,20 +369,20 @@ _nm_setting_class_commit(NMSettingClass *setting_class, const char *name = property_specs[i]->name; NMSettInfoProperty *p; - if (_nm_sett_info_property_find_in_array((NMSettInfoProperty *) properties_override->data, - override_len, - name)) + if (_nm_sett_info_property_find_in_array( + nm_g_array_index_p(properties_override, NMSettInfoProperty, 0), + override_len, + name)) continue; - g_array_set_size(properties_override, properties_override->len + 1); - p = &g_array_index(properties_override, NMSettInfoProperty, properties_override->len - 1); + p = nm_g_array_append_new(properties_override, NMSettInfoProperty); memset(p, 0, sizeof(*p)); p->name = name; p->param_spec = property_specs[i]; } for (i = 0; i < properties_override->len; i++) { - NMSettInfoProperty *p = &g_array_index(properties_override, NMSettInfoProperty, i); + NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i); GType vtype; if (p->property_type) @@ -492,12 +492,12 @@ _nm_sett_info_setting_get_property_info(const NMSettInfoSetting *sett_info, return NULL; G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMSettInfoProperty, name) == 0); - idx = nm_utils_array_find_binary_search(sett_info->property_infos, - sizeof(NMSettInfoProperty), - sett_info->property_infos_len, - &property_name, - nm_strcmp_p_with_data, - NULL); + idx = nm_array_find_bsearch(sett_info->property_infos, + sett_info->property_infos_len, + sizeof(NMSettInfoProperty), + &property_name, + nm_strcmp_p_with_data, + NULL); if (idx < 0) return NULL; @@ -604,7 +604,48 @@ _nm_setting_use_legacy_property(NMSetting *setting, const char *new_property) { gs_unref_variant GVariant *setting_dict = NULL; - gs_unref_variant GVariant *value = NULL; + gs_unref_variant GVariant *val_leg = NULL; + gs_unref_variant GVariant *val_new = NULL; + + /* We want to be both forward and backward compatible (both the client or the daemon + * can be newer). + * + * For the most part, we achieve that by ignoring unknown properties (to be forward + * compatible). That of course has the downside, that we don't do strong validation + * of the input. + * + * In some cases, we deprecated a D-Bus property for another one (e.g. the legacy property + * "ipv4.routes" became the new property "ipv4.route-data"). In that case, the to/from D-Bus + * methods behave differently on the client and the daemon. + * + * The daemon will serialize both the legacy property and the new property to D-Bus. + * The client, will prefer the newer property (if it exists) when deserializing from D-Bus. + * + * Usually that scheme would fully suffice to support forward and backward compatibility. + * However, there is a problem. An old client (unaware of the new property) might get + * the profile, modify the old property, and send the entire profile back to the daemon. + * In this case, the old client does not know that the new property conflicts with the + * old property. The client also might try to preserve any unknown properties and send + * them back to the daemon. If the daemon now would prefer the new property, it would be wrong. + * + * The solution to this is that the daemon -- when both old and new property is set -- + * will prefer the old property. This is what _nm_setting_use_legacy_property() checks + * for. Consequently, a new client will not serialize both the old and the new property. + * This is done via "to_dbus_only_in_manager_process" flag. + * + * The downside of this scheme is that: + * + * - to/from D-Bus just got more complicated and behaves differently on the client + * and the daemon. + * - backward compatibility does not work with a newer client vs. and older daemon. + * This is the major downside. It's only not that severe, because we only deprecate + * properties seldom and only on major versions. Major version updates happen not + * often and they user might reboot (restart the daemon). + * + * The benefit is that the case with an older client and a newer daemon works, even + * if the client fetches a (new) profile, modifies only parts that it understands, + * and sends back the complete profile (including the new, unmodified properties). + */ if (!connection_dict) { /* we also allow the caller to provide no connection_dict. @@ -626,19 +667,24 @@ _nm_setting_use_legacy_property(NMSetting *setting, g_return_val_if_fail(setting_dict != NULL, FALSE); - /* If the new property isn't set, we have to use the legacy property. */ - value = g_variant_lookup_value(setting_dict, new_property, NULL); - if (!value) - return TRUE; - nm_clear_pointer(&value, g_variant_unref); + if (!_nm_utils_is_manager_process) { + /* The client will prefer the new property, unless it does not exist and + * the legacy property exists. */ + val_new = g_variant_lookup_value(setting_dict, new_property, NULL); + if (!val_new) { + val_leg = g_variant_lookup_value(setting_dict, legacy_property, NULL); + if (val_leg) + return TRUE; + } - /* Otherwise, clients always prefer new properties sent from the daemon. */ - if (!_nm_utils_is_manager_process) return FALSE; + } - /* The daemon prefers the legacy property if it exists. */ - value = g_variant_lookup_value(setting_dict, legacy_property, NULL); - return !!value; + /* The daemon prefers the old property (if it exists). */ + val_leg = g_variant_lookup_value(setting_dict, legacy_property, NULL); + if (val_leg) + return TRUE; + return FALSE; } /*****************************************************************************/ @@ -1256,7 +1302,7 @@ _nm_setting_property_to_dbus_fcn_direct(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_ (const NMValueStrv *) _nm_setting_get_private_field(setting, sett_info, property_info); if (!val->arr) return NULL; - return g_variant_new_strv((const char *const *) val->arr->data, val->arr->len); + return g_variant_new_strv(nm_g_array_data(val->arr), val->arr->len); } default: return nm_assert_unreachable_val(NULL); @@ -1310,7 +1356,7 @@ _nm_setting_property_to_dbus_fcn_gprop(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_n nm_assert(G_VALUE_HOLDS(&prop_value, G_TYPE_ARRAY)); tmp_array = g_value_get_boxed(&prop_value); nm_assert(tmp_array); - return nm_g_variant_new_au((const guint32 *) tmp_array->data, tmp_array->len); + return nm_g_variant_new_au(nm_g_array_data(tmp_array), tmp_array->len); case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_STRDICT: nm_assert(G_VALUE_HOLDS(&prop_value, G_TYPE_HASH_TABLE)); return nm_strdict_to_variant_ass(g_value_get_boxed(&prop_value)); @@ -1752,31 +1798,36 @@ property_to_dbus(const NMSettInfoSetting *sett_info, || NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_WRITABLE) || property_info->property_type == &nm_sett_info_propert_type_setting_name); - if (property_info->param_spec && !ignore_flags - && !NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_TO_DBUS_IGNORE_FLAGS)) { - if (NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_LEGACY) - && !_nm_utils_is_manager_process) + if (ignore_flags) { + /* We are called from _nm_setting_property_compare_fcn_default(). We want + * to serialize the property, and ignore the flags. */ + } else { + if (property_info->to_dbus_only_in_manager_process && !_nm_utils_is_manager_process) return NULL; - if (NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET)) { - NMSettingSecretFlags f = NM_SETTING_SECRET_FLAG_NONE; + if (property_info->param_spec + && !NM_FLAGS_HAS(property_info->param_spec->flags, + NM_SETTING_PARAM_TO_DBUS_IGNORE_FLAGS)) { + if (NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET)) { + NMSettingSecretFlags f = NM_SETTING_SECRET_FLAG_NONE; + + if (NM_FLAGS_ANY(flags, + NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED + | NM_CONNECTION_SERIALIZE_WITH_SECRETS_SYSTEM_OWNED + | NM_CONNECTION_SERIALIZE_WITH_SECRETS_NOT_SAVED)) { + if (!nm_setting_get_secret_flags(setting, + property_info->param_spec->name, + &f, + NULL)) + return NULL; + } - if (NM_FLAGS_ANY(flags, - NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED - | NM_CONNECTION_SERIALIZE_WITH_SECRETS_SYSTEM_OWNED - | NM_CONNECTION_SERIALIZE_WITH_SECRETS_NOT_SAVED)) { - if (!nm_setting_get_secret_flags(setting, - property_info->param_spec->name, - &f, - NULL)) + if (!_nm_connection_serialize_secrets(flags, f)) + return NULL; + } else { + if (!_nm_connection_serialize_non_secret(flags)) return NULL; } - - if (!_nm_connection_serialize_secrets(flags, f)) - return NULL; - } else { - if (!_nm_connection_serialize_non_secret(flags)) - return NULL; } } @@ -3196,6 +3247,12 @@ _nm_setting_clear_secrets(NMSetting *setting, /** * _nm_setting_need_secrets: * @setting: the #NMSetting + * @check_rerequest: If %TRUE: the stored secrets might be wrong and the agent + * should query the user for the correct credentials. If an #NMSetting knows + * that this cannot be the case it should *not* return the corresponding + * setting object. Otherwise it should always return it, even if it is not + * missing. + * If %FALSE: only return it when it is missing. * * Returns an array of property names for each secret which may be required * to make a successful connection. The returned hints are only intended as a @@ -3208,14 +3265,14 @@ _nm_setting_clear_secrets(NMSetting *setting, * free the elements. **/ GPtrArray * -_nm_setting_need_secrets(NMSetting *setting) +_nm_setting_need_secrets(NMSetting *setting, gboolean check_rerequest) { GPtrArray *secrets = NULL; g_return_val_if_fail(NM_IS_SETTING(setting), NULL); if (NM_SETTING_GET_CLASS(setting)->need_secrets) - secrets = NM_SETTING_GET_CLASS(setting)->need_secrets(setting); + secrets = NM_SETTING_GET_CLASS(setting)->need_secrets(setting, check_rerequest); return secrets; } @@ -3513,7 +3570,7 @@ nm_setting_to_string(NMSetting *setting) } static GVariant * -_nm_setting_get_deprecated_virtual_interface_name(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +depreated_interface_name_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { NMSettingConnection *s_con; @@ -3533,8 +3590,9 @@ _nm_setting_get_deprecated_virtual_interface_name(_NM_SETT_INFO_PROP_TO_DBUS_FCN const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_interface_name = NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING, .compare_fcn = _nm_setting_property_compare_fcn_ignore, - .to_dbus_fcn = - _nm_setting_get_deprecated_virtual_interface_name, ); + .to_dbus_fcn = depreated_interface_name_to_dbus, + /* from_dbus_fcn() is handled by the connection.interface-name setter. + * See nm_setting_connection_no_interface_name(). */ ); const NMSettInfoPropertType nm_sett_info_propert_type_setting_name = NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING, @@ -4066,6 +4124,233 @@ nm_setting_option_set_uint32(NMSetting *setting, const char *opt_name, guint32 v /*****************************************************************************/ +G_DEFINE_BOXED_TYPE(NMRange, nm_range, nm_range_ref, (GBoxedFreeFunc) nm_range_unref) + +static gboolean +NM_IS_RANGE(const NMRange *self) +{ + return self && self->refcount > 0; +} + +/** + * nm_range_new: + * @start: the first element of the range + * @end: the last element of the range, must be greater than or equal + * to @start. + * + * Creates a new #NMRange object for the given range. Setting @end + * equal to @start creates a single-element range. + * + * Returns: (transfer full): the new #NMRange object. + * + * Since: 1.42 + **/ +NMRange * +nm_range_new(guint64 start, guint64 end) +{ + NMRange *range; + + g_return_val_if_fail(start <= end, NULL); + + range = g_slice_new(NMRange); + *range = (NMRange){ + .refcount = 1, + .start = start, + .end = end, + }; + + return range; +} + +/** + * nm_range_ref: + * @range: the #NMRange + * + * Increases the reference count of the object. + * This is thread-safe. + * + * Returns: the input argument @range object. + * + * Since: 1.42 + **/ +NMRange * +nm_range_ref(const NMRange *range) +{ + g_return_val_if_fail(NM_IS_RANGE(range), NULL); + + nm_assert(range->refcount < G_MAXINT); + + g_atomic_int_inc(&((NMRange *) range)->refcount); + return (NMRange *) range; +} + +/** + * nm_range_unref: + * @range: the #NMRange + * + * Decreases the reference count of the object. If the reference count + * reaches zero the object will be destroyed. + * This is thread-safe. + * + * Since: 1.42 + **/ +void +nm_range_unref(const NMRange *range) +{ + g_return_if_fail(NM_IS_RANGE(range)); + + if (g_atomic_int_dec_and_test(&((NMRange *) range)->refcount)) + nm_g_slice_free((NMRange *) range); +} + +/** + * nm_range_cmp: + * @a: a #NMRange + * @b: another #NMRange + * + * Compare two ranges. + * + * Returns: zero if the two instances are equivalent or + * a non-zero integer otherwise. This defines a total ordering + * over the ranges. + * + * Since: 1.42 + **/ +int +nm_range_cmp(const NMRange *a, const NMRange *b) +{ + NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, start); + NM_CMP_FIELD(a, b, end); + + return 0; +} + +/** + * nm_range_get_range: + * @range: the #NMRange + * @start: (out): location to store the start value + * @end: (out): location to store the end value + * + * Gets the start and end values for the range. + * + * Returns: %TRUE if the range contains more than one + * element, %FALSE otherwise. + * + * Since: 1.42 + **/ +gboolean +nm_range_get_range(const NMRange *range, guint64 *start, guint64 *end) +{ + /* with LTO and optimization, the compiler complains that the + * output variables are not initialized. In practice, the function + * only sets the output on success. But make the compiler happy. + */ + NM_SET_OUT(start, 0); + NM_SET_OUT(end, 0); + + g_return_val_if_fail(NM_IS_RANGE(range), 0); + + NM_SET_OUT(start, range->start); + NM_SET_OUT(end, range->end); + + return range->start != range->end; +} + +/** + * nm_range_to_str: + * @range: the %NMRange + * + * Convert a %NMRange to a string. + * + * Returns: (transfer full): a string representing the range. + * + * Since: 1.42 + */ +char * +nm_range_to_str(const NMRange *range) +{ + char buf[200]; + char *b = buf; + gsize l = sizeof(buf); + + g_return_val_if_fail(NM_IS_RANGE(range), NULL); + + nm_strbuf_append(&b, &l, "%" G_GUINT64_FORMAT, range->start); + if (range->start != range->end) + nm_strbuf_append(&b, &l, "-%" G_GUINT64_FORMAT, range->end); + + nm_assert(l > 0); + return nm_memdup_nul(buf, sizeof(buf) - l); +} + +/** + * nm_range_from_str: + * @str: the string representation of a range + * @error: (out) (allow-none): location to store the error on failure + * + * Parses the string representation of the range to create a %NMRange + * instance. + * + * Returns: (transfer full): the %NMRange or %NULL + * + * Since: 1.42 + */ +NMRange * +nm_range_from_str(const char *str, GError **error) +{ + gs_free char *str_free = NULL; + guint64 start; + guint64 end = 0; + char *c; + + g_return_val_if_fail(str, NULL); + g_return_val_if_fail(!error || !*error, NULL); + + c = strchr(str, '-'); + if (c) { + str = nm_strndup_a(300, str, c - str, &str_free); + c++; + } + + start = _nm_utils_ascii_str_to_uint64(str, 10, 0, G_MAXUINT64, 0); + if (errno != 0) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid range start '%s'", + str); + return NULL; + } + + if (c) { + end = _nm_utils_ascii_str_to_uint64(c, 10, 0, G_MAXUINT64, 0); + if (errno != 0) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid range end '%s'", + c); + return NULL; + } + if (end < start) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid range %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT + ", start must be less than or equal to end", + start, + end); + return NULL; + } + } else + end = start; + + return nm_range_new(start, end); +} + +/*****************************************************************************/ + static void get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { diff --git a/src/libnm-core-impl/nm-simple-connection.c b/src/libnm-core-impl/nm-simple-connection.c index 6252dc2c..a9c66dc7 100644 --- a/src/libnm-core-impl/nm-simple-connection.c +++ b/src/libnm-core-impl/nm-simple-connection.c @@ -47,9 +47,6 @@ G_DEFINE_TYPE_WITH_CODE(NMSimpleConnection, G_IMPLEMENT_INTERFACE(NM_TYPE_CONNECTION, nm_simple_connection_interface_init);) -#define _GET_PRIVATE(self) \ - G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_SIMPLE_CONNECTION, NMConnectionPrivate) - /*****************************************************************************/ static void @@ -57,7 +54,7 @@ nm_simple_connection_init(NMSimpleConnection *self) { NMConnectionPrivate *priv; - priv = _GET_PRIVATE(self); + priv = _NM_SIMPLE_CONNECTION_GET_CONNECTION_PRIVATE(self); priv->self = (NMConnection *) self; } @@ -139,15 +136,12 @@ NMConnection * nm_simple_connection_new_clone(NMConnection *connection) { NMConnection *clone; - const char *path; g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL); clone = nm_simple_connection_new(); - path = nm_connection_get_path(connection); - if (path) - nm_connection_set_path(clone, path); + _nm_connection_set_path_rstr(clone, _nm_connection_get_path_rstr(connection)); nm_connection_replace_settings_from_connection(clone, connection); @@ -157,14 +151,12 @@ nm_simple_connection_new_clone(NMConnection *connection) static void dispose(GObject *object) { - NMConnection *connection = NM_CONNECTION(object); - #if NM_MORE_ASSERTS g_signal_handlers_disconnect_by_data(object, (gpointer) &_nm_assert_connection_unchanging_user_data); #endif - _nm_connection_private_clear(_GET_PRIVATE(connection)); + _nm_connection_private_clear(_NM_SIMPLE_CONNECTION_GET_CONNECTION_PRIVATE(object)); G_OBJECT_CLASS(nm_simple_connection_parent_class)->dispose(object); } diff --git a/src/libnm-core-impl/nm-team-utils.c b/src/libnm-core-impl/nm-team-utils.c index 6d87f50b..2f62f6c6 100644 --- a/src/libnm-core-impl/nm-team-utils.c +++ b/src/libnm-core-impl/nm-team-utils.c @@ -2148,11 +2148,11 @@ nm_team_setting_config_set(NMTeamSetting *self, const char *js_str) else { gboolean unrecognized_content = FALSE; bool has_lst[_NM_TEAM_ATTRIBUTE_NUM] = { - FALSE, + FALSE, }; NMValueTypUnion val_lst[_NM_TEAM_ATTRIBUTE_NUM]; nm_json_t *found_keys[_NM_TEAM_ATTRIBUTE_NUM] = { - NULL, + NULL, }; gs_unref_ptrarray GPtrArray *ptr_array_master_runner_tx_hash_free = NULL; gs_unref_ptrarray GPtrArray *ptr_array_link_watchers_free = NULL; diff --git a/src/libnm-core-impl/nm-utils-private.h b/src/libnm-core-impl/nm-utils-private.h index 1a6f29ef..e2e1aff7 100644 --- a/src/libnm-core-impl/nm-utils-private.h +++ b/src/libnm-core-impl/nm-utils-private.h @@ -64,11 +64,13 @@ GVariant *_nm_team_settings_property_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS void _nm_team_settings_property_from_dbus_link_watchers( _NM_SETT_INFO_PROP_FROM_DBUS_GPROP_FCN_ARGS _nm_nil); -GVariant *_nm_utils_ip4_dns_to_variant(const char *const *dns, gssize len); -GVariant *_nm_utils_ip6_dns_to_variant(const char *const *dns, gssize len); +GVariant *nm_utils_dns_to_variant(int addr_family, const char *const *dns, gssize len); const char *const *nmtst_system_encodings_for_lang(const char *lang); const char *const *nmtst_system_encodings_get_default(void); const char *const *nmtst_system_encodings_get(void); +gboolean +_nm_setting_ovs_verify_connection_type(GType gtype, NMConnection *connection, GError **error); + #endif diff --git a/src/libnm-core-impl/nm-utils.c b/src/libnm-core-impl/nm-utils.c index 1c6d36c2..8ffc070e 100644 --- a/src/libnm-core-impl/nm-utils.c +++ b/src/libnm-core-impl/nm-utils.c @@ -269,7 +269,7 @@ nm_sock_addr_endpoint_get_fixed_sockaddr(NMSockAddrEndpoint *self, gpointer sock if (!self->host) return FALSE; - if (nm_utils_parse_inaddr_bin(AF_UNSPEC, self->host, &addr_family, &addrbin)) + if (nm_inet_parse_bin(AF_UNSPEC, self->host, &addr_family, &addrbin)) goto good; /* See if there is an IPv6 scope-id... @@ -295,7 +295,7 @@ nm_sock_addr_endpoint_get_fixed_sockaddr(NMSockAddrEndpoint *self, gpointer sock const char *host_part; host_part = nm_strndup_a(200, self->host, s - self->host, &tmp_str); - if (nm_utils_parse_inaddr_bin(AF_INET6, host_part, &addr_family, &addrbin)) + if (nm_inet_parse_bin(AF_INET6, host_part, &addr_family, &addrbin)) goto good; } @@ -1297,12 +1297,30 @@ nm_utils_wpa_psk_valid(const char *psk) GVariant * nm_utils_ip4_dns_to_variant(char **dns) { - return _nm_utils_ip4_dns_to_variant(NM_CAST_STRV_CC(dns), -1); + return nm_utils_dns_to_variant(AF_INET, NM_CAST_STRV_CC(dns), -1); } +/** + * nm_utils_ip6_dns_to_variant: + * @dns: (type utf8): an array of IP address strings + * + * Utility function to convert an array of IP address strings int a #GVariant of + * type 'aay' representing an array of IPv6 addresses. + * + * If a string cannot be parsed, it will be silently ignored. + * + * Returns: (transfer none): a new floating #GVariant representing @dns. + **/ GVariant * -_nm_utils_ip4_dns_to_variant(const char *const *dns, gssize len) +nm_utils_ip6_dns_to_variant(char **dns) { + return nm_utils_dns_to_variant(AF_INET6, NM_CAST_STRV_CC(dns), -1); +} + +GVariant * +nm_utils_dns_to_variant(int addr_family, const char *const *dns, gssize len) +{ + const int IS_IPv4 = NM_IS_IPv4(addr_family); GVariantBuilder builder; gsize l; gsize i; @@ -1312,13 +1330,20 @@ _nm_utils_ip4_dns_to_variant(const char *const *dns, gssize len) else l = len; - g_variant_builder_init(&builder, G_VARIANT_TYPE("au")); + g_variant_builder_init(&builder, IS_IPv4 ? G_VARIANT_TYPE("au") : G_VARIANT_TYPE("aay")); for (i = 0; i < l; i++) { - in_addr_t ip; + NMIPAddr ip; + + /* We can only represent the IP address on the legacy property "ipv[46].dns". + * Expose what we can. */ + if (!nm_utils_dnsname_parse(addr_family, dns[i], NULL, &ip, NULL)) + continue; - if (inet_pton(AF_INET, dns[i], &ip) == 1) + if (IS_IPv4) g_variant_builder_add(&builder, "u", ip); + else + g_variant_builder_add(&builder, "@ay", nm_g_variant_new_ay_in6addr(&ip.addr6)); } return g_variant_builder_end(&builder); @@ -1346,7 +1371,7 @@ nm_utils_ip4_dns_from_variant(GVariant *value) array = g_variant_get_fixed_array(value, &length, sizeof(guint32)); dns = g_new(char *, length + 1u); for (i = 0; i < length; i++) - dns[i] = nm_utils_inet4_ntop_dup(array[i]); + dns[i] = nm_inet4_ntop_dup(array[i]); dns[i] = NULL; return dns; @@ -1449,7 +1474,7 @@ nm_utils_ip4_addresses_from_variant(GVariant *value, char **out_gateway) g_ptr_array_add(addresses, addr); if (addr_array[2] && out_gateway && !*out_gateway) - *out_gateway = nm_utils_inet4_ntop_dup(addr_array[2]); + *out_gateway = nm_inet4_ntop_dup(addr_array[2]); } else { g_warning("Ignoring invalid IP4 address: %s", error->message); g_clear_error(&error); @@ -1571,7 +1596,7 @@ nm_utils_ip4_routes_from_variant(GVariant *value) guint32 nm_utils_ip4_netmask_to_prefix(guint32 netmask) { - return _nm_utils_ip4_netmask_to_prefix(netmask); + return nm_ip4_addr_netmask_to_prefix(netmask); } /** @@ -1585,7 +1610,7 @@ nm_utils_ip4_prefix_to_netmask(guint32 prefix) { g_return_val_if_fail(prefix <= 32, 0xffffffffu); - return _nm_utils_ip4_prefix_to_netmask(prefix); + return nm_ip4_addr_netmask_from_prefix(prefix); } /** @@ -1603,47 +1628,7 @@ nm_utils_ip4_prefix_to_netmask(guint32 prefix) guint32 nm_utils_ip4_get_default_prefix(guint32 ip) { - return _nm_utils_ip4_get_default_prefix(ip); -} - -/** - * nm_utils_ip6_dns_to_variant: - * @dns: (type utf8): an array of IP address strings - * - * Utility function to convert an array of IP address strings int a #GVariant of - * type 'aay' representing an array of IPv6 addresses. - * - * If a string cannot be parsed, it will be silently ignored. - * - * Returns: (transfer none): a new floating #GVariant representing @dns. - **/ -GVariant * -nm_utils_ip6_dns_to_variant(char **dns) -{ - return _nm_utils_ip6_dns_to_variant(NM_CAST_STRV_CC(dns), -1); -} - -GVariant * -_nm_utils_ip6_dns_to_variant(const char *const *dns, gssize len) -{ - GVariantBuilder builder; - gsize i; - gsize l; - - if (len < 0) - l = NM_PTRARRAY_LEN(dns); - else - l = len; - - g_variant_builder_init(&builder, G_VARIANT_TYPE("aay")); - for (i = 0; i < l; i++) { - struct in6_addr ip; - - if (inet_pton(AF_INET6, dns[i], &ip) != 1) - continue; - g_variant_builder_add(&builder, "@ay", nm_g_variant_new_ay_in6addr(&ip)); - } - return g_variant_builder_end(&builder); + return nm_ip4_addr_get_default_prefix(ip); } /** @@ -1676,7 +1661,7 @@ nm_utils_ip6_dns_from_variant(GVariant *value) const struct in6_addr *ip = g_variant_get_fixed_array(ip_var, &length, 1); if (length == sizeof(struct in6_addr)) - dns[i++] = nm_utils_inet6_ntop_dup(ip); + dns[i++] = nm_inet6_ntop_dup(ip); g_variant_unref(ip_var); } @@ -1798,7 +1783,7 @@ nm_utils_ip6_addresses_from_variant(GVariant *value, char **out_gateway) goto next; } if (!IN6_IS_ADDR_UNSPECIFIED(gateway_bytes)) - *out_gateway = nm_utils_inet6_ntop_dup(gateway_bytes); + *out_gateway = nm_inet6_ntop_dup(gateway_bytes); } } else { g_warning("Ignoring invalid IP6 address: %s", error->message); @@ -1943,7 +1928,7 @@ next: * * Returns: (transfer none): a new floating #GVariant representing @addresses. * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ GVariant * nm_utils_ip_addresses_to_variant(GPtrArray *addresses) @@ -1998,7 +1983,7 @@ nm_utils_ip_addresses_to_variant(GPtrArray *addresses) * Returns: (transfer full) (element-type NMIPAddress): a newly allocated * #GPtrArray of #NMIPAddress objects * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ GPtrArray * nm_utils_ip_addresses_from_variant(GVariant *value, int family) @@ -2059,7 +2044,7 @@ nm_utils_ip_addresses_from_variant(GVariant *value, int family) * * Returns: (transfer none): a new floating #GVariant representing @routes. * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ GVariant * nm_utils_ip_routes_to_variant(GPtrArray *routes) @@ -2127,7 +2112,7 @@ nm_utils_ip_routes_to_variant(GPtrArray *routes) * Returns: (transfer full) (element-type NMIPRoute): a newly allocated * #GPtrArray of #NMIPRoute objects * - * Since: 1.42, 1.40.4 + * Since: 1.42 **/ GPtrArray * nm_utils_ip_routes_from_variant(GVariant *value, int family) @@ -3833,13 +3818,13 @@ _nm_utils_ipaddr_canonical_or_invalid(int addr_family, const char *ip, gboolean if (!ip) return NULL; - if (!nm_utils_parse_inaddr_bin(addr_family, ip, &addr_family, &addr_bin)) + if (!nm_inet_parse_bin(addr_family, ip, &addr_family, &addr_bin)) return g_strdup(ip); if (map_zero_to_null && nm_ip_addr_is_null(addr_family, &addr_bin)) return NULL; - return nm_utils_inet_ntop_dup(addr_family, &addr_bin); + return nm_inet_ntop_dup(addr_family, &addr_bin); } /* @@ -3988,7 +3973,7 @@ nm_utils_hwaddr_to_dbus(const char *str) } GVariant * -_nm_utils_hwaddr_cloned_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +_nm_sett_info_prop_to_dbus_fcn_cloned_mac_address(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { gs_free char *addr = NULL; @@ -3999,7 +3984,7 @@ _nm_utils_hwaddr_cloned_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) } gboolean -_nm_utils_hwaddr_cloned_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +_nm_sett_info_prop_from_dbus_fcn_cloned_mac_address(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { gsize length; const guint8 *array; @@ -4030,14 +4015,15 @@ _nm_utils_hwaddr_cloned_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) } gboolean -_nm_utils_hwaddr_cloned_not_set(_NM_SETT_INFO_PROP_MISSING_FROM_DBUS_FCN_ARGS _nm_nil) +_nm_sett_info_prop_missing_from_dbus_fcn_cloned_mac_address( + _NM_SETT_INFO_PROP_MISSING_FROM_DBUS_FCN_ARGS _nm_nil) { nm_assert(nm_streq0(property, "cloned-mac-address")); return TRUE; } static GVariant * -_nm_utils_hwaddr_cloned_data_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +assigned_mac_address_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) { gs_free char *addr = NULL; @@ -4066,7 +4052,7 @@ _nm_utils_hwaddr_cloned_data_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) } static gboolean -_nm_utils_hwaddr_cloned_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +assigned_mac_address_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) { nm_assert(nm_streq0(property_info->name, "assigned-mac-address")); @@ -4088,8 +4074,8 @@ _nm_utils_hwaddr_cloned_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) const NMSettInfoPropertType nm_sett_info_propert_type_assigned_mac_address = NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING, .compare_fcn = _nm_setting_property_compare_fcn_ignore, - .to_dbus_fcn = _nm_utils_hwaddr_cloned_data_synth, - .from_dbus_fcn = _nm_utils_hwaddr_cloned_data_set, ); + .to_dbus_fcn = assigned_mac_address_to_dbus, + .from_dbus_fcn = assigned_mac_address_from_dbus, ); /*****************************************************************************/ @@ -4255,13 +4241,13 @@ _nm_utils_generate_mac_address_mask_parse(const char *value, ouis = g_array_sized_new(FALSE, FALSE, sizeof(struct ether_addr), 4); do { + struct ether_addr *new; + s = s_next; s_next = _split_word(s); - g_array_set_size(ouis, ouis->len + 1); - if (!nm_utils_hwaddr_aton(s, - &g_array_index(ouis, struct ether_addr, ouis->len - 1), - ETH_ALEN)) { + new = nm_g_array_append_new(ouis, struct ether_addr); + if (!nm_utils_hwaddr_aton(s, new, ETH_ALEN)) { g_set_error(error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, @@ -4373,13 +4359,13 @@ nm_utils_is_uuid(const char *str) return nm_uuid_is_valid_nmlegacy(str); } -static _nm_thread_local char _nm_utils_inet_ntop_buffer[NM_UTILS_INET_ADDRSTRLEN]; +static _nm_thread_local char _nm_utils_inet_ntop_buffer[NM_INET_ADDRSTRLEN]; /** * nm_utils_inet4_ntop: (skip) * @inaddr: the address that should be converted to string. * @dst: the destination buffer, it must contain at least - * INET_ADDRSTRLEN or %NM_UTILS_INET_ADDRSTRLEN + * INET_ADDRSTRLEN or %NM_INET_ADDRSTRLEN * characters. If set to %NULL, it will return a pointer to an internal, static * buffer (shared with nm_utils_inet6_ntop()). Beware, that the internal * buffer will be overwritten with ever new call of nm_utils_inet4_ntop() or @@ -4401,14 +4387,14 @@ nm_utils_inet4_ntop(in_addr_t inaddr, char *dst) * * However, still support it to be lenient against mistakes and because * this is public API of libnm. */ - return _nm_utils_inet4_ntop(inaddr, dst ?: _nm_utils_inet_ntop_buffer); + return nm_inet4_ntop(inaddr, dst ?: _nm_utils_inet_ntop_buffer); } /** * nm_utils_inet6_ntop: (skip) * @in6addr: the address that should be converted to string. * @dst: the destination buffer, it must contain at least - * INET6_ADDRSTRLEN or %NM_UTILS_INET_ADDRSTRLEN + * INET6_ADDRSTRLEN or %NM_INET_ADDRSTRLEN * characters. If set to %NULL, it will return a pointer to an internal, static * buffer (shared with nm_utils_inet4_ntop()). Beware, that the internal * buffer will be overwritten with ever new call of nm_utils_inet4_ntop() or @@ -4432,7 +4418,7 @@ nm_utils_inet6_ntop(const struct in6_addr *in6addr, char *dst) * However, still support it to be lenient against mistakes and because * this is public API of libnm. */ g_return_val_if_fail(in6addr, NULL); - return _nm_utils_inet6_ntop(in6addr, dst ?: _nm_utils_inet_ntop_buffer); + return nm_inet6_ntop(in6addr, dst ?: _nm_utils_inet_ntop_buffer); } /** @@ -4450,7 +4436,7 @@ nm_utils_ipaddr_valid(int family, const char *ip) { g_return_val_if_fail(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC, FALSE); - return nm_utils_ipaddr_is_valid(family, ip); + return nm_inet_is_valid(family, ip); } /** @@ -5030,11 +5016,8 @@ _nm_variant_attribute_spec_find_binary_search(const NMVariantAttributeSpec *cons G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMVariantAttributeSpec, name) == 0); - idx = nm_utils_ptrarray_find_binary_search((gconstpointer *) array, - len, - &name, - nm_strcmp_p_with_data, - NULL); + idx = + nm_ptrarray_find_bsearch((gconstpointer *) array, len, &name, nm_strcmp_p_with_data, NULL); if (idx < 0) return NULL; return array[idx]; @@ -5589,6 +5572,97 @@ _nm_utils_bridge_vlan_verify_list(GPtrArray *vlans, return TRUE; } +GVariant * +_nm_utils_ranges_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil) +{ + gs_unref_ptrarray GPtrArray *ranges = NULL; + GVariantBuilder builder; + const char *property_name = property_info->name; + guint i; + + nm_assert(property_name); + + g_object_get(setting, property_name, &ranges, NULL); + g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); + + if (ranges) { + for (i = 0; i < ranges->len; i++) { + NMRange *range = ranges->pdata[i]; + GVariantBuilder range_builder; + + g_variant_builder_init(&range_builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&range_builder, + "{sv}", + "start", + g_variant_new_uint64(range->start)); + g_variant_builder_add(&range_builder, "{sv}", "end", g_variant_new_uint64(range->end)); + + g_variant_builder_add(&builder, "a{sv}", &range_builder); + } + } + + return g_variant_builder_end(&builder); +} + +gboolean +_nm_utils_ranges_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil) +{ + gs_unref_ptrarray GPtrArray *ranges = NULL; + GVariantIter iter; + GVariant *range_var; + + g_return_val_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}")), FALSE); + + ranges = g_ptr_array_new_with_free_func((GDestroyNotify) nm_range_unref); + g_variant_iter_init(&iter, value); + while (g_variant_iter_next(&iter, "@a{sv}", &range_var)) { + _nm_unused gs_unref_variant GVariant *var_unref = range_var; + gint64 start; + gint64 end; + + if (!g_variant_lookup(range_var, "start", "t", &start)) + continue; + if (!g_variant_lookup(range_var, "end", "t", &end)) + continue; + if (start > end) + continue; + + g_ptr_array_add(ranges, nm_range_new(start, end)); + } + + g_object_set(setting, property_info->name, ranges, NULL); + + return TRUE; +} + +NMTernary +_nm_utils_ranges_cmp(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil) +{ + const GPtrArray *ranges_a = NULL; + const GPtrArray *ranges_b = NULL; + guint len; + guint i; + + if (nm_streq0(nm_setting_get_name(set_a), NM_SETTING_OVS_PORT_SETTING_NAME) + && nm_streq0(property_info->name, NM_SETTING_OVS_PORT_TRUNKS)) { + ranges_a = _nm_setting_ovs_port_get_trunks_arr(NM_SETTING_OVS_PORT(set_a)); + if (set_b) + ranges_b = _nm_setting_ovs_port_get_trunks_arr(NM_SETTING_OVS_PORT(set_b)); + } else { + nm_assert_not_reached(); + } + + len = nm_g_ptr_array_len(ranges_a); + if (len != nm_g_ptr_array_len(ranges_b)) + return FALSE; + for (i = 0; i < len; i++) { + if (nm_range_cmp(ranges_a->pdata[i], ranges_b->pdata[i])) + return FALSE; + } + + return TRUE; +} + gboolean _nm_utils_iaid_verify(const char *str, gint64 *out_value) { @@ -5660,3 +5734,24 @@ _nm_utils_validate_dhcp_hostname_flags(NMDhcpHostnameFlags flags, int addr_famil return TRUE; } + +/*****************************************************************************/ + +/** + * nm_utils_ensure_gtypes: + * + * This ensures that all NMSetting GTypes are created. For example, + * after this call, g_type_from_name("NMSettingConnection") will work. + * + * This cannot fail and does nothing if the type already exists. + * + * Since: 1.42 + */ +void +nm_utils_ensure_gtypes(void) +{ + NMMetaSettingType meta_type; + + for (meta_type = 0; meta_type < _NM_META_SETTING_TYPE_NUM; meta_type++) + nm_meta_setting_infos[meta_type].get_setting_gtype(); +} diff --git a/src/libnm-core-impl/nm-vpn-plugin-info.c b/src/libnm-core-impl/nm-vpn-plugin-info.c index baa19347..61e9cde5 100644 --- a/src/libnm-core-impl/nm-vpn-plugin-info.c +++ b/src/libnm-core-impl/nm-vpn-plugin-info.c @@ -198,7 +198,7 @@ _sort_files(LoadDirInfo *a, LoadDirInfo *b) * VPN plugin directory. */ const char * -_nm_vpn_plugin_info_get_default_dir_etc() +_nm_vpn_plugin_info_get_default_dir_etc(void) { return DEFAULT_DIR_ETC; } @@ -210,7 +210,7 @@ _nm_vpn_plugin_info_get_default_dir_etc() * VPN plugin directory. */ const char * -_nm_vpn_plugin_info_get_default_dir_lib() +_nm_vpn_plugin_info_get_default_dir_lib(void) { return DEFAULT_DIR_LIB; } @@ -223,7 +223,7 @@ _nm_vpn_plugin_info_get_default_dir_lib() * that directory. */ const char * -_nm_vpn_plugin_info_get_default_dir_user() +_nm_vpn_plugin_info_get_default_dir_user(void) { return nm_str_not_empty(g_getenv("NM_VPN_PLUGIN_DIR")); } @@ -296,7 +296,7 @@ _nm_vpn_plugin_info_list_load_dir(const char *dirname, g_array_sort(array, (GCompareFunc) _sort_files); for (i = 0; i < array->len; i++) - res = g_slist_prepend(res, g_array_index(array, LoadDirInfo, i).plugin_info); + res = g_slist_prepend(res, nm_g_array_index(array, LoadDirInfo, i).plugin_info); g_array_unref(array); @@ -312,7 +312,7 @@ _nm_vpn_plugin_info_list_load_dir(const char *dirname, * Since: 1.2 */ GSList * -nm_vpn_plugin_info_list_load() +nm_vpn_plugin_info_list_load(void) { int i; gint64 uid; @@ -898,7 +898,7 @@ nm_vpn_plugin_info_get_program(NMVpnPluginInfo *self) * * Returns: %TRUE if the service supports multiple instances with different bus names, otherwise %FALSE * - * Since: 1.42, 1.40.4 + * Since: 1.42 */ gboolean nm_vpn_plugin_info_supports_multiple(NMVpnPluginInfo *self) diff --git a/src/libnm-core-impl/tests/test-general.c b/src/libnm-core-impl/tests/test-general.c index a21f50db..f6684bfb 100644 --- a/src/libnm-core-impl/tests/test-general.c +++ b/src/libnm-core-impl/tests/test-general.c @@ -2501,6 +2501,9 @@ test_setting_ip_route_attributes(void) TEST_ATTR("tos", byte, 127, AF_INET, TRUE, TRUE); TEST_ATTR("tos", string, "0x28", AF_INET, FALSE, TRUE); + TEST_ATTR("weight", uint32, 100, AF_INET, TRUE, TRUE); + TEST_ATTR("weight", string, "100", AF_INET, FALSE, TRUE); + TEST_ATTR("advmss", uint32, 1400, AF_INET, TRUE, TRUE); TEST_ATTR("advmss", string, "1400", AF_INET, FALSE, TRUE); @@ -3900,7 +3903,7 @@ ensure_diffs(GHashTable *diffs, const DiffSetting *check, gsize n_check) { guint i; - g_assert(g_hash_table_size(diffs) == n_check); + g_assert_cmpint(g_hash_table_size(diffs), ==, n_check); /* Loop through the settings */ for (i = 0; i < n_check; i++) { @@ -3913,14 +3916,14 @@ ensure_diffs(GHashTable *diffs, const DiffSetting *check, gsize n_check) /* Get the number of keys to check */ while (check[i].keys[z].key_name) z++; - g_assert(g_hash_table_size(setting_hash) == z); + g_assert_cmpint(g_hash_table_size(setting_hash), ==, z); /* Now compare the actual keys */ for (z = 0; check[i].keys[z].key_name; z++) { NMSettingDiffResult result; result = GPOINTER_TO_UINT(g_hash_table_lookup(setting_hash, check[i].keys[z].key_name)); - g_assert(result == check[i].keys[z].result); + g_assert_cmpint(result, ==, check[i].keys[z].result); } } } @@ -4010,6 +4013,7 @@ test_connection_diff_a_only(void) {NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER, NM_SETTING_DIFF_RESULT_IN_A}, {NM_SETTING_IP_CONFIG_DHCP_REJECT_SERVERS, NM_SETTING_DIFF_RESULT_IN_A}, {NM_SETTING_IP4_CONFIG_LINK_LOCAL, NM_SETTING_DIFF_RESULT_IN_A}, + {NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW, NM_SETTING_DIFF_RESULT_IN_A}, {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN}, }}, }; @@ -4052,11 +4056,11 @@ test_connection_diff_different(void) NMSettingIPConfig *s_ip4; gboolean same; const DiffSetting settings[] = { - {NM_SETTING_IP4_CONFIG_SETTING_NAME, + {NM_SETTING_IP4_CONFIG_SETTING_NAME, { - {NM_SETTING_IP_CONFIG_METHOD, + {NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_DIFF_RESULT_IN_A | NM_SETTING_DIFF_RESULT_IN_B}, - {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN}, + {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN}, }}, }; @@ -4137,10 +4141,10 @@ test_connection_diff_inferrable(void) NMSettingIPConfig *s_ip4; char *uuid; const DiffSetting settings[] = { - {NM_SETTING_CONNECTION_SETTING_NAME, + {NM_SETTING_CONNECTION_SETTING_NAME, { - {NM_SETTING_CONNECTION_INTERFACE_NAME, NM_SETTING_DIFF_RESULT_IN_A}, - {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN}, + {NM_SETTING_CONNECTION_INTERFACE_NAME, NM_SETTING_DIFF_RESULT_IN_A}, + {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN}, }}, }; @@ -5061,7 +5065,7 @@ _netmask_to_prefix(guint32 netmask) /* we re-implemented the netmask-to-prefix code differently. Check * that they agree. */ - g_assert_cmpint(prefix, ==, _nm_utils_ip4_netmask_to_prefix(netmask)); + g_assert_cmpint(prefix, ==, nm_ip4_addr_netmask_to_prefix(netmask)); return prefix; } @@ -5072,7 +5076,7 @@ test_ip4_prefix_to_netmask(void) int i; for (i = 0; i <= 32; i++) { - guint32 netmask = _nm_utils_ip4_prefix_to_netmask(i); + guint32 netmask = nm_ip4_addr_netmask_from_prefix(i); int plen = _netmask_to_prefix(netmask); g_assert_cmpint(i, ==, plen); @@ -5100,8 +5104,8 @@ test_ip4_netmask_to_prefix(void) g_rand_set_seed(rand, 1); for (i = 2; i <= 32; i++) { - guint32 netmask = _nm_utils_ip4_prefix_to_netmask(i); - guint32 netmask_lowest_bit = netmask & ~_nm_utils_ip4_prefix_to_netmask(i - 1); + guint32 netmask = nm_ip4_addr_netmask_from_prefix(i); + guint32 netmask_lowest_bit = netmask & ~nm_ip4_addr_netmask_from_prefix(i - 1); g_assert_cmpint(i, ==, _netmask_to_prefix(netmask)); @@ -5250,7 +5254,8 @@ test_setting_ip4_changed_signal(void) ASSERT_CHANGED(nm_setting_ip_config_add_dns(s_ip4, "11.22.0.0")); ASSERT_CHANGED(nm_setting_ip_config_remove_dns(s_ip4, 0)); - NMTST_EXPECT_LIBNM_CRITICAL(NMTST_G_RETURN_MSG(idx >= 0 && idx < priv->dns->len)); + NMTST_EXPECT_LIBNM_CRITICAL( + NMTST_G_RETURN_MSG(idx >= 0 && ((guint) idx) < nm_g_ptr_array_len(priv->dns))); ASSERT_UNCHANGED(nm_setting_ip_config_remove_dns(s_ip4, 1)); g_test_assert_expected_messages(); @@ -5326,7 +5331,8 @@ test_setting_ip6_changed_signal(void) ASSERT_CHANGED(nm_setting_ip_config_add_dns(s_ip6, "1:2:3::4:5:6")); ASSERT_CHANGED(nm_setting_ip_config_remove_dns(s_ip6, 0)); - NMTST_EXPECT_LIBNM_CRITICAL(NMTST_G_RETURN_MSG(idx >= 0 && idx < priv->dns->len)); + NMTST_EXPECT_LIBNM_CRITICAL( + NMTST_G_RETURN_MSG(idx >= 0 && ((guint) idx) < nm_g_ptr_array_len(priv->dns))); ASSERT_UNCHANGED(nm_setting_ip_config_remove_dns(s_ip6, 1)); g_test_assert_expected_messages(); @@ -6957,7 +6963,7 @@ test_setting_ip6_gateway(void) gateway_bytes = g_variant_get_fixed_array(gateway_var, &length, 1); g_assert_cmpint(length, ==, 16); - nmtst_assert_ip6_address((struct in6_addr *) gateway_bytes, "abcd::1"); + nmtst_assert_ip6_address(NM_CAST_ALIGN(struct in6_addr, gateway_bytes), "abcd::1"); g_variant_unref(gateway_var); } g_variant_unref(value); @@ -7143,7 +7149,7 @@ _sock_addr_endpoint_fixed(const char *endpoint, const char *host, guint16 port, g_assert(host); g_assert(port > 0); - if (!nm_utils_parse_inaddr_bin(AF_UNSPEC, host, &addr_family, &addrbin)) + if (!nm_inet_parse_bin(AF_UNSPEC, host, &addr_family, &addrbin)) g_assert_not_reached(); ep = nm_sock_addr_endpoint_new(endpoint); @@ -7887,13 +7893,13 @@ test_nm_utils_uuid_generate_from_string(void) NM_UUID_NS_DNS); for (i = 0; i < G_N_ELEMENTS(zero_uuids); i++) { - nm_sprintf_buf(i_str, "%u", i), - _test_uuid(NM_UUID_TYPE_VERSION3, zero_uuids[i].uuid3, i_str, -1, NULL); + nm_sprintf_buf(i_str, "%u", i); + _test_uuid(NM_UUID_TYPE_VERSION3, zero_uuids[i].uuid3, i_str, -1, NULL); _test_uuid(NM_UUID_TYPE_VERSION5, zero_uuids[i].uuid5, i_str, -1, NULL); } for (i = 0; i < G_N_ELEMENTS(dns_uuids); i++) { - nm_sprintf_buf(i_str, "%u", i), - _test_uuid(NM_UUID_TYPE_VERSION3, dns_uuids[i].uuid3, i_str, -1, NM_UUID_NS_DNS); + nm_sprintf_buf(i_str, "%u", i); + _test_uuid(NM_UUID_TYPE_VERSION3, dns_uuids[i].uuid3, i_str, -1, NM_UUID_NS_DNS); _test_uuid(NM_UUID_TYPE_VERSION5, dns_uuids[i].uuid5, i_str, -1, NM_UUID_NS_DNS); } @@ -7949,12 +7955,52 @@ test_nm_utils_uuid_generate_from_string(void) /*****************************************************************************/ static void -__test_uuid(const char *expected_uuid, const char *str, gssize slen, char *uuid_test) +_check_uuid(NMUuidType uuid_type, + const NMUuid *type_arg, + const char *expected_uuid, + const char *str, + gssize slen, + const char *const *strv, + gssize strv_len) { + gs_free char *uuid_test = NULL; + gs_free char *uuid_test2 = NULL; + gboolean uuid_test2_valid = TRUE; + + g_assert(str); + g_assert(strv_len < 0 || strv); + + uuid_test = nm_uuid_generate_from_strings_strv(uuid_type, type_arg, strv, strv_len); + g_assert(uuid_test); g_assert(nm_uuid_is_normalized(uuid_test)); - if (strcmp(uuid_test, expected_uuid)) { + if (strv_len < 0 && strv) { + uuid_test2 = + nm_uuid_generate_from_strings_strv(uuid_type, type_arg, strv, NM_PTRARRAY_LEN(strv)); + } else if (strv_len >= 0) { + gssize l = nm_strv_find_first(strv, strv_len, NULL); + gs_free const char **strv2 = nm_strv_dup_packed(strv, l < 0 ? strv_len : l); + + uuid_test2 = nm_uuid_generate_from_strings_strv(uuid_type, + type_arg, + strv2 ?: NM_STRV_EMPTY_CC(), + -1); + if (l >= 0) { + /* there are NULL strings. The result won't be match. */ + uuid_test2_valid = FALSE; + } + } + if (uuid_test2) { + if (uuid_test2_valid) + g_assert_cmpstr(uuid_test, ==, uuid_test2); + else { + g_assert(nm_uuid_is_normalized(uuid_test)); + g_assert_cmpstr(uuid_test, !=, uuid_test2); + } + } + + if (!nm_streq(uuid_test, expected_uuid)) { g_error("UUID test failed (1): text=%s, len=%lld, expected=%s, uuid_test=%s", str, (long long) slen, @@ -7963,7 +8009,7 @@ __test_uuid(const char *expected_uuid, const char *str, gssize slen, char *uuid_ } g_free(uuid_test); - uuid_test = nm_uuid_generate_from_string_str(str, slen, NM_UUID_TYPE_VERSION3, &nm_uuid_ns_1); + uuid_test = nm_uuid_generate_from_string_str(str, slen, uuid_type, type_arg); g_assert(uuid_test); g_assert(nm_utils_is_uuid(uuid_test)); @@ -7975,11 +8021,20 @@ __test_uuid(const char *expected_uuid, const char *str, gssize slen, char *uuid_ expected_uuid, uuid_test); } - g_free(uuid_test); } -#define _test_uuid(expected_uuid, str, strlen, ...) \ - __test_uuid(expected_uuid, str, strlen, nm_uuid_generate_from_strings(__VA_ARGS__, NULL)) +#define check_uuid(uuid_type, type_arg, expected_uuid, str, ...) \ + ({ \ + const NMUuidType _uuid_type = (uuid_type); \ + const NMUuid *_type_arg = type_arg; \ + const char *_expected_uuid = (expected_uuid); \ + const char *_str = (str); \ + const gsize _strlen = NM_STRLEN(str); \ + const char *const *_strv = NM_MAKE_STRV(__VA_ARGS__); \ + const gssize _strv_len = NM_NARG(__VA_ARGS__); \ + \ + _check_uuid(_uuid_type, _type_arg, _expected_uuid, _str, _strlen, _strv, _strv_len); \ + }) static void test_nm_utils_uuid_generate_from_strings(void) @@ -8001,20 +8056,157 @@ test_nm_utils_uuid_generate_from_strings(void) g_assert_cmpstr(NM_UUID_NS_1, ==, nm_uuid_unparse(&nm_uuid_ns_1, buf)); g_assert_cmpstr(NM_UUID_NS_ZERO, ==, nm_uuid_unparse(&nm_uuid_ns_zero, buf)); - _test_uuid("b07c334a-399b-32de-8d50-58e4e08f98e3", "", 0, NULL); - _test_uuid("b8a426cb-bcb5-30a3-bd8f-6786fea72df9", "\0", 1, ""); - _test_uuid("12a4a982-7aae-39e1-951e-41aeb1250959", "a\0", 2, "a"); - _test_uuid("69e22c7e-f89f-3a43-b239-1cb52ed8db69", "aa\0", 3, "aa"); - _test_uuid("59829fd3-5ad5-3d90-a7b0-4911747e4088", "\0\0", 2, "", ""); - _test_uuid("01ad0e06-6c50-3384-8d86-ddab81421425", "a\0\0", 3, "a", ""); - _test_uuid("e1ed8647-9ed3-3ec8-8c6d-e8204524d71d", "aa\0\0", 4, "aa", ""); - _test_uuid("fb1c7cd6-275c-3489-9382-83b900da8af0", "\0a\0", 3, "", "a"); - _test_uuid("5d79494e-c4ba-31a6-80a2-d6016ccd7e17", "a\0a\0", 4, "a", "a"); - _test_uuid("fd698d86-1b60-3ebe-855f-7aada9950a8d", "aa\0a\0", 5, "aa", "a"); - _test_uuid("8c573b48-0f01-30ba-bb94-c5f59f4fe517", "\0aa\0", 4, "", "aa"); - _test_uuid("2bdd3d46-eb83-3c53-a41b-a724d04b5544", "a\0aa\0", 5, "a", "aa"); - _test_uuid("13d4b780-07c1-3ba7-b449-81c4844ef039", "aa\0aa\0", 6, "aa", "aa"); - _test_uuid("dd265bf7-c05a-3037-9939-b9629858a477", "a\0b\0", 4, "a", "b"); + _check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "457229f4-fe49-32f5-8b09-c531d81f44d9", + "x", + 1, + NULL, + -1); + check_uuid(NM_UUID_TYPE_VERSION3, &nm_uuid_ns_1, "b07c334a-399b-32de-8d50-58e4e08f98e3", ""); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "b8a426cb-bcb5-30a3-bd8f-6786fea72df9", + "\0", + ""); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "9232afda-85fc-3b8f-8736-4f99c8d5db9c", + "_n", + NULL); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "12a4a982-7aae-39e1-951e-41aeb1250959", + "a\0", + "a"); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "69e22c7e-f89f-3a43-b239-1cb52ed8db69", + "aa\0", + "aa"); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "59829fd3-5ad5-3d90-a7b0-4911747e4088", + "\0\0", + "", + ""); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "01ad0e06-6c50-3384-8d86-ddab81421425", + "a\0\0", + "a", + ""); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "e1ed8647-9ed3-3ec8-8c6d-e8204524d71d", + "aa\0\0", + "aa", + ""); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "fb1c7cd6-275c-3489-9382-83b900da8af0", + "\0a\0", + "", + "a"); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "5d79494e-c4ba-31a6-80a2-d6016ccd7e17", + "a\0a\0", + "a", + "a"); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "f36cec99-1db8-3baa-8c3f-13e13d980318", + "a\0a\0001_1n", + "a", + NULL, + "a"); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "fd698d86-1b60-3ebe-855f-7aada9950a8d", + "aa\0a\0", + "aa", + "a"); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "8c573b48-0f01-30ba-bb94-c5f59f4fe517", + "\0aa\0", + "", + "aa"); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "2bdd3d46-eb83-3c53-a41b-a724d04b5544", + "a\0aa\0", + "a", + "aa"); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "13d4b780-07c1-3ba7-b449-81c4844ef039", + "aa\0aa\0", + "aa", + "aa"); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_1, + "dd265bf7-c05a-3037-9939-b9629858a477", + "a\0b\0", + "a", + "b"); + + check_uuid(NM_UUID_TYPE_VERSION5, + _uuid(NM_UUID_NS_URL), + "dd247a64-df22-5d30-8087-0bd709f6941a", + "a\0b\0", + "a", + "b"); + check_uuid(NM_UUID_TYPE_VERSION5, + _uuid(NM_UUID_NS_URL), + "cbb93d73-085d-5072-94cd-a394b8149993", + "\0b\0", + "", + "b"); + check_uuid(NM_UUID_TYPE_VERSION5, + _uuid(NM_UUID_NS_URL), + "db3dfd17-c785-509d-a0ca-740fdd68dc68", + "\0b\00011_n", + "", + "b", + NULL); + check_uuid(NM_UUID_TYPE_VERSION3, + _uuid(NM_UUID_NS_URL), + "916dcdd8-5042-3b9b-9763-4312a31e5735", + "aa\0a\0", + "aa", + "a"); + check_uuid(NM_UUID_TYPE_VERSION3, + NULL, + "1700bb72-7116-3d1f-8cd2-6d074a40a3a9", + "aa\0a\0", + "aa", + "a"); + check_uuid(NM_UUID_TYPE_VERSION3, + &nm_uuid_ns_zero, + "1700bb72-7116-3d1f-8cd2-6d074a40a3a9", + "aa\0a\0", + "aa", + "a"); + check_uuid(NM_UUID_TYPE_VERSION5, + NULL, + "03c5de66-28ad-5a2e-8ed3-e256f3218900", + "aa\0a\0", + "aa", + "a"); + check_uuid(NM_UUID_TYPE_VERSION5, + &nm_uuid_ns_zero, + "03c5de66-28ad-5a2e-8ed3-e256f3218900", + "aa\0a\0", + "aa", + "a"); + check_uuid(NM_UUID_TYPE_LEGACY, + NULL, + "c38f63cf-1e50-ad7f-ae26-50f85cc5da47", + "aa\0a\0", + "aa", + "a"); } static void @@ -8287,7 +8479,7 @@ test_nm_utils_strstrdictkey(void) const char *v1; const char *v2; NMUtilsStrStrDictKey *v_static; - } * val1, *val2, + } *val1, *val2, values[] = { {NULL, NULL}, {"", NULL}, @@ -8787,19 +8979,15 @@ _test_find_binary_search_do(const int *array, gsize len) expected_result = nm_utils_ptrarray_find_first(parray, len, pneedle); - idx = nm_utils_ptrarray_find_binary_search_range(parray, - len, - pneedle, - _test_find_binary_search_cmp, - NULL, - &idx_first, - &idx_last); + idx = nm_ptrarray_find_bsearch_range(parray, + len, + pneedle, + _test_find_binary_search_cmp, + NULL, + &idx_first, + &idx_last); - idx2 = nm_utils_ptrarray_find_binary_search(parray, - len, - pneedle, - _test_find_binary_search_cmp, - NULL); + idx2 = nm_ptrarray_find_bsearch(parray, len, pneedle, _test_find_binary_search_cmp, NULL); g_assert_cmpint(idx, ==, idx2); if (expected_result >= 0) { @@ -8864,12 +9052,12 @@ _test_find_binary_search_do_uint32(const int *int_array, gsize len) expected_result = idx; } - idx = nm_utils_array_find_binary_search(array, - sizeof(guint32), - len, - &NEEDLE, - nm_cmp_uint32_p_with_data, - NULL); + idx = nm_array_find_bsearch(array, + len, + sizeof(guint32), + &NEEDLE, + nm_cmp_uint32_p_with_data, + NULL); if (expected_result >= 0) g_assert_cmpint(expected_result, ==, idx); else { @@ -8963,29 +9151,25 @@ test_nm_utils_ptrarray_find_binary_search_with_duplicates(void) for (i = 0; i < i_len + BIN_SEARCH_W_DUPS_JITTER; i++) { gconstpointer p = GINT_TO_POINTER(i); - idx = nm_utils_ptrarray_find_binary_search_range(arr, - i_len, - p, - _test_bin_search2_cmp, - NULL, - &idx_first, - &idx_last); + idx = nm_ptrarray_find_bsearch_range(arr, + i_len, + p, + _test_bin_search2_cmp, + NULL, + &idx_first, + &idx_last); idx_first2 = nm_utils_ptrarray_find_first(arr, i_len, p); - idx2 = nm_utils_array_find_binary_search(arr, - sizeof(gpointer), - i_len, - &p, - _test_bin_search2_cmp_p, - NULL); + idx2 = nm_array_find_bsearch(arr, + i_len, + sizeof(gpointer), + &p, + _test_bin_search2_cmp_p, + NULL); g_assert_cmpint(idx, ==, idx2); - idx2 = nm_utils_ptrarray_find_binary_search(arr, - i_len, - p, - _test_bin_search2_cmp, - NULL); + idx2 = nm_ptrarray_find_bsearch(arr, i_len, p, _test_bin_search2_cmp, NULL); g_assert_cmpint(idx, ==, idx2); if (idx_first2 < 0) { @@ -9791,7 +9975,7 @@ test_route_attributes_parse(void) g_assert(!ht); g_clear_error(&error); - ht = nm_utils_parse_variant_attributes("mtu.1400 src.1\\.2\\.3\\.4 ", + ht = nm_utils_parse_variant_attributes("mtu.1400 weight.5 src.1\\.2\\.3\\.4 ", ' ', '.', FALSE, @@ -9804,6 +9988,11 @@ test_route_attributes_parse(void) g_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32)); g_assert_cmpuint(g_variant_get_uint32(variant), ==, 1400); + variant = g_hash_table_lookup(ht, NM_IP_ROUTE_ATTRIBUTE_WEIGHT); + g_assert(variant); + g_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32)); + g_assert_cmpuint(g_variant_get_uint32(variant), ==, 5); + variant = g_hash_table_lookup(ht, NM_IP_ROUTE_ATTRIBUTE_SRC); g_assert(variant); g_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)); @@ -10255,8 +10444,8 @@ static void test_nm_ip_addr_zero(void) { in_addr_t a4 = nmtst_inet4_from_string("0.0.0.0"); - struct in6_addr a6 = *nmtst_inet6_from_string("::"); - char buf[NM_UTILS_INET_ADDRSTRLEN]; + struct in6_addr a6 = nmtst_inet6_from_string("::"); + char buf[NM_INET_ADDRSTRLEN]; NMIPAddr a = NM_IP_ADDR_INIT; g_assert(memcmp(&a, &nm_ip_addr_zero, sizeof(a)) == 0); @@ -10267,11 +10456,11 @@ test_nm_ip_addr_zero(void) g_assert(memcmp(&nm_ip_addr_zero, &a4, sizeof(a4)) == 0); g_assert(memcmp(&nm_ip_addr_zero, &a6, sizeof(a6)) == 0); - g_assert_cmpstr(_nm_utils_inet4_ntop(nm_ip_addr_zero.addr4, buf), ==, "0.0.0.0"); - g_assert_cmpstr(_nm_utils_inet6_ntop(&nm_ip_addr_zero.addr6, buf), ==, "::"); + g_assert_cmpstr(nm_inet4_ntop(nm_ip_addr_zero.addr4, buf), ==, "0.0.0.0"); + g_assert_cmpstr(nm_inet6_ntop(&nm_ip_addr_zero.addr6, buf), ==, "::"); - g_assert_cmpstr(nm_utils_inet_ntop(AF_INET, &nm_ip_addr_zero, buf), ==, "0.0.0.0"); - g_assert_cmpstr(nm_utils_inet_ntop(AF_INET6, &nm_ip_addr_zero, buf), ==, "::"); + g_assert_cmpstr(nm_inet_ntop(AF_INET, &nm_ip_addr_zero, buf), ==, "0.0.0.0"); + g_assert_cmpstr(nm_inet_ntop(AF_INET6, &nm_ip_addr_zero, buf), ==, "::"); G_STATIC_ASSERT_EXPR(sizeof(a) == sizeof(a.array)); } @@ -10951,6 +11140,218 @@ test_direct_string_is_refstr(void) /*****************************************************************************/ +static void +test_connection_path(void) +{ + gs_unref_object NMConnection *conn = NULL; + const char *const PATH = "/org/freedesktop/NetworkManager/Settings/171950003017"; + const char *path; + NMRefString *rstr; + + g_assert(!nmtst_ref_string_find(PATH)); + + conn = nmtst_create_minimal_connection("test_setting_ip6_gateway", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + g_assert(!nm_connection_get_path(conn)); + g_assert(!nmtst_ref_string_find(PATH)); + + nm_connection_set_path(conn, PATH); + + path = nm_connection_get_path(conn); + g_assert_cmpstr(path, ==, PATH); + + /* nm_connection_get_path() gives a NMRefString. This is an + * implementation detail, but libnm (which statically links with + * libnm-core) may choose to rely on that. */ + rstr = nmtst_ref_string_find(PATH); + g_assert(rstr); + g_assert(NM_REF_STRING_UPCAST(path) == rstr); + + g_clear_object(&conn); + + g_assert(!nmtst_ref_string_find(PATH)); +} + +/*****************************************************************************/ + +static void +_t_dnsname_1(const char *str, const char *exp_addr, const char *exp_server_name) +{ + int addr_family; + NMIPAddr exp_addr_bin; + gboolean addr_family_request; + gboolean r; + int detect_addr_family; + NMIPAddr detect_addr; + const char *detect_server_name; + int *p_detect_addr_family = &detect_addr_family; + NMIPAddr *p_detect_addr = &detect_addr; + const char **p_detect_server_name = &detect_server_name; + char str_construct_buf[100]; + char str_construct_buf2[100]; + const char *str_construct; + const char *str_construct2; + gsize l; + const char *str_normalized; + gs_free char *str_normalized_alloc = NULL; + + g_assert(str); + g_assert(exp_addr); + + r = nm_inet_parse_bin(AF_UNSPEC, exp_addr, &addr_family, &exp_addr_bin); + g_assert(r); + g_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); + + addr_family_request = nmtst_get_rand_bool(); + if (nmtst_get_rand_bool()) + p_detect_addr = NULL; + if ((addr_family_request || !p_detect_addr) && nmtst_get_rand_bool()) + p_detect_addr_family = NULL; + if (nmtst_get_rand_bool()) + p_detect_server_name = NULL; + + r = nm_utils_dnsname_parse(addr_family_request ? addr_family : AF_UNSPEC, + str, + p_detect_addr_family, + p_detect_addr, + p_detect_server_name); + g_assert(r); + + if (p_detect_addr_family) + g_assert_cmpint(addr_family, ==, detect_addr_family); + if (p_detect_addr) + g_assert_cmpstr(nmtst_inet_to_string(addr_family, &detect_addr), ==, exp_addr); + if (p_detect_server_name) + g_assert_cmpstr(detect_server_name, ==, exp_server_name); + + r = nm_utils_dnsname_parse(addr_family == AF_INET ? AF_INET6 : AF_INET, + str, + p_detect_addr_family, + p_detect_addr, + p_detect_server_name); + g_assert(!r); + + /* Construct the expected value. */ + str_construct = nm_utils_dnsname_construct(addr_family, + &exp_addr_bin, + exp_server_name, + str_construct_buf, + sizeof(str_construct_buf)); + g_assert(str_construct); + g_assert(str_construct == str_construct_buf); + g_assert(strlen(str_construct) < sizeof(str_construct_buf)); + + /* Check that a too short buffer causes truncation. */ + l = nmtst_get_rand_uint32() % (strlen(str_construct) + 10); + str_construct2 = nm_utils_dnsname_construct(addr_family, + &exp_addr_bin, + exp_server_name, + str_construct_buf2, + l); + if (str_construct2) { + g_assert(str_construct2 == str_construct_buf2); + g_assert_cmpstr(str_construct2, ==, str_construct); + g_assert(l > strlen(str_construct)); + } else + g_assert(l <= strlen(str_construct)); + + if (!nm_streq(str_construct, str)) { + _t_dnsname_1(str_construct, exp_addr, exp_server_name); + } + + str_normalized = nm_utils_dnsname_normalize(nmtst_get_rand_bool() ? addr_family : AF_UNSPEC, + str, + &str_normalized_alloc); + g_assert(str_normalized); + if (str_normalized_alloc) { + g_assert(str_normalized == str_normalized_alloc); + g_assert_cmpstr(str_normalized, !=, str); + } else { + g_assert(str == str_normalized); + } + g_assert_cmpstr(str_normalized, ==, str_construct); + + nm_clear_g_free(&str_normalized_alloc); + str_normalized = nm_utils_dnsname_normalize(addr_family == AF_INET ? AF_INET6 : AF_INET, + str, + &str_normalized_alloc); + g_assert(!str_normalized); + g_assert(!str_normalized_alloc); +} + +static void +_t_dnsname_0(const char *str) +{ + gboolean addr_family_request; + int detect_addr_family; + NMIPAddr detect_addr; + const char *detect_server_name; + int *p_detect_addr_family = &detect_addr_family; + NMIPAddr *p_detect_addr = &detect_addr; + const char **p_detect_server_name = &detect_server_name; + const char *str_normalized; + gs_free char *str_normalized_alloc = NULL; + gboolean r; + + g_assert(str); + + addr_family_request = nmtst_get_rand_bool(); + if (nmtst_get_rand_bool()) + p_detect_addr = NULL; + if ((addr_family_request || !p_detect_addr) && nmtst_get_rand_bool()) + p_detect_addr_family = NULL; + if (nmtst_get_rand_bool()) + p_detect_server_name = NULL; + + r = nm_utils_dnsname_parse(addr_family_request ? nmtst_rand_select(AF_INET, AF_INET6) + : AF_UNSPEC, + str, + p_detect_addr_family, + p_detect_addr, + p_detect_server_name); + g_assert(!r); + + str_normalized = nm_utils_dnsname_normalize(nmtst_rand_select(AF_UNSPEC, AF_INET, AF_INET6), + str, + &str_normalized_alloc); + g_assert(!str_normalized); + g_assert(!str_normalized_alloc); +} + +static void +test_dnsname(void) +{ + _t_dnsname_1("1.2.3.4", "1.2.3.4", NULL); + _t_dnsname_1("1.2.3.4#foo", "1.2.3.4", "foo"); + _t_dnsname_1("1::#x", "1::", "x"); + _t_dnsname_1("1::0#x", "1::", "x"); + _t_dnsname_1("192.168.0.1", "192.168.0.1", NULL); + _t_dnsname_1("192.168.0.1#test.com", "192.168.0.1", "test.com"); + _t_dnsname_1("fe80::18", "fe80::18", NULL); + _t_dnsname_1("fe80::18#hoge.com", "fe80::18", "hoge.com"); + + _t_dnsname_0("1.2.3.4#"); + _t_dnsname_0("1::0#"); + _t_dnsname_0("192.168.0.1:53"); + _t_dnsname_0("192.168.0.1:53#example.com"); + _t_dnsname_0("fe80::18%19"); + _t_dnsname_0("fe80::18%lo"); + _t_dnsname_0("[fe80::18]:53"); + _t_dnsname_0("[fe80::18]:53%19"); + _t_dnsname_0("[fe80::18]:53%lo"); + _t_dnsname_0("fe80::18%19#hoge.com"); + _t_dnsname_0("[fe80::18]:53#hoge.com"); + _t_dnsname_0("[fe80::18]:53%19"); + _t_dnsname_0("[fe80::18]:53%19#hoge.com"); + _t_dnsname_0("[fe80::18]:53%lo"); + _t_dnsname_0("[fe80::18]:53%lo#hoge.com"); +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -11257,7 +11658,7 @@ main(int argc, char **argv) g_test_add_func("/core/general/_nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64); g_test_add_func("/core/general/nm_utils_is_power_of_two", test_nm_utils_is_power_of_two); - g_test_add_func("/core/general/nm_utils_ptrarray_find_binary_search_range", + g_test_add_func("/core/general/nm_ptrarray_find_bsearch_range", test_nm_utils_ptrarray_find_binary_search); g_test_add_func("/core/general/nm_utils_ptrarray_find_binary_search_with_duplicates", test_nm_utils_ptrarray_find_binary_search_with_duplicates); @@ -11296,6 +11697,8 @@ main(int argc, char **argv) g_test_add_func("/core/general/test_system_encodings", test_system_encodings); g_test_add_func("/core/general/test_direct_string_is_refstr", test_direct_string_is_refstr); + g_test_add_func("/core/general/test_connection_path", test_connection_path); + g_test_add_func("/core/general/test_dnsname", test_dnsname); return g_test_run(); } diff --git a/src/libnm-core-impl/tests/test-setting.c b/src/libnm-core-impl/tests/test-setting.c index a4e3932a..de60afd1 100644 --- a/src/libnm-core-impl/tests/test-setting.c +++ b/src/libnm-core-impl/tests/test-setting.c @@ -121,7 +121,7 @@ test_nm_meta_setting_types_by_priority(void) G_STATIC_ASSERT_EXPR(_NM_META_SETTING_TYPE_NUM == G_N_ELEMENTS(nm_meta_setting_types_by_priority)); - G_STATIC_ASSERT_EXPR(_NM_META_SETTING_TYPE_NUM == 52); + G_STATIC_ASSERT_EXPR(_NM_META_SETTING_TYPE_NUM == 54); arr = g_ptr_array_new_with_free_func(g_object_unref); @@ -3755,7 +3755,7 @@ test_roundtrip_conversion(gconstpointer test_data) for (is_ipv4 = 0; is_ipv4 < 2; is_ipv4++) { g_assert(NM_IS_SETTING_IP_CONFIG(s_ip.s_x[is_ipv4])); for (i = 0; i < 3; i++) { - char addrstr[NM_UTILS_INET_ADDRSTRLEN]; + char addrstr[NM_INET_ADDRSTRLEN]; nm_auto_unref_ip_routing_rule NMIPRoutingRule *rr = NULL; @@ -4165,6 +4165,88 @@ test_routing_rule(gconstpointer test_data) /*****************************************************************************/ +static void +test_ranges(void) +{ + GError *error = NULL; + NMRange *r1; + NMRange *r2; + guint64 start; + guint64 end; + char *str = NULL; + char *str2 = NULL; + + r1 = nm_range_from_str("99", &error); + nmtst_assert_success(r1, error); + nm_range_get_range(r1, &start, &end); + g_assert_cmpint(start, ==, 99); + g_assert_cmpint(end, ==, 99); + str = nm_range_to_str(r1); + g_assert_cmpstr(str, ==, "99"); + nm_clear_g_free(&str); + nm_range_unref(r1); + + r1 = nm_range_from_str("1000-2000", &error); + nmtst_assert_success(r1, error); + nm_range_get_range(r1, &start, &end); + g_assert_cmpint(start, ==, 1000); + g_assert_cmpint(end, ==, 2000); + str = nm_range_to_str(r1); + g_assert_cmpstr(str, ==, "1000-2000"); + nm_clear_g_free(&str); + nm_range_unref(r1); + + r1 = nm_range_from_str("0", &error); + nmtst_assert_success(r1, error); + nm_range_unref(r1); + + r1 = nm_range_from_str("-1", &error); + nmtst_assert_no_success(r1, error); + g_clear_error(&error); + + r1 = nm_range_from_str("foobar", &error); + nmtst_assert_no_success(r1, error); + g_clear_error(&error); + + r1 = nm_range_from_str("200-100", &error); + nmtst_assert_no_success(r1, error); + g_clear_error(&error); + + r1 = nm_range_from_str("100-200", &error); + nmtst_assert_success(r1, error); + r2 = nm_range_from_str("100-200", &error); + nmtst_assert_success(r2, error); + g_assert_cmpint(nm_range_cmp(r1, r2), ==, 0); + nm_range_unref(r1); + nm_range_unref(r2); + + r1 = nm_range_from_str("100-200", &error); + nmtst_assert_success(r1, error); + r2 = nm_range_from_str("1", &error); + nmtst_assert_success(r2, error); + g_assert_cmpint(nm_range_cmp(r1, r2), ==, 1); + nm_range_ref(r1); + nm_range_unref(r1); + nm_range_unref(r1); + nm_range_unref(r2); + + r1 = nm_range_new(G_MAXUINT64 - 1, G_MAXUINT64); + g_assert(r1); + str = nm_range_to_str(r1); + g_assert_cmpstr(str, ==, "18446744073709551614-18446744073709551615"); + r2 = nm_range_from_str(str, &error); + nmtst_assert_success(r2, error); + str2 = nm_range_to_str(r2); + g_assert_cmpstr(str, ==, str2); + g_assert_cmpint(nm_range_cmp(r1, r2), ==, 0); + nm_range_unref(r1); + nm_range_unref(r2); + nm_clear_g_free(&str); + nm_clear_g_free(&str2); +} + +/*****************************************************************************/ + static void test_parse_tc_handle(void) { @@ -4303,7 +4385,7 @@ _PROP_IDX_OWNER(GHashTable *h_property_types, const NMSettInfoPropertType *prope g_assert(arr); g_assert(arr->len > 0); - idx = g_array_index(arr, guint, 0); + idx = nm_g_array_first(arr, guint); meta_type = (idx & 0xFFu); prop_idx = idx >> 8; @@ -5068,6 +5150,84 @@ test_6lowpan_1(void) /*****************************************************************************/ +static void +test_settings_dns(void) +{ + int i_run; + + for (i_run = 0; i_run < 10; i_run++) { + gs_unref_object NMConnection *con1 = NULL; + gs_unref_object NMConnection *con2 = NULL; + int IS_IPv4; + guint n_dns; + guint i; + gboolean same = TRUE; + + con1 = + nmtst_create_minimal_connection("test-dns", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL); + nmtst_connection_normalize(con1); + + con2 = nmtst_connection_duplicate_and_normalize(con1); + + nmtst_assert_connection_equals(con1, nmtst_get_rand_bool(), con2, nmtst_get_rand_bool()); + + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + const char *nameservers[2][7] = { + [0] = + { + "11:22::b:0", + "11:22::b:1#hello1", + "11:22::b:2", + "11:22::b:3#hello2", + "11:22::b:4", + "11:22::b:5", + "bogus6", + }, + [1] = + { + "1.1.1.0", + "1.1.1.1#foo1", + "1.1.1.2", + "1.1.1.3#foo2", + "1.1.1.4", + "1.1.1.5", + "bogus4", + }, + }; + GType gtype = IS_IPv4 ? NM_TYPE_SETTING_IP4_CONFIG : NM_TYPE_SETTING_IP6_CONFIG; + NMSettingIPConfig *s_ip1 = _nm_connection_get_setting(con1, gtype); + NMSettingIPConfig *s_ip2 = _nm_connection_get_setting(con2, gtype); + + n_dns = nmtst_get_rand_uint32() % G_N_ELEMENTS(nameservers[0]); + for (i = 0; i < n_dns; i++) { + const char *d = + nameservers[IS_IPv4][nmtst_get_rand_uint32() % G_N_ELEMENTS(nameservers[0])]; + + if (!nmtst_get_rand_one_case_in(4)) + nm_setting_ip_config_add_dns(s_ip1, d); + if (!nmtst_get_rand_one_case_in(4)) + nm_setting_ip_config_add_dns(s_ip2, d); + } + + if (nm_strv_ptrarray_cmp(_nm_setting_ip_config_get_dns_array(s_ip1), + _nm_setting_ip_config_get_dns_array(s_ip2)) + != 0) + same = FALSE; + } + + _nm_utils_is_manager_process = nmtst_get_rand_bool(); + if (same) { + nmtst_assert_connection_equals(con1, FALSE, con2, FALSE); + g_assert(nm_connection_compare(con1, con2, NM_SETTING_COMPARE_FLAG_EXACT)); + } else { + g_assert(!nm_connection_compare(con1, con2, NM_SETTING_COMPARE_FLAG_EXACT)); + } + _nm_utils_is_manager_process = FALSE; + } +} + +/*****************************************************************************/ + static void test_bond_meta(void) { @@ -5170,6 +5330,8 @@ main(int argc, char **argv) g_test_add_func("/libnm/settings/6lowpan/1", test_6lowpan_1); + g_test_add_func("/libnm/settings/dns", test_settings_dns); + g_test_add_func("/libnm/settings/sriov/vf", test_sriov_vf); g_test_add_func("/libnm/settings/sriov/vf-dup", test_sriov_vf_dup); g_test_add_func("/libnm/settings/sriov/vf-vlan", test_sriov_vf_vlan); @@ -5236,6 +5398,8 @@ main(int argc, char **argv) g_test_add_data_func("/libnm/settings/routing-rule/1", GINT_TO_POINTER(0), test_routing_rule); + g_test_add_func("/libnm/settings/ranges", test_ranges); + g_test_add_func("/libnm/parse-tc-handle", test_parse_tc_handle); g_test_add_func("/libnm/test_team_setting", test_team_setting); diff --git a/src/libnm-core-intern/nm-core-internal.h b/src/libnm-core-intern/nm-core-internal.h index 4e1bab47..9e9d55aa 100644 --- a/src/libnm-core-intern/nm-core-internal.h +++ b/src/libnm-core-intern/nm-core-internal.h @@ -43,6 +43,7 @@ #include "nm-setting-ip-tunnel.h" #include "nm-setting-ip4-config.h" #include "nm-setting-ip6-config.h" +#include "nm-setting-loopback.h" #include "nm-setting-macsec.h" #include "nm-setting-macvlan.h" #include "nm-setting-match.h" @@ -367,31 +368,6 @@ void _nm_dbus_errors_init(void); extern gboolean _nm_utils_is_manager_process; -gboolean -_nm_dbus_typecheck_response(GVariant *response, const GVariantType *reply_type, GError **error); - -gulong _nm_dbus_signal_connect_data(GDBusProxy *proxy, - const char *signal_name, - const GVariantType *signature, - GCallback c_handler, - gpointer data, - GClosureNotify destroy_data, - GConnectFlags connect_flags); -#define _nm_dbus_signal_connect(proxy, name, signature, handler, data) \ - _nm_dbus_signal_connect_data(proxy, name, signature, handler, data, NULL, (GConnectFlags) 0) - -GVariant *_nm_dbus_proxy_call_finish(GDBusProxy *proxy, - GAsyncResult *res, - const GVariantType *reply_type, - GError **error); - -GVariant *_nm_dbus_connection_call_finish(GDBusConnection *dbus_connection, - GAsyncResult *result, - const GVariantType *reply_type, - GError **error); - -gboolean _nm_dbus_error_has_name(GError *error, const char *dbus_error_name); - /*****************************************************************************/ char *_nm_utils_ssid_to_utf8(GBytes *ssid); @@ -419,6 +395,7 @@ GSList *_nm_vpn_plugin_info_list_load_dir(const char *dirname, /*****************************************************************************/ GHashTable *_nm_setting_ovs_external_ids_get_data(NMSettingOvsExternalIDs *self); +GHashTable *_nm_setting_ovs_other_config_get_data(NMSettingOvsOtherConfig *self); /*****************************************************************************/ @@ -481,6 +458,14 @@ NMSettingIPConfig *nm_connection_get_setting_ip_config(NMConnection *connection, /*****************************************************************************/ +struct _NMRefString; + +void _nm_connection_set_path_rstr(NMConnection *connection, struct _NMRefString *path); + +struct _NMRefString *_nm_connection_get_path_rstr(NMConnection *connection); + +/*****************************************************************************/ + typedef enum { NM_BOND_OPTION_TYPE_INT, NM_BOND_OPTION_TYPE_BOTH, @@ -582,6 +567,7 @@ gboolean _nm_utils_dhcp_duid_valid(const char *duid, GBytes **out_duid_bin); gboolean _nm_setting_sriov_sort_vfs(NMSettingSriov *setting); gboolean _nm_setting_bridge_port_sort_vlans(NMSettingBridgePort *setting); gboolean _nm_setting_bridge_sort_vlans(NMSettingBridge *setting); +gboolean _nm_setting_ovs_port_sort_trunks(NMSettingOvsPort *self); /*****************************************************************************/ @@ -818,6 +804,44 @@ struct _NMSettInfoProperty { * Set this flag to force always converting the property even if the value * is the default. */ bool to_dbus_including_default : 1; + + /* This indicates, that the property is deprecated (on D-Bus) for another property. + * See also _nm_setting_use_legacy_property() how that works. + * + * The to_dbus_fcn() will be skipped for such properties, if _nm_utils_is_manager_process + * is FALSE. */ + bool to_dbus_only_in_manager_process : 1; + + /* Whether the property is deprecated. + * + * Note that we have various representations of profiles, e.g. on D-Bus, keyfile, + * nmcli, libnm/NMSetting. Usually a property (in the general sense) is named and + * applies similarly to all those. But not always, for example, on D-Bus we + * have the field "ethernet.assigned-mac-address", but that exists nowhere + * as a property in the other parts (the real property is called + * "ethernet.cloned-mac-address"). + * + * This flag indicates whether a property is deprecated. Here "property" means + * no specific representation. When a property is deprecated this way, it will + * also indirectly apply to setting the property on D-Bus, keyfile, nmcli, etc. + * It means the general concept of this thing is no longer useful/recommended. + */ + bool is_deprecated : 1; + + /* Whether the property is deprecated in the D-Bus API. + * + * This has no real effect (for now). It is only self-documenting code that + * this property is discouraged on D-Bus. We might honor this when generating + * documentation, or we might use it to find properties that are deprecated. + * + * Note what this means. For example, "802-1x.phase2-subject-match" is deprecated + * as a property altogether, but that does not mean it's deprecated specifically on + * D-Bus. + * "ethernet.cloned-mac-address" is deprecated on D-Bus in favor of + * "ethernet.assigned-mac-address", but the property clone-mac-address itself + * is not deprecated. This flag is about the deprecation of the D-Bus representation + * of a property. */ + bool dbus_deprecated : 1; }; typedef struct { @@ -1043,4 +1067,8 @@ gboolean _nm_ip_tunnel_mode_is_layer2(NMIPTunnelMode mode); GPtrArray *_nm_setting_ip_config_get_dns_array(NMSettingIPConfig *setting); +gboolean nm_connection_need_secrets_for_rerequest(NMConnection *connection); + +const GPtrArray *_nm_setting_ovs_port_get_trunks_arr(NMSettingOvsPort *self); + #endif diff --git a/src/libnm-core-intern/nm-meta-setting-base-impl.h b/src/libnm-core-intern/nm-meta-setting-base-impl.h index 12688659..0c2def90 100644 --- a/src/libnm-core-intern/nm-meta-setting-base-impl.h +++ b/src/libnm-core-intern/nm-meta-setting-base-impl.h @@ -127,6 +127,7 @@ typedef enum _nm_packed { NM_META_SETTING_TYPE_IP_TUNNEL, NM_META_SETTING_TYPE_IP4_CONFIG, NM_META_SETTING_TYPE_IP6_CONFIG, + NM_META_SETTING_TYPE_LOOPBACK, NM_META_SETTING_TYPE_MACSEC, NM_META_SETTING_TYPE_MACVLAN, NM_META_SETTING_TYPE_MATCH, @@ -134,6 +135,7 @@ typedef enum _nm_packed { NM_META_SETTING_TYPE_OVS_DPDK, NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, NM_META_SETTING_TYPE_OVS_INTERFACE, + NM_META_SETTING_TYPE_OVS_OTHER_CONFIG, NM_META_SETTING_TYPE_OVS_PATCH, NM_META_SETTING_TYPE_OVS_PORT, NM_META_SETTING_TYPE_PPP, diff --git a/src/libnm-core-public/meson.build b/src/libnm-core-public/meson.build index f1da4d02..c5407130 100644 --- a/src/libnm-core-public/meson.build +++ b/src/libnm-core-public/meson.build @@ -29,6 +29,7 @@ libnm_core_headers = files( 'nm-setting-ip-tunnel.h', 'nm-setting-ip4-config.h', 'nm-setting-ip6-config.h', + 'nm-setting-loopback.h', 'nm-setting-macsec.h', 'nm-setting-macvlan.h', 'nm-setting-match.h', @@ -37,6 +38,7 @@ libnm_core_headers = files( 'nm-setting-ovs-dpdk.h', 'nm-setting-ovs-external-ids.h', 'nm-setting-ovs-interface.h', + 'nm-setting-ovs-other-config.h', 'nm-setting-ovs-patch.h', 'nm-setting-ovs-port.h', 'nm-setting-ppp.h', diff --git a/src/libnm-core-public/nm-connection.h b/src/libnm-core-public/nm-connection.h index 47004ce8..ff3e0f92 100644 --- a/src/libnm-core-public/nm-connection.h +++ b/src/libnm-core-public/nm-connection.h @@ -13,7 +13,6 @@ #include "nm-core-types.h" #include "nm-setting.h" -#include "nm-errors.h" G_BEGIN_DECLS diff --git a/src/libnm-core-public/nm-core-types.h b/src/libnm-core-public/nm-core-types.h index c86c28fa..f285a0f6 100644 --- a/src/libnm-core-public/nm-core-types.h +++ b/src/libnm-core-public/nm-core-types.h @@ -35,12 +35,14 @@ typedef struct _NMSettingIP6Config NMSettingIP6Config; typedef struct _NMSettingIPConfig NMSettingIPConfig; typedef struct _NMSettingIPTunnel NMSettingIPTunnel; typedef struct _NMSettingInfiniband NMSettingInfiniband; +typedef struct _NMSettingLoopback NMSettingLoopback; typedef struct _NMSettingMacsec NMSettingMacsec; typedef struct _NMSettingMacvlan NMSettingMacvlan; typedef struct _NMSettingMatch NMSettingMatch; typedef struct _NMSettingOlpcMesh NMSettingOlpcMesh; typedef struct _NMSettingOvsBridge NMSettingOvsBridge; typedef struct _NMSettingOvsDpdk NMSettingOvsDpdk; +typedef struct _NMSettingOvsOtherConfig NMSettingOvsOtherConfig; typedef struct _NMSettingOvsExternalIDs NMSettingOvsExternalIDs; typedef struct _NMSettingOvsInterface NMSettingOvsInterface; typedef struct _NMSettingOvsPatch NMSettingOvsPatch; diff --git a/src/libnm-core-public/nm-dbus-interface.h b/src/libnm-core-public/nm-dbus-interface.h index 3e79d4c6..9f64f75f 100644 --- a/src/libnm-core-public/nm-dbus-interface.h +++ b/src/libnm-core-public/nm-dbus-interface.h @@ -39,6 +39,7 @@ #define NM_DBUS_INTERFACE_DEVICE_GRE NM_DBUS_INTERFACE_DEVICE ".Gre" #define NM_DBUS_INTERFACE_DEVICE_INFINIBAND NM_DBUS_INTERFACE_DEVICE ".Infiniband" #define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL NM_DBUS_INTERFACE_DEVICE ".IPTunnel" +#define NM_DBUS_INTERFACE_DEVICE_LOOPBACK NM_DBUS_INTERFACE_DEVICE ".Loopback" #define NM_DBUS_INTERFACE_DEVICE_MACSEC NM_DBUS_INTERFACE_DEVICE ".Macsec" #define NM_DBUS_INTERFACE_DEVICE_MACVLAN NM_DBUS_INTERFACE_DEVICE ".Macvlan" #define NM_DBUS_INTERFACE_DEVICE_MODEM NM_DBUS_INTERFACE_DEVICE ".Modem" @@ -89,6 +90,20 @@ #define NM_DBUS_INTERFACE_DNS_MANAGER "org.freedesktop.NetworkManager.DnsManager" #define NM_DBUS_PATH_DNS_MANAGER "/org/freedesktop/NetworkManager/DnsManager" +/** + * NMVersionInfoCapability: + * %_NM_VERSION_INFO_CAPABILITY_UNUSED: a dummy capability. It has no meaning, + * don't use it. + * + * Currently no enum values are defined. These capabilities are exposed + * on D-Bus in the "VersionInfo" bit field. + * + * Since: 1.42 + */ +typedef enum { + _NM_VERSION_INFO_CAPABILITY_UNUSED = 0x7FFFFFFFu, +} NMVersionInfoCapability; + /** * NMCapability: * @NM_CAPABILITY_TEAM: Teams can be managed. This means the team device plugin @@ -218,6 +233,7 @@ typedef enum { * @NM_DEVICE_TYPE_WIREGUARD: a WireGuard interface * @NM_DEVICE_TYPE_WIFI_P2P: an 802.11 Wi-Fi P2P device. Since: 1.16. * @NM_DEVICE_TYPE_VRF: A VRF (Virtual Routing and Forwarding) interface. Since: 1.24. + * @NM_DEVICE_TYPE_LOOPBACK: a loopback interface. Since: 1.42. * * #NMDeviceType values indicate the type of hardware represented by a * device object. @@ -255,6 +271,7 @@ typedef enum { NM_DEVICE_TYPE_WIREGUARD = 29, NM_DEVICE_TYPE_WIFI_P2P = 30, NM_DEVICE_TYPE_VRF = 31, + NM_DEVICE_TYPE_LOOPBACK = 32, } NMDeviceType; /** @@ -1153,11 +1170,9 @@ typedef enum /*< flags >*/ { * Flags for the Reapply() D-Bus call of a device and * nm_device_reapply_async(). * - * Since: 1.42, 1.40.10 - * - * On 1.40.10+, no GFlags type is created. + * Since: 1.42 */ -typedef enum /*< skip >*/ { +typedef enum /*< flags >*/ { NM_DEVICE_REAPPLY_FLAGS_NONE = 0, NM_DEVICE_REAPPLY_FLAGS_PRESERVE_EXTERNAL_IP = 0x1, } NMDeviceReapplyFlags; diff --git a/src/libnm-core-public/nm-dbus-types.xml b/src/libnm-core-public/nm-dbus-types.xml index d8de9002..db5d8a8b 100644 --- a/src/libnm-core-public/nm-dbus-types.xml +++ b/src/libnm-core-public/nm-dbus-types.xml @@ -13,6 +13,26 @@ + + enum NMVersionInfoCapability + + NMVersionInfoCapability + + %_NM_VERSION_INFO_CAPABILITY_UNUSED: a dummy capability. It has no meaning, don't use it.Currently no enum values are defined. These capabilities are exposed on D-Bus in the "VersionInfo" bit field.Since: 1.42 + + Values + + + + + + + + + + + + enum NMCapability @@ -322,6 +342,11 @@ = 31 A VRF (Virtual Routing and Forwarding) interface. Since: 1.24. + + NM_DEVICE_TYPE_LOOPBACK + = 32 + a loopback interface. Since: 1.42. + @@ -1913,7 +1938,7 @@ NMDeviceReapplyFlags - Flags for the Reapply() D-Bus call of a device and nm_device_reapply_async().Since: 1.42, 1.40.10On 1.40.10+, no GFlags type is created. + Flags for the Reapply() D-Bus call of a device and nm_device_reapply_async().Since: 1.42 Values diff --git a/src/libnm-core-public/nm-setting-6lowpan.h b/src/libnm-core-public/nm-setting-6lowpan.h index ab2f7b44..41c35478 100644 --- a/src/libnm-core-public/nm-setting-6lowpan.h +++ b/src/libnm-core-public/nm-setting-6lowpan.h @@ -33,10 +33,10 @@ typedef struct _NMSetting6LowpanClass NMSetting6LowpanClass; NM_AVAILABLE_IN_1_14 GType nm_setting_6lowpan_get_type(void); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 NMSetting *nm_setting_6lowpan_new(void); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 const char *nm_setting_6lowpan_get_parent(NMSetting6Lowpan *setting); G_END_DECLS diff --git a/src/libnm-core-public/nm-setting-8021x.h b/src/libnm-core-public/nm-setting-8021x.h index 93c78fa3..d91850f7 100644 --- a/src/libnm-core-public/nm-setting-8021x.h +++ b/src/libnm-core-public/nm-setting-8021x.h @@ -63,8 +63,14 @@ typedef enum { /*< underscore_name=nm_setting_802_1x_ck_scheme >*/ * NMSetting8021xAuthFlags: * @NM_SETTING_802_1X_AUTH_FLAGS_NONE: No flags * @NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE: Disable TLSv1.0 + * @NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_ENABLE: Enable TLSv1.0. Since 1.42. * @NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE: Disable TLSv1.1 + * @NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_ENABLE: Enable TLSv1.1. Since 1.42. * @NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_DISABLE: Disable TLSv1.2 + * @NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_ENABLE: Enable TLSv1.2. Since 1.42. + * @NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_3_DISABLE: Disable TLSv1.3. Since 1.42. + * @NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_3_ENABLE: Enable TLSv1.3. Since 1.42. + * @NM_SETTING_802_1X_AUTH_FLAGS_TLS_DISABLE_TIME_CHECKS: Disable TLS time checks. Since 1.42. * @NM_SETTING_802_1X_AUTH_FLAGS_ALL: All supported flags * * #NMSetting8021xAuthFlags values indicate which authentication settings @@ -76,12 +82,18 @@ typedef enum { /*< underscore_name=nm_setting_802_1x_ck_scheme >*/ * Since: 1.8 */ typedef enum /*< underscore_name=nm_setting_802_1x_auth_flags, flags >*/ { - NM_SETTING_802_1X_AUTH_FLAGS_NONE = 0, - NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE = 0x1, - NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE = 0x2, - NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_DISABLE = 0x4, - - NM_SETTING_802_1X_AUTH_FLAGS_ALL = 0x7, + NM_SETTING_802_1X_AUTH_FLAGS_NONE = 0, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE = 0x1, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE = 0x2, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_DISABLE = 0x4, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_DISABLE_TIME_CHECKS = 0x8, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_3_DISABLE = 0x10, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_ENABLE = 0x20, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_ENABLE = 0x40, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_ENABLE = 0x80, + NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_3_ENABLE = 0x100, + + NM_SETTING_802_1X_AUTH_FLAGS_ALL = 0x1FF, } NMSetting8021xAuthFlags; #define NM_TYPE_SETTING_802_1X (nm_setting_802_1x_get_type()) diff --git a/src/libnm-core-public/nm-setting-bond.h b/src/libnm-core-public/nm-setting-bond.h index ed44abbf..10d703bc 100644 --- a/src/libnm-core-public/nm-setting-bond.h +++ b/src/libnm-core-public/nm-setting-bond.h @@ -37,6 +37,7 @@ G_BEGIN_DECLS #define NM_SETTING_BOND_OPTION_ARP_IP_TARGET "arp_ip_target" #define NM_SETTING_BOND_OPTION_ARP_VALIDATE "arp_validate" #define NM_SETTING_BOND_OPTION_PRIMARY "primary" +#define NM_SETTING_BOND_OPTION_BALANCE_SLB "balance-slb" #define NM_SETTING_BOND_OPTION_PRIMARY_RESELECT "primary_reselect" #define NM_SETTING_BOND_OPTION_FAIL_OVER_MAC "fail_over_mac" #define NM_SETTING_BOND_OPTION_USE_CARRIER "use_carrier" diff --git a/src/libnm-core-public/nm-setting-connection.h b/src/libnm-core-public/nm-setting-connection.h index 4f6653bf..875307bd 100644 --- a/src/libnm-core-public/nm-setting-connection.h +++ b/src/libnm-core-public/nm-setting-connection.h @@ -221,7 +221,7 @@ NMSettingConnectionLlmnr nm_setting_connection_get_llmnr(NMSettingConnection *se NM_AVAILABLE_IN_1_34 NMSettingConnectionDnsOverTls nm_setting_connection_get_dns_over_tls(NMSettingConnection *setting); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 NMMptcpFlags nm_setting_connection_get_mptcp_flags(NMSettingConnection *setting); NM_AVAILABLE_IN_1_20 diff --git a/src/libnm-core-public/nm-setting-hostname.h b/src/libnm-core-public/nm-setting-hostname.h index 3fb71197..99b221ca 100644 --- a/src/libnm-core-public/nm-setting-hostname.h +++ b/src/libnm-core-public/nm-setting-hostname.h @@ -36,7 +36,7 @@ typedef struct _NMSettingHostnameClass NMSettingHostnameClass; NM_AVAILABLE_IN_1_30 GType nm_setting_hostname_get_type(void); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 NMSetting *nm_setting_hostname_new(void); NM_AVAILABLE_IN_1_30 diff --git a/src/libnm-core-public/nm-setting-ip-config.h b/src/libnm-core-public/nm-setting-ip-config.h index acbdec0f..d52cf147 100644 --- a/src/libnm-core-public/nm-setting-ip-config.h +++ b/src/libnm-core-public/nm-setting-ip-config.h @@ -144,6 +144,7 @@ gboolean nm_ip_route_attribute_validate(const char *name, #define NM_IP_ROUTE_ATTRIBUTE_TOS "tos" #define NM_IP_ROUTE_ATTRIBUTE_TYPE "type" #define NM_IP_ROUTE_ATTRIBUTE_WINDOW "window" +#define NM_IP_ROUTE_ATTRIBUTE_WEIGHT "weight" /*****************************************************************************/ @@ -338,6 +339,7 @@ char *nm_ip_routing_rule_to_string(const NMIPRoutingRule *self, #define NM_SETTING_IP_CONFIG_REQUIRED_TIMEOUT "required-timeout" #define NM_SETTING_IP_CONFIG_DHCP_IAID "dhcp-iaid" #define NM_SETTING_IP_CONFIG_DHCP_REJECT_SERVERS "dhcp-reject-servers" +#define NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW "auto-route-ext-gw" /* these are not real GObject properties. */ #define NM_SETTING_IP_CONFIG_ROUTING_RULES "routing-rules" @@ -480,7 +482,7 @@ NM_AVAILABLE_IN_1_2 int nm_setting_ip_config_get_dhcp_timeout(NMSettingIPConfig *setting); NM_AVAILABLE_IN_1_34 int nm_setting_ip_config_get_required_timeout(NMSettingIPConfig *setting); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 const char *nm_setting_ip_config_get_dhcp_iaid(NMSettingIPConfig *setting); NM_AVAILABLE_IN_1_22 @@ -495,6 +497,8 @@ NM_AVAILABLE_IN_1_28 void nm_setting_ip_config_remove_dhcp_reject_server(NMSettingIPConfig *setting, guint idx); NM_AVAILABLE_IN_1_28 void nm_setting_ip_config_clear_dhcp_reject_servers(NMSettingIPConfig *setting); +NM_AVAILABLE_IN_1_42 +NMTernary nm_setting_ip_config_get_auto_route_ext_gw(NMSettingIPConfig *setting); G_END_DECLS diff --git a/src/libnm-core-public/nm-setting-ip-tunnel.h b/src/libnm-core-public/nm-setting-ip-tunnel.h index 62472f11..bcb3eab6 100644 --- a/src/libnm-core-public/nm-setting-ip-tunnel.h +++ b/src/libnm-core-public/nm-setting-ip-tunnel.h @@ -38,6 +38,7 @@ G_BEGIN_DECLS #define NM_SETTING_IP_TUNNEL_OUTPUT_KEY "output-key" #define NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT "encapsulation-limit" #define NM_SETTING_IP_TUNNEL_FLOW_LABEL "flow-label" +#define NM_SETTING_IP_TUNNEL_FWMARK "fwmark" #define NM_SETTING_IP_TUNNEL_MTU "mtu" #define NM_SETTING_IP_TUNNEL_FLAGS "flags" @@ -94,10 +95,12 @@ NM_AVAILABLE_IN_1_2 const char *nm_setting_ip_tunnel_get_input_key(NMSettingIPTunnel *setting); NM_AVAILABLE_IN_1_2 const char *nm_setting_ip_tunnel_get_output_key(NMSettingIPTunnel *setting); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 guint nm_setting_ip_tunnel_get_encapsulation_limit(NMSettingIPTunnel *setting); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 guint nm_setting_ip_tunnel_get_flow_label(NMSettingIPTunnel *setting); +NM_AVAILABLE_IN_1_42 +guint32 nm_setting_ip_tunnel_get_fwmark(NMSettingIPTunnel *setting); NM_AVAILABLE_IN_1_2 guint nm_setting_ip_tunnel_get_mtu(NMSettingIPTunnel *setting); NM_AVAILABLE_IN_1_12 diff --git a/src/libnm-core-public/nm-setting-ip4-config.h b/src/libnm-core-public/nm-setting-ip4-config.h index 66c457b9..323fe14d 100644 --- a/src/libnm-core-public/nm-setting-ip4-config.h +++ b/src/libnm-core-public/nm-setting-ip4-config.h @@ -113,7 +113,7 @@ const char *nm_setting_ip4_config_get_dhcp_fqdn(NMSettingIP4Config *setting); NM_AVAILABLE_IN_1_28 const char *nm_setting_ip4_config_get_dhcp_vendor_class_identifier(NMSettingIP4Config *setting); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 NMSettingIP4LinkLocal nm_setting_ip4_config_get_link_local(NMSettingIP4Config *setting); G_END_DECLS diff --git a/src/libnm-core-public/nm-setting-loopback.h b/src/libnm-core-public/nm-setting-loopback.h new file mode 100644 index 00000000..96b9c6d8 --- /dev/null +++ b/src/libnm-core-public/nm-setting-loopback.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022 Red Hat, Inc. + */ + +#ifndef __NM_SETTING_LOOPBACK_H__ +#define __NM_SETTING_LOOPBACK_H__ + +#if !defined(__NETWORKMANAGER_H_INSIDE__) && !defined(NETWORKMANAGER_COMPILATION) +#error "Only can be included directly." +#endif + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_LOOPBACK (nm_setting_loopback_get_type()) +#define NM_SETTING_LOOPBACK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTING_LOOPBACK, NMSettingLoopback)) +#define NM_SETTING_LOOPBACK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SETTING_LOOPBACKCONFIG, NMSettingLoopbackClass)) +#define NM_IS_SETTING_LOOPBACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SETTING_LOOPBACK)) +#define NM_IS_SETTING_LOOPBACK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_SETTING_LOOPBACK)) +#define NM_SETTING_LOOPBACK_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_SETTING_LOOPBACK, NMSettingLoopbackClass)) + +#define NM_SETTING_LOOPBACK_SETTING_NAME "loopback" + +#define NM_SETTING_LOOPBACK_MTU "mtu" + +typedef struct _NMSettingLoopbackClass NMSettingLoopbackClass; + +NM_AVAILABLE_IN_1_42 +GType nm_setting_loopback_get_type(void); +NM_AVAILABLE_IN_1_42 +NMSetting *nm_setting_loopback_new(void); + +NM_AVAILABLE_IN_1_42 +guint32 nm_setting_loopback_get_mtu(NMSettingLoopback *setting); + +G_END_DECLS + +#endif /* __NM_SETTING_LOOPBACK_H__ */ diff --git a/src/libnm-core-public/nm-setting-ovs-bridge.h b/src/libnm-core-public/nm-setting-ovs-bridge.h index bc7d7e9b..0f1a8724 100644 --- a/src/libnm-core-public/nm-setting-ovs-bridge.h +++ b/src/libnm-core-public/nm-setting-ovs-bridge.h @@ -49,7 +49,7 @@ NM_AVAILABLE_IN_1_10 gboolean nm_setting_ovs_bridge_get_rstp_enable(NMSettingOvsBridge *self); NM_AVAILABLE_IN_1_10 gboolean nm_setting_ovs_bridge_get_stp_enable(NMSettingOvsBridge *self); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 const char *nm_setting_ovs_bridge_get_datapath_type(NMSettingOvsBridge *self); G_END_DECLS diff --git a/src/libnm-core-public/nm-setting-ovs-dpdk.h b/src/libnm-core-public/nm-setting-ovs-dpdk.h index 2e2d11b9..0578ab2c 100644 --- a/src/libnm-core-public/nm-setting-ovs-dpdk.h +++ b/src/libnm-core-public/nm-setting-ovs-dpdk.h @@ -27,8 +27,10 @@ G_BEGIN_DECLS #define NM_SETTING_OVS_DPDK_SETTING_NAME "ovs-dpdk" -#define NM_SETTING_OVS_DPDK_DEVARGS "devargs" -#define NM_SETTING_OVS_DPDK_N_RXQ "n-rxq" +#define NM_SETTING_OVS_DPDK_DEVARGS "devargs" +#define NM_SETTING_OVS_DPDK_N_RXQ "n-rxq" +#define NM_SETTING_OVS_DPDK_N_RXQ_DESC "n-rxq-desc" +#define NM_SETTING_OVS_DPDK_N_TXQ_DESC "n-txq-desc" typedef struct _NMSettingOvsDpdkClass NMSettingOvsDpdkClass; @@ -41,6 +43,10 @@ NM_AVAILABLE_IN_1_20 const char *nm_setting_ovs_dpdk_get_devargs(NMSettingOvsDpdk *self); NM_AVAILABLE_IN_1_36 guint32 nm_setting_ovs_dpdk_get_n_rxq(NMSettingOvsDpdk *self); +NM_AVAILABLE_IN_1_42 +guint32 nm_setting_ovs_dpdk_get_n_rxq_desc(NMSettingOvsDpdk *self); +NM_AVAILABLE_IN_1_42 +guint32 nm_setting_ovs_dpdk_get_n_txq_desc(NMSettingOvsDpdk *self); G_END_DECLS diff --git a/src/libnm-core-public/nm-setting-ovs-interface.h b/src/libnm-core-public/nm-setting-ovs-interface.h index a36a1a53..8c6beec2 100644 --- a/src/libnm-core-public/nm-setting-ovs-interface.h +++ b/src/libnm-core-public/nm-setting-ovs-interface.h @@ -32,6 +32,8 @@ G_BEGIN_DECLS #define NM_SETTING_OVS_INTERFACE_TYPE "type" +#define NM_SETTING_OVS_INTERFACE_OFPORT_REQUEST "ofport-request" + typedef struct _NMSettingOvsInterfaceClass NMSettingOvsInterfaceClass; NM_AVAILABLE_IN_1_10 @@ -42,6 +44,9 @@ NMSetting *nm_setting_ovs_interface_new(void); NM_AVAILABLE_IN_1_10 const char *nm_setting_ovs_interface_get_interface_type(NMSettingOvsInterface *self); +NM_AVAILABLE_IN_1_42 +guint32 nm_setting_ovs_interface_get_ofport_request(NMSettingOvsInterface *self); + G_END_DECLS #endif /* __NM_SETTING_OVS_INTERFACE_H__ */ diff --git a/src/libnm-core-public/nm-setting-ovs-other-config.h b/src/libnm-core-public/nm-setting-ovs-other-config.h new file mode 100644 index 00000000..35de8ab2 --- /dev/null +++ b/src/libnm-core-public/nm-setting-ovs-other-config.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 - 2020, 2022 Red Hat, Inc. + */ + +#ifndef __NM_SETTING_OVS_OTHER_CONFIG_H__ +#define __NM_SETTING_OVS_OTHER_CONFIG_H__ + +#if !defined(__NETWORKMANAGER_H_INSIDE__) && !defined(NETWORKMANAGER_COMPILATION) +#error "Only can be included directly." +#endif + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_OVS_OTHER_CONFIG (nm_setting_ovs_other_config_get_type()) +#define NM_SETTING_OVS_OTHER_CONFIG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTING_OVS_OTHER_CONFIG, NMSettingOvsOtherConfig)) +#define NM_SETTING_OVS_OTHER_CONFIG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + NM_TYPE_SETTING_OVS_OTHER_CONFIG, \ + NMSettingOvsOtherConfigClass)) +#define NM_IS_SETTING_OVS_OTHER_CONFIG(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SETTING_OVS_OTHER_CONFIG)) +#define NM_IS_SETTING_OVS_OTHER_CONFIG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_SETTING_OVS_OTHER_CONFIG)) +#define NM_SETTING_OVS_OTHER_CONFIG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), \ + NM_TYPE_SETTING_OVS_OTHER_CONFIG, \ + NMSettingOvsOtherConfigClass)) + +#define NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME "ovs-other-config" + +#define NM_SETTING_OVS_OTHER_CONFIG_DATA "data" + +typedef struct _NMSettingOvsOtherConfigClass NMSettingOvsOtherConfigClass; + +NM_AVAILABLE_IN_1_42 +GType nm_setting_ovs_other_config_get_type(void); + +NM_AVAILABLE_IN_1_42 +NMSetting *nm_setting_ovs_other_config_new(void); + +/*****************************************************************************/ + +NM_AVAILABLE_IN_1_42 +const char *const *nm_setting_ovs_other_config_get_data_keys(NMSettingOvsOtherConfig *setting, + guint *out_len); + +NM_AVAILABLE_IN_1_42 +const char *nm_setting_ovs_other_config_get_data(NMSettingOvsOtherConfig *setting, const char *key); + +NM_AVAILABLE_IN_1_42 +void nm_setting_ovs_other_config_set_data(NMSettingOvsOtherConfig *setting, + const char *key, + const char *val); + +/*****************************************************************************/ + +G_END_DECLS + +#endif /* __NM_SETTING_OVS_OTHER_CONFIG_H__ */ diff --git a/src/libnm-core-public/nm-setting-ovs-port.h b/src/libnm-core-public/nm-setting-ovs-port.h index eda23d3e..4a452c0c 100644 --- a/src/libnm-core-public/nm-setting-ovs-port.h +++ b/src/libnm-core-public/nm-setting-ovs-port.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS #define NM_SETTING_OVS_PORT_VLAN_MODE "vlan-mode" #define NM_SETTING_OVS_PORT_TAG "tag" +#define NM_SETTING_OVS_PORT_TRUNKS "trunks" #define NM_SETTING_OVS_PORT_LACP "lacp" #define NM_SETTING_OVS_PORT_BOND_MODE "bond-mode" #define NM_SETTING_OVS_PORT_BOND_UPDELAY "bond-updelay" @@ -54,6 +55,20 @@ guint nm_setting_ovs_port_get_bond_updelay(NMSettingOvsPort *self); NM_AVAILABLE_IN_1_10 guint nm_setting_ovs_port_get_bond_downdelay(NMSettingOvsPort *self); +NM_AVAILABLE_IN_1_42 +void nm_setting_ovs_port_add_trunk(NMSettingOvsPort *setting, NMRange *trunk); +NM_AVAILABLE_IN_1_42 +guint nm_setting_ovs_port_get_num_trunks(NMSettingOvsPort *setting); +NM_AVAILABLE_IN_1_42 +NMRange *nm_setting_ovs_port_get_trunk(NMSettingOvsPort *setting, guint idx); +NM_AVAILABLE_IN_1_42 +void nm_setting_ovs_port_remove_trunk(NMSettingOvsPort *setting, guint idx); +NM_AVAILABLE_IN_1_42 +gboolean +nm_setting_ovs_port_remove_trunk_by_value(NMSettingOvsPort *setting, guint start, guint end); +NM_AVAILABLE_IN_1_42 +void nm_setting_ovs_port_clear_trunks(NMSettingOvsPort *setting); + G_END_DECLS #endif /* __NM_SETTING_OVS_PORT_H__ */ diff --git a/src/libnm-core-public/nm-setting-sriov.h b/src/libnm-core-public/nm-setting-sriov.h index 0438e800..071b9837 100644 --- a/src/libnm-core-public/nm-setting-sriov.h +++ b/src/libnm-core-public/nm-setting-sriov.h @@ -109,7 +109,7 @@ NM_AVAILABLE_IN_1_14 const char **nm_sriov_vf_get_attribute_names(const NMSriovVF *vf); NM_AVAILABLE_IN_1_14 GVariant *nm_sriov_vf_get_attribute(const NMSriovVF *vf, const char *name); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 gboolean nm_sriov_vf_attribute_validate(const char *name, GVariant *value, gboolean *known, GError **error); diff --git a/src/libnm-core-public/nm-setting-tc-config.h b/src/libnm-core-public/nm-setting-tc-config.h index ab1a9909..54871a4a 100644 --- a/src/libnm-core-public/nm-setting-tc-config.h +++ b/src/libnm-core-public/nm-setting-tc-config.h @@ -102,9 +102,9 @@ NM_AVAILABLE_IN_1_12 void nm_tc_tfilter_set_handle(NMTCTfilter *tfilter, guint32 handle); NM_AVAILABLE_IN_1_12 guint32 nm_tc_tfilter_get_parent(NMTCTfilter *tfilter); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 NMTCAction *nm_tc_tfilter_get_action(NMTCTfilter *tfilter); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 void nm_tc_tfilter_set_action(NMTCTfilter *tfilter, NMTCAction *action); #define NM_TYPE_SETTING_TC_CONFIG (nm_setting_tc_config_get_type()) diff --git a/src/libnm-core-public/nm-setting-vlan.h b/src/libnm-core-public/nm-setting-vlan.h index a99d7256..7fa79e35 100644 --- a/src/libnm-core-public/nm-setting-vlan.h +++ b/src/libnm-core-public/nm-setting-vlan.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS #define NM_SETTING_VLAN_PARENT "parent" #define NM_SETTING_VLAN_ID "id" #define NM_SETTING_VLAN_FLAGS "flags" +#define NM_SETTING_VLAN_PROTOCOL "protocol" #define NM_SETTING_VLAN_INGRESS_PRIORITY_MAP "ingress-priority-map" #define NM_SETTING_VLAN_EGRESS_PRIORITY_MAP "egress-priority-map" @@ -84,6 +85,8 @@ NMSetting *nm_setting_vlan_new(void); const char *nm_setting_vlan_get_parent(NMSettingVlan *setting); guint32 nm_setting_vlan_get_id(NMSettingVlan *setting); guint32 nm_setting_vlan_get_flags(NMSettingVlan *setting); +NM_AVAILABLE_IN_1_42 +const char *nm_setting_vlan_get_protocol(NMSettingVlan *setting); gint32 nm_setting_vlan_get_num_priorities(NMSettingVlan *setting, NMVlanPriorityMap map); diff --git a/src/libnm-core-public/nm-setting-vpn.h b/src/libnm-core-public/nm-setting-vpn.h index a00fa975..6f55f87f 100644 --- a/src/libnm-core-public/nm-setting-vpn.h +++ b/src/libnm-core-public/nm-setting-vpn.h @@ -50,7 +50,7 @@ NMSetting *nm_setting_vpn_new(void); const char *nm_setting_vpn_get_service_type(NMSettingVpn *setting); const char *nm_setting_vpn_get_user_name(NMSettingVpn *setting); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 gboolean nm_setting_vpn_get_persistent(NMSettingVpn *setting); guint32 nm_setting_vpn_get_num_data_items(NMSettingVpn *setting); diff --git a/src/libnm-core-public/nm-setting-wpan.h b/src/libnm-core-public/nm-setting-wpan.h index 934ab196..fb552c34 100644 --- a/src/libnm-core-public/nm-setting-wpan.h +++ b/src/libnm-core-public/nm-setting-wpan.h @@ -38,18 +38,18 @@ typedef struct _NMSettingWpanClass NMSettingWpanClass; NM_AVAILABLE_IN_1_14 GType nm_setting_wpan_get_type(void); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 NMSetting *nm_setting_wpan_new(void); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 const char *nm_setting_wpan_get_mac_address(NMSettingWpan *setting); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 guint16 nm_setting_wpan_get_pan_id(NMSettingWpan *setting); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 guint16 nm_setting_wpan_get_short_address(NMSettingWpan *setting); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 gint16 nm_setting_wpan_get_page(NMSettingWpan *setting); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 gint16 nm_setting_wpan_get_channel(NMSettingWpan *setting); G_END_DECLS diff --git a/src/libnm-core-public/nm-setting.h b/src/libnm-core-public/nm-setting.h index ae898458..737cf883 100644 --- a/src/libnm-core-public/nm-setting.h +++ b/src/libnm-core-public/nm-setting.h @@ -260,6 +260,25 @@ const GVariantType *nm_setting_get_dbus_property_type(NMSetting *setting, /*****************************************************************************/ +typedef struct _NMRange NMRange; + +NM_AVAILABLE_IN_1_42 +GType nm_range_get_type(void); +NM_AVAILABLE_IN_1_42 +NMRange *nm_range_new(guint64 start, guint64 end); +NM_AVAILABLE_IN_1_42 +NMRange *nm_range_ref(const NMRange *range); +NM_AVAILABLE_IN_1_42 +void nm_range_unref(const NMRange *range); +NM_AVAILABLE_IN_1_42 +int nm_range_cmp(const NMRange *a, const NMRange *b); +NM_AVAILABLE_IN_1_42 +gboolean nm_range_get_range(const NMRange *range, guint64 *start, guint64 *end); +NM_AVAILABLE_IN_1_42 +char *nm_range_to_str(const NMRange *range); +NM_AVAILABLE_IN_1_42 +NMRange *nm_range_from_str(const char *str, GError **error); + G_END_DECLS #endif /* __NM_SETTING_H__ */ diff --git a/src/libnm-core-public/nm-utils.h b/src/libnm-core-public/nm-utils.h index dc89f234..ca399c4c 100644 --- a/src/libnm-core-public/nm-utils.h +++ b/src/libnm-core-public/nm-utils.h @@ -104,13 +104,13 @@ GPtrArray *nm_utils_ip6_addresses_from_variant(GVariant *value, char **out_gatew GVariant *nm_utils_ip6_routes_to_variant(GPtrArray *routes); GPtrArray *nm_utils_ip6_routes_from_variant(GVariant *value); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 GVariant *nm_utils_ip_addresses_to_variant(GPtrArray *addresses); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 GPtrArray *nm_utils_ip_addresses_from_variant(GVariant *value, int family); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 GVariant *nm_utils_ip_routes_to_variant(GPtrArray *routes); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 GPtrArray *nm_utils_ip_routes_from_variant(GVariant *value, int family); char *nm_utils_uuid_generate(void); @@ -172,6 +172,7 @@ gboolean nm_utils_iface_valid_name(const char *name); NM_AVAILABLE_IN_1_6 gboolean nm_utils_is_valid_iface_name(const char *name, GError **error); +NM_DEPRECATED_IN_1_32 gboolean nm_utils_is_uuid(const char *str); /** @@ -254,6 +255,9 @@ NM_AVAILABLE_IN_1_16 gboolean nm_utils_base64secret_decode(const char *base64_key, gsize required_key_len, guint8 *out_key); +NM_AVAILABLE_IN_1_42 +void nm_utils_ensure_gtypes(void); + G_END_DECLS #endif /* __NM_UTILS_H__ */ diff --git a/src/libnm-core-public/nm-version-macros.h b/src/libnm-core-public/nm-version-macros.h index f3d50252..bbdeb2f3 100644 --- a/src/libnm-core-public/nm-version-macros.h +++ b/src/libnm-core-public/nm-version-macros.h @@ -22,7 +22,7 @@ * Evaluates to the minor version number of NetworkManager which this source * is compiled against. */ -#define NM_MINOR_VERSION (40) +#define NM_MINOR_VERSION (42) /** * NM_MICRO_VERSION: @@ -30,7 +30,7 @@ * Evaluates to the micro version number of NetworkManager which this source * compiled against. */ -#define NM_MICRO_VERSION (12) +#define NM_MICRO_VERSION (0) /** * NM_CHECK_VERSION: @@ -41,38 +41,37 @@ * Returns: %TRUE if the version of the NetworkManager header files * is the same as or newer than the passed-in version. */ -#define NM_CHECK_VERSION(major,minor,micro) \ - (NM_MAJOR_VERSION > (major) || \ - (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION > (minor)) || \ +#define NM_CHECK_VERSION(major, minor, micro) \ + (NM_MAJOR_VERSION > (major) || \ + (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION > (minor)) || \ (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION == (minor) && NM_MICRO_VERSION >= (micro))) +#define NM_ENCODE_VERSION(major, minor, micro) ((major) << 16 | (minor) << 8 | (micro)) -#define NM_ENCODE_VERSION(major,minor,micro) ((major) << 16 | (minor) << 8 | (micro)) - -#define NM_VERSION_0_9_8 (NM_ENCODE_VERSION (0, 9, 8)) -#define NM_VERSION_0_9_10 (NM_ENCODE_VERSION (0, 9, 10)) -#define NM_VERSION_1_0 (NM_ENCODE_VERSION (1, 0, 0)) -#define NM_VERSION_1_2 (NM_ENCODE_VERSION (1, 2, 0)) -#define NM_VERSION_1_4 (NM_ENCODE_VERSION (1, 4, 0)) -#define NM_VERSION_1_6 (NM_ENCODE_VERSION (1, 6, 0)) -#define NM_VERSION_1_8 (NM_ENCODE_VERSION (1, 8, 0)) -#define NM_VERSION_1_10 (NM_ENCODE_VERSION (1, 10, 0)) -#define NM_VERSION_1_12 (NM_ENCODE_VERSION (1, 12, 0)) -#define NM_VERSION_1_14 (NM_ENCODE_VERSION (1, 14, 0)) -#define NM_VERSION_1_16 (NM_ENCODE_VERSION (1, 16, 0)) -#define NM_VERSION_1_18 (NM_ENCODE_VERSION (1, 18, 0)) -#define NM_VERSION_1_20 (NM_ENCODE_VERSION (1, 20, 0)) -#define NM_VERSION_1_22 (NM_ENCODE_VERSION (1, 22, 0)) -#define NM_VERSION_1_24 (NM_ENCODE_VERSION (1, 24, 0)) -#define NM_VERSION_1_26 (NM_ENCODE_VERSION (1, 26, 0)) -#define NM_VERSION_1_28 (NM_ENCODE_VERSION (1, 28, 0)) -#define NM_VERSION_1_30 (NM_ENCODE_VERSION (1, 30, 0)) -#define NM_VERSION_1_32 (NM_ENCODE_VERSION (1, 32, 0)) -#define NM_VERSION_1_34 (NM_ENCODE_VERSION (1, 34, 0)) -#define NM_VERSION_1_36 (NM_ENCODE_VERSION (1, 36, 0)) -#define NM_VERSION_1_38 (NM_ENCODE_VERSION (1, 38, 0)) -#define NM_VERSION_1_40 (NM_ENCODE_VERSION (1, 40, 0)) -#define NM_VERSION_1_40_4 (NM_ENCODE_VERSION (1, 40, 4)) +#define NM_VERSION_0_9_8 (NM_ENCODE_VERSION(0, 9, 8)) +#define NM_VERSION_0_9_10 (NM_ENCODE_VERSION(0, 9, 10)) +#define NM_VERSION_1_0 (NM_ENCODE_VERSION(1, 0, 0)) +#define NM_VERSION_1_2 (NM_ENCODE_VERSION(1, 2, 0)) +#define NM_VERSION_1_4 (NM_ENCODE_VERSION(1, 4, 0)) +#define NM_VERSION_1_6 (NM_ENCODE_VERSION(1, 6, 0)) +#define NM_VERSION_1_8 (NM_ENCODE_VERSION(1, 8, 0)) +#define NM_VERSION_1_10 (NM_ENCODE_VERSION(1, 10, 0)) +#define NM_VERSION_1_12 (NM_ENCODE_VERSION(1, 12, 0)) +#define NM_VERSION_1_14 (NM_ENCODE_VERSION(1, 14, 0)) +#define NM_VERSION_1_16 (NM_ENCODE_VERSION(1, 16, 0)) +#define NM_VERSION_1_18 (NM_ENCODE_VERSION(1, 18, 0)) +#define NM_VERSION_1_20 (NM_ENCODE_VERSION(1, 20, 0)) +#define NM_VERSION_1_22 (NM_ENCODE_VERSION(1, 22, 0)) +#define NM_VERSION_1_24 (NM_ENCODE_VERSION(1, 24, 0)) +#define NM_VERSION_1_26 (NM_ENCODE_VERSION(1, 26, 0)) +#define NM_VERSION_1_28 (NM_ENCODE_VERSION(1, 28, 0)) +#define NM_VERSION_1_30 (NM_ENCODE_VERSION(1, 30, 0)) +#define NM_VERSION_1_32 (NM_ENCODE_VERSION(1, 32, 0)) +#define NM_VERSION_1_34 (NM_ENCODE_VERSION(1, 34, 0)) +#define NM_VERSION_1_36 (NM_ENCODE_VERSION(1, 36, 0)) +#define NM_VERSION_1_38 (NM_ENCODE_VERSION(1, 38, 0)) +#define NM_VERSION_1_40 (NM_ENCODE_VERSION(1, 40, 0)) +#define NM_VERSION_1_42 (NM_ENCODE_VERSION(1, 42, 0)) /* For releases, NM_API_VERSION is equal to NM_VERSION. * @@ -81,17 +80,17 @@ * version, you are already using the future API, even if * it is not yet released. Hence, the currently used API * version is the future one. */ -#define NM_API_VERSION \ - (((NM_MINOR_VERSION % 2) == 1) \ - ? NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION + 1, 0 ) \ +#define NM_API_VERSION \ + (((NM_MINOR_VERSION % 2) == 1) \ + ? NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION + 1, 0 ) \ : NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION , ((NM_MICRO_VERSION + 1) / 2) * 2)) /* deprecated. */ -#define NM_VERSION_CUR_STABLE NM_API_VERSION +#define NM_VERSION_CUR_STABLE NM_API_VERSION /* deprecated. */ #define NM_VERSION_NEXT_STABLE NM_API_VERSION -#define NM_VERSION NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION, NM_MICRO_VERSION) +#define NM_VERSION NM_ENCODE_VERSION(NM_MAJOR_VERSION, NM_MINOR_VERSION, NM_MICRO_VERSION) -#endif /* __NM_VERSION_MACROS_H__ */ +#endif /* __NM_VERSION_MACROS_H__ */ diff --git a/src/libnm-core-public/nm-version-macros.h.in b/src/libnm-core-public/nm-version-macros.h.in index fc854aef..543edcf0 100644 --- a/src/libnm-core-public/nm-version-macros.h.in +++ b/src/libnm-core-public/nm-version-macros.h.in @@ -41,38 +41,37 @@ * Returns: %TRUE if the version of the NetworkManager header files * is the same as or newer than the passed-in version. */ -#define NM_CHECK_VERSION(major,minor,micro) \ - (NM_MAJOR_VERSION > (major) || \ - (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION > (minor)) || \ +#define NM_CHECK_VERSION(major, minor, micro) \ + (NM_MAJOR_VERSION > (major) || \ + (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION > (minor)) || \ (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION == (minor) && NM_MICRO_VERSION >= (micro))) +#define NM_ENCODE_VERSION(major, minor, micro) ((major) << 16 | (minor) << 8 | (micro)) -#define NM_ENCODE_VERSION(major,minor,micro) ((major) << 16 | (minor) << 8 | (micro)) - -#define NM_VERSION_0_9_8 (NM_ENCODE_VERSION (0, 9, 8)) -#define NM_VERSION_0_9_10 (NM_ENCODE_VERSION (0, 9, 10)) -#define NM_VERSION_1_0 (NM_ENCODE_VERSION (1, 0, 0)) -#define NM_VERSION_1_2 (NM_ENCODE_VERSION (1, 2, 0)) -#define NM_VERSION_1_4 (NM_ENCODE_VERSION (1, 4, 0)) -#define NM_VERSION_1_6 (NM_ENCODE_VERSION (1, 6, 0)) -#define NM_VERSION_1_8 (NM_ENCODE_VERSION (1, 8, 0)) -#define NM_VERSION_1_10 (NM_ENCODE_VERSION (1, 10, 0)) -#define NM_VERSION_1_12 (NM_ENCODE_VERSION (1, 12, 0)) -#define NM_VERSION_1_14 (NM_ENCODE_VERSION (1, 14, 0)) -#define NM_VERSION_1_16 (NM_ENCODE_VERSION (1, 16, 0)) -#define NM_VERSION_1_18 (NM_ENCODE_VERSION (1, 18, 0)) -#define NM_VERSION_1_20 (NM_ENCODE_VERSION (1, 20, 0)) -#define NM_VERSION_1_22 (NM_ENCODE_VERSION (1, 22, 0)) -#define NM_VERSION_1_24 (NM_ENCODE_VERSION (1, 24, 0)) -#define NM_VERSION_1_26 (NM_ENCODE_VERSION (1, 26, 0)) -#define NM_VERSION_1_28 (NM_ENCODE_VERSION (1, 28, 0)) -#define NM_VERSION_1_30 (NM_ENCODE_VERSION (1, 30, 0)) -#define NM_VERSION_1_32 (NM_ENCODE_VERSION (1, 32, 0)) -#define NM_VERSION_1_34 (NM_ENCODE_VERSION (1, 34, 0)) -#define NM_VERSION_1_36 (NM_ENCODE_VERSION (1, 36, 0)) -#define NM_VERSION_1_38 (NM_ENCODE_VERSION (1, 38, 0)) -#define NM_VERSION_1_40 (NM_ENCODE_VERSION (1, 40, 0)) -#define NM_VERSION_1_40_4 (NM_ENCODE_VERSION (1, 40, 4)) +#define NM_VERSION_0_9_8 (NM_ENCODE_VERSION(0, 9, 8)) +#define NM_VERSION_0_9_10 (NM_ENCODE_VERSION(0, 9, 10)) +#define NM_VERSION_1_0 (NM_ENCODE_VERSION(1, 0, 0)) +#define NM_VERSION_1_2 (NM_ENCODE_VERSION(1, 2, 0)) +#define NM_VERSION_1_4 (NM_ENCODE_VERSION(1, 4, 0)) +#define NM_VERSION_1_6 (NM_ENCODE_VERSION(1, 6, 0)) +#define NM_VERSION_1_8 (NM_ENCODE_VERSION(1, 8, 0)) +#define NM_VERSION_1_10 (NM_ENCODE_VERSION(1, 10, 0)) +#define NM_VERSION_1_12 (NM_ENCODE_VERSION(1, 12, 0)) +#define NM_VERSION_1_14 (NM_ENCODE_VERSION(1, 14, 0)) +#define NM_VERSION_1_16 (NM_ENCODE_VERSION(1, 16, 0)) +#define NM_VERSION_1_18 (NM_ENCODE_VERSION(1, 18, 0)) +#define NM_VERSION_1_20 (NM_ENCODE_VERSION(1, 20, 0)) +#define NM_VERSION_1_22 (NM_ENCODE_VERSION(1, 22, 0)) +#define NM_VERSION_1_24 (NM_ENCODE_VERSION(1, 24, 0)) +#define NM_VERSION_1_26 (NM_ENCODE_VERSION(1, 26, 0)) +#define NM_VERSION_1_28 (NM_ENCODE_VERSION(1, 28, 0)) +#define NM_VERSION_1_30 (NM_ENCODE_VERSION(1, 30, 0)) +#define NM_VERSION_1_32 (NM_ENCODE_VERSION(1, 32, 0)) +#define NM_VERSION_1_34 (NM_ENCODE_VERSION(1, 34, 0)) +#define NM_VERSION_1_36 (NM_ENCODE_VERSION(1, 36, 0)) +#define NM_VERSION_1_38 (NM_ENCODE_VERSION(1, 38, 0)) +#define NM_VERSION_1_40 (NM_ENCODE_VERSION(1, 40, 0)) +#define NM_VERSION_1_42 (NM_ENCODE_VERSION(1, 42, 0)) /* For releases, NM_API_VERSION is equal to NM_VERSION. * @@ -81,17 +80,17 @@ * version, you are already using the future API, even if * it is not yet released. Hence, the currently used API * version is the future one. */ -#define NM_API_VERSION \ - (((NM_MINOR_VERSION % 2) == 1) \ - ? NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION + 1, 0 ) \ +#define NM_API_VERSION \ + (((NM_MINOR_VERSION % 2) == 1) \ + ? NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION + 1, 0 ) \ : NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION , ((NM_MICRO_VERSION + 1) / 2) * 2)) /* deprecated. */ -#define NM_VERSION_CUR_STABLE NM_API_VERSION +#define NM_VERSION_CUR_STABLE NM_API_VERSION /* deprecated. */ #define NM_VERSION_NEXT_STABLE NM_API_VERSION -#define NM_VERSION NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION, NM_MICRO_VERSION) +#define NM_VERSION NM_ENCODE_VERSION(NM_MAJOR_VERSION, NM_MINOR_VERSION, NM_MICRO_VERSION) -#endif /* __NM_VERSION_MACROS_H__ */ +#endif /* __NM_VERSION_MACROS_H__ */ diff --git a/src/libnm-core-public/nm-version.h b/src/libnm-core-public/nm-version.h index d9f9a121..0b2e6103 100644 --- a/src/libnm-core-public/nm-version.h +++ b/src/libnm-core-public/nm-version.h @@ -341,10 +341,18 @@ #define NM_AVAILABLE_IN_1_40 #endif -#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_40_4 -#define NM_AVAILABLE_IN_1_40_4 G_UNAVAILABLE(1, 40.4) +#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_1_42 +#define NM_DEPRECATED_IN_1_42 G_DEPRECATED +#define NM_DEPRECATED_IN_1_42_FOR(f) G_DEPRECATED_FOR(f) #else -#define NM_AVAILABLE_IN_1_40_4 +#define NM_DEPRECATED_IN_1_42 +#define NM_DEPRECATED_IN_1_42_FOR(f) +#endif + +#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_42 +#define NM_AVAILABLE_IN_1_42 G_UNAVAILABLE(1, 42) +#else +#define NM_AVAILABLE_IN_1_42 #endif /* diff --git a/src/libnm-core-public/nm-vpn-plugin-info.h b/src/libnm-core-public/nm-vpn-plugin-info.h index 167af52c..c045daa0 100644 --- a/src/libnm-core-public/nm-vpn-plugin-info.h +++ b/src/libnm-core-public/nm-vpn-plugin-info.h @@ -62,7 +62,7 @@ NM_AVAILABLE_IN_1_4 const char *nm_vpn_plugin_info_get_auth_dialog(NMVpnPluginInfo *self); NM_AVAILABLE_IN_1_4 gboolean nm_vpn_plugin_info_supports_hints(NMVpnPluginInfo *self); -NM_AVAILABLE_IN_1_40_4 +NM_AVAILABLE_IN_1_42 gboolean nm_vpn_plugin_info_supports_multiple(NMVpnPluginInfo *self); NM_AVAILABLE_IN_1_4 const char *const *nm_vpn_plugin_info_get_aliases(NMVpnPluginInfo *self); diff --git a/src/libnm-crypto/nm-crypto-nss.c b/src/libnm-crypto/nm-crypto-nss.c index b31ca55e..6ac52b2c 100644 --- a/src/libnm-crypto/nm-crypto-nss.c +++ b/src/libnm-crypto/nm-crypto-nss.c @@ -450,10 +450,17 @@ _nm_crypto_verify_pkcs12(const guint8 *data, gsize data_len, const char *passwor #if __BYTE_ORDER == __LITTLE_ENDIAN { - guint16 *p, *p_end; + const guint16 *p_end; + guint16 *p; - p_end = (guint16 *) &(((guint8 *) pw.data)[ucs2_password.len]); - for (p = (guint16 *) pw.data; p < p_end; p++) + /* we cast here to guint16 pointers (which would trigger a "-Wcast-align"). + * But this is safe, because ucs2_password.len is a multiple of 2 and + * because pw.data was an allocated buffer (that is presumably aligned + * correctly). */ + nm_assert(ucs2_password.len % 2 == 0); + + p_end = NM_CAST_ALIGN(guint16, &(((guint8 *) pw.data)[ucs2_password.len])); + for (p = NM_CAST_ALIGN(guint16, pw.data); p < p_end; p++) *p = GUINT16_SWAP_LE_BE(*p); } #endif diff --git a/src/libnm-glib-aux/meson.build b/src/libnm-glib-aux/meson.build index 4f13fd0b..afd86871 100644 --- a/src/libnm-glib-aux/meson.build +++ b/src/libnm-glib-aux/meson.build @@ -8,10 +8,12 @@ libnm_glib_aux = static_library( 'nm-enum-utils.c', 'nm-errno.c', 'nm-hash-utils.c', + 'nm-inet-utils.c', 'nm-io-utils.c', 'nm-json-aux.c', 'nm-keyfile-aux.c', 'nm-logging-base.c', + 'nm-prioq.c', 'nm-random-utils.c', 'nm-ref-string.c', 'nm-secret-utils.c', diff --git a/src/libnm-glib-aux/nm-dbus-aux.c b/src/libnm-glib-aux/nm-dbus-aux.c index 0f43ba8e..3925da55 100644 --- a/src/libnm-glib-aux/nm-dbus-aux.c +++ b/src/libnm-glib-aux/nm-dbus-aux.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* - * Copyright (C) 2019 Red Hat, Inc. + * Copyright (C) 2015,2019 Red Hat, Inc. */ #include "libnm-glib-aux/nm-default-glib-i18n-lib.h" @@ -96,6 +96,33 @@ nm_dbus_connection_call_get_all(GDBusConnection *dbus_connection, nm_utils_user_data_pack(user_data, callback)); } +void +nm_dbus_connection_call_get(GDBusConnection *dbus_connection, + const char *bus_name, + const char *object_path, + const char *interface_name, + const char *property_name, + int timeout_msec, + GCancellable *cancellable, + NMDBusConnectionCallDefaultCb callback, + gpointer user_data) +{ + nm_assert(callback); + + g_dbus_connection_call(dbus_connection, + bus_name, + object_path, + DBUS_INTERFACE_PROPERTIES, + "Get", + g_variant_new("(s)", interface_name, property_name), + G_VARIANT_TYPE("(v)"), + G_DBUS_CALL_FLAGS_NONE, + timeout_msec, + cancellable, + _nm_dbus_connection_call_default_cb, + nm_utils_user_data_pack(user_data, callback)); +} + void nm_dbus_connection_call_set(GDBusConnection *dbus_connection, const char *bus_name, @@ -387,6 +414,12 @@ _nm_dbus_error_is(GError *error, ...) gs_free char *dbus_error = NULL; const char *name; va_list ap; + gboolean found = FALSE; + + /* This should only be used for "foreign" D-Bus errors (eg, errors + * from BlueZ or wpa_supplicant). All NetworkManager D-Bus errors + * should be properly mapped by gdbus to one of the domains/codes in + * nm-errors.h. */ dbus_error = g_dbus_error_get_remote_error(error); if (!dbus_error) @@ -395,13 +428,13 @@ _nm_dbus_error_is(GError *error, ...) va_start(ap, error); while ((name = va_arg(ap, const char *))) { if (nm_streq(dbus_error, name)) { - va_end(ap); - return TRUE; + found = TRUE; + break; } } va_end(ap); - return FALSE; + return found; } /*****************************************************************************/ @@ -490,3 +523,194 @@ nm_dbus_connection_call_blocking(NMDBusConnectionCallBlockingData *data, GError return g_steal_pointer(&result); } + +/*****************************************************************************/ + +typedef struct { + char *signal_name; + const GVariantType *signature; +} NMDBusSignalData; + +static void +dbus_signal_data_free(gpointer data, GClosure *closure) +{ + NMDBusSignalData *sd = data; + + g_free(sd->signal_name); + g_slice_free(NMDBusSignalData, sd); +} + +static void +dbus_signal_meta_marshal(GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + NMDBusSignalData *sd = marshal_data; + const char *signal_name; + GVariant *parameters; + gs_free GValue *closure_params_free = NULL; + GValue *closure_params; + gsize n_params; + gsize i; + + g_return_if_fail(n_param_values == 4); + + signal_name = g_value_get_string(¶m_values[2]); + parameters = g_value_get_variant(¶m_values[3]); + + if (!nm_streq(signal_name, sd->signal_name)) + return; + + if (sd->signature) { + if (!g_variant_is_of_type(parameters, sd->signature)) { + g_warning("%p: got signal '%s' but parameters were of type '%s', not '%s'", + g_value_get_object(¶m_values[0]), + signal_name, + g_variant_get_type_string(parameters), + g_variant_type_peek_string(sd->signature)); + return; + } + + n_params = g_variant_n_children(parameters) + 1; + } else + n_params = 1; + + closure_params = nm_malloc0_maybe_a(240, sizeof(GValue) * n_params, &closure_params_free); + g_value_init(&closure_params[0], G_TYPE_OBJECT); + g_value_copy(¶m_values[0], &closure_params[0]); + + for (i = 1; i < n_params; i++) { + gs_unref_variant GVariant *param = NULL; + + param = g_variant_get_child_value(parameters, i - 1); + if (g_variant_is_of_type(param, G_VARIANT_TYPE("ay")) + || g_variant_is_of_type(param, G_VARIANT_TYPE("aay"))) { + /* g_dbus_gvariant_to_gvalue() thinks 'ay' means "non-UTF-8 NUL-terminated string" */ + g_value_init(&closure_params[i], G_TYPE_VARIANT); + g_value_set_variant(&closure_params[i], param); + } else + g_dbus_gvariant_to_gvalue(param, &closure_params[i]); + } + + g_cclosure_marshal_generic(closure, NULL, n_params, closure_params, invocation_hint, NULL); + + for (i = 0; i < n_params; i++) + g_value_unset(&closure_params[i]); +} + +/** + * _nm_dbus_proxy_signal_connect_data: + * @proxy: a #GDBusProxy + * @signal_name: the D-Bus signal to connect to + * @signature: (allow-none): the signal's type signature (must be a tuple) + * @c_handler: the signal handler function + * @data: (allow-none): data to pass to @c_handler + * @destroy_data: (allow-none): closure destroy notify for @data + * @connect_flags: connection flags + * + * Connects to the D-Bus signal @signal_name on @proxy. @c_handler must be a + * void function whose first argument is a #GDBusProxy, followed by arguments + * for each element of @signature, ending with a #gpointer argument for @data. + * + * The argument types in @c_handler correspond to the types output by + * g_dbus_gvariant_to_gvalue(), except for 'ay' and 'aay'. In particular: + * - both 16-bit and 32-bit integers are passed as #int/#guint + * - 'as' values are passed as #GStrv (char **) + * - all other array, tuple, and dict types are passed as #GVariant + * + * If @signature is %NULL, then the signal's parameters will be ignored, and + * @c_handler should take only the #GDBusProxy and #gpointer arguments. + * + * Returns: the signal handler ID, which can be used with + * g_signal_handler_remove(). Beware that because of the way the signal is + * connected, you will not be able to remove it with + * g_signal_handlers_disconnect_by_func(), although + * g_signal_handlers_disconnect_by_data() will work correctly. + */ +gulong +_nm_dbus_proxy_signal_connect_data(GDBusProxy *proxy, + const char *signal_name, + const GVariantType *signature, + GCallback c_handler, + gpointer data, + GClosureNotify destroy_data, + GConnectFlags connect_flags) +{ + NMDBusSignalData *sd; + GClosure *closure; + gboolean swapped = !!(connect_flags & G_CONNECT_SWAPPED); + gboolean after = !!(connect_flags & G_CONNECT_AFTER); + + g_return_val_if_fail(G_IS_DBUS_PROXY(proxy), 0); + g_return_val_if_fail(signal_name != NULL, 0); + g_return_val_if_fail(signature == NULL || g_variant_type_is_tuple(signature), 0); + g_return_val_if_fail(c_handler != NULL, 0); + + sd = g_slice_new(NMDBusSignalData); + sd->signal_name = g_strdup(signal_name); + sd->signature = signature; + + closure = (swapped ? g_cclosure_new_swap : g_cclosure_new)(c_handler, data, destroy_data); + g_closure_set_marshal(closure, g_cclosure_marshal_generic); + g_closure_set_meta_marshal(closure, sd, dbus_signal_meta_marshal); + g_closure_add_finalize_notifier(closure, sd, dbus_signal_data_free); + + return g_signal_connect_closure(proxy, "g-signal", closure, after); +} + +/*****************************************************************************/ + +static gboolean +_nm_dbus_typecheck_response(GVariant *response, const GVariantType *reply_type, GError **error) +{ + g_return_val_if_fail(response, FALSE); + + if (!reply_type) + return TRUE; + if (g_variant_is_of_type(response, reply_type)) + return TRUE; + + /* This is the same error code that g_dbus_connection_call() returns if + * @reply_type doesn't match. + */ + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Method returned type '%s', but expected '%s'"), + g_variant_get_type_string(response), + g_variant_type_peek_string(reply_type)); + return FALSE; +} + +/** + * _nm_dbus_proxy_call_finish: + * @proxy: A #GDBusProxy. + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to + * g_dbus_proxy_call(). + * @reply_type: (allow-none): the expected type of the reply, or %NULL + * @error: Return location for error or %NULL. + * + * Finishes an operation started with g_dbus_proxy_call(), as with + * g_dbus_proxy_call_finish(), except thatif @reply_type is non-%NULL, then it + * will also check that the response matches that type signature, and return + * an error if not. + * + * Returns: %NULL if @error is set. Otherwise, a #GVariant tuple with + * return values. Free with g_variant_unref(). + */ +GVariant * +_nm_dbus_proxy_call_finish(GDBusProxy *proxy, + GAsyncResult *res, + const GVariantType *reply_type, + GError **error) +{ + GVariant *variant; + + variant = g_dbus_proxy_call_finish(proxy, res, error); + if (variant && !_nm_dbus_typecheck_response(variant, reply_type, error)) + nm_clear_pointer(&variant, g_variant_unref); + return variant; +} diff --git a/src/libnm-glib-aux/nm-dbus-aux.h b/src/libnm-glib-aux/nm-dbus-aux.h index dfba61b0..5db79c08 100644 --- a/src/libnm-glib-aux/nm-dbus-aux.h +++ b/src/libnm-glib-aux/nm-dbus-aux.h @@ -147,6 +147,16 @@ void nm_dbus_connection_call_get_all(GDBusConnection *dbus_connecti NMDBusConnectionCallDefaultCb callback, gpointer user_data); +void nm_dbus_connection_call_get(GDBusConnection *dbus_connection, + const char *bus_name, + const char *object_path, + const char *interface_name, + const char *property_name, + int timeout_msec, + GCancellable *cancellable, + NMDBusConnectionCallDefaultCb callback, + gpointer user_data); + void nm_dbus_connection_call_set(GDBusConnection *dbus_connection, const char *bus_name, const char *object_path, @@ -264,4 +274,40 @@ nm_g_variant_tuple_get_u(GVariant *v, guint32 *out_u) return FALSE; } +/*****************************************************************************/ + +gulong _nm_dbus_proxy_signal_connect_data(GDBusProxy *proxy, + const char *signal_name, + const GVariantType *signature, + GCallback c_handler, + gpointer data, + GClosureNotify destroy_data, + GConnectFlags connect_flags); + +/** + * _nm_dbus_proxy_signal_connect: + * @proxy: a #GDBusProxy + * @signal_name: the D-Bus signal to connect to + * @signature: the signal's type signature (must be a tuple) + * @c_handler: the signal handler function + * @data: (allow-none): data to pass to @c_handler + * + * Simplified version of _nm_dbus_proxy_signal_connect_data() with fewer arguments. + * + * Returns: the signal handler ID, as with _nm_signal_connect_data(). + */ +#define _nm_dbus_proxy_signal_connect(proxy, name, signature, handler, data) \ + _nm_dbus_proxy_signal_connect_data(proxy, \ + name, \ + signature, \ + handler, \ + data, \ + NULL, \ + (GConnectFlags) 0) + +GVariant *_nm_dbus_proxy_call_finish(GDBusProxy *proxy, + GAsyncResult *res, + const GVariantType *reply_type, + GError **error); + #endif /* __NM_DBUS_AUX_H__ */ diff --git a/src/libnm-glib-aux/nm-dedup-multi.c b/src/libnm-glib-aux/nm-dedup-multi.c index 7d451d0d..625c4ef3 100644 --- a/src/libnm-glib-aux/nm-dedup-multi.c +++ b/src/libnm-glib-aux/nm-dedup-multi.c @@ -1004,12 +1004,14 @@ nm_dedup_multi_index_new(void) { NMDedupMultiIndex *self; - self = g_slice_new0(NMDedupMultiIndex); - self->ref_count = 1; - self->idx_entries = - g_hash_table_new((GHashFunc) _dict_idx_entries_hash, (GEqualFunc) _dict_idx_entries_equal); - self->idx_objs = - g_hash_table_new((GHashFunc) _dict_idx_objs_hash, (GEqualFunc) _dict_idx_objs_equal); + self = g_slice_new(NMDedupMultiIndex); + *self = (NMDedupMultiIndex){ + .ref_count = 1, + .idx_entries = g_hash_table_new((GHashFunc) _dict_idx_entries_hash, + (GEqualFunc) _dict_idx_entries_equal), + .idx_objs = + g_hash_table_new((GHashFunc) _dict_idx_objs_hash, (GEqualFunc) _dict_idx_objs_equal), + }; return self; } @@ -1018,6 +1020,7 @@ nm_dedup_multi_index_ref(NMDedupMultiIndex *self) { g_return_val_if_fail(self, NULL); g_return_val_if_fail(self->ref_count > 0, NULL); + nm_assert(self->ref_count < G_MAXINT32); self->ref_count++; return self; diff --git a/src/libnm-glib-aux/nm-dedup-multi.h b/src/libnm-glib-aux/nm-dedup-multi.h index 0fa47422..87a2b815 100644 --- a/src/libnm-glib-aux/nm-dedup-multi.h +++ b/src/libnm-glib-aux/nm-dedup-multi.h @@ -37,6 +37,8 @@ typedef enum _NMDedupMultiIdxMode { /*****************************************************************************/ +#define _NMDedupMultiObj_Align (MAX(_nm_alignof(void *), _nm_alignof(gint64))) + struct _NMDedupMultiObj { union { NMObjBaseInst parent; @@ -44,7 +46,7 @@ struct _NMDedupMultiObj { }; NMDedupMultiIndex *_multi_idx; guint _ref_count; -}; +} _nm_align(_NMDedupMultiObj_Align); struct _NMDedupMultiObjClass { NMObjBaseClass parent; @@ -101,21 +103,21 @@ nm_dedup_multi_index_obj_find(NMDedupMultiIndex *self, * The NMDedupMultiIdxTypeClass determines its behavior, but you can have * multiple instances (of the same class). * - * For example, NMIP4Config can have idx-type to put there all IPv4 Routes. - * This idx-type instance is private to the NMIP4Config instance. Basically, - * the NMIP4Config instance uses the idx-type to maintain an ordered list + * For example, NML3ConfigData can have idx-type to put there all IPv4 Routes. + * This idx-type instance is private to the NML3ConfigData instance. Basically, + * the NML3ConfigData instance uses the idx-type to maintain an ordered list * of routes in NMDedupMultiIndex. * * However, a NMDedupMultiIdxType may also partition the set of objects - * in multiple distinct lists. NMIP4Config doesn't do that (because instead - * of creating one idx-type for IPv4 and IPv6 routes, it just cretaes - * to distinct idx-types, one for each address family. + * in multiple distinct lists. NML3ConfigData doesn't do that (because instead + * of creating one idx-type for IPv4 and IPv6 routes, it just creates + * distinct idx-types, one for each object type and address family. * This partitioning is used by NMPlatform to maintain a lookup index for * routes by ifindex. As the ifindex is dynamic, it does not create an * idx-type instance for each ifindex. Instead, it has one idx-type for * all routes. But whenever accessing NMDedupMultiIndex with an NMDedupMultiObj, - * the partitioning NMDedupMultiIdxType takes into account the NMDedupMultiObj - * instance to associate it with the right list. + * the NMDedupMultiIdxType can partition the object and assign it to the right + * list. * * Hence, a NMDedupMultiIdxEntry has a list of possibly multiple NMDedupMultiHeadEntry * instances, which each is the head for a list of NMDedupMultiEntry instances. diff --git a/src/libnm-glib-aux/nm-default-glib.h b/src/libnm-glib-aux/nm-default-glib.h index f39e16cc..18460de8 100644 --- a/src/libnm-glib-aux/nm-default-glib.h +++ b/src/libnm-glib-aux/nm-default-glib.h @@ -67,6 +67,7 @@ #include "libnm-glib-aux/nm-shared-utils.h" #include "libnm-glib-aux/nm-errno.h" #include "libnm-glib-aux/nm-hash-utils.h" +#include "libnm-glib-aux/nm-inet-utils.h" /*****************************************************************************/ diff --git a/src/libnm-glib-aux/nm-glib.h b/src/libnm-glib-aux/nm-glib.h index 2dc86d7b..0436d135 100644 --- a/src/libnm-glib-aux/nm-glib.h +++ b/src/libnm-glib-aux/nm-glib.h @@ -45,28 +45,40 @@ __g_type_ensure(GType type) /*****************************************************************************/ -#if !GLIB_CHECK_VERSION(2, 34, 0) - -#define g_clear_pointer(pp, destroy) \ - G_STMT_START \ - { \ - G_STATIC_ASSERT(sizeof *(pp) == sizeof(gpointer)); \ - /* Only one access, please */ \ - gpointer *_pp = (gpointer *) (pp); \ - gpointer _p; \ - /* This assignment is needed to avoid a gcc warning */ \ - GDestroyNotify _destroy = (GDestroyNotify) (destroy); \ - \ - _p = *_pp; \ - if (_p) { \ - *_pp = NULL; \ - _destroy(_p); \ - } \ - } \ +/* glib 2.58+ defines an improved, type-safe variant of g_clear_pointer(). Reimplement + * that. + * + * Note that we also have nm_clear_pointer() which is similar to g_clear_pointer() + * and also preferred throughout. However, we do use g_clear_object(), which is + * implemented via g_clear_pointer(). So while there are no immediate users of + * g_clear_pointer() (use nm_clear_pointer() instead), there are indirect users + * via g_clear_object(). + * + * Anyway. So the glib 2.58+ version of g_clear_pointer() is only used if GLIB_VERSION_MAX_ALLOWED + * is set, which we don't do. + * + * To get the type-safe variant, reimplement g_clear_pointer() below. This aims to + * be identical to the version of the macro in glib 2.58. + * + * Still, don't use g_clear_pointer() directly (use nm_clear_pointer()). This compat + * implementation exists only for g_clear_object(). + */ +#undef g_clear_pointer + +#define g_clear_pointer(pp, destroy) \ + G_STMT_START \ + { \ + typeof((pp)) _pp = (pp); \ + typeof(*_pp) _ptr = *_pp; \ + \ + G_STATIC_ASSERT(sizeof *(pp) == sizeof(gpointer)); \ + \ + *_pp = NULL; \ + if (_ptr) \ + (destroy)(_ptr); \ + } \ G_STMT_END -#endif - /*****************************************************************************/ #if !GLIB_CHECK_VERSION(2, 34, 0) diff --git a/src/libnm-glib-aux/nm-hash-utils.c b/src/libnm-glib-aux/nm-hash-utils.c index 941aba01..973422a1 100644 --- a/src/libnm-glib-aux/nm-hash-utils.c +++ b/src/libnm-glib-aux/nm-hash-utils.c @@ -106,7 +106,7 @@ nm_hash_static(guint static_seed) * * Also, ensure that we don't return zero (like for nm_hash_complete()). */ - return ((*((const guint *) _get_hash_key())) ^ static_seed) ?: 3679500967u; + return ((*NM_CAST_ALIGN(guint, _get_hash_key())) ^ static_seed) ?: 3679500967u; } void diff --git a/src/libnm-glib-aux/nm-hash-utils.h b/src/libnm-glib-aux/nm-hash-utils.h index dffa828b..e2707fc2 100644 --- a/src/libnm-glib-aux/nm-hash-utils.h +++ b/src/libnm-glib-aux/nm-hash-utils.h @@ -105,15 +105,30 @@ nm_hash_update(NMHashState *state, const void *ptr, gsize n) c_siphash_append(&state->_state, ptr, n); } -#define nm_hash_update_val(state, val) \ +#define _NM_HASH_COMBINE_VALS_TYPE_OP(x, idx, op_arg) typeof(x) _v##idx; +#define _NM_HASH_COMBINE_VALS_INIT_OP(x, idx, op_arg) ._v##idx = (x), + +/* NM_HASH_COMBINE_VALS() is faster then nm_hash_update_val() as it combines multiple + * calls to nm_hash_update() using a packed structure. */ +#define NM_HASH_COMBINE_VALS(var, ...) \ + const struct _nm_packed { \ + NM_VA_ARGS_FOREACH(, , , _NM_HASH_COMBINE_VALS_TYPE_OP, , __VA_ARGS__) \ + } var _nm_alignas(max_align_t) = { \ + NM_VA_ARGS_FOREACH(, , , _NM_HASH_COMBINE_VALS_INIT_OP, , __VA_ARGS__)} + +/* nm_hash_update_vals() is faster then nm_hash_update_val() as it combines multiple + * calls to nm_hash_update() using a packed structure. */ +#define nm_hash_update_vals(state, ...) \ G_STMT_START \ { \ - typeof(val) _val = (val); \ + NM_HASH_COMBINE_VALS(_val, __VA_ARGS__); \ \ nm_hash_update((state), &_val, sizeof(_val)); \ } \ G_STMT_END +#define nm_hash_update_val(state, val) nm_hash_update_vals((state), (val)) + #define nm_hash_update_valp(state, val) nm_hash_update((state), (val), sizeof(*(val))) static inline void @@ -132,28 +147,6 @@ nm_hash_update_bool(NMHashState *state, bool val) #define nm_hash_update_bools(state, ...) \ nm_hash_update_val(state, NM_HASH_COMBINE_BOOLS(guint8, __VA_ARGS__)) -#define _NM_HASH_COMBINE_VALS_TYPE_OP(x, idx, op_arg) typeof(x) _v##idx; -#define _NM_HASH_COMBINE_VALS_INIT_OP(x, idx, op_arg) ._v##idx = (x), - -/* NM_HASH_COMBINE_VALS() is faster then nm_hash_update_val() as it combines multiple - * calls to nm_hash_update() using a packed structure. */ -#define NM_HASH_COMBINE_VALS(var, ...) \ - const struct _nm_packed { \ - NM_VA_ARGS_FOREACH(, , , _NM_HASH_COMBINE_VALS_TYPE_OP, , __VA_ARGS__) \ - } var _nm_alignas(max_align_t) = { \ - NM_VA_ARGS_FOREACH(, , , _NM_HASH_COMBINE_VALS_INIT_OP, , __VA_ARGS__)} - -/* nm_hash_update_vals() is faster then nm_hash_update_val() as it combines multiple - * calls to nm_hash_update() using a packed structure. */ -#define nm_hash_update_vals(state, ...) \ - G_STMT_START \ - { \ - NM_HASH_COMBINE_VALS(_val, __VA_ARGS__); \ - \ - nm_hash_update((state), &_val, sizeof(_val)); \ - } \ - G_STMT_END - static inline void nm_hash_update_mem(NMHashState *state, const void *ptr, gsize n) { @@ -206,15 +199,17 @@ guint nm_hash_ptr(gconstpointer ptr); guint nm_hash_str(const char *str); #define nm_str_hash ((guint(*)(gconstpointer str)) nm_hash_str) -#define nm_hash_val(static_seed, val) \ - ({ \ - NMHashState _h; \ - \ - nm_hash_init(&_h, (static_seed)); \ - nm_hash_update_val(&_h, (val)); \ - nm_hash_complete(&_h); \ +#define nm_hash_vals(static_seed, ...) \ + ({ \ + NMHashState _h; \ + \ + nm_hash_init(&_h, (static_seed)); \ + nm_hash_update_vals(&_h, __VA_ARGS__); \ + nm_hash_complete(&_h); \ }) +#define nm_hash_val(static_seed, val) nm_hash_vals((static_seed), (val)) + static inline guint nm_hash_mem(guint static_seed, const void *ptr, gsize n) { diff --git a/src/libnm-glib-aux/nm-inet-utils.c b/src/libnm-glib-aux/nm-inet-utils.c new file mode 100644 index 00000000..7f710f35 --- /dev/null +++ b/src/libnm-glib-aux/nm-inet-utils.c @@ -0,0 +1,529 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nm-inet-utils.h" + +#include +#include + +/*****************************************************************************/ + +const NMIPAddr nm_ip_addr_zero = {}; + +/* We use _nm_alignas(NMIPAddr) to ensure that fields for in_addr_t and + * struct in6_addr have all the same alignment. Ensure that this is suitable. */ +G_STATIC_ASSERT(_nm_alignof(in_addr_t) <= _nm_alignof(NMIPAddr)); +G_STATIC_ASSERT(_nm_alignof(struct in_addr) <= _nm_alignof(NMIPAddr)); +G_STATIC_ASSERT(_nm_alignof(struct in6_addr) <= _nm_alignof(NMIPAddr)); +G_STATIC_ASSERT(_nm_alignof(NMEtherAddr) <= _nm_alignof(NMIPAddr)); + +int +nm_ip_addr_cmp_for_sort(gconstpointer a, gconstpointer b, gpointer user_data) +{ + /* This is a compare function that can be used for sorting IP addresses. + * Essentially, it calls memcmp(). @user_data must be GINT_TO_POINTER(addr_family). + * @a and @b must be either pointers to in_addr_t, struct in6_addr or NMIPAddr. */ + return nm_ip_addr_cmp(GPOINTER_TO_INT(user_data), a, b); +} + +/* this initializes a struct in_addr/in6_addr and allows for untrusted + * arguments (like unsuitable @addr_family or @src_len). It's almost safe + * in the sense that it verifies input arguments strictly. Also, it + * uses memcpy() to access @src, so alignment is not an issue. + * + * Only potential pitfalls: + * + * - it allows for @addr_family to be AF_UNSPEC. If that is the case (and the + * caller allows for that), the caller MUST provide @out_addr_family. + * - when setting @dst to an IPv4 address, the trailing bytes are not touched. + * Meaning, if @dst is an NMIPAddr union, only the first bytes will be set. + * If that matter to you, clear @dst before. */ +gboolean +nm_ip_addr_set_from_untrusted(int addr_family, + gpointer dst, + gconstpointer src, + gsize src_len, + int *out_addr_family) +{ + nm_assert(dst); + + switch (addr_family) { + case AF_UNSPEC: + if (!out_addr_family) { + /* when the callers allow undefined @addr_family, they must provide + * an @out_addr_family argument. */ + nm_assert_not_reached(); + return FALSE; + } + switch (src_len) { + case sizeof(struct in_addr): + addr_family = AF_INET; + break; + case sizeof(struct in6_addr): + addr_family = AF_INET6; + break; + default: + return FALSE; + } + break; + case AF_INET: + if (src_len != sizeof(struct in_addr)) + return FALSE; + break; + case AF_INET6: + if (src_len != sizeof(struct in6_addr)) + return FALSE; + break; + default: + /* when the callers allow undefined @addr_family, they must provide + * an @out_addr_family argument. */ + nm_assert(out_addr_family); + return FALSE; + } + + nm_assert(src); + + memcpy(dst, src, src_len); + NM_SET_OUT(out_addr_family, addr_family); + return TRUE; +} + +gboolean +nm_ip_addr_set_from_variant(int addr_family, gpointer dst, GVariant *variant, int *out_addr_family) +{ + gconstpointer bytes; + gsize len; + + g_return_val_if_fail(dst, FALSE); + g_return_val_if_fail(variant, FALSE); + + /* This function always expects IP addresses as byte arrays ("ay"). Note that + * several NetworkManager API uses "u" (32 bit unsigned intergers) for IPv4 addresses. + * So this function won't work in those cases. + * + * Btw, using "u" for IPv4 address messes badly with the endianness (host + * vs network byte order). Don't do that. + */ + g_return_val_if_fail(g_variant_is_of_type(variant, G_VARIANT_TYPE("ay")), FALSE); + + bytes = g_variant_get_fixed_array(variant, &len, sizeof(guint8)); + + return nm_ip_addr_set_from_untrusted(addr_family, dst, bytes, len, out_addr_family); +} + +/*****************************************************************************/ + +guint32 +nm_ip4_addr_get_default_prefix0(in_addr_t ip) +{ + /* The function is originally from ipcalc.c of Red Hat's initscripts. */ + switch (ntohl(ip) >> 24) { + case 0 ... 127: + return 8; /* Class A */ + case 128 ... 191: + return 16; /* Class B */ + case 192 ... 223: + return 24; /* Class C */ + } + return 0; +} + +guint32 +nm_ip4_addr_get_default_prefix(in_addr_t ip) +{ + return nm_ip4_addr_get_default_prefix0(ip) ?: 24; +} + +gboolean +nm_ip_addr_is_site_local(int addr_family, const void *address) +{ + NMIPAddr a; + + nm_ip_addr_set(addr_family, &a, address); + + switch (addr_family) { + case AF_INET: + /* RFC1918 private addresses + * 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 */ + return (a.addr4 & htonl(0xff000000)) == htonl(0x0a000000) + || (a.addr4 & htonl(0xfff00000)) == htonl(0xac100000) + || (a.addr4 & htonl(0xffff0000)) == htonl(0xc0a80000); + case AF_INET6: + /* IN6_IS_ADDR_SITELOCAL() is for deprecated fec0::/10 addresses (see rfc3879, 4.). + * Note that for unique local IPv6 addresses (ULA, fc00::/7) this returns false. + * This may or may not be a bug of this function. */ + return IN6_IS_ADDR_SITELOCAL(&a.addr6); + default: + g_return_val_if_reached(FALSE); + } +} + +gboolean +nm_ip6_addr_is_ula(const struct in6_addr *address) +{ + /* Unique local IPv6 address (ULA) fc00::/7 */ + return (address->s6_addr32[0] & htonl(0xfe000000u)) == htonl(0xfc000000u); +} + +/*****************************************************************************/ + +gconstpointer +nm_ip_addr_clear_host_address(int family, gpointer dst, gconstpointer src, guint32 plen) +{ + NMIPAddr a; + NMIPAddr a2; + + g_return_val_if_fail(dst, NULL); + + if (!src) { + /* allow "self-assignment", by specifying %NULL as source. */ + src = dst; + } + + nm_ip_addr_set(family, &a, src); + + switch (family) { + case AF_INET: + g_return_val_if_fail(plen <= 32, NULL); + + a2.addr4 = nm_ip4_addr_clear_host_address(a.addr4, plen); + break; + case AF_INET6: + nm_ip6_addr_clear_host_address(&a2.addr6, &a.addr6, plen); + break; + default: + g_return_val_if_reached(NULL); + } + + nm_ip_addr_set(family, dst, &a2); + + return dst; +} + +/* nm_ip6_addr_clear_host_address: + * @dst: destination output buffer, will contain the network part of the @src address + * @src: source ip6 address. If NULL, this does an in-place update of @dst. + * Also, @src and @dst are allowed to be the same pointers. + * @plen: prefix length of network + * + * Note: this function is self assignment safe, to update @src inplace, set both + * @dst and @src to the same destination or set @src NULL. + */ +const struct in6_addr * +nm_ip6_addr_clear_host_address(struct in6_addr *dst, const struct in6_addr *src, guint32 plen) +{ + g_return_val_if_fail(plen <= 128, NULL); + g_return_val_if_fail(dst, NULL); + + if (!src) + src = dst; + + if (plen < 128) { + guint nbytes = plen / 8; + guint nbits = plen % 8; + + if (nbytes && dst != src) + memcpy(dst, src, nbytes); + if (nbits) { + dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits))); + nbytes++; + } + if (nbytes <= 15) + memset(&dst->s6_addr[nbytes], 0, 16 - nbytes); + } else if (src != dst) + *dst = *src; + + return dst; +} + +int +nm_ip6_addr_same_prefix_cmp(const struct in6_addr *addr_a, + const struct in6_addr *addr_b, + guint32 plen) +{ + int nbytes; + guint8 va, vb, m; + + if (plen >= 128) { + nm_assert(plen == 128); + NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, sizeof(struct in6_addr)); + } else { + nbytes = plen / 8; + if (nbytes) + NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, nbytes); + + plen = plen % 8; + if (plen != 0) { + m = ~((1 << (8 - plen)) - 1); + va = ((((const guint8 *) addr_a))[nbytes]) & m; + vb = ((((const guint8 *) addr_b))[nbytes]) & m; + NM_CMP_DIRECT(va, vb); + } + } + return 0; +} + +/*****************************************************************************/ + +static gboolean +_parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error) +{ + gs_free char *s_free = NULL; + struct in_addr a1; + guint8 bin[sizeof(a1)]; + char *s; + int i; + + if (inet_aton(text, &a1) != 1) { + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + "address invalid according to inet_aton()"); + return FALSE; + } + + /* OK, inet_aton() accepted the format. That's good, because we want + * to accept IPv4 addresses in octal format, like 255.255.000.000. + * That's what "legacy" means here. inet_pton() doesn't accept those. + * + * But inet_aton() also ignores trailing garbage and formats with fewer than + * 4 digits. That is just too crazy and we don't do that. Perform additional checks + * and reject some forms that inet_aton() accepted. + * + * Note that we still should (of course) accept everything that inet_pton() + * accepts. However this code never gets called if inet_pton() succeeds + * (see below, aside the assertion code). */ + + if (NM_STRCHAR_ANY(text, ch, (!(ch >= '0' && ch <= '9') && !NM_IN_SET(ch, '.', 'x')))) { + /* We only accepts '.', digits, and 'x' for "0x". */ + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + "contains an invalid character"); + return FALSE; + } + + s = nm_memdup_maybe_a(300, text, strlen(text) + 1, &s_free); + + for (i = 0; i < G_N_ELEMENTS(bin); i++) { + char *current_token = s; + gint32 v; + + s = strchr(s, '.'); + if (s) { + s[0] = '\0'; + s++; + } + + if ((i == G_N_ELEMENTS(bin) - 1) != (s == NULL)) { + /* Exactly for the last digit, we expect to have no more following token. + * But this isn't the case. Abort. */ + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + "wrong number of tokens (index %d, token '%s')", + i, + s); + return FALSE; + } + + v = _nm_utils_ascii_str_to_int64(current_token, 0, 0, 0xFF, -1); + if (v == -1) { + int errsv = errno; + + /* we do accept octal and hex (even with leading "0x"). But something + * about this token is wrong. */ + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + "invalid token '%s': %s (%d)", + current_token, + nm_strerror_native(errsv), + errsv); + return FALSE; + } + + bin[i] = v; + } + + if (memcmp(bin, &a1, sizeof(bin)) != 0) { + /* our parsing did not agree with what inet_aton() gave. Something + * is wrong. Abort. */ + g_set_error( + error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + "inet_aton() result 0x%08x differs from computed value 0x%02hhx%02hhx%02hhx%02hhx", + a1.s_addr, + bin[0], + bin[1], + bin[2], + bin[3]); + return FALSE; + } + + *out_addr = a1.s_addr; + return TRUE; +} + +gboolean +nm_inet_parse_bin_full(int addr_family, + gboolean accept_legacy, + const char *text, + int *out_addr_family, + gpointer out_addr) +{ + NMIPAddr addrbin; + + g_return_val_if_fail(text, FALSE); + + if (addr_family == AF_UNSPEC) { + g_return_val_if_fail(!out_addr || out_addr_family, FALSE); + addr_family = strchr(text, ':') ? AF_INET6 : AF_INET; + } else + g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE); + + if (inet_pton(addr_family, text, &addrbin) != 1) { + if (accept_legacy && addr_family == AF_INET + && _parse_legacy_addr4(text, &addrbin.addr4, NULL)) { + /* The address is in some legacy format which inet_aton() accepts, but not inet_pton(). + * Most likely octal digits (leading zeros). We accept the address. */ + } else + return FALSE; + } + +#if NM_MORE_ASSERTS > 10 + if (addr_family == AF_INET) { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER + gs_free_error GError *error = NULL; + in_addr_t a; + + /* The legacy parser should accept everything that inet_pton() accepts too. Meaning, + * it should strictly parse *more* formats. And of course, parse it the same way. */ + if (!_parse_legacy_addr4(text, &a, &error)) { + char buf[INET_ADDRSTRLEN]; + + g_error("unexpected assertion failure: could parse \"%s\" as %s, but not accepted by " + "legacy parser: %s", + text, + nm_inet4_ntop(addrbin.addr4, buf), + error->message); + } + nm_assert(addrbin.addr4 == a); + NM_PRAGMA_WARNING_REENABLE + } +#endif + + NM_SET_OUT(out_addr_family, addr_family); + if (out_addr) + nm_ip_addr_set(addr_family, out_addr, &addrbin); + return TRUE; +} + +gboolean +nm_inet_parse_str(int addr_family, const char *text, char **out_addr) +{ + NMIPAddr addrbin; + + if (!nm_inet_parse_bin(addr_family, text, &addr_family, &addrbin)) + return FALSE; + + NM_SET_OUT(out_addr, nm_inet_ntop_dup(addr_family, &addrbin)); + return TRUE; +} + +gboolean +nm_inet_parse_with_prefix_bin(int addr_family, + const char *text, + int *out_addr_family, + gpointer out_addr, + int *out_prefix) +{ + gs_free char *addrstr_free = NULL; + int prefix = -1; + const char *slash; + const char *addrstr; + NMIPAddr addrbin; + + g_return_val_if_fail(text, FALSE); + + if (addr_family == AF_UNSPEC) { + g_return_val_if_fail(!out_addr || out_addr_family, FALSE); + addr_family = strchr(text, ':') ? AF_INET6 : AF_INET; + } else + g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE); + + slash = strchr(text, '/'); + if (slash) + addrstr = nm_strndup_a(300, text, slash - text, &addrstr_free); + else + addrstr = text; + + if (inet_pton(addr_family, addrstr, &addrbin) != 1) + return FALSE; + + if (slash) { + /* For IPv4, `ip addr add` supports the prefix-length as a netmask. We don't + * do that. */ + prefix = + _nm_utils_ascii_str_to_int64(&slash[1], 10, 0, addr_family == AF_INET ? 32 : 128, -1); + if (prefix == -1) + return FALSE; + } + + NM_SET_OUT(out_addr_family, addr_family); + if (out_addr) + nm_ip_addr_set(addr_family, out_addr, &addrbin); + NM_SET_OUT(out_prefix, prefix); + return TRUE; +} + +gboolean +nm_inet_parse_with_prefix_str(int addr_family, const char *text, char **out_addr, int *out_prefix) +{ + NMIPAddr addrbin; + + if (!nm_inet_parse_with_prefix_bin(addr_family, text, &addr_family, &addrbin, out_prefix)) + return FALSE; + + NM_SET_OUT(out_addr, nm_inet_ntop_dup(addr_family, &addrbin)); + return TRUE; +} + +/*****************************************************************************/ + +gboolean +nm_inet_is_valid(int addr_family, const char *str_addr) +{ + nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + + return str_addr && nm_inet_parse_bin(addr_family, str_addr, NULL, NULL); +} + +gboolean +nm_inet_is_normalized(int addr_family, const char *str_addr) +{ + NMIPAddr addr; + char sbuf[NM_INET_ADDRSTRLEN]; + + nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + + if (!str_addr) + return FALSE; + + if (!nm_inet_parse_bin(addr_family, str_addr, &addr_family, &addr)) + return FALSE; + + nm_inet_ntop(addr_family, &addr, sbuf); + return nm_streq(sbuf, str_addr); +} + +/*****************************************************************************/ + +NM_UTILS_ENUM2STR_DEFINE(nm_icmpv6_router_pref_to_string, + NMIcmpv6RouterPref, + NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_LOW, "low"), + NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_MEDIUM, "medium"), + NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_HIGH, "high"), + NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_INVALID, "invalid"), ); diff --git a/src/libnm-glib-aux/nm-inet-utils.h b/src/libnm-glib-aux/nm-inet-utils.h new file mode 100644 index 00000000..8421929e --- /dev/null +++ b/src/libnm-glib-aux/nm-inet-utils.h @@ -0,0 +1,389 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef __NM_INET_UTILS_H__ +#define __NM_INET_UTILS_H__ + +typedef struct _NMIPAddr { + union { + guint8 addr_ptr[sizeof(struct in6_addr)]; + in_addr_t addr4; + struct in_addr addr4_struct; + struct in6_addr addr6; + + /* NMIPAddr is really a union for IP addresses. + * However, as ethernet addresses fit in here nicely, use + * it also for an ethernet MAC address. */ + guint8 ether_addr_octet[6 /*ETH_ALEN*/]; + NMEtherAddr ether_addr; + + guint8 array[sizeof(struct in6_addr)]; + }; +} NMIPAddr; + +#define NM_IP_ADDR_INIT \ + { \ + .array = { 0 } \ + } + +#define _NM_IN6ADDR_INIT(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af) \ + { \ + .s6_addr = { \ + (a0), \ + (a1), \ + (a2), \ + (a3), \ + (a4), \ + (a5), \ + (a6), \ + (a7), \ + (a8), \ + (a9), \ + (aa), \ + (ab), \ + (ac), \ + (ad), \ + (ae), \ + (af), \ + } \ + } + +#define NM_IN6ADDR_INIT(...) ((struct in6_addr) _NM_IN6ADDR_INIT(__VA_ARGS__)) + +extern const NMIPAddr nm_ip_addr_zero; + +static inline int +nm_ip_addr_cmp(int addr_family, gconstpointer a, gconstpointer b) +{ + /* Note that @a and @b are not required to be full NMIPAddr unions. + * Depending on @addr_family, they can also be only in_addr_t or + * struct in6_addr. */ + NM_CMP_SELF(a, b); + NM_CMP_DIRECT_MEMCMP(a, b, nm_utils_addr_family_to_size(addr_family)); + return 0; +} + +int nm_ip_addr_cmp_for_sort(gconstpointer a, gconstpointer b, gpointer user_data); + +static inline gboolean +nm_ip_addr_equal(int addr_family, gconstpointer a, gconstpointer b) +{ + return nm_ip_addr_cmp(addr_family, a, b) == 0; +} + +static inline void +nm_ip_addr_set(int addr_family, gpointer dst, gconstpointer src) +{ + nm_assert(dst); + nm_assert(src); + + /* this MUST use memcpy() to support unaligned src/dst pointers. */ + memcpy(dst, src, nm_utils_addr_family_to_size(addr_family)); + + /* Note that @dst is not necessarily a NMIPAddr, it could also be just + * an in_addr_t/struct in6_addr. We thus can only set the bytes that + * we know are present based on the address family. + * + * Using this function to initialize an NMIPAddr union (for IPv4) leaves + * uninitalized bytes. Avoid that by using nm_ip_addr_init() instead. */ +} + +static inline gboolean +nm_ip_addr_is_null(int addr_family, gconstpointer addr) +{ + NMIPAddr a; + + nm_ip_addr_set(addr_family, &a, addr); + + if (NM_IS_IPv4(addr_family)) + return a.addr4 == 0; + + return IN6_IS_ADDR_UNSPECIFIED(&a.addr6); +} + +static inline NMIPAddr +nm_ip_addr_init(int addr_family, gconstpointer src) +{ + NMIPAddr a; + + nm_assert_addr_family(addr_family); + nm_assert(src); + + G_STATIC_ASSERT_EXPR(sizeof(NMIPAddr) == sizeof(struct in6_addr)); + + /* this MUST use memcpy() to support unaligned src/dst pointers. */ + + if (NM_IS_IPv4(addr_family)) { + memcpy(&a, src, sizeof(in_addr_t)); + + /* ensure all bytes of the union are initialized. If only to make + * valgrind happy. */ + memset(&a.array[sizeof(in_addr_t)], 0, sizeof(a) - sizeof(in_addr_t)); + } else + memcpy(&a, src, sizeof(struct in6_addr)); + + return a; +} + +gboolean nm_ip_addr_set_from_untrusted(int addr_family, + gpointer dst, + gconstpointer src, + gsize src_len, + int *out_addr_family); + +gboolean +nm_ip_addr_set_from_variant(int addr_family, gpointer dst, GVariant *variant, int *out_addr_family); + +static inline gconstpointer +nm_ip_addr_from_packed_array(int addr_family, gconstpointer ipaddr_arr, gsize idx) +{ + return NM_IS_IPv4(addr_family) + ? ((gconstpointer) & (((const struct in_addr *) ipaddr_arr)[idx])) + : ((gconstpointer) & (((const struct in6_addr *) ipaddr_arr)[idx])); +} + +/*****************************************************************************/ + +static inline guint32 +nm_ip4_addr_netmask_to_prefix(in_addr_t subnetmask) +{ + G_STATIC_ASSERT_EXPR(__SIZEOF_INT__ == 4); + G_STATIC_ASSERT_EXPR(sizeof(int) == 4); + G_STATIC_ASSERT_EXPR(sizeof(guint) == 4); + G_STATIC_ASSERT_EXPR(sizeof(subnetmask) == 4); + + return ((subnetmask != 0u) ? (guint32) (32 - __builtin_ctz(ntohl(subnetmask))) : 0u); +} + +/** + * nm_ip4_addr_netmask_from_prefix: + * @prefix: a CIDR prefix + * + * Returns: the netmask represented by the prefix, in network byte order + **/ +static inline in_addr_t +nm_ip4_addr_netmask_from_prefix(guint32 prefix) +{ + nm_assert(prefix <= 32); + return prefix < 32 ? ~htonl(0xFFFFFFFFu >> prefix) : 0xFFFFFFFFu; +} + +guint32 nm_ip4_addr_get_default_prefix0(in_addr_t ip); +guint32 nm_ip4_addr_get_default_prefix(in_addr_t ip); + +static inline in_addr_t +nm_ip4_addr_get_broadcast_address(in_addr_t address, guint8 plen) +{ + return address | ~nm_ip4_addr_netmask_from_prefix(plen); +} + +gconstpointer +nm_ip_addr_clear_host_address(int family, gpointer dst, gconstpointer src, guint32 plen); + +/* nm_ip4_addr_clear_host_address: + * @addr: source ip6 address + * @plen: prefix length of network + * + * returns: the input address, with the host address set to 0. + */ +static inline in_addr_t +nm_ip4_addr_clear_host_address(in_addr_t addr, guint32 plen) +{ + return addr & nm_ip4_addr_netmask_from_prefix(plen); +} + +const struct in6_addr * +nm_ip6_addr_clear_host_address(struct in6_addr *dst, const struct in6_addr *src, guint32 plen); + +/*****************************************************************************/ + +static inline int +nm_ip4_addr_same_prefix_cmp(in_addr_t addr_a, in_addr_t addr_b, guint32 plen) +{ + NM_CMP_DIRECT(htonl(nm_ip4_addr_clear_host_address(addr_a, plen)), + htonl(nm_ip4_addr_clear_host_address(addr_b, plen))); + return 0; +} + +int nm_ip6_addr_same_prefix_cmp(const struct in6_addr *addr_a, + const struct in6_addr *addr_b, + guint32 plen); + +static inline gboolean +nm_ip4_addr_same_prefix(in_addr_t addr_a, in_addr_t addr_b, guint32 plen) +{ + return nm_ip4_addr_same_prefix_cmp(addr_a, addr_b, plen) == 0; +} + +static inline gboolean +nm_ip6_addr_same_prefix(const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen) +{ + return nm_ip6_addr_same_prefix_cmp(addr_a, addr_b, plen) == 0; +} + +static inline int +nm_ip_addr_same_prefix_cmp(int addr_family, gconstpointer addr_a, gconstpointer addr_b, guint8 plen) +{ + NMIPAddr a; + NMIPAddr b; + + NM_CMP_SELF(addr_a, addr_b); + + nm_ip_addr_set(addr_family, &a, addr_a); + nm_ip_addr_set(addr_family, &b, addr_b); + + if (NM_IS_IPv4(addr_family)) + return nm_ip4_addr_same_prefix_cmp(a.addr4, b.addr4, plen); + + return nm_ip6_addr_same_prefix_cmp(&a.addr6, &b.addr6, plen); +} + +static inline gboolean +nm_ip_addr_same_prefix(int addr_family, gconstpointer addr_a, gconstpointer addr_b, guint8 plen) +{ + return nm_ip_addr_same_prefix_cmp(addr_family, addr_a, addr_b, plen) == 0; +} + +#define NM_CMP_DIRECT_IP4_ADDR_SAME_PREFIX(a, b, plen) \ + NM_CMP_RETURN(nm_ip4_addr_same_prefix_cmp((a), (b), (plen))) + +#define NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(a, b, plen) \ + NM_CMP_RETURN(nm_ip6_addr_same_prefix_cmp((a), (b), (plen))) + +/*****************************************************************************/ + +gboolean nm_ip_addr_is_site_local(int addr_family, const void *address); +gboolean nm_ip6_addr_is_ula(const struct in6_addr *address); + +/*****************************************************************************/ + +#define NM_IPV4LL_NETWORK ((in_addr_t) htonl(0xA9FE0000lu)) /* 169.254.0.0 */ +#define NM_IPV4LL_NETMASK ((in_addr_t) htonl(0xFFFF0000lu)) /* 255.255.0.0 */ +#define NM_IPV4LO_NETWORK ((in_addr_t) htonl(0x7F000000lu)) /* 127.0.0.0 */ +#define NM_IPV4LO_NETMASK ((in_addr_t) htonl(0xFF000000lu)) /* 255.0.0.0 */ +#define NM_IPV4LO_PREFIXLEN 8 +#define NM_IPV4LO_ADDR1 ((in_addr_t) htonl(0x7F000001lu)) /* 127.0.0.1 */ + +static inline gboolean +nm_ip4_addr_is_loopback(in_addr_t addr) +{ + /* There is also IN_LOOPBACK() in , but there the + * argument is in host order not `in_addr_t`. */ + return (addr & NM_IPV4LO_NETMASK) == NM_IPV4LO_NETWORK; +} + +static inline gboolean +nm_ip4_addr_is_link_local(in_addr_t addr) +{ + return (addr & NM_IPV4LL_NETMASK) == NM_IPV4LL_NETWORK; +} + +static inline gboolean +nm_ip4_addr_is_zeronet(in_addr_t network) +{ + /* Same as ipv4_is_zeronet() from kernel's include/linux/in.h. */ + return (network & htonl(0xFF000000u)) == htonl(0x00000000u); +} + +/*****************************************************************************/ + +#define NM_INET_ADDRSTRLEN INET6_ADDRSTRLEN + +/* Forward declare function so we don't have to drag in . */ +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); + +static inline const char * +nm_inet_ntop(int addr_family, gconstpointer addr, char *dst) +{ + const char *s; + + nm_assert_addr_family(addr_family); + nm_assert(addr); + nm_assert(dst); + + s = inet_ntop(addr_family, + addr, + dst, + addr_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN); + nm_assert(s); + return s; +} + +static inline const char * +nm_inet4_ntop(in_addr_t addr, char dst[static INET_ADDRSTRLEN]) +{ + return nm_inet_ntop(AF_INET, &addr, dst); +} + +static inline const char * +nm_inet6_ntop(const struct in6_addr *addr, char dst[static INET6_ADDRSTRLEN]) +{ + return nm_inet_ntop(AF_INET6, addr, dst); +} + +static inline char * +nm_inet_ntop_dup(int addr_family, gconstpointer addr) +{ + char buf[NM_INET_ADDRSTRLEN]; + + return g_strdup(nm_inet_ntop(addr_family, addr, buf)); +} + +static inline char * +nm_inet4_ntop_dup(in_addr_t addr) +{ + return nm_inet_ntop_dup(AF_INET, &addr); +} + +static inline char * +nm_inet6_ntop_dup(const struct in6_addr *addr) +{ + return nm_inet_ntop_dup(AF_INET6, addr); +} + +/*****************************************************************************/ + +gboolean nm_inet_parse_bin_full(int addr_family, + gboolean accept_legacy, + const char *text, + int *out_addr_family, + gpointer out_addr); +static inline gboolean +nm_inet_parse_bin(int addr_family, const char *text, int *out_addr_family, gpointer out_addr) +{ + return nm_inet_parse_bin_full(addr_family, FALSE, text, out_addr_family, out_addr); +} + +gboolean nm_inet_parse_str(int addr_family, const char *text, char **out_addr); + +gboolean nm_inet_parse_with_prefix_bin(int addr_family, + const char *text, + int *out_addr_family, + gpointer out_addr, + int *out_prefix); + +gboolean +nm_inet_parse_with_prefix_str(int addr_family, const char *text, char **out_addr, int *out_prefix); + +/*****************************************************************************/ + +gboolean nm_inet_is_valid(int addr_family, const char *str_addr); + +gboolean nm_inet_is_normalized(int addr_family, const char *str_addr); + +/*****************************************************************************/ + +/* this enum is compatible with ICMPV6_ROUTER_PREF_* (from , + * the values for netlink attribute RTA_PREF) and "enum ndp_route_preference" + * from . */ +typedef enum _nm_packed { + NM_ICMPV6_ROUTER_PREF_MEDIUM = 0x0, /* ICMPV6_ROUTER_PREF_MEDIUM */ + NM_ICMPV6_ROUTER_PREF_LOW = 0x3, /* ICMPV6_ROUTER_PREF_LOW */ + NM_ICMPV6_ROUTER_PREF_HIGH = 0x1, /* ICMPV6_ROUTER_PREF_HIGH */ + NM_ICMPV6_ROUTER_PREF_INVALID = 0x2, /* ICMPV6_ROUTER_PREF_INVALID */ +} NMIcmpv6RouterPref; + +const char *nm_icmpv6_router_pref_to_string(NMIcmpv6RouterPref pref, char *buf, gsize len); + +/*****************************************************************************/ + +#endif /* __NM_INET_UTILS_H__ */ diff --git a/src/libnm-glib-aux/nm-io-utils.c b/src/libnm-glib-aux/nm-io-utils.c index 0823a16c..d1430595 100644 --- a/src/libnm-glib-aux/nm-io-utils.c +++ b/src/libnm-glib-aux/nm-io-utils.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "nm-str-buf.h" #include "nm-shared-utils.h" @@ -351,6 +352,7 @@ nm_utils_file_set_contents(const char *filename, int errsv; gssize s; int fd; + int r; g_return_val_if_fail(filename, FALSE); g_return_val_if_fail(contents || !length, FALSE); @@ -414,7 +416,16 @@ nm_utils_file_set_contents(const char *filename, tmp_name); } - nm_close(fd); + r = nm_close_with_error(fd); + if (r < 0) { + errsv = NM_ERRNO_NATIVE(-r); + unlink(tmp_name); + return _get_contents_error(error, + errsv, + out_errsv, + "failed close() after writing file %s", + tmp_name); + } if (rename(tmp_name, filename)) { errsv = NM_ERRNO_NATIVE(errno); @@ -496,6 +507,45 @@ nm_utils_fd_read(int fd, NMStrBuf *out_string) /*****************************************************************************/ +/* Taken from systemd's next_datagram_size_fd(). */ +gssize +nm_fd_next_datagram_size(int fd) +{ + gssize l; + int k; + + /* This is a bit like FIONREAD/SIOCINQ, however a bit more powerful. The difference being: recv(MSG_PEEK) will + * actually cause the next datagram in the queue to be validated regarding checksums, which FIONREAD doesn't + * do. This difference is actually of major importance as we need to be sure that the size returned here + * actually matches what we will read with recvmsg() next, as otherwise we might end up allocating a buffer of + * the wrong size. */ + + l = recv(fd, NULL, 0, MSG_PEEK | MSG_TRUNC); + if (l < 0) { + if (NM_IN_SET(errno, EOPNOTSUPP, EFAULT)) + goto fallback; + + return -errno; + } + if (l == 0) + goto fallback; + + return l; + +fallback: + k = 0; + + /* Some sockets (AF_PACKET) do not support null-sized recv() with MSG_TRUNC set, let's fall back to FIONREAD + * for them. Checksums don't matter for raw sockets anyway, hence this should be fine. */ + + if (ioctl(fd, FIONREAD, &k) < 0) + return -errno; + + return (gssize) k; +} + +/*****************************************************************************/ + typedef struct { GSubprocess *subprocess; GSource *timeout_source; diff --git a/src/libnm-glib-aux/nm-io-utils.h b/src/libnm-glib-aux/nm-io-utils.h index ef015153..54018140 100644 --- a/src/libnm-glib-aux/nm-io-utils.h +++ b/src/libnm-glib-aux/nm-io-utils.h @@ -52,10 +52,55 @@ struct _NMStrBuf; gssize nm_utils_fd_read(int fd, struct _NMStrBuf *out_string); +gssize nm_fd_next_datagram_size(int fd); + struct stat; int nm_utils_file_stat(const char *filename, struct stat *out_st); +/*****************************************************************************/ + +/* From systemd's ERRNO_IS_TRANSIENT(). + * + * For send()/recv() or read()/write(). */ +static inline gboolean +NM_ERRNO_IS_TRANSIENT(int r) +{ + return NM_IN_SET((r < 0 ? -r : r), EAGAIN, EINTR); +} + +/* From systemd's ERRNO_IS_DISCONNECT(). + * + * Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5. + * + * Hint #2: The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases. See the + * icmp_err_convert[] in net/ipv4/icmp.c in the kernel sources. + * + * Hint #3: When asynchronous connect() on TCP fails because the host never acknowledges a single packet, + * kernel tells us that with ETIMEDOUT, see tcp(7). */ +static inline gboolean +NM_ERRNO_IS_DISCONNECT(int r) +{ + return NM_IN_SET((r < 0 ? -r : r), + ECONNABORTED, + ECONNREFUSED, + ECONNRESET, + EHOSTDOWN, + EHOSTUNREACH, + ENETDOWN, + ENETRESET, + ENETUNREACH, + ENONET, + ENOPROTOOPT, + ENOTCONN, + EPIPE, + EPROTO, + ESHUTDOWN, + ETIMEDOUT); +} + +/*****************************************************************************/ + void nm_g_subprocess_terminate_in_background(GSubprocess *subprocess, int timeout_msec_before_kill); char **nm_utils_find_mkstemp_files(const char *dirname, const char *filename); diff --git a/src/libnm-glib-aux/nm-macros-internal.h b/src/libnm-glib-aux/nm-macros-internal.h index f2a4461e..71a17e3e 100644 --- a/src/libnm-glib-aux/nm-macros-internal.h +++ b/src/libnm-glib-aux/nm-macros-internal.h @@ -43,6 +43,27 @@ /*****************************************************************************/ +/* Historically, our cleanup macros come from a long gone library + * libgsystem, hence the "gs_" prefix. We still keep using them, + * although maybe we should drop them and use our respective nm_auto* + * macros (TODO). + * + * GLib also has g_auto() since 2.44. First of all, we still don't + * depend on 2.44, so we would have add compat implementations to + * "nm-glib.h" or bump the version. + * Also, they work differently (nm_auto_unref_hashtable vs g_auto(GHashTable)). + * If we were to switch to g_auto(), the change would be slightly more complicated + * than replacing one macro with another (but still easy). + * However, the reason for using our nm_auto* macros is that we also want cleanup + * macros in libnm-std-aux, which has no glib dependency. So we still would have + * some nm_auto* macros mixed with g_auto(). Instead, we consistently use + * nm_auto* macros (and the gs_* aliases). + * + * Note that c-stdaux also brings cleanup macros like _c_cleanup_(c_freep). + * We use c-stdaux like a proper internal library, so we could instead switch + * from nm_auto* macros to _c_cleanup_(). Unlike glib, c-stdaux is used by + * libnm-std-aux. Again, _c_cleanup_ follows a different pattern both from + * nm_auto* and g_auto(). */ #define gs_free nm_auto_g_free #define gs_unref_object nm_auto_unref_object #define gs_unref_variant nm_auto_unref_variant @@ -110,6 +131,9 @@ NM_AUTO_DEFINE_FCN_VOID0(void *, _nm_auto_unref_gtypeclass, g_type_class_unref); NM_AUTO_DEFINE_FCN0(GByteArray *, _nm_auto_unref_bytearray, g_byte_array_unref); #define nm_auto_unref_bytearray nm_auto(_nm_auto_unref_bytearray) +NM_AUTO_DEFINE_FCN0(GDateTime *, _nm_auto_unref_gdatetime, g_date_time_unref); +#define nm_auto_unref_gdatetime nm_auto(_nm_auto_unref_gdatetime) + static inline void _nm_auto_free_gstring(GString **str) { @@ -236,68 +260,40 @@ NM_G_ERROR_MSG(GError *error) #if _NM_CC_SUPPORT_GENERIC #define _NM_CONSTCAST_FULL_1(type, obj_expr, obj) \ (_Generic ((obj_expr), \ - const void *const: ((const type *) (obj)), \ - const void * : ((const type *) (obj)), \ - void *const: (( type *) (obj)), \ - void * : (( type *) (obj)), \ - const type *const: ((const type *) (obj)), \ - const type * : ((const type *) (obj)), \ - type *const: (( type *) (obj)), \ - type * : (( type *) (obj)))) + const void *: ((const type *) (obj)), \ + void *: (( type *) (obj)), \ + const type *: ((const type *) (obj)), \ + type *: (( type *) (obj)))) #define _NM_CONSTCAST_FULL_2(type, obj_expr, obj, alias_type2) \ (_Generic ((obj_expr), \ - const void *const: ((const type *) (obj)), \ - const void * : ((const type *) (obj)), \ - void *const: (( type *) (obj)), \ - void * : (( type *) (obj)), \ - const alias_type2 *const: ((const type *) (obj)), \ - const alias_type2 * : ((const type *) (obj)), \ - alias_type2 *const: (( type *) (obj)), \ - alias_type2 * : (( type *) (obj)), \ - const type *const: ((const type *) (obj)), \ - const type * : ((const type *) (obj)), \ - type *const: (( type *) (obj)), \ - type * : (( type *) (obj)))) + const void *: ((const type *) (obj)), \ + void *: (( type *) (obj)), \ + const alias_type2 *: ((const type *) (obj)), \ + alias_type2 *: (( type *) (obj)), \ + const type *: ((const type *) (obj)), \ + type *: (( type *) (obj)))) #define _NM_CONSTCAST_FULL_3(type, obj_expr, obj, alias_type2, alias_type3) \ (_Generic ((obj_expr), \ - const void *const: ((const type *) (obj)), \ - const void * : ((const type *) (obj)), \ - void *const: (( type *) (obj)), \ - void * : (( type *) (obj)), \ - const alias_type2 *const: ((const type *) (obj)), \ - const alias_type2 * : ((const type *) (obj)), \ - alias_type2 *const: (( type *) (obj)), \ - alias_type2 * : (( type *) (obj)), \ - const alias_type3 *const: ((const type *) (obj)), \ - const alias_type3 * : ((const type *) (obj)), \ - alias_type3 *const: (( type *) (obj)), \ - alias_type3 * : (( type *) (obj)), \ - const type *const: ((const type *) (obj)), \ - const type * : ((const type *) (obj)), \ - type *const: (( type *) (obj)), \ - type * : (( type *) (obj)))) + const void *: ((const type *) (obj)), \ + void *: (( type *) (obj)), \ + const alias_type2 *: ((const type *) (obj)), \ + alias_type2 *: (( type *) (obj)), \ + const alias_type3 *: ((const type *) (obj)), \ + alias_type3 *: (( type *) (obj)), \ + const type *: ((const type *) (obj)), \ + type *: (( type *) (obj)))) #define _NM_CONSTCAST_FULL_4(type, obj_expr, obj, alias_type2, alias_type3, alias_type4) \ (_Generic ((obj_expr), \ - const void *const: ((const type *) (obj)), \ - const void * : ((const type *) (obj)), \ - void *const: (( type *) (obj)), \ - void * : (( type *) (obj)), \ - const alias_type2 *const: ((const type *) (obj)), \ - const alias_type2 * : ((const type *) (obj)), \ - alias_type2 *const: (( type *) (obj)), \ - alias_type2 * : (( type *) (obj)), \ - const alias_type3 *const: ((const type *) (obj)), \ - const alias_type3 * : ((const type *) (obj)), \ - alias_type3 *const: (( type *) (obj)), \ - alias_type3 * : (( type *) (obj)), \ - const alias_type4 *const: ((const type *) (obj)), \ - const alias_type4 * : ((const type *) (obj)), \ - alias_type4 *const: (( type *) (obj)), \ - alias_type4 * : (( type *) (obj)), \ - const type *const: ((const type *) (obj)), \ - const type * : ((const type *) (obj)), \ - type *const: (( type *) (obj)), \ - type * : (( type *) (obj)))) + const void *: ((const type *) (obj)), \ + void *: (( type *) (obj)), \ + const alias_type2 *: ((const type *) (obj)), \ + alias_type2 *: (( type *) (obj)), \ + const alias_type3 *: ((const type *) (obj)), \ + alias_type3 *: (( type *) (obj)), \ + const alias_type4 *: ((const type *) (obj)), \ + alias_type4 *: (( type *) (obj)), \ + const type *: ((const type *) (obj)), \ + type *: (( type *) (obj)))) #define _NM_CONSTCAST_FULL_x(type, obj_expr, obj, n, ...) \ (_NM_CONSTCAST_FULL_##n(type, obj_expr, obj, ##__VA_ARGS__)) #define _NM_CONSTCAST_FULL_y(type, obj_expr, obj, n, ...) \ @@ -393,13 +389,7 @@ NM_G_ERROR_MSG(GError *error) char *const*: (const char *const*) (value), \ char * *: (const char *const*) (value), \ const void *: (const char *const*) (value), \ - void *: (const char *const*) (value), \ - const char *const*const: (const char *const*) (value), \ - const char * *const: (const char *const*) (value), \ - char *const*const: (const char *const*) (value), \ - char * *const: (const char *const*) (value), \ - const void *const: (const char *const*) (value), \ - void *const: (const char *const*) (value))) + void *: (const char *const*) (value))) #else #define NM_CAST_STRV_MC(value) ((const char **) (value)) #define NM_CAST_STRV_CC(value) ((const char *const *) (value)) @@ -495,6 +485,17 @@ nm_strdup_not_empty(const char *str) return !nm_str_is_empty(str) ? g_strdup(str) : NULL; } +static inline char * +nm_str_truncate(char *str) +{ + /* This is trivial, and is only useful in a macro, to + * ensure that we access the macro argument only once. */ + nm_assert(str); + + str[0] = '\0'; + return str; +} + static inline char * nm_str_realloc(char *str) { @@ -527,16 +528,27 @@ nm_str_realloc(char *str) /*****************************************************************************/ /* redefine assertions to use g_assert*() */ -#undef _nm_assert_call -#undef _nm_assert_call_not_reached -#define _nm_assert_call(cond) g_assert(cond) -#define _nm_assert_call_not_reached() g_assert_not_reached() +#undef _nm_assert_fail +#define _nm_assert_fail(msg) \ + G_STMT_START \ + { \ + g_assertion_message_expr(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg); \ + _nm_unreachable_code(); \ + } \ + G_STMT_END + +#undef _NM_ASSERT_FAIL_ENABLED +#ifndef G_DISABLE_ASSERT +#define _NM_ASSERT_FAIL_ENABLED 1 +#else +#define _NM_ASSERT_FAIL_ENABLED 0 +#endif /* Usage: * * if (NM_MORE_ASSERT_ONCE (5)) { extra_check (); } * - * This will only run the check once, and only if NM_MORE_ASSERT is >= than + * This will only run the check once, and only if NM_MORE_ASSERTS is >= than * more_assert_level. */ #define NM_MORE_ASSERT_ONCE(more_assert_level) \ @@ -652,6 +664,24 @@ nm_str_realloc(char *str) /*****************************************************************************/ +/* Unfortunately, G_TYPE_CHECK_INSTANCE_CAST() just does a direct cast, + * which can trigger a "-Wcast-align" warning, especially when casting + * a GObject pointer to the desired type. + * + * _NM_G_TYPE_CHECK_INSTANCE_CAST() avoids that warning. + * + * Since the entire point of G_TYPE_CHECK_INSTANCE_CAST_() is to do a + * runtime check on the type (with conditional assertions via G_DISABLE_CAST_CHECKS), + * we already assert that the gtype is right, and the alignment is also + * expected to be right. + * + * See https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3139 + */ +#define _NM_G_TYPE_CHECK_INSTANCE_CAST(instance, g_type, c_type) \ + G_TYPE_CHECK_INSTANCE_CAST(((void *) (instance)), (g_type), c_type) + +/*****************************************************************************/ + static inline gpointer nm_g_object_ref(gpointer obj) { @@ -1003,15 +1033,15 @@ nm_g_variant_equal(GVariant *a, GVariant *b) const typeof(flags) _flags = (flags); \ const typeof(flags) _val = (val); \ \ - _flags &(~_val); \ + _flags & (~_val); \ }) -#define NM_FLAGS_ASSIGN(flags, val, assign) \ - ({ \ - const typeof(flags) _flags = (flags); \ - const typeof(flags) _val = (val); \ - \ - (assign) ? _flags | (_val) : _flags &(~_val); \ +#define NM_FLAGS_ASSIGN(flags, val, assign) \ + ({ \ + const typeof(flags) _flags = (flags); \ + const typeof(flags) _val = (val); \ + \ + (assign) ? _flags | (_val) : _flags & (~_val); \ }) #define NM_FLAGS_ASSIGN_MASK(flags, mask, val) \ @@ -1071,6 +1101,32 @@ nm_ascii_is_newline(char ch) return NM_IN_SET(ch, '\n', '\t'); } +static inline gboolean +nm_ascii_is_regular_char(char ch) +{ + /* Checks whether "ch" is "regular", which basically + * means it's either a digit, a alpha, or some special + * characters that are suitable for base64 encoding. + * + * The meaning of what "regular" means is not well defined, + * but it's used to validate the keys for "ovs.external-ids" + * dictionary. */ + switch (ch) { + case 'a' ... 'z': + case 'A' ... 'Z': + case '0' ... '9': + case '-': + case '_': + case '+': + case '/': + case '=': + case '.': + return TRUE; + default: + return FALSE; + } +} + #define nm_str_skip_leading_spaces(str) \ ({ \ typeof(*(str)) *_str_sls = (str); \ @@ -1276,6 +1332,36 @@ nm_memdup(gconstpointer data, gsize size) return p; } +static inline gpointer +nm_memdup_nul(gconstpointer data, gsize size) +{ + gpointer p; + + /* Like systemd's memdup_suffix0() and kernel's kmemdup_nul(). + * + * This: + * - never returns NULL + * - always has one NUL byte after the @size data. Thus, + * the actually allocated buffer is size+1. + * + * This is like nm_memdup() except: + * - never returns NULL + * - always returns one NUL character appended to the data. + * + * This is like g_strndup(), except + * - never returns NULL. + * - g_strndup() treats the src pointer as a NUL terminated string, + * so if src is shorter than size, the rest is filled with padding. + * Essentially, it uses strncpy() to copy the input which does the + * truncation. If @data contains no NUL byte in teh first @size bytes, + * it behaves the same as g_strndup(). */ + + p = g_malloc(size + 1u); + nm_memcpy(p, data, size); + ((char *) p)[size] = '\0'; + return p; +} + #define nm_malloc_maybe_a(alloca_maxlen, bytes, to_free) \ ({ \ const gsize _bytes = (bytes); \ diff --git a/src/libnm-glib-aux/nm-prioq.c b/src/libnm-glib-aux/nm-prioq.c new file mode 100644 index 00000000..3448dcd9 --- /dev/null +++ b/src/libnm-glib-aux/nm-prioq.c @@ -0,0 +1,323 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +/* + * Taken from systemd's Prioq. + * + * Priority Queue + * The prioq object implements a priority queue. That is, it orders objects by + * their priority and allows O(1) access to the object with the highest + * priority. Insertion and removal are Θ(log n). Optionally, the caller can + * provide a pointer to an index which will be kept up-to-date by the prioq. + * + * The underlying algorithm used in this implementation is a Heap. + */ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nm-prioq.h" + +#include +#include + +/*****************************************************************************/ + +struct _NMPrioqItem { + void *data; + unsigned *idx; +}; + +/*****************************************************************************/ + +void +nm_prioq_init(NMPrioq *q, GCompareFunc compare_func) +{ + nm_assert(q); + nm_assert(compare_func); + + *q = (NMPrioq){ + ._priv = + { + .compare_func = compare_func, + .compare_data = NULL, + .compare_with_data = FALSE, + .items = NULL, + .n_items = 0, + .n_allocated = 0, + }, + }; +} + +void +nm_prioq_init_with_data(NMPrioq *q, GCompareDataFunc compare_func, gpointer compare_data) +{ + nm_assert(q); + nm_assert(compare_func); + + *q = (NMPrioq){ + ._priv = + { + .compare_data_func = compare_func, + .compare_data = compare_data, + .compare_with_data = TRUE, + .items = NULL, + .n_items = 0, + .n_allocated = 0, + }, + }; +} + +void +nm_prioq_destroy(NMPrioq *q) +{ + if (!q || !q->_priv.compare_func) + return; + + free(q->_priv.items); + q->_priv.compare_func = NULL; +} + +/*****************************************************************************/ + +static int +compare(NMPrioq *q, unsigned a, unsigned b) +{ + nm_assert(q); + nm_assert(q->_priv.compare_func); + nm_assert(a != b); + nm_assert(a < q->_priv.n_items); + nm_assert(b < q->_priv.n_items); + + if (q->_priv.compare_with_data) { + return q->_priv.compare_data_func(q->_priv.items[a].data, + q->_priv.items[b].data, + q->_priv.compare_data); + } + + return q->_priv.compare_func(q->_priv.items[a].data, q->_priv.items[b].data); +} + +static void +swap(NMPrioq *q, unsigned j, unsigned k) +{ + nm_assert(q); + nm_assert(j < q->_priv.n_items); + nm_assert(k < q->_priv.n_items); + + nm_assert(!q->_priv.items[j].idx || *(q->_priv.items[j].idx) == j); + nm_assert(!q->_priv.items[k].idx || *(q->_priv.items[k].idx) == k); + + NM_SWAP(&q->_priv.items[j].data, &q->_priv.items[k].data); + NM_SWAP(&q->_priv.items[j].idx, &q->_priv.items[k].idx); + + if (q->_priv.items[j].idx) + *q->_priv.items[j].idx = j; + + if (q->_priv.items[k].idx) + *q->_priv.items[k].idx = k; +} + +static unsigned +shuffle_up(NMPrioq *q, unsigned idx) +{ + nm_assert(q); + nm_assert(idx < q->_priv.n_items); + + while (idx > 0) { + unsigned k; + + k = (idx - 1) / 2; + + if (compare(q, k, idx) <= 0) + break; + + swap(q, idx, k); + idx = k; + } + + return idx; +} + +static unsigned +shuffle_down(NMPrioq *q, unsigned idx) +{ + nm_assert(q); + + for (;;) { + unsigned j; + unsigned k; + unsigned s; + + k = (idx + 1) * 2; /* right child */ + j = k - 1; /* left child */ + + if (j >= q->_priv.n_items) + break; + + if (compare(q, j, idx) < 0) { + /* So our left child is smaller than we are, let's + * remember this fact */ + s = j; + } else + s = idx; + + if ((k < q->_priv.n_items) && compare(q, k, s) < 0) { + /* So our right child is smaller than we are, let's + * remember this fact */ + s = k; + } + + /* s now points to the smallest of the three items */ + + if (s == idx) + /* No swap necessary, we're done */ + break; + + swap(q, idx, s); + idx = s; + } + + return idx; +} + +void +nm_prioq_put(NMPrioq *q, void *data, unsigned *idx) +{ + unsigned k; + + nm_assert(q); + + if (q->_priv.n_items >= q->_priv.n_allocated) { + q->_priv.n_allocated = NM_MAX((q->_priv.n_items + 1u) * 2u, 16u); + q->_priv.items = g_renew(struct _NMPrioqItem, q->_priv.items, q->_priv.n_allocated); + } + + k = q->_priv.n_items++; + + q->_priv.items[k] = (struct _NMPrioqItem){ + .data = data, + .idx = idx, + }; + if (idx) + *idx = k; + + shuffle_up(q, k); +} + +static void +remove_item(NMPrioq *q, struct _NMPrioqItem *i) +{ + struct _NMPrioqItem *l; + unsigned k; + + nm_assert(q); + nm_assert(i); + nm_assert(q->_priv.n_items > 0); + nm_assert(i >= q->_priv.items); + nm_assert(i < &q->_priv.items[q->_priv.n_items]); + + l = &q->_priv.items[q->_priv.n_items - 1u]; + + if (i == l) { + /* Last entry, let's just remove it */ + q->_priv.n_items--; + return; + } + + /* Not last entry, let's replace the last entry with + * this one, and reshuffle */ + k = i - q->_priv.items; + + *i = *l; + if (i->idx) + *i->idx = k; + q->_priv.n_items--; + + k = shuffle_down(q, k); + shuffle_up(q, k); +} + +_nm_pure static struct _NMPrioqItem * +find_item(NMPrioq *q, void *data, unsigned *idx) +{ + struct _NMPrioqItem *i; + + nm_assert(q); + + if (q->_priv.n_items <= 0) + return NULL; + + if (idx) { + if (*idx == NM_PRIOQ_IDX_NULL || *idx >= q->_priv.n_items) + return NULL; + + i = &q->_priv.items[*idx]; + if (i->data == data) + return i; + } else { + for (i = q->_priv.items; i < &q->_priv.items[q->_priv.n_items]; i++) { + if (i->data == data) + return i; + } + } + + return NULL; +} + +gboolean +nm_prioq_remove(NMPrioq *q, void *data, unsigned *idx) +{ + struct _NMPrioqItem *i; + + nm_assert(q); + + i = find_item(q, data, idx); + if (!i) + return FALSE; + + remove_item(q, i); + return TRUE; +} + +gboolean +nm_prioq_reshuffle(NMPrioq *q, void *data, unsigned *idx) +{ + struct _NMPrioqItem *i; + unsigned k; + + nm_assert(q); + + i = find_item(q, data, idx); + if (!i) + return FALSE; + + k = i - q->_priv.items; + k = shuffle_down(q, k); + shuffle_up(q, k); + return TRUE; +} + +void * +nm_prioq_peek_by_index(NMPrioq *q, unsigned idx) +{ + nm_assert(q); + + if (idx >= q->_priv.n_items) + return NULL; + + return q->_priv.items[idx].data; +} + +void * +nm_prioq_pop(NMPrioq *q) +{ + void *data; + + nm_assert(q); + + if (q->_priv.n_items <= 0) + return NULL; + + data = q->_priv.items[0].data; + remove_item(q, &q->_priv.items[0]); + return data; +} diff --git a/src/libnm-glib-aux/nm-prioq.h b/src/libnm-glib-aux/nm-prioq.h new file mode 100644 index 00000000..918c6447 --- /dev/null +++ b/src/libnm-glib-aux/nm-prioq.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef __NM_PRIOQ_H__ +#define __NM_PRIOQ_H__ + +#define NM_PRIOQ_IDX_NULL G_MAXUINT + +struct _NMPrioqItem; + +typedef struct { + struct { + union { + GCompareDataFunc compare_data_func; + GCompareFunc compare_func; + }; + + gpointer compare_data; + + struct _NMPrioqItem *items; + + unsigned n_items; + unsigned n_allocated; + + bool compare_with_data; + } _priv; +} NMPrioq; + +#define NM_PRIOQ_ZERO \ + { \ + ._priv = { \ + .compare_func = NULL, \ + }, \ + } + +void nm_prioq_init(NMPrioq *q, GCompareFunc compare_func); +void nm_prioq_init_with_data(NMPrioq *q, GCompareDataFunc compare_func, gpointer compare_data); + +void nm_prioq_destroy(NMPrioq *q); + +#define nm_auto_prioq nm_auto(nm_prioq_destroy) + +void nm_prioq_put(NMPrioq *q, void *data, unsigned *idx); +gboolean nm_prioq_remove(NMPrioq *q, void *data, unsigned *idx); +gboolean nm_prioq_reshuffle(NMPrioq *q, void *data, unsigned *idx); + +void *nm_prioq_peek_by_index(NMPrioq *q, unsigned idx) _nm_pure; + +static inline void * +nm_prioq_peek(NMPrioq *q) +{ + return nm_prioq_peek_by_index(q, 0); +} + +void *nm_prioq_pop(NMPrioq *q); + +#define NM_PRIOQ_FOREACH_ITEM(q, p) for (unsigned _i = 0; (p = nm_prioq_peek_by_index(q, _i)); _i++) + +_nm_pure static inline unsigned +nm_prioq_size(NMPrioq *q) +{ + nm_assert(q); + return q->_priv.n_items; +} + +_nm_pure static inline gboolean +nm_prioq_isempty(NMPrioq *q) +{ + return nm_prioq_size(q) <= 0; +} + +#endif /* __NM_PRIOQ_H__ */ diff --git a/src/libnm-glib-aux/nm-ref-string.c b/src/libnm-glib-aux/nm-ref-string.c index fec5d60e..27a6c938 100644 --- a/src/libnm-glib-aux/nm-ref-string.c +++ b/src/libnm-glib-aux/nm-ref-string.c @@ -157,8 +157,7 @@ nm_ref_string_new_len(const char *cstr, gsize len) g_atomic_int_inc(&rstr->_ref_count); } else { rstr = g_malloc((G_STRUCT_OFFSET(NMRefString, str) + 1u) + len); - if (len > 0) - memcpy((char *) rstr->str, cstr, len); + nm_memcpy((char *) rstr->str, cstr, len); ((char *) rstr->str)[len] = '\0'; *((gsize *) &rstr->len) = len; rstr->_ref_count = 1; diff --git a/src/libnm-glib-aux/nm-ref-string.h b/src/libnm-glib-aux/nm-ref-string.h index eb3c38de..1b0cabf2 100644 --- a/src/libnm-glib-aux/nm-ref-string.h +++ b/src/libnm-glib-aux/nm-ref-string.h @@ -93,6 +93,23 @@ nm_ref_string_unref(NMRefString *rstr) NM_AUTO_DEFINE_FCN_VOID(NMRefString *, _nm_auto_ref_string, nm_ref_string_unref); #define nm_auto_ref_string nm_auto(_nm_auto_ref_string) +static inline gboolean +nm_ref_string_reset(NMRefString **ptr, NMRefString *str) +{ + NMRefString *rstr; + + nm_assert(ptr); + + rstr = *ptr; + + if (rstr == str) + return FALSE; + + *ptr = nm_ref_string_ref(str); + nm_ref_string_unref(rstr); + return TRUE; +} + /*****************************************************************************/ static inline const char * @@ -139,7 +156,7 @@ nm_ref_string_equal_str(NMRefString *rstr, const char *str) /* We don't use streq() here, because an NMRefString might have embedded NUL characters * (as the length is tracked separately). The NUL terminated C string @str must not * compare equal to such a @rstr, thus we first explicitly check strlen(). */ - return rstr->len == strlen(str) && (rstr->str == str || memcmp(rstr->str, str, rstr->len) == 0); + return rstr->str == str || (rstr->len == strlen(str) && memcmp(rstr->str, str, rstr->len) == 0); } static inline gboolean diff --git a/src/libnm-glib-aux/nm-secret-utils.c b/src/libnm-glib-aux/nm-secret-utils.c index 983b04ca..ffab2479 100644 --- a/src/libnm-glib-aux/nm-secret-utils.c +++ b/src/libnm-glib-aux/nm-secret-utils.c @@ -39,24 +39,10 @@ nm_explicit_bzero(void *s, gsize n) void nm_free_secret(char *secret) { - gsize len; - if (!secret) return; -#if GLIB_CHECK_VERSION(2, 44, 0) - /* Here we mix malloc() and g_malloc() API. Usually we avoid this, - * however since glib 2.44.0 we are in fact guaranteed that g_malloc()/g_free() - * just wraps malloc()/free(), so this is actually fine. - * - * See https://gitlab.gnome.org/GNOME/glib/commit/3be6ed60aa58095691bd697344765e715a327fc1 - */ - len = malloc_usable_size(secret); -#else - len = strlen(secret); -#endif - - nm_explicit_bzero(secret, len); + nm_explicit_bzero(secret, strlen(secret)); g_free(secret); } diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c index aca7d708..702a63e9 100644 --- a/src/libnm-glib-aux/nm-shared-utils.c +++ b/src/libnm-glib-aux/nm-shared-utils.c @@ -3,6 +3,8 @@ * Copyright (C) 2016 Red Hat, Inc. */ +#define NM_WANT_NM_ARRAY_FIND_BSEARCH_INLINE + #include "libnm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-shared-utils.h" @@ -35,111 +37,6 @@ const void *const _NM_PTRARRAY_EMPTY[1] = {NULL}; /*****************************************************************************/ -const NMIPAddr nm_ip_addr_zero = {}; - -/* We use _nm_alignas(NMIPAddr) to ensure that fields for in_addr_t and - * struct in6_addr have all the same alignment. Ensure that this is suitable. */ -G_STATIC_ASSERT(_nm_alignof(in_addr_t) <= _nm_alignof(NMIPAddr)); -G_STATIC_ASSERT(_nm_alignof(struct in_addr) <= _nm_alignof(NMIPAddr)); -G_STATIC_ASSERT(_nm_alignof(struct in6_addr) <= _nm_alignof(NMIPAddr)); -G_STATIC_ASSERT(_nm_alignof(NMEtherAddr) <= _nm_alignof(NMIPAddr)); - -int -nm_ip_addr_cmp_for_sort(gconstpointer a, gconstpointer b, gpointer user_data) -{ - /* This is a compare function that can be used for sorting IP addresses. - * Essentially, it calls memcmp(). @user_data must be GINT_TO_POINTER(addr_family). - * @a and @b must be either pointers to in_addr_t, struct in6_addr or NMIPAddr. */ - return nm_ip_addr_cmp(GPOINTER_TO_INT(user_data), a, b); -} - -/* this initializes a struct in_addr/in6_addr and allows for untrusted - * arguments (like unsuitable @addr_family or @src_len). It's almost safe - * in the sense that it verifies input arguments strictly. Also, it - * uses memcpy() to access @src, so alignment is not an issue. - * - * Only potential pitfalls: - * - * - it allows for @addr_family to be AF_UNSPEC. If that is the case (and the - * caller allows for that), the caller MUST provide @out_addr_family. - * - when setting @dst to an IPv4 address, the trailing bytes are not touched. - * Meaning, if @dst is an NMIPAddr union, only the first bytes will be set. - * If that matter to you, clear @dst before. */ -gboolean -nm_ip_addr_set_from_untrusted(int addr_family, - gpointer dst, - gconstpointer src, - gsize src_len, - int *out_addr_family) -{ - nm_assert(dst); - - switch (addr_family) { - case AF_UNSPEC: - if (!out_addr_family) { - /* when the callers allow undefined @addr_family, they must provide - * an @out_addr_family argument. */ - nm_assert_not_reached(); - return FALSE; - } - switch (src_len) { - case sizeof(struct in_addr): - addr_family = AF_INET; - break; - case sizeof(struct in6_addr): - addr_family = AF_INET6; - break; - default: - return FALSE; - } - break; - case AF_INET: - if (src_len != sizeof(struct in_addr)) - return FALSE; - break; - case AF_INET6: - if (src_len != sizeof(struct in6_addr)) - return FALSE; - break; - default: - /* when the callers allow undefined @addr_family, they must provide - * an @out_addr_family argument. */ - nm_assert(out_addr_family); - return FALSE; - } - - nm_assert(src); - - memcpy(dst, src, src_len); - NM_SET_OUT(out_addr_family, addr_family); - return TRUE; -} - -gboolean -nm_ip_addr_set_from_variant(int addr_family, gpointer dst, GVariant *variant, int *out_addr_family) -{ - gconstpointer bytes; - gsize len; - - g_return_val_if_fail(dst, FALSE); - g_return_val_if_fail(variant, FALSE); - - /* This function always expects IP addressea a byte arrays ("ay"). Note that - * several NetworkManager API uses "u" (32 bit unsigned intergers) for IPv4 addresses. - * So this function won't work in those cases. - * - * Btw, using "u" for IPv4 address messes badly with the endianness (host - * vs network byte order). Don't do that. - */ - g_return_val_if_fail(g_variant_is_of_type(variant, G_VARIANT_TYPE("ay")), FALSE); - - bytes = g_variant_get_fixed_array(variant, &len, sizeof(guint8)); - - return nm_ip_addr_set_from_untrusted(addr_family, dst, bytes, len, out_addr_family); -} - -/*****************************************************************************/ - G_STATIC_ASSERT(ETH_ALEN == sizeof(struct ether_addr)); G_STATIC_ASSERT(ETH_ALEN == 6); G_STATIC_ASSERT(ETH_ALEN == sizeof(NMEtherAddr)); @@ -158,6 +55,7 @@ nm_ether_addr_from_string(NMEtherAddr *addr, const char *str) return addr; } + /*****************************************************************************/ /** @@ -183,37 +81,6 @@ _nm_utils_inet6_is_token(const struct in6_addr *in6addr) return FALSE; } -/** - * nm_utils_ipv6_addr_set_interface_identifier: - * @addr: output token encoded as %in6_addr - * @iid: %NMUtilsIPv6IfaceId interface identifier - * - * Converts the %NMUtilsIPv6IfaceId to an %in6_addr (suitable for use - * with Linux platform). This only copies the lower 8 bytes, ignoring - * the /64 network prefix which is expected to be all-zero for a valid - * token. - */ -void -nm_utils_ipv6_addr_set_interface_identifier(struct in6_addr *addr, const NMUtilsIPv6IfaceId *iid) -{ - memcpy(addr->s6_addr + 8, &iid->id_u8, 8); -} - -/** - * nm_utils_ipv6_interface_identifier_get_from_addr: - * @iid: output %NMUtilsIPv6IfaceId interface identifier set from the token - * @addr: token encoded as %in6_addr - * - * Converts the %in6_addr encoded token (as used by Linux platform) to - * the interface identifier. - */ -void -nm_utils_ipv6_interface_identifier_get_from_addr(NMUtilsIPv6IfaceId *iid, - const struct in6_addr *addr) -{ - memcpy(iid, addr->s6_addr + 8, 8); -} - /** * nm_utils_ipv6_interface_identifier_get_from_token: * @iid: output %NMUtilsIPv6IfaceId interface identifier set from the token @@ -244,7 +111,7 @@ nm_utils_ipv6_interface_identifier_get_from_token(NMUtilsIPv6IfaceId *iid, const /** * nm_utils_inet6_interface_identifier_to_token: * @iid: %NMUtilsIPv6IfaceId interface identifier - * @buf: the destination buffer of at least %NM_UTILS_INET_ADDRSTRLEN + * @buf: the destination buffer of at least %NM_INET_ADDRSTRLEN * bytes. * * Converts the interface identifier to a string token. @@ -261,7 +128,7 @@ nm_utils_inet6_interface_identifier_to_token(const NMUtilsIPv6IfaceId *iid, nm_assert(buf); nm_utils_ipv6_addr_set_interface_identifier(&i6_token, iid); - return _nm_utils_inet6_ntop(&i6_token, buf); + return nm_inet6_ntop(&i6_token, buf); } /*****************************************************************************/ @@ -981,293 +848,6 @@ nm_utils_flags2str(const NMUtilsFlags2StrDesc *descs, /*****************************************************************************/ -guint32 -_nm_utils_ip4_get_default_prefix0(in_addr_t ip) -{ - /* The function is originally from ipcalc.c of Red Hat's initscripts. */ - switch (ntohl(ip) >> 24) { - case 0 ... 127: - return 8; /* Class A */ - case 128 ... 191: - return 16; /* Class B */ - case 192 ... 223: - return 24; /* Class C */ - } - return 0; -} - -guint32 -_nm_utils_ip4_get_default_prefix(in_addr_t ip) -{ - return _nm_utils_ip4_get_default_prefix0(ip) ?: 24; -} - -gboolean -nm_utils_ip_is_site_local(int addr_family, const void *address) -{ - in_addr_t addr4; - - switch (addr_family) { - case AF_INET: - /* RFC1918 private addresses - * 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 */ - addr4 = ntohl(*((const in_addr_t *) address)); - return (addr4 & 0xff000000) == 0x0a000000 || (addr4 & 0xfff00000) == 0xac100000 - || (addr4 & 0xffff0000) == 0xc0a80000; - case AF_INET6: - /* IN6_IS_ADDR_SITELOCAL() is for deprecated fec0::/10 addresses (see rfc3879, 4.). - * Note that for unique local IPv6 addresses (ULA, fc00::/7) this returns false, - * which may or may not be a bug. */ - return IN6_IS_ADDR_SITELOCAL(address); - default: - g_return_val_if_reached(FALSE); - } -} - -gboolean -nm_utils_ip6_is_ula(const struct in6_addr *address) -{ - /* Unique local IPv6 address (ULA) fc00::/7 */ - return (address->s6_addr32[0] & htonl(0xfe000000u)) == htonl(0xfc000000u); -} - -/*****************************************************************************/ - -static gboolean -_parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error) -{ - gs_free char *s_free = NULL; - struct in_addr a1; - guint8 bin[sizeof(a1)]; - char *s; - int i; - - if (inet_aton(text, &a1) != 1) { - g_set_error_literal(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "address invalid according to inet_aton()"); - return FALSE; - } - - /* OK, inet_aton() accepted the format. That's good, because we want - * to accept IPv4 addresses in octal format, like 255.255.000.000. - * That's what "legacy" means here. inet_pton() doesn't accept those. - * - * But inet_aton() also ignores trailing garbage and formats with fewer than - * 4 digits. That is just too crazy and we don't do that. Perform additional checks - * and reject some forms that inet_aton() accepted. - * - * Note that we still should (of course) accept everything that inet_pton() - * accepts. However this code never gets called if inet_pton() succeeds - * (see below, aside the assertion code). */ - - if (NM_STRCHAR_ANY(text, ch, (!(ch >= '0' && ch <= '9') && !NM_IN_SET(ch, '.', 'x')))) { - /* We only accepts '.', digits, and 'x' for "0x". */ - g_set_error_literal(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "contains an invalid character"); - return FALSE; - } - - s = nm_memdup_maybe_a(300, text, strlen(text) + 1, &s_free); - - for (i = 0; i < G_N_ELEMENTS(bin); i++) { - char *current_token = s; - gint32 v; - - s = strchr(s, '.'); - if (s) { - s[0] = '\0'; - s++; - } - - if ((i == G_N_ELEMENTS(bin) - 1) != (s == NULL)) { - /* Exactly for the last digit, we expect to have no more following token. - * But this isn't the case. Abort. */ - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "wrong number of tokens (index %d, token '%s')", - i, - s); - return FALSE; - } - - v = _nm_utils_ascii_str_to_int64(current_token, 0, 0, 0xFF, -1); - if (v == -1) { - int errsv = errno; - - /* we do accept octal and hex (even with leading "0x"). But something - * about this token is wrong. */ - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "invalid token '%s': %s (%d)", - current_token, - nm_strerror_native(errsv), - errsv); - return FALSE; - } - - bin[i] = v; - } - - if (memcmp(bin, &a1, sizeof(bin)) != 0) { - /* our parsing did not agree with what inet_aton() gave. Something - * is wrong. Abort. */ - g_set_error( - error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "inet_aton() result 0x%08x differs from computed value 0x%02hhx%02hhx%02hhx%02hhx", - a1.s_addr, - bin[0], - bin[1], - bin[2], - bin[3]); - return FALSE; - } - - *out_addr = a1.s_addr; - return TRUE; -} - -gboolean -nm_utils_parse_inaddr_bin_full(int addr_family, - gboolean accept_legacy, - const char *text, - int *out_addr_family, - gpointer out_addr) -{ - NMIPAddr addrbin; - - g_return_val_if_fail(text, FALSE); - - if (addr_family == AF_UNSPEC) { - g_return_val_if_fail(!out_addr || out_addr_family, FALSE); - addr_family = strchr(text, ':') ? AF_INET6 : AF_INET; - } else - g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE); - - if (inet_pton(addr_family, text, &addrbin) != 1) { - if (accept_legacy && addr_family == AF_INET - && _parse_legacy_addr4(text, &addrbin.addr4, NULL)) { - /* The address is in some legacy format which inet_aton() accepts, but not inet_pton(). - * Most likely octal digits (leading zeros). We accept the address. */ - } else - return FALSE; - } - -#if NM_MORE_ASSERTS > 10 - if (addr_family == AF_INET) { - NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER - gs_free_error GError *error = NULL; - in_addr_t a; - - /* The legacy parser should accept everything that inet_pton() accepts too. Meaning, - * it should strictly parse *more* formats. And of course, parse it the same way. */ - if (!_parse_legacy_addr4(text, &a, &error)) { - char buf[INET_ADDRSTRLEN]; - - g_error("unexpected assertion failure: could parse \"%s\" as %s, but not accepted by " - "legacy parser: %s", - text, - _nm_utils_inet4_ntop(addrbin.addr4, buf), - error->message); - } - nm_assert(addrbin.addr4 == a); - NM_PRAGMA_WARNING_REENABLE - } -#endif - - NM_SET_OUT(out_addr_family, addr_family); - if (out_addr) - nm_ip_addr_set(addr_family, out_addr, &addrbin); - return TRUE; -} - -gboolean -nm_utils_parse_inaddr(int addr_family, const char *text, char **out_addr) -{ - NMIPAddr addrbin; - char addrstr_buf[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)]; - - g_return_val_if_fail(text, FALSE); - - if (addr_family == AF_UNSPEC) - addr_family = strchr(text, ':') ? AF_INET6 : AF_INET; - else - g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE); - - if (inet_pton(addr_family, text, &addrbin) != 1) - return FALSE; - - NM_SET_OUT(out_addr, - g_strdup(inet_ntop(addr_family, &addrbin, addrstr_buf, sizeof(addrstr_buf)))); - return TRUE; -} - -gboolean -nm_utils_parse_inaddr_prefix_bin(int addr_family, - const char *text, - int *out_addr_family, - gpointer out_addr, - int *out_prefix) -{ - gs_free char *addrstr_free = NULL; - int prefix = -1; - const char *slash; - const char *addrstr; - NMIPAddr addrbin; - - g_return_val_if_fail(text, FALSE); - - if (addr_family == AF_UNSPEC) { - g_return_val_if_fail(!out_addr || out_addr_family, FALSE); - addr_family = strchr(text, ':') ? AF_INET6 : AF_INET; - } else - g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE); - - slash = strchr(text, '/'); - if (slash) - addrstr = nm_strndup_a(300, text, slash - text, &addrstr_free); - else - addrstr = text; - - if (inet_pton(addr_family, addrstr, &addrbin) != 1) - return FALSE; - - if (slash) { - /* For IPv4, `ip addr add` supports the prefix-length as a netmask. We don't - * do that. */ - prefix = - _nm_utils_ascii_str_to_int64(&slash[1], 10, 0, addr_family == AF_INET ? 32 : 128, -1); - if (prefix == -1) - return FALSE; - } - - NM_SET_OUT(out_addr_family, addr_family); - if (out_addr) - nm_ip_addr_set(addr_family, out_addr, &addrbin); - NM_SET_OUT(out_prefix, prefix); - return TRUE; -} - -gboolean -nm_utils_parse_inaddr_prefix(int addr_family, const char *text, char **out_addr, int *out_prefix) -{ - NMIPAddr addrbin; - char addrstr_buf[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)]; - - if (!nm_utils_parse_inaddr_prefix_bin(addr_family, text, &addr_family, &addrbin, out_prefix)) - return FALSE; - NM_SET_OUT(out_addr, - g_strdup(inet_ntop(addr_family, &addrbin, addrstr_buf, sizeof(addrstr_buf)))); - return TRUE; -} - gboolean nm_utils_parse_next_line(const char **inout_ptr, gsize *inout_len, @@ -1323,43 +903,6 @@ done: /*****************************************************************************/ -gboolean -nm_utils_ipaddr_is_valid(int addr_family, const char *str_addr) -{ - nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); - - return str_addr && nm_utils_parse_inaddr_bin(addr_family, str_addr, NULL, NULL); -} - -gboolean -nm_utils_ipaddr_is_normalized(int addr_family, const char *str_addr) -{ - NMIPAddr addr; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); - - if (!str_addr) - return FALSE; - - if (!nm_utils_parse_inaddr_bin(addr_family, str_addr, &addr_family, &addr)) - return FALSE; - - nm_utils_inet_ntop(addr_family, &addr, sbuf); - return nm_streq(sbuf, str_addr); -} - -/*****************************************************************************/ - -NM_UTILS_ENUM2STR_DEFINE(nm_icmpv6_router_pref_to_string, - NMIcmpv6RouterPref, - NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_LOW, "low"), - NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_MEDIUM, "medium"), - NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_HIGH, "high"), - NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_INVALID, "invalid"), ); - -/*****************************************************************************/ - /** * nm_g_ascii_strtoll() * @nptr: the string to parse @@ -2587,15 +2130,16 @@ nm_strv_cleanup(char **strv, gboolean strip_whitespace, gboolean skip_empty, gbo /*****************************************************************************/ GPtrArray * -_nm_g_ptr_array_copy(GPtrArray *array, - GCopyFunc func, - gpointer user_data, - GDestroyNotify element_free_func) +nm_g_ptr_array_new_clone(GPtrArray *array, + GCopyFunc func, + gpointer user_data, + GDestroyNotify element_free_func) { GPtrArray *new_array; guint i; g_return_val_if_fail(array, NULL); + nm_assert((!!func) == (!!element_free_func)); new_array = g_ptr_array_new_full(array->len, element_free_func); for (i = 0; i < array->len; i++) { @@ -3522,12 +3066,12 @@ nm_utils_named_value_list_find(const NMUtilsNamedValue *arr, #endif if (sorted) { - return nm_utils_array_find_binary_search(arr, - sizeof(NMUtilsNamedValue), - len, - &name, - nm_strcmp_p_with_data, - NULL); + return nm_array_find_bsearch(arr, + len, + sizeof(NMUtilsNamedValue), + &name, + nm_strcmp_p_with_data, + NULL); } for (i = 0; i < len; i++) { if (nm_streq(arr[i].name, name)) @@ -4215,17 +3759,19 @@ nm_utils_ptrarray_is_sorted(gconstpointer *list, } gssize -nm_utils_ptrarray_find_binary_search(gconstpointer *list, - gsize len, - gconstpointer needle, - GCompareDataFunc cmpfcn, - gpointer user_data) -{ - gssize imin, imax, imid; +nm_ptrarray_find_bsearch(gconstpointer *list, + gsize len, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data) +{ + gssize imax; + gssize imid; + gssize imin; int cmp; - g_return_val_if_fail(list || !len, ~((gssize) 0)); - g_return_val_if_fail(cmpfcn, ~((gssize) 0)); + nm_assert(list || len == 0); + nm_assert(cmpfcn); imin = 0; if (len > 0) { @@ -4252,19 +3798,24 @@ nm_utils_ptrarray_find_binary_search(gconstpointer *list, } gssize -nm_utils_ptrarray_find_binary_search_range(gconstpointer *list, - gsize len, - gconstpointer needle, - GCompareDataFunc cmpfcn, - gpointer user_data, - gssize *out_idx_first, - gssize *out_idx_last) -{ - gssize imin, imax, imid, i2min, i2max, i2mid; +nm_ptrarray_find_bsearch_range(gconstpointer *list, + gsize len, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data, + gssize *out_idx_first, + gssize *out_idx_last) +{ + gssize imax; + gssize imid; + gssize imin; + gssize i2max; + gssize i2mid; + gssize i2min; int cmp; - g_return_val_if_fail(list || !len, ~((gssize) 0)); - g_return_val_if_fail(cmpfcn, ~((gssize) 0)); + nm_assert(list || len == 0); + nm_assert(cmpfcn); imin = 0; if (len > 0) { @@ -4333,10 +3884,10 @@ nm_utils_ptrarray_find_binary_search_range(gconstpointer *list, /*****************************************************************************/ /** - * nm_utils_array_find_binary_search: + * nm_array_find_bsearch: * @list: the list to search. It must be sorted according to @cmpfcn ordering. - * @elem_size: the size in bytes of each element in the list * @len: the number of elements in @list + * @elem_size: the size in bytes of each element in the list * @needle: the value that is searched * @cmpfcn: the compare function. The elements @list are passed as first * argument to @cmpfcn, while @needle is passed as second. Usually, the @@ -4357,42 +3908,14 @@ nm_utils_ptrarray_find_binary_search_range(gconstpointer *list, * position where it should be. */ gssize -nm_utils_array_find_binary_search(gconstpointer list, - gsize elem_size, - gsize len, - gconstpointer needle, - GCompareDataFunc cmpfcn, - gpointer user_data) -{ - gssize imin, imax, imid; - int cmp; - - g_return_val_if_fail(list || !len, ~((gssize) 0)); - g_return_val_if_fail(cmpfcn, ~((gssize) 0)); - g_return_val_if_fail(elem_size > 0, ~((gssize) 0)); - - imin = 0; - if (len == 0) - return ~imin; - - imax = len - 1; - - while (imin <= imax) { - imid = imin + (imax - imin) / 2; - - cmp = cmpfcn(&((const char *) list)[elem_size * imid], needle, user_data); - if (cmp == 0) - return imid; - - if (cmp < 0) - imin = imid + 1; - else - imax = imid - 1; - } - - /* return the inverse of @imin. This is a negative number, but - * also is ~imin the position where the value should be inserted. */ - return ~imin; +nm_array_find_bsearch(gconstpointer list, + gsize len, + gsize elem_size, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data) +{ + return nm_array_find_bsearch_inline(list, len, elem_size, needle, cmpfcn, user_data); } /*****************************************************************************/ @@ -6370,98 +5893,6 @@ _nm_utils_ssid_to_string_gbytes(GBytes *ssid) /*****************************************************************************/ -gconstpointer -nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint32 plen) -{ - g_return_val_if_fail(dst, NULL); - - switch (family) { - case AF_INET: - g_return_val_if_fail(plen <= 32, NULL); - - if (!src) { - /* allow "self-assignment", by specifying %NULL as source. */ - src = dst; - } - - *((guint32 *) dst) = nm_utils_ip4_address_clear_host_address(*((guint32 *) src), plen); - break; - case AF_INET6: - nm_utils_ip6_address_clear_host_address(dst, src, plen); - break; - default: - g_return_val_if_reached(NULL); - } - return dst; -} - -/* nm_utils_ip6_address_clear_host_address: - * @dst: destination output buffer, will contain the network part of the @src address - * @src: source ip6 address. If NULL, this does an in-place update of @dst. - * Also, @src and @dst are allowed to be the same pointers. - * @plen: prefix length of network - * - * Note: this function is self assignment safe, to update @src inplace, set both - * @dst and @src to the same destination or set @src NULL. - */ -const struct in6_addr * -nm_utils_ip6_address_clear_host_address(struct in6_addr *dst, - const struct in6_addr *src, - guint32 plen) -{ - g_return_val_if_fail(plen <= 128, NULL); - g_return_val_if_fail(dst, NULL); - - if (!src) - src = dst; - - if (plen < 128) { - guint nbytes = plen / 8; - guint nbits = plen % 8; - - if (nbytes && dst != src) - memcpy(dst, src, nbytes); - if (nbits) { - dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits))); - nbytes++; - } - if (nbytes <= 15) - memset(&dst->s6_addr[nbytes], 0, 16 - nbytes); - } else if (src != dst) - *dst = *src; - - return dst; -} - -int -nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a, - const struct in6_addr *addr_b, - guint32 plen) -{ - int nbytes; - guint8 va, vb, m; - - if (plen >= 128) { - nm_assert(plen == 128); - NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, sizeof(struct in6_addr)); - } else { - nbytes = plen / 8; - if (nbytes) - NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, nbytes); - - plen = plen % 8; - if (plen != 0) { - m = ~((1 << (8 - plen)) - 1); - va = ((((const guint8 *) addr_a))[nbytes]) & m; - vb = ((((const guint8 *) addr_b))[nbytes]) & m; - NM_CMP_DIRECT(va, vb); - } - } - return 0; -} - -/*****************************************************************************/ - #define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/" #define IPV4_PROPERTY_DIR "/proc/sys/net/ipv4/conf/" diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index 53cf7f3e..083ed137 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -139,6 +139,8 @@ typedef enum { NM_LINK_TYPE_VETH, NM_LINK_TYPE_VLAN, NM_LINK_TYPE_VRF, + NM_LINK_TYPE_VTI, + NM_LINK_TYPE_VTI6, NM_LINK_TYPE_VXLAN, NM_LINK_TYPE_WIREGUARD, #define _NM_LINK_TYPE_SW_LAST NM_LINK_TYPE_WIREGUARD @@ -198,6 +200,13 @@ typedef struct { #define NM_ETHER_ADDR_INIT(...) ((NMEtherAddr) _NM_ETHER_ADDR_INIT(__VA_ARGS__)) +struct _NMIPAddr; +extern const struct _NMIPAddr nm_ip_addr_zero; + +/* Let's reuse nm_ip_addr_zero also for nm_ether_addr_zero. It's a union that + * also contains a NMEtherAddr field. */ +#define nm_ether_addr_zero (*((const NMEtherAddr *) ((gconstpointer) &nm_ip_addr_zero))) + static inline int nm_ether_addr_cmp(const NMEtherAddr *a, const NMEtherAddr *b) { @@ -212,126 +221,10 @@ nm_ether_addr_equal(const NMEtherAddr *a, const NMEtherAddr *b) return nm_ether_addr_cmp(a, b) == 0; } -/*****************************************************************************/ - -typedef struct { - union { - guint8 addr_ptr[sizeof(struct in6_addr)]; - in_addr_t addr4; - struct in_addr addr4_struct; - struct in6_addr addr6; - - /* NMIPAddr is really a union for IP addresses. - * However, as ethernet addresses fit in here nicely, use - * it also for an ethernet MAC address. */ - guint8 ether_addr_octet[6 /*ETH_ALEN*/]; - NMEtherAddr ether_addr; - - guint8 array[sizeof(struct in6_addr)]; - }; -} NMIPAddr; - -#define NM_IP_ADDR_INIT \ - { \ - .array = { 0 } \ - } - -extern const NMIPAddr nm_ip_addr_zero; - -#define nm_ether_addr_zero (nm_ip_addr_zero.ether_addr) - -static inline int -nm_ip_addr_cmp(int addr_family, gconstpointer a, gconstpointer b) -{ - /* Note that @a and @b are not required to be full NMIPAddr unions. - * Depending on @addr_family, they can also be only in_addr_t or - * struct in6_addr. */ - NM_CMP_SELF(a, b); - NM_CMP_DIRECT_MEMCMP(a, b, nm_utils_addr_family_to_size(addr_family)); - return 0; -} - -int nm_ip_addr_cmp_for_sort(gconstpointer a, gconstpointer b, gpointer user_data); - static inline gboolean -nm_ip_addr_equal(int addr_family, gconstpointer a, gconstpointer b) +nm_ether_addr_is_zero(const NMEtherAddr *a) { - return nm_ip_addr_cmp(addr_family, a, b) == 0; -} - -static inline gboolean -nm_ip_addr_is_null(int addr_family, gconstpointer addr) -{ - nm_assert(addr); - - if (NM_IS_IPv4(addr_family)) { - in_addr_t t; - - /* also for in_addr_t type (AF_INET), we accept that the pointer might - * be unaligned. */ - memcpy(&t, addr, sizeof(t)); - return t == 0; - } - - return IN6_IS_ADDR_UNSPECIFIED((const struct in6_addr *) addr); -} - -static inline void -nm_ip_addr_set(int addr_family, gpointer dst, gconstpointer src) -{ - nm_assert(dst); - nm_assert(src); - - /* this MUST use memcpy() to support unaligned src/dst pointers. */ - memcpy(dst, src, nm_utils_addr_family_to_size(addr_family)); - - /* Note that @dst is not necessarily a NMIPAddr, it could also be just - * an in_addr_t/struct in6_addr. We thus can only set the bytes that - * we know are present based on the address family. - * - * Using this function to initialize an NMIPAddr union (for IPv4) leaves - * uninitalized bytes. Avoid that by using nm_ip_addr_init() instead. */ -} - -static inline NMIPAddr -nm_ip_addr_init(int addr_family, gconstpointer src) -{ - NMIPAddr a; - - nm_assert_addr_family(addr_family); - nm_assert(src); - - G_STATIC_ASSERT_EXPR(sizeof(NMIPAddr) == sizeof(struct in6_addr)); - - /* this MUST use memcpy() to support unaligned src/dst pointers. */ - - if (NM_IS_IPv4(addr_family)) { - memcpy(&a, src, sizeof(in_addr_t)); - - /* ensure all bytes of the union are initialized. If only to make - * valgrind happy. */ - memset(&a.array[sizeof(in_addr_t)], 0, sizeof(a) - sizeof(in_addr_t)); - } else - memcpy(&a, src, sizeof(struct in6_addr)); - - return a; -} - -gboolean nm_ip_addr_set_from_untrusted(int addr_family, - gpointer dst, - gconstpointer src, - gsize src_len, - int *out_addr_family); - -gboolean -nm_ip_addr_set_from_variant(int addr_family, gpointer dst, GVariant *variant, int *out_addr_family); - -static inline gconstpointer -nm_ip_addr_from_packed_array(int addr_family, gconstpointer ipaddr_arr, gsize idx) -{ - return NM_IS_IPv4(addr_family) - ? ((gconstpointer) & (((const struct in_addr *) ipaddr_arr)[idx])) - : ((gconstpointer) & (((const struct in6_addr *) ipaddr_arr)[idx])); + return nm_memeq(a, &nm_ether_addr_zero, sizeof(NMEtherAddr)); } /*****************************************************************************/ @@ -343,6 +236,7 @@ nm_utils_ether_addr_cmp(const struct ether_addr *a1, const struct ether_addr *a2 { nm_assert(a1); nm_assert(a2); + return memcmp(a1, a2, 6 /*ETH_ALEN*/); } @@ -377,11 +271,36 @@ typedef struct _NMUtilsIPv6IfaceId { } \ } -void nm_utils_ipv6_addr_set_interface_identifier(struct in6_addr *addr, - const NMUtilsIPv6IfaceId *iid); +/** + * nm_utils_ipv6_addr_set_interface_identifier: + * @addr: output token encoded as %in6_addr + * @iid: %NMUtilsIPv6IfaceId interface identifier + * + * Converts the %NMUtilsIPv6IfaceId to an %in6_addr (suitable for use + * with Linux platform). This only copies the lower 8 bytes, ignoring + * the /64 network prefix which is expected to be all-zero for a valid + * token. + */ +static inline void +nm_utils_ipv6_addr_set_interface_identifier(struct in6_addr *addr, const NMUtilsIPv6IfaceId *iid) +{ + memcpy(addr->s6_addr + 8, &iid->id_u8, 8); +} -void nm_utils_ipv6_interface_identifier_get_from_addr(NMUtilsIPv6IfaceId *iid, - const struct in6_addr *addr); +/** + * nm_utils_ipv6_interface_identifier_get_from_addr: + * @iid: output %NMUtilsIPv6IfaceId interface identifier set from the token + * @addr: token encoded as %in6_addr + * + * Converts the %in6_addr encoded token (as used by Linux platform) to + * the interface identifier. + */ +static inline void +nm_utils_ipv6_interface_identifier_get_from_addr(NMUtilsIPv6IfaceId *iid, + const struct in6_addr *addr) +{ + memcpy(iid, addr->s6_addr + 8, 8); +} gboolean nm_utils_ipv6_interface_identifier_get_from_token(NMUtilsIPv6IfaceId *iid, const char *token); @@ -397,219 +316,6 @@ gboolean nm_utils_get_ipv6_interface_identifier(NMLinkType link_type, /*****************************************************************************/ -static inline guint32 -_nm_utils_ip4_netmask_to_prefix(in_addr_t subnetmask) -{ - G_STATIC_ASSERT_EXPR(__SIZEOF_INT__ == 4); - G_STATIC_ASSERT_EXPR(sizeof(int) == 4); - G_STATIC_ASSERT_EXPR(sizeof(guint) == 4); - G_STATIC_ASSERT_EXPR(sizeof(subnetmask) == 4); - - return ((subnetmask != 0u) ? (guint32) (32 - __builtin_ctz(ntohl(subnetmask))) : 0u); -} - -/** - * _nm_utils_ip4_prefix_to_netmask: - * @prefix: a CIDR prefix - * - * Returns: the netmask represented by the prefix, in network byte order - **/ -static inline in_addr_t -_nm_utils_ip4_prefix_to_netmask(guint32 prefix) -{ - nm_assert(prefix <= 32); - return prefix < 32 ? ~htonl(0xFFFFFFFFu >> prefix) : 0xFFFFFFFFu; -} - -guint32 _nm_utils_ip4_get_default_prefix0(in_addr_t ip); -guint32 _nm_utils_ip4_get_default_prefix(in_addr_t ip); - -gconstpointer -nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint32 plen); - -/* nm_utils_ip4_address_clear_host_address: - * @addr: source ip6 address - * @plen: prefix length of network - * - * returns: the input address, with the host address set to 0. - */ -static inline in_addr_t -nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint32 plen) -{ - return addr & _nm_utils_ip4_prefix_to_netmask(plen); -} - -const struct in6_addr *nm_utils_ip6_address_clear_host_address(struct in6_addr *dst, - const struct in6_addr *src, - guint32 plen); - -static inline int -nm_utils_ip4_address_same_prefix_cmp(in_addr_t addr_a, in_addr_t addr_b, guint32 plen) -{ - NM_CMP_DIRECT(htonl(nm_utils_ip4_address_clear_host_address(addr_a, plen)), - htonl(nm_utils_ip4_address_clear_host_address(addr_b, plen))); - return 0; -} - -int nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a, - const struct in6_addr *addr_b, - guint32 plen); - -static inline gboolean -nm_utils_ip4_address_same_prefix(in_addr_t addr_a, in_addr_t addr_b, guint32 plen) -{ - return nm_utils_ip4_address_same_prefix_cmp(addr_a, addr_b, plen) == 0; -} - -static inline gboolean -nm_utils_ip6_address_same_prefix(const struct in6_addr *addr_a, - const struct in6_addr *addr_b, - guint8 plen) -{ - return nm_utils_ip6_address_same_prefix_cmp(addr_a, addr_b, plen) == 0; -} - -static inline int -nm_utils_ip_address_same_prefix_cmp(int addr_family, - gconstpointer addr_a, - gconstpointer addr_b, - guint8 plen) -{ - NM_CMP_SELF(addr_a, addr_b); - - if (NM_IS_IPv4(addr_family)) { - return nm_utils_ip4_address_same_prefix_cmp(*((const in_addr_t *) addr_a), - *((const in_addr_t *) addr_b), - plen); - } - - return nm_utils_ip6_address_same_prefix_cmp(addr_a, addr_b, plen); -} - -static inline gboolean -nm_utils_ip_address_same_prefix(int addr_family, - gconstpointer addr_a, - gconstpointer addr_b, - guint8 plen) -{ - return nm_utils_ip_address_same_prefix_cmp(addr_family, addr_a, addr_b, plen) == 0; -} - -#define NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a, b, plen) \ - NM_CMP_RETURN(nm_utils_ip4_address_same_prefix_cmp((a), (b), (plen))) - -#define NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(a, b, plen) \ - NM_CMP_RETURN(nm_utils_ip6_address_same_prefix_cmp((a), (b), (plen))) - -/*****************************************************************************/ - -gboolean nm_utils_ip_is_site_local(int addr_family, const void *address); -gboolean nm_utils_ip6_is_ula(const struct in6_addr *address); - -/*****************************************************************************/ - -#define NM_IPV4LL_NETWORK ((in_addr_t) htonl(0xA9FE0000lu)) -#define NM_IPV4LL_NETMASK ((in_addr_t) htonl(0xFFFF0000lu)) - -static inline gboolean -nm_utils_ip4_address_is_loopback(in_addr_t addr) -{ - /* There is also IN_LOOPBACK() in , but there the - * argument is in host order not `in_addr_t`. */ - return (addr & htonl(0xFF000000u)) == htonl(0x7F000000u); -} - -static inline gboolean -nm_utils_ip4_address_is_link_local(in_addr_t addr) -{ - return (addr & NM_IPV4LL_NETMASK) == NM_IPV4LL_NETWORK; -} - -static inline gboolean -nm_utils_ip4_address_is_zeronet(in_addr_t network) -{ - /* Same as ipv4_is_zeronet() from kernel's include/linux/in.h. */ - return (network & htonl(0xFF000000u)) == htonl(0x00000000u); -} - -/*****************************************************************************/ - -#define NM_UTILS_INET_ADDRSTRLEN INET6_ADDRSTRLEN - -/* Forward declare function so we don't have to drag in . */ -const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); - -static inline const char * -nm_utils_inet_ntop(int addr_family, gconstpointer addr, char *dst) -{ - const char *s; - - nm_assert_addr_family(addr_family); - nm_assert(addr); - nm_assert(dst); - - s = inet_ntop(addr_family, - addr, - dst, - addr_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN); - nm_assert(s); - return s; -} - -static inline const char * -_nm_utils_inet4_ntop(in_addr_t addr, char dst[static INET_ADDRSTRLEN]) -{ - return nm_utils_inet_ntop(AF_INET, &addr, dst); -} - -static inline const char * -_nm_utils_inet6_ntop(const struct in6_addr *addr, char dst[static INET6_ADDRSTRLEN]) -{ - return nm_utils_inet_ntop(AF_INET6, addr, dst); -} - -static inline char * -nm_utils_inet_ntop_dup(int addr_family, gconstpointer addr) -{ - char buf[NM_UTILS_INET_ADDRSTRLEN]; - - return g_strdup(nm_utils_inet_ntop(addr_family, addr, buf)); -} - -static inline char * -nm_utils_inet4_ntop_dup(in_addr_t addr) -{ - return nm_utils_inet_ntop_dup(AF_INET, &addr); -} - -static inline char * -nm_utils_inet6_ntop_dup(const struct in6_addr *addr) -{ - return nm_utils_inet_ntop_dup(AF_INET6, addr); -} - -/*****************************************************************************/ - -gboolean nm_utils_ipaddr_is_valid(int addr_family, const char *str_addr); - -gboolean nm_utils_ipaddr_is_normalized(int addr_family, const char *str_addr); - -/*****************************************************************************/ - -/* this enum is compatible with ICMPV6_ROUTER_PREF_* (from , - * the values for netlink attribute RTA_PREF) and "enum ndp_route_preference" - * from . */ -typedef enum _nm_packed { - NM_ICMPV6_ROUTER_PREF_MEDIUM = 0x0, /* ICMPV6_ROUTER_PREF_MEDIUM */ - NM_ICMPV6_ROUTER_PREF_LOW = 0x3, /* ICMPV6_ROUTER_PREF_LOW */ - NM_ICMPV6_ROUTER_PREF_HIGH = 0x1, /* ICMPV6_ROUTER_PREF_HIGH */ - NM_ICMPV6_ROUTER_PREF_INVALID = 0x2, /* ICMPV6_ROUTER_PREF_INVALID */ -} NMIcmpv6RouterPref; - -const char *nm_icmpv6_router_pref_to_string(NMIcmpv6RouterPref pref, char *buf, gsize len); - -/*****************************************************************************/ - gboolean nm_utils_memeqzero(gconstpointer data, gsize length); /*****************************************************************************/ @@ -1044,31 +750,6 @@ nm_utils_escaped_tokens_options_escape_val(const char *val, char **out_to_free) /*****************************************************************************/ -gboolean nm_utils_parse_inaddr_bin_full(int addr_family, - gboolean accept_legacy, - const char *text, - int *out_addr_family, - gpointer out_addr); -static inline gboolean -nm_utils_parse_inaddr_bin(int addr_family, - const char *text, - int *out_addr_family, - gpointer out_addr) -{ - return nm_utils_parse_inaddr_bin_full(addr_family, FALSE, text, out_addr_family, out_addr); -} - -gboolean nm_utils_parse_inaddr(int addr_family, const char *text, char **out_addr); - -gboolean nm_utils_parse_inaddr_prefix_bin(int addr_family, - const char *text, - int *out_addr_family, - gpointer out_addr, - int *out_prefix); - -gboolean -nm_utils_parse_inaddr_prefix(int addr_family, const char *text, char **out_addr, int *out_prefix); - gboolean nm_utils_parse_next_line(const char **inout_ptr, gsize *inout_len, const char **out_line, @@ -1335,6 +1016,7 @@ typedef enum { * message from the device that returned the *highest* error code, * in the hope that this message makes the most sense for the caller. * */ + NM_UTILS_ERROR_CONNECTION_AVAILABLE_STRICTLY_UNMANAGED_DEVICE, NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE, NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, @@ -1662,6 +1344,10 @@ nm_g_variant_new_au(const guint32 *data, gsize len) return g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, data, len, sizeof(guint32)); } +struct _NMIPAddr; + +extern const struct _NMIPAddr nm_ip_addr_zero; + static inline GVariant * nm_g_variant_new_ay_inaddr(int addr_family, gconstpointer addr) { @@ -2210,6 +1896,8 @@ char *nm_utils_g_slist_strlist_join(const GSList *a, const char *separator); static inline gpointer nm_g_array_data(const GArray *arr) { + /* You may want to use nm_g_array_first_p() instead, which can assert + * for the expected type. */ return arr ? arr->data : NULL; } @@ -2226,43 +1914,67 @@ nm_g_array_unref(GArray *arr) g_array_unref(arr); } -#define nm_g_array_first(arr, Type) \ - ({ \ - GArray *const _arr = (arr); \ - \ - nm_assert(_arr); \ - nm_assert(sizeof(Type) == g_array_get_element_size(_arr)); \ - nm_assert(_arr->len > 0); \ - \ - &g_array_index(arr, Type, 0); \ - }) - -#define nm_g_array_last(arr, Type) \ - ({ \ - GArray *const _arr = (arr); \ - \ - nm_assert(_arr); \ - nm_assert(sizeof(Type) == g_array_get_element_size(_arr)); \ - nm_assert(_arr->len > 0); \ - \ - &g_array_index(arr, Type, _arr->len - 1u); \ - }) - /* Similar to g_array_index(). The differences are * - this does nm_assert() checks that the arguments are valid. - * - returns a pointer to the element. */ -#define nm_g_array_index_p(arr, Type, idx) \ - ({ \ - GArray *const _arr_55 = (arr); \ - const guint _idx_55 = (idx); \ - \ - nm_assert(_arr_55); \ - nm_assert(sizeof(Type) == g_array_get_element_size(_arr_55)); \ - nm_assert(_idx_55 < _arr_55->len); \ - \ - &g_array_index(_arr_55, Type, _idx_55); \ + * - returns a pointer to the element. + * - it asserts that @idx is <= arr->len. That is, it allows + * to get a pointer after the data, of course, you are not + * allowed to dereference in that case. + * - in particular, unlike nm_g_array_index(), you are allowed to call this + * with "arr" NULL (for index zero) or with "arr->data" NULL + * (for index zero). In that case, NULL is returned. + * + * When accessing index zero, then this returns NULL if-and-only-if + * "arr" is NULL or "arr->data" is NULL. In all other cases, this + * returns the pointer &((Type*) arr->data)[idx]. Note that the pointer + * may not be followed, if "idx" is equal to "arr->len". */ +#define nm_g_array_index_p(arr, Type, idx) \ + ({ \ + const GArray *const _arr_55 = (arr); \ + const guint _idx_55 = (idx); \ + \ + nm_assert(_arr_55 || _idx_55 == 0); \ + nm_assert(_idx_55 <= (_arr_55 ? _arr_55->len : 0u)); \ + nm_assert(!_arr_55 || sizeof(Type) == g_array_get_element_size((GArray *) _arr_55)); \ + \ + ((_arr_55 && _arr_55->data) ? &(((Type *) ((gpointer) _arr_55->data))[_idx_55]) : NULL); \ }) +/* Very similar to g_array_index(). + * - nm_assert() that arguments are valid. + * - returns an lvalue to the element. + * - similar to nm_g_array_index_p(), but dereferences the pointer. + * - one difference to nm_g_array_index_p() is that it @idx MUST be + * smaller than arr->len (unlike nm_g_array_index_p() which allows + * access one element past the buffer. */ +#define nm_g_array_index(arr, Type, idx) \ + (*({ \ + const GArray *const _arr_55 = (arr); \ + const guint _idx_55 = (idx); \ + \ + nm_assert(_arr_55); \ + nm_assert(sizeof(Type) == g_array_get_element_size((GArray *) _arr_55)); \ + nm_assert(_idx_55 < _arr_55->len); \ + \ + &g_array_index((GArray *) _arr_55, Type, _idx_55); \ + })) + +#define nm_g_array_first(arr, Type) nm_g_array_index(arr, Type, 0) + +#define nm_g_array_first_p(arr, Type) nm_g_array_index_p(arr, Type, 0) + +/* Same as g_array_index(arr, Type, arr->len-1). */ +#define nm_g_array_last(arr, Type) \ + (*({ \ + const GArray *const _arr = (arr); \ + \ + nm_assert(_arr); \ + nm_assert(sizeof(Type) == g_array_get_element_size((GArray *) _arr)); \ + nm_assert(_arr->len > 0); \ + \ + &g_array_index((GArray *) arr, Type, _arr->len - 1u); \ + })) + #define nm_g_array_append_new(arr, Type) \ ({ \ GArray *const _arr = (arr); \ @@ -2279,6 +1991,20 @@ nm_g_array_unref(GArray *arr) &g_array_index(arr, Type, _len); \ }) +#define nm_g_array_append_simple(arr, val) \ + G_STMT_START \ + { \ + /* Similar to `g_array_append_val()`, but `g_array_append_val()` + * only works with lvalues. That makes sense if the value is a larger + * struct and you anyway have a pointer to it. It doesn't make sense + * if you have a list of int and want to append a number literal. + * + * nm_g_array_append_simple() is different. It depends on typeof(val) + * to be compatible. */ \ + (*nm_g_array_append_new((arr), typeof(val))) = (val); \ + } \ + G_STMT_END + /*****************************************************************************/ static inline GPtrArray * @@ -2342,40 +2068,29 @@ nm_g_ptr_array_pdata(const GPtrArray *arr) return arr ? arr->pdata : NULL; } -GPtrArray *_nm_g_ptr_array_copy(GPtrArray *array, - GCopyFunc func, - gpointer user_data, - GDestroyNotify element_free_func); - /** - * nm_g_ptr_array_copy: + * nm_g_ptr_array_new_clone: * @array: the #GPtrArray to clone. * @func: the copy function. * @user_data: the user data for the copy function - * @element_free_func: the free function of the elements. @array MUST have - * the same element_free_func. This argument is only used on older - * glib, that doesn't support g_ptr_array_copy(). + * @element_free_func: the free function of the elements. This function + * must agree with the owner-ship semantics of @func. * * This is a replacement for g_ptr_array_copy(), which is not available * before glib 2.62. Since GPtrArray does not allow to access the internal * element_free_func, we cannot add a compatibility implementation of g_ptr_array_copy() - * and the user must provide a suitable destroy function. + * as the caller must provide the correct element_free_func. * - * Note that the @element_free_func MUST correspond to free function set in @array. + * So this is not the same as g_ptr_array_copy() (hence the different name) because + * g_ptr_array_copy() uses the free func of the source array, which we cannot access. + * With g_ptr_array_copy() the copy func must agree with the array's free func. + * Here, it must agree with the provided @element_free_func. This allows for example + * to do a shallow-copy without cloning the elements (which you cannot do with g_ptr_array_copy()). */ -#if GLIB_CHECK_VERSION(2, 62, 0) -#define nm_g_ptr_array_copy(array, func, user_data, element_free_func) \ - ({ \ - _nm_unused GDestroyNotify const _element_free_func = (element_free_func); \ - \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; \ - g_ptr_array_copy((array), (func), (user_data)); \ - G_GNUC_END_IGNORE_DEPRECATIONS; \ - }) -#else -#define nm_g_ptr_array_copy(array, func, user_data, element_free_func) \ - _nm_g_ptr_array_copy((array), (func), (user_data), (element_free_func)) -#endif +GPtrArray *nm_g_ptr_array_new_clone(GPtrArray *array, + GCopyFunc func, + gpointer user_data, + GDestroyNotify element_free_func); /*****************************************************************************/ @@ -2443,42 +2158,95 @@ gboolean nm_utils_ptrarray_is_sorted(gconstpointer *list, GCompareDataFunc cmpfcn, gpointer user_data); -gssize nm_utils_ptrarray_find_binary_search(gconstpointer *list, - gsize len, - gconstpointer needle, - GCompareDataFunc cmpfcn, - gpointer user_data); - -gssize nm_utils_ptrarray_find_binary_search_range(gconstpointer *list, - gsize len, - gconstpointer needle, - GCompareDataFunc cmpfcn, - gpointer user_data, - gssize *out_idx_first, - gssize *out_idx_last); - -#define nm_strv_find_binary_search(strv, len, needle) \ - ({ \ - const char *const *const _strv = NM_CAST_STRV_CC(strv); \ - const gsize _len = (len); \ - const char *const _needle = (needle); \ - \ - nm_assert(_len == 0 || _strv); \ - nm_assert(_needle); \ - \ - nm_utils_ptrarray_find_binary_search((gconstpointer *) _strv, \ - _len, \ - _needle, \ - nm_strcmp_with_data, \ - NULL); \ +gssize nm_ptrarray_find_bsearch(gconstpointer *list, + gsize len, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data); + +gssize nm_ptrarray_find_bsearch_range(gconstpointer *list, + gsize len, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data, + gssize *out_idx_first, + gssize *out_idx_last); + +#define nm_strv_find_binary_search(strv, len, needle) \ + ({ \ + const char *const *const _strv = NM_CAST_STRV_CC(strv); \ + const gsize _len = (len); \ + const char *const _needle = (needle); \ + \ + nm_assert(_len == 0 || _strv); \ + nm_assert(_needle); \ + \ + nm_ptrarray_find_bsearch((gconstpointer *) _strv, \ + _len, \ + _needle, \ + nm_strcmp_with_data, \ + NULL); \ }) -gssize nm_utils_array_find_binary_search(gconstpointer list, - gsize elem_size, - gsize len, - gconstpointer needle, - GCompareDataFunc cmpfcn, - gpointer user_data); +/*****************************************************************************/ + +#ifdef NM_WANT_NM_ARRAY_FIND_BSEARCH_INLINE +/** + * nm_array_find_bsearch_inline: + * + * An inlined version of nm_array_find_bsearch(). See there. + * Define NM_WANT_NM_ARRAY_FIND_BSEARCH_INLINE to get it. + */ +_nm_always_inline static inline gssize +nm_array_find_bsearch_inline(gconstpointer list, + gsize len, + gsize elem_size, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data) +{ + gssize imax; + gssize imid; + gssize imin; + int cmp; + + nm_assert(list || len == 0); + nm_assert(cmpfcn); + nm_assert(elem_size > 0); + + imin = 0; + if (len == 0) + return ~imin; + + imax = len - 1; + + while (imin <= imax) { + imid = imin + (imax - imin) / 2; + + cmp = cmpfcn(&((const char *) list)[elem_size * imid], needle, user_data); + if (cmp == 0) + return imid; + + if (cmp < 0) + imin = imid + 1; + else + imax = imid - 1; + } + + /* return the inverse of @imin. This is a negative number, but + * also is ~imin the position where the value should be inserted. */ + return ~imin; +} +#endif + +gssize nm_array_find_bsearch(gconstpointer list, + gsize len, + gsize elem_size, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data); + +/*****************************************************************************/ gssize nm_utils_ptrarray_find_first(gconstpointer *list, gssize len, gconstpointer needle); @@ -2731,7 +2499,7 @@ nm_strv_ptrarray_clone(const GPtrArray *src, gboolean null_if_empty) { if (!src || (null_if_empty && src->len == 0)) return NULL; - return nm_g_ptr_array_copy((GPtrArray *) src, nm_copy_func_g_strdup, NULL, g_free); + return nm_g_ptr_array_new_clone((GPtrArray *) src, nm_copy_func_g_strdup, NULL, g_free); } static inline void @@ -2894,6 +2662,9 @@ nm_ether_addr_to_string(const NMEtherAddr *ether_addr, char sbuf[static(sizeof(N #define nm_ether_addr_to_string_a(ether_addr) \ nm_ether_addr_to_string((ether_addr), g_alloca(sizeof(NMEtherAddr) * 3)) +#define nm_ether_addr_to_string_dup(ether_addr) \ + ((char *) nm_ether_addr_to_string((ether_addr), g_malloc(sizeof(NMEtherAddr) * 3))) + NMEtherAddr *nm_ether_addr_from_string(NMEtherAddr *addr, const char *str); guint8 *nm_utils_hexstr2bin_full(const char *hexstr, @@ -3211,7 +2982,7 @@ nm_strvarray_add(GArray *array, const char *str) static inline const char * nm_strvarray_get_idx(GArray *array, guint idx) { - return *nm_g_array_index_p(array, const char *, idx); + return nm_g_array_index(array, const char *, idx); } static inline const char *const * @@ -3310,12 +3081,12 @@ nm_strvarray_remove_first(GArray *strv, const char *needle) static inline int nm_strvarray_cmp(const GArray *a, const GArray *b) { + nm_assert(!a || sizeof(const char *const *) == g_array_get_element_size((GArray *) a)); + nm_assert(!b || sizeof(const char *const *) == g_array_get_element_size((GArray *) b)); + NM_CMP_SELF(a, b); - return nm_strv_cmp_n((const char *const *) a->data, - a->len, - (const char *const *) b->data, - b->len); + return nm_strv_cmp_n(nm_g_array_data(a), a->len, nm_g_array_data(b), b->len); } #define nm_strvarray_equal(a, b) (nm_strvarray_cmp((a), (b)) == 0) @@ -3323,10 +3094,9 @@ nm_strvarray_cmp(const GArray *a, const GArray *b) static inline int _nm_strvarray_cmp_strv(const GArray *strv, const char *const *ss, gsize ss_len) { - return nm_strv_cmp_n(strv ? (const char *const *) strv->data : NULL, - strv ? ((gssize) strv->len) : -1, - ss, - ss_len); + nm_assert(!strv || sizeof(const char *const *) == g_array_get_element_size((GArray *) strv)); + + return nm_strv_cmp_n(nm_g_array_data(strv), strv ? ((gssize) strv->len) : -1, ss, ss_len); } #define nm_strvarray_cmp_strv(strv, ss, ss_len) \ _nm_strvarray_cmp_strv((strv), NM_CAST_STRV_CC(ss), (ss_len)) diff --git a/src/libnm-glib-aux/nm-str-buf.h b/src/libnm-glib-aux/nm-str-buf.h index e8b51208..32ab8da8 100644 --- a/src/libnm-glib-aux/nm-str-buf.h +++ b/src/libnm-glib-aux/nm-str-buf.h @@ -447,6 +447,24 @@ nm_str_buf_get_str(NMStrBuf *strbuf) return strbuf->_priv_str; } +static inline char * +nm_str_buf_dup_str(NMStrBuf *strbuf) +{ + _nm_str_buf_assert(strbuf); + + /* Gives almost the same as g_strdup(nm_str_buf_get_str(strbuf)). The difference + * is: + * - unlike nm_str_buf_get_str(), it does not need to allocate + * one more character to NUL terminate the string. + * - it does not require an additional strlen(), because we + * already know the length. */ + + if (!strbuf->_priv_str) + return NULL; + + return nm_memdup_nul(strbuf->_priv_str, strbuf->_priv_len); +} + /** * nm_str_buf_get_str_unsafe: * @strbuf: the buffer @@ -530,10 +548,7 @@ nm_str_buf_finalize(NMStrBuf *strbuf, gsize *out_len) char *str = g_steal_pointer(&strbuf->_priv_str); char *result; - result = g_new(char, strbuf->_priv_len + 1u); - memcpy(result, str, strbuf->_priv_len); - result[strbuf->_priv_len] = '\0'; - + result = nm_memdup_nul(str, strbuf->_priv_len); if (strbuf->_priv_do_bzero_mem) nm_explicit_bzero(str, strbuf->_priv_len); return result; diff --git a/src/libnm-glib-aux/nm-test-utils.h b/src/libnm-glib-aux/nm-test-utils.h index d338d0af..41e1a6ce 100644 --- a/src/libnm-glib-aux/nm-test-utils.h +++ b/src/libnm-glib-aux/nm-test-utils.h @@ -39,7 +39,7 @@ * depending on the test. See nmtst_is_debug(). * Known differences: * - a test might leave the logging level unspecified. In this case, running in - * debug mode, will turn on DEBUG logging, otherwise WARN logging only. + * debug mode, will turn on TRACE logging, otherwise WARN logging only. * - if G_MESSAGES_DEBUG is unset, nm-test will set G_MESSAGES_DEBUG=all * for tests that don't do assert-logging. * Debug mode is determined as follows (highest priority first): @@ -85,6 +85,9 @@ #undef g_assertion_message_expr #endif +#undef NDEBUG +#include + #include #include #include @@ -221,17 +224,24 @@ /*****************************************************************************/ +struct __nmtst_testdata_track { + gpointer data; + GDestroyNotify destroy_notify; +}; + struct __nmtst_internal { - GRand *rand0; - guint32 rand_seed; - GRand *rand; - gboolean is_debug; - gboolean assert_logging; - gboolean no_expect_message; - gboolean test_quick; - gboolean test_tap_log; - char *sudo_cmd; - char **orig_argv; + GRand *rand0; + guint32 rand_seed; + GRand *rand; + gboolean is_debug; + gboolean assert_logging; + gboolean no_expect_message; + gboolean test_quick; + gboolean test_tap_log; + char *sudo_cmd; + char **orig_argv; + const char *testpath; + GArray *testdata_track_array; }; extern struct __nmtst_internal __nmtst_internal; @@ -252,6 +262,62 @@ nmtst_initialized(void) return !!__nmtst_internal.rand0; } +/*****************************************************************************/ + +static inline void +_nmtst_testdata_track_clear_func(gpointer ptr) +{ + struct __nmtst_testdata_track *d = ptr; + + if (d->destroy_notify) + d->destroy_notify(d->data); + memset(d, 0, sizeof(*d)); +} + +static inline void +_nmtst_testdata_track_add(gpointer data, GDestroyNotify destroy_notify) +{ + struct __nmtst_testdata_track d = { + .data = data, + .destroy_notify = destroy_notify, + }; + + g_assert(data); + g_assert(destroy_notify); + g_assert(__nmtst_internal.testdata_track_array); + + g_array_append_val(__nmtst_internal.testdata_track_array, d); +} + +static inline void +_nmtst_testdata_track_steal(gpointer data) +{ + struct __nmtst_testdata_track *d; + guint i; + + g_assert(data); + g_assert(__nmtst_internal.testdata_track_array); + + for (i = 0; i < __nmtst_internal.testdata_track_array->len; i++) { + d = &g_array_index(__nmtst_internal.testdata_track_array, struct __nmtst_testdata_track, i); + + if (d->data != data) + continue; + + d->destroy_notify = NULL; + g_array_remove_index_fast(__nmtst_internal.testdata_track_array, i); + return; + } + g_assert_not_reached(); +} + +static inline void +_nmtst_testdata_track_steal_and_free(gpointer data) +{ + _nmtst_testdata_track_steal(data); + g_free(data); +} + #define __NMTST_LOG(cmd, ...) \ G_STMT_START \ { \ @@ -309,7 +375,7 @@ BREAK_INNER_LOOPS: str = &str[i]; } - return (char **) g_array_free(result, FALSE); + return (char **) ((gpointer) g_array_free(result, FALSE)); } /* free instances allocated by nmtst (especially nmtst_init()) on shutdown @@ -320,6 +386,9 @@ nmtst_free(void) if (!nmtst_initialized()) return; + g_array_set_size(__nmtst_internal.testdata_track_array, 0); + g_array_unref(__nmtst_internal.testdata_track_array); + g_rand_free(__nmtst_internal.rand0); if (__nmtst_internal.rand) g_rand_free(__nmtst_internal.rand); @@ -576,10 +645,14 @@ __nmtst_init(int *argc, __nmtst_internal.sudo_cmd = sudo_cmd; __nmtst_internal.no_expect_message = no_expect_message; + __nmtst_internal.testdata_track_array = + g_array_new(FALSE, FALSE, sizeof(struct __nmtst_testdata_track)); + g_array_set_clear_func(__nmtst_internal.testdata_track_array, _nmtst_testdata_track_clear_func); + if (!log_level && log_domains) { /* if the log level is not specified (but the domain is), we assume * the caller wants to set it depending on is_debug */ - log_level = is_debug ? "DEBUG" : "WARN"; + log_level = is_debug ? "TRACE" : "WARN"; } if (!__nmtst_internal.assert_logging) { @@ -661,9 +734,9 @@ __nmtst_init(int *argc, /* Delay messages until we setup logging. */ for (i = 0; i < debug_messages->len; i++) - __NMTST_LOG(g_message, "%s", g_array_index(debug_messages, const char *, i)); + __NMTST_LOG(g_message, "%s", nm_g_array_index(debug_messages, const char *, i)); - g_strfreev((char **) g_array_free(debug_messages, FALSE)); + g_strfreev((char **) ((gpointer) g_array_free(debug_messages, FALSE))); g_free(c_log_level); g_free(c_log_domains); @@ -699,6 +772,12 @@ nmtst_init(int *argc, char ***argv, gboolean assert_logging) static inline gboolean nmtst_is_debug(void) { + /* This is based on the "debug"/"no-debug" flag in "$NMTST_DEBUG". + * + * If debugging is enabled, print more information. However, make sure + * that the test behaves still in a similar manner and that the same code + * path are taken where it matters (it matters for example, if the code path + * consumes random numbers). */ g_assert(nmtst_initialized()); return __nmtst_internal.is_debug; } @@ -774,15 +853,12 @@ typedef struct _NmtstTestData NmtstTestData; typedef void (*NmtstTestHandler)(const NmtstTestData *test_data); struct _NmtstTestData { - union { - const char *testpath; - char *_testpath; - }; + const char *testpath; gsize n_args; - gpointer *args; NmtstTestHandler _func_setup; GTestDataFunc _func_test; NmtstTestHandler _func_teardown; + gpointer args[]; }; static inline void @@ -807,15 +883,20 @@ _nmtst_test_data_unpack(const NmtstTestData *test_data, gsize n_args, ...) #define nmtst_test_data_unpack(test_data, ...) \ _nmtst_test_data_unpack(test_data, NM_NARG(__VA_ARGS__), ##__VA_ARGS__) -static inline void -_nmtst_test_data_free(gpointer data) +static inline const char * +nmtst_test_get_path(void) { - NmtstTestData *test_data = data; + g_assert(nmtst_initialized()); + g_assert(__nmtst_internal.testpath); - g_assert(test_data); + /* Similar to g_test_get_path() (which only exists since glib 2.68). + * + * This is the test name while running the test added with + * nmtst_add_test_func*(). + * + * You are only allowed to call this from inside such a test. */ - g_free(test_data->_testpath); - g_free(test_data); + return __nmtst_internal.testpath; } static inline void @@ -823,6 +904,13 @@ _nmtst_test_run(gconstpointer data) { const NmtstTestData *test_data = data; + g_assert(test_data); + g_assert(nmtst_initialized()); + g_assert(test_data->testpath); + g_assert(!__nmtst_internal.testpath); + + __nmtst_internal.testpath = test_data->testpath; + if (test_data->_func_setup) test_data->_func_setup(test_data); @@ -830,6 +918,10 @@ _nmtst_test_run(gconstpointer data) if (test_data->_func_teardown) test_data->_func_teardown(test_data); + + g_assert(__nmtst_internal.testpath); + g_assert(__nmtst_internal.testpath == test_data->testpath); + __nmtst_internal.testpath = NULL; } static inline void @@ -843,26 +935,41 @@ _nmtst_add_test_func_full(const char *testpath, gsize i; NmtstTestData *data; va_list ap; + gsize testpath_len; g_assert(testpath && testpath[0]); g_assert(func_test); - data = g_malloc0(sizeof(NmtstTestData) + (sizeof(gpointer) * (n_args + 1))); + testpath_len = strlen(testpath) + 1u; + + data = g_malloc(G_STRUCT_OFFSET(NmtstTestData, args) + + (sizeof(gpointer) * (n_args + 1u) + testpath_len)); + + *data = (NmtstTestData){ + .testpath = (gpointer) &data->args[n_args + 1u], + ._func_test = func_test, + ._func_setup = func_setup, + ._func_teardown = func_teardown, + .n_args = n_args, + }; - data->_testpath = g_strdup(testpath); - data->_func_test = func_test; - data->_func_setup = func_setup; - data->_func_teardown = func_teardown; - data->n_args = n_args; - data->args = (gpointer) &data[1]; va_start(ap, n_args); for (i = 0; i < n_args; i++) data->args[i] = va_arg(ap, gpointer); data->args[i] = NULL; va_end(ap); - g_test_add_data_func_full(testpath, data, _nmtst_test_run, _nmtst_test_data_free); + g_assert(data->testpath == (gpointer) &data->args[i + 1]); + + memcpy((char *) data->testpath, testpath, testpath_len); + + _nmtst_testdata_track_add(data, g_free); + g_test_add_data_func_full(testpath, + data, + _nmtst_test_run, + _nmtst_testdata_track_steal_and_free); } + #define nmtst_add_test_func_full(testpath, func_test, func_setup, func_teardown, ...) \ _nmtst_add_test_func_full(testpath, \ func_test, \ @@ -870,6 +977,7 @@ _nmtst_add_test_func_full(const char *testpath, func_teardown, \ NM_NARG(__VA_ARGS__), \ ##__VA_ARGS__) + #define nmtst_add_test_func(testpath, func_test, ...) \ nmtst_add_test_func_full(testpath, func_test, NULL, NULL, ##__VA_ARGS__) @@ -1191,6 +1299,28 @@ nmtst_get_rand_word_length(GRand *rand) /*****************************************************************************/ +static inline gboolean +nmtst_true_once(gboolean *state, gboolean new_val) +{ + /* Returns only once a TRUE flag. When returning TRUE, + * it will be remembered in "state" and future invocations + * return FALSE. + * + * Also, if "new_val" is FALSE, it won't return TRUE. + * + * The point is to do an action once (depending on "new_val"), + * and remember it in "state". + */ + if (!new_val) + return FALSE; + if (*state) + return FALSE; + *state = TRUE; + return TRUE; +} + +/*****************************************************************************/ + static inline gboolean nmtst_g_source_assert_not_called(gpointer user_data) { @@ -1564,7 +1694,7 @@ nmtst_inet4_from_string(const char *str) } static inline const struct in6_addr * -nmtst_inet6_from_string(const char *str) +nmtst_inet6_from_string_p(const char *str) { static _nm_thread_local struct in6_addr addr; int success; @@ -1579,6 +1709,22 @@ nmtst_inet6_from_string(const char *str) return &addr; } +static inline struct in6_addr +nmtst_inet6_from_string(const char *str) +{ + struct in6_addr addr; + int success; + + if (!str) + addr = in6addr_any; + else { + success = inet_pton(AF_INET6, str, &addr); + g_assert(success == 1); + } + + return addr; +} + static inline gconstpointer nmtst_inet_from_string(int addr_family, const char *str) { @@ -1589,7 +1735,7 @@ nmtst_inet_from_string(int addr_family, const char *str) return &a; } if (addr_family == AF_INET6) - return nmtst_inet6_from_string(str); + return nmtst_inet6_from_string_p(str); g_assert_not_reached(); return NULL; @@ -1648,7 +1794,7 @@ _nmtst_assert_ip6_address(const char *file, if (!addr) addr = &any; - if (memcmp(nmtst_inet6_from_string(str_expected), addr, sizeof(*addr)) != 0) { + if (memcmp(nmtst_inet6_from_string_p(str_expected), addr, sizeof(*addr)) != 0) { char buf[100]; g_error("%s:%d: Unexpected IPv6 address: expected %s, got %s", @@ -1917,7 +2063,7 @@ nmtst_logging_disable(gboolean always) return NULL; } - p = g_memdup(_nm_logging_enabled_state, sizeof(_nm_logging_enabled_state)); + p = nm_memdup(_nm_logging_enabled_state, sizeof(_nm_logging_enabled_state)); memset(_nm_logging_enabled_state, 0, sizeof(_nm_logging_enabled_state)); return p; } @@ -1944,9 +2090,9 @@ nmtst_setting_ip_config_add_address(NMSettingIPConfig *s_ip, const char *address g_assert(s_ip); - if (nm_utils_ipaddr_is_valid(AF_INET, address)) + if (nm_inet_is_valid(AF_INET, address)) family = AF_INET; - else if (nm_utils_ipaddr_is_valid(AF_INET6, address)) + else if (nm_inet_is_valid(AF_INET6, address)) family = AF_INET6; else g_assert_not_reached(); @@ -1969,9 +2115,9 @@ nmtst_setting_ip_config_add_route(NMSettingIPConfig *s_ip, g_assert(s_ip); - if (nm_utils_ipaddr_is_valid(AF_INET, dest)) + if (nm_inet_is_valid(AF_INET, dest)) family = AF_INET; - else if (nm_utils_ipaddr_is_valid(AF_INET6, dest)) + else if (nm_inet_is_valid(AF_INET6, dest)) family = AF_INET6; else g_assert_not_reached(); @@ -2682,7 +2828,8 @@ _nmtst_variant_new_vardict(int dummy, ...) G_STMT_END #else #define _nmtst_assert_variant_bytestring_cmp_str(_ptr, _ptr2, _len) \ - G_STMT_START {} \ + G_STMT_START \ + {} \ G_STMT_END #endif @@ -2913,7 +3060,7 @@ nmtst_ip_address_new(int addr_family, const char *str) GError *error = NULL; NMIPAddress *a; - if (!nm_utils_parse_inaddr_prefix_bin(addr_family, str, &addr_family, &addr, &plen)) + if (!nm_inet_parse_with_prefix_bin(addr_family, str, &addr_family, &addr, &plen)) g_assert_not_reached(); if (plen == -1) @@ -2928,6 +3075,27 @@ nmtst_ip_address_new(int addr_family, const char *str) /*****************************************************************************/ +static inline gpointer +nmtst_keeper_add(GPtrArray **p_arr, gpointer ptr) +{ + if (!p_arr) { + /* If not GPtrArray in/out argument is given, track the pointer + * via _nmtst_testdata_track_add(), which means it stays alive + * until the end of the test. */ + _nmtst_testdata_track_add(ptr, g_free); + } else { + if (!*p_arr) + *p_arr = g_ptr_array_new_with_free_func(g_free); + + g_ptr_array_add(*p_arr, ptr); + } + return ptr; +} + +#define nmtst_keeper_printf(p_ptr, ...) nmtst_keeper_add((p_ptr), g_strdup_printf(__VA_ARGS__)) + +/*****************************************************************************/ + #define nmtst_gbytes_from_arr(...) \ ({ \ const guint8 _arr[] = {__VA_ARGS__}; \ diff --git a/src/libnm-glib-aux/nm-time-utils.c b/src/libnm-glib-aux/nm-time-utils.c index f30e6a19..077d0a4c 100644 --- a/src/libnm-glib-aux/nm-time-utils.c +++ b/src/libnm-glib-aux/nm-time-utils.c @@ -301,8 +301,13 @@ nm_utils_monotonic_timestamp_from_boottime(guint64 boottime, gint64 timestamp_ns nm_assert(offset <= 0 && offset > G_MININT64); - /* check for overflow (note that offset is non-positive). */ - g_return_val_if_fail(boottime < G_MAXINT64, G_MAXINT64); + if (boottime >= (guint64) G_MAXINT64) { + /* This indicates infinity. We keep it at such. */ + return G_MAXINT64; + } + + /* Note that overflow cannot happen, because bootime is non-negative, and + * offset is non-positive. */ return (gint64) boottime + offset; } @@ -317,6 +322,16 @@ nm_utils_clock_gettime_nsec(clockid_t clockid) return nm_utils_timespec_to_nsec(&tp); } +gint64 +nm_utils_clock_gettime_usec(clockid_t clockid) +{ + struct timespec tp; + + if (clock_gettime(clockid, &tp) != 0) + return -NM_ERRNO_NATIVE(errno); + return nm_utils_timespec_to_usec(&tp); +} + gint64 nm_utils_clock_gettime_msec(clockid_t clockid) { @@ -326,3 +341,36 @@ nm_utils_clock_gettime_msec(clockid_t clockid) return -NM_ERRNO_NATIVE(errno); return nm_utils_timespec_to_msec(&tp); } + +/*****************************************************************************/ + +/* Taken from systemd's map_clock_usec_internal(). */ +gint64 +nm_time_map_clock(gint64 from, gint64 from_base, gint64 to_base) +{ + /* Maps the time 'from' between two clocks, based on a common reference point where the first clock + * is at 'from_base' and the second clock at 'to_base'. Basically calculates: + * + * from - from_base + to_base + * + * But takes care of overflows/underflows and avoids signed operations. */ + + if (from >= from_base) { + gint64 delta = from - from_base; + + /* In the future */ + if (to_base >= G_MAXINT64 - delta) + return G_MAXINT64; + + return to_base + delta; + + } else { + gint64 delta = from_base - from; + + /* In the past */ + if (to_base <= G_MININT64 + delta) + return G_MININT64; + + return to_base - delta; + } +} diff --git a/src/libnm-glib-aux/nm-time-utils.h b/src/libnm-glib-aux/nm-time-utils.h index 461d6845..d3153686 100644 --- a/src/libnm-glib-aux/nm-time-utils.h +++ b/src/libnm-glib-aux/nm-time-utils.h @@ -8,17 +8,38 @@ #include +_nm_always_inline static inline gint64 +_nm_utils_timespec_to_xsec(const struct timespec *ts, gint64 xsec_per_sec) +{ + nm_assert(ts); + + if (ts->tv_sec < 0 || ts->tv_nsec < 0) + return G_MAXINT64; + + if (ts->tv_sec > ((guint64) G_MAXINT64) || ts->tv_nsec > ((guint64) G_MAXINT64) + || ts->tv_sec >= (G_MAXINT64 - ((gint64) ts->tv_nsec)) / xsec_per_sec) + return G_MAXINT64; + + return (((gint64) ts->tv_sec) * xsec_per_sec) + + (((gint64) ts->tv_nsec) / (NM_UTILS_NSEC_PER_SEC / xsec_per_sec)); +} + static inline gint64 nm_utils_timespec_to_nsec(const struct timespec *ts) { - return (((gint64) ts->tv_sec) * ((gint64) NM_UTILS_NSEC_PER_SEC)) + ((gint64) ts->tv_nsec); + return _nm_utils_timespec_to_xsec(ts, NM_UTILS_NSEC_PER_SEC); +} + +static inline gint64 +nm_utils_timespec_to_usec(const struct timespec *ts) +{ + return _nm_utils_timespec_to_xsec(ts, NM_UTILS_USEC_PER_SEC); } static inline gint64 nm_utils_timespec_to_msec(const struct timespec *ts) { - return (((gint64) ts->tv_sec) * ((gint64) 1000)) - + (((gint64) ts->tv_nsec) / ((gint64) NM_UTILS_NSEC_PER_SEC / 1000)); + return _nm_utils_timespec_to_xsec(ts, NM_UTILS_MSEC_PER_SEC); } gint64 nm_utils_get_monotonic_timestamp_nsec(void); @@ -35,6 +56,12 @@ nm_utils_get_monotonic_timestamp_nsec_cached(gint64 *cache_now) return (*cache_now) ?: (*cache_now = nm_utils_get_monotonic_timestamp_nsec()); } +static inline gint64 +nm_utils_get_monotonic_timestamp_usec_cached(gint64 *cache_now) +{ + return (*cache_now) ?: (*cache_now = nm_utils_get_monotonic_timestamp_usec()); +} + static inline gint64 nm_utils_get_monotonic_timestamp_msec_cached(gint64 *cache_now) { @@ -48,6 +75,9 @@ nm_utils_get_monotonic_timestamp_sec_cached(gint32 *cache_now) } gint64 nm_utils_clock_gettime_nsec(clockid_t clockid); +gint64 nm_utils_clock_gettime_usec(clockid_t clockid); gint64 nm_utils_clock_gettime_msec(clockid_t clockid); +gint64 nm_time_map_clock(gint64 from, gint64 from_base, gint64 to_base); + #endif /* __NM_TIME_UTILS_H__ */ diff --git a/src/libnm-glib-aux/nm-uuid.c b/src/libnm-glib-aux/nm-uuid.c index 53e8b78c..cdfa5f62 100644 --- a/src/libnm-glib-aux/nm-uuid.c +++ b/src/libnm-glib-aux/nm-uuid.c @@ -12,7 +12,8 @@ const NMUuid nm_uuid_ns_zero = NM_UUID_INIT(00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00); -/* arbitrarily chosen namespace UUID for nm_uuid_generate_from_strings() */ +/* arbitrarily chosen namespace UUID for some uses of nm_uuid_generate_from_strings_old(). + * Try not to re-use this namespace, instead, generate a unique one. */ const NMUuid nm_uuid_ns_1 = NM_UUID_INIT(b4, 25, e9, fb, 75, 98, 44, b4, 9e, 3b, 5a, 2e, 3a, aa, 49, 05); @@ -406,42 +407,85 @@ nm_uuid_generate_from_string_str(const char *s, } /** - * nm_uuid_generate_from_strings: - * @string1: a variadic list of strings. Must be NULL terminated. + * nm_uuid_generate_from_strings_strv: + * @uuid_type: the UUID type to use. Prefer version 5 unless you have + * good reasons. + * @type_args: the namespace UUID. + * @strv: (allow-none): the strv list to hash. Can be NULL, in which + * case the result is different from an empty array. + * @len: if negative, @strv is a NULL terminated array. Otherwise, + * it is the length of the strv array. In the latter case it may + * also contain NULL strings. The result hashes differently depending + * on whether we have a NULL terminated strv array or given length. * - * Returns a variant3 UUID based on the concatenated C strings. + * Returns a @uuid_type UUID based on the concatenated C strings. * It does not simply concatenate them, but also includes the * terminating '\0' character. For example "a", "b", gives * "a\0b\0". - * * This has the advantage, that the following invocations * all give different UUIDs: (NULL), (""), ("",""), ("","a"), ("a",""), * ("aa"), ("aa", ""), ("", "aa"), ... */ char * -nm_uuid_generate_from_strings(const char *string1, ...) +nm_uuid_generate_from_strings_strv(NMUuidType uuid_type, + const NMUuid *type_args, + const char *const *strv, + gssize len) { - if (!string1) - return nm_uuid_generate_from_string_str(NULL, 0, NM_UUID_TYPE_VERSION3, &nm_uuid_ns_1); + nm_auto_str_buf NMStrBuf str = NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_232, TRUE); + gsize slen; + const char *s; - { - nm_auto_str_buf NMStrBuf str = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_104, FALSE); - va_list args; - const char *s; + if (len >= 0) { + gboolean has_nulls = FALSE; + gssize i; - nm_str_buf_append_len(&str, string1, strlen(string1) + 1u); + nm_assert(len == 0 || strv); - va_start(args, string1); - s = va_arg(args, const char *); - while (s) { - nm_str_buf_append_len(&str, s, strlen(s) + 1u); - s = va_arg(args, const char *); + for (i = 0; i < len; i++) { + if (strv[i]) + nm_str_buf_append_len(&str, strv[i], strlen(strv[i]) + 1u); + else + has_nulls = TRUE; } - va_end(args); - - return nm_uuid_generate_from_string_str(nm_str_buf_get_str_unsafe(&str), - str.len, - NM_UUID_TYPE_VERSION3, - &nm_uuid_ns_1); + if (has_nulls) { + /* We either support a NULL terminated strv array, or a ptr array of fixed + * length (@len argument). + * + * If there are no NULLs within the first @len strings, then the result + * is the same. If there are any NULL strings, we need to encode that + * in a unique way. We do that by appending a bitmap of the elements + * whether they were set, plus one 'n' character (without NUL termination). + * None of the other branches below hashes to that, so this will uniquely + * encoded the NULL strings. + */ + for (i = 0; i < len; i++) + nm_str_buf_append_c(&str, strv[i] ? '1' : '_'); + nm_str_buf_append_c(&str, 'n'); + } + slen = str.len; + s = nm_str_buf_get_str_unsafe(&str); + } else if (!strv) { + /* NULL is treated differently from an empty strv. We achieve that + * by using a non-empty, non-NUL terminated string (which cannot happen + * in the other cases). */ + slen = 1; + s = "x"; + } else if (!strv[0]) { + slen = 0; + s = ""; + } else if (!strv[1]) { + slen = strlen(strv[0]) + 1u; + s = strv[0]; + } else { + /* We concatenate the NUL termiated string, including the NUL + * character. This way, ("a","a"), ("aa"), ("aa","") all hash + * differently. */ + for (; strv[0]; strv++) + nm_str_buf_append_len(&str, strv[0], strlen(strv[0]) + 1u); + slen = str.len; + s = nm_str_buf_get_str_unsafe(&str); } + + return nm_uuid_generate_from_string_str(s, slen, uuid_type, type_args); } diff --git a/src/libnm-glib-aux/nm-uuid.h b/src/libnm-glib-aux/nm-uuid.h index 15b03a2d..b8955452 100644 --- a/src/libnm-glib-aux/nm-uuid.h +++ b/src/libnm-glib-aux/nm-uuid.h @@ -9,6 +9,9 @@ typedef struct _NMUuid { #define NM_UUID_INIT_ZERO() ((NMUuid){.uuid = {0}}) +/* Beware, the 16 macro arguments are two hex-digits, not plain numbers. The macro + * will automatically add the "0x". In particular, "09" is not an octal number, it's + * 0x09. This oddity is so that the arguments look very much like the UUID in string form. */ #define NM_UUID_INIT(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) \ ((NMUuid){ \ .uuid = {(0x##a0), \ @@ -120,7 +123,23 @@ char *nm_uuid_generate_from_string_str(const char *s, NMUuidType uuid_type, const NMUuid *type_args); -char *nm_uuid_generate_from_strings(const char *string1, ...) G_GNUC_NULL_TERMINATED; +char *nm_uuid_generate_from_strings_strv(NMUuidType uuid_type, + const NMUuid *type_args, + const char *const *strv, + gssize len); + +#define nm_uuid_generate_from_strings(uuid_type, type_args, ...) \ + nm_uuid_generate_from_strings_strv((uuid_type), \ + (type_args), \ + NM_MAKE_STRV(__VA_ARGS__), \ + NM_NARG(__VA_ARGS__)) + +/* Legacy function. Don't use for new code. */ +#define nm_uuid_generate_from_strings_old(...) \ + nm_uuid_generate_from_strings_strv(NM_UUID_TYPE_VERSION3, \ + &nm_uuid_ns_1, \ + NM_MAKE_STRV(__VA_ARGS__), \ + -1) /*****************************************************************************/ diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index cfe70b58..7503dc9b 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -11,6 +11,7 @@ #include "libnm-glib-aux/nm-time-utils.h" #include "libnm-glib-aux/nm-ref-string.h" #include "libnm-glib-aux/nm-io-utils.h" +#include "libnm-glib-aux/nm-prioq.h" #include "libnm-glib-aux/nm-test-utils.h" @@ -77,6 +78,9 @@ test_gpid(void) * the case. */ int_ptr = &pid; g_assert_cmpint(*int_ptr, ==, 42); + + /* also check how assert() works. */ + assert(*int_ptr == 42); } /*****************************************************************************/ @@ -89,6 +93,38 @@ test_monotonic_timestamp(void) /*****************************************************************************/ +static void +test_timespect_to(void) +{ + struct timespec ts; + int i; + + for (i = 0; i < 1000; i++) { + gint64 t_msec; + gint64 t_usec; + gint64 t_nsec; + + nmtst_rand_buf(NULL, &ts, sizeof(ts)); + ts.tv_sec = llabs(ts.tv_sec % 100000); + ts.tv_nsec = llabs(ts.tv_nsec % NM_UTILS_NSEC_PER_SEC); + + t_msec = nm_utils_timespec_to_msec(&ts); + t_usec = nm_utils_timespec_to_usec(&ts); + t_nsec = nm_utils_timespec_to_nsec(&ts); + + g_assert_cmpint(t_msec, <=, t_usec / 1000); + g_assert_cmpint(t_msec + 1, >=, t_usec / 1000); + + g_assert_cmpint(t_msec, <=, t_nsec / 1000000); + g_assert_cmpint(t_msec + 1, >=, t_nsec / 1000000); + + g_assert_cmpint(t_usec, <=, t_nsec / 1000); + g_assert_cmpint(t_usec + 1, >=, t_nsec / 1000); + } +} + +/*****************************************************************************/ + static void test_nmhash(void) { @@ -117,8 +153,8 @@ test_make_strv(void) const char *const *v2a = NM_MAKE_STRV("a", "b"); const char *const *v2b = NM_MAKE_STRV("a", "b", ); const char *const v3[] = { - "a", - "b", + "a", + "b", }; const char *const *v4b = NM_MAKE_STRV("a", _make_strv_foo(), ); @@ -251,37 +287,41 @@ test_nm_strndup_a(void) /*****************************************************************************/ static void -test_nm_utils_ip4_address_is_loopback(void) +test_nm_ip4_addr_is_loopback(void) { - g_assert(nm_utils_ip4_address_is_loopback(nmtst_inet4_from_string("127.0.0.0"))); - g_assert(nm_utils_ip4_address_is_loopback(nmtst_inet4_from_string("127.0.0.1"))); - g_assert(nm_utils_ip4_address_is_loopback(nmtst_inet4_from_string("127.5.0.1"))); - g_assert(!nm_utils_ip4_address_is_loopback(nmtst_inet4_from_string("126.5.0.1"))); - g_assert(!nm_utils_ip4_address_is_loopback(nmtst_inet4_from_string("128.5.0.1"))); - g_assert(!nm_utils_ip4_address_is_loopback(nmtst_inet4_from_string("129.5.0.1"))); + g_assert(nm_ip4_addr_is_loopback(nmtst_inet4_from_string("127.0.0.0"))); + g_assert(nm_ip4_addr_is_loopback(nmtst_inet4_from_string("127.0.0.1"))); + g_assert(nm_ip4_addr_is_loopback(nmtst_inet4_from_string("127.5.0.1"))); + g_assert(!nm_ip4_addr_is_loopback(nmtst_inet4_from_string("126.5.0.1"))); + g_assert(!nm_ip4_addr_is_loopback(nmtst_inet4_from_string("128.5.0.1"))); + g_assert(!nm_ip4_addr_is_loopback(nmtst_inet4_from_string("129.5.0.1"))); + g_assert_cmpint(nmtst_inet4_from_string("127.0.0.0"), ==, NM_IPV4LO_NETWORK); + g_assert_cmpint(nmtst_inet4_from_string("127.0.0.1"), ==, NM_IPV4LO_ADDR1); + g_assert_cmpint(nmtst_inet4_from_string("255.0.0.0"), ==, NM_IPV4LO_NETMASK); + g_assert_cmpint(nm_ip4_addr_netmask_to_prefix(NM_IPV4LO_NETMASK), ==, NM_IPV4LO_PREFIXLEN); } /*****************************************************************************/ static void -test_nm_utils_ip4_prefix_to_netmask(void) +test_nm_ip4_addr_netmask_from_prefix(void) { - g_assert_cmpint(_nm_utils_ip4_prefix_to_netmask(0), ==, nmtst_inet4_from_string("0.0.0.0")); - g_assert_cmpint(_nm_utils_ip4_prefix_to_netmask(1), ==, nmtst_inet4_from_string("128.0.0.0")); - g_assert_cmpint(_nm_utils_ip4_prefix_to_netmask(2), ==, nmtst_inet4_from_string("192.0.0.0")); - g_assert_cmpint(_nm_utils_ip4_prefix_to_netmask(16), + g_assert_cmpint(nm_ip4_addr_netmask_from_prefix(0), ==, nmtst_inet4_from_string("0.0.0.0")); + g_assert_cmpint(nm_ip4_addr_netmask_from_prefix(1), ==, nmtst_inet4_from_string("128.0.0.0")); + g_assert_cmpint(nm_ip4_addr_netmask_from_prefix(2), ==, nmtst_inet4_from_string("192.0.0.0")); + g_assert_cmpint(nm_ip4_addr_netmask_from_prefix(16), ==, nmtst_inet4_from_string("255.255.0.0")); - g_assert_cmpint(_nm_utils_ip4_prefix_to_netmask(24), + g_assert_cmpint(nm_ip4_addr_netmask_from_prefix(24), ==, nmtst_inet4_from_string("255.255.255.0")); - g_assert_cmpint(_nm_utils_ip4_prefix_to_netmask(30), + g_assert_cmpint(nm_ip4_addr_netmask_from_prefix(30), ==, nmtst_inet4_from_string("255.255.255.252")); - g_assert_cmpint(_nm_utils_ip4_prefix_to_netmask(31), + g_assert_cmpint(nm_ip4_addr_netmask_from_prefix(31), ==, nmtst_inet4_from_string("255.255.255.254")); - g_assert_cmpint(_nm_utils_ip4_prefix_to_netmask(32), + g_assert_cmpint(nm_ip4_addr_netmask_from_prefix(32), ==, nmtst_inet4_from_string("255.255.255.255")); } @@ -1294,7 +1334,7 @@ test_utils_hashtable_cmp(void) for (test_run = 0; test_run < 30; test_run++) { for (is_num_key = 0; is_num_key < 2; is_num_key++) { GHashFunc func_key_hash = is_num_key ? nm_direct_hash : nm_str_hash; - GEqualFunc func_key_equal = is_num_key ? g_direct_equal : g_str_equal; + GEqualFunc func_key_equal = is_num_key ? NULL : g_str_equal; GCompareDataFunc func_key_cmp = is_num_key ? _hash_func_cmp_direct : (GCompareDataFunc) nm_strcmp_with_data; GCompareDataFunc func_val_cmp = @@ -2211,6 +2251,124 @@ test_hostname_is_valid(void) /*****************************************************************************/ +static void +test_inet_utils(void) +{ + g_assert(nm_ip_addr_is_site_local(AF_INET, nmtst_inet_from_string(AF_INET, "172.16.0.1"))); + g_assert(nm_ip_addr_is_site_local(AF_INET, nmtst_inet_from_string(AF_INET, "172.17.0.1"))); + g_assert(nm_ip_addr_is_site_local(AF_INET, nmtst_inet_from_string(AF_INET, "192.168.7.5"))); + g_assert(!nm_ip_addr_is_site_local(AF_INET, nmtst_inet_from_string(AF_INET, "192.0.7.5"))); + g_assert(nm_ip_addr_is_site_local(AF_INET6, nmtst_inet_from_string(AF_INET6, "fec0::"))); + g_assert(!nm_ip_addr_is_site_local(AF_INET6, nmtst_inet_from_string(AF_INET6, "fc00::"))); +} + +/*****************************************************************************/ + +static void +test_garray(void) +{ + gs_unref_array GArray *arr = NULL; + int v; + + arr = g_array_new(FALSE, FALSE, sizeof(int)); + g_assert(nm_g_array_index_p(arr, int, 0) == (gpointer) arr->data); + + v = 1; + g_array_append_val(arr, v); + g_assert(nm_g_array_index_p(arr, int, 0) == (gpointer) arr->data); + g_assert(nm_g_array_index_p(arr, int, 1) == ((int *) ((gpointer) arr->data)) + 1); + g_assert(&nm_g_array_index(arr, int, 0) == (gpointer) arr->data); + g_assert(&nm_g_array_first(arr, int) == (gpointer) arr->data); + g_assert(&nm_g_array_last(arr, int) == (gpointer) arr->data); + g_assert(nm_g_array_index(arr, int, 0) == 1); + + v = 2; + g_array_append_val(arr, v); + g_assert(nm_g_array_index_p(arr, int, 0) == (gpointer) arr->data); + g_assert(nm_g_array_index_p(arr, int, 1) == ((int *) ((gpointer) arr->data)) + 1); + g_assert(nm_g_array_index_p(arr, int, 2) == ((int *) ((gpointer) arr->data)) + 2); + g_assert(&nm_g_array_index(arr, int, 0) == (gpointer) arr->data); + g_assert(&nm_g_array_first(arr, int) == (gpointer) arr->data); + g_assert(&nm_g_array_last(arr, int) == ((int *) ((gpointer) arr->data)) + 1); + g_assert(nm_g_array_index(arr, int, 0) == 1); + g_assert(nm_g_array_index(arr, int, 1) == 2); +} + +/*****************************************************************************/ + +static int +_prioq_cmp(gconstpointer a, gconstpointer b) +{ + NM_CMP_DIRECT(GPOINTER_TO_UINT(a), GPOINTER_TO_UINT(b)); + return 0; +} + +static int +_prioq_cmp_with_data(gconstpointer a, gconstpointer b, gpointer user_data) +{ + return _prioq_cmp(a, b); +} + +static void +test_nm_prioq(void) +{ + nm_auto_prioq NMPrioq q = NM_PRIOQ_ZERO; + gpointer data[200]; + gpointer data_pop[200]; + guint data_idx[G_N_ELEMENTS(data)]; + guint i; + guint n; + gpointer p; + + if (nmtst_get_rand_one_case_in(10)) + return; + + if (nmtst_get_rand_bool()) + nm_prioq_init(&q, _prioq_cmp); + else + nm_prioq_init_with_data(&q, _prioq_cmp_with_data, NULL); + + g_assert(nm_prioq_size(&q) == 0); + + if (nmtst_get_rand_one_case_in(10)) + return; + + for (i = 0; i < G_N_ELEMENTS(data); i++) { + data[i] = GUINT_TO_POINTER((nmtst_get_rand_uint32() % G_N_ELEMENTS(data)) + 1u); + data_idx[i] = NM_PRIOQ_IDX_NULL; + } + + nm_prioq_put(&q, data[0], NULL); + g_assert(nm_prioq_size(&q) == 1); + + p = nm_prioq_pop(&q); + g_assert(p == data[0]); + g_assert(nm_prioq_size(&q) == 0); + + g_assert(!nm_prioq_pop(&q)); + + n = nmtst_get_rand_uint32() % G_N_ELEMENTS(data); + for (i = 0; i < n; i++) + nm_prioq_put(&q, data[i], &data_idx[i]); + + g_assert_cmpint(nm_prioq_size(&q), ==, n); + + if (nmtst_get_rand_one_case_in(10)) + return; + + for (i = 0; i < n; i++) { + data_pop[i] = nm_prioq_pop(&q); + g_assert(data_pop[i]); + if (i > 0) + g_assert(_prioq_cmp(data_pop[i - 1], data_pop[i]) <= 0); + } + + g_assert(!nm_prioq_pop(&q)); + g_assert(nm_prioq_size(&q) == 0); +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -2221,14 +2379,14 @@ main(int argc, char **argv) g_test_add_func("/general/test_nm_static_assert", test_nm_static_assert); g_test_add_func("/general/test_gpid", test_gpid); g_test_add_func("/general/test_monotonic_timestamp", test_monotonic_timestamp); + g_test_add_func("/general/test_timespect_to", test_timespect_to); g_test_add_func("/general/test_nmhash", test_nmhash); g_test_add_func("/general/test_nm_make_strv", test_make_strv); g_test_add_func("/general/test_nm_strdup_int", test_nm_strdup_int); g_test_add_func("/general/test_nm_strndup_a", test_nm_strndup_a); - g_test_add_func("/general/test_nm_utils_ip4_address_is_loopback", - test_nm_utils_ip4_address_is_loopback); - g_test_add_func("/general/test_nm_utils_ip4_prefix_to_netmask", - test_nm_utils_ip4_prefix_to_netmask); + g_test_add_func("/general/test_nm_ip4_addr_is_loopback", test_nm_ip4_addr_is_loopback); + g_test_add_func("/general/test_nm_ip4_addr_netmask_from_prefix", + test_nm_ip4_addr_netmask_from_prefix); g_test_add_func("/general/test_unaligned", test_unaligned); g_test_add_func("/general/test_strv_cmp", test_strv_cmp); g_test_add_func("/general/test_strstrip_avoid_copy", test_strstrip_avoid_copy); @@ -2256,6 +2414,9 @@ main(int argc, char **argv) g_test_add_func("/general/test_path_startswith", test_path_startswith); g_test_add_func("/general/test_path_simplify", test_path_simplify); g_test_add_func("/general/test_hostname_is_valid", test_hostname_is_valid); + g_test_add_func("/general/test_inet_utils", test_inet_utils); + g_test_add_func("/general/test_garray", test_garray); + g_test_add_func("/general/test_nm_prioq", test_nm_prioq); return g_test_run(); } diff --git a/src/libnm-lldp/meson.build b/src/libnm-lldp/meson.build new file mode 100644 index 00000000..0d2065e8 --- /dev/null +++ b/src/libnm-lldp/meson.build @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +libnm_lldp = static_library( + 'nm-lldp', + sources: [ + 'nm-lldp-neighbor.c', + 'nm-lldp-network.c', + 'nm-lldp-rx.c', + ], + include_directories: [ + src_inc, + top_inc, + ], + dependencies: [ + glib_dep, + libudev_dep, + ], +) diff --git a/src/libnm-lldp/nm-lldp-neighbor.c b/src/libnm-lldp/nm-lldp-neighbor.c new file mode 100644 index 00000000..f1e2d42e --- /dev/null +++ b/src/libnm-lldp/nm-lldp-neighbor.c @@ -0,0 +1,842 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nm-lldp-neighbor.h" + +#include + +#include "libnm-std-aux/unaligned.h" +#include "libnm-glib-aux/nm-time-utils.h" +#include "nm-lldp-network.h" +#include "nm-lldp.h" +#include "nm-lldp-rx-internal.h" + +/*****************************************************************************/ + +guint +nm_lldp_neighbor_id_hash(const NMLldpNeighborID *id) +{ + NMHashState h; + + nm_assert(id); + + nm_hash_init(&h, 1925469911u); + nm_hash_update_mem(&h, id->chassis_id, id->chassis_id_size); + nm_hash_update_mem(&h, id->port_id, id->port_id_size); + return nm_hash_complete(&h); +} + +int +nm_lldp_neighbor_id_cmp(const NMLldpNeighborID *x, const NMLldpNeighborID *y) +{ + nm_assert(x); + nm_assert(y); + + NM_CMP_SELF(x, y); + NM_CMP_RETURN_DIRECT( + nm_memcmp_n(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size, 1)); + NM_CMP_RETURN_DIRECT(nm_memcmp_n(x->port_id, x->port_id_size, y->port_id, y->port_id_size, 1)); + return 0; +} + +gboolean +nm_lldp_neighbor_id_equal(const NMLldpNeighborID *x, const NMLldpNeighborID *y) +{ + return nm_lldp_neighbor_id_cmp(x, y) == 0; +} + +int +nm_lldp_neighbor_prioq_compare_func(const void *a, const void *b) +{ + const NMLldpNeighbor *x = a; + const NMLldpNeighbor *y = b; + + nm_assert(x); + nm_assert(y); + + NM_CMP_FIELD(x, y, until_usec); + return 0; +} + +static int +parse_string(NMLldpRX *lldp_rx, char **s, const void *q, size_t n) +{ + const char *p = q; + char *k; + + nm_assert(s); + nm_assert(p || n == 0); + + if (*s) { + _LOG2D(lldp_rx, "Found duplicate string, ignoring field."); + return 0; + } + + /* Strip trailing NULs, just to be nice */ + while (n > 0 && p[n - 1] == 0) + n--; + + if (n <= 0) /* Ignore empty strings */ + return 0; + + /* Look for inner NULs */ + if (memchr(p, 0, n)) { + _LOG2D(lldp_rx, "Found inner NUL in string, ignoring field."); + return 0; + } + + /* Let's escape weird chars, for security reasons */ + k = nm_utils_buf_utf8safe_escape_cp(p, + n, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL + | NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII); + + g_free(*s); + *s = k; + + return 1; +} + +int +nm_lldp_neighbor_parse(NMLldpNeighbor *n) +{ + struct ether_header h; + const uint8_t *p; + size_t left; + int r; + + nm_assert(n); + + if (n->raw_size < sizeof(struct ether_header)) { + _LOG2D(n->lldp_rx, "Received truncated packet, ignoring."); + return -NME_UNSPEC; + } + + memcpy(&h, NM_LLDP_NEIGHBOR_RAW(n), sizeof(h)); + + if (h.ether_type != htobe16(NM_ETHERTYPE_LLDP)) { + _LOG2D(n->lldp_rx, "Received packet with wrong type, ignoring."); + return -NME_UNSPEC; + } + + if (h.ether_dhost[0] != 0x01 || h.ether_dhost[1] != 0x80 || h.ether_dhost[2] != 0xc2 + || h.ether_dhost[3] != 0x00 || h.ether_dhost[4] != 0x00 + || !NM_IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) { + _LOG2D(n->lldp_rx, "Received packet with wrong destination address, ignoring."); + return -NME_UNSPEC; + } + + memcpy(&n->source_address, h.ether_shost, sizeof(NMEtherAddr)); + memcpy(&n->destination_address, h.ether_dhost, sizeof(NMEtherAddr)); + + p = (const uint8_t *) NM_LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header); + left = n->raw_size - sizeof(struct ether_header); + + for (;;) { + uint8_t type; + uint16_t length; + + if (left < 2) { + _LOG2D(n->lldp_rx, "TLV lacks header, ignoring."); + return -NME_UNSPEC; + } + + type = p[0] >> 1; + length = p[1] + (((uint16_t) (p[0] & 1)) << 8); + p += 2, left -= 2; + + if (left < length) { + _LOG2D(n->lldp_rx, "TLV truncated, ignoring datagram."); + return -NME_UNSPEC; + } + + switch (type) { + case NM_LLDP_TYPE_END: + if (length != 0) { + _LOG2D(n->lldp_rx, "End marker TLV not zero-sized, ignoring datagram."); + return -NME_UNSPEC; + } + + /* Note that after processing the NM_LLDP_TYPE_END left could still be > 0 + * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */ + + goto end_marker; + + case NM_LLDP_TYPE_CHASSIS_ID: + if (length < 2 || length > 256) { + /* includes the chassis subtype, hence one extra byte */ + _LOG2D(n->lldp_rx, "Chassis ID field size out of range, ignoring datagram."); + return -NME_UNSPEC; + } + + if (n->id.chassis_id) { + _LOG2D(n->lldp_rx, "Duplicate chassis ID field, ignoring datagram."); + return -NME_UNSPEC; + } + + n->id.chassis_id = nm_memdup(p, length); + n->id.chassis_id_size = length; + break; + + case NM_LLDP_TYPE_PORT_ID: + if (length < 2 || length > 256) { + /* includes the port subtype, hence one extra byte */ + _LOG2D(n->lldp_rx, "Port ID field size out of range, ignoring datagram."); + return -NME_UNSPEC; + } + + if (n->id.port_id) { + _LOG2D(n->lldp_rx, "Duplicate port ID field, ignoring datagram."); + return -NME_UNSPEC; + } + + n->id.port_id = nm_memdup(p, length); + n->id.port_id_size = length; + break; + + case NM_LLDP_TYPE_TTL: + if (length != 2) { + _LOG2D(n->lldp_rx, "TTL field has wrong size, ignoring datagram."); + return -NME_UNSPEC; + } + + if (n->has_ttl) { + _LOG2D(n->lldp_rx, "Duplicate TTL field, ignoring datagram."); + return -NME_UNSPEC; + } + + n->ttl = unaligned_read_be16(p); + n->has_ttl = true; + break; + + case NM_LLDP_TYPE_PORT_DESCRIPTION: + r = parse_string(n->lldp_rx, &n->port_description, p, length); + if (r < 0) + return r; + break; + + case NM_LLDP_TYPE_SYSTEM_NAME: + r = parse_string(n->lldp_rx, &n->system_name, p, length); + if (r < 0) + return r; + break; + + case NM_LLDP_TYPE_SYSTEM_DESCRIPTION: + r = parse_string(n->lldp_rx, &n->system_description, p, length); + if (r < 0) + return r; + break; + + case NM_LLDP_TYPE_SYSTEM_CAPABILITIES: + if (length != 4) { + _LOG2D(n->lldp_rx, "System capabilities field has wrong size."); + return -NME_UNSPEC; + } + + n->system_capabilities = unaligned_read_be16(p); + n->enabled_capabilities = unaligned_read_be16(p + 2); + n->has_capabilities = true; + break; + + case NM_LLDP_TYPE_PRIVATE: + if (length < 4) { + _LOG2D(n->lldp_rx, "Found private TLV that is too short, ignoring."); + return -NME_UNSPEC; + } + + /* RFC 8520: MUD URL */ + if (memcmp(p, NM_LLDP_OUI_IANA_MUD, sizeof(NM_LLDP_OUI_IANA_MUD)) == 0) { + r = parse_string(n->lldp_rx, + &n->mud_url, + p + sizeof(NM_LLDP_OUI_IANA_MUD), + length - sizeof(NM_LLDP_OUI_IANA_MUD)); + if (r < 0) + return r; + } + break; + } + + p += length, left -= length; + } + +end_marker: + if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) { + _LOG2D(n->lldp_rx, "One or more mandatory TLV missing in datagram. Ignoring."); + return -NME_UNSPEC; + } + + n->rindex = sizeof(struct ether_header); + + return 0; +} + +void +nm_lldp_neighbor_start_ttl(NMLldpNeighbor *n) +{ + nm_assert(n); + + if (n->ttl > 0) { + /* Use the packet's timestamp if there is one known */ + if (n->timestamp_usec <= 0) { + /* Otherwise, take the current time */ + n->timestamp_usec = nm_utils_get_monotonic_timestamp_usec(); + } + + n->until_usec = n->timestamp_usec + (n->ttl * NM_UTILS_USEC_PER_SEC); + } else + n->until_usec = 0; + + if (n->lldp_rx) + nm_prioq_reshuffle(&n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx); +} + +int +nm_lldp_neighbor_cmp(const NMLldpNeighbor *a, const NMLldpNeighbor *b) +{ + NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, raw_size); + NM_CMP_DIRECT_MEMCMP(NM_LLDP_NEIGHBOR_RAW(a), NM_LLDP_NEIGHBOR_RAW(b), a->raw_size); + return 0; +} + +int +nm_lldp_neighbor_get_source_address(NMLldpNeighbor *n, NMEtherAddr *address) +{ + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(address, -EINVAL); + + *address = n->source_address; + return 0; +} + +int +nm_lldp_neighbor_get_destination_address(NMLldpNeighbor *n, NMEtherAddr *address) +{ + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(address, -EINVAL); + + *address = n->destination_address; + return 0; +} + +int +nm_lldp_neighbor_get_raw(NMLldpNeighbor *n, const void **ret, size_t *size) +{ + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(ret, -EINVAL); + g_return_val_if_fail(size, -EINVAL); + + *ret = NM_LLDP_NEIGHBOR_RAW(n); + *size = n->raw_size; + + return 0; +} + +int +nm_lldp_neighbor_get_chassis_id(NMLldpNeighbor *n, uint8_t *type, const void **ret, size_t *size) +{ + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(type, -EINVAL); + g_return_val_if_fail(ret, -EINVAL); + g_return_val_if_fail(size, -EINVAL); + + nm_assert(n->id.chassis_id_size > 0); + + *type = *(uint8_t *) n->id.chassis_id; + *ret = (uint8_t *) n->id.chassis_id + 1; + *size = n->id.chassis_id_size - 1; + + return 0; +} + +static char * +format_mac_address(const void *data, size_t sz) +{ + NMEtherAddr a; + + nm_assert(data || sz <= 0); + + if (sz != 7) + return NULL; + + memcpy(&a, (uint8_t *) data + 1, sizeof(a)); + return nm_ether_addr_to_string_dup(&a); +} + +static char * +format_network_address(const void *data, size_t sz) +{ + int addr_family; + NMIPAddr a; + + if (sz == 6 && ((uint8_t *) data)[1] == 1) { + memcpy(&a.addr4, (uint8_t *) data + 2, sizeof(a.addr4)); + addr_family = AF_INET; + } else if (sz == 18 && ((uint8_t *) data)[1] == 2) { + memcpy(&a.addr6, (uint8_t *) data + 2, sizeof(a.addr6)); + addr_family = AF_INET6; + } else + return NULL; + + return nm_inet_ntop_dup(addr_family, &a); +} + +const char * +nm_lldp_neighbor_get_chassis_id_as_string(NMLldpNeighbor *n) +{ + char *k; + + g_return_val_if_fail(n, NULL); + + if (n->chassis_id_as_string) + return n->chassis_id_as_string; + + nm_assert(n->id.chassis_id_size > 0); + + switch (*(uint8_t *) n->id.chassis_id) { + case NM_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT: + case NM_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS: + case NM_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT: + case NM_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME: + case NM_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED: + k = nm_utils_buf_utf8safe_escape_cp((char *) n->id.chassis_id + 1, + n->id.chassis_id_size - 1, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL + | NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII); + goto done; + + case NM_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS: + k = format_mac_address(n->id.chassis_id, n->id.chassis_id_size); + if (k) + goto done; + break; + + case NM_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS: + k = format_network_address(n->id.chassis_id, n->id.chassis_id_size); + if (k) + goto done; + break; + } + + /* Generic fallback */ + k = nm_utils_bin2hexstr_full(n->id.chassis_id, n->id.chassis_id_size, '\0', FALSE, NULL); + +done: + nm_assert(k); + return (n->chassis_id_as_string = k); +} + +int +nm_lldp_neighbor_get_port_id(NMLldpNeighbor *n, uint8_t *type, const void **ret, size_t *size) +{ + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(type, -EINVAL); + g_return_val_if_fail(ret, -EINVAL); + g_return_val_if_fail(size, -EINVAL); + + nm_assert(n->id.port_id_size > 0); + + *type = *(uint8_t *) n->id.port_id; + *ret = (uint8_t *) n->id.port_id + 1; + *size = n->id.port_id_size - 1; + + return 0; +} + +const char * +nm_lldp_neighbor_get_port_id_as_string(NMLldpNeighbor *n) +{ + char *k; + + g_return_val_if_fail(n, NULL); + + if (n->port_id_as_string) + return n->port_id_as_string; + + nm_assert(n->id.port_id_size > 0); + + switch (*(uint8_t *) n->id.port_id) { + case NM_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS: + case NM_LLDP_PORT_SUBTYPE_PORT_COMPONENT: + case NM_LLDP_PORT_SUBTYPE_INTERFACE_NAME: + case NM_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED: + k = nm_utils_buf_utf8safe_escape_cp((char *) n->id.port_id + 1, + n->id.port_id_size - 1, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL + | NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII); + goto done; + + case NM_LLDP_PORT_SUBTYPE_MAC_ADDRESS: + k = format_mac_address(n->id.port_id, n->id.port_id_size); + if (k) + goto done; + break; + + case NM_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS: + k = format_network_address(n->id.port_id, n->id.port_id_size); + if (k) + goto done; + break; + } + + /* Generic fallback */ + k = nm_utils_bin2hexstr_full(n->id.port_id, n->id.port_id_size, '\0', FALSE, NULL); + +done: + nm_assert(k); + return (n->port_id_as_string = k); +} + +int +nm_lldp_neighbor_get_ttl(NMLldpNeighbor *n, uint16_t *ret_sec) +{ + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(ret_sec, -EINVAL); + + *ret_sec = n->ttl; + return 0; +} + +int +nm_lldp_neighbor_get_system_name(NMLldpNeighbor *n, const char **ret) +{ + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(ret, -EINVAL); + + if (!n->system_name) + return -ENODATA; + + *ret = n->system_name; + return 0; +} + +int +nm_lldp_neighbor_get_system_description(NMLldpNeighbor *n, const char **ret) +{ + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(ret, -EINVAL); + + if (!n->system_description) + return -ENODATA; + + *ret = n->system_description; + return 0; +} + +int +nm_lldp_neighbor_get_port_description(NMLldpNeighbor *n, const char **ret) +{ + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(ret, -EINVAL); + + if (!n->port_description) + return -ENODATA; + + *ret = n->port_description; + return 0; +} + +int +nm_lldp_neighbor_get_mud_url(NMLldpNeighbor *n, const char **ret) +{ + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(ret, -EINVAL); + + if (!n->mud_url) + return -ENODATA; + + *ret = n->mud_url; + return 0; +} + +int +nm_lldp_neighbor_get_system_capabilities(NMLldpNeighbor *n, uint16_t *ret) +{ + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(ret, -EINVAL); + + if (!n->has_capabilities) + return -ENODATA; + + *ret = n->system_capabilities; + return 0; +} + +int +nm_lldp_neighbor_get_enabled_capabilities(NMLldpNeighbor *n, uint16_t *ret) +{ + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(ret, -EINVAL); + + if (!n->has_capabilities) + return -ENODATA; + + *ret = n->enabled_capabilities; + return 0; +} + +int +nm_lldp_neighbor_tlv_rewind(NMLldpNeighbor *n) +{ + g_return_val_if_fail(n, -EINVAL); + + nm_assert(n->raw_size >= sizeof(struct ether_header)); + + n->rindex = sizeof(struct ether_header); + + return n->rindex < n->raw_size; +} + +int +nm_lldp_neighbor_tlv_next(NMLldpNeighbor *n) +{ + size_t length; + + g_return_val_if_fail(n, -EINVAL); + + if (n->rindex == n->raw_size) /* EOF */ + return -ESPIPE; + + if (n->rindex + 2 > n->raw_size) /* Truncated message */ + return -EBADMSG; + + length = NM_LLDP_NEIGHBOR_TLV_LENGTH(n); + if (n->rindex + 2 + length > n->raw_size) + return -EBADMSG; + + n->rindex += 2 + length; + return n->rindex < n->raw_size; +} + +int +nm_lldp_neighbor_tlv_get_type(NMLldpNeighbor *n, uint8_t *type) +{ + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(type, -EINVAL); + + if (n->rindex == n->raw_size) /* EOF */ + return -ESPIPE; + + if (n->rindex + 2 > n->raw_size) + return -EBADMSG; + + *type = NM_LLDP_NEIGHBOR_TLV_TYPE(n); + return 0; +} + +int +nm_lldp_neighbor_tlv_is_type(NMLldpNeighbor *n, uint8_t type) +{ + uint8_t k; + int r; + + g_return_val_if_fail(n, -EINVAL); + + r = nm_lldp_neighbor_tlv_get_type(n, &k); + if (r < 0) + return r; + + return type == k; +} + +int +nm_lldp_neighbor_tlv_get_oui(NMLldpNeighbor *n, uint8_t oui[static 3], uint8_t *subtype) +{ + const uint8_t *d; + size_t length; + int r; + + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(oui, -EINVAL); + g_return_val_if_fail(subtype, -EINVAL); + + r = nm_lldp_neighbor_tlv_is_type(n, NM_LLDP_TYPE_PRIVATE); + if (r < 0) + return r; + if (r == 0) + return -ENXIO; + + length = NM_LLDP_NEIGHBOR_TLV_LENGTH(n); + if (length < 4) + return -EBADMSG; + + if (n->rindex + 2 + length > n->raw_size) + return -EBADMSG; + + d = NM_LLDP_NEIGHBOR_TLV_DATA(n); + memcpy(oui, d, 3); + *subtype = d[3]; + + return 0; +} + +int +nm_lldp_neighbor_tlv_is_oui(NMLldpNeighbor *n, const uint8_t oui[static 3], uint8_t subtype) +{ + uint8_t k[3], st; + int r; + + r = nm_lldp_neighbor_tlv_get_oui(n, k, &st); + if (r == -ENXIO) + return 0; + if (r < 0) + return r; + + return memcmp(k, oui, 3) == 0 && st == subtype; +} + +int +nm_lldp_neighbor_tlv_get_raw(NMLldpNeighbor *n, const void **ret, size_t *size) +{ + size_t length; + + g_return_val_if_fail(n, -EINVAL); + g_return_val_if_fail(ret, -EINVAL); + g_return_val_if_fail(size, -EINVAL); + + /* Note that this returns the full TLV, including the TLV header */ + + if (n->rindex + 2 > n->raw_size) + return -EBADMSG; + + length = NM_LLDP_NEIGHBOR_TLV_LENGTH(n); + if (n->rindex + 2 + length > n->raw_size) + return -EBADMSG; + + *ret = (uint8_t *) NM_LLDP_NEIGHBOR_RAW(n) + n->rindex; + *size = length + 2; + + return 0; +} + +int +nm_lldp_neighbor_get_timestamp_usec(NMLldpNeighbor *n, gint64 *out_usec) +{ + g_return_val_if_fail(n, -EINVAL); + + if (n->timestamp_usec == 0) + return -ENODATA; + + NM_SET_OUT(out_usec, n->timestamp_usec); + return 0; +} + +/*****************************************************************************/ + +NMLldpNeighbor * +nm_lldp_neighbor_new(size_t raw_size) +{ + NMLldpNeighbor *n; + + nm_assert(raw_size < SIZE_MAX - NM_ALIGN(sizeof(NMLldpNeighbor))); + + n = g_malloc0(NM_ALIGN(sizeof(NMLldpNeighbor)) + raw_size); + + n->raw_size = raw_size; + n->ref_count = 1; + return n; +} + +NMLldpNeighbor * +nm_lldp_neighbor_new_from_raw(const void *raw, size_t raw_size) +{ + nm_auto(nm_lldp_neighbor_unrefp) NMLldpNeighbor *n = NULL; + int r; + + g_return_val_if_fail(raw || raw_size <= 0, NULL); + + n = nm_lldp_neighbor_new(raw_size); + + nm_memcpy(NM_LLDP_NEIGHBOR_RAW(n), raw, raw_size); + + r = nm_lldp_neighbor_parse(n); + if (r < 0) + return NULL; + + return g_steal_pointer(&n); +} + +NMLldpNeighbor * +nm_lldp_neighbor_ref(NMLldpNeighbor *n) +{ + if (!n) + return NULL; + + nm_assert(n->ref_count > 0 || n->lldp_rx); + + n->ref_count++; + return n; +} + +static void +_lldp_neighbor_free(NMLldpNeighbor *n) +{ + if (!n) + return; + + g_free((gpointer) n->id.port_id); + g_free((gpointer) n->id.chassis_id); + g_free(n->port_description); + g_free(n->system_name); + g_free(n->system_description); + g_free(n->mud_url); + g_free(n->chassis_id_as_string); + g_free(n->port_id_as_string); + g_free(n); + return; +} + +NMLldpNeighbor * +nm_lldp_neighbor_unref(NMLldpNeighbor *n) +{ + /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from + * the sd_lldp object. */ + + if (!n) + return NULL; + + nm_assert(n->ref_count > 0); + n->ref_count--; + + if (n->ref_count <= 0 && !n->lldp_rx) + _lldp_neighbor_free(n); + + return NULL; +} + +void +nm_lldp_neighbor_unlink(NMLldpNeighbor *n) +{ + gpointer old_key; + gpointer old_val; + + /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */ + + if (!n) + return; + + if (!n->lldp_rx) + return; + + /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is + * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register + * ourselves from the hashtable and sometimes are called after we already are de-registered. */ + + if (g_hash_table_steal_extended(n->lldp_rx->neighbor_by_id, n, &old_key, &old_val)) { + nm_assert(NM_IN_SET(old_val, NULL, old_key)); + if (old_key != n) { + /* it wasn't the right key. Add it again. */ + g_hash_table_add(n->lldp_rx->neighbor_by_id, old_key); + } + } + + nm_prioq_remove(&n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx); + + n->lldp_rx = NULL; + + if (n->ref_count <= 0) + _lldp_neighbor_free(n); + + return; +} diff --git a/src/libnm-lldp/nm-lldp-neighbor.h b/src/libnm-lldp/nm-lldp-neighbor.h new file mode 100644 index 00000000..1adc967e --- /dev/null +++ b/src/libnm-lldp/nm-lldp-neighbor.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef __NM_LLDP_NEIGHBOR_H__ +#define __NM_LLDP_NEIGHBOR_H__ + +#include "nm-lldp-rx.h" + +struct _NMLldpNeighbor { + NMLldpNeighborID id; + + /* Neighbor objects stay around as long as they are linked into an "NMLldpRX" object or n_ref > 0. */ + struct _NMLldpRX *lldp_rx; + + gint64 timestamp_usec; + + gint64 until_usec; + + int ref_count; + unsigned prioq_idx; + + NMEtherAddr source_address; + NMEtherAddr destination_address; + + /* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */ + size_t raw_size; + + /* The current read index for the iterative TLV interface */ + size_t rindex; + + /* And a couple of fields parsed out. */ + bool has_ttl : 1; + bool has_capabilities : 1; + bool has_port_vlan_id : 1; + + uint16_t ttl; + + uint16_t system_capabilities; + uint16_t enabled_capabilities; + + char *port_description; + char *system_name; + char *system_description; + char *mud_url; + + uint16_t port_vlan_id; + + char *chassis_id_as_string; + char *port_id_as_string; +}; + +static inline void * +NM_LLDP_NEIGHBOR_RAW(const NMLldpNeighbor *n) +{ + return (uint8_t *) n + NM_ALIGN(sizeof(NMLldpNeighbor)); +} + +static inline uint8_t +NM_LLDP_NEIGHBOR_TLV_TYPE(const NMLldpNeighbor *n) +{ + return ((uint8_t *) NM_LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1; +} + +static inline size_t +NM_LLDP_NEIGHBOR_TLV_LENGTH(const NMLldpNeighbor *n) +{ + uint8_t *p; + + p = (uint8_t *) NM_LLDP_NEIGHBOR_RAW(n) + n->rindex; + return p[1] + (((size_t) (p[0] & 1)) << 8); +} + +static inline void * +NM_LLDP_NEIGHBOR_TLV_DATA(const NMLldpNeighbor *n) +{ + return ((uint8_t *) NM_LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2; +} + +int nm_lldp_neighbor_prioq_compare_func(const void *a, const void *b); + +void nm_lldp_neighbor_unlink(NMLldpNeighbor *n); +NMLldpNeighbor *nm_lldp_neighbor_new(size_t raw_size); +int nm_lldp_neighbor_parse(NMLldpNeighbor *n); +void nm_lldp_neighbor_start_ttl(NMLldpNeighbor *n); + +#endif /* __NM_LLDP_NEIGHBOR_H__ */ diff --git a/src/libnm-lldp/nm-lldp-network.c b/src/libnm-lldp/nm-lldp-network.c new file mode 100644 index 00000000..811c3a72 --- /dev/null +++ b/src/libnm-lldp/nm-lldp-network.c @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nm-lldp-network.h" + +#include +#include +#include + +int +nm_lldp_network_bind_raw_socket(int ifindex) +{ + static const struct sock_filter filter[] = { + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, + offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */ + BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, + offsetof(struct ethhdr, h_dest) + + 4), /* A <- remaining 2 bytes of destination MAC */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0), /* A != 00:00 */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0), /* A != 00:03 */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0), /* A != 00:0e */ + BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)), /* A <- protocol */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, NM_ETHERTYPE_LLDP, 1, 0), /* A != NM_ETHERTYPE_LLDP */ + BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ + BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept packet */ + }; + static const struct sock_fprog fprog = { + .len = G_N_ELEMENTS(filter), + .filter = (struct sock_filter *) filter, + }; + struct packet_mreq mreq = { + .mr_ifindex = ifindex, + .mr_type = PACKET_MR_MULTICAST, + .mr_alen = ETH_ALEN, + .mr_address = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x00}, + }; + struct sockaddr_ll saddrll = { + .sll_family = AF_PACKET, + .sll_ifindex = ifindex, + }; + nm_auto_close int fd = -1; + + assert(ifindex > 0); + + fd = socket(AF_PACKET, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, htobe16(NM_ETHERTYPE_LLDP)); + if (fd < 0) + return -errno; + + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) + return -errno; + + /* customer bridge */ + if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + return -errno; + + /* non TPMR bridge */ + mreq.mr_address[ETH_ALEN - 1] = 0x03; + if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + return -errno; + + /* nearest bridge */ + mreq.mr_address[ETH_ALEN - 1] = 0x0E; + if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + return -errno; + + if (bind(fd, (const struct sockaddr *) &saddrll, sizeof(saddrll)) < 0) + return -errno; + + return nm_steal_fd(&fd); +} diff --git a/src/libnm-lldp/nm-lldp-network.h b/src/libnm-lldp/nm-lldp-network.h new file mode 100644 index 00000000..431ac60f --- /dev/null +++ b/src/libnm-lldp/nm-lldp-network.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef __NM_LLDP_NETWORK_H__ +#define __NM_LLDP_NETWORK_H__ + +#define NM_ETHERTYPE_LLDP 0x88cc + +int nm_lldp_network_bind_raw_socket(int ifindex); + +#endif /* __NM_LLDP_NETWORK_H__ */ diff --git a/src/libnm-lldp/nm-lldp-rx-internal.h b/src/libnm-lldp/nm-lldp-rx-internal.h new file mode 100644 index 00000000..47d063ae --- /dev/null +++ b/src/libnm-lldp/nm-lldp-rx-internal.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef __NM_LLDP_RX_INTERNAL_H__ +#define __NM_LLDP_RX_INTERNAL_H__ + +#include "libnm-glib-aux/nm-prioq.h" +#include "libnm-log-core/nm-logging.h" + +#include "nm-lldp-rx.h" + +struct _NMLldpRX { + int ref_count; + + int fd; + + NMLldpRXConfig config; + + GMainContext *main_context; + + GSource *io_event_source; + GSource *timer_event_source; + + GHashTable *neighbor_by_id; + NMPrioq neighbor_by_expiry; +}; + +/*****************************************************************************/ + +#define _NMLOG2_DOMAIN LOGD_PLATFORM +#define _NMLOG2(level, lldp_rx, ...) \ + G_STMT_START \ + { \ + const NMLogLevel _level = (level); \ + NMLldpRX *_lldp_rx = (lldp_rx); \ + \ + if (_NMLOG2_ENABLED(_level)) { \ + _nm_log(level, \ + _NMLOG2_DOMAIN, \ + 0, \ + _lldp_rx->config.log_ifname, \ + _lldp_rx->config.log_uuid, \ + "lldp-rx[" NM_HASH_OBFUSCATE_PTR_FMT \ + "%s%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + NM_HASH_OBFUSCATE_PTR(_lldp_rx), \ + NM_PRINT_FMT_QUOTED2(_lldp_rx->config.log_ifname, \ + ", ", \ + _lldp_rx->config.log_ifname, \ + "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } \ + G_STMT_END + +/*****************************************************************************/ + +#endif /* __NM_LLDP_RX_INTERNAL_H__ */ diff --git a/src/libnm-lldp/nm-lldp-rx.c b/src/libnm-lldp/nm-lldp-rx.c new file mode 100644 index 00000000..6d0f4a18 --- /dev/null +++ b/src/libnm-lldp/nm-lldp-rx.c @@ -0,0 +1,469 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nm-lldp-rx.h" + +#include +#include +#include + +#include "libnm-glib-aux/nm-io-utils.h" +#include "libnm-glib-aux/nm-time-utils.h" +#include "nm-lldp-network.h" +#include "nm-lldp-neighbor.h" +#include "nm-lldp-rx-internal.h" + +#define LLDP_DEFAULT_NEIGHBORS_MAX 128U + +/*****************************************************************************/ + +static void lldp_rx_start_timer(NMLldpRX *lldp_rx, NMLldpNeighbor *neighbor); + +/*****************************************************************************/ + +NM_UTILS_LOOKUP_STR_DEFINE(nm_lldp_rx_event_to_string, + NMLldpRXEvent, + NM_UTILS_LOOKUP_DEFAULT_WARN(""), + NM_UTILS_LOOKUP_STR_ITEM(NM_LLDP_RX_EVENT_ADDED, "added"), + NM_UTILS_LOOKUP_STR_ITEM(NM_LLDP_RX_EVENT_REMOVED, "removed"), + NM_UTILS_LOOKUP_STR_ITEM(NM_LLDP_RX_EVENT_UPDATED, "updated"), + NM_UTILS_LOOKUP_STR_ITEM(NM_LLDP_RX_EVENT_REFRESHED, "refreshed"), + NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER()); + +/*****************************************************************************/ + +#define nm_assert_is_lldp_rx(lldp_rx) \ + G_STMT_START \ + { \ + NMLldpRX *_lldp_rx = (lldp_rx); \ + \ + nm_assert(_lldp_rx); \ + nm_assert(_lldp_rx->ref_count > 0); \ + } \ + G_STMT_END + +/*****************************************************************************/ + +/* This needs to be first. Check nm_lldp_rx_get_id(). */ +G_STATIC_ASSERT(G_STRUCT_OFFSET(NMLldpNeighbor, id) == 0); + +/*****************************************************************************/ + +static void +lldp_rx_callback(NMLldpRX *lldp_rx, NMLldpRXEvent event, NMLldpNeighbor *n) +{ + nm_assert_is_lldp_rx(lldp_rx); + nm_assert(event >= 0 && event < _NM_LLDP_RX_EVENT_MAX); + + _LOG2D(lldp_rx, "invoking callback for '%s' event", nm_lldp_rx_event_to_string(event)); + lldp_rx->config.callback(lldp_rx, event, n, lldp_rx->config.userdata); +} + +static gboolean +lldp_rx_make_space(NMLldpRX *lldp_rx, gboolean flush, size_t extra) +{ + nm_auto(nm_lldp_rx_unrefp) NMLldpRX *lldp_rx_alive = NULL; + gint64 now_usec = 0; + gboolean changed = FALSE; + size_t max; + + /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries + * are free. */ + + max = (!flush && lldp_rx->config.neighbors_max > extra) + ? (lldp_rx->config.neighbors_max - extra) + : 0u; + + for (;;) { + NMLldpNeighbor *n; + + nm_assert(g_hash_table_size(lldp_rx->neighbor_by_id) + == nm_prioq_size(&lldp_rx->neighbor_by_expiry)); + + n = nm_prioq_peek(&lldp_rx->neighbor_by_expiry); + if (!n) + break; + + if (nm_prioq_size(&lldp_rx->neighbor_by_expiry) > max) { + /* drop it. */ + } else { + if (n->until_usec > nm_utils_get_monotonic_timestamp_usec_cached(&now_usec)) + break; + } + + if (flush) { + changed = TRUE; + nm_lldp_neighbor_unlink(n); + } else { + nm_auto(nm_lldp_neighbor_unrefp) NMLldpNeighbor *n_alive = NULL; + + if (!changed) { + lldp_rx_alive = nm_lldp_rx_ref(lldp_rx); + changed = TRUE; + } + n_alive = nm_lldp_neighbor_ref(n); + nm_lldp_neighbor_unlink(n); + lldp_rx_callback(lldp_rx, NM_LLDP_RX_EVENT_REMOVED, n); + } + } + + return changed; +} + +static bool +lldp_rx_keep_neighbor(NMLldpRX *lldp_rx, NMLldpNeighbor *n) +{ + nm_assert_is_lldp_rx(lldp_rx); + nm_assert(n); + + /* Don't keep data with a zero TTL */ + if (n->ttl <= 0) + return FALSE; + + /* Filter out data from the filter address */ + if (!nm_ether_addr_is_zero(&lldp_rx->config.filter_address) + && nm_ether_addr_equal(&lldp_rx->config.filter_address, &n->source_address)) + return FALSE; + + /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with + * no caps field set. */ + if (n->has_capabilities && (n->enabled_capabilities & lldp_rx->config.capability_mask) == 0) + return FALSE; + + /* Keep everything else */ + return TRUE; +} + +static void +lldp_rx_add_neighbor(NMLldpRX *lldp_rx, NMLldpNeighbor *n) +{ + nm_auto(nm_lldp_neighbor_unrefp) NMLldpNeighbor *old_alive = NULL; + NMLldpNeighbor *old; + gboolean keep; + + nm_assert_is_lldp_rx(lldp_rx); + nm_assert(n); + nm_assert(!n->lldp_rx); + + keep = lldp_rx_keep_neighbor(lldp_rx, n); + + /* First retrieve the old entry for this MSAP */ + old = g_hash_table_lookup(lldp_rx->neighbor_by_id, n); + if (old) { + old_alive = nm_lldp_neighbor_ref(old); + + if (!keep) { + nm_lldp_neighbor_unlink(old); + lldp_rx_callback(lldp_rx, NM_LLDP_RX_EVENT_REMOVED, old); + return; + } + + if (nm_lldp_neighbor_equal(n, old)) { + /* Is this equal, then restart the TTL counter, but don't do anything else. */ + old->timestamp_usec = n->timestamp_usec; + lldp_rx_start_timer(lldp_rx, old); + lldp_rx_callback(lldp_rx, NM_LLDP_RX_EVENT_REFRESHED, old); + return; + } + + /* Data changed, remove the old entry, and add a new one */ + nm_lldp_neighbor_unlink(old); + + } else if (!keep) + return; + + /* Then, make room for at least one new neighbor */ + lldp_rx_make_space(lldp_rx, FALSE, 1); + + if (!g_hash_table_add(lldp_rx->neighbor_by_id, n)) + nm_assert_not_reached(); + + nm_prioq_put(&lldp_rx->neighbor_by_expiry, n, &n->prioq_idx); + + n->lldp_rx = lldp_rx; + + lldp_rx_start_timer(lldp_rx, n); + lldp_rx_callback(lldp_rx, old ? NM_LLDP_RX_EVENT_UPDATED : NM_LLDP_RX_EVENT_ADDED, n); +} + +static gboolean +lldp_rx_receive_datagram(int fd, GIOCondition condition, gpointer user_data) + +{ + NMLldpRX *lldp_rx = user_data; + nm_auto(nm_lldp_neighbor_unrefp) NMLldpNeighbor *n = NULL; + ssize_t space; + ssize_t length; + struct timespec ts; + gint64 ts_usec; + gint64 now_usec; + gint64 now_usec_rt; + gint64 now_usec_bt; + int r; + + nm_assert_is_lldp_rx(lldp_rx); + nm_assert(lldp_rx->fd == fd); + + _LOG2T(lldp_rx, "fd ready"); + + space = nm_fd_next_datagram_size(lldp_rx->fd); + if (space < 0) { + if (!NM_ERRNO_IS_TRANSIENT(space) && !NM_ERRNO_IS_DISCONNECT(space)) { + _LOG2D(lldp_rx, + "Failed to determine datagram size to read, ignoring: %s", + nm_strerror_native(-space)); + } + return G_SOURCE_CONTINUE; + } + + n = nm_lldp_neighbor_new(space); + + length = recv(lldp_rx->fd, NM_LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT); + if (length < 0) { + if (!NM_ERRNO_IS_TRANSIENT(errno) && !NM_ERRNO_IS_DISCONNECT(errno)) { + _LOG2D(lldp_rx, + "Failed to read LLDP datagram, ignoring: %s", + nm_strerror_native(errno)); + } + return G_SOURCE_CONTINUE; + } + + if ((size_t) length != n->raw_size) { + _LOG2D(lldp_rx, "Packet size mismatch, ignoring"); + return G_SOURCE_CONTINUE; + } + + /* Try to get the timestamp of this packet if it is known */ + if (ioctl(lldp_rx->fd, SIOCGSTAMPNS, &ts) >= 0 + && (ts_usec = nm_utils_timespec_to_usec(&ts)) < G_MAXINT64 + && (now_usec_bt = nm_utils_clock_gettime_usec(CLOCK_BOOTTIME)) >= 0 + && (now_usec_rt = nm_utils_clock_gettime_usec(CLOCK_REALTIME)) >= 0) { + gint64 t; + + now_usec = nm_utils_monotonic_timestamp_from_boottime(now_usec_bt, 1000); + ts_usec = nm_time_map_clock(ts_usec, now_usec_rt, now_usec_bt); + + t = now_usec; + if (ts_usec >= 0) { + ts_usec = nm_utils_monotonic_timestamp_from_boottime(ts_usec, 1000); + if (ts_usec > NM_UTILS_USEC_PER_SEC && ts_usec < now_usec) + t = ts_usec; + } + + n->timestamp_usec = t; + } else + n->timestamp_usec = nm_utils_get_monotonic_timestamp_usec(); + + r = nm_lldp_neighbor_parse(n); + if (r < 0) { + _LOG2D(lldp_rx, "Failure parsing invalid LLDP datagram."); + return G_SOURCE_CONTINUE; + } + + _LOG2D(lldp_rx, "Successfully processed LLDP datagram."); + lldp_rx_add_neighbor(lldp_rx, n); + + return G_SOURCE_CONTINUE; +} + +static void +lldp_rx_reset(NMLldpRX *lldp_rx) +{ + nm_clear_g_source_inst(&lldp_rx->timer_event_source); + nm_clear_g_source_inst(&lldp_rx->io_event_source); + nm_clear_fd(&lldp_rx->fd); + + lldp_rx_make_space(lldp_rx, TRUE, 0); + + nm_assert(g_hash_table_size(lldp_rx->neighbor_by_id) == 0); + nm_assert(nm_prioq_size(&lldp_rx->neighbor_by_expiry) == 0); +} + +gboolean +nm_lldp_rx_is_running(NMLldpRX *lldp_rx) +{ + if (!lldp_rx) + return FALSE; + + return lldp_rx->fd >= 0; +} + +int +nm_lldp_rx_start(NMLldpRX *lldp_rx) +{ + int r; + + g_return_val_if_fail(lldp_rx, -EINVAL); + nm_assert(lldp_rx->main_context); + nm_assert(lldp_rx->config.ifindex > 0); + + if (nm_lldp_rx_is_running(lldp_rx)) + return 0; + + nm_assert(!lldp_rx->io_event_source); + + r = nm_lldp_network_bind_raw_socket(lldp_rx->config.ifindex); + if (r < 0) { + _LOG2D(lldp_rx, "start failed to bind socket (%s)", nm_strerror_native(-r)); + return r; + } + + lldp_rx->fd = r; + + lldp_rx->io_event_source = nm_g_source_attach(nm_g_unix_fd_source_new(lldp_rx->fd, + G_IO_IN, + G_PRIORITY_DEFAULT, + lldp_rx_receive_datagram, + lldp_rx, + NULL), + lldp_rx->main_context); + + _LOG2D(lldp_rx, "started (fd %d)", lldp_rx->fd); + return 1; +} + +int +nm_lldp_rx_stop(NMLldpRX *lldp_rx) +{ + if (!nm_lldp_rx_is_running(lldp_rx)) + return 0; + + _LOG2D(lldp_rx, "stopping"); + + lldp_rx_reset(lldp_rx); + return 1; +} + +static gboolean +on_timer_event(gpointer user_data) +{ + NMLldpRX *lldp_rx = user_data; + + lldp_rx_make_space(lldp_rx, FALSE, 0); + lldp_rx_start_timer(lldp_rx, NULL); + return G_SOURCE_CONTINUE; +} + +static void +lldp_rx_start_timer(NMLldpRX *lldp_rx, NMLldpNeighbor *neighbor) +{ + NMLldpNeighbor *n; + gint64 timeout_msec; + + nm_assert_is_lldp_rx(lldp_rx); + + nm_clear_g_source_inst(&lldp_rx->timer_event_source); + + if (neighbor) + nm_lldp_neighbor_start_ttl(neighbor); + + n = nm_prioq_peek(&lldp_rx->neighbor_by_expiry); + if (!n) + return; + + timeout_msec = (n->until_usec / 1000) - nm_utils_get_monotonic_timestamp_msec(); + + lldp_rx->timer_event_source = + nm_g_source_attach(nm_g_timeout_source_new(NM_CLAMP(timeout_msec, 0, G_MAXUINT), + G_PRIORITY_DEFAULT, + on_timer_event, + lldp_rx, + NULL), + lldp_rx->main_context); +} + +static inline int +neighbor_compare_func(gconstpointer p_a, gconstpointer p_b, gpointer user_data) +{ + NMLldpNeighbor *const *a = p_a; + NMLldpNeighbor *const *b = p_b; + + nm_assert(a); + nm_assert(b); + nm_assert(*a); + nm_assert(*b); + + return nm_lldp_neighbor_id_cmp(&(*a)->id, &(*b)->id); +} + +NMLldpNeighbor ** +nm_lldp_rx_get_neighbors(NMLldpRX *lldp_rx, guint *out_len) +{ + g_return_val_if_fail(lldp_rx, NULL); + + return (NMLldpNeighbor **) + nm_utils_hash_keys_to_array(lldp_rx->neighbor_by_id, neighbor_compare_func, NULL, out_len); +} + +/*****************************************************************************/ + +NMLldpRX * +nm_lldp_rx_new(const NMLldpRXConfig *config) +{ + NMLldpRX *lldp_rx; + + nm_assert(config); + nm_assert(config->ifindex > 0); + nm_assert(config->callback); + + /* This needs to be first, see neighbor_by_id hash. */ + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMLldpNeighbor, id) == 0); + + lldp_rx = g_slice_new(NMLldpRX); + *lldp_rx = (NMLldpRX){ + .ref_count = 1, + .fd = -1, + .main_context = g_main_context_ref_thread_default(), + .config = *config, + .neighbor_by_id = g_hash_table_new((GHashFunc) nm_lldp_neighbor_id_hash, + (GEqualFunc) nm_lldp_neighbor_id_equal), + }; + lldp_rx->config.log_ifname = g_strdup(lldp_rx->config.log_ifname); + lldp_rx->config.log_uuid = g_strdup(lldp_rx->config.log_uuid); + if (lldp_rx->config.neighbors_max == 0) + lldp_rx->config.neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX; + if (!lldp_rx->config.has_capability_mask && lldp_rx->config.capability_mask == 0) + lldp_rx->config.capability_mask = UINT16_MAX; + + nm_prioq_init(&lldp_rx->neighbor_by_expiry, (GCompareFunc) nm_lldp_neighbor_prioq_compare_func); + + return lldp_rx; +} + +NMLldpRX * +nm_lldp_rx_ref(NMLldpRX *lldp_rx) +{ + if (!lldp_rx) + return NULL; + + nm_assert_is_lldp_rx(lldp_rx); + nm_assert(lldp_rx->ref_count < G_MAXINT); + + lldp_rx->ref_count++; + return lldp_rx; +} + +void +nm_lldp_rx_unref(NMLldpRX *lldp_rx) +{ + if (!lldp_rx) + return; + + nm_assert_is_lldp_rx(lldp_rx); + + if (--lldp_rx->ref_count > 0) + return; + + lldp_rx_reset(lldp_rx); + + g_hash_table_unref(lldp_rx->neighbor_by_id); + nm_prioq_destroy(&lldp_rx->neighbor_by_expiry); + + free((char *) lldp_rx->config.log_ifname); + free((char *) lldp_rx->config.log_uuid); + + g_main_context_unref(lldp_rx->main_context); + + nm_g_slice_free(lldp_rx); +} diff --git a/src/libnm-lldp/nm-lldp-rx.h b/src/libnm-lldp/nm-lldp-rx.h new file mode 100644 index 00000000..a3f38053 --- /dev/null +++ b/src/libnm-lldp/nm-lldp-rx.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef __NM_LLDP_RX_H__ +#define __NM_LLDP_RX_H__ + +#include "nm-lldp.h" + +typedef struct _NMLldpRX NMLldpRX; +typedef struct _NMLldpNeighbor NMLldpNeighbor; + +typedef enum { + NM_LLDP_RX_EVENT_ADDED, + NM_LLDP_RX_EVENT_REMOVED, + NM_LLDP_RX_EVENT_UPDATED, + NM_LLDP_RX_EVENT_REFRESHED, + _NM_LLDP_RX_EVENT_MAX, + _NM_LLDP_RX_EVENT_INVALID = -EINVAL, +} NMLldpRXEvent; + +const char *nm_lldp_rx_event_to_string(NMLldpRXEvent e) _nm_pure; + +typedef struct { + /* The spec calls this an "MSAP identifier" */ + const void *chassis_id; + size_t chassis_id_size; + + const void *port_id; + size_t port_id_size; +} NMLldpNeighborID; + +guint nm_lldp_neighbor_id_hash(const NMLldpNeighborID *id); +int nm_lldp_neighbor_id_cmp(const NMLldpNeighborID *x, const NMLldpNeighborID *y); +gboolean nm_lldp_neighbor_id_equal(const NMLldpNeighborID *x, const NMLldpNeighborID *y); + +typedef void (*NMLldpRXCallback)(NMLldpRX *lldp_rx, + NMLldpRXEvent event, + NMLldpNeighbor *n, + void *userdata); + +typedef struct { + int ifindex; + guint neighbors_max; + const char *log_ifname; + const char *log_uuid; + NMLldpRXCallback callback; + void *userdata; + + /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so + * that our own can be filtered out here. */ + NMEtherAddr filter_address; + + uint16_t capability_mask; + bool has_capability_mask : 1; +} NMLldpRXConfig; + +NMLldpRX *nm_lldp_rx_new(const NMLldpRXConfig *config); +NMLldpRX *nm_lldp_rx_ref(NMLldpRX *lldp_rx); +void nm_lldp_rx_unref(NMLldpRX *lldp_rx); + +NM_AUTO_DEFINE_FCN(NMLldpRX *, nm_lldp_rx_unrefp, nm_lldp_rx_unref); + +int nm_lldp_rx_start(NMLldpRX *lldp_rx); +int nm_lldp_rx_stop(NMLldpRX *lldp_rx); +gboolean nm_lldp_rx_is_running(NMLldpRX *lldp_rx); + +/* Controls how much and what to store in the neighbors database */ + +NMLldpNeighbor **nm_lldp_rx_get_neighbors(NMLldpRX *lldp_rx, guint *out_len); + +/*****************************************************************************/ + +NMLldpNeighbor *nm_lldp_neighbor_new_from_raw(const void *raw, size_t raw_size); + +NMLldpNeighbor *nm_lldp_neighbor_ref(NMLldpNeighbor *n); +NMLldpNeighbor *nm_lldp_neighbor_unref(NMLldpNeighbor *n); + +NM_AUTO_DEFINE_FCN(NMLldpNeighbor *, nm_lldp_neighbor_unrefp, nm_lldp_neighbor_unref); + +int nm_lldp_neighbor_cmp(const NMLldpNeighbor *a, const NMLldpNeighbor *b); + +static inline gboolean +nm_lldp_neighbor_equal(const NMLldpNeighbor *a, const NMLldpNeighbor *b) +{ + return nm_lldp_neighbor_cmp(a, b) == 0; +} + +/*****************************************************************************/ + +static inline const NMLldpNeighborID * +nm_lldp_neighbor_get_id(NMLldpNeighbor *lldp_neigbor) +{ + return (const NMLldpNeighborID *) ((gconstpointer) lldp_neigbor); +} + +/* Access to LLDP frame metadata */ +int nm_lldp_neighbor_get_source_address(NMLldpNeighbor *n, NMEtherAddr *address); +int nm_lldp_neighbor_get_destination_address(NMLldpNeighbor *n, NMEtherAddr *address); +int nm_lldp_neighbor_get_timestamp_usec(NMLldpNeighbor *n, gint64 *out_usec); +int nm_lldp_neighbor_get_raw(NMLldpNeighbor *n, const void **ret, size_t *size); + +/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */ +int +nm_lldp_neighbor_get_chassis_id(NMLldpNeighbor *n, uint8_t *type, const void **ret, size_t *size); +const char *nm_lldp_neighbor_get_chassis_id_as_string(NMLldpNeighbor *n); +int nm_lldp_neighbor_get_port_id(NMLldpNeighbor *n, uint8_t *type, const void **ret, size_t *size); +const char *nm_lldp_neighbor_get_port_id_as_string(NMLldpNeighbor *n); +int nm_lldp_neighbor_get_ttl(NMLldpNeighbor *n, uint16_t *ret_sec); +int nm_lldp_neighbor_get_system_name(NMLldpNeighbor *n, const char **ret); +int nm_lldp_neighbor_get_system_description(NMLldpNeighbor *n, const char **ret); +int nm_lldp_neighbor_get_port_description(NMLldpNeighbor *n, const char **ret); +int nm_lldp_neighbor_get_mud_url(NMLldpNeighbor *n, const char **ret); +int nm_lldp_neighbor_get_system_capabilities(NMLldpNeighbor *n, uint16_t *ret); +int nm_lldp_neighbor_get_enabled_capabilities(NMLldpNeighbor *n, uint16_t *ret); + +/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs + * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */ +int nm_lldp_neighbor_tlv_rewind(NMLldpNeighbor *n); +int nm_lldp_neighbor_tlv_next(NMLldpNeighbor *n); +int nm_lldp_neighbor_tlv_get_type(NMLldpNeighbor *n, uint8_t *type); +int nm_lldp_neighbor_tlv_is_type(NMLldpNeighbor *n, uint8_t type); +int nm_lldp_neighbor_tlv_get_oui(NMLldpNeighbor *n, uint8_t oui[static 3], uint8_t *subtype); +int nm_lldp_neighbor_tlv_is_oui(NMLldpNeighbor *n, const uint8_t oui[static 3], uint8_t subtype); +int nm_lldp_neighbor_tlv_get_raw(NMLldpNeighbor *n, const void **ret, size_t *size); + +#endif /* __NM_LLDP_RX_H__ */ diff --git a/src/libnm-lldp/nm-lldp.h b/src/libnm-lldp/nm-lldp.h new file mode 100644 index 00000000..55e7de29 --- /dev/null +++ b/src/libnm-lldp/nm-lldp.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef __NM_LLDP_H__ +#define __NM_LLDP_H__ + +/* IEEE 802.1AB-2009 Clause 8: TLV Types */ +enum { + NM_LLDP_TYPE_END = 0, + NM_LLDP_TYPE_CHASSIS_ID = 1, + NM_LLDP_TYPE_PORT_ID = 2, + NM_LLDP_TYPE_TTL = 3, + NM_LLDP_TYPE_PORT_DESCRIPTION = 4, + NM_LLDP_TYPE_SYSTEM_NAME = 5, + NM_LLDP_TYPE_SYSTEM_DESCRIPTION = 6, + NM_LLDP_TYPE_SYSTEM_CAPABILITIES = 7, + NM_LLDP_TYPE_MGMT_ADDRESS = 8, + NM_LLDP_TYPE_PRIVATE = 127 +}; + +/* IEEE 802.1AB-2009 Clause 8.5.2: Chassis subtypes */ +enum { + NM_LLDP_CHASSIS_SUBTYPE_RESERVED = 0, + NM_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1, + NM_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS = 2, + NM_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT = 3, + NM_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS = 4, + NM_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS = 5, + NM_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME = 6, + NM_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7 +}; + +/* IEEE 802.1AB-2009 Clause 8.5.3: Port subtype */ +enum { + NM_LLDP_PORT_SUBTYPE_RESERVED = 0, + NM_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1, + NM_LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2, + NM_LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3, + NM_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4, + NM_LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5, + NM_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6, + NM_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7 +}; + +/* IEEE 802.1AB-2009 Clause 8.5.8: System capabilities */ +enum { + NM_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0, + NM_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1, + NM_LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2, + NM_LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3, + NM_LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4, + NM_LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5, + NM_LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6, + NM_LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7, + NM_LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8, + NM_LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9, + NM_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10 +}; + +#define NM_LLDP_SYSTEM_CAPABILITIES_ALL UINT16_MAX + +#define NM_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \ + ((uint16_t) (NM_LLDP_SYSTEM_CAPABILITIES_REPEATER | NM_LLDP_SYSTEM_CAPABILITIES_BRIDGE \ + | NM_LLDP_SYSTEM_CAPABILITIES_WLAN_AP | NM_LLDP_SYSTEM_CAPABILITIES_ROUTER \ + | NM_LLDP_SYSTEM_CAPABILITIES_DOCSIS | NM_LLDP_SYSTEM_CAPABILITIES_CVLAN \ + | NM_LLDP_SYSTEM_CAPABILITIES_SVLAN | NM_LLDP_SYSTEM_CAPABILITIES_TPMR)) + +#define NM_LLDP_OUI_802_1 \ + (const uint8_t[]) \ + { \ + 0x00, 0x80, 0xc2 \ + } +#define NM_LLDP_OUI_802_3 \ + (const uint8_t[]) \ + { \ + 0x00, 0x12, 0x0f \ + } + +#define _SD_LLDP_OUI_IANA 0x00, 0x00, 0x5E +#define NM_LLDP_OUI_IANA \ + (const uint8_t[]) \ + { \ + _SD_LLDP_OUI_IANA \ + } + +#define NM_LLDP_OUI_IANA_SUBTYPE_MUD 0x01 +#define NM_LLDP_OUI_IANA_MUD \ + (const uint8_t[]) \ + { \ + _SD_LLDP_OUI_IANA, NM_LLDP_OUI_IANA_SUBTYPE_MUD \ + } + +/* IEEE 802.1AB-2009 Annex E */ +enum { + NM_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1, + NM_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2, + NM_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3, + NM_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4, + NM_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5, + NM_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6, + NM_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7 +}; + +/* IEEE 802.1AB-2009 Annex F */ +enum { + NM_LLDP_OUI_802_3_SUBTYPE_MAC_PHY_CONFIG_STATUS = 1, + NM_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI = 2, + NM_LLDP_OUI_802_3_SUBTYPE_LINK_AGGREGATION = 3, + NM_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE = 4 +}; + +#endif /* __NM_LLDP_H__ */ diff --git a/src/libnm-platform/meson.build b/src/libnm-platform/meson.build index e273046f..696ca1a6 100644 --- a/src/libnm-platform/meson.build +++ b/src/libnm-platform/meson.build @@ -7,9 +7,11 @@ libnm_platform = static_library( 'nm-netlink.c', 'nm-platform-utils.c', 'nm-platform.c', + 'nmp-base.c', 'nmp-global-tracker.c', 'nmp-netns.c', 'nmp-object.c', + 'nmp-plobj.c', 'wifi/nm-wifi-utils-nl80211.c', 'wifi/nm-wifi-utils.c', 'wpan/nm-wpan-utils.c', diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index b798d12d..d4ab36f9 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -222,6 +222,16 @@ G_STATIC_ASSERT(RTA_MAX == (__RTA_MAX - 1)); /*****************************************************************************/ +#define IFLA_VTI_UNSPEC 0 +#define IFLA_VTI_LINK 1 +#define IFLA_VTI_IKEY 2 +#define IFLA_VTI_OKEY 3 +#define IFLA_VTI_LOCAL 4 +#define IFLA_VTI_REMOTE 5 +#define IFLA_VTI_FWMARK 6 + +/*****************************************************************************/ + #define WG_CMD_GET_DEVICE 0 #define WG_CMD_SET_DEVICE 1 @@ -580,25 +590,29 @@ NM_LINUX_PLATFORM_FROM_PRIVATE(NMLinuxPlatformPrivate *priv) #define _NMLOG2(level, ...) _LOG(level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__) #define _NMLOG2_err(errsv, level, ...) _LOG_err(errsv, level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__) -#define _LOG_print(__level, __domain, __errsv, self, ...) \ - G_STMT_START \ - { \ - char __prefix[32]; \ - const char *__p_prefix = _NMLOG_PREFIX_NAME; \ - NMPlatform *const __self = (self); \ - \ - if (__self && nm_platform_get_log_with_ptr(__self)) { \ - g_snprintf(__prefix, sizeof(__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ - __p_prefix = __prefix; \ - } \ - _nm_log(__level, \ - __domain, \ - __errsv, \ - NULL, \ - NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - __p_prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ +#define _LOG_print(__level, __domain, __errsv, self, ...) \ + G_STMT_START \ + { \ + char __prefix[64]; \ + const char *__p_prefix = _NMLOG_PREFIX_NAME; \ + NMPlatform *const __self = (self); \ + \ + if (__self && nm_platform_get_log_with_ptr(__self)) { \ + g_snprintf(__prefix, \ + sizeof(__prefix), \ + "%s[" NM_HASH_OBFUSCATE_PTR_FMT "]", \ + _NMLOG_PREFIX_NAME, \ + NM_HASH_OBFUSCATE_PTR(__self)); \ + __p_prefix = __prefix; \ + } \ + _nm_log(__level, \ + __domain, \ + __errsv, \ + NULL, \ + NULL, \ + "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + __p_prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ G_STMT_END #define _LOG(level, domain, self, ...) \ @@ -811,6 +825,8 @@ static const LinkDesc link_descs[] = { [NM_LINK_TYPE_VETH] = {"veth", "veth", NULL}, [NM_LINK_TYPE_VLAN] = {"vlan", "vlan", "vlan"}, [NM_LINK_TYPE_VRF] = {"vrf", "vrf", "vrf"}, + [NM_LINK_TYPE_VTI] = {"vti", "vti", NULL}, + [NM_LINK_TYPE_VTI6] = {"vti6", "vti6", NULL}, [NM_LINK_TYPE_VXLAN] = {"vxlan", "vxlan", "vxlan"}, [NM_LINK_TYPE_WIREGUARD] = {"wireguard", "wireguard", "wireguard"}, @@ -853,6 +869,8 @@ _link_type_from_rtnl_type(const char *name) NM_LINK_TYPE_VETH, /* "veth" */ NM_LINK_TYPE_VLAN, /* "vlan" */ NM_LINK_TYPE_VRF, /* "vrf" */ + NM_LINK_TYPE_VTI, /* "vti" */ + NM_LINK_TYPE_VTI6, /* "vti6" */ NM_LINK_TYPE_VXLAN, /* "vxlan" */ NM_LINK_TYPE_WIMAX, /* "wimax" */ NM_LINK_TYPE_WIREGUARD, /* "wireguard" */ @@ -1158,7 +1176,7 @@ _linktype_read_devtype(int dirfd) end = strpbrk(cont, "\r\n"); if (end) *end++ = '\0'; - if (strncmp(cont, DEVTYPE_PREFIX, NM_STRLEN(DEVTYPE_PREFIX)) == 0) { + if (NM_STR_HAS_PREFIX(cont, DEVTYPE_PREFIX)) { cont += NM_STRLEN(DEVTYPE_PREFIX); memmove(contents, cont, strlen(cont) + 1); return g_steal_pointer(&contents); @@ -1289,14 +1307,36 @@ _linktype_get_type(NMPlatform *platform, } if (arptype == ARPHRD_ETHER) { - /* Misc non-upstream WWAN drivers. rmnet is Qualcomm's proprietary - * modem interface, ccmni is MediaTek's. FIXME: these drivers should - * really set devtype=WWAN. - */ - if (g_str_has_prefix(ifname, "rmnet") || g_str_has_prefix(ifname, "rev_rmnet") - || g_str_has_prefix(ifname, "ccmni")) + /* The USB gadget interfaces behave and look like ordinary ethernet devices + * aside from the DEVTYPE. */ + if (nm_streq0(devtype, "gadget")) + return NM_LINK_TYPE_ETHERNET; + + /* Distributed Switch Architecture switch chips */ + if (nm_streq0(devtype, "dsa")) + return NM_LINK_TYPE_ETHERNET; + } + + /* Misc non-upstream WWAN drivers. rmnet is Qualcomm's proprietary + * modem interface, ccmni is MediaTek's. FIXME: these drivers should + * really set devtype=WWAN. + * + * Value "530" is the out-of-tree version of ARPHRD_RAWIP before it's + * merged in Linux 4.14. For the mainline version, this has the value + * of "519". + * + * [1] https://github.com/LineageOS/android_kernel_google_msm-4.9/commit/54948008c293fdf48552a5c39c91c09c3eb76ed2 + */ + if (NM_IN_SET(arptype, + ARPHRD_ETHER, + 519 /* ARPHRD_RAWIP */, + 530 /* out-of-tree ARPHRD_RAWIP */)) { + if (NM_STR_HAS_PREFIX(ifname, "rmnet") || NM_STR_HAS_PREFIX(ifname, "rev_rmnet") + || NM_STR_HAS_PREFIX(ifname, "ccmni")) return NM_LINK_TYPE_WWAN_NET; + } + if (arptype == ARPHRD_ETHER) { /* Standard wired ethernet interfaces don't report an rtnl_link_type, so * only allow fallback to Ethernet if no type is given. This should * prevent future virtual network drivers from being treated as Ethernet @@ -1304,15 +1344,6 @@ _linktype_get_type(NMPlatform *platform, */ if (!kind && !devtype) return NM_LINK_TYPE_ETHERNET; - - /* The USB gadget interfaces behave and look like ordinary ethernet devices - * aside from the DEVTYPE. */ - if (nm_streq0(devtype, "gadget")) - return NM_LINK_TYPE_ETHERNET; - - /* Distributed Switch Architecture switch chips */ - if (nm_streq0(devtype, "dsa")) - return NM_LINK_TYPE_ETHERNET; } } @@ -1338,7 +1369,8 @@ typedef struct { }; } ParseNlmsgIter; -#define NLMSG_TAIL(nmsg) ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) +#define NLMSG_TAIL(nmsg) \ + NM_CAST_ALIGN(struct rtattr, ((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)) /* copied from iproute2's addattr_l(). */ static gboolean @@ -2244,6 +2276,11 @@ _parse_lnk_vlan(const char *kind, struct nlattr *info_data) obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VLAN, NULL); obj->lnk_vlan.id = nla_get_u16(tb[IFLA_VLAN_ID]); + if (tb[IFLA_VLAN_PROTOCOL]) + obj->lnk_vlan.protocol = ntohs(nla_get_u16(tb[IFLA_VLAN_PROTOCOL])); + else + obj->lnk_vlan.protocol = ETH_P_8021Q; + if (tb[IFLA_VLAN_FLAGS]) { struct ifla_vlan_flags flags; @@ -2388,6 +2425,76 @@ _parse_lnk_vxlan(const char *kind, struct nlattr *info_data) return obj; } +static NMPObject * +_parse_lnk_vti(const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[] = { + [IFLA_VTI_LINK] = {.type = NLA_U32}, + [IFLA_VTI_LOCAL] = {.type = NLA_U32}, + [IFLA_VTI_REMOTE] = {.type = NLA_U32}, + [IFLA_VTI_IKEY] = {.type = NLA_U32}, + [IFLA_VTI_OKEY] = {.type = NLA_U32}, + [IFLA_VTI_FWMARK] = {.type = NLA_U32}, + }; + struct nlattr *tb[G_N_ELEMENTS(policy)]; + NMPObject *obj; + NMPlatformLnkVti *props; + + if (!info_data || !nm_streq0(kind, "vti")) + return NULL; + + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; + + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VTI, NULL); + props = &obj->lnk_vti; + + props->parent_ifindex = tb[IFLA_VTI_LINK] ? nla_get_u32(tb[IFLA_VTI_LINK]) : 0; + props->local = tb[IFLA_VTI_LOCAL] ? nla_get_u32(tb[IFLA_VTI_LOCAL]) : 0; + props->remote = tb[IFLA_VTI_REMOTE] ? nla_get_u32(tb[IFLA_VTI_REMOTE]) : 0; + props->ikey = tb[IFLA_VTI_IKEY] ? ntohl(nla_get_u32(tb[IFLA_VTI_IKEY])) : 0; + props->okey = tb[IFLA_VTI_OKEY] ? ntohl(nla_get_u32(tb[IFLA_VTI_OKEY])) : 0; + props->fwmark = tb[IFLA_VTI_FWMARK] ? nla_get_u32(tb[IFLA_VTI_FWMARK]) : 0; + + return obj; +} + +static NMPObject * +_parse_lnk_vti6(const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[] = { + [IFLA_VTI_LINK] = {.type = NLA_U32}, + [IFLA_VTI_LOCAL] = {.minlen = sizeof(struct in6_addr)}, + [IFLA_VTI_REMOTE] = {.minlen = sizeof(struct in6_addr)}, + [IFLA_VTI_IKEY] = {.type = NLA_U32}, + [IFLA_VTI_OKEY] = {.type = NLA_U32}, + [IFLA_VTI_FWMARK] = {.type = NLA_U32}, + }; + struct nlattr *tb[G_N_ELEMENTS(policy)]; + NMPObject *obj; + NMPlatformLnkVti6 *props; + + if (!info_data || !nm_streq0(kind, "vti6")) + return NULL; + + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; + + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VTI6, NULL); + props = &obj->lnk_vti6; + + props->parent_ifindex = tb[IFLA_VTI_LINK] ? nla_get_u32(tb[IFLA_VTI_LINK]) : 0; + if (tb[IFLA_VTI_LOCAL]) + props->local = *nla_data_as(struct in6_addr, tb[IFLA_VTI_LOCAL]); + if (tb[IFLA_VTI_REMOTE]) + props->remote = *nla_data_as(struct in6_addr, tb[IFLA_VTI_REMOTE]); + props->ikey = tb[IFLA_VTI_IKEY] ? ntohl(nla_get_u32(tb[IFLA_VTI_IKEY])) : 0; + props->okey = tb[IFLA_VTI_OKEY] ? ntohl(nla_get_u32(tb[IFLA_VTI_OKEY])) : 0; + props->fwmark = tb[IFLA_VTI_FWMARK] ? nla_get_u32(tb[IFLA_VTI_FWMARK]) : 0; + + return obj; +} + static NMPObject * _parse_lnk_vrf(const char *kind, struct nlattr *info_data) { @@ -2540,16 +2647,15 @@ _wireguard_update_from_peers_nla(CList *peers, GArray **p_allowed_ips, struct nl GArray *allowed_ips = *p_allowed_ips; nla_for_each_nested (attr, tb[WGPEER_A_ALLOWEDIPS], rem) { + NMPWireGuardAllowedIP *new; + if (!allowed_ips) { allowed_ips = g_array_new(FALSE, FALSE, sizeof(NMPWireGuardAllowedIP)); *p_allowed_ips = allowed_ips; - g_array_set_size(allowed_ips, 1); - } else - g_array_set_size(allowed_ips, allowed_ips->len + 1); + } - if (!_wireguard_update_from_allowed_ips_nla( - &g_array_index(allowed_ips, NMPWireGuardAllowedIP, allowed_ips->len - 1), - attr)) { + new = nm_g_array_append_new(allowed_ips, NMPWireGuardAllowedIP); + if (!_wireguard_update_from_allowed_ips_nla(new, attr)) { /* we ignore the error of parsing one allowed-ip. */ g_array_set_size(allowed_ips, allowed_ips->len - 1); continue; @@ -3379,6 +3485,12 @@ _new_from_nl_link(NMPlatform *platform, case NM_LINK_TYPE_VRF: lnk_data = _parse_lnk_vrf(nl_info_kind, nl_info_data); break; + case NM_LINK_TYPE_VTI: + lnk_data = _parse_lnk_vti(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_VTI6: + lnk_data = _parse_lnk_vti6(nl_info_kind, nl_info_data); + break; case NM_LINK_TYPE_VXLAN: lnk_data = _parse_lnk_vxlan(nl_info_kind, nl_info_data); break; @@ -3631,21 +3743,27 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter struct { gboolean found; gboolean has_more; + guint8 weight; int ifindex; NMIPAddr gateway; } nh = { .found = FALSE, .has_more = FALSE, }; - guint32 mss; - guint32 window = 0; - guint32 cwnd = 0; - guint32 initcwnd = 0; - guint32 initrwnd = 0; - guint32 mtu = 0; - guint32 rto_min = 0; - guint32 lock = 0; - gboolean quickack = FALSE; + guint v4_n_nexthops = 0; + NMPlatformIP4RtNextHop v4_nh_extra_nexthops_stack[10]; + gs_free NMPlatformIP4RtNextHop *v4_nh_extra_nexthops_heap = NULL; + NMPlatformIP4RtNextHop *v4_nh_extra_nexthops = v4_nh_extra_nexthops_stack; + guint v4_nh_extra_alloc = G_N_ELEMENTS(v4_nh_extra_nexthops_stack); + guint32 mss; + guint32 window = 0; + guint32 cwnd = 0; + guint32 initcwnd = 0; + guint32 initrwnd = 0; + guint32 mtu = 0; + guint32 rto_min = 0; + guint32 lock = 0; + gboolean quickack = FALSE; nm_assert((parse_nlmsg_iter->iter_more && parse_nlmsg_iter->ip6_route.next_multihop > 0) || (!parse_nlmsg_iter->iter_more && parse_nlmsg_iter->ip6_route.next_multihop == 0)); @@ -3669,15 +3787,6 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter else return NULL; - if (!NM_IN_SET(rtm->rtm_type, - RTN_UNICAST, - RTN_LOCAL, - RTN_BLACKHOLE, - RTN_UNREACHABLE, - RTN_PROHIBIT, - RTN_THROW)) - return NULL; - if (nlmsg_parse_arr(nlh, sizeof(struct rtmsg), tb, policy) < 0) return NULL; @@ -3703,9 +3812,57 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter idx = 0; while (TRUE) { - if (idx == multihop_idx) { + if (nh.found && IS_IPv4) { + NMPlatformIP4RtNextHop *new_nexthop; + + /* we parsed the first IPv4 nexthop in "nh", let's parse the following ones. + * + * At this point, v4_n_nexthops still counts how many hops we already added, + * now we are about to add the (v4_n_nexthops+1) hop. + * + * Note that the first hop (of then v4_n_nexthops) is tracked in "nh". + * v4_nh_extra_nexthops tracks the additional hops. + * + * v4_nh_extra_alloc is how many space is allocated for + * v4_nh_extra_nexthops (note that in the end we will only add (v4_n_nexthops-1) + * hops in this list). */ + nm_assert(v4_n_nexthops > 0u); + if (v4_n_nexthops - 1u >= v4_nh_extra_alloc) { + v4_nh_extra_alloc = NM_MAX(4, v4_nh_extra_alloc * 2u); + if (!v4_nh_extra_nexthops_heap) { + v4_nh_extra_nexthops_heap = + g_new(NMPlatformIP4RtNextHop, v4_nh_extra_alloc); + memcpy(v4_nh_extra_nexthops_heap, + v4_nh_extra_nexthops_stack, + G_N_ELEMENTS(v4_nh_extra_nexthops_stack)); + } else { + v4_nh_extra_nexthops_heap = g_renew(NMPlatformIP4RtNextHop, + v4_nh_extra_nexthops_heap, + v4_nh_extra_alloc); + } + v4_nh_extra_nexthops = v4_nh_extra_nexthops_heap; + } + nm_assert(v4_n_nexthops - 1u < v4_nh_extra_alloc); + new_nexthop = &v4_nh_extra_nexthops[v4_n_nexthops - 1u]; + new_nexthop->ifindex = rtnh->rtnh_ifindex; + new_nexthop->weight = NM_MAX(((guint) rtnh->rtnh_hops) + 1u, 1u); + if (rtnh->rtnh_len > sizeof(*rtnh)) { + struct nlattr *ntb[RTA_MAX + 1]; + + if (nla_parse_arr(ntb, + (struct nlattr *) RTNH_DATA(rtnh), + rtnh->rtnh_len - sizeof(*rtnh), + NULL) + < 0) + return NULL; + + if (_check_addr_or_return_null(ntb, RTA_GATEWAY, addr_len)) + memcpy(&new_nexthop->gateway, nla_data(ntb[RTA_GATEWAY]), addr_len); + } + } else if (IS_IPv4 || idx == multihop_idx) { nh.found = TRUE; nh.ifindex = rtnh->rtnh_ifindex; + nh.weight = NM_MAX(((guint) rtnh->rtnh_hops) + 1u, 1u); if (rtnh->rtnh_len > sizeof(*rtnh)) { struct nlattr *ntb[RTA_MAX + 1]; @@ -3722,15 +3879,6 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter } else if (nh.found) { /* we just parsed a nexthop, but there is yet another hop afterwards. */ nm_assert(idx == multihop_idx + 1); - if (IS_IPv4) { - /* for IPv4, multihop routes are currently not supported. - * - * If we ever support them, then the next-hop list is part of the NMPlatformIPRoute, - * that is, for IPv4 we truly have multihop routes. Unlike for IPv6. - * - * For now, just error out. */ - return NULL; - } /* For IPv6 multihop routes, we need to remember to iterate again. * For each next-hop, we will create a distinct single-hop NMPlatformIP6Route. */ @@ -3738,6 +3886,9 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter break; } + if (IS_IPv4) + v4_n_nexthops++; + if (tlen < RTNH_ALIGN(rtnh->rtnh_len) + sizeof(*rtnh)) break; @@ -3771,6 +3922,9 @@ rta_multipath_done: nh.ifindex = ifindex; nh.gateway = gateway; nh.found = TRUE; + nm_assert(v4_n_nexthops == 0); + if (IS_IPv4) + v4_n_nexthops = 1; } else { /* Kernel supports new style nexthop configuration, * verify that it is a duplicate and ignore old-style nexthop. */ @@ -3861,6 +4015,23 @@ rta_multipath_done: obj->ip_route.ifindex = nh.ifindex; + if (IS_IPv4) { + nm_assert((!!nh.found) == (v4_n_nexthops > 0u)); + obj->ip4_route.n_nexthops = v4_n_nexthops; + if (v4_n_nexthops > 1) { + /* We only set the weight for multihop routes. I think that corresponds to what kernel + * does. The weight is mostly undefined for single-hop. */ + obj->ip4_route.weight = NM_MAX(nh.weight, 1u); + + obj->_ip4_route.extra_nexthops = + (v4_nh_extra_alloc == v4_n_nexthops - 1u + && v4_nh_extra_nexthops == v4_nh_extra_nexthops_heap) + ? g_steal_pointer(&v4_nh_extra_nexthops_heap) + : nm_memdup(v4_nh_extra_nexthops, + sizeof(v4_nh_extra_nexthops[0]) * (v4_n_nexthops - 1u)); + } + } + if (_check_addr_or_return_null(tb, RTA_DST, addr_len)) memcpy(obj->ip_route.network_ptr, nla_data(tb[RTA_DST]), addr_len); @@ -4580,11 +4751,13 @@ _nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpo const NMPlatformLnkVlan *props = extra_data; nm_assert(extra_data); + nm_assert(props->protocol != 0); if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) goto nla_put_failure; NLA_PUT_U16(msg, IFLA_VLAN_ID, props->id); + NLA_PUT_U16(msg, IFLA_VLAN_PROTOCOL, htons(props->protocol)); { struct ifla_vlan_flags flags = { @@ -4835,6 +5008,44 @@ _nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpo NLA_PUT_U16(msg, IFLA_MACVLAN_FLAGS, props->no_promisc ? MACVLAN_FLAG_NOPROMISC : 0); break; } + case NM_LINK_TYPE_VTI: + { + const NMPlatformLnkVti *props = extra_data; + + nm_assert(props); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (props->parent_ifindex > 0) + NLA_PUT_U32(msg, IFLA_VTI_LINK, props->parent_ifindex); + NLA_PUT_U32(msg, IFLA_VTI_LOCAL, props->local); + NLA_PUT_U32(msg, IFLA_VTI_REMOTE, props->remote); + NLA_PUT_U32(msg, IFLA_VTI_IKEY, htonl(props->ikey)); + NLA_PUT_U32(msg, IFLA_VTI_OKEY, htonl(props->okey)); + NLA_PUT_U32(msg, IFLA_VTI_FWMARK, props->fwmark); + break; + } + case NM_LINK_TYPE_VTI6: + { + const NMPlatformLnkVti6 *props = extra_data; + + nm_assert(props); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (props->parent_ifindex > 0) + NLA_PUT_U32(msg, IFLA_VTI_LINK, props->parent_ifindex); + if (!IN6_IS_ADDR_UNSPECIFIED(&props->local)) + NLA_PUT(msg, IFLA_VTI_LOCAL, sizeof(props->local), &props->local); + if (!IN6_IS_ADDR_UNSPECIFIED(&props->remote)) + NLA_PUT(msg, IFLA_VTI_REMOTE, sizeof(props->remote), &props->remote); + NLA_PUT_U32(msg, IFLA_VTI_IKEY, htonl(props->ikey)); + NLA_PUT_U32(msg, IFLA_VTI_OKEY, htonl(props->okey)); + NLA_PUT_U32(msg, IFLA_VTI_FWMARK, props->fwmark); + break; + } default: nm_assert(!extra_data); break; @@ -5088,7 +5299,7 @@ ip_route_get_lock_flag(const NMPlatformIPRoute *route) } static gboolean -ip_route_ignored_protocol(const NMPlatformIPRoute *route) +ip_route_is_alive(const NMPlatformIPRoute *route) { guint8 prot; @@ -5100,12 +5311,29 @@ ip_route_ignored_protocol(const NMPlatformIPRoute *route) nm_assert(nmp_utils_ip_config_source_from_rtprot(prot) == route->rt_source); - /* We ignore all routes outside a certain subest of rtm_protocol. NetworkManager - * itself wouldn't configure those, so they are always configured by somebody - * external. We thus ignore them to avoid the overhead that processing them brings. - * For example, the BGP daemon "bird" might configure a huge number of RTPROT_BIRD routes. */ + if (prot > RTPROT_STATIC && !NM_IN_SET(prot, RTPROT_DHCP, RTPROT_RA)) { + /* We ignore certain rtm_protocol, because NetworkManager would only ever + * configure certain protocols. Other routes are not configured by NetworkManager + * and we don't track them in the platform cache. + * + * This is to help with the performance overhead of a huge number of + * routes, for example with the bird BGP software, that adds routes + * with RTPROT_BIRD protocol. */ + return FALSE; + } - return prot > RTPROT_STATIC && !NM_IN_SET(prot, RTPROT_DHCP, RTPROT_RA); + if (!NM_IN_SET(nm_platform_route_type_uncoerce(route->type_coerced), + RTN_UNICAST, + RTN_LOCAL, + RTN_BLACKHOLE, + RTN_UNREACHABLE, + RTN_PROHIBIT, + RTN_THROW)) { + /* Certain route types are ignored and not placed into the cache. */ + return FALSE; + } + + return TRUE; } /* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */ @@ -5118,7 +5346,7 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob const guint32 lock = ip_route_get_lock_flag(NMP_OBJECT_CAST_IP_ROUTE(obj)); const guint32 table = nm_platform_route_table_uncoerce(NMP_OBJECT_CAST_IP_ROUTE(obj)->table_coerced, TRUE); - const struct rtmsg rtmsg = { + struct rtmsg rtmsg = { .rtm_family = klass->addr_family, .rtm_tos = IS_IPv4 ? obj->ip4_route.tos : 0, .rtm_table = table <= 0xFF ? table : RT_TABLE_UNSPEC, @@ -5126,7 +5354,7 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob .rtm_scope = IS_IPv4 ? nm_platform_route_scope_inv(obj->ip4_route.scope_inv) : RT_SCOPE_NOWHERE, .rtm_type = nm_platform_route_type_uncoerce(NMP_OBJECT_CAST_IP_ROUTE(obj)->type_coerced), - .rtm_flags = obj->ip_route.r_rtm_flags & ((unsigned) (RTNH_F_ONLINK)), + .rtm_flags = 0, .rtm_dst_len = obj->ip_route.plen, .rtm_src_len = IS_IPv4 ? 0 : NMP_OBJECT_CAST_IP6_ROUTE(obj)->src_plen, }; @@ -5137,6 +5365,17 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); nm_assert(NM_IN_SET(nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE)); + if (NM_FLAGS_HAS(obj->ip_route.r_rtm_flags, ((unsigned) (RTNH_F_ONLINK)))) { + if (IS_IPv4 && obj->ip4_route.gateway == 0) { + /* Kernel does not allow setting the onlink flag, if there is no gateway. + * We silently don't configure the flag. + * + * For multi-hop routes, we will set the flag for each next-hop (which + * has a gateway set). */ + } else + rtmsg.rtm_flags |= ((unsigned) RTNH_F_ONLINK); + } + msg = nlmsg_alloc_new(0, nlmsg_type, nlmsg_flags); if (nlmsg_append_struct(msg, &rtmsg) < 0) @@ -5171,6 +5410,54 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob NLA_PUT(msg, RTA_PREFSRC, addr_len, &obj->ip6_route.pref_src); } + if (IS_IPv4 && obj->ip4_route.n_nexthops > 1u) { + struct nlattr *multipath; + guint i; + + if (!(multipath = nla_nest_start(msg, RTA_MULTIPATH))) + goto nla_put_failure; + + for (i = 0u; i < obj->ip4_route.n_nexthops; i++) { + struct rtnexthop *rtnh; + in_addr_t gw; + + rtnh = nlmsg_reserve(msg, sizeof(*rtnh), NLMSG_ALIGNTO); + if (!rtnh) + goto nla_put_failure; + + /* For multihop routes, a valid weight must be in range 1-256 (on netlink's + * "rtnh_hops" this is 0-255). We allow that the caller leaves the value unset + * at zero. */ + if (i == 0u) { + rtnh->rtnh_hops = NM_MAX(obj->ip4_route.weight, 1u) - 1u; + rtnh->rtnh_ifindex = obj->ip4_route.ifindex; + gw = obj->ip4_route.gateway; + } else { + const NMPlatformIP4RtNextHop *n = &obj->_ip4_route.extra_nexthops[i - 1u]; + + rtnh->rtnh_hops = NM_MAX(n->weight, 1u) - 1u; + rtnh->rtnh_ifindex = n->ifindex; + gw = n->gateway; + } + NLA_PUT_U32(msg, RTA_GATEWAY, gw); + + rtnh->rtnh_flags = 0; + + if (obj->ip4_route.n_nexthops > 1 + && NM_FLAGS_HAS(obj->ip_route.r_rtm_flags, (unsigned) (RTNH_F_ONLINK)) && gw != 0) { + /* Unlike kernel, we only track the onlink flag per NMPlatformIP4Address, and + * not per nexthop. That is fine for NetworkManager configuring addresses. + * It is not fine for tracking addresses from kernel in platform cache, + * because the rtnh_flags of the nexthops need to be part of nmp_object_id_cmp(). */ + rtnh->rtnh_flags |= RTNH_F_ONLINK; + } + + rtnh->rtnh_len = (char *) nlmsg_tail(nlmsg_hdr(msg)) - (char *) rtnh; + } + + nla_nest_end(msg, multipath); + } + if (obj->ip_route.mss || obj->ip_route.window || obj->ip_route.cwnd || obj->ip_route.initcwnd || obj->ip_route.initrwnd || obj->ip_route.mtu || obj->ip_route.quickack || obj->ip_route.rto_min || lock) { @@ -5462,7 +5749,7 @@ _nl_msg_new_tfilter(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPlatformT if (nm_streq(action->kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { const NMPlatformActionSimple *simple = &action->simple; struct tc_defact sel = { - 0, + 0, }; if (!(act_options = nla_nest_start(msg, TCA_ACT_OPTIONS))) @@ -5476,7 +5763,7 @@ _nl_msg_new_tfilter(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPlatformT } else if (nm_streq(action->kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { const NMPlatformActionMirred *mirred = &action->mirred; struct tc_mirred sel = { - 0, + 0, }; if (!(act_options = nla_nest_start(msg, TCA_ACT_OPTIONS))) @@ -5512,25 +5799,25 @@ nla_put_failure: /*****************************************************************************/ -#define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ - G_STMT_START \ - { \ - const char *const _pathid = (pathid); \ - const int _dirfd = (dirfd); \ - const char *const _path = (path); \ - \ - nm_assert(_path &&_path[0]); \ - g_assert(!strstr(_path, "/../")); \ - if (_dirfd < 0) { \ - nm_assert(!_pathid); \ - nm_assert(_path[0] == '/'); \ - nm_assert(g_str_has_prefix(_path, "/proc/sys/") || g_str_has_prefix(_path, "/sys/") \ - || g_str_has_prefix(_path, "/proc/net")); \ - } else { \ - nm_assert(_pathid &&_pathid[0] && _pathid[0] != '/'); \ - nm_assert(_path[0] != '/'); \ - } \ - } \ +#define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ + G_STMT_START \ + { \ + const char *const _pathid = (pathid); \ + const int _dirfd = (dirfd); \ + const char *const _path = (path); \ + \ + nm_assert(_path &&_path[0]); \ + g_assert(!strstr(_path, "/../")); \ + if (_dirfd < 0) { \ + nm_assert(!_pathid); \ + nm_assert(_path[0] == '/'); \ + nm_assert(NM_STR_HAS_PREFIX(_path, "/proc/sys/") || NM_STR_HAS_PREFIX(_path, "/sys/") \ + || NM_STR_HAS_PREFIX(_path, "/proc/net")); \ + } else { \ + nm_assert(_pathid &&_pathid[0] && _pathid[0] != '/'); \ + nm_assert(_path[0] != '/'); \ + } \ + } \ G_STMT_END /*****************************************************************************/ @@ -5599,12 +5886,14 @@ sysctl_set_internal(NMPlatform *platform, const char *path, const char *value) { - int fd, tries; + int fd; + int tries; gssize nwrote; gssize len; char *actual; gs_free char *actual_free = NULL; int errsv; + int r; if (dirfd < 0) { pathid = path; @@ -5698,18 +5987,13 @@ sysctl_set_internal(NMPlatform *platform, _LOGE("sysctl: failed to set '%s' to '%s' after three attempts", path, value); } - if (nwrote < len - 1) { - if (nm_close(fd) != 0) { - if (errsv != 0) - errno = errsv; - } else if (errsv != 0) - errno = errsv; - else - errno = EIO; - return FALSE; - } - if (nm_close(fd) != 0) { - /* errno is already properly set. */ + r = nm_close_with_error(fd); + if (r < 0 || nwrote < len - 1) { + if (errsv == 0) { + /* propagate the error from nm_close_with_error(). */ + errsv = (r < 0) ? -r : EIO; + } + errno = errsv; return FALSE; } @@ -6260,11 +6544,11 @@ static NM_UTILS_LOOKUP_STR_DEFINE( NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_ALL), NM_UTILS_LOOKUP_ITEM_IGNORE(__DELAYED_ACTION_TYPE_MAX), ); -#define delayed_action_get_list_wait_for_resonse(priv, netlink_protocol, idx) \ - (&g_array_index((priv)->delayed_action.list_wait_for_response_x[nmp_netlink_protocol_check( \ - (netlink_protocol))], \ - DelayedActionWaitForNlResponseData, \ - (idx))) +#define delayed_action_get_list_wait_for_resonse(priv, netlink_protocol, idx) \ + (&nm_g_array_index((priv)->delayed_action.list_wait_for_response_x[nmp_netlink_protocol_check( \ + (netlink_protocol))], \ + DelayedActionWaitForNlResponseData, \ + (idx))) static const char * delayed_action_to_string_full(DelayedActionType action_type, @@ -6753,6 +7037,14 @@ cache_prune_one_type(NMPlatform *platform, const NMPLookup *lookup) obj = main_entry->obj; + if (NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP6_ADDRESS) { + const NMPlatformIP6Address *pladdr = NMP_OBJECT_CAST_IP6_ADDRESS(obj); + + if (pladdr->n_ifa_flags & IFA_F_TENTATIVE) { + nm_platform_ip6_dadfailed_set(platform, pladdr->ifindex, &pladdr->address, TRUE); + } + } + _LOGt("cache-prune: prune %s", nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, sbuf, sizeof(sbuf))); @@ -7568,6 +7860,7 @@ _rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg) gboolean resync_required = FALSE; gboolean only_dirty = FALSE; gboolean is_ipv6; + gboolean route_is_alive; /* IPv4 routes that are a response to RTM_GETROUTE must have * the cloned flag while IPv6 routes don't have to. */ @@ -7597,24 +7890,13 @@ _rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg) } } - if (ip_route_ignored_protocol(NMP_OBJECT_CAST_IP_ROUTE(obj))) { - /* We ignore certain rtm_protocol, because NetworkManager would only ever - * configure certain protocols. Other routes were not added by NetworkManager - * and we don't need to track them in the platform cache. - * - * This is to help with the performance overhead of a huge number of - * routes, for example with the bird BGP software, that adds routes - * with RTPROT_BIRD protocol. - * - * Even if this is a IPv6 multipath route, we abort (parse_nlmsg_iter). There - * is nothing for us to do. */ - return; - } + route_is_alive = ip_route_is_alive(NMP_OBJECT_CAST_IP_ROUTE(obj)); cache_op = nmp_cache_update_netlink_route(cache, obj, is_dump, msghdr->nlmsg_flags, + route_is_alive, &obj_old, &obj_new, &obj_replace, @@ -7661,11 +7943,21 @@ _rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg) delayed_action_schedule(platform, delayed_action_refresh_from_needle_object(obj), NULL); + /* We are done here. */ + return; } break; } case RTM_DELADDR: + if (NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP6_ADDRESS) { + const NMPlatformIP6Address *ip6 = NMP_OBJECT_CAST_IP6_ADDRESS(obj); + + if (ip6->n_ifa_flags & IFA_F_DADFAILED) { + nm_platform_ip6_dadfailed_set(platform, ip6->ifindex, &ip6->address, TRUE); + } + } + /* fall-through */ case RTM_DELLINK: case RTM_DELQDISC: case RTM_DELROUTE: @@ -7948,7 +8240,8 @@ retry: } else if (NM_IN_SET(seq_result, -ESRCH, -ENOENT)) { log_detail = ", firmware not found"; result = -NME_PL_NO_FIRMWARE; - } else if (NM_IN_SET(seq_result, -ERANGE) && change_link_type == CHANGE_LINK_TYPE_SET_MTU) { + } else if (NM_IN_SET(seq_result, -ERANGE, -EINVAL) + && change_link_type == CHANGE_LINK_TYPE_SET_MTU) { log_detail = ", setting MTU to requested size is not possible"; result = -NME_PL_CANT_SET_MTU; } else if (NM_IN_SET(seq_result, -ENFILE) && change_link_type == CHANGE_LINK_TYPE_SET_ADDRESS @@ -8126,7 +8419,7 @@ static gboolean link_set_token(NMPlatform *platform, int ifindex, const NMUtilsIPv6IfaceId *iid) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; _LOGD("link: change %d: token: set IPv6 address generation token to %s", ifindex, @@ -8202,7 +8495,7 @@ link_set_address(NMPlatform *platform, int ifindex, gconstpointer address, size_ nm_auto_nlmsg struct nl_msg *nlmsg = NULL; const ChangeLinkData d = { .set_address = - { + { .address = address, .length = length, }, @@ -9310,25 +9603,15 @@ ip6_address_delete(NMPlatform *platform, int ifindex, struct in6_addr addr, guin /*****************************************************************************/ static int -ip_route_add(NMPlatform *platform, - NMPNlmFlags flags, - int addr_family, - const NMPlatformIPRoute *route) +ip_route_add(NMPlatform *platform, NMPNlmFlags flags, NMPObject *obj_stack) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj; - - nmp_object_stackinit(&obj, - NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)), - (const NMPlatformObject *) route); - - nm_platform_ip_route_normalize(addr_family, NMP_OBJECT_CAST_IP_ROUTE(&obj)); - nlmsg = _nl_msg_new_route(RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, &obj); + nlmsg = _nl_msg_new_route(RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, obj_stack); if (!nlmsg) g_return_val_if_reached(-NME_BUG); return do_add_addrroute(platform, - &obj, + obj_stack, nlmsg, NM_FLAGS_HAS(flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE)); } @@ -9820,7 +10103,7 @@ continue_reading: goto stop; } - hdr = (struct nlmsghdr *) priv->netlink_recv_buf.buf; + hdr = NM_CAST_ALIGN(struct nlmsghdr, priv->netlink_recv_buf.buf); while (nlmsg_ok(hdr, n)) { WaitForNlResponseResult seq_result; gboolean process_valid_msg = FALSE; @@ -10588,8 +10871,8 @@ constructed(GObject *_object) : (!nmp_netns_get_current() ? "no netns support" : nm_sprintf_bufa(100, - "in netns[%p]%s", - nmp_netns_get_current(), + "in netns[" NM_HASH_OBFUSCATE_PTR_FMT "]%s", + NM_HASH_OBFUSCATE_PTR(nmp_netns_get_current()), nmp_netns_get_current() == nmp_netns_get_initial() ? "/main" : "")), nm_platform_get_use_udev(platform) ? "use" : "no", @@ -10732,7 +11015,10 @@ path_is_read_only_fs(const char *path) } NMPlatform * -nm_linux_platform_new(gboolean log_with_ptr, gboolean netns_support, gboolean cache_tc) +nm_linux_platform_new(NMDedupMultiIndex *multi_idx, + gboolean log_with_ptr, + gboolean netns_support, + gboolean cache_tc) { gboolean use_udev = FALSE; @@ -10740,6 +11026,8 @@ nm_linux_platform_new(gboolean log_with_ptr, gboolean netns_support, gboolean ca use_udev = TRUE; return g_object_new(NM_TYPE_LINUX_PLATFORM, + NM_PLATFORM_MULTI_IDX, + multi_idx, NM_PLATFORM_LOG_WITH_PTR, log_with_ptr, NM_PLATFORM_USE_UDEV, diff --git a/src/libnm-platform/nm-linux-platform.h b/src/libnm-platform/nm-linux-platform.h index 85e06934..08135a4a 100644 --- a/src/libnm-platform/nm-linux-platform.h +++ b/src/libnm-platform/nm-linux-platform.h @@ -10,7 +10,7 @@ #define NM_TYPE_LINUX_PLATFORM (nm_linux_platform_get_type()) #define NM_LINUX_PLATFORM(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatform)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatform)) #define NM_LINUX_PLATFORM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformClass)) #define NM_IS_LINUX_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_LINUX_PLATFORM)) @@ -23,6 +23,11 @@ typedef struct _NMLinuxPlatformClass NMLinuxPlatformClass; GType nm_linux_platform_get_type(void); -NMPlatform *nm_linux_platform_new(gboolean log_with_ptr, gboolean netns_support, gboolean cache_tc); +struct _NMDedupMultiIndex; + +NMPlatform *nm_linux_platform_new(struct _NMDedupMultiIndex *multi_idx, + gboolean log_with_ptr, + gboolean netns_support, + gboolean cache_tc); #endif /* __NETWORKMANAGER_LINUX_PLATFORM_H__ */ diff --git a/src/libnm-platform/nm-netlink.c b/src/libnm-platform/nm-netlink.c index e08eee5e..fc704226 100644 --- a/src/libnm-platform/nm-netlink.c +++ b/src/libnm-platform/nm-netlink.c @@ -325,7 +325,8 @@ nlmsg_parse_error(const struct nlmsghdr *nlh, const char **out_extack_msg) struct nlattr *tb[G_N_ELEMENTS(policy)]; struct nlattr *tlvs; - tlvs = (struct nlattr *) ((char *) e + sizeof(*e) + e->msg.nlmsg_len - NLMSG_HDRLEN); + tlvs = NM_CAST_ALIGN(struct nlattr, + (((char *) e) + sizeof(*e) + e->msg.nlmsg_len - NLMSG_HDRLEN)); if (nla_parse_arr(tb, tlvs, nlh->nlmsg_len - sizeof(*e) - e->msg.nlmsg_len, policy) >= 0) { if (tb[NLMSGERR_ATTR_MSG]) *out_extack_msg = nla_get_string(tb[NLMSGERR_ATTR_MSG]); @@ -842,7 +843,7 @@ genlmsg_len(const struct genlmsghdr *gnlh) { const struct nlmsghdr *nlh; - nlh = (const struct nlmsghdr *) ((const unsigned char *) gnlh - NLMSG_HDRLEN); + nlh = NM_CAST_ALIGN(const struct nlmsghdr, (((char *) gnlh) - NLMSG_HDRLEN)); return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); } @@ -1253,7 +1254,7 @@ continue_reading: if (n <= 0) return n; - hdr = (struct nlmsghdr *) buf; + hdr = NM_CAST_ALIGN(struct nlmsghdr, buf); while (nlmsg_ok(hdr, n)) { nm_auto_nlmsg struct nl_msg *msg = NULL; diff --git a/src/libnm-platform/nm-netlink.h b/src/libnm-platform/nm-netlink.h index 634be2b4..cdb8a81c 100644 --- a/src/libnm-platform/nm-netlink.h +++ b/src/libnm-platform/nm-netlink.h @@ -340,7 +340,7 @@ nla_next(const struct nlattr *nla, int *remaining) int totlen = NLA_ALIGN(nla->nla_len); *remaining -= totlen; - return (struct nlattr *) ((char *) nla + totlen); + return NM_CAST_ALIGN(struct nlattr, (((char *) nla) + totlen)); } #define nla_for_each_attr(pos, head, len, rem) \ @@ -434,7 +434,7 @@ nlmsg_next(struct nlmsghdr *nlh, int *remaining) *remaining -= totlen; - return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); + return NM_CAST_ALIGN(struct nlmsghdr, (((char *) nlh) + totlen)); } int nlmsg_get_proto(struct nl_msg *msg); @@ -493,8 +493,9 @@ nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) static inline struct nlattr * nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) { - unsigned char *data = nlmsg_data(nlh); - return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); + char *data = nlmsg_data(nlh); + + return NM_CAST_ALIGN(struct nlattr, (data + NLMSG_ALIGN(hdrlen))); } static inline struct nlattr * diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index 198e5f0a..c80d9648 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -115,28 +115,32 @@ nmp_link_address_get_as_bytes(const NMPLinkAddress *addr) #define _NMLOG_DOMAIN LOGD_PLATFORM #define _NMLOG_PREFIX_NAME "platform" -#define NMLOG_COMMON(level, name, ...) \ - G_STMT_START \ - { \ - char __prefix[32]; \ - const char *__p_prefix = _NMLOG_PREFIX_NAME; \ - const NMPlatform *const __self = (self); \ - const char *__name = name; \ - \ - if (__self && NM_PLATFORM_GET_PRIVATE(__self)->log_with_ptr) { \ - g_snprintf(__prefix, sizeof(__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ - __p_prefix = __prefix; \ - } \ - _nm_log((level), \ - _NMLOG_DOMAIN, \ - 0, \ - __name, \ - NULL, \ - "%s: %s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - __p_prefix, \ - NM_PRINT_FMT_QUOTED(__name, "(", __name, ") ", "") \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ +#define NMLOG_COMMON(level, name, ...) \ + G_STMT_START \ + { \ + char __prefix[64]; \ + const char *__p_prefix = _NMLOG_PREFIX_NAME; \ + const NMPlatform *const __self = (self); \ + const char *__name = name; \ + \ + if (__self && NM_PLATFORM_GET_PRIVATE(__self)->log_with_ptr) { \ + g_snprintf(__prefix, \ + sizeof(__prefix), \ + "%s[" NM_HASH_OBFUSCATE_PTR_FMT "]", \ + _NMLOG_PREFIX_NAME, \ + NM_HASH_OBFUSCATE_PTR(__self)); \ + __p_prefix = __prefix; \ + } \ + _nm_log((level), \ + _NMLOG_DOMAIN, \ + 0, \ + __name, \ + NULL, \ + "%s: %s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + __p_prefix, \ + NM_PRINT_FMT_QUOTED(__name, "(", __name, ") ", "") \ + _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ G_STMT_END #define _NMLOG(level, ...) \ @@ -180,6 +184,7 @@ static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = {0}; enum { PROP_0, + PROP_MULTI_IDX, PROP_NETNS_SUPPORT, PROP_USE_UDEV, PROP_LOG_WITH_PTR, @@ -195,6 +200,7 @@ typedef struct _NMPlatformPrivate { guint ip4_dev_route_blacklist_check_id; guint ip4_dev_route_blacklist_gc_timeout_id; GHashTable *ip4_dev_route_blacklist_hash; + CList ip6_dadfailed_lst_head; NMDedupMultiIndex *multi_idx; NMPCache *cache; } NMPlatformPrivate; @@ -948,9 +954,9 @@ _link_get_all_presort(gconstpointer p_a, gconstpointer p_b, gpointer sort_by_nam const NMPlatformLink *b = NMP_OBJECT_CAST_LINK(*((const NMPObject **) p_b)); /* Loopback always first */ - if (a->ifindex == 1) + if (a->ifindex == NM_LOOPBACK_IFINDEX) return -1; - if (b->ifindex == 1) + if (b->ifindex == NM_LOOPBACK_IFINDEX) return 1; if (GPOINTER_TO_INT(sort_by_name)) { @@ -1369,6 +1375,18 @@ nm_platform_link_add(NMPlatform *self, buf_p, buf_len); break; + case NM_LINK_TYPE_VTI: + nm_strbuf_append_str(&buf_p, &buf_len, ", "); + nm_platform_lnk_vti_to_string((const NMPlatformLnkVti *) extra_data, + buf_p, + buf_len); + break; + case NM_LINK_TYPE_VTI6: + nm_strbuf_append_str(&buf_p, &buf_len, ", "); + nm_platform_lnk_vti6_to_string((const NMPlatformLnkVti6 *) extra_data, + buf_p, + buf_len); + break; case NM_LINK_TYPE_BOND: nm_strbuf_append_str(&buf_p, &buf_len, ", "); nm_platform_lnk_bond_to_string((const NMPlatformLnkBond *) extra_data, @@ -2417,6 +2435,18 @@ nm_platform_link_get_lnk_vrf(NMPlatform *self, int ifindex, const NMPlatformLink return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VRF, out_link); } +const NMPlatformLnkVti * +nm_platform_link_get_lnk_vti(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VTI, out_link); +} + +const NMPlatformLnkVti6 * +nm_platform_link_get_lnk_vti6(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VTI6, out_link); +} + const NMPlatformLnkVxlan * nm_platform_link_get_lnk_vxlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) { @@ -3305,52 +3335,14 @@ nm_platform_wpan_set_channel(NMPlatform *self, int ifindex, guint8 page, guint8 return klass->wpan_set_channel(self, ifindex, page, channel); } -#define TO_STRING_DEV_BUF_SIZE (5 + 15 + 1) -static const char * -_to_string_dev(NMPlatform *self, int ifindex, char *buf, size_t size) -{ - nm_assert(buf); - nm_assert(size >= TO_STRING_DEV_BUF_SIZE); - - if (ifindex) { - const char *name = ifindex > 0 && self ? nm_platform_link_get_name(self, ifindex) : NULL; - char *buf2; - - strcpy(buf, " dev "); - buf2 = buf + 5; - size -= 5; - - if (name) - g_strlcpy(buf2, name, size); - else - g_snprintf(buf2, size, "%d", ifindex); - } else - buf[0] = 0; - - return buf; -} - -#define TO_STRING_IFA_FLAGS_BUF_SIZE 256 - -static const char * -_to_string_ifa_flags(guint32 ifa_flags, char *buf, gsize size) -{ -#define S_FLAGS_PREFIX " flags " - nm_assert(buf && size >= TO_STRING_IFA_FLAGS_BUF_SIZE && size > NM_STRLEN(S_FLAGS_PREFIX)); +/*****************************************************************************/ - if (!ifa_flags) - buf[0] = '\0'; - else { - nm_platform_addr_flags2str(ifa_flags, - &buf[NM_STRLEN(S_FLAGS_PREFIX)], - size - NM_STRLEN(S_FLAGS_PREFIX)); - if (buf[NM_STRLEN(S_FLAGS_PREFIX)] == '\0') - buf[0] = '\0'; - else - memcpy(buf, S_FLAGS_PREFIX, NM_STRLEN(S_FLAGS_PREFIX)); - } - return buf; -} +#define _to_string_dev(arr, ifindex) \ + ({ \ + const int _ifindex = (ifindex); \ + \ + _ifindex ? nm_sprintf_buf((arr), " dev %d", ifindex) : nm_str_truncate((arr)); \ + }) /*****************************************************************************/ @@ -3557,72 +3549,6 @@ nm_platform_lookup_clone(NMPlatform *self, user_data); } -void -nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen) -{ - nm_assert(plen <= 32); - - addr->address = address; - addr->peer_address = address; - addr->plen = plen; -} - -const struct in6_addr * -nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr) -{ - if (IN6_IS_ADDR_UNSPECIFIED(&addr->peer_address) - || IN6_ARE_ADDR_EQUAL(&addr->peer_address, &addr->address)) - return &addr->address; - return &addr->peer_address; -} - -gboolean -nm_platform_ip_address_match(int addr_family, - const NMPlatformIPAddress *address, - NMPlatformMatchFlags match_flag) -{ - nm_assert(!NM_FLAGS_ANY( - match_flag, - ~(NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY))); - nm_assert(NM_FLAGS_ANY(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY)); - nm_assert(NM_FLAGS_ANY(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)); - - if (addr_family == AF_INET) { - if (nm_utils_ip4_address_is_link_local(((NMPlatformIP4Address *) address)->address)) { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL)) - return FALSE; - } else { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL)) - return FALSE; - } - } else { - if (IN6_IS_ADDR_LINKLOCAL(address->address_ptr)) { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL)) - return FALSE; - } else { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL)) - return FALSE; - } - } - - if (NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_DADFAILED)) { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED)) - return FALSE; - } else if (NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_TENTATIVE) - && !NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_OPTIMISTIC)) { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE)) - return FALSE; - } else if (NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_DEPRECATED)) { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED)) - return FALSE; - } else { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) - return FALSE; - } - - return TRUE; -} - gboolean nm_platform_ip4_address_add(NMPlatform *self, int ifindex, @@ -3711,6 +3637,9 @@ nm_platform_ip6_address_add(NMPlatform *self, _LOG3D("address: adding or updating IPv6 address: %s", nm_platform_ip6_address_to_string(&addr, sbuf, sizeof(sbuf))); } + + nm_platform_ip6_dadfailed_set(self, ifindex, &address, FALSE); + return klass ->ip6_address_add(self, ifindex, address, plen, peer_address, lifetime, preferred, flags); } @@ -3722,9 +3651,9 @@ nm_platform_ip4_address_delete(NMPlatform *self, guint8 plen, in_addr_t peer_address) { - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char b1[NM_UTILS_INET_ADDRSTRLEN]; - char b2[NM_UTILS_INET_ADDRSTRLEN]; + char str_dev[30]; + char b1[NM_INET_ADDRSTRLEN]; + char b2[NM_INET_ADDRSTRLEN]; char str_peer[INET_ADDRSTRLEN + 50]; _CHECK_SELF(self, klass, FALSE); @@ -3733,20 +3662,20 @@ nm_platform_ip4_address_delete(NMPlatform *self, g_return_val_if_fail(plen <= 32, FALSE); _LOG3D("address: deleting IPv4 address %s/%d, %s%s", - _nm_utils_inet4_ntop(address, b1), + nm_inet4_ntop(address, b1), plen, peer_address != address - ? nm_sprintf_buf(str_peer, "peer %s, ", _nm_utils_inet4_ntop(peer_address, b2)) + ? nm_sprintf_buf(str_peer, "peer %s, ", nm_inet4_ntop(peer_address, b2)) : "", - _to_string_dev(self, ifindex, str_dev, sizeof(str_dev))); + _to_string_dev(str_dev, ifindex)); return klass->ip4_address_delete(self, ifindex, address, plen, peer_address); } gboolean nm_platform_ip6_address_delete(NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen) { - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char str_dev[30]; + char sbuf[NM_INET_ADDRSTRLEN]; _CHECK_SELF(self, klass, FALSE); @@ -3754,9 +3683,9 @@ nm_platform_ip6_address_delete(NMPlatform *self, int ifindex, struct in6_addr ad g_return_val_if_fail(plen <= 128, FALSE); _LOG3D("address: deleting IPv6 address %s/%d, %s", - _nm_utils_inet6_ntop(&address, sbuf), + nm_inet6_ntop(&address, sbuf), plen, - _to_string_dev(self, ifindex, str_dev, sizeof(str_dev))); + _to_string_dev(str_dev, ifindex)); return klass->ip6_address_delete(self, ifindex, address, plen); } @@ -3940,13 +3869,10 @@ static guint _ip4_addr_subnets_hash(gconstpointer ptr) { const NMPlatformIP4Address *addr = NMP_OBJECT_CAST_IP4_ADDRESS(ptr); - NMHashState h; - nm_hash_init(&h, 3282159733); - nm_hash_update_vals(&h, + return nm_hash_vals(3282159733, addr->plen, - nm_utils_ip4_address_clear_host_address(addr->address, addr->plen)); - return nm_hash_complete(&h); + nm_ip4_addr_clear_host_address(addr->address, addr->plen)); } static gboolean @@ -3956,8 +3882,8 @@ _ip4_addr_subnets_equal(gconstpointer p_a, gconstpointer p_b) const NMPlatformIP4Address *b = NMP_OBJECT_CAST_IP4_ADDRESS(p_b); return a->plen == b->plen - && (nm_utils_ip4_address_clear_host_address(a->address, a->plen) - == nm_utils_ip4_address_clear_host_address(b->address, b->plen)); + && (nm_ip4_addr_clear_host_address(a->address, a->plen) + == nm_ip4_addr_clear_host_address(b->address, b->plen)); } static GHashTable * @@ -4107,6 +4033,9 @@ ip6_address_scope_cmp_descending(gconstpointer p_a, gconstpointer p_b, gpointer * by the function. * Addresses that are both contained in @known_addresses and @addresses_prune * will be configured. + * @flags: #NMPIPAddressSyncFlags to affect the sync. If "with-noprefixroute" + * flag is set, the method will automatically set IFA_F_NOPREFIXROUTE for + * all addresses. * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -4115,11 +4044,12 @@ ip6_address_scope_cmp_descending(gconstpointer p_a, gconstpointer p_b, gpointer * Returns: %TRUE on success. */ gboolean -nm_platform_ip_address_sync(NMPlatform *self, - int addr_family, - int ifindex, - GPtrArray *known_addresses, - GPtrArray *addresses_prune) +nm_platform_ip_address_sync(NMPlatform *self, + int addr_family, + int ifindex, + GPtrArray *known_addresses, + GPtrArray *addresses_prune, + NMPIPAddressSyncFlags flags) { gint32 now = 0; const int IS_IPv4 = NM_IS_IPv4(addr_family); @@ -4531,7 +4461,9 @@ next_plat:; nm_platform_ip4_broadcast_address_from_addr(&known_address->a4), lifetime, preferred, - known_address->ax.a_no_auto_noprefixroute ? 0 : IFA_F_NOPREFIXROUTE, + NM_FLAGS_HAS(flags, NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE) + ? IFA_F_NOPREFIXROUTE + : 0, known_address->a4.label)) success = FALSE; } else { @@ -4543,7 +4475,9 @@ next_plat:; known_address->a6.peer_address, lifetime, preferred, - (known_address->ax.a_no_auto_noprefixroute ? 0 : IFA_F_NOPREFIXROUTE) + (NM_FLAGS_HAS(flags, NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE) + ? IFA_F_NOPREFIXROUTE + : 0) | known_address->a6.n_ifa_flags)) success = FALSE; } @@ -4572,7 +4506,12 @@ nm_platform_ip_address_flush(NMPlatform *self, int addr_family, int ifindex) addresses_prune = nm_platform_ip_address_get_prune_list(self, addr_family2, ifindex, NULL, 0); - if (!nm_platform_ip_address_sync(self, addr_family2, ifindex, NULL, addresses_prune)) + if (!nm_platform_ip_address_sync(self, + addr_family2, + ifindex, + NULL, + addresses_prune, + NMP_IP_ADDRESS_SYNC_FLAGS_NONE)) success = FALSE; } return success; @@ -4654,7 +4593,26 @@ nm_platform_ip_address_get_prune_list(NMPlatform *self, c_list_for_each (iter, &head_entry->lst_entries_head) { const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj; - if (!IS_IPv4) { + if (IS_IPv4) { + const NMPlatformIP4Address *a4 = NMP_OBJECT_CAST_IP4_ADDRESS(obj); + + if (a4->address == NM_IPV4LO_ADDR1 && a4->plen == NM_IPV4LO_PREFIXLEN) { + const NMPlatformIP4Address addr = (NMPlatformIP4Address){ + .ifindex = NM_LOOPBACK_IFINDEX, + .address = NM_IPV4LO_ADDR1, + .peer_address = NM_IPV4LO_ADDR1, + .plen = NM_IPV4LO_PREFIXLEN, + .use_ip4_broadcast_address = TRUE, + }; + + if (nm_platform_ip4_address_cmp(a4, + &addr, + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) + == 0) { + continue; + } + } + } else { const NMPlatformIP6Address *a6 = NMP_OBJECT_CAST_IP6_ADDRESS(obj); if (NM_FLAGS_HAS(a6->n_ifa_flags, IFA_F_SECONDARY) @@ -4710,9 +4668,6 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, GPtrArray *routes_prune = NULL; const NMDedupMultiHeadEntry *head_entry; CList *iter; - NMPlatformIP4Route rt_local4; - NMPlatformIP6Route rt_local6; - NMPlatformIP6Route rt_mcast6; const NMPlatformLink *pllink; const NMPlatformLnkVrf *lnk_vrf; guint32 local_table; @@ -4737,10 +4692,6 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, lnk_vrf = nm_platform_link_get_lnk_vrf(self, pllink->master, NULL); local_table = lnk_vrf ? lnk_vrf->table : RT_TABLE_LOCAL; - rt_local4.plen = 0; - rt_local6.plen = 0; - rt_mcast6.plen = 0; - c_list_for_each (iter, &head_entry->lst_entries_head) { const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj; const NMPlatformIPXRoute *rt = NMP_OBJECT_CAST_IPX_ROUTE(obj); @@ -4762,6 +4713,44 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, * pruning them. */ if (NM_IS_IPv4(addr_family)) { + if (ifindex == NM_LOOPBACK_IFINDEX + && NM_IN_SET(rt->r4.network, NM_IPV4LO_ADDR1, NM_IPV4LO_NETWORK)) { + NMPlatformIP4Route r; + + if (rt->r4.network == NM_IPV4LO_ADDR1) { + r = (NMPlatformIP4Route){ + .ifindex = NM_LOOPBACK_IFINDEX, + .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), + .table_coerced = nm_platform_route_table_coerce(local_table), + .network = NM_IPV4LO_ADDR1, + .plen = 32, + .metric = 0, + .rt_source = NM_IPV4LO_ADDR1, + .scope_inv = nm_platform_route_scope_inv(RT_SCOPE_HOST), + .pref_src = NM_IPV4LO_ADDR1, + }; + } else { + r = (NMPlatformIP4Route){ + .ifindex = NM_LOOPBACK_IFINDEX, + .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), + .table_coerced = nm_platform_route_table_coerce(local_table), + .network = NM_IPV4LO_NETWORK, + .plen = NM_IPV4LO_PREFIXLEN, + .metric = 0, + .rt_source = NM_IPV4LO_ADDR1, + .scope_inv = nm_platform_route_scope_inv(RT_SCOPE_HOST), + .pref_src = NM_IPV4LO_ADDR1, + }; + } + + if (nm_platform_ip4_route_cmp(&rt->r4, + &r, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + == 0) { + continue; + } + } + /* for each IPv4 address kernel adds a route like * * local $ADDR dev $IFACE table local proto kernel scope host src $PRIMARY_ADDR @@ -4772,29 +4761,26 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, && rt->rx.metric == 0 && rt->r4.scope_inv == nm_platform_route_scope_inv(RT_SCOPE_HOST) && rt->r4.gateway == INADDR_ANY) { - if (rt_local4.plen == 0) { - rt_local4 = (NMPlatformIP4Route){ - .ifindex = ifindex, - .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), - .plen = 32, - .rt_source = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, - .metric = 0, - .table_coerced = nm_platform_route_table_coerce(local_table), - .scope_inv = nm_platform_route_scope_inv(RT_SCOPE_HOST), - .gateway = INADDR_ANY, - }; - } - - /* the possible "network" depends on the addresses we have. We don't check that - * carefully. If the other parameters match, we assume that this route is the one - * generated by kernel. */ - rt_local4.network = rt->r4.network; - rt_local4.pref_src = rt->r4.pref_src; + const NMPlatformIP4Route r = { + .ifindex = ifindex, + .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), + .plen = 32, + .rt_source = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, + .metric = 0, + .table_coerced = nm_platform_route_table_coerce(local_table), + .scope_inv = nm_platform_route_scope_inv(RT_SCOPE_HOST), + .gateway = INADDR_ANY, + /* the possible "network" depends on the addresses we have. We don't check that + * carefully. If the other parameters match, we assume that this route is the one + * generated by kernel. */ + .network = rt->r4.network, + .pref_src = rt->r4.pref_src, + }; /* to be more confident about comparing the value, use our nm_platform_ip4_route_cmp() * implementation. That will also consider parameters that we leave unspecified here. */ if (nm_platform_ip4_route_cmp(&rt->r4, - &rt_local4, + &r, NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 0) continue; @@ -4809,23 +4795,20 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, && rt->rx.plen == 128 && rt->rx.rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL && rt->rx.metric == 0 && rt->r6.rt_pref == NM_ICMPV6_ROUTER_PREF_MEDIUM && IN6_IS_ADDR_UNSPECIFIED(&rt->r6.gateway)) { - if (rt_local6.plen == 0) { - rt_local6 = (NMPlatformIP6Route){ - .ifindex = ifindex, - .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), - .plen = 128, - .rt_source = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, - .metric = 0, - .table_coerced = nm_platform_route_table_coerce(local_table), - .rt_pref = NM_ICMPV6_ROUTER_PREF_MEDIUM, - .gateway = IN6ADDR_ANY_INIT, - }; - } - - rt_local6.network = rt->r6.network; + const NMPlatformIP6Route r = { + .ifindex = ifindex, + .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), + .plen = 128, + .rt_source = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, + .metric = 0, + .table_coerced = nm_platform_route_table_coerce(local_table), + .rt_pref = NM_ICMPV6_ROUTER_PREF_MEDIUM, + .gateway = IN6ADDR_ANY_INIT, + .network = rt->r6.network, + }; if (nm_platform_ip6_route_cmp(&rt->r6, - &rt_local6, + &r, NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 0) continue; @@ -4847,22 +4830,21 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, && rt->rx.plen == 8 && rt->rx.rt_source == NM_IP_CONFIG_SOURCE_RTPROT_BOOT && rt->rx.metric == 256 && rt->r6.rt_pref == NM_ICMPV6_ROUTER_PREF_MEDIUM && IN6_IS_ADDR_UNSPECIFIED(&rt->r6.gateway)) { - if (rt_mcast6.plen == 0) { - rt_mcast6 = (NMPlatformIP6Route){ - .ifindex = ifindex, - .type_coerced = nm_platform_route_type_coerce(RTN_UNICAST), - .plen = 8, - .rt_source = NM_IP_CONFIG_SOURCE_RTPROT_BOOT, - .metric = 256, - .table_coerced = nm_platform_route_table_coerce(local_table), - .rt_pref = NM_ICMPV6_ROUTER_PREF_MEDIUM, - .gateway = IN6ADDR_ANY_INIT, - .network = {{{0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}}, - }; - } + const NMPlatformIP6Route r = { + .ifindex = ifindex, + .type_coerced = nm_platform_route_type_coerce(RTN_UNICAST), + .plen = 8, + .rt_source = NM_IP_CONFIG_SOURCE_RTPROT_BOOT, + .metric = 256, + .table_coerced = nm_platform_route_table_coerce(local_table), + .rt_pref = NM_ICMPV6_ROUTER_PREF_MEDIUM, + .gateway = IN6ADDR_ANY_INIT, + .network = + NM_IN6ADDR_INIT(0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + }; if (nm_platform_ip6_route_cmp(&rt->r6, - &rt_mcast6, + &r, NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 0) continue; @@ -5251,15 +5233,15 @@ nm_platform_ip_route_normalize(int addr_family, NMPlatformIPRoute *route) r4 = (NMPlatformIP4Route *) route; route->metric = nm_platform_ip4_route_get_effective_metric(r4); route->metric_any = FALSE; - r4->network = nm_utils_ip4_address_clear_host_address(r4->network, r4->plen); + r4->network = nm_ip4_addr_clear_host_address(r4->network, r4->plen); r4->scope_inv = _ip_route_scope_inv_get_normalized(r4); break; case AF_INET6: r6 = (NMPlatformIP6Route *) route; route->metric = nm_platform_ip6_route_get_effective_metric(r6); route->metric_any = FALSE; - nm_utils_ip6_address_clear_host_address(&r6->network, &r6->network, r6->plen); - nm_utils_ip6_address_clear_host_address(&r6->src, &r6->src, r6->src_plen); + nm_ip6_addr_clear_host_address(&r6->network, &r6->network, r6->plen); + nm_ip6_addr_clear_host_address(&r6->src, &r6->src, r6->src_plen); break; default: nm_assert_not_reached(); @@ -5268,55 +5250,107 @@ nm_platform_ip_route_normalize(int addr_family, NMPlatformIPRoute *route) } static int -_ip_route_add(NMPlatform *self, NMPNlmFlags flags, int addr_family, gconstpointer route) +_ip_route_add(NMPlatform *self, NMPNlmFlags flags, NMPObject *obj_stack) { char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; int ifindex; _CHECK_SELF(self, klass, FALSE); - nm_assert(route); - nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); + /* The caller already ensures that this is a stack allocated copy, that + * - stays alive for the duration of the call. + * - that the ip_route_add() implementation is allowed to modify. + */ + nm_assert(obj_stack); + nm_assert(NMP_OBJECT_IS_STACKINIT(obj_stack)); + nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_stack), + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + + nm_assert(NMP_OBJECT_GET_TYPE(obj_stack) != NMP_OBJECT_TYPE_IP4_ROUTE + || obj_stack->ip4_route.n_nexthops <= 1u || obj_stack->_ip4_route.extra_nexthops); + + nm_platform_ip_route_normalize(NMP_OBJECT_GET_ADDR_FAMILY((obj_stack)), + NMP_OBJECT_CAST_IP_ROUTE(obj_stack)); + + ifindex = obj_stack->ip_route.ifindex; - ifindex = ((const NMPlatformIPRoute *) route)->ifindex; _LOG3D("route: %-10s IPv%c route: %s", _nmp_nlm_flag_to_string(flags & NMP_NLM_FLAG_FMASK), - nm_utils_addr_family_to_char(addr_family), - NM_IS_IPv4(addr_family) ? nm_platform_ip4_route_to_string(route, sbuf, sizeof(sbuf)) - : nm_platform_ip6_route_to_string(route, sbuf, sizeof(sbuf))); - - return klass->ip_route_add(self, flags, addr_family, route); + nm_utils_addr_family_to_char(NMP_OBJECT_GET_ADDR_FAMILY(obj_stack)), + nmp_object_to_string(obj_stack, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); + + /* At this point, we pass "obj_stack" to the klass->ip_route_add() implementation. + * The callee can rely on: + * - the object being normalized and validated. + * - staying fully alive until the function returns. In this case it + * is stack allocated (and the potential "extra_nexthops" array is + * guaranteed to stay alive too). + */ + return klass->ip_route_add(self, flags, obj_stack); } int -nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *route) +nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *obj) { - int addr_family; + nm_auto_nmpobj const NMPObject *obj_keep_alive = NULL; + NMPObject obj_stack; - switch (NMP_OBJECT_GET_TYPE(route)) { - case NMP_OBJECT_TYPE_IP4_ROUTE: - addr_family = AF_INET; - break; - case NMP_OBJECT_TYPE_IP6_ROUTE: - addr_family = AF_INET6; - break; - default: - g_return_val_if_reached(FALSE); + nm_assert( + NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + + nmp_object_stackinit(&obj_stack, NMP_OBJECT_GET_TYPE(obj), &obj->ip_route); + + if (NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP4_ROUTE && obj->ip4_route.n_nexthops > 1u) { + /* Ensure @obj stays alive, so we can alias extra_nexthops from the stackallocated + * @obj_stack. */ + nm_assert(obj->_ip4_route.extra_nexthops); + obj_keep_alive = nmp_object_ref(obj); + obj_stack._ip4_route.extra_nexthops = obj->_ip4_route.extra_nexthops; } - return _ip_route_add(self, flags, addr_family, NMP_OBJECT_CAST_IP_ROUTE(route)); + return _ip_route_add(self, flags, &obj_stack); } int -nm_platform_ip4_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route) +nm_platform_ip4_route_add(NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformIP4Route *route, + const NMPlatformIP4RtNextHop *extra_nexthops) { - return _ip_route_add(self, flags, AF_INET, route); + gs_free NMPlatformIP4RtNextHop *extra_nexthops_free = NULL; + NMPObject obj; + + nm_assert(route); + nm_assert(route->n_nexthops <= 1u || extra_nexthops); + + nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP4_ROUTE, (const NMPlatformObject *) route); + + if (route->n_nexthops > 1u) { + nm_assert(extra_nexthops); + /* we need to ensure that @extra_nexthops stays alive until the function returns. + * Copy the buffer. + * + * This is probably not necessary, because likely the caller will somehow ensure that + * the extra_nexthops stay alive. Still do it, because it is a very unusual case and + * likely cheap. */ + obj._ip4_route.extra_nexthops = + nm_memdup_maybe_a(500u, + extra_nexthops, + sizeof(extra_nexthops[0]) * (route->n_nexthops - 1u), + &extra_nexthops_free); + } + + return _ip_route_add(self, flags, &obj); } int nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route) { - return _ip_route_add(self, flags, AF_INET6, route); + NMPObject obj; + + nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); + return _ip_route_add(self, flags, &obj); } gboolean @@ -5364,7 +5398,7 @@ nm_platform_ip_route_get(NMPlatform *self, char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; nm_auto_nmpobj NMPObject *route = NULL; int result; - char buf[NM_UTILS_INET_ADDRSTRLEN]; + char buf[NM_INET_ADDRSTRLEN]; char buf_oif[64]; _CHECK_SELF(self, klass, FALSE); @@ -5839,37 +5873,6 @@ nm_platform_vlan_qos_mapping_to_string(const char *name, return buf; } -static const char * -_lifetime_to_string(guint32 timestamp, guint32 lifetime, gint32 now, char *buf, size_t buf_size) -{ - if (lifetime == NM_PLATFORM_LIFETIME_PERMANENT) - return "forever"; - - g_snprintf(buf, - buf_size, - "%usec", - nmp_utils_lifetime_rebase_relative_time_on_now(timestamp, lifetime, now)); - return buf; -} - -static const char * -_lifetime_summary_to_string(gint32 now, - guint32 timestamp, - guint32 preferred, - guint32 lifetime, - char *buf, - size_t buf_size) -{ - g_snprintf(buf, - buf_size, - " lifetime %d-%u[%u,%u]", - (signed) now, - (unsigned) timestamp, - (unsigned) preferred, - (unsigned) lifetime); - return buf; -} - /** * nm_platform_link_to_string: * @route: pointer to NMPlatformLink address structure @@ -5893,7 +5896,7 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len) char str_address[_NM_UTILS_HWADDR_LEN_MAX * 3]; char str_perm_address[_NM_UTILS_HWADDR_LEN_MAX * 3]; char str_broadcast[_NM_UTILS_HWADDR_LEN_MAX * 3]; - char str_inet6_token[NM_UTILS_INET_ADDRSTRLEN]; + char str_inet6_token[NM_INET_ADDRSTRLEN]; const char *str_link_type; if (!nm_utils_to_string_buffer_init_null(link, &buf, &len)) @@ -6180,7 +6183,7 @@ nm_platform_lnk_bond_to_string(const NMPlatformLnkBond *lnk, char *buf, gsize le char target[INET_ADDRSTRLEN]; nm_strbuf_append_c(&buf, &len, ' '); - nm_strbuf_append_str(&buf, &len, _nm_utils_inet4_ntop(lnk->arp_ip_target[i], target)); + nm_strbuf_append_str(&buf, &len, nm_inet4_ntop(lnk->arp_ip_target[i], target)); } } return buf; @@ -6190,18 +6193,18 @@ const char * nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len) { char str_local[30]; - char str_local1[NM_UTILS_INET_ADDRSTRLEN]; + char str_local1[NM_INET_ADDRSTRLEN]; char str_remote[30]; - char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; + char str_remote1[NM_INET_ADDRSTRLEN]; char str_ttl[30]; char str_tos[30]; char str_parent_ifindex[30]; char str_input_flags[30]; char str_output_flags[30]; char str_input_key[30]; - char str_input_key1[NM_UTILS_INET_ADDRSTRLEN]; + char str_input_key1[NM_INET_ADDRSTRLEN]; char str_output_key[30]; - char str_output_key1[NM_UTILS_INET_ADDRSTRLEN]; + char str_output_key1[NM_INET_ADDRSTRLEN]; if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) return buf; @@ -6222,13 +6225,11 @@ nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len) "%s" /* okey */ "", lnk->is_tap ? "tap" : "", - lnk->remote ? nm_sprintf_buf(str_remote, - " remote %s", - _nm_utils_inet4_ntop(lnk->remote, str_remote1)) - : "", - lnk->local - ? nm_sprintf_buf(str_local, " local %s", _nm_utils_inet4_ntop(lnk->local, str_local1)) + lnk->remote + ? nm_sprintf_buf(str_remote, " remote %s", nm_inet4_ntop(lnk->remote, str_remote1)) : "", + lnk->local ? nm_sprintf_buf(str_local, " local %s", nm_inet4_ntop(lnk->local, str_local1)) + : "", lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "", lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", @@ -6241,12 +6242,12 @@ nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len) NM_FLAGS_HAS(lnk->input_flags, GRE_KEY) || lnk->input_key ? nm_sprintf_buf(str_input_key, " ikey %s", - _nm_utils_inet4_ntop(lnk->input_key, str_input_key1)) + nm_inet4_ntop(lnk->input_key, str_input_key1)) : "", NM_FLAGS_HAS(lnk->output_flags, GRE_KEY) || lnk->output_key ? nm_sprintf_buf(str_output_key, " okey %s", - _nm_utils_inet4_ntop(lnk->output_key, str_output_key1)) + nm_inet4_ntop(lnk->output_key, str_output_key1)) : ""); return buf; } @@ -6275,9 +6276,9 @@ const char * nm_platform_lnk_ip6tnl_to_string(const NMPlatformLnkIp6Tnl *lnk, char *buf, gsize len) { char str_local[30]; - char str_local1[NM_UTILS_INET_ADDRSTRLEN]; + char str_local1[NM_INET_ADDRSTRLEN]; char str_remote[30]; - char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; + char str_remote1[NM_INET_ADDRSTRLEN]; char str_ttl[30]; char str_tclass[30]; char str_flow[30]; @@ -6294,32 +6295,32 @@ nm_platform_lnk_ip6tnl_to_string(const NMPlatformLnkIp6Tnl *lnk, char *buf, gsiz else str_type = "ip6tnl"; - g_snprintf( - buf, - len, - "%s" /* type */ - "%s" /* remote */ - "%s" /* local */ - "%s" /* parent_ifindex */ - "%s" /* ttl */ - "%s" /* tclass */ - "%s" /* encap limit */ - "%s" /* flow label */ - "%s" /* proto */ - " flags 0x%x" - "", - str_type, - nm_sprintf_buf(str_remote, " remote %s", _nm_utils_inet6_ntop(&lnk->remote, str_remote1)), - nm_sprintf_buf(str_local, " local %s", _nm_utils_inet6_ntop(&lnk->local, str_local1)), - lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) - : "", - lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", - lnk->tclass == 1 ? " tclass inherit" - : nm_sprintf_buf(str_tclass, " tclass 0x%x", lnk->tclass), - nm_sprintf_buf(str_encap, " encap-limit %u", lnk->encap_limit), - nm_sprintf_buf(str_flow, " flow-label 0x05%x", lnk->flow_label), - nm_sprintf_buf(str_proto, " proto %u", lnk->proto), - (guint) lnk->flags); + g_snprintf(buf, + len, + "%s" /* type */ + "%s" /* remote */ + "%s" /* local */ + "%s" /* parent_ifindex */ + "%s" /* ttl */ + "%s" /* tclass */ + "%s" /* encap limit */ + "%s" /* flow label */ + "%s" /* proto */ + " flags 0x%x" + "", + str_type, + nm_sprintf_buf(str_remote, " remote %s", nm_inet6_ntop(&lnk->remote, str_remote1)), + nm_sprintf_buf(str_local, " local %s", nm_inet6_ntop(&lnk->local, str_local1)), + lnk->parent_ifindex + ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) + : "", + lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", + lnk->tclass == 1 ? " tclass inherit" + : nm_sprintf_buf(str_tclass, " tclass 0x%x", lnk->tclass), + nm_sprintf_buf(str_encap, " encap-limit %u", lnk->encap_limit), + nm_sprintf_buf(str_flow, " flow-label 0x05%x", lnk->flow_label), + nm_sprintf_buf(str_proto, " proto %u", lnk->proto), + (guint) lnk->flags); return buf; } @@ -6327,9 +6328,9 @@ const char * nm_platform_lnk_ipip_to_string(const NMPlatformLnkIpIp *lnk, char *buf, gsize len) { char str_local[30]; - char str_local1[NM_UTILS_INET_ADDRSTRLEN]; + char str_local1[NM_INET_ADDRSTRLEN]; char str_remote[30]; - char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; + char str_remote1[NM_INET_ADDRSTRLEN]; char str_ttl[30]; char str_tos[30]; char str_parent_ifindex[30]; @@ -6348,13 +6349,11 @@ nm_platform_lnk_ipip_to_string(const NMPlatformLnkIpIp *lnk, char *buf, gsize le "%s" /* tos */ "%s" /* path_mtu_discovery */ "", - lnk->remote ? nm_sprintf_buf(str_remote, - " remote %s", - _nm_utils_inet4_ntop(lnk->remote, str_remote1)) - : "", - lnk->local - ? nm_sprintf_buf(str_local, " local %s", _nm_utils_inet4_ntop(lnk->local, str_local1)) + lnk->remote + ? nm_sprintf_buf(str_remote, " remote %s", nm_inet4_ntop(lnk->remote, str_remote1)) : "", + lnk->local ? nm_sprintf_buf(str_local, " local %s", nm_inet4_ntop(lnk->local, str_local1)) + : "", lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "", lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", @@ -6417,9 +6416,9 @@ const char * nm_platform_lnk_sit_to_string(const NMPlatformLnkSit *lnk, char *buf, gsize len) { char str_local[30]; - char str_local1[NM_UTILS_INET_ADDRSTRLEN]; + char str_local1[NM_INET_ADDRSTRLEN]; char str_remote[30]; - char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; + char str_remote1[NM_INET_ADDRSTRLEN]; char str_ttl[30]; char str_tos[30]; char str_flags[30]; @@ -6442,13 +6441,11 @@ nm_platform_lnk_sit_to_string(const NMPlatformLnkSit *lnk, char *buf, gsize len) "%s" /* flags */ "%s" /* proto */ "", - lnk->remote ? nm_sprintf_buf(str_remote, - " remote %s", - _nm_utils_inet4_ntop(lnk->remote, str_remote1)) - : "", - lnk->local - ? nm_sprintf_buf(str_local, " local %s", _nm_utils_inet4_ntop(lnk->local, str_local1)) + lnk->remote + ? nm_sprintf_buf(str_remote, " remote %s", nm_inet4_ntop(lnk->remote, str_remote1)) : "", + lnk->local ? nm_sprintf_buf(str_local, " local %s", nm_inet4_ntop(lnk->local, str_local1)) + : "", lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "", lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", @@ -6502,18 +6499,111 @@ const char * nm_platform_lnk_vlan_to_string(const NMPlatformLnkVlan *lnk, char *buf, gsize len) { char *b; + char protocol[32]; if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) return buf; b = buf; + switch (lnk->protocol) { + case ETH_P_8021AD: + nm_sprintf_buf(protocol, "802.1ad"); + break; + case ETH_P_8021Q: + nm_sprintf_buf(protocol, "802.1Q"); + break; + default: + nm_sprintf_buf(protocol, "0x%04hx", lnk->protocol); + break; + } + nm_strbuf_append(&b, &len, "vlan %u", lnk->id); + nm_strbuf_append(&b, &len, " protocol %s", protocol); if (lnk->flags) nm_strbuf_append(&b, &len, " flags 0x%x", lnk->flags); return buf; } +const char * +nm_platform_lnk_vti_to_string(const NMPlatformLnkVti *lnk, char *buf, gsize len) +{ + char str_local[30 + NM_INET_ADDRSTRLEN]; + char str_local1[NM_INET_ADDRSTRLEN]; + char str_remote[30 + NM_INET_ADDRSTRLEN]; + char str_remote1[NM_INET_ADDRSTRLEN]; + char str_ikey[30]; + char str_okey[30]; + char str_fwmark[30]; + char str_parent_ifindex[30]; + + if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) + return buf; + + g_snprintf( + buf, + len, + "vti" + "%s" /* remote */ + "%s" /* local */ + "%s" /* parent_ifindex */ + "%s" /* ikey */ + "%s" /* okey */ + "%s" /* fwmark */ + "", + lnk->remote + ? nm_sprintf_buf(str_remote, " remote %s", nm_inet4_ntop(lnk->remote, str_remote1)) + : "", + lnk->local ? nm_sprintf_buf(str_local, " local %s", nm_inet4_ntop(lnk->local, str_local1)) + : "", + lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) + : "", + lnk->ikey ? nm_sprintf_buf(str_ikey, " ikey %u", lnk->ikey) : "", + lnk->okey ? nm_sprintf_buf(str_okey, " okey %u", lnk->okey) : "", + lnk->fwmark ? nm_sprintf_buf(str_fwmark, " fwmark 0x%x", lnk->fwmark) : ""); + return buf; +} + +const char * +nm_platform_lnk_vti6_to_string(const NMPlatformLnkVti6 *lnk, char *buf, gsize len) +{ + char str_local[30 + NM_INET_ADDRSTRLEN]; + char str_local1[NM_INET_ADDRSTRLEN]; + char str_remote[30 + NM_INET_ADDRSTRLEN]; + char str_remote1[NM_INET_ADDRSTRLEN]; + char str_ikey[30]; + char str_okey[30]; + char str_fwmark[30]; + char str_parent_ifindex[30]; + + if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) + return buf; + + g_snprintf( + buf, + len, + "vti6" + "%s" /* remote */ + "%s" /* local */ + "%s" /* parent_ifindex */ + "%s" /* ikey */ + "%s" /* okey */ + "%s" /* fwmark */ + "", + IN6_IS_ADDR_UNSPECIFIED(&lnk->remote) + ? "" + : nm_sprintf_buf(str_remote, " remote %s", nm_inet6_ntop(&lnk->remote, str_remote1)), + IN6_IS_ADDR_UNSPECIFIED(&lnk->local) + ? "" + : nm_sprintf_buf(str_local, " local %s", nm_inet6_ntop(&lnk->local, str_local1)), + lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) + : "", + lnk->ikey ? nm_sprintf_buf(str_ikey, " ikey %u", lnk->ikey) : "", + lnk->okey ? nm_sprintf_buf(str_okey, " okey %u", lnk->okey) : "", + lnk->fwmark ? nm_sprintf_buf(str_fwmark, " fwmark 0x%x", lnk->fwmark) : ""); + return buf; +} + const char * nm_platform_lnk_vrf_to_string(const NMPlatformLnkVrf *lnk, char *buf, gsize len) { @@ -6535,13 +6625,13 @@ nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize char str_group6[100]; char str_local[100]; char str_local6[100]; - char str_dev[25]; + char str_dev[30]; char str_limit[25]; char str_src_port[35]; char str_dst_port[25]; char str_tos[25]; char str_ttl[25]; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) return buf; @@ -6553,7 +6643,7 @@ nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize sizeof(str_group), " %s %s", IN_MULTICAST(ntohl(lnk->group)) ? "group" : "remote", - _nm_utils_inet4_ntop(lnk->group, sbuf)); + nm_inet4_ntop(lnk->group, sbuf)); } if (IN6_IS_ADDR_UNSPECIFIED(&lnk->group6)) str_group6[0] = '\0'; @@ -6563,16 +6653,13 @@ nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize " %s%s %s", IN6_IS_ADDR_MULTICAST(&lnk->group6) ? "group" : "remote", str_group[0] ? "6" : "", /* usually, a vxlan has either v4 or v6 only. */ - _nm_utils_inet6_ntop(&lnk->group6, sbuf)); + nm_inet6_ntop(&lnk->group6, sbuf)); } if (lnk->local == 0) str_local[0] = '\0'; else { - g_snprintf(str_local, - sizeof(str_local), - " local %s", - _nm_utils_inet4_ntop(lnk->local, sbuf)); + g_snprintf(str_local, sizeof(str_local), " local %s", nm_inet4_ntop(lnk->local, sbuf)); } if (IN6_IS_ADDR_UNSPECIFIED(&lnk->local6)) str_local6[0] = '\0'; @@ -6581,7 +6668,7 @@ nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize sizeof(str_local6), " local%s %s", str_local[0] ? "6" : "", /* usually, a vxlan has either v4 or v6 only. */ - _nm_utils_inet6_ntop(&lnk->local6, sbuf)); + nm_inet6_ntop(&lnk->local6, sbuf)); } g_snprintf( @@ -6609,7 +6696,7 @@ nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize str_group6, str_local, str_local6, - lnk->parent_ifindex ? nm_sprintf_buf(str_dev, " dev %d", lnk->parent_ifindex) : "", + _to_string_dev(str_dev, lnk->parent_ifindex), lnk->src_port_min || lnk->src_port_max ? nm_sprintf_buf(str_src_port, " srcport %u %u", lnk->src_port_min, lnk->src_port_max) : "", @@ -6631,9 +6718,9 @@ nm_platform_wireguard_peer_to_string(const NMPWireGuardPeer *peer, char *buf, gs { char *buf0 = buf; gs_free char *public_key_b64 = NULL; - char s_sockaddr[NM_UTILS_INET_ADDRSTRLEN + 100]; + char s_sockaddr[NM_INET_ADDRSTRLEN + 100]; char s_endpoint[20 + sizeof(s_sockaddr)]; - char s_addr[NM_UTILS_INET_ADDRSTRLEN]; + char s_addr[NM_INET_ADDRSTRLEN]; char s_keepalive[100]; guint i; @@ -6677,7 +6764,7 @@ nm_platform_wireguard_peer_to_string(const NMPWireGuardPeer *peer, char *buf, gs nm_strbuf_append(&buf, &len, " %s/%u", - nm_utils_inet_ntop(allowed_ip->family, &allowed_ip->addr, s_addr), + nm_inet_ntop(allowed_ip->family, &allowed_ip->addr, s_addr), allowed_ip->mask); } @@ -6713,334 +6800,107 @@ nm_platform_lnk_wireguard_to_string(const NMPlatformLnkWireGuard *lnk, char *buf return buf; } +static NM_UTILS_FLAGS2STR_DEFINE(_rtm_flags_to_string, + unsigned, + NM_UTILS_FLAGS2STR(RTNH_F_DEAD, "dead"), + NM_UTILS_FLAGS2STR(RTNH_F_PERVASIVE, "pervasive"), + NM_UTILS_FLAGS2STR(RTNH_F_ONLINK, "onlink"), + NM_UTILS_FLAGS2STR(8 /*RTNH_F_OFFLOAD*/, "offload"), + NM_UTILS_FLAGS2STR(16 /*RTNH_F_LINKDOWN*/, "linkdown"), + NM_UTILS_FLAGS2STR(32 /*RTNH_F_UNRESOLVED*/, "unresolved"), + + NM_UTILS_FLAGS2STR(RTM_F_NOTIFY, "notify"), + NM_UTILS_FLAGS2STR(RTM_F_CLONED, "cloned"), + NM_UTILS_FLAGS2STR(RTM_F_EQUALIZE, "equalize"), + NM_UTILS_FLAGS2STR(RTM_F_PREFIX, "prefix"), + NM_UTILS_FLAGS2STR(0x1000 /*RTM_F_LOOKUP_TABLE*/, "lookup-table"), + NM_UTILS_FLAGS2STR(0x2000 /*RTM_F_FIB_MATCH*/, "fib-match"), ); + +#define _RTM_FLAGS_TO_STRING_MAXLEN 200 + +static const char * +_rtm_flags_to_string_full(char *buf, gsize buf_size, unsigned rtm_flags) +{ + const char *buf0 = buf; + + nm_assert(buf_size >= _RTM_FLAGS_TO_STRING_MAXLEN); + + if (!rtm_flags) + return ""; + + nm_strbuf_append_str(&buf, &buf_size, " rtm_flags "); + _rtm_flags_to_string(rtm_flags, buf, buf_size); + nm_assert(strlen(buf) < buf_size); + return buf0; +} + /** - * nm_platform_ip4_address_to_string: - * @route: pointer to NMPlatformIP4Address address structure + * nm_platform_ip4_route_to_string: + * @route: pointer to NMPlatformIP4Route route structure + * @extra_nexthops: (allow-none): the route might be a ECMP multihop route + * (with n_nexthops > 1). In that case, provide the list of extra hops + * to print too. It is allowed for a multihop route to omit the extra hops + * by passing NULL. * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. * - * A method for converting an address struct into a string representation. + * A method for converting a route struct into a string representation. * - * Example output: "" + * Example output: "192.168.1.0/24 via 0.0.0.0 dev em1 metric 0 mss 0" * - * Returns: a string representation of the address. + * Returns: a string representation of the route. */ const char * -nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf, gsize len) -{ - char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE]; - char s_address[INET_ADDRSTRLEN]; - char s_peer[INET_ADDRSTRLEN]; - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char str_label[32]; - char str_lft[30], str_pref[30], str_time[50], s_source[50]; - char *str_peer = NULL; - const char *str_lft_p, *str_pref_p, *str_time_p; - gint32 now = nm_utils_get_monotonic_timestamp_sec(); - in_addr_t broadcast_address; - char str_broadcast[INET_ADDRSTRLEN]; - - if (!nm_utils_to_string_buffer_init_null(address, &buf, &len)) +nm_platform_ip4_route_to_string_full(const NMPlatformIP4Route *route, + const NMPlatformIP4RtNextHop *extra_nexthops, + char *buf, + gsize len) +{ + char *buf0; + char s_network[INET_ADDRSTRLEN]; + char s_gateway[INET_ADDRSTRLEN]; + char s_pref_src[INET_ADDRSTRLEN]; + char str_dev[30]; + char str_mss[32]; + char str_table[30]; + char str_scope[30]; + char s_source[50]; + char str_tos[32]; + char str_window[32]; + char str_cwnd[32]; + char str_initcwnd[32]; + char str_initrwnd[32]; + char str_rto_min[32]; + char str_mtu[32]; + char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN]; + char str_type[30]; + char str_metric[30]; + char weight_str[20]; + guint n_nexthops; + + if (!nm_utils_to_string_buffer_init_null(route, &buf, &len)) return buf; - inet_ntop(AF_INET, &address->address, s_address, sizeof(s_address)); + buf0 = buf; - if (address->peer_address != address->address) { - inet_ntop(AF_INET, &address->peer_address, s_peer, sizeof(s_peer)); - str_peer = g_strconcat(" ptp ", s_peer, NULL); - } + n_nexthops = nm_platform_ip4_route_get_n_nexthops(route); - _to_string_dev(NULL, address->ifindex, str_dev, sizeof(str_dev)); - - if (*address->label) - g_snprintf(str_label, sizeof(str_label), " label %s", address->label); - else - str_label[0] = 0; - - str_lft_p = _lifetime_to_string(address->timestamp, - address->lifetime ?: NM_PLATFORM_LIFETIME_PERMANENT, - now, - str_lft, - sizeof(str_lft)), - str_pref_p = - (address->lifetime == address->preferred) - ? str_lft_p - : (_lifetime_to_string(address->timestamp, - address->lifetime ? MIN(address->preferred, address->lifetime) - : NM_PLATFORM_LIFETIME_PERMANENT, - now, - str_pref, - sizeof(str_pref))); - str_time_p = _lifetime_summary_to_string(now, - address->timestamp, - address->preferred, - address->lifetime, - str_time, - sizeof(str_time)); - - broadcast_address = nm_platform_ip4_broadcast_address_from_addr(address); - - g_snprintf( - buf, - len, - "%s/%d" - "%s%s" /* broadcast */ - " lft %s" - " pref %s" - "%s" /* time */ - "%s" /* peer */ - "%s" /* dev */ - "%s" /* flags */ - "%s" /* label */ - " src %s" - "%s" /* a_acd_not_ready */ - "%s" /* a_force_commit */ - "%s" /* a_no_auto_noprefixroute */ - "", - s_address, - address->plen, - broadcast_address != 0u || address->use_ip4_broadcast_address - ? (address->use_ip4_broadcast_address ? " brd " : " brd* ") - : "", - broadcast_address != 0u || address->use_ip4_broadcast_address - ? _nm_utils_inet4_ntop(broadcast_address, str_broadcast) - : "", - str_lft_p, - str_pref_p, - str_time_p, - str_peer ?: "", - str_dev, - _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)), - str_label, - nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), - address->a_acd_not_ready ? " ip4acd-not-ready" : "", - address->a_force_commit ? " force-commit" : "", - address->a_no_auto_noprefixroute ? " no-auto-noprefixroute" : ""); - g_free(str_peer); - return buf; -} - -NM_UTILS_FLAGS2STR_DEFINE(nm_platform_link_flags2str, - unsigned, - NM_UTILS_FLAGS2STR(IFF_LOOPBACK, "loopback"), - NM_UTILS_FLAGS2STR(IFF_BROADCAST, "broadcast"), - NM_UTILS_FLAGS2STR(IFF_POINTOPOINT, "pointopoint"), - NM_UTILS_FLAGS2STR(IFF_MULTICAST, "multicast"), - NM_UTILS_FLAGS2STR(IFF_NOARP, "noarp"), - NM_UTILS_FLAGS2STR(IFF_ALLMULTI, "allmulti"), - NM_UTILS_FLAGS2STR(IFF_PROMISC, "promisc"), - NM_UTILS_FLAGS2STR(IFF_MASTER, "master"), - NM_UTILS_FLAGS2STR(IFF_SLAVE, "slave"), - NM_UTILS_FLAGS2STR(IFF_DEBUG, "debug"), - NM_UTILS_FLAGS2STR(IFF_DYNAMIC, "dynamic"), - NM_UTILS_FLAGS2STR(IFF_AUTOMEDIA, "automedia"), - NM_UTILS_FLAGS2STR(IFF_PORTSEL, "portsel"), - NM_UTILS_FLAGS2STR(IFF_NOTRAILERS, "notrailers"), - NM_UTILS_FLAGS2STR(IFF_UP, "up"), - NM_UTILS_FLAGS2STR(IFF_RUNNING, "running"), - NM_UTILS_FLAGS2STR(IFF_LOWER_UP, "lowerup"), - NM_UTILS_FLAGS2STR(IFF_DORMANT, "dormant"), - NM_UTILS_FLAGS2STR(IFF_ECHO, "echo"), ); - -NM_UTILS_ENUM2STR_DEFINE(nm_platform_link_inet6_addrgenmode2str, - guint8, - NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_NONE, "none"), - NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_EUI64, "eui64"), - NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY, "stable-privacy"), - NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_RANDOM, "random"), ); - -G_STATIC_ASSERT(IFA_F_SECONDARY == IFA_F_TEMPORARY); - -NM_UTILS_FLAGS2STR_DEFINE(nm_platform_addr_flags2str, - unsigned, - NM_UTILS_FLAGS2STR(IFA_F_SECONDARY, "secondary"), - NM_UTILS_FLAGS2STR(IFA_F_NODAD, "nodad"), - NM_UTILS_FLAGS2STR(IFA_F_OPTIMISTIC, "optimistic"), - NM_UTILS_FLAGS2STR(IFA_F_DADFAILED, "dadfailed"), - NM_UTILS_FLAGS2STR(IFA_F_HOMEADDRESS, "homeaddress"), - NM_UTILS_FLAGS2STR(IFA_F_DEPRECATED, "deprecated"), - NM_UTILS_FLAGS2STR(IFA_F_TENTATIVE, "tentative"), - NM_UTILS_FLAGS2STR(IFA_F_PERMANENT, "permanent"), - NM_UTILS_FLAGS2STR(IFA_F_MANAGETEMPADDR, "mngtmpaddr"), - NM_UTILS_FLAGS2STR(IFA_F_NOPREFIXROUTE, "noprefixroute"), - NM_UTILS_FLAGS2STR(IFA_F_MCAUTOJOIN, "mcautojoin"), - NM_UTILS_FLAGS2STR(IFA_F_STABLE_PRIVACY, "stable-privacy"), ); - -NM_UTILS_ENUM2STR_DEFINE(nm_platform_route_scope2str, - int, - NM_UTILS_ENUM2STR(RT_SCOPE_NOWHERE, "nowhere"), - NM_UTILS_ENUM2STR(RT_SCOPE_HOST, "host"), - NM_UTILS_ENUM2STR(RT_SCOPE_LINK, "link"), - NM_UTILS_ENUM2STR(RT_SCOPE_SITE, "site"), - NM_UTILS_ENUM2STR(RT_SCOPE_UNIVERSE, "global"), ); - -/** - * nm_platform_ip6_address_to_string: - * @route: pointer to NMPlatformIP6Address address structure - * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. - * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. - * - * A method for converting an address struct into a string representation. - * - * Example output: "2001:db8:0:f101::1/64 lft 4294967295 pref 4294967295 time 16922666 on dev em1" - * - * Returns: a string representation of the address. - */ -const char * -nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf, gsize len) -{ - char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE]; - char s_address[INET6_ADDRSTRLEN]; - char s_peer[INET6_ADDRSTRLEN]; - char str_lft[30], str_pref[30], str_time[50], s_source[50]; - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char *str_peer = NULL; - const char *str_lft_p, *str_pref_p, *str_time_p; - gint32 now = nm_utils_get_monotonic_timestamp_sec(); - - if (!nm_utils_to_string_buffer_init_null(address, &buf, &len)) - return buf; - - inet_ntop(AF_INET6, &address->address, s_address, sizeof(s_address)); - - if (!IN6_IS_ADDR_UNSPECIFIED(&address->peer_address)) { - inet_ntop(AF_INET6, &address->peer_address, s_peer, sizeof(s_peer)); - str_peer = g_strconcat(" ptp ", s_peer, NULL); - } - - _to_string_dev(NULL, address->ifindex, str_dev, sizeof(str_dev)); - - str_lft_p = _lifetime_to_string(address->timestamp, - address->lifetime ?: NM_PLATFORM_LIFETIME_PERMANENT, - now, - str_lft, - sizeof(str_lft)), - str_pref_p = - (address->lifetime == address->preferred) - ? str_lft_p - : (_lifetime_to_string(address->timestamp, - address->lifetime ? MIN(address->preferred, address->lifetime) - : NM_PLATFORM_LIFETIME_PERMANENT, - now, - str_pref, - sizeof(str_pref))); - str_time_p = _lifetime_summary_to_string(now, - address->timestamp, - address->preferred, - address->lifetime, - str_time, - sizeof(str_time)); - - g_snprintf( - buf, - len, - "%s/%d lft %s pref %s%s%s%s%s src %s" - "%s" /* a_force_commit */ - "%s" /* a_no_auto_noprefixroute */ - "", - s_address, - address->plen, - str_lft_p, - str_pref_p, - str_time_p, - str_peer ?: "", - str_dev, - _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)), - nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), - address->a_force_commit ? " force-commit" : "", - address->a_no_auto_noprefixroute ? " no-auto-noprefixroute" : ""); - g_free(str_peer); - return buf; -} - -static NM_UTILS_FLAGS2STR_DEFINE(_rtm_flags_to_string, - unsigned, - NM_UTILS_FLAGS2STR(RTNH_F_DEAD, "dead"), - NM_UTILS_FLAGS2STR(RTNH_F_PERVASIVE, "pervasive"), - NM_UTILS_FLAGS2STR(RTNH_F_ONLINK, "onlink"), - NM_UTILS_FLAGS2STR(8 /*RTNH_F_OFFLOAD*/, "offload"), - NM_UTILS_FLAGS2STR(16 /*RTNH_F_LINKDOWN*/, "linkdown"), - NM_UTILS_FLAGS2STR(32 /*RTNH_F_UNRESOLVED*/, "unresolved"), - - NM_UTILS_FLAGS2STR(RTM_F_NOTIFY, "notify"), - NM_UTILS_FLAGS2STR(RTM_F_CLONED, "cloned"), - NM_UTILS_FLAGS2STR(RTM_F_EQUALIZE, "equalize"), - NM_UTILS_FLAGS2STR(RTM_F_PREFIX, "prefix"), - NM_UTILS_FLAGS2STR(0x1000 /*RTM_F_LOOKUP_TABLE*/, "lookup-table"), - NM_UTILS_FLAGS2STR(0x2000 /*RTM_F_FIB_MATCH*/, "fib-match"), ); - -#define _RTM_FLAGS_TO_STRING_MAXLEN 200 - -static const char * -_rtm_flags_to_string_full(char *buf, gsize buf_size, unsigned rtm_flags) -{ - const char *buf0 = buf; - - nm_assert(buf_size >= _RTM_FLAGS_TO_STRING_MAXLEN); - - if (!rtm_flags) - return ""; - - nm_strbuf_append_str(&buf, &buf_size, " rtm_flags "); - _rtm_flags_to_string(rtm_flags, buf, buf_size); - nm_assert(strlen(buf) < buf_size); - return buf0; -} - -/** - * nm_platform_ip4_route_to_string: - * @route: pointer to NMPlatformIP4Route route structure - * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. - * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. - * - * A method for converting a route struct into a string representation. - * - * Example output: "192.168.1.0/24 via 0.0.0.0 dev em1 metric 0 mss 0" - * - * Returns: a string representation of the route. - */ -const char * -nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len) -{ - char s_network[INET_ADDRSTRLEN]; - char s_gateway[INET_ADDRSTRLEN]; - char s_pref_src[INET_ADDRSTRLEN]; - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char str_mss[32]; - char str_table[30]; - char str_scope[30]; - char s_source[50]; - char str_tos[32]; - char str_window[32]; - char str_cwnd[32]; - char str_initcwnd[32]; - char str_initrwnd[32]; - char str_rto_min[32]; - char str_mtu[32]; - char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN]; - char str_type[30]; - char str_metric[30]; - - if (!nm_utils_to_string_buffer_init_null(route, &buf, &len)) - return buf; - - inet_ntop(AF_INET, &route->network, s_network, sizeof(s_network)); + inet_ntop(AF_INET, &route->network, s_network, sizeof(s_network)); if (route->gateway == 0) s_gateway[0] = '\0'; else inet_ntop(AF_INET, &route->gateway, s_gateway, sizeof(s_gateway)); - _to_string_dev(NULL, route->ifindex, str_dev, sizeof(str_dev)); - - g_snprintf( - buf, - len, + nm_strbuf_append( + &buf, + &len, "type %s " /* type */ "%s" /* table */ "%s/%d" "%s%s" /* gateway */ - "%s" + "%s%s" /* weight */ + "%s" /* dev/ifindex */ " metric %s" "%s" /* mss */ " rt-src %s" /* protocol */ @@ -7068,9 +6928,13 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz : ""), s_network, route->plen, - s_gateway[0] ? " via " : "", - s_gateway, - str_dev, + n_nexthops <= 1 && s_gateway[0] ? " via " : "", + n_nexthops <= 1 ? s_gateway : "", + NM_PRINT_FMT_QUOTED2(n_nexthops <= 1 && route->weight != 0, + " weight ", + nm_sprintf_buf(weight_str, "%u", route->weight), + ""), + n_nexthops <= 1 ? _to_string_dev(str_dev, route->ifindex) : "", route->metric_any ? (route->metric ? nm_sprintf_buf(str_metric, "??+%u", route->metric) : "??") : nm_sprintf_buf(str_metric, "%u", route->metric), @@ -7120,7 +6984,50 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz route->mtu) : "", route->r_force_commit ? " force-commit" : ""); - return buf; + + if ((n_nexthops == 1 && route->ifindex > 0) || n_nexthops == 0) { + /* A plain single hop route. Nothing extra to remark. */ + } else { + nm_strbuf_append(&buf, &len, " n_nexthops %u", n_nexthops); + if (n_nexthops > 1) { + nm_strbuf_append(&buf, + &len, + " nexthop" + "%s%s" /* gateway */ + " weight %s" /* weight */ + "%s" /* dev/ifindex */ + "", + s_gateway[0] ? " via " : "", + s_gateway, + nm_sprintf_buf(weight_str, "%u", route->weight), + _to_string_dev(str_dev, route->ifindex)); + if (!extra_nexthops) + nm_strbuf_append_str(&buf, &len, " nexthops [...]"); + else { + guint i; + + for (i = 1; i < n_nexthops; i++) { + const NMPlatformIP4RtNextHop *nexthop = &extra_nexthops[i - 1]; + + nm_strbuf_append( + &buf, + &len, + " nexthop" + "%s" /* ifindex */ + "%s%s" /* gateway */ + " weight %s" /* weight */ + "", + NM_PRINT_FMT_QUOTED2(nexthop->gateway != 0 || nexthop->ifindex <= 0, + " via ", + nm_inet4_ntop(nexthop->gateway, s_gateway), + ""), + _to_string_dev(str_dev, nexthop->ifindex), + nm_sprintf_buf(weight_str, "%u", nexthop->weight)); + } + } + } + } + return buf0; } /** @@ -7147,7 +7054,7 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz char str_table[30]; char str_pref[40]; char str_pref2[30]; - char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_dev[30]; char str_mss[32]; char s_source[50]; char str_window[32]; @@ -7174,8 +7081,6 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz else inet_ntop(AF_INET6, &route->pref_src, s_pref_src, sizeof(s_pref_src)); - _to_string_dev(NULL, route->ifindex, str_dev, sizeof(str_dev)); - g_snprintf( buf, len, @@ -7213,7 +7118,7 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz route->plen, s_gateway[0] ? " via " : "", s_gateway, - str_dev, + _to_string_dev(str_dev, route->ifindex), route->metric_any ? (route->metric ? nm_sprintf_buf(str_metric, "??+%u", route->metric) : "??") : nm_sprintf_buf(str_metric, "%u", route->metric), @@ -7225,7 +7130,7 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz route->src_plen || !IN6_IS_ADDR_UNSPECIFIED(&route->src) ? nm_sprintf_buf(s_src_all, " src %s/%u", - _nm_utils_inet6_ntop(&route->src, s_src), + nm_inet6_ntop(&route->src, s_src), (unsigned) route->src_plen) : "", _rtm_flags_to_string_full(str_rtm_flags, sizeof(str_rtm_flags), route->r_rtm_flags), @@ -7279,7 +7184,7 @@ _routing_rule_addr_to_string(char **buf, guint8 plen, gboolean is_src) { - char s_addr[NM_UTILS_INET_ADDRSTRLEN]; + char s_addr[NM_INET_ADDRSTRLEN]; gboolean is_zero; gsize addr_size; @@ -7300,7 +7205,7 @@ _routing_rule_addr_to_string(char **buf, nm_strbuf_append_str(buf, len, is_src ? " from " : " to "); - nm_strbuf_append_str(buf, len, nm_utils_inet_ntop(addr_family, addr, s_addr)); + nm_strbuf_append_str(buf, len, nm_inet_ntop(addr_family, addr, s_addr)); if (plen != (addr_size * 8)) nm_strbuf_append(buf, len, "/%u", plen); @@ -7495,7 +7400,7 @@ nm_platform_routing_rule_to_string(const NMPlatformRoutingRule *routing_rule, ch const char * nm_platform_qdisc_to_string(const NMPlatformQdisc *qdisc, char *buf, gsize len) { - char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_dev[30]; const char *buf0; if (!nm_utils_to_string_buffer_init_null(qdisc, &buf, &len)) @@ -7507,7 +7412,7 @@ nm_platform_qdisc_to_string(const NMPlatformQdisc *qdisc, char *buf, gsize len) &len, "%s%s family %u handle %x parent %x info %x", qdisc->kind, - _to_string_dev(NULL, qdisc->ifindex, str_dev, sizeof(str_dev)), + _to_string_dev(str_dev, qdisc->ifindex), qdisc->addr_family, qdisc->handle, qdisc->parent, @@ -7584,9 +7489,7 @@ nm_platform_qdisc_hash_update(const NMPlatformQdisc *obj, NMHashState *h) } int -nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a, - const NMPlatformQdisc *b, - gboolean compare_handle) +nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b, gboolean compare_handle) { NM_CMP_SELF(a, b); NM_CMP_FIELD(a, b, ifindex); @@ -7623,16 +7526,10 @@ nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a, return 0; } -int -nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b) -{ - return nm_platform_qdisc_cmp_full(a, b, TRUE); -} - const char * nm_platform_tfilter_to_string(const NMPlatformTfilter *tfilter, char *buf, gsize len) { - char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_dev[30]; char act_buf[300]; char *p; gsize l; @@ -7673,7 +7570,7 @@ nm_platform_tfilter_to_string(const NMPlatformTfilter *tfilter, char *buf, gsize len, "%s%s family %u handle %x parent %x info %x%s", tfilter->kind, - _to_string_dev(NULL, tfilter->ifindex, str_dev, sizeof(str_dev)), + _to_string_dev(str_dev, tfilter->ifindex), tfilter->addr_family, tfilter->handle, tfilter->parent, @@ -7741,7 +7638,7 @@ static NM_UTILS_FLAGS2STR_DEFINE(_mptcp_flags_to_string, const char * nm_platform_mptcp_addr_to_string(const NMPlatformMptcpAddr *mptcp_addr, char *buf, gsize len) { - char str_addr[30 + NM_UTILS_INET_ADDRSTRLEN]; + char str_addr[30 + NM_INET_ADDRSTRLEN]; char str_port[30]; char str_id[30]; char str_flags[200]; @@ -7754,7 +7651,7 @@ nm_platform_mptcp_addr_to_string(const NMPlatformMptcpAddr *mptcp_addr, char *bu if (mptcp_addr->addr_family == 0) nm_sprintf_buf(str_addr, "no-addr"); else if (NM_IN_SET(mptcp_addr->addr_family, AF_INET, AF_INET6)) - nm_utils_inet_ntop(mptcp_addr->addr_family, &mptcp_addr->addr, str_addr); + nm_inet_ntop(mptcp_addr->addr_family, &mptcp_addr->addr, str_addr); else nm_sprintf_buf(str_addr, "af %d", mptcp_addr->addr_family); @@ -8363,7 +8260,7 @@ nm_platform_lnk_tun_cmp(const NMPlatformLnkTun *a, const NMPlatformLnkTun *b) void nm_platform_lnk_vlan_hash_update(const NMPlatformLnkVlan *obj, NMHashState *h) { - nm_hash_update_vals(h, obj->id, obj->flags); + nm_hash_update_vals(h, obj->id, obj->protocol, obj->flags); } int @@ -8371,6 +8268,7 @@ nm_platform_lnk_vlan_cmp(const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b) { NM_CMP_SELF(a, b); NM_CMP_FIELD(a, b, id); + NM_CMP_FIELD(a, b, protocol); NM_CMP_FIELD(a, b, flags); return 0; } @@ -8389,6 +8287,56 @@ nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b) return 0; } +void +nm_platform_lnk_vti_hash_update(const NMPlatformLnkVti *obj, NMHashState *h) +{ + nm_hash_update_vals(h, + obj->local, + obj->remote, + obj->parent_ifindex, + obj->ikey, + obj->okey, + obj->fwmark); +} + +int +nm_platform_lnk_vti_cmp(const NMPlatformLnkVti *a, const NMPlatformLnkVti *b) +{ + NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, parent_ifindex); + NM_CMP_FIELD(a, b, local); + NM_CMP_FIELD(a, b, remote); + NM_CMP_FIELD(a, b, ikey); + NM_CMP_FIELD(a, b, okey); + NM_CMP_FIELD(a, b, fwmark); + return 0; +} + +void +nm_platform_lnk_vti6_hash_update(const NMPlatformLnkVti6 *obj, NMHashState *h) +{ + nm_hash_update_vals(h, + obj->local, + obj->remote, + obj->parent_ifindex, + obj->ikey, + obj->okey, + obj->fwmark); +} + +int +nm_platform_lnk_vti6_cmp(const NMPlatformLnkVti6 *a, const NMPlatformLnkVti6 *b) +{ + NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, parent_ifindex); + NM_CMP_FIELD_MEMCMP(a, b, local); + NM_CMP_FIELD_MEMCMP(a, b, remote); + NM_CMP_FIELD(a, b, ikey); + NM_CMP_FIELD(a, b, okey); + NM_CMP_FIELD(a, b, fwmark); + return 0; +} + void nm_platform_lnk_vxlan_hash_update(const NMPlatformLnkVxlan *obj, NMHashState *h) { @@ -8458,275 +8406,18 @@ nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformL return 0; } -static int -_address_pretty_sort_get_prio_4(in_addr_t addr) -{ - if (nm_utils_ip4_address_is_link_local(addr)) - return 0; - return 1; -} - -int -nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, - const NMPlatformIP4Address *a2) -{ - in_addr_t n1; - in_addr_t n2; - - nm_assert(a1); - nm_assert(a2); - - /* Sort by address type. For example link local will - * be sorted *after* a global address. */ - NM_CMP_DIRECT(_address_pretty_sort_get_prio_4(a2->address), - _address_pretty_sort_get_prio_4(a1->address)); - - /* Sort the addresses based on their source. */ - NM_CMP_DIRECT(a2->addr_source, a1->addr_source); - - NM_CMP_DIRECT((a2->label[0] == '\0'), (a1->label[0] == '\0')); - - /* Finally, sort addresses lexically. We compare only the - * network part so that the order of addresses in the same - * subnet (and thus also the primary/secondary role) is - * preserved. - */ - n1 = nm_utils_ip4_address_clear_host_address(a1->address, a1->plen); - n2 = nm_utils_ip4_address_clear_host_address(a2->address, a2->plen); - NM_CMP_DIRECT_MEMCMP(&n1, &n2, sizeof(guint32)); - return 0; -} - -static int -_address_pretty_sort_get_prio_6(const struct in6_addr *addr) -{ - if (IN6_IS_ADDR_V4MAPPED(addr)) - return 0; - if (IN6_IS_ADDR_V4COMPAT(addr)) - return 1; - if (IN6_IS_ADDR_UNSPECIFIED(addr)) - return 2; - if (IN6_IS_ADDR_LOOPBACK(addr)) - return 3; - if (IN6_IS_ADDR_LINKLOCAL(addr)) - return 4; - if (IN6_IS_ADDR_SITELOCAL(addr)) - return 5; - return 6; -} - -static int -_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b) -{ - guint32 lifetime_a; - guint32 lifetime_b; - guint32 preferred_a; - guint32 preferred_b; - gint32 now = 0; - - lifetime_a = - nmp_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, &now, &preferred_a); - lifetime_b = - nmp_utils_lifetime_get(b->timestamp, b->lifetime, b->preferred, &now, &preferred_b); - - NM_CMP_DIRECT(lifetime_a, lifetime_b); - NM_CMP_DIRECT(preferred_a, preferred_b); - return 0; -} - -int -nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1, - const NMPlatformIP6Address *a2, - gboolean prefer_temp) -{ - gboolean ipv6_privacy1; - gboolean ipv6_privacy2; - - nm_assert(a1); - nm_assert(a2); - - /* tentative addresses are always sorted back... */ - /* sort tentative addresses after non-tentative. */ - NM_CMP_DIRECT(NM_FLAGS_HAS(a1->n_ifa_flags, IFA_F_TENTATIVE), - NM_FLAGS_HAS(a2->n_ifa_flags, IFA_F_TENTATIVE)); - - /* Sort by address type. For example link local will - * be sorted *after* site local or global. */ - NM_CMP_DIRECT(_address_pretty_sort_get_prio_6(&a2->address), - _address_pretty_sort_get_prio_6(&a1->address)); - - ipv6_privacy1 = NM_FLAGS_ANY(a1->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_SECONDARY); - ipv6_privacy2 = NM_FLAGS_ANY(a2->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_SECONDARY); - if (ipv6_privacy1 || ipv6_privacy2) { - gboolean public1 = TRUE; - gboolean public2 = TRUE; - - if (ipv6_privacy1) { - if (a1->n_ifa_flags & IFA_F_SECONDARY) - public1 = prefer_temp; - else - public1 = !prefer_temp; - } - if (ipv6_privacy2) { - if (a2->n_ifa_flags & IFA_F_SECONDARY) - public2 = prefer_temp; - else - public2 = !prefer_temp; - } - - NM_CMP_DIRECT(public2, public1); - } - - /* Sort the addresses based on their source. */ - NM_CMP_DIRECT(a2->addr_source, a1->addr_source); - - /* sort permanent addresses before non-permanent. */ - NM_CMP_DIRECT(NM_FLAGS_HAS(a2->n_ifa_flags, IFA_F_PERMANENT), - NM_FLAGS_HAS(a1->n_ifa_flags, IFA_F_PERMANENT)); - - /* finally sort addresses lexically */ - NM_CMP_DIRECT_IN6ADDR(&a1->address, &a2->address); - NM_CMP_DIRECT_MEMCMP(a1, a2, sizeof(*a1)); - return 0; -} - void -nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h) -{ - nm_hash_update_vals(h, - obj->ifindex, - obj->addr_source, - obj->use_ip4_broadcast_address ? obj->broadcast_address : ((in_addr_t) 0u), - obj->timestamp, - obj->lifetime, - obj->preferred, - obj->n_ifa_flags, - obj->plen, - obj->address, - obj->peer_address, - NM_HASH_COMBINE_BOOLS(guint8, - obj->use_ip4_broadcast_address, - obj->a_acd_not_ready, - obj->a_force_commit, - obj->a_no_auto_noprefixroute)); - nm_hash_update_strarr(h, obj->label); -} - -int -nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, - const NMPlatformIP4Address *b, - NMPlatformIPAddressCmpType cmp_type) +nm_platform_ip4_rt_nexthop_hash_update(const NMPlatformIP4RtNextHop *obj, + gboolean for_id, + NMHashState *h) { - NM_CMP_SELF(a, b); - - NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD(a, b, plen); - NM_CMP_FIELD(a, b, address); - - switch (cmp_type) { - case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID: - /* for IPv4 addresses, you can add the same local address with differing peer-address - * (IFA_ADDRESS), provided that their net-part differs. */ - NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a->peer_address, b->peer_address, a->plen); - return 0; - case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY: - case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL: - NM_CMP_FIELD(a, b, peer_address); - NM_CMP_FIELD_STR(a, b, label); - if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) { - NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a, - (const NMPlatformIPAddress *) b)); - - /* Most flags are set by kernel. We only compare the ones that - * NetworkManager actively sets. - * - * NM actively only sets IFA_F_NOPREFIXROUTE (and IFA_F_MANAGETEMPADDR for IPv6), - * where nm_platform_ip_address_sync() always sets IFA_F_NOPREFIXROUTE. - * There are thus no flags to compare for IPv4. */ - - NM_CMP_DIRECT(nm_platform_ip4_broadcast_address_from_addr(a), - nm_platform_ip4_broadcast_address_from_addr(b)); - } else { - NM_CMP_FIELD(a, b, timestamp); - NM_CMP_FIELD(a, b, lifetime); - NM_CMP_FIELD(a, b, preferred); - NM_CMP_FIELD(a, b, n_ifa_flags); - NM_CMP_FIELD(a, b, addr_source); - NM_CMP_FIELD_UNSAFE(a, b, use_ip4_broadcast_address); - if (a->use_ip4_broadcast_address) - NM_CMP_FIELD(a, b, broadcast_address); - NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready); - NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); - NM_CMP_FIELD_UNSAFE(a, b, a_no_auto_noprefixroute); - } - return 0; - } - return nm_assert_unreachable_val(0); -} + guint8 w; -void -nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h) -{ - nm_hash_update_vals( - h, - obj->ifindex, - obj->addr_source, - obj->timestamp, - obj->lifetime, - obj->preferred, - obj->n_ifa_flags, - obj->plen, - obj->address, - obj->peer_address, - NM_HASH_COMBINE_BOOLS(guint8, obj->a_force_commit, obj->a_no_auto_noprefixroute)); -} - -int -nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, - const NMPlatformIP6Address *b, - NMPlatformIPAddressCmpType cmp_type) -{ - const struct in6_addr *p_a, *p_b; - - NM_CMP_SELF(a, b); + nm_assert(obj); - NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD_IN6ADDR(a, b, address); + w = for_id ? NM_MAX(obj->weight, 1u) : obj->weight; - switch (cmp_type) { - case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID: - /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ - return 0; - case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY: - case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL: - NM_CMP_FIELD(a, b, plen); - p_a = nm_platform_ip6_address_get_peer(a); - p_b = nm_platform_ip6_address_get_peer(b); - NM_CMP_DIRECT_MEMCMP(p_a, p_b, sizeof(*p_a)); - if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) { - NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a, - (const NMPlatformIPAddress *) b)); - - /* Most flags are set by kernel. We only compare the ones that - * NetworkManager actively sets. - * - * NM actively only sets IFA_F_NOPREFIXROUTE and IFA_F_MANAGETEMPADDR, - * where nm_platform_ip_address_sync() always sets IFA_F_NOPREFIXROUTE. - * We thus only care about IFA_F_MANAGETEMPADDR. */ - NM_CMP_DIRECT(a->n_ifa_flags & IFA_F_MANAGETEMPADDR, - b->n_ifa_flags & IFA_F_MANAGETEMPADDR); - } else { - NM_CMP_FIELD(a, b, timestamp); - NM_CMP_FIELD(a, b, lifetime); - NM_CMP_FIELD(a, b, preferred); - NM_CMP_FIELD(a, b, n_ifa_flags); - NM_CMP_FIELD(a, b, addr_source); - NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); - NM_CMP_FIELD_UNSAFE(a, b, a_no_auto_noprefixroute); - } - return 0; - } - return nm_assert_unreachable_val(0); + nm_hash_update_vals(h, obj->ifindex, obj->gateway, w); } void @@ -8736,48 +8427,48 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, { switch (cmp_type) { case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: - nm_hash_update_vals( - h, - nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - nm_utils_ip4_address_clear_host_address(obj->network, obj->plen), - obj->plen, - obj->metric, - obj->tos, - NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any)); - break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: nm_hash_update_vals( h, - obj->type_coerced, nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - nm_utils_ip4_address_clear_host_address(obj->network, obj->plen), + nm_ip4_addr_clear_host_address(obj->network, obj->plen), obj->plen, obj->metric, obj->tos, - /* on top of WEAK_ID: */ - obj->ifindex, - nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), - _ip_route_scope_inv_get_normalized(obj), - obj->gateway, - obj->mss, - obj->pref_src, - obj->window, - obj->cwnd, - obj->initcwnd, - obj->initrwnd, - obj->mtu, - obj->rto_min, - obj->r_rtm_flags & RTNH_F_ONLINK, - NM_HASH_COMBINE_BOOLS(guint16, - obj->metric_any, - obj->table_any, - obj->quickack, - obj->lock_window, - obj->lock_cwnd, - obj->lock_initcwnd, - obj->lock_initrwnd, - obj->lock_mtu, - obj->lock_mss)); + NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any)); + if (NM_IN_SET(cmp_type, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID)) { + nm_hash_update_vals(h, + obj->type_coerced, + nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), + _ip_route_scope_inv_get_normalized(obj), + obj->mss, + obj->pref_src, + obj->window, + obj->cwnd, + obj->initcwnd, + obj->initrwnd, + obj->mtu, + obj->rto_min, + obj->r_rtm_flags & RTNH_F_ONLINK, + NM_HASH_COMBINE_BOOLS(guint16, + obj->quickack, + obj->lock_window, + obj->lock_cwnd, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu, + obj->lock_mss)); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { + nm_hash_update_vals(h, + obj->ifindex, + nm_platform_ip4_route_get_n_nexthops(obj), + obj->gateway, + (guint8) MAX(obj->weight, 1u)); + } + } break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: nm_hash_update_vals( @@ -8785,10 +8476,12 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->type_coerced, nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), obj->ifindex, - nm_utils_ip4_address_clear_host_address(obj->network, obj->plen), + nm_ip4_addr_clear_host_address(obj->network, obj->plen), obj->plen, obj->metric, + nm_platform_ip4_route_get_n_nexthops(obj), obj->gateway, + (guint8) MAX(obj->weight, 1u), nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), _ip_route_scope_inv_get_normalized(obj), obj->tos, @@ -8821,6 +8514,8 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->plen, obj->metric, obj->gateway, + obj->n_nexthops, + obj->weight, obj->rt_source, obj->scope_inv, obj->tos, @@ -8848,6 +8543,30 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, } } +int +nm_platform_ip4_rt_nexthop_cmp(const NMPlatformIP4RtNextHop *a, + const NMPlatformIP4RtNextHop *b, + gboolean for_id) +{ + guint8 w_a; + guint8 w_b; + + /* Note that weight zero is not valid (in kernel). We thus treat + * weight zero usually the same as 1. + * + * Not here for cmp/hash_update functions. These functions check for the exact + * bit-pattern, and not the it means at other places. */ + NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, ifindex); + NM_CMP_FIELD(a, b, gateway); + + w_a = for_id ? NM_MAX(a->weight, 1u) : a->weight; + w_b = for_id ? NM_MAX(b->weight, 1u) : b->weight; + NM_CMP_DIRECT(w_a, w_b); + + return 0; +} + int nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, @@ -8855,24 +8574,25 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, { NM_CMP_SELF(a, b); switch (cmp_type) { + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: NM_CMP_FIELD_UNSAFE(a, b, table_any); NM_CMP_DIRECT(nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(a)), nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(b))); - NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a->network, b->network, MIN(a->plen, b->plen)); + NM_CMP_DIRECT_IP4_ADDR_SAME_PREFIX(a->network, b->network, MIN(a->plen, b->plen)); NM_CMP_FIELD(a, b, plen); NM_CMP_FIELD_UNSAFE(a, b, metric_any); NM_CMP_FIELD(a, b, metric); NM_CMP_FIELD(a, b, tos); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { - NM_CMP_FIELD(a, b, ifindex); + if (NM_IN_SET(cmp_type, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID)) { NM_CMP_FIELD(a, b, type_coerced); NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->rt_source), nmp_utils_ip_config_source_round_trip_rtprot(b->rt_source)); NM_CMP_DIRECT(_ip_route_scope_inv_get_normalized(a), _ip_route_scope_inv_get_normalized(b)); - NM_CMP_FIELD(a, b, gateway); NM_CMP_FIELD(a, b, mss); NM_CMP_FIELD(a, b, pref_src); NM_CMP_FIELD(a, b, window); @@ -8881,7 +8601,13 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, NM_CMP_FIELD(a, b, initrwnd); NM_CMP_FIELD(a, b, mtu); NM_CMP_FIELD(a, b, rto_min); + + /* Note that for NetworkManager, the onlink flag is only part of the entire route. + * For kernel, each next hop has it's own onlink flag (rtnh_flags). This means, + * we can only merge ECMP routes, if they agree with their onlink flag, and then + * all next hops are onlink (or not). */ NM_CMP_DIRECT(a->r_rtm_flags & RTNH_F_ONLINK, b->r_rtm_flags & RTNH_F_ONLINK); + NM_CMP_FIELD_UNSAFE(a, b, quickack); NM_CMP_FIELD_UNSAFE(a, b, lock_window); NM_CMP_FIELD_UNSAFE(a, b, lock_cwnd); @@ -8889,6 +8615,13 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, NM_CMP_FIELD_UNSAFE(a, b, lock_initrwnd); NM_CMP_FIELD_UNSAFE(a, b, lock_mtu); NM_CMP_FIELD_UNSAFE(a, b, lock_mss); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { + NM_CMP_FIELD(a, b, ifindex); + NM_CMP_FIELD(a, b, gateway); + NM_CMP_DIRECT(NM_MAX(a->weight, 1u), NM_MAX(b->weight, 1u)); + NM_CMP_DIRECT(nm_platform_ip4_route_get_n_nexthops(a), + nm_platform_ip4_route_get_n_nexthops(b)); + } } break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: @@ -8902,13 +8635,22 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, NM_CMP_FIELD(a, b, table_coerced); NM_CMP_FIELD(a, b, ifindex); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) - NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a->network, b->network, MIN(a->plen, b->plen)); + NM_CMP_DIRECT_IP4_ADDR_SAME_PREFIX(a->network, b->network, MIN(a->plen, b->plen)); else NM_CMP_FIELD(a, b, network); NM_CMP_FIELD(a, b, plen); NM_CMP_FIELD_UNSAFE(a, b, metric_any); NM_CMP_FIELD(a, b, metric); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { + NM_CMP_DIRECT(nm_platform_ip4_route_get_n_nexthops(a), + nm_platform_ip4_route_get_n_nexthops(b)); + } else + NM_CMP_FIELD(a, b, n_nexthops); NM_CMP_FIELD(a, b, gateway); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + NM_CMP_DIRECT(NM_MAX(a->weight, 1u), NM_MAX(b->weight, 1u)); + else + NM_CMP_FIELD(a, b, weight); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->rt_source), nmp_utils_ip_config_source_round_trip_rtprot(b->rt_source)); @@ -8958,22 +8700,25 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, nm_hash_update_vals( h, nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - *nm_utils_ip6_address_clear_host_address(&a1, &obj->network, obj->plen), + *nm_ip6_addr_clear_host_address(&a1, &obj->network, obj->plen), obj->plen, obj->metric, - *nm_utils_ip6_address_clear_host_address(&a2, &obj->src, obj->src_plen), + *nm_ip6_addr_clear_host_address(&a2, &obj->src, obj->src_plen), obj->src_plen, NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any)); break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID: + nm_assert_not_reached(); + /* fall-through */ case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: nm_hash_update_vals( h, obj->type_coerced, nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - *nm_utils_ip6_address_clear_host_address(&a1, &obj->network, obj->plen), + *nm_ip6_addr_clear_host_address(&a1, &obj->network, obj->plen), obj->plen, obj->metric, - *nm_utils_ip6_address_clear_host_address(&a2, &obj->src, obj->src_plen), + *nm_ip6_addr_clear_host_address(&a2, &obj->src, obj->src_plen), obj->src_plen, NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any), /* on top of WEAK_ID: */ @@ -8986,12 +8731,12 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, obj->type_coerced, nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), obj->ifindex, - *nm_utils_ip6_address_clear_host_address(&a1, &obj->network, obj->plen), + *nm_ip6_addr_clear_host_address(&a1, &obj->network, obj->plen), obj->plen, obj->metric, obj->gateway, obj->pref_src, - *nm_utils_ip6_address_clear_host_address(&a2, &obj->src, obj->src_plen), + *nm_ip6_addr_clear_host_address(&a2, &obj->src, obj->src_plen), obj->src_plen, nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), obj->mss, @@ -9057,16 +8802,19 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, { NM_CMP_SELF(a, b); switch (cmp_type) { + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID: + nm_assert_not_reached(); + /* fall-through */ case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: NM_CMP_FIELD_UNSAFE(a, b, table_any); NM_CMP_DIRECT(nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(a)), nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(b))); - NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(&a->network, &b->network, MIN(a->plen, b->plen)); + NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(&a->network, &b->network, MIN(a->plen, b->plen)); NM_CMP_FIELD(a, b, plen); NM_CMP_FIELD_UNSAFE(a, b, metric_any); NM_CMP_FIELD(a, b, metric); - NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(&a->src, &b->src, MIN(a->src_plen, b->src_plen)); + NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(&a->src, &b->src, MIN(a->src_plen, b->src_plen)); NM_CMP_FIELD(a, b, src_plen); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { NM_CMP_FIELD(a, b, ifindex); @@ -9085,7 +8833,7 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, NM_CMP_FIELD(a, b, table_coerced); NM_CMP_FIELD(a, b, ifindex); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) - NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(&a->network, &b->network, MIN(a->plen, b->plen)); + NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(&a->network, &b->network, MIN(a->plen, b->plen)); else NM_CMP_FIELD_IN6ADDR(a, b, network); NM_CMP_FIELD(a, b, plen); @@ -9094,7 +8842,7 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, NM_CMP_FIELD_IN6ADDR(a, b, gateway); NM_CMP_FIELD_IN6ADDR(a, b, pref_src); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { - NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(&a->src, &b->src, MIN(a->src_plen, b->src_plen)); + NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(&a->src, &b->src, MIN(a->src_plen, b->src_plen)); NM_CMP_FIELD(a, b, src_plen); NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->rt_source), nmp_utils_ip_config_source_round_trip_rtprot(b->rt_source)); @@ -9463,9 +9211,9 @@ nm_platform_ip4_address_generate_device_route(const NMPlatformIP4Address *addr, if (addr->plen == 0) return NULL; - network_4 = nm_utils_ip4_address_clear_host_address(addr->peer_address, addr->plen); + network_4 = nm_ip4_addr_clear_host_address(addr->peer_address, addr->plen); - if (nm_utils_ip4_address_is_zeronet(network_4)) { + if (nm_ip4_addr_is_zeronet(network_4)) { /* Kernel doesn't add device-routes for destinations that * start with 0.x.y.z. Skip them. */ return NULL; @@ -9570,7 +9318,10 @@ log_ip4_route(NMPlatform *self, _LOG3D("signal: route 4 %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip4_route_to_string(route, sbuf, sizeof(sbuf))); + nmp_object_to_string(NMP_OBJECT_UP_CAST(route), + NMP_OBJECT_TO_STRING_PUBLIC, + sbuf, + sizeof(sbuf))); } static void @@ -9585,7 +9336,10 @@ log_ip6_route(NMPlatform *self, _LOG3D("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip6_route_to_string(route, sbuf, sizeof(sbuf))); + nmp_object_to_string(NMP_OBJECT_UP_CAST(route), + NMP_OBJECT_TO_STRING_PUBLIC, + sbuf, + sizeof(sbuf))); } static void @@ -9750,36 +9504,91 @@ nm_platform_netns_push(NMPlatform *self, NMPNetns **netns) /*****************************************************************************/ -const _NMPlatformVTableAddressUnion nm_platform_vtable_address = { - .v4 = - { - .is_ip4 = TRUE, - .obj_type = NMP_OBJECT_TYPE_IP4_ADDRESS, - .addr_family = AF_INET, - .sizeof_address = sizeof(NMPlatformIP4Address), - .address_cmp = - (int (*)(const NMPlatformIPXAddress *a, - const NMPlatformIPXAddress *b, - NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip4_address_cmp, - .address_to_string = (const char *(*) (const NMPlatformIPXAddress *address, - char *buf, - gsize len)) nm_platform_ip4_address_to_string, - }, - .v6 = - { - .is_ip4 = FALSE, - .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, - .addr_family = AF_INET6, - .sizeof_address = sizeof(NMPlatformIP6Address), - .address_cmp = - (int (*)(const NMPlatformIPXAddress *a, - const NMPlatformIPXAddress *b, - NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip6_address_cmp, - .address_to_string = (const char *(*) (const NMPlatformIPXAddress *address, - char *buf, - gsize len)) nm_platform_ip6_address_to_string, - }, -}; +typedef struct { + struct in6_addr address; + CList lst; + gint64 timestamp_nsec; + int ifindex; +} IP6DadFailedAddr; + +static void +ip6_dadfailed_addr_free(IP6DadFailedAddr *addr) +{ + c_list_unlink_stale(&addr->lst); + nm_g_slice_free(addr); +} + +static void +ip6_dadfailed_prune_old(NMPlatform *self, gint64 now_nsec) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); + IP6DadFailedAddr *addr; + IP6DadFailedAddr *safe; + + c_list_for_each_entry_safe (addr, safe, &priv->ip6_dadfailed_lst_head, lst) { + if (addr->timestamp_nsec + (10 * NM_UTILS_NSEC_PER_SEC) > now_nsec) + break; + ip6_dadfailed_addr_free(addr); + } +} + +gboolean +nm_platform_ip6_dadfailed_check(NMPlatform *self, int ifindex, const struct in6_addr *ip6) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); + IP6DadFailedAddr *addr; + + ip6_dadfailed_prune_old(self, nm_utils_get_monotonic_timestamp_nsec()); + + c_list_for_each_entry_prev (addr, &priv->ip6_dadfailed_lst_head, lst) { + if (addr->ifindex == ifindex && IN6_ARE_ADDR_EQUAL(&addr->address, ip6)) { + return TRUE; + } + } + return FALSE; +} + +/* + * If an IPv6 address fails DAD and has infinite lifetime, kernel just + * sets the DADFAILED flag. However when the address has a finite + * lifetime kernel deletes it immediately and the RTM_DELLINK netlink + * message contains the DADFAILED flag. In the second case, we remove + * the address from the platform cache and there is no way for + * platform's clients to check whether DAD failed. To work around + * this, we store all deleted-with-DADFAILED addresses and provide a + * mechanism to access them. + */ +void +nm_platform_ip6_dadfailed_set(NMPlatform *self, + int ifindex, + const struct in6_addr *ip6, + gboolean failed) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); + gint64 now_nsec = nm_utils_get_monotonic_timestamp_nsec(); + IP6DadFailedAddr *addr; + IP6DadFailedAddr *safe; + + ip6_dadfailed_prune_old(self, now_nsec); + + if (failed) { + addr = g_slice_new(IP6DadFailedAddr); + *addr = (IP6DadFailedAddr){ + .address = *ip6, + .ifindex = ifindex, + .timestamp_nsec = now_nsec, + }; + c_list_link_tail(&priv->ip6_dadfailed_lst_head, &addr->lst); + } else { + c_list_for_each_entry_safe (addr, safe, &priv->ip6_dadfailed_lst_head, lst) { + if (addr->ifindex == ifindex && IN6_ARE_ADDR_EQUAL(&addr->address, ip6)) { + ip6_dadfailed_addr_free(addr); + } + } + } +} + +/*****************************************************************************/ const _NMPlatformVTableRouteUnion nm_platform_vtable_route = { .v4 = @@ -9819,6 +9628,20 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); switch (prop_id) { + case PROP_MULTI_IDX: + /* construct-only */ + { + NMDedupMultiIndex *multi_idx; + + multi_idx = g_value_get_pointer(value); + if (!multi_idx) + multi_idx = nm_dedup_multi_index_new(); + else + multi_idx = nm_dedup_multi_index_ref(multi_idx); + + priv->multi_idx = multi_idx; + break; + } case PROP_NETNS_SUPPORT: /* construct-only */ if (g_value_get_boolean(value)) { @@ -9865,9 +9688,10 @@ constructor(GType type, guint n_construct_params, GObjectConstructParam *constru self = NM_PLATFORM(object); priv = NM_PLATFORM_GET_PRIVATE(self); - priv->multi_idx = nm_dedup_multi_index_new(); + nm_assert(priv->multi_idx); priv->cache = nmp_cache_new(priv->multi_idx, priv->use_udev); + c_list_init(&priv->ip6_dadfailed_lst_head); return object; } @@ -9877,6 +9701,7 @@ finalize(GObject *object) { NMPlatform *self = NM_PLATFORM(object); NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); + IP6DadFailedAddr *addr; nm_clear_g_source(&priv->ip4_dev_route_blacklist_check_id); nm_clear_g_source(&priv->ip4_dev_route_blacklist_gc_timeout_id); @@ -9885,6 +9710,10 @@ finalize(GObject *object) nm_dedup_multi_index_unref(priv->multi_idx); nmp_cache_free(priv->cache); + while ((addr = c_list_first_entry(&priv->ip6_dadfailed_lst_head, IP6DadFailedAddr, lst))) { + ip6_dadfailed_addr_free(addr); + } + G_OBJECT_CLASS(nm_platform_parent_class)->finalize(object); } @@ -9901,6 +9730,14 @@ nm_platform_class_init(NMPlatformClass *platform_class) platform_class->wifi_set_powersave = wifi_set_powersave; + g_object_class_install_property( + object_class, + PROP_MULTI_IDX, + g_param_spec_pointer(NM_PLATFORM_MULTI_IDX, + "", + "", + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property( object_class, PROP_NETNS_SUPPORT, diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index 30d0b506..1cd9c6c6 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -8,9 +8,10 @@ #include "libnm-platform/nmp-base.h" #include "libnm-base/nm-base.h" +#include "nmp-plobj.h" #define NM_TYPE_PLATFORM (nm_platform_get_type()) -#define NM_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PLATFORM, NMPlatform)) +#define NM_PLATFORM(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PLATFORM, NMPlatform)) #define NM_PLATFORM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_PLATFORM, NMPlatformClass)) #define NM_IS_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_PLATFORM)) @@ -22,16 +23,11 @@ /*****************************************************************************/ +#define NM_PLATFORM_CACHE_TC "cache-tc" +#define NM_PLATFORM_LOG_WITH_PTR "log-with-ptr" +#define NM_PLATFORM_MULTI_IDX "multi-idx" #define NM_PLATFORM_NETNS_SUPPORT "netns-support" #define NM_PLATFORM_USE_UDEV "use-udev" -#define NM_PLATFORM_LOG_WITH_PTR "log-with-ptr" -#define NM_PLATFORM_CACHE_TC "cache-tc" - -/*****************************************************************************/ - -/* IFNAMSIZ is both defined in and . In the past, these - * headers conflicted, so we cannot simply include either of them in a header-file.*/ -#define NMP_IFNAMSIZ 16 /*****************************************************************************/ @@ -41,24 +37,8 @@ struct udev_device; typedef gboolean (*NMPObjectPredicateFunc)(const NMPObject *obj, gpointer user_data); -/* workaround for older libnl version, that does not define these flags. */ -#ifndef IFA_F_MANAGETEMPADDR -#define IFA_F_MANAGETEMPADDR 0x100 -#endif -#ifndef IFA_F_NOPREFIXROUTE -#define IFA_F_NOPREFIXROUTE 0x200 -#endif - #define NM_RT_SCOPE_LINK 253 /* RT_SCOPE_LINK */ -/* Define of the IN6_ADDR_GEN_MODE_* values to workaround old kernel headers - * that don't define it. */ -#define NM_IN6_ADDR_GEN_MODE_UNKNOWN 255 /* no corresponding value. */ -#define NM_IN6_ADDR_GEN_MODE_EUI64 0 /* IN6_ADDR_GEN_MODE_EUI64 */ -#define NM_IN6_ADDR_GEN_MODE_NONE 1 /* IN6_ADDR_GEN_MODE_NONE */ -#define NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2 /* IN6_ADDR_GEN_MODE_STABLE_PRIVACY */ -#define NM_IN6_ADDR_GEN_MODE_RANDOM 3 /* IN6_ADDR_GEN_MODE_RANDOM */ - #define NM_IFF_MULTI_QUEUE 0x0100 /* IFF_MULTI_QUEUE */ #define NM_MPTCP_PM_ADDR_FLAG_SIGNAL ((guint32) (1 << 0)) @@ -100,14 +80,6 @@ typedef enum { NMP_NLM_FLAG_TEST = NMP_NLM_FLAG_F_EXCL, } NMPNlmFlags; -typedef enum { - NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID, - - NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY, - - NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL, -} NMPlatformIPAddressCmpType; - typedef enum { /* compare fields which kernel considers as similar routes. * It is a looser comparisong then NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID @@ -131,6 +103,11 @@ typedef enum { */ NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, + /* IPv4 route can have multiple hops. This is the ID, by which multiple + * routes are merged according to the next hop. This is basically NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID + * which ignores the next hops. */ + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID, + /* compare all fields as they make sense for kernel. For example, * a route destination 192.168.1.5/24 is not accepted by kernel and * we treat it identical to 192.168.1.0/24. Semantically these @@ -171,50 +148,11 @@ G_STATIC_ASSERT(_nm_alignof(NMPLinkAddress) == 1); gconstpointer nmp_link_address_get(const NMPLinkAddress *addr, size_t *length); GBytes *nmp_link_address_get_as_bytes(const NMPLinkAddress *addr); -typedef enum { - - /* match-flags are strictly inclusive. That means, - * by default nothing is matched, but if you enable a particular - * flag, a candidate that matches passes the check. - * - * In other words: adding more flags can only extend the result - * set of matching objects. - * - * Also, the flags form partitions. Like, an address can be either of - * ADDRTYPE_NORMAL or ADDRTYPE_LINKLOCAL, but never both. Same for - * the ADDRSTATE match types. - */ - NM_PLATFORM_MATCH_WITH_NONE = 0, - - NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL = (1LL << 0), - NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL = (1LL << 1), - NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY = - NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL | NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL, - - NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL = (1LL << 2), - NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE = (1LL << 3), - NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED = (1LL << 4), - NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED = (1LL << 5), - NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY = - NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL | NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED, -} NMPlatformMatchFlags; - #define NM_PLATFORM_LINK_OTHER_NETNS (-1) -struct _NMPlatformObject { - /* the object type has no fields of its own, it is only used to having - * a special pointer type that can be used to indicate "any" type. */ - char _dummy_don_t_use_me; -}; - -#define __NMPlatformObjWithIfindex_COMMON \ - int ifindex; \ - ; - struct _NMPlatformObjWithIfindex { __NMPlatformObjWithIfindex_COMMON; -}; +} _nm_alignas(NMPlatformObject); struct _NMPlatformLink { __NMPlatformObjWithIfindex_COMMON; @@ -271,7 +209,7 @@ struct _NMPlatformLink { bool connected : 1; bool initialized : 1; -}; +} _nm_alignas(NMPlatformObject); typedef enum { NM_PLATFORM_SIGNAL_ID_NONE, @@ -295,129 +233,6 @@ typedef enum { NM_PLATFORM_SIGNAL_REMOVED, } NMPlatformSignalChangeType; -#define NM_PLATFORM_IP_ADDRESS_CAST(address) \ - NM_CONSTCAST(NMPlatformIPAddress, \ - (address), \ - NMPlatformIPXAddress, \ - NMPlatformIP4Address, \ - NMPlatformIP6Address) - -#define __NMPlatformIPAddress_COMMON \ - __NMPlatformObjWithIfindex_COMMON; \ - NMIPConfigSource addr_source; \ - \ - /* Timestamp in seconds in the reference system of nm_utils_get_monotonic_timestamp_*(). - * - * The rules are: - * 1 @lifetime==0: @timestamp and @preferred is irrelevant (but mostly set to 0 too). Such addresses - * are permanent. This rule is so that unset addresses (calloc) are permanent by default. - * 2 @lifetime==@preferred==NM_PLATFORM_LIFETIME_PERMANENT: @timestamp is irrelevant (but mostly - * set to 0). Such addresses are permanent. - * 3 Non permanent addresses should (almost) always have @timestamp > 0. 0 is not a valid timestamp - * and never returned by nm_utils_get_monotonic_timestamp_sec(). In this case @valid/@preferred - * is anchored at @timestamp. - * 4 Non permanent addresses with @timestamp == 0 are implicitly anchored at *now*, thus the time - * moves as time goes by. This is usually not useful, except e.g. nm_platform_ip[46]_address_add(). - * - * Non permanent addresses from DHCP/RA might have the @timestamp set to the moment of when the - * lease was received. Addresses from kernel might have the @timestamp based on the last modification - * time of the addresses. But don't rely on this behaviour, the @timestamp is only defined for anchoring - * @lifetime and @preferred. - */ \ - guint32 timestamp; \ - guint32 lifetime; /* seconds since timestamp */ \ - guint32 preferred; /* seconds since timestamp */ \ - \ - /* ifa_flags in 'struct ifaddrmsg' from , extended to 32 bit by - * IFA_FLAGS attribute. */ \ - guint32 n_ifa_flags; \ - \ - bool use_ip4_broadcast_address : 1; \ - \ - /* Meta flags not honored by NMPlatform (netlink code). Instead, they can be - * used by the upper layers which use NMPlatformIPRoute to track addresses that - * should be configured. */ \ - bool a_force_commit : 1; \ - \ - /* nm_platform_ip_address_sync() likes to add IFA_F_NOPREFIXROUTE flag for all - * addresses, regardless of a_ifi_flags property. By setting this boolean, that - * automatism can be suppressed, and the noprefixroute flag does not get added - * automatically. */ \ - bool a_no_auto_noprefixroute : 1; \ - \ - /* Don't have a bitfield as last field in __NMPlatformIPAddress_COMMON. It would then - * be unclear how the following fields get merged. We could also use a zero bitfield, - * but instead we just have there the uint8 field. */ \ - guint8 plen; \ - ; - -/** - * NMPlatformIPAddress: - * - * Common parts of NMPlatformIP4Address and NMPlatformIP6Address. - **/ -typedef struct { - __NMPlatformIPAddress_COMMON; - _nm_alignas(NMIPAddr) guint8 address_ptr[]; -} NMPlatformIPAddress; - -/** - * NMPlatformIP4Address: - * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_sec() - **/ -struct _NMPlatformIP4Address { - __NMPlatformIPAddress_COMMON; - - /* The local address IFA_LOCAL. */ - _nm_alignas(NMIPAddr) in_addr_t address; - - /* The IFA_ADDRESS PTP peer address. This field is rather important, because - * it constitutes the identifier for the IPv4 address (e.g. you can add two - * addresses that only differ by their peer's network-part. - * - * Beware that for most cases, NetworkManager doesn't want to set an explicit - * peer-address. However, that corresponds to setting the peer address to @address - * itself. Leaving peer-address unset/zero, means explicitly setting the peer - * address to 0.0.0.0, which you probably don't want. - * */ - in_addr_t peer_address; /* PTP peer address */ - - /* IFA_BROADCAST. - * - * This parameter is ignored unless use_ip4_broadcast_address is TRUE. - * See nm_platform_ip4_broadcast_address_from_addr(). */ - in_addr_t broadcast_address; - - char label[NMP_IFNAMSIZ]; - - /* Whether the address is ready to be configured. By default, an address is, but this - * flag may indicate that the address is just for tracking purpose only, but the ACD - * state is not yet ready for the address to be configured. */ - bool a_acd_not_ready : 1; -}; - -/** - * NMPlatformIP6Address: - * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_sec() - **/ -struct _NMPlatformIP6Address { - __NMPlatformIPAddress_COMMON; - _nm_alignas(NMIPAddr) struct in6_addr address; - struct in6_addr peer_address; -}; - -typedef union { - NMPlatformIPAddress ax; - NMPlatformIP4Address a4; - NMPlatformIP6Address a6; -} NMPlatformIPXAddress; - -#undef __NMPlatformIPAddress_COMMON - -#define NM_PLATFORM_IP4_ADDRESS_INIT(...) (&((const NMPlatformIP4Address){__VA_ARGS__})) - -#define NM_PLATFORM_IP6_ADDRESS_INIT(...) (&((const NMPlatformIP6Address){__VA_ARGS__})) - /* Default value for adding an IPv4 route. This is also what iproute2 does. * Note that contrary to IPv6, you can add routes with metric 0 and it is even * the default. @@ -545,16 +360,16 @@ typedef union { */ \ guint8 type_coerced; \ \ - /* Don't have a bitfield as last field in __NMPlatformIPAddress_COMMON. It would then + /* Don't have a bitfield as last field in __NMPlatformIPRoute_COMMON. It would then * be unclear how the following fields get merged. We could also use a zero bitfield, - * but instead we just have there the uint8 field. */ \ + * but instead we just have there the uint8 field. */ \ guint8 plen; \ ; typedef struct { __NMPlatformIPRoute_COMMON; _nm_alignas(NMIPAddr) guint8 network_ptr[]; -} NMPlatformIPRoute; +} _nm_alignas(NMPlatformObject) NMPlatformIPRoute; #define NM_PLATFORM_IP_ROUTE_CAST(route) \ NM_CONSTCAST(NMPlatformIPRoute, \ @@ -567,9 +382,27 @@ typedef struct { struct _NMPlatformIP4Route { __NMPlatformIPRoute_COMMON; + in_addr_t network; - /* RTA_GATEWAY. The gateway is part of the primary key for a route */ + /* If n_nexthops is zero, the the address has no next hops. That applies + * to certain route types like blackhole. + * If n_nexthops is 1, then the fields "ifindex", "gateway" and "weight" + * are the first next-hop. There are no further nexthops. + * If n_nexthops is greater than 1, the first next hop is in the fields + * "ifindex", "gateway", "weight", and the (n_nexthops-1) hops are in + * NMPObjectIP4Route.extra_nexthops field (outside the NMPlatformIP4Route + * struct). + * + * For convenience, if ifindex > 0 and n_nexthops == 0, we assume that n_nexthops + * is in fact 1. If ifindex is <= 0, n_nexthops must be zero. + * See nm_platform_ip4_route_get_n_nexthops(). */ + guint n_nexthops; + + /* RTA_GATEWAY. The gateway is part of the primary key for a route. + * If n_nexthops is zero, this value is undefined (should be zero). + * If n_nexthops is greater or equal to one, this is the gateway of + * the first hop. */ in_addr_t gateway; /* RTA_PREFSRC (called "src" by iproute2). @@ -578,6 +411,20 @@ struct _NMPlatformIP4Route { * pref_src must match, unless set to 0.0.0.0 to match any. */ in_addr_t pref_src; + /* This is the weight of for the first next-hop, in case of n_nexthops > 1. + * + * If n_nexthops is zero, this value is undefined (should be zero). + * If n_nexthops is 1, this also doesn't matter, but it's usually set to + * zero. + * If n_nexthops is greater or equal to one, this is the weight of + * the first hop. + * + * Note that upper layers (nm_utils_ip_route_attribute_to_platform()) use this flag to indicate + * whether this is a multihop route. Single-hop, non-ECMP routes will have a weight of zero. + * + * The valid range for weight in kernel is 1-256. */ + guint16 weight; + /* rtm_tos (iproute2: tos) * * For IPv4, tos is part of the weak-id (like metric). @@ -597,7 +444,7 @@ struct _NMPlatformIP4Route { * For IPv6 routes, the scope is ignored and kernel always assumes global scope. * Hence, this field is only in NMPlatformIP4Route. */ guint8 scope_inv; -}; +} _nm_alignas(NMPlatformObject); struct _NMPlatformIP6Route { __NMPlatformIPRoute_COMMON; @@ -630,7 +477,7 @@ struct _NMPlatformIP6Route { * The type is guint8 to keep the struct size small. But the values are compatible with * the NMIcmpv6RouterPref enum. */ guint8 rt_pref; -}; +} _nm_alignas(NMPlatformObject); typedef union { NMPlatformIPRoute rx; @@ -684,7 +531,7 @@ typedef struct { guint8 ip_proto; /* FRA_IP_PROTO */ bool uid_range_has : 1; /* has(FRA_UID_RANGE) */ -} NMPlatformRoutingRule; +} _nm_alignas(NMPlatformObject) NMPlatformRoutingRule; #define NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET (~((guint32) 0)) @@ -752,7 +599,7 @@ typedef struct { NMPlatformQdiscSfq sfq; NMPlatformQdiscTbf tbf; }; -} NMPlatformQdisc; +} _nm_alignas(NMPlatformObject) NMPlatformQdisc; typedef struct { char sdata[32]; @@ -792,28 +639,7 @@ typedef struct { guint32 parent; guint32 info; NMPlatformAction action; -} NMPlatformTfilter; - -typedef struct { - bool is_ip4; - NMPObjectType obj_type; - gint8 addr_family; - guint8 sizeof_address; - int (*address_cmp)(const NMPlatformIPXAddress *a, - const NMPlatformIPXAddress *b, - NMPlatformIPAddressCmpType cmp_type); - const char *(*address_to_string)(const NMPlatformIPXAddress *address, char *buf, gsize len); -} NMPlatformVTableAddress; - -typedef union { - struct { - NMPlatformVTableAddress v6; - NMPlatformVTableAddress v4; - }; - NMPlatformVTableAddress vx[2]; -} _NMPlatformVTableAddressUnion; - -extern const _NMPlatformVTableAddressUnion nm_platform_vtable_address; +} _nm_alignas(NMPlatformObject) NMPlatformTfilter; typedef struct { bool is_ip4; @@ -842,6 +668,25 @@ typedef struct { bool proto_ad : 1; } NMPlatformVFVlan; +typedef struct { + int ifindex; + in_addr_t gateway; + /* The valid range for weight is 1-256. Single hop routes in kernel + * don't have a weight, we assign them weight zero (to indicate the + * weight is missing). + * + * Upper layers (nm_utils_ip_route_attribute_to_platform()) care about + * the distinction of unset weight (no-ECMP). They express no-ECMP as + * zero. + */ + guint16 weight; + + /* FIXME: each next hop in kernel also has a rtnh_flags (for example to + * set RTNH_F_ONLINK). As the next hop is part of the identifier of an + * IPv4 route, so is their flags. We must also track the flag, otherwise + * two routes that look different for kernel, get merged by platform cache. */ +} NMPlatformIP4RtNextHop; + typedef struct { guint num_vlans; guint32 index; @@ -887,7 +732,7 @@ typedef struct { bool mcast_snooping : 1; bool stp_state : 1; bool vlan_stats_enabled : 1; -} NMPlatformLnkBridge; +} _nm_alignas(NMPlatformObject) NMPlatformLnkBridge; extern const NMPlatformLnkBridge nm_platform_lnk_bridge_default; @@ -929,7 +774,7 @@ typedef struct { bool tlb_dynamic_lb_has : 1; bool updelay_has : 1; bool use_carrier : 1; -} NMPlatformLnkBond; +} _nm_alignas(NMPlatformObject) NMPlatformLnkBond; typedef struct { int parent_ifindex; @@ -943,12 +788,12 @@ typedef struct { guint8 tos; bool path_mtu_discovery : 1; bool is_tap : 1; -} NMPlatformLnkGre; +} _nm_alignas(NMPlatformObject) NMPlatformLnkGre; typedef struct { int p_key; const char *mode; -} NMPlatformLnkInfiniband; +} _nm_alignas(NMPlatformObject) NMPlatformLnkInfiniband; typedef struct { struct in6_addr local; @@ -968,7 +813,7 @@ typedef struct { guint16 output_flags; bool is_tap : 1; bool is_gre : 1; -} NMPlatformLnkIp6Tnl; +} _nm_alignas(NMPlatformObject) NMPlatformLnkIp6Tnl; typedef struct { int parent_ifindex; @@ -977,9 +822,28 @@ typedef struct { guint8 ttl; guint8 tos; bool path_mtu_discovery : 1; -} NMPlatformLnkIpIp; +} _nm_alignas(NMPlatformObject) NMPlatformLnkIpIp; + +typedef struct { + int parent_ifindex; + in_addr_t local; + in_addr_t remote; + guint32 ikey; + guint32 okey; + guint32 fwmark; +} _nm_alignas(NMPlatformObject) NMPlatformLnkVti; + +typedef struct { + int parent_ifindex; + struct in6_addr local; + struct in6_addr remote; + guint32 ikey; + guint32 okey; + guint32 fwmark; +} _nm_alignas(NMPlatformObject) NMPlatformLnkVti6; typedef struct { + int parent_ifindex; guint64 sci; /* host byte order */ guint64 cipher_suite; guint32 window; @@ -992,13 +856,13 @@ typedef struct { bool es : 1; bool scb : 1; bool replay_protect : 1; -} NMPlatformLnkMacsec; +} _nm_alignas(NMPlatformObject) NMPlatformLnkMacsec; typedef struct { guint mode; bool no_promisc : 1; bool tap : 1; -} NMPlatformLnkMacvlan; +} _nm_alignas(NMPlatformObject) NMPlatformLnkMacvlan; typedef struct { int parent_ifindex; @@ -1009,7 +873,7 @@ typedef struct { guint8 tos; guint8 proto; bool path_mtu_discovery : 1; -} NMPlatformLnkSit; +} _nm_alignas(NMPlatformObject) NMPlatformLnkSit; typedef struct { guint32 owner; @@ -1024,17 +888,17 @@ typedef struct { bool vnet_hdr : 1; bool multi_queue : 1; bool persist : 1; -} NMPlatformLnkTun; +} _nm_alignas(NMPlatformObject) NMPlatformLnkTun; typedef struct { - /* rtnl_link_vlan_get_id(), IFLA_VLAN_ID */ guint16 id; + guint16 protocol; _NMVlanFlags flags; -} NMPlatformLnkVlan; +} _nm_alignas(NMPlatformObject) NMPlatformLnkVlan; typedef struct { guint32 table; -} NMPlatformLnkVrf; +} _nm_alignas(NMPlatformObject) NMPlatformLnkVrf; typedef struct { struct in6_addr group6; @@ -1055,7 +919,7 @@ typedef struct { bool rsc : 1; bool l2miss : 1; bool l3miss : 1; -} NMPlatformLnkVxlan; +} _nm_alignas(NMPlatformObject) NMPlatformLnkVxlan; #define NMP_WIREGUARD_PUBLIC_KEY_LEN 32 #define NMP_WIREGUARD_SYMMETRIC_KEY_LEN 32 @@ -1065,7 +929,7 @@ typedef struct { guint16 listen_port; guint8 private_key[NMP_WIREGUARD_PUBLIC_KEY_LEN]; guint8 public_key[NMP_WIREGUARD_PUBLIC_KEY_LEN]; -} NMPlatformLnkWireGuard; +} _nm_alignas(NMPlatformObject) NMPlatformLnkWireGuard; typedef enum { NM_PLATFORM_WIREGUARD_CHANGE_FLAG_NONE = 0, @@ -1370,10 +1234,7 @@ typedef struct { struct in6_addr address, guint8 plen); - int (*ip_route_add)(NMPlatform *self, - NMPNlmFlags flags, - int addr_family, - const NMPlatformIPRoute *route); + int (*ip_route_add)(NMPlatform *self, NMPNlmFlags flags, NMPObject *obj_stack); int (*ip_route_get)(NMPlatform *self, int addr_family, gconstpointer address, @@ -1426,29 +1287,6 @@ GType nm_platform_get_type(void); /*****************************************************************************/ -static inline in_addr_t -nm_platform_ip4_broadcast_address_create(in_addr_t address, guint8 plen) -{ - return address | ~_nm_utils_ip4_prefix_to_netmask(plen); -} - -static inline in_addr_t -nm_platform_ip4_broadcast_address_from_addr(const NMPlatformIP4Address *addr) -{ - nm_assert(addr); - - if (addr->use_ip4_broadcast_address) - return addr->broadcast_address; - - /* the set broadcast-address gets ignored, and we determine a default brd base - * on the peer IFA_ADDRESS. */ - if (addr->peer_address != 0u && addr->plen < 31 /* RFC3021 */) - return nm_platform_ip4_broadcast_address_create(addr->peer_address, addr->plen); - return 0u; -} - -/*****************************************************************************/ - /** * nm_platform_route_table_coerce: * @table: the route table, in its original value as received @@ -1589,9 +1427,9 @@ nm_platform_ip4_address_get_scope(in_addr_t addr) /* For IPv4 addresses, we can set any scope we want (for any address). * However, there are scopes that make sense based on the address, * so choose those. */ - return nm_utils_ip4_address_is_loopback(addr) ? (254 /* RT_SCOPE_HOST */) - : nm_utils_ip4_address_is_link_local(addr) ? (253 /* RT_SCOPE_LINK */) - : (0 /* RT_SCOPE_UNIVERSE */); + return nm_ip4_addr_is_loopback(addr) ? (254 /* RT_SCOPE_HOST */) + : nm_ip4_addr_is_link_local(addr) ? (253 /* RT_SCOPE_LINK */) + : (0 /* RT_SCOPE_UNIVERSE */); } static inline guint8 @@ -1612,7 +1450,7 @@ nm_platform_ip_address_get_scope(int addr_family, gconstpointer addr) /* Note that this function returns the scope as we configure * it in kernel (for IPv4) or as kernel chooses it (for IPv6). * - * That means, rfc1918 private addresses nm_utils_ip_is_site_local() are + * That means, rfc1918 private addresses nm_ip_addr_is_site_local() are * considered RT_SCOPE_UNIVERSE. * * Also, the deprecated IN6_IS_ADDR_SITELOCAL() addresses (fec0::/10) @@ -1851,28 +1689,13 @@ nm_platform_link_sit_add(NMPlatform *self, } static inline int -nm_platform_link_vlan_add(NMPlatform *self, - const char *name, - int parent, - int vlanid, - guint32 vlanflags, - const NMPlatformLink **out_link) +nm_platform_link_vlan_add(NMPlatform *self, + const char *name, + int parent, + const NMPlatformLnkVlan *props, + const NMPlatformLink **out_link) { - g_return_val_if_fail(parent >= 0, -NME_BUG); - g_return_val_if_fail(vlanid >= 0, -NME_BUG); - - return nm_platform_link_add(self, - NM_LINK_TYPE_VLAN, - name, - parent, - NULL, - 0, - 0, - &((NMPlatformLnkVlan){ - .id = vlanid, - .flags = vlanflags, - }), - out_link); + return nm_platform_link_add(self, NM_LINK_TYPE_VLAN, name, parent, NULL, 0, 0, props, out_link); } static inline int @@ -1884,6 +1707,24 @@ nm_platform_link_vrf_add(NMPlatform *self, return nm_platform_link_add(self, NM_LINK_TYPE_VRF, name, 0, NULL, 0, 0, props, out_link); } +static inline int +nm_platform_link_vti_add(NMPlatform *self, + const char *name, + const NMPlatformLnkVti *props, + const NMPlatformLink **out_link) +{ + return nm_platform_link_add(self, NM_LINK_TYPE_VTI, name, 0, NULL, 0, 0, props, out_link); +} + +static inline int +nm_platform_link_vti6_add(NMPlatform *self, + const char *name, + const NMPlatformLnkVti6 *props, + const NMPlatformLink **out_link) +{ + return nm_platform_link_add(self, NM_LINK_TYPE_VTI6, name, 0, NULL, 0, 0, props, out_link); +} + static inline int nm_platform_link_vxlan_add(NMPlatform *self, const char *name, @@ -2173,6 +2014,10 @@ const NMPlatformLnkVlan * nm_platform_link_get_lnk_vlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkVrf * nm_platform_link_get_lnk_vrf(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkVti * +nm_platform_link_get_lnk_vti(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkVti6 * +nm_platform_link_get_lnk_vti6(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkVxlan * nm_platform_link_get_lnk_vxlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkWireGuard * @@ -2238,9 +2083,6 @@ guint16 nm_platform_wpan_get_short_addr(NMPlatform *self, int ifindex); gboolean nm_platform_wpan_set_short_addr(NMPlatform *self, int ifindex, guint16 short_addr); gboolean nm_platform_wpan_set_channel(NMPlatform *self, int ifindex, guint8 page, guint8 channel); -void nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen); -const struct in6_addr *nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr); - const NMPObject *nm_platform_ip_address_get(NMPlatform *self, int addr_family, int ifindex, @@ -2252,6 +2094,9 @@ const NMPlatformIP4Address *nm_platform_ip4_address_get(NMPlatform *self, guint8 plen, in_addr_t peer_address); +const NMPlatformIP6Address * +nm_platform_ip6_address_get(NMPlatform *self, int ifindex, const struct in6_addr *address); + int nm_platform_link_sit_add(NMPlatform *self, const char *name, const NMPlatformLnkSit *props, @@ -2274,9 +2119,6 @@ int nm_platform_link_wireguard_change(NMPlatform * guint peers_len, NMPlatformWireGuardChangeFlags change_flags); -const NMPlatformIP6Address * -nm_platform_ip6_address_get(NMPlatform *self, int ifindex, const struct in6_addr *address); - gboolean nm_platform_object_delete(NMPlatform *self, const NMPObject *route); gboolean nm_platform_ip4_address_add(NMPlatform *self, @@ -2328,11 +2170,17 @@ nm_platform_ip_address_delete(NMPlatform * } } -gboolean nm_platform_ip_address_sync(NMPlatform *self, - int addr_family, - int ifindex, - GPtrArray *known_addresses, - GPtrArray *addresses_prune); +typedef enum { + NMP_IP_ADDRESS_SYNC_FLAGS_NONE = 0, + NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE = (1 << 0), +} NMPIPAddressSyncFlags; + +gboolean nm_platform_ip_address_sync(NMPlatform *self, + int addr_family, + int ifindex, + GPtrArray *known_addresses, + GPtrArray *addresses_prune, + NMPIPAddressSyncFlags flags); GPtrArray * nm_platform_ip_address_get_prune_list(NMPlatform *self, @@ -2343,17 +2191,6 @@ nm_platform_ip_address_get_prune_list(NMPlatform *self, gboolean nm_platform_ip_address_flush(NMPlatform *self, int addr_family, int ifindex); -static inline gpointer -nm_platform_ip_address_get_peer_address(int addr_family, const NMPlatformIPAddress *addr) -{ - nm_assert_addr_family(addr_family); - nm_assert(addr); - - if (NM_IS_IPv4(addr_family)) - return &((NMPlatformIP4Address *) addr)->peer_address; - return &((NMPlatformIP6Address *) addr)->peer_address; -} - void nm_platform_ip_route_normalize(int addr_family, NMPlatformIPRoute *route); static inline guint32 @@ -2365,6 +2202,23 @@ nm_platform_ip4_route_get_effective_metric(const NMPlatformIP4Route *r) : r->metric; } +static inline guint +nm_platform_ip4_route_get_n_nexthops(const NMPlatformIP4Route *r) +{ + /* The first hop of the "n_nexthops" is in NMPlatformIP4Route + * itself. Thus, if the caller only sets ifindex and leaves + * n_nexthops at zero, the number of next hops is still 1 + * (for convenience of the user who wants to initialize a + * single hop route). */ + if (r->n_nexthops >= 1) { + nm_assert(r->ifindex > 0); + return r->n_nexthops; + } + if (r->ifindex > 0) + return 1; + return 0; +} + static inline guint32 nm_platform_ip6_route_get_effective_metric(const NMPlatformIP6Route *r) { @@ -2398,7 +2252,10 @@ nm_platform_ip_route_get_gateway(int addr_family, const NMPlatformIPRoute *route } int nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *route); -int nm_platform_ip4_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route); +int nm_platform_ip4_route_add(NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformIP4Route *route, + const NMPlatformIP4RtNextHop *extra_nexthops); int nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route); GPtrArray *nm_platform_ip_route_get_prune_list(NMPlatform *self, @@ -2449,14 +2306,23 @@ const char *nm_platform_lnk_sit_to_string(const NMPlatformLnkSit *lnk, char *buf const char *nm_platform_lnk_tun_to_string(const NMPlatformLnkTun *lnk, char *buf, gsize len); const char *nm_platform_lnk_vlan_to_string(const NMPlatformLnkVlan *lnk, char *buf, gsize len); const char *nm_platform_lnk_vrf_to_string(const NMPlatformLnkVrf *lnk, char *buf, gsize len); +const char *nm_platform_lnk_vti_to_string(const NMPlatformLnkVti *lnk, char *buf, gsize len); +const char *nm_platform_lnk_vti6_to_string(const NMPlatformLnkVti6 *lnk, char *buf, gsize len); const char *nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize len); const char * nm_platform_lnk_wireguard_to_string(const NMPlatformLnkWireGuard *lnk, char *buf, gsize len); -const char * -nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf, gsize len); -const char * -nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf, gsize len); -const char *nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len); + +const char *nm_platform_ip4_route_to_string_full(const NMPlatformIP4Route *route, + const NMPlatformIP4RtNextHop *extra_nexthops, + char *buf, + gsize len); + +static inline const char * +nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len) +{ + return nm_platform_ip4_route_to_string_full(route, NULL, buf, len); +} + const char *nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsize len); const char * nm_platform_routing_rule_to_string(const NMPlatformRoutingRule *routing_rule, char *buf, gsize len); @@ -2492,79 +2358,52 @@ int nm_platform_lnk_sit_cmp(const NMPlatformLnkSit *a, const NMPlatformLnkSit *b int nm_platform_lnk_tun_cmp(const NMPlatformLnkTun *a, const NMPlatformLnkTun *b); int nm_platform_lnk_vlan_cmp(const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b); int nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b); +int nm_platform_lnk_vti_cmp(const NMPlatformLnkVti *a, const NMPlatformLnkVti *b); +int nm_platform_lnk_vti6_cmp(const NMPlatformLnkVti6 *a, const NMPlatformLnkVti6 *b); int nm_platform_lnk_vxlan_cmp(const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b); int nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformLnkWireGuard *b); -int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, - const NMPlatformIP4Address *b, - NMPlatformIPAddressCmpType cmp_type); -int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, - const NMPlatformIP6Address *b, - NMPlatformIPAddressCmpType cmp_type); - -static inline int -nm_platform_ip4_address_cmp_full(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) -{ - return nm_platform_ip4_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL); -} - -static inline int -nm_platform_ip6_address_cmp_full(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) -{ - return nm_platform_ip6_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL); -} - -int nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, - const NMPlatformIP4Address *a2); - -int nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1, - const NMPlatformIP6Address *a2, - gboolean prefer_temp); GHashTable *nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex); int nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, NMPlatformIPRouteCmpType cmp_type); +int nm_platform_ip4_rt_nexthop_cmp(const NMPlatformIP4RtNextHop *a, + const NMPlatformIP4RtNextHop *b, + gboolean for_id); int nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, NMPlatformIPRouteCmpType cmp_type); -static inline int -nm_platform_ip4_route_cmp_full(const NMPlatformIP4Route *a, const NMPlatformIP4Route *b) -{ - return nm_platform_ip4_route_cmp(a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); -} - -static inline int -nm_platform_ip6_route_cmp_full(const NMPlatformIP6Route *a, const NMPlatformIP6Route *b) -{ - return nm_platform_ip6_route_cmp(a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); -} - int nm_platform_routing_rule_cmp(const NMPlatformRoutingRule *a, const NMPlatformRoutingRule *b, NMPlatformRoutingRuleCmpType cmp_type); -static inline int -nm_platform_routing_rule_cmp_full(const NMPlatformRoutingRule *a, const NMPlatformRoutingRule *b) -{ - return nm_platform_routing_rule_cmp(a, b, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL); -} +int +nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b, gboolean compare_handle); -int nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b); -int nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a, - const NMPlatformQdisc *b, - gboolean compare_handle); int nm_platform_tfilter_cmp(const NMPlatformTfilter *a, const NMPlatformTfilter *b); int nm_platform_mptcp_addr_cmp(const NMPlatformMptcpAddr *a, const NMPlatformMptcpAddr *b); void nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h); -void nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h); -void nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h); void nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); + +static inline guint +nm_platform_ip4_route_hash(const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type) +{ + NMHashState h; + + nm_hash_init(&h, 1118769853u); + nm_platform_ip4_route_hash_update(obj, cmp_type, &h); + return nm_hash_complete(&h); +} + +void nm_platform_ip4_rt_nexthop_hash_update(const NMPlatformIP4RtNextHop *obj, + gboolean for_id, + NMHashState *h); void nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); @@ -2583,6 +2422,8 @@ void nm_platform_lnk_sit_hash_update(const NMPlatformLnkSit *obj, NMHashState *h void nm_platform_lnk_tun_hash_update(const NMPlatformLnkTun *obj, NMHashState *h); void nm_platform_lnk_vlan_hash_update(const NMPlatformLnkVlan *obj, NMHashState *h); void nm_platform_lnk_vrf_hash_update(const NMPlatformLnkVrf *obj, NMHashState *h); +void nm_platform_lnk_vti_hash_update(const NMPlatformLnkVti *obj, NMHashState *h); +void nm_platform_lnk_vti6_hash_update(const NMPlatformLnkVti6 *obj, NMHashState *h); void nm_platform_lnk_vxlan_hash_update(const NMPlatformLnkVxlan *obj, NMHashState *h); void nm_platform_lnk_wireguard_hash_update(const NMPlatformLnkWireGuard *obj, NMHashState *h); @@ -2596,13 +2437,6 @@ gboolean nm_platform_mptcp_addr_index_addr_equal(gconstpointer data_a, gconstpoi #define NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN ((gsize) 162) -const char *nm_platform_link_flags2str(unsigned flags, char *buf, gsize len); -const char *nm_platform_link_inet6_addrgenmode2str(guint8 mode, char *buf, gsize len); -const char *nm_platform_addr_flags2str(unsigned flags, char *buf, gsize len); -const char *nm_platform_route_scope2str(int scope, char *buf, gsize len); - -int nm_platform_ip_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b); - gboolean nm_platform_ethtool_set_wake_on_lan(NMPlatform *self, int ifindex, _NMSettingWiredWakeOnLan wol, @@ -2653,21 +2487,6 @@ struct _NMDedupMultiIndex *nm_platform_get_multi_idx(NMPlatform *self); /*****************************************************************************/ -NMPlatformIP4Route *nm_platform_ip4_address_generate_device_route(const NMPlatformIP4Address *addr, - int ifindex, - guint32 route_table, - guint32 route_metric, - gboolean force_commit, - NMPlatformIP4Route *dst); - -/*****************************************************************************/ - -gboolean nm_platform_ip_address_match(int addr_family, - const NMPlatformIPAddress *addr, - NMPlatformMatchFlags match_flag); - -/*****************************************************************************/ - guint16 nm_platform_genl_get_family_id(NMPlatform *self, NMPGenlFamilyType family_type); int @@ -2675,4 +2494,35 @@ nm_platform_mptcp_addr_update(NMPlatform *self, NMOptionBool add, const NMPlatfo GPtrArray *nm_platform_mptcp_addrs_dump(NMPlatform *self); +gboolean nm_platform_ip6_dadfailed_check(NMPlatform *self, int ifindex, const struct in6_addr *ip6); +void nm_platform_ip6_dadfailed_set(NMPlatform *self, + int ifindex, + const struct in6_addr *ip6, + gboolean failed); + +/*****************************************************************************/ + +static inline NMPlatformIP4Address * +nm_platform_ip4_address_init_loopback_addr1(NMPlatformIP4Address *a) +{ + *a = ((NMPlatformIP4Address){ + .address = NM_IPV4LO_ADDR1, + .peer_address = NM_IPV4LO_ADDR1, + .ifindex = NM_LOOPBACK_IFINDEX, + .plen = NM_IPV4LO_PREFIXLEN, + }); + return a; +} + +static inline NMPlatformIP6Address * +nm_platform_ip6_address_init_loopback(NMPlatformIP6Address *a) +{ + *a = ((NMPlatformIP6Address){ + .address = IN6ADDR_LOOPBACK_INIT, + .ifindex = NM_LOOPBACK_IFINDEX, + .plen = 128, + }); + return a; +} + #endif /* __NETWORKMANAGER_PLATFORM_H__ */ diff --git a/src/libnm-platform/nmp-base.c b/src/libnm-platform/nmp-base.c new file mode 100644 index 00000000..c23190b3 --- /dev/null +++ b/src/libnm-platform/nmp-base.c @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2015 - 2018 Red Hat, Inc. + */ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nmp-base.h" + +#include +#include +#include "nm-compat-headers/linux/if_addr.h" + +/*****************************************************************************/ + +NM_UTILS_FLAGS2STR_DEFINE(nm_platform_link_flags2str, + unsigned, + NM_UTILS_FLAGS2STR(IFF_LOOPBACK, "loopback"), + NM_UTILS_FLAGS2STR(IFF_BROADCAST, "broadcast"), + NM_UTILS_FLAGS2STR(IFF_POINTOPOINT, "pointopoint"), + NM_UTILS_FLAGS2STR(IFF_MULTICAST, "multicast"), + NM_UTILS_FLAGS2STR(IFF_NOARP, "noarp"), + NM_UTILS_FLAGS2STR(IFF_ALLMULTI, "allmulti"), + NM_UTILS_FLAGS2STR(IFF_PROMISC, "promisc"), + NM_UTILS_FLAGS2STR(IFF_MASTER, "master"), + NM_UTILS_FLAGS2STR(IFF_SLAVE, "slave"), + NM_UTILS_FLAGS2STR(IFF_DEBUG, "debug"), + NM_UTILS_FLAGS2STR(IFF_DYNAMIC, "dynamic"), + NM_UTILS_FLAGS2STR(IFF_AUTOMEDIA, "automedia"), + NM_UTILS_FLAGS2STR(IFF_PORTSEL, "portsel"), + NM_UTILS_FLAGS2STR(IFF_NOTRAILERS, "notrailers"), + NM_UTILS_FLAGS2STR(IFF_UP, "up"), + NM_UTILS_FLAGS2STR(IFF_RUNNING, "running"), + NM_UTILS_FLAGS2STR(IFF_LOWER_UP, "lowerup"), + NM_UTILS_FLAGS2STR(IFF_DORMANT, "dormant"), + NM_UTILS_FLAGS2STR(IFF_ECHO, "echo"), ); + +NM_UTILS_ENUM2STR_DEFINE(nm_platform_link_inet6_addrgenmode2str, + guint8, + NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_NONE, "none"), + NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_EUI64, "eui64"), + NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY, "stable-privacy"), + NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_RANDOM, "random"), ); + +NM_UTILS_FLAGS2STR_DEFINE(nm_platform_addr_flags2str, + unsigned, + NM_UTILS_FLAGS2STR(IFA_F_SECONDARY, "secondary"), + NM_UTILS_FLAGS2STR(IFA_F_NODAD, "nodad"), + NM_UTILS_FLAGS2STR(IFA_F_OPTIMISTIC, "optimistic"), + NM_UTILS_FLAGS2STR(IFA_F_DADFAILED, "dadfailed"), + NM_UTILS_FLAGS2STR(IFA_F_HOMEADDRESS, "homeaddress"), + NM_UTILS_FLAGS2STR(IFA_F_DEPRECATED, "deprecated"), + NM_UTILS_FLAGS2STR(IFA_F_TENTATIVE, "tentative"), + NM_UTILS_FLAGS2STR(IFA_F_PERMANENT, "permanent"), + NM_UTILS_FLAGS2STR(IFA_F_MANAGETEMPADDR, "mngtmpaddr"), + NM_UTILS_FLAGS2STR(IFA_F_NOPREFIXROUTE, "noprefixroute"), + NM_UTILS_FLAGS2STR(IFA_F_MCAUTOJOIN, "mcautojoin"), + NM_UTILS_FLAGS2STR(IFA_F_STABLE_PRIVACY, "stable-privacy"), ); + +G_STATIC_ASSERT(IFA_F_SECONDARY == IFA_F_TEMPORARY); + +NM_UTILS_ENUM2STR_DEFINE(nm_platform_route_scope2str, + int, + NM_UTILS_ENUM2STR(RT_SCOPE_NOWHERE, "nowhere"), + NM_UTILS_ENUM2STR(RT_SCOPE_HOST, "host"), + NM_UTILS_ENUM2STR(RT_SCOPE_LINK, "link"), + NM_UTILS_ENUM2STR(RT_SCOPE_SITE, "site"), + NM_UTILS_ENUM2STR(RT_SCOPE_UNIVERSE, "global"), ); diff --git a/src/libnm-platform/nmp-base.h b/src/libnm-platform/nmp-base.h index a6ee3df1..80d254b2 100644 --- a/src/libnm-platform/nmp-base.h +++ b/src/libnm-platform/nmp-base.h @@ -11,6 +11,22 @@ /*****************************************************************************/ +/* IFNAMSIZ is both defined in and . In the past, these + * headers conflicted, so we cannot simply include either of them in a header-file.*/ +#define NMP_IFNAMSIZ 16 + +/*****************************************************************************/ + +/* Define of the IN6_ADDR_GEN_MODE_* values to workaround old kernel headers + * that don't define it. */ +#define NM_IN6_ADDR_GEN_MODE_UNKNOWN 255 /* no corresponding value. */ +#define NM_IN6_ADDR_GEN_MODE_EUI64 0 /* IN6_ADDR_GEN_MODE_EUI64 */ +#define NM_IN6_ADDR_GEN_MODE_NONE 1 /* IN6_ADDR_GEN_MODE_NONE */ +#define NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2 /* IN6_ADDR_GEN_MODE_STABLE_PRIVACY */ +#define NM_IN6_ADDR_GEN_MODE_RANDOM 3 /* IN6_ADDR_GEN_MODE_RANDOM */ + +/*****************************************************************************/ + typedef enum { NM_PLATFORM_LINK_DUPLEX_UNKNOWN, NM_PLATFORM_LINK_DUPLEX_HALF, @@ -145,6 +161,8 @@ typedef enum _nm_packed { NMP_OBJECT_TYPE_LNK_TUN, NMP_OBJECT_TYPE_LNK_VLAN, NMP_OBJECT_TYPE_LNK_VRF, + NMP_OBJECT_TYPE_LNK_VTI, + NMP_OBJECT_TYPE_LNK_VTI6, NMP_OBJECT_TYPE_LNK_VXLAN, NMP_OBJECT_TYPE_LNK_WIREGUARD, NMP_OBJECT_TYPE_LNK_BOND, @@ -189,4 +207,11 @@ typedef enum { NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE, } NMIPRouteTableSyncMode; +/*****************************************************************************/ + +const char *nm_platform_link_flags2str(unsigned flags, char *buf, gsize len); +const char *nm_platform_link_inet6_addrgenmode2str(guint8 mode, char *buf, gsize len); +const char *nm_platform_addr_flags2str(unsigned flags, char *buf, gsize len); +const char *nm_platform_route_scope2str(int scope, char *buf, gsize len); + #endif /* __NMP_FWD_H__ */ diff --git a/src/libnm-platform/nmp-global-tracker.c b/src/libnm-platform/nmp-global-tracker.c index 12869cb2..09f1e217 100644 --- a/src/libnm-platform/nmp-global-tracker.c +++ b/src/libnm-platform/nmp-global-tracker.c @@ -800,7 +800,7 @@ nmp_global_tracker_sync_mptcp_addrs(NMPGlobalTracker *self, gboolean reapply) /* Now, drop all duplicates addresses. Only keep the first one. */ for (i = 0, j = 0; i < entries->len; i++) { - const MptcpSyncData *d = nm_g_array_index_p(entries, MptcpSyncData, i); + const MptcpSyncData *d = &nm_g_array_index(entries, MptcpSyncData, i); const NMPlatformMptcpAddr *mptcp_addr = NMP_OBJECT_CAST_MPTCP_ADDR(d->obj_data->obj); obj_data = g_hash_table_lookup(entries_hash_by_addr, (gpointer) mptcp_addr); @@ -826,7 +826,7 @@ nmp_global_tracker_sync_mptcp_addrs(NMPGlobalTracker *self, gboolean reapply) nm_assert_not_reached(); if (i != j) - *(nm_g_array_index_p(entries, MptcpSyncData, j)) = *d; + (nm_g_array_index(entries, MptcpSyncData, j)) = *d; j++; if (j >= MPTCP_PM_ADDR_MAX) { @@ -931,7 +931,7 @@ keep_and_next: if (entries) { for (i = 0; i < entries->len; i++) { - const MptcpSyncData *d = nm_g_array_index_p(entries, MptcpSyncData, i); + const MptcpSyncData *d = &nm_g_array_index(entries, MptcpSyncData, i); const NMPlatformMptcpAddr *mptcp_addr = NMP_OBJECT_CAST_MPTCP_ADDR(d->obj_data->obj); const NMPObject *kobj; diff --git a/src/libnm-platform/nmp-netns.c b/src/libnm-platform/nmp-netns.c index 2563e8f6..c18f67ab 100644 --- a/src/libnm-platform/nmp-netns.c +++ b/src/libnm-platform/nmp-netns.c @@ -66,26 +66,28 @@ __ns_types_to_str(int ns_types, int ns_types_already_set, char *buf, gsize len) #define _NMLOG_DOMAIN LOGD_PLATFORM #define _NMLOG_PREFIX_NAME "netns" -#define _NMLOG(level, netns, ...) \ - G_STMT_START \ - { \ - NMLogLevel _level = (level); \ - \ - if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) { \ - NMPNetns *_netns = (netns); \ - char _sbuf[20]; \ - \ - _nm_log(_level, \ - _NMLOG_DOMAIN, \ - 0, \ - NULL, \ - NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - (_netns ? nm_sprintf_buf(_sbuf, "[%p]", _netns) \ - : "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ +#define _NMLOG(level, netns, ...) \ + G_STMT_START \ + { \ + NMLogLevel _level = (level); \ + \ + if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) { \ + NMPNetns *_netns = (netns); \ + char _sbuf[32]; \ + \ + _nm_log(_level, \ + _NMLOG_DOMAIN, \ + 0, \ + NULL, \ + NULL, \ + "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + (_netns ? nm_sprintf_buf(_sbuf, \ + "[" NM_HASH_OBFUSCATE_PTR_FMT "]", \ + NM_HASH_OBFUSCATE_PTR(_netns)) \ + : "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } \ G_STMT_END /*****************************************************************************/ @@ -194,7 +196,7 @@ _stack_current_netns(GArray *netns_stack, int ns_types) for (j = netns_stack->len; ns_types && j >= 1;) { NetnsInfo *info; - info = &g_array_index(netns_stack, NetnsInfo, --j); + info = &nm_g_array_index(netns_stack, NetnsInfo, --j); if (NM_FLAGS_ALL(info->ns_types, ns_types)) return info->netns; @@ -218,7 +220,7 @@ _stack_current_ns_types(GArray *netns_stack, NMPNetns *netns, int ns_types) for (j = netns_stack->len; ns_types && j >= 1;) { NetnsInfo *info; - info = &g_array_index(netns_stack, NetnsInfo, --j); + info = &nm_g_array_index(netns_stack, NetnsInfo, --j); if (info->netns != netns) { ns_types = NM_FLAGS_UNSET(ns_types, info->ns_types); continue; @@ -240,7 +242,7 @@ static NetnsInfo * _stack_peek(GArray *netns_stack) { if (netns_stack->len > 0) - return &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); + return &nm_g_array_last(netns_stack, NetnsInfo); return NULL; } @@ -248,7 +250,7 @@ static NetnsInfo * _stack_bottom(GArray *netns_stack) { if (netns_stack->len > 0) - return &g_array_index(netns_stack, NetnsInfo, 0); + return &nm_g_array_first(netns_stack, NetnsInfo); return NULL; } @@ -262,9 +264,7 @@ _stack_push(GArray *netns_stack, NMPNetns *netns, int ns_types) nm_assert(NM_FLAGS_ANY(ns_types, _CLONE_NS_ALL)); nm_assert(!NM_FLAGS_ANY(ns_types, ~_CLONE_NS_ALL)); - g_array_set_size(netns_stack, netns_stack->len + 1); - - info = &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); + info = nm_g_array_append_new(netns_stack, NetnsInfo); *info = (NetnsInfo){ .netns = g_object_ref(netns), .ns_types = ns_types, @@ -280,7 +280,7 @@ _stack_pop(GArray *netns_stack) nm_assert(netns_stack); nm_assert(netns_stack->len > 1); - info = &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); + info = &nm_g_array_last(netns_stack, NetnsInfo); nm_assert(NMP_IS_NETNS(info->netns)); nm_assert(info->count == 1); diff --git a/src/libnm-platform/nmp-netns.h b/src/libnm-platform/nmp-netns.h index b18bd03e..baeae4f1 100644 --- a/src/libnm-platform/nmp-netns.h +++ b/src/libnm-platform/nmp-netns.h @@ -11,7 +11,7 @@ /*****************************************************************************/ #define NMP_TYPE_NETNS (nmp_netns_get_type()) -#define NMP_NETNS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMP_TYPE_NETNS, NMPNetns)) +#define NMP_NETNS(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMP_TYPE_NETNS, NMPNetns)) #define NMP_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NMP_TYPE_NETNS, NMPNetnsClass)) #define NMP_IS_NETNS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMP_TYPE_NETNS)) #define NMP_IS_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMP_TYPE_NETNS)) diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c index db7bd314..9ba027ba 100644 --- a/src/libnm-platform/nmp-object.c +++ b/src/libnm-platform/nmp-object.c @@ -216,7 +216,7 @@ nm_sock_addr_union_cpy_untrusted(NMSockAddrUnion *dst, const char * nm_sock_addr_union_to_string(const NMSockAddrUnion *sa, char *buf, gsize len) { - char s_addr[NM_UTILS_INET_ADDRSTRLEN]; + char s_addr[NM_INET_ADDRSTRLEN]; char s_scope_id[40]; if (!nm_utils_to_string_buffer_init_null(sa, &buf, &len)) @@ -232,14 +232,14 @@ nm_sock_addr_union_to_string(const NMSockAddrUnion *sa, char *buf, gsize len) g_snprintf(buf, len, "%s:%u", - _nm_utils_inet4_ntop(sa->in.sin_addr.s_addr, s_addr), + nm_inet4_ntop(sa->in.sin_addr.s_addr, s_addr), (guint) htons(sa->in.sin_port)); break; case AF_INET6: g_snprintf(buf, len, "[%s%s]:%u", - _nm_utils_inet6_ntop(&sa->in6.sin6_addr, s_addr), + nm_inet6_ntop(&sa->in6.sin6_addr, s_addr), (sa->in6.sin6_scope_id != 0 ? nm_sprintf_buf(s_scope_id, "%u", sa->in6.sin6_scope_id) : ""), @@ -734,6 +734,12 @@ _vt_cmd_obj_dispose_link(NMPObject *obj) nmp_object_unref(obj->_link.netlink.lnk); } +static void +_vt_cmd_obj_dispose_ip4_route(NMPObject *obj) +{ + nm_clear_g_free((gpointer *) &obj->_ip4_route.extra_nexthops); +} + static void _vt_cmd_obj_dispose_lnk_vlan(NMPObject *obj) { @@ -762,17 +768,23 @@ _vt_cmd_obj_dispose_lnk_wireguard(NMPObject *obj) _wireguard_clear(&obj->_lnk_wireguard); } +static gsize +_NMP_OBJECT_STRUCT_SIZE(const NMPClass *klass) +{ + nm_assert(klass); + nm_assert(klass->sizeof_public > 0); + nm_assert(klass->sizeof_public <= klass->sizeof_data); + + return klass->sizeof_data + G_STRUCT_OFFSET(NMPObject, object); +} + static NMPObject * _nmp_object_new_from_class(const NMPClass *klass) { NMPObject *obj; - nm_assert(klass); - nm_assert(klass->sizeof_data > 0); - nm_assert(klass->sizeof_public > 0 && klass->sizeof_public <= klass->sizeof_data); - - obj = g_slice_alloc0(klass->sizeof_data + G_STRUCT_OFFSET(NMPObject, object)); - obj->_class = klass; + obj = g_slice_alloc0(_NMP_OBJECT_STRUCT_SIZE(klass)); + obj->_class = klass; obj->parent._ref_count = 1; return obj; } @@ -805,9 +817,8 @@ static NMPObject * _nmp_object_stackinit_from_class(NMPObject *obj, const NMPClass *klass) { nm_assert(obj); - nm_assert(klass); - memset(obj, 0, sizeof(NMPObject)); + memset(obj, 0, _NMP_OBJECT_STRUCT_SIZE(klass)); obj->_class = klass; obj->parent._ref_count = NM_OBJ_REF_COUNT_STACKINIT; return obj; @@ -843,14 +854,12 @@ nmp_object_stackinit_id(NMPObject *obj, const NMPObject *src) if (klass->cmd_plobj_id_copy) klass->cmd_plobj_id_copy(&obj->object, &src->object); else { - /* This object must not implement cmd_obj_copy(). - * If it would, it would mean that we require a deep copy - * of the data. As @obj is stack-allocated, it cannot track - * ownership. The caller must not use nmp_object_stackinit_id() - * with an object of such a type. */ - nm_assert(!klass->cmd_obj_copy); - - /* plain memcpy of the public part suffices. */ + /* plain memcpy. + * + * Note that for NMPObjectIP4Route this also copies extra_nexthops + * pointer, aliasing it without taking ownership. That is potentially + * dangerous, but when using a stack allocated instance, you must + * always take care of ownership. */ memcpy(&obj->object, &src->object, klass->sizeof_data); } return obj; @@ -907,30 +916,33 @@ nmp_object_to_string(const NMPObject *obj, klass = NMP_OBJECT_GET_CLASS(obj); - if (klass->cmd_obj_to_string) + if (klass->cmd_obj_to_string) { + nm_assert(!klass->cmd_plobj_to_string); + nm_assert(!klass->cmd_plobj_to_string_id); return klass->cmd_obj_to_string(obj, to_string_mode, buf, buf_size); + } + + nm_assert(klass->cmd_plobj_to_string); switch (to_string_mode) { case NMP_OBJECT_TO_STRING_ID: - if (!klass->cmd_plobj_to_string_id) { - g_snprintf(buf, buf_size, NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(obj)); - return buf; - } - return klass->cmd_plobj_to_string_id(&obj->object, buf, buf_size); + if (klass->cmd_plobj_to_string_id) + return klass->cmd_plobj_to_string_id(&obj->object, buf, buf_size); + g_snprintf(buf, buf_size, NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(obj)); + return buf; case NMP_OBJECT_TO_STRING_ALL: - g_snprintf( - buf, - buf_size, - "[%s," NM_HASH_OBFUSCATE_PTR_FMT ",%u,%calive,%cvisible; %s]", - klass->obj_type_name, - NM_HASH_OBFUSCATE_PTR(obj), - obj->parent._ref_count, - nmp_object_is_alive(obj) ? '+' : '-', - nmp_object_is_visible(obj) ? '+' : '-', - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf2, sizeof(buf2))); + g_snprintf(buf, + buf_size, + "[%s," NM_HASH_OBFUSCATE_PTR_FMT ",%u,%calive,%cvisible; %s]", + klass->obj_type_name, + NM_HASH_OBFUSCATE_PTR(obj), + obj->parent._ref_count, + nmp_object_is_alive(obj) ? '+' : '-', + nmp_object_is_visible(obj) ? '+' : '-', + klass->cmd_plobj_to_string(&obj->object, buf2, sizeof(buf2))); return buf; case NMP_OBJECT_TO_STRING_PUBLIC: - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf, buf_size); + klass->cmd_plobj_to_string(&obj->object, buf, buf_size); return buf; default: g_return_val_if_reached("ERROR"); @@ -948,7 +960,8 @@ _vt_cmd_obj_to_string_link(const NMPObject *obj, switch (to_string_mode) { case NMP_OBJECT_TO_STRING_ID: - return klass->cmd_plobj_to_string_id(&obj->object, buf, buf_size); + g_snprintf(buf, buf_size, "%d", obj->link.ifindex); + return buf; case NMP_OBJECT_TO_STRING_ALL: nm_strbuf_append(&b, &buf_size, @@ -961,7 +974,7 @@ _vt_cmd_obj_to_string_link(const NMPObject *obj, nmp_object_is_visible(obj) ? '+' : '-', obj->_link.netlink.is_in_netlink ? '+' : '-', NM_HASH_OBFUSCATE_PTR(obj->_link.udev.device)); - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, b, buf_size); + nm_platform_link_to_string(&obj->link, b, buf_size); nm_strbuf_seek_end(&b, &buf_size); if (obj->_link.netlink.lnk) { nm_strbuf_append_str(&b, &buf_size, "; "); @@ -971,7 +984,7 @@ _vt_cmd_obj_to_string_link(const NMPObject *obj, nm_strbuf_append_c(&b, &buf_size, ']'); return buf; case NMP_OBJECT_TO_STRING_PUBLIC: - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, b, buf_size); + nm_platform_link_to_string(&obj->link, b, buf_size); if (obj->_link.netlink.lnk) { nm_strbuf_seek_end(&b, &buf_size); nm_strbuf_append_str(&b, &buf_size, "; "); @@ -983,6 +996,41 @@ _vt_cmd_obj_to_string_link(const NMPObject *obj, } } +static const char * +_vt_cmd_obj_to_string_ip4_route(const NMPObject *obj, + NMPObjectToStringMode to_string_mode, + char *buf, + gsize buf_size) +{ + const NMPClass *klass; + char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; + + klass = NMP_OBJECT_GET_CLASS(obj); + + switch (to_string_mode) { + case NMP_OBJECT_TO_STRING_PUBLIC: + case NMP_OBJECT_TO_STRING_ID: + nm_platform_ip4_route_to_string_full(&obj->ip4_route, + obj->_ip4_route.extra_nexthops, + buf, + buf_size); + return buf; + case NMP_OBJECT_TO_STRING_ALL: + g_snprintf(buf, + buf_size, + "[%s," NM_HASH_OBFUSCATE_PTR_FMT ",%u,%calive,%cvisible; %s]", + klass->obj_type_name, + NM_HASH_OBFUSCATE_PTR(obj), + obj->parent._ref_count, + nmp_object_is_alive(obj) ? '+' : '-', + nmp_object_is_visible(obj) ? '+' : '-', + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof(buf2))); + return buf; + default: + g_return_val_if_reached("ERROR"); + } +} + static const char * _vt_cmd_obj_to_string_lnk_vlan(const NMPObject *obj, NMPObjectToStringMode to_string_mode, @@ -1013,7 +1061,7 @@ _vt_cmd_obj_to_string_lnk_vlan(const NMPObject *obj, nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof(buf2))); return buf; case NMP_OBJECT_TO_STRING_PUBLIC: - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf, buf_size); + nm_platform_lnk_vlan_to_string(&obj->lnk_vlan, buf, buf_size); b = buf; l = strlen(b); @@ -1092,8 +1140,7 @@ _vt_cmd_obj_to_string_lnk_wireguard(const NMPObject *obj, return buf; case NMP_OBJECT_TO_STRING_PUBLIC: - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf, buf_size); - + nm_platform_lnk_wireguard_to_string(&obj->lnk_wireguard, buf, buf_size); return buf; default: g_return_val_if_reached("ERROR"); @@ -1106,41 +1153,38 @@ _vt_cmd_obj_to_string_lnk_wireguard(const NMPObject *obj, gsize buf_len) \ { \ plat_type *const obj = (plat_type *) _obj; \ - _nm_unused char buf1[NM_UTILS_INET_ADDRSTRLEN]; \ - _nm_unused char buf2[NM_UTILS_INET_ADDRSTRLEN]; \ + _nm_unused char buf1[NM_INET_ADDRSTRLEN]; \ + _nm_unused char buf2[NM_INET_ADDRSTRLEN]; \ \ g_snprintf(buf, buf_len, __VA_ARGS__); \ return buf; \ } \ _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON -_vt_cmd_plobj_to_string_id(link, NMPlatformLink, "%d", obj->ifindex); - -_vt_cmd_plobj_to_string_id(ip4_address, - NMPlatformIP4Address, - "%d: %s/%d%s%s", - obj->ifindex, - _nm_utils_inet4_ntop(obj->address, buf1), - obj->plen, - obj->peer_address != obj->address ? "," : "", - obj->peer_address != obj->address ? _nm_utils_inet4_ntop( - nm_utils_ip4_address_clear_host_address(obj->peer_address, - obj->plen), - buf2) - : ""); +_vt_cmd_plobj_to_string_id( + ip4_address, + NMPlatformIP4Address, + "%d: %s/%d%s%s", + obj->ifindex, + nm_inet4_ntop(obj->address, buf1), + obj->plen, + obj->peer_address != obj->address ? "," : "", + obj->peer_address != obj->address + ? nm_inet4_ntop(nm_ip4_addr_clear_host_address(obj->peer_address, obj->plen), buf2) + : ""); _vt_cmd_plobj_to_string_id(ip6_address, NMPlatformIP6Address, "%d: %s", obj->ifindex, - _nm_utils_inet6_ntop(&obj->address, buf1)); + nm_inet6_ntop(&obj->address, buf1)); _vt_cmd_plobj_to_string_id(qdisc, NMPlatformQdisc, "%d: %d", obj->ifindex, obj->parent); _vt_cmd_plobj_to_string_id(tfilter, NMPlatformTfilter, "%d: %d", obj->ifindex, obj->parent); void -nmp_object_hash_update(const NMPObject *obj, NMHashState *h) +nmp_object_hash_update_full(const NMPObject *obj, gboolean for_id, NMHashState *h) { const NMPClass *klass; @@ -1148,20 +1192,44 @@ nmp_object_hash_update(const NMPObject *obj, NMHashState *h) klass = NMP_OBJECT_GET_CLASS(obj); + nm_assert((!!klass->cmd_plobj_id_cmp) == (!!klass->cmd_plobj_id_hash_update)); + nm_assert((!!klass->cmd_obj_cmp) == (!!klass->cmd_obj_hash_update)); + nm_assert((!!klass->cmd_plobj_cmp) == (!!klass->cmd_plobj_hash_update)); + + nm_assert((!!klass->cmd_obj_hash_update) ^ (!!klass->cmd_plobj_hash_update)); + nm_assert((!klass->cmd_obj_hash_update) || (!klass->cmd_plobj_id_hash_update)); + nm_hash_update_val(h, klass->obj_type); + + if (for_id) { + if (klass->cmd_obj_hash_update) + klass->cmd_obj_hash_update(obj, TRUE, h); + else if (klass->cmd_plobj_id_hash_update) + klass->cmd_plobj_id_hash_update(&obj->object, h); + else { + /* The klass doesn't implement ID compare. It means, to use pointer + * equality. */ + nm_hash_update_val(h, obj); + } + return; + } + if (klass->cmd_obj_hash_update) - klass->cmd_obj_hash_update(obj, h); - else if (klass->cmd_plobj_hash_update) - klass->cmd_plobj_hash_update(&obj->object, h); + klass->cmd_obj_hash_update(obj, FALSE, h); else - nm_hash_update_val(h, obj); + klass->cmd_plobj_hash_update(&obj->object, h); } static void -_vt_cmd_obj_hash_update_link(const NMPObject *obj, NMHashState *h) +_vt_cmd_obj_hash_update_link(const NMPObject *obj, gboolean for_id, NMHashState *h) { nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LINK); + if (for_id) { + nm_hash_update_val(h, obj->link.ifindex); + return; + } + nm_platform_link_hash_update(&obj->link, h); nm_hash_update_vals(h, obj->_link.netlink.is_in_netlink, obj->_link.udev.device); if (obj->_link.netlink.lnk) @@ -1169,10 +1237,30 @@ _vt_cmd_obj_hash_update_link(const NMPObject *obj, NMHashState *h) } static void -_vt_cmd_obj_hash_update_lnk_vlan(const NMPObject *obj, NMHashState *h) +_vt_cmd_obj_hash_update_ip4_route(const NMPObject *obj, gboolean for_id, NMHashState *h) +{ + guint i; + + nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP4_ROUTE); + + nm_platform_ip4_route_hash_update(&obj->ip4_route, + for_id ? NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID + : NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, + h); + for (i = 1u; i < obj->ip4_route.n_nexthops; i++) + nm_platform_ip4_rt_nexthop_hash_update(&obj->_ip4_route.extra_nexthops[i - 1u], for_id, h); +} + +static void +_vt_cmd_obj_hash_update_lnk_vlan(const NMPObject *obj, gboolean for_id, NMHashState *h) { nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LNK_VLAN); + if (for_id) { + nm_hash_update_val(h, obj); + return; + } + nm_platform_lnk_vlan_hash_update(&obj->lnk_vlan, h); _vlan_xgress_qos_mappings_hash_update(obj->_lnk_vlan.n_ingress_qos_map, obj->_lnk_vlan.ingress_qos_map, @@ -1183,12 +1271,17 @@ _vt_cmd_obj_hash_update_lnk_vlan(const NMPObject *obj, NMHashState *h) } static void -_vt_cmd_obj_hash_update_lnk_wireguard(const NMPObject *obj, NMHashState *h) +_vt_cmd_obj_hash_update_lnk_wireguard(const NMPObject *obj, gboolean for_id, NMHashState *h) { guint i; nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LNK_WIREGUARD); + if (for_id) { + nm_hash_update_val(h, obj); + return; + } + nm_platform_lnk_wireguard_hash_update(&obj->lnk_wireguard, h); nm_hash_update_val(h, obj->_lnk_wireguard.peers_len); @@ -1199,47 +1292,85 @@ _vt_cmd_obj_hash_update_lnk_wireguard(const NMPObject *obj, NMHashState *h) int nmp_object_cmp_full(const NMPObject *obj1, const NMPObject *obj2, NMPObjectCmpFlags flags) { - const NMPClass *klass1; + const NMPClass *klass; const NMPClass *klass2; NMPObject obj_stackcopy; + nm_assert( + !NM_FLAGS_ANY(flags, ~(NMP_OBJECT_CMP_FLAGS_ID | NMP_OBJECT_CMP_FLAGS_IGNORE_IFINDEX))); + + /* The ID flag (currently) cannot be combined with other flags. That's partly because + * it's not implemented, but also because some objects use only pointer equality. So it's + * not clear how that combines with other flags (well, they'd be ignored). */ + nm_assert(!NM_FLAGS_HAS(flags, NMP_OBJECT_CMP_FLAGS_ID) || flags == NMP_OBJECT_CMP_FLAGS_ID); + NM_CMP_SELF(obj1, obj2); g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj1), -1); g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj2), 1); - klass1 = NMP_OBJECT_GET_CLASS(obj1); + klass = NMP_OBJECT_GET_CLASS(obj1); + + nm_assert(klass); + + nm_assert((!!klass->cmd_plobj_id_cmp) == (!!klass->cmd_plobj_id_hash_update)); + nm_assert((!!klass->cmd_obj_cmp) == (!!klass->cmd_obj_hash_update)); + nm_assert((!!klass->cmd_plobj_cmp) == (!!klass->cmd_plobj_hash_update)); + + nm_assert((!!klass->cmd_obj_cmp) ^ (!!klass->cmd_plobj_cmp)); + nm_assert((!klass->cmd_obj_cmp) || (!klass->cmd_plobj_id_cmp)); + klass2 = NMP_OBJECT_GET_CLASS(obj2); - if (klass1 != klass2) { - nm_assert(klass1->obj_type != klass2->obj_type); - return klass1->obj_type < klass2->obj_type ? -1 : 1; + if (klass != klass2) { + nm_assert(klass2); + NM_CMP_DIRECT(klass->obj_type, klass2->obj_type); + return nm_assert_unreachable_val(0); + } + + if (NM_FLAGS_HAS(flags, NMP_OBJECT_CMP_FLAGS_ID)) { + if (klass->cmd_obj_cmp) + return klass->cmd_obj_cmp(obj1, obj2, TRUE); + if (klass->cmd_plobj_id_cmp) + return klass->cmd_plobj_id_cmp(&obj1->object, &obj2->object); + + /* the klass doesn't implement ID cmp(). That means, different objects + * never compare equal, but the cmp() according to their pointer value. */ + NM_CMP_DIRECT_PTR(obj1, obj2); + return nm_assert_unreachable_val(0); } if (NM_FLAGS_HAS(flags, NMP_OBJECT_CMP_FLAGS_IGNORE_IFINDEX)) { - if (!NM_IN_SET(klass1, - nmp_class_from_type(NMP_OBJECT_TYPE_IP4_ADDRESS), - nmp_class_from_type(NMP_OBJECT_TYPE_IP6_ADDRESS), - nmp_class_from_type(NMP_OBJECT_TYPE_IP4_ROUTE), - nmp_class_from_type(NMP_OBJECT_TYPE_IP6_ROUTE))) { + if (!NM_IN_SET(klass->obj_type, + NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)) { /* This flag is currently only implemented for certain types. * That is, because we just create a stack copy, and that naive * approach only knows for types where we know that it works. */ } else if (obj1->obj_with_ifindex.ifindex != obj2->obj_with_ifindex.ifindex) { - nmp_object_stackinit(&obj_stackcopy, klass1->obj_type, &obj2->obj_with_ifindex); + nmp_object_stackinit(&obj_stackcopy, klass->obj_type, &obj2->obj_with_ifindex); obj_stackcopy.obj_with_ifindex.ifindex = obj1->obj_with_ifindex.ifindex; - obj2 = &obj_stackcopy; + if (klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) + obj_stackcopy._ip4_route.extra_nexthops = obj2->_ip4_route.extra_nexthops; + obj2 = &obj_stackcopy; } } - if (klass1->cmd_obj_cmp) - return klass1->cmd_obj_cmp(obj1, obj2); - return klass1->cmd_plobj_cmp(&obj1->object, &obj2->object); + if (klass->cmd_obj_cmp) + return klass->cmd_obj_cmp(obj1, obj2, FALSE); + return klass->cmd_plobj_cmp(&obj1->object, &obj2->object); } static int -_vt_cmd_obj_cmp_link(const NMPObject *obj1, const NMPObject *obj2) +_vt_cmd_obj_cmp_link(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id) { + if (for_id) { + NM_CMP_FIELD(obj1, obj2, link.ifindex); + return 0; + } + NM_CMP_RETURN(nm_platform_link_cmp(&obj1->link, &obj2->link)); NM_CMP_DIRECT(obj1->_link.netlink.is_in_netlink, obj2->_link.netlink.is_in_netlink); NM_CMP_RETURN(nmp_object_cmp(obj1->_link.netlink.lnk, obj2->_link.netlink.lnk)); @@ -1261,10 +1392,37 @@ _vt_cmd_obj_cmp_link(const NMPObject *obj1, const NMPObject *obj2) } static int -_vt_cmd_obj_cmp_lnk_vlan(const NMPObject *obj1, const NMPObject *obj2) +_vt_cmd_obj_cmp_ip4_route(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id) +{ + int c; + guint i; + + c = nm_platform_ip4_route_cmp(&obj1->ip4_route, + &obj2->ip4_route, + for_id ? NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID + : NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); + NM_CMP_RETURN_DIRECT(c); + + for (i = 1u; i < obj1->ip4_route.n_nexthops; i++) { + c = nm_platform_ip4_rt_nexthop_cmp(&obj1->_ip4_route.extra_nexthops[i - 1u], + &obj2->_ip4_route.extra_nexthops[i - 1u], + for_id); + NM_CMP_RETURN_DIRECT(c); + } + + return 0; +} + +static int +_vt_cmd_obj_cmp_lnk_vlan(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id) { int c; + if (for_id) { + NM_CMP_DIRECT_PTR(obj1, obj2); + return nm_assert_unreachable_val(0); + } + c = nm_platform_lnk_vlan_cmp(&obj1->lnk_vlan, &obj2->lnk_vlan); if (c) return c; @@ -1287,10 +1445,15 @@ _vt_cmd_obj_cmp_lnk_vlan(const NMPObject *obj1, const NMPObject *obj2) } static int -_vt_cmd_obj_cmp_lnk_wireguard(const NMPObject *obj1, const NMPObject *obj2) +_vt_cmd_obj_cmp_lnk_wireguard(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id) { guint i; + if (for_id) { + NM_CMP_DIRECT_PTR(obj1, obj2); + return nm_assert_unreachable_val(0); + } + NM_CMP_RETURN(nm_platform_lnk_wireguard_cmp(&obj1->lnk_wireguard, &obj2->lnk_wireguard)); NM_CMP_FIELD(obj1, obj2, _lnk_wireguard.peers_len); @@ -1353,6 +1516,28 @@ _vt_cmd_obj_copy_link(NMPObject *dst, const NMPObject *src) dst->_link = src->_link; } +static void +_vt_cmd_obj_copy_ip4_route(NMPObject *dst, const NMPObject *src) +{ + nm_assert(dst != src); + + if (src->ip4_route.n_nexthops <= 1) { + nm_clear_g_free((gpointer *) &dst->_ip4_route.extra_nexthops); + } else if (src->ip4_route.n_nexthops != dst->ip4_route.n_nexthops + || !nm_memeq_n(src->_ip4_route.extra_nexthops, + src->ip4_route.n_nexthops - 1u, + dst->_ip4_route.extra_nexthops, + dst->ip4_route.n_nexthops - 1u, + sizeof(NMPlatformIP4RtNextHop))) { + nm_clear_g_free((gpointer *) &dst->_ip4_route.extra_nexthops); + dst->_ip4_route.extra_nexthops = + nm_memdup(src->_ip4_route.extra_nexthops, + sizeof(NMPlatformIP4RtNextHop) * (src->ip4_route.n_nexthops - 1u)); + } + + dst->ip4_route = src->ip4_route; +} + static void _vt_cmd_obj_copy_lnk_vlan(NMPObject *dst, const NMPObject *src) { @@ -1451,39 +1636,6 @@ nmp_object_clone(const NMPObject *obj, gboolean id_only) return dst; } -int -nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2) -{ - const NMPClass *klass, *klass2; - - NM_CMP_SELF(obj1, obj2); - - g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj1), FALSE); - g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj2), FALSE); - - klass = NMP_OBJECT_GET_CLASS(obj1); - nm_assert(!klass->cmd_plobj_id_hash_update == !klass->cmd_plobj_id_cmp); - - klass2 = NMP_OBJECT_GET_CLASS(obj2); - nm_assert(klass); - if (klass != klass2) { - nm_assert(klass2); - NM_CMP_DIRECT(klass->obj_type, klass2->obj_type); - /* resort to pointer comparison */ - NM_CMP_DIRECT_PTR(klass, klass2); - return 0; - } - - if (!klass->cmd_plobj_id_cmp) { - /* the klass doesn't implement ID cmp(). That means, different objects - * never compare equal, but the cmp() according to their pointer value. */ - NM_CMP_DIRECT_PTR(obj1, obj2); - return 0; - } - - return klass->cmd_plobj_id_cmp(&obj1->object, &obj2->object); -} - #define _vt_cmd_plobj_id_cmp(type, plat_type, cmd) \ static int _vt_cmd_plobj_id_cmp_##type(const NMPlatformObject *_obj1, \ const NMPlatformObject *_obj2) \ @@ -1499,8 +1651,6 @@ nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2) } \ _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON -_vt_cmd_plobj_id_cmp(link, NMPlatformLink, { NM_CMP_FIELD(obj1, obj2, ifindex); }); - static int _vt_cmd_plobj_id_cmp_ip4_address(const NMPlatformObject *obj1, const NMPlatformObject *obj2) { @@ -1527,14 +1677,6 @@ _vt_cmd_plobj_id_cmp(tfilter, NMPlatformTfilter, { NM_CMP_FIELD(obj1, obj2, handle); }); -static int -_vt_cmd_plobj_id_cmp_ip4_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) -{ - return nm_platform_ip4_route_cmp((const NMPlatformIP4Route *) obj1, - (const NMPlatformIP4Route *) obj2, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); -} - static int _vt_cmd_plobj_id_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) { @@ -1582,28 +1724,6 @@ _vt_cmd_plobj_id_cmp(mptcp_addr, NMPlatformMptcpAddr, { NM_CMP_FIELD(obj1, obj2, port); }); -void -nmp_object_id_hash_update(const NMPObject *obj, NMHashState *h) -{ - const NMPClass *klass; - - g_return_if_fail(NMP_OBJECT_IS_VALID(obj)); - - klass = NMP_OBJECT_GET_CLASS(obj); - - nm_assert(!klass->cmd_plobj_id_hash_update == !klass->cmd_plobj_id_cmp); - - if (!klass->cmd_plobj_id_hash_update) { - /* The klass doesn't implement ID compare. It means, to use pointer - * equality. */ - nm_hash_update_val(h, obj); - return; - } - - nm_hash_update_val(h, klass->obj_type); - klass->cmd_plobj_id_hash_update(&obj->object, h); -} - guint nmp_object_id_hash(const NMPObject *obj) { @@ -1627,8 +1747,6 @@ nmp_object_id_hash(const NMPObject *obj) } \ _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON -_vt_cmd_plobj_id_hash_update(link, NMPlatformLink, { nm_hash_update_val(h, obj->ifindex); }); - _vt_cmd_plobj_id_hash_update(ip4_address, NMPlatformIP4Address, { nm_hash_update_vals( h, @@ -1636,7 +1754,7 @@ _vt_cmd_plobj_id_hash_update(ip4_address, NMPlatformIP4Address, { obj->plen, obj->address, /* for IPv4 we must also consider the net-part of the peer-address (IFA_ADDRESS) */ - nm_utils_ip4_address_clear_host_address(obj->peer_address, obj->plen)); + nm_ip4_addr_clear_host_address(obj->peer_address, obj->plen)); }); _vt_cmd_plobj_id_hash_update(ip6_address, NMPlatformIP6Address, { @@ -1647,10 +1765,6 @@ _vt_cmd_plobj_id_hash_update(ip6_address, NMPlatformIP6Address, { obj->address); }); -_vt_cmd_plobj_id_hash_update(ip4_route, NMPlatformIP4Route, { - nm_platform_ip4_route_hash_update(obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); -}); - _vt_cmd_plobj_id_hash_update(ip6_route, NMPlatformIP6Route, { nm_platform_ip6_route_hash_update(obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); }); @@ -1674,19 +1788,19 @@ _vt_cmd_plobj_id_hash_update(mptcp_addr, NMPlatformMptcpAddr, { }); static void -_vt_cmd_plobj_hash_update_ip4_route(const NMPlatformObject *obj, NMHashState *h) +_vt_cmd_plobj_hash_update_ip6_route(const NMPlatformObject *obj, NMHashState *h) { - return nm_platform_ip4_route_hash_update((const NMPlatformIP4Route *) obj, + return nm_platform_ip6_route_hash_update((const NMPlatformIP6Route *) obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, h); } -static void -_vt_cmd_plobj_hash_update_ip6_route(const NMPlatformObject *obj, NMHashState *h) +static int +_vt_cmd_plobj_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) { - return nm_platform_ip6_route_hash_update((const NMPlatformIP6Route *) obj, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, - h); + return nm_platform_ip6_route_cmp((const NMPlatformIP6Route *) obj1, + (const NMPlatformIP6Route *) obj2, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); } static void @@ -1697,6 +1811,24 @@ _vt_cmd_plobj_hash_update_routing_rule(const NMPlatformObject *obj, NMHashState h); } +static inline int +_vt_cmd_plobj_cmp_routing_rule(const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_routing_rule_cmp((const NMPlatformRoutingRule *) obj1, + (const NMPlatformRoutingRule *) obj2, + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL); +} + +static int +_vt_cmd_plobj_cmp_qdisc(const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_qdisc_cmp((const NMPlatformQdisc *) obj1, + (const NMPlatformQdisc *) obj2, + TRUE); +} + +/*****************************************************************************/ + guint nmp_object_indirect_id_hash(gconstpointer a) { @@ -1859,7 +1991,7 @@ _vt_dedup_obj_destroy(NMDedupMultiObj *obj) klass = o->_class; if (klass->cmd_obj_dispose) klass->cmd_obj_dispose(o); - g_slice_free1(klass->sizeof_data + G_STRUCT_OFFSET(NMPObject, object), o); + g_slice_free1(_NMP_OBJECT_STRUCT_SIZE(klass), o); } static const NMDedupMultiObj * @@ -2827,6 +2959,7 @@ nmp_cache_update_netlink_route(NMPCache *cache, NMPObject *obj_hand_over, gboolean is_dump, guint16 nlmsgflags, + gboolean route_is_alive, const NMPObject **out_obj_old, const NMPObject **out_obj_new, const NMPObject **out_obj_replace, @@ -2845,30 +2978,60 @@ nmp_cache_update_netlink_route(NMPCache *cache, nm_assert(cache); nm_assert(NMP_OBJECT_IS_VALID(obj_hand_over)); nm_assert(!NMP_OBJECT_IS_STACKINIT(obj_hand_over)); - /* A link object from netlink must have the udev related fields unset. - * We could implement to handle that, but there is no need to support such - * a use-case */ nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_hand_over), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); nm_assert(nm_dedup_multi_index_obj_find(cache->multi_idx, obj_hand_over) != obj_hand_over); + if (NM_FLAGS_HAS(nlmsgflags, NLM_F_REPLACE)) { + /* This means, that the message indicates that another route was replaced. + * Since we don't cache all routes (see "route_is_alive"), we cannot know + * with certainty which route was replaced. + * + * Even if we would cache *all* routes (which we cannot, if kernel adds new + * routing features that modify the known nmp_object_id_equal()), it would + * be hard to find the right route that was replaced. Well, probably we + * would have to keep NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID sorted by order + * of notifications, which is hard. The code below actually makes an effort + * to do that, but it's not actually used, because we just resync. + * + * The only proper solution for this would be to improve kernel with [1] + * and [2]. + * + * [1] https://bugzilla.redhat.com/show_bug.cgi?id=1337855 + * [2] https://bugzilla.redhat.com/show_bug.cgi?id=1337860 + * + * We need to resync. + */ + if (NMP_OBJECT_GET_TYPE(obj_hand_over) == NMP_OBJECT_TYPE_IP4_ROUTE + && !nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) { + /* For IPv4, we can do a small optimization. We skip the resync, if we have + * no conflicting routes (by weak-id). + * + * This optimization does not work for IPv6 (maybe should be fixed). + */ + } else { + entry_replace = NULL; + resync_required = TRUE; + goto out; + } + } + entry_old = _lookup_entry(cache, obj_hand_over); entry_new = NULL; NM_SET_OUT(out_obj_old, nmp_object_ref(nm_dedup_multi_entry_get_obj(entry_old))); - if (!entry_old) { - if (!nmp_object_is_alive(obj_hand_over)) - goto update_done; + is_alive = route_is_alive && nmp_object_is_alive(obj_hand_over); - _idxcache_update(cache, NULL, obj_hand_over, is_dump, &entry_new); - ops_type = NMP_CACHE_OPS_ADDED; + if (!entry_old) { + if (is_alive) { + _idxcache_update(cache, NULL, obj_hand_over, is_dump, &entry_new); + ops_type = NMP_CACHE_OPS_ADDED; + } goto update_done; } - is_alive = nmp_object_is_alive(obj_hand_over); - if (!is_alive) { /* the update would make @entry_old invalid. Remove it. */ _idxcache_update(cache, entry_old, NULL, FALSE, NULL); @@ -2901,19 +3064,11 @@ update_done: if (is_dump) goto out; - if (!entry_new) { - if (NM_FLAGS_HAS(nlmsgflags, NLM_F_REPLACE) - && nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) { - /* hm. @obj_hand_over was not added, meaning it was not alive. - * However, we track some other objects with the same weak-id. - * It's unclear what that means. To be sure, resync. */ - resync_required = TRUE; - } + if (!entry_new) goto out; - } - /* FIXME: for routes, we only maintain the order correctly for the BY_WEAK_ID - * index. For all other indexes their order becomes messed up. */ + /* For routes, we only maintain the order correctly for the BY_WEAK_ID + * index. For all other indexes, their order is not preserved. */ entry_cur = _lookup_entry_with_idx_type(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, entry_new->obj); if (!entry_cur) { @@ -3138,29 +3293,23 @@ typedef int (*CmdPlobjCmpFunc)(const NMPlatformObject *obj1, const NMPlatformObj const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { [NMP_OBJECT_TYPE_LINK - 1] = { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LINK, - .sizeof_data = sizeof(NMPObjectLink), - .sizeof_public = sizeof(NMPlatformLink), - .obj_type_name = "link", - .rtm_gettype = RTM_GETLINK, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_LINK, - .signal_type = NM_PLATFORM_SIGNAL_LINK_CHANGED, - .supported_cache_ids = _supported_cache_ids_link, - .cmd_obj_hash_update = _vt_cmd_obj_hash_update_link, - .cmd_obj_cmp = _vt_cmd_obj_cmp_link, - .cmd_obj_copy = _vt_cmd_obj_copy_link, - .cmd_obj_dispose = _vt_cmd_obj_dispose_link, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_link, - .cmd_obj_is_visible = _vt_cmd_obj_is_visible_link, - .cmd_obj_to_string = _vt_cmd_obj_to_string_link, - .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_link, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_link, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_link, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_link, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_link_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_link_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_link_cmp, + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_LINK, + .sizeof_data = sizeof(NMPObjectLink), + .sizeof_public = sizeof(NMPlatformLink), + .obj_type_name = "link", + .rtm_gettype = RTM_GETLINK, + .signal_type_id = NM_PLATFORM_SIGNAL_ID_LINK, + .signal_type = NM_PLATFORM_SIGNAL_LINK_CHANGED, + .supported_cache_ids = _supported_cache_ids_link, + .cmd_obj_hash_update = _vt_cmd_obj_hash_update_link, + .cmd_obj_cmp = _vt_cmd_obj_cmp_link, + .cmd_obj_copy = _vt_cmd_obj_copy_link, + .cmd_obj_dispose = _vt_cmd_obj_dispose_link, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_link, + .cmd_obj_is_visible = _vt_cmd_obj_is_visible_link, + .cmd_obj_to_string = _vt_cmd_obj_to_string_link, + .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_link, }, [NMP_OBJECT_TYPE_IP4_ADDRESS - 1] = { @@ -3206,23 +3355,22 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { }, [NMP_OBJECT_TYPE_IP4_ROUTE - 1] = { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, - .sizeof_data = sizeof(NMPObjectIP4Route), - .sizeof_public = sizeof(NMPlatformIP4Route), - .obj_type_name = "ip4-route", - .addr_family = AF_INET, - .rtm_gettype = RTM_GETROUTE, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, - .signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - .supported_cache_ids = _supported_cache_ids_ipx_route, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip4_route, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip4_route, - .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_ip4_route_to_string, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip4_route_to_string, - .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip4_route, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip4_route_cmp_full, + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, + .sizeof_data = sizeof(NMPObjectIP4Route), + .sizeof_public = sizeof(NMPlatformIP4Route), + .obj_type_name = "ip4-route", + .addr_family = AF_INET, + .rtm_gettype = RTM_GETROUTE, + .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, + .signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, + .supported_cache_ids = _supported_cache_ids_ipx_route, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, + .cmd_obj_hash_update = _vt_cmd_obj_hash_update_ip4_route, + .cmd_obj_cmp = _vt_cmd_obj_cmp_ip4_route, + .cmd_obj_copy = _vt_cmd_obj_copy_ip4_route, + .cmd_obj_dispose = _vt_cmd_obj_dispose_ip4_route, + .cmd_obj_to_string = _vt_cmd_obj_to_string_ip4_route, }, [NMP_OBJECT_TYPE_IP6_ROUTE - 1] = { @@ -3242,7 +3390,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_ip6_route_to_string, .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_route_to_string, .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip6_route, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip6_route_cmp_full, + .cmd_plobj_cmp = _vt_cmd_plobj_cmp_ip6_route, }, [NMP_OBJECT_TYPE_ROUTING_RULE - 1] = { @@ -3261,7 +3409,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_routing_rule_to_string, .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_routing_rule_to_string, .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_routing_rule, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_routing_rule_cmp_full, + .cmd_plobj_cmp = _vt_cmd_plobj_cmp_routing_rule, }, [NMP_OBJECT_TYPE_QDISC - 1] = { @@ -3280,7 +3428,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_qdisc, .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_qdisc_to_string, .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_qdisc_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_qdisc_cmp, + .cmd_plobj_cmp = _vt_cmd_plobj_cmp_qdisc, }, [NMP_OBJECT_TYPE_TFILTER - 1] = { @@ -3460,20 +3608,17 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { }, [NMP_OBJECT_TYPE_LNK_VLAN - 1] = { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_VLAN, - .sizeof_data = sizeof(NMPObjectLnkVlan), - .sizeof_public = sizeof(NMPlatformLnkVlan), - .obj_type_name = "vlan", - .lnk_link_type = NM_LINK_TYPE_VLAN, - .cmd_obj_hash_update = _vt_cmd_obj_hash_update_lnk_vlan, - .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_vlan, - .cmd_obj_copy = _vt_cmd_obj_copy_lnk_vlan, - .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_vlan, - .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_vlan, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_vlan_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vlan_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vlan_cmp, + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_LNK_VLAN, + .sizeof_data = sizeof(NMPObjectLnkVlan), + .sizeof_public = sizeof(NMPlatformLnkVlan), + .obj_type_name = "vlan", + .lnk_link_type = NM_LINK_TYPE_VLAN, + .cmd_obj_hash_update = _vt_cmd_obj_hash_update_lnk_vlan, + .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_vlan, + .cmd_obj_copy = _vt_cmd_obj_copy_lnk_vlan, + .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_vlan, + .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_vlan, }, [NMP_OBJECT_TYPE_LNK_VRF - 1] = { @@ -3487,6 +3632,30 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vrf_hash_update, .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vrf_cmp, }, + [NMP_OBJECT_TYPE_LNK_VTI - 1] = + { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_LNK_VTI, + .sizeof_data = sizeof(NMPObjectLnkVti), + .sizeof_public = sizeof(NMPlatformLnkVti), + .obj_type_name = "vti", + .lnk_link_type = NM_LINK_TYPE_VTI, + .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_vti_to_string, + .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vti_hash_update, + .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vti_cmp, + }, + [NMP_OBJECT_TYPE_LNK_VTI6 - 1] = + { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_LNK_VTI6, + .sizeof_data = sizeof(NMPObjectLnkVti6), + .sizeof_public = sizeof(NMPlatformLnkVti6), + .obj_type_name = "vti6", + .lnk_link_type = NM_LINK_TYPE_VTI6, + .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_vti6_to_string, + .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vti6_hash_update, + .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vti6_cmp, + }, [NMP_OBJECT_TYPE_LNK_VXLAN - 1] = { .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), @@ -3501,20 +3670,17 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { }, [NMP_OBJECT_TYPE_LNK_WIREGUARD - 1] = { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_WIREGUARD, - .sizeof_data = sizeof(NMPObjectLnkWireGuard), - .sizeof_public = sizeof(NMPlatformLnkWireGuard), - .obj_type_name = "wireguard", - .lnk_link_type = NM_LINK_TYPE_WIREGUARD, - .cmd_obj_hash_update = _vt_cmd_obj_hash_update_lnk_wireguard, - .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_wireguard, - .cmd_obj_copy = _vt_cmd_obj_copy_lnk_wireguard, - .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_wireguard, - .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_wireguard, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_wireguard_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_wireguard_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_wireguard_cmp, + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_LNK_WIREGUARD, + .sizeof_data = sizeof(NMPObjectLnkWireGuard), + .sizeof_public = sizeof(NMPlatformLnkWireGuard), + .obj_type_name = "wireguard", + .lnk_link_type = NM_LINK_TYPE_WIREGUARD, + .cmd_obj_hash_update = _vt_cmd_obj_hash_update_lnk_wireguard, + .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_wireguard, + .cmd_obj_copy = _vt_cmd_obj_copy_lnk_wireguard, + .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_wireguard, + .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_wireguard, }, [NMP_OBJECT_TYPE_LNK_BOND - 1] = { diff --git a/src/libnm-platform/nmp-object.h b/src/libnm-platform/nmp-object.h index 25156fe2..0d5f84b3 100644 --- a/src/libnm-platform/nmp-object.h +++ b/src/libnm-platform/nmp-object.h @@ -183,25 +183,26 @@ typedef struct { /* Only for NMPObjectLnk* types. */ NMLinkType lnk_link_type; - void (*cmd_obj_hash_update)(const NMPObject *obj, NMHashState *h); - int (*cmd_obj_cmp)(const NMPObject *obj1, const NMPObject *obj2); - void (*cmd_obj_copy)(NMPObject *dst, const NMPObject *src); - void (*cmd_obj_dispose)(NMPObject *obj); gboolean (*cmd_obj_is_alive)(const NMPObject *obj); gboolean (*cmd_obj_is_visible)(const NMPObject *obj); + void (*cmd_obj_copy)(NMPObject *dst, const NMPObject *src); + void (*cmd_obj_dispose)(NMPObject *obj); + + void (*cmd_obj_hash_update)(const NMPObject *obj, gboolean for_id, NMHashState *h); + int (*cmd_obj_cmp)(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id); const char *(*cmd_obj_to_string)(const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size); /* functions that operate on NMPlatformObject */ + void (*cmd_plobj_hash_update)(const NMPlatformObject *obj, NMHashState *h); + int (*cmd_plobj_cmp)(const NMPlatformObject *obj1, const NMPlatformObject *obj2); void (*cmd_plobj_id_copy)(NMPlatformObject *dst, const NMPlatformObject *src); int (*cmd_plobj_id_cmp)(const NMPlatformObject *obj1, const NMPlatformObject *obj2); void (*cmd_plobj_id_hash_update)(const NMPlatformObject *obj, NMHashState *h); const char *(*cmd_plobj_to_string_id)(const NMPlatformObject *obj, char *buf, gsize buf_size); const char *(*cmd_plobj_to_string)(const NMPlatformObject *obj, char *buf, gsize len); - void (*cmd_plobj_hash_update)(const NMPlatformObject *obj, NMHashState *h); - int (*cmd_plobj_cmp)(const NMPlatformObject *obj1, const NMPlatformObject *obj2); } NMPClass; extern const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX]; @@ -295,6 +296,14 @@ typedef struct { NMPlatformLnkVrf _public; } NMPObjectLnkVrf; +typedef struct { + NMPlatformLnkVti _public; +} NMPObjectLnkVti; + +typedef struct { + NMPlatformLnkVti6 _public; +} NMPObjectLnkVti6; + typedef struct { NMPlatformLnkVxlan _public; } NMPObjectLnkVxlan; @@ -313,6 +322,13 @@ typedef struct { typedef struct { NMPlatformIP4Route _public; + + /* The first hop is embedded in _public (in the + * ifindex, gateway and weight fields). + * Only if _public.n_nexthops is greater than 1, then + * this contains the remaining(!!) (_public.n_nexthops - 1) + * extra hops for ECMP multihop routes. */ + const NMPlatformIP4RtNextHop *extra_nexthops; } NMPObjectIP4Route; typedef struct { @@ -388,6 +404,12 @@ struct _NMPObject { NMPlatformLnkVrf lnk_vrf; NMPObjectLnkVrf _lnk_vrf; + NMPlatformLnkVti lnk_vti; + NMPObjectLnkVti _lnk_vti; + + NMPlatformLnkVti6 lnk_vti6; + NMPObjectLnkVti6 _lnk_vti6; + NMPlatformLnkVxlan lnk_vxlan; NMPObjectLnkVxlan _lnk_vxlan; @@ -419,7 +441,7 @@ struct _NMPObject { NMPlatformMptcpAddr mptcp_addr; NMPObjectMptcpAddr _mptcp_addr; }; -}; +} _nm_alignas(NMDedupMultiObj); /*****************************************************************************/ @@ -446,9 +468,12 @@ NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj) { NMPObject *obj; - obj = plobj ? (NMPObject *) (&(((char *) plobj)[-((int) G_STRUCT_OFFSET(NMPObject, object))])) + obj = plobj ? NM_CAST_ALIGN(NMPObject, + &(((char *) plobj)[-((int) G_STRUCT_OFFSET(NMPObject, object))])) : NULL; + nm_assert(!obj || (obj->parent._ref_count > 0 && NMP_CLASS_IS_VALID(obj->_class))); + return obj; } #define NMP_OBJECT_UP_CAST(plobj) (NMP_OBJECT_UP_CAST((const NMPlatformObject *) (plobj))) @@ -517,6 +542,8 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type) case NMP_OBJECT_TYPE_LNK_TUN: case NMP_OBJECT_TYPE_LNK_VLAN: case NMP_OBJECT_TYPE_LNK_VRF: + case NMP_OBJECT_TYPE_LNK_VTI: + case NMP_OBJECT_TYPE_LNK_VTI6: case NMP_OBJECT_TYPE_LNK_VXLAN: case NMP_OBJECT_TYPE_LNK_WIREGUARD: @@ -534,6 +561,8 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type) return FALSE; } +#define NMP_OBJECT_TYPE_NAME(obj_type) (nmp_class_from_type(obj_type)->obj_type_name) + #define NMP_OBJECT_CAST_OBJECT(obj) \ ({ \ typeof(obj) _obj = (obj); \ @@ -717,14 +746,35 @@ const char *nmp_object_to_string(const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size); -void nmp_object_hash_update(const NMPObject *obj, NMHashState *h); + +void nmp_object_hash_update_full(const NMPObject *obj, gboolean for_id, NMHashState *h); + +static inline void +nmp_object_hash_update(const NMPObject *obj, NMHashState *h) +{ + return nmp_object_hash_update_full(obj, FALSE, h); +} typedef enum { NMP_OBJECT_CMP_FLAGS_NONE = 0, + /* Only compare for the ID. This is what nmp_object_id_cmp() does. + * + * In most cases, the identity of an object is a (non-strict) subset + * of the attributes of the object. + * + * However, for some objects (like NMPObjectLnk) there is on concept + * of identity. They implement object identity based on pointer equality + * (in that case, the ID is not a subset of the object's attributes). + * + * That's why this flag (currently) cannot be meaningfully combined with + * other flags. + */ + NMP_OBJECT_CMP_FLAGS_ID = NM_BIT(0), + /* Warning: this flag is currently only implemented for certain object types * (address and routes). */ - NMP_OBJECT_CMP_FLAGS_IGNORE_IFINDEX = (1llu << 0), + NMP_OBJECT_CMP_FLAGS_IGNORE_IFINDEX = NM_BIT(1), } NMPObjectCmpFlags; int nmp_object_cmp_full(const NMPObject *obj1, const NMPObject *obj2, NMPObjectCmpFlags flags); @@ -744,8 +794,18 @@ nmp_object_equal(const NMPObject *obj1, const NMPObject *obj2) void nmp_object_copy(NMPObject *dst, const NMPObject *src, gboolean id_only); NMPObject *nmp_object_clone(const NMPObject *obj, gboolean id_only); -int nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2); -void nmp_object_id_hash_update(const NMPObject *obj, NMHashState *h); +static inline int +nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2) +{ + return nmp_object_cmp_full(obj1, obj2, NMP_OBJECT_CMP_FLAGS_ID); +} + +static inline void +nmp_object_id_hash_update(const NMPObject *obj, NMHashState *h) +{ + return nmp_object_hash_update_full(obj, TRUE, h); +} + guint nmp_object_id_hash(const NMPObject *obj); static inline gboolean @@ -915,6 +975,7 @@ NMPCacheOpsType nmp_cache_update_netlink_route(NMPCache *cache, NMPObject *obj_hand_over, gboolean is_dump, guint16 nlmsgflags, + gboolean route_is_alive, const NMPObject **out_obj_old, const NMPObject **out_obj_new, const NMPObject **out_obj_replace, diff --git a/src/libnm-platform/nmp-plobj.c b/src/libnm-platform/nmp-plobj.c new file mode 100644 index 00000000..c27aa1fc --- /dev/null +++ b/src/libnm-platform/nmp-plobj.c @@ -0,0 +1,637 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2015 - 2018 Red Hat, Inc. + */ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nmp-plobj.h" + +#include "nm-compat-headers/linux/if_addr.h" + +#include "libnm-glib-aux/nm-time-utils.h" +#include "nm-platform-utils.h" + +/*****************************************************************************/ + +#define TO_STRING_IFA_FLAGS_BUF_SIZE 256 + +static const char * +_to_string_ifa_flags(guint32 ifa_flags, char *buf, gsize size) +{ +#define S_FLAGS_PREFIX " flags " + nm_assert(buf && size >= TO_STRING_IFA_FLAGS_BUF_SIZE && size > NM_STRLEN(S_FLAGS_PREFIX)); + + if (!ifa_flags) + buf[0] = '\0'; + else { + nm_platform_addr_flags2str(ifa_flags, + &buf[NM_STRLEN(S_FLAGS_PREFIX)], + size - NM_STRLEN(S_FLAGS_PREFIX)); + if (buf[NM_STRLEN(S_FLAGS_PREFIX)] == '\0') + buf[0] = '\0'; + else + memcpy(buf, S_FLAGS_PREFIX, NM_STRLEN(S_FLAGS_PREFIX)); + } + return buf; +} + +#define _to_string_dev(arr, ifindex) \ + ({ \ + const int _ifindex = (ifindex); \ + \ + _ifindex ? nm_sprintf_buf((arr), " dev %d", ifindex) : nm_str_truncate((arr)); \ + }) + +static const char * +_lifetime_to_string(guint32 timestamp, guint32 lifetime, gint32 now, char *buf, size_t buf_size) +{ + if (lifetime == NM_PLATFORM_LIFETIME_PERMANENT) + return "forever"; + + g_snprintf(buf, + buf_size, + "%usec", + nmp_utils_lifetime_rebase_relative_time_on_now(timestamp, lifetime, now)); + return buf; +} + +static const char * +_lifetime_summary_to_string(gint32 now, + guint32 timestamp, + guint32 preferred, + guint32 lifetime, + char *buf, + size_t buf_size) +{ + g_snprintf(buf, + buf_size, + " lifetime %d-%u[%u,%u]", + (signed) now, + (unsigned) timestamp, + (unsigned) preferred, + (unsigned) lifetime); + return buf; +} + +static int +_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b) +{ + guint32 lifetime_a; + guint32 lifetime_b; + guint32 preferred_a; + guint32 preferred_b; + gint32 now = 0; + + lifetime_a = + nmp_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, &now, &preferred_a); + lifetime_b = + nmp_utils_lifetime_get(b->timestamp, b->lifetime, b->preferred, &now, &preferred_b); + + NM_CMP_DIRECT(lifetime_a, lifetime_b); + NM_CMP_DIRECT(preferred_a, preferred_b); + return 0; +} + +/*****************************************************************************/ + +void +nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h) +{ + nm_hash_update_vals(h, + obj->ifindex, + obj->addr_source, + obj->use_ip4_broadcast_address ? obj->broadcast_address : ((in_addr_t) 0u), + obj->timestamp, + obj->lifetime, + obj->preferred, + obj->n_ifa_flags, + obj->plen, + obj->address, + obj->peer_address, + NM_HASH_COMBINE_BOOLS(guint8, + obj->use_ip4_broadcast_address, + obj->a_acd_not_ready, + obj->a_force_commit)); + nm_hash_update_strarr(h, obj->label); +} + +int +nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, + const NMPlatformIP4Address *b, + NMPlatformIPAddressCmpType cmp_type) +{ + NM_CMP_SELF(a, b); + + NM_CMP_FIELD(a, b, ifindex); + NM_CMP_FIELD(a, b, plen); + NM_CMP_FIELD(a, b, address); + + switch (cmp_type) { + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID: + /* for IPv4 addresses, you can add the same local address with differing peer-address + * (IFA_ADDRESS), provided that their net-part differs. */ + NM_CMP_DIRECT_IP4_ADDR_SAME_PREFIX(a->peer_address, b->peer_address, a->plen); + return 0; + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY: + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL: + NM_CMP_FIELD(a, b, peer_address); + NM_CMP_FIELD_STR(a, b, label); + if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) { + NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a, + (const NMPlatformIPAddress *) b)); + + /* Most flags are set by kernel. We only compare the ones that + * NetworkManager actively sets. + * + * NM actively only sets IFA_F_NOPREFIXROUTE (and IFA_F_MANAGETEMPADDR for IPv6), + * where nm_platform_ip_address_sync() sets IFA_F_NOPREFIXROUTE depending on + * NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE. + * There are thus no flags to compare for IPv4. */ + + NM_CMP_DIRECT(nm_platform_ip4_broadcast_address_from_addr(a), + nm_platform_ip4_broadcast_address_from_addr(b)); + } else { + NM_CMP_FIELD(a, b, timestamp); + NM_CMP_FIELD(a, b, lifetime); + NM_CMP_FIELD(a, b, preferred); + NM_CMP_FIELD(a, b, n_ifa_flags); + NM_CMP_FIELD(a, b, addr_source); + NM_CMP_FIELD_UNSAFE(a, b, use_ip4_broadcast_address); + if (a->use_ip4_broadcast_address) + NM_CMP_FIELD(a, b, broadcast_address); + NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready); + NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); + } + return 0; + } + return nm_assert_unreachable_val(0); +} + +/*****************************************************************************/ + +void +nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h) +{ + nm_hash_update_vals(h, + obj->ifindex, + obj->addr_source, + obj->timestamp, + obj->lifetime, + obj->preferred, + obj->n_ifa_flags, + obj->plen, + obj->address, + obj->peer_address, + NM_HASH_COMBINE_BOOLS(guint8, obj->a_force_commit)); +} + +int +nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, + const NMPlatformIP6Address *b, + NMPlatformIPAddressCmpType cmp_type) +{ + const struct in6_addr *p_a, *p_b; + + NM_CMP_SELF(a, b); + + NM_CMP_FIELD(a, b, ifindex); + NM_CMP_FIELD_IN6ADDR(a, b, address); + + switch (cmp_type) { + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID: + /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ + return 0; + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY: + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL: + NM_CMP_FIELD(a, b, plen); + p_a = nm_platform_ip6_address_get_peer(a); + p_b = nm_platform_ip6_address_get_peer(b); + NM_CMP_DIRECT_MEMCMP(p_a, p_b, sizeof(*p_a)); + if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) { + NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a, + (const NMPlatformIPAddress *) b)); + + /* Most flags are set by kernel. We only compare the ones that + * NetworkManager actively sets. + * + * NM actively only sets IFA_F_NOPREFIXROUTE and IFA_F_MANAGETEMPADDR, + * where nm_platform_ip_address_sync() sets IFA_F_NOPREFIXROUTE depending on + * NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE. + * We thus only care about IFA_F_MANAGETEMPADDR. */ + NM_CMP_DIRECT(a->n_ifa_flags & IFA_F_MANAGETEMPADDR, + b->n_ifa_flags & IFA_F_MANAGETEMPADDR); + } else { + NM_CMP_FIELD(a, b, timestamp); + NM_CMP_FIELD(a, b, lifetime); + NM_CMP_FIELD(a, b, preferred); + NM_CMP_FIELD(a, b, n_ifa_flags); + NM_CMP_FIELD(a, b, addr_source); + NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); + } + return 0; + } + return nm_assert_unreachable_val(0); +} + +/*****************************************************************************/ + +static int +_address_pretty_sort_get_prio_4(in_addr_t addr) +{ + if (nm_ip4_addr_is_link_local(addr)) + return 0; + return 1; +} + +static int +_address_pretty_sort_get_prio_6(const struct in6_addr *addr) +{ + if (IN6_IS_ADDR_V4MAPPED(addr)) + return 0; + if (IN6_IS_ADDR_V4COMPAT(addr)) + return 1; + if (IN6_IS_ADDR_UNSPECIFIED(addr)) + return 2; + if (IN6_IS_ADDR_LOOPBACK(addr)) + return 3; + if (IN6_IS_ADDR_LINKLOCAL(addr)) + return 4; + if (IN6_IS_ADDR_SITELOCAL(addr)) + return 5; + return 6; +} + +int +nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, + const NMPlatformIP4Address *a2) +{ + in_addr_t n1; + in_addr_t n2; + + nm_assert(a1); + nm_assert(a2); + + /* Sort by address type. For example link local will + * be sorted *after* a global address. */ + NM_CMP_DIRECT(_address_pretty_sort_get_prio_4(a2->address), + _address_pretty_sort_get_prio_4(a1->address)); + + /* Sort the addresses based on their source. */ + NM_CMP_DIRECT(a2->addr_source, a1->addr_source); + + NM_CMP_DIRECT((a2->label[0] == '\0'), (a1->label[0] == '\0')); + + /* Finally, sort addresses lexically. We compare only the + * network part so that the order of addresses in the same + * subnet (and thus also the primary/secondary role) is + * preserved. + */ + n1 = nm_ip4_addr_clear_host_address(a1->address, a1->plen); + n2 = nm_ip4_addr_clear_host_address(a2->address, a2->plen); + NM_CMP_DIRECT_MEMCMP(&n1, &n2, sizeof(guint32)); + return 0; +} + +int +nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1, + const NMPlatformIP6Address *a2, + gboolean prefer_temp) +{ + gboolean ipv6_privacy1; + gboolean ipv6_privacy2; + + nm_assert(a1); + nm_assert(a2); + + /* tentative addresses are always sorted back... */ + /* sort tentative addresses after non-tentative. */ + NM_CMP_DIRECT(NM_FLAGS_HAS(a1->n_ifa_flags, IFA_F_TENTATIVE), + NM_FLAGS_HAS(a2->n_ifa_flags, IFA_F_TENTATIVE)); + + /* Sort by address type. For example link local will + * be sorted *after* site local or global. */ + NM_CMP_DIRECT(_address_pretty_sort_get_prio_6(&a2->address), + _address_pretty_sort_get_prio_6(&a1->address)); + + ipv6_privacy1 = NM_FLAGS_ANY(a1->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_SECONDARY); + ipv6_privacy2 = NM_FLAGS_ANY(a2->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_SECONDARY); + if (ipv6_privacy1 || ipv6_privacy2) { + gboolean public1 = TRUE; + gboolean public2 = TRUE; + + if (ipv6_privacy1) { + if (a1->n_ifa_flags & IFA_F_SECONDARY) + public1 = prefer_temp; + else + public1 = !prefer_temp; + } + if (ipv6_privacy2) { + if (a2->n_ifa_flags & IFA_F_SECONDARY) + public2 = prefer_temp; + else + public2 = !prefer_temp; + } + + NM_CMP_DIRECT(public2, public1); + } + + /* Sort the addresses based on their source. */ + NM_CMP_DIRECT(a2->addr_source, a1->addr_source); + + /* sort permanent addresses before non-permanent. */ + NM_CMP_DIRECT(NM_FLAGS_HAS(a2->n_ifa_flags, IFA_F_PERMANENT), + NM_FLAGS_HAS(a1->n_ifa_flags, IFA_F_PERMANENT)); + + /* finally sort addresses lexically */ + NM_CMP_DIRECT_IN6ADDR(&a1->address, &a2->address); + NM_CMP_DIRECT_MEMCMP(a1, a2, sizeof(*a1)); + return 0; +} + +void +nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen) +{ + nm_assert(plen <= 32); + + addr->address = address; + addr->peer_address = address; + addr->plen = plen; +} + +const struct in6_addr * +nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr) +{ + if (IN6_IS_ADDR_UNSPECIFIED(&addr->peer_address) + || IN6_ARE_ADDR_EQUAL(&addr->peer_address, &addr->address)) + return &addr->address; + return &addr->peer_address; +} + +/*****************************************************************************/ + +/** + * nm_platform_ip4_address_to_string: + * @route: pointer to NMPlatformIP4Address address structure + * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. + * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. + * + * A method for converting an address struct into a string representation. + * + * Example output: "" + * + * Returns: a string representation of the address. + */ +const char * +nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf, gsize len) +{ + char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE]; + char s_address[INET_ADDRSTRLEN]; + char s_peer[INET_ADDRSTRLEN]; + char str_dev[30]; + char str_label[32]; + char str_lft[30]; + char str_pref[30]; + char str_time[50]; + char s_source[50]; + char *str_peer = NULL; + const char *str_lft_p, *str_pref_p, *str_time_p; + gint32 now = nm_utils_get_monotonic_timestamp_sec(); + in_addr_t broadcast_address; + char str_broadcast[INET_ADDRSTRLEN]; + + if (!nm_utils_to_string_buffer_init_null(address, &buf, &len)) + return buf; + + inet_ntop(AF_INET, &address->address, s_address, sizeof(s_address)); + + if (address->peer_address != address->address) { + inet_ntop(AF_INET, &address->peer_address, s_peer, sizeof(s_peer)); + str_peer = g_strconcat(" ptp ", s_peer, NULL); + } + + if (*address->label) + g_snprintf(str_label, sizeof(str_label), " label %s", address->label); + else + str_label[0] = 0; + + str_lft_p = _lifetime_to_string(address->timestamp, + address->lifetime ?: NM_PLATFORM_LIFETIME_PERMANENT, + now, + str_lft, + sizeof(str_lft)), + str_pref_p = + (address->lifetime == address->preferred) + ? str_lft_p + : (_lifetime_to_string(address->timestamp, + address->lifetime ? MIN(address->preferred, address->lifetime) + : NM_PLATFORM_LIFETIME_PERMANENT, + now, + str_pref, + sizeof(str_pref))); + str_time_p = _lifetime_summary_to_string(now, + address->timestamp, + address->preferred, + address->lifetime, + str_time, + sizeof(str_time)); + + broadcast_address = nm_platform_ip4_broadcast_address_from_addr(address); + + g_snprintf( + buf, + len, + "%s/%d" + "%s%s" /* broadcast */ + " lft %s" + " pref %s" + "%s" /* time */ + "%s" /* peer */ + "%s" /* dev */ + "%s" /* flags */ + "%s" /* label */ + " src %s" + "%s" /* a_acd_not_ready */ + "%s" /* a_force_commit */ + "", + s_address, + address->plen, + broadcast_address != 0u || address->use_ip4_broadcast_address + ? (address->use_ip4_broadcast_address ? " brd " : " brd* ") + : "", + broadcast_address != 0u || address->use_ip4_broadcast_address + ? nm_inet4_ntop(broadcast_address, str_broadcast) + : "", + str_lft_p, + str_pref_p, + str_time_p, + str_peer ?: "", + _to_string_dev(str_dev, address->ifindex), + _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)), + str_label, + nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), + address->a_acd_not_ready ? " ip4acd-not-ready" : "", + address->a_force_commit ? " force-commit" : ""); + g_free(str_peer); + return buf; +} + +/** + * nm_platform_ip6_address_to_string: + * @route: pointer to NMPlatformIP6Address address structure + * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. + * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. + * + * A method for converting an address struct into a string representation. + * + * Example output: "2001:db8:0:f101::1/64 lft 4294967295 pref 4294967295 time 16922666 on dev em1" + * + * Returns: a string representation of the address. + */ +const char * +nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf, gsize len) +{ + char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE]; + char s_address[INET6_ADDRSTRLEN]; + char s_peer[INET6_ADDRSTRLEN]; + char str_lft[30]; + char str_pref[30]; + char str_time[50]; + char s_source[50]; + char str_dev[30]; + char *str_peer = NULL; + const char *str_lft_p, *str_pref_p, *str_time_p; + gint32 now = nm_utils_get_monotonic_timestamp_sec(); + + if (!nm_utils_to_string_buffer_init_null(address, &buf, &len)) + return buf; + + inet_ntop(AF_INET6, &address->address, s_address, sizeof(s_address)); + + if (!IN6_IS_ADDR_UNSPECIFIED(&address->peer_address)) { + inet_ntop(AF_INET6, &address->peer_address, s_peer, sizeof(s_peer)); + str_peer = g_strconcat(" ptp ", s_peer, NULL); + } + + str_lft_p = _lifetime_to_string(address->timestamp, + address->lifetime ?: NM_PLATFORM_LIFETIME_PERMANENT, + now, + str_lft, + sizeof(str_lft)), + str_pref_p = + (address->lifetime == address->preferred) + ? str_lft_p + : (_lifetime_to_string(address->timestamp, + address->lifetime ? MIN(address->preferred, address->lifetime) + : NM_PLATFORM_LIFETIME_PERMANENT, + now, + str_pref, + sizeof(str_pref))); + str_time_p = _lifetime_summary_to_string(now, + address->timestamp, + address->preferred, + address->lifetime, + str_time, + sizeof(str_time)); + + g_snprintf( + buf, + len, + "%s/%d lft %s pref %s%s%s%s%s src %s" + "%s" /* a_force_commit */ + "", + s_address, + address->plen, + str_lft_p, + str_pref_p, + str_time_p, + str_peer ?: "", + _to_string_dev(str_dev, address->ifindex), + _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)), + nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), + address->a_force_commit ? " force-commit" : ""); + g_free(str_peer); + return buf; +} + +/*****************************************************************************/ + +gboolean +nm_platform_ip_address_match(int addr_family, + const NMPlatformIPAddress *address, + NMPlatformMatchFlags match_flag) +{ + nm_assert(!NM_FLAGS_ANY( + match_flag, + ~(NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY))); + nm_assert(NM_FLAGS_ANY(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY)); + nm_assert(NM_FLAGS_ANY(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)); + + if (addr_family == AF_INET) { + if (nm_ip4_addr_is_link_local(((NMPlatformIP4Address *) address)->address)) { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL)) + return FALSE; + } else { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL)) + return FALSE; + } + } else { + if (IN6_IS_ADDR_LINKLOCAL(address->address_ptr)) { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL)) + return FALSE; + } else { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL)) + return FALSE; + } + } + + if (NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_DADFAILED)) { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED)) + return FALSE; + } else if (NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_TENTATIVE) + && !NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_OPTIMISTIC)) { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE)) + return FALSE; + } else if (NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_DEPRECATED)) { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED)) + return FALSE; + } else { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +const _NMPlatformVTableAddressUnion nm_platform_vtable_address = { + .v4 = + { + .is_ip4 = TRUE, + .obj_type = NMP_OBJECT_TYPE_IP4_ADDRESS, + .addr_family = AF_INET, + .sizeof_address = sizeof(NMPlatformIP4Address), + .address_cmp = + (int (*)(const NMPlatformIPXAddress *a, + const NMPlatformIPXAddress *b, + NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip4_address_cmp, + .address_to_string = (const char *(*) (const NMPlatformIPXAddress *address, + char *buf, + gsize len)) nm_platform_ip4_address_to_string, + }, + .v6 = + { + .is_ip4 = FALSE, + .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, + .addr_family = AF_INET6, + .sizeof_address = sizeof(NMPlatformIP6Address), + .address_cmp = + (int (*)(const NMPlatformIPXAddress *a, + const NMPlatformIPXAddress *b, + NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip6_address_cmp, + .address_to_string = (const char *(*) (const NMPlatformIPXAddress *address, + char *buf, + gsize len)) nm_platform_ip6_address_to_string, + }, +}; diff --git a/src/libnm-platform/nmp-plobj.h b/src/libnm-platform/nmp-plobj.h new file mode 100644 index 00000000..e149150d --- /dev/null +++ b/src/libnm-platform/nmp-plobj.h @@ -0,0 +1,296 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2015 - 2018 Red Hat, Inc. + */ + +#ifndef __NMP_PLOBJ_H__ +#define __NMP_PLOBJ_H__ + +#include "libnm-base/nm-base.h" + +#include "nmp-base.h" + +/*****************************************************************************/ + +/* This is used with _nm_align() on the platform structs. Note that + * "align" attribute can only increase the natural alignment, unless + * also "packed" is specified. That's what we want. + * https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#Common-Type-Attributes. + */ +#define _NMPlatformObject_Align (MAX(_nm_alignof(void *), _nm_alignof(gint64))) + +struct _NMPlatformObject { + /* the object type has no fields of its own, it is only used to having + * a special pointer type that can be used to indicate "any" type. */ + char _dummy_don_t_use_me; +} _nm_align(_NMPlatformObject_Align); + +/*****************************************************************************/ + +#define __NMPlatformObjWithIfindex_COMMON \ + int ifindex; \ + ; + +/*****************************************************************************/ + +#define __NMPlatformIPAddress_COMMON \ + __NMPlatformObjWithIfindex_COMMON; \ + NMIPConfigSource addr_source; \ + \ + /* Timestamp in seconds in the reference system of nm_utils_get_monotonic_timestamp_*(). + * + * The rules are: + * 1 @lifetime==0: @timestamp and @preferred is irrelevant (but mostly set to 0 too). Such addresses + * are permanent. This rule is so that unset addresses (calloc) are permanent by default. + * 2 @lifetime==@preferred==NM_PLATFORM_LIFETIME_PERMANENT: @timestamp is irrelevant (but mostly + * set to 0). Such addresses are permanent. + * 3 Non permanent addresses should (almost) always have @timestamp > 0. 0 is not a valid timestamp + * and never returned by nm_utils_get_monotonic_timestamp_sec(). In this case @valid/@preferred + * is anchored at @timestamp. + * 4 Non permanent addresses with @timestamp == 0 are implicitly anchored at *now*, thus the time + * moves as time goes by. This is usually not useful, except e.g. nm_platform_ip[46]_address_add(). + * + * Non permanent addresses from DHCP/RA might have the @timestamp set to the moment of when the + * lease was received. Addresses from kernel might have the @timestamp based on the last modification + * time of the addresses. But don't rely on this behaviour, the @timestamp is only defined for anchoring + * @lifetime and @preferred. + */ \ + guint32 timestamp; \ + guint32 lifetime; /* seconds since timestamp */ \ + guint32 preferred; /* seconds since timestamp */ \ + \ + /* ifa_flags in 'struct ifaddrmsg' from , extended to 32 bit by + * IFA_FLAGS attribute. */ \ + guint32 n_ifa_flags; \ + \ + bool use_ip4_broadcast_address : 1; \ + \ + /* Meta flags not honored by NMPlatform (netlink code). Instead, they can be + * used by the upper layers which use NMPlatformIPRoute to track addresses that + * should be configured. */ \ + bool a_force_commit : 1; \ + \ + /* Don't have a bitfield as last field in __NMPlatformIPAddress_COMMON. It would then + * be unclear how the following fields get merged. We could also use a zero bitfield, + * but instead we just have there the uint8 field. */ \ + guint8 plen; \ + ; + +/** + * NMPlatformIPAddress: + * + * Common parts of NMPlatformIP4Address and NMPlatformIP6Address. + **/ +typedef struct { + __NMPlatformIPAddress_COMMON; + _nm_alignas(NMIPAddr) guint8 address_ptr[]; +} _nm_alignas(NMPlatformObject) NMPlatformIPAddress; + +/** + * NMPlatformIP4Address: + * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_sec() + **/ +struct _NMPlatformIP4Address { + __NMPlatformIPAddress_COMMON; + + /* The local address IFA_LOCAL. */ + _nm_alignas(NMIPAddr) in_addr_t address; + + /* The IFA_ADDRESS PTP peer address. This field is rather important, because + * it constitutes the identifier for the IPv4 address (e.g. you can add two + * addresses that only differ by their peer's network-part. + * + * Beware that for most cases, NetworkManager doesn't want to set an explicit + * peer-address. However, that corresponds to setting the peer address to @address + * itself. Leaving peer-address unset/zero, means explicitly setting the peer + * address to 0.0.0.0, which you probably don't want. + * */ + in_addr_t peer_address; /* PTP peer address */ + + /* IFA_BROADCAST. + * + * This parameter is ignored unless use_ip4_broadcast_address is TRUE. + * See nm_platform_ip4_broadcast_address_from_addr(). */ + in_addr_t broadcast_address; + + char label[NMP_IFNAMSIZ]; + + /* Whether the address is ready to be configured. By default, an address is, but this + * flag may indicate that the address is just for tracking purpose only, but the ACD + * state is not yet ready for the address to be configured. */ + bool a_acd_not_ready : 1; +} _nm_alignas(NMPlatformObject); + +/** + * NMPlatformIP6Address: + * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_sec() + **/ +struct _NMPlatformIP6Address { + __NMPlatformIPAddress_COMMON; + _nm_alignas(NMIPAddr) struct in6_addr address; + struct in6_addr peer_address; +} _nm_alignas(NMPlatformObject); + +typedef union { + NMPlatformIPAddress ax; + NMPlatformIP4Address a4; + NMPlatformIP6Address a6; +} NMPlatformIPXAddress; + +#undef __NMPlatformIPAddress_COMMON + +/*****************************************************************************/ + +typedef enum { + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID, + + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY, + + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL, +} NMPlatformIPAddressCmpType; + +#define NM_PLATFORM_IP_ADDRESS_CAST(address) \ + NM_CONSTCAST(NMPlatformIPAddress, \ + (address), \ + NMPlatformIPXAddress, \ + NMPlatformIP4Address, \ + NMPlatformIP6Address) + +#define NM_PLATFORM_IP4_ADDRESS_INIT(...) (&((const NMPlatformIP4Address){__VA_ARGS__})) + +#define NM_PLATFORM_IP6_ADDRESS_INIT(...) (&((const NMPlatformIP6Address){__VA_ARGS__})) + +/*****************************************************************************/ + +typedef struct { + bool is_ip4; + NMPObjectType obj_type; + gint8 addr_family; + guint8 sizeof_address; + int (*address_cmp)(const NMPlatformIPXAddress *a, + const NMPlatformIPXAddress *b, + NMPlatformIPAddressCmpType cmp_type); + const char *(*address_to_string)(const NMPlatformIPXAddress *address, char *buf, gsize len); +} NMPlatformVTableAddress; + +typedef union { + struct { + NMPlatformVTableAddress v6; + NMPlatformVTableAddress v4; + }; + NMPlatformVTableAddress vx[2]; +} _NMPlatformVTableAddressUnion; + +extern const _NMPlatformVTableAddressUnion nm_platform_vtable_address; + +void nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h); + +int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, + const NMPlatformIP4Address *b, + NMPlatformIPAddressCmpType cmp_type); + +static inline int +nm_platform_ip4_address_cmp_full(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) +{ + return nm_platform_ip4_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL); +} + +void nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h); + +int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, + const NMPlatformIP6Address *b, + NMPlatformIPAddressCmpType cmp_type); + +static inline int +nm_platform_ip6_address_cmp_full(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) +{ + return nm_platform_ip6_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL); +} + +int nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, + const NMPlatformIP4Address *a2); + +int nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1, + const NMPlatformIP6Address *a2, + gboolean prefer_temp); + +static inline in_addr_t +nm_platform_ip4_broadcast_address_from_addr(const NMPlatformIP4Address *addr) +{ + nm_assert(addr); + + if (addr->use_ip4_broadcast_address) + return addr->broadcast_address; + + /* the set broadcast-address gets ignored, and we determine a default brd base + * on the peer IFA_ADDRESS. */ + if (addr->peer_address != 0u && addr->plen < 31 /* RFC3021 */) + return nm_ip4_addr_get_broadcast_address(addr->peer_address, addr->plen); + return 0u; +} + +void nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen); + +const struct in6_addr *nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr); + +static inline gpointer +nm_platform_ip_address_get_peer_address(int addr_family, const NMPlatformIPAddress *addr) +{ + nm_assert_addr_family(addr_family); + nm_assert(addr); + + if (NM_IS_IPv4(addr_family)) + return &((NMPlatformIP4Address *) addr)->peer_address; + return &((NMPlatformIP6Address *) addr)->peer_address; +} + +const char * +nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf, gsize len); +const char * +nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf, gsize len); + +int nm_platform_ip_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b); + +NMPlatformIP4Route *nm_platform_ip4_address_generate_device_route(const NMPlatformIP4Address *addr, + int ifindex, + guint32 route_table, + guint32 route_metric, + gboolean force_commit, + NMPlatformIP4Route *dst); + +typedef enum { + + /* match-flags are strictly inclusive. That means, + * by default nothing is matched, but if you enable a particular + * flag, a candidate that matches passes the check. + * + * In other words: adding more flags can only extend the result + * set of matching objects. + * + * Also, the flags form partitions. Like, an address can be either of + * ADDRTYPE_NORMAL or ADDRTYPE_LINKLOCAL, but never both. Same for + * the ADDRSTATE match types. + */ + NM_PLATFORM_MATCH_WITH_NONE = 0, + + NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL = (1LL << 0), + NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL = (1LL << 1), + NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY = + NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL | NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL, + + NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL = (1LL << 2), + NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE = (1LL << 3), + NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED = (1LL << 4), + NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED = (1LL << 5), + NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY = + NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL | NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE + | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED, +} NMPlatformMatchFlags; + +gboolean nm_platform_ip_address_match(int addr_family, + const NMPlatformIPAddress *addr, + NMPlatformMatchFlags match_flag); + +/*****************************************************************************/ + +#endif /* __NMP_PLOBJ_H__ */ diff --git a/src/libnm-platform/tests/test-nm-platform.c b/src/libnm-platform/tests/test-nm-platform.c index cd54df92..90a29045 100644 --- a/src/libnm-platform/tests/test-nm-platform.c +++ b/src/libnm-platform/tests/test-nm-platform.c @@ -6,16 +6,67 @@ #include "libnm-platform/nm-netlink.h" #include "libnm-platform/nmp-netns.h" #include "libnm-platform/nm-platform-utils.h" +#include "libnm-platform/nmp-object.h" #include "libnm-glib-aux/nm-test-utils.h" /*****************************************************************************/ -void -_nm_logging_clear_platform_logging_cache(void) -{ - /* this symbols is required by nm-log-core library. */ -} +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectIP4Address)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectIP4Route)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectIP6Address)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectIP6Route)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLink)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkBond)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkBridge)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkGre)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkInfiniband)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkIp6Tnl)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkIpIp)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkMacsec)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkMacvlan)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkSit)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkTun)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVlan)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVrf)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVti)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVti6)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVxlan)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkWireGuard)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectQdisc)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectRoutingRule)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectTfilter)); + +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIP4Address)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIP4Route)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIP6Address)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIP6Route)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPAddress)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPAddress)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPRoute)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPXAddress)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPXRoute)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLink)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkBond)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkBridge)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkGre)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkInfiniband)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkIp6Tnl)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkIpIp)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkMacsec)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkMacvlan)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkSit)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkTun)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVlan)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVrf)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVti)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVti6)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVxlan)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkWireGuard)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformObjWithIfindex)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformQdisc)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformRoutingRule)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformTfilter)); /*****************************************************************************/ @@ -140,6 +191,57 @@ test_nmp_link_mode_all_advertised_modes_bits(void) /*****************************************************************************/ +static void +test_nmpclass_consistency(void) +{ + NMPObjectType obj_type; + NMPObjectType obj_type2; + + G_STATIC_ASSERT(G_N_ELEMENTS(_nmp_classes) == NMP_OBJECT_TYPE_MAX); + + for (obj_type = 1; obj_type <= NMP_OBJECT_TYPE_MAX; obj_type++) { + const NMPClass *klass = nmp_class_from_type(obj_type); + gboolean is_lnk; + + g_assert(klass); + g_assert(klass == &_nmp_classes[obj_type - 1]); + + g_assert_cmpint(klass->obj_type, ==, obj_type); + g_assert(klass->obj_type_name); + + g_assert((!!klass->cmd_obj_cmp) == (!!klass->cmd_obj_hash_update)); + g_assert((!!klass->cmd_plobj_cmp) == (!!klass->cmd_plobj_hash_update)); + g_assert((!!klass->cmd_plobj_id_cmp) == (!!klass->cmd_plobj_id_hash_update)); + + g_assert((!!klass->cmd_obj_cmp) != (!!klass->cmd_plobj_cmp)); + g_assert((!!klass->cmd_obj_hash_update) != (!!klass->cmd_plobj_hash_update)); + + g_assert((!klass->cmd_obj_cmp) || (!klass->cmd_plobj_id_cmp)); + g_assert((!klass->cmd_obj_hash_update) || (!klass->cmd_plobj_id_hash_update)); + + g_assert_cmpint(klass->sizeof_public, >, 0); + g_assert_cmpint(klass->sizeof_data, >=, klass->sizeof_public); + + g_assert((!!klass->cmd_obj_to_string) != (!!klass->cmd_plobj_to_string)); + g_assert(!klass->cmd_plobj_to_string_id || klass->cmd_plobj_to_string); + + is_lnk = (obj_type >= NMP_OBJECT_TYPE_LNK_BRIDGE && obj_type <= NMP_OBJECT_TYPE_LNK_BOND); + if (klass->lnk_link_type == NM_LINK_TYPE_NONE) { + G_STATIC_ASSERT(NM_LINK_TYPE_NONE == 0); + g_assert(!is_lnk); + } else + g_assert(is_lnk); + + for (obj_type2 = 1; obj_type2 < obj_type; obj_type2++) { + const NMPClass *klass2 = nmp_class_from_type(obj_type2); + + g_assert_cmpstr(klass->obj_type_name, !=, klass2->obj_type_name); + } + } +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -150,6 +252,7 @@ main(int argc, char **argv) g_test_add_func("/nm-platform/test_use_symbols", test_use_symbols); g_test_add_func("/nm-platform/test_nmp_link_mode_all_advertised_modes_bits", test_nmp_link_mode_all_advertised_modes_bits); + g_test_add_func("/nm-platform/test_nmpclass_consistency", test_nmpclass_consistency); return g_test_run(); } diff --git a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.h b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.h index 4783c572..094c0ff0 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.h +++ b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.h @@ -12,7 +12,7 @@ #define NM_TYPE_WIFI_UTILS_NL80211 (nm_wifi_utils_nl80211_get_type()) #define NM_WIFI_UTILS_NL80211(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211)) #define NM_WIFI_UTILS_NL80211_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211Class)) #define NM_IS_WIFI_UTILS_NL80211(obj) \ diff --git a/src/libnm-platform/wifi/nm-wifi-utils-wext.c b/src/libnm-platform/wifi/nm-wifi-utils-wext.c index 678d71fe..eac3c929 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-wext.c +++ b/src/libnm-platform/wifi/nm-wifi-utils-wext.c @@ -90,7 +90,7 @@ dispose(GObject *object) { NMWifiUtilsWext *wext = NM_WIFI_UTILS_WEXT(object); - wext->fd = nm_close(wext->fd); + nm_clear_fd(&wext->fd); } static gboolean diff --git a/src/libnm-platform/wifi/nm-wifi-utils-wext.h b/src/libnm-platform/wifi/nm-wifi-utils-wext.h index d6f3453c..28a74eba 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-wext.h +++ b/src/libnm-platform/wifi/nm-wifi-utils-wext.h @@ -10,7 +10,7 @@ #define NM_TYPE_WIFI_UTILS_WEXT (nm_wifi_utils_wext_get_type()) #define NM_WIFI_UTILS_WEXT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS_WEXT, NMWifiUtilsWext)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS_WEXT, NMWifiUtilsWext)) #define NM_WIFI_UTILS_WEXT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_UTILS_WEXT, NMWifiUtilsWextClass)) #define NM_IS_WIFI_UTILS_WEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WIFI_UTILS_WEXT)) diff --git a/src/libnm-platform/wifi/nm-wifi-utils.h b/src/libnm-platform/wifi/nm-wifi-utils.h index fd636418..aa5a34cd 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils.h +++ b/src/libnm-platform/wifi/nm-wifi-utils.h @@ -13,7 +13,7 @@ typedef struct NMWifiUtils NMWifiUtils; #define NM_TYPE_WIFI_UTILS (nm_wifi_utils_get_type()) -#define NM_WIFI_UTILS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS, NMWifiUtils)) +#define NM_WIFI_UTILS(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS, NMWifiUtils)) #define NM_WIFI_UTILS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_UTILS, NMWifiUtilsClass)) #define NM_IS_WIFI_UTILS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WIFI_UTILS)) diff --git a/src/libnm-platform/wpan/nm-wpan-utils.h b/src/libnm-platform/wpan/nm-wpan-utils.h index eea5587d..67c5f412 100644 --- a/src/libnm-platform/wpan/nm-wpan-utils.h +++ b/src/libnm-platform/wpan/nm-wpan-utils.h @@ -11,7 +11,7 @@ typedef struct NMWpanUtils NMWpanUtils; #define NM_TYPE_WPAN_UTILS (nm_wpan_utils_get_type()) -#define NM_WPAN_UTILS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WPAN_UTILS, NMWpanUtils)) +#define NM_WPAN_UTILS(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WPAN_UTILS, NMWpanUtils)) #define NM_WPAN_UTILS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WPAN_UTILS, NMWpanUtilsClass)) #define NM_IS_WPAN_UTILS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WPAN_UTILS)) diff --git a/src/libnm-std-aux/nm-default-std.h b/src/libnm-std-aux/nm-default-std.h index 45c0d153..075bdf97 100644 --- a/src/libnm-std-aux/nm-default-std.h +++ b/src/libnm-std-aux/nm-default-std.h @@ -41,7 +41,7 @@ #endif #if NM_MORE_ASSERTS == 0 -/* The cast macros like NM_TYPE() are implemented via G_TYPE_CHECK_INSTANCE_CAST() +/* The cast macros like NM_TYPE() are implemented via _NM_G_TYPE_CHECK_INSTANCE_CAST() * and _G_TYPE_CIC(). The latter, by default performs runtime checks of the type * by calling g_type_check_instance_cast(). * This check has a certain overhead without being helpful. diff --git a/src/libnm-std-aux/nm-std-aux.h b/src/libnm-std-aux/nm-std-aux.h index d49f7c43..e556aa4b 100644 --- a/src/libnm-std-aux/nm-std-aux.h +++ b/src/libnm-std-aux/nm-std-aux.h @@ -16,9 +16,11 @@ #define _nm_packed __attribute__((__packed__)) #define _nm_unused __attribute__((__unused__)) +#define _nm_always_inline __attribute__((__always_inline__)) #define _nm_used __attribute__((__used__)) #define _nm_pure __attribute__((__pure__)) #define _nm_const __attribute__((__const__)) +#define _nm_noreturn __attribute__((__noreturn__)) #define _nm_warn_unused_result __attribute__((__warn_unused_result__)) #define _nm_printf(a, b) __attribute__((__format__(__printf__, a, b))) #define _nm_align(s) __attribute__((__aligned__(s))) @@ -42,8 +44,8 @@ #endif #endif -#if defined(__clang__) && __clang_major__ == 13 -/* Clang 13 can emit -Wunused-but-set-variable warning for cleanup variables +#if defined(__clang__) +/* Clang can emit -Wunused-but-set-variable warning for cleanup variables * that are only assigned (never used otherwise). Hack around */ #define _nm_auto_extra _nm_unused #else @@ -73,6 +75,14 @@ /*****************************************************************************/ +/* This is mainly used in case of failed assertions. Usually assert() itself + * already ensures that the code path is marked as unreachable, however with + * NDEBUG that might not be the case. We want to mark the code as unreachable + * even with NDEBUG/G_DISABLE_ASSERT. */ +#define _nm_unreachable_code() __builtin_unreachable() + +/*****************************************************************************/ + #ifndef _NM_CC_SUPPORT_AUTO_TYPE #if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))) #define _NM_CC_SUPPORT_AUTO_TYPE 1 @@ -154,6 +164,15 @@ typedef uint64_t _nm_bitwise nm_be64_t; /*****************************************************************************/ +/* NM_BOOLEAN_EXPR(expr) exists to ensure that there is still a compiler + * warning when accidentally(?) using assignments like `NM_BOOLEAN_EXPR(x = 1)` + * Compiler will warn about that and suggest either == or additional parentheses + * `NM_BOOLEAN_EXPR((x = 1))`. + * + * This also is true for users of this macro, like `NM_LIKELY(x = 1)` and further + * up `nm_assert(x = 1)`. Those users must make sure not themselves adding additional + * parentheses around the condition. + */ #define _NM_BOOLEAN_EXPR_IMPL(v, expr) \ ({ \ int NM_UNIQ_T(V, v); \ @@ -164,14 +183,22 @@ typedef uint64_t _nm_bitwise nm_be64_t; NM_UNIQ_T(V, v) = 0; \ NM_UNIQ_T(V, v); \ }) -#define NM_BOOLEAN_EXPR(expr) _NM_BOOLEAN_EXPR_IMPL(NM_UNIQ, expr) + +#if defined(__GNUC__) && (__GNUC__ > 4) +#define NM_BOOLEAN_EXPR(expr) \ + __builtin_choose_expr(__builtin_constant_p(expr), \ + (!!(expr)), \ + _NM_BOOLEAN_EXPR_IMPL(NM_UNIQ, expr)) +#else +#define NM_BOOLEAN_EXPR(expr) (!!(expr)) +#endif #if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) -#define NM_LIKELY(expr) (__builtin_expect(NM_BOOLEAN_EXPR(expr), 1)) -#define NM_UNLIKELY(expr) (__builtin_expect(NM_BOOLEAN_EXPR(expr), 0)) +#define NM_LIKELY(expr) __builtin_expect(NM_BOOLEAN_EXPR(expr), 1) +#define NM_UNLIKELY(expr) __builtin_expect(NM_BOOLEAN_EXPR(expr), 0) #else -#define NM_LIKELY(expr) (NM_BOOLEAN_EXPR(expr)) -#define NM_UNLIKELY(expr) (NM_BOOLEAN_EXPR(expr)) +#define NM_LIKELY(expr) NM_BOOLEAN_EXPR(expr) +#define NM_UNLIKELY(expr) NM_BOOLEAN_EXPR(expr) #endif /*****************************************************************************/ @@ -192,67 +219,106 @@ typedef uint64_t _nm_bitwise nm_be64_t; #define NM_MORE_ASSERTS 0 #endif -#ifndef _nm_assert_call -#define _nm_assert_call(cond) assert(cond) -#define _nm_assert_call_not_reached() assert(0) +#if NM_MORE_ASSERTS == 0 +/* The string with the assertion check and the function name blows up the + * binary size. In production mode, let's drop those, similar to + * g_assertion_message_expr. + * + * Note that can be included multiple times. We can thus + * not redefine __assert_fail(...). Instead, just redefine the name + * __assert_fail. */ +_nm_noreturn static inline void +_nm_assert_fail_internal(const char *assertion, + const char *file, + unsigned int line, + const char *function) +{ + __assert_fail("", file, line, ""); +} +#define __assert_fail _nm_assert_fail_internal #endif -#if NM_MORE_ASSERTS -#define nm_assert(cond) \ - ({ \ - _nm_assert_call(cond); \ - 1; \ - }) -#define nm_assert_se(cond) \ - ({ \ - if (NM_LIKELY(cond)) { \ - ; \ - } else { \ - _nm_assert_call(0 && (cond)); \ - } \ - 1; \ - }) -#define nm_assert_not_reached() \ - ({ \ - _nm_assert_call_not_reached(); \ - 1; \ - }) +#ifndef NDEBUG +#define _NM_ASSERT_FAIL_ENABLED 1 +#define _nm_assert_fail(msg) __assert_fail((msg), __FILE__, __LINE__, __func__) #else -#define nm_assert(cond) \ - ({ \ - if (0) { \ - if (cond) {} \ - } \ - 1; \ +#define _NM_ASSERT_FAIL_ENABLED 0 +#define _nm_assert_fail(msg) \ + do { \ + _nm_unused const char *_msg = (msg); \ + \ + _nm_unreachable_code(); \ + } while (0) +#endif + +#define NM_MORE_ASSERTS_EFFECTIVE (_NM_ASSERT_FAIL_ENABLED ? NM_MORE_ASSERTS : 0) + +#define nm_assert(cond) \ + ({ \ + /* nm_assert() must do *nothing* of effect, except evaluating + * @cond (0 or 1 times). + * + * As such, nm_assert() is async-signal-safe (provided @cond is, and + * the assertion does not fail). */ \ + if (NM_MORE_ASSERTS_EFFECTIVE == 0) { \ + if (__builtin_constant_p(cond) && !(cond)) { \ + /* Constant expressions are still evaluated and result + * in unreachable code. This handles nm_assert(FALSE). */ \ + _nm_unreachable_code(); \ + } \ + /* pass */ \ + } else if (NM_LIKELY(cond)) { \ + /* pass */ \ + } else { \ + _nm_assert_fail(#cond); \ + } \ + 1; \ }) -#define nm_assert_se(cond) \ - ({ \ - if (NM_LIKELY(cond)) { \ - ; \ - } \ - 1; \ + +#define nm_assert_se(cond) \ + ({ \ + /* nm_assert() must do *nothing* of effect, except evaluating + * @cond (exactly 1 times). + * + * As such, nm_assert() is async-signal-safe (provided @cond is, and + * the assertion does not fail). */ \ + if (NM_LIKELY(cond)) { \ + /* pass */ \ + } else { \ + if (NM_MORE_ASSERTS_EFFECTIVE != 0) { \ + _nm_assert_fail(#cond); \ + } \ + _nm_unreachable_code(); \ + } \ + 1; \ + }) + +#define nm_assert_not_reached() \ + ({ \ + _nm_assert_fail("unreachable"); \ + 1; \ }) -#define nm_assert_not_reached() ({ 1; }) -#endif /* This is similar nm_assert_not_reached(), but it's supposed to be used only during * development. Like _XXX_ comments, they can be used as a marker that something still * needs to be done. */ -#define XXX(msg) \ - nm_assert(!"X" \ - "XX error: " msg "") +#define XXX(msg) \ + ({ \ + _nm_assert_fail("X" \ + "XX error: " msg ""); \ + 1; \ + }) -#define nm_assert_unreachable_val(val) \ - ({ \ - nm_assert_not_reached(); \ - (val); \ +#define nm_assert_unreachable_val(val) \ + ({ \ + _nm_assert_fail("unreachable value " #val); \ + (val); \ }) #define NM_STATIC_ASSERT(cond) static_assert(cond, "") #define NM_STATIC_ASSERT_EXPR_1(cond) \ - (sizeof(struct { char __static_assert_expr_1[(cond) ? 1 : -1]; }) == 1) -#define NM_STATIC_ASSERT_EXPR_VOID(cond) \ - ((void) (sizeof(struct { char __static_assert_expr_void[(cond) ? 1 : -1]; }) == 1)) + (!!sizeof(struct { unsigned __static_assert_expr_1 : ((cond) ? 2 : -1); })) +#define NM_STATIC_ASSERT_EXPR_VOID(cond) ((void) NM_STATIC_ASSERT_EXPR_1(cond)) /*****************************************************************************/ @@ -282,12 +348,8 @@ typedef uint64_t _nm_bitwise nm_be64_t; * It's useful to check the let the compiler ensure that @value is * of a certain type. */ #define _NM_ENSURE_TYPE(type, value) (_Generic((value), type : (value))) -#define _NM_ENSURE_TYPE_CONST(type, value) \ - (_Generic((value), const type \ - : ((const type)(value)), const type const \ - : ((const type)(value)), type \ - : ((const type)(value)), type const \ - : ((const type)(value)))) +#define _NM_ENSURE_TYPE_CONST(type, value) \ + (_Generic((value), const type : ((const type)(value)), type : ((const type)(value)))) #else #define _NM_ENSURE_TYPE(type, value) (value) #define _NM_ENSURE_TYPE_CONST(type, value) ((const type)(value)) @@ -413,6 +475,24 @@ nm_mult_clamped_u(unsigned a, unsigned b) /*****************************************************************************/ +static inline size_t +NM_ALIGN_TO(size_t l, size_t ali) +{ + nm_assert(nm_utils_is_power_of_two(ali)); + + if (l > SIZE_MAX - (ali - 1)) + return SIZE_MAX; /* indicate overflow */ + + return ((l + ali - 1) & ~(ali - 1)); +} + +#define NM_ALIGN4(l) NM_ALIGN_TO(l, 4) +#define NM_ALIGN8(l) NM_ALIGN_TO(l, 8) +#define NM_ALIGN(l) NM_ALIGN_TO(l, sizeof(void *)) +#define NM_ALIGN_PTR(p) ((void *) NM_ALIGN((uintptr_t) (p))) + +/*****************************************************************************/ + #define NM_SWAP(p_a, p_b) \ do { \ typeof(*(p_a)) *const _p_a = (p_a); \ @@ -504,10 +584,21 @@ nm_memeq(const void *s1, const void *s2, size_t len) return nm_memcmp(s1, s2, len) == 0; } +static inline void * +nm_memcpy(void *restrict dest, const void *restrict src, size_t n) +{ + /* Workaround undefined behavior in memcpy() with NULL pointers. */ + if (n == 0) + return dest; + + nm_assert(src); + return memcpy(dest, src, n); +} + /* * Very similar to g_str_has_prefix() with the obvious meaning. * Differences: - * 1) suffix is enforced to be a C string literal + * 1) prefix is enforced to be a C string literal * (it is thus more restricted, but you'll know it at compile time). * 2) it accepts str==NULL * (it is thus more forgiving than g_str_has_prefix()) @@ -902,38 +993,6 @@ _NM_IN_STRSET_EVAL_op_streq(const char *x1, const char *x) /*****************************************************************************/ -/** - * nm_close: - * - * Like close() but throws an assertion if the input fd is - * invalid. Closing an invalid fd is a programming error, so - * it's better to catch it early. - */ -static inline int -nm_close(int fd) -{ - int r; - - r = close(fd); - nm_assert(r != -1 || fd < 0 || errno != EBADF); - return r; -} - -static inline bool -nm_clear_fd(int *p_fd) -{ - int fd; - - if (!p_fd || (fd = *p_fd) < 0) - return false; - - *p_fd = -1; - nm_close(fd); - return true; -} - -/*****************************************************************************/ - /* Note: @value is only evaluated when *out_val is present. * Thus, * NM_SET_OUT (out_str, g_strdup ("hallo")); @@ -1011,13 +1070,106 @@ _nm_auto_protect_errno(const int *p_saved_errno) /*****************************************************************************/ +/** + * nm_close_with_error: + * + * Wrapper around close(). + * + * This fails an nm_assert() for EBADF with a non-negative file descriptor. Trying + * to close an invalid file descriptor is always a serious bug. Never use close() + * directly, because we want to catch such bugs. + * + * This also suppresses any EINTR and pretends success. That is appropriate + * on Linux (but not necessarily on other POSIX systems). + * + * In no case is it appropriate to use @fd afterwards (or retry). + * + * This function returns 0 on success, or a negative errno value. + * On success, errno is undefined afterwards. On failure, errno is + * the same as the (negative) return value. + * + * In the common case, when you don't intend to handle the error from close(), + * prefer nm_close() over nm_close_with_error(). Never use close() directly. + * + * The function is also async-signal-safe (unless an assertion fails). + * + * Returns: 0 on success or the negative errno from close(). + */ +static inline int +nm_close_with_error(int fd) +{ + int r; + + r = close(fd); + + if (r != 0) { + int errsv = errno; + + nm_assert(r == -1); + + /* EBADF indicates a bug. + * + * - if fd is non-negative, this means the tracking of the descriptor + * got messed up. That's very bad, somebody closed a wrong FD or we + * might do so. On a multi threaded application, messing up the tracking + * of the file descriptor means we race against closing an unrelated FD. + * - if fd is negative, it may not be a bug but intentional. However, our callers + * are not supposed to call close() on a negative FD either. Assert + * against that too. */ + nm_assert(errsv != EBADF); + + if (errsv == EINTR) { + /* There isn't really much we can do about EINTR. On Linux, always this means + * the FD was closed. On some POSIX systems that may be different and retry + * would be appropriate. + * + * Whether there was any IO error is unknown. Assume not and signal success. */ + return 0; + } + + return -errsv; + } + + return 0; +} + +/** + * nm_close: + * + * Wrapper around nm_close_with_error(), which ignores any error and preserves the + * caller's errno. + * + * We usually don't care about errors from close, so this is usually preferable over + * nm_close_with_error(). Never use close() directly. + * + * Everything from nm_close_with_error() applies. + */ +static inline void +nm_close(int fd) +{ + NM_AUTO_PROTECT_ERRNO(errsv); + + nm_close_with_error(fd); +} + +static inline bool +nm_clear_fd(int *p_fd) +{ + int fd; + + if (!p_fd || (fd = *p_fd) < 0) + return false; + + *p_fd = -1; + nm_close(fd); + return true; +} + static inline void _nm_auto_close(int *pfd) { - if (*pfd >= 0) { - NM_AUTO_PROTECT_ERRNO(errsv); - (void) nm_close(*pfd); - } + if (*pfd >= 0) + nm_close(*pfd); } #define nm_auto_close nm_auto(_nm_auto_close) @@ -1298,13 +1450,10 @@ nm_utils_addr_family_other(int addr_family) static inline size_t nm_utils_addr_family_to_size(int addr_family) { - switch (addr_family) { - case NM_AF_INET: - return NM_AF_INET_SIZE; - case NM_AF_INET6: + if (!NM_IS_IPv4(addr_family)) return NM_AF_INET6_SIZE; - } - return nm_assert_unreachable_val(0); + else + return NM_AF_INET_SIZE; } static inline size_t @@ -1341,4 +1490,33 @@ nm_utils_addr_family_from_size(size_t len) return NM_AF_UNSPEC; } +#define _NM_PTR_IS_ALIGNED_(uniq, type, ptr) \ + ({ \ + const void *const NM_UNIQ_T(_ptr, uniq) = (ptr); \ + \ + /* NULL is accepted too. */ \ + \ + (!NM_UNIQ_T(_ptr, uniq) \ + || ((((uintptr_t) (void *) NM_UNIQ_T(_ptr, uniq)) % _nm_alignof(type)) == 0)); \ + }) + +#define _NM_PTR_IS_ALIGNED(type, ptr) _NM_PTR_IS_ALIGNED_(NM_UNIQ, type, (ptr)) + +/* We build with "-Wcast-align=strict", which can warn about alignment problems + * with casting. In some cases, we know that the pointer has the suitable + * alignment and the cast is in fact correct. The way to disable the warning + * would be to cast ((Type *) ((void *) (ptr))). + * + * This macro does essentially that, but it also does an nm_assert() that the + * alignment of the pointer is suitable to cast to (Type *). */ +#define _NM_CAST_ALIGN(uniq, Type, ptr) \ + ({ \ + const void *const NM_UNIQ_T(_ptr, uniq) = (ptr); \ + \ + nm_assert(_NM_PTR_IS_ALIGNED(Type, NM_UNIQ_T(_ptr, uniq))); \ + \ + ((Type *) NM_UNIQ_T(_ptr, uniq)); \ + }) +#define NM_CAST_ALIGN(Type, ptr) _NM_CAST_ALIGN(NM_UNIQ, Type, ptr) + #endif /* __NM_STD_AUX_H__ */ diff --git a/src/libnm-systemd-core/meson.build b/src/libnm-systemd-core/meson.build index 70293a61..753ca149 100644 --- a/src/libnm-systemd-core/meson.build +++ b/src/libnm-systemd-core/meson.build @@ -7,12 +7,9 @@ libnm_systemd_core = static_library( 'src/libsystemd-network/dhcp6-network.c', 'src/libsystemd-network/dhcp6-option.c', 'src/libsystemd-network/dhcp6-protocol.c', - 'src/libsystemd-network/lldp-neighbor.c', - 'src/libsystemd-network/lldp-network.c', 'src/libsystemd-network/network-common.c', 'src/libsystemd-network/sd-dhcp6-client.c', 'src/libsystemd-network/sd-dhcp6-lease.c', - 'src/libsystemd-network/sd-lldp-rx.c', 'src/libsystemd/sd-event/event-util.c', 'src/libsystemd/sd-event/sd-event.c', 'src/libsystemd/sd-id128/id128-util.c', diff --git a/src/libnm-systemd-core/nm-sd.h b/src/libnm-systemd-core/nm-sd.h index 79bc5031..80af296e 100644 --- a/src/libnm-systemd-core/nm-sd.h +++ b/src/libnm-systemd-core/nm-sd.h @@ -7,7 +7,6 @@ #define __NM_SD_H__ #include "src/systemd/sd-dhcp6-client.h" -#include "src/systemd/sd-lldp-rx.h" /*****************************************************************************/ diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c index 05f2a383..14e9c806 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c @@ -213,47 +213,55 @@ bool dhcp6_option_can_request(uint16_t option) { } } -static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { - assert_return(buf, -EINVAL); - assert_return(*buf, -EINVAL); - assert_return(buflen, -EINVAL); +static int option_append_hdr(uint8_t **buf, size_t *offset, uint16_t optcode, size_t optlen) { + assert(buf); + assert(*buf); + assert(offset); - if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data)) + if (optlen > 0xffff) return -ENOBUFS; - unaligned_write_be16(*buf + offsetof(DHCP6Option, code), optcode); - unaligned_write_be16(*buf + offsetof(DHCP6Option, len), optlen); + if (optlen + offsetof(DHCP6Option, data) > SIZE_MAX - *offset) + return -ENOBUFS; - *buf += offsetof(DHCP6Option, data); - *buflen -= offsetof(DHCP6Option, data); + if (!GREEDY_REALLOC(*buf, *offset + optlen + offsetof(DHCP6Option, data))) + return -ENOMEM; + + unaligned_write_be16(*buf + *offset + offsetof(DHCP6Option, code), optcode); + unaligned_write_be16(*buf + *offset + offsetof(DHCP6Option, len), optlen); + *offset += offsetof(DHCP6Option, data); return 0; } -int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, - size_t optlen, const void *optval) { +int dhcp6_option_append( + uint8_t **buf, + size_t *offset, + uint16_t code, + size_t optlen, + const void *optval) { + int r; - assert_return(optval || optlen == 0, -EINVAL); + assert(optval || optlen == 0); - r = option_append_hdr(buf, buflen, code, optlen); + r = option_append_hdr(buf, offset, code, optlen); if (r < 0) return r; - *buf = mempcpy_safe(*buf, optval, optlen); - *buflen -= optlen; + memcpy_safe(*buf + *offset, optval, optlen); + *offset += optlen; return 0; } -int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options) { +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *offset, OrderedSet *vendor_options) { sd_dhcp6_option *options; int r; assert(buf); assert(*buf); - assert(buflen); - assert(vendor_options); + assert(offset); ORDERED_SET_FOREACH(options, vendor_options) { _cleanup_free_ uint8_t *p = NULL; @@ -270,7 +278,7 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet unaligned_write_be16(p + 6, options->length); memcpy(p + 8, options->data, options->length); - r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_OPTS, total, p); + r = dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_VENDOR_OPTS, total, p); if (r < 0) return r; } @@ -278,69 +286,48 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet return 0; } -static int option_append_ia_address(uint8_t **buf, size_t *buflen, const struct iaaddr *address) { - struct iaaddr a; - int r; - +static int option_append_ia_address(uint8_t **buf, size_t *offset, const struct iaaddr *address) { assert(buf); assert(*buf); - assert(buflen); + assert(offset); assert(address); /* Do not append T1 and T2. */ - a = (struct iaaddr) { + const struct iaaddr a = { .address = address->address, }; - r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr)); - if (r < 0) - return r; - - *buf = mempcpy(*buf, &a, sizeof(struct iaaddr)); - *buflen -= sizeof(struct iaaddr); - - return offsetof(DHCP6Option, data) + sizeof(struct iaaddr); + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr), &a); } -static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct iapdprefix *prefix) { - struct iapdprefix p; - int r; - +static int option_append_pd_prefix(uint8_t **buf, size_t *offset, const struct iapdprefix *prefix) { assert(buf); assert(*buf); - assert(buflen); + assert(offset); assert(prefix); if (prefix->prefixlen == 0) return -EINVAL; /* Do not append T1 and T2. */ - p = (struct iapdprefix) { + const struct iapdprefix p = { .prefixlen = prefix->prefixlen, .address = prefix->address, }; - r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix)); - if (r < 0) - return r; - - *buf = mempcpy(*buf, &p, sizeof(struct iapdprefix)); - *buflen -= sizeof(struct iapdprefix); - - return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix); + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix), &p); } -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { +int dhcp6_option_append_ia(uint8_t **buf, size_t *offset, const DHCP6IA *ia) { + _cleanup_free_ uint8_t *data = NULL; struct ia_header header; - size_t ia_buflen; - uint8_t *ia_hdr; - uint16_t len; + size_t len; int r; - assert_return(buf, -EINVAL); - assert_return(*buf, -EINVAL); - assert_return(buflen, -EINVAL); - assert_return(ia, -EINVAL); + assert(buf); + assert(*buf); + assert(offset); + assert(ia); /* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */ @@ -364,43 +351,38 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { assert_not_reached(); } - if (*buflen < offsetof(DHCP6Option, data) + len) - return -ENOBUFS; - - ia_hdr = *buf; - ia_buflen = *buflen; - - /* The header will be written at the end of this function. */ - *buf += offsetof(DHCP6Option, data); - *buflen -= offsetof(DHCP6Option, data); + if (!GREEDY_REALLOC(data, len)) + return -ENOMEM; - *buf = mempcpy(*buf, &header, len); - *buflen -= len; + memcpy(data, &header, len); LIST_FOREACH(addresses, addr, ia->addresses) { if (ia->type == SD_DHCP6_OPTION_IA_PD) - r = option_append_pd_prefix(buf, buflen, &addr->iapdprefix); + r = option_append_pd_prefix(&data, &len, &addr->iapdprefix); else - r = option_append_ia_address(buf, buflen, &addr->iaaddr); + r = option_append_ia_address(&data, &len, &addr->iaaddr); if (r < 0) return r; - - len += r; } - return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len); + return dhcp6_option_append(buf, offset, ia->type, len, data); } -int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { +int dhcp6_option_append_fqdn(uint8_t **buf, size_t *offset, const char *fqdn) { uint8_t buffer[1 + DNS_WIRE_FORMAT_HOSTNAME_MAX]; int r; - assert_return(buf && *buf && buflen && fqdn, -EINVAL); + assert(buf); + assert(*buf); + assert(offset); + + if (isempty(fqdn)) + return 0; buffer[0] = DHCP6_FQDN_FLAG_S; /* Request server to perform AAAA RR DNS updates */ /* Store domain name after flags field */ - r = dns_name_to_wire_format(fqdn, buffer + 1, sizeof(buffer) - 1, false); + r = dns_name_to_wire_format(fqdn, buffer + 1, sizeof(buffer) - 1, false); if (r <= 0) return r; @@ -413,82 +395,70 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { if (dns_name_is_single_label(fqdn)) r--; - r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_CLIENT_FQDN, 1 + r, buffer); - - return r; + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_CLIENT_FQDN, 1 + r, buffer); } -int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class) { +int dhcp6_option_append_user_class(uint8_t **buf, size_t *offset, char * const *user_class) { _cleanup_free_ uint8_t *p = NULL; - size_t total = 0, offset = 0; + size_t n = 0; assert(buf); assert(*buf); - assert(buflen); - assert(!strv_isempty(user_class)); + assert(offset); + + if (strv_isempty(user_class)) + return 0; STRV_FOREACH(s, user_class) { size_t len = strlen(*s); - uint8_t *q; - if (len > 0xffff || len == 0) + if (len > UINT16_MAX || len == 0) return -EINVAL; - q = realloc(p, total + len + 2); - if (!q) - return -ENOMEM; - p = q; - - unaligned_write_be16(&p[offset], len); - memcpy(&p[offset + 2], *s, len); + if (!GREEDY_REALLOC(p, n + len + 2)) + return -ENOMEM; - offset += 2 + len; - total += 2 + len; + unaligned_write_be16(p + n, len); + memcpy(p + n + 2, *s, len); + n += len + 2; } - return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, total, p); + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_USER_CLASS, n, p); } -int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *vendor_class) { +int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *offset, char * const *vendor_class) { _cleanup_free_ uint8_t *p = NULL; - uint32_t enterprise_identifier; - size_t total, offset; + size_t n = 0; assert(buf); assert(*buf); - assert(buflen); - assert(!strv_isempty(vendor_class)); + assert(offset); - enterprise_identifier = htobe32(SYSTEMD_PEN); + if (strv_isempty(vendor_class)) + return 0; - p = memdup(&enterprise_identifier, sizeof(enterprise_identifier)); - if (!p) + if (!GREEDY_REALLOC(p, sizeof(be32_t))) return -ENOMEM; - total = sizeof(enterprise_identifier); - offset = total; + /* Enterprise Identifier */ + unaligned_write_be32(p, SYSTEMD_PEN); + n += sizeof(be32_t); STRV_FOREACH(s, vendor_class) { size_t len = strlen(*s); - uint8_t *q; if (len > UINT16_MAX || len == 0) return -EINVAL; - q = realloc(p, total + len + 2); - if (!q) + if (!GREEDY_REALLOC(p, n + len + 2)) return -ENOMEM; - p = q; - - unaligned_write_be16(&p[offset], len); - memcpy(&p[offset + 2], *s, len); - - offset += 2 + len; - total += 2 + len; + unaligned_write_be16(p + n, len); + memcpy(p + n + 2, *s, len); + n += len + 2; } - return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p); + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_VENDOR_CLASS, n, p); } int dhcp6_option_parse( diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h index 80aba7f3..36841dd2 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h @@ -72,13 +72,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6IA*, dhcp6_ia_free); bool dhcp6_option_can_request(uint16_t option); -int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, +int dhcp6_option_append(uint8_t **buf, size_t *offset, uint16_t code, size_t optlen, const void *optval); -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); -int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); -int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class); -int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class); -int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options); +int dhcp6_option_append_ia(uint8_t **buf, size_t *offset, const DHCP6IA *ia); +int dhcp6_option_append_fqdn(uint8_t **buf, size_t *offset, const char *fqdn); +int dhcp6_option_append_user_class(uint8_t **buf, size_t *offset, char * const *user_class); +int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *offset, char * const *vendor_class); +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *offset, OrderedSet *vendor_options); int dhcp6_option_parse( const uint8_t *buf, diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c index e19faa51..4e945777 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c @@ -84,3 +84,14 @@ static const char * const dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, DHCP6Status); + +int dhcp6_message_status_to_errno(DHCP6Status s) { + switch (s) { + case DHCP6_STATUS_SUCCESS: + return 0; + case DHCP6_STATUS_NO_BINDING: + return -EADDRNOTAVAIL; + default: + return -EINVAL; + } +} diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h index f4e47857..18217691 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h @@ -154,3 +154,4 @@ const char *dhcp6_message_type_to_string(DHCP6MessageType s) _const_; DHCP6MessageType dhcp6_message_type_from_string(const char *s) _pure_; const char *dhcp6_message_status_to_string(DHCP6Status s) _const_; DHCP6Status dhcp6_message_status_from_string(const char *s) _pure_; +int dhcp6_message_status_to_errno(DHCP6Status s); diff --git a/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c b/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c deleted file mode 100644 index d4aec944..00000000 --- a/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c +++ /dev/null @@ -1,798 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-core.h" - -#include "alloc-util.h" -#include "escape.h" -#include "ether-addr-util.h" -#include "hexdecoct.h" -#include "in-addr-util.h" -#include "lldp-neighbor.h" -#include "memory-util.h" -#include "missing_network.h" -#include "unaligned.h" - -static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) { - assert(id); - assert(state); - - siphash24_compress(id->chassis_id, id->chassis_id_size, state); - siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state); - siphash24_compress(id->port_id, id->port_id_size, state); - siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state); -} - -int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) { - assert(x); - assert(y); - - return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size) - ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size); -} - -DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR( - lldp_neighbor_hash_ops, - LLDPNeighborID, - lldp_neighbor_id_hash_func, - lldp_neighbor_id_compare_func, - sd_lldp_neighbor, - lldp_neighbor_unlink); - -int lldp_neighbor_prioq_compare_func(const void *a, const void *b) { - const sd_lldp_neighbor *x = a, *y = b; - - assert(x); - assert(y); - - return CMP(x->until, y->until); -} - -sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) { - if (!n) - return NULL; - - assert(n->n_ref > 0 || n->lldp_rx); - n->n_ref++; - - return n; -} - -static sd_lldp_neighbor *lldp_neighbor_free(sd_lldp_neighbor *n) { - if (!n) - return NULL; - - free(n->id.port_id); - free(n->id.chassis_id); - free(n->port_description); - free(n->system_name); - free(n->system_description); - free(n->mud_url); - free(n->chassis_id_as_string); - free(n->port_id_as_string); - return mfree(n); -} - -sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) { - - /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from - * the sd_lldp object. */ - - if (!n) - return NULL; - - assert(n->n_ref > 0); - n->n_ref--; - - if (n->n_ref <= 0 && !n->lldp_rx) - lldp_neighbor_free(n); - - return NULL; -} - -sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) { - - /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */ - - if (!n) - return NULL; - - if (!n->lldp_rx) - return NULL; - - /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is - * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register - * ourselves from the hashtable and sometimes are called after we already are de-registered. */ - - (void) hashmap_remove_value(n->lldp_rx->neighbor_by_id, &n->id, n); - - assert_se(prioq_remove(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx) >= 0); - - n->lldp_rx = NULL; - - if (n->n_ref <= 0) - lldp_neighbor_free(n); - - return NULL; -} - -sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) { - sd_lldp_neighbor *n; - - if (raw_size > SIZE_MAX - ALIGN(sizeof(sd_lldp_neighbor))) - return NULL; - - n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size); - if (!n) - return NULL; - - n->raw_size = raw_size; - n->n_ref = 1; - - return n; -} - -static int parse_string(sd_lldp_rx *lldp_rx, char **s, const void *q, size_t n) { - const char *p = q; - char *k; - - assert(s); - assert(p || n == 0); - - if (*s) { - log_lldp_rx(lldp_rx, "Found duplicate string, ignoring field."); - return 0; - } - - /* Strip trailing NULs, just to be nice */ - while (n > 0 && p[n-1] == 0) - n--; - - if (n <= 0) /* Ignore empty strings */ - return 0; - - /* Look for inner NULs */ - if (memchr(p, 0, n)) { - log_lldp_rx(lldp_rx, "Found inner NUL in string, ignoring field."); - return 0; - } - - /* Let's escape weird chars, for security reasons */ - k = cescape_length(p, n); - if (!k) - return log_oom_debug(); - - free(*s); - *s = k; - - return 1; -} - -int lldp_neighbor_parse(sd_lldp_neighbor *n) { - struct ether_header h; - const uint8_t *p; - size_t left; - int r; - - assert(n); - - if (n->raw_size < sizeof(struct ether_header)) - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "Received truncated packet, ignoring."); - - memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h)); - - if (h.ether_type != htobe16(ETHERTYPE_LLDP)) - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "Received packet with wrong type, ignoring."); - - if (h.ether_dhost[0] != 0x01 || - h.ether_dhost[1] != 0x80 || - h.ether_dhost[2] != 0xc2 || - h.ether_dhost[3] != 0x00 || - h.ether_dhost[4] != 0x00 || - !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "Received packet with wrong destination address, ignoring."); - - memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr)); - memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr)); - - p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header); - left = n->raw_size - sizeof(struct ether_header); - - for (;;) { - uint8_t type; - uint16_t length; - - if (left < 2) - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "TLV lacks header, ignoring."); - - type = p[0] >> 1; - length = p[1] + (((uint16_t) (p[0] & 1)) << 8); - p += 2, left -= 2; - - if (left < length) - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "TLV truncated, ignoring datagram."); - - switch (type) { - - case SD_LLDP_TYPE_END: - if (length != 0) - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "End marker TLV not zero-sized, ignoring datagram."); - - /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0 - * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */ - - goto end_marker; - - case SD_LLDP_TYPE_CHASSIS_ID: - if (length < 2 || length > 256) - /* includes the chassis subtype, hence one extra byte */ - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "Chassis ID field size out of range, ignoring datagram."); - - if (n->id.chassis_id) - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "Duplicate chassis ID field, ignoring datagram."); - - n->id.chassis_id = memdup(p, length); - if (!n->id.chassis_id) - return log_oom_debug(); - - n->id.chassis_id_size = length; - break; - - case SD_LLDP_TYPE_PORT_ID: - if (length < 2 || length > 256) - /* includes the port subtype, hence one extra byte */ - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "Port ID field size out of range, ignoring datagram."); - - if (n->id.port_id) - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "Duplicate port ID field, ignoring datagram."); - - n->id.port_id = memdup(p, length); - if (!n->id.port_id) - return log_oom_debug(); - - n->id.port_id_size = length; - break; - - case SD_LLDP_TYPE_TTL: - if (length != 2) - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "TTL field has wrong size, ignoring datagram."); - - if (n->has_ttl) - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "Duplicate TTL field, ignoring datagram."); - - n->ttl = unaligned_read_be16(p); - n->has_ttl = true; - break; - - case SD_LLDP_TYPE_PORT_DESCRIPTION: - r = parse_string(n->lldp_rx, &n->port_description, p, length); - if (r < 0) - return r; - break; - - case SD_LLDP_TYPE_SYSTEM_NAME: - r = parse_string(n->lldp_rx, &n->system_name, p, length); - if (r < 0) - return r; - break; - - case SD_LLDP_TYPE_SYSTEM_DESCRIPTION: - r = parse_string(n->lldp_rx, &n->system_description, p, length); - if (r < 0) - return r; - break; - - case SD_LLDP_TYPE_SYSTEM_CAPABILITIES: - if (length != 4) - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "System capabilities field has wrong size."); - - n->system_capabilities = unaligned_read_be16(p); - n->enabled_capabilities = unaligned_read_be16(p + 2); - n->has_capabilities = true; - break; - - case SD_LLDP_TYPE_PRIVATE: - if (length < 4) - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "Found private TLV that is too short, ignoring."); - - /* RFC 8520: MUD URL */ - if (memcmp(p, SD_LLDP_OUI_IANA_MUD, sizeof(SD_LLDP_OUI_IANA_MUD)) == 0) { - r = parse_string(n->lldp_rx, &n->mud_url, p + sizeof(SD_LLDP_OUI_IANA_MUD), - length - sizeof(SD_LLDP_OUI_IANA_MUD)); - if (r < 0) - return r; - } - break; - } - - p += length, left -= length; - } - -end_marker: - if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) - return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), - "One or more mandatory TLV missing in datagram. Ignoring."); - - n->rindex = sizeof(struct ether_header); - - return 0; -} - -void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) { - assert(n); - - if (n->ttl > 0) { - usec_t base; - - /* Use the packet's timestamp if there is one known */ - base = triple_timestamp_by_clock(&n->timestamp, CLOCK_BOOTTIME); - if (!timestamp_is_set(base)) - base = now(CLOCK_BOOTTIME); /* Otherwise, take the current time */ - - n->until = usec_add(base, n->ttl * USEC_PER_SEC); - } else - n->until = 0; - - if (n->lldp_rx) - prioq_reshuffle(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx); -} - -bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) { - if (a == b) - return true; - - if (!a || !b) - return false; - - if (a->raw_size != b->raw_size) - return false; - - return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0; -} - -int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) { - assert_return(n, -EINVAL); - assert_return(address, -EINVAL); - - *address = n->source_address; - return 0; -} - -int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) { - assert_return(n, -EINVAL); - assert_return(address, -EINVAL); - - *address = n->destination_address; - return 0; -} - -int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(size, -EINVAL); - - *ret = LLDP_NEIGHBOR_RAW(n); - *size = n->raw_size; - - return 0; -} - -int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) { - assert_return(n, -EINVAL); - assert_return(type, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(size, -EINVAL); - - assert(n->id.chassis_id_size > 0); - - *type = *(uint8_t*) n->id.chassis_id; - *ret = (uint8_t*) n->id.chassis_id + 1; - *size = n->id.chassis_id_size - 1; - - return 0; -} - -static int format_mac_address(const void *data, size_t sz, char **ret) { - struct ether_addr a; - char *k; - - assert(data || sz <= 0); - - if (sz != 7) - return 0; - - memcpy(&a, (uint8_t*) data + 1, sizeof(a)); - - k = new(char, ETHER_ADDR_TO_STRING_MAX); - if (!k) - return -ENOMEM; - - *ret = ether_addr_to_string(&a, k); - return 1; -} - -static int format_network_address(const void *data, size_t sz, char **ret) { - union in_addr_union a; - int family, r; - - if (sz == 6 && ((uint8_t*) data)[1] == 1) { - memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in)); - family = AF_INET; - } else if (sz == 18 && ((uint8_t*) data)[1] == 2) { - memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6)); - family = AF_INET6; - } else - return 0; - - r = in_addr_to_string(family, &a, ret); - if (r < 0) - return r; - return 1; -} - -int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) { - char *k; - int r; - - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (n->chassis_id_as_string) { - *ret = n->chassis_id_as_string; - return 0; - } - - assert(n->id.chassis_id_size > 0); - - switch (*(uint8_t*) n->id.chassis_id) { - - case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT: - case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS: - case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT: - case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME: - case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED: - k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1); - if (!k) - return -ENOMEM; - - goto done; - - case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS: - r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k); - if (r < 0) - return r; - if (r > 0) - goto done; - - break; - - case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS: - r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k); - if (r < 0) - return r; - if (r > 0) - goto done; - - break; - } - - /* Generic fallback */ - k = hexmem(n->id.chassis_id, n->id.chassis_id_size); - if (!k) - return -ENOMEM; - -done: - *ret = n->chassis_id_as_string = k; - return 0; -} - -int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) { - assert_return(n, -EINVAL); - assert_return(type, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(size, -EINVAL); - - assert(n->id.port_id_size > 0); - - *type = *(uint8_t*) n->id.port_id; - *ret = (uint8_t*) n->id.port_id + 1; - *size = n->id.port_id_size - 1; - - return 0; -} - -int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) { - char *k; - int r; - - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (n->port_id_as_string) { - *ret = n->port_id_as_string; - return 0; - } - - assert(n->id.port_id_size > 0); - - switch (*(uint8_t*) n->id.port_id) { - - case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS: - case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT: - case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME: - case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED: - k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1); - if (!k) - return -ENOMEM; - - goto done; - - case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS: - r = format_mac_address(n->id.port_id, n->id.port_id_size, &k); - if (r < 0) - return r; - if (r > 0) - goto done; - - break; - - case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS: - r = format_network_address(n->id.port_id, n->id.port_id_size, &k); - if (r < 0) - return r; - if (r > 0) - goto done; - - break; - } - - /* Generic fallback */ - k = hexmem(n->id.port_id, n->id.port_id_size); - if (!k) - return -ENOMEM; - -done: - *ret = n->port_id_as_string = k; - return 0; -} - -int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) { - assert_return(n, -EINVAL); - assert_return(ret_sec, -EINVAL); - - *ret_sec = n->ttl; - return 0; -} - -int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) { - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (!n->system_name) - return -ENODATA; - - *ret = n->system_name; - return 0; -} - -int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) { - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (!n->system_description) - return -ENODATA; - - *ret = n->system_description; - return 0; -} - -int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) { - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (!n->port_description) - return -ENODATA; - - *ret = n->port_description; - return 0; -} - -int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) { - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (!n->mud_url) - return -ENODATA; - - *ret = n->mud_url; - return 0; -} - -int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (!n->has_capabilities) - return -ENODATA; - - *ret = n->system_capabilities; - return 0; -} - -int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (!n->has_capabilities) - return -ENODATA; - - *ret = n->enabled_capabilities; - return 0; -} - -int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) { - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; - int r; - - assert_return(ret, -EINVAL); - assert_return(raw || raw_size <= 0, -EINVAL); - - n = lldp_neighbor_new(raw_size); - if (!n) - return -ENOMEM; - - memcpy_safe(LLDP_NEIGHBOR_RAW(n), raw, raw_size); - - r = lldp_neighbor_parse(n); - if (r < 0) - return r; - - *ret = TAKE_PTR(n); - - return r; -} - -int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) { - assert_return(n, -EINVAL); - - assert(n->raw_size >= sizeof(struct ether_header)); - n->rindex = sizeof(struct ether_header); - - return n->rindex < n->raw_size; -} - -int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) { - size_t length; - - assert_return(n, -EINVAL); - - if (n->rindex == n->raw_size) /* EOF */ - return -ESPIPE; - - if (n->rindex + 2 > n->raw_size) /* Truncated message */ - return -EBADMSG; - - length = LLDP_NEIGHBOR_TLV_LENGTH(n); - if (n->rindex + 2 + length > n->raw_size) - return -EBADMSG; - - n->rindex += 2 + length; - return n->rindex < n->raw_size; -} - -int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) { - assert_return(n, -EINVAL); - assert_return(type, -EINVAL); - - if (n->rindex == n->raw_size) /* EOF */ - return -ESPIPE; - - if (n->rindex + 2 > n->raw_size) - return -EBADMSG; - - *type = LLDP_NEIGHBOR_TLV_TYPE(n); - return 0; -} - -int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) { - uint8_t k; - int r; - - assert_return(n, -EINVAL); - - r = sd_lldp_neighbor_tlv_get_type(n, &k); - if (r < 0) - return r; - - return type == k; -} - -int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) { - const uint8_t *d; - size_t length; - int r; - - assert_return(n, -EINVAL); - assert_return(oui, -EINVAL); - assert_return(subtype, -EINVAL); - - r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE); - if (r < 0) - return r; - if (r == 0) - return -ENXIO; - - length = LLDP_NEIGHBOR_TLV_LENGTH(n); - if (length < 4) - return -EBADMSG; - - if (n->rindex + 2 + length > n->raw_size) - return -EBADMSG; - - d = LLDP_NEIGHBOR_TLV_DATA(n); - memcpy(oui, d, 3); - *subtype = d[3]; - - return 0; -} - -int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) { - uint8_t k[3], st; - int r; - - r = sd_lldp_neighbor_tlv_get_oui(n, k, &st); - if (r == -ENXIO) - return 0; - if (r < 0) - return r; - - return memcmp(k, oui, 3) == 0 && st == subtype; -} - -int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { - size_t length; - - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(size, -EINVAL); - - /* Note that this returns the full TLV, including the TLV header */ - - if (n->rindex + 2 > n->raw_size) - return -EBADMSG; - - length = LLDP_NEIGHBOR_TLV_LENGTH(n); - if (n->rindex + 2 + length > n->raw_size) - return -EBADMSG; - - *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex; - *size = length + 2; - - return 0; -} - -int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) { - assert_return(n, -EINVAL); - assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP); - assert_return(clock_supported(clock), -EOPNOTSUPP); - assert_return(ret, -EINVAL); - - if (!triple_timestamp_is_set(&n->timestamp)) - return -ENODATA; - - *ret = triple_timestamp_by_clock(&n->timestamp, clock); - return 0; -} diff --git a/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.h b/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.h deleted file mode 100644 index 016286b1..00000000 --- a/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.h +++ /dev/null @@ -1,92 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include -#include -#include - -#include "sd-lldp-rx.h" - -#include "hash-funcs.h" -#include "lldp-rx-internal.h" -#include "time-util.h" - -typedef struct LLDPNeighborID { - /* The spec calls this an "MSAP identifier" */ - void *chassis_id; - size_t chassis_id_size; - - void *port_id; - size_t port_id_size; -} LLDPNeighborID; - -struct sd_lldp_neighbor { - /* Neighbor objects stay around as long as they are linked into an "sd_lldp_rx" object or n_ref > 0. */ - sd_lldp_rx *lldp_rx; - unsigned n_ref; - - triple_timestamp timestamp; - - usec_t until; - unsigned prioq_idx; - - struct ether_addr source_address; - struct ether_addr destination_address; - - LLDPNeighborID id; - - /* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */ - size_t raw_size; - - /* The current read index for the iterative TLV interface */ - size_t rindex; - - /* And a couple of fields parsed out. */ - bool has_ttl:1; - bool has_capabilities:1; - bool has_port_vlan_id:1; - - uint16_t ttl; - - uint16_t system_capabilities; - uint16_t enabled_capabilities; - - char *port_description; - char *system_name; - char *system_description; - char *mud_url; - - uint16_t port_vlan_id; - - char *chassis_id_as_string; - char *port_id_as_string; -}; - -static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) { - return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor)); -} - -static inline uint8_t LLDP_NEIGHBOR_TLV_TYPE(const sd_lldp_neighbor *n) { - return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1; -} - -static inline size_t LLDP_NEIGHBOR_TLV_LENGTH(const sd_lldp_neighbor *n) { - uint8_t *p; - - p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex; - return p[1] + (((size_t) (p[0] & 1)) << 8); -} - -static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) { - return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2; -} - -extern const struct hash_ops lldp_neighbor_hash_ops; -int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y); -int lldp_neighbor_prioq_compare_func(const void *a, const void *b); - -sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n); -sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size); -int lldp_neighbor_parse(sd_lldp_neighbor *n); -void lldp_neighbor_start_ttl(sd_lldp_neighbor *n); -bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b); diff --git a/src/libnm-systemd-core/src/libsystemd-network/lldp-network.c b/src/libnm-systemd-core/src/libsystemd-network/lldp-network.c deleted file mode 100644 index 00eb9703..00000000 --- a/src/libnm-systemd-core/src/libsystemd-network/lldp-network.c +++ /dev/null @@ -1,72 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-core.h" - -#include -#include - -#include "fd-util.h" -#include "lldp-network.h" -#include "missing_network.h" -#include "socket-util.h" - -int lldp_network_bind_raw_socket(int ifindex) { - static const struct sock_filter filter[] = { - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */ - BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0), /* A != 00:00 */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0), /* A != 00:03 */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0), /* A != 00:0e */ - BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)), /* A <- protocol */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0), /* A != ETHERTYPE_LLDP */ - BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ - BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept packet */ - }; - static const struct sock_fprog fprog = { - .len = ELEMENTSOF(filter), - .filter = (struct sock_filter*) filter, - }; - struct packet_mreq mreq = { - .mr_ifindex = ifindex, - .mr_type = PACKET_MR_MULTICAST, - .mr_alen = ETH_ALEN, - .mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 } - }; - union sockaddr_union saddrll = { - .ll.sll_family = AF_PACKET, - .ll.sll_ifindex = ifindex, - }; - _cleanup_close_ int fd = -1; - - assert(ifindex > 0); - - fd = socket(AF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, - htobe16(ETHERTYPE_LLDP)); - if (fd < 0) - return -errno; - - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) - return -errno; - - /* customer bridge */ - if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) - return -errno; - - /* non TPMR bridge */ - mreq.mr_address[ETH_ALEN - 1] = 0x03; - if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) - return -errno; - - /* nearest bridge */ - mreq.mr_address[ETH_ALEN - 1] = 0x0E; - if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) - return -errno; - - if (bind(fd, &saddrll.sa, sizeof(saddrll.ll)) < 0) - return -errno; - - return TAKE_FD(fd); -} diff --git a/src/libnm-systemd-core/src/libsystemd-network/lldp-network.h b/src/libnm-systemd-core/src/libsystemd-network/lldp-network.h deleted file mode 100644 index bc69b324..00000000 --- a/src/libnm-systemd-core/src/libsystemd-network/lldp-network.h +++ /dev/null @@ -1,6 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include "sd-event.h" - -int lldp_network_bind_raw_socket(int ifindex); diff --git a/src/libnm-systemd-core/src/libsystemd-network/lldp-rx-internal.h b/src/libnm-systemd-core/src/libsystemd-network/lldp-rx-internal.h deleted file mode 100644 index 83d0bc46..00000000 --- a/src/libnm-systemd-core/src/libsystemd-network/lldp-rx-internal.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include "sd-event.h" -#include "sd-lldp-rx.h" - -#include "hashmap.h" -#include "network-common.h" -#include "prioq.h" - -struct sd_lldp_rx { - unsigned n_ref; - - int ifindex; - char *ifname; - int fd; - - sd_event *event; - int64_t event_priority; - sd_event_source *io_event_source; - sd_event_source *timer_event_source; - - Prioq *neighbor_by_expiry; - Hashmap *neighbor_by_id; - - uint64_t neighbors_max; - - sd_lldp_rx_callback_t callback; - void *userdata; - - uint16_t capability_mask; - - struct ether_addr filter_address; -}; - -const char* lldp_rx_event_to_string(sd_lldp_rx_event_t e) _const_; -sd_lldp_rx_event_t lldp_rx_event_from_string(const char *s) _pure_; - -#define log_lldp_rx_errno(lldp_rx, error, fmt, ...) \ - log_interface_prefix_full_errno( \ - "LLDP Rx: ", \ - sd_lldp_rx, lldp_rx, \ - error, fmt, ##__VA_ARGS__) -#define log_lldp_rx(lldp_rx, fmt, ...) \ - log_interface_prefix_full_errno_zerook( \ - "LLDP Rx: ", \ - sd_lldp_rx, lldp_rx, \ - 0, fmt, ##__VA_ARGS__) diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c index fbe15f00..db159660 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c @@ -213,7 +213,7 @@ static int dhcp6_client_set_duid_internal( if (r < 0) return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m"); - log_dhcp6_client(client, "Using DUID of type %u of incorrect length, proceeding.", duid_type); + log_dhcp6_client(client, "Using DUID of type %i of incorrect length, proceeding.", duid_type); } client->duid.type = htobe16(duid_type); @@ -553,13 +553,9 @@ static void client_notify(sd_dhcp6_client *client, int event) { client->callback(client, event, client->userdata); } -static void client_stop(sd_dhcp6_client *client, int error) { - DHCP6_CLIENT_DONT_DESTROY(client); - +static void client_cleanup(sd_dhcp6_client *client) { assert(client); - client_notify(client, error); - client->lease = sd_dhcp6_lease_unref(client->lease); /* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode, @@ -576,10 +572,20 @@ static void client_stop(sd_dhcp6_client *client, int error) { client_set_state(client, DHCP6_STATE_STOPPED); } +static void client_stop(sd_dhcp6_client *client, int error) { + DHCP6_CLIENT_DONT_DESTROY(client); + + assert(client); + + client_notify(client, error); + + client_cleanup(client); +} + static int client_append_common_options_in_managed_mode( sd_dhcp6_client *client, - uint8_t **opt, - size_t *optlen, + uint8_t **buf, + size_t *offset, const DHCP6IA *ia_na, const DHCP6IA *ia_pd) { @@ -591,44 +597,37 @@ static int client_append_common_options_in_managed_mode( DHCP6_STATE_REQUEST, DHCP6_STATE_RENEW, DHCP6_STATE_REBIND)); - assert(opt); - assert(optlen); + assert(buf); + assert(*buf); + assert(offset); if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && ia_na) { - r = dhcp6_option_append_ia(opt, optlen, ia_na); + r = dhcp6_option_append_ia(buf, offset, ia_na); if (r < 0) return r; } if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && ia_pd) { - r = dhcp6_option_append_ia(opt, optlen, ia_pd); + r = dhcp6_option_append_ia(buf, offset, ia_pd); if (r < 0) return r; } - if (client->fqdn) { - r = dhcp6_option_append_fqdn(opt, optlen, client->fqdn); - if (r < 0) - return r; - } + r = dhcp6_option_append_fqdn(buf, offset, client->fqdn); + if (r < 0) + return r; - if (client->user_class) { - r = dhcp6_option_append_user_class(opt, optlen, client->user_class); - if (r < 0) - return r; - } + r = dhcp6_option_append_user_class(buf, offset, client->user_class); + if (r < 0) + return r; - if (client->vendor_class) { - r = dhcp6_option_append_vendor_class(opt, optlen, client->vendor_class); - if (r < 0) - return r; - } + r = dhcp6_option_append_vendor_class(buf, offset, client->vendor_class); + if (r < 0) + return r; - if (!ordered_set_isempty(client->vendor_options)) { - r = dhcp6_option_append_vendor_option(opt, optlen, client->vendor_options); - if (r < 0) - return r; - } + r = dhcp6_option_append_vendor_option(buf, offset, client->vendor_options); + if (r < 0) + return r; return 0; } @@ -652,41 +651,42 @@ static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) } } -static int client_append_oro(sd_dhcp6_client *client, uint8_t **opt, size_t *optlen) { - _cleanup_free_ be16_t *buf = NULL; +static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *offset) { + _cleanup_free_ be16_t *p = NULL; be16_t *req_opts; size_t n; assert(client); - assert(opt); - assert(optlen); + assert(buf); + assert(*buf); + assert(offset); switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: n = client->n_req_opts; - buf = new(be16_t, n + 2); - if (!buf) + p = new(be16_t, n + 2); + if (!p) return -ENOMEM; - memcpy_safe(buf, client->req_opts, n * sizeof(be16_t)); - buf[n++] = htobe16(SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME); /* RFC 8415 section 21.23 */ - buf[n++] = htobe16(SD_DHCP6_OPTION_INF_MAX_RT); /* RFC 8415 section 21.25 */ + memcpy_safe(p, client->req_opts, n * sizeof(be16_t)); + p[n++] = htobe16(SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME); /* RFC 8415 section 21.23 */ + p[n++] = htobe16(SD_DHCP6_OPTION_INF_MAX_RT); /* RFC 8415 section 21.25 */ - typesafe_qsort(buf, n, be16_compare_func); - req_opts = buf; + typesafe_qsort(p, n, be16_compare_func); + req_opts = p; break; case DHCP6_STATE_SOLICITATION: n = client->n_req_opts; - buf = new(be16_t, n + 1); - if (!buf) + p = new(be16_t, n + 1); + if (!p) return -ENOMEM; - memcpy_safe(buf, client->req_opts, n * sizeof(be16_t)); - buf[n++] = htobe16(SD_DHCP6_OPTION_SOL_MAX_RT); /* RFC 8415 section 21.24 */ + memcpy_safe(p, client->req_opts, n * sizeof(be16_t)); + p[n++] = htobe16(SD_DHCP6_OPTION_SOL_MAX_RT); /* RFC 8415 section 21.24 */ - typesafe_qsort(buf, n, be16_compare_func); - req_opts = buf; + typesafe_qsort(p, n, be16_compare_func); + req_opts = p; break; default: @@ -694,18 +694,21 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **opt, size_t *opt req_opts = client->req_opts; } - return dhcp6_option_append(opt, optlen, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts); + if (n == 0) + return 0; + + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts); } int dhcp6_client_send_message(sd_dhcp6_client *client) { - _cleanup_free_ DHCP6Message *message = NULL; + _cleanup_free_ uint8_t *buf = NULL; struct in6_addr all_servers = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; struct sd_dhcp6_option *j; - size_t len, optlen = 512; - uint8_t *opt; usec_t elapsed_usec, time_now; be16_t elapsed_time; + DHCP6Message *message; + size_t offset; int r; assert(client); @@ -715,16 +718,13 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { if (r < 0) return r; - len = sizeof(DHCP6Message) + optlen; - - message = malloc0(len); - if (!message) + if (!GREEDY_REALLOC0(buf, offsetof(DHCP6Message, options))) return -ENOMEM; - opt = (uint8_t *)(message + 1); - + message = (DHCP6Message*) buf; message->transaction_id = client->transaction_id; message->type = client_message_type_from_state(client); + offset = offsetof(DHCP6Message, options); switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: @@ -732,12 +732,12 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { case DHCP6_STATE_SOLICITATION: if (client->rapid_commit) { - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); + r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); if (r < 0) return r; } - r = client_append_common_options_in_managed_mode(client, &opt, &optlen, + r = client_append_common_options_in_managed_mode(client, &buf, &offset, &client->ia_na, &client->ia_pd); if (r < 0) return r; @@ -746,7 +746,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID, + r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_SERVERID, client->lease->serverid_len, client->lease->serverid); if (r < 0) @@ -757,7 +757,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { assert(client->lease); - r = client_append_common_options_in_managed_mode(client, &opt, &optlen, + r = client_append_common_options_in_managed_mode(client, &buf, &offset, client->lease->ia_na, client->lease->ia_pd); if (r < 0) return r; @@ -770,24 +770,24 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { } if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_MUD_URL_V6, + r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), client->mudurl); if (r < 0) return r; } - r = client_append_oro(client, &opt, &optlen); + r = client_append_oro(client, &buf, &offset); if (r < 0) return r; - assert(client->duid_len); - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID, + assert(client->duid_len > 0); + r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_CLIENTID, client->duid_len, &client->duid); if (r < 0) return r; ORDERED_HASHMAP_FOREACH(j, client->extra_options) { - r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data); + r = dhcp6_option_append(&buf, &offset, j->option, j->length, j->data); if (r < 0) return r; } @@ -797,18 +797,16 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { * been trying to complete a DHCP message exchange. */ elapsed_usec = MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX); elapsed_time = htobe16(elapsed_usec); - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time); + r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time); if (r < 0) return r; - r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message, - len - optlen); + r = dhcp6_network_send_udp_socket(client->fd, &all_servers, buf, offset); if (r < 0) return r; log_dhcp6_client(client, "Sent %s", - dhcp6_message_type_to_string(message->type)); - + dhcp6_message_type_to_string(client_message_type_from_state(client))); return 0; } @@ -1143,6 +1141,20 @@ static int client_process_reply( return log_invalid_message_type(client, message); r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); + if (r == -EADDRNOTAVAIL) { + + /* If NoBinding status code is received, we cannot request the address anymore. + * Let's restart transaction from the beginning. */ + + if (client->state == DHCP6_STATE_REQUEST) + /* The lease is not acquired yet, hence it is not necessary to notify the restart. */ + client_cleanup(client); + else + /* We need to notify the previous lease was expired. */ + client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE); + + return client_start_transaction(client, DHCP6_STATE_SOLICITATION); + } if (r < 0) return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); @@ -1291,7 +1303,7 @@ static int client_receive_message( if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP && cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) - triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg))); + triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) (void *) CMSG_DATA(cmsg))); } if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff))) diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c index 55c7d627..5dcd2eb2 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c @@ -514,7 +514,7 @@ static int dhcp6_lease_parse_message( return log_dhcp6_client_errno(client, r, "Failed to parse status code: %m"); if (r > 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + return log_dhcp6_client_errno(client, dhcp6_message_status_to_errno(r), "Received %s message with non-zero status: %s%s%s", dhcp6_message_type_to_string(message->type), strempty(msg), isempty(msg) ? "" : ": ", @@ -621,7 +621,7 @@ static int dhcp6_lease_parse_message( return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received information refresh time option with an invalid length (%zu).", optlen); - irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC; + irt = unaligned_read_be32(optval) * USEC_PER_SEC; break; } } diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c b/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c deleted file mode 100644 index 496eb8f6..00000000 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c +++ /dev/null @@ -1,528 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-core.h" - -#include -#include -#include - -#include "sd-lldp-rx.h" - -#include "alloc-util.h" -#include "ether-addr-util.h" -#include "event-util.h" -#include "fd-util.h" -#include "lldp-neighbor.h" -#include "lldp-network.h" -#include "lldp-rx-internal.h" -#include "memory-util.h" -#include "network-common.h" -#include "socket-util.h" -#include "sort-util.h" -#include "string-table.h" - -#define LLDP_DEFAULT_NEIGHBORS_MAX 128U - -static const char * const lldp_rx_event_table[_SD_LLDP_RX_EVENT_MAX] = { - [SD_LLDP_RX_EVENT_ADDED] = "added", - [SD_LLDP_RX_EVENT_REMOVED] = "removed", - [SD_LLDP_RX_EVENT_UPDATED] = "updated", - [SD_LLDP_RX_EVENT_REFRESHED] = "refreshed", -}; - -DEFINE_STRING_TABLE_LOOKUP(lldp_rx_event, sd_lldp_rx_event_t); - -static void lldp_rx_flush_neighbors(sd_lldp_rx *lldp_rx) { - assert(lldp_rx); - - hashmap_clear(lldp_rx->neighbor_by_id); -} - -static void lldp_rx_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n) { - assert(lldp_rx); - assert(event >= 0 && event < _SD_LLDP_RX_EVENT_MAX); - - if (!lldp_rx->callback) - return (void) log_lldp_rx(lldp_rx, "Received '%s' event.", lldp_rx_event_to_string(event)); - - log_lldp_rx(lldp_rx, "Invoking callback for '%s' event.", lldp_rx_event_to_string(event)); - lldp_rx->callback(lldp_rx, event, n, lldp_rx->userdata); -} - -static int lldp_rx_make_space(sd_lldp_rx *lldp_rx, size_t extra) { - usec_t t = USEC_INFINITY; - bool changed = false; - - assert(lldp_rx); - - /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries - * are free. */ - - for (;;) { - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; - - n = prioq_peek(lldp_rx->neighbor_by_expiry); - if (!n) - break; - - sd_lldp_neighbor_ref(n); - - if (hashmap_size(lldp_rx->neighbor_by_id) > LESS_BY(lldp_rx->neighbors_max, extra)) - goto remove_one; - - if (t == USEC_INFINITY) - t = now(CLOCK_BOOTTIME); - - if (n->until > t) - break; - - remove_one: - lldp_neighbor_unlink(n); - lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, n); - changed = true; - } - - return changed; -} - -static bool lldp_rx_keep_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) { - assert(lldp_rx); - assert(n); - - /* Don't keep data with a zero TTL */ - if (n->ttl <= 0) - return false; - - /* Filter out data from the filter address */ - if (!ether_addr_is_null(&lldp_rx->filter_address) && - ether_addr_equal(&lldp_rx->filter_address, &n->source_address)) - return false; - - /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with - * no caps field set. */ - if (n->has_capabilities && - (n->enabled_capabilities & lldp_rx->capability_mask) == 0) - return false; - - /* Keep everything else */ - return true; -} - -static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor); - -static int lldp_rx_add_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) { - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL; - bool keep; - int r; - - assert(lldp_rx); - assert(n); - assert(!n->lldp_rx); - - keep = lldp_rx_keep_neighbor(lldp_rx, n); - - /* First retrieve the old entry for this MSAP */ - old = hashmap_get(lldp_rx->neighbor_by_id, &n->id); - if (old) { - sd_lldp_neighbor_ref(old); - - if (!keep) { - lldp_neighbor_unlink(old); - lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old); - return 0; - } - - if (lldp_neighbor_equal(n, old)) { - /* Is this equal, then restart the TTL counter, but don't do anything else. */ - old->timestamp = n->timestamp; - lldp_rx_start_timer(lldp_rx, old); - lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REFRESHED, old); - return 0; - } - - /* Data changed, remove the old entry, and add a new one */ - lldp_neighbor_unlink(old); - - } else if (!keep) - return 0; - - /* Then, make room for at least one new neighbor */ - lldp_rx_make_space(lldp_rx, 1); - - r = hashmap_ensure_put(&lldp_rx->neighbor_by_id, &lldp_neighbor_hash_ops, &n->id, n); - if (r < 0) - goto finish; - - r = prioq_ensure_put(&lldp_rx->neighbor_by_expiry, lldp_neighbor_prioq_compare_func, n, &n->prioq_idx); - if (r < 0) { - assert_se(hashmap_remove(lldp_rx->neighbor_by_id, &n->id) == n); - goto finish; - } - - n->lldp_rx = lldp_rx; - - lldp_rx_start_timer(lldp_rx, n); - lldp_rx_callback(lldp_rx, old ? SD_LLDP_RX_EVENT_UPDATED : SD_LLDP_RX_EVENT_ADDED, n); - - return 1; - -finish: - if (old) - lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old); - - return r; -} - -static int lldp_rx_handle_datagram(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) { - int r; - - assert(lldp_rx); - assert(n); - - r = lldp_neighbor_parse(n); - if (r < 0) - return r; - - r = lldp_rx_add_neighbor(lldp_rx, n); - if (r < 0) - return log_lldp_rx_errno(lldp_rx, r, "Failed to add datagram. Ignoring."); - - log_lldp_rx(lldp_rx, "Successfully processed LLDP datagram."); - return 0; -} - -static int lldp_rx_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; - ssize_t space, length; - sd_lldp_rx *lldp_rx = userdata; - struct timespec ts; - - assert(fd >= 0); - assert(lldp_rx); - - space = next_datagram_size_fd(fd); - if (space < 0) { - if (ERRNO_IS_TRANSIENT(space) || ERRNO_IS_DISCONNECT(space)) - return 0; - - log_lldp_rx_errno(lldp_rx, space, "Failed to determine datagram size to read, ignoring: %m"); - return 0; - } - - n = lldp_neighbor_new(space); - if (!n) { - log_oom_debug(); - return 0; - } - - length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT); - if (length < 0) { - if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno)) - return 0; - - log_lldp_rx_errno(lldp_rx, errno, "Failed to read LLDP datagram, ignoring: %m"); - return 0; - } - - if ((size_t) length != n->raw_size) { - log_lldp_rx(lldp_rx, "Packet size mismatch, ignoring"); - return 0; - } - - /* Try to get the timestamp of this packet if it is known */ - if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0) - triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts)); - else - triple_timestamp_get(&n->timestamp); - - (void) lldp_rx_handle_datagram(lldp_rx, n); - return 0; -} - -static void lldp_rx_reset(sd_lldp_rx *lldp_rx) { - assert(lldp_rx); - - (void) event_source_disable(lldp_rx->timer_event_source); - lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source); - lldp_rx->fd = safe_close(lldp_rx->fd); -} - -int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx) { - if (!lldp_rx) - return false; - - return lldp_rx->fd >= 0; -} - -int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) { - int r; - - assert_return(lldp_rx, -EINVAL); - assert_return(lldp_rx->event, -EINVAL); - assert_return(lldp_rx->ifindex > 0, -EINVAL); - - if (sd_lldp_rx_is_running(lldp_rx)) - return 0; - - assert(!lldp_rx->io_event_source); - - lldp_rx->fd = lldp_network_bind_raw_socket(lldp_rx->ifindex); - if (lldp_rx->fd < 0) - return lldp_rx->fd; - - r = sd_event_add_io(lldp_rx->event, &lldp_rx->io_event_source, lldp_rx->fd, EPOLLIN, lldp_rx_receive_datagram, lldp_rx); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(lldp_rx->io_event_source, lldp_rx->event_priority); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(lldp_rx->io_event_source, "lldp-rx-io"); - - log_lldp_rx(lldp_rx, "Started LLDP client"); - return 1; - -fail: - lldp_rx_reset(lldp_rx); - return r; -} - -int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) { - if (!sd_lldp_rx_is_running(lldp_rx)) - return 0; - - log_lldp_rx(lldp_rx, "Stopping LLDP client"); - - lldp_rx_reset(lldp_rx); - lldp_rx_flush_neighbors(lldp_rx); - - return 1; -} - -int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) { - int r; - - assert_return(lldp_rx, -EINVAL); - assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY); - assert_return(!lldp_rx->event, -EBUSY); - - if (event) - lldp_rx->event = sd_event_ref(event); - else { - r = sd_event_default(&lldp_rx->event); - if (r < 0) - return r; - } - - lldp_rx->event_priority = priority; - - return 0; -} - -int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) { - assert_return(lldp_rx, -EINVAL); - assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY); - - lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source); - lldp_rx->timer_event_source = sd_event_source_disable_unref(lldp_rx->timer_event_source); - lldp_rx->event = sd_event_unref(lldp_rx->event); - return 0; -} - -sd_event* sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx) { - assert_return(lldp_rx, NULL); - - return lldp_rx->event; -} - -int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata) { - assert_return(lldp_rx, -EINVAL); - - lldp_rx->callback = cb; - lldp_rx->userdata = userdata; - - return 0; -} - -int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex) { - assert_return(lldp_rx, -EINVAL); - assert_return(ifindex > 0, -EINVAL); - assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY); - - lldp_rx->ifindex = ifindex; - return 0; -} - -int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname) { - assert_return(lldp_rx, -EINVAL); - assert_return(ifname, -EINVAL); - - if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE)) - return -EINVAL; - - return free_and_strdup(&lldp_rx->ifname, ifname); -} - -int sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx, const char **ret) { - int r; - - assert_return(lldp_rx, -EINVAL); - - r = get_ifname(lldp_rx->ifindex, &lldp_rx->ifname); - if (r < 0) - return r; - - if (ret) - *ret = lldp_rx->ifname; - - return 0; -} - -static sd_lldp_rx *lldp_rx_free(sd_lldp_rx *lldp_rx) { - if (!lldp_rx) - return NULL; - - lldp_rx_reset(lldp_rx); - - sd_lldp_rx_detach_event(lldp_rx); - - lldp_rx_flush_neighbors(lldp_rx); - - hashmap_free(lldp_rx->neighbor_by_id); - prioq_free(lldp_rx->neighbor_by_expiry); - free(lldp_rx->ifname); - return mfree(lldp_rx); -} - -DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_rx, sd_lldp_rx, lldp_rx_free); - -int sd_lldp_rx_new(sd_lldp_rx **ret) { - _cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL; - - assert_return(ret, -EINVAL); - - lldp_rx = new(sd_lldp_rx, 1); - if (!lldp_rx) - return -ENOMEM; - - *lldp_rx = (sd_lldp_rx) { - .n_ref = 1, - .fd = -1, - .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX, - .capability_mask = UINT16_MAX, - }; - - *ret = TAKE_PTR(lldp_rx); - return 0; -} - -static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) { - sd_lldp_rx *lldp_rx = userdata; - int r; - - r = lldp_rx_make_space(lldp_rx, 0); - if (r < 0) { - log_lldp_rx_errno(lldp_rx, r, "Failed to make space, ignoring: %m"); - return 0; - } - - r = lldp_rx_start_timer(lldp_rx, NULL); - if (r < 0) { - log_lldp_rx_errno(lldp_rx, r, "Failed to restart timer, ignoring: %m"); - return 0; - } - - return 0; -} - -static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor) { - sd_lldp_neighbor *n; - - assert(lldp_rx); - assert(lldp_rx->event); - - if (neighbor) - lldp_neighbor_start_ttl(neighbor); - - n = prioq_peek(lldp_rx->neighbor_by_expiry); - if (!n) - return event_source_disable(lldp_rx->timer_event_source); - - return event_reset_time(lldp_rx->event, &lldp_rx->timer_event_source, - CLOCK_BOOTTIME, - n->until, 0, - on_timer_event, lldp_rx, - lldp_rx->event_priority, "lldp-rx-timer", true); -} - -static inline int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) { - assert(a); - assert(b); - assert(*a); - assert(*b); - - return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id); -} - -int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***ret) { - _cleanup_free_ sd_lldp_neighbor **l = NULL; - sd_lldp_neighbor *n; - int k = 0; - - assert_return(lldp_rx, -EINVAL); - assert_return(ret, -EINVAL); - - if (hashmap_isempty(lldp_rx->neighbor_by_id)) { /* Special shortcut */ - *ret = NULL; - return 0; - } - - l = new0(sd_lldp_neighbor*, hashmap_size(lldp_rx->neighbor_by_id)); - if (!l) - return -ENOMEM; - - HASHMAP_FOREACH(n, lldp_rx->neighbor_by_id) - l[k++] = sd_lldp_neighbor_ref(n); - - assert((size_t) k == hashmap_size(lldp_rx->neighbor_by_id)); - - /* Return things in a stable order */ - typesafe_qsort(l, k, neighbor_compare_func); - *ret = TAKE_PTR(l); - - return k; -} - -int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) { - assert_return(lldp_rx, -EINVAL); - assert_return(m > 0, -EINVAL); - - lldp_rx->neighbors_max = m; - lldp_rx_make_space(lldp_rx, 0); - - return 0; -} - -int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) { - assert_return(lldp_rx, -EINVAL); - assert_return(mask != 0, -EINVAL); - - lldp_rx->capability_mask = mask; - - return 0; -} - -int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *addr) { - assert_return(lldp_rx, -EINVAL); - - /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so - * that our own can be filtered out here. */ - - if (addr) - lldp_rx->filter_address = *addr; - else - zero(lldp_rx->filter_address); - - return 0; -} diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h b/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h index 74cbc269..6092652d 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h @@ -99,6 +99,7 @@ struct sd_event_source { sd_event_signal_handler_t callback; struct signalfd_siginfo siginfo; int sig; + bool unblock; } signal; struct { sd_event_child_handler_t callback; diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c index 8b2c554c..70776077 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c @@ -155,6 +155,8 @@ struct sd_event { LIST_HEAD(sd_event_source, sources); + sd_event_source *sigint_event_source, *sigterm_event_source; + usec_t last_run_usec, last_log_usec; unsigned delays[sizeof(usec_t) * 8]; }; @@ -325,6 +327,9 @@ static sd_event *event_free(sd_event *e) { assert(e); + e->sigterm_event_source = sd_event_source_unref(e->sigterm_event_source); + e->sigint_event_source = sd_event_source_unref(e->sigint_event_source); + while ((s = e->sources)) { assert(s->floating); source_disconnect(s); @@ -815,6 +820,7 @@ static void event_source_time_prioq_remove( static void source_disconnect(sd_event_source *s) { sd_event *event; + int r; assert(s); @@ -855,6 +861,20 @@ static void source_disconnect(sd_event_source *s) { s->event->signal_sources[s->signal.sig] = NULL; event_gc_signal_data(s->event, &s->priority, s->signal.sig); + + if (s->signal.unblock) { + sigset_t new_ss; + + if (sigemptyset(&new_ss) < 0) + log_debug_errno(errno, "Failed to reset signal set, ignoring: %m"); + else if (sigaddset(&new_ss, s->signal.sig) < 0) + log_debug_errno(errno, "Failed to add signal %i to signal mask, ignoring: %m", s->signal.sig); + else { + r = pthread_sigmask(SIG_UNBLOCK, &new_ss, NULL); + if (r != 0) + log_debug_errno(r, "Failed to unblock signal %i, ignoring: %m", s->signal.sig); + } + } } break; @@ -1331,23 +1351,38 @@ _public_ int sd_event_add_signal( _cleanup_(source_freep) sd_event_source *s = NULL; struct signal_data *d; + sigset_t new_ss; + bool block_it; int r; assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); - assert_return(SIGNAL_VALID(sig), -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); + /* Let's make sure our special flag stays outside of the valid signal range */ + assert_cc(_NSIG < SD_EVENT_SIGNAL_PROCMASK); + + if (sig & SD_EVENT_SIGNAL_PROCMASK) { + sig &= ~SD_EVENT_SIGNAL_PROCMASK; + assert_return(SIGNAL_VALID(sig), -EINVAL); + + block_it = true; + } else { + assert_return(SIGNAL_VALID(sig), -EINVAL); + + r = signal_is_blocked(sig); + if (r < 0) + return r; + if (r == 0) + return -EBUSY; + + block_it = false; + } + if (!callback) callback = signal_exit_callback; - r = signal_is_blocked(sig); - if (r < 0) - return r; - if (r == 0) - return -EBUSY; - if (!e->signal_sources) { e->signal_sources = new0(sd_event_source*, _NSIG); if (!e->signal_sources) @@ -1366,9 +1401,34 @@ _public_ int sd_event_add_signal( e->signal_sources[sig] = s; + if (block_it) { + sigset_t old_ss; + + if (sigemptyset(&new_ss) < 0) + return -errno; + + if (sigaddset(&new_ss, sig) < 0) + return -errno; + + r = pthread_sigmask(SIG_BLOCK, &new_ss, &old_ss); + if (r != 0) + return -r; + + r = sigismember(&old_ss, sig); + if (r < 0) + return -errno; + + s->signal.unblock = !r; + } else + s->signal.unblock = false; + r = event_make_signal_data(e, sig, &d); - if (r < 0) + if (r < 0) { + if (s->signal.unblock) + (void) pthread_sigmask(SIG_UNBLOCK, &new_ss, NULL); + return r; + } /* Use the signal name as description for the event source by default */ (void) sd_event_source_set_description(s, signal_to_string(sig)); @@ -3827,7 +3887,7 @@ static void event_close_inode_data_fds(sd_event *e) { /* Close the fds pointing to the inodes to watch now. We need to close them as they might otherwise pin * filesystems. But we can't close them right-away as we need them as long as the user still wants to make - * adjustments to the even source, such as changing the priority (which requires us to remove and re-add a watch + * adjustments to the event source, such as changing the priority (which requires us to remove and re-add a watch * for the inode). Hence, let's close them when entering the first iteration after they were added, as a * compromise. */ @@ -4538,8 +4598,8 @@ _public_ int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, s _public_ int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval, unsigned *ret_burst) { assert_return(s, -EINVAL); - /* Querying whether an event source has ratelimiting configured is not a loggable offsense, hence - * don't use assert_return(). Unlike turning on ratelimiting it's not really a programming error */ + /* Querying whether an event source has ratelimiting configured is not a loggable offense, hence + * don't use assert_return(). Unlike turning on ratelimiting it's not really a programming error. */ if (!EVENT_SOURCE_CAN_RATE_LIMIT(s->type)) return -EDOM; @@ -4565,4 +4625,56 @@ _public_ int sd_event_source_is_ratelimited(sd_event_source *s) { return s->ratelimited; } + +_public_ int sd_event_set_signal_exit(sd_event *e, int b) { + bool change = false; + int r; + + assert_return(e, -EINVAL); + + if (b) { + /* We want to maintain pointers to these event sources, so that we can destroy them when told + * so. But we also don't want them to pin the event loop itself. Hence we mark them as + * floating after creation (and undo this before deleting them again). */ + + if (!e->sigint_event_source) { + r = sd_event_add_signal(e, &e->sigint_event_source, SIGINT | SD_EVENT_SIGNAL_PROCMASK, NULL, NULL); + if (r < 0) + return r; + + assert(sd_event_source_set_floating(e->sigint_event_source, true) >= 0); + change = true; + } + + if (!e->sigterm_event_source) { + r = sd_event_add_signal(e, &e->sigterm_event_source, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, NULL, NULL); + if (r < 0) { + if (change) { + assert(sd_event_source_set_floating(e->sigint_event_source, false) >= 0); + e->sigint_event_source = sd_event_source_unref(e->sigint_event_source); + } + + return r; + } + + assert(sd_event_source_set_floating(e->sigterm_event_source, true) >= 0); + change = true; + } + + } else { + if (e->sigint_event_source) { + assert(sd_event_source_set_floating(e->sigint_event_source, false) >= 0); + e->sigint_event_source = sd_event_source_unref(e->sigint_event_source); + change = true; + } + + if (e->sigterm_event_source) { + assert(sd_event_source_set_floating(e->sigterm_event_source, false) >= 0); + e->sigterm_event_source = sd_event_source_unref(e->sigterm_event_source); + change = true; + } + } + + return change; +} #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-core/src/systemd/sd-event.h b/src/libnm-systemd-core/src/systemd/sd-event.h index e782339c..cae4c867 100644 --- a/src/libnm-systemd-core/src/systemd/sd-event.h +++ b/src/libnm-systemd-core/src/systemd/sd-event.h @@ -68,6 +68,8 @@ enum { SD_EVENT_PRIORITY_IDLE = 100 }; +#define SD_EVENT_SIGNAL_PROCMASK (1 << 30) + typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata); typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata); typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata); @@ -114,6 +116,7 @@ int sd_event_get_exit_code(sd_event *e, int *code); int sd_event_set_watchdog(sd_event *e, int b); int sd_event_get_watchdog(sd_event *e); int sd_event_get_iteration(sd_event *e, uint64_t *ret); +int sd_event_set_signal_exit(sd_event *e, int b); sd_event_source* sd_event_source_ref(sd_event_source *s); sd_event_source* sd_event_source_unref(sd_event_source *s); diff --git a/src/libnm-systemd-core/src/systemd/sd-lldp-rx.h b/src/libnm-systemd-core/src/systemd/sd-lldp-rx.h deleted file mode 100644 index 504d7f59..00000000 --- a/src/libnm-systemd-core/src/systemd/sd-lldp-rx.h +++ /dev/null @@ -1,109 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#ifndef foosdlldprxhfoo -#define foosdlldprxhfoo - -/*** - systemd 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. - - systemd 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 systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-event.h" -#include "sd-lldp.h" - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -typedef struct sd_lldp_rx sd_lldp_rx; -typedef struct sd_lldp_neighbor sd_lldp_neighbor; - -__extension__ typedef enum sd_lldp_rx_event_t { - SD_LLDP_RX_EVENT_ADDED, - SD_LLDP_RX_EVENT_REMOVED, - SD_LLDP_RX_EVENT_UPDATED, - SD_LLDP_RX_EVENT_REFRESHED, - _SD_LLDP_RX_EVENT_MAX, - _SD_LLDP_RX_EVENT_INVALID = -EINVAL, - _SD_ENUM_FORCE_S64(LLDP_RX_EVENT) -} sd_lldp_rx_event_t; - -typedef void (*sd_lldp_rx_callback_t)(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n, void *userdata); - -int sd_lldp_rx_new(sd_lldp_rx **ret); -sd_lldp_rx *sd_lldp_rx_ref(sd_lldp_rx *lldp_rx); -sd_lldp_rx *sd_lldp_rx_unref(sd_lldp_rx *lldp_rx); - -int sd_lldp_rx_start(sd_lldp_rx *lldp_rx); -int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx); -int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx); - -int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority); -int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx); -sd_event *sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx); - -int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata); -int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex); -int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname); -int sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx, const char **ret); - -/* Controls how much and what to store in the neighbors database */ -int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t n); -int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask); -int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *address); - -int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***neighbors); - -int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size); -sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n); -sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n); - -/* Access to LLDP frame metadata */ -int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address); -int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address); -int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret); -int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); - -/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */ -int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); -int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); -int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec); -int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret); -int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret); - -/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs - * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */ -int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n); -int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n); -int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type); -int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type); -int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype); -int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype); -int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); - -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_rx, sd_lldp_rx_unref); -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref); - -_SD_END_DECLARATIONS; - -#endif diff --git a/src/libnm-systemd-core/src/systemd/sd-lldp.h b/src/libnm-systemd-core/src/systemd/sd-lldp.h deleted file mode 100644 index 4069c5b2..00000000 --- a/src/libnm-systemd-core/src/systemd/sd-lldp.h +++ /dev/null @@ -1,123 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#ifndef foosdlldphfoo -#define foosdlldphfoo - -/*** - systemd 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. - - systemd 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 systemd; If not, see . -***/ - -#include - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -/* IEEE 802.1AB-2009 Clause 8: TLV Types */ -enum { - SD_LLDP_TYPE_END = 0, - SD_LLDP_TYPE_CHASSIS_ID = 1, - SD_LLDP_TYPE_PORT_ID = 2, - SD_LLDP_TYPE_TTL = 3, - SD_LLDP_TYPE_PORT_DESCRIPTION = 4, - SD_LLDP_TYPE_SYSTEM_NAME = 5, - SD_LLDP_TYPE_SYSTEM_DESCRIPTION = 6, - SD_LLDP_TYPE_SYSTEM_CAPABILITIES = 7, - SD_LLDP_TYPE_MGMT_ADDRESS = 8, - SD_LLDP_TYPE_PRIVATE = 127 -}; - -/* IEEE 802.1AB-2009 Clause 8.5.2: Chassis subtypes */ -enum { - SD_LLDP_CHASSIS_SUBTYPE_RESERVED = 0, - SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1, - SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS = 2, - SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT = 3, - SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS = 4, - SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS = 5, - SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME = 6, - SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7 -}; - -/* IEEE 802.1AB-2009 Clause 8.5.3: Port subtype */ -enum { - SD_LLDP_PORT_SUBTYPE_RESERVED = 0, - SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1, - SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2, - SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3, - SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4, - SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5, - SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6, - SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7 -}; - -/* IEEE 802.1AB-2009 Clause 8.5.8: System capabilities */ -enum { - SD_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0, - SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1, - SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2, - SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3, - SD_LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4, - SD_LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5, - SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6, - SD_LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7, - SD_LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8, - SD_LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9, - SD_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10 -}; - -#define SD_LLDP_SYSTEM_CAPABILITIES_ALL UINT16_MAX - -#define SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \ - ((uint16_t) \ - (SD_LLDP_SYSTEM_CAPABILITIES_REPEATER | \ - SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE | \ - SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP | \ - SD_LLDP_SYSTEM_CAPABILITIES_ROUTER | \ - SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS | \ - SD_LLDP_SYSTEM_CAPABILITIES_CVLAN | \ - SD_LLDP_SYSTEM_CAPABILITIES_SVLAN | \ - SD_LLDP_SYSTEM_CAPABILITIES_TPMR)) - -#define SD_LLDP_OUI_802_1 (const uint8_t[]) { 0x00, 0x80, 0xc2 } -#define SD_LLDP_OUI_802_3 (const uint8_t[]) { 0x00, 0x12, 0x0f } - -#define _SD_LLDP_OUI_IANA 0x00, 0x00, 0x5E -#define SD_LLDP_OUI_IANA (const uint8_t[]) { _SD_LLDP_OUI_IANA } - -#define SD_LLDP_OUI_IANA_SUBTYPE_MUD 0x01 -#define SD_LLDP_OUI_IANA_MUD \ - (const uint8_t[]) { _SD_LLDP_OUI_IANA, SD_LLDP_OUI_IANA_SUBTYPE_MUD } - -/* IEEE 802.1AB-2009 Annex E */ -enum { - SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1, - SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2, - SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3, - SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4, - SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5, - SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6, - SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7 -}; - -/* IEEE 802.1AB-2009 Annex F */ -enum { - SD_LLDP_OUI_802_3_SUBTYPE_MAC_PHY_CONFIG_STATUS = 1, - SD_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI = 2, - SD_LLDP_OUI_802_3_SUBTYPE_LINK_AGGREGATION = 3, - SD_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE = 4 -}; - -_SD_END_DECLARATIONS; - -#endif diff --git a/src/libnm-systemd-shared/sd-adapt-shared/user-util.h b/src/libnm-systemd-shared/sd-adapt-shared/user-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/libnm-systemd-shared/sd-adapt-shared/user-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h index 4c413a8d..df6d5b7b 100644 --- a/src/libnm-systemd-shared/src/basic/cgroup-util.h +++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h @@ -86,6 +86,7 @@ bool cpu_accounting_is_cheap(void); /* Special values for all weight knobs on unified hierarchy */ #define CGROUP_WEIGHT_INVALID UINT64_MAX +#define CGROUP_WEIGHT_IDLE UINT64_C(0) #define CGROUP_WEIGHT_MIN UINT64_C(1) #define CGROUP_WEIGHT_MAX UINT64_C(10000) #define CGROUP_WEIGHT_DEFAULT UINT64_C(100) diff --git a/src/libnm-systemd-shared/src/basic/env-file.c b/src/libnm-systemd-shared/src/basic/env-file.c index 61a9cd60..266e4e79 100644 --- a/src/libnm-systemd-shared/src/basic/env-file.c +++ b/src/libnm-systemd-shared/src/basic/env-file.c @@ -446,11 +446,9 @@ static int merge_env_file_push( const char *key, char *value, void *userdata) { - char ***env = userdata; + char ***env = ASSERT_PTR(userdata); char *expanded_value; - assert(env); - if (!value) { log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key); return 0; diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c index e869fad1..f58d86a7 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.c +++ b/src/libnm-systemd-shared/src/basic/env-util.c @@ -781,6 +781,18 @@ int getenv_bool_secure(const char *p) { } #if 0 /* NM_IGNORED */ +int getenv_uint64_secure(const char *p, uint64_t *ret) { + const char *e; + + assert(p); + + e = secure_getenv(p); + if (!e) + return -ENXIO; + + return safe_atou64(e, ret); +} + int set_unset_env(const char *name, const char *value, bool overwrite) { assert(name); diff --git a/src/libnm-systemd-shared/src/basic/env-util.h b/src/libnm-systemd-shared/src/basic/env-util.h index 2bf0603f..b927ac7a 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.h +++ b/src/libnm-systemd-shared/src/basic/env-util.h @@ -57,6 +57,8 @@ char *strv_env_pairs_get(char **l, const char *name) _pure_; int getenv_bool(const char *p); int getenv_bool_secure(const char *p); +int getenv_uint64_secure(const char *p, uint64_t *ret); + /* Like setenv, but calls unsetenv if value == NULL. */ int set_unset_env(const char *name, const char *value, bool overwrite); diff --git a/src/libnm-systemd-shared/src/basic/errno-util.h b/src/libnm-systemd-shared/src/basic/errno-util.h index 648de50e..091f99c5 100644 --- a/src/libnm-systemd-shared/src/basic/errno-util.h +++ b/src/libnm-systemd-shared/src/basic/errno-util.h @@ -6,6 +6,21 @@ #include "macro.h" +/* strerror(3) says that glibc uses a maximum length of 1024 bytes. */ +#define ERRNO_BUF_LEN 1024 + +/* Note: the lifetime of the compound literal is the immediately surrounding block, + * see C11 §6.5.2.5, and + * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks + * + * Note that we use the GNU variant of strerror_r() here. */ +#define STRERROR(errnum) strerror_r(abs(errnum), (char[ERRNO_BUF_LEN]){}, ERRNO_BUF_LEN) + +/* A helper to print an error message or message for functions that return 0 on EOF. + * Note that we can't use ({ … }) to define a temporary variable, so errnum is + * evaluated twice. */ +#define STRERROR_OR_EOF(errnum) ((errnum) != 0 ? STRERROR(errnum) : "Unexpected EOF") + static inline void _reset_errno_(int *saved_errno) { if (*saved_errno < 0) /* Invalidated by UNPROTECT_ERRNO? */ return; @@ -22,6 +37,10 @@ static inline void _reset_errno_(int *saved_errno) { _saved_errno_ = -1; \ } while (false) +#define LOCAL_ERRNO(value) \ + PROTECT_ERRNO; \ + errno = abs(value) + static inline int negative_errno(void) { /* This helper should be used to shut up gcc if you know 'errno' is * negative. Instead of "return -errno;", use "return negative_errno();" @@ -54,11 +73,6 @@ static inline int RET_NERRNO(int ret) { return ret; } -static inline const char *strerror_safe(int error) { - /* 'safe' here does NOT mean thread safety. */ - return strerror(abs(error)); /* lgtm [cpp/potentially-dangerous-function] */ -} - static inline int errno_or_else(int fallback) { /* To be used when invoking library calls where errno handling is not defined clearly: we return * errno if it is set, and the specified error otherwise. The idea is that the caller initializes @@ -153,3 +167,10 @@ static inline bool ERRNO_IS_DEVICE_ABSENT(int r) { ENXIO, ENOENT); } + +/* Quite often we want to handle cases where the backing FS doesn't support extended attributes at all and + * where it simply doesn't have the requested xattr the same way */ +static inline bool ERRNO_IS_XATTR_ABSENT(int r) { + return abs(r) == ENODATA || + ERRNO_IS_NOT_SUPPORTED(r); +} diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c index 80295445..61a5bb78 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.c +++ b/src/libnm-systemd-shared/src/basic/fd-util.c @@ -4,7 +4,6 @@ #include #include -#include #if WANT_LINUX_FS_H #include #endif @@ -480,7 +479,7 @@ void cmsg_close_all(struct msghdr *mh) { CMSG_FOREACH(cmsg, mh) if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) - close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int)); + close_many((int*) ((void*) CMSG_DATA(cmsg)), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int)); } bool fdname_is_valid(const char *s) { @@ -661,7 +660,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_ goto finish; } - CLOSE_AND_REPLACE(null_fd, copy); + close_and_replace(null_fd, copy); } } @@ -786,20 +785,6 @@ int read_nr_open(void) { return 1024 * 1024; } -/* This is here because it's fd-related and is called from sd-journal code. Other btrfs-related utilities are - * in src/shared, but libsystemd must not link to libsystemd-shared, see docs/ARCHITECTURE.md. */ -int btrfs_defrag_fd(int fd) { - int r; - - assert(fd >= 0); - - r = fd_verify_regular(fd); - if (r < 0) - return r; - - return RET_NERRNO(ioctl(fd, BTRFS_IOC_DEFRAG, NULL)); -} - int fd_get_diskseq(int fd, uint64_t *ret) { uint64_t diskseq; diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h index 808cac4d..d9896e27 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.h +++ b/src/libnm-systemd-shared/src/basic/fd-util.h @@ -98,7 +98,7 @@ static inline int make_null_stdio(void) { }) /* Like free_and_replace(), but for file descriptors */ -#define CLOSE_AND_REPLACE(a, b) \ +#define close_and_replace(a, b) \ ({ \ int *_fdp_ = &(a); \ safe_close(*_fdp_); \ @@ -108,7 +108,6 @@ static inline int make_null_stdio(void) { int fd_reopen(int fd, int flags); int read_nr_open(void); -int btrfs_defrag_fd(int fd); int fd_get_diskseq(int fd, uint64_t *ret); /* The maximum length a buffer for a /proc/self/fd/ path needs */ diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c index 1a951bd5..23f8d403 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.c +++ b/src/libnm-systemd-shared/src/basic/fs-util.c @@ -403,10 +403,6 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi return ret; } -int touch(const char *path) { - return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); -} - int symlink_idempotent(const char *from, const char *to, bool make_relative) { _cleanup_free_ char *relpath = NULL; int r; @@ -415,13 +411,7 @@ int symlink_idempotent(const char *from, const char *to, bool make_relative) { assert(to); if (make_relative) { - _cleanup_free_ char *parent = NULL; - - r = path_extract_directory(to, &parent); - if (r < 0) - return r; - - r = path_make_relative(parent, from, &relpath); + r = path_make_relative_parent(to, from, &relpath); if (r < 0) return r; @@ -447,50 +437,38 @@ int symlink_idempotent(const char *from, const char *to, bool make_relative) { return 0; } -int symlink_atomic(const char *from, const char *to) { - _cleanup_free_ char *t = NULL; +int symlinkat_atomic_full(const char *from, int atfd, const char *to, bool make_relative) { + _cleanup_free_ char *relpath = NULL, *t = NULL; int r; assert(from); assert(to); - r = tempfn_random(to, NULL, &t); - if (r < 0) - return r; - - if (symlink(from, t) < 0) - return -errno; + if (make_relative) { + r = path_make_relative_parent(to, from, &relpath); + if (r < 0) + return r; - if (rename(t, to) < 0) { - unlink_noerrno(t); - return -errno; + from = relpath; } - return 0; -} - -int mknod_atomic(const char *path, mode_t mode, dev_t dev) { - _cleanup_free_ char *t = NULL; - int r; - - assert(path); - - r = tempfn_random(path, NULL, &t); + r = tempfn_random(to, NULL, &t); if (r < 0) return r; - if (mknod(t, mode, dev) < 0) + if (symlinkat(from, atfd, t) < 0) return -errno; - if (rename(t, path) < 0) { - unlink_noerrno(t); - return -errno; + r = RET_NERRNO(renameat(atfd, t, atfd, to)); + if (r < 0) { + (void) unlinkat(atfd, t, 0); + return r; } return 0; } -int mkfifo_atomic(const char *path, mode_t mode) { +int mknodat_atomic(int atfd, const char *path, mode_t mode, dev_t dev) { _cleanup_free_ char *t = NULL; int r; @@ -500,37 +478,36 @@ int mkfifo_atomic(const char *path, mode_t mode) { if (r < 0) return r; - if (mkfifo(t, mode) < 0) + if (mknodat(atfd, t, mode, dev) < 0) return -errno; - if (rename(t, path) < 0) { - unlink_noerrno(t); - return -errno; + r = RET_NERRNO(renameat(atfd, t, atfd, path)); + if (r < 0) { + (void) unlinkat(atfd, t, 0); + return r; } return 0; } -int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) { +int mkfifoat_atomic(int atfd, const char *path, mode_t mode) { _cleanup_free_ char *t = NULL; int r; assert(path); - if (path_is_absolute(path)) - return mkfifo_atomic(path, mode); - /* We're only interested in the (random) filename. */ - r = tempfn_random_child("", NULL, &t); + r = tempfn_random(path, NULL, &t); if (r < 0) return r; - if (mkfifoat(dirfd, t, mode) < 0) + if (mkfifoat(atfd, t, mode) < 0) return -errno; - if (renameat(dirfd, t, dirfd, path) < 0) { - unlink_noerrno(t); - return -errno; + r = RET_NERRNO(renameat(atfd, t, atfd, path)); + if (r < 0) { + (void) unlinkat(atfd, t, 0); + return r; } return 0; diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h index e48cf680..c4dffc48 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.h +++ b/src/libnm-systemd-shared/src/basic/fs-util.h @@ -13,6 +13,7 @@ #include "alloc-util.h" #include "errno-util.h" #include "time-util.h" +#include "user-util.h" #define MODE_INVALID ((mode_t) -1) @@ -50,14 +51,27 @@ int stat_warn_permissions(const char *path, const struct stat *st); RET_NERRNO(faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW)) int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode); -int touch(const char *path); + +static inline int touch(const char *path) { + return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); +} int symlink_idempotent(const char *from, const char *to, bool make_relative); -int symlink_atomic(const char *from, const char *to); -int mknod_atomic(const char *path, mode_t mode, dev_t dev); -int mkfifo_atomic(const char *path, mode_t mode); +int symlinkat_atomic_full(const char *from, int atfd, const char *to, bool make_relative); +static inline int symlink_atomic(const char *from, const char *to) { + return symlinkat_atomic_full(from, AT_FDCWD, to, false); +} + +int mknodat_atomic(int atfd, const char *path, mode_t mode, dev_t dev); +static inline int mknod_atomic(const char *path, mode_t mode, dev_t dev) { + return mknodat_atomic(AT_FDCWD, path, mode, dev); +} + int mkfifoat_atomic(int dir_fd, const char *path, mode_t mode); +static inline int mkfifo_atomic(const char *path, mode_t mode) { + return mkfifoat_atomic(AT_FDCWD, path, mode); +} int get_files_in_directory(const char *path, char ***list); diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.c b/src/libnm-systemd-shared/src/basic/glyph-util.c index ec851e8b..5289cffe 100644 --- a/src/libnm-systemd-shared/src/basic/glyph-util.c +++ b/src/libnm-systemd-shared/src/basic/glyph-util.c @@ -55,6 +55,7 @@ const char *special_glyph(SpecialGlyph code) { [SPECIAL_GLYPH_LIGHT_SHADE] = "-", [SPECIAL_GLYPH_DARK_SHADE] = "X", [SPECIAL_GLYPH_SIGMA] = "S", + [SPECIAL_GLYPH_ARROW_LEFT] = "<-", [SPECIAL_GLYPH_ARROW_RIGHT] = "->", [SPECIAL_GLYPH_ARROW_UP] = "^", [SPECIAL_GLYPH_ARROW_DOWN] = "v", @@ -101,6 +102,7 @@ const char *special_glyph(SpecialGlyph code) { [SPECIAL_GLYPH_ARROW_DOWN] = u8"↓", /* actually called: DOWNWARDS ARROW */ /* Single glyph in Unicode, two in ASCII */ + [SPECIAL_GLYPH_ARROW_LEFT] = u8"←", /* actually called: LEFTWARDS ARROW */ [SPECIAL_GLYPH_ARROW_RIGHT] = u8"→", /* actually called: RIGHTWARDS ARROW */ /* Single glyph in Unicode, three in ASCII */ diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.h b/src/libnm-systemd-shared/src/basic/glyph-util.h index 065dde8a..621d7a85 100644 --- a/src/libnm-systemd-shared/src/basic/glyph-util.h +++ b/src/libnm-systemd-shared/src/basic/glyph-util.h @@ -22,6 +22,7 @@ typedef enum SpecialGlyph { SPECIAL_GLYPH_MU, SPECIAL_GLYPH_CHECK_MARK, SPECIAL_GLYPH_CROSS_MARK, + SPECIAL_GLYPH_ARROW_LEFT, SPECIAL_GLYPH_ARROW_RIGHT, SPECIAL_GLYPH_ARROW_UP, SPECIAL_GLYPH_ARROW_DOWN, diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c index 5d0ae6aa..d0b950e0 100644 --- a/src/libnm-systemd-shared/src/basic/hashmap.c +++ b/src/libnm-systemd-shared/src/basic/hashmap.c @@ -374,7 +374,7 @@ static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { } static struct hashmap_base_entry* bucket_at(HashmapBase *h, unsigned idx) { - return (struct hashmap_base_entry*) + return (struct hashmap_base_entry*) (void *) ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); } @@ -1192,7 +1192,7 @@ static int resize_buckets(HashmapBase *h, unsigned entries_add) { } while (rehash_next); } - assert(n_rehashed == n_entries(h)); + assert_se(n_rehashed == n_entries(h)); return 1; } @@ -1886,11 +1886,10 @@ int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l HA } int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags) { - const char *p = v; + const char *p = ASSERT_PTR(v); int r; assert(s); - assert(v); for (;;) { char *word; @@ -2083,6 +2082,8 @@ static bool set_fnmatch_one(Set *patterns, const char *needle) { assert(needle); + /* Any failure of fnmatch() is treated as equivalent to FNM_NOMATCH, i.e. as non-matching pattern */ + SET_FOREACH(p, patterns) if (fnmatch(p, needle, 0) == 0) return true; diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.c b/src/libnm-systemd-shared/src/basic/in-addr-util.c index dc4446ae..b25c803b 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.c +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c @@ -68,7 +68,7 @@ bool in4_addr_is_link_local_dynamic(const struct in_addr *a) { bool in6_addr_is_link_local(const struct in6_addr *a) { assert(a); - return IN6_IS_ADDR_LINKLOCAL(a); /* lgtm [cpp/potentially-dangerous-function] */ + return IN6_IS_ADDR_LINKLOCAL(a); } int in_addr_is_link_local(int family, const union in_addr_union *u) { @@ -133,7 +133,7 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) { return in4_addr_is_localhost(&u->in); if (family == AF_INET6) - return IN6_IS_ADDR_LOOPBACK(&u->in6); /* lgtm [cpp/potentially-dangerous-function] */ + return IN6_IS_ADDR_LOOPBACK(&u->in6); return -EAFNOSUPPORT; } @@ -146,7 +146,7 @@ int in_addr_is_localhost_one(int family, const union in_addr_union *u) { return be32toh(u->in.s_addr) == UINT32_C(0x7F000001); if (family == AF_INET6) - return IN6_IS_ADDR_LOOPBACK(&u->in6); /* lgtm [cpp/potentially-dangerous-function] */ + return IN6_IS_ADDR_LOOPBACK(&u->in6); return -EAFNOSUPPORT; } @@ -197,8 +197,7 @@ int in_addr_prefix_intersect( assert(a); assert(b); - /* Checks whether there are any addresses that are in both - * networks */ + /* Checks whether there are any addresses that are in both networks */ m = MIN(aprefixlen, bprefixlen); @@ -206,7 +205,7 @@ int in_addr_prefix_intersect( uint32_t x, nm; x = be32toh(a->in.s_addr ^ b->in.s_addr); - nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m); + nm = m == 0 ? 0 : 0xFFFFFFFFUL << (32 - m); return (x & nm) == 0; } @@ -594,6 +593,7 @@ unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) { return 32U - u32ctz(be32toh(addr->s_addr)); } +/* Calculate an IPv4 netmask from prefix length, for example /8 -> 255.0.0.0. */ struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) { assert(addr); assert(prefixlen <= 32); @@ -607,6 +607,47 @@ struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned cha return addr; } +/* Calculate an IPv6 netmask from prefix length, for example /16 -> ffff::. */ +struct in6_addr* in6_addr_prefixlen_to_netmask(struct in6_addr *addr, unsigned char prefixlen) { + assert(addr); + assert(prefixlen <= 128); + + for (unsigned i = 0; i < 16; i++) { + uint8_t mask; + + if (prefixlen >= 8) { + mask = 0xFF; + prefixlen -= 8; + } else if (prefixlen > 0) { + mask = 0xFF << (8 - prefixlen); + prefixlen = 0; + } else { + assert(prefixlen == 0); + mask = 0; + } + + addr->s6_addr[i] = mask; + } + + return addr; +} + +/* Calculate an IPv4 or IPv6 netmask from prefix length, for example /8 -> 255.0.0.0 or /16 -> ffff::. */ +int in_addr_prefixlen_to_netmask(int family, union in_addr_union *addr, unsigned char prefixlen) { + assert(addr); + + switch (family) { + case AF_INET: + in4_addr_prefixlen_to_netmask(&addr->in, prefixlen); + return 0; + case AF_INET6: + in6_addr_prefixlen_to_netmask(&addr->in6, prefixlen); + return 0; + default: + return -EAFNOSUPPORT; + } +} + int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { uint8_t msb_octet = *(uint8_t*) addr; diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.h b/src/libnm-systemd-shared/src/basic/in-addr-util.h index fbc60436..19fa35f1 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.h +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h @@ -138,6 +138,8 @@ int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr); struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); +struct in6_addr* in6_addr_prefixlen_to_netmask(struct in6_addr *addr, unsigned char prefixlen); +int in_addr_prefixlen_to_netmask(int family, union in_addr_union *addr, unsigned char prefixlen); int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen); diff --git a/src/libnm-systemd-shared/src/basic/io-util.c b/src/libnm-systemd-shared/src/basic/io-util.c index 8a2b9c2f..fc65a5e7 100644 --- a/src/libnm-systemd-shared/src/basic/io-util.c +++ b/src/libnm-systemd-shared/src/basic/io-util.c @@ -53,11 +53,10 @@ int flush_fd(int fd) { #endif /* NM_IGNORED */ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { - uint8_t *p = buf; + uint8_t *p = ASSERT_PTR(buf); ssize_t n = 0; assert(fd >= 0); - assert(buf); /* If called with nbytes == 0, let's call read() at least * once, to validate the operation */ @@ -113,10 +112,9 @@ int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) { #if 0 /* NM_IGNORED */ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { - const uint8_t *p = buf; + const uint8_t *p = ASSERT_PTR(buf); assert(fd >= 0); - assert(buf); if (_unlikely_(nbytes > (size_t) SSIZE_MAX)) return -EINVAL; diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h index 1bc84e46..a6adacf6 100644 --- a/src/libnm-systemd-shared/src/basic/log.h +++ b/src/libnm-systemd-shared/src/basic/log.h @@ -8,6 +8,7 @@ #include #include "macro.h" +#include "ratelimit.h" /* Some structures we reference but don't want to pull in headers for */ struct iovec; @@ -508,3 +509,41 @@ int log_syntax_invalid_utf8_internal( #define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG) void log_setup(void); + +typedef struct LogRateLimit { + int error; + int level; + RateLimit ratelimit; +} LogRateLimit; + +#define log_ratelimit_internal(_level, _error, _format, _file, _line, _func, ...) \ +({ \ + int _log_ratelimit_error = (_error); \ + int _log_ratelimit_level = (_level); \ + static LogRateLimit _log_ratelimit = { \ + .ratelimit = { \ + .interval = 1 * USEC_PER_SEC, \ + .burst = 1, \ + }, \ + }; \ + unsigned _num_dropped_errors = ratelimit_num_dropped(&_log_ratelimit.ratelimit); \ + if (_log_ratelimit_error != _log_ratelimit.error || _log_ratelimit_level != _log_ratelimit.level) { \ + ratelimit_reset(&_log_ratelimit.ratelimit); \ + _log_ratelimit.error = _log_ratelimit_error; \ + _log_ratelimit.level = _log_ratelimit_level; \ + } \ + if (ratelimit_below(&_log_ratelimit.ratelimit)) \ + _log_ratelimit_error = _num_dropped_errors > 0 \ + ? log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format " (Dropped %u similar message(s))", __VA_ARGS__, _num_dropped_errors) \ + : log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format, __VA_ARGS__); \ + _log_ratelimit_error; \ +}) + +#define log_ratelimit_full_errno(level, error, format, ...) \ + ({ \ + int _level = (level), _e = (error); \ + _e = (log_get_max_level() >= LOG_PRI(_level)) \ + ? log_ratelimit_internal(_level, _e, format, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \ + : -ERRNO_VALUE(_e); \ + _e < 0 ? _e : -ESTRPIPE; \ + }) diff --git a/src/libnm-systemd-shared/src/basic/missing_stat.h b/src/libnm-systemd-shared/src/basic/missing_stat.h index 9c1df698..8b39d4f4 100644 --- a/src/libnm-systemd-shared/src/basic/missing_stat.h +++ b/src/libnm-systemd-shared/src/basic/missing_stat.h @@ -49,6 +49,8 @@ struct statx STATX_DEFINITION; /* Always define the newest version we are aware of as a distinct type, so that we can use it even if glibc * defines an older definition */ struct new_statx STATX_DEFINITION; +#else /* NM_IGNORED */ +struct new_statx; #endif /* NM_IGNORED */ /* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c index d5dcaae8..968f01c6 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.c +++ b/src/libnm-systemd-shared/src/basic/parse-util.c @@ -482,69 +482,31 @@ int safe_atolli(const char *s, long long int *ret_lli) { return 0; } -int safe_atou8(const char *s, uint8_t *ret) { - unsigned base = 0; - unsigned long l; - char *x = NULL; - - assert(s); - - s += strspn(s, WHITESPACE); - s = mangle_base(s, &base); +int safe_atou8_full(const char *s, unsigned base, uint8_t *ret) { + unsigned u; + int r; - errno = 0; - l = strtoul(s, &x, base); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (l != 0 && s[0] == '-') - return -ERANGE; - if ((unsigned long) (uint8_t) l != l) + r = safe_atou_full(s, base, &u); + if (r < 0) + return r; + if (u > UINT8_MAX) return -ERANGE; - if (ret) - *ret = (uint8_t) l; + *ret = (uint8_t) u; return 0; } int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { - char *x = NULL; - unsigned long l; - - assert(s); - assert(SAFE_ATO_MASK_FLAGS(base) <= 16); - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && - strchr(WHITESPACE, s[0])) - return -EINVAL; - - s += strspn(s, WHITESPACE); - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && - IN_SET(s[0], '+', '-')) - return -EINVAL; - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && - s[0] == '0' && s[1] != 0) - return -EINVAL; - - s = mangle_base(s, &base); + unsigned u; + int r; - errno = 0; - l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base)); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (l != 0 && s[0] == '-') - return -ERANGE; - if ((unsigned long) (uint16_t) l != l) + r = safe_atou_full(s, base, &u); + if (r < 0) + return r; + if (u > UINT16_MAX) return -ERANGE; - if (ret) - *ret = (uint16_t) l; - + *ret = (uint16_t) u; return 0; } diff --git a/src/libnm-systemd-shared/src/basic/parse-util.h b/src/libnm-systemd-shared/src/basic/parse-util.h index f2222dcf..8d8d5232 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.h +++ b/src/libnm-systemd-shared/src/basic/parse-util.h @@ -36,7 +36,11 @@ static inline int safe_atou(const char *s, unsigned *ret_u) { int safe_atoi(const char *s, int *ret_i); int safe_atolli(const char *s, long long int *ret_i); -int safe_atou8(const char *s, uint8_t *ret); +int safe_atou8_full(const char *s, unsigned base, uint8_t *ret); + +static inline int safe_atou8(const char *s, uint8_t *ret) { + return safe_atou8_full(s, 0, ret); +} int safe_atou16_full(const char *s, unsigned base, uint16_t *ret); diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c index 3ba3510f..b45bb0b5 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.c +++ b/src/libnm-systemd-shared/src/basic/path-util.c @@ -3,22 +3,18 @@ #include "nm-sd-adapt-shared.h" #include +#include #include #include #include #include -/* When we include libgen.h because we need dirname() we immediately - * undefine basename() since libgen.h defines it as a macro to the - * POSIX version which is really broken. We prefer GNU basename(). */ -#include -#undef basename - #include "alloc-util.h" #include "chase-symlinks.h" #include "extract-word.h" #include "fd-util.h" #include "fs-util.h" +#include "glob-util.h" #include "log.h" #include "macro.h" #include "path-util.h" @@ -200,6 +196,34 @@ int path_make_relative(const char *from, const char *to, char **ret) { return 0; } +int path_make_relative_parent(const char *from_child, const char *to, char **ret) { + _cleanup_free_ char *from = NULL; + int r; + + assert(from_child); + assert(to); + assert(ret); + + /* Similar to path_make_relative(), but provides the relative path from the parent directory of + * 'from_child'. This may be useful when creating relative symlink. + * + * E.g. + * - from = "/path/to/aaa", to = "/path/to/bbb" + * path_make_relative(from, to) = "../bbb" + * path_make_relative_parent(from, to) = "bbb" + * + * - from = "/path/to/aaa/bbb", to = "/path/to/ccc/ddd" + * path_make_relative(from, to) = "../../ccc/ddd" + * path_make_relative_parent(from, to) = "../ccc/ddd" + */ + + r = path_extract_directory(from_child, &from); + if (r < 0) + return r; + + return path_make_relative(from, to, ret); +} + char* path_startswith_strv(const char *p, char **set) { STRV_FOREACH(s, set) { char *t; @@ -328,11 +352,9 @@ char **path_strv_resolve_uniq(char **l, const char *root) { char *path_simplify(char *path) { bool add_slash = false; - char *f = path; + char *f = ASSERT_PTR(path); int r; - assert(path); - /* Removes redundant inner and trailing slashes. Also removes unnecessary dots. * Modifies the passed string in-place. * @@ -765,38 +787,26 @@ static int executable_is_good(const char *executable) { "/dev/null"); } -int fsck_exists(const char *fstype) { +int fsck_exists(void) { + return executable_is_good("fsck"); +} + +int fsck_exists_for_fstype(const char *fstype) { const char *checker; + int r; assert(fstype); if (streq(fstype, "auto")) return -EINVAL; + r = fsck_exists(); + if (r <= 0) + return r; + checker = strjoina("fsck.", fstype); return executable_is_good(checker); } - -char* dirname_malloc(const char *path) { - char *d, *dir, *dir2; - - assert(path); - - d = strdup(path); - if (!d) - return NULL; - - dir = dirname(d); - assert(dir); - - if (dir == d) - return d; - - dir2 = strdup(dir); - free(d); - - return dir2; -} #endif /* NM_IGNORED */ static const char *skip_slash_or_dot(const char *p) { @@ -1319,4 +1329,67 @@ bool prefixed_path_strv_contains(char **l, const char *path) { return false; } + +int path_glob_can_match(const char *pattern, const char *prefix, char **ret) { + assert(pattern); + assert(prefix); + + for (const char *a = pattern, *b = prefix;;) { + _cleanup_free_ char *g = NULL, *h = NULL; + const char *p, *q; + int r, s; + + r = path_find_first_component(&a, /* accept_dot_dot = */ false, &p); + if (r < 0) + return r; + + s = path_find_first_component(&b, /* accept_dot_dot = */ false, &q); + if (s < 0) + return s; + + if (s == 0) { + /* The pattern matches the prefix. */ + if (ret) { + char *t; + + t = path_join(prefix, p); + if (!t) + return -ENOMEM; + + *ret = t; + } + return true; + } + + if (r == 0) + break; + + if (r == s && strneq(p, q, r)) + continue; /* common component. Check next. */ + + g = strndup(p, r); + if (!g) + return -ENOMEM; + + if (!string_is_glob(g)) + break; + + /* We found a glob component. Check if the glob pattern matches the prefix component. */ + + h = strndup(q, s); + if (!h) + return -ENOMEM; + + r = fnmatch(g, h, 0); + if (r == FNM_NOMATCH) + break; + if (r != 0) /* Failure to process pattern? */ + return -EINVAL; + } + + /* The pattern does not match the prefix. */ + if (ret) + *ret = NULL; + return false; +} #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h index c97c4540..949fdc86 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.h +++ b/src/libnm-systemd-shared/src/basic/path-util.h @@ -63,6 +63,7 @@ char* path_make_absolute(const char *p, const char *prefix); int safe_getcwd(char **ret); int path_make_absolute_cwd(const char *p, char **ret); int path_make_relative(const char *from, const char *to, char **ret); +int path_make_relative_parent(const char *from_child, const char *to, char **ret); char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_; static inline char* path_startswith(const char *path, const char *prefix) { return path_startswith_full(path, prefix, true); @@ -104,7 +105,8 @@ static inline int find_executable(const char *name, char **ret_filename) { bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update); -int fsck_exists(const char *fstype); +int fsck_exists(void); +int fsck_exists_for_fstype(const char *fstype); /* Iterates through the path prefixes of the specified path, going up * the tree, to root. Also returns "" (and not "/"!) for the root @@ -153,7 +155,6 @@ int fsck_exists(const char *fstype); _ret; \ }) -char* dirname_malloc(const char *path); int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret); int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret); const char *last_path_component(const char *path); @@ -198,3 +199,5 @@ static inline const char *empty_to_root(const char *path) { bool path_strv_contains(char **l, const char *path); bool prefixed_path_strv_contains(char **l, const char *path); + +int path_glob_can_match(const char *pattern, const char *prefix, char **ret); diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c index 1c23a2f8..428323ef 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.c +++ b/src/libnm-systemd-shared/src/basic/process-util.c @@ -821,7 +821,7 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) { if (status.si_pid == pid) { /* This is the correct child. */ if (status.si_code == CLD_EXITED) - return (status.si_status == 0) ? 0 : -EPROTO; + return status.si_status == 0 ? 0 : -EPROTO; else return -EPROTO; } diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c index d576bff1..9e1a746f 100644 --- a/src/libnm-systemd-shared/src/basic/random-util.c +++ b/src/libnm-systemd-shared/src/basic/random-util.c @@ -2,10 +2,6 @@ #include "nm-sd-adapt-shared.h" -#if defined(__i386__) || defined(__x86_64__) -#include -#endif - #include #include #include @@ -35,11 +31,10 @@ #include "sha256.h" #include "time-util.h" -/* This is a "best effort" kind of thing, but has no real security value. - * So, this should only be used by random_bytes(), which is not meant for - * crypto. This could be made better, but we're *not* trying to roll a - * userspace prng here, or even have forward secrecy, but rather just do - * the shortest thing that is at least better than libc rand(). */ +/* This is a "best effort" kind of thing, but has no real security value. So, this should only be used by + * random_bytes(), which is not meant for crypto. This could be made better, but we're *not* trying to roll a + * userspace prng here, or even have forward secrecy, but rather just do the shortest thing that is at least + * better than libc rand(). */ static void fallback_random_bytes(void *p, size_t n) { static thread_local uint64_t fallback_counter = 0; struct { @@ -55,7 +50,7 @@ static void fallback_random_bytes(void *p, size_t n) { .stamp_mono = now(CLOCK_MONOTONIC), .stamp_real = now(CLOCK_REALTIME), .pid = getpid(), - .tid = gettid() + .tid = gettid(), }; #if HAVE_SYS_AUXV_H diff --git a/src/libnm-systemd-shared/src/basic/ratelimit.c b/src/libnm-systemd-shared/src/basic/ratelimit.c index fba05875..3ccec119 100644 --- a/src/libnm-systemd-shared/src/basic/ratelimit.c +++ b/src/libnm-systemd-shared/src/basic/ratelimit.c @@ -32,9 +32,16 @@ bool ratelimit_below(RateLimit *r) { if (r->num < r->burst) goto good; + r->num++; return false; good: r->num++; return true; } + +unsigned ratelimit_num_dropped(RateLimit *r) { + assert(r); + + return r->num > r->burst ? r->num - r->burst : 0; +} diff --git a/src/libnm-systemd-shared/src/basic/ratelimit.h b/src/libnm-systemd-shared/src/basic/ratelimit.h index ee1d17c0..22361898 100644 --- a/src/libnm-systemd-shared/src/basic/ratelimit.h +++ b/src/libnm-systemd-shared/src/basic/ratelimit.h @@ -4,7 +4,6 @@ #include #include "time-util.h" -#include "util.h" typedef struct RateLimit { usec_t interval; /* Keep those two fields first so they can be initialized easily: */ @@ -22,3 +21,5 @@ static inline bool ratelimit_configured(RateLimit *rl) { } bool ratelimit_below(RateLimit *r); + +unsigned ratelimit_num_dropped(RateLimit *r); diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c index 8c5858ff..5a142263 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.c +++ b/src/libnm-systemd-shared/src/basic/stat-util.c @@ -373,6 +373,32 @@ bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */ } +bool statx_inode_same(const struct statx *a, const struct statx *b) { + + /* Same as stat_inode_same() but for struct statx */ + + return a && b && + FLAGS_SET(a->stx_mask, STATX_TYPE|STATX_INO) && FLAGS_SET(b->stx_mask, STATX_TYPE|STATX_INO) && + (a->stx_mode & S_IFMT) != 0 && + ((a->stx_mode ^ b->stx_mode) & S_IFMT) == 0 && + a->stx_dev_major == b->stx_dev_major && + a->stx_dev_minor == b->stx_dev_minor && + a->stx_ino == b->stx_ino; +} + +bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) { + if (!a || !b) + return false; + + /* if we have the mount ID, that's all we need */ + if (FLAGS_SET(a->stx_mask, STATX_MNT_ID) && FLAGS_SET(b->stx_mask, STATX_MNT_ID)) + return a->stx_mnt_id == b->stx_mnt_id; + + /* Otherwise, major/minor of backing device must match */ + return a->stx_dev_major == b->stx_dev_major && + a->stx_dev_minor == b->stx_dev_minor; +} + int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) { static bool avoid_statx = false; struct stat st; diff --git a/src/libnm-systemd-shared/src/basic/stat-util.h b/src/libnm-systemd-shared/src/basic/stat-util.h index 56f15534..f9519d8c 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.h +++ b/src/libnm-systemd-shared/src/basic/stat-util.h @@ -73,6 +73,9 @@ int proc_mounted(void); bool stat_inode_same(const struct stat *a, const struct stat *b); bool stat_inode_unmodified(const struct stat *a, const struct stat *b); +bool statx_inode_same(const struct statx *a, const struct statx *b); +bool statx_mount_same(const struct new_statx *a, const struct new_statx *b); + int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx); #if HAS_FEATURE_MEMORY_SANITIZER diff --git a/src/libnm-systemd-shared/src/basic/stdio-util.h b/src/libnm-systemd-shared/src/basic/stdio-util.h index f3b213c5..07cbfe9d 100644 --- a/src/libnm-systemd-shared/src/basic/stdio-util.h +++ b/src/libnm-systemd-shared/src/basic/stdio-util.h @@ -11,16 +11,16 @@ #include "macro.h" #include "memory-util.h" -#define snprintf_ok(buf, len, fmt, ...) \ - ({ \ - char *_buf = (buf); \ - size_t _len = (len); \ - int _snpf = snprintf(_buf, _len, (fmt), __VA_ARGS__); \ - _snpf >= 0 && (size_t) _snpf < _len ? _buf : NULL; \ +#define snprintf_ok(buf, len, fmt, ...) \ + ({ \ + char *_buf = (buf); \ + size_t _len = (len); \ + int _snpf = snprintf(_buf, _len, (fmt), ##__VA_ARGS__); \ + _snpf >= 0 && (size_t) _snpf < _len ? _buf : NULL; \ }) #define xsprintf(buf, fmt, ...) \ - assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough") + assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, ##__VA_ARGS__), "xsprintf: " #buf "[] must be big enough") #define VA_FORMAT_ADVANCE(format, ap) \ do { \ diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c index 7cd4b055..9b2ee879 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.c +++ b/src/libnm-systemd-shared/src/basic/string-util.c @@ -516,7 +516,6 @@ char *cellescape(char *buf, size_t len, const char *s) { buf[i] = '\0'; return buf; } -#endif /* NM_IGNORED */ char* strshorten(char *s, size_t l) { assert(s); @@ -527,6 +526,20 @@ char* strshorten(char *s, size_t l) { return s; } +int strgrowpad0(char **s, size_t l) { + assert(s); + + char *q = realloc(*s, l); + if (!q) + return -ENOMEM; + *s = q; + + size_t sz = strlen(*s); + memzero(*s + sz, l - sz); + return 0; +} +#endif /* NM_IGNORED */ + char *strreplace(const char *text, const char *old_string, const char *new_string) { size_t l, old_len, new_len; char *t, *ret = NULL; diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h index 1dd46f7f..46681ced 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.h +++ b/src/libnm-systemd-shared/src/basic/string-util.h @@ -152,6 +152,8 @@ char *cellescape(char *buf, size_t len, const char *s); char* strshorten(char *s, size_t l); +int strgrowpad0(char **s, size_t l); + char *strreplace(const char *text, const char *old_string, const char *new_string); char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]); @@ -169,9 +171,12 @@ int split_pair(const char *s, const char *sep, char **l, char **r); int free_and_strdup(char **p, const char *s); static inline int free_and_strdup_warn(char **p, const char *s) { - if (free_and_strdup(p, s) < 0) + int r; + + r = free_and_strdup(p, s); + if (r < 0) return log_oom(); - return 0; + return r; } int free_and_strndup(char **p, const char *s, size_t l); diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c index d9c58633..48ca8575 100644 --- a/src/libnm-systemd-shared/src/basic/strv.c +++ b/src/libnm-systemd-shared/src/basic/strv.c @@ -840,13 +840,26 @@ char** strv_shell_escape(char **l, const char *bad) { } #endif /* NM_IGNORED */ -bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos) { - for (size_t i = 0; patterns && patterns[i]; i++) - if (fnmatch(patterns[i], s, flags) == 0) { - if (matched_pos) - *matched_pos = i; - return true; - } +bool strv_fnmatch_full( + char* const* patterns, + const char *s, + int flags, + size_t *ret_matched_pos) { + + assert(s); + + if (patterns) + for (size_t i = 0; patterns[i]; i++) + /* NB: We treat all fnmatch() errors as equivalent to FNM_NOMATCH, i.e. if fnmatch() fails to + * process the pattern for some reason we'll consider this equivalent to non-matching. */ + if (fnmatch(patterns[i], s, flags) == 0) { + if (ret_matched_pos) + *ret_matched_pos = i; + return true; + } + + if (ret_matched_pos) + *ret_matched_pos = SIZE_MAX; return false; } diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h index 87ec6337..d6f5ac6b 100644 --- a/src/libnm-systemd-shared/src/basic/strv.h +++ b/src/libnm-systemd-shared/src/basic/strv.h @@ -240,7 +240,7 @@ void strv_print(char * const *l); char** strv_reverse(char **l); char** strv_shell_escape(char **l, const char *bad); -bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos); +bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *ret_matched_pos); static inline bool strv_fnmatch(char* const* patterns, const char *s) { return strv_fnmatch_full(patterns, s, 0, NULL); } diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c index 0c8f4929..cf556a73 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.c +++ b/src/libnm-systemd-shared/src/basic/time-util.c @@ -517,10 +517,9 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { { "us", 1 }, }; - char *p = buf; + char *p = ASSERT_PTR(buf); bool something = false; - assert(buf); assert(l > 0); if (t == USEC_INFINITY) { diff --git a/src/libnm-systemd-shared/src/basic/time-util.h b/src/libnm-systemd-shared/src/basic/time-util.h index bf312442..c98f95a5 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.h +++ b/src/libnm-systemd-shared/src/basic/time-util.h @@ -189,10 +189,15 @@ static inline usec_t usec_sub_unsigned(usec_t timestamp, usec_t delta) { } static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) { + if (delta == INT64_MIN) { /* prevent overflow */ + assert_cc(-(INT64_MIN + 1) == INT64_MAX); + assert_cc(USEC_INFINITY > INT64_MAX); + return usec_add(timestamp, (usec_t) INT64_MAX + 1); + } if (delta < 0) return usec_add(timestamp, (usec_t) (-delta)); - else - return usec_sub_unsigned(timestamp, (usec_t) delta); + + return usec_sub_unsigned(timestamp, (usec_t) delta); } #if SIZEOF_TIME_T == 8 diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.c b/src/libnm-systemd-shared/src/basic/tmpfile-util.c index 57447a42..2a27f86b 100644 --- a/src/libnm-systemd-shared/src/basic/tmpfile-util.c +++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.c @@ -90,51 +90,92 @@ int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) { } #endif /* NM_IGNORED */ -int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { - _cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL; +static int tempfn_build(const char *p, const char *pre, const char *post, bool child, char **ret) { + _cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL, *result = NULL; + size_t len_pre, len_post, len_add; int r; + assert(p); assert(ret); /* * Turns this: * /foo/bar/waldo * - * Into this: - * /foo/bar/.#waldoXXXXXX + * Into this : + * /foo/bar/waldo/.#
 (child == true)
+         *         /foo/bar/.#
waldo (child == false)
          */
 
-        r = path_extract_directory(p, &d);
-        if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */
-                return r;
+        if (pre && strchr(pre, '/'))
+                return -EINVAL;
 
-        r = path_extract_filename(p, &fn);
-        if (r < 0)
-                return r;
+        if (post && strchr(post, '/'))
+                return -EINVAL;
+
+        len_pre = strlen_ptr(pre);
+        len_post = strlen_ptr(post);
+        /* NAME_MAX is counted *without* the trailing NUL byte. */
+        if (len_pre > NAME_MAX - STRLEN(".#") ||
+            len_post > NAME_MAX - STRLEN(".#") - len_pre)
+                return -EINVAL;
+
+        len_add = len_pre + len_post + STRLEN(".#");
+
+        if (child) {
+                d = strdup(p);
+                if (!d)
+                        return -ENOMEM;
+        } else {
+                r = path_extract_directory(p, &d);
+                if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */
+                        return r;
 
-        nf = strjoin(".#", strempty(extra), fn, "XXXXXX");
+                r = path_extract_filename(p, &fn);
+                if (r < 0)
+                        return r;
+
+                if (strlen(fn) > NAME_MAX - len_add)
+                        /* We cannot simply prepend and append strings to the filename. Let's truncate the filename. */
+                        fn[NAME_MAX - len_add] = '\0';
+        }
+
+        nf = strjoin(".#", strempty(pre), strempty(fn), strempty(post));
         if (!nf)
                 return -ENOMEM;
 
-        if (!filename_is_valid(nf)) /* New name is not valid? (Maybe because too long?) Refuse. */
-                return -EINVAL;
-
-        if (d)  {
+        if (d) {
                 if (!path_extend(&d, nf))
                         return -ENOMEM;
 
-                *ret = path_simplify(TAKE_PTR(d));
+                result = path_simplify(TAKE_PTR(d));
         } else
-                *ret = TAKE_PTR(nf);
+                result = TAKE_PTR(nf);
+
+        if (!path_is_valid(result)) /* New path is not valid? (Maybe because too long?) Refuse. */
+                return -EINVAL;
 
+        *ret = TAKE_PTR(result);
         return 0;
 }
 
+int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
+        /*
+         * Turns this:
+         *         /foo/bar/waldo
+         *
+         * Into this:
+         *         /foo/bar/.#waldoXXXXXX
+         */
+
+        return tempfn_build(p, extra, "XXXXXX", /* child = */ false, ret);
+}
+
 #if 0 /* NM_IGNORED */
 int tempfn_random(const char *p, const char *extra, char **ret) {
-        _cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL;
-        int r;
+        _cleanup_free_ char *s = NULL;
 
+        assert(p);
         assert(ret);
 
         /*
@@ -145,37 +186,14 @@ int tempfn_random(const char *p, const char *extra, char **ret) {
          *         /foo/bar/.#waldobaa2a261115984a9
          */
 
-        r = path_extract_directory(p, &d);
-        if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */
-                return r;
-
-        r = path_extract_filename(p, &fn);
-        if (r < 0)
-                return r;
-
-        if (asprintf(&nf, ".#%s%s%016" PRIx64,
-                     strempty(extra),
-                     fn,
-                     random_u64()) < 0)
+        if (asprintf(&s, "%016" PRIx64, random_u64()) < 0)
                 return -ENOMEM;
 
-        if (!filename_is_valid(nf)) /* Not valid? (maybe because too long now?) — refuse early */
-                return -EINVAL;
-
-        if (d) {
-                if (!path_extend(&d, nf))
-                        return -ENOMEM;
-
-                *ret = path_simplify(TAKE_PTR(d));
-        } else
-                *ret = TAKE_PTR(nf);
-
-        return 0;
+        return tempfn_build(p, extra, s, /* child = */ false, ret);
 }
 
 int tempfn_random_child(const char *p, const char *extra, char **ret) {
-        char *t, *x;
-        uint64_t u;
+        _cleanup_free_ char *s = NULL;
         int r;
 
         assert(ret);
@@ -192,27 +210,10 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
                         return r;
         }
 
-        extra = strempty(extra);
-
-        t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
-        if (!t)
+        if (asprintf(&s, "%016" PRIx64, random_u64()) < 0)
                 return -ENOMEM;
 
-        if (isempty(p))
-                x = stpcpy(stpcpy(t, ".#"), extra);
-        else
-                x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
-
-        u = random_u64();
-        for (unsigned i = 0; i < 16; i++) {
-                *(x++) = hexchar(u & 0xF);
-                u >>= 4;
-        }
-
-        *x = 0;
-
-        *ret = path_simplify(t);
-        return 0;
+        return tempfn_build(p, extra, s, /* child = */ true, ret);
 }
 
 int open_tmpfile_unlinkable(const char *directory, int flags) {
diff --git a/src/libnm-systemd-shared/src/basic/user-util.h b/src/libnm-systemd-shared/src/basic/user-util.h
new file mode 100644
index 00000000..a08683bc
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/user-util.h
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include 
+#if ENABLE_GSHADOW
+#  include 
+#endif
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/* Users managed by systemd-homed. See https://systemd.io/UIDS-GIDS for details how this range fits into the rest of the world */
+#define HOME_UID_MIN ((uid_t) 60001)
+#define HOME_UID_MAX ((uid_t) 60513)
+
+/* Users mapped from host into a container */
+#define MAP_UID_MIN ((uid_t) 60514)
+#define MAP_UID_MAX ((uid_t) 60577)
+
+bool uid_is_valid(uid_t uid);
+
+static inline bool gid_is_valid(gid_t gid) {
+        return uid_is_valid((uid_t) gid);
+}
+
+int parse_uid(const char *s, uid_t* ret_uid);
+int parse_uid_range(const char *s, uid_t *ret_lower, uid_t *ret_upper);
+
+static inline int parse_gid(const char *s, gid_t *ret_gid) {
+        return parse_uid(s, (uid_t*) ret_gid);
+}
+
+char* getlogname_malloc(void);
+char* getusername_malloc(void);
+
+typedef enum UserCredsFlags {
+        USER_CREDS_PREFER_NSS    = 1 << 0,  /* if set, only synthesize user records if database lacks them. Normally we bypass the userdb entirely for the records we can synthesize */
+        USER_CREDS_ALLOW_MISSING = 1 << 1,  /* if a numeric UID string is resolved, be OK if there's no record for it */
+        USER_CREDS_CLEAN         = 1 << 2,  /* try to clean up shell and home fields with invalid data */
+} UserCredsFlags;
+
+int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell, UserCredsFlags flags);
+int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags);
+
+char* uid_to_name(uid_t uid);
+char* gid_to_name(gid_t gid);
+
+int in_gid(gid_t gid);
+int in_group(const char *name);
+
+int merge_gid_lists(const gid_t *list1, size_t size1, const gid_t *list2, size_t size2, gid_t **result);
+int getgroups_alloc(gid_t** gids);
+
+int get_home_dir(char **ret);
+int get_shell(char **ret);
+
+int reset_uid_gid(void);
+
+int take_etc_passwd_lock(const char *root);
+
+#define UID_INVALID ((uid_t) -1)
+#define GID_INVALID ((gid_t) -1)
+
+#define UID_NOBODY ((uid_t) 65534U)
+#define GID_NOBODY ((gid_t) 65534U)
+
+/* If REMOUNT_IDMAPPING_HOST_ROOT is set for remount_idmap() we'll include a mapping here that maps the host
+ * root user accessing the idmapped mount to the this user ID on the backing fs. This is the last valid UID in
+ * the *signed* 32bit range. You might wonder why precisely use this specific UID for this purpose? Well, we
+ * definitely cannot use the first 0…65536 UIDs for that, since in most cases that's precisely the file range
+ * we intend to map to some high UID range, and since UID mappings have to be bijective we thus cannot use
+ * them at all. Furthermore the UID range beyond INT32_MAX (i.e. the range above the signed 32bit range) is
+ * icky, since many APIs cannot use it (example: setfsuid() returns the old UID as signed integer). Following
+ * our usual logic of assigning a 16bit UID range to each container, so that the upper 16bit of a 32bit UID
+ * value indicate kind of a "container ID" and the lower 16bit map directly to the intended user you can read
+ * this specific UID as the "nobody" user of the container with ID 0x7FFF, which is kinda nice. */
+#define UID_MAPPED_ROOT ((uid_t) (INT32_MAX-1))
+#define GID_MAPPED_ROOT ((gid_t) (INT32_MAX-1))
+
+#define ETC_PASSWD_LOCK_PATH "/etc/.pwd.lock"
+
+/* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer
+ * NULL is special */
+#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))
+#define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
+
+#define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1))
+#define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
+
+static inline bool userns_supported(void) {
+        return access("/proc/self/uid_map", F_OK) >= 0;
+}
+
+typedef enum ValidUserFlags {
+        VALID_USER_RELAX         = 1 << 0,
+        VALID_USER_WARN          = 1 << 1,
+        VALID_USER_ALLOW_NUMERIC = 1 << 2,
+} ValidUserFlags;
+
+bool valid_user_group_name(const char *u, ValidUserFlags flags);
+bool valid_gecos(const char *d);
+char *mangle_gecos(const char *d);
+bool valid_home(const char *p);
+
+static inline bool valid_shell(const char *p) {
+        /* We have the same requirements, so just piggy-back on the home check.
+         *
+         * Let's ignore /etc/shells because this is only applicable to real and
+         * not system users. It is also incompatible with the idea of empty /etc.
+         */
+        return valid_home(p);
+}
+
+int maybe_setgroups(size_t size, const gid_t *list);
+
+bool synthesize_nobody(void);
+
+int fgetpwent_sane(FILE *stream, struct passwd **pw);
+int fgetspent_sane(FILE *stream, struct spwd **sp);
+int fgetgrent_sane(FILE *stream, struct group **gr);
+int putpwent_sane(const struct passwd *pw, FILE *stream);
+int putspent_sane(const struct spwd *sp, FILE *stream);
+int putgrent_sane(const struct group *gr, FILE *stream);
+#if ENABLE_GSHADOW
+int fgetsgent_sane(FILE *stream, struct sgrp **sg);
+int putsgent_sane(const struct sgrp *sg, FILE *stream);
+#endif
+
+bool is_nologin_shell(const char *shell);
+const char* default_root_shell(const char *root);
+
+int is_this_me(const char *username);
+
+const char *get_home_root(void);
+
+static inline bool hashed_password_is_locked_or_invalid(const char *password) {
+        return password && password[0] != '$';
+}
+
+/* A locked *and* invalid password for "struct spwd"'s .sp_pwdp and "struct passwd"'s .pw_passwd field */
+#define PASSWORD_LOCKED_AND_INVALID "!*"
+
+/* A password indicating "look in shadow file, please!" for "struct passwd"'s .pw_passwd */
+#define PASSWORD_SEE_SHADOW "x"
+
+/* A password indicating "hey, no password required for login" */
+#define PASSWORD_NONE ""
diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
index 0ed99403..0c69d98a 100644
--- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
@@ -81,18 +81,13 @@
 #endif
 
 /* This passes the argument through after (if asserts are enabled) checking that it is not null. */
-#define ASSERT_PTR(expr)                        \
-        ({                                      \
-                typeof(expr) _expr_ = (expr);   \
-                assert(_expr_);                 \
-                _expr_;                         \
-        })
-
-#define ASSERT_SE_PTR(expr)                     \
-        ({                                      \
-                typeof(expr) _expr_ = (expr);   \
-                assert_se(_expr_);              \
-                _expr_;                         \
+#define ASSERT_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert)
+#define ASSERT_SE_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert_se)
+#define _ASSERT_PTR(expr, var, check)      \
+        ({                                 \
+                typeof(expr) var = (expr); \
+                check(var);                \
+                var;                       \
         })
 
 #define ASSERT_NONNEG(expr)                              \
diff --git a/src/libnm-systemd-shared/src/fundamental/sha256.c b/src/libnm-systemd-shared/src/fundamental/sha256.c
index 0f3872ae..b4c36ab3 100644
--- a/src/libnm-systemd-shared/src/fundamental/sha256.c
+++ b/src/libnm-systemd-shared/src/fundamental/sha256.c
@@ -106,7 +106,7 @@ void sha256_init_ctx(struct sha256_ctx *ctx) {
 
 /* Process the remaining bytes in the internal buffer and the usual
    prolog according to the standard and write the result to RESBUF. */
-void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) {
+uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_DIGEST_SIZE]) {
         /* Take yet unprocessed bytes into account.  */
         uint32_t bytes = ctx->buflen;
         size_t pad;
@@ -131,9 +131,9 @@ void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) {
         /* Put result from CTX in first 32 bytes following RESBUF.  */
         for (size_t i = 0; i < 8; ++i)
                 if (UNALIGNED_P(resbuf))
-                        memcpy((uint8_t*) resbuf + i * sizeof(uint32_t), (uint32_t[]) { SWAP(ctx->H[i]) }, sizeof(uint32_t));
+                        memcpy(resbuf + i * sizeof(uint32_t), (uint32_t[]) { SWAP(ctx->H[i]) }, sizeof(uint32_t));
                 else
-                        ((uint32_t *) resbuf)[i] = SWAP(ctx->H[i]);
+                        ((uint32_t *) (void *) resbuf)[i] = SWAP(ctx->H[i]);
 
         return resbuf;
 }
@@ -199,10 +199,9 @@ void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx
 /* Process LEN bytes of BUFFER, accumulating context into CTX.
    It is assumed that LEN % 64 == 0.  */
 static void sha256_process_block(const void *buffer, size_t len, struct sha256_ctx *ctx) {
-        const uint32_t *words = buffer;
+        const uint32_t *words = ASSERT_PTR(buffer);
         size_t nwords = len / sizeof(uint32_t);
 
-        assert(buffer);
         assert(ctx);
 
         uint32_t a = ctx->H[0];
@@ -291,3 +290,10 @@ static void sha256_process_block(const void *buffer, size_t len, struct sha256_c
         ctx->H[6] = g;
         ctx->H[7] = h;
 }
+
+uint8_t* sha256_direct(const void *buffer, size_t sz, uint8_t result[static SHA256_DIGEST_SIZE]) {
+        struct sha256_ctx ctx;
+        sha256_init_ctx(&ctx);
+        sha256_process_bytes(buffer, sz, &ctx);
+        return sha256_finish_ctx(&ctx, result);
+}
diff --git a/src/libnm-systemd-shared/src/fundamental/sha256.h b/src/libnm-systemd-shared/src/fundamental/sha256.h
index f296f76a..31790c2e 100644
--- a/src/libnm-systemd-shared/src/fundamental/sha256.h
+++ b/src/libnm-systemd-shared/src/fundamental/sha256.h
@@ -25,5 +25,9 @@ struct sha256_ctx {
 };
 
 void sha256_init_ctx(struct sha256_ctx *ctx);
-void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf);
+uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_DIGEST_SIZE]);
 void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx);
+
+uint8_t* sha256_direct(const void *buffer, size_t sz, uint8_t result[static SHA256_DIGEST_SIZE]);
+
+#define SHA256_DIRECT(buffer, sz) sha256_direct(buffer, sz, (uint8_t[SHA256_DIGEST_SIZE]) {})
diff --git a/src/libnmc-base/nm-polkit-listener.h b/src/libnmc-base/nm-polkit-listener.h
index 8a4c6c38..15decbe1 100644
--- a/src/libnmc-base/nm-polkit-listener.h
+++ b/src/libnmc-base/nm-polkit-listener.h
@@ -17,7 +17,7 @@ typedef struct _NMPolkitListenerClass NMPolkitListenerClass;
 
 #define NM_TYPE_POLKIT_LISTENER (nm_polkit_listener_get_type())
 #define NM_POLKIT_LISTENER(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_POLKIT_LISTENER, NMPolkitListener))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_POLKIT_LISTENER, NMPolkitListener))
 #define NM_POLKIT_LISTENER_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_POLKIT_LISTENER, NMPolkitListenerClass))
 #define NM_IS_POLKIT_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_POLKIT_LISTENER))
diff --git a/src/libnmc-base/nm-secret-agent-simple.h b/src/libnmc-base/nm-secret-agent-simple.h
index 7193f7e5..a1d15881 100644
--- a/src/libnmc-base/nm-secret-agent-simple.h
+++ b/src/libnmc-base/nm-secret-agent-simple.h
@@ -33,7 +33,7 @@ typedef struct {
 
 #define NM_TYPE_SECRET_AGENT_SIMPLE (nm_secret_agent_simple_get_type())
 #define NM_SECRET_AGENT_SIMPLE(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimple))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimple))
 #define NM_SECRET_AGENT_SIMPLE_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimpleClass))
 #define NM_IS_SECRET_AGENT_SIMPLE(obj) \
diff --git a/src/libnmc-base/nm-vpn-helpers.c b/src/libnmc-base/nm-vpn-helpers.c
index 7ad5bee5..476fbe51 100644
--- a/src/libnmc-base/nm-vpn-helpers.c
+++ b/src/libnmc-base/nm-vpn-helpers.c
@@ -199,13 +199,13 @@ nm_vpn_openconnect_authenticate_helper(const char *host,
     const char *const   *iter;
     const char          *path;
     const char *const    DEFAULT_PATHS[] = {
-           "/sbin/",
-           "/usr/sbin/",
-           "/usr/local/sbin/",
-           "/bin/",
-           "/usr/bin/",
-           "/usr/local/bin/",
-           NULL,
+        "/sbin/",
+        "/usr/sbin/",
+        "/usr/local/sbin/",
+        "/bin/",
+        "/usr/bin/",
+        "/usr/local/bin/",
+        NULL,
     };
 
     path = nm_utils_file_search_in_paths("openconnect",
diff --git a/src/libnmc-setting/meson.build b/src/libnmc-setting/meson.build
index 49314cad..cf8a21fc 100644
--- a/src/libnmc-setting/meson.build
+++ b/src/libnmc-setting/meson.build
@@ -31,9 +31,8 @@ if enable_docs
     'check-settings-docs',
     find_program(join_paths(source_root, 'tools', 'check-compare-generated.sh')),
     args: [
-      source_root,
-      build_root,
-      'src/libnmc-setting/settings-docs.h',
+      source_root + '/src/libnmc-setting/settings-docs.h.in',
+      settings_docs_source,
     ],
   )
 else
diff --git a/src/libnmc-setting/nm-meta-setting-access.c b/src/libnmc-setting/nm-meta-setting-access.c
index 7f679ae5..14857048 100644
--- a/src/libnmc-setting/nm-meta-setting-access.c
+++ b/src/libnmc-setting/nm-meta-setting-access.c
@@ -416,7 +416,7 @@ _output_selection_pack(const NMMetaAbstractInfo *const *fields_array, GArray *ar
         if (str)
             memcpy(pdata, str->str, str->len);
         for (i = 0; i < len; i++) {
-            const OutputSelectionItem *a = &g_array_index(array, OutputSelectionItem, i);
+            const OutputSelectionItem *a = &nm_g_array_index(array, OutputSelectionItem, i);
             NMMetaSelectionItem       *p = (NMMetaSelectionItem *) &result->items[i];
 
             p->info = fields_array[a->idx];
diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.c b/src/libnmc-setting/nm-meta-setting-base-impl.c
index 69daa76c..19082671 100644
--- a/src/libnmc-setting/nm-meta-setting-base-impl.c
+++ b/src/libnmc-setting/nm-meta-setting-base-impl.c
@@ -3,6 +3,8 @@
  * Copyright (C) 2017 - 2018 Red Hat, Inc.
  */
 
+#define NM_WANT_NM_ARRAY_FIND_BSEARCH_INLINE
+
 #include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-meta-setting-base.h"
@@ -32,14 +34,16 @@
 #include "nm-setting-ip-tunnel.h"
 #include "nm-setting-ip4-config.h"
 #include "nm-setting-ip6-config.h"
+#include "nm-setting-loopback.h"
 #include "nm-setting-macsec.h"
 #include "nm-setting-macvlan.h"
 #include "nm-setting-match.h"
 #include "nm-setting-olpc-mesh.h"
 #include "nm-setting-ovs-bridge.h"
-#include "nm-setting-ovs-interface.h"
 #include "nm-setting-ovs-dpdk.h"
 #include "nm-setting-ovs-external-ids.h"
+#include "nm-setting-ovs-interface.h"
+#include "nm-setting-ovs-other-config.h"
 #include "nm-setting-ovs-patch.h"
 #include "nm-setting-ovs-port.h"
 #include "nm-setting-ppp.h"
@@ -358,6 +362,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
             .setting_name      = NM_SETTING_IP_TUNNEL_SETTING_NAME,
             .get_setting_gtype = nm_setting_ip_tunnel_get_type,
         },
+    [NM_META_SETTING_TYPE_LOOPBACK] =
+        {
+            .meta_type         = NM_META_SETTING_TYPE_LOOPBACK,
+            .setting_priority  = NM_SETTING_PRIORITY_HW_BASE,
+            .setting_name      = NM_SETTING_LOOPBACK_SETTING_NAME,
+            .get_setting_gtype = nm_setting_loopback_get_type,
+        },
     [NM_META_SETTING_TYPE_MACSEC] =
         {
             .meta_type         = NM_META_SETTING_TYPE_MACSEC,
@@ -400,6 +411,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
             .setting_name      = NM_SETTING_OVS_DPDK_SETTING_NAME,
             .get_setting_gtype = nm_setting_ovs_dpdk_get_type,
         },
+    [NM_META_SETTING_TYPE_OVS_OTHER_CONFIG] =
+        {
+            .meta_type         = NM_META_SETTING_TYPE_OVS_OTHER_CONFIG,
+            .setting_priority  = NM_SETTING_PRIORITY_AUX,
+            .setting_name      = NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME,
+            .get_setting_gtype = nm_setting_ovs_other_config_get_type,
+        },
     [NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS] =
         {
             .meta_type         = NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS,
@@ -608,6 +626,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = {
     NM_META_SETTING_TYPE_GSM,
     NM_META_SETTING_TYPE_INFINIBAND,
     NM_META_SETTING_TYPE_IP_TUNNEL,
+    NM_META_SETTING_TYPE_LOOPBACK,
     NM_META_SETTING_TYPE_MACSEC,
     NM_META_SETTING_TYPE_MACVLAN,
     NM_META_SETTING_TYPE_OVS_BRIDGE,
@@ -643,6 +662,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = {
     NM_META_SETTING_TYPE_ETHTOOL,
     NM_META_SETTING_TYPE_MATCH,
     NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS,
+    NM_META_SETTING_TYPE_OVS_OTHER_CONFIG,
     NM_META_SETTING_TYPE_PPP,
     NM_META_SETTING_TYPE_PPPOE,
     NM_META_SETTING_TYPE_TEAM_PORT,
@@ -693,29 +713,31 @@ nm_meta_setting_infos_by_name(const char *name)
     }
 
     G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMMetaSettingInfo, setting_name) == 0);
-    idx = nm_utils_array_find_binary_search(nm_meta_setting_infos,
-                                            sizeof(NMMetaSettingInfo),
-                                            _NM_META_SETTING_TYPE_NUM,
-                                            &name,
-                                            nm_strcmp_p_with_data,
-                                            NULL);
+    idx = nm_array_find_bsearch(nm_meta_setting_infos,
+                                _NM_META_SETTING_TYPE_NUM,
+                                sizeof(NMMetaSettingInfo),
+                                &name,
+                                nm_strcmp_p_with_data,
+                                NULL);
 
     return idx >= 0 ? &nm_meta_setting_infos[idx] : NULL;
 }
 
-const NMMetaSettingInfo *
-nm_meta_setting_infos_by_gtype(GType gtype)
-{
+/*****************************************************************************/
+
 #if _NM_META_SETTING_BASE_IMPL_LIBNM
+static const NMMetaSettingInfo *
+_infos_by_gtype_from_class(GType gtype)
+{
     nm_auto_unref_gtypeclass GTypeClass *gtypeclass_unref = NULL;
     GTypeClass                          *gtypeclass;
     NMSettingClass                      *klass;
 
     if (!g_type_is_a(gtype, NM_TYPE_SETTING))
-        goto out_none;
+        return NULL;
 
     gtypeclass = g_type_class_peek(gtype);
-    if (!gtypeclass)
+    if (G_UNLIKELY(!gtypeclass))
         gtypeclass = gtypeclass_unref = g_type_class_ref(gtype);
 
     nm_assert(NM_IS_SETTING_CLASS(gtypeclass));
@@ -723,38 +745,112 @@ nm_meta_setting_infos_by_gtype(GType gtype)
     klass = (NMSettingClass *) gtypeclass;
 
     if (!klass->setting_info)
-        goto out_none;
+        return NULL;
 
     nm_assert(klass->setting_info->get_setting_gtype);
     nm_assert(klass->setting_info->get_setting_gtype() == gtype);
-
     return klass->setting_info;
+}
+#endif
 
-out_none:
-
-    if (NM_MORE_ASSERTS > 10) {
-        int i;
-
-        /* this might hint to a bug, but it would be expected for NM_TYPE_SETTING
-         * and NM_TYPE_SETTING_IP_CONFIG.
-         *
-         * Assert that we didn't lookup for a gtype, which we would expect to find.
-         * An assertion failure here, hints to a bug in nm_setting_*_class_init().
-         */
-        for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++)
-            nm_assert(nm_meta_setting_infos[i].get_setting_gtype() != gtype);
-    }
-
-    return NULL;
-#else
-    guint i;
+static const NMMetaSettingInfo *
+_infos_by_gtype_search(GType gtype)
+{
+    int i;
 
-    for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) {
+    for (i = 0; i < (int) _NM_META_SETTING_TYPE_NUM; i++) {
         if (nm_meta_setting_infos[i].get_setting_gtype() == gtype)
             return &nm_meta_setting_infos[i];
     }
     return NULL;
+}
+
+typedef struct {
+    GType                    gtype;
+    const NMMetaSettingInfo *setting_info;
+} LookupData;
+
+_nm_always_inline static inline int
+_lookup_data_cmp(gconstpointer ptr_a, gconstpointer ptr_b, gpointer user_data)
+{
+    const GType *const a = ptr_a;
+    const GType *const b = ptr_b;
+
+    nm_assert(a);
+    nm_assert(b);
+    nm_assert(a != b);
+
+    NM_CMP_DIRECT(*a, *b);
+    return 0;
+}
+
+static const NMMetaSettingInfo *
+_infos_by_gtype_binary_search(GType gtype)
+{
+    static LookupData        static_array[_NM_META_SETTING_TYPE_NUM];
+    static const LookupData *static_ptr = NULL;
+    const LookupData        *ptr;
+    gssize                   idx;
+
+again:
+    ptr = g_atomic_pointer_get(&static_ptr);
+    if (G_UNLIKELY(!ptr)) {
+        static gsize g_lock = 0;
+        int          i;
+
+        if (!g_once_init_enter(&g_lock))
+            goto again;
+
+        for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) {
+            const NMMetaSettingInfo *m = &nm_meta_setting_infos[i];
+
+            static_array[i] = (LookupData){
+                .gtype        = m->get_setting_gtype(),
+                .setting_info = m,
+            };
+        }
+
+        g_qsort_with_data(static_array,
+                          _NM_META_SETTING_TYPE_NUM,
+                          sizeof(static_array[0]),
+                          _lookup_data_cmp,
+                          NULL);
+
+        ptr = static_array;
+        g_atomic_pointer_set(&static_ptr, ptr);
+
+        g_once_init_leave(&g_lock, 1);
+    }
+
+    idx = nm_array_find_bsearch_inline(ptr,
+                                       _NM_META_SETTING_TYPE_NUM,
+                                       sizeof(ptr[0]),
+                                       >ype,
+                                       _lookup_data_cmp,
+                                       NULL);
+    if (idx < 0)
+        return NULL;
+
+    return ptr[idx].setting_info;
+}
+
+const NMMetaSettingInfo *
+nm_meta_setting_infos_by_gtype(GType gtype)
+{
+    const NMMetaSettingInfo *setting_info;
+
+#if _NM_META_SETTING_BASE_IMPL_LIBNM
+    setting_info = _infos_by_gtype_from_class(gtype);
+#else
+    setting_info = _infos_by_gtype_binary_search(gtype);
 #endif
+
+    if (NM_MORE_ASSERTS > 20) {
+        nm_assert(setting_info == _infos_by_gtype_search(gtype));
+        nm_assert(setting_info == _infos_by_gtype_binary_search(gtype));
+    }
+
+    return setting_info;
 }
 
 /*****************************************************************************/
diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.h b/src/libnmc-setting/nm-meta-setting-base-impl.h
index 12688659..0c2def90 100644
--- a/src/libnmc-setting/nm-meta-setting-base-impl.h
+++ b/src/libnmc-setting/nm-meta-setting-base-impl.h
@@ -127,6 +127,7 @@ typedef enum _nm_packed {
     NM_META_SETTING_TYPE_IP_TUNNEL,
     NM_META_SETTING_TYPE_IP4_CONFIG,
     NM_META_SETTING_TYPE_IP6_CONFIG,
+    NM_META_SETTING_TYPE_LOOPBACK,
     NM_META_SETTING_TYPE_MACSEC,
     NM_META_SETTING_TYPE_MACVLAN,
     NM_META_SETTING_TYPE_MATCH,
@@ -134,6 +135,7 @@ typedef enum _nm_packed {
     NM_META_SETTING_TYPE_OVS_DPDK,
     NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS,
     NM_META_SETTING_TYPE_OVS_INTERFACE,
+    NM_META_SETTING_TYPE_OVS_OTHER_CONFIG,
     NM_META_SETTING_TYPE_OVS_PATCH,
     NM_META_SETTING_TYPE_OVS_PORT,
     NM_META_SETTING_TYPE_PPP,
diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c
index 31beb65e..f3372fb2 100644
--- a/src/libnmc-setting/nm-meta-setting-desc.c
+++ b/src/libnmc-setting/nm-meta-setting-desc.c
@@ -15,6 +15,7 @@
 #include "libnm-core-aux-intern/nm-common-macros.h"
 #include "libnm-glib-aux/nm-enum-utils.h"
 #include "libnm-glib-aux/nm-secret-utils.h"
+#include "libnm-glib-aux/nm-uuid.h"
 #include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-aux-extern/nm-libnm-core-aux.h"
 
@@ -125,11 +126,7 @@ _value_str_as_index_list(const char *value, gsize *out_len)
             if (!str_cloned) {
                 /* we use alloca() inside a loop here, but it is guarded to happen at most once. */
                 str_cloned = TRUE;
-                str        = nm_strndup_a(/* lgtm [cpp/alloca-in-loop] */
-                                   200,
-                                   str,
-                                   strlen(str),
-                                   &str_clone_free);
+                str        = nm_strndup_a(200, str, strlen(str), &str_clone_free);
             }
             s         = str;
             str[good] = '\0';
@@ -262,23 +259,21 @@ _parse_ip_address(int family, const char *address, GError **error)
     if (plen) {
         *plen++ = '\0';
         if ((prefix = _nm_utils_ascii_str_to_int64(plen, 10, 0, MAX_PREFIX, -1)) == -1) {
-            g_set_error(error,
-                        NM_UTILS_ERROR,
-                        NM_UTILS_ERROR_INVALID_ARGUMENT,
-                        _("invalid prefix '%s'; <0-%d> allowed"),
-                        plen,
-                        MAX_PREFIX);
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_INVALID_ARGUMENT,
+                               _("invalid prefix '%s'; <0-%d> allowed"),
+                               plen,
+                               MAX_PREFIX);
             return NULL;
         }
     }
 
     addr = nm_ip_address_new(family, ip_str, prefix, &local);
     if (!addr) {
-        g_set_error(error,
-                    NM_UTILS_ERROR,
-                    NM_UTILS_ERROR_INVALID_ARGUMENT,
-                    _("invalid IP address: %s"),
-                    local->message);
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_INVALID_ARGUMENT,
+                           _("invalid IP address: %s"),
+                           local->message);
         g_clear_error(&local);
     }
     return addr;
@@ -312,7 +307,11 @@ _parse_ip_route(int family, const char *str, GError **error)
     str_clean = nm_strstrip_avoid_copy_a(300, str, &str_clean_free);
     routev    = nm_strsplit_set(str_clean, " \t");
     if (!routev) {
-        g_set_error(error, 1, 0, "'%s' is not valid. %s", str, ROUTE_SYNTAX);
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           "'%s' is not valid. %s",
+                           str,
+                           ROUTE_SYNTAX);
         return NULL;
     }
 
@@ -328,12 +327,11 @@ _parse_ip_route(int family, const char *str, GError **error)
     prefix = MAX_PREFIX;
     if (plen) {
         if ((prefix = _nm_utils_ascii_str_to_int64(plen, 10, 0, MAX_PREFIX, -1)) == -1) {
-            g_set_error(error,
-                        NM_UTILS_ERROR,
-                        NM_UTILS_ERROR_INVALID_ARGUMENT,
-                        _("invalid prefix '%s'; <0-%d> allowed"),
-                        plen,
-                        MAX_PREFIX);
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_INVALID_ARGUMENT,
+                               _("invalid prefix '%s'; <0-%d> allowed"),
+                               plen,
+                               MAX_PREFIX);
             return NULL;
         }
     }
@@ -341,20 +339,22 @@ _parse_ip_route(int family, const char *str, GError **error)
     for (i = 1; routev[i]; i++) {
         gint64 tmp64;
 
-        if (nm_utils_ipaddr_is_valid(family, routev[i])) {
+        if (nm_inet_is_valid(family, routev[i])) {
             if (metric != -1 || attrs) {
-                g_set_error(error, 1, 0, _("the next hop ('%s') must be first"), routev[i]);
+                nm_utils_error_set(error,
+                                   NM_UTILS_ERROR_UNKNOWN,
+                                   _("the next hop ('%s') must be first"),
+                                   routev[i]);
                 return NULL;
             }
             next_hop = routev[i];
         } else if ((tmp64 = _nm_utils_ascii_str_to_int64(routev[i], 10, 0, G_MAXUINT32, -1))
                    != -1) {
             if (attrs) {
-                g_set_error(error,
-                            1,
-                            0,
-                            _("the metric ('%s') must be before attributes"),
-                            routev[i]);
+                nm_utils_error_set(error,
+                                   NM_UTILS_ERROR_UNKNOWN,
+                                   _("the metric ('%s') must be before attributes"),
+                                   routev[i]);
                 return NULL;
             }
             metric = tmp64;
@@ -397,14 +397,18 @@ _parse_ip_route(int family, const char *str, GError **error)
                 g_hash_table_iter_steal(&iter);
             }
         } else {
-            g_set_error(error, 1, 0, "%s", ROUTE_SYNTAX);
+            nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, "%s", ROUTE_SYNTAX);
             return NULL;
         }
     }
 
     route = nm_ip_route_new(family, dest, prefix, next_hop, metric, &local);
     if (!route) {
-        g_set_error(error, 1, 0, _("invalid route: %s. %s"), local->message, ROUTE_SYNTAX);
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           _("invalid route: %s. %s"),
+                           local->message,
+                           ROUTE_SYNTAX);
         g_clear_error(&local);
         return NULL;
     }
@@ -469,20 +473,18 @@ nmc_proxy_check_script(const char *script, char **out_script, GError **error)
 
         if (!g_file_get_contents(script, &contents, &c_len, NULL)) {
             if (desired_type == _PAC_SCRIPT_TYPE_FILE) {
-                g_set_error(error,
-                            NM_UTILS_ERROR,
-                            NM_UTILS_ERROR_INVALID_ARGUMENT,
-                            _("cannot read pac-script from file '%s'"),
-                            script);
+                nm_utils_error_set(error,
+                                   NM_UTILS_ERROR_INVALID_ARGUMENT,
+                                   _("cannot read pac-script from file '%s'"),
+                                   script);
                 return FALSE;
             }
         } else {
             if (c_len != strlen(contents)) {
-                g_set_error(error,
-                            NM_UTILS_ERROR,
-                            NM_UTILS_ERROR_INVALID_ARGUMENT,
-                            _("file '%s' contains non-valid utf-8"),
-                            script);
+                nm_utils_error_set(error,
+                                   NM_UTILS_ERROR_INVALID_ARGUMENT,
+                                   _("file '%s' contains non-valid utf-8"),
+                                   script);
                 return FALSE;
             }
             filename = script;
@@ -492,16 +494,12 @@ nmc_proxy_check_script(const char *script, char **out_script, GError **error)
 
     if (!strstr(script, "FindProxyForURL") || !g_utf8_validate(script, -1, NULL)) {
         if (filename) {
-            g_set_error(error,
-                        NM_UTILS_ERROR,
-                        NM_UTILS_ERROR_INVALID_ARGUMENT,
-                        _("'%s' does not contain a valid PAC Script"),
-                        filename);
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_INVALID_ARGUMENT,
+                               _("'%s' does not contain a valid PAC Script"),
+                               filename);
         } else {
-            g_set_error(error,
-                        NM_UTILS_ERROR,
-                        NM_UTILS_ERROR_INVALID_ARGUMENT,
-                        _("Not a valid PAC Script"));
+            nm_utils_error_set(error, NM_UTILS_ERROR_INVALID_ARGUMENT, _("Not a valid PAC Script"));
         }
         return FALSE;
     }
@@ -551,20 +549,18 @@ nmc_team_check_config(const char *config, char **out_config, GError **error)
 
         if (!g_file_get_contents(config, &contents, &c_len, NULL)) {
             if (desired_type == _TEAM_CONFIG_TYPE_FILE) {
-                g_set_error(error,
-                            NM_UTILS_ERROR,
-                            NM_UTILS_ERROR_INVALID_ARGUMENT,
-                            _("cannot read team config from file '%s'"),
-                            config);
+                nm_utils_error_set(error,
+                                   NM_UTILS_ERROR_INVALID_ARGUMENT,
+                                   _("cannot read team config from file '%s'"),
+                                   config);
                 return FALSE;
             }
         } else {
             if (c_len != strlen(contents)) {
-                g_set_error(error,
-                            NM_UTILS_ERROR,
-                            NM_UTILS_ERROR_INVALID_ARGUMENT,
-                            _("team config file '%s' contains non-valid utf-8"),
-                            config);
+                nm_utils_error_set(error,
+                                   NM_UTILS_ERROR_INVALID_ARGUMENT,
+                                   _("team config file '%s' contains non-valid utf-8"),
+                                   config);
                 return FALSE;
             }
             config = config_clone = g_steal_pointer(&contents);
@@ -1411,29 +1407,27 @@ _set_fcn_gobject_int(ARGS_SET_FCN)
     if ((errsv = errno) != 0) {
         if (errsv == ERANGE) {
             if (is_uint64) {
-                g_set_error(
+                nm_utils_error_set(
                     error,
-                    NM_UTILS_ERROR,
                     NM_UTILS_ERROR_INVALID_ARGUMENT,
                     _("'%s' is out of range [%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "]"),
                     value,
                     min.u64,
                     max.u64);
             } else {
-                g_set_error(error,
-                            NM_UTILS_ERROR,
-                            NM_UTILS_ERROR_INVALID_ARGUMENT,
-                            _("'%s' is out of range [%" G_GINT64_FORMAT ", %" G_GINT64_FORMAT "]"),
-                            value,
-                            min.i64,
-                            max.i64);
+                nm_utils_error_set(
+                    error,
+                    NM_UTILS_ERROR_INVALID_ARGUMENT,
+                    _("'%s' is out of range [%" G_GINT64_FORMAT ", %" G_GINT64_FORMAT "]"),
+                    value,
+                    min.i64,
+                    max.i64);
             }
         } else {
-            g_set_error(error,
-                        NM_UTILS_ERROR,
-                        NM_UTILS_ERROR_INVALID_ARGUMENT,
-                        _("'%s' is not a valid number"),
-                        value);
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_INVALID_ARGUMENT,
+                               _("'%s' is not a valid number"),
+                               value);
         }
         return FALSE;
     }
@@ -1486,12 +1480,11 @@ _set_fcn_gobject_mtu(ARGS_SET_FCN)
 
     v = _nm_utils_ascii_str_to_int64(value, 10, 0, G_MAXUINT32, -1);
     if (v < 0) {
-        g_set_error(error,
-                    NM_UTILS_ERROR,
-                    NM_UTILS_ERROR_INVALID_ARGUMENT,
-                    _("'%s' is out of range [0, %u]"),
-                    value,
-                    (unsigned) G_MAXUINT32);
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_INVALID_ARGUMENT,
+                           _("'%s' is out of range [0, %u]"),
+                           value,
+                           (unsigned) G_MAXUINT32);
         return FALSE;
     }
 
@@ -1534,7 +1527,10 @@ _set_fcn_gobject_mac(ARGS_SET_FCN)
     }
 
     if (!valid) {
-        g_set_error(error, 1, 0, _("'%s' is not a valid Ethernet MAC"), value);
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           _("'%s' is not a valid Ethernet MAC"),
+                           value);
         return FALSE;
     }
 
@@ -1656,19 +1652,17 @@ fail:
         valid_all = nm_utils_enum_get_values(gtype, min, max);
         valid_str = g_strjoinv(",", (char **) valid_all);
         if (is_flags) {
-            g_set_error(error,
-                        NM_UTILS_ERROR,
-                        NM_UTILS_ERROR_INVALID_ARGUMENT,
-                        _("invalid option '%s', use a combination of [%s]"),
-                        value,
-                        valid_str);
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_INVALID_ARGUMENT,
+                               _("invalid option '%s', use a combination of [%s]"),
+                               value,
+                               valid_str);
         } else {
-            g_set_error(error,
-                        NM_UTILS_ERROR,
-                        NM_UTILS_ERROR_INVALID_ARGUMENT,
-                        _("invalid option '%s', use one of [%s]"),
-                        value,
-                        valid_str);
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_INVALID_ARGUMENT,
+                               _("invalid option '%s', use one of [%s]"),
+                               value,
+                               valid_str);
         }
     }
     return FALSE;
@@ -2159,12 +2153,11 @@ validate_flags(NMSetting *setting, const char *prop, guint val, GError **error)
         flag_values = flag_values_to_string(pspec_flags->flags_class->values,
                                             pspec_flags->flags_class->n_values);
 
-        g_set_error(error,
-                    1,
-                    0,
-                    _("'%u' flags are not valid; use combination of %s"),
-                    val,
-                    flag_values);
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           _("'%u' flags are not valid; use combination of %s"),
+                           val,
+                           flag_values);
         success = FALSE;
     }
     g_value_unset(&value);
@@ -2180,7 +2173,10 @@ _set_fcn_gobject_flags(ARGS_SET_FCN)
         return _gobject_property_reset_default(setting, property_info->property_name);
 
     if (!nmc_string_to_uint(value, TRUE, 0, G_MAXUINT, &val_int)) {
-        g_set_error(error, 1, 0, _("'%s' is not a valid number (or out of range)"), value);
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           _("'%s' is not a valid number (or out of range)"),
+                           value);
         return FALSE;
     }
 
@@ -2201,7 +2197,7 @@ _set_fcn_gobject_ssid(ARGS_SET_FCN)
         return _gobject_property_reset_default(setting, property_info->property_name);
 
     if (strlen(value) > 32) {
-        g_set_error(error, 1, 0, _("'%s' is not valid"), value);
+        nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, _("'%s' is not valid"), value);
         return FALSE;
     }
 
@@ -2276,7 +2272,7 @@ _multilist_validate_fcn_is_domain(const char *domain, GError **error)
 static const char *
 _multilist_validate_fcn_is_ipv4_addr_or_subnet(const char *value, GError **error)
 {
-    if (!nm_utils_parse_inaddr_prefix_bin(AF_INET, value, NULL, NULL, NULL)) {
+    if (!nm_inet_parse_with_prefix_bin(AF_INET, value, NULL, NULL, NULL)) {
         nm_utils_error_set(error,
                            NM_UTILS_ERROR_INVALID_ARGUMENT,
                            _("invalid IPv4 or subnet \"%s\""),
@@ -2326,7 +2322,10 @@ _set_fcn_gobject_bytes(ARGS_SET_FCN)
 
         v = _nm_utils_ascii_str_to_int64(*iter, 16, 0, 255, -1);
         if (v == -1) {
-            g_set_error(error, 1, 0, _("'%s' is not a valid hex character"), *iter);
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_UNKNOWN,
+                               _("'%s' is not a valid hex character"),
+                               *iter);
             g_byte_array_free(array, TRUE);
             return FALSE;
         }
@@ -2631,30 +2630,67 @@ _get_fcn_connection_permissions(ARGS_GET_FCN)
 }
 
 static gboolean
-_set_fcn_connection_type(ARGS_SET_FCN)
+_set_fcn_connection_uuid(ARGS_SET_FCN)
 {
-    gs_free char *uuid = NULL;
-
-    if (nm_setting_connection_get_uuid(NM_SETTING_CONNECTION(setting))) {
-        /* Don't allow setting type unless the connection is brand new.
-         * Just because it's a bad idea and the user wouldn't probably want that.
-         * No technical reason, really.
-         * Also, using uuid to see if the connection is brand new is a bit
-         * hacky: we can not see if the type is already set, because
-         * nmc_setting_set_property() is called only after the property
-         * we're setting (type) has been removed. */
-        g_set_error(error, 1, 0, _("Can not change the connection type"));
+    const char *uuid;
+
+    uuid = nm_setting_connection_get_uuid(NM_SETTING_CONNECTION(setting));
+
+    if (!uuid) {
+        /* No UUID yet, we are about to create a new profile. We can set the UUID. */
+    } else if (NM_FLAGS_HAS(nm_meta_environment_get_env_flags(environment, environment_user_data),
+                            NM_META_ENV_FLAGS_OFFLINE)) {
+        /* In offline mode, we can change it. */
+    } else if (nm_uuid_is_valid_nmlegacy(value) && nm_streq(uuid, value)) {
+        /* Setting the same value does not actually trigger an error. */
+    } else {
+        /* The UUID is the unchanging ID of a profile. It cannot change, unless
+         * we are in offline mode (in which case, it can be useful to do just that). */
+        nm_utils_error_set(error, NM_UTILS_ERROR_INVALID_ARGUMENT, "the property can't be changed");
         return FALSE;
     }
 
+    if (NM_IN_STRSET(value, NULL, "", "new", "generate")) {
+        /* Special keywords are used for generating a new UUID. */
+        value = nm_uuid_generate_random_str_a();
+    }
+
+    g_object_set(G_OBJECT(setting), NM_SETTING_CONNECTION_UUID, value, NULL);
+    return TRUE;
+}
+
+static gboolean
+_set_fcn_connection_type(ARGS_SET_FCN)
+{
+    const char *connection_type;
+
+    connection_type = nm_setting_connection_get_connection_type(NM_SETTING_CONNECTION(setting));
+
+    if (connection_type) {
+        if (nm_streq0(connection_type, value)) {
+            /* No change. Setting the same again is fine. */
+            return TRUE;
+        }
+
+        /* There is no technical reason that a profile couldn't change its type.
+         * NetworkManager allows that just fine.
+         *
+         * However, it seems an unusual thing to do, so only allow that in
+         * offline mode. */
+        if (!NM_FLAGS_HAS(nm_meta_environment_get_env_flags(environment, environment_user_data),
+                          NM_META_ENV_FLAGS_OFFLINE)) {
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_UNKNOWN,
+                               _("Can not change the connection type"));
+            return FALSE;
+        }
+    }
+
     if (_SET_FCN_DO_RESET_DEFAULT(property_info, modifier, value)) {
         g_object_set(G_OBJECT(setting), property_info->property_name, NULL, NULL);
         return TRUE;
     }
 
-    uuid = nm_utils_uuid_generate();
-    g_object_set(G_OBJECT(setting), NM_SETTING_CONNECTION_UUID, uuid, NULL);
-
     g_object_set(G_OBJECT(setting), property_info->property_name, value, NULL);
     return TRUE;
 }
@@ -2994,7 +3030,10 @@ _set_fcn_dcb_flags(ARGS_SET_FCN)
                      || g_ascii_strcasecmp(*iter, "disabled") == 0 || t == 0) {
                 /* pass */
             } else {
-                g_set_error(error, 1, 0, _("'%s' is not a valid DCB flag"), *iter);
+                nm_utils_error_set(error,
+                                   NM_UTILS_ERROR_UNKNOWN,
+                                   _("'%s' is not a valid DCB flag"),
+                                   *iter);
                 return FALSE;
             }
         }
@@ -3034,20 +3073,18 @@ dcb_parse_uint_array(const char *val,
         /* If number is greater than 'max' it must equal 'other' */
         if (num == -1 || (other && (num > max) && (num != other))) {
             if (other) {
-                g_set_error(error,
-                            1,
-                            0,
-                            _("'%s' not a number between 0 and %u (inclusive) or %u"),
-                            *iter,
-                            max,
-                            other);
+                nm_utils_error_set(error,
+                                   NM_UTILS_ERROR_UNKNOWN,
+                                   _("'%s' not a number between 0 and %u (inclusive) or %u"),
+                                   *iter,
+                                   max,
+                                   other);
             } else {
-                g_set_error(error,
-                            1,
-                            0,
-                            _("'%s' not a number between 0 and %u (inclusive)"),
-                            *iter,
-                            max);
+                nm_utils_error_set(error,
+                                   NM_UTILS_ERROR_UNKNOWN,
+                                   _("'%s' not a number between 0 and %u (inclusive)"),
+                                   *iter,
+                                   max);
             }
             return FALSE;
         }
@@ -3233,7 +3270,10 @@ _set_fcn_infiniband_p_key(ARGS_SET_FCN)
     else {
         p_key = _nm_utils_ascii_str_to_int64(value, 0, -1, G_MAXUINT16, -2);
         if (p_key == -2) {
-            g_set_error(error, 1, 0, _("'%s' is not a valid IBoIP P_Key"), value);
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_UNKNOWN,
+                               _("'%s' is not a valid IBoIP P_Key"),
+                               value);
             return FALSE;
         }
     }
@@ -3409,23 +3449,6 @@ _set_fcn_ip_config_method(ARGS_SET_FCN)
     return TRUE;
 }
 
-static const char *
-_multilist_validate2_fcn_ip_config_dns(NMSetting *setting, const char *value, GError **error)
-{
-    int addr_family = nm_setting_ip_config_get_addr_family(NM_SETTING_IP_CONFIG(setting));
-
-    if (!nm_utils_parse_inaddr(addr_family, value, NULL)) {
-        nm_utils_error_set(error,
-                           NM_UTILS_ERROR_INVALID_ARGUMENT,
-                           _("invalid IPv%c address '%s'"),
-                           nm_utils_addr_family_to_char(addr_family),
-                           value);
-        return NULL;
-    }
-
-    return value;
-}
-
 static gboolean
 _multilist_add_fcn_ip_config_dns_options(NMSetting *setting, const char *item)
 {
@@ -3541,12 +3564,11 @@ _set_fcn_ip_config_gateway(ARGS_SET_FCN)
 
     value = nm_strstrip_avoid_copy_a(300, value, &value_to_free);
 
-    if (!nm_utils_ipaddr_is_valid(addr_family, value)) {
-        g_set_error(error,
-                    NM_UTILS_ERROR,
-                    NM_UTILS_ERROR_INVALID_ARGUMENT,
-                    _("invalid gateway address '%s'"),
-                    value);
+    if (!nm_inet_is_valid(addr_family, value)) {
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_INVALID_ARGUMENT,
+                           _("invalid gateway address '%s'"),
+                           value);
         return FALSE;
     }
 
@@ -3695,7 +3717,10 @@ _set_fcn_olpc_mesh_channel(ARGS_SET_FCN)
         return _gobject_property_reset_default(setting, property_info->property_name);
 
     if (!nmc_string_to_uint(value, TRUE, 1, 13, &chan_int)) {
-        g_set_error(error, 1, 0, _("'%s' is not a valid channel; use <1-13>"), value);
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           _("'%s' is not a valid channel; use <1-13>"),
+                           value);
         return FALSE;
     }
     g_object_set(setting, property_info->property_name, chan_int, NULL);
@@ -3742,6 +3767,20 @@ _objlist_obj_to_str_fcn_tc_config_qdiscs(NMMetaAccessorGetType get_type,
         g_string_append(str, s);
 }
 
+static void
+_objlist_obj_to_str_fcn_ovs_port_trunks(NMMetaAccessorGetType get_type,
+                                        NMSetting            *setting,
+                                        guint                 idx,
+                                        GString              *str)
+{
+    gs_free char *s = NULL;
+    NMRange      *trunk;
+
+    trunk = nm_setting_ovs_port_get_trunk(NM_SETTING_OVS_PORT(setting), idx);
+    s     = nm_range_to_str(trunk);
+    nm_utils_escaped_tokens_escape_gstr_assert(s, ESCAPED_TOKENS_DELIMITERS, str);
+}
+
 static void
 _objlist_obj_to_str_fcn_bridge_vlans(NMMetaAccessorGetType get_type,
                                      NMSetting            *setting,
@@ -3810,6 +3849,37 @@ _objlist_set_fcn_tc_config_qdiscs(NMSetting  *setting,
     return TRUE;
 }
 
+static gboolean
+_objlist_set_fcn_ovs_port_trunks(NMSetting  *setting,
+                                 gboolean    do_add,
+                                 const char *value,
+                                 GError    **error)
+{
+    NMRange              *range;
+    gs_free_error GError *local = NULL;
+    guint64               start;
+    guint64               end;
+
+    range = nm_range_from_str(value, &local);
+    if (!range) {
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_INVALID_ARGUMENT,
+                           "%s. %s",
+                           local->message,
+                           _("The valid syntax is: '' or '-"));
+        return FALSE;
+    }
+
+    if (do_add)
+        nm_setting_ovs_port_add_trunk(NM_SETTING_OVS_PORT(setting), range);
+    else {
+        nm_range_get_range(range, &start, &end);
+        nm_setting_ovs_port_remove_trunk_by_value(NM_SETTING_OVS_PORT(setting), start, end);
+    }
+
+    return TRUE;
+}
+
 static gboolean
 _objlist_set_fcn_bridge_vlans(NMSetting  *setting,
                               gboolean    do_add,
@@ -4026,7 +4096,10 @@ _set_fcn_vlan_xgress_priority_map(ARGS_SET_FCN)
                                                   NULL,
                                                   NULL,
                                                   NULL)) {
-            g_set_error(error, 1, 0, _("invalid priority map '%s'"), prio_map[i]);
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_UNKNOWN,
+                               _("invalid priority map '%s'"),
+                               prio_map[i]);
             return FALSE;
         }
     }
@@ -4116,7 +4189,10 @@ _set_fcn_wired_s390_subchannels(ARGS_SET_FCN)
     strv = nm_strsplit_set(value, " ,\t");
     len  = NM_PTRARRAY_LEN(strv);
     if (len != 2 && len != 3) {
-        g_set_error(error, 1, 0, _("'%s' is not valid; 2 or 3 strings should be provided"), value);
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           _("'%s' is not valid; 2 or 3 strings should be provided"),
+                           value);
         return FALSE;
     }
 
@@ -4188,13 +4264,16 @@ _set_fcn_wireless_channel(ARGS_SET_FCN)
         return _gobject_property_reset_default(setting, property_info->property_name);
 
     if (!nmc_string_to_uint(value, FALSE, 0, 0, &chan_int)) {
-        g_set_error(error, 1, 0, _("'%s' is not a valid channel"), value);
+        nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, _("'%s' is not a valid channel"), value);
         return FALSE;
     }
 
     if (!nm_utils_wifi_is_channel_valid(chan_int, "a")
         && !nm_utils_wifi_is_channel_valid(chan_int, "bg")) {
-        g_set_error(error, 1, 0, _("'%ld' is not a valid channel"), chan_int);
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           _("'%ld' is not a valid channel"),
+                           chan_int);
         return FALSE;
     }
 
@@ -4241,7 +4320,7 @@ _get_fcn_wireless_security_wep_key(ARGS_GET_FCN)
 static gboolean
 _set_fcn_wireless_wep_key(ARGS_SET_FCN)
 {
-    NMWepKeyType guessed_type = NM_WEP_KEY_TYPE_UNKNOWN;
+    NMWepKeyType guessed_type;
     NMWepKeyType type;
     guint32      prev_idx, idx;
 
@@ -4260,36 +4339,39 @@ _set_fcn_wireless_wep_key(ARGS_SET_FCN)
         guessed_type = NM_WEP_KEY_TYPE_KEY;
     else if (nm_utils_wep_key_valid(value, NM_WEP_KEY_TYPE_PASSPHRASE))
         guessed_type = NM_WEP_KEY_TYPE_PASSPHRASE;
-
-    if (guessed_type == NM_WEP_KEY_TYPE_UNKNOWN) {
-        g_set_error(error, 1, 0, _("'%s' is not valid"), value);
+    else {
+        nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, _("'%s' is not valid"), value);
         return FALSE;
     }
 
-    if (type != NM_WEP_KEY_TYPE_UNKNOWN && type != guessed_type) {
-        if (nm_utils_wep_key_valid(value, type))
+    if (type != guessed_type) {
+        if (type == NM_WEP_KEY_TYPE_UNKNOWN) {
+            /* pass */
+        } else if (nm_utils_wep_key_valid(value, type))
             guessed_type = type;
         else {
-            g_set_error(error,
-                        1,
-                        0,
-                        _("'%s' not compatible with %s '%s', please change the key or set the "
-                          "right %s first."),
-                        value,
-                        NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE,
-                        wep_key_type_to_string(type),
-                        NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE);
+            nm_utils_error_set(
+                error,
+                NM_UTILS_ERROR_UNKNOWN,
+                _("'%s' not compatible with %s '%s', please change the key or set the "
+                  "right %s first."),
+                value,
+                NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE,
+                wep_key_type_to_string(type),
+                NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE);
             return FALSE;
         }
     }
     prev_idx =
         nm_setting_wireless_security_get_wep_tx_keyidx(NM_SETTING_WIRELESS_SECURITY(setting));
     idx = property_info->property_name[strlen(property_info->property_name) - 1] - '0';
-    _env_warn_fcn(environment,
-                  environment_user_data,
-                  NM_META_ENV_WARN_LEVEL_INFO,
-                  N_("WEP key is guessed to be of '%s'"),
-                  wep_key_type_to_string(guessed_type));
+    if (type == NM_WEP_KEY_TYPE_UNKNOWN) {
+        _env_warn_fcn(environment,
+                      environment_user_data,
+                      NM_META_ENV_WARN_LEVEL_INFO,
+                      N_("WEP key is guessed to be of %s"),
+                      wep_key_type_to_string(guessed_type));
+    }
     if (idx != prev_idx) {
         _env_warn_fcn(environment,
                       environment_user_data,
@@ -4387,13 +4469,13 @@ _set_fcn_ethtool(ARGS_SET_FCN)
     if (nm_ethtool_id_is_coalesce(ethtool_id) || nm_ethtool_id_is_ring(ethtool_id)) {
         i64 = _nm_utils_ascii_str_to_int64(value, 10, 0, G_MAXUINT32, -1);
         if (i64 == -1) {
-            g_set_error(error,
-                        NM_UTILS_ERROR,
-                        NM_UTILS_ERROR_INVALID_ARGUMENT,
-                        _("'%s' is out of range [%" G_GUINT32_FORMAT ", %" G_GUINT32_FORMAT "]"),
-                        value,
-                        0,
-                        G_MAXUINT32);
+            nm_utils_error_set(
+                error,
+                NM_UTILS_ERROR_INVALID_ARGUMENT,
+                _("'%s' is out of range [%" G_GUINT32_FORMAT ", %" G_GUINT32_FORMAT "]"),
+                value,
+                0,
+                G_MAXUINT32);
             return FALSE;
         }
 
@@ -4407,11 +4489,10 @@ _set_fcn_ethtool(ARGS_SET_FCN)
                                     NMC_STRING_TO_TERNARY_FLAGS_IGNORE_FOR_DEFAULT,
                                     &t,
                                     NULL)) {
-        g_set_error(error,
-                    NM_UTILS_ERROR,
-                    NM_UTILS_ERROR_INVALID_ARGUMENT,
-                    _("'%s' is not valid; use 'on', 'off', or 'ignore'"),
-                    value);
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_INVALID_ARGUMENT,
+                           _("'%s' is not valid; use 'on', 'off', or 'ignore'"),
+                           value);
         return FALSE;
     }
     if (t == NM_TERNARY_DEFAULT)
@@ -5379,7 +5460,10 @@ static const NMMetaPropertyInfo *const property_infos_CONNECTION[] = {
         .property_type =                &_pt_gobject_string,
     ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_UUID,
-        .property_type =                DEFINE_PROPERTY_TYPE ( .get_fcn = _get_fcn_gobject ),
+            .property_type =                DEFINE_PROPERTY_TYPE (
+                .get_fcn =                  _get_fcn_gobject,
+                .set_fcn =                  _set_fcn_connection_uuid,
+            ),
     ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_STABLE_ID,
         .property_type =                &_pt_gobject_string,
@@ -5987,7 +6071,6 @@ static const NMMetaPropertyInfo *const property_infos_IP4_CONFIG[] = {
                 .add_fcn =              MULTILIST_ADD_FCN             (NMSettingIPConfig, nm_setting_ip_config_add_dns),
                 .remove_by_idx_fcn_s =  MULTILIST_REMOVE_BY_IDX_FCN_S (NMSettingIPConfig, nm_setting_ip_config_remove_dns),
                 .remove_by_value_fcn =  MULTILIST_REMOVE_BY_VALUE_FCN (NMSettingIPConfig, nm_setting_ip_config_remove_dns_by_value),
-                .validate2_fcn =        _multilist_validate2_fcn_ip_config_dns,
                 .strsplit_plain =       TRUE,
             ),
         ),
@@ -6202,6 +6285,9 @@ static const NMMetaPropertyInfo *const property_infos_IP4_CONFIG[] = {
             ),
         ),
     ),
+    PROPERTY_INFO (NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_AUTO_ROUTE_EXT_GW,
+        .property_type =                &_pt_gobject_ternary,
+    ),
     NULL
 };
 
@@ -6240,7 +6326,6 @@ static const NMMetaPropertyInfo *const property_infos_IP6_CONFIG[] = {
                 .add_fcn =              MULTILIST_ADD_FCN             (NMSettingIPConfig, nm_setting_ip_config_add_dns),
                 .remove_by_idx_fcn_s =  MULTILIST_REMOVE_BY_IDX_FCN_S (NMSettingIPConfig, nm_setting_ip_config_remove_dns),
                 .remove_by_value_fcn =  MULTILIST_REMOVE_BY_VALUE_FCN (NMSettingIPConfig, nm_setting_ip_config_remove_dns_by_value),
-                .validate2_fcn =        _multilist_validate2_fcn_ip_config_dns,
                 .strsplit_plain =       TRUE,
             ),
         ),
@@ -6448,6 +6533,9 @@ static const NMMetaPropertyInfo *const property_infos_IP6_CONFIG[] = {
             ),
         )
     ),
+    PROPERTY_INFO (NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_AUTO_ROUTE_EXT_GW,
+        .property_type =                &_pt_gobject_ternary,
+    ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_IP6_CONFIG_TOKEN,
         .property_type =                &_pt_gobject_string,
     ),
@@ -6513,6 +6601,12 @@ static const NMMetaPropertyInfo *const property_infos_IP_TUNNEL[] = {
     PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_FLOW_LABEL,
         .property_type =                &_pt_gobject_int,
     ),
+    PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_FWMARK,
+        .property_type =                &_pt_gobject_int,
+        .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+            .base =                     16,
+        ),
+    ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_IP_TUNNEL_MTU,
         .property_type =                &_pt_gobject_mtu,
     ),
@@ -6527,6 +6621,21 @@ static const NMMetaPropertyInfo *const property_infos_IP_TUNNEL[] = {
     NULL
 };
 
+#undef  _CURRENT_NM_META_SETTING_TYPE
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_LOOPBACK
+static const NMMetaPropertyInfo *const property_infos_LOOPBACK[] = {
+    PROPERTY_INFO_WITH_DESC (NM_SETTING_LOOPBACK_MTU,
+        .is_cli_option =                TRUE,
+        .property_alias =               "mtu",
+        .prompt =                       N_("MTU"),
+        .property_type =                &_pt_gobject_mtu,
+        .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (mtu,
+            .get_fcn =                  MTU_GET_FCN (NMSettingLoopback, nm_setting_loopback_get_mtu),
+        ),
+    ),
+    NULL
+};
+
 #undef  _CURRENT_NM_META_SETTING_TYPE
 #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_MACSEC
 static const NMMetaPropertyInfo *const property_infos_MACSEC[] = {
@@ -6750,6 +6859,12 @@ static const NMMetaPropertyInfo *const property_infos_OVS_DPDK[] = {
    ),
    PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_DPDK_N_RXQ,
         .property_type =                &_pt_gobject_int,
+   ),
+   PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_DPDK_N_RXQ_DESC,
+        .property_type =                &_pt_gobject_int,
+   ),
+   PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_DPDK_N_TXQ_DESC,
+        .property_type =                &_pt_gobject_int,
    ),
     NULL
 };
@@ -6763,6 +6878,9 @@ static const NMMetaPropertyInfo *const property_infos_OVS_INTERFACE[] = {
             .values_static =            NM_MAKE_STRV ("internal", "system", "patch", "dpdk"),
         ),
     ),
+    PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_INTERFACE_OFPORT_REQUEST,
+          .property_type = &_pt_gobject_int,
+    ),
     NULL
 };
 
@@ -6781,7 +6899,18 @@ static const NMMetaPropertyInfo *const property_infos_OVS_PORT[] = {
     PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_VLAN_MODE,
         .property_type =                &_pt_gobject_string,
         .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
-            .values_static =            NM_MAKE_STRV ("access", "native-tagged", "native-untagged", "trunk"),
+            .values_static =            NM_MAKE_STRV ("access", "native-tagged", "native-untagged", "trunk", "dot1q-tunnel"),
+        ),
+    ),
+    PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_TRUNKS,
+        .property_type =                &_pt_objlist,
+        .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+            PROPERTY_TYP_DATA_SUBTYPE (objlist,
+                .get_num_fcn =          OBJLIST_GET_NUM_FCN         (NMSettingOvsPort, nm_setting_ovs_port_get_num_trunks),
+                .clear_all_fcn =        OBJLIST_CLEAR_ALL_FCN       (NMSettingOvsPort, nm_setting_ovs_port_clear_trunks),
+                .obj_to_str_fcn =       _objlist_obj_to_str_fcn_ovs_port_trunks,
+                .set_fcn =              _objlist_set_fcn_ovs_port_trunks,
+            ),
         ),
     ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_TAG,
@@ -7422,6 +7551,12 @@ static const NMMetaPropertyInfo *const property_infos_VLAN[] = {
             .set_fcn =                  _set_fcn_gobject_flags,
         ),
     ),
+    PROPERTY_INFO_WITH_DESC (NM_SETTING_VLAN_PROTOCOL,
+        .property_type =                &_pt_gobject_string,
+        .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+            .values_static =            NM_MAKE_STRV ("802.1Q", "802.1ad"),
+        ),
+    ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_VLAN_INGRESS_PRIORITY_MAP,
         .is_cli_option =                TRUE,
         .property_alias =               "ingress",
@@ -8280,12 +8415,14 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN)
 #define SETTING_PRETTY_NAME_IP4_CONFIG          N_("IPv4 protocol")
 #define SETTING_PRETTY_NAME_IP6_CONFIG          N_("IPv6 protocol")
 #define SETTING_PRETTY_NAME_IP_TUNNEL           N_("IP-tunnel settings")
+#define SETTING_PRETTY_NAME_LOOPBACK            N_("Loopback settings")
 #define SETTING_PRETTY_NAME_MACSEC              N_("MACsec connection")
 #define SETTING_PRETTY_NAME_MACVLAN             N_("macvlan connection")
 #define SETTING_PRETTY_NAME_MATCH               N_("Match")
 #define SETTING_PRETTY_NAME_OLPC_MESH           N_("OLPC Mesh connection")
 #define SETTING_PRETTY_NAME_OVS_BRIDGE          N_("Open vSwitch bridge settings")
 #define SETTING_PRETTY_NAME_OVS_DPDK            N_("Open vSwitch DPDK interface settings")
+#define SETTING_PRETTY_NAME_OVS_OTHER_CONFIG    N_("OVS Other Config")
 #define SETTING_PRETTY_NAME_OVS_EXTERNAL_IDS    N_("OVS External IDs")
 #define SETTING_PRETTY_NAME_OVS_INTERFACE       N_("Open vSwitch interface settings")
 #define SETTING_PRETTY_NAME_OVS_PATCH           N_("Open vSwitch patch interface settings")
@@ -8439,6 +8576,12 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
             NM_META_SETTING_VALID_PART_ITEM (ETHTOOL,               FALSE),
         ),
     ),
+    SETTING_INFO (LOOPBACK,
+        .valid_parts = NM_META_SETTING_VALID_PARTS (
+            NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
+            NM_META_SETTING_VALID_PART_ITEM (LOOPBACK,              TRUE),
+        ),
+    ),
     SETTING_INFO (MACSEC,
         .valid_parts = NM_META_SETTING_VALID_PARTS (
             NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
@@ -8478,6 +8621,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
             NM_META_SETTING_VALID_PART_ITEM (OVS_DPDK,              TRUE),
         ),
     ),
+    SETTING_INFO_EMPTY (OVS_OTHER_CONFIG),
     SETTING_INFO_EMPTY (OVS_EXTERNAL_IDS),
     SETTING_INFO (OVS_INTERFACE,
         .valid_parts = NM_META_SETTING_VALID_PARTS (
diff --git a/src/libnmc-setting/nm-meta-setting-desc.h b/src/libnmc-setting/nm-meta-setting-desc.h
index ca044571..8e3383cc 100644
--- a/src/libnmc-setting/nm-meta-setting-desc.h
+++ b/src/libnmc-setting/nm-meta-setting-desc.h
@@ -486,6 +486,11 @@ typedef enum {
     NM_META_ENV_WARN_LEVEL_WARN,
 } NMMetaEnvWarnLevel;
 
+typedef enum {
+    NM_META_ENV_FLAGS_NONE    = 0,
+    NM_META_ENV_FLAGS_OFFLINE = 0x1,
+} NMMetaEnvFlags;
+
 /* the settings-meta data is supposed to be independent of an actual client
  * implementation. Hence, there is a need for hooks to the meta-data.
  * The meta-data handlers may call back to the environment with certain
@@ -506,8 +511,20 @@ struct _NMMetaEnvironment {
     struct _NMRemoteConnection *const *(*get_nm_connections)(const NMMetaEnvironment *environment,
                                                              gpointer environment_user_data,
                                                              guint   *out_len);
+
+    NMMetaEnvFlags (*get_env_flags)(const NMMetaEnvironment *environment,
+                                    gpointer                 environment_user_data);
 };
 
+static inline NMMetaEnvFlags
+nm_meta_environment_get_env_flags(const NMMetaEnvironment *environment,
+                                  gpointer                 environment_user_data)
+{
+    if (environment && environment->get_env_flags)
+        return environment->get_env_flags(environment, environment_user_data);
+    return NM_META_ENV_FLAGS_NONE;
+}
+
 /*****************************************************************************/
 
 /* NMSettingBond is special in that it has nested properties.
diff --git a/src/libnmc-setting/settings-docs-input.xml b/src/libnmc-setting/settings-docs-input.xml
deleted file mode 100644
index 39bfb540..00000000
--- a/src/libnmc-setting/settings-docs-input.xml
+++ /dev/null
@@ -1,938 +0,0 @@
- The number of retries for the authentication. Zero means to try indefinitely; -1 means to use a global default. If the global default is not set, the authentication retries for 3 times before failing the connection. Currently, this only applies to 802-1x authentication. The number of retries for the authentication. Zero means to try indefinitely; -1 means to use a global default. If the global default is not set, the authentication retries for 3 times before failing the connection.
-
- Currently, this only applies to 802-1x authentication. Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See "secondaries" as an alternative to automatically connect VPN profiles. If multiple profiles are ready to autoconnect on the same device, the one with the better "connection.autoconnect-priority" is chosen. If the priorities are equal, then the most recently connected profile is activated. If the profiles were not connected earlier or their "connection.timestamp" is identical, the choice is undefined. Depending on "connection.multi-connect", a profile can (auto)connect only once at a time or multiple times. Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection.
-
- Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile.
-
- Note that autoconnect is not implemented for VPN profiles. See "secondaries" as an alternative to automatically connect VPN profiles.
-
- If multiple profiles are ready to autoconnect on the same device, the one with the better "connection.autoconnect-priority" is chosen. If the priorities are equal, then the most recently connected profile is activated. If the profiles were not connected earlier or their "connection.timestamp" is identical, the choice is undefined.
-
- Depending on "connection.multi-connect", a profile can (auto)connect only once at a time or multiple times. The autoconnect priority in range -999 to 999. If the connection is set to autoconnect, connections with higher priority will be preferred. The higher number means higher priority. Defaults to 0. Note that this property only matters if there are more than one candidate profile to select for autoconnect. In case of equal priority, the profile used most recently is chosen. The autoconnect priority in range -999 to 999. If the connection is set to autoconnect, connections with higher priority will be preferred. The higher number means higher priority. Defaults to 0. Note that this property only matters if there are more than one candidate profile to select for autoconnect. In case of equal priority, the profile used most recently is chosen. The number of times a connection should be tried when autoactivating before giving up. Zero means forever, -1 means the global default (4 times if not overridden). Setting this to 1 means to try activation only once before blocking autoconnect. Note that after a timeout, NetworkManager will try to autoconnect again. The number of times a connection should be tried when autoactivating before giving up. Zero means forever, -1 means the global default (4 times if not overridden). Setting this to 1 means to try activation only once before blocking autoconnect. Note that after a timeout, NetworkManager will try to autoconnect again. Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties "autoconnect", "autoconnect-priority" and "autoconnect-retries" are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0. Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties "autoconnect", "autoconnect-priority" and "autoconnect-retries" are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0. Whether DNSOverTls (dns-over-tls) is enabled for the connection. DNSOverTls is a technology which uses TLS to encrypt dns traffic. The permitted values are: "yes" (2) use DNSOverTls and disabled fallback, "opportunistic" (1) use DNSOverTls but allow fallback to unencrypted resolution, "no" (0) don't ever use DNSOverTls. If unspecified "default" depends on the plugin used. Systemd-resolved uses global setting. This feature requires a plugin which supports DNSOverTls. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved. Whether DNSOverTls (dns-over-tls) is enabled for the connection. DNSOverTls is a technology which uses TLS to encrypt dns traffic.
-
- The permitted values are: "yes" (2) use DNSOverTls and disabled fallback, "opportunistic" (1) use DNSOverTls but allow fallback to unencrypted resolution, "no" (0) don't ever use DNSOverTls. If unspecified "default" depends on the plugin used. Systemd-resolved uses global setting.
-
- This feature requires a plugin which supports DNSOverTls. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved. If greater than zero, delay success of IP addressing until either the timeout is reached, or an IP gateway replies to a ping. If greater than zero, delay success of IP addressing until either the timeout is reached, or an IP gateway replies to a ping. A human readable unique identifier for the connection, like "Work Wi-Fi" or "T-Mobile 3G". A human readable unique identifier for the connection, like "Work Wi-Fi" or "T-Mobile 3G". The name of the network interface this connection is bound to. If not set, then the connection can be attached to any interface of the appropriate type (subject to restrictions imposed by other settings). For software devices this specifies the name of the created device. For connection types where interface names cannot easily be made persistent (e.g. mobile broadband or USB Ethernet), this property should not be used. Setting this property restricts the interfaces a connection can be used with, and if interface names change or are reordered the connection may be applied to the wrong interface. The name of the network interface this connection is bound to. If not set, then the connection can be attached to any interface of the appropriate type (subject to restrictions imposed by other settings).
-
- For software devices this specifies the name of the created device.
-
- For connection types where interface names cannot easily be made persistent (e.g. mobile broadband or USB Ethernet), this property should not be used. Setting this property restricts the interfaces a connection can be used with, and if interface names change or are reordered the connection may be applied to the wrong interface. Whether LLDP is enabled for the connection. Whether LLDP is enabled for the connection. Whether Link-Local Multicast Name Resolution (LLMNR) is enabled for the connection. LLMNR is a protocol based on the Domain Name System (DNS) packet format that allows both IPv4 and IPv6 hosts to perform name resolution for hosts on the same local link. The permitted values are: "yes" (2) register hostname and resolving for the connection, "no" (0) disable LLMNR for the interface, "resolve" (1) do not register hostname but allow resolving of LLMNR host names If unspecified, "default" ultimately depends on the DNS plugin (which for systemd-resolved currently means "yes"). This feature requires a plugin which supports LLMNR. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved. Whether Link-Local Multicast Name Resolution (LLMNR) is enabled for the connection. LLMNR is a protocol based on the Domain Name System (DNS) packet format that allows both IPv4 and IPv6 hosts to perform name resolution for hosts on the same local link.
-
- The permitted values are: "yes" (2) register hostname and resolving for the connection, "no" (0) disable LLMNR for the interface, "resolve" (1) do not register hostname but allow resolving of LLMNR host names If unspecified, "default" ultimately depends on the DNS plugin (which for systemd-resolved currently means "yes").
-
- This feature requires a plugin which supports LLMNR. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved. Interface name of the master device or UUID of the master connection. Interface name of the master device or UUID of the master connection. Whether mDNS is enabled for the connection. The permitted values are: "yes" (2) register hostname and resolving for the connection, "no" (0) disable mDNS for the interface, "resolve" (1) do not register hostname but allow resolving of mDNS host names and "default" (-1) to allow lookup of a global default in NetworkManager.conf. If unspecified, "default" ultimately depends on the DNS plugin (which for systemd-resolved currently means "no"). This feature requires a plugin which supports mDNS. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved. Whether mDNS is enabled for the connection.
-
- The permitted values are: "yes" (2) register hostname and resolving for the connection, "no" (0) disable mDNS for the interface, "resolve" (1) do not register hostname but allow resolving of mDNS host names and "default" (-1) to allow lookup of a global default in NetworkManager.conf. If unspecified, "default" ultimately depends on the DNS plugin (which for systemd-resolved currently means "no").
-
- This feature requires a plugin which supports mDNS. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved. Whether the connection is metered. When updating this property on a currently activated connection, the change takes effect immediately. Whether the connection is metered.
-
- When updating this property on a currently activated connection, the change takes effect immediately. Whether to configure MPTCP endpoints and the address flags. If MPTCP is enabled in NetworkManager, it will configure the addresses of the interface as MPTCP endpoints. Note that IPv4 loopback addresses (127.0.0.0/8), IPv4 link local addresses (169.254.0.0/16), the IPv6 loopback address (::1), IPv6 link local addresses (fe80::/10), IPv6 unique local addresses (ULA, fc00::/7) and IPv6 privacy extension addresses (rfc3041, ipv6.ip6-privacy) will be excluded from being configured as endpoints. If "disabled" (0x1), MPTCP handling for the interface is disabled and no endpoints are registered. The "enabled" (0x2) flag means that MPTCP handling is enabled. This flag can also be implied from the presence of other flags. Even when enabled, MPTCP handling will by default still be disabled unless "/proc/sys/net/mptcp/enabled" sysctl is on. NetworkManager does not change the sysctl and this is up to the administrator or distribution. To configure endpoints even if the sysctl is disabled, "also-without-sysctl" (0x4) flag can be used. In that case, NetworkManager doesn't look at the sysctl and configures endpoints regardless. Even when enabled, NetworkManager will only configure MPTCP endpoints for a certain address family, if there is a unicast default route (0.0.0.0/0 or ::/0) in the main routing table. The flag "also-without-default-route" (0x8) can override that. When MPTCP handling is enabled then endpoints are configured with the specified address flags "signal" (0x10), "subflow" (0x20), "backup" (0x40), "fullmesh" (0x80). See ip-mptcp(8) manual for additional information about the flags. If the flags are zero (0x0), the global connection default from NetworkManager.conf is honored. If still unspecified, the fallback is "enabled,subflow". Note that this means that MPTCP is by default done depending on the "/proc/sys/net/mptcp/enabled" sysctl. NetworkManager does not change the MPTCP limits nor enable MPTCP via "/proc/sys/net/mptcp/enabled". That is a host configuration which the admin can change via sysctl and ip-mptcp. Strict reverse path filtering (rp_filter) breaks many MPTCP use cases, so when MPTCP handling for IPv4 addresses on the interface is enabled, NetworkManager would loosen the strict reverse path filtering (1) to the loose setting (2). Whether to configure MPTCP endpoints and the address flags. If MPTCP is enabled in NetworkManager, it will configure the addresses of the interface as MPTCP endpoints. Note that IPv4 loopback addresses (127.0.0.0/8), IPv4 link local addresses (169.254.0.0/16), the IPv6 loopback address (::1), IPv6 link local addresses (fe80::/10), IPv6 unique local addresses (ULA, fc00::/7) and IPv6 privacy extension addresses (rfc3041, ipv6.ip6-privacy) will be excluded from being configured as endpoints.
-
- If "disabled" (0x1), MPTCP handling for the interface is disabled and no endpoints are registered.
-
- The "enabled" (0x2) flag means that MPTCP handling is enabled. This flag can also be implied from the presence of other flags.
-
- Even when enabled, MPTCP handling will by default still be disabled unless "/proc/sys/net/mptcp/enabled" sysctl is on. NetworkManager does not change the sysctl and this is up to the administrator or distribution. To configure endpoints even if the sysctl is disabled, "also-without-sysctl" (0x4) flag can be used. In that case, NetworkManager doesn't look at the sysctl and configures endpoints regardless.
-
- Even when enabled, NetworkManager will only configure MPTCP endpoints for a certain address family, if there is a unicast default route (0.0.0.0/0 or ::/0) in the main routing table. The flag "also-without-default-route" (0x8) can override that.
-
- When MPTCP handling is enabled then endpoints are configured with the specified address flags "signal" (0x10), "subflow" (0x20), "backup" (0x40), "fullmesh" (0x80). See ip-mptcp(8) manual for additional information about the flags.
-
- If the flags are zero (0x0), the global connection default from NetworkManager.conf is honored. If still unspecified, the fallback is "enabled,subflow". Note that this means that MPTCP is by default done depending on the "/proc/sys/net/mptcp/enabled" sysctl.
-
- NetworkManager does not change the MPTCP limits nor enable MPTCP via "/proc/sys/net/mptcp/enabled". That is a host configuration which the admin can change via sysctl and ip-mptcp.
-
- Strict reverse path filtering (rp_filter) breaks many MPTCP use cases, so when MPTCP handling for IPv4 addresses on the interface is enabled, NetworkManager would loosen the strict reverse path filtering (1) to the loose setting (2). If configured, set to a Manufacturer Usage Description (MUD) URL that points to manufacturer-recommended network policies for IoT devices. It is transmitted as a DHCPv4 or DHCPv6 option. The value must be a valid URL starting with "https://". The special value "none" is allowed to indicate that no MUD URL is used. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the ultimate default is "none". If configured, set to a Manufacturer Usage Description (MUD) URL that points to manufacturer-recommended network policies for IoT devices. It is transmitted as a DHCPv4 or DHCPv6 option. The value must be a valid URL starting with "https://".
-
- The special value "none" is allowed to indicate that no MUD URL is used.
-
- If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the ultimate default is "none". Specifies whether the profile can be active multiple times at a particular moment. The value is of type NMConnectionMultiConnect. Specifies whether the profile can be active multiple times at a particular moment. The value is of type NMConnectionMultiConnect. An array of strings defining what access a given user has to this connection.  If this is NULL or empty, all users are allowed to access this connection; otherwise users are allowed if and only if they are in this list.  When this is not empty, the connection can be active only when one of the specified users is logged into an active session.  Each entry is of the form "[type]:[id]:[reserved]"; for example, "user:dcbw:blah". At this time only the "user" [type] is allowed.  Any other values are ignored and reserved for future use.  [id] is the username that this permission refers to, which may not contain the ":" character. Any [reserved] information present must be ignored and is reserved for future use.  All of [type], [id], and [reserved] must be valid UTF-8. An array of strings defining what access a given user has to this connection.  If this is NULL or empty, all users are allowed to access this connection; otherwise users are allowed if and only if they are in this list.  When this is not empty, the connection can be active only when one of the specified users is logged into an active session.  Each entry is of the form "[type]:[id]:[reserved]"; for example, "user:dcbw:blah".
-
- At this time only the "user" [type] is allowed.  Any other values are ignored and reserved for future use.  [id] is the username that this permission refers to, which may not contain the ":" character. Any [reserved] information present must be ignored and is reserved for future use.  All of [type], [id], and [reserved] must be valid UTF-8. FALSE if the connection can be modified using the provided settings service's D-Bus interface with the right privileges, or TRUE if the connection is read-only and cannot be modified. FALSE if the connection can be modified using the provided settings service's D-Bus interface with the right privileges, or TRUE if the connection is read-only and cannot be modified. List of connection UUIDs that should be activated when the base connection itself is activated. Currently, only VPN connections are supported. List of connection UUIDs that should be activated when the base connection itself is activated. Currently, only VPN connections are supported. Setting name of the device type of this slave's master connection (eg, "bond"), or NULL if this connection is not a slave. Setting name of the device type of this slave's master connection (eg, "bond"), or NULL if this connection is not a slave. This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual about the secret-key and the host identity. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are "${CONNECTION}", "${DEVICE}", "${MAC}", "${BOOT}", "${RANDOM}". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that "${DEVICE}" corresponds to the interface name of the device and "${MAC}" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as "$$". For example, set it to "${CONNECTION}-${BOOT}-${DEVICE}" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to "${CONNECTION}" and uses a unique, fixed ID for the connection. This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context.
-
- The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid].
-
- Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual about the secret-key and the host identity.
-
- The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are "${CONNECTION}", "${DEVICE}", "${MAC}", "${BOOT}", "${RANDOM}". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that "${DEVICE}" corresponds to the interface name of the device and "${MAC}" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as "$$". For example, set it to "${CONNECTION}-${BOOT}-${DEVICE}" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates.
-
- If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to "${CONNECTION}" and uses a unique, fixed ID for the connection. The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved). The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated.
-
- NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved). Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, "802-3-ethernet" or "802-11-wireless" or "bluetooth", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, "vpn" or "bridge", etc). Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, "802-3-ethernet" or "802-11-wireless" or "bluetooth", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, "vpn" or "bridge", etc). A universally unique identifier for the connection, for example generated with libuuid.  It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network.  For example, it should not be changed when the "id" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or "type" property changes. The UUID must be in the format "2815492f-7e56-435e-b2e9-246bd7cdc664" (ie, contains only hexadecimal characters and "-"). A universally unique identifier for the connection, for example generated with libuuid.  It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network.  For example, it should not be changed when the "id" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or "type" property changes.
-
- The UUID must be in the format "2815492f-7e56-435e-b2e9-246bd7cdc664" (ie, contains only hexadecimal characters and "-"). Time in milliseconds to wait for connection to be considered activated. The wait will start after the pre-up dispatcher event. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time. Time in milliseconds to wait for connection to be considered activated. The wait will start after the pre-up dispatcher event.
-
- The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time. Timeout in milliseconds to wait for device at startup. During boot, devices may take a while to be detected by the driver. This property will cause to delay NetworkManager-wait-online.service and nm-online to give the device a chance to appear. This works by waiting for the given timeout until a compatible device for the profile is available and managed. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time. Timeout in milliseconds to wait for device at startup. During boot, devices may take a while to be detected by the driver. This property will cause to delay NetworkManager-wait-online.service and nm-online to give the device a chance to appear. This works by waiting for the given timeout until a compatible device for the profile is available and managed.
-
- The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time. The trust level of a the connection.  Free form case-insensitive string (for example "Home", "Work", "Public").  NULL or unspecified zone means the connection will be placed in the default zone as defined by the firewall. When updating this property on a currently activated connection, the change takes effect immediately. The trust level of a the connection.  Free form case-insensitive string (for example "Home", "Work", "Public").  NULL or unspecified zone means the connection will be placed in the default zone as defined by the firewall.
-
- When updating this property on a currently activated connection, the change takes effect immediately. If given, specifies the parent interface name or parent connection UUID from which this 6LowPAN interface should be created. If given, specifies the parent interface name or parent connection UUID from which this 6LowPAN interface should be created. List of strings to be matched against the altSubjectName of the certificate presented by the authentication server. If the list is empty, no verification of the server certificate's altSubjectName is performed. List of strings to be matched against the altSubjectName of the certificate presented by the authentication server. If the list is empty, no verification of the server certificate's altSubjectName is performed. Anonymous identity string for EAP authentication methods.  Used as the unencrypted identity with EAP types that support different tunneled identity like EAP-TTLS. Anonymous identity string for EAP authentication methods.  Used as the unencrypted identity with EAP types that support different tunneled identity like EAP-TTLS. A timeout for the authentication. Zero means the global default; if the global default is not set, the authentication timeout is 25 seconds. A timeout for the authentication. Zero means the global default; if the global default is not set, the authentication timeout is 25 seconds. Contains the CA certificate if used by the EAP method specified in the "eap" property. Certificate data is specified using a "scheme"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory. Contains the CA certificate if used by the EAP method specified in the "eap" property.
-
- Certificate data is specified using a "scheme"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended.
-
- Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory. The password used to access the CA certificate stored in "ca-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. The password used to access the CA certificate stored in "ca-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. Flags indicating how to handle the "ca-cert-password" property. Flags indicating how to handle the "ca-cert-password" property. UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the "ca-cert" property. If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored. UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the "ca-cert" property.
-
- If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored. Contains the client certificate if used by the EAP method specified in the "eap" property. Certificate data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. Contains the client certificate if used by the EAP method specified in the "eap" property.
-
- Certificate data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. The password used to access the client certificate stored in "client-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. The password used to access the client certificate stored in "client-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. Flags indicating how to handle the "client-cert-password" property. Flags indicating how to handle the "client-cert-password" property. Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server.  If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a ";" delimited list. Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server.  If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a ";" delimited list. Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server.  If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a ";" delimited list. Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server.  If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a ";" delimited list. The allowed EAP method to be used when authenticating to the network with 802.1x.  Valid methods are: "leap", "md5", "tls", "peap", "ttls", "pwd", and "fast".  Each method requires different configuration using the properties of this setting; refer to wpa_supplicant documentation for the allowed combinations. The allowed EAP method to be used when authenticating to the network with 802.1x.  Valid methods are: "leap", "md5", "tls", "peap", "ttls", "pwd", and "fast".  Each method requires different configuration using the properties of this setting; refer to wpa_supplicant documentation for the allowed combinations. Identity string for EAP authentication methods.  Often the user's user or login name. Identity string for EAP authentication methods.  Often the user's user or login name. Whether the 802.1X authentication is optional. If TRUE, the activation will continue even after a timeout or an authentication failure. Setting the property to TRUE is currently allowed only for Ethernet connections. If set to FALSE, the activation can continue only after a successful authentication. Whether the 802.1X authentication is optional. If TRUE, the activation will continue even after a timeout or an authentication failure. Setting the property to TRUE is currently allowed only for Ethernet connections. If set to FALSE, the activation can continue only after a successful authentication. UTF-8 encoded file path containing PAC for EAP-FAST. UTF-8 encoded file path containing PAC for EAP-FAST. UTF-8 encoded password used for EAP authentication methods. If both the "password" property and the "password-raw" property are specified, "password" is preferred. UTF-8 encoded password used for EAP authentication methods. If both the "password" property and the "password-raw" property are specified, "password" is preferred. Flags indicating how to handle the "password" property. Flags indicating how to handle the "password" property. Password used for EAP authentication methods, given as a byte array to allow passwords in other encodings than UTF-8 to be used. If both the "password" property and the "password-raw" property are specified, "password" is preferred. Password used for EAP authentication methods, given as a byte array to allow passwords in other encodings than UTF-8 to be used. If both the "password" property and the "password-raw" property are specified, "password" is preferred. Flags indicating how to handle the "password-raw" property. Flags indicating how to handle the "password-raw" property. Specifies authentication flags to use in "phase 1" outer authentication using NMSetting8021xAuthFlags options. The individual TLS versions can be explicitly disabled. If a certain TLS disable flag is not set, it is up to the supplicant to allow or forbid it. The TLS options map to tls_disable_tlsv1_x settings. See the wpa_supplicant documentation for more details. Specifies authentication flags to use in "phase 1" outer authentication using NMSetting8021xAuthFlags options. The individual TLS versions can be explicitly disabled. If a certain TLS disable flag is not set, it is up to the supplicant to allow or forbid it. The TLS options map to tls_disable_tlsv1_x settings. See the wpa_supplicant documentation for more details. Enables or disables in-line provisioning of EAP-FAST credentials when FAST is specified as the EAP method in the "eap" property. Recognized values are "0" (disabled), "1" (allow unauthenticated provisioning), "2" (allow authenticated provisioning), and "3" (allow both authenticated and unauthenticated provisioning).  See the wpa_supplicant documentation for more details. Enables or disables in-line provisioning of EAP-FAST credentials when FAST is specified as the EAP method in the "eap" property. Recognized values are "0" (disabled), "1" (allow unauthenticated provisioning), "2" (allow authenticated provisioning), and "3" (allow both authenticated and unauthenticated provisioning).  See the wpa_supplicant documentation for more details. Forces use of the new PEAP label during key derivation.  Some RADIUS servers may require forcing the new PEAP label to interoperate with PEAPv1.  Set to "1" to force use of the new PEAP label.  See the wpa_supplicant documentation for more details. Forces use of the new PEAP label during key derivation.  Some RADIUS servers may require forcing the new PEAP label to interoperate with PEAPv1.  Set to "1" to force use of the new PEAP label.  See the wpa_supplicant documentation for more details. Forces which PEAP version is used when PEAP is set as the EAP method in the "eap" property.  When unset, the version reported by the server will be used.  Sometimes when using older RADIUS servers, it is necessary to force the client to use a particular PEAP version.  To do so, this property may be set to "0" or "1" to force that specific PEAP version. Forces which PEAP version is used when PEAP is set as the EAP method in the "eap" property.  When unset, the version reported by the server will be used.  Sometimes when using older RADIUS servers, it is necessary to force the client to use a particular PEAP version.  To do so, this property may be set to "0" or "1" to force that specific PEAP version. List of strings to be matched against the altSubjectName of the certificate presented by the authentication server during the inner "phase 2" authentication. If the list is empty, no verification of the server certificate's altSubjectName is performed. List of strings to be matched against the altSubjectName of the certificate presented by the authentication server during the inner "phase 2" authentication. If the list is empty, no verification of the server certificate's altSubjectName is performed. Specifies the allowed "phase 2" inner authentication method when an EAP method that uses an inner TLS tunnel is specified in the "eap" property.  For TTLS this property selects one of the supported non-EAP inner methods: "pap", "chap", "mschap", "mschapv2" while "phase2-autheap" selects an EAP inner method.  For PEAP this selects an inner EAP method, one of: "gtc", "otp", "md5" and "tls". Each "phase 2" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details. Both "phase2-auth" and "phase2-autheap" cannot be specified. Specifies the allowed "phase 2" inner authentication method when an EAP method that uses an inner TLS tunnel is specified in the "eap" property.  For TTLS this property selects one of the supported non-EAP inner methods: "pap", "chap", "mschap", "mschapv2" while "phase2-autheap" selects an EAP inner method.  For PEAP this selects an inner EAP method, one of: "gtc", "otp", "md5" and "tls". Each "phase 2" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details. Both "phase2-auth" and "phase2-autheap" cannot be specified. Specifies the allowed "phase 2" inner EAP-based authentication method when TTLS is specified in the "eap" property.  Recognized EAP-based "phase 2" methods are "md5", "mschapv2", "otp", "gtc", and "tls". Each "phase 2" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details. Specifies the allowed "phase 2" inner EAP-based authentication method when TTLS is specified in the "eap" property.  Recognized EAP-based "phase 2" methods are "md5", "mschapv2", "otp", "gtc", and "tls". Each "phase 2" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details. Contains the "phase 2" CA certificate if used by the EAP method specified in the "phase2-auth" or "phase2-autheap" properties. Certificate data is specified using a "scheme"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory. Contains the "phase 2" CA certificate if used by the EAP method specified in the "phase2-auth" or "phase2-autheap" properties.
-
- Certificate data is specified using a "scheme"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended.
-
- Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory. The password used to access the "phase2" CA certificate stored in "phase2-ca-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. The password used to access the "phase2" CA certificate stored in "phase2-ca-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. Flags indicating how to handle the "phase2-ca-cert-password" property. Flags indicating how to handle the "phase2-ca-cert-password" property. UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the "phase2-ca-cert" property. If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored. UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the "phase2-ca-cert" property.
-
- If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored. Contains the "phase 2" client certificate if used by the EAP method specified in the "phase2-auth" or "phase2-autheap" properties. Certificate data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Contains the "phase 2" client certificate if used by the EAP method specified in the "phase2-auth" or "phase2-autheap" properties.
-
- Certificate data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string "file://" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. The password used to access the "phase2" client certificate stored in "phase2-client-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. The password used to access the "phase2" client certificate stored in "phase2-client-cert" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login. Flags indicating how to handle the "phase2-client-cert-password" property. Flags indicating how to handle the "phase2-client-cert-password" property. Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner "phase 2" authentication. If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a ";" delimited list. Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner "phase 2" authentication. If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a ";" delimited list. Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner "phase 2" authentication.  If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a ";" delimited list. Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner "phase 2" authentication.  If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a ";" delimited list. Contains the "phase 2" inner private key when the "phase2-auth" or "phase2-autheap" property is set to "tls". Key data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the "phase2-private-key-password" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte, and as with the blob scheme the "phase2-private-key-password" property must be set to the password used to decode the PKCS#12 private key and certificate. Contains the "phase 2" inner private key when the "phase2-auth" or "phase2-autheap" property is set to "tls".
-
- Key data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the "phase2-private-key-password" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte, and as with the blob scheme the "phase2-private-key-password" property must be set to the password used to decode the PKCS#12 private key and certificate. The password used to decrypt the "phase 2" private key specified in the "phase2-private-key" property when the private key either uses the path scheme, or is a PKCS#12 format key. The password used to decrypt the "phase 2" private key specified in the "phase2-private-key" property when the private key either uses the path scheme, or is a PKCS#12 format key. Flags indicating how to handle the "phase2-private-key-password" property. Flags indicating how to handle the "phase2-private-key-password" property. Substring to be matched against the subject of the certificate presented by the authentication server during the inner "phase 2" authentication. When unset, no verification of the authentication server certificate's subject is performed.  This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:phase2-domain-suffix-match. Substring to be matched against the subject of the certificate presented by the authentication server during the inner "phase 2" authentication. When unset, no verification of the authentication server certificate's subject is performed.  This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:phase2-domain-suffix-match. PIN used for EAP authentication methods. PIN used for EAP authentication methods. Flags indicating how to handle the "pin" property. Flags indicating how to handle the "pin" property. Contains the private key when the "eap" property is set to "tls". Key data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the "private-key-password" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte, and as with the blob scheme the "private-key-password" property must be set to the password used to decode the PKCS#12 private key and certificate. WARNING: "private-key" is not a "secret" property, and thus unencrypted private key data using the BLOB scheme may be readable by unprivileged users.  Private keys should always be encrypted with a private key password to prevent unauthorized access to unencrypted private key data. Contains the private key when the "eap" property is set to "tls".
-
- Key data is specified using a "scheme"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the "private-key-password" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string "file://" and ending with a terminating NUL byte, and as with the blob scheme the "private-key-password" property must be set to the password used to decode the PKCS#12 private key and certificate.
-
- WARNING: "private-key" is not a "secret" property, and thus unencrypted private key data using the BLOB scheme may be readable by unprivileged users.  Private keys should always be encrypted with a private key password to prevent unauthorized access to unencrypted private key data. The password used to decrypt the private key specified in the "private-key" property when the private key either uses the path scheme, or if the private key is a PKCS#12 format key. The password used to decrypt the private key specified in the "private-key" property when the private key either uses the path scheme, or if the private key is a PKCS#12 format key. Flags indicating how to handle the "private-key-password" property. Flags indicating how to handle the "private-key-password" property. Substring to be matched against the subject of the certificate presented by the authentication server. When unset, no verification of the authentication server certificate's subject is performed.  This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:domain-suffix-match. Substring to be matched against the subject of the certificate presented by the authentication server. When unset, no verification of the authentication server certificate's subject is performed.  This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:domain-suffix-match. When TRUE, overrides the "ca-path" and "phase2-ca-path" properties using the system CA directory specified at configure time with the --system-ca-path switch.  The certificates in this directory are added to the verification chain in addition to any certificates specified by the "ca-cert" and "phase2-ca-cert" properties. If the path provided with --system-ca-path is rather a file name (bundle of trusted CA certificates), it overrides "ca-cert" and "phase2-ca-cert" properties instead (sets ca_cert/ca_cert2 options for wpa_supplicant). When TRUE, overrides the "ca-path" and "phase2-ca-path" properties using the system CA directory specified at configure time with the --system-ca-path switch.  The certificates in this directory are added to the verification chain in addition to any certificates specified by the "ca-cert" and "phase2-ca-cert" properties. If the path provided with --system-ca-path is rather a file name (bundle of trusted CA certificates), it overrides "ca-cert" and "phase2-ca-cert" properties instead (sets ca_cert/ca_cert2 options for wpa_supplicant). Encapsulation of ADSL connection.  Can be "vcmux" or "llc". Encapsulation of ADSL connection.  Can be "vcmux" or "llc". Password used to authenticate with the ADSL service. Password used to authenticate with the ADSL service. Flags indicating how to handle the "password" property. Flags indicating how to handle the "password" property. ADSL connection protocol.  Can be "pppoa", "pppoe" or "ipoatm". ADSL connection protocol.  Can be "pppoa", "pppoe" or "ipoatm". Username used to authenticate with the ADSL service. Username used to authenticate with the ADSL service. VCI of ADSL connection VCI of ADSL connection VPI of ADSL connection VPI of ADSL connection The Bluetooth address of the device. The Bluetooth address of the device. Either "dun" for Dial-Up Networking connections or "panu" for Personal Area Networking connections to devices supporting the NAP profile. Either "dun" for Dial-Up Networking connections or "panu" for Personal Area Networking connections to devices supporting the NAP profile. Dictionary of key/value pairs of bonding options.  Both keys and values must be strings. Option names must contain only alphanumeric characters (ie, [a-zA-Z0-9]). Dictionary of key/value pairs of bonding options.  Both keys and values must be strings. Option names must contain only alphanumeric characters (ie, [a-zA-Z0-9]). The Ethernet MAC address aging time, in seconds. The Ethernet MAC address aging time, in seconds. The Spanning Tree Protocol (STP) forwarding delay, in seconds. The Spanning Tree Protocol (STP) forwarding delay, in seconds. If specified, The MAC address of the multicast group this bridge uses for STP. The address must be a link-local address in standard Ethernet MAC address format, ie an address of the form 01:80:C2:00:00:0X, with X in [0, 4..F]. If not specified the default value is 01:80:C2:00:00:00. If specified, The MAC address of the multicast group this bridge uses for STP.
-
- The address must be a link-local address in standard Ethernet MAC address format, ie an address of the form 01:80:C2:00:00:0X, with X in [0, 4..F]. If not specified the default value is 01:80:C2:00:00:00. A mask of group addresses to forward. Usually, group addresses in the range from 01:80:C2:00:00:00 to 01:80:C2:00:00:0F are not forwarded according to standards. This property is a mask of 16 bits, each corresponding to a group address in that range that must be forwarded. The mask can't have bits 0, 1 or 2 set because they are used for STP, MAC pause frames and LACP. A mask of group addresses to forward. Usually, group addresses in the range from 01:80:C2:00:00:00 to 01:80:C2:00:00:0F are not forwarded according to standards. This property is a mask of 16 bits, each corresponding to a group address in that range that must be forwarded. The mask can't have bits 0, 1 or 2 set because they are used for STP, MAC pause frames and LACP. The Spanning Tree Protocol (STP) hello time, in seconds. The Spanning Tree Protocol (STP) hello time, in seconds. If specified, the MAC address of bridge. When creating a new bridge, this MAC address will be set. If this field is left unspecified, the "ethernet.cloned-mac-address" is referred instead to generate the initial MAC address. Note that setting "ethernet.cloned-mac-address" anyway overwrites the MAC address of the bridge later while activating the bridge. Hence, this property is deprecated. Deprecated: 1 If specified, the MAC address of bridge. When creating a new bridge, this MAC address will be set.
-
- If this field is left unspecified, the "ethernet.cloned-mac-address" is referred instead to generate the initial MAC address. Note that setting "ethernet.cloned-mac-address" anyway overwrites the MAC address of the bridge later while activating the bridge. Hence, this property is deprecated. Deprecated: 1 The Spanning Tree Protocol (STP) maximum message age, in seconds. The Spanning Tree Protocol (STP) maximum message age, in seconds. Set maximum size of multicast hash table (value must be a power of 2). Set maximum size of multicast hash table (value must be a power of 2). Set the number of queries the bridge will send before stopping forwarding a multicast group after a "leave" message has been received. Set the number of queries the bridge will send before stopping forwarding a multicast group after a "leave" message has been received. Set interval (in deciseconds) between queries to find remaining members of a group, after a "leave" message is received. Set interval (in deciseconds) between queries to find remaining members of a group, after a "leave" message is received. Set delay (in deciseconds) after which the bridge will leave a group, if no membership reports for this group are received. Set delay (in deciseconds) after which the bridge will leave a group, if no membership reports for this group are received. Enable or disable sending of multicast queries by the bridge. If not specified the option is disabled. Enable or disable sending of multicast queries by the bridge. If not specified the option is disabled. If no queries are seen after this delay (in deciseconds) has passed, the bridge will start to send its own queries. If no queries are seen after this delay (in deciseconds) has passed, the bridge will start to send its own queries. Interval (in deciseconds) between queries sent by the bridge after the end of the startup phase. Interval (in deciseconds) between queries sent by the bridge after the end of the startup phase. Set the Max Response Time/Max Response Delay (in deciseconds) for IGMP/MLD queries sent by the bridge. Set the Max Response Time/Max Response Delay (in deciseconds) for IGMP/MLD queries sent by the bridge. If enabled the bridge's own IP address is used as the source address for IGMP queries otherwise the default of 0.0.0.0 is used. If enabled the bridge's own IP address is used as the source address for IGMP queries otherwise the default of 0.0.0.0 is used. Sets bridge's multicast router. Multicast-snooping must be enabled for this option to work. Supported values are: 'auto', 'disabled', 'enabled' to which kernel assigns the numbers 1, 0, and 2, respectively. If not specified the default value is 'auto' (1). Sets bridge's multicast router. Multicast-snooping must be enabled for this option to work.
-
- Supported values are: 'auto', 'disabled', 'enabled' to which kernel assigns the numbers 1, 0, and 2, respectively. If not specified the default value is 'auto' (1). Controls whether IGMP snooping is enabled for this bridge. Note that if snooping was automatically disabled due to hash collisions, the system may refuse to enable the feature until the collisions are resolved. Controls whether IGMP snooping is enabled for this bridge. Note that if snooping was automatically disabled due to hash collisions, the system may refuse to enable the feature until the collisions are resolved. Set the number of IGMP queries to send during startup phase. Set the number of IGMP queries to send during startup phase. Sets the time (in deciseconds) between queries sent out at startup to determine membership information. Sets the time (in deciseconds) between queries sent out at startup to determine membership information. Sets the Spanning Tree Protocol (STP) priority for this bridge.  Lower values are "better"; the lowest priority bridge will be elected the root bridge. Sets the Spanning Tree Protocol (STP) priority for this bridge.  Lower values are "better"; the lowest priority bridge will be elected the root bridge. Controls whether Spanning Tree Protocol (STP) is enabled for this bridge. Controls whether Spanning Tree Protocol (STP) is enabled for this bridge. The default PVID for the ports of the bridge, that is the VLAN id assigned to incoming untagged frames. The default PVID for the ports of the bridge, that is the VLAN id assigned to incoming untagged frames. Control whether VLAN filtering is enabled on the bridge. Control whether VLAN filtering is enabled on the bridge. If specified, the protocol used for VLAN filtering. Supported values are: '802.1Q', '802.1ad'. If not specified the default value is '802.1Q'. If specified, the protocol used for VLAN filtering.
-
- Supported values are: '802.1Q', '802.1ad'. If not specified the default value is '802.1Q'. Controls whether per-VLAN stats accounting is enabled. Controls whether per-VLAN stats accounting is enabled. Array of bridge VLAN objects. In addition to the VLANs specified here, the bridge will also have the default-pvid VLAN configured  by the bridge.vlan-default-pvid property. In nmcli the VLAN list can be specified with the following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash. Array of bridge VLAN objects. In addition to the VLANs specified here, the bridge will also have the default-pvid VLAN configured  by the bridge.vlan-default-pvid property.
-
- In nmcli the VLAN list can be specified with the following syntax:
-
- $vid [pvid] [untagged] [, $vid [pvid] [untagged]]...
-
- where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash. Enables or disables "hairpin mode" for the port, which allows frames to be sent back out through the port the frame was received on. Enables or disables "hairpin mode" for the port, which allows frames to be sent back out through the port the frame was received on. The Spanning Tree Protocol (STP) port cost for destinations via this port. The Spanning Tree Protocol (STP) port cost for destinations via this port. The Spanning Tree Protocol (STP) priority of this bridge port. The Spanning Tree Protocol (STP) priority of this bridge port. Array of bridge VLAN objects. In addition to the VLANs specified here, the port will also have the default-pvid VLAN configured on the bridge by the bridge.vlan-default-pvid property. In nmcli the VLAN list can be specified with the following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash. Array of bridge VLAN objects. In addition to the VLANs specified here, the port will also have the default-pvid VLAN configured on the bridge by the bridge.vlan-default-pvid property.
-
- In nmcli the VLAN list can be specified with the following syntax:
-
- $vid [pvid] [untagged] [, $vid [pvid] [untagged]]...
-
- where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames. The number to dial to establish the connection to the CDMA-based mobile broadband network, if any.  If not specified, the default number (#777) is used when required. The number to dial to establish the connection to the CDMA-based mobile broadband network, if any.  If not specified, the default number (#777) is used when required. The password used to authenticate with the network, if required.  Many providers do not require a password, or accept any password.  But if a password is required, it is specified here. The password used to authenticate with the network, if required.  Many providers do not require a password, or accept any password.  But if a password is required, it is specified here. Flags indicating how to handle the "password" property. Flags indicating how to handle the "password" property. The username used to authenticate with the network, if required.  Many providers do not require a username, or accept any username.  But if a username is required, it is specified here. The username used to authenticate with the network, if required.  Many providers do not require a username, or accept any username.  But if a username is required, it is specified here. Specifies the NMSettingDcbFlags for the DCB FCoE application.  Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). Specifies the NMSettingDcbFlags for the DCB FCoE application.  Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). The FCoE controller mode; either "fabric" or "vn2vn". Since 1.34, NULL is the default and means "fabric". Before 1.34, NULL was rejected as invalid and the default was "fabric". The FCoE controller mode; either "fabric" or "vn2vn".
-
- Since 1.34, NULL is the default and means "fabric". Before 1.34, NULL was rejected as invalid and the default was "fabric". The highest User Priority (0 - 7) which FCoE frames should use, or -1 for default priority.  Only used when the "app-fcoe-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag. The highest User Priority (0 - 7) which FCoE frames should use, or -1 for default priority.  Only used when the "app-fcoe-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag. Specifies the NMSettingDcbFlags for the DCB FIP application.  Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). Specifies the NMSettingDcbFlags for the DCB FIP application.  Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). The highest User Priority (0 - 7) which FIP frames should use, or -1 for default priority.  Only used when the "app-fip-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag. The highest User Priority (0 - 7) which FIP frames should use, or -1 for default priority.  Only used when the "app-fip-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag. Specifies the NMSettingDcbFlags for the DCB iSCSI application.  Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). Specifies the NMSettingDcbFlags for the DCB iSCSI application.  Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). The highest User Priority (0 - 7) which iSCSI frames should use, or -1 for default priority. Only used when the "app-iscsi-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag. The highest User Priority (0 - 7) which iSCSI frames should use, or -1 for default priority. Only used when the "app-iscsi-flags" property includes the NM_SETTING_DCB_FLAG_ENABLE (0x1) flag. An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the percentage of bandwidth of the priority's assigned group that the priority may use.  The sum of all percentages for priorities which belong to the same group must total 100 percents. An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the percentage of bandwidth of the priority's assigned group that the priority may use.  The sum of all percentages for priorities which belong to the same group must total 100 percents. An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the corresponding priority should transmit priority pause. An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the corresponding priority should transmit priority pause. Specifies the NMSettingDcbFlags for DCB Priority Flow Control (PFC). Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). Specifies the NMSettingDcbFlags for DCB Priority Flow Control (PFC). Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). An array of 8 uint values, where the array index corresponds to the Priority Group ID (0 - 7) and the value indicates the percentage of link bandwidth allocated to that group.  Allowed values are 0 - 100, and the sum of all values must total 100 percents. An array of 8 uint values, where the array index corresponds to the Priority Group ID (0 - 7) and the value indicates the percentage of link bandwidth allocated to that group.  Allowed values are 0 - 100, and the sum of all values must total 100 percents. Specifies the NMSettingDcbFlags for DCB Priority Groups.  Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). Specifies the NMSettingDcbFlags for DCB Priority Groups.  Flags may be any combination of NM_SETTING_DCB_FLAG_ENABLE (0x1), NM_SETTING_DCB_FLAG_ADVERTISE (0x2), and NM_SETTING_DCB_FLAG_WILLING (0x4). An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the Priority Group ID.  Allowed Priority Group ID values are 0 - 7 or 15 for the unrestricted group. An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the Priority Group ID.  Allowed Priority Group ID values are 0 - 7 or 15 for the unrestricted group. An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the priority may use all of the bandwidth allocated to its assigned group. An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the priority may use all of the bandwidth allocated to its assigned group. An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the traffic class (0 - 7) to which the priority is mapped. An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the traffic class (0 - 7) to which the priority is mapped. The GPRS Access Point Name specifying the APN used when establishing a data session with the GSM-based network.  The APN often determines how the user will be billed for their network usage and whether the user has access to the Internet or just a provider-specific walled-garden, so it is important to use the correct APN for the user's mobile broadband plan. The APN may only be composed of the characters a-z, 0-9, ., and - per GSM 03.60 Section 14.9. The GPRS Access Point Name specifying the APN used when establishing a data session with the GSM-based network.  The APN often determines how the user will be billed for their network usage and whether the user has access to the Internet or just a provider-specific walled-garden, so it is important to use the correct APN for the user's mobile broadband plan. The APN may only be composed of the characters a-z, 0-9, ., and - per GSM 03.60 Section 14.9. When TRUE, the settings such as APN, username, or password will default to values that match the network the modem will register to in the Mobile Broadband Provider database. When TRUE, the settings such as APN, username, or password will default to values that match the network the modem will register to in the Mobile Broadband Provider database. The device unique identifier (as given by the WWAN management service) which this connection applies to.  If given, the connection will only apply to the specified device. The device unique identifier (as given by the WWAN management service) which this connection applies to.  If given, the connection will only apply to the specified device. When TRUE, only connections to the home network will be allowed. Connections to roaming networks will not be made. When TRUE, only connections to the home network will be allowed. Connections to roaming networks will not be made. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames. The Network ID (GSM LAI format, ie MCC-MNC) to force specific network registration.  If the Network ID is specified, NetworkManager will attempt to force the device to register only on the specified network. This can be used to ensure that the device does not roam when direct roaming control of the device is not otherwise possible. The Network ID (GSM LAI format, ie MCC-MNC) to force specific network registration.  If the Network ID is specified, NetworkManager will attempt to force the device to register only on the specified network. This can be used to ensure that the device does not roam when direct roaming control of the device is not otherwise possible. Legacy setting that used to help establishing PPP data sessions for GSM-based modems. Deprecated: 1 Legacy setting that used to help establishing PPP data sessions for GSM-based modems. Deprecated: 1 The password used to authenticate with the network, if required.  Many providers do not require a password, or accept any password.  But if a password is required, it is specified here. The password used to authenticate with the network, if required.  Many providers do not require a password, or accept any password.  But if a password is required, it is specified here. Flags indicating how to handle the "password" property. Flags indicating how to handle the "password" property. If the SIM is locked with a PIN it must be unlocked before any other operations are requested.  Specify the PIN here to allow operation of the device. If the SIM is locked with a PIN it must be unlocked before any other operations are requested.  Specify the PIN here to allow operation of the device. Flags indicating how to handle the "pin" property. Flags indicating how to handle the "pin" property. The SIM card unique identifier (as given by the WWAN management service) which this connection applies to.  If given, the connection will apply to any device also allowed by "device-id" which contains a SIM card matching the given identifier. The SIM card unique identifier (as given by the WWAN management service) which this connection applies to.  If given, the connection will apply to any device also allowed by "device-id" which contains a SIM card matching the given identifier. A MCC/MNC string like "310260" or "21601" identifying the specific mobile network operator which this connection applies to.  If given, the connection will apply to any device also allowed by "device-id" and "sim-id" which contains a SIM card provisioned by the given operator. A MCC/MNC string like "310260" or "21601" identifying the specific mobile network operator which this connection applies to.  If given, the connection will apply to any device also allowed by "device-id" and "sim-id" which contains a SIM card provisioned by the given operator. The username used to authenticate with the network, if required.  Many providers do not require a username, or accept any username.  But if a username is required, it is specified here. The username used to authenticate with the network, if required.  Many providers do not require a username, or accept any username.  But if a username is required, it is specified here. If specified, this connection will only apply to the IPoIB device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing). If specified, this connection will only apply to the IPoIB device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing). If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames. The InfiniBand P_Key to use for this device. A value of -1 means to use the default P_Key (aka "the P_Key at index 0"). Otherwise, it is a 16-bit unsigned integer, whose high bit is set if it is a "full membership" P_Key. The InfiniBand P_Key to use for this device. A value of -1 means to use the default P_Key (aka "the P_Key at index 0"). Otherwise, it is a 16-bit unsigned integer, whose high bit is set if it is a "full membership" P_Key. The interface name of the parent device of this device. Normally NULL, but if the "p_key" property is set, then you must specify the base device by setting either this property or "mac-address". The interface name of the parent device of this device. Normally NULL, but if the "p_key" property is set, then you must specify the base device by setting either this property or "mac-address". The IP-over-InfiniBand transport mode. Either "datagram" or "connected". The IP-over-InfiniBand transport mode. Either "datagram" or "connected". Array of IP addresses.A list of IPv4 addresses and their prefix length. Multiple addresses can be separated by comma. For example "192.168.1.5/24, 10.1.0.5/24". The addresses are listed in decreasing priority, meaning the first address will be the primary address. Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network.  If an address conflict is detected, the activation will fail.  A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero).  A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4. Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network.  If an address conflict is detected, the activation will fail.  A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero).  A value greater than zero is a timeout in milliseconds.
-
- The property is currently implemented only for IPv4. A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string ('aa:bb:cc') it is interpreted as a binary client ID, in which case the first byte is assumed to be the 'type' field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the 'type' field is set to 0. The special values "mac" and "perm-mac" are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. The special value "ipv6-duid" uses the DUID from "ipv6.dhcp-duid" property as an RFC4361-compliant client identifier. As IAID it uses "ipv4.dhcp-iaid" and falls back to "ipv6.dhcp-iaid" if unset. The special value "duid" generates a RFC4361-compliant client identifier based on "ipv4.dhcp-iaid" and uses a DUID generated by hashing /etc/machine-id. The special value "stable" is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the "${DEVICE}" or "${MAC}" specifier to get a per-device key. If unset, a globally configured default is used. If still unset, the default depends on the DHCP plugin. A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string ('aa:bb:cc') it is interpreted as a binary client ID, in which case the first byte is assumed to be the 'type' field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the 'type' field is set to 0.
-
- The special values "mac" and "perm-mac" are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links.
-
- The special value "ipv6-duid" uses the DUID from "ipv6.dhcp-duid" property as an RFC4361-compliant client identifier. As IAID it uses "ipv4.dhcp-iaid" and falls back to "ipv6.dhcp-iaid" if unset.
-
- The special value "duid" generates a RFC4361-compliant client identifier based on "ipv4.dhcp-iaid" and uses a DUID generated by hashing /etc/machine-id.
-
- The special value "stable" is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the "${DEVICE}" or "${MAC}" specifier to get a per-device key.
-
- If unset, a globally configured default is used. If still unset, the default depends on the DHCP plugin. If the "dhcp-send-hostname" property is TRUE, then the specified FQDN will be sent to the DHCP server when acquiring a lease. This property and "dhcp-hostname" are mutually exclusive and cannot be set at the same time. If the "dhcp-send-hostname" property is TRUE, then the specified FQDN will be sent to the DHCP server when acquiring a lease. This property and "dhcp-hostname" are mutually exclusive and cannot be set at the same time. If the "dhcp-send-hostname" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and "dhcp-fqdn" are mutually exclusive and cannot be set at the same time. If the "dhcp-send-hostname" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and "dhcp-fqdn" are mutually exclusive and cannot be set at the same time. Flags for the DHCP hostname and FQDN. Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4).  When no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the standard FQDN flags are set in the request: NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6. When this property is set to the default value NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags described above are sent in the DHCP requests. Flags for the DHCP hostname and FQDN.
-
- Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4).  When no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the standard FQDN flags are set in the request: NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6.
-
- When this property is set to the default value NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags described above are sent in the DHCP requests. A string containing the "Identity Association Identifier" (IAID) used by the DHCP client. The property is a 32-bit decimal value or a special value among "mac", "perm-mac", "ifname" and "stable". When set to "mac" (or "perm-mac"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to "ifname", the IAID is computed by hashing the interface name. The special value "stable" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be "ifname". Note that at the moment this property is ignored for IPv6 by dhclient, which always derives the IAID from the MAC address. A string containing the "Identity Association Identifier" (IAID) used by the DHCP client. The property is a 32-bit decimal value or a special value among "mac", "perm-mac", "ifname" and "stable". When set to "mac" (or "perm-mac"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to "ifname", the IAID is computed by hashing the interface name. The special value "stable" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be "ifname". Note that at the moment this property is ignored for IPv6 by dhclient, which always derives the IAID from the MAC address. Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. "192.168.122.0/24"). This property is currently not implemented for DHCPv6. Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers.
-
- For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. "192.168.122.0/24").
-
- This property is currently not implemented for DHCPv6. If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer.  If the "dhcp-hostname" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent. If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer.  If the "dhcp-hostname" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent. A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity. A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds).
-
- Set to 2147483647 (MAXINT32) for infinity. The Vendor Class Identifier DHCP option (60). Special characters in the data string may be escaped using C-style escapes, nevertheless this property cannot contain nul bytes. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the DHCP option is not sent to the server. The Vendor Class Identifier DHCP option (60). Special characters in the data string may be escaped using C-style escapes, nevertheless this property cannot contain nul bytes. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the DHCP option is not sent to the server. Array of IP addresses of DNS servers. Array of IP addresses of DNS servers. Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties. The currently supported options are "attempts", "debug", "edns0", "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names", "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single-request", "single-request-reopen", "timeout", "trust-ad", "use-vc". The "trust-ad" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have "trust-ad" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then "edns0" and "trust-ad" are automatically added. Array of DNS options as described in man 5 resolv.conf.
-
- NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties.
-
- The currently supported options are "attempts", "debug", "edns0", "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names", "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single-request", "single-request-reopen", "timeout", "trust-ad", "use-vc".
-
- The "trust-ad" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have "trust-ad" enabled.
-
- When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then "edns0" and "trust-ad" are automatically added. DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the "rotate" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins.  If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured. DNS servers priority.
-
- The relative priority for DNS servers specified by this setting.  A lower numerical value is better (higher priority).
-
- Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles.
-
- Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections.
-
- Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile.
-
- When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices.
-
- When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the "rotate" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered.
-
- When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins.  If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured. List of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting. When set on a profile that also enabled DHCP, the DNS search list received automatically (option 119 for DHCPv4 and option 24 for DHCPv6) gets merged with the manual list. This can be prevented by setting "ignore-auto-dns". Note that if no DNS searches are configured, the fallback will be derived from the domain from DHCP (option 15). List of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names.
-
- When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting.
-
- When set on a profile that also enabled DHCP, the DNS search list received automatically (option 119 for DHCPv4 and option 24 for DHCPv6) gets merged with the manual list. This can be prevented by setting "ignore-auto-dns". Note that if no DNS searches are configured, the fallback will be derived from the domain from DHCP (option 15). The gateway associated with this configuration. This is only meaningful if "addresses" is also set. Setting the gateway causes NetworkManager to configure a standard default route with the gateway as next hop. This is ignored if "never-default" is set. An alternative is to configure the default route explicitly with a manual route and /0 as prefix length. Note that the gateway usually conflicts with routing that NetworkManager configures for WireGuard interfaces, so usually it should not be set in that case. See "ip4-auto-default-route". The gateway associated with this configuration. This is only meaningful if "addresses" is also set.
-
- Setting the gateway causes NetworkManager to configure a standard default route with the gateway as next hop. This is ignored if "never-default" is set. An alternative is to configure the default route explicitly with a manual route and /0 as prefix length.
-
- Note that the gateway usually conflicts with routing that NetworkManager configures for WireGuard interfaces, so usually it should not be set in that case. See "ip4-auto-default-route". When "method" is set to "auto" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the "dns" and "dns-search" properties, if any, are used. When "method" is set to "auto" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the "dns" and "dns-search" properties, if any, are used. When "method" is set to "auto" and this property to TRUE, automatically configured routes are ignored and only routes specified in the "routes" property, if any, are used. When "method" is set to "auto" and this property to TRUE, automatically configured routes are ignored and only routes specified in the "routes" property, if any, are used. Enable and disable the IPv4 link-local configuration independently of the ipv4.method configuration. This allows a link-local address (169.254.x.y/16) to be obtained in addition to other addresses, such as those manually configured or obtained from a DHCP server. When set to "auto", the value is dependent on "ipv4.method". When set to "default", it honors the global connection default, before falling back to "auto". Note that if "ipv4.method" is "disabled", then link local addressing is always disabled too. The default is "default". Enable and disable the IPv4 link-local configuration independently of the ipv4.method configuration. This allows a link-local address (169.254.x.y/16) to be obtained in addition to other addresses, such as those manually configured or obtained from a DHCP server.
-
- When set to "auto", the value is dependent on "ipv4.method". When set to "default", it honors the global connection default, before falling back to "auto". Note that if "ipv4.method" is "disabled", then link local addressing is always disabled too. The default is "default". If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out.  Note that at least one IP configuration must succeed or overall network configuration will still fail.  For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully. If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out.  Note that at least one IP configuration must succeed or overall network configuration will still fail.  For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully. IP configuration method. NMSettingIP4Config and NMSettingIP6Config both support "disabled", "auto", "manual", and "link-local". See the subclass-specific documentation for other values. In general, for the "auto" method, properties such as "dns" and "routes" specify information that is added on to the information returned from automatic configuration.  The "ignore-auto-routes" and "ignore-auto-dns" properties modify this behavior. For methods that imply no upstream network, such as "shared" or "link-local", these properties must be empty. For IPv4 method "shared", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared. IP configuration method.
-
- NMSettingIP4Config and NMSettingIP6Config both support "disabled", "auto", "manual", and "link-local". See the subclass-specific documentation for other values.
-
- In general, for the "auto" method, properties such as "dns" and "routes" specify information that is added on to the information returned from automatic configuration.  The "ignore-auto-routes" and "ignore-auto-dns" properties modify this behavior.
-
- For methods that imply no upstream network, such as "shared" or "link-local", these properties must be empty.
-
- For IPv4 method "shared", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared. If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager. If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager. The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. Note that if "may-fail" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero). The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds.
-
- This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active.
-
- Note that if "may-fail" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout.
-
- A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero). The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric. The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric. Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager. Enable policy routing (source routing) and set the routing table used when adding routes.
-
- This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table.
-
- If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection.
-
- Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager.
-
-A list of IPv4 destination addresses, prefix length, optional IPv4
-next hop addresses, optional route metric, optional attribute. The valid syntax is:
-"ip[/prefix] [next-hop] [metric] [attribute=val]...[,ip[/prefix]...]".
-For example "192.0.2.0/24 10.1.1.1 77, 198.51.100.0/24".
-
-
-Various attributes are supported:
-
-
-"advmss" - an unsigned 32 bit integer.
-
-
-"cwnd" - an unsigned 32 bit integer.
-
-
-"initcwnd" - an unsigned 32 bit integer.
-
-
-"initrwnd" - an unsigned 32 bit integer.
-
-
-"lock-advmss" - a boolean value.
-
-
-"lock-cwnd" - a boolean value.
-
-
-"lock-initcwnd" - a boolean value.
-
-
-"lock-initrwnd" - a boolean value.
-
-
-"lock-mtu" - a boolean value.
-
-
-"lock-window" - a boolean value.
-
-
-"mtu" - an unsigned 32 bit integer.
-
-
-"onlink" - a boolean value.
-
-
-"quickack" - a boolean value.
-
-
-"rto_min" - an unsigned 32 bit integer.
-The value is in milliseconds.
-
-
-"scope" - an unsigned 8 bit integer. IPv4 only.
-
-
-"src" - an IPv4 address.
-
-
-"table" - an unsigned 32 bit integer. The default depends on ipv4.route-table.
-
-
-"tos" - an unsigned 8 bit integer. IPv4 only.
-
-
-"type" - one of unicast, local, blackhole,
-unavailable, prohibit, throw.
-The default is unicast.
-
-
-"window" - an unsigned 32 bit integer.
-
-
-
-
-For details see also `man ip-route`.
-A list of IPv4 destination addresses, prefix length, optional IPv4 next hop addresses, optional route metric, optional attribute. The valid syntax is: "ip[/prefix] [next-hop] [metric] [attribute=val]...[,ip[/prefix]...]". For example "192.0.2.0/24 10.1.1.1 77, 198.51.100.0/24".
-
-A comma separated list of routing rules for policy routing. The format
-is based on ip rule add syntax and mostly compatible.
-One difference is that routing rules in NetworkManager always need a
-fixed priority.
-
-
-Example: priority 5 from 192.167.4.0/24 table 45
-A comma separated list of routing rules for policy routing. Configure method for creating the address for use with RFC4862 IPv6 Stateless Address Autoconfiguration. The permitted values are: NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 (0), NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY (1). NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT_OR_EUI64 (2) or NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT (3). If the property is set to EUI64, the addresses will be generated using the interface tokens derived from hardware address. This makes the host part of the address to stay constant, making it possible to track host's presence when it changes networks. The address changes when the interface hardware is replaced. The value of stable-privacy enables use of cryptographically secure hash of a secret host-specific key along with the connection's stable-id and the network address as specified by RFC7217. This makes it impossible to use the address track host's presence, and makes the address stable when the network interface hardware is replaced. The special values "default" and "default-or-eui64" will fallback to the global connection default in as documented in NetworkManager.conf(5) manual. If the global default is not specified, the fallback value is "stable-privacy" or "eui64", respectively. For libnm, the property defaults to "default" since 1.40. Previously it defaulted to "stable-privacy". On D-Bus, the absence of an addr-gen-mode setting equals "default". For keyfile plugin, the absence of the setting on disk means "default-or-eui64" so that the property doesn't change on upgrade from older versions. Note that this setting is distinct from the Privacy Extensions as configured by "ip6-privacy" property and it does not affect the temporary addresses configured with this option. Configure method for creating the address for use with RFC4862 IPv6 Stateless Address Autoconfiguration. The permitted values are: NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 (0), NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY (1). NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT_OR_EUI64 (2) or NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT (3).
-
- If the property is set to EUI64, the addresses will be generated using the interface tokens derived from hardware address. This makes the host part of the address to stay constant, making it possible to track host's presence when it changes networks. The address changes when the interface hardware is replaced.
-
- The value of stable-privacy enables use of cryptographically secure hash of a secret host-specific key along with the connection's stable-id and the network address as specified by RFC7217. This makes it impossible to use the address track host's presence, and makes the address stable when the network interface hardware is replaced.
-
- The special values "default" and "default-or-eui64" will fallback to the global connection default in as documented in NetworkManager.conf(5) manual. If the global default is not specified, the fallback value is "stable-privacy" or "eui64", respectively.
-
- For libnm, the property defaults to "default" since 1.40. Previously it defaulted to "stable-privacy". On D-Bus, the absence of an addr-gen-mode setting equals "default". For keyfile plugin, the absence of the setting on disk means "default-or-eui64" so that the property doesn't change on upgrade from older versions.
-
- Note that this setting is distinct from the Privacy Extensions as configured by "ip6-privacy" property and it does not affect the temporary addresses configured with this option. Array of IP addresses.A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example "2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64". The addresses are listed in decreasing priority, meaning the first address will be the primary address. This can make a difference with IPv6 source address selection (RFC 6724, section 5). Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network.  If an address conflict is detected, the activation will fail.  A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero).  A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4. Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network.  If an address conflict is detected, the activation will fail.  A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero).  A value greater than zero is a timeout in milliseconds.
-
- The property is currently implemented only for IPv4. A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp client to identify itself to DHCPv6 servers (RFC 3315). The DUID is carried in the Client Identifier option. If the property is a hex string ('aa:bb:cc') it is interpreted as a binary DUID and filled as an opaque value in the Client Identifier option. The special value "lease" will retrieve the DUID previously used from the lease file belonging to the connection. If no DUID is found and "dhclient" is the configured dhcp client, the DUID is searched in the system-wide dhclient lease file. If still no DUID is found, or another dhcp client is used, a global and permanent DUID-UUID (RFC 6355) will be generated based on the machine-id. The special values "llt" and "ll" will generate a DUID of type LLT or LL (see RFC 3315) based on the current MAC address of the device. In order to try providing a stable DUID-LLT, the time field will contain a constant timestamp that is used globally (for all profiles) and persisted to disk. The special values "stable-llt", "stable-ll" and "stable-uuid" will generate a DUID of the corresponding type, derived from the connection's stable-id and a per-host unique key. You may want to include the "${DEVICE}" or "${MAC}" specifier in the stable-id, in case this profile gets activated on multiple devices. So, the link-layer address of "stable-ll" and "stable-llt" will be a generated address derived from the stable id. The DUID-LLT time value in the "stable-llt" option will be picked among a static timespan of three years (the upper bound of the interval is the same constant timestamp used in "llt"). When the property is unset, the global value provided for "ipv6.dhcp-duid" is used. If no global value is provided, the default "lease" value is assumed. A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp client to identify itself to DHCPv6 servers (RFC 3315). The DUID is carried in the Client Identifier option. If the property is a hex string ('aa:bb:cc') it is interpreted as a binary DUID and filled as an opaque value in the Client Identifier option.
-
- The special value "lease" will retrieve the DUID previously used from the lease file belonging to the connection. If no DUID is found and "dhclient" is the configured dhcp client, the DUID is searched in the system-wide dhclient lease file. If still no DUID is found, or another dhcp client is used, a global and permanent DUID-UUID (RFC 6355) will be generated based on the machine-id.
-
- The special values "llt" and "ll" will generate a DUID of type LLT or LL (see RFC 3315) based on the current MAC address of the device. In order to try providing a stable DUID-LLT, the time field will contain a constant timestamp that is used globally (for all profiles) and persisted to disk.
-
- The special values "stable-llt", "stable-ll" and "stable-uuid" will generate a DUID of the corresponding type, derived from the connection's stable-id and a per-host unique key. You may want to include the "${DEVICE}" or "${MAC}" specifier in the stable-id, in case this profile gets activated on multiple devices. So, the link-layer address of "stable-ll" and "stable-llt" will be a generated address derived from the stable id. The DUID-LLT time value in the "stable-llt" option will be picked among a static timespan of three years (the upper bound of the interval is the same constant timestamp used in "llt").
-
- When the property is unset, the global value provided for "ipv6.dhcp-duid" is used. If no global value is provided, the default "lease" value is assumed. If the "dhcp-send-hostname" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and "dhcp-fqdn" are mutually exclusive and cannot be set at the same time. If the "dhcp-send-hostname" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and "dhcp-fqdn" are mutually exclusive and cannot be set at the same time. Flags for the DHCP hostname and FQDN. Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4).  When no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the standard FQDN flags are set in the request: NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6. When this property is set to the default value NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags described above are sent in the DHCP requests. Flags for the DHCP hostname and FQDN.
-
- Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) and NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE (0x4).  When no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS (0x8) is not set, the standard FQDN flags are set in the request: NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1), NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED (0x2) for IPv4 and NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE (0x1) for IPv6.
-
- When this property is set to the default value NM_DHCP_HOSTNAME_FLAG_NONE (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also NM_DHCP_HOSTNAME_FLAG_NONE (0x0), then the standard FQDN flags described above are sent in the DHCP requests. A string containing the "Identity Association Identifier" (IAID) used by the DHCP client. The property is a 32-bit decimal value or a special value among "mac", "perm-mac", "ifname" and "stable". When set to "mac" (or "perm-mac"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to "ifname", the IAID is computed by hashing the interface name. The special value "stable" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be "ifname". Note that at the moment this property is ignored for IPv6 by dhclient, which always derives the IAID from the MAC address. A string containing the "Identity Association Identifier" (IAID) used by the DHCP client. The property is a 32-bit decimal value or a special value among "mac", "perm-mac", "ifname" and "stable". When set to "mac" (or "perm-mac"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to "ifname", the IAID is computed by hashing the interface name. The special value "stable" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be "ifname". Note that at the moment this property is ignored for IPv6 by dhclient, which always derives the IAID from the MAC address. Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. "192.168.122.0/24"). This property is currently not implemented for DHCPv6. Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers.
-
- For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. "192.168.122.0/24").
-
- This property is currently not implemented for DHCPv6. If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer.  If the "dhcp-hostname" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent. If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer.  If the "dhcp-hostname" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent. A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity. A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds).
-
- Set to 2147483647 (MAXINT32) for infinity. Array of IP addresses of DNS servers. Array of IP addresses of DNS servers. Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties. The currently supported options are "attempts", "debug", "edns0", "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names", "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single-request", "single-request-reopen", "timeout", "trust-ad", "use-vc". The "trust-ad" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have "trust-ad" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then "edns0" and "trust-ad" are automatically added. Array of DNS options as described in man 5 resolv.conf.
-
- NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties.
-
- The currently supported options are "attempts", "debug", "edns0", "inet6", "ip6-bytestring", "ip6-dotint", "ndots", "no-check-names", "no-ip6-dotint", "no-reload", "no-tld-query", "rotate", "single-request", "single-request-reopen", "timeout", "trust-ad", "use-vc".
-
- The "trust-ad" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have "trust-ad" enabled.
-
- When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then "edns0" and "trust-ad" are automatically added. DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the "rotate" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins.  If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured. DNS servers priority.
-
- The relative priority for DNS servers specified by this setting.  A lower numerical value is better (higher priority).
-
- Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles.
-
- Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections.
-
- Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile.
-
- When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices.
-
- When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the "rotate" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered.
-
- When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins.  If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured. List of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting. When set on a profile that also enabled DHCP, the DNS search list received automatically (option 119 for DHCPv4 and option 24 for DHCPv6) gets merged with the manual list. This can be prevented by setting "ignore-auto-dns". Note that if no DNS searches are configured, the fallback will be derived from the domain from DHCP (option 15). List of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names.
-
- When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting.
-
- When set on a profile that also enabled DHCP, the DNS search list received automatically (option 119 for DHCPv4 and option 24 for DHCPv6) gets merged with the manual list. This can be prevented by setting "ignore-auto-dns". Note that if no DNS searches are configured, the fallback will be derived from the domain from DHCP (option 15). The gateway associated with this configuration. This is only meaningful if "addresses" is also set. Setting the gateway causes NetworkManager to configure a standard default route with the gateway as next hop. This is ignored if "never-default" is set. An alternative is to configure the default route explicitly with a manual route and /0 as prefix length. Note that the gateway usually conflicts with routing that NetworkManager configures for WireGuard interfaces, so usually it should not be set in that case. See "ip4-auto-default-route". The gateway associated with this configuration. This is only meaningful if "addresses" is also set.
-
- Setting the gateway causes NetworkManager to configure a standard default route with the gateway as next hop. This is ignored if "never-default" is set. An alternative is to configure the default route explicitly with a manual route and /0 as prefix length.
-
- Note that the gateway usually conflicts with routing that NetworkManager configures for WireGuard interfaces, so usually it should not be set in that case. See "ip4-auto-default-route". When "method" is set to "auto" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the "dns" and "dns-search" properties, if any, are used. When "method" is set to "auto" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the "dns" and "dns-search" properties, if any, are used. When "method" is set to "auto" and this property to TRUE, automatically configured routes are ignored and only routes specified in the "routes" property, if any, are used. When "method" is set to "auto" and this property to TRUE, automatically configured routes are ignored and only routes specified in the "routes" property, if any, are used. Configure IPv6 Privacy Extensions for SLAAC, described in RFC4941.  If enabled, it makes the kernel generate a temporary IPv6 address in addition to the public one generated from MAC address via modified EUI-64.  This enhances privacy, but could cause problems in some applications, on the other hand.  The permitted values are: -1: unknown, 0: disabled, 1: enabled (prefer public address), 2: enabled (prefer temporary addresses). Having a per-connection setting set to "-1" (unknown) means fallback to global configuration "ipv6.ip6-privacy". If also global configuration is unspecified or set to "-1", fallback to read "/proc/sys/net/ipv6/conf/default/use_tempaddr". Note that this setting is distinct from the Stable Privacy addresses that can be enabled with the "addr-gen-mode" property's "stable-privacy" setting as another way of avoiding host tracking with IPv6 addresses. Configure IPv6 Privacy Extensions for SLAAC, described in RFC4941.  If enabled, it makes the kernel generate a temporary IPv6 address in addition to the public one generated from MAC address via modified EUI-64.  This enhances privacy, but could cause problems in some applications, on the other hand.  The permitted values are: -1: unknown, 0: disabled, 1: enabled (prefer public address), 2: enabled (prefer temporary addresses).
-
- Having a per-connection setting set to "-1" (unknown) means fallback to global configuration "ipv6.ip6-privacy".
-
- If also global configuration is unspecified or set to "-1", fallback to read "/proc/sys/net/ipv6/conf/default/use_tempaddr".
-
- Note that this setting is distinct from the Stable Privacy addresses that can be enabled with the "addr-gen-mode" property's "stable-privacy" setting as another way of avoiding host tracking with IPv6 addresses. If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out.  Note that at least one IP configuration must succeed or overall network configuration will still fail.  For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully. If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out.  Note that at least one IP configuration must succeed or overall network configuration will still fail.  For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully. IP configuration method. NMSettingIP4Config and NMSettingIP6Config both support "disabled", "auto", "manual", and "link-local". See the subclass-specific documentation for other values. In general, for the "auto" method, properties such as "dns" and "routes" specify information that is added on to the information returned from automatic configuration.  The "ignore-auto-routes" and "ignore-auto-dns" properties modify this behavior. For methods that imply no upstream network, such as "shared" or "link-local", these properties must be empty. For IPv4 method "shared", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared. IP configuration method.
-
- NMSettingIP4Config and NMSettingIP6Config both support "disabled", "auto", "manual", and "link-local". See the subclass-specific documentation for other values.
-
- In general, for the "auto" method, properties such as "dns" and "routes" specify information that is added on to the information returned from automatic configuration.  The "ignore-auto-routes" and "ignore-auto-dns" properties modify this behavior.
-
- For methods that imply no upstream network, such as "shared" or "link-local", these properties must be empty.
-
- For IPv4 method "shared", the IP subnet can be configured by adding one manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the shared method must be configured on the interface which shares the internet to a subnet, not on the uplink which is shared. Maximum transmission unit size, in bytes. If zero (the default), the MTU is set automatically from router advertisements or is left equal to the link-layer MTU. If greater than the link-layer MTU, or greater than zero but less than the minimum IPv6 MTU of 1280, this value has no effect. Maximum transmission unit size, in bytes. If zero (the default), the MTU is set automatically from router advertisements or is left equal to the link-layer MTU. If greater than the link-layer MTU, or greater than zero but less than the minimum IPv6 MTU of 1280, this value has no effect. If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager. If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager. A timeout for waiting Router Advertisements in seconds. If zero (the default), a globally configured default is used. If still unspecified, the timeout depends on the sysctl settings of the device. Set to 2147483647 (MAXINT32) for infinity. A timeout for waiting Router Advertisements in seconds. If zero (the default), a globally configured default is used. If still unspecified, the timeout depends on the sysctl settings of the device.
-
- Set to 2147483647 (MAXINT32) for infinity. The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. Note that if "may-fail" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero). The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds.
-
- This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active.
-
- Note that if "may-fail" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout.
-
- A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero). The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric. The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric. Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager. Enable policy routing (source routing) and set the routing table used when adding routes.
-
- This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table.
-
- If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection.
-
- Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager.
-
-A list of IPv6 destination addresses, prefix length, optional IPv6
-next hop addresses, optional route metric, optional attribute. The valid syntax is:
-"ip[/prefix] [next-hop] [metric] [attribute=val]...[,ip[/prefix]...]".
-
-
-Various attributes are supported:
-
-
-"advmss" - an unsigned 32 bit integer.
-
-
-"cwnd" - an unsigned 32 bit integer.
-
-
-"from" - an IPv6 address with optional prefix. IPv6 only.
-
-
-"initcwnd" - an unsigned 32 bit integer.
-
-
-"initrwnd" - an unsigned 32 bit integer.
-
-
-"lock-advmss" - a boolean value.
-
-
-"lock-cwnd" - a boolean value.
-
-
-"lock-initcwnd" - a boolean value.
-
-
-"lock-initrwnd" - a boolean value.
-
-
-"lock-mtu" - a boolean value.
-
-
-"lock-window" - a boolean value.
-
-
-"mtu" - an unsigned 32 bit integer.
-
-
-"onlink" - a boolean value.
-
-
-"quickack" - a boolean value.
-
-
-"rto_min" - an unsigned 32 bit integer.
-The value is in milliseconds.
-
-
-"src" - an IPv6 address.
-
-
-"table" - an unsigned 32 bit integer. The default depends on ipv6.route-table.
-
-
-"type" - one of unicast, local, blackhole,
-unavailable, prohibit, throw.
-The default is unicast.
-
-
-"window" - an unsigned 32 bit integer.
-
-
-
-
-For details see also `man ip-route`.
- Array of IP routes.
-
-A comma separated list of routing rules for policy routing. The format
-is based on ip rule add syntax and mostly compatible.
-One difference is that routing rules in NetworkManager always need a
-fixed priority.
-
-
-Example: priority 5 from 1:2:3::5/128 table 45
-A comma separated list of routing rules for policy routing. Configure the token for draft-chown-6man-tokenised-ipv6-identifiers-02 IPv6 tokenized interface identifiers. Useful with eui64 addr-gen-mode. Configure the token for draft-chown-6man-tokenised-ipv6-identifiers-02 IPv6 tokenized interface identifiers. Useful with eui64 addr-gen-mode. How many additional levels of encapsulation are permitted to be prepended to packets. This property applies only to IPv6 tunnels. How many additional levels of encapsulation are permitted to be prepended to packets. This property applies only to IPv6 tunnels. Tunnel flags. Currently, the following values are supported: NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT (0x1), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS (0x2), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL (0x4), NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV (0x8), NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY (0x10), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK (0x20). They are valid only for IPv6 tunnels. Tunnel flags. Currently, the following values are supported: NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT (0x1), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS (0x2), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL (0x4), NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV (0x8), NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY (0x10), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK (0x20). They are valid only for IPv6 tunnels. The flow label to assign to tunnel packets. This property applies only to IPv6 tunnels. The flow label to assign to tunnel packets. This property applies only to IPv6 tunnels. The key used for tunnel input packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used. The key used for tunnel input packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used. The local endpoint of the tunnel; the value can be empty, otherwise it must contain an IPv4 or IPv6 address. The local endpoint of the tunnel; the value can be empty, otherwise it must contain an IPv4 or IPv6 address. The tunneling mode, for example NM_IP_TUNNEL_MODE_IPIP (1) or NM_IP_TUNNEL_MODE_GRE (2). The tunneling mode, for example NM_IP_TUNNEL_MODE_IPIP (1) or NM_IP_TUNNEL_MODE_GRE (2). If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments. The key used for tunnel output packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used. The key used for tunnel output packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used. If given, specifies the parent interface name or parent connection UUID the new device will be bound to so that tunneled packets will only be routed via that interface. If given, specifies the parent interface name or parent connection UUID the new device will be bound to so that tunneled packets will only be routed via that interface. Whether to enable Path MTU Discovery on this tunnel. Whether to enable Path MTU Discovery on this tunnel. The remote endpoint of the tunnel; the value must contain an IPv4 or IPv6 address. The remote endpoint of the tunnel; the value must contain an IPv4 or IPv6 address. The type of service (IPv4) or traffic class (IPv6) field to be set on tunneled packets. The type of service (IPv4) or traffic class (IPv6) field to be set on tunneled packets. The TTL to assign to tunneled packets. 0 is a special value meaning that packets inherit the TTL value. The TTL to assign to tunneled packets. 0 is a special value meaning that packets inherit the TTL value. Whether the transmitted traffic must be encrypted. Whether the transmitted traffic must be encrypted. The pre-shared CAK (Connectivity Association Key) for MACsec Key Agreement. Must be a string of 32 hexadecimal characters. The pre-shared CAK (Connectivity Association Key) for MACsec Key Agreement. Must be a string of 32 hexadecimal characters. Flags indicating how to handle the "mka-cak" property. Flags indicating how to handle the "mka-cak" property. The pre-shared CKN (Connectivity-association Key Name) for MACsec Key Agreement. Must be a string of hexadecimal characters with a even length between 2 and 64. The pre-shared CKN (Connectivity-association Key Name) for MACsec Key Agreement. Must be a string of hexadecimal characters with a even length between 2 and 64. Specifies how the CAK (Connectivity Association Key) for MKA (MACsec Key Agreement) is obtained. Specifies how the CAK (Connectivity Association Key) for MKA (MACsec Key Agreement) is obtained. If given, specifies the parent interface name or parent connection UUID from which this MACSEC interface should be created.  If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property. If given, specifies the parent interface name or parent connection UUID from which this MACSEC interface should be created.  If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property. The port component of the SCI (Secure Channel Identifier), between 1 and 65534. The port component of the SCI (Secure Channel Identifier), between 1 and 65534. Specifies whether the SCI (Secure Channel Identifier) is included in every packet. Specifies whether the SCI (Secure Channel Identifier) is included in every packet. Specifies the validation mode for incoming frames. Specifies the validation mode for incoming frames. The macvlan mode, which specifies the communication mechanism between multiple macvlans on the same lower device. The macvlan mode, which specifies the communication mechanism between multiple macvlans on the same lower device. If given, specifies the parent interface name or parent connection UUID from which this MAC-VLAN interface should be created.  If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property. If given, specifies the parent interface name or parent connection UUID from which this MAC-VLAN interface should be created.  If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property. Whether the interface should be put in promiscuous mode. Whether the interface should be put in promiscuous mode. Whether the interface should be a MACVTAP. Whether the interface should be a MACVTAP. A list of driver names to match. Each element is a shell wildcard pattern. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the pattern. A list of driver names to match. Each element is a shell wildcard pattern.
-
- See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the pattern. A list of interface names to match. Each element is a shell wildcard pattern. An element can be prefixed with a pipe symbol (|) or an ampersand (&). The former means that the element is optional and the latter means that it is mandatory. If there are any optional elements, than the match evaluates to true if at least one of the optional element matches (logical OR). If there are any mandatory elements, then they all must match (logical AND). By default, an element is optional. This means that an element "foo" behaves the same as "|foo". An element can also be inverted with exclamation mark (!) between the pipe symbol (or the ampersand) and before the pattern. Note that "!foo" is a shortcut for the mandatory match "&!foo". Finally, a backslash can be used at the beginning of the element (after the optional special characters) to escape the start of the pattern. For example, "&\\!a" is an mandatory match for literally "!a". A list of interface names to match. Each element is a shell wildcard pattern.
-
- An element can be prefixed with a pipe symbol (|) or an ampersand (&). The former means that the element is optional and the latter means that it is mandatory. If there are any optional elements, than the match evaluates to true if at least one of the optional element matches (logical OR). If there are any mandatory elements, then they all must match (logical AND). By default, an element is optional. This means that an element "foo" behaves the same as "|foo". An element can also be inverted with exclamation mark (!) between the pipe symbol (or the ampersand) and before the pattern. Note that "!foo" is a shortcut for the mandatory match "&!foo". Finally, a backslash can be used at the beginning of the element (after the optional special characters) to escape the start of the pattern. For example, "&\\!a" is an mandatory match for literally "!a". A list of kernel command line arguments to match. This may be used to check whether a specific kernel command line option is set (or unset, if prefixed with the exclamation mark). The argument must either be a single word, or an assignment (i.e. two words, joined by "="). In the former case the kernel command line is searched for the word appearing as is, or as left hand side of an assignment. In the latter case, the exact assignment is looked for with right and left hand side matching. Wildcard patterns are not supported. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the match. A list of kernel command line arguments to match. This may be used to check whether a specific kernel command line option is set (or unset, if prefixed with the exclamation mark). The argument must either be a single word, or an assignment (i.e. two words, joined by "="). In the former case the kernel command line is searched for the word appearing as is, or as left hand side of an assignment. In the latter case, the exact assignment is looked for with right and left hand side matching. Wildcard patterns are not supported.
-
- See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the match. A list of paths to match against the ID_PATH udev property of devices. ID_PATH represents the topological persistent path of a device. It typically contains a subsystem string (pci, usb, platform, etc.) and a subsystem-specific identifier. For PCI devices the path has the form "pci-$domain:$bus:$device.$function", where each variable is an hexadecimal value; for example "pci-0000:0a:00.0". The path of a device can be obtained with "udevadm info /sys/class/net/$dev | grep ID_PATH=" or by looking at the "path" property exported by NetworkManager ("nmcli -f general.path device show $dev"). Each element of the list is a shell wildcard pattern. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the pattern. A list of paths to match against the ID_PATH udev property of devices. ID_PATH represents the topological persistent path of a device. It typically contains a subsystem string (pci, usb, platform, etc.) and a subsystem-specific identifier.
-
- For PCI devices the path has the form "pci-$domain:$bus:$device.$function", where each variable is an hexadecimal value; for example "pci-0000:0a:00.0".
-
- The path of a device can be obtained with "udevadm info /sys/class/net/$dev | grep ID_PATH=" or by looking at the "path" property exported by NetworkManager ("nmcli -f general.path device show $dev").
-
- Each element of the list is a shell wildcard pattern.
-
- See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the pattern. Channel on which the mesh network to join is located. Channel on which the mesh network to join is located. Anycast DHCP MAC address used when requesting an IP address via DHCP. The specific anycast address used determines which DHCP server class answers the request. This is currently only implemented by dhclient DHCP plugin. Anycast DHCP MAC address used when requesting an IP address via DHCP. The specific anycast address used determines which DHCP server class answers the request.
-
- This is currently only implemented by dhclient DHCP plugin. SSID of the mesh network to join. SSID of the mesh network to join. The data path type. One of "system", "netdev" or empty. The data path type. One of "system", "netdev" or empty. The bridge failure mode. One of "secure", "standalone" or empty. The bridge failure mode. One of "secure", "standalone" or empty. Enable or disable multicast snooping. Enable or disable multicast snooping. Enable or disable RSTP. Enable or disable RSTP. Enable or disable STP. Enable or disable STP. Open vSwitch DPDK device arguments. Open vSwitch DPDK device arguments. Open vSwitch DPDK number of rx queues. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures one queue. Open vSwitch DPDK number of rx queues. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures one queue. The interface type. Either "internal", "system", "patch", "dpdk", or empty. The interface type. Either "internal", "system", "patch", "dpdk", or empty. Specifies the name of the interface for the other side of the patch. The patch on the other side must also set this interface as peer. Specifies the name of the interface for the other side of the patch. The patch on the other side must also set this interface as peer. The time port must be inactive in order to be considered down. The time port must be inactive in order to be considered down. Bonding mode. One of "active-backup", "balance-slb", or "balance-tcp". Bonding mode. One of "active-backup", "balance-slb", or "balance-tcp". The time port must be active before it starts forwarding traffic. The time port must be active before it starts forwarding traffic. LACP mode. One of "active", "off", or "passive". LACP mode. One of "active", "off", or "passive". The VLAN tag in the range 0-4095. The VLAN tag in the range 0-4095. The VLAN mode. One of "access", "native-tagged", "native-untagged", "trunk" or unset. The VLAN mode. One of "access", "native-tagged", "native-untagged", "trunk" or unset. If non-zero, instruct pppd to set the serial port to the specified baudrate.  This value should normally be left as 0 to automatically choose the speed. If non-zero, instruct pppd to set the serial port to the specified baudrate.  This value should normally be left as 0 to automatically choose the speed. If TRUE, specify that pppd should set the serial port to use hardware flow control with RTS and CTS signals.  This value should normally be set to FALSE. If TRUE, specify that pppd should set the serial port to use hardware flow control with RTS and CTS signals.  This value should normally be set to FALSE. If non-zero, instruct pppd to presume the connection to the peer has failed if the specified number of LCP echo-requests go unanswered by the peer.  The "lcp-echo-interval" property must also be set to a non-zero value if this property is used. If non-zero, instruct pppd to presume the connection to the peer has failed if the specified number of LCP echo-requests go unanswered by the peer.  The "lcp-echo-interval" property must also be set to a non-zero value if this property is used. If non-zero, instruct pppd to send an LCP echo-request frame to the peer every n seconds (where n is the specified value).  Note that some PPP peers will respond to echo requests and some will not, and it is not possible to autodetect this. If non-zero, instruct pppd to send an LCP echo-request frame to the peer every n seconds (where n is the specified value).  Note that some PPP peers will respond to echo requests and some will not, and it is not possible to autodetect this. If TRUE, stateful MPPE is used.  See pppd documentation for more information on stateful MPPE. If TRUE, stateful MPPE is used.  See pppd documentation for more information on stateful MPPE. If non-zero, instruct pppd to request that the peer send packets no larger than the specified size.  If non-zero, the MRU should be between 128 and 16384. If non-zero, instruct pppd to request that the peer send packets no larger than the specified size.  If non-zero, the MRU should be between 128 and 16384. If non-zero, instruct pppd to send packets no larger than the specified size. If non-zero, instruct pppd to send packets no larger than the specified size. If TRUE, Van Jacobsen TCP header compression will not be requested. If TRUE, Van Jacobsen TCP header compression will not be requested. If TRUE, do not require the other side (usually the PPP server) to authenticate itself to the client.  If FALSE, require authentication from the remote side.  In almost all cases, this should be TRUE. If TRUE, do not require the other side (usually the PPP server) to authenticate itself to the client.  If FALSE, require authentication from the remote side.  In almost all cases, this should be TRUE. If TRUE, BSD compression will not be requested. If TRUE, BSD compression will not be requested. If TRUE, "deflate" compression will not be requested. If TRUE, "deflate" compression will not be requested. If TRUE, the CHAP authentication method will not be used. If TRUE, the CHAP authentication method will not be used. If TRUE, the EAP authentication method will not be used. If TRUE, the EAP authentication method will not be used. If TRUE, the MSCHAP authentication method will not be used. If TRUE, the MSCHAP authentication method will not be used. If TRUE, the MSCHAPv2 authentication method will not be used. If TRUE, the MSCHAPv2 authentication method will not be used. If TRUE, the PAP authentication method will not be used. If TRUE, the PAP authentication method will not be used. If TRUE, MPPE (Microsoft Point-to-Point Encryption) will be required for the PPP session.  If either 64-bit or 128-bit MPPE is not available the session will fail.  Note that MPPE is not used on mobile broadband connections. If TRUE, MPPE (Microsoft Point-to-Point Encryption) will be required for the PPP session.  If either 64-bit or 128-bit MPPE is not available the session will fail.  Note that MPPE is not used on mobile broadband connections. If TRUE, 128-bit MPPE (Microsoft Point-to-Point Encryption) will be required for the PPP session, and the "require-mppe" property must also be set to TRUE.  If 128-bit MPPE is not available the session will fail. If TRUE, 128-bit MPPE (Microsoft Point-to-Point Encryption) will be required for the PPP session, and the "require-mppe" property must also be set to TRUE.  If 128-bit MPPE is not available the session will fail. If given, specifies the parent interface name on which this PPPoE connection should be created.  If this property is not specified, the connection is activated on the interface specified in "interface-name" of NMSettingConnection. If given, specifies the parent interface name on which this PPPoE connection should be created.  If this property is not specified, the connection is activated on the interface specified in "interface-name" of NMSettingConnection. Password used to authenticate with the PPPoE service. Password used to authenticate with the PPPoE service. Flags indicating how to handle the "password" property. Flags indicating how to handle the "password" property. If specified, instruct PPPoE to only initiate sessions with access concentrators that provide the specified service.  For most providers, this should be left blank.  It is only required if there are multiple access concentrators or a specific service is known to be required. If specified, instruct PPPoE to only initiate sessions with access concentrators that provide the specified service.  For most providers, this should be left blank.  It is only required if there are multiple access concentrators or a specific service is known to be required. Username used to authenticate with the PPPoE service. Username used to authenticate with the PPPoE service. Whether the proxy configuration is for browser only. Whether the proxy configuration is for browser only. Method for proxy configuration, Default is NM_SETTING_PROXY_METHOD_NONE (0) Method for proxy configuration, Default is NM_SETTING_PROXY_METHOD_NONE (0) PAC script for the connection. This is an UTF-8 encoded javascript code that defines a FindProxyForURL() function.The PAC script. In the profile this must be an UTF-8 encoded javascript code that defines a FindProxyForURL() function. When setting the property in nmcli, a filename is accepted too. In that case, nmcli will read the content of the file and set the script. The prefixes "file://" and "js://" are supported to explicitly differentiate between the two. PAC URL for obtaining PAC file. PAC URL for obtaining PAC file. Speed to use for communication over the serial port.  Note that this value usually has no effect for mobile broadband modems as they generally ignore speed settings and use the highest available speed. Speed to use for communication over the serial port.  Note that this value usually has no effect for mobile broadband modems as they generally ignore speed settings and use the highest available speed. Byte-width of the serial communication. The 8 in "8n1" for example. Byte-width of the serial communication. The 8 in "8n1" for example. Parity setting of the serial port. Parity setting of the serial port. Time to delay between each byte sent to the modem, in microseconds. Time to delay between each byte sent to the modem, in microseconds. Number of stop bits for communication on the serial port.  Either 1 or 2. The 1 in "8n1" for example. Number of stop bits for communication on the serial port.  Either 1 or 2. The 1 in "8n1" for example. Whether to autoprobe virtual functions by a compatible driver. If set to NM_TERNARY_TRUE (1), the kernel will try to bind VFs to a compatible driver and if this succeeds a new network interface will be instantiated for each VF. If set to NM_TERNARY_FALSE (0), VFs will not be claimed and no network interfaces will be created for them. When set to NM_TERNARY_DEFAULT (-1), the global default is used; in case the global default is unspecified it is assumed to be NM_TERNARY_TRUE (1). Whether to autoprobe virtual functions by a compatible driver.
-
- If set to NM_TERNARY_TRUE (1), the kernel will try to bind VFs to a compatible driver and if this succeeds a new network interface will be instantiated for each VF.
-
- If set to NM_TERNARY_FALSE (0), VFs will not be claimed and no network interfaces will be created for them.
-
- When set to NM_TERNARY_DEFAULT (-1), the global default is used; in case the global default is unspecified it is assumed to be NM_TERNARY_TRUE (1). The total number of virtual functions to create. Note that when the sriov setting is present NetworkManager enforces the number of virtual functions on the interface (also when it is zero) during activation and resets it upon deactivation. To prevent any changes to SR-IOV parameters don't add a sriov setting to the connection. The total number of virtual functions to create.
-
- Note that when the sriov setting is present NetworkManager enforces the number of virtual functions on the interface (also when it is zero) during activation and resets it upon deactivation. To prevent any changes to SR-IOV parameters don't add a sriov setting to the connection. Array of virtual function descriptors. Each VF descriptor is a dictionary mapping attribute names to GVariant values. The 'index' entry is mandatory for each VF. When represented as string a VF is in the form: "INDEX [ATTR=VALUE[ ATTR=VALUE]...]". for example: "2 mac=00:11:22:33:44:55 spoof-check=true". Multiple VFs can be specified using a comma as separator. Currently, the following attributes are supported: mac, spoof-check, trust, min-tx-rate, max-tx-rate, vlans. The "vlans" attribute is represented as a semicolon-separated list of VLAN descriptors, where each descriptor has the form "ID[.PRIORITY[.PROTO]]". PROTO can be either 'q' for 802.1Q (the default) or 'ad' for 802.1ad. Array of virtual function descriptors.
-
- Each VF descriptor is a dictionary mapping attribute names to GVariant values. The 'index' entry is mandatory for each VF.
-
- When represented as string a VF is in the form:
-
- "INDEX [ATTR=VALUE[ ATTR=VALUE]...]".
-
- for example:
-
- "2 mac=00:11:22:33:44:55 spoof-check=true".
-
- Multiple VFs can be specified using a comma as separator. Currently, the following attributes are supported: mac, spoof-check, trust, min-tx-rate, max-tx-rate, vlans.
-
- The "vlans" attribute is represented as a semicolon-separated list of VLAN descriptors, where each descriptor has the form
-
- "ID[.PRIORITY[.PROTO]]".
-
- PROTO can be either 'q' for 802.1Q (the default) or 'ad' for 802.1ad.
-
-Array of TC queueing disciplines. qdisc is a basic block in the
-Linux traffic control subsystem
-
-
-Each qdisc can be specified by the following attributes:
-
-
-
-handle HANDLE
-
-
-specifies the qdisc handle. A qdisc, which potentially can have children, gets
-assigned a major number, called a 'handle', leaving the minor number namespace
-available for classes. The handle is expressed as '10:'. It is customary to
-explicitly assign a handle to qdiscs expected to have children.
-
-
-
-
-parent HANDLE
-
-
-specifies the handle of the parent qdisc the current qdisc must be
-attached to.
-
-
-
-
-root
-
-
-specifies that the qdisc is attached to the root of device.
-
-
-
-
-KIND
-
-
-this is the qdisc kind. NetworkManager currently supports the
-following kinds: fq_codel, sfq, tbf. Each qdisc kind has a
-different set of parameters, described below. There are also some
-kinds like pfifo, pfifo_fast, prio supported by NetworkManager
-but their parameters are not supported by NetworkManager.
-
-
-
-
-
-Parameters for 'fq_codel':
-
-
-
-limit U32
-
-
-the hard limit on the real queue size.  When this limit is
-reached, incoming packets are dropped. Default is 10240 packets.
-
-
-
-
-memory_limit U32
-
-
-sets a limit on the total number of bytes that can be queued in
-this FQ-CoDel instance. The lower of the packet limit of the
-limit parameter and the memory limit will be enforced. Default is
-32 MB.
-
-
-
-
-flows U32
-
-
-the number of flows into which the incoming packets are
-classified. Due to the stochastic nature of hashing, multiple
-flows may end up being hashed into the same slot. Newer flows
-have priority over older ones. This parameter can be set only at
-load time since memory has to be allocated for the hash table.
-Default value is 1024.
-
-
-
-
-target U32
-
-
-the acceptable minimum standing/persistent queue delay. This minimum
-delay is identified by tracking the local minimum queue delay that packets
-experience. The unit of measurement is microsecond(us). Default value is 5ms.
-
-
-
-
-interval U32
-
-
-used to ensure that the measured minimum delay does not become too stale.
-The minimum delay must be experienced in the last epoch of length .B
-interval.  It should be set on the order of the worst-case RTT
-through the bottleneck to give endpoints sufficient time to
-react. Default value is 100ms.
-
-
-
-
-quantum U32
-
-
-the number of bytes used as 'deficit' in the fair queuing
-algorithm. Default is set to 1514 bytes which corresponds to the
-Ethernet MTU plus the hardware header length of 14 bytes.
-
-
-
-
-ecn BOOL
-
-
-can be used to mark packets instead of dropping them. ecn is turned
-on by default.
-
-
-
-
-ce_threshold U32
-
-
-sets a threshold above which all packets are marked with ECN
-Congestion Experienced. This is useful for DCTCP-style congestion
-control algorithms that require marking at very shallow queueing
-thresholds.
-
-
-
-
-
-Parameters for 'sfq':
-
-
-
-divisor U32
-
-
-can be used to set a different hash table size, available
-from kernel 2.6.39 onwards.  The specified divisor must be
-a power of two and cannot be larger than 65536.  Default
-value: 1024.
-
-
-
-
-limit U32
-
-
-Upper limit of the SFQ. Can be used to reduce the default
-length of 127 packets.
-
-
-
-
-depth U32
-
-
-Limit of packets per flow. Default to
-127 and can be lowered.
-
-
-
-
-perturb_period U32
-
-
-Interval in seconds for queue algorithm perturbation.
-Defaults to 0, which means that no perturbation occurs. Do
-not set too low for each perturbation may cause some
-packet reordering or losses. Advised value: 60 This value
-has no effect when external flow classification is used.
-Its better to increase divisor value to lower risk of hash
-collisions.
-
-
-
-
-quantum U32
-
-
-Amount of bytes a flow is allowed to dequeue during a
-round of the round robin process.  Defaults to the MTU of
-the interface which is also the advised value and the
-minimum value.
-
-
-
-
-flows U32
-
-
-Default value is 127.
-
-
-
-
-
-Parameters for 'tbf':
-
-
-
-rate U64
-
-
-Bandwidth or rate.  These parameters accept a floating
-point number, possibly followed by either a unit (both SI
-and IEC units supported), or a float followed by a percent
-character to specify the rate as a percentage of the
-device's speed.
-
-
-
-
-burst U32
-
-
-Also known as buffer or maxburst.  Size of the bucket, in
-bytes. This is the maximum amount of bytes that tokens can
-be available for instantaneously.  In general, larger
-shaping rates require a larger buffer. For 10mbit/s on
-Intel, you need at least 10kbyte buffer if you want to
-reach your configured rate!
-
-
-If your buffer is too small, packets may be dropped
-because more tokens arrive per timer tick than fit in your
-bucket.  The minimum buffer size can be calculated by
-dividing the rate by HZ.
-
-
-Token usage calculations are performed using a table which
-by default has a resolution of 8 packets.  This resolution
-can be changed by specifying the cell size with the burst.
-For example, to specify a 6000 byte buffer with a 16 byte
-cell size, set a burst of 6000/16. You will probably never
-have to set this. Must be an integral power of 2.
-
-
-
-
-limit U32
-
-
-Limit is the number of bytes that can be queued waiting
-for tokens to become available.
-
-
-
-
-latency U32
-
-
-specifies the maximum amount of time a packet can
-sit in the TBF. The latency calculation takes into account
-the size of the bucket, the rate and possibly the peakrate
-(if set). The latency and limit are mutually exclusive.
-
-
-
- Array of TC queueing disciplines.
-
- When the "tc" setting is present, qdiscs from this property are applied upon activation. If the property is empty, all qdiscs are removed and the device will only have the default qdisc assigned by kernel according to the "net.core.default_qdisc" sysctl.
-
- If the "tc" setting is not present, NetworkManager doesn't touch the qdiscs present on the interface.
-
-Array of TC traffic filters. Traffic control can manage the packet content during
-classification by using filters.
-
-
-Each tfilters can be specified by the following attributes:
-
-
-
-handle HANDLE
-
-
-specifies the tfilters handle. A filter is used by a classful qdisc to determine in which class
-a packet will be enqueued. It is important to notice that filters reside within qdiscs. Therefore,
-see qdiscs handle for detailed information.
-
-
-
-
-parent HANDLE
-
-
-specifies the handle of the parent qdisc the current qdisc must be
-attached to.
-
-
-
-
-root
-
-
-specifies that the qdisc is attached to the root of device.
-
-
-
-
-KIND
-
-
-this is the tfilters kind. NetworkManager currently supports
-following kinds: mirred, simple. Each filter kind has a
-different set of actions, described below. There are also some
-other kinds like matchall, basic, u32 supported by NetworkManager.
-
-
-
-
-
-Actions for 'mirred':
-
-
-
-egress bool
-
-
-Define whether the packet should exit from the interface.
-
-
-
-
-ingress bool
-
-
-Define whether the packet should come into the interface.
-
-
-
-
-mirror bool
-
-
-Define whether the packet should be copied to the destination space.
-
-
-
-
-redirect bool
-
-
-Define whether the packet should be moved to the destination space.
-
-
-
-
-
-Action for 'simple':
-
-
-
-sdata char[32]
-
-
-The actual string to print.
-
-
-
- Array of TC traffic filters.
-
- When the "tc" setting is present, filters from this property are applied upon activation. If the property is empty, NetworkManager removes all the filters.
-
- If the "tc" setting is not present, NetworkManager doesn't touch the filters present on the interface. The JSON configuration for the team network interface.  The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used.  See man teamd.conf for the format details. The JSON configuration for the team network interface.  The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used.  See man teamd.conf for the format details. Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details. Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details. Corresponds to the teamd mcast_rejoin.count. Corresponds to the teamd mcast_rejoin.count. Corresponds to the teamd mcast_rejoin.interval. Corresponds to the teamd mcast_rejoin.interval. Corresponds to the teamd notify_peers.count. Corresponds to the teamd notify_peers.count. Corresponds to the teamd notify_peers.interval. Corresponds to the teamd notify_peers.interval. Corresponds to the teamd runner.name. Permitted values are: "roundrobin", "broadcast", "activebackup", "loadbalance", "lacp", "random". Corresponds to the teamd runner.name. Permitted values are: "roundrobin", "broadcast", "activebackup", "loadbalance", "lacp", "random". Corresponds to the teamd runner.active. Corresponds to the teamd runner.active. Corresponds to the teamd runner.agg_select_policy. Corresponds to the teamd runner.agg_select_policy. Corresponds to the teamd runner.fast_rate. Corresponds to the teamd runner.fast_rate. Corresponds to the teamd runner.hwaddr_policy. Corresponds to the teamd runner.hwaddr_policy. Corresponds to the teamd runner.min_ports. Corresponds to the teamd runner.min_ports. Corresponds to the teamd runner.sys_prio. Corresponds to the teamd runner.sys_prio. Corresponds to the teamd runner.tx_balancer.name. Corresponds to the teamd runner.tx_balancer.name. Corresponds to the teamd runner.tx_balancer.interval. Corresponds to the teamd runner.tx_balancer.interval. Corresponds to the teamd runner.tx_hash. Corresponds to the teamd runner.tx_hash. The JSON configuration for the team port. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details. The JSON configuration for the team port. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details. Corresponds to the teamd ports.PORTIFNAME.lacp_key. Corresponds to the teamd ports.PORTIFNAME.lacp_key. Corresponds to the teamd ports.PORTIFNAME.lacp_prio. Corresponds to the teamd ports.PORTIFNAME.lacp_prio. Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details. Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details. Corresponds to the teamd ports.PORTIFNAME.prio. Corresponds to the teamd ports.PORTIFNAME.prio. Corresponds to the teamd ports.PORTIFNAME.queue_id. When set to -1 means the parameter is skipped from the json config. Corresponds to the teamd ports.PORTIFNAME.queue_id. When set to -1 means the parameter is skipped from the json config. Corresponds to the teamd ports.PORTIFNAME.sticky. Corresponds to the teamd ports.PORTIFNAME.sticky. The group ID which will own the device. If set to NULL everyone will be able to use the device. The group ID which will own the device. If set to NULL everyone will be able to use the device. The operating mode of the virtual device. Allowed values are NM_SETTING_TUN_MODE_TUN (1) to create a layer 3 device and NM_SETTING_TUN_MODE_TAP (2) to create an Ethernet-like layer 2 one. The operating mode of the virtual device. Allowed values are NM_SETTING_TUN_MODE_TUN (1) to create a layer 3 device and NM_SETTING_TUN_MODE_TAP (2) to create an Ethernet-like layer 2 one. If the property is set to TRUE, the interface will support multiple file descriptors (queues) to parallelize packet sending or receiving. Otherwise, the interface will only support a single queue. If the property is set to TRUE, the interface will support multiple file descriptors (queues) to parallelize packet sending or receiving. Otherwise, the interface will only support a single queue. The user ID which will own the device. If set to NULL everyone will be able to use the device. The user ID which will own the device. If set to NULL everyone will be able to use the device. If TRUE the interface will prepend a 4 byte header describing the physical interface to the packets. If TRUE the interface will prepend a 4 byte header describing the physical interface to the packets. If TRUE the IFF_VNET_HDR the tunnel packets will include a virtio network header. If TRUE the IFF_VNET_HDR the tunnel packets will include a virtio network header. A dictionary of key/value pairs with user data. This data is ignored by NetworkManager and can be used at the users discretion. The keys only support a strict ascii format, but the values can be arbitrary UTF8 strings up to a certain length. A dictionary of key/value pairs with user data. This data is ignored by NetworkManager and can be used at the users discretion. The keys only support a strict ascii format, but the values can be arbitrary UTF8 strings up to a certain length. For outgoing packets, a list of mappings from Linux SKB priorities to 802.1p priorities.  The mapping is given in the format "from:to" where both "from" and "to" are unsigned integers, ie "7:3". For outgoing packets, a list of mappings from Linux SKB priorities to 802.1p priorities.  The mapping is given in the format "from:to" where both "from" and "to" are unsigned integers, ie "7:3". One or more flags which control the behavior and features of the VLAN interface.  Flags include NM_VLAN_FLAG_REORDER_HEADERS (0x1) (reordering of output packet headers), NM_VLAN_FLAG_GVRP (0x2) (use of the GVRP protocol), and NM_VLAN_FLAG_LOOSE_BINDING (0x4) (loose binding of the interface to its master device's operating state). NM_VLAN_FLAG_MVRP (0x8) (use of the MVRP protocol). The default value of this property is NM_VLAN_FLAG_REORDER_HEADERS, but it used to be 0. To preserve backward compatibility, the default-value in the D-Bus API continues to be 0 and a missing property on D-Bus is still considered as 0. One or more flags which control the behavior and features of the VLAN interface.  Flags include NM_VLAN_FLAG_REORDER_HEADERS (0x1) (reordering of output packet headers), NM_VLAN_FLAG_GVRP (0x2) (use of the GVRP protocol), and NM_VLAN_FLAG_LOOSE_BINDING (0x4) (loose binding of the interface to its master device's operating state). NM_VLAN_FLAG_MVRP (0x8) (use of the MVRP protocol).
-
- The default value of this property is NM_VLAN_FLAG_REORDER_HEADERS, but it used to be 0. To preserve backward compatibility, the default-value in the D-Bus API continues to be 0 and a missing property on D-Bus is still considered as 0. The VLAN identifier that the interface created by this connection should be assigned. The valid range is from 0 to 4094, without the reserved id 4095. The VLAN identifier that the interface created by this connection should be assigned. The valid range is from 0 to 4094, without the reserved id 4095. For incoming packets, a list of mappings from 802.1p priorities to Linux SKB priorities.  The mapping is given in the format "from:to" where both "from" and "to" are unsigned integers, ie "7:3". For incoming packets, a list of mappings from 802.1p priorities to Linux SKB priorities.  The mapping is given in the format "from:to" where both "from" and "to" are unsigned integers, ie "7:3". If given, specifies the parent interface name or parent connection UUID from which this VLAN interface should be created.  If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property. If given, specifies the parent interface name or parent connection UUID from which this VLAN interface should be created.  If this property is not specified, the connection must contain an "802-3-ethernet" setting with a "mac-address" property. Dictionary of key/value pairs of VPN plugin specific data.  Both keys and values must be strings. Dictionary of key/value pairs of VPN plugin specific data.  Both keys and values must be strings. If the VPN service supports persistence, and this property is TRUE, the VPN will attempt to stay connected across link changes and outages, until explicitly disconnected. If the VPN service supports persistence, and this property is TRUE, the VPN will attempt to stay connected across link changes and outages, until explicitly disconnected. Dictionary of key/value pairs of VPN plugin specific secrets like passwords or private keys.  Both keys and values must be strings. Dictionary of key/value pairs of VPN plugin specific secrets like passwords or private keys.  Both keys and values must be strings. D-Bus service name of the VPN plugin that this setting uses to connect to its network.  i.e. org.freedesktop.NetworkManager.vpnc for the vpnc plugin. D-Bus service name of the VPN plugin that this setting uses to connect to its network.  i.e. org.freedesktop.NetworkManager.vpnc for the vpnc plugin. Timeout for the VPN service to establish the connection. Some services may take quite a long time to connect. Value of 0 means a default timeout, which is 60 seconds (unless overridden by vpn.timeout in configuration file). Values greater than zero mean timeout in seconds. Timeout for the VPN service to establish the connection. Some services may take quite a long time to connect. Value of 0 means a default timeout, which is 60 seconds (unless overridden by vpn.timeout in configuration file). Values greater than zero mean timeout in seconds. If the VPN connection requires a user name for authentication, that name should be provided here.  If the connection is available to more than one user, and the VPN requires each user to supply a different name, then leave this property empty.  If this property is empty, NetworkManager will automatically supply the username of the user which requested the VPN connection. If the VPN connection requires a user name for authentication, that name should be provided here.  If the connection is available to more than one user, and the VPN requires each user to supply a different name, then leave this property empty.  If this property is empty, NetworkManager will automatically supply the username of the user which requested the VPN connection. The routing table for this VRF. The routing table for this VRF. Specifies the lifetime in seconds of FDB entries learnt by the kernel. Specifies the lifetime in seconds of FDB entries learnt by the kernel. Specifies the UDP destination port to communicate to the remote VXLAN tunnel endpoint. Specifies the UDP destination port to communicate to the remote VXLAN tunnel endpoint. Specifies the VXLAN Network Identifier (or VXLAN Segment Identifier) to use. Specifies the VXLAN Network Identifier (or VXLAN Segment Identifier) to use. Specifies whether netlink LL ADDR miss notifications are generated. Specifies whether netlink LL ADDR miss notifications are generated. Specifies whether netlink IP ADDR miss notifications are generated. Specifies whether netlink IP ADDR miss notifications are generated. Specifies whether unknown source link layer addresses and IP addresses are entered into the VXLAN device forwarding database. Specifies whether unknown source link layer addresses and IP addresses are entered into the VXLAN device forwarding database. Specifies the maximum number of FDB entries. A value of zero means that the kernel will store unlimited entries. Specifies the maximum number of FDB entries. A value of zero means that the kernel will store unlimited entries. If given, specifies the source IP address to use in outgoing packets. If given, specifies the source IP address to use in outgoing packets. If given, specifies the parent interface name or parent connection UUID. If given, specifies the parent interface name or parent connection UUID. Specifies whether ARP proxy is turned on. Specifies whether ARP proxy is turned on. Specifies the unicast destination IP address to use in outgoing packets when the destination link layer address is not known in the VXLAN device forwarding database, or the multicast IP address to join. Specifies the unicast destination IP address to use in outgoing packets when the destination link layer address is not known in the VXLAN device forwarding database, or the multicast IP address to join. Specifies whether route short circuit is turned on. Specifies whether route short circuit is turned on. Specifies the maximum UDP source port to communicate to the remote VXLAN tunnel endpoint. Specifies the maximum UDP source port to communicate to the remote VXLAN tunnel endpoint. Specifies the minimum UDP source port to communicate to the remote VXLAN tunnel endpoint. Specifies the minimum UDP source port to communicate to the remote VXLAN tunnel endpoint. Specifies the TOS value to use in outgoing packets. Specifies the TOS value to use in outgoing packets. Specifies the time-to-live value to use in outgoing packets. Specifies the time-to-live value to use in outgoing packets. The P2P device that should be connected to. Currently, this is the only way to create or join a group. The P2P device that should be connected to. Currently, this is the only way to create or join a group. The Wi-Fi Display (WFD) Information Elements (IEs) to set. Wi-Fi Display requires a protocol specific information element to be set in certain Wi-Fi frames. These can be specified here for the purpose of establishing a connection. This setting is only useful when implementing a Wi-Fi Display client. The Wi-Fi Display (WFD) Information Elements (IEs) to set.
-
- Wi-Fi Display requires a protocol specific information element to be set in certain Wi-Fi frames. These can be specified here for the purpose of establishing a connection. This setting is only useful when implementing a Wi-Fi Display client. Flags indicating which mode of WPS is to be used. There's little point in changing the default setting as NetworkManager will automatically determine the best method to use. Flags indicating which mode of WPS is to be used.
-
- There's little point in changing the default setting as NetworkManager will automatically determine the best method to use. If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing). Deprecated: 1 If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing). Deprecated: 1 Network Service Provider (NSP) name of the WiMAX network this connection should use. Deprecated: 1 Network Service Provider (NSP) name of the WiMAX network this connection should use. Deprecated: 1 When TRUE, setup the interface to accept packets for all MAC addresses. This is enabling the kernel interface flag IFF_PROMISC. When FALSE, the interface will only accept the packets with the interface destination mac address or broadcast. When TRUE, setup the interface to accept packets for all MAC addresses. This is enabling the kernel interface flag IFF_PROMISC. When FALSE, the interface will only accept the packets with the interface destination mac address or broadcast. When TRUE, enforce auto-negotiation of speed and duplex mode. If "speed" and "duplex" properties are both specified, only that single mode will be advertised and accepted during the link auto-negotiation process: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabits modes, as in these cases link negotiation is mandatory. When FALSE, "speed" and "duplex" properties should be both set or link configuration will be skipped. When TRUE, enforce auto-negotiation of speed and duplex mode. If "speed" and "duplex" properties are both specified, only that single mode will be advertised and accepted during the link auto-negotiation process: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabits modes, as in these cases link negotiation is mandatory. When FALSE, "speed" and "duplex" properties should be both set or link configuration will be skipped. If specified, request that the device use this MAC address instead. This is known as MAC cloning or spoofing. Beside explicitly specifying a MAC address, the special values "preserve", "permanent", "random" and "stable" are supported. "preserve" means not to touch the MAC address on activation. "permanent" means to use the permanent hardware address if the device has one (otherwise this is treated as "preserve"). "random" creates a random MAC address on each connect. "stable" creates a hashed MAC address based on connection.stable-id and a machine dependent key. If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to "preserve" (older versions of NetworkManager may use a different default value). On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated "cloned-mac-address". If specified, request that the device use this MAC address instead. This is known as MAC cloning or spoofing.
-
- Beside explicitly specifying a MAC address, the special values "preserve", "permanent", "random" and "stable" are supported. "preserve" means not to touch the MAC address on activation. "permanent" means to use the permanent hardware address if the device has one (otherwise this is treated as "preserve"). "random" creates a random MAC address on each connect. "stable" creates a hashed MAC address based on connection.stable-id and a machine dependent key.
-
- If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to "preserve" (older versions of NetworkManager may use a different default value).
-
- On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated "cloned-mac-address". When a value is set, either "half" or "full", configures the device to use the specified duplex mode. If "auto-negotiate" is "yes" the specified duplex mode will be the only one advertised during link negotiation: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabits modes, as in these cases link negotiation is mandatory. If the value is unset (the default), the link configuration will be either skipped (if "auto-negotiate" is "no", the default) or will be auto-negotiated (if "auto-negotiate" is "yes") and the local device will advertise all the supported duplex modes. Must be set together with the "speed" property if specified. Before specifying a duplex mode be sure your device supports it. When a value is set, either "half" or "full", configures the device to use the specified duplex mode. If "auto-negotiate" is "yes" the specified duplex mode will be the only one advertised during link negotiation: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabits modes, as in these cases link negotiation is mandatory. If the value is unset (the default), the link configuration will be either skipped (if "auto-negotiate" is "no", the default) or will be auto-negotiated (if "auto-negotiate" is "yes") and the local device will advertise all the supported duplex modes. Must be set together with the "speed" property if specified. Before specifying a duplex mode be sure your device supports it. With "cloned-mac-address" setting "random" or "stable", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address. If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address. If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the "random" or "stable" algorithm. If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled globally-administered, burned-in MAC address. If the value contains more than one additional MAC addresses, one of them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" will create a fully scrambled MAC address, randomly locally or globally administered. With "cloned-mac-address" setting "random" or "stable", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address.
-
- If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address.
-
- If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the "random" or "stable" algorithm.
-
- If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled globally-administered, burned-in MAC address.
-
- If the value contains more than one additional MAC addresses, one of them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" will create a fully scrambled MAC address, randomly locally or globally administered. If specified, this connection will only apply to the Ethernet device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing). If specified, this connection will only apply to the Ethernet device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing). If specified, this connection will never apply to the Ethernet device whose permanent MAC address matches an address in the list.  Each MAC address is in the standard hex-digits-and-colons notation (00:11:22:33:44:55). If specified, this connection will never apply to the Ethernet device whose permanent MAC address matches an address in the list.  Each MAC address is in the standard hex-digits-and-colons notation (00:11:22:33:44:55). If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames. Specific port type to use if the device supports multiple attachment methods.  One of "tp" (Twisted Pair), "aui" (Attachment Unit Interface), "bnc" (Thin Ethernet) or "mii" (Media Independent Interface). If the device supports only one port type, this setting is ignored. Specific port type to use if the device supports multiple attachment methods.  One of "tp" (Twisted Pair), "aui" (Attachment Unit Interface), "bnc" (Thin Ethernet) or "mii" (Media Independent Interface). If the device supports only one port type, this setting is ignored. s390 network device type; one of "qeth", "lcs", or "ctc", representing the different types of virtual network devices available on s390 systems. s390 network device type; one of "qeth", "lcs", or "ctc", representing the different types of virtual network devices available on s390 systems. Dictionary of key/value pairs of s390-specific device options.  Both keys and values must be strings.  Allowed keys include "portno", "layer2", "portname", "protocol", among others.  Key names must contain only alphanumeric characters (ie, [a-zA-Z0-9]). Currently, NetworkManager itself does nothing with this information. However, s390utils ships a udev rule which parses this information and applies it to the interface. Dictionary of key/value pairs of s390-specific device options.  Both keys and values must be strings.  Allowed keys include "portno", "layer2", "portname", "protocol", among others.  Key names must contain only alphanumeric characters (ie, [a-zA-Z0-9]).
-
- Currently, NetworkManager itself does nothing with this information. However, s390utils ships a udev rule which parses this information and applies it to the interface. Identifies specific subchannels that this network device uses for communication with z/VM or s390 host.  Like the "mac-address" property for non-z/VM devices, this property can be used to ensure this connection only applies to the network device that uses these subchannels.  The list should contain exactly 3 strings, and each string may only be composed of hexadecimal characters and the period (.) character. Identifies specific subchannels that this network device uses for communication with z/VM or s390 host.  Like the "mac-address" property for non-z/VM devices, this property can be used to ensure this connection only applies to the network device that uses these subchannels.  The list should contain exactly 3 strings, and each string may only be composed of hexadecimal characters and the period (.) character. When a value greater than 0 is set, configures the device to use the specified speed. If "auto-negotiate" is "yes" the specified speed will be the only one advertised during link negotiation: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabit speeds, as in this case link negotiation is mandatory. If the value is unset (0, the default), the link configuration will be either skipped (if "auto-negotiate" is "no", the default) or will be auto-negotiated (if "auto-negotiate" is "yes") and the local device will advertise all the supported speeds. In Mbit/s, ie 100 == 100Mbit/s. Must be set together with the "duplex" property when non-zero. Before specifying a speed value be sure your device supports it. When a value greater than 0 is set, configures the device to use the specified speed. If "auto-negotiate" is "yes" the specified speed will be the only one advertised during link negotiation: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabit speeds, as in this case link negotiation is mandatory. If the value is unset (0, the default), the link configuration will be either skipped (if "auto-negotiate" is "no", the default) or will be auto-negotiated (if "auto-negotiate" is "yes") and the local device will advertise all the supported speeds. In Mbit/s, ie 100 == 100Mbit/s. Must be set together with the "duplex" property when non-zero. Before specifying a speed value be sure your device supports it. The NMSettingWiredWakeOnLan options to enable. Not all devices support all options. May be any combination of NM_SETTING_WIRED_WAKE_ON_LAN_PHY (0x2), NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST (0x4), NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST (0x8), NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST (0x10), NM_SETTING_WIRED_WAKE_ON_LAN_ARP (0x20), NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC (0x40) or the special values NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT (0x1) (to use global settings) and NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE (0x8000) (to disable management of Wake-on-LAN in NetworkManager). The NMSettingWiredWakeOnLan options to enable. Not all devices support all options. May be any combination of NM_SETTING_WIRED_WAKE_ON_LAN_PHY (0x2), NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST (0x4), NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST (0x8), NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST (0x10), NM_SETTING_WIRED_WAKE_ON_LAN_ARP (0x20), NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC (0x40) or the special values NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT (0x1) (to use global settings) and NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE (0x8000) (to disable management of Wake-on-LAN in NetworkManager). If specified, the password used with magic-packet-based Wake-on-LAN, represented as an Ethernet MAC address.  If NULL, no password will be required. If specified, the password used with magic-packet-based Wake-on-LAN, represented as an Ethernet MAC address.  If NULL, no password will be required. The use of fwmark is optional and is by default off. Setting it to 0 disables it. Otherwise, it is a 32-bit fwmark for outgoing packets. Note that "ip4-auto-default-route" or "ip6-auto-default-route" enabled, implies to automatically choose a fwmark. The use of fwmark is optional and is by default off. Setting it to 0 disables it. Otherwise, it is a 32-bit fwmark for outgoing packets.
-
- Note that "ip4-auto-default-route" or "ip6-auto-default-route" enabled, implies to automatically choose a fwmark. Whether to enable special handling of the IPv4 default route. If enabled, the IPv4 default route from wireguard.peer-routes will be placed to a dedicated routing-table and two policy routing rules will be added. The fwmark number is also used as routing-table for the default-route, and if fwmark is zero, an unused fwmark/table is chosen automatically. This corresponds to what wg-quick does with Table=auto and what WireGuard calls "Improved Rule-based Routing". Note that for this automatism to work, you usually don't want to set ipv4.gateway, because that will result in a conflicting default route. Leaving this at the default will enable this option automatically if ipv4.never-default is not set and there are any peers that use a default-route as allowed-ips. Since this automatism only makes sense if you also have a peer with an /0 allowed-ips, it is usually not necessary to enable this explicitly. However, you can disable it if you want to configure your own routing and rules. Whether to enable special handling of the IPv4 default route. If enabled, the IPv4 default route from wireguard.peer-routes will be placed to a dedicated routing-table and two policy routing rules will be added. The fwmark number is also used as routing-table for the default-route, and if fwmark is zero, an unused fwmark/table is chosen automatically. This corresponds to what wg-quick does with Table=auto and what WireGuard calls "Improved Rule-based Routing".
-
- Note that for this automatism to work, you usually don't want to set ipv4.gateway, because that will result in a conflicting default route.
-
- Leaving this at the default will enable this option automatically if ipv4.never-default is not set and there are any peers that use a default-route as allowed-ips. Since this automatism only makes sense if you also have a peer with an /0 allowed-ips, it is usually not necessary to enable this explicitly. However, you can disable it if you want to configure your own routing and rules. Like ip4-auto-default-route, but for the IPv6 default route. Like ip4-auto-default-route, but for the IPv6 default route. The listen-port. If listen-port is not specified, the port will be chosen randomly when the interface comes up. The listen-port. If listen-port is not specified, the port will be chosen randomly when the interface comes up. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments. If zero a default MTU is used. Note that contrary to wg-quick's MTU setting, this does not take into account the current routes at the time of activation. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments.
-
- If zero a default MTU is used. Note that contrary to wg-quick's MTU setting, this does not take into account the current routes at the time of activation. Whether to automatically add routes for the AllowedIPs ranges of the peers. If TRUE (the default), NetworkManager will automatically add routes in the routing tables according to ipv4.route-table and ipv6.route-table. Usually you want this automatism enabled. If FALSE, no such routes are added automatically. In this case, the user may want to configure static routes in ipv4.routes and ipv6.routes, respectively. Note that if the peer's AllowedIPs is "0.0.0.0/0" or "::/0" and the profile's ipv4.never-default or ipv6.never-default setting is enabled, the peer route for this peer won't be added automatically. Whether to automatically add routes for the AllowedIPs ranges of the peers. If TRUE (the default), NetworkManager will automatically add routes in the routing tables according to ipv4.route-table and ipv6.route-table. Usually you want this automatism enabled. If FALSE, no such routes are added automatically. In this case, the user may want to configure static routes in ipv4.routes and ipv6.routes, respectively.
-
- Note that if the peer's AllowedIPs is "0.0.0.0/0" or "::/0" and the profile's ipv4.never-default or ipv6.never-default setting is enabled, the peer route for this peer won't be added automatically. The 256 bit private-key in base64 encoding. The 256 bit private-key in base64 encoding. Flags indicating how to handle the "private-key" property. Flags indicating how to handle the "private-key" property. Configures AP isolation, which prevents communication between wireless devices connected to this AP. This property can be set to a value different from NM_TERNARY_DEFAULT (-1) only when the interface is configured in AP mode. If set to NM_TERNARY_TRUE (1), devices are not able to communicate with each other. This increases security because it protects devices against attacks from other clients in the network. At the same time, it prevents devices to access resources on the same wireless networks as file shares, printers, etc. If set to NM_TERNARY_FALSE (0), devices can talk to each other. When set to NM_TERNARY_DEFAULT (-1), the global default is used; in case the global default is unspecified it is assumed to be NM_TERNARY_FALSE (0). Configures AP isolation, which prevents communication between wireless devices connected to this AP. This property can be set to a value different from NM_TERNARY_DEFAULT (-1) only when the interface is configured in AP mode.
-
- If set to NM_TERNARY_TRUE (1), devices are not able to communicate with each other. This increases security because it protects devices against attacks from other clients in the network. At the same time, it prevents devices to access resources on the same wireless networks as file shares, printers, etc.
-
- If set to NM_TERNARY_FALSE (0), devices can talk to each other.
-
- When set to NM_TERNARY_DEFAULT (-1), the global default is used; in case the global default is unspecified it is assumed to be NM_TERNARY_FALSE (0). 802.11 frequency band of the network.  One of "a" for 5GHz 802.11a or "bg" for 2.4GHz 802.11.  This will lock associations to the Wi-Fi network to the specific band, i.e. if "a" is specified, the device will not associate with the same network in the 2.4GHz band even if the network's settings are compatible.  This setting depends on specific driver capability and may not work with all drivers. 802.11 frequency band of the network.  One of "a" for 5GHz 802.11a or "bg" for 2.4GHz 802.11.  This will lock associations to the Wi-Fi network to the specific band, i.e. if "a" is specified, the device will not associate with the same network in the 2.4GHz band even if the network's settings are compatible.  This setting depends on specific driver capability and may not work with all drivers. If specified, directs the device to only associate with the given access point.  This capability is highly driver dependent and not supported by all devices.  Note: this property does not control the BSSID used when creating an Ad-Hoc network and is unlikely to in the future. Locking a client profile to a certain BSSID will prevent roaming and also disable background scanning. That can be useful, if there is only one access point for the SSID. If specified, directs the device to only associate with the given access point.  This capability is highly driver dependent and not supported by all devices.  Note: this property does not control the BSSID used when creating an Ad-Hoc network and is unlikely to in the future.
-
- Locking a client profile to a certain BSSID will prevent roaming and also disable background scanning. That can be useful, if there is only one access point for the SSID. Wireless channel to use for the Wi-Fi connection.  The device will only join (or create for Ad-Hoc networks) a Wi-Fi network on the specified channel.  Because channel numbers overlap between bands, this property also requires the "band" property to be set. Wireless channel to use for the Wi-Fi connection.  The device will only join (or create for Ad-Hoc networks) a Wi-Fi network on the specified channel.  Because channel numbers overlap between bands, this property also requires the "band" property to be set. If specified, request that the device use this MAC address instead. This is known as MAC cloning or spoofing. Beside explicitly specifying a MAC address, the special values "preserve", "permanent", "random" and "stable" are supported. "preserve" means not to touch the MAC address on activation. "permanent" means to use the permanent hardware address of the device. "random" creates a random MAC address on each connect. "stable" creates a hashed MAC address based on connection.stable-id and a machine dependent key. If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to "preserve" (older versions of NetworkManager may use a different default value). On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated "cloned-mac-address". If specified, request that the device use this MAC address instead. This is known as MAC cloning or spoofing.
-
- Beside explicitly specifying a MAC address, the special values "preserve", "permanent", "random" and "stable" are supported. "preserve" means not to touch the MAC address on activation. "permanent" means to use the permanent hardware address of the device. "random" creates a random MAC address on each connect. "stable" creates a hashed MAC address based on connection.stable-id and a machine dependent key.
-
- If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to "preserve" (older versions of NetworkManager may use a different default value).
-
- On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated "cloned-mac-address". With "cloned-mac-address" setting "random" or "stable", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address. If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address. If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the "random" or "stable" algorithm. If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled globally-administered, burned-in MAC address. If the value contains more than one additional MAC addresses, one of them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" will create a fully scrambled MAC address, randomly locally or globally administered. With "cloned-mac-address" setting "random" or "stable", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address.
-
- If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address.
-
- If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the "random" or "stable" algorithm.
-
- If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled globally-administered, burned-in MAC address.
-
- If the value contains more than one additional MAC addresses, one of them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" will create a fully scrambled MAC address, randomly locally or globally administered. If TRUE, indicates that the network is a non-broadcasting network that hides its SSID. This works both in infrastructure and AP mode. In infrastructure mode, various workarounds are used for a more reliable discovery of hidden networks, such as probe-scanning the SSID.  However, these workarounds expose inherent insecurities with hidden SSID networks, and thus hidden SSID networks should be used with caution. In AP mode, the created network does not broadcast its SSID. Note that marking the network as hidden may be a privacy issue for you (in infrastructure mode) or client stations (in AP mode), as the explicit probe-scans are distinctly recognizable on the air. If TRUE, indicates that the network is a non-broadcasting network that hides its SSID. This works both in infrastructure and AP mode.
-
- In infrastructure mode, various workarounds are used for a more reliable discovery of hidden networks, such as probe-scanning the SSID.  However, these workarounds expose inherent insecurities with hidden SSID networks, and thus hidden SSID networks should be used with caution.
-
- In AP mode, the created network does not broadcast its SSID.
-
- Note that marking the network as hidden may be a privacy issue for you (in infrastructure mode) or client stations (in AP mode), as the explicit probe-scans are distinctly recognizable on the air. If specified, this connection will only apply to the Wi-Fi device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing). If specified, this connection will only apply to the Wi-Fi device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing). A list of permanent MAC addresses of Wi-Fi devices to which this connection should never apply.  Each MAC address should be given in the standard hex-digits-and-colons notation (eg "00:11:22:33:44:55"). A list of permanent MAC addresses of Wi-Fi devices to which this connection should never apply.  Each MAC address should be given in the standard hex-digits-and-colons notation (eg "00:11:22:33:44:55"). One of NM_SETTING_MAC_RANDOMIZATION_DEFAULT (0) (never randomize unless the user has set a global default to randomize and the supplicant supports randomization),  NM_SETTING_MAC_RANDOMIZATION_NEVER (1) (never randomize the MAC address), or NM_SETTING_MAC_RANDOMIZATION_ALWAYS (2) (always randomize the MAC address). This property is deprecated for 'cloned-mac-address'. Deprecated: 1 One of NM_SETTING_MAC_RANDOMIZATION_DEFAULT (0) (never randomize unless the user has set a global default to randomize and the supplicant supports randomization),  NM_SETTING_MAC_RANDOMIZATION_NEVER (1) (never randomize the MAC address), or NM_SETTING_MAC_RANDOMIZATION_ALWAYS (2) (always randomize the MAC address). This property is deprecated for 'cloned-mac-address'. Deprecated: 1 Wi-Fi network mode; one of "infrastructure", "mesh", "adhoc" or "ap".  If blank, infrastructure is assumed. Wi-Fi network mode; one of "infrastructure", "mesh", "adhoc" or "ap".  If blank, infrastructure is assumed. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames. If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames. One of NM_SETTING_WIRELESS_POWERSAVE_DISABLE (2) (disable Wi-Fi power saving), NM_SETTING_WIRELESS_POWERSAVE_ENABLE (3) (enable Wi-Fi power saving), NM_SETTING_WIRELESS_POWERSAVE_IGNORE (1) (don't touch currently configure setting) or NM_SETTING_WIRELESS_POWERSAVE_DEFAULT (0) (use the globally configured value). All other values are reserved. One of NM_SETTING_WIRELESS_POWERSAVE_DISABLE (2) (disable Wi-Fi power saving), NM_SETTING_WIRELESS_POWERSAVE_ENABLE (3) (enable Wi-Fi power saving), NM_SETTING_WIRELESS_POWERSAVE_IGNORE (1) (don't touch currently configure setting) or NM_SETTING_WIRELESS_POWERSAVE_DEFAULT (0) (use the globally configured value). All other values are reserved. If non-zero, directs the device to only use the specified bitrate for communication with the access point.  Units are in Kb/s, ie 5500 = 5.5 Mbit/s.  This property is highly driver dependent and not all devices support setting a static bitrate. If non-zero, directs the device to only use the specified bitrate for communication with the access point.  Units are in Kb/s, ie 5500 = 5.5 Mbit/s.  This property is highly driver dependent and not all devices support setting a static bitrate. A list of BSSIDs (each BSSID formatted as a MAC address like "00:11:22:33:44:55") that have been detected as part of the Wi-Fi network.  NetworkManager internally tracks previously seen BSSIDs. The property is only meant for reading and reflects the BSSID list of NetworkManager. The changes you make to this property will not be preserved. A list of BSSIDs (each BSSID formatted as a MAC address like "00:11:22:33:44:55") that have been detected as part of the Wi-Fi network.  NetworkManager internally tracks previously seen BSSIDs. The property is only meant for reading and reflects the BSSID list of NetworkManager. The changes you make to this property will not be preserved. SSID of the Wi-Fi network. Must be specified. SSID of the Wi-Fi network. Must be specified. If non-zero, directs the device to use the specified transmit power. Units are dBm.  This property is highly driver dependent and not all devices support setting a static transmit power. If non-zero, directs the device to use the specified transmit power. Units are dBm.  This property is highly driver dependent and not all devices support setting a static transmit power. The NMSettingWirelessWakeOnWLan options to enable. Not all devices support all options. May be any combination of NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY (0x2), NM_SETTING_WIRELESS_WAKE_ON_WLAN_DISCONNECT (0x4), NM_SETTING_WIRELESS_WAKE_ON_WLAN_MAGIC (0x8), NM_SETTING_WIRELESS_WAKE_ON_WLAN_GTK_REKEY_FAILURE (0x10), NM_SETTING_WIRELESS_WAKE_ON_WLAN_EAP_IDENTITY_REQUEST (0x20), NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE (0x40), NM_SETTING_WIRELESS_WAKE_ON_WLAN_RFKILL_RELEASE (0x80), NM_SETTING_WIRELESS_WAKE_ON_WLAN_TCP (0x100) or the special values NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT (0x1) (to use global settings) and NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE (0x8000) (to disable management of Wake-on-LAN in NetworkManager). The NMSettingWirelessWakeOnWLan options to enable. Not all devices support all options. May be any combination of NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY (0x2), NM_SETTING_WIRELESS_WAKE_ON_WLAN_DISCONNECT (0x4), NM_SETTING_WIRELESS_WAKE_ON_WLAN_MAGIC (0x8), NM_SETTING_WIRELESS_WAKE_ON_WLAN_GTK_REKEY_FAILURE (0x10), NM_SETTING_WIRELESS_WAKE_ON_WLAN_EAP_IDENTITY_REQUEST (0x20), NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE (0x40), NM_SETTING_WIRELESS_WAKE_ON_WLAN_RFKILL_RELEASE (0x80), NM_SETTING_WIRELESS_WAKE_ON_WLAN_TCP (0x100) or the special values NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT (0x1) (to use global settings) and NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE (0x8000) (to disable management of Wake-on-LAN in NetworkManager). When WEP is used (ie, key-mgmt = "none" or "ieee8021x") indicate the 802.11 authentication algorithm required by the AP here.  One of "open" for Open System, "shared" for Shared Key, or "leap" for Cisco LEAP.  When using Cisco LEAP (ie, key-mgmt = "ieee8021x" and auth-alg = "leap") the "leap-username" and "leap-password" properties must be specified. When WEP is used (ie, key-mgmt = "none" or "ieee8021x") indicate the 802.11 authentication algorithm required by the AP here.  One of "open" for Open System, "shared" for Shared Key, or "leap" for Cisco LEAP.  When using Cisco LEAP (ie, key-mgmt = "ieee8021x" and auth-alg = "leap") the "leap-username" and "leap-password" properties must be specified. Indicates whether Fast Initial Link Setup (802.11ai) must be enabled for the connection.  One of NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) (use global default value), NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE (1) (disable FILS), NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL (2) (enable FILS if the supplicant and the access point support it) or NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED (3) (enable FILS and fail if not supported).  When set to NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) and no global default is set, FILS will be optionally enabled. Indicates whether Fast Initial Link Setup (802.11ai) must be enabled for the connection.  One of NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) (use global default value), NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE (1) (disable FILS), NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL (2) (enable FILS if the supplicant and the access point support it) or NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED (3) (enable FILS and fail if not supported).  When set to NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (0) and no global default is set, FILS will be optionally enabled. A list of group/broadcast encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list.  For maximum compatibility leave this property empty.  Each list element may be one of "wep40", "wep104", "tkip", or "ccmp". A list of group/broadcast encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list.  For maximum compatibility leave this property empty.  Each list element may be one of "wep40", "wep104", "tkip", or "ccmp". Key management used for the connection. One of "none" (WEP or no password protection), "ieee8021x" (Dynamic WEP), "owe" (Opportunistic Wireless Encryption), "wpa-psk" (WPA2 + WPA3 personal), "sae" (WPA3 personal only), "wpa-eap" (WPA2 + WPA3 enterprise) or "wpa-eap-suite-b-192" (WPA3 enterprise only). This property must be set for any Wi-Fi connection that uses security. Key management used for the connection. One of "none" (WEP or no password protection), "ieee8021x" (Dynamic WEP), "owe" (Opportunistic Wireless Encryption), "wpa-psk" (WPA2 + WPA3 personal), "sae" (WPA3 personal only), "wpa-eap" (WPA2 + WPA3 enterprise) or "wpa-eap-suite-b-192" (WPA3 enterprise only).
-
- This property must be set for any Wi-Fi connection that uses security. The login password for legacy LEAP connections (ie, key-mgmt = "ieee8021x" and auth-alg = "leap"). The login password for legacy LEAP connections (ie, key-mgmt = "ieee8021x" and auth-alg = "leap"). Flags indicating how to handle the "leap-password" property. Flags indicating how to handle the "leap-password" property. The login username for legacy LEAP connections (ie, key-mgmt = "ieee8021x" and auth-alg = "leap"). The login username for legacy LEAP connections (ie, key-mgmt = "ieee8021x" and auth-alg = "leap"). A list of pairwise encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list. For maximum compatibility leave this property empty.  Each list element may be one of "tkip" or "ccmp". A list of pairwise encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list. For maximum compatibility leave this property empty.  Each list element may be one of "tkip" or "ccmp". Indicates whether Protected Management Frames (802.11w) must be enabled for the connection.  One of NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) (use global default value), NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE (1) (disable PMF), NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL (2) (enable PMF if the supplicant and the access point support it) or NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED (3) (enable PMF and fail if not supported).  When set to NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) and no global default is set, PMF will be optionally enabled. Indicates whether Protected Management Frames (802.11w) must be enabled for the connection.  One of NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) (use global default value), NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE (1) (disable PMF), NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL (2) (enable PMF if the supplicant and the access point support it) or NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED (3) (enable PMF and fail if not supported).  When set to NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT (0) and no global default is set, PMF will be optionally enabled. List of strings specifying the allowed WPA protocol versions to use. Each element may be one "wpa" (allow WPA) or "rsn" (allow WPA2/RSN).  If not specified, both WPA and RSN connections are allowed. List of strings specifying the allowed WPA protocol versions to use. Each element may be one "wpa" (allow WPA) or "rsn" (allow WPA2/RSN).  If not specified, both WPA and RSN connections are allowed. Pre-Shared-Key for WPA networks. For WPA-PSK, it's either an ASCII passphrase of 8 to 63 characters that is (as specified in the 802.11i standard) hashed to derive the actual key, or the key in form of 64 hexadecimal character. The WPA3-Personal networks use a passphrase of any length for SAE authentication. Pre-Shared-Key for WPA networks. For WPA-PSK, it's either an ASCII passphrase of 8 to 63 characters that is (as specified in the 802.11i standard) hashed to derive the actual key, or the key in form of 64 hexadecimal character. The WPA3-Personal networks use a passphrase of any length for SAE authentication. Flags indicating how to handle the "psk" property. Flags indicating how to handle the "psk" property. Flags indicating how to handle the "wep-key0", "wep-key1", "wep-key2", and "wep-key3" properties. Flags indicating how to handle the "wep-key0", "wep-key1", "wep-key2", and "wep-key3" properties. Controls the interpretation of WEP keys.  Allowed values are NM_WEP_KEY_TYPE_KEY (1), in which case the key is either a 10- or 26-character hexadecimal string, or a 5- or 13-character ASCII password; or NM_WEP_KEY_TYPE_PASSPHRASE (2), in which case the passphrase is provided as a string and will be hashed using the de-facto MD5 method to derive the actual WEP key. Controls the interpretation of WEP keys.  Allowed values are NM_WEP_KEY_TYPE_KEY (1), in which case the key is either a 10- or 26-character hexadecimal string, or a 5- or 13-character ASCII password; or NM_WEP_KEY_TYPE_PASSPHRASE (2), in which case the passphrase is provided as a string and will be hashed using the de-facto MD5 method to derive the actual WEP key. Index 0 WEP key.  This is the WEP key used in most networks.  See the "wep-key-type" property for a description of how this key is interpreted. Index 0 WEP key.  This is the WEP key used in most networks.  See the "wep-key-type" property for a description of how this key is interpreted. Index 1 WEP key.  This WEP index is not used by most networks.  See the "wep-key-type" property for a description of how this key is interpreted. Index 1 WEP key.  This WEP index is not used by most networks.  See the "wep-key-type" property for a description of how this key is interpreted. Index 2 WEP key.  This WEP index is not used by most networks.  See the "wep-key-type" property for a description of how this key is interpreted. Index 2 WEP key.  This WEP index is not used by most networks.  See the "wep-key-type" property for a description of how this key is interpreted. Index 3 WEP key.  This WEP index is not used by most networks.  See the "wep-key-type" property for a description of how this key is interpreted. Index 3 WEP key.  This WEP index is not used by most networks.  See the "wep-key-type" property for a description of how this key is interpreted. When static WEP is used (ie, key-mgmt = "none") and a non-default WEP key index is used by the AP, put that WEP key index here.  Valid values are 0 (default key) through 3.  Note that some consumer access points (like the Linksys WRT54G) number the keys 1 - 4. When static WEP is used (ie, key-mgmt = "none") and a non-default WEP key index is used by the AP, put that WEP key index here.  Valid values are 0 (default key) through 3.  Note that some consumer access points (like the Linksys WRT54G) number the keys 1 - 4. Flags indicating which mode of WPS is to be used if any. There's little point in changing the default setting as NetworkManager will automatically determine whether it's feasible to start WPS enrollment from the Access Point capabilities. WPS can be disabled by setting this property to a value of 1. Flags indicating which mode of WPS is to be used if any.
-
- There's little point in changing the default setting as NetworkManager will automatically determine whether it's feasible to start WPS enrollment from the Access Point capabilities.
-
- WPS can be disabled by setting this property to a value of 1. IEEE 802.15.4 channel. A positive integer or -1, meaning "do not set, use whatever the device is already set to". IEEE 802.15.4 channel. A positive integer or -1, meaning "do not set, use whatever the device is already set to". If specified, this connection will only apply to the IEEE 802.15.4 (WPAN) MAC layer device whose permanent MAC address matches. If specified, this connection will only apply to the IEEE 802.15.4 (WPAN) MAC layer device whose permanent MAC address matches. IEEE 802.15.4 channel page. A positive integer or -1, meaning "do not set, use whatever the device is already set to". IEEE 802.15.4 channel page. A positive integer or -1, meaning "do not set, use whatever the device is already set to". IEEE 802.15.4 Personal Area Network (PAN) identifier. IEEE 802.15.4 Personal Area Network (PAN) identifier. Short IEEE 802.15.4 address to be used within a restricted environment. Short IEEE 802.15.4 address to be used within a restricted environment. The queue ID of this bond port. The maximum value of queue ID is the number of TX queues currently active in device. The queue ID of this bond port. The maximum value of queue ID is the number of TX queues currently active in device. Whether the system hostname can be determined from DHCP on this connection. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1). Whether the system hostname can be determined from DHCP on this connection.
-
- When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1). Whether the system hostname can be determined from reverse DNS lookup of addresses on this device. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1). Whether the system hostname can be determined from reverse DNS lookup of addresses on this device.
-
- When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1). If set to NM_TERNARY_TRUE (1), NetworkManager attempts to get the hostname via DHCPv4/DHCPv6 or reverse DNS lookup on this device only when the device has the default route for the given address family (IPv4/IPv6). If set to NM_TERNARY_FALSE (0), the hostname can be set from this device even if it doesn't have the default route. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_FALSE (0). If set to NM_TERNARY_TRUE (1), NetworkManager attempts to get the hostname via DHCPv4/DHCPv6 or reverse DNS lookup on this device only when the device has the default route for the given address family (IPv4/IPv6).
-
- If set to NM_TERNARY_FALSE (0), the hostname can be set from this device even if it doesn't have the default route.
-
- When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_FALSE (0). The relative priority of this connection to determine the system hostname. A lower numerical value is better (higher priority).  A connection with higher priority is considered before connections with lower priority. If the value is zero, it can be overridden by a global value from NetworkManager configuration. If the property doesn't have a value in the global configuration, the value is assumed to be 100. Negative values have the special effect of excluding other connections with a greater numerical priority value; so in presence of at least one negative priority, only connections with the lowest priority value will be used to determine the hostname. The relative priority of this connection to determine the system hostname. A lower numerical value is better (higher priority).  A connection with higher priority is considered before connections with lower priority.
-
- If the value is zero, it can be overridden by a global value from NetworkManager configuration. If the property doesn't have a value in the global configuration, the value is assumed to be 100.
-
- Negative values have the special effect of excluding other connections with a greater numerical priority value; so in presence of at least one negative priority, only connections with the lowest priority value will be used to determine the hostname. A dictionary of key/value pairs with exernal-ids for OVS. A dictionary of key/value pairs with exernal-ids for OVS. This property specifies the peer interface name of the veth. This property is mandatory. This property specifies the peer interface name of the veth. This property is mandatory.
\ No newline at end of file
diff --git a/src/libnmc-setting/settings-docs.h b/src/libnmc-setting/settings-docs.h
index 208cad86..b7f1dc24 100644
--- a/src/libnmc-setting/settings-docs.h
+++ b/src/libnmc-setting/settings-docs.h
@@ -24,7 +24,7 @@
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_STABLE_ID N_("This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual about the secret-key and the host identity. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are \"${CONNECTION}\", \"${DEVICE}\", \"${MAC}\", \"${BOOT}\", \"${RANDOM}\". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that \"${DEVICE}\" corresponds to the interface name of the device and \"${MAC}\" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as \"$$\". For example, set it to \"${CONNECTION}-${BOOT}-${DEVICE}\" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to \"${CONNECTION}\" and uses a unique, fixed ID for the connection.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TIMESTAMP N_("The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved).")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TYPE N_("Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, \"802-3-ethernet\" or \"802-11-wireless\" or \"bluetooth\", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, \"vpn\" or \"bridge\", etc).")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_UUID N_("A universally unique identifier for the connection, for example generated with libuuid.  It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network.  For example, it should not be changed when the \"id\" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or \"type\" property changes. The UUID must be in the format \"2815492f-7e56-435e-b2e9-246bd7cdc664\" (ie, contains only hexadecimal characters and \"-\").")
+#define DESCRIBE_DOC_NM_SETTING_CONNECTION_UUID N_("The connection.uuid is the real identifier of a profile. It cannot change and it must be unique. It is therefore often best to refer to a profile by UUID, for example with `nmcli connection up uuid $UUID`. The UUID cannot be changed, except in offline mode. In that case, the special values \"new\", \"generate\" and \"\" are allowed to generate a new random UUID.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY N_("Time in milliseconds to wait for connection to be considered activated. The wait will start after the pre-up dispatcher event. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT N_("Timeout in milliseconds to wait for device at startup. During boot, devices may take a while to be detected by the driver. This property will cause to delay NetworkManager-wait-online.service and nm-online to give the device a chance to appear. This works by waiting for the given timeout until a compatible device for the profile is available and managed. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_ZONE N_("The trust level of a the connection.  Free form case-insensitive string (for example \"Home\", \"Work\", \"Public\").  NULL or unspecified zone means the connection will be placed in the default zone as defined by the firewall. When updating this property on a currently activated connection, the change takes effect immediately.")
@@ -49,7 +49,7 @@
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PASSWORD_FLAGS N_("Flags indicating how to handle the \"password\" property.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PASSWORD_RAW N_("Password used for EAP authentication methods, given as a byte array to allow passwords in other encodings than UTF-8 to be used. If both the \"password\" property and the \"password-raw\" property are specified, \"password\" is preferred.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PASSWORD_RAW_FLAGS N_("Flags indicating how to handle the \"password-raw\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE1_AUTH_FLAGS N_("Specifies authentication flags to use in \"phase 1\" outer authentication using NMSetting8021xAuthFlags options. The individual TLS versions can be explicitly disabled. If a certain TLS disable flag is not set, it is up to the supplicant to allow or forbid it. The TLS options map to tls_disable_tlsv1_x settings. See the wpa_supplicant documentation for more details.")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE1_AUTH_FLAGS N_("Specifies authentication flags to use in \"phase 1\" outer authentication using NMSetting8021xAuthFlags options. The individual TLS versions can be explicitly disabled. TLS time checks can be also disabled. If a certain TLS disable flag is not set, it is up to the supplicant to allow or forbid it. The TLS options map to tls_disable_tlsv1_x and tls_disable_time_checks settings. See the wpa_supplicant documentation for more details.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING N_("Enables or disables in-line provisioning of EAP-FAST credentials when FAST is specified as the EAP method in the \"eap\" property. Recognized values are \"0\" (disabled), \"1\" (allow unauthenticated provisioning), \"2\" (allow authenticated provisioning), and \"3\" (allow both authenticated and unauthenticated provisioning).  See the wpa_supplicant documentation for more details.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE1_PEAPLABEL N_("Forces use of the new PEAP label during key derivation.  Some RADIUS servers may require forcing the new PEAP label to interoperate with PEAPv1.  Set to \"1\" to force use of the new PEAP label.  See the wpa_supplicant documentation for more details.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE1_PEAPVER N_("Forces which PEAP version is used when PEAP is set as the EAP method in the \"eap\" property.  When unset, the version reported by the server will be used.  Sometimes when using older RADIUS servers, it is necessary to force the client to use a particular PEAP version.  To do so, this property may be set to \"0\" or \"1\" to force that specific PEAP version.")
@@ -68,13 +68,13 @@
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_PRIVATE_KEY N_("Contains the \"phase 2\" inner private key when the \"phase2-auth\" or \"phase2-autheap\" property is set to \"tls\". Key data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the \"phase2-private-key-password\" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte, and as with the blob scheme the \"phase2-private-key-password\" property must be set to the password used to decode the PKCS#12 private key and certificate.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD N_("The password used to decrypt the \"phase 2\" private key specified in the \"phase2-private-key\" property when the private key either uses the path scheme, or is a PKCS#12 format key.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS N_("Flags indicating how to handle the \"phase2-private-key-password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH N_("Substring to be matched against the subject of the certificate presented by the authentication server during the inner \"phase 2\" authentication. When unset, no verification of the authentication server certificate's subject is performed.  This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:phase2-domain-suffix-match.")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH N_("Substring to be matched against the subject of the certificate presented by the authentication server during the inner \"phase 2\" authentication. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and should not be used.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PIN N_("PIN used for EAP authentication methods.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PIN_FLAGS N_("Flags indicating how to handle the \"pin\" property.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PRIVATE_KEY N_("Contains the private key when the \"eap\" property is set to \"tls\". Key data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the \"private-key-password\" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte, and as with the blob scheme the \"private-key-password\" property must be set to the password used to decode the PKCS#12 private key and certificate. WARNING: \"private-key\" is not a \"secret\" property, and thus unencrypted private key data using the BLOB scheme may be readable by unprivileged users.  Private keys should always be encrypted with a private key password to prevent unauthorized access to unencrypted private key data.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD N_("The password used to decrypt the private key specified in the \"private-key\" property when the private key either uses the path scheme, or if the private key is a PKCS#12 format key.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS N_("Flags indicating how to handle the \"private-key-password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_SUBJECT_MATCH N_("Substring to be matched against the subject of the certificate presented by the authentication server. When unset, no verification of the authentication server certificate's subject is performed.  This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:domain-suffix-match.")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_SUBJECT_MATCH N_("Substring to be matched against the subject of the certificate presented by the authentication server. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and should not be used.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_SYSTEM_CA_CERTS N_("When TRUE, overrides the \"ca-path\" and \"phase2-ca-path\" properties using the system CA directory specified at configure time with the --system-ca-path switch.  The certificates in this directory are added to the verification chain in addition to any certificates specified by the \"ca-cert\" and \"phase2-ca-cert\" properties. If the path provided with --system-ca-path is rather a file name (bundle of trusted CA certificates), it overrides \"ca-cert\" and \"phase2-ca-cert\" properties instead (sets ca_cert/ca_cert2 options for wpa_supplicant).")
 #define DESCRIBE_DOC_NM_SETTING_ADSL_ENCAPSULATION N_("Encapsulation of ADSL connection.  Can be \"vcmux\" or \"llc\".")
 #define DESCRIBE_DOC_NM_SETTING_ADSL_PASSWORD N_("Password used to authenticate with the ADSL service.")
@@ -91,7 +91,7 @@
 #define DESCRIBE_DOC_NM_SETTING_BRIDGE_GROUP_ADDRESS N_("If specified, The MAC address of the multicast group this bridge uses for STP. The address must be a link-local address in standard Ethernet MAC address format, ie an address of the form 01:80:C2:00:00:0X, with X in [0, 4..F]. If not specified the default value is 01:80:C2:00:00:00.")
 #define DESCRIBE_DOC_NM_SETTING_BRIDGE_GROUP_FORWARD_MASK N_("A mask of group addresses to forward. Usually, group addresses in the range from 01:80:C2:00:00:00 to 01:80:C2:00:00:0F are not forwarded according to standards. This property is a mask of 16 bits, each corresponding to a group address in that range that must be forwarded. The mask can't have bits 0, 1 or 2 set because they are used for STP, MAC pause frames and LACP.")
 #define DESCRIBE_DOC_NM_SETTING_BRIDGE_HELLO_TIME N_("The Spanning Tree Protocol (STP) hello time, in seconds.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MAC_ADDRESS N_("If specified, the MAC address of bridge. When creating a new bridge, this MAC address will be set. If this field is left unspecified, the \"ethernet.cloned-mac-address\" is referred instead to generate the initial MAC address. Note that setting \"ethernet.cloned-mac-address\" anyway overwrites the MAC address of the bridge later while activating the bridge. Hence, this property is deprecated. Deprecated: 1")
+#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MAC_ADDRESS N_("If specified, the MAC address of bridge. When creating a new bridge, this MAC address will be set. If this field is left unspecified, the \"ethernet.cloned-mac-address\" is referred instead to generate the initial MAC address. Note that setting \"ethernet.cloned-mac-address\" anyway overwrites the MAC address of the bridge later while activating the bridge.")
 #define DESCRIBE_DOC_NM_SETTING_BRIDGE_MAX_AGE N_("The Spanning Tree Protocol (STP) maximum message age, in seconds.")
 #define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_HASH_MAX N_("Set maximum size of multicast hash table (value must be a power of 2).")
 #define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_LAST_MEMBER_COUNT N_("Set the number of queries the bridge will send before stopping forwarding a multicast group after a \"leave\" message has been received.")
@@ -143,7 +143,7 @@
 #define DESCRIBE_DOC_NM_SETTING_GSM_HOME_ONLY N_("When TRUE, only connections to the home network will be allowed. Connections to roaming networks will not be made.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_NETWORK_ID N_("The Network ID (GSM LAI format, ie MCC-MNC) to force specific network registration.  If the Network ID is specified, NetworkManager will attempt to force the device to register only on the specified network. This can be used to ensure that the device does not roam when direct roaming control of the device is not otherwise possible.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_NUMBER N_("Legacy setting that used to help establishing PPP data sessions for GSM-based modems. Deprecated: 1")
+#define DESCRIBE_DOC_NM_SETTING_GSM_NUMBER N_("Legacy setting that used to help establishing PPP data sessions for GSM-based modems.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_PASSWORD N_("The password used to authenticate with the network, if required.  Many providers do not require a password, or accept any password.  But if a password is required, it is specified here.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_PASSWORD_FLAGS N_("Flags indicating how to handle the \"password\" property.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_PIN N_("If the SIM is locked with a PIN it must be unlocked before any other operations are requested.  Specify the PIN here to allow operation of the device.")
@@ -157,6 +157,7 @@
 #define DESCRIBE_DOC_NM_SETTING_INFINIBAND_PARENT N_("The interface name of the parent device of this device. Normally NULL, but if the \"p_key\" property is set, then you must specify the base device by setting either this property or \"mac-address\".")
 #define DESCRIBE_DOC_NM_SETTING_INFINIBAND_TRANSPORT_MODE N_("The IP-over-InfiniBand transport mode. Either \"datagram\" or \"connected\".")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ADDRESSES N_("A list of IPv4 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"192.168.1.5/24, 10.1.0.5/24\". The addresses are listed in decreasing priority, meaning the first address will be the primary address.")
+#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_AUTO_ROUTE_EXT_GW N_("VPN connections will default to add the route automatically unless this setting is set to FALSE. For other connection types, adding such an automatic route is currently not supported and setting this to TRUE has no effect.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DAD_TIMEOUT N_("Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network.  If an address conflict is detected, the activation will fail.  A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero).  A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID N_("A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string ('aa:bb:cc') it is interpreted as a binary client ID, in which case the first byte is assumed to be the 'type' field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the 'type' field is set to 0. The special values \"mac\" and \"perm-mac\" are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. The special value \"ipv6-duid\" uses the DUID from \"ipv6.dhcp-duid\" property as an RFC4361-compliant client identifier. As IAID it uses \"ipv4.dhcp-iaid\" and falls back to \"ipv6.dhcp-iaid\" if unset. The special value \"duid\" generates a RFC4361-compliant client identifier based on \"ipv4.dhcp-iaid\" and uses a DUID generated by hashing /etc/machine-id. The special value \"stable\" is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the \"${DEVICE}\" or \"${MAC}\" specifier to get a per-device key. If unset, a globally configured default is used. If still unset, the default depends on the DHCP plugin.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_FQDN N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified FQDN will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-hostname\" are mutually exclusive and cannot be set at the same time.")
@@ -167,7 +168,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME N_("If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer.  If the \"dhcp-hostname\" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_TIMEOUT N_("A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER N_("The Vendor Class Identifier DHCP option (60). Special characters in the data string may be escaped using C-style escapes, nevertheless this property cannot contain nul bytes. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the DHCP option is not sent to the server.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS N_("Array of IP addresses of DNS servers.")
+#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS N_("Array of IP addresses of DNS servers. For DoT (DNS over TLS), the SNI server name can be specified by appending \"#example.com\" to the IP address of the DNS server. This currently only has effect when using systemd-resolved.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_OPTIONS N_("Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties. The currently supported options are \"attempts\", \"debug\", \"edns0\", \"inet6\", \"ip6-bytestring\", \"ip6-dotint\", \"ndots\", \"no-check-names\", \"no-ip6-dotint\", \"no-reload\", \"no-tld-query\", \"rotate\", \"single-request\", \"single-request-reopen\", \"timeout\", \"trust-ad\", \"use-vc\". The \"trust-ad\" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have \"trust-ad\" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then \"edns0\" and \"trust-ad\" are automatically added.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the \"rotate\" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins.  If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_SEARCH N_("List of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting. When set on a profile that also enabled DHCP, the DNS search list received automatically (option 119 for DHCPv4 and option 24 for DHCPv6) gets merged with the manual list. This can be prevented by setting \"ignore-auto-dns\". Note that if no DNS searches are configured, the fallback will be derived from the domain from DHCP (option 15).")
@@ -185,6 +186,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTING_RULES N_("A comma separated list of routing rules for policy routing.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE N_("Configure method for creating the address for use with RFC4862 IPv6 Stateless Address Autoconfiguration. The permitted values are: NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 (0), NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY (1). NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT_OR_EUI64 (2) or NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT (3). If the property is set to EUI64, the addresses will be generated using the interface tokens derived from hardware address. This makes the host part of the address to stay constant, making it possible to track host's presence when it changes networks. The address changes when the interface hardware is replaced. The value of stable-privacy enables use of cryptographically secure hash of a secret host-specific key along with the connection's stable-id and the network address as specified by RFC7217. This makes it impossible to use the address track host's presence, and makes the address stable when the network interface hardware is replaced. The special values \"default\" and \"default-or-eui64\" will fallback to the global connection default in as documented in NetworkManager.conf(5) manual. If the global default is not specified, the fallback value is \"stable-privacy\" or \"eui64\", respectively. For libnm, the property defaults to \"default\" since 1.40. Previously it defaulted to \"stable-privacy\". On D-Bus, the absence of an addr-gen-mode setting equals \"default\". For keyfile plugin, the absence of the setting on disk means \"default-or-eui64\" so that the property doesn't change on upgrade from older versions. Note that this setting is distinct from the Privacy Extensions as configured by \"ip6-privacy\" property and it does not affect the temporary addresses configured with this option.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDRESSES N_("A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64\". The addresses are listed in decreasing priority, meaning the first address will be the primary address. This can make a difference with IPv6 source address selection (RFC 6724, section 5).")
+#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_AUTO_ROUTE_EXT_GW N_("VPN connections will default to add the route automatically unless this setting is set to FALSE. For other connection types, adding such an automatic route is currently not supported and setting this to TRUE has no effect.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DAD_TIMEOUT N_("Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network.  If an address conflict is detected, the activation will fail.  A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero).  A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_DUID N_("A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp client to identify itself to DHCPv6 servers (RFC 3315). The DUID is carried in the Client Identifier option. If the property is a hex string ('aa:bb:cc') it is interpreted as a binary DUID and filled as an opaque value in the Client Identifier option. The special value \"lease\" will retrieve the DUID previously used from the lease file belonging to the connection. If no DUID is found and \"dhclient\" is the configured dhcp client, the DUID is searched in the system-wide dhclient lease file. If still no DUID is found, or another dhcp client is used, a global and permanent DUID-UUID (RFC 6355) will be generated based on the machine-id. The special values \"llt\" and \"ll\" will generate a DUID of type LLT or LL (see RFC 3315) based on the current MAC address of the device. In order to try providing a stable DUID-LLT, the time field will contain a constant timestamp that is used globally (for all profiles) and persisted to disk. The special values \"stable-llt\", \"stable-ll\" and \"stable-uuid\" will generate a DUID of the corresponding type, derived from the connection's stable-id and a per-host unique key. You may want to include the \"${DEVICE}\" or \"${MAC}\" specifier in the stable-id, in case this profile gets activated on multiple devices. So, the link-layer address of \"stable-ll\" and \"stable-llt\" will be a generated address derived from the stable id. The DUID-LLT time value in the \"stable-llt\" option will be picked among a static timespan of three years (the upper bound of the interval is the same constant timestamp used in \"llt\"). When the property is unset, the global value provided for \"ipv6.dhcp-duid\" is used. If no global value is provided, the default \"lease\" value is assumed.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-fqdn\" are mutually exclusive and cannot be set at the same time.")
@@ -193,7 +195,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_REJECT_SERVERS N_("Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. \"192.168.122.0/24\"). This property is currently not implemented for DHCPv6.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_SEND_HOSTNAME N_("If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer.  If the \"dhcp-hostname\" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_TIMEOUT N_("A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS N_("Array of IP addresses of DNS servers.")
+#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS N_("Array of IP addresses of DNS servers. For DoT (DNS over TLS), the SNI server name can be specified by appending \"#example.com\" to the IP address of the DNS server. This currently only has effect when using systemd-resolved.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_OPTIONS N_("Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties. The currently supported options are \"attempts\", \"debug\", \"edns0\", \"inet6\", \"ip6-bytestring\", \"ip6-dotint\", \"ndots\", \"no-check-names\", \"no-ip6-dotint\", \"no-reload\", \"no-tld-query\", \"rotate\", \"single-request\", \"single-request-reopen\", \"timeout\", \"trust-ad\", \"use-vc\". The \"trust-ad\" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have \"trust-ad\" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then \"edns0\" and \"trust-ad\" are automatically added.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the \"rotate\" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins.  If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_SEARCH N_("List of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting. When set on a profile that also enabled DHCP, the DNS search list received automatically (option 119 for DHCPv4 and option 24 for DHCPv6) gets merged with the manual list. This can be prevented by setting \"ignore-auto-dns\". Note that if no DNS searches are configured, the fallback will be derived from the domain from DHCP (option 15).")
@@ -215,6 +217,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT N_("How many additional levels of encapsulation are permitted to be prepended to packets. This property applies only to IPv6 tunnels.")
 #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_FLAGS N_("Tunnel flags. Currently, the following values are supported: NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT (0x1), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS (0x2), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL (0x4), NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV (0x8), NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY (0x10), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK (0x20). They are valid only for IPv6 tunnels.")
 #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_FLOW_LABEL N_("The flow label to assign to tunnel packets. This property applies only to IPv6 tunnels.")
+#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_FWMARK N_("The fwmark value to assign to tunnel packets. This property can be set to a non zero value only on VTI and VTI6 tunnels.")
 #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_INPUT_KEY N_("The key used for tunnel input packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used.")
 #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_LOCAL N_("The local endpoint of the tunnel; the value can be empty, otherwise it must contain an IPv4 or IPv6 address.")
 #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_MODE N_("The tunneling mode, for example NM_IP_TUNNEL_MODE_IPIP (1) or NM_IP_TUNNEL_MODE_GRE (2).")
@@ -252,6 +255,9 @@
 #define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_STP_ENABLE N_("Enable or disable STP.")
 #define DESCRIBE_DOC_NM_SETTING_OVS_DPDK_DEVARGS N_("Open vSwitch DPDK device arguments.")
 #define DESCRIBE_DOC_NM_SETTING_OVS_DPDK_N_RXQ N_("Open vSwitch DPDK number of rx queues. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures one queue.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_DPDK_N_RXQ_DESC N_("The rx queue size (number of rx descriptors) for DPDK ports. Must be zero or a power of 2 between 1 and 4096, and supported by the hardware. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures 2048 descriptors.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_DPDK_N_TXQ_DESC N_("The tx queue size (number of tx descriptors) for DPDK ports. Must be zero or a power of 2 between 1 and 4096, and supported by the hardware. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures 2048 descriptors.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_INTERFACE_OFPORT_REQUEST N_("Open vSwitch openflow port number. Defaults to zero which means that port number will not be specified and it will be chosen randomly by ovs. OpenFlow ports are the network interfaces for passing packets between OpenFlow processing and the rest of the network. OpenFlow switches connect logically to each other via their OpenFlow ports.")
 #define DESCRIBE_DOC_NM_SETTING_OVS_INTERFACE_TYPE N_("The interface type. Either \"internal\", \"system\", \"patch\", \"dpdk\", or empty.")
 #define DESCRIBE_DOC_NM_SETTING_OVS_PATCH_PEER N_("Specifies the name of the interface for the other side of the patch. The patch on the other side must also set this interface as peer.")
 #define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_DOWNDELAY N_("The time port must be inactive in order to be considered down.")
@@ -259,7 +265,8 @@
 #define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_UPDELAY N_("The time port must be active before it starts forwarding traffic.")
 #define DESCRIBE_DOC_NM_SETTING_OVS_PORT_LACP N_("LACP mode. One of \"active\", \"off\", or \"passive\".")
 #define DESCRIBE_DOC_NM_SETTING_OVS_PORT_TAG N_("The VLAN tag in the range 0-4095.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_VLAN_MODE N_("The VLAN mode. One of \"access\", \"native-tagged\", \"native-untagged\", \"trunk\" or unset.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_TRUNKS N_("A list of VLAN ranges that this port trunks. The property is valid only for ports with mode \"trunk\", \"native-tagged\", or \"native-untagged port\". If it is empty, the port trunks all VLANs.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_VLAN_MODE N_("The VLAN mode. One of \"access\", \"native-tagged\", \"native-untagged\", \"trunk\", \"dot1q-tunnel\" or unset.")
 #define DESCRIBE_DOC_NM_SETTING_PPP_BAUD N_("If non-zero, instruct pppd to set the serial port to the specified baudrate.  This value should normally be left as 0 to automatically choose the speed.")
 #define DESCRIBE_DOC_NM_SETTING_PPP_CRTSCTS N_("If TRUE, specify that pppd should set the serial port to use hardware flow control with RTS and CTS signals.  This value should normally be set to FALSE.")
 #define DESCRIBE_DOC_NM_SETTING_PPP_LCP_ECHO_FAILURE N_("If non-zero, instruct pppd to presume the connection to the peer has failed if the specified number of LCP echo-requests go unanswered by the peer.  The \"lcp-echo-interval\" property must also be set to a non-zero value if this property is used.")
@@ -332,6 +339,7 @@
 #define DESCRIBE_DOC_NM_SETTING_VLAN_ID N_("The VLAN identifier that the interface created by this connection should be assigned. The valid range is from 0 to 4094, without the reserved id 4095.")
 #define DESCRIBE_DOC_NM_SETTING_VLAN_INGRESS_PRIORITY_MAP N_("For incoming packets, a list of mappings from 802.1p priorities to Linux SKB priorities.  The mapping is given in the format \"from:to\" where both \"from\" and \"to\" are unsigned integers, ie \"7:3\".")
 #define DESCRIBE_DOC_NM_SETTING_VLAN_PARENT N_("If given, specifies the parent interface name or parent connection UUID from which this VLAN interface should be created.  If this property is not specified, the connection must contain an \"802-3-ethernet\" setting with a \"mac-address\" property.")
+#define DESCRIBE_DOC_NM_SETTING_VLAN_PROTOCOL N_("Specifies the VLAN protocol to use for encapsulation. Supported values are: '802.1Q', '802.1ad'. If not specified the default value is '802.1Q'.")
 #define DESCRIBE_DOC_NM_SETTING_VPN_DATA N_("Dictionary of key/value pairs of VPN plugin specific data.  Both keys and values must be strings.")
 #define DESCRIBE_DOC_NM_SETTING_VPN_PERSISTENT N_("If the VPN service supports persistence, and this property is TRUE, the VPN will attempt to stay connected across link changes and outages, until explicitly disconnected.")
 #define DESCRIBE_DOC_NM_SETTING_VPN_SECRETS N_("Dictionary of key/value pairs of VPN plugin specific secrets like passwords or private keys.  Both keys and values must be strings.")
@@ -358,8 +366,8 @@
 #define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_PEER N_("The P2P device that should be connected to. Currently, this is the only way to create or join a group.")
 #define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_WFD_IES N_("The Wi-Fi Display (WFD) Information Elements (IEs) to set. Wi-Fi Display requires a protocol specific information element to be set in certain Wi-Fi frames. These can be specified here for the purpose of establishing a connection. This setting is only useful when implementing a Wi-Fi Display client.")
 #define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_WPS_METHOD N_("Flags indicating which mode of WPS is to be used. There's little point in changing the default setting as NetworkManager will automatically determine the best method to use.")
-#define DESCRIBE_DOC_NM_SETTING_WIMAX_MAC_ADDRESS N_("If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing). Deprecated: 1")
-#define DESCRIBE_DOC_NM_SETTING_WIMAX_NETWORK_NAME N_("Network Service Provider (NSP) name of the WiMAX network this connection should use. Deprecated: 1")
+#define DESCRIBE_DOC_NM_SETTING_WIMAX_MAC_ADDRESS N_("If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing).")
+#define DESCRIBE_DOC_NM_SETTING_WIMAX_NETWORK_NAME N_("Network Service Provider (NSP) name of the WiMAX network this connection should use.")
 #define DESCRIBE_DOC_NM_SETTING_WIRED_ACCEPT_ALL_MAC_ADDRESSES N_("When TRUE, setup the interface to accept packets for all MAC addresses. This is enabling the kernel interface flag IFF_PROMISC. When FALSE, the interface will only accept the packets with the interface destination mac address or broadcast.")
 #define DESCRIBE_DOC_NM_SETTING_WIRED_AUTO_NEGOTIATE N_("When TRUE, enforce auto-negotiation of speed and duplex mode. If \"speed\" and \"duplex\" properties are both specified, only that single mode will be advertised and accepted during the link auto-negotiation process: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabits modes, as in these cases link negotiation is mandatory. When FALSE, \"speed\" and \"duplex\" properties should be both set or link configuration will be skipped.")
 #define DESCRIBE_DOC_NM_SETTING_WIRED_CLONED_MAC_ADDRESS N_("If specified, request that the device use this MAC address instead. This is known as MAC cloning or spoofing. Beside explicitly specifying a MAC address, the special values \"preserve\", \"permanent\", \"random\" and \"stable\" are supported. \"preserve\" means not to touch the MAC address on activation. \"permanent\" means to use the permanent hardware address if the device has one (otherwise this is treated as \"preserve\"). \"random\" creates a random MAC address on each connect. \"stable\" creates a hashed MAC address based on connection.stable-id and a machine dependent key. If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to \"preserve\" (older versions of NetworkManager may use a different default value). On D-Bus, this field is expressed as \"assigned-mac-address\" or the deprecated \"cloned-mac-address\".")
@@ -392,7 +400,7 @@
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_HIDDEN N_("If TRUE, indicates that the network is a non-broadcasting network that hides its SSID. This works both in infrastructure and AP mode. In infrastructure mode, various workarounds are used for a more reliable discovery of hidden networks, such as probe-scanning the SSID.  However, these workarounds expose inherent insecurities with hidden SSID networks, and thus hidden SSID networks should be used with caution. In AP mode, the created network does not broadcast its SSID. Note that marking the network as hidden may be a privacy issue for you (in infrastructure mode) or client stations (in AP mode), as the explicit probe-scans are distinctly recognizable on the air.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS N_("If specified, this connection will only apply to the Wi-Fi device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing).")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST N_("A list of permanent MAC addresses of Wi-Fi devices to which this connection should never apply.  Each MAC address should be given in the standard hex-digits-and-colons notation (eg \"00:11:22:33:44:55\").")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION N_("One of NM_SETTING_MAC_RANDOMIZATION_DEFAULT (0) (never randomize unless the user has set a global default to randomize and the supplicant supports randomization),  NM_SETTING_MAC_RANDOMIZATION_NEVER (1) (never randomize the MAC address), or NM_SETTING_MAC_RANDOMIZATION_ALWAYS (2) (always randomize the MAC address). This property is deprecated for 'cloned-mac-address'. Deprecated: 1")
+#define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION N_("One of NM_SETTING_MAC_RANDOMIZATION_DEFAULT (0) (never randomize unless the user has set a global default to randomize and the supplicant supports randomization),  NM_SETTING_MAC_RANDOMIZATION_NEVER (1) (never randomize the MAC address), or NM_SETTING_MAC_RANDOMIZATION_ALWAYS (2) (always randomize the MAC address).")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_MODE N_("Wi-Fi network mode; one of \"infrastructure\", \"mesh\", \"adhoc\" or \"ap\".  If blank, infrastructure is assumed.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_POWERSAVE N_("One of NM_SETTING_WIRELESS_POWERSAVE_DISABLE (2) (disable Wi-Fi power saving), NM_SETTING_WIRELESS_POWERSAVE_ENABLE (3) (enable Wi-Fi power saving), NM_SETTING_WIRELESS_POWERSAVE_IGNORE (1) (don't touch currently configure setting) or NM_SETTING_WIRELESS_POWERSAVE_DEFAULT (0) (use the globally configured value). All other values are reserved.")
@@ -431,5 +439,7 @@
 #define DESCRIBE_DOC_NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP N_("Whether the system hostname can be determined from reverse DNS lookup of addresses on this device. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1).")
 #define DESCRIBE_DOC_NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT N_("If set to NM_TERNARY_TRUE (1), NetworkManager attempts to get the hostname via DHCPv4/DHCPv6 or reverse DNS lookup on this device only when the device has the default route for the given address family (IPv4/IPv6). If set to NM_TERNARY_FALSE (0), the hostname can be set from this device even if it doesn't have the default route. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_FALSE (0).")
 #define DESCRIBE_DOC_NM_SETTING_HOSTNAME_PRIORITY N_("The relative priority of this connection to determine the system hostname. A lower numerical value is better (higher priority).  A connection with higher priority is considered before connections with lower priority. If the value is zero, it can be overridden by a global value from NetworkManager configuration. If the property doesn't have a value in the global configuration, the value is assumed to be 100. Negative values have the special effect of excluding other connections with a greater numerical priority value; so in presence of at least one negative priority, only connections with the lowest priority value will be used to determine the hostname.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_EXTERNAL_IDS_DATA N_("A dictionary of key/value pairs with exernal-ids for OVS.")
+#define DESCRIBE_DOC_NM_SETTING_LOOPBACK_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_EXTERNAL_IDS_DATA N_("A dictionary of key/value pairs with external-ids for OVS.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_OTHER_CONFIG_DATA N_("A dictionary of key/value pairs with other_config settings for OVS. See also \"other_config\" in the \"ovs-vswitchd.conf.db\" manual for the keys that OVS supports.")
 #define DESCRIBE_DOC_NM_SETTING_VETH_PEER N_("This property specifies the peer interface name of the veth. This property is mandatory.")
diff --git a/src/libnmc-setting/settings-docs.h.in b/src/libnmc-setting/settings-docs.h.in
index 208cad86..b7f1dc24 100644
--- a/src/libnmc-setting/settings-docs.h.in
+++ b/src/libnmc-setting/settings-docs.h.in
@@ -24,7 +24,7 @@
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_STABLE_ID N_("This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual about the secret-key and the host identity. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are \"${CONNECTION}\", \"${DEVICE}\", \"${MAC}\", \"${BOOT}\", \"${RANDOM}\". These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that \"${DEVICE}\" corresponds to the interface name of the device and \"${MAC}\" is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as \"$$\". For example, set it to \"${CONNECTION}-${BOOT}-${DEVICE}\" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to \"${CONNECTION}\" and uses a unique, fixed ID for the connection.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TIMESTAMP N_("The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved).")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_TYPE N_("Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, \"802-3-ethernet\" or \"802-11-wireless\" or \"bluetooth\", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, \"vpn\" or \"bridge\", etc).")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_UUID N_("A universally unique identifier for the connection, for example generated with libuuid.  It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network.  For example, it should not be changed when the \"id\" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or \"type\" property changes. The UUID must be in the format \"2815492f-7e56-435e-b2e9-246bd7cdc664\" (ie, contains only hexadecimal characters and \"-\").")
+#define DESCRIBE_DOC_NM_SETTING_CONNECTION_UUID N_("The connection.uuid is the real identifier of a profile. It cannot change and it must be unique. It is therefore often best to refer to a profile by UUID, for example with `nmcli connection up uuid $UUID`. The UUID cannot be changed, except in offline mode. In that case, the special values \"new\", \"generate\" and \"\" are allowed to generate a new random UUID.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY N_("Time in milliseconds to wait for connection to be considered activated. The wait will start after the pre-up dispatcher event. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT N_("Timeout in milliseconds to wait for device at startup. During boot, devices may take a while to be detected by the driver. This property will cause to delay NetworkManager-wait-online.service and nm-online to give the device a chance to appear. This works by waiting for the given timeout until a compatible device for the profile is available and managed. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_ZONE N_("The trust level of a the connection.  Free form case-insensitive string (for example \"Home\", \"Work\", \"Public\").  NULL or unspecified zone means the connection will be placed in the default zone as defined by the firewall. When updating this property on a currently activated connection, the change takes effect immediately.")
@@ -49,7 +49,7 @@
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PASSWORD_FLAGS N_("Flags indicating how to handle the \"password\" property.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PASSWORD_RAW N_("Password used for EAP authentication methods, given as a byte array to allow passwords in other encodings than UTF-8 to be used. If both the \"password\" property and the \"password-raw\" property are specified, \"password\" is preferred.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PASSWORD_RAW_FLAGS N_("Flags indicating how to handle the \"password-raw\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE1_AUTH_FLAGS N_("Specifies authentication flags to use in \"phase 1\" outer authentication using NMSetting8021xAuthFlags options. The individual TLS versions can be explicitly disabled. If a certain TLS disable flag is not set, it is up to the supplicant to allow or forbid it. The TLS options map to tls_disable_tlsv1_x settings. See the wpa_supplicant documentation for more details.")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE1_AUTH_FLAGS N_("Specifies authentication flags to use in \"phase 1\" outer authentication using NMSetting8021xAuthFlags options. The individual TLS versions can be explicitly disabled. TLS time checks can be also disabled. If a certain TLS disable flag is not set, it is up to the supplicant to allow or forbid it. The TLS options map to tls_disable_tlsv1_x and tls_disable_time_checks settings. See the wpa_supplicant documentation for more details.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING N_("Enables or disables in-line provisioning of EAP-FAST credentials when FAST is specified as the EAP method in the \"eap\" property. Recognized values are \"0\" (disabled), \"1\" (allow unauthenticated provisioning), \"2\" (allow authenticated provisioning), and \"3\" (allow both authenticated and unauthenticated provisioning).  See the wpa_supplicant documentation for more details.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE1_PEAPLABEL N_("Forces use of the new PEAP label during key derivation.  Some RADIUS servers may require forcing the new PEAP label to interoperate with PEAPv1.  Set to \"1\" to force use of the new PEAP label.  See the wpa_supplicant documentation for more details.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE1_PEAPVER N_("Forces which PEAP version is used when PEAP is set as the EAP method in the \"eap\" property.  When unset, the version reported by the server will be used.  Sometimes when using older RADIUS servers, it is necessary to force the client to use a particular PEAP version.  To do so, this property may be set to \"0\" or \"1\" to force that specific PEAP version.")
@@ -68,13 +68,13 @@
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_PRIVATE_KEY N_("Contains the \"phase 2\" inner private key when the \"phase2-auth\" or \"phase2-autheap\" property is set to \"tls\". Key data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the \"phase2-private-key-password\" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte, and as with the blob scheme the \"phase2-private-key-password\" property must be set to the password used to decode the PKCS#12 private key and certificate.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD N_("The password used to decrypt the \"phase 2\" private key specified in the \"phase2-private-key\" property when the private key either uses the path scheme, or is a PKCS#12 format key.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS N_("Flags indicating how to handle the \"phase2-private-key-password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH N_("Substring to be matched against the subject of the certificate presented by the authentication server during the inner \"phase 2\" authentication. When unset, no verification of the authentication server certificate's subject is performed.  This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:phase2-domain-suffix-match.")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH N_("Substring to be matched against the subject of the certificate presented by the authentication server during the inner \"phase 2\" authentication. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and should not be used.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PIN N_("PIN used for EAP authentication methods.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PIN_FLAGS N_("Flags indicating how to handle the \"pin\" property.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PRIVATE_KEY N_("Contains the private key when the \"eap\" property is set to \"tls\". Key data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the \"private-key-password\" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte, and as with the blob scheme the \"private-key-password\" property must be set to the password used to decode the PKCS#12 private key and certificate. WARNING: \"private-key\" is not a \"secret\" property, and thus unencrypted private key data using the BLOB scheme may be readable by unprivileged users.  Private keys should always be encrypted with a private key password to prevent unauthorized access to unencrypted private key data.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD N_("The password used to decrypt the private key specified in the \"private-key\" property when the private key either uses the path scheme, or if the private key is a PKCS#12 format key.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS N_("Flags indicating how to handle the \"private-key-password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_SUBJECT_MATCH N_("Substring to be matched against the subject of the certificate presented by the authentication server. When unset, no verification of the authentication server certificate's subject is performed.  This property provides little security, if any, and its use is deprecated in favor of NMSetting8021x:domain-suffix-match.")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_SUBJECT_MATCH N_("Substring to be matched against the subject of the certificate presented by the authentication server. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and should not be used.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_SYSTEM_CA_CERTS N_("When TRUE, overrides the \"ca-path\" and \"phase2-ca-path\" properties using the system CA directory specified at configure time with the --system-ca-path switch.  The certificates in this directory are added to the verification chain in addition to any certificates specified by the \"ca-cert\" and \"phase2-ca-cert\" properties. If the path provided with --system-ca-path is rather a file name (bundle of trusted CA certificates), it overrides \"ca-cert\" and \"phase2-ca-cert\" properties instead (sets ca_cert/ca_cert2 options for wpa_supplicant).")
 #define DESCRIBE_DOC_NM_SETTING_ADSL_ENCAPSULATION N_("Encapsulation of ADSL connection.  Can be \"vcmux\" or \"llc\".")
 #define DESCRIBE_DOC_NM_SETTING_ADSL_PASSWORD N_("Password used to authenticate with the ADSL service.")
@@ -91,7 +91,7 @@
 #define DESCRIBE_DOC_NM_SETTING_BRIDGE_GROUP_ADDRESS N_("If specified, The MAC address of the multicast group this bridge uses for STP. The address must be a link-local address in standard Ethernet MAC address format, ie an address of the form 01:80:C2:00:00:0X, with X in [0, 4..F]. If not specified the default value is 01:80:C2:00:00:00.")
 #define DESCRIBE_DOC_NM_SETTING_BRIDGE_GROUP_FORWARD_MASK N_("A mask of group addresses to forward. Usually, group addresses in the range from 01:80:C2:00:00:00 to 01:80:C2:00:00:0F are not forwarded according to standards. This property is a mask of 16 bits, each corresponding to a group address in that range that must be forwarded. The mask can't have bits 0, 1 or 2 set because they are used for STP, MAC pause frames and LACP.")
 #define DESCRIBE_DOC_NM_SETTING_BRIDGE_HELLO_TIME N_("The Spanning Tree Protocol (STP) hello time, in seconds.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MAC_ADDRESS N_("If specified, the MAC address of bridge. When creating a new bridge, this MAC address will be set. If this field is left unspecified, the \"ethernet.cloned-mac-address\" is referred instead to generate the initial MAC address. Note that setting \"ethernet.cloned-mac-address\" anyway overwrites the MAC address of the bridge later while activating the bridge. Hence, this property is deprecated. Deprecated: 1")
+#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MAC_ADDRESS N_("If specified, the MAC address of bridge. When creating a new bridge, this MAC address will be set. If this field is left unspecified, the \"ethernet.cloned-mac-address\" is referred instead to generate the initial MAC address. Note that setting \"ethernet.cloned-mac-address\" anyway overwrites the MAC address of the bridge later while activating the bridge.")
 #define DESCRIBE_DOC_NM_SETTING_BRIDGE_MAX_AGE N_("The Spanning Tree Protocol (STP) maximum message age, in seconds.")
 #define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_HASH_MAX N_("Set maximum size of multicast hash table (value must be a power of 2).")
 #define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_LAST_MEMBER_COUNT N_("Set the number of queries the bridge will send before stopping forwarding a multicast group after a \"leave\" message has been received.")
@@ -143,7 +143,7 @@
 #define DESCRIBE_DOC_NM_SETTING_GSM_HOME_ONLY N_("When TRUE, only connections to the home network will be allowed. Connections to roaming networks will not be made.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_NETWORK_ID N_("The Network ID (GSM LAI format, ie MCC-MNC) to force specific network registration.  If the Network ID is specified, NetworkManager will attempt to force the device to register only on the specified network. This can be used to ensure that the device does not roam when direct roaming control of the device is not otherwise possible.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_NUMBER N_("Legacy setting that used to help establishing PPP data sessions for GSM-based modems. Deprecated: 1")
+#define DESCRIBE_DOC_NM_SETTING_GSM_NUMBER N_("Legacy setting that used to help establishing PPP data sessions for GSM-based modems.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_PASSWORD N_("The password used to authenticate with the network, if required.  Many providers do not require a password, or accept any password.  But if a password is required, it is specified here.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_PASSWORD_FLAGS N_("Flags indicating how to handle the \"password\" property.")
 #define DESCRIBE_DOC_NM_SETTING_GSM_PIN N_("If the SIM is locked with a PIN it must be unlocked before any other operations are requested.  Specify the PIN here to allow operation of the device.")
@@ -157,6 +157,7 @@
 #define DESCRIBE_DOC_NM_SETTING_INFINIBAND_PARENT N_("The interface name of the parent device of this device. Normally NULL, but if the \"p_key\" property is set, then you must specify the base device by setting either this property or \"mac-address\".")
 #define DESCRIBE_DOC_NM_SETTING_INFINIBAND_TRANSPORT_MODE N_("The IP-over-InfiniBand transport mode. Either \"datagram\" or \"connected\".")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ADDRESSES N_("A list of IPv4 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"192.168.1.5/24, 10.1.0.5/24\". The addresses are listed in decreasing priority, meaning the first address will be the primary address.")
+#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_AUTO_ROUTE_EXT_GW N_("VPN connections will default to add the route automatically unless this setting is set to FALSE. For other connection types, adding such an automatic route is currently not supported and setting this to TRUE has no effect.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DAD_TIMEOUT N_("Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network.  If an address conflict is detected, the activation will fail.  A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero).  A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID N_("A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string ('aa:bb:cc') it is interpreted as a binary client ID, in which case the first byte is assumed to be the 'type' field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the 'type' field is set to 0. The special values \"mac\" and \"perm-mac\" are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. The special value \"ipv6-duid\" uses the DUID from \"ipv6.dhcp-duid\" property as an RFC4361-compliant client identifier. As IAID it uses \"ipv4.dhcp-iaid\" and falls back to \"ipv6.dhcp-iaid\" if unset. The special value \"duid\" generates a RFC4361-compliant client identifier based on \"ipv4.dhcp-iaid\" and uses a DUID generated by hashing /etc/machine-id. The special value \"stable\" is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the \"${DEVICE}\" or \"${MAC}\" specifier to get a per-device key. If unset, a globally configured default is used. If still unset, the default depends on the DHCP plugin.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_FQDN N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified FQDN will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-hostname\" are mutually exclusive and cannot be set at the same time.")
@@ -167,7 +168,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME N_("If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer.  If the \"dhcp-hostname\" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_TIMEOUT N_("A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER N_("The Vendor Class Identifier DHCP option (60). Special characters in the data string may be escaped using C-style escapes, nevertheless this property cannot contain nul bytes. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the DHCP option is not sent to the server.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS N_("Array of IP addresses of DNS servers.")
+#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS N_("Array of IP addresses of DNS servers. For DoT (DNS over TLS), the SNI server name can be specified by appending \"#example.com\" to the IP address of the DNS server. This currently only has effect when using systemd-resolved.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_OPTIONS N_("Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties. The currently supported options are \"attempts\", \"debug\", \"edns0\", \"inet6\", \"ip6-bytestring\", \"ip6-dotint\", \"ndots\", \"no-check-names\", \"no-ip6-dotint\", \"no-reload\", \"no-tld-query\", \"rotate\", \"single-request\", \"single-request-reopen\", \"timeout\", \"trust-ad\", \"use-vc\". The \"trust-ad\" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have \"trust-ad\" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then \"edns0\" and \"trust-ad\" are automatically added.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the \"rotate\" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins.  If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_SEARCH N_("List of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting. When set on a profile that also enabled DHCP, the DNS search list received automatically (option 119 for DHCPv4 and option 24 for DHCPv6) gets merged with the manual list. This can be prevented by setting \"ignore-auto-dns\". Note that if no DNS searches are configured, the fallback will be derived from the domain from DHCP (option 15).")
@@ -185,6 +186,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTING_RULES N_("A comma separated list of routing rules for policy routing.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE N_("Configure method for creating the address for use with RFC4862 IPv6 Stateless Address Autoconfiguration. The permitted values are: NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 (0), NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY (1). NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT_OR_EUI64 (2) or NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT (3). If the property is set to EUI64, the addresses will be generated using the interface tokens derived from hardware address. This makes the host part of the address to stay constant, making it possible to track host's presence when it changes networks. The address changes when the interface hardware is replaced. The value of stable-privacy enables use of cryptographically secure hash of a secret host-specific key along with the connection's stable-id and the network address as specified by RFC7217. This makes it impossible to use the address track host's presence, and makes the address stable when the network interface hardware is replaced. The special values \"default\" and \"default-or-eui64\" will fallback to the global connection default in as documented in NetworkManager.conf(5) manual. If the global default is not specified, the fallback value is \"stable-privacy\" or \"eui64\", respectively. For libnm, the property defaults to \"default\" since 1.40. Previously it defaulted to \"stable-privacy\". On D-Bus, the absence of an addr-gen-mode setting equals \"default\". For keyfile plugin, the absence of the setting on disk means \"default-or-eui64\" so that the property doesn't change on upgrade from older versions. Note that this setting is distinct from the Privacy Extensions as configured by \"ip6-privacy\" property and it does not affect the temporary addresses configured with this option.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDRESSES N_("A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64\". The addresses are listed in decreasing priority, meaning the first address will be the primary address. This can make a difference with IPv6 source address selection (RFC 6724, section 5).")
+#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_AUTO_ROUTE_EXT_GW N_("VPN connections will default to add the route automatically unless this setting is set to FALSE. For other connection types, adding such an automatic route is currently not supported and setting this to TRUE has no effect.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DAD_TIMEOUT N_("Timeout in milliseconds used to check for the presence of duplicate IP addresses on the network.  If an address conflict is detected, the activation will fail.  A zero value means that no duplicate address detection is performed, -1 means the default value (either configuration ipvx.dad-timeout override or zero).  A value greater than zero is a timeout in milliseconds. The property is currently implemented only for IPv4.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_DUID N_("A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp client to identify itself to DHCPv6 servers (RFC 3315). The DUID is carried in the Client Identifier option. If the property is a hex string ('aa:bb:cc') it is interpreted as a binary DUID and filled as an opaque value in the Client Identifier option. The special value \"lease\" will retrieve the DUID previously used from the lease file belonging to the connection. If no DUID is found and \"dhclient\" is the configured dhcp client, the DUID is searched in the system-wide dhclient lease file. If still no DUID is found, or another dhcp client is used, a global and permanent DUID-UUID (RFC 6355) will be generated based on the machine-id. The special values \"llt\" and \"ll\" will generate a DUID of type LLT or LL (see RFC 3315) based on the current MAC address of the device. In order to try providing a stable DUID-LLT, the time field will contain a constant timestamp that is used globally (for all profiles) and persisted to disk. The special values \"stable-llt\", \"stable-ll\" and \"stable-uuid\" will generate a DUID of the corresponding type, derived from the connection's stable-id and a per-host unique key. You may want to include the \"${DEVICE}\" or \"${MAC}\" specifier in the stable-id, in case this profile gets activated on multiple devices. So, the link-layer address of \"stable-ll\" and \"stable-llt\" will be a generated address derived from the stable id. The DUID-LLT time value in the \"stable-llt\" option will be picked among a static timespan of three years (the upper bound of the interval is the same constant timestamp used in \"llt\"). When the property is unset, the global value provided for \"ipv6.dhcp-duid\" is used. If no global value is provided, the default \"lease\" value is assumed.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-fqdn\" are mutually exclusive and cannot be set at the same time.")
@@ -193,7 +195,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_REJECT_SERVERS N_("Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. \"192.168.122.0/24\"). This property is currently not implemented for DHCPv6.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_SEND_HOSTNAME N_("If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer.  If the \"dhcp-hostname\" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_TIMEOUT N_("A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS N_("Array of IP addresses of DNS servers.")
+#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS N_("Array of IP addresses of DNS servers. For DoT (DNS over TLS), the SNI server name can be specified by appending \"#example.com\" to the IP address of the DNS server. This currently only has effect when using systemd-resolved.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_OPTIONS N_("Array of DNS options as described in man 5 resolv.conf. NULL means that the options are unset and left at the default. In this case NetworkManager will use default options. This is distinct from an empty list of properties. The currently supported options are \"attempts\", \"debug\", \"edns0\", \"inet6\", \"ip6-bytestring\", \"ip6-dotint\", \"ndots\", \"no-check-names\", \"no-ip6-dotint\", \"no-reload\", \"no-tld-query\", \"rotate\", \"single-request\", \"single-request-reopen\", \"timeout\", \"trust-ad\", \"use-vc\". The \"trust-ad\" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have \"trust-ad\" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then \"edns0\" and \"trust-ad\" are automatically added.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the \"rotate\" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins.  If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_SEARCH N_("List of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting. When set on a profile that also enabled DHCP, the DNS search list received automatically (option 119 for DHCPv4 and option 24 for DHCPv6) gets merged with the manual list. This can be prevented by setting \"ignore-auto-dns\". Note that if no DNS searches are configured, the fallback will be derived from the domain from DHCP (option 15).")
@@ -215,6 +217,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT N_("How many additional levels of encapsulation are permitted to be prepended to packets. This property applies only to IPv6 tunnels.")
 #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_FLAGS N_("Tunnel flags. Currently, the following values are supported: NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT (0x1), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS (0x2), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL (0x4), NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV (0x8), NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY (0x10), NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK (0x20). They are valid only for IPv6 tunnels.")
 #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_FLOW_LABEL N_("The flow label to assign to tunnel packets. This property applies only to IPv6 tunnels.")
+#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_FWMARK N_("The fwmark value to assign to tunnel packets. This property can be set to a non zero value only on VTI and VTI6 tunnels.")
 #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_INPUT_KEY N_("The key used for tunnel input packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used.")
 #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_LOCAL N_("The local endpoint of the tunnel; the value can be empty, otherwise it must contain an IPv4 or IPv6 address.")
 #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_MODE N_("The tunneling mode, for example NM_IP_TUNNEL_MODE_IPIP (1) or NM_IP_TUNNEL_MODE_GRE (2).")
@@ -252,6 +255,9 @@
 #define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_STP_ENABLE N_("Enable or disable STP.")
 #define DESCRIBE_DOC_NM_SETTING_OVS_DPDK_DEVARGS N_("Open vSwitch DPDK device arguments.")
 #define DESCRIBE_DOC_NM_SETTING_OVS_DPDK_N_RXQ N_("Open vSwitch DPDK number of rx queues. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures one queue.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_DPDK_N_RXQ_DESC N_("The rx queue size (number of rx descriptors) for DPDK ports. Must be zero or a power of 2 between 1 and 4096, and supported by the hardware. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures 2048 descriptors.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_DPDK_N_TXQ_DESC N_("The tx queue size (number of tx descriptors) for DPDK ports. Must be zero or a power of 2 between 1 and 4096, and supported by the hardware. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures 2048 descriptors.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_INTERFACE_OFPORT_REQUEST N_("Open vSwitch openflow port number. Defaults to zero which means that port number will not be specified and it will be chosen randomly by ovs. OpenFlow ports are the network interfaces for passing packets between OpenFlow processing and the rest of the network. OpenFlow switches connect logically to each other via their OpenFlow ports.")
 #define DESCRIBE_DOC_NM_SETTING_OVS_INTERFACE_TYPE N_("The interface type. Either \"internal\", \"system\", \"patch\", \"dpdk\", or empty.")
 #define DESCRIBE_DOC_NM_SETTING_OVS_PATCH_PEER N_("Specifies the name of the interface for the other side of the patch. The patch on the other side must also set this interface as peer.")
 #define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_DOWNDELAY N_("The time port must be inactive in order to be considered down.")
@@ -259,7 +265,8 @@
 #define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_UPDELAY N_("The time port must be active before it starts forwarding traffic.")
 #define DESCRIBE_DOC_NM_SETTING_OVS_PORT_LACP N_("LACP mode. One of \"active\", \"off\", or \"passive\".")
 #define DESCRIBE_DOC_NM_SETTING_OVS_PORT_TAG N_("The VLAN tag in the range 0-4095.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_VLAN_MODE N_("The VLAN mode. One of \"access\", \"native-tagged\", \"native-untagged\", \"trunk\" or unset.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_TRUNKS N_("A list of VLAN ranges that this port trunks. The property is valid only for ports with mode \"trunk\", \"native-tagged\", or \"native-untagged port\". If it is empty, the port trunks all VLANs.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_VLAN_MODE N_("The VLAN mode. One of \"access\", \"native-tagged\", \"native-untagged\", \"trunk\", \"dot1q-tunnel\" or unset.")
 #define DESCRIBE_DOC_NM_SETTING_PPP_BAUD N_("If non-zero, instruct pppd to set the serial port to the specified baudrate.  This value should normally be left as 0 to automatically choose the speed.")
 #define DESCRIBE_DOC_NM_SETTING_PPP_CRTSCTS N_("If TRUE, specify that pppd should set the serial port to use hardware flow control with RTS and CTS signals.  This value should normally be set to FALSE.")
 #define DESCRIBE_DOC_NM_SETTING_PPP_LCP_ECHO_FAILURE N_("If non-zero, instruct pppd to presume the connection to the peer has failed if the specified number of LCP echo-requests go unanswered by the peer.  The \"lcp-echo-interval\" property must also be set to a non-zero value if this property is used.")
@@ -332,6 +339,7 @@
 #define DESCRIBE_DOC_NM_SETTING_VLAN_ID N_("The VLAN identifier that the interface created by this connection should be assigned. The valid range is from 0 to 4094, without the reserved id 4095.")
 #define DESCRIBE_DOC_NM_SETTING_VLAN_INGRESS_PRIORITY_MAP N_("For incoming packets, a list of mappings from 802.1p priorities to Linux SKB priorities.  The mapping is given in the format \"from:to\" where both \"from\" and \"to\" are unsigned integers, ie \"7:3\".")
 #define DESCRIBE_DOC_NM_SETTING_VLAN_PARENT N_("If given, specifies the parent interface name or parent connection UUID from which this VLAN interface should be created.  If this property is not specified, the connection must contain an \"802-3-ethernet\" setting with a \"mac-address\" property.")
+#define DESCRIBE_DOC_NM_SETTING_VLAN_PROTOCOL N_("Specifies the VLAN protocol to use for encapsulation. Supported values are: '802.1Q', '802.1ad'. If not specified the default value is '802.1Q'.")
 #define DESCRIBE_DOC_NM_SETTING_VPN_DATA N_("Dictionary of key/value pairs of VPN plugin specific data.  Both keys and values must be strings.")
 #define DESCRIBE_DOC_NM_SETTING_VPN_PERSISTENT N_("If the VPN service supports persistence, and this property is TRUE, the VPN will attempt to stay connected across link changes and outages, until explicitly disconnected.")
 #define DESCRIBE_DOC_NM_SETTING_VPN_SECRETS N_("Dictionary of key/value pairs of VPN plugin specific secrets like passwords or private keys.  Both keys and values must be strings.")
@@ -358,8 +366,8 @@
 #define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_PEER N_("The P2P device that should be connected to. Currently, this is the only way to create or join a group.")
 #define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_WFD_IES N_("The Wi-Fi Display (WFD) Information Elements (IEs) to set. Wi-Fi Display requires a protocol specific information element to be set in certain Wi-Fi frames. These can be specified here for the purpose of establishing a connection. This setting is only useful when implementing a Wi-Fi Display client.")
 #define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_WPS_METHOD N_("Flags indicating which mode of WPS is to be used. There's little point in changing the default setting as NetworkManager will automatically determine the best method to use.")
-#define DESCRIBE_DOC_NM_SETTING_WIMAX_MAC_ADDRESS N_("If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing). Deprecated: 1")
-#define DESCRIBE_DOC_NM_SETTING_WIMAX_NETWORK_NAME N_("Network Service Provider (NSP) name of the WiMAX network this connection should use. Deprecated: 1")
+#define DESCRIBE_DOC_NM_SETTING_WIMAX_MAC_ADDRESS N_("If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing).")
+#define DESCRIBE_DOC_NM_SETTING_WIMAX_NETWORK_NAME N_("Network Service Provider (NSP) name of the WiMAX network this connection should use.")
 #define DESCRIBE_DOC_NM_SETTING_WIRED_ACCEPT_ALL_MAC_ADDRESSES N_("When TRUE, setup the interface to accept packets for all MAC addresses. This is enabling the kernel interface flag IFF_PROMISC. When FALSE, the interface will only accept the packets with the interface destination mac address or broadcast.")
 #define DESCRIBE_DOC_NM_SETTING_WIRED_AUTO_NEGOTIATE N_("When TRUE, enforce auto-negotiation of speed and duplex mode. If \"speed\" and \"duplex\" properties are both specified, only that single mode will be advertised and accepted during the link auto-negotiation process: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabits modes, as in these cases link negotiation is mandatory. When FALSE, \"speed\" and \"duplex\" properties should be both set or link configuration will be skipped.")
 #define DESCRIBE_DOC_NM_SETTING_WIRED_CLONED_MAC_ADDRESS N_("If specified, request that the device use this MAC address instead. This is known as MAC cloning or spoofing. Beside explicitly specifying a MAC address, the special values \"preserve\", \"permanent\", \"random\" and \"stable\" are supported. \"preserve\" means not to touch the MAC address on activation. \"permanent\" means to use the permanent hardware address if the device has one (otherwise this is treated as \"preserve\"). \"random\" creates a random MAC address on each connect. \"stable\" creates a hashed MAC address based on connection.stable-id and a machine dependent key. If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to \"preserve\" (older versions of NetworkManager may use a different default value). On D-Bus, this field is expressed as \"assigned-mac-address\" or the deprecated \"cloned-mac-address\".")
@@ -392,7 +400,7 @@
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_HIDDEN N_("If TRUE, indicates that the network is a non-broadcasting network that hides its SSID. This works both in infrastructure and AP mode. In infrastructure mode, various workarounds are used for a more reliable discovery of hidden networks, such as probe-scanning the SSID.  However, these workarounds expose inherent insecurities with hidden SSID networks, and thus hidden SSID networks should be used with caution. In AP mode, the created network does not broadcast its SSID. Note that marking the network as hidden may be a privacy issue for you (in infrastructure mode) or client stations (in AP mode), as the explicit probe-scans are distinctly recognizable on the air.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS N_("If specified, this connection will only apply to the Wi-Fi device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing).")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST N_("A list of permanent MAC addresses of Wi-Fi devices to which this connection should never apply.  Each MAC address should be given in the standard hex-digits-and-colons notation (eg \"00:11:22:33:44:55\").")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION N_("One of NM_SETTING_MAC_RANDOMIZATION_DEFAULT (0) (never randomize unless the user has set a global default to randomize and the supplicant supports randomization),  NM_SETTING_MAC_RANDOMIZATION_NEVER (1) (never randomize the MAC address), or NM_SETTING_MAC_RANDOMIZATION_ALWAYS (2) (always randomize the MAC address). This property is deprecated for 'cloned-mac-address'. Deprecated: 1")
+#define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION N_("One of NM_SETTING_MAC_RANDOMIZATION_DEFAULT (0) (never randomize unless the user has set a global default to randomize and the supplicant supports randomization),  NM_SETTING_MAC_RANDOMIZATION_NEVER (1) (never randomize the MAC address), or NM_SETTING_MAC_RANDOMIZATION_ALWAYS (2) (always randomize the MAC address).")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_MODE N_("Wi-Fi network mode; one of \"infrastructure\", \"mesh\", \"adhoc\" or \"ap\".  If blank, infrastructure is assumed.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_POWERSAVE N_("One of NM_SETTING_WIRELESS_POWERSAVE_DISABLE (2) (disable Wi-Fi power saving), NM_SETTING_WIRELESS_POWERSAVE_ENABLE (3) (enable Wi-Fi power saving), NM_SETTING_WIRELESS_POWERSAVE_IGNORE (1) (don't touch currently configure setting) or NM_SETTING_WIRELESS_POWERSAVE_DEFAULT (0) (use the globally configured value). All other values are reserved.")
@@ -431,5 +439,7 @@
 #define DESCRIBE_DOC_NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP N_("Whether the system hostname can be determined from reverse DNS lookup of addresses on this device. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_TRUE (1).")
 #define DESCRIBE_DOC_NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT N_("If set to NM_TERNARY_TRUE (1), NetworkManager attempts to get the hostname via DHCPv4/DHCPv6 or reverse DNS lookup on this device only when the device has the default route for the given address family (IPv4/IPv6). If set to NM_TERNARY_FALSE (0), the hostname can be set from this device even if it doesn't have the default route. When set to NM_TERNARY_DEFAULT (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be NM_TERNARY_FALSE (0).")
 #define DESCRIBE_DOC_NM_SETTING_HOSTNAME_PRIORITY N_("The relative priority of this connection to determine the system hostname. A lower numerical value is better (higher priority).  A connection with higher priority is considered before connections with lower priority. If the value is zero, it can be overridden by a global value from NetworkManager configuration. If the property doesn't have a value in the global configuration, the value is assumed to be 100. Negative values have the special effect of excluding other connections with a greater numerical priority value; so in presence of at least one negative priority, only connections with the lowest priority value will be used to determine the hostname.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_EXTERNAL_IDS_DATA N_("A dictionary of key/value pairs with exernal-ids for OVS.")
+#define DESCRIBE_DOC_NM_SETTING_LOOPBACK_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_EXTERNAL_IDS_DATA N_("A dictionary of key/value pairs with external-ids for OVS.")
+#define DESCRIBE_DOC_NM_SETTING_OVS_OTHER_CONFIG_DATA N_("A dictionary of key/value pairs with other_config settings for OVS. See also \"other_config\" in the \"ovs-vswitchd.conf.db\" manual for the keys that OVS supports.")
 #define DESCRIBE_DOC_NM_SETTING_VETH_PEER N_("This property specifies the peer interface name of the veth. This property is mandatory.")
diff --git a/src/libnmt-newt/nmt-newt-button-box.h b/src/libnmt-newt/nmt-newt-button-box.h
index 6d17778b..e24cde7f 100644
--- a/src/libnmt-newt/nmt-newt-button-box.h
+++ b/src/libnmt-newt/nmt-newt-button-box.h
@@ -10,7 +10,7 @@
 
 #define NMT_TYPE_NEWT_BUTTON_BOX (nmt_newt_button_box_get_type())
 #define NMT_NEWT_BUTTON_BOX(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBox))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBox))
 #define NMT_NEWT_BUTTON_BOX_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBoxClass))
 #define NMT_IS_NEWT_BUTTON_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_BUTTON_BOX))
diff --git a/src/libnmt-newt/nmt-newt-button.h b/src/libnmt-newt/nmt-newt-button.h
index 12649600..cd724b23 100644
--- a/src/libnmt-newt/nmt-newt-button.h
+++ b/src/libnmt-newt/nmt-newt-button.h
@@ -10,7 +10,7 @@
 
 #define NMT_TYPE_NEWT_BUTTON (nmt_newt_button_get_type())
 #define NMT_NEWT_BUTTON(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_BUTTON, NmtNewtButton))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_BUTTON, NmtNewtButton))
 #define NMT_NEWT_BUTTON_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonClass))
 #define NMT_IS_NEWT_BUTTON(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_BUTTON))
diff --git a/src/libnmt-newt/nmt-newt-checkbox.h b/src/libnmt-newt/nmt-newt-checkbox.h
index c33c086a..86595b74 100644
--- a/src/libnmt-newt/nmt-newt-checkbox.h
+++ b/src/libnmt-newt/nmt-newt-checkbox.h
@@ -10,7 +10,7 @@
 
 #define NMT_TYPE_NEWT_CHECKBOX (nmt_newt_checkbox_get_type())
 #define NMT_NEWT_CHECKBOX(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckbox))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckbox))
 #define NMT_NEWT_CHECKBOX_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckboxClass))
 #define NMT_IS_NEWT_CHECKBOX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_CHECKBOX))
diff --git a/src/libnmt-newt/nmt-newt-component.h b/src/libnmt-newt/nmt-newt-component.h
index 54164c54..5426f0b0 100644
--- a/src/libnmt-newt/nmt-newt-component.h
+++ b/src/libnmt-newt/nmt-newt-component.h
@@ -10,7 +10,7 @@
 
 #define NMT_TYPE_NEWT_COMPONENT (nmt_newt_component_get_type())
 #define NMT_NEWT_COMPONENT(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponent))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponent))
 #define NMT_NEWT_COMPONENT_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponentClass))
 #define NMT_IS_NEWT_COMPONENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_COMPONENT))
diff --git a/src/libnmt-newt/nmt-newt-container.h b/src/libnmt-newt/nmt-newt-container.h
index 02585294..69851f6c 100644
--- a/src/libnmt-newt/nmt-newt-container.h
+++ b/src/libnmt-newt/nmt-newt-container.h
@@ -10,7 +10,7 @@
 
 #define NMT_TYPE_NEWT_CONTAINER (nmt_newt_container_get_type())
 #define NMT_NEWT_CONTAINER(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainer))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainer))
 #define NMT_NEWT_CONTAINER_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainerClass))
 #define NMT_IS_NEWT_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_CONTAINER))
diff --git a/src/libnmt-newt/nmt-newt-entry-numeric.h b/src/libnmt-newt/nmt-newt-entry-numeric.h
index a10e4c5b..7a4d2c81 100644
--- a/src/libnmt-newt/nmt-newt-entry-numeric.h
+++ b/src/libnmt-newt/nmt-newt-entry-numeric.h
@@ -10,7 +10,7 @@
 
 #define NMT_TYPE_NEWT_ENTRY_NUMERIC (nmt_newt_entry_numeric_get_type())
 #define NMT_NEWT_ENTRY_NUMERIC(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumeric))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumeric))
 #define NMT_NEWT_ENTRY_NUMERIC_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericClass))
 #define NMT_IS_NEWT_ENTRY_NUMERIC(obj) \
diff --git a/src/libnmt-newt/nmt-newt-entry.h b/src/libnmt-newt/nmt-newt-entry.h
index 81221812..5b0ff908 100644
--- a/src/libnmt-newt/nmt-newt-entry.h
+++ b/src/libnmt-newt/nmt-newt-entry.h
@@ -9,7 +9,8 @@
 #include "nmt-newt-component.h"
 
 #define NMT_TYPE_NEWT_ENTRY (nmt_newt_entry_get_type())
-#define NMT_NEWT_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_ENTRY, NmtNewtEntry))
+#define NMT_NEWT_ENTRY(obj) \
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_ENTRY, NmtNewtEntry))
 #define NMT_NEWT_ENTRY_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_ENTRY, NmtNewtEntryClass))
 #define NMT_IS_NEWT_ENTRY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_ENTRY))
diff --git a/src/libnmt-newt/nmt-newt-form.c b/src/libnmt-newt/nmt-newt-form.c
index 7d28c80e..de80b42a 100644
--- a/src/libnmt-newt/nmt-newt-form.c
+++ b/src/libnmt-newt/nmt-newt-form.c
@@ -36,7 +36,7 @@ typedef struct {
     gboolean fixed_width, fixed_height;
     char    *title_lc;
 
-    gboolean       dirty, escape_exits;
+    gboolean       dirty;
     NmtNewtWidget *focus;
 #ifdef HAVE_NEWTFORMGETSCROLLPOSITION
     int scroll_position = 0;
@@ -54,7 +54,6 @@ enum {
     PROP_WIDTH,
     PROP_HEIGHT,
     PROP_PADDING,
-    PROP_ESCAPE_EXITS,
 
     LAST_PROP
 };
@@ -211,8 +210,7 @@ nmt_newt_form_build(NmtNewtForm *form)
     } else
         priv->form = newtForm(NULL, NULL, NEWT_FLAG_NOF12);
 
-    if (priv->escape_exits)
-        newtFormAddHotKey(priv->form, NEWT_KEY_ESCAPE);
+    newtFormAddHotKey(priv->form, NEWT_KEY_ESCAPE);
 
     cos = nmt_newt_widget_get_components(priv->content);
     for (i = 0; cos[i]; i++)
@@ -504,9 +502,6 @@ nmt_newt_form_set_property(GObject *object, guint prop_id, const GValue *value,
     case PROP_PADDING:
         priv->padding = g_value_get_uint(value);
         break;
-    case PROP_ESCAPE_EXITS:
-        priv->escape_exits = g_value_get_boolean(value);
-        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -540,9 +535,6 @@ nmt_newt_form_get_property(GObject *object, guint prop_id, GValue *value, GParam
     case PROP_PADDING:
         g_value_set_uint(value, priv->padding);
         break;
-    case PROP_ESCAPE_EXITS:
-        g_value_set_boolean(value, priv->escape_exits);
-        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -722,18 +714,4 @@ nmt_newt_form_class_init(NmtNewtFormClass *form_class)
                           G_MAXUINT,
                           1,
                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
-    /**
-     * NmtNewtForm:escape-exits:
-     *
-     * If %TRUE, then hitting the Escape key will cause the form to
-     * exit.
-     */
-    g_object_class_install_property(
-        object_class,
-        PROP_ESCAPE_EXITS,
-        g_param_spec_boolean("escape-exits",
-                             "",
-                             "",
-                             FALSE,
-                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
 }
diff --git a/src/libnmt-newt/nmt-newt-form.h b/src/libnmt-newt/nmt-newt-form.h
index 55fb583b..6a2ff9dc 100644
--- a/src/libnmt-newt/nmt-newt-form.h
+++ b/src/libnmt-newt/nmt-newt-form.h
@@ -9,7 +9,7 @@
 #include "nmt-newt-container.h"
 
 #define NMT_TYPE_NEWT_FORM (nmt_newt_form_get_type())
-#define NMT_NEWT_FORM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_FORM, NmtNewtForm))
+#define NMT_NEWT_FORM(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_FORM, NmtNewtForm))
 #define NMT_NEWT_FORM_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_FORM, NmtNewtFormClass))
 #define NMT_IS_NEWT_FORM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_FORM))
diff --git a/src/libnmt-newt/nmt-newt-grid.c b/src/libnmt-newt/nmt-newt-grid.c
index 1078723d..830c6466 100644
--- a/src/libnmt-newt/nmt-newt-grid.c
+++ b/src/libnmt-newt/nmt-newt-grid.c
@@ -109,7 +109,7 @@ nmt_newt_grid_get_components(NmtNewtWidget *widget)
     int                 i, c;
 
     g_array_sort(priv->children, child_sort_func);
-    children = (NmtNewtGridChild *) priv->children->data;
+    children = nm_g_array_first_p(priv->children, NmtNewtGridChild);
 
     cos = g_ptr_array_new();
 
@@ -132,7 +132,7 @@ nmt_newt_grid_size_request(NmtNewtWidget *widget, int *width, int *height)
 {
     NmtNewtGrid        *grid     = NMT_NEWT_GRID(widget);
     NmtNewtGridPrivate *priv     = NMT_NEWT_GRID_GET_PRIVATE(grid);
-    NmtNewtGridChild   *children = (NmtNewtGridChild *) priv->children->data;
+    NmtNewtGridChild   *children = nm_g_array_first_p(priv->children, NmtNewtGridChild);
     int                 row, col, i;
 
     g_free(priv->row_heights);
@@ -190,7 +190,7 @@ static void
 nmt_newt_grid_size_allocate(NmtNewtWidget *widget, int x, int y, int width, int height)
 {
     NmtNewtGridPrivate *priv     = NMT_NEWT_GRID_GET_PRIVATE(widget);
-    NmtNewtGridChild   *children = (NmtNewtGridChild *) priv->children->data, *child;
+    NmtNewtGridChild   *children = nm_g_array_first_p(priv->children, NmtNewtGridChild), *child;
     int                 i, row, col;
     int                 child_x, child_y, child_width, child_height;
     int                 extra, extra_all, extra_some;
@@ -268,7 +268,7 @@ static void
 nmt_newt_grid_find_size(NmtNewtGrid *grid)
 {
     NmtNewtGridPrivate *priv     = NMT_NEWT_GRID_GET_PRIVATE(grid);
-    NmtNewtGridChild   *children = (NmtNewtGridChild *) priv->children->data;
+    NmtNewtGridChild   *children = nm_g_array_first_p(priv->children, NmtNewtGridChild);
     int                 i;
 
     priv->max_x = priv->max_y = 0;
@@ -315,7 +315,7 @@ static int
 find_child(NmtNewtGrid *grid, NmtNewtWidget *widget)
 {
     NmtNewtGridPrivate *priv     = NMT_NEWT_GRID_GET_PRIVATE(grid);
-    NmtNewtGridChild   *children = (NmtNewtGridChild *) priv->children->data;
+    NmtNewtGridChild   *children = nm_g_array_first_p(priv->children, NmtNewtGridChild);
     int                 i;
 
     for (i = 0; i < priv->children->len; i++) {
@@ -355,7 +355,7 @@ void
 nmt_newt_grid_move(NmtNewtGrid *grid, NmtNewtWidget *widget, int x, int y)
 {
     NmtNewtGridPrivate *priv     = NMT_NEWT_GRID_GET_PRIVATE(grid);
-    NmtNewtGridChild   *children = (NmtNewtGridChild *) priv->children->data;
+    NmtNewtGridChild   *children = nm_g_array_first_p(priv->children, NmtNewtGridChild);
     int                 i;
 
     i = find_child(grid, widget);
@@ -411,7 +411,7 @@ void
 nmt_newt_grid_set_flags(NmtNewtGrid *grid, NmtNewtWidget *widget, NmtNewtGridFlags flags)
 {
     NmtNewtGridPrivate *priv     = NMT_NEWT_GRID_GET_PRIVATE(grid);
-    NmtNewtGridChild   *children = (NmtNewtGridChild *) priv->children->data;
+    NmtNewtGridChild   *children = nm_g_array_first_p(priv->children, NmtNewtGridChild);
     int                 i;
 
     i = find_child(grid, widget);
diff --git a/src/libnmt-newt/nmt-newt-grid.h b/src/libnmt-newt/nmt-newt-grid.h
index 7aac18bc..999788e9 100644
--- a/src/libnmt-newt/nmt-newt-grid.h
+++ b/src/libnmt-newt/nmt-newt-grid.h
@@ -9,7 +9,7 @@
 #include "nmt-newt-container.h"
 
 #define NMT_TYPE_NEWT_GRID (nmt_newt_grid_get_type())
-#define NMT_NEWT_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_GRID, NmtNewtGrid))
+#define NMT_NEWT_GRID(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_GRID, NmtNewtGrid))
 #define NMT_NEWT_GRID_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_GRID, NmtNewtGridClass))
 #define NMT_IS_NEWT_GRID(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_GRID))
diff --git a/src/libnmt-newt/nmt-newt-label.h b/src/libnmt-newt/nmt-newt-label.h
index ff2e471b..5ec50aa1 100644
--- a/src/libnmt-newt/nmt-newt-label.h
+++ b/src/libnmt-newt/nmt-newt-label.h
@@ -9,7 +9,8 @@
 #include "nmt-newt-component.h"
 
 #define NMT_TYPE_NEWT_LABEL (nmt_newt_label_get_type())
-#define NMT_NEWT_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_LABEL, NmtNewtLabel))
+#define NMT_NEWT_LABEL(obj) \
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_LABEL, NmtNewtLabel))
 #define NMT_NEWT_LABEL_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_LABEL, NmtNewtLabelClass))
 #define NMT_IS_NEWT_LABEL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_LABEL))
diff --git a/src/libnmt-newt/nmt-newt-listbox.h b/src/libnmt-newt/nmt-newt-listbox.h
index 602e3380..c429b941 100644
--- a/src/libnmt-newt/nmt-newt-listbox.h
+++ b/src/libnmt-newt/nmt-newt-listbox.h
@@ -10,7 +10,7 @@
 
 #define NMT_TYPE_NEWT_LISTBOX (nmt_newt_listbox_get_type())
 #define NMT_NEWT_LISTBOX(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_LISTBOX, NmtNewtListbox))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_LISTBOX, NmtNewtListbox))
 #define NMT_NEWT_LISTBOX_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxClass))
 #define NMT_IS_NEWT_LISTBOX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_LISTBOX))
diff --git a/src/libnmt-newt/nmt-newt-popup.c b/src/libnmt-newt/nmt-newt-popup.c
index c62951ca..d0a3e74c 100644
--- a/src/libnmt-newt/nmt-newt-popup.c
+++ b/src/libnmt-newt/nmt-newt-popup.c
@@ -107,7 +107,7 @@ static newtComponent
 nmt_newt_popup_build_component(NmtNewtComponent *component, gboolean sensitive)
 {
     NmtNewtPopupPrivate *priv    = NMT_NEWT_POPUP_GET_PRIVATE(component);
-    NmtNewtPopupEntry   *entries = (NmtNewtPopupEntry *) priv->entries->data;
+    NmtNewtPopupEntry   *entries = nm_g_array_first_p(priv->entries, NmtNewtPopupEntry);
 
     nmt_newt_button_set_label(NMT_NEWT_BUTTON(component), entries[priv->active].label);
     return NMT_NEWT_COMPONENT_CLASS(nmt_newt_popup_parent_class)
@@ -118,7 +118,7 @@ static void
 nmt_newt_popup_activated(NmtNewtWidget *widget)
 {
     NmtNewtPopupPrivate *priv    = NMT_NEWT_POPUP_GET_PRIVATE(widget);
-    NmtNewtPopupEntry   *entries = (NmtNewtPopupEntry *) priv->entries->data;
+    NmtNewtPopupEntry   *entries = nm_g_array_first_p(priv->entries, NmtNewtPopupEntry);
     NmtNewtForm         *form;
     NmtNewtWidget       *listbox, *ret;
     int                  button_x, button_y;
@@ -162,8 +162,6 @@ nmt_newt_popup_activated(NmtNewtWidget *widget)
                         list_h,
                         "padding",
                         0,
-                        "escape-exits",
-                        TRUE,
                         NULL);
     nmt_newt_form_set_content(form, listbox);
 
@@ -234,7 +232,7 @@ const char *
 nmt_newt_popup_get_active_id(NmtNewtPopup *popup)
 {
     NmtNewtPopupPrivate *priv    = NMT_NEWT_POPUP_GET_PRIVATE(popup);
-    NmtNewtPopupEntry   *entries = (NmtNewtPopupEntry *) priv->entries->data;
+    NmtNewtPopupEntry   *entries = nm_g_array_first_p(priv->entries, NmtNewtPopupEntry);
 
     return entries[priv->active].id;
 }
@@ -250,7 +248,7 @@ void
 nmt_newt_popup_set_active_id(NmtNewtPopup *popup, const char *active_id)
 {
     NmtNewtPopupPrivate *priv    = NMT_NEWT_POPUP_GET_PRIVATE(popup);
-    NmtNewtPopupEntry   *entries = (NmtNewtPopupEntry *) priv->entries->data;
+    NmtNewtPopupEntry   *entries = nm_g_array_first_p(priv->entries, NmtNewtPopupEntry);
     int                  i;
 
     for (i = 0; i < priv->entries->len; i++) {
diff --git a/src/libnmt-newt/nmt-newt-popup.h b/src/libnmt-newt/nmt-newt-popup.h
index 15e247b4..6ca5e5ed 100644
--- a/src/libnmt-newt/nmt-newt-popup.h
+++ b/src/libnmt-newt/nmt-newt-popup.h
@@ -9,7 +9,8 @@
 #include "nmt-newt-button.h"
 
 #define NMT_TYPE_NEWT_POPUP (nmt_newt_popup_get_type())
-#define NMT_NEWT_POPUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_POPUP, NmtNewtPopup))
+#define NMT_NEWT_POPUP(obj) \
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_POPUP, NmtNewtPopup))
 #define NMT_NEWT_POPUP_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_POPUP, NmtNewtPopupClass))
 #define NMT_IS_NEWT_POPUP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_POPUP))
diff --git a/src/libnmt-newt/nmt-newt-section.h b/src/libnmt-newt/nmt-newt-section.h
index c3b9f909..a40c043d 100644
--- a/src/libnmt-newt/nmt-newt-section.h
+++ b/src/libnmt-newt/nmt-newt-section.h
@@ -10,7 +10,7 @@
 
 #define NMT_TYPE_NEWT_SECTION (nmt_newt_section_get_type())
 #define NMT_NEWT_SECTION(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_SECTION, NmtNewtSection))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_SECTION, NmtNewtSection))
 #define NMT_NEWT_SECTION_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_SECTION, NmtNewtSectionClass))
 #define NMT_IS_NEWT_SECTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_SECTION))
diff --git a/src/libnmt-newt/nmt-newt-separator.h b/src/libnmt-newt/nmt-newt-separator.h
index 0aa0fd0e..432e1495 100644
--- a/src/libnmt-newt/nmt-newt-separator.h
+++ b/src/libnmt-newt/nmt-newt-separator.h
@@ -10,7 +10,7 @@
 
 #define NMT_TYPE_NEWT_SEPARATOR (nmt_newt_separator_get_type())
 #define NMT_NEWT_SEPARATOR(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparator))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparator))
 #define NMT_NEWT_SEPARATOR_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparatorClass))
 #define NMT_IS_NEWT_SEPARATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_SEPARATOR))
diff --git a/src/libnmt-newt/nmt-newt-stack.c b/src/libnmt-newt/nmt-newt-stack.c
index bb05e51b..ef7454f5 100644
--- a/src/libnmt-newt/nmt-newt-stack.c
+++ b/src/libnmt-newt/nmt-newt-stack.c
@@ -166,8 +166,7 @@ nmt_newt_stack_child_validity_changed(NmtNewtContainer *container, NmtNewtWidget
         return;
 
     if (priv->children->pdata[priv->active] == (gpointer) widget) {
-        NMT_NEWT_CONTAINER_CLASS(nmt_newt_stack_parent_class)
-            ->child_validity_changed(container, widget);
+        nmt_newt_widget_set_valid(NMT_NEWT_WIDGET(container), nmt_newt_widget_get_valid(widget));
     }
 }
 
@@ -190,6 +189,7 @@ nmt_newt_stack_set_active(NmtNewtStack *stack, guint active)
     g_object_notify(G_OBJECT(stack), "active");
     g_object_notify(G_OBJECT(stack), "active-id");
     nmt_newt_widget_needs_rebuild(NMT_NEWT_WIDGET(stack));
+    nmt_newt_stack_child_validity_changed(NMT_NEWT_CONTAINER(stack), priv->children->pdata[active]);
 }
 
 /**
@@ -230,6 +230,8 @@ nmt_newt_stack_set_active_id(NmtNewtStack *stack, const char *id)
             g_object_notify(G_OBJECT(stack), "active");
             g_object_notify(G_OBJECT(stack), "active-id");
             nmt_newt_widget_needs_rebuild(NMT_NEWT_WIDGET(stack));
+            nmt_newt_stack_child_validity_changed(NMT_NEWT_CONTAINER(stack),
+                                                  priv->children->pdata[i]);
             return;
         }
     }
diff --git a/src/libnmt-newt/nmt-newt-stack.h b/src/libnmt-newt/nmt-newt-stack.h
index ca6ad203..ec13a2b5 100644
--- a/src/libnmt-newt/nmt-newt-stack.h
+++ b/src/libnmt-newt/nmt-newt-stack.h
@@ -9,7 +9,8 @@
 #include "nmt-newt-container.h"
 
 #define NMT_TYPE_NEWT_STACK (nmt_newt_stack_get_type())
-#define NMT_NEWT_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_STACK, NmtNewtStack))
+#define NMT_NEWT_STACK(obj) \
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_STACK, NmtNewtStack))
 #define NMT_NEWT_STACK_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_STACK, NmtNewtStackClass))
 #define NMT_IS_NEWT_STACK(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_STACK))
diff --git a/src/libnmt-newt/nmt-newt-textbox.h b/src/libnmt-newt/nmt-newt-textbox.h
index f589d62c..9d35fd9d 100644
--- a/src/libnmt-newt/nmt-newt-textbox.h
+++ b/src/libnmt-newt/nmt-newt-textbox.h
@@ -10,7 +10,7 @@
 
 #define NMT_TYPE_NEWT_TEXTBOX (nmt_newt_textbox_get_type())
 #define NMT_NEWT_TEXTBOX(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextbox))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextbox))
 #define NMT_NEWT_TEXTBOX_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextboxClass))
 #define NMT_IS_NEWT_TEXTBOX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_TEXTBOX))
diff --git a/src/libnmt-newt/nmt-newt-toggle-button.h b/src/libnmt-newt/nmt-newt-toggle-button.h
index bdaa445d..0eb1307e 100644
--- a/src/libnmt-newt/nmt-newt-toggle-button.h
+++ b/src/libnmt-newt/nmt-newt-toggle-button.h
@@ -10,7 +10,7 @@
 
 #define NMT_TYPE_NEWT_TOGGLE_BUTTON (nmt_newt_toggle_button_get_type())
 #define NMT_NEWT_TOGGLE_BUTTON(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButton))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButton))
 #define NMT_NEWT_TOGGLE_BUTTON_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButtonClass))
 #define NMT_IS_NEWT_TOGGLE_BUTTON(obj) \
diff --git a/src/libnmt-newt/nmt-newt-widget.h b/src/libnmt-newt/nmt-newt-widget.h
index 70b62d29..4c669fca 100644
--- a/src/libnmt-newt/nmt-newt-widget.h
+++ b/src/libnmt-newt/nmt-newt-widget.h
@@ -10,7 +10,7 @@
 
 #define NMT_TYPE_NEWT_WIDGET (nmt_newt_widget_get_type())
 #define NMT_NEWT_WIDGET(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_WIDGET, NmtNewtWidget))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_WIDGET, NmtNewtWidget))
 #define NMT_NEWT_WIDGET_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetClass))
 #define NMT_IS_NEWT_WIDGET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_WIDGET))
diff --git a/src/meson.build b/src/meson.build
index f3c87f1a..92e95e68 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -78,6 +78,7 @@ subdir('libnm-systemd-core')
 subdir('libnm-udev-aux')
 subdir('libnm-base')
 subdir('libnm-platform')
+subdir('libnm-lldp')
 subdir('libnm-crypto')
 subdir('libnm-core-public')
 subdir('libnm-core-intern')
diff --git a/src/nm-cloud-setup/main.c b/src/nm-cloud-setup/main.c
index e1cbd1d4..d6802634 100644
--- a/src/nm-cloud-setup/main.c
+++ b/src/nm-cloud-setup/main.c
@@ -347,13 +347,12 @@ _nmc_mangle_connection(NMDevice                             *device,
          * We don't need to configure policy routing in this case. */
         NM_SET_OUT(out_skipped_single_addr, TRUE);
     } else if (config_data->has_ipv4s && config_data->has_cidr) {
-        gs_unref_hashtable GHashTable *unique_subnets =
-            g_hash_table_new(nm_direct_hash, g_direct_equal);
-        NMIPAddress     *addr_entry;
-        NMIPRoute       *route_entry;
-        NMIPRoutingRule *rule_entry;
-        in_addr_t        gateway;
-        char             sbuf[NM_UTILS_INET_ADDRSTRLEN];
+        gs_unref_hashtable GHashTable *unique_subnets = g_hash_table_new(nm_direct_hash, NULL);
+        NMIPAddress                   *addr_entry;
+        NMIPRoute                     *route_entry;
+        NMIPRoutingRule               *rule_entry;
+        in_addr_t                      gateway;
+        char                           sbuf[NM_INET_ADDRSTRLEN];
 
         for (i = 0; i < config_data->ipv4s_len; i++) {
             addr_entry = nm_ip_address_new_binary(AF_INET,
@@ -367,8 +366,8 @@ _nmc_mangle_connection(NMDevice                             *device,
         if (config_data->has_gateway && config_data->gateway) {
             gateway = config_data->gateway;
         } else {
-            gateway = nm_utils_ip4_address_clear_host_address(config_data->cidr_addr,
-                                                              config_data->cidr_prefix);
+            gateway =
+                nm_ip4_addr_clear_host_address(config_data->cidr_addr, config_data->cidr_prefix);
             if (config_data->cidr_prefix < 32)
                 ((guint8 *) &gateway)[3] += 1;
         }
@@ -376,7 +375,7 @@ _nmc_mangle_connection(NMDevice                             *device,
         for (i = 0; i < config_data->ipv4s_len; i++) {
             in_addr_t a = config_data->ipv4s_arr[i];
 
-            a = nm_utils_ip4_address_clear_host_address(a, config_data->cidr_prefix);
+            a = nm_ip4_addr_clear_host_address(a, config_data->cidr_prefix);
 
             G_STATIC_ASSERT_EXPR(sizeof(gsize) >= sizeof(in_addr_t));
             if (g_hash_table_add(unique_subnets, GSIZE_TO_POINTER(a))) {
@@ -391,7 +390,7 @@ _nmc_mangle_connection(NMDevice                             *device,
             rule_entry = nm_ip_routing_rule_new(AF_INET);
             nm_ip_routing_rule_set_priority(rule_entry, 30200 + config_data->iface_idx);
             nm_ip_routing_rule_set_from(rule_entry,
-                                        _nm_utils_inet4_ntop(config_data->ipv4s_arr[i], sbuf),
+                                        nm_inet4_ntop(config_data->ipv4s_arr[i], sbuf),
                                         32);
             nm_ip_routing_rule_set_table(rule_entry, 30200 + config_data->iface_idx);
             nm_assert(nm_ip_routing_rule_validate(rule_entry, NULL));
@@ -415,7 +414,7 @@ _nmc_mangle_connection(NMDevice                             *device,
             rule_entry = nm_ip_routing_rule_new(AF_INET);
             nm_ip_routing_rule_set_priority(rule_entry, 30400 + config_data->iface_idx);
             nm_ip_routing_rule_set_from(rule_entry,
-                                        _nm_utils_inet4_ntop(config_data->ipv4s_arr[i], sbuf),
+                                        nm_inet4_ntop(config_data->ipv4s_arr[i], sbuf),
                                         32);
             nm_ip_routing_rule_set_table(rule_entry, 30400 + config_data->iface_idx);
             nm_assert(nm_ip_routing_rule_validate(rule_entry, NULL));
@@ -553,12 +552,11 @@ try_again:
     /* we are about to call Reapply(). Even if that fails, it counts as if we changed something. */
     any_changes = TRUE;
 
-    /* "preserve-external-ip" flag was only introduced in 1.41.6 and 1.40.9.
-     * We have no convenient way to check the daemon version (short of parsing the "Version"
-     * string). Hence, we don't know it. Take into account, that the daemon that we
-     * talk to might not support the flag yet. This is to support backward compatibility
-     * during package upgrade. */
-    maybe_no_preserved_external_ip = TRUE;
+    /* "preserve-external-ip" flag was only introduced in 1.41.6 (but maybe backported!).
+     * If we run 1.41.6+, we are sure that it's gonna work. Otherwise, we take into account
+     * that the call might fail due to the invalid flag and we retry. */
+    maybe_no_preserved_external_ip =
+        (nmc_client_has_version_info_v(nmc) < NM_ENCODE_VERSION(1, 41, 6));
 
     if (!nmcs_device_reapply(device,
                              sigterm_cancellable,
diff --git a/src/nm-cloud-setup/nm-cloud-setup-utils.c b/src/nm-cloud-setup/nm-cloud-setup-utils.c
index 7cf79592..cb8e9f55 100644
--- a/src/nm-cloud-setup/nm-cloud-setup-utils.c
+++ b/src/nm-cloud-setup/nm-cloud-setup-utils.c
@@ -507,7 +507,7 @@ nmcs_utils_ipaddr_normalize_bin(int         addr_family,
 
     g_strstrip(ad);
 
-    return nm_utils_parse_inaddr_bin(addr_family, ad, out_addr_family, out_addr_bin);
+    return nm_inet_parse_bin(addr_family, ad, out_addr_family, out_addr_bin);
 }
 
 char *
@@ -518,7 +518,7 @@ nmcs_utils_ipaddr_normalize(int addr_family, const char *addr, gssize len)
     if (!nmcs_utils_ipaddr_normalize_bin(addr_family, addr, len, &addr_family, &ipaddr))
         return NULL;
 
-    return nm_utils_inet_ntop_dup(addr_family, &ipaddr);
+    return nm_inet_ntop_dup(addr_family, &ipaddr);
 }
 
 /*****************************************************************************/
diff --git a/src/nm-cloud-setup/nm-http-client.h b/src/nm-cloud-setup/nm-http-client.h
index 0a7052ae..b041e99f 100644
--- a/src/nm-cloud-setup/nm-http-client.h
+++ b/src/nm-cloud-setup/nm-http-client.h
@@ -9,7 +9,8 @@ typedef struct _NMHttpClient      NMHttpClient;
 typedef struct _NMHttpClientClass NMHttpClientClass;
 
 #define NM_TYPE_HTTP_CLIENT (nm_http_client_get_type())
-#define NM_HTTP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_HTTP_CLIENT, NMHttpClient))
+#define NM_HTTP_CLIENT(obj) \
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_HTTP_CLIENT, NMHttpClient))
 #define NM_HTTP_CLIENT_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_HTTP_CLIENT, NMHttpClientClass))
 #define NM_IS_HTTP_CLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_HTTP_CLIENT))
diff --git a/src/nm-cloud-setup/nmcs-provider-aliyun.c b/src/nm-cloud-setup/nmcs-provider-aliyun.c
index 34ab5ecc..4b109609 100644
--- a/src/nm-cloud-setup/nmcs-provider-aliyun.c
+++ b/src/nm-cloud-setup/nmcs-provider-aliyun.c
@@ -167,7 +167,7 @@ _get_config_fetch_done_cb(NMHttpClient                   *http_client,
             config_iface_data->ipv4s_arr = g_new(in_addr_t, len);
             for (i = 0; i < len; i++) {
                 filter_chars((char *) s_addrs[i], "[]\"");
-                if (nm_utils_parse_inaddr_bin(AF_INET, s_addrs[i], NULL, &tmp_addr)) {
+                if (nm_inet_parse_bin(AF_INET, s_addrs[i], NULL, &tmp_addr)) {
                     config_iface_data->ipv4s_arr[config_iface_data->ipv4s_len++] = tmp_addr;
                 }
             }
@@ -176,7 +176,7 @@ _get_config_fetch_done_cb(NMHttpClient                   *http_client,
 
     case GET_CONFIG_FETCH_DONE_TYPE_PRIMARY_IP_ADDRESS:
 
-        if (nm_utils_parse_inaddr_bin(AF_INET, g_bytes_get_data(response, NULL), NULL, &tmp_addr)) {
+        if (nm_inet_parse_bin(AF_INET, g_bytes_get_data(response, NULL), NULL, &tmp_addr)) {
             nm_assert(config_iface_data->priv.aliyun.primary_ip_address == 0);
             nm_assert(!config_iface_data->priv.aliyun.has_primary_ip_address);
             config_iface_data->priv.aliyun.primary_ip_address     = tmp_addr;
@@ -186,11 +186,11 @@ _get_config_fetch_done_cb(NMHttpClient                   *http_client,
 
     case GET_CONFIG_FETCH_DONE_TYPE_SUBNET_VPC_CIDR_BLOCK:
 
-        if (nm_utils_parse_inaddr_prefix_bin(AF_INET,
-                                             g_bytes_get_data(response, NULL),
-                                             NULL,
-                                             &tmp_addr,
-                                             &tmp_prefix)) {
+        if (nm_inet_parse_with_prefix_bin(AF_INET,
+                                          g_bytes_get_data(response, NULL),
+                                          NULL,
+                                          &tmp_addr,
+                                          &tmp_prefix)) {
             nm_assert(!config_iface_data->has_cidr);
             config_iface_data->has_cidr  = TRUE;
             config_iface_data->cidr_addr = tmp_addr;
@@ -199,20 +199,14 @@ _get_config_fetch_done_cb(NMHttpClient                   *http_client,
 
     case GET_CONFIG_FETCH_DONE_TYPE_NETMASK:
 
-        if (nm_utils_parse_inaddr_bin(AF_INET,
-                                      g_bytes_get_data(response, NULL),
-                                      NULL,
-                                      &netmask_bin)) {
-            config_iface_data->cidr_prefix = _nm_utils_ip4_netmask_to_prefix(netmask_bin);
+        if (nm_inet_parse_bin(AF_INET, g_bytes_get_data(response, NULL), NULL, &netmask_bin)) {
+            config_iface_data->cidr_prefix = nm_ip4_addr_netmask_to_prefix(netmask_bin);
         };
         break;
 
     case GET_CONFIG_FETCH_DONE_TYPE_GATEWAY:
 
-        if (nm_utils_parse_inaddr_bin(AF_INET,
-                                      g_bytes_get_data(response, NULL),
-                                      NULL,
-                                      &gateway_bin)) {
+        if (nm_inet_parse_bin(AF_INET, g_bytes_get_data(response, NULL), NULL, &gateway_bin)) {
             config_iface_data->has_gateway = TRUE;
             config_iface_data->gateway     = gateway_bin;
         };
diff --git a/src/nm-cloud-setup/nmcs-provider-aliyun.h b/src/nm-cloud-setup/nmcs-provider-aliyun.h
index 6e733a11..3e8ab454 100644
--- a/src/nm-cloud-setup/nmcs-provider-aliyun.h
+++ b/src/nm-cloud-setup/nmcs-provider-aliyun.h
@@ -12,7 +12,7 @@ typedef struct _NMCSProviderAliyunClass NMCSProviderAliyunClass;
 
 #define NMCS_TYPE_PROVIDER_ALIYUN (nmcs_provider_aliyun_get_type())
 #define NMCS_PROVIDER_ALIYUN(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMCS_TYPE_PROVIDER_ALIYUN, NMCSProviderAliyun))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMCS_TYPE_PROVIDER_ALIYUN, NMCSProviderAliyun))
 #define NMCS_PROVIDER_ALIYUN_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMCS_TYPE_PROVIDER_ALIYUN, NMCSProviderAliyunClass))
 #define NMCS_IS_PROVIDER_ALIYUN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMCS_TYPE_PROVIDER_ALIYUN))
diff --git a/src/nm-cloud-setup/nmcs-provider-azure.c b/src/nm-cloud-setup/nmcs-provider-azure.c
index 9b27af28..b59235bf 100644
--- a/src/nm-cloud-setup/nmcs-provider-azure.c
+++ b/src/nm-cloud-setup/nmcs-provider-azure.c
@@ -117,7 +117,7 @@ _get_config_fetch_done_cb(NMHttpClient      *http_client,
     gs_free_error GError           *error    = NULL;
     const char                     *resp_str = NULL;
     gsize                           resp_len;
-    char                            tmp_addr_str[NM_UTILS_INET_ADDRSTRLEN];
+    char                            tmp_addr_str[NM_INET_ADDRSTRLEN];
     in_addr_t                       tmp_addr;
     int                             tmp_prefix = -1;
 
@@ -145,7 +145,7 @@ _get_config_fetch_done_cb(NMHttpClient      *http_client,
         }
         _LOGD("interface[%" G_GSSIZE_FORMAT "]: received address %s",
               iface_data->intern_iface_idx,
-              _nm_utils_inet4_ntop(tmp_addr, tmp_addr_str));
+              nm_inet4_ntop(tmp_addr, tmp_addr_str));
         iface_get_config->ipv4s_arr[iface_get_config->ipv4s_len] = tmp_addr;
         iface_get_config->has_ipv4s                              = TRUE;
         iface_get_config->ipv4s_len++;
@@ -159,7 +159,7 @@ _get_config_fetch_done_cb(NMHttpClient      *http_client,
         }
         _LOGD("interface[%" G_GSSIZE_FORMAT "]: received subnet address %s",
               iface_data->intern_iface_idx,
-              _nm_utils_inet4_ntop(tmp_addr, tmp_addr_str));
+              nm_inet4_ntop(tmp_addr, tmp_addr_str));
         iface_get_config->cidr_addr = tmp_addr;
         break;
 
diff --git a/src/nm-cloud-setup/nmcs-provider-azure.h b/src/nm-cloud-setup/nmcs-provider-azure.h
index be78768f..913dd0e8 100644
--- a/src/nm-cloud-setup/nmcs-provider-azure.h
+++ b/src/nm-cloud-setup/nmcs-provider-azure.h
@@ -12,7 +12,7 @@ typedef struct _NMCSProviderAzureClass NMCSProviderAzureClass;
 
 #define NMCS_TYPE_PROVIDER_AZURE (nmcs_provider_azure_get_type())
 #define NMCS_PROVIDER_AZURE(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMCS_TYPE_PROVIDER_AZURE, NMCSProviderAzure))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMCS_TYPE_PROVIDER_AZURE, NMCSProviderAzure))
 #define NMCS_PROVIDER_AZURE_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMCS_TYPE_PROVIDER_AZURE, NMCSProviderAzureClass))
 #define NMCS_IS_PROVIDER_AZURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMCS_TYPE_PROVIDER_AZURE))
diff --git a/src/nm-cloud-setup/nmcs-provider-ec2.c b/src/nm-cloud-setup/nmcs-provider-ec2.c
index d6fa0311..58d691c7 100644
--- a/src/nm-cloud-setup/nmcs-provider-ec2.c
+++ b/src/nm-cloud-setup/nmcs-provider-ec2.c
@@ -151,16 +151,16 @@ _get_config_fetch_done_cb(NMHttpClient                   *http_client,
             config_iface_data->ipv4s_arr = g_new(in_addr_t, len);
 
             for (i = 0; i < len; i++) {
-                if (nm_utils_parse_inaddr_bin(AF_INET, s_addrs[i], NULL, &tmp_addr))
+                if (nm_inet_parse_bin(AF_INET, s_addrs[i], NULL, &tmp_addr))
                     config_iface_data->ipv4s_arr[config_iface_data->ipv4s_len++] = tmp_addr;
             }
         }
     } else {
-        if (nm_utils_parse_inaddr_prefix_bin(AF_INET,
-                                             g_bytes_get_data(response, NULL),
-                                             NULL,
-                                             &tmp_addr,
-                                             &tmp_prefix)) {
+        if (nm_inet_parse_with_prefix_bin(AF_INET,
+                                          g_bytes_get_data(response, NULL),
+                                          NULL,
+                                          &tmp_addr,
+                                          &tmp_prefix)) {
             nm_assert(!config_iface_data->has_cidr);
             config_iface_data->has_cidr    = TRUE;
             config_iface_data->cidr_prefix = tmp_prefix;
diff --git a/src/nm-cloud-setup/nmcs-provider-ec2.h b/src/nm-cloud-setup/nmcs-provider-ec2.h
index 8f8b6400..e44f0a0f 100644
--- a/src/nm-cloud-setup/nmcs-provider-ec2.h
+++ b/src/nm-cloud-setup/nmcs-provider-ec2.h
@@ -12,7 +12,7 @@ typedef struct _NMCSProviderEC2Class NMCSProviderEC2Class;
 
 #define NMCS_TYPE_PROVIDER_EC2 (nmcs_provider_ec2_get_type())
 #define NMCS_PROVIDER_EC2(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMCS_TYPE_PROVIDER_EC2, NMCSProviderEC2))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMCS_TYPE_PROVIDER_EC2, NMCSProviderEC2))
 #define NMCS_PROVIDER_EC2_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMCS_TYPE_PROVIDER_EC2, NMCSProviderEC2Class))
 #define NMCS_IS_PROVIDER_EC2(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMCS_TYPE_PROVIDER_EC2))
diff --git a/src/nm-cloud-setup/nmcs-provider-gcp.h b/src/nm-cloud-setup/nmcs-provider-gcp.h
index b0329935..80e4ec41 100644
--- a/src/nm-cloud-setup/nmcs-provider-gcp.h
+++ b/src/nm-cloud-setup/nmcs-provider-gcp.h
@@ -12,7 +12,7 @@ typedef struct _NMCSProviderGCPClass NMCSProviderGCPClass;
 
 #define NMCS_TYPE_PROVIDER_GCP (nmcs_provider_gcp_get_type())
 #define NMCS_PROVIDER_GCP(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NMCS_TYPE_PROVIDER_GCP, NMCSProviderGCP))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMCS_TYPE_PROVIDER_GCP, NMCSProviderGCP))
 #define NMCS_PROVIDER_GCP_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMCS_TYPE_PROVIDER_GCP, NMCSProviderGCPClass))
 #define NMCS_IS_PROVIDER_GCP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMCS_TYPE_PROVIDER_GCP))
diff --git a/src/nm-cloud-setup/nmcs-provider.h b/src/nm-cloud-setup/nmcs-provider.h
index 502f1d03..994b1e24 100644
--- a/src/nm-cloud-setup/nmcs-provider.h
+++ b/src/nm-cloud-setup/nmcs-provider.h
@@ -130,7 +130,7 @@ nmcs_provider_get_config_iface_data_create(NMCSProviderGetConfigTaskData *get_co
 /*****************************************************************************/
 
 #define NMCS_TYPE_PROVIDER (nmcs_provider_get_type())
-#define NMCS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMCS_TYPE_PROVIDER, NMCSProvider))
+#define NMCS_PROVIDER(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMCS_TYPE_PROVIDER, NMCSProvider))
 #define NMCS_PROVIDER_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NMCS_TYPE_PROVIDER, NMCSProviderClass))
 #define NMCS_IS_PROVIDER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMCS_TYPE_PROVIDER))
diff --git a/src/nm-compat-headers/linux/if_addr.h b/src/nm-compat-headers/linux/if_addr.h
new file mode 100644
index 00000000..51cdf7ea
--- /dev/null
+++ b/src/nm-compat-headers/linux/if_addr.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#ifndef __NM_COMPAT_HEADER_LINUX_IF_ADDR_H__
+#define __NM_COMPAT_HEADER_LINUX_IF_ADDR_H__
+
+#include 
+
+#ifndef IFA_F_MANAGETEMPADDR
+#define IFA_F_MANAGETEMPADDR 0x100
+#endif
+
+#ifndef IFA_F_NOPREFIXROUTE
+#define IFA_F_NOPREFIXROUTE 0x200
+#endif
+
+#endif /* __NM_COMPAT_HEADER_LINUX_IF_ADDR_H__ */
diff --git a/src/nm-dispatcher/nm-dispatcher.c b/src/nm-dispatcher/nm-dispatcher.c
index 4cf662c4..80fe40a0 100644
--- a/src/nm-dispatcher/nm-dispatcher.c
+++ b/src/nm-dispatcher/nm-dispatcher.c
@@ -62,7 +62,7 @@ typedef struct {
     bool shutdown_quitting;
 } GlobalData;
 
-GlobalData gl;
+static GlobalData gl;
 
 typedef struct {
     Request *request;
@@ -1138,25 +1138,25 @@ _initial_setup(int *p_argc, char ***p_argv, GError **error)
     GOptionContext *opt_ctx;
     gboolean        arg_debug = FALSE;
     GOptionEntry    entries[] = {{
-                                     "debug",
-                                     0,
-                                     0,
-                                     G_OPTION_ARG_NONE,
-                                     &arg_debug,
-                                     "Output to console rather than syslog",
-                                     NULL,
+                                  "debug",
+                                  0,
+                                  0,
+                                  G_OPTION_ARG_NONE,
+                                  &arg_debug,
+                                  "Output to console rather than syslog",
+                                  NULL,
                               },
                                  {
-                                     "persist",
-                                     0,
-                                     0,
-                                     G_OPTION_ARG_NONE,
-                                     &gl.persist,
-                                     "Don't quit after a short timeout",
-                                     NULL,
+                                  "persist",
+                                  0,
+                                  0,
+                                  G_OPTION_ARG_NONE,
+                                  &gl.persist,
+                                  "Don't quit after a short timeout",
+                                  NULL,
                               },
                                  {
-                                     NULL,
+                                  NULL,
                               }};
     gboolean        success;
 
diff --git a/src/nm-initrd-generator/nm-initrd-generator.c b/src/nm-initrd-generator/nm-initrd-generator.c
index 09f8baf2..d84e95e5 100644
--- a/src/nm-initrd-generator/nm-initrd-generator.c
+++ b/src/nm-initrd-generator/nm-initrd-generator.c
@@ -103,50 +103,50 @@ main(int argc, char *argv[])
     gboolean           dump_to_stdout      = FALSE;
     gs_strfreev char **remaining           = NULL;
     GOptionEntry       option_entries[]    = {
-                 {"connections-dir",
+        {"connections-dir",
                   'c',
                   0,
                   G_OPTION_ARG_FILENAME,
                   &connections_dir,
                   "Output connection directory",
                   NM_KEYFILE_PATH_NAME_RUN},
-                 {"persistent-connections-dir",
+        {"persistent-connections-dir",
                   'p',
                   0,
                   G_OPTION_ARG_FILENAME,
                   &etc_connections_dir,
                   "Persistent connection directory",
                   NM_KEYFILE_PATH_NAME_ETC_DEFAULT},
-                 {"initrd-data-dir",
+        {"initrd-data-dir",
                   'i',
                   0,
                   G_OPTION_ARG_FILENAME,
                   &initrd_dir,
                   "Output initrd data directory",
                   DEFAULT_INITRD_DATA_DIR},
-                 {"sysfs-dir",
+        {"sysfs-dir",
                   'd',
                   0,
                   G_OPTION_ARG_FILENAME,
                   &sysfs_dir,
                   "The sysfs mount point",
                   DEFAULT_SYSFS_DIR},
-                 {"run-config-dir",
+        {"run-config-dir",
                   'r',
                   0,
                   G_OPTION_ARG_FILENAME,
                   &run_config_dir,
                   "Output config directory",
                   DEFAULT_RUN_CONFIG_DIR},
-                 {"stdout",
+        {"stdout",
                   's',
                   0,
                   G_OPTION_ARG_NONE,
                   &dump_to_stdout,
                   "Dump connections to standard output",
                   NULL},
-                 {G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &remaining, NULL, NULL},
-                 {NULL}};
+        {G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &remaining, NULL, NULL},
+        {NULL}};
     nm_auto_free_option_context GOptionContext *option_context = NULL;
     gs_free_error GError                       *error          = NULL;
     gs_free char                               *hostname       = NULL;
@@ -241,7 +241,7 @@ main(int argc, char *argv[])
             g_print("\n*** Hostname '%s' ***\n", hostname);
 
         for (i = 0; i < confs->len; i++) {
-            NMUtilsNamedValue *v    = &g_array_index(confs, NMUtilsNamedValue, i);
+            NMUtilsNamedValue *v    = &nm_g_array_index(confs, NMUtilsNamedValue, i);
             gs_free char      *name = g_path_get_basename(v->name);
 
             g_print("\n*** Configuration '%s' ***\n\n%s\n", name, v->value_str);
@@ -277,7 +277,7 @@ main(int argc, char *argv[])
         }
 
         for (i = 0; i < confs->len; i++) {
-            NMUtilsNamedValue *v = &g_array_index(confs, NMUtilsNamedValue, i);
+            NMUtilsNamedValue *v = &nm_g_array_index(confs, NMUtilsNamedValue, i);
 
             if (!g_file_set_contents(v->name, v->value_str, strlen(v->value_str), &error)) {
                 _LOGW(LOGD_CORE, "%s: %s", v->name, error->message);
diff --git a/src/nm-initrd-generator/nm-initrd-generator.h b/src/nm-initrd-generator/nm-initrd-generator.h
index 0761c9bb..87db9fc6 100644
--- a/src/nm-initrd-generator/nm-initrd-generator.h
+++ b/src/nm-initrd-generator/nm-initrd-generator.h
@@ -24,10 +24,10 @@ get_ip_address_family(const char *str, gboolean with_prefix)
         return AF_UNSPEC;
 
     if (with_prefix) {
-        if (nm_utils_parse_inaddr_prefix_bin(AF_UNSPEC, str, &addr_family, NULL, NULL))
+        if (nm_inet_parse_with_prefix_bin(AF_UNSPEC, str, &addr_family, NULL, NULL))
             return addr_family;
     } else {
-        if (nm_utils_parse_inaddr_bin(AF_UNSPEC, str, &addr_family, NULL))
+        if (nm_inet_parse_bin(AF_UNSPEC, str, &addr_family, NULL))
             return addr_family;
     }
 
diff --git a/src/nm-initrd-generator/nmi-cmdline-reader.c b/src/nm-initrd-generator/nmi-cmdline-reader.c
index 36bfdb0b..bafad041 100644
--- a/src/nm-initrd-generator/nmi-cmdline-reader.c
+++ b/src/nm-initrd-generator/nmi-cmdline-reader.c
@@ -539,8 +539,8 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument)
     int                            client_ip_prefix           = -1;
     gboolean                       clear_ip4_required_timeout = TRUE;
     const char                    *dns[2]                     = {
-                                               NULL,
-                                               NULL,
+        NULL,
+        NULL,
     };
     int dns_addr_family[2] = {
         AF_UNSPEC,
@@ -637,8 +637,8 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument)
         gboolean is_ipv4 = client_ip_family == AF_INET;
         NMIPAddr addr;
 
-        if (is_ipv4 && nm_utils_parse_inaddr_bin(AF_INET, netmask, NULL, &addr))
-            client_ip_prefix = _nm_utils_ip4_netmask_to_prefix(addr.addr4);
+        if (is_ipv4 && nm_inet_parse_bin(AF_INET, netmask, NULL, &addr))
+            client_ip_prefix = nm_ip4_addr_netmask_to_prefix(addr.addr4);
         else
             client_ip_prefix = _nm_utils_ascii_str_to_int64(netmask, 10, 0, is_ipv4 ? 32 : 128, -1);
 
@@ -651,15 +651,15 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument)
         NMIPAddress *address = NULL;
         NMIPAddr     addr;
 
-        if (nm_utils_parse_inaddr_prefix_bin(client_ip_family,
-                                             client_ip,
-                                             NULL,
-                                             &addr,
-                                             client_ip_prefix == -1 ? &client_ip_prefix : NULL)) {
+        if (nm_inet_parse_with_prefix_bin(client_ip_family,
+                                          client_ip,
+                                          NULL,
+                                          &addr,
+                                          client_ip_prefix == -1 ? &client_ip_prefix : NULL)) {
             if (client_ip_prefix == -1) {
                 switch (client_ip_family) {
                 case AF_INET:
-                    client_ip_prefix = _nm_utils_ip4_get_default_prefix(addr.addr4);
+                    client_ip_prefix = nm_ip4_addr_get_default_prefix(addr.addr4);
                     break;
                 case AF_INET6:
                     client_ip_prefix = 64;
@@ -847,7 +847,7 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument)
     for (i = 0; i < 2; i++) {
         if (dns_addr_family[i] == AF_UNSPEC)
             break;
-        nm_assert(nm_utils_ipaddr_is_valid(dns_addr_family[i], dns[i]));
+        nm_assert(nm_inet_is_valid(dns_addr_family[i], dns[i]));
         nm_setting_ip_config_add_dns(NM_IS_IPv4(dns_addr_family[i]) ? s_ip4 : s_ip6, dns[i]);
     }
 
@@ -969,14 +969,14 @@ reader_add_routes(Reader *reader, GPtrArray *array)
             connection = reader_get_default_connection(reader);
 
         if (net && *net) {
-            if (!nm_utils_parse_inaddr_prefix_bin(family, net, &family, &net_addr, &net_prefix)) {
+            if (!nm_inet_parse_with_prefix_bin(family, net, &family, &net_addr, &net_prefix)) {
                 _LOGW(LOGD_CORE, "Unrecognized address: %s", net);
                 continue;
             }
         }
 
         if (gateway && *gateway) {
-            if (!nm_utils_parse_inaddr_bin(family, gateway, &family, &gateway_addr)) {
+            if (!nm_inet_parse_bin(family, gateway, &family, &gateway_addr)) {
                 _LOGW(LOGD_CORE, "Unrecognized address: %s", gateway);
                 continue;
             }
diff --git a/src/nm-initrd-generator/nmi-dt-reader.c b/src/nm-initrd-generator/nmi-dt-reader.c
index 99fe25a2..f1279d58 100644
--- a/src/nm-initrd-generator/nmi-dt-reader.c
+++ b/src/nm-initrd-generator/nmi-dt-reader.c
@@ -96,7 +96,7 @@ str_addr(const char *str, int *family)
 {
     NMIPAddr addr_bin;
 
-    if (!nm_utils_parse_inaddr_bin_full(*family, TRUE, str, family, &addr_bin)) {
+    if (!nm_inet_parse_bin_full(*family, TRUE, str, family, &addr_bin)) {
         _LOGW(LOGD_CORE, "Malformed IP address: '%s'", str);
         return NULL;
     }
@@ -290,7 +290,7 @@ nmi_dt_reader_parse(const char *sysfs_dir)
             guint32 netmask_v4;
 
             nm_ip_address_get_address_binary(netmask, &netmask_v4);
-            prefix = _nm_utils_ip4_netmask_to_prefix(netmask_v4);
+            prefix = nm_ip4_addr_netmask_to_prefix(netmask_v4);
         }
 
         if (prefix == -1)
diff --git a/src/nm-initrd-generator/nmi-ibft-reader.c b/src/nm-initrd-generator/nmi-ibft-reader.c
index 3f30e531..2a3bbdfb 100644
--- a/src/nm-initrd-generator/nmi-ibft-reader.c
+++ b/src/nm-initrd-generator/nmi-ibft-reader.c
@@ -80,7 +80,7 @@ nmi_ibft_read(const char *sysfs_dir)
     gs_free char         *ibft_path = NULL;
     GDir                 *ibft_dir;
     const char           *dir_name;
-    GHashTable           *ibft, *nic;
+    GHashTable           *ibft;
     char                 *mac;
     gs_free_error GError *error = NULL;
 
@@ -93,10 +93,11 @@ nmi_ibft_read(const char *sysfs_dir)
                                  g_free,
                                  (GDestroyNotify) g_hash_table_unref);
 
-    if (!g_file_test(ibft_path, G_FILE_TEST_IS_DIR))
+    if (!g_file_test(ibft_path, G_FILE_TEST_IS_DIR)) {
         nmp_utils_modprobe(NULL, FALSE, "iscsi_ibft", NULL);
-    if (!g_file_test(ibft_path, G_FILE_TEST_IS_DIR))
-        return ibft;
+        if (!g_file_test(ibft_path, G_FILE_TEST_IS_DIR))
+            return ibft;
+    }
 
     ibft_dir = g_dir_open(ibft_path, 0, &error);
     if (!ibft_dir) {
@@ -105,20 +106,21 @@ nmi_ibft_read(const char *sysfs_dir)
     }
 
     while ((dir_name = g_dir_read_name(ibft_dir))) {
+        gs_unref_hashtable GHashTable *nic = NULL;
+
         if (!g_str_has_prefix(dir_name, "ethernet"))
             continue;
 
         nic = load_one_nic(ibft_path, dir_name);
-        mac = g_hash_table_lookup(nic, "mac");
+        mac = nm_g_hash_table_lookup(nic, "mac");
 
         if (!mac) {
             _LOGW(LOGD_CORE, "Ignoring an iBFT record without a MAC address");
-            g_hash_table_unref(nic);
             continue;
         }
 
         mac = g_ascii_strup(mac, -1);
-        if (!g_hash_table_insert(ibft, mac, nic))
+        if (!g_hash_table_insert(ibft, mac, g_steal_pointer(&nic)))
             _LOGW(LOGD_CORE, "Duplicate iBFT record for %s", mac);
     }
 
@@ -217,7 +219,7 @@ ip_setting_add_from_block(GHashTable *nic, NMConnection *connection, GError **er
                  FALSE,
                  NULL);
 
-    if (s_gateway && !nm_utils_ipaddr_is_valid(family, s_gateway)) {
+    if (s_gateway && !nm_inet_is_valid(family, s_gateway)) {
         g_set_error(error,
                     NM_SETTINGS_ERROR,
                     NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -226,7 +228,7 @@ ip_setting_add_from_block(GHashTable *nic, NMConnection *connection, GError **er
         return FALSE;
     }
 
-    if (s_dns1 && !nm_utils_ipaddr_is_valid(family, s_dns1)) {
+    if (s_dns1 && !nm_inet_is_valid(family, s_dns1)) {
         g_set_error(error,
                     NM_SETTINGS_ERROR,
                     NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -235,7 +237,7 @@ ip_setting_add_from_block(GHashTable *nic, NMConnection *connection, GError **er
         return FALSE;
     }
 
-    if (s_dns2 && !nm_utils_ipaddr_is_valid(family, s_dns2)) {
+    if (s_dns2 && !nm_inet_is_valid(family, s_dns2)) {
         g_set_error(error,
                     NM_SETTINGS_ERROR,
                     NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -305,13 +307,12 @@ connection_setting_add(GHashTable   *nic,
                          s_index ? " " : "",
                          s_index ? s_index : "");
 
-    uuid = nm_uuid_generate_from_strings("ibft",
-                                         s_hwaddr,
-                                         s_vlanid ? "V" : "v",
-                                         s_vlanid ? s_vlanid : "",
-                                         s_ipaddr ? "A" : "DHCP",
-                                         s_ipaddr ? s_ipaddr : "",
-                                         NULL);
+    uuid = nm_uuid_generate_from_strings_old("ibft",
+                                             s_hwaddr,
+                                             s_vlanid ? "V" : "v",
+                                             s_vlanid ? s_vlanid : "",
+                                             s_ipaddr ? "A" : "DHCP",
+                                             s_ipaddr ? s_ipaddr : "");
 
     s_con = (NMSetting *) nm_connection_get_setting_connection(connection);
     if (!s_con) {
diff --git a/src/nm-initrd-generator/tests/test-cmdline-reader.c b/src/nm-initrd-generator/tests/test-cmdline-reader.c
index 977e58b9..2dfc23a3 100644
--- a/src/nm-initrd-generator/tests/test-cmdline-reader.c
+++ b/src/nm-initrd-generator/tests/test-cmdline-reader.c
@@ -1680,13 +1680,12 @@ test_ibft_ip_dev(void)
                     NM_SETTING_VLAN_SETTING_NAME);
     g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, NULL);
 
-    expected_uuid = nm_uuid_generate_from_strings("ibft",
-                                                  s_hwaddr,
-                                                  s_vlanid ? "V" : "v",
-                                                  s_vlanid ? s_vlanid : "",
-                                                  s_ipaddr ? "A" : "DHCP",
-                                                  s_ipaddr ? s_ipaddr : "",
-                                                  NULL);
+    expected_uuid = nm_uuid_generate_from_strings_old("ibft",
+                                                      s_hwaddr,
+                                                      s_vlanid ? "V" : "v",
+                                                      s_vlanid ? s_vlanid : "",
+                                                      s_ipaddr ? "A" : "DHCP",
+                                                      s_ipaddr ? s_ipaddr : "");
 
     g_assert_cmpstr(expected_uuid, ==, "16d9bd1c-e2ab-31ef-9196-860078e81a23");
     g_assert_cmpstr(nm_connection_get_uuid(connection), ==, expected_uuid);
diff --git a/src/nm-online/nm-online.c b/src/nm-online/nm-online.c
index 0807270e..bba3cd5c 100644
--- a/src/nm-online/nm-online.c
+++ b/src/nm-online/nm-online.c
@@ -233,15 +233,15 @@ main(int argc, char *argv[])
     GOptionContext *opt_ctx = NULL;
     gboolean        success;
     GOptionEntry    options[] = {
-           {"quiet", 'q', 0, G_OPTION_ARG_NONE, &data.quiet, N_("Don't print anything"), NULL},
-           {"wait-for-startup",
+        {"quiet", 'q', 0, G_OPTION_ARG_NONE, &data.quiet, N_("Don't print anything"), NULL},
+        {"wait-for-startup",
             's',
             0,
             G_OPTION_ARG_NONE,
             &data.wait_startup,
             N_("Wait for NetworkManager startup instead of a connection"),
             NULL},
-           {"timeout",
+        {"timeout",
             't',
             0,
             G_OPTION_ARG_INT,
@@ -249,14 +249,14 @@ main(int argc, char *argv[])
             N_("Time to wait for a connection, in seconds (without the option, default value is 30). "
                "Maximum value is 2073600 seconds."),
             ""},
-           {"exit",
+        {"exit",
             'x',
             0,
             G_OPTION_ARG_NONE,
             &data.exit_no_nm,
             N_("Exit immediately if NetworkManager is not running or connecting"),
             NULL},
-           {NULL},
+        {NULL},
     };
 
     /* Set locale to be able to use environment variables */
diff --git a/src/nmcli/agent.c b/src/nmcli/agent.c
index 6d0fcfb5..b16a3c51 100644
--- a/src/nmcli/agent.c
+++ b/src/nmcli/agent.c
@@ -22,36 +22,36 @@
 static void
 usage(void)
 {
-    g_printerr(_("Usage: nmcli agent { COMMAND | help }\n\n"
-                 "COMMAND := { secret | polkit | all }\n\n"));
+    nmc_printerr(_("Usage: nmcli agent { COMMAND | help }\n\n"
+                   "COMMAND := { secret | polkit | all }\n\n"));
 }
 
 static void
 usage_agent_secret(void)
 {
-    g_printerr(_("Usage: nmcli agent secret { help }\n"
-                 "\n"
-                 "Runs nmcli as NetworkManager secret agent. When NetworkManager requires\n"
-                 "a password it asks registered agents for it. This command keeps nmcli running\n"
-                 "and if a password is required asks the user for it.\n\n"));
+    nmc_printerr(_("Usage: nmcli agent secret { help }\n"
+                   "\n"
+                   "Runs nmcli as NetworkManager secret agent. When NetworkManager requires\n"
+                   "a password it asks registered agents for it. This command keeps nmcli running\n"
+                   "and if a password is required asks the user for it.\n\n"));
 }
 
 static void
 usage_agent_polkit(void)
 {
-    g_printerr(_("Usage: nmcli agent polkit { help }\n"
-                 "\n"
-                 "Registers nmcli as a polkit action for the user session.\n"
-                 "When a polkit daemon requires an authorization, nmcli asks the user and gives\n"
-                 "the response back to polkit.\n\n"));
+    nmc_printerr(_("Usage: nmcli agent polkit { help }\n"
+                   "\n"
+                   "Registers nmcli as a polkit action for the user session.\n"
+                   "When a polkit daemon requires an authorization, nmcli asks the user and gives\n"
+                   "the response back to polkit.\n\n"));
 }
 
 static void
 usage_agent_all(void)
 {
-    g_printerr(_("Usage: nmcli agent all { help }\n"
-                 "\n"
-                 "Runs nmcli as both NetworkManager secret and a polkit agent.\n\n"));
+    nmc_printerr(_("Usage: nmcli agent all { help }\n"
+                   "\n"
+                   "Runs nmcli as both NetworkManager secret and a polkit agent.\n\n"));
 }
 
 static char *pre_input_deftext;
@@ -82,7 +82,7 @@ get_secrets_from_user(const NmcConfig *nmc_config,
 
         /* Ask user for the password */
         if (msg)
-            g_print("%s\n", msg);
+            nmc_print("%s\n", msg);
         if (secret->value) {
             /* Prefill the password if we have it. */
             rl_startup_hook = set_deftext;
@@ -138,7 +138,7 @@ do_agent_secret(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const *
                          NM_SECRET_AGENT_SIMPLE_REQUEST_SECRETS,
                          G_CALLBACK(secrets_requested),
                          nmc);
-        g_print(_("nmcli successfully registered as a NetworkManager's secret agent.\n"));
+        nmc_print(_("nmcli successfully registered as a NetworkManager's secret agent.\n"));
     } else {
         g_string_printf(nmc->return_text, _("Error: secret agent initialization failed"));
         nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
@@ -148,7 +148,7 @@ do_agent_secret(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const *
 static void
 polkit_registered(gpointer instance, gpointer user_data)
 {
-    g_print(_("nmcli successfully registered as a polkit agent.\n"));
+    nmc_print(_("nmcli successfully registered as a polkit agent.\n"));
 }
 
 static void
@@ -202,14 +202,14 @@ do_agent_all(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const *arg
     do_agent_secret(cmd, nmc, argc, argv);
     r = nmc->return_value;
     if (r != NMC_RESULT_SUCCESS) {
-        g_printerr("%s\n", nmc->return_text->str);
+        nmc_printerr("%s\n", nmc->return_text->str);
         g_string_truncate(nmc->return_text, 0);
         nmc->return_value = NMC_RESULT_SUCCESS;
     }
 
     do_agent_polkit(cmd, nmc, argc, argv);
     if (nmc->return_value != NMC_RESULT_SUCCESS) {
-        g_printerr("%s\n", nmc->return_text->str);
+        nmc_printerr("%s\n", nmc->return_text->str);
         g_string_truncate(nmc->return_text, 0);
     }
 
diff --git a/src/nmcli/common.c b/src/nmcli/common.c
index ff4c15c3..f31d0987 100644
--- a/src/nmcli/common.c
+++ b/src/nmcli/common.c
@@ -354,15 +354,15 @@ print_ip_config(NMIPConfig      *cfg,
             g_strdup_printf("IP%c.%s", nm_utils_addr_family_to_char(addr_family), one_field);
     }
 
-    if (!nmc_print(nmc_config,
-                   (gpointer[]){cfg, NULL},
-                   NULL,
-                   NULL,
-                   addr_family == AF_INET
-                       ? NMC_META_GENERIC_GROUP("IP4", metagen_ip4_config, N_("GROUP"))
-                       : NMC_META_GENERIC_GROUP("IP6", metagen_ip6_config, N_("GROUP")),
-                   field_str,
-                   &error)) {
+    if (!nmc_print_table(nmc_config,
+                         (gpointer[]){cfg, NULL},
+                         NULL,
+                         NULL,
+                         addr_family == AF_INET
+                             ? NMC_META_GENERIC_GROUP("IP4", metagen_ip4_config, N_("GROUP"))
+                             : NMC_META_GENERIC_GROUP("IP6", metagen_ip6_config, N_("GROUP")),
+                         field_str,
+                         &error)) {
         return FALSE;
     }
     return TRUE;
@@ -385,15 +385,15 @@ print_dhcp_config(NMDhcpConfig    *dhcp,
             g_strdup_printf("DHCP%c.%s", nm_utils_addr_family_to_char(addr_family), one_field);
     }
 
-    if (!nmc_print(nmc_config,
-                   (gpointer[]){dhcp, NULL},
-                   NULL,
-                   NULL,
-                   addr_family == AF_INET
-                       ? NMC_META_GENERIC_GROUP("DHCP4", metagen_dhcp_config, N_("GROUP"))
-                       : NMC_META_GENERIC_GROUP("DHCP6", metagen_dhcp_config, N_("GROUP")),
-                   field_str,
-                   &error)) {
+    if (!nmc_print_table(nmc_config,
+                         (gpointer[]){dhcp, NULL},
+                         NULL,
+                         NULL,
+                         addr_family == AF_INET
+                             ? NMC_META_GENERIC_GROUP("DHCP4", metagen_dhcp_config, N_("GROUP"))
+                             : NMC_META_GENERIC_GROUP("DHCP6", metagen_dhcp_config, N_("GROUP")),
+                         field_str,
+                         &error)) {
         return FALSE;
     }
     return TRUE;
@@ -432,10 +432,13 @@ nmc_find_connection(const GPtrArray *connections,
     GPtrArray                   *result              = out_result ? *out_result : NULL;
     const guint                  result_inital_len   = result ? result->len : 0u;
     guint                        i, j;
+    gboolean                     must_match_uniquely;
 
     nm_assert(connections);
     nm_assert(filter_val);
 
+    must_match_uniquely = NM_IN_STRSET(filter_type, "uuid", "path");
+
     for (i = 0; i < connections->len; i++) {
         gboolean      match_by_uuid = FALSE;
         NMConnection *connection;
@@ -452,6 +455,12 @@ nmc_find_connection(const GPtrArray *connections,
                 match_by_uuid = TRUE;
                 goto found;
             }
+            if (filter_type && !nm_str_is_empty(filter_val) && g_str_has_prefix(v, filter_val)) {
+                /* If the selector is qualified by "uuid", prefix matches for the UUID are
+                 * also OK. At least, if they result in a unique match. */
+                nm_assert(must_match_uniquely);
+                goto found;
+            }
         }
 
         if (NM_IN_STRSET(filter_type, NULL, "id")) {
@@ -483,14 +492,26 @@ nmc_find_connection(const GPtrArray *connections,
         continue;
 
 found:
+
+        if (must_match_uniquely && (best_candidate || best_candidate_uuid)) {
+            /* We found duplicates. This is wrong. */
+            if (out_result && *out_result) {
+                /* Remove the element that we added before. */
+                g_ptr_array_set_size(*out_result, result_inital_len);
+            }
+            return NULL;
+        }
+
         if (match_by_uuid) {
             if (!complete && !out_result)
                 return connection;
-            best_candidate_uuid = connection;
+            if (!best_candidate_uuid)
+                best_candidate_uuid = connection;
         } else {
             if (!best_candidate)
                 best_candidate = connection;
         }
+
         if (out_result) {
             gboolean already_tracked = FALSE;
 
@@ -639,16 +660,16 @@ vpn_openconnect_get_secrets(NMConnection *connection, GPtrArray *secrets)
     /* Interactively authenticate to OpenConnect server and get secrets */
     ret = nm_vpn_openconnect_authenticate_helper(gw, &cookie, &gateway, &gwcert, &status, &error);
     if (!ret) {
-        g_printerr(_("Error: openconnect failed: %s\n"), error->message);
+        nmc_printerr(_("Error: openconnect failed: %s\n"), error->message);
         g_clear_error(&error);
         return FALSE;
     }
 
     if (WIFEXITED(status)) {
         if (WEXITSTATUS(status) != 0)
-            g_printerr(_("Error: openconnect failed with status %d\n"), WEXITSTATUS(status));
+            nmc_printerr(_("Error: openconnect failed with status %d\n"), WEXITSTATUS(status));
     } else if (WIFSIGNALED(status))
-        g_printerr(_("Error: openconnect failed with signal %d\n"), WTERMSIG(status));
+        nmc_printerr(_("Error: openconnect failed with signal %d\n"), WTERMSIG(status));
 
     /* Append port to the host value */
     if (gateway && port) {
@@ -722,7 +743,7 @@ get_secrets_from_user(const NmcConfig *nmc_config,
                     }
                 }
                 if (msg)
-                    g_print("%s\n", msg);
+                    nmc_print("%s\n", msg);
 
                 echo_on = secret->is_secret ? nmc_config->show_secrets : TRUE;
 
@@ -739,10 +760,10 @@ get_secrets_from_user(const NmcConfig *nmc_config,
                     pwd = g_strdup("");
             } else {
                 if (msg)
-                    g_print("%s\n", msg);
-                g_printerr(_("Warning: password for '%s' not given in 'passwd-file' "
-                             "and nmcli cannot ask without '--ask' option.\n"),
-                           secret->entry_id);
+                    nmc_print("%s\n", msg);
+                nmc_printerr(_("Warning: password for '%s' not given in 'passwd-file' "
+                               "and nmcli cannot ask without '--ask' option.\n"),
+                             secret->entry_id);
             }
         }
         /* No password provided, cancel the secrets. */
@@ -874,7 +895,10 @@ static void
 readline_cb(char *line)
 {
     rl_got_line = TRUE;
-    rl_string   = line;
+
+    free(rl_string);
+    rl_string = line;
+
     rl_callback_handler_remove();
 }
 
@@ -889,13 +913,15 @@ static char *
 nmc_readline_helper(const NmcConfig *nmc_config, const char *prompt)
 {
     GSource *io_source;
+    char    *result;
 
     nmc_set_in_readline(TRUE);
 
     io_source = nm_g_unix_fd_add_source(STDIN_FILENO, G_IO_IN, stdin_ready_cb, NULL);
 
 read_again:
-    rl_string   = NULL;
+    nm_clear_free(&rl_string);
+
     rl_got_line = FALSE;
     rl_callback_handler_install(prompt, readline_cb);
 
@@ -921,7 +947,6 @@ read_again:
         if (nmc_config->in_editor || (rl_string && *rl_string)) {
             /* In editor, or the line is not empty */
             /* Call readline again to get new prompt (repeat) */
-            g_free(rl_string);
             goto read_again;
         } else {
             /* Not in editor and line is empty, exit */
@@ -934,16 +959,19 @@ read_again:
     }
 
     /* Return NULL, not empty string */
-    if (rl_string && *rl_string == '\0') {
-        g_free(rl_string);
-        rl_string = NULL;
-    }
+    if (rl_string && *rl_string == '\0')
+        nm_clear_free(&rl_string);
 
     nm_clear_g_source_inst(&io_source);
 
     nmc_set_in_readline(FALSE);
 
-    return rl_string;
+    if (!rl_string)
+        return NULL;
+
+    result = g_strdup(rl_string);
+    nm_clear_free(&rl_string);
+    return result;
 }
 
 /**
@@ -1014,7 +1042,7 @@ nmc_readline_echo(const NmcConfig *nmc_config, gboolean echo_on, const char *pro
 #if HAVE_READLINE_HISTORY
     nm_auto_free HISTORY_STATE *saved_history  = NULL;
     HISTORY_STATE               passwd_history = {
-                      0,
+        0,
     };
 #else
     int start, curpos;
@@ -1504,7 +1532,7 @@ nmc_do_cmd(NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, const
     if (argc == 1 && nmc->complete) {
         for (c = cmds; c->cmd; ++c) {
             if (!*cmd || matches(cmd, c->cmd))
-                g_print("%s\n", c->cmd);
+                nmc_print("%s\n", c->cmd);
         }
         nmc_complete_help(cmd);
         g_task_return_boolean(task, TRUE);
@@ -1584,7 +1612,7 @@ nmc_complete_strv(const char *prefix, gssize nargs, const char *const *args)
         if (prefix && !matches(prefix, candidate))
             continue;
 
-        g_print("%s\n", candidate);
+        nmc_print("%s\n", candidate);
     }
 }
 
diff --git a/src/nmcli/connections.c b/src/nmcli/connections.c
index 8c6ebd98..36de6373 100644
--- a/src/nmcli/connections.c
+++ b/src/nmcli/connections.c
@@ -169,9 +169,7 @@ print_connection_done(GObject *source_object, GAsyncResult *res, gpointer user_d
 
     written = g_output_stream_write_finish(stream, res, &error);
     if (written == -1) {
-        g_string_printf(nmc->return_text,
-                        _("Error: Error writting connection: %s"),
-                        error->message);
+        g_string_printf(nmc->return_text, _("Error: Error writing connection: %s"), error->message);
         nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
         nmc->should_wait--;
         quit();
@@ -230,7 +228,7 @@ nmc_print_connection_and_quit(NmCli *nmc, NMConnection *connection)
     return;
 
 error:
-    g_string_printf(nmc->return_text, _("Error: Error writting connection: %s"), error->message);
+    g_string_printf(nmc->return_text, _("Error: Error writing connection: %s"), error->message);
     nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
     nmc->should_wait--;
     quit();
@@ -1086,7 +1084,7 @@ static TabCompletionInfo nmc_tab_completion;
 static void
 usage(void)
 {
-    g_printerr(
+    nmc_printerr(
         _("Usage: nmcli connection { COMMAND | help }\n\n"
           "COMMAND := { show | up | down | add | modify | clone | edit | delete | monitor | reload "
           "| load | import | export }\n\n"
@@ -1112,7 +1110,7 @@ usage(void)
 static void
 usage_connection_show(void)
 {
-    g_printerr(
+    nmc_printerr(
         _("Usage: nmcli connection show { ARGUMENTS | help }\n"
           "\n"
           "ARGUMENTS := [--active] [--order ]\n"
@@ -1134,171 +1132,173 @@ usage_connection_show(void)
 static void
 usage_connection_up(void)
 {
-    g_printerr(_("Usage: nmcli connection up { ARGUMENTS | help }\n"
-                 "\n"
-                 "ARGUMENTS := [id | uuid | path]  [ifname ] [ap ] [nsp ] "
-                 "[passwd-file ]\n"
-                 "\n"
-                 "Activate a connection on a device. The profile to activate is identified by its\n"
-                 "name, UUID or D-Bus path.\n"
-                 "\n"
-                 "ARGUMENTS := ifname  [ap ] [nsp ] [passwd-file ]\n"
-                 "\n"
-                 "Activate a device with a connection. The connection profile is selected\n"
-                 "automatically by NetworkManager.\n"
-                 "\n"
-                 "ifname      - specifies the device to active the connection on\n"
-                 "ap          - specifies AP to connect to (only valid for Wi-Fi)\n"
-                 "nsp         - specifies NSP to connect to (only valid for WiMAX)\n"
-                 "passwd-file - file with password(s) required to activate the connection\n\n"));
+    nmc_printerr(
+        _("Usage: nmcli connection up { ARGUMENTS | help }\n"
+          "\n"
+          "ARGUMENTS := [id | uuid | path]  [ifname ] [ap ] [nsp ] "
+          "[passwd-file ]\n"
+          "\n"
+          "Activate a connection on a device. The profile to activate is identified by its\n"
+          "name, UUID or D-Bus path.\n"
+          "\n"
+          "ARGUMENTS := ifname  [ap ] [nsp ] [passwd-file ]\n"
+          "\n"
+          "Activate a device with a connection. The connection profile is selected\n"
+          "automatically by NetworkManager.\n"
+          "\n"
+          "ifname      - specifies the device to active the connection on\n"
+          "ap          - specifies AP to connect to (only valid for Wi-Fi)\n"
+          "nsp         - specifies NSP to connect to (only valid for WiMAX)\n"
+          "passwd-file - file with password(s) required to activate the connection\n\n"));
 }
 
 static void
 usage_connection_down(void)
 {
-    g_printerr(_("Usage: nmcli connection down { ARGUMENTS | help }\n"
-                 "\n"
-                 "ARGUMENTS := [id | uuid | path | apath]  ...\n"
-                 "\n"
-                 "Deactivate a connection from a device (without preventing the device from\n"
-                 "further auto-activation). The profile to deactivate is identified by its name,\n"
-                 "UUID or D-Bus path.\n\n"));
+    nmc_printerr(
+        _("Usage: nmcli connection down { ARGUMENTS | help }\n"
+          "\n"
+          "ARGUMENTS := [id | uuid | path | apath]  ...\n"
+          "\n"
+          "Deactivate a connection from a device (without preventing the device from\n"
+          "further auto-activation). The profile to deactivate is identified by its name,\n"
+          "UUID or D-Bus path.\n\n"));
 }
 
 static void
 usage_connection_add(void)
 {
-    g_printerr(_("Usage: nmcli connection add { ARGUMENTS | help }\n"
-                 "\n"
-                 "ARGUMENTS := COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS SLAVE_OPTIONS IP_OPTIONS [-- "
-                 "([+|-]. )+]\n\n"
-                 "  COMMON_OPTIONS:\n"
-                 "                  type \n"
-                 "                  ifname  | \"*\"\n"
-                 "                  [con-name ]\n"
-                 "                  [autoconnect yes|no]\n"
-                 "                  [save yes|no]\n"
-                 "                  [master ]\n"
-                 "                  [slave-type ]\n\n"
-                 "  TYPE_SPECIFIC_OPTIONS:\n"
-                 "    ethernet:     [mac ]\n"
-                 "                  [cloned-mac ]\n"
-                 "                  [mtu ]\n\n"
-                 "    wifi:         ssid \n"
-                 "                  [mac ]\n"
-                 "                  [cloned-mac ]\n"
-                 "                  [mtu ]\n"
-                 "                  [mode infrastructure|ap|adhoc]\n\n"
-                 "    wimax:        [mac ]\n"
-                 "                  [nsp ]\n\n"
-                 "    pppoe:        username \n"
-                 "                  [password ]\n"
-                 "                  [service ]\n"
-                 "                  [mtu ]\n"
-                 "                  [mac ]\n\n"
-                 "    gsm:          apn \n"
-                 "                  [user ]\n"
-                 "                  [password ]\n\n"
-                 "    cdma:         [user ]\n"
-                 "                  [password ]\n\n"
-                 "    infiniband:   [mac ]\n"
-                 "                  [mtu ]\n"
-                 "                  [transport-mode datagram | connected]\n"
-                 "                  [parent ]\n"
-                 "                  [p-key ]\n\n"
-                 "    bluetooth:    [addr ]\n"
-                 "                  [bt-type panu|nap|dun-gsm|dun-cdma]\n\n"
-                 "    vlan:         dev \n"
-                 "                  id \n"
-                 "                  [flags ]\n"
-                 "                  [ingress ]\n"
-                 "                  [egress ]\n"
-                 "                  [mtu ]\n\n"
-                 "    bond:         [mode balance-rr (0) | active-backup (1) | balance-xor (2) | "
-                 "broadcast (3) |\n"
-                 "                        802.3ad    (4) | balance-tlb   (5) | balance-alb (6)]\n"
-                 "                  [primary ]\n"
-                 "                  [miimon ]\n"
-                 "                  [downdelay ]\n"
-                 "                  [updelay ]\n"
-                 "                  [arp-interval ]\n"
-                 "                  [arp-ip-target ]\n"
-                 "                  [lacp-rate slow (0) | fast (1)]\n\n"
-                 "    bond-slave:   master \n"
-                 "                  [queue-id <0-65535>]\n\n"
-                 "    team:         [config |]\n\n"
-                 "    team-slave:   master \n"
-                 "                  [config |]\n\n"
-                 "    bridge:       [stp yes|no]\n"
-                 "                  [priority ]\n"
-                 "                  [forward-delay <2-30>]\n"
-                 "                  [hello-time <1-10>]\n"
-                 "                  [max-age <6-40>]\n"
-                 "                  [ageing-time <0-1000000>]\n"
-                 "                  [multicast-snooping yes|no]\n"
-                 "                  [mac ]\n\n"
-                 "    bridge-slave: master \n"
-                 "                  [priority <0-63>]\n"
-                 "                  [path-cost <1-65535>]\n"
-                 "                  [hairpin yes|no]\n\n"
-                 "    vpn:          vpn-type "
-                 "vpnc|openvpn|pptp|openconnect|openswan|libreswan|ssh|l2tp|iodine|...\n"
-                 "                  [user ]\n\n"
-                 "    olpc-mesh:    ssid \n"
-                 "                  [channel <1-13>]\n"
-                 "                  [dhcp-anycast ]\n\n"
-                 "    adsl:         username \n"
-                 "                  protocol pppoa|pppoe|ipoatm\n"
-                 "                  [password ]\n"
-                 "                  [encapsulation vcmux|llc]\n\n"
-                 "    tun:          mode tun|tap\n"
-                 "                  [owner ]\n"
-                 "                  [group ]\n"
-                 "                  [pi yes|no]\n"
-                 "                  [vnet-hdr yes|no]\n"
-                 "                  [multi-queue yes|no]\n\n"
-                 "    ip-tunnel:    mode ipip|gre|sit|isatap|vti|ip6ip6|ipip6|ip6gre|vti6\n"
-                 "                  remote \n"
-                 "                  [local ]\n"
-                 "                  [dev ]\n\n"
-                 "    macsec:       dev \n"
-                 "                  mode \n"
-                 "                  [cak  ckn ]\n"
-                 "                  [encrypt yes|no]\n"
-                 "                  [port 1-65534]\n\n\n"
-                 "    macvlan:      dev \n"
-                 "                  mode vepa|bridge|private|passthru|source\n"
-                 "                  [tap yes|no]\n\n"
-                 "    vxlan:        id \n"
-                 "                  [remote ]\n"
-                 "                  [local ]\n"
-                 "                  [dev ]\n"
-                 "                  [source-port-min <0-65535>]\n"
-                 "                  [source-port-max <0-65535>]\n"
-                 "                  [destination-port <0-65535>]\n\n"
-                 "    wpan:         [short-addr <0x0000-0xffff>]\n"
-                 "                  [pan-id <0x0000-0xffff>]\n"
-                 "                  [page ]\n"
-                 "                  [channel ]\n"
-                 "                  [mac ]\n\n"
-                 "    6lowpan:      dev \n"
-                 "    dummy:\n\n"
-                 "  SLAVE_OPTIONS:\n"
-                 "    bridge:       [priority <0-63>]\n"
-                 "                  [path-cost <1-65535>]\n"
-                 "                  [hairpin yes|no]\n\n"
-                 "    team:         [config |]\n\n"
-                 "    bond:         [queue-id <0-65535>]\n\n"
-                 "  IP_OPTIONS:\n"
-                 "                  [ip4 ] [gw4 ]\n"
-                 "                  [ip6 ] [gw6 ]\n\n"));
+    nmc_printerr(_("Usage: nmcli connection add { ARGUMENTS | help }\n"
+                   "\n"
+                   "ARGUMENTS := COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS SLAVE_OPTIONS IP_OPTIONS [-- "
+                   "([+|-]. )+]\n\n"
+                   "  COMMON_OPTIONS:\n"
+                   "                  type \n"
+                   "                  [ifname  | \"*\"]\n"
+                   "                  [con-name ]\n"
+                   "                  [autoconnect yes|no]\n"
+                   "                  [save yes|no]\n"
+                   "                  [master ]\n"
+                   "                  [slave-type ]\n\n"
+                   "  TYPE_SPECIFIC_OPTIONS:\n"
+                   "    ethernet:     [mac ]\n"
+                   "                  [cloned-mac ]\n"
+                   "                  [mtu ]\n\n"
+                   "    wifi:         ssid \n"
+                   "                  [mac ]\n"
+                   "                  [cloned-mac ]\n"
+                   "                  [mtu ]\n"
+                   "                  [mode infrastructure|ap|adhoc]\n\n"
+                   "    wimax:        [mac ]\n"
+                   "                  [nsp ]\n\n"
+                   "    pppoe:        username \n"
+                   "                  [password ]\n"
+                   "                  [service ]\n"
+                   "                  [mtu ]\n"
+                   "                  [mac ]\n\n"
+                   "    gsm:          apn \n"
+                   "                  [user ]\n"
+                   "                  [password ]\n\n"
+                   "    cdma:         [user ]\n"
+                   "                  [password ]\n\n"
+                   "    infiniband:   [mac ]\n"
+                   "                  [mtu ]\n"
+                   "                  [transport-mode datagram | connected]\n"
+                   "                  [parent ]\n"
+                   "                  [p-key ]\n\n"
+                   "    bluetooth:    [addr ]\n"
+                   "                  [bt-type panu|nap|dun-gsm|dun-cdma]\n\n"
+                   "    vlan:         dev \n"
+                   "                  id \n"
+                   "                  [flags ]\n"
+                   "                  [ingress ]\n"
+                   "                  [egress ]\n"
+                   "                  [mtu ]\n\n"
+                   "    bond:         [mode balance-rr (0) | active-backup (1) | balance-xor (2) | "
+                   "broadcast (3) |\n"
+                   "                        802.3ad    (4) | balance-tlb   (5) | balance-alb (6)]\n"
+                   "                  [primary ]\n"
+                   "                  [miimon ]\n"
+                   "                  [downdelay ]\n"
+                   "                  [updelay ]\n"
+                   "                  [arp-interval ]\n"
+                   "                  [arp-ip-target ]\n"
+                   "                  [lacp-rate slow (0) | fast (1)]\n\n"
+                   "    bond-slave:   master \n"
+                   "                  [queue-id <0-65535>]\n\n"
+                   "    team:         [config |]\n\n"
+                   "    team-slave:   master \n"
+                   "                  [config |]\n\n"
+                   "    bridge:       [stp yes|no]\n"
+                   "                  [priority ]\n"
+                   "                  [forward-delay <2-30>]\n"
+                   "                  [hello-time <1-10>]\n"
+                   "                  [max-age <6-40>]\n"
+                   "                  [ageing-time <0-1000000>]\n"
+                   "                  [multicast-snooping yes|no]\n"
+                   "                  [mac ]\n\n"
+                   "    bridge-slave: master \n"
+                   "                  [priority <0-63>]\n"
+                   "                  [path-cost <1-65535>]\n"
+                   "                  [hairpin yes|no]\n\n"
+                   "    vpn:          vpn-type "
+                   "vpnc|openvpn|pptp|openconnect|openswan|libreswan|ssh|l2tp|iodine|...\n"
+                   "                  [user ]\n\n"
+                   "    olpc-mesh:    ssid \n"
+                   "                  [channel <1-13>]\n"
+                   "                  [dhcp-anycast ]\n\n"
+                   "    adsl:         username \n"
+                   "                  protocol pppoa|pppoe|ipoatm\n"
+                   "                  [password ]\n"
+                   "                  [encapsulation vcmux|llc]\n\n"
+                   "    tun:          mode tun|tap\n"
+                   "                  [owner ]\n"
+                   "                  [group ]\n"
+                   "                  [pi yes|no]\n"
+                   "                  [vnet-hdr yes|no]\n"
+                   "                  [multi-queue yes|no]\n\n"
+                   "    ip-tunnel:    mode ipip|gre|sit|isatap|vti|ip6ip6|ipip6|ip6gre|vti6\n"
+                   "                  remote \n"
+                   "                  [local ]\n"
+                   "                  [dev ]\n\n"
+                   "    macsec:       dev \n"
+                   "                  mode \n"
+                   "                  [cak  ckn ]\n"
+                   "                  [encrypt yes|no]\n"
+                   "                  [port 1-65534]\n\n\n"
+                   "    macvlan:      dev \n"
+                   "                  mode vepa|bridge|private|passthru|source\n"
+                   "                  [tap yes|no]\n\n"
+                   "    vxlan:        id \n"
+                   "                  [remote ]\n"
+                   "                  [local ]\n"
+                   "                  [dev ]\n"
+                   "                  [source-port-min <0-65535>]\n"
+                   "                  [source-port-max <0-65535>]\n"
+                   "                  [destination-port <0-65535>]\n\n"
+                   "    wpan:         [short-addr <0x0000-0xffff>]\n"
+                   "                  [pan-id <0x0000-0xffff>]\n"
+                   "                  [page ]\n"
+                   "                  [channel ]\n"
+                   "                  [mac ]\n\n"
+                   "    6lowpan:      dev \n"
+                   "    dummy:\n\n"
+                   "  SLAVE_OPTIONS:\n"
+                   "    bridge:       [priority <0-63>]\n"
+                   "                  [path-cost <1-65535>]\n"
+                   "                  [hairpin yes|no]\n\n"
+                   "    team:         [config |]\n\n"
+                   "    bond:         [queue-id <0-65535>]\n\n"
+                   "  IP_OPTIONS:\n"
+                   "                  [ip4 ] [gw4 ]\n"
+                   "                  [ip6 ] [gw6 ]\n\n"));
 }
 
 static void
 usage_connection_modify(void)
 {
-    g_printerr(
+    nmc_printerr(
         _("Usage: nmcli connection modify { ARGUMENTS | help }\n"
           "\n"
           "ARGUMENTS := [id | uuid | path]  ([+|-]. )+\n"
@@ -1327,77 +1327,78 @@ usage_connection_modify(void)
 static void
 usage_connection_clone(void)
 {
-    g_printerr(_("Usage: nmcli connection clone { ARGUMENTS | help }\n"
-                 "\n"
-                 "ARGUMENTS := [--temporary] [id | uuid | path]  \n"
-                 "\n"
-                 "Clone an existing connection profile. The newly created connection will be\n"
-                 "the exact copy of the , except the uuid property (will be generated) and\n"
-                 "id (provided as  argument).\n\n"));
+    nmc_printerr(_("Usage: nmcli connection clone { ARGUMENTS | help }\n"
+                   "\n"
+                   "ARGUMENTS := [--temporary] [id | uuid | path]  \n"
+                   "\n"
+                   "Clone an existing connection profile. The newly created connection will be\n"
+                   "the exact copy of the , except the uuid property (will be generated) and\n"
+                   "id (provided as  argument).\n\n"));
 }
 
 static void
 usage_connection_edit(void)
 {
-    g_printerr(_("Usage: nmcli connection edit { ARGUMENTS | help }\n"
-                 "\n"
-                 "ARGUMENTS := [id | uuid | path] \n"
-                 "\n"
-                 "Edit an existing connection profile in an interactive editor.\n"
-                 "The profile is identified by its name, UUID or D-Bus path\n"
-                 "\n"
-                 "ARGUMENTS := [type ] [con-name ]\n"
-                 "\n"
-                 "Add a new connection profile in an interactive editor.\n\n"));
+    nmc_printerr(_("Usage: nmcli connection edit { ARGUMENTS | help }\n"
+                   "\n"
+                   "ARGUMENTS := [id | uuid | path] \n"
+                   "\n"
+                   "Edit an existing connection profile in an interactive editor.\n"
+                   "The profile is identified by its name, UUID or D-Bus path\n"
+                   "\n"
+                   "ARGUMENTS := [type ] [con-name ]\n"
+                   "\n"
+                   "Add a new connection profile in an interactive editor.\n\n"));
 }
 
 static void
 usage_connection_delete(void)
 {
-    g_printerr(_("Usage: nmcli connection delete { ARGUMENTS | help }\n"
-                 "\n"
-                 "ARGUMENTS := [id | uuid | path] , ...\n"
-                 "\n"
-                 "Delete connection profiles.\n"
-                 "The profiles are identified by their name, UUID or D-Bus path.\n\n"));
+    nmc_printerr(_("Usage: nmcli connection delete { ARGUMENTS | help }\n"
+                   "\n"
+                   "ARGUMENTS := [id | uuid | path] , ...\n"
+                   "\n"
+                   "Delete connection profiles.\n"
+                   "The profiles are identified by their name, UUID or D-Bus path.\n\n"));
 }
 
 static void
 usage_connection_monitor(void)
 {
-    g_printerr(_("Usage: nmcli connection monitor { ARGUMENTS | help }\n"
-                 "\n"
-                 "ARGUMENTS := [id | uuid | path]  ...\n"
-                 "\n"
-                 "Monitor connection profile activity.\n"
-                 "This command prints a line whenever the specified connection changes.\n"
-                 "Monitors all connection profiles in case none is specified.\n\n"));
+    nmc_printerr(_("Usage: nmcli connection monitor { ARGUMENTS | help }\n"
+                   "\n"
+                   "ARGUMENTS := [id | uuid | path]  ...\n"
+                   "\n"
+                   "Monitor connection profile activity.\n"
+                   "This command prints a line whenever the specified connection changes.\n"
+                   "Monitors all connection profiles in case none is specified.\n\n"));
 }
 
 static void
 usage_connection_reload(void)
 {
-    g_printerr(_("Usage: nmcli connection reload { help }\n"
-                 "\n"
-                 "Reload all connection files from disk.\n\n"));
+    nmc_printerr(_("Usage: nmcli connection reload { help }\n"
+                   "\n"
+                   "Reload all connection files from disk.\n\n"));
 }
 
 static void
 usage_connection_load(void)
 {
-    g_printerr(_("Usage: nmcli connection load { ARGUMENTS | help }\n"
-                 "\n"
-                 "ARGUMENTS :=  [...]\n"
-                 "\n"
-                 "Load/reload one or more connection files from disk. Use this after manually\n"
-                 "editing a connection file to ensure that NetworkManager is aware of its latest\n"
-                 "state.\n\n"));
+    nmc_printerr(
+        _("Usage: nmcli connection load { ARGUMENTS | help }\n"
+          "\n"
+          "ARGUMENTS :=  [...]\n"
+          "\n"
+          "Load/reload one or more connection files from disk. Use this after manually\n"
+          "editing a connection file to ensure that NetworkManager is aware of its latest\n"
+          "state.\n\n"));
 }
 
 static void
 usage_connection_import(void)
 {
-    g_printerr(
+    nmc_printerr(
         _("Usage: nmcli connection import { ARGUMENTS | help }\n"
           "\n"
           "ARGUMENTS := [--temporary] type  file \n"
@@ -1411,23 +1412,24 @@ usage_connection_import(void)
 static void
 usage_connection_export(void)
 {
-    g_printerr(_("Usage: nmcli connection export { ARGUMENTS | help }\n"
-                 "\n"
-                 "ARGUMENTS := [id | uuid | path]  []\n"
-                 "\n"
-                 "Export a connection. Only VPN connections are supported at the moment.\n"
-                 "The data are directed to standard output or to a file if a name is given.\n\n"));
+    nmc_printerr(
+        _("Usage: nmcli connection export { ARGUMENTS | help }\n"
+          "\n"
+          "ARGUMENTS := [id | uuid | path]  []\n"
+          "\n"
+          "Export a connection. Only VPN connections are supported at the moment.\n"
+          "The data are directed to standard output or to a file if a name is given.\n\n"));
 }
 
 static void
 usage_connection_migrate(void)
 {
-    g_printerr(_("Usage: nmcli connection migrate { ARGUMENTS | help }\n"
-                 "\n"
-                 "ARGUMENTS := [--plugin ] [id | uuid | path] , ...\n"
-                 "\n"
-                 "Migrate connection profiles to a different settings plugin,\n"
-                 "such as \"keyfile\" (default) or \"ifcfg-rh\".\n\n"));
+    nmc_printerr(_("Usage: nmcli connection migrate { ARGUMENTS | help }\n"
+                   "\n"
+                   "ARGUMENTS := [--plugin ] [id | uuid | path] , ...\n"
+                   "\n"
+                   "Migrate connection profiles to a different settings plugin,\n"
+                   "such as \"keyfile\" (default) or \"ifcfg-rh\".\n\n"));
 }
 
 static char *
@@ -1505,9 +1507,9 @@ got_secrets(GObject *source_object, GAsyncResult *res, gpointer user_data)
         gs_free_error GError *error = NULL;
 
         if (!nm_connection_update_secrets(data->local, NULL, secrets, &error) && error) {
-            g_printerr(_("Error updating secrets for %s: %s\n"),
-                       data->setting_name,
-                       error->message);
+            nmc_printerr(_("Error updating secrets for %s: %s\n"),
+                         data->setting_name,
+                         error->message);
         }
     }
 
@@ -1610,12 +1612,12 @@ nmc_connection_profile_details(NMConnection *connection, NmCli *nmc)
     /* Loop through the required settings and print them. */
     for (i = 0; i < print_settings_array->len; i++) {
         NMSetting  *setting;
-        int         section_idx = g_array_index(print_settings_array, int, i);
+        int         section_idx = nm_g_array_index(print_settings_array, int, i);
         const char *prop_name   = (const char *) g_ptr_array_index(prop_array, i);
 
         if (NM_IN_SET(nmc->nmc_config.print_output, NMC_PRINT_NORMAL, NMC_PRINT_PRETTY)
             && !nmc->nmc_config.multiline_output && was_output)
-            g_print("\n"); /* Empty line */
+            nmc_print("\n"); /* Empty line */
 
         was_output = FALSE;
 
@@ -1716,12 +1718,12 @@ nmc_active_connection_details(NMActiveConnection *acon, NmCli *nmc)
 
     /* Loop through the groups and print them. */
     for (i = 0; i < print_groups->len; i++) {
-        int   group_idx = g_array_index(print_groups, int, i);
+        int   group_idx = nm_g_array_index(print_groups, int, i);
         char *group_fld = (char *) g_ptr_array_index(group_fields, i);
 
         if (NM_IN_SET(nmc->nmc_config.print_output, NMC_PRINT_NORMAL, NMC_PRINT_PRETTY)
             && !nmc->nmc_config.multiline_output && was_output)
-            g_print("\n");
+            nmc_print("\n");
 
         was_output = FALSE;
 
@@ -1731,13 +1733,14 @@ nmc_active_connection_details(NMActiveConnection *acon, NmCli *nmc)
             if (group_fld)
                 f = g_strdup_printf("GENERAL.%s", group_fld);
 
-            nmc_print(&nmc->nmc_config,
-                      (gpointer[]){acon, NULL},
-                      NULL,
-                      NULL,
-                      NMC_META_GENERIC_GROUP("GENERAL", metagen_con_active_general, N_("GROUP")),
-                      f,
-                      NULL);
+            nmc_print_table(
+                &nmc->nmc_config,
+                (gpointer[]){acon, NULL},
+                NULL,
+                NULL,
+                NMC_META_GENERIC_GROUP("GENERAL", metagen_con_active_general, N_("GROUP")),
+                f,
+                NULL);
             was_output = TRUE;
             continue;
         }
@@ -1788,13 +1791,13 @@ nmc_active_connection_details(NMActiveConnection *acon, NmCli *nmc)
 
         if (nmc_fields_con_active_details_groups[group_idx]->nested == metagen_con_active_vpn) {
             if (NM_IS_VPN_CONNECTION(acon)) {
-                nmc_print(&nmc->nmc_config,
-                          (gpointer[]){acon, NULL},
-                          NULL,
-                          NULL,
-                          NMC_META_GENERIC_GROUP("VPN", metagen_con_active_vpn, N_("NAME")),
-                          group_fld,
-                          NULL);
+                nmc_print_table(&nmc->nmc_config,
+                                (gpointer[]){acon, NULL},
+                                NULL,
+                                NULL,
+                                NMC_META_GENERIC_GROUP("VPN", metagen_con_active_vpn, N_("NAME")),
+                                group_fld,
+                                NULL);
                 was_output = TRUE;
             }
             continue;
@@ -1983,7 +1986,7 @@ con_show_get_items_cmp(gconstpointer pa, gconstpointer pb, gpointer user_data)
             nmc_print_output_to_accessor_get_type(sort_info->nmc->nmc_config.print_output);
 
         if (sort_info->order) {
-            order_arr = &g_array_index(sort_info->order, NmcSortOrder, 0);
+            order_arr = nm_g_array_first_p(sort_info->order, NmcSortOrder);
             order_len = sort_info->order->len;
         } else {
             static const NmcSortOrder def[] = {NMC_SORT_ACTIVE, NMC_SORT_NAME, NMC_SORT_PATH};
@@ -2206,7 +2209,7 @@ parse_preferred_connection_order(const char *order, GError **error)
         /* Check for duplicates and ignore them. */
         unique = TRUE;
         for (i = 0; i < order_arr->len; i++) {
-            if (abs(g_array_index(order_arr, NmcSortOrder, i)) - abs(val) == 0) {
+            if (abs(nm_g_array_index(order_arr, NmcSortOrder, i)) - abs(val) == 0) {
                 unique = FALSE;
                 break;
             }
@@ -2369,14 +2372,14 @@ do_connections_show(const NMCCommand *cmd, NmCli *nmc, int argc, const char *con
 
         items = con_show_get_items(nmc, active_only, show_active_fields, order);
         g_ptr_array_add(items, NULL);
-        if (!nmc_print(&nmc->nmc_config,
-                       items->pdata,
-                       NULL,
-                       active_only ? _("NetworkManager active profiles")
-                                   : _("NetworkManager connection profiles"),
-                       (const NMMetaAbstractInfo *const *) metagen_con_show,
-                       fields_str,
-                       &err))
+        if (!nmc_print_table(&nmc->nmc_config,
+                             items->pdata,
+                             NULL,
+                             active_only ? _("NetworkManager active profiles")
+                                         : _("NetworkManager connection profiles"),
+                             (const NMMetaAbstractInfo *const *) metagen_con_show,
+                             fields_str,
+                             &err))
             goto finish;
     } else {
         gboolean         new_line       = FALSE;
@@ -2508,7 +2511,7 @@ do_connections_show(const NMCCommand *cmd, NmCli *nmc, int argc, const char *con
                 nm_assert(explicit_acon || con);
 
                 if (new_line)
-                    g_print("\n");
+                    nmc_print("\n");
                 new_line = TRUE;
 
                 if (without_fields || profile_flds) {
@@ -2533,7 +2536,7 @@ do_connections_show(const NMCCommand *cmd, NmCli *nmc, int argc, const char *con
                             /* if there are multiple active connections, separate them with newline.
                              * that is a bit odd, because we already separate connections with newlines,
                              * and commonly don't separate the connection from the first active connection. */
-                            g_print("\n");
+                            nmc_print("\n");
                         }
 
                         if (explicit_acon)
@@ -2801,12 +2804,12 @@ check_activated(ActivateConnectionInfo *info)
         if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
             nmc_terminal_erase_line();
         if (reason) {
-            g_print(_("Connection successfully activated (%s) (D-Bus active path: %s)\n"),
-                    reason,
-                    nm_object_get_path(NM_OBJECT(info->active)));
+            nmc_print(_("Connection successfully activated (%s) (D-Bus active path: %s)\n"),
+                      reason,
+                      nm_object_get_path(NM_OBJECT(info->active)));
         } else {
-            g_print(_("Connection successfully activated (D-Bus active path: %s)\n"),
-                    nm_object_get_path(NM_OBJECT(info->active)));
+            nmc_print(_("Connection successfully activated (D-Bus active path: %s)\n"),
+                      nm_object_get_path(NM_OBJECT(info->active)));
         }
         activate_connection_info_finish(info);
         break;
@@ -2956,8 +2959,8 @@ activate_connection_cb(GObject *client, GAsyncResult *result, gpointer user_data
             if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
                 if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
                     nmc_terminal_erase_line();
-                g_print(_("Connection successfully activated (D-Bus active path: %s)\n"),
-                        nm_object_get_path(NM_OBJECT(active)));
+                nmc_print(_("Connection successfully activated (D-Bus active path: %s)\n"),
+                          nm_object_get_path(NM_OBJECT(active)));
             }
             activate_connection_info_finish(info);
         } else {
@@ -3326,9 +3329,9 @@ connection_removed_cb(NMClient *client, NMConnection *connection, ConnectionCbIn
 {
     if (!connection_cb_info_obj_list_has(info, connection))
         return;
-    g_print(_("Connection '%s' (%s) successfully deleted.\n"),
-            nm_connection_get_id(connection),
-            nm_connection_get_uuid(connection));
+    nmc_print(_("Connection '%s' (%s) successfully deleted.\n"),
+              nm_connection_get_id(connection),
+              nm_connection_get_uuid(connection));
     connection_cb_info_finish(info, connection);
 }
 
@@ -3342,9 +3345,9 @@ down_active_connection_state_cb(NMActiveConnection *active,
 
     if (info->nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
         nmc_terminal_erase_line();
-    g_print(_("Connection '%s' successfully deactivated (D-Bus active path: %s)\n"),
-            nm_active_connection_get_id(active),
-            nm_object_get_path(NM_OBJECT(active)));
+    nmc_print(_("Connection '%s' successfully deactivated (D-Bus active path: %s)\n"),
+              nm_active_connection_get_id(active),
+              nm_object_get_path(NM_OBJECT(active)));
 
     g_signal_handlers_disconnect_by_func(G_OBJECT(active), down_active_connection_state_cb, info);
     connection_cb_info_finish(info, active);
@@ -3423,7 +3426,7 @@ do_connection_down(const NMCCommand *cmd, NmCli *nmc, int argc, const char *cons
                                             arg_num == 1 && nmc->complete);
         if (!active) {
             if (!nmc->complete)
-                g_printerr(_("Error: '%s' is not an active connection.\n"), *arg_ptr);
+                nmc_printerr(_("Error: '%s' is not an active connection.\n"), *arg_ptr);
             g_string_printf(nmc->return_text, _("Error: not all active connections found."));
             nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
         }
@@ -3464,9 +3467,9 @@ do_connection_down(const NMCCommand *cmd, NmCli *nmc, int argc, const char *cons
         active = found_active_cons->pdata[i];
 
         if (!nm_client_deactivate_connection(nmc->client, active, NULL, &error)) {
-            g_print(_("Connection '%s' deactivation failed: %s\n"),
-                    nm_active_connection_get_id(active),
-                    error->message);
+            nmc_print(_("Connection '%s' deactivation failed: %s\n"),
+                      nm_active_connection_get_id(active),
+                      error->message);
             g_clear_error(&error);
 
             if (info) {
@@ -3945,7 +3948,7 @@ normalized_master_for_slave(const GPtrArray *connections,
     }
 
     if (!out_master) {
-        g_print(_("Warning: master='%s' doesn't refer to any existing profile.\n"), master);
+        nmc_print(_("Warning: master='%s' doesn't refer to any existing profile.\n"), master);
         out_master = master;
         if (out_type)
             *out_type = type;
@@ -4343,8 +4346,9 @@ set_property(NMClient              *client,
         g_set_error(error,
                     NMCLI_ERROR,
                     NMC_RESULT_ERROR_USER_INPUT,
-                    _("Error: failed to %s %s.%s: %s."),
-                    (modifier != NM_META_ACCESSOR_MODIFIER_DEL ? "modify" : "remove a value from"),
+                    modifier != NM_META_ACCESSOR_MODIFIER_DEL
+                        ? _("Error: failed to modify %s.%s: %s.")
+                        : _("Error: failed to remove a value from %s.%s: %s."),
                     setting_name,
                     property,
                     local->message);
@@ -4985,7 +4989,7 @@ complete_property_name(NmCli                 *nmc,
                                             prefix,
                                             postfix);
     if (word_list)
-        g_print("%s", word_list);
+        nmc_print("%s", word_list);
 
     if (modifier != NM_META_ACCESSOR_MODIFIER_SET)
         return;
@@ -5014,7 +5018,7 @@ complete_property_name(NmCli                 *nmc,
                     if (!bi->base.property_alias
                         || !g_str_has_prefix(bi->base.property_alias, prefix))
                         continue;
-                    g_print("%s\n", bi->base.property_alias);
+                    nmc_print("%s\n", bi->base.property_alias);
                 }
             } else {
                 if (!property_info->is_cli_option)
@@ -5022,7 +5026,7 @@ complete_property_name(NmCli                 *nmc,
                 if (!property_info->property_alias
                     || !g_str_has_prefix(property_info->property_alias, prefix))
                     continue;
-                g_print("%s\n", property_info->property_alias);
+                nmc_print("%s\n", property_info->property_alias);
             }
         }
     }
@@ -5035,7 +5039,7 @@ run_rl_generator(rl_compentry_func_t *generator_func, const char *prefix)
     char *str;
 
     while ((str = generator_func(prefix, state))) {
-        g_print("%s\n", str);
+        nmc_print("%s\n", str);
         g_free(str);
         if (state == 0)
             state = 1;
@@ -5069,7 +5073,7 @@ complete_option(NmCli                    *nmc,
     }
     if (values) {
         for (; values[0]; values++)
-            g_print("%s\n", values[0]);
+            nmc_print("%s\n", values[0]);
         return TRUE;
     }
 
@@ -5094,11 +5098,11 @@ complete_existing_setting(NmCli *nmc, NMConnection *connection, const char *pref
         editor = nm_meta_setting_info_editor_find_by_setting(settings[i]);
 
         if (!prefix || g_str_has_prefix(editor->general->setting_name, prefix))
-            g_print("%s\n", editor->general->setting_name);
+            nmc_print("%s\n", editor->general->setting_name);
 
         if (editor->alias) {
             if (!prefix || g_str_has_prefix(editor->alias, prefix))
-                g_print("%s\n", editor->alias);
+                nmc_print("%s\n", editor->alias);
         }
     }
 }
@@ -5386,7 +5390,7 @@ nmc_process_connection_properties(NmCli              *nmc,
         if (!chosen) {
             if (*argc == 1 && nmc->complete) {
                 if (allow_setting_removal && g_str_has_prefix("remove", option))
-                    g_print("remove\n");
+                    nmc_print("remove\n");
                 complete_property_name(nmc, connection, modifier, option, NULL);
             }
             g_set_error(error,
@@ -5426,7 +5430,7 @@ connection_warnings(NmCli *nmc, NMConnection *connection)
 
     deprecated = nmc_connection_check_deprecated(NM_CONNECTION(connection));
     if (deprecated)
-        g_printerr(_("Warning: %s.\n"), deprecated);
+        nmc_printerr(_("Warning: %s.\n"), deprecated);
 
     connections = nmc_get_connections(nmc);
     id          = nm_connection_get_id(connection);
@@ -5441,15 +5445,15 @@ connection_warnings(NmCli *nmc, NMConnection *connection)
     }
 
     if (found > 0) {
-        g_printerr(g_dngettext(GETTEXT_PACKAGE,
-                               "Warning: There is another connection with the name '%1$s'. "
-                               "Reference the connection by its uuid '%2$s'\n",
-                               "Warning: There are %3$u other connections with the name "
-                               "'%1$s'. Reference the connection by its uuid '%2$s'\n",
-                               found),
-                   id,
-                   nm_connection_get_uuid(NM_CONNECTION(connection)),
-                   found);
+        nmc_printerr(g_dngettext(GETTEXT_PACKAGE,
+                                 "Warning: There is another connection with the name '%1$s'. "
+                                 "Reference the connection by its uuid '%2$s'\n",
+                                 "Warning: There are %3$u other connections with the name "
+                                 "'%1$s'. Reference the connection by its uuid '%2$s'\n",
+                                 found),
+                     id,
+                     nm_connection_get_uuid(NM_CONNECTION(connection)),
+                     found);
     }
 }
 
@@ -5481,9 +5485,9 @@ add_connection_cb(GObject *client, GAsyncResult *result, gpointer user_data)
          *
          * This is true for many messages that the user might parse. But this one
          * seems in particular interesting for a user to parse. */
-        g_print(_("Connection '%s' (%s) successfully added.\n"),
-                nm_connection_get_id(NM_CONNECTION(connection)),
-                nm_connection_get_uuid(NM_CONNECTION(connection)));
+        nmc_print(_("Connection '%s' (%s) successfully added.\n"),
+                  nm_connection_get_id(NM_CONNECTION(connection)),
+                  nm_connection_get_uuid(NM_CONNECTION(connection)));
         g_object_unref(connection);
     }
 
@@ -5595,8 +5599,8 @@ next:
 static void
 ask_option(NmCli *nmc, NMConnection *connection, const NMMetaAbstractInfo *abstract_info)
 {
-    char                  *value;
-    GError                *error  = NULL;
+    gs_free char          *value  = NULL;
+    gs_free_error GError  *error  = NULL;
     gs_free char          *prompt = NULL;
     gboolean               multi;
     const char            *setting_name, *property_name;
@@ -5629,14 +5633,17 @@ ask_option(NmCli *nmc, NMConnection *connection, const NMMetaAbstractInfo *abstr
     multi = NM_FLAGS_HAS(inf_flags, NM_META_PROPERTY_INF_FLAG_MULTI);
 
     if (multi)
-        g_print(_("You can specify this option more than once. Press  when you're done.\n"));
+        nmc_print(
+            _("You can specify this option more than once. Press  when you're done.\n"));
 
 again:
+    nm_clear_g_free(&value);
+    g_clear_error(&error);
+
     value = nmc_readline(&nmc->nmc_config, "%s", prompt);
 
     if (!set_option(nmc, connection, abstract_info, value, FALSE, &error)) {
-        g_printerr("%s\n", error->message);
-        g_clear_error(&error);
+        nmc_printerr("%s\n", error->message);
         goto again;
     }
 
@@ -5738,7 +5745,7 @@ want_provide_opt_args(const NmcConfig *nmc_config, const char *type, guint num)
         return TRUE;
 
     /* Ask for optional arguments. */
-    g_print(_("There are %d optional settings for %s.\n"), (int) num, type);
+    nmc_print(_("There are %d optional settings for %s.\n"), (int) num, type);
     answer =
         nmc_readline(nmc_config, _("Do you want to provide them? %s"), prompt_yes_no(TRUE, NULL));
     nm_strstrip(answer);
@@ -6870,7 +6877,7 @@ load_history_cmds(const char *uuid)
     kf       = g_key_file_new();
     if (!g_key_file_load_from_file(kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
         if (g_error_matches(err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE))
-            g_print("Warning: %s parse error: %s\n", filename, err->message);
+            nmc_print("Warning: %s parse error: %s\n", filename, err->message);
         g_key_file_free(kf);
         g_free(filename);
         return;
@@ -6909,7 +6916,7 @@ save_history_cmds(const char *uuid)
     if (!g_key_file_load_from_file(kf, filename, G_KEY_FILE_KEEP_COMMENTS, &error)) {
         if (!g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)
             && !g_error_matches(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
-            g_print("Warning: %s parse error: %s\n", filename, error->message);
+            nmc_print("Warning: %s parse error: %s\n", filename, error->message);
             return;
         }
         g_clear_error(&error);
@@ -6945,7 +6952,7 @@ editor_show_connection(NMConnection *connection, NmCli *nmc)
 static void
 editor_show_setting(NMSetting *setting, NmCli *nmc)
 {
-    g_print(_("['%s' setting values]\n"), nm_setting_get_name(setting));
+    nmc_print(_("['%s' setting values]\n"), nm_setting_get_name(setting));
 
     nmc->nmc_config_mutable.print_output     = NMC_PRINT_NORMAL;
     nmc->nmc_config_mutable.multiline_output = TRUE;
@@ -7052,24 +7059,24 @@ fail:
 static void
 editor_main_usage(void)
 {
-    g_print("------------------------------------------------------------------------------\n");
+    nmc_print("------------------------------------------------------------------------------\n");
     /* TRANSLATORS: do not translate command names and keywords before ::
      *              However, you should translate terms enclosed in <>.
      */
-    g_print(_("---[ Main menu ]---\n"
-              "goto     [ | ]        :: go to a setting or property\n"
-              "remove   [.] |  :: remove setting or reset property value\n"
-              "set      [. ]  :: set property value\n"
-              "describe [.]          :: describe property\n"
-              "print    [all | [.]]  :: print the connection\n"
-              "verify   [all | fix]                 :: verify the connection\n"
-              "save     [persistent|temporary]      :: save the connection\n"
-              "activate [] [/|]    :: activate the connection\n"
-              "back                                 :: go one level up (back)\n"
-              "help/?   []                 :: print this help\n"
-              "nmcli            :: nmcli configuration\n"
-              "quit                                 :: exit nmcli\n"));
-    g_print("------------------------------------------------------------------------------\n");
+    nmc_print(_("---[ Main menu ]---\n"
+                "goto     [ | ]        :: go to a setting or property\n"
+                "remove   [.] |  :: remove setting or reset property value\n"
+                "set      [. ]  :: set property value\n"
+                "describe [.]          :: describe property\n"
+                "print    [all | [.]]  :: print the connection\n"
+                "verify   [all | fix]                 :: verify the connection\n"
+                "save     [persistent|temporary]      :: save the connection\n"
+                "activate [] [/|]    :: activate the connection\n"
+                "back                                 :: go one level up (back)\n"
+                "help/?   []                 :: print this help\n"
+                "nmcli            :: nmcli configuration\n"
+                "quit                                 :: exit nmcli\n"));
+    nmc_print("------------------------------------------------------------------------------\n");
 }
 
 static void
@@ -7083,14 +7090,15 @@ editor_main_help(const char *command)
 
         switch (cmd) {
         case NMC_EDITOR_MAIN_CMD_GOTO:
-            g_print(_("goto [.] |   :: enter setting/property for editing\n\n"
-                      "This command enters into a setting or property for editing it.\n\n"
-                      "Examples: nmcli> goto connection\n"
-                      "          nmcli connection> goto secondaries\n"
-                      "          nmcli> goto ipv4.addresses\n"));
+            nmc_print(
+                _("goto [.] |   :: enter setting/property for editing\n\n"
+                  "This command enters into a setting or property for editing it.\n\n"
+                  "Examples: nmcli> goto connection\n"
+                  "          nmcli connection> goto secondaries\n"
+                  "          nmcli> goto ipv4.addresses\n"));
             break;
         case NMC_EDITOR_MAIN_CMD_REMOVE:
-            g_print(
+            nmc_print(
                 _("remove [.]  :: remove setting or reset property value\n\n"
                   "This command removes an entire setting from the connection, or if a property\n"
                   "is given, resets that property to the default value.\n\n"
@@ -7098,27 +7106,27 @@ editor_main_help(const char *command)
                   "          nmcli> remove eth.mtu\n"));
             break;
         case NMC_EDITOR_MAIN_CMD_SET:
-            g_print(_("set [. ]  :: set property value\n\n"
-                      "This command sets property value.\n\n"
-                      "Example: nmcli> set con.id My connection\n"));
+            nmc_print(_("set [. ]  :: set property value\n\n"
+                        "This command sets property value.\n\n"
+                        "Example: nmcli> set con.id My connection\n"));
             break;
         case NMC_EDITOR_MAIN_CMD_ADD:
-            g_print(_("add [. ]  :: add property value\n\n"
-                      "This command appends property value.\n\n"
-                      "Example: nmcli> add ipv4.addresses 192.168.1.1/24\n"));
+            nmc_print(_("add [. ]  :: add property value\n\n"
+                        "This command appends property value.\n\n"
+                        "Example: nmcli> add ipv4.addresses 192.168.1.1/24\n"));
             break;
         case NMC_EDITOR_MAIN_CMD_DESCRIBE:
-            g_print(_("describe [.]  :: describe property\n\n"
-                      "Shows property description. You can consult nm-settings(5) "
-                      "manual page to see all NM settings and properties.\n"));
+            nmc_print(_("describe [.]  :: describe property\n\n"
+                        "Shows property description. You can consult nm-settings(5) "
+                        "manual page to see all NM settings and properties.\n"));
             break;
         case NMC_EDITOR_MAIN_CMD_PRINT:
-            g_print(_("print [all]  :: print setting or connection values\n\n"
-                      "Shows current property or the whole connection.\n\n"
-                      "Example: nmcli ipv4> print all\n"));
+            nmc_print(_("print [all]  :: print setting or connection values\n\n"
+                        "Shows current property or the whole connection.\n\n"
+                        "Example: nmcli ipv4> print all\n"));
             break;
         case NMC_EDITOR_MAIN_CMD_VERIFY:
-            g_print(
+            nmc_print(
                 _("verify [all | fix]  :: verify setting or connection validity\n\n"
                   "Verifies whether the setting or connection is valid and can be saved later.\n"
                   "It indicates invalid values on error. Some errors may be fixed automatically\n"
@@ -7128,7 +7136,7 @@ editor_main_help(const char *command)
                   "          nmcli bond> verify\n"));
             break;
         case NMC_EDITOR_MAIN_CMD_SAVE:
-            g_print(
+            nmc_print(
                 _("save [persistent|temporary]  :: save the connection\n\n"
                   "Sends the connection profile to NetworkManager that either will save it\n"
                   "persistently, or will only keep it in memory. 'save' without an argument\n"
@@ -7140,48 +7148,48 @@ editor_main_help(const char *command)
                   "profile must be deleted.\n"));
             break;
         case NMC_EDITOR_MAIN_CMD_ACTIVATE:
-            g_print(_("activate [] [/|]  :: activate the connection\n\n"
-                      "Activates the connection.\n\n"
-                      "Available options:\n"
-                      "    - device the connection will be activated on\n"
-                      "/| - AP (Wi-Fi) or NSP (WiMAX) (prepend with / when  is "
-                      "not specified)\n"));
+            nmc_print(_("activate [] [/|]  :: activate the connection\n\n"
+                        "Activates the connection.\n\n"
+                        "Available options:\n"
+                        "    - device the connection will be activated on\n"
+                        "/| - AP (Wi-Fi) or NSP (WiMAX) (prepend with / when  is "
+                        "not specified)\n"));
             break;
         case NMC_EDITOR_MAIN_CMD_BACK:
-            g_print(_("back  :: go to upper menu level\n\n"));
+            nmc_print(_("back  :: go to upper menu level\n\n"));
             break;
         case NMC_EDITOR_MAIN_CMD_HELP:
-            g_print(_("help/? []  :: help for the nmcli commands\n\n"));
+            nmc_print(_("help/? []  :: help for the nmcli commands\n\n"));
             break;
         case NMC_EDITOR_MAIN_CMD_NMCLI:
-            g_print(_("nmcli [ ]  :: nmcli configuration\n\n"
-                      "Configures nmcli. The following options are available:\n"
-                      "status-line yes | no          [default: no]\n"
-                      "save-confirmation yes | no    [default: yes]\n"
-                      "show-secrets yes | no         [default: no]\n"
-                      "prompt-color  | <0-8>  [default: 0]\n"
-                      "%s" /* color table description */
-                      "\n"
-                      "Examples: nmcli> nmcli status-line yes\n"
-                      "          nmcli> nmcli save-confirmation no\n"
-                      "          nmcli> nmcli prompt-color 3\n"),
-                    "  0 = normal\n"
-                    "  1 = \33[30mblack\33[0m\n"
-                    "  2 = \33[31mred\33[0m\n"
-                    "  3 = \33[32mgreen\33[0m\n"
-                    "  4 = \33[33myellow\33[0m\n"
-                    "  5 = \33[34mblue\33[0m\n"
-                    "  6 = \33[35mmagenta\33[0m\n"
-                    "  7 = \33[36mcyan\33[0m\n"
-                    "  8 = \33[37mwhite\33[0m\n");
+            nmc_print(_("nmcli [ ]  :: nmcli configuration\n\n"
+                        "Configures nmcli. The following options are available:\n"
+                        "status-line yes | no          [default: no]\n"
+                        "save-confirmation yes | no    [default: yes]\n"
+                        "show-secrets yes | no         [default: no]\n"
+                        "prompt-color  | <0-8>  [default: 0]\n"
+                        "%s" /* color table description */
+                        "\n"
+                        "Examples: nmcli> nmcli status-line yes\n"
+                        "          nmcli> nmcli save-confirmation no\n"
+                        "          nmcli> nmcli prompt-color 3\n"),
+                      "  0 = normal\n"
+                      "  1 = \33[30mblack\33[0m\n"
+                      "  2 = \33[31mred\33[0m\n"
+                      "  3 = \33[32mgreen\33[0m\n"
+                      "  4 = \33[33myellow\33[0m\n"
+                      "  5 = \33[34mblue\33[0m\n"
+                      "  6 = \33[35mmagenta\33[0m\n"
+                      "  7 = \33[36mcyan\33[0m\n"
+                      "  8 = \33[37mwhite\33[0m\n");
             break;
         case NMC_EDITOR_MAIN_CMD_QUIT:
-            g_print(_("quit  :: exit nmcli\n\n"
-                      "This command exits nmcli. When the connection being edited "
-                      "is not saved, the user is asked to confirm the action.\n"));
+            nmc_print(_("quit  :: exit nmcli\n\n"
+                        "This command exits nmcli. When the connection being edited "
+                        "is not saved, the user is asked to confirm the action.\n"));
             break;
         default:
-            g_print(_("Unknown command: '%s'\n"), command);
+            nmc_print(_("Unknown command: '%s'\n"), command);
             break;
         }
     }
@@ -7242,21 +7250,21 @@ fail:
 static void
 editor_sub_help(void)
 {
-    g_print("------------------------------------------------------------------------------\n");
+    nmc_print("------------------------------------------------------------------------------\n");
     /* TRANSLATORS: do not translate command names and keywords before ::
      *              However, you should translate terms enclosed in <>.
      */
-    g_print(_("---[ Property menu ]---\n"
-              "set      []               :: set new value\n"
-              "add      []               :: add new option to the property\n"
-              "change                           :: change current value\n"
-              "remove   [ |