From 58287cbcc0c8a4964309e6d528aa65ea508b70d2 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 6 Aug 2021 15:17:05 +0200 Subject: [PATCH 01/45] core: rework IP configuration in NetworkManager using layer 3 configuration Completely rework IP configuration in the daemon. Use NML3Cfg as layer 3 manager for the IP configuration of an interface. Use NML3ConfigData as pieces of configuration that the various components collect and configure. NMDevice is managing most of the IP configuration at a higher level, that is, it starts DHCP and other IP methods. Rework the state handling there. This is a huge rework of how NetworkManager daemon handles IP configuration. Some fallout is to be expected. It appears the patch deletes many lines of code. That is not accurate, because you also have to count the files `src/core/nm-l3*`, which were unused previously. Co-authored-by: Beniamino Galvani --- .gitignore | 4 +- Makefile.am | 75 +- contrib/fedora/rpm/NetworkManager.spec | 1 - po/POTFILES.in | 1 - src/core/NetworkManagerUtils.c | 204 +- src/core/NetworkManagerUtils.h | 14 +- src/core/README.l3cfg.md | 368 + src/core/README.next.ip-config.md | 59 + src/core/devices/adsl/nm-device-adsl.c | 351 +- src/core/devices/bluetooth/nm-device-bt.c | 103 +- src/core/devices/nm-acd-manager.c | 497 - src/core/devices/nm-acd-manager.h | 34 - src/core/devices/nm-device-bond.c | 1 - src/core/devices/nm-device-dummy.c | 1 - src/core/devices/nm-device-ethernet.c | 436 +- src/core/devices/nm-device-infiniband.c | 1 - src/core/devices/nm-device-ip-tunnel.c | 1 - src/core/devices/nm-device-macsec.c | 2 +- src/core/devices/nm-device-macvlan.c | 1 - src/core/devices/nm-device-ppp.c | 286 +- src/core/devices/nm-device-private.h | 107 +- src/core/devices/nm-device-tun.c | 1 - src/core/devices/nm-device-utils.c | 7 +- src/core/devices/nm-device-utils.h | 11 +- src/core/devices/nm-device-vlan.c | 16 +- src/core/devices/nm-device-vxlan.c | 1 - src/core/devices/nm-device-wireguard.c | 77 +- src/core/devices/nm-device-wpan.c | 1 - src/core/devices/nm-device.c | 8101 +++++++---------- src/core/devices/nm-device.h | 44 +- src/core/devices/ovs/nm-device-ovs-bridge.c | 18 +- .../devices/ovs/nm-device-ovs-interface.c | 54 +- src/core/devices/ovs/nm-device-ovs-port.c | 18 +- src/core/devices/team/nm-device-team.c | 1 - src/core/devices/tests/meson.build | 1 - src/core/devices/tests/test-acd.c | 259 - src/core/devices/wifi/nm-device-iwd.c | 6 +- src/core/devices/wifi/nm-device-wifi-p2p.c | 47 +- src/core/devices/wifi/nm-device-wifi.c | 161 +- src/core/devices/wwan/libnm-wwan.ver | 5 +- src/core/devices/wwan/nm-device-modem.c | 195 +- src/core/devices/wwan/nm-modem-broadband.c | 438 +- src/core/devices/wwan/nm-modem-ofono.c | 148 +- src/core/devices/wwan/nm-modem.c | 949 +- src/core/devices/wwan/nm-modem.h | 77 +- src/core/dhcp/README.next.md | 103 + src/core/dhcp/nm-dhcp-client.c | 1173 +-- src/core/dhcp/nm-dhcp-client.h | 256 +- src/core/dhcp/nm-dhcp-dhclient-utils.c | 1 - src/core/dhcp/nm-dhcp-dhclient.c | 141 +- src/core/dhcp/nm-dhcp-dhcpcanon.c | 2 +- src/core/dhcp/nm-dhcp-dhcpcd.c | 22 +- src/core/dhcp/nm-dhcp-manager.c | 372 +- src/core/dhcp/nm-dhcp-manager.h | 48 +- src/core/dhcp/nm-dhcp-nettools.c | 334 +- src/core/dhcp/nm-dhcp-systemd.c | 327 +- src/core/dhcp/nm-dhcp-utils.c | 299 +- src/core/dhcp/nm-dhcp-utils.h | 27 +- src/core/dhcp/tests/test-dhcp-dhclient.c | 1 - src/core/dhcp/tests/test-dhcp-utils.c | 559 +- src/core/dns/nm-dns-dnsmasq.c | 33 +- src/core/dns/nm-dns-manager.c | 592 +- src/core/dns/nm-dns-manager.h | 18 +- src/core/dns/nm-dns-systemd-resolved.c | 63 +- src/core/dnsmasq/nm-dnsmasq-manager.c | 51 +- src/core/dnsmasq/nm-dnsmasq-manager.h | 10 +- src/core/main-utils.h | 3 - src/core/main.c | 10 - src/core/meson.build | 41 +- src/core/ndisc/nm-fake-ndisc.c | 38 +- src/core/ndisc/nm-fake-ndisc.h | 2 +- src/core/ndisc/nm-lndp-ndisc.c | 92 +- src/core/ndisc/nm-lndp-ndisc.h | 13 +- src/core/ndisc/nm-ndisc.c | 490 +- src/core/ndisc/nm-ndisc.h | 39 +- src/core/ndisc/tests/test-ndisc-fake.c | 70 +- src/core/ndisc/tests/test-ndisc-linux.c | 53 +- src/core/nm-act-request.c | 38 +- src/core/nm-act-request.h | 8 - src/core/nm-active-connection.h | 3 + src/core/nm-config-data.c | 84 +- src/core/nm-config-data.h | 6 +- src/core/nm-config.c | 36 +- src/core/nm-config.h | 3 +- src/core/nm-connectivity.c | 1 + src/core/nm-dhcp-config.c | 101 +- src/core/nm-dhcp-config.h | 5 +- src/core/nm-dispatcher.c | 181 +- src/core/nm-dispatcher.h | 8 +- src/core/nm-iface-helper.c | 857 -- src/core/nm-ip-config.c | 778 +- src/core/nm-ip-config.h | 59 +- src/core/nm-ip4-config.c | 3337 ------- src/core/nm-ip4-config.h | 673 -- src/core/nm-ip6-config.c | 2675 ------ src/core/nm-ip6-config.h | 205 - src/core/nm-l3-config-data.c | 40 +- src/core/nm-l3-config-data.h | 5 + src/core/nm-l3cfg.c | 79 + src/core/nm-l3cfg.h | 6 + src/core/nm-manager.c | 12 - src/core/nm-netns.c | 4 +- src/core/nm-netns.h | 4 +- src/core/nm-pacrunner-manager.c | 101 +- src/core/nm-pacrunner-manager.h | 7 +- src/core/nm-policy.c | 329 +- src/core/nm-proxy-config.c | 172 - src/core/nm-proxy-config.h | 41 - src/core/nm-types.h | 3 - src/core/platform/nm-fake-platform.c | 2 - src/core/platform/tests/test-route.c | 104 +- src/core/ppp/nm-ppp-manager-call.c | 18 - src/core/ppp/nm-ppp-manager-call.h | 6 - src/core/ppp/nm-ppp-manager.c | 252 +- src/core/ppp/nm-ppp-manager.h | 3 +- src/core/ppp/nm-ppp-mgr.c | 620 ++ src/core/ppp/nm-ppp-mgr.h | 131 + src/core/ppp/nm-ppp-plugin-api.h | 6 - src/core/ppp/nm-ppp-status.h | 5 +- src/core/tests/meson.build | 2 - src/core/tests/test-ip4-config.c | 380 - src/core/tests/test-ip6-config.c | 538 -- src/core/tests/test-l3cfg.c | 4 +- src/core/vpn/nm-vpn-connection.c | 4045 ++++---- src/core/vpn/nm-vpn-connection.h | 7 +- src/libnm-platform/nm-linux-platform.c | 1 - src/libnm-platform/nm-platform.c | 41 +- src/libnm-platform/nm-platform.h | 12 - src/libnm-systemd-core/meson.build | 2 - src/libnm-systemd-core/nm-sd.h | 2 - .../src/libsystemd-network/sd-ipv4acd.c | 616 -- .../src/libsystemd-network/sd-ipv4ll.c | 382 - .../src/systemd/sd-ipv4acd.h | 63 - .../src/systemd/sd-ipv4ll.h | 65 - tools/test-build.sh | 1 - 135 files changed, 12299 insertions(+), 23336 deletions(-) create mode 100644 src/core/README.l3cfg.md create mode 100644 src/core/README.next.ip-config.md delete mode 100644 src/core/devices/nm-acd-manager.c delete mode 100644 src/core/devices/nm-acd-manager.h delete mode 100644 src/core/devices/tests/test-acd.c create mode 100644 src/core/dhcp/README.next.md delete mode 100644 src/core/nm-iface-helper.c delete mode 100644 src/core/nm-ip4-config.c delete mode 100644 src/core/nm-ip4-config.h delete mode 100644 src/core/nm-ip6-config.c delete mode 100644 src/core/nm-ip6-config.h delete mode 100644 src/core/nm-proxy-config.c delete mode 100644 src/core/nm-proxy-config.h create mode 100644 src/core/ppp/nm-ppp-mgr.c create mode 100644 src/core/ppp/nm-ppp-mgr.h delete mode 100644 src/core/tests/test-ip4-config.c delete mode 100644 src/core/tests/test-ip6-config.c delete mode 100644 src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c delete mode 100644 src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c delete mode 100644 src/libnm-systemd-core/src/systemd/sd-ipv4acd.h delete mode 100644 src/libnm-systemd-core/src/systemd/sd-ipv4ll.h diff --git a/.gitignore b/.gitignore index 25c60b1270..8750c30c43 100644 --- a/.gitignore +++ b/.gitignore @@ -240,7 +240,6 @@ test-*.trs /src/core/NetworkManager-all-sym /src/core/NetworkManager.ver /src/core/devices/bluetooth/tests/nm-bt-test -/src/core/devices/tests/test-acd /src/core/devices/tests/test-lldp /src/core/devices/wifi/tests/test-devices-wifi /src/core/devices/wwan/tests/test-service-providers @@ -249,7 +248,6 @@ test-*.trs /src/core/dhcp/tests/test-dhcp-options /src/core/dhcp/tests/test-dhcp-utils /src/core/dnsmasq/tests/test-dnsmasq-utils -/src/core/nm-iface-helper /src/core/ndisc/tests/test-ndisc-fake /src/core/ndisc/tests/test-ndisc-linux /src/core/platform/tests/monitor @@ -406,10 +404,12 @@ test-*.trs /src/NetworkManager /src/NetworkManager-all-sym /src/NetworkManager.ver +/src/core/devices/tests/test-acd /src/core/initrd/nm-initrd-generator /src/core/initrd/tests/test-cmdline-reader /src/core/initrd/tests/test-dt-reader /src/core/initrd/tests/test-ibft-reader +/src/core/nm-iface-helper /src/devices/bluetooth/tests/nm-bt-test /src/devices/tests/test-acd /src/devices/tests/test-arping diff --git a/Makefile.am b/Makefile.am index 23bee4f89c..98b1507c7c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2074,9 +2074,6 @@ src_core_ldflags = $(CODE_COVERAGE_LDFLAGS) sbin_PROGRAMS += \ src/core/NetworkManager -libexec_PROGRAMS += \ - src/core/nm-iface-helper - noinst_LTLIBRARIES += \ src/core/libNetworkManagerBase.la \ src/core/libNetworkManager.la \ @@ -2323,8 +2320,6 @@ src_libnm_systemd_core_libnm_systemd_core_la_SOURCES = \ src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c \ src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c \ src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c \ - src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c \ - src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c \ src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c \ src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h \ src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c \ @@ -2342,8 +2337,6 @@ src_libnm_systemd_core_libnm_systemd_core_la_SOURCES = \ src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h \ src/libnm-systemd-core/src/systemd/sd-event.h \ src/libnm-systemd-core/src/systemd/sd-id128.h \ - src/libnm-systemd-core/src/systemd/sd-ipv4acd.h \ - src/libnm-systemd-core/src/systemd/sd-ipv4ll.h \ src/libnm-systemd-core/src/systemd/sd-lldp-rx.h \ src/libnm-systemd-core/src/systemd/sd-lldp.h \ src/libnm-systemd-core/src/systemd/sd-ndisc.h \ @@ -2394,10 +2387,6 @@ src_core_libNetworkManagerBase_la_SOURCES = \ src/core/nm-l3cfg.h \ src/core/nm-ip-config.c \ src/core/nm-ip-config.h \ - src/core/nm-ip4-config.c \ - src/core/nm-ip4-config.h \ - src/core/nm-ip6-config.c \ - src/core/nm-ip6-config.h \ \ src/core/dhcp/nm-dhcp-client.c \ src/core/dhcp/nm-dhcp-client.h \ @@ -2436,8 +2425,6 @@ src_core_libNetworkManager_la_SOURCES = \ src/core/nm-checkpoint-manager.c \ src/core/nm-checkpoint-manager.h \ \ - src/core/devices/nm-acd-manager.c \ - src/core/devices/nm-acd-manager.h \ src/core/devices/nm-lldp-listener.c \ src/core/devices/nm-lldp-listener.h \ src/core/devices/nm-device-utils.c \ @@ -2513,6 +2500,8 @@ src_core_libNetworkManager_la_SOURCES = \ src/core/dnsmasq/nm-dnsmasq-utils.c \ src/core/dnsmasq/nm-dnsmasq-utils.h \ \ + src/core/ppp/nm-ppp-mgr.c \ + src/core/ppp/nm-ppp-mgr.h \ src/core/ppp/nm-ppp-manager-call.c \ src/core/ppp/nm-ppp-manager-call.h \ src/core/ppp/nm-ppp-manager.h \ @@ -2586,8 +2575,6 @@ src_core_libNetworkManager_la_SOURCES = \ src/core/nm-firewall-utils.h \ src/core/nm-firewalld-manager.c \ src/core/nm-firewalld-manager.h \ - src/core/nm-proxy-config.c \ - src/core/nm-proxy-config.h \ src/core/nm-auth-manager.c \ src/core/nm-auth-manager.h \ src/core/nm-auth-utils.c \ @@ -2721,44 +2708,6 @@ $(src_core_NetworkManager_OBJECTS): $(src_libnm_core_public_mkenums_h) ############################################################################### -src_core_nm_iface_helper_CPPFLAGS = $(src_core_cppflags) - -src_core_nm_iface_helper_SOURCES = \ - src/core/nm-iface-helper.c - -src_core_nm_iface_helper_LDADD = \ - src/core/libNetworkManagerBase.la \ - src/libnm-core-aux-extern/libnm-core-aux-extern.la \ - src/libnm-core-impl/libnm-core-impl.la \ - $(libnm_crypto_lib) \ - src/libnm-core-aux-intern/libnm-core-aux-intern.la \ - src/libnm-platform/libnm-platform.la \ - src/libnm-base/libnm-base.la \ - src/libnm-log-core/libnm-log-core.la \ - src/libnm-udev-aux/libnm-udev-aux.la \ - src/libnm-glib-aux/libnm-glib-aux.la \ - src/libnm-std-aux/libnm-std-aux.la \ - src/libnm-systemd-core/libnm-systemd-core.la \ - src/libnm-systemd-shared/libnm-systemd-shared.la \ - src/n-acd/libn-acd.la \ - src/n-dhcp4/libn-dhcp4.la \ - src/c-rbtree/libc-rbtree.la \ - src/c-siphash/libc-siphash.la \ - $(SYSTEMD_JOURNAL_LIBS) \ - $(GLIB_LIBS) \ - $(LIBUDEV_LIBS) \ - $(LIBNDP_LIBS) \ - $(DL_LIBS) \ - $(NULL) - -src_core_nm_iface_helper_LDFLAGS = \ - -Wl,--version-script="$(srcdir)/linker-script-binary.ver" \ - $(SANITIZER_EXEC_LDFLAGS) - -$(src_core_nm_iface_helper_OBJECTS): $(src_libnm_core_public_mkenums_h) - -############################################################################### - noinst_LTLIBRARIES += src/nm-initrd-generator/libnmi-core.la src_nm_initrd_generator_libnmi_core_la_CPPFLAGS = \ @@ -4275,20 +4224,14 @@ src_core_devices_tests_ldflags = \ check_programs += \ src/core/devices/tests/test-lldp \ - src/core/devices/tests/test-acd + $(NULL) src_core_devices_tests_test_lldp_CPPFLAGS = $(src_core_cppflags_test) src_core_devices_tests_test_lldp_LDFLAGS = $(src_core_devices_tests_ldflags) src_core_devices_tests_test_lldp_LDADD = \ src/core/libNetworkManagerTest.la -src_core_devices_tests_test_acd_CPPFLAGS = $(src_core_cppflags_test) -src_core_devices_tests_test_acd_LDFLAGS = $(src_core_devices_tests_ldflags) -src_core_devices_tests_test_acd_LDADD = \ - src/core/libNetworkManagerTest.la - $(src_core_devices_tests_test_lldp_OBJECTS): $(src_libnm_core_public_mkenums_h) -$(src_core_devices_tests_test_acd_OBJECTS): $(src_libnm_core_public_mkenums_h) EXTRA_DIST += \ src/core/devices/tests/meson.build @@ -4391,22 +4334,12 @@ check_programs += \ src/core/tests/test-core \ src/core/tests/test-core-with-expect \ src/core/tests/test-dcb \ - src/core/tests/test-ip4-config \ - src/core/tests/test-ip6-config \ src/core/tests/test-l3cfg \ src/core/tests/test-systemd \ src/core/tests/test-utils \ src/core/tests/test-wired-defname \ $(NULL) -src_core_tests_test_ip4_config_CPPFLAGS = $(src_core_cppflags_test) -src_core_tests_test_ip4_config_LDFLAGS = $(src_core_tests_ldflags) -src_core_tests_test_ip4_config_LDADD = $(src_core_tests_ldadd) - -src_core_tests_test_ip6_config_CPPFLAGS = $(src_core_cppflags_test) -src_core_tests_test_ip6_config_LDFLAGS = $(src_core_tests_ldflags) -src_core_tests_test_ip6_config_LDADD = $(src_core_tests_ldadd) - src_core_tests_test_dcb_CPPFLAGS = $(src_core_cppflags_test) src_core_tests_test_dcb_LDFLAGS = $(src_core_tests_ldflags) src_core_tests_test_dcb_LDADD = $(src_core_tests_ldadd) @@ -4434,8 +4367,6 @@ src_core_tests_test_l3cfg_LDADD = $(src_core_tests_ldadd) $(src_core_tests_test_core_OBJECTS): $(src_libnm_core_public_mkenums_h) $(src_core_tests_test_core_with_expect_OBJECTS): $(src_libnm_core_public_mkenums_h) $(src_core_tests_test_dcb_OBJECTS): $(src_libnm_core_public_mkenums_h) -$(src_core_tests_test_ip4_config_OBJECTS): $(src_libnm_core_public_mkenums_h) -$(src_core_tests_test_ip6_config_OBJECTS): $(src_libnm_core_public_mkenums_h) $(src_core_tests_test_l3cfg_OBJECTS): $(src_libnm_core_public_mkenums_h) $(src_core_tests_test_utils_OBJECTS): $(src_libnm_core_public_mkenums_h) $(src_core_tests_test_wired_defname_OBJECTS): $(src_libnm_core_public_mkenums_h) diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec index 0d8e392316..f1b814cbd0 100644 --- a/contrib/fedora/rpm/NetworkManager.spec +++ b/contrib/fedora/rpm/NetworkManager.spec @@ -1000,7 +1000,6 @@ fi %ghost %attr(755, root, root) %{_sbindir}/ifdown %{_libexecdir}/nm-dhcp-helper %{_libexecdir}/nm-dispatcher -%{_libexecdir}/nm-iface-helper %{_libexecdir}/nm-initrd-generator %{_libexecdir}/nm-daemon-helper %{_libexecdir}/nm-sudo diff --git a/po/POTFILES.in b/po/POTFILES.in index 0cd24d9940..76d20094d0 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -33,7 +33,6 @@ src/core/dns/nm-dns-manager.c src/core/main-utils.c src/core/main.c src/core/nm-config.c -src/core/nm-iface-helper.c src/core/nm-manager.c src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c diff --git a/src/core/NetworkManagerUtils.c b/src/core/NetworkManagerUtils.c index 0da8e0a949..fdb2b40443 100644 --- a/src/core/NetworkManagerUtils.c +++ b/src/core/NetworkManagerUtils.c @@ -227,6 +227,21 @@ out: /*****************************************************************************/ +void +nm_utils_ppp_ip_methods_enabled(NMConnection *connection, + gboolean * out_ip4_enabled, + gboolean * out_ip6_enabled) +{ + NM_SET_OUT(out_ip4_enabled, + nm_streq0(nm_utils_get_ip_config_method(connection, AF_INET), + NM_SETTING_IP4_CONFIG_METHOD_AUTO)); + NM_SET_OUT(out_ip6_enabled, + nm_streq0(nm_utils_get_ip_config_method(connection, AF_INET6), + NM_SETTING_IP6_CONFIG_METHOD_AUTO)); +} + +/*****************************************************************************/ + void _nm_utils_complete_generic_with_params(NMPlatform * platform, NMConnection * connection, @@ -1298,7 +1313,7 @@ void nm_utils_ip_route_attribute_to_platform(int addr_family, NMIPRoute * s_route, NMPlatformIPRoute *r, - guint32 route_table) + gint64 route_table) { GVariant * variant; guint32 table; @@ -1310,6 +1325,8 @@ nm_utils_ip_route_attribute_to_platform(int addr_family, nm_assert(s_route); nm_assert_addr_family(addr_family); nm_assert(r); + nm_assert(route_table >= -1); + nm_assert(route_table <= (gint64) G_MAXUINT32); #define GET_ATTR(name, dst, variant_type, type, dflt) \ G_STMT_START \ @@ -1336,10 +1353,16 @@ nm_utils_ip_route_attribute_to_platform(int addr_family, GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_TABLE, table, UINT32, uint32, 0); - if (!table && r->type_coerced == nm_platform_route_type_coerce(RTN_LOCAL)) + if (table != 0) + r->table_coerced = nm_platform_route_table_coerce(table); + else if (r->type_coerced == nm_platform_route_type_coerce(RTN_LOCAL)) r->table_coerced = nm_platform_route_table_coerce(RT_TABLE_LOCAL); + else if (route_table == 0) + r->table_coerced = nm_platform_route_table_coerce(RT_TABLE_MAIN); + else if (route_table > 0) + r->table_coerced = nm_platform_route_table_coerce(route_table); else - r->table_coerced = nm_platform_route_table_coerce(table ?: (route_table ?: RT_TABLE_MAIN)); + r->table_any = TRUE; if (NM_IS_IPv4(addr_family)) { guint8 scope; @@ -1395,39 +1418,20 @@ nm_utils_ip_route_attribute_to_platform(int addr_family, /*****************************************************************************/ -static int -_addresses_sort_cmp_4(gconstpointer a, gconstpointer b, gpointer user_data) -{ - return nm_platform_ip4_address_pretty_sort_cmp( - NMP_OBJECT_CAST_IP4_ADDRESS(*((const NMPObject **) a)), - NMP_OBJECT_CAST_IP4_ADDRESS(*((const NMPObject **) b))); -} - -static int -_addresses_sort_cmp_6(gconstpointer a, gconstpointer b, gpointer user_data) -{ - return nm_platform_ip6_address_pretty_sort_cmp( - NMP_OBJECT_CAST_IP6_ADDRESS(*((const NMPObject **) a)), - NMP_OBJECT_CAST_IP6_ADDRESS(*((const NMPObject **) b)), - (((NMSettingIP6ConfigPrivacy) GPOINTER_TO_INT(user_data)) - == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR)); -} - void nm_utils_ip_addresses_to_dbus(int addr_family, const NMDedupMultiHeadEntry *head_entry, const NMPObject * best_default_route, - NMSettingIP6ConfigPrivacy ipv6_privacy, GVariant ** out_address_data, GVariant ** out_addresses) { - const int IS_IPv4 = NM_IS_IPv4(addr_family); - GVariantBuilder builder_data; - GVariantBuilder builder_legacy; - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; - gs_free const NMPObject **addresses = NULL; - guint naddr; - guint i; + const int IS_IPv4 = NM_IS_IPv4(addr_family); + GVariantBuilder builder_data; + GVariantBuilder builder_legacy; + char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + NMDedupMultiIter iter; + const NMPObject *obj; + guint i; nm_assert_addr_family(addr_family); @@ -1443,19 +1447,11 @@ nm_utils_ip_addresses_to_dbus(int addr_family, if (!head_entry) goto out; - addresses = - (const NMPObject **) nm_dedup_multi_objs_to_array_head(head_entry, NULL, NULL, &naddr); - - nm_assert(addresses && naddr); - - g_qsort_with_data(addresses, - naddr, - sizeof(addresses[0]), - IS_IPv4 ? _addresses_sort_cmp_4 : _addresses_sort_cmp_6, - GINT_TO_POINTER(ipv6_privacy)); - - for (i = 0; i < naddr; i++) { - const NMPlatformIPXAddress *address = NMP_OBJECT_CAST_IPX_ADDRESS(addresses[i]); + i = 0; + nm_dedup_multi_iter_init(&iter, head_entry); + while ( + nm_platform_dedup_multi_iter_next_obj(&iter, &obj, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4))) { + const NMPlatformIPXAddress *address = NMP_OBJECT_CAST_IPX_ADDRESS(obj); if (out_address_data) { GVariantBuilder addr_builder; @@ -1527,6 +1523,8 @@ nm_utils_ip_addresses_to_dbus(int addr_family, : &in6addr_any)); } } + + i++; } out: @@ -1654,6 +1652,128 @@ nm_utils_ip_routes_to_dbus(int addr_family, /*****************************************************************************/ +NMSetting * +nm_utils_platform_capture_ip_setting(NMPlatform *platform, + int addr_family, + int ifindex, + gboolean maybe_ipv6_disabled) +{ + const int IS_IPv4 = NM_IS_IPv4(addr_family); + gs_unref_object NMSettingIPConfig *s_ip = NULL; + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPObject * obj; + const char * method = NULL; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + const NMPlatformIPXRoute * best_default_route = NULL; + + s_ip = + NM_SETTING_IP_CONFIG(IS_IPv4 ? nm_setting_ip4_config_new() : nm_setting_ip6_config_new()); + + if (ifindex <= 0 || !nm_platform_link_get(platform, ifindex)) { + g_object_set(s_ip, + NM_SETTING_IP_CONFIG_METHOD, + IS_IPv4 ? NM_SETTING_IP4_CONFIG_METHOD_DISABLED + : NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NULL); + return NM_SETTING(g_steal_pointer(&s_ip)); + } + + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex); + nm_platform_iter_obj_for_each (&iter, platform, &lookup, &obj) { + const NMPlatformIPXAddress *address = NMP_OBJECT_CAST_IPX_ADDRESS(obj); + nm_auto_unref_ip_address NMIPAddress *s_addr = NULL; + + if (!IS_IPv4) { + /* Ignore link-local address. */ + if (IN6_IS_ADDR_LINKLOCAL(address->ax.address_ptr)) { + if (!method) + method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; + continue; + } + } + + /* Detect dynamic address */ + if (address->ax.lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { + method = + IS_IPv4 ? NM_SETTING_IP4_CONFIG_METHOD_AUTO : NM_SETTING_IP6_CONFIG_METHOD_AUTO; + continue; + } + + /* Static address found. */ + if (IS_IPv4) { + if (!method) + method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL; + } else { + if (NM_IN_STRSET(method, NULL, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) + method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; + } + + s_addr = + nm_ip_address_new_binary(addr_family, address->ax.address_ptr, address->ax.plen, NULL); + + if (IS_IPv4) { + if (address->a4.label[0]) { + nm_ip_address_set_attribute(s_addr, + NM_IP_ADDRESS_ATTRIBUTE_LABEL, + g_variant_new_string(address->a4.label)); + } + } + + nm_setting_ip_config_add_address(s_ip, s_addr); + } + + if (!method) { + /* Use 'disabled/ignore' if the method wasn't previously set */ + if (IS_IPv4) + method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; + else + method = maybe_ipv6_disabled ? NM_SETTING_IP6_CONFIG_METHOD_DISABLED + : NM_SETTING_IP6_CONFIG_METHOD_IGNORE; + } + g_object_set(s_ip, NM_SETTING_IP_CONFIG_METHOD, method, NULL); + + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4), ifindex); + nm_platform_iter_obj_for_each (&iter, platform, &lookup, &obj) { + const NMPlatformIPXRoute *route = NMP_OBJECT_CAST_IPX_ROUTE(obj); + nm_auto_unref_ip_route NMIPRoute *s_route = NULL; + + if (!IS_IPv4) { + /* Ignore link-local route. */ + if (IN6_IS_ADDR_LINKLOCAL(route->rx.network_ptr)) + continue; + } + + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route)) { + if (!best_default_route) + best_default_route = route; + continue; + } + + s_route = nm_ip_route_new_binary(addr_family, + route->rx.network_ptr, + route->rx.plen, + nm_platform_ip_route_get_gateway(addr_family, &route->rx), + route->rx.metric, + NULL); + nm_setting_ip_config_add_route(s_ip, s_route); + } + + 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, + nm_platform_ip_route_get_gateway(addr_family, &best_default_route->rx), + sbuf), + NULL); + } + + return NM_SETTING(g_steal_pointer(&s_ip)); +} + +/*****************************************************************************/ + /* Singleton NMPlatform subclass instance and cached class object */ NM_DEFINE_SINGLETON_INSTANCE(NMPlatform); diff --git a/src/core/NetworkManagerUtils.h b/src/core/NetworkManagerUtils.h index a2ac732e83..0f68a3f6ef 100644 --- a/src/core/NetworkManagerUtils.h +++ b/src/core/NetworkManagerUtils.h @@ -19,6 +19,10 @@ const char *nm_utils_get_ip_config_method(NMConnection *connection, int addr_fam const char *nm_utils_get_shared_wifi_permission(NMConnection *connection); +void nm_utils_ppp_ip_methods_enabled(NMConnection *connection, + gboolean * out_ip4_enabled, + gboolean * out_ip6_enabled); + void _nm_utils_complete_generic_with_params(NMPlatform * platform, NMConnection * connection, const char * ctype, @@ -195,12 +199,11 @@ nm_utils_tfilters_from_tc_setting(NMPlatform *platform, NMSettingTCConfig *s_tc, void nm_utils_ip_route_attribute_to_platform(int addr_family, NMIPRoute * s_route, NMPlatformIPRoute *r, - guint32 route_table); + gint64 route_table); void nm_utils_ip_addresses_to_dbus(int addr_family, const NMDedupMultiHeadEntry *head_entry, const NMPObject * best_default_route, - NMSettingIP6ConfigPrivacy ipv6_privacy, GVariant ** out_address_data, GVariant ** out_addresses); @@ -271,6 +274,13 @@ NM_AUTO_DEFINE_FCN(NMDhcpLease *, _nm_auto_unref_dhcplease, nm_dhcp_lease_unref) /*****************************************************************************/ +NMSetting *nm_utils_platform_capture_ip_setting(NMPlatform *platform, + int addr_family, + int ifindex, + gboolean maybe_ipv6_disabled); + +/*****************************************************************************/ + void nm_platform_setup(NMPlatform *instance); NMPlatform *nm_platform_get(void); diff --git a/src/core/README.l3cfg.md b/src/core/README.l3cfg.md new file mode 100644 index 0000000000..901d40b4a9 --- /dev/null +++ b/src/core/README.l3cfg.md @@ -0,0 +1,368 @@ +L3Cfg Rework +============ + +NMDevice is complex. Together with NMManager, NMDevice does too much. + +The goal is to rework the IP configuration (Layer 3) to be a more separate +part of the code that is better maintainable, easier to understand and +extend and more correct. + +Current Situation +----------------- + +- [NMManager](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-manager.c): + this is the main object (a singleton) that drives most things. + Among many other things, it creates NMDevice instances and coordinates. + +- [NMDevice](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c): + this represents a device. This is a subclass of NMDBusObject, + it is thus directly exported on D-Bus (as D-Bus objects like + `/org/freedesktop/NetworkManager/Devices/1`). + It also manages all aspects of the device. It has an overall state + (`NM_DEVICE_STATE`) but lots of more specific states (e.g. current state + of DHCP configuration). As always, the hardest part in programming are + stateful objects, and NMDevice has *a lot* of state. The code is huge and + hard to understand and the class has (too) many responsibilities. \ + \ + NMDevice also has subclasses, which are determined based on the "device type". That + means, there are subclasses like NMDeviceEthernet and NMDeviceBridge. As such, the + subclasses also export additional D-Bus interfaces. These subclasses also handle + the Layer 2 specific aspects of the device. For this aspect, delegation probably + would have been a better choice. On the other hand, IP configuration is almost entirely + managed by the parent class. Which is good, because the IP configuration is common to all + device types, but is bad because NMDevice already does so many things. + +- [NMIP4Config](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-ip4-config.c) (and NMIP6Config): + these are also subclasses of NMDBusObject + and exported on D-Bus on paths like `/org/freedesktop/NetworkManager/IP4Config/1`. + The device's `IP4Config` property refers to these objects. They contain + the runtime IP information of that device. I don't think these objects + should exist on the D-Bus API, as NMDevice could directly expose these properties. + But for historic reasons, such is our D-Bus API. + Other than that, NMIP4Config objects are also used internally for tracking + IP configuration. For example, [when](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/dhcp/nm-dhcp-nettools.c#L563) + we receive a DHCP lease, we construct a NMIP4Config object with the addresses, DNS settings, + and so on. These + instances are then [tracked by](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L519) + NMDevice, and [merged](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L8928) + into an instance that is then exposed on D-Bus. As such, this class has two + mostly independent purposes. + +- [NMDhcpClient](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/dhcp/nm-dhcp-client.c): + our DHCP "library". It's a simple object with a clear API that + abstracts most of the complexity of handling DHCP. But still, NMDevice + needs to drive the DHCP client instance. Meaning, it needs to create (start) and stop + them and hook up signals for changes (new lease) and timeout. This is mostly + fine and unavoidable. The point is that while specific tasks are well abstracted + (like the details of DHCP), there is still some state in NMDevice that is related + to manage these tasks. DHCP is one of many such tasks, like also + link local addresses, SLAAC or LLDP. + This leads to the increased complexity of NMDevice, which manages a large variety + of such tasks. + +### Problems: + +1. first the sheer code size of [nm-device.c](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L19030). + It's hard to understand and maintain, and this results in misbehaviours. Also, features that should be easy to implement + are not. Also, there are inefficiencies that are hard to fix. + +2. NMDevice and NMIP4Config are both exported on D-Bus while having other responsibilities. + Being subclasses of NMDBusObject, they are glued to the D-Bus API. For example, NMIP4Config is + also used for other purposes (for tracking IP configuration internally). + +3. NMDevice simply does too much. IP configuration should be a separate, encapsulated + API to make allow NMDevice to be smaller and the IP configuration part better + testable, understandable and smaller too. + +4. in the current model, NMDevice can be related to zero, one or two ifindexes. For example, + for ethernet devices, there is commonly only one actual netdev device (and one ifindex). + For OVS devices, there is no ifindex. For NMDeviceModem or NMDeviceBluetooth there is + a NMDevice instance that has initially no ifindex (it represents the tty serial port + or the bluetooth device) but during activation it gets and ip ifindex. With PPPoE, + the ethernet device can even have two ifindexes (one for the underlying ethernet and + one for the PPP device). That is all utterly confusing, inconsistent and limited. + For example, not all interfaces you see in `ip link` can be found in the D-Bus API. + The D-Bus API also does not give access to the ifindex (which is the real identifier + for a netdev devices). It only exposes the IpInterface name. That should be improved too, + but even such seemingly simple things are not done for years, because it's not trivially + clear what the right ifindex is. + Also a device instance on D-Bus significantly changes its meaning when it activates/deactivates + and it starts/stops being responsible for an ifindex. + In the future there should be devices that represent exactly one netdev device (an ifindex) + and devices that don't have an ifindex. That is follow up work and hinted by + [rhbz#1066703](https://bugzilla.redhat.com/show_bug.cgi?id=1066703). But simplifying + the IP configuration is a requisite before addressing that rework. + With this we will have controller and controlled devices. That means, a controller devices + (that for example represents a bluetooth device) will need to configure IP address on the + controlled IP device. That would be doable by injecting the IP config on that device, + but as the device already does so much, it would be better if this would be a separate + IP configuration manager for that ifindex. + +5. NMIP4Config exports properties on D-Bus like [AddressData](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/introspection/org.freedesktop.NetworkManager.IP4Config.xml#L26). + which are the currently configured IP addresses. These should be directly obtained + from the NMPlatform cache, which contains the correct list of addresses as kernel + exposes them via rtnetlink. Instead, whenever there are changes in platform we + [generate](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L14223) + an NMIP4Config instance, then we merge, intersect and subtract this captured information + with the IP configs we want to configure. Finally we merge them together again + and sync the result to platform. This is bad, wrong and inefficient. + We must not mix "what is configured" with "what we want to configure". The current + approach also re-generates these IP config instance whenever something in platform changes. + That does not scale. If we have any hope to handle thousands of routes, this needs to change. + +6. The NMIP4Config objects are mutable, and they are heavily mutated. When we create an NMIP4Config + instance that represent a DHCP lease, we will [subtract](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L14236) + addresses that were externally removed. That is wrong, because during a reapply we + will need to know these addresses again. The solution for that is not to mutate this + data, but track whether IP addresses are removed separately. + +7. NMDevice also does ACD, but it can only do it for addresses received via DHCP. + It implicitly also does ACD for IPv4LL, but that is via using the n_ipv4ll library. + It would be good to have an option that we can configure IPv4LL for any address. + Also, if you manually configure an address like 192.168.2.5 (for which we don't do + ACD) and the same address is obtained via DHCP, then doing ACD for the address is wrong. + There needs to be link-wide view of the addresses, and not only looking at individual + addresses when deciding to do ACD. + +8. As IP configuration is done by NMDevice, VPN connections have limited capabilities + in this regard. + When a VPN has IP addresses, then it injects them into NMDevice by + [providing](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L13696) + an NMIP4Config. However, that means VPNs cannot do DHCP or IPv4LL, because it can + only inject known configuration. That would be very useful for example with a tap + device with openvpn. The real problem here is that NMVpnConnection are + treated special, when they should be more like devices. That should be reworked in the future, + by reworking VPN plugins. Regardless, having IP configuration handled by NMDevice is limiting. + +9. NetworkManager currently supports `ipv4.method` which can be "manual", "disabled" or + "auto". This scheme does not allow for example to enable IPv4LL together with DHCPv4. + As a special case, you can configure `ipv4.method=auto` together with static + addresses in `ipv4.addresses`, so combining DHCP and static addressing works. But in general, + this scheme is limited. In the future we should have more flexible schemes, where + addressing methods can be independently enabled or disabled. Also, we currently + have `ipv4.may-fail`, but that is limited as well. For example, + `ipv4.may-fail=yes` and `ipv6.may-fail=yes` still means that at least one of the + address families must succeed. That makes sense for certain use cases, but it + means, you cannot have truly best-effort, opportunistic DHCP with this way. + As workaround for that there is `ipv4.dhcp-timeout=infinity`. In + general it is not only useful to enable methods independently, we also configure + independently whether they are required or optional (and possibly, that they are optional + but at least one of several optional methods must succeed). Anyway. The point + is there is a need to make IP configuration more flexible. Currently it is not. + Such a seemingly simple extension would be surprisingly difficult to implement + because [the code](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L6616) is + all over the place. The way how NMDevice tracks the overall activation state is + hard to understand. This should be improved and possibly could be improved in a + smaller refactoring effort. But instead of a smaller effort, we will use the big hammer + with L3Cfg rework. + +10. There are two classes NMIP4Config and NMIP6Config. Handling both address families is + commonly similar, so there is lot of similar code in both. They should be unified + so that similar code can handle both address families. + + +Solution and Future +------------------- + +NML3Cfg work is supposed to simplify some part of NMDevice: the part related to +IP configuration. This is a huge rework of a core part of NetworkManager. Arguably, +some parts maybe could be done more evolutionary, but the fundamental problems require +to rip out NMIP4Config and replace it by something better. Doing that is a large rework +that changes NMDevice heavily. That is also the opportunity to get the smaller issues +right. + +There is already a new class [NML3Cfg](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.h#L141) +(currently unused). An NML3Cfg instance is responsible for handling IP configuration +of an ifindex. Consequently, we can ask NMNetns to [get](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-netns.c#L142) +(or create) a NML3Cfg instance for an ifindex. +The idea is that there can be multiple users (NMDevice and NMVpnConnection and future controller devices) +that use the same NML3Cfg instance. Especially with a future rework of NMDevice where +a NMDevice only manages one ifindex (or none), there is a need that multiple +devices manage the IP configuration on the same device. Independent users can cooperate +to configure IP configuration on the same device. Already now with Libreswan VPN, where the VPN "injects" +its NMIP4Config in NMDevice. Or with PPPoE, where the NMDeviceEthernet is both about IP configuration +for the PPPoE device. + +There is also a new class [NML3ConfigData](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3-config-data.h). +This replaces some aspect of NMIP4Config/NMIP6Config. A NML3ConfigData object is immutable and has no real logic +(or state). It has some "logic", like comparing two NML3ConfigData instances, logging it, or merging two (immutable) +instances into a new instance. But as the class is immutable, that logic is rather simple. This class is +used to track information. As it's immutable, anybody who is interested can keep a reference +for it's own purpose. For example, NMDhcpClient will generate a NML3ConfigData with the information +of the lease. It may keep the reference, but it will also tell NMDevice about it. The NMDevice +will then itself tell NML3Cfg to accept this configuration. This works by calling +[add()/remove()](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L2654). +One NML3ConfigData can also track both IPv4 and IPv6 information. It's a general set of IP related +configuration, that has some address specific properties. Those are then duplicated for both address +families and implemented in a way to minimize code duplication and encourage to treat them the same. +As this replaces an aspect of NMIP4Config, NMIP4Config can focus on it's other purpose: to expose data on D-Bus. + +What NML3Cfg then does, is to merge all NML3ConfigData, and "commit" it to platform. Thereby it knows +which addresses it configured the last time (if they no longer are to be configured, they must be removed). +This is done [here](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L3442). + +As independent users should be able to cooperate, it is not appropriate that they call "commit". +Instead, they set a commit type ([here](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L3476), +and whenever something changes, NML3Cfg knows the aggregated commit type. That is necessary +because when we activate a device, we may want to preserve the existing IP configuration (e.g. after +a restart of NetworkManager). During that time is the NML3Cfg instance set to a reduced commit +mode (ASSUME). + +NML3Cfg will also handle IPv4 ACD. Any user of NML3Cfg registers/unregisters NML3ConfigData instances +that should be configured. Thereby they also say whether ACD should be done for the IPv4 addresses. +NML3Cfg then keeps state for each IPv4 address, whether ACD should be performed, and whether the +address is ready to be configured. NML3Cfg does not do DHCP or similar. That is still the responsibility +of NMDevice to run a NMDhcpClient. But it does run ACD, because whether to perform ACD on an address +requires a holistic view of all addresses of that interface. For example, if you configure a static +IP address 192.168.2.5 (with ACD disabled) and you also get the same address via DHCP, then ACD should +not performed for that address (even if the user configured ACD with DHCP). Of course, that is a very +unlikely example. More likely is that NetworkManager is restarted and it leaves the addresses (that passed +ACD) configured. After restart, DHCP finds the same addresses and no new ACD should be performed. This shows +that the ACD state depends all the IP addresses on an interface, +and thus it's done by NML3Cfg. The API for this is very simple. Users enable/disable ACD during nm_l3cfg_add_config() +and receive events like [NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L303). +Another advantage is that ACD now works for any kinds of addresses. Currently it only works for addresses +from DHCP and link local addresses. + +NML3Cfg does not implement or drive DHCP. However, as it already does ACD it gained it's own IPv4LL +"library": [NML3IPv4LL](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L3624). +This will replace nettools' n-ipv4ll library, because that library also does ACD internally, while we want +to use the holistic view that NML3Cfg has. What this means, is that the user (NMDevice) +can request a NML3IPv4LL handle from the NML3Cfg instance, and it just does it with a simple API. +All the user might do is to enable/disable the handle and to react to signals (if it cares to find +out whether IPv4LL fails). + +The general parts of NML3Cfg are already implemented. It has unit tests and can be tested independently. +You might note that NML3Cfg is not trivial already, but the API that it provides is as simple as possible: +create immutable NML3ConfigData instance, and add/remove them. Optionally, handle the ACD events and +listen to some events. The complexity that NML3Cfg has, will lead in the same amount simplify NMDevice. + +What is missing is NMDevice using this new API. Instead of creating and tracking NMIP4Config instances, +it needs to track NML3ConfigData instances. In principle that sounds simple, in practice that changes +large part of "nm-device.c". + +Thereby also the state machine for NM_DEVICE_STATE will be improved. It's anyway a rewrite. This will lay the +groundwork for more flexible configuration of IP methods, with different failure modes (opportunistic or +mandatory). + +What then also should be easier, to combine IPv4LL with other addressing methods. In Windows AFAIK, if you +don't get a DHCP address it will configure a IPv4LL address. That is also what RFC suggests, but which we +currently don't support. + +In general, change the way how external IP addresses/routes are tracked. This merge, intersect, subtract +approach does not perform well. Currently we react on signals and it's hard to understand what happens +in response to that, or whether it's really the correct thing to do. See yourself starting from +[here](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L14214). + +### DHCP + +Currently, when NMDhcpClient receives a lease, it emits a signal with two things: the NMIP4Config +instance (containing addresses, routes, DNS settings and other information for later use), and a string +dictionary with the DHCP lease options (they are mainly used to expose them on D-Bus). The latter is +immutable (meaning, it's not changed afterwards). That does not significantly change with L3Cfg. The +difference is that instead of NMIP4Config a NML3ConfigData instance gets created. That instance then +references the (immutable) strdict. With that, any part of the code that has access to the NML3ConfigData, +also has access to the lease options. So instead of two separate +pieces of information, the result of a lease event will only be a NML3ConfigData instance (which internally +tracks the strdict with the DHCP lease options). + +Later, when NML3Cfg configures an interface, it takes all NML3ConfigData instances that were added to +it, and merges them. [Currently](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3-config-data.c#L2693), +the merged data will not contain the lease information, but it's probably not needed anyway. + +If it would be needed, the question is what happens if multiple lease informations are present +during the merge. Duplicate leases would not commonly happen, but in general, the merging algorithm +needs to take into account priorities and conflicting data. +That is done by users who call [add](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L2658) +to provide a priority for the NML3ConfigData instance. +Later, the instances get sorted by priority and merging is smart to take that into account +([here](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L2983)). + +Also, we currently inject the route-metric and table into the generated NMIP4Config. +Those settings come from the connection profiles and not from DHCP. We will avoid that +by allowing the routes in NML3ConfigData to be marked as metric\_any and table\_any. +That way,the NML3ConfigData is independent (and immutable) with respect to those settings. +The same happens for example with PPP, where the modem starts PPP, and currently the +route and metric needs to be passed several layers down. But worst, those settings +can change during reapply. Currently that means we need to hack NMIP4Config with +those changes. Later, we will only tell NML3Cfg to track the NML3ConfigData with +different settings. + +### DNS + +DNS information is currently set in the NMIP4Config instances. That happens for example with the DNS information +from a DHCP lease, but also with the static DNS settings from the connection profile. Later, the same information +will packed in NML3ConfigData. + +One nice difference is again the immutability. Currently, NMDnsManager keeps a reference to all relevant NMIP4Config instances, +but as they are mutable, it needs to [subscribe](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/dns/nm-dns-manager.c#L275) +to changes. Later, when a NML3ConfigData instance "changes", it means it was +replaced by a different one and NMDnsManager needs to update its list of tracked NML3ConfigData. I find that +cleaner, because adding and removal to the list of NMIP4Config/NML3ConfigData happens anyway and needs to be handled. + + +Related Bugs +------------ + +* Main bug: + + - [rh#1868254](https://bugzilla.redhat.com/show_bug.cgi?id=1868254): + "refactor NetworkManager's IP configuration done by NMDevice" + +* Follow up but to improve model of devices: + + - [rh#1066703](https://bugzilla.redhat.com/show_bug.cgi?id=1066703): + "\[RFE\] Handle parent/child relationships more cleanly" + +* Flexible IP methods: + + - [rh#1791624](https://bugzilla.redhat.com/show_bug.cgi?id=1791624): + "NetworkManager must not remove used bridge" + +* Improving performance issues, this will lay ground work: + + - [rh#1847125](https://bugzilla.redhat.com/show_bug.cgi?id=1847125): + "\[RFE\] Improve 20% performance on creating 1000 bridge over 1000 VLANs" + + - [rh#1861527](https://bugzilla.redhat.com/show_bug.cgi?id=1861527): + "Excessive memory and CPU usage on a router with IPv6 BGP feed" + + - [rh#1753677](https://bugzilla.redhat.com/show_bug.cgi?id=1753677): + "High cpu usage while non-controlled interface is mangling tc filters" + +TODO +---- + +- Before considering an IP method completely activated, check that all addresses + we want to configure are no longer tentative. + For example, when activating a `connection.type=pppoe`, we might get an IPv6 address + from the pppd daemon. We set that via + `nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, ip_data->l3cd)` + Note that this currently already is sufficient to make the IP method (as far as PPP + is concerned), to be "ready". + We should however wait for IPv6 DAD to complete. See how that is already done + for ipmanual (`_dev_ipmanual_check_ready()`). + +- NMDevicePPP fails to re-activate a profile that is currently active. This is not + new on "next" branch, but also happened before. Have a pppoe profile active, + and issue `nmcli connection up $PROFILE` again. This, as expected, brings down + the device but fails to re-activate it. It's not trivial to fix (I think), because + of the ip-ifindex madness. Check the logs. Test with contrib/scripts/test-ppp.sh + script. Possibly affects other types. + +- NMDevice's ip4_config_pre_commit() had only one effect, to call nm_modem_ip4_pre_commit() + which set IFF_NOARP. That is currently dropped. Find a different way to achieve that. + Theoretically, we still could do that from NML3Cfg's post-commit notification. But + this really should be handled by NML3Cfg. That means, to disable IFF_NOARP needs somehow + configured by NMDeviceModem -- possibly by setting a flag in NML3ConfigData that indicates + to do this. Or maybe NML3Cfg should detect automatically when to set IFF_NOARP. + +- `ipvx.method=auto` usually means autoconf6/DHCPv4, but it doesn't have to. For example + with PPP/VPN it might mean that the IP configuration is provided by pppd/VPN or with + Wi-Fi-P2P it might mean that the controller of a peer runs a DHCP server (shared). + Now the parents implementation activate_stage3_ip_config() can no longer be overwritten + by subclasses, and they always perform their steps. For subclasses to modify what a + method means there is klass->get_ip_method_auto(self(). NMDeviceModem does not yet + implement that, which is most certainly lacking. We need to test at least NMDeviceModem + whether IP methods work correctly in this regard. Possibly other subclasses are affected + too (NMDeviceBluetooth?). diff --git a/src/core/README.next.ip-config.md b/src/core/README.next.ip-config.md new file mode 100644 index 0000000000..e5be74f133 --- /dev/null +++ b/src/core/README.next.ip-config.md @@ -0,0 +1,59 @@ +Rework `NMIP[46]Config` for `next` branch +========================================= + +The `next` branch is a large rework of internals, how IP configuration is done by `NMDevice`. + +Previously, there are two `GObject`s named `NMIP4Config` and `NMIP6Config`. These +serve different purposes: + +1) They are data containers that can track IP configuration. As such, `NMDevice` + and various parts (like `NMDhcpClient`) create them, pass them around and + mutate/merge them to track the IP configuration. + +2) They are also subclasses of `NMDBusObject` and exported on D-Bus as + `/org/freedesktop/NetworkManager/IP4Config/1`, etc. As such, see their + [D-Bus API](../../introspection/org.freedesktop.NetworkManager.IP4Config.xml) + (and [for IPv6](../../introspection/org.freedesktop.NetworkManager.IP6Config.xml)). + +`next` branch will replace use 1) with `NML3ConfigData`. `NML3ConfigData` are immutable +(sealable) data containers with little logic. This leaves `NMIP4Config` to only +implement 2). + +This needs to be reworked. + +* Now `NMIP4Config` and `NMIP6Config` are subclasses of `NMIPConfig`. The goal + is to treat IPv4/IPv6 similar and generically. Probably there should be very + little code in the subclasses left and most should move to the parent classes. + We still need separate GObject types though, because that is how `NMDBusObject`'s + glue code can handle different D-Bus paths. + +* Now `NML3Cfg` is a handle for the IP configuration parameters of a device (ifindex). + As `NMIPConfig` mostly is about exporting the current IP configuration, it probably + can get most of it from there (and by listening to signals to that). + +* Note that `NMDevice`, `NMActiveConnection` refer `NMIP[46]Config`s, and most + importantly, the respective D-Bus objects refer to them. As `NMVpnConnection` + (and "org.freedesktop.NetworkManager.VPN.Connection" interface) are modeled + as "subclasses" of `NMActiveConnection`, they also have one. That means, + it's not entirely clear what these properties even are. For example, currently, + `NMDevice` does a (terrible) dance of tracking external `NMIP[46]Config` objects, + merging, intersecting and subtracting them with other `NMIP4Config` objects + to get the merged one, which is then exported on D-Bus. That merged object + does therefore not directly expose the IP addresses that are actually + configured on the interface (`ip addr`), but more what NetworkManager + wanted to configure and the (terrible) feedback loop where the platform + addresses get synced. With `next` branch and `NML3Cfg` there is a clear distinction + between what NetworkManager wants to configure vs. what is actually configured. + I think for `NMDevice` and `NMActiveConnection`, the IP addresses on + "org.freedesktop.NetworkManager.IP4Config" should expose the IP addresses + that are actually in platform (`ip addr`). If there is a need to expose + additional information (like things that NetworkManager wanted to configure), + then this should be different/new API. + On the other hand, currently `NMVpnConnection`'s `NMIP4Config` only tracks the + IP addresses that come from the VPN plugin. So it's much more what it wants + to configure (from the VPN plugin), and not at all about what is configured + on the interface. + I think that needs to change. A `NMIPConfig` object on D-Bus exposes IP configuration + information about an netdev interface. Period. That also means that a `NMVpnConnection` + (which currently is like a active connection associated with the device) links to + the same `NMIPConfig` object as the underlying device. diff --git a/src/core/devices/adsl/nm-device-adsl.c b/src/core/devices/adsl/nm-device-adsl.c index adcf878523..6133a3d056 100644 --- a/src/core/devices/adsl/nm-device-adsl.c +++ b/src/core/devices/adsl/nm-device-adsl.c @@ -15,13 +15,12 @@ #include #include -#include "nm-ip4-config.h" #include "devices/nm-device-private.h" #include "libnm-platform/nm-platform.h" -#include "ppp/nm-ppp-manager-call.h" -#include "ppp/nm-ppp-status.h" +#include "nm-manager.h" #include "nm-setting-adsl.h" #include "nm-utils.h" +#include "ppp/nm-ppp-mgr.h" #define _NMLOG_DEVICE_TYPE NMDeviceAdsl #include "devices/nm-device-logging.h" @@ -34,15 +33,14 @@ typedef struct { guint carrier_poll_id; int atm_index; - /* PPP */ - NMPPPManager *ppp_manager; + NMPppMgr *ppp_mgr; /* RFC 2684 bridging (PPPoE over ATM) */ - int brfd; - int nas_ifindex; - char *nas_ifname; - guint nas_update_id; - guint nas_update_count; + int brfd; + int nas_ifindex; + char * nas_ifname; + GSource *nas_update_source; + guint nas_update_count; } NMDeviceAdslPrivate; struct _NMDeviceAdsl { @@ -271,7 +269,7 @@ pppoe_vcc_config(NMDeviceAdsl *self) } static gboolean -nas_update_cb(gpointer user_data) +nas_update_timeout_cb(gpointer user_data) { NMDeviceAdsl * self = NM_DEVICE_ADSL(user_data); NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE(self); @@ -284,33 +282,35 @@ nas_update_cb(gpointer user_data) nm_assert(priv->nas_ifindex <= 0); priv->nas_ifindex = nm_platform_link_get_ifindex(nm_device_get_platform(device), priv->nas_ifname); + + if (priv->nas_ifindex <= 0 && priv->nas_update_count <= 10) { + /* Keep waiting for it to appear */ + return G_SOURCE_CONTINUE; + } + + nm_clear_g_source_inst(&priv->nas_update_source); + if (priv->nas_ifindex <= 0) { - if (priv->nas_update_count <= 10) { - /* Keep waiting for it to appear */ - return G_SOURCE_CONTINUE; - } - priv->nas_update_id = 0; _LOGW(LOGD_ADSL, "failed to find br2684 interface %s ifindex after timeout", priv->nas_ifname); nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BR2684_FAILED); - return G_SOURCE_REMOVE; + return G_SOURCE_CONTINUE; } - priv->nas_update_id = 0; _LOGD(LOGD_ADSL, "using br2684 iface '%s' index %d", priv->nas_ifname, priv->nas_ifindex); if (!pppoe_vcc_config(self)) { nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BR2684_FAILED); - return G_SOURCE_REMOVE; + return G_SOURCE_CONTINUE; } nm_device_activate_schedule_stage2_device_config(device, TRUE); - return G_SOURCE_REMOVE; + return G_SOURCE_CONTINUE; } static gboolean @@ -319,11 +319,11 @@ br2684_create_iface(NMDeviceAdsl *self) NMDeviceAdslPrivate * priv = NM_DEVICE_ADSL_GET_PRIVATE(self); struct atm_newif_br2684 ni; nm_auto_close int fd = -1; - int err, errsv; + int err; + int errsv; guint num = 0; - if (nm_clear_g_source(&priv->nas_update_id)) - nm_assert_not_reached(); + nm_assert(!priv->nas_update_source); fd = socket(PF_ATMPVC, SOCK_DGRAM | SOCK_CLOEXEC, ATM_AAL5); if (fd < 0) { @@ -358,174 +358,187 @@ br2684_create_iface(NMDeviceAdsl *self) nm_strdup_reset(&priv->nas_ifname, ni.ifname); _LOGD(LOGD_ADSL, "waiting for br2684 iface '%s' to appear", priv->nas_ifname); - priv->nas_update_count = 0; - priv->nas_update_id = g_timeout_add(100, nas_update_cb, self); + priv->nas_update_count = 0; + priv->nas_update_source = nm_g_timeout_add_source(100, nas_update_timeout_cb, self); return TRUE; } } +/*****************************************************************************/ + +static void +_ppp_mgr_cleanup(NMDeviceAdsl *self) +{ + NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE(self); + + nm_clear_pointer(&priv->ppp_mgr, nm_ppp_mgr_destroy); +} + +static void +_ppp_mgr_stage3_maybe_ready(NMDeviceAdsl *self) +{ + NMDevice * device = NM_DEVICE(self); + NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE(self); + int IS_IPv4; + + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + const int addr_family = IS_IPv4 ? AF_INET : AF_INET6; + const NMPppMgrIPData *ip_data; + + ip_data = nm_ppp_mgr_get_ip_data(priv->ppp_mgr, addr_family); + if (ip_data->ip_received) + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, ip_data->l3cd); + } + + if (nm_ppp_mgr_get_state(priv->ppp_mgr) >= NM_PPP_MGR_STATE_HAVE_IP_CONFIG) + nm_device_devip_set_state(device, AF_UNSPEC, NM_DEVICE_IP_STATE_READY, NULL); +} + +static void +_ppp_mgr_callback(NMPppMgr *ppp_mgr, const NMPppMgrCallbackData *callback_data, gpointer user_data) +{ + NMDeviceAdsl *self = NM_DEVICE_ADSL(user_data); + NMDevice * device = NM_DEVICE(self); + NMDeviceState device_state; + + if (callback_data->callback_type != NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED) + return; + + device_state = nm_device_get_state(device); + + if (callback_data->data.state >= _NM_PPP_MGR_STATE_FAILED_START) { + if (device_state <= NM_DEVICE_STATE_ACTIVATED) + nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, callback_data->data.reason); + return; + } + + if (device_state < NM_DEVICE_STATE_IP_CONFIG) { + if (callback_data->data.state >= NM_PPP_MGR_STATE_HAVE_IFINDEX) { + gs_free char *old_name = NULL; + gs_free_error GError *error = NULL; + + if (!nm_device_take_over_link(device, callback_data->data.ifindex, &old_name, &error)) { + _LOGW(LOGD_DEVICE | LOGD_PPP, + "could not take control of link %d: %s", + callback_data->data.ifindex, + error->message); + _ppp_mgr_cleanup(self); + nm_device_state_changed(device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_CONFIG_FAILED); + return; + } + + if (old_name) + nm_manager_remove_device(NM_MANAGER_GET, old_name, NM_DEVICE_TYPE_ADSL); + + nm_device_activate_schedule_stage2_device_config(device, FALSE); + } + return; + } + + _ppp_mgr_stage3_maybe_ready(self); +} + +/*****************************************************************************/ + static NMActStageReturn act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) { NMDeviceAdsl * self = NM_DEVICE_ADSL(device); NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE(self); - NMSettingAdsl * s_adsl; - const char * protocol; - s_adsl = nm_device_get_applied_setting(device, NM_TYPE_SETTING_ADSL); + if (!priv->ppp_mgr) { + gs_free_error GError *error = NULL; + NMSettingAdsl * s_adsl; + const char * protocol; + NMActRequest * req; + const char * ppp_iface; - g_return_val_if_fail(s_adsl, NM_ACT_STAGE_RETURN_FAILURE); + req = nm_device_get_act_request(device); + g_return_val_if_fail(req, NM_ACT_STAGE_RETURN_FAILURE); - protocol = nm_setting_adsl_get_protocol(s_adsl); - _LOGD(LOGD_ADSL, "using ADSL protocol '%s'", protocol); + s_adsl = nm_device_get_applied_setting(device, NM_TYPE_SETTING_ADSL); + g_return_val_if_fail(s_adsl, NM_ACT_STAGE_RETURN_FAILURE); - if (nm_streq0(protocol, NM_SETTING_ADSL_PROTOCOL_PPPOA)) { - /* PPPoA doesn't need anything special */ - return NM_ACT_STAGE_RETURN_SUCCESS; - } + protocol = nm_setting_adsl_get_protocol(s_adsl); - if (nm_streq0(protocol, NM_SETTING_ADSL_PROTOCOL_PPPOE)) { - /* PPPoE needs RFC2684 bridging before we can do PPP over it */ - if (priv->nas_ifindex <= 0) { - if (priv->nas_update_id == 0) { - if (!br2684_create_iface(self)) { - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_BR2684_FAILED); - return NM_ACT_STAGE_RETURN_FAILURE; + _LOGD(LOGD_ADSL, "using ADSL protocol '%s'", protocol); + + if (nm_streq0(protocol, NM_SETTING_ADSL_PROTOCOL_PPPOA)) { + /* PPPoA doesn't need anything special */ + } else if (nm_streq0(protocol, NM_SETTING_ADSL_PROTOCOL_PPPOE)) { + /* PPPoE needs RFC2684 bridging before we can do PPP over it */ + if (priv->nas_ifindex <= 0) { + if (!priv->nas_update_source) { + if (!br2684_create_iface(self)) { + NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_BR2684_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } } + return NM_ACT_STAGE_RETURN_POSTPONE; } - return NM_ACT_STAGE_RETURN_POSTPONE; + } else + nm_assert(nm_streq0(protocol, NM_SETTING_ADSL_PROTOCOL_IPOATM)); + + /* PPPoE uses the NAS interface, not the ATM interface */ + if (nm_streq0(protocol, NM_SETTING_ADSL_PROTOCOL_PPPOE)) { + nm_assert(priv->nas_ifname); + ppp_iface = priv->nas_ifname; + _LOGD(LOGD_ADSL, "starting PPPoE on br2684 interface %s", priv->nas_ifname); + } else { + ppp_iface = nm_device_get_iface(device); + _LOGD(LOGD_ADSL, "starting PPPoA"); } - return NM_ACT_STAGE_RETURN_SUCCESS; + + priv->ppp_mgr = nm_ppp_mgr_start(&((const NMPppMgrConfig){ + .netns = nm_device_get_netns(device), + .parent_iface = ppp_iface, + .callback = _ppp_mgr_callback, + .user_data = self, + .act_req = req, + .ppp_username = nm_setting_adsl_get_username(s_adsl), + .timeout_secs = 30, + .baud_override = 0, + }), + &error); + if (!priv->ppp_mgr) { + _LOGW(LOGD_DEVICE | LOGD_PPP, "PPPoE failed to start: %s", error->message); + *out_failure_reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED; + return NM_ACT_STAGE_RETURN_FAILURE; + } + + return NM_ACT_STAGE_RETURN_POSTPONE; } - _LOGW(LOGD_ADSL, "unhandled ADSL protocol '%s'", protocol); + if (nm_ppp_mgr_get_state(priv->ppp_mgr) < NM_PPP_MGR_STATE_HAVE_IFINDEX) + return NM_ACT_STAGE_RETURN_POSTPONE; + return NM_ACT_STAGE_RETURN_SUCCESS; } static void -ppp_state_changed(NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data) -{ - NMDevice *device = NM_DEVICE(user_data); - - switch (status) { - case NM_PPP_STATUS_DISCONNECT: - nm_device_state_changed(device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_PPP_DISCONNECT); - break; - case NM_PPP_STATUS_DEAD: - nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED); - break; - default: - break; - } -} - -static void -ppp_ifindex_set(NMPPPManager *ppp_manager, int ifindex, const char *iface, gpointer user_data) -{ - NMDevice *device = NM_DEVICE(user_data); - - if (!nm_device_set_ip_ifindex(device, ifindex)) { - nm_device_state_changed(device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } -} - -static void -ppp_ip4_config(NMPPPManager *ppp_manager, NMIP4Config *config, gpointer user_data) -{ - NMDevice *device = NM_DEVICE(user_data); - - /* Ignore PPP IP4 events that come in after initial configuration */ - if (nm_device_activate_ip4_state_in_conf(device)) - nm_device_activate_schedule_ip_config_result(device, AF_INET, NM_IP_CONFIG_CAST(config)); -} - -static NMActStageReturn -act_stage3_ip4_config_start(NMDevice * device, - NMIP4Config ** out_config, - NMDeviceStateReason *out_failure_reason) +act_stage3_ip_config(NMDevice *device, int addr_family) { NMDeviceAdsl * self = NM_DEVICE_ADSL(device); NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE(self); - NMSettingAdsl * s_adsl; - NMActRequest * req; - GError * err = NULL; - const char * ppp_iface; + NMPppMgrState ppp_state; - req = nm_device_get_act_request(device); - - g_return_val_if_fail(req, NM_ACT_STAGE_RETURN_FAILURE); - - s_adsl = nm_device_get_applied_setting(device, NM_TYPE_SETTING_ADSL); - - g_return_val_if_fail(s_adsl, NM_ACT_STAGE_RETURN_FAILURE); - - /* PPPoE uses the NAS interface, not the ATM interface */ - if (nm_streq0(nm_setting_adsl_get_protocol(s_adsl), NM_SETTING_ADSL_PROTOCOL_PPPOE)) { - nm_assert(priv->nas_ifname); - ppp_iface = priv->nas_ifname; - - _LOGD(LOGD_ADSL, "starting PPPoE on br2684 interface %s", priv->nas_ifname); - } else { - ppp_iface = nm_device_get_iface(device); - _LOGD(LOGD_ADSL, "starting PPPoA"); + if (!priv->ppp_mgr) { + nm_assert_not_reached(); + return; } - priv->ppp_manager = nm_ppp_manager_create(ppp_iface, &err); + ppp_state = nm_ppp_mgr_get_state(priv->ppp_mgr); - if (priv->ppp_manager) { - nm_ppp_manager_set_route_parameters(priv->ppp_manager, - nm_device_get_route_table(device, AF_INET), - nm_device_get_route_metric(device, AF_INET), - nm_device_get_route_table(device, AF_INET6), - nm_device_get_route_metric(device, AF_INET6)); + nm_assert(NM_IN_SET(ppp_state, NM_PPP_MGR_STATE_HAVE_IFINDEX, NM_PPP_MGR_STATE_HAVE_IP_CONFIG)); + + if (ppp_state < NM_PPP_MGR_STATE_HAVE_IP_CONFIG) { + nm_device_devip_set_state(device, AF_UNSPEC, NM_DEVICE_IP_STATE_PENDING, NULL); + return; } - if (!priv->ppp_manager - || !nm_ppp_manager_start(priv->ppp_manager, - req, - nm_setting_adsl_get_username(s_adsl), - 30, - 0, - &err)) { - _LOGW(LOGD_ADSL, "PPP failed to start: %s", err->message); - g_error_free(err); - - g_clear_object(&priv->ppp_manager); - - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - g_signal_connect(priv->ppp_manager, - NM_PPP_MANAGER_SIGNAL_STATE_CHANGED, - G_CALLBACK(ppp_state_changed), - self); - g_signal_connect(priv->ppp_manager, - NM_PPP_MANAGER_SIGNAL_IFINDEX_SET, - G_CALLBACK(ppp_ifindex_set), - self); - g_signal_connect(priv->ppp_manager, - NM_PPP_MANAGER_SIGNAL_IP4_CONFIG, - G_CALLBACK(ppp_ip4_config), - self); - return NM_ACT_STAGE_RETURN_POSTPONE; -} - -static NMActStageReturn -act_stage3_ip_config_start(NMDevice * device, - int addr_family, - gpointer * out_config, - NMDeviceStateReason *out_failure_reason) -{ - if (addr_family == AF_INET) - return act_stage3_ip4_config_start(device, (NMIP4Config **) out_config, out_failure_reason); - - return NM_DEVICE_CLASS(nm_device_adsl_parent_class) - ->act_stage3_ip_config_start(device, addr_family, out_config, out_failure_reason); + _ppp_mgr_stage3_maybe_ready(self); } static void @@ -533,23 +546,15 @@ adsl_cleanup(NMDeviceAdsl *self) { NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE(self); - if (priv->ppp_manager) { - g_signal_handlers_disconnect_by_func(priv->ppp_manager, - G_CALLBACK(ppp_state_changed), - self); - g_signal_handlers_disconnect_by_func(priv->ppp_manager, G_CALLBACK(ppp_ip4_config), self); - nm_ppp_manager_stop(priv->ppp_manager, NULL, NULL, NULL); - g_clear_object(&priv->ppp_manager); - } + _ppp_mgr_cleanup(self); g_signal_handlers_disconnect_by_func(nm_device_get_platform(NM_DEVICE(self)), G_CALLBACK(link_changed_cb), self); - nm_close(priv->brfd); - priv->brfd = -1; + nm_clear_fd(&priv->brfd); - nm_clear_g_source(&priv->nas_update_id); + nm_clear_g_source_inst(&priv->nas_update_source); /* FIXME: kernel has no way of explicitly deleting the 'nasX' interface yet, * so it gets leaked. It does get destroyed when it's no longer in use, @@ -699,9 +704,9 @@ nm_device_adsl_class_init(NMDeviceAdslClass *klass) device_class->check_connection_compatible = check_connection_compatible; device_class->complete_connection = complete_connection; - device_class->act_stage2_config = act_stage2_config; - device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; - device_class->deactivate = deactivate; + device_class->act_stage2_config = act_stage2_config; + device_class->act_stage3_ip_config = act_stage3_ip_config; + device_class->deactivate = deactivate; obj_properties[PROP_ATM_INDEX] = g_param_spec_int(NM_DEVICE_ADSL_ATM_INDEX, diff --git a/src/core/devices/bluetooth/nm-device-bt.c b/src/core/devices/bluetooth/nm-device-bt.c index 3dbfbbe06e..5346aaed62 100644 --- a/src/core/devices/bluetooth/nm-device-bt.c +++ b/src/core/devices/bluetooth/nm-device-bt.c @@ -26,7 +26,6 @@ #include "settings/nm-settings-connection.h" #include "nm-utils.h" #include "nm-bt-error.h" -#include "nm-ip4-config.h" #include "libnm-platform/nm-platform.h" #include "devices/wwan/nm-modem-manager.h" @@ -425,43 +424,9 @@ static void ppp_failed(NMModem *modem, guint i_reason, gpointer user_data) { NMDevice * device = NM_DEVICE(user_data); - NMDeviceBt * self = NM_DEVICE_BT(user_data); NMDeviceStateReason reason = i_reason; - switch (nm_device_get_state(device)) { - case NM_DEVICE_STATE_PREPARE: - case NM_DEVICE_STATE_CONFIG: - case NM_DEVICE_STATE_NEED_AUTH: - nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, reason); - break; - case NM_DEVICE_STATE_IP_CONFIG: - case NM_DEVICE_STATE_IP_CHECK: - case NM_DEVICE_STATE_SECONDARIES: - case NM_DEVICE_STATE_ACTIVATED: - if (nm_device_activate_ip4_state_in_conf(device)) - nm_device_activate_schedule_ip_config_timeout(device, AF_INET); - else if (nm_device_activate_ip6_state_in_conf(device)) - nm_device_activate_schedule_ip_config_timeout(device, AF_INET6); - else if (nm_device_activate_ip4_state_done(device)) { - nm_device_ip_method_failed(device, - AF_INET, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } else if (nm_device_activate_ip6_state_done(device)) { - nm_device_ip_method_failed(device, - AF_INET6, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } else { - _LOGW(LOGD_MB, - "PPP failure in unexpected state %u", - (guint) nm_device_get_state(device)); - nm_device_state_changed(device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } - break; - default: - break; - } + nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, reason); } static void @@ -547,22 +512,35 @@ device_state_changed(NMDevice * device, } static void -modem_ip4_config_result(NMModem *modem, NMIP4Config *config, GError *error, gpointer user_data) +modem_new_config(NMModem * modem, + int addr_family, + const NML3ConfigData * l3cd, + gboolean do_auto, + const NMUtilsIPv6IfaceId *iid, + int failure_reason_i, + GError * error, + gpointer user_data) { - NMDeviceBt *self = NM_DEVICE_BT(user_data); - NMDevice * device = NM_DEVICE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDeviceBt *self = NM_DEVICE_BT(user_data); + NMDevice * device = NM_DEVICE(self); - g_return_if_fail(nm_device_activate_ip4_state_in_conf(device) == TRUE); + g_return_if_fail(nm_device_devip_get_state(device, addr_family) == NM_DEVICE_IP_STATE_PENDING); if (error) { - _LOGW(LOGD_MB | LOGD_IP4 | LOGD_BT, - "retrieving IP4 configuration failed: %s", - error->message); - nm_device_ip_method_failed(device, AF_INET, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + _LOGW(LOGD_MB | LOGD_IP4, "retrieving IP configuration failed: %s", error->message); + nm_device_devip_set_failed(device, addr_family, failure_reason_i); return; } - nm_device_activate_schedule_ip_config_result(device, AF_INET, NM_IP_CONFIG_CAST(config)); + if (do_auto) { + if (IS_IPv4) + nm_device_ip_method_dhcp4_start(device); + else + nm_device_ip_method_autoconf6_start(device); + } + + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, l3cd); } static void @@ -687,7 +665,7 @@ modem_try_claim(NMDeviceBt *self, NMModem *modem) g_signal_connect(modem, NM_MODEM_PPP_STATS, G_CALLBACK(ppp_stats), self); g_signal_connect(modem, NM_MODEM_PPP_FAILED, G_CALLBACK(ppp_failed), self); g_signal_connect(modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK(modem_prepare_result), self); - g_signal_connect(modem, NM_MODEM_IP4_CONFIG_RESULT, G_CALLBACK(modem_ip4_config_result), self); + g_signal_connect(modem, NM_MODEM_NEW_CONFIG, G_CALLBACK(modem_new_config), self); g_signal_connect(modem, NM_MODEM_AUTH_REQUESTED, G_CALLBACK(modem_auth_requested), self); g_signal_connect(modem, NM_MODEM_AUTH_RESULT, G_CALLBACK(modem_auth_result), self); g_signal_connect(modem, NM_MODEM_STATE_CHANGED, G_CALLBACK(modem_state_cb), self); @@ -992,35 +970,22 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) NMDeviceBt * self = NM_DEVICE_BT(device); NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE(self); - if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN) - nm_modem_act_stage2_config(priv->modem); + if (priv->connect_bt_type != NM_BT_CAPABILITY_DUN) + return NM_ACT_STAGE_RETURN_SUCCESS; - return NM_ACT_STAGE_RETURN_SUCCESS; + return nm_modem_act_stage2_config(priv->modem, device, out_failure_reason); } -static NMActStageReturn -act_stage3_ip_config_start(NMDevice * device, - int addr_family, - gpointer * out_config, - NMDeviceStateReason *out_failure_reason) +static void +act_stage3_ip_config(NMDevice *device, int addr_family) { - NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE(device); - gboolean autoip4 = FALSE; - NMActStageReturn ret; + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE(device); if (priv->connect_bt_type != NM_BT_CAPABILITY_DUN) - goto out_chain_up; + return; - if (!NM_IS_IPv4(addr_family)) - return nm_modem_stage3_ip6_config_start(priv->modem, device, out_failure_reason); - - ret = nm_modem_stage3_ip4_config_start(priv->modem, device, &autoip4, out_failure_reason); - if (ret != NM_ACT_STAGE_RETURN_SUCCESS || !autoip4) - return ret; - -out_chain_up: - return NM_DEVICE_CLASS(nm_device_bt_parent_class) - ->act_stage3_ip_config_start(device, addr_family, out_config, out_failure_reason); + if (nm_modem_stage3_ip_config_start(priv->modem, addr_family, device)) + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_PENDING, NULL); } static void @@ -1344,7 +1309,7 @@ nm_device_bt_class_init(NMDeviceBtClass *klass) device_class->deactivate = deactivate; device_class->act_stage1_prepare = act_stage1_prepare; device_class->act_stage2_config = act_stage2_config; - device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + device_class->act_stage3_ip_config = act_stage3_ip_config; device_class->check_connection_compatible = check_connection_compatible; device_class->check_connection_available = check_connection_available; device_class->complete_connection = complete_connection; diff --git a/src/core/devices/nm-acd-manager.c b/src/core/devices/nm-acd-manager.c deleted file mode 100644 index c041163ef8..0000000000 --- a/src/core/devices/nm-acd-manager.c +++ /dev/null @@ -1,497 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2015 - 2018 Red Hat, Inc. - */ - -#include "src/core/nm-default-daemon.h" - -#include "nm-acd-manager.h" - -#include -#include -#include -#include - -#include "libnm-platform/nm-platform.h" -#include "nm-utils.h" -#include "NetworkManagerUtils.h" -#include "n-acd/src/n-acd.h" - -/*****************************************************************************/ - -typedef enum { - STATE_INIT, - STATE_PROBING, - STATE_PROBE_DONE, - STATE_ANNOUNCING, -} State; - -typedef struct { - in_addr_t address; - gboolean duplicate; - NAcdProbe *probe; -} AddressInfo; - -struct _NMAcdManager { - int ifindex; - guint8 hwaddr[ETH_ALEN]; - State state; - GHashTable *addresses; - guint completed; - NAcd * acd; - GSource * event_source; - - NMAcdCallbacks callbacks; - gpointer user_data; -}; - -/*****************************************************************************/ - -#define _NMLOG_DOMAIN LOGD_IP4 -#define _NMLOG_PREFIX_NAME "acd" -#define _NMLOG(level, ...) \ - G_STMT_START \ - { \ - char _sbuf[64]; \ - \ - nm_log((level), \ - _NMLOG_DOMAIN, \ - self && self->ifindex > 0 \ - ? nm_platform_link_get_name(NM_PLATFORM_GET, self->ifindex) \ - : NULL, \ - NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - self ? nm_sprintf_buf(_sbuf, "[%p,%d]", self, self->ifindex) \ - : "" _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - G_STMT_END - -/*****************************************************************************/ - -static const char * -_acd_event_to_string(unsigned int event) -{ - switch (event) { - case N_ACD_EVENT_READY: - return "ready"; - case N_ACD_EVENT_USED: - return "used"; - case N_ACD_EVENT_DEFENDED: - return "defended"; - case N_ACD_EVENT_CONFLICT: - return "conflict"; - case N_ACD_EVENT_DOWN: - return "down"; - } - return NULL; -} - -#define ACD_EVENT_TO_STRING_BUF_SIZE 50 - -static const char * -_acd_event_to_string_buf(unsigned event, char buffer[static ACD_EVENT_TO_STRING_BUF_SIZE]) -{ - const char *s; - - s = _acd_event_to_string(event); - if (s) - return s; - - g_snprintf(buffer, ACD_EVENT_TO_STRING_BUF_SIZE, "(%u)", event); - return buffer; -} - -static const char * -acd_error_to_string(int error) -{ - if (error < 0) - return nm_strerror_native(-error); - - switch (error) { - case _N_ACD_E_SUCCESS: - return "success"; - case N_ACD_E_PREEMPTED: - return "preempted"; - case N_ACD_E_INVALID_ARGUMENT: - return "invalid argument"; - } - - g_return_val_if_reached(NULL); -} - -static int -acd_error_to_nmerr(int error, gboolean always_fail) -{ - if (error < 0) - return -nm_errno_native(error); - - if (always_fail) { - if (NM_IN_SET(error, N_ACD_E_PREEMPTED, N_ACD_E_INVALID_ARGUMENT)) - return -NME_UNSPEC; - g_return_val_if_reached(-NME_UNSPEC); - } - - /* so, @error is either zero (indicating success) or one - * of the special status codes like N_ACD_E_*. In both cases, - * return the positive value here. */ - if (NM_IN_SET(error, _N_ACD_E_SUCCESS, N_ACD_E_PREEMPTED, N_ACD_E_INVALID_ARGUMENT)) - return error; - - g_return_val_if_reached(error); -} - -/*****************************************************************************/ - -/** - * nm_acd_manager_add_address: - * @self: a #NMAcdManager - * @address: an IP address - * - * Add @address to the list of IP addresses to probe. - - * Returns: %TRUE on success, %FALSE if the address was already in the list - */ -gboolean -nm_acd_manager_add_address(NMAcdManager *self, in_addr_t address) -{ - AddressInfo *info; - - g_return_val_if_fail(self, FALSE); - g_return_val_if_fail(self->state == STATE_INIT, FALSE); - - if (g_hash_table_lookup(self->addresses, GUINT_TO_POINTER(address))) - return FALSE; - - info = g_slice_new0(AddressInfo); - info->address = address; - - g_hash_table_insert(self->addresses, GUINT_TO_POINTER(address), info); - - return TRUE; -} - -static gboolean -acd_event(int fd, GIOCondition condition, gpointer data) -{ - NMAcdManager *self = data; - NAcdEvent * event; - AddressInfo * info; - gboolean emit_probe_terminated = FALSE; - char address_str[INET_ADDRSTRLEN]; - int r; - - if (n_acd_dispatch(self->acd)) - return G_SOURCE_CONTINUE; - - while (!n_acd_pop_event(self->acd, &event) && event) { - char to_string_buffer[ACD_EVENT_TO_STRING_BUF_SIZE]; - gs_free char *hwaddr_str = NULL; - gboolean check_probing_done = FALSE; - char buf[ETH_ALEN * 3]; - - switch (event->event) { - case N_ACD_EVENT_READY: - n_acd_probe_get_userdata(event->ready.probe, (void **) &info); - info->duplicate = FALSE; - if (self->state == STATE_ANNOUNCING) { - /* fake probe ended, start announcing */ - r = n_acd_probe_announce(info->probe, N_ACD_DEFEND_ONCE); - if (r) { - _LOGW("couldn't announce address %s on interface '%s': %s", - _nm_utils_inet4_ntop(info->address, address_str), - nm_platform_link_get_name(NM_PLATFORM_GET, self->ifindex), - acd_error_to_string(r)); - } else { - _LOGD("announcing address %s (hw-addr %s)", - _nm_utils_inet4_ntop(info->address, address_str), - _nm_utils_hwaddr_ntoa(self->hwaddr, ETH_ALEN, TRUE, buf, sizeof(buf))); - } - } - check_probing_done = TRUE; - break; - case N_ACD_EVENT_USED: - n_acd_probe_get_userdata(event->used.probe, (void **) &info); - info->duplicate = TRUE; - check_probing_done = TRUE; - break; - case N_ACD_EVENT_DEFENDED: - n_acd_probe_get_userdata(event->defended.probe, (void **) &info); - _LOGD("defended address %s from host %s", - _nm_utils_inet4_ntop(info->address, address_str), - (hwaddr_str = - nm_utils_hwaddr_ntoa(event->defended.sender, event->defended.n_sender))); - break; - case N_ACD_EVENT_CONFLICT: - n_acd_probe_get_userdata(event->conflict.probe, (void **) &info); - _LOGW("conflict for address %s detected with host %s on interface '%s'", - _nm_utils_inet4_ntop(info->address, address_str), - (hwaddr_str = - nm_utils_hwaddr_ntoa(event->defended.sender, event->defended.n_sender)), - nm_platform_link_get_name(NM_PLATFORM_GET, self->ifindex)); - break; - default: - _LOGD("unhandled event '%s'", _acd_event_to_string_buf(event->event, to_string_buffer)); - break; - } - - if (check_probing_done && self->state == STATE_PROBING - && ++self->completed == g_hash_table_size(self->addresses)) { - self->state = STATE_PROBE_DONE; - emit_probe_terminated = TRUE; - } - } - - if (emit_probe_terminated) { - if (self->callbacks.probe_terminated_callback) { - self->callbacks.probe_terminated_callback(self, self->user_data); - } - } - - return G_SOURCE_CONTINUE; -} - -static gboolean -acd_probe_add(NMAcdManager *self, AddressInfo *info, guint64 timeout) -{ - NAcdProbeConfig *probe_config; - int r; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - r = n_acd_probe_config_new(&probe_config); - if (r) { - _LOGW("could not create probe config for %s on interface '%s': %s", - _nm_utils_inet4_ntop(info->address, sbuf), - nm_platform_link_get_name(NM_PLATFORM_GET, self->ifindex), - acd_error_to_string(r)); - return FALSE; - } - - n_acd_probe_config_set_ip(probe_config, (struct in_addr){info->address}); - n_acd_probe_config_set_timeout(probe_config, timeout); - - r = n_acd_probe(self->acd, &info->probe, probe_config); - if (r) { - _LOGW("could not start probe for %s on interface '%s': %s", - _nm_utils_inet4_ntop(info->address, sbuf), - nm_platform_link_get_name(NM_PLATFORM_GET, self->ifindex), - acd_error_to_string(r)); - n_acd_probe_config_free(probe_config); - return FALSE; - } - - n_acd_probe_set_userdata(info->probe, info); - n_acd_probe_config_free(probe_config); - - return TRUE; -} - -static int -acd_init(NMAcdManager *self) -{ - NAcdConfig *config; - int r; - - if (self->acd) - return 0; - - r = n_acd_config_new(&config); - if (r) - return r; - - n_acd_config_set_ifindex(config, self->ifindex); - n_acd_config_set_transport(config, N_ACD_TRANSPORT_ETHERNET); - n_acd_config_set_mac(config, self->hwaddr, ETH_ALEN); - - r = n_acd_new(&self->acd, config); - n_acd_config_free(config); - return r; -} - -/** - * nm_acd_manager_start_probe: - * @self: a #NMAcdManager - * @timeout: maximum probe duration in milliseconds - * @error: location to store error, or %NULL - * - * Start probing IP addresses for duplicates; when the probe terminates a - * PROBE_TERMINATED signal is emitted. - * - * Returns: 0 on success or a negative NetworkManager error code (NME_*). - */ -int -nm_acd_manager_start_probe(NMAcdManager *self, guint timeout) -{ - GHashTableIter iter; - AddressInfo * info; - gboolean success = FALSE; - int fd, r; - - g_return_val_if_fail(self, FALSE); - g_return_val_if_fail(self->state == STATE_INIT, FALSE); - - r = acd_init(self); - if (r) { - _LOGW("couldn't init ACD for probing on interface '%s': %s", - nm_platform_link_get_name(NM_PLATFORM_GET, self->ifindex), - acd_error_to_string(r)); - return acd_error_to_nmerr(r, TRUE); - } - - self->completed = 0; - - g_hash_table_iter_init(&iter, self->addresses); - while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &info)) - success |= acd_probe_add(self, info, timeout); - - if (success) - self->state = STATE_PROBING; - - nm_assert(!self->event_source); - n_acd_get_fd(self->acd, &fd); - self->event_source = nm_g_unix_fd_add_source(fd, G_IO_IN, acd_event, self); - - return success ? 0 : -NME_UNSPEC; -} - -/** - * nm_acd_manager_check_address: - * @self: a #NMAcdManager - * @address: an IP address - * - * Check if an IP address is duplicate. @address must have been added with - * nm_acd_manager_add_address(). - * - * Returns: %TRUE if the address is not duplicate, %FALSE otherwise - */ -gboolean -nm_acd_manager_check_address(NMAcdManager *self, in_addr_t address) -{ - AddressInfo *info; - - g_return_val_if_fail(self, FALSE); - g_return_val_if_fail(NM_IN_SET(self->state, STATE_INIT, STATE_PROBE_DONE), FALSE); - - info = g_hash_table_lookup(self->addresses, GUINT_TO_POINTER(address)); - g_return_val_if_fail(info, FALSE); - - return !info->duplicate; -} - -/** - * nm_acd_manager_announce_addresses: - * @self: a #NMAcdManager - * - * Start announcing addresses. - * - * Returns: a negative NetworkManager error number or zero on success. - */ -int -nm_acd_manager_announce_addresses(NMAcdManager *self) -{ - GHashTableIter iter; - AddressInfo * info; - int r; - int fd; - gboolean success = TRUE; - char buf[ETH_ALEN * 3]; - - r = acd_init(self); - if (r) { - _LOGW("couldn't init ACD for announcing addresses on interface '%s': %s", - nm_platform_link_get_name(NM_PLATFORM_GET, self->ifindex), - acd_error_to_string(r)); - return acd_error_to_nmerr(r, TRUE); - } - - if (self->state == STATE_INIT) { - /* n-acd can't announce without probing, therefore let's - * start a fake probe with zero timeout and then perform - * the announcement. */ - g_hash_table_iter_init(&iter, self->addresses); - while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &info)) { - if (!acd_probe_add(self, info, 0)) - success = FALSE; - } - self->state = STATE_ANNOUNCING; - } else if (self->state == STATE_ANNOUNCING) { - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - g_hash_table_iter_init(&iter, self->addresses); - while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &info)) { - if (info->duplicate) - continue; - r = n_acd_probe_announce(info->probe, N_ACD_DEFEND_ONCE); - if (r) { - _LOGW("couldn't announce address %s on interface '%s': %s", - _nm_utils_inet4_ntop(info->address, sbuf), - nm_platform_link_get_name(NM_PLATFORM_GET, self->ifindex), - acd_error_to_string(r)); - success = FALSE; - } else - _LOGD("announcing address %s (hw-addr %s)", - _nm_utils_inet4_ntop(info->address, sbuf), - _nm_utils_hwaddr_ntoa(self->hwaddr, ETH_ALEN, TRUE, buf, sizeof(buf))); - } - } - - if (!self->event_source) { - n_acd_get_fd(self->acd, &fd); - self->event_source = nm_g_unix_fd_add_source(fd, G_IO_IN, acd_event, self); - } - - return success ? 0 : -NME_UNSPEC; -} - -static void -destroy_address_info(gpointer data) -{ - AddressInfo *info = (AddressInfo *) data; - - n_acd_probe_free(info->probe); - - g_slice_free(AddressInfo, info); -} - -/*****************************************************************************/ - -NMAcdManager * -nm_acd_manager_new(int ifindex, - const guint8 * hwaddr, - guint hwaddr_len, - const NMAcdCallbacks *callbacks, - gpointer user_data) -{ - NMAcdManager *self; - - g_return_val_if_fail(ifindex > 0, NULL); - g_return_val_if_fail(hwaddr, NULL); - g_return_val_if_fail(hwaddr_len == ETH_ALEN, NULL); - - self = g_slice_new0(NMAcdManager); - - if (callbacks) - self->callbacks = *callbacks; - self->user_data = user_data; - - self->addresses = g_hash_table_new_full(nm_direct_hash, NULL, NULL, destroy_address_info); - self->state = STATE_INIT; - self->ifindex = ifindex; - memcpy(self->hwaddr, hwaddr, ETH_ALEN); - return self; -} - -void -nm_acd_manager_free(NMAcdManager *self) -{ - g_return_if_fail(self); - - if (self->callbacks.user_data_destroy) - self->callbacks.user_data_destroy(self->user_data); - - nm_clear_pointer(&self->addresses, g_hash_table_destroy); - nm_clear_g_source_inst(&self->event_source); - nm_clear_pointer(&self->acd, n_acd_unref); - - g_slice_free(NMAcdManager, self); -} diff --git a/src/core/devices/nm-acd-manager.h b/src/core/devices/nm-acd-manager.h deleted file mode 100644 index e8ef6f2b99..0000000000 --- a/src/core/devices/nm-acd-manager.h +++ /dev/null @@ -1,34 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2015 - 2018 Red Hat, Inc. - */ - -#ifndef __NM_ACD_MANAGER__ -#define __NM_ACD_MANAGER__ - -#include - -typedef struct _NMAcdManager NMAcdManager; - -typedef struct { - void (*probe_terminated_callback)(NMAcdManager *self, gpointer user_data); - GDestroyNotify user_data_destroy; -} NMAcdCallbacks; - -NMAcdManager *nm_acd_manager_new(int ifindex, - const guint8 * hwaddr, - guint hwaddr_len, - const NMAcdCallbacks *callbacks, - gpointer user_data); - -void nm_acd_manager_free(NMAcdManager *self); - -gboolean nm_acd_manager_add_address(NMAcdManager *self, in_addr_t address); -int nm_acd_manager_start_probe(NMAcdManager *self, guint timeout); -gboolean nm_acd_manager_check_address(NMAcdManager *self, in_addr_t address); -int nm_acd_manager_announce_addresses(NMAcdManager *self); - -NM_AUTO_DEFINE_FCN0(NMAcdManager *, _nm_auto_free_acdmgr, nm_acd_manager_free); -#define nm_auto_free_acdmgr nm_auto(_nm_auto_free_acdmgr) - -#endif /* __NM_ACD_MANAGER__ */ diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c index f7d78f57b7..3e7fd0ee18 100644 --- a/src/core/devices/nm-device-bond.c +++ b/src/core/devices/nm-device-bond.c @@ -16,7 +16,6 @@ #include "nm-device-factory.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h" #include "libnm-core-intern/nm-core-internal.h" -#include "nm-ip4-config.h" #include "nm-setting-bond-port.h" #define _NMLOG_DEVICE_TYPE NMDeviceBond diff --git a/src/core/devices/nm-device-dummy.c b/src/core/devices/nm-device-dummy.c index 4c12648fc1..60ac524988 100644 --- a/src/core/devices/nm-device-dummy.c +++ b/src/core/devices/nm-device-dummy.c @@ -12,7 +12,6 @@ #include "nm-act-request.h" #include "nm-device-private.h" -#include "nm-ip4-config.h" #include "libnm-platform/nm-platform.h" #include "nm-device-factory.h" #include "nm-setting-dummy.h" diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c index 667ea9b927..52fce56943 100644 --- a/src/core/devices/nm-device-ethernet.c +++ b/src/core/devices/nm-device-ethernet.c @@ -14,38 +14,36 @@ #include #include -#include "libnm-glib-aux/nm-uuid.h" -#include "nm-device-private.h" -#include "nm-act-request.h" -#include "nm-ip4-config.h" #include "NetworkManagerUtils.h" -#include "supplicant/nm-supplicant-manager.h" -#include "supplicant/nm-supplicant-interface.h" -#include "supplicant/nm-supplicant-config.h" -#include "ppp/nm-ppp-manager.h" -#include "ppp/nm-ppp-manager-call.h" -#include "ppp/nm-ppp-status.h" -#include "libnm-platform/nm-platform.h" -#include "libnm-platform/nm-platform-utils.h" -#include "nm-dcb.h" -#include "settings/nm-settings-connection.h" -#include "nm-config.h" -#include "nm-device-ethernet-utils.h" -#include "settings/nm-settings.h" -#include "nm-device-factory.h" +#include "NetworkManagerUtils.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h" #include "libnm-core-intern/nm-core-internal.h" -#include "NetworkManagerUtils.h" +#include "libnm-glib-aux/nm-uuid.h" +#include "libnm-platform/nm-platform-utils.h" +#include "libnm-platform/nm-platform.h" #include "libnm-udev-aux/nm-udev-utils.h" +#include "nm-act-request.h" +#include "nm-config.h" +#include "nm-dcb.h" +#include "nm-device-ethernet-utils.h" +#include "nm-device-factory.h" +#include "nm-device-private.h" #include "nm-device-veth.h" +#include "nm-manager.h" +#include "ppp/nm-ppp-mgr.h" +#include "settings/nm-settings-connection.h" +#include "settings/nm-settings.h" +#include "supplicant/nm-supplicant-config.h" +#include "supplicant/nm-supplicant-interface.h" +#include "supplicant/nm-supplicant-manager.h" #define _NMLOG_DEVICE_TYPE NMDeviceEthernet #include "nm-device-logging.h" /*****************************************************************************/ -#define PPPOE_RECONNECT_DELAY 7 -#define PPPOE_ENCAP_OVERHEAD 8 /* 2 bytes for PPP, 6 for PPPoE */ +#define PPPOE_RECONNECT_DELAY_MSEC 7000 +#define PPPOE_ENCAP_OVERHEAD 8 /* 2 bytes for PPP, 6 for PPPoE */ #define SUPPLICANT_LNK_TIMEOUT_SEC 15 @@ -91,14 +89,16 @@ typedef struct _NMDeviceEthernetPrivate { guint lnk_timeout_id; bool is_associated : 1; + bool ready : 1; } supplicant; NMActRequestGetSecretsCallId *wired_secrets_id; - /* PPPoE */ - NMPPPManager *ppp_manager; - gint32 last_pppoe_time; - guint pppoe_wait_id; + struct { + NMPppMgr *ppp_mgr; + GSource * wait_source; + gint64 last_pppoe_time_msec; + } ppp_data; /* DCB */ DcbWait dcb_wait; @@ -113,6 +113,8 @@ typedef struct _NMDeviceEthernetPrivate { bool ethtool_prev_set : 1; bool ethtool_prev_autoneg : 1; + bool stage2_ready_dcb : 1; + } NMDeviceEthernetPrivate; NM_GOBJECT_PROPERTIES_DEFINE(NMDeviceEthernet, PROP_SPEED, PROP_S390_SUBCHANNELS, ); @@ -428,6 +430,7 @@ supplicant_interface_release(NMDeviceEthernet *self) nm_clear_g_source(&priv->supplicant.con_timeout_id); nm_clear_g_signal_handler(priv->supplicant.iface, &priv->supplicant.iface_state_id); nm_clear_g_signal_handler(priv->supplicant.iface, &priv->supplicant.auth_state_id); + priv->supplicant.ready = FALSE; if (priv->supplicant.iface) { nm_supplicant_interface_disconnect(priv->supplicant.iface); @@ -468,27 +471,25 @@ wired_auth_cond_fail(NMDeviceEthernet *self, NMDeviceStateReason reason) NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE(self); NMDevice * device = NM_DEVICE(self); - if (wired_auth_is_optional(self)) { - _LOGI( - LOGD_DEVICE | LOGD_ETHER, - "Activation: (ethernet) 802.1X authentication is optional, continuing after a failure"); - if (NM_IN_SET(nm_device_get_state(device), - NM_DEVICE_STATE_CONFIG, - NM_DEVICE_STATE_NEED_AUTH)) - nm_device_activate_schedule_stage3_ip_config_start(device); - - if (!priv->supplicant.auth_state_id) { - priv->supplicant.auth_state_id = - g_signal_connect(priv->supplicant.iface, - "notify::" NM_SUPPLICANT_INTERFACE_AUTH_STATE, - G_CALLBACK(supplicant_auth_state_changed), - self); - } + if (!wired_auth_is_optional(self)) { + supplicant_interface_release(self); + nm_device_state_changed(NM_DEVICE(self), NM_DEVICE_STATE_FAILED, reason); return; } - supplicant_interface_release(self); - nm_device_state_changed(NM_DEVICE(self), NM_DEVICE_STATE_FAILED, reason); + _LOGI(LOGD_DEVICE | LOGD_ETHER, + "Activation: (ethernet) 802.1X authentication is optional, continuing after a failure"); + + if (NM_IN_SET(nm_device_get_state(device), NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_NEED_AUTH)) + nm_device_activate_schedule_stage2_device_config(device, FALSE); + + if (!priv->supplicant.auth_state_id) { + priv->supplicant.auth_state_id = + g_signal_connect(priv->supplicant.iface, + "notify::" NM_SUPPLICANT_INTERFACE_AUTH_STATE, + G_CALLBACK(supplicant_auth_state_changed), + self); + } } static void @@ -644,14 +645,15 @@ supplicant_iface_state_is_completed(NMDeviceEthernet *self, NMSupplicantInterfac if (state == NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) { nm_clear_g_source(&priv->supplicant.lnk_timeout_id); nm_clear_g_source(&priv->supplicant.con_timeout_id); + priv->supplicant.ready = TRUE; /* If this is the initial association during device activation, - * schedule the next activation stage. + * schedule the activation stage again to proceed. */ if (nm_device_get_state(NM_DEVICE(self)) == NM_DEVICE_STATE_CONFIG) { _LOGI(LOGD_DEVICE | LOGD_ETHER, "Activation: (ethernet) Stage 2 of 5 (Device Configure) successful."); - nm_device_activate_schedule_stage3_ip_config_start(NM_DEVICE(self)); + nm_device_activate_schedule_stage2_device_config(NM_DEVICE(self), FALSE); } return; } @@ -969,11 +971,11 @@ pppoe_reconnect_delay(gpointer user_data) NMDeviceEthernet * self = NM_DEVICE_ETHERNET(user_data); NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE(self); - priv->pppoe_wait_id = 0; - priv->last_pppoe_time = 0; + nm_clear_g_source_inst(&priv->ppp_data.wait_source); + priv->ppp_data.last_pppoe_time_msec = 0; _LOGI(LOGD_DEVICE, "PPPoE reconnect delay complete, resuming connection..."); nm_device_activate_schedule_stage1_device_prepare(NM_DEVICE(self), FALSE); - return G_SOURCE_REMOVE; + return G_SOURCE_CONTINUE; } static NMActStageReturn @@ -1014,21 +1016,24 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) * at least for additional NM_SHUTDOWN_TIMEOUT_MS seconds because * otherwise after restart the device won't work for the first seconds. */ - if (priv->last_pppoe_time != 0) { - gint32 delay = nm_utils_get_monotonic_timestamp_sec() - priv->last_pppoe_time; + if (priv->ppp_data.last_pppoe_time_msec != 0) { + gint64 delay = + nm_utils_get_monotonic_timestamp_msec() - priv->ppp_data.last_pppoe_time_msec; - if (delay < PPPOE_RECONNECT_DELAY + if (delay < PPPOE_RECONNECT_DELAY_MSEC && nm_device_get_applied_setting(device, NM_TYPE_SETTING_PPPOE)) { - if (priv->pppoe_wait_id == 0) { + if (!priv->ppp_data.wait_source) { _LOGI(LOGD_DEVICE, - "delaying PPPoE reconnect for %d seconds to ensure peer is ready...", - delay); - priv->pppoe_wait_id = g_timeout_add_seconds(delay, pppoe_reconnect_delay, self); + "delaying PPPoE reconnect for %d.%03d seconds to ensure peer is ready...", + (int) (delay / 1000), + (int) (delay % 1000)); + priv->ppp_data.wait_source = + nm_g_timeout_add_source(delay, pppoe_reconnect_delay, self); } return NM_ACT_STAGE_RETURN_POSTPONE; } - nm_clear_g_source(&priv->pppoe_wait_id); - priv->last_pppoe_time = 0; + nm_clear_g_source_inst(&priv->ppp_data.wait_source); + priv->ppp_data.last_pppoe_time_msec = 0; } return NM_ACT_STAGE_RETURN_SUCCESS; @@ -1105,105 +1110,79 @@ carrier_changed(NMSupplicantInterface *iface, GParamSpec *pspec, NMDeviceEtherne } /*****************************************************************************/ -/* PPPoE */ static void -ppp_state_changed(NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data) +_ppp_mgr_cleanup(NMDeviceEthernet *self) { - NMDevice *device = NM_DEVICE(user_data); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE(self); - switch (status) { - case NM_PPP_STATUS_DISCONNECT: - nm_device_state_changed(device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_PPP_DISCONNECT); - break; - case NM_PPP_STATUS_DEAD: - nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED); - break; - default: - break; - } + nm_clear_pointer(&priv->ppp_data.ppp_mgr, nm_ppp_mgr_destroy); } static void -ppp_ifindex_set(NMPPPManager *ppp_manager, int ifindex, const char *iface, gpointer user_data) -{ - NMDevice *device = NM_DEVICE(user_data); - - if (!nm_device_set_ip_ifindex(device, ifindex)) { - nm_device_state_changed(device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } -} - -static void -ppp_ip4_config(NMPPPManager *ppp_manager, NMIP4Config *config, gpointer user_data) -{ - NMDevice *device = NM_DEVICE(user_data); - - /* Ignore PPP IP4 events that come in after initial configuration */ - if (nm_device_activate_ip4_state_in_conf(device)) - nm_device_activate_schedule_ip_config_result(device, AF_INET, NM_IP_CONFIG_CAST(config)); -} - -static NMActStageReturn -pppoe_stage3_ip4_config_start(NMDeviceEthernet *self, NMDeviceStateReason *out_failure_reason) +_ppp_mgr_stage3_maybe_ready(NMDeviceEthernet *self) { NMDevice * device = NM_DEVICE(self); NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE(self); - NMSettingPppoe * s_pppoe; - NMActRequest * req; - GError * err = NULL; + int IS_IPv4; - req = nm_device_get_act_request(device); + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + const int addr_family = IS_IPv4 ? AF_INET : AF_INET6; + const NMPppMgrIPData *ip_data; - g_return_val_if_fail(req, NM_ACT_STAGE_RETURN_FAILURE); - - s_pppoe = nm_device_get_applied_setting(device, NM_TYPE_SETTING_PPPOE); - - g_return_val_if_fail(s_pppoe, NM_ACT_STAGE_RETURN_FAILURE); - - priv->ppp_manager = nm_ppp_manager_create(nm_device_get_iface(device), &err); - - if (priv->ppp_manager) { - nm_ppp_manager_set_route_parameters(priv->ppp_manager, - nm_device_get_route_table(device, AF_INET), - nm_device_get_route_metric(device, AF_INET), - nm_device_get_route_table(device, AF_INET6), - nm_device_get_route_metric(device, AF_INET6)); + ip_data = nm_ppp_mgr_get_ip_data(priv->ppp_data.ppp_mgr, addr_family); + if (ip_data->ip_received) + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, ip_data->l3cd); } - if (!priv->ppp_manager - || !nm_ppp_manager_start(priv->ppp_manager, - req, - nm_setting_pppoe_get_username(s_pppoe), - 30, - 0, - &err)) { - _LOGW(LOGD_DEVICE, "PPPoE failed to start: %s", err->message); - g_error_free(err); + if (nm_ppp_mgr_get_state(priv->ppp_data.ppp_mgr) >= NM_PPP_MGR_STATE_HAVE_IP_CONFIG) + nm_device_devip_set_state(device, AF_UNSPEC, NM_DEVICE_IP_STATE_READY, NULL); +} - g_clear_object(&priv->ppp_manager); +static void +_ppp_mgr_callback(NMPppMgr *ppp_mgr, const NMPppMgrCallbackData *callback_data, gpointer user_data) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET(user_data); + NMDevice * device = NM_DEVICE(self); + NMDeviceState device_state; - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED); - return NM_ACT_STAGE_RETURN_FAILURE; + if (callback_data->callback_type != NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED) + return; + + device_state = nm_device_get_state(device); + + if (callback_data->data.state >= _NM_PPP_MGR_STATE_FAILED_START) { + if (device_state <= NM_DEVICE_STATE_ACTIVATED) + nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, callback_data->data.reason); + return; } - g_signal_connect(priv->ppp_manager, - NM_PPP_MANAGER_SIGNAL_STATE_CHANGED, - G_CALLBACK(ppp_state_changed), - self); - g_signal_connect(priv->ppp_manager, - NM_PPP_MANAGER_SIGNAL_IFINDEX_SET, - G_CALLBACK(ppp_ifindex_set), - self); - g_signal_connect(priv->ppp_manager, - NM_PPP_MANAGER_SIGNAL_IP4_CONFIG, - G_CALLBACK(ppp_ip4_config), - self); - return NM_ACT_STAGE_RETURN_POSTPONE; + if (device_state < NM_DEVICE_STATE_IP_CONFIG) { + if (callback_data->data.state >= NM_PPP_MGR_STATE_HAVE_IFINDEX) { + gs_free char *old_name = NULL; + gs_free_error GError *error = NULL; + + if (!nm_device_take_over_link(device, callback_data->data.ifindex, &old_name, &error)) { + _LOGW(LOGD_DEVICE | LOGD_PPP, + "could not take control of link %d: %s", + callback_data->data.ifindex, + error->message); + _ppp_mgr_cleanup(self); + nm_device_state_changed(device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_CONFIG_FAILED); + return; + } + + if (old_name) + nm_manager_remove_device(NM_MANAGER_GET, old_name, NM_DEVICE_TYPE_PPP); + + nm_device_activate_schedule_stage2_device_config(device, FALSE); + } + return; + } + + _ppp_mgr_stage3_maybe_ready(self); } /*****************************************************************************/ @@ -1351,7 +1330,7 @@ dcb_state(NMDevice *device, gboolean timeout) nm_clear_g_source(&priv->dcb_timeout_id); priv->dcb_handle_carrier_changes = FALSE; priv->dcb_wait = DCB_WAIT_UNKNOWN; - nm_device_activate_schedule_stage3_ip_config_start(device); + nm_device_activate_schedule_stage2_device_config(device, FALSE); } break; default: @@ -1407,31 +1386,98 @@ found: static NMActStageReturn act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) { - NMDeviceEthernet * self = (NMDeviceEthernet *) device; + NMDeviceEthernet * self = NM_DEVICE_ETHERNET(device); NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE(self); + NMConnection * connection; NMSettingConnection * s_con; const char * connection_type; - gboolean do_postpone = FALSE; NMSettingDcb * s_dcb; + NMActRequest * req; - s_con = nm_device_get_applied_setting(device, NM_TYPE_SETTING_CONNECTION); + connection = nm_device_get_applied_connection(device); + g_return_val_if_fail(connection, NM_ACT_STAGE_RETURN_FAILURE); + s_con = _nm_connection_get_setting(connection, NM_TYPE_SETTING_CONNECTION); g_return_val_if_fail(s_con, NM_ACT_STAGE_RETURN_FAILURE); nm_clear_g_source(&priv->dcb_timeout_id); priv->dcb_handle_carrier_changes = FALSE; + connection_type = nm_setting_connection_get_connection_type(s_con); + + if (nm_streq(connection_type, NM_SETTING_PPPOE_SETTING_NAME)) { + if (!priv->ppp_data.ppp_mgr) { + gs_free_error GError *error = NULL; + NMSettingPppoe * s_pppoe; + NMSettingPpp * s_ppp; + + s_ppp = nm_device_get_applied_setting(device, NM_TYPE_SETTING_PPP); + if (s_ppp) { + guint32 mtu; + guint32 mru; + guint32 mxu; + + mtu = nm_setting_ppp_get_mtu(s_ppp); + mru = nm_setting_ppp_get_mru(s_ppp); + mxu = MAX(mru, mtu); + if (mxu) { + _LOGD(LOGD_PPP, + "set MTU to %u (PPP interface MRU %u, MTU %u)", + mxu + PPPOE_ENCAP_OVERHEAD, + mru, + mtu); + nm_platform_link_set_mtu(nm_device_get_platform(device), + nm_device_get_ifindex(device), + mxu + PPPOE_ENCAP_OVERHEAD); + } + } + + req = nm_device_get_act_request(device); + g_return_val_if_fail(req, NM_ACT_STAGE_RETURN_FAILURE); + + s_pppoe = _nm_connection_get_setting(connection, NM_TYPE_SETTING_PPPOE); + g_return_val_if_fail(s_pppoe, NM_ACT_STAGE_RETURN_FAILURE); + + priv->ppp_data.ppp_mgr = + nm_ppp_mgr_start(&((const NMPppMgrConfig){ + .netns = nm_device_get_netns(device), + .parent_iface = nm_device_get_iface(device), + .callback = _ppp_mgr_callback, + .user_data = self, + .act_req = req, + .ppp_username = nm_setting_pppoe_get_username(s_pppoe), + .timeout_secs = 30, + .baud_override = 0, + }), + &error); + if (!priv->ppp_data.ppp_mgr) { + _LOGW(LOGD_DEVICE | LOGD_PPP, "PPPoE failed to start: %s", error->message); + *out_failure_reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED; + return NM_ACT_STAGE_RETURN_FAILURE; + } + + return NM_ACT_STAGE_RETURN_POSTPONE; + } + + if (nm_ppp_mgr_get_state(priv->ppp_data.ppp_mgr) < NM_PPP_MGR_STATE_HAVE_IFINDEX) + return NM_ACT_STAGE_RETURN_POSTPONE; + } + /* 802.1x has to run before any IP configuration since the 802.1x auth * process opens the port up for normal traffic. */ - connection_type = nm_setting_connection_get_connection_type(s_con); if (nm_streq(connection_type, NM_SETTING_WIRED_SETTING_NAME)) { NMSetting8021x *security; security = nm_device_get_applied_setting(device, NM_TYPE_SETTING_802_1X); if (security) { - /* FIXME: for now 802.1x is mutually exclusive with DCB */ + /* FIXME: we always return from this. stage2 must be re-entrant, and + * process all the necessary steps. Just returning for 8021x is wrong. */ + + if (priv->supplicant.ready) + return NM_ACT_STAGE_RETURN_SUCCESS; + if (!nm_device_has_carrier(NM_DEVICE(self))) { _LOGD(LOGD_DEVICE | LOGD_ETHER, "delay supplicant initialization until carrier goes up"); @@ -1450,7 +1496,7 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) /* DCB and FCoE setup */ s_dcb = nm_device_get_applied_setting(device, NM_TYPE_SETTING_DCB); - if (s_dcb) { + if (!priv->stage2_ready_dcb && s_dcb) { /* lldpad really really wants the carrier to be up */ if (nm_platform_link_is_connected(nm_device_get_platform(device), nm_device_get_ifindex(device))) { @@ -1465,78 +1511,46 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) } priv->dcb_handle_carrier_changes = TRUE; - do_postpone = TRUE; + return NM_ACT_STAGE_RETURN_POSTPONE; } - /* PPPoE setup */ - if (nm_connection_is_type(nm_device_get_applied_connection(device), - NM_SETTING_PPPOE_SETTING_NAME)) { - NMSettingPpp *s_ppp; - - s_ppp = nm_device_get_applied_setting(device, NM_TYPE_SETTING_PPP); - if (s_ppp) { - guint32 mtu; - guint32 mru; - guint32 mxu; - - mtu = nm_setting_ppp_get_mtu(s_ppp); - mru = nm_setting_ppp_get_mru(s_ppp); - mxu = MAX(mru, mtu); - if (mxu) { - _LOGD(LOGD_PPP, - "set MTU to %u (PPP interface MRU %u, MTU %u)", - mxu + PPPOE_ENCAP_OVERHEAD, - mru, - mtu); - nm_platform_link_set_mtu(nm_device_get_platform(device), - nm_device_get_ifindex(device), - mxu + PPPOE_ENCAP_OVERHEAD); - } - } - } - - return do_postpone ? NM_ACT_STAGE_RETURN_POSTPONE : NM_ACT_STAGE_RETURN_SUCCESS; -} - -static NMActStageReturn -act_stage3_ip_config_start(NMDevice * device, - int addr_family, - gpointer * out_config, - NMDeviceStateReason *out_failure_reason) -{ - NMSettingConnection *s_con; - const char * connection_type; - int ifindex; - - ifindex = nm_device_get_ifindex(device); - - if (ifindex <= 0) - return NM_ACT_STAGE_RETURN_FAILURE; - - if (addr_family == AF_INET) { - s_con = nm_device_get_applied_setting(device, NM_TYPE_SETTING_CONNECTION); - - g_return_val_if_fail(s_con, NM_ACT_STAGE_RETURN_FAILURE); - - connection_type = nm_setting_connection_get_connection_type(s_con); - if (!strcmp(connection_type, NM_SETTING_PPPOE_SETTING_NAME)) - return pppoe_stage3_ip4_config_start(NM_DEVICE_ETHERNET(device), out_failure_reason); - } - - return NM_DEVICE_CLASS(nm_device_ethernet_parent_class) - ->act_stage3_ip_config_start(device, addr_family, out_config, out_failure_reason); + return NM_ACT_STAGE_RETURN_SUCCESS; } static guint32 get_configured_mtu(NMDevice *device, NMDeviceMtuSource *out_source, gboolean *out_force) { + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE(device); + /* MTU only set for plain ethernet */ - if (NM_DEVICE_ETHERNET_GET_PRIVATE(device)->ppp_manager) + if (priv->ppp_data.ppp_mgr) return 0; return nm_device_get_configured_mtu_for_wired(device, out_source, out_force); } +static void +act_stage3_ip_config(NMDevice *device, int addr_family) +{ + NMDeviceEthernet * self = NM_DEVICE_ETHERNET(device); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE(self); + NMPppMgrState ppp_state; + + if (!priv->ppp_data.ppp_mgr) + return; + + ppp_state = nm_ppp_mgr_get_state(priv->ppp_data.ppp_mgr); + + nm_assert(NM_IN_SET(ppp_state, NM_PPP_MGR_STATE_HAVE_IFINDEX, NM_PPP_MGR_STATE_HAVE_IP_CONFIG)); + + if (ppp_state < NM_PPP_MGR_STATE_HAVE_IP_CONFIG) { + nm_device_devip_set_state(device, AF_UNSPEC, NM_DEVICE_IP_STATE_PENDING, NULL); + return; + } + + _ppp_mgr_stage3_maybe_ready(self); +} + static void deactivate(NMDevice *device) { @@ -1546,19 +1560,17 @@ deactivate(NMDevice *device) GError * error = NULL; int ifindex; - nm_clear_g_source(&priv->pppoe_wait_id); + nm_clear_g_source_inst(&priv->ppp_data.wait_source); nm_clear_g_signal_handler(self, &priv->carrier_id); - if (priv->ppp_manager) { - nm_ppp_manager_stop(priv->ppp_manager, NULL, NULL, NULL); - g_clear_object(&priv->ppp_manager); - } + _ppp_mgr_cleanup(self); supplicant_interface_release(self); priv->dcb_wait = DCB_WAIT_UNKNOWN; nm_clear_g_source(&priv->dcb_timeout_id); priv->dcb_handle_carrier_changes = FALSE; + priv->stage2_ready_dcb = FALSE; /* Tear down DCB/FCoE if it was enabled */ s_dcb = nm_device_get_applied_setting(device, NM_TYPE_SETTING_DCB); @@ -1571,7 +1583,7 @@ deactivate(NMDevice *device) /* Set last PPPoE connection time */ if (nm_device_get_applied_setting(device, NM_TYPE_SETTING_PPPOE)) - priv->last_pppoe_time = nm_utils_get_monotonic_timestamp_sec(); + priv->ppp_data.last_pppoe_time_msec = nm_utils_get_monotonic_timestamp_msec(); ifindex = nm_device_get_ifindex(device); if (ifindex > 0 && priv->ethtool_prev_set) { @@ -1933,7 +1945,7 @@ dispose(GObject *object) supplicant_interface_release(self); - nm_clear_g_source(&priv->pppoe_wait_id); + nm_clear_g_source_inst(&priv->ppp_data.wait_source); nm_clear_g_source(&priv->dcb_timeout_id); @@ -2032,7 +2044,7 @@ nm_device_ethernet_class_init(NMDeviceEthernetClass *klass) device_class->act_stage1_prepare = act_stage1_prepare; device_class->act_stage1_prepare_set_hwaddr_ethernet = TRUE; device_class->act_stage2_config = act_stage2_config; - device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + device_class->act_stage3_ip_config = act_stage3_ip_config; device_class->get_configured_mtu = get_configured_mtu; device_class->deactivate = deactivate; device_class->get_s390_subchannels = get_s390_subchannels; diff --git a/src/core/devices/nm-device-infiniband.c b/src/core/devices/nm-device-infiniband.c index df14dfa838..57b7698cb3 100644 --- a/src/core/devices/nm-device-infiniband.c +++ b/src/core/devices/nm-device-infiniband.c @@ -13,7 +13,6 @@ #include "NetworkManagerUtils.h" #include "nm-device-private.h" #include "nm-act-request.h" -#include "nm-ip4-config.h" #include "libnm-platform/nm-platform.h" #include "nm-device-factory.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h" diff --git a/src/core/devices/nm-device-ip-tunnel.c b/src/core/devices/nm-device-ip-tunnel.c index fe2e20239a..59653a685d 100644 --- a/src/core/devices/nm-device-ip-tunnel.c +++ b/src/core/devices/nm-device-ip-tunnel.c @@ -22,7 +22,6 @@ #include "libnm-core-intern/nm-core-internal.h" #include "settings/nm-settings.h" #include "nm-act-request.h" -#include "nm-ip4-config.h" #define _NMLOG_DEVICE_TYPE NMDeviceIPTunnel #include "nm-device-logging.h" diff --git a/src/core/devices/nm-device-macsec.c b/src/core/devices/nm-device-macsec.c index 51b7225bbf..7fa0779b52 100644 --- a/src/core/devices/nm-device-macsec.c +++ b/src/core/devices/nm-device-macsec.c @@ -409,7 +409,7 @@ supplicant_iface_state_is_completed(NMDeviceMacsec *self, NMSupplicantInterfaceS */ if (nm_device_get_state(NM_DEVICE(self)) == NM_DEVICE_STATE_CONFIG) { _LOGI(LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) successful."); - nm_device_activate_schedule_stage3_ip_config_start(NM_DEVICE(self)); + nm_device_activate_schedule_stage3_ip_config(NM_DEVICE(self), FALSE); } return; } diff --git a/src/core/devices/nm-device-macvlan.c b/src/core/devices/nm-device-macvlan.c index 937ba9e4e4..27be9f0023 100644 --- a/src/core/devices/nm-device-macvlan.c +++ b/src/core/devices/nm-device-macvlan.c @@ -19,7 +19,6 @@ #include "nm-setting-macvlan.h" #include "nm-setting-wired.h" #include "nm-active-connection.h" -#include "nm-ip4-config.h" #include "nm-utils.h" #define _NMLOG_DEVICE_TYPE NMDeviceMacvlan diff --git a/src/core/devices/nm-device-ppp.c b/src/core/devices/nm-device-ppp.c index 04051bece5..855916397b 100644 --- a/src/core/devices/nm-device-ppp.c +++ b/src/core/devices/nm-device-ppp.c @@ -7,16 +7,14 @@ #include "nm-device-ppp.h" -#include "nm-ip4-config.h" +#include "nm-l3-config-data.h" #include "nm-act-request.h" #include "nm-device-factory.h" #include "nm-device-private.h" #include "nm-manager.h" #include "nm-setting-pppoe.h" #include "libnm-platform/nm-platform.h" -#include "ppp/nm-ppp-manager.h" -#include "ppp/nm-ppp-manager-call.h" -#include "ppp/nm-ppp-status.h" +#include "ppp/nm-ppp-mgr.h" #define _NMLOG_DEVICE_TYPE NMDevicePpp #include "nm-device-logging.h" @@ -24,8 +22,7 @@ /*****************************************************************************/ typedef struct _NMDevicePppPrivate { - NMPPPManager *ppp_manager; - NMIP4Config * ip4_config; + NMPppMgr *ppp_mgr; } NMDevicePppPrivate; struct _NMDevicePpp { @@ -42,86 +39,92 @@ G_DEFINE_TYPE(NMDevicePpp, nm_device_ppp, NM_TYPE_DEVICE) #define NM_DEVICE_PPP_GET_PRIVATE(self) \ _NM_GET_PRIVATE(self, NMDevicePpp, NM_IS_DEVICE_PPP, NMDevice) +/*****************************************************************************/ + static NMDeviceCapabilities get_generic_capabilities(NMDevice *device) { return NM_DEVICE_CAP_IS_SOFTWARE; } -static void -ppp_state_changed(NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data) -{ - NMDevice *device = NM_DEVICE(user_data); +/*****************************************************************************/ - switch (status) { - case NM_PPP_STATUS_DISCONNECT: - nm_device_state_changed(device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_PPP_DISCONNECT); - break; - case NM_PPP_STATUS_DEAD: - nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED); - break; - default: - break; - } +static void +_ppp_mgr_cleanup(NMDevicePpp *self) +{ + NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE(self); + + nm_clear_pointer(&priv->ppp_mgr, nm_ppp_mgr_destroy); } static void -ppp_ifindex_set(NMPPPManager *ppp_manager, int ifindex, const char *iface, gpointer user_data) -{ - NMDevice * device = NM_DEVICE(user_data); - NMDevicePpp * self = NM_DEVICE_PPP(device); - gs_free char *old_name = NULL; - gs_free_error GError *error = NULL; - - if (!nm_device_take_over_link(device, ifindex, &old_name, &error)) { - _LOGW(LOGD_DEVICE | LOGD_PPP, - "could not take control of link %d: %s", - ifindex, - error->message); - nm_device_state_changed(device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return; - } - - if (old_name) - nm_manager_remove_device(NM_MANAGER_GET, old_name, NM_DEVICE_TYPE_PPP); - - nm_device_activate_schedule_stage3_ip_config_start(device); -} - -static void -_ppp_ip4_config_handle(NMDevicePpp *self) +_ppp_mgr_stage3_maybe_ready(NMDevicePpp *self) { NMDevice * device = NM_DEVICE(self); NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE(self); + int IS_IPv4; - if (!priv->ip4_config) - return; + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + const int addr_family = IS_IPv4 ? AF_INET : AF_INET6; + const NMPppMgrIPData *ip_data; - if (nm_device_get_state(device) == NM_DEVICE_STATE_IP_CONFIG - && nm_device_activate_ip4_state_in_conf(device)) { - nm_device_activate_schedule_ip_config_result( - device, - AF_INET, - NM_IP_CONFIG_CAST(g_steal_pointer(&priv->ip4_config))); - return; + ip_data = nm_ppp_mgr_get_ip_data(priv->ppp_mgr, addr_family); + if (ip_data->ip_received) + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, ip_data->l3cd); } + + if (nm_ppp_mgr_get_state(priv->ppp_mgr) >= NM_PPP_MGR_STATE_HAVE_IP_CONFIG) + nm_device_devip_set_state(device, AF_UNSPEC, NM_DEVICE_IP_STATE_READY, NULL); } static void -ppp_ip4_config(NMPPPManager *ppp_manager, NMIP4Config *config, gpointer user_data) +_ppp_mgr_callback(NMPppMgr *ppp_mgr, const NMPppMgrCallbackData *callback_data, gpointer user_data) { - NMDevicePpp * self = NM_DEVICE_PPP(user_data); - NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE(self); + NMDevicePpp * self = NM_DEVICE_PPP(user_data); + NMDevice * device = NM_DEVICE(self); + NMDeviceState device_state; - _LOGT(LOGD_DEVICE | LOGD_PPP, "received IPv4 config from pppd"); - nm_g_object_ref_set(&priv->ip4_config, config); - _ppp_ip4_config_handle(self); + if (callback_data->callback_type != NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED) + return; + + device_state = nm_device_get_state(device); + + if (callback_data->data.state >= _NM_PPP_MGR_STATE_FAILED_START) { + if (device_state <= NM_DEVICE_STATE_ACTIVATED) + nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, callback_data->data.reason); + return; + } + + if (device_state < NM_DEVICE_STATE_IP_CONFIG) { + if (callback_data->data.state >= NM_PPP_MGR_STATE_HAVE_IFINDEX) { + gs_free char *old_name = NULL; + gs_free_error GError *error = NULL; + + if (!nm_device_take_over_link(device, callback_data->data.ifindex, &old_name, &error)) { + _LOGW(LOGD_DEVICE | LOGD_PPP, + "could not take control of link %d: %s", + callback_data->data.ifindex, + error->message); + _ppp_mgr_cleanup(self); + nm_device_state_changed(device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_CONFIG_FAILED); + return; + } + + if (old_name) + nm_manager_remove_device(NM_MANAGER_GET, old_name, NM_DEVICE_TYPE_PPP); + + nm_device_activate_schedule_stage2_device_config(device, FALSE); + } + return; + } + + _ppp_mgr_stage3_maybe_ready(self); } +/*****************************************************************************/ + static gboolean check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) { @@ -149,85 +152,80 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE(self); NMSettingPppoe * s_pppoe; NMActRequest * req; - GError * error = NULL; - req = nm_device_get_act_request(device); - g_return_val_if_fail(req, NM_ACT_STAGE_RETURN_FAILURE); + if (!priv->ppp_mgr) { + gs_free_error GError *error = NULL; - s_pppoe = nm_device_get_applied_setting(device, NM_TYPE_SETTING_PPPOE); - g_return_val_if_fail(s_pppoe, NM_ACT_STAGE_RETURN_FAILURE); + req = nm_device_get_act_request(device); + g_return_val_if_fail(req, NM_ACT_STAGE_RETURN_FAILURE); - g_clear_object(&priv->ip4_config); + s_pppoe = nm_device_get_applied_setting(device, NM_TYPE_SETTING_PPPOE); + g_return_val_if_fail(s_pppoe, NM_ACT_STAGE_RETURN_FAILURE); - priv->ppp_manager = nm_ppp_manager_create(nm_setting_pppoe_get_parent(s_pppoe), &error); + priv->ppp_mgr = nm_ppp_mgr_start(&((const NMPppMgrConfig){ + .netns = nm_device_get_netns(device), + .parent_iface = nm_setting_pppoe_get_parent(s_pppoe), + .callback = _ppp_mgr_callback, + .user_data = self, + .act_req = req, + .ppp_username = nm_setting_pppoe_get_username(s_pppoe), + .timeout_secs = 30, + .baud_override = 0, + }), + &error); + if (!priv->ppp_mgr) { + _LOGW(LOGD_DEVICE | LOGD_PPP, "PPPoE failed to start: %s", error->message); + *out_failure_reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED; + return NM_ACT_STAGE_RETURN_FAILURE; + } - if (priv->ppp_manager) { - nm_ppp_manager_set_route_parameters(priv->ppp_manager, - nm_device_get_route_table(device, AF_INET), - nm_device_get_route_metric(device, AF_INET), - nm_device_get_route_table(device, AF_INET6), - nm_device_get_route_metric(device, AF_INET6)); - } - - if (!priv->ppp_manager - || !nm_ppp_manager_start(priv->ppp_manager, - req, - nm_setting_pppoe_get_username(s_pppoe), - 30, - 0, - &error)) { - _LOGW(LOGD_DEVICE | LOGD_PPP, "PPPoE failed to start: %s", error->message); - g_error_free(error); - - g_clear_object(&priv->ppp_manager); - - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - g_signal_connect(priv->ppp_manager, - NM_PPP_MANAGER_SIGNAL_STATE_CHANGED, - G_CALLBACK(ppp_state_changed), - self); - g_signal_connect(priv->ppp_manager, - NM_PPP_MANAGER_SIGNAL_IFINDEX_SET, - G_CALLBACK(ppp_ifindex_set), - self); - g_signal_connect(priv->ppp_manager, - NM_PPP_MANAGER_SIGNAL_IP4_CONFIG, - G_CALLBACK(ppp_ip4_config), - self); - return NM_ACT_STAGE_RETURN_POSTPONE; -} - -static gboolean -_schedule_ip_config_result(gpointer user_data) -{ - gs_unref_object NMDevicePpp *self = user_data; - - _ppp_ip4_config_handle(self); - return G_SOURCE_REMOVE; -} - -static NMActStageReturn -act_stage3_ip_config_start(NMDevice * device, - int addr_family, - gpointer * out_config, - NMDeviceStateReason *out_failure_reason) -{ - if (addr_family == AF_INET) { - NMDevicePpp * self = NM_DEVICE_PPP(device); - NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE(self); - - if (priv->ip4_config) - nm_g_idle_add(_schedule_ip_config_result, g_object_ref(self)); - - /* Wait IPCP termination */ return NM_ACT_STAGE_RETURN_POSTPONE; } - return NM_DEVICE_CLASS(nm_device_ppp_parent_class) - ->act_stage3_ip_config_start(device, addr_family, out_config, out_failure_reason); + if (nm_ppp_mgr_get_state(priv->ppp_mgr) < NM_PPP_MGR_STATE_HAVE_IFINDEX) + return NM_ACT_STAGE_RETURN_POSTPONE; + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static void +act_stage3_ip_config(NMDevice *device, int addr_family) +{ + NMDevicePpp * self = NM_DEVICE_PPP(device); + NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE(self); + NMPppMgrState ppp_state; + + if (!priv->ppp_mgr) { + nm_assert_not_reached(); + return; + } + + ppp_state = nm_ppp_mgr_get_state(priv->ppp_mgr); + + nm_assert(NM_IN_SET(ppp_state, NM_PPP_MGR_STATE_HAVE_IFINDEX, NM_PPP_MGR_STATE_HAVE_IP_CONFIG)); + + if (ppp_state < NM_PPP_MGR_STATE_HAVE_IP_CONFIG) { + nm_device_devip_set_state(device, AF_UNSPEC, NM_DEVICE_IP_STATE_PENDING, NULL); + return; + } + + _ppp_mgr_stage3_maybe_ready(self); +} + +static const char * +get_ip_method_auto(NMDevice *device, int addr_family) +{ + if (NM_IS_IPv4(addr_family)) { + /* We cannot do DHCPv4 on a PPP link, instead we get "auto" IP addresses + * by pppd. Return "manual" here, which has the suitable effect to a + * (zero) manual addresses in addition. */ + return NM_SETTING_IP6_CONFIG_METHOD_MANUAL; + } + + /* We can do autoconf6 on an PPP link, but we should already get an IPv6 + * address from pppd. Use that instead. We however do want to generate our + * (own) IPv6 link local address, so return "link-local". */ + return NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; } static gboolean @@ -260,17 +258,13 @@ create_and_realize(NMDevice * device, static void deactivate(NMDevice *device) { - NMDevicePpp * self = NM_DEVICE_PPP(device); - NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE(self); + NMDevicePpp *self = NM_DEVICE_PPP(device); - if (priv->ppp_manager) { - nm_ppp_manager_stop(priv->ppp_manager, NULL, NULL, NULL); - g_clear_object(&priv->ppp_manager); - } - - g_clear_object(&priv->ip4_config); + _ppp_mgr_cleanup(self); } +/*****************************************************************************/ + static void nm_device_ppp_init(NMDevicePpp *self) {} @@ -278,10 +272,9 @@ nm_device_ppp_init(NMDevicePpp *self) static void dispose(GObject *object) { - NMDevicePpp * self = NM_DEVICE_PPP(object); - NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE(self); + NMDevicePpp *self = NM_DEVICE_PPP(object); - g_clear_object(&priv->ip4_config); + _ppp_mgr_cleanup(self); G_OBJECT_CLASS(nm_device_ppp_parent_class)->dispose(object); } @@ -306,7 +299,8 @@ nm_device_ppp_class_init(NMDevicePppClass *klass) device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_PPP); device_class->act_stage2_config = act_stage2_config; - device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + device_class->act_stage3_ip_config = act_stage3_ip_config; + device_class->get_ip_method_auto = get_ip_method_auto; device_class->check_connection_compatible = check_connection_compatible; device_class->create_and_realize = create_and_realize; device_class->deactivate = deactivate; diff --git a/src/core/devices/nm-device-private.h b/src/core/devices/nm-device-private.h index b55e8b4380..ae89848307 100644 --- a/src/core/devices/nm-device-private.h +++ b/src/core/devices/nm-device-private.h @@ -8,6 +8,7 @@ #define __NETWORKMANAGER_DEVICE_PRIVATE_H__ #include "nm-device.h" +#include "nm-l3-config-data.h" /* This file should only be used by subclasses of NMDevice */ @@ -21,12 +22,6 @@ enum NMActStageReturn { NM_ACT_STAGE_RETURN_FAILURE = 0, /* Hard failure of activation */ NM_ACT_STAGE_RETURN_SUCCESS, /* Activation stage done */ NM_ACT_STAGE_RETURN_POSTPONE, /* Long-running operation in progress */ - NM_ACT_STAGE_RETURN_IP_WAIT, /* IP config stage is waiting (state IP_WAIT) */ - NM_ACT_STAGE_RETURN_IP_DONE, /* IP config stage is done (state IP_DONE), - * For the ip-config stage, this is similar to - * NM_ACT_STAGE_RETURN_SUCCESS, except that no - * IP config should be committed. */ - NM_ACT_STAGE_RETURN_IP_FAIL, /* IP config stage failed (state IP_FAIL), activation may proceed */ }; #define NM_DEVICE_CAP_NONSTANDARD_CARRIER 0x80000000 @@ -34,8 +29,6 @@ enum NMActStageReturn { #define NM_DEVICE_CAP_INTERNAL_MASK 0xc0000000 -void nm_device_arp_announce(NMDevice *self); - NMSettings *nm_device_get_settings(NMDevice *self); NMManager *nm_device_get_manager(NMDevice *self); @@ -44,10 +37,6 @@ gboolean nm_device_set_ip_ifindex(NMDevice *self, int ifindex); gboolean nm_device_set_ip_iface(NMDevice *self, const char *iface); -void nm_device_activate_schedule_stage3_ip_config_start(NMDevice *device); - -gboolean nm_device_activate_stage3_ip_start(NMDevice *self, int addr_family); - gboolean nm_device_bring_up(NMDevice *self, gboolean wait, gboolean *no_firmware); void nm_device_take_down(NMDevice *self, gboolean block); @@ -65,52 +54,7 @@ void nm_device_set_firmware_missing(NMDevice *self, gboolean missing); void nm_device_activate_schedule_stage1_device_prepare(NMDevice *device, gboolean do_sync); void nm_device_activate_schedule_stage2_device_config(NMDevice *device, gboolean do_sync); - -void -nm_device_activate_schedule_ip_config_result(NMDevice *device, int addr_family, NMIPConfig *config); - -void nm_device_activate_schedule_ip_config_timeout(NMDevice *device, int addr_family); - -NMDeviceIPState nm_device_activate_get_ip_state(NMDevice *self, int addr_family); - -static inline gboolean -nm_device_activate_ip4_state_in_conf(NMDevice *self) -{ - return nm_device_activate_get_ip_state(self, AF_INET) == NM_DEVICE_IP_STATE_CONF; -} - -static inline gboolean -nm_device_activate_ip4_state_in_wait(NMDevice *self) -{ - return nm_device_activate_get_ip_state(self, AF_INET) == NM_DEVICE_IP_STATE_WAIT; -} - -static inline gboolean -nm_device_activate_ip4_state_done(NMDevice *self) -{ - return nm_device_activate_get_ip_state(self, AF_INET) == NM_DEVICE_IP_STATE_DONE; -} - -static inline gboolean -nm_device_activate_ip6_state_in_conf(NMDevice *self) -{ - return nm_device_activate_get_ip_state(self, AF_INET6) == NM_DEVICE_IP_STATE_CONF; -} - -static inline gboolean -nm_device_activate_ip6_state_in_wait(NMDevice *self) -{ - return nm_device_activate_get_ip_state(self, AF_INET6) == NM_DEVICE_IP_STATE_WAIT; -} - -static inline gboolean -nm_device_activate_ip6_state_done(NMDevice *self) -{ - return nm_device_activate_get_ip_state(self, AF_INET6) == NM_DEVICE_IP_STATE_DONE; -} - -gboolean nm_device_dhcp4_renew(NMDevice *device, gboolean release); -gboolean nm_device_dhcp6_renew(NMDevice *device, gboolean release); +void nm_device_activate_schedule_stage3_ip_config(NMDevice *device, gboolean do_sync); void nm_device_recheck_available_connections(NMDevice *device); @@ -126,25 +70,54 @@ void nm_device_queue_recheck_available(NMDevice * device, NMDeviceStateReason available_reason, NMDeviceStateReason unavailable_reason); -void nm_device_set_dev2_ip_config(NMDevice *device, int addr_family, NMIPConfig *config); - gboolean nm_device_hw_addr_is_explict(NMDevice *device); -void nm_device_ip_method_failed(NMDevice *self, int addr_family, NMDeviceStateReason reason); +NMDeviceIPState nm_device_devip_get_state(NMDevice *self, int addr_family); + +void nm_device_devip_set_state_full(NMDevice * self, + int addr_family, + NMDeviceIPState ip_state, + const NML3ConfigData *l3cd, + NMDeviceStateReason failed_reason); + +static inline void +nm_device_devip_set_state(NMDevice * self, + int addr_family, + NMDeviceIPState ip_state, + const NML3ConfigData *l3cd) +{ + nm_assert(NM_IS_DEVICE(self)); + nm_assert_addr_family_or_unspec(addr_family); + nm_assert(!l3cd || NM_IS_L3_CONFIG_DATA(l3cd)); + nm_assert(NM_IN_SET(ip_state, NM_DEVICE_IP_STATE_PENDING, NM_DEVICE_IP_STATE_READY)); + + nm_device_devip_set_state_full(self, addr_family, ip_state, l3cd, NM_DEVICE_STATE_REASON_NONE); +} + +static inline void +nm_device_devip_set_failed(NMDevice *self, int addr_family, NMDeviceStateReason reason) +{ + nm_assert(NM_IS_DEVICE(self)); + nm_assert_addr_family_or_unspec(addr_family); + nm_assert(reason != NM_DEVICE_STATE_REASON_NONE); + + nm_device_devip_set_state_full(self, addr_family, NM_DEVICE_IP_STATE_FAILED, NULL, reason); +} gboolean nm_device_sysctl_ip_conf_set(NMDevice * self, int addr_family, const char *property, const char *value); -NMIP4Config *nm_device_ip4_config_new(NMDevice *self); - -NMIP6Config *nm_device_ip6_config_new(NMDevice *self); - -NMIPConfig *nm_device_ip_config_new(NMDevice *self, int addr_family); - NML3ConfigData *nm_device_create_l3_config_data(NMDevice *self, NMIPConfigSource source); +const NML3ConfigData *nm_device_create_l3_config_data_from_connection(NMDevice * self, + NMConnection *connection); + +void nm_device_ip_method_dhcp4_start(NMDevice *self); + +void nm_device_ip_method_autoconf6_start(NMDevice *self); + /*****************************************************************************/ gint64 nm_device_get_configured_mtu_from_connection_default(NMDevice * self, diff --git a/src/core/devices/nm-device-tun.c b/src/core/devices/nm-device-tun.c index 0ab08f65b5..1f6a427636 100644 --- a/src/core/devices/nm-device-tun.c +++ b/src/core/devices/nm-device-tun.c @@ -13,7 +13,6 @@ #include "nm-act-request.h" #include "nm-device-private.h" -#include "nm-ip4-config.h" #include "libnm-platform/nm-platform.h" #include "nm-device-factory.h" #include "nm-setting-tun.h" diff --git a/src/core/devices/nm-device-utils.c b/src/core/devices/nm-device-utils.c index 2a6ab04e24..9e56d0b2b9 100644 --- a/src/core/devices/nm-device-utils.c +++ b/src/core/devices/nm-device-utils.c @@ -151,10 +151,9 @@ NM_UTILS_LOOKUP_STR_DEFINE(nm_device_ip_state_to_string, NMDeviceIPState, NM_UTILS_LOOKUP_DEFAULT_WARN("unknown"), NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_NONE, "none"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_WAIT, "wait"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_CONF, "conf"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_DONE, "done"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_FAIL, "fail"), ); + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_PENDING, "pending"), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_READY, "done"), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_FAILED, "fail"), ); /*****************************************************************************/ diff --git a/src/core/devices/nm-device-utils.h b/src/core/devices/nm-device-utils.h index 7200b0ea2c..759e610568 100644 --- a/src/core/devices/nm-device-utils.h +++ b/src/core/devices/nm-device-utils.h @@ -73,20 +73,17 @@ const char *nm_device_sys_iface_state_to_string(NMDeviceSysIfaceState sys_iface_ /*****************************************************************************/ -typedef enum { +typedef enum _nm_packed { NM_DEVICE_IP_STATE_NONE, - NM_DEVICE_IP_STATE_WAIT, - NM_DEVICE_IP_STATE_CONF, - NM_DEVICE_IP_STATE_DONE, - NM_DEVICE_IP_STATE_FAIL, + NM_DEVICE_IP_STATE_PENDING, + NM_DEVICE_IP_STATE_READY, + NM_DEVICE_IP_STATE_FAILED, } NMDeviceIPState; const char *nm_device_ip_state_to_string(NMDeviceIPState ip_state); /*****************************************************************************/ -/*****************************************************************************/ - void nm_device_resolve_address(int addr_family, gconstpointer address, GCancellable * cancellable, diff --git a/src/core/devices/nm-device-vlan.c b/src/core/devices/nm-device-vlan.c index d1e5741170..c1902d5eea 100644 --- a/src/core/devices/nm-device-vlan.c +++ b/src/core/devices/nm-device-vlan.c @@ -15,7 +15,6 @@ #include "nm-device-private.h" #include "settings/nm-settings.h" #include "nm-act-request.h" -#include "nm-ip4-config.h" #include "libnm-platform/nm-platform.h" #include "nm-device-factory.h" #include "nm-manager.h" @@ -85,11 +84,11 @@ parent_mtu_maybe_changed(NMDevice *parent, GParamSpec *pspec, gpointer user_data static void parent_hwaddr_maybe_changed(NMDevice *parent, GParamSpec *pspec, gpointer user_data) { - NMDevice * device = NM_DEVICE(user_data); - NMDeviceVlan * self = NM_DEVICE_VLAN(device); - NMConnection * connection; - const char * new_mac, *old_mac; - NMSettingIPConfig *s_ip6; + NMDevice * device = NM_DEVICE(user_data); + NMDeviceVlan *self = NM_DEVICE_VLAN(device); + NMConnection *connection; + const char * old_mac; + const char * new_mac; /* Never touch assumed devices */ if (nm_device_sys_iface_state_is_external_or_assume(device)) @@ -113,13 +112,10 @@ parent_hwaddr_maybe_changed(NMDevice *parent, GParamSpec *pspec, gpointer user_d NM_PRINT_FMT_QUOTE_STRING(new_mac)); if (new_mac) { nm_device_hw_addr_set(device, new_mac, "vlan-parent", TRUE); - nm_device_arp_announce(device); /* When changing the hw address the interface is taken down, * removing the IPv6 configuration; reapply it. */ - s_ip6 = nm_connection_get_setting_ip6_config(connection); - if (s_ip6) - nm_device_reactivate_ip_config(device, AF_INET6, s_ip6, s_ip6); + nm_device_l3cfg_commit(device, NM_L3_CFG_COMMIT_TYPE_REAPPLY, FALSE); } } diff --git a/src/core/devices/nm-device-vxlan.c b/src/core/devices/nm-device-vxlan.c index fcd6950b88..329cc3febb 100644 --- a/src/core/devices/nm-device-vxlan.c +++ b/src/core/devices/nm-device-vxlan.c @@ -16,7 +16,6 @@ #include "nm-setting-wired.h" #include "settings/nm-settings.h" #include "nm-act-request.h" -#include "nm-ip4-config.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h" #include "libnm-core-intern/nm-core-internal.h" diff --git a/src/core/devices/nm-device-wireguard.c b/src/core/devices/nm-device-wireguard.c index f129478f7f..de715264ce 100644 --- a/src/core/devices/nm-device-wireguard.c +++ b/src/core/devices/nm-device-wireguard.c @@ -12,6 +12,7 @@ #include "nm-setting-wireguard.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h" +#include "nm-l3-config-data.h" #include "libnm-core-intern/nm-core-internal.h" #include "libnm-glib-aux/nm-secret-utils.h" #include "nm-device-private.h" @@ -1625,19 +1626,19 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) return ret; } -static NMIPConfig * +static const NML3ConfigData * _get_dev2_ip_config(NMDeviceWireGuard *self, int addr_family) { - NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self); - gs_unref_object NMIPConfig *ip_config = NULL; - NMConnection * connection; - NMSettingWireGuard * s_wg; - guint n_peers; - guint i; - int ip_ifindex; - guint32 route_metric; - guint32 route_table_coerced; - gboolean auto_default_route_enabled; + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self); + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + NMConnection * connection; + NMSettingWireGuard * s_wg; + guint n_peers; + guint i; + int ip_ifindex; + guint32 route_metric; + guint32 route_table_coerced; + gboolean auto_default_route_enabled; _auto_default_route_init(self); @@ -1715,11 +1716,10 @@ _get_dev2_ip_config(NMDeviceWireGuard *self, int addr_family) continue; } - if (!ip_config) { - ip_config = nm_device_ip_config_new(NM_DEVICE(self), addr_family); - nm_ip_config_set_config_flags(ip_config, - NM_IP_CONFIG_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES, - 0); + if (!l3cd) { + l3cd = nm_device_create_l3_config_data(NM_DEVICE(self), NM_IP_CONFIG_SOURCE_USER); + nm_l3_config_data_set_flags(l3cd, + NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES); } nm_utils_ipx_address_clear_host_address(addr_family, &addrbin, NULL, prefix); @@ -1754,27 +1754,23 @@ _get_dev2_ip_config(NMDeviceWireGuard *self, int addr_family) }; } - nm_ip_config_add_route(ip_config, &rt.rx, NULL); + nm_l3_config_data_add_route(l3cd, addr_family, NULL, &rt.rx); } } - return g_steal_pointer(&ip_config); + if (!l3cd) + return NULL; + + return nm_l3_config_data_seal(g_steal_pointer(&l3cd)); } -static NMActStageReturn -act_stage3_ip_config_start(NMDevice * device, - int addr_family, - gpointer * out_config, - NMDeviceStateReason *out_failure_reason) +static void +act_stage3_ip_config(NMDevice *device, int addr_family) { - gs_unref_object NMIPConfig *ip_config = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; - ip_config = _get_dev2_ip_config(NM_DEVICE_WIREGUARD(device), addr_family); - - nm_device_set_dev2_ip_config(device, addr_family, ip_config); - - return NM_DEVICE_CLASS(nm_device_wireguard_parent_class) - ->act_stage3_ip_config_start(device, addr_family, out_config, out_failure_reason); + l3cd = _get_dev2_ip_config(NM_DEVICE_WIREGUARD(device), addr_family); + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, l3cd); } static guint32 @@ -1864,11 +1860,9 @@ can_reapply_change(NMDevice * device, static void reapply_connection(NMDevice *device, NMConnection *con_old, NMConnection *con_new) { - NMDeviceWireGuard * self = NM_DEVICE_WIREGUARD(device); - NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self); - gs_unref_object NMIPConfig *ip4_config = NULL; - gs_unref_object NMIPConfig *ip6_config = NULL; - NMDeviceState state = nm_device_get_state(device); + NMDeviceWireGuard * self = NM_DEVICE_WIREGUARD(device); + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self); + NMDeviceState state = nm_device_get_state(device); NM_DEVICE_CLASS(nm_device_wireguard_parent_class)->reapply_connection(device, con_old, con_new); @@ -1878,11 +1872,14 @@ reapply_connection(NMDevice *device, NMConnection *con_old, NMConnection *con_ne } if (state >= NM_DEVICE_STATE_IP_CONFIG) { - ip4_config = _get_dev2_ip_config(self, AF_INET); - ip6_config = _get_dev2_ip_config(self, AF_INET6); + nm_auto_unref_l3cd const NML3ConfigData *l3cd_4 = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd_6 = NULL; - nm_device_set_dev2_ip_config(device, AF_INET, ip4_config); - nm_device_set_dev2_ip_config(device, AF_INET6, ip6_config); + l3cd_4 = _get_dev2_ip_config(self, AF_INET); + l3cd_6 = _get_dev2_ip_config(self, AF_INET6); + + nm_device_devip_set_state(device, AF_INET, NM_DEVICE_IP_STATE_READY, l3cd_4); + nm_device_devip_set_state(device, AF_INET6, NM_DEVICE_IP_STATE_READY, l3cd_6); } } @@ -2025,7 +2022,7 @@ nm_device_wireguard_class_init(NMDeviceWireGuardClass *klass) device_class->create_and_realize = create_and_realize; device_class->act_stage2_config = act_stage2_config; device_class->act_stage2_config_also_for_external_or_assume = TRUE; - device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + device_class->act_stage3_ip_config = act_stage3_ip_config; device_class->get_generic_capabilities = get_generic_capabilities; device_class->link_changed = link_changed; device_class->update_connection = update_connection; diff --git a/src/core/devices/nm-device-wpan.c b/src/core/devices/nm-device-wpan.c index 9bc43a850d..ed5bcbde4d 100644 --- a/src/core/devices/nm-device-wpan.c +++ b/src/core/devices/nm-device-wpan.c @@ -14,7 +14,6 @@ #include "nm-act-request.h" #include "nm-device-private.h" -#include "nm-ip4-config.h" #include "libnm-platform/nm-platform.h" #include "nm-device-factory.h" #include "nm-setting-wpan.h" diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index fc11e458ed..627066d94a 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -34,6 +34,8 @@ #include "nm-device-private.h" #include "nm-l3cfg.h" #include "nm-l3-config-data.h" +#include "nm-l3-ipv4ll.h" +#include "nm-l3-ipv6ll.h" #include "NetworkManagerUtils.h" #include "nm-manager.h" #include "libnm-platform/nm-platform.h" @@ -42,14 +44,13 @@ #include "libnm-platform/nmp-rules-manager.h" #include "ndisc/nm-ndisc.h" #include "ndisc/nm-lndp-ndisc.h" + #include "dhcp/nm-dhcp-manager.h" #include "dhcp/nm-dhcp-utils.h" #include "nm-act-request.h" -#include "nm-proxy-config.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" #include "nm-pacrunner-manager.h" #include "dnsmasq/nm-dnsmasq-manager.h" +#include "nm-ip-config.h" #include "nm-dhcp-config.h" #include "nm-rfkill-manager.h" #include "nm-firewall-utils.h" @@ -66,7 +67,6 @@ #include "nm-config.h" #include "c-list/src/c-list.h" #include "dns/nm-dns-manager.h" -#include "nm-acd-manager.h" #include "libnm-core-intern/nm-core-internal.h" #include "libnm-systemd-core/nm-sd.h" #include "nm-lldp-listener.h" @@ -86,19 +86,7 @@ #define DEFAULT_AUTOCONNECT TRUE -static guint32 -dhcp_grace_period_from_timeout(guint32 timeout) -{ -#define DHCP_GRACE_PERIOD_MULTIPLIER 2U - - nm_assert(timeout > 0); - nm_assert(timeout < G_MAXINT32); - - if (timeout < G_MAXUINT32 / DHCP_GRACE_PERIOD_MULTIPLIER) - return timeout * DHCP_GRACE_PERIOD_MULTIPLIER; - - return G_MAXUINT32; -} +#define GRACE_PERIOD_MULTIPLIER 2U #define CARRIER_WAIT_TIME_MS 6000 #define CARRIER_WAIT_TIME_AFTER_MTU_MS 10000 @@ -117,6 +105,13 @@ typedef enum { CLEANUP_TYPE_DECONFIGURE, } CleanupType; +typedef enum _nm_packed { + ADDR_METHOD_STATE_DISABLED, + ADDR_METHOD_STATE_PENDING, + ADDR_METHOD_STATE_GOOD, + ADDR_METHOD_STATE_FAILED, +} AddrMethodState; + typedef struct { CList lst_slave; NMDevice *slave; @@ -139,28 +134,61 @@ typedef struct { NMOptionBool autoprobe; } SriovOp; -typedef void (*AcdCallback)(NMDevice *, NMIP4Config **, gboolean); - typedef enum { /* The various NML3ConfigData types that we track explicitly. Note that * their relative order matters: higher numbers in this enum means more * important (and during merge overwrites other settings). */ + L3_CONFIG_DATA_TYPE_LL_4, + L3_CONFIG_DATA_TYPE_LL_6, + +#define L3_CONFIG_DATA_TYPE_LL_X(IS_IPv4) \ + ((IS_IPv4) ? L3_CONFIG_DATA_TYPE_LL_4 : L3_CONFIG_DATA_TYPE_LL_6) + L3_CONFIG_DATA_TYPE_AC_6, + L3_CONFIG_DATA_TYPE_PD_6, + L3_CONFIG_DATA_TYPE_DHCP_4, L3_CONFIG_DATA_TYPE_DHCP_6, - L3_CONFIG_DATA_TYPE_DEV_4, - L3_CONFIG_DATA_TYPE_DEV_6, - L3_CONFIG_DATA_TYPE_SETTING, + +#define L3_CONFIG_DATA_TYPE_DHCP_X(IS_IPv4) \ + ((IS_IPv4) ? L3_CONFIG_DATA_TYPE_DHCP_4 : L3_CONFIG_DATA_TYPE_DHCP_6) + + L3_CONFIG_DATA_TYPE_SHARED_4, + L3_CONFIG_DATA_TYPE_DEVIP_UNSPEC, + L3_CONFIG_DATA_TYPE_DEVIP_4, + L3_CONFIG_DATA_TYPE_DEVIP_6, + +#define L3_CONFIG_DATA_TYPE_DEVIP(addr_family) \ + ({ \ + L3ConfigDataType _t; \ + \ + switch (addr_family) { \ + case AF_INET: \ + _t = L3_CONFIG_DATA_TYPE_DEVIP_4; \ + break; \ + case AF_INET6: \ + _t = L3_CONFIG_DATA_TYPE_DEVIP_6; \ + break; \ + default: \ + nm_assert_not_reached(); \ + /* fall-through */ \ + case AF_UNSPEC: \ + _t = L3_CONFIG_DATA_TYPE_DEVIP_UNSPEC; \ + break; \ + } \ + \ + _t; \ + }) + + L3_CONFIG_DATA_TYPE_MANUALIP, + _L3_CONFIG_DATA_TYPE_NUM, _L3_CONFIG_DATA_TYPE_NONE, + _L3_CONFIG_DATA_TYPE_ACD_ONLY, } L3ConfigDataType; -typedef struct { - AcdCallback callback; - NMDevice * device; - NMIP4Config **configs; -} AcdData; +G_STATIC_ASSERT(NM_L3CFG_CONFIG_PRIORITY_IPV4LL == L3_CONFIG_DATA_TYPE_LL_4); typedef enum { HW_ADDR_TYPE_UNSET = 0, @@ -177,19 +205,74 @@ typedef enum { } FirewallState; typedef struct { - NMIPConfig *orig; /* the original configuration applied to the device */ - NMIPConfig *current; /* configuration after external changes. NULL means - * that the original configuration didn't change. */ -} AppliedConfig; + NMIPConfig *ip_config; +} L3IPData; typedef struct { - NMDhcpClient *client; - NMDhcpConfig *config; - gulong notify_sigid; - guint grace_id; - bool grace_pending : 1; - bool was_active : 1; -} DhcpData; + GSource *check_async_source; + GSource *req_timeout_source; + union { + const NMDeviceIPState state; + NMDeviceIPState state_; + }; + bool wait_for_carrier : 1; + bool wait_for_ports : 1; + bool is_disabled : 1; + bool is_ignore : 1; + bool do_reapply : 1; +} IPStateData; + +typedef struct { + NMDhcpClient * client; + NMDhcpConfig * config; + gulong notify_sigid; + NMDeviceIPState state; + union { + struct { + } v4; + struct { + guint needed_prefixes; + NMNDiscDHCPLevel mode; + } v6; + }; +} IPDhcpStateData; + +typedef struct { + NMDeviceIPState state; + NMDeviceStateReason failed_reason; +} IPDevStateData; + +typedef struct { + NMDeviceIPState state; + union { + struct { + NMDnsMasqManager * dnsmasq_manager; + NMNetnsSharedIPHandle *shared_ip_handle; + NMFirewallConfig * firewall_config; + gulong dnsmasq_state_id; + const NML3ConfigData * l3cd; + } v4; + struct { + } v6; + }; +} IPSharedStateData; + +typedef struct { + NMDeviceIPState state; + union { + struct { + NML3IPv4LL * ipv4ll; + NML3IPv4LLRegistration *ipv4ll_registation; + GSource * timeout_source; + } v4; + struct { + NML3IPv6LL * ipv6ll; + GSource * retry_source; + NML3IPv6LLState llstate; + struct in6_addr lladdr; + } v6; + }; +} IPLLStateData; struct _NMDeviceConnectivityHandle { CList concheck_lst; @@ -234,8 +317,7 @@ typedef struct { enum { STATE_CHANGED, AUTOCONNECT_ALLOWED, - IP4_CONFIG_CHANGED, - IP6_CONFIG_CHANGED, + L3CD_CHANGED, IP6_PREFIX_DELEGATED, IP6_SUBNET_NEEDED, REMOVED, @@ -260,6 +342,9 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDevice, PROP_IP4_ADDRESS, PROP_IP4_CONFIG, PROP_DHCP4_CONFIG, + +#define PROP_DHCPX_CONFIG(IS_IPv4) ((IS_IPv4) ? PROP_DHCP4_CONFIG : PROP_DHCP6_CONFIG) + PROP_IP6_CONFIG, PROP_DHCP6_CONFIG, PROP_STATE, @@ -294,8 +379,6 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDevice, PROP_PORTS, ); typedef struct _NMDevicePrivate { - bool in_state_changed; - guint device_link_changed_id; guint device_ip_link_changed_id; @@ -309,22 +392,12 @@ typedef struct _NMDevicePrivate { NMDeviceStateReason reason; } queued_state; - union { - struct { - guint queued_ip_config_id_6; - guint queued_ip_config_id_4; - }; - guint queued_ip_config_id_x[2]; - }; - struct { const char **arr; guint len; guint alloc; } pending_actions; - GSList *dad6_failed_addrs; - NMDBusTrackObjPath parent_device; char *udi; @@ -339,16 +412,34 @@ typedef struct _NMDevicePrivate { char * ip_iface_; }; + union { + NML3Cfg *const l3cfg; + NML3Cfg * l3cfg_; + }; + + union { + struct { + L3IPData l3ipdata_6; + L3IPData l3ipdata_4; + }; + L3IPData l3ipdata_x[2]; + }; + + NML3CfgCommitTypeHandle *l3cfg_commit_type; + union { const int ifindex; int ifindex_; }; + union { const int ip_ifindex; int ip_ifindex_; }; - NMNetnsSharedIPHandle *shared_ip_handle; + union { + const NML3ConfigData *d; + } l3cds[_L3_CONFIG_DATA_TYPE_NUM]; int parent_ifindex; @@ -371,9 +462,6 @@ typedef struct _NMDevicePrivate { bool real : 1; - bool update_ip_config_completed_v4 : 1; - bool update_ip_config_completed_v6 : 1; - NMDeviceType type; char * type_desc; NMLinkType link_type; @@ -387,6 +475,8 @@ typedef struct _NMDevicePrivate { bool hw_addr_perm_fake : 1; /* whether the permanent HW address could not be read and is a fake */ + guint8 in_state_changed : 4; + NMUtilsStableType current_stable_id_type : 3; bool nm_owned : 1; /* whether the device is a device owned and created by NM */ @@ -414,21 +504,8 @@ typedef struct _NMDevicePrivate { bool queued_act_request_is_waiting_for_carrier : 1; NMDBusTrackObjPath act_request; - union { - struct { - guint activation_source_id_6; - guint activation_source_id_4; /* for layer2 and IPv4. */ - }; - guint activation_source_id_x[2]; - }; - - union { - struct { - ActivationHandleFunc activation_source_func_6; - ActivationHandleFunc activation_source_func_4; /* for layer2 and IPv4. */ - }; - ActivationHandleFunc activation_source_func_x[2]; - }; + GSource * activation_idle_source; + ActivationHandleFunc activation_func; guint recheck_assume_id; @@ -452,7 +529,7 @@ typedef struct _NMDevicePrivate { gulong config_changed_id; gulong ifindex_changed_id; guint32 mtu; - guint32 ip6_mtu; + guint32 ip6_mtu; /* FIXME(l3cfg) */ guint32 mtu_initial; guint32 ip6_mtu_initial; NMDeviceMtuSource mtu_source; @@ -468,6 +545,14 @@ typedef struct _NMDevicePrivate { * in the future. This is used to extend the grace period in this particular case. */ gint64 carrier_wait_until_ms; + union { + struct { + NML3ConfigMergeFlags l3config_merge_flags_6; + NML3ConfigMergeFlags l3config_merge_flags_4; + }; + NML3ConfigMergeFlags l3config_merge_flags_x[2]; + }; + union { const NMDeviceSysIfaceState sys_iface_state; NMDeviceSysIfaceState sys_iface_state_; @@ -478,9 +563,6 @@ typedef struct _NMDevicePrivate { bool up : 1; /* IFF_UP */ - bool v4_commit_first_time : 1; - bool v6_commit_first_time : 1; - bool default_route_metric_penalty_ip4_has : 1; bool default_route_metric_penalty_ip6_has : 1; @@ -496,103 +578,95 @@ typedef struct _NMDevicePrivate { bool is_enslaved : 1; - bool ipv6ll_handle : 1; /* TRUE if NM handles the device's IPv6LL address */ - bool ipv6ll_has : 1; - bool ndisc_started : 1; bool device_link_changed_down : 1; bool concheck_rp_filter_checked : 1; - NMDeviceStageState stage1_sriov_state : 3; - - bool ip_config_started : 1; bool tc_committed : 1; + NMDeviceStageState stage1_sriov_state : 3; + char *current_stable_id; - union { - struct { - GSource *ip_req_timeout_source_6; - GSource *ip_req_timeout_source_4; - }; - GSource *ip_req_timeout_source_x[2]; - }; - - /* Proxy Configuration */ - NMProxyConfig * proxy_config; NMPacrunnerConfId *pacrunner_conf_id; - /* IP configuration info. Combined config from VPN, settings, and device */ - union { - struct { - NMIP6Config *ip_config_6; - NMIP4Config *ip_config_4; - }; - NMIPConfig *ip_config_x[2]; - }; - - /* Config from DHCP, PPP, LLv4, etc */ - AppliedConfig dev_ip_config_4; - - /* config from the setting */ - union { - struct { - NMIP6Config *con_ip_config_6; - NMIP4Config *con_ip_config_4; - }; - NMIPConfig *con_ip_config_x[2]; - }; - - /* Stuff added outside NM */ - union { - struct { - NMIP6Config *ext_ip_config_6; - NMIP4Config *ext_ip_config_4; - }; - NMIPConfig *ext_ip_config_x[2]; - }; - - /* VPNs which use this device */ - union { - struct { - GSList *vpn_configs_6; - GSList *vpn_configs_4; - }; - GSList *vpn_configs_x[2]; - }; - - /* Extra device configuration, injected by the subclass of NMDevice. - * This is used for example by NMDeviceModem for WWAN configuration. */ - union { - struct { - AppliedConfig dev2_ip_config_6; - AppliedConfig dev2_ip_config_4; - }; - AppliedConfig dev2_ip_config_x[2]; - }; - - /* DHCPv4 tracking */ struct { - char *pac_url; - } dhcp4; + union { + const NMDeviceIPState state; + NMDeviceIPState state_; + }; + } ip_data; + + union { + struct { + IPStateData ip_data_6; + IPStateData ip_data_4; + }; + IPStateData ip_data_x[2]; + }; struct { - /* IP6 config from DHCP */ - AppliedConfig ip6_config; - /* Event ID of the current IP6 config from DHCP */ - char * event_id; - NMNDiscDHCPLevel mode; - guint needed_prefixes; - } dhcp6; + GSource *carrier_timeout; + union { + struct { + NMDeviceIPState state_6; + NMDeviceIPState state_4; + }; + NMDeviceIPState state_x[2]; + }; + bool carrier_timeout_expired; + } ipmanual_data; union { struct { - DhcpData dhcp_data_6; - DhcpData dhcp_data_4; + IPDhcpStateData ipdhcp_data_6; + IPDhcpStateData ipdhcp_data_4; }; - DhcpData dhcp_data_x[2]; + IPDhcpStateData ipdhcp_data_x[2]; }; + struct { + NMNDisc * ndisc; + GSource * ndisc_grace_source; + gulong ndisc_changed_id; + gulong ndisc_timeout_id; + NMDeviceIPState state; + } ipac6_data; + + union { + struct { + IPLLStateData ipll_data_6; + IPLLStateData ipll_data_4; + }; + IPLLStateData ipll_data_x[2]; + }; + + union { + struct { + IPSharedStateData ipshared_data_6; + IPSharedStateData ipshared_data_4; + }; + IPSharedStateData ipshared_data_x[2]; + }; + + union { + struct { + IPDevStateData ipdev_data_6; + IPDevStateData ipdev_data_4; + }; + IPDevStateData ipdev_data_x[2]; + }; + + IPDevStateData ipdev_data_unspec; + + struct { + /* If we set the addrgenmode6, this records the previously set value. */ + guint8 previous_mode_val; + + /* whether @previous_mode_val is set. */ + bool previous_mode_has : 1; + } addrgenmode6_data; + struct { NMLogDomain log_domain; guint timeout; @@ -603,58 +677,15 @@ typedef struct _NMDevicePrivate { guint deadline; } gw_ping; - /* dnsmasq stuff for shared connections */ - NMDnsMasqManager *dnsmasq_manager; - gulong dnsmasq_state_id; - /* Firewall */ FirewallState fw_state : 4; NMFirewalldManager * fw_mgr; NMFirewalldManagerCallId *fw_call; - /* IPv4LL stuff */ - sd_ipv4ll *ipv4ll; - guint ipv4ll_timeout; - guint rt6_temporary_not_available_id; - - /* IPv4 DAD stuff */ - struct { - GSList * dad_list; - NMAcdManager *announcing; - } acd; - - union { - struct { - const NMDeviceIPState ip_state_6; - const NMDeviceIPState ip_state_4; - }; - union { - const NMDeviceIPState ip_state_x[2]; - NMDeviceIPState ip_state_x_[2]; - }; - }; - - AppliedConfig ac_ip6_config; /* config from IPv6 autoconfiguration */ - NMIP6Config * ext_ip6_config_captured; /* Configuration captured from platform. */ - NMIP6Config * dad6_ip6_config; - struct in6_addr ipv6ll_addr; - - GHashTable *rt6_temporary_not_available; - - NMNDisc * ndisc; - gulong ndisc_changed_id; - gulong ndisc_timeout_id; - NMSettingIP6ConfigPrivacy ndisc_use_tempaddr; - - guint linklocal6_timeout_id; - guint8 linklocal6_dad_counter; - GHashTable *ip6_saved_properties; EthtoolState *ethtool_state; - gboolean needs_ip6_subnet; - /* master interface for bridge/bond/team slave */ NMDevice *master; gulong master_ready_id; @@ -710,6 +741,8 @@ typedef struct _NMDevicePrivate { bool mtu_force_set_done : 1; + bool needs_ip6_subnet : 1; + NMOptionBool promisc_reset; GVariant *ports_variant; /* Array of port devices D-Bus path */ @@ -724,56 +757,79 @@ G_DEFINE_ABSTRACT_TYPE(NMDevice, nm_device, NM_TYPE_DBUS_OBJECT) static const NMDBusInterfaceInfoExtended interface_info_device; static const GDBusSignalInfo signal_info_state_changed; -static void nm_device_set_proxy_config(NMDevice *self, const char *pac_url); +static void _dev_l3_cfg_commit(NMDevice *self, gboolean do_sync); -static gboolean update_ext_ip_config(NMDevice *self, int addr_family, gboolean intersect_configs); - -static gboolean nm_device_set_ip_config(NMDevice * self, - int addr_family, - NMIPConfig *config, - gboolean commit, - GPtrArray * ip4_dev_route_blacklist); - -static gboolean ip_config_merge_and_apply(NMDevice *self, int addr_family, gboolean commit); +static void _dev_l3_cfg_commit_type_reset(NMDevice *self); static gboolean nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure); static void nm_device_slave_notify_enslave(NMDevice *self, gboolean success); static void nm_device_slave_notify_release(NMDevice *self, NMDeviceStateReason reason); -static void addrconf6_start_with_link_ready(NMDevice *self); -static gboolean linklocal6_start(NMDevice *self); +static void _dev_ipll6_start(NMDevice *self); -static guint32 default_route_metric_penalty_get(NMDevice *self, int addr_family); +static void _dev_ipac6_start_continue(NMDevice *self); +static void _dev_ipac6_ndisc_set_router_config(NMDevice *self); -static guint _prop_get_ipv4_dad_timeout(NMDevice *self); +static guint32 _dev_default_route_metric_penalty_get(NMDevice *self, int addr_family); -static NMIP6Config *dad6_get_pending_addresses(NMDevice *self); +static guint32 _prop_get_ipv4_dad_timeout(NMDevice *self); static void _carrier_wait_check_queued_act_request(NMDevice *self); static gint64 _get_carrier_wait_ms(NMDevice *self); +static GBytes *_prop_get_ipv6_dhcp_duid(NMDevice * self, + NMConnection *connection, + GBytes * hwaddr, + gboolean * out_enforce); + static const char *_activation_func_to_string(ActivationHandleFunc func); static void _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, gboolean quitting); -static void queued_state_clear(NMDevice *device); -static gboolean queued_ip4_config_change(gpointer user_data); -static gboolean queued_ip6_config_change(gpointer user_data); -static void ip_check_ping_watch_cb(GPid pid, int status, gpointer user_data); -static gboolean ip_config_valid(NMDeviceState state); -static NMActStageReturn dhcp4_start(NMDevice *self); -static gboolean dhcp6_start(NMDevice *self, gboolean wait_for_ll); -static void nm_device_start_ip_check(NMDevice *self); -static void realize_start_setup(NMDevice * self, - const NMPlatformLink *plink, - gboolean assume_state_guess_assume, - const char * assume_state_connection_uuid, - gboolean set_nm_owned, - NMUnmanFlagOp unmanaged_user_explicit, - gboolean force_platform_init); -static void _set_mtu(NMDevice *self, guint32 mtu); -static void _commit_mtu(NMDevice *self, const NMIP4Config *config); -static void _cancel_activation(NMDevice *self); +static void queued_state_clear(NMDevice *device); +static void ip_check_ping_watch_cb(GPid pid, int status, gpointer user_data); +static void nm_device_start_ip_check(NMDevice *self); +static void realize_start_setup(NMDevice * self, + const NMPlatformLink *plink, + gboolean assume_state_guess_assume, + const char * assume_state_connection_uuid, + gboolean set_nm_owned, + NMUnmanFlagOp unmanaged_user_explicit, + gboolean force_platform_init); +static void _set_mtu(NMDevice *self, guint32 mtu); +static void _commit_mtu(NMDevice *self); +static void _cancel_activation(NMDevice *self); + +static void _dev_ipll4_notify_event(NMDevice *self); + +static void _dev_ip_state_check(NMDevice *self, int addr_family); + +static void _dev_ipmanual_check_ready(NMDevice *self); + +static void +_dev_ipdhcpx_cleanup(NMDevice *self, int addr_family, gboolean reset_dhcp_config, gboolean release); + +static void _dev_ip_state_check_async(NMDevice *self, int addr_family); + +static void _dev_ipdhcpx_set_state(NMDevice *self, int addr_family, NMDeviceIPState state); + +static void _dev_ipdhcpx_restart(NMDevice *self, int addr_family, gboolean release); + +static void _dev_ipdhcpx_handle_accept(NMDevice *self, int addr_family, const NML3ConfigData *l3cd); + +static gboolean +_dev_ipac6_grace_period_start(NMDevice *self, guint32 timeout_sec, gboolean force_restart); + +static void _dev_ipac6_start(NMDevice *self); + +static void _dev_unamanged_check_external_down(NMDevice *self, gboolean only_if_unmanaged); + +static void _dev_ipshared4_start(NMDevice *self); +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); static void concheck_update_state(NMDevice * self, int addr_family, @@ -783,8 +839,80 @@ static void concheck_update_state(NMDevice * self, static void sriov_op_cb(GError *error, gpointer user_data); static void device_ifindex_changed_cb(NMManager *manager, NMDevice *device_changed, NMDevice *self); -static gboolean device_link_changed(NMDevice *self); -static void check_ip_state(NMDevice *self, gboolean may_fail, gboolean full_state_update); +static gboolean device_link_changed(gpointer user_data); +static gboolean _get_maybe_ipv6_disabled(NMDevice *self); + +/*****************************************************************************/ + +#define _NMLOG_addr_family(level, prefix, addr_family, fmt, ...) \ + G_STMT_START \ + { \ + const int _addr_family2 = (addr_family); \ + \ + _NMLOG(level, \ + (_addr_family2 == AF_UNSPEC ? LOGD_IP : LOGD_IPX(NM_IS_IPv4(_addr_family2))), \ + "" prefix "%s: " fmt, \ + nm_utils_addr_family_to_str(_addr_family2), \ + ##__VA_ARGS__); \ + } \ + G_STMT_END + +#define _NMLOG_ip(level, ...) _NMLOG_addr_family(level, "ip", __VA_ARGS__) +#define _LOGT_ip(...) _NMLOG_ip(LOGL_TRACE, __VA_ARGS__) +#define _LOGD_ip(...) _NMLOG_ip(LOGL_DEBUG, __VA_ARGS__) +#define _LOGI_ip(...) _NMLOG_ip(LOGL_INFO, __VA_ARGS__) +#define _LOGW_ip(...) _NMLOG_ip(LOGL_WARN, __VA_ARGS__) + +#define _NMLOG_ipll(level, ...) _NMLOG_addr_family(level, "ip:ll", __VA_ARGS__) +#define _LOGT_ipll(...) _NMLOG_ipll(LOGL_TRACE, __VA_ARGS__) +#define _LOGD_ipll(...) _NMLOG_ipll(LOGL_DEBUG, __VA_ARGS__) +#define _LOGI_ipll(...) _NMLOG_ipll(LOGL_INFO, __VA_ARGS__) +#define _LOGW_ipll(...) _NMLOG_ipll(LOGL_WARN, __VA_ARGS__) + +#define _NMLOG_ipdev(level, ...) _NMLOG_addr_family(level, "ip:dev", __VA_ARGS__) +#define _LOGT_ipdev(...) _NMLOG_ipdev(LOGL_TRACE, __VA_ARGS__) +#define _LOGD_ipdev(...) _NMLOG_ipdev(LOGL_DEBUG, __VA_ARGS__) +#define _LOGI_ipdev(...) _NMLOG_ipdev(LOGL_INFO, __VA_ARGS__) +#define _LOGW_ipdev(...) _NMLOG_ipdev(LOGL_WARN, __VA_ARGS__) + +#define _NMLOG_ipdhcp(level, ...) _NMLOG_addr_family(level, "ip:dhcp", __VA_ARGS__) +#define _LOGT_ipdhcp(...) _NMLOG_ipdhcp(LOGL_TRACE, __VA_ARGS__) +#define _LOGD_ipdhcp(...) _NMLOG_ipdhcp(LOGL_DEBUG, __VA_ARGS__) +#define _LOGI_ipdhcp(...) _NMLOG_ipdhcp(LOGL_INFO, __VA_ARGS__) +#define _LOGW_ipdhcp(...) _NMLOG_ipdhcp(LOGL_WARN, __VA_ARGS__) + +#define _NMLOG_ipshared(level, ...) _NMLOG_addr_family(level, "ip:shared", __VA_ARGS__) +#define _LOGT_ipshared(...) _NMLOG_ipshared(LOGL_TRACE, __VA_ARGS__) +#define _LOGD_ipshared(...) _NMLOG_ipshared(LOGL_DEBUG, __VA_ARGS__) +#define _LOGI_ipshared(...) _NMLOG_ipshared(LOGL_INFO, __VA_ARGS__) +#define _LOGW_ipshared(...) _NMLOG_ipshared(LOGL_WARN, __VA_ARGS__) + +#define _NMLOG_ipac6(level, ...) _NMLOG_addr_family(level, "ip:ac6", AF_UNSPEC, __VA_ARGS__) +#define _LOGT_ipac6(...) _NMLOG_ipac6(LOGL_TRACE, __VA_ARGS__) +#define _LOGD_ipac6(...) _NMLOG_ipac6(LOGL_DEBUG, __VA_ARGS__) +#define _LOGI_ipac6(...) _NMLOG_ipac6(LOGL_INFO, __VA_ARGS__) +#define _LOGW_ipac6(...) _NMLOG_ipac6(LOGL_WARN, __VA_ARGS__) + +#define _NMLOG_ipmanual(level, ...) _NMLOG_addr_family(level, "ip:manual", __VA_ARGS__) +#define _LOGT_ipmanual(...) _NMLOG_ipmanual(LOGL_TRACE, __VA_ARGS__) +#define _LOGD_ipmanual(...) _NMLOG_ipmanual(LOGL_DEBUG, __VA_ARGS__) +#define _LOGI_ipmanual(...) _NMLOG_ipmanual(LOGL_INFO, __VA_ARGS__) +#define _LOGW_ipmanual(...) _NMLOG_ipmanual(LOGL_WARN, __VA_ARGS__) + +/*****************************************************************************/ + +#define _CACHED_BOOL(cached_value, cmd) \ + ({ \ + NMTernary *const _cached_value = (cached_value); \ + \ + nm_assert(_cached_value); \ + nm_assert_is_ternary(*_cached_value); \ + \ + if (*_cached_value == NM_TERNARY_DEFAULT) \ + *_cached_value = !!(cmd); \ + \ + !!(*_cached_value); \ + }) /*****************************************************************************/ @@ -1335,7 +1463,7 @@ _prop_get_connection_lldp(NMDevice *self) return lldp == NM_SETTING_CONNECTION_LLDP_ENABLE_RX; } -static guint +static guint32 _prop_get_ipv4_dad_timeout(NMDevice *self) { NMConnection * connection; @@ -1347,6 +1475,9 @@ _prop_get_ipv4_dad_timeout(NMDevice *self) s_ip4 = nm_connection_get_setting_ip4_config(connection); if (s_ip4) timeout = nm_setting_ip_config_get_dad_timeout(s_ip4); + + nm_assert(timeout >= -1 && timeout <= NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX); + if (timeout >= 0) return timeout; @@ -1363,8 +1494,8 @@ _prop_get_ipvx_dhcp_timeout(NMDevice *self, int addr_family) { NMDeviceClass *klass; NMConnection * connection; - int timeout_i; guint32 timeout; + int timeout_i; nm_assert(NM_IS_DEVICE(self)); nm_assert_addr_family(addr_family); @@ -1406,6 +1537,37 @@ out: return timeout; } +static guint32 +_prop_get_ipvx_dns_priority(NMDevice *self, int addr_family) +{ + NMConnection * connection; + NMSettingIPConfig *s_ip; + int prio = 0; + + connection = nm_device_get_applied_connection(self); + s_ip = nm_connection_get_setting_ip_config(connection, addr_family); + if (s_ip) + prio = nm_setting_ip_config_get_dns_priority(s_ip); + + if (prio == 0) { + prio = nm_config_data_get_connection_default_int64( + NM_CONFIG_GET_DATA, + NM_IS_IPv4(addr_family) ? NM_CON_DEFAULT("ipv4.dns-priority") + : NM_CON_DEFAULT("ipv6.dns-priority"), + self, + G_MININT32, + G_MAXINT32, + 0); + if (prio == 0) { + prio = nm_device_is_vpn(self) ? NM_DNS_PRIORITY_DEFAULT_VPN + : NM_DNS_PRIORITY_DEFAULT_NORMAL; + } + } + + nm_assert(prio != 0); + return prio; +} + static guint32 _prop_get_ipvx_required_timeout(NMDevice *self, int addr_family) { @@ -1440,6 +1602,25 @@ _prop_get_ipvx_required_timeout(NMDevice *self, int addr_family) 0); } +static gboolean +_prop_get_ipvx_may_fail(NMDevice *self, int addr_family) +{ + NMConnection * connection; + NMSettingIPConfig *s_ip = NULL; + + connection = nm_device_get_applied_connection(self); + if (connection) + s_ip = nm_connection_get_setting_ip_config(connection, addr_family); + + return !s_ip || nm_setting_ip_config_get_may_fail(s_ip); +} + +static gboolean +_prop_get_ipvx_may_fail_cached(NMDevice *self, int addr_family, NMTernary *cache) +{ + return _CACHED_BOOL(cache, _prop_get_ipvx_may_fail(self, addr_family)); +} + /** * _prop_get_ipvx_dhcp_iaid: * @self: the #NMDevice @@ -2416,27 +2597,6 @@ concheck_get_mgr(NMDevice *self) return priv->concheck_mgr; } -NMIP4Config * -nm_device_ip4_config_new(NMDevice *self) -{ - return nm_ip4_config_new(nm_device_get_multi_index(self), nm_device_get_ip_ifindex(self)); -} - -NMIP6Config * -nm_device_ip6_config_new(NMDevice *self) -{ - return nm_ip6_config_new(nm_device_get_multi_index(self), nm_device_get_ip_ifindex(self)); -} - -NMIPConfig * -nm_device_ip_config_new(NMDevice *self, int addr_family) -{ - nm_assert_addr_family(addr_family); - - return NM_IS_IPv4(addr_family) ? (gpointer) nm_device_ip4_config_new(self) - : (gpointer) nm_device_ip6_config_new(self); -} - NML3ConfigData * nm_device_create_l3_config_data(NMDevice *self, NMIPConfigSource source) { @@ -2451,98 +2611,28 @@ nm_device_create_l3_config_data(NMDevice *self, NMIPConfigSource source) return nm_l3_config_data_new(nm_device_get_multi_index(self), ifindex, source); } -static void -applied_config_clear(AppliedConfig *config) +const NML3ConfigData * +nm_device_create_l3_config_data_from_connection(NMDevice *self, NMConnection *connection) { - g_clear_object(&config->current); - g_clear_object(&config->orig); -} + NML3ConfigData *l3cd; + int ifindex; -static void -applied_config_init(AppliedConfig *config, gpointer ip_config) -{ - nm_assert(!ip_config || (!config->orig && !config->current) - || nm_ip_config_get_addr_family(ip_config) - == nm_ip_config_get_addr_family(config->orig ?: config->current)); - nm_assert(!ip_config || NM_IS_IP_CONFIG(ip_config)); + nm_assert(NM_IS_DEVICE(self)); + nm_assert(!connection || NM_IS_CONNECTION(connection)); - nm_g_object_ref(ip_config); - applied_config_clear(config); - config->orig = ip_config; -} + if (!connection) + return NULL; -static void -applied_config_init_new(AppliedConfig *config, NMDevice *self, int addr_family) -{ - gs_unref_object NMIPConfig *c = nm_device_ip_config_new(self, addr_family); + ifindex = nm_device_get_ip_ifindex(self); + if (ifindex <= 0) + g_return_val_if_reached(NULL); - applied_config_init(config, c); -} - -static NMIPConfig * -applied_config_get_current(AppliedConfig *config) -{ - return config->current ?: config->orig; -} - -static void -applied_config_add_address(AppliedConfig *config, const NMPlatformIPAddress *address) -{ - if (config->orig) - nm_ip_config_add_address(config->orig, address); - else - nm_assert(!config->current); - - if (config->current) - nm_ip_config_add_address(config->current, address); -} - -static void -applied_config_add_nameserver(AppliedConfig *config, const NMIPAddr *ns) -{ - if (config->orig) - nm_ip_config_add_nameserver(config->orig, ns); - else - nm_assert(!config->current); - - if (config->current) - nm_ip_config_add_nameserver(config->current, ns); -} - -static void -applied_config_add_search(AppliedConfig *config, const char *new) -{ - if (config->orig) - nm_ip_config_add_search(config->orig, new); - else - nm_assert(!config->current); - - if (config->current) - nm_ip_config_add_search(config->current, new); -} - -static void -applied_config_reset_searches(AppliedConfig *config) -{ - if (config->orig) - nm_ip_config_reset_searches(config->orig); - else - nm_assert(!config->current); - - if (config->current) - nm_ip_config_reset_searches(config->current); -} - -static void -applied_config_reset_nameservers(AppliedConfig *config) -{ - if (config->orig) - nm_ip_config_reset_nameservers(config->orig); - else - nm_assert(!config->current); - - if (config->current) - nm_ip_config_reset_nameservers(config->current); + l3cd = + nm_l3_config_data_new_from_connection(nm_device_get_multi_index(self), ifindex, connection); + nm_l3_config_data_set_mdns(l3cd, _prop_get_connection_mdns(self)); + nm_l3_config_data_set_llmnr(l3cd, _prop_get_connection_llmnr(self)); + nm_l3_config_data_set_dns_over_tls(l3cd, _prop_get_connection_dns_over_tls(self)); + return l3cd; } /*****************************************************************************/ @@ -2588,6 +2678,7 @@ nm_device_sys_iface_state_set(NMDevice *self, NMDeviceSysIfaceState sys_iface_st nm_device_sys_iface_state_to_string(priv->sys_iface_state), nm_device_sys_iface_state_to_string(sys_iface_state)); priv->sys_iface_state_ = sys_iface_state; + _dev_l3_cfg_commit_type_reset(self); } /* this function only sets a flag, no immediate actions are initiated. @@ -2700,33 +2791,6 @@ nm_device_assume_state_reset(NMDevice *self) /*****************************************************************************/ -static void -init_ip_config_dns_priority(NMDevice *self, NMIPConfig *config) -{ - const char *property; - int priority; - - property = (nm_ip_config_get_addr_family(config) == AF_INET) - ? NM_CON_DEFAULT("ipv4.dns-priority") - : NM_CON_DEFAULT("ipv6.dns-priority"); - - priority = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, - property, - self, - G_MININT, - G_MAXINT, - 0); - - if (priority == 0) { - priority = - nm_device_is_vpn(self) ? NM_DNS_PRIORITY_DEFAULT_VPN : NM_DNS_PRIORITY_DEFAULT_NORMAL; - } - - nm_ip_config_set_dns_priority(config, priority); -} - -/*****************************************************************************/ - static char * nm_device_sysctl_ip_conf_get(NMDevice *self, int addr_family, const char *property) { @@ -2863,95 +2927,1005 @@ _add_capabilities(NMDevice *self, NMDeviceCapabilities capabilities) /*****************************************************************************/ -static gboolean -ip_required_timeout_x(NMDevice *self, int addr_family) +static void +_dev_ip_state_req_timeout_cancel(NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - _LOGD(LOGD_CORE, - "required-timeout expired for IPv%c", - nm_utils_addr_family_to_char(addr_family)); - nm_clear_g_source_inst(&priv->ip_req_timeout_source_x[NM_IS_IPv4(addr_family)]); - check_ip_state(self, FALSE, TRUE); + if (addr_family == AF_UNSPEC) { + _dev_ip_state_req_timeout_cancel(self, AF_INET); + _dev_ip_state_req_timeout_cancel(self, AF_INET6); + return; + } + + if (nm_clear_g_source_inst(&priv->ip_data_x[NM_IS_IPv4(addr_family)].req_timeout_source)) + _LOGD_ip(addr_family, "required-timeout: cancelled"); +} + +static gboolean +_dev_ip_state_req_timeout_cb_x(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + _LOGD_ip(addr_family, "required-timeout: expired"); + nm_clear_g_source_inst(&priv->ip_data_x[NM_IS_IPv4(addr_family)].req_timeout_source); + _dev_ip_state_check(self, addr_family); return G_SOURCE_CONTINUE; } static gboolean -ip_required_timeout_4(gpointer data) +_dev_ip_state_req_timeout_cb_4(gpointer user_data) { - return ip_required_timeout_x(data, AF_INET); + return _dev_ip_state_req_timeout_cb_x(user_data, AF_INET); } static gboolean -ip_required_timeout_6(gpointer data) +_dev_ip_state_req_timeout_cb_6(gpointer user_data) { - return ip_required_timeout_x(data, AF_INET6); + return _dev_ip_state_req_timeout_cb_x(user_data, AF_INET6); } static void -_set_ip_state(NMDevice *self, int addr_family, NMDeviceIPState new_state) +_dev_ip_state_req_timeout_schedule(NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); const int IS_IPv4 = NM_IS_IPv4(addr_family); - guint timeout_msec; - int v4; + guint32 timeout_msec; + char buf[32]; - nm_assert_addr_family(addr_family); + nm_assert(!priv->ip_data_x[IS_IPv4].req_timeout_source); - if (new_state == NM_DEVICE_IP_STATE_CONF && !priv->ip_config_started) { - /* Start the required-timeout timers when one of IPv4/IPv6 - * enters the CONF state. This means that if there is no carrier and - * ipv4.method=auto,ipv6.method=manual, the timeout for IPv4 will - * start as soon as connection is activated, even if DHCPv4 did not - * start yet. - */ - priv->ip_config_started = TRUE; + timeout_msec = _prop_get_ipvx_required_timeout(self, addr_family); + if (timeout_msec == 0) { + _LOGD_ip(addr_family, "required-timeout: disabled"); + return; + } - for (v4 = 1; v4 >= 0; v4--) { - char buf[32]; + _LOGD_ip(addr_family, + "required-timeout: started (%s msec)", + timeout_msec == G_MAXINT32 ? "∞" : nm_sprintf_buf(buf, "%u", timeout_msec)); - nm_assert(!priv->ip_req_timeout_source_x[v4]); - if ((timeout_msec = _prop_get_ipvx_required_timeout(self, v4 ? AF_INET : AF_INET6))) { - _LOGD(LOGD_CORE, - "required-timeout in %s msec for IPv%c", - timeout_msec == G_MAXINT32 ? "∞" : nm_sprintf_buf(buf, "%u", timeout_msec), - v4 ? '4' : '6'); + if (timeout_msec == G_MAXINT32) { + priv->ip_data_x[IS_IPv4].req_timeout_source = g_source_ref(nm_g_source_sentinel_get(0)); + } else { + priv->ip_data_x[IS_IPv4].req_timeout_source = nm_g_timeout_add_source( + timeout_msec, + IS_IPv4 ? _dev_ip_state_req_timeout_cb_4 : _dev_ip_state_req_timeout_cb_6, + self); + } +} - if (timeout_msec == G_MAXINT32) { - priv->ip_req_timeout_source_x[v4] = g_source_ref(nm_g_source_sentinel_get(0)); - } else { - priv->ip_req_timeout_source_x[v4] = - nm_g_timeout_add_source(timeout_msec, - v4 ? ip_required_timeout_4 : ip_required_timeout_6, - self); +static gboolean +_dev_ip_state_set_state(NMDevice * self, + int addr_family, + NMDeviceIPState ip_state, + const char * reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + int IS_IPv4; + + if (addr_family == AF_UNSPEC) { + if (priv->ip_data.state == ip_state) + return FALSE; + _LOGD_ip(addr_family, + "set (combined) state %s (was %s, reason: %s)", + nm_device_ip_state_to_string(ip_state), + nm_device_ip_state_to_string(priv->ip_data.state), + reason); + priv->ip_data.state_ = ip_state; + return TRUE; + } + + IS_IPv4 = NM_IS_IPv4(addr_family); + + if (priv->ip_data_x[IS_IPv4].state_ == ip_state) + return FALSE; + + _LOGD_ip(addr_family, + "set state %s (was %s, reason: %s)", + nm_device_ip_state_to_string(ip_state), + nm_device_ip_state_to_string(priv->ip_data_x[IS_IPv4].state), + reason); + priv->ip_data_x[IS_IPv4].state_ = ip_state; + return TRUE; +} + +static void +_device_ip_state_accumulate(NMDeviceIPState state, + gboolean * out_is_started, + gboolean * out_is_pending, + gboolean * out_is_failed) +{ + switch (state) { + case NM_DEVICE_IP_STATE_NONE: + return; + case NM_DEVICE_IP_STATE_PENDING: + *out_is_started = TRUE; + *out_is_pending = TRUE; + return; + case NM_DEVICE_IP_STATE_READY: + *out_is_started = TRUE; + return; + case NM_DEVICE_IP_STATE_FAILED: + *out_is_started = TRUE; + *out_is_failed = TRUE; + return; + } + nm_assert_not_reached(); + return; +} + +static void +_dev_ip_state_check(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + gboolean s_is_started = FALSE; + gboolean s_is_failed = FALSE; + gboolean s_is_pending = FALSE; + gboolean v_bool; + NMDeviceIPState ip_state; + NMDeviceIPState ip_state_other; + NMDeviceIPState combinedip_state; + NMTernary may_fail = NM_TERNARY_DEFAULT; + NMTernary may_fail_other = NM_TERNARY_DEFAULT; + + /* State handling in NMDevice: + * + * NMDevice manages a lot of state, that is for the various IP addressing methods, the state + * of the interface (controller/port), and a overall nm_device_get_state(). + * + * The idea is to compartmentalize these states into smaller units, and combine them as appropriate. + * + * For example, NMDhcpClient already provides an API that hides most of the complexity. But it still + * needs to expose some state, like whether we are still trying to get a lease (PENDING), whether there + * was a critical failure (FAILED) or we have a lease (READY). This state is grouped in NMDevice + * under priv->ipdhcp_data_x. Most important is priv->ipdhcp_data_x[].state, which distills all of this + * into 4 values of type NMDeviceIPState. This state is cached, so whenever something changes (e.g. + * an event from NMDhcpClient), we determine the new state and compare it with what is cached. If + * the cached state is as the new state, we are done. Otherwise, the change gets escalated (which + * means to call _dev_ip_state_check_async()). + * + * Then, the various sub-states escalate their changes to this function (_dev_ip_state_check). This + * function first takes the sub-states related to one IP address family, and combines them into + * priv->ip_data_x[] (and in particular priv->ip_data_x[].state). The same repeats. The current + * state is cached in priv->ip_data_x[].state, and _dev_ip_state_check() determines the new state. + * If there is no change, it ends here. Otherwise, it gets escalated. In this case, the escaplation + * happens in _dev_ip_state_check() below by combining the combined per-address-family into + * priv->ip_data. In particular this step needs to take into account settings like "may-fail" + * and "required-timeout". + * + * The escalation and compartmentalization priv->ip_data repeats. This time it escalates + * to the overall device state (nm_device_state_changed() and nm_device_get_state()), which then + * triggers larger state changes (e.g. the activation might fail). + */ + + if (priv->l3cfg && nm_l3cfg_commit_on_idle_is_scheduled(priv->l3cfg)) { + /* we have an update on NML3Cfg scheduled. We first process that, before + * progressing the IP state. When that's done, we will be called again. */ + _dev_ip_state_check_async(self, addr_family); + return; + } + + if (priv->ip_data_x[IS_IPv4].state == NM_DEVICE_IP_STATE_NONE) { + ip_state = NM_DEVICE_IP_STATE_NONE; + goto got_ip_state; + } + + if (nm_device_sys_iface_state_is_external(self)) { + ip_state = NM_DEVICE_IP_STATE_READY; + goto got_ip_state; + } + + if (priv->ip_data_x[IS_IPv4].state == NM_DEVICE_IP_STATE_PENDING + && (priv->state < NM_DEVICE_STATE_IP_CONFIG || priv->state > NM_DEVICE_STATE_ACTIVATED)) { + /* we can only leave pending state, if we are between (including) IP_CONFIG and ACTIVATED states. */ + ip_state = NM_DEVICE_IP_STATE_PENDING; + goto got_ip_state; + } + + if (priv->ip_data_x[IS_IPv4].state == NM_DEVICE_IP_STATE_PENDING + && nm_active_connection_get_master(NM_ACTIVE_CONNECTION(priv->act_request.obj)) + && !priv->is_enslaved) { + /* Don't progress into IP_CHECK or SECONDARIES if we're waiting for the + * master to enslave us. */ + ip_state = NM_DEVICE_IP_STATE_PENDING; + goto got_ip_state; + } + + if (priv->ip_data_x[IS_IPv4].wait_for_carrier || priv->ip_data_x[IS_IPv4].wait_for_ports) { + ip_state = NM_DEVICE_IP_STATE_PENDING; + goto got_ip_state; + } + + if (priv->ip_data_x[IS_IPv4].is_disabled || priv->ip_data_x[IS_IPv4].is_ignore) { + ip_state = NM_DEVICE_IP_STATE_READY; + goto got_ip_state; + } + + _device_ip_state_accumulate(priv->ipmanual_data.state_x[IS_IPv4], + &s_is_started, + &s_is_pending, + &s_is_failed); + + _device_ip_state_accumulate(priv->ipll_data_x[IS_IPv4].state, + &s_is_started, + &s_is_pending, + &s_is_failed); + + if (!IS_IPv4) { + _device_ip_state_accumulate(priv->ipac6_data.state, + &s_is_started, + &s_is_pending, + &s_is_failed); + } + + v_bool = FALSE; + _device_ip_state_accumulate(priv->ipdhcp_data_x[IS_IPv4].state, + &s_is_started, + &s_is_pending, + &v_bool); + if (v_bool) { + if (!IS_IPv4 && priv->ipdhcp_data_6.v6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) { + /* DHCPv6 is best-effort and not required. */ + } else + s_is_failed = TRUE; + } + + _device_ip_state_accumulate(priv->ipshared_data_x[IS_IPv4].state, + &s_is_started, + &s_is_pending, + &s_is_failed); + + _device_ip_state_accumulate(priv->ipdev_data_x[IS_IPv4].state, + &s_is_started, + &s_is_pending, + &s_is_failed); + + _device_ip_state_accumulate(priv->ipdev_data_unspec.state, + &s_is_started, + &s_is_pending, + &s_is_failed); + + if (s_is_failed) + ip_state = NM_DEVICE_IP_STATE_FAILED; + else if (s_is_pending) + ip_state = NM_DEVICE_IP_STATE_PENDING; + else if (s_is_started) + ip_state = NM_DEVICE_IP_STATE_READY; + else + ip_state = NM_DEVICE_IP_STATE_PENDING; + +got_ip_state: + +#define _state_str_a(state, name) \ + ({ \ + const NMDeviceIPState _state = (state); \ + char * _s = ""; \ + \ + if (_state != NM_DEVICE_IP_STATE_NONE) { \ + _s = nm_sprintf_bufa(NM_STRLEN(name) + 11, \ + " " name "=%s", \ + nm_device_ip_state_to_string(_state)); \ + } \ + _s; \ + }) + + nm_assert(!priv->ip_data_4.is_ignore); + + _LOGT_ip(addr_family, + "check-state: state %s => %s, is_failed=%d, is_pending=%d, is_started=%d, " + "may-fail-4=%d, may-fail-6=%d;" + "%s;%s%s%s%s%s%s;%s%s%s%s%s%s%s%s", + nm_device_ip_state_to_string(priv->ip_data_x[IS_IPv4].state), + nm_device_ip_state_to_string(ip_state), + s_is_failed, + s_is_pending, + s_is_started, + _prop_get_ipvx_may_fail_cached(self, AF_INET, IS_IPv4 ? &may_fail : &may_fail_other), + _prop_get_ipvx_may_fail_cached(self, AF_INET6, !IS_IPv4 ? &may_fail : &may_fail_other), + priv->ip_data_4.is_disabled ? " disabled4" : "", + _state_str_a(priv->ipmanual_data.state_4, "manualip4"), + _state_str_a(priv->ipdev_data_unspec.state, "dev"), + _state_str_a(priv->ipll_data_4.state, "ll4"), + _state_str_a(priv->ipdhcp_data_4.state, "dhcp4"), + _state_str_a(priv->ipdev_data_4.state, "dev4"), + _state_str_a(priv->ipshared_data_4.state, "shared4"), + priv->ip_data_6.is_disabled ? " disabled6" : "", + priv->ip_data_6.is_ignore ? " ignore6" : "", + _state_str_a(priv->ipmanual_data.state_6, "manualip6"), + _state_str_a(priv->ipll_data_6.state, "ll6"), + _state_str_a(priv->ipac6_data.state, "ac6"), + _state_str_a(priv->ipdhcp_data_6.state, "dhcp6"), + _state_str_a(priv->ipdev_data_6.state, "dev6"), + _state_str_a(priv->ipshared_data_6.state, "shared6")); + + if (priv->ip_data_x[IS_IPv4].state == ip_state) { + /* no change. We can stop here. However, we also cancel the pending check, if any, + * because we just determined that there is no change. */ + } else { + _dev_ip_state_set_state(self, addr_family, ip_state, "check-ip-state"); + } + + if (ip_state == NM_DEVICE_IP_STATE_NONE) { + /* Nothing to do. This almost cannot happen, and there is probably nothing + * to do about this case. */ + goto out_done; + } + + ip_state_other = priv->ip_data_x[!IS_IPv4].state; + + if (ip_state == NM_DEVICE_IP_STATE_READY) { + /* we only set NM_ACTIVATION_STATE_FLAG_IP_READY_X() flag once we reach NM_DEVICE_IP_STATE_READY state. + * We don't ever clear it, even if we later enter NM_DEVICE_IP_STATE_FAILED state. + * + * This is not documented/guaranteed behavior, but seems to make sense for now. */ + _active_connection_set_state_flags(self, NM_ACTIVATION_STATE_FLAG_IP_READY_X(IS_IPv4)); + } + + if (ip_state == NM_DEVICE_IP_STATE_READY && ip_state_other == NM_DEVICE_IP_STATE_READY) + combinedip_state = NM_DEVICE_IP_STATE_READY; + else if (ip_state == NM_DEVICE_IP_STATE_READY && ip_state_other == NM_DEVICE_IP_STATE_PENDING + && (priv->ip_data_x[IS_IPv4].is_disabled || priv->ip_data_x[IS_IPv4].is_ignore)) { + /* This IP method is disabled/ignore, but the other family is still pending. + * Regardless of ipvx.may-fail, this means that we always require the other IP family + * to get ready too. */ + combinedip_state = NM_DEVICE_IP_STATE_PENDING; + } else if (ip_state == NM_DEVICE_IP_STATE_READY && ip_state_other == NM_DEVICE_IP_STATE_PENDING + && (priv->ip_data_x[!IS_IPv4].req_timeout_source + || !_prop_get_ipvx_may_fail_cached(self, + nm_utils_addr_family_other(addr_family), + &may_fail_other))) + combinedip_state = NM_DEVICE_IP_STATE_PENDING; + else if (ip_state == NM_DEVICE_IP_STATE_READY + && NM_IN_SET(ip_state_other, + NM_DEVICE_IP_STATE_PENDING, + NM_DEVICE_IP_STATE_READY, + NM_DEVICE_IP_STATE_FAILED) + && _prop_get_ipvx_may_fail_cached(self, + nm_utils_addr_family_other(addr_family), + &may_fail_other)) + combinedip_state = NM_DEVICE_IP_STATE_READY; + else if (ip_state == NM_DEVICE_IP_STATE_FAILED + && !_prop_get_ipvx_may_fail_cached(self, addr_family, &may_fail)) + combinedip_state = NM_DEVICE_IP_STATE_FAILED; + else if (ip_state == NM_DEVICE_IP_STATE_FAILED && ip_state_other == NM_DEVICE_IP_STATE_FAILED) { + /* If both IP states fail, then it's a failure for good. may-fail does not mean that + * both families may fail, instead it means that at least one family must succeed. */ + combinedip_state = NM_DEVICE_IP_STATE_FAILED; + } else { + if (priv->ip_data.state == NM_DEVICE_IP_STATE_NONE) + combinedip_state = NM_DEVICE_IP_STATE_PENDING; + else + combinedip_state = priv->ip_data.state; + } + + _LOGT_ip(AF_UNSPEC, + "check-state: (combined) state %s => %s", + nm_device_ip_state_to_string(priv->ip_data.state), + nm_device_ip_state_to_string(combinedip_state)); + + if (!_dev_ip_state_set_state(self, AF_UNSPEC, combinedip_state, "check-ip-state")) + goto out_done; + + switch (combinedip_state) { + case NM_DEVICE_IP_STATE_PENDING: + break; + case NM_DEVICE_IP_STATE_READY: + _dev_ip_state_req_timeout_cancel(self, AF_UNSPEC); + if (priv->state == NM_DEVICE_STATE_IP_CONFIG) { + nm_device_state_changed(self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); + } + break; + case NM_DEVICE_IP_STATE_FAILED: + nm_device_state_changed(self, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + break; + case NM_DEVICE_IP_STATE_NONE: + default: + nm_assert_not_reached(); + } + +out_done: + /* we just checked the state. We can cancel the pending async check. */ + nm_clear_g_source_inst(&priv->ip_data_x[IS_IPv4].check_async_source); +} + +static gboolean +_dev_ip_state_check_async_cb(NMDevice *self, int addr_family) +{ + _dev_ip_state_check(self, addr_family); + return G_SOURCE_CONTINUE; +} + +static gboolean +_dev_ip_state_check_async_cb_4(gpointer user_data) +{ + return _dev_ip_state_check_async_cb(user_data, AF_INET); +} + +static gboolean +_dev_ip_state_check_async_cb_6(gpointer user_data) +{ + return _dev_ip_state_check_async_cb(user_data, AF_INET6); +} + +static void +_dev_ip_state_check_async(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + int IS_IPv4; + + if (addr_family == AF_UNSPEC) { + _dev_ip_state_check_async(self, AF_INET); + _dev_ip_state_check_async(self, AF_INET6); + return; + } + + IS_IPv4 = NM_IS_IPv4(addr_family); + if (!priv->ip_data_x[IS_IPv4].check_async_source) { + priv->ip_data_x[IS_IPv4].check_async_source = nm_g_idle_add_source( + (IS_IPv4 ? _dev_ip_state_check_async_cb_4 : _dev_ip_state_check_async_cb_6), + self); + } +} + +static void +_dev_ip_state_cleanup(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + int IS_IPv4; + + if (addr_family == AF_UNSPEC) { + _dev_ip_state_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE, "ip-state-clear"); + return; + } + + IS_IPv4 = NM_IS_IPv4(addr_family); + + nm_clear_g_source_inst(&priv->ip_data_x[IS_IPv4].check_async_source); + nm_clear_g_source_inst(&priv->ip_data_x[IS_IPv4].req_timeout_source); + _dev_ip_state_set_state(self, addr_family, 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; + priv->ip_data_x[IS_IPv4].is_disabled = FALSE; + priv->ip_data_x[IS_IPv4].is_ignore = FALSE; + priv->ip_data_x[IS_IPv4].do_reapply = FALSE; +} + +/*****************************************************************************/ + +static gpointer +_dev_l3_config_data_tag_get(NMDevicePrivate *priv, L3ConfigDataType l3cd_type) +{ + nm_assert(_NM_INT_NOT_NEGATIVE(l3cd_type) && l3cd_type < G_N_ELEMENTS(priv->l3cds)); + + return &priv->l3cds[l3cd_type]; +} + +static L3ConfigDataType +_dev_l3_config_data_tag_to_type(NMDevice *self, gconstpointer tag) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + int d; + + /* In C it is undefined behavior to compare unrelated pointers. + * Work around that by using nm_ptr_to_uintptr(), which casts the pointers + * to integers. + * + * I guess, theoretically it's still a problem to assume that if tag pointers + * somewhere inside priv->l3cds, that the uintptr_t case would also yield + * a value in that range. In practice, I couldn't imaging this not not work + * reliably. */ + + if (nm_ptr_to_uintptr(tag) < nm_ptr_to_uintptr(&priv->l3cds[0]) + || nm_ptr_to_uintptr(tag) >= nm_ptr_to_uintptr(&priv->l3cds[G_N_ELEMENTS(priv->l3cds)])) + return _L3_CONFIG_DATA_TYPE_NONE; + + d = ((typeof(priv->l3cds[0]) *) tag) - (&priv->l3cds[0]); + + nm_assert(d >= 0); + nm_assert(d < _L3_CONFIG_DATA_TYPE_NUM); + nm_assert(tag == &priv->l3cds[d]); + nm_assert(tag == _dev_l3_config_data_tag_get(priv, d)); + return d; +} + +static L3ConfigDataType +_dev_l3_config_data_acd_addr_info_to_type(NMDevice * self, + const NML3AcdAddrInfo *addr_info, + guint i_track_infos) +{ + nm_assert(NM_IS_DEVICE(self)); + nm_assert(addr_info); + nm_assert(i_track_infos < addr_info->n_track_infos); + + return _dev_l3_config_data_tag_to_type(self, addr_info->track_infos[i_track_infos].tag); +} + +// FIXME(l3cfg): unused function?? +_nm_unused static const NML3AcdAddrTrackInfo * +_dev_l3_config_data_acd_addr_info_has_by_type(NMDevice * self, + const NML3AcdAddrInfo *addr_info, + L3ConfigDataType l3cd_type) +{ + guint i; + + nm_assert(NM_IS_DEVICE(self)); + nm_assert(addr_info); + nm_assert(_NM_INT_NOT_NEGATIVE(l3cd_type) && l3cd_type < _L3_CONFIG_DATA_TYPE_NUM); + + for (i = 0; i < addr_info->n_track_infos; i++) { + if (l3cd_type == _dev_l3_config_data_acd_addr_info_to_type(self, addr_info, i)) + return &addr_info->track_infos[i]; + } + return NULL; +} + +static void +_dev_l3_get_config_settings(NMDevice * self, + L3ConfigDataType type, + NML3ConfigMergeFlags *out_merge_flags, + NML3AcdDefendType * out_acd_defend_type, + guint32 * out_acd_timeout_msec) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NML3ConfigMergeFlags flags; + NMConnection * connection; + NMSettingIPConfig * s_ip; + + nm_assert(_NM_INT_NOT_NEGATIVE(type) && type < _L3_CONFIG_DATA_TYPE_NUM); + + if (G_UNLIKELY(!priv->l3config_merge_flags_has)) { + int IS_IPv4; + + connection = nm_device_get_applied_connection(self); + + for (IS_IPv4 = 0; IS_IPv4 < 2; IS_IPv4++) { + flags = NM_L3_CONFIG_MERGE_FLAGS_NONE; + + if (connection + && (s_ip = nm_connection_get_setting_ip_config(connection, + IS_IPv4 ? AF_INET : AF_INET6))) { + if (nm_setting_ip_config_get_ignore_auto_routes(s_ip)) + flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES; + + if (nm_setting_ip_config_get_ignore_auto_dns(s_ip)) + flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_DNS; + + if (nm_setting_ip_config_get_never_default(s_ip) + || nm_setting_ip_config_get_gateway(s_ip)) { + /* if the connection has an explicit gateway, we also ignore + * the default routes from other sources. */ + flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES; } } + + priv->l3config_merge_flags_x[IS_IPv4] = flags; + } + priv->l3config_merge_flags_has = TRUE; + } + + switch (type) { + case L3_CONFIG_DATA_TYPE_DEVIP_UNSPEC: + case L3_CONFIG_DATA_TYPE_MANUALIP: + case L3_CONFIG_DATA_TYPE_LL_4: + case L3_CONFIG_DATA_TYPE_LL_6: + case L3_CONFIG_DATA_TYPE_PD_6: + case L3_CONFIG_DATA_TYPE_SHARED_4: + case L3_CONFIG_DATA_TYPE_DEVIP_4: + case L3_CONFIG_DATA_TYPE_AC_6: + case L3_CONFIG_DATA_TYPE_DHCP_6: + case L3_CONFIG_DATA_TYPE_DEVIP_6: + *out_acd_timeout_msec = _prop_get_ipv4_dad_timeout(self); + goto after_acd_timeout; + + case L3_CONFIG_DATA_TYPE_DHCP_4: + /* For DHCP, we perform ACD separately, because we want to decline the + * lease in case of a conflict. */ + *out_acd_timeout_msec = 0; + goto after_acd_timeout; + + case _L3_CONFIG_DATA_TYPE_NUM: + case _L3_CONFIG_DATA_TYPE_NONE: + case _L3_CONFIG_DATA_TYPE_ACD_ONLY: + break; + } + *out_acd_timeout_msec = nm_assert_unreachable_val(0); + +after_acd_timeout: + switch (type) { + case L3_CONFIG_DATA_TYPE_LL_4: + *out_acd_defend_type = NM_L3_ACD_DEFEND_TYPE_ONCE; + goto after_acd_defend_type; + + case L3_CONFIG_DATA_TYPE_DEVIP_UNSPEC: + case L3_CONFIG_DATA_TYPE_MANUALIP: + case L3_CONFIG_DATA_TYPE_LL_6: + case L3_CONFIG_DATA_TYPE_PD_6: + case L3_CONFIG_DATA_TYPE_SHARED_4: + case L3_CONFIG_DATA_TYPE_DHCP_4: + case L3_CONFIG_DATA_TYPE_DEVIP_4: + case L3_CONFIG_DATA_TYPE_AC_6: + case L3_CONFIG_DATA_TYPE_DHCP_6: + case L3_CONFIG_DATA_TYPE_DEVIP_6: + *out_acd_defend_type = NM_L3_ACD_DEFEND_TYPE_ALWAYS; + goto after_acd_defend_type; + + case _L3_CONFIG_DATA_TYPE_NUM: + case _L3_CONFIG_DATA_TYPE_NONE: + case _L3_CONFIG_DATA_TYPE_ACD_ONLY: + break; + } + *out_acd_defend_type = nm_assert_unreachable_val(NM_L3_ACD_DEFEND_TYPE_ALWAYS); + +after_acd_defend_type: + switch (type) { + case L3_CONFIG_DATA_TYPE_DEVIP_UNSPEC: + case L3_CONFIG_DATA_TYPE_MANUALIP: + case L3_CONFIG_DATA_TYPE_LL_4: + case L3_CONFIG_DATA_TYPE_LL_6: + case L3_CONFIG_DATA_TYPE_PD_6: + case L3_CONFIG_DATA_TYPE_SHARED_4: + *out_merge_flags = NM_L3_CONFIG_MERGE_FLAGS_NONE; + goto after_merge_flags; + + case L3_CONFIG_DATA_TYPE_DHCP_4: + case L3_CONFIG_DATA_TYPE_DEVIP_4: + *out_merge_flags = priv->l3config_merge_flags_4; + goto after_merge_flags; + + case L3_CONFIG_DATA_TYPE_AC_6: + case L3_CONFIG_DATA_TYPE_DHCP_6: + case L3_CONFIG_DATA_TYPE_DEVIP_6: + *out_merge_flags = priv->l3config_merge_flags_6; + goto after_merge_flags; + + case _L3_CONFIG_DATA_TYPE_NUM: + case _L3_CONFIG_DATA_TYPE_NONE: + case _L3_CONFIG_DATA_TYPE_ACD_ONLY: + break; + } + *out_merge_flags = nm_assert_unreachable_val(NM_L3_CONFIG_MERGE_FLAGS_NONE); + +after_merge_flags: + return; +} + +static gboolean +_dev_l3_register_l3cds_add_config(NMDevice *self, L3ConfigDataType l3cd_type) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NML3ConfigMergeFlags merge_flags; + NML3AcdDefendType acd_defend_type; + guint32 acd_timeout_msec; + + _dev_l3_get_config_settings(self, l3cd_type, &merge_flags, &acd_defend_type, &acd_timeout_msec); + return nm_l3cfg_add_config(priv->l3cfg, + _dev_l3_config_data_tag_get(priv, l3cd_type), + FALSE, + priv->l3cds[l3cd_type].d, + l3cd_type, + nm_device_get_route_table(self, AF_INET), + nm_device_get_route_table(self, AF_INET6), + nm_device_get_route_metric(self, AF_INET), + nm_device_get_route_metric(self, AF_INET6), + _dev_default_route_metric_penalty_get(self, AF_INET), + _dev_default_route_metric_penalty_get(self, AF_INET6), + _prop_get_ipvx_dns_priority(self, AF_INET), + _prop_get_ipvx_dns_priority(self, AF_INET6), + acd_defend_type, + acd_timeout_msec, + NM_L3CFG_CONFIG_FLAGS_NONE, + merge_flags); +} + +static gboolean +_dev_l3_register_l3cds_set_one(NMDevice * self, + L3ConfigDataType l3cd_type, + const NML3ConfigData *l3cd, + NMTernary commit_sync) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + nm_auto_unref_l3cd const NML3ConfigData *l3cd_old = NULL; + gboolean changed = FALSE; + + if (priv->l3cds[l3cd_type].d != l3cd) { + if (nm_l3_config_data_equal(priv->l3cds[l3cd_type].d, l3cd)) { + /* we would set to a different instance, but the same content! + * We keep the previous one and ignore the new @l3cd. + * + * Warning: this means, that after calling this function, + * priv->l3cds[l3cd_type].d still might point to a different + * (though semantically equal) l3cd instance. */ + } else { + l3cd_old = g_steal_pointer(&priv->l3cds[l3cd_type].d); + if (l3cd) + priv->l3cds[l3cd_type].d = nm_l3_config_data_ref_and_seal(l3cd); } } - if (priv->ip_state_x[IS_IPv4] == new_state) + if (priv->l3cfg) { + if (priv->l3cds[l3cd_type].d) { + if (_dev_l3_register_l3cds_add_config(self, l3cd_type)) + changed = TRUE; + } + + if (l3cd_old) { + if (nm_l3cfg_remove_config(priv->l3cfg, + _dev_l3_config_data_tag_get(priv, l3cd_type), + l3cd_old)) + changed = TRUE; + } + } + + if (changed && commit_sync != NM_TERNARY_DEFAULT) + _dev_l3_cfg_commit(self, !!commit_sync); + + return changed; +} + +static void +_dev_l3_update_l3cds_ifindex(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + int ip_ifindex; + int i; + + ip_ifindex = nm_device_get_ip_ifindex(self); + if (ip_ifindex <= 0) return; - _LOGT(LOGD_DEVICE, - "ip%c-state: set to %d (%s)", - nm_utils_addr_family_to_char(addr_family), - (int) new_state, - nm_device_ip_state_to_string(new_state)); + for (i = 0; i < (int) G_N_ELEMENTS(priv->l3cds); i++) { + if (priv->l3cds[i].d && nm_l3_config_data_get_ifindex(priv->l3cds[i].d) != ip_ifindex) { + nm_auto_unref_l3cd const NML3ConfigData *l3cd_old = NULL; - priv->ip_state_x_[IS_IPv4] = new_state; + l3cd_old = g_steal_pointer(&priv->l3cds[i].d); - if (new_state == NM_DEVICE_IP_STATE_DONE) { - /* we only set the IPx_READY flag once we reach NM_DEVICE_IP_STATE_DONE state. We don't - * ever clear it, even if we later enter NM_DEVICE_IP_STATE_FAIL state. - * - * This is not documented/guaranteed behavior, but seems to make sense for now. */ - _active_connection_set_state_flags(self, - NM_IS_IPv4(addr_family) - ? NM_ACTIVATION_STATE_FLAG_IP4_READY - : NM_ACTIVATION_STATE_FLAG_IP6_READY); + priv->l3cds[i].d = + nm_l3_config_data_seal(nm_l3_config_data_new_clone(l3cd_old, ip_ifindex)); + } } } +static gboolean +_dev_l3_register_l3cds(NMDevice *self, + NML3Cfg * l3cfg, + gboolean do_add /* else remove */, + NMTernary do_commit) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gboolean is_external; + gboolean changed; + int i; + + if (!l3cfg) + return FALSE; + + is_external = nm_device_sys_iface_state_is_external(self); + + changed = FALSE; + for (i = 0; i < (int) G_N_ELEMENTS(priv->l3cds); i++) { + if (!priv->l3cds[i].d) + continue; + if (!do_add) { + if (nm_l3cfg_remove_config(l3cfg, + _dev_l3_config_data_tag_get(priv, i), + priv->l3cds[i].d)) + changed = TRUE; + continue; + } + if (is_external) + continue; + if (_dev_l3_register_l3cds_add_config(self, i)) + changed = TRUE; + } + + if (do_commit == NM_TERNARY_DEFAULT) + do_commit = changed; + if (do_commit) + _dev_l3_cfg_commit(self, TRUE); + + return changed; +} + +/*****************************************************************************/ + +void +nm_device_l3cfg_commit(NMDevice *self, NML3CfgCommitType commit_type, gboolean commit_sync) +{ + NMDevicePrivate *priv; + + g_return_if_fail(NM_IS_DEVICE(self)); + + priv = NM_DEVICE_GET_PRIVATE(self); + + if (!priv->l3cfg) + return; + + /* FIXME(l3cfg): commit_sync should go away and not be used. The reason is that + * a commit does *a lot* of things which are outside the control of the caller, + * which makes it unsuitable to call in most cases. */ + if (!commit_sync) { + nm_l3cfg_commit_on_idle_schedule(priv->l3cfg, commit_type); + return; + } + + nm_l3cfg_commit(priv->l3cfg, commit_type); +} + +static void +_dev_l3_cfg_commit(NMDevice *self, gboolean commit_sync) +{ + nm_device_l3cfg_commit(self, NM_L3_CFG_COMMIT_TYPE_AUTO, commit_sync); +} + +static void +update_external_connection(NMDevice *self) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NMSettingsConnection *settings_connection; + gs_unref_object NMConnection *connection_new = NULL; + NMConnection * connection_old; + gs_unref_object NMSetting *s_ip4_new = NULL; + gs_unref_object NMSetting *s_ip6_new = NULL; + NMSetting * s_ip4_old; + NMSetting * s_ip6_old; + + /* Update external connections with configuration from platform */ + + if (!nm_device_sys_iface_state_is_external(self)) + return; + + settings_connection = nm_device_get_settings_connection(self); + if (!settings_connection) + return; + + if (!NM_FLAGS_HAS(nm_settings_connection_get_flags(settings_connection), + NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL)) + return; + + if (nm_active_connection_get_activation_type(NM_ACTIVE_CONNECTION(priv->act_request.obj)) + != NM_ACTIVATION_TYPE_EXTERNAL) + return; + + connection_old = nm_settings_connection_get_connection(settings_connection); + s_ip4_old = nm_connection_get_setting(connection_old, NM_TYPE_SETTING_IP4_CONFIG); + s_ip6_old = nm_connection_get_setting(connection_old, NM_TYPE_SETTING_IP6_CONFIG); + + s_ip4_new = nm_utils_platform_capture_ip_setting(nm_device_get_platform(self), + AF_INET, + nm_device_get_ip_ifindex(self), + FALSE); + s_ip6_new = nm_utils_platform_capture_ip_setting(nm_device_get_platform(self), + AF_INET6, + nm_device_get_ip_ifindex(self), + _get_maybe_ipv6_disabled(self)); + + if (!s_ip4_old || !nm_setting_compare(s_ip4_new, s_ip4_old, NM_SETTING_COMPARE_FLAG_EXACT)) { + connection_new = nm_simple_connection_new_clone(connection_old); + nm_connection_add_setting(connection_new, g_steal_pointer(&s_ip4_new)); + } + + if (!s_ip6_old || !nm_setting_compare(s_ip6_new, s_ip6_old, NM_SETTING_COMPARE_FLAG_EXACT)) { + if (!connection_new) + connection_new = nm_simple_connection_new_clone(connection_old); + nm_connection_add_setting(connection_new, g_steal_pointer(&s_ip6_new)); + } + + if (connection_new) { + nm_settings_connection_update(settings_connection, + connection_new, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET, + "update-external", + NULL); + } +} + +static void +_dev_l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + nm_assert(l3cfg == priv->l3cfg); + + switch (notify_data->notify_type) { + case NM_L3_CONFIG_NOTIFY_TYPE_L3CD_CHANGED: + if (notify_data->l3cd_changed.commited) { + g_signal_emit(self, + signals[L3CD_CHANGED], + 0, + notify_data->l3cd_changed.l3cd_old, + notify_data->l3cd_changed.l3cd_new); + } + return; + case NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT: + { + const NML3AcdAddrInfo *addr_info = ¬ify_data->acd_event.info; + + if (addr_info->state > NM_L3_ACD_ADDR_STATE_PROBING) + _dev_ipmanual_check_ready(self); + return; + } + case NM_L3_CONFIG_NOTIFY_TYPE_PRE_COMMIT: + { + const NML3ConfigData *l3cd; + + /* FIXME(l3cfg): MTU handling should be moved to l3cfg. */ + l3cd = nm_l3cfg_get_combined_l3cd(l3cfg, TRUE); + if (l3cd) + priv->ip6_mtu = nm_l3_config_data_get_ip6_mtu(l3cd); + _commit_mtu(self); + return; + } + case NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT: + if (priv->ipshared_data_4.state == NM_DEVICE_IP_STATE_PENDING + && !priv->ipshared_data_4.v4.dnsmasq_manager && priv->ipshared_data_4.v4.l3cd) { + _dev_ipshared4_spawn_dnsmasq(self); + nm_clear_l3cd(&priv->ipshared_data_4.v4.l3cd); + } + _dev_ipmanual_check_ready(self); + return; + case NM_L3_CONFIG_NOTIFY_TYPE_IPV4LL_EVENT: + nm_assert(NM_IS_L3_IPV4LL(notify_data->ipv4ll_event.ipv4ll)); + if (priv->ipll_data_4.v4.ipv4ll == notify_data->ipv4ll_event.ipv4ll) + _dev_ipll4_notify_event(self); + return; + case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE: + return; + case NM_L3_CONFIG_NOTIFY_TYPE_ROUTES_TEMPORARY_NOT_AVAILABLE_EXPIRED: + /* we commit again. This way we try to configure the routes.*/ + _dev_l3_cfg_commit(self, FALSE); + return; + case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE: + if (NM_FLAGS_ANY(notify_data->platform_change_on_idle.obj_type_flags, + nmp_object_type_to_flags(NMP_OBJECT_TYPE_LINK) + | nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP4_ADDRESS) + | nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP6_ADDRESS))) + _dev_unamanged_check_external_down(self, TRUE); + _dev_ipmanual_check_ready(self); + update_external_connection(self); + return; + + case _NM_L3_CONFIG_NOTIFY_TYPE_NUM: + break; + } + nm_assert_not_reached(); +} + +static void +_dev_l3_cfg_commit_type_reset(NMDevice *self) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NML3CfgCommitType commit_type; + + if (!priv->l3cfg) + return; + + switch (priv->sys_iface_state) { + case NM_DEVICE_SYS_IFACE_STATE_EXTERNAL: + case NM_DEVICE_SYS_IFACE_STATE_REMOVED: + commit_type = NM_L3_CFG_COMMIT_TYPE_NONE; + goto do_set; + case NM_DEVICE_SYS_IFACE_STATE_ASSUME: + commit_type = NM_L3_CFG_COMMIT_TYPE_ASSUME; + goto do_set; + case NM_DEVICE_SYS_IFACE_STATE_MANAGED: + commit_type = NM_L3_CFG_COMMIT_TYPE_UPDATE; + goto do_set; + } + nm_assert_not_reached(); + return; + +do_set: + priv->l3cfg_commit_type = + nm_l3cfg_commit_type_register(priv->l3cfg, commit_type, priv->l3cfg_commit_type, "device"); +} + /*****************************************************************************/ const char * @@ -2973,11 +3947,18 @@ nm_device_get_iface(NMDevice *self) static gboolean _set_ifindex(NMDevice *self, int ifindex, gboolean is_ip_ifindex) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - int * p_ifindex; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gs_unref_object NML3Cfg *l3cfg_old = NULL; + NML3CfgCommitTypeHandle *l3cfg_commit_type_old = NULL; + gboolean l3_changed; + int ip_ifindex_new; + int * p_ifindex; + gboolean l3cfg_was_reset = FALSE; - if (ifindex < 0) + if (ifindex < 0) { + nm_assert_not_reached(); ifindex = 0; + } p_ifindex = is_ip_ifindex ? &priv->ip_ifindex_ : &priv->ifindex_; @@ -2986,13 +3967,90 @@ _set_ifindex(NMDevice *self, int ifindex, gboolean is_ip_ifindex) *p_ifindex = ifindex; - _LOGD(LOGD_DEVICE, "ifindex: set %sifindex %d", is_ip_ifindex ? "ip-" : "", ifindex); + ip_ifindex_new = nm_device_get_ip_ifindex(self); + + if (priv->l3cfg) { + if (ip_ifindex_new <= 0 || ip_ifindex_new != nm_l3cfg_get_ifindex(priv->l3cfg)) { + g_signal_handlers_disconnect_by_func(priv->l3cfg, + G_CALLBACK(_dev_l3_cfg_notify_cb), + self); + l3cfg_old = g_steal_pointer(&priv->l3cfg_); + l3cfg_commit_type_old = g_steal_pointer(&priv->l3cfg_commit_type); + l3cfg_was_reset = TRUE; + } + } + if (!priv->l3cfg && ip_ifindex_new > 0) { + priv->l3cfg_ = nm_netns_l3cfg_acquire(priv->netns, ip_ifindex_new); + + g_signal_connect(priv->l3cfg, + NM_L3CFG_SIGNAL_NOTIFY, + G_CALLBACK(_dev_l3_cfg_notify_cb), + self); + + _dev_l3_cfg_commit_type_reset(self); + l3cfg_was_reset = TRUE; + } + + _LOGD(LOGD_DEVICE, + "ifindex: set %sifindex %d%s%s%s%s%s%s", + is_ip_ifindex ? "ip-" : "", + ifindex, + NM_PRINT_FMT_QUOTED(l3cfg_old && l3cfg_old != priv->l3cfg, + " (old-l3cfg: ", + nm_hash_obfuscated_ptr_str_a(l3cfg_old), + ")", + ""), + NM_PRINT_FMT_QUOTED(priv->l3cfg && l3cfg_old != priv->l3cfg, + " (l3cfg: ", + nm_hash_obfuscated_ptr_str_a(priv->l3cfg), + ")", + "")); + + if (priv->manager) + nm_manager_emit_device_ifindex_changed(priv->manager, self); if (!is_ip_ifindex) _notify(self, PROP_IFINDEX); - if (priv->manager) - nm_manager_emit_device_ifindex_changed(priv->manager, self); + if (l3cfg_was_reset) { + gs_unref_object NMIPConfig *ipconf_old_4 = NULL; + gs_unref_object NMIPConfig *ipconf_old_6 = NULL; + + ipconf_old_4 = g_steal_pointer(&priv->l3ipdata_4.ip_config); + ipconf_old_6 = g_steal_pointer(&priv->l3ipdata_6.ip_config); + if (priv->l3cfg) { + priv->l3ipdata_4.ip_config = nm_l3cfg_ipconfig_acquire(priv->l3cfg, AF_INET); + priv->l3ipdata_6.ip_config = nm_l3cfg_ipconfig_acquire(priv->l3cfg, AF_INET6); + } + _notify(self, PROP_IP4_CONFIG); + _notify(self, PROP_IP6_CONFIG); + } + + if (l3cfg_old != priv->l3cfg) { + l3_changed = FALSE; + if (_dev_l3_register_l3cds(self, l3cfg_old, FALSE, FALSE)) + l3_changed = TRUE; + + /* Now it gets ugly. We changed the ip-ifindex, which determines the NML3Cfg instance. + * But all the NML3ConfigData we currently track are still for the old ifindex. We + * need to update them. + * + * This should be all handled entirely different, where an NMDevice is strictly + * associated with one ifindex (and not the ifindex/ip-ifindex split). Or it + * is not at all associated with an ifindex, but only a controlling device for + * a real NMDevice (that has the ifindex). */ + _dev_l3_update_l3cds_ifindex(self); + + if (_dev_l3_register_l3cds(self, priv->l3cfg, TRUE, FALSE)) + l3_changed = TRUE; + + if (l3_changed) + _dev_l3_cfg_commit(self, TRUE); + } + + if (l3cfg_commit_type_old) + nm_l3cfg_commit_type_unregister(l3cfg_old, l3cfg_commit_type_old); + return TRUE; } @@ -3159,13 +4217,12 @@ _set_ip_ifindex(NMDevice *self, int ifindex, const char *ifname) NM_PRINT_FMT_QUOTE_STRING(ifname), ifindex); - _set_ifindex(self, ifindex, TRUE); - if (!eq_name) { g_free(priv->ip_iface_); priv->ip_iface_ = g_strdup(ifname); _notify(self, PROP_IP_IFACE); } + _set_ifindex(self, ifindex, TRUE); if (priv->ip_ifindex > 0) { platform = nm_device_get_platform(self); @@ -3729,8 +4786,10 @@ nm_device_get_route_metric_default(NMDeviceType device_type) return 11000; } -static gboolean -default_route_metric_penalty_detect(NMDevice *self, int addr_family) +/* FIXME(l3cfg): we currently never call this function. We need to react + * to changes and re-commit the IP configuration with updated penalty. */ +_nm_unused static gboolean +_dev_default_route_metric_penalty_detect(NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); const int IS_IPv4 = NM_IS_IPv4(addr_family); @@ -3745,7 +4804,7 @@ default_route_metric_penalty_detect(NMDevice *self, int addr_family) } static guint32 -default_route_metric_penalty_get(NMDevice *self, int addr_family) +_dev_default_route_metric_penalty_get(NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); @@ -3813,28 +4872,23 @@ nm_device_get_route_table(NMDevice *self, int addr_family) return route_table ?: (guint32) RT_TABLE_MAIN; } -static NMIPRouteTableSyncMode +/* FIXME(l3cfg): need to properly handle the route-table sync mode and + * use it during commit. */ +_nm_unused static NMIPRouteTableSyncMode _get_route_table_sync_mode_stateful(NMDevice *self, int addr_family) { - const int IS_IPv4 = NM_IS_IPv4(addr_family); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMDedupMultiIter ipconf_iter; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); gboolean all_sync_now; gboolean all_sync_eff; all_sync_now = _prop_get_ipvx_route_table(self, addr_family) != 0u; if (!all_sync_now) { - const NMPlatformIPRoute *route; + const NML3ConfigData *l3cd = priv->l3cds[L3_CONFIG_DATA_TYPE_MANUALIP].d; /* If there's a local route switch to all-sync in order * to properly manage the local table */ - nm_ip_config_iter_ip_route_for_each (&ipconf_iter, priv->con_ip_config_x[IS_IPv4], &route) { - if (nm_platform_route_type_uncoerce(route->type_coerced) == RTN_LOCAL) { - all_sync_now = TRUE; - break; - } - } + all_sync_now = l3cd && nm_l3_config_data_has_routes_with_type_local(l3cd, addr_family); } if (all_sync_now) @@ -3866,18 +4920,20 @@ nm_device_get_best_default_route(NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - switch (addr_family) { - case AF_INET: - return priv->ip_config_4 ? nm_ip4_config_best_default_route_get(priv->ip_config_4) : NULL; - case AF_INET6: - return priv->ip_config_6 ? nm_ip6_config_best_default_route_get(priv->ip_config_6) : NULL; - case AF_UNSPEC: - return (priv->ip_config_4 ? nm_ip4_config_best_default_route_get(priv->ip_config_4) : NULL) - ?: (priv->ip_config_6 ? nm_ip6_config_best_default_route_get(priv->ip_config_6) - : NULL); - default: - g_return_val_if_reached(NULL); - } + if (!priv->l3cfg) + return NULL; + + /* FIXME(l3cfg): this function returns the best default route that we + * *want* to configure. What is the meaning of that? Possibly the caller + * cares whether there *is* a default route configured, for which they + * should ask platform. + * + * Check callers why they call this. Quite possibly this whole notion of + * "has a default route" is wrong to being with, regardless whether we + * look at the desired or actual configuration. That is, because "has a default route" + * does not do justice to the complexity of routing (with policy routing, + * etc.). */ + return nm_l3cfg_get_best_default_route(priv->l3cfg, addr_family, TRUE); } const char * @@ -4432,14 +5488,8 @@ concheck_update_state(NMDevice * self, _notify(self, IS_IPv4 ? PROP_IP4_CONNECTIVITY : PROP_IP6_CONNECTIVITY); - if (priv->state == NM_DEVICE_STATE_ACTIVATED && !nm_device_sys_iface_state_is_external(self)) { - if (nm_device_get_best_default_route(self, AF_INET) - && !ip_config_merge_and_apply(self, AF_INET, TRUE)) - _LOGW(LOGD_IP4, "Failed to update IPv4 route metric"); - if (nm_device_get_best_default_route(self, AF_INET6) - && !ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "Failed to update IPv6 route metric"); - } + if (priv->state == NM_DEVICE_STATE_ACTIVATED && !nm_device_sys_iface_state_is_external(self)) + _dev_l3_register_l3cds(self, priv->l3cfg, TRUE, NM_TERNARY_DEFAULT); } static const char * @@ -4774,16 +5824,14 @@ find_slave_info(NMDevice *self, NMDevice *slave) static gboolean nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *connection) { - NMDevicePrivate *priv; - SlaveInfo * info; - gboolean success = FALSE; - gboolean configure; + SlaveInfo *info; + gboolean success = FALSE; + gboolean configure; g_return_val_if_fail(self != NULL, FALSE); g_return_val_if_fail(slave != NULL, FALSE); g_return_val_if_fail(NM_DEVICE_GET_CLASS(self)->enslave_slave != NULL, FALSE); - priv = NM_DEVICE_GET_PRIVATE(self); info = find_slave_info(self, slave); if (!info) return FALSE; @@ -4806,22 +5854,17 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co */ nm_device_update_hw_address(self); + /* Since slave devices don't have their own IP configuration, + * set the MTU here. + */ + _commit_mtu(slave); + /* Restart IP configuration if we're waiting for slaves. Do this * after updating the hardware address as IP config may need the * new address. */ - if (success) { - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_WAIT) - nm_device_activate_stage3_ip_start(self, AF_INET); - - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_WAIT) - nm_device_activate_stage3_ip_start(self, AF_INET6); - } - - /* Since slave devices don't have their own IP configuration, - * set the MTU here. - */ - _commit_mtu(slave, NM_DEVICE_GET_PRIVATE(slave)->ip_config_4); + if (success) + nm_device_activate_schedule_stage3_ip_config(self, FALSE); return success; } @@ -4908,6 +5951,8 @@ nm_device_master_release_one_slave(NMDevice * self, NM_DEVICE_STATE_REASON_REMOVED); } +/*****************************************************************************/ + /** * can_unmanaged_external_down: * @self: the device @@ -4922,7 +5967,7 @@ can_unmanaged_external_down(NMDevice *self) } static NMUnmanFlagOp -is_unmanaged_external_down(NMDevice *self, gboolean consider_can) +_dev_unmanaged_is_external_down(NMDevice *self, gboolean consider_can) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); @@ -4939,7 +5984,7 @@ is_unmanaged_external_down(NMDevice *self, gboolean consider_can) } static void -set_unmanaged_external_down(NMDevice *self, gboolean only_if_unmanaged) +_dev_unamanged_check_external_down(NMDevice *self, gboolean only_if_unmanaged) { NMUnmanFlagOp ext_flags; @@ -4951,7 +5996,7 @@ set_unmanaged_external_down(NMDevice *self, gboolean only_if_unmanaged) return; } - ext_flags = is_unmanaged_external_down(self, FALSE); + ext_flags = _dev_unmanaged_is_external_down(self, FALSE); if (ext_flags != NM_UNMAN_FLAG_OP_SET_UNMANAGED) { /* Ensure the assume check is queued before any queued state changes * from the transition to UNAVAILABLE. @@ -4979,26 +6024,15 @@ nm_device_update_dynamic_ip_setup(NMDevice *self) g_hash_table_remove_all(priv->ip6_saved_properties); - if (priv->dhcp_data_4.client) { - if (!nm_device_dhcp4_renew(self, FALSE)) { - nm_device_state_changed(self, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_DHCP_FAILED); - return; - } - } - if (priv->dhcp_data_6.client) { - if (!nm_device_dhcp6_renew(self, FALSE)) { - nm_device_state_changed(self, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_DHCP_FAILED); - return; - } - } - if (priv->ndisc) { + if (priv->ipdhcp_data_4.state != NM_DEVICE_IP_STATE_NONE) + _dev_ipdhcpx_restart(self, AF_INET, FALSE); + if (priv->ipdhcp_data_6.state != NM_DEVICE_IP_STATE_NONE) + _dev_ipdhcpx_restart(self, AF_INET6, FALSE); + + if (priv->ipac6_data.ndisc) { /* FIXME: todo */ } - if (priv->dnsmasq_manager) { + if (priv->ipshared_data_4.v4.dnsmasq_manager) { /* FIXME: todo */ } } @@ -5033,10 +6067,7 @@ carrier_changed(NMDevice *self, gboolean carrier) nm_device_update_dynamic_ip_setup(self); /* If needed, also resume IP configuration that is * waiting for carrier. */ - if (nm_device_activate_ip4_state_in_wait(self)) - nm_device_activate_stage3_ip_start(self, AF_INET); - if (nm_device_activate_ip6_state_in_wait(self)) - nm_device_activate_stage3_ip_start(self, AF_INET6); + nm_device_activate_schedule_stage3_ip_config(self, FALSE); return; } /* fall-through and change state of device */ @@ -5134,10 +6165,6 @@ nm_device_set_carrier(NMDevice *self, gboolean carrier) nm_device_remove_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); _carrier_wait_check_queued_act_request(self); } - - /* Send ARP announcements if did not yet and have carrier. */ - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && !priv->acd.announcing) - nm_device_arp_announce(self); } else { if (priv->carrier_wait_id) nm_device_add_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); @@ -5268,85 +6295,6 @@ device_ifindex_changed_cb(NMManager *manager, NMDevice *device_changed, NMDevice priv->device_link_changed_id = g_idle_add((GSourceFunc) device_link_changed, self); } -static void -ndisc_set_router_config(NMNDisc *ndisc, NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - gs_unref_array GArray *addresses = NULL; - gs_unref_array GArray *dns_servers = NULL; - gs_unref_array GArray * dns_domains = NULL; - guint len; - guint i; - const NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIter ipconf_iter; - - if (nm_ndisc_get_node_type(ndisc) != NM_NDISC_NODE_TYPE_ROUTER) - return; - - head_entry = nm_ip6_config_lookup_addresses(priv->ip_config_6); - addresses = - g_array_sized_new(FALSE, TRUE, sizeof(NMNDiscAddress), head_entry ? head_entry->len : 0); - nm_dedup_multi_iter_for_each (&ipconf_iter, head_entry) { - const NMPlatformIP6Address *addr = NMP_OBJECT_CAST_IP6_ADDRESS(ipconf_iter.current->obj); - NMNDiscAddress * ndisc_addr; - guint32 lifetime; - guint32 preferred; - - if (IN6_IS_ADDR_UNSPECIFIED(&addr->address) || IN6_IS_ADDR_LINKLOCAL(&addr->address)) - continue; - - if (addr->n_ifa_flags & IFA_F_TENTATIVE || addr->n_ifa_flags & IFA_F_DADFAILED) - continue; - - if (addr->plen != 64) - continue; - - lifetime = nmp_utils_lifetime_get(addr->timestamp, - addr->lifetime, - addr->preferred, - NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000, - &preferred); - if (!lifetime) - continue; - - g_array_set_size(addresses, addresses->len + 1); - ndisc_addr = &g_array_index(addresses, NMNDiscAddress, addresses->len - 1); - ndisc_addr->address = addr->address; - ndisc_addr->expiry_msec = - _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, lifetime); - ndisc_addr->expiry_preferred_msec = - _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, preferred); - } - - len = nm_ip6_config_get_num_nameservers(priv->ip_config_6); - dns_servers = g_array_sized_new(FALSE, TRUE, sizeof(NMNDiscDNSServer), len); - g_array_set_size(dns_servers, len); - for (i = 0; i < len; i++) { - const struct in6_addr *nameserver = nm_ip6_config_get_nameserver(priv->ip_config_6, i); - NMNDiscDNSServer * ndisc_nameserver; - - ndisc_nameserver = &g_array_index(dns_servers, NMNDiscDNSServer, i); - ndisc_nameserver->address = *nameserver; - ndisc_nameserver->expiry_msec = - _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, NM_NDISC_ROUTER_LIFETIME); - } - - len = nm_ip6_config_get_num_searches(priv->ip_config_6); - dns_domains = g_array_sized_new(FALSE, TRUE, sizeof(NMNDiscDNSDomain), len); - g_array_set_size(dns_domains, len); - for (i = 0; i < len; i++) { - const char * search = nm_ip6_config_get_search(priv->ip_config_6, i); - NMNDiscDNSDomain *ndisc_search; - - ndisc_search = &g_array_index(dns_domains, NMNDiscDNSDomain, i); - ndisc_search->domain = (char *) search; - ndisc_search->expiry_msec = - _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, NM_NDISC_ROUTER_LIFETIME); - } - - nm_ndisc_set_config(ndisc, addresses, dns_servers, dns_domains); -} - static void device_update_interface_flags(NMDevice *self, const NMPlatformLink *plink) { @@ -5378,8 +6326,9 @@ device_update_interface_flags(NMDevice *self, const NMPlatformLink *plink) } static gboolean -device_link_changed(NMDevice *self) +device_link_changed(gpointer user_data) { + NMDevice * self = user_data; NMDeviceClass * klass = NM_DEVICE_GET_CLASS(self); NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); gboolean ip_ifname_changed = FALSE; @@ -5464,8 +6413,8 @@ device_link_changed(NMDevice *self) nm_device_emit_recheck_auto_activate(self); } - if (priv->ndisc && pllink->inet6_token.id) { - if (nm_ndisc_set_iid(priv->ndisc, pllink->inet6_token)) + if (priv->ipac6_data.ndisc && pllink->inet6_token.id) { + if (nm_ndisc_set_iid(priv->ipac6_data.ndisc, pllink->inet6_token)) _LOGD(LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); } @@ -5512,23 +6461,22 @@ device_link_changed(NMDevice *self) nm_device_set_unmanaged_by_flags(self, NM_UNMANAGED_PLATFORM_INIT, FALSE, reason); } - set_unmanaged_external_down(self, FALSE); + _dev_unamanged_check_external_down(self, FALSE); device_recheck_slave_status(self, pllink); if (priv->up && (!was_up || seen_down)) { /* the link was down and just came up. That happens for example, while changing MTU. - * We must restore IP configuration. */ - if (NM_IN_SET(priv->ip_state_4, NM_DEVICE_IP_STATE_CONF, NM_DEVICE_IP_STATE_DONE)) { - if (!ip_config_merge_and_apply(self, AF_INET, TRUE)) - _LOGW(LOGD_IP4, "failed applying IP4 config after link comes up again"); - } - - priv->linklocal6_dad_counter = 0; - if (NM_IN_SET(priv->ip_state_6, NM_DEVICE_IP_STATE_CONF, NM_DEVICE_IP_STATE_DONE)) { - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "failed applying IP6 config after link comes up again"); - } + * We must restore IP configuration. + * + * FIXME(l3cfg): when NML3Cfg notices that the device goes down and up, then + * it should automatically schedule a REAPPLY commit -- provided that the current + * commit-type is >= UPDATE. The idea is to move logic away from NMDevice + * so that it theoretically would also work for NMVpnConnection (although, + * NMVpnConnection should become like a regular device, akin to NMDevicePpp). + */ + if (!nm_device_sys_iface_state_is_external(self)) + nm_device_l3cfg_commit(self, NM_L3_CFG_COMMIT_TYPE_REAPPLY, FALSE); } if (update_unmanaged_specs) @@ -5550,8 +6498,9 @@ device_link_changed(NMDevice *self) } static gboolean -device_ip_link_changed(NMDevice *self) +device_ip_link_changed(gpointer user_data) { + NMDevice * self = user_data; NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); const NMPlatformLink *pllink; const char * ip_iface; @@ -5614,13 +6563,12 @@ link_changed_cb(NMPlatform * platform, if (!(info->n_ifi_flags & IFF_UP)) priv->device_link_changed_down = TRUE; if (!priv->device_link_changed_id) { - priv->device_link_changed_id = g_idle_add((GSourceFunc) device_link_changed, self); + priv->device_link_changed_id = g_idle_add(device_link_changed, self); _LOGD(LOGD_DEVICE, "queued link change for ifindex %d", ifindex); } } else if (ifindex == nm_device_get_ip_ifindex(self)) { if (!priv->device_ip_link_changed_id) { - priv->device_ip_link_changed_id = - g_idle_add((GSourceFunc) device_ip_link_changed, self); + priv->device_ip_link_changed_id = g_idle_add(device_ip_link_changed, self); _LOGD(LOGD_DEVICE, "queued link change for ip-ifindex %d", ifindex); } } @@ -6072,8 +7020,6 @@ realize_start_setup(NMDevice * self, g_return_if_fail(nm_device_get_unmanaged_flags(self, NM_UNMANAGED_PLATFORM_INIT)); g_return_if_fail(priv->ip_ifindex <= 0); g_return_if_fail(priv->ip_iface == NULL); - g_return_if_fail(!priv->queued_ip_config_id_4); - g_return_if_fail(!priv->queued_ip_config_id_6); _LOGD(LOGD_DEVICE, "start setup of %s, kernel ifindex %d", @@ -6120,9 +7066,6 @@ realize_start_setup(NMDevice * self, if (priv->firmware_version) _notify(self, PROP_FIRMWARE_VERSION); - priv->ipv6ll_handle = (nm_platform_link_get_inet6_addr_gen_mode(platform, priv->ifindex) - == NM_IN6_ADDR_GEN_MODE_NONE); - if (nm_platform_link_supports_sriov(platform, priv->ifindex)) capabilities |= NM_DEVICE_CAP_SRIOV; } @@ -6180,7 +7123,7 @@ realize_start_setup(NMDevice * self, * or have IP addressing */ nm_device_set_unmanaged_flags(self, NM_UNMANAGED_EXTERNAL_DOWN, - is_unmanaged_external_down(self, TRUE)); + _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 @@ -6219,9 +7162,6 @@ nm_device_realize_finish(NMDevice *self, const NMPlatformLink *plink) if (plink) device_recheck_slave_status(self, plink); - priv->update_ip_config_completed_v4 = FALSE; - priv->update_ip_config_completed_v6 = FALSE; - priv->real = TRUE; _notify(self, PROP_REAL); @@ -6317,9 +7257,6 @@ nm_device_unrealize(NMDevice *self, gboolean remove_resources, GError **error) } } - nm_clear_g_source(&priv->queued_ip_config_id_4); - nm_clear_g_source(&priv->queued_ip_config_id_6); - g_object_freeze_notify(G_OBJECT(self)); NM_DEVICE_GET_CLASS(self)->unrealize_notify(self); @@ -6683,117 +7620,6 @@ nm_device_get_master(NMDevice *self) return NULL; } -static gboolean -get_ip_config_may_fail(NMDevice *self, int addr_family) -{ - NMConnection * connection; - NMSettingIPConfig *s_ip; - - connection = nm_device_get_applied_connection(self); - - s_ip = nm_connection_get_setting_ip_config(connection, addr_family); - - return !s_ip || nm_setting_ip_config_get_may_fail(s_ip); -} - -/* - * check_ip_state - * - * When @full_state_update is TRUE, transition the device from IP_CONFIG to the - * next state according to the outcome of IPv4 and IPv6 configuration. @may_fail - * indicates that we are called just after the initial configuration and thus - * IPv4/IPv6 are allowed to fail if the ipvx.may-fail properties say so, because - * the IP methods couldn't even be started. - * If @full_state_update is FALSE, just check if the connection should be failed - * due to the state of both ip families and the ipvx.may-fail settings. - */ -static void -check_ip_state(NMDevice *self, gboolean may_fail, gboolean full_state_update) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - gboolean ip4_disabled = FALSE, ip6_disabled = FALSE; - NMSettingIPConfig *s_ip4, *s_ip6; - NMDeviceState state; - int IS_IPv4; - - if (full_state_update && nm_device_get_state(self) != NM_DEVICE_STATE_IP_CONFIG) - return; - - /* Don't progress into IP_CHECK or SECONDARIES if we're waiting for the - * master to enslave us. */ - if (nm_active_connection_get_master(NM_ACTIVE_CONNECTION(priv->act_request.obj)) - && !priv->is_enslaved) - return; - - s_ip4 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP4_CONFIG); - if (s_ip4 - && nm_streq0(nm_setting_ip_config_get_method(s_ip4), NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) - ip4_disabled = TRUE; - - s_ip6 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP6_CONFIG); - if (s_ip6 - && NM_IN_STRSET(nm_setting_ip_config_get_method(s_ip6), - NM_SETTING_IP6_CONFIG_METHOD_IGNORE, - NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) - ip6_disabled = TRUE; - - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE - && priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) { - /* Both method completed (or disabled), proceed with activation */ - nm_device_state_changed(self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); - return; - } - - for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { - if (priv->ip_state_x[IS_IPv4] == NM_DEVICE_IP_STATE_CONF - && priv->ip_req_timeout_source_x[IS_IPv4]) { - return; - } - } - - if ((priv->ip_state_4 == NM_DEVICE_IP_STATE_FAIL - || (ip4_disabled && priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE)) - && (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL - || (ip6_disabled && priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE))) { - /* Either both methods failed, or only one failed and the other is - * disabled */ - if (nm_device_sys_iface_state_is_external_or_assume(self)) { - /* We have assumed configuration, but couldn't redo it. No problem, - * move to check state. */ - _set_ip_state(self, AF_INET, NM_DEVICE_IP_STATE_DONE); - _set_ip_state(self, AF_INET6, NM_DEVICE_IP_STATE_DONE); - state = NM_DEVICE_STATE_IP_CHECK; - } else if (may_fail && get_ip_config_may_fail(self, AF_INET) - && get_ip_config_may_fail(self, AF_INET6)) { - /* Couldn't start either IPv6 and IPv4 autoconfiguration, - * but both are allowed to fail. */ - state = NM_DEVICE_STATE_SECONDARIES; - } else { - /* Autoconfiguration attempted without success. */ - state = NM_DEVICE_STATE_FAILED; - } - - if (full_state_update || state == NM_DEVICE_STATE_FAILED) { - nm_device_state_changed(self, state, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } - return; - } - - /* If a method is still pending but required, wait */ - if (priv->ip_state_4 != NM_DEVICE_IP_STATE_DONE && !get_ip_config_may_fail(self, AF_INET)) - return; - if (priv->ip_state_6 != NM_DEVICE_IP_STATE_DONE && !get_ip_config_may_fail(self, AF_INET6)) - return; - - /* If at least a method has completed, proceed with activation */ - if ((priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && !ip4_disabled) - || (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE && !ip6_disabled)) { - if (full_state_update) - nm_device_state_changed(self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); - return; - } -} - /** * nm_device_slave_notify_enslave: * @self: the slave device @@ -6833,13 +7659,17 @@ nm_device_slave_notify_enslave(NMDevice *self, gboolean success) } } - if (activating) { - if (success) - check_ip_state(self, FALSE, TRUE); - else - nm_device_queue_state(self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_UNKNOWN); - } else + if (!activating) { nm_device_queue_recheck_assume(self); + return; + } + + if (!success) { + nm_device_queue_state(self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_UNKNOWN); + return; + } + + nm_device_activate_schedule_stage3_ip_config(self, FALSE); } /** @@ -6913,6 +7743,9 @@ nm_device_removed(NMDevice *self, gboolean unconfigure_ip_config) g_return_if_fail(NM_IS_DEVICE(self)); + _dev_ipdhcpx_cleanup(self, AF_INET, TRUE, FALSE); + _dev_ipdhcpx_cleanup(self, AF_INET6, TRUE, FALSE); + priv = NM_DEVICE_GET_PRIVATE(self); if (priv->master) { /* this is called when something externally messes with the slave or during shut-down. @@ -6924,15 +7757,7 @@ nm_device_removed(NMDevice *self, gboolean unconfigure_ip_config) NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); } - if (unconfigure_ip_config) { - nm_device_set_ip_config(self, AF_INET, NULL, FALSE, NULL); - nm_device_set_ip_config(self, AF_INET6, NULL, FALSE, NULL); - } else { - if (priv->dhcp_data_4.client) - nm_dhcp_client_stop(priv->dhcp_data_4.client, FALSE); - if (priv->dhcp_data_6.client) - nm_dhcp_client_stop(priv->dhcp_data_6.client, FALSE); - } + _dev_l3_register_l3cds(self, priv->l3cfg, FALSE, unconfigure_ip_config); } static gboolean @@ -7190,22 +8015,36 @@ nm_device_can_auto_connect(NMDevice *self, NMSettingsConnection *sett_conn, char static gboolean device_has_config(NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + const NMDedupMultiHeadEntry *head_entry; + const NMPlatformLink * pllink; + NMPLookup lookup; - /* Check for IP configuration. */ - if (priv->ip_config_4 && nm_ip4_config_get_num_addresses(priv->ip_config_4)) + pllink = nm_l3cfg_get_pllink(priv->l3cfg, TRUE); + if (!pllink) + return FALSE; + + if (pllink->master > 0) { + /* Master-slave relationship is also a configuration */ return TRUE; - if (priv->ip_config_6 && nm_ip6_config_get_num_addresses(priv->ip_config_6)) + } + + head_entry = nm_platform_lookup( + nm_device_get_platform(self), + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, pllink->ifindex)); + if (head_entry) return TRUE; - /* The existence of a software device is good enough. */ - if (nm_device_is_software(self) && nm_device_is_real(self)) + head_entry = nm_platform_lookup( + nm_device_get_platform(self), + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, pllink->ifindex)); + if (head_entry) return TRUE; - /* Master-slave relationship is also a configuration */ - if (!c_list_is_empty(&priv->slaves) - || nm_platform_link_get_master(nm_device_get_platform(self), priv->ifindex) > 0) + if (nm_device_is_software(self) && nm_device_is_real(self)) { + /* The existence of a software device is good enough. */ return TRUE; + } return FALSE; } @@ -7363,10 +8202,16 @@ nm_device_generate_connection(NMDevice *self, } } else { /* Only regular and master devices get IP configuration; slaves do not */ - s_ip4 = nm_ip4_config_create_setting(priv->ip_config_4); + s_ip4 = nm_utils_platform_capture_ip_setting(nm_device_get_platform(self), + AF_INET, + nm_device_get_ip_ifindex(self), + FALSE); nm_connection_add_setting(connection, s_ip4); - s_ip6 = nm_ip6_config_create_setting(priv->ip_config_6, _get_maybe_ipv6_disabled(self)); + s_ip6 = nm_utils_platform_capture_ip_setting(nm_device_get_platform(self), + AF_INET6, + nm_device_get_ip_ifindex(self), + _get_maybe_ipv6_disabled(self)); nm_connection_add_setting(connection, s_ip6); nm_connection_add_setting(connection, nm_setting_proxy_new()); @@ -7832,20 +8677,6 @@ nm_device_emit_recheck_auto_activate(NMDevice *self) g_signal_emit(self, signals[RECHECK_AUTO_ACTIVATE], 0); } -static void -dnsmasq_state_changed_cb(NMDnsMasqManager *manager, guint32 status, gpointer user_data) -{ - NMDevice *self = NM_DEVICE(user_data); - - switch (status) { - case NM_DNSMASQ_STATUS_DEAD: - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_SHARED_START_FAILED); - break; - default: - break; - } -} - void nm_device_auth_request(NMDevice * self, GDBusMethodInvocation * context, @@ -7870,148 +8701,112 @@ nm_device_auth_request(NMDevice * self, /*****************************************************************************/ static void -activation_source_clear(NMDevice *self, int addr_family) +activation_source_clear(NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (priv->activation_source_id_x[IS_IPv4] != 0) { + if (nm_clear_g_source_inst(&priv->activation_idle_source)) { _LOGD(LOGD_DEVICE, - "activation-stage: clear %s,v%c (id %u)", - _activation_func_to_string(priv->activation_source_func_x[IS_IPv4]), - nm_utils_addr_family_to_char(addr_family), - priv->activation_source_id_x[IS_IPv4]); - nm_clear_g_source(&priv->activation_source_id_x[IS_IPv4]); - priv->activation_source_func_x[IS_IPv4] = NULL; + "activation-stage: clear %s", + _activation_func_to_string(priv->activation_func)); + priv->activation_func = NULL; } } static gboolean -activation_source_handle_cb(NMDevice *self, int addr_family) +activation_source_handle_cb(gpointer user_data) { + NMDevice * self = user_data; NMDevicePrivate * priv; - const int IS_IPv4 = NM_IS_IPv4(addr_family); - ActivationHandleFunc activation_source_func; - guint activation_source_id; + ActivationHandleFunc activation_func; g_return_val_if_fail(NM_IS_DEVICE(self), G_SOURCE_REMOVE); priv = NM_DEVICE_GET_PRIVATE(self); - activation_source_func = priv->activation_source_func_x[IS_IPv4]; - activation_source_id = priv->activation_source_id_x[IS_IPv4]; + g_return_val_if_fail(priv->activation_idle_source, G_SOURCE_REMOVE); - g_return_val_if_fail(activation_source_id != 0, G_SOURCE_REMOVE); - nm_assert(activation_source_func); + nm_assert(priv->activation_func); - priv->activation_source_func_x[IS_IPv4] = NULL; - priv->activation_source_id_x[IS_IPv4] = 0; + activation_func = priv->activation_func; + priv->activation_func = NULL; - _LOGD(LOGD_DEVICE, - "activation-stage: invoke %s,v%c (id %u)", - _activation_func_to_string(activation_source_func), - nm_utils_addr_family_to_char(addr_family), - activation_source_id); + nm_clear_g_source_inst(&priv->activation_idle_source); - activation_source_func(self); + _LOGD(LOGD_DEVICE, "activation-stage: invoke %s", _activation_func_to_string(activation_func)); - _LOGT(LOGD_DEVICE, - "activation-stage: complete %s,v%c (id %u)", - _activation_func_to_string(activation_source_func), - nm_utils_addr_family_to_char(addr_family), - activation_source_id); + activation_func(self); - return G_SOURCE_REMOVE; -} - -static gboolean -activation_source_handle_cb_4(gpointer user_data) -{ - return activation_source_handle_cb(user_data, AF_INET); -} - -static gboolean -activation_source_handle_cb_6(gpointer user_data) -{ - return activation_source_handle_cb(user_data, AF_INET6); + return G_SOURCE_CONTINUE; } static void -activation_source_schedule(NMDevice *self, ActivationHandleFunc func, int addr_family) +activation_source_schedule(NMDevice *self, ActivationHandleFunc func) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - const int IS_IPv4 = NM_IS_IPv4(addr_family); - guint new_id = 0; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (priv->activation_source_id_x[IS_IPv4] != 0 - && priv->activation_source_func_x[IS_IPv4] == func) { + if (priv->activation_idle_source && priv->activation_func == func) { /* Scheduling the same stage multiple times is fine. */ _LOGT(LOGD_DEVICE, - "activation-stage: already scheduled %s,v%c (id %u)", - _activation_func_to_string(func), - nm_utils_addr_family_to_char(addr_family), - priv->activation_source_id_x[IS_IPv4]); + "activation-stage: already scheduled %s", + _activation_func_to_string(func)); return; } - new_id = - g_idle_add(IS_IPv4 ? activation_source_handle_cb_4 : activation_source_handle_cb_6, self); - - if (priv->activation_source_id_x[IS_IPv4] != 0) { + if (priv->activation_idle_source) { _LOGD(LOGD_DEVICE, - "activation-stage: schedule %s,v%c which replaces %s,v%c (id %u -> %u)", + "activation-stage: schedule %s (which replaces %s)", _activation_func_to_string(func), - nm_utils_addr_family_to_char(addr_family), - _activation_func_to_string(priv->activation_source_func_x[IS_IPv4]), - nm_utils_addr_family_to_char(addr_family), - priv->activation_source_id_x[IS_IPv4], - new_id); - nm_clear_g_source(&priv->activation_source_id_x[IS_IPv4]); + _activation_func_to_string(priv->activation_func)); + nm_clear_g_source_inst(&priv->activation_idle_source); } else { - _LOGD(LOGD_DEVICE, - "activation-stage: schedule %s,v%c (id %u)", - _activation_func_to_string(func), - nm_utils_addr_family_to_char(addr_family), - new_id); + _LOGD(LOGD_DEVICE, "activation-stage: schedule %s", _activation_func_to_string(func)); } - priv->activation_source_func_x[IS_IPv4] = func; - priv->activation_source_id_x[IS_IPv4] = new_id; + priv->activation_idle_source = nm_g_idle_add_source(activation_source_handle_cb, self); + priv->activation_func = func; } static void -activation_source_invoke_sync(NMDevice *self, ActivationHandleFunc func, int addr_family) +activation_source_invoke_sync(NMDevice *self, ActivationHandleFunc func) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (priv->activation_source_id_x[IS_IPv4] == 0) { + if (!priv->activation_idle_source) { _LOGD(LOGD_DEVICE, - "activation-stage: synchronously invoke %s,v%c", - _activation_func_to_string(func), - nm_utils_addr_family_to_char(addr_family)); - } else if (priv->activation_source_func_x[IS_IPv4] == func) { + "activation-stage: synchronously invoke %s", + _activation_func_to_string(func)); + } else if (priv->activation_func == func) { _LOGD(LOGD_DEVICE, - "activation-stage: synchronously invoke %s,v%c which was already scheduled (id %u)", - _activation_func_to_string(func), - nm_utils_addr_family_to_char(addr_family), - priv->activation_source_id_x[IS_IPv4]); + "activation-stage: synchronously invoke %s (which was already scheduled)", + _activation_func_to_string(func)); } else { _LOGD(LOGD_DEVICE, - "activation-stage: synchronously invoke %s,v%c which replaces %s,v%c (id %u)", + "activation-stage: synchronously invoke %s (which replaces %s)", _activation_func_to_string(func), - nm_utils_addr_family_to_char(addr_family), - _activation_func_to_string(priv->activation_source_func_x[IS_IPv4]), - nm_utils_addr_family_to_char(addr_family), - priv->activation_source_id_x[IS_IPv4]); + _activation_func_to_string(priv->activation_func)); } - nm_clear_g_source(&priv->activation_source_id_x[IS_IPv4]); - priv->activation_source_func_x[IS_IPv4] = NULL; + nm_clear_g_source_inst(&priv->activation_idle_source); + priv->activation_func = NULL; func(self); } +static void +activation_source_invoke_or_schedule(NMDevice *self, ActivationHandleFunc func, gboolean do_sync) +{ + nm_assert(NM_IS_DEVICE(self)); + nm_assert(NM_DEVICE_GET_PRIVATE(self)->act_request.obj); + nm_assert(func); + + if (do_sync) { + activation_source_invoke_sync(self, func); + return; + } + activation_source_schedule(self, func); +} + /*****************************************************************************/ static void @@ -8180,14 +8975,20 @@ activate_stage1_device_prepare(NMDevice *self) NMActiveConnection *master; NMDeviceClass * klass; - priv->v4_route_table_initialized = FALSE; - priv->v6_route_table_initialized = FALSE; + nm_assert((priv->ip_data_4.state == NM_DEVICE_IP_STATE_NONE) + == (priv->ip_data_6.state == NM_DEVICE_IP_STATE_NONE)); - _set_ip_state(self, AF_INET, NM_DEVICE_IP_STATE_NONE); - _set_ip_state(self, AF_INET6, NM_DEVICE_IP_STATE_NONE); + if (priv->ip_data_4.state == NM_DEVICE_IP_STATE_NONE) { + _dev_ip_state_set_state(self, AF_INET, NM_DEVICE_IP_STATE_PENDING, "stage1"); + _dev_ip_state_set_state(self, AF_INET6, NM_DEVICE_IP_STATE_PENDING, "stage1"); - /* Notify the new ActiveConnection along with the state change */ - nm_dbus_track_obj_path_set(&priv->act_request, priv->act_request.obj, TRUE); + /* Notify the new ActiveConnection along with the state change */ + nm_dbus_track_obj_path_set(&priv->act_request, priv->act_request.obj, TRUE); + + priv->v4_route_table_initialized = FALSE; + priv->v6_route_table_initialized = FALSE; + priv->l3config_merge_flags_has = FALSE; + } nm_device_state_changed(self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); @@ -8248,6 +9049,7 @@ activate_stage1_device_prepare(NMDevice *self) priv->stage1_sriov_state = NM_DEVICE_STAGE_STATE_PENDING; return; } + priv->stage1_sriov_state = NM_DEVICE_STAGE_STATE_COMPLETED; } @@ -8323,15 +9125,7 @@ activate_stage1_device_prepare(NMDevice *self) void nm_device_activate_schedule_stage1_device_prepare(NMDevice *self, gboolean do_sync) { - g_return_if_fail(NM_IS_DEVICE(self)); - g_return_if_fail(NM_DEVICE_GET_PRIVATE(self)->act_request.obj); - - if (!do_sync) { - activation_source_schedule(self, activate_stage1_device_prepare, AF_INET); - return; - } - - activation_source_invoke_sync(self, activate_stage1_device_prepare, AF_INET); + activation_source_invoke_or_schedule(self, activate_stage1_device_prepare, do_sync); } static NMActStageReturn @@ -8623,420 +9417,115 @@ activate_stage2_device_config(NMDevice *self) lldp_setup(self, NM_TERNARY_DEFAULT); - _commit_mtu(self, NULL); + _commit_mtu(self); - nm_device_activate_schedule_stage3_ip_config_start(self); + nm_device_activate_schedule_stage3_ip_config(self, TRUE); } void nm_device_activate_schedule_stage2_device_config(NMDevice *self, gboolean do_sync) { - g_return_if_fail(NM_IS_DEVICE(self)); - - if (!do_sync) { - activation_source_schedule(self, activate_stage2_device_config, AF_INET); - return; - } - - activation_source_invoke_sync(self, activate_stage2_device_config, AF_INET); -} - -void -nm_device_ip_method_failed(NMDevice *self, int addr_family, NMDeviceStateReason reason) -{ - g_return_if_fail(NM_IS_DEVICE(self)); - g_return_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6)); - - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_FAIL); - - if (get_ip_config_may_fail(self, addr_family)) - check_ip_state(self, FALSE, (nm_device_get_state(self) == NM_DEVICE_STATE_IP_CONFIG)); - else - nm_device_state_changed(self, NM_DEVICE_STATE_FAILED, reason); + activation_source_invoke_or_schedule(self, activate_stage2_device_config, do_sync); } /*****************************************************************************/ static void -acd_data_destroy(gpointer ptr) +_dev_ipllx_set_state(NMDevice *self, int addr_family, NMDeviceIPState state) { - AcdData *data = ptr; - int i; - - for (i = 0; data->configs && data->configs[i]; i++) - g_object_unref(data->configs[i]); - g_free(data->configs); - g_slice_free(AcdData, data); -} - -static void -ipv4_manual_method_apply(NMDevice *self, NMIP4Config **configs, gboolean success) -{ - NMConnection *connection; - const char * method; - - connection = nm_device_get_applied_connection(self); - nm_assert(connection); - method = nm_utils_get_ip_config_method(connection, AF_INET); - nm_assert(NM_IN_STRSET(method, - NM_SETTING_IP4_CONFIG_METHOD_MANUAL, - NM_SETTING_IP4_CONFIG_METHOD_AUTO)); - - if (!success) { - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE); - return; - } - - if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) - nm_device_activate_schedule_ip_config_result(self, AF_INET, NULL); - else { - if (NM_DEVICE_GET_PRIVATE(self)->ip_state_4 != NM_DEVICE_IP_STATE_DONE) - ip_config_merge_and_apply(self, AF_INET, TRUE); - } -} - -static void -acd_manager_probe_terminated(NMAcdManager *acd_manager, gpointer user_data) -{ - AcdData * data = user_data; - NMDevice * self; - NMDevicePrivate * priv; - NMDedupMultiIter ipconf_iter; - const NMPlatformIP4Address *address; - gboolean result, success = TRUE; - int i; - - g_assert(data); - self = data->device; - priv = NM_DEVICE_GET_PRIVATE(self); - - for (i = 0; data->configs && data->configs[i]; i++) { - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, data->configs[i], &address) { - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - result = nm_acd_manager_check_address(acd_manager, address->address); - success &= result; - - _NMLOG(result ? LOGL_DEBUG : LOGL_WARN, - LOGD_DEVICE, - "IPv4 DAD result: address %s is %s", - _nm_utils_inet4_ntop(address->address, sbuf), - result ? "unique" : "duplicate"); - } - } - - data->callback(self, data->configs, success); - - priv->acd.dad_list = g_slist_remove(priv->acd.dad_list, acd_manager); - nm_acd_manager_free(acd_manager); -} - -/** - * ipv4_dad_start: - * @self: device instance - * @configs: NULL-terminated array of IPv4 configurations - * @cb: callback function - * - * Start IPv4 DAD on device @self, check addresses in @configs and call @cb - * when the procedure ends. @cb will be called in any case, even if DAD can't - * be started. @configs will be unreferenced after @cb has been called. - */ -static void -ipv4_dad_start(NMDevice *self, NMIP4Config **configs, AcdCallback cb) -{ - static const NMAcdCallbacks acd_callbacks = { - .probe_terminated_callback = acd_manager_probe_terminated, - .user_data_destroy = acd_data_destroy, - }; - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMAcdManager * acd_manager; - const NMPlatformIP4Address *address; - NMDedupMultiIter ipconf_iter; - AcdData * data; - guint timeout; - gboolean addr_found; - int r; - const guint8 * hwaddr_arr; - size_t length; - guint i; - - g_return_if_fail(NM_IS_DEVICE(self)); - g_return_if_fail(configs); - g_return_if_fail(cb); - - for (i = 0, addr_found = FALSE; configs[i]; i++) { - if (nm_ip4_config_get_num_addresses(configs[i]) > 0) { - addr_found = TRUE; - break; - } - } - - timeout = _prop_get_ipv4_dad_timeout(self); - hwaddr_arr = nm_platform_link_get_address(nm_device_get_platform(self), - nm_device_get_ip_ifindex(self), - &length); - - if (!timeout || !hwaddr_arr || !addr_found || length != ETH_ALEN - || nm_device_sys_iface_state_is_external_or_assume(self)) { - /* DAD not needed, signal success */ - cb(self, configs, TRUE); - - for (i = 0; configs[i]; i++) - g_object_unref(configs[i]); - g_free(configs); - - return; - } - - data = g_slice_new0(AcdData); - data->configs = configs; - data->callback = cb; - data->device = self; - - acd_manager = nm_acd_manager_new(nm_device_get_ip_ifindex(self), - hwaddr_arr, - length, - &acd_callbacks, - data); - priv->acd.dad_list = g_slist_append(priv->acd.dad_list, acd_manager); - - for (i = 0; configs[i]; i++) { - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, configs[i], &address) - nm_acd_manager_add_address(acd_manager, address->address); - } - - r = nm_acd_manager_start_probe(acd_manager, timeout); - if (r < 0) { - _LOGW(LOGD_DEVICE, "acd probe failed"); - - /* DAD could not be started, signal success */ - cb(self, configs, TRUE); - - priv->acd.dad_list = g_slist_remove(priv->acd.dad_list, acd_manager); - nm_acd_manager_free(acd_manager); - } -} - -/*****************************************************************************/ -/* IPv4LL stuff */ - -static void -ipv4ll_cleanup(NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - if (priv->ipv4ll) { - sd_ipv4ll_set_callback(priv->ipv4ll, NULL, NULL); - sd_ipv4ll_stop(priv->ipv4ll); - priv->ipv4ll = sd_ipv4ll_unref(priv->ipv4ll); - } - - nm_clear_g_source(&priv->ipv4ll_timeout); -} - -static NMIP4Config * -ipv4ll_get_ip4_config(NMDevice *self, guint32 lla) -{ - NMIP4Config * config = NULL; - NMPlatformIP4Address address; - NMPlatformIP4Route route; - - config = nm_device_ip4_config_new(self); - g_assert(config); - - memset(&address, 0, sizeof(address)); - nm_platform_ip4_address_set_addr(&address, lla, 16); - address.addr_source = NM_IP_CONFIG_SOURCE_IP4LL; - nm_ip4_config_add_address(config, &address); - - /* Add a multicast route for link-local connections: destination= 224.0.0.0, netmask=240.0.0.0 */ - memset(&route, 0, sizeof(route)); - route.network = htonl(0xE0000000L); - route.plen = 4; - route.rt_source = NM_IP_CONFIG_SOURCE_IP4LL; - route.table_coerced = nm_platform_route_table_coerce(nm_device_get_route_table(self, AF_INET)); - route.metric = nm_device_get_route_metric(self, AF_INET); - nm_ip4_config_add_route(config, &route, NULL); - - return config; -} - -static void -nm_device_handle_ipv4ll_event(sd_ipv4ll *ll, int event, void *data) -{ - NMDevice * self = data; - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - struct in_addr address; - NMIP4Config * config; - int r; - - if (priv->act_request.obj == NULL) - return; - - nm_assert(nm_streq(nm_device_get_effective_ip_config_method(self, AF_INET), - NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)); - - switch (event) { - case SD_IPV4LL_EVENT_BIND: - r = sd_ipv4ll_get_address(ll, &address); - if (r < 0) { - _LOGE(LOGD_AUTOIP4, "invalid IPv4 link-local address received, error %d.", r); - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED); - return; - } - - if (!nm_utils_ip4_address_is_link_local(address.s_addr)) { - _LOGE(LOGD_AUTOIP4, "invalid address %08x received (not link-local).", address.s_addr); - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_ERROR); - return; - } - - config = ipv4ll_get_ip4_config(self, address.s_addr); - if (config == NULL) { - _LOGE(LOGD_AUTOIP4, "failed to get IPv4LL config"); - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED); - return; - } - - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF) { - nm_clear_g_source(&priv->ipv4ll_timeout); - nm_device_activate_schedule_ip_config_result(self, AF_INET, NM_IP_CONFIG_CAST(config)); - } else if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE) { - applied_config_init(&priv->dev_ip_config_4, config); - if (!ip_config_merge_and_apply(self, AF_INET, TRUE)) { - _LOGE(LOGD_AUTOIP4, "failed to update IP4 config for autoip change."); - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED); - } - } else - g_assert_not_reached(); - - g_object_unref(config); - break; - default: - _LOGW(LOGD_AUTOIP4, "IPv4LL address no longer valid after event %d.", event); - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED); - } -} - -static gboolean -ipv4ll_timeout_cb(gpointer user_data) -{ - NMDevice * self = NM_DEVICE(user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - if (priv->ipv4ll_timeout) { - _LOGI(LOGD_AUTOIP4, "IPv4LL configuration timed out."); - priv->ipv4ll_timeout = 0; - ipv4ll_cleanup(self); - - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF) - nm_device_activate_schedule_ip_config_timeout(self, AF_INET); - } - - return FALSE; -} - -static NMActStageReturn -ipv4ll_start(NMDevice *self) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - const struct ether_addr *addr; - int ifindex, r; - size_t addr_len; - - ipv4ll_cleanup(self); - - r = sd_ipv4ll_new(&priv->ipv4ll); - if (r < 0) { - _LOGE(LOGD_AUTOIP4, "IPv4LL: new() failed with error %d", r); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - r = sd_ipv4ll_attach_event(priv->ipv4ll, NULL, 0); - if (r < 0) { - _LOGE(LOGD_AUTOIP4, "IPv4LL: attach_event() failed with error %d", r); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - ifindex = nm_device_get_ip_ifindex(self); - addr = nm_platform_link_get_address(nm_device_get_platform(self), ifindex, &addr_len); - if (!addr || addr_len != ETH_ALEN) { - _LOGE(LOGD_AUTOIP4, "IPv4LL: can't retrieve hardware address"); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - r = sd_ipv4ll_set_mac(priv->ipv4ll, addr); - if (r < 0) { - _LOGE(LOGD_AUTOIP4, "IPv4LL: set_mac() failed with error %d", r); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - r = sd_ipv4ll_set_ifindex(priv->ipv4ll, ifindex); - if (r < 0) { - _LOGE(LOGD_AUTOIP4, "IPv4LL: set_ifindex() failed with error %d", r); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - r = sd_ipv4ll_set_callback(priv->ipv4ll, nm_device_handle_ipv4ll_event, self); - if (r < 0) { - _LOGE(LOGD_AUTOIP4, "IPv4LL: set_callback() failed with error %d", r); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - r = sd_ipv4ll_start(priv->ipv4ll); - if (r < 0) { - _LOGE(LOGD_AUTOIP4, "IPv4LL: start() failed with error %d", r); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - _LOGI(LOGD_DEVICE | LOGD_AUTOIP4, "IPv4LL: started"); - - /* Start a timeout to bound the address attempt */ - priv->ipv4ll_timeout = g_timeout_add_seconds(20, ipv4ll_timeout_cb, self); - return NM_ACT_STAGE_RETURN_POSTPONE; -} - -/*****************************************************************************/ - -static void -ensure_con_ip_config(NMDevice *self, int addr_family) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMConnection * connection; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); const int IS_IPv4 = NM_IS_IPv4(addr_family); - NMIPConfig * con_ip_config; - if (priv->con_ip_config_x[IS_IPv4]) - return; + if (priv->ipll_data_x[IS_IPv4].state != state) { + _LOGD_ipll(addr_family, + "set state %s (was %s)", + nm_device_ip_state_to_string(state), + nm_device_ip_state_to_string(priv->ipll_data_x[IS_IPv4].state)); + priv->ipll_data_x[IS_IPv4].state = state; + } +} - connection = nm_device_get_applied_connection(self); - if (!connection) - return; - - con_ip_config = nm_device_ip_config_new(self, addr_family); +static void +_dev_ipllx_cleanup(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); if (IS_IPv4) { - nm_ip4_config_merge_setting(NM_IP4_CONFIG(con_ip_config), - nm_connection_get_setting_ip4_config(connection), - _prop_get_connection_mdns(self), - _prop_get_connection_llmnr(self), - _prop_get_connection_dns_over_tls(self), - nm_device_get_route_table(self, addr_family), - nm_device_get_route_metric(self, addr_family)); + if (nm_clear_pointer(&priv->ipll_data_4.v4.ipv4ll, nm_l3_ipv4ll_unref)) + nm_clear_pointer(&priv->ipll_data_4.v4.ipv4ll_registation, + nm_l3_ipv4ll_register_remove); + else + nm_assert(!priv->ipll_data_4.v4.ipv4ll_registation); + + nm_clear_g_source_inst(&priv->ipll_data_4.v4.timeout_source); } else { - nm_ip6_config_merge_setting(NM_IP6_CONFIG(con_ip_config), - nm_connection_get_setting_ip6_config(connection), - nm_device_get_route_table(self, addr_family), - nm_device_get_route_metric(self, addr_family)); + nm_clear_pointer(&priv->ipll_data_6.v6.ipv6ll, nm_l3_ipv6ll_destroy); + priv->ipll_data_6.v6.llstate = NM_L3_IPV6LL_STATE_NONE; + priv->ipll_data_6.v6.lladdr = nm_ip_addr_zero.addr6; + nm_clear_g_source_inst(&priv->ipll_data_6.v6.retry_source); } - if (nm_device_sys_iface_state_is_external_or_assume(self)) { - /* For assumed connections ignore all addresses and routes. */ - nm_ip_config_reset_addresses(con_ip_config); - nm_ip_config_reset_routes(con_ip_config); + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_LL_X(IS_IPv4), NULL, FALSE); + + _dev_ipllx_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE); +} + +/*****************************************************************************/ + +static void +_dev_ipll4_notify_event(NMDevice *self) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NML3IPv4LLState ipv4ll_state; + const NML3ConfigData *l3cd; + NMDeviceIPState state; + + nm_assert(NM_IS_L3_IPV4LL(priv->ipll_data_4.v4.ipv4ll)); + nm_assert(priv->ipll_data_4.state >= NM_DEVICE_IP_STATE_PENDING); + + ipv4ll_state = nm_l3_ipv4ll_get_state(priv->ipll_data_4.v4.ipv4ll); + + if (nm_l3_ipv4ll_state_is_good(ipv4ll_state)) { + l3cd = nm_l3_ipv4ll_get_l3cd(priv->ipll_data_4.v4.ipv4ll); + nm_assert(NM_IS_L3_CONFIG_DATA(l3cd)); + nm_assert(!nm_l3_ipv4ll_is_timed_out(priv->ipll_data_4.v4.ipv4ll)); + state = NM_DEVICE_IP_STATE_READY; + } else if (priv->ipll_data_4.v4.ipv4ll + && nm_l3_ipv4ll_is_timed_out(priv->ipll_data_4.v4.ipv4ll)) { + l3cd = NULL; + state = NM_DEVICE_IP_STATE_FAILED; + } else { + l3cd = NULL; + state = (priv->ipll_data_4.state == NM_DEVICE_IP_STATE_PENDING) ? NM_DEVICE_IP_STATE_PENDING + : NM_DEVICE_IP_STATE_FAILED; } - priv->con_ip_config_x[IS_IPv4] = con_ip_config; + _dev_ipllx_set_state(self, AF_INET, state); + + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_LL_4, l3cd, FALSE); + + _dev_ip_state_check_async(self, AF_INET); +} + +static void +_dev_ipll4_start(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + guint32 timeout_msec; + + if (priv->ipll_data_4.state >= NM_DEVICE_IP_STATE_PENDING) + return; + + _dev_ipllx_set_state(self, AF_INET, NM_DEVICE_IP_STATE_PENDING); + + timeout_msec = _prop_get_ipv4_dad_timeout(self); + if (timeout_msec == 0) + timeout_msec = NM_ACD_TIMEOUT_RFC5227_MSEC; + + priv->ipll_data_4.v4.ipv4ll = nm_l3cfg_access_ipv4ll(priv->l3cfg); + priv->ipll_data_4.v4.ipv4ll_registation = + nm_l3_ipv4ll_register_new(priv->ipll_data_4.v4.ipv4ll, timeout_msec); } /*****************************************************************************/ @@ -9056,545 +9545,488 @@ _device_get_dhcp_anycast_address(NMDevice *self) return NULL; } +/*****************************************************************************/ + +static IPDevStateData * +_dev_ipdev_data(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + switch (addr_family) { + case AF_INET: + return &priv->ipdev_data_4; + case AF_INET6: + return &priv->ipdev_data_6; + default: + nm_assert_not_reached(); + /* fall-through */ + case AF_UNSPEC: + return &priv->ipdev_data_unspec; + } +} + static void -dhcp4_cleanup(NMDevice *self, CleanupType cleanup_type, gboolean release) +_dev_ipdev_cleanup(NMDevice *self, int addr_family) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + IPDevStateData *p; - priv->dhcp_data_4.was_active = FALSE; - nm_clear_g_source(&priv->dhcp_data_4.grace_id); - priv->dhcp_data_4.grace_pending = FALSE; - nm_clear_g_free(&priv->dhcp4.pac_url); - - if (priv->dhcp_data_4.client) { - /* Stop any ongoing DHCP transaction on this device */ - nm_clear_g_signal_handler(priv->dhcp_data_4.client, &priv->dhcp_data_4.notify_sigid); - - if (cleanup_type == CLEANUP_TYPE_DECONFIGURE || cleanup_type == CLEANUP_TYPE_REMOVED) - nm_dhcp_client_stop(priv->dhcp_data_4.client, release); - - g_clear_object(&priv->dhcp_data_4.client); + p = _dev_ipdev_data(self, addr_family); + if (p->state != NM_DEVICE_IP_STATE_NONE) { + _LOGD_ipdev(addr_family, "reset state"); + p->state = NM_DEVICE_IP_STATE_NONE; + p->failed_reason = NM_DEVICE_STATE_REASON_NONE; } - if (priv->dhcp_data_4.config) { - nm_dbus_object_clear_and_unexport(&priv->dhcp_data_4.config); - _notify(self, PROP_DHCP4_CONFIG); - } + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_DEVIP(addr_family), NULL, FALSE); } -static gboolean -ip_config_merge_and_apply(NMDevice *self, int addr_family, gboolean commit) +NMDeviceIPState +nm_device_devip_get_state(NMDevice *self, int addr_family) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - gboolean success; - gs_unref_object NMIPConfig *composite = NULL; - NMIPConfig * config; - gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL; - NMConnection * connection; - gboolean ignore_auto_routes = FALSE; - gboolean ignore_auto_dns = FALSE; - gboolean ignore_default_routes = FALSE; - GSList * iter; - const char * ip6_addr_gen_token = NULL; - const int IS_IPv4 = NM_IS_IPv4(addr_family); + g_return_val_if_fail(NM_IS_DEVICE(self), NM_DEVICE_IP_STATE_NONE); - if (nm_device_sys_iface_state_is_external(self)) - commit = FALSE; - - connection = nm_device_get_applied_connection(self); - - /* Apply ignore-auto-routes and ignore-auto-dns settings */ - if (connection) { - NMSettingIPConfig *s_ip; - - s_ip = nm_connection_get_setting_ip_config(connection, addr_family); - if (s_ip) { - ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes(s_ip); - ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns(s_ip); - - /* if the connection has an explicit gateway, we also ignore - * the default routes from other sources. */ - ignore_default_routes = nm_setting_ip_config_get_never_default(s_ip) - || nm_setting_ip_config_get_gateway(s_ip); - - if (!IS_IPv4) { - NMSettingIP6Config *s_ip6 = NM_SETTING_IP6_CONFIG(s_ip); - - if (nm_setting_ip6_config_get_addr_gen_mode(s_ip6) - == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) - ip6_addr_gen_token = nm_setting_ip6_config_get_token(s_ip6); - } - } - } - - composite = nm_device_ip_config_new(self, addr_family); - - if (!IS_IPv4) { - nm_ip6_config_set_privacy(NM_IP6_CONFIG(composite), - priv->ndisc ? priv->ndisc_use_tempaddr - : NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - } - - init_ip_config_dns_priority(self, composite); - - if (commit) { - if (priv->queued_ip_config_id_x[IS_IPv4]) - update_ext_ip_config(self, addr_family, FALSE); - ensure_con_ip_config(self, addr_family); - } - - if (!IS_IPv4) { - if (commit && priv->ipv6ll_has) { - const NMPlatformIP6Address ll_a = { - .address = priv->ipv6ll_addr, - .plen = 64, - .addr_source = NM_IP_CONFIG_SOURCE_IP6LL, - }; - const NMPlatformIP6Route ll_r = { - .network.s6_addr16[0] = htons(0xfe80u), - .plen = 64, - .metric = nm_device_get_route_metric(self, addr_family), - .rt_source = NM_IP_CONFIG_SOURCE_IP6LL, - }; - - nm_assert(IN6_IS_ADDR_LINKLOCAL(&priv->ipv6ll_addr)); - - nm_ip6_config_add_address(NM_IP6_CONFIG(composite), &ll_a); - nm_ip6_config_add_route(NM_IP6_CONFIG(composite), &ll_r, NULL); - } - } - - if (commit) { - gboolean v; - - v = default_route_metric_penalty_detect(self, addr_family); - if (IS_IPv4) - priv->default_route_metric_penalty_ip4_has = v; - else - priv->default_route_metric_penalty_ip6_has = v; - } - - /* Merge all the IP configs into the composite config */ - - if (IS_IPv4) { - config = applied_config_get_current(&priv->dev_ip_config_4); - if (config) { - nm_ip4_config_merge( - NM_IP4_CONFIG(composite), - NM_IP4_CONFIG(config), - (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), - default_route_metric_penalty_get(self, addr_family)); - } - } - - if (!IS_IPv4) { - config = applied_config_get_current(&priv->ac_ip6_config); - if (config) { - nm_ip6_config_merge( - NM_IP6_CONFIG(composite), - NM_IP6_CONFIG(config), - (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), - default_route_metric_penalty_get(self, addr_family)); - } - } - - if (!IS_IPv4) { - config = applied_config_get_current(&priv->dhcp6.ip6_config); - if (config) { - nm_ip6_config_merge( - NM_IP6_CONFIG(composite), - NM_IP6_CONFIG(config), - (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), - default_route_metric_penalty_get(self, addr_family)); - } - } - - for (iter = priv->vpn_configs_x[IS_IPv4]; iter; iter = iter->next) - nm_ip_config_merge(composite, iter->data, NM_IP_CONFIG_MERGE_DEFAULT, 0); - - if (priv->ext_ip_config_x[IS_IPv4]) - nm_ip_config_merge(composite, - priv->ext_ip_config_x[IS_IPv4], - NM_IP_CONFIG_MERGE_EXTERNAL, - 0); - - /* Merge WWAN config *last* to ensure modem-given settings overwrite - * any external stuff set by pppd or other scripts. - */ - config = applied_config_get_current(&priv->dev2_ip_config_x[IS_IPv4]); - if (config) { - nm_ip_config_merge(composite, - config, - (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), - default_route_metric_penalty_get(self, addr_family)); - } - - if (!IS_IPv4) { - if (priv->rt6_temporary_not_available) { - const NMPObject *o; - GHashTableIter hiter; - - g_hash_table_iter_init(&hiter, priv->rt6_temporary_not_available); - while (g_hash_table_iter_next(&hiter, (gpointer *) &o, NULL)) { - nm_ip6_config_add_route(NM_IP6_CONFIG(composite), - NMP_OBJECT_CAST_IP6_ROUTE(o), - NULL); - } - } - } - - /* Merge user overrides into the composite config. For assumed connections, - * con_ip_config_x is empty. */ - if (priv->con_ip_config_x[IS_IPv4]) { - nm_ip_config_merge(composite, - priv->con_ip_config_x[IS_IPv4], - NM_IP_CONFIG_MERGE_DEFAULT, - default_route_metric_penalty_get(self, addr_family)); - } - - if (commit) { - gboolean is_vrf; - - is_vrf = priv->master && nm_device_get_device_type(priv->master) == NM_DEVICE_TYPE_VRF; - - if (IS_IPv4) { - nm_ip4_config_add_dependent_routes(NM_IP4_CONFIG(composite), - nm_device_get_route_table(self, addr_family), - nm_device_get_route_metric(self, addr_family), - is_vrf, - &ip4_dev_route_blacklist); - } else { - nm_ip6_config_add_dependent_routes(NM_IP6_CONFIG(composite), - nm_device_get_route_table(self, addr_family), - nm_device_get_route_metric(self, addr_family), - is_vrf); - } - } - - if (IS_IPv4) { - if (commit) { - if (NM_DEVICE_GET_CLASS(self)->ip4_config_pre_commit) - NM_DEVICE_GET_CLASS(self)->ip4_config_pre_commit(self, NM_IP4_CONFIG(composite)); - } - } - - if (!IS_IPv4) { - NMUtilsIPv6IfaceId iid; - - if (commit && priv->ndisc_started && ip6_addr_gen_token - && nm_utils_ipv6_interface_identifier_get_from_token(&iid, ip6_addr_gen_token)) { - set_ipv6_token(self, &iid, ip6_addr_gen_token); - } - } - - success = - nm_device_set_ip_config(self, addr_family, composite, commit, ip4_dev_route_blacklist); - if (commit) { - if (IS_IPv4) - priv->v4_commit_first_time = FALSE; - else - priv->v6_commit_first_time = FALSE; - } - - return success; + return _dev_ipdev_data(self, addr_family)->state; } -static gboolean -dhcp4_lease_change(NMDevice *self, NMIP4Config *config, gboolean bound) +void +nm_device_devip_set_state_full(NMDevice * self, + int addr_family, + NMDeviceIPState ip_state, + const NML3ConfigData *l3cd, + NMDeviceStateReason failed_reason) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - gs_free_error GError *error = NULL; + NMDevicePrivate *priv; + IPDevStateData * p; - g_return_val_if_fail(config, FALSE); + g_return_if_fail(NM_IS_DEVICE(self)); - applied_config_init(&priv->dev_ip_config_4, config); + priv = NM_DEVICE_GET_PRIVATE(self); - if (!ip_config_merge_and_apply(self, AF_INET, TRUE)) { - _LOGW(LOGD_DHCP4, "failed to update IPv4 config for DHCP change."); - return FALSE; + nm_assert_addr_family_or_unspec(addr_family); + nm_assert(NM_IN_SET(ip_state, + NM_DEVICE_IP_STATE_PENDING, + NM_DEVICE_IP_STATE_READY, + NM_DEVICE_IP_STATE_FAILED)); + nm_assert(!l3cd || NM_IS_L3_CONFIG_DATA(l3cd)); + + nm_assert((ip_state != NM_DEVICE_IP_STATE_FAILED) + == (failed_reason == NM_DEVICE_STATE_REASON_NONE)); + nm_assert((ip_state != NM_DEVICE_IP_STATE_FAILED) || !l3cd); + + p = _dev_ipdev_data(self, addr_family); + + if (p->state == ip_state && p->failed_reason == failed_reason + && priv->l3cds[L3_CONFIG_DATA_TYPE_DEVIP(addr_family)].d == l3cd) + return; + + if (ip_state == NM_DEVICE_IP_STATE_FAILED) { + _LOGD_ipdev(addr_family, + "set state=failed (reason %s)", + nm_device_state_reason_to_string_a(failed_reason)); + } else { + _LOGD_ipdev(addr_family, + "set state=%s%s", + nm_device_ip_state_to_string(ip_state), + l3cd ? " (has extra IP configuration)" : ""); } - - /* TODO: we should perform DAD again whenever we obtain a - * new lease after an expiry. But what should we do if - * a duplicate address is detected? Fail the connection; - * restart DHCP; continue without an address? */ - if (bound && !nm_dhcp_client_accept(priv->dhcp_data_4.client, &error)) { - _LOGW(LOGD_DHCP4, "error accepting lease: %s", error->message); - return FALSE; - } - - nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_4, self, NULL, NULL, NULL, NULL); - - return TRUE; + p->state = ip_state; + p->failed_reason = failed_reason; + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_DEVIP(addr_family), l3cd, FALSE); + _dev_ip_state_check_async(self, addr_family); } -static gboolean -dhcp_grace_period_expired(NMDevice *self, int addr_family) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - const int IS_IPv4 = NM_IS_IPv4(addr_family); +/*****************************************************************************/ - priv->dhcp_data_x[IS_IPv4].grace_id = 0; - priv->dhcp_data_x[IS_IPv4].grace_pending = FALSE; - - _LOGI(LOGD_DHCPX(IS_IPv4), - "DHCPv%c: grace period expired", - nm_utils_addr_family_to_char(addr_family)); - - nm_device_ip_method_failed(self, addr_family, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); - /* If the device didn't fail, the DHCP client will continue */ - - return G_SOURCE_REMOVE; -} - -static gboolean -dhcp_grace_period_expired_4(gpointer user_data) -{ - return dhcp_grace_period_expired(user_data, AF_INET); -} - -static gboolean -dhcp_grace_period_expired_6(gpointer user_data) -{ - return dhcp_grace_period_expired(user_data, AF_INET6); -} - -static gboolean -dhcp_grace_period_start(NMDevice *self, int addr_family) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - const int IS_IPv4 = NM_IS_IPv4(addr_family); - guint32 timeout; - - /* In any other case (expired lease, assumed connection, etc.), - * wait for some time before failing the IP method. - */ - if (priv->dhcp_data_x[IS_IPv4].grace_pending) { - /* already pending. */ - return FALSE; - } - - /* Start a grace period equal to the DHCP timeout multiplied - * by a constant factor. */ - timeout = _prop_get_ipvx_dhcp_timeout(self, addr_family); - if (timeout == NM_DHCP_TIMEOUT_INFINITY) - _LOGI(LOGD_DHCPX(IS_IPv4), - "DHCPv%c: trying to acquire a new lease", - nm_utils_addr_family_to_char(addr_family)); - else { - timeout = dhcp_grace_period_from_timeout(timeout); - _LOGI(LOGD_DHCPX(IS_IPv4), - "DHCPv%c: trying to acquire a new lease within %u seconds", - nm_utils_addr_family_to_char(addr_family), - timeout); - nm_assert(!priv->dhcp_data_x[IS_IPv4].grace_id); - priv->dhcp_data_x[IS_IPv4].grace_id = g_timeout_add_seconds( - timeout, - IS_IPv4 ? dhcp_grace_period_expired_4 : dhcp_grace_period_expired_6, - self); - } - - priv->dhcp_data_x[IS_IPv4].grace_pending = TRUE; - - return TRUE; -} static void -dhcp4_fail(NMDevice *self, NMDhcpState dhcp_state) +_dev_ipmanual_set_state(NMDevice *self, int addr_family, NMDeviceIPState state) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + int IS_IPv4; - _LOGD(LOGD_DHCP4, - "DHCPv4 failed (ip_state %s, was_active %d)", - nm_device_ip_state_to_string(priv->ip_state_4), - priv->dhcp_data_4.was_active); - - /* The client is always left running after a failure. */ - - /* Nothing to do if we failed before... */ - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_FAIL) - goto clear_config; - - /* ... and also if there are static addresses configured - * on the interface. - */ - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && priv->con_ip_config_4 - && nm_ip4_config_get_num_addresses(priv->con_ip_config_4) > 0) - goto clear_config; - - /* Fail the method when one of the following is true: - * 1) the DHCP client terminated: it does not make sense to start a grace - * period without a client running; - * 2) we failed to get an initial lease AND the client was - * not active before. - */ - if (dhcp_state == NM_DHCP_STATE_TERMINATED - || (!priv->dhcp_data_4.was_active && priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF)) { - nm_device_activate_schedule_ip_config_timeout(self, AF_INET); + if (addr_family == AF_UNSPEC) { + _dev_ipmanual_set_state(self, AF_INET, state); + _dev_ipmanual_set_state(self, AF_INET6, state); return; } - if (dhcp_grace_period_start(self, AF_INET)) - goto clear_config; - - return; - -clear_config: - /* The previous configuration is no longer valid */ - if (priv->dhcp_data_4.config) { - nm_dbus_object_clear_and_unexport(&priv->dhcp_data_4.config); - priv->dhcp_data_4.config = nm_dhcp_config_new(AF_INET); - _notify(self, PROP_DHCP4_CONFIG); + IS_IPv4 = NM_IS_IPv4(addr_family); + if (priv->ipmanual_data.state_x[IS_IPv4] != state) { + _LOGD_ipmanual(addr_family, "set state %s", nm_device_ip_state_to_string(state)); + priv->ipmanual_data.state_x[IS_IPv4] = state; } } static void -dhcp4_dad_cb(NMDevice *self, NMIP4Config **configs, gboolean success) +_dev_ipmanual_cleanup(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (success) { - nm_device_activate_schedule_ip_config_result(self, AF_INET, NM_IP_CONFIG_CAST(configs[1])); + if (priv->ipmanual_data.state_4 == NM_DEVICE_IP_STATE_NONE + && priv->ipmanual_data.state_6 == NM_DEVICE_IP_STATE_NONE) { + nm_assert(!priv->l3cds[L3_CONFIG_DATA_TYPE_MANUALIP].d); + return; + } + + _dev_ipmanual_set_state(self, AF_UNSPEC, NM_DEVICE_IP_STATE_NONE); + nm_clear_g_source_inst(&priv->ipmanual_data.carrier_timeout); + priv->ipmanual_data.carrier_timeout_expired = FALSE; + + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_MANUALIP, NULL, FALSE); + + _dev_ip_state_check_async(self, AF_INET); + _dev_ip_state_check_async(self, AF_INET6); +} + +static gboolean +_dev_ipmanual_carrier_timeout(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + priv->ipmanual_data.carrier_timeout_expired = TRUE; + nm_clear_g_source_inst(&priv->ipmanual_data.carrier_timeout); + _dev_ipmanual_check_ready(self); + + return G_SOURCE_CONTINUE; +} + +static void +_dev_ipmanual_check_ready(NMDevice *self) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + const NMPlatformLink * plink; + gboolean has_carrier; + NML3CfgCheckReadyFlags flags; + gboolean ready; + gboolean acd_used = FALSE; + int IS_IPv4; + + if (priv->ipmanual_data.state_4 != NM_DEVICE_IP_STATE_PENDING + && priv->ipmanual_data.state_6 != NM_DEVICE_IP_STATE_PENDING) { + /* we only care about PENDING to get it READY. Currently not other + * conditions are implemented. That is, we cannot get to FAILED + * (maybe we should, if DAD fails) and we cannot get from anything + * once we are READY. */ + return; + } + + plink = nm_l3cfg_get_pllink(priv->l3cfg, TRUE); + has_carrier = plink && NM_FLAGS_HAS(plink->n_ifi_flags, IFF_LOWER_UP); + + if (has_carrier) { + nm_clear_g_source_inst(&priv->ipmanual_data.carrier_timeout); } else { - nm_dhcp_client_decline(priv->dhcp_data_4.client, "Address conflict detected", NULL); - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE); + if (priv->ipmanual_data.carrier_timeout_expired) { + /* go on */ + } else if (priv->ipmanual_data.carrier_timeout) { + /* wait a bit more until timer expires */ + return; + } else { + priv->ipmanual_data.carrier_timeout = + nm_g_timeout_add_source(2000, G_SOURCE_FUNC(_dev_ipmanual_carrier_timeout), self); + return; + } + } + + flags = NM_L3CFG_CHECK_READY_FLAGS_NONE; + if (has_carrier) { + flags |= NM_L3CFG_CHECK_READY_FLAGS_IP4_ACD_READY; + flags |= NM_L3CFG_CHECK_READY_FLAGS_IP6_DAD_READY; + } + + for (IS_IPv4 = 0; IS_IPv4 < 2; IS_IPv4++) { + const int addr_family = IS_IPv4 ? AF_INET : AF_INET6; + + ready = nm_l3cfg_check_ready(priv->l3cfg, + priv->l3cds[L3_CONFIG_DATA_TYPE_MANUALIP].d, + addr_family, + flags, + &acd_used); + if (acd_used) { + _dev_ipmanual_set_state(self, addr_family, NM_DEVICE_IP_STATE_FAILED); + _dev_ip_state_check_async(self, AF_UNSPEC); + } else if (ready) { + _dev_ipmanual_set_state(self, addr_family, NM_DEVICE_IP_STATE_READY); + _dev_ip_state_check_async(self, AF_UNSPEC); + } } } static void -dhcp4_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_data, NMDevice *self) +_dev_ipmanual_start(NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMIP4Config * manual; - NMIP4Config ** configs; - NMConnection * connection; - NMDhcpState state; - NMIP4Config * ip4_config; - GHashTable * options; + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; - nm_assert(nm_dhcp_client_get_addr_family(client) == AF_INET); - nm_assert(notify_data); - nm_assert(notify_data->notify_type == NM_DHCP_CLIENT_NOTIFY_TYPE_STATE_CHANGED); + if (priv->ipmanual_data.state_4 != NM_DEVICE_IP_STATE_NONE + || priv->ipmanual_data.state_6 != NM_DEVICE_IP_STATE_NONE) + return; - state = notify_data->state_changed.dhcp_state; - ip4_config = NM_IP4_CONFIG(notify_data->state_changed.ip_config); - options = notify_data->state_changed.options; + if (nm_device_get_ip_ifindex(self) > 0) { + l3cd = + nm_device_create_l3_config_data_from_connection(self, + nm_device_get_applied_connection(self)); + } - nm_assert(!ip4_config || NM_IS_IP4_CONFIG(ip4_config)); + if (!l3cd) { + _dev_ipmanual_cleanup(self); + return; + } - _LOGD(LOGD_DHCP4, "new DHCPv4 client state %d", (int) state); + /* Initially we set the state to pending, because we (maybe) have to perform ACD first. */ + _dev_ipmanual_set_state(self, AF_UNSPEC, NM_DEVICE_IP_STATE_PENDING); - switch (state) { - case NM_DHCP_STATE_BOUND: - case NM_DHCP_STATE_EXTENDED: - if (!ip4_config) { - _LOGW(LOGD_DHCP4, "failed to get IPv4 config in response to DHCP event."); - dhcp4_fail(self, state); - break; - } + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_MANUALIP, l3cd, FALSE); - nm_clear_g_source(&priv->dhcp_data_4.grace_id); - priv->dhcp_data_4.grace_pending = FALSE; + _dev_ip_state_check_async(self, AF_UNSPEC); +} - /* After some failures, we have been able to renew the lease: - * update the ip state - */ - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_FAIL) - _set_ip_state(self, AF_INET, NM_DEVICE_IP_STATE_CONF); +/*****************************************************************************/ - g_free(priv->dhcp4.pac_url); - priv->dhcp4.pac_url = g_strdup(g_hash_table_lookup(options, "wpad")); - nm_device_set_proxy_config(self, priv->dhcp4.pac_url); +static void +_dev_ipdhcpx_set_state(NMDevice *self, int addr_family, NMDeviceIPState state) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); - nm_dhcp_config_set_options(priv->dhcp_data_4.config, options); - _notify(self, PROP_DHCP4_CONFIG); - - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF) { - connection = nm_device_get_applied_connection(self); - g_assert(connection); - - manual = nm_device_ip4_config_new(self); - nm_ip4_config_merge_setting(manual, - nm_connection_get_setting_ip4_config(connection), - NM_SETTING_CONNECTION_MDNS_DEFAULT, - NM_SETTING_CONNECTION_LLMNR_DEFAULT, - NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT, - nm_device_get_route_table(self, AF_INET), - nm_device_get_route_metric(self, AF_INET)); - - configs = g_new0(NMIP4Config *, 3); - configs[0] = manual; - configs[1] = g_object_ref(ip4_config); - - ipv4_dad_start(self, configs, dhcp4_dad_cb); - } else if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE) { - if (dhcp4_lease_change(self, ip4_config, state == NM_DHCP_STATE_BOUND)) - nm_device_update_metered(self); - else - dhcp4_fail(self, state); - } - break; - case NM_DHCP_STATE_TIMEOUT: - dhcp4_fail(self, state); - break; - case NM_DHCP_STATE_EXPIRE: - /* Ignore expiry before we even have a lease (NAK, old lease, etc) */ - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF) - break; - /* fall-through */ - case NM_DHCP_STATE_DONE: - case NM_DHCP_STATE_FAIL: - case NM_DHCP_STATE_TERMINATED: - dhcp4_fail(self, state); - break; - default: - break; + if (priv->ipdhcp_data_x[IS_IPv4].state != state) { + _LOGD_ipdhcp(addr_family, + "set state %s (was %s)", + nm_device_ip_state_to_string(state), + nm_device_ip_state_to_string(priv->ipdhcp_data_x[IS_IPv4].state)); + priv->ipdhcp_data_x[IS_IPv4].state = state; } } -static NMActStageReturn -dhcp4_start(NMDevice *self) +static void +_dev_ipdhcpx_cleanup(NMDevice *self, int addr_family, gboolean full_cleanup, gboolean release) { - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMSettingIPConfig *s_ip4; - gs_unref_bytes GBytes *vendor_class_identifier = NULL; - gs_unref_bytes GBytes *hwaddr = NULL; - gs_unref_bytes GBytes *bcast_hwaddr = NULL; - gs_unref_bytes GBytes *client_id = NULL; - NMConnection * connection; - NMSettingConnection * s_con; - GError * error = NULL; - const NMPlatformLink * pllink; - const char *const * reject_servers; - gboolean request_broadcast; - const char * str; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + + _dev_ipdhcpx_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE); + + if (full_cleanup && !IS_IPv4) { + priv->ipdhcp_data_6.v6.mode = NM_NDISC_DHCP_LEVEL_NONE; + priv->ipdhcp_data_6.v6.needed_prefixes = 0; + } + + if (full_cleanup) + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_DHCP_X(IS_IPv4), NULL, FALSE); + + if (priv->ipdhcp_data_x[IS_IPv4].client) { + nm_clear_g_signal_handler(priv->ipdhcp_data_x[IS_IPv4].client, + &priv->ipdhcp_data_x[IS_IPv4].notify_sigid); + nm_dhcp_client_stop(priv->ipdhcp_data_x[IS_IPv4].client, release); + g_clear_object(&priv->ipdhcp_data_x[IS_IPv4].client); + } + + if (full_cleanup && priv->ipdhcp_data_x[IS_IPv4].config) { + gs_unref_object NMDhcpConfig *config = + g_steal_pointer(&priv->ipdhcp_data_x[IS_IPv4].config); + + _notify(self, PROP_DHCPX_CONFIG(IS_IPv4)); + nm_dbus_object_unexport_on_idle(g_steal_pointer(&config)); + } + + _dev_ip_state_check_async(self, addr_family); +} + +static void +_dev_ipdhcpx_handle_fail(NMDevice *self, int addr_family, const char *reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + + if (priv->ipdhcp_data_x[IS_IPv4].state == NM_DEVICE_IP_STATE_FAILED) + return; + + _LOGT_ipdhcp(addr_family, "DHCP failing: %s", reason ?: "unknown reason"); + + _dev_ipdhcpx_set_state(self, addr_family, NM_DEVICE_IP_STATE_FAILED); + + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_DHCP_X(IS_IPv4), NULL, FALSE); + + if (priv->ipdhcp_data_x[IS_IPv4].config) + nm_dhcp_config_set_lease(priv->ipdhcp_data_x[IS_IPv4].config, NULL); + + _dev_ip_state_check_async(self, addr_family); +} + +static void +_dev_ipdhcpx_handle_accept(NMDevice *self, int addr_family, const NML3ConfigData *l3cd) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + gs_free_error GError *error = NULL; + + nm_assert(NM_IS_L3_CONFIG_DATA(l3cd)); + + nm_dhcp_config_set_lease(priv->ipdhcp_data_x[IS_IPv4].config, l3cd); + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_DHCP_X(IS_IPv4), l3cd, TRUE); + + /* FIXME(l3cfg:dhcp): accept also should be handled by NMDhcpClient transparently. + * NMDhcpClient should do ACD (if enabled), and only after that passes, it exposes + * the lease to the user (NMDevice). NMDevice then may choose to configure the address. + * NMDhcpClient then checks in NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT whether the requested + * address is to be configured by NML3Cfg (here, the intent is what matters, not + * whether the address is actually visible in NMPlatform -- that is because while NML3Cfg + * configures the address (in platform), the user might delete it right away. Also, + * depending on the commit type, NML3Cfg may even choose not to configure it right now + * (which arguably will be odd). Anyway, what matters is whether the user configured + * the address in NML3Cfg (by checking the ObjStateData). + * + * If yes, then NMDhcpClient needs to accept automatically. + * + * Doing it here is wrong for two reasons: + * + * - NMDevice already has enough to do, it should not be concerned with telling + * NMDhcpClient to accept the lease, it should only configure the address. + * + * - we should only accept the lease *after* configuring the address (see n_dhcp4_client_lease_accept(). + * Currently this only works by passing commit_sync=TRUE to _dev_l3_register_l3cds_set_one(), but + * doing that is wrong (see FIXME why commit_sync needs to go away). */ + if (priv->ipdhcp_data_x[IS_IPv4].state != NM_DEVICE_IP_STATE_READY + && !nm_dhcp_client_accept(priv->ipdhcp_data_x[IS_IPv4].client, &error)) { + _LOGW(LOGD_DHCPX(IS_IPv4), "error accepting lease: %s", error->message); + _dev_ipdhcpx_set_state(self, addr_family, NM_DEVICE_IP_STATE_FAILED); + _dev_ip_state_check_async(self, addr_family); + return; + } + + _dev_ipdhcpx_set_state(self, addr_family, NM_DEVICE_IP_STATE_READY); + + nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_X(IS_IPv4), + self, + NULL, + NULL, + NULL, + NULL); + + _dev_ip_state_check_async(self, addr_family); +} + +static void +_dev_ipdhcpx_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_data, NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int addr_family = nm_dhcp_client_get_addr_family(client); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + + nm_assert(notify_data); + nm_assert(priv->ipdhcp_data_x[IS_IPv4].state > NM_DEVICE_IP_STATE_NONE); + nm_assert(client && priv->ipdhcp_data_x[IS_IPv4].client == client); + + switch (notify_data->notify_type) { + case NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED: + nm_assert(!IS_IPv4); + /* Just re-emit. The device just contributes the prefix to the + * pool in NMPolicy, which decides about subnet allocation + * on the shared devices. */ + g_signal_emit(self, signals[IP6_PREFIX_DELEGATED], 0, notify_data->prefix_delegated.prefix); + return; + + case NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT: + /* Here we also fail if we had a lease and it expired. Maybe, + * ipv[46].dhcp-timeout should only cover the time until we get + * a lease for the first time. How it is here, it means that a + * connection can fail after being connected successfully for a + * longer time. */ + _dev_ipdhcpx_handle_fail(self, addr_family, "timeout getting lease"); + return; + + case NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD: + /* Like NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT, this does not + * apply only if we never got a lease, but also after being fully + * connected. We can also fail then. */ + _dev_ipdhcpx_handle_fail(self, addr_family, notify_data->it_looks_bad.reason); + return; + + case NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE: + + if (!notify_data->lease_update.l3cd) { + _LOGT_ipdhcp(addr_family, "lease lost"); + _dev_ipdhcpx_handle_fail(self, addr_family, "lease lost"); + return; + } + + _LOGT_ipdhcp(addr_family, "lease update"); + _dev_ipdhcpx_handle_accept(self, addr_family, notify_data->lease_update.l3cd); + return; + } + + nm_assert_not_reached(); +} + +/*****************************************************************************/ + +static void +_dev_ipdhcpx_start(NMDevice *self, int addr_family) +{ + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingIPConfig * s_ip; + const NML3ConfigData *previous_lease; + gs_unref_bytes GBytes *hwaddr = NULL; + gboolean enforce_duid = FALSE; + gs_free_error GError *error = NULL; + const NMPlatformLink *pllink; + guint no_lease_timeout_sec; + int ifindex; + const char * str; + gboolean request_broadcast; + const char * fail_reason; + + if (priv->ipdhcp_data_x[IS_IPv4].state == NM_DEVICE_IP_STATE_NONE) + _dev_ipdhcpx_set_state(self, addr_family, NM_DEVICE_IP_STATE_PENDING); + else if (priv->ipdhcp_data_x[IS_IPv4].state > NM_DEVICE_IP_STATE_PENDING) { + /* already succeeded or failed */ + return; + } else if (priv->ipdhcp_data_x[IS_IPv4].client) { + /* DHCP client already started */ + return; + } + + if (nm_device_sys_iface_state_is_external(self)) { + fail_reason = nm_assert_unreachable_val("cannot run DHCP on external interface"); + goto out_fail; + } connection = nm_device_get_applied_connection(self); - g_return_val_if_fail(connection, FALSE); - - s_ip4 = nm_connection_get_setting_ip4_config(connection); + if (!connection) { + fail_reason = nm_assert_unreachable_val("no applied connection for starting DHCP"); + goto out_fail; + } s_con = nm_connection_get_setting_connection(connection); + s_ip = nm_connection_get_setting_ip_config(connection, addr_family); nm_assert(s_con); + nm_assert(s_ip); - /* Clear old exported DHCP options */ - nm_dbus_object_clear_and_unexport(&priv->dhcp_data_4.config); - priv->dhcp_data_4.config = nm_dhcp_config_new(AF_INET); + ifindex = 0; + pllink = nm_l3cfg_get_pllink(priv->l3cfg, TRUE); + if (pllink) { + ifindex = pllink->ifindex; + nm_assert(ifindex > 0); + nm_assert(ifindex == nm_device_get_ip_ifindex(self)); + } + if (ifindex <= 0) { + fail_reason = "cannot start DHCP without interface"; + goto out_fail; + } + + hwaddr = nmp_link_address_get_as_bytes(&pllink->l_address); + + if (!IS_IPv4) { + if (!hwaddr) { + fail_reason = "interface has no MAC address to start DHCPv6"; + goto out_fail; + } + } request_broadcast = FALSE; - - pllink = nm_platform_link_get(nm_device_get_platform(self), nm_device_get_ip_ifindex(self)); if (pllink) { - hwaddr = nmp_link_address_get_as_bytes(&pllink->l_address); - bcast_hwaddr = nmp_link_address_get_as_bytes(&pllink->l_broadcast); - str = nmp_object_link_udev_device_get_property_value(NMP_OBJECT_UP_CAST(pllink), "ID_NET_DHCP_BROADCAST"); if (str && _nm_utils_ascii_str_to_bool(str, FALSE)) { @@ -9606,103 +10038,205 @@ dhcp4_start(NMDevice *self) } } - client_id = _prop_get_ipv4_dhcp_client_id(self, connection, hwaddr); - vendor_class_identifier = - _prop_get_ipv4_dhcp_vendor_class_identifier(self, NM_SETTING_IP4_CONFIG(s_ip4)); - reject_servers = nm_setting_ip_config_get_dhcp_reject_servers(s_ip4, NULL); - - g_warn_if_fail(priv->dhcp_data_4.client == NULL); - priv->dhcp_data_4.client = nm_dhcp_manager_start_ip4( - nm_dhcp_manager_get(), - nm_netns_get_multi_idx(nm_device_get_netns(self)), - nm_device_get_ip_iface(self), - nm_device_get_ip_ifindex(self), - hwaddr, - bcast_hwaddr, - nm_connection_get_uuid(connection), - nm_device_get_route_table(self, AF_INET), - nm_device_get_route_metric(self, AF_INET), - request_broadcast ? NM_DHCP_CLIENT_FLAGS_REQUEST_BROADCAST : NM_DHCP_CLIENT_FLAGS_NONE, - nm_setting_ip_config_get_dhcp_send_hostname(s_ip4), - nm_setting_ip_config_get_dhcp_hostname(s_ip4), - nm_setting_ip4_config_get_dhcp_fqdn(NM_SETTING_IP4_CONFIG(s_ip4)), - _prop_get_ipvx_dhcp_hostname_flags(self, AF_INET), - _prop_get_connection_mud_url(self, s_con), - client_id, - _prop_get_ipvx_dhcp_timeout(self, AF_INET), - _device_get_dhcp_anycast_address(self), - NULL, - vendor_class_identifier, - reject_servers, - &error); - if (!priv->dhcp_data_4.client) { - _LOGW(LOGD_DHCP4, "failure to start DHCP: %s", error->message); - g_clear_error(&error); - return NM_ACT_STAGE_RETURN_FAILURE; + if (!IS_IPv4 + && NM_IN_SET(priv->ipll_data_6.state, + NM_DEVICE_IP_STATE_NONE, + NM_DEVICE_IP_STATE_PENDING)) { + _dev_ipll6_start(self); + return; } - priv->dhcp_data_4.notify_sigid = g_signal_connect(priv->dhcp_data_4.client, - NM_DHCP_CLIENT_NOTIFY, - G_CALLBACK(dhcp4_notify), - self); + no_lease_timeout_sec = _prop_get_ipvx_dhcp_timeout(self, addr_family); - if (nm_device_sys_iface_state_is_external_or_assume(self)) - priv->dhcp_data_4.was_active = TRUE; + if (IS_IPv4) { + NMDhcpClientConfig config; + gs_unref_bytes GBytes *bcast_hwaddr = NULL; + gs_unref_bytes GBytes *client_id = NULL; + gs_unref_bytes GBytes *vendor_class_identifier = NULL; + const char *const * reject_servers; + const char * hostname; + gboolean hostname_is_fqdn; - /* DHCP devices will be notified by the DHCP manager when stuff happens */ - return NM_ACT_STAGE_RETURN_POSTPONE; + client_id = _prop_get_ipv4_dhcp_client_id(self, connection, hwaddr); + vendor_class_identifier = + _prop_get_ipv4_dhcp_vendor_class_identifier(self, NM_SETTING_IP4_CONFIG(s_ip)); + reject_servers = nm_setting_ip_config_get_dhcp_reject_servers(s_ip, NULL); + + bcast_hwaddr = nmp_link_address_get_as_bytes(&pllink->l_broadcast); + + hostname = nm_setting_ip4_config_get_dhcp_fqdn(NM_SETTING_IP4_CONFIG(s_ip)); + if (hostname) { + hostname_is_fqdn = TRUE; + } else { + hostname_is_fqdn = FALSE; + hostname = nm_setting_ip_config_get_dhcp_hostname(s_ip); + } + + config = (NMDhcpClientConfig){ + .addr_family = AF_INET, + .l3cfg = nm_device_get_l3cfg(self), + .iface = nm_device_get_ip_iface(self), + .uuid = nm_connection_get_uuid(connection), + .hwaddr = hwaddr, + .bcast_hwaddr = bcast_hwaddr, + .send_hostname = nm_setting_ip_config_get_dhcp_send_hostname(s_ip), + .hostname = hostname, + .hostname_flags = _prop_get_ipvx_dhcp_hostname_flags(self, AF_INET), + .client_id = client_id, + .mud_url = _prop_get_connection_mud_url(self, s_con), + .timeout = no_lease_timeout_sec, + .anycast_address = _device_get_dhcp_anycast_address(self), + .vendor_class_identifier = vendor_class_identifier, + .use_fqdn = hostname_is_fqdn, + .reject_servers = reject_servers, + .v4.request_broadcast = request_broadcast, + }; + + priv->ipdhcp_data_4.client = + nm_dhcp_manager_start_client(nm_dhcp_manager_get(), &config, &error); + } else { + gs_unref_bytes GBytes *duid = NULL; + gboolean iaid_explicit; + guint32 iaid; + NMDhcpClientConfig config; + + iaid = _prop_get_ipvx_dhcp_iaid(self, AF_INET6, connection, FALSE, &iaid_explicit); + duid = _prop_get_ipv6_dhcp_duid(self, connection, hwaddr, &enforce_duid); + + config = (NMDhcpClientConfig){ + .addr_family = AF_INET6, + .l3cfg = nm_device_get_l3cfg(self), + .iface = nm_device_get_ip_iface(self), + .uuid = nm_connection_get_uuid(connection), + .send_hostname = nm_setting_ip_config_get_dhcp_send_hostname(s_ip), + .hostname = nm_setting_ip_config_get_dhcp_hostname(s_ip), + .hostname_flags = _prop_get_ipvx_dhcp_hostname_flags(self, AF_INET6), + .client_id = duid, + .mud_url = _prop_get_connection_mud_url(self, s_con), + .timeout = no_lease_timeout_sec, + .anycast_address = _device_get_dhcp_anycast_address(self), + .v6.enforce_duid = enforce_duid, + .v6.iaid = iaid, + .v6.iaid_explicit = iaid_explicit, + .v6.info_only = (priv->ipdhcp_data_6.v6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF), + .v6.needed_prefixes = priv->ipdhcp_data_6.v6.needed_prefixes, + }; + + priv->ipdhcp_data_6.client = + nm_dhcp_manager_start_client(nm_dhcp_manager_get(), &config, &error); + } + + if (!priv->ipdhcp_data_x[IS_IPv4].client) { + fail_reason = error->message; + goto out_fail; + } + + priv->ipdhcp_data_x[IS_IPv4].notify_sigid = + g_signal_connect(priv->ipdhcp_data_x[IS_IPv4].client, + NM_DHCP_CLIENT_NOTIFY, + 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. */ + + 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) + _dev_ipdhcpx_handle_accept(self, addr_family, previous_lease); + + return; + +out_fail: + _dev_ipdhcpx_handle_fail(self, addr_family, fail_reason); } -gboolean -nm_device_dhcp4_renew(NMDevice *self, gboolean release) +static void +_dev_ipdhcpx_start_continue(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + + if (priv->ipdhcp_data_x[IS_IPv4].state != NM_DEVICE_IP_STATE_NONE) + _dev_ipdhcpx_start(self, addr_family); +} + +static void +_dev_ipdhcpx_restart(NMDevice *self, int addr_family, gboolean release) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + + if (priv->ipdhcp_data_x[IS_IPv4].state != NM_DEVICE_IP_STATE_NONE) { + _LOGI_ipdhcp(addr_family, "restarting%s", release ? " (release lease)" : ""); + _dev_ipdhcpx_cleanup(self, addr_family, FALSE, release); + } + + _dev_ipdhcpx_start(self, addr_family); +} + +void +nm_device_ip_method_dhcp4_start(NMDevice *self) +{ + _dev_ipdhcpx_start(self, AF_INET); +} + +static void +_dev_ipdhcp6_set_dhcp_level(NMDevice *self, NMNDiscDHCPLevel dhcp_level) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - g_return_val_if_fail(priv->dhcp_data_4.client != NULL, FALSE); + nm_assert(NM_IN_SET(dhcp_level, + NM_NDISC_DHCP_LEVEL_NONE, + NM_NDISC_DHCP_LEVEL_OTHERCONF, + NM_NDISC_DHCP_LEVEL_MANAGED)); - _LOGI(LOGD_DHCP4, "DHCPv4 lease renewal requested"); + if (dhcp_level == NM_NDISC_DHCP_LEVEL_NONE && priv->ipdhcp_data_6.v6.needed_prefixes > 0) + dhcp_level = NM_NDISC_DHCP_LEVEL_OTHERCONF; - /* Terminate old DHCP instance and release the old lease */ - dhcp4_cleanup(self, CLEANUP_TYPE_DECONFIGURE, release); + if (priv->ipdhcp_data_6.v6.mode == dhcp_level) + return; - /* Start DHCP again on the interface */ - return dhcp4_start(self) != NM_ACT_STAGE_RETURN_FAILURE; -} + _LOGD_ipdhcp(AF_INET6, "level: set to %s", nm_ndisc_dhcp_level_to_string(dhcp_level)); -/*****************************************************************************/ - -static NMIP4Config * -shared4_new_config(NMDevice *self, NMConnection *connection) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMIP4Config * config; - NMSettingIPConfig * s_ip4; - NMPlatformIP4Address address = { - .addr_source = NM_IP_CONFIG_SOURCE_SHARED, - }; - - g_return_val_if_fail(self, NULL); - g_return_val_if_fail(connection, NULL); - - s_ip4 = nm_connection_get_setting_ip4_config(connection); - if (s_ip4 && nm_setting_ip_config_get_num_addresses(s_ip4) > 0) { - /* Use the first user-supplied address */ - NMIPAddress *user = nm_setting_ip_config_get_address(s_ip4, 0); - in_addr_t a; - - nm_ip_address_get_address_binary(user, &a); - nm_platform_ip4_address_set_addr(&address, a, nm_ip_address_get_prefix(user)); - nm_clear_pointer(&priv->shared_ip_handle, nm_netns_shared_ip_release); - } else { - if (!priv->shared_ip_handle) - priv->shared_ip_handle = nm_netns_shared_ip_reserve(nm_device_get_netns(self)); - nm_platform_ip4_address_set_addr(&address, priv->shared_ip_handle->addr, 24); + if (dhcp_level == NM_NDISC_DHCP_LEVEL_NONE) { + _dev_ipdhcpx_cleanup(self, AF_INET6, TRUE, TRUE); + return; } - config = nm_device_ip4_config_new(self); - nm_ip4_config_add_address(config, &address); + priv->ipdhcp_data_6.v6.mode = dhcp_level; + _dev_ipdhcpx_restart(self, AF_INET6, TRUE); +} - return config; +/* + * Called on the requesting interface when a subnet can't be obtained + * from known prefixes for a newly active shared connection. + */ +void +nm_device_request_ip6_prefixes(NMDevice *self, guint needed_prefixes) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + if (priv->ipdhcp_data_6.v6.needed_prefixes == needed_prefixes) + return; + + _LOGD(LOGD_IP6, "ipv6-pd: asking DHCPv6 for %u prefixes", needed_prefixes); + + priv->ipdhcp_data_6.v6.needed_prefixes = needed_prefixes; + + if (priv->ipdhcp_data_6.v6.mode == NM_NDISC_DHCP_LEVEL_NONE) { + priv->ipdhcp_data_6.v6.mode = NM_NDISC_DHCP_LEVEL_OTHERCONF; + _LOGD_ipdhcp(AF_INET6, + "level: set to %s", + nm_ndisc_dhcp_level_to_string(NM_NDISC_DHCP_LEVEL_OTHERCONF)); + } + + _dev_ipdhcpx_restart(self, AF_INET6, TRUE); } /*****************************************************************************/ @@ -9800,392 +10334,6 @@ have_any_ready_slaves(NMDevice *self) } /*****************************************************************************/ -/* DHCPv6 stuff */ - -static void -dhcp6_cleanup(NMDevice *self, CleanupType cleanup_type, gboolean release) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - priv->dhcp_data_6.was_active = FALSE; - priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_NONE; - applied_config_clear(&priv->dhcp6.ip6_config); - nm_clear_g_free(&priv->dhcp6.event_id); - nm_clear_g_source(&priv->dhcp_data_6.grace_id); - priv->dhcp_data_6.grace_pending = FALSE; - - if (priv->dhcp_data_6.client) { - nm_clear_g_signal_handler(priv->dhcp_data_6.client, &priv->dhcp_data_6.notify_sigid); - - if (cleanup_type == CLEANUP_TYPE_DECONFIGURE || cleanup_type == CLEANUP_TYPE_REMOVED) - nm_dhcp_client_stop(priv->dhcp_data_6.client, release); - - g_clear_object(&priv->dhcp_data_6.client); - } - - if (priv->dhcp_data_6.config) { - nm_dbus_object_clear_and_unexport(&priv->dhcp_data_6.config); - _notify(self, PROP_DHCP6_CONFIG); - } -} - -static gboolean -dhcp6_lease_change(NMDevice *self) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMSettingsConnection *settings_connection; - - if (!applied_config_get_current(&priv->dhcp6.ip6_config)) { - _LOGW(LOGD_DHCP6, "failed to get DHCPv6 config for rebind"); - return FALSE; - } - - g_assert(priv->dhcp_data_6.client); /* sanity check */ - - settings_connection = nm_device_get_settings_connection(self); - g_assert(settings_connection); - - /* Apply the updated config */ - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) { - _LOGW(LOGD_DHCP6, "failed to update IPv6 config in response to DHCP event"); - return FALSE; - } - - nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_6, self, NULL, NULL, NULL, NULL); - - return TRUE; -} - -static void -dhcp6_fail(NMDevice *self, NMDhcpState dhcp_state) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - gboolean is_dhcp_managed; - - _LOGD(LOGD_DHCP6, - "DHCPv6 failed (ip_state %s, was_active %d)", - nm_device_ip_state_to_string(priv->ip_state_6), - priv->dhcp_data_6.was_active); - - /* The client is always left running after a failure. */ - - /* Nothing to do if we failed before... */ - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL) - goto clear_config; - - is_dhcp_managed = (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED); - - if (is_dhcp_managed) { - /* ... and also if there are static addresses configured - * on the interface. - */ - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE && priv->con_ip_config_6 - && nm_ip6_config_get_num_addresses(priv->con_ip_config_6)) - goto clear_config; - - /* Fail the method when one of the following is true: - * 1) the DHCP client terminated: it does not make sense to start a grace - * period without a client running; - * 2) we failed to get an initial lease AND the client was - * not active before. - */ - if (dhcp_state == NM_DHCP_STATE_TERMINATED - || (!priv->dhcp_data_6.was_active && priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF)) { - nm_device_activate_schedule_ip_config_timeout(self, AF_INET6); - return; - } - - if (dhcp_grace_period_start(self, AF_INET6)) - goto clear_config; - } else { - /* not a hard failure; just live with the RA info */ - dhcp6_cleanup(self, CLEANUP_TYPE_DECONFIGURE, FALSE); - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - } - return; - -clear_config: - /* The previous configuration is no longer valid */ - if (priv->dhcp_data_6.config) { - nm_dbus_object_clear_and_unexport(&priv->dhcp_data_6.config); - priv->dhcp_data_6.config = nm_dhcp_config_new(AF_INET6); - _notify(self, PROP_DHCP6_CONFIG); - } -} - -static void -dhcp6_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_data, NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - gs_free char * event_id = NULL; - NMDhcpState state; - NMIP6Config * ip6_config; - GHashTable * options; - - nm_assert(nm_dhcp_client_get_addr_family(client) == AF_INET6); - nm_assert(notify_data); - - if (notify_data->notify_type == NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED) { - /* Just re-emit. The device just contributes the prefix to the - * pool in NMPolicy, which decides about subnet allocation - * on the shared devices. */ - g_signal_emit(self, signals[IP6_PREFIX_DELEGATED], 0, notify_data->prefix_delegated.prefix); - return; - } - - nm_assert(notify_data->notify_type == NM_DHCP_CLIENT_NOTIFY_TYPE_STATE_CHANGED); - - state = notify_data->state_changed.dhcp_state; - ip6_config = NM_IP6_CONFIG(notify_data->state_changed.ip_config); - options = notify_data->state_changed.options; - - nm_assert(!ip6_config || NM_IS_IP6_CONFIG(ip6_config)); - - _LOGD(LOGD_DHCP6, "new DHCPv6 client state %d", (int) state); - - switch (state) { - case NM_DHCP_STATE_BOUND: - case NM_DHCP_STATE_EXTENDED: - nm_clear_g_source(&priv->dhcp_data_6.grace_id); - priv->dhcp_data_6.grace_pending = FALSE; - /* If the server sends multiple IPv6 addresses, we receive a state - * changed event for each of them. Use the event ID to merge IPv6 - * addresses from the same transaction into a single configuration. - */ - - event_id = nm_dhcp_utils_get_dhcp6_event_id(options); - - if (ip6_config && event_id && priv->dhcp6.event_id - && nm_streq(event_id, priv->dhcp6.event_id)) { - NMDedupMultiIter ipconf_iter; - const NMPlatformIP6Address *a; - - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6_config, &a) - applied_config_add_address(&priv->dhcp6.ip6_config, NM_PLATFORM_IP_ADDRESS_CAST(a)); - } else { - nm_clear_g_free(&priv->dhcp6.event_id); - if (ip6_config) { - applied_config_init(&priv->dhcp6.ip6_config, ip6_config); - priv->dhcp6.event_id = g_strdup(event_id); - nm_dhcp_config_set_options(priv->dhcp_data_6.config, options); - _notify(self, PROP_DHCP6_CONFIG); - } else - applied_config_clear(&priv->dhcp6.ip6_config); - } - - /* After long time we have been able to renew the lease: - * update the ip state - */ - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL) - _set_ip_state(self, AF_INET6, NM_DEVICE_IP_STATE_CONF); - - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) { - if (!applied_config_get_current(&priv->dhcp6.ip6_config)) { - nm_device_ip_method_failed(self, AF_INET6, NM_DEVICE_STATE_REASON_DHCP_FAILED); - break; - } - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - } else if (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) - if (!dhcp6_lease_change(self)) - dhcp6_fail(self, state); - break; - case NM_DHCP_STATE_TIMEOUT: - if (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED) - dhcp6_fail(self, state); - else { - /* not a hard failure; just live with the RA info */ - dhcp6_cleanup(self, CLEANUP_TYPE_DECONFIGURE, FALSE); - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - } - break; - case NM_DHCP_STATE_EXPIRE: - /* Ignore expiry before we even have a lease (NAK, old lease, etc) */ - if (priv->ip_state_6 != NM_DEVICE_IP_STATE_CONF) - dhcp6_fail(self, state); - break; - case NM_DHCP_STATE_TERMINATED: - /* In IPv6 info-only mode, the client doesn't handle leases so it - * may exit right after getting a response from the server. That's - * normal. In that case we just ignore the exit. - */ - if (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) - break; - /* fall-through */ - case NM_DHCP_STATE_DONE: - case NM_DHCP_STATE_FAIL: - dhcp6_fail(self, state); - break; - default: - break; - } -} - -/*****************************************************************************/ - -static gboolean -dhcp6_start_with_link_ready(NMDevice *self, NMConnection *connection) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMSettingIPConfig *s_ip6; - gs_unref_bytes GBytes *hwaddr = NULL; - gs_unref_bytes GBytes * duid = NULL; - gboolean enforce_duid = FALSE; - const NMPlatformLink * pllink; - GError * error = NULL; - guint32 iaid; - gboolean iaid_explicit; - NMSettingConnection * s_con; - const NMPlatformIP6Address *ll_addr = NULL; - int ip_ifindex; - - g_return_val_if_fail(connection, FALSE); - - s_ip6 = nm_connection_get_setting_ip6_config(connection); - nm_assert(s_ip6); - s_con = nm_connection_get_setting_connection(connection); - nm_assert(s_con); - - if (priv->ext_ip6_config_captured) { - ll_addr = nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ext_ip6_config_captured), - NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL); - } - - if (!ll_addr) { - _LOGW(LOGD_DHCP6, "can't start DHCPv6: no link-local address"); - return FALSE; - } - - ip_ifindex = nm_device_get_ip_ifindex(self); - if (ip_ifindex <= 0) { - _LOGD(LOGD_DHCP6, "can't start DHCPv6: interface is gone"); - return FALSE; - } - - pllink = nm_platform_link_get(nm_device_get_platform(self), ip_ifindex); - if (pllink) - hwaddr = nmp_link_address_get_as_bytes(&pllink->l_address); - - iaid = _prop_get_ipvx_dhcp_iaid(self, AF_INET6, connection, TRUE, &iaid_explicit); - duid = _prop_get_ipv6_dhcp_duid(self, connection, hwaddr, &enforce_duid); - - priv->dhcp_data_6.client = nm_dhcp_manager_start_ip6( - nm_dhcp_manager_get(), - nm_device_get_multi_index(self), - nm_device_get_ip_iface(self), - ip_ifindex, - &ll_addr->address, - nm_connection_get_uuid(connection), - nm_device_get_route_table(self, AF_INET6), - nm_device_get_route_metric(self, AF_INET6), - (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) ? NM_DHCP_CLIENT_FLAGS_INFO_ONLY - : NM_DHCP_CLIENT_FLAGS_NONE, - nm_setting_ip_config_get_dhcp_send_hostname(s_ip6), - nm_setting_ip_config_get_dhcp_hostname(s_ip6), - _prop_get_ipvx_dhcp_hostname_flags(self, AF_INET6), - _prop_get_connection_mud_url(self, s_con), - duid, - enforce_duid, - iaid, - iaid_explicit, - _prop_get_ipvx_dhcp_timeout(self, AF_INET6), - _device_get_dhcp_anycast_address(self), - nm_setting_ip6_config_get_ip6_privacy(NM_SETTING_IP6_CONFIG(s_ip6)), - priv->dhcp6.needed_prefixes, - &error); - if (!priv->dhcp_data_6.client) { - _LOGW(LOGD_DHCP6, "failure to start DHCPv6: %s", error->message); - g_clear_error(&error); - if (nm_device_sys_iface_state_is_external_or_assume(self)) - priv->dhcp_data_6.was_active = TRUE; - return FALSE; - } - - priv->dhcp_data_6.notify_sigid = g_signal_connect(priv->dhcp_data_6.client, - NM_DHCP_CLIENT_NOTIFY, - G_CALLBACK(dhcp6_notify), - self); - - if (nm_device_sys_iface_state_is_external_or_assume(self)) - priv->dhcp_data_6.was_active = TRUE; - - return TRUE; -} - -static gboolean -dhcp6_start(NMDevice *self, gboolean wait_for_ll) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMConnection * connection; - - nm_dbus_object_clear_and_unexport(&priv->dhcp_data_6.config); - priv->dhcp_data_6.config = nm_dhcp_config_new(AF_INET6); - - nm_assert(!applied_config_get_current(&priv->dhcp6.ip6_config)); - applied_config_clear(&priv->dhcp6.ip6_config); - nm_clear_g_free(&priv->dhcp6.event_id); - - connection = nm_device_get_applied_connection(self); - g_return_val_if_fail(connection, FALSE); - - if (wait_for_ll) { - /* ensure link local is ready... */ - if (!linklocal6_start(self)) { - /* wait for the LL address to show up */ - return TRUE; - } - /* already have the LL address; kick off DHCP */ - } - - if (!dhcp6_start_with_link_ready(self, connection)) - return FALSE; - - return TRUE; -} - -gboolean -nm_device_dhcp6_renew(NMDevice *self, gboolean release) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMNDiscDHCPLevel mode; - - g_return_val_if_fail(priv->dhcp_data_6.client != NULL, FALSE); - - _LOGI(LOGD_DHCP6, "DHCPv6 lease renewal requested"); - - /* Terminate old DHCP instance and release the old lease */ - mode = priv->dhcp6.mode; - dhcp6_cleanup(self, CLEANUP_TYPE_DECONFIGURE, release); - priv->dhcp6.mode = mode; - - /* Start DHCP again on the interface */ - return dhcp6_start(self, FALSE); -} - -/*****************************************************************************/ - -/* - * Called on the requesting interface when a subnet can't be obtained - * from known prefixes for a newly active shared connection. - */ -void -nm_device_request_ip6_prefixes(NMDevice *self, int needed_prefixes) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - priv->dhcp6.needed_prefixes = needed_prefixes; - - if (priv->dhcp_data_6.client) { - _LOGD(LOGD_IP6, "ipv6-pd: asking DHCPv6 for %d prefixes", needed_prefixes); - nm_device_dhcp6_renew(self, FALSE); - } else { - priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_OTHERCONF; - _LOGD(LOGD_DEVICE | LOGD_DHCP6, "ipv6-pd: starting DHCPv6 to request a prefix"); - dhcp6_start(self, FALSE); - } -} gboolean nm_device_needs_ip6_subnet(NMDevice *self) @@ -10200,25 +10348,25 @@ nm_device_needs_ip6_subnet(NMDevice *self) void nm_device_use_ip6_subnet(NMDevice *self, const NMPlatformIP6Address *subnet) { - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMPlatformIP6Address address = *subnet; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + char sbuf[sizeof(_nm_utils_to_string_buffer)]; + NMPlatformIP6Address address; - if (!applied_config_get_current(&priv->ac_ip6_config)) - applied_config_init_new(&priv->ac_ip6_config, self, AF_INET6); + l3cd = nm_device_create_l3_config_data(self, NM_IP_CONFIG_SOURCE_SHARED); /* Assign a ::1 address in the subnet for us. */ + address = *subnet; address.address.s6_addr32[3] |= htonl(1); - applied_config_add_address(&priv->ac_ip6_config, NM_PLATFORM_IP_ADDRESS_CAST(&address)); + + nm_l3_config_data_add_address_6(l3cd, &address); _LOGD(LOGD_IP6, - "ipv6-pd: using %s address (preferred for %u seconds)", - _nm_utils_inet6_ntop(&address.address, sbuf), - subnet->preferred); + "ipv6-pd: using %s", + nm_platform_ip6_address_to_string(&address, sbuf, sizeof(sbuf))); - /* This also updates the ndisc if there are actual changes. */ - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "ipv6-pd: failed applying IP6 config for connection sharing"); + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_PD_6, l3cd, FALSE); + _dev_l3_cfg_commit(self, TRUE); + _dev_ipac6_ndisc_set_router_config(self); } /* @@ -10228,137 +10376,186 @@ nm_device_use_ip6_subnet(NMDevice *self, const NMPlatformIP6Address *subnet) void nm_device_copy_ip6_dns_config(NMDevice *self, NMDevice *from_device) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMIP6Config * from_config = NULL; - guint i, len; + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NMDevicePrivate * priv_src; + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + const NML3ConfigData * l3cd_src = NULL; - if (applied_config_get_current(&priv->ac_ip6_config)) { - applied_config_reset_nameservers(&priv->ac_ip6_config); - applied_config_reset_searches(&priv->ac_ip6_config); + /* FIXME(l3cfg): this entire code an approach seems flawed. It's flawed, because the + * very next RA will reset the changes. */ + + if (priv->l3cds[L3_CONFIG_DATA_TYPE_AC_6].d) { + l3cd = nm_l3_config_data_new_clone(priv->l3cds[L3_CONFIG_DATA_TYPE_AC_6].d, 0); + nm_l3_config_data_clear_nameservers(l3cd, AF_INET6); + nm_l3_config_data_clear_searches(l3cd, AF_INET6); } else - applied_config_init_new(&priv->ac_ip6_config, self, AF_INET6); + l3cd = nm_device_create_l3_config_data(self, NM_IP_CONFIG_SOURCE_SHARED); - if (from_device) - from_config = nm_device_get_ip6_config(from_device); - if (!from_config) - return; + if (from_device) { + priv_src = NM_DEVICE_GET_PRIVATE(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 *const *addrs; + guint n; + guint i; - len = nm_ip6_config_get_num_nameservers(from_config); - for (i = 0; i < len; i++) { - applied_config_add_nameserver( - &priv->ac_ip6_config, - (const NMIPAddr *) nm_ip6_config_get_nameserver(from_config, 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]); + + strvarr = nm_l3_config_data_get_searches(l3cd_src, AF_INET6, &n); + for (i = 0; i < n; i++) + nm_l3_config_data_add_search(l3cd, AF_INET6, strvarr[i]); } - len = nm_ip6_config_get_num_searches(from_config); - for (i = 0; i < len; i++) { - applied_config_add_search(&priv->ac_ip6_config, nm_ip6_config_get_search(from_config, i)); - } + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_AC_6, l3cd, FALSE); - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "ipv6-pd: failed applying DNS config for connection sharing"); + _dev_l3_cfg_commit(self, TRUE); } /*****************************************************************************/ -static void -linklocal6_failed(NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - nm_clear_g_source(&priv->linklocal6_timeout_id); - nm_device_activate_schedule_ip_config_timeout(self, AF_INET6); -} - static gboolean -linklocal6_timeout_cb(gpointer user_data) -{ - NMDevice *self = user_data; - - _LOGD(LOGD_DEVICE, "linklocal6: waiting for link-local addresses failed due to timeout"); - linklocal6_failed(self); - return G_SOURCE_REMOVE; -} - -static void -linklocal6_check_complete(NMDevice *self) +_dev_ipll6_state_retry_cb(gpointer user_data) { + NMDevice * self = user_data; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMConnection * connection; - const char * method; - if (!priv->linklocal6_timeout_id) { - /* we are not waiting for linklocal to complete. Nothing to do. */ - return; - } - - if (!priv->ext_ip6_config_captured - || !nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ext_ip6_config_captured), - NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) { - /* we don't have a non-tentative link local address yet. Wait longer. */ - return; - } - - nm_clear_g_source(&priv->linklocal6_timeout_id); - - connection = nm_device_get_applied_connection(self); - g_assert(connection); - - method = nm_device_get_effective_ip_config_method(self, AF_INET6); - - _LOGD(LOGD_DEVICE, - "linklocal6: waiting for link-local addresses successful, continue with method %s", - method); - - if (NM_IN_STRSET(method, - NM_SETTING_IP6_CONFIG_METHOD_AUTO, - NM_SETTING_IP6_CONFIG_METHOD_SHARED)) - addrconf6_start_with_link_ready(self); - else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { - if (!dhcp6_start_with_link_ready(self, connection)) { - /* Time out IPv6 instead of failing the entire activation */ - nm_device_activate_schedule_ip_config_timeout(self, AF_INET6); - } - } else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - else - g_return_if_fail(FALSE); + nm_clear_g_source_inst(&priv->ipll_data_6.v6.retry_source); + _dev_ipll6_start(self); + return G_SOURCE_CONTINUE; } static void -check_and_add_ipv6ll_addr(NMDevice *self) +_dev_ipll6_set_llstate(NMDevice *self, NML3IPv6LLState llstate, const struct in6_addr *lladdr) { - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - struct in6_addr lladdr; - NMConnection * connection; - NMSettingIP6Config *s_ip6 = NULL; - GError * error = NULL; - const char * addr_type; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gboolean changed = FALSE; + NMDeviceIPState state; + NMDeviceIPState old_state; - if (!priv->ipv6ll_handle) - return; + if (!lladdr) + lladdr = &nm_ip_addr_zero.addr6; - if (priv->ext_ip6_config_captured - && nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ext_ip6_config_captured), - NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE)) { - /* Already have an LL address, nothing to do */ - return; + if (priv->ipll_data_6.v6.llstate != llstate + || !IN6_ARE_ADDR_EQUAL(&priv->ipll_data_6.v6.lladdr, lladdr)) { + changed = TRUE; + priv->ipll_data_6.v6.llstate = llstate; + priv->ipll_data_6.v6.lladdr = *lladdr; } - priv->ipv6ll_has = FALSE; - memset(&priv->ipv6ll_addr, 0, sizeof(priv->ipv6ll_addr)); + nm_assert((priv->ipll_data_6.v6.ipv6ll + && NM_IN_SET(priv->ipll_data_6.v6.llstate, + NM_L3_IPV6LL_STATE_STARTING, + NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, + NM_L3_IPV6LL_STATE_READY, + NM_L3_IPV6LL_STATE_DAD_FAILED)) + || (!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))); - memset(&lladdr, 0, sizeof(lladdr)); - lladdr.s6_addr16[0] = htons(0xfe80); + switch (priv->ipll_data_6.v6.llstate) { + case NM_L3_IPV6LL_STATE_NONE: + state = NM_DEVICE_IP_STATE_NONE; + break; + case NM_L3_IPV6LL_STATE_DEFUNCT: + case NM_L3_IPV6LL_STATE_DAD_FAILED: + state = NM_DEVICE_IP_STATE_FAILED; + break; + case NM_L3_IPV6LL_STATE_READY: + state = NM_DEVICE_IP_STATE_READY; + break; + case NM_L3_IPV6LL_STATE_STARTING: + case NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS: + state = NM_DEVICE_IP_STATE_PENDING; + break; + default: + state = nm_assert_unreachable_val(NM_DEVICE_IP_STATE_FAILED); + break; + } + + old_state = priv->ipll_data_6.state; + if (priv->ipll_data_6.state != state) { + priv->ipll_data_6.state = state; + changed = TRUE; + } + + if (priv->ipll_data_6.v6.llstate != NM_L3_IPV6LL_STATE_DEFUNCT) + nm_clear_g_source_inst(&priv->ipll_data_6.v6.retry_source); + else if (!priv->ipll_data_6.v6.retry_source) { + /* we schedule a timer to try to recover from this... Possibly some higher layer + * will however fail the activation... */ + priv->ipll_data_6.v6.retry_source = + nm_g_timeout_add_source(10000, _dev_ipll6_state_retry_cb, self); + } + + if (changed) { + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + + _LOGT_ipll(AF_INET6, + "set state %s (was %s, llstate=%s, lladdr=%s)", + nm_device_ip_state_to_string(priv->ipll_data_6.state), + nm_device_ip_state_to_string(old_state), + 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)); + } + + if (changed) + _dev_ip_state_check_async(self, AF_INET6); + + if (priv->ipll_data_6.v6.llstate == NM_L3_IPV6LL_STATE_READY) { + /* if we got an IPv6LL address, we might poke some other methods + * to progress... */ + _dev_ipac6_start_continue(self); + _dev_ipdhcpx_start_continue(self, AF_INET6); + } +} + +static void +_dev_ipll6_state_change_cb(NML3IPv6LL * ipv6ll, + NML3IPv6LLState llstate, + const struct in6_addr *lladdr, + gpointer user_data) +{ + _dev_ipll6_set_llstate(user_data, llstate, lladdr); +} + +static void +_dev_ipll6_start(NMDevice *self) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NMConnection * connection; + NMSettingIP6Config * s_ip6 = NULL; + gboolean assume; + const char * ifname; + NML3IPv6LLState llstate; + const struct in6_addr *lladdr; + + if (priv->ipll_data_6.v6.ipv6ll) + return; + + if (!priv->l3cfg) { + _LOGD(LOGD_IP6, "linklocal6: no IP link for IPv6"); + goto out_fail; + } + + ifname = nm_device_get_ip_iface(self); + if (!ifname) { + _LOGD(LOGD_IP6, "linklocal6: no interface name for IPv6"); + goto out_fail; + } connection = nm_device_get_applied_connection(self); if (connection) s_ip6 = NM_SETTING_IP6_CONFIG(nm_connection_get_setting_ip6_config(connection)); + assume = nm_device_sys_iface_state_is_external_or_assume(self); + if (s_ip6 && nm_setting_ip6_config_get_addr_gen_mode(s_ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) { @@ -10366,73 +10563,31 @@ check_and_add_ipv6ll_addr(NMDevice *self) const char * stable_id; stable_id = _prop_get_connection_stable_id(self, connection, &stable_type); - if (!nm_utils_ipv6_addr_set_stable_privacy_may_fail(stable_type, - &lladdr, - nm_device_get_iface(self), - stable_id, - priv->linklocal6_dad_counter++, - &error)) { - _LOGW(LOGD_IP6, "linklocal6: failed to generate an address: %s", error->message); - g_clear_error(&error); - linklocal6_failed(self); - return; - } - addr_type = "stable-privacy"; + priv->ipll_data_6.v6.ipv6ll = nm_l3_ipv6ll_new_stable_privacy(priv->l3cfg, + assume, + stable_type, + ifname, + stable_id, + _dev_ipll6_state_change_cb, + self); } else { NMUtilsIPv6IfaceId iid; - if (priv->linklocal6_timeout_id) { - /* We already started and attempt to add a LL address. For the EUI-64 - * mode we can't pick a new one, we'll just fail. */ - _LOGW(LOGD_IP6, "linklocal6: DAD failed for an EUI-64 address"); - linklocal6_failed(self); - return; - } - if (!nm_device_get_ip_iface_identifier(self, &iid, TRUE)) { _LOGW(LOGD_IP6, "linklocal6: failed to get interface identifier; IPv6 cannot continue"); - return; + goto out_fail; } - nm_utils_ipv6_addr_set_interface_identifier(&lladdr, &iid); - addr_type = "EUI-64"; + + priv->ipll_data_6.v6.ipv6ll = + nm_l3_ipv6ll_new_token(priv->l3cfg, assume, &iid, _dev_ipll6_state_change_cb, self); } - _LOGD(LOGD_IP6, - "linklocal6: generated %s IPv6LL address %s", - addr_type, - _nm_utils_inet6_ntop(&lladdr, sbuf)); - priv->ipv6ll_has = TRUE; - priv->ipv6ll_addr = lladdr; - ip_config_merge_and_apply(self, AF_INET6, TRUE); -} + llstate = nm_l3_ipv6ll_get_state(priv->ipll_data_6.v6.ipv6ll, &lladdr); + _dev_ipll6_set_llstate(self, llstate, lladdr); + return; -static gboolean -linklocal6_start(NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - nm_clear_g_source(&priv->linklocal6_timeout_id); - - if (priv->ext_ip6_config_captured - && nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ext_ip6_config_captured), - NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) - return TRUE; - - _LOGD(LOGD_DEVICE, - "linklocal6: starting IPv6 with method '%s', but the device has no link-local addresses " - "configured. Wait.", - nm_device_get_effective_ip_config_method(self, AF_INET6)); - - check_and_add_ipv6ll_addr(self); - - /* Depending on the network and what the 'dad_transmits' and 'retrans_time_ms' - * sysctl values are, DAD for the IPv6LL address may take quite a while. - * FIXME: use dad/retrans sysctl values if they are higher than a minimum time. - * (rh #1101809) - */ - priv->linklocal6_timeout_id = g_timeout_add_seconds(15, linklocal6_timeout_cb, self); - return FALSE; +out_fail: + _dev_ipll6_set_llstate(self, NM_L3_IPV6LL_STATE_DEFUNCT, NULL); } /*****************************************************************************/ @@ -10595,13 +10750,16 @@ set_platform_mtu(NMDevice *self, guint32 mtu) } static void -_commit_mtu(NMDevice *self, const NMIP4Config *config) +_commit_mtu(NMDevice *self) { - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMDeviceMtuSource source = NM_DEVICE_MTU_SOURCE_NONE; - guint32 ip6_mtu, ip6_mtu_orig; - guint32 mtu_desired, mtu_desired_orig; - guint32 mtu_plat; + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + NMDeviceMtuSource source = NM_DEVICE_MTU_SOURCE_NONE; + const NML3ConfigData *l3cd; + guint32 ip6_mtu_orig; + guint32 ip6_mtu; + guint32 mtu_desired_orig; + guint32 mtu_desired; + guint32 mtu_plat; struct { gboolean initialized; guint32 value; @@ -10609,7 +10767,9 @@ _commit_mtu(NMDevice *self, const NMIP4Config *config) 0, }; int ifindex; - char sbuf[64], sbuf1[64], sbuf2[64]; + char sbuf[64]; + char sbuf1[64]; + char sbuf2[64]; gboolean success = TRUE; ifindex = nm_device_get_ip_ifindex(self); @@ -10623,8 +10783,11 @@ _commit_mtu(NMDevice *self, const NMIP4Config *config) return; } + l3cd = nm_l3cfg_get_combined_l3cd(priv->l3cfg, FALSE); + { - guint32 mtu = 0; + guint32 mtu = 0; + guint32 mtu2; gboolean force = FALSE; /* We take the MTU from various sources: (in order of increasing @@ -10651,9 +10814,9 @@ _commit_mtu(NMDevice *self, const NMIP4Config *config) if (NM_DEVICE_GET_CLASS(self)->get_configured_mtu) mtu = NM_DEVICE_GET_CLASS(self)->get_configured_mtu(self, &source, &force); - if (config && !force && source < NM_DEVICE_MTU_SOURCE_IP_CONFIG - && nm_ip4_config_get_mtu(config)) { - mtu = nm_ip4_config_get_mtu(config); + if (l3cd && !force && source < NM_DEVICE_MTU_SOURCE_IP_CONFIG + && (mtu2 = nm_l3_config_data_get_mtu(l3cd)) > 0) { + mtu = mtu2; source = NM_DEVICE_MTU_SOURCE_IP_CONFIG; } @@ -10842,239 +11005,181 @@ nm_device_commit_mtu(NMDevice *self) state = nm_device_get_state(self); if (state >= NM_DEVICE_STATE_CONFIG && state < NM_DEVICE_STATE_DEACTIVATING) { _LOGT(LOGD_DEVICE, "mtu: commit-mtu..."); - _commit_mtu(self, NM_DEVICE_GET_PRIVATE(self)->ip_config_4); + _commit_mtu(self); } else _LOGT(LOGD_DEVICE, "mtu: commit-mtu... skip due to state %s", nm_device_state_to_string(state)); } +/*****************************************************************************/ + static void -ndisc_config_changed(NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_int, NMDevice *self) +_dev_ipac6_ndisc_set_router_config(NMDevice *self) { - NMNDiscConfigMap changed = changed_int; - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - guint i; + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + const NML3ConfigData *l3cd; - g_return_if_fail(priv->act_request.obj); + if (!priv->ipac6_data.ndisc) + return; - if (!applied_config_get_current(&priv->ac_ip6_config)) - applied_config_init_new(&priv->ac_ip6_config, self, AF_INET6); + if (nm_ndisc_get_node_type(priv->ipac6_data.ndisc) != NM_NDISC_NODE_TYPE_ROUTER) + return; - if (changed & NM_NDISC_CONFIG_ADDRESSES) { - guint32 ifa_flags; - - /* Check, whether kernel is recent enough to help user space handling RA. - * If it's not supported, we have no ipv6-privacy and must add autoconf - * addresses as /128. The reason for the /128 is to prevent the kernel - * from adding a prefix route for this address. */ - ifa_flags = IFA_F_NOPREFIXROUTE; - if (NM_IN_SET(priv->ndisc_use_tempaddr, - NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, - NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)) - ifa_flags |= IFA_F_MANAGETEMPADDR; - - nm_ip6_config_reset_addresses_ndisc((NMIP6Config *) priv->ac_ip6_config.orig, - rdata->addresses, - rdata->addresses_n, - 64, - ifa_flags); - if (priv->ac_ip6_config.current) { - nm_ip6_config_reset_addresses_ndisc((NMIP6Config *) priv->ac_ip6_config.current, - rdata->addresses, - rdata->addresses_n, - 64, - ifa_flags); - } - } - - if (NM_FLAGS_ANY(changed, NM_NDISC_CONFIG_ROUTES | NM_NDISC_CONFIG_GATEWAYS)) { - nm_ip6_config_reset_routes_ndisc((NMIP6Config *) priv->ac_ip6_config.orig, - rdata->gateways, - rdata->gateways_n, - rdata->routes, - rdata->routes_n, - nm_device_get_route_table(self, AF_INET6), - nm_device_get_route_metric(self, AF_INET6)); - if (priv->ac_ip6_config.current) { - nm_ip6_config_reset_routes_ndisc((NMIP6Config *) priv->ac_ip6_config.current, - rdata->gateways, - rdata->gateways_n, - rdata->routes, - rdata->routes_n, - nm_device_get_route_table(self, AF_INET6), - nm_device_get_route_metric(self, AF_INET6)); - } - } - - if (changed & NM_NDISC_CONFIG_DNS_SERVERS) { - /* Rebuild DNS server list from neighbor discovery cache. */ - applied_config_reset_nameservers(&priv->ac_ip6_config); - - for (i = 0; i < rdata->dns_servers_n; i++) - applied_config_add_nameserver(&priv->ac_ip6_config, - (const NMIPAddr *) &rdata->dns_servers[i].address); - } - - if (changed & NM_NDISC_CONFIG_DNS_DOMAINS) { - /* Rebuild domain list from neighbor discovery cache. */ - applied_config_reset_searches(&priv->ac_ip6_config); - - for (i = 0; i < rdata->dns_domains_n; i++) - applied_config_add_search(&priv->ac_ip6_config, rdata->dns_domains[i].domain); - } - - if (changed & NM_NDISC_CONFIG_DHCP_LEVEL) { - dhcp6_cleanup(self, CLEANUP_TYPE_DECONFIGURE, TRUE); - - priv->dhcp6.mode = rdata->dhcp_level; - if (priv->dhcp6.mode != NM_NDISC_DHCP_LEVEL_NONE) { - _LOGD(LOGD_DEVICE | LOGD_DHCP6, - "Activation: Stage 3 of 5 (IP Configure Start) starting DHCPv6" - " as requested by IPv6 router..."); - if (!dhcp6_start(self, FALSE)) { - if (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED) { - nm_device_state_changed(self, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_DHCP_START_FAILED); - return; - } - } - } - } - - if (changed & NM_NDISC_CONFIG_HOP_LIMIT) - nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe(nm_device_get_platform(self), - nm_device_get_ip_iface(self), - rdata->hop_limit); - - if (changed & NM_NDISC_CONFIG_REACHABLE_TIME) { - nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time(nm_device_get_platform(self), - nm_device_get_ip_iface(self), - rdata->reachable_time_ms); - } - - if (changed & NM_NDISC_CONFIG_RETRANS_TIMER) { - nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time(nm_device_get_platform(self), - nm_device_get_ip_iface(self), - rdata->retrans_timer_ms); - } - - if (changed & NM_NDISC_CONFIG_MTU) { - if (priv->ip6_mtu != rdata->mtu) { - _LOGD(LOGD_DEVICE, "mtu: set IPv6 MTU to %u", (guint) rdata->mtu); - priv->ip6_mtu = rdata->mtu; - } - } - - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); + /* FIXME(l3cfg): this doesn't seem right. What is the meaning of the l3cd at this + * point? Also, when do we need to reset the config (and call this function again?). */ + l3cd = nm_l3cfg_get_combined_l3cd(priv->l3cfg, FALSE); + if (l3cd) + nm_ndisc_set_config(priv->ipac6_data.ndisc, l3cd); } static void -ndisc_ra_timeout(NMNDisc *ndisc, NMDevice *self) +_dev_ipac6_set_state(NMDevice *self, NMDeviceIPState state) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - /* We don't want to stop listening for router advertisements completely, - * but instead let device activation continue activating. If an RA - * shows up later, we'll use it as long as the device is not disconnected. - */ - - _LOGD(LOGD_IP6, "timed out waiting for IPv6 router advertisement"); - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) { - /* If RA is our only source of addressing information and we don't - * ever receive one, then time out IPv6. But if there is other - * IPv6 configuration, like manual IPv6 addresses or external IPv6 - * config, consider that sufficient for IPv6 success. - * - * FIXME: it doesn't seem correct to determine this based on which - * addresses we find inside priv->ip_config_6. - */ - if (priv->ip_config_6 - && nm_ip_config_find_first_address(NM_IP_CONFIG(priv->ip_config_6), - NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)) - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - else - nm_device_activate_schedule_ip_config_timeout(self, AF_INET6); + if (priv->ipac6_data.state != state) { + _LOGD_ipac6("set state: %s (was %s)", + nm_device_ip_state_to_string(state), + nm_device_ip_state_to_string(priv->ipac6_data.state)); + priv->ipac6_data.state = state; } } static void -addrconf6_start_with_link_ready(NMDevice *self) +_dev_ipac6_ndisc_config_changed(NMNDisc * ndisc, + const NMNDiscData * rdata, + guint changed_i, + const NML3ConfigData *l3cd, + NMDevice * self) { - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMUtilsIPv6IfaceId iid; + _dev_ipac6_grace_period_start(self, 0, TRUE); - g_assert(priv->ndisc); + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_AC_6, l3cd, FALSE); - if (nm_device_get_ip_iface_identifier(self, &iid, FALSE)) { - _LOGD(LOGD_IP6, "addrconf6: using the device EUI-64 identifier"); - nm_ndisc_set_iid(priv->ndisc, iid); - } else { - /* Don't abort the addrconf at this point -- if ndisc needs the iid - * it will notice this itself. */ - _LOGI(LOGD_IP6, "addrconf6: no interface identifier; IPv6 address creation may fail"); - } + _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_READY); - /* Apply any manual configuration before starting RA */ - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "failed to apply manual IPv6 configuration"); + _dev_ipdhcp6_set_dhcp_level(self, rdata->dhcp_level); - if (nm_ndisc_get_node_type(priv->ndisc) == NM_NDISC_NODE_TYPE_ROUTER) { - nm_device_sysctl_ip_conf_set(self, AF_INET6, "forwarding", "1"); - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - priv->needs_ip6_subnet = TRUE; - g_signal_emit(self, signals[IP6_SUBNET_NEEDED], 0); - } + _dev_l3_cfg_commit(self, FALSE); - priv->ndisc_changed_id = g_signal_connect(priv->ndisc, - NM_NDISC_CONFIG_RECEIVED, - G_CALLBACK(ndisc_config_changed), - self); - priv->ndisc_timeout_id = g_signal_connect(priv->ndisc, - NM_NDISC_RA_TIMEOUT_SIGNAL, - G_CALLBACK(ndisc_ra_timeout), - self); + _dev_ip_state_check_async(self, AF_INET6); +} - ndisc_set_router_config(priv->ndisc, self); - nm_ndisc_start(priv->ndisc); - priv->ndisc_started = TRUE; - return; +static void +_dev_ipac6_handle_timeout(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + _LOGD_ipac6("timeout for autoconf (IPv6 router advertisement) reached"); + + nm_clear_g_source_inst(&priv->ipac6_data.ndisc_grace_source); + + _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_FAILED); + + _dev_ip_state_check_async(self, AF_INET6); +} + +static void +_dev_ipac6_ndisc_ra_timeout(NMNDisc *ndisc, NMDevice *self) +{ + _dev_ipac6_handle_timeout(self); } static gboolean -addrconf6_start(NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) +_dev_ipac6_grace_period_expired(gpointer user_data) +{ + _dev_ipac6_handle_timeout(user_data); + return G_SOURCE_REMOVE; +} + +static gboolean +_dev_ipac6_grace_period_start(NMDevice *self, guint32 timeout_sec, gboolean force_restart) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gboolean stopped; + + /* In any other case (expired lease, assumed connection, etc.), + * wait for some time before failing the IP method. + */ + if (!force_restart && priv->ipac6_data.ndisc_grace_source) { + /* already pending. */ + return FALSE; + } + + /* Start a grace period equal to the RA timeout multiplied + * by a constant factor. */ + + stopped = nm_clear_g_source_inst(&priv->ipac6_data.ndisc_grace_source); + + if (timeout_sec == 0) { + if (stopped) + _LOGD_ipac6("grace period stopped"); + return FALSE; + } + + nm_assert(timeout_sec <= G_MAXINT32); + + if (timeout_sec >= G_MAXUINT / (GRACE_PERIOD_MULTIPLIER * 1000u)) + timeout_sec = NM_RA_TIMEOUT_INFINITY; + + if (timeout_sec == NM_RA_TIMEOUT_INFINITY) { + _LOGD_ipac6("grace period starts with infinity timeout"); + priv->ipac6_data.ndisc_grace_source = g_source_ref(nm_g_source_sentinel_get(0)); + } else { + _LOGD_ipac6("grace period starts with %u seconds", timeout_sec); + priv->ipac6_data.ndisc_grace_source = + nm_g_timeout_add_source(timeout_sec * (GRACE_PERIOD_MULTIPLIER * 1000u), + _dev_ipac6_grace_period_expired, + self); + } + + return TRUE; +} + +static void +_dev_ipac6_start(NMDevice *self) { NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); NMConnection * connection; - NMSettingIP6Config *s_ip6 = NULL; - GError * error = NULL; + NMSettingIP6Config *s_ip = NULL; + NMNDiscNodeType node_type; NMUtilsStableType stable_type; const char * stable_id; - NMNDiscNodeType node_type; int max_addresses; int router_solicitations; int router_solicitation_interval; guint32 ra_timeout; guint32 default_ra_timeout; + NMUtilsIPv6IfaceId iid; - if (!g_file_test("/proc/sys/net/ipv6", G_FILE_TEST_IS_DIR)) { - _LOGI(LOGD_IP6, "addrconf6: kernel does not support IPv6"); - return FALSE; + if (priv->ipac6_data.state == NM_DEVICE_IP_STATE_NONE) { + if (!g_file_test("/proc/sys/net/ipv6", G_FILE_TEST_IS_DIR)) { + _LOGI_ipac6("addrconf6: kernel does not support IPv6"); + _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_FAILED); + _dev_ip_state_check_async(self, AF_INET6); + return; + } + + _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_PENDING); + } + + if (NM_IN_SET(priv->ipll_data_6.state, NM_DEVICE_IP_STATE_NONE, NM_DEVICE_IP_STATE_PENDING)) { + _dev_ipac6_grace_period_start(self, 30, TRUE); + _dev_ipll6_start(self); + return; + } + + if (priv->ipac6_data.ndisc) { + /* we already started. Nothing to do. */ + return; } connection = nm_device_get_applied_connection(self); - g_assert(connection); + if (connection) + s_ip = NM_SETTING_IP6_CONFIG(nm_connection_get_setting_ip6_config(connection)); - nm_assert(!applied_config_get_current(&priv->ac_ip6_config)); - applied_config_clear(&priv->ac_ip6_config); - - nm_clear_pointer(&priv->rt6_temporary_not_available, g_hash_table_unref); - nm_clear_g_source(&priv->rt6_temporary_not_available_id); - - s_ip6 = NM_SETTING_IP6_CONFIG(nm_connection_get_setting_ip6_config(connection)); - g_assert(s_ip6); + g_return_if_fail(s_ip); if (nm_streq(nm_device_get_effective_ip_config_method(self, AF_INET6), NM_SETTING_IP6_CONFIG_METHOD_SHARED)) @@ -11097,60 +11202,101 @@ addrconf6_start(NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) ra_timeout = default_ra_timeout; } - stable_id = _prop_get_connection_stable_id(self, connection, &stable_type); - priv->ndisc = nm_lndp_ndisc_new(nm_device_get_platform(self), - nm_device_get_ip_ifindex(self), - nm_device_get_ip_iface(self), - stable_type, - stable_id, - nm_setting_ip6_config_get_addr_gen_mode(s_ip6), - node_type, - max_addresses, - router_solicitations, - router_solicitation_interval, - ra_timeout, - &error); - if (!priv->ndisc) { - _LOGE(LOGD_IP6, "addrconf6: failed to start neighbor discovery: %s", error->message); - g_error_free(error); - return FALSE; + stable_id = _prop_get_connection_stable_id(self, connection, &stable_type); + + { + const NMNDiscConfig config = { + .l3cfg = nm_device_get_l3cfg(self), + .ifname = nm_device_get_ip_iface(self), + .stable_type = stable_type, + .network_id = stable_id, + .addr_gen_mode = nm_setting_ip6_config_get_addr_gen_mode(s_ip), + .node_type = node_type, + .max_addresses = max_addresses, + .router_solicitations = router_solicitations, + .router_solicitation_interval = router_solicitation_interval, + .ra_timeout = ra_timeout, + .ip6_privacy = _prop_get_ipv6_ip6_privacy(self), + }; + + priv->ipac6_data.ndisc = nm_lndp_ndisc_new(&config); + + priv->ipac6_data.ndisc_changed_id = + g_signal_connect(priv->ipac6_data.ndisc, + NM_NDISC_CONFIG_RECEIVED, + G_CALLBACK(_dev_ipac6_ndisc_config_changed), + self); + priv->ipac6_data.ndisc_timeout_id = + g_signal_connect(priv->ipac6_data.ndisc, + NM_NDISC_RA_TIMEOUT_SIGNAL, + G_CALLBACK(_dev_ipac6_ndisc_ra_timeout), + self); } - priv->ndisc_use_tempaddr = use_tempaddr; - - /* ensure link local is ready... */ - if (!linklocal6_start(self)) { - /* wait for the LL address to show up */ - return TRUE; + if (nm_device_get_ip_iface_identifier(self, &iid, FALSE)) { + _LOGD_ipac6("using the device EUI-64 identifier"); + nm_ndisc_set_iid(priv->ipac6_data.ndisc, iid); + } else { + /* Don't abort the addrconf at this point -- if ndisc needs the iid + * it will notice this itself. */ + _LOGD_ipac6("no interface identifier; IPv6 address creation may fail"); } - /* already have the LL address; kick off neighbor discovery */ - addrconf6_start_with_link_ready(self); - return TRUE; + if (nm_ndisc_get_node_type(priv->ipac6_data.ndisc) == NM_NDISC_NODE_TYPE_ROUTER) { + nm_device_sysctl_ip_conf_set(self, AF_INET6, "forwarding", "1"); + priv->needs_ip6_subnet = TRUE; + g_signal_emit(self, signals[IP6_SUBNET_NEEDED], 0); + } + + _dev_ipac6_ndisc_set_router_config(self); + + if (node_type == NM_NDISC_NODE_TYPE_ROUTER) + _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_READY); + else + _dev_ipac6_grace_period_start(self, ra_timeout, TRUE); + + nm_ndisc_start(priv->ipac6_data.ndisc); +} + +void +nm_device_ip_method_autoconf6_start(NMDevice *self) +{ + _dev_ipac6_start(self); } static void -addrconf6_cleanup(NMDevice *self) +_dev_ipac6_start_continue(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - priv->ndisc_started = FALSE; - nm_clear_g_signal_handler(priv->ndisc, &priv->ndisc_changed_id); - nm_clear_g_signal_handler(priv->ndisc, &priv->ndisc_timeout_id); + if (priv->ipac6_data.state != NM_DEVICE_IP_STATE_NONE) + _dev_ipac6_start(self); +} - applied_config_clear(&priv->ac_ip6_config); - nm_clear_pointer(&priv->rt6_temporary_not_available, g_hash_table_unref); - nm_clear_g_source(&priv->rt6_temporary_not_available_id); - if (priv->ndisc) { - nm_ndisc_stop(priv->ndisc); - g_clear_object(&priv->ndisc); +static void +_dev_ipac6_cleanup(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + nm_clear_g_source_inst(&priv->ipac6_data.ndisc_grace_source); + + nm_clear_g_signal_handler(priv->ipac6_data.ndisc, &priv->ipac6_data.ndisc_changed_id); + nm_clear_g_signal_handler(priv->ipac6_data.ndisc, &priv->ipac6_data.ndisc_timeout_id); + + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_AC_6, NULL, FALSE); + + if (priv->ipac6_data.ndisc) { + nm_ndisc_stop(priv->ipac6_data.ndisc); + g_clear_object(&priv->ipac6_data.ndisc); } + + _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_NONE); } /*****************************************************************************/ static void -save_ip6_properties(NMDevice *self) +_dev_sysctl_save_ip6_properties(NMDevice *self) { static const char *const ip6_properties_to_save[] = { "accept_ra", @@ -11183,61 +11329,80 @@ save_ip6_properties(NMDevice *self) } static void -restore_ip6_properties(NMDevice *self) +_dev_sysctl_restore_ip6_properties(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); GHashTableIter iter; - gpointer key, value; + gpointer key; + gpointer value; g_hash_table_iter_init(&iter, priv->ip6_saved_properties); - while (g_hash_table_iter_next(&iter, &key, &value)) { - /* Don't touch "disable_ipv6" if we're doing userland IPv6LL */ - if (priv->ipv6ll_handle && nm_streq(key, "disable_ipv6")) - continue; + while (g_hash_table_iter_next(&iter, &key, &value)) nm_device_sysctl_ip_conf_set(self, AF_INET6, key, value); +} + +static void +_dev_sysctl_set_disable_ipv6(NMDevice *self, gboolean do_disable) +{ + nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", do_disable ? "1" : "0"); +} + +/*****************************************************************************/ + +static void +_dev_addrgenmode6_set(NMDevice *self, guint8 addr_gen_mode) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + int ifindex = nm_device_get_ip_ifindex(self); + const NMPlatformLink *plink; + int r; + int cur_addr_gen_mode; + char sbuf[100]; + + if (ifindex <= 0) + return; + + plink = nm_platform_link_get(nm_device_get_platform(self), ifindex); + if (!plink) + return; + + cur_addr_gen_mode = _nm_platform_link_get_inet6_addr_gen_mode(plink); + nm_assert(cur_addr_gen_mode >= 0 && cur_addr_gen_mode <= 255); + + if (!priv->addrgenmode6_data.previous_mode_has) { + priv->addrgenmode6_data.previous_mode_has = TRUE; + priv->addrgenmode6_data.previous_mode_val = cur_addr_gen_mode; + nm_assert(priv->addrgenmode6_data.previous_mode_val == cur_addr_gen_mode); } -} -static void -set_disable_ipv6(NMDevice *self, const char *value) -{ - /* We only touch disable_ipv6 when NM is not managing the IPv6LL address */ - if (!NM_DEVICE_GET_PRIVATE(self)->ipv6ll_handle) - nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", value); -} + _LOGD_ip(AF_INET6, + "addrgenmode6: set %s%s", + nm_platform_link_inet6_addrgenmode2str(addr_gen_mode, sbuf, sizeof(sbuf)), + (cur_addr_gen_mode == addr_gen_mode) ? " (already set)" : ""); -static void -set_nm_ipv6ll(NMDevice *self, gboolean enable) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - int ifindex = nm_device_get_ip_ifindex(self); + if (cur_addr_gen_mode == addr_gen_mode) + return; - priv->ipv6ll_handle = enable; - if (ifindex > 0) { - int r; + r = nm_platform_link_set_inet6_addr_gen_mode(nm_device_get_platform(self), + ifindex, + addr_gen_mode); + if (r < 0) { + _NMLOG_ip(NM_IN_SET(r, -NME_PL_NOT_FOUND, -NME_PL_OPNOTSUPP) ? LOGL_DEBUG : LOGL_WARN, + AF_INET6, + "addrgenmode6: failed to set %s: (%s)", + nm_platform_link_inet6_addrgenmode2str(addr_gen_mode, sbuf, sizeof(sbuf)), + nm_strerror(r)); + } - _LOGD(LOGD_IP6, "will %s userland IPv6LL", enable ? "enable" : "disable"); - r = nm_platform_link_set_inet6_addr_gen_mode(nm_device_get_platform(self), - ifindex, - enable ? NM_IN6_ADDR_GEN_MODE_NONE - : NM_IN6_ADDR_GEN_MODE_EUI64); - if (r < 0) { - _NMLOG(NM_IN_SET(r, -NME_PL_NOT_FOUND, -NME_PL_OPNOTSUPP) ? LOGL_DEBUG : LOGL_WARN, - LOGD_IP6, - "failed to %s userspace IPv6LL address handling (%s)", - enable ? "enable" : "disable", - nm_strerror(r)); - } + if (addr_gen_mode == NM_IN6_ADDR_GEN_MODE_NONE) { + gs_free char *value = NULL; - if (enable) { - gs_free char *value = NULL; - - /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */ - value = nm_device_sysctl_ip_conf_get(self, AF_INET6, "disable_ipv6"); - if (nm_streq0(value, "0")) - nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", "1"); - - /* Ensure IPv6 is enabled */ + /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */ + _LOGD_ip(AF_INET6, + "addrgenmode6: toggle disable_ipv6 sysctl after disabling addr-gen-mode"); + value = nm_device_sysctl_ip_conf_get(self, AF_INET6, "disable_ipv6"); + if (nm_streq0(value, "0")) { + nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", "1"); nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", "0"); } } @@ -11263,30 +11428,51 @@ ip_requires_slaves(NMDevice *self, int addr_family) NM_SETTING_IP6_CONFIG_METHOD_DHCP); } -static NMActStageReturn -act_stage3_ip_config_start(NMDevice * self, - int addr_family, - gpointer * out_config, - NMDeviceStateReason *out_failure_reason) +static const char * +get_ip_method_auto(NMDevice *self, int addr_family) +{ + return NM_IS_IPv4(addr_family) ? NM_SETTING_IP4_CONFIG_METHOD_AUTO + : NM_SETTING_IP6_CONFIG_METHOD_AUTO; +} + +static void +activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family) { const int IS_IPv4 = NM_IS_IPv4(addr_family); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMDeviceClass * klass = NM_DEVICE_GET_CLASS(self); NMConnection * connection; - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + int ip_ifindex; const char * method; - nm_assert_addr_family(addr_family); + if (nm_device_sys_iface_state_is_external(self)) + goto out; connection = nm_device_get_applied_connection(self); + g_return_if_fail(connection); - g_return_val_if_fail(connection, NM_ACT_STAGE_RETURN_FAILURE); + ip_ifindex = nm_device_get_ip_ifindex(self); + + if (ip_ifindex > 0 && !nm_platform_link_is_up(nm_device_get_platform(self), ip_ifindex) + && !nm_device_sys_iface_state_is_external(self)) { + nm_platform_link_change_flags(nm_device_get_platform(self), ip_ifindex, IFF_UP, TRUE); + if (!nm_platform_link_is_up(nm_device_get_platform(self), ip_ifindex)) + _LOGW(LOGD_DEVICE, + "interface %s not up for IP configuration", + nm_device_get_ip_iface(self)); + } if (connection_ip_method_requires_carrier(connection, addr_family, NULL) && nm_device_is_master(self) && !priv->carrier) { - _LOGI(LOGD_IP | LOGD_DEVICE, - "IPv%c config waiting until carrier is on", - nm_utils_addr_family_to_char(addr_family)); - return NM_ACT_STAGE_RETURN_IP_WAIT; + if (!priv->ip_data_x[IS_IPv4].wait_for_carrier) { + _LOGT_ip(addr_family, "waiting until carrier is on"); + priv->ip_data_x[IS_IPv4].wait_for_carrier = TRUE; + } + goto out; + } + if (priv->ip_data_x[IS_IPv4].wait_for_carrier) { + _LOGT_ip(addr_family, "waiting until carrier completed"); + priv->ip_data_x[IS_IPv4].wait_for_carrier = FALSE; } if (nm_device_is_master(self) && ip_requires_slaves(self, addr_family)) { @@ -11294,94 +11480,56 @@ act_stage3_ip_config_start(NMDevice * self, * a successful IP configuration attempt, then postpone IP addressing. */ if (!have_any_ready_slaves(self)) { - _LOGI(LOGD_DEVICE | LOGD_IP, - "IPv%c config waiting until slaves are ready", - nm_utils_addr_family_to_char(addr_family)); - return NM_ACT_STAGE_RETURN_IP_WAIT; + if (!priv->ip_data_x[IS_IPv4].wait_for_ports) { + _LOGT_ip(addr_family, "waiting for ports"); + priv->ip_data_x[IS_IPv4].wait_for_ports = TRUE; + } + goto out; } } - - if (!IS_IPv4) - priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_NONE; + if (priv->ip_data_x[IS_IPv4].wait_for_ports) { + _LOGT_ip(addr_family, "waiting until ports completed"); + priv->ip_data_x[IS_IPv4].wait_for_ports = FALSE; + } method = nm_device_get_effective_ip_config_method(self, addr_family); - _LOGD(LOGD_IP | LOGD_DEVICE, - "IPv%c config method is %s", - nm_utils_addr_family_to_char(addr_family), - method); + if (nm_streq(method, + IS_IPv4 ? NM_SETTING_IP4_CONFIG_METHOD_AUTO : NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { + /* "auto" usually means DHCPv6 or autoconf6, but it doesn't have to be. Subclasses + * can overwrite it. For example, you cannot run DHCPv4 on PPP/WireGuard links. */ + method = klass->get_ip_method_auto(self, addr_family); + } + + if (klass->ready_for_ip_config && !klass->ready_for_ip_config(self)) + goto out_devip; + + _dev_ipmanual_start(self); if (IS_IPv4) { - if (NM_IN_STRSET(method, - NM_SETTING_IP4_CONFIG_METHOD_AUTO, - NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { - NMSettingIPConfig *s_ip4; - NMIP4Config ** configs, *config; - guint num_addresses; - - s_ip4 = nm_connection_get_setting_ip4_config(connection); - g_return_val_if_fail(s_ip4, NM_ACT_STAGE_RETURN_FAILURE); - num_addresses = nm_setting_ip_config_get_num_addresses(s_ip4); - - if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { - ret = dhcp4_start(self); - if (ret == NM_ACT_STAGE_RETURN_FAILURE) { - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_DHCP_START_FAILED); - return ret; - } - } else { - g_return_val_if_fail(num_addresses != 0, NM_ACT_STAGE_RETURN_FAILURE); - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } - - if (num_addresses) { - config = nm_device_ip4_config_new(self); - nm_ip4_config_merge_setting(config, - nm_connection_get_setting_ip4_config(connection), - NM_SETTING_CONNECTION_MDNS_DEFAULT, - NM_SETTING_CONNECTION_LLMNR_DEFAULT, - NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT, - nm_device_get_route_table(self, AF_INET), - nm_device_get_route_metric(self, AF_INET)); - configs = g_new0(NMIP4Config *, 2); - configs[0] = config; - ipv4_dad_start(self, configs, ipv4_manual_method_apply); - } - } else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) { - ret = ipv4ll_start(self); - if (ret == NM_ACT_STAGE_RETURN_FAILURE) - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED); - } else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { - if (out_config) { - *out_config = shared4_new_config(self, connection); - if (*out_config) { - priv->dnsmasq_manager = nm_dnsmasq_manager_new(nm_device_get_ip_iface(self)); - ret = NM_ACT_STAGE_RETURN_SUCCESS; - } else { - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - ret = NM_ACT_STAGE_RETURN_FAILURE; - } - } else - g_return_val_if_reached(NM_ACT_STAGE_RETURN_FAILURE); - } else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) - ret = NM_ACT_STAGE_RETURN_SUCCESS; - else - _LOGW(LOGD_IP4, "unhandled IPv4 config method '%s'; will fail", method); - - return ret; - } else { - NMSettingIP6ConfigPrivacy ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; - const char * ip6_privacy_str = "0"; - NMPlatform * platform; - int ifindex; + if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) + _dev_ipdhcpx_start(self, AF_INET); + else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) + _dev_ipll4_start(self); + else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) + _dev_ipshared4_start(self); + else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) + priv->ip_data_x[IS_IPv4].is_disabled = TRUE; + else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + /* pass */ + } else + nm_assert_not_reached(); + } + if (!IS_IPv4) { if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) { - nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", "1"); - return NM_ACT_STAGE_RETURN_IP_DONE; - } - - if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { - if (!nm_device_sys_iface_state_is_external(self)) { + if (!priv->ip_data_x[IS_IPv4].is_disabled) { + priv->ip_data_x[IS_IPv4].is_disabled = TRUE; + nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", "1"); + } + } else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { + if (!priv->ip_data_x[IS_IPv4].is_ignore) { + priv->ip_data_x[IS_IPv4].is_ignore = TRUE; if (priv->master) { /* If a device only has an IPv6 link-local address, * we don't generate an assumed connection. Therefore, @@ -11391,165 +11539,62 @@ act_stage3_ip_config_start(NMDevice * self, * slave should not depend on the previous state. Flush * addresses and routes on activation. */ - ifindex = nm_device_get_ip_ifindex(self); - platform = nm_device_get_platform(self); - - if (ifindex > 0) { - gs_unref_object NMIP6Config *config = nm_device_ip6_config_new(self); - - nm_platform_ip_route_flush(platform, AF_INET6, ifindex); - nm_platform_ip_address_flush(platform, AF_INET6, ifindex); - nm_device_set_ip_config(self, AF_INET6, (NMIPConfig *) config, FALSE, NULL); + if (ip_ifindex > 0) { + nm_platform_ip_route_flush(nm_device_get_platform(self), + AF_INET6, + ip_ifindex); + nm_platform_ip_address_flush(nm_device_get_platform(self), + AF_INET6, + ip_ifindex); } } else { - gboolean ipv6ll_handle_old = priv->ipv6ll_handle; - /* When activating an IPv6 'ignore' connection we need to revert back * to kernel IPv6LL, but the kernel won't actually assign an address * to the interface until disable_ipv6 is bounced. */ - set_nm_ipv6ll(self, FALSE); - if (ipv6ll_handle_old) - nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", "1"); - restore_ip6_properties(self); + _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_EUI64); + _dev_sysctl_set_disable_ipv6(self, TRUE); + _dev_sysctl_restore_ip6_properties(self); } } - return NM_ACT_STAGE_RETURN_IP_DONE; - } + } else { + /* Ensure the MTU makes sense. If it was below 1280 the kernel would not + * expose any ipv6 sysctls or allow presence of any addresses on the interface, + * including LL, which * would make it impossible to autoconfigure MTU to a + * correct value. */ + _commit_mtu(self); - /* Ensure the MTU makes sense. If it was below 1280 the kernel would not - * expose any ipv6 sysctls or allow presence of any addresses on the interface, - * including LL, which * would make it impossible to autoconfigure MTU to a - * correct value. */ - _commit_mtu(self, priv->ip_config_4); + /* Any method past this point requires an IPv6LL address. Use NM-controlled + * IPv6LL if this is not an assumed connection, since assumed connections + * will already have IPv6 set up. + */ + _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_NONE); - /* Any method past this point requires an IPv6LL address. Use NM-controlled - * IPv6LL if this is not an assumed connection, since assumed connections - * will already have IPv6 set up. - */ - if (!nm_device_sys_iface_state_is_external_or_assume(self)) - set_nm_ipv6ll(self, TRUE); + /* Re-enable IPv6 on the interface */ + nm_device_sysctl_ip_conf_set(self, AF_INET6, "accept_ra", "0"); + _dev_sysctl_set_disable_ipv6(self, FALSE); + _dev_ipll6_start(self); - /* Re-enable IPv6 on the interface */ - nm_device_sysctl_ip_conf_set(self, AF_INET6, "accept_ra", "0"); - set_disable_ipv6(self, "0"); - - /* Synchronize external IPv6 configuration with kernel, since - * linklocal6_start() uses the information there to determine if we can - * proceed with the selected method (SLAAC, DHCP, link-local). - */ - nm_platform_process_events(nm_device_get_platform(self)); - g_clear_object(&priv->ext_ip6_config_captured); - priv->ext_ip6_config_captured = - nm_ip6_config_capture(nm_device_get_multi_index(self), - nm_device_get_platform(self), - nm_device_get_ip_ifindex(self), - NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - - ip6_privacy = _prop_get_ipv6_ip6_privacy(self); - - if (NM_IN_STRSET(method, - NM_SETTING_IP6_CONFIG_METHOD_AUTO, - NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { - if (!addrconf6_start(self, ip6_privacy)) { - /* IPv6 might be disabled; allow IPv4 to proceed */ - ret = NM_ACT_STAGE_RETURN_IP_FAIL; + if (NM_IN_STRSET(method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) + _dev_ipac6_start(self); + else if (NM_IN_STRSET(method, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) + _dev_ipshared6_start(self); + else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { + priv->ipdhcp_data_6.v6.mode = NM_NDISC_DHCP_LEVEL_MANAGED; + _dev_ipdhcpx_start(self, AF_INET6); } else - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) { - ret = - linklocal6_start(self) ? NM_ACT_STAGE_RETURN_SUCCESS : NM_ACT_STAGE_RETURN_POSTPONE; - } else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { - priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_MANAGED; - if (!dhcp6_start(self, TRUE)) { - /* IPv6 might be disabled; allow IPv4 to proceed */ - ret = NM_ACT_STAGE_RETURN_IP_FAIL; - } else - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) - ret = NM_ACT_STAGE_RETURN_SUCCESS; - else - _LOGW(LOGD_IP6, "unhandled IPv6 config method '%s'; will fail", method); - - if (ret != NM_ACT_STAGE_RETURN_FAILURE - && !nm_device_sys_iface_state_is_external_or_assume(self)) { - switch (ip6_privacy) { - case NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN: - case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED: - ip6_privacy_str = "0"; - break; - case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR: - ip6_privacy_str = "1"; - break; - case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR: - ip6_privacy_str = "2"; - break; - } - nm_device_sysctl_ip_conf_set(self, AF_INET6, "use_tempaddr", ip6_privacy_str); + nm_assert(NM_IN_STRSET(method, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)); } - - return ret; - } -} - -gboolean -nm_device_activate_stage3_ip_start(NMDevice *self, int addr_family) -{ - const int IS_IPv4 = NM_IS_IPv4(addr_family); - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMActStageReturn ret; - NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; - gs_unref_object NMIPConfig *ip_config = NULL; - - g_assert(priv->ip_state_x[IS_IPv4] == NM_DEVICE_IP_STATE_WAIT); - - if (nm_device_sys_iface_state_is_external(self)) { - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_DONE); - check_ip_state(self, FALSE, TRUE); - return TRUE; } - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_CONF); +out_devip: + if (klass->act_stage3_ip_config) + klass->act_stage3_ip_config(self, addr_family); - ret = NM_DEVICE_GET_CLASS(self)->act_stage3_ip_config_start(self, - addr_family, - (gpointer *) &ip_config, - &failure_reason); - - switch (ret) { - case NM_ACT_STAGE_RETURN_SUCCESS: - if (!IS_IPv4) { - /* Here we get a static IPv6 config, like for Shared where it's - * autogenerated or from modems where it comes from ModemManager. - */ - if (!ip_config) - ip_config = nm_device_ip_config_new(self, addr_family); - nm_assert(!applied_config_get_current(&priv->ac_ip6_config)); - applied_config_init(&priv->ac_ip6_config, ip_config); - ip_config = NULL; - } - nm_device_activate_schedule_ip_config_result(self, addr_family, ip_config); - break; - case NM_ACT_STAGE_RETURN_IP_DONE: - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_DONE); - check_ip_state(self, FALSE, TRUE); - break; - case NM_ACT_STAGE_RETURN_FAILURE: - nm_device_state_changed(self, NM_DEVICE_STATE_FAILED, failure_reason); - return FALSE; - case NM_ACT_STAGE_RETURN_IP_FAIL: - /* Activation not wanted */ - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_FAIL); - break; - case NM_ACT_STAGE_RETURN_IP_WAIT: - /* Wait for something to try IP config again */ - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_WAIT); - break; - default: - g_assert(ret == NM_ACT_STAGE_RETURN_POSTPONE); - } - - return TRUE; +out: + _dev_ip_state_check_async(self, addr_family); } static void @@ -11576,12 +11621,12 @@ fw_change_zone_cb(NMFirewalldManager * firewalld_manager, switch (priv->fw_state) { case FIREWALL_STATE_WAIT_STAGE_3: priv->fw_state = FIREWALL_STATE_INITIALIZED; - nm_device_activate_schedule_stage3_ip_config_start(self); + nm_device_activate_schedule_stage3_ip_config(self, TRUE); break; case FIREWALL_STATE_WAIT_IP_CONFIG: priv->fw_state = FIREWALL_STATE_INITIALIZED; - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE - || priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) + if (priv->ip_data_4.state == NM_DEVICE_IP_STATE_READY + || priv->ip_data_6.state == NM_DEVICE_IP_STATE_READY) nm_device_start_ip_check(self); break; case FIREWALL_STATE_INITIALIZED: @@ -11633,24 +11678,48 @@ fw_change_zone(NMDevice *self) self); } -/* - * activate_stage3_ip_config_start - * - * Begin automatic/manual IP configuration - * - */ static void -activate_stage3_ip_config_start(NMDevice *self) +activate_stage3_ip_config(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); int ifindex; + /* stage3 is different from stage1+2. + * + * What is true in all cases is that when we start a stage, we call the corresponding + * nm_device_activate_schedule_stage*() function. But usually the stage cannot complete + * right away but needs to wait for some things to happen. So the activate_stage*() function + * returns, and will be later proceeded by calling *the same* stage again. That means, + * activate_stage*() must be re-entrant and be called repeatedly until we can proceed + * to the next stage. Only when the stage is completed, we schedule the next one. + * + * stage3 is different. It does IP configuration and as such (the stage handling itself) + * cannot fail. If a failure happens (for example for DHCP), we remember that (in priv->ipdhcp_data_x) + * and issue _dev_ip_state_check_async(). That one combines the DHCP state to determine the + * overall per-address-family state (priv->ip_data_x). Those states are then combined + * further into priv->combinedip_state, which then leads to nm_device_state_changed() + * (which for example can make the device fully ACTIVATED or FAILED). + * + * The difference between stage1+2 and stage3 is that IP configuration is running continuously + * while the device is active. As such the activate_stage3_ip_config() does not fail directly, + * unlike the other stages which can abort via NM_ACT_STAGE_RETURN_FAILURE. */ + g_return_if_fail(priv->act_request.obj); ifindex = nm_device_get_ip_ifindex(self); + if (priv->ip_data_4.do_reapply) { + _LOGD_ip(AF_INET, "reapply..."); + _cleanup_ip_pre(self, AF_INET, CLEANUP_TYPE_DECONFIGURE); + } + if (priv->ip_data_6.do_reapply) { + _LOGD_ip(AF_INET6, "reapply..."); + _cleanup_ip_pre(self, AF_INET6, CLEANUP_TYPE_DECONFIGURE); + } + /* Add the interface to the specified firewall zone */ - if (priv->fw_state == FIREWALL_STATE_UNMANAGED) { + switch (priv->fw_state) { + case FIREWALL_STATE_UNMANAGED: if (nm_device_sys_iface_state_is_external(self)) { /* fake success */ priv->fw_state = FIREWALL_STATE_INITIALIZED; @@ -11660,143 +11729,141 @@ activate_stage3_ip_config_start(NMDevice *self) return; } /* no ifindex, nothing to do for now */ - } else if (priv->fw_state == FIREWALL_STATE_WAIT_STAGE_3) { + break; + case FIREWALL_STATE_WAIT_STAGE_3: /* a firewall call for stage3 is pending. Return and wait. */ return; + default: + nm_assert(NM_IN_SET((FirewallState) priv->fw_state, + FIREWALL_STATE_INITIALIZED, + FIREWALL_STATE_WAIT_IP_CONFIG)); + break; } - nm_assert(ifindex <= 0 || priv->fw_state == FIREWALL_STATE_INITIALIZED); - _set_ip_state(self, AF_INET, NM_DEVICE_IP_STATE_WAIT); - _set_ip_state(self, AF_INET6, NM_DEVICE_IP_STATE_WAIT); + if (priv->state < NM_DEVICE_STATE_IP_CONFIG) { + _dev_ip_state_req_timeout_schedule(self, AF_INET); + _dev_ip_state_req_timeout_schedule(self, AF_INET6); - _active_connection_set_state_flags(self, NM_ACTIVATION_STATE_FLAG_LAYER2_READY); + _active_connection_set_state_flags(self, NM_ACTIVATION_STATE_FLAG_LAYER2_READY); - nm_device_state_changed(self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); + nm_device_state_changed(self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); - /* Device should be up before we can do anything with it */ - if (!nm_device_sys_iface_state_is_external(self) - && (ifindex = nm_device_get_ip_ifindex(self)) > 0 - && !nm_platform_link_is_up(nm_device_get_platform(self), ifindex)) - _LOGW(LOGD_DEVICE, - "interface %s not up for IP configuration", - nm_device_get_ip_iface(self)); - - if (nm_device_activate_ip4_state_in_wait(self) - && !nm_device_activate_stage3_ip_start(self, AF_INET)) - return; - - if (nm_device_activate_ip6_state_in_wait(self) - && !nm_device_activate_stage3_ip_start(self, AF_INET6)) - return; - - /* Proxy */ - nm_device_set_proxy_config(self, NULL); - - check_ip_state(self, TRUE, TRUE); -} - -/* - * nm_device_activate_schedule_stage3_ip_config_start - * - * Schedule IP configuration start - */ -void -nm_device_activate_schedule_stage3_ip_config_start(NMDevice *self) -{ - NMDevicePrivate *priv; - - g_return_if_fail(NM_IS_DEVICE(self)); - - priv = NM_DEVICE_GET_PRIVATE(self); - - g_return_if_fail(priv->act_request.obj); - - activation_source_schedule(self, activate_stage3_ip_config_start, AF_INET); -} - -static NMActStageReturn -act_stage4_ip_config_timeout(NMDevice * self, - int addr_family, - NMDeviceStateReason *out_failure_reason) -{ - nm_assert_addr_family(addr_family); - - if (!get_ip_config_may_fail(self, addr_family)) { - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return NM_ACT_STAGE_RETURN_FAILURE; + /* Device should be up before we can do anything with it */ + if (!nm_device_sys_iface_state_is_external(self) && ifindex > 0 + && !nm_platform_link_is_up(nm_device_get_platform(self), ifindex)) + _LOGW(LOGD_DEVICE, + "interface %s not up for IP configuration", + nm_device_get_ip_iface(self)); } - return NM_ACT_STAGE_RETURN_SUCCESS; + activate_stage3_ip_config_for_addr_family(self, AF_INET); + activate_stage3_ip_config_for_addr_family(self, AF_INET6); } -static void -activate_stage4_ip_config_timeout_x(NMDevice *self, int addr_family) -{ - NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; - NMActStageReturn ret; - - ret = - NM_DEVICE_GET_CLASS(self)->act_stage4_ip_config_timeout(self, addr_family, &failure_reason); - - if (ret == NM_ACT_STAGE_RETURN_POSTPONE) - return; - - if (ret == NM_ACT_STAGE_RETURN_FAILURE) { - nm_device_state_changed(self, NM_DEVICE_STATE_FAILED, failure_reason); - return; - } - g_assert(ret == NM_ACT_STAGE_RETURN_SUCCESS); - - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_FAIL); - check_ip_state(self, FALSE, TRUE); -} - -static void -activate_stage4_ip_config_timeout_4(NMDevice *self) -{ - activate_stage4_ip_config_timeout_x(self, AF_INET); -} - -static void -activate_stage4_ip_config_timeout_6(NMDevice *self) -{ - activate_stage4_ip_config_timeout_x(self, AF_INET6); -} - -#define activate_stage4_ip_config_timeout_x_fcn(addr_family) \ - (NM_IS_IPv4(addr_family) ? activate_stage4_ip_config_timeout_4 \ - : activate_stage4_ip_config_timeout_6) - void -nm_device_activate_schedule_ip_config_timeout(NMDevice *self, int addr_family) +nm_device_activate_schedule_stage3_ip_config(NMDevice *self, gboolean do_sync) { - NMDevicePrivate *priv; + activation_source_invoke_or_schedule(self, activate_stage3_ip_config, do_sync); +} - g_return_if_fail(NM_IS_DEVICE(self)); +/*****************************************************************************/ - priv = NM_DEVICE_GET_PRIVATE(self); +static void +_dev_ipsharedx_set_state(NMDevice *self, int addr_family, NMDeviceIPState state) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); - g_return_if_fail(priv->act_request.obj); + if (priv->ipshared_data_x[IS_IPv4].state != state) { + _LOGD_ipshared(addr_family, + "set state %s (was %s)", + nm_device_ip_state_to_string(state), + nm_device_ip_state_to_string(priv->ipshared_data_x[IS_IPv4].state)); + priv->ipshared_data_x[IS_IPv4].state = state; + } +} - activation_source_schedule(self, - activate_stage4_ip_config_timeout_x_fcn(addr_family), - addr_family); +static void +_dev_ipsharedx_cleanup(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + + if (IS_IPv4) { + if (priv->ipshared_data_4.v4.dnsmasq_manager) { + nm_clear_g_signal_handler(priv->ipshared_data_4.v4.dnsmasq_manager, + &priv->ipshared_data_4.v4.dnsmasq_state_id); + nm_dnsmasq_manager_stop(priv->ipshared_data_4.v4.dnsmasq_manager); + g_clear_object(&priv->ipshared_data_4.v4.dnsmasq_manager); + } + + if (priv->ipshared_data_4.v4.firewall_config) { + nm_firewall_config_apply(priv->ipshared_data_4.v4.firewall_config, FALSE); + nm_clear_pointer(&priv->ipshared_data_4.v4.firewall_config, nm_firewall_config_free); + } + + nm_clear_pointer(&priv->ipshared_data_4.v4.shared_ip_handle, nm_netns_shared_ip_release); + nm_clear_l3cd(&priv->ipshared_data_4.v4.l3cd); + } + + _dev_ipsharedx_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE); +} + +/*****************************************************************************/ + +static const NML3ConfigData * +_dev_ipshared4_new_l3cd(NMDevice *self, NMConnection *connection, NMPlatformIP4Address *out_addr4) +{ + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + NMSettingIPConfig * s_ip4; + NMPlatformIP4Address address = { + .addr_source = NM_IP_CONFIG_SOURCE_SHARED, + }; + + g_return_val_if_fail(self, NULL); + g_return_val_if_fail(connection, NULL); + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + if (s_ip4 && nm_setting_ip_config_get_num_addresses(s_ip4) > 0) { + /* Use the first user-supplied address */ + NMIPAddress *user = nm_setting_ip_config_get_address(s_ip4, 0); + in_addr_t a; + + nm_ip_address_get_address_binary(user, &a); + nm_platform_ip4_address_set_addr(&address, a, nm_ip_address_get_prefix(user)); + nm_clear_pointer(&priv->ipshared_data_4.v4.shared_ip_handle, nm_netns_shared_ip_release); + } else { + if (!priv->ipshared_data_4.v4.shared_ip_handle) + priv->ipshared_data_4.v4.shared_ip_handle = + nm_netns_shared_ip_reserve(nm_device_get_netns(self)); + nm_platform_ip4_address_set_addr(&address, + priv->ipshared_data_4.v4.shared_ip_handle->addr, + 24); + } + + l3cd = nm_device_create_l3_config_data(self, NM_IP_CONFIG_SOURCE_SHARED); + nm_l3_config_data_add_address_4(l3cd, &address); + + NM_SET_OUT(out_addr4, address); + + return nm_l3_config_data_seal(g_steal_pointer(&l3cd)); } static gboolean -share_init(NMDevice *self, GError **error) +_dev_ipshared4_init(NMDevice *self) { - 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"}; - guint i; - int errsv; + 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; if (nm_platform_sysctl_get_int32(nm_device_get_platform(self), NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_forward"), @@ -11807,15 +11874,7 @@ share_init(NMDevice *self, GError **error) NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_forward"), "1")) { errsv = errno; - _LOGD(LOGD_SHARING, - "share: error enabling IPv4 forwarding: (%d) %s", - errsv, - nm_strerror_native(errsv)); - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "cannot set ipv4/ip_forward: %s", - nm_strerror_native(errsv)); + _LOGW_ipshared(AF_INET, "error enabling IPv4 forwarding: %s", nm_strerror_native(errsv)); return FALSE; } @@ -11828,10 +11887,9 @@ share_init(NMDevice *self, GError **error) NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_dynaddr"), "1")) { errsv = errno; - _LOGD(LOGD_SHARING, - "share: error enabling dynamic addresses: (%d) %s", - errsv, - nm_strerror_native(errsv)); + _LOGD_ipshared(AF_INET, + "share: error enabling dynamic addresses: %s", + nm_strerror_native(errsv)); } for (i = 0; i < G_N_ELEMENTS(modules); i++) @@ -11840,48 +11898,87 @@ share_init(NMDevice *self, GError **error) return TRUE; } -static gboolean -start_sharing(NMDevice *self, NMIP4Config *config, GError **error) +static void +_dev_ipshared4_dnsmasq_state_changed_cb(NMDnsMasqManager *manager, guint status, gpointer user_data) { - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMActRequest * req; - const NMPlatformIP4Address *ip4_addr = NULL; - const char * ip_iface; - GError * local = NULL; - NMConnection * conn; - NMSettingConnection * s_con; - gboolean announce_android_metered; - NMFirewallConfig * firewall_config; + NMDevice *self = NM_DEVICE(user_data); - g_return_val_if_fail(config, FALSE); + if (status != NM_DNSMASQ_STATUS_DEAD) + return; + + _dev_ipsharedx_set_state(self, AF_INET, NM_DEVICE_IP_STATE_FAILED); + _dev_ip_state_check_async(self, AF_INET); +} + +static void +_dev_ipshared4_start(NMDevice *self) +{ + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + NMPlatformIP4Address ip4_addr; + NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); + const char * ip_iface; + gs_free_error GError *error = NULL; + NMConnection * applied; + + if (priv->ipshared_data_4.state != NM_DEVICE_IP_STATE_NONE) + return; + + nm_assert(!priv->ipshared_data_4.v4.firewall_config); + nm_assert(!priv->ipshared_data_4.v4.dnsmasq_manager); + nm_assert(priv->ipshared_data_4.v4.dnsmasq_state_id == 0); ip_iface = nm_device_get_ip_iface(self); - if (!ip_iface) { - g_set_error(error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "device has no ip interface"); - return FALSE; + g_return_if_fail(ip_iface); + + applied = nm_device_get_applied_connection(self); + g_return_if_fail(applied); + + _dev_ipsharedx_set_state(self, AF_INET, NM_DEVICE_IP_STATE_PENDING); + + l3cd = _dev_ipshared4_new_l3cd(self, applied, &ip4_addr); + if (!l3cd) { + nm_assert_not_reached(); + goto out_fail; } - ip4_addr = nm_ip4_config_get_first_address(config); - if (!ip4_addr || !ip4_addr->address) { - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "could not determine IPv4 address"); - return FALSE; - } + if (!_dev_ipshared4_init(self)) + goto out_fail; - if (!share_init(self, error)) - return FALSE; + priv->ipshared_data_4.v4.firewall_config = + nm_firewall_config_new(ip_iface, ip4_addr.address, ip4_addr.plen); + nm_firewall_config_apply(priv->ipshared_data_4.v4.firewall_config, TRUE); - req = nm_device_get_act_request(self); - g_return_val_if_fail(req, FALSE); + priv->ipshared_data_4.v4.l3cd = nm_l3_config_data_ref(l3cd); + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_SHARED_4, l3cd, FALSE); - firewall_config = nm_firewall_config_new(ip_iface, ip4_addr->address, ip4_addr->plen); + /* Wait that the address gets committed before spawning dnsmasq */ + return; +out_fail: + _dev_ipsharedx_set_state(self, AF_INET, NM_DEVICE_IP_STATE_FAILED); + _dev_ip_state_check_async(self, AF_INET); +} - nm_act_request_set_shared(req, firewall_config); +static void +_dev_ipshared4_spawn_dnsmasq(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const char * ip_iface; + gs_free_error GError *error = NULL; + NMSettingConnection * s_con; + gboolean announce_android_metered; + NMConnection * applied; - conn = nm_act_request_get_applied_connection(req); - s_con = nm_connection_get_setting_connection(conn); + nm_assert(priv->ipshared_data_4.v4.firewall_config); + nm_assert(priv->ipshared_data_4.v4.dnsmasq_state_id == 0); + nm_assert(!priv->ipshared_data_4.v4.dnsmasq_manager); + nm_assert(priv->ipshared_data_4.v4.l3cd); + + ip_iface = nm_device_get_ip_iface(self); + g_return_if_fail(ip_iface); + + applied = nm_device_get_applied_connection(self); + g_return_if_fail(applied); + s_con = nm_connection_get_setting_connection(applied); switch (nm_setting_connection_get_metered(s_con)) { case NM_METERED_YES: @@ -11903,353 +12000,54 @@ start_sharing(NMDevice *self, NMIP4Config *config, GError **error) break; } - if (!nm_dnsmasq_manager_start(priv->dnsmasq_manager, - config, + priv->ipshared_data_4.v4.dnsmasq_manager = nm_dnsmasq_manager_new(ip_iface); + if (!nm_dnsmasq_manager_start(priv->ipshared_data_4.v4.dnsmasq_manager, + priv->ipshared_data_4.v4.l3cd, announce_android_metered, - &local)) { - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "could not start dnsmasq due to %s", - local->message); - g_error_free(local); - nm_act_request_set_shared(req, NULL); - return FALSE; + &error)) { + _LOGW_ipshared(AF_INET, "could not start dnsmasq: %s", error->message); + goto out_fail; } - priv->dnsmasq_state_id = g_signal_connect(priv->dnsmasq_manager, - NM_DNS_MASQ_MANAGER_STATE_CHANGED, - G_CALLBACK(dnsmasq_state_changed_cb), - self); - return TRUE; + priv->ipshared_data_4.v4.dnsmasq_state_id = + g_signal_connect(priv->ipshared_data_4.v4.dnsmasq_manager, + NM_DNS_MASQ_MANAGER_STATE_CHANGED, + G_CALLBACK(_dev_ipshared4_dnsmasq_state_changed_cb), + self); + + _dev_ipsharedx_set_state(self, AF_INET, NM_DEVICE_IP_STATE_READY); + _dev_ip_state_check_async(self, AF_INET); + return; + +out_fail: + _dev_ipsharedx_set_state(self, AF_INET, NM_DEVICE_IP_STATE_FAILED); + _dev_ip_state_check_async(self, AF_INET); } +/*****************************************************************************/ + static void -arp_cleanup(NMDevice *self) +_dev_ipshared6_start(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - nm_clear_pointer(&priv->acd.announcing, nm_acd_manager_free); -} + _dev_ipac6_start(self); -void -nm_device_arp_announce(NMDevice *self) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - NMConnection * connection; - NMSettingIPConfig *s_ip4; - guint num, i; - const guint8 * hw_addr; - size_t hw_addr_len = 0; - - arp_cleanup(self); - - hw_addr = nm_platform_link_get_address(nm_device_get_platform(self), - nm_device_get_ip_ifindex(self), - &hw_addr_len); - - if (!hw_addr || hw_addr_len != ETH_ALEN) + if (priv->ipshared_data_6.state != NM_DEVICE_IP_STATE_NONE) return; - /* We only care about manually-configured addresses; DHCP- and autoip-configured - * ones should already have been seen on the network at this point. - */ - connection = nm_device_get_applied_connection(self); - if (!connection) - return; - s_ip4 = nm_connection_get_setting_ip4_config(connection); - if (!s_ip4) - return; - num = nm_setting_ip_config_get_num_addresses(s_ip4); - if (num == 0) - return; - - priv->acd.announcing = - nm_acd_manager_new(nm_device_get_ip_ifindex(self), hw_addr, hw_addr_len, NULL, NULL); - - for (i = 0; i < num; i++) { - NMIPAddress *ip = nm_setting_ip_config_get_address(s_ip4, i); - in_addr_t addr; - - if (inet_pton(AF_INET, nm_ip_address_get_address(ip), &addr) == 1) - nm_acd_manager_add_address(priv->acd.announcing, addr); - else - g_warn_if_reached(); - } - - nm_acd_manager_announce_addresses(priv->acd.announcing); -} - -static void -activate_stage5_ip_config_result_x(NMDevice *self, int addr_family) -{ - const int IS_IPv4 = NM_IS_IPv4(addr_family); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMActRequest * req; - const char * method; - int ip_ifindex; - int errsv; - - req = nm_device_get_act_request(self); - g_assert(req); - - nm_clear_g_source_inst(&priv->ip_req_timeout_source_x[IS_IPv4]); - - /* Interface must be IFF_UP before IP config can be applied */ - ip_ifindex = nm_device_get_ip_ifindex(self); - g_return_if_fail(ip_ifindex); - - if (!nm_platform_link_is_up(nm_device_get_platform(self), ip_ifindex) - && !nm_device_sys_iface_state_is_external(self)) { - nm_platform_link_change_flags(nm_device_get_platform(self), ip_ifindex, IFF_UP, TRUE); - if (!nm_platform_link_is_up(nm_device_get_platform(self), ip_ifindex)) - _LOGW(LOGD_DEVICE, - "interface %s not up for IP configuration", - nm_device_get_ip_iface(self)); - } - - if (!ip_config_merge_and_apply(self, addr_family, TRUE)) { - _LOGD(LOGD_DEVICE | LOGD_IPX(IS_IPv4), - "Activation: Stage 5 of 5 (IPv%c Commit) failed", - nm_utils_addr_family_to_char(addr_family)); - nm_device_ip_method_failed(self, addr_family, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + if (!nm_platform_sysctl_set( + nm_device_get_platform(self), + NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv6/conf/all/forwarding"), + "1")) { + _LOGW_ipshared(AF_INET6, "failure to enable ipv6 forwarding"); + _dev_ipsharedx_set_state(self, AF_INET6, NM_DEVICE_IP_STATE_FAILED); + _dev_ip_state_check_async(self, AF_INET6); return; } - if (!IS_IPv4) { - if (priv->dhcp6.mode != NM_NDISC_DHCP_LEVEL_NONE - && priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) { - if (applied_config_get_current(&priv->dhcp6.ip6_config)) { - /* If IPv6 wasn't the first IP to complete, and DHCP was used, - * then ensure dispatcher scripts get the DHCP lease information. - */ - nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_6, - self, - NULL, - NULL, - NULL, - NULL); - } else { - /* still waiting for first dhcp6 lease. */ - return; - } - } - } - - /* Start IPv4 sharing/IPv6 forwarding if we need it */ - method = nm_device_get_effective_ip_config_method(self, addr_family); - if (IS_IPv4) { - if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { - gs_free_error GError *error = NULL; - - if (!start_sharing(self, priv->ip_config_4, &error)) { - _LOGW(LOGD_SHARING, - "Activation: Stage 5 of 5 (IPv4 Commit) start sharing failed: %s", - error->message); - nm_device_ip_method_failed(self, - AF_INET, - NM_DEVICE_STATE_REASON_SHARED_START_FAILED); - return; - } - } - } else { - if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { - if (!nm_platform_sysctl_set( - nm_device_get_platform(self), - NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv6/conf/all/forwarding"), - "1")) { - errsv = errno; - _LOGE(LOGD_SHARING, - "share: error enabling IPv6 forwarding: (%d) %s", - errsv, - nm_strerror_native(errsv)); - nm_device_ip_method_failed(self, - AF_INET6, - NM_DEVICE_STATE_REASON_SHARED_START_FAILED); - return; - } - } - } - - if (IS_IPv4) { - if (priv->dhcp_data_4.client) { - gs_free_error GError *error = NULL; - - if (!nm_dhcp_client_accept(priv->dhcp_data_4.client, &error)) { - _LOGW(LOGD_DHCP4, - "Activation: Stage 5 of 5 (IPv4 Commit) error accepting lease: %s", - error->message); - nm_device_ip_method_failed(self, AF_INET, NM_DEVICE_STATE_REASON_DHCP_ERROR); - return; - } - } - - /* If IPv4 wasn't the first to complete, and DHCP was used, then ensure - * dispatcher scripts get the DHCP lease information. - */ - if (priv->dhcp_data_4.client && nm_device_activate_ip4_state_in_conf(self) - && (nm_device_get_state(self) > NM_DEVICE_STATE_IP_CONFIG)) { - nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_4, - self, - NULL, - NULL, - NULL, - NULL); - } - } - - if (!IS_IPv4) { - /* Check if we have to wait for DAD */ - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF && !priv->dad6_ip6_config) { - if (!priv->carrier && priv->ignore_carrier && get_ip_config_may_fail(self, AF_INET6)) - _LOGI(LOGD_DEVICE | LOGD_IP6, - "IPv6 DAD: carrier missing and ignored, not delaying activation"); - else - priv->dad6_ip6_config = dad6_get_pending_addresses(self); - - if (priv->dad6_ip6_config) { - _LOGD(LOGD_DEVICE | LOGD_IP6, "IPv6 DAD: awaiting termination"); - } else { - _set_ip_state(self, AF_INET6, NM_DEVICE_IP_STATE_DONE); - check_ip_state(self, FALSE, TRUE); - } - } - } - - if (IS_IPv4 && priv->carrier) { - /* We send ARP announcements only when the link gets carrier, - * otherwise the announcements would be lost. Furthermore, for - * controllers having carrier implies that there is at least one - * port and therefore the MAC address is the correct one. - */ - nm_device_arp_announce(self); - } - - if (IS_IPv4) { - /* Enter the IP_CHECK state if this is the first method to complete */ - _set_ip_state(self, AF_INET, NM_DEVICE_IP_STATE_DONE); - check_ip_state(self, FALSE, TRUE); - } -} - -static void -activate_stage5_ip_config_result_4(NMDevice *self) -{ - activate_stage5_ip_config_result_x(self, AF_INET); -} - -static void -activate_stage5_ip_config_result_6(NMDevice *self) -{ - activate_stage5_ip_config_result_x(self, AF_INET6); -} - -#define activate_stage5_ip_config_result_x_fcn(addr_family) \ - (NM_IS_IPv4(addr_family) ? activate_stage5_ip_config_result_4 \ - : activate_stage5_ip_config_result_6) - -void -nm_device_activate_schedule_ip_config_result(NMDevice *self, int addr_family, NMIPConfig *config) -{ - NMDevicePrivate *priv; - const int IS_IPv4 = NM_IS_IPv4(addr_family); - - g_return_if_fail(NM_IS_DEVICE(self)); - g_return_if_fail(!config || (IS_IPv4 && nm_ip_config_get_addr_family(config) == AF_INET)); - - priv = NM_DEVICE_GET_PRIVATE(self); - - if (IS_IPv4) { - applied_config_init(&priv->dev_ip_config_4, config); - } else { - /* If IP had previously failed, move it back to NM_DEVICE_IP_STATE_CONF since we - * clearly now have configuration. - */ - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL) - _set_ip_state(self, AF_INET6, NM_DEVICE_IP_STATE_CONF); - } - - activation_source_schedule(self, - activate_stage5_ip_config_result_x_fcn(addr_family), - addr_family); -} - -NMDeviceIPState -nm_device_activate_get_ip_state(NMDevice *self, int addr_family) -{ - const int IS_IPv4 = NM_IS_IPv4(addr_family); - - g_return_val_if_fail(NM_IS_DEVICE(self), NM_DEVICE_IP_STATE_NONE); - g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), NM_DEVICE_IP_STATE_NONE); - - return NM_DEVICE_GET_PRIVATE(self)->ip_state_x[IS_IPv4]; -} - -static void -dad6_add_pending_address(NMDevice * self, - NMPlatform * platform, - int ifindex, - const struct in6_addr *address, - NMIP6Config ** dad6_config) -{ - const NMPlatformIP6Address *pl_addr; - - pl_addr = nm_platform_ip6_address_get(platform, ifindex, address); - if (pl_addr && NM_FLAGS_HAS(pl_addr->n_ifa_flags, IFA_F_TENTATIVE) - && !NM_FLAGS_HAS(pl_addr->n_ifa_flags, IFA_F_DADFAILED) - && !NM_FLAGS_HAS(pl_addr->n_ifa_flags, IFA_F_OPTIMISTIC)) { - _LOGt(LOGD_DEVICE, - "IPv6 DAD: pending address %s", - nm_platform_ip6_address_to_string(pl_addr, NULL, 0)); - - if (!*dad6_config) - *dad6_config = nm_device_ip6_config_new(self); - - nm_ip6_config_add_address(*dad6_config, pl_addr); - } -} - -/* - * Returns a NMIP6Config containing NM-configured addresses which - * have the tentative flag, or NULL if none is present. - */ -static NMIP6Config * -dad6_get_pending_addresses(NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMIP6Config * confs[] = {(NMIP6Config *) applied_config_get_current(&priv->ac_ip6_config), - (NMIP6Config *) applied_config_get_current(&priv->dhcp6.ip6_config), - priv->con_ip_config_6, - (NMIP6Config *) applied_config_get_current(&priv->dev2_ip_config_6)}; - const NMPlatformIP6Address *addr; - NMIP6Config * dad6_config = NULL; - NMDedupMultiIter ipconf_iter; - guint i; - int ifindex; - NMPlatform * platform; - - ifindex = nm_device_get_ip_ifindex(self); - g_return_val_if_fail(ifindex > 0, NULL); - - platform = nm_device_get_platform(self); - - if (priv->ipv6ll_has) { - dad6_add_pending_address(self, platform, ifindex, &priv->ipv6ll_addr, &dad6_config); - } - - /* We are interested only in addresses that we have explicitly configured, - * not in externally added ones. - */ - for (i = 0; i < G_N_ELEMENTS(confs); i++) { - if (!confs[i]) - continue; - - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, confs[i], &addr) { - dad6_add_pending_address(self, platform, ifindex, &addr->address, &dad6_config); - } - } - - return dad6_config; + _dev_ipsharedx_set_state(self, AF_INET6, NM_DEVICE_IP_STATE_READY); + _dev_ip_state_check_async(self, AF_INET6); } /*****************************************************************************/ @@ -12290,21 +12088,6 @@ act_request_set(NMDevice *self, NMActRequest *act_request) } } -static void -dnsmasq_cleanup(NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - if (!priv->dnsmasq_manager) - return; - - nm_clear_g_signal_handler(priv->dnsmasq_manager, &priv->dnsmasq_state_id); - - nm_dnsmasq_manager_stop(priv->dnsmasq_manager); - g_object_unref(priv->dnsmasq_manager); - priv->dnsmasq_manager = NULL; -} - gboolean nm_device_is_nm_owned(NMDevice *self) { @@ -12391,32 +12174,24 @@ delete_on_deactivate_check_and_schedule(NMDevice *self) static void _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - const int IS_IPv4 = NM_IS_IPv4(addr_family); + const int IS_IPv4 = NM_IS_IPv4(addr_family); - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_NONE); + _dev_ipsharedx_cleanup(self, addr_family); - if (nm_clear_g_source(&priv->queued_ip_config_id_x[IS_IPv4])) { - _LOGD(LOGD_DEVICE, - "clearing queued IP%c config change", - nm_utils_addr_family_to_char(addr_family)); - } + _dev_ipdev_cleanup(self, AF_UNSPEC); + _dev_ipdev_cleanup(self, addr_family); - if (IS_IPv4) { - dhcp4_cleanup(self, cleanup_type, FALSE); - arp_cleanup(self); - dnsmasq_cleanup(self); - ipv4ll_cleanup(self); - g_slist_free_full(priv->acd.dad_list, (GDestroyNotify) nm_acd_manager_free); - priv->acd.dad_list = NULL; - } else { - g_slist_free_full(priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref); - priv->dad6_failed_addrs = NULL; - g_clear_object(&priv->dad6_ip6_config); - dhcp6_cleanup(self, cleanup_type, FALSE); - nm_clear_g_source(&priv->linklocal6_timeout_id); - addrconf6_cleanup(self); - } + _dev_ipdhcpx_cleanup(self, addr_family, TRUE, FALSE); + + if (!IS_IPv4) + _dev_ipac6_cleanup(self); + + _dev_ipllx_cleanup(self, addr_family); + + _dev_ipmanual_cleanup(self); + + _dev_ip_state_cleanup(self, AF_UNSPEC); + _dev_ip_state_cleanup(self, addr_family); } gboolean @@ -12487,128 +12262,6 @@ _nm_device_hash_check_invalid_keys(GHashTable * hash, return FALSE; } -void -nm_device_reactivate_ip_config(NMDevice * self, - int addr_family, - NMSettingIPConfig *s_ip_old, - NMSettingIPConfig *s_ip_new) -{ - const int IS_IPv4 = NM_IS_IPv4(addr_family); - NMDevicePrivate *priv; - const char * method_old; - const char * method_new; - - g_return_if_fail(NM_IS_DEVICE(self)); - - priv = NM_DEVICE_GET_PRIVATE(self); - - if (priv->ip_state_x[IS_IPv4] == NM_DEVICE_IP_STATE_NONE) - return; - - g_clear_object(&priv->con_ip_config_x[IS_IPv4]); - g_clear_object(&priv->ext_ip_config_x[IS_IPv4]); - if (IS_IPv4) { - g_clear_object(&priv->dev_ip_config_4.current); - } else { - g_clear_object(&priv->ac_ip6_config.current); - g_clear_object(&priv->dhcp6.ip6_config.current); - } - g_clear_object(&priv->dev2_ip_config_x[IS_IPv4].current); - - if (!IS_IPv4) { - if (priv->ipv6ll_handle && !IN6_IS_ADDR_UNSPECIFIED(&priv->ipv6ll_addr)) - priv->ipv6ll_has = TRUE; - } - - priv->con_ip_config_x[IS_IPv4] = nm_device_ip_config_new(self, addr_family); - - if (IS_IPv4) { - nm_ip4_config_merge_setting(priv->con_ip_config_4, - s_ip_new, - _prop_get_connection_mdns(self), - _prop_get_connection_llmnr(self), - _prop_get_connection_dns_over_tls(self), - nm_device_get_route_table(self, AF_INET), - nm_device_get_route_metric(self, AF_INET)); - } else { - nm_ip6_config_merge_setting(priv->con_ip_config_6, - s_ip_new, - nm_device_get_route_table(self, AF_INET6), - nm_device_get_route_metric(self, AF_INET6)); - } - - method_old = (s_ip_old ? nm_setting_ip_config_get_method(s_ip_old) : NULL) - ?: (IS_IPv4 ? NM_SETTING_IP4_CONFIG_METHOD_DISABLED - : NM_SETTING_IP6_CONFIG_METHOD_IGNORE); - method_new = (s_ip_new ? nm_setting_ip_config_get_method(s_ip_new) : NULL) - ?: (IS_IPv4 ? NM_SETTING_IP4_CONFIG_METHOD_DISABLED - : NM_SETTING_IP6_CONFIG_METHOD_IGNORE); - - if (!nm_streq0(method_old, method_new)) { - _cleanup_ip_pre(self, addr_family, CLEANUP_TYPE_DECONFIGURE); - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_WAIT); - if (!nm_device_activate_stage3_ip_start(self, addr_family)) { - _LOGW(LOGD_IP4, - "Failed to apply IPv%c configuration", - nm_utils_addr_family_to_char(addr_family)); - } - return; - } - - if (s_ip_old && s_ip_new) { - gint64 metric_old, metric_new; - - /* For dynamic IP methods (DHCP, IPv4LL, WWAN) the route metric is - * set at activation/renewal time using the value from static - * configuration. To support runtime change we need to update the - * dynamic configuration in place and tell the DHCP client the new - * value to use for future renewals. - */ - metric_old = nm_setting_ip_config_get_route_metric(s_ip_old); - metric_new = nm_setting_ip_config_get_route_metric(s_ip_new); - - if (metric_old != metric_new) { - if (IS_IPv4) { - if (priv->dev_ip_config_4.orig) { - nm_ip4_config_update_routes_metric((NMIP4Config *) priv->dev_ip_config_4.orig, - nm_device_get_route_metric(self, AF_INET)); - } - if (priv->dev2_ip_config_4.orig) { - nm_ip4_config_update_routes_metric((NMIP4Config *) priv->dev2_ip_config_4.orig, - nm_device_get_route_metric(self, AF_INET)); - } - if (priv->dhcp_data_4.client) { - nm_dhcp_client_set_route_metric(priv->dhcp_data_4.client, - nm_device_get_route_metric(self, AF_INET)); - } - } else { - if (priv->ac_ip6_config.orig) { - nm_ip6_config_update_routes_metric((NMIP6Config *) priv->ac_ip6_config.orig, - nm_device_get_route_metric(self, AF_INET6)); - } - if (priv->dhcp6.ip6_config.orig) { - nm_ip6_config_update_routes_metric((NMIP6Config *) priv->dhcp6.ip6_config.orig, - nm_device_get_route_metric(self, AF_INET6)); - } - if (priv->dev2_ip_config_6.orig) { - nm_ip6_config_update_routes_metric((NMIP6Config *) priv->dev2_ip_config_6.orig, - nm_device_get_route_metric(self, AF_INET6)); - } - if (priv->dhcp_data_6.client) { - nm_dhcp_client_set_route_metric(priv->dhcp_data_6.client, - nm_device_get_route_metric(self, AF_INET6)); - } - } - } - } - - if (nm_device_get_ip_ifindex(self) > 0 && !ip_config_merge_and_apply(self, addr_family, TRUE)) { - _LOGW(LOGD_IPX(IS_IPv4), - "Failed to reapply IPv%c configuration", - nm_utils_addr_family_to_char(addr_family)); - } -} - static void _pacrunner_manager_add(NMDevice *self) { @@ -12617,10 +12270,8 @@ _pacrunner_manager_add(NMDevice *self) nm_pacrunner_manager_remove_clear(&priv->pacrunner_conf_id); priv->pacrunner_conf_id = nm_pacrunner_manager_add(nm_pacrunner_manager_get(), - priv->proxy_config, nm_device_get_ip_iface(self), - NULL, - NULL); + nm_device_get_l3cd(self, TRUE)); } static void @@ -12628,12 +12279,12 @@ reactivate_proxy_config(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (!priv->pacrunner_conf_id) - return; - nm_device_set_proxy_config(self, priv->dhcp4.pac_url); - _pacrunner_manager_add(self); + if (priv->pacrunner_conf_id) + _pacrunner_manager_add(self); } +/*****************************************************************************/ + static gboolean can_reapply_change(NMDevice * self, const char *setting_name, @@ -12729,9 +12380,8 @@ check_and_reapply_connection(NMDevice * self, NMConnection * applied = nm_device_get_applied_connection(self); gs_unref_object NMConnection *applied_clone = NULL; gs_unref_hashtable GHashTable *diffs = NULL; - NMConnection * con_old, *con_new; - NMSettingIPConfig * s_ip4_old, *s_ip4_new; - NMSettingIPConfig * s_ip6_old, *s_ip6_new; + NMConnection * con_old; + NMConnection * con_new; GHashTableIter iter; if (priv->state < NM_DEVICE_STATE_PREPARE || priv->state > NM_DEVICE_STATE_ACTIVATED) { @@ -12780,11 +12430,11 @@ check_and_reapply_connection(NMDevice * self, && version_id != nm_active_connection_version_id_get( (NMActiveConnection *) priv->act_request.obj)) { - g_set_error_literal( - error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_VERSION_ID_MISMATCH, - "Reapply failed because device changed in the meantime and the version-id mismatches"); + g_set_error_literal(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_VERSION_ID_MISMATCH, + "Reapply failed because device changed in the meantime and the " + "version-id mismatches"); return FALSE; } @@ -12847,11 +12497,9 @@ check_and_reapply_connection(NMDevice * self, } else con_old = con_new = applied; - priv->v4_commit_first_time = TRUE; - priv->v6_commit_first_time = TRUE; - priv->v4_route_table_initialized = FALSE; priv->v6_route_table_initialized = FALSE; + priv->l3config_merge_flags_has = FALSE; /************************************************************************** * Reapply changes @@ -12868,20 +12516,21 @@ check_and_reapply_connection(NMDevice * self, lldp_setup(self, NM_TERNARY_DEFAULT); if (priv->state >= NM_DEVICE_STATE_IP_CONFIG) { - s_ip4_old = nm_connection_get_setting_ip4_config(con_old); - s_ip4_new = nm_connection_get_setting_ip4_config(con_new); - s_ip6_old = nm_connection_get_setting_ip6_config(con_old); - s_ip6_new = nm_connection_get_setting_ip6_config(con_new); - /* Allow reapply of MTU */ priv->mtu_source = NM_DEVICE_MTU_SOURCE_NONE; - nm_device_reactivate_ip_config(self, AF_INET, s_ip4_old, s_ip4_new); - nm_device_reactivate_ip_config(self, AF_INET6, s_ip6_old, s_ip6_new); + if (nm_g_hash_table_lookup(diffs, NM_SETTING_IP4_CONFIG_SETTING_NAME)) + priv->ip_data_4.do_reapply = TRUE; + if (nm_g_hash_table_lookup(diffs, NM_SETTING_IP6_CONFIG_SETTING_NAME)) + priv->ip_data_6.do_reapply = TRUE; + + nm_device_activate_schedule_stage3_ip_config(self, FALSE); _routing_rules_sync(self, NM_TERNARY_TRUE); reactivate_proxy_config(self); + + nm_device_l3cfg_commit(self, NM_L3_CFG_COMMIT_TYPE_REAPPLY, FALSE); } if (priv->state >= NM_DEVICE_STATE_IP_CHECK) @@ -13111,96 +12760,6 @@ impl_device_get_applied_connection(NMDBusObject * obj, /*****************************************************************************/ -typedef struct { - gint64 timestamp_ms; - bool dirty; -} IP6RoutesTemporaryNotAvailableData; - -static gboolean -_rt6_temporary_not_available_timeout(gpointer user_data) -{ - NMDevice * self = NM_DEVICE(user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - priv->rt6_temporary_not_available_id = 0; - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - - return G_SOURCE_REMOVE; -} - -static gboolean -_rt6_temporary_not_available_set(NMDevice *self, GPtrArray *temporary_not_available) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - IP6RoutesTemporaryNotAvailableData *data; - GHashTableIter iter; - gint64 now_ms, oldest_ms; - const gint64 MAX_AGE_MS = 20000; - guint i; - gboolean success = TRUE; - - if (!temporary_not_available || !temporary_not_available->len) { - /* nothing outstanding. Clear tracking the routes. */ - nm_clear_pointer(&priv->rt6_temporary_not_available, g_hash_table_unref); - nm_clear_g_source(&priv->rt6_temporary_not_available_id); - return success; - } - - if (priv->rt6_temporary_not_available) { - g_hash_table_iter_init(&iter, priv->rt6_temporary_not_available); - while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &data)) - data->dirty = TRUE; - } else { - priv->rt6_temporary_not_available = - g_hash_table_new_full((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal, - (GDestroyNotify) nmp_object_unref, - nm_g_slice_free_fcn(IP6RoutesTemporaryNotAvailableData)); - } - - now_ms = nm_utils_get_monotonic_timestamp_msec(); - oldest_ms = now_ms; - - for (i = 0; i < temporary_not_available->len; i++) { - const NMPObject *o = temporary_not_available->pdata[i]; - - data = g_hash_table_lookup(priv->rt6_temporary_not_available, o); - if (data) { - if (!data->dirty) - continue; - data->dirty = FALSE; - nm_assert(data->timestamp_ms > 0 && data->timestamp_ms <= now_ms); - if (now_ms > data->timestamp_ms + MAX_AGE_MS) { - /* timeout. Could not add this address. */ - _LOGW(LOGD_DEVICE, - "failure to add IPv6 route: %s", - nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - success = FALSE; - } else - oldest_ms = MIN(data->timestamp_ms, oldest_ms); - continue; - } - - data = g_slice_new0(IP6RoutesTemporaryNotAvailableData); - data->timestamp_ms = now_ms; - g_hash_table_insert(priv->rt6_temporary_not_available, (gpointer) nmp_object_ref(o), data); - } - - g_hash_table_iter_init(&iter, priv->rt6_temporary_not_available); - while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &data)) { - if (data->dirty) - g_hash_table_iter_remove(&iter); - } - - nm_clear_g_source(&priv->rt6_temporary_not_available_id); - priv->rt6_temporary_not_available_id = - g_timeout_add(oldest_ms + MAX_AGE_MS - now_ms, _rt6_temporary_not_available_timeout, self); - - return success; -} - -/*****************************************************************************/ - static void disconnect_cb(NMDevice * self, GDBusMethodInvocation *context, @@ -13580,324 +13139,44 @@ nm_device_is_activating(NMDevice *self) * handler is actually run. If there's an activation handler scheduled * we're activating anyway. */ - return priv->activation_source_id_4 != 0; + return !!priv->activation_idle_source; } -NMProxyConfig * -nm_device_get_proxy_config(NMDevice *self) -{ - g_return_val_if_fail(NM_IS_DEVICE(self), NULL); - - return NM_DEVICE_GET_PRIVATE(self)->proxy_config; -} - -static void -nm_device_set_proxy_config(NMDevice *self, const char *pac_url) -{ - NMDevicePrivate *priv; - NMConnection * connection; - NMSettingProxy * s_proxy = NULL; - - g_return_if_fail(NM_IS_DEVICE(self)); - - priv = NM_DEVICE_GET_PRIVATE(self); - - g_clear_object(&priv->proxy_config); - priv->proxy_config = nm_proxy_config_new(); - - if (pac_url) { - nm_proxy_config_set_method(priv->proxy_config, NM_PROXY_CONFIG_METHOD_AUTO); - nm_proxy_config_set_pac_url(priv->proxy_config, pac_url); - _LOGD(LOGD_PROXY, "proxy: PAC url \"%s\"", pac_url); - } else - nm_proxy_config_set_method(priv->proxy_config, NM_PROXY_CONFIG_METHOD_NONE); - - connection = nm_device_get_applied_connection(self); - if (connection) - s_proxy = nm_connection_get_setting_proxy(connection); - - if (s_proxy) - nm_proxy_config_merge_setting(priv->proxy_config, s_proxy); -} - -/* IP Configuration stuff */ NMDhcpConfig * nm_device_get_dhcp_config(NMDevice *self, int addr_family) { - const int IS_IPv4 = NM_IS_IPv4(addr_family); - g_return_val_if_fail(NM_IS_DEVICE(self), NULL); - nm_assert_addr_family(addr_family); - - return NM_DEVICE_GET_PRIVATE(self)->dhcp_data_x[IS_IPv4].config; + return NM_DEVICE_GET_PRIVATE(self)->ipdhcp_data_x[NM_IS_IPv4(addr_family)].config; } -NMIP4Config * -nm_device_get_ip4_config(NMDevice *self) +NML3Cfg * +nm_device_get_l3cfg(NMDevice *self) { g_return_val_if_fail(NM_IS_DEVICE(self), NULL); - return NM_DEVICE_GET_PRIVATE(self)->ip_config_4; + return NM_DEVICE_GET_PRIVATE(self)->l3cfg; } -static gboolean -nm_device_set_ip_config(NMDevice * self, - int addr_family, - NMIPConfig *new_config, - gboolean commit, - GPtrArray * ip4_dev_route_blacklist) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - const int IS_IPv4 = NM_IS_IPv4(addr_family); - NMIPConfig * old_config; - gboolean has_changes = FALSE; - gboolean success = TRUE; - NMSettingsConnection * settings_connection; - NMIPRouteTableSyncMode route_table_sync_mode; - - nm_assert_addr_family(addr_family); - nm_assert(!new_config || nm_ip_config_get_addr_family(new_config) == addr_family); - nm_assert(!new_config - || (new_config && ({ - int ip_ifindex = nm_device_get_ip_ifindex(self); - - (ip_ifindex > 0 && ip_ifindex == nm_ip_config_get_ifindex(new_config)); - }))); - nm_assert(IS_IPv4 || !ip4_dev_route_blacklist); - - if (commit && new_config) - route_table_sync_mode = _get_route_table_sync_mode_stateful(self, addr_family); - else - route_table_sync_mode = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE; - - _LOGD(LOGD_IPX(IS_IPv4), - "ip%c-config: update (commit=%d, new-config=" NM_HASH_OBFUSCATE_PTR_FMT - ", route-table-sync-mode=%d)", - nm_utils_addr_family_to_char(addr_family), - commit, - NM_HASH_OBFUSCATE_PTR(new_config), - (int) route_table_sync_mode); - - /* Always commit to nm-platform to update lifetimes */ - if (commit && new_config) { - _commit_mtu(self, IS_IPv4 ? NM_IP4_CONFIG(new_config) : priv->ip_config_4); - - if (IS_IPv4) { - success = nm_ip4_config_commit(NM_IP4_CONFIG(new_config), - nm_device_get_platform(self), - route_table_sync_mode); - nm_platform_ip4_dev_route_blacklist_set(nm_device_get_platform(self), - nm_ip_config_get_ifindex(new_config), - ip4_dev_route_blacklist); - } else { - gs_unref_ptrarray GPtrArray *temporary_not_available = NULL; - - success = nm_ip6_config_commit(NM_IP6_CONFIG(new_config), - nm_device_get_platform(self), - route_table_sync_mode, - &temporary_not_available); - - if (!_rt6_temporary_not_available_set(self, temporary_not_available)) - success = FALSE; - } - } - - old_config = priv->ip_config_x[IS_IPv4]; - - if (new_config && old_config) { - /* has_changes is set only on relevant changes, because when the configuration changes, - * this causes a re-read and reset. This should only happen for relevant changes */ - nm_ip_config_replace(old_config, new_config, &has_changes); - if (has_changes) { - _LOGD(LOGD_IPX(IS_IPv4), - "ip%c-config: update IP Config instance (%s)", - nm_utils_addr_family_to_char(addr_family), - nm_dbus_object_get_path(NM_DBUS_OBJECT(old_config))); - } - } else if (new_config /*&& !old_config*/) { - has_changes = TRUE; - priv->ip_config_x[IS_IPv4] = g_object_ref(new_config); - if (!nm_dbus_object_is_exported(NM_DBUS_OBJECT(new_config))) - nm_dbus_object_export(NM_DBUS_OBJECT(new_config)); - - _LOGD(LOGD_IPX(IS_IPv4), - "ip%c-config: set IP Config instance (%s)", - nm_utils_addr_family_to_char(addr_family), - nm_dbus_object_get_path(NM_DBUS_OBJECT(new_config))); - } else if (old_config /*&& !new_config*/) { - has_changes = TRUE; - priv->ip_config_x[IS_IPv4] = NULL; - _LOGD(LOGD_IPX(IS_IPv4), - "ip%c-config: clear IP Config instance (%s)", - nm_utils_addr_family_to_char(addr_family), - nm_dbus_object_get_path(NM_DBUS_OBJECT(old_config))); - if (IS_IPv4) { - /* Device config is invalid if combined config is invalid */ - applied_config_clear(&priv->dev_ip_config_4); - } else - priv->needs_ip6_subnet = FALSE; - } - - if (has_changes) { - if (old_config != priv->ip_config_x[IS_IPv4]) - _notify(self, IS_IPv4 ? PROP_IP4_CONFIG : PROP_IP6_CONFIG); - - g_signal_emit(self, - signals[IS_IPv4 ? IP4_CONFIG_CHANGED : IP6_CONFIG_CHANGED], - 0, - priv->ip_config_x[IS_IPv4], - old_config); - - if (old_config != priv->ip_config_x[IS_IPv4]) - nm_dbus_object_clear_and_unexport(&old_config); - - if (nm_device_sys_iface_state_is_external(self) - && (settings_connection = nm_device_get_settings_connection(self)) - && NM_FLAGS_HAS(nm_settings_connection_get_flags(settings_connection), - NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL) - && nm_active_connection_get_activation_type(NM_ACTIVE_CONNECTION(priv->act_request.obj)) - == NM_ACTIVATION_TYPE_EXTERNAL) { - gs_unref_object NMConnection *new_connection = NULL; - - new_connection = nm_simple_connection_new_clone( - nm_settings_connection_get_connection(settings_connection)); - - nm_connection_add_setting( - new_connection, - IS_IPv4 ? nm_ip4_config_create_setting(priv->ip_config_4) - : nm_ip6_config_create_setting(priv->ip_config_6, - _get_maybe_ipv6_disabled(self))); - - nm_settings_connection_update(settings_connection, - new_connection, - NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, - NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, - NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, - NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET, - "update-external", - NULL); - } - - nm_device_queue_recheck_assume(self); - - if (!IS_IPv4) { - if (priv->ndisc) - ndisc_set_router_config(priv->ndisc, self); - } - } - - nm_assert(!old_config || old_config == priv->ip_config_x[IS_IPv4]); - - return success; -} - -static gboolean -_replace_vpn_config_in_list(GSList **plist, GObject *old, GObject *new) -{ - GSList *old_link; - - /* Below, assert that @new is not yet tracked, but still behave - * correctly in any case. Don't complain for missing @old since - * it could have been removed when the parent device became - * unmanaged. */ - - if (old && (old_link = g_slist_find(*plist, old))) { - if (old != new) { - if (new) - old_link->data = g_object_ref(new); - else - *plist = g_slist_delete_link(*plist, old_link); - g_object_unref(old); - } - return TRUE; - } - - if (new) { - if (!g_slist_find(*plist, new)) - *plist = g_slist_append(*plist, g_object_ref(new)); - else - g_return_val_if_reached(TRUE); - return TRUE; - } - - return FALSE; -} - -void -nm_device_replace_vpn4_config(NMDevice *self, NMIP4Config *old, NMIP4Config *config) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - nm_assert(!old || NM_IS_IP4_CONFIG(old)); - nm_assert(!config || NM_IS_IP4_CONFIG(config)); - nm_assert(!old || nm_ip4_config_get_ifindex(old) == nm_device_get_ip_ifindex(self)); - nm_assert(!config || nm_ip4_config_get_ifindex(config) == nm_device_get_ip_ifindex(self)); - - if (!_replace_vpn_config_in_list(&priv->vpn_configs_4, (GObject *) old, (GObject *) config)) - return; - - /* NULL to use existing configs */ - if (!ip_config_merge_and_apply(self, AF_INET, TRUE)) - _LOGW(LOGD_IP4, "failed to set VPN routes for device"); -} - -void -nm_device_set_dev2_ip_config(NMDevice *self, int addr_family, NMIPConfig *config) +const NML3ConfigData * +nm_device_get_l3cd(NMDevice *self, gboolean get_commited) { NMDevicePrivate *priv; - const int IS_IPv4 = NM_IS_IPv4(addr_family); - g_return_if_fail(NM_IS_DEVICE(self)); - g_return_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6)); - g_return_if_fail(!config || nm_ip_config_get_addr_family(config) == addr_family); + g_return_val_if_fail(NM_IS_DEVICE(self), NULL); priv = NM_DEVICE_GET_PRIVATE(self); - applied_config_init(&priv->dev2_ip_config_x[IS_IPv4], config); - if (!ip_config_merge_and_apply(self, addr_family, TRUE)) { - _LOGW(LOGD_IP, - "failed to set extra device IPv%c configuration", - nm_utils_addr_family_to_char(addr_family)); - } -} + if (!priv->l3cfg) + return NULL; -void -nm_device_replace_vpn6_config(NMDevice *self, NMIP6Config *old, NMIP6Config *config) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMDeviceState state; - - nm_assert(!old || NM_IS_IP6_CONFIG(old)); - nm_assert(!old || nm_ip6_config_get_ifindex(old) > 0); - nm_assert(!old || nm_device_get_ip_ifindex(self) == 0 - || nm_device_get_ip_ifindex(self) == nm_ip6_config_get_ifindex(old)); - nm_assert(!config || NM_IS_IP6_CONFIG(config)); - nm_assert(!config || nm_ip6_config_get_ifindex(config) > 0); - nm_assert(!config || nm_device_get_ip_ifindex(self) == nm_ip6_config_get_ifindex(config)); - - if (!_replace_vpn_config_in_list(&priv->vpn_configs_6, (GObject *) old, (GObject *) config)) - return; - - state = nm_device_get_state(self); - if (state >= NM_DEVICE_STATE_IP_CONFIG && state <= NM_DEVICE_STATE_ACTIVATED) { - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "failed to set VPN routes for device"); - } -} - -NMIP6Config * -nm_device_get_ip6_config(NMDevice *self) -{ - g_return_val_if_fail(NM_IS_DEVICE(self), NULL); - - return NM_DEVICE_GET_PRIVATE(self)->ip_config_6; + return nm_l3cfg_get_combined_l3cd(priv->l3cfg, get_commited); } /*****************************************************************************/ static gboolean -dispatcher_cleanup(NMDevice *self) +_dispatcher_cleanup(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); @@ -13911,7 +13190,7 @@ dispatcher_cleanup(NMDevice *self) } static void -dispatcher_complete_proceed_state(NMDispatcherCallId *call_id, gpointer user_data) +_dispatcher_complete_proceed_state(NMDispatcherCallId *call_id, gpointer user_data) { NMDevice * self = NM_DEVICE(user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); @@ -13931,7 +13210,7 @@ ip_check_pre_up(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (dispatcher_cleanup(self)) + if (_dispatcher_cleanup(self)) nm_assert_not_reached(); priv->dispatcher.post_state = NM_DEVICE_STATE_SECONDARIES; @@ -13939,11 +13218,11 @@ ip_check_pre_up(NMDevice *self) if (!nm_dispatcher_call_device(NM_DISPATCHER_ACTION_PRE_UP, self, NULL, - dispatcher_complete_proceed_state, + _dispatcher_complete_proceed_state, self, &priv->dispatcher.call_id)) { /* Just proceed on errors */ - dispatcher_complete_proceed_state(0, self); + _dispatcher_complete_proceed_state(0, self); } } @@ -14123,8 +13402,8 @@ nm_device_start_ip_check(NMDevice *self) g_return_if_fail(!priv->gw_ping.watch); g_return_if_fail(!priv->gw_ping.timeout); g_return_if_fail(!priv->gw_ping.pid); - g_return_if_fail(priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE - || priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE); + g_return_if_fail(priv->ip_data_4.state == NM_DEVICE_IP_STATE_READY + || priv->ip_data_6.state == NM_DEVICE_IP_STATE_READY); connection = nm_device_get_applied_connection(self); g_assert(connection); @@ -14135,17 +13414,21 @@ nm_device_start_ip_check(NMDevice *self) buf[0] = '\0'; if (timeout) { - const NMPObject *gw; + const NMPObject * gw; + const NML3ConfigData *l3cd; - if (priv->ip_config_4 && priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE) { - gw = nm_ip4_config_best_default_route_get(priv->ip_config_4); + l3cd = priv->l3cfg ? nm_l3cfg_get_combined_l3cd(priv->l3cfg, TRUE) : NULL; + if (!l3cd) { + /* pass */ + } 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); ping_binary = nm_utils_find_helper("ping", "/usr/bin/ping", NULL); log_domain = LOGD_IP4; } - } else if (priv->ip_config_6 && priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) { - gw = nm_ip6_config_best_default_route_get(priv->ip_config_6); + } 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); ping_binary = nm_utils_find_helper("ping6", "/usr/bin/ping6", NULL); @@ -14285,15 +13568,7 @@ nm_device_bring_up(NMDevice *self, gboolean block, gboolean *no_firmware) /* Can only get HW address of some devices when they are up */ nm_device_update_hw_address(self); - /* when the link comes up, we must restore IP configuration if necessary. */ - if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE) { - if (!ip_config_merge_and_apply(self, AF_INET, TRUE)) - _LOGW(LOGD_IP4, "failed applying IP4 config after bringing link up"); - } - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) { - if (!ip_config_merge_and_apply(self, AF_INET6, TRUE)) - _LOGW(LOGD_IP6, "failed applying IP6 config after bringing link up"); - } + _dev_l3_cfg_commit(self, TRUE); return TRUE; } @@ -14356,381 +13631,6 @@ nm_device_get_firmware_missing(NMDevice *self) return NM_DEVICE_GET_PRIVATE(self)->firmware_missing; } -static void -intersect_ext_config(NMDevice * self, - AppliedConfig *config, - gboolean intersect_addresses, - gboolean intersect_routes) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - NMIPConfig * ext; - guint32 penalty; - int family; - - if (!config->orig) - return; - - family = nm_ip_config_get_addr_family(config->orig); - penalty = default_route_metric_penalty_get(self, family); - ext = family == AF_INET ? (NMIPConfig *) priv->ext_ip_config_4 - : (NMIPConfig *) priv->ext_ip_config_6; - - if (config->current) { - nm_ip_config_intersect(config->current, - ext, - intersect_addresses, - intersect_routes, - penalty); - } else { - config->current = nm_ip_config_intersect_alloc(config->orig, - ext, - intersect_addresses, - intersect_routes, - penalty); - } -} - -static gboolean -update_ext_ip_config(NMDevice *self, int addr_family, gboolean intersect_configs) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - int ifindex; - GSList * iter; - gboolean is_up; - - nm_assert_addr_family(addr_family); - - ifindex = nm_device_get_ip_ifindex(self); - if (!ifindex) - return FALSE; - - is_up = nm_platform_link_is_up(nm_device_get_platform(self), ifindex); - - if (NM_IS_IPv4(addr_family)) { - g_clear_object(&priv->ext_ip_config_4); - priv->ext_ip_config_4 = nm_ip4_config_capture(nm_device_get_multi_index(self), - nm_device_get_platform(self), - ifindex); - if (priv->ext_ip_config_4) { - if (intersect_configs) { - /* This function was called upon external changes. Remove the configuration - * (addresses,routes) that is no longer present externally from the internal - * config. This way, we don't re-add addresses that were manually removed - * by the user. */ - if (priv->con_ip_config_4) { - nm_ip4_config_intersect(priv->con_ip_config_4, - priv->ext_ip_config_4, - TRUE, - is_up, - default_route_metric_penalty_get(self, AF_INET)); - } - - intersect_ext_config(self, &priv->dev_ip_config_4, TRUE, is_up); - intersect_ext_config(self, &priv->dev2_ip_config_4, TRUE, is_up); - - for (iter = priv->vpn_configs_4; iter; iter = iter->next) - nm_ip4_config_intersect(iter->data, priv->ext_ip_config_4, TRUE, is_up, 0); - } - - /* Remove parts from ext_ip_config_4 to only contain the information that - * was configured externally -- we already have the same configuration from - * internal origins. */ - if (priv->con_ip_config_4) { - nm_ip4_config_subtract(priv->ext_ip_config_4, - priv->con_ip_config_4, - default_route_metric_penalty_get(self, AF_INET)); - } - if (applied_config_get_current(&priv->dev_ip_config_4)) { - nm_ip_config_subtract((NMIPConfig *) priv->ext_ip_config_4, - applied_config_get_current(&priv->dev_ip_config_4), - default_route_metric_penalty_get(self, AF_INET)); - } - if (applied_config_get_current(&priv->dev2_ip_config_4)) { - nm_ip_config_subtract((NMIPConfig *) priv->ext_ip_config_4, - applied_config_get_current(&priv->dev2_ip_config_4), - default_route_metric_penalty_get(self, AF_INET)); - } - for (iter = priv->vpn_configs_4; iter; iter = iter->next) - nm_ip4_config_subtract(priv->ext_ip_config_4, iter->data, 0); - } - - } else { - nm_assert(!NM_IS_IPv4(addr_family)); - - g_clear_object(&priv->ext_ip_config_6); - g_clear_object(&priv->ext_ip6_config_captured); - priv->ext_ip6_config_captured = - nm_ip6_config_capture(nm_device_get_multi_index(self), - nm_device_get_platform(self), - ifindex, - NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - if (priv->ext_ip6_config_captured) { - priv->ext_ip_config_6 = nm_ip6_config_new_cloned(priv->ext_ip6_config_captured); - - if (intersect_configs) { - /* This function was called upon external changes. Remove the configuration - * (addresses,routes) that is no longer present externally from the internal - * config. This way, we don't re-add addresses that were manually removed - * by the user. */ - if (priv->con_ip_config_6) { - nm_ip6_config_intersect(priv->con_ip_config_6, - priv->ext_ip_config_6, - is_up, - is_up, - default_route_metric_penalty_get(self, AF_INET6)); - } - - intersect_ext_config(self, &priv->ac_ip6_config, is_up, is_up); - intersect_ext_config(self, &priv->dhcp6.ip6_config, is_up, is_up); - intersect_ext_config(self, &priv->dev2_ip_config_6, is_up, is_up); - - for (iter = priv->vpn_configs_6; iter; iter = iter->next) - nm_ip6_config_intersect(iter->data, priv->ext_ip_config_6, is_up, is_up, 0); - - if (is_up && priv->ipv6ll_has - && !nm_ip6_config_lookup_address(priv->ext_ip_config_6, &priv->ipv6ll_addr)) - priv->ipv6ll_has = FALSE; - } - - /* Remove parts from ext_ip_config_6 to only contain the information that - * was configured externally -- we already have the same configuration from - * internal origins. */ - if (priv->con_ip_config_6) { - nm_ip6_config_subtract(priv->ext_ip_config_6, - priv->con_ip_config_6, - default_route_metric_penalty_get(self, AF_INET6)); - } - if (applied_config_get_current(&priv->ac_ip6_config)) { - nm_ip_config_subtract((NMIPConfig *) priv->ext_ip_config_6, - applied_config_get_current(&priv->ac_ip6_config), - default_route_metric_penalty_get(self, AF_INET6)); - } - if (applied_config_get_current(&priv->dhcp6.ip6_config)) { - nm_ip_config_subtract((NMIPConfig *) priv->ext_ip_config_6, - applied_config_get_current(&priv->dhcp6.ip6_config), - default_route_metric_penalty_get(self, AF_INET6)); - } - if (applied_config_get_current(&priv->dev2_ip_config_6)) { - nm_ip_config_subtract((NMIPConfig *) priv->ext_ip_config_6, - applied_config_get_current(&priv->dev2_ip_config_6), - default_route_metric_penalty_get(self, AF_INET6)); - } - for (iter = priv->vpn_configs_6; iter; iter = iter->next) - nm_ip6_config_subtract(priv->ext_ip_config_6, iter->data, 0); - } - } - - return TRUE; -} - -static void -update_ip_config(NMDevice *self, int addr_family) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - nm_assert_addr_family(addr_family); - - if (NM_IS_IPv4(addr_family)) - priv->update_ip_config_completed_v4 = TRUE; - else - priv->update_ip_config_completed_v6 = TRUE; - - if (update_ext_ip_config(self, addr_family, TRUE)) { - if (NM_IS_IPv4(addr_family)) { - if (priv->ext_ip_config_4) - ip_config_merge_and_apply(self, AF_INET, FALSE); - } else { - if (priv->ext_ip6_config_captured) - ip_config_merge_and_apply(self, AF_INET6, FALSE); - } - } -} - -void -nm_device_capture_initial_config(NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - if (!priv->update_ip_config_completed_v4) - update_ip_config(self, AF_INET); - if (!priv->update_ip_config_completed_v6) - update_ip_config(self, AF_INET6); -} - -static gboolean -queued_ip_config_change(NMDevice *self, int addr_family) -{ - NMDevicePrivate *priv; - const int IS_IPv4 = NM_IS_IPv4(addr_family); - - g_return_val_if_fail(NM_IS_DEVICE(self), G_SOURCE_REMOVE); - - priv = NM_DEVICE_GET_PRIVATE(self); - - /* Wait for any queued state changes */ - if (priv->queued_state.id) - return G_SOURCE_CONTINUE; - - /* If a commit is scheduled, this function would potentially interfere with - * it changing IP configurations before they are applied. Postpone the - * update in such case. - */ - if (priv->activation_source_id_x[IS_IPv4] != 0 - && priv->activation_source_func_x[IS_IPv4] - == activate_stage5_ip_config_result_x_fcn(addr_family)) - return G_SOURCE_CONTINUE; - - priv->queued_ip_config_id_x[IS_IPv4] = 0; - - update_ip_config(self, addr_family); - - if (!IS_IPv4) { - /* Check whether we need to complete waiting for link-local. - * We are also called from an idle handler, so no problem doing state transitions - * now. */ - linklocal6_check_complete(self); - } - - if (!IS_IPv4) { - NMPlatform * platform; - GSList * dad6_failed_addrs, *iter; - const NMPlatformLink *pllink; - - dad6_failed_addrs = g_steal_pointer(&priv->dad6_failed_addrs); - - if (priv->state > NM_DEVICE_STATE_DISCONNECTED && priv->state < NM_DEVICE_STATE_DEACTIVATING - && priv->ifindex > 0 && !nm_device_sys_iface_state_is_external(self) - && (platform = nm_device_get_platform(self)) - && (pllink = nm_platform_link_get(platform, priv->ifindex)) - && (pllink->n_ifi_flags & IFF_UP)) { - gboolean need_ipv6ll = FALSE; - NMNDiscConfigMap ndisc_config_changed = NM_NDISC_CONFIG_NONE; - - /* Handle DAD failures */ - for (iter = dad6_failed_addrs; iter; iter = iter->next) { - const NMPObject * obj = iter->data; - const NMPlatformIP6Address *addr; - - if (!nm_ndisc_dad_addr_is_fail_candidate(platform, obj)) - continue; - - addr = NMP_OBJECT_CAST_IP6_ADDRESS(obj); - - _LOGI(LOGD_IP6, - "ipv6: duplicate address check failed for the %s address", - nm_platform_ip6_address_to_string(addr, NULL, 0)); - - if (IN6_IS_ADDR_LINKLOCAL(&addr->address)) - need_ipv6ll = TRUE; - else if (priv->ndisc) - ndisc_config_changed |= nm_ndisc_dad_failed(priv->ndisc, &addr->address, FALSE); - } - - if (ndisc_config_changed != NM_NDISC_CONFIG_NONE) - nm_ndisc_emit_config_change(priv->ndisc, ndisc_config_changed); - - /* If no IPv6 link-local address exists but other addresses do then we - * must add the LL address to remain conformant with RFC 3513 chapter 2.1 - * ("Addressing Model"): "All interfaces are required to have at least - * one link-local unicast address". - */ - if (priv->ip_config_6 && nm_ip6_config_get_num_addresses(priv->ip_config_6)) - need_ipv6ll = TRUE; - if (need_ipv6ll) - check_and_add_ipv6ll_addr(self); - } - - g_slist_free_full(dad6_failed_addrs, (GDestroyNotify) nmp_object_unref); - } - - if (!IS_IPv4) { - /* Check if DAD is still pending */ - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF && priv->dad6_ip6_config - && priv->ext_ip6_config_captured - && !nm_ip6_config_has_any_dad_pending(priv->ext_ip6_config_captured, - priv->dad6_ip6_config)) { - _LOGD(LOGD_DEVICE | LOGD_IP6, "IPv6 DAD terminated"); - g_clear_object(&priv->dad6_ip6_config); - _set_ip_state(self, addr_family, NM_DEVICE_IP_STATE_DONE); - check_ip_state(self, FALSE, TRUE); - if (priv->rt6_temporary_not_available) - nm_device_activate_schedule_ip_config_result(self, AF_INET6, NULL); - } - } - - set_unmanaged_external_down(self, TRUE); - - return G_SOURCE_REMOVE; -} - -static gboolean -queued_ip4_config_change(gpointer user_data) -{ - return queued_ip_config_change(user_data, AF_INET); -} - -static gboolean -queued_ip6_config_change(gpointer user_data) -{ - return queued_ip_config_change(user_data, AF_INET6); -} - -static void -device_ipx_changed(NMPlatform * platform, - int obj_type_i, - int ifindex, - gconstpointer platform_object, - int change_type_i, - NMDevice * self) -{ - const NMPObjectType obj_type = obj_type_i; - const NMPlatformSignalChangeType change_type = change_type_i; - NMDevicePrivate * priv; - const NMPlatformIP6Address * addr; - - if (nm_device_get_ip_ifindex(self) != ifindex) - return; - - if (!nm_device_is_real(self)) - return; - - if (nm_device_get_unmanaged_flags(self, NM_UNMANAGED_PLATFORM_INIT)) { - /* ignore all platform signals until the link is initialized in platform. */ - return; - } - - priv = NM_DEVICE_GET_PRIVATE(self); - - switch (obj_type) { - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP4_ROUTE: - if (!priv->queued_ip_config_id_4) { - priv->queued_ip_config_id_4 = g_idle_add(queued_ip4_config_change, self); - _LOGD(LOGD_DEVICE, "queued IP4 config change"); - } - break; - case NMP_OBJECT_TYPE_IP6_ADDRESS: - addr = platform_object; - - if (priv->state > NM_DEVICE_STATE_DISCONNECTED && priv->state < NM_DEVICE_STATE_DEACTIVATING - && nm_ndisc_dad_addr_is_fail_candidate_event(change_type, addr)) { - priv->dad6_failed_addrs = - g_slist_prepend(priv->dad6_failed_addrs, - (gpointer) nmp_object_ref(NMP_OBJECT_UP_CAST(addr))); - } - - /* fall-through */ - case NMP_OBJECT_TYPE_IP6_ROUTE: - if (!priv->queued_ip_config_id_6) { - priv->queued_ip_config_id_6 = g_idle_add(queued_ip6_config_change, self); - _LOGD(LOGD_DEVICE, "queued IP6 config change"); - } - break; - default: - g_return_if_reached(); - } -} - /*****************************************************************************/ NM_UTILS_FLAGS2STR_DEFINE(nm_unmanaged_flags2str, @@ -14983,12 +13883,6 @@ _set_unmanaged_flags(NMDevice * self, nm_device_set_unmanaged_flags(self, NM_UNMANAGED_USER_SETTINGS, !!unmanaged); } - /* trigger an initial update of IP configuration. */ - nm_assert_se(!nm_clear_g_source(&priv->queued_ip_config_id_4)); - nm_assert_se(!nm_clear_g_source(&priv->queued_ip_config_id_6)); - priv->queued_ip_config_id_4 = g_idle_add(queued_ip4_config_change, self); - priv->queued_ip_config_id_6 = g_idle_add(queued_ip6_config_change, self); - if (priv->pending_actions.len == 0) { do_notify_has_pending_actions = TRUE; had_pending_actions = nm_device_has_pending_action(self); @@ -15340,9 +14234,13 @@ nm_device_update_metered(NMDevice *self) /* Try to guess a value using the metered flag in IP configuration */ if (value == NM_METERED_INVALID) { - if (priv->ip_config_4 && priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE - && nm_ip4_config_get_metered(priv->ip_config_4)) - value = NM_METERED_GUESS_YES; + if (priv->l3cfg) { + const NML3ConfigData *l3cd; + + l3cd = nm_l3cfg_get_combined_l3cd(priv->l3cfg, TRUE); + if (l3cd && nm_l3_config_data_get_metered(l3cd) == NM_TERNARY_TRUE) + value = NM_METERED_GUESS_YES; + } } /* Otherwise, look at connection type. For Bluetooth, we look at the type of @@ -15913,12 +14811,11 @@ _cancel_activation(NMDevice *self) priv->fw_state = FIREWALL_STATE_INITIALIZED; } - dispatcher_cleanup(self); + _dispatcher_cleanup(self); ip_check_gw_ping_cleanup(self); /* Break the activation chain */ - activation_source_clear(self, AF_INET); - activation_source_clear(self, AF_INET6); + activation_source_clear(self); } static void @@ -15948,17 +14845,13 @@ _cleanup_generic_pre(NMDevice *self, CleanupType cleanup_type) queued_state_clear(self); - nm_clear_pointer(&priv->shared_ip_handle, nm_netns_shared_ip_release); - for (i = 0; i < 2; i++) nm_clear_pointer(&priv->hostname_resolver_x[i], _hostname_resolver_free); _cleanup_ip_pre(self, AF_INET, cleanup_type); _cleanup_ip_pre(self, AF_INET6, cleanup_type); - priv->ip_config_started = FALSE; - nm_clear_g_source_inst(&priv->ip_req_timeout_source_4); - nm_clear_g_source_inst(&priv->ip_req_timeout_source_6); + _dev_ip_state_req_timeout_cancel(self, AF_UNSPEC); } static void @@ -15966,11 +14859,9 @@ _cleanup_generic_post(NMDevice *self, CleanupType cleanup_type) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - priv->v4_commit_first_time = TRUE; - priv->v6_commit_first_time = TRUE; - priv->v4_route_table_initialized = FALSE; priv->v6_route_table_initialized = FALSE; + priv->l3config_merge_flags_has = FALSE; priv->v4_route_table_all_sync_before = FALSE; priv->v6_route_table_all_sync_before = FALSE; @@ -15978,42 +14869,9 @@ _cleanup_generic_post(NMDevice *self, CleanupType cleanup_type) priv->default_route_metric_penalty_ip4_has = FALSE; priv->default_route_metric_penalty_ip6_has = FALSE; - priv->linklocal6_dad_counter = 0; - priv->mtu_force_set_done = FALSE; - /* Clean up IP configs; this does not actually deconfigure the - * interface; the caller must flush routes and addresses explicitly. - */ - nm_device_set_ip_config(self, AF_INET, NULL, TRUE, NULL); - nm_device_set_ip_config(self, AF_INET6, NULL, TRUE, NULL); - g_clear_object(&priv->proxy_config); - g_clear_object(&priv->con_ip_config_4); - applied_config_clear(&priv->dev_ip_config_4); - applied_config_clear(&priv->dev2_ip_config_4); - g_clear_object(&priv->ext_ip_config_4); - g_clear_object(&priv->ip_config_4); - g_clear_object(&priv->con_ip_config_6); - applied_config_clear(&priv->ac_ip6_config); - g_clear_object(&priv->ext_ip_config_6); - g_clear_object(&priv->ext_ip6_config_captured); - applied_config_clear(&priv->dev2_ip_config_6); - g_clear_object(&priv->ip_config_6); - g_clear_object(&priv->dad6_ip6_config); - priv->ipv6ll_has = FALSE; - memset(&priv->ipv6ll_addr, 0, sizeof(priv->ipv6ll_addr)); - - nm_clear_pointer(&priv->rt6_temporary_not_available, g_hash_table_unref); - nm_clear_g_source(&priv->rt6_temporary_not_available_id); - - g_slist_free_full(priv->vpn_configs_4, g_object_unref); - priv->vpn_configs_4 = NULL; - g_slist_free_full(priv->vpn_configs_6, g_object_unref); - priv->vpn_configs_6 = NULL; - - /* We no longer accept the delegations. nm_device_set_ip_config(NULL) - * above disables them. */ - nm_assert(priv->needs_ip6_subnet == FALSE); + priv->needs_ip6_subnet = FALSE; if (priv->act_request.obj) { nm_active_connection_set_default(NM_ACTIVE_CONNECTION(priv->act_request.obj), @@ -16035,6 +14893,9 @@ _cleanup_generic_post(NMDevice *self, CleanupType cleanup_type) * or ATM device). */ _set_ip_ifindex(self, 0, NULL); + + nm_clear_g_source_inst(&priv->ip_data_4.check_async_source); + nm_clear_g_source_inst(&priv->ip_data_6.check_async_source); } /* @@ -16066,7 +14927,7 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu /* Turn off kernel IPv6 */ if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { - set_disable_ipv6(self, "1"); + _dev_sysctl_set_disable_ipv6(self, TRUE); nm_device_sysctl_ip_conf_set(self, AF_INET6, "use_tempaddr", "0"); } @@ -16180,213 +15041,13 @@ deactivate_reset_hw_addr(NMDevice *self) nm_device_hw_addr_reset(self, "deactivate"); } -static char * -find_dhcp4_address(NMDevice *self) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - const NMPlatformIP4Address *a; - NMDedupMultiIter ipconf_iter; - - if (!priv->ip_config_4) - return NULL; - - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, priv->ip_config_4, &a) { - if (a->addr_source == NM_IP_CONFIG_SOURCE_DHCP) - return nm_utils_inet4_ntop_dup(a->address); - } - return NULL; -} - -void -nm_device_spawn_iface_helper(NMDevice *self) -{ - NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); - gboolean configured = FALSE; - NMConnection * connection; - GError * error = NULL; - const char * method; - GPtrArray * argv; - gs_free char * dhcp4_address = NULL; - char * logging_backend; - NMUtilsStableType stable_type; - const char * stable_id; - - if (priv->state != NM_DEVICE_STATE_ACTIVATED) - return; - if (!nm_device_can_assume_connections(self)) - return; - - connection = nm_device_get_applied_connection(self); - - g_return_if_fail(connection); - - argv = g_ptr_array_sized_new(10); - g_ptr_array_set_free_func(argv, g_free); - - g_ptr_array_add(argv, g_strdup(LIBEXECDIR "/nm-iface-helper")); - g_ptr_array_add(argv, g_strdup("--ifname")); - g_ptr_array_add(argv, g_strdup(nm_device_get_ip_iface(self))); - g_ptr_array_add(argv, g_strdup("--uuid")); - g_ptr_array_add(argv, g_strdup(nm_connection_get_uuid(connection))); - - stable_id = _prop_get_connection_stable_id(self, connection, &stable_type); - if (stable_type != NM_UTILS_STABLE_TYPE_UUID) { - g_ptr_array_add(argv, g_strdup("--stable-id")); - g_ptr_array_add(argv, g_strdup_printf("%d %s", (int) stable_type, stable_id)); - } - - logging_backend = - nm_config_data_get_value(NM_CONFIG_GET_DATA_ORIG, - NM_CONFIG_KEYFILE_GROUP_LOGGING, - NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND, - NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); - if (logging_backend) { - g_ptr_array_add(argv, g_strdup("--logging-backend")); - g_ptr_array_add(argv, logging_backend); - } - - g_ptr_array_add(argv, g_strdup("--log-level")); - g_ptr_array_add(argv, g_strdup(nm_logging_level_to_string())); - - g_ptr_array_add(argv, g_strdup("--log-domains")); - g_ptr_array_add(argv, g_strdup(nm_logging_domains_to_string())); - - dhcp4_address = find_dhcp4_address(self); - - method = nm_device_get_effective_ip_config_method(self, AF_INET); - if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { - NMSettingIPConfig *s_ip4; - - s_ip4 = nm_connection_get_setting_ip4_config(connection); - nm_assert(s_ip4); - - g_ptr_array_add(argv, g_strdup("--priority4")); - g_ptr_array_add(argv, g_strdup_printf("%u", nm_device_get_route_metric(self, AF_INET))); - - g_ptr_array_add(argv, g_strdup("--dhcp4")); - g_ptr_array_add(argv, g_strdup(dhcp4_address)); - if (nm_setting_ip_config_get_may_fail(s_ip4) == FALSE) - g_ptr_array_add(argv, g_strdup("--dhcp4-required")); - - if (priv->dhcp_data_4.client) { - const char *hostname; - GBytes * client_id; - - client_id = nm_dhcp_client_get_client_id(priv->dhcp_data_4.client); - if (client_id) { - g_ptr_array_add(argv, g_strdup("--dhcp4-clientid")); - g_ptr_array_add(argv, - nm_utils_bin2hexstr_full(g_bytes_get_data(client_id, NULL), - g_bytes_get_size(client_id), - ':', - FALSE, - NULL)); - } - - hostname = nm_dhcp_client_get_hostname(priv->dhcp_data_4.client); - if (hostname) { - if (NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(priv->dhcp_data_4.client), - NM_DHCP_CLIENT_FLAGS_USE_FQDN)) - g_ptr_array_add(argv, g_strdup("--dhcp4-fqdn")); - else - g_ptr_array_add(argv, g_strdup("--dhcp4-hostname")); - g_ptr_array_add(argv, g_strdup(hostname)); - } - } - - configured = TRUE; - } - - method = nm_utils_get_ip_config_method(connection, AF_INET6); - if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { - NMSettingIPConfig *s_ip6; - NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT; - - s_ip6 = nm_connection_get_setting_ip6_config(connection); - g_assert(s_ip6); - - g_ptr_array_add(argv, g_strdup("--priority6")); - g_ptr_array_add(argv, g_strdup_printf("%u", nm_device_get_route_metric(self, AF_INET6))); - - g_ptr_array_add(argv, g_strdup("--slaac")); - - if (nm_setting_ip_config_get_may_fail(s_ip6) == FALSE) - g_ptr_array_add(argv, g_strdup("--slaac-required")); - - g_ptr_array_add(argv, g_strdup("--slaac-tempaddr")); - g_ptr_array_add(argv, g_strdup_printf("%d", priv->ndisc_use_tempaddr)); - - if (nm_device_get_ip_iface_identifier(self, &iid, FALSE)) { - g_ptr_array_add(argv, g_strdup("--iid")); - g_ptr_array_add( - argv, - nm_utils_bin2hexstr_full(iid.id_u8, sizeof(NMUtilsIPv6IfaceId), ':', FALSE, NULL)); - } - - g_ptr_array_add(argv, g_strdup("--addr-gen-mode")); - g_ptr_array_add( - argv, - g_strdup_printf("%d", - nm_setting_ip6_config_get_addr_gen_mode(NM_SETTING_IP6_CONFIG(s_ip6)))); - - configured = TRUE; - } - - if (configured) { - GPid pid; - - g_ptr_array_add(argv, NULL); - - if (nm_logging_enabled(LOGL_DEBUG, LOGD_DEVICE)) { - char *tmp; - - tmp = g_strjoinv(" ", (char **) argv->pdata); - _LOGD(LOGD_DEVICE, "running '%s'", tmp); - g_free(tmp); - } - - if (g_spawn_async(NULL, - (char **) argv->pdata, - NULL, - G_SPAWN_DO_NOT_REAP_CHILD, - NULL, - NULL, - &pid, - &error)) { - _LOGI(LOGD_DEVICE, "spawned helper PID %u", (guint) pid); - } else { - _LOGW(LOGD_DEVICE, "failed to spawn helper: %s", error->message); - g_error_free(error); - } - } - - g_ptr_array_unref(argv); -} - /*****************************************************************************/ -static gboolean -ip_config_valid(NMDeviceState state) -{ - return (state == NM_DEVICE_STATE_UNMANAGED) - || (state >= NM_DEVICE_STATE_IP_CHECK && state <= NM_DEVICE_STATE_DEACTIVATING); -} - -static void -notify_ip_properties(NMDevice *self) -{ - _notify(self, PROP_IP_IFACE); - _notify(self, PROP_IP4_CONFIG); - _notify(self, PROP_DHCP4_CONFIG); - _notify(self, PROP_IP6_CONFIG); - _notify(self, PROP_DHCP6_CONFIG); -} - static void ip6_managed_setup(NMDevice *self) { - set_nm_ipv6ll(self, TRUE); - set_disable_ipv6(self, "1"); + _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_NONE); + _dev_sysctl_set_disable_ipv6(self, TRUE); nm_device_sysctl_ip_conf_set(self, AF_INET6, "accept_ra", "0"); nm_device_sysctl_ip_conf_set(self, AF_INET6, "use_tempaddr", "0"); nm_device_sysctl_ip_conf_set(self, AF_INET6, "forwarding", "0"); @@ -16520,9 +15181,9 @@ deactivate_dispatcher_complete(NMDispatcherCallId *call_id, gpointer user_data) static void _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, gboolean quitting) { - NMDevicePrivate *priv; - NMDeviceState old_state; - gs_unref_object NMActRequest *req = NULL; + gs_unref_object NMActRequest *req = NULL; + NMDevicePrivate * priv; + NMDeviceState old_state; gboolean no_firmware = FALSE; NMSettingsConnection * sett_conn; NMSettingSriov * s_sriov; @@ -16532,8 +15193,7 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, priv = NM_DEVICE_GET_PRIVATE(self); - /* Track re-entry */ - g_warn_if_fail(priv->in_state_changed == FALSE); + g_return_if_fail(priv->in_state_changed == 0); old_state = priv->state; @@ -16570,14 +15230,14 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, * by the device not having any pending action anymore * we add one here that gets removed at the end of the function */ nm_device_add_pending_action(self, NM_PENDING_ACTION_IN_STATE_CHANGE, TRUE); - priv->in_state_changed = TRUE; + priv->in_state_changed++; priv->state = state; priv->state_reason = reason; queued_state_clear(self); - dispatcher_cleanup(self); + _dispatcher_cleanup(self); nm_clear_g_cancellable(&priv->deactivating_cancellable); @@ -16634,15 +15294,15 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, nm_device_cleanup(self, reason, CLEANUP_TYPE_DECONFIGURE); nm_device_take_down(self, TRUE); nm_device_hw_addr_reset(self, "unmanage"); - set_nm_ipv6ll(self, FALSE); - restore_ip6_properties(self); + _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_EUI64); + _dev_sysctl_restore_ip6_properties(self); } } nm_device_sys_iface_state_set(self, NM_DEVICE_SYS_IFACE_STATE_EXTERNAL); break; case NM_DEVICE_STATE_UNAVAILABLE: if (old_state == NM_DEVICE_STATE_UNMANAGED) { - save_ip6_properties(self); + _dev_sysctl_save_ip6_properties(self); if (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED) ip6_managed_setup(self); device_init_static_sriov_num_vfs(self); @@ -16671,7 +15331,7 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, /* Ensure devices that previously assumed a connection now have * userspace IPv6LL enabled. */ - set_nm_ipv6ll(self, TRUE); + _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_NONE); nm_device_cleanup(self, reason, CLEANUP_TYPE_DECONFIGURE); } else if (old_state < NM_DEVICE_STATE_DISCONNECTED) { @@ -16784,9 +15444,7 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, _LOGI(LOGD_DEVICE, "Activation: successful, device activated."); nm_device_update_metered(self); nm_dispatcher_call_device(NM_DISPATCHER_ACTION_UP, self, req, NULL, NULL, NULL); - - if (priv->proxy_config) - _pacrunner_manager_add(self); + _pacrunner_manager_add(self); break; case NM_DEVICE_STATE_FAILED: /* Usually upon failure the activation chain is interrupted in @@ -16846,11 +15504,6 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, fw_change_zone(self); } else nm_device_start_ip_check(self); - - /* IP-related properties are only valid when the device has IP configuration; - * now that it does, ensure their change notifications are emitted. - */ - notify_ip_properties(self); break; } case NM_DEVICE_STATE_SECONDARIES: @@ -16873,18 +15526,13 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, } } - /* IP-related properties are only valid when the device has IP configuration. - * If it no longer does, ensure their change notifications are emitted. - */ - if (ip_config_valid(old_state) && !ip_config_valid(state)) - notify_ip_properties(self); - concheck_now = NM_IN_SET(state, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_DISCONNECTED) || old_state >= NM_DEVICE_STATE_ACTIVATED; concheck_update_interval(self, AF_INET, concheck_now); concheck_update_interval(self, AF_INET6, concheck_now); - priv->in_state_changed = FALSE; + priv->in_state_changed--; + nm_device_remove_pending_action(self, NM_PENDING_ACTION_IN_STATE_CHANGE, TRUE); if ((old_state > NM_DEVICE_STATE_UNMANAGED) != (state > NM_DEVICE_STATE_UNMANAGED)) @@ -17906,15 +16554,16 @@ nm_device_clear_dns_lookup_data(NMDevice *self) const char * nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean *out_wait) { - NMDevicePrivate * priv; const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDevicePrivate * priv; HostnameResolver *resolver; - NMIPConfig * ip_config; const char * method; + int ifindex; gboolean address_changed = FALSE; gs_unref_object GInetAddress *new_address = NULL; g_return_val_if_fail(NM_IS_DEVICE(self), NULL); + priv = NM_DEVICE_GET_PRIVATE(self); /* If the device is not supposed to have addresses, @@ -17954,38 +16603,28 @@ nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean /* Determine the most suitable address of the interface * and whether it changed from the previous lookup */ - ip_config = priv->ip_config_x[IS_IPv4]; - if (ip_config) { - const NMPlatformIPAddress *addr = NULL; + ifindex = nm_device_get_ip_ifindex(self); + if (ifindex > 0) { + NMPLookup lookup; + const NMDedupMultiHeadEntry *head_entry; + const NMDedupMultiEntry * iter; - if (IS_IPv4) { - addr = nm_ip_config_get_first_address(ip_config); - } else { - /* For IPv6 prefer, in order: - * - !link-local, !deprecated - * - !link-local, deprecated - * - link-local - */ - addr = nm_ip_config_find_first_address(ip_config, - NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL); - if (!addr) { - addr = nm_ip_config_find_first_address( - ip_config, - NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED); - } - if (!addr) { - addr = nm_ip_config_find_first_address(ip_config, - NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY); - } - } + /* FIXME(l3cfg): now we lookup the address from platform. Should we instead look + * it up from NML3Cfg? That is, take an address that we want to configure as + * opposed to an address that is configured? */ + head_entry = nm_platform_lookup( + nm_device_get_platform(self), + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex)); - if (addr) { - new_address = g_inet_address_new_from_bytes(addr->address_ptr, - IS_IPv4 ? G_SOCKET_FAMILY_IPV4 - : G_SOCKET_FAMILY_IPV6); + 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); + + new_address = g_inet_address_new_from_bytes(addr->address_ptr, + IS_IPv4 ? G_SOCKET_FAMILY_IPV4 + : G_SOCKET_FAMILY_IPV6); + break; + } } } @@ -18060,11 +16699,7 @@ _activation_func_to_string(ActivationHandleFunc func) G_STMT_END FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage1_device_prepare); FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage2_device_config); - FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage3_ip_config_start); - FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage4_ip_config_timeout_4); - FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage4_ip_config_timeout_6); - FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage5_ip_config_result_4); - FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage5_ip_config_result_6); + FUNC_TO_STRING_CHECK_AND_RETURN(func, activate_stage3_ip_config); g_return_val_if_reached("unknown"); } @@ -18127,13 +16762,10 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) nm_utils_str_utf8safe_escape_cp(priv->iface, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)); break; case PROP_IP_IFACE: - if (ip_config_valid(priv->state)) { - g_value_take_string( - value, - nm_utils_str_utf8safe_escape_cp(nm_device_get_ip_iface(self), - NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)); - } else - g_value_set_string(value, NULL); + g_value_take_string( + value, + nm_utils_str_utf8safe_escape_cp(nm_device_get_ip_iface(self), + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)); break; case PROP_IFINDEX: g_value_set_int(value, priv->ifindex); @@ -18168,24 +16800,16 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) g_value_set_uint(value, priv->mtu); break; case PROP_IP4_CONFIG: - nm_dbus_utils_g_value_set_object_path(value, - ip_config_valid(priv->state) ? priv->ip_config_4 - : NULL); + nm_dbus_utils_g_value_set_object_path(value, priv->l3ipdata_4.ip_config); break; case PROP_DHCP4_CONFIG: - nm_dbus_utils_g_value_set_object_path( - value, - ip_config_valid(priv->state) ? priv->dhcp_data_4.config : NULL); + nm_dbus_utils_g_value_set_object_path(value, priv->ipdhcp_data_4.config); break; case PROP_IP6_CONFIG: - nm_dbus_utils_g_value_set_object_path(value, - ip_config_valid(priv->state) ? priv->ip_config_6 - : NULL); + nm_dbus_utils_g_value_set_object_path(value, priv->l3ipdata_6.ip_config); break; case PROP_DHCP6_CONFIG: - nm_dbus_utils_g_value_set_object_path( - value, - ip_config_valid(priv->state) ? priv->dhcp_data_6.config : NULL); + nm_dbus_utils_g_value_set_object_path(value, priv->ipdhcp_data_6.config); break; case PROP_STATE: g_value_set_uint(value, priv->state); @@ -18392,6 +17016,8 @@ nm_device_init(NMDevice *self) c_list_init(&self->devices_lst); c_list_init(&priv->slaves); + priv->ipdhcp_data_6.v6.mode = NM_NDISC_DHCP_LEVEL_NONE; + priv->concheck_x[0].state = NM_CONNECTIVITY_UNKNOWN; priv->concheck_x[1].state = NM_CONNECTIVITY_UNKNOWN; @@ -18417,9 +17043,6 @@ nm_device_init(NMDevice *self) priv->ip6_saved_properties = g_hash_table_new_full(nm_str_hash, g_str_equal, NULL, g_free); priv->sys_iface_state_ = NM_DEVICE_SYS_IFACE_STATE_EXTERNAL; - priv->v4_commit_first_time = TRUE; - priv->v6_commit_first_time = TRUE; - priv->promisc_reset = NM_OPTION_BOOL_DEFAULT; } @@ -18476,24 +17099,7 @@ constructed(GObject *object) if (NM_DEVICE_GET_CLASS(self)->get_generic_capabilities) priv->capabilities |= NM_DEVICE_GET_CLASS(self)->get_generic_capabilities(self); - /* Watch for external IP config changes */ platform = nm_device_get_platform(self); - g_signal_connect(platform, - NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, - G_CALLBACK(device_ipx_changed), - self); - g_signal_connect(platform, - NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - G_CALLBACK(device_ipx_changed), - self); - g_signal_connect(platform, - NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - G_CALLBACK(device_ipx_changed), - self); - g_signal_connect(platform, - NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, - G_CALLBACK(device_ipx_changed), - self); g_signal_connect(platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK(link_changed_cb), self); priv->manager = g_object_ref(NM_MANAGER_GET); @@ -18545,15 +17151,12 @@ dispose(GObject *object) _parent_set_ifindex(self, 0, FALSE); platform = nm_device_get_platform(self); - g_signal_handlers_disconnect_by_func(platform, G_CALLBACK(device_ipx_changed), self); g_signal_handlers_disconnect_by_func(platform, G_CALLBACK(link_changed_cb), self); - arp_cleanup(self); - nm_clear_g_signal_handler(nm_config_get(), &priv->config_changed_id); nm_clear_g_signal_handler(priv->manager, &priv->ifindex_changed_id); - dispatcher_cleanup(self); + _dispatcher_cleanup(self); nm_pacrunner_manager_remove_clear(&priv->pacrunner_conf_id); @@ -18562,7 +17165,7 @@ dispose(GObject *object) nm_assert(c_list_is_empty(&priv->slaves)); /* Let the kernel manage IPv6LL again */ - set_nm_ipv6ll(self, FALSE); + _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_EUI64); _cleanup_generic_post(self, CLEANUP_TYPE_KEEP); @@ -18609,6 +17212,10 @@ dispose(GObject *object) priv->sriov.next = NULL; } + g_clear_object(&priv->l3cfg); + g_clear_object(&priv->l3ipdata_4.ip_config); + g_clear_object(&priv->l3ipdata_6.ip_config); + G_OBJECT_CLASS(nm_device_parent_class)->dispose(object); if (nm_clear_g_source(&priv->queued_state.id)) { @@ -18631,7 +17238,6 @@ finalize(GObject *object) g_free(priv->hw_addr_perm); g_free(priv->hw_addr_initial); g_free(priv->pending_actions.arr); - g_slist_free_full(priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref); nm_clear_g_free(&priv->physical_port_id); g_free(priv->udi); g_free(priv->path); @@ -18812,10 +17418,9 @@ nm_device_class_init(NMDeviceClass *klass) klass->link_changed = link_changed; - klass->is_available = is_available; - klass->act_stage2_config = act_stage2_config; - klass->act_stage3_ip_config_start = act_stage3_ip_config_start; - klass->act_stage4_ip_config_timeout = act_stage4_ip_config_timeout; + klass->is_available = is_available; + klass->act_stage2_config = act_stage2_config; + klass->get_ip_method_auto = get_ip_method_auto; klass->get_type_description = get_type_description; klass->can_auto_connect = can_auto_connect; @@ -19154,29 +17759,17 @@ nm_device_class_init(NMDeviceClass *klass) G_TYPE_BOOLEAN, 0); - signals[IP4_CONFIG_CHANGED] = g_signal_new(NM_DEVICE_IP4_CONFIG_CHANGED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 2, - G_TYPE_OBJECT, - G_TYPE_OBJECT); - - signals[IP6_CONFIG_CHANGED] = g_signal_new(NM_DEVICE_IP6_CONFIG_CHANGED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 2, - G_TYPE_OBJECT, - G_TYPE_OBJECT); + signals[L3CD_CHANGED] = g_signal_new(NM_DEVICE_L3CD_CHANGED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 2, + G_TYPE_POINTER, /* (const NML3ConfigData *l3cd_old) */ + G_TYPE_POINTER /* (const NML3ConfigData *l3cd_new) */); signals[IP6_PREFIX_DELEGATED] = g_signal_new(NM_DEVICE_IP6_PREFIX_DELEGATED, diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h index d967bcb212..9bd8aa7bee 100644 --- a/src/core/devices/nm-device.h +++ b/src/core/devices/nm-device.h @@ -9,13 +9,14 @@ #include -#include "nm-setting-connection.h" -#include "nm-dbus-object.h" -#include "nm-dbus-interface.h" -#include "nm-connection.h" -#include "nm-rfkill-manager.h" #include "NetworkManagerUtils.h" +#include "nm-connection.h" +#include "nm-dbus-interface.h" +#include "nm-dbus-object.h" #include "nm-device-utils.h" +#include "nm-l3cfg.h" +#include "nm-rfkill-manager.h" +#include "nm-setting-connection.h" /* Properties */ #define NM_DEVICE_UDI "udi" @@ -71,8 +72,7 @@ /* Internal signals */ #define NM_DEVICE_DNS_LOOKUP_DONE "dns-lookup-done" -#define NM_DEVICE_IP4_CONFIG_CHANGED "ip4-config-changed" -#define NM_DEVICE_IP6_CONFIG_CHANGED "ip6-config-changed" +#define NM_DEVICE_L3CD_CHANGED "l3cd-changed" #define NM_DEVICE_IP6_PREFIX_DELEGATED "ip6-prefix-delegated" #define NM_DEVICE_IP6_SUBNET_NEEDED "ip6-subnet-needed" #define NM_DEVICE_REMOVED "removed" @@ -344,15 +344,10 @@ typedef struct _NMDeviceClass { NMActStageReturn (*act_stage1_prepare)(NMDevice *self, NMDeviceStateReason *out_failure_reason); NMActStageReturn (*act_stage2_config)(NMDevice *self, NMDeviceStateReason *out_failure_reason); - NMActStageReturn (*act_stage3_ip_config_start)(NMDevice * self, - int addr_family, - gpointer * out_config, - NMDeviceStateReason *out_failure_reason); - NMActStageReturn (*act_stage4_ip_config_timeout)(NMDevice * self, - int addr_family, - NMDeviceStateReason *out_failure_reason); + void (*act_stage3_ip_config)(NMDevice *self, int addr_family); + gboolean (*ready_for_ip_config)(NMDevice *self); - void (*ip4_config_pre_commit)(NMDevice *self, NMIP4Config *config); + const char *(*get_ip_method_auto)(NMDevice *self, int addr_family); /* Async deactivating (in the DEACTIVATING phase) */ void (*deactivate_async)(NMDevice * self, @@ -460,16 +455,13 @@ const char *nm_device_get_permanent_hw_address_full(NMDevice *self, gboolean *out_is_fake); const char *nm_device_get_initial_hw_address(NMDevice *dev); -NMProxyConfig *nm_device_get_proxy_config(NMDevice *dev); - NMDhcpConfig *nm_device_get_dhcp_config(NMDevice *dev, int addr_family); -NMIP4Config * nm_device_get_ip4_config(NMDevice *dev); -void nm_device_replace_vpn4_config(NMDevice *dev, NMIP4Config *old, NMIP4Config *config); -NMIP6Config *nm_device_get_ip6_config(NMDevice *dev); -void nm_device_replace_vpn6_config(NMDevice *dev, NMIP6Config *old, NMIP6Config *config); +NML3Cfg *nm_device_get_l3cfg(NMDevice *self); -void nm_device_capture_initial_config(NMDevice *dev); +const NML3ConfigData *nm_device_get_l3cd(NMDevice *self, gboolean get_commited); + +void nm_device_l3cfg_commit(NMDevice *self, NML3CfgCommitType commit_type, gboolean commit_sync); int nm_device_parent_get_ifindex(NMDevice *dev); NMDevice *nm_device_parent_get_device(NMDevice *dev); @@ -547,7 +539,7 @@ RfKillType nm_device_get_rfkill_type(NMDevice *device); /* IPv6 prefix delegation */ -void nm_device_request_ip6_prefixes(NMDevice *self, int needed_prefixes); +void nm_device_request_ip6_prefixes(NMDevice *self, guint needed_prefixes); gboolean nm_device_needs_ip6_subnet(NMDevice *self); @@ -754,17 +746,11 @@ NMConnection *nm_device_new_default_connection(NMDevice *self); const NMPObject *nm_device_get_best_default_route(NMDevice *self, int addr_family); -void nm_device_spawn_iface_helper(NMDevice *self); - gboolean nm_device_reapply(NMDevice *self, NMConnection *connection, GError **error); void nm_device_reapply_settings_immediately(NMDevice *self); void nm_device_update_firewall_zone(NMDevice *self); void nm_device_update_metered(NMDevice *self); -void nm_device_reactivate_ip_config(NMDevice * device, - int addr_family, - NMSettingIPConfig *s_ip_old, - NMSettingIPConfig *s_ip_new); gboolean nm_device_update_hw_address(NMDevice *self); void nm_device_update_initial_hw_address(NMDevice *self); diff --git a/src/core/devices/ovs/nm-device-ovs-bridge.c b/src/core/devices/ovs/nm-device-ovs-bridge.c index 26a5ba002c..1fe298cfa1 100644 --- a/src/core/devices/ovs/nm-device-ovs-bridge.c +++ b/src/core/devices/ovs/nm-device-ovs-bridge.c @@ -66,13 +66,16 @@ get_generic_capabilities(NMDevice *device) return NM_DEVICE_CAP_IS_SOFTWARE; } -static NMActStageReturn -act_stage3_ip_config_start(NMDevice * device, - int addr_family, - gpointer * out_config, - NMDeviceStateReason *out_failure_reason) +static gboolean +ready_for_ip_config(NMDevice *device) { - return NM_ACT_STAGE_RETURN_IP_FAIL; + return FALSE; +} + +static void +act_stage3_ip_config(NMDevice *device, int addr_family) +{ + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, NULL); } static gboolean @@ -154,7 +157,8 @@ nm_device_ovs_bridge_class_init(NMDeviceOvsBridgeClass *klass) device_class->create_and_realize = create_and_realize; device_class->unrealize = unrealize; device_class->get_generic_capabilities = get_generic_capabilities; - device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + device_class->act_stage3_ip_config = act_stage3_ip_config; + device_class->ready_for_ip_config = ready_for_ip_config; device_class->enslave_slave = enslave_slave; device_class->release_slave = release_slave; device_class->can_reapply_change_ovs_external_ids = TRUE; diff --git a/src/core/devices/ovs/nm-device-ovs-interface.c b/src/core/devices/ovs/nm-device-ovs-interface.c index 46a612aca7..672e95f18b 100644 --- a/src/core/devices/ovs/nm-device-ovs-interface.c +++ b/src/core/devices/ovs/nm-device-ovs-interface.c @@ -124,14 +124,19 @@ link_changed(NMDevice *device, const NMPlatformLink *pllink) if (!nm_device_hw_addr_set_cloned(device, nm_device_get_applied_connection(device), FALSE)) { - nm_device_state_changed(device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_CONFIG_FAILED); + nm_device_devip_set_failed(device, AF_INET, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + nm_device_devip_set_failed(device, AF_INET6, NM_DEVICE_STATE_REASON_CONFIG_FAILED); return; } nm_device_bring_up(device, TRUE, NULL); - nm_device_activate_schedule_stage3_ip_config_start(device); + + nm_device_devip_set_state(device, AF_INET, NM_DEVICE_IP_STATE_PENDING, NULL); + nm_device_devip_set_state(device, AF_INET6, NM_DEVICE_IP_STATE_PENDING, NULL); + nm_device_activate_schedule_stage3_ip_config(device, FALSE); + return; } + + nm_device_activate_schedule_stage2_device_config(device, FALSE); } static gboolean @@ -188,31 +193,43 @@ set_platform_mtu(NMDevice *device, guint32 mtu) return NM_DEVICE_CLASS(nm_device_ovs_interface_parent_class)->set_platform_mtu(device, mtu); } -static NMActStageReturn -act_stage3_ip_config_start(NMDevice * device, - int addr_family, - gpointer * out_config, - NMDeviceStateReason *out_failure_reason) +static gboolean +ready_for_ip_config(NMDevice *device) +{ + return nm_device_get_ip_ifindex(device) > 0; +} + +static void +act_stage3_ip_config(NMDevice *device, int addr_family) { NMDeviceOvsInterface * self = NM_DEVICE_OVS_INTERFACE(device); - NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(device); + NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self); - if (!_is_internal_interface(device)) - return NM_ACT_STAGE_RETURN_IP_FAIL; + if (!_is_internal_interface(device)) { + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, NULL); + return; + } + /* FIXME(l3cfg): we should create the IP ifindex before stage3 start. + * + * For now it's here because when the ovs-interface enters stage3, then it's added to the + * controller (ovs-port) and the entry is create in the ovsdb. Only after that the kernel + * link appears. + * + * This should change. */ if (nm_device_get_ip_ifindex(device) <= 0) { _LOGT(LOGD_DEVICE, "waiting for link to appear"); priv->waiting_for_interface = TRUE; - return NM_ACT_STAGE_RETURN_POSTPONE; + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_PENDING, NULL); + return; } if (!nm_device_hw_addr_set_cloned(device, nm_device_get_applied_connection(device), FALSE)) { - *out_failure_reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED; - return NM_ACT_STAGE_RETURN_FAILURE; + nm_device_devip_set_failed(device, addr_family, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + return; } - return NM_DEVICE_CLASS(nm_device_ovs_interface_parent_class) - ->act_stage3_ip_config_start(device, addr_family, out_config, out_failure_reason); + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, NULL); } static gboolean @@ -443,7 +460,8 @@ nm_device_ovs_interface_class_init(NMDeviceOvsInterfaceClass *klass) device_class->is_available = is_available; device_class->check_connection_compatible = check_connection_compatible; device_class->link_changed = link_changed; - device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + device_class->act_stage3_ip_config = act_stage3_ip_config; + device_class->ready_for_ip_config = ready_for_ip_config; device_class->can_unmanaged_external_down = can_unmanaged_external_down; device_class->set_platform_mtu = set_platform_mtu; device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired; diff --git a/src/core/devices/ovs/nm-device-ovs-port.c b/src/core/devices/ovs/nm-device-ovs-port.c index f11c585235..9ed03b3d43 100644 --- a/src/core/devices/ovs/nm-device-ovs-port.c +++ b/src/core/devices/ovs/nm-device-ovs-port.c @@ -59,13 +59,16 @@ get_generic_capabilities(NMDevice *device) return NM_DEVICE_CAP_IS_SOFTWARE; } -static NMActStageReturn -act_stage3_ip_config_start(NMDevice * device, - int addr_family, - gpointer * out_config, - NMDeviceStateReason *out_failure_reason) +static gboolean +ready_for_ip_config(NMDevice *device) { - return NM_ACT_STAGE_RETURN_IP_FAIL; + return FALSE; +} + +static void +act_stage3_ip_config(NMDevice *device, int addr_family) +{ + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, NULL); } static void @@ -186,7 +189,8 @@ nm_device_ovs_port_class_init(NMDeviceOvsPortClass *klass) device_class->get_type_description = get_type_description; device_class->create_and_realize = create_and_realize; device_class->get_generic_capabilities = get_generic_capabilities; - device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + device_class->act_stage3_ip_config = act_stage3_ip_config; + device_class->ready_for_ip_config = ready_for_ip_config; device_class->enslave_slave = enslave_slave; device_class->release_slave = release_slave; device_class->can_reapply_change_ovs_external_ids = TRUE; diff --git a/src/core/devices/team/nm-device-team.c b/src/core/devices/team/nm-device-team.c index 3398e467e5..7ec7305118 100644 --- a/src/core/devices/team/nm-device-team.c +++ b/src/core/devices/team/nm-device-team.c @@ -23,7 +23,6 @@ #include "libnm-core-aux-intern/nm-libnm-core-utils.h" #include "libnm-core-intern/nm-core-internal.h" #include "nm-dbus-manager.h" -#include "nm-ip4-config.h" #include "libnm-std-aux/nm-dbus-compat.h" #define _NMLOG_DEVICE_TYPE NMDeviceTeam diff --git a/src/core/devices/tests/meson.build b/src/core/devices/tests/meson.build index 1bc883706e..871c320629 100644 --- a/src/core/devices/tests/meson.build +++ b/src/core/devices/tests/meson.build @@ -1,7 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later test_units = [ - 'test-acd', 'test-lldp', ] diff --git a/src/core/devices/tests/test-acd.c b/src/core/devices/tests/test-acd.c deleted file mode 100644 index b4b6516549..0000000000 --- a/src/core/devices/tests/test-acd.c +++ /dev/null @@ -1,259 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#include "src/core/nm-default-daemon.h" - -#include "n-acd/src/n-acd.h" - -#include - -#include "devices/nm-acd-manager.h" -#include "platform/tests/test-common.h" - -#define IFACE_VETH0 "nm-test-veth0" -#define IFACE_VETH1 "nm-test-veth1" - -#define ADDR1 0x01010101 -#define ADDR2 0x02020202 -#define ADDR3 0x03030303 -#define ADDR4 0x04040404 - -/*****************************************************************************/ - -static gboolean -_skip_acd_test_check(void) -{ - NAcd * acd; - NAcdConfig * config; - const guint8 hwaddr[ETH_ALEN] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; - int r; - static int skip = -1; - - if (skip == -1) { - r = n_acd_config_new(&config); - g_assert(r == 0); - - n_acd_config_set_ifindex(config, 1); - n_acd_config_set_transport(config, N_ACD_TRANSPORT_ETHERNET); - n_acd_config_set_mac(config, hwaddr, sizeof(hwaddr)); - - r = n_acd_new(&acd, config); - n_acd_config_free(config); - if (r == 0) - n_acd_unref(acd); - - skip = (r != 0); - } - return skip; -} - -#define _skip_acd_test() \ - ({ \ - gboolean _skip = _skip_acd_test_check(); \ - \ - if (_skip) \ - g_test_skip("Cannot create NAcd. Running under valgind?"); \ - _skip; \ - }) - -/*****************************************************************************/ - -typedef struct { - int ifindex0; - int ifindex1; - const guint8 *hwaddr0; - const guint8 *hwaddr1; - size_t hwaddr0_len; - size_t hwaddr1_len; -} test_fixture; - -static void -fixture_setup(test_fixture *fixture, gconstpointer user_data) -{ - /* create veth pair. */ - fixture->ifindex0 = - nmtstp_link_veth_add(NM_PLATFORM_GET, -1, IFACE_VETH0, IFACE_VETH1)->ifindex; - fixture->ifindex1 = - nmtstp_link_get_typed(NM_PLATFORM_GET, -1, IFACE_VETH1, NM_LINK_TYPE_VETH)->ifindex; - - g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, fixture->ifindex0, IFF_UP, TRUE) >= 0); - g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, fixture->ifindex1, IFF_UP, TRUE) >= 0); - - fixture->hwaddr0 = - nm_platform_link_get_address(NM_PLATFORM_GET, fixture->ifindex0, &fixture->hwaddr0_len); - fixture->hwaddr1 = - nm_platform_link_get_address(NM_PLATFORM_GET, fixture->ifindex1, &fixture->hwaddr1_len); -} - -typedef struct { - in_addr_t addresses[8]; - in_addr_t peer_addresses[8]; - gboolean expected_result[8]; -} TestInfo; - -static void -acd_manager_probe_terminated(NMAcdManager *acd_manager, gpointer user_data) -{ - g_main_loop_quit(user_data); -} - -static void -test_acd_common(test_fixture *fixture, TestInfo *info) -{ - nm_auto_free_acdmgr NMAcdManager *manager = NULL; - nm_auto_unref_gmainloop GMainLoop *loop = NULL; - int i; - const guint WAIT_TIME_OPTIMISTIC = 50; - guint wait_time; - static const NMAcdCallbacks callbacks = { - .probe_terminated_callback = acd_manager_probe_terminated, - .user_data_destroy = (GDestroyNotify) g_main_loop_unref, - }; - int r; - - if (_skip_acd_test()) - return; - - /* first, try with a short waittime. We hope that this is long enough - * to successfully complete the test. Only if that's not the case, we - * assume the computer is currently busy (high load) and we retry with - * a longer timeout. */ - wait_time = WAIT_TIME_OPTIMISTIC; -again: - - nm_clear_pointer(&loop, g_main_loop_unref); - loop = g_main_loop_new(NULL, FALSE); - - nm_clear_pointer(&manager, nm_acd_manager_free); - manager = nm_acd_manager_new(fixture->ifindex0, - fixture->hwaddr0, - fixture->hwaddr0_len, - &callbacks, - g_main_loop_ref(loop)); - g_assert(manager != NULL); - - for (i = 0; info->addresses[i]; i++) - g_assert(nm_acd_manager_add_address(manager, info->addresses[i])); - - for (i = 0; info->peer_addresses[i]; i++) { - nmtstp_ip4_address_add(NULL, - FALSE, - fixture->ifindex1, - info->peer_addresses[i], - 24, - 0, - 3600, - 1800, - 0, - NULL); - } - - r = nm_acd_manager_start_probe(manager, wait_time); - g_assert_cmpint(r, ==, 0); - - g_assert(nmtst_main_loop_run(loop, 2000)); - - for (i = 0; info->addresses[i]; i++) { - gboolean val; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - val = nm_acd_manager_check_address(manager, info->addresses[i]); - if (val == info->expected_result[i]) - continue; - - if (wait_time == WAIT_TIME_OPTIMISTIC) { - /* probably we just had a glitch and the system took longer than - * expected. Re-verify with a large timeout this time. */ - wait_time = 1000; - goto again; - } - - g_error("expected check for address #%d (%s) to %s, but it didn't", - i, - _nm_utils_inet4_ntop(info->addresses[i], sbuf), - info->expected_result[i] ? "detect no duplicated" : "detect a duplicate"); - } -} - -static void -test_acd_probe_1(test_fixture *fixture, gconstpointer user_data) -{ - TestInfo info = {.addresses = {ADDR1, ADDR2, ADDR3}, - .peer_addresses = {ADDR4}, - .expected_result = {TRUE, TRUE, TRUE}}; - - test_acd_common(fixture, &info); -} - -static void -test_acd_probe_2(test_fixture *fixture, gconstpointer user_data) -{ - TestInfo info = {.addresses = {ADDR1, ADDR2, ADDR3, ADDR4}, - .peer_addresses = {ADDR3, ADDR2}, - .expected_result = {TRUE, FALSE, FALSE, TRUE}}; - - test_acd_common(fixture, &info); -} - -static void -test_acd_announce(test_fixture *fixture, gconstpointer user_data) -{ - nm_auto_free_acdmgr NMAcdManager *manager = NULL; - nm_auto_unref_gmainloop GMainLoop *loop = NULL; - int r; - - if (_skip_acd_test()) - return; - - manager = - nm_acd_manager_new(fixture->ifindex0, fixture->hwaddr0, fixture->hwaddr0_len, NULL, NULL); - g_assert(manager != NULL); - - g_assert(nm_acd_manager_add_address(manager, ADDR1)); - g_assert(nm_acd_manager_add_address(manager, ADDR2)); - - loop = g_main_loop_new(NULL, FALSE); - r = nm_acd_manager_announce_addresses(manager); - g_assert_cmpint(r, ==, 0); - g_assert(!nmtst_main_loop_run(loop, 200)); -} - -static void -fixture_teardown(test_fixture *fixture, gconstpointer user_data) -{ - nm_platform_link_delete(NM_PLATFORM_GET, fixture->ifindex0); - nm_platform_link_delete(NM_PLATFORM_GET, fixture->ifindex1); -} - -NMTstpSetupFunc const _nmtstp_setup_platform_func = nm_linux_platform_setup; - -void -_nmtstp_init_tests(int *argc, char ***argv) -{ - nmtst_init_with_logging(argc, argv, NULL, "ALL"); -} - -void -_nmtstp_setup_tests(void) -{ - g_test_add("/acd/probe/1", - test_fixture, - NULL, - fixture_setup, - test_acd_probe_1, - fixture_teardown); - g_test_add("/acd/probe/2", - test_fixture, - NULL, - fixture_setup, - test_acd_probe_2, - fixture_teardown); - g_test_add("/acd/announce", - test_fixture, - NULL, - fixture_setup, - test_acd_announce, - fixture_teardown); -} diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c index 27a3188b0d..9a1ac8a162 100644 --- a/src/core/devices/wifi/nm-device-iwd.c +++ b/src/core/devices/wifi/nm-device-iwd.c @@ -1642,7 +1642,7 @@ network_connect_cb(GObject *source, GAsyncResult *res, gpointer user_data) _LOGI(LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) Stage 2 of 5 (Device Configure) successful. Connected to '%s'.", ssid); - nm_device_activate_schedule_stage3_ip_config_start(device); + nm_device_activate_schedule_stage3_ip_config(device, FALSE); return; @@ -1721,7 +1721,7 @@ act_start_cb(GObject *source, GAsyncResult *res, gpointer user_data) _LOGI(LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) Stage 2 of 5 (Device Configure) successful. Started '%s'.", ssid); - nm_device_activate_schedule_stage3_ip_config_start(device); + nm_device_activate_schedule_stage3_ip_config(device, FALSE); return; @@ -2127,7 +2127,7 @@ assumed_connection_progress_to_ip_config(NMDeviceIwd *self, gboolean was_postpon * that stage2 is done. */ if (was_postponed) - nm_device_activate_schedule_stage3_ip_config_start(NM_DEVICE(self)); + nm_device_activate_schedule_stage3_ip_config(NM_DEVICE(self), FALSE); } static void diff --git a/src/core/devices/wifi/nm-device-wifi-p2p.c b/src/core/devices/wifi/nm-device-wifi-p2p.c index 67202a6778..ed74d30ec5 100644 --- a/src/core/devices/wifi/nm-device-wifi-p2p.c +++ b/src/core/devices/wifi/nm-device-wifi-p2p.c @@ -14,18 +14,18 @@ #include "NetworkManagerUtils.h" #include "devices/nm-device-private.h" -#include "nm-act-request.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h" #include "libnm-core-intern/nm-core-internal.h" #include "libnm-glib-aux/nm-ref-string.h" -#include "nm-ip4-config.h" +#include "libnm-platform/nm-platform.h" +#include "libnm-platform/nmp-object.h" +#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" -#include "libnm-platform/nm-platform.h" -#include "libnm-platform/nmp-object.h" #include "settings/nm-settings.h" #define _NMLOG_DEVICE_TYPE NMDeviceWifiP2P @@ -552,13 +552,11 @@ remove_all_peers(NMDeviceWifiP2P *self) /*****************************************************************************/ -static NMActStageReturn -act_stage3_ip_config_start(NMDevice * device, - int addr_family, - gpointer * out_config, - NMDeviceStateReason *out_failure_reason) +static void +act_stage3_ip_config(NMDevice *device, int addr_family) { - NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE(device); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE(device); + const int IS_IPv4 = NM_IS_IPv4(addr_family); gboolean indicate_addressing_running; NMConnection * connection; const char * method; @@ -568,30 +566,30 @@ act_stage3_ip_config_start(NMDevice * device, method = nm_utils_get_ip_config_method(connection, addr_family); /* We may have an address assigned by the group owner */ - if (NM_IN_STRSET(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) && priv->group_iface + if (IS_IPv4 && NM_IN_STRSET(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) && priv->group_iface && !nm_supplicant_interface_get_p2p_group_owner(priv->group_iface)) { in_addr_t addr; guint8 plen; if (nm_supplicant_interface_get_p2p_assigned_addr(priv->group_iface, &addr, &plen)) { - NMPlatformIP4Address address = { + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + NMPlatformIP4Address address = { .addr_source = NM_IP_CONFIG_SOURCE_DHCP, }; - gs_unref_object NMIP4Config *ip4_config = NULL; nm_platform_ip4_address_set_addr(&address, addr, plen); - ip4_config = nm_device_ip4_config_new(device); - nm_ip4_config_add_address(ip4_config, &address); + l3cd = nm_device_create_l3_config_data(device, NM_IP_CONFIG_SOURCE_DHCP); + nm_l3_config_data_add_address_4(l3cd, &address); - nm_device_set_dev2_ip_config(device, AF_INET, NM_IP_CONFIG(ip4_config)); + nm_device_devip_set_state(device, AF_INET, NM_DEVICE_IP_STATE_READY, l3cd); /* This just disables the addressing indicator. */ method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; } } - if (addr_family == AF_INET) + if (IS_IPv4) indicate_addressing_running = NM_IN_STRSET(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO); else { indicate_addressing_running = NM_IN_STRSET(method, @@ -603,9 +601,6 @@ act_stage3_ip_config_start(NMDevice * device, nm_platform_wifi_indicate_addressing_running(nm_device_get_platform(device), nm_device_get_ip_ifindex(device), TRUE); - - return NM_DEVICE_CLASS(nm_device_wifi_p2p_parent_class) - ->act_stage3_ip_config_start(device, addr_family, out_config, out_failure_reason); } static void @@ -757,7 +752,7 @@ check_group_iface_ready(NMDeviceWifiP2P *self) nm_clear_g_source(&priv->sup_timeout_id); update_disconnect_on_connection_peer_missing(self); - nm_device_activate_schedule_stage3_ip_config_start(NM_DEVICE(self)); + nm_device_activate_schedule_stage3_ip_config(NM_DEVICE(self), FALSE); } static void @@ -1286,11 +1281,11 @@ nm_device_wifi_p2p_class_init(NMDeviceWifiP2PClass *klass) device_class->check_connection_compatible = check_connection_compatible; device_class->complete_connection = complete_connection; - 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->get_auto_ip_config_method = get_auto_ip_config_method; - device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + 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->get_auto_ip_config_method = get_auto_ip_config_method; + device_class->act_stage3_ip_config = act_stage3_ip_config; device_class->deactivate = deactivate; device_class->unmanaged_on_quit = unmanaged_on_quit; diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c index 82896c778e..4c5ec3e3ce 100644 --- a/src/core/devices/wifi/nm-device-wifi.c +++ b/src/core/devices/wifi/nm-device-wifi.c @@ -32,7 +32,6 @@ #include "nm-setting-wireless-security.h" #include "nm-setting-8021x.h" #include "nm-setting-ip4-config.h" -#include "nm-ip4-config.h" #include "nm-setting-ip6-config.h" #include "libnm-platform/nm-platform.h" #include "nm-auth-utils.h" @@ -136,6 +135,8 @@ typedef struct { bool ssid_found : 1; bool hidden_probe_scan_warn : 1; + bool addressing_running_indicated : 1; + } NMDeviceWifiPrivate; struct _NMDeviceWifi { @@ -393,6 +394,22 @@ nm_device_wifi_scanning_prohibited_track(NMDeviceWifi *self, /*****************************************************************************/ +static void +_indicate_addressing_running_reset(NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(self); + + if (!priv->addressing_running_indicated) + return; + + priv->addressing_running_indicated = FALSE; + nm_platform_wifi_indicate_addressing_running(nm_device_get_platform(NM_DEVICE(self)), + nm_device_get_ifindex(NM_DEVICE(self)), + FALSE); +} + +/*****************************************************************************/ + static void _ap_dump(NMDeviceWifi * self, NMLogLevel log_level, @@ -932,8 +949,7 @@ deactivate(NMDevice *device) if (!wake_on_wlan_restore(self)) _LOGW(LOGD_DEVICE | LOGD_WIFI, "Cannot unconfigure WoWLAN."); - /* Clear any critical protocol notification in the Wi-Fi stack */ - nm_platform_wifi_indicate_addressing_running(nm_device_get_platform(device), ifindex, FALSE); + _indicate_addressing_running_reset(self); /* Ensure we're in infrastructure mode after deactivation; some devices * (usually older ones) don't scan well in adhoc mode. @@ -2478,7 +2494,7 @@ supplicant_iface_state(NMDeviceWifi * self, priv->mode == _NM_802_11_MODE_AP ? "Started Wi-Fi Hotspot" : "Connected to wireless network", (ssid_str = _nm_utils_ssid_to_string_gbytes(ssid))); - nm_device_activate_schedule_stage3_ip_config_start(device); + nm_device_activate_schedule_stage3_ip_config(device, FALSE); } else if (devstate == NM_DEVICE_STATE_ACTIVATED) periodic_update(self); break; @@ -3263,20 +3279,24 @@ out_fail: return NM_ACT_STAGE_RETURN_FAILURE; } -static NMActStageReturn -act_stage3_ip_config_start(NMDevice * device, - int addr_family, - gpointer * out_config, - NMDeviceStateReason *out_failure_reason) +static void +act_stage3_ip_config(NMDevice *device, int addr_family) { - gboolean indicate_addressing_running; - NMConnection *connection; - const char * method; + NMDeviceWifi * self = NM_DEVICE_WIFI(device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(self); + const char * method; + gboolean indicate_addressing_running; - connection = nm_device_get_applied_connection(device); + if (priv->addressing_running_indicated) + return; - method = nm_utils_get_ip_config_method(connection, addr_family); - if (addr_family == AF_INET) + /* we always set the flag, even if we don't indicate it below. The reason + * is that we always want to *clear* the flag after we are done (as we don't + * know whether it isn't already set on the interface). */ + priv->addressing_running_indicated = TRUE; + + method = nm_utils_get_ip_config_method(nm_device_get_applied_connection(device), addr_family); + if (NM_IS_IPv4(addr_family)) indicate_addressing_running = NM_IN_STRSET(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO); else { indicate_addressing_running = NM_IN_STRSET(method, @@ -3284,13 +3304,11 @@ act_stage3_ip_config_start(NMDevice * device, NM_SETTING_IP6_CONFIG_METHOD_DHCP); } - if (indicate_addressing_running) + if (indicate_addressing_running) { nm_platform_wifi_indicate_addressing_running(nm_device_get_platform(device), nm_device_get_ip_ifindex(device), TRUE); - - return NM_DEVICE_CLASS(nm_device_wifi_parent_class) - ->act_stage3_ip_config_start(device, addr_family, out_config, out_failure_reason); + } } static guint32 @@ -3301,77 +3319,6 @@ get_configured_mtu(NMDevice *device, NMDeviceMtuSource *out_source, gboolean *ou out_source); } -static gboolean -is_static_wep(NMConnection *connection) -{ - NMSettingWirelessSecurity *s_wsec; - const char * str; - - g_return_val_if_fail(connection != NULL, FALSE); - - s_wsec = nm_connection_get_setting_wireless_security(connection); - if (!s_wsec) - return FALSE; - - str = nm_setting_wireless_security_get_key_mgmt(s_wsec); - if (g_strcmp0(str, "none") != 0) - return FALSE; - - str = nm_setting_wireless_security_get_auth_alg(s_wsec); - if (g_strcmp0(str, "leap") == 0) - return FALSE; - - return TRUE; -} - -static NMActStageReturn -act_stage4_ip_config_timeout(NMDevice * device, - int addr_family, - NMDeviceStateReason *out_failure_reason) -{ - NMDeviceWifi * self = NM_DEVICE_WIFI(device); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(self); - NMConnection * connection; - NMSettingIPConfig * s_ip; - gboolean may_fail; - - connection = nm_device_get_applied_connection(device); - s_ip = nm_connection_get_setting_ip_config(connection, addr_family); - may_fail = nm_setting_ip_config_get_may_fail(s_ip); - - if (priv->mode == _NM_802_11_MODE_AP) - goto call_parent; - - if (may_fail || !is_static_wep(connection)) { - /* Not static WEP or failure allowed; let superclass handle it */ - goto call_parent; - } - - /* If IP configuration times out and it's a static WEP connection, that - * usually means the WEP key is wrong. WEP's Open System auth mode has - * no provision for figuring out if the WEP key is wrong, so you just have - * to wait for DHCP to fail to figure it out. For all other Wi-Fi security - * types (open, WPA, 802.1x, etc) if the secrets/certs were wrong the - * connection would have failed before IP configuration. - * - * Activation failed, we must have bad encryption key */ - _LOGW(LOGD_DEVICE | LOGD_WIFI, - "Activation: (wifi) could not get IP configuration for connection '%s'.", - nm_connection_get_id(connection)); - - if (!handle_auth_or_fail(self, NULL, TRUE)) { - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - _LOGI(LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) asking for new secrets"); - return NM_ACT_STAGE_RETURN_POSTPONE; - -call_parent: - return NM_DEVICE_CLASS(nm_device_wifi_parent_class) - ->act_stage4_ip_config_timeout(device, addr_family, out_failure_reason); -} - static void activation_success_handler(NMDevice *device) { @@ -3383,8 +3330,7 @@ activation_success_handler(NMDevice *device) req = nm_device_get_act_request(device); g_assert(req); - /* Clear any critical protocol notification in the wifi stack */ - nm_platform_wifi_indicate_addressing_running(nm_device_get_platform(device), ifindex, FALSE); + _indicate_addressing_running_reset(self); /* There should always be a current AP, either a fake one because we haven't * seen a scan result for the activated AP yet, or a real one from the @@ -3484,19 +3430,13 @@ device_state_changed(NMDevice * device, nm_supplicant_interface_disconnect(priv->sup_iface); break; case NM_DEVICE_STATE_IP_CHECK: - /* Clear any critical protocol notification in the wifi stack */ - nm_platform_wifi_indicate_addressing_running(nm_device_get_platform(device), - nm_device_get_ifindex(device), - FALSE); + _indicate_addressing_running_reset(self); break; case NM_DEVICE_STATE_ACTIVATED: activation_success_handler(device); break; case NM_DEVICE_STATE_FAILED: - /* Clear any critical protocol notification in the wifi stack */ - nm_platform_wifi_indicate_addressing_running(nm_device_get_platform(device), - nm_device_get_ifindex(device), - FALSE); + _indicate_addressing_running_reset(self); break; case NM_DEVICE_STATE_DISCONNECTED: break; @@ -3803,17 +3743,16 @@ nm_device_wifi_class_init(NMDeviceWifiClass *klass) device_class->get_guessed_metered = get_guessed_metered; device_class->set_enabled = set_enabled; - 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->act_stage3_ip_config_start = act_stage3_ip_config_start; - device_class->act_stage4_ip_config_timeout = act_stage4_ip_config_timeout; - device_class->deactivate_async = deactivate_async; - device_class->deactivate = deactivate; - device_class->deactivate_reset_hw_addr = deactivate_reset_hw_addr; - device_class->unmanaged_on_quit = unmanaged_on_quit; - device_class->can_reapply_change = can_reapply_change; - device_class->reapply_connection = reapply_connection; + 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->act_stage3_ip_config = act_stage3_ip_config; + device_class->deactivate_async = deactivate_async; + device_class->deactivate = deactivate; + device_class->deactivate_reset_hw_addr = deactivate_reset_hw_addr; + device_class->unmanaged_on_quit = unmanaged_on_quit; + device_class->can_reapply_change = can_reapply_change; + device_class->reapply_connection = reapply_connection; device_class->state_changed = device_state_changed; diff --git a/src/core/devices/wwan/libnm-wwan.ver b/src/core/devices/wwan/libnm-wwan.ver index c368a5907d..640df36dd8 100644 --- a/src/core/devices/wwan/libnm-wwan.ver +++ b/src/core/devices/wwan/libnm-wwan.ver @@ -14,7 +14,6 @@ global: nm_modem_get_control_port; nm_modem_get_device_id; nm_modem_get_driver; - nm_modem_get_iid; nm_modem_get_ip_ifindex; nm_modem_get_operator_code; nm_modem_get_path; @@ -22,7 +21,6 @@ global: nm_modem_get_state; nm_modem_get_type; nm_modem_get_uid; - nm_modem_ip4_pre_commit; nm_modem_is_claimed; nm_modem_manager_get; nm_modem_manager_get_modems; @@ -32,8 +30,7 @@ global: nm_modem_manager_name_owner_unref; nm_modem_owns_port; nm_modem_set_mm_enabled; - nm_modem_stage3_ip4_config_start; - nm_modem_stage3_ip6_config_start; + nm_modem_stage3_ip_config_start; nm_modem_state_to_string; nm_modem_unclaim; local: diff --git a/src/core/devices/wwan/nm-device-modem.c b/src/core/devices/wwan/nm-device-modem.c index 1b28546937..97fc1b6d29 100644 --- a/src/core/devices/wwan/nm-device-modem.c +++ b/src/core/devices/wwan/nm-device-modem.c @@ -8,7 +8,7 @@ #include "nm-device-modem.h" #include "nm-modem.h" -#include "nm-ip4-config.h" +#include "nm-l3-config-data.h" #include "devices/nm-device-private.h" #include "nm-rfkill-manager.h" #include "settings/nm-settings-connection.h" @@ -33,6 +33,7 @@ typedef struct { NMModem * modem; NMDeviceModemCapabilities caps; NMDeviceModemCapabilities current_caps; + NMUtilsIPv6IfaceId iid; char * device_id; char * operator_code; char * apn; @@ -60,43 +61,9 @@ static void ppp_failed(NMModem *modem, guint i_reason, gpointer user_data) { NMDevice * device = NM_DEVICE(user_data); - NMDeviceModem * self = NM_DEVICE_MODEM(user_data); NMDeviceStateReason reason = i_reason; - switch (nm_device_get_state(device)) { - case NM_DEVICE_STATE_PREPARE: - case NM_DEVICE_STATE_CONFIG: - case NM_DEVICE_STATE_NEED_AUTH: - nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, reason); - break; - case NM_DEVICE_STATE_IP_CONFIG: - case NM_DEVICE_STATE_IP_CHECK: - case NM_DEVICE_STATE_SECONDARIES: - case NM_DEVICE_STATE_ACTIVATED: - if (nm_device_activate_ip4_state_in_conf(device)) - nm_device_activate_schedule_ip_config_timeout(device, AF_INET); - else if (nm_device_activate_ip6_state_in_conf(device)) - nm_device_activate_schedule_ip_config_timeout(device, AF_INET6); - else if (nm_device_activate_ip4_state_done(device)) { - nm_device_ip_method_failed(device, - AF_INET, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } else if (nm_device_activate_ip6_state_done(device)) { - nm_device_ip_method_failed(device, - AF_INET6, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } else { - _LOGW(LOGD_MB, - "PPP failure in unexpected state %u", - (guint) nm_device_get_state(device)); - nm_device_state_changed(device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } - break; - default: - break; - } + nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, reason); } static void @@ -188,96 +155,39 @@ modem_auth_result(NMModem *modem, GError *error, gpointer user_data) } static void -modem_ip4_config_result(NMModem *modem, NMIP4Config *config, GError *error, gpointer user_data) +modem_new_config(NMModem * modem, + int addr_family, + const NML3ConfigData * l3cd, + gboolean do_auto, + const NMUtilsIPv6IfaceId *iid, + int failure_reason_i, + GError * error, + gpointer user_data) { - NMDeviceModem *self = NM_DEVICE_MODEM(user_data); - NMDevice * device = NM_DEVICE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDeviceModem * self = NM_DEVICE_MODEM(user_data); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE(self); + NMDevice * device = NM_DEVICE(self); - if (!nm_device_activate_ip4_state_in_conf(device)) { - _LOGD(LOGD_MB | LOGD_IP4, - "retrieving IPv4 configuration while no longer in state IPv4 conf"); - return; - } + g_return_if_fail(nm_device_devip_get_state(device, addr_family) == NM_DEVICE_IP_STATE_PENDING); if (error) { - _LOGW(LOGD_MB | LOGD_IP4, "retrieving IPv4 configuration failed: %s", error->message); - nm_device_ip_method_failed(device, AF_INET, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + _LOGW(LOGD_MB | LOGD_IP4, "retrieving IP configuration failed: %s", error->message); + nm_device_devip_set_failed(device, addr_family, failure_reason_i); return; } - nm_device_set_dev2_ip_config(device, AF_INET, NM_IP_CONFIG_CAST(config)); - nm_device_activate_schedule_ip_config_result(device, AF_INET, NULL); -} + if (!IS_IPv4) + priv->iid = iid ? *iid : ((NMUtilsIPv6IfaceId) NM_UTILS_IPV6_IFACE_ID_INIT); -static void -modem_ip6_config_result(NMModem * modem, - NMIP6Config *config, - gboolean do_slaac, - GError * error, - gpointer user_data) -{ - NMDeviceModem * self = NM_DEVICE_MODEM(user_data); - NMDevice * device = NM_DEVICE(self); - NMActStageReturn ret; - NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; - gs_unref_object NMIP6Config *ignored = NULL; - gboolean got_config = !!config; - - if (!nm_device_activate_ip6_state_in_conf(device)) { - _LOGD(LOGD_MB | LOGD_IP6, - "retrieving IPv6 configuration while no longer in state IPv6 conf"); - return; + if (do_auto) { + if (IS_IPv4) + nm_device_ip_method_dhcp4_start(device); + else + nm_device_ip_method_autoconf6_start(device); } - if (error) { - _LOGW(LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: %s", error->message); - nm_device_ip_method_failed(device, AF_INET6, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return; - } - - /* Re-enable IPv6 on the interface */ - nm_device_sysctl_ip_conf_set(device, AF_INET6, "disable_ipv6", "0"); - - if (config) - nm_device_set_dev2_ip_config(device, AF_INET6, NM_IP_CONFIG_CAST(config)); - - if (do_slaac == FALSE) { - if (got_config) - nm_device_activate_schedule_ip_config_result(device, AF_INET6, NULL); - else { - _LOGW(LOGD_MB | LOGD_IP6, - "retrieving IPv6 configuration failed: SLAAC not requested and no addresses"); - nm_device_ip_method_failed(device, - AF_INET6, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } - return; - } - - /* Start SLAAC now that we have a link-local address from the modem */ - ret = - NM_DEVICE_CLASS(nm_device_modem_parent_class) - ->act_stage3_ip_config_start(device, AF_INET6, (gpointer *) &ignored, &failure_reason); - - nm_assert(ignored == NULL); - - switch (ret) { - case NM_ACT_STAGE_RETURN_FAILURE: - nm_device_ip_method_failed(device, AF_INET6, failure_reason); - break; - case NM_ACT_STAGE_RETURN_IP_FAIL: - /* all done */ - nm_device_activate_schedule_ip_config_result(device, AF_INET6, NULL); - break; - case NM_ACT_STAGE_RETURN_POSTPONE: - /* let SLAAC run */ - break; - default: - /* Should never get here since we've assured that the IPv6 method - * will either be "auto" or "ignored" when starting IPv6 configuration. - */ - nm_assert_not_reached(); - } + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, l3cd); } static void @@ -606,36 +516,18 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) static NMActStageReturn act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) { - nm_modem_act_stage2_config(NM_DEVICE_MODEM_GET_PRIVATE(device)->modem); - return NM_ACT_STAGE_RETURN_SUCCESS; -} - -static NMActStageReturn -act_stage3_ip_config_start(NMDevice * device, - int addr_family, - gpointer * out_config, - NMDeviceStateReason *out_failure_reason) -{ - NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE(device); - gboolean autoip4 = FALSE; - NMActStageReturn ret; - - if (!NM_IS_IPv4(addr_family)) - return nm_modem_stage3_ip6_config_start(priv->modem, device, out_failure_reason); - - ret = nm_modem_stage3_ip4_config_start(priv->modem, device, &autoip4, out_failure_reason); - - if (ret != NM_ACT_STAGE_RETURN_SUCCESS || !autoip4) - return ret; - - return NM_DEVICE_CLASS(nm_device_modem_parent_class) - ->act_stage3_ip_config_start(device, addr_family, out_config, out_failure_reason); + return nm_modem_act_stage2_config(NM_DEVICE_MODEM_GET_PRIVATE(device)->modem, + device, + out_failure_reason); } static void -ip4_config_pre_commit(NMDevice *device, NMIP4Config *config) +act_stage3_ip_config(NMDevice *device, int addr_family) { - nm_modem_ip4_pre_commit(NM_DEVICE_MODEM_GET_PRIVATE(device)->modem, device, config); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE(device); + + if (nm_modem_stage3_ip_config_start(priv->modem, addr_family, device)) + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_PENDING, NULL); } static gboolean @@ -643,14 +535,15 @@ get_ip_iface_identifier(NMDevice *device, NMUtilsIPv6IfaceId *out_iid) { NMDeviceModem * self = NM_DEVICE_MODEM(device); NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE(self); - gboolean success; g_return_val_if_fail(priv->modem, FALSE); - success = nm_modem_get_iid(priv->modem, out_iid); - if (!success) - success = - NM_DEVICE_CLASS(nm_device_modem_parent_class)->get_ip_iface_identifier(device, out_iid); - return success; + + if (priv->iid.id != 0) { + *out_iid = priv->iid; + return TRUE; + } + + return NM_DEVICE_CLASS(nm_device_modem_parent_class)->get_ip_iface_identifier(device, out_iid); } /*****************************************************************************/ @@ -716,8 +609,7 @@ set_modem(NMDeviceModem *self, NMModem *modem) g_signal_connect(modem, NM_MODEM_PPP_FAILED, G_CALLBACK(ppp_failed), self); g_signal_connect(modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK(modem_prepare_result), self); - g_signal_connect(modem, NM_MODEM_IP4_CONFIG_RESULT, G_CALLBACK(modem_ip4_config_result), self); - g_signal_connect(modem, NM_MODEM_IP6_CONFIG_RESULT, G_CALLBACK(modem_ip6_config_result), self); + g_signal_connect(modem, NM_MODEM_NEW_CONFIG, G_CALLBACK(modem_new_config), self); g_signal_connect(modem, NM_MODEM_AUTH_REQUESTED, G_CALLBACK(modem_auth_requested), self); g_signal_connect(modem, NM_MODEM_AUTH_RESULT, G_CALLBACK(modem_auth_result), self); g_signal_connect(modem, NM_MODEM_STATE_CHANGED, G_CALLBACK(modem_state_cb), self); @@ -904,8 +796,7 @@ nm_device_modem_class_init(NMDeviceModemClass *klass) device_class->deactivate = deactivate; device_class->act_stage1_prepare = act_stage1_prepare; device_class->act_stage2_config = act_stage2_config; - device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; - device_class->ip4_config_pre_commit = ip4_config_pre_commit; + device_class->act_stage3_ip_config = act_stage3_ip_config; device_class->get_enabled = get_enabled; device_class->set_enabled = set_enabled; device_class->owns_iface = owns_iface; diff --git a/src/core/devices/wwan/nm-modem-broadband.c b/src/core/devices/wwan/nm-modem-broadband.c index a5139f0833..483314ff21 100644 --- a/src/core/devices/wwan/nm-modem-broadband.c +++ b/src/core/devices/wwan/nm-modem-broadband.c @@ -16,8 +16,7 @@ #include "NetworkManagerUtils.h" #include "devices/nm-device-private.h" #include "libnm-platform/nm-platform.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" +#include "nm-l3-config-data.h" #define NM_MODEM_BROADBAND_MODEM "modem" @@ -91,9 +90,6 @@ typedef struct { MMBearerIpConfig *ipv4_config; MMBearerIpConfig *ipv6_config; - guint idle_id_ip4; - guint idle_id_ip6; - guint32 pin_tries; } NMModemBroadbandPrivate; @@ -958,249 +954,247 @@ set_mm_enabled(NMModem *_self, gboolean enabled) } /*****************************************************************************/ -/* IPv4 method static */ -static gboolean -static_stage3_ip4_done(NMModemBroadband *self) +static void +stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_method) { - GError * error = NULL; - gs_unref_object NMIP4Config *config = NULL; - const char * data_port; - const char * address_string; - const char * gw_string; - guint32 address_network; - guint32 gw = 0; - NMPlatformIP4Address address; - const char ** dns; - guint i; - guint32 ip4_route_table, ip4_route_metric; - NMPlatformIP4Route * r; - guint32 mtu_n; + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMModemBroadband * self = NM_MODEM_BROADBAND(modem); + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + char sbuf[sizeof(_nm_utils_to_string_buffer)]; + gs_free_error GError * error = NULL; + const char * data_port; + const char * address_string; + const char ** dns; + guint i; + gboolean do_auto = FALSE; + int ifindex; + NMUtilsIPv6IfaceId iid_data; + const NMUtilsIPv6IfaceId *iid = NULL; - g_return_val_if_fail(self->_priv.ipv4_config, FALSE); - g_return_val_if_fail(self->_priv.bearer, FALSE); + if (IS_IPv4) { + g_return_if_fail(self->_priv.ipv4_config); + g_return_if_fail(self->_priv.bearer); - self->_priv.idle_id_ip4 = 0; + if (ip_method == NM_MODEM_IP_METHOD_AUTO) { + do_auto = TRUE; + goto out; + } + } else { + g_return_if_fail(self->_priv.ipv6_config); + } - _LOGI("IPv4 static configuration:"); + if (IS_IPv4) { + guint32 address_network; + guint32 gw = 0; + NMPlatformIP4Address address; + NMPlatformIP4Route route; + guint32 mtu_n; + const char * gw_string; - /* 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)) { - error = - g_error_new(NM_DEVICE_ERROR, + _LOGI("IPv4 static configuration:"); + + /* 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)) { + g_set_error(&error, + NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IP4 configuration failed: invalid address given %s%s%s", nm_modem_get_uid(NM_MODEM(self)), NM_PRINT_FMT_QUOTE_STRING(address_string)); - goto out; - } + goto out; + } - /* 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)) { - error = - g_error_new(NM_DEVICE_ERROR, + /* 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)) { + g_set_error(&error, + NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IP4 configuration failed: invalid gateway address \"%s\"", nm_modem_get_uid(NM_MODEM(self)), gw_string); - goto out; - } - - data_port = mm_bearer_get_interface(self->_priv.bearer); - g_return_val_if_fail(data_port, FALSE); - config = nm_ip4_config_new(nm_platform_get_multi_idx(NM_PLATFORM_GET), - nm_platform_link_get_ifindex(NM_PLATFORM_GET, data_port)); - - memset(&address, 0, sizeof(address)); - address.address = address_network; - address.peer_address = address_network; - address.plen = mm_bearer_ip_config_get_prefix(self->_priv.ipv4_config); - address.addr_source = NM_IP_CONFIG_SOURCE_WWAN; - if (address.plen <= 32) - nm_ip4_config_add_address(config, &address); - - _LOGI(" address %s/%d", address_string, address.plen); - - nm_modem_get_route_parameters(NM_MODEM(self), &ip4_route_table, &ip4_route_metric, NULL, NULL); - r = &(NMPlatformIP4Route){ - .rt_source = NM_IP_CONFIG_SOURCE_WWAN, - .gateway = gw, - .table_coerced = nm_platform_route_table_coerce(ip4_route_table), - .metric = ip4_route_metric, - }; - nm_ip4_config_add_route(config, r, NULL); - _LOGI(" gateway %s", gw_string); - - /* DNS servers */ - 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_ip4_config_add_nameserver(config, address_network); - _LOGI(" DNS %s", dns[i]); + goto out; + } + + data_port = mm_bearer_get_interface(self->_priv.bearer); + g_return_if_fail(data_port); + + ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, data_port); + if (ifindex <= 0) { + g_set_error(&error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "(%s) data port %s not found", + nm_modem_get_uid(NM_MODEM(self)), + data_port); + goto out; + } + + l3cd = nm_l3_config_data_new(nm_platform_get_multi_idx(NM_PLATFORM_GET), + ifindex, + NM_IP_CONFIG_SOURCE_WWAN); + + address = (NMPlatformIP4Address){ + .address = address_network, + .peer_address = address_network, + .plen = mm_bearer_ip_config_get_prefix(self->_priv.ipv4_config), + .addr_source = NM_IP_CONFIG_SOURCE_WWAN, + }; + if (address.plen <= 32) + nm_l3_config_data_add_address_4(l3cd, &address); + + _LOGI(" address %s", nm_platform_ip4_address_to_string(&address, sbuf, sizeof(sbuf))); + + route = (NMPlatformIP4Route){ + .rt_source = NM_IP_CONFIG_SOURCE_WWAN, + .gateway = gw, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, + .metric = 0, + }; + nm_l3_config_data_add_route_4(l3cd, &route); + _LOGI(" gateway %s", gw_string); + + 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); + _LOGI(" DNS %s", dns[i]); + } } - } #if MM_CHECK_VERSION(1, 4, 0) - mtu_n = mm_bearer_ip_config_get_mtu(self->_priv.ipv4_config); - if (mtu_n) { - nm_ip4_config_set_mtu(config, mtu_n, NM_IP_CONFIG_SOURCE_WWAN); - _LOGI(" MTU %u", mtu_n); - } -#endif - -out: - g_signal_emit_by_name(self, NM_MODEM_IP4_CONFIG_RESULT, config, error); - g_clear_error(&error); - return FALSE; -} - -static NMActStageReturn -static_stage3_ip4_config_start(NMModem * modem, - NMActRequest * req, - NMDeviceStateReason *out_failure_reason) -{ - NMModemBroadband * self = NM_MODEM_BROADBAND(modem); - NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE(self); - - /* We schedule it in an idle just to follow the same logic as in the - * generic modem implementation. */ - nm_clear_g_source(&priv->idle_id_ip4); - priv->idle_id_ip4 = g_idle_add((GSourceFunc) static_stage3_ip4_done, self); - - return NM_ACT_STAGE_RETURN_POSTPONE; -} - -/*****************************************************************************/ -/* IPv6 method static */ - -static gboolean -stage3_ip6_done(NMModemBroadband *self) -{ - GError * error = NULL; - NMIP6Config * config = NULL; - const char * data_port; - const char * address_string; - NMPlatformIP6Address address; - NMModemIPMethod ip_method; - const char ** dns; - guint i; - - g_return_val_if_fail(self->_priv.ipv6_config, FALSE); - - self->_priv.idle_id_ip6 = 0; - memset(&address, 0, sizeof(address)); - - ip_method = get_bearer_ip_method(self->_priv.ipv6_config); - - address_string = mm_bearer_ip_config_get_address(self->_priv.ipv6_config); - if (!address_string) { - /* DHCP/SLAAC is allowed to skip addresses; other methods require it */ - if (ip_method != NM_MODEM_IP_METHOD_AUTO) { - error = g_error_new(NM_DEVICE_ERROR, - NM_DEVICE_ERROR_INVALID_CONNECTION, - "(%s) retrieving IPv6 configuration failed: no address given", - nm_modem_get_uid(NM_MODEM(self))); + mtu_n = mm_bearer_ip_config_get_mtu(self->_priv.ipv4_config); + if (mtu_n) { + nm_l3_config_data_set_mtu(l3cd, mtu_n); + _LOGI(" MTU %u", mtu_n); } - goto out; - } +#endif + } else { + NMPlatformIP6Address address; - /* Fail if invalid IP address retrieved */ - if (!inet_pton(AF_INET6, address_string, (void *) &(address.address))) { - error = g_error_new(NM_DEVICE_ERROR, + address_string = mm_bearer_ip_config_get_address(self->_priv.ipv6_config); + if (!address_string) { + /* DHCP/SLAAC is allowed to skip addresses; other methods require it */ + if (ip_method != NM_MODEM_IP_METHOD_AUTO) { + g_set_error(&error, + NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, - "(%s) retrieving IPv6 configuration failed: invalid address given '%s'", - nm_modem_get_uid(NM_MODEM(self)), - address_string); - goto out; - } + "(%s) retrieving IPv6 configuration failed: no address given", + nm_modem_get_uid(NM_MODEM(self))); + } + goto out; + } - _LOGI("IPv6 base configuration:"); + address = (NMPlatformIP6Address){}; - data_port = mm_bearer_get_interface(self->_priv.bearer); - g_return_val_if_fail(data_port, FALSE); + if (!inet_pton(AF_INET6, address_string, &address.address)) { + g_set_error(&error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "(%s) retrieving IPv6 configuration failed: invalid address given '%s'", + nm_modem_get_uid(NM_MODEM(self)), + address_string); + goto out; + } - config = nm_ip6_config_new(nm_platform_get_multi_idx(NM_PLATFORM_GET), - nm_platform_link_get_ifindex(NM_PLATFORM_GET, data_port)); + data_port = mm_bearer_get_interface(self->_priv.bearer); + g_return_if_fail(data_port); - address.plen = mm_bearer_ip_config_get_prefix(self->_priv.ipv6_config); - if (address.plen <= 128) - nm_ip6_config_add_address(config, &address); + ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, data_port); + if (ifindex <= 0) { + g_set_error(&error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "(%s) data port %s not found", + nm_modem_get_uid(NM_MODEM(self)), + data_port); + goto out; + } - _LOGI(" address %s/%d", address_string, address.plen); + _LOGI("IPv6 base configuration:"); - address_string = mm_bearer_ip_config_get_gateway(self->_priv.ipv6_config); - if (address_string) { - guint32 ip6_route_table, ip6_route_metric; + l3cd = nm_l3_config_data_new(nm_platform_get_multi_idx(NM_PLATFORM_GET), + ifindex, + NM_IP_CONFIG_SOURCE_WWAN); - if (inet_pton(AF_INET6, address_string, &address.address) != 1) { - error = - g_error_new(NM_DEVICE_ERROR, + do_auto = TRUE; + + address.plen = mm_bearer_ip_config_get_prefix(self->_priv.ipv6_config); + if (address.plen <= 128) { + if (IN6_IS_ADDR_LINKLOCAL(&address.address)) { + iid_data.id = ((guint64 *) (&address.address.s6_addr))[1]; + iid = &iid_data; + } else + do_auto = FALSE; + nm_l3_config_data_add_address_6(l3cd, &address); + } + + _LOGI(" address %s (slaac %s)", + nm_platform_ip6_address_to_string(&address, sbuf, sizeof(sbuf)), + do_auto ? "enabled" : "disabled"); + + address_string = mm_bearer_ip_config_get_gateway(self->_priv.ipv6_config); + if (address_string) { + if (inet_pton(AF_INET6, address_string, &address.address) != 1) { + g_set_error(&error, + NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IPv6 configuration failed: invalid gateway given '%s'", nm_modem_get_uid(NM_MODEM(self)), address_string); + goto out; + } + + { + const NMPlatformIP6Route r = { + .rt_source = NM_IP_CONFIG_SOURCE_WWAN, + .gateway = address.address, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, + .metric = 0, + }; + + _LOGI(" gateway %s", address_string); + nm_l3_config_data_add_route_6(l3cd, &r); + } + } else if (ip_method == NM_MODEM_IP_METHOD_STATIC) { + /* Gateway required for the 'static' method */ + g_set_error(&error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "(%s) retrieving IPv6 configuration failed: missing gateway", + nm_modem_get_uid(NM_MODEM(self))); goto out; } - nm_modem_get_route_parameters(NM_MODEM(self), - NULL, - NULL, - &ip6_route_table, - &ip6_route_metric); - { - const NMPlatformIP6Route r = { - .rt_source = NM_IP_CONFIG_SOURCE_WWAN, - .gateway = address.address, - .table_coerced = nm_platform_route_table_coerce(ip6_route_table), - .metric = ip6_route_metric, - }; + dns = mm_bearer_ip_config_get_dns(self->_priv.ipv6_config); + for (i = 0; dns && dns[i]; i++) { + struct in6_addr addr; - _LOGI(" gateway %s", address_string); - nm_ip6_config_add_route(config, &r, NULL); - } - } else if (ip_method == NM_MODEM_IP_METHOD_STATIC) { - /* Gateway required for the 'static' method */ - error = g_error_new(NM_DEVICE_ERROR, - NM_DEVICE_ERROR_INVALID_CONNECTION, - "(%s) retrieving IPv6 configuration failed: missing gateway", - nm_modem_get_uid(NM_MODEM(self))); - goto out; - } - - /* DNS servers */ - dns = mm_bearer_ip_config_get_dns(self->_priv.ipv6_config); - for (i = 0; dns && dns[i]; i++) { - struct in6_addr addr; - - if (inet_pton(AF_INET6, dns[i], &addr)) { - nm_ip6_config_add_nameserver(config, &addr); - _LOGI(" DNS %s", dns[i]); + if (inet_pton(AF_INET6, dns[i], &addr)) { + nm_l3_config_data_add_nameserver(l3cd, AF_INET6, &addr); + _LOGI(" DNS %s", dns[i]); + } } } out: - nm_modem_emit_ip6_config_result(NM_MODEM(self), config, error); - g_clear_object(&config); - g_clear_error(&error); - return FALSE; -} + if (error) { + nm_modem_emit_signal_new_config_failure(modem, + addr_family, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE, + error); + return; + } -static NMActStageReturn -stage3_ip6_config_request(NMModem *modem, NMDeviceStateReason *out_failure_reason) -{ - NMModemBroadband * self = NM_MODEM_BROADBAND(modem); - NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE(self); - - /* We schedule it in an idle just to follow the same logic as in the - * generic modem implementation. */ - nm_clear_g_source(&priv->idle_id_ip6); - priv->idle_id_ip6 = g_idle_add((GSourceFunc) stage3_ip6_done, self); - - return NM_ACT_STAGE_RETURN_POSTPONE; + nm_modem_emit_signal_new_config_success(modem, addr_family, l3cd, do_auto, iid); } /*****************************************************************************/ @@ -1564,27 +1558,24 @@ dispose(GObject *object) NMModemBroadband * self = NM_MODEM_BROADBAND(object); NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE(self); - nm_clear_g_source(&priv->idle_id_ip4); - nm_clear_g_source(&priv->idle_id_ip6); - connect_context_clear(self); - g_clear_object(&self->_priv.ipv4_config); - g_clear_object(&self->_priv.ipv6_config); - g_clear_object(&self->_priv.bearer); + g_clear_object(&priv->ipv4_config); + g_clear_object(&priv->ipv6_config); + g_clear_object(&priv->bearer); - if (self->_priv.modem_iface) { - g_signal_handlers_disconnect_by_data(self->_priv.modem_iface, self); - g_clear_object(&self->_priv.modem_iface); + if (priv->modem_iface) { + g_signal_handlers_disconnect_by_data(priv->modem_iface, self); + g_clear_object(&priv->modem_iface); } - if (self->_priv.modem_3gpp_iface) { - g_signal_handlers_disconnect_by_data(self->_priv.modem_3gpp_iface, self); - g_clear_object(&self->_priv.modem_3gpp_iface); + if (priv->modem_3gpp_iface) { + g_signal_handlers_disconnect_by_data(priv->modem_3gpp_iface, self); + g_clear_object(&priv->modem_3gpp_iface); } - g_clear_object(&self->_priv.simple_iface); - g_clear_object(&self->_priv.sim_iface); - g_clear_object(&self->_priv.modem_object); + g_clear_object(&priv->simple_iface); + g_clear_object(&priv->sim_iface); + g_clear_object(&priv->modem_object); G_OBJECT_CLASS(nm_modem_broadband_parent_class)->dispose(object); } @@ -1600,8 +1591,7 @@ nm_modem_broadband_class_init(NMModemBroadbandClass *klass) object_class->set_property = set_property; modem_class->get_capabilities = get_capabilities; - modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start; - modem_class->stage3_ip6_config_request = stage3_ip6_config_request; + modem_class->stage3_ip_config_start = stage3_ip_config_start; modem_class->disconnect = disconnect; modem_class->deactivate_cleanup = deactivate_cleanup; modem_class->set_mm_enabled = set_mm_enabled; diff --git a/src/core/devices/wwan/nm-modem-ofono.c b/src/core/devices/wwan/nm-modem-ofono.c index 78ad7b6820..47dc1e3b6d 100644 --- a/src/core/devices/wwan/nm-modem-ofono.c +++ b/src/core/devices/wwan/nm-modem-ofono.c @@ -11,7 +11,7 @@ #include "devices/nm-device-private.h" #include "nm-modem.h" #include "libnm-platform/nm-platform.h" -#include "nm-ip4-config.h" +#include "nm-l3-config-data.h" #define VARIANT_IS_OF_TYPE_BOOLEAN(v) \ ((v) != NULL && (g_variant_is_of_type((v), G_VARIANT_TYPE_BOOLEAN))) @@ -47,7 +47,7 @@ typedef struct { gboolean modem_online; gboolean gprs_attached; - NMIP4Config *ip4_config; + NML3ConfigData *l3cd_4; } NMModemOfonoPrivate; struct _NMModemOfono { @@ -240,7 +240,7 @@ deactivate_cleanup(NMModem *modem, NMDevice *device, gboolean stop_ppp_manager) /* TODO: cancel SimpleConnect() if any */ - g_clear_object(&priv->ip4_config); + nm_clear_l3cd(&priv->l3cd_4); NM_MODEM_CLASS(nm_modem_ofono_parent_class) ->deactivate_cleanup(modem, device, stop_ppp_manager); @@ -734,13 +734,13 @@ handle_settings(GVariant *v_dict, gpointer user_data) { NMModemOfono * self = NM_MODEM_OFONO(user_data); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); - NMPlatformIP4Address addr; + char sbuf[sizeof(_nm_utils_to_string_buffer)]; + NMPlatformIP4Address address; gboolean ret = FALSE; const char * interface; const char * s; - const char ** array, **iter; + const char ** array; guint32 address_network, gateway_network; - guint32 ip4_route_table, ip4_route_metric; int ifindex; GError * error = NULL; @@ -773,12 +773,14 @@ handle_settings(GVariant *v_dict, gpointer user_data) } ifindex = nm_modem_get_ip_ifindex(NM_MODEM(self)); - nm_assert(ifindex > 0); + g_return_if_fail(ifindex > 0); - /* TODO: verify handling of ip4_config; check other places it's used... */ - g_clear_object(&priv->ip4_config); + /* TODO: verify handling of l3cd_4; check other places it's used... */ + nm_clear_l3cd(&priv->l3cd_4); - priv->ip4_config = nm_ip4_config_new(nm_platform_get_multi_idx(NM_PLATFORM_GET), ifindex); + priv->l3cd_4 = nm_l3_config_data_new(nm_platform_get_multi_idx(NM_PLATFORM_GET), + ifindex, + NM_IP_CONFIG_SOURCE_WWAN); if (!g_variant_lookup(v_dict, "Address", "&s", &s)) { _LOGW("Settings 'Address' missing"); @@ -788,10 +790,12 @@ handle_settings(GVariant *v_dict, gpointer user_data) _LOGW("can't convert 'Address' %s to addr", s ?: ""); goto out; } - memset(&addr, 0, sizeof(addr)); - addr.ifindex = ifindex; - addr.address = address_network; - addr.addr_source = NM_IP_CONFIG_SOURCE_WWAN; + + address = (NMPlatformIP4Address){ + .ifindex = ifindex, + .address = address_network, + .addr_source = NM_IP_CONFIG_SOURCE_WWAN, + }; if (!g_variant_lookup(v_dict, "Netmask", "&s", &s)) { _LOGW("Settings 'Netmask' missing"); @@ -801,10 +805,10 @@ handle_settings(GVariant *v_dict, gpointer user_data) _LOGW("invalid 'Netmask': %s", s ?: ""); goto out; } - addr.plen = nm_utils_ip4_netmask_to_prefix(address_network); + address.plen = nm_utils_ip4_netmask_to_prefix(address_network); - _LOGI("Address: %s", nm_platform_ip4_address_to_string(&addr, NULL, 0)); - nm_ip4_config_add_address(priv->ip4_config, &addr); + _LOGI("Address: %s", nm_platform_ip4_address_to_string(&address, sbuf, sizeof(sbuf))); + nm_l3_config_data_add_address_4(priv->l3cd_4, &address); if (!g_variant_lookup(v_dict, "Gateway", "&s", &s) || !s) { _LOGW("Settings 'Gateway' missing"); @@ -814,17 +818,18 @@ handle_settings(GVariant *v_dict, gpointer user_data) _LOGW("invalid 'Gateway': %s", s); goto out; } - nm_modem_get_route_parameters(NM_MODEM(self), &ip4_route_table, &ip4_route_metric, NULL, NULL); { const NMPlatformIP4Route r = { .rt_source = NM_IP_CONFIG_SOURCE_WWAN, .gateway = gateway_network, - .table_coerced = nm_platform_route_table_coerce(ip4_route_table), - .metric = ip4_route_metric, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, + .metric = 0, }; _LOGI("Gateway: %s", s); - nm_ip4_config_add_route(priv->ip4_config, &r, NULL); + nm_l3_config_data_add_route_4(priv->l3cd_4, &r); } if (!g_variant_lookup(v_dict, "DomainNameServers", "^a&s", &array)) { @@ -832,52 +837,48 @@ handle_settings(GVariant *v_dict, gpointer user_data) goto out; } if (array) { - for (iter = array; *iter; iter++) { - if (nm_utils_parse_inaddr_bin(AF_INET, *iter, NULL, &address_network) - && address_network) { - _LOGI("DNS: %s", *iter); - nm_ip4_config_add_nameserver(priv->ip4_config, address_network); - } else { - _LOGW("invalid NameServer: %s", *iter); - } - } + gboolean any_good = FALSE; - if (iter == array) { + for (; array[0]; array++) { + if (!nm_utils_parse_inaddr_bin(AF_INET, *array, NULL, &address_network) + || !address_network) { + _LOGW("invalid NameServer: %s", *array); + continue; + } + any_good = TRUE; + _LOGI("DNS: %s", *array); + nm_l3_config_data_add_nameserver(priv->l3cd_4, AF_INET, &address_network); + } + if (!any_good) { _LOGW("Settings: 'DomainNameServers': none specified"); - g_free(array); goto out; } - g_free(array); } 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)) { - nm_modem_get_route_parameters(NM_MODEM(self), - &ip4_route_table, - &ip4_route_metric, - NULL, - NULL); + const NMPlatformIP4Route mms_route = { + .network = address_network, + .plen = 32, + .gateway = gateway_network, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, + .metric = 0, + }; - { - const NMPlatformIP4Route mms_route = { - .network = address_network, - .plen = 32, - .gateway = gateway_network, - .table_coerced = nm_platform_route_table_coerce(ip4_route_table), - .metric = ip4_route_metric, - }; - - nm_ip4_config_add_route(priv->ip4_config, &mms_route, NULL); - } - } else { + nm_l3_config_data_add_route_4(priv->l3cd_4, &mms_route); + } else _LOGW("invalid MessageProxy: %s", s); - } } ret = TRUE; out: + if (priv->l3cd_4) + nm_l3_config_data_seal(priv->l3cd_4); + if (nm_modem_get_state(NM_MODEM(self)) != NM_MODEM_STATE_CONNECTED) { _LOGI("emitting PREPARE_RESULT: %s", ret ? "TRUE" : "FALSE"); nm_modem_emit_prepare_result(NM_MODEM(self), @@ -912,30 +913,35 @@ context_property_changed(GDBusProxy *proxy, const char *property, GVariant *v, g handle_settings(v_dict, user_data); } -static NMActStageReturn -static_stage3_ip4_config_start(NMModem * modem, - NMActRequest * req, - NMDeviceStateReason *out_failure_reason) +static void +stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_method) { - NMModemOfono * self = NM_MODEM_OFONO(modem); - NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); - GError * error = NULL; - - if (!priv->ip4_config) { - _LOGD("IP4 config not ready(?)"); - return NM_ACT_STAGE_RETURN_FAILURE; - } + NMModemOfono * self = NM_MODEM_OFONO(modem); + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + gs_free_error GError *error = NULL; _LOGD("IP4 config is done; setting modem_state -> CONNECTED"); - g_signal_emit_by_name(self, NM_MODEM_IP4_CONFIG_RESULT, priv->ip4_config, error); - /* Signal listener takes ownership of the IP4Config */ - priv->ip4_config = NULL; + if (!NM_IS_IPv4(addr_family) || ip_method == NM_MODEM_IP_METHOD_AUTO) { + nm_modem_emit_signal_new_config_success(modem, addr_family, NULL, TRUE, NULL); + goto out; + } + if (!priv->l3cd_4) { + nm_utils_error_set(&error, NM_UTILS_ERROR_UNKNOWN, "IP config not received"); + nm_modem_emit_signal_new_config_failure(modem, + addr_family, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE, + error); + goto out; + } + + nm_modem_emit_signal_new_config_success(modem, addr_family, priv->l3cd_4, FALSE, NULL); + +out: nm_modem_set_state(NM_MODEM(self), NM_MODEM_STATE_CONNECTED, nm_modem_state_to_string(NM_MODEM_STATE_CONNECTED)); - return NM_ACT_STAGE_RETURN_POSTPONE; } static void @@ -1040,7 +1046,7 @@ context_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) * clear it so that we can gate getting the IP config from oFono * on whether or not we have already received them */ - g_clear_object(&priv->ip4_config); + nm_clear_l3cd(&priv->l3cd_4); /* We need to directly query ConnectionContextinteface to get the current * property values */ @@ -1258,7 +1264,7 @@ dispose(GObject *object) priv->connect_properties = NULL; } - g_clear_object(&priv->ip4_config); + nm_clear_l3cd(&priv->l3cd_4); if (priv->modem_proxy) { g_signal_handlers_disconnect_by_data(priv->modem_proxy, self); @@ -1300,6 +1306,6 @@ nm_modem_ofono_class_init(NMModemOfonoClass *klass) modem_class->deactivate_cleanup = deactivate_cleanup; modem_class->check_connection_compatible_with_modem = check_connection_compatible_with_modem; - modem_class->modem_act_stage1_prepare = modem_act_stage1_prepare; - modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start; + modem_class->modem_act_stage1_prepare = modem_act_stage1_prepare; + modem_class->stage3_ip_config_start = stage3_ip_config_start; } diff --git a/src/core/devices/wwan/nm-modem.c b/src/core/devices/wwan/nm-modem.c index ccea69db16..579f366c17 100644 --- a/src/core/devices/wwan/nm-modem.c +++ b/src/core/devices/wwan/nm-modem.c @@ -13,16 +13,16 @@ #include #include -#include "libnm-core-intern/nm-core-internal.h" -#include "libnm-platform/nm-platform.h" -#include "nm-setting-connection.h" #include "NetworkManagerUtils.h" #include "devices/nm-device-private.h" -#include "nm-netns.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "libnm-platform/nm-platform.h" #include "nm-act-request.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" +#include "nm-l3-config-data.h" +#include "nm-netns.h" +#include "nm-setting-connection.h" #include "ppp/nm-ppp-manager-call.h" +#include "ppp/nm-ppp-mgr.h" #include "ppp/nm-ppp-status.h" /*****************************************************************************/ @@ -45,8 +45,7 @@ enum { PPP_STATS, PPP_FAILED, PREPARE_RESULT, - IP4_CONFIG_RESULT, - IP6_CONFIG_RESULT, + NEW_CONFIG, AUTH_REQUESTED, AUTH_RESULT, REMOVED, @@ -56,6 +55,11 @@ enum { static guint signals[LAST_SIGNAL] = {0}; +typedef struct { + GSource *stage3_on_idle_source; + bool stage3_started : 1; +} IPData; + typedef struct _NMModemPrivate { char *uid; char *path; @@ -63,41 +67,38 @@ typedef struct _NMModemPrivate { char *control_port; char *data_port; - /* TODO: ip_iface is solely used for nm_modem_owns_port(). - * We should rework the code that it's not necessary */ - char *ip_iface; - - int ip_ifindex; - NMModemIPMethod ip4_method; - NMModemIPMethod ip6_method; - NMUtilsIPv6IfaceId iid; - NMModemState state; - NMModemState prev_state; /* revert to this state if enable/disable fails */ - char * device_id; - char * sim_id; - NMModemIPType ip_types; - char * sim_operator_id; - char * operator_code; - char * apn; + int ip_ifindex; + NMModemIPMethod ip4_method; + NMModemIPMethod ip6_method; + NMModemState state; + NMModemState prev_state; /* revert to this state if enable/disable fails */ + char * device_id; + char * sim_id; + NMModemIPType ip_types; + char * sim_operator_id; + char * operator_code; + char * apn; NMPPPManager *ppp_manager; + NMPppMgr * ppp_mgr; - NMActRequest * act_request; + NMActRequest * act_req; + NMDevice * device; guint32 secrets_tries; NMActRequestGetSecretsCallId *secrets_id; guint mm_ip_timeout; - guint32 ip4_route_table; - guint32 ip4_route_metric; - guint32 ip6_route_table; - guint32 ip6_route_metric; - - /* PPP stats */ - guint32 in_bytes; - guint32 out_bytes; - bool claimed : 1; + + union { + struct { + IPData ip_data_6; + IPData ip_data_4; + }; + IPData ip_data_x[2]; + }; + } NMModemPrivate; G_DEFINE_TYPE(NMModem, nm_modem, G_TYPE_OBJECT) @@ -149,7 +150,7 @@ _nmlog_prefix(char *prefix, NMModem *self) /*****************************************************************************/ -static void _set_ip_ifindex(NMModem *self, int ifindex, const char *ifname); +static void _set_ip_ifindex(NMModem *self, int ifindex); /*****************************************************************************/ /* State/enabled/connected */ @@ -180,6 +181,91 @@ nm_modem_state_to_string(NMModemState state) /*****************************************************************************/ +static NMPlatform * +_get_platform(NMModem *self) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE(self); + + if (!priv->device) + return NULL; + + return nm_device_get_platform(priv->device); +} + +/*****************************************************************************/ + +void +nm_modem_emit_signal_new_config(NMModem * self, + int addr_family, + const NML3ConfigData * l3cd, + gboolean do_auto, + const NMUtilsIPv6IfaceId *iid, + NMDeviceStateReason failure_reason, + GError * error) +{ + nm_assert(NM_IS_MODEM(self)); + nm_assert_addr_family(addr_family); + nm_assert(!l3cd || NM_IS_L3_CONFIG_DATA(l3cd)); + nm_assert(!do_auto || addr_family == AF_INET6); + nm_assert(!iid || addr_family == AF_INET6); + nm_assert(!error || (!l3cd && !do_auto && !iid)); + + if (error) { + _LOGD("signal: new-config: IPv%c, failed '%s', %s", + nm_utils_addr_family_to_char(addr_family), + nm_device_state_reason_to_string_a(failure_reason), + error->message); + } else { + gs_free char *str_to_free = NULL; + + _LOGD( + "signal: new-config: IPv%c%s%s%s%s", + nm_utils_addr_family_to_char(addr_family), + l3cd ? ", has-l3cd" : "", + do_auto ? ", do-auto" : "", + NM_PRINT_FMT_QUOTED2(iid, + ", iid=", + nm_utils_bin2hexstr_a(iid, sizeof(*iid), ':', FALSE, &str_to_free), + "")); + } + + g_signal_emit(self, + signals[NEW_CONFIG], + 0, + addr_family, + nm_l3_config_data_seal(l3cd), + do_auto, + iid, + (int) failure_reason, + error); +} + +void +nm_modem_emit_signal_new_config_success(NMModem * self, + int addr_family, + const NML3ConfigData * l3cd, + gboolean do_auto, + const NMUtilsIPv6IfaceId *iid) +{ + nm_modem_emit_signal_new_config(self, + addr_family, + l3cd, + do_auto, + iid, + NM_DEVICE_STATE_REASON_NONE, + NULL); +} + +void +nm_modem_emit_signal_new_config_failure(NMModem * self, + int addr_family, + NMDeviceStateReason failure_reason, + GError * error) +{ + nm_assert(error); + nm_modem_emit_signal_new_config(self, addr_family, NULL, FALSE, NULL, failure_reason, error); +} + gboolean nm_modem_is_claimed(NMModem *self) { @@ -240,10 +326,10 @@ nm_modem_set_state(NMModem *self, NMModemState new_state, const char *reason) priv->prev_state = NM_MODEM_STATE_UNKNOWN; if (new_state != old_state) { - _LOGI("modem state changed, '%s' --> '%s' (reason: %s)", + _LOGD("signal: modem state changed, '%s' --> '%s' (reason: %s%s%s)", nm_modem_state_to_string(old_state), nm_modem_state_to_string(new_state), - reason ?: "none"); + NM_PRINT_FMT_QUOTE_STRING(reason)); priv->state = new_state; _notify(self, PROP_STATE); @@ -285,7 +371,7 @@ nm_modem_set_mm_enabled(NMModem *self, gboolean enabled) /* Try to unlock the modem if it's being enabled */ if (enabled) - g_signal_emit(self, signals[AUTH_REQUESTED], 0); + nm_modem_emit_auth_requested(self); return; } @@ -303,14 +389,25 @@ nm_modem_set_mm_enabled(NMModem *self, gboolean enabled) void nm_modem_emit_removed(NMModem *self) { + _LOGD("signal: removed"); g_signal_emit(self, signals[REMOVED], 0); } +void +nm_modem_emit_auth_requested(NMModem *self) +{ + _LOGD("signal: auth-requested"); + g_signal_emit(self, signals[AUTH_REQUESTED], 0); +} + void nm_modem_emit_prepare_result(NMModem *self, gboolean success, NMDeviceStateReason reason) { nm_assert(NM_IS_MODEM(self)); + _LOGD("signal: prepare-result: %s (%s)", + success ? "success" : "failure", + nm_device_state_reason_to_string_a(reason)); g_signal_emit(self, signals[PREPARE_RESULT], 0, success, (guint) reason); } @@ -319,6 +416,7 @@ nm_modem_emit_ppp_failed(NMModem *self, NMDeviceStateReason reason) { nm_assert(NM_IS_MODEM(self)); + _LOGD("signal: ppp-failed (%s)", nm_device_state_reason_to_string_a(reason)); g_signal_emit(self, signals[PPP_FAILED], 0, (guint) reason); } @@ -372,7 +470,7 @@ nm_modem_get_connection_ip_type(NMModem *self, NMConnection *connection, GError s_ip4 = nm_connection_get_setting_ip4_config(connection); if (s_ip4) { method = nm_setting_ip_config_get_method(s_ip4); - if (g_strcmp0(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) + if (nm_streq0(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) ip4 = FALSE; ip4_may_fail = nm_setting_ip_config_get_may_fail(s_ip4); } @@ -487,76 +585,90 @@ nm_modem_get_apn(NMModem *self) } /*****************************************************************************/ -/* IP method PPP */ static void -ppp_state_changed(NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data) +_ppp_mgr_cleanup(NMModem *self) { - switch (status) { - case NM_PPP_STATUS_DISCONNECT: - nm_modem_emit_ppp_failed(user_data, NM_DEVICE_STATE_REASON_PPP_DISCONNECT); - break; - case NM_PPP_STATUS_DEAD: - nm_modem_emit_ppp_failed(user_data, NM_DEVICE_STATE_REASON_PPP_FAILED); - break; - default: - break; + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE(self); + + nm_clear_pointer(&priv->ppp_mgr, nm_ppp_mgr_destroy); +} + +static void +_ppp_maybe_emit_new_config(NMModem *self, int addr_family) +{ + NMModemPrivate * priv = NM_MODEM_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + const NMPppMgrIPData *ip_data; + gboolean do_auto; + + ip_data = nm_ppp_mgr_get_ip_data(priv->ppp_mgr, addr_family); + + if (!ip_data->ip_received) + return; + + if (IS_IPv4) + do_auto = FALSE; + else { + do_auto = !ip_data->l3cd + || (!nm_l3_config_data_get_first_obj(ip_data->l3cd, + NMP_OBJECT_TYPE_IP6_ADDRESS, + nmp_object_ip6_address_is_not_link_local)); } + + nm_assert(!IS_IPv4 || !ip_data->ipv6_iid); + + nm_modem_emit_signal_new_config_success(self, + addr_family, + ip_data->l3cd, + do_auto, + ip_data->ipv6_iid); } static void -ppp_ifindex_set(NMPPPManager *ppp_manager, int ifindex, const char *iface, gpointer user_data) +_ppp_mgr_callback(NMPppMgr *ppp_mgr, const NMPppMgrCallbackData *callback_data, gpointer user_data) { - NMModem *self = NM_MODEM(user_data); + NMModem * self = NM_MODEM(user_data); + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE(self); + int IS_IPv4; - nm_assert(ifindex >= 0); - nm_assert(NM_MODEM_GET_PRIVATE(self)->ppp_manager == ppp_manager); + switch (callback_data->callback_type) { + case NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED: - if (ifindex <= 0 && iface) { - /* this might happen, if the ifname was already deleted - * and we failed to resolve ifindex. - * - * Forget about the name. */ - iface = NULL; + if (callback_data->data.state >= _NM_PPP_MGR_STATE_FAILED_START) { + nm_modem_emit_ppp_failed(self, callback_data->data.reason); + return; + } + + if (callback_data->data.state >= NM_PPP_MGR_STATE_HAVE_IFINDEX) + _set_ip_ifindex(self, callback_data->data.ifindex); + + if (callback_data->data.state >= NM_PPP_MGR_STATE_HAVE_IP_CONFIG) { + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + if (!priv->ip_data_x[IS_IPv4].stage3_started) { + /* stage3 didn't yet start. We don't emit the IP signal yet. + * We will emit it together with stage3. */ + continue; + } + if (callback_data->data.ip_changed_x[IS_IPv4]) + _ppp_maybe_emit_new_config(self, IS_IPv4 ? AF_INET : AF_INET6); + } + } + return; + + case NM_PPP_MGR_CALLBACK_TYPE_STATS_CHANGED: + g_signal_emit(self, + signals[PPP_STATS], + 0, + (guint) callback_data->data.stats_data->in_bytes, + (guint) callback_data->data.stats_data->out_bytes); + return; } - _set_ip_ifindex(self, ifindex, iface); + + nm_assert_not_reached(); } -static void -ppp_ip4_config(NMPPPManager *ppp_manager, NMIP4Config *config, gpointer user_data) -{ - NMModem *self = NM_MODEM(user_data); - - g_signal_emit(self, signals[IP4_CONFIG_RESULT], 0, config, NULL); -} - -static void -ppp_ip6_config(NMPPPManager * ppp_manager, - const NMUtilsIPv6IfaceId *iid, - NMIP6Config * config, - gpointer user_data) -{ - NMModem *self = NM_MODEM(user_data); - - NM_MODEM_GET_PRIVATE(self)->iid = *iid; - - nm_modem_emit_ip6_config_result(self, config, NULL); -} - -static void -ppp_stats(NMPPPManager *ppp_manager, guint i_in_bytes, guint i_out_bytes, gpointer user_data) -{ - NMModem * self = NM_MODEM(user_data); - NMModemPrivate *priv = NM_MODEM_GET_PRIVATE(self); - guint32 in_bytes = i_in_bytes; - guint32 out_bytes = i_out_bytes; - - if (priv->in_bytes != in_bytes || priv->out_bytes != out_bytes) { - priv->in_bytes = in_bytes; - priv->out_bytes = out_bytes; - g_signal_emit(self, signals[PPP_STATS], 0, (guint) in_bytes, (guint) out_bytes); - } -} +/*****************************************************************************/ static gboolean port_speed_is_zero(const char *port) @@ -585,283 +697,108 @@ port_speed_is_zero(const char *port) return cfgetospeed(&options) == B0; } -static NMActStageReturn -ppp_stage3_ip_config_start(NMModem * self, - NMActRequest * req, - NMDeviceStateReason *out_failure_reason) +/*****************************************************************************/ + +static gboolean +_stage3_ip_config_start_on_idle(NMModem *self, int addr_family) { - NMModemPrivate *priv = NM_MODEM_GET_PRIVATE(self); - const char * ppp_name = NULL; - GError * error = NULL; - guint ip_timeout = 30; - guint baud_override = 0; + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE(self); + NMModemIPMethod ip_method; + NMConnection * connection; + const char * method; + gs_free_error GError *error = NULL; + NMDeviceStateReason failure_reason; - g_return_val_if_fail(NM_IS_MODEM(self), NM_ACT_STAGE_RETURN_FAILURE); - g_return_val_if_fail(NM_IS_ACT_REQUEST(req), NM_ACT_STAGE_RETURN_FAILURE); + nm_clear_g_source_inst(&priv->ip_data_x[IS_IPv4].stage3_on_idle_source); - /* If we're already running PPP don't restart it; for example, if both - * IPv4 and IPv6 are requested, IPv4 gets started first, but we use the - * same pppd for both v4 and v6. - */ - if (priv->ppp_manager) - return NM_ACT_STAGE_RETURN_POSTPONE; + connection = nm_act_request_get_applied_connection(priv->act_req); + g_return_val_if_fail(connection, G_SOURCE_CONTINUE); - if (NM_MODEM_GET_CLASS(self)->get_user_pass) { - NMConnection *connection = nm_act_request_get_applied_connection(req); + method = nm_utils_get_ip_config_method(connection, addr_family); - g_assert(connection); - if (!NM_MODEM_GET_CLASS(self)->get_user_pass(self, connection, &ppp_name, NULL)) - return NM_ACT_STAGE_RETURN_FAILURE; + if (IS_IPv4 ? NM_IN_STRSET(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) + : NM_IN_STRSET(method, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) { + nm_modem_emit_signal_new_config_success(self, addr_family, NULL, FALSE, NULL); + return G_SOURCE_CONTINUE; } - if (!priv->data_port) { - _LOGE("error starting PPP (no data port)"); - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED); - return NM_ACT_STAGE_RETURN_FAILURE; + if (!nm_streq(method, + IS_IPv4 ? NM_SETTING_IP4_CONFIG_METHOD_AUTO + : NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { + failure_reason = NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED; + nm_utils_error_set(&error, NM_UTILS_ERROR_UNKNOWN, "ip method unsupported by modem"); + goto out_failure; } - /* Check if ModemManager requested a specific IP timeout to be used. If 0 reported, - * use the default one (30s) */ - if (priv->mm_ip_timeout > 0) { - _LOGI("using modem-specified IP timeout: %u seconds", priv->mm_ip_timeout); - ip_timeout = priv->mm_ip_timeout; + ip_method = IS_IPv4 ? priv->ip4_method : priv->ip6_method; + + switch (ip_method) { + case NM_MODEM_IP_METHOD_PPP: + _ppp_maybe_emit_new_config(self, addr_family); + return G_SOURCE_CONTINUE; + case NM_MODEM_IP_METHOD_STATIC: + case NM_MODEM_IP_METHOD_AUTO: + NM_MODEM_GET_CLASS(self)->stage3_ip_config_start(self, addr_family, ip_method); + return G_SOURCE_CONTINUE; + default: + failure_reason = NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED; + nm_utils_error_set(&error, NM_UTILS_ERROR_UNKNOWN, "modem IP method unsupported"); + goto out_failure; } - /* Some tty drivers and modems ignore port speed, but pppd requires the - * port speed to be > 0 or it exits. If the port speed is 0 pass an - * explicit speed to pppd to prevent the exit. - * https://bugzilla.redhat.com/show_bug.cgi?id=1281731 - */ - if (port_speed_is_zero(priv->data_port)) - baud_override = 57600; + nm_assert_not_reached(); - priv->ppp_manager = nm_ppp_manager_create(priv->data_port, &error); +out_failure: + nm_modem_emit_signal_new_config_failure(self, addr_family, failure_reason, error); + return G_SOURCE_CONTINUE; +} - if (priv->ppp_manager) { - nm_ppp_manager_set_route_parameters(priv->ppp_manager, - priv->ip4_route_table, - priv->ip4_route_metric, - priv->ip6_route_table, - priv->ip6_route_metric); +static gboolean +_stage3_ip_config_start_on_idle_4(gpointer user_data) +{ + return _stage3_ip_config_start_on_idle(user_data, AF_INET); +} + +static gboolean +_stage3_ip_config_start_on_idle_6(gpointer user_data) +{ + return _stage3_ip_config_start_on_idle(user_data, AF_INET6); +} + +gboolean +nm_modem_stage3_ip_config_start(NMModem *self, int addr_family, NMDevice *device) +{ + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMModemPrivate *priv; + + g_return_val_if_fail(NM_IS_MODEM(self), FALSE); + g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); + + priv = NM_MODEM_GET_PRIVATE(self); + + g_return_val_if_fail(priv->device == device, FALSE); + + if (priv->ip_data_x[IS_IPv4].stage3_started) { + /* we already started. Nothing to do. */ + return FALSE; } - if (!priv->ppp_manager - || !nm_ppp_manager_start(priv->ppp_manager, - req, - ppp_name, - ip_timeout, - baud_override, - &error)) { - _LOGE("error starting PPP: %s", error->message); - g_error_free(error); - g_clear_object(&priv->ppp_manager); - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED); - return NM_ACT_STAGE_RETURN_FAILURE; - } + nm_assert(!priv->ppp_mgr + || nm_ppp_mgr_get_state(priv->ppp_mgr) >= NM_PPP_MGR_STATE_HAVE_IFINDEX); - g_signal_connect(priv->ppp_manager, - NM_PPP_MANAGER_SIGNAL_STATE_CHANGED, - G_CALLBACK(ppp_state_changed), - self); - g_signal_connect(priv->ppp_manager, - NM_PPP_MANAGER_SIGNAL_IFINDEX_SET, - G_CALLBACK(ppp_ifindex_set), - self); - g_signal_connect(priv->ppp_manager, - NM_PPP_MANAGER_SIGNAL_IP4_CONFIG, - G_CALLBACK(ppp_ip4_config), - self); - g_signal_connect(priv->ppp_manager, - NM_PPP_MANAGER_SIGNAL_IP6_CONFIG, - G_CALLBACK(ppp_ip6_config), - self); - g_signal_connect(priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATS, G_CALLBACK(ppp_stats), self); + priv->ip_data_x[IS_IPv4].stage3_started = TRUE; - return NM_ACT_STAGE_RETURN_POSTPONE; + priv->ip_data_x[IS_IPv4].stage3_on_idle_source = nm_g_idle_add_source( + IS_IPv4 ? _stage3_ip_config_start_on_idle_4 : _stage3_ip_config_start_on_idle_6, + self); + return TRUE; } /*****************************************************************************/ -NMActStageReturn -nm_modem_stage3_ip4_config_start(NMModem * self, - NMDevice * device, - gboolean * out_autoip4, - NMDeviceStateReason *out_failure_reason) -{ - NMModemPrivate * priv; - NMActRequest * req; - NMConnection * connection; - const char * method; - NMActStageReturn ret; - - _LOGD("ip4_config_start"); - - g_return_val_if_fail(NM_IS_MODEM(self), NM_ACT_STAGE_RETURN_FAILURE); - g_return_val_if_fail(NM_IS_DEVICE(device), NM_ACT_STAGE_RETURN_FAILURE); - nm_assert(out_autoip4 && !*out_autoip4); - - req = nm_device_get_act_request(device); - g_return_val_if_fail(req, NM_ACT_STAGE_RETURN_FAILURE); - - connection = nm_act_request_get_applied_connection(req); - g_return_val_if_fail(connection, NM_ACT_STAGE_RETURN_FAILURE); - - nm_modem_set_route_parameters_from_device(self, device); - - method = nm_utils_get_ip_config_method(connection, AF_INET); - - /* Only Disabled and Auto methods make sense for WWAN */ - if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) - return NM_ACT_STAGE_RETURN_SUCCESS; - - if (!nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { - _LOGE("unhandled WWAN IPv4 method '%s'; will fail", method); - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - priv = NM_MODEM_GET_PRIVATE(self); - switch (priv->ip4_method) { - case NM_MODEM_IP_METHOD_PPP: - ret = ppp_stage3_ip_config_start(self, req, out_failure_reason); - break; - case NM_MODEM_IP_METHOD_STATIC: - _LOGD("MODEM_IP_METHOD_STATIC"); - ret = - NM_MODEM_GET_CLASS(self)->static_stage3_ip4_config_start(self, req, out_failure_reason); - break; - case NM_MODEM_IP_METHOD_AUTO: - _LOGD("MODEM_IP_METHOD_AUTO"); - *out_autoip4 = TRUE; - ret = NM_ACT_STAGE_RETURN_SUCCESS; - break; - default: - _LOGI("IPv4 configuration disabled"); - ret = NM_ACT_STAGE_RETURN_IP_FAIL; - break; - } - - return ret; -} - -void -nm_modem_ip4_pre_commit(NMModem *modem, NMDevice *device, NMIP4Config *config) -{ - NMModemPrivate *priv = NM_MODEM_GET_PRIVATE(modem); - - /* If the modem has an ethernet-type data interface (ie, not PPP and thus - * not point-to-point) and IP config has a /32 prefix, then we assume that - * ARP will be pointless and we turn it off. - */ - if (priv->ip4_method == NM_MODEM_IP_METHOD_STATIC - || priv->ip4_method == NM_MODEM_IP_METHOD_AUTO) { - const NMPlatformIP4Address *address = nm_ip4_config_get_first_address(config); - - g_assert(address); - if (address->plen == 32) - nm_platform_link_change_flags(nm_device_get_platform(device), - nm_device_get_ip_ifindex(device), - IFF_NOARP, - TRUE); - } -} - -/*****************************************************************************/ - -void -nm_modem_emit_ip6_config_result(NMModem *self, NMIP6Config *config, GError *error) -{ - NMModemPrivate * priv = NM_MODEM_GET_PRIVATE(self); - NMDedupMultiIter ipconf_iter; - const NMPlatformIP6Address *addr; - gboolean do_slaac = TRUE; - - if (error) { - g_signal_emit(self, signals[IP6_CONFIG_RESULT], 0, NULL, FALSE, error); - return; - } - - if (config) { - /* If the IPv6 configuration only included a Link-Local address, then - * we have to run SLAAC to get the full IPv6 configuration. - */ - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, config, &addr) { - if (IN6_IS_ADDR_LINKLOCAL(&addr->address)) { - if (!priv->iid.id) - priv->iid.id = ((guint64 *) (&addr->address.s6_addr))[1]; - } else - do_slaac = FALSE; - } - } - g_assert(config || do_slaac); - - g_signal_emit(self, signals[IP6_CONFIG_RESULT], 0, config, do_slaac, NULL); -} - -static NMActStageReturn -stage3_ip6_config_request(NMModem *self, NMDeviceStateReason *out_failure_reason) -{ - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return NM_ACT_STAGE_RETURN_FAILURE; -} - -NMActStageReturn -nm_modem_stage3_ip6_config_start(NMModem * self, - NMDevice * device, - NMDeviceStateReason *out_failure_reason) -{ - NMModemPrivate * priv; - NMActRequest * req; - NMActStageReturn ret; - NMConnection * connection; - const char * method; - - g_return_val_if_fail(NM_IS_MODEM(self), NM_ACT_STAGE_RETURN_FAILURE); - - req = nm_device_get_act_request(device); - g_return_val_if_fail(req, NM_ACT_STAGE_RETURN_FAILURE); - - connection = nm_act_request_get_applied_connection(req); - g_return_val_if_fail(connection, NM_ACT_STAGE_RETURN_FAILURE); - - nm_modem_set_route_parameters_from_device(self, device); - - method = nm_utils_get_ip_config_method(connection, AF_INET6); - - /* Only Ignore, Disabled and Auto methods make sense for WWAN */ - if (NM_IN_STRSET(method, - NM_SETTING_IP6_CONFIG_METHOD_IGNORE, - NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) - return NM_ACT_STAGE_RETURN_IP_DONE; - - if (!nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { - _LOGW("unhandled WWAN IPv6 method '%s'; will fail", method); - NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - priv = NM_MODEM_GET_PRIVATE(self); - switch (priv->ip6_method) { - case NM_MODEM_IP_METHOD_PPP: - ret = ppp_stage3_ip_config_start(self, req, out_failure_reason); - break; - case NM_MODEM_IP_METHOD_STATIC: - case NM_MODEM_IP_METHOD_AUTO: - /* Both static and DHCP/Auto retrieve a base IP config from the modem - * which in the static case is the full config, and the DHCP/Auto case - * is just the IPv6LL address to use for SLAAC. - */ - ret = NM_MODEM_GET_CLASS(self)->stage3_ip6_config_request(self, out_failure_reason); - break; - default: - _LOGI("IPv6 configuration disabled"); - ret = NM_ACT_STAGE_RETURN_IP_FAIL; - break; - } - - return ret; -} - guint32 nm_modem_get_configured_mtu(NMDevice *self, NMDeviceMtuSource *out_source, gboolean *out_force) { @@ -910,7 +847,7 @@ cancel_get_secrets(NMModem *self) NMModemPrivate *priv = NM_MODEM_GET_PRIVATE(self); if (priv->secrets_id) - nm_act_request_cancel_secrets(priv->act_request, priv->secrets_id); + nm_act_request_cancel_secrets(priv->act_req, priv->secrets_id); } static void @@ -934,6 +871,8 @@ modem_secrets_cb(NMActRequest * req, if (error) _LOGW("modem-secrets: %s", error->message); + _LOGD("signal: auth-result: %s%s", + NM_PRINT_FMT_QUOTED2(error, "failed: ", error->message, "success")); g_signal_emit(self, signals[AUTH_RESULT], 0, error); } @@ -950,7 +889,7 @@ nm_modem_get_secrets(NMModem * self, if (request_new) flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW; - priv->secrets_id = nm_act_request_get_secrets(priv->act_request, + priv->secrets_id = nm_act_request_get_secrets(priv->act_req, FALSE, setting_name, flags, @@ -958,7 +897,7 @@ nm_modem_get_secrets(NMModem * self, modem_secrets_cb, self); g_return_if_fail(priv->secrets_id); - g_signal_emit(self, signals[AUTH_REQUESTED], 0); + nm_modem_emit_auth_requested(self); } /*****************************************************************************/ @@ -982,16 +921,19 @@ nm_modem_act_stage1_prepare(NMModem * self, const char * setting_name = NULL; NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION; NMConnection * connection; + NMDevice * device; g_return_val_if_fail(NM_IS_ACT_REQUEST(req), NM_ACT_STAGE_RETURN_FAILURE); - if (priv->act_request) - g_object_unref(priv->act_request); - priv->act_request = g_object_ref(req); + nm_g_object_ref_set(&priv->act_req, req); + device = nm_active_connection_get_device(NM_ACTIVE_CONNECTION(priv->act_req)); + g_return_val_if_fail(NM_IS_DEVICE(device), NM_ACT_STAGE_RETURN_FAILURE); connection = nm_act_request_get_applied_connection(req); g_return_val_if_fail(connection, NM_ACT_STAGE_RETURN_FAILURE); + nm_g_object_ref_set(&priv->device, device); + setting_name = nm_connection_need_secrets(connection, &hints); if (!setting_name) { nm_assert(!hints); @@ -1015,24 +957,99 @@ nm_modem_act_stage1_prepare(NMModem * self, modem_secrets_cb, self); g_return_val_if_fail(priv->secrets_id, NM_ACT_STAGE_RETURN_FAILURE); - g_signal_emit(self, signals[AUTH_REQUESTED], 0); + nm_modem_emit_auth_requested(self); return NM_ACT_STAGE_RETURN_POSTPONE; } /*****************************************************************************/ -void -nm_modem_act_stage2_config(NMModem *self) +NMActStageReturn +nm_modem_act_stage2_config(NMModem *self, NMDevice *device, NMDeviceStateReason *out_failure_reason) { NMModemPrivate *priv; + gboolean needs_ppp; - g_return_if_fail(NM_IS_MODEM(self)); + g_return_val_if_fail(NM_IS_MODEM(self), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail(NM_IS_DEVICE(device), NM_ACT_STAGE_RETURN_FAILURE); priv = NM_MODEM_GET_PRIVATE(self); + + g_return_val_if_fail(priv->device == device, NM_ACT_STAGE_RETURN_FAILURE); + /* Clear secrets tries counter since secrets were successfully used * already if we get here. */ priv->secrets_tries = 0; + + needs_ppp = + (priv->ip4_method == NM_MODEM_IP_METHOD_PPP || priv->ip6_method == NM_MODEM_IP_METHOD_PPP); + + if (needs_ppp && !priv->ppp_mgr) { + const char * ppp_name = NULL; + gs_free_error GError *error = NULL; + guint ip_timeout; + guint baud_override; + NMActRequest * req; + + req = nm_device_get_act_request(device); + g_return_val_if_fail(req, NM_ACT_STAGE_RETURN_FAILURE); + + if (NM_MODEM_GET_CLASS(self)->get_user_pass) { + NMConnection *connection = nm_act_request_get_applied_connection(req); + + g_return_val_if_fail(connection, NM_ACT_STAGE_RETURN_FAILURE); + if (!NM_MODEM_GET_CLASS(self)->get_user_pass(self, connection, &ppp_name, NULL)) + return NM_ACT_STAGE_RETURN_FAILURE; + } + + if (!priv->data_port) { + _LOGW("error starting PPP (no data port)"); + NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + /* Check if ModemManager requested a specific IP timeout to be used. If 0 reported, + * use the default one (30s) */ + if (priv->mm_ip_timeout > 0) { + _LOGI("using modem-specified IP timeout: %u seconds", priv->mm_ip_timeout); + ip_timeout = priv->mm_ip_timeout; + } else + ip_timeout = 30; + + /* Some tty drivers and modems ignore port speed, but pppd requires the + * port speed to be > 0 or it exits. If the port speed is 0 pass an + * explicit speed to pppd to prevent the exit. + * https://bugzilla.redhat.com/show_bug.cgi?id=1281731 + */ + if (port_speed_is_zero(priv->data_port)) + baud_override = 57600; + else + baud_override = 0; + + priv->ppp_mgr = nm_ppp_mgr_start(&((const NMPppMgrConfig){ + .netns = nm_device_get_netns(device), + .parent_iface = priv->data_port, + .callback = _ppp_mgr_callback, + .user_data = self, + .act_req = req, + .ppp_username = ppp_name, + .timeout_secs = ip_timeout, + .baud_override = baud_override, + }), + &error); + if (!priv->ppp_mgr) { + _LOGW("PPP failed to start: %s", error->message); + *out_failure_reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED; + return NM_ACT_STAGE_RETURN_FAILURE; + } + + return NM_ACT_STAGE_RETURN_POSTPONE; + } + + if (needs_ppp && nm_ppp_mgr_get_state(priv->ppp_mgr) < NM_PPP_MGR_STATE_HAVE_IFINDEX) + return NM_ACT_STAGE_RETURN_POSTPONE; + + return NM_ACT_STAGE_RETURN_SUCCESS; } /*****************************************************************************/ @@ -1126,27 +1143,26 @@ deactivate_cleanup(NMModem *self, NMDevice *device, gboolean stop_ppp_manager) { NMModemPrivate *priv; int ifindex; + int IS_IPv4; g_return_if_fail(NM_IS_MODEM(self)); priv = NM_MODEM_GET_PRIVATE(self); + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + priv->ip_data_x[IS_IPv4].stage3_started = FALSE; + nm_clear_g_source_inst(&priv->ip_data_x[IS_IPv4].stage3_on_idle_source); + } + priv->secrets_tries = 0; - if (priv->act_request) { + if (priv->act_req) { cancel_get_secrets(self); - g_object_unref(priv->act_request); - priv->act_request = NULL; + g_clear_object(&priv->act_req); } + g_clear_object(&priv->device); - priv->in_bytes = priv->out_bytes = 0; - - if (priv->ppp_manager) { - g_signal_handlers_disconnect_by_data(priv->ppp_manager, self); - if (stop_ppp_manager) - nm_ppp_manager_stop(priv->ppp_manager, NULL, NULL, NULL); - g_clear_object(&priv->ppp_manager); - } + _ppp_mgr_cleanup(self); if (device) { g_return_if_fail(NM_IS_DEVICE(device)); @@ -1170,7 +1186,7 @@ deactivate_cleanup(NMModem *self, NMDevice *device, gboolean stop_ppp_manager) priv->mm_ip_timeout = 0; priv->ip4_method = NM_MODEM_IP_METHOD_UNKNOWN; priv->ip6_method = NM_MODEM_IP_METHOD_UNKNOWN; - _set_ip_ifindex(self, -1, NULL); + _set_ip_ifindex(self, -1); } /*****************************************************************************/ @@ -1307,11 +1323,11 @@ nm_modem_device_state_changed(NMModem *self, NMDeviceState new_state, NMDeviceSt case NM_DEVICE_STATE_UNAVAILABLE: case NM_DEVICE_STATE_FAILED: case NM_DEVICE_STATE_DISCONNECTED: - if (priv->act_request) { + if (priv->act_req) { cancel_get_secrets(self); - g_object_unref(priv->act_request); - priv->act_request = NULL; + g_clear_object(&priv->act_req); } + g_clear_object(&priv->device); if (was_connected) { /* Don't bother warning on FAILED since the modem is already gone */ @@ -1377,19 +1393,14 @@ nm_modem_get_ip_ifindex(NMModem *self) } static void -_set_ip_ifindex(NMModem *self, int ifindex, const char *ifname) +_set_ip_ifindex(NMModem *self, int ifindex) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE(self); nm_assert(ifindex >= -1); - nm_assert((ifindex > 0) == !!ifname); - - if (!nm_streq0(priv->ip_iface, ifname)) { - g_free(priv->ip_iface); - priv->ip_iface = g_strdup(ifname); - } if (priv->ip_ifindex != ifindex) { + _LOGD("signal: ifindex changed: %d", ifindex); priv->ip_ifindex = ifindex; _notify(self, PROP_IP_IFINDEX); } @@ -1477,10 +1488,10 @@ nm_modem_set_data_port(NMModem * self, priv->ip6_method = ip6_method; if (is_ppp) { priv->data_port = g_strdup(data_port); - _set_ip_ifindex(self, -1, NULL); + _set_ip_ifindex(self, -1); } else { priv->data_port = NULL; - _set_ip_ifindex(self, ifindex, data_port); + _set_ip_ifindex(self, ifindex); } return TRUE; } @@ -1488,90 +1499,28 @@ nm_modem_set_data_port(NMModem * self, gboolean nm_modem_owns_port(NMModem *self, const char *iface) { - NMModemPrivate *priv = NM_MODEM_GET_PRIVATE(self); + NMModemPrivate * priv = NM_MODEM_GET_PRIVATE(self); + NMPlatform * platform; + const NMPlatformLink *plink; g_return_val_if_fail(iface != NULL, FALSE); if (NM_MODEM_GET_CLASS(self)->owns_port) return NM_MODEM_GET_CLASS(self)->owns_port(self, iface); - return NM_IN_STRSET(iface, priv->ip_iface, priv->data_port, priv->control_port); -} + if (NM_IN_STRSET(iface, priv->data_port, priv->control_port)) + return TRUE; -gboolean -nm_modem_get_iid(NMModem *self, NMUtilsIPv6IfaceId *out_iid) -{ - g_return_val_if_fail(NM_IS_MODEM(self), FALSE); + /* FIXME(parent-child-relationship): the whole notion of "owns-port" is wrong. + * When we have a name (iface) it must be always clear what this name is (which + * domain). Mixing data_port, control_port and devlink names is wrong. Looking + * up devlinks by name is also wrong (use ifindex). */ + if (priv->ip_ifindex > 0 && (platform = _get_platform(self)) + && (plink = nm_platform_link_get(platform, priv->ip_ifindex)) + && nm_streq(iface, plink->name)) + return TRUE; - *out_iid = NM_MODEM_GET_PRIVATE(self)->iid; - return TRUE; -} - -/*****************************************************************************/ - -void -nm_modem_get_route_parameters(NMModem *self, - guint32 *out_ip4_route_table, - guint32 *out_ip4_route_metric, - guint32 *out_ip6_route_table, - guint32 *out_ip6_route_metric) -{ - NMModemPrivate *priv; - - g_return_if_fail(NM_IS_MODEM(self)); - - priv = NM_MODEM_GET_PRIVATE(self); - NM_SET_OUT(out_ip4_route_table, priv->ip4_route_table); - NM_SET_OUT(out_ip4_route_metric, priv->ip4_route_metric); - NM_SET_OUT(out_ip6_route_table, priv->ip6_route_table); - NM_SET_OUT(out_ip6_route_metric, priv->ip6_route_metric); -} - -void -nm_modem_set_route_parameters(NMModem *self, - guint32 ip4_route_table, - guint32 ip4_route_metric, - guint32 ip6_route_table, - guint32 ip6_route_metric) -{ - NMModemPrivate *priv; - - g_return_if_fail(NM_IS_MODEM(self)); - - priv = NM_MODEM_GET_PRIVATE(self); - if (priv->ip4_route_table != ip4_route_table || priv->ip4_route_metric != ip4_route_metric - || priv->ip6_route_table != ip6_route_table || priv->ip6_route_metric != ip6_route_metric) { - priv->ip4_route_table = ip4_route_table; - priv->ip4_route_metric = ip4_route_metric; - priv->ip6_route_table = ip6_route_table; - priv->ip6_route_metric = ip6_route_metric; - - _LOGT("route-parameters: table-v4: %u, metric-v4: %u, table-v6: %u, metric-v6: %u", - priv->ip4_route_table, - priv->ip4_route_metric, - priv->ip6_route_table, - priv->ip6_route_metric); - } - - if (priv->ppp_manager) { - nm_ppp_manager_set_route_parameters(priv->ppp_manager, - priv->ip4_route_table, - priv->ip4_route_metric, - priv->ip6_route_table, - priv->ip6_route_metric); - } -} - -void -nm_modem_set_route_parameters_from_device(NMModem *self, NMDevice *device) -{ - g_return_if_fail(NM_IS_DEVICE(device)); - - nm_modem_set_route_parameters(self, - nm_device_get_route_table(device, AF_INET), - nm_device_get_route_metric(device, AF_INET), - nm_device_get_route_table(device, AF_INET6), - nm_device_get_route_metric(device, AF_INET6)); + return FALSE; } /*****************************************************************************/ @@ -1593,9 +1542,10 @@ _nm_modem_set_operator_code(NMModem *self, const char *operator_code) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE(self); - if (g_strcmp0(priv->operator_code, operator_code) != 0) { + if (!nm_streq0(priv->operator_code, operator_code)) { g_free(priv->operator_code); priv->operator_code = g_strdup(operator_code); + _LOGD("signal: operator-code changed: %s%s%s", NM_PRINT_FMT_QUOTE_STRING(operator_code)); _notify(self, PROP_OPERATOR_CODE); } } @@ -1605,9 +1555,10 @@ _nm_modem_set_apn(NMModem *self, const char *apn) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE(self); - if (g_strcmp0(priv->apn, apn) != 0) { + if (!nm_streq0(priv->apn, apn)) { g_free(priv->apn); priv->apn = g_strdup(apn); + _LOGD("signal: apn changed: %s%s%s", NM_PRINT_FMT_QUOTE_STRING(apn)); _notify(self, PROP_APN); } } @@ -1726,11 +1677,7 @@ nm_modem_init(NMModem *self) self->_priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_MODEM, NMModemPrivate); priv = self->_priv; - priv->ip_ifindex = -1; - priv->ip4_route_table = RT_TABLE_MAIN; - priv->ip4_route_metric = 700; - priv->ip6_route_table = RT_TABLE_MAIN; - priv->ip6_route_metric = 700; + priv->ip_ifindex = -1; } static void @@ -1752,7 +1699,8 @@ dispose(GObject *object) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE(object); - g_clear_object(&priv->act_request); + g_clear_object(&priv->act_req); + g_clear_object(&priv->device); G_OBJECT_CLASS(nm_modem_parent_class)->dispose(object); } @@ -1767,7 +1715,6 @@ finalize(GObject *object) g_free(priv->driver); g_free(priv->control_port); g_free(priv->data_port); - g_free(priv->ip_iface); g_free(priv->device_id); g_free(priv->sim_id); g_free(priv->sim_operator_id); @@ -1790,9 +1737,8 @@ nm_modem_class_init(NMModemClass *klass) object_class->dispose = dispose; object_class->finalize = finalize; - klass->modem_act_stage1_prepare = modem_act_stage1_prepare; - klass->stage3_ip6_config_request = stage3_ip6_config_request; - klass->deactivate_cleanup = deactivate_cleanup; + klass->modem_act_stage1_prepare = modem_act_stage1_prepare; + klass->deactivate_cleanup = deactivate_cleanup; obj_properties[PROP_UID] = g_param_spec_string(NM_MODEM_UID, @@ -1904,43 +1850,28 @@ nm_modem_class_init(NMModemClass *klass) 1, G_TYPE_UINT); - signals[IP4_CONFIG_RESULT] = g_signal_new(NM_MODEM_IP4_CONFIG_RESULT, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 2, - G_TYPE_OBJECT, - G_TYPE_POINTER); - - /** - * NMModem::ip6-config-result: - * @modem: the #NMModem on which the signal is emitted - * @config: the #NMIP6Config to apply to the modem's data port - * @do_slaac: %TRUE if IPv6 SLAAC should be started - * @error: a #GError if any error occurred during IP configuration - * - * This signal is emitted when IPv6 configuration has completed or failed. - * If @error is set the configuration failed. If @config is set, then + /* + * This signal is emitted when IP configuration has completed or failed. + * If @error is set the configuration failed. If @l3cd is set, then * the details should be applied to the data port before any further - * configuration (like SLAAC) is done. @do_slaac indicates whether SLAAC - * should be started after applying @config to the data port. + * configuration (like SLAAC) is done. @do_auto indicates whether DHCPv4/SLAAC + * should be started after applying @l3cd to the data port. */ - signals[IP6_CONFIG_RESULT] = g_signal_new(NM_MODEM_IP6_CONFIG_RESULT, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 3, - G_TYPE_OBJECT, - G_TYPE_BOOLEAN, - G_TYPE_POINTER); + signals[NEW_CONFIG] = g_signal_new(NM_MODEM_NEW_CONFIG, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 3, + G_TYPE_INT, /* int addr_family */ + G_TYPE_POINTER, /* const NML3ConfigData *l3cd */ + G_TYPE_BOOLEAN, /* gboolean do_auto */ + G_TYPE_POINTER, /* const NMUtilsIPv6IfaceId *iid */ + G_TYPE_INT, /* NMDeviceStateReason failure_reason */ + G_TYPE_POINTER); /* GError *error */ signals[PREPARE_RESULT] = g_signal_new(NM_MODEM_PREPARE_RESULT, G_OBJECT_CLASS_TYPE(object_class), diff --git a/src/core/devices/wwan/nm-modem.h b/src/core/devices/wwan/nm-modem.h index 4bc81ff8dd..31df805671 100644 --- a/src/core/devices/wwan/nm-modem.h +++ b/src/core/devices/wwan/nm-modem.h @@ -32,15 +32,14 @@ #define NM_MODEM_APN "apn" /* Signals */ -#define NM_MODEM_PPP_STATS "ppp-stats" -#define NM_MODEM_PPP_FAILED "ppp-failed" -#define NM_MODEM_PREPARE_RESULT "prepare-result" -#define NM_MODEM_IP4_CONFIG_RESULT "ip4-config-result" -#define NM_MODEM_IP6_CONFIG_RESULT "ip6-config-result" -#define NM_MODEM_AUTH_REQUESTED "auth-requested" -#define NM_MODEM_AUTH_RESULT "auth-result" -#define NM_MODEM_REMOVED "removed" -#define NM_MODEM_STATE_CHANGED "state-changed" +#define NM_MODEM_PPP_STATS "ppp-stats" +#define NM_MODEM_PPP_FAILED "ppp-failed" +#define NM_MODEM_PREPARE_RESULT "prepare-result" +#define NM_MODEM_NEW_CONFIG "new-config" +#define NM_MODEM_AUTH_REQUESTED "auth-requested" +#define NM_MODEM_AUTH_RESULT "auth-result" +#define NM_MODEM_REMOVED "removed" +#define NM_MODEM_STATE_CHANGED "state-changed" typedef enum { NM_MODEM_IP_METHOD_UNKNOWN = 0, @@ -124,15 +123,7 @@ typedef struct { NMConnection * connection, NMDeviceStateReason *out_failure_reason); - NMActStageReturn (*static_stage3_ip4_config_start)(NMModem * self, - NMActRequest * req, - NMDeviceStateReason *out_failure_reason); - - /* Request the IP6 config; when the config returns the modem - * subclass should emit the ip6_config_result signal. - */ - NMActStageReturn (*stage3_ip6_config_request)(NMModem * self, - NMDeviceStateReason *out_failure_reason); + void (*stage3_ip_config_start)(NMModem *self, int addr_family, NMModemIPMethod method); void (*set_mm_enabled)(NMModem *self, gboolean enabled); @@ -161,7 +152,6 @@ const char *nm_modem_get_driver(NMModem *modem); const char *nm_modem_get_device_id(NMModem *modem); const char *nm_modem_get_sim_id(NMModem *modem); const char *nm_modem_get_sim_operator_id(NMModem *modem); -gboolean nm_modem_get_iid(NMModem *modem, NMUtilsIPv6IfaceId *out_iid); const char *nm_modem_get_operator_code(NMModem *modem); const char *nm_modem_get_apn(NMModem *modem); @@ -188,36 +178,15 @@ gboolean nm_modem_complete_connection(NMModem * self, NMConnection *const *existing_connections, GError ** error); -void nm_modem_get_route_parameters(NMModem *self, - guint32 *out_ip4_route_table, - guint32 *out_ip4_route_metric, - guint32 *out_ip6_route_table, - guint32 *out_ip6_route_metric); - -void nm_modem_set_route_parameters(NMModem *self, - guint32 ip4_route_table, - guint32 ip4_route_metric, - guint32 ip6_route_table, - guint32 ip6_route_metric); - -void nm_modem_set_route_parameters_from_device(NMModem *modem, NMDevice *device); - NMActStageReturn nm_modem_act_stage1_prepare(NMModem * modem, NMActRequest * req, NMDeviceStateReason *out_failure_reason); -void nm_modem_act_stage2_config(NMModem *modem); +NMActStageReturn nm_modem_act_stage2_config(NMModem * self, + NMDevice * device, + NMDeviceStateReason *out_failure_reason); -NMActStageReturn nm_modem_stage3_ip4_config_start(NMModem * modem, - NMDevice * device, - gboolean * out_autoip4, - NMDeviceStateReason *out_failure_reason); - -NMActStageReturn nm_modem_stage3_ip6_config_start(NMModem * modem, - NMDevice * device, - NMDeviceStateReason *out_failure_reason); - -void nm_modem_ip4_pre_commit(NMModem *modem, NMDevice *device, NMIP4Config *config); +gboolean nm_modem_stage3_ip_config_start(NMModem *self, int addr_family, NMDevice *device); void nm_modem_get_secrets(NMModem * modem, const char *setting_name, @@ -249,6 +218,8 @@ NMModemIPType nm_modem_get_supported_ip_types(NMModem *self); /* For the modem-manager only */ void nm_modem_emit_removed(NMModem *self); +void nm_modem_emit_auth_requested(NMModem *self); + void nm_modem_emit_prepare_result(NMModem *self, gboolean success, NMDeviceStateReason reason); void nm_modem_emit_ppp_failed(NMModem *self, NMDeviceStateReason reason); @@ -256,7 +227,23 @@ void nm_modem_emit_ppp_failed(NMModem *self, NMDeviceStateReason reason); GArray *nm_modem_get_connection_ip_type(NMModem *self, NMConnection *connection, GError **error); /* For subclasses */ -void nm_modem_emit_ip6_config_result(NMModem *self, NMIP6Config *config, GError *error); + +void nm_modem_emit_signal_new_config(NMModem * self, + int addr_family, + const NML3ConfigData * l3cd, + gboolean do_slaac, + const NMUtilsIPv6IfaceId *iid, + NMDeviceStateReason failure_reason, + GError * error); +void nm_modem_emit_signal_new_config_success(NMModem * self, + int addr_family, + const NML3ConfigData * l3cd, + gboolean do_auto, + const NMUtilsIPv6IfaceId *iid); +void nm_modem_emit_signal_new_config_failure(NMModem * self, + int addr_family, + NMDeviceStateReason failure_reason, + GError * error); const char *nm_modem_ip_type_to_string(NMModemIPType ip_type); diff --git a/src/core/dhcp/README.next.md b/src/core/dhcp/README.next.md new file mode 100644 index 0000000000..88fa6683c7 --- /dev/null +++ b/src/core/dhcp/README.next.md @@ -0,0 +1,103 @@ +`NMDhcpClient` +============== + +Using `NMDhcpClient` still requires a lot of logic in `NMDevice`. The main goal +is to simplify `NMDevice`, so `NMDhcpClient` must become more complicated to +provide a simpler (but robust) API. + +NMDevice has basically two timeouts (talking about IPv4, but it applies +similarly to IPv6): `ipv4.dhcp-timeout` and `ipv4.required-timeout`. They +control how long NMDevice is willing to try, before failing the activation +altogether. Note that with `ipv4.may-fail=yes`, we may very well never want to +fail the activation entirely, regardless how DHCP is doing. In that case we +want to stay up, but also constantly retrying whether we cannot get a lease and +recover. + +Currently, if `NMDhcpClient` signals a failure, then it's basically up to +`NMDevice` to schedule and retry. That is complicated, and we should move the +complexity out of `NMDevice`. + +`NMDhcpClient` should have a simpler API: + +- `nm_dhcp_manager_start_ip[46]()`: creates (and starts) a `NMDhcpClient` + instance. The difference is, this function tries really hard not to fail + to create an `NMDhcpClient`. There is no explicit `start()`, but note that the + instance must not emit any signals before the next maincontext iteration. That is, + it only will call back the user after a timeout/idle or some other IO event, which + happens during a future iteration of the maincontext. + +- `nm_dhcp_client_stop()`: when `NMDevice` is done with the `NMDhcpClient` + instance, it will stop it and throw it away. This method exists because + `NMDhcpClient` is a `GObject` and ref-counted. Thus, we don't want to rely on + the last unref to stop the instance, but have an explicit stop. After stop, the + instance is defunct and won't emit any signals anymore. The class does not need + to support restarting a stopped instance. If `NMDevice` wants to restart DHCP, it + should create a new one. `NMDevice` would only want to do that, if the parameters + change, hence a new instance is in order (and no need for the complexity of + restart in `NMDhcpClient`). + +- as already now, `NMDhcpClient` is not very configurable. You provide most + (all) parameters during `nm_dhcp_manager_start_ip[46]()`, and then it keeps + running until stop. + +- `NMDhcpClient` exposes a simple state to the user: + + 1. "no lease, but good". When starting, there is no lease, but we are + optimistic to get one. This is the inital state, but we can also get back to + this state after we had a lease (which might expire). + + 1. "has a lease". Here there is no need to distinguish whether the current + lease was the first we received, or whether this was an update. In this state, + the instance has a lease and we are good. + + 1. "no lease, but bad". `NMDhcpClient` tries really hard, and "bad" does not + mean that it gave up. It will keep retrying, it's just that there is little + hope of getting a new lease. This happens, when you try to run DHCP on a Layer3 + link (WireGuard). There is little hope to succeed, but `NMDhcpClient` + (theoretically) will retry and may recover from this. Another example is when + we fail to start dhclient because it's not installed. In that case, we are not + optimistic to recover, however `NMDhcpDhclient` will retry (with backoff + timeout) and might still recover from this. For most cases, `NMDevice` will + treat the no-lease cases the same, but in case of "bad" it might give up + earlier. + +When a lease expires, that does not necessarily mean that we are now in a bad +state. It might mean that the DHCP server is temporarily down, but we might +recover from that easily. "bad" really means, something is wrong on our side +which prevents us from getting a lease. Also, imagine `dhclient` dies (we would +try to restart, but assume that fails too), but we still have a valid lease, +then possibly `NMDhcpClient` should still pretend all is good and we still have +a lease until it expires. It may be we can recover before that happens. The +point of all of this, is to hide errors as much as possibly and automatically +recover. `NMDevice` will decide to tear down, if we didn't get a lease after +`ipv4.dhcp-timeout`. That's the main criteria, and it might not even +distinguish between "no lease, but good" and "no lease, but bad". + +- `NMDhcpClient` will also take care of the `ipv4.dhcp-timeout` grace period. + That timeout is provided during start, and starts ticking whenever there is + no lease. When it expires, a timeout signal gets emitted. That's it. This is + independent from the 3 states above, and only saves `NMDevice` from scheduling + this timer themselves. + This is NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT notification. + +- for nettools, `nm_dhcp_client_can_accept()` indicates that when we receive a + lease, we need to accept/decline it first. In that case, `NMDevice` +optionally does ACD first, then configures the IP address first and calls +`nm_dhcp_client_accept()`. In case of ACD conflict, it will call +`nm_dhcp_client_decline()` (which optimally causes `NMDhcpClient` to get a +different lease). With this, the above state "has a lease" has actually three +flavors: "has a lease but not yet ACD probed" and "has a lease but +accepted/declined" (but `NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED` gets only emitted +when we get the lease, not when we accept/decline it). With `dhclient`, when we +receive a lease, it means "has a lease but accepted" right away. + +- for IPv6 prefix delegation, there is also `needed_prefixes` and + `NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED`. Currently `needed_prefixes` needs + to be specified during start (which simplifies things). Maybe `needed_prefixes` + should be changable at runtime. Otherwise, whether we have prefixes is similar + to whether we have a lease, and the simple 3 states apply. + +When NetworkManager quits, it may want to leave the interface up. In that case, +we still always want to stop the DHCP client, but possibly not deconfiguring +the interface. I don't think that this concerns `NMDhcpClient`, because `NMDhcpClient` +only provides the lease information and `NMDevice` is responsible to configure it. diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c index f88c79c0be..8b9e724968 100644 --- a/src/core/dhcp/nm-dhcp-client.c +++ b/src/core/dhcp/nm-dhcp-client.c @@ -20,9 +20,13 @@ #include "NetworkManagerUtils.h" #include "nm-utils.h" +#include "nm-l3cfg.h" +#include "nm-l3-config-data.h" #include "nm-dhcp-utils.h" #include "nm-dhcp-options.h" #include "libnm-platform/nm-platform.h" +#include "nm-hostname-manager.h" +#include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "nm-dhcp-client-logging.h" @@ -32,53 +36,20 @@ enum { SIGNAL_NOTIFY, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; -NM_GOBJECT_PROPERTIES_DEFINE(NMDhcpClient, - PROP_ADDR_FAMILY, - PROP_ANYCAST_ADDRESS, - PROP_FLAGS, - PROP_HWADDR, - PROP_BROADCAST_HWADDR, - PROP_IFACE, - PROP_IFINDEX, - PROP_MULTI_IDX, - PROP_ROUTE_METRIC, - PROP_ROUTE_TABLE, - PROP_TIMEOUT, - PROP_UUID, - PROP_IAID, - PROP_IAID_EXPLICIT, - PROP_HOSTNAME, - PROP_HOSTNAME_FLAGS, - PROP_MUD_URL, - PROP_VENDOR_CLASS_IDENTIFIER, - PROP_REJECT_SERVERS, ); +NM_GOBJECT_PROPERTIES_DEFINE(NMDhcpClient, PROP_CONFIG, ); typedef struct _NMDhcpClientPrivate { - NMDedupMultiIndex * multi_idx; - char * iface; - GBytes * hwaddr; - GBytes * bcast_hwaddr; - char * uuid; - GBytes * client_id; - char * hostname; - const char ** reject_servers; - char * mud_url; - char * anycast_address; - GBytes * vendor_class_identifier; - pid_t pid; - guint timeout_id; - guint watch_id; - int addr_family; - int ifindex; - guint32 route_table; - guint32 route_metric; - guint32 timeout; - guint32 iaid; - NMDhcpState state; - NMDhcpHostnameFlags hostname_flags; - NMDhcpClientFlags client_flags; - bool iaid_explicit : 1; - bool is_stopped : 1; + NMDhcpClientConfig config; + const NML3ConfigData *l3cd; + GSource * no_lease_timeout_source; + GSource * ipv6_lladdr_timeout_source; + pid_t pid; + guint timeout_id; + guint watch_id; + NMDhcpState state; + bool iaid_explicit : 1; + bool is_stopped : 1; + GBytes * effective_client_id; } NMDhcpClientPrivate; G_DEFINE_ABSTRACT_TYPE(NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT) @@ -98,25 +69,6 @@ _emit_notify(NMDhcpClient *self, const NMDhcpClientNotifyData *notify_data) g_signal_emit(G_OBJECT(self), signals[SIGNAL_NOTIFY], 0, notify_data); } -static void -_emit_notify_state_changed(NMDhcpClient *self, - NMDhcpState dhcp_state, - NMIPConfig * ip_config, - GHashTable * options) -{ - const NMDhcpClientNotifyData notify_data = { - .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_STATE_CHANGED, - .state_changed = - { - .dhcp_state = dhcp_state, - .ip_config = ip_config, - .options = options, - }, - }; - - _emit_notify(self, ¬ify_data); -} - /*****************************************************************************/ pid_t @@ -127,149 +79,24 @@ nm_dhcp_client_get_pid(NMDhcpClient *self) return NM_DHCP_CLIENT_GET_PRIVATE(self)->pid; } -NMDedupMultiIndex * -nm_dhcp_client_get_multi_idx(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->multi_idx; -} - -const char * -nm_dhcp_client_get_iface(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->iface; -} - -int -nm_dhcp_client_get_ifindex(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), -1); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->ifindex; -} - -int -nm_dhcp_client_get_addr_family(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), AF_UNSPEC); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->addr_family; -} - -const char * -nm_dhcp_client_get_uuid(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->uuid; -} - -GBytes * -nm_dhcp_client_get_hw_addr(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->hwaddr; -} - -GBytes * -nm_dhcp_client_get_broadcast_hw_addr(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->bcast_hwaddr; -} - -guint32 -nm_dhcp_client_get_route_table(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), RT_TABLE_MAIN); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->route_table; -} - -void -nm_dhcp_client_set_route_table(NMDhcpClient *self, guint32 route_table) -{ - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - if (route_table != priv->route_table) { - priv->route_table = route_table; - _notify(self, PROP_ROUTE_TABLE); - } -} - -guint32 -nm_dhcp_client_get_route_metric(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), G_MAXUINT32); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->route_metric; -} - -void -nm_dhcp_client_set_route_metric(NMDhcpClient *self, guint32 route_metric) -{ - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - if (route_metric != priv->route_metric) { - priv->route_metric = route_metric; - _notify(self, PROP_ROUTE_METRIC); - } -} - -guint32 -nm_dhcp_client_get_timeout(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), 0); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->timeout; -} - -guint32 -nm_dhcp_client_get_iaid(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), 0); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->iaid; -} - -gboolean -nm_dhcp_client_get_iaid_explicit(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->iaid_explicit; -} - -GBytes * -nm_dhcp_client_get_client_id(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->client_id; -} - static void -_set_client_id(NMDhcpClient *self, GBytes *client_id, gboolean take) +_set_effective_client_id(NMDhcpClient *self, GBytes *client_id, gboolean take) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); nm_assert(!client_id || g_bytes_get_size(client_id) >= 2); - if (priv->client_id == client_id - || (priv->client_id && client_id && g_bytes_equal(priv->client_id, client_id))) { + if (priv->effective_client_id == client_id + || (priv->effective_client_id && client_id + && g_bytes_equal(priv->effective_client_id, client_id))) { if (take && client_id) g_bytes_unref(client_id); return; } - if (priv->client_id) - g_bytes_unref(priv->client_id); - priv->client_id = client_id; + if (priv->effective_client_id) + g_bytes_unref(priv->effective_client_id); + priv->effective_client_id = client_id; if (!take && client_id) g_bytes_ref(client_id); @@ -277,94 +104,20 @@ _set_client_id(NMDhcpClient *self, GBytes *client_id, gboolean take) gs_free char *s = NULL; _LOGT("%s: set %s", - nm_dhcp_client_get_addr_family(self) == AF_INET6 ? "duid" : "client-id", - priv->client_id ? (s = nm_dhcp_utils_duid_to_string(priv->client_id)) : "default"); + priv->config.addr_family == AF_INET6 ? "duid" : "client-id", + priv->effective_client_id + ? (s = nm_dhcp_utils_duid_to_string(priv->effective_client_id)) + : "default"); } } void -nm_dhcp_client_set_client_id(NMDhcpClient *self, GBytes *client_id) +nm_dhcp_client_set_effective_client_id(NMDhcpClient *self, GBytes *client_id) { g_return_if_fail(NM_IS_DHCP_CLIENT(self)); g_return_if_fail(!client_id || g_bytes_get_size(client_id) >= 2); - _set_client_id(self, client_id, FALSE); -} - -void -nm_dhcp_client_set_client_id_bin(NMDhcpClient *self, - guint8 type, - const guint8 *client_id, - gsize len) -{ - guint8 *buf; - GBytes *b; - - g_return_if_fail(NM_IS_DHCP_CLIENT(self)); - g_return_if_fail(client_id); - g_return_if_fail(len > 0); - - buf = g_malloc(len + 1); - buf[0] = type; - memcpy(buf + 1, client_id, len); - b = g_bytes_new_take(buf, len + 1); - _set_client_id(self, b, TRUE); -} - -const char * -nm_dhcp_client_get_anycast_address(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->anycast_address; -} - -const char * -nm_dhcp_client_get_hostname(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->hostname; -} - -NMDhcpHostnameFlags -nm_dhcp_client_get_hostname_flags(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NM_DHCP_HOSTNAME_FLAG_NONE); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->hostname_flags; -} - -NMDhcpClientFlags -nm_dhcp_client_get_client_flags(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NM_DHCP_CLIENT_FLAGS_NONE); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->client_flags; -} - -const char * -nm_dhcp_client_get_mud_url(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->mud_url; -} - -GBytes * -nm_dhcp_client_get_vendor_class_identifier(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE(self)->vendor_class_identifier; -} - -const char *const * -nm_dhcp_client_get_reject_servers(NMDhcpClient *self) -{ - g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL); - - return (const char *const *) NM_DHCP_CLIENT_GET_PRIVATE(self)->reject_servers; + _set_effective_client_id(self, client_id, FALSE); } /*****************************************************************************/ @@ -457,78 +210,159 @@ stop(NMDhcpClient *self, gboolean release) if (priv->pid > 0) { /* Clean up the watch handler since we're explicitly killing the daemon */ watch_cleanup(self); - nm_dhcp_client_stop_pid(priv->pid, priv->iface); + nm_dhcp_client_stop_pid(priv->pid, priv->config.iface); } priv->pid = -1; } -void -nm_dhcp_client_set_state(NMDhcpClient *self, - NMDhcpState new_state, - NMIPConfig * ip_config, - GHashTable * options) +static gboolean +_no_lease_timeout(gpointer user_data) +{ + NMDhcpClient * self = 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, + })); + return G_SOURCE_CONTINUE; +} + +const NMDhcpClientConfig * +nm_dhcp_client_get_config(NMDhcpClient *self) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + return &priv->config; +} + +void +nm_dhcp_client_set_state(NMDhcpClient *self, NMDhcpState new_state, const NML3ConfigData *l3cd) +{ + NMDhcpClientPrivate * priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + GHashTable * options; + const int IS_IPv4 = NM_IS_IPv4(priv->config.addr_family); + nm_auto_unref_l3cd const NML3ConfigData *l3cd_merged = NULL; + + g_return_if_fail(NM_IS_DHCP_CLIENT(self)); + if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) { - g_return_if_fail(NM_IS_IP_CONFIG_ADDR_FAMILY(ip_config, priv->addr_family)); - g_return_if_fail(options); - } else { - g_return_if_fail(!ip_config); - g_return_if_fail(!options); - } + g_return_if_fail(NM_IS_L3_CONFIG_DATA(l3cd)); + g_return_if_fail(nm_l3_config_data_get_dhcp_lease(l3cd, priv->config.addr_family)); + } else + g_return_if_fail(!l3cd); + + if (l3cd) + nm_l3_config_data_seal(l3cd); if (new_state >= NM_DHCP_STATE_BOUND) timeout_cleanup(self); if (new_state >= NM_DHCP_STATE_TIMEOUT) watch_cleanup(self); - /* The client may send same-state transitions for RENEW/REBIND events and - * the lease may have changed, so handle same-state transitions for the - * EXTENDED and BOUND states. Ignore same-state transitions for other - * events since the lease won't have changed and the state was already handled. - */ - if ((priv->state == new_state) - && !NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) - return; - - if (_LOGD_ENABLED()) { - gs_free const char **keys = NULL; - guint i, nkeys; - - keys = nm_strdict_get_keys(options, TRUE, &nkeys); - for (i = 0; i < nkeys; i++) { - _LOGD("option %-20s => '%s'", keys[i], (char *) g_hash_table_lookup(options, keys[i])); + if (!IS_IPv4 && l3cd) { + if (nm_dhcp_utils_merge_new_dhcp6_lease(priv->l3cd, l3cd, &l3cd_merged)) { + l3cd = nm_l3_config_data_seal(l3cd_merged); } } - if (_LOGT_ENABLED() && priv->addr_family == AF_INET6) { - gs_free char *event_id = NULL; + if (priv->l3cd == l3cd) + return; - event_id = nm_dhcp_utils_get_dhcp6_event_id(options); - if (event_id) - _LOGT("event-id: \"%s\"", event_id); + if (l3cd) + nm_clear_g_source_inst(&priv->no_lease_timeout_source); + else { + /* FIXME(l3cfg:dhcp): we also need to start no_lease_timeout initially, if the + * instance starts without a previous_lease. */ + if (!priv->no_lease_timeout_source && priv->l3cd) { + if (priv->config.timeout == NM_DHCP_TIMEOUT_INFINITY) + priv->no_lease_timeout_source = g_source_ref(nm_g_source_sentinel_get(0)); + else { + priv->no_lease_timeout_source = + nm_g_timeout_add_seconds_source(priv->config.timeout, _no_lease_timeout, self); + } + } + } + + /* FIXME(l3cfg:dhcp): the API of NMDhcpClient is changing to expose a simpler API. + * The internals like NMDhcpState should not be exposed (or possibly dropped in large + * parts). */ + + nm_l3_config_data_reset(&priv->l3cd, l3cd); + + options = l3cd ? nm_dhcp_lease_get_options( + nm_l3_config_data_get_dhcp_lease(l3cd, priv->config.addr_family)) + : NULL; + + if (_LOGD_ENABLED()) { + if (options) { + gs_free const char **keys = NULL; + guint nkeys; + guint i; + + keys = nm_strdict_get_keys(options, TRUE, &nkeys); + for (i = 0; i < nkeys; i++) { + _LOGD("option %-20s => '%s'", + keys[i], + (char *) g_hash_table_lookup(options, keys[i])); + } + + if (priv->config.addr_family == AF_INET6) { + gs_free char *event_id = NULL; + + event_id = nm_dhcp_utils_get_dhcp6_event_id(options); + if (event_id) + _LOGT("event-id: \"%s\"", event_id); + } + } } if (_LOGI_ENABLED()) { const char *req_str = - NM_IS_IPv4(priv->addr_family) - ? nm_dhcp_option_request_string(AF_INET, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS) - : nm_dhcp_option_request_string(AF_INET6, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS); + IS_IPv4 ? nm_dhcp_option_request_string(AF_INET, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS) + : nm_dhcp_option_request_string(AF_INET6, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS); const char *addr = nm_g_hash_table_lookup(options, req_str); - _LOGI("state changed %s -> %s%s%s%s", - nm_dhcp_state_to_string(priv->state), - nm_dhcp_state_to_string(new_state), + _LOGI("state changed %s%s%s%s", + priv->l3cd ? "new lease" : "no lease", NM_PRINT_FMT_QUOTED(addr, ", address=", addr, "", "")); } - priv->state = new_state; + /* FIXME(l3cfg:dhcp:acd): NMDhcpClient must also do ACD. It needs acd_timeout_msec + * as a configuration parameter (in NMDhcpClientConfig). When ACD is enabled, + * when a new lease gets announced, it must first use NML3Cfg to run ACD on the + * interface (the previous lease -- if any -- will still be used at that point). + * If ACD fails, we call nm_dhcp_client_decline() and try to get a different + * lease. + * If ACD passes, we need to notify the new lease, and the user (NMDevice) may + * then configure the address. We need to watch the configured addresses (in NML3Cfg), + * and if the address appears there, we need to accept the lease. That is complicated + * but necessary, because we can only accept the lease after we configured the + * address. + * + * As a whole, ACD is transparent for the user (NMDevice). It's entirely managed + * by NMDhcpClient. Note that we do ACD through NML3Cfg, which centralizes IP handling + * for one interface, so for example if the same address happens to be configured + * as a static address (bypassing ACD), then NML3Cfg is aware of that and signals + * immediate success. */ - _emit_notify_state_changed(self, new_state, ip_config, options); + { + const NMDhcpClientNotifyData notify_data = { + .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE, + .lease_update = + { + .l3cd = priv->l3cd, + }, + }; + + _emit_notify(self, ¬ify_data); + } } -static gboolean +/* FIXME(l3cfg:dhcp) */ +_nm_unused static gboolean transaction_timeout(gpointer user_data) { NMDhcpClient * self = NM_DHCP_CLIENT(user_data); @@ -536,7 +370,7 @@ transaction_timeout(gpointer user_data) priv->timeout_id = 0; _LOGW("request timed out"); - nm_dhcp_client_set_state(self, NM_DHCP_STATE_TIMEOUT, NULL, NULL); + nm_dhcp_client_set_state(self, NM_DHCP_STATE_TIMEOUT, NULL); return G_SOURCE_REMOVE; } @@ -554,7 +388,7 @@ daemon_watch_cb(GPid pid, int status, gpointer user_data) priv->pid = -1; - nm_dhcp_client_set_state(self, NM_DHCP_STATE_TERMINATED, NULL, NULL); + nm_dhcp_client_set_state(self, NM_DHCP_STATE_TERMINATED, NULL); } void @@ -566,10 +400,10 @@ nm_dhcp_client_start_timeout(NMDhcpClient *self) /* Set up a timeout on the transaction to kill it after the timeout */ - if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) - return; - - priv->timeout_id = g_timeout_add_seconds(priv->timeout, transaction_timeout, self); + /* FIXME(l3cfg:dhcp): no-lease-timeout is only for convenience for the user (NMDevice). + * Whatever the purpose of nm_dhcp_client_start_timeout() is, it is not the same timer. */ + return; + //priv->timeout_id = g_timeout_add_seconds(priv->no_lease_timeout, transaction_timeout, self); } void @@ -599,10 +433,7 @@ nm_dhcp_client_stop_watch_child(NMDhcpClient *self, pid_t pid) } gboolean -nm_dhcp_client_start_ip4(NMDhcpClient *self, - GBytes * client_id, - const char * last_ip4_address, - GError ** error) +nm_dhcp_client_start_ip4(NMDhcpClient *self, GError **error) { NMDhcpClientPrivate *priv; @@ -610,24 +441,29 @@ nm_dhcp_client_start_ip4(NMDhcpClient *self, priv = NM_DHCP_CLIENT_GET_PRIVATE(self); g_return_val_if_fail(priv->pid == -1, FALSE); - g_return_val_if_fail(priv->addr_family == AF_INET, FALSE); - g_return_val_if_fail(priv->uuid != NULL, FALSE); + g_return_val_if_fail(priv->config.addr_family == AF_INET, FALSE); + g_return_val_if_fail(priv->config.uuid, FALSE); - if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) + if (priv->config.timeout == NM_DHCP_TIMEOUT_INFINITY) _LOGI("activation: beginning transaction (no timeout)"); else - _LOGI("activation: beginning transaction (timeout in %u seconds)", (guint) priv->timeout); + _LOGI("activation: beginning transaction (timeout in %u seconds)", + (guint) priv->config.timeout); - nm_dhcp_client_set_client_id(self, client_id); - - return NM_DHCP_CLIENT_GET_CLASS(self)->ip4_start(self, last_ip4_address, error); + return NM_DHCP_CLIENT_GET_CLASS(self)->ip4_start(self, error); } gboolean nm_dhcp_client_accept(NMDhcpClient *self, GError **error) { + NMDhcpClientPrivate *priv; + g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE); + priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + g_return_val_if_fail(priv->l3cd, FALSE); + if (NM_DHCP_CLIENT_GET_CLASS(self)->accept) { return NM_DHCP_CLIENT_GET_CLASS(self)->accept(self, error); } @@ -652,8 +488,14 @@ nm_dhcp_client_can_accept(NMDhcpClient *self) gboolean nm_dhcp_client_decline(NMDhcpClient *self, const char *error_message, GError **error) { + NMDhcpClientPrivate *priv; + g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE); + priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + g_return_val_if_fail(priv->l3cd, FALSE); + if (NM_DHCP_CLIENT_GET_CLASS(self)->decline) { return NM_DHCP_CLIENT_GET_CLASS(self)->decline(self, error_message, error); } @@ -667,43 +509,115 @@ get_duid(NMDhcpClient *self) return NULL; } +static gboolean +ipv6_lladdr_timeout(gpointer user_data) +{ + NMDhcpClient * self = user_data; + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + nm_clear_g_source_inst(&priv->ipv6_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", + })); + return G_SOURCE_CONTINUE; +} + +static const NMPlatformIP6Address * +ipv6_lladdr_find(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + NML3Cfg * l3cfg; + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPObject * obj; + + l3cfg = priv->config.l3cfg; + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, nm_l3cfg_get_ifindex(l3cfg)); + + nm_platform_iter_obj_for_each (&iter, nm_l3cfg_get_platform(l3cfg), &lookup, &obj) { + const NMPlatformIP6Address *pladdr = NMP_OBJECT_CAST_IP6_ADDRESS(obj); + + if (!IN6_IS_ADDR_LINKLOCAL(&pladdr->address)) + continue; + if (NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_TENTATIVE) + && !NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_OPTIMISTIC)) + continue; + return pladdr; + } + return NULL; +} + +static void +l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + nm_assert(l3cfg == priv->config.l3cfg); + + if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE) { + const NMPlatformIP6Address *addr; + gs_free_error GError *error = NULL; + + addr = ipv6_lladdr_find(self); + if (addr) { + _LOGD("got IPv6LL address, starting transaction"); + g_signal_handlers_disconnect_by_func(l3cfg, l3_cfg_notify_cb, self); + nm_clear_g_source_inst(&priv->ipv6_lladdr_timeout_source); + + 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, + })); + } + } + } +} + gboolean -nm_dhcp_client_start_ip6(NMDhcpClient * self, - GBytes * client_id, - gboolean enforce_duid, - const struct in6_addr * ll_addr, - NMSettingIP6ConfigPrivacy privacy, - guint needed_prefixes, - GError ** error) +nm_dhcp_client_start_ip6(NMDhcpClient *self, GError **error) { NMDhcpClientPrivate *priv; - gs_unref_bytes GBytes *own_client_id = NULL; + gs_unref_bytes GBytes * own_client_id = NULL; + const NMPlatformIP6Address *addr; g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE); - g_return_val_if_fail(client_id, FALSE); - priv = NM_DHCP_CLIENT_GET_PRIVATE(self); g_return_val_if_fail(priv->pid == -1, FALSE); - g_return_val_if_fail(priv->addr_family == AF_INET6, FALSE); - g_return_val_if_fail(priv->uuid != NULL, FALSE); - g_return_val_if_fail(!priv->client_id, FALSE); + g_return_val_if_fail(priv->config.addr_family == AF_INET6, FALSE); + g_return_val_if_fail(priv->config.uuid, FALSE); + g_return_val_if_fail(!priv->effective_client_id, FALSE); - if (!enforce_duid) + if (!priv->config.v6.enforce_duid) own_client_id = NM_DHCP_CLIENT_GET_CLASS(self)->get_duid(self); - _set_client_id(self, own_client_id ?: client_id, FALSE); + _set_effective_client_id(self, own_client_id ?: priv->config.client_id, FALSE); - if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) + if (priv->config.timeout == NM_DHCP_TIMEOUT_INFINITY) _LOGI("activation: beginning transaction (no timeout)"); else - _LOGI("activation: beginning transaction (timeout in %u seconds)", (guint) priv->timeout); + _LOGI("activation: beginning transaction (timeout in %u seconds)", + (guint) priv->config.timeout); - return NM_DHCP_CLIENT_GET_CLASS(self)->ip6_start(self, - ll_addr, - privacy, - needed_prefixes, - error); + addr = ipv6_lladdr_find(self); + if (!addr) { + _LOGD("waiting for IPv6LL address"); + g_signal_connect(priv->config.l3cfg, + NM_L3CFG_SIGNAL_NOTIFY, + G_CALLBACK(l3_cfg_notify_cb), + self); + priv->ipv6_lladdr_timeout_source = + nm_g_timeout_add_seconds_source(10, ipv6_lladdr_timeout, self); + return TRUE; + } + + return NM_DHCP_CLIENT_GET_CLASS(self)->ip6_start(self, &addr->address, error); } void @@ -780,6 +694,8 @@ nm_dhcp_client_stop(NMDhcpClient *self, gboolean release) priv->is_stopped = TRUE; + g_signal_handlers_disconnect_by_func(priv->config.l3cfg, l3_cfg_notify_cb, self); + /* Kill the DHCP client */ old_pid = priv->pid; NM_DHCP_CLIENT_GET_CLASS(self)->stop(self, release); @@ -789,7 +705,7 @@ nm_dhcp_client_stop(NMDhcpClient *self, gboolean release) _LOGI("canceled DHCP transaction"); nm_assert(priv->pid == -1); - nm_dhcp_client_set_state(self, NM_DHCP_STATE_TERMINATED, NULL, NULL); + nm_dhcp_client_set_state(self, NM_DHCP_STATE_TERMINATED, NULL); } /*****************************************************************************/ @@ -921,12 +837,10 @@ nm_dhcp_client_handle_event(gpointer unused, const char * reason, NMDhcpClient *self) { - NMDhcpClientPrivate *priv; - guint32 old_state; - guint32 new_state; - gs_unref_hashtable GHashTable *str_options = NULL; - gs_unref_object NMIPConfig *ip_config = NULL; - NMPlatformIP6Address prefix = { + NMDhcpClientPrivate * priv; + guint32 new_state; + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + NMPlatformIP6Address prefix = { 0, }; @@ -938,25 +852,26 @@ nm_dhcp_client_handle_event(gpointer unused, priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - if (g_strcmp0(priv->iface, iface) != 0) + if (!nm_streq0(priv->config.iface, iface)) return FALSE; if (priv->pid != pid) return FALSE; - old_state = priv->state; - new_state = reason_to_state(self, priv->iface, reason); - _LOGD("DHCP state '%s' -> '%s' (reason: '%s')", - nm_dhcp_state_to_string(old_state), - nm_dhcp_state_to_string(new_state), - reason); - + new_state = reason_to_state(self, priv->config.iface, reason); if (new_state == NM_DHCP_STATE_NOOP) return TRUE; + _LOGD("DHCP state '%s' -> '%s' (reason: '%s')", + nm_dhcp_state_to_string(priv->state), + nm_dhcp_state_to_string(new_state), + reason); + priv->state = new_state; + if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) { - GVariantIter iter; - const char * name; - GVariant * value; + gs_unref_hashtable GHashTable *str_options = NULL; + GVariantIter iter; + const char * name; + GVariant * value; /* Copy options */ str_options = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); @@ -968,25 +883,29 @@ nm_dhcp_client_handle_event(gpointer unused, /* Create the IP config */ if (g_hash_table_size(str_options) > 0) { - if (priv->addr_family == AF_INET) { - ip_config = NM_IP_CONFIG_CAST( - nm_dhcp_utils_ip4_config_from_options(nm_dhcp_client_get_multi_idx(self), - priv->ifindex, - priv->iface, - str_options, - priv->route_table, - priv->route_metric)); + if (priv->config.addr_family == AF_INET) { + l3cd = nm_dhcp_utils_ip4_config_from_options( + nm_l3cfg_get_multi_idx(priv->config.l3cfg), + nm_l3cfg_get_ifindex(priv->config.l3cfg), + priv->config.iface, + str_options); } else { - prefix = nm_dhcp_utils_ip6_prefix_from_options(str_options); - ip_config = NM_IP_CONFIG_CAST(nm_dhcp_utils_ip6_config_from_options( - nm_dhcp_client_get_multi_idx(self), - priv->ifindex, - priv->iface, + prefix = nm_dhcp_utils_ip6_prefix_from_options(str_options); + l3cd = nm_dhcp_utils_ip6_config_from_options( + nm_l3cfg_get_multi_idx(priv->config.l3cfg), + nm_l3cfg_get_ifindex(priv->config.l3cfg), + priv->config.iface, str_options, - NM_FLAGS_HAS(priv->client_flags, NM_DHCP_CLIENT_FLAGS_INFO_ONLY))); + priv->config.v6.info_only); } } else g_warn_if_reached(); + + if (l3cd) { + nm_l3_config_data_set_dhcp_lease_from_options(l3cd, + priv->config.addr_family, + g_steal_pointer(&str_options)); + } } if (!IN6_IS_ADDR_UNSPECIFIED(&prefix.address)) { @@ -994,17 +913,16 @@ nm_dhcp_client_handle_event(gpointer unused, * of the DHCP client instance. Instead, we just signal the prefix * to the device. */ nm_dhcp_client_emit_ipv6_prefix_delegated(self, &prefix); - } else { - /* Fail if no valid IP config was received */ - if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED) && !ip_config) { - _LOGW("client bound but IP config not received"); - new_state = NM_DHCP_STATE_FAIL; - nm_clear_pointer(&str_options, g_hash_table_unref); - } - - nm_dhcp_client_set_state(self, new_state, ip_config, str_options); + return TRUE; } + /* Fail if no valid IP config was received */ + if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED) && !l3cd) { + _LOGW("client bound but IP config not received"); + new_state = NM_DHCP_STATE_FAIL; + } + + nm_dhcp_client_set_state(self, new_state, l3cd); return TRUE; } @@ -1016,18 +934,18 @@ nm_dhcp_client_server_id_is_rejected(NMDhcpClient *self, gconstpointer addr) guint i; /* IPv6 not implemented yet */ - nm_assert(priv->addr_family == AF_INET); + nm_assert(priv->config.addr_family == AF_INET); - if (!priv->reject_servers || !priv->reject_servers[0]) + if (!priv->config.reject_servers || !priv->config.reject_servers[0]) return FALSE; - for (i = 0; priv->reject_servers[i]; i++) { + for (i = 0; priv->config.reject_servers[i]; i++) { in_addr_t r_addr; in_addr_t mask; int r_prefix; if (!nm_utils_parse_inaddr_prefix_bin(AF_INET, - priv->reject_servers[i], + priv->config.reject_servers[i], NULL, &r_addr, &r_prefix)) @@ -1040,146 +958,146 @@ nm_dhcp_client_server_id_is_rejected(NMDhcpClient *self, gconstpointer addr) return FALSE; } -/*****************************************************************************/ - static void -get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +config_init(NMDhcpClientConfig *config, const NMDhcpClientConfig *src) { - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(object); + *config = *src; - switch (prop_id) { - case PROP_IFACE: - g_value_set_string(value, priv->iface); - break; - case PROP_IFINDEX: - g_value_set_int(value, priv->ifindex); - break; - case PROP_HWADDR: - g_value_set_boxed(value, priv->hwaddr); - break; - case PROP_BROADCAST_HWADDR: - g_value_set_boxed(value, priv->bcast_hwaddr); - break; - case PROP_ADDR_FAMILY: - g_value_set_int(value, priv->addr_family); - break; - case PROP_UUID: - g_value_set_string(value, priv->uuid); - break; - case PROP_IAID: - g_value_set_uint(value, priv->iaid); - break; - case PROP_IAID_EXPLICIT: - g_value_set_boolean(value, priv->iaid_explicit); - break; - case PROP_HOSTNAME: - g_value_set_string(value, priv->hostname); - break; - case PROP_ROUTE_METRIC: - g_value_set_uint(value, priv->route_metric); - break; - case PROP_ROUTE_TABLE: - g_value_set_uint(value, priv->route_table); - break; - case PROP_TIMEOUT: - g_value_set_uint(value, priv->timeout); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; + g_object_ref(config->l3cfg); + + if (config->hwaddr) + g_bytes_ref(config->hwaddr); + if (config->bcast_hwaddr) + g_bytes_ref(config->bcast_hwaddr); + if (config->vendor_class_identifier) + g_bytes_ref(config->vendor_class_identifier); + if (config->client_id) + g_bytes_ref(config->client_id); + + config->iface = g_strdup(config->iface); + config->uuid = g_strdup(config->uuid); + config->anycast_address = g_strdup(config->anycast_address); + config->hostname = g_strdup(config->hostname); + config->mud_url = g_strdup(config->mud_url); + + config->reject_servers = (const char *const *) nm_strv_dup(config->reject_servers, -1, TRUE); + + if (config->addr_family == AF_INET) { + config->v4.last_address = g_strdup(config->v4.last_address); + } else if (config->addr_family == AF_INET6) { + config->hwaddr = NULL; + config->bcast_hwaddr = NULL; + config->use_fqdn = TRUE; + } else { + nm_assert_not_reached(); + } + + if (!config->hostname && config->send_hostname) { + const char * hostname; + gs_free char *hostname_tmp = NULL; + + hostname = nm_hostname_manager_get_hostname(nm_hostname_manager_get()); + + if (nm_utils_is_specific_hostname(hostname)) { + if (config->addr_family == AF_INET) { + char *dot; + + hostname_tmp = g_strdup(hostname); + dot = strchr(hostname_tmp, '.'); + if (dot) + *dot = '\0'; + } + config->hostname = hostname_tmp ? g_steal_pointer(&hostname_tmp) : g_strdup(hostname); + } + } + + if (config->hostname) { + if ((config->use_fqdn && !nm_sd_dns_name_is_valid(config->hostname)) + || (!config->use_fqdn && !nm_sd_hostname_is_valid(config->hostname, FALSE))) { + nm_log_warn(LOGD_DHCP, + "dhcp%c: %s '%s' is invalid, will be ignored", + nm_utils_addr_family_to_char(config->addr_family), + config->use_fqdn ? "FQDN" : "hostname", + config->hostname); + nm_clear_g_free((gpointer *) &config->hostname); + } } } +static void +config_clear(NMDhcpClientConfig *config) +{ + g_object_unref(config->l3cfg); + + 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); + nm_clear_pointer(&config->client_id, g_bytes_unref); + + nm_clear_g_free((gpointer *) &config->iface); + nm_clear_g_free((gpointer *) &config->uuid); + nm_clear_g_free((gpointer *) &config->anycast_address); + nm_clear_g_free((gpointer *) &config->hostname); + nm_clear_g_free((gpointer *) &config->mud_url); + + nm_clear_pointer((gpointer *) &config->reject_servers, g_strfreev); + + if (config->addr_family == AF_INET) { + nm_clear_g_free((gpointer *) &config->v4.last_address); + } +} + +int +nm_dhcp_client_get_addr_family(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + return priv->config.addr_family; +} + +const char * +nm_dhcp_client_get_iface(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + return priv->config.iface; +} + +NMDedupMultiIndex * +nm_dhcp_client_get_multi_idx(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + return nm_l3cfg_get_multi_idx(priv->config.l3cfg); +} + +int +nm_dhcp_client_get_ifindex(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + return nm_l3cfg_get_ifindex(priv->config.l3cfg); +} + +GBytes * +nm_dhcp_client_get_effective_client_id(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + return priv->effective_client_id; +} + +/*****************************************************************************/ + static void set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(object); - guint flags; switch (prop_id) { - case PROP_FLAGS: + case PROP_CONFIG: /* construct-only */ - flags = g_value_get_uint(value); - nm_assert(!NM_FLAGS_ANY(flags, ~((guint) NM_DHCP_CLIENT_FLAGS_ALL))); - priv->client_flags = flags; - break; - case PROP_MULTI_IDX: - /* construct-only */ - priv->multi_idx = g_value_get_pointer(value); - if (!priv->multi_idx) - g_return_if_reached(); - nm_dedup_multi_index_ref(priv->multi_idx); - break; - case PROP_IFACE: - /* construct-only */ - priv->iface = g_value_dup_string(value); - g_return_if_fail(priv->iface); - nm_assert(nm_utils_ifname_valid_kernel(priv->iface, NULL)); - break; - case PROP_IFINDEX: - /* construct-only */ - priv->ifindex = g_value_get_int(value); - g_return_if_fail(priv->ifindex > 0); - break; - case PROP_HWADDR: - /* construct-only */ - priv->hwaddr = g_value_dup_boxed(value); - break; - case PROP_ANYCAST_ADDRESS: - /* construct-only */ - priv->anycast_address = g_value_dup_string(value); - break; - case PROP_BROADCAST_HWADDR: - /* construct-only */ - priv->bcast_hwaddr = g_value_dup_boxed(value); - break; - case PROP_ADDR_FAMILY: - /* construct-only */ - priv->addr_family = g_value_get_int(value); - if (!NM_IN_SET(priv->addr_family, AF_INET, AF_INET6)) - g_return_if_reached(); - break; - case PROP_UUID: - /* construct-only */ - priv->uuid = g_value_dup_string(value); - break; - case PROP_IAID: - /* construct-only */ - priv->iaid = g_value_get_uint(value); - break; - case PROP_IAID_EXPLICIT: - /* construct-only */ - priv->iaid_explicit = g_value_get_boolean(value); - break; - case PROP_HOSTNAME: - /* construct-only */ - priv->hostname = g_value_dup_string(value); - break; - case PROP_HOSTNAME_FLAGS: - /* construct-only */ - priv->hostname_flags = g_value_get_uint(value); - break; - case PROP_MUD_URL: - /* construct-only */ - priv->mud_url = g_value_dup_string(value); - break; - case PROP_ROUTE_TABLE: - priv->route_table = g_value_get_uint(value); - break; - case PROP_ROUTE_METRIC: - priv->route_metric = g_value_get_uint(value); - break; - case PROP_TIMEOUT: - /* construct-only */ - priv->timeout = g_value_get_uint(value); - break; - case PROP_VENDOR_CLASS_IDENTIFIER: - /* construct-only */ - priv->vendor_class_identifier = g_value_dup_boxed(value); - break; - case PROP_REJECT_SERVERS: - /* construct-only */ - priv->reject_servers = nm_strv_dup_packed(g_value_get_boxed(value), -1); + config_init(&priv->config, g_value_get_pointer(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -1200,28 +1118,6 @@ nm_dhcp_client_init(NMDhcpClient *self) priv->pid = -1; } -#if NM_MORE_ASSERTS -static void -constructed(GObject *object) -{ - NMDhcpClient * self = NM_DHCP_CLIENT(object); - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - /* certain flags only make sense with certain address family. Assert - * for that. */ - if (NM_IS_IPv4(priv->addr_family)) - nm_assert(!NM_FLAGS_ANY(priv->client_flags, NM_DHCP_CLIENT_FLAGS_INFO_ONLY)); - else { - nm_assert(NM_FLAGS_HAS(priv->client_flags, NM_DHCP_CLIENT_FLAGS_USE_FQDN)); - nm_assert(!NM_FLAGS_ANY(priv->client_flags, NM_DHCP_CLIENT_FLAGS_REQUEST_BROADCAST)); - } - - nm_assert(!priv->anycast_address || nm_utils_hwaddr_valid(priv->anycast_address, ETH_ALEN)); - - G_OBJECT_CLASS(nm_dhcp_client_parent_class)->constructed(object); -} -#endif - static void dispose(GObject *object) { @@ -1233,20 +1129,21 @@ dispose(GObject *object) watch_cleanup(self); timeout_cleanup(self); - nm_clear_g_free(&priv->iface); - nm_clear_g_free(&priv->hostname); - nm_clear_g_free(&priv->uuid); - nm_clear_g_free(&priv->anycast_address); - nm_clear_g_free(&priv->mud_url); - nm_clear_g_free(&priv->reject_servers); - nm_clear_pointer(&priv->client_id, g_bytes_unref); - nm_clear_pointer(&priv->hwaddr, g_bytes_unref); - nm_clear_pointer(&priv->bcast_hwaddr, g_bytes_unref); - nm_clear_pointer(&priv->vendor_class_identifier, g_bytes_unref); + nm_clear_g_source_inst(&priv->no_lease_timeout_source); + nm_clear_g_source_inst(&priv->ipv6_lladdr_timeout_source); G_OBJECT_CLASS(nm_dhcp_client_parent_class)->dispose(object); +} - priv->multi_idx = nm_dedup_multi_index_unref(priv->multi_idx); +static void +finalize(GObject *object) +{ + NMDhcpClient * self = NM_DHCP_CLIENT(object); + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + config_clear(&priv->config); + + G_OBJECT_CLASS(nm_dhcp_client_parent_class)->finalize(object); } static void @@ -1256,165 +1153,19 @@ nm_dhcp_client_class_init(NMDhcpClientClass *client_class) g_type_class_add_private(client_class, sizeof(NMDhcpClientPrivate)); -#if NM_MORE_ASSERTS - object_class->constructed = constructed; -#endif object_class->dispose = dispose; - object_class->get_property = get_property; + object_class->finalize = finalize; object_class->set_property = set_property; client_class->stop = stop; client_class->get_duid = get_duid; - obj_properties[PROP_MULTI_IDX] = - g_param_spec_pointer(NM_DHCP_CLIENT_MULTI_IDX, + obj_properties[PROP_CONFIG] = + g_param_spec_pointer(NM_DHCP_CLIENT_CONFIG, "", "", G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_IFACE] = - g_param_spec_string(NM_DHCP_CLIENT_INTERFACE, - "", - "", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_IFINDEX] = - g_param_spec_int(NM_DHCP_CLIENT_IFINDEX, - "", - "", - -1, - G_MAXINT, - -1, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_HWADDR] = - g_param_spec_boxed(NM_DHCP_CLIENT_HWADDR, - "", - "", - G_TYPE_BYTES, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_BROADCAST_HWADDR] = - g_param_spec_boxed(NM_DHCP_CLIENT_BROADCAST_HWADDR, - "", - "", - G_TYPE_BYTES, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_ADDR_FAMILY] = - g_param_spec_int(NM_DHCP_CLIENT_ADDR_FAMILY, - "", - "", - 0, - G_MAXINT, - AF_UNSPEC, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_ANYCAST_ADDRESS] = - g_param_spec_string(NM_DHCP_CLIENT_ANYCAST_ADDRESS, - "", - "", - NULL, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_UUID] = - g_param_spec_string(NM_DHCP_CLIENT_UUID, - "", - "", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_IAID] = - g_param_spec_uint(NM_DHCP_CLIENT_IAID, - "", - "", - 0, - G_MAXUINT32, - 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_IAID_EXPLICIT] = - g_param_spec_boolean(NM_DHCP_CLIENT_IAID_EXPLICIT, - "", - "", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_HOSTNAME] = - g_param_spec_string(NM_DHCP_CLIENT_HOSTNAME, - "", - "", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_HOSTNAME_FLAGS] = - g_param_spec_uint(NM_DHCP_CLIENT_HOSTNAME_FLAGS, - "", - "", - 0, - G_MAXUINT32, - NM_DHCP_HOSTNAME_FLAG_NONE, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_MUD_URL] = - g_param_spec_string(NM_DHCP_CLIENT_MUD_URL, - "", - "", - NULL, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_ROUTE_TABLE] = - g_param_spec_uint(NM_DHCP_CLIENT_ROUTE_TABLE, - "", - "", - 0, - G_MAXUINT32, - RT_TABLE_MAIN, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_ROUTE_METRIC] = - g_param_spec_uint(NM_DHCP_CLIENT_ROUTE_METRIC, - "", - "", - 0, - G_MAXUINT32, - 0, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - G_STATIC_ASSERT_EXPR(G_MAXINT32 == NM_DHCP_TIMEOUT_INFINITY); - obj_properties[PROP_TIMEOUT] = - g_param_spec_uint(NM_DHCP_CLIENT_TIMEOUT, - "", - "", - 1, - G_MAXINT32, - NM_DHCP_TIMEOUT_DEFAULT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_FLAGS] = - g_param_spec_uint(NM_DHCP_CLIENT_FLAGS, - "", - "", - 0, - G_MAXUINT32, - 0, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_VENDOR_CLASS_IDENTIFIER] = - g_param_spec_boxed(NM_DHCP_CLIENT_VENDOR_CLASS_IDENTIFIER, - "", - "", - G_TYPE_BYTES, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_REJECT_SERVERS] = - g_param_spec_boxed(NM_DHCP_CLIENT_REJECT_SERVERS, - "", - "", - G_TYPE_STRV, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); signals[SIGNAL_NOTIFY] = diff --git a/src/core/dhcp/nm-dhcp-client.h b/src/core/dhcp/nm-dhcp-client.h index 2e7e021650..fac4b0f7df 100644 --- a/src/core/dhcp/nm-dhcp-client.h +++ b/src/core/dhcp/nm-dhcp-client.h @@ -8,8 +8,6 @@ #include "nm-setting-ip4-config.h" #include "nm-setting-ip6-config.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" #include "nm-dhcp-utils.h" #define NM_DHCP_TIMEOUT_DEFAULT ((guint32) 45) /* default DHCP timeout, in seconds */ @@ -24,25 +22,7 @@ #define NM_DHCP_CLIENT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass)) -#define NM_DHCP_CLIENT_ADDR_FAMILY "addr-family" -#define NM_DHCP_CLIENT_ANYCAST_ADDRESS "anycast-address" -#define NM_DHCP_CLIENT_FLAGS "flags" -#define NM_DHCP_CLIENT_HWADDR "hwaddr" -#define NM_DHCP_CLIENT_BROADCAST_HWADDR "broadcast-hwaddr" -#define NM_DHCP_CLIENT_IFINDEX "ifindex" -#define NM_DHCP_CLIENT_INTERFACE "iface" -#define NM_DHCP_CLIENT_MULTI_IDX "multi-idx" -#define NM_DHCP_CLIENT_HOSTNAME "hostname" -#define NM_DHCP_CLIENT_MUD_URL "mud-url" -#define NM_DHCP_CLIENT_ROUTE_METRIC "route-metric" -#define NM_DHCP_CLIENT_ROUTE_TABLE "route-table" -#define NM_DHCP_CLIENT_TIMEOUT "timeout" -#define NM_DHCP_CLIENT_UUID "uuid" -#define NM_DHCP_CLIENT_IAID "iaid" -#define NM_DHCP_CLIENT_IAID_EXPLICIT "iaid-explicit" -#define NM_DHCP_CLIENT_HOSTNAME_FLAGS "hostname-flags" -#define NM_DHCP_CLIENT_VENDOR_CLASS_IDENTIFIER "vendor-class-identifier" -#define NM_DHCP_CLIENT_REJECT_SERVERS "reject-servers" +#define NM_DHCP_CLIENT_CONFIG "config" #define NM_DHCP_CLIENT_NOTIFY "dhcp-notify" @@ -59,7 +39,27 @@ typedef enum { } NMDhcpState; typedef enum _nm_packed { - NM_DHCP_CLIENT_NOTIFY_TYPE_STATE_CHANGED, + NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE, + + /* When NM_DHCP_CLIENT_NO_LEASE_TIMEOUT expired and the state + * switched from NM_DHCP_CLIENT_STATE_NO_LEASE to + * NM_DHCP_CLIENT_STATE_NO_LEASE_WITH_TIMEOUT. */ + NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT, + + /* NMDhcpClient will indefinitely try to get/renew the lease. + * As such, it's never officially in a non-recoverable state. + * However, there are cases when it really looks like we won't + * be able to get a lease. For example, if the underlying interface + * is layer 3 only, if we have no IPv6 link local address for a prolonged + * time, or if dhclient is not installed. + * But even these cases are potentially recoverable. This is only + * a hint to the user (which they might ignore). + * + * In particular, NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT might mean + * that the DHCP is currently not running, but that could change + * at any moment and from client's side, it does not look bad. */ + NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD, + NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED, } NMDhcpClientNotifyType; @@ -67,18 +67,123 @@ typedef struct { NMDhcpClientNotifyType notify_type; union { struct { - NMIPConfig *ip_config; - GHashTable *options; - NMDhcpState dhcp_state; - } state_changed; + /* This is either the new lease information we just received, + * or NULL (if a previous lease timed out). It can also be the + * previous lease, that was injected. */ + const NML3ConfigData *l3cd; + } lease_update; struct { const NMPlatformIP6Address *prefix; } prefix_delegated; + struct { + const char *reason; + } it_looks_bad; }; } NMDhcpClientNotifyData; const char *nm_dhcp_state_to_string(NMDhcpState state); +/* FIXME(l3cfg:dhcp:config): nm_dhcp_manager_start_ip[46]() has a gazillion of parameters, + * those get passed on as CONSTRUCT_ONLY properties to the NMDhcpClient. Drop + * all these parameters, and let the caller provide one NMDhcpClientConfig + * instance. There will be only one GObject property (NM_DHCP_CLIENT_CONFIG), + * which is CONSTRUCT_ONLY and takes a (mandatory) G_TYPE_POINTER for the + * configuration. + * + * Since NMDhcpClientConfig has an addr_family, we also don't need separate + * nm_dhcp_manager_start_ip[46]() methods. */ +typedef struct { + int addr_family; + + /* The NML3Cfg instance is the manager object for the ifindex on which + * 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. + * + * However, while the restart happens, we want to stick to the previous + * lease (if any). Allow the caller to provide such a previous lease, + * and if present, the instance starts by pretending that it just received + * this lease, before really starting. */ + const NML3ConfigData *previous_lease; + + const char *iface; + + /* The hardware address */ + GBytes *hwaddr; + + /* The broadcast hardware address */ + GBytes *bcast_hwaddr; + + /* Timeout in seconds before reporting failure */ + guint32 timeout; + + /* Flags for the hostname and FQDN DHCP options */ + NMDhcpHostnameFlags hostname_flags; + + /* The UUID of the connection. Used mainly to build + * lease file names. */ + const char *uuid; + + /* Set to reduce the number of broadcast packets when the + * anycast hardware address of the DHCP service is known. */ + const char *anycast_address; + + /* The hostname or FQDN to send. */ + const char *hostname; + + /* The Manufacturer Usage Description (RFC 8520) URL to send */ + const char *mud_url; + + /* The value for the Vendor Class Identifier option */ + GBytes *vendor_class_identifier; + + /* A list of servers from which offers should be rejected */ + const char *const *reject_servers; + + /* The client identifier (DHCPv4) or DUID (DHCPv6) to send */ + GBytes *client_id; + + /* Whether to send the hostname or FQDN option */ + bool send_hostname : 1; + + /* Whether to send the hostname as HOSTNAME option or FQDN. + * For DHCPv6 this is always TRUE. */ + bool use_fqdn : 1; + + union { + struct { + /* Set BOOTP broadcast flag in request packets, so that servers + * will always broadcast replies. */ + bool request_broadcast : 1; + + /* The address from the previous lease */ + const char *last_address; + } v4; + struct { + /* If set, the DUID from the connection is used; otherwise + * the one from an existing lease is used. */ + gboolean enforce_duid; + + /* The IAID to use */ + guint32 iaid; + + /* Whether the IAID was explicitly set in the connection or + * as global default */ + gboolean iaid_explicit; + + /* Number to prefixes (IA_PD) to request */ + guint needed_prefixes; + + /* Use Information-request to get stateless configuration + * parameters (don't request a IA_NA) */ + bool info_only : 1; + } v6; + }; +} NMDhcpClientConfig; + struct _NMDhcpClientPrivate; typedef struct { @@ -100,17 +205,13 @@ typedef enum _nm_packed { typedef struct { GObjectClass parent; - gboolean (*ip4_start)(NMDhcpClient *self, const char *last_ip4_address, GError **error); + gboolean (*ip4_start)(NMDhcpClient *self, GError **error); gboolean (*accept)(NMDhcpClient *self, GError **error); gboolean (*decline)(NMDhcpClient *self, const char *error_message, GError **error); - gboolean (*ip6_start)(NMDhcpClient * self, - const struct in6_addr * ll_addr, - NMSettingIP6ConfigPrivacy privacy, - guint needed_prefixes, - GError ** error); + gboolean (*ip6_start)(NMDhcpClient *self, const struct in6_addr *ll_addr, GError **error); void (*stop)(NMDhcpClient *self, gboolean release); @@ -128,64 +229,25 @@ typedef struct { GType nm_dhcp_client_get_type(void); -struct _NMDedupMultiIndex *nm_dhcp_client_get_multi_idx(NMDhcpClient *self); +gboolean nm_dhcp_client_start_ip4(NMDhcpClient *self, GError **error); +gboolean nm_dhcp_client_start_ip6(NMDhcpClient *self, GError **error); + +const NMDhcpClientConfig *nm_dhcp_client_get_config(NMDhcpClient *self); pid_t nm_dhcp_client_get_pid(NMDhcpClient *self); -int nm_dhcp_client_get_addr_family(NMDhcpClient *self); - -const char *nm_dhcp_client_get_iface(NMDhcpClient *self); - -int nm_dhcp_client_get_ifindex(NMDhcpClient *self); - -const char *nm_dhcp_client_get_uuid(NMDhcpClient *self); - -GBytes *nm_dhcp_client_get_duid(NMDhcpClient *self); - -GBytes *nm_dhcp_client_get_hw_addr(NMDhcpClient *self); - -GBytes *nm_dhcp_client_get_broadcast_hw_addr(NMDhcpClient *self); - -const char *nm_dhcp_client_get_anycast_address(NMDhcpClient *self); - -guint32 nm_dhcp_client_get_route_table(NMDhcpClient *self); - -void nm_dhcp_client_set_route_table(NMDhcpClient *self, guint32 route_table); - -guint32 nm_dhcp_client_get_route_metric(NMDhcpClient *self); - -void nm_dhcp_client_set_route_metric(NMDhcpClient *self, guint32 route_metric); - -guint32 nm_dhcp_client_get_timeout(NMDhcpClient *self); - -guint32 nm_dhcp_client_get_iaid(NMDhcpClient *self); - -gboolean nm_dhcp_client_get_iaid_explicit(NMDhcpClient *self); - -GBytes *nm_dhcp_client_get_client_id(NMDhcpClient *self); - -const char * nm_dhcp_client_get_hostname(NMDhcpClient *self); -const char * nm_dhcp_client_get_mud_url(NMDhcpClient *self); -const char *const *nm_dhcp_client_get_reject_servers(NMDhcpClient *self); - -NMDhcpHostnameFlags nm_dhcp_client_get_hostname_flags(NMDhcpClient *self); - -NMDhcpClientFlags nm_dhcp_client_get_client_flags(NMDhcpClient *self); - -GBytes *nm_dhcp_client_get_vendor_class_identifier(NMDhcpClient *self); - -gboolean nm_dhcp_client_start_ip4(NMDhcpClient *self, - GBytes * client_id, - const char * last_ip4_address, - GError ** error); - -gboolean nm_dhcp_client_start_ip6(NMDhcpClient * self, - GBytes * client_id, - gboolean enforce_duid, - const struct in6_addr * ll_addr, - NMSettingIP6ConfigPrivacy privacy, - guint needed_prefixes, - GError ** error); +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; +} gboolean nm_dhcp_client_accept(NMDhcpClient *self, GError **error); gboolean nm_dhcp_client_can_accept(NMDhcpClient *self); @@ -205,10 +267,8 @@ void nm_dhcp_client_watch_child(NMDhcpClient *self, pid_t pid); void nm_dhcp_client_stop_watch_child(NMDhcpClient *self, pid_t pid); -void nm_dhcp_client_set_state(NMDhcpClient *self, - NMDhcpState new_state, - NMIPConfig * ip_config, - GHashTable * options); /* str:str hash */ +void +nm_dhcp_client_set_state(NMDhcpClient *self, NMDhcpState new_state, const NML3ConfigData *l3cd); gboolean nm_dhcp_client_handle_event(gpointer unused, const char * iface, @@ -217,17 +277,19 @@ gboolean nm_dhcp_client_handle_event(gpointer unused, const char * reason, NMDhcpClient *self); -void nm_dhcp_client_set_client_id(NMDhcpClient *self, GBytes *client_id); -void nm_dhcp_client_set_client_id_bin(NMDhcpClient *self, - guint8 type, - const guint8 *client_id, - gsize len); - void nm_dhcp_client_emit_ipv6_prefix_delegated(NMDhcpClient * self, const NMPlatformIP6Address *prefix); gboolean nm_dhcp_client_server_id_is_rejected(NMDhcpClient *self, gconstpointer addr); +int nm_dhcp_client_get_addr_family(NMDhcpClient *self); +const char * nm_dhcp_client_get_iface(NMDhcpClient *self); +NMDedupMultiIndex *nm_dhcp_client_get_multi_idx(NMDhcpClient *self); +int nm_dhcp_client_get_ifindex(NMDhcpClient *self); + +void nm_dhcp_client_set_effective_client_id(NMDhcpClient *self, GBytes *client_id); +GBytes *nm_dhcp_client_get_effective_client_id(NMDhcpClient *self); + /***************************************************************************** * Client data *****************************************************************************/ diff --git a/src/core/dhcp/nm-dhcp-dhclient-utils.c b/src/core/dhcp/nm-dhcp-dhclient-utils.c index 341ac7b2fa..60cb46e507 100644 --- a/src/core/dhcp/nm-dhcp-dhclient-utils.c +++ b/src/core/dhcp/nm-dhcp-dhclient-utils.c @@ -15,7 +15,6 @@ #include "libnm-glib-aux/nm-dedup-multi.h" #include "nm-dhcp-utils.h" -#include "nm-ip4-config.h" #include "nm-utils.h" #include "libnm-platform/nm-platform.h" #include "NetworkManagerUtils.h" diff --git a/src/core/dhcp/nm-dhcp-dhclient.c b/src/core/dhcp/nm-dhcp-dhclient.c index 970a51f5dd..9c5d05bcf4 100644 --- a/src/core/dhcp/nm-dhcp-dhclient.c +++ b/src/core/dhcp/nm-dhcp-dhclient.c @@ -333,26 +333,28 @@ dhclient_start(NMDhcpClient *client, const char * mode_opt, gboolean release, pid_t * out_pid, - int prefixes, GError ** error) { NMDhcpDhclient * self = NM_DHCP_DHCLIENT(client); NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self); gs_unref_ptrarray GPtrArray *argv = NULL; pid_t pid; - gs_free_error GError *local = NULL; - const char * iface; - const char * uuid; - const char * system_bus_address; - const char * dhclient_path; - char * binary_name; - gs_free char * cmd_str = NULL; - gs_free char * pid_file = NULL; - gs_free char * system_bus_address_env = NULL; - gs_free char * preferred_leasefile_path = NULL; - const int addr_family = nm_dhcp_client_get_addr_family(client); + gs_free_error GError * local = NULL; + const char * iface; + const char * uuid; + const char * system_bus_address; + const char * dhclient_path; + char * binary_name; + gs_free char * cmd_str = NULL; + gs_free char * pid_file = NULL; + gs_free char * system_bus_address_env = NULL; + gs_free char * preferred_leasefile_path = NULL; + int addr_family; + const NMDhcpClientConfig *client_config; g_return_val_if_fail(!priv->pid_file, FALSE); + client_config = nm_dhcp_client_get_config(client); + addr_family = client_config->addr_family; NM_SET_OUT(out_pid, 0); @@ -362,8 +364,8 @@ dhclient_start(NMDhcpClient *client, return FALSE; } - iface = nm_dhcp_client_get_iface(client); - uuid = nm_dhcp_client_get_uuid(client); + iface = client_config->iface; + uuid = client_config->uuid; pid_file = g_strdup_printf(NMRUNDIR "/dhclient%s-%s.pid", _addr_family_to_path_part(addr_family), @@ -409,9 +411,7 @@ dhclient_start(NMDhcpClient *client, /* Save the DUID to the leasefile dhclient will actually use */ if (addr_family == AF_INET6) { - if (!nm_dhcp_dhclient_save_duid(priv->lease_file, - nm_dhcp_client_get_client_id(client), - &local)) { + if (!nm_dhcp_dhclient_save_duid(priv->lease_file, client_config->client_id, &local)) { nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, "failed to save DUID to '%s': %s", @@ -434,13 +434,13 @@ dhclient_start(NMDhcpClient *client, if (release) g_ptr_array_add(argv, (gpointer) "-r"); - if (!release - && NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(NM_DHCP_CLIENT(self)), - NM_DHCP_CLIENT_FLAGS_REQUEST_BROADCAST)) { + if (!release && client_config->addr_family == AF_INET && client_config->v4.request_broadcast) { g_ptr_array_add(argv, (gpointer) "-B"); } if (addr_family == AF_INET6) { + guint prefixes = client_config->v6.needed_prefixes; + g_ptr_array_add(argv, (gpointer) "-6"); if (prefixes > 0 && nm_streq0(mode_opt, "-S")) { @@ -513,29 +513,28 @@ dhclient_start(NMDhcpClient *client, } static gboolean -ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) +ip4_start(NMDhcpClient *client, GError **error) { - NMDhcpDhclient * self = NM_DHCP_DHCLIENT(client); - NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self); - GBytes * client_id; - gs_unref_bytes GBytes *new_client_id = NULL; + NMDhcpDhclient * self = NM_DHCP_DHCLIENT(client); + NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self); + gs_unref_bytes GBytes * new_client_id = NULL; + const NMDhcpClientConfig *client_config; - client_id = nm_dhcp_client_get_client_id(client); + client_config = nm_dhcp_client_get_config(client); - priv->conf_file = create_dhclient_config( - self, - AF_INET, - nm_dhcp_client_get_iface(client), - nm_dhcp_client_get_uuid(client), - client_id, - nm_dhcp_client_get_anycast_address(client), - nm_dhcp_client_get_hostname(client), - nm_dhcp_client_get_timeout(client), - NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(client), NM_DHCP_CLIENT_FLAGS_USE_FQDN), - nm_dhcp_client_get_hostname_flags(client), - nm_dhcp_client_get_mud_url(client), - nm_dhcp_client_get_reject_servers(client), - &new_client_id); + priv->conf_file = create_dhclient_config(self, + AF_INET, + client_config->iface, + client_config->uuid, + client_config->client_id, + client_config->anycast_address, + client_config->hostname, + client_config->timeout, + client_config->use_fqdn, + client_config->hostname_flags, + client_config->mud_url, + client_config->reject_servers, + &new_client_id); if (!priv->conf_file) { nm_utils_error_set_literal(error, NM_UTILS_ERROR_UNKNOWN, @@ -544,36 +543,35 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) } if (new_client_id) { - nm_assert(!client_id); - nm_dhcp_client_set_client_id(client, new_client_id); + nm_assert(!client_config->client_id); + nm_dhcp_client_set_effective_client_id(client, new_client_id); } - return dhclient_start(client, NULL, FALSE, NULL, 0, error); + return dhclient_start(client, NULL, FALSE, NULL, error); } static gboolean -ip6_start(NMDhcpClient * client, - const struct in6_addr * ll_addr, - NMSettingIP6ConfigPrivacy privacy, - guint needed_prefixes, - GError ** error) +ip6_start(NMDhcpClient *client, const struct in6_addr *ll_addr, GError **error) { - NMDhcpDhclient * self = NM_DHCP_DHCLIENT(client); - NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self); + NMDhcpDhclient * self = NM_DHCP_DHCLIENT(client); + NMDhcpDhclientPrivate * priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self); + const NMDhcpClientConfig *config; - if (nm_dhcp_client_get_iaid_explicit(client)) + config = nm_dhcp_client_get_config(client); + + if (config->v6.iaid_explicit) _LOGW("dhclient does not support specifying an IAID for DHCPv6, it will be ignored"); priv->conf_file = create_dhclient_config(self, AF_INET6, - nm_dhcp_client_get_iface(client), - nm_dhcp_client_get_uuid(client), + config->iface, + config->uuid, NULL, - nm_dhcp_client_get_anycast_address(client), - nm_dhcp_client_get_hostname(client), - nm_dhcp_client_get_timeout(client), + config->anycast_address, + config->hostname, + config->timeout, TRUE, - nm_dhcp_client_get_hostname_flags(client), - nm_dhcp_client_get_mud_url(client), + config->hostname_flags, + config->mud_url, NULL, NULL); if (!priv->conf_file) { @@ -583,15 +581,7 @@ ip6_start(NMDhcpClient * client, return FALSE; } - return dhclient_start(client, - NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(NM_DHCP_CLIENT(self)), - NM_DHCP_CLIENT_FLAGS_INFO_ONLY) - ? "-S" - : "-N", - FALSE, - NULL, - needed_prefixes, - error); + return dhclient_start(client, config->v6.needed_prefixes ? "-S" : "-N", FALSE, NULL, error); } static void @@ -625,7 +615,7 @@ stop(NMDhcpClient *client, gboolean release) if (release) { pid_t rpid = -1; - if (dhclient_start(client, NULL, TRUE, &rpid, 0, NULL)) { + if (dhclient_start(client, NULL, TRUE, &rpid, NULL)) { /* Wait a few seconds for the release to happen */ nm_dhcp_client_stop_pid(rpid, nm_dhcp_client_get_iface(client)); } @@ -635,16 +625,19 @@ stop(NMDhcpClient *client, gboolean release) static GBytes * get_duid(NMDhcpClient *client) { - NMDhcpDhclient * self = NM_DHCP_DHCLIENT(client); - NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self); - GBytes * duid = NULL; - gs_free char * leasefile = NULL; - GError * error = NULL; + NMDhcpDhclient * self = NM_DHCP_DHCLIENT(client); + NMDhcpDhclientPrivate * priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self); + const NMDhcpClientConfig *client_config; + GBytes * duid = NULL; + gs_free char * leasefile = NULL; + GError * error = NULL; + + client_config = nm_dhcp_client_get_config(client); /* Look in interface-specific leasefile first for backwards compat */ leasefile = get_dhclient_leasefile(AF_INET6, nm_dhcp_client_get_iface(client), - nm_dhcp_client_get_uuid(client), + client_config->uuid, NULL); if (leasefile) { _LOGD("looking for DUID in '%s'", leasefile); diff --git a/src/core/dhcp/nm-dhcp-dhcpcanon.c b/src/core/dhcp/nm-dhcp-dhcpcanon.c index f993ffb940..ed29b85cd0 100644 --- a/src/core/dhcp/nm-dhcp-dhcpcanon.c +++ b/src/core/dhcp/nm-dhcp-dhcpcanon.c @@ -160,7 +160,7 @@ dhcpcanon_start(NMDhcpClient *client, } static gboolean -ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) +ip4_start(NMDhcpClient *client, GError **error) { return dhcpcanon_start(client, NULL, NULL, FALSE, NULL, 0, error); } diff --git a/src/core/dhcp/nm-dhcp-dhcpcd.c b/src/core/dhcp/nm-dhcp-dhcpcd.c index 7522156bef..0a6a91b743 100644 --- a/src/core/dhcp/nm-dhcp-dhcpcd.c +++ b/src/core/dhcp/nm-dhcp-dhcpcd.c @@ -64,21 +64,19 @@ nm_dhcp_dhcpcd_get_path(void) } static gboolean -ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) +ip4_start(NMDhcpClient *client, GError **error) { - NMDhcpDhcpcd * self = NM_DHCP_DHCPCD(client); + NMDhcpDhcpcd * self = NM_DHCP_DHCPCD(client); + const NMDhcpClientConfig *client_config; gs_unref_ptrarray GPtrArray *argv = NULL; pid_t pid; GError * local; gs_free char * cmd_str = NULL; - const char * iface; const char * dhcpcd_path; - const char * hostname; pid = nm_dhcp_client_get_pid(client); g_return_val_if_fail(pid == -1, FALSE); - - iface = nm_dhcp_client_get_iface(client); + client_config = nm_dhcp_client_get_config(client); dhcpcd_path = nm_dhcp_dhcpcd_get_path(); if (!dhcpcd_path) { @@ -115,21 +113,19 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) */ g_ptr_array_add(argv, (gpointer) "-4"); - hostname = nm_dhcp_client_get_hostname(client); - - if (hostname) { - if (NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(client), NM_DHCP_CLIENT_FLAGS_USE_FQDN)) { + if (client_config->hostname) { + if (client_config->use_fqdn) { g_ptr_array_add(argv, (gpointer) "-h"); - g_ptr_array_add(argv, (gpointer) hostname); + g_ptr_array_add(argv, (gpointer) client_config->hostname); g_ptr_array_add(argv, (gpointer) "-F"); g_ptr_array_add(argv, (gpointer) "both"); } else { g_ptr_array_add(argv, (gpointer) "-h"); - g_ptr_array_add(argv, (gpointer) hostname); + g_ptr_array_add(argv, (gpointer) client_config->hostname); } } - g_ptr_array_add(argv, (gpointer) iface); + g_ptr_array_add(argv, (gpointer) client_config->iface); g_ptr_array_add(argv, NULL); _LOGD("running: %s", (cmd_str = g_strjoinv(" ", (char **) argv->pdata))); diff --git a/src/core/dhcp/nm-dhcp-manager.c b/src/core/dhcp/nm-dhcp-manager.c index 3cb893932d..8fe7478c39 100644 --- a/src/core/dhcp/nm-dhcp-manager.c +++ b/src/core/dhcp/nm-dhcp-manager.c @@ -17,7 +17,6 @@ #include #include "libnm-glib-aux/nm-dedup-multi.h" -#include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "nm-config.h" #include "NetworkManagerUtils.h" @@ -26,7 +25,6 @@ typedef struct { const NMDhcpClientFactory *client_factory; - char * default_hostname; } NMDhcpManagerPrivate; struct _NMDhcpManager { @@ -129,34 +127,8 @@ _client_factory_get_gtype(const NMDhcpClientFactory *client_factory, int addr_fa /*****************************************************************************/ -static NMDhcpClient * -client_start(NMDhcpManager * self, - int addr_family, - NMDedupMultiIndex * multi_idx, - const char * iface, - int ifindex, - GBytes * hwaddr, - GBytes * bcast_hwaddr, - const char * uuid, - guint32 route_table, - guint32 route_metric, - const struct in6_addr * ipv6_ll_addr, - GBytes * dhcp_client_id, - gboolean enforce_duid, - guint32 iaid, - gboolean iaid_explicit, - guint32 timeout, - NMDhcpClientFlags client_flags, - const char * anycast_address, - const char * hostname, - NMDhcpHostnameFlags hostname_flags, - const char * mud_url, - NMSettingIP6ConfigPrivacy privacy, - const char * last_ip4_address, - guint needed_prefixes, - GBytes * vendor_class_identifier, - const char *const * reject_servers, - GError ** error) +NMDhcpClient * +nm_dhcp_manager_start_client(NMDhcpManager *self, NMDhcpClientConfig *config, GError **error) { NMDhcpManagerPrivate *priv; gs_unref_object NMDhcpClient *client = NULL; @@ -165,100 +137,44 @@ client_start(NMDhcpManager * self, GType gtype; g_return_val_if_fail(NM_IS_DHCP_MANAGER(self), NULL); - g_return_val_if_fail(iface, NULL); - g_return_val_if_fail(ifindex > 0, NULL); - g_return_val_if_fail(uuid != NULL, NULL); - g_return_val_if_fail(!dhcp_client_id || g_bytes_get_size(dhcp_client_id) >= 2, NULL); - g_return_val_if_fail(!vendor_class_identifier - || g_bytes_get_size(vendor_class_identifier) <= 255, + g_return_val_if_fail(config, NULL); + g_return_val_if_fail(config->iface, NULL); + g_return_val_if_fail(config->l3cfg, NULL); + g_return_val_if_fail(config->uuid != NULL, NULL); + g_return_val_if_fail(!config->client_id || g_bytes_get_size(config->client_id) >= 2, NULL); + g_return_val_if_fail(!config->vendor_class_identifier + || g_bytes_get_size(config->vendor_class_identifier) <= 255, NULL); g_return_val_if_fail(!error || !*error, NULL); - nm_assert(!NM_FLAGS_ANY(client_flags, ~NM_DHCP_CLIENT_FLAGS_ALL)); - if (addr_family == AF_INET) { - if (!hwaddr || !bcast_hwaddr) { + if (config->addr_family == AF_INET) { + if (!config->hwaddr || !config->bcast_hwaddr) { nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, "missing %s address", - hwaddr ? "broadcast" : "MAC"); + config->hwaddr ? "broadcast" : "MAC"); return NULL; } - hwaddr_len = g_bytes_get_size(hwaddr); + hwaddr_len = g_bytes_get_size(config->hwaddr); if (hwaddr_len == 0 || hwaddr_len > _NM_UTILS_HWADDR_LEN_MAX) { nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, "invalid MAC address"); g_return_val_if_reached(NULL); } - nm_assert(g_bytes_get_size(hwaddr) == g_bytes_get_size(bcast_hwaddr)); - } else { - hwaddr = NULL; - bcast_hwaddr = NULL; - } - - if (hostname) { - gboolean use_fqdn = NM_FLAGS_HAS(client_flags, NM_DHCP_CLIENT_FLAGS_USE_FQDN); - - if ((use_fqdn && !nm_sd_dns_name_is_valid(hostname)) - || (!use_fqdn && !nm_sd_hostname_is_valid(hostname, FALSE))) { - nm_log_warn(LOGD_DHCP, - "dhcp%c: %s '%s' is invalid, will be ignored", - nm_utils_addr_family_to_char(addr_family), - use_fqdn ? "FQDN" : "hostname", - hostname); - hostname = NULL; - } + nm_assert(g_bytes_get_size(config->hwaddr) == g_bytes_get_size(config->bcast_hwaddr)); } priv = NM_DHCP_MANAGER_GET_PRIVATE(self); - gtype = _client_factory_get_gtype(priv->client_factory, addr_family); + gtype = _client_factory_get_gtype(priv->client_factory, config->addr_family); nm_log_trace(LOGD_DHCP, "dhcp%c: creating IPv%c DHCP client of type %s", - nm_utils_addr_family_to_char(addr_family), - nm_utils_addr_family_to_char(addr_family), + nm_utils_addr_family_to_char(config->addr_family), + nm_utils_addr_family_to_char(config->addr_family), g_type_name(gtype)); - client = g_object_new(gtype, - NM_DHCP_CLIENT_MULTI_IDX, - multi_idx, - NM_DHCP_CLIENT_ADDR_FAMILY, - addr_family, - NM_DHCP_CLIENT_INTERFACE, - iface, - NM_DHCP_CLIENT_IFINDEX, - ifindex, - NM_DHCP_CLIENT_HWADDR, - hwaddr, - NM_DHCP_CLIENT_BROADCAST_HWADDR, - bcast_hwaddr, - NM_DHCP_CLIENT_UUID, - uuid, - NM_DHCP_CLIENT_IAID, - (guint) iaid, - NM_DHCP_CLIENT_IAID_EXPLICIT, - iaid_explicit, - NM_DHCP_CLIENT_HOSTNAME, - hostname, - NM_DHCP_CLIENT_MUD_URL, - mud_url, - NM_DHCP_CLIENT_ROUTE_TABLE, - (guint) route_table, - NM_DHCP_CLIENT_ROUTE_METRIC, - (guint) route_metric, - NM_DHCP_CLIENT_TIMEOUT, - (guint) timeout, - NM_DHCP_CLIENT_HOSTNAME_FLAGS, - (guint) hostname_flags, - NM_DHCP_CLIENT_VENDOR_CLASS_IDENTIFIER, - vendor_class_identifier, - NM_DHCP_CLIENT_REJECT_SERVERS, - reject_servers, - NM_DHCP_CLIENT_FLAGS, - (guint) client_flags, - NM_DHCP_CLIENT_ANYCAST_ADDRESS, - anycast_address, - NULL); + client = g_object_new(gtype, NM_DHCP_CLIENT_CONFIG, config, NULL); /* unfortunately, our implementations work differently per address-family regarding client-id/DUID. * @@ -286,16 +202,10 @@ client_start(NMDhcpManager * self, * default outside of NetworkManager API. */ - if (addr_family == AF_INET) { - success = nm_dhcp_client_start_ip4(client, dhcp_client_id, last_ip4_address, error); + if (config->addr_family == AF_INET) { + success = nm_dhcp_client_start_ip4(client, error); } else { - success = nm_dhcp_client_start_ip6(client, - dhcp_client_id, - enforce_duid, - ipv6_ll_addr, - privacy, - needed_prefixes, - error); + success = nm_dhcp_client_start_ip6(client, error); } if (!success) @@ -304,178 +214,6 @@ client_start(NMDhcpManager * self, return g_steal_pointer(&client); } -/* Caller owns a reference to the NMDhcpClient on return */ -NMDhcpClient * -nm_dhcp_manager_start_ip4(NMDhcpManager * self, - NMDedupMultiIndex * multi_idx, - const char * iface, - int ifindex, - GBytes * hwaddr, - GBytes * bcast_hwaddr, - const char * uuid, - guint32 route_table, - guint32 route_metric, - NMDhcpClientFlags client_flags, - gboolean send_hostname, - const char * dhcp_hostname, - const char * dhcp_fqdn, - NMDhcpHostnameFlags hostname_flags, - const char * mud_url, - GBytes * dhcp_client_id, - guint32 timeout, - const char * anycast_address, - const char * last_ip_address, - GBytes * vendor_class_identifier, - const char *const * reject_servers, - GError ** error) -{ - NMDhcpManagerPrivate *priv; - const char * hostname = NULL; - gs_free char * hostname_tmp = NULL; - gboolean use_fqdn = FALSE; - char * dot; - - /* these flags are set automatically/prohibited, and not free to set to the caller. */ - nm_assert(!NM_FLAGS_ANY(client_flags, - NM_DHCP_CLIENT_FLAGS_USE_FQDN | NM_DHCP_CLIENT_FLAGS_INFO_ONLY)); - - g_return_val_if_fail(NM_IS_DHCP_MANAGER(self), NULL); - priv = NM_DHCP_MANAGER_GET_PRIVATE(self); - - if (send_hostname) { - /* Use, in order of preference: - * 1. FQDN from configuration - * 2. hostname from configuration - * 3. system hostname (only host part) - */ - if (dhcp_fqdn) { - hostname = dhcp_fqdn; - use_fqdn = TRUE; - } else if (dhcp_hostname) - hostname = dhcp_hostname; - else { - hostname = priv->default_hostname; - if (hostname) { - hostname_tmp = g_strdup(hostname); - dot = strchr(hostname_tmp, '.'); - if (dot) - *dot = '\0'; - hostname = hostname_tmp; - } - } - } - - return client_start( - self, - AF_INET, - multi_idx, - iface, - ifindex, - hwaddr, - bcast_hwaddr, - uuid, - route_table, - route_metric, - NULL, - dhcp_client_id, - FALSE, - 0, - FALSE, - timeout, - client_flags | (use_fqdn ? NM_DHCP_CLIENT_FLAGS_USE_FQDN : NM_DHCP_CLIENT_FLAGS_NONE), - anycast_address, - hostname, - hostname_flags, - mud_url, - 0, - last_ip_address, - 0, - vendor_class_identifier, - reject_servers, - error); -} - -/* Caller owns a reference to the NMDhcpClient on return */ -NMDhcpClient * -nm_dhcp_manager_start_ip6(NMDhcpManager * self, - NMDedupMultiIndex * multi_idx, - const char * iface, - int ifindex, - const struct in6_addr * ll_addr, - const char * uuid, - guint32 route_table, - guint32 route_metric, - NMDhcpClientFlags client_flags, - gboolean send_hostname, - const char * dhcp_hostname, - NMDhcpHostnameFlags hostname_flags, - const char * mud_url, - GBytes * duid, - gboolean enforce_duid, - guint32 iaid, - gboolean iaid_explicit, - guint32 timeout, - const char * anycast_address, - NMSettingIP6ConfigPrivacy privacy, - guint needed_prefixes, - GError ** error) -{ - NMDhcpManagerPrivate *priv; - const char * hostname = NULL; - - /* this flag is set automatically, and not free to set to the caller. */ - nm_assert(!NM_FLAGS_ANY(client_flags, NM_DHCP_CLIENT_FLAGS_USE_FQDN)); - - g_return_val_if_fail(NM_IS_DHCP_MANAGER(self), NULL); - priv = NM_DHCP_MANAGER_GET_PRIVATE(self); - - if (send_hostname) { - /* Always prefer the explicit dhcp-hostname if given */ - hostname = dhcp_hostname ?: priv->default_hostname; - } - return client_start(self, - AF_INET6, - multi_idx, - iface, - ifindex, - NULL, - NULL, - uuid, - route_table, - route_metric, - ll_addr, - duid, - enforce_duid, - iaid, - iaid_explicit, - timeout, - client_flags | NM_DHCP_CLIENT_FLAGS_USE_FQDN, - anycast_address, - hostname, - hostname_flags, - mud_url, - privacy, - NULL, - needed_prefixes, - NULL, - NULL, - error); -} - -void -nm_dhcp_manager_set_default_hostname(NMDhcpManager *manager, const char *hostname) -{ - NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE(manager); - - nm_clear_g_free(&priv->default_hostname); - - /* Never send 'localhost'-type names to the DHCP server */ - if (!nm_utils_is_specific_hostname(hostname)) - return; - - priv->default_hostname = g_strdup(hostname); -} - const char * nm_dhcp_manager_get_config(NMDhcpManager *self) { @@ -527,37 +265,30 @@ nm_dhcp_manager_init(NMDhcpManager *self) NM_CONFIG_KEYFILE_KEY_MAIN_DHCP, NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); client = client_free; - if (nm_config_get_configure_and_quit(config) == NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED) { - client_factory = &_nm_dhcp_client_factory_internal; - if (client && !nm_streq(client, client_factory->name)) - nm_log_info(LOGD_DHCP, - "dhcp-init: Using internal DHCP client since configure-and-quit is set."); - } else { - if (client) { - client_factory = _client_factory_available(_client_factory_find_by_name(client)); + if (client) { + client_factory = _client_factory_available(_client_factory_find_by_name(client)); + if (!client_factory) + nm_log_warn(LOGD_DHCP, "dhcp-init: DHCP client '%s' not available", client); + } + if (!client_factory) { + client_factory = _client_factory_find_by_name("" NM_CONFIG_DEFAULT_MAIN_DHCP); + if (!client_factory) + nm_log_err(LOGD_DHCP, + "dhcp-init: default DHCP client '%s' is not installed", + NM_CONFIG_DEFAULT_MAIN_DHCP); + else { + client_factory = _client_factory_available(client_factory); if (!client_factory) - nm_log_warn(LOGD_DHCP, "dhcp-init: DHCP client '%s' not available", client); + nm_log_info(LOGD_DHCP, + "dhcp-init: default DHCP client '%s' is not available", + NM_CONFIG_DEFAULT_MAIN_DHCP); } - if (!client_factory) { - client_factory = _client_factory_find_by_name("" NM_CONFIG_DEFAULT_MAIN_DHCP); - if (!client_factory) - nm_log_err(LOGD_DHCP, - "dhcp-init: default DHCP client '%s' is not installed", - NM_CONFIG_DEFAULT_MAIN_DHCP); - else { - client_factory = _client_factory_available(client_factory); - if (!client_factory) - nm_log_info(LOGD_DHCP, - "dhcp-init: default DHCP client '%s' is not available", - NM_CONFIG_DEFAULT_MAIN_DHCP); - } - } - if (!client_factory) { - for (i = 0; i < (int) G_N_ELEMENTS(_nm_dhcp_manager_factories); i++) { - client_factory = _client_factory_available(_nm_dhcp_manager_factories[i]); - if (client_factory) - break; - } + } + if (!client_factory) { + for (i = 0; i < (int) G_N_ELEMENTS(_nm_dhcp_manager_factories); i++) { + client_factory = _client_factory_available(_nm_dhcp_manager_factories[i]); + if (client_factory) + break; } } @@ -572,21 +303,6 @@ nm_dhcp_manager_init(NMDhcpManager *self) priv->client_factory = client_factory; } -static void -dispose(GObject *object) -{ - NMDhcpManager * self = NM_DHCP_MANAGER(object); - NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE(self); - - G_OBJECT_CLASS(nm_dhcp_manager_parent_class)->dispose(object); - - nm_clear_g_free(&priv->default_hostname); -} - static void nm_dhcp_manager_class_init(NMDhcpManagerClass *manager_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS(manager_class); - - object_class->dispose = dispose; -} +{} diff --git a/src/core/dhcp/nm-dhcp-manager.h b/src/core/dhcp/nm-dhcp-manager.h index ce160437a5..bbd68236a3 100644 --- a/src/core/dhcp/nm-dhcp-manager.h +++ b/src/core/dhcp/nm-dhcp-manager.h @@ -8,7 +8,6 @@ #define __NETWORKMANAGER_DHCP_MANAGER_H__ #include "nm-dhcp-client.h" -#include "nm-ip4-config.h" #include "nm-dhcp-config.h" #define NM_TYPE_DHCP_MANAGER (nm_dhcp_manager_get_type()) @@ -32,51 +31,8 @@ const char *nm_dhcp_manager_get_config(NMDhcpManager *self); void nm_dhcp_manager_set_default_hostname(NMDhcpManager *manager, const char *hostname); -NMDhcpClient *nm_dhcp_manager_start_ip4(NMDhcpManager * manager, - struct _NMDedupMultiIndex *multi_idx, - const char * iface, - int ifindex, - GBytes * hwaddr, - GBytes * bcast_hwaddr, - const char * uuid, - guint32 route_table, - guint32 route_metric, - NMDhcpClientFlags client_flags, - gboolean send_hostname, - const char * dhcp_hostname, - const char * dhcp_fqdn, - NMDhcpHostnameFlags hostname_flags, - const char * mud_url, - GBytes * dhcp_client_id, - guint32 timeout, - const char * anycast_address, - const char * last_ip_address, - GBytes * vendor_class_identifier, - const char *const * reject_servers, - GError ** error); - -NMDhcpClient *nm_dhcp_manager_start_ip6(NMDhcpManager * manager, - struct _NMDedupMultiIndex *multi_idx, - const char * iface, - int ifindex, - const struct in6_addr * ll_addr, - const char * uuid, - guint32 route_table, - guint32 route_metric, - NMDhcpClientFlags client_flags, - gboolean send_hostname, - const char * dhcp_hostname, - NMDhcpHostnameFlags hostname_flags, - const char * mud_url, - GBytes * duid, - gboolean enforce_duid, - guint32 iaid, - gboolean iaid_explicit, - guint32 timeout, - const char * anycast_address, - NMSettingIP6ConfigPrivacy privacy, - guint needed_prefixes, - GError ** error); +NMDhcpClient * +nm_dhcp_manager_start_client(NMDhcpManager *manager, NMDhcpClientConfig *config, GError **error); /* For testing only */ extern const char *nm_dhcp_helper_path; diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c index 56b485dd25..6ab7f8c18d 100644 --- a/src/core/dhcp/nm-dhcp-nettools.c +++ b/src/core/dhcp/nm-dhcp-nettools.c @@ -17,6 +17,7 @@ #include "libnm-std-aux/unaligned.h" #include "libnm-glib-aux/nm-str-buf.h" +#include "nm-l3-config-data.h" #include "nm-utils.h" #include "nm-config.h" #include "nm-dhcp-utils.h" @@ -151,7 +152,7 @@ lease_option_consume_route(const uint8_t **datap, static gboolean lease_parse_address(NDhcp4ClientLease *lease, - NMIP4Config * ip4_config, + NML3ConfigData * l3cd, GHashTable * options, GError ** error) { @@ -256,23 +257,23 @@ lease_parse_address(NDhcp4ClientLease *lease, a_next_server.s_addr); } - nm_ip4_config_add_address(ip4_config, - &((const NMPlatformIP4Address){ - .address = a_address.s_addr, - .peer_address = a_address.s_addr, - .plen = a_plen, - .addr_source = NM_IP_CONFIG_SOURCE_DHCP, - .timestamp = a_timestamp, - .lifetime = a_lifetime, - .preferred = a_lifetime, - })); + nm_l3_config_data_add_address_4(l3cd, + &((const NMPlatformIP4Address){ + .address = a_address.s_addr, + .peer_address = a_address.s_addr, + .plen = a_plen, + .addr_source = NM_IP_CONFIG_SOURCE_DHCP, + .timestamp = a_timestamp, + .lifetime = a_lifetime, + .preferred = a_lifetime, + })); return TRUE; } static void lease_parse_address_list(NDhcp4ClientLease * lease, - NMIP4Config * ip4_config, + NML3ConfigData * l3cd, NMDhcpOptionDhcp4Options option, GHashTable * options, NMStrBuf * sbuf) @@ -304,13 +305,13 @@ lease_parse_address_list(NDhcp4ClientLease * lease, * See https://github.com/systemd/systemd/issues/4524. */ continue; } - nm_ip4_config_add_nameserver(ip4_config, addr); + nm_l3_config_data_add_nameserver(l3cd, AF_INET, &addr); break; case NM_DHCP_OPTION_DHCP4_NIS_SERVERS: - nm_ip4_config_add_nis_server(ip4_config, addr); + nm_l3_config_data_add_nis_server(l3cd, addr); break; case NM_DHCP_OPTION_DHCP4_NETBIOS_NAMESERVER: - nm_ip4_config_add_wins(ip4_config, addr); + nm_l3_config_data_add_wins(l3cd, addr); break; case NM_DHCP_OPTION_DHCP4_NTP_SERVER: break; @@ -324,10 +325,8 @@ lease_parse_address_list(NDhcp4ClientLease * lease, static void lease_parse_routes(NDhcp4ClientLease *lease, - NMIP4Config * ip4_config, + NML3ConfigData * l3cd, GHashTable * options, - guint32 route_table, - guint32 route_metric, NMStrBuf * sbuf) { char dest_str[NM_UTILS_INET_ADDRSTRLEN]; @@ -336,9 +335,9 @@ lease_parse_routes(NDhcp4ClientLease *lease, in_addr_t gateway; uint8_t plen; guint32 m; - gboolean has_router_from_classless = FALSE; - gboolean has_classless = FALSE; - guint32 default_route_metric = route_metric; + gboolean has_router_from_classless = FALSE; + gboolean has_classless = FALSE; + guint32 default_route_metric_offset = 0; const guint8 *l_data; gsize l_data_len; int r; @@ -367,26 +366,22 @@ lease_parse_routes(NDhcp4ClientLease *lease, if (plen == 0) { /* if there are multiple default routes, we add them with differing * metrics. */ - m = default_route_metric; - if (default_route_metric < G_MAXUINT32) - default_route_metric++; - + m = default_route_metric_offset++; has_router_from_classless = TRUE; - } else { - m = route_metric; - } + } else + m = 0; - nm_ip4_config_add_route( - ip4_config, - &((const NMPlatformIP4Route){ - .network = dest, - .plen = plen, - .gateway = gateway, - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, - .metric = m, - .table_coerced = nm_platform_route_table_coerce(route_table), - }), - NULL); + nm_l3_config_data_add_route_4(l3cd, + &((const NMPlatformIP4Route){ + .network = dest, + .plen = plen, + .gateway = gateway, + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, + .metric = m, + })); } has_classless = TRUE; @@ -419,17 +414,17 @@ lease_parse_routes(NDhcp4ClientLease *lease, continue; } - nm_ip4_config_add_route( - ip4_config, - &((const NMPlatformIP4Route){ - .network = dest, - .plen = plen, - .gateway = gateway, - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, - .metric = route_metric, - .table_coerced = nm_platform_route_table_coerce(route_table), - }), - NULL); + nm_l3_config_data_add_route_4(l3cd, + &((const NMPlatformIP4Route){ + .network = dest, + .plen = plen, + .gateway = gateway, + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, + .metric = 0, + })); } nm_dhcp_option_add_option(options, @@ -463,19 +458,17 @@ lease_parse_routes(NDhcp4ClientLease *lease, /* if there are multiple default routes, we add them with differing * metrics. */ - m = default_route_metric; - if (default_route_metric < G_MAXUINT32) - default_route_metric++; + m = default_route_metric_offset++; - nm_ip4_config_add_route( - ip4_config, - &((const NMPlatformIP4Route){ - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, - .gateway = gateway, - .table_coerced = nm_platform_route_table_coerce(route_table), - .metric = m, - }), - NULL); + nm_l3_config_data_add_route_4(l3cd, + &((const NMPlatformIP4Route){ + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .gateway = gateway, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, + .metric = m, + })); } nm_dhcp_option_add_option(options, @@ -486,7 +479,7 @@ lease_parse_routes(NDhcp4ClientLease *lease, } static void -lease_parse_search_domains(NDhcp4ClientLease *lease, NMIP4Config *ip4_config, GHashTable *options) +lease_parse_search_domains(NDhcp4ClientLease *lease, NML3ConfigData *l3cd, GHashTable *options) { gs_strfreev char **domains = NULL; const guint8 * l_data; @@ -504,7 +497,7 @@ lease_parse_search_domains(NDhcp4ClientLease *lease, NMIP4Config *ip4_config, GH return; for (i = 0; domains[i]; i++) - nm_ip4_config_add_search(ip4_config, domains[i]); + nm_l3_config_data_add_search(l3cd, AF_INET, domains[i]); nm_dhcp_option_take_option(options, AF_INET, @@ -539,34 +532,31 @@ lease_parse_private_options(NDhcp4ClientLease *lease, GHashTable *options) } } -static NMIP4Config * +static NML3ConfigData * lease_to_ip4_config(NMDedupMultiIndex *multi_idx, const char * iface, int ifindex, NDhcp4ClientLease *lease, - guint32 route_table, - guint32 route_metric, - GHashTable ** out_options, GError ** error) { - nm_auto_str_buf NMStrBuf sbuf = NM_STR_BUF_INIT(0, FALSE); - gs_unref_object NMIP4Config *ip4_config = NULL; - gs_unref_hashtable GHashTable *options = NULL; + nm_auto_str_buf NMStrBuf sbuf = NM_STR_BUF_INIT(0, FALSE); + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + gs_unref_hashtable GHashTable *options = NULL; const guint8 * l_data; gsize l_data_len; const char * v_str; guint16 v_u16; - gboolean v_bool; in_addr_t v_inaddr; struct in_addr v_inaddr_s; int r; g_return_val_if_fail(lease != NULL, NULL); - ip4_config = nm_ip4_config_new(multi_idx, ifindex); - options = nm_dhcp_option_create_options_dict(); + l3cd = nm_l3_config_data_new(multi_idx, ifindex, NM_IP_CONFIG_SOURCE_DHCP); - if (!lease_parse_address(lease, ip4_config, options, error)) + options = nm_dhcp_option_create_options_dict(); + + if (!lease_parse_address(lease, l3cd, options, error)) return NULL; r = n_dhcp4_client_lease_get_server_identifier(lease, &v_inaddr_s); @@ -585,13 +575,9 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, v_inaddr); } - lease_parse_routes(lease, ip4_config, options, route_table, route_metric, &sbuf); + lease_parse_routes(lease, l3cd, options, &sbuf); - lease_parse_address_list(lease, - ip4_config, - NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, - options, - &sbuf); + lease_parse_address_list(lease, l3cd, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, options, &sbuf); r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, &l_data, &l_data_len); if (r == 0 && nm_dhcp_lease_data_parse_cstr(l_data, l_data_len, &l_data_len)) { @@ -616,7 +602,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, nm_str_buf_append_required_delimiter(&sbuf, ' '); nm_str_buf_append(&sbuf, s); - nm_ip4_config_add_domain(ip4_config, s); + nm_l3_config_data_add_domain(l3cd, AF_INET, s); } } @@ -628,18 +614,17 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, } } - lease_parse_search_domains(lease, ip4_config, options); + lease_parse_search_domains(lease, l3cd, options); r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, &l_data, &l_data_len); if (r == 0 && nm_dhcp_lease_data_parse_mtu(l_data, l_data_len, &v_u16)) { nm_dhcp_option_add_option_u64(options, AF_INET, NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, v_u16); - nm_ip4_config_set_mtu(ip4_config, v_u16, NM_IP_CONFIG_SOURCE_DHCP); + nm_l3_config_data_set_mtu(l3cd, v_u16); } r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_VENDOR_SPECIFIC, &l_data, &l_data_len); - v_bool = - (r == 0) && memmem(l_data, l_data_len, "ANDROID_METERED", NM_STRLEN("ANDROID_METERED")); - nm_ip4_config_set_metered(ip4_config, v_bool); + if ((r == 0) && memmem(l_data, l_data_len, "ANDROID_METERED", NM_STRLEN("ANDROID_METERED"))) + nm_l3_config_data_set_metered(l3cd, TRUE); r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_HOST_NAME, &l_data, &l_data_len); if (r == 0) { @@ -650,7 +635,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, } } - lease_parse_address_list(lease, ip4_config, NM_DHCP_OPTION_DHCP4_NTP_SERVER, options, &sbuf); + lease_parse_address_list(lease, l3cd, NM_DHCP_OPTION_DHCP4_NTP_SERVER, options, &sbuf); r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_ROOT_PATH, &l_data, &l_data_len); if (r == 0 && nm_dhcp_lease_data_parse_cstr(l_data, l_data_len, &l_data_len)) { @@ -681,11 +666,17 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, * We reject NUL characters inside the string (except trailing NULs). * Otherwise, we allow any encoding and backslash-escape the result to * UTF-8. */ - nm_dhcp_option_add_option_utf8safe_escape(options, - AF_INET, - NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY, - l_data, - l_data_len); + gs_free char *to_free = NULL; + const char * escaped; + + escaped = nm_utils_buf_utf8safe_escape((char *) l_data, l_data_len, 0, &to_free); + nm_dhcp_option_add_option(options, + AF_INET, + NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY, + escaped ?: ""); + + nm_l3_config_data_set_proxy_method(l3cd, NM_PROXY_CONFIG_METHOD_AUTO); + nm_l3_config_data_set_proxy_pac_url(l3cd, escaped ?: ""); } r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_NIS_DOMAIN, &l_data, &l_data_len); @@ -700,7 +691,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, &to_free); nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NIS_DOMAIN, v_str ?: ""); - nm_ip4_config_set_nis_domain(ip4_config, v_str ?: ""); + nm_l3_config_data_set_nis_domain(l3cd, v_str ?: ""); } r = n_dhcp4_client_lease_get_file(lease, &v_str); @@ -728,18 +719,17 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, v_str ?: ""); } - lease_parse_address_list(lease, ip4_config, NM_DHCP_OPTION_DHCP4_NIS_SERVERS, options, &sbuf); + lease_parse_address_list(lease, l3cd, NM_DHCP_OPTION_DHCP4_NIS_SERVERS, options, &sbuf); - lease_parse_address_list(lease, - ip4_config, - NM_DHCP_OPTION_DHCP4_NETBIOS_NAMESERVER, - options, - &sbuf); + lease_parse_address_list(lease, l3cd, NM_DHCP_OPTION_DHCP4_NETBIOS_NAMESERVER, options, &sbuf); lease_parse_private_options(lease, options); - NM_SET_OUT(out_options, g_steal_pointer(&options)); - return g_steal_pointer(&ip4_config); + nm_dhcp_option_add_requests_to_options(options, AF_INET); + + nm_l3_config_data_set_dhcp_lease_from_options(l3cd, AF_INET, g_steal_pointer(&options)); + + return g_steal_pointer(&l3cd); } /*****************************************************************************/ @@ -771,47 +761,44 @@ lease_save(NMDhcpNettools *self, NDhcp4ClientLease *lease, const char *lease_fil static void bound4_handle(NMDhcpNettools *self, NDhcp4ClientLease *lease, gboolean extended) { - NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self); - const char * iface = nm_dhcp_client_get_iface(NM_DHCP_CLIENT(self)); - gs_unref_object NMIP4Config *ip4_config = NULL; - gs_unref_hashtable GHashTable *options = NULL; - GError * error = NULL; + NMDhcpNettoolsPrivate * priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self); + NMDhcpClient * client = NM_DHCP_CLIENT(self); + const NMDhcpClientConfig *client_config; + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + GError * error = NULL; _LOGT("lease available (%s)", extended ? "extended" : "new"); - - ip4_config = lease_to_ip4_config(nm_dhcp_client_get_multi_idx(NM_DHCP_CLIENT(self)), - iface, - nm_dhcp_client_get_ifindex(NM_DHCP_CLIENT(self)), - lease, - nm_dhcp_client_get_route_table(NM_DHCP_CLIENT(self)), - nm_dhcp_client_get_route_metric(NM_DHCP_CLIENT(self)), - &options, - &error); - if (!ip4_config) { - _LOGW("%s", error->message); + client_config = nm_dhcp_client_get_config(client); + l3cd = lease_to_ip4_config(nm_dhcp_client_get_multi_idx(client), + client_config->iface, + nm_dhcp_client_get_ifindex(client), + lease, + &error); + if (!l3cd) { + _LOGW("failure to parse lease: %s", error->message); g_clear_error(&error); - nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL, NULL); + nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL); return; } - nm_dhcp_option_add_requests_to_options(options, AF_INET); lease_save(self, lease, priv->lease_file); nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), extended ? NM_DHCP_STATE_EXTENDED : NM_DHCP_STATE_BOUND, - NM_IP_CONFIG_CAST(ip4_config), - options); + l3cd); } static void dhcp4_event_handle(NMDhcpNettools *self, NDhcp4ClientEvent *event) { - NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self); - struct in_addr server_id; - char addr_str[INET_ADDRSTRLEN]; - int r; + NMDhcpNettoolsPrivate * priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self); + const NMDhcpClientConfig *client_config; + struct in_addr server_id; + char addr_str[INET_ADDRSTRLEN]; + int r; _LOGT("client event %d", event->event); + client_config = nm_dhcp_client_get_config(NM_DHCP_CLIENT(self)); switch (event->event) { case N_DHCP4_CLIENT_EVENT_OFFER: @@ -835,10 +822,10 @@ dhcp4_event_handle(NMDhcpNettools *self, NDhcp4ClientEvent *event) break; case N_DHCP4_CLIENT_EVENT_RETRACTED: case N_DHCP4_CLIENT_EVENT_EXPIRED: - nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_EXPIRE, NULL, NULL); + nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_EXPIRE, NULL); break; case N_DHCP4_CLIENT_EVENT_CANCELLED: - nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL, NULL); + nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL); break; case N_DHCP4_CLIENT_EVENT_GRANTED: priv->lease = n_dhcp4_client_lease_ref(event->granted.lease); @@ -861,7 +848,7 @@ dhcp4_event_handle(NMDhcpNettools *self, NDhcp4ClientEvent *event) NULL, NULL, "dhcp4 (%s): %s", - nm_dhcp_client_get_iface(NM_DHCP_CLIENT(self)), + client_config->iface, event->log.message); } } break; @@ -890,13 +877,12 @@ dhcp4_event_cb(int fd, GIOCondition condition, gpointer user_data) */ _LOGE("error %d dispatching events", r); nm_clear_g_source_inst(&priv->event_source); - nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL, NULL); + nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL); return G_SOURCE_REMOVE; } - while (!n_dhcp4_client_pop_event(priv->client, &event) && event) { + while (!n_dhcp4_client_pop_event(priv->client, &event) && event) dhcp4_event_handle(self, event); - } return G_SOURCE_CONTINUE; } @@ -914,23 +900,26 @@ nettools_create(NMDhcpNettools *self, GError **error) gsize hwaddr_len; gsize bcast_hwaddr_len; GBytes * client_id; - gs_unref_bytes GBytes *client_id_new = NULL; - const uint8_t * client_id_arr; - size_t client_id_len; - int r, fd, arp_type, transport; + gs_unref_bytes GBytes * client_id_new = NULL; + const uint8_t * client_id_arr; + size_t client_id_len; + int r, fd, arp_type, transport; + const NMDhcpClientConfig *client_config; + + client_config = nm_dhcp_client_get_config(NM_DHCP_CLIENT(self)); g_return_val_if_fail(!priv->client, FALSE); /* TODO: honor nm_dhcp_client_get_anycast_address() */ - hwaddr = nm_dhcp_client_get_hw_addr(NM_DHCP_CLIENT(self)); + hwaddr = client_config->hwaddr; if (!hwaddr || !(hwaddr_arr = g_bytes_get_data(hwaddr, &hwaddr_len)) || (arp_type = nm_utils_arp_type_detect_from_hwaddrlen(hwaddr_len)) < 0) { nm_utils_error_set_literal(error, NM_UTILS_ERROR_UNKNOWN, "invalid MAC address"); return FALSE; } - bcast_hwaddr = nm_dhcp_client_get_broadcast_hw_addr(NM_DHCP_CLIENT(self)); + bcast_hwaddr = client_config->bcast_hwaddr; bcast_hwaddr_arr = g_bytes_get_data(bcast_hwaddr, &bcast_hwaddr_len); switch (arp_type) { @@ -947,7 +936,7 @@ nettools_create(NMDhcpNettools *self, GError **error) /* Note that we always set a client-id. In particular for infiniband that is necessary, * see https://tools.ietf.org/html/rfc4390#section-2.1 . */ - client_id = nm_dhcp_client_get_client_id(NM_DHCP_CLIENT(self)); + client_id = client_config->client_id; if (!client_id) { client_id_new = nm_utils_dhcp_client_id_mac(arp_type, hwaddr_arr, hwaddr_len); client_id = client_id_new; @@ -971,10 +960,7 @@ nettools_create(NMDhcpNettools *self, GError **error) n_dhcp4_client_config_set_transport(config, transport); n_dhcp4_client_config_set_mac(config, hwaddr_arr, hwaddr_len); n_dhcp4_client_config_set_broadcast_mac(config, bcast_hwaddr_arr, bcast_hwaddr_len); - n_dhcp4_client_config_set_request_broadcast( - config, - NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(NM_DHCP_CLIENT(self)), - NM_DHCP_CLIENT_FLAGS_REQUEST_BROADCAST)); + n_dhcp4_client_config_set_request_broadcast(config, client_config->v4.request_broadcast); r = n_dhcp4_client_config_set_client_id(config, client_id_arr, NM_MIN(client_id_len, 1 + _NM_MAX_CLIENT_ID_LEN)); @@ -1033,7 +1019,7 @@ decline(NMDhcpClient *client, const char *error_message, GError **error) g_return_val_if_fail(priv->lease, FALSE); - _LOGT("dhcp4-client: decline"); + _LOGT("dhcp4-client: decline (%s)", error_message); r = n_dhcp4_client_lease_decline(priv->lease, error_message); if (r) { @@ -1063,19 +1049,20 @@ fqdn_flags_to_wire(NMDhcpHostnameFlags flags) } static gboolean -ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) +ip4_start(NMDhcpClient *client, GError **error) { nm_auto(n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *config = NULL; - NMDhcpNettools * self = NM_DHCP_NETTOOLS(client); - NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self); - gs_free char * lease_file = NULL; - struct in_addr last_addr = {0}; - const char * hostname; - const char * mud_url; - GBytes * vendor_class_identifier; - int r, i; + NMDhcpNettools * self = NM_DHCP_NETTOOLS(client); + NMDhcpNettoolsPrivate * priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self); + const NMDhcpClientConfig *client_config; + gs_free char * lease_file = NULL; + struct in_addr last_addr = {0}; + int r, i; + + client_config = nm_dhcp_client_get_config(client); g_return_val_if_fail(!priv->probe, FALSE); + g_return_val_if_fail(client_config, FALSE); if (!nettools_create(self, error)) return FALSE; @@ -1094,12 +1081,12 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) nm_dhcp_utils_get_leasefile_path(AF_INET, "internal", - nm_dhcp_client_get_iface(client), - nm_dhcp_client_get_uuid(client), + client_config->iface, + client_config->uuid, &lease_file); - if (last_ip4_address) - inet_pton(AF_INET, last_ip4_address, &last_addr); + if (client_config->v4.last_address) + inet_pton(AF_INET, client_config->v4.last_address, &last_addr); else { /* * TODO: we stick to the systemd-networkd lease file format. Quite easy for now to @@ -1129,31 +1116,33 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) } } - mud_url = nm_dhcp_client_get_mud_url(client); - if (mud_url) { + if (client_config->mud_url) { r = n_dhcp4_client_probe_config_append_option(config, NM_DHCP_OPTION_DHCP4_MUD_URL, - mud_url, - strlen(mud_url)); + client_config->mud_url, + strlen(client_config->mud_url)); if (r) { set_error_nettools(error, r, "failed to set MUD URL"); return FALSE; } } - hostname = nm_dhcp_client_get_hostname(client); - if (hostname) { - if (NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(client), NM_DHCP_CLIENT_FLAGS_USE_FQDN)) { + + if (client_config->hostname) { + if (client_config->use_fqdn) { uint8_t buffer[255]; NMDhcpHostnameFlags flags; size_t fqdn_len; - flags = nm_dhcp_client_get_hostname_flags(client); + flags = client_config->hostname_flags; buffer[0] = fqdn_flags_to_wire(flags); buffer[1] = 0; /* RCODE1 (deprecated) */ buffer[2] = 0; /* RCODE2 (deprecated) */ if (flags & NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED) { - r = nm_sd_dns_name_to_wire_format(hostname, buffer + 3, sizeof(buffer) - 3, FALSE); + r = nm_sd_dns_name_to_wire_format(client_config->hostname, + buffer + 3, + sizeof(buffer) - 3, + FALSE); if (r <= 0) { if (r < 0) nm_utils_error_set_errno(error, r, "failed to convert DHCP FQDN: %s"); @@ -1163,12 +1152,12 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) } fqdn_len = r; } else { - fqdn_len = strlen(hostname); + fqdn_len = strlen(client_config->hostname); if (fqdn_len > sizeof(buffer) - 3) { nm_utils_error_set(error, r, "failed to set DHCP FQDN: name too long"); return FALSE; } - memcpy(buffer + 3, hostname, fqdn_len); + memcpy(buffer + 3, client_config->hostname, fqdn_len); } r = n_dhcp4_client_probe_config_append_option(config, @@ -1182,8 +1171,8 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) } else { r = n_dhcp4_client_probe_config_append_option(config, NM_DHCP_OPTION_DHCP4_HOST_NAME, - hostname, - strlen(hostname)); + client_config->hostname, + strlen(client_config->hostname)); if (r) { set_error_nettools(error, r, "failed to set DHCP hostname"); return FALSE; @@ -1191,12 +1180,11 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) } } - vendor_class_identifier = nm_dhcp_client_get_vendor_class_identifier(client); - if (vendor_class_identifier) { + if (client_config->vendor_class_identifier) { const void *option_data; gsize option_size; - option_data = g_bytes_get_data(vendor_class_identifier, &option_size); + option_data = g_bytes_get_data(client_config->vendor_class_identifier, &option_size); nm_assert(option_data); nm_assert(option_size <= 255); diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c index af1d2238b4..f5b75b50de 100644 --- a/src/core/dhcp/nm-dhcp-systemd.c +++ b/src/core/dhcp/nm-dhcp-systemd.c @@ -17,6 +17,7 @@ #include "libnm-std-aux/unaligned.h" #include "nm-utils.h" +#include "nm-l3-config-data.h" #include "nm-dhcp-utils.h" #include "nm-dhcp-options.h" #include "nm-core-utils.h" @@ -51,8 +52,6 @@ typedef struct { char * lease_file; guint request_count; - - bool privacy : 1; } NMDhcpSystemdPrivate; struct _NMDhcpSystemd { @@ -70,18 +69,15 @@ G_DEFINE_TYPE(NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT) /*****************************************************************************/ -static NMIP4Config * +static NML3ConfigData * lease_to_ip4_config(NMDedupMultiIndex *multi_idx, const char * iface, int ifindex, sd_dhcp_lease * lease, - guint32 route_table, - guint32 route_metric, - GHashTable ** out_options, GError ** error) { - gs_unref_object NMIP4Config *ip4_config = NULL; - gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + gs_unref_hashtable GHashTable *options = NULL; const struct in_addr * addr_list; char addr_str[NM_UTILS_INET_ADDRSTRLEN]; const char * s; @@ -92,7 +88,6 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, int i, num; const void * data; gsize data_len; - gboolean metered = FALSE; gboolean has_router_from_classless = FALSE; gboolean has_classless_route = FALSE; gboolean has_static_route = FALSE; @@ -133,9 +128,9 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, return NULL; } - ip4_config = nm_ip4_config_new(multi_idx, ifindex); + l3cd = nm_l3_config_data_new(multi_idx, ifindex, NM_IP_CONFIG_SOURCE_DHCP); - options = out_options ? nm_dhcp_option_create_options_dict() : NULL; + options = nm_dhcp_option_create_options_dict(); _nm_utils_inet4_ntop(a_address.s_addr, addr_str); nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, addr_str); @@ -160,16 +155,16 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NM_NEXT_SERVER, addr_str); } - nm_ip4_config_add_address(ip4_config, - &((const NMPlatformIP4Address){ - .address = a_address.s_addr, - .peer_address = a_address.s_addr, - .plen = a_plen, - .addr_source = NM_IP_CONFIG_SOURCE_DHCP, - .timestamp = ts, - .lifetime = a_lifetime, - .preferred = a_lifetime, - })); + nm_l3_config_data_add_address_4(l3cd, + &((const NMPlatformIP4Address){ + .address = a_address.s_addr, + .peer_address = a_address.s_addr, + .plen = a_plen, + .addr_source = NM_IP_CONFIG_SOURCE_DHCP, + .timestamp = ts, + .lifetime = a_lifetime, + .preferred = a_lifetime, + })); if (sd_dhcp_lease_get_server_identifier(lease, &server_id) >= 0) { _nm_utils_inet4_ntop(server_id.s_addr, addr_str); @@ -193,7 +188,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, * See https://github.com/systemd/systemd/issues/4524. */ continue; } - nm_ip4_config_add_nameserver(ip4_config, addr_list[i].s_addr); + nm_l3_config_data_add_nameserver(l3cd, AF_INET, &addr_list[i].s_addr); } nm_dhcp_option_add_option(options, AF_INET, @@ -206,7 +201,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, nm_gstring_prepare(&str); for (i = 0; i < num; i++) { g_string_append(nm_gstring_add_space_delimiter(str), search_domains[i]); - nm_ip4_config_add_search(ip4_config, search_domains[i]); + nm_l3_config_data_add_search(l3cd, AF_INET, search_domains[i]); } nm_dhcp_option_add_option(options, AF_INET, @@ -224,7 +219,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, * As systemd escapes such characters, split them at \\032. */ domains = g_strsplit(s, "\\032", 0); for (d = domains; *d; d++) - nm_ip4_config_add_domain(ip4_config, *d); + nm_l3_config_data_add_domain(l3cd, AF_INET, *d); } if (sd_dhcp_lease_get_hostname(lease, &s) >= 0) { @@ -233,9 +228,9 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, num = sd_dhcp_lease_get_routes(lease, &routes); if (num > 0) { - nm_auto_free_gstring GString *str_classless = NULL; - nm_auto_free_gstring GString *str_static = NULL; - guint32 default_route_metric = route_metric; + nm_auto_free_gstring GString *str_classless = NULL; + nm_auto_free_gstring GString *str_static = NULL; + guint32 default_route_metric_offset = 0; for (i = 0; i < num; i++) { switch (sd_dhcp_route_get_option(routes[i])) { @@ -307,25 +302,22 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, if (r_plen == 0) { /* if there are multiple default routes, we add them with differing * metrics. */ - m = default_route_metric; - if (default_route_metric < G_MAXUINT32) - default_route_metric++; - + m = default_route_metric_offset++; has_router_from_classless = TRUE; } else - m = route_metric; + m = 0; - nm_ip4_config_add_route( - ip4_config, - &((const NMPlatformIP4Route){ - .network = network_net, - .plen = r_plen, - .gateway = r_gateway.s_addr, - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, - .metric = m, - .table_coerced = nm_platform_route_table_coerce(route_table), - }), - NULL); + nm_l3_config_data_add_route_4(l3cd, + &((const NMPlatformIP4Route){ + .network = network_net, + .plen = r_plen, + .gateway = r_gateway.s_addr, + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .metric_any = TRUE, + .metric = m, + .table_any = TRUE, + .table_coerced = 0, + })); } if (str_classless && str_classless->len > 0) @@ -342,7 +334,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, num = sd_dhcp_lease_get_router(lease, &a_router); if (num > 0) { - guint32 default_route_metric = route_metric; + guint32 default_route_metric_offset = 0; nm_gstring_prepare(&str); for (i = 0; i < num; i++) { @@ -368,26 +360,24 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, /* if there are multiple default routes, we add them with differing * metrics. */ - m = default_route_metric; - if (default_route_metric < G_MAXUINT32) - default_route_metric++; + m = default_route_metric_offset++; - nm_ip4_config_add_route( - ip4_config, - &((const NMPlatformIP4Route){ - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, - .gateway = a_router[i].s_addr, - .table_coerced = nm_platform_route_table_coerce(route_table), - .metric = m, - }), - NULL); + nm_l3_config_data_add_route_4(l3cd, + &((const NMPlatformIP4Route){ + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .gateway = a_router[i].s_addr, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, + .metric = m, + })); } nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_ROUTER, str->str); } if (sd_dhcp_lease_get_mtu(lease, &mtu) >= 0 && mtu) { nm_dhcp_option_add_option_u64(options, AF_INET, NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, mtu); - nm_ip4_config_set_mtu(ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP); + nm_l3_config_data_set_mtu(l3cd, mtu); } num = sd_dhcp_lease_get_ntp(lease, &addr_list); @@ -422,9 +412,10 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE, s); } - if (sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len) >= 0) - metered = !!memmem(data, data_len, "ANDROID_METERED", NM_STRLEN("ANDROID_METERED")); - nm_ip4_config_set_metered(ip4_config, metered); + if (sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len) >= 0) { + if (!!memmem(data, data_len, "ANDROID_METERED", NM_STRLEN("ANDROID_METERED"))) + nm_l3_config_data_set_metered(l3cd, TRUE); + } num = nm_sd_dhcp_lease_get_private_options(lease, &private_options); if (num > 0) { @@ -436,12 +427,18 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, if (code == NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY) { if (nm_dhcp_lease_data_parse_cstr(l_data, l_data_len, &l_data_len)) { - nm_dhcp_option_add_option_utf8safe_escape( - options, - AF_INET, - NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY, - l_data, - l_data_len); + gs_free char *to_free = NULL; + const char * escaped; + + escaped = + nm_utils_buf_utf8safe_escape((char *) l_data, l_data_len, 0, &to_free); + nm_dhcp_option_add_option(options, + AF_INET, + NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY, + escaped ?: ""); + + nm_l3_config_data_set_proxy_method(l3cd, NM_PROXY_CONFIG_METHOD_AUTO); + nm_l3_config_data_set_proxy_pac_url(l3cd, escaped ?: ""); } continue; } @@ -458,8 +455,12 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, nm_dhcp_option_take_option(options, AF_INET, code, option_string); } } - NM_SET_OUT(out_options, g_steal_pointer(&options)); - return g_steal_pointer(&ip4_config); + + nm_dhcp_option_add_requests_to_options(options, AF_INET); + + nm_l3_config_data_set_dhcp_lease_from_options(l3cd, AF_INET, g_steal_pointer(&options)); + + return g_steal_pointer(&l3cd); } /*****************************************************************************/ @@ -467,43 +468,37 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, static void bound4_handle(NMDhcpSystemd *self, gboolean extended) { - NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE(self); - const char * iface = nm_dhcp_client_get_iface(NM_DHCP_CLIENT(self)); - gs_unref_object NMIP4Config *ip4_config = NULL; - gs_unref_hashtable GHashTable *options = NULL; - sd_dhcp_lease * lease = NULL; - GError * error = NULL; + NMDhcpSystemdPrivate * priv = NM_DHCP_SYSTEMD_GET_PRIVATE(self); + const char * iface = nm_dhcp_client_get_iface(NM_DHCP_CLIENT(self)); + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + sd_dhcp_lease * lease = NULL; + GError * error = NULL; if (sd_dhcp_client_get_lease(priv->client4, &lease) < 0 || !lease) { _LOGW("no lease!"); - nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL, NULL); + nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL); return; } _LOGD("lease available"); - ip4_config = lease_to_ip4_config(nm_dhcp_client_get_multi_idx(NM_DHCP_CLIENT(self)), - iface, - nm_dhcp_client_get_ifindex(NM_DHCP_CLIENT(self)), - lease, - nm_dhcp_client_get_route_table(NM_DHCP_CLIENT(self)), - nm_dhcp_client_get_route_metric(NM_DHCP_CLIENT(self)), - &options, - &error); - if (!ip4_config) { + l3cd = lease_to_ip4_config(nm_dhcp_client_get_multi_idx(NM_DHCP_CLIENT(self)), + iface, + nm_dhcp_client_get_ifindex(NM_DHCP_CLIENT(self)), + lease, + &error); + if (!l3cd) { _LOGW("%s", error->message); g_clear_error(&error); - nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL, NULL); + nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL); return; } - nm_dhcp_option_add_requests_to_options(options, AF_INET); dhcp_lease_save(lease, priv->lease_file); nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), extended ? NM_DHCP_STATE_EXTENDED : NM_DHCP_STATE_BOUND, - NM_IP_CONFIG_CAST(ip4_config), - options); + l3cd); } static int @@ -522,10 +517,10 @@ dhcp_event_cb(sd_dhcp_client *client, int event, gpointer user_data) switch (event) { case SD_DHCP_CLIENT_EVENT_EXPIRED: - nm_dhcp_client_set_state(NM_DHCP_CLIENT(user_data), NM_DHCP_STATE_EXPIRE, NULL, NULL); + nm_dhcp_client_set_state(NM_DHCP_CLIENT(user_data), NM_DHCP_STATE_EXPIRE, NULL); break; case SD_DHCP_CLIENT_EVENT_STOP: - nm_dhcp_client_set_state(NM_DHCP_CLIENT(user_data), NM_DHCP_STATE_FAIL, NULL, NULL); + nm_dhcp_client_set_state(NM_DHCP_CLIENT(user_data), NM_DHCP_STATE_FAIL, NULL); break; case SD_DHCP_CLIENT_EVENT_RENEW: case SD_DHCP_CLIENT_EVENT_IP_CHANGE: @@ -558,11 +553,12 @@ dhcp_event_cb(sd_dhcp_client *client, int event, gpointer user_data) } static gboolean -ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) +ip4_start(NMDhcpClient *client, GError **error) { - nm_auto(sd_dhcp_client_unrefp) sd_dhcp_client *sd_client = NULL; - NMDhcpSystemd * self = NM_DHCP_SYSTEMD(client); - NMDhcpSystemdPrivate * priv = NM_DHCP_SYSTEMD_GET_PRIVATE(self); + nm_auto(sd_dhcp_client_unrefp) sd_dhcp_client *sd_client = NULL; + NMDhcpSystemd * self = NM_DHCP_SYSTEMD(client); + NMDhcpSystemdPrivate * priv = NM_DHCP_SYSTEMD_GET_PRIVATE(self); + const NMDhcpClientConfig * client_config; gs_free char * lease_file = NULL; GBytes * hwaddr; const uint8_t * hwaddr_arr; @@ -584,6 +580,8 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) g_return_val_if_fail(!priv->client4, FALSE); g_return_val_if_fail(!priv->client6, FALSE); + client_config = nm_dhcp_client_get_config(client); + /* TODO: honor nm_dhcp_client_get_anycast_address() */ r = sd_dhcp_client_new(&sd_client, FALSE); @@ -600,7 +598,7 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) return FALSE; } - hwaddr = nm_dhcp_client_get_hw_addr(client); + hwaddr = client_config->hwaddr; if (!hwaddr || !(hwaddr_arr = g_bytes_get_data(hwaddr, &hwaddr_len)) || (arp_type = nm_utils_arp_type_detect_from_hwaddrlen(hwaddr_len)) < 0) { nm_utils_error_set_literal(error, NM_UTILS_ERROR_UNKNOWN, "invalid MAC address"); @@ -608,7 +606,8 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) } bcast_hwaddr_arr = NULL; - if ((bcast_hwaddr = nm_dhcp_client_get_broadcast_hw_addr(NM_DHCP_CLIENT(self)))) { + bcast_hwaddr = client_config->bcast_hwaddr; + if (bcast_hwaddr) { bcast_hwaddr_arr = g_bytes_get_data(bcast_hwaddr, &bcast_hwaddr_len); if (bcast_hwaddr_len != hwaddr_len) bcast_hwaddr_arr = NULL; @@ -632,12 +631,12 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) nm_dhcp_utils_get_leasefile_path(AF_INET, "internal", - nm_dhcp_client_get_iface(client), - nm_dhcp_client_get_uuid(client), + client_config->iface, + client_config->uuid, &lease_file); - if (last_ip4_address) - inet_pton(AF_INET, last_ip4_address, &last_addr); + if (client_config->v4.last_address) + inet_pton(AF_INET, client_config->v4.last_address, &last_addr); else { nm_auto(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; @@ -646,9 +645,7 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) sd_dhcp_lease_get_address(lease, &last_addr); } - r = sd_dhcp_client_set_request_broadcast(sd_client, - NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(client), - NM_DHCP_CLIENT_FLAGS_REQUEST_BROADCAST)); + r = sd_dhcp_client_set_request_broadcast(sd_client, client_config->v4.request_broadcast); nm_assert(r >= 0); if (last_addr.s_addr) { @@ -659,7 +656,7 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) } } - client_id = nm_dhcp_client_get_client_id(client); + client_id = client_config->client_id; if (!client_id) { client_id_new = nm_utils_dhcp_client_id_mac(arp_type, hwaddr_arr, hwaddr_len); client_id = client_id_new; @@ -694,7 +691,7 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) } } - hostname = nm_dhcp_client_get_hostname(client); + hostname = client_config->hostname; if (hostname) { /* FIXME: sd-dhcp decides which hostname/FQDN option to send (12 or 81) * only based on whether the hostname has a domain part or not. At the @@ -707,7 +704,7 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) } } - mud_url = nm_dhcp_client_get_mud_url(client); + mud_url = client_config->mud_url; if (mud_url) { r = sd_dhcp_client_set_mud_url(sd_client, mud_url); if (r < 0) { @@ -716,7 +713,7 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) } } - vendor_class_identifier = nm_dhcp_client_get_vendor_class_identifier(client); + vendor_class_identifier = client_config->vendor_class_identifier; if (vendor_class_identifier) { const char *option_data; gsize len; @@ -745,7 +742,7 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) g_free(priv->lease_file); priv->lease_file = g_steal_pointer(&lease_file); - nm_dhcp_client_set_client_id(client, client_id); + nm_dhcp_client_set_effective_client_id(client, client_id); r = sd_dhcp_client_start(priv->client4); if (r < 0) { @@ -759,32 +756,32 @@ ip4_start(NMDhcpClient *client, const char *last_ip4_address, GError **error) return TRUE; } -static NMIP6Config * +static NML3ConfigData * lease_to_ip6_config(NMDedupMultiIndex *multi_idx, const char * iface, int ifindex, sd_dhcp6_lease * lease, gboolean info_only, - GHashTable ** out_options, gint32 ts, GError ** error) { - gs_unref_object NMIP6Config *ip6_config = NULL; - gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + gs_unref_hashtable GHashTable *options = NULL; struct in6_addr tmp_addr; const struct in6_addr * dns; uint32_t lft_pref, lft_valid; char addr_str[NM_UTILS_INET_ADDRSTRLEN]; char ** domains; const char * s; - nm_auto_free_gstring GString *str = NULL; + nm_auto_free_gstring GString *str = NULL; + gboolean has_any_addresses = FALSE; int num, i; nm_assert(lease); - ip6_config = nm_ip6_config_new(multi_idx, ifindex); + l3cd = nm_l3_config_data_new(multi_idx, ifindex, NM_IP_CONFIG_SOURCE_DHCP); - options = out_options ? nm_dhcp_option_create_options_dict() : NULL; + options = nm_dhcp_option_create_options_dict(); sd_dhcp6_lease_reset_address_iter(lease); nm_gstring_prepare(&str); @@ -798,15 +795,19 @@ lease_to_ip6_config(NMDedupMultiIndex *multi_idx, .addr_source = NM_IP_CONFIG_SOURCE_DHCP, }; - nm_ip6_config_add_address(ip6_config, &address); + nm_l3_config_data_add_address_6(l3cd, &address); _nm_utils_inet6_ntop(&tmp_addr, addr_str); g_string_append(nm_gstring_add_space_delimiter(str), addr_str); - }; - if (str->len) - nm_dhcp_option_add_option(options, AF_INET6, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS, str->str); - if (!info_only && nm_ip6_config_get_num_addresses(ip6_config) == 0) { + has_any_addresses = TRUE; + } + + if (str->len) { + nm_dhcp_option_add_option(options, AF_INET6, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS, str->str); + } + + if (!info_only && !has_any_addresses) { g_set_error_literal(error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, @@ -820,7 +821,7 @@ lease_to_ip6_config(NMDedupMultiIndex *multi_idx, for (i = 0; i < num; i++) { _nm_utils_inet6_ntop(&dns[i], addr_str); g_string_append(nm_gstring_add_space_delimiter(str), addr_str); - nm_ip6_config_add_nameserver(ip6_config, &dns[i]); + nm_l3_config_data_add_nameserver(l3cd, AF_INET6, &dns[i]); } nm_dhcp_option_add_option(options, AF_INET6, NM_DHCP_OPTION_DHCP6_DNS_SERVERS, str->str); } @@ -830,7 +831,7 @@ lease_to_ip6_config(NMDedupMultiIndex *multi_idx, nm_gstring_prepare(&str); for (i = 0; i < num; i++) { g_string_append(nm_gstring_add_space_delimiter(str), domains[i]); - nm_ip6_config_add_search(ip6_config, domains[i]); + nm_l3_config_data_add_search(l3cd, AF_INET6, domains[i]); } nm_dhcp_option_add_option(options, AF_INET6, NM_DHCP_OPTION_DHCP6_DOMAIN_LIST, str->str); } @@ -839,51 +840,48 @@ lease_to_ip6_config(NMDedupMultiIndex *multi_idx, nm_dhcp_option_add_option(options, AF_INET6, NM_DHCP_OPTION_DHCP6_FQDN, s); } - NM_SET_OUT(out_options, g_steal_pointer(&options)); - return g_steal_pointer(&ip6_config); + nm_l3_config_data_set_dhcp_lease_from_options(l3cd, AF_INET6, g_steal_pointer(&options)); + + return g_steal_pointer(&l3cd); } static void bound6_handle(NMDhcpSystemd *self) { - NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE(self); - const gint32 ts = nm_utils_get_monotonic_timestamp_sec(); - const char * iface = nm_dhcp_client_get_iface(NM_DHCP_CLIENT(self)); - gs_unref_object NMIP6Config *ip6_config = NULL; - gs_unref_hashtable GHashTable *options = NULL; - gs_free_error GError *error = NULL; - NMPlatformIP6Address prefix = {0}; - sd_dhcp6_lease * lease = NULL; + NMDhcpSystemdPrivate * priv = NM_DHCP_SYSTEMD_GET_PRIVATE(self); + const gint32 ts = nm_utils_get_monotonic_timestamp_sec(); + const char * iface = nm_dhcp_client_get_iface(NM_DHCP_CLIENT(self)); + const NMDhcpClientConfig *client_config; + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + gs_free_error GError *error = NULL; + NMPlatformIP6Address prefix = {0}; + sd_dhcp6_lease * lease = NULL; + + client_config = nm_dhcp_client_get_config(NM_DHCP_CLIENT(self)); if (sd_dhcp6_client_get_lease(priv->client6, &lease) < 0 || !lease) { _LOGW(" no lease!"); - nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL, NULL); + nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL); return; } _LOGD("lease available"); - ip6_config = - lease_to_ip6_config(nm_dhcp_client_get_multi_idx(NM_DHCP_CLIENT(self)), - iface, - nm_dhcp_client_get_ifindex(NM_DHCP_CLIENT(self)), - lease, - NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(NM_DHCP_CLIENT(self)), - NM_DHCP_CLIENT_FLAGS_INFO_ONLY), - &options, - ts, - &error); + l3cd = lease_to_ip6_config(nm_dhcp_client_get_multi_idx(NM_DHCP_CLIENT(self)), + iface, + nm_dhcp_client_get_ifindex(NM_DHCP_CLIENT(self)), + lease, + client_config->v6.info_only, + ts, + &error); - if (!ip6_config) { + if (!l3cd) { _LOGW("%s", error->message); - nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL, NULL); + nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL); return; } - nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), - NM_DHCP_STATE_BOUND, - NM_IP_CONFIG_CAST(ip6_config), - options); + nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_BOUND, l3cd); sd_dhcp6_lease_reset_pd_prefix_iter(lease); while (!sd_dhcp6_lease_get_pd(lease, @@ -908,11 +906,11 @@ dhcp6_event_cb(sd_dhcp6_client *client, int event, gpointer user_data) switch (event) { case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX: - nm_dhcp_client_set_state(NM_DHCP_CLIENT(user_data), NM_DHCP_STATE_TIMEOUT, NULL, NULL); + nm_dhcp_client_set_state(NM_DHCP_CLIENT(user_data), NM_DHCP_STATE_TIMEOUT, NULL); break; case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE: case SD_DHCP6_CLIENT_EVENT_STOP: - nm_dhcp_client_set_state(NM_DHCP_CLIENT(user_data), NM_DHCP_STATE_FAIL, NULL, NULL); + nm_dhcp_client_set_state(NM_DHCP_CLIENT(user_data), NM_DHCP_STATE_FAIL, NULL); break; case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE: case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST: @@ -925,15 +923,12 @@ dhcp6_event_cb(sd_dhcp6_client *client, int event, gpointer user_data) } static gboolean -ip6_start(NMDhcpClient * client, - const struct in6_addr * ll_addr, - NMSettingIP6ConfigPrivacy privacy, - guint needed_prefixes, - GError ** error) +ip6_start(NMDhcpClient *client, const struct in6_addr *ll_addr, GError **error) { NMDhcpSystemd * self = NM_DHCP_SYSTEMD(client); NMDhcpSystemdPrivate * priv = NM_DHCP_SYSTEMD_GET_PRIVATE(self); nm_auto(sd_dhcp6_client_unrefp) sd_dhcp6_client *sd_client = NULL; + const NMDhcpClientConfig * client_config; const char * hostname; const char * mud_url; int r, i; @@ -944,10 +939,12 @@ ip6_start(NMDhcpClient * client, g_return_val_if_fail(!priv->client4, FALSE); g_return_val_if_fail(!priv->client6, FALSE); + client_config = nm_dhcp_client_get_config(client); + /* TODO: honor nm_dhcp_client_get_anycast_address() */ - if (!(duid = nm_dhcp_client_get_client_id(client)) - || !(duid_arr = g_bytes_get_data(duid, &duid_len)) || duid_len < 2) { + duid = nm_dhcp_client_get_effective_client_id(client); + if (!duid || !(duid_arr = g_bytes_get_data(duid, &duid_len)) || duid_len < 2) { nm_utils_error_set_literal(error, NM_UTILS_ERROR_UNKNOWN, "missing DUID"); g_return_val_if_reached(FALSE); } @@ -960,13 +957,13 @@ ip6_start(NMDhcpClient * client, _LOGT("dhcp-client6: set %p", sd_client); - if (NM_FLAGS_HAS(nm_dhcp_client_get_client_flags(client), NM_DHCP_CLIENT_FLAGS_INFO_ONLY)) { + if (client_config->v6.info_only) { sd_dhcp6_client_set_address_request(sd_client, 0); - if (needed_prefixes == 0) + if (client_config->v6.needed_prefixes == 0) sd_dhcp6_client_set_information_request(sd_client, 1); } - r = sd_dhcp6_client_set_iaid(sd_client, nm_dhcp_client_get_iaid(client)); + r = sd_dhcp6_client_set_iaid(sd_client, client_config->v6.iaid); if (r < 0) { nm_utils_error_set_errno(error, r, "failed to set IAID: %s"); return FALSE; @@ -1002,7 +999,7 @@ ip6_start(NMDhcpClient * client, } } - mud_url = nm_dhcp_client_get_mud_url(client); + mud_url = client_config->mud_url; if (mud_url) { r = sd_dhcp6_client_set_request_mud_url(sd_client, mud_url); if (r < 0) { @@ -1011,8 +1008,8 @@ ip6_start(NMDhcpClient * client, } } - if (needed_prefixes > 0) { - if (needed_prefixes > 1) + if (client_config->v6.needed_prefixes > 0) { + if (client_config->v6.needed_prefixes > 1) _LOGW("dhcp-client6: only one prefix request is supported"); /* FIXME: systemd-networkd API only allows to request a * single prefix */ @@ -1029,7 +1026,7 @@ ip6_start(NMDhcpClient * client, return FALSE; } - hostname = nm_dhcp_client_get_hostname(client); + hostname = client_config->hostname; r = sd_dhcp6_client_set_fqdn(sd_client, hostname); if (r < 0) { nm_utils_error_set_errno(error, r, "failed to set DHCP hostname: %s"); diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c index 3cc6987ada..b1ff1b97a9 100644 --- a/src/core/dhcp/nm-dhcp-utils.c +++ b/src/core/dhcp/nm-dhcp-utils.c @@ -14,6 +14,7 @@ #include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "nm-dhcp-utils.h" +#include "nm-l3-config-data.h" #include "nm-utils.h" #include "nm-config.h" #include "NetworkManagerUtils.h" @@ -24,12 +25,10 @@ /*****************************************************************************/ static gboolean -ip4_process_dhcpcd_rfc3442_routes(const char * iface, - const char * str, - guint32 route_table, - guint32 route_metric, - NMIP4Config *ip4_config, - guint32 * gwaddr) +ip4_process_dhcpcd_rfc3442_routes(const char * iface, + const char * str, + NML3ConfigData *l3cd, + guint32 * gwaddr) { gs_free const char **routes = NULL; const char ** r; @@ -45,10 +44,9 @@ ip4_process_dhcpcd_rfc3442_routes(const char * iface, } for (r = routes; *r; r += 2) { - char * slash; - NMPlatformIP4Route route; - int rt_cidr = 32; - guint32 rt_addr, rt_route; + char * slash; + int rt_cidr = 32; + guint32 rt_addr, rt_route; slash = strchr(*r, '/'); if (slash) { @@ -89,14 +87,18 @@ ip4_process_dhcpcd_rfc3442_routes(const char * iface, *r, rt_cidr, *(r + 1)); - memset(&route, 0, sizeof(route)); - route.network = nm_utils_ip4_address_clear_host_address(rt_addr, rt_cidr); - route.plen = rt_cidr; - route.gateway = rt_route; - route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; - route.metric = route_metric; - route.table_coerced = nm_platform_route_table_coerce(route_table); - nm_ip4_config_add_route(ip4_config, &route, NULL); + + nm_l3_config_data_add_route_4( + l3cd, + &((const NMPlatformIP4Route){ + .network = nm_utils_ip4_address_clear_host_address(rt_addr, rt_cidr), + .plen = rt_cidr, + .gateway = rt_route, + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .metric_any = TRUE, + .table_any = TRUE, + + })); } } @@ -153,12 +155,10 @@ process_dhclient_rfc3442_route(const char *const **p_octets, NMPlatformIP4Route } static gboolean -ip4_process_dhclient_rfc3442_routes(const char * iface, - const char * str, - guint32 route_table, - guint32 route_metric, - NMIP4Config *ip4_config, - guint32 * gwaddr) +ip4_process_dhclient_rfc3442_routes(const char * iface, + const char * str, + NML3ConfigData *l3cd, + guint32 * gwaddr) { gs_free const char **octets = NULL; const char *const * o; @@ -189,9 +189,12 @@ ip4_process_dhclient_rfc3442_routes(const char * iface, /* normal route */ route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; - route.metric = route_metric; - route.table_coerced = nm_platform_route_table_coerce(route_table); - nm_ip4_config_add_route(ip4_config, &route, NULL); + route.table_any = TRUE; + route.table_coerced = 0; + route.metric_any = TRUE; + route.metric = 0; + + nm_l3_config_data_add_route_4(l3cd, &route); _LOG2I(LOGD_DHCP4, iface, @@ -206,17 +209,15 @@ ip4_process_dhclient_rfc3442_routes(const char * iface, } static gboolean -ip4_process_classless_routes(const char * iface, - GHashTable * options, - guint32 route_table, - guint32 route_metric, - NMIP4Config *ip4_config, - guint32 * gwaddr) +ip4_process_classless_routes(const char * iface, + GHashTable * options, + NML3ConfigData *l3cd, + guint32 * gwaddr) { const char *str, *p; g_return_val_if_fail(options != NULL, FALSE); - g_return_val_if_fail(ip4_config != NULL, FALSE); + g_return_val_if_fail(l3cd != NULL, FALSE); *gwaddr = 0; @@ -265,28 +266,14 @@ ip4_process_classless_routes(const char * iface, if (strchr(str, '/')) { /* dhcpcd format */ - return ip4_process_dhcpcd_rfc3442_routes(iface, - str, - route_table, - route_metric, - ip4_config, - gwaddr); + return ip4_process_dhcpcd_rfc3442_routes(iface, str, l3cd, gwaddr); } - return ip4_process_dhclient_rfc3442_routes(iface, - str, - route_table, - route_metric, - ip4_config, - gwaddr); + return ip4_process_dhclient_rfc3442_routes(iface, str, l3cd, gwaddr); } static void -process_classful_routes(const char * iface, - GHashTable * options, - guint32 route_table, - guint32 route_metric, - NMIP4Config *ip4_config) +process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData *l3cd) { gs_free const char **searches = NULL; const char ** s; @@ -320,8 +307,10 @@ process_classful_routes(const char * iface, // FIXME: ensure the IP address and route are sane - memset(&route, 0, sizeof(route)); - route.network = rt_addr; + route = (NMPlatformIP4Route){ + .network = rt_addr, + }; + /* RFC 2132, updated by RFC 3442: * 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 @@ -333,12 +322,15 @@ process_classful_routes(const char * iface, } route.gateway = rt_route; route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; - route.metric = route_metric; - route.table_coerced = nm_platform_route_table_coerce(route_table); + route.table_any = TRUE; + route.table_coerced = 0; + route.metric_any = TRUE; + route.metric = 0; route.network = nm_utils_ip4_address_clear_host_address(route.network, route.plen); - nm_ip4_config_add_route(ip4_config, &route, NULL); + nm_l3_config_data_add_route_4(l3cd, &route); + _LOG2I(LOGD_DHCP, iface, " static route %s", @@ -347,7 +339,7 @@ process_classful_routes(const char * iface, } static void -process_domain_search(const char *iface, const char *str, GFunc add_func, gpointer user_data) +process_domain_search(int addr_family, const char *iface, const char *str, NML3ConfigData *l3cd) { gs_free const char **searches = NULL; gs_free char * unescaped = NULL; @@ -356,7 +348,7 @@ process_domain_search(const char *iface, const char *str, GFunc add_func, gpoint int i; g_return_if_fail(str != NULL); - g_return_if_fail(add_func != NULL); + nm_assert(l3cd); unescaped = g_strdup(str); @@ -379,46 +371,43 @@ process_domain_search(const char *iface, const char *str, GFunc add_func, gpoint searches = nm_strsplit_set(unescaped, " "); for (s = searches; searches && *s; s++) { _LOG2I(LOGD_DHCP, iface, " domain search '%s'", *s); - add_func((gpointer) *s, user_data); + nm_l3_config_data_add_search(l3cd, addr_family, *s); } } -static void -ip4_add_domain_search(gpointer data, gpointer user_data) -{ - nm_ip4_config_add_search(NM_IP4_CONFIG(user_data), (const char *) data); -} - -NMIP4Config * +NML3ConfigData * nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, int ifindex, const char * iface, - GHashTable * options, - guint32 route_table, - guint32 route_metric) + GHashTable * options) { - gs_unref_object NMIP4Config *ip4_config = NULL; - guint32 tmp_addr; - in_addr_t addr; - NMPlatformIP4Address address; - char * str = NULL; - gboolean gateway_has = FALSE; - guint32 gateway = 0; - guint8 plen = 0; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + guint32 tmp_addr; + in_addr_t addr; + NMPlatformIP4Address address; + char * str = NULL; + gboolean gateway_has = FALSE; + guint32 gateway = 0; + guint8 plen = 0; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + guint32 now; g_return_val_if_fail(options != NULL, NULL); - ip4_config = nm_ip4_config_new(multi_idx, ifindex); - memset(&address, 0, sizeof(address)); - address.timestamp = nm_utils_get_monotonic_timestamp_sec(); + l3cd = nm_l3_config_data_new(multi_idx, ifindex, NM_IP_CONFIG_SOURCE_DHCP); + + now = nm_utils_get_monotonic_timestamp_sec(); + + address = (NMPlatformIP4Address){ + .timestamp = now, + }; str = g_hash_table_lookup(options, "ip_address"); - if (str && (inet_pton(AF_INET, str, &addr) > 0)) - _LOG2I(LOGD_DHCP4, iface, " address %s", str); - else + if (!str || !nm_utils_parse_inaddr_bin(AF_INET, str, NULL, &addr)) return NULL; + _LOG2I(LOGD_DHCP4, iface, " address %s", str); + 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); @@ -433,13 +422,8 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, /* Routes: if the server returns classless static routes, we MUST ignore * the 'static_routes' option. */ - if (!ip4_process_classless_routes(iface, - options, - route_table, - route_metric, - ip4_config, - &gateway)) - process_classful_routes(iface, options, route_table, route_metric, ip4_config); + if (!ip4_process_classless_routes(iface, options, l3cd, &gateway)) + process_classful_routes(iface, options, l3cd); if (gateway) { _LOG2I(LOGD_DHCP4, iface, " gateway %s", _nm_utils_inet4_ntop(gateway, sbuf)); @@ -453,7 +437,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, gs_free const char **routers = nm_strsplit_set(str, " "); const char ** s; - for (s = routers; routers && *s; s++) { + for (s = routers; *s; s++) { /* FIXME: how to handle multiple routers? */ if (inet_pton(AF_INET, *s, &gateway) > 0) { _LOG2I(LOGD_DHCP4, iface, " gateway %s", *s); @@ -469,11 +453,13 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, const NMPlatformIP4Route r = { .rt_source = NM_IP_CONFIG_SOURCE_DHCP, .gateway = gateway, - .table_coerced = nm_platform_route_table_coerce(route_table), - .metric = route_metric, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, + .metric = 0, }; - nm_ip4_config_add_route(ip4_config, &r, NULL); + nm_l3_config_data_add_route_4(l3cd, &r); } str = g_hash_table_lookup(options, "dhcp_lease_time"); @@ -483,7 +469,8 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, } address.addr_source = NM_IP_CONFIG_SOURCE_DHCP; - nm_ip4_config_add_address(ip4_config, &address); + + nm_l3_config_data_add_address_4(l3cd, &address); str = g_hash_table_lookup(options, "host_name"); if (str) @@ -497,7 +484,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_ip4_config_add_nameserver(ip4_config, tmp_addr); + nm_l3_config_data_add_nameserver(l3cd, AF_INET, &tmp_addr); _LOG2I(LOGD_DHCP4, iface, " nameserver '%s'", *s); } } else @@ -512,13 +499,13 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, for (s = domains; domains && *s; s++) { _LOG2I(LOGD_DHCP4, iface, " domain name '%s'", *s); - nm_ip4_config_add_domain(ip4_config, *s); + nm_l3_config_data_add_domain(l3cd, AF_INET, *s); } } str = g_hash_table_lookup(options, "domain_search"); if (str) - process_domain_search(iface, str, ip4_add_domain_search, ip4_config); + process_domain_search(AF_INET, iface, str, l3cd); str = g_hash_table_lookup(options, "netbios_name_servers"); if (str) { @@ -528,7 +515,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, for (s = nbns; nbns && *s; s++) { if (inet_pton(AF_INET, *s, &tmp_addr) > 0) { if (tmp_addr) { - nm_ip4_config_add_wins(ip4_config, tmp_addr); + nm_l3_config_data_add_wins(l3cd, tmp_addr); _LOG2I(LOGD_DHCP4, iface, " wins '%s'", *s); } } else @@ -546,13 +533,13 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, return NULL; if (int_mtu > 576) - nm_ip4_config_set_mtu(ip4_config, int_mtu, NM_IP_CONFIG_SOURCE_DHCP); + nm_l3_config_data_set_mtu(l3cd, int_mtu); } str = g_hash_table_lookup(options, "nis_domain"); if (str) { _LOG2I(LOGD_DHCP4, iface, " NIS domain '%s'", str); - nm_ip4_config_set_nis_domain(ip4_config, str); + nm_l3_config_data_add_domain(l3cd, AF_INET, str); } str = g_hash_table_lookup(options, "nis_servers"); @@ -563,7 +550,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, for (s = nis; nis && *s; s++) { if (inet_pton(AF_INET, *s, &tmp_addr) > 0) { if (tmp_addr) { - nm_ip4_config_add_nis_server(ip4_config, tmp_addr); + nm_l3_config_data_add_nis_server(l3cd, tmp_addr); _LOG2I(LOGD_DHCP4, iface, " nis '%s'", *s); } } else @@ -572,19 +559,20 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, } str = g_hash_table_lookup(options, "vendor_encapsulated_options"); - nm_ip4_config_set_metered(ip4_config, str && strstr(str, "ANDROID_METERED")); + if (str && strstr(str, "ANDROID_METERED")) + nm_l3_config_data_set_metered(l3cd, TRUE); - return g_steal_pointer(&ip4_config); + str = g_hash_table_lookup(options, "wpad"); + if (str) { + nm_l3_config_data_set_proxy_method(l3cd, NM_PROXY_CONFIG_METHOD_AUTO); + nm_l3_config_data_set_proxy_pac_url(l3cd, str); + } + + return g_steal_pointer(&l3cd); } /*****************************************************************************/ -static void -ip6_add_domain_search(gpointer data, gpointer user_data) -{ - nm_ip6_config_add_search(NM_IP6_CONFIG(user_data), (const char *) data); -} - NMPlatformIP6Address nm_dhcp_utils_ip6_prefix_from_options(GHashTable *options) { @@ -635,25 +623,29 @@ nm_dhcp_utils_ip6_prefix_from_options(GHashTable *options) return address; } -NMIP6Config * +NML3ConfigData * nm_dhcp_utils_ip6_config_from_options(NMDedupMultiIndex *multi_idx, int ifindex, const char * iface, GHashTable * options, gboolean info_only) { - gs_unref_object NMIP6Config *ip6_config = NULL; - struct in6_addr tmp_addr; - NMPlatformIP6Address address; - char * str = NULL; + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + struct in6_addr tmp_addr; + NMPlatformIP6Address address; + char * str = NULL; + guint32 now; g_return_val_if_fail(options != NULL, NULL); - memset(&address, 0, sizeof(address)); - address.plen = 128; - address.timestamp = nm_utils_get_monotonic_timestamp_sec(); + now = nm_utils_get_monotonic_timestamp_sec(); - ip6_config = nm_ip6_config_new(multi_idx, ifindex); + address = (NMPlatformIP6Address){ + .plen = 128, + .timestamp = now, + }; + + l3cd = nm_l3_config_data_new(multi_idx, ifindex, NM_IP_CONFIG_SOURCE_DHCP); str = g_hash_table_lookup(options, "max_life"); if (str) { @@ -676,7 +668,7 @@ nm_dhcp_utils_ip6_config_from_options(NMDedupMultiIndex *multi_idx, address.address = tmp_addr; address.addr_source = NM_IP_CONFIG_SOURCE_DHCP; - nm_ip6_config_add_address(ip6_config, &address); + nm_l3_config_data_add_address_6(l3cd, &address); _LOG2I(LOGD_DHCP6, iface, " address %s", str); } else if (info_only == FALSE) { /* No address in Managed mode is a hard error */ @@ -695,7 +687,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_ip6_config_add_nameserver(ip6_config, &tmp_addr); + nm_l3_config_data_add_nameserver(l3cd, AF_INET6, &tmp_addr); _LOG2I(LOGD_DHCP6, iface, " nameserver '%s'", *s); } } else @@ -705,9 +697,9 @@ nm_dhcp_utils_ip6_config_from_options(NMDedupMultiIndex *multi_idx, str = g_hash_table_lookup(options, "dhcp6_domain_search"); if (str) - process_domain_search(iface, str, ip6_add_domain_search, ip6_config); + process_domain_search(AF_INET6, iface, str, l3cd); - return g_steal_pointer(&ip6_config); + return g_steal_pointer(&l3cd); } char * @@ -838,6 +830,65 @@ nm_dhcp_utils_get_dhcp6_event_id(GHashTable *lease) return g_strdup_printf("%s|%s", iaid, start); } +gboolean +nm_dhcp_utils_merge_new_dhcp6_lease(const NML3ConfigData * l3cd_old, + const NML3ConfigData * l3cd_new, + const NML3ConfigData **out_l3cd_merged) +{ + nm_auto_unref_l3cd_init NML3ConfigData *l3cd_merged = NULL; + const NMPlatformIP6Address * addr; + NMDhcpLease * lease_old; + NMDhcpLease * lease_new; + NMDedupMultiIter iter; + const char * start; + const char * iaid; + + nm_assert(out_l3cd_merged); + nm_assert(!*out_l3cd_merged); + + if (!l3cd_old) + return FALSE; + if (!l3cd_new) + return FALSE; + + lease_new = nm_l3_config_data_get_dhcp_lease(l3cd_new, AF_INET6); + if (!lease_new) + return FALSE; + + lease_old = nm_l3_config_data_get_dhcp_lease(l3cd_old, AF_INET6); + if (!lease_old) + return FALSE; + + start = nm_dhcp_lease_lookup_option(lease_new, "life_starts"); + if (!start) + return FALSE; + iaid = nm_dhcp_lease_lookup_option(lease_new, "iaid"); + if (!iaid) + return FALSE; + + if (!nm_streq0(start, nm_dhcp_lease_lookup_option(lease_old, "life_starts"))) + return FALSE; + if (!nm_streq0(iaid, nm_dhcp_lease_lookup_option(lease_old, "iaid"))) + return FALSE; + + /* If the server sends multiple IPv6 addresses, we receive a state + * changed event for each of them. Use the event ID to merge IPv6 + * addresses from the same transaction into a single configuration. + **/ + + l3cd_merged = nm_l3_config_data_new_clone(l3cd_old, -1); + + nm_l3_config_data_iter_ip6_address_for_each (&iter, l3cd_new, &addr) + nm_l3_config_data_add_address_6(l3cd_merged, addr); + + /* FIXME(l3cfg): Note that we keep the original NMDhcpLease. All we take from the new lease are the + * addresses. Maybe this is not right and we should merge the leases too?? */ + nm_l3_config_data_set_dhcp_lease(l3cd_merged, AF_INET6, lease_old); + + *out_l3cd_merged = nm_l3_config_data_ref_and_seal(g_steal_pointer(&l3cd_merged)); + return TRUE; +} + /*****************************************************************************/ gboolean diff --git a/src/core/dhcp/nm-dhcp-utils.h b/src/core/dhcp/nm-dhcp-utils.h index 69715f90fe..bee10de3f6 100644 --- a/src/core/dhcp/nm-dhcp-utils.h +++ b/src/core/dhcp/nm-dhcp-utils.h @@ -8,21 +8,18 @@ #include -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" +#include "nm-l3-config-data.h" -NMIP4Config *nm_dhcp_utils_ip4_config_from_options(struct _NMDedupMultiIndex *multi_idx, - int ifindex, - const char * iface, - GHashTable * options, - guint32 route_table, - guint32 route_metric); +NML3ConfigData *nm_dhcp_utils_ip4_config_from_options(struct _NMDedupMultiIndex *multi_idx, + int ifindex, + const char * iface, + GHashTable * options); -NMIP6Config *nm_dhcp_utils_ip6_config_from_options(struct _NMDedupMultiIndex *multi_idx, - int ifindex, - const char * iface, - GHashTable * options, - gboolean info_only); +NML3ConfigData *nm_dhcp_utils_ip6_config_from_options(struct _NMDedupMultiIndex *multi_idx, + int ifindex, + const char * iface, + GHashTable * options, + gboolean info_only); NMPlatformIP6Address nm_dhcp_utils_ip6_prefix_from_options(GHashTable *options); @@ -38,6 +35,10 @@ gboolean nm_dhcp_utils_get_leasefile_path(int addr_family, char *nm_dhcp_utils_get_dhcp6_event_id(GHashTable *lease); +gboolean nm_dhcp_utils_merge_new_dhcp6_lease(const NML3ConfigData * l3cd_old, + const NML3ConfigData * l3cd_new, + const NML3ConfigData **out_l3cd_merged); + /*****************************************************************************/ static inline gboolean diff --git a/src/core/dhcp/tests/test-dhcp-dhclient.c b/src/core/dhcp/tests/test-dhcp-dhclient.c index 9c6865cc8c..92e4548fbe 100644 --- a/src/core/dhcp/tests/test-dhcp-dhclient.c +++ b/src/core/dhcp/tests/test-dhcp-dhclient.c @@ -15,7 +15,6 @@ #include "dhcp/nm-dhcp-dhclient-utils.h" #include "dhcp/nm-dhcp-utils.h" #include "nm-utils.h" -#include "nm-ip4-config.h" #include "libnm-platform/nm-platform.h" #include "nm-test-utils-core.h" diff --git a/src/core/dhcp/tests/test-dhcp-utils.c b/src/core/dhcp/tests/test-dhcp-utils.c index 4d47e7e283..0fab5c15ad 100644 --- a/src/core/dhcp/tests/test-dhcp-utils.c +++ b/src/core/dhcp/tests/test-dhcp-utils.c @@ -20,20 +20,18 @@ /*****************************************************************************/ -static NMIP4Config * -_ip4_config_from_options(int ifindex, const char *iface, GHashTable *options, guint32 route_metric) +static const NML3ConfigData * +_ip4_config_from_options(int ifindex, const char *iface, GHashTable *options) { nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new(); - NMIP4Config * config; + NML3ConfigData * l3cd; - config = nm_dhcp_utils_ip4_config_from_options(multi_idx, - ifindex, - iface, - options, - RT_TABLE_MAIN, - route_metric); - g_assert(config); - return config; + l3cd = nm_dhcp_utils_ip4_config_from_options(multi_idx, ifindex, iface, options); + g_assert(NM_IS_L3_CONFIG_DATA(l3cd)); + g_assert(!nm_l3_config_data_is_sealed(l3cd)); + if (nmtst_get_rand_bool()) + nm_l3_config_data_seal(l3cd); + return l3cd; } typedef struct { @@ -74,58 +72,57 @@ static const Option generic_options[] = { static void test_generic_options(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const NMPlatformIP4Address * address; - const NMPlatformIP4Route * route; - guint32 tmp; - const char * expected_addr = "192.168.1.106"; - const char * expected_gw = "192.168.1.1"; - const char * expected_dns1 = "216.254.95.2"; - const char * expected_dns2 = "216.231.41.2"; - const char * expected_search1 = "foobar.com"; - const char * expected_search2 = "blah.foobar.com"; - const char * expected_route1_dest = "10.1.1.5"; - const char * expected_route1_gw = "10.1.1.1"; - const char * expected_route2_dest = "100.99.88.56"; - const char * expected_route2_gw = "10.1.1.1"; + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const NMPlatformIP4Address * address; + const NMPlatformIP4Route * route; + guint32 tmp; + const char * expected_addr = "192.168.1.106"; + const char * expected_gw = "192.168.1.1"; + const char * expected_dns1 = "216.254.95.2"; + const char * expected_dns2 = "216.231.41.2"; + const char * expected_search1 = "foobar.com"; + const char * expected_search2 = "blah.foobar.com"; + const char * expected_route1_dest = "10.1.1.5"; + const char * expected_route1_gw = "10.1.1.1"; + 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); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + options = fill_table(generic_options, NULL); + l3cd = _ip4_config_from_options(1, "eth0", options); - /* IP4 address */ - g_assert_cmpint(nm_ip4_config_get_num_addresses(ip4_config), ==, 1); - address = _nmtst_ip4_config_get_address(ip4_config, 0); + g_assert_cmpint(nm_l3_config_data_get_num_addresses(l3cd, AF_INET), ==, 1); + address = nmtst_l3_config_data_get_address_at_4(l3cd, 0); g_assert(inet_pton(AF_INET, expected_addr, &tmp) > 0); g_assert(address->address == tmp); g_assert(address->peer_address == tmp); g_assert_cmpint(address->plen, ==, 24); - /* Gateway */ - g_assert(inet_pton(AF_INET, expected_gw, &tmp) > 0); - g_assert(nmtst_ip4_config_get_gateway(ip4_config) == tmp); + nmtst_assert_ip_address(AF_INET, + nmtst_l3_config_data_get_best_gateway(l3cd, AF_INET), + expected_gw); - g_assert_cmpint(nm_ip4_config_get_num_wins(ip4_config), ==, 0); + g_assert(!nm_l3_config_data_get_wins(l3cd, &u)); + g_assert_cmpint(u, ==, 0); - g_assert_cmpint(nm_ip4_config_get_mtu(ip4_config), ==, 987); + g_assert_cmpint(nm_l3_config_data_get_mtu(l3cd), ==, 987); - /* Domain searches */ - g_assert_cmpint(nm_ip4_config_get_num_searches(ip4_config), ==, 2); - g_assert_cmpstr(nm_ip4_config_get_search(ip4_config, 0), ==, expected_search1); - g_assert_cmpstr(nm_ip4_config_get_search(ip4_config, 1), ==, expected_search2); + strarr = nm_l3_config_data_get_searches(l3cd, AF_INET, &u); + g_assert_cmpint(u, ==, 2); + g_assert_cmpstr(strarr[0], ==, expected_search1); + g_assert_cmpstr(strarr[1], ==, expected_search2); - /* DNS servers */ - g_assert_cmpint(nm_ip4_config_get_num_nameservers(ip4_config), ==, 2); - g_assert(inet_pton(AF_INET, expected_dns1, &tmp) > 0); - g_assert(nm_ip4_config_get_nameserver(ip4_config, 0) == tmp); - g_assert(inet_pton(AF_INET, expected_dns2, &tmp) > 0); - g_assert(nm_ip4_config_get_nameserver(ip4_config, 1) == tmp); + ia_arr = 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); - /* Routes */ - g_assert_cmpint(nm_ip4_config_get_num_routes(ip4_config), ==, 3); + g_assert_cmpint(nm_l3_config_data_get_num_routes(l3cd, AF_INET), ==, 3); - /* Route #1 */ - route = _nmtst_ip4_config_get_route(ip4_config, 0); + route = nmtst_l3_config_data_get_route_at_4(l3cd, 0); g_assert(inet_pton(AF_INET, expected_route1_dest, &tmp) > 0); g_assert(route->network == tmp); g_assert(inet_pton(AF_INET, expected_route1_gw, &tmp) > 0); @@ -133,69 +130,63 @@ test_generic_options(void) g_assert_cmpint(route->plen, ==, 32); g_assert_cmpint(route->metric, ==, 0); - /* Route #2 */ - route = _nmtst_ip4_config_get_route(ip4_config, 1); + route = nmtst_l3_config_data_get_route_at_4(l3cd, 1); g_assert(route->network == nmtst_inet4_from_string(expected_route2_dest)); g_assert(route->gateway == nmtst_inet4_from_string(expected_route2_gw)); g_assert_cmpint(route->plen, ==, 32); g_assert_cmpint(route->metric, ==, 0); - route = _nmtst_ip4_config_get_route(ip4_config, 2); + route = nmtst_l3_config_data_get_route_at_4(l3cd, 2); g_assert(route->network == nmtst_inet4_from_string("0.0.0.0")); g_assert(route->gateway == nmtst_inet4_from_string("192.168.1.1")); g_assert_cmpint(route->plen, ==, 0); g_assert_cmpint(route->metric, ==, 0); - - g_hash_table_destroy(options); } static void test_wins_options(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const NMPlatformIP4Address * address; - guint32 tmp; - const char * expected_wins1 = "63.12.199.5"; - const char * expected_wins2 = "150.4.88.120"; - static const Option data[] = {{"netbios_name_servers", "63.12.199.5 150.4.88.120"}, + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const NMPlatformIP4Address * address; + const char * expected_wins1 = "63.12.199.5"; + const char * expected_wins2 = "150.4.88.120"; + static const Option data[] = {{"netbios_name_servers", "63.12.199.5 150.4.88.120"}, {NULL, NULL}}; + const in_addr_t * ia_arr; + guint u; - options = fill_table(generic_options, NULL); - options = fill_table(data, options); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + options = fill_table(generic_options, NULL); + options = fill_table(data, options); + l3cd = _ip4_config_from_options(1, "eth0", options); - /* IP4 address */ - g_assert_cmpint(nm_ip4_config_get_num_addresses(ip4_config), ==, 1); - address = _nmtst_ip4_config_get_address(ip4_config, 0); + g_assert_cmpint(nm_l3_config_data_get_num_addresses(l3cd, AF_INET), ==, 1); + address = nmtst_l3_config_data_get_address_at_4(l3cd, 0); g_assert(address); - g_assert_cmpint(nm_ip4_config_get_num_wins(ip4_config), ==, 2); - g_assert(inet_pton(AF_INET, expected_wins1, &tmp) > 0); - g_assert(nm_ip4_config_get_wins(ip4_config, 0) == tmp); - g_assert(inet_pton(AF_INET, expected_wins2, &tmp) > 0); - g_assert(nm_ip4_config_get_wins(ip4_config, 1) == tmp); - g_hash_table_destroy(options); + ia_arr = nm_l3_config_data_get_wins(l3cd, &u); + g_assert_cmpint(u, ==, 2); + nmtst_assert_ip4_address(ia_arr[0], expected_wins1); + nmtst_assert_ip4_address(ia_arr[1], expected_wins2); } static void test_vendor_option_metered(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; static const Option data[] = {{"vendor_encapsulated_options", "ANDROID_METERED"}, {NULL, NULL}}; - options = fill_table(generic_options, NULL); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); - g_assert(nm_ip4_config_get_metered(ip4_config) == FALSE); - g_hash_table_destroy(options); - g_clear_object(&ip4_config); + options = fill_table(generic_options, NULL); + l3cd = _ip4_config_from_options(1, "eth0", options); + g_assert(nm_l3_config_data_get_metered(l3cd) == NM_TERNARY_DEFAULT); + nm_clear_pointer(&options, g_hash_table_destroy); + nm_clear_l3cd(&l3cd); - options = fill_table(generic_options, NULL); - options = fill_table(data, options); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); - g_assert(nm_ip4_config_get_metered(ip4_config) == TRUE); - g_hash_table_destroy(options); + options = fill_table(generic_options, NULL); + options = fill_table(data, options); + l3cd = _ip4_config_from_options(1, "eth0", options); + g_assert(nm_l3_config_data_get_metered(l3cd) == TRUE); } static void @@ -252,18 +243,18 @@ test_parse_search_list(void) } static void -ip4_test_route(NMIP4Config *ip4_config, - guint route_num, - const char * expected_dest, - const char * expected_gw, - guint expected_prefix) +ip4_test_route(const NML3ConfigData *l3cd, + guint route_num, + const char * expected_dest, + const char * expected_gw, + guint expected_prefix) { const NMPlatformIP4Route *route; guint32 tmp; g_assert(expected_prefix <= 32); - route = _nmtst_ip4_config_get_route(ip4_config, route_num); + route = nmtst_l3_config_data_get_route_at_4(l3cd, route_num); g_assert(inet_pton(AF_INET, expected_dest, &tmp) > 0); g_assert(route->network == tmp); g_assert(inet_pton(AF_INET, expected_gw, &tmp) > 0); @@ -272,110 +263,105 @@ ip4_test_route(NMIP4Config *ip4_config, g_assert_cmpint(route->metric, ==, 0); } -static void -ip4_test_gateway(NMIP4Config *ip4_config, const char *expected_gw) -{ - guint32 tmp; - - g_assert_cmpint(nm_ip4_config_get_num_addresses(ip4_config), ==, 1); - g_assert(inet_pton(AF_INET, expected_gw, &tmp) > 0); - g_assert(nmtst_ip4_config_get_gateway(ip4_config) == tmp); -} +#define ip4_test_gateway(l3cd, expected_gw) \ + G_STMT_START \ + { \ + const NML3ConfigData *_l3cd = (l3cd); \ + \ + g_assert_cmpint(nm_l3_config_data_get_num_addresses(_l3cd, AF_INET), ==, 1); \ + nmtst_assert_ip_address(AF_INET, \ + nmtst_l3_config_data_get_best_gateway(_l3cd, AF_INET), \ + expected_gw); \ + } \ + G_STMT_END static void test_classless_static_routes_1(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const char * expected_route1_dest = "192.168.10.0"; - const char * expected_route1_gw = "192.168.1.1"; - const char * expected_route2_dest = "10.0.0.0"; - const char * expected_route2_gw = "10.17.66.41"; - static const Option data[] = { + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const char * expected_route1_dest = "192.168.10.0"; + const char * expected_route1_gw = "192.168.1.1"; + 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 */ {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 8 10 10 17 66 41"}, {NULL, NULL}}; - options = fill_table(generic_options, NULL); - options = fill_table(data, options); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + options = fill_table(generic_options, NULL); + options = fill_table(data, options); + l3cd = _ip4_config_from_options(1, "eth0", options); /* IP4 routes */ - g_assert_cmpint(nm_ip4_config_get_num_routes(ip4_config), ==, 3); - ip4_test_route(ip4_config, 0, expected_route1_dest, expected_route1_gw, 24); - ip4_test_route(ip4_config, 1, expected_route2_dest, expected_route2_gw, 8); - ip4_test_route(ip4_config, 2, "0.0.0.0", "192.168.1.1", 0); - - g_hash_table_destroy(options); + g_assert_cmpint(nm_l3_config_data_get_num_routes(l3cd, AF_INET), ==, 3); + ip4_test_route(l3cd, 0, expected_route1_dest, expected_route1_gw, 24); + ip4_test_route(l3cd, 1, expected_route2_dest, expected_route2_gw, 8); + ip4_test_route(l3cd, 2, "0.0.0.0", "192.168.1.1", 0); } static void test_classless_static_routes_2(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const char * expected_route1_dest = "192.168.10.0"; - const char * expected_route1_gw = "192.168.1.1"; - const char * expected_route2_dest = "10.0.0.0"; - const char * expected_route2_gw = "10.17.66.41"; - static const Option data[] = { + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const char * expected_route1_dest = "192.168.10.0"; + const char * expected_route1_gw = "192.168.1.1"; + const char * expected_route2_dest = "10.0.0.0"; + const char * expected_route2_gw = "10.17.66.41"; + static const Option data[] = { /* 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}}; - options = fill_table(generic_options, NULL); - options = fill_table(data, options); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + options = fill_table(generic_options, NULL); + options = fill_table(data, options); + l3cd = _ip4_config_from_options(1, "eth0", options); /* IP4 routes */ - g_assert_cmpint(nm_ip4_config_get_num_routes(ip4_config), ==, 3); - ip4_test_route(ip4_config, 0, expected_route1_dest, expected_route1_gw, 24); - ip4_test_route(ip4_config, 1, expected_route2_dest, expected_route2_gw, 8); - ip4_test_route(ip4_config, 2, "0.0.0.0", expected_route1_gw, 0); - - g_hash_table_destroy(options); + g_assert_cmpint(nm_l3_config_data_get_num_routes(l3cd, AF_INET), ==, 3); + ip4_test_route(l3cd, 0, expected_route1_dest, expected_route1_gw, 24); + ip4_test_route(l3cd, 1, expected_route2_dest, expected_route2_gw, 8); + ip4_test_route(l3cd, 2, "0.0.0.0", expected_route1_gw, 0); } static void test_fedora_dhclient_classless_static_routes(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const char * expected_route1_dest = "129.210.177.128"; - const char * expected_route1_gw = "192.168.0.113"; - const char * expected_route2_dest = "2.0.0.0"; - const char * expected_route2_gw = "10.34.255.6"; - const char * expected_gateway = "192.168.0.113"; - static const Option data[] = { + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const char * expected_route1_dest = "129.210.177.128"; + const char * expected_route1_gw = "192.168.0.113"; + const char * expected_route2_dest = "2.0.0.0"; + const char * expected_route2_gw = "10.34.255.6"; + const char * expected_gateway = "192.168.0.113"; + static const Option data[] = { /* 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}}; - options = fill_table(generic_options, NULL); - options = fill_table(data, options); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + options = fill_table(generic_options, NULL); + options = fill_table(data, options); + l3cd = _ip4_config_from_options(1, "eth0", options); /* IP4 routes */ - g_assert_cmpint(nm_ip4_config_get_num_routes(ip4_config), ==, 3); - ip4_test_route(ip4_config, 0, expected_route1_dest, expected_route1_gw, 25); - ip4_test_route(ip4_config, 1, expected_route2_dest, expected_route2_gw, 7); - ip4_test_route(ip4_config, 2, "0.0.0.0", expected_route1_gw, 0); + g_assert_cmpint(nm_l3_config_data_get_num_routes(l3cd, AF_INET), ==, 3); + ip4_test_route(l3cd, 0, expected_route1_dest, expected_route1_gw, 25); + ip4_test_route(l3cd, 1, expected_route2_dest, expected_route2_gw, 7); + ip4_test_route(l3cd, 2, "0.0.0.0", expected_route1_gw, 0); - /* Gateway */ - ip4_test_gateway(ip4_config, expected_gateway); - - g_hash_table_destroy(options); + ip4_test_gateway(l3cd, expected_gateway); } static void test_dhclient_invalid_classless_routes_1(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const char * expected_route1_dest = "192.168.10.0"; - const char * expected_route1_gw = "192.168.1.1"; - static const Option data[] = { + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const char * expected_route1_dest = "192.168.10.0"; + const char * expected_route1_gw = "192.168.1.1"; + static const Option data[] = { /* dhclient format */ {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 45 10 17 66 41"}, {NULL, NULL}}; @@ -384,27 +370,25 @@ test_dhclient_invalid_classless_routes_1(void) options = fill_table(data, options); NMTST_EXPECT_NM_WARN("*ignoring invalid classless static routes*"); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + l3cd = _ip4_config_from_options(1, "eth0", options); g_test_assert_expected_messages(); /* IP4 routes */ - g_assert_cmpint(nm_ip4_config_get_num_routes(ip4_config), ==, 2); - ip4_test_route(ip4_config, 0, expected_route1_dest, expected_route1_gw, 24); - ip4_test_route(ip4_config, 1, "0.0.0.0", expected_route1_gw, 0); - - g_hash_table_destroy(options); + g_assert_cmpint(nm_l3_config_data_get_num_routes(l3cd, AF_INET), ==, 2); + ip4_test_route(l3cd, 0, expected_route1_dest, expected_route1_gw, 24); + ip4_test_route(l3cd, 1, "0.0.0.0", expected_route1_gw, 0); } static void test_dhcpcd_invalid_classless_routes_1(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const char * expected_route1_dest = "10.1.1.5"; - const char * expected_route1_gw = "10.1.1.1"; - const char * expected_route2_dest = "100.99.88.56"; - const char * expected_route2_gw = "10.1.1.1"; - static const Option data[] = { + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const char * expected_route1_dest = "10.1.1.5"; + const char * expected_route1_gw = "10.1.1.1"; + const char * expected_route2_dest = "100.99.88.56"; + const char * expected_route2_gw = "10.1.1.1"; + static const Option data[] = { /* dhcpcd format */ {"classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.adfadf/44 10.17.66.41"}, {NULL, NULL}}; @@ -413,30 +397,28 @@ test_dhcpcd_invalid_classless_routes_1(void) options = fill_table(data, options); NMTST_EXPECT_NM_WARN("*ignoring invalid classless static routes*"); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + l3cd = _ip4_config_from_options(1, "eth0", options); g_test_assert_expected_messages(); /* Test falling back to old-style static routes if the classless static * routes are invalid. */ - g_assert_cmpint(nm_ip4_config_get_num_routes(ip4_config), ==, 3); - ip4_test_route(ip4_config, 0, expected_route1_dest, expected_route1_gw, 32); - ip4_test_route(ip4_config, 1, expected_route2_dest, expected_route2_gw, 32); - ip4_test_route(ip4_config, 2, "0.0.0.0", "192.168.1.1", 0); - - g_hash_table_destroy(options); + g_assert_cmpint(nm_l3_config_data_get_num_routes(l3cd, AF_INET), ==, 3); + ip4_test_route(l3cd, 0, expected_route1_dest, expected_route1_gw, 32); + ip4_test_route(l3cd, 1, expected_route2_dest, expected_route2_gw, 32); + ip4_test_route(l3cd, 2, "0.0.0.0", "192.168.1.1", 0); } static void test_dhclient_invalid_classless_routes_2(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const char * expected_route1_dest = "10.1.1.5"; - const char * expected_route1_gw = "10.1.1.1"; - const char * expected_route2_dest = "100.99.88.56"; - const char * expected_route2_gw = "10.1.1.1"; - static const Option data[] = { + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const char * expected_route1_dest = "10.1.1.5"; + const char * expected_route1_gw = "10.1.1.1"; + 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}}; @@ -444,30 +426,28 @@ test_dhclient_invalid_classless_routes_2(void) options = fill_table(data, options); NMTST_EXPECT_NM_WARN("*ignoring invalid classless static routes*"); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + l3cd = _ip4_config_from_options(1, "eth0", options); g_test_assert_expected_messages(); /* Test falling back to old-style static routes if the classless static * routes are invalid. */ - g_assert_cmpint(nm_ip4_config_get_num_routes(ip4_config), ==, 3); - ip4_test_route(ip4_config, 0, expected_route1_dest, expected_route1_gw, 32); - ip4_test_route(ip4_config, 1, expected_route2_dest, expected_route2_gw, 32); - ip4_test_route(ip4_config, 2, "0.0.0.0", "192.168.1.1", 0); - - g_hash_table_destroy(options); + g_assert_cmpint(nm_l3_config_data_get_num_routes(l3cd, AF_INET), ==, 3); + ip4_test_route(l3cd, 0, expected_route1_dest, expected_route1_gw, 32); + ip4_test_route(l3cd, 1, expected_route2_dest, expected_route2_gw, 32); + ip4_test_route(l3cd, 2, "0.0.0.0", "192.168.1.1", 0); } static void test_dhcpcd_invalid_classless_routes_2(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const char * expected_route1_dest = "10.1.1.5"; - const char * expected_route1_gw = "10.1.1.1"; - const char * expected_route2_dest = "100.99.88.56"; - const char * expected_route2_gw = "10.1.1.1"; - static const Option data[] = { + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const char * expected_route1_dest = "10.1.1.5"; + const char * expected_route1_gw = "10.1.1.1"; + 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}}; @@ -475,7 +455,7 @@ test_dhcpcd_invalid_classless_routes_2(void) options = fill_table(data, options); NMTST_EXPECT_NM_WARN("*ignoring invalid classless static routes*"); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + l3cd = _ip4_config_from_options(1, "eth0", options); g_test_assert_expected_messages(); /* Test falling back to old-style static routes if the classless static @@ -483,22 +463,20 @@ test_dhcpcd_invalid_classless_routes_2(void) */ /* Routes */ - g_assert_cmpint(nm_ip4_config_get_num_routes(ip4_config), ==, 3); - ip4_test_route(ip4_config, 0, expected_route1_dest, expected_route1_gw, 32); - ip4_test_route(ip4_config, 1, expected_route2_dest, expected_route2_gw, 32); - ip4_test_route(ip4_config, 2, "0.0.0.0", "192.168.1.1", 0); - - g_hash_table_destroy(options); + g_assert_cmpint(nm_l3_config_data_get_num_routes(l3cd, AF_INET), ==, 3); + ip4_test_route(l3cd, 0, expected_route1_dest, expected_route1_gw, 32); + ip4_test_route(l3cd, 1, expected_route2_dest, expected_route2_gw, 32); + ip4_test_route(l3cd, 2, "0.0.0.0", "192.168.1.1", 0); } static void test_dhclient_invalid_classless_routes_3(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const char * expected_route1_dest = "192.168.10.0"; - const char * expected_route1_gw = "192.168.1.1"; - static const Option data[] = { + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + 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}}; @@ -506,25 +484,23 @@ test_dhclient_invalid_classless_routes_3(void) options = fill_table(data, options); NMTST_EXPECT_NM_WARN("*ignoring invalid classless static routes*"); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + l3cd = _ip4_config_from_options(1, "eth0", options); g_test_assert_expected_messages(); /* IP4 routes */ - g_assert_cmpint(nm_ip4_config_get_num_routes(ip4_config), ==, 2); - ip4_test_route(ip4_config, 0, expected_route1_dest, expected_route1_gw, 24); - ip4_test_route(ip4_config, 1, "0.0.0.0", expected_route1_gw, 0); - - g_hash_table_destroy(options); + g_assert_cmpint(nm_l3_config_data_get_num_routes(l3cd, AF_INET), ==, 2); + ip4_test_route(l3cd, 0, expected_route1_dest, expected_route1_gw, 24); + ip4_test_route(l3cd, 1, "0.0.0.0", expected_route1_gw, 0); } static void test_dhcpcd_invalid_classless_routes_3(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const char * expected_route1_dest = "192.168.10.0"; - const char * expected_route1_gw = "192.168.1.1"; - static Option data[] = { + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + 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}}; @@ -532,133 +508,124 @@ test_dhcpcd_invalid_classless_routes_3(void) options = fill_table(data, options); NMTST_EXPECT_NM_WARN("*DHCP provided invalid classless static route*"); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + l3cd = _ip4_config_from_options(1, "eth0", options); g_test_assert_expected_messages(); /* IP4 routes */ - g_assert_cmpint(nm_ip4_config_get_num_routes(ip4_config), ==, 2); - ip4_test_route(ip4_config, 0, expected_route1_dest, expected_route1_gw, 24); - ip4_test_route(ip4_config, 1, "0.0.0.0", expected_route1_gw, 0); - - g_hash_table_destroy(options); + g_assert_cmpint(nm_l3_config_data_get_num_routes(l3cd, AF_INET), ==, 2); + ip4_test_route(l3cd, 0, expected_route1_dest, expected_route1_gw, 24); + ip4_test_route(l3cd, 1, "0.0.0.0", expected_route1_gw, 0); } static void test_dhclient_gw_in_classless_routes(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const char * expected_route1_dest = "192.168.10.0"; - const char * expected_route1_gw = "192.168.1.1"; - const char * expected_gateway = "192.2.3.4"; - static Option data[] = { + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const char * expected_route1_dest = "192.168.10.0"; + 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}}; - options = fill_table(generic_options, NULL); - options = fill_table(data, options); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + options = fill_table(generic_options, NULL); + options = fill_table(data, options); + l3cd = _ip4_config_from_options(1, "eth0", options); /* IP4 routes */ - g_assert_cmpint(nm_ip4_config_get_num_routes(ip4_config), ==, 2); - ip4_test_route(ip4_config, 0, expected_route1_dest, expected_route1_gw, 24); - ip4_test_route(ip4_config, 1, "0.0.0.0", "192.2.3.4", 0); + g_assert_cmpint(nm_l3_config_data_get_num_routes(l3cd, AF_INET), ==, 2); + ip4_test_route(l3cd, 0, expected_route1_dest, expected_route1_gw, 24); + ip4_test_route(l3cd, 1, "0.0.0.0", "192.2.3.4", 0); - /* Gateway */ - ip4_test_gateway(ip4_config, expected_gateway); - - g_hash_table_destroy(options); + ip4_test_gateway(l3cd, expected_gateway); } static void test_dhcpcd_gw_in_classless_routes(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const char * expected_route1_dest = "192.168.10.0"; - const char * expected_route1_gw = "192.168.1.1"; - const char * expected_gateway = "192.2.3.4"; - static Option data[] = { + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const char * expected_route1_dest = "192.168.10.0"; + 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}}; - options = fill_table(generic_options, NULL); - options = fill_table(data, options); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + options = fill_table(generic_options, NULL); + options = fill_table(data, options); + l3cd = _ip4_config_from_options(1, "eth0", options); /* IP4 routes */ - g_assert_cmpint(nm_ip4_config_get_num_routes(ip4_config), ==, 2); - ip4_test_route(ip4_config, 0, expected_route1_dest, expected_route1_gw, 24); - ip4_test_route(ip4_config, 1, "0.0.0.0", "192.2.3.4", 0); + g_assert_cmpint(nm_l3_config_data_get_num_routes(l3cd, AF_INET), ==, 2); + ip4_test_route(l3cd, 0, expected_route1_dest, expected_route1_gw, 24); + ip4_test_route(l3cd, 1, "0.0.0.0", "192.2.3.4", 0); - /* Gateway */ - ip4_test_gateway(ip4_config, expected_gateway); - - g_hash_table_destroy(options); + ip4_test_gateway(l3cd, expected_gateway); } static void test_escaped_domain_searches(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const char * expected_search0 = "host1"; - const char * expected_search1 = "host2"; - const char * expected_search2 = "host3"; + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const char * expected_search0 = "host1"; + const char * expected_search1 = "host2"; + const char * expected_search2 = "host3"; static const Option data[] = {{"domain_search", "host1\\032host2\\032host3"}, {NULL, NULL}}; + const char *const * strarr; + guint u; - options = fill_table(generic_options, NULL); - options = fill_table(data, options); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + options = fill_table(generic_options, NULL); + options = fill_table(data, options); + l3cd = _ip4_config_from_options(1, "eth0", options); - /* domain searches */ - g_assert_cmpint(nm_ip4_config_get_num_searches(ip4_config), ==, 3); - g_assert_cmpstr(nm_ip4_config_get_search(ip4_config, 0), ==, expected_search0); - g_assert_cmpstr(nm_ip4_config_get_search(ip4_config, 1), ==, expected_search1); - g_assert_cmpstr(nm_ip4_config_get_search(ip4_config, 2), ==, expected_search2); - - g_hash_table_destroy(options); + strarr = nm_l3_config_data_get_searches(l3cd, AF_INET, &u); + g_assert_cmpint(u, ==, 3); + g_assert_cmpstr(strarr[0], ==, expected_search0); + g_assert_cmpstr(strarr[1], ==, expected_search1); + g_assert_cmpstr(strarr[2], ==, expected_search2); } static void test_invalid_escaped_domain_searches(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; static const Option data[] = {{"domain_search", "host1\\aahost2\\032host3"}, {NULL, NULL}}; + const char *const * strarr; + guint u; options = fill_table(generic_options, NULL); options = fill_table(data, options); NMTST_EXPECT_NM_WARN("*invalid domain search*"); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + l3cd = _ip4_config_from_options(1, "eth0", options); g_test_assert_expected_messages(); - /* domain searches */ - g_assert_cmpint(nm_ip4_config_get_num_searches(ip4_config), ==, 0); - - g_hash_table_destroy(options); + strarr = nm_l3_config_data_get_searches(l3cd, AF_INET, &u); + g_assert_cmpint(u, ==, 0); + g_assert(!strarr); } static void test_ip4_missing_prefix(const char *ip, guint32 expected_prefix) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const NMPlatformIP4Address * address; + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const NMPlatformIP4Address * address; options = fill_table(generic_options, NULL); g_hash_table_insert(options, "ip_address", (gpointer) ip); g_hash_table_remove(options, "subnet_mask"); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + l3cd = _ip4_config_from_options(1, "eth0", options); - g_assert_cmpint(nm_ip4_config_get_num_addresses(ip4_config), ==, 1); - address = _nmtst_ip4_config_get_address(ip4_config, 0); + g_assert_cmpint(nm_l3_config_data_get_num_addresses(l3cd, AF_INET), ==, 1); + address = nmtst_l3_config_data_get_address_at_4(l3cd, 0); g_assert(address); g_assert_cmpint(address->plen, ==, expected_prefix); - - g_hash_table_destroy(options); } static void @@ -682,9 +649,9 @@ test_ip4_missing_prefix_8(void) static void test_ip4_prefix_classless(void) { - GHashTable * options; - gs_unref_object NMIP4Config *ip4_config = NULL; - const NMPlatformIP4Address * address; + gs_unref_hashtable GHashTable *options = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const NMPlatformIP4Address * address; /* Ensure that the missing-subnet-mask handler doesn't mangle classless * subnet masks at all. The handler should trigger only if the server @@ -695,14 +662,12 @@ test_ip4_prefix_classless(void) g_hash_table_insert(options, "ip_address", "172.16.54.22"); g_hash_table_insert(options, "subnet_mask", "255.255.252.0"); - ip4_config = _ip4_config_from_options(1, "eth0", options, 0); + l3cd = _ip4_config_from_options(1, "eth0", options); - g_assert_cmpint(nm_ip4_config_get_num_addresses(ip4_config), ==, 1); - address = _nmtst_ip4_config_get_address(ip4_config, 0); + g_assert_cmpint(nm_l3_config_data_get_num_addresses(l3cd, AF_INET), ==, 1); + address = nmtst_l3_config_data_get_address_at_4(l3cd, 0); g_assert(address); g_assert_cmpint(address->plen, ==, 22); - - g_hash_table_destroy(options); } #define COMPARE_ID(src, is_str, expected, expected_len) \ diff --git a/src/core/dns/nm-dns-dnsmasq.c b/src/core/dns/nm-dns-dnsmasq.c index 126e0a217a..b40521a32b 100644 --- a/src/core/dns/nm-dns-dnsmasq.c +++ b/src/core/dns/nm-dns-dnsmasq.c @@ -19,10 +19,9 @@ #include "libnm-core-intern/nm-core-internal.h" #include "libnm-platform/nm-platform.h" #include "nm-utils.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" #include "nm-dbus-manager.h" #include "NetworkManagerUtils.h" +#include "nm-l3-config-data.h" #define PIDFILE NMRUNDIR "/dnsmasq.pid" #define CONFDIR NMCONFDIR "/dnsmasq.d" @@ -806,20 +805,22 @@ add_global_config(NMDnsDnsmasq * self, static void add_ip_config(NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsConfigIPData *ip_data) { - NMIPConfig * ip_config = ip_data->ip_config; - gconstpointer addr; - const char * iface, *domain; + const char * iface; + const char * domain; char ip_addr_to_string_buf[IP_ADDR_TO_STRING_BUFLEN]; - int addr_family; - guint i, j, num; + gconstpointer nameservers; + guint num; + guint i; + guint j; - iface = nm_platform_link_get_name(NM_PLATFORM_GET, ip_data->data->ifindex); - addr_family = nm_ip_config_get_addr_family(ip_config); + iface = nm_platform_link_get_name(NM_PLATFORM_GET, ip_data->data->ifindex); - num = nm_ip_config_get_num_nameservers(ip_config); + nameservers = nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &num); for (i = 0; i < num; i++) { - addr = nm_ip_config_get_nameserver(ip_config, i); - ip_addr_to_string(addr_family, addr, iface, ip_addr_to_string_buf); + gconstpointer addr; + + 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 (!ip_data->domains.has_default_route_explicit && ip_data->domains.has_default_route) add_dnsmasq_nameserver(self, servers, ip_addr_to_string_buf, NULL); @@ -846,7 +847,7 @@ add_ip_config(NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsConfigIPD static GVariant * create_update_args(NMDnsDnsmasq * self, const NMGlobalDnsConfig *global_config, - const CList * ip_config_lst_head, + const CList * ip_data_lst_head, const char * hostname) { GVariantBuilder servers; @@ -857,7 +858,7 @@ create_update_args(NMDnsDnsmasq * self, if (global_config) add_global_config(self, &servers, global_config); else { - c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) + c_list_for_each_entry (ip_data, ip_data_lst_head, ip_data_lst) add_ip_config(self, &servers, ip_data); } @@ -1122,7 +1123,7 @@ start_dnsmasq(NMDnsDnsmasq *self, gboolean force_start, GError **error) static gboolean update(NMDnsPlugin * plugin, const NMGlobalDnsConfig *global_config, - const CList * ip_config_lst_head, + const CList * ip_data_lst_head, const char * hostname, GError ** error) { @@ -1134,7 +1135,7 @@ update(NMDnsPlugin * plugin, nm_clear_pointer(&priv->set_server_ex_args, g_variant_unref); priv->set_server_ex_args = - g_variant_ref_sink(create_update_args(self, global_config, ip_config_lst_head, hostname)); + g_variant_ref_sink(create_update_args(self, global_config, ip_data_lst_head, hostname)); send_dnsmasq_update(self); return TRUE; diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c index f0ec0c8921..1ca33e46ac 100644 --- a/src/core/dns/nm-dns-manager.c +++ b/src/core/dns/nm-dns-manager.c @@ -7,6 +7,8 @@ #include "src/core/nm-default-daemon.h" +#include "nm-dns-manager.h" + #include #include #include @@ -22,22 +24,20 @@ #include #endif -#include "libnm-glib-aux/nm-str-buf.h" -#include "nm-utils.h" #include "libnm-core-intern/nm-core-internal.h" -#include "nm-dns-manager.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" +#include "libnm-glib-aux/nm-str-buf.h" + #include "NetworkManagerUtils.h" +#include "devices/nm-device.h" #include "nm-config.h" #include "nm-dbus-object.h" -#include "devices/nm-device.h" -#include "nm-manager.h" - -#include "nm-dns-plugin.h" #include "nm-dns-dnsmasq.h" +#include "nm-dns-plugin.h" #include "nm-dns-systemd-resolved.h" #include "nm-dns-unbound.h" +#include "nm-l3-config-data.h" +#include "nm-manager.h" +#include "nm-utils.h" #define HASH_LEN NM_UTILS_CHECKSUM_LENGTH_SHA1 @@ -84,22 +84,21 @@ typedef struct { GHashTable *configs_dict; CList configs_lst_head; - CList ip_config_lst_head; + CList ip_data_lst_head; GVariant *config_variant; - bool ip_config_lst_need_sort : 1; + bool ip_data_lst_need_sort : 1; bool configs_lst_need_sort : 1; bool dns_touched : 1; bool is_stopped : 1; + bool config_changed : 1; + char *hostname; guint updates_queue; - guint8 hash[HASH_LEN]; /* SHA1 hash of current DNS config */ - guint8 prev_hash[HASH_LEN]; /* Hash when begin_updates() was called */ - NMDnsManagerResolvConfManager rc_manager; char * mode; NMDnsPlugin * sd_resolve_plugin; @@ -159,11 +158,6 @@ NM_DEFINE_SINGLETON_GETTER(NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER /*****************************************************************************/ -static void -_ip_config_dns_priority_changed(gpointer config, GParamSpec *pspec, NMDnsConfigIPData *ip_data); - -/*****************************************************************************/ - static gboolean domain_is_valid(const char *domain, gboolean check_public_suffix) { @@ -208,6 +202,24 @@ static NM_UTILS_LOOKUP_STR_DEFINE( /*****************************************************************************/ +static int +_dns_config_ip_data_get_dns_priority1(const NML3ConfigData *l3cd, int addr_family) +{ + int prio; + + if (!nm_l3_config_data_get_dns_priority(l3cd, addr_family, &prio)) + return 0; + + nm_assert(prio != 0); + return prio; +} + +static int +_dns_config_ip_data_get_dns_priority(const NMDnsConfigIPData *ip_data) +{ + return _dns_config_ip_data_get_dns_priority1(ip_data->l3cd, ip_data->addr_family); +} + static void _ASSERT_dns_config_data(const NMDnsConfigData *data) { @@ -221,9 +233,9 @@ _ASSERT_dns_config_ip_data(const NMDnsConfigIPData *ip_data) { nm_assert(ip_data); _ASSERT_dns_config_data(ip_data->data); - nm_assert(NM_IS_IP_CONFIG(ip_data->ip_config)); + nm_assert(NM_IS_L3_CONFIG_DATA(ip_data->l3cd)); nm_assert(c_list_contains(&ip_data->data->data_lst_head, &ip_data->data_lst)); - nm_assert(ip_data->data->ifindex == nm_ip_config_get_ifindex(ip_data->ip_config)); + nm_assert(ip_data->data->ifindex == nm_l3_config_data_get_ifindex(ip_data->l3cd)); #if NM_MORE_ASSERTS > 5 { gboolean has_default = FALSE; @@ -243,37 +255,36 @@ _ASSERT_dns_config_ip_data(const NMDnsConfigIPData *ip_data) if (ip_data->domains.has_default_route_exclusive) nm_assert(ip_data->domains.has_default_route); } + nm_assert(_dns_config_ip_data_get_dns_priority(ip_data) != 0); #endif } static NMDnsConfigIPData * -_dns_config_ip_data_new(NMDnsConfigData * data, - NMIPConfig * ip_config, - NMDnsIPConfigType ip_config_type) +_dns_config_ip_data_new(NMDnsConfigData * data, + int addr_family, + gconstpointer source_tag, + const NML3ConfigData *l3cd, + NMDnsIPConfigType ip_config_type) { NMDnsConfigIPData *ip_data; _ASSERT_dns_config_data(data); - nm_assert(NM_IS_IP_CONFIG(ip_config)); + nm_assert(NM_IS_L3_CONFIG_DATA(l3cd)); nm_assert(ip_config_type != NM_DNS_IP_CONFIG_TYPE_REMOVED); ip_data = g_slice_new(NMDnsConfigIPData); *ip_data = (NMDnsConfigIPData){ .data = data, - .ip_config = g_object_ref(ip_config), + .source_tag = source_tag, + .l3cd = nm_l3_config_data_ref_and_seal(l3cd), .ip_config_type = ip_config_type, + .addr_family = addr_family, }; c_list_link_tail(&data->data_lst_head, &ip_data->data_lst); - c_list_link_tail(&NM_DNS_MANAGER_GET_PRIVATE(data->self)->ip_config_lst_head, - &ip_data->ip_config_lst); + c_list_link_tail(&NM_DNS_MANAGER_GET_PRIVATE(data->self)->ip_data_lst_head, + &ip_data->ip_data_lst); - /* We also need to set priv->ip_config_lst_need_sort, but the caller will do that! */ - - g_signal_connect(ip_config, - NM_IS_IP4_CONFIG(ip_config) ? "notify::" NM_IP4_CONFIG_DNS_PRIORITY - : "notify::" NM_IP6_CONFIG_DNS_PRIORITY, - G_CALLBACK(_ip_config_dns_priority_changed), - ip_data); + /* We also need to set priv->ip_data_lst_need_sort, but the caller will do that! */ _ASSERT_dns_config_ip_data(ip_data); return ip_data; @@ -285,35 +296,15 @@ _dns_config_ip_data_free(NMDnsConfigIPData *ip_data) _ASSERT_dns_config_ip_data(ip_data); c_list_unlink_stale(&ip_data->data_lst); - c_list_unlink_stale(&ip_data->ip_config_lst); + c_list_unlink_stale(&ip_data->ip_data_lst); g_free(ip_data->domains.search); g_strfreev(ip_data->domains.reverse); - g_signal_handlers_disconnect_by_func(ip_data->ip_config, - _ip_config_dns_priority_changed, - ip_data); - - g_object_unref(ip_data->ip_config); + nm_l3_config_data_unref(ip_data->l3cd); nm_g_slice_free(ip_data); } -static NMDnsConfigIPData * -_dns_config_data_find_ip_config(NMDnsConfigData *data, NMIPConfig *ip_config) -{ - NMDnsConfigIPData *ip_data; - - _ASSERT_dns_config_data(data); - - c_list_for_each_entry (ip_data, &data->data_lst_head, data_lst) { - _ASSERT_dns_config_ip_data(ip_data); - - if (ip_data->ip_config == ip_config) - return ip_data; - } - return NULL; -} - static void _dns_config_data_free(NMDnsConfigData *data) { @@ -325,14 +316,13 @@ _dns_config_data_free(NMDnsConfigData *data) } static int -_mgr_get_ip_config_lst_cmp(const CList *a_lst, const CList *b_lst, const void *user_data) +_mgr_get_ip_data_lst_cmp(const CList *a_lst, const CList *b_lst, const void *user_data) { - const NMDnsConfigIPData *a = c_list_entry(a_lst, NMDnsConfigIPData, ip_config_lst); - const NMDnsConfigIPData *b = c_list_entry(b_lst, NMDnsConfigIPData, ip_config_lst); + const NMDnsConfigIPData *a = c_list_entry(a_lst, NMDnsConfigIPData, ip_data_lst); + const NMDnsConfigIPData *b = c_list_entry(b_lst, NMDnsConfigIPData, ip_data_lst); /* Configurations with lower priority value first */ - NM_CMP_DIRECT(nm_ip_config_get_dns_priority(a->ip_config), - nm_ip_config_get_dns_priority(b->ip_config)); + NM_CMP_DIRECT(_dns_config_ip_data_get_dns_priority(a), _dns_config_ip_data_get_dns_priority(b)); /* Sort according to type (descendingly) */ NM_CMP_FIELD(b, a, ip_config_type); @@ -341,16 +331,16 @@ _mgr_get_ip_config_lst_cmp(const CList *a_lst, const CList *b_lst, const void *u } static CList * -_mgr_get_ip_config_lst_head(NMDnsManager *self) +_mgr_get_ip_data_lst_head(NMDnsManager *self) { NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); - if (G_UNLIKELY(priv->ip_config_lst_need_sort)) { - priv->ip_config_lst_need_sort = FALSE; - c_list_sort(&priv->ip_config_lst_head, _mgr_get_ip_config_lst_cmp, NULL); + if (G_UNLIKELY(priv->ip_data_lst_need_sort)) { + priv->ip_data_lst_need_sort = FALSE; + c_list_sort(&priv->ip_data_lst_head, _mgr_get_ip_data_lst_cmp, NULL); } - return &priv->ip_config_lst_head; + return &priv->ip_data_lst_head; } static int @@ -430,28 +420,33 @@ add_dns_option_item(GPtrArray *array, const char *str) } static void -add_dns_domains(GPtrArray * array, - const NMIPConfig *ip_config, - gboolean include_routing, - gboolean dup) +add_dns_domains(GPtrArray * array, + int addr_family, + const NML3ConfigData *l3cd, + gboolean include_routing, + gboolean dup) { - guint num_domains, num_searches, i; - const char *str; + const char *const *domains; + const char *const *searches; + guint num_domains; + guint num_searches; + guint i; + const char * str; - num_domains = nm_ip_config_get_num_domains(ip_config); - num_searches = nm_ip_config_get_num_searches(ip_config); + domains = nm_l3_config_data_get_domains(l3cd, addr_family, &num_domains); + searches = nm_l3_config_data_get_searches(l3cd, addr_family, &num_searches); for (i = 0; i < num_searches; i++) { - str = nm_ip_config_get_search(ip_config, i); + str = searches[i]; if (!include_routing && domain_is_routing(str)) continue; if (!domain_is_valid(nm_utils_parse_dns_domain(str, NULL), FALSE)) continue; add_string_item(array, str, dup); } - if (num_domains > 1 || !num_searches) { + if (num_domains > 1 || num_searches == 0) { for (i = 0; i < num_domains; i++) { - str = nm_ip_config_get_domain(ip_config, i); + str = domains[i]; if (!include_routing && domain_is_routing(str)) continue; if (!domain_is_valid(nm_utils_parse_dns_domain(str, NULL), FALSE)) @@ -462,26 +457,24 @@ add_dns_domains(GPtrArray * array, } static void -merge_one_ip_config(NMResolvConfData *rc, int ifindex, const NMIPConfig *ip_config) +merge_one_l3cd(NMResolvConfData *rc, int addr_family, int ifindex, const NML3ConfigData *l3cd) { - int addr_family; - char buf[NM_UTILS_INET_ADDRSTRLEN + 50]; - gboolean has_trust_ad; - guint num_nameservers; - guint num; - guint i; + char buf[NM_UTILS_INET_ADDRSTRLEN + 50]; + gboolean has_trust_ad; + guint num_nameservers; + guint num; + guint i; + gconstpointer nameservers; + const char *const *strv; - addr_family = nm_ip_config_get_addr_family(ip_config); + nm_assert(ifindex == nm_l3_config_data_get_ifindex(l3cd)); - nm_assert_addr_family(addr_family); - nm_assert(ifindex > 0); - nm_assert(ifindex == nm_ip_config_get_ifindex(ip_config)); - - num_nameservers = nm_ip_config_get_num_nameservers(ip_config); + nameservers = nm_l3_config_data_get_nameservers(l3cd, addr_family, &num_nameservers); for (i = 0; i < num_nameservers; i++) { const NMIPAddr *addr; - addr = nm_ip_config_get_nameserver(ip_config, i); + addr = nm_ip_addr_from_packed_array(addr_family, nameservers, i); + if (addr_family == AF_INET) nm_utils_inet_ntop(addr_family, addr, buf); else if (IN6_IS_ADDR_V4MAPPED(addr)) @@ -502,22 +495,23 @@ merge_one_ip_config(NMResolvConfData *rc, int ifindex, const NMIPConfig *ip_conf add_string_item(rc->nameservers, buf, TRUE); } - add_dns_domains(rc->searches, ip_config, FALSE, TRUE); + add_dns_domains(rc->searches, addr_family, l3cd, FALSE, TRUE); has_trust_ad = FALSE; - num = nm_ip_config_get_num_dns_options(ip_config); + strv = nm_l3_config_data_get_dns_options(l3cd, addr_family, &num); for (i = 0; i < num; i++) { - const char *option = nm_ip_config_get_dns_option(ip_config, i); + const char *option = strv[i]; if (nm_streq(option, NM_SETTING_DNS_OPTION_TRUST_AD)) { has_trust_ad = TRUE; continue; } - add_dns_option_item(rc->options, nm_ip_config_get_dns_option(ip_config, i)); + add_dns_option_item(rc->options, option); } + if (num_nameservers == 0) { - /* If the @ip_config contributes no DNS servers, ignore whether trust-ad is set or unset - * for this @ip_config. */ + /* If the @l3cd contributes no DNS servers, ignore whether trust-ad is set or unset + * for this @l3cd. */ } else if (has_trust_ad) { /* We only set has_trust_ad to TRUE, if all IP configs agree (or don't contribute). * Once set to FALSE, it doesn't get reset. */ @@ -527,20 +521,17 @@ merge_one_ip_config(NMResolvConfData *rc, int ifindex, const NMIPConfig *ip_conf rc->has_trust_ad = NM_TERNARY_FALSE; if (addr_family == AF_INET) { - const NMIP4Config *ip4_config = (const NMIP4Config *) ip_config; + const in_addr_t *nis_servers; + const char * nis_domain; - /* NIS stuff */ - num = nm_ip4_config_get_num_nis_servers(ip4_config); - for (i = 0; i < num; i++) { - add_string_item(rc->nis_servers, - _nm_utils_inet4_ntop(nm_ip4_config_get_nis_server(ip4_config, i), buf), - TRUE); - } + 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); - if (nm_ip4_config_get_nis_domain(ip4_config)) { + if ((nis_domain = nm_l3_config_data_get_nis_domain(l3cd))) { /* FIXME: handle multiple domains */ if (!rc->nis_domain) - rc->nis_domain = nm_ip4_config_get_nis_domain(ip4_config); + rc->nis_domain = nis_domain; } } } @@ -1106,30 +1097,6 @@ update_resolv_conf(NMDnsManager * self, return write_file_result; } -static void -compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[static HASH_LEN]) -{ - nm_auto_free_checksum GChecksum *sum = NULL; - NMDnsConfigIPData * ip_data; - - sum = g_checksum_new(G_CHECKSUM_SHA1); - nm_assert(HASH_LEN == g_checksum_type_get_length(G_CHECKSUM_SHA1)); - - if (global) - nm_global_dns_config_update_checksum(global, sum); - else { - 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_config_lst_head(self); - c_list_for_each_entry (ip_data, head, ip_config_lst) - nm_ip_config_hash(ip_data->ip_config, sum, TRUE); - } - - nm_utils_checksum_get_digest_len(sum, buffer, HASH_LEN); -} - static gboolean merge_global_dns_config(NMResolvConfData *rc, NMGlobalDnsConfig *global_conf) { @@ -1172,19 +1139,21 @@ merge_global_dns_config(NMResolvConfData *rc, NMGlobalDnsConfig *global_conf) } static const char * -get_nameserver_list(const NMIPConfig *config, NMStrBuf *tmp_strbuf) +get_nameserver_list(int addr_family, const NML3ConfigData *l3cd, NMStrBuf *tmp_strbuf) { - char buf[NM_UTILS_INET_ADDRSTRLEN]; - int addr_family; - guint num; - guint i; + char buf[NM_UTILS_INET_ADDRSTRLEN]; + guint num; + guint i; + gconstpointer nameservers; nm_str_buf_reset(tmp_strbuf); - addr_family = nm_ip_config_get_addr_family(config); - num = nm_ip_config_get_num_nameservers(config); + nameservers = nm_l3_config_data_get_nameservers(l3cd, addr_family, &num); for (i = 0; i < num; i++) { - nm_utils_inet_ntop(addr_family, nm_ip_config_get_nameserver(config, i), buf); + const NMIPAddr *addr; + + addr = nm_ip_addr_from_packed_array(addr_family, nameservers, i); + nm_utils_inet_ntop(addr_family, addr->addr_ptr, buf); if (i > 0) nm_str_buf_append_c(tmp_strbuf, ' '); nm_str_buf_append(tmp_strbuf, buf); @@ -1227,36 +1196,39 @@ _collect_resolv_conf_data(NMDnsManager * self, merge_global_dns_config(&rc, global_config); else { nm_auto_str_buf NMStrBuf tmp_strbuf = NM_STR_BUF_INIT(0, FALSE); - int prio; int first_prio = 0; const NMDnsConfigIPData *ip_data; const CList * head; gboolean is_first = TRUE; - head = _mgr_get_ip_config_lst_head(self); - c_list_for_each_entry (ip_data, head, ip_config_lst) { + head = _mgr_get_ip_data_lst_head(self); + c_list_for_each_entry (ip_data, head, ip_data_lst) { gboolean skip = FALSE; + int dns_priority; _ASSERT_dns_config_ip_data(ip_data); - prio = nm_ip_config_get_dns_priority(ip_data->ip_config); + if (!nm_l3_config_data_get_dns_priority(ip_data->l3cd, + ip_data->addr_family, + &dns_priority)) + nm_assert_not_reached(); if (is_first) { is_first = FALSE; - first_prio = prio; - } else if (first_prio < 0 && first_prio != prio) + first_prio = dns_priority; + } else if (first_prio < 0 && first_prio != dns_priority) skip = TRUE; _LOGT("config: %8d %-7s v%c %-5d %s: %s", - prio, + dns_priority, _config_type_to_string(ip_data->ip_config_type), - nm_utils_addr_family_to_char(nm_ip_config_get_addr_family(ip_data->ip_config)), + nm_utils_addr_family_to_char(ip_data->addr_family), ip_data->data->ifindex, skip ? "" : "", - get_nameserver_list(ip_data->ip_config, &tmp_strbuf)); + get_nameserver_list(ip_data->addr_family, ip_data->l3cd, &tmp_strbuf)); if (!skip) - merge_one_ip_config(&rc, ip_data->data->ifindex, ip_data->ip_config); + merge_one_l3cd(&rc, ip_data->addr_family, ip_data->data->ifindex, ip_data->l3cd); } } @@ -1292,27 +1264,32 @@ _collect_resolv_conf_data(NMDnsManager * self, /*****************************************************************************/ static char ** -get_ip_rdns_domains(NMIPConfig *ip_config) +get_ip_rdns_domains(int addr_family, const NML3ConfigData *l3cd) { - int addr_family = nm_ip_config_get_addr_family(ip_config); - char ** strv; - GPtrArray * domains; - NMDedupMultiIter ipconf_iter; - const NMPlatformIPAddress *address; - const NMPlatformIPRoute * route; - - nm_assert_addr_family(addr_family); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + char ** strv; + GPtrArray * domains; + NMDedupMultiIter ipconf_iter; + const NMPObject *obj; domains = g_ptr_array_sized_new(5); - nm_ip_config_iter_ip_address_for_each (&ipconf_iter, ip_config, &address) { + nm_l3_config_data_iter_obj_for_each (&ipconf_iter, + l3cd, + &obj, + NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)) { nm_utils_get_reverse_dns_domains_ip(addr_family, - address->address_ptr, - address->plen, + NMP_OBJECT_CAST_IP_ADDRESS(obj)->address_ptr, + NMP_OBJECT_CAST_IP_ADDRESS(obj)->plen, domains); } - nm_ip_config_iter_ip_route_for_each (&ipconf_iter, ip_config, &route) { + nm_l3_config_data_iter_obj_for_each (&ipconf_iter, + l3cd, + &obj, + NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)) { + const NMPlatformIPRoute *route = NMP_OBJECT_CAST_IP_ROUTE(obj); + if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route)) { nm_utils_get_reverse_dns_domains_ip(addr_family, route->network_ptr, @@ -1394,12 +1371,12 @@ _mgr_configs_data_construct(NMDnsManager *self) CList * head; int prev_priority = G_MININT; - head = _mgr_get_ip_config_lst_head(self); + head = _mgr_get_ip_data_lst_head(self); #if NM_MORE_ASSERTS /* we call _mgr_configs_data_clear() at the end of update. We * don't expect any domain settings here. */ - c_list_for_each_entry (ip_data, head, ip_config_lst) { + c_list_for_each_entry (ip_data, head, ip_data_lst) { nm_assert(!ip_data->domains.search); nm_assert(!ip_data->domains.reverse); nm_assert(!ip_data->domains.has_default_route_explicit); @@ -1408,15 +1385,17 @@ _mgr_configs_data_construct(NMDnsManager *self) } #endif - c_list_for_each_entry (ip_data, head, ip_config_lst) { - NMIPConfig *ip_config = ip_data->ip_config; - gboolean add_wildcard = FALSE; + c_list_for_each_entry (ip_data, head, ip_data_lst) { + gboolean add_wildcard = FALSE; + guint num; - if (!nm_ip_config_get_num_nameservers(ip_config)) + nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &num); + if (num == 0) continue; - if (nm_ip_config_best_default_route_get(ip_config)) + if (nm_l3_config_data_get_best_default_route(ip_data->l3cd, ip_data->addr_family)) { + /* FIXME(l3cfg): the best-default route of a l3cd is not significant! */ add_wildcard = TRUE; - else { + } else { /* If a VPN has never-default=no but doesn't get a default * route (this can happen for example when the server * pushes routes with openconnect), and there are no @@ -1424,9 +1403,10 @@ _mgr_configs_data_construct(NMDnsManager *self) * by the server would be unused. It is preferable in this * case to use the VPN DNS server for all queries. */ if (ip_data->ip_config_type == NM_DNS_IP_CONFIG_TYPE_VPN - && !nm_ip_config_get_never_default(ip_data->ip_config) - && nm_ip_config_get_num_searches(ip_data->ip_config) == 0 - && nm_ip_config_get_num_domains(ip_data->ip_config) == 0) + && nm_l3_config_data_get_never_default(ip_data->l3cd, ip_data->addr_family) + == NM_TERNARY_FALSE + && !nm_l3_config_data_get_searches(ip_data->l3cd, ip_data->addr_family, NULL) + && !nm_l3_config_data_get_domains(ip_data->l3cd, ip_data->addr_family, NULL)) add_wildcard = TRUE; } @@ -1437,29 +1417,33 @@ _mgr_configs_data_construct(NMDnsManager *self) } } - c_list_for_each_entry (ip_data, head, ip_config_lst) { - NMIPConfig * ip_config = ip_data->ip_config; - int priority; - const char **domains; - guint n_searches; - guint n_domains; - guint num_dom1; - guint num_dom2; - guint n_domains_allocated; - guint i; - gboolean has_default_route_maybe = FALSE; - gboolean has_default_route_explicit = FALSE; - gboolean has_default_route_auto = FALSE; + c_list_for_each_entry (ip_data, head, ip_data_lst) { + int priority; + const char ** domains; + const char *const *strv_searches; + const char *const *strv_domains; + guint n_searches; + guint n_domains; + guint num_dom1; + guint num_dom2; + guint n_domains_allocated; + guint i; + gboolean has_default_route_maybe = FALSE; + gboolean has_default_route_explicit = FALSE; + gboolean has_default_route_auto = FALSE; + guint num; - if (!nm_ip_config_get_num_nameservers(ip_config)) + nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &num); + if (num == 0) continue; - n_searches = nm_ip_config_get_num_searches(ip_config); - n_domains = nm_ip_config_get_num_domains(ip_config); + strv_searches = + nm_l3_config_data_get_searches(ip_data->l3cd, ip_data->addr_family, &n_searches); + strv_domains = + nm_l3_config_data_get_domains(ip_data->l3cd, ip_data->addr_family, &n_domains); - priority = nm_ip_config_get_dns_priority(ip_config); + priority = _dns_config_ip_data_get_dns_priority(ip_data); - nm_assert(priority != 0); nm_assert(prev_priority <= priority); prev_priority = priority; @@ -1487,10 +1471,10 @@ _mgr_configs_data_construct(NMDnsManager *self) /* searches are preferred over domains */ if (n_searches > 0) { for (i = 0; i < n_searches; i++) - domains[num_dom1++] = nm_ip_config_get_search(ip_config, i); + domains[num_dom1++] = strv_searches[i]; } else { for (i = 0; i < n_domains; i++) - domains[num_dom1++] = nm_ip_config_get_domain(ip_config, i); + domains[num_dom1++] = strv_domains[i]; } nm_assert(num_dom1 < n_domains_allocated); @@ -1578,8 +1562,8 @@ _mgr_configs_data_construct(NMDnsManager *self) nm_assert(!ip_data->domains.search); nm_assert(!ip_data->domains.reverse); - ip_data->domains.search = domains; - ip_data->domains.reverse = get_ip_rdns_domains(ip_config); + ip_data->domains.search = domains; + ip_data->domains.reverse = get_ip_rdns_domains(ip_data->addr_family, ip_data->l3cd); ip_data->domains.has_default_route_explicit = has_default_route_explicit; ip_data->domains.has_default_route_exclusive = has_default_route_explicit || (priority < 0 && has_default_route_auto); @@ -1611,8 +1595,8 @@ _mgr_configs_data_clear(NMDnsManager *self) NMDnsConfigIPData *ip_data; CList * head; - head = _mgr_get_ip_config_lst_head(self); - c_list_for_each_entry (ip_data, head, ip_config_lst) { + head = _mgr_get_ip_data_lst_head(self); + c_list_for_each_entry (ip_data, head, ip_data_lst) { nm_clear_g_free(&ip_data->domains.search); nm_clear_pointer(&ip_data->domains.reverse, g_strfreev); ip_data->domains.has_default_route_explicit = FALSE; @@ -1643,6 +1627,8 @@ update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError nm_assert(!error || !*error); + priv->config_changed = FALSE; + if (priv->is_stopped) { _LOGD("update-dns: not updating resolv.conf (is stopped)"); return TRUE; @@ -1663,9 +1649,6 @@ update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError data = nm_config_get_data(priv->config); global_config = nm_config_data_get_global_dns_config(data); - /* Update hash with config we're applying */ - compute_hash(self, global_config, priv->hash); - _collect_resolv_conf_data(self, global_config, &searches, @@ -1680,7 +1663,7 @@ update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError if (priv->sd_resolve_plugin) { nm_dns_plugin_update(priv->sd_resolve_plugin, global_config, - _mgr_get_ip_config_lst_head(self), + _mgr_get_ip_data_lst_head(self), priv->hostname, NULL); } @@ -1702,7 +1685,7 @@ update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError _LOGD("update-dns: updating plugin %s", plugin_name); if (!nm_dns_plugin_update(plugin, global_config, - _mgr_get_ip_config_lst_head(self), + _mgr_get_ip_data_lst_head(self), priv->hostname, &plugin_error)) { _LOGW("update-dns: plugin %s update failed: %s", plugin_name, plugin_error->message); @@ -1834,53 +1817,101 @@ plugin_skip:; /*****************************************************************************/ -static void -_ip_config_dns_priority_changed(gpointer config, GParamSpec *pspec, NMDnsConfigIPData *ip_data) -{ - _ASSERT_dns_config_ip_data(ip_data); - - NM_DNS_MANAGER_GET_PRIVATE(ip_data->data->self)->ip_config_lst_need_sort = TRUE; -} - gboolean -nm_dns_manager_set_ip_config(NMDnsManager * self, - NMIPConfig * ip_config, - NMDnsIPConfigType ip_config_type) +nm_dns_manager_set_ip_config(NMDnsManager * self, + int addr_family, + gconstpointer source_tag, + const NML3ConfigData *l3cd, + NMDnsIPConfigType ip_config_type, + gboolean replace_all) { NMDnsManagerPrivate *priv; - NMDnsConfigIPData * ip_data; NMDnsConfigData * data; int ifindex; + gboolean changed = FALSE; + NMDnsConfigIPData * ip_data = NULL; + int dns_priority; g_return_val_if_fail(NM_IS_DNS_MANAGER(self), FALSE); - g_return_val_if_fail(NM_IS_IP_CONFIG(ip_config), FALSE); + g_return_val_if_fail(!l3cd || NM_IS_L3_CONFIG_DATA(l3cd), FALSE); + g_return_val_if_fail(source_tag, FALSE); - ifindex = nm_ip_config_get_ifindex(ip_config); - g_return_val_if_fail(ifindex > 0, FALSE); + if (addr_family == AF_UNSPEC) { + /* Setting AF_UNSPEC is a shortcut for calling this function twice for AF_INET and + * AF_INET6. */ + if (nm_dns_manager_set_ip_config(self, + AF_INET, + source_tag, + l3cd, + ip_config_type, + replace_all)) + changed = TRUE; + if (nm_dns_manager_set_ip_config(self, + AF_INET6, + source_tag, + l3cd, + ip_config_type, + replace_all)) + changed = TRUE; + return changed; + } + + nm_assert_addr_family(addr_family); priv = NM_DNS_MANAGER_GET_PRIVATE(self); - data = g_hash_table_lookup(priv->configs_dict, &ifindex); - if (!data) - ip_data = NULL; - else - ip_data = _dns_config_data_find_ip_config(data, ip_config); - - if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_REMOVED) { - if (!ip_data) - return FALSE; - /* deleting a config doesn't invalidate the configs' sort order. */ - _dns_config_ip_data_free(ip_data); - if (c_list_is_empty(&data->data_lst_head)) - g_hash_table_remove(priv->configs_dict, &ifindex); - goto changed; + data = NULL; + if (l3cd) { + ifindex = nm_l3_config_data_get_ifindex(l3cd); + nm_assert(ifindex > 0); + data = g_hash_table_lookup(priv->configs_dict, &ifindex); } + if (data) { + NMDnsConfigIPData *ip_data_iter; + NMDnsConfigIPData *ip_data_safe; + + c_list_for_each_entry_safe (ip_data_iter, ip_data_safe, &data->data_lst_head, data_lst) { + _ASSERT_dns_config_ip_data(ip_data_iter); + + if (ip_data_iter->source_tag != source_tag) + continue; + if (ip_data_iter->addr_family != addr_family) + continue; + + if (ip_config_type != NM_DNS_IP_CONFIG_TYPE_REMOVED && ip_data_iter->l3cd == l3cd) { + nm_assert(!ip_data); + ip_data = ip_data_iter; + continue; + } + + if (!replace_all && l3cd && ip_data_iter->l3cd != l3cd) + continue; + + changed = TRUE; + _dns_config_ip_data_free(ip_data_iter); + } + } + + if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_REMOVED) + goto done; + + if (!l3cd) + goto done; + if (ip_data && ip_data->ip_config_type == ip_config_type) { /* nothing to do. */ - return FALSE; + goto done; } + dns_priority = _dns_config_ip_data_get_dns_priority1(l3cd, addr_family); + if (dns_priority == 0) { + /* no DNS priority for this address family. Skip it! */ + goto done; + } + + changed = TRUE; + if (!data) { data = g_slice_new(NMDnsConfigData); *data = (NMDnsConfigData){ @@ -1895,13 +1926,28 @@ nm_dns_manager_set_ip_config(NMDnsManager * self, } if (!ip_data) - ip_data = _dns_config_ip_data_new(data, ip_config, ip_config_type); + ip_data = _dns_config_ip_data_new(data, addr_family, source_tag, l3cd, ip_config_type); else ip_data->ip_config_type = ip_config_type; - priv->ip_config_lst_need_sort = TRUE; + priv->ip_data_lst_need_sort = TRUE; + + nm_assert(l3cd); + nm_assert(ip_config_type != NM_DNS_IP_CONFIG_TYPE_REMOVED); + nm_assert(ip_data->addr_family == addr_family); + nm_assert(ip_data->source_tag == source_tag); + nm_assert(ip_data->l3cd == l3cd); + nm_assert(ip_data->ip_config_type == ip_config_type); + +done: + if (!changed) + return FALSE; + + priv->config_changed = TRUE; + + if (data && c_list_is_empty(&data->data_lst_head)) + g_hash_table_remove(priv->configs_dict, data); -changed: if (!priv->updates_queue) { gs_free_error GError *error = NULL; @@ -1957,11 +2003,8 @@ nm_dns_manager_begin_updates(NMDnsManager *self, const char *func) NMDnsManagerPrivate *priv; g_return_if_fail(self != NULL); - priv = NM_DNS_MANAGER_GET_PRIVATE(self); - /* Save current hash when starting a new batch */ - if (priv->updates_queue == 0) - memcpy(priv->prev_hash, priv->hash, sizeof(priv->hash)); + priv = NM_DNS_MANAGER_GET_PRIVATE(self); priv->updates_queue++; @@ -1973,20 +2016,16 @@ nm_dns_manager_end_updates(NMDnsManager *self, const char *func) { NMDnsManagerPrivate *priv; gs_free_error GError *error = NULL; - gboolean changed; - guint8 new[HASH_LEN]; g_return_if_fail(self != NULL); priv = NM_DNS_MANAGER_GET_PRIVATE(self); g_return_if_fail(priv->updates_queue > 0); - compute_hash(self, nm_config_data_get_global_dns_config(nm_config_get_data(priv->config)), new); - changed = (memcmp(new, priv->prev_hash, sizeof(new)) != 0) ? TRUE : FALSE; - _LOGD("(%s): DNS configuration %s", func, changed ? "changed" : "did not change"); + _LOGD("(%s): DNS configuration %s", func, priv->config_changed ? "changed" : "did not change"); priv->updates_queue--; - if ((priv->updates_queue > 0) || (changed == FALSE)) { + if ((priv->updates_queue > 0) || !priv->config_changed) { _LOGD("(%s): no DNS changes to commit (%d)", func, priv->updates_queue); return; } @@ -1995,8 +2034,6 @@ nm_dns_manager_end_updates(NMDnsManager *self, const char *func) _LOGD("(%s): committing DNS changes (%d)", func, priv->updates_queue); if (!update_dns(self, FALSE, FALSE, &error)) _LOGW("could not commit DNS changes: %s", error->message); - - memset(priv->prev_hash, 0, sizeof(priv->prev_hash)); } void @@ -2319,6 +2356,8 @@ config_changed_cb(NMConfig * config, NMConfigData * old_data, NMDnsManager * self) { + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self); + if (NM_FLAGS_ANY(changes, NM_CONFIG_CHANGE_DNS_MODE | NM_CONFIG_CHANGE_RC_MANAGER | NM_CONFIG_CHANGE_CAUSE_SIGHUP | NM_CONFIG_CHANGE_CAUSE_DNS_FULL)) { @@ -2338,6 +2377,7 @@ config_changed_cb(NMConfig * config, | NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG)) { gs_free_error GError *error = NULL; + priv->config_changed = TRUE; if (!update_dns(self, FALSE, TRUE, &error)) _LOGW("could not commit DNS changes: %s", error->message); } @@ -2415,51 +2455,54 @@ _get_config_variant(NMDnsManager *self) g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); - head = _mgr_get_ip_config_lst_head(self); - c_list_for_each_entry (ip_data, head, ip_config_lst) { - const NMIPConfig *ip_config = ip_data->ip_config; - GVariantBuilder entry_builder; - GVariantBuilder strv_builder; - guint i, num; - const int addr_family = nm_ip_config_get_addr_family(ip_config); - char buf[NM_UTILS_INET_ADDRSTRLEN]; - const NMIPAddr * addr; - const char * ifname; + 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; - num = nm_ip_config_get_num_nameservers(ip_config); - if (!num) + nameservers = nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &num); + if (num == 0) continue; g_variant_builder_init(&entry_builder, G_VARIANT_TYPE("a{sv}")); g_variant_builder_init(&strv_builder, G_VARIANT_TYPE("as")); for (i = 0; i < num; i++) { - addr = nm_ip_config_get_nameserver(ip_config, i); - g_variant_builder_add(&strv_builder, "s", nm_utils_inet_ntop(addr_family, addr, buf)); + const NMIPAddr *addr; + + 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(&entry_builder, "{sv}", "nameservers", g_variant_builder_end(&strv_builder)); - num = nm_ip_config_get_num_domains(ip_config); - num += nm_ip_config_get_num_searches(ip_config); + nm_l3_config_data_get_domains(ip_data->l3cd, ip_data->addr_family, &num_domains); + nm_l3_config_data_get_searches(ip_data->l3cd, ip_data->addr_family, &num_searches); + num = num_domains + num_searches; if (num > 0) { if (!array_domains) array_domains = g_ptr_array_sized_new(num); else g_ptr_array_set_size(array_domains, 0); - add_dns_domains(array_domains, ip_config, TRUE, FALSE); + add_dns_domains(array_domains, ip_data->addr_family, ip_data->l3cd, TRUE, FALSE); if (array_domains->len) { - g_variant_builder_init(&strv_builder, G_VARIANT_TYPE("as")); - for (i = 0; i < array_domains->len; i++) { - g_variant_builder_add(&strv_builder, "s", array_domains->pdata[i]); - } g_variant_builder_add(&entry_builder, "{sv}", "domains", - g_variant_builder_end(&strv_builder)); + g_variant_new_strv((const char *const *) array_domains->pdata, + array_domains->len)); } } @@ -2474,7 +2517,7 @@ _get_config_variant(NMDnsManager *self) g_variant_builder_add(&entry_builder, "{sv}", "priority", - g_variant_new_int32(nm_ip_config_get_dns_priority(ip_config))); + g_variant_new_int32(_dns_config_ip_data_get_dns_priority(ip_data))); g_variant_builder_add( &entry_builder, @@ -2521,7 +2564,7 @@ nm_dns_manager_init(NMDnsManager *self) _LOGT("creating..."); c_list_init(&priv->configs_lst_head); - c_list_init(&priv->ip_config_lst_head); + c_list_init(&priv->ip_data_lst_head); priv->config = g_object_ref(nm_config_get()); @@ -2531,9 +2574,6 @@ nm_dns_manager_init(NMDnsManager *self) (GDestroyNotify) _dns_config_data_free, NULL); - /* Set the initial hash */ - compute_hash(self, NULL, NM_DNS_MANAGER_GET_PRIVATE(self)->hash); - g_signal_connect(G_OBJECT(priv->config), NM_CONFIG_SIGNAL_CONFIG_CHANGED, G_CALLBACK(config_changed_cb), @@ -2559,7 +2599,7 @@ dispose(GObject *object) g_clear_object(&priv->sd_resolve_plugin); _clear_plugin(self); - c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_config_lst_head, ip_config_lst) + c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_data_lst_head, ip_data_lst) _dns_config_ip_data_free(ip_data); nm_clear_pointer(&priv->configs_dict, g_hash_table_destroy); diff --git a/src/core/dns/nm-dns-manager.h b/src/core/dns/nm-dns-manager.h index 1972a5dd7a..2b5ca634b1 100644 --- a/src/core/dns/nm-dns-manager.h +++ b/src/core/dns/nm-dns-manager.h @@ -8,8 +8,7 @@ #ifndef __NETWORKMANAGER_DNS_MANAGER_H__ #define __NETWORKMANAGER_DNS_MANAGER_H__ -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" +#include "c-list/src/c-list.h" #include "nm-setting-connection.h" #include "nm-dns-plugin.h" @@ -28,10 +27,12 @@ struct _NMDnsManager; typedef struct { struct _NMDnsConfigData *data; - NMIPConfig * ip_config; + gconstpointer source_tag; + const NML3ConfigData * l3cd; CList data_lst; - CList ip_config_lst; + CList ip_data_lst; NMDnsIPConfigType ip_config_type; + int addr_family; struct { const char **search; char ** reverse; @@ -97,9 +98,12 @@ NMDnsManager *nm_dns_manager_get(void); void nm_dns_manager_begin_updates(NMDnsManager *self, const char *func); void nm_dns_manager_end_updates(NMDnsManager *self, const char *func); -gboolean nm_dns_manager_set_ip_config(NMDnsManager * self, - NMIPConfig * ip_config, - NMDnsIPConfigType ip_config_type); +gboolean nm_dns_manager_set_ip_config(NMDnsManager * self, + int addr_family, + gconstpointer source_tag, + const NML3ConfigData *l3cd, + NMDnsIPConfigType ip_config_type, + gboolean replace_all); void nm_dns_manager_set_initial_hostname(NMDnsManager *self, const char *hostname); void nm_dns_manager_set_hostname(NMDnsManager *self, const char *hostname, gboolean skip_update); diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c index 445f8498a7..c9d6cc5fea 100644 --- a/src/core/dns/nm-dns-systemd-resolved.c +++ b/src/core/dns/nm-dns-systemd-resolved.c @@ -21,10 +21,9 @@ #include "libnm-core-intern/nm-core-internal.h" #include "libnm-platform/nm-platform.h" #include "nm-utils.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" #include "nm-dbus-manager.h" #include "nm-manager.h" +#include "nm-l3-config-data.h" #include "nm-setting-connection.h" #include "devices/nm-device.h" #include "NetworkManagerUtils.h" @@ -246,40 +245,42 @@ static gboolean update_add_ip_config(NMDnsSystemdResolved *self, GVariantBuilder * dns, GVariantBuilder * domains, - NMDnsConfigIPData * data) + NMDnsConfigIPData * ip_data) { - int addr_family; - gsize addr_size; - guint i, n; - gboolean is_routing; - const char *domain; - gboolean has_config = FALSE; + gsize addr_size; + guint n; + guint i; + gboolean is_routing; + const char * domain; + gboolean has_config = FALSE; + gconstpointer nameservers; - addr_family = nm_ip_config_get_addr_family(data->ip_config); - addr_size = nm_utils_addr_family_to_size(addr_family); + addr_size = nm_utils_addr_family_to_size(ip_data->addr_family); - if ((!data->domains.search || !data->domains.search[0]) - && !data->domains.has_default_route_exclusive && !data->domains.has_default_route) + if ((!ip_data->domains.search || !ip_data->domains.search[0]) + && !ip_data->domains.has_default_route_exclusive && !ip_data->domains.has_default_route) return FALSE; - n = nm_ip_config_get_num_nameservers(data->ip_config); + nameservers = 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", addr_family); + g_variant_builder_add(dns, "i", ip_data->addr_family); g_variant_builder_add_value( dns, - nm_g_variant_new_ay(nm_ip_config_get_nameserver(data->ip_config, i), addr_size)); + 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; } - if (!data->domains.has_default_route_explicit && data->domains.has_default_route_exclusive) { + 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 (data->domains.search) { - for (i = 0; data->domains.search[i]; i++) { - domain = nm_utils_parse_dns_domain(data->domains.search[i], &is_routing); + 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; } @@ -321,19 +322,17 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) g_variant_builder_open(&domains, G_VARIANT_TYPE("a(sb)")); c_list_for_each_entry (elem, &ic->configs_lst_head, lst) { - NMDnsConfigIPData *data = elem->data; - NMIPConfig * ip_config = data->ip_config; + NMDnsConfigIPData *ip_data = elem->data; - has_config |= update_add_ip_config(self, &dns, &domains, data); + has_config |= update_add_ip_config(self, &dns, &domains, ip_data); - if (data->domains.has_default_route) + if (ip_data->domains.has_default_route) has_default_route = TRUE; - if (NM_IS_IP4_CONFIG(ip_config)) { - mdns = NM_MAX(mdns, nm_ip4_config_mdns_get(NM_IP4_CONFIG(ip_config))); - llmnr = NM_MAX(llmnr, nm_ip4_config_llmnr_get(NM_IP4_CONFIG(ip_config))); - dns_over_tls = - NM_MAX(dns_over_tls, nm_ip4_config_dns_over_tls_get(NM_IP4_CONFIG(ip_config))); + if (NM_IS_IPv4(ip_data->addr_family)) { + mdns = NM_MAX(mdns, nm_l3_config_data_get_mdns(ip_data->l3cd)); + llmnr = NM_MAX(llmnr, nm_l3_config_data_get_llmnr(ip_data->l3cd)); + dns_over_tls = NM_MAX(dns_over_tls, nm_l3_config_data_get_dns_over_tls(ip_data->l3cd)); } } @@ -551,7 +550,7 @@ start_resolve: static gboolean update(NMDnsPlugin * plugin, const NMGlobalDnsConfig *global_config, - const CList * ip_config_lst_head, + const CList * ip_data_lst_head, const char * hostname, GError ** error) { @@ -569,11 +568,11 @@ update(NMDnsPlugin * plugin, interfaces = g_hash_table_new_full(nm_direct_hash, NULL, NULL, (GDestroyNotify) _interface_config_free); - c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) { + c_list_for_each_entry (ip_data, ip_data_lst_head, ip_data_lst) { InterfaceConfig *ic = NULL; ifindex = ip_data->data->ifindex; - nm_assert(ifindex == nm_ip_config_get_ifindex(ip_data->ip_config)); + nm_assert(ifindex == nm_l3_config_data_get_ifindex(ip_data->l3cd)); ic = g_hash_table_lookup(interfaces, GINT_TO_POINTER(ifindex)); if (!ic) { diff --git a/src/core/dnsmasq/nm-dnsmasq-manager.c b/src/core/dnsmasq/nm-dnsmasq-manager.c index e8697e817e..f4eea33a13 100644 --- a/src/core/dnsmasq/nm-dnsmasq-manager.c +++ b/src/core/dnsmasq/nm-dnsmasq-manager.c @@ -17,6 +17,7 @@ #include "nm-dnsmasq-utils.h" #include "nm-utils.h" #include "NetworkManagerUtils.h" +#include "nm-l3-config-data.h" #include "libnm-core-intern/nm-core-internal.h" #define CONFDIR NMCONFDIR "/dnsmasq-shared.d" @@ -85,24 +86,28 @@ dm_watch_cb(GPid pid, int status, gpointer user_data) } static GPtrArray * -create_dm_cmd_line(const char * iface, - const NMIP4Config *ip4_config, - const char * pidfile, - gboolean announce_android_metered, - GError ** error) +create_dm_cmd_line(const char * iface, + const NML3ConfigData *l3cd, + const char * pidfile, + gboolean announce_android_metered, + GError ** error) { gs_unref_ptrarray GPtrArray *cmd = NULL; nm_auto_free_gstring GString *s = NULL; char first[INET_ADDRSTRLEN]; char last[INET_ADDRSTRLEN]; char listen_address_s[INET_ADDRSTRLEN]; - char tmpaddr[INET_ADDRSTRLEN]; + char sbuf_addr[INET_ADDRSTRLEN]; gs_free char * error_desc = NULL; const char * dm_binary; const NMPlatformIP4Address * listen_address; - guint i, n; + const in_addr_t * ipv4arr; + const char *const * strarr; + guint n; + guint i; - listen_address = nm_ip4_config_get_first_address(ip4_config); + listen_address = NMP_OBJECT_CAST_IP4_ADDRESS( + nm_l3_config_data_get_first_obj(l3cd, NMP_OBJECT_TYPE_IP4_ADDRESS, NULL)); g_return_val_if_fail(listen_address, NULL); @@ -151,28 +156,28 @@ create_dm_cmd_line(const char * iface, nm_strv_ptrarray_add_string_printf(cmd, "--dhcp-range=%s,%s,60m", first, last); - if (nm_ip4_config_best_default_route_get(ip4_config)) { + if (nm_l3_config_data_get_best_default_route(l3cd, AF_INET)) { nm_strv_ptrarray_add_string_concat(cmd, "--dhcp-option=option:router,", listen_address_s); } - if ((n = nm_ip4_config_get_num_nameservers(ip4_config))) { + ipv4arr = 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++) { g_string_append_c(s, ','); - g_string_append( - s, - _nm_utils_inet4_ntop(nm_ip4_config_get_nameserver(ip4_config, i), tmpaddr)); + g_string_append(s, _nm_utils_inet4_ntop(ipv4arr[i], sbuf_addr)); } nm_strv_ptrarray_take_gstring(cmd, &s); } - if ((n = nm_ip4_config_get_num_searches(ip4_config))) { + strarr = nm_l3_config_data_get_searches(l3cd, AF_INET, &n); + if (n > 0) { nm_gstring_prepare(&s); g_string_append(s, "--dhcp-option=option:domain-search"); for (i = 0; i < n; i++) { g_string_append_c(s, ','); - g_string_append(s, nm_ip4_config_get_search(ip4_config, i)); + g_string_append(s, strarr[i]); } nm_strv_ptrarray_take_gstring(cmd, &s); } @@ -237,25 +242,25 @@ out: } gboolean -nm_dnsmasq_manager_start(NMDnsMasqManager *manager, - NMIP4Config * ip4_config, - gboolean announce_android_metered, - GError ** error) +nm_dnsmasq_manager_start(NMDnsMasqManager * manager, + const NML3ConfigData *l3cd, + gboolean announce_android_metered, + GError ** error) { - NMDnsMasqManagerPrivate *priv; gs_unref_ptrarray GPtrArray *dm_cmd = NULL; gs_free char * cmd_str = NULL; + NMDnsMasqManagerPrivate * priv; g_return_val_if_fail(NM_IS_DNSMASQ_MANAGER(manager), FALSE); g_return_val_if_fail(!error || !*error, FALSE); - g_return_val_if_fail(nm_ip4_config_get_num_addresses(ip4_config) > 0, FALSE); + g_return_val_if_fail(NM_IS_L3_CONFIG_DATA(l3cd), FALSE); + g_return_val_if_fail(nm_l3_config_data_get_num_addresses(l3cd, AF_INET) > 0, FALSE); priv = NM_DNSMASQ_MANAGER_GET_PRIVATE(manager); kill_existing_by_pidfile(priv->pidfile); - dm_cmd = - create_dm_cmd_line(priv->iface, ip4_config, priv->pidfile, announce_android_metered, error); + dm_cmd = create_dm_cmd_line(priv->iface, l3cd, priv->pidfile, announce_android_metered, error); if (!dm_cmd) return FALSE; diff --git a/src/core/dnsmasq/nm-dnsmasq-manager.h b/src/core/dnsmasq/nm-dnsmasq-manager.h index 272d1cead1..78ca37251e 100644 --- a/src/core/dnsmasq/nm-dnsmasq-manager.h +++ b/src/core/dnsmasq/nm-dnsmasq-manager.h @@ -6,8 +6,6 @@ #ifndef __NETWORKMANAGER_DNSMASQ_MANAGER_H__ #define __NETWORKMANAGER_DNSMASQ_MANAGER_H__ -#include "nm-ip4-config.h" - #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)) @@ -35,10 +33,10 @@ GType nm_dnsmasq_manager_get_type(void); NMDnsMasqManager *nm_dnsmasq_manager_new(const char *iface); -gboolean nm_dnsmasq_manager_start(NMDnsMasqManager *manager, - NMIP4Config * ip4_config, - gboolean announce_android_metered, - GError ** error); +gboolean nm_dnsmasq_manager_start(NMDnsMasqManager * manager, + const NML3ConfigData *l3cd, + gboolean announce_android_metered, + GError ** error); void nm_dnsmasq_manager_stop(NMDnsMasqManager *manager); diff --git a/src/core/main-utils.h b/src/core/main-utils.h index 2eb731a1a6..9be2433e31 100644 --- a/src/core/main-utils.h +++ b/src/core/main-utils.h @@ -26,9 +26,6 @@ gboolean nm_main_utils_early_setup(const char * progname, gpointer option_context_hook_data, const char *summary); -/* The following functions are not implemented inside nm-main-utils.c, instead - * main.c and nm-iface-helper.c */ - void nm_main_config_reload(int signal); #endif /* __MAIN_UTILS_H__ */ diff --git a/src/core/main.c b/src/core/main.c index 7d448a5976..14d499f29f 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -269,16 +269,6 @@ _dbus_manager_init(NMConfig *config) if (c_a_q_type == NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED) return nm_dbus_manager_acquire_bus(busmgr, TRUE); - if (c_a_q_type == NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED) { - /* D-Bus is useless in configure and quit mode -- we're eventually dropping - * off and potential clients would have no way of knowing whether we're - * finished already or didn't start yet. - * - * But we still create a nm_dbus_manager_get_dbus_connection() D-Bus connection - * so that we can talk to other services like firewalld. */ - return nm_dbus_manager_acquire_bus(busmgr, FALSE); - } - nm_assert(c_a_q_type == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD); /* in initrd we don't have D-Bus at all. Don't even try to get the G_BUS_TYPE_SYSTEM * connection. And of course don't claim the D-Bus name. */ diff --git a/src/core/meson.build b/src/core/meson.build index 46b636817d..5bd10dba0b 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -54,8 +54,6 @@ libNetworkManagerBase = static_library( 'nm-l3-ipv6ll.c', 'nm-l3cfg.c', 'nm-ip-config.c', - 'nm-ip4-config.c', - 'nm-ip6-config.c', ), dependencies: [ core_default_dep, @@ -93,7 +91,6 @@ endif libNetworkManager = static_library( 'NetworkManager', sources: files( - 'devices/nm-acd-manager.c', 'devices/nm-device-6lowpan.c', 'devices/nm-device-bond.c', 'devices/nm-device-bridge.c', @@ -130,9 +127,10 @@ libNetworkManager = static_library( 'dnsmasq/nm-dnsmasq-manager.c', 'dnsmasq/nm-dnsmasq-utils.c', 'ppp/nm-ppp-manager-call.c', - 'settings/plugins/keyfile/nms-keyfile-storage.c', + 'ppp/nm-ppp-mgr.c', 'settings/plugins/keyfile/nms-keyfile-plugin.c', 'settings/plugins/keyfile/nms-keyfile-reader.c', + 'settings/plugins/keyfile/nms-keyfile-storage.c', 'settings/plugins/keyfile/nms-keyfile-utils.c', 'settings/plugins/keyfile/nms-keyfile-writer.c', 'settings/nm-agent-manager.c', @@ -169,7 +167,6 @@ libNetworkManager = static_library( 'nm-manager.c', 'nm-pacrunner-manager.c', 'nm-policy.c', - 'nm-proxy-config.c', 'nm-rfkill-manager.c', 'nm-session-monitor.c', 'nm-sleep-monitor.c', @@ -183,40 +180,6 @@ libNetworkManager = static_library( ], ) -executable( - 'nm-iface-helper', - 'nm-iface-helper.c', - dependencies: [ - core_default_dep, - glib_dep, - libudev_dep, - libndp_dep, - dl_dep, - ], - link_with: [ - libNetworkManagerBase, - libnm_core_aux_intern, - libnm_core_impl, - libnm_crypto, - libnm_systemd_core, - libnm_platform, - libnm_systemd_shared, - libnm_base, - libnm_udev_aux, - libnm_log_core, - libnm_glib_aux, - libnm_std_aux, - libn_acd, - libn_dhcp4, - libc_rbtree, - libc_siphash, - ], - link_args: ldflags_linker_script_binary, - link_depends: linker_script_binary, - install: true, - install_dir: nm_libexecdir, -) - if enable_tests test_c_flags = [] if require_root_tests diff --git a/src/core/ndisc/nm-fake-ndisc.c b/src/core/ndisc/nm-fake-ndisc.c index ffe07ab6b7..2794e286da 100644 --- a/src/core/ndisc/nm-fake-ndisc.c +++ b/src/core/ndisc/nm-fake-ndisc.c @@ -10,6 +10,7 @@ #include #include "nm-ndisc-private.h" +#include "nm-l3cfg.h" #define _NMLOG_PREFIX_NAME "ndisc-fake" @@ -354,28 +355,23 @@ nm_fake_ndisc_init(NMFakeNDisc *fake_ndisc) {} NMNDisc * -nm_fake_ndisc_new(int ifindex, const char *ifname) +nm_fake_ndisc_new(NML3Cfg *l3cfg) { - return g_object_new(NM_TYPE_FAKE_NDISC, - NM_NDISC_IFINDEX, - ifindex, - NM_NDISC_IFNAME, - ifname, - NM_NDISC_NODE_TYPE, - (int) NM_NDISC_NODE_TYPE_HOST, - NM_NDISC_STABLE_TYPE, - (int) NM_UTILS_STABLE_TYPE_UUID, - NM_NDISC_NETWORK_ID, - "fake", - NM_NDISC_MAX_ADDRESSES, - NM_NDISC_MAX_ADDRESSES_DEFAULT, - NM_NDISC_ROUTER_SOLICITATIONS, - NM_NDISC_ROUTER_SOLICITATIONS_DEFAULT, - NM_NDISC_ROUTER_SOLICITATION_INTERVAL, - NM_NDISC_RFC4861_RTR_SOLICITATION_INTERVAL, - NM_NDISC_RA_TIMEOUT, - 30u, - NULL); + const NMNDiscConfig config = { + .l3cfg = g_object_ref(NM_L3CFG(l3cfg)), + .ifname = nm_l3cfg_get_ifname(l3cfg, TRUE), + .node_type = NM_NDISC_NODE_TYPE_HOST, + .stable_type = NM_UTILS_STABLE_TYPE_UUID, + .network_id = "fake", + .max_addresses = NM_NDISC_MAX_ADDRESSES_DEFAULT, + .router_solicitations = NM_NDISC_ROUTER_SOLICITATIONS_DEFAULT, + .router_solicitation_interval = NM_NDISC_RFC4861_RTR_SOLICITATION_INTERVAL, + .ra_timeout = 30u, + .addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64, + .ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, + }; + + return g_object_new(NM_TYPE_FAKE_NDISC, NM_NDISC_CONFIG, &config, NULL); } static void diff --git a/src/core/ndisc/nm-fake-ndisc.h b/src/core/ndisc/nm-fake-ndisc.h index 677f15fcac..ba0a7acb78 100644 --- a/src/core/ndisc/nm-fake-ndisc.h +++ b/src/core/ndisc/nm-fake-ndisc.h @@ -24,7 +24,7 @@ typedef struct _NMFakeRNDiscClass NMFakeNDiscClass; GType nm_fake_ndisc_get_type(void); -NMNDisc *nm_fake_ndisc_new(int ifindex, const char *ifname); +NMNDisc *nm_fake_ndisc_new(NML3Cfg *l3cfg); guint nm_fake_ndisc_add_ra(NMFakeNDisc * self, guint seconds, diff --git a/src/core/ndisc/nm-lndp-ndisc.c b/src/core/ndisc/nm-lndp-ndisc.c index 552b0e4b59..ee040be157 100644 --- a/src/core/ndisc/nm-lndp-ndisc.c +++ b/src/core/ndisc/nm-lndp-ndisc.c @@ -9,16 +9,16 @@ #include #include -/* stdarg.h included because of a bug in ndp.h */ #include #include -#include "libnm-glib-aux/nm-str-buf.h" -#include "libnm-systemd-shared/nm-sd-utils-shared.h" -#include "nm-ndisc-private.h" #include "NetworkManagerUtils.h" +#include "libnm-glib-aux/nm-str-buf.h" #include "libnm-platform/nm-platform.h" #include "libnm-platform/nmp-netns.h" +#include "libnm-systemd-shared/nm-sd-utils-shared.h" +#include "nm-l3cfg.h" +#include "nm-ndisc-private.h" #define _NMLOG_PREFIX_NAME "ndisc-lndp" @@ -669,72 +669,39 @@ nm_lndp_ndisc_init(NMLndpNDisc *lndp_ndisc) {} NMNDisc * -nm_lndp_ndisc_new(NMPlatform * platform, - int ifindex, - const char * ifname, - NMUtilsStableType stable_type, - const char * network_id, - NMSettingIP6ConfigAddrGenMode addr_gen_mode, - NMNDiscNodeType node_type, - int max_addresses, - int router_solicitations, - int router_solicitation_interval, - guint32 ra_timeout, - GError ** error) +nm_lndp_ndisc_new(const NMNDiscConfig *config) { nm_auto_pop_netns NMPNetns *netns = NULL; - NMNDisc * ndisc; - NMLndpNDiscPrivate * priv; - int errsv; + gs_unref_object NMNDisc *ndisc = NULL; + NMLndpNDiscPrivate * priv; + int errsv; - g_return_val_if_fail(NM_IS_PLATFORM(platform), NULL); - g_return_val_if_fail(!error || !*error, NULL); - g_return_val_if_fail(network_id, NULL); + g_return_val_if_fail(config, NULL); + g_return_val_if_fail(NM_IS_L3CFG(config->l3cfg), NULL); + g_return_val_if_fail(config->network_id, NULL); - if (!nm_platform_netns_push(platform, &netns)) - return NULL; + if (!nm_platform_netns_push(nm_l3cfg_get_platform(config->l3cfg), &netns)) { + /* The inability to change the name space is also considered + * a fatal error. We have a FD open to the file descriptor, and + * it's unclear how to handle (or recover from) a failure to setns(). */ + g_return_val_if_reached(NULL); + } - ndisc = g_object_new(NM_TYPE_LNDP_NDISC, - NM_NDISC_PLATFORM, - platform, - NM_NDISC_STABLE_TYPE, - (int) stable_type, - NM_NDISC_IFINDEX, - ifindex, - NM_NDISC_IFNAME, - ifname, - NM_NDISC_NETWORK_ID, - network_id, - NM_NDISC_ADDR_GEN_MODE, - (int) addr_gen_mode, - NM_NDISC_NODE_TYPE, - (int) node_type, - NM_NDISC_MAX_ADDRESSES, - max_addresses, - NM_NDISC_ROUTER_SOLICITATIONS, - router_solicitations, - NM_NDISC_ROUTER_SOLICITATION_INTERVAL, - router_solicitation_interval, - NM_NDISC_RA_TIMEOUT, - (guint) ra_timeout, - NULL); + ndisc = g_object_new(NM_TYPE_LNDP_NDISC, NM_NDISC_CONFIG, config, NULL); priv = NM_LNDP_NDISC_GET_PRIVATE(ndisc); errsv = ndp_open(&priv->ndp); if (errsv != 0) { - errsv = nm_errno_native(errsv); - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "failure creating libndp socket: %s (%d)", - nm_strerror_native(errsv), - errsv); - g_object_unref(ndisc); - return NULL; + /* This is serious. It might be ENOMEM or the inability to open (or modify) + * a file descriptor. In all cases there is not much reason trying to recover + * from that. File descriptors are a basic resource, that we just require (just + * like memory). */ + g_return_val_if_reached(NULL); } - return ndisc; + + return g_steal_pointer(&ndisc); } static void @@ -754,8 +721,9 @@ nm_lndp_ndisc_class_init(NMLndpNDiscClass *klass) NMNDiscClass *ndisc_class = NM_NDISC_CLASS(klass); object_class->dispose = dispose; - ndisc_class->start = start; - ndisc_class->stop = stop; - ndisc_class->send_rs = send_rs; - ndisc_class->send_ra = send_ra; + + ndisc_class->start = start; + ndisc_class->stop = stop; + ndisc_class->send_rs = send_rs; + ndisc_class->send_ra = send_ra; } diff --git a/src/core/ndisc/nm-lndp-ndisc.h b/src/core/ndisc/nm-lndp-ndisc.h index 151f4bf7ae..711a95e177 100644 --- a/src/core/ndisc/nm-lndp-ndisc.h +++ b/src/core/ndisc/nm-lndp-ndisc.h @@ -23,17 +23,6 @@ typedef struct _NMLndpNDiscClass NMLndpNDiscClass; GType nm_lndp_ndisc_get_type(void); -NMNDisc *nm_lndp_ndisc_new(NMPlatform * platform, - int ifindex, - const char * ifname, - NMUtilsStableType stable_type, - const char * network_id, - NMSettingIP6ConfigAddrGenMode addr_gen_mode, - NMNDiscNodeType node_type, - int max_addresses, - int router_solicitations, - int router_solicitation_interval, - guint32 ra_timeout, - GError ** error); +NMNDisc *nm_lndp_ndisc_new(const NMNDiscConfig *config); #endif /* __NETWORKMANAGER_LNDP_NDISC_H__ */ diff --git a/src/core/ndisc/nm-ndisc.c b/src/core/ndisc/nm-ndisc.c index d7b1a2e154..d870f5f119 100644 --- a/src/core/ndisc/nm-ndisc.c +++ b/src/core/ndisc/nm-ndisc.c @@ -7,16 +7,17 @@ #include "nm-ndisc.h" -#include #include +#include -#include "nm-setting-ip6-config.h" - -#include "nm-ndisc-private.h" -#include "nm-utils.h" +#include "libnm-platform/nm-platform-utils.h" #include "libnm-platform/nm-platform.h" #include "libnm-platform/nmp-netns.h" #include "nm-l3-config-data.h" +#include "nm-l3cfg.h" +#include "nm-ndisc-private.h" +#include "nm-setting-ip6-config.h" +#include "nm-utils.h" #define _NMLOG_PREFIX_NAME "ndisc" @@ -38,6 +39,8 @@ struct _NMNDiscPrivate { /* this *must* be the first field. */ NMNDiscDataInternal rdata; + const NML3ConfigData *l3cd; + char *last_error; GSource *ra_timeout_source; @@ -55,37 +58,25 @@ struct _NMNDiscPrivate { NMUtilsIPv6IfaceId iid; - /* immutable values: */ - int ifindex; - char * ifname; - char * network_id; - guint max_addresses; - NMSettingIP6ConfigAddrGenMode addr_gen_mode; - NMUtilsStableType stable_type; - guint32 ra_timeout; - gint32 router_solicitations; - gint32 router_solicitation_interval; - NMNDiscNodeType node_type; + /* immutable values from here on: */ - NMPlatform *platform; - NMPNetns * netns; + union { + const NMNDiscConfig config; + NMNDiscConfig config_; + }; + + NMPNetns *netns; }; typedef struct _NMNDiscPrivate NMNDiscPrivate; -NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PLATFORM, - PROP_IFINDEX, - PROP_IFNAME, - PROP_STABLE_TYPE, - PROP_NETWORK_ID, - PROP_ADDR_GEN_MODE, - PROP_MAX_ADDRESSES, - PROP_RA_TIMEOUT, - PROP_ROUTER_SOLICITATIONS, - PROP_ROUTER_SOLICITATION_INTERVAL, - PROP_NODE_TYPE, ); +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CONFIG, ); -enum { CONFIG_RECEIVED, RA_TIMEOUT_SIGNAL, LAST_SIGNAL }; +enum { + CONFIG_RECEIVED, + RA_TIMEOUT_SIGNAL, + LAST_SIGNAL, +}; static guint signals[LAST_SIGNAL] = {0}; @@ -114,15 +105,10 @@ NML3ConfigData * nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, int ifindex, const NMNDiscData * rdata, - NMSettingIP6ConfigPrivacy ip6_privacy, - guint32 route_table, - guint32 route_metric, - gboolean kernel_support_rta_pref, - gboolean kernel_support_extended_ifa_flags) + NMSettingIP6ConfigPrivacy ip6_privacy) { nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; guint32 ifa_flags; - guint8 plen; guint i; const gint32 now_sec = nm_utils_get_monotonic_timestamp_sec(); @@ -130,20 +116,11 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, nm_l3_config_data_set_ip6_privacy(l3cd, ip6_privacy); - /* Check, whether kernel is recent enough to help user space handling RA. - * If it's not supported, we have no ipv6-privacy and must add autoconf - * addresses as /128. The reason for the /128 is to prevent the kernel - * from adding a prefix route for this address. */ - ifa_flags = 0; - if (kernel_support_extended_ifa_flags) { - ifa_flags |= IFA_F_NOPREFIXROUTE; - if (NM_IN_SET(ip6_privacy, - NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, - NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)) - ifa_flags |= IFA_F_MANAGETEMPADDR; - plen = 64; - } else - plen = 128; + ifa_flags = IFA_F_NOPREFIXROUTE; + if (NM_IN_SET(ip6_privacy, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)) + ifa_flags |= IFA_F_MANAGETEMPADDR; for (i = 0; i < rdata->addresses_n; i++) { const NMNDiscAddress *ndisc_addr = &rdata->addresses[i]; @@ -152,7 +129,7 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, a = (NMPlatformIP6Address){ .ifindex = ifindex, .address = ndisc_addr->address, - .plen = plen, + .plen = 64, .timestamp = now_sec, .lifetime = _nm_ndisc_lifetime_from_expiry(((gint64) now_sec) * 1000, ndisc_addr->expiry_msec, @@ -178,8 +155,10 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, .plen = ndisc_route->plen, .gateway = ndisc_route->gateway, .rt_source = NM_IP_CONFIG_SOURCE_NDISC, - .table_coerced = nm_platform_route_table_coerce(route_table), - .metric = route_metric, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, + .metric = 0, .rt_pref = ndisc_route->preference, }; nm_assert((NMIcmpv6RouterPref) r.rt_pref == ndisc_route->preference); @@ -188,12 +167,13 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, } if (rdata->gateways_n > 0) { - const NMIcmpv6RouterPref first_pref = rdata->gateways[0].preference; - NMPlatformIP6Route r = { + NMPlatformIP6Route r = { .rt_source = NM_IP_CONFIG_SOURCE_NDISC, .ifindex = ifindex, - .table_coerced = nm_platform_route_table_coerce(route_table), - .metric = route_metric, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, + .metric = 0, }; for (i = 0; i < rdata->gateways_n; i++) { @@ -201,13 +181,6 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, r.rt_pref = rdata->gateways[i].preference; nm_assert((NMIcmpv6RouterPref) r.rt_pref == rdata->gateways[i].preference); nm_l3_config_data_add_route_6(l3cd, &r); - - if (first_pref != rdata->gateways[i].preference && !kernel_support_rta_pref) { - /* We are unable to configure a router preference. Hence, we skip all gateways - * with a different preference from the first gateway. Note, that the gateways - * are sorted in order of highest to lowest preference. */ - break; - } } } @@ -315,7 +288,7 @@ nm_ndisc_get_ifindex(NMNDisc *self) { g_return_val_if_fail(NM_IS_NDISC(self), 0); - return NM_NDISC_GET_PRIVATE(self)->ifindex; + return nm_l3cfg_get_ifindex(NM_NDISC_GET_PRIVATE(self)->config.l3cfg); } const char * @@ -323,7 +296,7 @@ nm_ndisc_get_ifname(NMNDisc *self) { g_return_val_if_fail(NM_IS_NDISC(self), NULL); - return NM_NDISC_GET_PRIVATE(self)->ifname; + return NM_NDISC_GET_PRIVATE(self)->config.ifname; } NMNDiscNodeType @@ -331,7 +304,7 @@ nm_ndisc_get_node_type(NMNDisc *self) { g_return_val_if_fail(NM_IS_NDISC(self), NM_NDISC_NODE_TYPE_INVALID); - return NM_NDISC_GET_PRIVATE(self)->node_type; + return NM_NDISC_GET_PRIVATE(self)->config.node_type; } /*****************************************************************************/ @@ -391,15 +364,26 @@ _data_complete(NMNDiscDataInternal *data) return &data->public; } -void +static void nm_ndisc_emit_config_change(NMNDisc *self, NMNDiscConfigMap changed) { + NMNDiscPrivate * priv = NM_NDISC_GET_PRIVATE(self); + nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; + const NMNDiscData * rdata; + _config_changed_log(self, changed); - g_signal_emit(self, - signals[CONFIG_RECEIVED], - 0, - _data_complete(&NM_NDISC_GET_PRIVATE(self)->rdata), - (guint) changed); + + rdata = _data_complete(&NM_NDISC_GET_PRIVATE(self)->rdata), + + l3cd = nm_l3_config_data_seal(nm_ndisc_data_to_l3cd(nm_l3cfg_get_multi_idx(priv->config.l3cfg), + nm_l3cfg_get_ifindex(priv->config.l3cfg), + rdata, + priv->config.ip6_privacy)); + + if (!nm_l3_config_data_equal(priv->l3cd, l3cd)) + NM_SWAP(&priv->l3cd, &l3cd); + + g_signal_emit(self, signals[CONFIG_RECEIVED], 0, rdata, (guint) changed, priv->l3cd); } /*****************************************************************************/ @@ -479,11 +463,11 @@ complete_address(NMNDisc *ndisc, NMNDiscAddress *addr) g_return_val_if_fail(NM_IS_NDISC(ndisc), FALSE); priv = NM_NDISC_GET_PRIVATE(ndisc); - if (priv->addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) { - if (!nm_utils_ipv6_addr_set_stable_privacy_may_fail(priv->stable_type, + if (priv->config.addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) { + if (!nm_utils_ipv6_addr_set_stable_privacy_may_fail(priv->config.stable_type, &addr->address, - priv->ifname, - priv->network_id, + priv->config.ifname, + priv->config.network_id, addr->dad_counter++, &error)) { _LOGW("complete-address: failed to generate an stable-privacy address: %s", @@ -608,7 +592,7 @@ nm_ndisc_add_address(NMNDisc * ndisc, * what the kernel does, because it considers *all* addresses (including * static and other temporary addresses). **/ - if (rdata->addresses->len >= priv->max_addresses) + if (rdata->addresses->len >= priv->config.max_addresses) return FALSE; if (new_item->expiry_msec <= now_msec) @@ -899,7 +883,7 @@ solicit_timer_start(NMNDisc *ndisc) g_random_int() % ((guint32) (NM_NDISC_RFC4861_MAX_RTR_SOLICITATION_DELAY * 1000 / 4)); _LOGD("solicit: schedule sending first solicitation (of %d) in %.3f seconds", - priv->router_solicitations, + priv->config.router_solicitations, ((double) delay_msec) / 1000); priv->solicit_retransmit_time_msec = 0; @@ -997,30 +981,82 @@ announce_router_solicited(NMNDisc *ndisc) /*****************************************************************************/ void -nm_ndisc_set_config(NMNDisc * ndisc, - const GArray *addresses, - const GArray *dns_servers, - const GArray *dns_domains) +nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd) { - gboolean changed = FALSE; - guint i; + gboolean changed = FALSE; + const struct in6_addr *in6arr; + const char *const * strvarr; + NMDedupMultiIter iter; + const NMPObject * obj; + guint len; + guint i; - for (i = 0; i < addresses->len; i++) { - if (nm_ndisc_add_address(ndisc, &g_array_index(addresses, NMNDiscAddress, i), 0, FALSE)) + nm_assert(NM_IS_NDISC(ndisc)); + nm_assert(nm_ndisc_get_node_type(ndisc) == NM_NDISC_NODE_TYPE_ROUTER); + + 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); + guint32 preferred; + guint32 lifetime; + NMNDiscAddress a; + + if (IN6_IS_ADDR_UNSPECIFIED(&addr->address) || IN6_IS_ADDR_LINKLOCAL(&addr->address)) + continue; + + if (addr->n_ifa_flags & IFA_F_TENTATIVE || addr->n_ifa_flags & IFA_F_DADFAILED) + continue; + + if (addr->plen != 64) + continue; + + lifetime = nmp_utils_lifetime_get(addr->timestamp, + addr->lifetime, + addr->preferred, + NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000, + &preferred); + if (!lifetime) + continue; + + a = (NMNDiscAddress){ + .address = addr->address, + .expiry_msec = _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, lifetime), + .expiry_preferred_msec = + _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, preferred), + }; + + if (nm_ndisc_add_address(ndisc, &a, 0, FALSE)) changed = TRUE; } - for (i = 0; i < dns_servers->len; i++) { - if (nm_ndisc_add_dns_server(ndisc, - &g_array_index(dns_servers, NMNDiscDNSServer, i), - G_MININT64)) + in6arr = NULL; + len = 0; + if (l3cd) + in6arr = nm_l3_config_data_get_nameservers(l3cd, AF_INET6, &len); + for (i = 0; i < len; i++) { + NMNDiscDNSServer n; + + n = (NMNDiscDNSServer){ + .address = in6arr[i], + .expiry_msec = _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, + NM_NDISC_ROUTER_LIFETIME), + }; + if (nm_ndisc_add_dns_server(ndisc, &n, G_MININT64)) changed = TRUE; } - for (i = 0; i < dns_domains->len; i++) { - if (nm_ndisc_add_dns_domain(ndisc, - &g_array_index(dns_domains, NMNDiscDNSDomain, i), - G_MININT64)) + strvarr = NULL; + len = 0; + if (l3cd) + strvarr = nm_l3_config_data_get_searches(l3cd, AF_INET6, &len); + for (i = 0; i < len; i++) { + NMNDiscDNSDomain n; + + n = (NMNDiscDNSDomain){ + .domain = (char *) strvarr[i], + .expiry_msec = _nm_ndisc_lifetime_to_expiry(NM_NDISC_EXPIRY_BASE_TIMESTAMP, + NM_NDISC_ROUTER_LIFETIME), + }; + if (nm_ndisc_add_dns_domain(ndisc, &n, G_MININT64)) changed = TRUE; } @@ -1061,7 +1097,7 @@ nm_ndisc_set_iid(NMNDisc *ndisc, const NMUtilsIPv6IfaceId iid) if (priv->iid.id != iid.id) { priv->iid = iid; - if (priv->addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) + if (priv->config.addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) return FALSE; if (rdata->addresses->len) { @@ -1100,26 +1136,26 @@ nm_ndisc_start(NMNDisc *ndisc) nm_assert(!priv->ra_timeout_source); _LOGD("starting neighbor discovery for ifindex %d%s", - priv->ifindex, - priv->node_type == NM_NDISC_NODE_TYPE_HOST ? " (solicit)" : " (announce)"); + nm_l3cfg_get_ifindex(priv->config.l3cfg), + priv->config.node_type == NM_NDISC_NODE_TYPE_HOST ? " (solicit)" : " (announce)"); if (!nm_ndisc_netns_push(ndisc, &netns)) return; NM_NDISC_GET_CLASS(ndisc)->start(ndisc); - if (priv->node_type == NM_NDISC_NODE_TYPE_HOST) { + if (priv->config.node_type == NM_NDISC_NODE_TYPE_HOST) { G_STATIC_ASSERT_EXPR(NM_RA_TIMEOUT_DEFAULT == 0); G_STATIC_ASSERT_EXPR(NM_RA_TIMEOUT_INFINITY == G_MAXINT32); - nm_assert(priv->ra_timeout > 0u); - nm_assert(priv->ra_timeout <= NM_RA_TIMEOUT_INFINITY); + nm_assert(priv->config.ra_timeout > 0u); + nm_assert(priv->config.ra_timeout <= NM_RA_TIMEOUT_INFINITY); - if (priv->ra_timeout < NM_RA_TIMEOUT_INFINITY) { + if (priv->config.ra_timeout < NM_RA_TIMEOUT_INFINITY) { guint timeout_msec; - _LOGD("scheduling RA timeout in %u seconds", priv->ra_timeout); - if (priv->ra_timeout < G_MAXUINT / 1000u) - timeout_msec = priv->ra_timeout * 1000u; + _LOGD("scheduling RA timeout in %u seconds", priv->config.ra_timeout); + if (priv->config.ra_timeout < G_MAXUINT / 1000u) + timeout_msec = priv->config.ra_timeout * 1000u; else timeout_msec = G_MAXUINT; priv->ra_timeout_source = nm_g_timeout_add_source(timeout_msec, ra_timeout_cb, ndisc); @@ -1129,8 +1165,8 @@ nm_ndisc_start(NMNDisc *ndisc) return; } - nm_assert(priv->ra_timeout == 0u); - nm_assert(priv->node_type == NM_NDISC_NODE_TYPE_ROUTER); + nm_assert(priv->config.ra_timeout == 0u); + nm_assert(priv->config.node_type == NM_NDISC_NODE_TYPE_ROUTER); announce_router_initial(ndisc); } @@ -1147,7 +1183,7 @@ nm_ndisc_stop(NMNDisc *ndisc) nm_assert(NM_NDISC_GET_CLASS(ndisc)->stop); - _LOGD("stopping neighbor discovery for ifindex %d", priv->ifindex); + _LOGD("stopping neighbor discovery for ifindex %d", nm_l3cfg_get_ifindex(priv->config.l3cfg)); if (!nm_ndisc_netns_push(ndisc, &netns)) return; @@ -1374,7 +1410,7 @@ clean_addresses(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint g_array_set_size(rdata->addresses, j); } - if (_array_set_size_max(rdata->gateways, priv->max_addresses)) + if (_array_set_size_max(rdata->gateways, priv->config.max_addresses)) *changed |= NM_NDISC_CONFIG_ADDRESSES; } @@ -1699,86 +1735,88 @@ nm_ndisc_get_sysctl(NMPlatform *platform, /*****************************************************************************/ +static void +_config_clear(NMNDiscConfig *config) +{ + g_clear_object(&config->l3cfg); + nm_clear_g_free((gpointer *) &config->ifname); + nm_clear_g_free((gpointer *) &config->network_id); +} + +static void +_config_init(NMNDiscConfig *config, const NMNDiscConfig *src) +{ + nm_assert(config); + g_return_if_fail(src); + + /* we only allow to set @config if it was cleared (or is not yet initialized). */ + nm_assert(!config->l3cfg); + nm_assert(!config->ifname); + nm_assert(!config->network_id); + + g_return_if_fail(NM_IS_L3CFG(src->l3cfg)); + + *config = *src; + + g_object_ref(config->l3cfg); + config->ifname = g_strdup(config->ifname); + config->network_id = g_strdup(config->network_id); + + if (config->max_addresses <= 0) + config->max_addresses = _SIZE_MAX_ADDRESSES; + else if (config->max_addresses > 3u * _SIZE_MAX_ADDRESSES) + config->max_addresses = 3u * _SIZE_MAX_ADDRESSES; + + /* This setter is only used in specific circumstances, and in this case, + * we expect that @src only contains valid settings. We thus assert that to + * be the case.*/ + g_return_if_fail(config->ifname && config->ifname[0]); + g_return_if_fail(config->network_id); + g_return_if_fail(config->stable_type >= NM_UTILS_STABLE_TYPE_UUID + && config->stable_type <= NM_UTILS_STABLE_TYPE_RANDOM); + g_return_if_fail(config->addr_gen_mode >= NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 + && config->addr_gen_mode + <= NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY); + nm_assert(config->max_addresses >= 0 && config->max_addresses <= G_MAXINT32); + G_STATIC_ASSERT_EXPR(G_MAXINT32 == NM_RA_TIMEOUT_INFINITY); + g_return_if_fail(config->ra_timeout <= NM_RA_TIMEOUT_INFINITY); + g_return_if_fail(config->router_solicitations > 0 + && config->router_solicitations <= G_MAXINT32); + g_return_if_fail(config->router_solicitation_interval > 0 + && config->router_solicitation_interval <= G_MAXINT32); + g_return_if_fail( + NM_IN_SET(config->node_type, NM_NDISC_NODE_TYPE_HOST, NM_NDISC_NODE_TYPE_ROUTER)); + g_return_if_fail(NM_IN_SET(config->ip6_privacy, + NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR)); +} + +/*****************************************************************************/ + static void dns_domain_free(gpointer data) { g_free(((NMNDiscDNSDomain *) (data))->domain); } +/*****************************************************************************/ + static void set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMNDisc * self = NM_NDISC(object); NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE(self); - int i; switch (prop_id) { - case PROP_PLATFORM: + case PROP_CONFIG: /* construct-only */ - priv->platform = g_value_get_object(value) ?: NM_PLATFORM_GET; - if (!priv->platform) - g_return_if_reached(); - - g_object_ref(priv->platform); - - priv->netns = nm_platform_netns_get(priv->platform); - if (priv->netns) - g_object_ref(priv->netns); + _config_init(&priv->config_, g_value_get_pointer(value)); + priv->netns = + nm_g_object_ref(nm_platform_netns_get(nm_l3cfg_get_platform(priv->config.l3cfg))); g_return_if_fail(!priv->netns || priv->netns == nmp_netns_get_current()); break; - case PROP_IFINDEX: - /* construct-only */ - priv->ifindex = g_value_get_int(value); - g_return_if_fail(priv->ifindex > 0); - break; - case PROP_IFNAME: - /* construct-only */ - priv->ifname = g_value_dup_string(value); - g_return_if_fail(priv->ifname && priv->ifname[0]); - break; - case PROP_STABLE_TYPE: - /* construct-only */ - priv->stable_type = g_value_get_int(value); - break; - case PROP_NETWORK_ID: - /* construct-only */ - priv->network_id = g_value_dup_string(value); - g_return_if_fail(priv->network_id); - break; - case PROP_ADDR_GEN_MODE: - /* construct-only */ - priv->addr_gen_mode = g_value_get_int(value); - break; - case PROP_MAX_ADDRESSES: - /* construct-only */ - i = g_value_get_int(value); - nm_assert(i >= 0); - priv->max_addresses = i; - - if (priv->max_addresses <= 0) - priv->max_addresses = _SIZE_MAX_ADDRESSES; - else if (priv->max_addresses > 3u * _SIZE_MAX_ADDRESSES) - priv->max_addresses = 3u * _SIZE_MAX_ADDRESSES; - break; - case PROP_RA_TIMEOUT: - /* construct-only */ - priv->ra_timeout = g_value_get_uint(value); - nm_assert(priv->ra_timeout <= NM_RA_TIMEOUT_INFINITY); - break; - case PROP_ROUTER_SOLICITATIONS: - /* construct-only */ - priv->router_solicitations = g_value_get_int(value); - break; - case PROP_ROUTER_SOLICITATION_INTERVAL: - /* construct-only */ - priv->router_solicitation_interval = g_value_get_int(value); - break; - case PROP_NODE_TYPE: - /* construct-only */ - priv->node_type = g_value_get_int(value); - nm_assert(NM_IN_SET(priv->node_type, NM_NDISC_NODE_TYPE_HOST, NM_NDISC_NODE_TYPE_ROUTER)); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -1828,9 +1866,6 @@ finalize(GObject *object) NMNDiscPrivate * priv = NM_NDISC_GET_PRIVATE(ndisc); NMNDiscDataInternal *rdata = &priv->rdata; - g_free(priv->ifname); - g_free(priv->network_id); - g_array_unref(rdata->gateways); g_array_unref(rdata->addresses); g_array_unref(rdata->routes); @@ -1838,7 +1873,10 @@ finalize(GObject *object) g_array_unref(rdata->dns_domains); g_clear_object(&priv->netns); - g_clear_object(&priv->platform); + + _config_clear(&priv->config_); + + nm_clear_l3cd(&priv->l3cd); G_OBJECT_CLASS(nm_ndisc_parent_class)->finalize(object); } @@ -1854,89 +1892,12 @@ nm_ndisc_class_init(NMNDiscClass *klass) object_class->dispose = dispose; object_class->finalize = finalize; - obj_properties[PROP_PLATFORM] = - g_param_spec_object(NM_NDISC_PLATFORM, - "", - "", - NM_TYPE_PLATFORM, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_IFINDEX] = - g_param_spec_int(NM_NDISC_IFINDEX, - "", - "", - 0, - G_MAXINT, - 0, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_IFNAME] = - g_param_spec_string(NM_NDISC_IFNAME, - "", - "", - NULL, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_STABLE_TYPE] = - g_param_spec_int(NM_NDISC_STABLE_TYPE, - "", - "", - NM_UTILS_STABLE_TYPE_UUID, - NM_UTILS_STABLE_TYPE_RANDOM, - NM_UTILS_STABLE_TYPE_UUID, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_NETWORK_ID] = - g_param_spec_string(NM_NDISC_NETWORK_ID, - "", - "", - NULL, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_ADDR_GEN_MODE] = - g_param_spec_int(NM_NDISC_ADDR_GEN_MODE, - "", - "", - NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64, - NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY, - NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_MAX_ADDRESSES] = - g_param_spec_int(NM_NDISC_MAX_ADDRESSES, - "", - "", - 0, - G_MAXINT32, - NM_NDISC_MAX_ADDRESSES_DEFAULT, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - G_STATIC_ASSERT_EXPR(G_MAXINT32 == NM_RA_TIMEOUT_INFINITY); - obj_properties[PROP_RA_TIMEOUT] = - g_param_spec_uint(NM_NDISC_RA_TIMEOUT, - "", - "", - 0, - G_MAXINT32, - 0, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_ROUTER_SOLICITATIONS] = - g_param_spec_int(NM_NDISC_ROUTER_SOLICITATIONS, - "", - "", - 1, - G_MAXINT32, - NM_NDISC_ROUTER_SOLICITATIONS_DEFAULT, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_ROUTER_SOLICITATION_INTERVAL] = - g_param_spec_int(NM_NDISC_ROUTER_SOLICITATION_INTERVAL, - "", - "", - 1, - G_MAXINT32, - NM_NDISC_RFC4861_RTR_SOLICITATION_INTERVAL, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_NODE_TYPE] = - g_param_spec_int(NM_NDISC_NODE_TYPE, - "", - "", - NM_NDISC_NODE_TYPE_INVALID, - NM_NDISC_NODE_TYPE_ROUTER, - NM_NDISC_NODE_TYPE_INVALID, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_CONFIG] = + g_param_spec_pointer(NM_NDISC_CONFIG, + "", + "", + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); signals[CONFIG_RECEIVED] = g_signal_new(NM_NDISC_CONFIG_RECEIVED, @@ -1947,9 +1908,14 @@ nm_ndisc_class_init(NMNDiscClass *klass) NULL, NULL, G_TYPE_NONE, - 2, - G_TYPE_POINTER, - G_TYPE_UINT); + 3, + G_TYPE_POINTER + /* (const NMNDiscData *)rdata */, + G_TYPE_UINT + /* (guint) changed_i */, + G_TYPE_POINTER + /* (const NML3ConfigData *) l3cd */ + ); signals[RA_TIMEOUT_SIGNAL] = g_signal_new(NM_NDISC_RA_TIMEOUT_SIGNAL, G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, diff --git a/src/core/ndisc/nm-ndisc.h b/src/core/ndisc/nm-ndisc.h index 5b82752815..90f574a90b 100644 --- a/src/core/ndisc/nm-ndisc.h +++ b/src/core/ndisc/nm-ndisc.h @@ -26,17 +26,7 @@ #define NM_IS_NDISC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_NDISC)) #define NM_NDISC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_NDISC, NMNDiscClass)) -#define NM_NDISC_PLATFORM "platform" -#define NM_NDISC_IFINDEX "ifindex" -#define NM_NDISC_IFNAME "ifname" -#define NM_NDISC_NETWORK_ID "network-id" -#define NM_NDISC_ADDR_GEN_MODE "addr-gen-mode" -#define NM_NDISC_STABLE_TYPE "stable-type" -#define NM_NDISC_NODE_TYPE "node-type" -#define NM_NDISC_MAX_ADDRESSES "max-addresses" -#define NM_NDISC_RA_TIMEOUT "ra-timeout" -#define NM_NDISC_ROUTER_SOLICITATIONS "router-solicitations" -#define NM_NDISC_ROUTER_SOLICITATION_INTERVAL "router-solicitation-interval" +#define NM_NDISC_CONFIG "config" #define NM_NDISC_CONFIG_RECEIVED "config-received" #define NM_NDISC_RA_TIMEOUT_SIGNAL "ra-timeout-signal" @@ -169,6 +159,20 @@ typedef enum { #define NM_NDISC_ROUTER_ADVERT_MAX_INTERVAL 600 /* RFC4861, MaxRtrAdvInterval default */ #define NM_NDISC_ROUTER_LIFETIME 900 /* 1.5 * NM_NDISC_ROUTER_ADVERT_MAX_INTERVAL */ +typedef struct { + NML3Cfg * l3cfg; + const char * ifname; + const char * network_id; + int max_addresses; + int router_solicitations; + int router_solicitation_interval; + guint32 ra_timeout; + NMUtilsStableType stable_type; + NMSettingIP6ConfigAddrGenMode addr_gen_mode; + NMNDiscNodeType node_type; + NMSettingIP6ConfigPrivacy ip6_privacy; +} NMNDiscConfig; + struct _NMNDiscPrivate; struct _NMNDiscDataInternal; @@ -217,8 +221,6 @@ typedef struct { GType nm_ndisc_get_type(void); -void nm_ndisc_emit_config_change(NMNDisc *self, NMNDiscConfigMap changed); - int nm_ndisc_get_ifindex(NMNDisc *self); const char * nm_ndisc_get_ifname(NMNDisc *self); NMNDiscNodeType nm_ndisc_get_node_type(NMNDisc *self); @@ -228,10 +230,7 @@ void nm_ndisc_start(NMNDisc *ndisc); void nm_ndisc_stop(NMNDisc *ndisc); NMNDiscConfigMap nm_ndisc_dad_failed(NMNDisc *ndisc, const struct in6_addr *address, gboolean emit_changed_signal); -void nm_ndisc_set_config(NMNDisc * ndisc, - const GArray *addresses, - const GArray *dns_servers, - const GArray *dns_domains); +void nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd); NMPlatform *nm_ndisc_get_platform(NMNDisc *self); NMPNetns * nm_ndisc_netns_get(NMNDisc *self); @@ -280,10 +279,6 @@ struct _NML3ConfigData; struct _NML3ConfigData *nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, int ifindex, const NMNDiscData * rdata, - NMSettingIP6ConfigPrivacy ip6_privacy, - guint32 route_table, - guint32 route_metric, - gboolean kernel_support_rta_pref, - gboolean kernel_support_extended_ifa_flags); + NMSettingIP6ConfigPrivacy ip6_privacy); #endif /* __NETWORKMANAGER_NDISC_H__ */ diff --git a/src/core/ndisc/tests/test-ndisc-fake.c b/src/core/ndisc/tests/test-ndisc-fake.c index f201bc5641..8cfb9ddc16 100644 --- a/src/core/ndisc/tests/test-ndisc-fake.c +++ b/src/core/ndisc/tests/test-ndisc-fake.c @@ -11,23 +11,28 @@ #include "ndisc/nm-fake-ndisc.h" #include "platform/nm-fake-platform.h" +#include "nm-netns.h" -#include "nm-test-utils-core.h" +#include "platform/tests/test-common.h" /*****************************************************************************/ static NMFakeNDisc * ndisc_new(void) { - NMNDisc * ndisc; - const int ifindex = 1; - const char * ifname = nm_platform_link_get_name(NM_PLATFORM_GET, ifindex); - NMUtilsIPv6IfaceId iid = {}; + gs_unref_object NML3Cfg *l3cfg = NULL; + NMNDisc * ndisc; + const int ifindex = 1; + NMUtilsIPv6IfaceId iid; - ndisc = nm_fake_ndisc_new(ifindex, ifname); + l3cfg = nm_netns_l3cfg_acquire(NM_NETNS_GET, ifindex); + + ndisc = nm_fake_ndisc_new(l3cfg); + g_assert(ndisc); + + memset(&iid, 0, sizeof(iid)); iid.id_u8[7] = 1; nm_ndisc_set_iid(ndisc, iid); - g_assert(ndisc); return NM_FAKE_NDISC(ndisc); } @@ -140,9 +145,15 @@ typedef struct { /*****************************************************************************/ static void -test_simple_changed(NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_int, TestData *data) +test_simple_changed(NMNDisc * ndisc, + const NMNDiscData * rdata, + guint changed_i, + const NML3ConfigData *l3cd, + TestData * data) { - NMNDiscConfigMap changed = changed_int; + NMNDiscConfigMap changed = changed_i; + + _LOGT("test_simple: callback (counter=%u)", data->counter); switch (data->counter++) { case 0: @@ -206,9 +217,11 @@ test_simple(void) g_signal_connect(ndisc, NM_NDISC_CONFIG_RECEIVED, G_CALLBACK(test_simple_changed), &data); + _LOGT("test_simple: start"); nm_ndisc_start(NM_NDISC(ndisc)); nmtst_main_loop_run_assert(data.loop, 15000); g_assert_cmpint(data.counter, ==, 2); + _LOGT("test_simple: done"); } /*****************************************************************************/ @@ -221,9 +234,13 @@ test_everything_rs_sent(NMNDisc *ndisc, TestData *data) } static void -test_everything_changed(NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_int, TestData *data) +test_everything_changed(NMNDisc * ndisc, + const NMNDiscData * rdata, + guint changed_i, + const NML3ConfigData *l3cd, + TestData * data) { - NMNDiscConfigMap changed = changed_int; + NMNDiscConfigMap changed = changed_i; if (data->counter == 0) { g_assert_cmpint(data->rs_counter, ==, 1); @@ -342,12 +359,13 @@ test_everything(void) } static void -test_preference_order_cb(NMNDisc * ndisc, - const NMNDiscData *rdata, - guint changed_int, - TestData * data) +test_preference_order_cb(NMNDisc * ndisc, + const NMNDiscData * rdata, + guint changed_i, + const NML3ConfigData *l3cd, + TestData * data) { - NMNDiscConfigMap changed = changed_int; + NMNDiscConfigMap changed = changed_i; if (data->counter == 1) { g_assert_cmpint(changed, @@ -434,12 +452,13 @@ test_preference_order(void) } static void -test_preference_changed_cb(NMNDisc * ndisc, - const NMNDiscData *rdata, - guint changed_int, - TestData * data) +test_preference_changed_cb(NMNDisc * ndisc, + const NMNDiscData * rdata, + guint changed_i, + const NML3ConfigData *l3cd, + TestData * data) { - NMNDiscConfigMap changed = changed_int; + NMNDiscConfigMap changed = changed_i; if (data->counter == 1) { g_assert_cmpint(changed, @@ -576,10 +595,11 @@ test_preference_changed(void) /*****************************************************************************/ static void -_test_dns_solicit_loop_changed(NMNDisc * ndisc, - const NMNDiscData *rdata, - guint changed_int, - TestData * data) +_test_dns_solicit_loop_changed(NMNDisc * ndisc, + const NMNDiscData * rdata, + guint changed_i, + const NML3ConfigData *l3cd, + TestData * data) { data->counter++; } diff --git a/src/core/ndisc/tests/test-ndisc-linux.c b/src/core/ndisc/tests/test-ndisc-linux.c index 57021606d3..a4db5efaa5 100644 --- a/src/core/ndisc/tests/test-ndisc-linux.c +++ b/src/core/ndisc/tests/test-ndisc-linux.c @@ -11,6 +11,8 @@ #include "ndisc/nm-lndp-ndisc.h" #include "libnm-platform/nm-linux-platform.h" +#include "nm-netns.h" +#include "nm-l3cfg.h" #include "nm-test-utils-core.h" @@ -19,16 +21,18 @@ NMTST_DEFINE(); int main(int argc, char **argv) { - GMainLoop * loop; - NMNDisc * ndisc; - int ifindex = 1; - const char * ifname; - NMUtilsIPv6IfaceId iid = {}; - GError * error = NULL; - int max_addresses; - int router_solicitations; - int router_solicitation_interval; - guint32 ra_timeout; + gs_unref_object NML3Cfg *l3cfg = NULL; + NMNDiscConfig config; + GMainLoop * loop; + NMNDisc * ndisc; + int ifindex = 1; + const char * ifname; + NMUtilsIPv6IfaceId iid = {}; + GError * error = NULL; + int max_addresses; + int router_solicitations; + int router_solicitation_interval; + guint32 ra_timeout; nmtst_init_with_logging(&argc, &argv, NULL, "DEFAULT"); @@ -56,18 +60,23 @@ main(int argc, char **argv) &router_solicitation_interval, &ra_timeout); - ndisc = nm_lndp_ndisc_new(NM_PLATFORM_GET, - ifindex, - ifname, - NM_UTILS_STABLE_TYPE_UUID, - "8ce666e8-d34d-4fb1-b858-f15a7al28086", - NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64, - NM_NDISC_NODE_TYPE_HOST, - max_addresses, - router_solicitations, - router_solicitation_interval, - ra_timeout, - &error); + l3cfg = nm_netns_l3cfg_acquire(NM_NETNS_GET, ifindex); + + config = (NMNDiscConfig){ + .l3cfg = l3cfg, + .ifname = nm_l3cfg_get_ifname(l3cfg, TRUE), + .stable_type = NM_UTILS_STABLE_TYPE_UUID, + .network_id = "8ce666e8-d34d-4fb1-b858-f15a7al28086", + .addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64, + .node_type = NM_NDISC_NODE_TYPE_HOST, + .max_addresses = max_addresses, + .router_solicitations = router_solicitations, + .router_solicitation_interval = router_solicitation_interval, + .ra_timeout = ra_timeout, + .ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, + }; + + ndisc = nm_lndp_ndisc_new(&config); if (!ndisc) { g_print("Failed to create NMNDisc instance: %s\n", error->message); g_error_free(error); diff --git a/src/core/nm-act-request.c b/src/core/nm-act-request.c index d0a2e0c783..58dd4a87f8 100644 --- a/src/core/nm-act-request.c +++ b/src/core/nm-act-request.c @@ -24,8 +24,7 @@ #include "settings/nm-settings-connection.h" typedef struct { - CList call_ids_lst_head; - NMFirewallConfig *firewall_config; + CList call_ids_lst_head; } NMActRequestPrivate; struct _NMActRequest { @@ -250,36 +249,6 @@ nm_act_request_clear_secrets(NMActRequest *self) /*****************************************************************************/ -NMFirewallConfig * -nm_act_request_get_shared(NMActRequest *req) -{ - g_return_val_if_fail(NM_IS_ACT_REQUEST(req), FALSE); - - return NM_ACT_REQUEST_GET_PRIVATE(req)->firewall_config; -} - -void -nm_act_request_set_shared(NMActRequest *req, NMFirewallConfig *rules) -{ - NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE(req); - - g_return_if_fail(NM_IS_ACT_REQUEST(req)); - - if (priv->firewall_config == rules) - return; - - if (priv->firewall_config) { - nm_firewall_config_apply(priv->firewall_config, FALSE); - priv->firewall_config = NULL; - } - if (rules) { - priv->firewall_config = rules; - nm_firewall_config_apply(priv->firewall_config, TRUE); - } -} - -/*****************************************************************************/ - static void device_notify(GObject *object, GParamSpec *pspec, gpointer self) { @@ -508,11 +477,6 @@ dispose(GObject *object) c_list_for_each_entry_safe (call_id, call_id_safe, &priv->call_ids_lst_head, call_ids_lst) _do_cancel_secrets(self, call_id, TRUE); - if (priv->firewall_config) { - nm_firewall_config_apply(priv->firewall_config, FALSE); - nm_clear_pointer(&priv->firewall_config, nm_firewall_config_free); - } - G_OBJECT_CLASS(nm_act_request_parent_class)->dispose(object); } diff --git a/src/core/nm-act-request.h b/src/core/nm-act-request.h index 9c702d31eb..698a9985a4 100644 --- a/src/core/nm-act-request.h +++ b/src/core/nm-act-request.h @@ -38,14 +38,6 @@ NMConnection *nm_act_request_get_applied_connection(NMActRequest *req); /*****************************************************************************/ -struct _NMFirewallConfig; - -struct _NMFirewallConfig *nm_act_request_get_shared(NMActRequest *req); - -void nm_act_request_set_shared(NMActRequest *req, struct _NMFirewallConfig *rules); - -/*****************************************************************************/ - /* Secrets handling */ typedef void (*NMActRequestSecretsFunc)(NMActRequest * req, diff --git a/src/core/nm-active-connection.h b/src/core/nm-active-connection.h index 15c68e6e7e..1c2c8116ba 100644 --- a/src/core/nm-active-connection.h +++ b/src/core/nm-active-connection.h @@ -138,6 +138,9 @@ void nm_active_connection_set_state_fail(NMActiveConnection * active, NMActiveConnectionStateReason reason, const char * error_desc); +#define NM_ACTIVATION_STATE_FLAG_IP_READY_X(IS_IPv4) \ + ((IS_IPv4) ? NM_ACTIVATION_STATE_FLAG_IP4_READY : NM_ACTIVATION_STATE_FLAG_IP6_READY) + NMActivationStateFlags nm_active_connection_get_state_flags(NMActiveConnection *self); void nm_active_connection_set_state_flags_full(NMActiveConnection * self, diff --git a/src/core/nm-config-data.c b/src/core/nm-config-data.c index 1cb8a43c32..0c4f8099de 100644 --- a/src/core/nm-config-data.c +++ b/src/core/nm-config-data.c @@ -929,59 +929,57 @@ nm_global_dns_config_is_empty(const NMGlobalDnsConfig *dns_config) return !dns_config->searches && !dns_config->options && !dns_config->domain_list; } -void -nm_global_dns_config_update_checksum(const NMGlobalDnsConfig *dns_config, GChecksum *sum) +int +nm_global_dns_config_cmp(const NMGlobalDnsConfig *a, + const NMGlobalDnsConfig *b, + gboolean check_internal) { - NMGlobalDnsDomain *domain; - guint i, j; - guint8 v8; + guint i; - g_return_if_fail(dns_config); - g_return_if_fail(sum); + NM_CMP_SELF(a, b); - v8 = NM_HASH_COMBINE_BOOLS(guint8, - !dns_config->searches, - !dns_config->options, - !dns_config->domain_list); - g_checksum_update(sum, (guchar *) &v8, 1); + NM_CMP_RETURN( + nm_strv_cmp_n(a->searches ?: NM_STRV_EMPTY(), -1, b->searches ?: NM_STRV_EMPTY(), -1)); - if (dns_config->searches) { - for (i = 0; dns_config->searches[i]; i++) - g_checksum_update(sum, - (guchar *) dns_config->searches[i], - strlen(dns_config->searches[i]) + 1); - } - if (dns_config->options) { - for (i = 0; dns_config->options[i]; i++) - g_checksum_update(sum, - (guchar *) dns_config->options[i], - strlen(dns_config->options[i]) + 1); - } + NM_CMP_RETURN( + nm_strv_cmp_n(a->options ?: NM_STRV_EMPTY(), -1, b->options ?: NM_STRV_EMPTY(), -1)); - if (dns_config->domain_list) { - for (i = 0; dns_config->domain_list[i]; i++) { - domain = g_hash_table_lookup(dns_config->domains, dns_config->domain_list[i]); - nm_assert(domain); + NM_CMP_RETURN(nm_strv_cmp_n(a->domain_list ?: NM_STRV_EMPTY_CC(), + -1, + b->domain_list ?: NM_STRV_EMPTY_CC(), + -1)); - v8 = NM_HASH_COMBINE_BOOLS(guint8, !domain->servers, !domain->options); - g_checksum_update(sum, (guchar *) &v8, 1); + if (a->domain_list) { + for (i = 0; a->domain_list[i]; i++) { + const NMGlobalDnsDomain *domain_a; + const NMGlobalDnsDomain *domain_b; - g_checksum_update(sum, (guchar *) domain->name, strlen(domain->name) + 1); + nm_assert(nm_streq(a->domain_list[i], b->domain_list[i])); - if (domain->servers) { - for (j = 0; domain->servers[j]; j++) - g_checksum_update(sum, - (guchar *) domain->servers[j], - strlen(domain->servers[j]) + 1); - } - if (domain->options) { - for (j = 0; domain->options[j]; j++) - g_checksum_update(sum, - (guchar *) domain->options[j], - strlen(domain->options[j]) + 1); - } + domain_a = g_hash_table_lookup(a->domains, a->domain_list[i]); + nm_assert(domain_a); + + domain_b = g_hash_table_lookup(b->domains, b->domain_list[i]); + nm_assert(domain_b); + + NM_CMP_FIELD_STR0(domain_a, domain_b, name); + + NM_CMP_RETURN(nm_strv_cmp_n(domain_a->servers ?: NM_STRV_EMPTY(), + -1, + domain_b->servers ?: NM_STRV_EMPTY(), + -1)); + + NM_CMP_RETURN(nm_strv_cmp_n(domain_a->options ?: NM_STRV_EMPTY(), + -1, + domain_b->options ?: NM_STRV_EMPTY(), + -1)); } } + + if (check_internal) + NM_CMP_FIELD(a, b, internal); + + return 0; } static void diff --git a/src/core/nm-config-data.h b/src/core/nm-config-data.h index 92a7cb5c6f..89c69e2877 100644 --- a/src/core/nm-config-data.h +++ b/src/core/nm-config-data.h @@ -264,8 +264,10 @@ const char *const *nm_global_dns_domain_get_servers(const NMGlobalDnsDomain *dom const char *const *nm_global_dns_domain_get_options(const NMGlobalDnsDomain *domain); gboolean nm_global_dns_config_is_internal(const NMGlobalDnsConfig *dns_config); gboolean nm_global_dns_config_is_empty(const NMGlobalDnsConfig *dns_config); -void nm_global_dns_config_update_checksum(const NMGlobalDnsConfig *dns_config, GChecksum *sum); -void nm_global_dns_config_free(NMGlobalDnsConfig *dns_config); +int nm_global_dns_config_cmp(const NMGlobalDnsConfig *a, + const NMGlobalDnsConfig *b, + gboolean check_internal); +void nm_global_dns_config_free(NMGlobalDnsConfig *dns_config); NMGlobalDnsConfig *nm_global_dns_config_from_dbus(const GValue *value, GError **error); void nm_global_dns_config_to_dbus(const NMGlobalDnsConfig *dns_config, GValue *value); diff --git a/src/core/nm-config.c b/src/core/nm-config.c index c41f582329..f431365c59 100644 --- a/src/core/nm-config.c +++ b/src/core/nm-config.c @@ -529,7 +529,7 @@ nm_config_cmd_line_options_free(NMConfigCmdLineOptions *cli) static NMConfigConfigureAndQuitType string_to_configure_and_quit(const char *value, GError **error) { - NMConfigConfigureAndQuitType ret; + int v_bool; if (value == NULL) return NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED; @@ -537,11 +537,18 @@ string_to_configure_and_quit(const char *value, GError **error) if (nm_streq(value, "initrd")) return NM_CONFIG_CONFIGURE_AND_QUIT_INITRD; - ret = nm_config_parse_boolean(value, NM_CONFIG_CONFIGURE_AND_QUIT_INVALID); - if (ret == NM_CONFIG_CONFIGURE_AND_QUIT_INVALID) - g_set_error(error, 1, 0, N_("'%s' is not valid"), value); + v_bool = nm_config_parse_boolean(value, -1); + if (v_bool == -1) { + nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, N_("'%s' is not valid"), value); + return NM_CONFIG_CONFIGURE_AND_QUIT_INVALID; + } - return ret; + if (v_bool) { + _LOGW("'[main].configure-and-quit=1' in \"NetworkManager.conf\" is no longer implemented " + "and has no effect"); + } + + return NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED; } static gboolean @@ -552,11 +559,7 @@ parse_configure_and_quit(const char *option_name, { NMConfigCmdLineOptions *cli = user_data; - if (value == NULL) - cli->configure_and_quit = NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED; - else - cli->configure_and_quit = string_to_configure_and_quit(value, error); - + cli->configure_and_quit = string_to_configure_and_quit(value, error); if (cli->configure_and_quit == NM_CONFIG_CONFIGURE_AND_QUIT_INVALID) { g_prefix_error(error, N_("Bad '%s' option: "), option_name); return FALSE; @@ -1383,16 +1386,13 @@ read_entire_config(const NMConfigCmdLineOptions *cli, switch (cli->configure_and_quit) { case NM_CONFIG_CONFIGURE_AND_QUIT_INVALID: - g_assert_not_reached(); + nm_assert_not_reached(); break; case NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED: - /* do nothing */ - break; - case NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED: - g_key_file_set_boolean(keyfile, - NM_CONFIG_KEYFILE_GROUP_MAIN, - "configure-and-quit", - TRUE); + g_key_file_set_string(keyfile, + NM_CONFIG_KEYFILE_GROUP_MAIN, + "configure-and-quit", + "no"); break; case NM_CONFIG_CONFIGURE_AND_QUIT_INITRD: g_key_file_set_string(keyfile, diff --git a/src/core/nm-config.h b/src/core/nm-config.h index 8279f4d264..04e159bf5e 100644 --- a/src/core/nm-config.h +++ b/src/core/nm-config.h @@ -42,8 +42,7 @@ typedef enum { typedef enum { NM_CONFIG_CONFIGURE_AND_QUIT_INVALID = -1, NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED = FALSE, - NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED = TRUE, - NM_CONFIG_CONFIGURE_AND_QUIT_INITRD, + NM_CONFIG_CONFIGURE_AND_QUIT_INITRD = 2, } NMConfigConfigureAndQuitType; typedef struct { diff --git a/src/core/nm-connectivity.c b/src/core/nm-connectivity.c index 13fb026514..230c4afe53 100644 --- a/src/core/nm-connectivity.c +++ b/src/core/nm-connectivity.c @@ -15,6 +15,7 @@ #include #include "c-list/src/c-list.h" +#include "libnm-platform/nmp-object.h" #include "libnm-core-intern/nm-core-internal.h" #include "nm-config.h" #include "NetworkManagerUtils.h" diff --git a/src/core/nm-dhcp-config.c b/src/core/nm-dhcp-config.c index 8a02911942..8a89173c0f 100644 --- a/src/core/nm-dhcp-config.c +++ b/src/core/nm-dhcp-config.c @@ -8,9 +8,11 @@ #include "nm-dhcp-config.h" #include "nm-dbus-interface.h" +#include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-dbus-object.h" #include "nm-core-utils.h" +#include "nm-l3-config-data.h" /*****************************************************************************/ @@ -46,10 +48,11 @@ static GType nm_dhcp6_config_get_type(void); /*****************************************************************************/ -NM_GOBJECT_PROPERTIES_DEFINE(NMDhcpConfig, PROP_OPTIONS, ); +NM_GOBJECT_PROPERTIES_DEFINE(NMDhcpConfig, PROP_L3CD, PROP_OPTIONS, ); typedef struct { - GVariant *options; + const NML3ConfigData *l3cd; + GVariant * options; } NMDhcpConfigPrivate; struct _NMDhcpConfig { @@ -76,18 +79,49 @@ nm_dhcp_config_get_addr_family(NMDhcpConfig *self) /*****************************************************************************/ -void -nm_dhcp_config_set_options(NMDhcpConfig *self, GHashTable *options) +static GVariant * +_l3cd_to_options(const NML3ConfigData *l3cd, int addr_family) { - NMDhcpConfigPrivate *priv; + GVariant *options; + + options = NULL; + if (l3cd) { + NMDhcpLease *lease; + + lease = nm_l3_config_data_get_dhcp_lease(l3cd, addr_family); + if (lease) + options = nm_strdict_to_variant_asv(nm_dhcp_lease_get_options(lease)); + } + if (!options) + options = nm_g_variant_singleton_aLsvI(); + return g_variant_ref_sink(options); +} + +void +nm_dhcp_config_set_lease(NMDhcpConfig *self, const NML3ConfigData *l3cd) +{ + nm_auto_unref_l3cd const NML3ConfigData *l3cd_old = NULL; + gs_unref_variant GVariant *options2 = NULL; + NMDhcpConfigPrivate * priv; g_return_if_fail(NM_IS_DHCP_CONFIG(self)); - g_return_if_fail(options); priv = NM_DHCP_CONFIG_GET_PRIVATE(self); - nm_g_variant_unref(priv->options); - priv->options = g_variant_ref_sink(nm_strdict_to_variant_asv(options)); + if (priv->l3cd == l3cd) + return; + + l3cd_old = g_steal_pointer(&priv->l3cd); + if (l3cd) + priv->l3cd = nm_l3_config_data_ref_and_seal(l3cd); + + options2 = _l3cd_to_options(priv->l3cd, nm_dhcp_config_get_addr_family(self)); + + if (g_variant_equal(priv->options, options2)) + return; + + NM_SWAP(&priv->options, &options2); + _notify(self, PROP_OPTIONS); } @@ -95,19 +129,22 @@ const char * nm_dhcp_config_get_option(NMDhcpConfig *self, const char *key) { NMDhcpConfigPrivate *priv; - const char * value; + NMDhcpLease * lease; g_return_val_if_fail(NM_IS_DHCP_CONFIG(self), NULL); g_return_val_if_fail(key, NULL); priv = NM_DHCP_CONFIG_GET_PRIVATE(self); - if (priv->options && g_variant_lookup(priv->options, key, "&s", &value)) - return value; - else + if (!priv->l3cd) return NULL; + + lease = nm_l3_config_data_get_dhcp_lease(priv->l3cd, nm_dhcp_config_get_addr_family(self)); + return nm_dhcp_lease_lookup_option(lease, key); } +/*****************************************************************************/ + GVariant * nm_dhcp_config_get_options(NMDhcpConfig *self) { @@ -125,7 +162,25 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) switch (prop_id) { case PROP_OPTIONS: - g_value_set_variant(value, priv->options ?: nm_g_variant_singleton_aLsvI()); + g_value_set_variant(value, priv->options); + 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) +{ + NMDhcpConfig * self = NM_DHCP_CONFIG(object); + NMDhcpConfigPrivate *priv = NM_DHCP_CONFIG_GET_PRIVATE(self); + + switch (prop_id) { + case PROP_L3CD: + /* construct-only */ + priv->l3cd = nm_l3_config_data_ref_and_seal(g_value_get_pointer(value)); + priv->options = _l3cd_to_options(priv->l3cd, nm_dhcp_config_get_addr_family(self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -140,11 +195,12 @@ nm_dhcp_config_init(NMDhcpConfig *self) {} NMDhcpConfig * -nm_dhcp_config_new(int addr_family) +nm_dhcp_config_new(int addr_family, const NML3ConfigData *l3cd) { - nm_assert_addr_family(addr_family); - - return g_object_new(addr_family != AF_INET ? NM_TYPE_DHCP6_CONFIG : NM_TYPE_DHCP4_CONFIG, NULL); + return g_object_new(NM_IS_IPv4(addr_family) ? NM_TYPE_DHCP4_CONFIG : NM_TYPE_DHCP6_CONFIG, + NM_DHCP_CONFIG_L3CD, + l3cd, + NULL); } static void @@ -152,7 +208,9 @@ finalize(GObject *object) { NMDhcpConfigPrivate *priv = NM_DHCP_CONFIG_GET_PRIVATE(object); - nm_g_variant_unref(priv->options); + g_variant_unref(priv->options); + + nm_l3_config_data_unref(priv->l3cd); G_OBJECT_CLASS(nm_dhcp_config_parent_class)->finalize(object); } @@ -163,8 +221,15 @@ nm_dhcp_config_class_init(NMDhcpConfigClass *config_class) GObjectClass *object_class = G_OBJECT_CLASS(config_class); object_class->get_property = get_property; + object_class->set_property = set_property; object_class->finalize = finalize; + obj_properties[PROP_L3CD] = + g_param_spec_pointer(NM_DHCP_CONFIG_L3CD, + "", + "", + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_OPTIONS] = g_param_spec_variant(NM_DHCP_CONFIG_OPTIONS, "", "", diff --git a/src/core/nm-dhcp-config.h b/src/core/nm-dhcp-config.h index 9e68f2fb3a..cae0e11aa2 100644 --- a/src/core/nm-dhcp-config.h +++ b/src/core/nm-dhcp-config.h @@ -18,16 +18,17 @@ (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DHCP_CONFIG, NMDhcpConfigClass)) #define NM_DHCP_CONFIG_OPTIONS "options" +#define NM_DHCP_CONFIG_L3CD "l3cd" typedef struct _NMDhcpConfigClass NMDhcpConfigClass; GType nm_dhcp_config_get_type(void); -NMDhcpConfig *nm_dhcp_config_new(int addr_family); +NMDhcpConfig *nm_dhcp_config_new(int addr_family, const NML3ConfigData *l3cd); int nm_dhcp_config_get_addr_family(NMDhcpConfig *self); -void nm_dhcp_config_set_options(NMDhcpConfig *self, GHashTable *options); +void nm_dhcp_config_set_lease(NMDhcpConfig *self, const NML3ConfigData *l3cd); const char *nm_dhcp_config_get_option(NMDhcpConfig *self, const char *option); diff --git a/src/core/nm-dispatcher.c b/src/core/nm-dispatcher.c index b77197863b..d68dab5b22 100644 --- a/src/core/nm-dispatcher.c +++ b/src/core/nm-dispatcher.c @@ -15,9 +15,7 @@ #include "nm-act-request.h" #include "devices/nm-device.h" #include "nm-dhcp-config.h" -#include "nm-proxy-config.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" +#include "nm-l3-config-data.h" #include "nm-manager.h" #include "settings/nm-settings-connection.h" #include "libnm-platform/nm-platform.h" @@ -148,50 +146,47 @@ _init_dispatcher(void) /*****************************************************************************/ static void -dump_proxy_to_props(NMProxyConfig *proxy, GVariantBuilder *builder) +dump_proxy_to_props(const NML3ConfigData *l3cd, GVariantBuilder *builder) { - const char *pac_url = NULL, *pac_script = NULL; + const char *s; - if (nm_proxy_config_get_method(proxy) == NM_PROXY_CONFIG_METHOD_NONE) + if (nm_l3_config_data_get_proxy_method(l3cd) != NM_PROXY_CONFIG_METHOD_AUTO) return; - pac_url = nm_proxy_config_get_pac_url(proxy); - if (pac_url) { - g_variant_builder_add(builder, "{sv}", "pac-url", g_variant_new_string(pac_url)); - } + s = nm_l3_config_data_get_proxy_pac_url(l3cd); + if (s) + g_variant_builder_add(builder, "{sv}", "pac-url", g_variant_new_string(s)); - pac_script = nm_proxy_config_get_pac_script(proxy); - if (pac_script) { - g_variant_builder_add(builder, "{sv}", "pac-script", g_variant_new_string(pac_script)); - } + s = nm_l3_config_data_get_proxy_pac_script(l3cd); + if (s) + g_variant_builder_add(builder, "{sv}", "pac-script", g_variant_new_string(s)); } static void -dump_ip_to_props(NMIPConfig *ip, GVariantBuilder *builder) +dump_ip_to_props(const NML3ConfigData *l3cd, int addr_family, GVariantBuilder *builder) { - const int addr_family = nm_ip_config_get_addr_family(ip); - const int IS_IPv4 = NM_IS_IPv4(addr_family); - const NMPObject *obj; - GVariantBuilder int_builder; - NMDedupMultiIter ipconf_iter; - GVariant * var1; - GVariant * var2; - guint n; - guint i; - const NMPObject *default_route; + const int IS_IPv4 = NM_IS_IPv4(addr_family); + const NMPObject * obj; + GVariantBuilder int_builder; + NMDedupMultiIter ipconf_iter; + GVariant * var1; + GVariant * var2; + guint n; + guint i; + 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")); else g_variant_builder_init(&int_builder, G_VARIANT_TYPE("a(ayuay)")); - default_route = nm_ip_config_best_default_route_get(ip); - if (IS_IPv4) - nm_ip_config_iter_ip4_address_init(&ipconf_iter, NM_IP4_CONFIG(ip)); - else - nm_ip_config_iter_ip6_address_init(&ipconf_iter, NM_IP6_CONFIG(ip)); - while (nm_platform_dedup_multi_iter_next_obj(&ipconf_iter, - &obj, - NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4))) { + default_route = nm_l3_config_data_get_best_default_route(l3cd, addr_family); + nm_l3_config_data_iter_obj_for_each (&ipconf_iter, + l3cd, + &obj, + NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)) { const NMPlatformIPXAddress *addr = NMP_OBJECT_CAST_IPX_ADDRESS(obj); if (IS_IPv4) { @@ -225,30 +220,28 @@ dump_ip_to_props(NMIPConfig *ip, GVariantBuilder *builder) g_variant_builder_init(&int_builder, G_VARIANT_TYPE("au")); else g_variant_builder_init(&int_builder, G_VARIANT_TYPE("aay")); - n = nm_ip_config_get_num_nameservers(ip); + iparr = nm_l3_config_data_get_nameservers(l3cd, addr_family, &n); for (i = 0; i < n; i++) { - if (IS_IPv4) { - g_variant_builder_add(&int_builder, - "u", - nm_ip4_config_get_nameserver(NM_IP4_CONFIG(ip), i)); - } else { - var1 = nm_g_variant_new_ay_in6addr(nm_ip6_config_get_nameserver(NM_IP6_CONFIG(ip), i)); + 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(builder, "{sv}", "nameservers", g_variant_builder_end(&int_builder)); g_variant_builder_init(&int_builder, G_VARIANT_TYPE("as")); - n = nm_ip_config_get_num_domains(ip); + strarr = nm_l3_config_data_get_domains(l3cd, addr_family, &n); for (i = 0; i < n; i++) - g_variant_builder_add(&int_builder, "s", nm_ip_config_get_domain(ip, i)); + g_variant_builder_add(&int_builder, "s", strarr[i]); g_variant_builder_add(builder, "{sv}", "domains", g_variant_builder_end(&int_builder)); if (IS_IPv4) { g_variant_builder_init(&int_builder, G_VARIANT_TYPE("au")); - n = nm_ip4_config_get_num_wins(NM_IP4_CONFIG(ip)); + ip4arr = nm_l3_config_data_get_wins(l3cd, &n); for (i = 0; i < n; i++) - g_variant_builder_add(&int_builder, "u", nm_ip4_config_get_wins(NM_IP4_CONFIG(ip), i)); + g_variant_builder_add(&int_builder, "u", ip4arr[i]); g_variant_builder_add(builder, "{sv}", "wins-servers", g_variant_builder_end(&int_builder)); } @@ -256,13 +249,10 @@ dump_ip_to_props(NMIPConfig *ip, GVariantBuilder *builder) g_variant_builder_init(&int_builder, G_VARIANT_TYPE("aau")); else g_variant_builder_init(&int_builder, G_VARIANT_TYPE("a(ayuayu)")); - if (IS_IPv4) - nm_ip_config_iter_ip4_route_init(&ipconf_iter, NM_IP4_CONFIG(ip)); - else - nm_ip_config_iter_ip6_route_init(&ipconf_iter, NM_IP6_CONFIG(ip)); - while (nm_platform_dedup_multi_iter_next_obj(&ipconf_iter, - &obj, - NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4))) { + nm_l3_config_data_iter_obj_for_each (&ipconf_iter, + l3cd, + &obj, + NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)) { const NMPlatformIPXRoute *route = NMP_OBJECT_CAST_IPX_ROUTE(obj); if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route)) @@ -299,10 +289,8 @@ fill_device_props(NMDevice * device, GVariant ** dhcp4_props, GVariant ** dhcp6_props) { - NMProxyConfig *proxy_config; - NMIP4Config * ip4_config; - NMIP6Config * ip6_config; - NMDhcpConfig * dhcp_config; + const NML3ConfigData *l3cd; + NMDhcpConfig * dhcp_config; /* If the action is for a VPN, send the VPN's IP interface instead of the device's */ g_variant_builder_add(dev_builder, @@ -329,17 +317,12 @@ fill_device_props(NMDevice * device, g_variant_new_object_path(nm_dbus_object_get_path(NM_DBUS_OBJECT(device)))); } - proxy_config = nm_device_get_proxy_config(device); - if (proxy_config) - dump_proxy_to_props(proxy_config, proxy_builder); - - ip4_config = nm_device_get_ip4_config(device); - if (ip4_config) - dump_ip_to_props(NM_IP_CONFIG(ip4_config), ip4_builder); - - ip6_config = nm_device_get_ip6_config(device); - if (ip6_config) - dump_ip_to_props(NM_IP_CONFIG(ip6_config), ip6_builder); + l3cd = nm_device_get_l3cd(device, TRUE); + if (l3cd) { + dump_ip_to_props(l3cd, AF_INET, ip4_builder); + dump_ip_to_props(l3cd, AF_INET6, ip6_builder); + dump_proxy_to_props(l3cd, proxy_builder); + } dhcp_config = nm_device_get_dhcp_config(device, AF_INET); if (dhcp_config) @@ -351,19 +334,16 @@ fill_device_props(NMDevice * device, } static void -fill_vpn_props(NMProxyConfig * proxy_config, - NMIP4Config * ip4_config, - NMIP6Config * ip6_config, - GVariantBuilder *proxy_builder, - GVariantBuilder *ip4_builder, - GVariantBuilder *ip6_builder) +fill_vpn_props(const NML3ConfigData *l3cd, + GVariantBuilder * proxy_builder, + GVariantBuilder * ip4_builder, + GVariantBuilder * ip6_builder) { - if (proxy_config) - dump_proxy_to_props(proxy_config, proxy_builder); - if (ip4_config) - dump_ip_to_props(NM_IP_CONFIG(ip4_config), ip4_builder); - if (ip6_config) - dump_ip_to_props(NM_IP_CONFIG(ip6_config), ip6_builder); + if (l3cd) { + dump_ip_to_props(l3cd, AF_INET, ip4_builder); + dump_ip_to_props(l3cd, AF_INET6, ip6_builder); + dump_proxy_to_props(l3cd, proxy_builder); + } } static const char * @@ -479,9 +459,7 @@ _dispatcher_call(NMDispatcherAction action, gboolean activation_type_external, NMConnectivityState connectivity_state, const char * vpn_iface, - NMProxyConfig * vpn_proxy_config, - NMIP4Config * vpn_ip4_config, - NMIP6Config * vpn_ip6_config, + const NML3ConfigData *l3cd, NMDispatcherFunc callback, gpointer user_data, NMDispatcherCallId ** out_call_id) @@ -593,13 +571,8 @@ _dispatcher_call(NMDispatcherAction action, &device_ip6_props, &device_dhcp4_props, &device_dhcp6_props); - if (vpn_ip4_config || vpn_ip6_config) { - fill_vpn_props(vpn_proxy_config, - vpn_ip4_config, - vpn_ip6_config, - &vpn_proxy_props, - &vpn_ip4_props, - &vpn_ip6_props); + if (l3cd) { + fill_vpn_props(l3cd, &vpn_proxy_props, &vpn_ip4_props, &vpn_ip6_props); } } @@ -693,8 +666,6 @@ nm_dispatcher_call_hostname(NMDispatcherFunc callback, NM_CONNECTIVITY_UNKNOWN, NULL, NULL, - NULL, - NULL, callback, user_data, out_call_id); @@ -744,8 +715,6 @@ nm_dispatcher_call_device(NMDispatcherAction action, NM_CONNECTIVITY_UNKNOWN, NULL, NULL, - NULL, - NULL, callback, user_data, out_call_id); @@ -790,8 +759,6 @@ nm_dispatcher_call_device_sync(NMDispatcherAction action, NULL, NULL, NULL, - NULL, - NULL, NULL); } @@ -802,9 +769,7 @@ nm_dispatcher_call_device_sync(NMDispatcherAction action, * @applied_connection: the currently applied connection * @parent_device: the parent #NMDevice of the VPN connection * @vpn_iface: the IP interface of the VPN tunnel, if any - * @vpn_proxy_config: the #NMProxyConfig of the VPN connection - * @vpn_ip4_config: the #NMIP4Config of the VPN connection - * @vpn_ip6_config: the #NMIP6Config of the VPN connection + * @vpn_l3cd: the #NML3ConfigData of the VPN connection * @callback: a caller-supplied callback to execute when done * @user_data: caller-supplied pointer passed to @callback * @out_call_id: on success, a call identifier which can be passed to @@ -821,9 +786,7 @@ nm_dispatcher_call_vpn(NMDispatcherAction action, NMConnection * applied_connection, NMDevice * parent_device, const char * vpn_iface, - NMProxyConfig * vpn_proxy_config, - NMIP4Config * vpn_ip4_config, - NMIP6Config * vpn_ip6_config, + const NML3ConfigData *l3cd, NMDispatcherFunc callback, gpointer user_data, NMDispatcherCallId ** out_call_id) @@ -836,9 +799,7 @@ nm_dispatcher_call_vpn(NMDispatcherAction action, FALSE, NM_CONNECTIVITY_UNKNOWN, vpn_iface, - vpn_proxy_config, - vpn_ip4_config, - vpn_ip6_config, + l3cd, callback, user_data, out_call_id); @@ -851,9 +812,7 @@ nm_dispatcher_call_vpn(NMDispatcherAction action, * @applied_connection: the currently applied connection * @parent_device: the parent #NMDevice of the VPN connection * @vpn_iface: the IP interface of the VPN tunnel, if any - * @vpn_proxy_config: the #NMProxyConfig of the VPN connection - * @vpn_ip4_config: the #NMIP4Config of the VPN connection - * @vpn_ip6_config: the #NMIP6Config of the VPN connection + * @vpn_l3cd: the #NML3ConfigData of the VPN connection * * This method always invokes the dispatcher action synchronously and it may * take a long time to return. @@ -866,9 +825,7 @@ nm_dispatcher_call_vpn_sync(NMDispatcherAction action, NMConnection * applied_connection, NMDevice * parent_device, const char * vpn_iface, - NMProxyConfig * vpn_proxy_config, - NMIP4Config * vpn_ip4_config, - NMIP6Config * vpn_ip6_config) + const NML3ConfigData *l3cd) { return _dispatcher_call(action, TRUE, @@ -878,9 +835,7 @@ nm_dispatcher_call_vpn_sync(NMDispatcherAction action, FALSE, NM_CONNECTIVITY_UNKNOWN, vpn_iface, - vpn_proxy_config, - vpn_ip4_config, - vpn_ip6_config, + l3cd, NULL, NULL, NULL); @@ -913,8 +868,6 @@ nm_dispatcher_call_connectivity(NMConnectivityState connectivity_state, connectivity_state, NULL, NULL, - NULL, - NULL, callback, user_data, out_call_id); diff --git a/src/core/nm-dispatcher.h b/src/core/nm-dispatcher.h index 8e4f01cb62..c347352a2d 100644 --- a/src/core/nm-dispatcher.h +++ b/src/core/nm-dispatcher.h @@ -51,9 +51,7 @@ gboolean nm_dispatcher_call_vpn(NMDispatcherAction action, NMConnection * applied_connection, NMDevice * parent_device, const char * vpn_iface, - NMProxyConfig * vpn_proxy_config, - NMIP4Config * vpn_ip4_config, - NMIP6Config * vpn_ip6_config, + const NML3ConfigData *l3cd, NMDispatcherFunc callback, gpointer user_data, NMDispatcherCallId ** out_call_id); @@ -63,9 +61,7 @@ gboolean nm_dispatcher_call_vpn_sync(NMDispatcherAction action, NMConnection * applied_connection, NMDevice * parent_device, const char * vpn_iface, - NMProxyConfig * vpn_proxy_config, - NMIP4Config * vpn_ip4_config, - NMIP6Config * vpn_ip6_config); + const NML3ConfigData *l3cd); gboolean nm_dispatcher_call_connectivity(NMConnectivityState state, NMDispatcherFunc callback, diff --git a/src/core/nm-iface-helper.c b/src/core/nm-iface-helper.c deleted file mode 100644 index 83ae1b5466..0000000000 --- a/src/core/nm-iface-helper.c +++ /dev/null @@ -1,857 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2014 Red Hat, Inc. - */ - -#include "src/core/nm-default-daemon.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libnm-glib-aux/nm-c-list.h" - -#include "main-utils.h" -#include "NetworkManagerUtils.h" -#include "libnm-platform/nm-linux-platform.h" -#include "libnm-platform/nm-platform-utils.h" -#include "dhcp/nm-dhcp-manager.h" -#include "ndisc/nm-ndisc.h" -#include "ndisc/nm-lndp-ndisc.h" -#include "nm-utils.h" -#include "libnm-core-intern/nm-core-internal.h" -#include "nm-setting-ip6-config.h" -#include "libnm-systemd-core/nm-sd.h" - -#if !defined(NM_DIST_VERSION) -#define NM_DIST_VERSION VERSION -#endif - -#define NMIH_PID_FILE_FMT NMRUNDIR "/nm-iface-helper-%d.pid" - -/*****************************************************************************/ - -static struct { - GMainLoop *main_loop; - int ifindex; - gboolean is_vrf_device; - - guint dad_failed_id; - CList dad_failed_lst_head; -} gl /*obal*/ = { - .ifindex = -1, - .is_vrf_device = FALSE, -}; - -static struct { - gboolean slaac; - gboolean show_version; - gboolean become_daemon; - gboolean debug; - gboolean g_fatal_warnings; - gboolean slaac_required; - gboolean dhcp4_required; - int tempaddr; - char * ifname; - char * uuid; - char * stable_id; - char * dhcp4_address; - char * dhcp4_clientid; - char * dhcp4_hostname; - char * dhcp4_fqdn; - char * mud_url; - char * iid_str; - NMSettingIP6ConfigAddrGenMode addr_gen_mode; - char * logging_backend; - char * opt_log_level; - char * opt_log_domains; - guint32 priority_v4; - guint32 priority_v6; -} global_opt = { - .tempaddr = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, - .priority_v4 = NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4, - .priority_v6 = NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6, -}; - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "nm-iface-helper" -#define _NMLOG(level, domain, ...) \ - nm_log((level), \ - (domain), \ - global_opt.ifname, \ - NULL, \ - "iface-helper: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__) _NM_UTILS_MACRO_REST(__VA_ARGS__)) - -/*****************************************************************************/ - -static void -_dhcp_client_notify_cb(NMDhcpClient * client, - const NMDhcpClientNotifyData *notify_data, - gpointer user_data) -{ - static NMIP4Config *last_config = NULL; - NMIP4Config * existing; - gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL; - gs_free_error GError *error = NULL; - NMIP4Config * ip4_config; - - if (!notify_data || notify_data->notify_type != NM_DHCP_CLIENT_NOTIFY_TYPE_STATE_CHANGED) - g_return_if_reached(); - - nm_assert(!notify_data->state_changed.ip_config - || NM_IS_IP4_CONFIG(notify_data->state_changed.ip_config)); - - _LOGD(LOGD_DHCP4, "new DHCPv4 client state %d", (int) notify_data->state_changed.dhcp_state); - - switch (notify_data->state_changed.dhcp_state) { - case NM_DHCP_STATE_BOUND: - case NM_DHCP_STATE_EXTENDED: - ip4_config = NM_IP4_CONFIG(notify_data->state_changed.ip_config); - - g_assert(ip4_config); - g_assert(nm_ip4_config_get_ifindex(ip4_config) == gl.ifindex); - - existing = nm_ip4_config_capture(nm_platform_get_multi_idx(NM_PLATFORM_GET), - NM_PLATFORM_GET, - gl.ifindex); - if (last_config) - nm_ip4_config_subtract(existing, last_config, 0); - - nm_ip4_config_merge(existing, ip4_config, NM_IP_CONFIG_MERGE_DEFAULT, 0); - nm_ip4_config_add_dependent_routes(existing, - RT_TABLE_MAIN, - global_opt.priority_v4, - gl.is_vrf_device, - &ip4_dev_route_blacklist); - if (!nm_ip4_config_commit(existing, NM_PLATFORM_GET, NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN)) - _LOGW(LOGD_DHCP4, "failed to apply DHCPv4 config"); - - if (!last_config && !nm_dhcp_client_accept(client, &error)) - _LOGW(LOGD_DHCP4, "failed to accept lease: %s", error->message); - - nm_platform_ip4_dev_route_blacklist_set(NM_PLATFORM_GET, - gl.ifindex, - ip4_dev_route_blacklist); - - if (last_config) - g_object_unref(last_config); - last_config = nm_ip4_config_new(nm_platform_get_multi_idx(NM_PLATFORM_GET), - nm_dhcp_client_get_ifindex(client)); - nm_ip4_config_replace(last_config, ip4_config, NULL); - break; - case NM_DHCP_STATE_TIMEOUT: - case NM_DHCP_STATE_DONE: - case NM_DHCP_STATE_FAIL: - if (global_opt.dhcp4_required) { - _LOGW(LOGD_DHCP4, "DHCPv4 timed out or failed, quitting..."); - g_main_loop_quit(gl.main_loop); - } else - _LOGW(LOGD_DHCP4, "DHCPv4 timed out or failed"); - break; - default: - break; - } -} - -static void -ndisc_config_changed(NMNDisc * ndisc, - const NMNDiscData *rdata, - guint changed_int, - gpointer user_data) -{ - NMNDiscConfigMap changed = changed_int; - static NMIP6Config *ndisc_config = NULL; - NMIP6Config * existing; - - existing = nm_ip6_config_capture(nm_platform_get_multi_idx(NM_PLATFORM_GET), - NM_PLATFORM_GET, - gl.ifindex, - global_opt.tempaddr); - if (ndisc_config) - nm_ip6_config_subtract(existing, ndisc_config, 0); - else { - ndisc_config = nm_ip6_config_new(nm_platform_get_multi_idx(NM_PLATFORM_GET), gl.ifindex); - } - - if (changed & NM_NDISC_CONFIG_ADDRESSES) { - guint32 ifa_flags; - - /* Check, whether kernel is recent enough to help user space handling RA. - * If it's not supported, we have no ipv6-privacy and must add autoconf - * addresses as /128. The reason for the /128 is to prevent the kernel - * from adding a prefix route for this address. */ - ifa_flags = IFA_F_NOPREFIXROUTE; - if (NM_IN_SET(global_opt.tempaddr, - NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, - NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)) - ifa_flags |= IFA_F_MANAGETEMPADDR; - - nm_ip6_config_reset_addresses_ndisc(ndisc_config, - rdata->addresses, - rdata->addresses_n, - 64, - ifa_flags); - } - - if (NM_FLAGS_ANY(changed, NM_NDISC_CONFIG_ROUTES | NM_NDISC_CONFIG_GATEWAYS)) { - nm_ip6_config_reset_routes_ndisc(ndisc_config, - rdata->gateways, - rdata->gateways_n, - rdata->routes, - rdata->routes_n, - RT_TABLE_MAIN, - global_opt.priority_v6); - } - - if (changed & NM_NDISC_CONFIG_DHCP_LEVEL) { - /* Unsupported until systemd DHCPv6 is ready */ - } - - if (changed & NM_NDISC_CONFIG_HOP_LIMIT) - nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe(NM_PLATFORM_GET, - global_opt.ifname, - rdata->hop_limit); - - if (changed & NM_NDISC_CONFIG_REACHABLE_TIME) { - nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time(NM_PLATFORM_GET, - global_opt.ifname, - rdata->reachable_time_ms); - } - - if (changed & NM_NDISC_CONFIG_RETRANS_TIMER) { - nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time(NM_PLATFORM_GET, - global_opt.ifname, - rdata->retrans_timer_ms); - } - - if (changed & NM_NDISC_CONFIG_MTU) { - nm_platform_sysctl_ip_conf_set_int64(NM_PLATFORM_GET, - AF_INET6, - global_opt.ifname, - "mtu", - rdata->mtu); - } - - nm_ip6_config_merge(existing, ndisc_config, NM_IP_CONFIG_MERGE_DEFAULT, 0); - nm_ip6_config_add_dependent_routes(existing, - RT_TABLE_MAIN, - global_opt.priority_v6, - gl.is_vrf_device); - if (!nm_ip6_config_commit(existing, NM_PLATFORM_GET, NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN, NULL)) - _LOGW(LOGD_IP6, "failed to apply IPv6 config"); -} - -static void -ndisc_ra_timeout(NMNDisc *ndisc, gpointer user_data) -{ - if (global_opt.slaac_required) { - _LOGW(LOGD_IP6, "IPv6 timed out or failed, quitting..."); - g_main_loop_quit(gl.main_loop); - } else - _LOGW(LOGD_IP6, "IPv6 timed out or failed"); -} - -static gboolean -quit_handler(gpointer user_data) -{ - g_main_loop_quit(gl.main_loop); - return G_SOURCE_REMOVE; -} - -static void -setup_signals(void) -{ - signal(SIGPIPE, SIG_IGN); - g_unix_signal_add(SIGINT, quit_handler, NULL); - g_unix_signal_add(SIGTERM, quit_handler, NULL); -} - -static gboolean -do_early_setup(int *argc, char **argv[]) -{ - gint64 priority64_v4 = -1; - gint64 priority64_v6 = -1; - GOptionEntry options[] = { - /* Interface/IP config */ - {"ifname", - 'i', - 0, - G_OPTION_ARG_STRING, - &global_opt.ifname, - N_("The interface to manage"), - "eth0"}, - {"uuid", - 'u', - 0, - G_OPTION_ARG_STRING, - &global_opt.uuid, - N_("Connection UUID"), - "661e8cd0-b618-46b8-9dc9-31a52baaa16b"}, - {"stable-id", - '\0', - 0, - G_OPTION_ARG_STRING, - &global_opt.stable_id, - N_("Connection Token for Stable IDs"), - "eth"}, - {"slaac", - 's', - 0, - G_OPTION_ARG_NONE, - &global_opt.slaac, - N_("Whether to manage IPv6 SLAAC"), - NULL}, - {"slaac-required", - '6', - 0, - G_OPTION_ARG_NONE, - &global_opt.slaac_required, - N_("Whether SLAAC must be successful"), - NULL}, - {"slaac-tempaddr", - 't', - 0, - G_OPTION_ARG_INT, - &global_opt.tempaddr, - N_("Use an IPv6 temporary privacy address"), - NULL}, - {"dhcp4", - 'd', - 0, - G_OPTION_ARG_STRING, - &global_opt.dhcp4_address, - N_("Current DHCPv4 address"), - NULL}, - {"dhcp4-required", - '4', - 0, - G_OPTION_ARG_NONE, - &global_opt.dhcp4_required, - N_("Whether DHCPv4 must be successful"), - NULL}, - {"dhcp4-clientid", - 'c', - 0, - G_OPTION_ARG_STRING, - &global_opt.dhcp4_clientid, - N_("Hex-encoded DHCPv4 client ID"), - NULL}, - {"dhcp4-hostname", - 'h', - 0, - G_OPTION_ARG_STRING, - &global_opt.dhcp4_hostname, - N_("Hostname to send to DHCP server"), - N_("barbar")}, - {"dhcp4-fqdn", - 'F', - 0, - G_OPTION_ARG_STRING, - &global_opt.dhcp4_fqdn, - N_("FQDN to send to DHCP server"), - N_("host.domain.org")}, - {"priority4", - '\0', - 0, - G_OPTION_ARG_INT64, - &priority64_v4, - N_("Route priority for IPv4"), - N_("0")}, - {"priority6", - '\0', - 0, - G_OPTION_ARG_INT64, - &priority64_v6, - N_("Route priority for IPv6"), - N_("1024")}, - {"iid", - 'e', - 0, - G_OPTION_ARG_STRING, - &global_opt.iid_str, - N_("Hex-encoded Interface Identifier"), - ""}, - {"addr-gen-mode", - 'e', - 0, - G_OPTION_ARG_INT, - &global_opt.addr_gen_mode, - N_("IPv6 SLAAC address generation mode"), - "eui64"}, - {"logging-backend", - '\0', - 0, - G_OPTION_ARG_STRING, - &global_opt.logging_backend, - N_("The logging backend configuration value. See logging.backend in NetworkManager.conf"), - NULL}, - - /* Logging/debugging */ - {"version", - 'V', - 0, - G_OPTION_ARG_NONE, - &global_opt.show_version, - N_("Print NetworkManager version and exit"), - NULL}, - {"no-daemon", - 'n', - G_OPTION_FLAG_REVERSE, - G_OPTION_ARG_NONE, - &global_opt.become_daemon, - N_("Don't become a daemon"), - NULL}, - {"debug", - 'b', - 0, - G_OPTION_ARG_NONE, - &global_opt.debug, - N_("Don't become a daemon, and log to stderr"), - NULL}, - {"log-level", - 0, - 0, - G_OPTION_ARG_STRING, - &global_opt.opt_log_level, - N_("Log level: one of [%s]"), - "INFO"}, - {"log-domains", - 0, - 0, - G_OPTION_ARG_STRING, - &global_opt.opt_log_domains, - N_("Log domains separated by ',': any combination of [%s]"), - "PLATFORM,RFKILL,WIFI"}, - {"g-fatal-warnings", - 0, - 0, - G_OPTION_ARG_NONE, - &global_opt.g_fatal_warnings, - N_("Make all warnings fatal"), - NULL}, - {NULL}}; - - if (!nm_main_utils_early_setup("nm-iface-helper", - argc, - argv, - options, - NULL, - NULL, - _("nm-iface-helper is a small, standalone process that manages " - "a single network interface."))) - return FALSE; - - if (priority64_v4 >= 0 && priority64_v4 <= G_MAXUINT32) - global_opt.priority_v4 = (guint32) priority64_v4; - if (priority64_v6 >= 0 && priority64_v6 <= G_MAXUINT32) - global_opt.priority_v6 = (guint32) priority64_v6; - return TRUE; -} - -typedef struct { - NMPlatform *platform; - NMNDisc * ndisc; -} DadFailedHandleData; - -static gboolean -dad_failed_handle_idle(gpointer user_data) -{ - DadFailedHandleData *data = user_data; - NMCListElem * elem; - - while ((elem = c_list_first_entry(&gl.dad_failed_lst_head, NMCListElem, lst))) { - nm_auto_nmpobj const NMPObject *obj = elem->data; - - nm_c_list_elem_free(elem); - - if (nm_ndisc_dad_addr_is_fail_candidate(data->platform, obj)) { - nm_ndisc_dad_failed(data->ndisc, &NMP_OBJECT_CAST_IP6_ADDRESS(obj)->address, TRUE); - } - } - - gl.dad_failed_id = 0; - return G_SOURCE_REMOVE; -} - -static void -ip6_address_changed(NMPlatform * platform, - int obj_type_i, - int iface, - const NMPlatformIP6Address *addr, - int change_type_i, - NMNDisc * ndisc) -{ - const NMPlatformSignalChangeType change_type = change_type_i; - DadFailedHandleData * data; - - if (!nm_ndisc_dad_addr_is_fail_candidate_event(change_type, addr)) - return; - - c_list_link_tail( - &gl.dad_failed_lst_head, - &nm_c_list_elem_new_stale((gpointer) nmp_object_ref(NMP_OBJECT_UP_CAST(addr)))->lst); - if (gl.dad_failed_id) - return; - - data = g_slice_new(DadFailedHandleData); - data->platform = platform; - data->ndisc = ndisc; - gl.dad_failed_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, - dad_failed_handle_idle, - data, - nm_g_slice_free_fcn(DadFailedHandleData)); -} - -int -main(int argc, char *argv[]) -{ - char * bad_domains = NULL; - gs_free_error GError *error = NULL; - gboolean wrote_pidfile = FALSE; - gs_free char * pidfile = NULL; - gs_unref_object NMDhcpClient *dhcp4_client = NULL; - gs_unref_object NMNDisc *ndisc = NULL; - gs_unref_bytes GBytes *hwaddr = NULL; - gs_unref_bytes GBytes *bcast_hwaddr = NULL; - gs_unref_bytes GBytes *client_id = NULL; - gs_free NMUtilsIPv6IfaceId *iid = NULL; - const NMPlatformLink * pllink; - guint sd_id; - int errsv; - - c_list_init(&gl.dad_failed_lst_head); - - setpgid(getpid(), getpid()); - - if (!do_early_setup(&argc, &argv)) - return 1; - - nm_logging_init_pre("nm-iface-helper", - g_strdup_printf("%s[%ld] (%s): ", - _NMLOG_PREFIX_NAME, - (long) getpid(), - global_opt.ifname ?: "???")); - - if (global_opt.g_fatal_warnings) { - GLogLevelFlags fatal_mask; - - fatal_mask = g_log_set_always_fatal(G_LOG_FATAL_MASK); - fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL; - g_log_set_always_fatal(fatal_mask); - } - - if (global_opt.show_version) { - fprintf(stdout, NM_DIST_VERSION "\n"); - return 0; - } - - nm_main_utils_ensure_root(); - - if (!global_opt.ifname || !global_opt.uuid) { - fprintf(stderr, _("An interface name and UUID are required\n")); - return 1; - } - - gl.ifindex = nmp_utils_if_nametoindex(global_opt.ifname); - if (gl.ifindex <= 0) { - errsv = errno; - fprintf(stderr, - _("Failed to find interface index for %s (%s)\n"), - global_opt.ifname, - nm_strerror_native(errsv)); - return 1; - } - pidfile = g_strdup_printf(NMIH_PID_FILE_FMT, gl.ifindex); - nm_main_utils_ensure_not_running_pidfile(pidfile); - - nm_main_utils_ensure_rundir(); - - if (!nm_logging_setup(global_opt.opt_log_level, - global_opt.opt_log_domains, - &bad_domains, - &error)) { - fprintf(stderr, - _("%s. Please use --help to see a list of valid options.\n"), - error->message); - return 1; - } else if (bad_domains) { - fprintf(stderr, - _("Ignoring unrecognized log domain(s) '%s' passed on command line.\n"), - bad_domains); - nm_clear_g_free(&bad_domains); - } - - if (global_opt.become_daemon && !global_opt.debug) { - if (daemon(0, 0) < 0) { - errsv = errno; - fprintf(stderr, - _("Could not daemonize: %s [error %u]\n"), - nm_strerror_native(errsv), - errsv); - return 1; - } - if (nm_main_utils_write_pidfile(pidfile)) - wrote_pidfile = TRUE; - } - - /* Set up unix signal handling - before creating threads, but after daemonizing! */ - gl.main_loop = g_main_loop_new(NULL, FALSE); - setup_signals(); - - nm_logging_init(global_opt.logging_backend, global_opt.debug); - - _LOGI(LOGD_CORE, "nm-iface-helper (version " NM_DIST_VERSION ") is starting..."); - - /* Set up platform interaction layer */ - nm_linux_platform_setup(); - - pllink = nm_platform_link_get(NM_PLATFORM_GET, gl.ifindex); - if (pllink) { - hwaddr = nmp_link_address_get_as_bytes(&pllink->l_address); - bcast_hwaddr = nmp_link_address_get_as_bytes(&pllink->l_broadcast); - - if (pllink->master > 0) { - gl.is_vrf_device = - nm_platform_link_get_type(NM_PLATFORM_GET, pllink->master) == NM_LINK_TYPE_VRF; - } - } - - if (global_opt.iid_str) { - GBytes *bytes; - gsize ignored = 0; - - bytes = nm_utils_hexstr2bin(global_opt.iid_str); - if (!bytes || g_bytes_get_size(bytes) != sizeof(*iid)) { - fprintf(stderr, _("(%s): Invalid IID %s\n"), global_opt.ifname, global_opt.iid_str); - return 1; - } - iid = g_bytes_unref_to_data(bytes, &ignored); - } - - if (global_opt.dhcp4_clientid) { - /* this string is just a plain hex-string. Unlike ipv4.dhcp-client-id, which - * is parsed via nm_dhcp_utils_client_id_string_to_bytes(). */ - client_id = nm_utils_hexstr2bin(global_opt.dhcp4_clientid); - if (!client_id || g_bytes_get_size(client_id) < 2) { - fprintf(stderr, - _("(%s): Invalid DHCP client-id %s\n"), - global_opt.ifname, - global_opt.dhcp4_clientid); - return 1; - } - } - - if (global_opt.dhcp4_address) { - nm_platform_sysctl_ip_conf_set(NM_PLATFORM_GET, - AF_INET, - global_opt.ifname, - "promote_secondaries", - "1"); - - dhcp4_client = nm_dhcp_manager_start_ip4(nm_dhcp_manager_get(), - nm_platform_get_multi_idx(NM_PLATFORM_GET), - global_opt.ifname, - gl.ifindex, - hwaddr, - bcast_hwaddr, - global_opt.uuid, - RT_TABLE_MAIN, - global_opt.priority_v4, - NM_DHCP_CLIENT_FLAGS_NONE, - !!global_opt.dhcp4_hostname, - global_opt.dhcp4_hostname, - global_opt.dhcp4_fqdn, - NM_DHCP_HOSTNAME_FLAGS_FQDN_DEFAULT_IP4, - global_opt.mud_url, - client_id, - NM_DHCP_TIMEOUT_DEFAULT, - NULL, - global_opt.dhcp4_address, - NULL, - NULL, - &error); - if (!dhcp4_client) - g_error("failure to start DHCP: %s", error->message); - - g_signal_connect(dhcp4_client, - NM_DHCP_CLIENT_NOTIFY, - G_CALLBACK(_dhcp_client_notify_cb), - NULL); - } - - if (global_opt.slaac) { - NMUtilsStableType stable_type = NM_UTILS_STABLE_TYPE_UUID; - const char * stable_id = global_opt.uuid; - int router_solicitation_interval; - int router_solicitations; - guint32 default_ra_timeout; - int max_addresses; - - nm_platform_link_set_inet6_addr_gen_mode(NM_PLATFORM_GET, - gl.ifindex, - NM_IN6_ADDR_GEN_MODE_NONE); - - if (global_opt.stable_id - && (global_opt.stable_id[0] >= '0' && global_opt.stable_id[0] <= '9') - && global_opt.stable_id[1] == ' ') { - /* strict parsing of --stable-id, which is the numeric stable-type - * and the ID, joined with one space. For now, only support stable-types - * from 0 to 9. */ - stable_type = (global_opt.stable_id[0] - '0'); - stable_id = &global_opt.stable_id[2]; - } - - nm_ndisc_get_sysctl(NM_PLATFORM_GET, - global_opt.ifname, - &max_addresses, - &router_solicitations, - &router_solicitation_interval, - &default_ra_timeout); - - ndisc = nm_lndp_ndisc_new(NM_PLATFORM_GET, - gl.ifindex, - global_opt.ifname, - stable_type, - stable_id, - global_opt.addr_gen_mode, - NM_NDISC_NODE_TYPE_HOST, - max_addresses, - router_solicitations, - router_solicitation_interval, - default_ra_timeout, - NULL); - g_assert(ndisc); - - if (iid) - nm_ndisc_set_iid(ndisc, *iid); - - nm_platform_sysctl_ip_conf_set(NM_PLATFORM_GET, - AF_INET6, - global_opt.ifname, - "accept_ra", - "0"); - - g_signal_connect(NM_PLATFORM_GET, - NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - G_CALLBACK(ip6_address_changed), - ndisc); - g_signal_connect(ndisc, NM_NDISC_CONFIG_RECEIVED, G_CALLBACK(ndisc_config_changed), NULL); - g_signal_connect(ndisc, NM_NDISC_RA_TIMEOUT_SIGNAL, G_CALLBACK(ndisc_ra_timeout), NULL); - nm_ndisc_start(ndisc); - } - - sd_id = nm_sd_event_attach_default(); - - g_main_loop_run(gl.main_loop); - - nm_clear_g_source(&gl.dad_failed_id); - nm_c_list_elem_free_all(&gl.dad_failed_lst_head, (GDestroyNotify) nmp_object_unref); - - if (pidfile && wrote_pidfile) - unlink(pidfile); - - _LOGI(LOGD_CORE, "exiting"); - - nm_clear_g_source(&sd_id); - nm_clear_pointer(&gl.main_loop, g_main_loop_unref); - return 0; -} - -/*****************************************************************************/ - -const NMDhcpClientFactory *const _nm_dhcp_manager_factories[6] = { - /* For nm-iface-helper there is no option to choose a DHCP plugin. - * It just uses the "internal" one. */ - &_nm_dhcp_client_factory_internal, -}; - -/*****************************************************************************/ -/* Stub functions */ - -#include "nm-config.h" -#include "devices/nm-device.h" -#include "nm-active-connection.h" -#include "nm-dbus-manager.h" - -void -nm_main_config_reload(int signal) -{ - _LOGI(LOGD_CORE, "reloading configuration not supported"); -} - -NMConfig * -nm_config_get(void) -{ - return GUINT_TO_POINTER(1); -} - -NMConfigData * -nm_config_get_data_orig(NMConfig *config) -{ - return GUINT_TO_POINTER(1); -} - -char * -nm_config_data_get_value(const NMConfigData * config_data, - const char * group, - const char * key, - NMConfigGetValueFlags flags) -{ - return NULL; -} - -NMConfigConfigureAndQuitType -nm_config_get_configure_and_quit(NMConfig *config) -{ - return NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED; -} - -NMDBusManager * -nm_dbus_manager_get(void) -{ - return NULL; -} - -gboolean -nm_dbus_manager_is_stopping(NMDBusManager *self) -{ - return FALSE; -} - -void -_nm_dbus_manager_obj_export(NMDBusObject *obj) -{} - -void -_nm_dbus_manager_obj_unexport(NMDBusObject *obj) -{} - -void -_nm_dbus_manager_obj_notify(NMDBusObject *obj, guint n_pspecs, const GParamSpec *const *pspecs) -{} - -void -_nm_dbus_manager_obj_emit_signal(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const GDBusSignalInfo * signal_info, - GVariant * args) -{} - -GType -nm_device_get_type(void) -{ - g_return_val_if_reached(0); -} - -GType -nm_active_connection_get_type(void) -{ - g_return_val_if_reached(0); -} diff --git a/src/core/nm-ip-config.c b/src/core/nm-ip-config.c index 2426eb4941..196cf35a98 100644 --- a/src/core/nm-ip-config.c +++ b/src/core/nm-ip-config.c @@ -8,31 +8,125 @@ #include "nm-ip-config.h" +#include + #include "nm-l3cfg.h" +#include "NetworkManagerUtils.h" /*****************************************************************************/ -NM_GOBJECT_PROPERTIES_DEFINE(NMIPConfig, PROP_L3CFG, PROP_IS_VPN, ); +GType nm_ip4_config_get_type(void); +GType nm_ip6_config_get_type(void); -typedef struct _NMIPConfigPrivate { - NML3Cfg *l3cfg; - bool is_vpn : 1; -} NMIPConfigPrivate; +/*****************************************************************************/ + +#define NM_IP_CONFIG_ADDRESS_DATA "address-data" +#define NM_IP_CONFIG_DNS_OPTIONS "dns-options" +#define NM_IP_CONFIG_DNS_PRIORITY "dns-priority" +#define NM_IP_CONFIG_DOMAINS "domains" +#define NM_IP_CONFIG_GATEWAY "gateway" +#define NM_IP_CONFIG_ROUTE_DATA "route-data" +#define NM_IP_CONFIG_SEARCHES "searches" + +/*****************************************************************************/ + +typedef struct _NMIPConfigPrivate NMIPConfigPrivate; + +NM_GOBJECT_PROPERTIES_DEFINE_FULL(_ip, + NMIPConfig, + PROP_IP_L3CFG, + PROP_IP_ADDRESS_DATA, + PROP_IP_GATEWAY, + PROP_IP_ROUTE_DATA, + PROP_IP_DOMAINS, + PROP_IP_SEARCHES, + PROP_IP_DNS_PRIORITY, + PROP_IP_DNS_OPTIONS, ); G_DEFINE_ABSTRACT_TYPE(NMIPConfig, nm_ip_config, NM_TYPE_DBUS_OBJECT) -#define NM_IP_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMIPConfig, NM_IS_IP_CONFIG) +#define NM_IP_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMIPConfig, NM_IS_IP_CONFIG) + +/*****************************************************************************/ + +static void _handle_platform_change(NMIPConfig *self, guint32 obj_type_flags, gboolean is_init); +static void _handle_l3cd_changed(NMIPConfig *self, const NML3ConfigData *l3cd); /*****************************************************************************/ static void -get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +_value_set_variant_as(GValue *value, const char *const *strv, guint len) { - NMIPConfig * self = NM_IP_CONFIG(object); - NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + if (len > 0) { + nm_assert(strv && strv[0]); + g_value_set_variant(value, g_variant_new_strv((const char *const *) strv, len)); + } else + g_value_set_variant(value, nm_g_variant_singleton_as()); +} + +/*****************************************************************************/ + +static void +_l3cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMIPConfig *self) +{ + switch (notify_data->notify_type) { + case NM_L3_CONFIG_NOTIFY_TYPE_L3CD_CHANGED: + if (notify_data->l3cd_changed.commited) + _handle_l3cd_changed(self, notify_data->l3cd_changed.l3cd_new); + break; + case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE: + _handle_platform_change(self, notify_data->platform_change_on_idle.obj_type_flags, FALSE); + break; + default: + break; + } +} + +/*****************************************************************************/ + +static void +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]; + const char *const *strv; + guint len; + int v_i; - (void) priv; switch (prop_id) { + case PROP_IP_ADDRESS_DATA: + g_value_set_variant(value, priv->v_address_data); + break; + case PROP_IP_GATEWAY: + g_value_set_variant( + value, + nm_ip_addr_is_null(addr_family, &priv->v_gateway.addr) + ? nm_g_variant_singleton_s_empty() + : g_variant_new_string( + nm_utils_inet_ntop(addr_family, &priv->v_gateway.addr, sbuf_addr))); + break; + case PROP_IP_ROUTE_DATA: + g_value_set_variant(value, priv->v_route_data); + break; + case PROP_IP_DOMAINS: + strv = nm_l3_config_data_get_domains(priv->l3cd, addr_family, &len); + _value_set_variant_as(value, strv, len); + break; + case PROP_IP_SEARCHES: + strv = nm_l3_config_data_get_searches(priv->l3cd, addr_family, &len); + _value_set_variant_as(value, strv, len); + break; + case PROP_IP_DNS_PRIORITY: + v_i = nm_l3_config_data_get_dns_priority_or_default(priv->l3cd, addr_family); + g_value_set_variant(value, + (v_i == 0) ? nm_g_variant_singleton_i_0() : g_variant_new_int32(v_i)); + break; + case PROP_IP_DNS_OPTIONS: + strv = nm_l3_config_data_get_dns_options(priv->l3cd, addr_family, &len); + _value_set_variant_as(value, strv, len); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -44,16 +138,14 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps { NMIPConfig * self = NM_IP_CONFIG(object); NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + gpointer ptr; switch (prop_id) { - case PROP_L3CFG: + case PROP_IP_L3CFG: /* construct-only */ - priv->l3cfg = nm_g_object_ref(g_value_get_pointer(value)); - nm_assert(!priv->l3cfg || NM_IS_L3CFG(priv->l3cfg)); - break; - case PROP_IS_VPN: - /* construct-only */ - priv->is_vpn = g_value_get_boolean(value); + ptr = g_value_get_pointer(value); + nm_assert(NM_IS_L3CFG(ptr)); + priv->l3cfg = g_object_ref(ptr); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -65,16 +157,10 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps static void nm_ip_config_init(NMIPConfig *self) -{ - NMIPConfigPrivate *priv; - - priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_IP_CONFIG, NMIPConfigPrivate); - - self->_priv = priv; -} +{} NMIPConfig * -nm_ip_config_new(int addr_family, NML3Cfg *l3cfg, gboolean is_vpn) +nm_ip_config_new(int addr_family, NML3Cfg *l3cfg) { nm_assert_addr_family(addr_family); nm_assert(NM_L3CFG(l3cfg)); @@ -83,18 +169,50 @@ nm_ip_config_new(int addr_family, NML3Cfg *l3cfg, gboolean is_vpn) : nm_ip6_config_get_type(), NM_IP_CONFIG_L3CFG, l3cfg, - NM_IP_CONFIG_IS_VPN, - is_vpn, NULL); } +static void +constructed(GObject *object) +{ + NMIPConfig * self = NM_IP_CONFIG(object); + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + + priv->l3cfg_notify_id = + g_signal_connect(priv->l3cfg, NM_L3CFG_SIGNAL_NOTIFY, G_CALLBACK(_l3cfg_notify_cb), self); + + priv->l3cd = nm_l3_config_data_ref(nm_l3cfg_get_combined_l3cd(priv->l3cfg, TRUE)); + + _handle_platform_change(self, ~((guint32) 0u), TRUE); + + G_OBJECT_CLASS(nm_ip_config_parent_class)->constructed(object); +} + +void +nm_ip_config_take_and_unexport_on_idle(NMIPConfig *self_take) +{ + if (self_take) + nm_dbus_object_unexport_on_idle(g_steal_pointer(&self_take)); +} + static void finalize(GObject *object) { NMIPConfig * self = NM_IP_CONFIG(object); NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); - nm_g_object_unref(priv->l3cfg); + nm_clear_g_signal_handler(priv->l3cfg, &priv->l3cfg_notify_id); + + g_object_unref(priv->l3cfg); + + nm_g_variant_unref(priv->v_address_data); + nm_g_variant_unref(priv->v_addresses); + nm_g_variant_unref(priv->v_route_data); + nm_g_variant_unref(priv->v_routes); + + nmp_object_unref(priv->v_gateway.best_default_route); + + nm_l3_config_data_unref(priv->l3cd); G_OBJECT_CLASS(nm_ip_config_parent_class)->finalize(object); } @@ -102,26 +220,610 @@ finalize(GObject *object) static void nm_ip_config_class_init(NMIPConfigClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS(klass); + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass); - g_type_class_add_private(object_class, sizeof(NMIPConfigPrivate)); - - object_class->get_property = get_property; + object_class->get_property = get_property_ip; object_class->set_property = set_property; + object_class->constructed = constructed; object_class->finalize = finalize; - obj_properties[PROP_L3CFG] = + dbus_object_class->export_on_construction = TRUE; + + obj_properties_ip[PROP_IP_L3CFG] = g_param_spec_pointer(NM_IP_CONFIG_L3CFG, "", "", G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_IS_VPN] = - g_param_spec_boolean(NM_IP_CONFIG_IS_VPN, + obj_properties_ip[PROP_IP_ADDRESS_DATA] = + g_param_spec_variant(NM_IP_CONFIG_ADDRESS_DATA, "", "", - FALSE, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + G_VARIANT_TYPE("aa{sv}"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties_ip[PROP_IP_GATEWAY] = + g_param_spec_variant(NM_IP_CONFIG_GATEWAY, + "", + "", + G_VARIANT_TYPE("s"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties_ip[PROP_IP_ROUTE_DATA] = + g_param_spec_variant(NM_IP_CONFIG_ROUTE_DATA, + "", + "", + G_VARIANT_TYPE("aa{sv}"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties_ip[PROP_IP_DOMAINS] = + g_param_spec_variant(NM_IP_CONFIG_DOMAINS, + "", + "", + G_VARIANT_TYPE("as"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties_ip[PROP_IP_SEARCHES] = + g_param_spec_variant(NM_IP_CONFIG_SEARCHES, + "", + "", + G_VARIANT_TYPE("as"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties_ip[PROP_IP_DNS_PRIORITY] = + g_param_spec_variant(NM_IP_CONFIG_DNS_PRIORITY, + "", + "", + G_VARIANT_TYPE("i"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties_ip[PROP_IP_DNS_OPTIONS] = + g_param_spec_variant(NM_IP_CONFIG_DNS_OPTIONS, + "", + "", + G_VARIANT_TYPE("as"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST_ip, obj_properties_ip); +} + +/*****************************************************************************/ + +/* public */ +#define NM_IP4_CONFIG_NAMESERVER_DATA "nameserver-data" +#define NM_IP4_CONFIG_WINS_SERVER_DATA "wins-server-data" + +/* deprecated */ +#define NM_IP4_CONFIG_ADDRESSES "addresses" +#define NM_IP4_CONFIG_NAMESERVERS "nameservers" +#define NM_IP4_CONFIG_ROUTES "routes" +#define NM_IP4_CONFIG_WINS_SERVERS "wins-servers" + +typedef struct _NMIP4Config NMIP4Config; +typedef struct _NMIP4ConfigClass NMIP4ConfigClass; + +NM_GOBJECT_PROPERTIES_DEFINE_FULL(_ip4, + NMIP4Config, + PROP_IP4_ADDRESSES, + PROP_IP4_NAMESERVERS, + PROP_IP4_NAMESERVER_DATA, + PROP_IP4_ROUTES, + PROP_IP4_WINS_SERVERS, + PROP_IP4_WINS_SERVER_DATA, ); + +struct _NMIP4Config { + NMIPConfig parent; +}; + +struct _NMIP4ConfigClass { + NMIPConfigClass parent; +}; + +G_DEFINE_TYPE(NMIP4Config, nm_ip4_config, NM_TYPE_IP_CONFIG) + +static void +get_property_ip4(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMIPConfig * self = NM_IP_CONFIG(object); + NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); + char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + GVariantBuilder builder; + const in_addr_t * addrs; + guint len; + guint i; + + switch (prop_id) { + case PROP_IP4_ADDRESSES: + g_value_set_variant(value, priv->v_addresses); + break; + case PROP_IP4_ROUTES: + 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}")); + for (i = 0; i < len; i++) { + GVariantBuilder nested_builder; + + _nm_utils_inet4_ntop(addrs[i], 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)); + } + break; + case PROP_IP4_WINS_SERVERS: + addrs = nm_l3_config_data_get_wins(priv->l3cd, &len); + g_value_set_variant(value, + (len == 0) ? nm_g_variant_singleton_au() + : nm_g_variant_new_au(addrs, len)); + break; + case PROP_IP4_WINS_SERVER_DATA: + addrs = nm_l3_config_data_get_wins(priv->l3cd, &len); + if (len == 0) + g_value_set_variant(value, nm_g_variant_singleton_as()); + 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_value_take_variant(value, g_variant_builder_end(&builder)); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static const NMDBusInterfaceInfoExtended interface_info_ip4_config = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT( + NM_DBUS_INTERFACE_IP4_CONFIG, + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Addresses", + "aau", + NM_IP4_CONFIG_ADDRESSES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("AddressData", + "aa{sv}", + NM_IP_CONFIG_ADDRESS_DATA), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Gateway", "s", NM_IP_CONFIG_GATEWAY), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Routes", "aau", NM_IP4_CONFIG_ROUTES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("RouteData", + "aa{sv}", + NM_IP_CONFIG_ROUTE_DATA), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("NameserverData", + "aa{sv}", + NM_IP4_CONFIG_NAMESERVER_DATA), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Nameservers", + "au", + NM_IP4_CONFIG_NAMESERVERS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Domains", "as", NM_IP_CONFIG_DOMAINS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Searches", "as", NM_IP_CONFIG_SEARCHES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DnsOptions", + "as", + NM_IP_CONFIG_DNS_OPTIONS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DnsPriority", + "i", + NM_IP_CONFIG_DNS_PRIORITY), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WinsServerData", + "as", + NM_IP4_CONFIG_WINS_SERVER_DATA), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WinsServers", + "au", + NM_IP4_CONFIG_WINS_SERVERS), ), ), +}; + +static void +nm_ip4_config_init(NMIP4Config *self) +{} + +static void +nm_ip4_config_class_init(NMIP4ConfigClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass); + NMIPConfigClass * ip_config_class = NM_IP_CONFIG_CLASS(klass); + + ip_config_class->addr_family = AF_INET; + + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED(NM_DBUS_PATH "/IP4Config"); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_ip4_config); + + object_class->get_property = get_property_ip4; + + obj_properties_ip4[PROP_IP4_ADDRESSES] = + g_param_spec_variant(NM_IP4_CONFIG_ADDRESSES, + "", + "", + G_VARIANT_TYPE("aau"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties_ip4[PROP_IP4_ROUTES] = + g_param_spec_variant(NM_IP4_CONFIG_ROUTES, + "", + "", + G_VARIANT_TYPE("aau"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties_ip4[PROP_IP4_NAMESERVER_DATA] = + g_param_spec_variant(NM_IP4_CONFIG_NAMESERVER_DATA, + "", + "", + G_VARIANT_TYPE("aa{sv}"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties_ip4[PROP_IP4_NAMESERVERS] = + g_param_spec_variant(NM_IP4_CONFIG_NAMESERVERS, + "", + "", + G_VARIANT_TYPE("au"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties_ip4[PROP_IP4_WINS_SERVER_DATA] = + g_param_spec_variant(NM_IP4_CONFIG_WINS_SERVER_DATA, + "", + "", + G_VARIANT_TYPE("as"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties_ip4[PROP_IP4_WINS_SERVERS] = + g_param_spec_variant(NM_IP4_CONFIG_WINS_SERVERS, + "", + "", + G_VARIANT_TYPE("au"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST_ip4, obj_properties_ip4); +} + +/*****************************************************************************/ + +/* public */ +#define NM_IP6_CONFIG_NAMESERVERS "nameservers" + +/* deprecated */ +#define NM_IP6_CONFIG_ADDRESSES "addresses" +#define NM_IP6_CONFIG_ROUTES "routes" + +typedef struct _NMIP6Config NMIP6Config; +typedef struct _NMIP6ConfigClass NMIP6ConfigClass; + +NM_GOBJECT_PROPERTIES_DEFINE_FULL(_ip6, + NMIP6Config, + PROP_IP6_NAMESERVERS, + PROP_IP6_ADDRESSES, + PROP_IP6_ROUTES, ); + +struct _NMIP6Config { + NMIPConfig parent; +}; + +struct _NMIP6ConfigClass { + NMIPConfigClass parent; +}; + +G_DEFINE_TYPE(NMIP6Config, nm_ip6_config, NM_TYPE_IP_CONFIG) + +static const NMDBusInterfaceInfoExtended interface_info_ip6_config = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT( + NM_DBUS_INTERFACE_IP6_CONFIG, + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Addresses", + "a(ayuay)", + NM_IP6_CONFIG_ADDRESSES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("AddressData", + "aa{sv}", + NM_IP_CONFIG_ADDRESS_DATA), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Gateway", "s", NM_IP_CONFIG_GATEWAY), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Routes", + "a(ayuayu)", + NM_IP6_CONFIG_ROUTES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("RouteData", + "aa{sv}", + NM_IP_CONFIG_ROUTE_DATA), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Nameservers", + "aay", + NM_IP6_CONFIG_NAMESERVERS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Domains", "as", NM_IP_CONFIG_DOMAINS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Searches", "as", NM_IP_CONFIG_SEARCHES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DnsOptions", + "as", + NM_IP_CONFIG_DNS_OPTIONS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DnsPriority", + "i", + NM_IP_CONFIG_DNS_PRIORITY), ), ), +}; + +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; + + switch (prop_id) { + case PROP_IP6_ADDRESSES: + g_value_set_variant(value, priv->v_addresses); + break; + case PROP_IP6_ROUTES: + 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); + 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])); + g_value_take_variant(value, g_variant_builder_end(&builder)); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nm_ip6_config_init(NMIP6Config *self) +{} + +static void +nm_ip6_config_class_init(NMIP6ConfigClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass); + NMIPConfigClass * ip_config_class = NM_IP_CONFIG_CLASS(klass); + + ip_config_class->addr_family = AF_INET6; + + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED(NM_DBUS_PATH "/IP6Config"); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_ip6_config); + + object_class->get_property = get_property_ip6; + + obj_properties_ip6[PROP_IP6_ADDRESSES] = + g_param_spec_variant(NM_IP6_CONFIG_ADDRESSES, + "", + "", + G_VARIANT_TYPE("a(ayuay)"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties_ip6[PROP_IP6_ROUTES] = + g_param_spec_variant(NM_IP6_CONFIG_ROUTES, + "", + "", + G_VARIANT_TYPE("a(ayuayu)"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties_ip6[PROP_IP6_NAMESERVERS] = + g_param_spec_variant(NM_IP6_CONFIG_NAMESERVERS, + "", + "", + G_VARIANT_TYPE("aay"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST_ip6, obj_properties_ip6); +} + +/*****************************************************************************/ + +#define _notify_all(self, changed_params, n_changed_params) \ + G_STMT_START \ + { \ + NMIPConfig *const _self = (self); \ + const guint _n_changed_params = (n_changed_params); \ + GParamSpec *const *const _changed_params = (changed_params); \ + guint _i; \ + \ + if (_n_changed_params > 0) { \ + nm_assert(_n_changed_params <= G_N_ELEMENTS(changed_params)); \ + if (_n_changed_params > 1) \ + g_object_freeze_notify(G_OBJECT(_self)); \ + for (_i = 0; _i < _n_changed_params; _i++) \ + g_object_notify_by_pspec(G_OBJECT(_self), _changed_params[_i]); \ + if (_n_changed_params > 1) \ + g_object_thaw_notify(G_OBJECT(_self)); \ + } \ + } \ + G_STMT_END + +static void +_handle_l3cd_changed(NMIPConfig *self, const NML3ConfigData *l3cd) +{ + const int addr_family = nm_ip_config_get_addr_family(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMIPConfigPrivate * priv = NM_IP_CONFIG_GET_PRIVATE(self); + 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; + gconstpointer addrs; + gconstpointer addrs_old; + guint len; + guint len_old; + int v_i; + int v_i_old; + + 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))) { + 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]; + } else + 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)) + 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)) + 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); + v_i = nm_l3_config_data_get_dns_priority_or_default(priv->l3cd, addr_family); + 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)) + changed_params[n_changed_params++] = obj_properties_ip[PROP_IP_DNS_OPTIONS]; + + if (IS_IPv4) { + addrs_old = nm_l3_config_data_get_wins(l3cd_old, &len_old); + addrs = nm_l3_config_data_get_wins(priv->l3cd, &len); + if (!nm_memeq_n(addrs_old, len_old, addrs, len, sizeof(in_addr_t))) { + changed_params[n_changed_params++] = obj_properties_ip4[PROP_IP4_WINS_SERVER_DATA]; + changed_params[n_changed_params++] = obj_properties_ip4[PROP_IP4_WINS_SERVERS]; + } + } + + _notify_all(self, changed_params, n_changed_params); +} + +static void +_handle_platform_change(NMIPConfig *self, guint32 obj_type_flags, gboolean is_init) +{ + const int addr_family = nm_ip_config_get_addr_family(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMIPConfigPrivate * priv = NM_IP_CONFIG_GET_PRIVATE(self); + GParamSpec * changed_params[5]; + guint n_changed_params = 0; + const NMDedupMultiHeadEntry *head_entry_routes = NULL; + gboolean best_default_route_changed = FALSE; + + if (NM_FLAGS_ANY(obj_type_flags, + (nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)) + | nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4))))) { + const NMPObject *best_default_route = NULL; + + head_entry_routes = nm_platform_lookup_object(nm_l3cfg_get_platform(priv->l3cfg), + NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4), + nm_l3cfg_get_ifindex(priv->l3cfg)); + if (head_entry_routes) { + NMDedupMultiIter iter; + const NMPObject *obj; + + nm_dedup_multi_iter_init(&iter, head_entry_routes); + while (nm_platform_dedup_multi_iter_next_obj(&iter, + &obj, + NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4))) { + const NMPlatformIPXRoute *r = NMP_OBJECT_CAST_IPX_ROUTE(obj); + + /* Determine the gateway. That is the next hop of a route + * - 0.0.0.0/0 or ::/0 + * - type=unicast + * - table=main + */ + if (r->rx.plen != 0 + || r->rx.type_coerced != nm_platform_route_type_coerce(RTN_UNICAST) + || r->rx.table_coerced != nm_platform_route_table_coerce(RT_TABLE_MAIN) + || !nm_ip_addr_is_null(addr_family, r->rx.network_ptr)) + continue; + + if (!best_default_route + || NMP_OBJECT_CAST_IP_ROUTE(best_default_route)->metric > r->rx.metric) + best_default_route = obj; + } + } + + if (nmp_object_ref_set(&priv->v_gateway.best_default_route, best_default_route)) { + gconstpointer gateway_next_hop; + + gateway_next_hop = priv->v_gateway.best_default_route + ? nm_platform_ip_route_get_gateway( + addr_family, + NMP_OBJECT_CAST_IP_ROUTE(priv->v_gateway.best_default_route)) + : &nm_ip_addr_zero; + if (!nm_ip_addr_equal(addr_family, &priv->v_gateway.addr, gateway_next_hop)) { + nm_ip_addr_set(addr_family, &priv->v_gateway.addr, gateway_next_hop); + best_default_route_changed = TRUE; + } + } + } + + if (best_default_route_changed + || NM_FLAGS_ANY(obj_type_flags, + nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)))) { + gs_unref_variant GVariant *x_address_data = NULL; + gs_unref_variant GVariant *x_addresses = NULL; + + nm_utils_ip_addresses_to_dbus(addr_family, + nm_platform_lookup_object(nm_l3cfg_get_platform(priv->l3cfg), + NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), + nm_l3cfg_get_ifindex(priv->l3cfg)), + priv->v_gateway.best_default_route, + &x_address_data, + &x_addresses); + + if (!nm_g_variant_equal(priv->v_address_data, x_address_data)) { + changed_params[n_changed_params++] = obj_properties_ip[PROP_IP_ADDRESS_DATA]; + g_variant_ref_sink(x_address_data); + NM_SWAP(&priv->v_address_data, &x_address_data); + } + if (!nm_g_variant_equal(priv->v_addresses, x_addresses)) { + changed_params[n_changed_params++] = IS_IPv4 ? obj_properties_ip4[PROP_IP4_ADDRESSES] + : obj_properties_ip6[PROP_IP6_ADDRESSES]; + g_variant_ref_sink(x_addresses); + NM_SWAP(&priv->v_addresses, &x_addresses); + } + } + + if (best_default_route_changed) + changed_params[n_changed_params++] = obj_properties_ip[PROP_IP_GATEWAY]; + + if (NM_FLAGS_ANY(obj_type_flags, nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)))) { + gs_unref_variant GVariant *x_route_data = NULL; + gs_unref_variant GVariant *x_routes = NULL; + + nm_utils_ip_routes_to_dbus(addr_family, head_entry_routes, &x_route_data, &x_routes); + + if (!nm_g_variant_equal(priv->v_route_data, x_route_data)) { + changed_params[n_changed_params++] = obj_properties_ip[PROP_IP_ROUTE_DATA]; + g_variant_ref_sink(x_route_data); + NM_SWAP(&priv->v_route_data, &x_route_data); + } + if (!nm_g_variant_equal(priv->v_routes, x_routes)) { + changed_params[n_changed_params++] = + IS_IPv4 ? obj_properties_ip4[PROP_IP4_ROUTES] : obj_properties_ip6[PROP_IP6_ROUTES]; + g_variant_ref_sink(x_routes); + NM_SWAP(&priv->v_routes, &x_routes); + } + } + + if (!is_init) + _notify_all(self, changed_params, n_changed_params); } diff --git a/src/core/nm-ip-config.h b/src/core/nm-ip-config.h index 8e1a593412..e3c3b30a96 100644 --- a/src/core/nm-ip-config.h +++ b/src/core/nm-ip-config.h @@ -7,6 +7,7 @@ #define __NM_IP_CONFIG_H__ #include "nm-dbus-object.h" +#include "nm-l3cfg.h" /*****************************************************************************/ @@ -19,26 +20,66 @@ #define NM_IP_CONFIG_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_IP_CONFIG, NMIPConfigClass)) -#define NM_IP_CONFIG_L3CFG "l3cfg" -#define NM_IP_CONFIG_IS_VPN "is-vpn" +#define NM_IP_CONFIG_L3CFG "l3cfg" -struct _NMIPConfigPrivate; +struct _NMIPConfigPrivate { + NML3Cfg * l3cfg; + const NML3ConfigData *l3cd; + GVariant * v_address_data; + GVariant * v_addresses; + GVariant * v_route_data; + GVariant * v_routes; + struct { + const NMPObject *best_default_route; + NMIPAddr addr; + } v_gateway; + gulong l3cfg_notify_id; +}; struct _NMIPConfig { - NMDBusObject parent; - struct _NMIPConfigPrivate *_priv; + NMDBusObject parent; + struct _NMIPConfigPrivate _priv; }; typedef struct { NMDBusObjectClass parent; - gboolean is_ipv4; int addr_family; } NMIPConfigClass; GType nm_ip_config_get_type(void); -GType nm_ip4_config_get_type(void); -GType nm_ip6_config_get_type(void); -NMIPConfig *nm_ip_config_new(int addr_family, NML3Cfg *l3cfg, gboolean is_vpn); +NMIPConfig *nm_ip_config_new(int addr_family, NML3Cfg *l3cfg); + +void nm_ip_config_take_and_unexport_on_idle(NMIPConfig *self_take); + +/*****************************************************************************/ + +static inline NML3Cfg * +nm_ip_config_get_l3cfg(NMIPConfig *self) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(self), NULL); + + return self->_priv.l3cfg; +} + +static inline struct _NMDedupMultiIndex * +nm_ip_config_get_multi_index(NMIPConfig *self) +{ + return nm_l3cfg_get_multi_idx(nm_ip_config_get_l3cfg(self)); +} + +static inline int +nm_ip_config_get_ifindex(NMIPConfig *self) +{ + return nm_l3cfg_get_ifindex(nm_ip_config_get_l3cfg(self)); +} + +static inline int +nm_ip_config_get_addr_family(NMIPConfig *self) +{ + g_return_val_if_fail(NM_IS_IP_CONFIG(self), AF_UNSPEC); + + return NM_IP_CONFIG_GET_CLASS(self)->addr_family; +} #endif /* __NM_IP_CONFIG_H__ */ diff --git a/src/core/nm-ip4-config.c b/src/core/nm-ip4-config.c deleted file mode 100644 index 90398dcc07..0000000000 --- a/src/core/nm-ip4-config.c +++ /dev/null @@ -1,3337 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2005 - 2017 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#include "src/core/nm-default-daemon.h" - -#include "nm-ip4-config.h" - -#include -#include -#include - -#include "libnm-glib-aux/nm-dedup-multi.h" - -#include "nm-utils.h" -#include "libnm-platform/nmp-object.h" -#include "libnm-platform/nm-platform.h" -#include "libnm-platform/nm-platform-utils.h" -#include "NetworkManagerUtils.h" -#include "libnm-core-intern/nm-core-internal.h" -#include "nm-dbus-object.h" - -/*****************************************************************************/ - -/* internal guint32 are assigned to gobject properties of type uint. Ensure, that uint is large enough */ -G_STATIC_ASSERT(sizeof(uint) >= sizeof(guint32)); -G_STATIC_ASSERT(G_MAXUINT >= 0xFFFFFFFF); - -/*****************************************************************************/ - -static gboolean -_route_valid(const NMPlatformIP4Route *r) -{ - return r && r->plen <= 32 - && r->network == nm_utils_ip4_address_clear_host_address(r->network, r->plen); -} - -/*****************************************************************************/ - -static void -_idx_obj_id_hash_update(const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj * obj, - NMHashState * h) -{ - nmp_object_id_hash_update((NMPObject *) obj, h); -} - -static gboolean -_idx_obj_id_equal(const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj * obj_a, - const NMDedupMultiObj * obj_b) -{ - return nmp_object_id_equal((NMPObject *) obj_a, (NMPObject *) obj_b); -} - -void -nm_ip_config_dedup_multi_idx_type_init(NMIPConfigDedupMultiIdxType *idx_type, - NMPObjectType obj_type) -{ - static const NMDedupMultiIdxTypeClass idx_type_class = { - .idx_obj_id_hash_update = _idx_obj_id_hash_update, - .idx_obj_id_equal = _idx_obj_id_equal, - }; - - nm_dedup_multi_idx_type_init((NMDedupMultiIdxType *) idx_type, &idx_type_class); - idx_type->obj_type = obj_type; -} - -/*****************************************************************************/ - -gboolean -_nm_ip_config_add_obj(NMDedupMultiIndex * multi_idx, - NMIPConfigDedupMultiIdxType *idx_type, - int ifindex, - const NMPObject * obj_new, - const NMPlatformObject * pl_new, - gboolean merge, - gboolean append_force, - const NMPObject ** out_obj_old /* returns a reference! */, - const NMPObject ** out_obj_new /* does not return a reference */) -{ - NMPObject obj_new_stackinit; - const NMDedupMultiEntry *entry_old; - const NMDedupMultiEntry *entry_new; - - nm_assert(multi_idx); - nm_assert(idx_type); - nm_assert(NM_IN_SET(idx_type->obj_type, - NMP_OBJECT_TYPE_IP4_ADDRESS, - NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ADDRESS, - NMP_OBJECT_TYPE_IP6_ROUTE)); - nm_assert(ifindex > 0); - - /* we go through extra lengths to accept a full obj_new object. That one, - * can be reused by increasing the ref-count. */ - if (!obj_new) { - nm_assert(pl_new); - obj_new = nmp_object_stackinit(&obj_new_stackinit, idx_type->obj_type, pl_new); - NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(&obj_new_stackinit)->ifindex = ifindex; - } else { - nm_assert(!pl_new); - nm_assert(NMP_OBJECT_GET_TYPE(obj_new) == idx_type->obj_type); - if (NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj_new)->ifindex != ifindex) { - obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new); - NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(&obj_new_stackinit)->ifindex = ifindex; - } - } - nm_assert(NMP_OBJECT_GET_TYPE(obj_new) == idx_type->obj_type); - nm_assert(nmp_object_is_alive(obj_new)); - - entry_old = nm_dedup_multi_index_lookup_obj(multi_idx, &idx_type->parent, obj_new); - - if (entry_old) { - gboolean modified = FALSE; - const NMPObject *obj_old = entry_old->obj; - - if (nmp_object_equal(obj_new, obj_old)) { - nm_dedup_multi_entry_set_dirty(entry_old, FALSE); - goto append_force_and_out; - } - - /* if @merge, we merge the new object with the existing one. - * Otherwise, we replace it entirely. */ - if (merge) { - switch (idx_type->obj_type) { - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP6_ADDRESS: - /* for addresses that we read from the kernel, we keep the timestamps as defined - * by the previous source (item_old). The reason is, that the other source configured the lifetimes - * with "what should be" and the kernel values are "what turned out after configuring it". - * - * For other sources, the longer lifetime wins. */ - if ((obj_new->ip_address.addr_source == NM_IP_CONFIG_SOURCE_KERNEL - && obj_old->ip_address.addr_source != NM_IP_CONFIG_SOURCE_KERNEL) - || nm_platform_ip_address_cmp_expiry(NMP_OBJECT_CAST_IP_ADDRESS(obj_old), - NMP_OBJECT_CAST_IP_ADDRESS(obj_new)) - > 0) { - obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new); - obj_new_stackinit.ip_address.timestamp = - NMP_OBJECT_CAST_IP_ADDRESS(obj_old)->timestamp; - obj_new_stackinit.ip_address.lifetime = - NMP_OBJECT_CAST_IP_ADDRESS(obj_old)->lifetime; - obj_new_stackinit.ip_address.preferred = - NMP_OBJECT_CAST_IP_ADDRESS(obj_old)->preferred; - modified = TRUE; - } - - /* keep the maximum addr_source. */ - if (obj_new->ip_address.addr_source < obj_old->ip_address.addr_source) { - obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new); - obj_new_stackinit.ip_address.addr_source = obj_old->ip_address.addr_source; - modified = TRUE; - } - break; - case NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - /* keep the maximum rt_source. */ - if (obj_new->ip_route.rt_source < obj_old->ip_route.rt_source) { - obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new); - obj_new_stackinit.ip_route.rt_source = obj_old->ip_route.rt_source; - modified = TRUE; - } - if (!obj_new->ip_route.is_external && obj_old->ip_route.is_external) { - obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new); - obj_new_stackinit.ip_route.is_external = FALSE; - modified = TRUE; - } - break; - default: - nm_assert_not_reached(); - break; - } - - if (modified && nmp_object_equal(obj_new, obj_old)) { - nm_dedup_multi_entry_set_dirty(entry_old, FALSE); - goto append_force_and_out; - } - } - } - - if (!nm_dedup_multi_index_add_full(multi_idx, - &idx_type->parent, - obj_new, - append_force ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE - : NM_DEDUP_MULTI_IDX_MODE_APPEND, - NULL, - entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING, - NULL, - &entry_new, - out_obj_old)) { - nm_assert_not_reached(); - NM_SET_OUT(out_obj_new, NULL); - return FALSE; - } - - NM_SET_OUT(out_obj_new, entry_new->obj); - return TRUE; - -append_force_and_out: - NM_SET_OUT(out_obj_old, nmp_object_ref(entry_old->obj)); - NM_SET_OUT(out_obj_new, entry_old->obj); - if (append_force) { - if (nm_dedup_multi_entry_reorder(entry_old, NULL, TRUE)) - return TRUE; - } - return FALSE; -} - -/** - * _nm_ip_config_lookup_ip_route: - * @multi_idx: - * @idx_type: - * @needle: - * @cmp_type: after lookup, filter the result by comparing with @cmp_type. Only - * return the result, if it compares equal to @needle according to this @cmp_type. - * Note that the index uses %NM_PLATFORM_IP_ROUTE_CMP_TYPE_DST type, so passing - * that compare-type means not to filter any further. - * - * Returns: the found entry or %NULL. - */ -const NMDedupMultiEntry * -_nm_ip_config_lookup_ip_route(const NMDedupMultiIndex * multi_idx, - const NMIPConfigDedupMultiIdxType *idx_type, - const NMPObject * needle, - NMPlatformIPRouteCmpType cmp_type) -{ - const NMDedupMultiEntry *entry; - - nm_assert(multi_idx); - nm_assert(idx_type); - nm_assert(NM_IN_SET(idx_type->obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - nm_assert(NMP_OBJECT_GET_TYPE(needle) == idx_type->obj_type); - - entry = nm_dedup_multi_index_lookup_obj(multi_idx, &idx_type->parent, needle); - if (!entry) - return NULL; - - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { - nm_assert((NMP_OBJECT_GET_TYPE(needle) == NMP_OBJECT_TYPE_IP4_ROUTE - && nm_platform_ip4_route_cmp(NMP_OBJECT_CAST_IP4_ROUTE(entry->obj), - NMP_OBJECT_CAST_IP4_ROUTE(needle), - cmp_type) - == 0) - || (NMP_OBJECT_GET_TYPE(needle) == NMP_OBJECT_TYPE_IP6_ROUTE - && nm_platform_ip6_route_cmp(NMP_OBJECT_CAST_IP6_ROUTE(entry->obj), - NMP_OBJECT_CAST_IP6_ROUTE(needle), - cmp_type) - == 0)); - } else { - if (NMP_OBJECT_GET_TYPE(needle) == NMP_OBJECT_TYPE_IP4_ROUTE) { - if (nm_platform_ip4_route_cmp(NMP_OBJECT_CAST_IP4_ROUTE(entry->obj), - NMP_OBJECT_CAST_IP4_ROUTE(needle), - cmp_type) - != 0) - return NULL; - } else { - if (nm_platform_ip6_route_cmp(NMP_OBJECT_CAST_IP6_ROUTE(entry->obj), - NMP_OBJECT_CAST_IP6_ROUTE(needle), - cmp_type) - != 0) - return NULL; - } - } - return entry; -} - -/*****************************************************************************/ - -NM_GOBJECT_PROPERTIES_DEFINE(NMIP4Config, - PROP_MULTI_IDX, - PROP_IFINDEX, - PROP_ADDRESS_DATA, - PROP_ADDRESSES, - PROP_ROUTE_DATA, - PROP_ROUTES, - PROP_GATEWAY, - PROP_NAMESERVER_DATA, - PROP_NAMESERVERS, - PROP_DOMAINS, - PROP_SEARCHES, - PROP_DNS_OPTIONS, - PROP_WINS_SERVER_DATA, - PROP_WINS_SERVERS, - PROP_DNS_PRIORITY, ); - -typedef struct { - bool metered : 1; - bool never_default : 1; - guint32 mtu; - int ifindex; - NMIPConfigSource mtu_source; - int dns_priority; - NMSettingConnectionMdns mdns; - NMSettingConnectionLlmnr llmnr; - NMSettingConnectionDnsOverTls dns_over_tls; - GArray * nameservers; - GPtrArray * domains; - GPtrArray * searches; - GPtrArray * dns_options; - GArray * nis; - char * nis_domain; - GArray * wins; - GVariant * address_data_variant; - GVariant * addresses_variant; - GVariant * route_data_variant; - GVariant * routes_variant; - NMDedupMultiIndex * multi_idx; - const NMPObject * best_default_route; - union { - NMIPConfigDedupMultiIdxType idx_ip4_addresses_; - NMDedupMultiIdxType idx_ip4_addresses; - }; - union { - NMIPConfigDedupMultiIdxType idx_ip4_routes_; - NMDedupMultiIdxType idx_ip4_routes; - }; - NMIPConfigFlags config_flags; -} NMIP4ConfigPrivate; - -struct _NMIP4Config { - NMIPConfig parent; - NMIP4ConfigPrivate _priv; -}; - -struct _NMIP4ConfigClass { - NMIPConfigClass parent; -}; - -G_DEFINE_TYPE(NMIP4Config, nm_ip4_config, NM_TYPE_IP_CONFIG) - -#define NM_IP4_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMIP4Config, NM_IS_IP4_CONFIG) - -/*****************************************************************************/ - -static void -_add_address(NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Address *new); -static void _add_route(NMIP4Config * self, - const NMPObject *obj_new, - const NMPlatformIP4Route *new, - const NMPObject **out_obj_new); -static const NMDedupMultiEntry * -_lookup_route(const NMIP4Config *self, const NMPObject *needle, NMPlatformIPRouteCmpType cmp_type); - -/*****************************************************************************/ - -int -nm_ip4_config_get_ifindex(const NMIP4Config *self) -{ - return NM_IP4_CONFIG_GET_PRIVATE(self)->ifindex; -} - -NMDedupMultiIndex * -nm_ip4_config_get_multi_idx(const NMIP4Config *self) -{ - return NM_IP4_CONFIG_GET_PRIVATE(self)->multi_idx; -} - -/*****************************************************************************/ - -const NMDedupMultiHeadEntry * -nm_ip4_config_lookup_addresses(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return nm_dedup_multi_index_lookup_head(priv->multi_idx, &priv->idx_ip4_addresses, NULL); -} - -void -nm_ip_config_iter_ip4_address_init(NMDedupMultiIter *ipconf_iter, const NMIP4Config *self) -{ - nm_assert(NM_IS_IP4_CONFIG(self)); - - nm_dedup_multi_iter_init(ipconf_iter, nm_ip4_config_lookup_addresses(self)); -} - -/*****************************************************************************/ - -const NMDedupMultiHeadEntry * -nm_ip4_config_lookup_routes(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return nm_dedup_multi_index_lookup_head(priv->multi_idx, &priv->idx_ip4_routes, NULL); -} - -void -nm_ip_config_iter_ip4_route_init(NMDedupMultiIter *ipconf_iter, const NMIP4Config *self) -{ - nm_assert(NM_IS_IP4_CONFIG(self)); - - nm_dedup_multi_iter_init(ipconf_iter, nm_ip4_config_lookup_routes(self)); -} - -/*****************************************************************************/ - -const NMPObject * -_nm_ip_config_best_default_route_find_better(const NMPObject *obj_cur, const NMPObject *obj_cmp) -{ - nm_assert(!obj_cur - || NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_cur), - NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ROUTE)); - nm_assert(!obj_cmp - || (!obj_cur - && NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_cmp), - NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ROUTE)) - || NMP_OBJECT_GET_TYPE(obj_cur) == NMP_OBJECT_GET_TYPE(obj_cmp)); - nm_assert(!obj_cur || nmp_object_ip_route_is_best_default_route(obj_cur)); - - /* assumes that @obj_cur is already the best default route (or NULL). It checks whether - * @obj_cmp is also a default route and returns the best of both. */ - if (obj_cmp && nmp_object_ip_route_is_best_default_route(obj_cmp)) { - guint32 metric_cur, metric_cmp; - - if (!obj_cur) - return obj_cmp; - - metric_cur = NMP_OBJECT_CAST_IP_ROUTE(obj_cur)->metric; - metric_cmp = NMP_OBJECT_CAST_IP_ROUTE(obj_cmp)->metric; - - if (metric_cmp < metric_cur) - return obj_cmp; - - if (metric_cmp == metric_cur) { - int c; - - /* Routes have the same metric. We still want to deterministically - * prefer one or the other. It's important to consistently choose one - * or the other, so that the order doesn't matter how routes are added - * (and merged). */ - c = nmp_object_cmp(obj_cur, obj_cmp); - if (c != 0) - return c < 0 ? obj_cur : obj_cmp; - - /* as last resort, compare pointers. */ - if (obj_cmp < obj_cur) - return obj_cmp; - } - } - return obj_cur; -} - -gboolean -_nm_ip_config_best_default_route_merge(const NMPObject **best_default_route, - const NMPObject * new_candidate) -{ - new_candidate = - _nm_ip_config_best_default_route_find_better(*best_default_route, new_candidate); - return nmp_object_ref_set(best_default_route, new_candidate); -} - -const NMPObject * -nm_ip4_config_best_default_route_get(const NMIP4Config *self) -{ - g_return_val_if_fail(NM_IS_IP4_CONFIG(self), NULL); - - return NM_IP4_CONFIG_GET_PRIVATE(self)->best_default_route; -} - -const NMPObject * -_nm_ip4_config_best_default_route_find(const NMIP4Config *self) -{ - NMDedupMultiIter ipconf_iter; - const NMPObject *new_best_default_route = NULL; - - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, NULL) { - new_best_default_route = - _nm_ip_config_best_default_route_find_better(new_best_default_route, - ipconf_iter.current->obj); - } - return new_best_default_route; -} - -in_addr_t -nmtst_ip4_config_get_gateway(NMIP4Config *config) -{ - const NMPObject *rt; - - g_assert(NM_IS_IP4_CONFIG(config)); - - rt = nm_ip4_config_best_default_route_get(config); - if (!rt) - return 0; - return NMP_OBJECT_CAST_IP4_ROUTE(rt)->gateway; -} - -/*****************************************************************************/ - -static void -_notify_addresses(NMIP4Config *self) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - nm_clear_g_variant(&priv->address_data_variant); - nm_clear_g_variant(&priv->addresses_variant); - nm_gobject_notify_together(self, PROP_ADDRESS_DATA, PROP_ADDRESSES); -} - -static void -_notify_routes(NMIP4Config *self) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - nm_assert(priv->best_default_route == _nm_ip4_config_best_default_route_find(self)); - nm_clear_g_variant(&priv->route_data_variant); - nm_clear_g_variant(&priv->routes_variant); - nm_gobject_notify_together(self, PROP_ROUTE_DATA, PROP_ROUTES); -} - -/*****************************************************************************/ - -NMIP4Config * -nm_ip4_config_clone(const NMIP4Config *self) -{ - NMIP4Config *copy; - - copy = nm_ip4_config_new(nm_ip4_config_get_multi_idx(self), -1); - nm_ip4_config_replace(copy, self, NULL); - - return copy; -} - -NMIP4Config * -nm_ip4_config_capture(NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex) -{ - NMIP4Config * self; - NMIP4ConfigPrivate * priv; - const NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIter iter; - const NMPObject * plobj = NULL; - - nm_assert(ifindex > 0); - - /* Slaves have no IP configuration */ - if (nm_platform_link_get_master(platform, ifindex) > 0) - return NULL; - - self = nm_ip4_config_new(multi_idx, ifindex); - priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - head_entry = nm_platform_lookup_object(platform, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex); - if (head_entry) { - nmp_cache_iter_for_each (&iter, head_entry, &plobj) { - if (!_nm_ip_config_add_obj(priv->multi_idx, - &priv->idx_ip4_addresses_, - ifindex, - plobj, - NULL, - FALSE, - TRUE, - NULL, - NULL)) - nm_assert_not_reached(); - } - _notify_addresses(self); - } - - head_entry = nm_platform_lookup_object(platform, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex); - - /* Extract gateway from default route */ - nmp_cache_iter_for_each (&iter, head_entry, &plobj) - _add_route(self, plobj, NULL, NULL); - - return self; -} - -void -nm_ip4_config_update_routes_metric(NMIP4Config *self, gint64 metric) -{ - gs_free NMPlatformIP4Route *routes = NULL; - gboolean need_update = FALSE; - const NMPlatformIP4Route * r; - NMDedupMultiIter iter; - guint num = 0, i = 0; - - nm_ip_config_iter_ip4_route_for_each (&iter, self, &r) { - if (r->metric != metric) - need_update = TRUE; - num++; - } - if (!need_update) - return; - - routes = g_new(NMPlatformIP4Route, num); - nm_ip_config_iter_ip4_route_for_each (&iter, self, &r) { - routes[i] = *r; - routes[i].metric = metric; - i++; - } - - g_object_freeze_notify(G_OBJECT(self)); - nm_ip4_config_reset_routes(self); - for (i = 0; i < num; i++) - nm_ip4_config_add_route(self, &routes[i], NULL); - g_object_thaw_notify(G_OBJECT(self)); -} - -void -nm_ip4_config_add_dependent_routes(NMIP4Config *self, - guint32 route_table, - guint32 route_metric, - gboolean is_vrf, - GPtrArray ** out_ip4_dev_route_blacklist) -{ - GPtrArray * ip4_dev_route_blacklist = NULL; - const NMPlatformIP4Address *my_addr; - const NMPlatformIP4Route * my_route; - int ifindex; - NMDedupMultiIter iter; - - g_return_if_fail(NM_IS_IP4_CONFIG(self)); - - ifindex = nm_ip4_config_get_ifindex(self); - g_return_if_fail(ifindex > 0); - - /* For IPv6 slaac, we explicitly add the device-routes (onlink) to NMIP6Config. - * As we don't do that for IPv4 (and manual IPv6 addresses), add them explicitly. */ - - nm_ip_config_iter_ip4_address_for_each (&iter, self, &my_addr) { - nm_auto_nmpobj NMPObject *r = NULL; - NMPlatformIP4Route * route; - in_addr_t network; - - if (my_addr->plen == 0) - continue; - - nm_assert(my_addr->plen <= 32); - - /* The destination network depends on the peer-address. */ - network = nm_utils_ip4_address_clear_host_address(my_addr->peer_address, my_addr->plen); - - if (my_addr->external) - continue; - - if (nm_utils_ip4_address_is_zeronet(network)) { - /* Kernel doesn't add device-routes for destinations that - * start with 0.x.y.z. Skip them. */ - continue; - } - - if (my_addr->plen == 32 && my_addr->address == my_addr->peer_address) { - /* Kernel doesn't add device-routes for /32 addresses unless - * they have a peer. */ - continue; - } - - r = nmp_object_new(NMP_OBJECT_TYPE_IP4_ROUTE, NULL); - route = NMP_OBJECT_CAST_IP4_ROUTE(r); - - route->ifindex = ifindex; - route->rt_source = NM_IP_CONFIG_SOURCE_KERNEL; - route->network = network; - route->plen = my_addr->plen; - route->pref_src = my_addr->address; - route->table_coerced = nm_platform_route_table_coerce(route_table); - route->metric = route_metric; - route->scope_inv = nm_platform_route_scope_inv(NM_RT_SCOPE_LINK); - - nm_platform_ip_route_normalize(AF_INET, (NMPlatformIPRoute *) route); - - if (_lookup_route(self, r, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) { - /* we already track this route. Don't add it again. */ - } else - _add_route(self, r, NULL, NULL); - - if (out_ip4_dev_route_blacklist - && (route_table != RT_TABLE_MAIN - || route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE)) { - nm_auto_nmpobj NMPObject *r_dev = NULL; - - r_dev = nmp_object_clone(r, FALSE); - route = NMP_OBJECT_CAST_IP4_ROUTE(r_dev); - route->table_coerced = nm_platform_route_table_coerce(RT_TABLE_MAIN); - route->metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE; - - nm_platform_ip_route_normalize(AF_INET, (NMPlatformIPRoute *) route); - - if (_lookup_route(self, r_dev, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) { - /* we track such a route explicitly. Don't blacklist it. */ - } else { - if (!ip4_dev_route_blacklist) - ip4_dev_route_blacklist = - g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); - - g_ptr_array_add(ip4_dev_route_blacklist, g_steal_pointer(&r_dev)); - } - } - } - -again: - nm_ip_config_iter_ip4_route_for_each (&iter, self, &my_route) { - NMPlatformIP4Route rt; - - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT(my_route) || my_route->gateway == 0 - || NM_IS_IP_CONFIG_SOURCE_RTPROT(my_route->rt_source) - || nm_ip4_config_get_direct_route_for_host( - self, - my_route->gateway, - nm_platform_route_table_uncoerce(my_route->table_coerced, TRUE))) - continue; - - rt = *my_route; - rt.network = my_route->gateway; - rt.plen = 32; - rt.gateway = 0; - _add_route(self, NULL, &rt, NULL); - /* adding the route might have invalidated the iteration. Start again. */ - goto again; - } - - NM_SET_OUT(out_ip4_dev_route_blacklist, ip4_dev_route_blacklist); -} - -gboolean -nm_ip4_config_commit(const NMIP4Config * self, - NMPlatform * platform, - NMIPRouteTableSyncMode route_table_sync) -{ - gs_unref_ptrarray GPtrArray *addresses = NULL; - gs_unref_ptrarray GPtrArray *routes = NULL; - gs_unref_ptrarray GPtrArray *routes_prune = NULL; - int ifindex; - gboolean success = TRUE; - - g_return_val_if_fail(NM_IS_IP4_CONFIG(self), FALSE); - - ifindex = nm_ip4_config_get_ifindex(self); - g_return_val_if_fail(ifindex > 0, FALSE); - - addresses = - nm_dedup_multi_objs_to_ptr_array_head(nm_ip4_config_lookup_addresses(self), NULL, NULL); - - routes = nm_dedup_multi_objs_to_ptr_array_head(nm_ip4_config_lookup_routes(self), NULL, NULL); - - routes_prune = - nm_platform_ip_route_get_prune_list(platform, AF_INET, ifindex, route_table_sync); - - nm_platform_ip4_address_sync(platform, ifindex, addresses); - - if (!nm_platform_ip_route_sync(platform, AF_INET, ifindex, routes, routes_prune, NULL)) - success = FALSE; - - return success; -} - -void -nm_ip4_config_merge_setting(NMIP4Config * self, - NMSettingIPConfig * setting, - NMSettingConnectionMdns mdns, - NMSettingConnectionLlmnr llmnr, - NMSettingConnectionDnsOverTls dns_over_tls, - guint32 route_table, - guint32 route_metric) -{ - guint naddresses, nroutes, nnameservers, nsearches; - int i, priority; - const char *gateway_str; - guint32 gateway_bin; - - if (!setting) - return; - - g_return_if_fail(NM_IS_SETTING_IP4_CONFIG(setting)); - - g_object_freeze_notify(G_OBJECT(self)); - - naddresses = nm_setting_ip_config_get_num_addresses(setting); - nroutes = nm_setting_ip_config_get_num_routes(setting); - nnameservers = nm_setting_ip_config_get_num_dns(setting); - nsearches = nm_setting_ip_config_get_num_dns_searches(setting); - - /* Gateway */ - if (!nm_setting_ip_config_get_never_default(setting) - && (gateway_str = nm_setting_ip_config_get_gateway(setting)) - && inet_pton(AF_INET, gateway_str, &gateway_bin) == 1 && gateway_bin) { - const NMPlatformIP4Route r = { - .rt_source = NM_IP_CONFIG_SOURCE_USER, - .gateway = gateway_bin, - .table_coerced = nm_platform_route_table_coerce(route_table), - .metric = route_metric, - }; - - _add_route(self, NULL, &r, NULL); - } - - /* Addresses */ - for (i = 0; i < naddresses; i++) { - NMIPAddress * s_addr = nm_setting_ip_config_get_address(setting, i); - GVariant * label; - NMPlatformIP4Address address; - - memset(&address, 0, sizeof(address)); - nm_ip_address_get_address_binary(s_addr, &address.address); - address.peer_address = address.address; - address.plen = nm_ip_address_get_prefix(s_addr); - nm_assert(address.plen <= 32); - address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT; - address.preferred = NM_PLATFORM_LIFETIME_PERMANENT; - address.addr_source = NM_IP_CONFIG_SOURCE_USER; - - label = nm_ip_address_get_attribute(s_addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL); - if (label) - g_strlcpy(address.label, g_variant_get_string(label, NULL), sizeof(address.label)); - - _add_address(self, NULL, &address); - } - - /* Routes */ - for (i = 0; i < nroutes; i++) { - NMIPRoute * s_route = nm_setting_ip_config_get_route(setting, i); - NMPlatformIP4Route route; - gint64 m; - - if (nm_ip_route_get_family(s_route) != AF_INET) { - nm_assert_not_reached(); - continue; - } - - memset(&route, 0, sizeof(route)); - nm_ip_route_get_dest_binary(s_route, &route.network); - - route.plen = nm_ip_route_get_prefix(s_route); - nm_assert(route.plen <= 32); - - nm_ip_route_get_next_hop_binary(s_route, &route.gateway); - m = nm_ip_route_get_metric(s_route); - if (m < 0) - route.metric = route_metric; - else - route.metric = m; - route.rt_source = NM_IP_CONFIG_SOURCE_USER; - - route.network = nm_utils_ip4_address_clear_host_address(route.network, route.plen); - - nm_utils_ip_route_attribute_to_platform(AF_INET, - s_route, - NM_PLATFORM_IP_ROUTE_CAST(&route), - route_table); - _add_route(self, NULL, &route, NULL); - } - - /* DNS */ - if (nm_setting_ip_config_get_ignore_auto_dns(setting)) { - nm_ip4_config_reset_nameservers(self); - nm_ip4_config_reset_domains(self); - nm_ip4_config_reset_searches(self); - } - for (i = 0; i < nnameservers; i++) { - guint32 ip; - - if (inet_pton(AF_INET, nm_setting_ip_config_get_dns(setting, i), &ip) == 1) - nm_ip4_config_add_nameserver(self, ip); - } - for (i = 0; i < nsearches; i++) - nm_ip4_config_add_search(self, nm_setting_ip_config_get_dns_search(setting, i)); - - i = 0; - while ((i = nm_setting_ip_config_next_valid_dns_option(setting, i)) >= 0) { - nm_ip4_config_add_dns_option(self, nm_setting_ip_config_get_dns_option(setting, i)); - i++; - } - - priority = nm_setting_ip_config_get_dns_priority(setting); - if (priority) - nm_ip4_config_set_dns_priority(self, priority); - - nm_ip4_config_mdns_set(self, mdns); - nm_ip4_config_llmnr_set(self, llmnr); - nm_ip4_config_dns_over_tls_set(self, dns_over_tls); - - nm_ip4_config_set_never_default(self, nm_setting_ip_config_get_never_default(setting)); - - g_object_thaw_notify(G_OBJECT(self)); -} - -NMSetting * -nm_ip4_config_create_setting(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate * priv; - NMSettingIPConfig * s_ip4; - guint nnameservers, nsearches, noptions; - const char * method = NULL; - int i; - NMDedupMultiIter ipconf_iter; - const NMPlatformIP4Address *address; - const NMPlatformIP4Route * route; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - s_ip4 = NM_SETTING_IP_CONFIG(nm_setting_ip4_config_new()); - - if (!self) { - g_object_set(s_ip4, - NM_SETTING_IP_CONFIG_METHOD, - NM_SETTING_IP4_CONFIG_METHOD_DISABLED, - NULL); - return NM_SETTING(s_ip4); - } - - priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - nnameservers = nm_ip4_config_get_num_nameservers(self); - nsearches = nm_ip4_config_get_num_searches(self); - noptions = nm_ip4_config_get_num_dns_options(self); - - /* Addresses */ - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, self, &address) { - NMIPAddress *s_addr; - - /* Detect dynamic address */ - if (address->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { - method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; - continue; - } - - /* Static address found. */ - if (!method) - method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL; - - s_addr = nm_ip_address_new_binary(AF_INET, &address->address, address->plen, NULL); - if (*address->label) - nm_ip_address_set_attribute(s_addr, - NM_IP_ADDRESS_ATTRIBUTE_LABEL, - g_variant_new_string(address->label)); - - nm_setting_ip_config_add_address(s_ip4, s_addr); - nm_ip_address_unref(s_addr); - } - - /* Gateway */ - if (priv->best_default_route && nm_setting_ip_config_get_num_addresses(s_ip4) > 0) { - g_object_set( - s_ip4, - NM_SETTING_IP_CONFIG_GATEWAY, - _nm_utils_inet4_ntop(NMP_OBJECT_CAST_IP4_ROUTE(priv->best_default_route)->gateway, - sbuf), - NULL); - } - - /* Use 'disabled' if the method wasn't previously set */ - if (!method) - method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - - g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, method, NULL); - - /* Routes */ - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route) { - NMIPRoute *s_route; - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route)) - continue; - - /* Ignore routes provided by external sources */ - if (route->rt_source - != nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER)) - continue; - - s_route = nm_ip_route_new_binary(AF_INET, - &route->network, - route->plen, - &route->gateway, - route->metric, - NULL); - nm_setting_ip_config_add_route(s_ip4, s_route); - nm_ip_route_unref(s_route); - } - - /* DNS */ - for (i = 0; i < nnameservers; i++) { - guint32 nameserver = nm_ip4_config_get_nameserver(self, i); - - nm_setting_ip_config_add_dns(s_ip4, _nm_utils_inet4_ntop(nameserver, sbuf)); - } - for (i = 0; i < nsearches; i++) { - const char *search = nm_ip4_config_get_search(self, i); - - nm_setting_ip_config_add_dns_search(s_ip4, search); - } - - for (i = 0; i < noptions; i++) { - const char *option = nm_ip4_config_get_dns_option(self, i); - - nm_setting_ip_config_add_dns_option(s_ip4, option); - } - - g_object_set(s_ip4, - NM_SETTING_IP_CONFIG_DNS_PRIORITY, - nm_ip4_config_get_dns_priority(self), - NULL); - - return NM_SETTING(s_ip4); -} - -/*****************************************************************************/ - -void -nm_ip4_config_merge(NMIP4Config * dst, - const NMIP4Config * src, - NMIPConfigMergeFlags merge_flags, - guint32 default_route_metric_penalty) -{ - NMIP4ConfigPrivate * dst_priv; - const NMIP4ConfigPrivate * src_priv; - guint32 i; - NMDedupMultiIter ipconf_iter; - const NMPlatformIP4Address *address = NULL; - - g_return_if_fail(src != NULL); - g_return_if_fail(dst != NULL); - - dst_priv = NM_IP4_CONFIG_GET_PRIVATE(dst); - src_priv = NM_IP4_CONFIG_GET_PRIVATE(src); - - g_object_freeze_notify(G_OBJECT(dst)); - - /* addresses */ - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, src, &address) { - if (NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_EXTERNAL) && !address->external) { - NMPlatformIP4Address a; - - a = *address; - a.external = TRUE; - _add_address(dst, NULL, &a); - } else - _add_address(dst, NMP_OBJECT_UP_CAST(address), NULL); - } - - /* nameservers */ - if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { - for (i = 0; i < nm_ip4_config_get_num_nameservers(src); i++) - nm_ip4_config_add_nameserver(dst, nm_ip4_config_get_nameserver(src, i)); - } - - /* routes */ - if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) { - const NMPlatformIP4Route *r_src; - - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, src, &r_src) { - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r_src)) { - if (NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES) - && !NM_FLAGS_HAS(src_priv->config_flags, - NM_IP_CONFIG_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES)) - continue; - if (default_route_metric_penalty) { - NMPlatformIP4Route r = *r_src; - - r.metric = - nm_utils_ip_route_metric_penalize(r.metric, default_route_metric_penalty); - _add_route(dst, NULL, &r, NULL); - continue; - } - } - _add_route(dst, ipconf_iter.current->obj, NULL, NULL); - } - } - - /* domains */ - if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { - for (i = 0; i < nm_ip4_config_get_num_domains(src); i++) - nm_ip4_config_add_domain(dst, nm_ip4_config_get_domain(src, i)); - } - - /* dns searches */ - if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { - for (i = 0; i < nm_ip4_config_get_num_searches(src); i++) - nm_ip4_config_add_search(dst, nm_ip4_config_get_search(src, i)); - } - - /* dns options */ - if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { - for (i = 0; i < nm_ip4_config_get_num_dns_options(src); i++) - nm_ip4_config_add_dns_option(dst, nm_ip4_config_get_dns_option(src, i)); - } - - /* MTU */ - if (src_priv->mtu_source > dst_priv->mtu_source - || (src_priv->mtu_source == dst_priv->mtu_source - && ((!dst_priv->mtu && src_priv->mtu) - || (dst_priv->mtu && src_priv->mtu < dst_priv->mtu)))) - nm_ip4_config_set_mtu(dst, src_priv->mtu, src_priv->mtu_source); - - /* NIS */ - if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { - for (i = 0; i < nm_ip4_config_get_num_nis_servers(src); i++) - nm_ip4_config_add_nis_server(dst, nm_ip4_config_get_nis_server(src, i)); - - if (nm_ip4_config_get_nis_domain(src)) - nm_ip4_config_set_nis_domain(dst, nm_ip4_config_get_nis_domain(src)); - } - - /* WINS */ - if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { - for (i = 0; i < nm_ip4_config_get_num_wins(src); i++) - nm_ip4_config_add_wins(dst, nm_ip4_config_get_wins(src, i)); - } - - /* metered flag */ - nm_ip4_config_set_metered(dst, - nm_ip4_config_get_metered(dst) || nm_ip4_config_get_metered(src)); - - /* never default */ - nm_ip4_config_set_never_default(dst, - nm_ip4_config_get_never_default(dst) - || nm_ip4_config_get_never_default(src)); - - /* DNS priority */ - if (nm_ip4_config_get_dns_priority(src)) - nm_ip4_config_set_dns_priority(dst, nm_ip4_config_get_dns_priority(src)); - - /* mdns */ - nm_ip4_config_mdns_set(dst, NM_MAX(nm_ip4_config_mdns_get(src), nm_ip4_config_mdns_get(dst))); - /* LLMNR */ - nm_ip4_config_llmnr_set(dst, - NM_MAX(nm_ip4_config_llmnr_get(src), nm_ip4_config_llmnr_get(dst))); - /* dns_over_tls */ - nm_ip4_config_dns_over_tls_set( - dst, - NM_MAX(nm_ip4_config_dns_over_tls_get(src), nm_ip4_config_dns_over_tls_get(dst))); - - g_object_thaw_notify(G_OBJECT(dst)); -} - -/*****************************************************************************/ - -static int -_nameservers_get_index(const NMIP4Config *self, guint32 ns) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - guint i; - - for (i = 0; i < priv->nameservers->len; i++) { - guint32 n = g_array_index(priv->nameservers, guint32, i); - - if (ns == n) - return (int) i; - } - return -1; -} - -static int -_domains_get_index(const NMIP4Config *self, const char *domain) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - guint i; - - for (i = 0; i < priv->domains->len; i++) { - const char *d = g_ptr_array_index(priv->domains, i); - - if (g_strcmp0(domain, d) == 0) - return (int) i; - } - return -1; -} - -static int -_searches_get_index(const NMIP4Config *self, const char *search) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - guint i; - - for (i = 0; i < priv->searches->len; i++) { - const char *s = g_ptr_array_index(priv->searches, i); - - if (g_strcmp0(search, s) == 0) - return (int) i; - } - return -1; -} - -static int -_dns_options_get_index(const NMIP4Config *self, const char *option) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - guint i; - - for (i = 0; i < priv->dns_options->len; i++) { - const char *s = g_ptr_array_index(priv->dns_options, i); - - if (g_strcmp0(option, s) == 0) - return (int) i; - } - return -1; -} - -static int -_nis_servers_get_index(const NMIP4Config *self, guint32 nis_server) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - guint i; - - for (i = 0; i < priv->nis->len; i++) { - guint32 n = g_array_index(priv->nis, guint32, i); - - if (n == nis_server) - return (int) i; - } - return -1; -} - -static int -_wins_get_index(const NMIP4Config *self, guint32 wins_server) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - guint i; - - for (i = 0; i < priv->wins->len; i++) { - guint32 n = g_array_index(priv->wins, guint32, i); - - if (n == wins_server) - return (int) i; - } - return -1; -} - -/*****************************************************************************/ - -/** - * nm_ip4_config_subtract: - * @dst: config from which to remove everything in @src - * @src: config to remove from @dst - * @default_route_metric_penalty: pretend that on source we applied - * a route penalty on the default-route. It means, for default routes - * we don't remove routes that match exactly, but those with a lower - * metric (with the penalty removed). - * - * Removes everything in @src from @dst. - */ -void -nm_ip4_config_subtract(NMIP4Config * dst, - const NMIP4Config *src, - guint32 default_route_metric_penalty) -{ - NMIP4ConfigPrivate * dst_priv; - guint i; - int idx; - const NMPlatformIP4Address *a; - const NMPlatformIP4Route * r; - NMDedupMultiIter ipconf_iter; - gboolean changed; - gboolean changed_default_route; - - g_return_if_fail(src != NULL); - g_return_if_fail(dst != NULL); - - dst_priv = NM_IP4_CONFIG_GET_PRIVATE(dst); - - g_object_freeze_notify(G_OBJECT(dst)); - - /* addresses */ - changed = FALSE; - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, src, &a) { - if (nm_dedup_multi_index_remove_obj(dst_priv->multi_idx, - &dst_priv->idx_ip4_addresses, - NMP_OBJECT_UP_CAST(a), - NULL)) - changed = TRUE; - } - if (changed) - _notify_addresses(dst); - - /* nameservers */ - for (i = 0; i < nm_ip4_config_get_num_nameservers(src); i++) { - idx = _nameservers_get_index(dst, nm_ip4_config_get_nameserver(src, i)); - if (idx >= 0) - nm_ip4_config_del_nameserver(dst, idx); - } - - /* routes */ - changed = FALSE; - changed_default_route = FALSE; - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, src, &r) { - const NMPObject * o_src = NMP_OBJECT_UP_CAST(r); - NMPObject o_lookup_copy; - const NMPObject * o_lookup; - nm_auto_nmpobj const NMPObject *obj_old = NULL; - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r) && default_route_metric_penalty) { - NMPlatformIP4Route *rr; - - /* the default route was penalized when merging it to the combined ip-config. - * When subtracting the routes, we must re-do that process when comparing - * the routes. */ - o_lookup = nmp_object_stackinit_obj(&o_lookup_copy, o_src); - rr = NMP_OBJECT_CAST_IP4_ROUTE(&o_lookup_copy); - rr->metric = - nm_utils_ip_route_metric_penalize(rr->metric, default_route_metric_penalty); - } else - o_lookup = o_src; - - if (nm_dedup_multi_index_remove_obj(dst_priv->multi_idx, - &dst_priv->idx_ip4_routes, - o_lookup, - (gconstpointer *) &obj_old)) { - if (dst_priv->best_default_route == obj_old) { - nm_clear_nmp_object(&dst_priv->best_default_route); - changed_default_route = TRUE; - } - changed = TRUE; - } - } - if (changed_default_route) { - nmp_object_ref_set(&dst_priv->best_default_route, - _nm_ip4_config_best_default_route_find(dst)); - _notify(dst, PROP_GATEWAY); - } - if (changed) - _notify_routes(dst); - - /* domains */ - for (i = 0; i < nm_ip4_config_get_num_domains(src); i++) { - idx = _domains_get_index(dst, nm_ip4_config_get_domain(src, i)); - if (idx >= 0) - nm_ip4_config_del_domain(dst, idx); - } - - /* dns searches */ - for (i = 0; i < nm_ip4_config_get_num_searches(src); i++) { - idx = _searches_get_index(dst, nm_ip4_config_get_search(src, i)); - if (idx >= 0) - nm_ip4_config_del_search(dst, idx); - } - - /* dns options */ - for (i = 0; i < nm_ip4_config_get_num_dns_options(src); i++) { - idx = _dns_options_get_index(dst, nm_ip4_config_get_dns_option(src, i)); - if (idx >= 0) - nm_ip4_config_del_dns_option(dst, idx); - } - - /* MTU */ - if (nm_ip4_config_get_mtu(src) == nm_ip4_config_get_mtu(dst) - && nm_ip4_config_get_mtu_source(src) == nm_ip4_config_get_mtu_source(dst)) - nm_ip4_config_set_mtu(dst, 0, NM_IP_CONFIG_SOURCE_UNKNOWN); - - /* NIS */ - for (i = 0; i < nm_ip4_config_get_num_nis_servers(src); i++) { - idx = _nis_servers_get_index(dst, nm_ip4_config_get_nis_server(src, i)); - if (idx >= 0) - nm_ip4_config_del_nis_server(dst, idx); - } - - if (g_strcmp0(nm_ip4_config_get_nis_domain(src), nm_ip4_config_get_nis_domain(dst)) == 0) - nm_ip4_config_set_nis_domain(dst, NULL); - - /* WINS */ - for (i = 0; i < nm_ip4_config_get_num_wins(src); i++) { - idx = _wins_get_index(dst, nm_ip4_config_get_wins(src, i)); - if (idx >= 0) - nm_ip4_config_del_wins(dst, idx); - } - - /* DNS priority */ - if (nm_ip4_config_get_dns_priority(src) == nm_ip4_config_get_dns_priority(dst)) - nm_ip4_config_set_dns_priority(dst, 0); - - /* mdns */ - if (nm_ip4_config_mdns_get(src) == nm_ip4_config_mdns_get(dst)) - nm_ip4_config_mdns_set(dst, NM_SETTING_CONNECTION_MDNS_DEFAULT); - - /* LLMNR */ - if (nm_ip4_config_llmnr_get(src) == nm_ip4_config_llmnr_get(dst)) - nm_ip4_config_llmnr_set(dst, NM_SETTING_CONNECTION_LLMNR_DEFAULT); - - /* dns_over_tls */ - if (nm_ip4_config_dns_over_tls_get(src) == nm_ip4_config_dns_over_tls_get(dst)) - nm_ip4_config_dns_over_tls_set(dst, NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT); - - g_object_thaw_notify(G_OBJECT(dst)); -} - -static gboolean -_nm_ip4_config_intersect_helper(NMIP4Config * dst, - const NMIP4Config *src, - gboolean intersect_addresses, - gboolean intersect_routes, - guint32 default_route_metric_penalty, - gboolean update_dst) -{ - NMIP4ConfigPrivate * dst_priv; - const NMIP4ConfigPrivate * src_priv; - NMDedupMultiIter ipconf_iter; - const NMPlatformIP4Address *a; - const NMPlatformIP4Route * r; - const NMPObject * new_best_default_route; - gboolean changed, result = FALSE; - - g_return_val_if_fail(src, FALSE); - g_return_val_if_fail(dst, FALSE); - - dst_priv = NM_IP4_CONFIG_GET_PRIVATE(dst); - src_priv = NM_IP4_CONFIG_GET_PRIVATE(src); - - if (update_dst) - g_object_freeze_notify(G_OBJECT(dst)); - - /* addresses */ - if (intersect_addresses) { - changed = FALSE; - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, dst, &a) { - if (nm_dedup_multi_index_lookup_obj(src_priv->multi_idx, - &src_priv->idx_ip4_addresses, - NMP_OBJECT_UP_CAST(a))) - continue; - - if (!update_dst) - return TRUE; - - if (nm_dedup_multi_index_remove_entry(dst_priv->multi_idx, ipconf_iter.current) != 1) - nm_assert_not_reached(); - changed = TRUE; - } - if (changed) { - _notify_addresses(dst); - result = TRUE; - } - } - - /* ignore nameservers */ - - /* routes */ - if (!intersect_routes) - goto skip_routes; - - changed = FALSE; - new_best_default_route = NULL; - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, dst, &r) { - const NMPObject *o_dst = NMP_OBJECT_UP_CAST(r); - const NMPObject *o_lookup; - NMPObject o_lookup_copy; - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r) && default_route_metric_penalty) { - NMPlatformIP4Route *rr; - - /* the default route was penalized when merging it to the combined ip-config. - * When intersecting the routes, we must re-do that process when comparing - * the routes. */ - o_lookup = nmp_object_stackinit_obj(&o_lookup_copy, o_dst); - rr = NMP_OBJECT_CAST_IP4_ROUTE(&o_lookup_copy); - rr->metric = - nm_utils_ip_route_metric_penalize(rr->metric, default_route_metric_penalty); - } else - o_lookup = o_dst; - - if (nm_dedup_multi_index_lookup_obj(src_priv->multi_idx, - &src_priv->idx_ip4_routes, - o_lookup)) { - new_best_default_route = - _nm_ip_config_best_default_route_find_better(new_best_default_route, o_dst); - continue; - } - - if (!update_dst) - return TRUE; - - if (nm_dedup_multi_index_remove_entry(dst_priv->multi_idx, ipconf_iter.current) != 1) - nm_assert_not_reached(); - changed = TRUE; - } - if (nmp_object_ref_set(&dst_priv->best_default_route, new_best_default_route)) { - nm_assert(changed); - _notify(dst, PROP_GATEWAY); - } - - if (changed) { - _notify_routes(dst); - result = TRUE; - } - -skip_routes: - /* ignore domains */ - /* ignore dns searches */ - /* ignore dns options */ - /* ignore NIS */ - /* ignore WINS */ - /* ignore mdns */ - /* ignore LLMNR */ - /* ignore dns_over_tls */ - - if (update_dst) - g_object_thaw_notify(G_OBJECT(dst)); - return result; -} - -/** - * nm_ip4_config_intersect: - * @dst: a configuration to be updated - * @src: another configuration - * @intersect_addresses: whether addresses should be intersected - * @intersect_routes: whether routes should be intersected - * @default_route_metric_penalty: the default route metric penalty - * - * Computes the intersection between @src and @dst and updates @dst in place - * with the result. - */ -void -nm_ip4_config_intersect(NMIP4Config * dst, - const NMIP4Config *src, - gboolean intersect_addresses, - gboolean intersect_routes, - guint32 default_route_metric_penalty) -{ - _nm_ip4_config_intersect_helper(dst, - src, - intersect_addresses, - intersect_routes, - default_route_metric_penalty, - TRUE); -} - -/** - * nm_ip4_config_intersect_alloc: - * @a: a configuration - * @b: another configuration - * @intersect_addresses: whether addresses should be intersected - * @intersect_routes: whether routes should be intersected - * @default_route_metric_penalty: the default route metric penalty - * - * Computes the intersection between @a and @b and returns the result in a newly - * allocated configuration. As a special case, if @a and @b are identical (with - * respect to the only properties considered - addresses and routes) the - * functions returns NULL so that one of existing configuration can be reused - * without allocation. - * - * Returns: the intersection between @a and @b, or %NULL if the result is equal - * to @a and @b. - */ -NMIP4Config * -nm_ip4_config_intersect_alloc(const NMIP4Config *a, - const NMIP4Config *b, - gboolean intersect_addresses, - gboolean intersect_routes, - guint32 default_route_metric_penalty) -{ - NMIP4Config *a_copy; - - if (_nm_ip4_config_intersect_helper((NMIP4Config *) a, - b, - intersect_addresses, - intersect_routes, - default_route_metric_penalty, - FALSE)) { - a_copy = nm_ip4_config_clone(a); - _nm_ip4_config_intersect_helper(a_copy, - b, - intersect_addresses, - intersect_routes, - default_route_metric_penalty, - TRUE); - return a_copy; - } else - return NULL; -} - -/** - * nm_ip4_config_replace: - * @dst: config to replace with @src content - * @src: source config to copy - * @relevant_changes: return whether there are changes to the - * destination object that are relevant. This is equal to - * nm_ip4_config_equal() showing any difference. - * - * Replaces everything in @dst with @src so that the two configurations - * contain the same content -- with the exception of the dbus path. - * - * Returns: whether the @dst instance changed in any way (including minor changes, - * that are not signaled by the output parameter @relevant_changes). - */ -gboolean -nm_ip4_config_replace(NMIP4Config *dst, const NMIP4Config *src, gboolean *relevant_changes) -{ -#if NM_MORE_ASSERTS - gboolean config_equal; -#endif - gboolean has_minor_changes = FALSE, has_relevant_changes = FALSE, are_equal; - guint i, num; - NMIP4ConfigPrivate * dst_priv; - const NMIP4ConfigPrivate * src_priv; - NMDedupMultiIter ipconf_iter_src, ipconf_iter_dst; - const NMDedupMultiHeadEntry *head_entry_src; - const NMPObject * new_best_default_route; - - g_return_val_if_fail(src != NULL, FALSE); - g_return_val_if_fail(dst != NULL, FALSE); - g_return_val_if_fail(src != dst, FALSE); - -#if NM_MORE_ASSERTS - config_equal = nm_ip4_config_equal(dst, src); -#endif - - dst_priv = NM_IP4_CONFIG_GET_PRIVATE(dst); - src_priv = NM_IP4_CONFIG_GET_PRIVATE(src); - - g_object_freeze_notify(G_OBJECT(dst)); - - /* ifindex */ - if (src_priv->ifindex != dst_priv->ifindex) { - dst_priv->ifindex = src_priv->ifindex; - has_minor_changes = TRUE; - } - - /* addresses */ - head_entry_src = nm_ip4_config_lookup_addresses(src); - nm_dedup_multi_iter_init(&ipconf_iter_src, head_entry_src); - nm_ip_config_iter_ip4_address_init(&ipconf_iter_dst, dst); - are_equal = TRUE; - while (TRUE) { - gboolean has; - const NMPlatformIP4Address *r_src = NULL; - const NMPlatformIP4Address *r_dst = NULL; - - has = nm_platform_dedup_multi_iter_next_ip4_address(&ipconf_iter_src, &r_src); - if (has != nm_platform_dedup_multi_iter_next_ip4_address(&ipconf_iter_dst, &r_dst)) { - are_equal = FALSE; - has_relevant_changes = TRUE; - break; - } - if (!has) - break; - - if (nm_platform_ip4_address_cmp(r_src, r_dst) != 0) { - are_equal = FALSE; - if (r_src->address != r_dst->address || r_src->plen != r_dst->plen - || r_src->peer_address != r_dst->peer_address) { - has_relevant_changes = TRUE; - break; - } - } - } - if (!are_equal) { - has_minor_changes = TRUE; - nm_dedup_multi_index_dirty_set_idx(dst_priv->multi_idx, &dst_priv->idx_ip4_addresses); - nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) { - _nm_ip_config_add_obj(dst_priv->multi_idx, - &dst_priv->idx_ip4_addresses_, - dst_priv->ifindex, - ipconf_iter_src.current->obj, - NULL, - FALSE, - TRUE, - NULL, - NULL); - } - nm_dedup_multi_index_dirty_remove_idx(dst_priv->multi_idx, - &dst_priv->idx_ip4_addresses, - FALSE); - _notify_addresses(dst); - } - - /* routes */ - head_entry_src = nm_ip4_config_lookup_routes(src); - nm_dedup_multi_iter_init(&ipconf_iter_src, head_entry_src); - nm_ip_config_iter_ip4_route_init(&ipconf_iter_dst, dst); - are_equal = TRUE; - while (TRUE) { - gboolean has; - const NMPlatformIP4Route *r_src = NULL; - const NMPlatformIP4Route *r_dst = NULL; - - has = nm_platform_dedup_multi_iter_next_ip4_route(&ipconf_iter_src, &r_src); - if (has != nm_platform_dedup_multi_iter_next_ip4_route(&ipconf_iter_dst, &r_dst)) { - are_equal = FALSE; - has_relevant_changes = TRUE; - break; - } - if (!has) - break; - - if (nm_platform_ip4_route_cmp_full(r_src, r_dst) != 0) { - are_equal = FALSE; - if (r_src->plen != r_dst->plen - || !nm_utils_ip4_address_same_prefix(r_src->network, r_dst->network, r_src->plen) - || r_src->gateway != r_dst->gateway || r_src->metric != r_dst->metric) { - has_relevant_changes = TRUE; - break; - } - } - } - if (!are_equal) { - has_minor_changes = TRUE; - new_best_default_route = NULL; - nm_dedup_multi_index_dirty_set_idx(dst_priv->multi_idx, &dst_priv->idx_ip4_routes); - nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) { - const NMPObject *o = ipconf_iter_src.current->obj; - const NMPObject *obj_new; - - _nm_ip_config_add_obj(dst_priv->multi_idx, - &dst_priv->idx_ip4_routes_, - dst_priv->ifindex, - o, - NULL, - FALSE, - TRUE, - NULL, - &obj_new); - new_best_default_route = - _nm_ip_config_best_default_route_find_better(new_best_default_route, obj_new); - } - nm_dedup_multi_index_dirty_remove_idx(dst_priv->multi_idx, - &dst_priv->idx_ip4_routes, - FALSE); - if (nmp_object_ref_set(&dst_priv->best_default_route, new_best_default_route)) - _notify(dst, PROP_GATEWAY); - _notify_routes(dst); - } - - /* nameservers */ - num = nm_ip4_config_get_num_nameservers(src); - are_equal = num == nm_ip4_config_get_num_nameservers(dst); - if (are_equal) { - for (i = 0; i < num; i++) { - if (nm_ip4_config_get_nameserver(src, i) != nm_ip4_config_get_nameserver(dst, i)) { - are_equal = FALSE; - break; - } - } - } - if (!are_equal) { - nm_ip4_config_reset_nameservers(dst); - for (i = 0; i < num; i++) - nm_ip4_config_add_nameserver(dst, nm_ip4_config_get_nameserver(src, i)); - has_relevant_changes = TRUE; - } - - /* domains */ - num = nm_ip4_config_get_num_domains(src); - are_equal = num == nm_ip4_config_get_num_domains(dst); - if (are_equal) { - for (i = 0; i < num; i++) { - if (g_strcmp0(nm_ip4_config_get_domain(src, i), nm_ip4_config_get_domain(dst, i))) { - are_equal = FALSE; - break; - } - } - } - if (!are_equal) { - nm_ip4_config_reset_domains(dst); - for (i = 0; i < num; i++) - nm_ip4_config_add_domain(dst, nm_ip4_config_get_domain(src, i)); - has_relevant_changes = TRUE; - } - - /* dns searches */ - num = nm_ip4_config_get_num_searches(src); - are_equal = num == nm_ip4_config_get_num_searches(dst); - if (are_equal) { - for (i = 0; i < num; i++) { - if (g_strcmp0(nm_ip4_config_get_search(src, i), nm_ip4_config_get_search(dst, i))) { - are_equal = FALSE; - break; - } - } - } - if (!are_equal) { - nm_ip4_config_reset_searches(dst); - for (i = 0; i < num; i++) - nm_ip4_config_add_search(dst, nm_ip4_config_get_search(src, i)); - has_relevant_changes = TRUE; - } - - /* dns options */ - num = nm_ip4_config_get_num_dns_options(src); - are_equal = num == nm_ip4_config_get_num_dns_options(dst); - if (are_equal) { - for (i = 0; i < num; i++) { - if (g_strcmp0(nm_ip4_config_get_dns_option(src, i), - nm_ip4_config_get_dns_option(dst, i))) { - are_equal = FALSE; - break; - } - } - } - if (!are_equal) { - nm_ip4_config_reset_dns_options(dst); - for (i = 0; i < num; i++) - nm_ip4_config_add_dns_option(dst, nm_ip4_config_get_dns_option(src, i)); - has_relevant_changes = TRUE; - } - - if (src_priv->mdns != dst_priv->mdns) { - dst_priv->mdns = src_priv->mdns; - has_relevant_changes = TRUE; - } - - if (src_priv->llmnr != dst_priv->llmnr) { - dst_priv->llmnr = src_priv->llmnr; - has_relevant_changes = TRUE; - } - - if (src_priv->dns_over_tls != dst_priv->dns_over_tls) { - dst_priv->dns_over_tls = src_priv->dns_over_tls; - has_relevant_changes = TRUE; - } - - /* DNS priority */ - if (src_priv->dns_priority != dst_priv->dns_priority) { - nm_ip4_config_set_dns_priority(dst, src_priv->dns_priority); - has_minor_changes = TRUE; - } - - /* nis */ - num = nm_ip4_config_get_num_nis_servers(src); - are_equal = num == nm_ip4_config_get_num_nis_servers(dst); - if (are_equal) { - for (i = 0; i < num; i++) { - if (nm_ip4_config_get_nis_server(src, i) != nm_ip4_config_get_nis_server(dst, i)) { - are_equal = FALSE; - break; - } - } - } - if (!are_equal) { - nm_ip4_config_reset_nis_servers(dst); - for (i = 0; i < num; i++) - nm_ip4_config_add_nis_server(dst, nm_ip4_config_get_nis_server(src, i)); - has_relevant_changes = TRUE; - } - - /* nis_domain */ - if (g_strcmp0(src_priv->nis_domain, dst_priv->nis_domain)) { - nm_ip4_config_set_nis_domain(dst, src_priv->nis_domain); - has_relevant_changes = TRUE; - } - - /* wins */ - num = nm_ip4_config_get_num_wins(src); - are_equal = num == nm_ip4_config_get_num_wins(dst); - if (are_equal) { - for (i = 0; i < num; i++) { - if (nm_ip4_config_get_wins(src, i) != nm_ip4_config_get_wins(dst, i)) { - are_equal = FALSE; - break; - } - } - } - if (!are_equal) { - nm_ip4_config_reset_wins(dst); - for (i = 0; i < num; i++) - nm_ip4_config_add_wins(dst, nm_ip4_config_get_wins(src, i)); - has_relevant_changes = TRUE; - } - - /* mtu */ - if (src_priv->mtu != dst_priv->mtu || src_priv->mtu_source != dst_priv->mtu_source) { - nm_ip4_config_set_mtu(dst, src_priv->mtu, src_priv->mtu_source); - has_minor_changes = TRUE; - } - - /* metered */ - if (src_priv->metered != dst_priv->metered) { - dst_priv->metered = src_priv->metered; - has_minor_changes = TRUE; - } - - /* never default */ - if (src_priv->never_default != dst_priv->never_default) { - dst_priv->never_default = src_priv->never_default; - has_minor_changes = TRUE; - } - -#if NM_MORE_ASSERTS - /* config_equal does not compare *all* the fields, therefore, we might have has_minor_changes - * regardless of config_equal. But config_equal must correspond to has_relevant_changes. */ - nm_assert(config_equal == !has_relevant_changes); -#endif - - g_object_thaw_notify(G_OBJECT(dst)); - - if (relevant_changes) - *relevant_changes = has_relevant_changes; - - return has_relevant_changes || has_minor_changes; -} - -void -nm_ip_config_dump(const NMIPConfig *self, const char *detail, NMLogLevel level, NMLogDomain domain) -{ - NMDedupMultiIter ipconf_iter; - const NMPlatformIP4Address *addr4; - const NMPlatformIP6Address *addr6; - const NMPlatformIP4Route * route4; - const NMPlatformIP6Route * route6; - const NMIP4Config * ip4; - const NMIP6Config * ip6; - int addr_family = AF_UNSPEC; - char addr_family_char = '?'; - const char * path; - gconstpointer ptr; - guint i; - - if (self) { - addr_family = nm_ip_config_get_addr_family(self); - addr_family_char = nm_utils_addr_family_to_char(addr_family); - } - - nm_log(level, domain, NULL, NULL, "---- NMIP%cConfig %p (%s)", addr_family_char, self, detail); - - if (!self) - return; - - path = nm_dbus_object_get_path(NM_DBUS_OBJECT(self)); - if (path) - nm_log(level, domain, NULL, NULL, " path : %s", path); - - if (addr_family == AF_INET) { - ip4 = NM_IP4_CONFIG(self); - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &addr4) { - nm_log(level, - domain, - NULL, - NULL, - " address : %s", - nm_platform_ip4_address_to_string(addr4, NULL, 0)); - } - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route4) { - nm_log(level, - domain, - NULL, - NULL, - " route : %s", - nm_platform_ip4_route_to_string(route4, NULL, 0)); - } - } else { - ip6 = NM_IP6_CONFIG(self); - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &addr6) { - nm_log(level, - domain, - NULL, - NULL, - " address : %s", - nm_platform_ip6_address_to_string(addr6, NULL, 0)); - } - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route6) { - nm_log(level, - domain, - NULL, - NULL, - " route : %s", - nm_platform_ip6_route_to_string(route6, NULL, 0)); - } - } - - for (i = 0; i < nm_ip_config_get_num_nameservers(self); i++) { - char buf[NM_UTILS_INET_ADDRSTRLEN]; - - ptr = nm_ip_config_get_nameserver(self, i); - nm_log(level, - domain, - NULL, - NULL, - " dns : %s", - nm_utils_inet_ntop(addr_family, ptr, buf)); - } - - for (i = 0; i < nm_ip_config_get_num_domains(self); i++) - nm_log(level, domain, NULL, NULL, " domain : %s", nm_ip_config_get_domain(self, i)); - - for (i = 0; i < nm_ip_config_get_num_searches(self); i++) - nm_log(level, domain, NULL, NULL, " search : %s", nm_ip_config_get_search(self, i)); - - for (i = 0; i < nm_ip_config_get_num_dns_options(self); i++) - nm_log(level, domain, NULL, NULL, "dns-option: %s", nm_ip_config_get_dns_option(self, i)); - - nm_log(level, domain, NULL, NULL, " dns-prio : %d", nm_ip_config_get_dns_priority(self)); - - if (addr_family == AF_INET) { - ip4 = NM_IP4_CONFIG(self); - nm_log(level, - domain, - NULL, - NULL, - " mtu : %" G_GUINT32_FORMAT " (source: %d)", - nm_ip4_config_get_mtu(ip4), - (int) nm_ip4_config_get_mtu_source(ip4)); - nm_log(level, domain, NULL, NULL, " metered : %d", (int) nm_ip4_config_get_metered(ip4)); - } -} - -/*****************************************************************************/ - -gconstpointer -nm_ip_config_find_first_address(const NMIPConfig *self, NMPlatformMatchFlags match_flag) -{ - NMDedupMultiIter iter; - const NMPlatformIPAddress *address; - - g_return_val_if_fail(NM_IS_IP_CONFIG(self), NULL); - - nm_assert(!NM_FLAGS_ANY( - match_flag, - ~(NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY))); - - nm_ip_config_iter_ip_address_for_each (&iter, self, &address) { - if (nm_platform_ip_address_match(nm_ip_config_get_addr_family(self), address, match_flag)) - return address; - } - - return NULL; -} - -void -nm_ip4_config_reset_addresses(NMIP4Config *self) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - if (nm_dedup_multi_index_remove_idx(priv->multi_idx, &priv->idx_ip4_addresses) > 0) - _notify_addresses(self); -} - -static void -_add_address(NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Address *new) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - if (_nm_ip_config_add_obj(priv->multi_idx, - &priv->idx_ip4_addresses_, - priv->ifindex, - obj_new, - (const NMPlatformObject *) new, - TRUE, - FALSE, - NULL, - NULL)) - _notify_addresses(self); -} - -/** - * nm_ip4_config_add_address: - * @self: the #NMIP4Config - * @new: the new address to add to @self - * - * Adds the new address to @self. If an address with the same basic properties - * (address, prefix) already exists in @self, it is overwritten with the - * lifetime and preferred of @new. The source is also overwritten by the source - * from @new if that source is higher priority. - */ -void -nm_ip4_config_add_address(NMIP4Config *self, const NMPlatformIP4Address *new) -{ - g_return_if_fail(self); - g_return_if_fail(new); - g_return_if_fail(new->plen <= 32); - g_return_if_fail(NM_IP4_CONFIG_GET_PRIVATE(self)->ifindex > 0); - - _add_address(self, NULL, new); -} - -void -_nmtst_ip4_config_del_address(NMIP4Config *self, guint i) -{ - const NMPlatformIP4Address *a; - - a = _nmtst_ip4_config_get_address(self, i); - if (!nm_ip4_config_nmpobj_remove(self, NMP_OBJECT_UP_CAST(a))) - g_assert_not_reached(); -} - -guint -nm_ip4_config_get_num_addresses(const NMIP4Config *self) -{ - const NMDedupMultiHeadEntry *head_entry; - - head_entry = nm_ip4_config_lookup_addresses(self); - return head_entry ? head_entry->len : 0; -} - -const NMPlatformIP4Address * -nm_ip4_config_get_first_address(const NMIP4Config *self) -{ - NMDedupMultiIter iter; - const NMPlatformIP4Address *a = NULL; - - nm_ip_config_iter_ip4_address_for_each (&iter, self, &a) - return a; - return NULL; -} - -const NMPlatformIP4Address * -_nmtst_ip4_config_get_address(const NMIP4Config *self, guint i) -{ - NMDedupMultiIter iter = {}; - const NMPlatformIP4Address *a = NULL; - guint j; - - j = 0; - nm_ip_config_iter_ip4_address_for_each (&iter, self, &a) { - if (i == j) - return a; - j++; - } - g_return_val_if_reached(NULL); -} - -gboolean -nm_ip4_config_address_exists(const NMIP4Config *self, const NMPlatformIP4Address *needle) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - NMPObject obj_stack; - - nmp_object_stackinit_id_ip4_address(&obj_stack, - priv->ifindex, - needle->address, - needle->plen, - needle->peer_address); - return !!nm_dedup_multi_index_lookup_obj(priv->multi_idx, &priv->idx_ip4_addresses, &obj_stack); -} - -/*****************************************************************************/ - -static const NMDedupMultiEntry * -_lookup_route(const NMIP4Config *self, const NMPObject *needle, NMPlatformIPRouteCmpType cmp_type) -{ - const NMIP4ConfigPrivate *priv; - - nm_assert(NM_IS_IP4_CONFIG(self)); - nm_assert(NMP_OBJECT_GET_TYPE(needle) == NMP_OBJECT_TYPE_IP4_ROUTE); - - priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return _nm_ip_config_lookup_ip_route(priv->multi_idx, &priv->idx_ip4_routes_, needle, cmp_type); -} - -void -nm_ip4_config_reset_routes(NMIP4Config *self) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - if (nm_dedup_multi_index_remove_idx(priv->multi_idx, &priv->idx_ip4_routes) > 0) { - if (nm_clear_nmp_object(&priv->best_default_route)) - _notify(self, PROP_GATEWAY); - _notify_routes(self); - } -} - -static void -_add_route(NMIP4Config * self, - const NMPObject *obj_new, - const NMPlatformIP4Route *new, - const NMPObject **out_obj_new) -{ - NMIP4ConfigPrivate * priv = NM_IP4_CONFIG_GET_PRIVATE(self); - nm_auto_nmpobj const NMPObject *obj_old = NULL; - const NMPObject * obj_new_2; - - nm_assert((!new) != (!obj_new)); - nm_assert(!new || _route_valid(new)); - nm_assert(!obj_new || _route_valid(NMP_OBJECT_CAST_IP4_ROUTE(obj_new))); - - if (_nm_ip_config_add_obj(priv->multi_idx, - &priv->idx_ip4_routes_, - priv->ifindex, - obj_new, - (const NMPlatformObject *) new, - TRUE, - FALSE, - &obj_old, - &obj_new_2)) { - gboolean changed_default_route = FALSE; - - if (priv->best_default_route == obj_old && obj_old != obj_new_2) { - changed_default_route = TRUE; - nm_clear_nmp_object(&priv->best_default_route); - } - NM_SET_OUT(out_obj_new, nmp_object_ref(obj_new_2)); - if (_nm_ip_config_best_default_route_merge(&priv->best_default_route, obj_new_2)) - changed_default_route = TRUE; - - if (changed_default_route) - _notify(self, PROP_GATEWAY); - _notify_routes(self); - } else - NM_SET_OUT(out_obj_new, nmp_object_ref(obj_new_2)); -} - -/** - * nm_ip4_config_add_route: - * @self: the #NMIP4Config - * @new: the new route to add to @self - * @out_obj_new: (allow-none) (out): the added route object. Must be unrefed - * by caller. - * - * Adds the new route to @self. If a route with the same basic properties - * (network, prefix) already exists in @self, it is overwritten including the - * gateway and metric of @new. The source is also overwritten by the source - * from @new if that source is higher priority. - */ -void -nm_ip4_config_add_route(NMIP4Config *self, - const NMPlatformIP4Route *new, - const NMPObject **out_obj_new) -{ - g_return_if_fail(self); - g_return_if_fail(new); - g_return_if_fail(new->plen <= 32); - g_return_if_fail(NM_IP4_CONFIG_GET_PRIVATE(self)->ifindex > 0); - - _add_route(self, NULL, new, out_obj_new); -} - -void -_nmtst_ip4_config_del_route(NMIP4Config *self, guint i) -{ - const NMPlatformIP4Route *r; - - r = _nmtst_ip4_config_get_route(self, i); - if (!nm_ip4_config_nmpobj_remove(self, NMP_OBJECT_UP_CAST(r))) - g_assert_not_reached(); -} - -guint -nm_ip4_config_get_num_routes(const NMIP4Config *self) -{ - const NMDedupMultiHeadEntry *head_entry; - - head_entry = nm_ip4_config_lookup_routes(self); - nm_assert(!head_entry || head_entry->len == c_list_length(&head_entry->lst_entries_head)); - return head_entry ? head_entry->len : 0; -} - -const NMPlatformIP4Route * -_nmtst_ip4_config_get_route(const NMIP4Config *self, guint i) -{ - NMDedupMultiIter iter; - const NMPlatformIP4Route *r = NULL; - guint j; - - j = 0; - nm_ip_config_iter_ip4_route_for_each (&iter, self, &r) { - if (i == j) - return r; - j++; - } - g_return_val_if_reached(NULL); -} - -const NMPlatformIP4Route * -nm_ip4_config_get_direct_route_for_host(const NMIP4Config *self, - in_addr_t host, - guint32 route_table) -{ - const NMPlatformIP4Route *best_route = NULL; - const NMPlatformIP4Route *item; - NMDedupMultiIter ipconf_iter; - - g_return_val_if_fail(host, NULL); - - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &item) { - if (item->gateway != 0) - continue; - - if (best_route && best_route->plen > item->plen) - continue; - - if (nm_platform_route_table_uncoerce(item->table_coerced, TRUE) != route_table) - continue; - - if (nm_utils_ip4_address_clear_host_address(host, item->plen) - != nm_utils_ip4_address_clear_host_address(item->network, item->plen)) - continue; - - if (best_route && best_route->metric <= item->metric) - continue; - - best_route = item; - } - return best_route; -} - -/*****************************************************************************/ - -void -nm_ip4_config_reset_nameservers(NMIP4Config *self) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - if (priv->nameservers->len != 0) { - g_array_set_size(priv->nameservers, 0); - nm_gobject_notify_together(self, PROP_NAMESERVER_DATA, PROP_NAMESERVERS); - } -} - -void -nm_ip4_config_add_nameserver(NMIP4Config *self, guint32 new) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - int i; - - g_return_if_fail(new != 0); - - for (i = 0; i < priv->nameservers->len; i++) - if (new == g_array_index(priv->nameservers, guint32, i)) - return; - - g_array_append_val(priv->nameservers, new); - nm_gobject_notify_together(self, PROP_NAMESERVER_DATA, PROP_NAMESERVERS); -} - -void -nm_ip4_config_del_nameserver(NMIP4Config *self, guint i) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - g_return_if_fail(i < priv->nameservers->len); - - g_array_remove_index(priv->nameservers, i); - nm_gobject_notify_together(self, PROP_NAMESERVER_DATA, PROP_NAMESERVERS); -} - -guint -nm_ip4_config_get_num_nameservers(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return priv->nameservers->len; -} - -guint32 -nm_ip4_config_get_nameserver(const NMIP4Config *self, guint i) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return g_array_index(priv->nameservers, guint32, i); -} - -const in_addr_t * -_nm_ip4_config_get_nameserver(const NMIP4Config *self, guint i) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return &g_array_index(priv->nameservers, guint32, i); -} - -/*****************************************************************************/ - -gboolean -_nm_ip_config_check_and_add_domain(GPtrArray *array, const char *domain) -{ - char * copy = NULL; - size_t len; - - g_return_val_if_fail(domain, FALSE); - g_return_val_if_fail(domain[0] != '\0', FALSE); - - if (domain[0] == '.' || strstr(domain, "..")) - return FALSE; - - len = strlen(domain); - if (domain[len - 1] == '.') - domain = copy = g_strndup(domain, len - 1); - - if (nm_strv_ptrarray_find_first(array, domain) >= 0) { - g_free(copy); - return FALSE; - } - - g_ptr_array_add(array, copy ?: g_strdup(domain)); - return TRUE; -} - -void -nm_ip4_config_reset_domains(NMIP4Config *self) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - if (priv->domains->len != 0) { - g_ptr_array_set_size(priv->domains, 0); - _notify(self, PROP_DOMAINS); - } -} - -void -nm_ip4_config_add_domain(NMIP4Config *self, const char *domain) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - if (_nm_ip_config_check_and_add_domain(priv->domains, domain)) - _notify(self, PROP_DOMAINS); -} - -void -nm_ip4_config_del_domain(NMIP4Config *self, guint i) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - g_return_if_fail(i < priv->domains->len); - - g_ptr_array_remove_index(priv->domains, i); - _notify(self, PROP_DOMAINS); -} - -guint -nm_ip4_config_get_num_domains(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return priv->domains->len; -} - -const char * -nm_ip4_config_get_domain(const NMIP4Config *self, guint i) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return g_ptr_array_index(priv->domains, i); -} - -/*****************************************************************************/ - -void -nm_ip4_config_reset_searches(NMIP4Config *self) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - if (priv->searches->len != 0) { - g_ptr_array_set_size(priv->searches, 0); - _notify(self, PROP_SEARCHES); - } -} - -void -nm_ip4_config_add_search(NMIP4Config *self, const char *search) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - if (_nm_ip_config_check_and_add_domain(priv->searches, search)) - _notify(self, PROP_SEARCHES); -} - -void -nm_ip4_config_del_search(NMIP4Config *self, guint i) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - g_return_if_fail(i < priv->searches->len); - - g_ptr_array_remove_index(priv->searches, i); - _notify(self, PROP_SEARCHES); -} - -guint -nm_ip4_config_get_num_searches(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return priv->searches->len; -} - -const char * -nm_ip4_config_get_search(const NMIP4Config *self, guint i) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return g_ptr_array_index(priv->searches, i); -} - -/*****************************************************************************/ - -void -nm_ip4_config_reset_dns_options(NMIP4Config *self) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - if (priv->dns_options->len != 0) { - g_ptr_array_set_size(priv->dns_options, 0); - _notify(self, PROP_DNS_OPTIONS); - } -} - -void -nm_ip4_config_add_dns_option(NMIP4Config *self, const char *new) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - int i; - - g_return_if_fail(new != NULL); - g_return_if_fail(new[0] != '\0'); - - for (i = 0; i < priv->dns_options->len; i++) - if (!g_strcmp0(g_ptr_array_index(priv->dns_options, i), new)) - return; - - g_ptr_array_add(priv->dns_options, g_strdup(new)); - _notify(self, PROP_DNS_OPTIONS); -} - -void -nm_ip4_config_del_dns_option(NMIP4Config *self, guint i) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - g_return_if_fail(i < priv->dns_options->len); - - g_ptr_array_remove_index(priv->dns_options, i); - _notify(self, PROP_DNS_OPTIONS); -} - -guint -nm_ip4_config_get_num_dns_options(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return priv->dns_options->len; -} - -const char * -nm_ip4_config_get_dns_option(const NMIP4Config *self, guint i) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return g_ptr_array_index(priv->dns_options, i); -} - -/*****************************************************************************/ - -NMSettingConnectionMdns -nm_ip4_config_mdns_get(const NMIP4Config *self) -{ - return NM_IP4_CONFIG_GET_PRIVATE(self)->mdns; -} - -void -nm_ip4_config_mdns_set(NMIP4Config *self, NMSettingConnectionMdns mdns) -{ - NM_IP4_CONFIG_GET_PRIVATE(self)->mdns = mdns; -} - -NMSettingConnectionLlmnr -nm_ip4_config_llmnr_get(const NMIP4Config *self) -{ - return NM_IP4_CONFIG_GET_PRIVATE(self)->llmnr; -} - -void -nm_ip4_config_llmnr_set(NMIP4Config *self, NMSettingConnectionLlmnr llmnr) -{ - NM_IP4_CONFIG_GET_PRIVATE(self)->llmnr = llmnr; -} - -NMSettingConnectionDnsOverTls -nm_ip4_config_dns_over_tls_get(const NMIP4Config *self) -{ - return NM_IP4_CONFIG_GET_PRIVATE(self)->dns_over_tls; -} - -void -nm_ip4_config_dns_over_tls_set(NMIP4Config *self, NMSettingConnectionDnsOverTls dns_over_tls) -{ - NM_IP4_CONFIG_GET_PRIVATE(self)->dns_over_tls = dns_over_tls; -} - -/*****************************************************************************/ - -NMIPConfigFlags -nm_ip4_config_get_config_flags(const NMIP4Config *self) -{ - return NM_IP4_CONFIG_GET_PRIVATE(self)->config_flags; -} - -void -nm_ip4_config_set_config_flags(NMIP4Config *self, NMIPConfigFlags flags, NMIPConfigFlags mask) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - if (mask == 0) { - /* for convenience, accept 0 mask to set any flags. */ - mask = flags; - } - - nm_assert(!NM_FLAGS_ANY(flags, ~mask)); - priv->config_flags = (flags & mask) | (priv->config_flags & ~mask); -} - -/*****************************************************************************/ - -void -nm_ip4_config_set_dns_priority(NMIP4Config *self, int priority) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - if (priority != priv->dns_priority) { - priv->dns_priority = priority; - _notify(self, PROP_DNS_PRIORITY); - } -} - -int -nm_ip4_config_get_dns_priority(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return priv->dns_priority; -} - -/*****************************************************************************/ - -void -nm_ip4_config_reset_nis_servers(NMIP4Config *self) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - g_array_set_size(priv->nis, 0); -} - -void -nm_ip4_config_add_nis_server(NMIP4Config *self, guint32 nis) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - int i; - - for (i = 0; i < priv->nis->len; i++) - if (nis == g_array_index(priv->nis, guint32, i)) - return; - - g_array_append_val(priv->nis, nis); -} - -void -nm_ip4_config_del_nis_server(NMIP4Config *self, guint i) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - g_return_if_fail(i < priv->nis->len); - - g_array_remove_index(priv->nis, i); -} - -guint -nm_ip4_config_get_num_nis_servers(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return priv->nis->len; -} - -guint32 -nm_ip4_config_get_nis_server(const NMIP4Config *self, guint i) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return g_array_index(priv->nis, guint32, i); -} - -void -nm_ip4_config_set_nis_domain(NMIP4Config *self, const char *domain) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - g_free(priv->nis_domain); - priv->nis_domain = g_strdup(domain); -} - -const char * -nm_ip4_config_get_nis_domain(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return priv->nis_domain; -} - -/*****************************************************************************/ - -void -nm_ip4_config_reset_wins(NMIP4Config *self) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - if (priv->wins->len != 0) { - g_array_set_size(priv->wins, 0); - nm_gobject_notify_together(self, PROP_WINS_SERVER_DATA, PROP_WINS_SERVERS); - } -} - -void -nm_ip4_config_add_wins(NMIP4Config *self, guint32 wins) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - int i; - - g_return_if_fail(wins != 0); - - for (i = 0; i < priv->wins->len; i++) - if (wins == g_array_index(priv->wins, guint32, i)) - return; - - g_array_append_val(priv->wins, wins); - nm_gobject_notify_together(self, PROP_WINS_SERVER_DATA, PROP_WINS_SERVERS); -} - -void -nm_ip4_config_del_wins(NMIP4Config *self, guint i) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - g_return_if_fail(i < priv->wins->len); - - g_array_remove_index(priv->wins, i); - nm_gobject_notify_together(self, PROP_WINS_SERVER_DATA, PROP_WINS_SERVERS); -} - -guint -nm_ip4_config_get_num_wins(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return priv->wins->len; -} - -guint32 -nm_ip4_config_get_wins(const NMIP4Config *self, guint i) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return g_array_index(priv->wins, guint32, i); -} - -/*****************************************************************************/ - -void -nm_ip4_config_set_mtu(NMIP4Config *self, guint32 mtu, NMIPConfigSource source) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - if (!mtu) - source = NM_IP_CONFIG_SOURCE_UNKNOWN; - - priv->mtu = mtu; - priv->mtu_source = source; -} - -guint32 -nm_ip4_config_get_mtu(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return priv->mtu; -} - -NMIPConfigSource -nm_ip4_config_get_mtu_source(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return priv->mtu_source; -} - -/*****************************************************************************/ - -void -nm_ip4_config_set_metered(NMIP4Config *self, gboolean metered) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - priv->metered = metered; -} - -gboolean -nm_ip4_config_get_metered(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return priv->metered; -} - -/*****************************************************************************/ - -void -nm_ip4_config_set_never_default(NMIP4Config *self, gboolean never_default) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - priv->never_default = never_default; -} - -gboolean -nm_ip4_config_get_never_default(const NMIP4Config *self) -{ - const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - return priv->never_default; -} - -/*****************************************************************************/ - -const NMPObject * -nm_ip4_config_nmpobj_lookup(const NMIP4Config *self, const NMPObject *needle) -{ - const NMIP4ConfigPrivate * priv; - const NMDedupMultiIdxType *idx_type; - - g_return_val_if_fail(NM_IS_IP4_CONFIG(self), NULL); - - priv = NM_IP4_CONFIG_GET_PRIVATE(self); - switch (NMP_OBJECT_GET_TYPE(needle)) { - case NMP_OBJECT_TYPE_IP4_ADDRESS: - idx_type = &priv->idx_ip4_addresses; - break; - case NMP_OBJECT_TYPE_IP4_ROUTE: - idx_type = &priv->idx_ip4_routes; - break; - default: - g_return_val_if_reached(NULL); - } - - return nm_dedup_multi_entry_get_obj( - nm_dedup_multi_index_lookup_obj(priv->multi_idx, idx_type, needle)); -} - -gboolean -nm_ip4_config_nmpobj_remove(NMIP4Config *self, const NMPObject *needle) -{ - NMIP4ConfigPrivate * priv; - NMDedupMultiIdxType *idx_type; - nm_auto_nmpobj const NMPObject *obj_old = NULL; - guint n; - - g_return_val_if_fail(NM_IS_IP4_CONFIG(self), FALSE); - - priv = NM_IP4_CONFIG_GET_PRIVATE(self); - switch (NMP_OBJECT_GET_TYPE(needle)) { - case NMP_OBJECT_TYPE_IP4_ADDRESS: - idx_type = &priv->idx_ip4_addresses; - break; - case NMP_OBJECT_TYPE_IP4_ROUTE: - idx_type = &priv->idx_ip4_routes; - break; - default: - g_return_val_if_reached(FALSE); - } - - n = nm_dedup_multi_index_remove_obj(priv->multi_idx, - idx_type, - needle, - (gconstpointer *) &obj_old); - if (n != 1) { - nm_assert(n == 0); - return FALSE; - } - - nm_assert(NMP_OBJECT_GET_TYPE(obj_old) == NMP_OBJECT_GET_TYPE(needle)); - - switch (NMP_OBJECT_GET_TYPE(obj_old)) { - case NMP_OBJECT_TYPE_IP4_ADDRESS: - _notify_addresses(self); - break; - case NMP_OBJECT_TYPE_IP4_ROUTE: - if (priv->best_default_route == obj_old) { - if (nmp_object_ref_set(&priv->best_default_route, - _nm_ip4_config_best_default_route_find(self))) - _notify(self, PROP_GATEWAY); - } - _notify_routes(self); - break; - default: - nm_assert_not_reached(); - } - return TRUE; -} - -/*****************************************************************************/ - -static void -hash_u32(GChecksum *sum, guint32 n) -{ - g_checksum_update(sum, (const guint8 *) &n, sizeof(n)); -} - -void -nm_ip4_config_hash(const NMIP4Config *self, GChecksum *sum, gboolean dns_only) -{ - guint i; - const char * s; - NMDedupMultiIter ipconf_iter; - const NMPlatformIP4Address *address; - const NMPlatformIP4Route * route; - int val; - - g_return_if_fail(self); - g_return_if_fail(sum); - - if (!dns_only) { - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, self, &address) { - hash_u32(sum, address->address); - hash_u32(sum, address->plen); - hash_u32(sum, address->peer_address & _nm_utils_ip4_prefix_to_netmask(address->plen)); - } - - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route) { - hash_u32(sum, route->network); - hash_u32(sum, route->plen); - hash_u32(sum, route->gateway); - hash_u32(sum, route->metric); - } - - for (i = 0; i < nm_ip4_config_get_num_nis_servers(self); i++) - hash_u32(sum, nm_ip4_config_get_nis_server(self, i)); - - s = nm_ip4_config_get_nis_domain(self); - if (s) - g_checksum_update(sum, (const guint8 *) s, strlen(s)); - } - - for (i = 0; i < nm_ip4_config_get_num_nameservers(self); i++) - hash_u32(sum, nm_ip4_config_get_nameserver(self, i)); - - for (i = 0; i < nm_ip4_config_get_num_wins(self); i++) - hash_u32(sum, nm_ip4_config_get_wins(self, i)); - - for (i = 0; i < nm_ip4_config_get_num_domains(self); i++) { - s = nm_ip4_config_get_domain(self, i); - g_checksum_update(sum, (const guint8 *) s, strlen(s)); - } - - for (i = 0; i < nm_ip4_config_get_num_searches(self); i++) { - s = nm_ip4_config_get_search(self, i); - g_checksum_update(sum, (const guint8 *) s, strlen(s)); - } - - for (i = 0; i < nm_ip4_config_get_num_dns_options(self); i++) { - s = nm_ip4_config_get_dns_option(self, i); - g_checksum_update(sum, (const guint8 *) s, strlen(s)); - } - - val = nm_ip4_config_mdns_get(self); - if (val != NM_SETTING_CONNECTION_MDNS_DEFAULT) - g_checksum_update(sum, (const guint8 *) &val, sizeof(val)); - - val = nm_ip4_config_llmnr_get(self); - if (val != NM_SETTING_CONNECTION_LLMNR_DEFAULT) - g_checksum_update(sum, (const guint8 *) &val, sizeof(val)); - - val = nm_ip4_config_dns_over_tls_get(self); - 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 - */ -} - -/** - * nm_ip4_config_equal: - * @a: first config to compare - * @b: second config to compare - * - * Compares two #NMIP4Configs for basic equality. This means that all - * attributes must exist in the same order in both configs (addresses, routes, - * domains, DNS servers, etc) but some attributes (address lifetimes, and address - * and route sources) are ignored. - * - * Returns: %TRUE if the configurations are basically equal to each other, - * %FALSE if not - */ -gboolean -nm_ip4_config_equal(const NMIP4Config *a, const NMIP4Config *b) -{ - nm_auto_free_checksum GChecksum *a_checksum = g_checksum_new(G_CHECKSUM_SHA1); - nm_auto_free_checksum GChecksum *b_checksum = g_checksum_new(G_CHECKSUM_SHA1); - guint8 a_data[NM_UTILS_CHECKSUM_LENGTH_SHA1]; - guint8 b_data[NM_UTILS_CHECKSUM_LENGTH_SHA1]; - - if (a) - nm_ip4_config_hash(a, a_checksum, FALSE); - if (b) - nm_ip4_config_hash(b, b_checksum, FALSE); - - nm_utils_checksum_get_digest(a_checksum, a_data); - nm_utils_checksum_get_digest(b_checksum, b_data); - return !memcmp(a_data, b_data, sizeof(a_data)); -} - -/*****************************************************************************/ - -static void -get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - NMIP4Config * self = NM_IP4_CONFIG(object); - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; - GVariantBuilder builder_data; - guint i; - - switch (prop_id) { - case PROP_IFINDEX: - g_value_set_int(value, priv->ifindex); - break; - case PROP_ADDRESS_DATA: - case PROP_ADDRESSES: - nm_assert(!!priv->address_data_variant == !!priv->addresses_variant); - - if (!priv->address_data_variant) { - nm_utils_ip_addresses_to_dbus(AF_INET, - nm_ip4_config_lookup_addresses(self), - priv->best_default_route, - NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, - &priv->address_data_variant, - &priv->addresses_variant); - g_variant_ref_sink(priv->address_data_variant); - g_variant_ref_sink(priv->addresses_variant); - } - - g_value_set_variant(value, - prop_id == PROP_ADDRESS_DATA ? priv->address_data_variant - : priv->addresses_variant); - break; - case PROP_ROUTE_DATA: - case PROP_ROUTES: - nm_assert(!!priv->route_data_variant == !!priv->routes_variant); - - if (!priv->route_data_variant) { - nm_utils_ip_routes_to_dbus(AF_INET, - nm_ip4_config_lookup_routes(self), - &priv->route_data_variant, - &priv->routes_variant); - g_variant_ref_sink(priv->route_data_variant); - g_variant_ref_sink(priv->routes_variant); - } - - g_value_set_variant(value, - prop_id == PROP_ROUTE_DATA ? priv->route_data_variant - : priv->routes_variant); - break; - case PROP_GATEWAY: - if (priv->best_default_route) { - g_value_take_string(value, - nm_utils_inet4_ntop_dup( - NMP_OBJECT_CAST_IP4_ROUTE(priv->best_default_route)->gateway)); - } else - g_value_set_string(value, NULL); - break; - case PROP_NAMESERVER_DATA: - g_variant_builder_init(&builder_data, G_VARIANT_TYPE("aa{sv}")); - - for (i = 0; i < priv->nameservers->len; i++) { - GVariantBuilder nested_builder; - - _nm_utils_inet4_ntop(g_array_index(priv->nameservers, in_addr_t, i), 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_data, "a{sv}", &nested_builder); - } - - g_value_take_variant(value, g_variant_builder_end(&builder_data)); - break; - case PROP_NAMESERVERS: - g_value_take_variant( - value, - nm_g_variant_new_au((const guint32 *) priv->nameservers->data, priv->nameservers->len)); - break; - case PROP_DOMAINS: - nm_utils_g_value_set_strv(value, priv->domains); - break; - case PROP_SEARCHES: - nm_utils_g_value_set_strv(value, priv->searches); - break; - case PROP_DNS_OPTIONS: - nm_utils_g_value_set_strv(value, priv->dns_options); - break; - case PROP_DNS_PRIORITY: - g_value_set_int(value, priv->dns_priority); - break; - case PROP_WINS_SERVER_DATA: - g_variant_builder_init(&builder_data, G_VARIANT_TYPE("as")); - for (i = 0; i < priv->wins->len; i++) { - g_variant_builder_add( - &builder_data, - "s", - _nm_utils_inet4_ntop(g_array_index(priv->wins, in_addr_t, i), addr_str)); - } - g_value_take_variant(value, g_variant_builder_end(&builder_data)); - break; - case PROP_WINS_SERVERS: - g_value_take_variant( - value, - nm_g_variant_new_au((const guint32 *) priv->wins->data, priv->wins->len)); - 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) -{ - NMIP4Config * self = NM_IP4_CONFIG(object); - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - switch (prop_id) { - case PROP_MULTI_IDX: - /* construct-only */ - priv->multi_idx = g_value_get_pointer(value); - if (!priv->multi_idx) - g_return_if_reached(); - nm_dedup_multi_index_ref(priv->multi_idx); - break; - case PROP_IFINDEX: - /* construct-only */ - priv->ifindex = g_value_get_int(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -/*****************************************************************************/ - -static void -nm_ip4_config_init(NMIP4Config *self) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - nm_ip_config_dedup_multi_idx_type_init((NMIPConfigDedupMultiIdxType *) &priv->idx_ip4_addresses, - NMP_OBJECT_TYPE_IP4_ADDRESS); - nm_ip_config_dedup_multi_idx_type_init((NMIPConfigDedupMultiIdxType *) &priv->idx_ip4_routes, - NMP_OBJECT_TYPE_IP4_ROUTE); - - priv->mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT; - priv->llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT; - priv->dns_over_tls = NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT; - priv->nameservers = g_array_new(FALSE, FALSE, sizeof(guint32)); - priv->domains = g_ptr_array_new_with_free_func(g_free); - priv->searches = g_ptr_array_new_with_free_func(g_free); - priv->dns_options = g_ptr_array_new_with_free_func(g_free); - priv->nis = g_array_new(FALSE, TRUE, sizeof(guint32)); - priv->wins = g_array_new(FALSE, TRUE, sizeof(guint32)); -} - -NMIP4Config * -nm_ip4_config_new(NMDedupMultiIndex *multi_idx, int ifindex) -{ - g_return_val_if_fail(ifindex >= -1, NULL); - return g_object_new(NM_TYPE_IP4_CONFIG, - NM_IP4_CONFIG_MULTI_IDX, - multi_idx, - NM_IP4_CONFIG_IFINDEX, - ifindex, - NULL); -} - -static void -finalize(GObject *object) -{ - NMIP4Config * self = NM_IP4_CONFIG(object); - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE(self); - - nm_clear_nmp_object(&priv->best_default_route); - - nm_dedup_multi_index_remove_idx(priv->multi_idx, &priv->idx_ip4_addresses); - nm_dedup_multi_index_remove_idx(priv->multi_idx, &priv->idx_ip4_routes); - - nm_clear_g_variant(&priv->address_data_variant); - nm_clear_g_variant(&priv->addresses_variant); - nm_clear_g_variant(&priv->route_data_variant); - nm_clear_g_variant(&priv->routes_variant); - - g_array_unref(priv->nameservers); - g_ptr_array_unref(priv->domains); - g_ptr_array_unref(priv->searches); - g_ptr_array_unref(priv->dns_options); - g_array_unref(priv->nis); - g_free(priv->nis_domain); - g_array_unref(priv->wins); - - G_OBJECT_CLASS(nm_ip4_config_parent_class)->finalize(object); - - nm_dedup_multi_index_unref(priv->multi_idx); -} - -static const NMDBusInterfaceInfoExtended interface_info_ip4_config = { - .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT( - NM_DBUS_INTERFACE_IP4_CONFIG, - .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS( - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Addresses", - "aau", - NM_IP4_CONFIG_ADDRESSES), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("AddressData", - "aa{sv}", - NM_IP4_CONFIG_ADDRESS_DATA), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Gateway", "s", NM_IP4_CONFIG_GATEWAY), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Routes", "aau", NM_IP4_CONFIG_ROUTES), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("RouteData", - "aa{sv}", - NM_IP4_CONFIG_ROUTE_DATA), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("NameserverData", - "aa{sv}", - NM_IP4_CONFIG_NAMESERVER_DATA), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Nameservers", - "au", - NM_IP4_CONFIG_NAMESERVERS), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Domains", "as", NM_IP4_CONFIG_DOMAINS), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Searches", - "as", - NM_IP4_CONFIG_SEARCHES), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DnsOptions", - "as", - NM_IP4_CONFIG_DNS_OPTIONS), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DnsPriority", - "i", - NM_IP4_CONFIG_DNS_PRIORITY), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WinsServerData", - "as", - NM_IP4_CONFIG_WINS_SERVER_DATA), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WinsServers", - "au", - NM_IP4_CONFIG_WINS_SERVERS), ), ), -}; - -static void -nm_ip4_config_class_init(NMIP4ConfigClass *klass) -{ - GObjectClass * object_class = G_OBJECT_CLASS(klass); - NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass); - NMIPConfigClass * ip_config_class = NM_IP_CONFIG_CLASS(klass); - - ip_config_class->is_ipv4 = TRUE; - ip_config_class->addr_family = AF_INET; - - dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED(NM_DBUS_PATH "/IP4Config"); - dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_ip4_config); - - object_class->get_property = get_property; - object_class->set_property = set_property; - object_class->finalize = finalize; - - obj_properties[PROP_MULTI_IDX] = - g_param_spec_pointer(NM_IP4_CONFIG_MULTI_IDX, - "", - "", - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_IFINDEX] = - g_param_spec_int(NM_IP4_CONFIG_IFINDEX, - "", - "", - -1, - G_MAXINT, - -1, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_ADDRESS_DATA] = - g_param_spec_variant(NM_IP4_CONFIG_ADDRESS_DATA, - "", - "", - G_VARIANT_TYPE("aa{sv}"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_ADDRESSES] = - g_param_spec_variant(NM_IP4_CONFIG_ADDRESSES, - "", - "", - G_VARIANT_TYPE("aau"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_ROUTE_DATA] = - g_param_spec_variant(NM_IP4_CONFIG_ROUTE_DATA, - "", - "", - G_VARIANT_TYPE("aa{sv}"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_ROUTES] = g_param_spec_variant(NM_IP4_CONFIG_ROUTES, - "", - "", - G_VARIANT_TYPE("aau"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_GATEWAY] = g_param_spec_string(NM_IP4_CONFIG_GATEWAY, - "", - "", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_NAMESERVER_DATA] = - g_param_spec_variant(NM_IP4_CONFIG_NAMESERVER_DATA, - "", - "", - G_VARIANT_TYPE("aa{sv}"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_NAMESERVERS] = - g_param_spec_variant(NM_IP4_CONFIG_NAMESERVERS, - "", - "", - G_VARIANT_TYPE("au"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_DOMAINS] = g_param_spec_boxed(NM_IP4_CONFIG_DOMAINS, - "", - "", - G_TYPE_STRV, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_SEARCHES] = g_param_spec_boxed(NM_IP4_CONFIG_SEARCHES, - "", - "", - G_TYPE_STRV, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_DNS_OPTIONS] = - g_param_spec_boxed(NM_IP4_CONFIG_DNS_OPTIONS, - "", - "", - G_TYPE_STRV, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_DNS_PRIORITY] = g_param_spec_int(NM_IP4_CONFIG_DNS_PRIORITY, - "", - "", - G_MININT32, - G_MAXINT32, - 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_WINS_SERVER_DATA] = - g_param_spec_variant(NM_IP4_CONFIG_WINS_SERVER_DATA, - "", - "", - G_VARIANT_TYPE("as"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_WINS_SERVERS] = - g_param_spec_variant(NM_IP4_CONFIG_WINS_SERVERS, - "", - "", - G_VARIANT_TYPE("au"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); -} diff --git a/src/core/nm-ip4-config.h b/src/core/nm-ip4-config.h deleted file mode 100644 index 2eb193b2be..0000000000 --- a/src/core/nm-ip4-config.h +++ /dev/null @@ -1,673 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2008 - 2013 Red Hat, Inc. - */ - -#ifndef __NETWORKMANAGER_IP4_CONFIG_H__ -#define __NETWORKMANAGER_IP4_CONFIG_H__ - -#include "nm-setting-connection.h" - -#include "nm-setting-ip4-config.h" - -#include "libnm-glib-aux/nm-dedup-multi.h" -#include "libnm-platform/nmp-object.h" -#include "nm-ip-config.h" - -/*****************************************************************************/ - -typedef enum _NMIPConfigFlags { - NM_IP_CONFIG_FLAG_NONE = 0, - - /* if set, then the merge flag NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES gets - * ignored during merge. */ - NM_IP_CONFIG_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES = (1ull << 0), -} NMIPConfigFlags; - -typedef struct { - NMDedupMultiIdxType parent; - NMPObjectType obj_type; -} NMIPConfigDedupMultiIdxType; - -void nm_ip_config_dedup_multi_idx_type_init(NMIPConfigDedupMultiIdxType *idx_type, - NMPObjectType obj_type); - -/*****************************************************************************/ - -void nm_ip_config_iter_ip4_address_init(NMDedupMultiIter *iter, const NMIP4Config *self); -void nm_ip_config_iter_ip4_route_init(NMDedupMultiIter *iter, const NMIP4Config *self); - -#define nm_ip_config_iter_ip4_address_for_each(iter, self, address) \ - for (nm_ip_config_iter_ip4_address_init((iter), (self)); \ - nm_platform_dedup_multi_iter_next_ip4_address((iter), (address));) - -#define nm_ip_config_iter_ip4_route_for_each(iter, self, route) \ - for (nm_ip_config_iter_ip4_route_init((iter), (self)); \ - nm_platform_dedup_multi_iter_next_ip4_route((iter), (route));) - -/*****************************************************************************/ - -const NMPObject *_nm_ip_config_best_default_route_find_better(const NMPObject *obj_cur, - const NMPObject *obj_cmp); -gboolean _nm_ip_config_best_default_route_merge(const NMPObject **best_default_route, - const NMPObject * new_candidate); - -/*****************************************************************************/ - -gboolean _nm_ip_config_add_obj(NMDedupMultiIndex * multi_idx, - NMIPConfigDedupMultiIdxType *idx_type, - int ifindex, - const NMPObject * obj_new, - const NMPlatformObject * pl_new, - gboolean merge, - gboolean append_force, - const NMPObject ** out_obj_old, - const NMPObject ** out_obj_new); - -const NMDedupMultiEntry *_nm_ip_config_lookup_ip_route(const NMDedupMultiIndex * multi_idx, - const NMIPConfigDedupMultiIdxType *idx_type, - const NMPObject * needle, - NMPlatformIPRouteCmpType cmp_type); - -/*****************************************************************************/ - -#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_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)) -#define NM_IS_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_IP4_CONFIG)) -#define NM_IP4_CONFIG_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass)) - -typedef struct _NMIP4ConfigClass NMIP4ConfigClass; - -/* internal */ -#define NM_IP4_CONFIG_MULTI_IDX "multi-idx" -#define NM_IP4_CONFIG_IFINDEX "ifindex" - -/* public*/ -#define NM_IP4_CONFIG_ADDRESS_DATA "address-data" -#define NM_IP4_CONFIG_ROUTE_DATA "route-data" -#define NM_IP4_CONFIG_GATEWAY "gateway" -#define NM_IP4_CONFIG_NAMESERVER_DATA "nameserver-data" -#define NM_IP4_CONFIG_DOMAINS "domains" -#define NM_IP4_CONFIG_SEARCHES "searches" -#define NM_IP4_CONFIG_DNS_OPTIONS "dns-options" -#define NM_IP4_CONFIG_DNS_PRIORITY "dns-priority" -#define NM_IP4_CONFIG_WINS_SERVER_DATA "wins-server-data" - -/* deprecated */ -#define NM_IP4_CONFIG_ADDRESSES "addresses" -#define NM_IP4_CONFIG_ROUTES "routes" -#define NM_IP4_CONFIG_NAMESERVERS "nameservers" -#define NM_IP4_CONFIG_WINS_SERVERS "wins-servers" - -GType nm_ip4_config_get_type(void); - -NMIP4Config *nm_ip4_config_new(NMDedupMultiIndex *multi_idx, int ifindex); - -NMIP4Config *nm_ip4_config_clone(const NMIP4Config *self); -int nm_ip4_config_get_ifindex(const NMIP4Config *self); - -NMDedupMultiIndex *nm_ip4_config_get_multi_idx(const NMIP4Config *self); - -NMIP4Config *nm_ip4_config_capture(NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex); - -void nm_ip4_config_add_dependent_routes(NMIP4Config *self, - guint32 route_table, - guint32 route_metric, - gboolean is_vrf, - GPtrArray ** out_ip4_dev_route_blacklist); - -gboolean nm_ip4_config_commit(const NMIP4Config * self, - NMPlatform * platform, - NMIPRouteTableSyncMode route_table_sync); - -void nm_ip4_config_merge_setting(NMIP4Config * self, - NMSettingIPConfig * setting, - NMSettingConnectionMdns mdns, - NMSettingConnectionLlmnr llmnr, - NMSettingConnectionDnsOverTls dns_over_tls, - guint32 route_table, - guint32 route_metric); -NMSetting *nm_ip4_config_create_setting(const NMIP4Config *self); - -void nm_ip4_config_merge(NMIP4Config * dst, - const NMIP4Config * src, - NMIPConfigMergeFlags merge_flags, - guint32 default_route_metric_penalty); -void nm_ip4_config_subtract(NMIP4Config * dst, - const NMIP4Config *src, - guint32 default_route_metric_penalty); -void nm_ip4_config_intersect(NMIP4Config * dst, - const NMIP4Config *src, - gboolean intersect_addresses, - gboolean intersect_routes, - guint32 default_route_metric_penalty); -NMIP4Config *nm_ip4_config_intersect_alloc(const NMIP4Config *a, - const NMIP4Config *b, - gboolean intersect_addresses, - gboolean intersect_routes, - guint32 default_route_metric_penalty); -gboolean -nm_ip4_config_replace(NMIP4Config *dst, const NMIP4Config *src, gboolean *relevant_changes); - -const NMPObject *nm_ip4_config_best_default_route_get(const NMIP4Config *self); -const NMPObject *_nm_ip4_config_best_default_route_find(const NMIP4Config *self); - -in_addr_t nmtst_ip4_config_get_gateway(NMIP4Config *config); - -NMSettingConnectionMdns nm_ip4_config_mdns_get(const NMIP4Config *self); -void nm_ip4_config_mdns_set(NMIP4Config *self, NMSettingConnectionMdns mdns); -NMSettingConnectionLlmnr nm_ip4_config_llmnr_get(const NMIP4Config *self); -void nm_ip4_config_llmnr_set(NMIP4Config *self, NMSettingConnectionLlmnr llmnr); -NMSettingConnectionDnsOverTls nm_ip4_config_dns_over_tls_get(const NMIP4Config *self); -void nm_ip4_config_dns_over_tls_set(NMIP4Config *self, NMSettingConnectionDnsOverTls dns_over_tls); - -void nm_ip4_config_set_config_flags(NMIP4Config *self, NMIPConfigFlags flags, NMIPConfigFlags mask); -NMIPConfigFlags nm_ip4_config_get_config_flags(const NMIP4Config *self); - -const NMDedupMultiHeadEntry *nm_ip4_config_lookup_addresses(const NMIP4Config *self); -void nm_ip4_config_reset_addresses(NMIP4Config *self); -void nm_ip4_config_add_address(NMIP4Config *self, const NMPlatformIP4Address *address); -void _nmtst_ip4_config_del_address(NMIP4Config *self, guint i); -guint nm_ip4_config_get_num_addresses(const NMIP4Config *self); -const NMPlatformIP4Address *nm_ip4_config_get_first_address(const NMIP4Config *self); -const NMPlatformIP4Address *_nmtst_ip4_config_get_address(const NMIP4Config *self, guint i); -gboolean nm_ip4_config_address_exists(const NMIP4Config *self, const NMPlatformIP4Address *address); - -const NMDedupMultiHeadEntry *nm_ip4_config_lookup_routes(const NMIP4Config *self); -void nm_ip4_config_reset_routes(NMIP4Config *self); -void nm_ip4_config_add_route(NMIP4Config * self, - const NMPlatformIP4Route *route, - const NMPObject ** out_obj_new); -void _nmtst_ip4_config_del_route(NMIP4Config *self, guint i); -guint nm_ip4_config_get_num_routes(const NMIP4Config *self); -const NMPlatformIP4Route * _nmtst_ip4_config_get_route(const NMIP4Config *self, guint i); - -const NMPlatformIP4Route *nm_ip4_config_get_direct_route_for_host(const NMIP4Config *self, - in_addr_t host, - guint32 route_table); -void nm_ip4_config_update_routes_metric(NMIP4Config *self, gint64 metric); - -void nm_ip4_config_reset_nameservers(NMIP4Config *self); -void nm_ip4_config_add_nameserver(NMIP4Config *self, guint32 nameserver); - -static inline void -_nm_ip4_config_add_nameserver(NMIP4Config *self, const guint32 *nameserver) -{ - nm_ip4_config_add_nameserver(self, *nameserver); -} - -void nm_ip4_config_del_nameserver(NMIP4Config *self, guint i); -guint nm_ip4_config_get_num_nameservers(const NMIP4Config *self); -guint32 nm_ip4_config_get_nameserver(const NMIP4Config *self, guint i); -const in_addr_t *_nm_ip4_config_get_nameserver(const NMIP4Config *self, guint i); - -void nm_ip4_config_reset_domains(NMIP4Config *self); -void nm_ip4_config_add_domain(NMIP4Config *self, const char *domain); -void nm_ip4_config_del_domain(NMIP4Config *self, guint i); -guint nm_ip4_config_get_num_domains(const NMIP4Config *self); -const char *nm_ip4_config_get_domain(const NMIP4Config *self, guint i); - -void nm_ip4_config_reset_searches(NMIP4Config *self); -void nm_ip4_config_add_search(NMIP4Config *self, const char *search); -void nm_ip4_config_del_search(NMIP4Config *self, guint i); -guint nm_ip4_config_get_num_searches(const NMIP4Config *self); -const char *nm_ip4_config_get_search(const NMIP4Config *self, guint i); - -void nm_ip4_config_reset_dns_options(NMIP4Config *self); -void nm_ip4_config_add_dns_option(NMIP4Config *self, const char *option); -void nm_ip4_config_del_dns_option(NMIP4Config *self, guint i); -guint nm_ip4_config_get_num_dns_options(const NMIP4Config *self); -const char *nm_ip4_config_get_dns_option(const NMIP4Config *self, guint i); - -void nm_ip4_config_set_dns_priority(NMIP4Config *self, int priority); -int nm_ip4_config_get_dns_priority(const NMIP4Config *self); - -void nm_ip4_config_reset_nis_servers(NMIP4Config *self); -void nm_ip4_config_add_nis_server(NMIP4Config *self, guint32 nis); -void nm_ip4_config_del_nis_server(NMIP4Config *self, guint i); -guint nm_ip4_config_get_num_nis_servers(const NMIP4Config *self); -guint32 nm_ip4_config_get_nis_server(const NMIP4Config *self, guint i); -void nm_ip4_config_set_nis_domain(NMIP4Config *self, const char *domain); -const char *nm_ip4_config_get_nis_domain(const NMIP4Config *self); - -void nm_ip4_config_reset_wins(NMIP4Config *self); -void nm_ip4_config_add_wins(NMIP4Config *self, guint32 wins); -void nm_ip4_config_del_wins(NMIP4Config *self, guint i); -guint nm_ip4_config_get_num_wins(const NMIP4Config *self); -guint32 nm_ip4_config_get_wins(const NMIP4Config *self, guint i); - -void nm_ip4_config_set_mtu(NMIP4Config *self, guint32 mtu, NMIPConfigSource source); -guint32 nm_ip4_config_get_mtu(const NMIP4Config *self); -NMIPConfigSource nm_ip4_config_get_mtu_source(const NMIP4Config *self); - -void nm_ip4_config_set_metered(NMIP4Config *self, gboolean metered); -gboolean nm_ip4_config_get_metered(const NMIP4Config *self); - -void nm_ip4_config_set_never_default(NMIP4Config *self, gboolean never_default); -gboolean nm_ip4_config_get_never_default(const NMIP4Config *self); - -const NMPObject *nm_ip4_config_nmpobj_lookup(const NMIP4Config *self, const NMPObject *needle); -gboolean nm_ip4_config_nmpobj_remove(NMIP4Config *self, const NMPObject *needle); - -void nm_ip4_config_hash(const NMIP4Config *self, GChecksum *sum, gboolean dns_only); -gboolean nm_ip4_config_equal(const NMIP4Config *a, const NMIP4Config *b); - -gboolean _nm_ip_config_check_and_add_domain(GPtrArray *array, const char *domain); - -void -nm_ip_config_dump(const NMIPConfig *self, const char *detail, NMLogLevel level, NMLogDomain domain); - -/*****************************************************************************/ - -#include "nm-ip6-config.h" - -static inline gboolean -NM_IS_IP_CONFIG_ADDR_FAMILY(gconstpointer config, int addr_family) -{ - if (addr_family == AF_UNSPEC) - return NM_IS_IP4_CONFIG(config) || NM_IS_IP6_CONFIG(config); - if (addr_family == AF_INET) - return NM_IS_IP4_CONFIG(config); - if (addr_family == AF_INET6) - return NM_IS_IP6_CONFIG(config); - g_return_val_if_reached(FALSE); -} - -#if _NM_CC_SUPPORT_GENERIC -/* _NM_IS_IP_CONFIG() is a bit unusual. If _Generic() is supported, - * it checks whether @config is either NM_IS_IP4_CONFIG() or NM_IS_IP6_CONFIG(), - * depending on the pointer type of @config. - * - * For example, with _Generic() support, the following assertions would fail: - * NMIP6Config *ptr = (NMIP6Config *) nm_ip4_config_new(...); - * g_assert (_NM_IS_IP_CONFIG (ptr, ptr)); - * but the following would pass: - * NMIP4Config *ptr = nm_ip4_config_new(...); - * g_assert (_NM_IS_IP_CONFIG (ptr, ptr)); - */ -#define _NM_IS_IP_CONFIG(typeexpr, config) \ - ({ \ - const void *const _config = (config); \ - _Generic ((typeexpr), \ - const void *const: (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \ - const void * : (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \ - void *const: (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \ - void * : (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \ - const NMIPConfig *const: (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \ - const NMIPConfig * : (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \ - NMIPConfig *const: (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \ - NMIPConfig * : (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \ - const NMIP4Config *const: (NM_IS_IP4_CONFIG (_config)), \ - const NMIP4Config * : (NM_IS_IP4_CONFIG (_config)), \ - NMIP4Config *const: (NM_IS_IP4_CONFIG (_config)), \ - NMIP4Config * : (NM_IS_IP4_CONFIG (_config)), \ - const NMIP6Config *const: (NM_IS_IP6_CONFIG (_config)), \ - const NMIP6Config * : (NM_IS_IP6_CONFIG (_config)), \ - NMIP6Config *const: (NM_IS_IP6_CONFIG (_config)), \ - NMIP6Config * : (NM_IS_IP6_CONFIG (_config))); \ - }) -#else -#define _NM_IS_IP_CONFIG(typeexpr, config) NM_IS_IP_CONFIG(config) -#endif - -#define NM_IP_CONFIG_CAST(config) \ - ({ \ - const void *const _configx = (config); \ - \ - nm_assert(!_configx || _NM_IS_IP_CONFIG((config), _configx)); \ - NM_CONSTCAST_FULL(NMIPConfig, (config), _configx, NMIP4Config, NMIP6Config); \ - }) - -static inline gboolean -nm_ip_config_is_ipv4(const NMIPConfig *config) -{ - if (NM_IP_CONFIG_GET_CLASS(config)->is_ipv4) { - nm_assert(NM_IS_IP4_CONFIG(config)); - return TRUE; - } - nm_assert(NM_IS_IP6_CONFIG(config)); - return FALSE; -} - -static inline int -nm_ip_config_get_addr_family(const NMIPConfig *config) -{ - return nm_ip_config_is_ipv4(config) ? AF_INET : AF_INET6; -} - -#define _NM_IP_CONFIG_DISPATCH(config, v4_func, v6_func, ...) \ - G_STMT_START \ - { \ - gconstpointer _config = (config); \ - \ - if (nm_ip_config_is_ipv4(_config)) { \ - return v4_func((NMIP4Config *) _config, ##__VA_ARGS__); \ - } else { \ - return v6_func((NMIP6Config *) _config, ##__VA_ARGS__); \ - } \ - } \ - G_STMT_END - -#define _NM_IP_CONFIG_DISPATCH_VOID(config, v4_func, v6_func, ...) \ - G_STMT_START \ - { \ - gconstpointer _config = (config); \ - \ - if (nm_ip_config_is_ipv4(_config)) { \ - v4_func((NMIP4Config *) _config, ##__VA_ARGS__); \ - } else { \ - v6_func((NMIP6Config *) _config, ##__VA_ARGS__); \ - } \ - } \ - G_STMT_END - -static inline int -nm_ip_config_get_ifindex(const NMIPConfig *self) -{ - _NM_IP_CONFIG_DISPATCH(self, nm_ip4_config_get_ifindex, nm_ip6_config_get_ifindex); -} - -static inline void -nm_ip_config_hash(const NMIPConfig *self, GChecksum *sum, gboolean dns_only) -{ - _NM_IP_CONFIG_DISPATCH_VOID(self, nm_ip4_config_hash, nm_ip6_config_hash, sum, dns_only); -} - -static inline gconstpointer -nm_ip_config_get_first_address(NMIPConfig *self) -{ - _NM_IP_CONFIG_DISPATCH(self, nm_ip4_config_get_first_address, nm_ip6_config_get_first_address); -} - -static inline void -nm_ip_config_iter_ip_address_init(NMDedupMultiIter *iter, const NMIPConfig *self) -{ - if (nm_ip_config_is_ipv4(self)) - nm_ip_config_iter_ip4_address_init(iter, (const NMIP4Config *) self); - else - nm_ip_config_iter_ip6_address_init(iter, (const NMIP6Config *) self); -} - -#define nm_ip_config_iter_ip_address_for_each(iter, self, address) \ - for (nm_ip_config_iter_ip_address_init((iter), (self)); \ - nm_platform_dedup_multi_iter_next_ip_address((iter), (address));) - -static inline void -nm_ip_config_iter_ip_route_init(NMDedupMultiIter *iter, const NMIPConfig *self) -{ - if (nm_ip_config_is_ipv4(self)) - nm_ip_config_iter_ip4_route_init(iter, (const NMIP4Config *) self); - else - nm_ip_config_iter_ip6_route_init(iter, (const NMIP6Config *) self); -} - -#define nm_ip_config_iter_ip_route_for_each(iter, self, route) \ - for (nm_ip_config_iter_ip_route_init((iter), (self)); \ - nm_platform_dedup_multi_iter_next_ip_route((iter), (route));) - -static inline void -nm_ip_config_add_address(NMIPConfig *self, const NMPlatformIPAddress *address) -{ - _NM_IP_CONFIG_DISPATCH_VOID(self, - nm_ip4_config_add_address, - nm_ip6_config_add_address, - (gconstpointer) address); -} - -static inline void -nm_ip_config_reset_addresses(NMIPConfig *self) -{ - _NM_IP_CONFIG_DISPATCH_VOID(self, nm_ip4_config_reset_addresses, nm_ip6_config_reset_addresses); -} - -static inline void -nm_ip_config_add_route(NMIPConfig *self, - const NMPlatformIPRoute *new, - const NMPObject **out_obj_new) -{ - _NM_IP_CONFIG_DISPATCH_VOID(self, - nm_ip4_config_add_route, - nm_ip6_config_add_route, - (gpointer) new, - out_obj_new); -} - -static inline void -nm_ip_config_reset_routes(NMIPConfig *self) -{ - _NM_IP_CONFIG_DISPATCH_VOID(self, nm_ip4_config_reset_routes, nm_ip6_config_reset_routes); -} - -static inline int -nm_ip_config_get_dns_priority(const NMIPConfig *self) -{ - _NM_IP_CONFIG_DISPATCH(self, nm_ip4_config_get_dns_priority, nm_ip6_config_get_dns_priority); -} - -static inline void -nm_ip_config_set_dns_priority(NMIPConfig *self, int priority) -{ - _NM_IP_CONFIG_DISPATCH_VOID(self, - nm_ip4_config_set_dns_priority, - nm_ip6_config_set_dns_priority, - priority); -} - -static inline void -nm_ip_config_add_nameserver(NMIPConfig *self, const NMIPAddr *ns) -{ - _NM_IP_CONFIG_DISPATCH_VOID(self, - _nm_ip4_config_add_nameserver, - nm_ip6_config_add_nameserver, - (gconstpointer) ns); -} - -static inline void -nm_ip_config_reset_nameservers(const NMIPConfig *self) -{ - _NM_IP_CONFIG_DISPATCH_VOID(self, - nm_ip4_config_reset_nameservers, - nm_ip6_config_reset_nameservers); -} - -static inline guint -nm_ip_config_get_num_nameservers(const NMIPConfig *self) -{ - _NM_IP_CONFIG_DISPATCH(self, - nm_ip4_config_get_num_nameservers, - nm_ip6_config_get_num_nameservers); -} - -static inline gconstpointer -nm_ip_config_get_nameserver(const NMIPConfig *self, guint i) -{ - _NM_IP_CONFIG_DISPATCH(self, _nm_ip4_config_get_nameserver, nm_ip6_config_get_nameserver, i); -} - -static inline guint -nm_ip_config_get_num_domains(const NMIPConfig *self) -{ - _NM_IP_CONFIG_DISPATCH(self, nm_ip4_config_get_num_domains, nm_ip6_config_get_num_domains); -} - -static inline const char * -nm_ip_config_get_domain(const NMIPConfig *self, guint i) -{ - _NM_IP_CONFIG_DISPATCH(self, nm_ip4_config_get_domain, nm_ip6_config_get_domain, i); -} - -static inline void -nm_ip_config_reset_searches(const NMIPConfig *self) -{ - _NM_IP_CONFIG_DISPATCH_VOID(self, nm_ip4_config_reset_searches, nm_ip6_config_reset_searches); -} - -static inline void -nm_ip_config_add_search(const NMIPConfig *self, const char *new) -{ - _NM_IP_CONFIG_DISPATCH_VOID(self, nm_ip4_config_add_search, nm_ip6_config_add_search, new); -} - -static inline guint -nm_ip_config_get_num_searches(const NMIPConfig *self) -{ - _NM_IP_CONFIG_DISPATCH(self, nm_ip4_config_get_num_searches, nm_ip6_config_get_num_searches); -} - -static inline const char * -nm_ip_config_get_search(const NMIPConfig *self, guint i) -{ - _NM_IP_CONFIG_DISPATCH(self, nm_ip4_config_get_search, nm_ip6_config_get_search, i); -} - -static inline guint -nm_ip_config_get_num_dns_options(const NMIPConfig *self) -{ - _NM_IP_CONFIG_DISPATCH(self, - nm_ip4_config_get_num_dns_options, - nm_ip6_config_get_num_dns_options); -} - -static inline const char * -nm_ip_config_get_dns_option(const NMIPConfig *self, guint i) -{ - _NM_IP_CONFIG_DISPATCH(self, nm_ip4_config_get_dns_option, nm_ip6_config_get_dns_option, i); -} - -static inline const NMPObject * -nm_ip_config_best_default_route_get(const NMIPConfig *self) -{ - _NM_IP_CONFIG_DISPATCH(self, - nm_ip4_config_best_default_route_get, - nm_ip6_config_best_default_route_get); -} - -static inline NMIPConfigFlags -nm_ip_config_get_config_flags(const NMIPConfig *self) -{ - _NM_IP_CONFIG_DISPATCH(self, nm_ip4_config_get_config_flags, nm_ip6_config_get_config_flags); -} - -static inline void -nm_ip_config_set_config_flags(NMIPConfig *self, NMIPConfigFlags flags, NMIPConfigFlags mask) -{ - _NM_IP_CONFIG_DISPATCH_VOID(self, - nm_ip4_config_set_config_flags, - nm_ip6_config_set_config_flags, - flags, - mask); -} - -static inline gboolean -nm_ip_config_get_never_default(const NMIPConfig *self) -{ - _NM_IP_CONFIG_DISPATCH(self, nm_ip4_config_get_never_default, nm_ip6_config_get_never_default); -} - -static inline void -nm_ip_config_set_never_default(NMIPConfig *self, gboolean never_default) -{ - _NM_IP_CONFIG_DISPATCH_VOID(self, - nm_ip4_config_set_never_default, - nm_ip6_config_set_never_default, - never_default); -} - -#define _NM_IP_CONFIG_DISPATCH_SET_OP(_return, dst, src, v4_func, v6_func, ...) \ - G_STMT_START \ - { \ - gpointer _dst = (dst); \ - gconstpointer _src = (src); \ - \ - if (nm_ip_config_is_ipv4(_dst)) { \ - _return v4_func((NMIP4Config *) _dst, (const NMIP4Config *) _src, ##__VA_ARGS__); \ - } else { \ - _return v6_func((NMIP6Config *) _dst, (const NMIP6Config *) _src, ##__VA_ARGS__); \ - } \ - } \ - G_STMT_END - -static inline void -nm_ip_config_intersect(NMIPConfig * dst, - const NMIPConfig *src, - gboolean intersect_addresses, - gboolean intersect_routes, - guint32 default_route_metric_penalty) -{ - _NM_IP_CONFIG_DISPATCH_SET_OP(, - dst, - src, - nm_ip4_config_intersect, - nm_ip6_config_intersect, - intersect_addresses, - intersect_routes, - default_route_metric_penalty); -} - -static inline void -nm_ip_config_subtract(NMIPConfig *dst, const NMIPConfig *src, guint32 default_route_metric_penalty) -{ - _NM_IP_CONFIG_DISPATCH_SET_OP(, - dst, - src, - nm_ip4_config_subtract, - nm_ip6_config_subtract, - default_route_metric_penalty); -} - -static inline void -nm_ip_config_merge(NMIPConfig * dst, - const NMIPConfig * src, - NMIPConfigMergeFlags merge_flags, - guint32 default_route_metric_penalty) -{ - _NM_IP_CONFIG_DISPATCH_SET_OP(, - dst, - src, - nm_ip4_config_merge, - nm_ip6_config_merge, - merge_flags, - default_route_metric_penalty); -} - -static inline gboolean -nm_ip_config_replace(NMIPConfig *dst, const NMIPConfig *src, gboolean *relevant_changes) -{ - _NM_IP_CONFIG_DISPATCH_SET_OP( - return, dst, src, nm_ip4_config_replace, nm_ip6_config_replace, relevant_changes); -} - -static inline NMIPConfig * -nm_ip_config_intersect_alloc(const NMIPConfig *a, - const NMIPConfig *b, - gboolean intersect_addresses, - gboolean intersect_routes, - guint32 default_route_metric_penalty) -{ - if (nm_ip_config_is_ipv4(a)) { - nm_assert(NM_IS_IP4_CONFIG(a)); - nm_assert(NM_IS_IP4_CONFIG(b)); - return (NMIPConfig *) nm_ip4_config_intersect_alloc((const NMIP4Config *) a, - (const NMIP4Config *) b, - intersect_addresses, - intersect_routes, - default_route_metric_penalty); - } else { - nm_assert(NM_IS_IP6_CONFIG(a)); - nm_assert(NM_IS_IP6_CONFIG(b)); - return (NMIPConfig *) nm_ip6_config_intersect_alloc((const NMIP6Config *) a, - (const NMIP6Config *) b, - intersect_addresses, - intersect_routes, - default_route_metric_penalty); - } -} - -gconstpointer nm_ip_config_find_first_address(const NMIPConfig * self, - NMPlatformMatchFlags match_flag); - -#endif /* __NETWORKMANAGER_IP4_CONFIG_H__ */ diff --git a/src/core/nm-ip6-config.c b/src/core/nm-ip6-config.c deleted file mode 100644 index 5ec68bd28c..0000000000 --- a/src/core/nm-ip6-config.c +++ /dev/null @@ -1,2675 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2005 - 2017 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#include "src/core/nm-default-daemon.h" - -#include "nm-ip6-config.h" - -#include -#include -#include -#include - -#include "libnm-glib-aux/nm-dedup-multi.h" - -#include "nm-utils.h" -#include "libnm-platform/nmp-object.h" -#include "libnm-platform/nm-platform.h" -#include "libnm-platform/nm-platform-utils.h" -#include "libnm-core-intern/nm-core-internal.h" -#include "NetworkManagerUtils.h" -#include "nm-ip4-config.h" -#include "ndisc/nm-ndisc.h" -#include "nm-dbus-object.h" - -/*****************************************************************************/ - -static gboolean -_route_valid(const NMPlatformIP6Route *r) -{ - struct in6_addr n; - - return r && r->plen <= 128 - && (memcmp(&r->network, - nm_utils_ip6_address_clear_host_address(&n, &r->network, r->plen), - sizeof(n)) - == 0); -} - -/*****************************************************************************/ - -typedef struct { - int ifindex; - int dns_priority; - NMSettingIP6ConfigPrivacy privacy; - GArray * nameservers; - GPtrArray * domains; - GPtrArray * searches; - GPtrArray * dns_options; - GVariant * address_data_variant; - GVariant * addresses_variant; - GVariant * route_data_variant; - GVariant * routes_variant; - NMDedupMultiIndex * multi_idx; - const NMPObject * best_default_route; - union { - NMIPConfigDedupMultiIdxType idx_ip6_addresses_; - NMDedupMultiIdxType idx_ip6_addresses; - }; - union { - NMIPConfigDedupMultiIdxType idx_ip6_routes_; - NMDedupMultiIdxType idx_ip6_routes; - }; - NMIPConfigFlags config_flags; - bool never_default : 1; -} NMIP6ConfigPrivate; - -struct _NMIP6Config { - NMIPConfig parent; - NMIP6ConfigPrivate _priv; -}; - -struct _NMIP6ConfigClass { - NMIPConfigClass parent; -}; - -G_DEFINE_TYPE(NMIP6Config, nm_ip6_config, NM_TYPE_IP_CONFIG) - -#define NM_IP6_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMIP6Config, NM_IS_IP6_CONFIG) - -NM_GOBJECT_PROPERTIES_DEFINE(NMIP6Config, - PROP_MULTI_IDX, - PROP_IFINDEX, - PROP_ADDRESS_DATA, - PROP_ADDRESSES, - PROP_ROUTE_DATA, - PROP_ROUTES, - PROP_GATEWAY, - PROP_NAMESERVERS, - PROP_DOMAINS, - PROP_SEARCHES, - PROP_DNS_OPTIONS, - PROP_DNS_PRIORITY, ); - -/*****************************************************************************/ - -static void -_add_address(NMIP6Config *self, const NMPObject *obj_new, const NMPlatformIP6Address *new); -static void _add_route(NMIP6Config * self, - const NMPObject *obj_new, - const NMPlatformIP6Route *new, - const NMPObject **out_obj_new); -static const NMDedupMultiEntry * -_lookup_route(const NMIP6Config *self, const NMPObject *needle, NMPlatformIPRouteCmpType cmp_type); - -/*****************************************************************************/ - -int -nm_ip6_config_get_ifindex(const NMIP6Config *self) -{ - return NM_IP6_CONFIG_GET_PRIVATE(self)->ifindex; -} - -NMDedupMultiIndex * -nm_ip6_config_get_multi_idx(const NMIP6Config *self) -{ - return NM_IP6_CONFIG_GET_PRIVATE(self)->multi_idx; -} - -void -nm_ip6_config_set_privacy(NMIP6Config *self, NMSettingIP6ConfigPrivacy privacy) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - priv->privacy = privacy; -} - -/*****************************************************************************/ - -void -nm_ip6_config_set_never_default(NMIP6Config *self, gboolean never_default) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - priv->never_default = never_default; -} - -gboolean -nm_ip6_config_get_never_default(const NMIP6Config *self) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - return priv->never_default; -} - -/*****************************************************************************/ - -const NMDedupMultiHeadEntry * -nm_ip6_config_lookup_addresses(const NMIP6Config *self) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - return nm_dedup_multi_index_lookup_head(priv->multi_idx, &priv->idx_ip6_addresses, NULL); -} - -void -nm_ip_config_iter_ip6_address_init(NMDedupMultiIter *ipconf_iter, const NMIP6Config *self) -{ - nm_assert(NM_IS_IP6_CONFIG(self)); - - nm_dedup_multi_iter_init(ipconf_iter, nm_ip6_config_lookup_addresses(self)); -} - -/*****************************************************************************/ - -const NMDedupMultiHeadEntry * -nm_ip6_config_lookup_routes(const NMIP6Config *self) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - return nm_dedup_multi_index_lookup_head(priv->multi_idx, &priv->idx_ip6_routes, NULL); -} - -void -nm_ip_config_iter_ip6_route_init(NMDedupMultiIter *ipconf_iter, const NMIP6Config *self) -{ - nm_assert(NM_IS_IP6_CONFIG(self)); - - nm_dedup_multi_iter_init(ipconf_iter, nm_ip6_config_lookup_routes(self)); -} - -/*****************************************************************************/ - -const NMPObject * -nm_ip6_config_best_default_route_get(const NMIP6Config *self) -{ - g_return_val_if_fail(NM_IS_IP6_CONFIG(self), NULL); - - return NM_IP6_CONFIG_GET_PRIVATE(self)->best_default_route; -} - -const NMPObject * -_nm_ip6_config_best_default_route_find(const NMIP6Config *self) -{ - NMDedupMultiIter ipconf_iter; - const NMPObject *new_best_default_route = NULL; - - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, NULL) { - new_best_default_route = - _nm_ip_config_best_default_route_find_better(new_best_default_route, - ipconf_iter.current->obj); - } - return new_best_default_route; -} - -/*****************************************************************************/ - -static void -_notify_addresses(NMIP6Config *self) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - nm_clear_g_variant(&priv->address_data_variant); - nm_clear_g_variant(&priv->addresses_variant); - nm_gobject_notify_together(self, PROP_ADDRESS_DATA, PROP_ADDRESSES); -} - -static void -_notify_routes(NMIP6Config *self) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - nm_assert(priv->best_default_route == _nm_ip6_config_best_default_route_find(self)); - nm_clear_g_variant(&priv->route_data_variant); - nm_clear_g_variant(&priv->routes_variant); - nm_gobject_notify_together(self, PROP_ROUTE_DATA, PROP_ROUTES); -} - -/*****************************************************************************/ - -static int -sort_captured_addresses(const CList *lst_a, const CList *lst_b, gconstpointer user_data) -{ - return nm_platform_ip6_address_pretty_sort_cmp( - NMP_OBJECT_CAST_IP6_ADDRESS(c_list_entry(lst_a, NMDedupMultiEntry, lst_entries)->obj), - NMP_OBJECT_CAST_IP6_ADDRESS(c_list_entry(lst_b, NMDedupMultiEntry, lst_entries)->obj), - (((NMSettingIP6ConfigPrivacy) GPOINTER_TO_INT(user_data)) - == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR)); -} - -gboolean -_nmtst_ip6_config_addresses_sort(NMIP6Config *self) -{ - NMIP6ConfigPrivate * priv; - const NMDedupMultiHeadEntry *head_entry; - - g_return_val_if_fail(NM_IS_IP6_CONFIG(self), FALSE); - - head_entry = nm_ip6_config_lookup_addresses(self); - if (head_entry && head_entry->len > 1) { - gboolean changed; - gs_free gconstpointer *addresses_old = NULL; - guint naddr, j; - NMDedupMultiIter iter; - - priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - addresses_old = nm_dedup_multi_objs_to_array_head(head_entry, NULL, NULL, &naddr); - nm_assert(addresses_old); - nm_assert(naddr > 0 && naddr == head_entry->len); - - nm_dedup_multi_head_entry_sort(head_entry, - sort_captured_addresses, - GINT_TO_POINTER(priv->privacy)); - - changed = FALSE; - j = 0; - nm_dedup_multi_iter_for_each (&iter, head_entry) { - nm_assert(j < naddr); - if (iter.current->obj != addresses_old[j++]) - changed = TRUE; - } - nm_assert(j == naddr); - - if (changed) { - _notify_addresses(self); - return TRUE; - } - } - return FALSE; -} - -NMIP6Config * -nm_ip6_config_clone(const NMIP6Config *self) -{ - NMIP6Config *copy; - - copy = nm_ip6_config_new(nm_ip6_config_get_multi_idx(self), -1); - nm_ip6_config_replace(copy, self, NULL); - - return copy; -} - -NMIP6Config * -nm_ip6_config_capture(NMDedupMultiIndex * multi_idx, - NMPlatform * platform, - int ifindex, - NMSettingIP6ConfigPrivacy use_temporary) -{ - NMIP6Config * self; - NMIP6ConfigPrivate * priv; - const NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIter iter; - const NMPObject * plobj = NULL; - - nm_assert(ifindex > 0); - - /* Slaves have no IP configuration */ - if (nm_platform_link_get_master(platform, ifindex) > 0) - return NULL; - - self = nm_ip6_config_new(multi_idx, ifindex); - priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - head_entry = nm_platform_lookup_object(platform, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex); - if (head_entry) { - nmp_cache_iter_for_each_reverse (&iter, head_entry, &plobj) { - if (!_nm_ip_config_add_obj(priv->multi_idx, - &priv->idx_ip6_addresses_, - ifindex, - plobj, - NULL, - FALSE, - TRUE, - NULL, - NULL)) - nm_assert_not_reached(); - } - _notify_addresses(self); - } - - head_entry = nm_platform_lookup_object(platform, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex); - - nmp_cache_iter_for_each (&iter, head_entry, &plobj) - _add_route(self, plobj, NULL, NULL); - - return self; -} - -void -nm_ip6_config_update_routes_metric(NMIP6Config *self, gint64 metric) -{ - gs_free NMPlatformIP6Route *routes = NULL; - gboolean need_update = FALSE; - const NMPlatformIP6Route * r; - NMDedupMultiIter iter; - guint num = 0, i = 0; - - nm_ip_config_iter_ip6_route_for_each (&iter, self, &r) { - if (r->metric != metric) - need_update = TRUE; - num++; - } - if (!need_update) - return; - - routes = g_new(NMPlatformIP6Route, num); - nm_ip_config_iter_ip6_route_for_each (&iter, self, &r) { - routes[i] = *r; - routes[i].metric = metric; - i++; - } - - g_object_freeze_notify(G_OBJECT(self)); - nm_ip6_config_reset_routes(self); - for (i = 0; i < num; i++) - nm_ip6_config_add_route(self, &routes[i], NULL); - g_object_thaw_notify(G_OBJECT(self)); -} - -void -nm_ip6_config_add_dependent_routes(NMIP6Config *self, - guint32 route_table, - guint32 route_metric, - gboolean is_vrf) -{ - const NMPlatformIP6Address *my_addr; - const NMPlatformIP6Route * my_route; - int ifindex; - NMDedupMultiIter iter; - - g_return_if_fail(NM_IS_IP6_CONFIG(self)); - - ifindex = nm_ip6_config_get_ifindex(self); - g_return_if_fail(ifindex > 0); - - /* For IPv6 addresses received via SLAAC/autoconf, we explicitly add the - * device-routes (onlink) to NMIP6Config. - * - * For manually added IPv6 routes, add the device routes explicitly. */ - - nm_ip_config_iter_ip6_address_for_each (&iter, self, &my_addr) { - NMPlatformIP6Route *route; - gboolean has_peer; - int routes_n, routes_i; - - if (my_addr->external) - continue; - - if (NM_FLAGS_HAS(my_addr->n_ifa_flags, IFA_F_NOPREFIXROUTE)) - continue; - if (my_addr->plen == 0) - continue; - - has_peer = !IN6_IS_ADDR_UNSPECIFIED(&my_addr->peer_address); - - /* If we have an IPv6 peer, we add two /128 routes - * (unless, both addresses are identical). */ - routes_n = - (has_peer && !IN6_ARE_ADDR_EQUAL(&my_addr->address, &my_addr->peer_address)) ? 2 : 1; - - for (routes_i = 0; routes_i < routes_n; routes_i++) { - nm_auto_nmpobj NMPObject *r = NULL; - - r = nmp_object_new(NMP_OBJECT_TYPE_IP6_ROUTE, NULL); - route = NMP_OBJECT_CAST_IP6_ROUTE(r); - - route->ifindex = ifindex; - route->rt_source = NM_IP_CONFIG_SOURCE_KERNEL; - route->table_coerced = nm_platform_route_table_coerce(route_table); - route->metric = route_metric; - - if (has_peer) { - if (routes_i == 0) - route->network = my_addr->address; - else - route->network = my_addr->peer_address; - route->plen = 128; - } else { - nm_utils_ip6_address_clear_host_address(&route->network, - &my_addr->address, - my_addr->plen); - route->plen = my_addr->plen; - } - - nm_platform_ip_route_normalize(AF_INET6, (NMPlatformIPRoute *) route); - - if (_lookup_route(self, r, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) { - /* we already track this route. Don't add it again. */ - } else - _add_route(self, r, NULL, NULL); - } - } - -again: - nm_ip_config_iter_ip6_route_for_each (&iter, self, &my_route) { - NMPlatformIP6Route rt; - - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT(my_route) - || IN6_IS_ADDR_UNSPECIFIED(&my_route->gateway) - || NM_IS_IP_CONFIG_SOURCE_RTPROT(my_route->rt_source) - || nm_ip6_config_get_direct_route_for_host( - self, - &my_route->gateway, - nm_platform_route_table_uncoerce(my_route->table_coerced, TRUE))) - continue; - - rt = *my_route; - rt.network = my_route->gateway; - rt.plen = 128; - rt.gateway = in6addr_any; - _add_route(self, NULL, &rt, NULL); - /* adding the route might have invalidated the iteration. Start again. */ - goto again; - } -} - -gboolean -nm_ip6_config_commit(const NMIP6Config * self, - NMPlatform * platform, - NMIPRouteTableSyncMode route_table_sync, - GPtrArray ** out_temporary_not_available) -{ - gs_unref_ptrarray GPtrArray *addresses = NULL; - gs_unref_ptrarray GPtrArray *routes = NULL; - gs_unref_ptrarray GPtrArray *routes_prune = NULL; - int ifindex; - gboolean success = TRUE; - - g_return_val_if_fail(NM_IS_IP6_CONFIG(self), FALSE); - - ifindex = nm_ip6_config_get_ifindex(self); - g_return_val_if_fail(ifindex > 0, FALSE); - - addresses = - nm_dedup_multi_objs_to_ptr_array_head(nm_ip6_config_lookup_addresses(self), NULL, NULL); - - routes = nm_dedup_multi_objs_to_ptr_array_head(nm_ip6_config_lookup_routes(self), NULL, NULL); - - routes_prune = - nm_platform_ip_route_get_prune_list(platform, AF_INET6, ifindex, route_table_sync); - - nm_platform_ip6_address_sync(platform, ifindex, addresses, FALSE); - - if (!nm_platform_ip_route_sync(platform, - AF_INET6, - ifindex, - routes, - routes_prune, - out_temporary_not_available)) - success = FALSE; - - return success; -} - -void -nm_ip6_config_merge_setting(NMIP6Config * self, - NMSettingIPConfig *setting, - guint32 route_table, - guint32 route_metric) -{ - guint naddresses, nroutes, nnameservers, nsearches; - const char * gateway_str; - struct in6_addr gateway_bin; - int i, priority; - - if (!setting) - return; - - g_return_if_fail(NM_IS_SETTING_IP6_CONFIG(setting)); - - naddresses = nm_setting_ip_config_get_num_addresses(setting); - nroutes = nm_setting_ip_config_get_num_routes(setting); - nnameservers = nm_setting_ip_config_get_num_dns(setting); - nsearches = nm_setting_ip_config_get_num_dns_searches(setting); - - g_object_freeze_notify(G_OBJECT(self)); - - /* Gateway */ - if (!nm_setting_ip_config_get_never_default(setting) - && (gateway_str = nm_setting_ip_config_get_gateway(setting)) - && inet_pton(AF_INET6, gateway_str, &gateway_bin) == 1 - && !IN6_IS_ADDR_UNSPECIFIED(&gateway_bin)) { - const NMPlatformIP6Route r = { - .rt_source = NM_IP_CONFIG_SOURCE_USER, - .gateway = gateway_bin, - .table_coerced = nm_platform_route_table_coerce(route_table), - .metric = route_metric, - }; - - _add_route(self, NULL, &r, NULL); - } - - /* Addresses */ - for (i = 0; i < naddresses; i++) { - NMIPAddress * s_addr = nm_setting_ip_config_get_address(setting, i); - NMPlatformIP6Address address; - - memset(&address, 0, sizeof(address)); - nm_ip_address_get_address_binary(s_addr, &address.address); - address.plen = nm_ip_address_get_prefix(s_addr); - nm_assert(address.plen <= 128); - address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT; - address.preferred = NM_PLATFORM_LIFETIME_PERMANENT; - address.addr_source = NM_IP_CONFIG_SOURCE_USER; - - _add_address(self, NULL, &address); - } - - /* Routes */ - for (i = 0; i < nroutes; i++) { - NMIPRoute * s_route = nm_setting_ip_config_get_route(setting, i); - NMPlatformIP6Route route; - gint64 m; - - if (nm_ip_route_get_family(s_route) != AF_INET6) { - nm_assert_not_reached(); - continue; - } - - memset(&route, 0, sizeof(route)); - nm_ip_route_get_dest_binary(s_route, &route.network); - - route.plen = nm_ip_route_get_prefix(s_route); - nm_assert(route.plen <= 128); - - nm_ip_route_get_next_hop_binary(s_route, &route.gateway); - m = nm_ip_route_get_metric(s_route); - if (m < 0) - route.metric = route_metric; - else - route.metric = nm_utils_ip6_route_metric_normalize(m); - route.rt_source = NM_IP_CONFIG_SOURCE_USER; - - nm_utils_ip6_address_clear_host_address(&route.network, &route.network, route.plen); - - nm_utils_ip_route_attribute_to_platform(AF_INET6, - s_route, - NM_PLATFORM_IP_ROUTE_CAST(&route), - route_table); - _add_route(self, NULL, &route, NULL); - } - - /* DNS */ - if (nm_setting_ip_config_get_ignore_auto_dns(setting)) { - nm_ip6_config_reset_nameservers(self); - nm_ip6_config_reset_domains(self); - nm_ip6_config_reset_searches(self); - } - for (i = 0; i < nnameservers; i++) { - struct in6_addr ip; - - if (inet_pton(AF_INET6, nm_setting_ip_config_get_dns(setting, i), &ip) == 1) - nm_ip6_config_add_nameserver(self, &ip); - } - for (i = 0; i < nsearches; i++) - nm_ip6_config_add_search(self, nm_setting_ip_config_get_dns_search(setting, i)); - - i = 0; - while ((i = nm_setting_ip_config_next_valid_dns_option(setting, i)) >= 0) { - nm_ip6_config_add_dns_option(self, nm_setting_ip_config_get_dns_option(setting, i)); - i++; - } - - priority = nm_setting_ip_config_get_dns_priority(setting); - if (priority) - nm_ip6_config_set_dns_priority(self, priority); - - nm_ip6_config_set_never_default(self, nm_setting_ip_config_get_never_default(setting)); - - g_object_thaw_notify(G_OBJECT(self)); -} - -NMSetting * -nm_ip6_config_create_setting(const NMIP6Config *self, gboolean maybe_ipv6_disabled) -{ - const NMIP6ConfigPrivate * priv; - NMSettingIPConfig * s_ip6; - guint nnameservers, nsearches, noptions; - const char * method = NULL; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - int i; - NMDedupMultiIter ipconf_iter; - const NMPlatformIP6Address *address; - const NMPlatformIP6Route * route; - - s_ip6 = NM_SETTING_IP_CONFIG(nm_setting_ip6_config_new()); - - if (!self) { - g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); - return NM_SETTING(s_ip6); - } - - priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - nnameservers = nm_ip6_config_get_num_nameservers(self); - nsearches = nm_ip6_config_get_num_searches(self); - noptions = nm_ip6_config_get_num_dns_options(self); - - /* Addresses */ - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, self, &address) { - NMIPAddress *s_addr; - - /* Ignore link-local address. */ - if (IN6_IS_ADDR_LINKLOCAL(&address->address)) { - if (!method) - method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; - continue; - } - - /* Detect dynamic address */ - if (address->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { - method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; - continue; - } - - /* Static address found. */ - if (!method || strcmp(method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) - method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; - - s_addr = nm_ip_address_new_binary(AF_INET6, &address->address, address->plen, NULL); - nm_setting_ip_config_add_address(s_ip6, s_addr); - nm_ip_address_unref(s_addr); - } - - /* Gateway */ - if (priv->best_default_route && nm_setting_ip_config_get_num_addresses(s_ip6) > 0) { - g_object_set( - s_ip6, - NM_SETTING_IP_CONFIG_GATEWAY, - _nm_utils_inet6_ntop(&NMP_OBJECT_CAST_IP6_ROUTE(priv->best_default_route)->gateway, - sbuf), - NULL); - } - - /* Use 'ignore' if the method wasn't previously set */ - if (!method) { - method = maybe_ipv6_disabled ? NM_SETTING_IP6_CONFIG_METHOD_DISABLED - : NM_SETTING_IP6_CONFIG_METHOD_IGNORE; - } - - g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, method, NULL); - - /* Routes */ - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, &route) { - NMIPRoute *s_route; - - /* Ignore link-local route. */ - if (IN6_IS_ADDR_LINKLOCAL(&route->network)) - continue; - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route)) - continue; - - /* Ignore routes provided by external sources */ - if (route->rt_source - != nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER)) - continue; - - s_route = nm_ip_route_new_binary(AF_INET6, - &route->network, - route->plen, - &route->gateway, - route->metric, - NULL); - nm_setting_ip_config_add_route(s_ip6, s_route); - nm_ip_route_unref(s_route); - } - - /* DNS */ - for (i = 0; i < nnameservers; i++) { - const struct in6_addr *nameserver = nm_ip6_config_get_nameserver(self, i); - - nm_setting_ip_config_add_dns(s_ip6, _nm_utils_inet6_ntop(nameserver, sbuf)); - } - for (i = 0; i < nsearches; i++) { - const char *search = nm_ip6_config_get_search(self, i); - - nm_setting_ip_config_add_dns_search(s_ip6, search); - } - for (i = 0; i < noptions; i++) { - const char *option = nm_ip6_config_get_dns_option(self, i); - - nm_setting_ip_config_add_dns_option(s_ip6, option); - } - - g_object_set(s_ip6, - NM_SETTING_IP_CONFIG_DNS_PRIORITY, - nm_ip6_config_get_dns_priority(self), - NULL); - - return NM_SETTING(s_ip6); -} - -/*****************************************************************************/ - -void -nm_ip6_config_merge(NMIP6Config * dst, - const NMIP6Config * src, - NMIPConfigMergeFlags merge_flags, - guint32 default_route_metric_penalty) -{ - guint32 i; - NMDedupMultiIter ipconf_iter; - const NMPlatformIP6Address *address = NULL; - const NMIP6ConfigPrivate * src_priv; - - g_return_if_fail(src != NULL); - g_return_if_fail(dst != NULL); - - src_priv = NM_IP6_CONFIG_GET_PRIVATE(src); - - g_object_freeze_notify(G_OBJECT(dst)); - - /* addresses */ - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, src, &address) { - if (NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_EXTERNAL) && !address->external) { - NMPlatformIP6Address a; - - a = *address; - a.external = TRUE; - _add_address(dst, NULL, &a); - } else - _add_address(dst, NMP_OBJECT_UP_CAST(address), NULL); - } - - /* nameservers */ - if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { - for (i = 0; i < nm_ip6_config_get_num_nameservers(src); i++) - nm_ip6_config_add_nameserver(dst, nm_ip6_config_get_nameserver(src, i)); - } - - /* routes */ - if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) { - const NMPlatformIP6Route *r_src; - - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, src, &r_src) { - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r_src)) { - if (NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES) - && !NM_FLAGS_HAS(src_priv->config_flags, - NM_IP_CONFIG_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES)) - continue; - if (default_route_metric_penalty) { - NMPlatformIP6Route r = *r_src; - - r.metric = - nm_utils_ip_route_metric_penalize(r.metric, default_route_metric_penalty); - _add_route(dst, NULL, &r, NULL); - continue; - } - } - _add_route(dst, ipconf_iter.current->obj, NULL, NULL); - } - } - - /* domains */ - if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { - for (i = 0; i < nm_ip6_config_get_num_domains(src); i++) - nm_ip6_config_add_domain(dst, nm_ip6_config_get_domain(src, i)); - } - - /* dns searches */ - if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { - for (i = 0; i < nm_ip6_config_get_num_searches(src); i++) - nm_ip6_config_add_search(dst, nm_ip6_config_get_search(src, i)); - } - - /* dns options */ - if (!NM_FLAGS_HAS(merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) { - for (i = 0; i < nm_ip6_config_get_num_dns_options(src); i++) - nm_ip6_config_add_dns_option(dst, nm_ip6_config_get_dns_option(src, i)); - } - - /* DNS priority */ - if (nm_ip6_config_get_dns_priority(src)) - nm_ip6_config_set_dns_priority(dst, nm_ip6_config_get_dns_priority(src)); - - g_object_thaw_notify(G_OBJECT(dst)); -} - -/*****************************************************************************/ - -static int -_nameservers_get_index(const NMIP6Config *self, const struct in6_addr *ns) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - guint i; - - for (i = 0; i < priv->nameservers->len; i++) { - const struct in6_addr *n = &g_array_index(priv->nameservers, struct in6_addr, i); - - if (IN6_ARE_ADDR_EQUAL(ns, n)) - return (int) i; - } - return -1; -} - -static int -_domains_get_index(const NMIP6Config *self, const char *domain) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - guint i; - - for (i = 0; i < priv->domains->len; i++) { - const char *d = g_ptr_array_index(priv->domains, i); - - if (g_strcmp0(domain, d) == 0) - return (int) i; - } - return -1; -} - -static int -_searches_get_index(const NMIP6Config *self, const char *search) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - guint i; - - for (i = 0; i < priv->searches->len; i++) { - const char *s = g_ptr_array_index(priv->searches, i); - - if (g_strcmp0(search, s) == 0) - return (int) i; - } - return -1; -} - -static int -_dns_options_get_index(const NMIP6Config *self, const char *option) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - guint i; - - for (i = 0; i < priv->dns_options->len; i++) { - const char *s = g_ptr_array_index(priv->dns_options, i); - - if (g_strcmp0(option, s) == 0) - return (int) i; - } - return -1; -} - -/*****************************************************************************/ - -/** - * nm_ip6_config_subtract: - * @dst: config from which to remove everything in @src - * @src: config to remove from @dst - * @default_route_metric_penalty: pretend that on source we applied - * a route penalty on the default-route. It means, for default routes - * we don't remove routes that match exactly, but those with a lower - * metric (with the penalty removed). -* - * Removes everything in @src from @dst. - */ -void -nm_ip6_config_subtract(NMIP6Config * dst, - const NMIP6Config *src, - guint32 default_route_metric_penalty) -{ - NMIP6ConfigPrivate * dst_priv; - guint i; - int idx; - const NMPlatformIP6Address *a; - const NMPlatformIP6Route * r; - NMDedupMultiIter ipconf_iter; - gboolean changed; - gboolean changed_default_route; - - g_return_if_fail(src != NULL); - g_return_if_fail(dst != NULL); - - dst_priv = NM_IP6_CONFIG_GET_PRIVATE(dst); - - g_object_freeze_notify(G_OBJECT(dst)); - - /* addresses */ - changed = FALSE; - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, src, &a) { - if (nm_dedup_multi_index_remove_obj(dst_priv->multi_idx, - &dst_priv->idx_ip6_addresses, - NMP_OBJECT_UP_CAST(a), - NULL)) - changed = TRUE; - } - if (changed) - _notify_addresses(dst); - - /* nameservers */ - for (i = 0; i < nm_ip6_config_get_num_nameservers(src); i++) { - idx = _nameservers_get_index(dst, nm_ip6_config_get_nameserver(src, i)); - if (idx >= 0) - nm_ip6_config_del_nameserver(dst, idx); - } - - /* routes */ - changed = FALSE; - changed_default_route = FALSE; - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, src, &r) { - const NMPObject * o_src = NMP_OBJECT_UP_CAST(r); - NMPObject o_lookup_copy; - const NMPObject * o_lookup; - nm_auto_nmpobj const NMPObject *obj_old = NULL; - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r) && default_route_metric_penalty) { - NMPlatformIP6Route *rr; - - /* the default route was penalized when merging it to the combined ip-config. - * When subtracting the routes, we must re-do that process when comparing - * the routes. */ - o_lookup = nmp_object_stackinit_obj(&o_lookup_copy, o_src); - rr = NMP_OBJECT_CAST_IP6_ROUTE(&o_lookup_copy); - rr->metric = - nm_utils_ip_route_metric_penalize(rr->metric, default_route_metric_penalty); - } else - o_lookup = o_src; - - if (nm_dedup_multi_index_remove_obj(dst_priv->multi_idx, - &dst_priv->idx_ip6_routes, - o_lookup, - (gconstpointer *) &obj_old)) { - if (dst_priv->best_default_route == obj_old) { - nm_clear_nmp_object(&dst_priv->best_default_route); - changed_default_route = TRUE; - } - changed = TRUE; - } - } - if (changed_default_route) { - nmp_object_ref_set(&dst_priv->best_default_route, - _nm_ip6_config_best_default_route_find(dst)); - _notify(dst, PROP_GATEWAY); - } - if (changed) - _notify_routes(dst); - - /* domains */ - for (i = 0; i < nm_ip6_config_get_num_domains(src); i++) { - idx = _domains_get_index(dst, nm_ip6_config_get_domain(src, i)); - if (idx >= 0) - nm_ip6_config_del_domain(dst, idx); - } - - /* dns searches */ - for (i = 0; i < nm_ip6_config_get_num_searches(src); i++) { - idx = _searches_get_index(dst, nm_ip6_config_get_search(src, i)); - if (idx >= 0) - nm_ip6_config_del_search(dst, idx); - } - - /* dns options */ - for (i = 0; i < nm_ip6_config_get_num_dns_options(src); i++) { - idx = _dns_options_get_index(dst, nm_ip6_config_get_dns_option(src, i)); - if (idx >= 0) - nm_ip6_config_del_dns_option(dst, idx); - } - - /* DNS priority */ - if (nm_ip6_config_get_dns_priority(src) == nm_ip6_config_get_dns_priority(dst)) - nm_ip6_config_set_dns_priority(dst, 0); - - g_object_thaw_notify(G_OBJECT(dst)); -} - -static gboolean -_nm_ip6_config_intersect_helper(NMIP6Config * dst, - const NMIP6Config *src, - gboolean intersect_addresses, - gboolean intersect_routes, - guint32 default_route_metric_penalty, - gboolean update_dst) -{ - NMIP6ConfigPrivate * dst_priv; - const NMIP6ConfigPrivate * src_priv; - NMDedupMultiIter ipconf_iter; - const NMPlatformIP6Address *a; - const NMPlatformIP6Route * r; - gboolean changed, result = FALSE; - const NMPObject * new_best_default_route; - - g_return_val_if_fail(src, FALSE); - g_return_val_if_fail(dst, FALSE); - - dst_priv = NM_IP6_CONFIG_GET_PRIVATE(dst); - src_priv = NM_IP6_CONFIG_GET_PRIVATE(src); - - if (update_dst) - g_object_freeze_notify(G_OBJECT(dst)); - - /* addresses */ - if (intersect_addresses) { - changed = FALSE; - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, dst, &a) { - if (nm_dedup_multi_index_lookup_obj(src_priv->multi_idx, - &src_priv->idx_ip6_addresses, - NMP_OBJECT_UP_CAST(a))) - continue; - - if (!update_dst) - return TRUE; - - if (nm_dedup_multi_index_remove_entry(dst_priv->multi_idx, ipconf_iter.current) != 1) - nm_assert_not_reached(); - changed = TRUE; - } - if (changed) { - _notify_addresses(dst); - result = TRUE; - } - } - - /* ignore nameservers */ - - /* routes */ - if (!intersect_routes) - goto skip_routes; - - changed = FALSE; - new_best_default_route = NULL; - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, dst, &r) { - const NMPObject *o_dst = NMP_OBJECT_UP_CAST(r); - const NMPObject *o_lookup; - NMPObject o_lookup_copy; - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r) && default_route_metric_penalty) { - NMPlatformIP6Route *rr; - - /* the default route was penalized when merging it to the combined ip-config. - * When intersecting the routes, we must re-do that process when comparing - * the routes. */ - o_lookup = nmp_object_stackinit_obj(&o_lookup_copy, o_dst); - rr = NMP_OBJECT_CAST_IP6_ROUTE(&o_lookup_copy); - rr->metric = - nm_utils_ip_route_metric_penalize(rr->metric, default_route_metric_penalty); - } else - o_lookup = o_dst; - - if (nm_dedup_multi_index_lookup_obj(src_priv->multi_idx, - &src_priv->idx_ip6_routes, - o_lookup)) { - new_best_default_route = - _nm_ip_config_best_default_route_find_better(new_best_default_route, o_dst); - continue; - } - - if (!update_dst) - return TRUE; - - if (nm_dedup_multi_index_remove_entry(dst_priv->multi_idx, ipconf_iter.current) != 1) - nm_assert_not_reached(); - changed = TRUE; - } - if (nmp_object_ref_set(&dst_priv->best_default_route, new_best_default_route)) { - nm_assert(changed); - _notify(dst, PROP_GATEWAY); - } - if (changed) { - _notify_routes(dst); - result = TRUE; - } - -skip_routes: - /* ignore domains */ - /* ignore dns searches */ - /* ignore dns options */ - - if (update_dst) - g_object_thaw_notify(G_OBJECT(dst)); - - return result; -} - -/** - * nm_ip6_config_intersect: - * @dst: a configuration to be updated - * @src: another configuration - * @intersect_addresses: whether addresses should be intersected - * @intersect_routes: whether routes should be intersected - * @default_route_metric_penalty: the default route metric penalty - * - * Computes the intersection between @src and @dst and updates @dst in place - * with the result. - */ -void -nm_ip6_config_intersect(NMIP6Config * dst, - const NMIP6Config *src, - gboolean intersect_addresses, - gboolean intersect_routes, - guint32 default_route_metric_penalty) -{ - _nm_ip6_config_intersect_helper(dst, - src, - intersect_addresses, - intersect_routes, - default_route_metric_penalty, - TRUE); -} - -/** - * nm_ip6_config_intersect_alloc: - * @a: a configuration - * @b: another configuration - * @intersect_addresses: whether addresses should be intersected - * @intersect_routes: whether routes should be intersected - * @default_route_metric_penalty: the default route metric penalty - * - * Computes the intersection between @a and @b and returns the result in a newly - * allocated configuration. As a special case, if @a and @b are identical (with - * respect to the only properties considered - addresses and routes) the - * functions returns NULL so that one of existing configuration can be reused - * without allocation. - * - * Returns: the intersection between @a and @b, or %NULL if the result is equal - * to @a and @b. - */ -NMIP6Config * -nm_ip6_config_intersect_alloc(const NMIP6Config *a, - const NMIP6Config *b, - gboolean intersect_addresses, - gboolean intersect_routes, - guint32 default_route_metric_penalty) -{ - NMIP6Config *a_copy; - - if (_nm_ip6_config_intersect_helper((NMIP6Config *) a, - b, - intersect_addresses, - intersect_routes, - default_route_metric_penalty, - FALSE)) { - a_copy = nm_ip6_config_clone(a); - _nm_ip6_config_intersect_helper(a_copy, - b, - intersect_addresses, - intersect_routes, - default_route_metric_penalty, - TRUE); - return a_copy; - } else - return NULL; -} - -/** - * nm_ip6_config_replace: - * @dst: config which will be replaced with everything in @src - * @src: config to copy over to @dst - * @relevant_changes: return whether there are changes to the - * destination object that are relevant. This is equal to - * nm_ip6_config_equal() showing any difference. - * - * Replaces everything in @dst with @src so that the two configurations - * contain the same content -- with the exception of the dbus path. - * - * Returns: whether the @dst instance changed in any way (including minor changes, - * that are not signaled by the output parameter @relevant_changes). - */ -gboolean -nm_ip6_config_replace(NMIP6Config *dst, const NMIP6Config *src, gboolean *relevant_changes) -{ -#if NM_MORE_ASSERTS - gboolean config_equal; -#endif - gboolean has_minor_changes = FALSE, has_relevant_changes = FALSE, are_equal; - guint i, num; - NMIP6ConfigPrivate * dst_priv; - const NMIP6ConfigPrivate * src_priv; - NMDedupMultiIter ipconf_iter_src, ipconf_iter_dst; - const NMDedupMultiHeadEntry *head_entry_src; - const NMPObject * new_best_default_route; - - g_return_val_if_fail(NM_IS_IP6_CONFIG(src), FALSE); - g_return_val_if_fail(NM_IS_IP6_CONFIG(dst), FALSE); - g_return_val_if_fail(src != dst, FALSE); - -#if NM_MORE_ASSERTS - config_equal = nm_ip6_config_equal(dst, src); -#endif - - dst_priv = NM_IP6_CONFIG_GET_PRIVATE(dst); - src_priv = NM_IP6_CONFIG_GET_PRIVATE(src); - - g_return_val_if_fail(src_priv->ifindex > 0, FALSE); - - g_object_freeze_notify(G_OBJECT(dst)); - - /* ifindex */ - if (src_priv->ifindex != dst_priv->ifindex) { - dst_priv->ifindex = src_priv->ifindex; - has_minor_changes = TRUE; - } - - /* addresses */ - head_entry_src = nm_ip6_config_lookup_addresses(src); - nm_dedup_multi_iter_init(&ipconf_iter_src, head_entry_src); - nm_ip_config_iter_ip6_address_init(&ipconf_iter_dst, dst); - are_equal = TRUE; - while (TRUE) { - gboolean has; - const NMPlatformIP6Address *r_src = NULL; - const NMPlatformIP6Address *r_dst = NULL; - - has = nm_platform_dedup_multi_iter_next_ip6_address(&ipconf_iter_src, &r_src); - if (has != nm_platform_dedup_multi_iter_next_ip6_address(&ipconf_iter_dst, &r_dst)) { - are_equal = FALSE; - has_relevant_changes = TRUE; - break; - } - if (!has) - break; - - if (nm_platform_ip6_address_cmp(r_src, r_dst) != 0) { - are_equal = FALSE; - if (!IN6_ARE_ADDR_EQUAL(&r_src->address, &r_dst->address) || r_src->plen != r_dst->plen - || !IN6_ARE_ADDR_EQUAL(nm_platform_ip6_address_get_peer(r_src), - nm_platform_ip6_address_get_peer(r_dst))) { - has_relevant_changes = TRUE; - break; - } - } - } - if (!are_equal) { - has_minor_changes = TRUE; - nm_dedup_multi_index_dirty_set_idx(dst_priv->multi_idx, &dst_priv->idx_ip6_addresses); - nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) { - _nm_ip_config_add_obj(dst_priv->multi_idx, - &dst_priv->idx_ip6_addresses_, - dst_priv->ifindex, - ipconf_iter_src.current->obj, - NULL, - FALSE, - TRUE, - NULL, - NULL); - } - nm_dedup_multi_index_dirty_remove_idx(dst_priv->multi_idx, - &dst_priv->idx_ip6_addresses, - FALSE); - _notify_addresses(dst); - } - - /* routes */ - head_entry_src = nm_ip6_config_lookup_routes(src); - nm_dedup_multi_iter_init(&ipconf_iter_src, head_entry_src); - nm_ip_config_iter_ip6_route_init(&ipconf_iter_dst, dst); - are_equal = TRUE; - while (TRUE) { - gboolean has; - const NMPlatformIP6Route *r_src = NULL; - const NMPlatformIP6Route *r_dst = NULL; - - has = nm_platform_dedup_multi_iter_next_ip6_route(&ipconf_iter_src, &r_src); - if (has != nm_platform_dedup_multi_iter_next_ip6_route(&ipconf_iter_dst, &r_dst)) { - are_equal = FALSE; - has_relevant_changes = TRUE; - break; - } - if (!has) - break; - - if (nm_platform_ip6_route_cmp_full(r_src, r_dst) != 0) { - are_equal = FALSE; - if (r_src->plen != r_dst->plen - || !nm_utils_ip6_address_same_prefix(&r_src->network, &r_dst->network, r_src->plen) - || r_src->metric != r_dst->metric - || !IN6_ARE_ADDR_EQUAL(&r_src->gateway, &r_dst->gateway)) { - has_relevant_changes = TRUE; - break; - } - } - } - if (!are_equal) { - has_minor_changes = TRUE; - new_best_default_route = NULL; - nm_dedup_multi_index_dirty_set_idx(dst_priv->multi_idx, &dst_priv->idx_ip6_routes); - nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) { - const NMPObject *o = ipconf_iter_src.current->obj; - const NMPObject *obj_new; - - _nm_ip_config_add_obj(dst_priv->multi_idx, - &dst_priv->idx_ip6_routes_, - dst_priv->ifindex, - o, - NULL, - FALSE, - TRUE, - NULL, - &obj_new); - new_best_default_route = - _nm_ip_config_best_default_route_find_better(new_best_default_route, obj_new); - } - nm_dedup_multi_index_dirty_remove_idx(dst_priv->multi_idx, - &dst_priv->idx_ip6_routes, - FALSE); - if (nmp_object_ref_set(&dst_priv->best_default_route, new_best_default_route)) - _notify(dst, PROP_GATEWAY); - _notify_routes(dst); - } - - /* nameservers */ - num = nm_ip6_config_get_num_nameservers(src); - are_equal = num == nm_ip6_config_get_num_nameservers(dst); - if (are_equal) { - for (i = 0; i < num; i++) { - if (!IN6_ARE_ADDR_EQUAL(nm_ip6_config_get_nameserver(src, i), - nm_ip6_config_get_nameserver(dst, i))) { - are_equal = FALSE; - break; - } - } - } - if (!are_equal) { - nm_ip6_config_reset_nameservers(dst); - for (i = 0; i < num; i++) - nm_ip6_config_add_nameserver(dst, nm_ip6_config_get_nameserver(src, i)); - has_relevant_changes = TRUE; - } - - /* domains */ - num = nm_ip6_config_get_num_domains(src); - are_equal = num == nm_ip6_config_get_num_domains(dst); - if (are_equal) { - for (i = 0; i < num; i++) { - if (g_strcmp0(nm_ip6_config_get_domain(src, i), nm_ip6_config_get_domain(dst, i))) { - are_equal = FALSE; - break; - } - } - } - if (!are_equal) { - nm_ip6_config_reset_domains(dst); - for (i = 0; i < num; i++) - nm_ip6_config_add_domain(dst, nm_ip6_config_get_domain(src, i)); - has_relevant_changes = TRUE; - } - - /* dns searches */ - num = nm_ip6_config_get_num_searches(src); - are_equal = num == nm_ip6_config_get_num_searches(dst); - if (are_equal) { - for (i = 0; i < num; i++) { - if (g_strcmp0(nm_ip6_config_get_search(src, i), nm_ip6_config_get_search(dst, i))) { - are_equal = FALSE; - break; - } - } - } - if (!are_equal) { - nm_ip6_config_reset_searches(dst); - for (i = 0; i < num; i++) - nm_ip6_config_add_search(dst, nm_ip6_config_get_search(src, i)); - has_relevant_changes = TRUE; - } - - /* dns options */ - num = nm_ip6_config_get_num_dns_options(src); - are_equal = num == nm_ip6_config_get_num_dns_options(dst); - if (are_equal) { - for (i = 0; i < num; i++) { - if (g_strcmp0(nm_ip6_config_get_dns_option(src, i), - nm_ip6_config_get_dns_option(dst, i))) { - are_equal = FALSE; - break; - } - } - } - if (!are_equal) { - nm_ip6_config_reset_dns_options(dst); - for (i = 0; i < num; i++) - nm_ip6_config_add_dns_option(dst, nm_ip6_config_get_dns_option(src, i)); - has_relevant_changes = TRUE; - } - - /* DNS priority */ - if (src_priv->dns_priority != dst_priv->dns_priority) { - nm_ip6_config_set_dns_priority(dst, src_priv->dns_priority); - has_minor_changes = TRUE; - } - - if (src_priv->privacy != dst_priv->privacy) { - nm_ip6_config_set_privacy(dst, src_priv->privacy); - has_minor_changes = TRUE; - } - -#if NM_MORE_ASSERTS - /* config_equal does not compare *all* the fields, therefore, we might have has_minor_changes - * regardless of config_equal. But config_equal must correspond to has_relevant_changes. */ - nm_assert(config_equal == !has_relevant_changes); -#endif - - g_object_thaw_notify(G_OBJECT(dst)); - - if (relevant_changes) - *relevant_changes = has_relevant_changes; - - return has_relevant_changes || has_minor_changes; -} - -/*****************************************************************************/ - -void -nm_ip6_config_reset_addresses_ndisc(NMIP6Config * self, - const NMNDiscAddress *addresses, - guint addresses_n, - guint8 plen, - guint32 ifa_flags) -{ - NMIP6ConfigPrivate *priv; - guint i; - gboolean changed = FALSE; - gint32 base_time_sec; - - g_return_if_fail(NM_IS_IP6_CONFIG(self)); - - priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - g_return_if_fail(priv->ifindex > 0); - - /* the base-timestamp doesn't matter it's only an anchor for the - * expiry. However, try to re-use the same base-time for a while - * by rounding it to 10000 seconds. - * - * That is because we deduplicate and NMPlatformIP6Address instances - * so using the same timestamps is preferable. */ - base_time_sec = nm_utils_get_monotonic_timestamp_sec(); - base_time_sec = (base_time_sec / 10000) * 10000; - base_time_sec = NM_MAX(1, base_time_sec); - - nm_dedup_multi_index_dirty_set_idx(priv->multi_idx, &priv->idx_ip6_addresses); - - for (i = 0; i < addresses_n; i++) { - const NMNDiscAddress *ndisc_addr = &addresses[i]; - NMPObject obj; - NMPlatformIP6Address *a; - - nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP6_ADDRESS, NULL); - a = NMP_OBJECT_CAST_IP6_ADDRESS(&obj); - a->ifindex = priv->ifindex; - a->address = ndisc_addr->address; - a->plen = plen; - a->timestamp = base_time_sec, - a->lifetime = _nm_ndisc_lifetime_from_expiry(((gint64) base_time_sec) * 1000, - ndisc_addr->expiry_msec, - TRUE), - a->preferred = _nm_ndisc_lifetime_from_expiry(((gint64) base_time_sec) * 1000, - ndisc_addr->expiry_preferred_msec, - TRUE), - a->addr_source = NM_IP_CONFIG_SOURCE_NDISC; - a->n_ifa_flags = ifa_flags; - - if (_nm_ip_config_add_obj(priv->multi_idx, - &priv->idx_ip6_addresses_, - priv->ifindex, - &obj, - NULL, - FALSE, - TRUE, - NULL, - NULL)) - changed = TRUE; - } - - if (nm_dedup_multi_index_dirty_remove_idx(priv->multi_idx, &priv->idx_ip6_addresses, FALSE) > 0) - changed = TRUE; - - if (changed) - _notify_addresses(self); -} - -void -nm_ip6_config_reset_addresses(NMIP6Config *self) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - if (nm_dedup_multi_index_remove_idx(priv->multi_idx, &priv->idx_ip6_addresses) > 0) - _notify_addresses(self); -} - -static void -_add_address(NMIP6Config *self, const NMPObject *obj_new, const NMPlatformIP6Address *new) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - if (_nm_ip_config_add_obj(priv->multi_idx, - &priv->idx_ip6_addresses_, - priv->ifindex, - obj_new, - (const NMPlatformObject *) new, - TRUE, - FALSE, - NULL, - NULL)) - _notify_addresses(self); -} - -/** - * nm_ip6_config_add_address: - * @self: the #NMIP6Config - * @new: the new address to add to @self - * - * Adds the new address to @self. If an address with the same basic properties - * (address, prefix) already exists in @self, it is overwritten with the - * lifetime and preferred of @new. The source is also overwritten by the source - * from @new if that source is higher priority. - */ -void -nm_ip6_config_add_address(NMIP6Config *self, const NMPlatformIP6Address *new) -{ - g_return_if_fail(self); - g_return_if_fail(new); - g_return_if_fail(new->plen <= 128); - g_return_if_fail(NM_IP6_CONFIG_GET_PRIVATE(self)->ifindex > 0); - - _add_address(self, NULL, new); -} - -void -_nmtst_ip6_config_del_address(NMIP6Config *self, guint i) -{ - const NMPlatformIP6Address *a; - - a = _nmtst_ip6_config_get_address(self, i); - if (!nm_ip6_config_nmpobj_remove(self, NMP_OBJECT_UP_CAST(a))) - g_assert_not_reached(); -} - -guint -nm_ip6_config_get_num_addresses(const NMIP6Config *self) -{ - const NMDedupMultiHeadEntry *head_entry; - - head_entry = nm_ip6_config_lookup_addresses(self); - return head_entry ? head_entry->len : 0; -} - -const NMPlatformIP6Address * -nm_ip6_config_get_first_address(const NMIP6Config *self) -{ - NMDedupMultiIter iter; - const NMPlatformIP6Address *a = NULL; - - nm_ip_config_iter_ip6_address_for_each (&iter, self, &a) - return a; - return NULL; -} - -const NMPlatformIP6Address * -_nmtst_ip6_config_get_address(const NMIP6Config *self, guint i) -{ - NMDedupMultiIter iter; - const NMPlatformIP6Address *a = NULL; - guint j; - - j = 0; - nm_ip_config_iter_ip6_address_for_each (&iter, self, &a) { - if (i == j) - return a; - j++; - } - g_return_val_if_reached(NULL); -} - -const NMPlatformIP6Address * -nm_ip6_config_lookup_address(const NMIP6Config *self, const struct in6_addr *addr) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - NMPObject obj_stack; - const NMDedupMultiEntry * entry; - - nmp_object_stackinit_id_ip6_address(&obj_stack, priv->ifindex, addr); - entry = nm_dedup_multi_index_lookup_obj(priv->multi_idx, &priv->idx_ip6_addresses, &obj_stack); - return entry ? NMP_OBJECT_CAST_IP6_ADDRESS(entry->obj) : NULL; -} - -/** - * nm_ip6_config_has_dad_pending_addresses - * @self: configuration containing the addresses to check - * @candidates: configuration with the list of addresses we are - * interested in - * - * Check whether there are addresses with DAD pending in @self, that - * are also contained in @candidates. - * - * Returns: %TRUE if at least one matching address was found, %FALSE - * otherwise - */ -gboolean -nm_ip6_config_has_any_dad_pending(const NMIP6Config *self, const NMIP6Config *candidates) -{ - NMDedupMultiIter ipconf_iter; - const NMPlatformIP6Address *addr, *addr_c; - - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, self, &addr) { - if (NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_TENTATIVE) - && !NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_DADFAILED) - && !NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_OPTIMISTIC)) { - addr_c = nm_ip6_config_lookup_address(candidates, &addr->address); - if (addr_c) { - if (addr->plen == addr_c->plen) - return TRUE; - } - } - } - - return FALSE; -} - -/*****************************************************************************/ - -static const NMDedupMultiEntry * -_lookup_route(const NMIP6Config *self, const NMPObject *needle, NMPlatformIPRouteCmpType cmp_type) -{ - const NMIP6ConfigPrivate *priv; - - nm_assert(NM_IS_IP6_CONFIG(self)); - nm_assert(NMP_OBJECT_GET_TYPE(needle) == NMP_OBJECT_TYPE_IP6_ROUTE); - - priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - return _nm_ip_config_lookup_ip_route(priv->multi_idx, &priv->idx_ip6_routes_, needle, cmp_type); -} - -void -nm_ip6_config_reset_routes_ndisc(NMIP6Config * self, - const NMNDiscGateway *gateways, - guint gateways_n, - const NMNDiscRoute * routes, - guint routes_n, - guint32 route_table, - guint32 route_metric) -{ - NMIP6ConfigPrivate *priv; - guint i; - gboolean changed = FALSE; - const NMPObject * new_best_default_route; - - g_return_if_fail(NM_IS_IP6_CONFIG(self)); - - priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - g_return_if_fail(priv->ifindex > 0); - - nm_dedup_multi_index_dirty_set_idx(priv->multi_idx, &priv->idx_ip6_routes); - - new_best_default_route = NULL; - for (i = 0; i < routes_n; i++) { - const NMNDiscRoute *ndisc_route = &routes[i]; - NMPObject obj; - const NMPObject * obj_new; - NMPlatformIP6Route *r; - - nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP6_ROUTE, NULL); - r = NMP_OBJECT_CAST_IP6_ROUTE(&obj); - r->ifindex = priv->ifindex; - r->network = ndisc_route->network; - r->plen = ndisc_route->plen; - r->gateway = ndisc_route->gateway; - r->rt_source = NM_IP_CONFIG_SOURCE_NDISC; - r->table_coerced = nm_platform_route_table_coerce(route_table); - r->metric = route_metric; - r->rt_pref = ndisc_route->preference; - nm_assert((NMIcmpv6RouterPref) r->rt_pref == ndisc_route->preference); - - if (_nm_ip_config_add_obj(priv->multi_idx, - &priv->idx_ip6_routes_, - priv->ifindex, - &obj, - NULL, - FALSE, - TRUE, - NULL, - &obj_new)) - changed = TRUE; - new_best_default_route = - _nm_ip_config_best_default_route_find_better(new_best_default_route, obj_new); - } - - if (gateways_n) { - const NMPObject * obj_new; - NMPlatformIP6Route r = { - .rt_source = NM_IP_CONFIG_SOURCE_NDISC, - .ifindex = priv->ifindex, - .table_coerced = nm_platform_route_table_coerce(route_table), - .metric = route_metric, - }; - - for (i = 0; i < gateways_n; i++) { - r.gateway = gateways[i].address; - r.rt_pref = gateways[i].preference; - nm_assert((NMIcmpv6RouterPref) r.rt_pref == gateways[i].preference); - if (_nm_ip_config_add_obj(priv->multi_idx, - &priv->idx_ip6_routes_, - priv->ifindex, - NULL, - (const NMPlatformObject *) &r, - FALSE, - TRUE, - NULL, - &obj_new)) - changed = TRUE; - new_best_default_route = - _nm_ip_config_best_default_route_find_better(new_best_default_route, obj_new); - } - } - - if (nm_dedup_multi_index_dirty_remove_idx(priv->multi_idx, &priv->idx_ip6_routes, FALSE) > 0) - changed = TRUE; - - if (nmp_object_ref_set(&priv->best_default_route, new_best_default_route)) { - changed = TRUE; - _notify(self, PROP_GATEWAY); - } - - if (changed) - _notify_routes(self); -} - -void -nm_ip6_config_reset_routes(NMIP6Config *self) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - if (nm_dedup_multi_index_remove_idx(priv->multi_idx, &priv->idx_ip6_routes) > 0) { - if (nm_clear_nmp_object(&priv->best_default_route)) - _notify(self, PROP_GATEWAY); - _notify_routes(self); - } -} - -static void -_add_route(NMIP6Config * self, - const NMPObject *obj_new, - const NMPlatformIP6Route *new, - const NMPObject **out_obj_new) -{ - NMIP6ConfigPrivate * priv = NM_IP6_CONFIG_GET_PRIVATE(self); - nm_auto_nmpobj const NMPObject *obj_old = NULL; - const NMPObject * obj_new_2; - - nm_assert((!new) != (!obj_new)); - nm_assert(!new || _route_valid(new)); - nm_assert(!obj_new || _route_valid(NMP_OBJECT_CAST_IP6_ROUTE(obj_new))); - - if (_nm_ip_config_add_obj(priv->multi_idx, - &priv->idx_ip6_routes_, - priv->ifindex, - obj_new, - (const NMPlatformObject *) new, - TRUE, - FALSE, - &obj_old, - &obj_new_2)) { - gboolean changed_default_route = FALSE; - - if (priv->best_default_route == obj_old && obj_old != obj_new_2) { - changed_default_route = TRUE; - nm_clear_nmp_object(&priv->best_default_route); - } - NM_SET_OUT(out_obj_new, nmp_object_ref(obj_new_2)); - if (_nm_ip_config_best_default_route_merge(&priv->best_default_route, obj_new_2)) - changed_default_route = TRUE; - - if (changed_default_route) - _notify(self, PROP_GATEWAY); - _notify_routes(self); - } else - NM_SET_OUT(out_obj_new, nmp_object_ref(obj_new_2)); -} - -/** - * nm_ip6_config_add_route: - * @self: the #NMIP6Config - * @new: the new route to add to @self - * @out_obj_new: (allow-none) (out): the added route object. Must be unrefed - * by caller. - * - * Adds the new route to @self. If a route with the same basic properties - * (network, prefix) already exists in @self, it is overwritten including the - * gateway and metric of @new. The source is also overwritten by the source - * from @new if that source is higher priority. - */ -void -nm_ip6_config_add_route(NMIP6Config *self, - const NMPlatformIP6Route *new, - const NMPObject **out_obj_new) -{ - g_return_if_fail(self); - g_return_if_fail(new); - g_return_if_fail(new->plen <= 128); - g_return_if_fail(NM_IP6_CONFIG_GET_PRIVATE(self)->ifindex > 0); - - _add_route(self, NULL, new, out_obj_new); -} - -void -_nmtst_ip6_config_del_route(NMIP6Config *self, guint i) -{ - const NMPlatformIP6Route *r; - - r = _nmtst_ip6_config_get_route(self, i); - if (!nm_ip6_config_nmpobj_remove(self, NMP_OBJECT_UP_CAST(r))) - g_assert_not_reached(); -} - -guint -nm_ip6_config_get_num_routes(const NMIP6Config *self) -{ - const NMDedupMultiHeadEntry *head_entry; - - head_entry = nm_ip6_config_lookup_routes(self); - nm_assert(!head_entry || head_entry->len == c_list_length(&head_entry->lst_entries_head)); - return head_entry ? head_entry->len : 0; -} - -const NMPlatformIP6Route * -_nmtst_ip6_config_get_route(const NMIP6Config *self, guint i) -{ - NMDedupMultiIter iter; - const NMPlatformIP6Route *r = NULL; - guint j; - - j = 0; - nm_ip_config_iter_ip6_route_for_each (&iter, self, &r) { - if (i == j) - return r; - j++; - } - g_return_val_if_reached(NULL); -} - -const NMPlatformIP6Route * -nm_ip6_config_get_direct_route_for_host(const NMIP6Config * self, - const struct in6_addr *host, - guint32 route_table) -{ - const NMPlatformIP6Route *best_route = NULL; - const NMPlatformIP6Route *item; - NMDedupMultiIter ipconf_iter; - - g_return_val_if_fail(host && !IN6_IS_ADDR_UNSPECIFIED(host), NULL); - - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, &item) { - if (!IN6_IS_ADDR_UNSPECIFIED(&item->gateway)) - continue; - - if (best_route && best_route->plen > item->plen) - continue; - - if (nm_platform_route_table_uncoerce(item->table_coerced, TRUE) != route_table) - continue; - - if (!nm_utils_ip6_address_same_prefix(host, &item->network, item->plen)) - continue; - - if (best_route && best_route->metric <= item->metric) - continue; - - best_route = item; - } - return best_route; -} - -const NMPlatformIP6Address * -nm_ip6_config_get_subnet_for_host(const NMIP6Config *self, const struct in6_addr *host) -{ - NMDedupMultiIter iter; - const NMPlatformIP6Address *item; - const NMPlatformIP6Address *subnet = NULL; - struct in6_addr subnet2, host2; - - g_return_val_if_fail(host && !IN6_IS_ADDR_UNSPECIFIED(host), NULL); - - nm_ip_config_iter_ip6_address_for_each (&iter, self, &item) { - if (subnet && subnet->plen >= item->plen) - continue; - - nm_utils_ip6_address_clear_host_address(&host2, host, item->plen); - nm_utils_ip6_address_clear_host_address(&subnet2, &item->address, item->plen); - - if (IN6_ARE_ADDR_EQUAL(&subnet2, &host2)) - subnet = item; - } - - return subnet; -} - -/*****************************************************************************/ - -void -nm_ip6_config_reset_nameservers(NMIP6Config *self) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - if (priv->nameservers->len != 0) { - g_array_set_size(priv->nameservers, 0); - _notify(self, PROP_NAMESERVERS); - } -} - -void -nm_ip6_config_add_nameserver(NMIP6Config *self, const struct in6_addr *new) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - int i; - - g_return_if_fail(new != NULL); - - for (i = 0; i < priv->nameservers->len; i++) - if (IN6_ARE_ADDR_EQUAL(new, &g_array_index(priv->nameservers, struct in6_addr, i))) - return; - - g_array_append_val(priv->nameservers, *new); - _notify(self, PROP_NAMESERVERS); -} - -void -nm_ip6_config_del_nameserver(NMIP6Config *self, guint i) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - g_return_if_fail(i < priv->nameservers->len); - - g_array_remove_index(priv->nameservers, i); - _notify(self, PROP_NAMESERVERS); -} - -guint -nm_ip6_config_get_num_nameservers(const NMIP6Config *self) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - return priv->nameservers->len; -} - -const struct in6_addr * -nm_ip6_config_get_nameserver(const NMIP6Config *self, guint i) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - return &g_array_index(priv->nameservers, struct in6_addr, i); -} - -/*****************************************************************************/ - -void -nm_ip6_config_reset_domains(NMIP6Config *self) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - if (priv->domains->len != 0) { - g_ptr_array_set_size(priv->domains, 0); - _notify(self, PROP_DOMAINS); - } -} - -void -nm_ip6_config_add_domain(NMIP6Config *self, const char *domain) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - if (_nm_ip_config_check_and_add_domain(priv->domains, domain)) - _notify(self, PROP_DOMAINS); -} - -void -nm_ip6_config_del_domain(NMIP6Config *self, guint i) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - g_return_if_fail(i < priv->domains->len); - - g_ptr_array_remove_index(priv->domains, i); - _notify(self, PROP_DOMAINS); -} - -guint -nm_ip6_config_get_num_domains(const NMIP6Config *self) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - return priv->domains->len; -} - -const char * -nm_ip6_config_get_domain(const NMIP6Config *self, guint i) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - return g_ptr_array_index(priv->domains, i); -} - -/*****************************************************************************/ - -void -nm_ip6_config_reset_searches(NMIP6Config *self) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - if (priv->searches->len != 0) { - g_ptr_array_set_size(priv->searches, 0); - _notify(self, PROP_SEARCHES); - } -} - -void -nm_ip6_config_add_search(NMIP6Config *self, const char *search) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - if (_nm_ip_config_check_and_add_domain(priv->searches, search)) - _notify(self, PROP_SEARCHES); -} - -void -nm_ip6_config_del_search(NMIP6Config *self, guint i) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - g_return_if_fail(i < priv->searches->len); - - g_ptr_array_remove_index(priv->searches, i); - _notify(self, PROP_SEARCHES); -} - -guint -nm_ip6_config_get_num_searches(const NMIP6Config *self) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - return priv->searches->len; -} - -const char * -nm_ip6_config_get_search(const NMIP6Config *self, guint i) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - return g_ptr_array_index(priv->searches, i); -} - -/*****************************************************************************/ - -void -nm_ip6_config_reset_dns_options(NMIP6Config *self) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - if (priv->dns_options->len != 0) { - g_ptr_array_set_size(priv->dns_options, 0); - _notify(self, PROP_DNS_OPTIONS); - } -} - -void -nm_ip6_config_add_dns_option(NMIP6Config *self, const char *new) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - int i; - - g_return_if_fail(new != NULL); - g_return_if_fail(new[0] != '\0'); - - for (i = 0; i < priv->dns_options->len; i++) - if (!g_strcmp0(g_ptr_array_index(priv->dns_options, i), new)) - return; - - g_ptr_array_add(priv->dns_options, g_strdup(new)); - _notify(self, PROP_DNS_OPTIONS); -} - -void -nm_ip6_config_del_dns_option(NMIP6Config *self, guint i) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - g_return_if_fail(i < priv->dns_options->len); - - g_ptr_array_remove_index(priv->dns_options, i); - _notify(self, PROP_DNS_OPTIONS); -} - -guint -nm_ip6_config_get_num_dns_options(const NMIP6Config *self) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - return priv->dns_options->len; -} - -const char * -nm_ip6_config_get_dns_option(const NMIP6Config *self, guint i) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - return g_ptr_array_index(priv->dns_options, i); -} - -/*****************************************************************************/ - -NMIPConfigFlags -nm_ip6_config_get_config_flags(const NMIP6Config *self) -{ - return NM_IP6_CONFIG_GET_PRIVATE(self)->config_flags; -} - -void -nm_ip6_config_set_config_flags(NMIP6Config *self, NMIPConfigFlags flags, NMIPConfigFlags mask) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - if (mask == 0) { - /* for convenience, accept 0 mask to set any flags. */ - mask = flags; - } - - nm_assert(!NM_FLAGS_ANY(flags, ~mask)); - priv->config_flags = (flags & mask) | (priv->config_flags & ~mask); -} - -/*****************************************************************************/ - -void -nm_ip6_config_set_dns_priority(NMIP6Config *self, int priority) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - if (priority != priv->dns_priority) { - priv->dns_priority = priority; - _notify(self, PROP_DNS_PRIORITY); - } -} - -int -nm_ip6_config_get_dns_priority(const NMIP6Config *self) -{ - const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - return priv->dns_priority; -} - -/*****************************************************************************/ - -const NMPObject * -nm_ip6_config_nmpobj_lookup(const NMIP6Config *self, const NMPObject *needle) -{ - const NMIP6ConfigPrivate * priv; - const NMDedupMultiIdxType *idx_type; - - g_return_val_if_fail(NM_IS_IP6_CONFIG(self), NULL); - - priv = NM_IP6_CONFIG_GET_PRIVATE(self); - switch (NMP_OBJECT_GET_TYPE(needle)) { - case NMP_OBJECT_TYPE_IP6_ADDRESS: - idx_type = &priv->idx_ip6_addresses; - break; - case NMP_OBJECT_TYPE_IP6_ROUTE: - idx_type = &priv->idx_ip6_routes; - break; - default: - g_return_val_if_reached(NULL); - } - - return nm_dedup_multi_entry_get_obj( - nm_dedup_multi_index_lookup_obj(priv->multi_idx, idx_type, needle)); -} - -gboolean -nm_ip6_config_nmpobj_remove(NMIP6Config *self, const NMPObject *needle) -{ - NMIP6ConfigPrivate * priv; - NMDedupMultiIdxType *idx_type; - nm_auto_nmpobj const NMPObject *obj_old = NULL; - guint n; - - g_return_val_if_fail(NM_IS_IP6_CONFIG(self), FALSE); - - priv = NM_IP6_CONFIG_GET_PRIVATE(self); - switch (NMP_OBJECT_GET_TYPE(needle)) { - case NMP_OBJECT_TYPE_IP6_ADDRESS: - idx_type = &priv->idx_ip6_addresses; - break; - case NMP_OBJECT_TYPE_IP6_ROUTE: - idx_type = &priv->idx_ip6_routes; - break; - default: - g_return_val_if_reached(FALSE); - } - - n = nm_dedup_multi_index_remove_obj(priv->multi_idx, - idx_type, - needle, - (gconstpointer *) &obj_old); - if (n != 1) { - nm_assert(n == 0); - return FALSE; - } - - nm_assert(NMP_OBJECT_GET_TYPE(obj_old) == NMP_OBJECT_GET_TYPE(needle)); - - switch (NMP_OBJECT_GET_TYPE(obj_old)) { - case NMP_OBJECT_TYPE_IP6_ADDRESS: - _notify_addresses(self); - break; - case NMP_OBJECT_TYPE_IP6_ROUTE: - if (priv->best_default_route == obj_old) { - if (nmp_object_ref_set(&priv->best_default_route, - _nm_ip6_config_best_default_route_find(self))) - _notify(self, PROP_GATEWAY); - } - _notify_routes(self); - break; - default: - nm_assert_not_reached(); - } - return TRUE; -} - -/*****************************************************************************/ - -static void -hash_u32(GChecksum *sum, guint32 n) -{ - g_checksum_update(sum, (const guint8 *) &n, sizeof(n)); -} - -static void -hash_in6addr(GChecksum *sum, const struct in6_addr *a) -{ - if (a) - g_checksum_update(sum, (const guint8 *) a, sizeof(*a)); - else - g_checksum_update(sum, (const guint8 *) &in6addr_any, sizeof(in6addr_any)); -} - -void -nm_ip6_config_hash(const NMIP6Config *self, GChecksum *sum, gboolean dns_only) -{ - guint32 i; - const char * s; - NMDedupMultiIter ipconf_iter; - const NMPlatformIP6Address *address; - const NMPlatformIP6Route * route; - - g_return_if_fail(self); - g_return_if_fail(sum); - - if (dns_only == FALSE) { - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, self, &address) { - hash_in6addr(sum, &address->address); - hash_u32(sum, address->plen); - } - - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, &route) { - hash_in6addr(sum, &route->network); - hash_u32(sum, route->plen); - hash_in6addr(sum, &route->gateway); - hash_u32(sum, route->metric); - } - } - - for (i = 0; i < nm_ip6_config_get_num_nameservers(self); i++) - hash_in6addr(sum, nm_ip6_config_get_nameserver(self, i)); - - for (i = 0; i < nm_ip6_config_get_num_domains(self); i++) { - s = nm_ip6_config_get_domain(self, i); - g_checksum_update(sum, (const guint8 *) s, strlen(s)); - } - - for (i = 0; i < nm_ip6_config_get_num_searches(self); i++) { - s = nm_ip6_config_get_search(self, i); - g_checksum_update(sum, (const guint8 *) s, strlen(s)); - } - - for (i = 0; i < nm_ip6_config_get_num_dns_options(self); i++) { - s = nm_ip6_config_get_dns_option(self, i); - g_checksum_update(sum, (const guint8 *) s, strlen(s)); - } -} - -/** - * nm_ip6_config_equal: - * @a: first config to compare - * @b: second config to compare - * - * Compares two #NMIP6Configs for basic equality. This means that all - * attributes must exist in the same order in both configs (addresses, routes, - * domains, DNS servers, etc) but some attributes (address lifetimes, and address - * and route sources) are ignored. - * - * Returns: %TRUE if the configurations are basically equal to each other, - * %FALSE if not - */ -gboolean -nm_ip6_config_equal(const NMIP6Config *a, const NMIP6Config *b) -{ - nm_auto_free_checksum GChecksum *a_checksum = g_checksum_new(G_CHECKSUM_SHA1); - nm_auto_free_checksum GChecksum *b_checksum = g_checksum_new(G_CHECKSUM_SHA1); - guint8 a_data[NM_UTILS_CHECKSUM_LENGTH_SHA1]; - guint8 b_data[NM_UTILS_CHECKSUM_LENGTH_SHA1]; - - if (a) - nm_ip6_config_hash(a, a_checksum, FALSE); - if (b) - nm_ip6_config_hash(b, b_checksum, FALSE); - - nm_utils_checksum_get_digest(a_checksum, a_data); - nm_utils_checksum_get_digest(b_checksum, b_data); - return !memcmp(a_data, b_data, sizeof(a_data)); -} - -/*****************************************************************************/ - -static void -nameservers_to_gvalue(GArray *array, GValue *value) -{ - GVariantBuilder builder; - guint i = 0; - - g_variant_builder_init(&builder, G_VARIANT_TYPE("aay")); - - while (array && (i < array->len)) { - struct in6_addr *addr; - - addr = &g_array_index(array, struct in6_addr, i++); - g_variant_builder_add(&builder, "@ay", nm_g_variant_new_ay_in6addr(addr)); - } - - g_value_take_variant(value, g_variant_builder_end(&builder)); -} - -static void -get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - NMIP6Config * self = NM_IP6_CONFIG(object); - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - switch (prop_id) { - case PROP_IFINDEX: - g_value_set_int(value, priv->ifindex); - break; - case PROP_ADDRESS_DATA: - case PROP_ADDRESSES: - nm_assert(!!priv->address_data_variant == !!priv->addresses_variant); - - if (!priv->address_data_variant) { - nm_utils_ip_addresses_to_dbus(AF_INET6, - nm_ip6_config_lookup_addresses(self), - priv->best_default_route, - priv->privacy, - &priv->address_data_variant, - &priv->addresses_variant); - g_variant_ref_sink(priv->address_data_variant); - g_variant_ref_sink(priv->addresses_variant); - } - g_value_set_variant(value, - prop_id == PROP_ADDRESS_DATA ? priv->address_data_variant - : priv->addresses_variant); - break; - - case PROP_ROUTE_DATA: - case PROP_ROUTES: - nm_assert(!!priv->route_data_variant == !!priv->routes_variant); - - if (!priv->route_data_variant) { - nm_utils_ip_routes_to_dbus(AF_INET6, - nm_ip6_config_lookup_routes(self), - &priv->route_data_variant, - &priv->routes_variant); - g_variant_ref_sink(priv->route_data_variant); - g_variant_ref_sink(priv->routes_variant); - } - - g_value_set_variant(value, - prop_id == PROP_ROUTE_DATA ? priv->route_data_variant - : priv->routes_variant); - break; - case PROP_GATEWAY: - if (priv->best_default_route) { - g_value_take_string(value, - nm_utils_inet6_ntop_dup( - &NMP_OBJECT_CAST_IP6_ROUTE(priv->best_default_route)->gateway)); - } else - g_value_set_string(value, NULL); - break; - case PROP_NAMESERVERS: - nameservers_to_gvalue(priv->nameservers, value); - break; - case PROP_DOMAINS: - nm_utils_g_value_set_strv(value, priv->domains); - break; - case PROP_SEARCHES: - nm_utils_g_value_set_strv(value, priv->searches); - break; - case PROP_DNS_OPTIONS: - nm_utils_g_value_set_strv(value, priv->dns_options); - break; - case PROP_DNS_PRIORITY: - g_value_set_int(value, priv->dns_priority); - 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) -{ - NMIP6Config * self = NM_IP6_CONFIG(object); - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - switch (prop_id) { - case PROP_MULTI_IDX: - /* construct-only */ - priv->multi_idx = g_value_get_pointer(value); - if (!priv->multi_idx) - g_return_if_reached(); - nm_dedup_multi_index_ref(priv->multi_idx); - break; - case PROP_IFINDEX: - /* construct-only */ - priv->ifindex = g_value_get_int(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -/*****************************************************************************/ - -static void -nm_ip6_config_init(NMIP6Config *self) -{ - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - nm_ip_config_dedup_multi_idx_type_init((NMIPConfigDedupMultiIdxType *) &priv->idx_ip6_addresses, - NMP_OBJECT_TYPE_IP6_ADDRESS); - nm_ip_config_dedup_multi_idx_type_init((NMIPConfigDedupMultiIdxType *) &priv->idx_ip6_routes, - NMP_OBJECT_TYPE_IP6_ROUTE); - - priv->nameservers = g_array_new(FALSE, TRUE, sizeof(struct in6_addr)); - priv->domains = g_ptr_array_new_with_free_func(g_free); - priv->searches = g_ptr_array_new_with_free_func(g_free); - priv->dns_options = g_ptr_array_new_with_free_func(g_free); -} - -NMIP6Config * -nm_ip6_config_new(NMDedupMultiIndex *multi_idx, int ifindex) -{ - g_return_val_if_fail(ifindex >= -1, NULL); - return g_object_new(NM_TYPE_IP6_CONFIG, - NM_IP6_CONFIG_MULTI_IDX, - multi_idx, - NM_IP6_CONFIG_IFINDEX, - ifindex, - NULL); -} - -NMIP6Config * -nm_ip6_config_new_cloned(const NMIP6Config *src) -{ - NMIP6Config *new; - - g_return_val_if_fail(NM_IS_IP6_CONFIG(src), NULL); - - new = nm_ip6_config_new(nm_ip6_config_get_multi_idx(src), nm_ip6_config_get_ifindex(src)); - nm_ip6_config_replace(new, src, NULL); - return new; -} - -static void -finalize(GObject *object) -{ - NMIP6Config * self = NM_IP6_CONFIG(object); - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE(self); - - nm_clear_nmp_object(&priv->best_default_route); - - nm_dedup_multi_index_remove_idx(priv->multi_idx, &priv->idx_ip6_addresses); - nm_dedup_multi_index_remove_idx(priv->multi_idx, &priv->idx_ip6_routes); - - nm_clear_g_variant(&priv->address_data_variant); - nm_clear_g_variant(&priv->addresses_variant); - nm_clear_g_variant(&priv->route_data_variant); - nm_clear_g_variant(&priv->routes_variant); - - g_array_unref(priv->nameservers); - g_ptr_array_unref(priv->domains); - g_ptr_array_unref(priv->searches); - g_ptr_array_unref(priv->dns_options); - - G_OBJECT_CLASS(nm_ip6_config_parent_class)->finalize(object); - - nm_dedup_multi_index_unref(priv->multi_idx); -} - -static const NMDBusInterfaceInfoExtended interface_info_ip6_config = { - .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT( - NM_DBUS_INTERFACE_IP6_CONFIG, - .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS( - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Addresses", - "a(ayuay)", - NM_IP6_CONFIG_ADDRESSES), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("AddressData", - "aa{sv}", - NM_IP6_CONFIG_ADDRESS_DATA), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Gateway", "s", NM_IP6_CONFIG_GATEWAY), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Routes", - "a(ayuayu)", - NM_IP6_CONFIG_ROUTES), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("RouteData", - "aa{sv}", - NM_IP6_CONFIG_ROUTE_DATA), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Nameservers", - "aay", - NM_IP6_CONFIG_NAMESERVERS), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Domains", "as", NM_IP6_CONFIG_DOMAINS), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Searches", - "as", - NM_IP6_CONFIG_SEARCHES), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DnsOptions", - "as", - NM_IP6_CONFIG_DNS_OPTIONS), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DnsPriority", - "i", - NM_IP6_CONFIG_DNS_PRIORITY), ), ), -}; - -static void -nm_ip6_config_class_init(NMIP6ConfigClass *klass) -{ - GObjectClass * object_class = G_OBJECT_CLASS(klass); - NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass); - NMIPConfigClass * ip_config_class = NM_IP_CONFIG_CLASS(klass); - - ip_config_class->is_ipv4 = FALSE; - ip_config_class->addr_family = AF_INET6; - - dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED(NM_DBUS_PATH "/IP6Config"); - dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_ip6_config); - - object_class->get_property = get_property; - object_class->set_property = set_property; - object_class->finalize = finalize; - - obj_properties[PROP_MULTI_IDX] = - g_param_spec_pointer(NM_IP6_CONFIG_MULTI_IDX, - "", - "", - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_IFINDEX] = - g_param_spec_int(NM_IP6_CONFIG_IFINDEX, - "", - "", - -1, - G_MAXINT, - -1, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_ADDRESS_DATA] = - g_param_spec_variant(NM_IP6_CONFIG_ADDRESS_DATA, - "", - "", - G_VARIANT_TYPE("aa{sv}"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_ADDRESSES] = - g_param_spec_variant(NM_IP6_CONFIG_ADDRESSES, - "", - "", - G_VARIANT_TYPE("a(ayuay)"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_ROUTE_DATA] = - g_param_spec_variant(NM_IP6_CONFIG_ROUTE_DATA, - "", - "", - G_VARIANT_TYPE("aa{sv}"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_ROUTES] = g_param_spec_variant(NM_IP6_CONFIG_ROUTES, - "", - "", - G_VARIANT_TYPE("a(ayuayu)"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_GATEWAY] = g_param_spec_string(NM_IP6_CONFIG_GATEWAY, - "", - "", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_NAMESERVERS] = - g_param_spec_variant(NM_IP6_CONFIG_NAMESERVERS, - "", - "", - G_VARIANT_TYPE("aay"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_DOMAINS] = g_param_spec_boxed(NM_IP6_CONFIG_DOMAINS, - "", - "", - G_TYPE_STRV, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_SEARCHES] = g_param_spec_boxed(NM_IP6_CONFIG_SEARCHES, - "", - "", - G_TYPE_STRV, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_DNS_OPTIONS] = - g_param_spec_boxed(NM_IP6_CONFIG_DNS_OPTIONS, - "", - "", - G_TYPE_STRV, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_DNS_PRIORITY] = g_param_spec_int(NM_IP6_CONFIG_DNS_PRIORITY, - "", - "", - G_MININT32, - G_MAXINT32, - 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); -} diff --git a/src/core/nm-ip6-config.h b/src/core/nm-ip6-config.h deleted file mode 100644 index a54040fc59..0000000000 --- a/src/core/nm-ip6-config.h +++ /dev/null @@ -1,205 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2008 - 2013 Red Hat, Inc. - */ - -#ifndef __NETWORKMANAGER_IP6_CONFIG_H__ -#define __NETWORKMANAGER_IP6_CONFIG_H__ - -#include - -#include "nm-setting-ip6-config.h" - -#include "libnm-glib-aux/nm-dedup-multi.h" -#include "libnm-platform/nmp-object.h" -#include "nm-ip-config.h" - -/*****************************************************************************/ - -void nm_ip_config_iter_ip6_address_init(NMDedupMultiIter *iter, const NMIP6Config *self); -void nm_ip_config_iter_ip6_route_init(NMDedupMultiIter *iter, const NMIP6Config *self); - -#define nm_ip_config_iter_ip6_address_for_each(iter, self, address) \ - for (nm_ip_config_iter_ip6_address_init((iter), (self)); \ - nm_platform_dedup_multi_iter_next_ip6_address((iter), (address));) - -#define nm_ip_config_iter_ip6_route_for_each(iter, self, route) \ - for (nm_ip_config_iter_ip6_route_init((iter), (self)); \ - nm_platform_dedup_multi_iter_next_ip6_route((iter), (route));) - -/*****************************************************************************/ - -#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_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)) -#define NM_IS_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_IP6_CONFIG)) -#define NM_IP6_CONFIG_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass)) - -typedef struct _NMIP6ConfigClass NMIP6ConfigClass; - -/* internal */ -#define NM_IP6_CONFIG_MULTI_IDX "multi-idx" -#define NM_IP6_CONFIG_IFINDEX "ifindex" - -/* public */ -#define NM_IP6_CONFIG_ADDRESS_DATA "address-data" -#define NM_IP6_CONFIG_ROUTE_DATA "route-data" -#define NM_IP6_CONFIG_GATEWAY "gateway" -#define NM_IP6_CONFIG_NAMESERVERS "nameservers" -#define NM_IP6_CONFIG_DOMAINS "domains" -#define NM_IP6_CONFIG_SEARCHES "searches" -#define NM_IP6_CONFIG_DNS_OPTIONS "dns-options" -#define NM_IP6_CONFIG_DNS_PRIORITY "dns-priority" - -/* deprecated */ -#define NM_IP6_CONFIG_ADDRESSES "addresses" -#define NM_IP6_CONFIG_ROUTES "routes" - -GType nm_ip6_config_get_type(void); - -NMIP6Config *nm_ip6_config_new(struct _NMDedupMultiIndex *multi_idx, int ifindex); -NMIP6Config *nm_ip6_config_new_cloned(const NMIP6Config *src); - -NMIP6Config *nm_ip6_config_clone(const NMIP6Config *self); -int nm_ip6_config_get_ifindex(const NMIP6Config *self); - -struct _NMDedupMultiIndex *nm_ip6_config_get_multi_idx(const NMIP6Config *self); - -NMIP6Config *nm_ip6_config_capture(struct _NMDedupMultiIndex *multi_idx, - NMPlatform * platform, - int ifindex, - NMSettingIP6ConfigPrivacy use_temporary); - -void nm_ip6_config_add_dependent_routes(NMIP6Config *self, - guint32 route_table, - guint32 route_metric, - gboolean is_vrf); - -gboolean nm_ip6_config_commit(const NMIP6Config * self, - NMPlatform * platform, - NMIPRouteTableSyncMode route_table_sync, - GPtrArray ** out_temporary_not_available); -void nm_ip6_config_merge_setting(NMIP6Config * self, - NMSettingIPConfig *setting, - guint32 route_table, - guint32 route_metric); -NMSetting *nm_ip6_config_create_setting(const NMIP6Config *self, gboolean maybe_ipv6_disabled); - -void nm_ip6_config_merge(NMIP6Config * dst, - const NMIP6Config * src, - NMIPConfigMergeFlags merge_flags, - guint32 default_route_metric_penalty); -void nm_ip6_config_subtract(NMIP6Config * dst, - const NMIP6Config *src, - guint32 default_route_metric_penalty); -void nm_ip6_config_intersect(NMIP6Config * dst, - const NMIP6Config *src, - gboolean intersect_addresses, - gboolean intersect_routes, - guint32 default_route_metric_penalty); -NMIP6Config *nm_ip6_config_intersect_alloc(const NMIP6Config *a, - const NMIP6Config *b, - gboolean intersect_addresses, - gboolean intersect_routes, - guint32 default_route_metric_penalty); -gboolean -nm_ip6_config_replace(NMIP6Config *dst, const NMIP6Config *src, gboolean *relevant_changes); - -const NMPObject *nm_ip6_config_best_default_route_get(const NMIP6Config *self); -const NMPObject *_nm_ip6_config_best_default_route_find(const NMIP6Config *self); - -enum _NMIPConfigFlags; - -void nm_ip6_config_set_config_flags(NMIP6Config * self, - enum _NMIPConfigFlags flags, - enum _NMIPConfigFlags mask); -enum _NMIPConfigFlags nm_ip6_config_get_config_flags(const NMIP6Config *self); - -const NMDedupMultiHeadEntry *nm_ip6_config_lookup_addresses(const NMIP6Config *self); -void nm_ip6_config_reset_addresses(NMIP6Config *self); -void nm_ip6_config_add_address(NMIP6Config *self, const NMPlatformIP6Address *address); -void _nmtst_ip6_config_del_address(NMIP6Config *self, guint i); -guint nm_ip6_config_get_num_addresses(const NMIP6Config *self); -const NMPlatformIP6Address *nm_ip6_config_get_first_address(const NMIP6Config *self); -const NMPlatformIP6Address *_nmtst_ip6_config_get_address(const NMIP6Config *self, guint i); -gboolean nm_ip6_config_address_exists(const NMIP6Config *self, const NMPlatformIP6Address *address); -const NMPlatformIP6Address *nm_ip6_config_lookup_address(const NMIP6Config * self, - const struct in6_addr *addr); -gboolean _nmtst_ip6_config_addresses_sort(NMIP6Config *self); -gboolean nm_ip6_config_has_any_dad_pending(const NMIP6Config *self, const NMIP6Config *candidates); - -const NMDedupMultiHeadEntry *nm_ip6_config_lookup_routes(const NMIP6Config *self); -void nm_ip6_config_reset_routes(NMIP6Config *self); -void nm_ip6_config_add_route(NMIP6Config * self, - const NMPlatformIP6Route *route, - const NMPObject ** out_obj_new); -void _nmtst_ip6_config_del_route(NMIP6Config *self, guint i); -guint nm_ip6_config_get_num_routes(const NMIP6Config *self); -const NMPlatformIP6Route * _nmtst_ip6_config_get_route(const NMIP6Config *self, guint i); - -const NMPlatformIP6Route * nm_ip6_config_get_direct_route_for_host(const NMIP6Config * self, - const struct in6_addr *host, - guint32 route_table); -const NMPlatformIP6Address *nm_ip6_config_get_subnet_for_host(const NMIP6Config * self, - const struct in6_addr *host); - -void nm_ip6_config_reset_nameservers(NMIP6Config *self); -void nm_ip6_config_add_nameserver(NMIP6Config *self, const struct in6_addr *nameserver); -void nm_ip6_config_del_nameserver(NMIP6Config *self, guint i); -guint nm_ip6_config_get_num_nameservers(const NMIP6Config *self); -const struct in6_addr *nm_ip6_config_get_nameserver(const NMIP6Config *self, guint i); - -void nm_ip6_config_reset_domains(NMIP6Config *self); -void nm_ip6_config_add_domain(NMIP6Config *self, const char *domain); -void nm_ip6_config_del_domain(NMIP6Config *self, guint i); -guint nm_ip6_config_get_num_domains(const NMIP6Config *self); -const char *nm_ip6_config_get_domain(const NMIP6Config *self, guint i); - -void nm_ip6_config_reset_searches(NMIP6Config *self); -void nm_ip6_config_add_search(NMIP6Config *self, const char *search); -void nm_ip6_config_del_search(NMIP6Config *self, guint i); -guint nm_ip6_config_get_num_searches(const NMIP6Config *self); -const char *nm_ip6_config_get_search(const NMIP6Config *self, guint i); - -void nm_ip6_config_reset_dns_options(NMIP6Config *self); -void nm_ip6_config_add_dns_option(NMIP6Config *self, const char *option); -void nm_ip6_config_del_dns_option(NMIP6Config *self, guint i); -guint nm_ip6_config_get_num_dns_options(const NMIP6Config *self); -const char *nm_ip6_config_get_dns_option(const NMIP6Config *self, guint i); - -void nm_ip6_config_set_dns_priority(NMIP6Config *self, int priority); -int nm_ip6_config_get_dns_priority(const NMIP6Config *self); - -void nm_ip6_config_set_never_default(NMIP6Config *self, gboolean never_default); -gboolean nm_ip6_config_get_never_default(const NMIP6Config *self); - -const NMPObject *nm_ip6_config_nmpobj_lookup(const NMIP6Config *self, const NMPObject *needle); -gboolean nm_ip6_config_nmpobj_remove(NMIP6Config *self, const NMPObject *needle); - -void nm_ip6_config_hash(const NMIP6Config *self, GChecksum *sum, gboolean dns_only); -gboolean nm_ip6_config_equal(const NMIP6Config *a, const NMIP6Config *b); - -void nm_ip6_config_set_privacy(NMIP6Config *self, NMSettingIP6ConfigPrivacy privacy); - -struct _NMNDiscAddress; -void nm_ip6_config_reset_addresses_ndisc(NMIP6Config * self, - const struct _NMNDiscAddress *addresses, - guint addresses_n, - guint8 plen, - guint32 ifa_flags); -struct _NMNDiscRoute; -struct _NMNDiscGateway; -void nm_ip6_config_reset_routes_ndisc(NMIP6Config * self, - const struct _NMNDiscGateway *gateways, - guint gateways_n, - const struct _NMNDiscRoute * routes, - guint routes_n, - guint32 route_table, - guint32 route_metric); - -void nm_ip6_config_update_routes_metric(NMIP6Config *self, gint64 metric); - -#endif /* __NETWORKMANAGER_IP6_CONFIG_H__ */ diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c index 033c8942c2..deec665d1d 100644 --- a/src/core/nm-l3-config-data.c +++ b/src/core/nm-l3-config-data.c @@ -117,8 +117,9 @@ struct _NML3ConfigData { NMIPRouteTableSyncMode route_table_sync_x[2]; }; - NMSettingConnectionMdns mdns; - NMSettingConnectionLlmnr llmnr; + NMSettingConnectionMdns mdns; + NMSettingConnectionLlmnr llmnr; + NMSettingConnectionDnsOverTls dns_over_tls; NML3ConfigDatFlags flags; @@ -570,6 +571,16 @@ nm_l3_config_data_log(const NML3ConfigData *self, NULL))); } + if (self->dns_over_tls != NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT) { + gs_free char *s = NULL; + + _L("dns-over-tls: %s", + (s = _nm_utils_enum_to_str_full(nm_setting_connection_dns_over_tls_get_type(), + self->dns_over_tls, + " ", + NULL))); + } + if (self->metered != NM_TERNARY_DEFAULT) _L("metered: %s", self->metered ? "yes" : "no"); @@ -666,6 +677,7 @@ nm_l3_config_data_new(NMDedupMultiIndex *multi_idx, int ifindex, NMIPConfigSourc .multi_idx = nm_dedup_multi_index_ref(multi_idx), .mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT, .llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT, + .dns_over_tls = NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT, .flags = NM_L3_CONFIG_DAT_FLAGS_NONE, .metered = NM_TERNARY_DEFAULT, .proxy_browser_only = NM_TERNARY_DEFAULT, @@ -1679,6 +1691,26 @@ nm_l3_config_data_set_llmnr(NML3ConfigData *self, NMSettingConnectionLlmnr llmnr return TRUE; } +NMSettingConnectionDnsOverTls +nm_l3_config_data_get_dns_over_tls(const NML3ConfigData *self) +{ + nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE)); + + return self->dns_over_tls; +} + +gboolean +nm_l3_config_data_set_dns_over_tls(NML3ConfigData *self, NMSettingConnectionDnsOverTls dns_over_tls) +{ + nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE)); + + if (self->dns_over_tls == dns_over_tls) + return FALSE; + + self->dns_over_tls = dns_over_tls; + return TRUE; +} + NMIPRouteTableSyncMode nm_l3_config_data_get_route_table_sync(const NML3ConfigData *self, int addr_family) { @@ -2125,6 +2157,7 @@ nm_l3_config_data_cmp_full(const NML3ConfigData *a, NM_CMP_DIRECT_REF_STRING(a->nis_domain, b->nis_domain); NM_CMP_DIRECT(a->mdns, b->mdns); NM_CMP_DIRECT(a->llmnr, b->llmnr); + NM_CMP_DIRECT(a->dns_over_tls, b->dns_over_tls); NM_CMP_DIRECT(a->mtu, b->mtu); NM_CMP_DIRECT(a->ip6_mtu, b->ip6_mtu); NM_CMP_DIRECT_UNSAFE(a->metered, b->metered); @@ -3019,6 +3052,9 @@ nm_l3_config_data_merge(NML3ConfigData * self, if (self->llmnr == NM_SETTING_CONNECTION_LLMNR_DEFAULT) self->llmnr = src->llmnr; + if (self->dns_over_tls == NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT) + self->dns_over_tls = src->dns_over_tls; + self->metered = NM_MAX((NMTernary) self->metered, (NMTernary) src->metered); if (self->proxy_method == NM_PROXY_CONFIG_METHOD_UNKNOWN) diff --git a/src/core/nm-l3-config-data.h b/src/core/nm-l3-config-data.h index 1ad23c3251..2f9cfbbab8 100644 --- a/src/core/nm-l3-config-data.h +++ b/src/core/nm-l3-config-data.h @@ -446,6 +446,11 @@ NMSettingConnectionLlmnr nm_l3_config_data_get_llmnr(const NML3ConfigData *self) gboolean nm_l3_config_data_set_llmnr(NML3ConfigData *self, NMSettingConnectionLlmnr llmnr); +NMSettingConnectionDnsOverTls nm_l3_config_data_get_dns_over_tls(const NML3ConfigData *self); + +gboolean nm_l3_config_data_set_dns_over_tls(NML3ConfigData * self, + NMSettingConnectionDnsOverTls dns_over_tls); + NMIPRouteTableSyncMode nm_l3_config_data_get_route_table_sync(const NML3ConfigData *self, int addr_family); diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index aa56c13f87..24f139b7d4 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -15,6 +15,7 @@ #include "nm-netns.h" #include "n-acd/src/n-acd.h" #include "nm-l3-ipv4ll.h" +#include "nm-ip-config.h" /*****************************************************************************/ @@ -246,6 +247,14 @@ typedef struct _NML3CfgPrivate { GSource *nacd_event_down_source; gint64 nacd_event_down_ratelimited_until_msec; + union { + struct { + NMIPConfig *ipconfig_6; + NMIPConfig *ipconfig_4; + }; + NMIPConfig *ipconfig_x[2]; + }; + /* This is for rate-limiting the creation of nacd instance. */ GSource *nacd_instance_ensure_retry; @@ -402,6 +411,73 @@ static NM_UTILS_LOOKUP_DEFINE(_l3_acd_addr_state_to_string, /*****************************************************************************/ +NMIPConfig * +nm_l3cfg_ipconfig_get(NML3Cfg *self, int addr_family) +{ + g_return_val_if_fail(NM_IS_L3CFG(self), NULL); + nm_assert_addr_family(addr_family); + + return self->priv.p->ipconfig_x[NM_IS_IPv4(addr_family)]; +} + +static void +_ipconfig_toggle_notify(gpointer data, GObject *object, gboolean is_last_ref) +{ + NML3Cfg * self = NM_L3CFG(data); + NMIPConfig *ipconfig = NM_IP_CONFIG(object); + + if (!is_last_ref) { + /* This happens while we take another ref below. Ignore the signal. */ + nm_assert(!NM_IN_SET(ipconfig, self->priv.p->ipconfig_4, self->priv.p->ipconfig_6)); + return; + } + + if (ipconfig == self->priv.p->ipconfig_4) + self->priv.p->ipconfig_4 = NULL; + else { + nm_assert(ipconfig == self->priv.p->ipconfig_6); + self->priv.p->ipconfig_6 = NULL; + } + + /* We take a second reference to keep the instance alive, while also removing the + * toggle ref. This will notify the function again, but we will ignore that. */ + g_object_ref(ipconfig); + + g_object_remove_toggle_ref(G_OBJECT(ipconfig), _ipconfig_toggle_notify, self); + + /* pass on the reference, and unexport on idle. */ + nm_ip_config_take_and_unexport_on_idle(g_steal_pointer(&ipconfig)); +} + +NMIPConfig * +nm_l3cfg_ipconfig_acquire(NML3Cfg *self, int addr_family) +{ + NMIPConfig *ipconfig; + + g_return_val_if_fail(NM_IS_L3CFG(self), NULL); + nm_assert_addr_family(addr_family); + + ipconfig = self->priv.p->ipconfig_x[NM_IS_IPv4(addr_family)]; + + if (ipconfig) + return g_object_ref(ipconfig); + + ipconfig = nm_ip_config_new(addr_family, self); + + self->priv.p->ipconfig_x[NM_IS_IPv4(addr_family)] = ipconfig; + + /* The ipconfig keeps self alive. We use a toggle reference + * to avoid a cycle. But we anyway wouldn't want a strong reference, + * because the user releases the instance by unrefing it, and we + * notice that via the weak reference. */ + g_object_add_toggle_ref(G_OBJECT(ipconfig), _ipconfig_toggle_notify, self); + + /* We keep the toggle reference, and return the other reference to the caller. */ + return g_steal_pointer(&ipconfig); +} + +/*****************************************************************************/ + gboolean nm_l3cfg_is_vrf(const NML3Cfg *self) { @@ -4376,6 +4452,9 @@ finalize(GObject *object) { NML3Cfg *self = NM_L3CFG(object); + nm_assert(!self->priv.p->ipconfig_4); + nm_assert(!self->priv.p->ipconfig_6); + nm_assert(!self->priv.p->l3_config_datas); nm_assert(!self->priv.p->ipv4ll); diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h index 19dec11839..5eca683a90 100644 --- a/src/core/nm-l3cfg.h +++ b/src/core/nm-l3cfg.h @@ -447,4 +447,10 @@ void _nm_l3cfg_unregister_ipv4ll(NML3Cfg *self); /*****************************************************************************/ +struct _NMIPConfig; +struct _NMIPConfig *nm_l3cfg_ipconfig_get(NML3Cfg *self, int addr_family); +struct _NMIPConfig *nm_l3cfg_ipconfig_acquire(NML3Cfg *self, int addr_family); + +/*****************************************************************************/ + #endif /* __NM_L3CFG_H__ */ diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c index 49c56bf68f..ebd68f7604 100644 --- a/src/core/nm-manager.c +++ b/src/core/nm-manager.c @@ -1733,10 +1733,6 @@ remove_device(NMManager *self, NMDevice *device, gboolean quitting) TRUE, NM_DEVICE_STATE_REASON_REMOVED); } - } else if (quitting - && nm_config_get_configure_and_quit(priv->config) - == NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED) { - nm_device_spawn_iface_helper(device); } } @@ -2276,13 +2272,7 @@ system_unmanaged_devices_changed_cb(NMSettings *settings, GParamSpec *pspec, gpo static void hostname_changed_cb(NMHostnameManager *hostname_manager, GParamSpec *pspec, NMManager *self) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - const char * hostname; - - hostname = nm_hostname_manager_get_hostname(priv->hostname_manager); - nm_dispatcher_call_hostname(NULL, NULL, NULL); - nm_dhcp_manager_set_default_hostname(nm_dhcp_manager_get(), hostname); } /*****************************************************************************/ @@ -2599,8 +2589,6 @@ get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generat if (out_generated) *out_generated = FALSE; - nm_device_capture_initial_config(device); - if (!nm_device_can_assume_connections(device)) { nm_device_assume_state_reset(device); _LOG2D(LOGD_DEVICE, device, "assume: device cannot assume connection"); diff --git a/src/core/nm-netns.c b/src/core/nm-netns.c index 224070bee7..538b0e0c1f 100644 --- a/src/core/nm-netns.c +++ b/src/core/nm-netns.c @@ -126,7 +126,7 @@ _l3cfg_weak_notify(gpointer data, GObject *where_the_object_was) } NML3Cfg * -nm_netns_get_l3cfg(NMNetns *self, int ifindex) +nm_netns_l3cfg_get(NMNetns *self, int ifindex) { NMNetnsPrivate *priv; @@ -139,7 +139,7 @@ nm_netns_get_l3cfg(NMNetns *self, int ifindex) } NML3Cfg * -nm_netns_access_l3cfg(NMNetns *self, int ifindex) +nm_netns_l3cfg_acquire(NMNetns *self, int ifindex) { NMNetnsPrivate *priv; L3CfgData * l3cfg_data; diff --git a/src/core/nm-netns.h b/src/core/nm-netns.h index 78a9121a6c..36ed497288 100644 --- a/src/core/nm-netns.h +++ b/src/core/nm-netns.h @@ -35,9 +35,9 @@ struct _NMDedupMultiIndex *nm_netns_get_multi_idx(NMNetns *self); #define NM_NETNS_GET (nm_netns_get()) -NML3Cfg *nm_netns_get_l3cfg(NMNetns *self, int ifindex); +NML3Cfg *nm_netns_l3cfg_get(NMNetns *self, int ifindex); -NML3Cfg *nm_netns_access_l3cfg(NMNetns *netns, int ifindex); +NML3Cfg *nm_netns_l3cfg_acquire(NMNetns *netns, int ifindex); /*****************************************************************************/ diff --git a/src/core/nm-pacrunner-manager.c b/src/core/nm-pacrunner-manager.c index 67930dbae0..6eca4d9432 100644 --- a/src/core/nm-pacrunner-manager.c +++ b/src/core/nm-pacrunner-manager.c @@ -7,15 +7,14 @@ #include "nm-pacrunner-manager.h" -#include "nm-utils.h" -#include "NetworkManagerUtils.h" -#include "libnm-platform/nm-platform.h" -#include "nm-dbus-manager.h" -#include "nm-proxy-config.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" #include "c-list/src/c-list.h" #include "libnm-glib-aux/nm-dbus-aux.h" +#include "libnm-platform/nm-platform.h" + +#include "NetworkManagerUtils.h" +#include "nm-dbus-manager.h" +#include "nm-l3-config-data.h" +#include "nm-utils.h" #define PACRUNNER_DBUS_SERVICE "org.pacrunner" #define PACRUNNER_DBUS_INTERFACE "org.pacrunner.Manager" @@ -124,58 +123,48 @@ NM_AUTO_DEFINE_FCN0(NMPacrunnerConfId *, _nm_auto_unref_conf_id, conf_id_unref); /*****************************************************************************/ static void -get_ip_domains(GPtrArray *domains, NMIPConfig *ip_config) +get_ip_domains(GPtrArray *domains, const NML3ConfigData *l3cd, int addr_family) { NMDedupMultiIter ipconf_iter; char * cidr; - guint i, num; + guint num; + guint i; char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - int addr_family; const NMPlatformIPAddress *address; - const NMPlatformIPRoute * routes; + const NMPlatformIPRoute * route; + const char *const * strv; - if (!ip_config) - return; - - addr_family = nm_ip_config_get_addr_family(ip_config); - - num = nm_ip_config_get_num_searches(ip_config); + strv = nm_l3_config_data_get_searches(l3cd, addr_family, &num); for (i = 0; i < num; i++) - g_ptr_array_add(domains, g_strdup(nm_ip_config_get_search(ip_config, i))); + g_ptr_array_add(domains, g_strdup(strv[i])); - num = nm_ip_config_get_num_domains(ip_config); + strv = nm_l3_config_data_get_domains(l3cd, addr_family, &num); for (i = 0; i < num; i++) - g_ptr_array_add(domains, g_strdup(nm_ip_config_get_domain(ip_config, i))); + g_ptr_array_add(domains, g_strdup(strv[i])); - nm_ip_config_iter_ip_address_for_each (&ipconf_iter, ip_config, &address) { + 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), address->plen); g_ptr_array_add(domains, cidr); } - nm_ip_config_iter_ip_route_for_each (&ipconf_iter, ip_config, &routes) { - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(routes)) + nm_l3_config_data_iter_ip_route_for_each (&ipconf_iter, l3cd, addr_family, &route) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route)) continue; cidr = g_strdup_printf("%s/%u", - nm_utils_inet_ntop(addr_family, routes->network_ptr, sbuf), - routes->plen); + nm_utils_inet_ntop(addr_family, route->network_ptr, sbuf), + route->plen); g_ptr_array_add(domains, cidr); } } static GVariant * -_make_request_create_proxy_configuration(NMProxyConfig *proxy_config, - const char * iface, - NMIP4Config * ip4_config, - NMIP6Config * ip6_config) +_make_request_create_proxy_configuration(const char *iface, const NML3ConfigData *l3cd) { GVariantBuilder builder; NMProxyConfigMethod method; - const char * pac_url; - const char * pac_script; - - nm_assert(NM_IS_PROXY_CONFIG(proxy_config)); + const char * s; g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); @@ -183,20 +172,19 @@ _make_request_create_proxy_configuration(NMProxyConfig *proxy_config, g_variant_builder_add(&builder, "{sv}", "Interface", g_variant_new_string(iface)); } - method = nm_proxy_config_get_method(proxy_config); + method = l3cd ? nm_l3_config_data_get_proxy_method(l3cd) : NM_PROXY_CONFIG_METHOD_UNKNOWN; + switch (method) { case NM_PROXY_CONFIG_METHOD_AUTO: g_variant_builder_add(&builder, "{sv}", "Method", g_variant_new_string("auto")); - pac_url = nm_proxy_config_get_pac_url(proxy_config); - if (pac_url) { - g_variant_builder_add(&builder, "{sv}", "URL", g_variant_new_string(pac_url)); - } + s = nm_l3_config_data_get_proxy_pac_url(l3cd); + if (s) + g_variant_builder_add(&builder, "{sv}", "URL", g_variant_new_string(s)); - pac_script = nm_proxy_config_get_pac_script(proxy_config); - if (pac_script) { - g_variant_builder_add(&builder, "{sv}", "Script", g_variant_new_string(pac_script)); - } + s = nm_l3_config_data_get_proxy_pac_script(l3cd); + if (s) + g_variant_builder_add(&builder, "{sv}", "Script", g_variant_new_string(s)); break; case NM_PROXY_CONFIG_METHOD_UNKNOWN: case NM_PROXY_CONFIG_METHOD_NONE: @@ -204,18 +192,19 @@ _make_request_create_proxy_configuration(NMProxyConfig *proxy_config, break; } - g_variant_builder_add(&builder, - "{sv}", - "BrowserOnly", - g_variant_new_boolean(nm_proxy_config_get_browser_only(proxy_config))); + g_variant_builder_add( + &builder, + "{sv}", + "BrowserOnly", + g_variant_new_boolean(l3cd ? !!nm_l3_config_data_get_proxy_browser_only(l3cd) : FALSE)); - if (ip4_config || ip6_config) { + if (l3cd) { gs_unref_ptrarray GPtrArray *domains = NULL; domains = g_ptr_array_new_with_free_func(g_free); - get_ip_domains(domains, NM_IP_CONFIG_CAST(ip4_config)); - get_ip_domains(domains, NM_IP_CONFIG_CAST(ip6_config)); + get_ip_domains(domains, l3cd, AF_INET); + get_ip_domains(domains, l3cd, AF_INET6); if (domains->len > 0) { g_variant_builder_add( @@ -356,10 +345,8 @@ _try_start_service_by_name(NMPacrunnerManager *self) /** * nm_pacrunner_manager_add: * @self: the #NMPacrunnerManager - * @proxy_config: proxy config of the connection * @iface: the iface for the connection or %NULL - * @ip4_config: IP4 config of the connection to extract domain info from - * @ip6_config: IP6 config of the connection to extract domain info from + * @l3cd: the #NML3ConfigData of the connection to extract domain info from * * Returns: a #NMPacrunnerConfId id. The function cannot * fail and always returns a non NULL pointer. The conf-id may @@ -367,18 +354,13 @@ _try_start_service_by_name(NMPacrunnerManager *self) * Note that the conf-id keeps the @self instance alive. */ NMPacrunnerConfId * -nm_pacrunner_manager_add(NMPacrunnerManager *self, - NMProxyConfig * proxy_config, - const char * iface, - NMIP4Config * ip4_config, - NMIP6Config * ip6_config) +nm_pacrunner_manager_add(NMPacrunnerManager *self, const char *iface, const NML3ConfigData *l3cd) { NMPacrunnerManagerPrivate *priv; NMPacrunnerConfId * conf_id; gs_free char * log_msg = NULL; g_return_val_if_fail(NM_IS_PACRUNNER_MANAGER(self), NULL); - g_return_val_if_fail(proxy_config, NULL); priv = NM_PACRUNNER_MANAGER_GET_PRIVATE(self); @@ -387,8 +369,7 @@ nm_pacrunner_manager_add(NMPacrunnerManager *self, .log_id = ++priv->log_id_counter, .refcount = 1, .self = g_object_ref(self), - .parameters = g_variant_ref_sink( - _make_request_create_proxy_configuration(proxy_config, iface, ip4_config, ip6_config)), + .parameters = g_variant_ref_sink(_make_request_create_proxy_configuration(iface, l3cd)), }; c_list_link_tail(&priv->conf_id_lst_head, &conf_id->conf_id_lst); diff --git a/src/core/nm-pacrunner-manager.h b/src/core/nm-pacrunner-manager.h index fd40f1204d..3f16908f4e 100644 --- a/src/core/nm-pacrunner-manager.h +++ b/src/core/nm-pacrunner-manager.h @@ -26,11 +26,8 @@ GType nm_pacrunner_manager_get_type(void); NMPacrunnerManager *nm_pacrunner_manager_get(void); -NMPacrunnerConfId *nm_pacrunner_manager_add(NMPacrunnerManager *self, - NMProxyConfig * proxy_config, - const char * iface, - NMIP4Config * ip4_config, - NMIP6Config * ip6_config); +NMPacrunnerConfId * +nm_pacrunner_manager_add(NMPacrunnerManager *self, const char *iface, const NML3ConfigData *l3cd); void nm_pacrunner_manager_remove(NMPacrunnerConfId *conf_id); diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c index 03e082f12f..f089f1cb5e 100644 --- a/src/core/nm-policy.c +++ b/src/core/nm-policy.c @@ -11,28 +11,31 @@ #include #include -#include "NetworkManagerUtils.h" -#include "nm-act-request.h" -#include "nm-keep-alive.h" -#include "devices/nm-device.h" -#include "nm-setting-ip4-config.h" -#include "nm-setting-connection.h" -#include "libnm-platform/nm-platform.h" -#include "dns/nm-dns-manager.h" -#include "vpn/nm-vpn-manager.h" -#include "nm-auth-utils.h" -#include "nm-firewalld-manager.h" -#include "nm-dispatcher.h" -#include "nm-utils.h" #include "libnm-core-intern/nm-core-internal.h" -#include "nm-manager.h" -#include "settings/nm-settings.h" -#include "settings/nm-settings-connection.h" -#include "settings/nm-agent-manager.h" -#include "nm-dhcp-config.h" +#include "libnm-platform/nm-platform.h" +#include "libnm-platform/nmp-object.h" + +#include "NetworkManagerUtils.h" +#include "devices/nm-device.h" +#include "dns/nm-dns-manager.h" +#include "nm-act-request.h" +#include "nm-auth-utils.h" #include "nm-config.h" -#include "nm-netns.h" +#include "nm-dhcp-config.h" +#include "nm-dispatcher.h" +#include "nm-firewalld-manager.h" #include "nm-hostname-manager.h" +#include "nm-keep-alive.h" +#include "nm-l3-config-data.h" +#include "nm-manager.h" +#include "nm-netns.h" +#include "nm-setting-connection.h" +#include "nm-setting-ip4-config.h" +#include "nm-utils.h" +#include "settings/nm-agent-manager.h" +#include "settings/nm-settings-connection.h" +#include "settings/nm-settings.h" +#include "vpn/nm-vpn-manager.h" /*****************************************************************************/ @@ -136,30 +139,6 @@ static NMDevice *get_default_device(NMPolicy *self, int addr_family); /*****************************************************************************/ -static void -_dns_manager_set_ip_config(NMDnsManager * dns_manager, - NMIPConfig * ip_config, - NMDnsIPConfigType ip_config_type, - NMDevice * device) -{ - if (device && nm_device_sys_iface_state_is_external(device)) { - nm_dns_manager_set_ip_config(dns_manager, ip_config, NM_DNS_IP_CONFIG_TYPE_REMOVED); - return; - } - - if (NM_IN_SET(ip_config_type, NM_DNS_IP_CONFIG_TYPE_DEFAULT, NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE) - && device - && nm_device_get_route_metric_default(nm_device_get_device_type(device)) - == NM_VPN_ROUTE_METRIC_DEFAULT) { - /* some device types are inherently VPN. */ - ip_config_type = NM_DNS_IP_CONFIG_TYPE_VPN; - } - - nm_dns_manager_set_ip_config(dns_manager, ip_config, ip_config_type); -} - -/*****************************************************************************/ - typedef struct { NMPlatformIP6Address prefix; NMDevice * device; /* The requesting ("uplink") device */ @@ -974,7 +953,7 @@ update_default_ac(NMPolicy *self, int addr_family, NMActiveConnection *best) nm_active_connection_set_default(best, addr_family, TRUE); } -static gpointer +static const NML3ConfigData * get_best_ip_config(NMPolicy * self, int addr_family, const char ** out_ip_iface, @@ -982,20 +961,21 @@ get_best_ip_config(NMPolicy * self, NMDevice ** out_device, NMVpnConnection ** out_vpn) { - NMPolicyPrivate * priv = NM_POLICY_GET_PRIVATE(self); - gpointer conf, best_conf = NULL; - const CList * tmp_list; - NMActiveConnection *ac; - guint64 best_metric = G_MAXUINT64; - NMVpnConnection * best_vpn = NULL; + NMPolicyPrivate * priv = NM_POLICY_GET_PRIVATE(self); + const NML3ConfigData *l3cd_best = NULL; + const CList * tmp_list; + NMActiveConnection * ac; + guint64 best_metric = G_MAXUINT64; + NMVpnConnection * best_vpn = NULL; nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) { - NMVpnConnection * candidate; - NMVpnConnectionState vpn_state; - const NMPObject * obj; - guint32 metric; + const NML3ConfigData *l3cd; + NMVpnConnection * candidate; + NMVpnConnectionState vpn_state; + const NMPObject * obj; + guint32 metric; if (!NM_IS_VPN_CONNECTION(ac)) continue; @@ -1006,24 +986,18 @@ get_best_ip_config(NMPolicy * self, if (vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED) continue; - if (addr_family == AF_INET) - conf = nm_vpn_connection_get_ip4_config(candidate); - else - conf = nm_vpn_connection_get_ip6_config(candidate); - if (!conf) + l3cd = nm_vpn_connection_get_l3cd(candidate); + if (!l3cd) continue; - if (addr_family == AF_INET) - obj = nm_ip4_config_best_default_route_get(conf); - else - obj = nm_ip6_config_best_default_route_get(conf); + obj = nm_l3_config_data_get_best_default_route(l3cd, addr_family); if (!obj) continue; metric = NMP_OBJECT_CAST_IPX_ROUTE(obj)->rx.metric; if (metric <= best_metric) { best_metric = metric; - best_conf = conf; + l3cd_best = l3cd; best_vpn = candidate; } } @@ -1033,25 +1007,20 @@ get_best_ip_config(NMPolicy * self, NM_SET_OUT(out_vpn, best_vpn); NM_SET_OUT(out_ac, NM_ACTIVE_CONNECTION(best_vpn)); NM_SET_OUT(out_ip_iface, nm_vpn_connection_get_ip_iface(best_vpn, TRUE)); - return best_conf; + return l3cd_best; } ac = get_best_active_connection(self, addr_family, TRUE); if (ac) { NMDevice *device = nm_active_connection_get_device(ac); - nm_assert(device); - - if (addr_family == AF_INET) - conf = nm_device_get_ip4_config(device); - else - conf = nm_device_get_ip6_config(device); + nm_assert(NM_IS_DEVICE(device)); NM_SET_OUT(out_device, device); NM_SET_OUT(out_vpn, NULL); NM_SET_OUT(out_ac, ac); NM_SET_OUT(out_ip_iface, nm_device_get_ip_iface(device)); - return conf; + return nm_device_get_l3cd(device, TRUE); } NM_SET_OUT(out_device, NULL); @@ -1069,8 +1038,6 @@ update_ip4_routing(NMPolicy *self, gboolean force_update) NMVpnConnection * vpn = NULL; NMActiveConnection *best_ac = NULL; const char * ip_iface = NULL; - const CList * tmp_list; - NMActiveConnection *ac; /* Note that we might have an IPv4 VPN tunneled over an IPv6-only device, * so we can get (vpn != NULL && best == NULL). @@ -1088,9 +1055,11 @@ update_ip4_routing(NMPolicy *self, gboolean force_update) return; if (best) { + const CList * tmp_list; + NMActiveConnection *ac; + nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) { - if (NM_IS_VPN_CONNECTION(ac) && nm_vpn_connection_get_ip4_config(NM_VPN_CONNECTION(ac)) - && !nm_active_connection_get_device(ac)) + if (NM_IS_VPN_CONNECTION(ac) && !nm_active_connection_get_device(ac)) nm_active_connection_set_device(ac, best); } } @@ -1132,8 +1101,6 @@ update_ip6_routing(NMPolicy *self, gboolean force_update) NMVpnConnection * vpn = NULL; NMActiveConnection *best_ac = NULL; const char * ip_iface = NULL; - NMActiveConnection *ac; - const CList * tmp_list; /* Note that we might have an IPv6 VPN tunneled over an IPv4-only device, * so we can get (vpn != NULL && best == NULL). @@ -1151,9 +1118,11 @@ update_ip6_routing(NMPolicy *self, gboolean force_update) return; if (best) { + const CList * tmp_list; + NMActiveConnection *ac; + nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) { - if (NM_IS_VPN_CONNECTION(ac) && nm_vpn_connection_get_ip6_config(NM_VPN_CONNECTION(ac)) - && !nm_active_connection_get_device(ac)) + if (NM_IS_VPN_CONNECTION(ac) && !nm_active_connection_get_device(ac)) nm_active_connection_set_device(ac, best); } } @@ -1176,25 +1145,38 @@ update_ip6_routing(NMPolicy *self, gboolean force_update) static void update_ip_dns(NMPolicy *self, int addr_family, NMDevice *changed_device) { - NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self); - gpointer ip_config; - const char * ip_iface = NULL; - NMVpnConnection *vpn = NULL; - NMDevice * device = NULL; + NMPolicyPrivate * priv = NM_POLICY_GET_PRIVATE(self); + const NML3ConfigData *l3cd; + const char * ip_iface = NULL; + NMVpnConnection * vpn = NULL; + NMDevice * device = NULL; nm_assert_addr_family(addr_family); - ip_config = get_best_ip_config(self, addr_family, &ip_iface, NULL, &device, &vpn); - if (ip_config) { + l3cd = get_best_ip_config(self, addr_family, &ip_iface, NULL, &device, &vpn); + if (l3cd) { + NMDnsIPConfigType ip_config_type; + + nm_assert(!device || NM_IS_DEVICE(device)); + nm_assert(!vpn || NM_IS_VPN_CONNECTION(vpn)); + nm_assert((!!device) != (!!vpn)); + /* Tell the DNS manager this config is preferred by re-adding it with * a different IP config type. */ - _dns_manager_set_ip_config(NM_POLICY_GET_PRIVATE(self)->dns_manager, - ip_config, - (vpn || (device && nm_device_is_vpn(device))) - ? NM_DNS_IP_CONFIG_TYPE_VPN - : NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE, - device); + if (device && nm_device_sys_iface_state_is_external(device)) + ip_config_type = NM_DNS_IP_CONFIG_TYPE_REMOVED; + else if (vpn || (device && nm_device_is_vpn(device))) + ip_config_type = NM_DNS_IP_CONFIG_TYPE_VPN; + else + ip_config_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE; + + nm_dns_manager_set_ip_config(NM_POLICY_GET_PRIVATE(self)->dns_manager, + addr_family, + ((gconstpointer) device) ?: ((gconstpointer) vpn), + l3cd, + ip_config_type, + TRUE); } if (addr_family == AF_INET6) { @@ -1909,9 +1891,7 @@ device_state_changed(NMDevice * device, NMPolicy * self = _PRIV_TO_SELF(priv); NMActiveConnection * ac; NMSettingsConnection *sett_conn = nm_device_get_settings_connection(device); - NMIP4Config * ip4_config; - NMIP6Config * ip6_config; - NMSettingConnection * s_con = NULL; + NMSettingConnection * s_con = NULL; switch (nm_device_state_reason_check(reason)) { case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: @@ -2013,27 +1993,6 @@ device_state_changed(NMDevice * device, /* Reset auto retries back to default since connection was successful */ nm_settings_connection_autoconnect_retries_reset(sett_conn); } - - /* Add device's new IPv4 and IPv6 configs to DNS */ - - nm_dns_manager_begin_updates(priv->dns_manager, __func__); - - ip4_config = nm_device_get_ip4_config(device); - if (ip4_config) - _dns_manager_set_ip_config(priv->dns_manager, - NM_IP_CONFIG_CAST(ip4_config), - NM_DNS_IP_CONFIG_TYPE_DEFAULT, - device); - ip6_config = nm_device_get_ip6_config(device); - if (ip6_config) - _dns_manager_set_ip_config(priv->dns_manager, - NM_IP_CONFIG_CAST(ip6_config), - NM_DNS_IP_CONFIG_TYPE_DEFAULT, - device); - - update_routing_and_dns(self, FALSE, device); - - nm_dns_manager_end_updates(priv->dns_manager, __func__); break; case NM_DEVICE_STATE_UNMANAGED: case NM_DEVICE_STATE_UNAVAILABLE: @@ -2135,58 +2094,51 @@ device_state_changed(NMDevice * device, } static void -device_ip_config_changed(NMDevice * device, - NMIPConfig *new_config, - NMIPConfig *old_config, - gpointer user_data) +device_l3cd_changed(NMDevice * device, + const NML3ConfigData *l3cd_old, + const NML3ConfigData *l3cd_new, + gpointer user_data) { NMPolicyPrivate *priv = user_data; NMPolicy * self = _PRIV_TO_SELF(priv); - int addr_family; NMDeviceState state; - nm_assert(new_config || old_config); - nm_assert(!new_config || NM_IS_IP_CONFIG(new_config)); - nm_assert(!old_config || NM_IS_IP_CONFIG(old_config)); - - if (new_config) { - addr_family = nm_ip_config_get_addr_family(new_config); - nm_assert(!old_config || addr_family == nm_ip_config_get_addr_family(old_config)); - } else - addr_family = nm_ip_config_get_addr_family(old_config); + nm_assert(!l3cd_new || NM_IS_L3_CONFIG_DATA(l3cd_new)); + nm_assert(!l3cd_old || NM_IS_L3_CONFIG_DATA(l3cd_old)); nm_dns_manager_begin_updates(priv->dns_manager, __func__); /* We catch already all the IP events registering on the device state changes but - * the ones where the IP changes but the device state keep stable (i.e., activated): + * the ones where the IP changes with a stable state (i.e., activated): * ignore IP config changes but when the device is in activated state. * Prevents unnecessary changes to DNS information. + * FIXME(l3cfg): check why ^^^ this is needed and implement it. Note that + * this function is not always called when the device becomes ACTIVATED. + * Previously, we would also update the DNS manager's IP config in + * device_state_change(ACTIVATED). There we would also special-case + * pseudo-VPNs like wireguard. I don't see the code where this is handled + * now. */ state = nm_device_get_state(device); - if (state > NM_DEVICE_STATE_IP_CONFIG && state <= NM_DEVICE_STATE_ACTIVATED) { - if (old_config != new_config) { - if (new_config) - _dns_manager_set_ip_config(priv->dns_manager, - new_config, - NM_DNS_IP_CONFIG_TYPE_DEFAULT, - device); - if (old_config) - nm_dns_manager_set_ip_config(priv->dns_manager, - old_config, - NM_DNS_IP_CONFIG_TYPE_REMOVED); - } - update_ip_dns(self, addr_family, device); - if (addr_family == AF_INET) - update_ip4_routing(self, TRUE); - else - update_ip6_routing(self, TRUE); - update_system_hostname(self, addr_family == AF_INET ? "ip4 conf" : "ip6 conf"); + if (state > NM_DEVICE_STATE_DISCONNECTED && state < NM_DEVICE_STATE_DEACTIVATING) { + nm_dns_manager_set_ip_config(priv->dns_manager, + AF_UNSPEC, + device, + l3cd_new, + NM_DNS_IP_CONFIG_TYPE_DEFAULT, + TRUE); + update_ip_dns(self, AF_INET, device); + update_ip_dns(self, AF_INET6, device); + update_ip4_routing(self, TRUE); + update_ip6_routing(self, TRUE); + update_system_hostname(self, "ip conf"); } else { - /* Old configs get removed immediately */ - if (old_config) - nm_dns_manager_set_ip_config(priv->dns_manager, - old_config, - NM_DNS_IP_CONFIG_TYPE_REMOVED); + nm_dns_manager_set_ip_config(priv->dns_manager, + AF_UNSPEC, + device, + l3cd_old, + NM_DNS_IP_CONFIG_TYPE_REMOVED, + TRUE); } nm_dns_manager_end_updates(priv->dns_manager, __func__); @@ -2227,14 +2179,7 @@ devices_list_register(NMPolicy *self, NMDevice *device) /* Connect state-changed with _after, so that the handler is invoked after other handlers. */ g_signal_connect_after(device, NM_DEVICE_STATE_CHANGED, G_CALLBACK(device_state_changed), priv); - g_signal_connect(device, - NM_DEVICE_IP4_CONFIG_CHANGED, - G_CALLBACK(device_ip_config_changed), - priv); - g_signal_connect(device, - NM_DEVICE_IP6_CONFIG_CHANGED, - G_CALLBACK(device_ip_config_changed), - priv); + g_signal_connect(device, NM_DEVICE_L3CD_CHANGED, G_CALLBACK(device_l3cd_changed), priv); g_signal_connect(device, NM_DEVICE_IP6_PREFIX_DELEGATED, G_CALLBACK(device_ip6_prefix_delegated), @@ -2296,51 +2241,26 @@ device_removed(NMManager *manager, NMDevice *device, gpointer user_data) /*****************************************************************************/ static void -vpn_connection_activated(NMPolicy *self, NMVpnConnection *vpn) +vpn_connection_update_dns(NMPolicy *self, NMVpnConnection *vpn, gboolean remove) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self); - NMIP4Config * ip4_config; - NMIP6Config * ip6_config; + int IS_IPv4; nm_dns_manager_begin_updates(priv->dns_manager, __func__); - ip4_config = nm_vpn_connection_get_ip4_config(vpn); - if (ip4_config) + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + int addr_family = IS_IPv4 ? AF_INET : AF_INET6; + const NML3ConfigData *l3cd; + + l3cd = nm_vpn_connection_get_l3cd(vpn); nm_dns_manager_set_ip_config(priv->dns_manager, - NM_IP_CONFIG_CAST(ip4_config), - NM_DNS_IP_CONFIG_TYPE_VPN); - - ip6_config = nm_vpn_connection_get_ip6_config(vpn); - if (ip6_config) - nm_dns_manager_set_ip_config(priv->dns_manager, - NM_IP_CONFIG_CAST(ip6_config), - NM_DNS_IP_CONFIG_TYPE_VPN); - - update_routing_and_dns(self, TRUE, NULL); - - nm_dns_manager_end_updates(priv->dns_manager, __func__); -} - -static void -vpn_connection_deactivated(NMPolicy *self, NMVpnConnection *vpn) -{ - NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self); - NMIP4Config * ip4_config; - NMIP6Config * ip6_config; - - nm_dns_manager_begin_updates(priv->dns_manager, __func__); - - ip4_config = nm_vpn_connection_get_ip4_config(vpn); - if (ip4_config) - nm_dns_manager_set_ip_config(priv->dns_manager, - NM_IP_CONFIG_CAST(ip4_config), - NM_DNS_IP_CONFIG_TYPE_REMOVED); - - ip6_config = nm_vpn_connection_get_ip6_config(vpn); - if (ip6_config) - nm_dns_manager_set_ip_config(priv->dns_manager, - NM_IP_CONFIG_CAST(ip6_config), - NM_DNS_IP_CONFIG_TYPE_REMOVED); + addr_family, + vpn, + l3cd, + remove ? NM_DNS_IP_CONFIG_TYPE_REMOVED + : NM_DNS_IP_CONFIG_TYPE_VPN, + TRUE); + } update_routing_and_dns(self, TRUE, NULL); @@ -2354,13 +2274,14 @@ vpn_connection_state_changed(NMVpnConnection * vpn, NMActiveConnectionStateReason reason, NMPolicy * self) { + /* FIXME(l3cfg): we need to track changes to nm_vpn_connection_get_l3cd(). */ if (new_state == NM_VPN_CONNECTION_STATE_ACTIVATED) - vpn_connection_activated(self, vpn); + vpn_connection_update_dns(self, vpn, FALSE); else if (new_state >= NM_VPN_CONNECTION_STATE_FAILED) { /* Only clean up IP/DNS if the connection ever got past IP_CONFIG */ if (old_state >= NM_VPN_CONNECTION_STATE_IP_CONFIG_GET && old_state <= NM_VPN_CONNECTION_STATE_ACTIVATED) - vpn_connection_deactivated(self, vpn); + vpn_connection_update_dns(self, vpn, TRUE); } } diff --git a/src/core/nm-proxy-config.c b/src/core/nm-proxy-config.c deleted file mode 100644 index 49156dfae0..0000000000 --- a/src/core/nm-proxy-config.c +++ /dev/null @@ -1,172 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Atul Anand . - */ - -#include "src/core/nm-default-daemon.h" - -#include "nm-proxy-config.h" - -#include - -#include "libnm-core-intern/nm-core-internal.h" - -/*****************************************************************************/ - -typedef struct { - NMProxyConfigMethod method; - gboolean browser_only; - char * pac_url; - char * pac_script; -} NMProxyConfigPrivate; - -struct _NMProxyConfig { - GObject parent; - NMProxyConfigPrivate _priv; -}; - -struct _NMProxyConfigClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE(NMProxyConfig, nm_proxy_config, G_TYPE_OBJECT) - -#define NM_PROXY_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMProxyConfig, NM_IS_PROXY_CONFIG) - -/*****************************************************************************/ - -void -nm_proxy_config_set_method(NMProxyConfig *config, NMProxyConfigMethod method) -{ - NMProxyConfigPrivate *priv = NM_PROXY_CONFIG_GET_PRIVATE(config); - - priv->method = method; -} - -NMProxyConfigMethod -nm_proxy_config_get_method(const NMProxyConfig *config) -{ - const NMProxyConfigPrivate *priv = NM_PROXY_CONFIG_GET_PRIVATE(config); - - return priv->method; -} - -void -nm_proxy_config_merge_setting(NMProxyConfig *config, NMSettingProxy *setting) -{ - const char * tmp = NULL; - NMProxyConfigPrivate *priv; - NMSettingProxyMethod method; - - if (!setting) - return; - - g_return_if_fail(NM_IS_SETTING_PROXY(setting)); - - priv = NM_PROXY_CONFIG_GET_PRIVATE(config); - - nm_clear_g_free(&priv->pac_script); - - method = nm_setting_proxy_get_method(setting); - switch (method) { - case NM_SETTING_PROXY_METHOD_AUTO: - priv->method = NM_PROXY_CONFIG_METHOD_AUTO; - - /* Free DHCP Obtained PAC Url (i.e Option 252) - * only when libnm overrides it. - */ - tmp = nm_setting_proxy_get_pac_url(setting); - if (tmp) { - g_free(priv->pac_url); - priv->pac_url = g_strdup(tmp); - } - - tmp = nm_setting_proxy_get_pac_script(setting); - priv->pac_script = g_strdup(tmp); - - break; - case NM_SETTING_PROXY_METHOD_NONE: - priv->method = NM_PROXY_CONFIG_METHOD_NONE; - break; - } - - priv->browser_only = nm_setting_proxy_get_browser_only(setting); -} - -gboolean -nm_proxy_config_get_browser_only(const NMProxyConfig *config) -{ - const NMProxyConfigPrivate *priv = NM_PROXY_CONFIG_GET_PRIVATE(config); - - return priv->browser_only; -} - -void -nm_proxy_config_set_pac_url(NMProxyConfig *config, const char *url) -{ - NMProxyConfigPrivate *priv = NM_PROXY_CONFIG_GET_PRIVATE(config); - - g_free(priv->pac_url); - priv->pac_url = g_strdup(url); -} - -const char * -nm_proxy_config_get_pac_url(const NMProxyConfig *config) -{ - const NMProxyConfigPrivate *priv = NM_PROXY_CONFIG_GET_PRIVATE(config); - - return priv->pac_url; -} - -void -nm_proxy_config_set_pac_script(NMProxyConfig *config, const char *script) -{ - NMProxyConfigPrivate *priv = NM_PROXY_CONFIG_GET_PRIVATE(config); - - g_free(priv->pac_script); - priv->pac_script = g_strdup(script); -} - -const char * -nm_proxy_config_get_pac_script(const NMProxyConfig *config) -{ - const NMProxyConfigPrivate *priv = NM_PROXY_CONFIG_GET_PRIVATE(config); - - return priv->pac_script; -} - -/*****************************************************************************/ - -static void -nm_proxy_config_init(NMProxyConfig *config) -{ - NMProxyConfigPrivate *priv = NM_PROXY_CONFIG_GET_PRIVATE(config); - - priv->method = NM_PROXY_CONFIG_METHOD_NONE; -} - -NMProxyConfig * -nm_proxy_config_new(void) -{ - return g_object_new(NM_TYPE_PROXY_CONFIG, NULL); -} - -static void -finalize(GObject *object) -{ - NMProxyConfig * self = NM_PROXY_CONFIG(object); - NMProxyConfigPrivate *priv = NM_PROXY_CONFIG_GET_PRIVATE(self); - - g_free(priv->pac_url); - g_free(priv->pac_script); - - G_OBJECT_CLASS(nm_proxy_config_parent_class)->finalize(object); -} - -static void -nm_proxy_config_class_init(NMProxyConfigClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - - object_class->finalize = finalize; -} diff --git a/src/core/nm-proxy-config.h b/src/core/nm-proxy-config.h deleted file mode 100644 index 7b6241f849..0000000000 --- a/src/core/nm-proxy-config.h +++ /dev/null @@ -1,41 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Atul Anand . - */ - -#ifndef __NETWORKMANAGER_PROXY_CONFIG_H__ -#define __NETWORKMANAGER_PROXY_CONFIG_H__ - -#include "nm-setting-proxy.h" -#include "nm-l3-config-data.h" - -#define NM_TYPE_PROXY_CONFIG (nm_proxy_config_get_type()) -#define NM_PROXY_CONFIG(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PROXY_CONFIG, NMProxyConfig)) -#define NM_PROXY_CONFIG_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_PROXY_CONFIG, NMProxyConfigClass)) -#define NM_IS_PROXY_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_PROXY_CONFIG)) -#define NM_IS_PROXY_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_PROXY_CONFIG)) -#define NM_PROXY_CONFIG_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_PROXY_CONFIG, NMProxyConfigClass)) - -typedef struct _NMProxyConfigClass NMProxyConfigClass; - -GType nm_proxy_config_get_type(void); - -NMProxyConfig *nm_proxy_config_new(void); - -void nm_proxy_config_set_method(NMProxyConfig *config, NMProxyConfigMethod method); -NMProxyConfigMethod nm_proxy_config_get_method(const NMProxyConfig *config); - -void nm_proxy_config_merge_setting(NMProxyConfig *config, NMSettingProxy *setting); - -gboolean nm_proxy_config_get_browser_only(const NMProxyConfig *config); - -void nm_proxy_config_set_pac_url(NMProxyConfig *config, const char *url); -const char *nm_proxy_config_get_pac_url(const NMProxyConfig *config); - -void nm_proxy_config_set_pac_script(NMProxyConfig *config, const char *script); -const char *nm_proxy_config_get_pac_script(const NMProxyConfig *config); - -#endif /* __NETWORKMANAGER_PROXY_CONFIG_H__ */ diff --git a/src/core/nm-types.h b/src/core/nm-types.h index 294cf40936..76287e4dbf 100644 --- a/src/core/nm-types.h +++ b/src/core/nm-types.h @@ -25,10 +25,7 @@ typedef struct _NML3Cfg NML3Cfg; typedef struct _NML3ConfigData NML3ConfigData; typedef struct _NMDevice NMDevice; typedef struct _NMDhcpConfig NMDhcpConfig; -typedef struct _NMProxyConfig NMProxyConfig; typedef struct _NMIPConfig NMIPConfig; -typedef struct _NMIP4Config NMIP4Config; -typedef struct _NMIP6Config NMIP6Config; typedef struct _NMManager NMManager; typedef struct _NMNetns NMNetns; typedef struct _NMPolicy NMPolicy; diff --git a/src/core/platform/nm-fake-platform.c b/src/core/platform/nm-fake-platform.c index b773df9a43..870350c5ca 100644 --- a/src/core/platform/nm-fake-platform.c +++ b/src/core/platform/nm-fake-platform.c @@ -1118,8 +1118,6 @@ ip_route_add(NMPlatform * platform, (const NMPlatformObject *) route); r = NMP_OBJECT_CAST_IP_ROUTE(obj); - r->is_external = TRUE; - nm_platform_ip_route_normalize(addr_family, r); switch (addr_family) { diff --git a/src/core/platform/tests/test-route.c b/src/core/platform/tests/test-route.c index 4b1db7fad7..2b9b2f8f67 100644 --- a/src/core/platform/tests/test-route.c +++ b/src/core/platform/tests/test-route.c @@ -332,33 +332,30 @@ 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].is_external = TRUE; - 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[1].is_external = TRUE; - 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].is_external = TRUE; + 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); g_assert_cmpint(routes->len, ==, 3); nmtst_platform_ip4_routes_equal_aptr((const NMPObject *const *) routes->pdata, rts, @@ -492,33 +489,30 @@ test_ip6_route(void) /* Test route listing */ routes = nmtstp_ip6_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 = 128; - rts[0].ifindex = ifindex; - rts[0].gateway = in6addr_any; - rts[0].pref_src = in6addr_any; - rts[0].metric = metric; - rts[0].mss = mss; - rts[0].is_external = TRUE; - 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].pref_src = pref_src; - rts[1].metric = metric; - rts[1].mss = mss; - rts[1].is_external = TRUE; - rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[2].network = in6addr_any; - rts[2].plen = 0; - rts[2].ifindex = ifindex; - rts[2].gateway = gateway; - rts[2].pref_src = in6addr_any; - rts[2].metric = metric; - rts[2].mss = mss; - rts[2].is_external = TRUE; + rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); + rts[0].network = gateway; + rts[0].plen = 128; + rts[0].ifindex = ifindex; + rts[0].gateway = in6addr_any; + rts[0].pref_src = in6addr_any; + rts[0].metric = metric; + rts[0].mss = mss; + 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].pref_src = pref_src; + rts[1].metric = metric; + rts[1].mss = mss; + rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); + rts[2].network = in6addr_any; + rts[2].plen = 0; + rts[2].ifindex = ifindex; + rts[2].gateway = gateway; + rts[2].pref_src = in6addr_any; + rts[2].metric = metric; + rts[2].mss = mss; g_assert_cmpint(routes->len, ==, 3); nmtst_platform_ip6_routes_equal_aptr((const NMPObject *const *) routes->pdata, rts, @@ -715,7 +709,6 @@ test_ip4_route_options(gconstpointer test_data) for (i = 0; i < rts_n; i++) { rts_cmp[i] = rts_add[i]; nm_platform_ip_route_normalize(AF_INET, NM_PLATFORM_IP_ROUTE_CAST(&rts_cmp[i])); - rts_cmp[i].is_external = TRUE; } routes = nmtstp_ip4_route_get_all(NM_PLATFORM_GET, IFINDEX); @@ -887,7 +880,6 @@ test_ip6_route_options(gconstpointer test_data) for (i = 0; i < rts_n; i++) { rts_cmp[i] = rts_add[i]; nm_platform_ip_route_normalize(AF_INET6, NM_PLATFORM_IP_ROUTE_CAST(&rts_cmp[i])); - rts_cmp[i].is_external = TRUE; } routes = nmtstp_ip6_route_get_all(NM_PLATFORM_GET, IFINDEX); diff --git a/src/core/ppp/nm-ppp-manager-call.c b/src/core/ppp/nm-ppp-manager-call.c index 5e84f59610..8adeb8adbe 100644 --- a/src/core/ppp/nm-ppp-manager-call.c +++ b/src/core/ppp/nm-ppp-manager-call.c @@ -94,24 +94,6 @@ again: return ret; } -void -nm_ppp_manager_set_route_parameters(NMPPPManager *self, - guint32 ip4_route_table, - guint32 ip4_route_metric, - guint32 ip6_route_table, - guint32 ip6_route_metric) -{ - const NMPPPOps *ppp_ops = ppp_ops_get(); - - g_return_if_fail(ppp_ops); - - ppp_ops->set_route_parameters(self, - ip4_route_table, - ip4_route_metric, - ip6_route_table, - ip6_route_metric); -} - gboolean nm_ppp_manager_start(NMPPPManager *self, NMActRequest *req, diff --git a/src/core/ppp/nm-ppp-manager-call.h b/src/core/ppp/nm-ppp-manager-call.h index c831cf0d61..87d2f93f55 100644 --- a/src/core/ppp/nm-ppp-manager-call.h +++ b/src/core/ppp/nm-ppp-manager-call.h @@ -10,12 +10,6 @@ NMPPPManager *nm_ppp_manager_create(const char *iface, GError **error); -void nm_ppp_manager_set_route_parameters(NMPPPManager *ppp_manager, - guint32 ip4_route_table, - guint32 ip4_route_metric, - guint32 ip6_route_table, - guint32 ip6_route_metric); - gboolean nm_ppp_manager_start(NMPPPManager *self, NMActRequest *req, const char * ppp_name, diff --git a/src/core/ppp/nm-ppp-manager.c b/src/core/ppp/nm-ppp-manager.c index e6790f2260..c347978ff0 100644 --- a/src/core/ppp/nm-ppp-manager.c +++ b/src/core/ppp/nm-ppp-manager.c @@ -32,8 +32,7 @@ #include "libnm-platform/nm-platform-utils.h" #include "libnm-core-intern/nm-core-internal.h" #include "nm-act-request.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" +#include "nm-l3-config-data.h" #include "nm-dbus-object.h" #include "nm-pppd-plugin.h" @@ -46,6 +45,26 @@ static NM_CACHED_QUARK_FCN("ppp-manager-secret-tries", ppp_manager_secret_tries_ /*****************************************************************************/ +/* FIXME(l3cfg:ppp): I think NMPPPManager's API should be improved to be easier + * usable (by the higher layers). That means to make the class more complex, to + * provide a simpler API. + * + * For example: + * + * - NM_PPP_MANAGER_SIGNAL_STATE_CHANGED just gets re-emitted when we receive + * the D-Bus call from the plugin. The emitted state is like NM_PPP_STATUS_SERIALCONN, + * but none of the users cares about this (what would it mean anyway)? The + * class should itself consume the state, and emit something more consumable + * (like: interface is ready (with ifindex), IPvX configuration done (with l3cd). + * + * - currently signals can be emitted in any order, and it's not clear which + * signals we can expect. For example, when we activate a device, we may want to wait + * for IPv4 and IPv6 configuration, but it's not clear whether this configuration + * is still to be received or whether we can stop waiting. + **/ + +/*****************************************************************************/ + #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_CLASS(klass) \ @@ -62,8 +81,7 @@ GType nm_ppp_manager_get_type(void); enum { STATE_CHANGED, IFINDEX_SET, - IP4_CONFIG, - IP6_CONFIG, + NEW_CONFIG, STATS, LAST_SIGNAL @@ -91,11 +109,6 @@ typedef struct { /* Monitoring */ int monitor_fd; guint monitor_id; - - guint32 ip4_route_table; - guint32 ip4_route_metric; - guint32 ip6_route_table; - guint32 ip6_route_metric; } NMPPPManagerPrivate; struct _NMPPPManager { @@ -131,30 +144,19 @@ static void _ppp_manager_stop_cancel(NMPPPManagerStopHandle *handle); /*****************************************************************************/ static void -_ppp_manager_set_route_parameters(NMPPPManager *self, - guint32 ip4_route_table, - guint32 ip4_route_metric, - guint32 ip6_route_table, - guint32 ip6_route_metric) +_emit_signal_new_config(NMPPPManager * self, + int addr_family, + const NML3ConfigData * l3cd, + const NMUtilsIPv6IfaceId *iid) { - NMPPPManagerPrivate *priv; + nm_assert(NM_IS_PPP_MANAGER(self)); + nm_assert_addr_family(addr_family); + nm_assert(NM_IS_L3_CONFIG_DATA(l3cd)); + nm_assert((!!iid) == (addr_family == AF_INET6)); - g_return_if_fail(NM_IS_PPP_MANAGER(self)); + nm_l3_config_data_seal(l3cd); - priv = NM_PPP_MANAGER_GET_PRIVATE(self); - if (priv->ip4_route_table != ip4_route_table || priv->ip4_route_metric != ip4_route_metric - || priv->ip6_route_table != ip6_route_table || priv->ip6_route_metric != ip6_route_metric) { - priv->ip4_route_table = ip4_route_table; - priv->ip4_route_metric = ip4_route_metric; - priv->ip6_route_table = ip6_route_table; - priv->ip6_route_metric = ip6_route_metric; - - _LOGT("route-parameters: table-v4: %u, metric-v4: %u, table-v6: %u, metric-v6: %u", - priv->ip4_route_table, - priv->ip4_route_metric, - priv->ip6_route_table, - priv->ip6_route_metric); - } + g_signal_emit(self, signals[NEW_CONFIG], 0, addr_family, l3cd, iid); } /*****************************************************************************/ @@ -419,10 +421,16 @@ impl_ppp_manager_set_state(NMDBusObject * obj, GVariant * parameters) { NMPPPManager *self = NM_PPP_MANAGER(obj); - guint32 state; + guint32 ppp_state; - g_variant_get(parameters, "(u)", &state); - g_signal_emit(self, signals[STATE_CHANGED], 0, (guint) state); + g_variant_get(parameters, "(u)", &ppp_state); + + if (ppp_state >= NM_PPP_STATUS_INTERN_DEAD) { + /* we don't expect an intern state to be reported by the plugin. */ + ppp_state = NM_PPP_STATUS_UNKNOWN; + } + + g_signal_emit(self, signals[STATE_CHANGED], 0, (guint) ppp_state); g_dbus_method_invocation_return_value(invocation, NULL); } @@ -435,9 +443,10 @@ impl_ppp_manager_set_ifindex(NMDBusObject * obj, GDBusMethodInvocation * invocation, GVariant * parameters) { - NMPPPManager * self = NM_PPP_MANAGER(obj); - NMPPPManagerPrivate * priv = NM_PPP_MANAGER_GET_PRIVATE(self); - const NMPlatformLink *plink = NULL; + NMPPPManager * self = NM_PPP_MANAGER(obj); + gs_unref_object NMPPPManager *self_keep_alive = NULL; + NMPPPManagerPrivate * priv = NM_PPP_MANAGER_GET_PRIVATE(self); + const NMPlatformLink * plink = NULL; nm_auto_nmpobj const NMPObject *obj_keep_alive = NULL; gint32 ifindex; @@ -456,6 +465,10 @@ impl_ppp_manager_set_ifindex(NMDBusObject * obj, if (ifindex > 0) { plink = nm_platform_link_get(NM_PLATFORM_GET, ifindex); if (!plink) { + /* processing events has side-effects. We need to keep self alive + * during that.*/ + self_keep_alive = g_object_ref(self); + nm_platform_process_events(NM_PLATFORM_GET); plink = nm_platform_link_get(NM_PLATFORM_GET, ifindex); } @@ -510,12 +523,12 @@ impl_ppp_manager_set_ip4_config(NMDBusObject * obj, GDBusMethodInvocation * invocation, GVariant * parameters) { - NMPPPManager * self = NM_PPP_MANAGER(obj); - NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE(self); - gs_unref_object NMIP4Config *config = NULL; - NMPlatformIP4Address address; - guint32 u32, mtu; - GVariantIter * iter; + NMPPPManager * self = NM_PPP_MANAGER(obj); + NMPPPManagerPrivate * priv = NM_PPP_MANAGER_GET_PRIVATE(self); + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + NMPlatformIP4Address address; + guint32 u32, mtu; + GVariantIter * iter; gs_unref_variant GVariant *config_dict = NULL; _LOGI("(IPv4 Config Get) reply received."); @@ -527,37 +540,41 @@ impl_ppp_manager_set_ip4_config(NMDBusObject * obj, if (!set_ip_config_common(self, config_dict, &mtu)) goto out; - config = nm_ip4_config_new(nm_platform_get_multi_idx(NM_PLATFORM_GET), priv->ifindex); + l3cd = nm_l3_config_data_new(nm_platform_get_multi_idx(NM_PLATFORM_GET), + priv->ifindex, + NM_IP_CONFIG_SOURCE_PPP); - if (mtu) - nm_ip4_config_set_mtu(config, mtu, NM_IP_CONFIG_SOURCE_PPP); + nm_l3_config_data_set_mtu(l3cd, mtu); - memset(&address, 0, sizeof(address)); - address.plen = 32; + address = (NMPlatformIP4Address){ + .plen = 32, + }; if (g_variant_lookup(config_dict, NM_PPP_IP4_CONFIG_ADDRESS, "u", &u32)) address.address = u32; + if (g_variant_lookup(config_dict, NM_PPP_IP4_CONFIG_PREFIX, "u", &u32)) + address.plen = u32; + if (g_variant_lookup(config_dict, NM_PPP_IP4_CONFIG_GATEWAY, "u", &u32)) { const NMPlatformIP4Route r = { .ifindex = priv->ifindex, .rt_source = NM_IP_CONFIG_SOURCE_PPP, .gateway = u32, - .table_coerced = nm_platform_route_table_coerce(priv->ip4_route_table), - .metric = priv->ip4_route_metric, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, + .metric = 0, }; - nm_ip4_config_add_route(config, &r, NULL); + nm_l3_config_data_add_route_4(l3cd, &r); address.peer_address = u32; } else address.peer_address = address.address; - if (g_variant_lookup(config_dict, NM_PPP_IP4_CONFIG_PREFIX, "u", &u32)) - address.plen = u32; - - if (address.address && address.plen && address.plen <= 32) { + if (address.address && address.plen > 0 && address.plen <= 32) { address.addr_source = NM_IP_CONFIG_SOURCE_PPP; - nm_ip4_config_add_address(config, &address); + nm_l3_config_data_add_address_4(l3cd, &address); } else { _LOGE("invalid IPv4 address received!"); goto out; @@ -565,18 +582,17 @@ 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_ip4_config_add_nameserver(config, u32); + nm_l3_config_data_add_nameserver(l3cd, AF_INET, &u32); g_variant_iter_free(iter); } if (g_variant_lookup(config_dict, NM_PPP_IP4_CONFIG_WINS, "au", &iter)) { while (g_variant_iter_next(iter, "u", &u32)) - nm_ip4_config_add_wins(config, u32); + nm_l3_config_data_add_wins(l3cd, u32); g_variant_iter_free(iter); } - /* Push the IP4 config up to the device */ - g_signal_emit(self, signals[IP4_CONFIG], 0, config); + _emit_signal_new_config(self, AF_INET, l3cd, NULL); out: g_dbus_method_invocation_return_value(invocation, NULL); @@ -620,14 +636,14 @@ impl_ppp_manager_set_ip6_config(NMDBusObject * obj, GDBusMethodInvocation * invocation, GVariant * parameters) { - NMPPPManager * self = NM_PPP_MANAGER(obj); - NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE(self); - gs_unref_object NMIP6Config *config = NULL; - NMPlatformIP6Address addr; - struct in6_addr a; - NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT; - gboolean has_peer = FALSE; - gs_unref_variant GVariant *config_dict = NULL; + NMPPPManager * self = NM_PPP_MANAGER(obj); + NMPPPManagerPrivate * priv = NM_PPP_MANAGER_GET_PRIVATE(self); + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + NMPlatformIP6Address address; + struct in6_addr a; + NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT; + gboolean has_peer = FALSE; + gs_unref_variant GVariant *config_dict = NULL; _LOGI("(IPv6 Config Get) reply received."); @@ -638,32 +654,37 @@ impl_ppp_manager_set_ip6_config(NMDBusObject * obj, if (!set_ip_config_common(self, config_dict, NULL)) goto out; - config = nm_ip6_config_new(nm_platform_get_multi_idx(NM_PLATFORM_GET), priv->ifindex); + l3cd = nm_l3_config_data_new(nm_platform_get_multi_idx(NM_PLATFORM_GET), + priv->ifindex, + NM_IP_CONFIG_SOURCE_PPP); - memset(&addr, 0, sizeof(addr)); - addr.plen = 64; + address = (NMPlatformIP6Address){ + .plen = 64, + .addr_source = NM_IP_CONFIG_SOURCE_PPP, + }; if (iid_value_to_ll6_addr(config_dict, NM_PPP_IP6_CONFIG_PEER_IID, &a, NULL)) { const NMPlatformIP6Route r = { .ifindex = priv->ifindex, .rt_source = NM_IP_CONFIG_SOURCE_PPP, .gateway = a, - .table_coerced = nm_platform_route_table_coerce(priv->ip6_route_table), - .metric = priv->ip6_route_metric, + .table_any = TRUE, + .table_coerced = 0, + .metric_any = TRUE, + .metric = 0, }; - nm_ip6_config_add_route(config, &r, NULL); - addr.peer_address = a; - has_peer = TRUE; + nm_l3_config_data_add_route_6(l3cd, &r); + address.peer_address = a; + has_peer = TRUE; } - if (iid_value_to_ll6_addr(config_dict, NM_PPP_IP6_CONFIG_OUR_IID, &addr.address, &iid)) { + if (iid_value_to_ll6_addr(config_dict, NM_PPP_IP6_CONFIG_OUR_IID, &address.address, &iid)) { if (!has_peer) - addr.peer_address = addr.address; - nm_ip6_config_add_address(config, &addr); + address.peer_address = address.address; + nm_l3_config_data_add_address_6(l3cd, &address); - /* Push the IPv6 config and interface identifier up to the device */ - g_signal_emit(self, signals[IP6_CONFIG], 0, &iid, config); + _emit_signal_new_config(self, AF_INET6, l3cd, &iid); } else _LOGE("invalid IPv6 address received!"); @@ -726,7 +747,7 @@ ppp_watch_cb(GPid pid, int status, gpointer user_data) priv->pid = 0; priv->ppp_watch_id = 0; _ppp_cleanup(self); - g_signal_emit(self, signals[STATE_CHANGED], 0, (guint) NM_PPP_STATUS_DEAD); + g_signal_emit(self, signals[STATE_CHANGED], 0, (guint) NM_PPP_STATUS_INTERN_DEAD); } static gboolean @@ -734,10 +755,16 @@ pppd_timed_out(gpointer data) { NMPPPManager *self = NM_PPP_MANAGER(data); + /* FIXME(l3cfg): we should not use NMPPPManager directly, instead use + * NMPppMgr. That one already schedules a (better) timer. We don't need + * a timeout here anymore. + * + * At this moment, NMPPPManager is still used by NMModem. Once that changes, + * this timer needs to go. */ _LOGW("pppd timed out or didn't initialize our dbus module"); _ppp_manager_stop(self, NULL, NULL, NULL); - g_signal_emit(self, signals[STATE_CHANGED], 0, (guint) NM_PPP_STATUS_DEAD); + g_signal_emit(self, signals[STATE_CHANGED], 0, (guint) NM_PPP_STATUS_INTERN_DEAD); return FALSE; } @@ -975,9 +1002,8 @@ _ppp_manager_start(NMPPPManager *self, gs_unref_ptrarray GPtrArray *ppp_cmd = NULL; gs_free char * cmd_str = NULL; struct stat st; - const char * ip6_method, *ip4_method; - gboolean ip6_enabled = FALSE; - gboolean ip4_enabled = FALSE; + gboolean ip6_enabled; + gboolean ip4_enabled; g_return_val_if_fail(NM_IS_PPP_MANAGER(self), FALSE); g_return_val_if_fail(NM_IS_ACT_REQUEST(req), FALSE); @@ -1022,11 +1048,7 @@ _ppp_manager_start(NMPPPManager *self, adsl_setting = (NMSettingAdsl *) nm_connection_get_setting(connection, NM_TYPE_SETTING_ADSL); - /* Figure out what address methods should be enabled */ - ip4_method = nm_utils_get_ip_config_method(connection, AF_INET); - ip4_enabled = nm_streq(ip4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO); - ip6_method = nm_utils_get_ip_config_method(connection, AF_INET6); - ip6_enabled = nm_streq(ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO); + nm_utils_ppp_ip_methods_enabled(connection, &ip4_enabled, &ip6_enabled); ppp_cmd = create_pppd_cmd_line(self, s_ppp, @@ -1059,9 +1081,10 @@ _ppp_manager_start(NMPPPManager *self, _LOGI("pppd started with pid %lld", (long long) priv->pid); - priv->ppp_watch_id = g_child_watch_add(priv->pid, (GChildWatchFunc) ppp_watch_cb, self); - priv->ppp_timeout_handler = g_timeout_add_seconds(timeout_secs, pppd_timed_out, self); - priv->act_req = g_object_ref(req); + priv->ppp_watch_id = g_child_watch_add(priv->pid, (GChildWatchFunc) ppp_watch_cb, self); + if (timeout_secs > 0) + priv->ppp_timeout_handler = g_timeout_add_seconds(timeout_secs, pppd_timed_out, self); + priv->act_req = g_object_ref(req); return TRUE; fail: @@ -1290,12 +1313,8 @@ nm_ppp_manager_init(NMPPPManager *self) { NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE(self); - priv->ifindex = -1; - priv->monitor_fd = -1; - priv->ip4_route_table = RT_TABLE_MAIN; - priv->ip4_route_metric = 460; - priv->ip6_route_table = RT_TABLE_MAIN; - priv->ip6_route_metric = 460; + priv->ifindex = -1; + priv->monitor_fd = -1; } static NMPPPManager * @@ -1400,7 +1419,7 @@ nm_ppp_manager_class_init(NMPPPManagerClass *manager_class) NULL, G_TYPE_NONE, 1, - G_TYPE_UINT); + G_TYPE_UINT /* ppp_state */); signals[IFINDEX_SET] = g_signal_new(NM_PPP_MANAGER_SIGNAL_IFINDEX_SET, G_OBJECT_CLASS_TYPE(object_class), @@ -1411,10 +1430,10 @@ nm_ppp_manager_class_init(NMPPPManagerClass *manager_class) NULL, G_TYPE_NONE, 2, - G_TYPE_INT, - G_TYPE_STRING); + G_TYPE_INT /* ifindex */, + G_TYPE_STRING /* ifname */); - signals[IP4_CONFIG] = g_signal_new(NM_PPP_MANAGER_SIGNAL_IP4_CONFIG, + signals[NEW_CONFIG] = g_signal_new(NM_PPP_MANAGER_SIGNAL_NEW_CONFIG, G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_FIRST, 0, @@ -1422,20 +1441,10 @@ nm_ppp_manager_class_init(NMPPPManagerClass *manager_class) NULL, NULL, G_TYPE_NONE, - 1, - G_TYPE_OBJECT); - - signals[IP6_CONFIG] = g_signal_new(NM_PPP_MANAGER_SIGNAL_IP6_CONFIG, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 2, - G_TYPE_POINTER, - G_TYPE_OBJECT); + 3, + G_TYPE_INT, /* addr_family */ + G_TYPE_POINTER, /* (const NML3ConfigData *) */ + G_TYPE_POINTER); /* (const NMUtilsIPv6IfaceId *) */ signals[STATS] = g_signal_new(NM_PPP_MANAGER_SIGNAL_STATS, G_OBJECT_CLASS_TYPE(object_class), @@ -1451,9 +1460,8 @@ nm_ppp_manager_class_init(NMPPPManagerClass *manager_class) } const NMPPPOps ppp_ops = { - .create = _ppp_manager_new, - .set_route_parameters = _ppp_manager_set_route_parameters, - .start = _ppp_manager_start, - .stop = _ppp_manager_stop, - .stop_cancel = _ppp_manager_stop_cancel, + .create = _ppp_manager_new, + .start = _ppp_manager_start, + .stop = _ppp_manager_stop, + .stop_cancel = _ppp_manager_stop_cancel, }; diff --git a/src/core/ppp/nm-ppp-manager.h b/src/core/ppp/nm-ppp-manager.h index c41dda2074..9cf465517c 100644 --- a/src/core/ppp/nm-ppp-manager.h +++ b/src/core/ppp/nm-ppp-manager.h @@ -11,8 +11,7 @@ #define NM_PPP_MANAGER_SIGNAL_STATE_CHANGED "state-changed" #define NM_PPP_MANAGER_SIGNAL_IFINDEX_SET "ifindex-set" -#define NM_PPP_MANAGER_SIGNAL_IP4_CONFIG "ip4-config" -#define NM_PPP_MANAGER_SIGNAL_IP6_CONFIG "ip6-config" +#define NM_PPP_MANAGER_SIGNAL_NEW_CONFIG "new-config" #define NM_PPP_MANAGER_SIGNAL_STATS "stats" typedef struct _NMPPPManager NMPPPManager; diff --git a/src/core/ppp/nm-ppp-mgr.c b/src/core/ppp/nm-ppp-mgr.c new file mode 100644 index 0000000000..82e511e995 --- /dev/null +++ b/src/core/ppp/nm-ppp-mgr.c @@ -0,0 +1,620 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "src/core/nm-default-daemon.h" + +#include "nm-ppp-mgr.h" + +#include + +#include "NetworkManagerUtils.h" +#include "devices/nm-device-utils.h" +#include "nm-act-request.h" +#include "nm-netns.h" +#include "nm-ppp-manager-call.h" +#include "nm-ppp-status.h" + +/*****************************************************************************/ + +struct _NMPppMgr { + NMPppMgrConfig config; + NMPPPManager * ppp_manager; + GSource * idle_start; + GSource * connect_timeout_source; + union { + struct { + NMPppMgrIPData ip_data_6; + NMPppMgrIPData ip_data_4; + }; + NMPppMgrIPData ip_data_x[2]; + }; + int ifindex; + NMPppMgrStatsData stats_data; + NMPppMgrState state; + NMUtilsIPv6IfaceId ipv6_iid; + bool ppp_started : 1; +}; + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_PPP +#define _NMLOG_PREFIX_NAME "ppp-mgr" +#define _NMLOG(level, ...) \ + __NMLOG_DEFAULT_WITH_ADDR(level, _NMLOG_DOMAIN, _NMLOG_PREFIX_NAME, __VA_ARGS__) + +/*****************************************************************************/ + +NM_UTILS_LOOKUP_STR_DEFINE( + nm_ppp_mgr_state_to_string, + NMPppMgrState, + NM_UTILS_LOOKUP_DEFAULT_WARN("???"), + NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_STATE_STARTING, "starting"), + NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX, "waiting-for-ifindex"), + NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_STATE_HAVE_IFINDEX, "have-ifindex"), + NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_STATE_HAVE_IP_CONFIG, "have-ip-config"), + NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_STATE_FAILED_TO_START, "failed-to-start"), + NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_STATE_FAILED_TO_IFINDEX, "failed-to-ifindex"), + NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_STATE_FAILED, "failed")); + +NM_UTILS_LOOKUP_STR_DEFINE(nm_ppp_mgr_callback_type_to_string, + NMPppMgrCallbackType, + NM_UTILS_LOOKUP_DEFAULT_WARN("???"), + NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED, + "state-changed"), + NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_CALLBACK_TYPE_STATS_CHANGED, + "stats-changed")); + +/*****************************************************************************/ + +#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))) + +/*****************************************************************************/ + +gboolean +_nm_assert_is_ppp_mgr(const NMPppMgr *self) +{ + nm_assert(G_IS_OBJECT(self->ppp_manager)); + nm_assert(NM_IS_NETNS(self->config.netns)); + nm_assert(self->config.parent_iface); + + return TRUE; +} + +/*****************************************************************************/ + +static void +_callback_emit_with_data(NMPppMgr *self, const NMPppMgrCallbackData *callback_data) +{ + char sbuf_int[30]; + + switch (callback_data->callback_type) { + case NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED: + _LOGT("emit signal: %s, state=%s%s%s%s%s, reason=%s", + nm_ppp_mgr_callback_type_to_string(callback_data->callback_type), + nm_ppp_mgr_state_to_string(callback_data->data.state), + NM_PRINT_FMT_QUOTED2(callback_data->data.state != callback_data->data.old_state, + ", old-state=", + nm_ppp_mgr_state_to_string(callback_data->data.old_state), + ""), + NM_PRINT_FMT_QUOTED2(callback_data->data.ifindex > 0, + ", ifindex=", + nm_sprintf_buf(sbuf_int, "%d", callback_data->data.ifindex), + ""), + callback_data->data.reason_msg); + break; + case NM_PPP_MGR_CALLBACK_TYPE_STATS_CHANGED: + /* This signal might be emitted every 5 seconds. It's too verbose for logging. + * Be silent. */ + break; + default: + nm_assert_not_reached(); + } + + self->config.callback(self, callback_data, self->config.user_data); +} + +static void +_callback_emit_state_change(NMPppMgr * self, + NMPppMgrCallbackType callback_type, + NMPppMgrState old_state, + gboolean ip_changed_4, + gboolean ip_changed_6, + NMDeviceStateReason reason, + const char * reason_msg) +{ + const NMPppMgrCallbackData callback_data = { + .callback_type = callback_type, + .data = + { + .old_state = old_state, + .state = self->state, + .ifindex = self->ifindex, + .reason = reason, + .reason_msg = reason_msg, + .ip_data_4 = &self->ip_data_4, + .ip_data_6 = &self->ip_data_6, + .stats_data = &self->stats_data, + .ip_changed_4 = ip_changed_4, + .ip_changed_6 = ip_changed_6, + }, + }; + + _callback_emit_with_data(self, &callback_data); +} + +/*****************************************************************************/ + +NMPppMgrState +nm_ppp_mgr_get_state(const NMPppMgr *self) +{ + g_return_val_if_fail(NM_IS_PPP_MGR(self), NM_PPP_MGR_STATE_FAILED); + + return self->state; +} + +int +nm_ppp_mgr_get_ifindex(const NMPppMgr *self) +{ + g_return_val_if_fail(NM_IS_PPP_MGR(self), 0); + + return self->ifindex; +} + +const NMPppMgrIPData * +nm_ppp_mgr_get_ip_data(const NMPppMgr *self, int addr_family) +{ + const int IS_IPv4 = NM_IS_IPv4(addr_family); + + g_return_val_if_fail(NM_IS_PPP_MGR(self), NULL); + + return &self->ip_data_x[IS_IPv4]; +} + +const NMPppMgrStatsData * +nm_ppp_mgr_get_stats(const NMPppMgr *self) +{ + g_return_val_if_fail(NM_IS_PPP_MGR(self), NULL); + + return &self->stats_data; +} + +/*****************************************************************************/ + +static void +_set_state(NMPppMgr *self, NMPppMgrState state, NMPppMgrState *out_old_state) +{ + if (state >= NM_PPP_MGR_STATE_HAVE_IP_CONFIG) + nm_clear_g_source_inst(&self->connect_timeout_source); + + NM_SET_OUT(out_old_state, self->state); + if (self->state != state) { + _LOGT("set state: %s (was %s)", + nm_ppp_mgr_state_to_string(state), + nm_ppp_mgr_state_to_string(self->state)); + self->state = state; + } +} + +static void +_set_state_failed(NMPppMgr *self, NMPppMgrState state, NMPppMgrState *out_old_state) +{ + nm_assert(state >= _NM_PPP_MGR_STATE_FAILED_START); + nm_assert(self->state < _NM_PPP_MGR_STATE_FAILED_START); + + _set_state(self, state, out_old_state); + + self->ifindex = 0; + nm_clear_l3cd(&self->ip_data_4.l3cd); + nm_clear_l3cd(&self->ip_data_6.l3cd); + self->ip_data_4 = (NMPppMgrIPData){ + .ip_received = FALSE, + .ip_enabled = FALSE, + }; + self->ip_data_6 = (NMPppMgrIPData){ + .ip_received = FALSE, + .ip_enabled = FALSE, + }; + + if (self->ppp_manager) { + g_signal_handlers_disconnect_by_data(self->ppp_manager, SELF_TO_USERDATA(self)); + if (self->ppp_started) { + self->ppp_started = FALSE; + nm_ppp_manager_stop(self->ppp_manager, NULL, NULL, NULL); + } + g_object_unref(self->ppp_manager); + } +} + +static gboolean +_state_ready_for_have_ifindex(NMPppMgr *self) +{ + if (self->ip_data_4.ip_received) { + /* once we receive an IPv4 config, we consider this as ready. + * + * The problem is that we don't know when we can expect an IPv6 config + * too, so we cannot just keep waiting. I don't know how to solve this, but + * it means IPv4+IPv6 together doesn't work well (because we would not + * wait for IPv6, once IPv4 config is received. */ + return TRUE; + } + + return ((!self->ip_data_4.ip_enabled || self->ip_data_4.ip_received) + && (!self->ip_data_6.ip_enabled || self->ip_data_6.ip_received)); +} + +/*****************************************************************************/ + +static void +_ppp_signal_state_changed(NMPPPManager *ppp_manager, guint ppp_state_u, gpointer user_data) +{ + NMPppMgr * self = SELF_FROM_USERDATA(user_data); + NMPPPStatus ppp_state = ppp_state_u; + NMPppMgrState state; + NMPppMgrState old_state; + NMDeviceStateReason reason; + const char * reason_msg; + + if ((guint) ppp_state != ppp_state_u) + ppp_state = NM_PPP_STATUS_UNKNOWN; + + switch (ppp_state) { + case NM_PPP_STATUS_DISCONNECT: + state = NM_PPP_MGR_STATE_FAILED; + reason = NM_DEVICE_STATE_REASON_PPP_DISCONNECT; + reason_msg = "ppp signals disconnect"; + break; + case NM_PPP_STATUS_DEAD: + case NM_PPP_STATUS_INTERN_DEAD: + state = NM_PPP_MGR_STATE_FAILED; + reason = NM_DEVICE_STATE_REASON_PPP_FAILED; + reason_msg = "ppp signals disconnect"; + break; + default: + _LOGT("ppp signal about state changed: #%u signal (ignored)", ppp_state_u); + return; + } + + _LOGT("ppp signal about state changed: #%u signal, new-state %s, state-reason=%s, %s", + ppp_state_u, + nm_ppp_mgr_state_to_string(state), + nm_device_state_reason_to_string(reason), + reason_msg); + + _set_state_failed(self, state, &old_state); + _callback_emit_state_change(self, + NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED, + old_state, + FALSE, + FALSE, + reason, + reason_msg); +} + +static void +_ppp_signal_ifindex_set(NMPPPManager *ppp_manager, + int ifindex, + const char * ifname, + gpointer user_data) +{ + NMPppMgr * self = SELF_FROM_USERDATA(user_data); + NMPppMgrState old_state; + NMPppMgrState new_state; + gboolean ip_changed_4; + gboolean ip_changed_6; + + if (ifindex <= 0) { + nm_assert(self->state == NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX); + _LOGT("ifindex set: invalid ifindex received"); + _set_state_failed(self, NM_PPP_MGR_STATE_FAILED_TO_IFINDEX, &old_state); + _callback_emit_state_change(self, + NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED, + old_state, + FALSE, + FALSE, + NM_DEVICE_STATE_REASON_PPP_FAILED, + "invalid ifindex provided by ppp plugin"); + return; + } + + if (self->ifindex > 0) { + nm_assert(self->state > NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX); + nm_assert(self->state < _NM_PPP_MGR_STATE_FAILED_START); + + _LOGT("ifindex set: ignore ifindex %d, already set to %d", ifindex, self->ifindex); + return; + } + + nm_assert(self->state == NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX); + self->ifindex = ifindex; + + if (_state_ready_for_have_ifindex(self)) { + new_state = NM_PPP_MGR_STATE_HAVE_IP_CONFIG; + ip_changed_4 = self->ip_data_4.ip_received; + ip_changed_6 = self->ip_data_6.ip_received; + } else { + new_state = NM_PPP_MGR_STATE_HAVE_IFINDEX; + ip_changed_4 = FALSE; + ip_changed_6 = FALSE; + } + + _LOGT("ifindex set: ifindex %d", ifindex); + + _set_state(self, new_state, &old_state); + _callback_emit_state_change(self, + NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED, + old_state, + ip_changed_4, + ip_changed_6, + NM_DEVICE_STATE_REASON_PPP_FAILED, + "invalid ifindex provided by ppp plugin"); +} + +static void +_ppp_signal_new_config(NMPPPManager * ppp_manager, + int addr_family, + const NML3ConfigData * l3cd, + const NMUtilsIPv6IfaceId *iid, + gpointer user_data) +{ + NMPppMgr * self = SELF_FROM_USERDATA(user_data); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMPppMgrState old_state; + gboolean ip_changed_4; + gboolean ip_changed_6; + + nm_assert(self->state >= NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX); + nm_assert(self->state < _NM_PPP_MGR_STATE_FAILED_START); + + if (nm_l3_config_data_equal(self->ip_data_x[IS_IPv4].l3cd, l3cd)) + l3cd = self->ip_data_x[IS_IPv4].l3cd; + + nm_l3_config_data_reset(&self->ip_data_x[IS_IPv4].l3cd, l3cd); + if (!IS_IPv4) { + if (iid) + self->ipv6_iid = *iid; + else + self->ipv6_iid = (NMUtilsIPv6IfaceId) NM_UTILS_IPV6_IFACE_ID_INIT; + self->ip_data_6.ipv6_iid = + nm_utils_memeqzero(&self->ipv6_iid, sizeof(self->ipv6_iid)) ? NULL : &self->ipv6_iid; + } + self->ip_data_x[IS_IPv4].ip_received = TRUE; + + if (self->state == NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX) { + /* we still wait for the ifindex. We just cache the IP configuration, + * but leave the state unchanged. */ + _LOGT("ip-config v%c received (still waiting for ifindex)", + nm_utils_addr_family_to_char(addr_family)); + old_state = NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX; + } else { + NMPppMgrState new_state; + + _LOGT("ip-config v%c received", nm_utils_addr_family_to_char(addr_family)); + new_state = _state_ready_for_have_ifindex(self) ? NM_PPP_MGR_STATE_HAVE_IP_CONFIG + : NM_PPP_MGR_STATE_HAVE_IFINDEX; + nm_assert((self->state == NM_PPP_MGR_STATE_HAVE_IFINDEX) + || (self->state == NM_PPP_MGR_STATE_HAVE_IP_CONFIG + && new_state == NM_PPP_MGR_STATE_HAVE_IP_CONFIG)); + _set_state(self, new_state, &old_state); + } + + ip_changed_4 = IS_IPv4; + ip_changed_6 = !IS_IPv4; + + _callback_emit_state_change(self, + NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED, + old_state, + ip_changed_4, + ip_changed_6, + NM_DEVICE_STATE_REASON_NONE, + "ip config received"); +} + +static void +_ppp_signal_stats(NMPPPManager *ppp_manager, guint in_bytes, guint out_bytes, gpointer user_data) +{ + NMPppMgr *self = SELF_FROM_USERDATA(user_data); + + if (self->stats_data.in_bytes == in_bytes && self->stats_data.out_bytes == out_bytes) + return; + + self->stats_data.in_bytes = in_bytes; + self->stats_data.out_bytes = out_bytes; + + _callback_emit_state_change(self, + NM_PPP_MGR_CALLBACK_TYPE_STATS_CHANGED, + self->state, + FALSE, + FALSE, + NM_DEVICE_STATE_REASON_NONE, + "stats update"); +} + +/*****************************************************************************/ + +static gboolean +_ifindex_timeout_cb(gpointer user_data) +{ + NMPppMgr * self = user_data; + NMPppMgrState old_state; + + nm_clear_g_source_inst(&self->connect_timeout_source); + + _set_state_failed(self, NM_PPP_MGR_STATE_FAILED_TO_IFINDEX, &old_state); + _callback_emit_state_change(self, + NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED, + old_state, + FALSE, + FALSE, + NM_DEVICE_STATE_REASON_PPP_FAILED, + "timeout connecting"); + return G_SOURCE_CONTINUE; +} + +/*****************************************************************************/ + +static gboolean +_idle_start_cb(gpointer user_data) +{ + NMPppMgr * self = user_data; + gs_free_error GError *error = NULL; + NMPppMgrState old_state; + gboolean ip4_enabled; + gboolean ip6_enabled; + NMPlatform * platform; + const NMPlatformLink *plink; + + nm_clear_g_source_inst(&self->idle_start); + + /* We only evaluate ipx_enabled here. That is because nm_ppp_manager_start() + * will evaluate it based on act_req's applied connection (like we do now), + * but as the applied connection can be reapplied, let's do it at the + * same time to be sure we agree. + * + * This should be nicer solved by NMPPPManager not accessing the NMConnection + * or make little/no use of NMActRequest. */ + nm_utils_ppp_ip_methods_enabled(nm_act_request_get_applied_connection(self->config.act_req), + &ip4_enabled, + &ip6_enabled); + self->ip_data_4.ip_enabled = !!ip4_enabled; + self->ip_data_6.ip_enabled = !!ip6_enabled; + + g_signal_connect(self->ppp_manager, + NM_PPP_MANAGER_SIGNAL_STATE_CHANGED, + G_CALLBACK(_ppp_signal_state_changed), + SELF_TO_USERDATA(self)); + g_signal_connect(self->ppp_manager, + NM_PPP_MANAGER_SIGNAL_IFINDEX_SET, + G_CALLBACK(_ppp_signal_ifindex_set), + SELF_TO_USERDATA(self)); + g_signal_connect(self->ppp_manager, + NM_PPP_MANAGER_SIGNAL_NEW_CONFIG, + G_CALLBACK(_ppp_signal_new_config), + SELF_TO_USERDATA(self)); + g_signal_connect(self->ppp_manager, + NM_PPP_MANAGER_SIGNAL_STATS, + G_CALLBACK(_ppp_signal_stats), + SELF_TO_USERDATA(self)); + + platform = nm_netns_get_platform(self->config.netns); + plink = nm_platform_link_get_by_ifname(platform, self->config.parent_iface); + if (plink && !NM_FLAGS_HAS(plink->n_ifi_flags, IFF_UP)) { + nm_platform_link_change_flags(platform, plink->ifindex, IFF_UP, TRUE); + } + + self->ppp_started = TRUE; + if (!nm_ppp_manager_start(self->ppp_manager, + self->config.act_req, + self->config.ppp_username, + 0, + self->config.baud_override, + &error)) { + gs_free char *reason_msg = NULL; + + _set_state_failed(self, NM_PPP_MGR_STATE_FAILED_TO_START, &old_state); + reason_msg = g_strdup_printf("failed to start: %s", error->message); + _callback_emit_state_change(self, + NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED, + old_state, + FALSE, + FALSE, + NM_DEVICE_STATE_REASON_PPP_START_FAILED, + reason_msg); + return G_SOURCE_CONTINUE; + } + + nm_assert(self->state == NM_PPP_MGR_STATE_STARTING); + _set_state(self, NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX, &old_state); + + self->connect_timeout_source = + nm_g_timeout_add_seconds_source(self->config.timeout_secs, _ifindex_timeout_cb, self); + + _callback_emit_state_change(self, + NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED, + old_state, + FALSE, + FALSE, + NM_DEVICE_STATE_REASON_NONE, + "pppd is starting"); + + return G_SOURCE_CONTINUE; +} + +/*****************************************************************************/ + +NMPppMgr * +nm_ppp_mgr_start(const NMPppMgrConfig *config, GError **error) +{ + NMPppMgr * self; + NMPPPManager *ppp_manager; + + g_return_val_if_fail(config, NULL); + g_return_val_if_fail(NM_IS_NETNS(config->netns), NULL); + g_return_val_if_fail(config->parent_iface, NULL); + g_return_val_if_fail(NM_IS_ACT_REQUEST(config->act_req), NULL); + g_return_val_if_fail(config->callback, NULL); + g_return_val_if_fail(!error || !*error, NULL); + + ppp_manager = nm_ppp_manager_create(config->parent_iface, error); + + if (!ppp_manager) + return NULL; + + self = g_slice_new(NMPppMgr); + + *self = (NMPppMgr){ + .config = *config, + .ppp_manager = ppp_manager, + .idle_start = nm_g_idle_add_source(_idle_start_cb, self), + .state = NM_PPP_MGR_STATE_STARTING, + .ip_data_4 = + { + .ip_received = FALSE, + .ip_enabled = NM_OPTION_BOOL_DEFAULT, + }, + .ip_data_6 = + { + .ip_received = FALSE, + .ip_enabled = NM_OPTION_BOOL_DEFAULT, + }, + .stats_data = + { + .in_bytes = 0, + .out_bytes = 0, + }, + }; + + g_object_ref(self->config.act_req); + g_object_ref(self->config.netns); + self->config.parent_iface = g_strdup(self->config.parent_iface); + self->config.ppp_username = g_strdup(self->config.ppp_username); + + _LOGD("created"); + + return self; +} + +void +nm_ppp_mgr_destroy(NMPppMgr *self) +{ + if (!self) + return; + + _LOGD("destroying"); + + if (self->state < _NM_PPP_MGR_STATE_FAILED_START) + _set_state_failed(self, NM_PPP_MGR_STATE_FAILED, NULL); + + nm_clear_g_source_inst(&self->idle_start); + nm_clear_g_source_inst(&self->connect_timeout_source); + + g_object_unref(self->config.act_req); + + g_free((char *) self->config.parent_iface); + g_free((char *) self->config.ppp_username); + + g_object_unref(self->config.netns); + + nm_g_slice_free(self); +} diff --git a/src/core/ppp/nm-ppp-mgr.h b/src/core/ppp/nm-ppp-mgr.h new file mode 100644 index 0000000000..f1aca4ed7a --- /dev/null +++ b/src/core/ppp/nm-ppp-mgr.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __NM_PPP_MGR_H__ +#define __NM_PPP_MGR_H__ + +#include "nm-l3cfg.h" + +typedef struct _NMPppMgr NMPppMgr; + +typedef enum _nm_packed { + /* NMPppMgr is starting. It will call nm_ppp_manager_start() on an idle + * handler. */ + NM_PPP_MGR_STATE_STARTING, + + /* NMPppMgr called nm_ppp_manager_start() and is now waiting to get + * an ifindex. At this time, we theoretically might already get IP configuration + * but that is cached and meaningless until we have the ifindex. */ + NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX, + + /* NMPppMgr received an ifindex from NMPPPManager. But no IP configuration + * is yet received. */ + NM_PPP_MGR_STATE_HAVE_IFINDEX, + + /* NMPppMgr received an ifindex and IP configuration from NMPPPManager. + * Whether we have IPv4 and/or IPv6 is unspecified. + * + * If we have only either IPv4 or IPv6, then it's also unclear unknown + * whether the other address family will still arrive or not. */ + NM_PPP_MGR_STATE_HAVE_IP_CONFIG, + + /* Meta enum value which is the first failed state. All states larger than + * this are final (dead) states. */ + _NM_PPP_MGR_STATE_FAILED_START, + + /* NMPPPManager failed to start. This is a final (dead) state. */ + NM_PPP_MGR_STATE_FAILED_TO_START = _NM_PPP_MGR_STATE_FAILED_START, + + /* NMPppMgr started, but it failed to get the ifindex (possibly after timeout). + * This is a final (dead) state. */ + NM_PPP_MGR_STATE_FAILED_TO_IFINDEX, + + /* An unspecified failed state. This is a final (dead) state. */ + NM_PPP_MGR_STATE_FAILED, +} NMPppMgrState; + +const char *nm_ppp_mgr_state_to_string(NMPppMgrState state); + +typedef enum { + NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED, + NM_PPP_MGR_CALLBACK_TYPE_STATS_CHANGED, +} NMPppMgrCallbackType; + +const char *nm_ppp_mgr_callback_type_to_string(NMPppMgrCallbackType callback_type); + +typedef struct { + guint32 in_bytes; + guint32 out_bytes; +} NMPppMgrStatsData; + +typedef struct { + const NML3ConfigData * l3cd; + const NMUtilsIPv6IfaceId *ipv6_iid; + NMOptionBool ip_enabled; + bool ip_received; +} NMPppMgrIPData; + +typedef struct { + NMPppMgrCallbackType callback_type; + union { + struct { + const char *reason_msg; + union { + struct { + const NMPppMgrIPData *ip_data_6; + const NMPppMgrIPData *ip_data_4; + }; + const NMPppMgrIPData *ip_data_x[2]; + }; + const NMPppMgrStatsData *stats_data; + int ifindex; + NMDeviceStateReason reason; + NMPppMgrState old_state; + NMPppMgrState state; + union { + struct { + bool ip_changed_6; + bool ip_changed_4; + }; + bool ip_changed_x[2]; + }; + } data; + }; +} NMPppMgrCallbackData; + +typedef void (*NMPppMgrCallback)(NMPppMgr * self, + const NMPppMgrCallbackData *callback_data, + gpointer user_data); + +typedef struct { + NMNetns * netns; + const char *parent_iface; + + NMPppMgrCallback callback; + gpointer user_data; + + NMActRequest *act_req; + const char * ppp_username; + guint32 timeout_secs; + guint baud_override; +} NMPppMgrConfig; + +gboolean _nm_assert_is_ppp_mgr(const NMPppMgr *self); + +#define NM_IS_PPP_MGR(self) \ + ({ \ + const NMPppMgr *_self = (self); \ + \ + nm_assert(_nm_assert_is_ppp_mgr(_self)); \ + !!_self; \ + }) + +NMPppMgr *nm_ppp_mgr_start(const NMPppMgrConfig *config, GError **error); + +NMPppMgrState nm_ppp_mgr_get_state(const NMPppMgr *self); +int nm_ppp_mgr_get_ifindex(const NMPppMgr *self); +const NMPppMgrIPData * nm_ppp_mgr_get_ip_data(const NMPppMgr *self, int addr_family); +const NMPppMgrStatsData *nm_ppp_mgr_get_stats(const NMPppMgr *self); + +void nm_ppp_mgr_destroy(NMPppMgr *self); + +#endif /* __NM_PPP_MGR_H__ */ diff --git a/src/core/ppp/nm-ppp-plugin-api.h b/src/core/ppp/nm-ppp-plugin-api.h index 647e1c240e..a867b94920 100644 --- a/src/core/ppp/nm-ppp-plugin-api.h +++ b/src/core/ppp/nm-ppp-plugin-api.h @@ -11,12 +11,6 @@ typedef const struct { NMPPPManager *(*create)(const char *iface); - void (*set_route_parameters)(NMPPPManager *manager, - guint32 route_table_v4, - guint32 route_metric_v4, - guint32 route_table_v6, - guint32 route_metric_v6); - gboolean (*start)(NMPPPManager *manager, NMActRequest *req, const char * ppp_name, diff --git a/src/core/ppp/nm-ppp-status.h b/src/core/ppp/nm-ppp-status.h index c346b119a2..df0ac10ba2 100644 --- a/src/core/ppp/nm-ppp-status.h +++ b/src/core/ppp/nm-ppp-status.h @@ -22,7 +22,10 @@ typedef enum { NM_PPP_STATUS_TERMINATE, NM_PPP_STATUS_DISCONNECT, NM_PPP_STATUS_HOLDOFF, - NM_PPP_STATUS_MASTER + NM_PPP_STATUS_MASTER, + + /* these states are internal and not announced by the pppd plugin. */ + NM_PPP_STATUS_INTERN_DEAD, } NMPPPStatus; #endif /* __NM_PPP_STATUS_H__ */ diff --git a/src/core/tests/meson.build b/src/core/tests/meson.build index 99fa0ae9a1..78c689f798 100644 --- a/src/core/tests/meson.build +++ b/src/core/tests/meson.build @@ -6,8 +6,6 @@ test_units = [ 'test-core', 'test-core-with-expect', 'test-dcb', - 'test-ip4-config', - 'test-ip6-config', 'test-l3cfg', 'test-utils', 'test-wired-defname', diff --git a/src/core/tests/test-ip4-config.c b/src/core/tests/test-ip4-config.c deleted file mode 100644 index 3a095d4ad9..0000000000 --- a/src/core/tests/test-ip4-config.c +++ /dev/null @@ -1,380 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2013 - 2014 Red Hat, Inc. - */ - -#include "src/core/nm-default-daemon.h" - -#include - -#include "nm-ip4-config.h" -#include "libnm-platform/nm-platform.h" - -#include "nm-test-utils-core.h" - -static NMIP4Config * -build_test_config(void) -{ - NMIP4Config * config; - NMPlatformIP4Address addr; - NMPlatformIP4Route route; - - /* Build up the config to subtract */ - config = nmtst_ip4_config_new(1); - - nm_assert(NM_IP_CONFIG_CAST(config)); - - addr = *nmtst_platform_ip4_address("192.168.1.10", "1.2.3.4", 24); - nm_ip4_config_add_address(config, &addr); - - route = *nmtst_platform_ip4_route("10.0.0.0", 8, "192.168.1.1"); - nm_ip4_config_add_route(config, &route, NULL); - - route = *nmtst_platform_ip4_route("172.16.0.0", 16, "192.168.1.1"); - nm_ip4_config_add_route(config, &route, NULL); - - { - const NMPlatformIP4Route r = { - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, - .gateway = nmtst_inet4_from_string("192.168.1.1"), - .table_coerced = 0, - .metric = 100, - }; - - nm_ip4_config_add_route(config, &r, NULL); - } - - nm_ip4_config_add_nameserver(config, nmtst_inet4_from_string("4.2.2.1")); - nm_ip4_config_add_nameserver(config, nmtst_inet4_from_string("4.2.2.2")); - nm_ip4_config_add_domain(config, "foobar.com"); - nm_ip4_config_add_domain(config, "baz.com"); - nm_ip4_config_add_search(config, "blahblah.com"); - nm_ip4_config_add_search(config, "beatbox.com"); - - nm_ip4_config_add_nis_server(config, nmtst_inet4_from_string("1.2.3.9")); - nm_ip4_config_add_nis_server(config, nmtst_inet4_from_string("1.2.3.10")); - - nm_ip4_config_add_wins(config, nmtst_inet4_from_string("4.2.3.9")); - nm_ip4_config_add_wins(config, nmtst_inet4_from_string("4.2.3.10")); - - return config; -} - -static void -test_replace(void) -{ - gs_unref_object NMIP4Config *config1 = NULL; - gs_unref_object NMIP4Config *config2 = NULL; - NMPlatformIP4Address addr; - gboolean relevant_changes; - - config1 = nmtst_ip4_config_new(1); - - addr = *nmtst_platform_ip4_address("172.16.0.1", NULL, 24); - addr.timestamp = 10; - addr.preferred = 3600; - addr.lifetime = 7200; - nm_ip4_config_add_address(config1, &addr); - - addr = *nmtst_platform_ip4_address("172.16.0.2", NULL, 24); - addr.timestamp = 10; - addr.preferred = 3600; - addr.lifetime = 7200; - nm_ip4_config_add_address(config1, &addr); - - config2 = nmtst_ip4_config_new(1); - - addr = *nmtst_platform_ip4_address("192.168.1.1", NULL, 24); - addr.timestamp = 40; - addr.preferred = 60; - addr.lifetime = 120; - nm_ip4_config_add_address(config2, &addr); - - addr = *nmtst_platform_ip4_address("172.16.0.2", NULL, 24); - addr.timestamp = 40; - addr.preferred = 60; - addr.lifetime = 120; - nm_ip4_config_add_address(config2, &addr); - - g_assert(nm_ip4_config_replace(config2, config1, &relevant_changes)); - g_assert(relevant_changes); - g_assert(nm_ip4_config_equal(config1, config2)); -} - -static void -test_subtract(void) -{ - NMIP4Config * src, *dst; - NMPlatformIP4Address addr; - NMPlatformIP4Route route; - const NMPlatformIP4Address *test_addr; - const NMPlatformIP4Route * test_route; - const char * expected_addr = "192.168.1.12"; - guint32 expected_addr_plen = 24; - const char * expected_route_dest = "8.0.0.0"; - guint32 expected_route_plen = 8; - const char * expected_route_next_hop = "192.168.1.1"; - guint32 expected_ns1 = nmtst_inet4_from_string("8.8.8.8"); - guint32 expected_ns2 = nmtst_inet4_from_string("8.8.8.9"); - const char * expected_domain = "wonderfalls.com"; - const char * expected_search = "somewhere.com"; - guint32 expected_nis = nmtst_inet4_from_string("1.2.3.13"); - guint32 expected_wins = nmtst_inet4_from_string("2.3.4.5"); - guint32 expected_mtu = 1492; - - src = build_test_config(); - - /* add a couple more things to the test config */ - dst = build_test_config(); - addr = *nmtst_platform_ip4_address(expected_addr, NULL, expected_addr_plen); - nm_ip4_config_add_address(dst, &addr); - - route = *nmtst_platform_ip4_route(expected_route_dest, - expected_route_plen, - expected_route_next_hop); - nm_ip4_config_add_route(dst, &route, NULL); - - nm_ip4_config_add_nameserver(dst, expected_ns1); - nm_ip4_config_add_nameserver(dst, expected_ns2); - nm_ip4_config_add_domain(dst, expected_domain); - nm_ip4_config_add_search(dst, expected_search); - - nm_ip4_config_add_nis_server(dst, expected_nis); - nm_ip4_config_add_wins(dst, expected_wins); - - nm_ip4_config_set_mtu(dst, expected_mtu, NM_IP_CONFIG_SOURCE_UNKNOWN); - - nm_ip4_config_subtract(dst, src, 0); - - /* ensure what's left is what we expect */ - g_assert_cmpuint(nm_ip4_config_get_num_addresses(dst), ==, 1); - test_addr = _nmtst_ip4_config_get_address(dst, 0); - g_assert(test_addr != NULL); - g_assert_cmpuint(test_addr->address, ==, nmtst_inet4_from_string(expected_addr)); - g_assert_cmpuint(test_addr->peer_address, ==, test_addr->address); - g_assert_cmpuint(test_addr->plen, ==, expected_addr_plen); - - g_assert(!nm_ip4_config_best_default_route_get(dst)); - g_assert_cmpuint(nmtst_ip4_config_get_gateway(dst), ==, 0); - - g_assert_cmpuint(nm_ip4_config_get_num_routes(dst), ==, 1); - test_route = _nmtst_ip4_config_get_route(dst, 0); - g_assert(test_route != NULL); - g_assert_cmpuint(test_route->network, ==, nmtst_inet4_from_string(expected_route_dest)); - g_assert_cmpuint(test_route->plen, ==, expected_route_plen); - g_assert_cmpuint(test_route->gateway, ==, nmtst_inet4_from_string(expected_route_next_hop)); - - g_assert_cmpuint(nm_ip4_config_get_num_nameservers(dst), ==, 2); - g_assert_cmpuint(nm_ip4_config_get_nameserver(dst, 0), ==, expected_ns1); - g_assert_cmpuint(nm_ip4_config_get_nameserver(dst, 1), ==, expected_ns2); - - g_assert_cmpuint(nm_ip4_config_get_num_domains(dst), ==, 1); - g_assert_cmpstr(nm_ip4_config_get_domain(dst, 0), ==, expected_domain); - g_assert_cmpuint(nm_ip4_config_get_num_searches(dst), ==, 1); - g_assert_cmpstr(nm_ip4_config_get_search(dst, 0), ==, expected_search); - - g_assert_cmpuint(nm_ip4_config_get_num_nis_servers(dst), ==, 1); - g_assert_cmpuint(nm_ip4_config_get_nis_server(dst, 0), ==, expected_nis); - - g_assert_cmpuint(nm_ip4_config_get_num_wins(dst), ==, 1); - g_assert_cmpuint(nm_ip4_config_get_wins(dst, 0), ==, expected_wins); - - g_assert_cmpuint(nm_ip4_config_get_mtu(dst), ==, expected_mtu); - - g_object_unref(src); - g_object_unref(dst); -} - -static void -test_compare_with_source(void) -{ - NMIP4Config * a, *b; - NMPlatformIP4Address addr; - NMPlatformIP4Route route; - - a = nmtst_ip4_config_new(1); - b = nmtst_ip4_config_new(2); - - /* Address */ - addr = *nmtst_platform_ip4_address("1.2.3.4", NULL, 24); - addr.addr_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip4_config_add_address(a, &addr); - - addr.addr_source = NM_IP_CONFIG_SOURCE_VPN; - nm_ip4_config_add_address(b, &addr); - - /* Route */ - route = *nmtst_platform_ip4_route("10.0.0.0", 8, "192.168.1.1"); - route.rt_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip4_config_add_route(a, &route, NULL); - - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - nm_ip4_config_add_route(b, &route, NULL); - - /* Assert that the configs are basically the same, eg that the source is ignored */ - g_assert(nm_ip4_config_equal(a, b)); - - g_object_unref(a); - g_object_unref(b); -} - -static void -test_add_address_with_source(void) -{ - NMIP4Config * a; - NMPlatformIP4Address addr; - const NMPlatformIP4Address *test_addr; - - a = nmtst_ip4_config_new(1); - - /* Test that a higher priority source is not overwritten */ - addr = *nmtst_platform_ip4_address("1.2.3.4", NULL, 24); - addr.addr_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip4_config_add_address(a, &addr); - - test_addr = _nmtst_ip4_config_get_address(a, 0); - g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); - - addr.addr_source = NM_IP_CONFIG_SOURCE_VPN; - nm_ip4_config_add_address(a, &addr); - - test_addr = _nmtst_ip4_config_get_address(a, 0); - g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); - - /* Test that a lower priority address source is overwritten */ - _nmtst_ip4_config_del_address(a, 0); - addr.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; - nm_ip4_config_add_address(a, &addr); - - test_addr = _nmtst_ip4_config_get_address(a, 0); - g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); - - addr.addr_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip4_config_add_address(a, &addr); - - test_addr = _nmtst_ip4_config_get_address(a, 0); - g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); - - g_object_unref(a); -} - -static void -test_add_route_with_source(void) -{ - gs_unref_object NMIP4Config *a = NULL; - NMPlatformIP4Route route; - const NMPlatformIP4Route * test_route; - - a = nmtst_ip4_config_new(1); - - /* Test that a higher priority source is not overwritten */ - route = *nmtst_platform_ip4_route("1.2.3.0", 24, "1.2.3.1"); - route.rt_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip4_config_add_route(a, &route, NULL); - - g_assert_cmpint(nm_ip4_config_get_num_routes(a), ==, 1); - test_route = _nmtst_ip4_config_get_route(a, 0); - g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); - - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - nm_ip4_config_add_route(a, &route, NULL); - - g_assert_cmpint(nm_ip4_config_get_num_routes(a), ==, 1); - test_route = _nmtst_ip4_config_get_route(a, 0); - g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); - - _nmtst_ip4_config_del_route(a, 0); - g_assert_cmpint(nm_ip4_config_get_num_routes(a), ==, 0); - - /* Test that a lower priority address source is overwritten */ - route.rt_source = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL; - nm_ip4_config_add_route(a, &route, NULL); - - g_assert_cmpint(nm_ip4_config_get_num_routes(a), ==, 1); - test_route = _nmtst_ip4_config_get_route(a, 0); - g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_RTPROT_KERNEL); - - route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; - nm_ip4_config_add_route(a, &route, NULL); - - g_assert_cmpint(nm_ip4_config_get_num_routes(a), ==, 1); - test_route = _nmtst_ip4_config_get_route(a, 0); - g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); -} - -static void -test_merge_subtract_mtu(void) -{ - NMIP4Config *cfg1, *cfg2, *cfg3; - guint32 expected_mtu2 = 1492; - guint32 expected_mtu3 = 666; - - cfg1 = build_test_config(); - cfg2 = build_test_config(); - cfg3 = build_test_config(); - - /* add MSS, MTU to configs to test them */ - nm_ip4_config_set_mtu(cfg2, expected_mtu2, NM_IP_CONFIG_SOURCE_UNKNOWN); - nm_ip4_config_set_mtu(cfg3, expected_mtu3, NM_IP_CONFIG_SOURCE_UNKNOWN); - - nm_ip4_config_merge(cfg1, cfg2, NM_IP_CONFIG_MERGE_DEFAULT, 0); - /* ensure MSS and MTU are in cfg1 */ - g_assert_cmpuint(nm_ip4_config_get_mtu(cfg1), ==, expected_mtu2); - - nm_ip4_config_merge(cfg1, cfg3, NM_IP_CONFIG_MERGE_DEFAULT, 0); - /* ensure again the MSS and MTU in cfg1 got overridden */ - g_assert_cmpuint(nm_ip4_config_get_mtu(cfg1), ==, expected_mtu3); - - nm_ip4_config_subtract(cfg1, cfg3, 0); - /* ensure MSS and MTU are zero in cfg1 */ - g_assert_cmpuint(nm_ip4_config_get_mtu(cfg1), ==, 0); - - g_object_unref(cfg1); - g_object_unref(cfg2); - g_object_unref(cfg3); -} - -static void -test_strip_search_trailing_dot(void) -{ - NMIP4Config *config; - - config = nmtst_ip4_config_new(1); - - nm_ip4_config_add_search(config, "."); - nm_ip4_config_add_search(config, "foo"); - nm_ip4_config_add_search(config, "bar."); - nm_ip4_config_add_search(config, "baz.com"); - nm_ip4_config_add_search(config, "baz.com."); - nm_ip4_config_add_search(config, "foobar.."); - nm_ip4_config_add_search(config, ".foobar"); - nm_ip4_config_add_search(config, "~."); - - g_assert_cmpuint(nm_ip4_config_get_num_searches(config), ==, 4); - g_assert_cmpstr(nm_ip4_config_get_search(config, 0), ==, "foo"); - g_assert_cmpstr(nm_ip4_config_get_search(config, 1), ==, "bar"); - g_assert_cmpstr(nm_ip4_config_get_search(config, 2), ==, "baz.com"); - g_assert_cmpstr(nm_ip4_config_get_search(config, 3), ==, "~"); - - g_object_unref(config); -} - -/*****************************************************************************/ - -NMTST_DEFINE(); - -int -main(int argc, char **argv) -{ - nmtst_init_with_logging(&argc, &argv, NULL, "DEFAULT"); - - g_test_add_func("/ip4-config/replace", test_replace); - g_test_add_func("/ip4-config/subtract", test_subtract); - g_test_add_func("/ip4-config/compare-with-source", test_compare_with_source); - g_test_add_func("/ip4-config/add-address-with-source", test_add_address_with_source); - g_test_add_func("/ip4-config/add-route-with-source", test_add_route_with_source); - g_test_add_func("/ip4-config/merge-subtract-mtu", test_merge_subtract_mtu); - g_test_add_func("/ip4-config/strip-search-trailing-dot", test_strip_search_trailing_dot); - - return g_test_run(); -} diff --git a/src/core/tests/test-ip6-config.c b/src/core/tests/test-ip6-config.c deleted file mode 100644 index 2e6d8aaa19..0000000000 --- a/src/core/tests/test-ip6-config.c +++ /dev/null @@ -1,538 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2013 Red Hat, Inc. - */ - -#include "src/core/nm-default-daemon.h" - -#include -#include - -#include "nm-ip6-config.h" - -#include "libnm-platform/nm-platform.h" -#include "nm-test-utils-core.h" - -static NMIP6Config * -build_test_config(void) -{ - NMIP6Config *config; - - /* Build up the config to subtract */ - config = nmtst_ip6_config_new(1); - - nm_ip6_config_add_address(config, - nmtst_platform_ip6_address("abcd:1234:4321::cdde", "1:2:3:4::5", 64)); - nm_ip6_config_add_route( - config, - nmtst_platform_ip6_route("abcd:1200::", 24, "abcd:1234:4321:cdde::2", NULL), - NULL); - nm_ip6_config_add_route(config, - nmtst_platform_ip6_route("2001::", 16, "2001:abba::2234", NULL), - NULL); - - nm_ip6_config_add_route(config, - nmtst_platform_ip6_route("::", 0, "3001:abba::3234", NULL), - NULL); - - nm_ip6_config_add_nameserver(config, nmtst_inet6_from_string("1:2:3:4::1")); - nm_ip6_config_add_nameserver(config, nmtst_inet6_from_string("1:2:3:4::2")); - nm_ip6_config_add_domain(config, "foobar.com"); - nm_ip6_config_add_domain(config, "baz.com"); - nm_ip6_config_add_search(config, "blahblah.com"); - nm_ip6_config_add_search(config, "beatbox.com"); - - return config; -} - -static void -test_subtract(void) -{ - NMIP6Config * src, *dst; - const NMPlatformIP6Address *test_addr; - const NMPlatformIP6Route * test_route; - const char * expected_addr = "1122:3344:5566::7788"; - guint32 expected_addr_plen = 96; - const char * expected_route_dest = "9991:8800::"; - guint32 expected_route_plen = 24; - const char * expected_route_next_hop = "1119:2228:3337:4446::5555"; - struct in6_addr expected_ns1; - struct in6_addr expected_ns2; - const char * expected_domain = "wonderfalls.com"; - const char * expected_search = "somewhere.com"; - struct in6_addr tmp; - - src = build_test_config(); - - /* add a couple more things to the test config */ - dst = build_test_config(); - nm_ip6_config_add_address(dst, - nmtst_platform_ip6_address(expected_addr, NULL, expected_addr_plen)); - nm_ip6_config_add_route(dst, - nmtst_platform_ip6_route(expected_route_dest, - expected_route_plen, - expected_route_next_hop, - NULL), - NULL); - - expected_ns1 = *nmtst_inet6_from_string("2222:3333:4444::5555"); - nm_ip6_config_add_nameserver(dst, &expected_ns1); - expected_ns2 = *nmtst_inet6_from_string("2222:3333:4444::5556"); - nm_ip6_config_add_nameserver(dst, &expected_ns2); - - nm_ip6_config_add_domain(dst, expected_domain); - nm_ip6_config_add_search(dst, expected_search); - - nm_ip6_config_subtract(dst, src, 0); - - /* ensure what's left is what we expect */ - g_assert_cmpuint(nm_ip6_config_get_num_addresses(dst), ==, 1); - test_addr = _nmtst_ip6_config_get_address(dst, 0); - g_assert(test_addr != NULL); - tmp = *nmtst_inet6_from_string(expected_addr); - g_assert(memcmp(&test_addr->address, &tmp, sizeof(tmp)) == 0); - g_assert(memcmp(&test_addr->peer_address, &in6addr_any, sizeof(tmp)) == 0); - g_assert_cmpuint(test_addr->plen, ==, expected_addr_plen); - - g_assert(nm_ip6_config_best_default_route_get(dst) == NULL); - - g_assert_cmpuint(nm_ip6_config_get_num_routes(dst), ==, 1); - test_route = _nmtst_ip6_config_get_route(dst, 0); - g_assert(test_route != NULL); - - tmp = *nmtst_inet6_from_string(expected_route_dest); - g_assert(memcmp(&test_route->network, &tmp, sizeof(tmp)) == 0); - g_assert_cmpuint(test_route->plen, ==, expected_route_plen); - tmp = *nmtst_inet6_from_string(expected_route_next_hop); - g_assert(memcmp(&test_route->gateway, &tmp, sizeof(tmp)) == 0); - - g_assert_cmpuint(nm_ip6_config_get_num_nameservers(dst), ==, 2); - g_assert(memcmp(nm_ip6_config_get_nameserver(dst, 0), &expected_ns1, sizeof(expected_ns1)) - == 0); - g_assert(memcmp(nm_ip6_config_get_nameserver(dst, 1), &expected_ns2, sizeof(expected_ns2)) - == 0); - - g_assert_cmpuint(nm_ip6_config_get_num_domains(dst), ==, 1); - g_assert_cmpstr(nm_ip6_config_get_domain(dst, 0), ==, expected_domain); - g_assert_cmpuint(nm_ip6_config_get_num_searches(dst), ==, 1); - g_assert_cmpstr(nm_ip6_config_get_search(dst, 0), ==, expected_search); - - g_object_unref(src); - g_object_unref(dst); -} - -static void -test_compare_with_source(void) -{ - NMIP6Config * a, *b; - NMPlatformIP6Address addr; - NMPlatformIP6Route route; - - a = nmtst_ip6_config_new(1); - b = nmtst_ip6_config_new(2); - - /* Address */ - addr = *nmtst_platform_ip6_address("1122:3344:5566::7788", NULL, 64); - addr.addr_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip6_config_add_address(a, &addr); - - addr.addr_source = NM_IP_CONFIG_SOURCE_VPN; - nm_ip6_config_add_address(b, &addr); - - /* Route */ - route = *nmtst_platform_ip6_route("abcd:1200::", 24, "abcd:1234:4321:cdde::2", NULL); - route.rt_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip6_config_add_route(a, &route, NULL); - - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - nm_ip6_config_add_route(b, &route, NULL); - - /* Assert that the configs are basically the same, eg that the source is ignored */ - g_assert(nm_ip6_config_equal(a, b)); - - g_object_unref(a); - g_object_unref(b); -} - -static void -test_add_address_with_source(void) -{ - NMIP6Config * a; - NMPlatformIP6Address addr; - const NMPlatformIP6Address *test_addr; - - a = nmtst_ip6_config_new(1); - - /* Test that a higher priority source is not overwritten */ - addr = *nmtst_platform_ip6_address("1122:3344:5566::7788", NULL, 64); - addr.addr_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip6_config_add_address(a, &addr); - - test_addr = _nmtst_ip6_config_get_address(a, 0); - g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); - - addr.addr_source = NM_IP_CONFIG_SOURCE_VPN; - nm_ip6_config_add_address(a, &addr); - - test_addr = _nmtst_ip6_config_get_address(a, 0); - g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); - - /* Test that a lower priority address source is overwritten */ - _nmtst_ip6_config_del_address(a, 0); - addr.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; - nm_ip6_config_add_address(a, &addr); - - test_addr = _nmtst_ip6_config_get_address(a, 0); - g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); - - addr.addr_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip6_config_add_address(a, &addr); - - test_addr = _nmtst_ip6_config_get_address(a, 0); - g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); - - g_object_unref(a); -} - -static void -test_add_route_with_source(void) -{ - gs_unref_object NMIP6Config *a = NULL; - NMPlatformIP6Route route; - const NMPlatformIP6Route * test_route; - - a = nmtst_ip6_config_new(1); - - /* Test that a higher priority source is not overwritten */ - route = *nmtst_platform_ip6_route("abcd:1200::", 24, "abcd:1234:4321:cdde::2", NULL); - route.rt_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip6_config_add_route(a, &route, NULL); - - g_assert_cmpint(nm_ip6_config_get_num_routes(a), ==, 1); - test_route = _nmtst_ip6_config_get_route(a, 0); - g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); - - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - nm_ip6_config_add_route(a, &route, NULL); - - g_assert_cmpint(nm_ip6_config_get_num_routes(a), ==, 1); - test_route = _nmtst_ip6_config_get_route(a, 0); - g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); - - _nmtst_ip6_config_del_route(a, 0); - g_assert_cmpint(nm_ip6_config_get_num_routes(a), ==, 0); - - /* Test that a lower priority address source is overwritten */ - route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; - nm_ip6_config_add_route(a, &route, NULL); - - g_assert_cmpint(nm_ip6_config_get_num_routes(a), ==, 1); - test_route = _nmtst_ip6_config_get_route(a, 0); - g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); - - route.rt_source = NM_IP_CONFIG_SOURCE_USER; - nm_ip6_config_add_route(a, &route, NULL); - - g_assert_cmpint(nm_ip6_config_get_num_routes(a), ==, 1); - test_route = _nmtst_ip6_config_get_route(a, 0); - g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); -} - -static void -test_nm_ip6_config_addresses_sort_check(NMIP6Config * config, - NMSettingIP6ConfigPrivacy use_tempaddr, - int repeat) -{ - int addr_count = nm_ip6_config_get_num_addresses(config); - int i, irepeat; - NMIP6Config *copy, *copy2; - int * idx = g_new(int, addr_count); - - nm_ip6_config_set_privacy(config, use_tempaddr); - copy = nm_ip6_config_clone(config); - g_assert(copy); - copy2 = nm_ip6_config_clone(config); - g_assert(copy2); - - /* initialize the array of indices, and keep shuffling them for every @repeat iteration. */ - for (i = 0; i < addr_count; i++) - idx[i] = i; - - for (irepeat = 0; irepeat < repeat; irepeat++) { - /* randomly shuffle the addresses. */ - nm_ip6_config_reset_addresses(copy); - for (i = 0; i < addr_count; i++) { - int j = g_rand_int_range(nmtst_get_rand(), i, addr_count); - - NM_SWAP(&idx[i], &idx[j]); - nm_ip6_config_add_address(copy, _nmtst_ip6_config_get_address(config, idx[i])); - } - - /* reorder them again */ - _nmtst_ip6_config_addresses_sort(copy); - - /* check equality using nm_ip6_config_equal() */ - if (!nm_ip6_config_equal(copy, config)) { - g_message("%s", "SORTING yields unexpected output:"); - for (i = 0; i < addr_count; i++) { - g_message( - " >> [%d] = %s", - i, - nm_platform_ip6_address_to_string(_nmtst_ip6_config_get_address(config, i), - NULL, - 0)); - g_message(" << [%d] = %s", - i, - nm_platform_ip6_address_to_string(_nmtst_ip6_config_get_address(copy, i), - NULL, - 0)); - } - g_assert_not_reached(); - } - - /* also check equality using nm_ip6_config_replace() */ - g_assert(nm_ip6_config_replace(copy2, copy, NULL) == FALSE); - } - - g_free(idx); - g_object_unref(copy); - g_object_unref(copy2); -} - -static void -test_nm_ip6_config_addresses_sort(void) -{ - NMIP6Config *config = build_test_config(); - -#define ADDR_ADD(...) \ - nm_ip6_config_add_address(config, nmtst_platform_ip6_address_full(__VA_ARGS__)) - - nm_ip6_config_reset_addresses(config); - ADDR_ADD("2607:f0d0:1002:51::4", NULL, 64, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, 0); - ADDR_ADD("2607:f0d0:1002:51::5", NULL, 64, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, 0); - ADDR_ADD("2607:f0d0:1002:51::6", - NULL, - 64, - 0, - NM_IP_CONFIG_SOURCE_NDISC, - 0, - 0, - 0, - IFA_F_MANAGETEMPADDR); - ADDR_ADD("2607:f0d0:1002:51::3", - NULL, - 64, - 0, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0, - 0, - IFA_F_SECONDARY); - ADDR_ADD("2607:f0d0:1002:51::8", - NULL, - 64, - 0, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0, - 0, - IFA_F_SECONDARY); - ADDR_ADD("2607:f0d0:1002:51::0", - NULL, - 64, - 0, - NM_IP_CONFIG_SOURCE_KERNEL, - 0, - 0, - 0, - IFA_F_SECONDARY); - ADDR_ADD("fec0::1", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0); - ADDR_ADD("fe80::208:74ff:feda:625c", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0); - ADDR_ADD("fe80::208:74ff:feda:625d", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0); - ADDR_ADD("::1", NULL, 128, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, 0); - ADDR_ADD("2607:f0d0:1002:51::2", - NULL, - 64, - 0, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0, - 0, - IFA_F_TENTATIVE); - test_nm_ip6_config_addresses_sort_check(config, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, 8); - test_nm_ip6_config_addresses_sort_check(config, NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED, 8); - test_nm_ip6_config_addresses_sort_check(config, - NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR, - 8); - - nm_ip6_config_reset_addresses(config); - ADDR_ADD("2607:f0d0:1002:51::3", - NULL, - 64, - 0, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0, - 0, - IFA_F_SECONDARY); - ADDR_ADD("2607:f0d0:1002:51::4", NULL, 64, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, 0); - ADDR_ADD("2607:f0d0:1002:51::5", NULL, 64, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, 0); - ADDR_ADD("2607:f0d0:1002:51::8", - NULL, - 64, - 0, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0, - 0, - IFA_F_SECONDARY); - ADDR_ADD("2607:f0d0:1002:51::0", - NULL, - 64, - 0, - NM_IP_CONFIG_SOURCE_KERNEL, - 0, - 0, - 0, - IFA_F_SECONDARY); - ADDR_ADD("2607:f0d0:1002:51::6", - NULL, - 64, - 0, - NM_IP_CONFIG_SOURCE_NDISC, - 0, - 0, - 0, - IFA_F_MANAGETEMPADDR); - ADDR_ADD("fec0::1", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0); - ADDR_ADD("fe80::208:74ff:feda:625c", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0); - ADDR_ADD("fe80::208:74ff:feda:625d", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0); - ADDR_ADD("::1", NULL, 128, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, 0); - ADDR_ADD("2607:f0d0:1002:51::2", - NULL, - 64, - 0, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0, - 0, - IFA_F_TENTATIVE); - test_nm_ip6_config_addresses_sort_check(config, - NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, - 8); - -#undef ADDR_ADD - g_object_unref(config); -} - -static void -test_strip_search_trailing_dot(void) -{ - NMIP6Config *config; - - config = nmtst_ip6_config_new(1); - - nm_ip6_config_add_search(config, "."); - nm_ip6_config_add_search(config, "foo"); - nm_ip6_config_add_search(config, "bar."); - nm_ip6_config_add_search(config, "baz.com"); - nm_ip6_config_add_search(config, "baz.com."); - nm_ip6_config_add_search(config, "foobar.."); - nm_ip6_config_add_search(config, ".foobar"); - nm_ip6_config_add_search(config, "~."); - - g_assert_cmpuint(nm_ip6_config_get_num_searches(config), ==, 4); - g_assert_cmpstr(nm_ip6_config_get_search(config, 0), ==, "foo"); - g_assert_cmpstr(nm_ip6_config_get_search(config, 1), ==, "bar"); - g_assert_cmpstr(nm_ip6_config_get_search(config, 2), ==, "baz.com"); - g_assert_cmpstr(nm_ip6_config_get_search(config, 3), ==, "~"); - - g_object_unref(config); -} - -/*****************************************************************************/ - -static void -test_replace(gconstpointer user_data) -{ - nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new(); - const int TEST_IDX = GPOINTER_TO_INT(user_data); - const int IFINDEX = 1; - gs_unref_object NMIP6Config *src_conf = NULL; - gs_unref_object NMIP6Config *dst_conf = NULL; - NMPlatformIP6Address * addr; - NMPlatformIP6Address addrs[5] = {}; - guint addrs_n = 0; - guint i; - - dst_conf = nm_ip6_config_new(multi_idx, IFINDEX); - src_conf = nm_ip6_config_new(multi_idx, IFINDEX); - - switch (TEST_IDX) { - case 1: - addr = &addrs[addrs_n++]; - addr->ifindex = IFINDEX; - addr->address = *nmtst_inet6_from_string("fe80::78ec:7a6d:602d:20f2"); - addr->plen = 64; - addr->n_ifa_flags = IFA_F_PERMANENT; - addr->addr_source = NM_IP_CONFIG_SOURCE_KERNEL; - break; - case 2: - addr = &addrs[addrs_n++]; - addr->ifindex = IFINDEX; - addr->address = *nmtst_inet6_from_string("fe80::78ec:7a6d:602d:20f2"); - addr->plen = 64; - addr->n_ifa_flags = IFA_F_PERMANENT; - addr->addr_source = NM_IP_CONFIG_SOURCE_KERNEL; - - addr = &addrs[addrs_n++]; - addr->ifindex = IFINDEX; - addr->address = *nmtst_inet6_from_string("1::1"); - addr->plen = 64; - addr->addr_source = NM_IP_CONFIG_SOURCE_USER; - - nm_ip6_config_add_address(dst_conf, addr); - break; - default: - g_assert_not_reached(); - } - - g_assert(addrs_n < G_N_ELEMENTS(addrs)); - - for (i = 0; i < addrs_n; i++) - nm_ip6_config_add_address(src_conf, &addrs[i]); - - nm_ip6_config_replace(dst_conf, src_conf, NULL); - - for (i = 0; i < addrs_n; i++) { - const NMPlatformIP6Address *a = _nmtst_ip6_config_get_address(dst_conf, i); - const NMPlatformIP6Address *b = _nmtst_ip6_config_get_address(src_conf, i); - - g_assert(nm_platform_ip6_address_cmp(&addrs[i], a) == 0); - g_assert(nm_platform_ip6_address_cmp(&addrs[i], b) == 0); - } - g_assert(addrs_n == nm_ip6_config_get_num_addresses(dst_conf)); - g_assert(addrs_n == nm_ip6_config_get_num_addresses(src_conf)); -} - -/*****************************************************************************/ - -NMTST_DEFINE(); - -int -main(int argc, char **argv) -{ - nmtst_init_with_logging(&argc, &argv, NULL, "ALL"); - - g_test_add_func("/ip6-config/subtract", test_subtract); - g_test_add_func("/ip6-config/compare-with-source", test_compare_with_source); - g_test_add_func("/ip6-config/add-address-with-source", test_add_address_with_source); - g_test_add_func("/ip6-config/add-route-with-source", test_add_route_with_source); - g_test_add_func("/ip6-config/test_nm_ip6_config_addresses_sort", - test_nm_ip6_config_addresses_sort); - g_test_add_func("/ip6-config/strip-search-trailing-dot", test_strip_search_trailing_dot); - g_test_add_data_func("/ip6-config/replace/1", GINT_TO_POINTER(1), test_replace); - g_test_add_data_func("/ip6-config/replace/2", GINT_TO_POINTER(2), test_replace); - - return g_test_run(); -} diff --git a/src/core/tests/test-l3cfg.c b/src/core/tests/test-l3cfg.c index 100c24967a..b5c85b8224 100644 --- a/src/core/tests/test-l3cfg.c +++ b/src/core/tests/test-l3cfg.c @@ -22,9 +22,9 @@ _netns_access_l3cfg(NMNetns *netns, int ifindex) g_assert(NM_IS_NETNS(netns)); g_assert(ifindex > 0); - g_assert(!nm_netns_get_l3cfg(netns, ifindex)); + g_assert(!nm_netns_l3cfg_get(netns, ifindex)); - l3cfg = nm_netns_access_l3cfg(netns, ifindex); + l3cfg = nm_netns_l3cfg_acquire(netns, ifindex); g_assert(NM_IS_L3CFG(l3cfg)); return l3cfg; } diff --git a/src/core/vpn/nm-vpn-connection.c b/src/core/vpn/nm-vpn-connection.c index d2034278c0..5e0990ff62 100644 --- a/src/core/vpn/nm-vpn-connection.c +++ b/src/core/vpn/nm-vpn-connection.c @@ -8,32 +8,55 @@ #include "nm-vpn-connection.h" -#include -#include #include -#include -#include -#include #include #include +#include +#include +#include +#include +#include -#include "nm-proxy-config.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" -#include "libnm-platform/nm-platform.h" -#include "nm-active-connection.h" #include "NetworkManagerUtils.h" -#include "settings/nm-settings-connection.h" -#include "nm-dispatcher.h" -#include "nm-netns.h" -#include "settings/nm-agent-manager.h" -#include "libnm-core-intern/nm-core-internal.h" -#include "nm-pacrunner-manager.h" -#include "nm-firewalld-manager.h" -#include "nm-config.h" -#include "nm-vpn-plugin-info.h" -#include "nm-vpn-manager.h" #include "dns/nm-dns-manager.h" +#include "libnm-core-intern/nm-core-internal.h" +#include "libnm-glib-aux/nm-dbus-aux.h" +#include "libnm-platform/nm-platform.h" +#include "libnm-std-aux/unaligned.h" +#include "nm-active-connection.h" +#include "nm-config.h" +#include "nm-dbus-manager.h" +#include "nm-dispatcher.h" +#include "nm-firewalld-manager.h" +#include "nm-ip-config.h" +#include "nm-l3-config-data.h" +#include "nm-netns.h" +#include "nm-pacrunner-manager.h" +#include "nm-vpn-manager.h" +#include "nm-vpn-plugin-info.h" +#include "settings/nm-agent-manager.h" +#include "settings/nm-settings-connection.h" + +/* FIXME(l3cfg): Check that we handle it correctly if the parent device is VRF type. */ + +/* FIXME(l3cfg): Proxy settings are no longer configured. That needs to be handled by NML3Cfg. */ + +/*****************************************************************************/ + +#define DBUS_DEFAULT_TIMEOUT_MSEC 10000 + +typedef enum { + L3CD_TYPE_GW_EXTERN, + + L3CD_TYPE_STATIC, + L3CD_TYPE_GENERIC, + L3CD_TYPE_IP_4, + L3CD_TYPE_IP_6, + +#define L3CD_TYPE_IP_X(IS_IPv4) ((IS_IPv4) ? L3CD_TYPE_IP_4 : L3CD_TYPE_IP_6) + + _L3CD_TYPE_NUM, +} L3CDType; typedef enum { /* Only system secrets */ @@ -78,6 +101,22 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMVpnConnection, PROP_VPN_STATE, PROP_BANNER, #define PROP_MASTER 2002 ); +typedef struct { + NMIPConfig *ip_config; + + 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. */ + 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 { gboolean service_can_persist; gboolean connection_can_persist; @@ -91,42 +130,65 @@ typedef struct { NMActiveConnectionStateReason failure_reason; NMVpnServiceState service_state; - guint start_timeout; - gboolean service_running; + GSource * start_timeout_source; NMVpnPluginInfo * plugin_info; - char * bus_name; - - NMFirewalldManagerCallId *fw_call; NMNetns *netns; - GPtrArray *ip4_dev_route_blacklist; + NML3Cfg * l3cfg_if; + NML3CfgCommitTypeHandle *l3cfg_commit_type_if; - GDBusProxy * proxy; - GCancellable * cancellable; + NML3Cfg * l3cfg_dev; + NML3CfgCommitTypeHandle *l3cfg_commit_type_dev; + + struct { + GDBusConnection *connection; + char * bus_name; + char * owner; + guint signal_id_vpn; + guint signal_id_name_changed; + bool name_owner_initialized : 1; + } dbus; + + NMFirewalldManagerCallId *fw_call; + + union { + const NML3ConfigData *const l3cds[_L3CD_TYPE_NUM]; + const NML3ConfigData * l3cds_[_L3CD_TYPE_NUM]; + }; + + /* This combines the l3cds of the VPN (basically, excluding l3cd_gw_extern which + * is only about configuration for the parent device). This is used to configure + * DNS. */ + const NML3ConfigData *l3cd_combined; + + union { + struct { + IPData ip_data_6; + IPData ip_data_4; + }; + IPData ip_data_x[2]; + }; + + GSource * init_fail_on_idle_source; + GSource * connect_timeout_source; + GCancellable * main_cancellable; GVariant * connect_hash; - guint connect_timeout; - NMProxyConfig * proxy_config; + char * banner; NMPacrunnerConfId *pacrunner_conf_id; - gboolean has_ip4; - NMIP4Config * ip4_config; - guint32 ip4_internal_gw; - guint32 ip4_external_gw; - gboolean has_ip6; - NMIP6Config * ip6_config; - /* These config instances are passed on to NMDevice and modified by NMDevice. - * This pointer is only useful for nm_device_replace_vpn4_config() to clear the - * previous configuration. Consider these instances to be owned by NMDevice. */ - NMIP4Config *last_device_ip4_config; - NMIP6Config *last_device_ip6_config; + int ifindex_if; + int ifindex_dev; - struct in6_addr *ip6_internal_gw; - struct in6_addr *ip6_external_gw; - char * ip_iface; - int ip_ifindex; - char * banner; - guint32 mtu; + guint32 mtu; + + bool wait_for_pre_up_state : 1; + + bool dbus_service_started : 1; + + bool generic_config_received : 1; + + bool l3cds_changed : 1; } NMVpnConnectionPrivate; struct _NMVpnConnection { @@ -151,23 +213,22 @@ static const GDBusSignalInfo signal_info_vpn_state_changed; static NMSettingsConnection *_get_settings_connection(NMVpnConnection *self, gboolean allow_missing); -static void get_secrets(NMVpnConnection *self, SecretsReq secrets_idx, const char *const *hints); +static void _secrets_get(NMVpnConnection *self, SecretsReq secrets_idx, const char *const *hints); static guint32 get_route_table(NMVpnConnection *self, int addr_family, gboolean fallback_main); -static void plugin_interactive_secrets_required(NMVpnConnection * self, - const char * message, - const char *const *secrets); - static void _set_vpn_state(NMVpnConnection * self, VpnState vpn_state, NMActiveConnectionStateReason reason, gboolean quitting); +static void +_l3cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMVpnConnection *self); + /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_VPN -#define _NMLOG_PREFIX_NAME "vpn-connection" +#define _NMLOG_PREFIX_NAME "vpn" #define __NMLOG_prefix_buf_len 128 @@ -176,6 +237,9 @@ __LOG_create_prefix(char *buf, NMVpnConnection *self, NMSettingsConnection *con) { NMVpnConnectionPrivate *priv; const char * id; + const char * iface; + char buf1[100]; + char buf2[100]; if (!self) return _NMLOG_PREFIX_NAME; @@ -183,13 +247,16 @@ __LOG_create_prefix(char *buf, NMVpnConnection *self, NMSettingsConnection *con) priv = NM_VPN_CONNECTION_GET_PRIVATE(self); id = con ? nm_settings_connection_get_id(con) : NULL; + iface = nm_vpn_connection_get_ip_iface(self, FALSE); + g_snprintf(buf, __NMLOG_prefix_buf_len, "%s[" "%p" /*self*/ "%s%s" /*con-uuid*/ "%s%s%s%s" /*con-id*/ - ",%d" /*ifindex*/ + "%s" /*ifindex_if*/ + "%s" /*ifindex_dev*/ "%s%s%s" /*iface*/ "]", _NMLOG_PREFIX_NAME, @@ -198,8 +265,9 @@ __LOG_create_prefix(char *buf, NMVpnConnection *self, NMSettingsConnection *con) con ? (nm_settings_connection_get_uuid(con) ?: "??") : "", con ? "," : "", NM_PRINT_FMT_QUOTED(id, "\"", id, "\"", con ? "??" : ""), - priv->ip_ifindex, - NM_PRINT_FMT_QUOTED(priv->ip_iface, ":(", priv->ip_iface, ")", "")); + priv->ifindex_if > 0 ? nm_sprintf_buf(buf1, ",if:%d", priv->ifindex_if) : "", + priv->ifindex_dev > 0 ? nm_sprintf_buf(buf2, ",dev:%d", priv->ifindex_dev) : "", + NM_PRINT_FMT_QUOTED(iface, ":(", iface, ")", "")); return buf; } @@ -216,7 +284,7 @@ __LOG_create_prefix(char *buf, NMVpnConnection *self, NMSettingsConnection *con) _nm_log(_level, \ _NMLOG_DOMAIN, \ 0, \ - (self) ? NM_VPN_CONNECTION_GET_PRIVATE(self)->ip_iface : NULL, \ + (self) ? nm_vpn_connection_get_ip_iface(self, FALSE) : NULL, \ (_con) ? nm_settings_connection_get_uuid(_con) : NULL, \ "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ __LOG_create_prefix(__prefix, (self), _con) \ @@ -227,18 +295,58 @@ __LOG_create_prefix(char *buf, NMVpnConnection *self, NMSettingsConnection *con) /*****************************************************************************/ -static void -cancel_get_secrets(NMVpnConnection *self) -{ - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); +static NM_UTILS_LOOKUP_STR_DEFINE(_l3cd_type_to_string, + L3CDType, + NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(NULL), + NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER(), + NM_UTILS_LOOKUP_STR_ITEM(L3CD_TYPE_GW_EXTERN, "gw-extern"), + NM_UTILS_LOOKUP_STR_ITEM(L3CD_TYPE_STATIC, "static"), + NM_UTILS_LOOKUP_STR_ITEM(L3CD_TYPE_GENERIC, "generic"), + NM_UTILS_LOOKUP_STR_ITEM(L3CD_TYPE_IP_4, "ip-4"), + NM_UTILS_LOOKUP_STR_ITEM(L3CD_TYPE_IP_6, "ip-6"), ); - if (priv->secrets_id) { - nm_settings_connection_cancel_secrets(_get_settings_connection(self, FALSE), - priv->secrets_id); - g_warn_if_fail(!priv->secrets_id); - priv->secrets_id = NULL; - } -} +static NM_UTILS_LOOKUP_STR_DEFINE( + _vpn_service_state_to_string, + NMVpnServiceState, + NM_UTILS_LOOKUP_DEFAULT(NULL), + NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_SERVICE_STATE_UNKNOWN, "unknown"), + NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_SERVICE_STATE_INIT, "init"), + NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_SERVICE_STATE_SHUTDOWN, "shutdown"), + NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_SERVICE_STATE_STARTING, "starting"), + NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_SERVICE_STATE_STARTED, "started"), + NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_SERVICE_STATE_STOPPING, "stopping"), + NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_SERVICE_STATE_STOPPED, "stopped"), ); + +#define vpn_service_state_to_string_a(state) \ + NM_UTILS_LOOKUP_STR_A(_vpn_service_state_to_string, state) + +static NM_UTILS_LOOKUP_STR_DEFINE(_vpn_state_to_string, + VpnState, + NM_UTILS_LOOKUP_DEFAULT(NULL), + NM_UTILS_LOOKUP_STR_ITEM(STATE_UNKNOWN, "unknown"), + NM_UTILS_LOOKUP_STR_ITEM(STATE_WAITING, "waiting"), + NM_UTILS_LOOKUP_STR_ITEM(STATE_PREPARE, "prepare"), + NM_UTILS_LOOKUP_STR_ITEM(STATE_NEED_AUTH, "need-auth"), + NM_UTILS_LOOKUP_STR_ITEM(STATE_CONNECT, "connect"), + NM_UTILS_LOOKUP_STR_ITEM(STATE_IP_CONFIG_GET, "ip-config-get"), + NM_UTILS_LOOKUP_STR_ITEM(STATE_PRE_UP, "pre-up"), + NM_UTILS_LOOKUP_STR_ITEM(STATE_ACTIVATED, "activated"), + NM_UTILS_LOOKUP_STR_ITEM(STATE_DEACTIVATING, "deactivating"), + NM_UTILS_LOOKUP_STR_ITEM(STATE_DISCONNECTED, "disconnected"), + NM_UTILS_LOOKUP_STR_ITEM(STATE_FAILED, "failed"), ); + +#define vpn_state_to_string_a(state) NM_UTILS_LOOKUP_STR_A(_vpn_state_to_string, state) + +static NM_UTILS_LOOKUP_STR_DEFINE( + _vpn_plugin_failure_to_string, + NMVpnPluginFailure, + NM_UTILS_LOOKUP_DEFAULT(NULL), + NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED, "login-failed"), + NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED, "connect-failed"), + NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG, "bad-ip-config"), ); + +#define vpn_plugin_failure_to_string_a(failure) \ + NM_UTILS_LOOKUP_STR_A(_vpn_plugin_failure_to_string, failure) static NMVpnConnectionState _state_to_nm_vpn_state(VpnState state) @@ -300,6 +408,8 @@ _state_to_ac_state(VpnState vpn_state) return NM_ACTIVE_CONNECTION_STATE_UNKNOWN; } +/*****************************************************************************/ + static NMSettingsConnection * _get_settings_connection(NMVpnConnection *self, gboolean allow_missing) { @@ -325,15 +435,460 @@ _get_applied_connection(NMVpnConnection *connection) return con; } -static void -disconnect_cb(GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) -{ - GVariant *variant; +/*****************************************************************************/ - variant = g_dbus_proxy_call_finish(proxy, result, NULL); - if (variant) - g_variant_unref(variant); - g_object_unref(user_data); +static void +_dbus_connection_call(NMVpnConnection * self, + const char * method_name, + GVariant * parameters, + const GVariantType *reply_type, + GAsyncReadyCallback callback) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + g_return_if_fail(priv->dbus.bus_name); + + _LOGT("dbus: call %s on %s", method_name, priv->dbus.bus_name); + g_dbus_connection_call(priv->dbus.connection, + priv->dbus.bus_name, + NM_VPN_DBUS_PLUGIN_PATH, + NM_VPN_DBUS_PLUGIN_INTERFACE, + method_name, + parameters, + reply_type, + G_DBUS_CALL_FLAGS_NONE, + DBUS_DEFAULT_TIMEOUT_MSEC, + priv->main_cancellable, + callback, + self); +} + +static NML3ConfigMergeFlags +_l3cfg_get_merge_flags(NMVpnConnection *self, L3CDType l3cd_type) +{ + NMConnection * applied; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + NML3ConfigMergeFlags merge_flags; + + merge_flags = NM_L3_CONFIG_MERGE_FLAGS_NONE; + + if (l3cd_type == L3CD_TYPE_IP_4) { + applied = _get_applied_connection(self); + s_ip4 = applied ? nm_connection_get_setting_ip_config(applied, AF_INET) : NULL; + + if (s_ip4 && nm_setting_ip_config_get_ignore_auto_routes(s_ip4)) + merge_flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES; + if (s_ip4 && nm_setting_ip_config_get_never_default(s_ip4)) + merge_flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES; + if (s_ip4 && nm_setting_ip_config_get_ignore_auto_dns(s_ip4)) + merge_flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_DNS; + } else if (l3cd_type == L3CD_TYPE_IP_6) { + applied = _get_applied_connection(self); + s_ip6 = applied ? nm_connection_get_setting_ip_config(applied, AF_INET6) : NULL; + + if (s_ip6 && nm_setting_ip_config_get_ignore_auto_routes(s_ip6)) + merge_flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES; + if (s_ip6 && nm_setting_ip_config_get_never_default(s_ip6)) + merge_flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES; + if (s_ip6 && nm_setting_ip_config_get_ignore_auto_dns(s_ip6)) + merge_flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_DNS; + } + + return merge_flags; +} + +static NML3ConfigData * +_l3cfg_l3cd_new(NMVpnConnection *self, int ifindex) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + return nm_l3_config_data_new(nm_netns_get_multi_idx(priv->netns), + ifindex, + NM_IP_CONFIG_SOURCE_VPN); +} + +/*****************************************************************************/ + +guint32 +nm_vpn_connection_get_ip4_internal_gateway(NMVpnConnection *self) +{ + g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), 0); + + return NM_VPN_CONNECTION_GET_PRIVATE(self)->ip_data_4.gw_internal.addr4; +} + +struct in6_addr * +nm_vpn_connection_get_ip6_internal_gateway(NMVpnConnection *self) +{ + g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), 0); + + return &NM_VPN_CONNECTION_GET_PRIVATE(self)->ip_data_6.gw_internal.addr6; +} + +NMVpnConnectionState +nm_vpn_connection_get_vpn_state(NMVpnConnection *self) +{ + g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), NM_VPN_CONNECTION_STATE_UNKNOWN); + + return _state_to_nm_vpn_state(NM_VPN_CONNECTION_GET_PRIVATE(self)->vpn_state); +} + +const char * +nm_vpn_connection_get_banner(NMVpnConnection *self) +{ + g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), NULL); + + return NM_VPN_CONNECTION_GET_PRIVATE(self)->banner; +} + +const NML3ConfigData * +nm_vpn_connection_get_l3cd(NMVpnConnection *self) +{ + NMVpnConnectionPrivate *priv; + + g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), NULL); + + priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + if (priv->l3cds_changed) { + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + L3CDType l3cd_type; + int ifindex; + + priv->l3cds_changed = FALSE; + + ifindex = nm_vpn_connection_get_ip_ifindex(self, TRUE); + + if (ifindex > 0) { + const int default_dns_priority_x[2] = { + NM_DNS_PRIORITY_DEFAULT_VPN, + NM_DNS_PRIORITY_DEFAULT_VPN, + }; + guint32 default_route_table_x[2]; + guint32 default_route_metric_x[2]; + + for (l3cd_type = 0; l3cd_type < _L3CD_TYPE_NUM; l3cd_type++) { + if (l3cd_type == L3CD_TYPE_GW_EXTERN) + continue; + if (!priv->l3cds[l3cd_type]) + continue; + + if (!l3cd) { + default_route_table_x[0] = get_route_table(self, AF_INET6, TRUE); + default_route_table_x[1] = get_route_table(self, AF_INET, TRUE); + default_route_metric_x[0] = + nm_vpn_connection_get_ip_route_metric(self, AF_INET6); + default_route_metric_x[1] = + nm_vpn_connection_get_ip_route_metric(self, AF_INET); + l3cd = _l3cfg_l3cd_new(self, ifindex); + } + + nm_l3_config_data_merge(l3cd, + priv->l3cds[l3cd_type], + _l3cfg_get_merge_flags(self, l3cd_type), + default_route_table_x, + default_route_metric_x, + NULL, + default_dns_priority_x, + NULL, + NULL); + } + } + + nm_l3_config_data_reset(&priv->l3cd_combined, l3cd); + } + + return priv->l3cd_combined; +} + +static int +_get_ifindex_for_device(NMVpnConnection *self) +{ + NMDevice *parent_dev; + int ifindex; + + nm_assert(NM_IS_VPN_CONNECTION(self)); + + parent_dev = nm_active_connection_get_device(NM_ACTIVE_CONNECTION(self)); + if (!parent_dev) + return 0; + ifindex = nm_device_get_ip_ifindex(parent_dev); + if (ifindex <= 0) + return 0; + + return ifindex; +} + +const char * +nm_vpn_connection_get_ip_iface(NMVpnConnection *self, gboolean fallback_device) +{ + NMVpnConnectionPrivate *priv; + + g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), NULL); + + priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + if (priv->l3cfg_if) + return nm_l3cfg_get_ifname(priv->l3cfg_if, TRUE); + if (fallback_device && priv->l3cfg_dev) + return nm_l3cfg_get_ifname(priv->l3cfg_dev, TRUE); + return NULL; +} + +int +nm_vpn_connection_get_ip_ifindex(NMVpnConnection *self, gboolean fallback_device) +{ + NMVpnConnectionPrivate *priv; + + g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), 0); + + priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + if (priv->ifindex_if > 0) + return priv->ifindex_if; + if (fallback_device && priv->ifindex_dev > 0) + return priv->ifindex_dev; + return 0; +} + +static guint32 +_get_vpn_timeout(NMVpnConnection *self) +{ + guint32 timeout; + NMSettingVpn *s_vpn; + + s_vpn = nm_connection_get_setting_vpn(_get_applied_connection(self)); + g_return_val_if_fail(s_vpn, 60); + + /* Timeout waiting for IP config signal from VPN service + * It is a configured value or 60 seconds */ + timeout = nm_setting_vpn_get_timeout(s_vpn); + if (timeout == 0) { + timeout = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, + NM_CON_DEFAULT("vpn.timeout"), + NULL, + 1, + G_MAXUINT32, + 60); + } + return timeout; +} + +/*****************************************************************************/ + +static gboolean +_l3cfg_l3cd_set(NMVpnConnection *self, L3CDType l3cd_type, const NML3ConfigData *l3cd) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + if (nm_l3_config_data_equal(l3cd, priv->l3cds[l3cd_type])) + return FALSE; + + if (_LOGT_ENABLED()) { + if (l3cd) { + char s_name[150]; + + /* Seal hear, so that we don't log about an unsealed instance. + * nm_l3_config_data_reset() anyway seals the instance too. */ + nm_l3_config_data_seal(l3cd); + + _LOGT("l3cd[%s]: set " NM_HASH_OBFUSCATE_PTR_FMT, + _l3cd_type_to_string(l3cd_type), + NM_HASH_OBFUSCATE_PTR(l3cd)); + nm_l3_config_data_log( + l3cd, + nm_sprintf_buf(s_name, "l3cd[%s]", _l3cd_type_to_string(l3cd_type)), + "vpn-config: ", + LOGL_TRACE, + _NMLOG_DOMAIN); + } else + _LOGT("l3cd[%s]: unset", _l3cd_type_to_string(l3cd_type)); + } + + nm_l3_config_data_reset(&priv->l3cds_[l3cd_type], l3cd); + priv->l3cds_changed = TRUE; + return TRUE; +} + +static void +_l3cfg_l3cd_update(NMVpnConnection *self, L3CDType l3cd_type) +{ + NMVpnConnectionPrivate * priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + NML3Cfg * l3cfg; + int priority; + const NML3ConfigData *const *p_l3cd; + + if (NM_IN_SET(l3cd_type, L3CD_TYPE_IP_4, L3CD_TYPE_IP_6, L3CD_TYPE_GENERIC, L3CD_TYPE_STATIC)) { + l3cfg = priv->l3cfg_if; + if (!l3cfg) { + l3cfg = priv->l3cfg_dev; + } + } else { + nm_assert(NM_IN_SET(l3cd_type, L3CD_TYPE_GW_EXTERN)); + l3cfg = priv->l3cfg_dev; + } + + p_l3cd = &priv->l3cds[l3cd_type]; + + if (!l3cfg) + return; + + if (!*p_l3cd) { + if (!nm_l3cfg_remove_config_all(l3cfg, p_l3cd)) + return; + _LOGT("l3cd[%s]: remove-config " NM_HASH_OBFUSCATE_PTR_FMT, + _l3cd_type_to_string(l3cd_type), + NM_HASH_OBFUSCATE_PTR(*p_l3cd)); + goto handle_changed; + } + + priority = 0; + + if (!nm_l3cfg_add_config(l3cfg, + p_l3cd, + TRUE, + *p_l3cd, + priority, + get_route_table(self, AF_INET, TRUE), + get_route_table(self, AF_INET6, TRUE), + nm_vpn_connection_get_ip_route_metric(self, AF_INET), + nm_vpn_connection_get_ip_route_metric(self, AF_INET6), + 0, + 0, + NM_DNS_PRIORITY_DEFAULT_VPN, + NM_DNS_PRIORITY_DEFAULT_VPN, + NM_L3_ACD_DEFEND_TYPE_ONCE, + 0, + NM_L3CFG_CONFIG_FLAGS_NONE, + _l3cfg_get_merge_flags(self, l3cd_type))) + return; + + _LOGT("l3cd[%s]: add-config " NM_HASH_OBFUSCATE_PTR_FMT, + _l3cd_type_to_string(l3cd_type), + NM_HASH_OBFUSCATE_PTR(*p_l3cd)); + +handle_changed: + nm_l3cfg_commit_on_idle_schedule(l3cfg, NM_L3_CFG_COMMIT_TYPE_AUTO); +} + +static void +_l3cfg_l3cd_update_all(NMVpnConnection *self) +{ + L3CDType l3cd_type; + + for (l3cd_type = 0; l3cd_type < _L3CD_TYPE_NUM; l3cd_type++) + _l3cfg_l3cd_update(self, l3cd_type); +} + +static void +_l3cfg_l3cd_clear_all(NMVpnConnection *self) +{ + L3CDType l3cd_type; + + for (l3cd_type = 0; l3cd_type < _L3CD_TYPE_NUM; l3cd_type++) + _l3cfg_l3cd_set(self, l3cd_type, NULL); + + _l3cfg_l3cd_update_all(self); +} + +static void +_l3cfg_clear(NMVpnConnection *self, NML3Cfg *l3cfg) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + L3CDType l3cd_type; + gboolean changed = FALSE; + + if (!l3cfg) + return; + + g_signal_handlers_disconnect_by_func(l3cfg, G_CALLBACK(_l3cfg_notify_cb), self); + + for (l3cd_type = 0; l3cd_type < _L3CD_TYPE_NUM; l3cd_type++) { + if (nm_l3cfg_remove_config_all(l3cfg, &priv->l3cds[l3cd_type])) + changed = TRUE; + } + + if (changed) + nm_l3cfg_commit_on_idle_schedule(l3cfg, NM_L3_CFG_COMMIT_TYPE_AUTO); +} + +/*****************************************************************************/ + +static void +cancel_get_secrets(NMVpnConnection *self) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + if (priv->secrets_id) { + _LOGT("secrets: cancel request"); + nm_settings_connection_cancel_secrets(_get_settings_connection(self, FALSE), + priv->secrets_id); + nm_assert(!priv->secrets_id); + } +} + +static void +_l3cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMVpnConnection *self) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT) { + if (l3cfg == (priv->l3cfg_if ?: priv->l3cfg_dev) && priv->wait_for_pre_up_state + && priv->vpn_state < STATE_PRE_UP) + _set_vpn_state(self, STATE_PRE_UP, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); + } +} + +static gboolean +_set_ip_ifindex(NMVpnConnection *self, int ifindex, gboolean is_if) +{ + NMVpnConnectionPrivate * priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + int * p_ifindex = is_if ? &priv->ifindex_if : &priv->ifindex_dev; + NML3Cfg ** p_l3cfg = is_if ? &priv->l3cfg_if : &priv->l3cfg_dev; + NML3CfgCommitTypeHandle **p_l3cfg_commit_type = + is_if ? &priv->l3cfg_commit_type_if : &priv->l3cfg_commit_type_dev; + gs_unref_object NML3Cfg *l3cfg_old = NULL; + + if (ifindex < 0) + ifindex = nm_assert_unreachable_val(0); + + if (*p_ifindex == ifindex) + return FALSE; + + _LOGD("set ip-ifindex-%s %d", is_if ? "if" : "dev", ifindex); + + *p_ifindex = ifindex; + + l3cfg_old = g_steal_pointer(p_l3cfg); + nm_l3cfg_commit_type_clear(l3cfg_old, p_l3cfg_commit_type); + _l3cfg_clear(self, l3cfg_old); + + if (ifindex > 0) { + *p_l3cfg = nm_netns_l3cfg_acquire(priv->netns, ifindex); + g_signal_connect(*p_l3cfg, NM_L3CFG_SIGNAL_NOTIFY, G_CALLBACK(_l3cfg_notify_cb), self); + *p_l3cfg_commit_type = + nm_l3cfg_commit_type_register(*p_l3cfg, NM_L3_CFG_COMMIT_TYPE_UPDATE, NULL, "vpn"); + } + + return TRUE; +} + +static void +disconnect_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMVpnConnection *self; + gs_unref_variant GVariant *res = NULL; + gs_free_error GError *error = NULL; + + res = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (nm_utils_error_is_cancelled(error)) + return; + + self = NM_VPN_CONNECTION(user_data); + + _LOGT("dbus: disconnected%s%s", + NM_PRINT_FMT_QUOTED2(error, " failed: ", error->message, " with success")); } static void @@ -348,57 +903,21 @@ fw_call_cleanup(NMVpnConnection *self) } } -static void -remove_parent_device_config(NMVpnConnection *connection, NMDevice *device) -{ - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(connection); - - if (priv->last_device_ip4_config) { - nm_device_replace_vpn4_config(device, priv->last_device_ip4_config, NULL); - g_clear_object(&priv->last_device_ip4_config); - } - - if (priv->last_device_ip6_config) { - nm_device_replace_vpn6_config(device, priv->last_device_ip6_config, NULL); - g_clear_object(&priv->last_device_ip6_config); - } -} - static void vpn_cleanup(NMVpnConnection *self, NMDevice *parent_dev) { - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - - if (priv->ip_ifindex) { - NMPlatform *platform = nm_netns_get_platform(priv->netns); - - nm_platform_link_change_flags(platform, priv->ip_ifindex, IFF_UP, FALSE); - nm_platform_ip_route_flush(platform, AF_UNSPEC, priv->ip_ifindex); - nm_platform_ip_address_flush(platform, AF_UNSPEC, priv->ip_ifindex); - } - - remove_parent_device_config(self, parent_dev); + const char *iface; /* Remove zone from firewall */ - if (priv->ip_iface) { - nm_firewalld_manager_remove_from_zone(nm_firewalld_manager_get(), - priv->ip_iface, - NULL, - NULL, - NULL); + iface = nm_vpn_connection_get_ip_iface(self, FALSE); + if (iface) { + nm_firewalld_manager_remove_from_zone(nm_firewalld_manager_get(), iface, NULL, NULL, NULL); } + /* Cancel pending firewall call */ fw_call_cleanup(self); - g_free(priv->banner); - priv->banner = NULL; - - g_free(priv->ip_iface); - priv->ip_iface = NULL; - priv->ip_ifindex = 0; - - g_free(priv->bus_name); - priv->bus_name = NULL; + _l3cfg_l3cd_clear_all(self); } static void @@ -445,27 +964,27 @@ _set_vpn_state(NMVpnConnection * self, NMActiveConnectionStateReason reason, gboolean quitting) { - NMVpnConnectionPrivate *priv; + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); VpnState old_vpn_state; - NMVpnConnectionState new_external_state, old_external_state; + NMVpnConnectionState new_external_state; + NMVpnConnectionState old_external_state; NMDevice * parent_dev = nm_active_connection_get_device(NM_ACTIVE_CONNECTION(self)); NMConnection *applied; - g_return_if_fail(NM_IS_VPN_CONNECTION(self)); - - priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - if (vpn_state == priv->vpn_state) return; old_vpn_state = priv->vpn_state; priv->vpn_state = vpn_state; + _LOGD("set state: %s (was %s)", + vpn_state_to_string_a(priv->vpn_state), + vpn_state_to_string_a(old_vpn_state)); + /* The device gets destroyed by active connection when it enters * the deactivated state, so we need to ref it for usage below. */ - if (parent_dev) - g_object_ref(parent_dev); + nm_g_object_ref(parent_dev); /* Update active connection base class state */ nm_active_connection_set_state(NM_ACTIVE_CONNECTION(self), @@ -495,9 +1014,9 @@ _set_vpn_state(NMVpnConnection * self, g_signal_emit(self, signals[INTERNAL_STATE_CHANGED], 0, - new_external_state, - old_external_state, - reason); + (guint) new_external_state, + (guint) old_external_state, + (guint) reason); _notify(self, PROP_VPN_STATE); } @@ -513,10 +1032,8 @@ _set_vpn_state(NMVpnConnection * self, _get_settings_connection(self, FALSE), _get_applied_connection(self), parent_dev, - priv->ip_iface, - priv->proxy_config, - priv->ip4_config, - priv->ip6_config, + nm_vpn_connection_get_ip_iface(self, FALSE), + nm_vpn_connection_get_l3cd(self), dispatcher_pre_up_done, self, &priv->dispatcher_id)) { @@ -525,6 +1042,9 @@ _set_vpn_state(NMVpnConnection * self, } break; case STATE_ACTIVATED: + + nm_clear_g_source_inst(&priv->start_timeout_source); + applied = _get_applied_connection(self); /* Secrets no longer needed now that we're connected */ @@ -535,22 +1055,11 @@ _set_vpn_state(NMVpnConnection * self, _get_settings_connection(self, FALSE), applied, parent_dev, - priv->ip_iface, - priv->proxy_config, - priv->ip4_config, - priv->ip6_config, + nm_vpn_connection_get_ip_iface(self, FALSE), + nm_vpn_connection_get_l3cd(self), NULL, NULL, NULL); - - if (priv->proxy_config) { - nm_pacrunner_manager_remove_clear(&priv->pacrunner_conf_id); - priv->pacrunner_conf_id = nm_pacrunner_manager_add(nm_pacrunner_manager_get(), - priv->proxy_config, - priv->ip_iface, - priv->ip4_config, - priv->ip6_config); - } break; case STATE_DEACTIVATING: applied = _get_applied_connection(self); @@ -559,19 +1068,15 @@ _set_vpn_state(NMVpnConnection * self, _get_settings_connection(self, FALSE), applied, parent_dev, - priv->ip_iface, - priv->proxy_config, - priv->ip4_config, - priv->ip6_config); + nm_vpn_connection_get_ip_iface(self, FALSE), + nm_vpn_connection_get_l3cd(self)); } else { if (!nm_dispatcher_call_vpn(NM_DISPATCHER_ACTION_VPN_PRE_DOWN, _get_settings_connection(self, FALSE), applied, parent_dev, - priv->ip_iface, - priv->proxy_config, - priv->ip4_config, - priv->ip6_config, + nm_vpn_connection_get_ip_iface(self, FALSE), + nm_vpn_connection_get_l3cd(self), dispatcher_pre_down_done, self, &priv->dispatcher_id)) { @@ -591,18 +1096,14 @@ _set_vpn_state(NMVpnConnection * self, _get_settings_connection(self, FALSE), _get_applied_connection(self), parent_dev, - priv->ip_iface, - NULL, - NULL, + nm_vpn_connection_get_ip_iface(self, FALSE), NULL); } else { nm_dispatcher_call_vpn(NM_DISPATCHER_ACTION_VPN_DOWN, _get_settings_connection(self, FALSE), _get_applied_connection(self), parent_dev, - priv->ip_iface, - NULL, - NULL, + nm_vpn_connection_get_ip_iface(self, FALSE), NULL, NULL, NULL, @@ -610,17 +1111,8 @@ _set_vpn_state(NMVpnConnection * self, } } - /* Tear down and clean up the connection */ - if (priv->proxy) { - g_dbus_proxy_call(priv->proxy, - "Disconnect", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->cancellable, - (GAsyncReadyCallback) disconnect_cb, - g_object_ref(self)); - } + if (priv->dbus.bus_name) + _dbus_connection_call(self, "Disconnect", NULL, G_VARIANT_TYPE("()"), disconnect_cb); vpn_cleanup(self, parent_dev); /* fall-through */ @@ -678,202 +1170,151 @@ device_state_changed(NMActiveConnection *active, */ } -static void -add_ip4_vpn_gateway_route(NMIP4Config *config, - NMDevice * parent_device, - in_addr_t vpn_gw, - NMPlatform * platform) +static gboolean +_parent_device_l3cd_add_gateway_route(NML3ConfigData *l3cd, + int addr_family, + NMDevice * parent_device, + const NMIPAddr *vpn_gw, + NMPlatform * platform) { - guint32 parent_gw = 0; - gboolean has_parent_gw = FALSE; - NMPlatformIP4Route route; - int ifindex; - guint32 route_metric; + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMIPAddr parent_gw = NM_IP_ADDR_INIT; + gboolean has_parent_gw = FALSE; nm_auto_nmpobj const NMPObject *route_resolved = NULL; + int ifindex; + NMPlatformIPXRoute route; + int r; - g_return_if_fail(NM_IS_IP4_CONFIG(config)); - g_return_if_fail(NM_IS_DEVICE(parent_device)); - g_return_if_fail(vpn_gw != 0); + nm_assert(NM_IS_L3_CONFIG_DATA(l3cd)); + g_return_val_if_fail(vpn_gw, FALSE); - ifindex = nm_ip4_config_get_ifindex(config); + if (nm_ip_addr_is_null(addr_family, vpn_gw)) + return FALSE; + + ifindex = nm_l3_config_data_get_ifindex(l3cd); nm_assert(ifindex > 0); - nm_assert(ifindex == nm_device_get_ip_ifindex(parent_device)); + + if (parent_device && ifindex != nm_device_get_ip_ifindex(parent_device)) + parent_device = 0; /* Ask kernel how to reach @vpn_gw. We can only inject the route in * @parent_device, so whatever we resolve, it can only be on @ifindex. */ - if (nm_platform_ip_route_get(platform, - AF_INET, - &vpn_gw, + r = nm_platform_ip_route_get(platform, + addr_family, + vpn_gw, ifindex, - (NMPObject **) &route_resolved) - >= 0) { - const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE(route_resolved); + (NMPObject **) &route_resolved); + if (r >= 0) { + const NMPlatformIPXRoute *rx = NMP_OBJECT_CAST_IPX_ROUTE(route_resolved); + const NMPObject * obj; - if (r->ifindex == ifindex) { - const NMPObject *obj; + if (rx->rx.ifindex == ifindex && nm_platform_route_table_is_main(rx->rx.table_coerced)) { + gconstpointer gw = nm_platform_ip_route_get_gateway(addr_family, &rx->rx); /* `ip route get` always resolves the route, even if the destination is unreachable. * In which case, it pretends the destination is directly reachable. * * So, only accept direct routes if @vpn_gw is a private network * or if the parent device also has a direct default route */ - if (nm_platform_route_table_is_main(r->table_coerced)) { - if (r->gateway) { - parent_gw = r->gateway; - has_parent_gw = TRUE; - } else if (nm_utils_ip_is_site_local(AF_INET, &vpn_gw)) { - has_parent_gw = TRUE; - } else if ((obj = nm_device_get_best_default_route(parent_device, AF_INET)) - && !NMP_OBJECT_CAST_IP4_ROUTE(obj)->gateway) { - has_parent_gw = TRUE; - } - } + 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)) + has_parent_gw = TRUE; + else if ((obj = nm_device_get_best_default_route(parent_device, addr_family)) + && nm_ip_addr_is_null( + addr_family, + nm_platform_ip_route_get_gateway(addr_family, + NMP_OBJECT_CAST_IP_ROUTE(obj)))) + has_parent_gw = TRUE; } } if (!has_parent_gw) - return; + return FALSE; - route_metric = nm_device_get_route_metric(parent_device, AF_INET); + if (IS_IPv4) { + route.r4 = (NMPlatformIP4Route){ + .ifindex = ifindex, + .network = vpn_gw->addr4, + .plen = 32, + .gateway = parent_gw.addr4, + .rt_source = NM_IP_CONFIG_SOURCE_VPN, + .metric_any = TRUE, + }; + } else { + route.r6 = (NMPlatformIP6Route){ + .ifindex = ifindex, + .network = vpn_gw->addr6, + .plen = 128, + .gateway = parent_gw.addr6, + .rt_source = NM_IP_CONFIG_SOURCE_VPN, + .metric_any = TRUE, + }; + } + nm_l3_config_data_add_route(l3cd, addr_family, NULL, &route.rx); - memset(&route, 0, sizeof(route)); - route.ifindex = ifindex; - route.network = vpn_gw; - route.plen = 32; - route.gateway = parent_gw; - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - route.metric = route_metric; - nm_ip4_config_add_route(config, &route, NULL); - - if (parent_gw) { + if (!nm_ip_addr_is_null(addr_family, &parent_gw)) { /* Ensure there's a route to the parent device's gateway through the * parent device, since if the VPN claims the default route and the VPN * routes include a subnet that matches the parent device's subnet, * the parent device's gateway would get routed through the VPN and fail. */ - memset(&route, 0, sizeof(route)); - route.network = parent_gw; - route.plen = 32; - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - route.metric = route_metric; - nm_ip4_config_add_route(config, &route, NULL); - } -} - -static void -add_ip6_vpn_gateway_route(NMIP6Config * config, - NMDevice * parent_device, - const struct in6_addr *vpn_gw, - NMPlatform * platform) -{ - const struct in6_addr *parent_gw = NULL; - gboolean has_parent_gw = FALSE; - NMPlatformIP6Route route; - int ifindex; - guint32 route_metric; - nm_auto_nmpobj const NMPObject *route_resolved = NULL; - - g_return_if_fail(NM_IS_IP6_CONFIG(config)); - g_return_if_fail(NM_IS_DEVICE(parent_device)); - g_return_if_fail(vpn_gw != NULL); - - ifindex = nm_ip6_config_get_ifindex(config); - - nm_assert(ifindex > 0); - nm_assert(ifindex == nm_device_get_ip_ifindex(parent_device)); - - /* Ask kernel how to reach @vpn_gw. We can only inject the route in - * @parent_device, so whatever we resolve, it can only be on @ifindex. */ - if (nm_platform_ip_route_get(platform, - AF_INET6, - vpn_gw, - ifindex, - (NMPObject **) &route_resolved) - >= 0) { - const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE(route_resolved); - - if (r->ifindex == ifindex) { - const NMPObject *obj; - - /* `ip route get` always resolves the route, even if the destination is unreachable. - * In which case, it pretends the destination is directly reachable. - * - * So, only accept direct routes if @vpn_gw is a private network - * or if the parent device also has a direct default route */ - if (nm_platform_route_table_is_main(r->table_coerced)) { - if (!IN6_IS_ADDR_UNSPECIFIED(&r->gateway)) { - parent_gw = &r->gateway; - has_parent_gw = TRUE; - } else if (nm_utils_ip_is_site_local(AF_INET6, &vpn_gw)) { - has_parent_gw = TRUE; - } else if ((obj = nm_device_get_best_default_route(parent_device, AF_INET6)) - && IN6_IS_ADDR_UNSPECIFIED(&NMP_OBJECT_CAST_IP6_ROUTE(obj)->gateway)) { - has_parent_gw = TRUE; - } - } + if (IS_IPv4) { + route.r4 = (NMPlatformIP4Route){ + .network = parent_gw.addr4, + .plen = 32, + .rt_source = NM_IP_CONFIG_SOURCE_VPN, + .metric_any = TRUE, + }; + } else { + route.r6 = (NMPlatformIP6Route){ + .network = parent_gw.addr6, + .plen = 128, + .rt_source = NM_IP_CONFIG_SOURCE_VPN, + .metric_any = TRUE, + }; } + nm_l3_config_data_add_route(l3cd, addr_family, NULL, &route.rx); } - if (!has_parent_gw) - return; - - route_metric = nm_device_get_route_metric(parent_device, AF_INET6); - - memset(&route, 0, sizeof(route)); - route.ifindex = ifindex; - route.network = *vpn_gw; - route.plen = 128; - if (parent_gw) - route.gateway = *parent_gw; - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - route.metric = route_metric; - nm_ip6_config_add_route(config, &route, NULL); - - /* Ensure there's a route to the parent device's gateway through the - * parent device, since if the VPN claims the default route and the VPN - * routes include a subnet that matches the parent device's subnet, - * the parent device's gateway would get routed through the VPN and fail. - */ - if (parent_gw && !IN6_IS_ADDR_UNSPECIFIED(parent_gw)) { - memset(&route, 0, sizeof(route)); - route.network = *parent_gw; - route.plen = 128; - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - route.metric = route_metric; - nm_ip6_config_add_route(config, &route, NULL); - } + return TRUE; } -NMVpnConnection * -nm_vpn_connection_new(NMSettingsConnection * settings_connection, - NMDevice * parent_device, - const char * specific_object, - NMActivationReason activation_reason, - NMActivationStateFlags initial_state_flags, - NMAuthSubject * subject) +static gboolean +_l3cfg_l3cd_gw_extern_update(NMVpnConnection *self) { - g_return_val_if_fail(!settings_connection || NM_IS_SETTINGS_CONNECTION(settings_connection), - NULL); - g_return_val_if_fail(NM_IS_DEVICE(parent_device), NULL); - g_return_val_if_fail(specific_object, NULL); + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + int ifindex; + gboolean changed; + int IS_IPv4; - return g_object_new(NM_TYPE_VPN_CONNECTION, - NM_ACTIVE_CONNECTION_INT_SETTINGS_CONNECTION, - settings_connection, - NM_ACTIVE_CONNECTION_INT_DEVICE, - parent_device, - NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, - specific_object, - NM_ACTIVE_CONNECTION_INT_SUBJECT, - subject, - NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON, - activation_reason, - NM_ACTIVE_CONNECTION_VPN, - TRUE, - NM_ACTIVE_CONNECTION_STATE_FLAGS, - (guint) initial_state_flags, - NULL); + ifindex = priv->ifindex_dev; + if (ifindex <= 0) + goto set; + + l3cd = _l3cfg_l3cd_new(self, ifindex); + + changed = FALSE; + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + if (_parent_device_l3cd_add_gateway_route( + l3cd, + IS_IPv4 ? AF_INET : AF_INET6, + nm_active_connection_get_device(NM_ACTIVE_CONNECTION(self)), + &priv->ip_data_x[IS_IPv4].gw_external, + nm_netns_get_platform(priv->netns))) + changed = TRUE; + } + if (!changed) + nm_clear_pointer(&l3cd, nm_l3_config_data_unref); + +set: + if (!_l3cfg_l3cd_set(self, L3CD_TYPE_GW_EXTERN, l3cd)) + return FALSE; + + return TRUE; } const char * @@ -885,23 +1326,370 @@ nm_vpn_connection_get_service(NMVpnConnection *self) return nm_setting_vpn_get_service_type(s_vpn); } -static NM_UTILS_LOOKUP_STR_DEFINE( - _vpn_plugin_failure_to_string, - NMVpnPluginFailure, - NM_UTILS_LOOKUP_DEFAULT(NULL), - NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED, "login-failed"), - NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED, "connect-failed"), - NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG, "bad-ip-config"), ); - -#define vpn_plugin_failure_to_string_a(failure) \ - NM_UTILS_LOOKUP_STR_A(_vpn_plugin_failure_to_string, failure) - static void -plugin_failed(NMVpnConnection *self, guint reason) +_apply_config(NMVpnConnection *self) { NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - _LOGW("VPN plugin: failed: %s (%d)", vpn_plugin_failure_to_string_a(reason), reason); + _LOGT("apply-config"); + + if (priv->ifindex_if > 0) { + nm_platform_link_change_flags(nm_netns_get_platform(priv->netns), + priv->ifindex_if, + IFF_UP, + TRUE); + } + + if (priv->ifindex_dev > 0) { + nm_platform_link_change_flags(nm_netns_get_platform(priv->netns), + priv->ifindex_dev, + IFF_UP, + TRUE); + } + + if (priv->ifindex_if > 0 && priv->ifindex_if != priv->ifindex_dev) { + if (priv->mtu + && priv->mtu + != nm_platform_link_get_mtu(nm_netns_get_platform(priv->netns), + priv->ifindex_if)) + nm_platform_link_set_mtu(nm_netns_get_platform(priv->netns), + priv->ifindex_if, + priv->mtu); + } + + priv->wait_for_pre_up_state = TRUE; + + _l3cfg_l3cd_update_all(self); +} + +static void +fw_change_zone_cb(NMFirewalldManager * firewalld_manager, + NMFirewalldManagerCallId *call_id, + GError * error, + gpointer user_data) +{ + NMVpnConnection * self = user_data; + NMVpnConnectionPrivate *priv; + + g_return_if_fail(NM_IS_VPN_CONNECTION(self)); + + priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + g_return_if_fail(priv->fw_call == call_id); + + priv->fw_call = NULL; + + if (nm_utils_error_is_cancelled(error)) + return; + + _apply_config(self); +} + +static void +_check_complete(NMVpnConnection *self, gboolean success) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + NMConnection * connection; + NMSettingConnection * s_con; + const char * zone; + const char * iface; + + if (priv->vpn_state < STATE_IP_CONFIG_GET || priv->vpn_state > STATE_ACTIVATED) + return; + + if (success) { + if (!priv->generic_config_received + || (priv->ip_data_4.enabled && !priv->l3cds[L3CD_TYPE_IP_4]) + || (priv->ip_data_6.enabled && !priv->l3cds[L3CD_TYPE_IP_6])) { + /* Need to wait more config. */ + return; + } + } + + nm_clear_g_source_inst(&priv->connect_timeout_source); + + if (!success) { + _LOGW("did not receive valid IP config information"); + _set_vpn_state(self, + STATE_FAILED, + NM_ACTIVE_CONNECTION_STATE_REASON_IP_CONFIG_INVALID, + FALSE); + return; + } + + connection = _get_applied_connection(self); + + l3cd = nm_l3_config_data_new_from_connection(nm_netns_get_multi_idx(priv->netns), + nm_vpn_connection_get_ip_ifindex(self, TRUE), + connection); + _l3cfg_l3cd_set(self, L3CD_TYPE_STATIC, l3cd); + + _l3cfg_l3cd_gw_extern_update(self); + + iface = nm_vpn_connection_get_ip_iface(self, FALSE); + + /* Add the tunnel interface to the specified firewall zone */ + if (iface) { + s_con = nm_connection_get_setting_connection(connection); + zone = nm_setting_connection_get_zone(s_con); + + fw_call_cleanup(self); + priv->fw_call = nm_firewalld_manager_add_or_change_zone(nm_firewalld_manager_get(), + iface, + zone, + FALSE, + fw_change_zone_cb, + self); + return; + } + + _apply_config(self); +} + +static gboolean +_vardict_to_addr(int addr_family, GVariant *dict, const char *key, gpointer dst) +{ + guint32 u32; + + if (!NM_IS_IPv4(addr_family)) { + gs_unref_variant GVariant *v = NULL; + + if (g_variant_lookup(dict, key, "@ay", &v)) { + if (nm_ip_addr_set_from_variant(AF_INET6, dst, v, NULL)) + return TRUE; + } + nm_ip_addr_set(AF_INET6, dst, &nm_ip_addr_zero.addr6); + return FALSE; + } + + /* The way we encode IPv4 addresses is not endianness safe. It works well enough + * on the same host and as we know that the VPN plugin sends the address in the + * same endianness that we expect. + * + * But we read a u32 (natively), and that happens to be already in the right + * endianness to be used directly as IPv4 address. */ + if (g_variant_lookup(dict, key, "u", &u32)) { + unaligned_write_ne32(dst, u32); + return TRUE; + } + unaligned_write_ne32(dst, 0); + return FALSE; +} + +guint32 +nm_vpn_connection_get_ip_route_metric(NMVpnConnection *self, int addr_family) +{ + gint64 route_metric = -1; + NMConnection *applied; + + applied = _get_applied_connection(self); + if (!applied) + g_return_val_if_reached(NM_VPN_ROUTE_METRIC_DEFAULT); + + route_metric = nm_setting_ip_config_get_route_metric( + nm_connection_get_setting_ip_config(applied, addr_family)); + return (route_metric >= 0) ? route_metric : NM_VPN_ROUTE_METRIC_DEFAULT; +} + +static guint32 +get_route_table(NMVpnConnection *self, int addr_family, gboolean fallback_main) +{ + NMConnection * connection; + NMSettingIPConfig *s_ip; + guint32 route_table = 0; + + nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); + + connection = _get_applied_connection(self); + if (connection) { + s_ip = nm_connection_get_setting_ip_config(connection, addr_family); + if (s_ip) + route_table = nm_setting_ip_config_get_route_table(s_ip); + } + + return route_table ?: (fallback_main ? RT_TABLE_MAIN : 0); +} + +static gboolean +connect_timeout_cb(gpointer user_data) +{ + NMVpnConnection * self = NM_VPN_CONNECTION(user_data); + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + nm_clear_g_source_inst(&priv->connect_timeout_source); + + /* Cancel activation if it's taken too long */ + if (priv->vpn_state == STATE_CONNECT || priv->vpn_state == STATE_IP_CONFIG_GET) { + _LOGW("connect timeout exceeded"); + _set_vpn_state(self, + STATE_FAILED, + NM_ACTIVE_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, + FALSE); + } + + return G_SOURCE_CONTINUE; +} + +static void +connect_success(NMVpnConnection *self) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + priv->connect_timeout_source = + nm_g_timeout_add_seconds_source(_get_vpn_timeout(self), connect_timeout_cb, self); + + nm_clear_pointer(&priv->connect_hash, g_variant_unref); +} + +static void +connect_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMVpnConnection *self; + gs_unref_variant GVariant *res = NULL; + gs_free_error GError *error = NULL; + + res = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (nm_utils_error_is_cancelled(error)) + return; + + self = NM_VPN_CONNECTION(user_data); + + if (error) { + g_dbus_error_strip_remote_error(error); + _LOGW("failed to connect: '%s'", error->message); + _set_vpn_state(self, + STATE_FAILED, + NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED, + FALSE); + } else + connect_success(self); +} + +static void +connect_interactive_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMVpnConnection * self; + NMVpnConnectionPrivate *priv; + gs_unref_variant GVariant *res = NULL; + gs_free_error GError *error = NULL; + + res = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (nm_utils_error_is_cancelled(error)) + return; + + self = NM_VPN_CONNECTION(user_data); + priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + if (g_error_matches(error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED) + && priv->dbus.bus_name) { + _LOGD("connect: falling back to non-interactive connect"); + _dbus_connection_call(self, + "Connect", + g_variant_new("(@a{sa{sv}})", priv->connect_hash), + G_VARIANT_TYPE("()"), + connect_cb); + return; + } + + if (error) { + _LOGW("connect: failed to connect interactively: '%s'", error->message); + _set_vpn_state(self, + STATE_FAILED, + NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED, + FALSE); + return; + } + + _LOGD("connect: success from ConnectInteractive"); + connect_success(self); +} + +/* Add a username to a hashed connection */ +static GVariant * +_hash_with_username(NMConnection *connection, const char *username) +{ + gs_unref_object NMConnection *dup = NULL; + NMSettingVpn * s_vpn; + + /* Shortcut if we weren't given a username or if there already was one in + * the VPN setting; don't bother duplicating the connection and everything. + */ + s_vpn = nm_connection_get_setting_vpn(connection); + g_return_val_if_fail(s_vpn, NULL); + + if (!username || nm_setting_vpn_get_user_name(s_vpn)) + return nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL); + + dup = nm_simple_connection_new_clone(connection); + nm_assert(dup); + + s_vpn = nm_connection_get_setting_vpn(dup); + g_return_val_if_fail(s_vpn, NULL); + + g_object_set(s_vpn, NM_SETTING_VPN_USER_NAME, username, NULL); + + return nm_connection_to_dbus(dup, NM_CONNECTION_SERIALIZE_ALL); +} + +static void +really_activate(NMVpnConnection *self, const char *username) +{ + NMVpnConnectionPrivate *priv; + GVariantBuilder details; + + g_return_if_fail(NM_IS_VPN_CONNECTION(self)); + + priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + g_return_if_fail(priv->vpn_state == STATE_NEED_AUTH); + + nm_clear_pointer(&priv->connect_hash, g_variant_unref); + priv->connect_hash = _hash_with_username(_get_applied_connection(self), username); + g_variant_ref_sink(priv->connect_hash); + + if (!priv->dbus.bus_name) { + _set_vpn_state(self, + STATE_FAILED, + NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_STOPPED, + FALSE); + return; + } + + /* If at least one agent doesn't support VPN hints, then we can't use + * ConnectInteractive(), because that agent won't be able to pass hints + * from the VPN plugin's interactive secrets requests to the VPN authentication + * dialog and we won't get the secrets we need. In this case fall back to + * the old Connect() call. + */ + if (nm_agent_manager_all_agents_have_capability( + nm_agent_manager_get(), + nm_active_connection_get_subject(NM_ACTIVE_CONNECTION(self)), + NM_SECRET_AGENT_CAPABILITY_VPN_HINTS)) { + _LOGD("connect: allowing interactive secrets as all agents have that capability"); + g_variant_builder_init(&details, G_VARIANT_TYPE_VARDICT); + _dbus_connection_call(self, + "ConnectInteractive", + g_variant_new("(@a{sa{sv}}a{sv})", priv->connect_hash, &details), + G_VARIANT_TYPE("()"), + connect_interactive_cb); + } else { + _LOGD( + "connect: calling old Connect function as not all agents support interactive secrets"); + _dbus_connection_call(self, + "Connect", + g_variant_new("(@a{sa{sv}})", priv->connect_hash), + G_VARIANT_TYPE("()"), + connect_cb); + } + + _set_vpn_state(self, STATE_CONNECT, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); +} + +static void +_dbus_signal_failure_cb(NMVpnConnection *self, guint32 reason) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + _LOGW("dbus: failure: %s (%d)", vpn_plugin_failure_to_string_a(reason), reason); switch (reason) { case NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED: @@ -916,45 +1704,13 @@ plugin_failed(NMVpnConnection *self, guint reason) } } -static NM_UTILS_LOOKUP_STR_DEFINE( - _vpn_service_state_to_string, - NMVpnServiceState, - NM_UTILS_LOOKUP_DEFAULT(NULL), - NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_SERVICE_STATE_UNKNOWN, "unknown"), - NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_SERVICE_STATE_INIT, "init"), - NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_SERVICE_STATE_SHUTDOWN, "shutdown"), - NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_SERVICE_STATE_STARTING, "starting"), - NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_SERVICE_STATE_STARTED, "started"), - NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_SERVICE_STATE_STOPPING, "stopping"), - NM_UTILS_LOOKUP_STR_ITEM(NM_VPN_SERVICE_STATE_STOPPED, "stopped"), ); - -#define vpn_service_state_to_string_a(state) \ - NM_UTILS_LOOKUP_STR_A(_vpn_service_state_to_string, state) - -static NM_UTILS_LOOKUP_STR_DEFINE(_vpn_state_to_string, - VpnState, - NM_UTILS_LOOKUP_DEFAULT(NULL), - NM_UTILS_LOOKUP_STR_ITEM(STATE_UNKNOWN, "unknown"), - NM_UTILS_LOOKUP_STR_ITEM(STATE_WAITING, "waiting"), - NM_UTILS_LOOKUP_STR_ITEM(STATE_PREPARE, "prepare"), - NM_UTILS_LOOKUP_STR_ITEM(STATE_NEED_AUTH, "need-auth"), - NM_UTILS_LOOKUP_STR_ITEM(STATE_CONNECT, "connect"), - NM_UTILS_LOOKUP_STR_ITEM(STATE_IP_CONFIG_GET, "ip-config-get"), - NM_UTILS_LOOKUP_STR_ITEM(STATE_PRE_UP, "pre-up"), - NM_UTILS_LOOKUP_STR_ITEM(STATE_ACTIVATED, "activated"), - NM_UTILS_LOOKUP_STR_ITEM(STATE_DEACTIVATING, "deactivating"), - NM_UTILS_LOOKUP_STR_ITEM(STATE_DISCONNECTED, "disconnected"), - NM_UTILS_LOOKUP_STR_ITEM(STATE_FAILED, "failed"), ); - -#define vpn_state_to_string_a(state) NM_UTILS_LOOKUP_STR_A(_vpn_state_to_string, state) - static void -plugin_state_changed(NMVpnConnection *self, NMVpnServiceState new_service_state) +_dbus_signal_state_changed_cb(NMVpnConnection *self, guint32 new_service_state) { NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); NMVpnServiceState old_service_state = priv->service_state; - _LOGI("VPN plugin: state changed: %s (%d)", + _LOGD("dbus: state changed: %s (%d)", vpn_service_state_to_string_a(new_service_state), new_service_state); priv->service_state = new_service_state; @@ -985,1233 +1741,713 @@ plugin_state_changed(NMVpnConnection *self, NMVpnServiceState new_service_state) } } -static void -print_vpn_config(NMVpnConnection *self) -{ - NMVpnConnectionPrivate * priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - const NMPlatformIP4Address *address4; - const NMPlatformIP6Address *address6; - char * dns_domain = NULL; - guint32 num, i; - char b1[NM_UTILS_INET_ADDRSTRLEN]; - char b2[NM_UTILS_INET_ADDRSTRLEN]; - NMDedupMultiIter ipconf_iter; - - if (priv->ip4_external_gw) { - _LOGI("Data: VPN Gateway: %s", _nm_utils_inet4_ntop(priv->ip4_external_gw, b1)); - } else if (priv->ip6_external_gw) { - _LOGI("Data: VPN Gateway: %s", _nm_utils_inet6_ntop(priv->ip6_external_gw, b1)); - } - - _LOGI("Data: Tunnel Device: %s%s%s", NM_PRINT_FMT_QUOTE_STRING(priv->ip_iface)); - - if (priv->ip4_config) { - const NMPlatformIP4Route *route; - - _LOGI("Data: IPv4 configuration:"); - - address4 = nm_ip4_config_get_first_address(priv->ip4_config); - nm_assert(address4); - - if (priv->ip4_internal_gw) - _LOGI("Data: Internal Gateway: %s", _nm_utils_inet4_ntop(priv->ip4_internal_gw, b1)); - _LOGI("Data: Internal Address: %s", - address4 ? _nm_utils_inet4_ntop(address4->address, b1) : "??"); - _LOGI("Data: Internal Prefix: %d", address4 ? (int) address4->plen : -1); - _LOGI("Data: Internal Point-to-Point Address: %s", - _nm_utils_inet4_ntop(address4->peer_address, b1)); - - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, priv->ip4_config, &route) { - _LOGI("Data: Static Route: %s/%d Next Hop: %s", - _nm_utils_inet4_ntop(route->network, b1), - route->plen, - _nm_utils_inet4_ntop(route->gateway, b2)); - } - - num = nm_ip4_config_get_num_nameservers(priv->ip4_config); - for (i = 0; i < num; i++) { - _LOGI("Data: Internal DNS: %s", - _nm_utils_inet4_ntop(nm_ip4_config_get_nameserver(priv->ip4_config, i), b1)); - } - - if (nm_ip4_config_get_num_domains(priv->ip4_config) > 0) - dns_domain = (char *) nm_ip4_config_get_domain(priv->ip4_config, 0); - - _LOGI("Data: DNS Domain: '%s'", dns_domain ?: "(none)"); - } else - _LOGI("Data: No IPv4 configuration"); - - if (priv->ip6_config) { - const NMPlatformIP6Route *route; - - _LOGI("Data: IPv6 configuration:"); - - address6 = nm_ip6_config_get_first_address(priv->ip6_config); - nm_assert(address6); - - if (priv->ip6_internal_gw) - _LOGI("Data: Internal Gateway: %s", _nm_utils_inet6_ntop(priv->ip6_internal_gw, b1)); - _LOGI("Data: Internal Address: %s", _nm_utils_inet6_ntop(&address6->address, b1)); - _LOGI("Data: Internal Prefix: %d", address6->plen); - _LOGI("Data: Internal Point-to-Point Address: %s", - _nm_utils_inet6_ntop(&address6->peer_address, b1)); - - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, priv->ip6_config, &route) { - _LOGI("Data: Static Route: %s/%d Next Hop: %s", - _nm_utils_inet6_ntop(&route->network, b1), - route->plen, - _nm_utils_inet6_ntop(&route->gateway, b2)); - } - - num = nm_ip6_config_get_num_nameservers(priv->ip6_config); - for (i = 0; i < num; i++) { - _LOGI("Data: Internal DNS: %s", - _nm_utils_inet6_ntop(nm_ip6_config_get_nameserver(priv->ip6_config, i), b1)); - } - - if (nm_ip6_config_get_num_domains(priv->ip6_config) > 0) - dns_domain = (char *) nm_ip6_config_get_domain(priv->ip6_config, 0); - - _LOGI("Data: DNS Domain: '%s'", dns_domain ?: "(none)"); - } else - _LOGI("Data: No IPv6 configuration"); - - if (priv->banner && strlen(priv->banner)) { - _LOGI("Data: Login Banner:"); - _LOGI("Data: -----------------------------------------"); - _LOGI("Data: %s", priv->banner); - _LOGI("Data: -----------------------------------------"); - } -} - -static void -apply_parent_device_config(NMVpnConnection *self) -{ - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - NMDevice * parent_dev = nm_active_connection_get_device(NM_ACTIVE_CONNECTION(self)); - int ifindex; - NMIP4Config *vpn4_parent_config = NULL; - NMIP6Config *vpn6_parent_config = NULL; - - ifindex = nm_device_get_ip_ifindex(parent_dev); - if (ifindex > 0) { - /* If the VPN didn't return a network interface, it is a route-based - * VPN (like kernel IPSec) and all IP addressing and routing should - * be done on the parent interface instead. - */ - if (priv->ip4_config) { - vpn4_parent_config = nm_ip4_config_new(nm_netns_get_multi_idx(priv->netns), ifindex); - if (priv->ip_ifindex <= 0) - nm_ip4_config_merge(vpn4_parent_config, - priv->ip4_config, - NM_IP_CONFIG_MERGE_NO_DNS, - 0); - } - if (priv->ip6_config) { - vpn6_parent_config = nm_ip6_config_new(nm_netns_get_multi_idx(priv->netns), ifindex); - if (priv->ip_ifindex <= 0) - nm_ip6_config_merge(vpn6_parent_config, - priv->ip6_config, - NM_IP_CONFIG_MERGE_NO_DNS, - 0); - } - } - - /* Add any explicit route to the VPN gateway through the parent device */ - if (vpn4_parent_config && priv->ip4_external_gw) { - add_ip4_vpn_gateway_route(vpn4_parent_config, - parent_dev, - priv->ip4_external_gw, - nm_netns_get_platform(priv->netns)); - } - if (vpn6_parent_config && priv->ip6_external_gw) { - add_ip6_vpn_gateway_route(vpn6_parent_config, - parent_dev, - priv->ip6_external_gw, - nm_netns_get_platform(priv->netns)); - } - - nm_device_replace_vpn4_config(parent_dev, priv->last_device_ip4_config, vpn4_parent_config); - g_clear_object(&priv->last_device_ip4_config); - priv->last_device_ip4_config = vpn4_parent_config; - - nm_device_replace_vpn6_config(parent_dev, priv->last_device_ip6_config, vpn6_parent_config); - g_clear_object(&priv->last_device_ip6_config); - priv->last_device_ip6_config = vpn6_parent_config; -} - static gboolean -nm_vpn_connection_apply_config(NMVpnConnection *self) +_config_process_generic(NMVpnConnection *self, GVariant *dict) { - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + nm_auto_g_object_thaw_notify GObject *self_thaw = NULL; + NMVpnConnectionPrivate * priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + int IS_IPv4; + int ip_ifindex = 0; + const char * v_str; + guint32 v_u32; + gboolean v_b; - apply_parent_device_config(self); + if (g_variant_lookup(dict, NM_VPN_PLUGIN_CONFIG_TUNDEV, "&s", &v_str)) { + const char *iface = NULL; - if (priv->ip_ifindex > 0) { - nm_platform_link_change_flags(nm_netns_get_platform(priv->netns), - priv->ip_ifindex, - IFF_UP, - TRUE); + /* Backwards compat with NM-openswan/libreswan */ + if (!NM_IN_STRSET(v_str, "", "_none_")) + iface = v_str; - if (priv->ip4_config) { - nm_assert(priv->ip_ifindex == nm_ip4_config_get_ifindex(priv->ip4_config)); - if (!nm_ip4_config_commit(priv->ip4_config, - nm_netns_get_platform(priv->netns), - get_route_table(self, AF_INET, FALSE) - ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL - : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN)) + if (iface) { + NMPlatform *platform = nm_netns_get_platform(priv->netns); + + ip_ifindex = nm_platform_link_get_ifindex(platform, iface); + if (ip_ifindex <= 0) { + nm_platform_process_events(platform); + ip_ifindex = nm_platform_link_get_ifindex(platform, iface); + } + if (ip_ifindex <= 0) { + _LOGW("config: failed to look up VPN interface index for \"%s\"", iface); return FALSE; - nm_platform_ip4_dev_route_blacklist_set(nm_netns_get_platform(priv->netns), - priv->ip_ifindex, - priv->ip4_dev_route_blacklist); - } - - if (priv->ip6_config) { - nm_assert(priv->ip_ifindex == nm_ip6_config_get_ifindex(priv->ip6_config)); - if (!nm_ip6_config_commit(priv->ip6_config, - nm_netns_get_platform(priv->netns), - get_route_table(self, AF_INET6, FALSE) - ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL - : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN, - NULL)) - return FALSE; - } - - if (priv->mtu - && priv->mtu - != nm_platform_link_get_mtu(nm_netns_get_platform(priv->netns), - priv->ip_ifindex)) - nm_platform_link_set_mtu(nm_netns_get_platform(priv->netns), - priv->ip_ifindex, - priv->mtu); - } - - _LOGI("VPN connection: (IP Config Get) complete"); - if (priv->vpn_state < STATE_PRE_UP) - _set_vpn_state(self, STATE_PRE_UP, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); - return TRUE; -} - -static void -_cleanup_failed_config(NMVpnConnection *self) -{ - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - - nm_dbus_object_clear_and_unexport(&priv->ip4_config); - nm_dbus_object_clear_and_unexport(&priv->ip6_config); - - _LOGW("VPN connection: did not receive valid IP config information"); - _set_vpn_state(self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_IP_CONFIG_INVALID, FALSE); -} - -static void -fw_change_zone_cb(NMFirewalldManager * firewalld_manager, - NMFirewalldManagerCallId *call_id, - GError * error, - gpointer user_data) -{ - NMVpnConnection * self = user_data; - NMVpnConnectionPrivate *priv; - - g_return_if_fail(NM_IS_VPN_CONNECTION(self)); - - priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - g_return_if_fail(priv->fw_call == call_id); - - priv->fw_call = NULL; - - if (nm_utils_error_is_cancelled(error)) - return; - - if (error) { - // FIXME: fail the activation? - } - - if (!nm_vpn_connection_apply_config(self)) - _cleanup_failed_config(self); -} - -static void -nm_vpn_connection_config_maybe_complete(NMVpnConnection *self, gboolean success) -{ - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - NMConnection * base_con; - NMSettingConnection * s_con; - const char * zone; - - if (priv->vpn_state < STATE_IP_CONFIG_GET || priv->vpn_state > STATE_ACTIVATED) - return; - - if (success) { - if ((priv->has_ip4 && !priv->ip4_config) || (priv->has_ip6 && !priv->ip6_config)) { - /* Need to wait for other config */ - return; + } } } - nm_clear_g_source(&priv->connect_timeout); + self_thaw = nm_g_object_freeze_notify(self); - if (success) { - print_vpn_config(self); + _set_ip_ifindex(self, ip_ifindex, TRUE); + _set_ip_ifindex(self, _get_ifindex_for_device(self), FALSE); - /* Add the tunnel interface to the specified firewall zone */ - if (priv->ip_iface) { - base_con = _get_applied_connection(self); - s_con = nm_connection_get_setting_connection(base_con); - zone = nm_setting_connection_get_zone(s_con); - - _LOGD("setting firewall zone %s%s%s for '%s'", - NM_PRINT_FMT_QUOTED(zone, "'", zone, "'", "(default)"), - priv->ip_iface); - fw_call_cleanup(self); - priv->fw_call = nm_firewalld_manager_add_or_change_zone(nm_firewalld_manager_get(), - priv->ip_iface, - zone, - FALSE, - fw_change_zone_cb, - self); - return; - } else if (nm_vpn_connection_apply_config(self)) - return; + ip_ifindex = nm_vpn_connection_get_ip_ifindex(self, TRUE); + if (ip_ifindex <= 0) { + _LOGW("config: no ip-ifindex for the VPN"); + return FALSE; } - _cleanup_failed_config(self); -} + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + NML3Cfg * l3cfg = priv->l3cfg_if ?: priv->l3cfg_dev; + gs_unref_object NMIPConfig *ipconfig_old = NULL; -static gboolean -ip6_addr_from_variant(GVariant *v, struct in6_addr *addr) -{ - const guint8 *bytes; - gsize len; - - g_return_val_if_fail(v, FALSE); - g_return_val_if_fail(addr, FALSE); - - if (g_variant_is_of_type(v, G_VARIANT_TYPE("ay"))) { - bytes = g_variant_get_fixed_array(v, &len, sizeof(guint8)); - if (len == sizeof(struct in6_addr) && !IN6_IS_ADDR_UNSPECIFIED(bytes)) { - memcpy(addr, bytes, len); - return TRUE; + ipconfig_old = g_steal_pointer(&priv->ip_data_x[IS_IPv4].ip_config); + if (l3cfg) { + priv->ip_data_x[IS_IPv4].ip_config = + nm_l3cfg_ipconfig_acquire(l3cfg, IS_IPv4 ? AF_INET : AF_INET6); } + g_object_notify(G_OBJECT(self), + IS_IPv4 ? NM_ACTIVE_CONNECTION_IP4_CONFIG + : NM_ACTIVE_CONNECTION_IP6_CONFIG); } - return FALSE; -} -static struct in6_addr * -ip6_addr_dup_from_variant(GVariant *v) -{ - struct in6_addr *addr; - - addr = g_malloc0(sizeof(*addr)); - if (ip6_addr_from_variant(v, addr)) - return addr; - g_free(addr); - return NULL; -} - -static gboolean -process_generic_config(NMVpnConnection *self, GVariant *dict) -{ - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - const char * str; - GVariant * v; - guint32 u32; - gboolean b; - - if (g_variant_lookup(dict, NM_VPN_PLUGIN_CAN_PERSIST, "b", &b) && b) { + if (g_variant_lookup(dict, NM_VPN_PLUGIN_CAN_PERSIST, "b", &v_b) && v_b) { /* Defaults to FALSE, so only let service indicate TRUE */ priv->service_can_persist = TRUE; } - nm_clear_g_free(&priv->ip_iface); - priv->ip_ifindex = 0; - - if (g_variant_lookup(dict, NM_VPN_PLUGIN_CONFIG_TUNDEV, "&s", &str)) { - /* Backwards compat with NM-openswan */ - if (g_strcmp0(str, "_none_") != 0) - priv->ip_iface = g_strdup(str); - } - - if (priv->ip_iface) { - /* Grab the interface index for address/routing operations */ - priv->ip_ifindex = - nm_platform_link_get_ifindex(nm_netns_get_platform(priv->netns), priv->ip_iface); - if (priv->ip_ifindex <= 0) { - nm_platform_process_events(nm_netns_get_platform(priv->netns)); - priv->ip_ifindex = - nm_platform_link_get_ifindex(nm_netns_get_platform(priv->netns), priv->ip_iface); - } - if (priv->ip_ifindex <= 0) { - _LOGE("failed to look up VPN interface index for \"%s\"", priv->ip_iface); - nm_clear_g_free(&priv->ip_iface); - priv->ip_ifindex = 0; - nm_vpn_connection_config_maybe_complete(self, FALSE); - return FALSE; - } - } - - nm_clear_g_free(&priv->banner); - if (g_variant_lookup(dict, NM_VPN_PLUGIN_CONFIG_BANNER, "&s", &str)) { - priv->banner = g_strdup(str); + if (!g_variant_lookup(dict, NM_VPN_PLUGIN_CONFIG_BANNER, "&s", &v_str)) + v_str = NULL; + if (nm_strdup_reset(&priv->banner, v_str)) _notify(self, PROP_BANNER); + + _vardict_to_addr(AF_INET, dict, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, &priv->ip_data_4.gw_external); + _vardict_to_addr(AF_INET6, + dict, + NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, + &priv->ip_data_6.gw_external); + + if (nm_ip_addr_is_null(AF_INET, &priv->ip_data_4.gw_external) + && nm_ip_addr_is_null(AF_INET6, &priv->ip_data_6.gw_external)) { + _LOGW("config: no VPN gateway address received"); + return FALSE; } - /* Proxy Config */ - g_clear_object(&priv->proxy_config); - priv->proxy_config = nm_proxy_config_new(); + l3cd = _l3cfg_l3cd_new(self, ip_ifindex); - if (g_variant_lookup(dict, NM_VPN_PLUGIN_CONFIG_PROXY_PAC, "&s", &str)) { - nm_proxy_config_set_method(priv->proxy_config, NM_PROXY_CONFIG_METHOD_AUTO); - nm_proxy_config_set_pac_url(priv->proxy_config, str); + if (g_variant_lookup(dict, NM_VPN_PLUGIN_CONFIG_PROXY_PAC, "&s", &v_str)) { + nm_l3_config_data_set_proxy_method(l3cd, NM_PROXY_CONFIG_METHOD_AUTO); + nm_l3_config_data_set_proxy_pac_url(l3cd, v_str); } else - nm_proxy_config_set_method(priv->proxy_config, NM_PROXY_CONFIG_METHOD_NONE); + nm_l3_config_data_set_proxy_method(l3cd, NM_PROXY_CONFIG_METHOD_NONE); - /* User overrides if any from the NMConnection's Proxy settings */ - nm_proxy_config_merge_setting(priv->proxy_config, - nm_connection_get_setting_proxy(_get_applied_connection(self))); + if (g_variant_lookup(dict, NM_VPN_PLUGIN_CONFIG_MTU, "u", &v_u32)) + priv->mtu = v_u32; + else + priv->mtu = 0; - /* External world-visible address of the VPN server */ - priv->ip4_external_gw = 0; - nm_clear_g_free(&priv->ip6_external_gw); + priv->generic_config_received = TRUE; - if (g_variant_lookup(dict, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, "u", &u32)) { - priv->ip4_external_gw = u32; - } else if (g_variant_lookup(dict, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, "@ay", &v)) { - priv->ip6_external_gw = ip6_addr_dup_from_variant(v); - g_variant_unref(v); + nm_g_object_thaw_notify_clear(&self_thaw); - if (!priv->ip6_external_gw) { - _LOGE("Invalid IPv6 VPN gateway address received"); - nm_vpn_connection_config_maybe_complete(self, FALSE); - return FALSE; - } - } - - priv->mtu = 0; - if (g_variant_lookup(dict, NM_VPN_PLUGIN_CONFIG_MTU, "u", &u32)) - priv->mtu = u32; + _l3cfg_l3cd_set(self, L3CD_TYPE_GENERIC, l3cd); return TRUE; } static void -nm_vpn_connection_config_get(NMVpnConnection *self, GVariant *dict) +_dbus_signal_config_cb(NMVpnConnection *self, GVariant *dict) { NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - gboolean b; + gboolean v_b; - g_return_if_fail(dict && g_variant_is_of_type(dict, G_VARIANT_TYPE_VARDICT)); + g_return_if_fail(dict); - _LOGI("VPN connection: (IP Config Get) reply received."); + if (!g_variant_is_of_type(dict, G_VARIANT_TYPE_VARDICT)) { + _LOGD("config: ignore invalid configuration type"); + return; + } + + if (priv->vpn_state < STATE_NEED_AUTH) { + /* Only list to this signals during and after connection */ + _LOGD("config: ignore configuration before need-auth state"); + return; + } + + if (g_variant_lookup(dict, NM_VPN_PLUGIN_CONFIG_HAS_IP4, "b", &v_b)) + priv->ip_data_4.enabled = v_b; + else + priv->ip_data_4.enabled = FALSE; + + if (g_variant_lookup(dict, NM_VPN_PLUGIN_CONFIG_HAS_IP6, "b", &v_b)) + priv->ip_data_6.enabled = v_b; + else + priv->ip_data_6.enabled = FALSE; + + _LOGD("config: reply received (IPv4:%s, IPv6:%s)", + priv->ip_data_4.enabled ? "on" : "off", + priv->ip_data_6.enabled ? "on" : "off"); if (priv->vpn_state == STATE_CONNECT) _set_vpn_state(self, STATE_IP_CONFIG_GET, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); - if (!process_generic_config(self, dict)) + if (!_config_process_generic(self, dict)) { + _check_complete(self, FALSE); return; - - /* Note whether to expect IPv4 and IPv6 configs */ - priv->has_ip4 = FALSE; - if (g_variant_lookup(dict, NM_VPN_PLUGIN_CONFIG_HAS_IP4, "b", &b)) - priv->has_ip4 = b; - nm_dbus_object_clear_and_unexport(&priv->ip4_config); - - priv->has_ip6 = FALSE; - if (g_variant_lookup(dict, NM_VPN_PLUGIN_CONFIG_HAS_IP6, "b", &b)) - priv->has_ip6 = b; - nm_dbus_object_clear_and_unexport(&priv->ip6_config); - - nm_vpn_connection_config_maybe_complete(self, TRUE); -} - -guint32 -nm_vpn_connection_get_ip4_route_metric(NMVpnConnection *self) -{ - gint64 route_metric; - NMConnection *applied; - - applied = _get_applied_connection(self); - route_metric = - nm_setting_ip_config_get_route_metric(nm_connection_get_setting_ip4_config(applied)); - - return (route_metric >= 0) ? route_metric : NM_VPN_ROUTE_METRIC_DEFAULT; -} - -guint32 -nm_vpn_connection_get_ip6_route_metric(NMVpnConnection *self) -{ - gint64 route_metric; - NMConnection *applied; - - applied = _get_applied_connection(self); - route_metric = - nm_setting_ip_config_get_route_metric(nm_connection_get_setting_ip6_config(applied)); - - return (route_metric >= 0) ? route_metric : NM_VPN_ROUTE_METRIC_DEFAULT; -} - -static guint32 -get_route_table(NMVpnConnection *self, int addr_family, gboolean fallback_main) -{ - NMConnection * connection; - NMSettingIPConfig *s_ip; - guint32 route_table = 0; - - nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); - - connection = _get_applied_connection(self); - if (connection) { - s_ip = nm_connection_get_setting_ip_config(connection, addr_family); - if (s_ip) - route_table = nm_setting_ip_config_get_route_table(s_ip); } - return route_table ?: (fallback_main ? RT_TABLE_MAIN : 0); -} - -static gboolean -_is_device_vrf(NMVpnConnection *self) -{ - NMDevice *parent; - NMDevice *master; - - parent = nm_active_connection_get_device(NM_ACTIVE_CONNECTION(self)); - if (!parent) - return FALSE; - - master = nm_device_get_master(parent); - return master && nm_device_get_link_type(master) == NM_LINK_TYPE_VRF; + _check_complete(self, TRUE); } static void -nm_vpn_connection_ip4_config_get(NMVpnConnection *self, GVariant *dict) +_dbus_signal_ip_config_cb(NMVpnConnection *self, int addr_family, GVariant *dict) { - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - NMPlatformIP4Address address; - guint32 u32, route_metric; - NMSettingIPConfig * s_ip; - NMSettingConnection * s_con; - guint32 route_table; - NMIP4Config * config; - GVariantIter * iter; - const char * str; - GVariant * v; - gboolean b; - int ip_ifindex; - guint32 mss = 0; - gboolean never_default = FALSE; + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + GVariantIter * var_iter; + guint32 u32; + const char * v_str; + NMIPAddr v_addr; + GVariant * v; + gboolean v_b; + int ip_ifindex; + guint32 mss = 0; + gboolean never_default; + NMPlatformIPXAddress address; g_return_if_fail(dict && g_variant_is_of_type(dict, G_VARIANT_TYPE_VARDICT)); - if (priv->vpn_state == STATE_CONNECT) - _set_vpn_state(self, STATE_IP_CONFIG_GET, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); - - if (priv->vpn_state > STATE_ACTIVATED) { - _LOGI("VPN connection: (IP4 Config Get) ignoring, the connection is no longer active"); + if (priv->vpn_state < STATE_NEED_AUTH) { + _LOGD("config%c: ignoring, the connection is not in need-auth state", + nm_utils_addr_family_to_char(addr_family)); return; } - if (priv->has_ip4) { - _LOGI("VPN connection: (IP4 Config Get) reply received"); + if (priv->vpn_state > STATE_ACTIVATED) { + _LOGD("config%c: ignoring, the connection is no longer active", + nm_utils_addr_family_to_char(addr_family)); + return; + } + + if (IS_IPv4) { + if (priv->generic_config_received) { + _LOGD("config4: reply received"); + + if (g_variant_n_children(dict) == 0) { + priv->ip_data_4.enabled = FALSE; + _check_complete(self, TRUE); + return; + } + } else { + _LOGD("config4: reply received (old style)"); + + /* In the old API, the generic and IPv4 configuration items + * were mixed together. + */ + if (!_config_process_generic(self, dict)) { + _check_complete(self, FALSE); + return; + } + + priv->ip_data_4.enabled = TRUE; + priv->ip_data_6.enabled = FALSE; + } + } else { + _LOGD("config6: reply received"); if (g_variant_n_children(dict) == 0) { - priv->has_ip4 = FALSE; - nm_vpn_connection_config_maybe_complete(self, TRUE); + priv->ip_data_6.enabled = FALSE; + _check_complete(self, TRUE); return; } - } else { - _LOGI("VPN connection: (IP4 Config Get) reply received from old-style plugin"); + } - /* In the old API, the generic and IPv4 configuration items - * were mixed together. - */ - if (!process_generic_config(self, dict)) - return; - - priv->has_ip4 = TRUE; - priv->has_ip6 = FALSE; + if (priv->vpn_state == STATE_CONNECT) { + _set_vpn_state(self, STATE_IP_CONFIG_GET, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); } ip_ifindex = nm_vpn_connection_get_ip_ifindex(self, TRUE); if (ip_ifindex <= 0) g_return_if_reached(); - config = nm_ip4_config_new(nm_netns_get_multi_idx(priv->netns), ip_ifindex); - nm_ip4_config_set_dns_priority(config, NM_DNS_PRIORITY_DEFAULT_VPN); + l3cd = _l3cfg_l3cd_new(self, ip_ifindex); - memset(&address, 0, sizeof(address)); - address.plen = 24; + nm_l3_config_data_set_dns_priority(l3cd, AF_INET, NM_DNS_PRIORITY_DEFAULT_VPN); - /* Internal address of the VPN subnet's gateway */ - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY, "u", &u32)) - priv->ip4_internal_gw = u32; - - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, "u", &u32)) - address.address = u32; - - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_PTP, "u", &u32)) - address.peer_address = u32; - else - address.peer_address = address.address; - - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, "u", &u32)) - address.plen = u32; - - if (address.address && address.plen && address.plen <= 32) { - address.addr_source = NM_IP_CONFIG_SOURCE_VPN; - nm_ip4_config_add_address(config, &address); + if (IS_IPv4) { + address.a4 = (NMPlatformIP4Address){ + .plen = 24, + }; } else { - _LOGW("invalid IP4 config received!"); - g_object_unref(config); - nm_vpn_connection_config_maybe_complete(self, FALSE); + address.a6 = (NMPlatformIP6Address){ + .plen = 128, + }; + } + + _vardict_to_addr(addr_family, + dict, + IS_IPv4 ? NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY + : NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY, + &priv->ip_data_x[IS_IPv4].gw_internal); + + _vardict_to_addr(addr_family, + dict, + IS_IPv4 ? NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS : NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS, + address.ax.address_ptr); + + if (!_vardict_to_addr(addr_family, + dict, + IS_IPv4 ? NM_VPN_PLUGIN_IP4_CONFIG_PTP : NM_VPN_PLUGIN_IP6_CONFIG_PTP, + nm_platform_ip_address_get_peer_address(addr_family, &address.ax))) { + if (IS_IPv4) + address.a4.peer_address = address.a4.address; + } + + if (g_variant_lookup(dict, + IS_IPv4 ? NM_VPN_PLUGIN_IP4_CONFIG_PREFIX + : NM_VPN_PLUGIN_IP6_CONFIG_PREFIX, + "u", + &u32)) + address.ax.plen = u32; + + if (address.ax.plen > 0 && address.ax.plen <= (IS_IPv4 ? 32 : 128) + && !nm_ip_addr_is_null(addr_family, &address.ax.address_ptr)) { + address.ax.addr_source = NM_IP_CONFIG_SOURCE_VPN; + nm_l3_config_data_add_address(l3cd, addr_family, NULL, &address.ax); + } else { + _LOGW("invalid IP%c config received: no valid IP address/prefix", + nm_utils_addr_family_to_char(addr_family)); + _check_complete(self, FALSE); return; } - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_DNS, "au", &iter)) { - while (g_variant_iter_next(iter, "u", &u32)) - nm_ip4_config_add_nameserver(config, u32); - g_variant_iter_free(iter); + 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); + 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); + g_variant_unref(v); + } + g_variant_iter_free(var_iter); + } } - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_NBNS, "au", &iter)) { - while (g_variant_iter_next(iter, "u", &u32)) - nm_ip4_config_add_wins(config, u32); - g_variant_iter_free(iter); + if (IS_IPv4) { + if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_NBNS, "au", &var_iter)) { + while (g_variant_iter_next(var_iter, "u", &u32)) + nm_l3_config_data_add_wins(l3cd, u32); + g_variant_iter_free(var_iter); + } } - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_MSS, "u", &u32)) + if (g_variant_lookup(dict, + IS_IPv4 ? NM_VPN_PLUGIN_IP4_CONFIG_MSS : NM_VPN_PLUGIN_IP6_CONFIG_MSS, + "u", + &u32)) mss = u32; - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN, "&s", &str)) - nm_ip4_config_add_domain(config, str); + if (g_variant_lookup(dict, + IS_IPv4 ? NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN + : NM_VPN_PLUGIN_IP6_CONFIG_DOMAIN, + "&s", + &v_str)) + nm_l3_config_data_add_domain(l3cd, addr_family, v_str); - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS, "as", &iter)) { - while (g_variant_iter_next(iter, "&s", &str)) - nm_ip4_config_add_domain(config, str); - g_variant_iter_free(iter); + if (g_variant_lookup(dict, + IS_IPv4 ? NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS + : NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS, + "as", + &var_iter)) { + while (g_variant_iter_next(var_iter, "&s", &v_str)) + nm_l3_config_data_add_domain(l3cd, addr_family, v_str); + g_variant_iter_free(var_iter); } - route_table = get_route_table(self, AF_INET, TRUE); - route_metric = nm_vpn_connection_get_ip4_route_metric(self); - s_ip = nm_connection_get_setting_ip4_config(_get_applied_connection(self)); - s_con = nm_connection_get_setting_connection(_get_applied_connection(self)); + if (g_variant_lookup(dict, + IS_IPv4 ? NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES + : NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES, + "b", + &v_b) + && v_b) { + if (priv->l3cds[L3CD_TYPE_IP_X(IS_IPv4)]) { + NMDedupMultiIter ipconf_iter; + const NMPObject *route; - if (nm_setting_ip_config_get_ignore_auto_routes(s_ip)) { - /* ignore VPN routes */ - } else if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES, "b", &b) && b) { - if (priv->ip4_config) { - NMDedupMultiIter ipconf_iter; - const NMPlatformIP4Route *route; - - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, priv->ip4_config, &route) - nm_ip4_config_add_route(config, route, NULL); + nm_l3_config_data_iter_obj_for_each (&ipconf_iter, + priv->l3cds[L3CD_TYPE_IP_X(IS_IPv4)], + &route, + NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)) + nm_l3_config_data_add_route(l3cd, addr_family, route, NULL); } - } else if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, "aau", &iter)) { - while (g_variant_iter_next(iter, "@au", &v)) { - NMPlatformIP4Route route = { - 0, - }; - guint32 plen; + } else if (IS_IPv4 ? g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, "aau", &var_iter) + : g_variant_lookup(dict, + NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, + "a(ayuayu)", + &var_iter)) { + _nm_unused nm_auto_free_variant_iter GVariantIter *var_iter_ref_owner = var_iter; + NMPlatformIPXRoute route = {}; + guint32 plen; + GVariant * next_hop; + GVariant * dest; + guint32 prefix; + guint32 metric; - switch (g_variant_n_children(v)) { - case 5: - g_variant_get_child(v, 4, "u", &route.pref_src); - /* fall-through */ - case 4: - g_variant_get_child(v, 0, "u", &route.network); - g_variant_get_child(v, 1, "u", &plen); - g_variant_get_child(v, 2, "u", &route.gateway); - /* 4th item is unused route metric */ - route.table_coerced = nm_platform_route_table_coerce(route_table); - route.metric = route_metric; - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; + if (IS_IPv4) { + while (g_variant_iter_next(var_iter, "@au", &v)) { + _nm_unused gs_unref_variant GVariant *v_ref_owner = v; - if (plen > 32) + switch (g_variant_n_children(v)) { + case 5: + g_variant_get_child(v, 4, "u", &route.r4.pref_src); + /* fall-through */ + case 4: + g_variant_get_child(v, 0, "u", &route.r4.network); + g_variant_get_child(v, 1, "u", &plen); + g_variant_get_child(v, 2, "u", &route.r4.gateway); + /* 4th item is unused route metric */ + route.r4.table_any = TRUE; + route.r4.metric_any = TRUE; + route.r4.rt_source = NM_IP_CONFIG_SOURCE_VPN; + + if (plen > 32) + break; + route.r4.plen = plen; + route.r4.network = + nm_utils_ip4_address_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 + && route.r4.plen == 32) { + /* Ignore host routes to the VPN gateway since NM adds one itself + * below. Since NM knows more about the routing situation than + * the VPN server, we want to use the NM created route instead of + * whatever the server provides. + */ + break; + } + + nm_l3_config_data_add_route_4(l3cd, &route.r4); break; - route.plen = plen; - route.network = nm_utils_ip4_address_clear_host_address(route.network, plen); - - if (priv->ip4_external_gw && route.network == priv->ip4_external_gw - && route.plen == 32) { - /* Ignore host routes to the VPN gateway since NM adds one itself - * below. Since NM knows more about the routing situation than - * the VPN server, we want to use the NM created route instead of - * whatever the server provides. - */ + default: break; } - - nm_ip4_config_add_route(config, &route, NULL); - break; - default: - break; } - g_variant_unref(v); - } - g_variant_iter_free(iter); - } + } else { + while ( + g_variant_iter_next(var_iter, "(@ayu@ayu)", &dest, &prefix, &next_hop, &metric)) { + _nm_unused gs_unref_variant GVariant *next_hop_ref_owner = next_hop; + _nm_unused gs_unref_variant GVariant *dest_ref_owner = dest; - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT, "b", &b)) - never_default = b; + if (prefix > 128) + continue; - /* Merge in user overrides from the NMConnection's IPv4 setting */ - nm_ip4_config_merge_setting(config, - s_ip, - nm_setting_connection_get_mdns(s_con), - nm_setting_connection_get_llmnr(s_con), - nm_setting_connection_get_dns_over_tls(s_con), - route_table, - route_metric); + route.r6 = (NMPlatformIP6Route){ + .plen = prefix, + .table_any = TRUE, + .metric_any = TRUE, + .rt_source = NM_IP_CONFIG_SOURCE_VPN, + }; - if (!never_default && !nm_setting_ip_config_get_never_default(s_ip)) { - const NMPlatformIP4Route r = { - .ifindex = ip_ifindex, - .rt_source = NM_IP_CONFIG_SOURCE_VPN, - .gateway = priv->ip4_internal_gw, - .table_coerced = nm_platform_route_table_coerce(route_table), - .metric = route_metric, - .mss = mss, - }; + if (!nm_ip_addr_set_from_variant(AF_INET6, &route.r6.network, dest, NULL)) + continue; - nm_ip4_config_add_route(config, &r, NULL); - } + nm_ip_addr_set_from_variant(AF_INET6, &route.r6.gateway, next_hop, NULL); - nm_clear_pointer(&priv->ip4_dev_route_blacklist, g_ptr_array_unref); + nm_utils_ip6_address_clear_host_address(&route.r6.network, + &route.r6.network, + route.r6.plen); - nm_ip4_config_add_dependent_routes(config, - route_table, - nm_vpn_connection_get_ip4_route_metric(self), - _is_device_vrf(self), - &priv->ip4_dev_route_blacklist); + 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) + && route.r6.plen == 128) { + /* Ignore host routes to the VPN gateway since NM adds one itself. + * Since NM knows more about the routing situation than the VPN + * server, we want to use the NM created route instead of whatever + * the server provides. + */ + continue; + } - if (priv->ip4_config) { - nm_ip4_config_replace(priv->ip4_config, config, NULL); - g_object_unref(config); - } else { - priv->ip4_config = config; - nm_dbus_object_export(NM_DBUS_OBJECT(config)); - g_object_notify((GObject *) self, NM_ACTIVE_CONNECTION_IP4_CONFIG); - } - - nm_vpn_connection_config_maybe_complete(self, TRUE); -} - -static void -nm_vpn_connection_ip6_config_get(NMVpnConnection *self, GVariant *dict) -{ - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - NMPlatformIP6Address address; - guint32 u32, route_metric; - NMSettingIPConfig * s_ip; - guint32 route_table; - NMIP6Config * config; - GVariantIter * iter; - const char * str; - GVariant * v; - gboolean b; - int ip_ifindex; - guint32 mss = 0; - gboolean never_default = FALSE; - - g_return_if_fail(dict && g_variant_is_of_type(dict, G_VARIANT_TYPE_VARDICT)); - - _LOGI("VPN connection: (IP6 Config Get) reply received"); - - if (priv->vpn_state == STATE_CONNECT) - _set_vpn_state(self, STATE_IP_CONFIG_GET, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); - - if (priv->vpn_state > STATE_ACTIVATED) { - _LOGI("VPN connection: (IP6 Config Get) ignoring, the connection is no longer active"); - return; - } - - if (g_variant_n_children(dict) == 0) { - priv->has_ip6 = FALSE; - nm_vpn_connection_config_maybe_complete(self, TRUE); - return; - } - - ip_ifindex = nm_vpn_connection_get_ip_ifindex(self, TRUE); - if (ip_ifindex <= 0) - g_return_if_reached(); - - config = nm_ip6_config_new(nm_netns_get_multi_idx(priv->netns), ip_ifindex); - nm_ip6_config_set_dns_priority(config, NM_DNS_PRIORITY_DEFAULT_VPN); - - memset(&address, 0, sizeof(address)); - address.plen = 128; - - /* Internal address of the VPN subnet's gateway */ - nm_clear_g_free(&priv->ip6_internal_gw); - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY, "@ay", &v)) { - priv->ip6_internal_gw = ip6_addr_dup_from_variant(v); - g_variant_unref(v); - } - - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS, "@ay", &v)) { - ip6_addr_from_variant(v, &address.address); - g_variant_unref(v); - } - - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_PTP, "@ay", &v)) { - ip6_addr_from_variant(v, &address.peer_address); - g_variant_unref(v); - } - - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_PREFIX, "u", &u32)) - address.plen = u32; - - if (!IN6_IS_ADDR_UNSPECIFIED(&address.address) && address.plen && address.plen <= 128) { - address.addr_source = NM_IP_CONFIG_SOURCE_VPN; - nm_ip6_config_add_address(config, &address); - } else { - _LOGW("invalid IP6 config received!"); - g_object_unref(config); - nm_vpn_connection_config_maybe_complete(self, FALSE); - return; - } - - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_DNS, "aay", &iter)) { - while (g_variant_iter_next(iter, "@ay", &v)) { - struct in6_addr dns; - - if (ip6_addr_from_variant(v, &dns)) - nm_ip6_config_add_nameserver(config, &dns); - g_variant_unref(v); - } - g_variant_iter_free(iter); - } - - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_MSS, "u", &u32)) - mss = u32; - - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_DOMAIN, "&s", &str)) - nm_ip6_config_add_domain(config, str); - - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS, "as", &iter)) { - while (g_variant_iter_next(iter, "&s", &str)) - nm_ip6_config_add_domain(config, str); - g_variant_iter_free(iter); - } - - route_table = get_route_table(self, AF_INET6, TRUE); - route_metric = nm_vpn_connection_get_ip6_route_metric(self); - s_ip = nm_connection_get_setting_ip6_config(_get_applied_connection(self)); - - if (nm_setting_ip_config_get_ignore_auto_routes(s_ip)) { - /* Ignore VPN routes */ - } else if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES, "b", &b) && b) { - if (priv->ip6_config) { - NMDedupMultiIter ipconf_iter; - const NMPlatformIP6Route *route; - - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, priv->ip6_config, &route) - nm_ip6_config_add_route(config, route, NULL); - } - } else if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, "a(ayuayu)", &iter)) { - GVariant *dest, *next_hop; - guint32 prefix, metric; - - while (g_variant_iter_next(iter, "(@ayu@ayu)", &dest, &prefix, &next_hop, &metric)) { - NMPlatformIP6Route route; - - memset(&route, 0, sizeof(route)); - - if (!ip6_addr_from_variant(dest, &route.network)) - goto next; - - if (prefix > 128) - goto next; - - route.plen = prefix; - ip6_addr_from_variant(next_hop, &route.gateway); - route.table_coerced = nm_platform_route_table_coerce(route_table); - route.metric = route_metric; - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - - nm_utils_ip6_address_clear_host_address(&route.network, &route.network, route.plen); - - if (priv->ip6_external_gw && IN6_ARE_ADDR_EQUAL(&route.network, priv->ip6_external_gw) - && route.plen == 128) { - /* Ignore host routes to the VPN gateway since NM adds one itself. - * Since NM knows more about the routing situation than the VPN - * server, we want to use the NM created route instead of whatever - * the server provides. - */ - goto next; + nm_l3_config_data_add_route_6(l3cd, &route.r6); } - - nm_ip6_config_add_route(config, &route, NULL); - -next: - g_variant_unref(dest); - g_variant_unref(next_hop); } - g_variant_iter_free(iter); } - if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT, "b", &b)) - never_default = b; + if (g_variant_lookup(dict, + IS_IPv4 ? NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT + : NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT, + "b", + &v_b)) + never_default = v_b; + else + never_default = FALSE; - /* Merge in user overrides from the NMConnection's IPv6 setting */ - nm_ip6_config_merge_setting(config, s_ip, route_table, route_metric); + if (!never_default) { + NMPlatformIPXRoute route; - if (!never_default && !nm_setting_ip_config_get_never_default(s_ip)) { - const NMPlatformIP6Route r = { - .ifindex = ip_ifindex, - .rt_source = NM_IP_CONFIG_SOURCE_VPN, - .gateway = *(priv->ip6_internal_gw ?: &in6addr_any), - .table_coerced = nm_platform_route_table_coerce(route_table), - .metric = route_metric, - .mss = mss, - }; - - nm_ip6_config_add_route(config, &r, NULL); + if (IS_IPv4) { + route.r4 = (NMPlatformIP4Route){ + .ifindex = ip_ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_VPN, + .gateway = priv->ip_data_4.gw_internal.addr4, + .table_any = TRUE, + .metric_any = TRUE, + .mss = mss, + }; + } else { + route.r6 = (NMPlatformIP6Route){ + .ifindex = ip_ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_VPN, + .gateway = priv->ip_data_6.gw_internal.addr6, + .table_any = TRUE, + .metric_any = TRUE, + .mss = mss, + }; + } + nm_l3_config_data_add_route(l3cd, addr_family, NULL, &route.rx); } - nm_ip6_config_add_dependent_routes(config, route_table, route_metric, _is_device_vrf(self)); + _l3cfg_l3cd_set(self, L3CD_TYPE_IP_X(IS_IPv4), l3cd); - if (priv->ip6_config) { - nm_ip6_config_replace(priv->ip6_config, config, NULL); - g_object_unref(config); - } else { - priv->ip6_config = config; - nm_dbus_object_export(NM_DBUS_OBJECT(config)); - g_object_notify((GObject *) self, NM_ACTIVE_CONNECTION_IP6_CONFIG); - } - - nm_vpn_connection_config_maybe_complete(self, TRUE); + _check_complete(self, TRUE); } -static gboolean -connect_timeout_cb(gpointer user_data) +void +nm_vpn_connection_disconnect(NMVpnConnection * self, + NMActiveConnectionStateReason reason, + gboolean quitting) { - NMVpnConnection * self = NM_VPN_CONNECTION(user_data); - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + g_return_if_fail(NM_IS_VPN_CONNECTION(self)); - priv->connect_timeout = 0; + _set_vpn_state(self, STATE_DISCONNECTED, reason, quitting); +} - /* Cancel activation if it's taken too long */ - if (priv->vpn_state == STATE_CONNECT || priv->vpn_state == STATE_IP_CONFIG_GET) { - _LOGW("VPN connection: connect timeout exceeded."); - _set_vpn_state(self, - STATE_FAILED, - NM_ACTIVE_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, - FALSE); +gboolean +nm_vpn_connection_deactivate(NMVpnConnection * self, + NMActiveConnectionStateReason reason, + gboolean quitting) +{ + NMVpnConnectionPrivate *priv; + + g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), FALSE); + + priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + if (priv->vpn_state <= STATE_UNKNOWN || priv->vpn_state > STATE_DEACTIVATING) + return FALSE; + + _set_vpn_state(self, STATE_DEACTIVATING, reason, quitting); + return TRUE; +} + +/*****************************************************************************/ + +static void +_secrets_dbus_need_secrets_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMVpnConnection * self; + NMVpnConnectionPrivate *priv; + gs_unref_variant GVariant *res = NULL; + gs_free_error GError *error = NULL; + const char * setting_name; + + res = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (nm_utils_error_is_cancelled(error)) + return; + + self = NM_VPN_CONNECTION(user_data); + priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + if (error) { + _LOGW("plugin NeedSecrets request #%d failed: %s", priv->secrets_idx + 1, error->message); + _set_vpn_state(self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS, FALSE); + return; } - return FALSE; + g_variant_get(res, "(&s)", &setting_name); + if (nm_str_is_empty(setting_name)) { + /* No secrets required; we can start the VPN */ + _LOGD("service indicated no additional secrets required"); + really_activate(self, priv->username); + return; + } + + /* More secrets required */ + if (priv->secrets_idx == SECRETS_REQ_NEW) { + _LOGW("final secrets request failed to provide sufficient secrets"); + _set_vpn_state(self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS, FALSE); + return; + } + + _LOGD("service indicated additional secrets required"); + _secrets_get(self, priv->secrets_idx + 1, NULL); } static void -connect_success(NMVpnConnection *self) -{ - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - NMSettingVpn * s_vpn; - guint32 timeout; - - s_vpn = nm_connection_get_setting_vpn(_get_applied_connection(self)); - g_assert(s_vpn); - - /* Timeout waiting for IP config signal from VPN service - * It is a configured value or 60 seconds */ - timeout = nm_setting_vpn_get_timeout(s_vpn); - if (timeout == 0) { - timeout = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, - NM_CON_DEFAULT("vpn.timeout"), - NULL, - 1, - G_MAXUINT32, - 60); - } - priv->connect_timeout = g_timeout_add_seconds(timeout, connect_timeout_cb, self); - - nm_clear_pointer(&priv->connect_hash, g_variant_unref); -} - -static void -connect_cb(GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +_secrets_dbus_new_secrets_cb(GObject *source, GAsyncResult *result, gpointer user_data) { NMVpnConnection *self; - gs_unref_variant GVariant *reply = NULL; - gs_free_error GError *error = NULL; + gs_unref_variant GVariant *res = NULL; + gs_free_error GError *error = NULL; - reply = g_dbus_proxy_call_finish(proxy, result, &error); - if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + res = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (nm_utils_error_is_cancelled(error)) return; self = NM_VPN_CONNECTION(user_data); if (error) { - g_dbus_error_strip_remote_error(error); - _LOGW("VPN connection: failed to connect: '%s'", error->message); - _set_vpn_state(self, - STATE_FAILED, - NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED, - FALSE); - } else - connect_success(self); -} - -static void -connect_interactive_cb(GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) -{ - NMVpnConnection * self; - NMVpnConnectionPrivate *priv; - gs_unref_variant GVariant *reply = NULL; - gs_free_error GError *error = NULL; - - reply = g_dbus_proxy_call_finish(proxy, result, &error); - if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + _LOGW("sending new secrets to the plugin failed: %s", error->message); + _set_vpn_state(self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS, FALSE); return; - - self = NM_VPN_CONNECTION(user_data); - priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - - _LOGI("VPN connection: (ConnectInteractive) reply received"); - - if (g_error_matches(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED)) { - _LOGD("VPN connection: falling back to non-interactive connect"); - - /* Fall back to Connect() */ - g_dbus_proxy_call(priv->proxy, - "Connect", - g_variant_new("(@a{sa{sv}})", priv->connect_hash), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->cancellable, - (GAsyncReadyCallback) connect_cb, - self); - } else if (error) { - g_dbus_error_strip_remote_error(error); - _LOGW("VPN connection: failed to connect interactively: '%s'", error->message); - _set_vpn_state(self, - STATE_FAILED, - NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED, - FALSE); - } else - connect_success(self); -} - -/* Add a username to a hashed connection */ -static GVariant * -_hash_with_username(NMConnection *connection, const char *username) -{ - gs_unref_object NMConnection *dup = NULL; - NMSettingVpn * s_vpn; - - /* Shortcut if we weren't given a username or if there already was one in - * the VPN setting; don't bother duplicating the connection and everything. - */ - s_vpn = nm_connection_get_setting_vpn(connection); - g_assert(s_vpn); - if (username == NULL || nm_setting_vpn_get_user_name(s_vpn)) - return nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL); - - dup = nm_simple_connection_new_clone(connection); - g_assert(dup); - s_vpn = nm_connection_get_setting_vpn(dup); - g_assert(s_vpn); - g_object_set(s_vpn, NM_SETTING_VPN_USER_NAME, username, NULL); - return nm_connection_to_dbus(dup, NM_CONNECTION_SERIALIZE_ALL); -} - -static void -really_activate(NMVpnConnection *self, const char *username) -{ - NMVpnConnectionPrivate *priv; - GVariantBuilder details; - - g_return_if_fail(NM_IS_VPN_CONNECTION(self)); - - priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - g_return_if_fail(priv->vpn_state == STATE_NEED_AUTH); - - nm_clear_pointer(&priv->connect_hash, g_variant_unref); - priv->connect_hash = _hash_with_username(_get_applied_connection(self), username); - g_variant_ref_sink(priv->connect_hash); - - /* If at least one agent doesn't support VPN hints, then we can't use - * ConnectInteractive(), because that agent won't be able to pass hints - * from the VPN plugin's interactive secrets requests to the VPN authentication - * dialog and we won't get the secrets we need. In this case fall back to - * the old Connect() call. - */ - if (nm_agent_manager_all_agents_have_capability( - nm_agent_manager_get(), - nm_active_connection_get_subject(NM_ACTIVE_CONNECTION(self)), - NM_SECRET_AGENT_CAPABILITY_VPN_HINTS)) { - _LOGD("Allowing interactive secrets as all agents have that capability"); - - g_variant_builder_init(&details, G_VARIANT_TYPE_VARDICT); - g_dbus_proxy_call(priv->proxy, - "ConnectInteractive", - g_variant_new("(@a{sa{sv}}a{sv})", priv->connect_hash, &details), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->cancellable, - (GAsyncReadyCallback) connect_interactive_cb, - self); - } else { - _LOGD("Calling old Connect function as not all agents support interactive secrets"); - g_dbus_proxy_call(priv->proxy, - "Connect", - g_variant_new("(@a{sa{sv}})", priv->connect_hash), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->cancellable, - (GAsyncReadyCallback) connect_cb, - self); } _set_vpn_state(self, STATE_CONNECT, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); } static void -failure_cb(GDBusProxy *proxy, guint32 reason, gpointer user_data) -{ - NMVpnConnection *self = NM_VPN_CONNECTION(user_data); - - plugin_failed(self, reason); -} - -static void -state_changed_cb(GDBusProxy *proxy, guint32 new_service_state, gpointer user_data) -{ - NMVpnConnection *self = NM_VPN_CONNECTION(user_data); - - plugin_state_changed(self, new_service_state); -} - -static void -secrets_required_cb(GDBusProxy * proxy, - const char * message, - const char *const *secrets, - gpointer user_data) -{ - NMVpnConnection *self = NM_VPN_CONNECTION(user_data); - - plugin_interactive_secrets_required(self, message, secrets); -} - -static void -config_cb(GDBusProxy *proxy, GVariant *dict, gpointer user_data) +_secrets_get_secrets_cb(NMSettingsConnection * connection, + NMSettingsConnectionCallId *call_id, + const char * agent_username, + const char * setting_name, + GError * error, + gpointer user_data) { NMVpnConnection * self = NM_VPN_CONNECTION(user_data); - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + NMVpnConnectionPrivate *priv; + GVariant * dict; - /* Only list to this signals during and after connection */ - if (priv->vpn_state >= STATE_NEED_AUTH) - nm_vpn_connection_config_get(self, dict); -} + g_return_if_fail(NM_IS_VPN_CONNECTION(self)); -static void -ip4_config_cb(GDBusProxy *proxy, GVariant *dict, gpointer user_data) -{ - NMVpnConnection * self = NM_VPN_CONNECTION(user_data); - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - /* Only list to this signals during and after connection */ - if (priv->vpn_state >= STATE_NEED_AUTH) - nm_vpn_connection_ip4_config_get(self, dict); -} + g_return_if_fail(connection && connection == _get_settings_connection(self, FALSE)); + g_return_if_fail(call_id == priv->secrets_id); -static void -ip6_config_cb(GDBusProxy *proxy, GVariant *dict, gpointer user_data) -{ - NMVpnConnection * self = NM_VPN_CONNECTION(user_data); - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + priv->secrets_id = NULL; - /* Only list to this signals during and after connection */ - if (priv->vpn_state >= STATE_NEED_AUTH) - nm_vpn_connection_ip6_config_get(self, dict); -} + if (nm_utils_error_is_cancelled(error)) + return; -static void -_name_owner_changed(GObject *object, GParamSpec *pspec, gpointer user_data) -{ - NMVpnConnection * self = NM_VPN_CONNECTION(user_data); - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - char * owner; - - owner = g_dbus_proxy_get_name_owner(G_DBUS_PROXY(object)); - - if (owner && !priv->service_running) { - /* service appeared */ - priv->service_running = TRUE; - _LOGI("Saw the service appear; activating connection"); - - /* No need to wait for the timeout any longer */ - nm_clear_g_source(&priv->start_timeout); - - /* Expect success because the VPN service has already appeared */ - _nm_dbus_signal_connect(priv->proxy, - "Failure", - G_VARIANT_TYPE("(u)"), - G_CALLBACK(failure_cb), - self); - _nm_dbus_signal_connect(priv->proxy, - "StateChanged", - G_VARIANT_TYPE("(u)"), - G_CALLBACK(state_changed_cb), - self); - _nm_dbus_signal_connect(priv->proxy, - "SecretsRequired", - G_VARIANT_TYPE("(sas)"), - G_CALLBACK(secrets_required_cb), - self); - _nm_dbus_signal_connect(priv->proxy, - "Config", - G_VARIANT_TYPE("(a{sv})"), - G_CALLBACK(config_cb), - self); - _nm_dbus_signal_connect(priv->proxy, - "Ip4Config", - G_VARIANT_TYPE("(a{sv})"), - G_CALLBACK(ip4_config_cb), - self); - _nm_dbus_signal_connect(priv->proxy, - "Ip6Config", - G_VARIANT_TYPE("(a{sv})"), - G_CALLBACK(ip6_config_cb), - self); - - _set_vpn_state(self, STATE_NEED_AUTH, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); - - /* Kick off the secrets requests; first we get existing system secrets - * and ask the plugin if these are sufficient, next we get all existing - * secrets from system and from user agents and ask the plugin again, - * and last we ask the user for new secrets if required. - */ - get_secrets(self, SECRETS_REQ_SYSTEM, NULL); - } else if (!owner && priv->service_running) { - /* service went away */ - priv->service_running = FALSE; - _LOGI("VPN service disappeared"); - nm_vpn_connection_disconnect(self, - NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_STOPPED, - FALSE); + if (error && priv->secrets_idx >= SECRETS_REQ_NEW) { + _LOGW("secrets: failed to request VPN secrets #%d: %s", + priv->secrets_idx + 1, + error->message); + _set_vpn_state(self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS, FALSE); + return; } - g_free(owner); + if (!priv->dbus.bus_name) { + _set_vpn_state(self, + STATE_FAILED, + NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_STOPPED, + FALSE); + return; + } + + /* Cache the username for later */ + if (agent_username) + nm_strdup_reset(&priv->username, agent_username); + + dict = _hash_with_username(_get_applied_connection(self), priv->username); + + if (priv->secrets_idx == SECRETS_REQ_INTERACTIVE) { + _LOGD("secrets: sending secrets to the plugin"); + _dbus_connection_call(self, + "NewSecrets", + g_variant_new("(@a{sa{sv}})", dict), + G_VARIANT_TYPE("()"), + _secrets_dbus_new_secrets_cb); + return; + } + + _LOGD("secrets: asking service if additional secrets are required"); + _dbus_connection_call(self, + "NeedSecrets", + g_variant_new("(@a{sa{sv}})", dict), + G_VARIANT_TYPE("(s)"), + _secrets_dbus_need_secrets_cb); } -static gboolean -_daemon_exec_timeout(gpointer data) +static void +_secrets_get(NMVpnConnection *self, SecretsReq secrets_idx, const char *const *hints) { - NMVpnConnection * self = NM_VPN_CONNECTION(data); - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + NMVpnConnectionPrivate * priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE; - _LOGW("Timed out waiting for the service to start"); - priv->start_timeout = 0; - nm_vpn_connection_disconnect(self, - NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT, - FALSE); - return G_SOURCE_REMOVE; + g_return_if_fail(secrets_idx < SECRETS_REQ_LAST); + + priv->secrets_idx = secrets_idx; + + cancel_get_secrets(self); + + _LOGD("secrets: requesting VPN secrets pass #%d", priv->secrets_idx + 1); + + switch (priv->secrets_idx) { + case SECRETS_REQ_SYSTEM: + flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM; + break; + case SECRETS_REQ_EXISTING: + flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE; + break; + case SECRETS_REQ_NEW: + case SECRETS_REQ_INTERACTIVE: + flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION; + break; + default: + g_return_if_reached(); + } + + if (nm_active_connection_get_user_requested(NM_ACTIVE_CONNECTION(self))) + flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED; + + priv->secrets_id = nm_settings_connection_get_secrets( + _get_settings_connection(self, FALSE), + _get_applied_connection(self), + nm_active_connection_get_subject(NM_ACTIVE_CONNECTION(self)), + NM_SETTING_VPN_SETTING_NAME, + flags, + hints, + _secrets_get_secrets_cb, + self); + + g_return_if_fail(priv->secrets_id); } +static void +_dbus_signal_secrets_required_cb(NMVpnConnection * self, + const char * message, + const char *const *secrets) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + const gsize secrets_len = NM_PTRARRAY_LEN(secrets); + gsize i; + gs_free const char ** hints = NULL; + gs_free char * message_hint = NULL; + + if (!NM_IN_SET(priv->vpn_state, STATE_CONNECT, STATE_NEED_AUTH)) { + _LOGD("secrets: request ignored in current state %s", + vpn_state_to_string_a(priv->vpn_state)); + return; + } + + _LOGD("secrets: request (state %s)", vpn_state_to_string_a(priv->vpn_state)); + + priv->secrets_idx = SECRETS_REQ_INTERACTIVE; + _set_vpn_state(self, STATE_NEED_AUTH, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); + + /* Copy hints and add message to the end */ + hints = g_new(const char *, secrets_len + 2); + for (i = 0; i < secrets_len; i++) + hints[i] = secrets[i]; + if (message) { + message_hint = g_strdup_printf("x-vpn-message:%s", message); + hints[i++] = message_hint; + } + hints[i] = NULL; + nm_assert(i < secrets_len + 2); + + _secrets_get(self, SECRETS_REQ_INTERACTIVE, hints); +} + +/*****************************************************************************/ + static int _get_log_level(void) { @@ -2253,14 +2489,14 @@ nm_vpn_service_daemon_exec(NMVpnConnection *self, GError **error) NMVpnConnectionPrivate *priv; GPid pid; char * vpn_argv[4]; - gboolean success = FALSE; - GError * spawn_error = NULL; - guint i, j, n_environ; gs_free char ** envp = NULL; char env_log_level[NM_STRLEN("NM_VPN_LOG_LEVEL=") + 100]; char env_log_syslog[NM_STRLEN("NM_VPN_LOG_SYSLOG=") + 10]; - const int N_ENVIRON_EXTRA = 3; + const gsize N_ENVIRON_EXTRA = 3; char ** p_environ; + gsize n_environ; + gsize i; + gsize j; g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), FALSE); @@ -2271,17 +2507,17 @@ nm_vpn_service_daemon_exec(NMVpnConnection *self, GError **error) g_return_val_if_fail(vpn_argv[0], FALSE); if (nm_vpn_plugin_info_supports_multiple(priv->plugin_info)) { vpn_argv[i++] = "--bus-name"; - vpn_argv[i++] = priv->bus_name; + vpn_argv[i++] = priv->dbus.bus_name; } vpn_argv[i++] = NULL; /* we include and "config.h" defines _GNU_SOURCE for us. So, we have @environ. */ p_environ = environ; - n_environ = p_environ ? g_strv_length(p_environ) : 0; + n_environ = NM_PTRARRAY_LEN(p_environ); envp = g_new(char *, n_environ + N_ENVIRON_EXTRA); for (i = 0, j = 0; j < n_environ; j++) { - if (g_str_has_prefix(p_environ[j], "NM_VPN_LOG_LEVEL=") - || g_str_has_prefix(p_environ[j], "NM_VPN_LOG_SYSLOG=")) + if (NM_STR_HAS_PREFIX(p_environ[j], "NM_VPN_LOG_LEVEL=") + || NM_STR_HAS_PREFIX(p_environ[j], "NM_VPN_LOG_SYSLOG=")) continue; envp[i++] = p_environ[j]; } @@ -2300,70 +2536,203 @@ nm_vpn_service_daemon_exec(NMVpnConnection *self, GError **error) envp[i++] = NULL; nm_assert(i <= n_environ + N_ENVIRON_EXTRA); - success = g_spawn_async(NULL, vpn_argv, envp, 0, nm_utils_setpgid, NULL, &pid, &spawn_error); + if (!g_spawn_async(NULL, vpn_argv, envp, 0, nm_utils_setpgid, NULL, &pid, error)) + return FALSE; - if (success) { - _LOGI("Started the VPN service, PID %ld", (long int) pid); - priv->start_timeout = g_timeout_add_seconds(5, _daemon_exec_timeout, self); - } else { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "%s", - spawn_error ? spawn_error->message : "unknown g_spawn_async() error"); + _LOGD("starting: VPN service has PID %lld", (long long) pid); + return TRUE; +} - if (spawn_error) - g_error_free(spawn_error); +/*****************************************************************************/ + +static gboolean +_start_timeout_cb(gpointer data) +{ + NMVpnConnection * self = NM_VPN_CONNECTION(data); + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + nm_clear_g_source_inst(&priv->start_timeout_source); + + if (priv->dbus_service_started) + _LOGW("starting: timed out waiting for the service to start"); + else + _LOGW("starting: timed out waiting for the VPN to activate"); + nm_vpn_connection_disconnect(self, + NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT, + FALSE); + return G_SOURCE_CONTINUE; +} + +/*****************************************************************************/ + +static void +_dbus_dispatch_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMVpnConnection * self = user_data; + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + guint32 v_u; + + nm_assert(nm_streq0(object_path, NM_VPN_DBUS_PLUGIN_PATH)); + nm_assert(nm_streq0(interface_name, NM_VPN_DBUS_PLUGIN_INTERFACE)); + nm_assert(signal_name); + + if (!nm_streq0(priv->dbus.owner, sender_name)) + return; + + if (nm_streq(signal_name, "Failure")) { + if (nm_g_variant_tuple_get_u(parameters, &v_u)) + _dbus_signal_failure_cb(self, v_u); + } else if (nm_streq(signal_name, "StateChanged")) { + if (nm_g_variant_tuple_get_u(parameters, &v_u)) + _dbus_signal_state_changed_cb(self, v_u); + } else if (nm_streq(signal_name, "SecretsRequired")) { + if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sas)"))) { + const char * v_s; + gs_free const char **v_strv = NULL; + + g_variant_get(parameters, "(&s^a&s)", &v_s, &v_strv); + _dbus_signal_secrets_required_cb(self, v_s, v_strv); + } + } else if (NM_IN_STRSET(signal_name, "Config", "Ip4Config", "Ip6Config")) { + if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(a{sv})"))) { + gs_unref_variant GVariant *v_var = NULL; + + g_variant_get(parameters, "(@a{sv})", &v_var); + if (signal_name[0] == 'C') + _dbus_signal_config_cb(self, v_var); + else if (signal_name[2] == '4') + _dbus_signal_ip_config_cb(self, AF_INET, v_var); + else + _dbus_signal_ip_config_cb(self, AF_INET6, v_var); + } } - - return success; } static void -on_proxy_acquired(GObject *object, GAsyncResult *result, gpointer user_data) +_name_owner_changed(NMVpnConnection *self, const char *owner, gboolean initializing) +{ + _nm_unused gs_unref_object NMVpnConnection *self_keep_alive = g_object_ref(self); + NMVpnConnectionPrivate * priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + owner = nm_str_not_empty(owner); + + if (!owner && initializing) { + gs_free_error GError *error = NULL; + + nm_assert(!priv->dbus.owner); + _LOGT("dbus: no name owner for %s (start VPN service)", priv->dbus.bus_name); + + if (!nm_vpn_service_daemon_exec(self, &error)) { + _LOGW("starting: failure to start VPN service: %s", error->message); + nm_vpn_connection_disconnect(self, + NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED, + FALSE); + } + priv->start_timeout_source = nm_g_timeout_add_seconds_source(5, _start_timeout_cb, self); + return; + } + + if (!nm_strdup_reset(&priv->dbus.owner, owner)) + return; + + if (!priv->dbus.owner) { + _LOGT("dbus: name owner for %s disappeared", priv->dbus.bus_name); + + /* We don't want to restart if the service re-appears. Disconnect the signal + * so that cannot happen and we don't disconnect the VPN again. */ + nm_clear_g_dbus_connection_signal(priv->dbus.connection, + &priv->dbus.signal_id_name_changed); + + nm_vpn_connection_disconnect(self, + NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_STOPPED, + FALSE); + return; + } + + _LOGT("dbus: name owner %s for %s", priv->dbus.owner, priv->dbus.bus_name); + + priv->dbus_service_started = TRUE; + nm_clear_g_source_inst(&priv->start_timeout_source); + priv->start_timeout_source = + nm_g_timeout_add_seconds_source(_get_vpn_timeout(self) + 180, _start_timeout_cb, self); + + _set_vpn_state(self, STATE_NEED_AUTH, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); + + /* Kick off the secrets requests; first we get existing system secrets + * and ask the plugin if these are sufficient, next we get all existing + * secrets from system and from user agents and ask the plugin again, + * and last we ask the user for new secrets if required. + */ + _secrets_get(self, SECRETS_REQ_SYSTEM, NULL); +} + +static void +_name_owner_changed_cb(GDBusConnection *connection, + const char * sender_name, + const char * object_path, + const char * interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMVpnConnection * self = user_data; + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + const char * new_owner; + + if (!priv->dbus.name_owner_initialized) + return; + + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) + return; + + g_variant_get(parameters, "(&s&s&s)", NULL, NULL, &new_owner); + + _name_owner_changed(self, new_owner, FALSE); +} + +static void +_name_owner_get_cb(const char *name_owner, GError *error, gpointer user_data) { NMVpnConnection * self; NMVpnConnectionPrivate *priv; - gs_free_error GError *error = NULL; - GDBusProxy * proxy; - proxy = g_dbus_proxy_new_for_bus_finish(result, &error); - if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + if (nm_utils_error_is_cancelled(error)) return; - self = NM_VPN_CONNECTION(user_data); + self = user_data; priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - if (error) { - _LOGE("failed to acquire dbus proxy for VPN service: %s", error->message); - _set_vpn_state(self, - STATE_FAILED, - NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED, - FALSE); - return; - } - - priv->proxy = proxy; - - g_signal_connect(priv->proxy, "notify::g-name-owner", G_CALLBACK(_name_owner_changed), self); - _name_owner_changed(G_OBJECT(priv->proxy), NULL, self); - - if (priv->service_running) - return; - - if (!nm_vpn_service_daemon_exec(self, &error)) { - _LOGW("Could not launch the VPN service. error: %s.", error->message); - - nm_vpn_connection_disconnect(self, - NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED, - FALSE); - } + priv->dbus.name_owner_initialized = TRUE; + _name_owner_changed(self, name_owner, TRUE); } +static gboolean +_init_fail_on_idle_cb(gpointer user_data) +{ + NMVpnConnection * self = user_data; + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + nm_clear_g_source_inst(&priv->init_fail_on_idle_source); + _set_vpn_state(self, + STATE_FAILED, + NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED, + FALSE); + return G_SOURCE_CONTINUE; +} + +/*****************************************************************************/ + void nm_vpn_connection_activate(NMVpnConnection *self, NMVpnPluginInfo *plugin_info) { NMVpnConnectionPrivate *priv; + NMConnection * connection; NMSettingVpn * s_vpn; const char * service; @@ -2373,7 +2742,9 @@ nm_vpn_connection_activate(NMVpnConnection *self, NMVpnPluginInfo *plugin_info) priv = NM_VPN_CONNECTION_GET_PRIVATE(self); g_return_if_fail(!priv->plugin_info); - s_vpn = nm_connection_get_setting_vpn(_get_applied_connection(self)); + connection = _get_applied_connection(self); + + s_vpn = nm_connection_get_setting_vpn(connection); g_return_if_fail(s_vpn); service = nm_vpn_plugin_info_get_service(plugin_info); @@ -2387,413 +2758,138 @@ nm_vpn_connection_activate(NMVpnConnection *self, NMVpnPluginInfo *plugin_info) path = strrchr(path, '/'); g_return_if_fail(path); - priv->bus_name = g_strdup_printf("%s.Connection_%s", service, &path[1]); + priv->dbus.bus_name = g_strdup_printf("%s.Connection_%s", service, &path[1]); } else - priv->bus_name = g_strdup(service); + priv->dbus.bus_name = g_strdup(service); + + _LOGI("starting %s", nm_vpn_plugin_info_get_name(plugin_info)); priv->connection_can_persist = nm_setting_vpn_get_persistent(s_vpn); priv->plugin_info = g_object_ref(plugin_info); - priv->cancellable = g_cancellable_new(); - g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, - NULL, - priv->bus_name, - NM_VPN_DBUS_PLUGIN_PATH, - NM_VPN_DBUS_PLUGIN_INTERFACE, - priv->cancellable, - (GAsyncReadyCallback) on_proxy_acquired, - self); + priv->main_cancellable = g_cancellable_new(); + priv->dbus.connection = nm_g_object_ref(NM_MAIN_DBUS_CONNECTION_GET); + + if (!priv->dbus.connection) { + _LOGD("starting: no D-Bus connection (will fail)"); + priv->init_fail_on_idle_source = nm_g_idle_add_source(_init_fail_on_idle_cb, self); + goto out; + } + + _LOGD("starting: watch D-Bus service %s", priv->dbus.bus_name); + priv->dbus.signal_id_name_changed = + nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus.connection, + priv->dbus.bus_name, + _name_owner_changed_cb, + self, + NULL); + + priv->dbus.signal_id_vpn = g_dbus_connection_signal_subscribe(priv->dbus.connection, + priv->dbus.bus_name, + NM_VPN_DBUS_PLUGIN_INTERFACE, + NULL, + NM_VPN_DBUS_PLUGIN_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + _dbus_dispatch_cb, + self, + NULL); + + nm_dbus_connection_call_get_name_owner(priv->dbus.connection, + priv->dbus.bus_name, + 3000, + priv->main_cancellable, + _name_owner_get_cb, + self); + +out: _set_vpn_state(self, STATE_PREPARE, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); } -NMVpnConnectionState -nm_vpn_connection_get_vpn_state(NMVpnConnection *self) -{ - g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), NM_VPN_CONNECTION_STATE_UNKNOWN); - - return _state_to_nm_vpn_state(NM_VPN_CONNECTION_GET_PRIVATE(self)->vpn_state); -} - -const char * -nm_vpn_connection_get_banner(NMVpnConnection *self) -{ - g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), NULL); - - return NM_VPN_CONNECTION_GET_PRIVATE(self)->banner; -} - -NMProxyConfig * -nm_vpn_connection_get_proxy_config(NMVpnConnection *self) -{ - g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), NULL); - - return NM_VPN_CONNECTION_GET_PRIVATE(self)->proxy_config; -} - -NMIP4Config * -nm_vpn_connection_get_ip4_config(NMVpnConnection *self) -{ - g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), NULL); - - return NM_VPN_CONNECTION_GET_PRIVATE(self)->ip4_config; -} - -NMIP6Config * -nm_vpn_connection_get_ip6_config(NMVpnConnection *self) -{ - g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), NULL); - - return NM_VPN_CONNECTION_GET_PRIVATE(self)->ip6_config; -} - -static int -_get_ip_iface_for_device(NMVpnConnection *self, const char **out_iface) -{ - NMDevice * parent_dev; - int ifindex; - const char *iface; - - nm_assert(NM_IS_VPN_CONNECTION(self)); - - /* the ifindex and the ifname in this case should come together. - * They either must be both set, or none. */ - - parent_dev = nm_active_connection_get_device(NM_ACTIVE_CONNECTION(self)); - if (!parent_dev) - goto none; - ifindex = nm_device_get_ip_ifindex(parent_dev); - if (ifindex <= 0) - goto none; - iface = nm_device_get_ip_iface(parent_dev); - if (!iface) - goto none; - - NM_SET_OUT(out_iface, iface); - return ifindex; -none: - NM_SET_OUT(out_iface, NULL); - return 0; -} - -const char * -nm_vpn_connection_get_ip_iface(NMVpnConnection *self, gboolean fallback_device) -{ - NMVpnConnectionPrivate *priv; - const char * iface; - - g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), NULL); - - priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - - if (priv->ip_iface || !fallback_device) - return priv->ip_iface; - - _get_ip_iface_for_device(self, &iface); - return iface; -} - -int -nm_vpn_connection_get_ip_ifindex(NMVpnConnection *self, gboolean fallback_device) -{ - NMVpnConnectionPrivate *priv; - - g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), 0); - - priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - - if (priv->ip_ifindex > 0) - return priv->ip_ifindex; - if (!fallback_device) - return 0; - - return _get_ip_iface_for_device(self, NULL); -} - -guint32 -nm_vpn_connection_get_ip4_internal_gateway(NMVpnConnection *self) -{ - g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), 0); - - return NM_VPN_CONNECTION_GET_PRIVATE(self)->ip4_internal_gw; -} - -struct in6_addr * -nm_vpn_connection_get_ip6_internal_gateway(NMVpnConnection *self) -{ - g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), 0); - - return NM_VPN_CONNECTION_GET_PRIVATE(self)->ip6_internal_gw; -} - -void -nm_vpn_connection_disconnect(NMVpnConnection * self, - NMActiveConnectionStateReason reason, - gboolean quitting) -{ - g_return_if_fail(NM_IS_VPN_CONNECTION(self)); - - _set_vpn_state(self, STATE_DISCONNECTED, reason, quitting); -} - -gboolean -nm_vpn_connection_deactivate(NMVpnConnection * self, - NMActiveConnectionStateReason reason, - gboolean quitting) -{ - NMVpnConnectionPrivate *priv; - gboolean success = FALSE; - - g_return_val_if_fail(NM_IS_VPN_CONNECTION(self), FALSE); - - priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - if (priv->vpn_state > STATE_UNKNOWN && priv->vpn_state <= STATE_DEACTIVATING) { - _set_vpn_state(self, STATE_DEACTIVATING, reason, quitting); - success = TRUE; - } - return success; -} - -/*****************************************************************************/ - -static void -plugin_need_secrets_cb(GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) -{ - NMVpnConnection * self; - NMVpnConnectionPrivate *priv; - gs_unref_variant GVariant *reply = NULL; - gs_free_error GError *error = NULL; - const char * setting_name; - - reply = _nm_dbus_proxy_call_finish(proxy, result, G_VARIANT_TYPE("(s)"), &error); - if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - - self = NM_VPN_CONNECTION(user_data); - priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - - if (error) { - g_dbus_error_strip_remote_error(error); - _LOGE("plugin NeedSecrets request #%d failed: %s", priv->secrets_idx + 1, error->message); - _set_vpn_state(self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS, FALSE); - return; - } - - g_variant_get(reply, "(&s)", &setting_name); - if (!strlen(setting_name)) { - _LOGD("service indicated no additional secrets required"); - - /* No secrets required; we can start the VPN */ - really_activate(self, priv->username); - return; - } - - /* More secrets required */ - if (priv->secrets_idx == SECRETS_REQ_NEW) { - _LOGE("final secrets request failed to provide sufficient secrets"); - _set_vpn_state(self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS, FALSE); - } else { - _LOGD("service indicated additional secrets required"); - get_secrets(self, priv->secrets_idx + 1, NULL); - } -} - -static void -plugin_new_secrets_cb(GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) -{ - NMVpnConnection *self; - gs_unref_variant GVariant *reply = NULL; - gs_free_error GError *error = NULL; - - reply = g_dbus_proxy_call_finish(proxy, result, &error); - if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - - self = NM_VPN_CONNECTION(user_data); - - if (error) { - g_dbus_error_strip_remote_error(error); - _LOGE("sending new secrets to the plugin failed: %s", error->message); - _set_vpn_state(self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS, FALSE); - } else - _set_vpn_state(self, STATE_CONNECT, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); -} - -static void -get_secrets_cb(NMSettingsConnection * connection, - NMSettingsConnectionCallId *call_id, - const char * agent_username, - const char * setting_name, - GError * error, - gpointer user_data) -{ - NMVpnConnection * self = NM_VPN_CONNECTION(user_data); - NMVpnConnectionPrivate *priv; - GVariant * dict; - - g_return_if_fail(NM_IS_VPN_CONNECTION(self)); - - priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - - g_return_if_fail(connection && connection == _get_settings_connection(self, FALSE)); - g_return_if_fail(call_id == priv->secrets_id); - - priv->secrets_id = NULL; - - if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - - if (error && priv->secrets_idx >= SECRETS_REQ_NEW) { - _LOGE("Failed to request VPN secrets #%d: %s", priv->secrets_idx + 1, error->message); - _set_vpn_state(self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS, FALSE); - return; - } - - /* Cache the username for later */ - if (agent_username) { - g_free(priv->username); - priv->username = g_strdup(agent_username); - } - - dict = _hash_with_username(_get_applied_connection(self), priv->username); - - if (priv->secrets_idx == SECRETS_REQ_INTERACTIVE) { - _LOGD("sending secrets to the plugin"); - - /* Send the secrets back to the plugin */ - g_dbus_proxy_call(priv->proxy, - "NewSecrets", - g_variant_new("(@a{sa{sv}})", dict), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->cancellable, - (GAsyncReadyCallback) plugin_new_secrets_cb, - self); - } else { - _LOGD("asking service if additional secrets are required"); - - /* Ask the VPN service if more secrets are required */ - g_dbus_proxy_call(priv->proxy, - "NeedSecrets", - g_variant_new("(@a{sa{sv}})", dict), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->cancellable, - (GAsyncReadyCallback) plugin_need_secrets_cb, - self); - } -} - -static void -get_secrets(NMVpnConnection *self, SecretsReq secrets_idx, const char *const *hints) -{ - NMVpnConnectionPrivate * priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE; - - g_return_if_fail(secrets_idx < SECRETS_REQ_LAST); - priv->secrets_idx = secrets_idx; - - cancel_get_secrets(self); - - _LOGD("requesting VPN secrets pass #%d", priv->secrets_idx + 1); - - switch (priv->secrets_idx) { - case SECRETS_REQ_SYSTEM: - flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM; - break; - case SECRETS_REQ_EXISTING: - flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE; - break; - case SECRETS_REQ_NEW: - case SECRETS_REQ_INTERACTIVE: - flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION; - break; - default: - g_assert_not_reached(); - } - - if (nm_active_connection_get_user_requested(NM_ACTIVE_CONNECTION(self))) - flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED; - - priv->secrets_id = nm_settings_connection_get_secrets( - _get_settings_connection(self, FALSE), - _get_applied_connection(self), - nm_active_connection_get_subject(NM_ACTIVE_CONNECTION(self)), - NM_SETTING_VPN_SETTING_NAME, - flags, - hints, - get_secrets_cb, - self); - g_return_if_fail(priv->secrets_id); -} - -static void -plugin_interactive_secrets_required(NMVpnConnection * self, - const char * message, - const char *const *secrets) -{ - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - const gsize secrets_len = NM_PTRARRAY_LEN(secrets); - gsize i; - gs_free const char ** hints = NULL; - gs_free char * message_hint = NULL; - - if (!NM_IN_SET(priv->vpn_state, STATE_CONNECT, STATE_NEED_AUTH)) { - _LOGD("VPN plugin: requested secrets; state %s (%d); ignore request in current state", - vpn_state_to_string_a(priv->vpn_state), - priv->vpn_state); - return; - } - - _LOGI("VPN plugin: requested secrets; state %s (%d)", - vpn_state_to_string_a(priv->vpn_state), - priv->vpn_state); - - priv->secrets_idx = SECRETS_REQ_INTERACTIVE; - _set_vpn_state(self, STATE_NEED_AUTH, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); - - /* Copy hints and add message to the end */ - hints = g_new(const char *, secrets_len + 2); - for (i = 0; i < secrets_len; i++) - hints[i] = secrets[i]; - if (message) { - message_hint = g_strdup_printf("x-vpn-message:%s", message); - hints[i++] = message_hint; - } - hints[i] = NULL; - nm_assert(i < secrets_len + 2); - - get_secrets(self, SECRETS_REQ_INTERACTIVE, hints); -} - /*****************************************************************************/ static void device_changed(NMActiveConnection *active, NMDevice *new_device, NMDevice *old_device) { - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(active); + NMVpnConnection * self = NM_VPN_CONNECTION(active); + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(active); + gs_unref_object NML3Cfg *l3cfg_old = NULL; + int ifindex; - if (!_service_and_connection_can_persist(NM_VPN_CONNECTION(active))) + if (!priv->generic_config_received) return; - if (priv->vpn_state < STATE_CONNECT || priv->vpn_state > STATE_ACTIVATED) + if (priv->vpn_state > STATE_ACTIVATED) + return; + if (!_service_and_connection_can_persist(self)) return; - /* Route-based VPNs must update their routing and send a new IP config - * since all their routes need to be adjusted for new_device. - */ - if (priv->ip_ifindex <= 0) + if (priv->ifindex_if <= 0) { + /* Route-based VPNs must updvate their routing and send a new IP config + * since all their routes need to be adjusted for new_device. + */ + return; + } + + ifindex = _get_ifindex_for_device(self); + if (ifindex <= 0) + return; + if (priv->ifindex_dev == ifindex) return; - /* Device changed underneath the VPN connection. Let the plugin figure - * out that connectivity is down and start its reconnect attempt if it - * needs to. - */ - if (old_device) - remove_parent_device_config(NM_VPN_CONNECTION(active), old_device); + _LOGD("set ip-ifindex-dev %d (was %d)", ifindex, priv->ifindex_dev); - if (new_device) - apply_parent_device_config(NM_VPN_CONNECTION(active)); + l3cfg_old = g_steal_pointer(&priv->l3cfg_dev); + nm_l3cfg_commit_type_clear(l3cfg_old, &priv->l3cfg_commit_type_dev); + _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"); + } + + if (_l3cfg_l3cd_gw_extern_update(self)) + nm_l3cfg_commit_on_idle_schedule(priv->l3cfg_dev, NM_L3_CFG_COMMIT_TYPE_AUTO); +} + +/*****************************************************************************/ + +static void +get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMVpnConnection * self = NM_VPN_CONNECTION(object); + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); + + switch (prop_id) { + case PROP_VPN_STATE: + g_value_set_uint(value, _state_to_nm_vpn_state(priv->vpn_state)); + break; + case PROP_BANNER: + g_value_set_string(value, priv->banner ?: ""); + break; + case PROP_IP4_CONFIG: + nm_dbus_utils_g_value_set_object_path(value, priv->ip_data_4.ip_config); + break; + case PROP_IP6_CONFIG: + nm_dbus_utils_g_value_set_object_path(value, priv->ip_data_6.ip_config); + break; + case PROP_MASTER: + nm_dbus_utils_g_value_set_object_path( + value, + nm_active_connection_get_device(NM_ACTIVE_CONNECTION(self))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } } /*****************************************************************************/ @@ -2808,35 +2904,69 @@ nm_vpn_connection_init(NMVpnConnection *self) priv->netns = g_object_ref(nm_netns_get()); } +NMVpnConnection * +nm_vpn_connection_new(NMSettingsConnection * settings_connection, + NMDevice * parent_device, + const char * specific_object, + NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, + NMAuthSubject * subject) +{ + g_return_val_if_fail(!settings_connection || NM_IS_SETTINGS_CONNECTION(settings_connection), + NULL); + g_return_val_if_fail(NM_IS_DEVICE(parent_device), NULL); + g_return_val_if_fail(specific_object, NULL); + + return g_object_new(NM_TYPE_VPN_CONNECTION, + NM_ACTIVE_CONNECTION_INT_SETTINGS_CONNECTION, + settings_connection, + NM_ACTIVE_CONNECTION_INT_DEVICE, + parent_device, + NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, + specific_object, + NM_ACTIVE_CONNECTION_INT_SUBJECT, + subject, + NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON, + activation_reason, + NM_ACTIVE_CONNECTION_VPN, + TRUE, + NM_ACTIVE_CONNECTION_STATE_FLAGS, + (guint) initial_state_flags, + NULL); +} + static void dispose(GObject *object) { NMVpnConnection * self = NM_VPN_CONNECTION(object); NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); - if (priv->proxy) - g_signal_handlers_disconnect_by_data(priv->proxy, self); + nm_clear_g_dbus_connection_signal(priv->dbus.connection, &priv->dbus.signal_id_vpn); + nm_clear_g_dbus_connection_signal(priv->dbus.connection, &priv->dbus.signal_id_name_changed); - nm_clear_g_source(&priv->start_timeout); + nm_clear_g_source_inst(&priv->init_fail_on_idle_source); + + nm_clear_g_cancellable(&priv->main_cancellable); + + nm_clear_g_source_inst(&priv->start_timeout_source); nm_clear_pointer(&priv->connect_hash, g_variant_unref); - nm_clear_pointer(&priv->ip4_dev_route_blacklist, g_ptr_array_unref); + nm_clear_g_source_inst(&priv->connect_timeout_source); - nm_clear_g_source(&priv->connect_timeout); + if (nm_l3cfg_commit_type_clear(priv->l3cfg_if, &priv->l3cfg_commit_type_if)) + nm_l3cfg_commit_on_idle_schedule(priv->l3cfg_if, NM_L3_CFG_COMMIT_TYPE_AUTO); + + if (nm_l3cfg_commit_type_clear(priv->l3cfg_dev, &priv->l3cfg_commit_type_dev)) + nm_l3cfg_commit_on_idle_schedule(priv->l3cfg_dev, NM_L3_CFG_COMMIT_TYPE_AUTO); + + g_clear_object(&priv->ip_data_4.ip_config); + g_clear_object(&priv->ip_data_6.ip_config); dispatcher_cleanup(self); cancel_get_secrets(self); - nm_clear_g_cancellable(&priv->cancellable); - - g_clear_object(&priv->proxy_config); - nm_dbus_object_clear_and_unexport(&priv->ip4_config); - nm_dbus_object_clear_and_unexport(&priv->ip6_config); - g_clear_object(&priv->proxy); - g_clear_object(&priv->plugin_info); - fw_call_cleanup(self); nm_pacrunner_manager_remove_clear(&priv->pacrunner_conf_id); @@ -2847,56 +2977,24 @@ dispose(GObject *object) static void finalize(GObject *object) { - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(object); - - g_free(priv->banner); - g_free(priv->ip_iface); - g_free(priv->username); - g_free(priv->ip6_internal_gw); - g_free(priv->ip6_external_gw); + NMVpnConnection * self = NM_VPN_CONNECTION(object); + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(self); G_OBJECT_CLASS(nm_vpn_connection_parent_class)->finalize(object); + g_free(priv->banner); + g_free(priv->username); + g_free(priv->dbus.bus_name); + + _l3cfg_l3cd_clear_all(self); + _l3cfg_clear(self, priv->l3cfg_if); + _l3cfg_clear(self, priv->l3cfg_dev); + + g_clear_object(&priv->plugin_info); + g_clear_object(&priv->l3cfg_if); + g_clear_object(&priv->l3cfg_dev); g_clear_object(&priv->netns); -} - -static gboolean -ip_config_valid(VpnState state) -{ - return (state == STATE_PRE_UP || state == STATE_ACTIVATED); -} - -static void -get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE(object); - NMDevice * parent_dev; - - switch (prop_id) { - case PROP_VPN_STATE: - g_value_set_uint(value, _state_to_nm_vpn_state(priv->vpn_state)); - break; - case PROP_BANNER: - g_value_set_string(value, priv->banner ?: ""); - break; - case PROP_IP4_CONFIG: - nm_dbus_utils_g_value_set_object_path(value, - ip_config_valid(priv->vpn_state) ? priv->ip4_config - : NULL); - break; - case PROP_IP6_CONFIG: - nm_dbus_utils_g_value_set_object_path(value, - ip_config_valid(priv->vpn_state) ? priv->ip6_config - : NULL); - break; - case PROP_MASTER: - parent_dev = nm_active_connection_get_device(NM_ACTIVE_CONNECTION(object)); - nm_dbus_utils_g_value_set_object_path(value, parent_dev); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } + g_clear_object(&priv->dbus.connection); } static const GDBusSignalInfo signal_info_vpn_state_changed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT( @@ -2918,11 +3016,11 @@ static const NMDBusInterfaceInfoExtended interface_info_vpn_connection = { }; static void -nm_vpn_connection_class_init(NMVpnConnectionClass *connection_class) +nm_vpn_connection_class_init(NMVpnConnectionClass *klass) { - GObjectClass * object_class = G_OBJECT_CLASS(connection_class); - NMActiveConnectionClass *active_class = NM_ACTIVE_CONNECTION_CLASS(connection_class); - NMDBusObjectClass * dbus_object_class = NM_DBUS_OBJECT_CLASS(connection_class); + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMActiveConnectionClass *active_class = NM_ACTIVE_CONNECTION_CLASS(klass); + NMDBusObjectClass * dbus_object_class = NM_DBUS_OBJECT_CLASS(klass); dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_vpn_connection); @@ -2957,18 +3055,19 @@ nm_vpn_connection_class_init(NMVpnConnectionClass *connection_class) PROP_IP6_CONFIG, NM_ACTIVE_CONNECTION_IP6_CONFIG); - signals[INTERNAL_STATE_CHANGED] = g_signal_new(NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 3, - G_TYPE_UINT, - G_TYPE_UINT, - G_TYPE_UINT); + signals[INTERNAL_STATE_CHANGED] = + g_signal_new(NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 3, + G_TYPE_UINT, /* NMVpnConnectionState new_external_state */ + G_TYPE_UINT, /* NMVpnConnectionState old_external_state */ + G_TYPE_UINT /* NMActiveConnectionStateReason reason */); signals[INTERNAL_RETRY_AFTER_FAILURE] = g_signal_new(NM_VPN_CONNECTION_INTERNAL_RETRY_AFTER_FAILURE, diff --git a/src/core/vpn/nm-vpn-connection.h b/src/core/vpn/nm-vpn-connection.h index 0209ea3d60..df575c47f4 100644 --- a/src/core/vpn/nm-vpn-connection.h +++ b/src/core/vpn/nm-vpn-connection.h @@ -54,16 +54,13 @@ void nm_vpn_connection_disconnect(NMVpnConnection * self, NMActiveConnectionStateReason reason, gboolean quitting); -NMProxyConfig *nm_vpn_connection_get_proxy_config(NMVpnConnection *self); +const NML3ConfigData *nm_vpn_connection_get_l3cd(NMVpnConnection *self); -NMIP4Config * nm_vpn_connection_get_ip4_config(NMVpnConnection *self); -NMIP6Config * nm_vpn_connection_get_ip6_config(NMVpnConnection *self); const char * nm_vpn_connection_get_ip_iface(NMVpnConnection *self, gboolean fallback_device); int nm_vpn_connection_get_ip_ifindex(NMVpnConnection *self, gboolean fallback_device); guint32 nm_vpn_connection_get_ip4_internal_gateway(NMVpnConnection *self); struct in6_addr *nm_vpn_connection_get_ip6_internal_gateway(NMVpnConnection *self); -guint32 nm_vpn_connection_get_ip4_route_metric(NMVpnConnection *self); -guint32 nm_vpn_connection_get_ip6_route_metric(NMVpnConnection *self); +guint32 nm_vpn_connection_get_ip_route_metric(NMVpnConnection *self, int addr_family); #endif /* __NM_VPN_CONNECTION_H__ */ diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index 4ea545a434..867ef7b501 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -3530,7 +3530,6 @@ rta_multipath_done:; obj = nmp_object_new(is_v4 ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, NULL); - obj->ip_route.is_external = TRUE; obj->ip_route.type_coerced = nm_platform_route_type_coerce(rtm->rtm_type); obj->ip_route.table_coerced = nm_platform_route_table_coerce( tb[RTA_TABLE] ? nla_get_u32(tb[RTA_TABLE]) : (guint32) rtm->rtm_table); diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index 5d4ec037ec..ee8748c631 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -4520,12 +4520,6 @@ nm_platform_ip_route_sync(NMPlatform *self, conf_o = routes->pdata[i]; - if (NMP_OBJECT_CAST_IP_ROUTE(conf_o)->is_external) { - /* This route is added externally. We don't have our own agenda to - * add it, so skip. */ - continue; - } - /* User space cannot add IPv6 routes with metric 0. However, kernel can, and we might track such * routes in @route as they are present external. As we already skipped external routes above, * we don't expect a user's choice to add such a route (it won't work anyway). */ @@ -4721,24 +4715,6 @@ sync_route_add: } if (routes_prune) { - if (routes) { - for (i = 0; i < routes->len; i++) { - conf_o = routes->pdata[i]; - - if (NMP_OBJECT_CAST_IP_ROUTE(conf_o)->is_external) { - /* this is only to catch the case where an external route is - * both in @routes and @routes_prune list. In that case, - * @routes should win and we should not remove the address. */ - if (!routes_idx) { - routes_idx = g_hash_table_new((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal); - } - g_hash_table_add(routes_idx, (gpointer) conf_o); - continue; - } - } - } - for (i = 0; i < routes_prune->len; i++) { const NMPObject *prune_o; @@ -6273,7 +6249,6 @@ nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf "%s" /* flags */ "%s" /* label */ " src %s" - "%s" /* external */ "%s" /* a_acd_not_ready */ "%s" /* a_assume_config_once */ "", @@ -6293,7 +6268,6 @@ nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf _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->external ? " ext" : "", address->a_acd_not_ready ? " ip4acd-not-ready" : "", address->a_assume_config_once ? " assume-config-once" : ""); g_free(str_peer); @@ -6415,7 +6389,6 @@ nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf buf, len, "%s/%d lft %s pref %s%s%s%s%s src %s" - "%s" /* external */ "%s" /* a_assume_config_once */ "", s_address, @@ -6427,7 +6400,6 @@ nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf 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->external ? " external" : "", address->a_assume_config_once ? " assume-config-once" : ""); g_free(str_peer); return buf; @@ -6519,7 +6491,6 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz "%s" /* initcwnd */ "%s" /* initrwnd */ "%s" /* mtu */ - "%s" /* is_external */ "%s" /* r_assume_config_once */ "", nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced), @@ -6577,7 +6548,6 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz route->lock_mtu ? "lock " : "", route->mtu) : "", - route->is_external ? " is-external" : "", route->r_assume_config_once ? " assume-config-once" : ""); return buf; } @@ -6648,7 +6618,6 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz "%s" /* initrwnd */ "%s" /* mtu */ "%s" /* pref */ - "%s" /* is_external */ "%s" /* r_assume_config_once */ "", nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced), @@ -6710,7 +6679,6 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz " pref %s", nm_icmpv6_router_pref_to_string(route->rt_pref, str_pref2, sizeof(str_pref2))) : "", - route->is_external ? " is-external" : "", route->r_assume_config_once ? " assume-config-once" : ""); return buf; @@ -7845,7 +7813,6 @@ nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState obj->address, obj->peer_address, NM_HASH_COMBINE_BOOLS(guint8, - obj->external, obj->use_ip4_broadcast_address, obj->a_acd_not_ready, obj->a_assume_config_once)); @@ -7869,7 +7836,6 @@ nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Ad NM_CMP_FIELD(a, b, preferred); NM_CMP_FIELD(a, b, n_ifa_flags); NM_CMP_FIELD_STR(a, b, label); - NM_CMP_FIELD_UNSAFE(a, b, external); NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready); NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once); return 0; @@ -7888,7 +7854,7 @@ nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState obj->plen, obj->address, obj->peer_address, - NM_HASH_COMBINE_BOOLS(guint8, obj->external, obj->a_assume_config_once)); + NM_HASH_COMBINE_BOOLS(guint8, obj->a_assume_config_once)); } int @@ -7908,7 +7874,6 @@ nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Ad NM_CMP_FIELD(a, b, lifetime); NM_CMP_FIELD(a, b, preferred); NM_CMP_FIELD(a, b, n_ifa_flags); - NM_CMP_FIELD_UNSAFE(a, b, external); NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once); return 0; } @@ -8018,7 +7983,6 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->lock_initcwnd, obj->lock_initrwnd, obj->lock_mtu, - obj->is_external, obj->r_assume_config_once)); break; } @@ -8110,7 +8074,6 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, NM_CMP_FIELD(a, b, initrwnd); NM_CMP_FIELD(a, b, mtu); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) { - NM_CMP_FIELD_UNSAFE(a, b, is_external); NM_CMP_FIELD_UNSAFE(a, b, r_assume_config_once); } break; @@ -8205,7 +8168,6 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, obj->lock_initcwnd, obj->lock_initrwnd, obj->lock_mtu, - obj->is_external, obj->r_assume_config_once), obj->window, obj->cwnd, @@ -8290,7 +8252,6 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, else NM_CMP_FIELD(a, b, rt_pref); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) { - NM_CMP_FIELD_UNSAFE(a, b, is_external); NM_CMP_FIELD_UNSAFE(a, b, r_assume_config_once); } break; diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index 7101b48c4f..d3d8ae3070 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -318,10 +318,6 @@ typedef enum { * IFA_FLAGS attribute. */ \ guint32 n_ifa_flags; \ \ - /* FIXME(l3cfg): the external marker won't be necessary anymore, because we only - * merge addresses we care about, and ignore (don't remove) external addresses. */ \ - bool external : 1; \ - \ bool use_ip4_broadcast_address : 1; \ \ /* Whether the address is should be configured once during assume. This is a meta flag @@ -471,14 +467,6 @@ typedef union { * the "table_coerced" field is ignored (unlike for the metric). */ \ bool table_any : 1; \ \ - /* This route is tracked as external route, that is not a route that NetworkManager - * actively wants to add, but a route that was added externally. In some cases, such - * a route should be ignored. - * - * Note that unlike most other fields here, this flag only exists inside NetworkManager - * and is not reflected on netlink. */ \ - bool is_external : 1; \ - \ /* Whether the route is should be configured once during assume. This is a meta flag * that is not honored by NMPlatform (netlink code). Instead, it can be used by the upper * layers which use NMPlatformIPRoute to track routes that should be configured. */ \ diff --git a/src/libnm-systemd-core/meson.build b/src/libnm-systemd-core/meson.build index fb472a7654..1fa9448645 100644 --- a/src/libnm-systemd-core/meson.build +++ b/src/libnm-systemd-core/meson.build @@ -18,8 +18,6 @@ libnm_systemd_core = static_library( 'src/libsystemd-network/sd-dhcp-lease.c', 'src/libsystemd-network/sd-dhcp6-client.c', 'src/libsystemd-network/sd-dhcp6-lease.c', - 'src/libsystemd-network/sd-ipv4acd.c', - 'src/libsystemd-network/sd-ipv4ll.c', 'src/libsystemd-network/sd-lldp-rx.c', 'src/libsystemd/sd-event/event-util.c', 'src/libsystemd/sd-event/sd-event.c', diff --git a/src/libnm-systemd-core/nm-sd.h b/src/libnm-systemd-core/nm-sd.h index 0ec2f16e5d..421f53729b 100644 --- a/src/libnm-systemd-core/nm-sd.h +++ b/src/libnm-systemd-core/nm-sd.h @@ -8,9 +8,7 @@ #include "src/systemd/sd-dhcp-client.h" #include "src/systemd/sd-dhcp6-client.h" -#include "src/systemd/sd-ipv4ll.h" #include "src/systemd/sd-lldp-rx.h" -#include "src/systemd/sd-lldp.h" /*****************************************************************************/ diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c deleted file mode 100644 index f0612ed583..0000000000 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c +++ /dev/null @@ -1,616 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/*** - Copyright © 2014 Axis Communications AB. All rights reserved. -***/ - -#include "nm-sd-adapt-core.h" - -#include -#include -#include -#include -#include - -#include "sd-ipv4acd.h" - -#include "alloc-util.h" -#include "arp-util.h" -#include "ether-addr-util.h" -#include "event-util.h" -#include "fd-util.h" -#include "in-addr-util.h" -#include "memory-util.h" -#include "network-common.h" -#include "random-util.h" -#include "siphash24.h" -#include "string-table.h" -#include "string-util.h" -#include "time-util.h" - -/* Constants from the RFC */ -#define PROBE_WAIT_USEC (1U * USEC_PER_SEC) -#define PROBE_NUM 3U -#define PROBE_MIN_USEC (1U * USEC_PER_SEC) -#define PROBE_MAX_USEC (2U * USEC_PER_SEC) -#define ANNOUNCE_WAIT_USEC (2U * USEC_PER_SEC) -#define ANNOUNCE_NUM 2U -#define ANNOUNCE_INTERVAL_USEC (2U * USEC_PER_SEC) -#define MAX_CONFLICTS 10U -#define RATE_LIMIT_INTERVAL_USEC (60U * USEC_PER_SEC) -#define DEFEND_INTERVAL_USEC (10U * USEC_PER_SEC) - -typedef enum IPv4ACDState { - IPV4ACD_STATE_INIT, - IPV4ACD_STATE_STARTED, - IPV4ACD_STATE_WAITING_PROBE, - IPV4ACD_STATE_PROBING, - IPV4ACD_STATE_WAITING_ANNOUNCE, - IPV4ACD_STATE_ANNOUNCING, - IPV4ACD_STATE_RUNNING, - _IPV4ACD_STATE_MAX, - _IPV4ACD_STATE_INVALID = -EINVAL, -} IPv4ACDState; - -struct sd_ipv4acd { - unsigned n_ref; - - IPv4ACDState state; - int ifindex; - int fd; - - char *ifname; - unsigned n_iteration; - unsigned n_conflict; - - sd_event_source *receive_message_event_source; - sd_event_source *timer_event_source; - - usec_t defend_window; - struct in_addr address; - - /* External */ - struct ether_addr mac_addr; - - sd_event *event; - int event_priority; - sd_ipv4acd_callback_t callback; - void *userdata; - sd_ipv4acd_check_mac_callback_t check_mac_callback; - void *check_mac_userdata; -}; - -#define log_ipv4acd_errno(acd, error, fmt, ...) \ - log_interface_prefix_full_errno( \ - "IPv4ACD: ", \ - sd_ipv4acd, acd, \ - error, fmt, ##__VA_ARGS__) -#define log_ipv4acd(acd, fmt, ...) \ - log_interface_prefix_full_errno_zerook( \ - "IPv4ACD: ", \ - sd_ipv4acd, acd, \ - 0, fmt, ##__VA_ARGS__) - -static const char * const ipv4acd_state_table[_IPV4ACD_STATE_MAX] = { - [IPV4ACD_STATE_INIT] = "init", - [IPV4ACD_STATE_STARTED] = "started", - [IPV4ACD_STATE_WAITING_PROBE] = "waiting-probe", - [IPV4ACD_STATE_PROBING] = "probing", - [IPV4ACD_STATE_WAITING_ANNOUNCE] = "waiting-announce", - [IPV4ACD_STATE_ANNOUNCING] = "announcing", - [IPV4ACD_STATE_RUNNING] = "running", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(ipv4acd_state, IPv4ACDState); - -static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) { - assert(acd); - assert(st < _IPV4ACD_STATE_MAX); - - if (st != acd->state) - log_ipv4acd(acd, "%s -> %s", ipv4acd_state_to_string(acd->state), ipv4acd_state_to_string(st)); - - if (st == acd->state && !reset_counter) - acd->n_iteration++; - else { - acd->state = st; - acd->n_iteration = 0; - } -} - -static void ipv4acd_reset(sd_ipv4acd *acd) { - assert(acd); - - (void) event_source_disable(acd->timer_event_source); - acd->receive_message_event_source = sd_event_source_disable_unref(acd->receive_message_event_source); - - acd->fd = safe_close(acd->fd); - - ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true); -} - -static sd_ipv4acd *ipv4acd_free(sd_ipv4acd *acd) { - assert(acd); - - ipv4acd_reset(acd); - sd_event_source_unref(acd->timer_event_source); - sd_ipv4acd_detach_event(acd); - free(acd->ifname); - return mfree(acd); -} - -DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4acd, sd_ipv4acd, ipv4acd_free); - -int sd_ipv4acd_new(sd_ipv4acd **ret) { - _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL; - - assert_return(ret, -EINVAL); - - acd = new(sd_ipv4acd, 1); - if (!acd) - return -ENOMEM; - - *acd = (sd_ipv4acd) { - .n_ref = 1, - .state = IPV4ACD_STATE_INIT, - .ifindex = -1, - .fd = -1, - }; - - *ret = TAKE_PTR(acd); - - return 0; -} - -static void ipv4acd_client_notify(sd_ipv4acd *acd, int event) { - assert(acd); - - if (!acd->callback) - return; - - acd->callback(acd, event, acd->userdata); -} - -int sd_ipv4acd_stop(sd_ipv4acd *acd) { - IPv4ACDState old_state; - - if (!acd) - return 0; - - old_state = acd->state; - - ipv4acd_reset(acd); - - if (old_state == IPV4ACD_STATE_INIT) - return 0; - - log_ipv4acd(acd, "STOPPED"); - - ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_STOP); - - return 0; -} - -static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata); - -static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) { - usec_t next_timeout, time_now; - - assert(acd); - - next_timeout = usec; - - if (random_usec > 0) - next_timeout += (usec_t) random_u64() % random_usec; - - assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0); - - return event_reset_time(acd->event, &acd->timer_event_source, - clock_boottime_or_monotonic(), - time_now + next_timeout, 0, - ipv4acd_on_timeout, acd, - acd->event_priority, "ipv4acd-timer", true); -} - -static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) { - sd_ipv4acd *acd = userdata; - int r = 0; - - assert(acd); - - switch (acd->state) { - - case IPV4ACD_STATE_STARTED: - acd->defend_window = 0; - - ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true); - - if (acd->n_conflict >= MAX_CONFLICTS) { - log_ipv4acd(acd, "Max conflicts reached, delaying by %s", - FORMAT_TIMESPAN(RATE_LIMIT_INTERVAL_USEC, 0)); - r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC); - } else - r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC); - if (r < 0) - goto fail; - - break; - - case IPV4ACD_STATE_WAITING_PROBE: - case IPV4ACD_STATE_PROBING: - /* Send a probe */ - r = arp_send_probe(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr); - if (r < 0) { - log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m"); - goto fail; - } - - log_ipv4acd(acd, "Probing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address)); - - if (acd->n_iteration < PROBE_NUM - 2) { - ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false); - - r = ipv4acd_set_next_wakeup(acd, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC)); - if (r < 0) - goto fail; - } else { - ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_ANNOUNCE, true); - - r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT_USEC, 0); - if (r < 0) - goto fail; - } - - break; - - case IPV4ACD_STATE_ANNOUNCING: - if (acd->n_iteration >= ANNOUNCE_NUM - 1) { - ipv4acd_set_state(acd, IPV4ACD_STATE_RUNNING, false); - break; - } - - _fallthrough_; - case IPV4ACD_STATE_WAITING_ANNOUNCE: - /* Send announcement packet */ - r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr); - if (r < 0) { - log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m"); - goto fail; - } - - log_ipv4acd(acd, "Announcing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address)); - - ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false); - - r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_INTERVAL_USEC, 0); - if (r < 0) - goto fail; - - if (acd->n_iteration == 0) { - acd->n_conflict = 0; - ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_BIND); - } - - break; - - default: - assert_not_reached(); - } - - return 0; - -fail: - sd_ipv4acd_stop(acd); - return 0; -} - -static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, const struct ether_arp *arp, bool announced) { - assert(acd); - assert(arp); - - /* RFC 5227 section 2.1.1. - * "the host receives any ARP packet (Request *or* Reply) on the interface where the probe is - * being performed, where the packet's 'sender IP address' is the address being probed for, - * then the host MUST treat this address as being in use by some other host" */ - if (memcmp(arp->arp_spa, &acd->address, sizeof(struct in_addr)) == 0) - return true; - - if (announced) - /* the TPA matched instead of SPA, this is not a conflict */ - return false; - - /* "any ARP Probe where the packet's 'target IP address' is the address being probed for, and - * the packet's 'sender hardware address' is not the hardware address of any of the host's - * interfaces, then the host SHOULD similarly treat this as an address conflict" */ - if (arp->ea_hdr.ar_op != htobe16(ARPOP_REQUEST)) - return false; /* not ARP Request, ignoring. */ - if (memeqzero(arp->arp_spa, sizeof(struct in_addr)) == 0) - return false; /* not ARP Probe, ignoring. */ - if (memcmp(arp->arp_tpa, &acd->address, sizeof(struct in_addr)) != 0) - return false; /* target IP address does not match, BPF code is broken? */ - - if (acd->check_mac_callback && - acd->check_mac_callback(acd, (const struct ether_addr*) arp->arp_sha, acd->check_mac_userdata) > 0) - /* sender hardware is one of the host's interfaces, ignoring. */ - return true; - - return true; /* conflict! */ -} - -static void ipv4acd_on_conflict(sd_ipv4acd *acd) { - assert(acd); - - acd->n_conflict++; - - log_ipv4acd(acd, "Conflict on "IPV4_ADDRESS_FMT_STR" (%u)", IPV4_ADDRESS_FMT_VAL(acd->address), acd->n_conflict); - - ipv4acd_reset(acd); - ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT); -} - -static int ipv4acd_on_packet( - sd_event_source *s, - int fd, - uint32_t revents, - void *userdata) { - - sd_ipv4acd *acd = userdata; - struct ether_arp packet; - ssize_t n; - int r; - - assert(s); - assert(acd); - assert(fd >= 0); - - n = recv(fd, &packet, sizeof(struct ether_arp), 0); - if (n < 0) { - if (IN_SET(errno, EAGAIN, EINTR)) - return 0; - - log_ipv4acd_errno(acd, errno, "Failed to read ARP packet: %m"); - goto fail; - } - if ((size_t) n != sizeof(struct ether_arp)) { - log_ipv4acd(acd, "Ignoring too short ARP packet."); - return 0; - } - - switch (acd->state) { - - case IPV4ACD_STATE_ANNOUNCING: - case IPV4ACD_STATE_RUNNING: - - if (ipv4acd_arp_conflict(acd, &packet, true)) { - usec_t ts; - - assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0); - - /* Defend address */ - if (ts > acd->defend_window) { - acd->defend_window = ts + DEFEND_INTERVAL_USEC; - r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr); - if (r < 0) { - log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m"); - goto fail; - } - - log_ipv4acd(acd, "Defending "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address)); - - } else - ipv4acd_on_conflict(acd); - } - break; - - case IPV4ACD_STATE_WAITING_PROBE: - case IPV4ACD_STATE_PROBING: - case IPV4ACD_STATE_WAITING_ANNOUNCE: - if (ipv4acd_arp_conflict(acd, &packet, false)) - ipv4acd_on_conflict(acd); - break; - - default: - assert_not_reached(); - } - - return 0; - -fail: - sd_ipv4acd_stop(acd); - return 0; -} - -int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int ifindex) { - assert_return(acd, -EINVAL); - assert_return(ifindex > 0, -EINVAL); - assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); - - acd->ifindex = ifindex; - - return 0; -} - -int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd) { - if (!acd) - return -EINVAL; - - return acd->ifindex; -} - -int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *ifname) { - assert_return(acd, -EINVAL); - assert_return(ifname, -EINVAL); - - if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE)) - return -EINVAL; - - return free_and_strdup(&acd->ifname, ifname); -} - -int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret) { - int r; - - assert_return(acd, -EINVAL); - - r = get_ifname(acd->ifindex, &acd->ifname); - if (r < 0) - return r; - - if (ret) - *ret = acd->ifname; - - return 0; -} - -int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) { - int r; - - assert_return(acd, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(!ether_addr_is_null(addr), -EINVAL); - - acd->mac_addr = *addr; - - if (!sd_ipv4acd_is_running(acd)) - return 0; - - assert(acd->fd >= 0); - r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr); - if (r < 0) { - ipv4acd_reset(acd); - return r; - } - - return 0; -} - -int sd_ipv4acd_detach_event(sd_ipv4acd *acd) { - assert_return(acd, -EINVAL); - - acd->event = sd_event_unref(acd->event); - - return 0; -} - -int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority) { - int r; - - assert_return(acd, -EINVAL); - assert_return(!acd->event, -EBUSY); - - if (event) - acd->event = sd_event_ref(event); - else { - r = sd_event_default(&acd->event); - if (r < 0) - return r; - } - - acd->event_priority = priority; - - return 0; -} - -int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata) { - assert_return(acd, -EINVAL); - - acd->callback = cb; - acd->userdata = userdata; - - return 0; -} - -int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata) { - assert_return(acd, -EINVAL); - - acd->check_mac_callback = cb; - acd->check_mac_userdata = userdata; - return 0; -} - -int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) { - int r; - - assert_return(acd, -EINVAL); - assert_return(address, -EINVAL); - assert_return(in4_addr_is_set(address), -EINVAL); - - if (in4_addr_equal(&acd->address, address)) - return 0; - - acd->address = *address; - - if (!sd_ipv4acd_is_running(acd)) - return 0; - - assert(acd->fd >= 0); - r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr); - if (r < 0) - goto fail; - - r = ipv4acd_set_next_wakeup(acd, 0, 0); - if (r < 0) - goto fail; - - ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true); - return 0; - -fail: - ipv4acd_reset(acd); - return r; -} - -int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address) { - assert_return(acd, -EINVAL); - assert_return(address, -EINVAL); - - *address = acd->address; - - return 0; -} - -int sd_ipv4acd_is_running(sd_ipv4acd *acd) { - assert_return(acd, false); - - return acd->state != IPV4ACD_STATE_INIT; -} - -int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) { - int r; - - assert_return(acd, -EINVAL); - assert_return(acd->event, -EINVAL); - assert_return(acd->ifindex > 0, -EINVAL); - assert_return(in4_addr_is_set(&acd->address), -EINVAL); - assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL); - assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); - - r = arp_network_bind_raw_socket(acd->ifindex, &acd->address, &acd->mac_addr); - if (r < 0) - return r; - - CLOSE_AND_REPLACE(acd->fd, r); - - if (reset_conflicts) - acd->n_conflict = 0; - - r = sd_event_add_io(acd->event, &acd->receive_message_event_source, acd->fd, EPOLLIN, ipv4acd_on_packet, acd); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(acd->receive_message_event_source, acd->event_priority); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(acd->receive_message_event_source, "ipv4acd-receive-message"); - - r = ipv4acd_set_next_wakeup(acd, 0, 0); - if (r < 0) - goto fail; - - ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true); - return 0; - -fail: - ipv4acd_reset(acd); - return r; -} diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c deleted file mode 100644 index 4a500dfc2a..0000000000 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c +++ /dev/null @@ -1,382 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/*** - Copyright © 2014 Axis Communications AB. All rights reserved. -***/ - -#include "nm-sd-adapt-core.h" - -#include -#include -#include -#include - -#include "sd-id128.h" -#include "sd-ipv4acd.h" -#include "sd-ipv4ll.h" - -#include "alloc-util.h" -#include "ether-addr-util.h" -#include "in-addr-util.h" -#include "network-common.h" -#include "random-util.h" -#include "siphash24.h" -#include "sparse-endian.h" -#include "string-util.h" -#include "util.h" - -#define IPV4LL_NETWORK UINT32_C(0xA9FE0000) -#define IPV4LL_NETMASK UINT32_C(0xFFFF0000) - -#define IPV4LL_DONT_DESTROY(ll) \ - _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll) - -struct sd_ipv4ll { - unsigned n_ref; - - sd_ipv4acd *acd; - - be32_t address; /* the address pushed to ACD */ - struct ether_addr mac; - - struct { - le64_t value; - le64_t generation; - } seed; - bool seed_set; - - /* External */ - be32_t claimed_address; - - sd_ipv4ll_callback_t callback; - void *userdata; - - sd_ipv4ll_check_mac_callback_t check_mac_callback; - void *check_mac_userdata; -}; - -#define log_ipv4ll_errno(ll, error, fmt, ...) \ - log_interface_prefix_full_errno( \ - "IPv4LL: ", \ - sd_ipv4ll, ll, \ - error, fmt, ##__VA_ARGS__) -#define log_ipv4ll(ll, fmt, ...) \ - log_interface_prefix_full_errno_zerook( \ - "IPv4LL: ", \ - sd_ipv4ll, ll, \ - 0, fmt, ##__VA_ARGS__) - -static void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata); -static int ipv4ll_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata); - -static sd_ipv4ll *ipv4ll_free(sd_ipv4ll *ll) { - assert(ll); - - sd_ipv4acd_unref(ll->acd); - return mfree(ll); -} - -DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4ll, sd_ipv4ll, ipv4ll_free); - -int sd_ipv4ll_new(sd_ipv4ll **ret) { - _cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL; - int r; - - assert_return(ret, -EINVAL); - - ll = new0(sd_ipv4ll, 1); - if (!ll) - return -ENOMEM; - - ll->n_ref = 1; - - r = sd_ipv4acd_new(&ll->acd); - if (r < 0) - return r; - - r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll); - if (r < 0) - return r; - - r = sd_ipv4acd_set_check_mac_callback(ll->acd, ipv4ll_check_mac, ll); - if (r < 0) - return r; - - *ret = TAKE_PTR(ll); - - return 0; -} - -int sd_ipv4ll_stop(sd_ipv4ll *ll) { - if (!ll) - return 0; - - return sd_ipv4acd_stop(ll->acd); -} - -int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int ifindex) { - assert_return(ll, -EINVAL); - assert_return(ifindex > 0, -EINVAL); - assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); - - return sd_ipv4acd_set_ifindex(ll->acd, ifindex); -} - -int sd_ipv4ll_get_ifindex(sd_ipv4ll *ll) { - if (!ll) - return -EINVAL; - - return sd_ipv4acd_get_ifindex(ll->acd); -} - -int sd_ipv4ll_set_ifname(sd_ipv4ll *ll, const char *ifname) { - assert_return(ll, -EINVAL); - assert_return(ifname, -EINVAL); - - return sd_ipv4acd_set_ifname(ll->acd, ifname); -} - -int sd_ipv4ll_get_ifname(sd_ipv4ll *ll, const char **ret) { - assert_return(ll, -EINVAL); - - return sd_ipv4acd_get_ifname(ll->acd, ret); -} - -int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) { - int r; - - assert_return(ll, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(!ether_addr_is_null(addr), -EINVAL); - - r = sd_ipv4acd_set_mac(ll->acd, addr); - if (r < 0) - return r; - - ll->mac = *addr; - return 0; -} - -int sd_ipv4ll_detach_event(sd_ipv4ll *ll) { - assert_return(ll, -EINVAL); - - return sd_ipv4acd_detach_event(ll->acd); -} - -int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) { - assert_return(ll, -EINVAL); - - return sd_ipv4acd_attach_event(ll->acd, event, priority); -} - -int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) { - assert_return(ll, -EINVAL); - - ll->callback = cb; - ll->userdata = userdata; - - return 0; -} - -int sd_ipv4ll_set_check_mac_callback(sd_ipv4ll *ll, sd_ipv4ll_check_mac_callback_t cb, void *userdata) { - assert_return(ll, -EINVAL); - - ll->check_mac_callback = cb; - ll->check_mac_userdata = userdata; - - return 0; -} - -int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) { - assert_return(ll, -EINVAL); - assert_return(address, -EINVAL); - - if (ll->claimed_address == 0) - return -ENOENT; - - address->s_addr = ll->claimed_address; - - return 0; -} - -int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed) { - assert_return(ll, -EINVAL); - assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); - - ll->seed.value = htole64(seed); - ll->seed_set = true; - - return 0; -} - -int sd_ipv4ll_is_running(sd_ipv4ll *ll) { - assert_return(ll, false); - - return sd_ipv4acd_is_running(ll->acd); -} - -static bool ipv4ll_address_is_valid(const struct in_addr *address) { - assert(address); - - if (!in4_addr_is_link_local(address)) - return false; - - return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U); -} - -int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) { - int r; - - assert_return(ll, -EINVAL); - assert_return(address, -EINVAL); - assert_return(ipv4ll_address_is_valid(address), -EINVAL); - - r = sd_ipv4acd_set_address(ll->acd, address); - if (r < 0) - return r; - - ll->address = address->s_addr; - - return 0; -} - -#define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b) - -static int ipv4ll_pick_address(sd_ipv4ll *ll) { - _cleanup_free_ char *address = NULL; - be32_t addr; - - assert(ll); - - do { - uint64_t h; - - h = siphash24(&ll->seed, sizeof(ll->seed), PICK_HASH_KEY.bytes); - - /* Increase the generation counter by one */ - ll->seed.generation = htole64(le64toh(ll->seed.generation) + 1); - - addr = htobe32((h & UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK); - } while (addr == ll->address || - IN_SET(be32toh(addr) & 0x0000FF00U, 0x0000U, 0xFF00U)); - - (void) in_addr_to_string(AF_INET, &(union in_addr_union) { .in.s_addr = addr }, &address); - log_ipv4ll(ll, "Picked new IP address %s.", strna(address)); - - return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr }); -} - -#define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2) - -static int ipv4ll_start_internal(sd_ipv4ll *ll, bool reset_generation) { - int r; - bool picked_address = false; - - assert_return(ll, -EINVAL); - assert_return(!ether_addr_is_null(&ll->mac), -EINVAL); - - /* If no random seed is set, generate some from the MAC address */ - if (!ll->seed_set) - ll->seed.value = htole64(siphash24(ll->mac.ether_addr_octet, ETH_ALEN, MAC_HASH_KEY.bytes)); - - if (reset_generation) - ll->seed.generation = 0; - - if (ll->address == 0) { - r = ipv4ll_pick_address(ll); - if (r < 0) - return r; - - picked_address = true; - } - - r = sd_ipv4acd_start(ll->acd, reset_generation); - if (r < 0) { - - /* We couldn't start? If so, let's forget the picked address again, the user might make a change and - * retry, and we want the new data to take effect when picking an address. */ - if (picked_address) - ll->address = 0; - - return r; - } - - return 1; -} - -int sd_ipv4ll_start(sd_ipv4ll *ll) { - assert_return(ll, -EINVAL); - - if (sd_ipv4ll_is_running(ll)) - return 0; - - return ipv4ll_start_internal(ll, true); -} - -int sd_ipv4ll_restart(sd_ipv4ll *ll) { - ll->address = 0; - - return ipv4ll_start_internal(ll, false); -} - -static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) { - assert(ll); - - if (ll->callback) - ll->callback(ll, event, ll->userdata); -} - -void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) { - sd_ipv4ll *ll = userdata; - IPV4LL_DONT_DESTROY(ll); - int r; - - assert(acd); - assert(ll); - - switch (event) { - - case SD_IPV4ACD_EVENT_STOP: - ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP); - ll->claimed_address = 0; - break; - - case SD_IPV4ACD_EVENT_BIND: - ll->claimed_address = ll->address; - ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND); - break; - - case SD_IPV4ACD_EVENT_CONFLICT: - /* if an address was already bound we must call up to the - user to handle this, otherwise we just try again */ - if (ll->claimed_address != 0) { - ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT); - - ll->claimed_address = 0; - } else { - r = sd_ipv4ll_restart(ll); - if (r < 0) - goto error; - } - - break; - - default: - assert_not_reached(); - } - - return; - -error: - ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP); -} - -static int ipv4ll_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) { - sd_ipv4ll *ll = userdata; - - assert(ll); - - if (ll->check_mac_callback) - return ll->check_mac_callback(ll, mac, ll->check_mac_userdata); - - return 0; -} diff --git a/src/libnm-systemd-core/src/systemd/sd-ipv4acd.h b/src/libnm-systemd-core/src/systemd/sd-ipv4acd.h deleted file mode 100644 index 90d3f0a0d1..0000000000 --- a/src/libnm-systemd-core/src/systemd/sd-ipv4acd.h +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#ifndef foosdipv4acdfoo -#define foosdipv4acdfoo - -/*** - Copyright © 2014 Axis Communications AB. All rights reserved. - 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 "sd-event.h" - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -enum { - SD_IPV4ACD_EVENT_STOP = 0, - SD_IPV4ACD_EVENT_BIND = 1, - SD_IPV4ACD_EVENT_CONFLICT = 2, -}; - -typedef struct sd_ipv4acd sd_ipv4acd; -typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *acd, int event, void *userdata); -typedef int (*sd_ipv4acd_check_mac_callback_t)(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata); - -int sd_ipv4acd_detach_event(sd_ipv4acd *acd); -int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority); -int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address); -int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata); -int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata); -int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr); -int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int interface_index); -int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd); -int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *interface_name); -int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret); -int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address); -int sd_ipv4acd_is_running(sd_ipv4acd *acd); -int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts); -int sd_ipv4acd_stop(sd_ipv4acd *acd); -sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd); -sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd); -int sd_ipv4acd_new(sd_ipv4acd **ret); - -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4acd, sd_ipv4acd_unref); - -_SD_END_DECLARATIONS; - -#endif diff --git a/src/libnm-systemd-core/src/systemd/sd-ipv4ll.h b/src/libnm-systemd-core/src/systemd/sd-ipv4ll.h deleted file mode 100644 index ed014b53f2..0000000000 --- a/src/libnm-systemd-core/src/systemd/sd-ipv4ll.h +++ /dev/null @@ -1,65 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#ifndef foosdipv4llfoo -#define foosdipv4llfoo - -/*** - Copyright © 2014 Axis Communications AB. All rights reserved. - - 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 "sd-event.h" - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -enum { - SD_IPV4LL_EVENT_STOP = 0, - SD_IPV4LL_EVENT_BIND = 1, - SD_IPV4LL_EVENT_CONFLICT = 2, -}; - -typedef struct sd_ipv4ll sd_ipv4ll; -typedef void (*sd_ipv4ll_callback_t)(sd_ipv4ll *ll, int event, void *userdata); -typedef int (*sd_ipv4ll_check_mac_callback_t)(sd_ipv4ll *ll, const struct ether_addr *mac, void *userdata); - -int sd_ipv4ll_detach_event(sd_ipv4ll *ll); -int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority); -int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address); -int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata); -int sd_ipv4ll_set_check_mac_callback(sd_ipv4ll *ll, sd_ipv4ll_check_mac_callback_t cb, void *userdata); -int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr); -int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int interface_index); -int sd_ipv4ll_get_ifindex(sd_ipv4ll *ll); -int sd_ipv4ll_set_ifname(sd_ipv4ll *ll, const char *interface_name); -int sd_ipv4ll_get_ifname(sd_ipv4ll *ll, const char **ret); -int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address); -int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed); -int sd_ipv4ll_is_running(sd_ipv4ll *ll); -int sd_ipv4ll_restart(sd_ipv4ll *ll); -int sd_ipv4ll_start(sd_ipv4ll *ll); -int sd_ipv4ll_stop(sd_ipv4ll *ll); -sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll); -sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll); -int sd_ipv4ll_new(sd_ipv4ll **ret); - -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4ll, sd_ipv4ll_unref); - -_SD_END_DECLARATIONS; - -#endif diff --git a/tools/test-build.sh b/tools/test-build.sh index cb5199c856..04d33b6ae4 100755 --- a/tools/test-build.sh +++ b/tools/test-build.sh @@ -35,7 +35,6 @@ if [ "${#TARGETS}" -lt 1 ]; then src/core/NetworkManager src/core/dhcp/nm-dhcp-helper src/core/ndisc/tests/test-ndisc-linux - src/core/nm-iface-helper src/core/platform/tests/monitor src/nm-online/nm-online src/nmcli/nmcli From e732cb9a70af513e14ad03464de357f0d656a175 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 18 Oct 2021 15:23:40 +0200 Subject: [PATCH 02/45] device: clear shared4 l3cd on disconnect Fixes-test: @vlan_connection_down_with_autoconnect Fixes-test: @vlan_disconnect_device_with_autoconnect --- src/core/devices/nm-device.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 627066d94a..7343c9cf5f 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -11805,6 +11805,8 @@ _dev_ipsharedx_cleanup(NMDevice *self, int addr_family) nm_clear_pointer(&priv->ipshared_data_4.v4.shared_ip_handle, nm_netns_shared_ip_release); nm_clear_l3cd(&priv->ipshared_data_4.v4.l3cd); + + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_SHARED_4, NULL, FALSE); } _dev_ipsharedx_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE); From 23d61367b3648a151b8b6ef16b0ec9f7ffd493fa Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 18 Oct 2021 17:45:52 +0200 Subject: [PATCH 03/45] core: set the route table for the ipv6 link-local method Previously the prefix route fe80::/64 was always added to the main table. Fixes-test: @ipv6_route_set_route_with_tables --- src/core/devices/nm-device.c | 23 +++++++++++++++-------- src/core/nm-l3-config-data.c | 1 + src/core/nm-l3-ipv6ll.c | 6 +++++- src/core/nm-l3-ipv6ll.h | 5 +++++ src/core/tests/test-l3cfg.c | 1 + 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 7343c9cf5f..d8f33cfb86 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -10563,13 +10563,15 @@ _dev_ipll6_start(NMDevice *self) const char * stable_id; stable_id = _prop_get_connection_stable_id(self, connection, &stable_type); - priv->ipll_data_6.v6.ipv6ll = nm_l3_ipv6ll_new_stable_privacy(priv->l3cfg, - assume, - stable_type, - ifname, - stable_id, - _dev_ipll6_state_change_cb, - self); + priv->ipll_data_6.v6.ipv6ll = + nm_l3_ipv6ll_new_stable_privacy(priv->l3cfg, + assume, + stable_type, + ifname, + stable_id, + nm_device_get_route_table(self, AF_INET6), + _dev_ipll6_state_change_cb, + self); } else { NMUtilsIPv6IfaceId iid; @@ -10579,7 +10581,12 @@ _dev_ipll6_start(NMDevice *self) } priv->ipll_data_6.v6.ipv6ll = - nm_l3_ipv6ll_new_token(priv->l3cfg, assume, &iid, _dev_ipll6_state_change_cb, self); + nm_l3_ipv6ll_new_token(priv->l3cfg, + assume, + &iid, + nm_device_get_route_table(self, AF_INET6), + _dev_ipll6_state_change_cb, + self); } llstate = nm_l3_ipv6ll_get_state(priv->ipll_data_6.v6.ipv6ll, &lladdr); diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c index deec665d1d..da933d5d04 100644 --- a/src/core/nm-l3-config-data.c +++ b/src/core/nm-l3-config-data.c @@ -2489,6 +2489,7 @@ nm_l3_config_data_add_dependent_device_routes(NML3ConfigData * self, .network = *a6, .plen = plen, }; + nm_platform_ip_route_normalize(addr_family, &rx.rx); nm_l3_config_data_add_route(self, addr_family, NULL, &rx.rx); } diff --git a/src/core/nm-l3-ipv6ll.c b/src/core/nm-l3-ipv6ll.c index 143873213d..17008c7ead 100644 --- a/src/core/nm-l3-ipv6ll.c +++ b/src/core/nm-l3-ipv6ll.c @@ -51,6 +51,8 @@ struct _NML3IPv6LL { * with the configuration. */ const NML3ConfigData *l3cd; + guint32 route_table; + /* "assume" means that we first look whether there is any suitable * IPv6 address on the device, and in that case, try to use that * instead of generating a new one. Otherwise, we always try to @@ -410,7 +412,7 @@ _lladdr_handle_changed(NML3IPv6LL *self) l3cd, NM_L3CFG_CONFIG_PRIORITY_IPV6LL, 0, - 0, + self->route_table, NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4, NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6, 0, @@ -624,6 +626,7 @@ _nm_l3_ipv6ll_new(NML3Cfg * l3cfg, const char * ifname, const char * network_id, const NMUtilsIPv6IfaceId *token_iid, + guint32 route_table, NML3IPv6LLNotifyFcn notify_fcn, gpointer user_data) { @@ -647,6 +650,7 @@ _nm_l3_ipv6ll_new(NML3Cfg * l3cfg, .cur_lladdr_obj = NULL, .cur_lladdr = IN6ADDR_ANY_INIT, .assume = assume, + .route_table = route_table, .addrgen = { .stable_type = stable_type, diff --git a/src/core/nm-l3-ipv6ll.h b/src/core/nm-l3-ipv6ll.h index 770a4268a3..3a4f4b4376 100644 --- a/src/core/nm-l3-ipv6ll.h +++ b/src/core/nm-l3-ipv6ll.h @@ -48,6 +48,7 @@ NML3IPv6LL *_nm_l3_ipv6ll_new(NML3Cfg * l3cfg, const char * ifname, const char * network_id, const NMUtilsIPv6IfaceId *token_iid, + guint32 route_table, NML3IPv6LLNotifyFcn notify_fcn, gpointer user_data); @@ -57,6 +58,7 @@ nm_l3_ipv6ll_new_stable_privacy(NML3Cfg * l3cfg, NMUtilsStableType stable_type, const char * ifname, const char * network_id, + guint32 route_table, NML3IPv6LLNotifyFcn notify_fcn, gpointer user_data) { @@ -67,6 +69,7 @@ nm_l3_ipv6ll_new_stable_privacy(NML3Cfg * l3cfg, ifname, network_id, NULL, + route_table, notify_fcn, user_data); } @@ -75,6 +78,7 @@ static inline NML3IPv6LL * nm_l3_ipv6ll_new_token(NML3Cfg * l3cfg, gboolean assume, const NMUtilsIPv6IfaceId *token_iid, + guint32 route_table, NML3IPv6LLNotifyFcn notify_fcn, gpointer user_data) { @@ -84,6 +88,7 @@ nm_l3_ipv6ll_new_token(NML3Cfg * l3cfg, NULL, NULL, token_iid, + route_table, notify_fcn, user_data); } diff --git a/src/core/tests/test-l3cfg.c b/src/core/tests/test-l3cfg.c index b5c85b8224..f02b0f037c 100644 --- a/src/core/tests/test-l3cfg.c +++ b/src/core/tests/test-l3cfg.c @@ -1025,6 +1025,7 @@ test_l3_ipv6ll(gconstpointer test_data) NM_UTILS_STABLE_TYPE_UUID, tdata->f->ifname0, "b6a5b934-c649-43dc-a524-3dfdb74f9419", + 0, _test_l3_ipv6ll_callback_changed, tdata); From e9c17fcc9b33ac300c2bf082e3ce0543bee99492 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 18 Oct 2021 18:07:30 +0200 Subject: [PATCH 04/45] l3cfg: default to 'main' route table sync mode If no l3cd specifies a route table sync mode, use 'main' as default. Fixes-test: @ipv6_route_set_route_with_tables_reapply --- src/core/nm-l3cfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index 24f139b7d4..72d3bbc7b5 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -3997,7 +3997,7 @@ _l3_commit_one(NML3Cfg * self, } if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_NONE) - route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_ALL; + route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN; if (commit_type == NM_L3_CFG_COMMIT_TYPE_REAPPLY) { addresses_prune = nm_platform_ip_address_get_prune_list(self->priv.platform, From 32443ef3b5b7d2a398bab2afca61537a16344068 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 18 Oct 2021 18:30:50 +0200 Subject: [PATCH 05/45] dns: fix assertion failure nm_l3_config_data_get_searches() and nm_l3_config_data_get_domains() require an 'out_len' argument. --- src/core/dns/nm-dns-manager.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c index 1ca33e46ac..15072f799d 100644 --- a/src/core/dns/nm-dns-manager.c +++ b/src/core/dns/nm-dns-manager.c @@ -1405,8 +1405,8 @@ _mgr_configs_data_construct(NMDnsManager *self) if (ip_data->ip_config_type == NM_DNS_IP_CONFIG_TYPE_VPN && nm_l3_config_data_get_never_default(ip_data->l3cd, ip_data->addr_family) == NM_TERNARY_FALSE - && !nm_l3_config_data_get_searches(ip_data->l3cd, ip_data->addr_family, NULL) - && !nm_l3_config_data_get_domains(ip_data->l3cd, ip_data->addr_family, NULL)) + && !nm_l3_config_data_get_searches(ip_data->l3cd, ip_data->addr_family, &num) + && !nm_l3_config_data_get_domains(ip_data->l3cd, ip_data->addr_family, &num)) add_wildcard = TRUE; } From 8d40d03a2c1fc1b2fcfd7453c0ea91e476a82ee1 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 19 Oct 2021 20:34:47 +0200 Subject: [PATCH 06/45] dhcp: honor the send_hostname property --- src/core/dhcp/nm-dhcp-client.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c index 8b9e724968..484ec34e72 100644 --- a/src/core/dhcp/nm-dhcp-client.c +++ b/src/core/dhcp/nm-dhcp-client.c @@ -1012,8 +1012,10 @@ config_init(NMDhcpClientConfig *config, const NMDhcpClientConfig *src) } if (config->hostname) { - if ((config->use_fqdn && !nm_sd_dns_name_is_valid(config->hostname)) - || (!config->use_fqdn && !nm_sd_hostname_is_valid(config->hostname, FALSE))) { + if (!config->send_hostname) { + nm_clear_g_free((gpointer *) &config->hostname); + } else if ((config->use_fqdn && !nm_sd_dns_name_is_valid(config->hostname)) + || (!config->use_fqdn && !nm_sd_hostname_is_valid(config->hostname, FALSE))) { nm_log_warn(LOGD_DHCP, "dhcp%c: %s '%s' is invalid, will be ignored", nm_utils_addr_family_to_char(config->addr_family), From 63ae9cd0351350e1c51793490878333942d33fa7 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 20 Oct 2021 10:40:55 +0200 Subject: [PATCH 07/45] wwan: schedule stage2 when ppp-mgr gets an ifindex --- src/core/devices/wwan/nm-device-modem.c | 27 +++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/core/devices/wwan/nm-device-modem.c b/src/core/devices/wwan/nm-device-modem.c index 97fc1b6d29..c916eb47f9 100644 --- a/src/core/devices/wwan/nm-device-modem.c +++ b/src/core/devices/wwan/nm-device-modem.c @@ -39,6 +39,7 @@ typedef struct { char * apn; bool rf_enabled : 1; NMDeviceStageState stage1_state : 3; + NMDeviceStageState stage2_state : 3; } NMDeviceModemPrivate; struct _NMDeviceModem { @@ -193,7 +194,9 @@ modem_new_config(NMModem * modem, static void ip_ifindex_changed_cb(NMModem *modem, GParamSpec *pspec, gpointer user_data) { - NMDevice *device = NM_DEVICE(user_data); + NMDevice * device = NM_DEVICE(user_data); + NMDeviceModem * self = NM_DEVICE_MODEM(device); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE(self); if (!nm_device_is_activating(device)) return; @@ -210,6 +213,11 @@ ip_ifindex_changed_cb(NMModem *modem, GParamSpec *pspec, gpointer user_data) * RA handling code to run before NM is ready. */ nm_device_sysctl_ip_conf_set(device, AF_INET6, "disable_ipv6", "1"); + + if (priv->stage2_state == NM_DEVICE_STAGE_STATE_PENDING) { + priv->stage2_state = NM_DEVICE_STAGE_STATE_COMPLETED; + nm_device_activate_schedule_stage2_device_config(device, FALSE); + } } static void @@ -457,6 +465,7 @@ deactivate(NMDevice *device) nm_modem_deactivate(priv->modem, device); priv->stage1_state = NM_DEVICE_STAGE_STATE_INIT; + priv->stage2_state = NM_DEVICE_STAGE_STATE_INIT; } /*****************************************************************************/ @@ -516,9 +525,19 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) static NMActStageReturn act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) { - return nm_modem_act_stage2_config(NM_DEVICE_MODEM_GET_PRIVATE(device)->modem, - device, - out_failure_reason); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE(device); + + if (priv->stage2_state == NM_DEVICE_STAGE_STATE_INIT) { + priv->stage2_state = NM_DEVICE_STAGE_STATE_PENDING; + return nm_modem_act_stage2_config(NM_DEVICE_MODEM_GET_PRIVATE(device)->modem, + device, + out_failure_reason); + } + if (priv->stage2_state == NM_DEVICE_STAGE_STATE_PENDING) + return NM_ACT_STAGE_RETURN_POSTPONE; + + nm_assert(priv->stage2_state == NM_DEVICE_STAGE_STATE_COMPLETED); + return NM_ACT_STAGE_RETURN_SUCCESS; } static void From 92e1b6650c08d901ed1991d3483039d948c6bc22 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 20 Oct 2021 10:41:45 +0200 Subject: [PATCH 08/45] wwan: fix signal definition --- src/core/devices/wwan/nm-modem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/devices/wwan/nm-modem.c b/src/core/devices/wwan/nm-modem.c index 579f366c17..fe1b99f72d 100644 --- a/src/core/devices/wwan/nm-modem.c +++ b/src/core/devices/wwan/nm-modem.c @@ -1865,7 +1865,7 @@ nm_modem_class_init(NMModemClass *klass) NULL, NULL, G_TYPE_NONE, - 3, + 6, G_TYPE_INT, /* int addr_family */ G_TYPE_POINTER, /* const NML3ConfigData *l3cd */ G_TYPE_BOOLEAN, /* gboolean do_auto */ From e5d28be02242d16a9b83d2f6356e4e31c9add5ed Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 20 Oct 2021 11:29:02 +0200 Subject: [PATCH 09/45] wwan: don't start generic IP configuration methods for NMDeviceModem --- src/core/devices/wwan/nm-device-modem.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/core/devices/wwan/nm-device-modem.c b/src/core/devices/wwan/nm-device-modem.c index c916eb47f9..176614783d 100644 --- a/src/core/devices/wwan/nm-device-modem.c +++ b/src/core/devices/wwan/nm-device-modem.c @@ -615,6 +615,16 @@ is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags) return TRUE; } +static gboolean +ready_for_ip_config(NMDevice *device) +{ + /* Tell NMDevice to only run device-specific IP + * configuration (devip) and skip other methods + * (manual, dhcp, etc). + */ + return FALSE; +} + /*****************************************************************************/ static void @@ -823,6 +833,7 @@ nm_device_modem_class_init(NMDeviceModemClass *klass) device_class->get_ip_iface_identifier = get_ip_iface_identifier; device_class->get_configured_mtu = nm_modem_get_configured_mtu; device_class->get_dhcp_timeout_for_device = get_dhcp_timeout_for_device; + device_class->ready_for_ip_config = ready_for_ip_config; device_class->state_changed = device_state_changed; From 36a97ad9ecf9e36ed176f0ee762263ac8635f120 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 21 Oct 2021 17:51:33 +0200 Subject: [PATCH 10/45] device: set up IPv6 before start manual method --- src/core/devices/nm-device.c | 85 +++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index d8f33cfb86..5a2d1c19ce 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -11443,14 +11443,13 @@ get_ip_method_auto(NMDevice *self, int addr_family) } static void -activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family) +activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family, const char *method) { const int IS_IPv4 = NM_IS_IPv4(addr_family); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); NMDeviceClass * klass = NM_DEVICE_GET_CLASS(self); NMConnection * connection; int ip_ifindex; - const char * method; if (nm_device_sys_iface_state_is_external(self)) goto out; @@ -11460,15 +11459,6 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family) ip_ifindex = nm_device_get_ip_ifindex(self); - if (ip_ifindex > 0 && !nm_platform_link_is_up(nm_device_get_platform(self), ip_ifindex) - && !nm_device_sys_iface_state_is_external(self)) { - nm_platform_link_change_flags(nm_device_get_platform(self), ip_ifindex, IFF_UP, TRUE); - if (!nm_platform_link_is_up(nm_device_get_platform(self), ip_ifindex)) - _LOGW(LOGD_DEVICE, - "interface %s not up for IP configuration", - nm_device_get_ip_iface(self)); - } - if (connection_ip_method_requires_carrier(connection, addr_family, NULL) && nm_device_is_master(self) && !priv->carrier) { if (!priv->ip_data_x[IS_IPv4].wait_for_carrier) { @@ -11499,20 +11489,9 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family) priv->ip_data_x[IS_IPv4].wait_for_ports = FALSE; } - method = nm_device_get_effective_ip_config_method(self, addr_family); - - if (nm_streq(method, - IS_IPv4 ? NM_SETTING_IP4_CONFIG_METHOD_AUTO : NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { - /* "auto" usually means DHCPv6 or autoconf6, but it doesn't have to be. Subclasses - * can overwrite it. For example, you cannot run DHCPv4 on PPP/WireGuard links. */ - method = klass->get_ip_method_auto(self, addr_family); - } - if (klass->ready_for_ip_config && !klass->ready_for_ip_config(self)) goto out_devip; - _dev_ipmanual_start(self); - if (IS_IPv4) { if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) _dev_ipdhcpx_start(self, AF_INET); @@ -11565,21 +11544,6 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family) } } } else { - /* Ensure the MTU makes sense. If it was below 1280 the kernel would not - * expose any ipv6 sysctls or allow presence of any addresses on the interface, - * including LL, which * would make it impossible to autoconfigure MTU to a - * correct value. */ - _commit_mtu(self); - - /* Any method past this point requires an IPv6LL address. Use NM-controlled - * IPv6LL if this is not an assumed connection, since assumed connections - * will already have IPv6 set up. - */ - _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_NONE); - - /* Re-enable IPv6 on the interface */ - nm_device_sysctl_ip_conf_set(self, AF_INET6, "accept_ra", "0"); - _dev_sysctl_set_disable_ipv6(self, FALSE); _dev_ipll6_start(self); if (NM_IN_STRSET(method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) @@ -11688,8 +11652,11 @@ fw_change_zone(NMDevice *self) static void activate_stage3_ip_config(NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMDeviceClass * klass = NM_DEVICE_GET_CLASS(self); int ifindex; + const char * ipv4_method; + const char * ipv6_method; /* stage3 is different from stage1+2. * @@ -11764,8 +11731,46 @@ activate_stage3_ip_config(NMDevice *self) nm_device_get_ip_iface(self)); } - activate_stage3_ip_config_for_addr_family(self, AF_INET); - activate_stage3_ip_config_for_addr_family(self, AF_INET6); + 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))) { + if (priv->ipmanual_data.state_6 == NM_DEVICE_IP_STATE_NONE + && !NM_IN_STRSET(ipv6_method, + NM_SETTING_IP6_CONFIG_METHOD_DISABLED, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { + /* Ensure the MTU makes sense. If it was below 1280 the kernel would not + * expose any ipv6 sysctls or allow presence of any addresses on the interface, + * including LL, which * would make it impossible to autoconfigure MTU to a + * correct value. */ + _commit_mtu(self); + + /* Any method past this point requires an IPv6LL address. Use NM-controlled + * IPv6LL if this is not an assumed connection, since assumed connections + * will already have IPv6 set up. + */ + _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_NONE); + + /* Re-enable IPv6 on the interface */ + nm_device_sysctl_ip_conf_set(self, AF_INET6, "accept_ra", "0"); + _dev_sysctl_set_disable_ipv6(self, FALSE); + } + + _dev_ipmanual_start(self); + } + + activate_stage3_ip_config_for_addr_family(self, AF_INET, ipv4_method); + activate_stage3_ip_config_for_addr_family(self, AF_INET6, ipv6_method); } void From a1de041184a4b95225e25514a5ba7b11df343912 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 21 Oct 2021 18:28:56 +0200 Subject: [PATCH 11/45] device: reschedule a assume check after platform changes --- src/core/devices/nm-device.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 5a2d1c19ce..179ccc4503 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -3889,6 +3889,7 @@ _dev_l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, N _dev_unamanged_check_external_down(self, TRUE); _dev_ipmanual_check_ready(self); update_external_connection(self); + nm_device_queue_recheck_assume(self); return; case _NM_L3_CONFIG_NOTIFY_TYPE_NUM: From c7ecaeb103a0232b413494b1f951d89885950b9e Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 25 Oct 2021 11:45:42 +0200 Subject: [PATCH 12/45] device: prefer non-LL and non-deprecated IPv6 address for hostname --- src/core/devices/nm-device.c | 82 ++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 179ccc4503..260e7f8eca 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -16565,6 +16565,60 @@ nm_device_clear_dns_lookup_data(NMDevice *self) nm_clear_pointer(&priv->hostname_resolver_x[i], _hostname_resolver_free); } +static GInetAddress * +get_address_for_hostname_dns_lookup(NMDevice *self, int addr_family) +{ + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMPLookup lookup; + const NMDedupMultiHeadEntry *head_entry; + const NMDedupMultiEntry * iter; + const guint8 * addr6_ll = NULL; + const guint8 * addr6_nonll = NULL; + int ifindex; + + ifindex = nm_device_get_ip_ifindex(self); + if (ifindex <= 0) + return NULL; + + /* FIXME(l3cfg): now we lookup the address from platform. Should we instead look + * it up from NML3Cfg? That is, take an address that we want to configure as + * opposed to an address that is configured? */ + head_entry = nm_platform_lookup( + nm_device_get_platform(self), + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex)); + + 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); + + if (IS_IPv4) { + return g_inet_address_new_from_bytes(addr->address_ptr, G_SOCKET_FAMILY_IPV4); + } + + /* For IPv6 prefer, in order: + * - !link-local, !deprecated + * - !link-local, deprecated + * - 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); + } + addr6_nonll = addr->address_ptr; + continue; + } + + addr6_ll = addr->address_ptr; + } + + if (addr6_nonll || addr6_ll) + return g_inet_address_new_from_bytes(addr6_nonll ?: addr6_ll, G_SOCKET_FAMILY_IPV6); + } + + return NULL; +} + /* return value is valid only immediately */ const char * nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean *out_wait) @@ -16573,12 +16627,10 @@ nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean NMDevicePrivate * priv; HostnameResolver *resolver; const char * method; - int ifindex; gboolean address_changed = FALSE; gs_unref_object GInetAddress *new_address = NULL; g_return_val_if_fail(NM_IS_DEVICE(self), NULL); - priv = NM_DEVICE_GET_PRIVATE(self); /* If the device is not supposed to have addresses, @@ -16618,31 +16670,7 @@ nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean /* Determine the most suitable address of the interface * and whether it changed from the previous lookup */ - ifindex = nm_device_get_ip_ifindex(self); - if (ifindex > 0) { - NMPLookup lookup; - const NMDedupMultiHeadEntry *head_entry; - const NMDedupMultiEntry * iter; - - /* FIXME(l3cfg): now we lookup the address from platform. Should we instead look - * it up from NML3Cfg? That is, take an address that we want to configure as - * opposed to an address that is configured? */ - head_entry = nm_platform_lookup( - nm_device_get_platform(self), - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex)); - - 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); - - new_address = g_inet_address_new_from_bytes(addr->address_ptr, - IS_IPv4 ? G_SOCKET_FAMILY_IPV4 - : G_SOCKET_FAMILY_IPV6); - break; - } - } - } - + new_address = get_address_for_hostname_dns_lookup(self, addr_family); if (new_address && resolver->address) { if (!g_inet_address_equal(new_address, resolver->address)) address_changed = TRUE; From 443c380f29f6dad860a7665eb7fa63f5a9be2f82 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 25 Oct 2021 11:47:26 +0200 Subject: [PATCH 13/45] core: update hostname when addresses on platform change --- src/core/devices/nm-device.c | 18 ++++++++++++++++++ src/core/devices/nm-device.h | 21 +++++++++++---------- src/core/nm-policy.c | 19 +++++++++++++++++++ 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 260e7f8eca..4435b550e0 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -324,6 +324,7 @@ enum { RECHECK_AUTO_ACTIVATE, RECHECK_ASSUME, DNS_LOOKUP_DONE, + PLATFORM_ADDRESS_CHANGED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = {0}; @@ -3887,6 +3888,13 @@ _dev_l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, N | nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP4_ADDRESS) | nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP6_ADDRESS))) _dev_unamanged_check_external_down(self, TRUE); + + if (NM_FLAGS_ANY(notify_data->platform_change_on_idle.obj_type_flags, + nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP4_ADDRESS) + | nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP6_ADDRESS))) { + g_signal_emit(self, signals[PLATFORM_ADDRESS_CHANGED], 0); + } + _dev_ipmanual_check_ready(self); update_external_connection(self); nm_device_queue_recheck_assume(self); @@ -17875,6 +17883,16 @@ nm_device_class_init(NMDeviceClass *klass) NULL, G_TYPE_NONE, 0); + + signals[PLATFORM_ADDRESS_CHANGED] = g_signal_new(NM_DEVICE_PLATFORM_ADDRESS_CHANGED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); } /* Connection defaults from plugins */ diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h index 9bd8aa7bee..93e9d51b14 100644 --- a/src/core/devices/nm-device.h +++ b/src/core/devices/nm-device.h @@ -71,16 +71,17 @@ #define NM_DEVICE_HAS_PENDING_ACTION "has-pending-action" /* Internal only */ /* Internal signals */ -#define NM_DEVICE_DNS_LOOKUP_DONE "dns-lookup-done" -#define NM_DEVICE_L3CD_CHANGED "l3cd-changed" -#define NM_DEVICE_IP6_PREFIX_DELEGATED "ip6-prefix-delegated" -#define NM_DEVICE_IP6_SUBNET_NEEDED "ip6-subnet-needed" -#define NM_DEVICE_REMOVED "removed" -#define NM_DEVICE_RECHECK_AUTO_ACTIVATE "recheck-auto-activate" -#define NM_DEVICE_RECHECK_ASSUME "recheck-assume" -#define NM_DEVICE_STATE_CHANGED "state-changed" -#define NM_DEVICE_LINK_INITIALIZED "link-initialized" -#define NM_DEVICE_AUTOCONNECT_ALLOWED "autoconnect-allowed" +#define NM_DEVICE_DNS_LOOKUP_DONE "dns-lookup-done" +#define NM_DEVICE_L3CD_CHANGED "l3cd-changed" +#define NM_DEVICE_IP6_PREFIX_DELEGATED "ip6-prefix-delegated" +#define NM_DEVICE_IP6_SUBNET_NEEDED "ip6-subnet-needed" +#define NM_DEVICE_REMOVED "removed" +#define NM_DEVICE_RECHECK_AUTO_ACTIVATE "recheck-auto-activate" +#define NM_DEVICE_RECHECK_ASSUME "recheck-assume" +#define NM_DEVICE_STATE_CHANGED "state-changed" +#define NM_DEVICE_LINK_INITIALIZED "link-initialized" +#define NM_DEVICE_AUTOCONNECT_ALLOWED "autoconnect-allowed" +#define NM_DEVICE_PLATFORM_ADDRESS_CHANGED "platform-address-changed" #define NM_DEVICE_STATISTICS_REFRESH_RATE_MS "refresh-rate-ms" #define NM_DEVICE_STATISTICS_TX_BYTES "tx-bytes" diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c index f089f1cb5e..2c3e80a325 100644 --- a/src/core/nm-policy.c +++ b/src/core/nm-policy.c @@ -2131,6 +2131,8 @@ device_l3cd_changed(NMDevice * device, update_ip_dns(self, AF_INET6, device); update_ip4_routing(self, TRUE); update_ip6_routing(self, TRUE); + /* FIXME: since we already monitor platform addresses changes, + * this is probably no longer necessary? */ update_system_hostname(self, "ip conf"); } else { nm_dns_manager_set_ip_config(priv->dns_manager, @@ -2144,6 +2146,19 @@ device_l3cd_changed(NMDevice * device, nm_dns_manager_end_updates(priv->dns_manager, __func__); } +static void +device_platform_address_changed(NMDevice *device, gpointer user_data) +{ + NMPolicyPrivate *priv = user_data; + NMPolicy * self = _PRIV_TO_SELF(priv); + NMDeviceState state; + + state = nm_device_get_state(device); + if (state > NM_DEVICE_STATE_DISCONNECTED && state < NM_DEVICE_STATE_DEACTIVATING) { + update_system_hostname(self, "address changed"); + } +} + /*****************************************************************************/ static void @@ -2180,6 +2195,10 @@ devices_list_register(NMPolicy *self, NMDevice *device) /* Connect state-changed with _after, so that the handler is invoked after other handlers. */ g_signal_connect_after(device, NM_DEVICE_STATE_CHANGED, G_CALLBACK(device_state_changed), priv); g_signal_connect(device, NM_DEVICE_L3CD_CHANGED, G_CALLBACK(device_l3cd_changed), priv); + g_signal_connect(device, + NM_DEVICE_PLATFORM_ADDRESS_CHANGED, + G_CALLBACK(device_platform_address_changed), + priv); g_signal_connect(device, NM_DEVICE_IP6_PREFIX_DELEGATED, G_CALLBACK(device_ip6_prefix_delegated), From b86388bef3758b46f9c4239230c57ee9b2787fd8 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 25 Oct 2021 15:58:25 +0200 Subject: [PATCH 14/45] core: avoid stale entries in the DNS manager When a virtual interface is removed externally, the device is unrealized and the ifindex is cleared; this also detaches the existing l3cfg from the device. At this point the l3cd entry for the device lingers forever in the DNS manager. Emit a last L3CD_CHANGED so that the old entry gets removed. Fixes-test: @disconnect_from_pppoe --- src/core/devices/nm-device.c | 12 ++++++++++++ src/core/nm-policy.c | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 4435b550e0..896b0dc63f 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -3980,6 +3980,18 @@ _set_ifindex(NMDevice *self, int ifindex, gboolean is_ip_ifindex) if (priv->l3cfg) { if (ip_ifindex_new <= 0 || ip_ifindex_new != nm_l3cfg_get_ifindex(priv->l3cfg)) { + const NML3ConfigData *l3cd_old; + + if (ip_ifindex_new <= 0) { + /* The ifindex was reset. Send a last L3CD_CHANGED + * signal with a NULL l3cd so that the old one can + * be removed from the DNS manager. + */ + l3cd_old = nm_l3cfg_get_combined_l3cd(priv->l3cfg, TRUE); + if (l3cd_old) + g_signal_emit(self, signals[L3CD_CHANGED], 0, l3cd_old, NULL); + } + g_signal_handlers_disconnect_by_func(priv->l3cfg, G_CALLBACK(_dev_l3_cfg_notify_cb), self); diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c index 2c3e80a325..329d14d167 100644 --- a/src/core/nm-policy.c +++ b/src/core/nm-policy.c @@ -2120,7 +2120,7 @@ device_l3cd_changed(NMDevice * device, * now. */ state = nm_device_get_state(device); - if (state > NM_DEVICE_STATE_DISCONNECTED && state < NM_DEVICE_STATE_DEACTIVATING) { + if (l3cd_new && state > NM_DEVICE_STATE_DISCONNECTED && state < NM_DEVICE_STATE_DEACTIVATING) { nm_dns_manager_set_ip_config(priv->dns_manager, AF_UNSPEC, device, From 98da62642b6461c73ae34b0005660f4cdd594110 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 26 Oct 2021 17:17:03 +0200 Subject: [PATCH 15/45] device: enable IPv6 in ip6_managed_setup() IPv6 must be enabled when the device enters the DISCONNECTED state. --- src/core/devices/nm-device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 896b0dc63f..d721bee74d 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -15082,7 +15082,7 @@ static void ip6_managed_setup(NMDevice *self) { _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_NONE); - _dev_sysctl_set_disable_ipv6(self, TRUE); + _dev_sysctl_set_disable_ipv6(self, FALSE); nm_device_sysctl_ip_conf_set(self, AF_INET6, "accept_ra", "0"); nm_device_sysctl_ip_conf_set(self, AF_INET6, "use_tempaddr", "0"); nm_device_sysctl_ip_conf_set(self, AF_INET6, "forwarding", "0"); From 8fb8e334b89124477fe14b4bd0df78c3a7b0017f Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 27 Oct 2021 09:49:28 +0200 Subject: [PATCH 16/45] device: wait that IPv6 autoconf addresses become non-tentative Fixes-test: @ipv6_preserve_cached_routes --- src/core/devices/nm-device.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index d721bee74d..1f27e4f328 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -627,11 +627,12 @@ typedef struct _NMDevicePrivate { }; struct { - NMNDisc * ndisc; - GSource * ndisc_grace_source; - gulong ndisc_changed_id; - gulong ndisc_timeout_id; - NMDeviceIPState state; + NMNDisc * ndisc; + GSource * ndisc_grace_source; + gulong ndisc_changed_id; + gulong ndisc_timeout_id; + NMDeviceIPState state; + const NML3ConfigData *l3cd; } ipac6_data; union { @@ -823,6 +824,8 @@ _dev_ipac6_grace_period_start(NMDevice *self, guint32 timeout_sec, gboolean forc static void _dev_ipac6_start(NMDevice *self); +static void _dev_ipac6_set_state(NMDevice *self, NMDeviceIPState state); + static void _dev_unamanged_check_external_down(NMDevice *self, gboolean only_if_unmanaged); static void _dev_ipshared4_start(NMDevice *self); @@ -3895,6 +3898,20 @@ _dev_l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, N g_signal_emit(self, signals[PLATFORM_ADDRESS_CHANGED], 0); } + /* 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)) { + 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); + } + _dev_ipmanual_check_ready(self); update_external_connection(self); nm_device_queue_recheck_assume(self); @@ -11081,17 +11098,23 @@ _dev_ipac6_ndisc_config_changed(NMNDisc * ndisc, const NML3ConfigData *l3cd, NMDevice * self) { + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + _dev_ipac6_grace_period_start(self, 0, TRUE); _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_AC_6, l3cd, FALSE); - _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_READY); + nm_l3_config_data_unref(priv->ipac6_data.l3cd); + priv->ipac6_data.l3cd = nm_l3_config_data_ref(l3cd); _dev_ipdhcp6_set_dhcp_level(self, rdata->dhcp_level); _dev_l3_cfg_commit(self, FALSE); _dev_ip_state_check_async(self, AF_INET6); + + /* wait that addresses are committed to platform and + * become non-tentative before declaring AC6 is ready.*/ } static void @@ -11307,6 +11330,7 @@ _dev_ipac6_cleanup(NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); nm_clear_g_source_inst(&priv->ipac6_data.ndisc_grace_source); + nm_clear_l3cd(&priv->ipac6_data.l3cd); nm_clear_g_signal_handler(priv->ipac6_data.ndisc, &priv->ipac6_data.ndisc_changed_id); nm_clear_g_signal_handler(priv->ipac6_data.ndisc, &priv->ipac6_data.ndisc_timeout_id); From d8d6ecf37a079e3e34c8960946b9a613896e245d Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 27 Oct 2021 10:19:47 +0200 Subject: [PATCH 17/45] l3cfg: remove event down source on finalize --- src/core/nm-l3cfg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index 72d3bbc7b5..02456d5b95 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -1570,6 +1570,7 @@ _l3_acd_nacd_instance_reset(NML3Cfg *self, NMTernary start_timer, gboolean acd_d } nm_clear_g_source_inst(&self->priv.p->nacd_source); nm_clear_g_source_inst(&self->priv.p->nacd_instance_ensure_retry); + nm_clear_g_source_inst(&self->priv.p->nacd_event_down_source); if (c_list_is_empty(&self->priv.p->acd_lst_head)) start_timer = NM_TERNARY_DEFAULT; @@ -4472,6 +4473,7 @@ finalize(GObject *object) nm_clear_pointer(&self->priv.p->nacd, n_acd_unref); nm_clear_g_source_inst(&self->priv.p->nacd_source); nm_clear_g_source_inst(&self->priv.p->nacd_instance_ensure_retry); + nm_clear_g_source_inst(&self->priv.p->nacd_event_down_source); nm_clear_g_source_inst(&self->priv.p->obj_state_temporary_not_available_timeout_source); From 96850933bbbacb3a07d875a80bad13f79068a7d6 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 27 Oct 2021 11:09:52 +0200 Subject: [PATCH 18/45] core: ignore external routes when generating connections --- src/core/NetworkManagerUtils.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/NetworkManagerUtils.c b/src/core/NetworkManagerUtils.c index fdb2b40443..e057f5534b 100644 --- a/src/core/NetworkManagerUtils.c +++ b/src/core/NetworkManagerUtils.c @@ -28,6 +28,7 @@ #include "libnm-platform/nm-platform.h" #include "libnm-platform/nm-linux-platform.h" +#include "libnm-platform/nm-platform-utils.h" #include "nm-auth-utils.h" #include "libnm-systemd-shared/nm-sd-utils-shared.h" @@ -1750,6 +1751,12 @@ nm_utils_platform_capture_ip_setting(NMPlatform *platform, continue; } + if (route->rx.rt_source + != nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER)) { + /* Ignore routes provided by external sources */ + continue; + } + s_route = nm_ip_route_new_binary(addr_family, route->rx.network_ptr, route->rx.plen, From a2d96a5c2eba93061747b2aa57dd93e0f88321bc Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 27 Oct 2021 13:38:44 +0200 Subject: [PATCH 19/45] vlan: preserve external IP changes on parent MAC update When the parent MAC is updated, a VLAN device also updates its MAC and then performs a new commit to restore IP configuration. This should not remove addresses added externally. Change the commit type to UPDATE. Fixes-test: @static_route_persists_mac_change --- src/core/devices/nm-device-vlan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/devices/nm-device-vlan.c b/src/core/devices/nm-device-vlan.c index c1902d5eea..5709ad7b18 100644 --- a/src/core/devices/nm-device-vlan.c +++ b/src/core/devices/nm-device-vlan.c @@ -115,7 +115,7 @@ parent_hwaddr_maybe_changed(NMDevice *parent, GParamSpec *pspec, gpointer user_d /* When changing the hw address the interface is taken down, * removing the IPv6 configuration; reapply it. */ - nm_device_l3cfg_commit(device, NM_L3_CFG_COMMIT_TYPE_REAPPLY, FALSE); + nm_device_l3cfg_commit(device, NM_L3_CFG_COMMIT_TYPE_UPDATE, FALSE); } } From 9380d3c71578a04bb01042231df68492c0eb8684 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 28 Oct 2021 10:52:47 +0200 Subject: [PATCH 20/45] device: fix waiting for IPv6 autoconf addresses When NM is restarted and the previous connection gets assumed, addresses are already configured; in such case we don't need to wait. --- src/core/devices/nm-device.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 1f27e4f328..ac83a1749a 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -11098,23 +11098,43 @@ _dev_ipac6_ndisc_config_changed(NMNDisc * ndisc, const NML3ConfigData *l3cd, NMDevice * self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gboolean ready = TRUE; + NMDedupMultiIter iter; + const NMPObject *obj; _dev_ipac6_grace_period_start(self, 0, TRUE); _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_AC_6, l3cd, FALSE); - nm_l3_config_data_unref(priv->ipac6_data.l3cd); - priv->ipac6_data.l3cd = nm_l3_config_data_ref(l3cd); + 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; + } + } + + if (ready) { + _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_READY); + } else { + priv->ipac6_data.l3cd = nm_l3_config_data_ref(l3cd); + } _dev_ipdhcp6_set_dhcp_level(self, rdata->dhcp_level); _dev_l3_cfg_commit(self, FALSE); _dev_ip_state_check_async(self, AF_INET6); - - /* wait that addresses are committed to platform and - * become non-tentative before declaring AC6 is ready.*/ } static void From c4238620d3e61c38e1a877af9ec77e5a61295966 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 29 Oct 2021 09:51:25 +0200 Subject: [PATCH 21/45] glib-aux: accept NULL addresses in nm_ip_addr_equal() nm_ether_addr_equal() already accepts NULL addresses and makes a distinction between NULL and a zero address. Introduce a similar behavior for nm_ip_addr_equal(), as it's useful in some cases. --- src/libnm-glib-aux/nm-shared-utils.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index 8dd53dcf29..e6e470b4cc 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -248,10 +248,9 @@ extern const NMIPAddr nm_ip_addr_zero; static inline int nm_ip_addr_cmp(int addr_family, gconstpointer a, gconstpointer b) { - nm_assert(a); - nm_assert(b); - - return memcmp(a, b, nm_utils_addr_family_to_size(addr_family)); + NM_CMP_SELF(a, b); + NM_CMP_DIRECT_MEMCMP(a, b, nm_utils_addr_family_to_size(addr_family)); + return 0; } static inline gboolean From 8f8e912e3b9e3c9df4fa4118a212b33efc36e401 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 29 Oct 2021 09:54:13 +0200 Subject: [PATCH 22/45] core: also export 0.0.0.0 as gateway in IP configuration The IP configuration exported over D-Bus for a device has a "gateway" property. If the device has a default route without a gateway, before the L3 rework the gateway used to be exported as "0.0.0.0". That seems useful to indicate that the device has a default route, but without a next hop. Restore that behavior. Fixes-test: @preserve_route_to_generic_device --- src/core/nm-ip-config.c | 24 +++++++++++++++--------- src/core/nm-ip-config.h | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/core/nm-ip-config.c b/src/core/nm-ip-config.c index 196cf35a98..4bbe1e1a4a 100644 --- a/src/core/nm-ip-config.c +++ b/src/core/nm-ip-config.c @@ -100,12 +100,14 @@ get_property_ip(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec g_value_set_variant(value, priv->v_address_data); break; case PROP_IP_GATEWAY: - g_value_set_variant( - value, - nm_ip_addr_is_null(addr_family, &priv->v_gateway.addr) - ? nm_g_variant_singleton_s_empty() - : g_variant_new_string( - nm_utils_inet_ntop(addr_family, &priv->v_gateway.addr, sbuf_addr))); + if (priv->v_gateway.addr) { + g_value_set_variant( + value, + g_variant_new_string( + nm_utils_inet_ntop(addr_family, priv->v_gateway.addr, sbuf_addr))); + } else { + g_value_set_variant(value, nm_g_variant_singleton_s_empty()); + } break; case PROP_IP_ROUTE_DATA: g_value_set_variant(value, priv->v_route_data); @@ -210,6 +212,7 @@ finalize(GObject *object) nm_g_variant_unref(priv->v_route_data); nm_g_variant_unref(priv->v_routes); + g_free(priv->v_gateway.addr); nmp_object_unref(priv->v_gateway.best_default_route); nm_l3_config_data_unref(priv->l3cd); @@ -767,9 +770,12 @@ _handle_platform_change(NMIPConfig *self, guint32 obj_type_flags, gboolean is_in ? nm_platform_ip_route_get_gateway( addr_family, NMP_OBJECT_CAST_IP_ROUTE(priv->v_gateway.best_default_route)) - : &nm_ip_addr_zero; - if (!nm_ip_addr_equal(addr_family, &priv->v_gateway.addr, gateway_next_hop)) { - nm_ip_addr_set(addr_family, &priv->v_gateway.addr, gateway_next_hop); + : NULL; + + if (!nm_ip_addr_equal(addr_family, priv->v_gateway.addr, gateway_next_hop)) { + g_free(priv->v_gateway.addr); + priv->v_gateway.addr = + g_memdup(gateway_next_hop, nm_utils_addr_family_to_size(addr_family)); best_default_route_changed = TRUE; } } diff --git a/src/core/nm-ip-config.h b/src/core/nm-ip-config.h index e3c3b30a96..cfe964d0ee 100644 --- a/src/core/nm-ip-config.h +++ b/src/core/nm-ip-config.h @@ -31,7 +31,7 @@ struct _NMIPConfigPrivate { GVariant * v_routes; struct { const NMPObject *best_default_route; - NMIPAddr addr; + NMIPAddr * addr; } v_gateway; gulong l3cfg_notify_id; }; From cc3ec405131d6371bbbcead81669b65e1c5adfc0 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Sat, 30 Oct 2021 11:33:51 +0200 Subject: [PATCH 23/45] device: fail when a addr family fails and the other is disabled Fixes-test: @ipv4_method_shared_with_already_running_dnsmasq --- src/core/devices/nm-device.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index ac83a1749a..b70df02db1 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -3071,8 +3071,15 @@ _dev_ip_state_check(NMDevice *self, int addr_family) NMDeviceIPState ip_state; NMDeviceIPState ip_state_other; NMDeviceIPState combinedip_state; - NMTernary may_fail = NM_TERNARY_DEFAULT; - NMTernary may_fail_other = NM_TERNARY_DEFAULT; + NMTernary may_fail = NM_TERNARY_DEFAULT; + NMTernary may_fail_other = NM_TERNARY_DEFAULT; + gboolean disabled_or_ignore = FALSE; + gboolean disabled_or_ignore_other = FALSE; + + if (priv->ip_data_x[IS_IPv4].is_disabled || priv->ip_data_x[IS_IPv4].is_ignore) + disabled_or_ignore = TRUE; + if (priv->ip_data_x[!IS_IPv4].is_disabled || priv->ip_data_x[!IS_IPv4].is_ignore) + disabled_or_ignore_other = TRUE; /* State handling in NMDevice: * @@ -3142,7 +3149,7 @@ _dev_ip_state_check(NMDevice *self, int addr_family) goto got_ip_state; } - if (priv->ip_data_x[IS_IPv4].is_disabled || priv->ip_data_x[IS_IPv4].is_ignore) { + if (disabled_or_ignore) { ip_state = NM_DEVICE_IP_STATE_READY; goto got_ip_state; } @@ -3270,7 +3277,7 @@ got_ip_state: if (ip_state == NM_DEVICE_IP_STATE_READY && ip_state_other == NM_DEVICE_IP_STATE_READY) combinedip_state = NM_DEVICE_IP_STATE_READY; else if (ip_state == NM_DEVICE_IP_STATE_READY && ip_state_other == NM_DEVICE_IP_STATE_PENDING - && (priv->ip_data_x[IS_IPv4].is_disabled || priv->ip_data_x[IS_IPv4].is_ignore)) { + && disabled_or_ignore) { /* This IP method is disabled/ignore, but the other family is still pending. * Regardless of ipvx.may-fail, this means that we always require the other IP family * to get ready too. */ @@ -3293,10 +3300,20 @@ got_ip_state: else if (ip_state == NM_DEVICE_IP_STATE_FAILED && !_prop_get_ipvx_may_fail_cached(self, addr_family, &may_fail)) combinedip_state = NM_DEVICE_IP_STATE_FAILED; - else if (ip_state == NM_DEVICE_IP_STATE_FAILED && ip_state_other == NM_DEVICE_IP_STATE_FAILED) { - /* If both IP states fail, then it's a failure for good. may-fail does not mean that - * both families may fail, instead it means that at least one family must succeed. */ - combinedip_state = NM_DEVICE_IP_STATE_FAILED; + else if ((ip_state == NM_DEVICE_IP_STATE_FAILED + || (ip_state == NM_DEVICE_IP_STATE_READY && disabled_or_ignore)) + && (ip_state_other == NM_DEVICE_IP_STATE_FAILED + || (ip_state_other = NM_DEVICE_IP_STATE_READY && disabled_or_ignore_other))) { + /* If both IP states failed, or one failed and the other is disabled + * then it's a failure. may-fail does not mean that both families may + * fail, instead it means that at least one family must succeed. */ + if (nm_device_sys_iface_state_is_external_or_assume(self)) { + _dev_ip_state_set_state(self, AF_INET, NM_DEVICE_IP_STATE_READY, "assumed"); + _dev_ip_state_set_state(self, AF_INET6, NM_DEVICE_IP_STATE_READY, "assumed"); + combinedip_state = NM_DEVICE_IP_STATE_READY; + } else { + combinedip_state = NM_DEVICE_IP_STATE_FAILED; + } } else { if (priv->ip_data.state == NM_DEVICE_IP_STATE_NONE) combinedip_state = NM_DEVICE_IP_STATE_PENDING; From bcf31a9b299b153b1cc5fb48f57c342edad1708d Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Sat, 30 Oct 2021 15:15:04 +0200 Subject: [PATCH 24/45] device: fix assertion failure on master carrier change Fix the following assertion failure: "nm_assert(NM_DEVICE_GET_PRIVATE(self)->act_request.obj);" 0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50 1 __GI_abort () at abort.c:79 2 g_assertion_message () at gtestutils.c:2533 3 g_assertion_message_expr () at gtestutils.c:2556 4 activation_source_invoke_or_schedule (self=0x55b543884610 [NMDeviceBridge], func=0x55b542e8c940 , do_sync=0) at src/core/devices/nm-device.c:8854 5 nm_device_set_carrier (self=0x55b543884610 [NMDeviceBridge], carrier=1) at src/core/devices/nm-device.c:6217 6 device_link_changed (user_data=user_data@entry=0x55b543884610) at src/core/devices/nm-device.c:6479 7 g_idle_dispatch (source=0x55b5438577d0, callback=0x55b542e95980 , user_data=0x55b543884610) at gmain.c:5579 8 g_main_dispatch (context=0x55b543719bc0) at gmain.c:3193 9 g_main_context_dispatch (context=context@entry=0x55b543719bc0) at gmain.c:3873 10 g_main_context_iterate (context=0x55b543719bc0, block=block@entry=1, dispatch=dispatch@entry=1, self=) at gmain.c:3946 --- src/core/devices/nm-device.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index b70df02db1..f96efc6504 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -6122,7 +6122,8 @@ carrier_changed(NMDevice *self, gboolean carrier) nm_device_update_dynamic_ip_setup(self); /* If needed, also resume IP configuration that is * waiting for carrier. */ - nm_device_activate_schedule_stage3_ip_config(self, FALSE); + if (priv->state == NM_DEVICE_STATE_IP_CONFIG) + nm_device_activate_schedule_stage3_ip_config(self, FALSE); return; } /* fall-through and change state of device */ From 7faeda83517341121b60ade2362767d62104c420 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sat, 30 Oct 2021 09:44:32 +0200 Subject: [PATCH 25/45] platform: accept %NULL route as parameter to nm_platform_ip_route_get_gateway() It's sometimes convenient to accept %NULL and have a "maybe type" like behavior to propagate the %NULL input of the getter. --- src/libnm-platform/nm-platform.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index d3d8ae3070..4667125f24 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -2176,7 +2176,9 @@ static inline gconstpointer nm_platform_ip_route_get_gateway(int addr_family, const NMPlatformIPRoute *route) { nm_assert_addr_family(addr_family); - nm_assert(route); + + if (!route) + return NULL; if (NM_IS_IPv4(addr_family)) return &((NMPlatformIP4Route *) route)->gateway; From 682d2c842adaa7e27bc20bf028c81ca5a6842c23 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sat, 30 Oct 2021 11:56:18 +0200 Subject: [PATCH 26/45] core: drop redundant "gateway" variable for NMIPConfig The "gateway" variable was redundant to best_default_route. It was somewhat convenient to have, because the best_default_route pointer might change, but the next hop might still be the same. So it was easy to track whether it changes. But it's also redundant information. By relying on best_default_route alone we have all the information we need. --- src/core/nm-ip-config.c | 38 ++++++++++++++++++-------------------- src/core/nm-ip-config.h | 1 - 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/core/nm-ip-config.c b/src/core/nm-ip-config.c index 4bbe1e1a4a..b3e599400e 100644 --- a/src/core/nm-ip-config.c +++ b/src/core/nm-ip-config.c @@ -100,14 +100,17 @@ get_property_ip(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec g_value_set_variant(value, priv->v_address_data); break; case PROP_IP_GATEWAY: - if (priv->v_gateway.addr) { + if (priv->v_gateway.best_default_route) { + const NMIPAddr *gateway; + + gateway = nm_platform_ip_route_get_gateway( + addr_family, + 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, priv->v_gateway.addr, sbuf_addr))); - } else { + g_variant_new_string(nm_utils_inet_ntop(addr_family, gateway, sbuf_addr))); + } else g_value_set_variant(value, nm_g_variant_singleton_s_empty()); - } break; case PROP_IP_ROUTE_DATA: g_value_set_variant(value, priv->v_route_data); @@ -212,7 +215,6 @@ finalize(GObject *object) nm_g_variant_unref(priv->v_route_data); nm_g_variant_unref(priv->v_routes); - g_free(priv->v_gateway.addr); nmp_object_unref(priv->v_gateway.best_default_route); nm_l3_config_data_unref(priv->l3cd); @@ -763,21 +765,17 @@ _handle_platform_change(NMIPConfig *self, guint32 obj_type_flags, gboolean is_in } } - if (nmp_object_ref_set(&priv->v_gateway.best_default_route, best_default_route)) { - gconstpointer gateway_next_hop; - - gateway_next_hop = priv->v_gateway.best_default_route - ? nm_platform_ip_route_get_gateway( - addr_family, - NMP_OBJECT_CAST_IP_ROUTE(priv->v_gateway.best_default_route)) - : NULL; - - if (!nm_ip_addr_equal(addr_family, priv->v_gateway.addr, gateway_next_hop)) { - g_free(priv->v_gateway.addr); - priv->v_gateway.addr = - g_memdup(gateway_next_hop, nm_utils_addr_family_to_size(addr_family)); + if (priv->v_gateway.best_default_route != best_default_route) { + if (!nm_ip_addr_equal( + addr_family, + nm_platform_ip_route_get_gateway( + addr_family, + NMP_OBJECT_CAST_IP_ROUTE(priv->v_gateway.best_default_route)), + nm_platform_ip_route_get_gateway(addr_family, + NMP_OBJECT_CAST_IP_ROUTE(best_default_route)))) best_default_route_changed = TRUE; - } + + nmp_object_ref_set(&priv->v_gateway.best_default_route, best_default_route); } } diff --git a/src/core/nm-ip-config.h b/src/core/nm-ip-config.h index cfe964d0ee..5b2552e3a3 100644 --- a/src/core/nm-ip-config.h +++ b/src/core/nm-ip-config.h @@ -31,7 +31,6 @@ struct _NMIPConfigPrivate { GVariant * v_routes; struct { const NMPObject *best_default_route; - NMIPAddr * addr; } v_gateway; gulong l3cfg_notify_id; }; From d2fc0984a1a6a06531a956a35daf8f25e3a41235 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Sat, 30 Oct 2021 16:41:30 +0200 Subject: [PATCH 27/45] l3cd: add ipv6 token --- src/core/nm-l3-config-data.c | 30 ++++++++++++++++++++++++++++++ src/core/nm-l3-config-data.h | 5 +++++ 2 files changed, 35 insertions(+) diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c index da933d5d04..02f9142a3c 100644 --- a/src/core/nm-l3-config-data.c +++ b/src/core/nm-l3-config-data.c @@ -120,6 +120,7 @@ struct _NML3ConfigData { NMSettingConnectionMdns mdns; NMSettingConnectionLlmnr llmnr; NMSettingConnectionDnsOverTls dns_over_tls; + NMUtilsIPv6IfaceId ip6_token; NML3ConfigDatFlags flags; @@ -581,6 +582,11 @@ nm_l3_config_data_log(const NML3ConfigData *self, NULL))); } + if (self->ip6_token.id != 0) { + _L("ipv6-token: %s", + nm_utils_inet6_interface_identifier_to_token(&self->ip6_token, sbuf_addr)); + } + if (self->metered != NM_TERNARY_DEFAULT) _L("metered: %s", self->metered ? "yes" : "no"); @@ -1849,6 +1855,26 @@ nm_l3_config_data_set_ip6_privacy(NML3ConfigData *self, NMSettingIP6ConfigPrivac return TRUE; } +NMUtilsIPv6IfaceId +nm_l3_config_data_get_ip6_token(const NML3ConfigData *self) +{ + nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE)); + + return self->ip6_token; +} + +gboolean +nm_l3_config_data_set_ip6_token(NML3ConfigData *self, NMUtilsIPv6IfaceId ipv6_token) +{ + nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE)); + + if (self->ip6_token.id == ipv6_token.id) + return FALSE; + + self->ip6_token.id = ipv6_token.id; + return TRUE; +} + NMProxyConfigMethod nm_l3_config_data_get_proxy_method(const NML3ConfigData *self) { @@ -2158,6 +2184,7 @@ nm_l3_config_data_cmp_full(const NML3ConfigData *a, NM_CMP_DIRECT(a->mdns, b->mdns); NM_CMP_DIRECT(a->llmnr, b->llmnr); NM_CMP_DIRECT(a->dns_over_tls, b->dns_over_tls); + NM_CMP_DIRECT(a->ip6_token.id, b->ip6_token.id); NM_CMP_DIRECT(a->mtu, b->mtu); NM_CMP_DIRECT(a->ip6_mtu, b->ip6_mtu); NM_CMP_DIRECT_UNSAFE(a->metered, b->metered); @@ -3056,6 +3083,9 @@ nm_l3_config_data_merge(NML3ConfigData * self, if (self->dns_over_tls == NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT) self->dns_over_tls = src->dns_over_tls; + if (self->ip6_token.id == 0) + self->ip6_token.id = src->ip6_token.id; + self->metered = NM_MAX((NMTernary) self->metered, (NMTernary) src->metered); if (self->proxy_method == NM_PROXY_CONFIG_METHOD_UNKNOWN) diff --git a/src/core/nm-l3-config-data.h b/src/core/nm-l3-config-data.h index 2f9cfbbab8..1b3fa3cc16 100644 --- a/src/core/nm-l3-config-data.h +++ b/src/core/nm-l3-config-data.h @@ -451,6 +451,7 @@ NMSettingConnectionDnsOverTls nm_l3_config_data_get_dns_over_tls(const NML3Confi gboolean nm_l3_config_data_set_dns_over_tls(NML3ConfigData * self, NMSettingConnectionDnsOverTls dns_over_tls); + NMIPRouteTableSyncMode nm_l3_config_data_get_route_table_sync(const NML3ConfigData *self, int addr_family); @@ -475,6 +476,10 @@ guint32 nm_l3_config_data_get_ip6_mtu(const NML3ConfigData *self); gboolean nm_l3_config_data_set_ip6_mtu(NML3ConfigData *self, guint32 ip6_mtu); +NMUtilsIPv6IfaceId nm_l3_config_data_get_ip6_token(const NML3ConfigData *self); + +gboolean nm_l3_config_data_set_ip6_token(NML3ConfigData *self, NMUtilsIPv6IfaceId ipv6_token); + const in_addr_t *nm_l3_config_data_get_wins(const NML3ConfigData *self, guint *out_len); gboolean nm_l3_config_data_add_wins(NML3ConfigData *self, in_addr_t wins); From 70676b3d0a65901b3a07ea0d2926c888dafd0242 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Sun, 31 Oct 2021 15:10:53 +0100 Subject: [PATCH 28/45] l3cfg: set ipv6 token Fixes-test: @ipv6_token --- src/core/devices/nm-device.c | 67 ++++++------------------- src/core/ndisc/nm-ndisc.c | 23 ++++++--- src/core/ndisc/nm-ndisc.h | 5 +- src/core/ndisc/tests/test-ndisc-fake.c | 2 +- src/core/ndisc/tests/test-ndisc-linux.c | 2 +- src/core/nm-l3-config-data.h | 1 - src/core/nm-l3cfg.c | 63 +++++++++++++++++++++++ 7 files changed, 98 insertions(+), 65 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index f96efc6504..b8495c0f0c 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -2839,50 +2839,6 @@ nm_device_sysctl_ip_conf_get_int_checked(NMDevice * self, fallback); } -static void -set_ipv6_token(NMDevice *self, const NMUtilsIPv6IfaceId *iid, const char *token_str) -{ - NMPlatform * platform; - int ifindex; - const NMPlatformLink *link; - char buf[32]; - gint64 val; - - /* Setting the kernel token is not strictly necessary as the - * IPv6 address is generated in userspace. However it is - * convenient so that users can see the token with iproute - * ('ip token'). */ - platform = nm_device_get_platform(self); - ifindex = nm_device_get_ip_ifindex(self); - link = nm_platform_link_get(platform, ifindex); - - if (link && link->inet6_token.id == iid->id) { - _LOGT(LOGD_DEVICE | LOGD_IP6, "token %s already set", token_str); - return; - } - - /* The kernel allows setting a token only when 'accept_ra' - * is 1: temporarily flip it if necessary; unfortunately - * this will also generate an additional Router Solicitation - * from kernel. */ - val = nm_device_sysctl_ip_conf_get_int_checked(self, - AF_INET6, - "accept_ra", - 10, - G_MININT32, - G_MAXINT32, - 1); - if (val != 1) - nm_device_sysctl_ip_conf_set(self, AF_INET6, "accept_ra", "1"); - - nm_platform_link_set_ipv6_token(platform, ifindex, iid); - - if (val != 1) { - nm_sprintf_buf(buf, "%d", (int) val); - nm_device_sysctl_ip_conf_set(self, AF_INET6, "accept_ra", buf); - } -} - gboolean nm_device_sysctl_ip_conf_set(NMDevice * self, int addr_family, @@ -4672,6 +4628,7 @@ get_ip_iface_identifier(NMDevice *self, NMUtilsIPv6IfaceId *out_iid) * @self: an #NMDevice * @iid: where to place the interface identifier * @ignore_token: force creation of a non-tokenized address + * @out_is_token: on return, whether the identifier is tokenized * * Return the interface's identifier for the EUI64 address generation mode. * It's either a manually set token or and identifier generated in a @@ -4683,19 +4640,26 @@ get_ip_iface_identifier(NMDevice *self, NMUtilsIPv6IfaceId *out_iid) * Returns: #TRUE if the @iid could be set */ static gboolean -nm_device_get_ip_iface_identifier(NMDevice *self, NMUtilsIPv6IfaceId *iid, gboolean ignore_token) +nm_device_get_ip_iface_identifier(NMDevice * self, + NMUtilsIPv6IfaceId *iid, + gboolean ignore_token, + gboolean * out_is_token) { NMSettingIP6Config *s_ip6; const char * token = NULL; g_return_val_if_fail(NM_IS_DEVICE(self), FALSE); + NM_SET_OUT(out_is_token, FALSE); + if (!ignore_token) { s_ip6 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP6_CONFIG); g_return_val_if_fail(s_ip6, FALSE); token = nm_setting_ip6_config_get_token(s_ip6); + if (token) + NM_SET_OUT(out_is_token, TRUE); } if (token) return nm_utils_ipv6_interface_identifier_get_from_token(iid, token); @@ -6470,7 +6434,7 @@ device_link_changed(gpointer user_data) } if (priv->ipac6_data.ndisc && pllink->inet6_token.id) { - if (nm_ndisc_set_iid(priv->ipac6_data.ndisc, pllink->inet6_token)) + if (nm_ndisc_set_iid(priv->ipac6_data.ndisc, pllink->inet6_token, TRUE)) _LOGD(LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); } @@ -10631,7 +10595,7 @@ _dev_ipll6_start(NMDevice *self) } else { NMUtilsIPv6IfaceId iid; - if (!nm_device_get_ip_iface_identifier(self, &iid, TRUE)) { + if (!nm_device_get_ip_iface_identifier(self, &iid, TRUE, NULL)) { _LOGW(LOGD_IP6, "linklocal6: failed to get interface identifier; IPv6 cannot continue"); goto out_fail; } @@ -11241,6 +11205,7 @@ _dev_ipac6_start(NMDevice *self) guint32 ra_timeout; guint32 default_ra_timeout; NMUtilsIPv6IfaceId iid; + gboolean is_token; if (priv->ipac6_data.state == NM_DEVICE_IP_STATE_NONE) { if (!g_file_test("/proc/sys/net/ipv6", G_FILE_TEST_IS_DIR)) { @@ -11322,9 +11287,9 @@ _dev_ipac6_start(NMDevice *self) self); } - if (nm_device_get_ip_iface_identifier(self, &iid, FALSE)) { + if (nm_device_get_ip_iface_identifier(self, &iid, FALSE, &is_token)) { _LOGD_ipac6("using the device EUI-64 identifier"); - nm_ndisc_set_iid(priv->ipac6_data.ndisc, iid); + nm_ndisc_set_iid(priv->ipac6_data.ndisc, iid, is_token); } else { /* Don't abort the addrconf at this point -- if ndisc needs the iid * it will notice this itself. */ @@ -15040,12 +15005,10 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu /* Take out any entries in the routing table and any IP address the device had. */ if (ifindex > 0) { - NMPlatform * platform = nm_device_get_platform(self); - NMUtilsIPv6IfaceId iid = {}; + NMPlatform *platform = nm_device_get_platform(self); nm_platform_ip_route_flush(platform, AF_UNSPEC, ifindex); nm_platform_ip_address_flush(platform, AF_UNSPEC, ifindex); - set_ipv6_token(self, &iid, "::"); if (nm_device_get_applied_setting(self, NM_TYPE_SETTING_TC_CONFIG)) { nm_platform_tc_sync(platform, ifindex, NULL, NULL); diff --git a/src/core/ndisc/nm-ndisc.c b/src/core/ndisc/nm-ndisc.c index d870f5f119..670b0f39ce 100644 --- a/src/core/ndisc/nm-ndisc.c +++ b/src/core/ndisc/nm-ndisc.c @@ -57,6 +57,7 @@ struct _NMNDiscPrivate { GSource *timeout_expire_source; NMUtilsIPv6IfaceId iid; + gboolean iid_is_token; /* immutable values from here on: */ @@ -105,7 +106,8 @@ NML3ConfigData * nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, int ifindex, const NMNDiscData * rdata, - NMSettingIP6ConfigPrivacy ip6_privacy) + NMSettingIP6ConfigPrivacy ip6_privacy, + NMUtilsIPv6IfaceId * token) { nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; guint32 ifa_flags; @@ -195,6 +197,8 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, nm_l3_config_data_set_ndisc_retrans_timer_msec(l3cd, rdata->retrans_timer_ms); nm_l3_config_data_set_ip6_mtu(l3cd, rdata->mtu); + if (token) + nm_l3_config_data_set_ip6_token(l3cd, *token); return g_steal_pointer(&l3cd); } @@ -375,10 +379,12 @@ nm_ndisc_emit_config_change(NMNDisc *self, NMNDiscConfigMap changed) rdata = _data_complete(&NM_NDISC_GET_PRIVATE(self)->rdata), - l3cd = nm_l3_config_data_seal(nm_ndisc_data_to_l3cd(nm_l3cfg_get_multi_idx(priv->config.l3cfg), - nm_l3cfg_get_ifindex(priv->config.l3cfg), - rdata, - priv->config.ip6_privacy)); + l3cd = nm_ndisc_data_to_l3cd(nm_l3cfg_get_multi_idx(priv->config.l3cfg), + nm_l3cfg_get_ifindex(priv->config.l3cfg), + rdata, + priv->config.ip6_privacy, + priv->iid_is_token ? &priv->iid : NULL); + l3cd = nm_l3_config_data_seal(l3cd); if (!nm_l3_config_data_equal(priv->l3cd, l3cd)) NM_SWAP(&priv->l3cd, &l3cd); @@ -1084,15 +1090,16 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd) * Returns: %TRUE if addresses need to be regenerated, %FALSE otherwise. **/ gboolean -nm_ndisc_set_iid(NMNDisc *ndisc, const NMUtilsIPv6IfaceId iid) +nm_ndisc_set_iid(NMNDisc *ndisc, const NMUtilsIPv6IfaceId iid, gboolean is_token) { NMNDiscPrivate * priv; NMNDiscDataInternal *rdata; g_return_val_if_fail(NM_IS_NDISC(ndisc), FALSE); - priv = NM_NDISC_GET_PRIVATE(ndisc); - rdata = &priv->rdata; + priv = NM_NDISC_GET_PRIVATE(ndisc); + priv->iid_is_token = is_token; + rdata = &priv->rdata; if (priv->iid.id != iid.id) { priv->iid = iid; diff --git a/src/core/ndisc/nm-ndisc.h b/src/core/ndisc/nm-ndisc.h index 90f574a90b..dc6ab078bf 100644 --- a/src/core/ndisc/nm-ndisc.h +++ b/src/core/ndisc/nm-ndisc.h @@ -225,7 +225,7 @@ int nm_ndisc_get_ifindex(NMNDisc *self); const char * nm_ndisc_get_ifname(NMNDisc *self); NMNDiscNodeType nm_ndisc_get_node_type(NMNDisc *self); -gboolean nm_ndisc_set_iid(NMNDisc *ndisc, const NMUtilsIPv6IfaceId iid); +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); NMNDiscConfigMap @@ -279,6 +279,7 @@ struct _NML3ConfigData; struct _NML3ConfigData *nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, int ifindex, const NMNDiscData * rdata, - NMSettingIP6ConfigPrivacy ip6_privacy); + NMSettingIP6ConfigPrivacy ip6_privacy, + NMUtilsIPv6IfaceId * token); #endif /* __NETWORKMANAGER_NDISC_H__ */ diff --git a/src/core/ndisc/tests/test-ndisc-fake.c b/src/core/ndisc/tests/test-ndisc-fake.c index 8cfb9ddc16..e98e158c1c 100644 --- a/src/core/ndisc/tests/test-ndisc-fake.c +++ b/src/core/ndisc/tests/test-ndisc-fake.c @@ -32,7 +32,7 @@ ndisc_new(void) memset(&iid, 0, sizeof(iid)); iid.id_u8[7] = 1; - nm_ndisc_set_iid(ndisc, iid); + nm_ndisc_set_iid(ndisc, iid, FALSE); return NM_FAKE_NDISC(ndisc); } diff --git a/src/core/ndisc/tests/test-ndisc-linux.c b/src/core/ndisc/tests/test-ndisc-linux.c index a4db5efaa5..4c2848088d 100644 --- a/src/core/ndisc/tests/test-ndisc-linux.c +++ b/src/core/ndisc/tests/test-ndisc-linux.c @@ -84,7 +84,7 @@ main(int argc, char **argv) } iid.id_u8[7] = 1; - nm_ndisc_set_iid(ndisc, iid); + nm_ndisc_set_iid(ndisc, iid, FALSE); nm_ndisc_start(ndisc); g_main_loop_run(loop); diff --git a/src/core/nm-l3-config-data.h b/src/core/nm-l3-config-data.h index 1b3fa3cc16..dfaf18549a 100644 --- a/src/core/nm-l3-config-data.h +++ b/src/core/nm-l3-config-data.h @@ -451,7 +451,6 @@ NMSettingConnectionDnsOverTls nm_l3_config_data_get_dns_over_tls(const NML3Confi gboolean nm_l3_config_data_set_dns_over_tls(NML3ConfigData * self, NMSettingConnectionDnsOverTls dns_over_tls); - NMIPRouteTableSyncMode nm_l3_config_data_get_route_table_sync(const NML3ConfigData *self, int addr_family); diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index 02456d5b95..e84c0ef75f 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -3938,6 +3938,68 @@ set: ip6_privacy_to_str(ip6_privacy)); } +static void +_l3_commit_ip6_token(NML3Cfg *self, NML3CfgCommitType commit_type) +{ + NMUtilsIPv6IfaceId token; + const NMPlatformLink *pllink; + int val; + + if (commit_type < NM_L3_CFG_COMMIT_TYPE_UPDATE || !self->priv.p->combined_l3cd_commited) + token.id = 0; + else + token = nm_l3_config_data_get_ip6_token(self->priv.p->combined_l3cd_commited); + + pllink = nm_l3cfg_get_pllink(self, TRUE); + if (!pllink || pllink->inet6_token.id == token.id) + return; + + if (_LOGT_ENABLED()) { + struct in6_addr addr = {}; + struct in6_addr addr_old = {}; + char addr_str[INET6_ADDRSTRLEN]; + char addr_str_old[INET6_ADDRSTRLEN]; + + nm_utils_ipv6_addr_set_interface_identifier(&addr, &token); + nm_utils_ipv6_addr_set_interface_identifier(&addr_old, &pllink->inet6_token); + + _LOGT("commit-ip6-token: set value %s (was %s)", + inet_ntop(AF_INET6, &addr, addr_str, INET6_ADDRSTRLEN), + inet_ntop(AF_INET6, &addr_old, addr_str_old, INET6_ADDRSTRLEN)); + } + + /* The kernel allows setting a token only when 'accept_ra' + * is 1: temporarily flip it if necessary; unfortunately + * this will also generate an additional Router Solicitation + * from kernel. */ + val = nm_platform_sysctl_ip_conf_get_int_checked(self->priv.platform, + AF_INET6, + pllink->name, + "accept_ra", + 10, + G_MININT32, + G_MAXINT32, + 1); + + if (val != 1) { + nm_platform_sysctl_ip_conf_set(self->priv.platform, + AF_INET6, + pllink->name, + "accept_ra", + "1"); + } + + nm_platform_link_set_ipv6_token(self->priv.platform, self->priv.ifindex, &token); + + if (val != 1) { + nm_platform_sysctl_ip_conf_set_int64(self->priv.platform, + AF_INET6, + pllink->name, + "accept_ra", + val); + } +} + static gboolean _l3_commit_one(NML3Cfg * self, int addr_family, @@ -3995,6 +4057,7 @@ _l3_commit_one(NML3Cfg * self, if (!IS_IPv4) { _l3_commit_ip6_privacy(self, commit_type); _l3_commit_ndisc_params(self, commit_type); + _l3_commit_ip6_token(self, commit_type); } if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_NONE) From e9bd8b3dacdd60267029f5d6295096ecb88193b1 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Fri, 5 Nov 2021 17:47:07 +0100 Subject: [PATCH 29/45] device: fix wrong assignment in ip_state_other comparison Fixes: 0b05c5adb4b0 ('device: fail when a addr family fails and the other is disabled') --- src/core/devices/nm-device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index b8495c0f0c..2348f017ef 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -3259,7 +3259,7 @@ got_ip_state: else if ((ip_state == NM_DEVICE_IP_STATE_FAILED || (ip_state == NM_DEVICE_IP_STATE_READY && disabled_or_ignore)) && (ip_state_other == NM_DEVICE_IP_STATE_FAILED - || (ip_state_other = NM_DEVICE_IP_STATE_READY && disabled_or_ignore_other))) { + || (ip_state_other == NM_DEVICE_IP_STATE_READY && disabled_or_ignore_other))) { /* If both IP states failed, or one failed and the other is disabled * then it's a failure. may-fail does not mean that both families may * fail, instead it means that at least one family must succeed. */ From de5e1eb9e5d2d47b01dc18c895b7cafce0832ca2 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 4 Nov 2021 17:17:16 +0100 Subject: [PATCH 30/45] device: don't fail immediately on DHCP expiry If we had a lease and it expired, don't fail immediately. The client will try to obtain a new lease and it will send a NO_LEASE_TIMEOUT event once it fails. Only at that time we should fail. --- src/core/devices/nm-device.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 2348f017ef..19796cf62b 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -9965,7 +9965,6 @@ _dev_ipdhcpx_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_d if (!notify_data->lease_update.l3cd) { _LOGT_ipdhcp(addr_family, "lease lost"); - _dev_ipdhcpx_handle_fail(self, addr_family, "lease lost"); return; } From a31919333321a9fbda2044044529abe62b90547a Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 4 Nov 2021 17:19:02 +0100 Subject: [PATCH 31/45] device: fix optional 802.1X authentication If the authentication is optional, we are going to re-enter stage2. Set the "ready" variable so that we can return success immediately and skip to stage3. --- src/core/devices/nm-device-ethernet.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c index 52fce56943..d89bd7371c 100644 --- a/src/core/devices/nm-device-ethernet.c +++ b/src/core/devices/nm-device-ethernet.c @@ -479,6 +479,7 @@ wired_auth_cond_fail(NMDeviceEthernet *self, NMDeviceStateReason reason) _LOGI(LOGD_DEVICE | LOGD_ETHER, "Activation: (ethernet) 802.1X authentication is optional, continuing after a failure"); + priv->supplicant.ready = TRUE; if (NM_IN_SET(nm_device_get_state(device), NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_NEED_AUTH)) nm_device_activate_schedule_stage2_device_config(device, FALSE); From bd7b5aa707d19f090a9a8d386f076480e6abe04c Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 5 Nov 2021 15:17:33 +0100 Subject: [PATCH 32/45] device: don't disable IPv6 when NM is managing IPv6 If NM set addrgenmode=none, it's because it manages the IPv6 in user space. In such case it should never disable IPv6. --- src/core/devices/nm-device.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 19796cf62b..8314a9771a 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -11398,6 +11398,14 @@ _dev_sysctl_restore_ip6_properties(NMDevice *self) static void _dev_sysctl_set_disable_ipv6(NMDevice *self, gboolean do_disable) { + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + /* If we previously set addrgenmode=none, we are managing + * IPv6 in user space and we should not disable it. */ + if (do_disable && priv->addrgenmode6_data.previous_mode_has + && priv->addrgenmode6_data.previous_mode_val == NM_IN6_ADDR_GEN_MODE_NONE) + return; + nm_device_sysctl_ip_conf_set(self, AF_INET6, "disable_ipv6", do_disable ? "1" : "0"); } From cd65351d29c41755c96349ec1435a85f9e56148e Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 5 Nov 2021 15:19:09 +0100 Subject: [PATCH 33/45] device: fix _dev_addrgenmode6_set() If addrgenmode=0 is already set, the function should still toggle disable_ipv6 if needed, to stop the generation of temporary addresses. Also, it should store the last set value into 'previous_mode_val'. Fixes-test: @ipv6_keep_external_routes --- src/core/devices/nm-device.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 8314a9771a..0392d54b6a 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -11442,24 +11442,26 @@ _dev_addrgenmode6_set(NMDevice *self, guint8 addr_gen_mode) nm_platform_link_inet6_addrgenmode2str(addr_gen_mode, sbuf, sizeof(sbuf)), (cur_addr_gen_mode == addr_gen_mode) ? " (already set)" : ""); - if (cur_addr_gen_mode == addr_gen_mode) - return; - - r = nm_platform_link_set_inet6_addr_gen_mode(nm_device_get_platform(self), - ifindex, - addr_gen_mode); - if (r < 0) { - _NMLOG_ip(NM_IN_SET(r, -NME_PL_NOT_FOUND, -NME_PL_OPNOTSUPP) ? LOGL_DEBUG : LOGL_WARN, - AF_INET6, - "addrgenmode6: failed to set %s: (%s)", - nm_platform_link_inet6_addrgenmode2str(addr_gen_mode, sbuf, sizeof(sbuf)), - nm_strerror(r)); + if (cur_addr_gen_mode != addr_gen_mode) { + r = nm_platform_link_set_inet6_addr_gen_mode(nm_device_get_platform(self), + ifindex, + addr_gen_mode); + if (r < 0) { + _NMLOG_ip(NM_IN_SET(r, -NME_PL_NOT_FOUND, -NME_PL_OPNOTSUPP) ? LOGL_DEBUG : LOGL_WARN, + AF_INET6, + "addrgenmode6: failed to set %s: (%s)", + nm_platform_link_inet6_addrgenmode2str(addr_gen_mode, sbuf, sizeof(sbuf)), + nm_strerror(r)); + } else { + priv->addrgenmode6_data.previous_mode_val = addr_gen_mode; + } } if (addr_gen_mode == NM_IN6_ADDR_GEN_MODE_NONE) { gs_free char *value = NULL; - /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */ + /* Bounce IPv6 to ensure the kernel stops IPv6LL address and temporary + * address generation */ _LOGD_ip(AF_INET6, "addrgenmode6: toggle disable_ipv6 sysctl after disabling addr-gen-mode"); value = nm_device_sysctl_ip_conf_get(self, AF_INET6, "disable_ipv6"); From 3a0eb586b8e1544bf68c27d08aefd9e9f62f76d7 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 8 Nov 2021 14:26:27 +0100 Subject: [PATCH 34/45] device: don't reset addrgenmode for assumed devices If we reset the addrgenmode, IPv6 addresses are lost. --- src/core/devices/nm-device.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 0392d54b6a..204cb5c407 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -11816,7 +11816,8 @@ activate_stage3_ip_config(NMDevice *self) * IPv6LL if this is not an assumed connection, since assumed connections * will already have IPv6 set up. */ - _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_NONE); + if (!nm_device_sys_iface_state_is_external_or_assume(self)) + _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_NONE); /* Re-enable IPv6 on the interface */ nm_device_sysctl_ip_conf_set(self, AF_INET6, "accept_ra", "0"); From 655896f75b03243f86f226e2e3bf2b1525e9b503 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 9 Nov 2021 19:45:07 +0100 Subject: [PATCH 35/45] device: set ipv6 privacy in the the ipmanual l3cd In this way, the ipv6 privacy setting is committed as soon as the connection goes up. Fixes-test: @ipv6_ip6-default_privacy --- src/core/devices/nm-device.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 204cb5c407..159f2abd6e 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -2636,6 +2636,7 @@ nm_device_create_l3_config_data_from_connection(NMDevice *self, NMConnection *co nm_l3_config_data_set_mdns(l3cd, _prop_get_connection_mdns(self)); nm_l3_config_data_set_llmnr(l3cd, _prop_get_connection_llmnr(self)); nm_l3_config_data_set_dns_over_tls(l3cd, _prop_get_connection_dns_over_tls(self)); + nm_l3_config_data_set_ip6_privacy(l3cd, _prop_get_ipv6_ip6_privacy(self)); return l3cd; } From 81ac02ae75e7f2d370d86cdd939f6694c6796f24 Mon Sep 17 00:00:00 2001 From: Wen Liang Date: Sun, 7 Nov 2021 17:06:03 -0500 Subject: [PATCH 36/45] core: clear sticky update flag when unmanaging a device Sticky update flag forces a commit at UPDATE level after unmanaging a device. As a result, all the link local addresses will be removed. To prevent the commit after unmanaging a device, clear sticky update flag. Signed-off-by: Wen Liang --- src/core/devices/nm-device.c | 2 ++ src/core/nm-l3cfg.c | 14 ++++++++++++++ src/core/nm-l3cfg.h | 2 ++ 3 files changed, 18 insertions(+) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 159f2abd6e..3711459820 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -3924,6 +3924,8 @@ _dev_l3_cfg_commit_type_reset(NMDevice *self) do_set: priv->l3cfg_commit_type = nm_l3cfg_commit_type_register(priv->l3cfg, commit_type, priv->l3cfg_commit_type, "device"); + if (commit_type == NM_L3_CFG_COMMIT_TYPE_NONE) + nm_l3cfg_commit_type_reset_update(priv->l3cfg); } /*****************************************************************************/ diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index e84c0ef75f..9c92f5cd53 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -4310,6 +4310,20 @@ nm_l3cfg_commit_type_unregister(NML3Cfg *self, NML3CfgCommitTypeHandle *handle) nm_g_slice_free(handle); } +void +nm_l3cfg_commit_type_reset_update(NML3Cfg *self) +{ + NML3CfgCommitTypeHandle *h; + + c_list_for_each_entry (h, &self->priv.p->commit_type_lst_head, commit_type_lst) { + if (h->commit_type >= NM_L3_CFG_COMMIT_TYPE_UPDATE) { + return; + } + } + + self->priv.p->commit_type_update_sticky = FALSE; +} + /*****************************************************************************/ const NML3ConfigData * diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h index 5eca683a90..35560142be 100644 --- a/src/core/nm-l3cfg.h +++ b/src/core/nm-l3cfg.h @@ -426,6 +426,8 @@ nm_l3cfg_commit_type_clear(NML3Cfg *self, NML3CfgCommitTypeHandle **handle) return TRUE; } +void nm_l3cfg_commit_type_reset_update(NML3Cfg *self); + /*****************************************************************************/ const NML3ConfigData *nm_l3cfg_get_combined_l3cd(NML3Cfg *self, gboolean get_commited); From 6b4123db1ce7aff16254976f7bc558b2fe4a9b69 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Wed, 10 Nov 2021 11:02:17 +0100 Subject: [PATCH 37/45] l3cfg: do not add dependent routes for non-default routes When the route is not a default route 0.0.0.0/0, NetworkManager should not add dependent routes. Signed-off-by: Fernando Fernandez Mancera --- src/core/nm-l3-config-data.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c index 02f9142a3c..bf0f976252 100644 --- a/src/core/nm-l3-config-data.c +++ b/src/core/nm-l3-config-data.c @@ -2385,7 +2385,8 @@ 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 (_data_get_direct_route_for_host( + if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route_src) + || _data_get_direct_route_for_host( self, addr_family, p_gateway, From bce13b0b2f98864fda03925e5d8a93023a937453 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 12 Nov 2021 15:41:18 +0100 Subject: [PATCH 38/45] dhcp: schedule a timeout when starting the client 'no_lease_timeout' is the timeout to get the initial lease and to renew it once it was lost. It should be scheduled when the client is started. Fixes-test: @dhcp4_outages_in_various_situation --- src/core/dhcp/nm-dhcp-client.c | 45 +++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c index 484ec34e72..71da40b707 100644 --- a/src/core/dhcp/nm-dhcp-client.c +++ b/src/core/dhcp/nm-dhcp-client.c @@ -238,6 +238,25 @@ nm_dhcp_client_get_config(NMDhcpClient *self) return &priv->config; } +static void +schedule_no_lease_timeout(NMDhcpClient *self) +{ + NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); + + if (priv->no_lease_timeout_source) + return; + + if (priv->config.timeout == NM_DHCP_TIMEOUT_INFINITY) { + _LOGI("activation: beginning transaction (no timeout)"); + priv->no_lease_timeout_source = g_source_ref(nm_g_source_sentinel_get(0)); + } else { + _LOGI("activation: beginning transaction (timeout in %u seconds)", + (guint) priv->config.timeout); + priv->no_lease_timeout_source = + nm_g_timeout_add_seconds_source(priv->config.timeout, _no_lease_timeout, self); + } +} + void nm_dhcp_client_set_state(NMDhcpClient *self, NMDhcpState new_state, const NML3ConfigData *l3cd) { @@ -271,19 +290,11 @@ nm_dhcp_client_set_state(NMDhcpClient *self, NMDhcpState new_state, const NML3Co if (priv->l3cd == l3cd) return; - if (l3cd) + if (l3cd) { nm_clear_g_source_inst(&priv->no_lease_timeout_source); - else { - /* FIXME(l3cfg:dhcp): we also need to start no_lease_timeout initially, if the - * instance starts without a previous_lease. */ - if (!priv->no_lease_timeout_source && priv->l3cd) { - if (priv->config.timeout == NM_DHCP_TIMEOUT_INFINITY) - priv->no_lease_timeout_source = g_source_ref(nm_g_source_sentinel_get(0)); - else { - priv->no_lease_timeout_source = - nm_g_timeout_add_seconds_source(priv->config.timeout, _no_lease_timeout, self); - } - } + } else { + if (priv->l3cd) + schedule_no_lease_timeout(self); } /* FIXME(l3cfg:dhcp): the API of NMDhcpClient is changing to expose a simpler API. @@ -444,11 +455,7 @@ nm_dhcp_client_start_ip4(NMDhcpClient *self, GError **error) g_return_val_if_fail(priv->config.addr_family == AF_INET, FALSE); g_return_val_if_fail(priv->config.uuid, FALSE); - if (priv->config.timeout == NM_DHCP_TIMEOUT_INFINITY) - _LOGI("activation: beginning transaction (no timeout)"); - else - _LOGI("activation: beginning transaction (timeout in %u seconds)", - (guint) priv->config.timeout); + schedule_no_lease_timeout(self); return NM_DHCP_CLIENT_GET_CLASS(self)->ip4_start(self, error); } @@ -568,6 +575,8 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp g_signal_handlers_disconnect_by_func(l3cfg, l3_cfg_notify_cb, self); nm_clear_g_source_inst(&priv->ipv6_lladdr_timeout_source); + schedule_no_lease_timeout(self); + if (!NM_DHCP_CLIENT_GET_CLASS(self)->ip6_start(self, &addr->address, &error)) { _emit_notify(self, &((NMDhcpClientNotifyData){ @@ -617,6 +626,8 @@ nm_dhcp_client_start_ip6(NMDhcpClient *self, GError **error) return TRUE; } + schedule_no_lease_timeout(self); + return NM_DHCP_CLIENT_GET_CLASS(self)->ip6_start(self, &addr->address, error); } From 6ac95047caf65edfb0c2005e46e5e207346a3a07 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 12 Nov 2021 15:49:34 +0100 Subject: [PATCH 39/45] dhcp: remove old timeout code There is already the 'no_lease_timeout'. --- src/core/dhcp/nm-dhcp-client.c | 49 -------------------------------- src/core/dhcp/nm-dhcp-nettools.c | 2 -- src/core/dhcp/nm-dhcp-systemd.c | 2 -- 3 files changed, 53 deletions(-) diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c index 71da40b707..dede18e540 100644 --- a/src/core/dhcp/nm-dhcp-client.c +++ b/src/core/dhcp/nm-dhcp-client.c @@ -44,7 +44,6 @@ typedef struct _NMDhcpClientPrivate { GSource * no_lease_timeout_source; GSource * ipv6_lladdr_timeout_source; pid_t pid; - guint timeout_id; guint watch_id; NMDhcpState state; bool iaid_explicit : 1; @@ -165,14 +164,6 @@ reason_to_state(NMDhcpClient *self, const char *iface, const char *reason) /*****************************************************************************/ -static void -timeout_cleanup(NMDhcpClient *self) -{ - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - nm_clear_g_source(&priv->timeout_id); -} - static void watch_cleanup(NMDhcpClient *self) { @@ -276,8 +267,6 @@ nm_dhcp_client_set_state(NMDhcpClient *self, NMDhcpState new_state, const NML3Co if (l3cd) nm_l3_config_data_seal(l3cd); - if (new_state >= NM_DHCP_STATE_BOUND) - timeout_cleanup(self); if (new_state >= NM_DHCP_STATE_TIMEOUT) watch_cleanup(self); @@ -372,19 +361,6 @@ nm_dhcp_client_set_state(NMDhcpClient *self, NMDhcpState new_state, const NML3Co } } -/* FIXME(l3cfg:dhcp) */ -_nm_unused static gboolean -transaction_timeout(gpointer user_data) -{ - NMDhcpClient * self = NM_DHCP_CLIENT(user_data); - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - priv->timeout_id = 0; - _LOGW("request timed out"); - nm_dhcp_client_set_state(self, NM_DHCP_STATE_TIMEOUT, NULL); - return G_SOURCE_REMOVE; -} - static void daemon_watch_cb(GPid pid, int status, gpointer user_data) { @@ -402,21 +378,6 @@ daemon_watch_cb(GPid pid, int status, gpointer user_data) nm_dhcp_client_set_state(self, NM_DHCP_STATE_TERMINATED, NULL); } -void -nm_dhcp_client_start_timeout(NMDhcpClient *self) -{ - NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self); - - g_return_if_fail(priv->timeout_id == 0); - - /* Set up a timeout on the transaction to kill it after the timeout */ - - /* FIXME(l3cfg:dhcp): no-lease-timeout is only for convenience for the user (NMDevice). - * Whatever the purpose of nm_dhcp_client_start_timeout() is, it is not the same timer. */ - return; - //priv->timeout_id = g_timeout_add_seconds(priv->no_lease_timeout, transaction_timeout, self); -} - void nm_dhcp_client_watch_child(NMDhcpClient *self, pid_t pid) { @@ -425,8 +386,6 @@ nm_dhcp_client_watch_child(NMDhcpClient *self, pid_t pid) g_return_if_fail(priv->pid == -1); priv->pid = pid; - nm_dhcp_client_start_timeout(self); - g_return_if_fail(priv->watch_id == 0); priv->watch_id = g_child_watch_add(pid, daemon_watch_cb, self); } @@ -440,7 +399,6 @@ nm_dhcp_client_stop_watch_child(NMDhcpClient *self, pid_t pid) priv->pid = -1; watch_cleanup(self); - timeout_cleanup(self); } gboolean @@ -608,12 +566,6 @@ nm_dhcp_client_start_ip6(NMDhcpClient *self, GError **error) _set_effective_client_id(self, own_client_id ?: priv->config.client_id, FALSE); - if (priv->config.timeout == NM_DHCP_TIMEOUT_INFINITY) - _LOGI("activation: beginning transaction (no timeout)"); - else - _LOGI("activation: beginning transaction (timeout in %u seconds)", - (guint) priv->config.timeout); - addr = ipv6_lladdr_find(self); if (!addr) { _LOGD("waiting for IPv6LL address"); @@ -1140,7 +1092,6 @@ dispose(GObject *object) nm_dhcp_client_stop(self, FALSE); watch_cleanup(self); - timeout_cleanup(self); nm_clear_g_source_inst(&priv->no_lease_timeout_source); nm_clear_g_source_inst(&priv->ipv6_lladdr_timeout_source); diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c index 6ab7f8c18d..e8e3a2f1e4 100644 --- a/src/core/dhcp/nm-dhcp-nettools.c +++ b/src/core/dhcp/nm-dhcp-nettools.c @@ -1208,8 +1208,6 @@ ip4_start(NMDhcpClient *client, GError **error) } _LOGT("dhcp-client4: start " NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(priv->client)); - - nm_dhcp_client_start_timeout(client); return TRUE; } diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c index f5b75b50de..7c3c289996 100644 --- a/src/core/dhcp/nm-dhcp-systemd.c +++ b/src/core/dhcp/nm-dhcp-systemd.c @@ -752,7 +752,6 @@ ip4_start(NMDhcpClient *client, GError **error) return FALSE; } - nm_dhcp_client_start_timeout(client); return TRUE; } @@ -1049,7 +1048,6 @@ ip6_start(NMDhcpClient *client, const struct in6_addr *ll_addr, GError **error) return FALSE; } - nm_dhcp_client_start_timeout(client); return TRUE; } From b85a9cd9df540e9ee7adc40fcdef56a8bf314d1f Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Tue, 9 Nov 2021 12:07:45 +0100 Subject: [PATCH 40/45] device: set ip_state to PENDING when cleaning up from reapply When doing a reapply the ip_state must be set as PENDING, if not the ipdhcp_state won't be extended to ip_state. In addition, if one of the IP configuration is ready and the other may fail, then we should consider it ready. The other ip state does not matter at all, it can be none too. Fixes-test: @nmcli_device_reapply_routes --- src/core/devices/nm-device.c | 37 +++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 3711459820..078dc16a5d 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -833,7 +833,8 @@ 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); +static void +_cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gboolean from_reapply); static void concheck_update_state(NMDevice * self, int addr_family, @@ -3246,10 +3247,6 @@ got_ip_state: &may_fail_other))) combinedip_state = NM_DEVICE_IP_STATE_PENDING; else if (ip_state == NM_DEVICE_IP_STATE_READY - && NM_IN_SET(ip_state_other, - NM_DEVICE_IP_STATE_PENDING, - NM_DEVICE_IP_STATE_READY, - NM_DEVICE_IP_STATE_FAILED) && _prop_get_ipvx_may_fail_cached(self, nm_utils_addr_family_other(addr_family), &may_fail_other)) @@ -3350,13 +3347,16 @@ _dev_ip_state_check_async(NMDevice *self, int addr_family) } static void -_dev_ip_state_cleanup(NMDevice *self, int addr_family) +_dev_ip_state_cleanup(NMDevice *self, int addr_family, gboolean from_reapply) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); int IS_IPv4; if (addr_family == AF_UNSPEC) { - _dev_ip_state_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE, "ip-state-clear"); + _dev_ip_state_set_state(self, + addr_family, + from_reapply ? NM_DEVICE_IP_STATE_PENDING : NM_DEVICE_IP_STATE_NONE, + "ip-state-clear"); return; } @@ -3364,7 +3364,10 @@ _dev_ip_state_cleanup(NMDevice *self, int addr_family) nm_clear_g_source_inst(&priv->ip_data_x[IS_IPv4].check_async_source); nm_clear_g_source_inst(&priv->ip_data_x[IS_IPv4].req_timeout_source); - _dev_ip_state_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE, "ip-state-clear"); + _dev_ip_state_set_state(self, + addr_family, + from_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; priv->ip_data_x[IS_IPv4].is_disabled = FALSE; @@ -11744,11 +11747,11 @@ activate_stage3_ip_config(NMDevice *self) if (priv->ip_data_4.do_reapply) { _LOGD_ip(AF_INET, "reapply..."); - _cleanup_ip_pre(self, AF_INET, CLEANUP_TYPE_DECONFIGURE); + _cleanup_ip_pre(self, AF_INET, CLEANUP_TYPE_DECONFIGURE, TRUE); } if (priv->ip_data_6.do_reapply) { _LOGD_ip(AF_INET6, "reapply..."); - _cleanup_ip_pre(self, AF_INET6, CLEANUP_TYPE_DECONFIGURE); + _cleanup_ip_pre(self, AF_INET6, CLEANUP_TYPE_DECONFIGURE, TRUE); } /* Add the interface to the specified firewall zone */ @@ -12247,7 +12250,7 @@ delete_on_deactivate_check_and_schedule(NMDevice *self) } static void -_cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type) +_cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gboolean from_reapply) { const int IS_IPv4 = NM_IS_IPv4(addr_family); @@ -12265,8 +12268,8 @@ _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type) _dev_ipmanual_cleanup(self); - _dev_ip_state_cleanup(self, AF_UNSPEC); - _dev_ip_state_cleanup(self, addr_family); + _dev_ip_state_cleanup(self, AF_UNSPEC, from_reapply); + _dev_ip_state_cleanup(self, addr_family, from_reapply); } gboolean @@ -14923,8 +14926,8 @@ _cleanup_generic_pre(NMDevice *self, CleanupType cleanup_type) for (i = 0; i < 2; i++) nm_clear_pointer(&priv->hostname_resolver_x[i], _hostname_resolver_free); - _cleanup_ip_pre(self, AF_INET, cleanup_type); - _cleanup_ip_pre(self, AF_INET6, cleanup_type); + _cleanup_ip_pre(self, AF_INET, cleanup_type, FALSE); + _cleanup_ip_pre(self, AF_INET6, cleanup_type, FALSE); _dev_ip_state_req_timeout_cancel(self, AF_UNSPEC); } @@ -15424,8 +15427,8 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, /* Clean up any half-done IP operations if the device's layer2 * finds out it needs authentication during IP config. */ - _cleanup_ip_pre(self, AF_INET, CLEANUP_TYPE_DECONFIGURE); - _cleanup_ip_pre(self, AF_INET6, CLEANUP_TYPE_DECONFIGURE); + _cleanup_ip_pre(self, AF_INET, CLEANUP_TYPE_DECONFIGURE, FALSE); + _cleanup_ip_pre(self, AF_INET6, CLEANUP_TYPE_DECONFIGURE, FALSE); } break; default: From 8995d44a0bae51c8bb41bc0a9d4e7b2ec9cb92af Mon Sep 17 00:00:00 2001 From: Wen Liang Date: Wed, 10 Nov 2021 21:13:38 -0500 Subject: [PATCH 41/45] core: compare the DNS configurations before updating DNS DNS manager always sets `priv->config_changed = TRUE` and overwrites the "resolv.conf" file. To fix it, compare the new configuration with the old configuration, if there is no change, skipping the update. Fixes-test: @ipv4_ignore_resolveconf_with_ignore_auto_dns Fixes-test: @ipv4_ignore_resolveconf_with_ignore_auto_dns_var1 https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1023 --- src/core/dns/nm-dns-manager.c | 39 +++++++++++++++++++ src/core/nm-config-data.c | 55 +++++++++++++++++++++++++++ src/core/nm-config-data.h | 3 +- src/core/nm-ip-config.c | 71 +++++++++++++++++++++++++++++++++++ src/core/nm-ip-config.h | 2 + 5 files changed, 169 insertions(+), 1 deletion(-) diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c index 15072f799d..4fe7945033 100644 --- a/src/core/dns/nm-dns-manager.c +++ b/src/core/dns/nm-dns-manager.c @@ -35,6 +35,7 @@ #include "nm-dns-plugin.h" #include "nm-dns-systemd-resolved.h" #include "nm-dns-unbound.h" +#include "nm-ip-config.h" #include "nm-l3-config-data.h" #include "nm-manager.h" #include "nm-utils.h" @@ -99,6 +100,9 @@ typedef struct { char *hostname; guint updates_queue; + guint8 hash[HASH_LEN]; /* SHA1 hash of current DNS config */ + guint8 prev_hash[HASH_LEN]; /* Hash when begin_updates() was called */ + NMDnsManagerResolvConfManager rc_manager; char * mode; NMDnsPlugin * sd_resolve_plugin; @@ -1097,6 +1101,30 @@ update_resolv_conf(NMDnsManager * self, return write_file_result; } +static void +compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[static HASH_LEN]) +{ + nm_auto_free_checksum GChecksum *sum = NULL; + NMDnsConfigIPData * ip_data; + + sum = g_checksum_new(G_CHECKSUM_SHA1); + nm_assert(HASH_LEN == g_checksum_type_get_length(G_CHECKSUM_SHA1)); + + if (global) + nm_global_dns_config_update_checksum(global, sum); + else { + 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); + } + + nm_utils_checksum_get_digest_len(sum, buffer, HASH_LEN); +} + static gboolean merge_global_dns_config(NMResolvConfData *rc, NMGlobalDnsConfig *global_conf) { @@ -1649,6 +1677,9 @@ update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError data = nm_config_get_data(priv->config); global_config = nm_config_data_get_global_dns_config(data); + /* Update hash with config we're applying */ + compute_hash(self, global_config, priv->hash); + _collect_resolv_conf_data(self, global_config, &searches, @@ -2006,6 +2037,10 @@ nm_dns_manager_begin_updates(NMDnsManager *self, const char *func) priv = NM_DNS_MANAGER_GET_PRIVATE(self); + /* Save current hash when starting a new batch */ + if (priv->updates_queue == 0) + memcpy(priv->prev_hash, priv->hash, sizeof(priv->hash)); + priv->updates_queue++; _LOGD("(%s): queueing DNS updates (%d)", func, priv->updates_queue); @@ -2016,12 +2051,15 @@ nm_dns_manager_end_updates(NMDnsManager *self, const char *func) { NMDnsManagerPrivate *priv; gs_free_error GError *error = NULL; + guint8 new[HASH_LEN]; g_return_if_fail(self != NULL); priv = NM_DNS_MANAGER_GET_PRIVATE(self); g_return_if_fail(priv->updates_queue > 0); + compute_hash(self, nm_config_data_get_global_dns_config(nm_config_get_data(priv->config)), new); + priv->config_changed = (memcmp(new, priv->prev_hash, sizeof(new)) != 0) ? TRUE : FALSE; _LOGD("(%s): DNS configuration %s", func, priv->config_changed ? "changed" : "did not change"); priv->updates_queue--; @@ -2574,6 +2612,7 @@ nm_dns_manager_init(NMDnsManager *self) (GDestroyNotify) _dns_config_data_free, NULL); + compute_hash(self, NULL, NM_DNS_MANAGER_GET_PRIVATE(self)->hash); g_signal_connect(G_OBJECT(priv->config), NM_CONFIG_SIGNAL_CONFIG_CHANGED, G_CALLBACK(config_changed_cb), diff --git a/src/core/nm-config-data.c b/src/core/nm-config-data.c index 0c4f8099de..e86e993c17 100644 --- a/src/core/nm-config-data.c +++ b/src/core/nm-config-data.c @@ -982,6 +982,61 @@ nm_global_dns_config_cmp(const NMGlobalDnsConfig *a, return 0; } +void +nm_global_dns_config_update_checksum(const NMGlobalDnsConfig *dns_config, GChecksum *sum) +{ + NMGlobalDnsDomain *domain; + guint i, j; + guint8 v8; + + g_return_if_fail(dns_config); + g_return_if_fail(sum); + + v8 = NM_HASH_COMBINE_BOOLS(guint8, + !dns_config->searches, + !dns_config->options, + !dns_config->domain_list); + g_checksum_update(sum, (guchar *) &v8, 1); + + if (dns_config->searches) { + for (i = 0; dns_config->searches[i]; i++) + g_checksum_update(sum, + (guchar *) dns_config->searches[i], + strlen(dns_config->searches[i]) + 1); + } + if (dns_config->options) { + for (i = 0; dns_config->options[i]; i++) + g_checksum_update(sum, + (guchar *) dns_config->options[i], + strlen(dns_config->options[i]) + 1); + } + + if (dns_config->domain_list) { + for (i = 0; dns_config->domain_list[i]; i++) { + domain = g_hash_table_lookup(dns_config->domains, dns_config->domain_list[i]); + nm_assert(domain); + + v8 = NM_HASH_COMBINE_BOOLS(guint8, !domain->servers, !domain->options); + g_checksum_update(sum, (guchar *) &v8, 1); + + g_checksum_update(sum, (guchar *) domain->name, strlen(domain->name) + 1); + + if (domain->servers) { + for (j = 0; domain->servers[j]; j++) + g_checksum_update(sum, + (guchar *) domain->servers[j], + strlen(domain->servers[j]) + 1); + } + if (domain->options) { + for (j = 0; domain->options[j]; j++) + g_checksum_update(sum, + (guchar *) domain->options[j], + strlen(domain->options[j]) + 1); + } + } + } +} + static void global_dns_domain_free(NMGlobalDnsDomain *domain) { diff --git a/src/core/nm-config-data.h b/src/core/nm-config-data.h index 89c69e2877..1923dc49c4 100644 --- a/src/core/nm-config-data.h +++ b/src/core/nm-config-data.h @@ -267,7 +267,8 @@ gboolean nm_global_dns_config_is_empty(const NMGlobalDnsConfig *dns_co int nm_global_dns_config_cmp(const NMGlobalDnsConfig *a, const NMGlobalDnsConfig *b, gboolean check_internal); -void nm_global_dns_config_free(NMGlobalDnsConfig *dns_config); +void nm_global_dns_config_update_checksum(const NMGlobalDnsConfig *dns_config, GChecksum *sum); +void nm_global_dns_config_free(NMGlobalDnsConfig *dns_config); NMGlobalDnsConfig *nm_global_dns_config_from_dbus(const GValue *value, GError **error); void nm_global_dns_config_to_dbus(const NMGlobalDnsConfig *dns_config, GValue *value); diff --git a/src/core/nm-ip-config.c b/src/core/nm-ip-config.c index b3e599400e..2470e40e2b 100644 --- a/src/core/nm-ip-config.c +++ b/src/core/nm-ip-config.c @@ -498,6 +498,77 @@ 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 */ diff --git a/src/core/nm-ip-config.h b/src/core/nm-ip-config.h index 5b2552e3a3..9bc1ff45c6 100644 --- a/src/core/nm-ip-config.h +++ b/src/core/nm-ip-config.h @@ -51,6 +51,8 @@ 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 * From fcfa598fc20ec68c9bbdb93364e5011531db7983 Mon Sep 17 00:00:00 2001 From: Ana Cabral Date: Tue, 16 Nov 2021 21:35:32 +0100 Subject: [PATCH 42/45] device: fix route metric penalty assignment When a route has the connectivity check enabled and does not have full connectivity, it should have its route metric penalized, this way this route will not be preferred over others. Fixes-test: @per_device_connectivity_check --- src/core/devices/nm-device.c | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 078dc16a5d..598d3fcd26 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -564,9 +564,6 @@ typedef struct _NMDevicePrivate { bool up : 1; /* IFF_UP */ - bool default_route_metric_penalty_ip4_has : 1; - bool default_route_metric_penalty_ip6_has : 1; - bool v4_route_table_initialized : 1; bool v6_route_table_initialized : 1; @@ -4811,31 +4808,16 @@ nm_device_get_route_metric_default(NMDeviceType device_type) return 11000; } -/* FIXME(l3cfg): we currently never call this function. We need to react - * to changes and re-commit the IP configuration with updated penalty. */ -_nm_unused static gboolean -_dev_default_route_metric_penalty_detect(NMDevice *self, int addr_family) +static guint32 +_dev_default_route_metric_penalty_get(NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); const int IS_IPv4 = NM_IS_IPv4(addr_family); - /* currently we don't differentiate between IPv4 and IPv6 when detecting - * connectivity. */ if (priv->concheck_x[IS_IPv4].state != NM_CONNECTIVITY_FULL && nm_connectivity_check_enabled(concheck_get_mgr(self))) - return TRUE; - - return FALSE; -} - -static guint32 -_dev_default_route_metric_penalty_get(NMDevice *self, int addr_family) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - if (NM_IS_IPv4(addr_family) ? priv->default_route_metric_penalty_ip4_has - : priv->default_route_metric_penalty_ip6_has) return 20000; + return 0; } @@ -14944,9 +14926,6 @@ _cleanup_generic_post(NMDevice *self, CleanupType cleanup_type) priv->v4_route_table_all_sync_before = FALSE; priv->v6_route_table_all_sync_before = FALSE; - priv->default_route_metric_penalty_ip4_has = FALSE; - priv->default_route_metric_penalty_ip6_has = FALSE; - priv->mtu_force_set_done = FALSE; priv->needs_ip6_subnet = FALSE; From c5f26a546b4e146b925efd7665ebab226f7634ef Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 10 Nov 2021 16:45:40 +0100 Subject: [PATCH 43/45] l3cfg: fix handling of removed addresses If the address appears after being removed, start defending it instead of throwing an assertion. --- src/core/nm-l3cfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index 9c92f5cd53..b5a948b889 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -2723,10 +2723,10 @@ handle_probing_done: goto handle_start_defending; case NM_L3_ACD_ADDR_STATE_READY: case NM_L3_ACD_ADDR_STATE_DEFENDING: + case NM_L3_ACD_ADDR_STATE_EXTERNAL_REMOVED: goto handle_start_defending; case NM_L3_ACD_ADDR_STATE_CONFLICT: return; - case NM_L3_ACD_ADDR_STATE_EXTERNAL_REMOVED: nm_assert_not_reached(); return; } From 7f03b0c9ef0f061a893b3a741c225309eb779107 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 10 Nov 2021 17:08:59 +0100 Subject: [PATCH 44/45] l3cfg: consider a externally-removed address as ACD-ready If an address is removed externally, it should be considered ready from ACD point of view. In this way it can be commit again (for example, if the DHCP lease is renewed). --- src/core/nm-l3cfg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index b5a948b889..4c1f8dc029 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -3404,7 +3404,8 @@ _l3_hook_add_obj_cb(const NML3ConfigData * l3cd, if (!NM_IN_SET(acd_data->info.state, NM_L3_ACD_ADDR_STATE_READY, - NM_L3_ACD_ADDR_STATE_DEFENDING)) { + NM_L3_ACD_ADDR_STATE_DEFENDING, + NM_L3_ACD_ADDR_STATE_EXTERNAL_REMOVED)) { acd_bad = TRUE; goto out_ip4_address; } From 2838b1c5e893f5e22b23f18bc44f81560a1f65fa Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 11 Nov 2021 22:16:14 +0100 Subject: [PATCH 45/45] core: track force-commit flag for l3cd and platform objects Problem: if l3cfg commits an address and routes from DHCP, when the address expires those objects are removed automatically. NM tracks the objects as missing as if the user removed them. This is to prevent l3cfg to committing them again. If the lease if renewed, l3cfg should be allowed to commit those objects again. Introduce a l3cd flag to indicate that it should be force-committed once, and propagate this flag to platform objects. In this way, l3cfg can avoid committing again objects that are removed externally, but it can commit them when the l3cd changes. Fixes-test: @bridge_down_to_l2_only --- src/core/devices/nm-device.c | 44 +++++++++++++++++++++------ src/core/nm-l3-config-data.c | 14 +++++++++ src/core/nm-l3-config-data.h | 1 + src/core/nm-l3cfg.c | 26 +++++++++++++--- src/core/nm-l3cfg.h | 4 +++ src/libnm-platform/nm-platform.c | 52 +++++++++++++++++++++----------- src/libnm-platform/nm-platform.h | 17 +++++++---- src/libnm-platform/nmp-object.h | 15 +++++++++ 8 files changed, 134 insertions(+), 39 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 598d3fcd26..4cba253c85 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -3572,7 +3572,9 @@ after_merge_flags: } static gboolean -_dev_l3_register_l3cds_add_config(NMDevice *self, L3ConfigDataType l3cd_type) +_dev_l3_register_l3cds_add_config(NMDevice * self, + L3ConfigDataType l3cd_type, + NML3CfgConfigFlags flags) { NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); NML3ConfigMergeFlags merge_flags; @@ -3595,15 +3597,16 @@ _dev_l3_register_l3cds_add_config(NMDevice *self, L3ConfigDataType l3cd_type) _prop_get_ipvx_dns_priority(self, AF_INET6), acd_defend_type, acd_timeout_msec, - NM_L3CFG_CONFIG_FLAGS_NONE, + flags, merge_flags); } static gboolean -_dev_l3_register_l3cds_set_one(NMDevice * self, - L3ConfigDataType l3cd_type, - const NML3ConfigData *l3cd, - NMTernary commit_sync) +_dev_l3_register_l3cds_set_one_full(NMDevice * self, + L3ConfigDataType l3cd_type, + const NML3ConfigData *l3cd, + NML3CfgConfigFlags flags, + NMTernary commit_sync) { NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); nm_auto_unref_l3cd const NML3ConfigData *l3cd_old = NULL; @@ -3626,7 +3629,7 @@ _dev_l3_register_l3cds_set_one(NMDevice * self, if (priv->l3cfg) { if (priv->l3cds[l3cd_type].d) { - if (_dev_l3_register_l3cds_add_config(self, l3cd_type)) + if (_dev_l3_register_l3cds_add_config(self, l3cd_type, flags)) changed = TRUE; } @@ -3644,6 +3647,19 @@ _dev_l3_register_l3cds_set_one(NMDevice * self, return changed; } +static gboolean +_dev_l3_register_l3cds_set_one(NMDevice * self, + L3ConfigDataType l3cd_type, + const NML3ConfigData *l3cd, + NMTernary commit_sync) +{ + return _dev_l3_register_l3cds_set_one_full(self, + l3cd_type, + l3cd, + NM_L3CFG_CONFIG_FLAGS_NONE, + commit_sync); +} + static void _dev_l3_update_l3cds_ifindex(NMDevice *self) { @@ -3696,7 +3712,7 @@ _dev_l3_register_l3cds(NMDevice *self, } if (is_external) continue; - if (_dev_l3_register_l3cds_add_config(self, i)) + if (_dev_l3_register_l3cds_add_config(self, i, NM_L3CFG_CONFIG_FLAGS_NONE)) changed = TRUE; } @@ -9870,7 +9886,11 @@ _dev_ipdhcpx_handle_accept(NMDevice *self, int addr_family, const NML3ConfigData nm_assert(NM_IS_L3_CONFIG_DATA(l3cd)); nm_dhcp_config_set_lease(priv->ipdhcp_data_x[IS_IPv4].config, l3cd); - _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_DHCP_X(IS_IPv4), l3cd, TRUE); + _dev_l3_register_l3cds_set_one_full(self, + L3_CONFIG_DATA_TYPE_DHCP_X(IS_IPv4), + l3cd, + NM_L3CFG_CONFIG_FLAGS_FORCE_ONCE, + TRUE); /* FIXME(l3cfg:dhcp): accept also should be handled by NMDhcpClient transparently. * NMDhcpClient should do ACD (if enabled), and only after that passes, it exposes @@ -11074,7 +11094,11 @@ _dev_ipac6_ndisc_config_changed(NMNDisc * ndisc, _dev_ipac6_grace_period_start(self, 0, TRUE); - _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_AC_6, l3cd, FALSE); + _dev_l3_register_l3cds_set_one_full(self, + L3_CONFIG_DATA_TYPE_AC_6, + l3cd, + NM_L3CFG_CONFIG_FLAGS_FORCE_ONCE, + FALSE); nm_clear_l3cd(&priv->ipac6_data.l3cd); diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c index bf0f976252..c2afec8b7d 100644 --- a/src/core/nm-l3-config-data.c +++ b/src/core/nm-l3-config-data.c @@ -2906,6 +2906,7 @@ nm_l3_config_data_merge(NML3ConfigData * self, NML3ConfigMergeHookResult hook_result = { .ip4acd_not_ready = NM_OPTION_BOOL_DEFAULT, .assume_config_once = NM_OPTION_BOOL_DEFAULT, + .force_commit = NM_OPTION_BOOL_DEFAULT, }; #define _ensure_a() \ @@ -2944,6 +2945,12 @@ nm_l3_config_data_merge(NML3ConfigData * self, a.ax.a_assume_config_once = (!!hook_result.assume_config_once); } + if (hook_result.force_commit != NM_OPTION_BOOL_DEFAULT + && (!!hook_result.force_commit) != a_src->a_force_commit) { + _ensure_a(); + a.ax.a_force_commit = (!!hook_result.force_commit); + } + nm_l3_config_data_add_address_full(self, addr_family, a_src == &a.ax ? NULL : obj, @@ -2964,6 +2971,7 @@ nm_l3_config_data_merge(NML3ConfigData * self, NML3ConfigMergeHookResult hook_result = { .ip4acd_not_ready = NM_OPTION_BOOL_DEFAULT, .assume_config_once = NM_OPTION_BOOL_DEFAULT, + .force_commit = NM_OPTION_BOOL_DEFAULT, }; #define _ensure_r() \ @@ -2995,6 +3003,12 @@ nm_l3_config_data_merge(NML3ConfigData * self, r.rx.r_assume_config_once = (!!hook_result.assume_config_once); } + if (hook_result.force_commit != NM_OPTION_BOOL_DEFAULT + && (!!hook_result.force_commit) != r_src->r_force_commit) { + _ensure_r(); + r.rx.r_force_commit = (!!hook_result.force_commit); + } + if (!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_CLONE)) { if (r_src->table_any) { _ensure_r(); diff --git a/src/core/nm-l3-config-data.h b/src/core/nm-l3-config-data.h index dfaf18549a..b96317a89c 100644 --- a/src/core/nm-l3-config-data.h +++ b/src/core/nm-l3-config-data.h @@ -138,6 +138,7 @@ NML3ConfigData *nm_l3_config_data_new_from_platform(NMDedupMultiIndex * mu typedef struct { NMOptionBool ip4acd_not_ready; NMOptionBool assume_config_once; + NMOptionBool force_commit; } NML3ConfigMergeHookResult; typedef gboolean (*NML3ConfigMergeHookAddObj)(const NML3ConfigData * l3cd, diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index 4c1f8dc029..3c8b13ddba 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -204,6 +204,7 @@ typedef struct { guint32 acd_timeout_msec_confdata; NML3AcdDefendType acd_defend_type_confdata : 3; bool dirty_confdata : 1; + gboolean force_commit_once : 1; } L3ConfigData; /*****************************************************************************/ @@ -1014,9 +1015,11 @@ _obj_states_sync_filter(/* const NMDedupMultiObj * */ gconstpointer o, gpointer const ObjStatesSyncFilterData *sync_filter_data = user_data; NMPObjectType obj_type; ObjStateData * obj_state; + NML3Cfg * self; nm_assert(sync_filter_data); nm_assert(NM_IS_L3CFG(sync_filter_data->self)); + self = sync_filter_data->self; obj_type = NMP_OBJECT_GET_TYPE(obj); @@ -1031,15 +1034,12 @@ _obj_states_sync_filter(/* const NMDedupMultiObj * */ gconstpointer o, gpointer nm_assert(c_list_is_empty(&obj_state->os_zombie_lst)); if (!obj_state->os_nm_configured) { - NML3Cfg *self; - if (sync_filter_data->commit_type == NM_L3_CFG_COMMIT_TYPE_ASSUME && !_obj_state_data_get_assume_config_once(obj_state)) return FALSE; obj_state->os_nm_configured = TRUE; - self = sync_filter_data->self; _LOGD("obj-state: configure-first-time: %s", _obj_state_data_to_string(obj_state, sbuf, sizeof(sbuf))); return TRUE; @@ -1051,7 +1051,8 @@ _obj_states_sync_filter(/* const NMDedupMultiObj * */ gconstpointer o, gpointer return TRUE; } - if (!obj_state->os_plobj && sync_filter_data->commit_type != NM_L3_CFG_COMMIT_TYPE_REAPPLY) + if (!obj_state->os_plobj && sync_filter_data->commit_type != NM_L3_CFG_COMMIT_TYPE_REAPPLY + && !nmp_object_get_force_commit(obj)) return FALSE; return TRUE; @@ -3200,7 +3201,8 @@ nm_l3cfg_add_config(NML3Cfg * self, .acd_timeout_msec_confdata = acd_timeout_msec, .priority_confdata = priority, .pseudo_timestamp_confdata = ++self->priv.p->pseudo_timestamp_counter, - .dirty_confdata = FALSE, + .force_commit_once = NM_FLAGS_HAS(config_flags, NM_L3CFG_CONFIG_FLAGS_FORCE_ONCE), + .dirty_confdata = FALSE, }; changed = TRUE; } else { @@ -3345,6 +3347,7 @@ typedef struct { gconstpointer tag; bool assume_config_once; bool to_commit; + bool force_commit_once; } L3ConfigMergeHookAddObjData; static gboolean @@ -3363,8 +3366,10 @@ _l3_hook_add_obj_cb(const NML3ConfigData * l3cd, nm_assert(hook_result); nm_assert(hook_result->ip4acd_not_ready == NM_OPTION_BOOL_DEFAULT); nm_assert(hook_result->assume_config_once == NM_OPTION_BOOL_DEFAULT); + nm_assert(hook_result->force_commit == NM_OPTION_BOOL_DEFAULT); hook_result->assume_config_once = hook_data->assume_config_once; + hook_result->force_commit = hook_data->force_commit_once; switch (NMP_OBJECT_GET_TYPE(obj)) { case NMP_OBJECT_TYPE_IP4_ADDRESS: @@ -3501,6 +3506,7 @@ _l3cfg_update_combined_config(NML3Cfg * self, hook_data.tag = l3cd_data->tag_confdata; hook_data.assume_config_once = NM_FLAGS_HAS(l3cd_data->config_flags, NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE); + hook_data.force_commit_once = l3cd_data->force_commit_once; nm_l3_config_data_merge(l3cd, l3cd_data->l3cd, @@ -4116,6 +4122,7 @@ _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle) gboolean is_sticky_update = FALSE; char sbuf_ct[30]; gboolean changed_combined_l3cd; + guint i; g_return_if_fail(NM_IS_L3CFG(self)); nm_assert(NM_IN_SET(commit_type, @@ -4179,6 +4186,15 @@ _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle) _l3_acd_data_process_changes(self); + if (self->priv.p->l3_config_datas) { + for (i = 0; i < self->priv.p->l3_config_datas->len; i++) { + L3ConfigData *l3_config_data = _l3_config_datas_at(self->priv.p->l3_config_datas, i); + + if (l3_config_data->force_commit_once) + l3_config_data->force_commit_once = FALSE; + } + } + nm_assert(self->priv.p->commit_reentrant_count == 1); self->priv.p->commit_reentrant_count--; diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h index 35560142be..768794389c 100644 --- a/src/core/nm-l3cfg.h +++ b/src/core/nm-l3cfg.h @@ -53,11 +53,15 @@ typedef enum _nm_packed { * "don't change" behavior. At least once. If the address/route * is still not (no longer) configured on the subsequent * commit, it's not getting added again. + * @NM_L3CFG_CONFIG_FLAGS_FORCE_ONCE: if set, objects in the + * NML3ConfigData are committed to platform even if they were + * removed externally. */ typedef enum _nm_packed { NM_L3CFG_CONFIG_FLAGS_NONE = 0, NM_L3CFG_CONFIG_FLAGS_ONLY_FOR_ACD = (1LL << 0), NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE = (1LL << 1), + NM_L3CFG_CONFIG_FLAGS_FORCE_ONCE = (1LL << 2), } NML3CfgConfigFlags; typedef enum _nm_packed { diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index ee8748c631..e9403c6189 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -6251,6 +6251,7 @@ nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf " src %s" "%s" /* a_acd_not_ready */ "%s" /* a_assume_config_once */ + "%s" /* a_force_commit */ "", s_address, address->plen, @@ -6269,7 +6270,8 @@ nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf 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_assume_config_once ? " assume-config-once" : ""); + address->a_assume_config_once ? " assume-config-once" : "", + address->a_force_commit ? " force-commit" : ""); g_free(str_peer); return buf; } @@ -6390,6 +6392,7 @@ nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf len, "%s/%d lft %s pref %s%s%s%s%s src %s" "%s" /* a_assume_config_once */ + "%s" /* a_force_commit */ "", s_address, address->plen, @@ -6400,7 +6403,8 @@ nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf 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_assume_config_once ? " assume-config-once" : ""); + address->a_assume_config_once ? " assume-config-once" : "", + address->a_force_commit ? " force-commit" : ""); g_free(str_peer); return buf; } @@ -6492,6 +6496,7 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz "%s" /* initrwnd */ "%s" /* mtu */ "%s" /* r_assume_config_once */ + "%s" /* r_force_commit */ "", nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced), str_type), @@ -6548,7 +6553,8 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz route->lock_mtu ? "lock " : "", route->mtu) : "", - route->r_assume_config_once ? " assume-config-once" : ""); + route->r_assume_config_once ? " assume-config-once" : "", + route->r_force_commit ? " force-commit" : ""); return buf; } @@ -6619,6 +6625,7 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz "%s" /* mtu */ "%s" /* pref */ "%s" /* r_assume_config_once */ + "%s" /* r_force_commit */ "", nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced), str_type), @@ -6679,7 +6686,8 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz " pref %s", nm_icmpv6_router_pref_to_string(route->rt_pref, str_pref2, sizeof(str_pref2))) : "", - route->r_assume_config_once ? " assume-config-once" : ""); + route->r_assume_config_once ? " assume-config-once" : "", + route->r_force_commit ? " force-commit" : ""); return buf; } @@ -7815,7 +7823,8 @@ nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState NM_HASH_COMBINE_BOOLS(guint8, obj->use_ip4_broadcast_address, obj->a_acd_not_ready, - obj->a_assume_config_once)); + obj->a_assume_config_once, + obj->a_force_commit)); nm_hash_update_strarr(h, obj->label); } @@ -7838,23 +7847,25 @@ nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Ad NM_CMP_FIELD_STR(a, b, label); NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready); NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once); + NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); return 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_assume_config_once)); + 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_assume_config_once, obj->a_force_commit)); } int @@ -7875,6 +7886,7 @@ nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Ad NM_CMP_FIELD(a, b, preferred); NM_CMP_FIELD(a, b, n_ifa_flags); NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once); + NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); return 0; } @@ -7983,7 +7995,8 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->lock_initcwnd, obj->lock_initrwnd, obj->lock_mtu, - obj->r_assume_config_once)); + obj->r_assume_config_once, + obj->r_force_commit)); break; } } @@ -8075,6 +8088,7 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, NM_CMP_FIELD(a, b, mtu); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) { NM_CMP_FIELD_UNSAFE(a, b, r_assume_config_once); + NM_CMP_FIELD_UNSAFE(a, b, r_force_commit); } break; } @@ -8168,7 +8182,8 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, obj->lock_initcwnd, obj->lock_initrwnd, obj->lock_mtu, - obj->r_assume_config_once), + obj->r_assume_config_once, + obj->r_force_commit), obj->window, obj->cwnd, obj->initcwnd, @@ -8253,6 +8268,7 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, NM_CMP_FIELD(a, b, rt_pref); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) { NM_CMP_FIELD_UNSAFE(a, b, r_assume_config_once); + NM_CMP_FIELD_UNSAFE(a, b, r_force_commit); } break; } diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index 4667125f24..f3a4534ce7 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -320,10 +320,12 @@ typedef enum { \ bool use_ip4_broadcast_address : 1; \ \ - /* Whether the address is should be configured once during assume. This is a meta flag - * that is not honored by NMPlatform (netlink code). Instead, it can be used by the upper - * layers which use NMPlatformIPAddress to track addresses that should be configured. */ \ + /* 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. */ \ + /* Whether the address is should be configured once during assume. */ \ bool a_assume_config_once : 1; \ + bool a_force_commit : 1; \ \ guint8 plen; \ ; @@ -467,10 +469,13 @@ typedef union { * the "table_coerced" field is ignored (unlike for the metric). */ \ bool table_any : 1; \ \ - /* Whether the route is should be configured once during assume. This is a meta flag - * that is not honored by NMPlatform (netlink code). Instead, it can be used by the upper - * layers which use NMPlatformIPRoute to track routes that should be configured. */ \ + /* Meta flags not honored by NMPlatform (netlink code). Instead, they can be + * used by the upper layers which use NMPlatformIPRoute to track routes that + * should be configured. */ \ + /* Whether the route is should be configured once during assume. */ \ bool r_assume_config_once : 1; \ + /* Whether the route should be committed even if it was removed externally. */ \ + bool r_force_commit : 1; \ \ /* rtnh_flags * diff --git a/src/libnm-platform/nmp-object.h b/src/libnm-platform/nmp-object.h index bf140d7838..9a35014fbb 100644 --- a/src/libnm-platform/nmp-object.h +++ b/src/libnm-platform/nmp-object.h @@ -1112,6 +1112,21 @@ nmp_object_get_assume_config_once(const NMPObject *obj) } } +static inline gboolean +nmp_object_get_force_commit(const NMPObject *obj) +{ + switch (NMP_OBJECT_GET_TYPE(obj)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + return NMP_OBJECT_CAST_IP_ADDRESS(obj)->a_force_commit; + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + return NMP_OBJECT_CAST_IP_ROUTE(obj)->r_force_commit; + default: + return nm_assert_unreachable_val(FALSE); + } +} + static inline const char * nmp_object_link_get_ifname(const NMPObject *obj) {