From 04bc9e1cd3544445d883ad29ea108c1645c8e7b7 Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Mon, 4 Jun 2018 00:07:45 +0200 Subject: New upstream version 1.11.4 --- src/NetworkManagerUtils.c | 257 +- src/NetworkManagerUtils.h | 44 +- src/devices/adsl/meson.build | 34 + src/devices/adsl/nm-atm-manager.c | 2 +- src/devices/adsl/nm-device-adsl.c | 50 +- src/devices/bluetooth/meson.build | 45 + src/devices/bluetooth/nm-bluez-device.c | 6 +- src/devices/bluetooth/nm-bluez-manager.c | 4 - src/devices/bluetooth/nm-bluez4-adapter.c | 1 - src/devices/bluetooth/nm-bluez5-manager.c | 2 +- src/devices/bluetooth/nm-device-bt.c | 77 +- src/devices/meson.build | 22 + src/devices/nm-acd-manager.c | 493 +++ src/devices/nm-acd-manager.h | 43 + src/devices/nm-arping-manager.c | 474 --- src/devices/nm-arping-manager.h | 43 - src/devices/nm-device-bond.c | 30 +- src/devices/nm-device-bridge.c | 26 +- src/devices/nm-device-dummy.c | 25 +- src/devices/nm-device-ethernet.c | 93 +- src/devices/nm-device-ethernet.h | 18 +- src/devices/nm-device-factory.c | 69 +- src/devices/nm-device-generic.c | 23 +- src/devices/nm-device-infiniband.c | 27 +- src/devices/nm-device-ip-tunnel.c | 78 +- src/devices/nm-device-ip-tunnel.h | 4 +- src/devices/nm-device-macsec.c | 60 +- src/devices/nm-device-macsec.h | 3 - src/devices/nm-device-macvlan.c | 43 +- src/devices/nm-device-macvlan.h | 3 - src/devices/nm-device-ppp.c | 95 +- src/devices/nm-device-private.h | 8 +- src/devices/nm-device-tun.c | 356 +- src/devices/nm-device-veth.c | 41 +- src/devices/nm-device-vlan.c | 38 +- src/devices/nm-device-vlan.h | 4 - src/devices/nm-device-vxlan.c | 110 +- src/devices/nm-device-vxlan.h | 3 - src/devices/nm-device.c | 3799 ++++++++++++-------- src/devices/nm-device.h | 48 +- src/devices/nm-lldp-listener.c | 45 +- src/devices/ovs/meson.build | 38 + src/devices/ovs/nm-device-ovs-bridge.c | 21 +- src/devices/ovs/nm-device-ovs-interface.c | 19 +- src/devices/ovs/nm-device-ovs-port.c | 22 +- src/devices/ovs/nm-ovsdb.c | 52 +- src/devices/team/meson.build | 35 + src/devices/team/nm-device-team.c | 29 +- src/devices/tests/meson.build | 18 + src/devices/tests/test-acd.c | 185 + src/devices/tests/test-arping.c | 164 - src/devices/tests/test-lldp.c | 53 +- src/devices/wifi/meson.build | 51 + src/devices/wifi/nm-device-iwd.c | 1921 ++++++++++ src/devices/wifi/nm-device-iwd.h | 62 + src/devices/wifi/nm-device-olpc-mesh.c | 39 +- src/devices/wifi/nm-device-wifi.c | 386 +- src/devices/wifi/nm-device-wifi.h | 12 +- src/devices/wifi/nm-iwd-manager.c | 693 ++++ src/devices/wifi/nm-iwd-manager.h | 65 + src/devices/wifi/nm-wifi-ap.c | 205 +- src/devices/wifi/nm-wifi-ap.h | 21 +- src/devices/wifi/nm-wifi-common.c | 206 ++ src/devices/wifi/nm-wifi-common.h | 37 + src/devices/wifi/nm-wifi-factory.c | 29 +- src/devices/wifi/nm-wifi-utils.c | 32 + src/devices/wifi/nm-wifi-utils.h | 2 + src/devices/wifi/tests/meson.build | 13 + src/devices/wwan/libnm-wwan.ver | 2 +- src/devices/wwan/meson.build | 75 + src/devices/wwan/nm-device-modem.c | 96 +- src/devices/wwan/nm-modem-broadband.c | 69 +- src/devices/wwan/nm-modem-ofono.c | 27 +- src/devices/wwan/nm-modem.c | 356 +- src/devices/wwan/nm-modem.h | 19 +- src/devices/wwan/nm-wwan-factory.c | 9 +- src/dhcp/meson.build | 22 + src/dhcp/nm-dhcp-client.c | 202 +- src/dhcp/nm-dhcp-client.h | 52 +- src/dhcp/nm-dhcp-dhclient-utils.c | 331 +- src/dhcp/nm-dhcp-dhclient-utils.h | 15 +- src/dhcp/nm-dhcp-dhclient.c | 191 +- src/dhcp/nm-dhcp-dhcpcanon.c | 16 +- src/dhcp/nm-dhcp-dhcpcd.c | 17 +- src/dhcp/nm-dhcp-helper.c | 2 - src/dhcp/nm-dhcp-listener.c | 86 +- src/dhcp/nm-dhcp-manager.c | 120 +- src/dhcp/nm-dhcp-manager.h | 15 +- src/dhcp/nm-dhcp-systemd.c | 120 +- src/dhcp/nm-dhcp-utils.c | 8 +- src/dhcp/nm-dhcp-utils.h | 2 +- src/dhcp/tests/leases/basic.leases | 31 - src/dhcp/tests/leases/malformed1.leases | 15 - src/dhcp/tests/leases/malformed2.leases | 15 - src/dhcp/tests/leases/malformed3.leases | 15 - src/dhcp/tests/meson.build | 19 + src/dhcp/tests/test-dhcp-dhclient.c | 322 +- src/dhcp/tests/test-dhcp-utils.c | 21 +- src/dns/nm-dns-dnsmasq.c | 317 +- src/dns/nm-dns-manager.c | 1017 ++++-- src/dns/nm-dns-manager.h | 37 +- src/dns/nm-dns-plugin.c | 4 +- src/dns/nm-dns-plugin.h | 4 +- src/dns/nm-dns-systemd-resolved.c | 295 +- src/dns/nm-dns-unbound.c | 2 +- src/dnsmasq/nm-dnsmasq-manager.c | 2 +- src/dnsmasq/tests/meson.build | 13 + src/main.c | 48 +- src/meson.build | 319 ++ src/ndisc/nm-lndp-ndisc.c | 52 +- src/ndisc/nm-ndisc.c | 55 +- src/ndisc/nm-ndisc.h | 33 +- src/ndisc/tests/meson.build | 23 + src/nm-act-request.c | 8 +- src/nm-active-connection.c | 417 ++- src/nm-active-connection.h | 15 +- src/nm-audit-manager.c | 3 +- src/nm-audit-manager.h | 1 + src/nm-auth-manager.c | 623 ++-- src/nm-auth-manager.h | 62 +- src/nm-auth-subject.c | 39 +- src/nm-auth-subject.h | 9 - src/nm-auth-utils.c | 489 ++- src/nm-auth-utils.h | 25 +- src/nm-bus-manager.c | 1024 ------ src/nm-bus-manager.h | 93 - src/nm-checkpoint-manager.c | 326 +- src/nm-checkpoint-manager.h | 24 +- src/nm-checkpoint.c | 288 +- src/nm-checkpoint.h | 29 +- src/nm-config-data.c | 308 +- src/nm-config-data.h | 26 +- src/nm-config.c | 24 +- src/nm-config.h | 3 +- src/nm-connectivity.c | 589 +-- src/nm-connectivity.h | 29 +- src/nm-core-utils.c | 456 +-- src/nm-core-utils.h | 46 +- src/nm-dbus-manager.c | 1673 +++++++++ src/nm-dbus-manager.h | 113 + src/nm-dbus-object.c | 309 ++ src/nm-dbus-object.h | 198 + src/nm-dbus-utils.c | 312 ++ src/nm-dbus-utils.h | 213 ++ src/nm-dhcp4-config.c | 51 +- src/nm-dhcp6-config.c | 51 +- src/nm-dispatcher.c | 19 +- src/nm-dispatcher.h | 1 - src/nm-exported-object.c | 1055 ------ src/nm-exported-object.h | 85 - src/nm-firewall-manager.c | 4 +- src/nm-iface-helper.c | 121 +- src/nm-ip4-config.c | 278 +- src/nm-ip4-config.h | 222 +- src/nm-ip6-config.c | 258 +- src/nm-ip6-config.h | 12 +- src/nm-logging.c | 7 +- src/nm-logging.h | 14 +- src/nm-manager.c | 2895 +++++++++------ src/nm-manager.h | 37 +- src/nm-netns.c | 4 - src/nm-pacrunner-manager.c | 16 +- src/nm-policy.c | 185 +- src/nm-rfkill-manager.c | 2 +- src/nm-session-monitor.c | 4 +- src/nm-sleep-monitor.c | 1 - src/nm-test-utils-core.h | 34 +- src/nm-types.h | 28 +- src/org.freedesktop.NetworkManager.conf | 2 +- src/platform/nm-fake-platform.c | 1 - src/platform/nm-linux-platform.c | 784 ++-- src/platform/nm-netlink.c | 1491 ++++++++ src/platform/nm-netlink.h | 511 +++ src/platform/nm-platform-utils.c | 3 + src/platform/nm-platform-utils.h | 3 - src/platform/nm-platform.c | 689 +++- src/platform/nm-platform.h | 120 +- src/platform/nmp-netns.c | 4 +- src/platform/nmp-object.c | 95 +- src/platform/nmp-object.h | 31 +- src/platform/tests/meson.build | 37 + src/platform/tests/monitor.c | 3 +- src/platform/tests/test-address.c | 5 +- src/platform/tests/test-cleanup.c | 3 +- src/platform/tests/test-common.c | 122 +- src/platform/tests/test-common.h | 30 +- src/platform/tests/test-general.c | 1 - src/platform/tests/test-link.c | 147 +- src/platform/tests/test-nmp-object.c | 27 +- src/platform/tests/test-route.c | 3 +- src/platform/wifi/wifi-utils-nl80211.c | 380 +- src/platform/wifi/wifi-utils-private.h | 14 +- src/platform/wifi/wifi-utils-wext.c | 42 +- src/platform/wifi/wifi-utils.c | 61 +- src/platform/wifi/wifi-utils.h | 3 +- src/ppp/meson.build | 41 + src/ppp/nm-ppp-manager-call.c | 36 +- src/ppp/nm-ppp-manager-call.h | 14 +- src/ppp/nm-ppp-manager.c | 564 +-- src/ppp/nm-ppp-manager.h | 8 + src/ppp/nm-ppp-plugin-api.h | 14 +- src/ppp/nm-pppd-plugin.c | 32 +- src/settings/nm-agent-manager.c | 160 +- src/settings/nm-agent-manager.h | 2 +- src/settings/nm-secret-agent.c | 140 +- src/settings/nm-settings-connection.c | 673 ++-- src/settings/nm-settings-connection.h | 62 +- src/settings/nm-settings-plugin.c | 24 - src/settings/nm-settings-plugin.h | 23 - src/settings/nm-settings.c | 1013 +++--- src/settings/nm-settings.h | 11 +- src/settings/plugins/ibft/meson.build | 48 + src/settings/plugins/ibft/nms-ibft-plugin.c | 35 - src/settings/plugins/ibft/nms-ibft-reader.c | 8 +- src/settings/plugins/ibft/tests/meson.build | 17 + src/settings/plugins/ibft/tests/test-ibft.c | 22 +- src/settings/plugins/ifcfg-rh/meson.build | 77 + src/settings/plugins/ifcfg-rh/nm-inotify-helper.c | 2 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-common.h | 3 - .../plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c | 172 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 176 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 68 +- src/settings/plugins/ifcfg-rh/shvar.c | 4 +- src/settings/plugins/ifcfg-rh/tests/meson.build | 17 + .../ifcfg-Test_Write_WiFi_Hidden.cexpected | 1 - .../ifcfg-Test_Write_Wired_Static_Routes.cexpected | 2 + .../ifcfg-rh/tests/network-scripts/ifcfg-test-tc | 16 + .../network-scripts/ifcfg-test-tc-write.cexpected | 16 + .../network-scripts/ifcfg-test-wired-ipv4-manual-1 | 2 + .../network-scripts/ifcfg-test-wired-ipv4-manual-2 | 2 + .../network-scripts/ifcfg-test-wired-ipv4-manual-3 | 2 + .../network-scripts/ifcfg-test-wired-ipv4-manual-4 | 2 + .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 683 ++-- .../plugins/ifnet/nms-ifnet-connection-parser.c | 2912 --------------- .../plugins/ifnet/nms-ifnet-connection-parser.h | 55 - src/settings/plugins/ifnet/nms-ifnet-connection.c | 233 -- src/settings/plugins/ifnet/nms-ifnet-connection.h | 46 - src/settings/plugins/ifnet/nms-ifnet-net-parser.c | 734 ---- src/settings/plugins/ifnet/nms-ifnet-net-parser.h | 42 - src/settings/plugins/ifnet/nms-ifnet-net-utils.c | 830 ----- src/settings/plugins/ifnet/nms-ifnet-net-utils.h | 71 - src/settings/plugins/ifnet/nms-ifnet-plugin.c | 524 --- src/settings/plugins/ifnet/nms-ifnet-plugin.h | 38 - src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c | 584 --- src/settings/plugins/ifnet/nms-ifnet-wpa-parser.h | 41 - src/settings/plugins/ifnet/tests/net | 158 - src/settings/plugins/ifnet/tests/net.all | 864 ----- .../plugins/ifnet/tests/nm-system-settings.conf | 5 - src/settings/plugins/ifnet/tests/test-ifnet.c | 396 -- src/settings/plugins/ifnet/tests/test_ca_cert.pem | 27 - .../plugins/ifnet/tests/wpa_supplicant.conf | 70 - src/settings/plugins/ifupdown/meson.build | 53 + .../ifupdown/nms-ifupdown-interface-parser.c | 7 +- .../ifupdown/nms-ifupdown-interface-parser.h | 1 - .../plugins/ifupdown/nms-ifupdown-parser.c | 7 +- .../plugins/ifupdown/nms-ifupdown-plugin.c | 38 - src/settings/plugins/ifupdown/tests/meson.build | 15 + .../plugins/ifupdown/tests/test-ifupdown.c | 46 +- src/settings/plugins/keyfile/nms-keyfile-plugin.c | 37 +- src/settings/plugins/keyfile/nms-keyfile-utils.h | 3 - .../keyfile/tests/keyfiles/Test_Wired_Connection | 10 +- src/settings/plugins/keyfile/tests/meson.build | 16 + src/settings/plugins/keyfile/tests/test-keyfile.c | 96 +- src/settings/plugins/meson.build | 15 + src/supplicant/nm-supplicant-config.c | 82 +- src/supplicant/nm-supplicant-config.h | 3 +- src/supplicant/nm-supplicant-interface.c | 42 +- src/supplicant/nm-supplicant-interface.h | 8 +- src/supplicant/nm-supplicant-manager.c | 15 +- src/supplicant/nm-supplicant-settings-verify.c | 16 +- src/supplicant/nm-supplicant-settings-verify.h | 1 - src/supplicant/nm-supplicant-types.h | 6 +- src/supplicant/tests/meson.build | 14 + src/supplicant/tests/test-supplicant-config.c | 202 +- src/systemd/meson.build | 71 + src/systemd/nm-sd.c | 1 - src/systemd/sd-adapt/device-nodes.h | 3 + src/systemd/sd-adapt/errno-list.h | 3 + src/systemd/sd-adapt/locale-util.h | 3 + src/systemd/sd-adapt/memfd-util.h | 3 + src/systemd/sd-adapt/nm-sd-adapt.h | 26 +- src/systemd/sd-adapt/procfs-util.h | 3 + src/systemd/sd-adapt/terminal-util.h | 3 + src/systemd/sd-adapt/unaligned.h | 3 + src/systemd/src/basic/alloc-util.c | 16 +- src/systemd/src/basic/alloc-util.h | 41 +- src/systemd/src/basic/async.h | 16 +- src/systemd/src/basic/escape.c | 18 +- src/systemd/src/basic/escape.h | 14 +- src/systemd/src/basic/ether-addr-util.c | 59 +- src/systemd/src/basic/ether-addr-util.h | 25 +- src/systemd/src/basic/extract-word.c | 17 +- src/systemd/src/basic/extract-word.h | 14 +- src/systemd/src/basic/fd-util.c | 441 ++- src/systemd/src/basic/fd-util.h | 60 +- src/systemd/src/basic/fileio.c | 185 +- src/systemd/src/basic/fileio.h | 28 +- src/systemd/src/basic/fs-util.c | 581 ++- src/systemd/src/basic/fs-util.h | 47 +- src/systemd/src/basic/hash-funcs.c | 69 +- src/systemd/src/basic/hash-funcs.h | 37 +- src/systemd/src/basic/hashmap.c | 176 +- src/systemd/src/basic/hashmap.h | 54 +- src/systemd/src/basic/hexdecoct.c | 298 +- src/systemd/src/basic/hexdecoct.h | 14 +- src/systemd/src/basic/hostname-util.c | 116 +- src/systemd/src/basic/hostname-util.h | 20 +- src/systemd/src/basic/in-addr-util.c | 16 +- src/systemd/src/basic/in-addr-util.h | 14 +- src/systemd/src/basic/io-util.c | 26 +- src/systemd/src/basic/io-util.h | 23 +- src/systemd/src/basic/list.h | 17 +- src/systemd/src/basic/log.h | 96 +- src/systemd/src/basic/macro.h | 72 +- src/systemd/src/basic/mempool.c | 25 +- src/systemd/src/basic/mempool.h | 16 +- src/systemd/src/basic/parse-util.c | 169 +- src/systemd/src/basic/parse-util.h | 37 +- src/systemd/src/basic/path-util.c | 121 +- src/systemd/src/basic/path-util.h | 48 +- src/systemd/src/basic/prioq.c | 16 +- src/systemd/src/basic/prioq.h | 14 +- src/systemd/src/basic/process-util.c | 528 ++- src/systemd/src/basic/process-util.h | 100 +- src/systemd/src/basic/random-util.c | 22 +- src/systemd/src/basic/random-util.h | 14 +- src/systemd/src/basic/refcnt.h | 14 +- src/systemd/src/basic/set.h | 28 +- src/systemd/src/basic/signal-util.h | 33 +- src/systemd/src/basic/siphash24.h | 53 + src/systemd/src/basic/socket-util.c | 177 +- src/systemd/src/basic/socket-util.h | 32 +- src/systemd/src/basic/sparse-endian.h | 4 +- src/systemd/src/basic/stat-util.c | 292 ++ src/systemd/src/basic/stat-util.h | 67 + src/systemd/src/basic/stdio-util.h | 20 +- src/systemd/src/basic/string-table.c | 14 +- src/systemd/src/basic/string-table.h | 23 +- src/systemd/src/basic/string-util.c | 304 +- src/systemd/src/basic/string-util.h | 42 +- src/systemd/src/basic/strv.c | 107 +- src/systemd/src/basic/strv.h | 35 +- src/systemd/src/basic/time-util.c | 106 +- src/systemd/src/basic/time-util.h | 22 +- src/systemd/src/basic/umask-util.h | 14 +- src/systemd/src/basic/utf8.c | 56 +- src/systemd/src/basic/utf8.h | 17 +- src/systemd/src/basic/util.c | 395 +- src/systemd/src/basic/util.h | 54 +- src/systemd/src/libsystemd-network/arp-util.c | 24 +- src/systemd/src/libsystemd-network/arp-util.h | 14 +- .../src/libsystemd-network/dhcp-identifier.c | 18 +- .../src/libsystemd-network/dhcp-identifier.h | 14 +- src/systemd/src/libsystemd-network/dhcp-internal.h | 14 +- .../src/libsystemd-network/dhcp-lease-internal.h | 16 +- src/systemd/src/libsystemd-network/dhcp-network.c | 29 +- src/systemd/src/libsystemd-network/dhcp-option.c | 53 +- src/systemd/src/libsystemd-network/dhcp-packet.c | 14 +- src/systemd/src/libsystemd-network/dhcp-protocol.h | 14 +- .../src/libsystemd-network/dhcp6-internal.h | 80 +- .../src/libsystemd-network/dhcp6-lease-internal.h | 16 +- src/systemd/src/libsystemd-network/dhcp6-network.c | 18 +- src/systemd/src/libsystemd-network/dhcp6-option.c | 360 +- .../src/libsystemd-network/dhcp6-protocol.h | 21 +- src/systemd/src/libsystemd-network/lldp-internal.h | 14 +- src/systemd/src/libsystemd-network/lldp-neighbor.c | 18 +- src/systemd/src/libsystemd-network/lldp-neighbor.h | 14 +- src/systemd/src/libsystemd-network/lldp-network.c | 19 +- src/systemd/src/libsystemd-network/lldp-network.h | 14 +- .../src/libsystemd-network/network-internal.c | 149 +- .../src/libsystemd-network/network-internal.h | 26 +- .../src/libsystemd-network/sd-dhcp-client.c | 117 +- src/systemd/src/libsystemd-network/sd-dhcp-lease.c | 58 +- .../src/libsystemd-network/sd-dhcp6-client.c | 308 +- .../src/libsystemd-network/sd-dhcp6-lease.c | 67 +- src/systemd/src/libsystemd-network/sd-ipv4acd.c | 23 +- src/systemd/src/libsystemd-network/sd-ipv4ll.c | 18 +- src/systemd/src/libsystemd-network/sd-lldp.c | 25 +- src/systemd/src/libsystemd/sd-event/sd-event.c | 93 +- src/systemd/src/libsystemd/sd-id128/id128-util.c | 26 +- src/systemd/src/libsystemd/sd-id128/id128-util.h | 14 +- src/systemd/src/libsystemd/sd-id128/sd-id128.c | 14 +- src/systemd/src/shared/dns-domain.c | 87 +- src/systemd/src/shared/dns-domain.h | 15 +- src/systemd/src/systemd/_sd-common.h | 1 + src/systemd/src/systemd/sd-dhcp-client.h | 10 + src/systemd/src/systemd/sd-dhcp-lease.h | 1 + src/systemd/src/systemd/sd-dhcp6-client.h | 11 + src/systemd/src/systemd/sd-dhcp6-lease.h | 6 + src/systemd/src/systemd/sd-event.h | 6 + src/systemd/src/systemd/sd-id128.h | 1 + src/systemd/src/systemd/sd-ipv4acd.h | 1 + src/systemd/src/systemd/sd-ipv4ll.h | 1 + src/systemd/src/systemd/sd-lldp.h | 1 + src/systemd/src/systemd/sd-ndisc.h | 1 + src/tests/config/meson.build | 21 + src/tests/config/nm-test-device.c | 2 +- src/tests/config/test-config.c | 99 +- src/tests/meson.build | 47 + src/tests/test-general-with-expect.c | 127 +- src/tests/test-general.c | 30 +- src/tests/test-ip4-config.c | 6 +- src/tests/test-ip6-config.c | 24 +- src/tests/test-resolvconf-capture.c | 298 -- src/vpn/nm-vpn-connection.c | 102 +- src/vpn/nm-vpn-connection.h | 1 - 406 files changed, 29271 insertions(+), 24990 deletions(-) create mode 100644 src/devices/adsl/meson.build create mode 100644 src/devices/bluetooth/meson.build create mode 100644 src/devices/meson.build create mode 100644 src/devices/nm-acd-manager.c create mode 100644 src/devices/nm-acd-manager.h delete mode 100644 src/devices/nm-arping-manager.c delete mode 100644 src/devices/nm-arping-manager.h create mode 100644 src/devices/ovs/meson.build create mode 100644 src/devices/team/meson.build create mode 100644 src/devices/tests/meson.build create mode 100644 src/devices/tests/test-acd.c delete mode 100644 src/devices/tests/test-arping.c create mode 100644 src/devices/wifi/meson.build create mode 100644 src/devices/wifi/nm-device-iwd.c create mode 100644 src/devices/wifi/nm-device-iwd.h create mode 100644 src/devices/wifi/nm-iwd-manager.c create mode 100644 src/devices/wifi/nm-iwd-manager.h create mode 100644 src/devices/wifi/nm-wifi-common.c create mode 100644 src/devices/wifi/nm-wifi-common.h create mode 100644 src/devices/wifi/tests/meson.build create mode 100644 src/devices/wwan/meson.build create mode 100644 src/dhcp/meson.build delete mode 100644 src/dhcp/tests/leases/basic.leases delete mode 100644 src/dhcp/tests/leases/malformed1.leases delete mode 100644 src/dhcp/tests/leases/malformed2.leases delete mode 100644 src/dhcp/tests/leases/malformed3.leases create mode 100644 src/dhcp/tests/meson.build create mode 100644 src/dnsmasq/tests/meson.build create mode 100644 src/meson.build create mode 100644 src/ndisc/tests/meson.build delete mode 100644 src/nm-bus-manager.c delete mode 100644 src/nm-bus-manager.h create mode 100644 src/nm-dbus-manager.c create mode 100644 src/nm-dbus-manager.h create mode 100644 src/nm-dbus-object.c create mode 100644 src/nm-dbus-object.h create mode 100644 src/nm-dbus-utils.c create mode 100644 src/nm-dbus-utils.h delete mode 100644 src/nm-exported-object.c delete mode 100644 src/nm-exported-object.h create mode 100644 src/platform/nm-netlink.c create mode 100644 src/platform/nm-netlink.h create mode 100644 src/platform/tests/meson.build create mode 100644 src/ppp/meson.build create mode 100644 src/settings/plugins/ibft/meson.build create mode 100644 src/settings/plugins/ibft/tests/meson.build create mode 100644 src/settings/plugins/ifcfg-rh/meson.build create mode 100644 src/settings/plugins/ifcfg-rh/tests/meson.build create mode 100644 src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc create mode 100644 src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected delete mode 100644 src/settings/plugins/ifnet/nms-ifnet-connection-parser.c delete mode 100644 src/settings/plugins/ifnet/nms-ifnet-connection-parser.h delete mode 100644 src/settings/plugins/ifnet/nms-ifnet-connection.c delete mode 100644 src/settings/plugins/ifnet/nms-ifnet-connection.h delete mode 100644 src/settings/plugins/ifnet/nms-ifnet-net-parser.c delete mode 100644 src/settings/plugins/ifnet/nms-ifnet-net-parser.h delete mode 100644 src/settings/plugins/ifnet/nms-ifnet-net-utils.c delete mode 100644 src/settings/plugins/ifnet/nms-ifnet-net-utils.h delete mode 100644 src/settings/plugins/ifnet/nms-ifnet-plugin.c delete mode 100644 src/settings/plugins/ifnet/nms-ifnet-plugin.h delete mode 100644 src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c delete mode 100644 src/settings/plugins/ifnet/nms-ifnet-wpa-parser.h delete mode 100644 src/settings/plugins/ifnet/tests/net delete mode 100644 src/settings/plugins/ifnet/tests/net.all delete mode 100644 src/settings/plugins/ifnet/tests/nm-system-settings.conf delete mode 100644 src/settings/plugins/ifnet/tests/test-ifnet.c delete mode 100644 src/settings/plugins/ifnet/tests/test_ca_cert.pem delete mode 100644 src/settings/plugins/ifnet/tests/wpa_supplicant.conf create mode 100644 src/settings/plugins/ifupdown/meson.build create mode 100644 src/settings/plugins/ifupdown/tests/meson.build create mode 100644 src/settings/plugins/keyfile/tests/meson.build create mode 100644 src/settings/plugins/meson.build create mode 100644 src/supplicant/tests/meson.build create mode 100644 src/systemd/meson.build create mode 100644 src/systemd/sd-adapt/device-nodes.h create mode 100644 src/systemd/sd-adapt/errno-list.h create mode 100644 src/systemd/sd-adapt/locale-util.h create mode 100644 src/systemd/sd-adapt/memfd-util.h create mode 100644 src/systemd/sd-adapt/procfs-util.h create mode 100644 src/systemd/sd-adapt/terminal-util.h create mode 100644 src/systemd/sd-adapt/unaligned.h create mode 100644 src/systemd/src/basic/siphash24.h create mode 100644 src/systemd/src/basic/stat-util.c create mode 100644 src/systemd/src/basic/stat-util.h create mode 100644 src/tests/config/meson.build create mode 100644 src/tests/meson.build delete mode 100644 src/tests/test-resolvconf-capture.c (limited to 'src') diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 89bd357f..a3bd6013 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -23,6 +23,8 @@ #include "NetworkManagerUtils.h" +#include "nm-utils/nm-c-list.h" + #include "nm-common-macros.h" #include "nm-utils.h" #include "nm-setting-connection.h" @@ -31,7 +33,6 @@ #include "nm-core-internal.h" #include "platform/nm-platform.h" -#include "nm-exported-object.h" #include "nm-auth-utils.h" /*****************************************************************************/ @@ -68,98 +69,93 @@ nm_utils_get_shared_wifi_permission (NMConnection *connection) /*****************************************************************************/ static char * -get_new_connection_name (const GSList *existing, +get_new_connection_name (NMConnection *const*existing_connections, const char *preferred, const char *fallback_prefix) { - GSList *names = NULL; - const GSList *iter; - char *cname = NULL; - int i = 0; - gboolean preferred_found = FALSE; + gs_free const char **existing_names = NULL; + guint i, existing_len = 0; g_assert (fallback_prefix); - for (iter = existing; iter; iter = g_slist_next (iter)) { - NMConnection *candidate = NM_CONNECTION (iter->data); - const char *id; + if (existing_connections) { + existing_len = NM_PTRARRAY_LEN (existing_connections); + existing_names = g_new (const char *, existing_len); + for (i = 0; i < existing_len; i++) { + NMConnection *candidate; + const char *id; + + candidate = existing_connections[i]; + nm_assert (NM_IS_CONNECTION (candidate)); + + id = nm_connection_get_id (candidate); + nm_assert (id); - id = nm_connection_get_id (candidate); - g_assert (id); - names = g_slist_append (names, (gpointer) id); + existing_names[i] = id; - if (preferred && !preferred_found && (strcmp (preferred, id) == 0)) - preferred_found = TRUE; + if ( preferred + && nm_streq (preferred, id)) { + /* the preferred name is already taken. Forget about it. */ + preferred = NULL; + } + } + nm_assert (!existing_connections[i]); } /* Return the preferred name if it was unique */ - if (preferred && !preferred_found) { - g_slist_free (names); + if (preferred) return g_strdup (preferred); - } /* Otherwise find the next available unique connection name using the given * connection name template. */ - while (!cname && (i++ < 10000)) { + for (i = 1; TRUE; i++) { char *temp; - gboolean found = FALSE; - /* Translators: the first %s is a prefix for the connection id, such + /* TRANSLATORS: the first %s is a prefix for the connection id, such * as "Wired Connection" or "VPN Connection". The %d is a number * that is combined with the first argument to create a unique * connection id. */ - temp = g_strdup_printf (C_("connection id fallback", "%s %d"), + temp = g_strdup_printf (C_("connection id fallback", "%s %u"), fallback_prefix, i); - for (iter = names; iter; iter = g_slist_next (iter)) { - if (!strcmp (iter->data, temp)) { - found = TRUE; - break; - } - } - if (!found) - cname = temp; - else - g_free (temp); - } - g_slist_free (names); - return cname; + if (nm_utils_strv_find_first ((char **) existing_names, + existing_len, + temp) < 0) + return temp; + + g_free (temp); + } } static char * get_new_connection_ifname (NMPlatform *platform, - const GSList *existing, + NMConnection *const*existing_connections, const char *prefix) { - int i; - char *name; - const GSList *iter; - gboolean found; + guint i, j; + + for (i = 0; TRUE; i++) { + char *name; - for (i = 0; i < 500; i++) { name = g_strdup_printf ("%s%d", prefix, i); if (nm_platform_link_get_by_ifname (platform, name)) goto next; - for (iter = existing, found = FALSE; iter; iter = g_slist_next (iter)) { - NMConnection *candidate = iter->data; - - if (g_strcmp0 (nm_connection_get_interface_name (candidate), name) == 0) { - found = TRUE; - break; + if (existing_connections) { + for (j = 0; existing_connections[j]; j++) { + if (nm_streq0 (nm_connection_get_interface_name (existing_connections[j]), + name)) + goto next; } } - if (!found) - return name; + return name; - next: +next: g_free (name); } - - return NULL; } const char * @@ -251,7 +247,7 @@ void nm_utils_complete_generic (NMPlatform *platform, NMConnection *connection, const char *ctype, - const GSList *existing, + NMConnection *const*existing_connections, const char *preferred_id, const char *fallback_id_prefix, const char *ifname_prefix, @@ -278,14 +274,14 @@ nm_utils_complete_generic (NMPlatform *platform, /* Add a connection ID if absent */ if (!nm_setting_connection_get_id (s_con)) { - id = get_new_connection_name (existing, preferred_id, fallback_id_prefix); + id = get_new_connection_name (existing_connections, preferred_id, fallback_id_prefix); g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_ID, id, NULL); g_free (id); } /* Add an interface name, if requested */ if (ifname_prefix && !nm_setting_connection_get_interface_name (s_con)) { - ifname = get_new_connection_ifname (platform, existing, ifname_prefix); + ifname = get_new_connection_ifname (platform, existing_connections, ifname_prefix); g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, NULL); g_free (ifname); } @@ -880,58 +876,123 @@ nm_utils_match_connection (NMConnection *const*connections, /*****************************************************************************/ -/** - * nm_utils_g_value_set_object_path: - * @value: a #GValue, initialized to store an object path - * @object: (allow-none): an #NMExportedObject - * - * Sets @value to @object's object path. If @object is %NULL, or not - * exported, @value is set to "/". - */ -void -nm_utils_g_value_set_object_path (GValue *value, gpointer object) +int +nm_match_spec_device_by_pllink (const NMPlatformLink *pllink, + const char *match_device_type, + const GSList *specs, + int no_match_value) { - g_return_if_fail (!object || NM_IS_EXPORTED_OBJECT (object)); + NMMatchSpecMatchType m; + + /* we can only match by certain properties that are available on the + * platform link (and even @pllink might be missing. + * + * It's still useful because of specs like "*" and "except:interface-name:eth0", + * which match even in that case. */ + m = nm_match_spec_device (specs, + pllink ? pllink->name : NULL, + match_device_type, + pllink ? pllink->driver : NULL, + NULL, + NULL, + NULL); + + switch (m) { + case NM_MATCH_SPEC_MATCH: + return TRUE; + case NM_MATCH_SPEC_NEG_MATCH: + return FALSE; + case NM_MATCH_SPEC_NO_MATCH: + return no_match_value; + } + nm_assert_not_reached (); + return no_match_value; +} - if (object && nm_exported_object_is_exported (object)) - g_value_set_string (value, nm_exported_object_get_path (object)); - else - g_value_set_string (value, "/"); +/*****************************************************************************/ + +struct _NMShutdownWaitObjHandle { + CList lst; + GObject *watched_obj; + const char *msg_reason; +}; + +static CList _shutdown_waitobj_lst_head; + +static void +_shutdown_waitobj_unregister (NMShutdownWaitObjHandle *handle) +{ + c_list_unlink_stale (&handle->lst); + g_slice_free (NMShutdownWaitObjHandle, handle); + + /* FIXME(shutdown): check whether the object list is empty, and + * signal shutdown-complete */ +} + +static void +_shutdown_waitobj_cb (gpointer user_data, + GObject *where_the_object_was) +{ + NMShutdownWaitObjHandle *handle = user_data; + + nm_assert (handle); + nm_assert (handle->watched_obj == where_the_object_was); + _shutdown_waitobj_unregister (handle); } /** - * nm_utils_g_value_set_object_path_array: - * @value: a #GValue, initialized to store an object path - * @objects: a #GSList of #NMExportedObjects - * @filter_func: (allow-none): function to call on each object in @objects - * @user_data: data to pass to @filter_func + * _nm_shutdown_wait_obj_register: + * @watched_obj: the object to watch. Takes a weak reference on the object + * to be notified when it gets destroyed. + * @msg_reason: a reason message, for debugging and logging purposes. It + * must be a static string. Or at least, be alive at least as long as + * @watched_obj. So, theoretically, if you need a dynamic @msg_reason, + * you could attach it to @watched_obj's user-data. + * + * Keep track of @watched_obj until it gets destroyed. During shutdown, + * we wait until all watched objects are destroyed. This is useful, if + * this object still conducts some asynchronous action, which needs to + * complete before NetworkManager is allowed to terminate. We re-use + * the reference-counter of @watched_obj as signal, that the object + * is still used. + * + * FIXME(shutdown): proper shutdown is not yet implemented, and registering + * an object (currently) has no effect. * - * Sets @value to an array of object paths of the objects in @objects. + * Returns: a handle to unregister the object. The caller may choose to ignore + * the handle, in which case, the object will be automatically unregistered, + * once it gets destroyed. */ +NMShutdownWaitObjHandle * +_nm_shutdown_wait_obj_register (GObject *watched_obj, + const char *msg_reason) +{ + NMShutdownWaitObjHandle *handle; + + g_return_val_if_fail (G_IS_OBJECT (watched_obj), NULL); + + if (G_UNLIKELY (!_shutdown_waitobj_lst_head.next)) + c_list_init (&_shutdown_waitobj_lst_head); + + handle = g_slice_new (NMShutdownWaitObjHandle); + handle->watched_obj = watched_obj; + /* we don't clone the string. We require the caller to use pass a static message. + * If he really cannot do that, he should attach the string to the watched_obj + * as user-data. */ + handle->msg_reason = msg_reason; + c_list_link_tail (&_shutdown_waitobj_lst_head, &handle->lst); + g_object_weak_ref (watched_obj, _shutdown_waitobj_cb, handle); + return handle; +} + void -nm_utils_g_value_set_object_path_array (GValue *value, - GSList *objects, - NMUtilsObjectFunc filter_func, - gpointer user_data) +nm_shutdown_wait_obj_unregister (NMShutdownWaitObjHandle *handle) { - char **paths; - guint i; - GSList *iter; + g_return_if_fail (handle); - paths = g_new (char *, g_slist_length (objects) + 1); - for (i = 0, iter = objects; iter; iter = iter->next) { - NMExportedObject *object = iter->data; - const char *path; + nm_assert (G_IS_OBJECT (handle->watched_obj)); + nm_assert (nm_c_list_contains_entry (&_shutdown_waitobj_lst_head, handle, lst)); - path = nm_exported_object_get_path (object); - if (!path) - continue; - if (filter_func && !filter_func ((GObject *) object, user_data)) - continue; - paths[i++] = g_strdup (path); - } - paths[i] = NULL; - g_value_take_boxed (value, paths); + g_object_weak_unref (handle->watched_obj, _shutdown_waitobj_cb, handle); + _shutdown_waitobj_unregister (handle); } - -/*****************************************************************************/ diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index e5f28b27..b26d08bd 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -31,7 +31,7 @@ const char *nm_utils_get_shared_wifi_permission (NMConnection *connection); void nm_utils_complete_generic (NMPlatform *platform, NMConnection *connection, const char *ctype, - const GSList *existing, + NMConnection *const*existing_connections, const char *preferred_id, const char *fallback_id_prefix, const char *ifname_prefix, @@ -48,21 +48,41 @@ NMConnection *nm_utils_match_connection (NMConnection *const*connections, NMUtilsMatchFilterFunc match_filter_func, gpointer match_filter_data); -void nm_utils_g_value_set_object_path (GValue *value, gpointer object); +int nm_match_spec_device_by_pllink (const NMPlatformLink *pllink, + const char *match_device_type, + const GSList *specs, + int no_match_value); -/** - * NMUtilsObjectFunc: - * @object: the object to filter on - * @user_data: data passed to the function from the caller + +/*****************************************************************************/ + +/* during shutdown, there are two relevant timeouts. One is + * NM_SHUTDOWN_TIMEOUT_MS which is plenty of time, that we give for all + * actions to complete. Of course, during shutdown components should hurry + * to cleanup. + * + * When we initiate shutdown, we should start killing child processes + * with SIGTERM. If they don't complete within NM_SHUTDOWN_TIMEOUT_MS, we send + * SIGKILL. * - * Returns: %TRUE if the object should be used, %FALSE if not + * After NM_SHUTDOWN_TIMEOUT_MS, NetworkManager will however not yet terminate right + * away. It iterates the mainloop for another NM_SHUTDOWN_TIMEOUT_MS_EXTRA. This + * should give time to reap the child process (after SIGKILL). + * + * So, the maxiumum time we should wait before sending SIGKILL should be at most + * NM_SHUTDOWN_TIMEOUT_MS. */ -typedef gboolean (*NMUtilsObjectFunc) (GObject *object, gpointer user_data); +#define NM_SHUTDOWN_TIMEOUT_MS 1500 +#define NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG 500 + +typedef struct _NMShutdownWaitObjHandle NMShutdownWaitObjHandle; + +NMShutdownWaitObjHandle *_nm_shutdown_wait_obj_register (GObject *watched_obj, + const char *msg_reason); + +#define nm_shutdown_wait_obj_register(watched_obj, msg_reason) _nm_shutdown_wait_obj_register((watched_obj), (""msg_reason"")) -void nm_utils_g_value_set_object_path_array (GValue *value, - GSList *objects, - NMUtilsObjectFunc filter_func, - gpointer user_data); +void nm_shutdown_wait_obj_unregister (NMShutdownWaitObjHandle *handle); /*****************************************************************************/ diff --git a/src/devices/adsl/meson.build b/src/devices/adsl/meson.build new file mode 100644 index 00000000..ee804d23 --- /dev/null +++ b/src/devices/adsl/meson.build @@ -0,0 +1,34 @@ +sources = files( + 'nm-atm-manager.c', + 'nm-device-adsl.c' +) + +deps = [ + libudev_dep, + nm_dep +] + +libnm_device_plugin_adsl = shared_module( + 'nm-device-plugin-adsl', + sources: sources, + dependencies: deps, + link_args: ldflags_linker_script_devices, + link_depends: linker_script_devices, + install: true, + install_dir: nm_plugindir +) + +core_plugins += libnm_device_plugin_adsl + +run_target( + 'check-local-devices-adsl', + command: [check_exports, libnm_device_plugin_adsl.full_path(), linker_script_devices], + depends: libnm_device_plugin_adsl +) + +# FIXME: check_so_symbols replacement +''' +check-local-devices-adsl: src/devices/adsl/libnm-device-plugin-adsl.la + $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/adsl/.libs/libnm-device-plugin-adsl.so "$(srcdir)/linker-script-devices.ver" + $(call check_so_symbols,$(builddir)/src/devices/adsl/.libs/libnm-device-plugin-adsl.so) +''' diff --git a/src/devices/adsl/nm-atm-manager.c b/src/devices/adsl/nm-atm-manager.c index 32c4c386..0ff4603d 100644 --- a/src/devices/adsl/nm-atm-manager.c +++ b/src/devices/adsl/nm-atm-manager.c @@ -236,7 +236,7 @@ handle_uevent (NMUdevClient *client, ifindex = udev_device_get_property_value (device, "IFINDEX"); seqnum = udev_device_get_seqnum (device); nm_log_dbg (LOGD_PLATFORM, "UDEV event: action '%s' subsys '%s' device '%s' (%s); seqnum=%" G_GUINT64_FORMAT, - action, subsys, udev_device_get_sysname (device), ifindex ? ifindex : "unknown", seqnum); + action, subsys, udev_device_get_sysname (device), ifindex ?: "unknown", seqnum); if (!strcmp (action, "add")) adsl_add (self, device); diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c index e9bd41ae..1450a836 100644 --- a/src/devices/adsl/nm-device-adsl.c +++ b/src/devices/adsl/nm-device-adsl.c @@ -39,8 +39,6 @@ #include "nm-setting-adsl.h" #include "nm-utils.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Adsl.h" - #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF (NMDeviceAdsl); @@ -116,7 +114,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMSettingAdsl *s_adsl; @@ -137,8 +135,6 @@ complete_connection (NMDevice *device, _("ADSL connection"), NULL, FALSE); /* No IPv6 yet by default */ - - return TRUE; } @@ -198,7 +194,7 @@ br2684_assign_vcc (NMDeviceAdsl *self, NMSettingAdsl *s_adsl) _LOGD (LOGD_ADSL, "assigning address %d.%d.%d encapsulation %s", priv->atm_index, addr.sap_addr.vpi, addr.sap_addr.vci, - encapsulation ? encapsulation : "(none)"); + encapsulation ?: "(none)"); err = connect (priv->brfd, (struct sockaddr*) &addr, sizeof (addr)); if (err != 0) { @@ -430,9 +426,23 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_ } } +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, - const char *iface, NMIP4Config *config, gpointer user_data) { @@ -440,7 +450,6 @@ ppp_ip4_config (NMPPPManager *ppp_manager, /* Ignore PPP IP4 events that come in after initial configuration */ if (nm_device_activate_ip4_state_in_conf (device)) { - nm_device_set_ip_iface (device, iface); nm_device_activate_schedule_ip4_config_result (device, config); } } @@ -499,6 +508,9 @@ act_stage3_ip4_config_start (NMDevice *device, 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); @@ -513,7 +525,7 @@ adsl_cleanup (NMDeviceAdsl *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_sync (priv->ppp_manager); + nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL); g_clear_object (&priv->ppp_manager); } @@ -639,10 +651,24 @@ dispose (GObject *object) G_OBJECT_CLASS (nm_device_adsl_parent_class)->dispose (object); } +static const NMDBusInterfaceInfoExtended interface_info_device_adsl = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_ADSL, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier", "b", NM_DEVICE_CARRIER), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_adsl_class_init (NMDeviceAdslClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); object_class->constructed = constructed; @@ -650,6 +676,8 @@ nm_device_adsl_class_init (NMDeviceAdslClass *klass) object_class->get_property = get_property; object_class->set_property = set_property; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_adsl); + parent_class->get_generic_capabilities = get_generic_capabilities; parent_class->check_connection_compatible = check_connection_compatible; @@ -666,8 +694,4 @@ nm_device_adsl_class_init (NMDeviceAdslClass *klass) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_ADSL_SKELETON, - NULL); } diff --git a/src/devices/bluetooth/meson.build b/src/devices/bluetooth/meson.build new file mode 100644 index 00000000..30284f9e --- /dev/null +++ b/src/devices/bluetooth/meson.build @@ -0,0 +1,45 @@ +sources = files( + 'nm-bluez-device.c', + 'nm-bluez-manager.c', + 'nm-bluez4-adapter.c', + 'nm-bluez4-manager.c', + 'nm-bluez5-manager.c', + 'nm-bt-error.c', + 'nm-device-bt.c' +) + +deps = [ + libnm_wwan_dep, + nm_dep +] + +if enable_bluez5_dun + sources += files('nm-bluez5-dun.c') + + deps += bluez5_dep +endif + +libnm_device_plugin_bluetooth = shared_module( + 'nm-device-plugin-bluetooth', + sources: sources, + dependencies: deps, + link_args: ldflags_linker_script_devices, + link_depends: linker_script_devices, + install: true, + install_dir: nm_plugindir +) + +core_plugins += libnm_device_plugin_bluetooth + +run_target( + 'check-local-devices-bluetooth', + command: [check_exports, libnm_device_plugin_bluetooth.full_path(), linker_script_devices], + depends: libnm_device_plugin_bluetooth +) + +# FIXME: check_so_symbols replacement +''' +check-local-devices-bluetooth: src/devices/bluetooth/libnm-device-plugin-bluetooth.la + $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/bluetooth/.libs/libnm-device-plugin-bluetooth.so "$(srcdir)/linker-script-devices.ver" + $(call check_so_symbols,$(builddir)/src/devices/bluetooth/.libs/libnm-device-plugin-bluetooth.so) +''' diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c index bd3cf18a..d8e40d6f 100644 --- a/src/devices/bluetooth/nm-bluez-device.c +++ b/src/devices/bluetooth/nm-bluez-device.c @@ -254,7 +254,7 @@ pan_connection_check_create (NMBluezDevice *self) g_assert (connection_compatible (self, added)); g_assert (nm_connection_compare (added, connection, NM_SETTING_COMPARE_FLAG_EXACT)); - nm_settings_connection_set_flags (NM_SETTINGS_CONNECTION (added), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED, TRUE); + nm_settings_connection_set_flags (NM_SETTINGS_CONNECTION (added), NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, TRUE); priv->connections = g_slist_prepend (priv->connections, g_object_ref (added)); priv->pan_connection = added; @@ -494,7 +494,7 @@ nm_bluez_device_disconnect (NMBluezDevice *self) priv->path, dbus_iface, "Disconnect", - args ? args : g_variant_new ("()"), + args ?: g_variant_new("()"), NULL, G_DBUS_CALL_FLAGS_NONE, 10000, @@ -1186,7 +1186,7 @@ dispose (GObject *object) /* Check whether we want to remove the created connection. If so, we take a reference * and delete it at the end of dispose(). */ if (NM_FLAGS_HAS (nm_settings_connection_get_flags (NM_SETTINGS_CONNECTION (priv->pan_connection)), - NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED)) + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) to_delete = g_object_ref (priv->pan_connection); priv->pan_connection = NULL; diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c index 96e80245..c74ecd81 100644 --- a/src/devices/bluetooth/nm-bluez-manager.c +++ b/src/devices/bluetooth/nm-bluez-manager.c @@ -125,7 +125,6 @@ async_data_unpack (struct AsyncData *async_data) return self; } - /** * Cancel any current attempt to detect the version and cleanup * the related fields. @@ -145,7 +144,6 @@ cleanup_checking (NMBluezManager *self, gboolean do_unwatch_name) } } - static void manager_bdaddr_added_cb (GObject *manager, NMBluezDevice *bt_device, @@ -244,7 +242,6 @@ setup_bluez5 (NMBluezManager *self) nm_bluez5_manager_query_devices (manager); } - static void watch_name_on_appeared (GDBusConnection *connection, const gchar *name, @@ -254,7 +251,6 @@ watch_name_on_appeared (GDBusConnection *connection, check_bluez_and_try_setup (NM_BLUEZ_MANAGER (user_data)); } - static void check_bluez_and_try_setup_final_step (NMBluezManager *self, int bluez_version, const char *reason) { diff --git a/src/devices/bluetooth/nm-bluez4-adapter.c b/src/devices/bluetooth/nm-bluez4-adapter.c index 0f19f998..c8ef7a27 100644 --- a/src/devices/bluetooth/nm-bluez4-adapter.c +++ b/src/devices/bluetooth/nm-bluez4-adapter.c @@ -25,7 +25,6 @@ #include #include "nm-dbus-interface.h" -#include "nm-utils/nm-hash-utils.h" #include "nm-bluez-device.h" #include "nm-bluez-common.h" #include "nm-core-internal.h" diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c index de3f4072..5d3bd23a 100644 --- a/src/devices/bluetooth/nm-bluez5-manager.c +++ b/src/devices/bluetooth/nm-bluez5-manager.c @@ -30,7 +30,7 @@ #include "nm-core-internal.h" -#include "nm-utils/c-list.h" +#include "c-list/src/c-list.h" #include "nm-bluez-device.h" #include "nm-bluez-common.h" #include "devices/nm-device-bridge.h" diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c index 977c1e1a..1d237d92 100644 --- a/src/devices/bluetooth/nm-device-bt.c +++ b/src/devices/bluetooth/nm-device-bt.c @@ -43,8 +43,6 @@ #include "devices/wwan/nm-modem-manager.h" #include "devices/wwan/nm-modem.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Bluetooth.h" - #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceBt); @@ -217,7 +215,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device); @@ -540,11 +538,16 @@ modem_ip4_config_result (NMModem *modem, } static void -data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) +ip_ifindex_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) { - NMDevice *self = NM_DEVICE (user_data); + NMDevice *device = NM_DEVICE (user_data); - nm_device_set_ip_iface (self, nm_modem_get_data_port (modem)); + if (!nm_device_set_ip_ifindex (device, + nm_modem_get_ip_ifindex (modem))) { + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + } } static gboolean @@ -640,29 +643,24 @@ component_added (NMDevice *device, GObject *component) NMDeviceBt *self = NM_DEVICE_BT (device); NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); NMModem *modem; - const gchar *modem_data_port; - const gchar *modem_control_port; - char *base; NMDeviceState state; NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; - if (!component || !NM_IS_MODEM (component)) + if ( !component + || !NM_IS_MODEM (component)) return FALSE; - modem = NM_MODEM (component); - - modem_data_port = nm_modem_get_data_port (modem); - modem_control_port = nm_modem_get_control_port (modem); - g_return_val_if_fail (modem_data_port != NULL || modem_control_port != NULL, FALSE); + modem = NM_MODEM (component); if (!priv->rfcomm_iface) return FALSE; - base = g_path_get_basename (priv->rfcomm_iface); - if (g_strcmp0 (base, modem_data_port) && g_strcmp0 (base, modem_control_port)) { - g_free (base); - return FALSE; + { + gs_free char *base = NULL; + + base = g_path_get_basename (priv->rfcomm_iface); + if (!nm_streq (base, nm_modem_get_control_port (modem))) + return FALSE; } - g_free (base); /* Got the modem */ nm_clear_g_source (&priv->timeout_id); @@ -696,7 +694,7 @@ component_added (NMDevice *device, GObject *component) g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self); g_signal_connect (modem, NM_MODEM_REMOVED, G_CALLBACK (modem_removed_cb), self); - g_signal_connect (modem, "notify::" NM_MODEM_DATA_PORT, G_CALLBACK (data_port_changed_cb), self); + g_signal_connect (modem, "notify::" NM_MODEM_IP_IFINDEX, G_CALLBACK (ip_ifindex_changed_cb), self); /* Kick off the modem connection */ if (!modem_stage1 (self, modem, &failure_reason)) @@ -753,7 +751,7 @@ bluez_connect_cb (GObject *object, GAsyncResult *res, void *user_data) { - NMDeviceBt *self = NM_DEVICE_BT (user_data); + gs_unref_object NMDeviceBt *self = NM_DEVICE_BT (user_data); NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); GError *error = NULL; const char *device; @@ -761,6 +759,9 @@ bluez_connect_cb (GObject *object, device = nm_bluez_device_connect_finish (NM_BLUEZ_DEVICE (object), res, &error); + if (!nm_device_is_activating (NM_DEVICE (self))) + return; + if (!device) { _LOGW (LOGD_BT, "Error connecting with bluez: %s", error->message); g_clear_error (&error); @@ -768,7 +769,6 @@ bluez_connect_cb (GObject *object, nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BT_FAILED); - g_object_unref (self); return; } @@ -776,7 +776,13 @@ bluez_connect_cb (GObject *object, g_free (priv->rfcomm_iface); priv->rfcomm_iface = g_strdup (device); } else if (priv->bt_type == NM_BT_CAPABILITY_NAP) { - nm_device_set_ip_iface (NM_DEVICE (self), device); + if (!nm_device_set_ip_iface (NM_DEVICE (self), device)) { + _LOGW (LOGD_BT, "Error connecting with bluez: cannot find device %s", device); + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_BT_FAILED); + return; + } } _LOGD (LOGD_BT, "connect request successful"); @@ -784,7 +790,6 @@ bluez_connect_cb (GObject *object, /* Stage 3 gets scheduled when Bluez says we're connected */ priv->have_iface = TRUE; check_connect_continue (self); - g_object_unref (self); } static void @@ -1145,10 +1150,26 @@ finalize (GObject *object) G_OBJECT_CLASS (nm_device_bt_parent_class)->finalize (object); } +static const NMDBusInterfaceInfoExtended interface_info_device_bluetooth = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_BLUETOOTH, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Name", "s", NM_DEVICE_BT_NAME), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("BtCapabilities", "u", NM_DEVICE_BT_CAPABILITIES), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_bt_class_init (NMDeviceBtClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); object_class->constructed = constructed; @@ -1157,6 +1178,8 @@ nm_device_bt_class_init (NMDeviceBtClass *klass) object_class->dispose = dispose; object_class->finalize = finalize; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_bluetooth); + device_class->get_generic_capabilities = get_generic_capabilities; device_class->can_auto_connect = can_auto_connect; device_class->deactivate = deactivate; @@ -1200,8 +1223,4 @@ nm_device_bt_class_init (NMDeviceBtClass *klass) G_TYPE_NONE, 2, G_TYPE_UINT /*guint32 in_bytes*/, G_TYPE_UINT /*guint32 out_bytes*/); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_BLUETOOTH_SKELETON, - NULL); } diff --git a/src/devices/meson.build b/src/devices/meson.build new file mode 100644 index 00000000..2d874659 --- /dev/null +++ b/src/devices/meson.build @@ -0,0 +1,22 @@ +subdir('adsl') + +if enable_modem_manager + subdir('wwan') + subdir('bluetooth') +endif + +if enable_wifi + subdir('wifi') +endif + +if enable_teamdctl + subdir('team') +endif + +if enable_ovs + subdir('ovs') +endif + +if enable_tests + subdir('tests') +endif diff --git a/src/devices/nm-acd-manager.c b/src/devices/nm-acd-manager.c new file mode 100644 index 00000000..035487a3 --- /dev/null +++ b/src/devices/nm-acd-manager.c @@ -0,0 +1,493 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2015-2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-acd-manager.h" + +#include +#include +#include + +#include "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; + NMAcdManager *manager; + NAcd *acd; + GIOChannel *channel; + guint event_id; +} AddressInfo; + +enum { + PROBE_TERMINATED, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct { + int ifindex; + guint8 hwaddr[ETH_ALEN]; + State state; + GHashTable *addresses; + guint completed; +} NMAcdManagerPrivate; + +struct _NMAcdManager { + GObject parent; + NMAcdManagerPrivate _priv; +}; + +struct _NMAcdManagerClass { + GObjectClass parent; +}; + +G_DEFINE_TYPE (NMAcdManager, nm_acd_manager, G_TYPE_OBJECT) + +#define NM_ACD_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMAcdManager, NM_IS_ACD_MANAGER) + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_IP4 +#define _NMLOG_PREFIX_NAME "acd" +#define _NMLOG(level, ...) \ + G_STMT_START { \ + char _sbuf[64]; \ + int _ifindex = (self) ? NM_ACD_MANAGER_GET_PRIVATE (self)->ifindex : 0; \ + \ + nm_log ((level), _NMLOG_DOMAIN, \ + nm_platform_link_get_name (NM_PLATFORM_GET, _ifindex), \ + NULL, \ + "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + self ? nm_sprintf_buf (_sbuf, "[%p,%d]", 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(event) NM_UTILS_LOOKUP_STR (_acd_event_to_string, event) + +static const char * +_acd_error_to_string (int error) +{ + if (error < 0) + return strerror(-error); + + switch (error) { + case _N_ACD_E_SUCCESS: + return "success"; + case N_ACD_E_DONE: + return "no more events (engine running)"; + case N_ACD_E_STOPPED: + return "no more events (engine stopped)"; + case N_ACD_E_PREEMPTED: + return "preempted"; + case N_ACD_E_INVALID_ARGUMENT: + return "invalid argument"; + case N_ACD_E_BUSY: + return "busy"; + } + return NULL; +} + +#define acd_error_to_string(error) NM_UTILS_LOOKUP_STR (_acd_error_to_string, 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) +{ + NMAcdManagerPrivate *priv; + AddressInfo *info; + + g_return_val_if_fail (NM_IS_ACD_MANAGER (self), FALSE); + priv = NM_ACD_MANAGER_GET_PRIVATE (self); + g_return_val_if_fail (priv->state == STATE_INIT, FALSE); + + if (g_hash_table_lookup (priv->addresses, GUINT_TO_POINTER (address))) + return FALSE; + + info = g_slice_new0 (AddressInfo); + info->address = address; + info->manager = self; + + g_hash_table_insert (priv->addresses, GUINT_TO_POINTER (address), info); + + return TRUE; +} + +static gboolean +acd_event (GIOChannel *source, GIOCondition condition, gpointer data) +{ + AddressInfo *info = data; + NMAcdManager *self = info->manager; + NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); + NAcdEvent *event; + char address_str[INET_ADDRSTRLEN]; + gs_free char *hwaddr_str = NULL; + int r; + + if ( n_acd_dispatch (info->acd) + || n_acd_pop_event (info->acd, &event)) + return G_SOURCE_CONTINUE; + + switch (event->event) { + case N_ACD_EVENT_READY: + info->duplicate = FALSE; + if (priv->state == STATE_ANNOUNCING) { + r = n_acd_announce (info->acd, 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, priv->ifindex), + acd_error_to_string (r)); + } else { + _LOGD ("announcing address %s", + nm_utils_inet4_ntop (info->address, address_str)); + } + } + break; + case N_ACD_EVENT_USED: + info->duplicate = TRUE; + break; + case N_ACD_EVENT_DEFENDED: + _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: + _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, priv->ifindex)); + break; + default: + _LOGD ("event '%s' for address %s", + acd_event_to_string (event->event), + nm_utils_inet4_ntop (info->address, address_str)); + return G_SOURCE_CONTINUE; + } + + if ( priv->state == STATE_PROBING + && ++priv->completed == g_hash_table_size (priv->addresses)) { + priv->state = STATE_PROBE_DONE; + g_signal_emit (self, signals[PROBE_TERMINATED], 0); + } + + return G_SOURCE_CONTINUE; +} + +static gboolean +acd_probe_start (NMAcdManager *self, + AddressInfo *info, + guint64 timeout) +{ + NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); + NAcdConfig *config; + int r, fd; + + r = n_acd_new (&info->acd); + if (r) { + _LOGW ("could not create ACD for %s on interface '%s': %s", + nm_utils_inet4_ntop (info->address, NULL), + nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), + acd_error_to_string (r)); + return FALSE; + } + + n_acd_get_fd (info->acd, &fd); + info->channel = g_io_channel_unix_new (fd); + info->event_id = g_io_add_watch (info->channel, G_IO_IN, acd_event, info); + + config = &(NAcdConfig) { + .ifindex = priv->ifindex, + .mac = priv->hwaddr, + .n_mac = ETH_ALEN, + .ip = info->address, + .timeout_msec = timeout, + .transport = N_ACD_TRANSPORT_ETHERNET, + }; + + r = n_acd_start (info->acd, config); + if (r) { + _LOGW ("could not start probe for %s on interface '%s': %s", + nm_utils_inet4_ntop (info->address, NULL), + nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), + acd_error_to_string (r)); + return FALSE; + } + + if (timeout) { + _LOGD ("started probe for %s with timeout %llu", + nm_utils_inet4_ntop (info->address, NULL), + (unsigned long long) timeout); + } + + return TRUE; +} + +/** + * 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: %TRUE if at least one probe could be started, %FALSE otherwise + */ +gboolean +nm_acd_manager_start_probe (NMAcdManager *self, guint timeout) +{ + NMAcdManagerPrivate *priv; + GHashTableIter iter; + AddressInfo *info; + gboolean success = FALSE; + + g_return_val_if_fail (NM_IS_ACD_MANAGER (self), FALSE); + priv = NM_ACD_MANAGER_GET_PRIVATE (self); + g_return_val_if_fail (priv->state == STATE_INIT, FALSE); + + priv->completed = 0; + + g_hash_table_iter_init (&iter, priv->addresses); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) + success |= acd_probe_start (self, info, timeout); + + if (success) + priv->state = STATE_PROBING; + + return success; +} + +/** + * nm_acd_manager_reset: + * @self: a #NMAcdManager + * + * Stop any operation in progress and reset @self to the initial state. + */ +void +nm_acd_manager_reset (NMAcdManager *self) +{ + NMAcdManagerPrivate *priv; + + g_return_if_fail (NM_IS_ACD_MANAGER (self)); + priv = NM_ACD_MANAGER_GET_PRIVATE (self); + + g_hash_table_remove_all (priv->addresses); + + priv->state = STATE_INIT; +} + +/** + * nm_acd_manager_destroy: + * @self: the #NMAcdManager + * + * Calls nm_acd_manager_reset() and unrefs @self. + */ +void +nm_acd_manager_destroy (NMAcdManager *self) +{ + g_return_if_fail (NM_IS_ACD_MANAGER (self)); + + nm_acd_manager_reset (self); + g_object_unref (self); +} + +/** + * 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) +{ + NMAcdManagerPrivate *priv; + AddressInfo *info; + + g_return_val_if_fail (NM_IS_ACD_MANAGER (self), FALSE); + priv = NM_ACD_MANAGER_GET_PRIVATE (self); + g_return_val_if_fail ( priv->state == STATE_INIT + || priv->state == STATE_PROBE_DONE, FALSE); + + info = g_hash_table_lookup (priv->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. + */ +void +nm_acd_manager_announce_addresses (NMAcdManager *self) +{ + NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); + GHashTableIter iter; + AddressInfo *info; + int r; + + if (priv->state == STATE_INIT) { + /* n-acd can't announce without probing, therefore let's + * start a fake probe with zero timeout and then perform + * the announce. */ + priv->state = STATE_ANNOUNCING; + g_hash_table_iter_init (&iter, priv->addresses); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { + if (!acd_probe_start (self, info, 0)) { + _LOGW ("couldn't announce address %s on interface '%s'", + nm_utils_inet4_ntop (info->address, NULL), + nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex)); + } + } + } else if (priv->state == STATE_PROBE_DONE) { + priv->state = STATE_ANNOUNCING; + g_hash_table_iter_init (&iter, priv->addresses); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { + if (info->duplicate) + continue; + r = n_acd_announce (info->acd, N_ACD_DEFEND_ONCE); + if (r) { + _LOGW ("couldn't announce address %s on interface '%s': %s", + nm_utils_inet4_ntop (info->address, NULL), + nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), + acd_error_to_string (r)); + } else + _LOGD ("announcing address %s", nm_utils_inet4_ntop (info->address, NULL)); + } + } else + nm_assert_not_reached (); +} + +static void +destroy_address_info (gpointer data) +{ + AddressInfo *info = (AddressInfo *) data; + + g_clear_pointer (&info->channel, g_io_channel_unref); + g_clear_pointer (&info->acd, n_acd_free); + nm_clear_g_source (&info->event_id); + + g_slice_free (AddressInfo, info); +} + +/*****************************************************************************/ + +static void +nm_acd_manager_init (NMAcdManager *self) +{ + NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); + + priv->addresses = g_hash_table_new_full (nm_direct_hash, NULL, + NULL, destroy_address_info); + priv->state = STATE_INIT; +} + +NMAcdManager * +nm_acd_manager_new (int ifindex, const guint8 *hwaddr, size_t hwaddr_len) +{ + NMAcdManager *self; + NMAcdManagerPrivate *priv; + + g_return_val_if_fail (hwaddr, NULL); + g_return_val_if_fail (hwaddr_len == ETH_ALEN, NULL); + + self = g_object_new (NM_TYPE_ACD_MANAGER, NULL); + priv = NM_ACD_MANAGER_GET_PRIVATE (self); + priv->ifindex = ifindex; + memcpy (priv->hwaddr, hwaddr, ETH_ALEN); + + return self; +} + +static void +dispose (GObject *object) +{ + NMAcdManager *self = NM_ACD_MANAGER (object); + NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); + + g_clear_pointer (&priv->addresses, g_hash_table_destroy); + + G_OBJECT_CLASS (nm_acd_manager_parent_class)->dispose (object); +} + +static void +nm_acd_manager_class_init (NMAcdManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = dispose; + + signals[PROBE_TERMINATED] = + g_signal_new (NM_ACD_MANAGER_PROBE_TERMINATED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); +} diff --git a/src/devices/nm-acd-manager.h b/src/devices/nm-acd-manager.h new file mode 100644 index 00000000..eeede5da --- /dev/null +++ b/src/devices/nm-acd-manager.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2015-2018 Red Hat, Inc. + */ + +#ifndef __NM_ACD_MANAGER__ +#define __NM_ACD_MANAGER__ + +#include + +#define NM_TYPE_ACD_MANAGER (nm_acd_manager_get_type ()) +#define NM_ACD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ACD_MANAGER, NMAcdManager)) +#define NM_ACD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ACD_MANAGER, NMAcdManagerClass)) +#define NM_IS_ACD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ACD_MANAGER)) +#define NM_IS_ACD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ACD_MANAGER)) +#define NM_ACD_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ACD_MANAGER, NMAcdManagerClass)) + +#define NM_ACD_MANAGER_PROBE_TERMINATED "probe-terminated" + +typedef struct _NMAcdManagerClass NMAcdManagerClass; + +GType nm_acd_manager_get_type (void); + +NMAcdManager *nm_acd_manager_new (int ifindex, const guint8 *hwaddr, size_t hwaddr_len); +void nm_acd_manager_destroy (NMAcdManager *self); +gboolean nm_acd_manager_add_address (NMAcdManager *self, in_addr_t address); +gboolean nm_acd_manager_start_probe (NMAcdManager *self, guint timeout); +gboolean nm_acd_manager_check_address (NMAcdManager *self, in_addr_t address); +void nm_acd_manager_announce_addresses (NMAcdManager *self); +void nm_acd_manager_reset (NMAcdManager *self); + +#endif /* __NM_ACD_MANAGER__ */ diff --git a/src/devices/nm-arping-manager.c b/src/devices/nm-arping-manager.c deleted file mode 100644 index 51f80e08..00000000 --- a/src/devices/nm-arping-manager.c +++ /dev/null @@ -1,474 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Copyright (C) 2015 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-arping-manager.h" - -#include -#include -#include - -#include "platform/nm-platform.h" -#include "nm-utils.h" -#include "NetworkManagerUtils.h" - -/*****************************************************************************/ - -typedef enum { - STATE_INIT, - STATE_PROBING, - STATE_PROBE_DONE, - STATE_ANNOUNCING, -} State; - -typedef struct { - in_addr_t address; - GPid pid; - guint watch; - gboolean duplicate; - NMArpingManager *manager; -} AddressInfo; - -/*****************************************************************************/ - -enum { - PROBE_TERMINATED, - LAST_SIGNAL, -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -typedef struct { - int ifindex; - State state; - GHashTable *addresses; - guint completed; - guint timer; - guint round2_id; -} NMArpingManagerPrivate; - -struct _NMArpingManager { - GObject parent; - NMArpingManagerPrivate _priv; -}; - -struct _NMArpingManagerClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE (NMArpingManager, nm_arping_manager, G_TYPE_OBJECT) - -#define NM_ARPING_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMArpingManager, NM_IS_ARPING_MANAGER) - -/*****************************************************************************/ - -#define _NMLOG_DOMAIN LOGD_IP4 -#define _NMLOG_PREFIX_NAME "arping" -#define _NMLOG(level, ...) \ - G_STMT_START { \ - char _sbuf[64]; \ - int _ifindex = (self) ? NM_ARPING_MANAGER_GET_PRIVATE (self)->ifindex : 0; \ - \ - nm_log ((level), _NMLOG_DOMAIN, \ - nm_platform_link_get_name (NM_PLATFORM_GET, _ifindex), \ - NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - self ? nm_sprintf_buf (_sbuf, "[%p,%d]", self, _ifindex) : "" \ - _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ - } G_STMT_END - -/*****************************************************************************/ - -/** - * nm_arping_manager_add_address: - * @self: a #NMArpingManager - * @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_arping_manager_add_address (NMArpingManager *self, in_addr_t address) -{ - NMArpingManagerPrivate *priv; - AddressInfo *info; - - g_return_val_if_fail (NM_IS_ARPING_MANAGER (self), FALSE); - priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - g_return_val_if_fail (priv->state == STATE_INIT, FALSE); - - if (g_hash_table_lookup (priv->addresses, GUINT_TO_POINTER (address))) { - _LOGD ("address already exists"); - return FALSE; - } - - info = g_slice_new0 (AddressInfo); - info->address = address; - info->manager = self; - - g_hash_table_insert (priv->addresses, GUINT_TO_POINTER (address), info); - - return TRUE; -} - -static void -arping_watch_cb (GPid pid, gint status, gpointer user_data) -{ - AddressInfo *info = user_data; - NMArpingManager *self = info->manager; - NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - const char *addr; - - info->pid = 0; - info->watch = 0; - addr = nm_utils_inet4_ntop (info->address, NULL); - - if (WIFEXITED (status)) { - if (WEXITSTATUS (status) != 0) { - _LOGD ("%s already used in the %s network", - addr, nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex)); - info->duplicate = TRUE; - } else - _LOGD ("DAD succeeded for %s", addr); - } else { - _LOGD ("stopped unexpectedly with status %d for %s", status, addr); - } - - if (++priv->completed == g_hash_table_size (priv->addresses)) { - priv->state = STATE_PROBE_DONE; - nm_clear_g_source (&priv->timer); - g_signal_emit (self, signals[PROBE_TERMINATED], 0); - } -} - -static gboolean -arping_timeout_cb (gpointer user_data) -{ - NMArpingManager *self = user_data; - NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - GHashTableIter iter; - AddressInfo *info; - - priv->timer = 0; - - g_hash_table_iter_init (&iter, priv->addresses); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { - nm_clear_g_source (&info->watch); - if (info->pid) { - _LOGD ("DAD timed out for %s", - nm_utils_inet4_ntop (info->address, NULL)); - nm_utils_kill_child_async (info->pid, SIGTERM, LOGD_IP4, - "arping", 1000, NULL, NULL); - info->pid = 0; - } - } - - priv->state = STATE_PROBE_DONE; - g_signal_emit (self, signals[PROBE_TERMINATED], 0); - - return G_SOURCE_REMOVE; -} - -/** - * nm_arping_manager_start_probe: - * @self: a #NMArpingManager - * @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: %TRUE if at least one probe could be started, %FALSE otherwise - */ -gboolean -nm_arping_manager_start_probe (NMArpingManager *self, guint timeout, GError **error) -{ - const char *argv[] = { NULL, "-D", "-q", "-I", NULL, "-c", NULL, "-w", NULL, NULL, NULL }; - NMArpingManagerPrivate *priv; - GHashTableIter iter; - AddressInfo *info; - gs_free char *timeout_str = NULL; - gboolean success = FALSE; - - g_return_val_if_fail (NM_IS_ARPING_MANAGER (self), FALSE); - g_return_val_if_fail (!error || !*error, FALSE); - g_return_val_if_fail (timeout, FALSE); - - priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - g_return_val_if_fail (priv->state == STATE_INIT, FALSE); - - argv[4] = nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex); - if (!argv[4]) { - /* The device was probably just removed. */ - g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, - "can't find a name for ifindex %d", priv->ifindex); - return FALSE; - } - - priv->completed = 0; - - argv[0] = nm_utils_find_helper ("arping", NULL, NULL); - if (!argv[0]) { - g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, - "arping could not be found"); - return FALSE; - } - - timeout_str = g_strdup_printf ("%u", timeout / 1000 + 2); - argv[6] = timeout_str; - argv[8] = timeout_str; - - g_hash_table_iter_init (&iter, priv->addresses); - - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { - gs_free char *tmp_str = NULL; - - argv[9] = nm_utils_inet4_ntop (info->address, NULL); - _LOGD ("run %s", (tmp_str = g_strjoinv (" ", (char **) argv))); - - if (g_spawn_async (NULL, (char **) argv, NULL, - G_SPAWN_STDOUT_TO_DEV_NULL | - G_SPAWN_STDERR_TO_DEV_NULL | - G_SPAWN_DO_NOT_REAP_CHILD, - NULL, NULL, &info->pid, NULL)) { - info->watch = g_child_watch_add (info->pid, arping_watch_cb, info); - success = TRUE; - } - } - - if (success) { - priv->timer = g_timeout_add (timeout, arping_timeout_cb, self); - priv->state = STATE_PROBING; - } else { - g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, - "could not spawn arping process"); - } - - return success; -} - -/** - * nm_arping_manager_reset: - * @self: a #NMArpingManager - * - * Stop any operation in progress and reset @self to the initial state. - */ -void -nm_arping_manager_reset (NMArpingManager *self) -{ - NMArpingManagerPrivate *priv; - - g_return_if_fail (NM_IS_ARPING_MANAGER (self)); - priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - - nm_clear_g_source (&priv->timer); - nm_clear_g_source (&priv->round2_id); - g_hash_table_remove_all (priv->addresses); - - priv->state = STATE_INIT; -} - -/** - * nm_arping_manager_destroy: - * @self: the #NMArpingManager - * - * Calls nm_arping_manager_reset() and unrefs @self. - */ -void -nm_arping_manager_destroy (NMArpingManager *self) -{ - g_return_if_fail (NM_IS_ARPING_MANAGER (self)); - - nm_arping_manager_reset (self); - g_object_unref (self); -} - -/** - * nm_arping_manager_check_address: - * @self: a #NMArpingManager - * @address: an IP address - * - * Check if an IP address is duplicate. @address must have been added with - * nm_arping_manager_add_address(). - * - * Returns: %TRUE if the address is not duplicate, %FALSE otherwise - */ -gboolean -nm_arping_manager_check_address (NMArpingManager *self, in_addr_t address) -{ - NMArpingManagerPrivate *priv; - AddressInfo *info; - - g_return_val_if_fail (NM_IS_ARPING_MANAGER (self), FALSE); - priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - g_return_val_if_fail ( priv->state == STATE_INIT - || priv->state == STATE_PROBE_DONE, FALSE); - - info = g_hash_table_lookup (priv->addresses, GUINT_TO_POINTER (address)); - g_return_val_if_fail (info, FALSE); - - return !info->duplicate; -} - -static void -send_announcements (NMArpingManager *self, const char *mode_arg) -{ - NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - const char *argv[] = { NULL, mode_arg, "-q", "-I", NULL, "-c", "1", NULL, NULL }; - int ip_arg = G_N_ELEMENTS (argv) - 2; - GError *error = NULL; - GHashTableIter iter; - AddressInfo *info; - - argv[4] = nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex); - if (!argv[4]) { - /* The device was probably just removed. */ - _LOGW ("can't find a name for ifindex %d", priv->ifindex); - return; - } - - argv[0] = nm_utils_find_helper ("arping", NULL, NULL); - if (!argv[0]) { - _LOGW ("arping could not be found; no ARPs will be sent"); - return; - } - - g_hash_table_iter_init (&iter, priv->addresses); - - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { - gs_free char *tmp_str = NULL; - gboolean success; - - if (info->duplicate) - continue; - - argv[ip_arg] = nm_utils_inet4_ntop (info->address, NULL); - _LOGD ("run %s", (tmp_str = g_strjoinv (" ", (char **) argv))); - - success = g_spawn_async (NULL, (char **) argv, NULL, - G_SPAWN_STDOUT_TO_DEV_NULL | - G_SPAWN_STDERR_TO_DEV_NULL, - NULL, NULL, NULL, &error); - if (!success) { - _LOGW ("could not send ARP for address %s: %s", argv[ip_arg], - error->message); - g_clear_error (&error); - } - } -} - -static gboolean -arp_announce_round2 (gpointer self) -{ - NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE ((NMArpingManager *) self); - - priv->round2_id = 0; - send_announcements (self, "-U"); - priv->state = STATE_INIT; - g_hash_table_remove_all (priv->addresses); - - return G_SOURCE_REMOVE; -} - -/** - * nm_arping_manager_announce_addresses: - * @self: a #NMArpingManager - * - * Start announcing addresses. - */ -void -nm_arping_manager_announce_addresses (NMArpingManager *self) -{ - NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - - g_return_if_fail ( priv->state == STATE_INIT - || priv->state == STATE_PROBE_DONE); - - send_announcements (self, "-A"); - nm_clear_g_source (&priv->round2_id); - priv->round2_id = g_timeout_add_seconds (2, arp_announce_round2, self); - priv->state = STATE_ANNOUNCING; -} - -static void -destroy_address_info (gpointer data) -{ - AddressInfo *info = (AddressInfo *) data; - - nm_clear_g_source (&info->watch); - - if (info->pid) { - nm_utils_kill_child_async (info->pid, SIGTERM, LOGD_IP4, "arping", - 1000, NULL, NULL); - } - - g_slice_free (AddressInfo, info); -} - -/*****************************************************************************/ - -static void -nm_arping_manager_init (NMArpingManager *self) -{ - NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - - priv->addresses = g_hash_table_new_full (g_direct_hash, g_direct_equal, - NULL, destroy_address_info); - priv->state = STATE_INIT; -} - -NMArpingManager * -nm_arping_manager_new (int ifindex) -{ - NMArpingManager *self; - NMArpingManagerPrivate *priv; - - self = g_object_new (NM_TYPE_ARPING_MANAGER, NULL); - priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - priv->ifindex = ifindex; - return self; -} - -static void -dispose (GObject *object) -{ - NMArpingManager *self = NM_ARPING_MANAGER (object); - NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self); - - nm_clear_g_source (&priv->timer); - nm_clear_g_source (&priv->round2_id); - g_clear_pointer (&priv->addresses, g_hash_table_destroy); - - G_OBJECT_CLASS (nm_arping_manager_parent_class)->dispose (object); -} - -static void -nm_arping_manager_class_init (NMArpingManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = dispose; - - signals[PROBE_TERMINATED] = - g_signal_new (NM_ARPING_MANAGER_PROBE_TERMINATED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); -} diff --git a/src/devices/nm-arping-manager.h b/src/devices/nm-arping-manager.h deleted file mode 100644 index c8a86af0..00000000 --- a/src/devices/nm-arping-manager.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_ARPING_MANAGER__ -#define __NM_ARPING_MANAGER__ - -#include - -#define NM_TYPE_ARPING_MANAGER (nm_arping_manager_get_type ()) -#define NM_ARPING_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ARPING_MANAGER, NMArpingManager)) -#define NM_ARPING_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ARPING_MANAGER, NMArpingManagerClass)) -#define NM_IS_ARPING_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ARPING_MANAGER)) -#define NM_IS_ARPING_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ARPING_MANAGER)) -#define NM_ARPING_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ARPING_MANAGER, NMArpingManagerClass)) - -#define NM_ARPING_MANAGER_PROBE_TERMINATED "probe-terminated" - -typedef struct _NMArpingManagerClass NMArpingManagerClass; - -GType nm_arping_manager_get_type (void); - -NMArpingManager *nm_arping_manager_new (int ifindex); -void nm_arping_manager_destroy (NMArpingManager *self); -gboolean nm_arping_manager_add_address (NMArpingManager *self, in_addr_t address); -gboolean nm_arping_manager_start_probe (NMArpingManager *self, guint timeout, GError **error); -gboolean nm_arping_manager_check_address (NMArpingManager *self, in_addr_t address); -void nm_arping_manager_announce_addresses (NMArpingManager *self); -void nm_arping_manager_reset (NMArpingManager *self); - -#endif /* __NM_ARPING_MANAGER__ */ diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 910dd0bf..22f7cdde 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -32,8 +32,6 @@ #include "nm-core-internal.h" #include "nm-ip4-config.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Bond.h" - #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceBond); @@ -78,7 +76,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMSettingBond *s_bond; @@ -316,7 +314,7 @@ apply_bonding_config (NMDevice *device) /* Primary */ value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_PRIMARY); - set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_PRIMARY, value ? value : ""); + set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_PRIMARY, value ?: ""); /* ARP targets: clear and initialize the list */ contents = nm_platform_sysctl_master_get_option (nm_device_get_platform (device), ifindex, @@ -593,7 +591,7 @@ reapply_connection (NMDevice *device, NMConnection *con_old, NMConnection *con_n /* Primary */ value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_PRIMARY); - set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_PRIMARY, value ? value : ""); + set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_PRIMARY, value ?: ""); /* Active slave */ set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE); @@ -607,13 +605,31 @@ nm_device_bond_init (NMDeviceBond * self) nm_assert (nm_device_is_master (NM_DEVICE (self))); } +static const NMDBusInterfaceInfoExtended interface_info_device_bond = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_BOND, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier", "b", NM_DEVICE_CARRIER), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Slaves", "ao", NM_DEVICE_SLAVES), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_bond_class_init (NMDeviceBondClass *klass) { + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_BOND_SETTING_NAME, NM_LINK_TYPE_BOND) + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_bond); + parent_class->is_master = TRUE; parent_class->get_generic_capabilities = get_generic_capabilities; parent_class->check_connection_compatible = check_connection_compatible; @@ -629,10 +645,6 @@ nm_device_bond_class_init (NMDeviceBondClass *klass) parent_class->release_slave = release_slave; parent_class->can_reapply_change = can_reapply_change; parent_class->reapply_connection = reapply_connection; - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_BOND_SKELETON, - NULL); } /*****************************************************************************/ diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 74689aef..c81a0253 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -30,8 +30,6 @@ #include "nm-device-factory.h" #include "nm-core-internal.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Bridge.h" - #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceBridge); @@ -117,7 +115,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMSettingBridge *s_bridge; @@ -488,13 +486,31 @@ nm_device_bridge_init (NMDeviceBridge * self) nm_assert (nm_device_is_master (NM_DEVICE (self))); } +static const NMDBusInterfaceInfoExtended interface_info_device_bridge = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_BRIDGE, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier", "b", NM_DEVICE_CARRIER), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Slaves", "ao", NM_DEVICE_SLAVES), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_bridge_class_init (NMDeviceBridgeClass *klass) { + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_BRIDGE_SETTING_NAME, NM_LINK_TYPE_BRIDGE) + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_bridge); + parent_class->is_master = TRUE; parent_class->get_generic_capabilities = get_generic_capabilities; parent_class->check_connection_compatible = check_connection_compatible; @@ -511,10 +527,6 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass) parent_class->enslave_slave = enslave_slave; parent_class->release_slave = release_slave; parent_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired; - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_BRIDGE_SKELETON, - NULL); } /*****************************************************************************/ diff --git a/src/devices/nm-device-dummy.c b/src/devices/nm-device-dummy.c index 085c44e6..9be4d29f 100644 --- a/src/devices/nm-device-dummy.c +++ b/src/devices/nm-device-dummy.c @@ -28,8 +28,6 @@ #include "nm-setting-dummy.h" #include "nm-core-internal.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Dummy.h" - #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceDummy); @@ -57,7 +55,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMSettingDummy *s_dummy; @@ -156,13 +154,29 @@ nm_device_dummy_init (NMDeviceDummy *self) { } +static const NMDBusInterfaceInfoExtended interface_info_device_dummy = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_DUMMY, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_dummy_class_init (NMDeviceDummyClass *klass) { + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_DUMMY) + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_dummy); + device_class->connection_type = NM_SETTING_DUMMY_SETTING_NAME; device_class->complete_connection = complete_connection; device_class->check_connection_compatible = check_connection_compatible; @@ -171,13 +185,8 @@ nm_device_dummy_class_init (NMDeviceDummyClass *klass) device_class->update_connection = update_connection; device_class->act_stage1_prepare = act_stage1_prepare; device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired; - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_DUMMY_SKELETON, - NULL); } - /*****************************************************************************/ #define NM_TYPE_DUMMY_DEVICE_FACTORY (nm_dummy_device_factory_get_type ()) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 3ca86e06..6c5e33e4 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -28,7 +28,6 @@ #include #include #include - #include #include "nm-device-private.h" @@ -51,8 +50,7 @@ #include "nm-device-factory.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" - -#include "introspection/org.freedesktop.NetworkManager.Device.Wired.h" +#include "nm-utils/nm-udev-utils.h" #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceEthernet); @@ -550,7 +548,7 @@ build_supplicant_config (NMDeviceEthernet *self, mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)), nm_device_get_ifindex (NM_DEVICE (self))); - config = nm_supplicant_config_new (); + config = nm_supplicant_config_new (FALSE, FALSE); security = nm_connection_get_setting_802_1x (connection); if (!nm_supplicant_config_add_setting_8021x (config, security, con_uuid, mtu, TRUE, error)) { @@ -658,12 +656,9 @@ handle_auth_or_fail (NMDeviceEthernet *self, NMActRequest *req, gboolean new_secrets) { - NMDeviceEthernetPrivate *priv; const char *setting_name; NMConnection *applied_connection; - priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - if (!nm_device_auth_retries_try_next (NM_DEVICE (self))) return NM_ACT_STAGE_RETURN_FAILURE; @@ -673,13 +668,14 @@ handle_auth_or_fail (NMDeviceEthernet *self, applied_connection = nm_act_request_get_applied_connection (req); setting_name = nm_connection_need_secrets (applied_connection, NULL); - if (setting_name) { - wired_secrets_get_secrets (self, setting_name, - NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION - | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0)); - } else + if (!setting_name) { _LOGI (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets."); + return NM_ACT_STAGE_RETURN_FAILURE; + } + wired_secrets_get_secrets (self, setting_name, + NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION + | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0)); return NM_ACT_STAGE_RETURN_POSTPONE; } @@ -742,7 +738,7 @@ supplicant_interface_init (NMDeviceEthernet *self) return FALSE; } - /* Listen for it's state signals */ + /* Listen for its state signals */ priv->supplicant.iface_state_id = g_signal_connect (priv->supplicant.iface, NM_SUPPLICANT_INTERFACE_STATE, G_CALLBACK (supplicant_iface_state_cb), @@ -955,9 +951,23 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_ } } +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, - const char *iface, NMIP4Config *config, gpointer user_data) { @@ -965,7 +975,6 @@ ppp_ip4_config (NMPPPManager *ppp_manager, /* Ignore PPP IP4 events that come in after initial configuration */ if (nm_device_activate_ip4_state_in_conf (device)) { - nm_device_set_ip_iface (device, iface); nm_device_activate_schedule_ip4_config_result (device, config); } } @@ -1012,6 +1021,9 @@ pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *out_ 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); @@ -1105,7 +1117,6 @@ dcb_state (NMDevice *device, gboolean timeout) g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG); - carrier = nm_platform_link_is_connected (nm_device_get_platform (device), nm_device_get_ifindex (device)); _LOGD (LOGD_DCB, "dcb_state() wait %d carrier %d timeout %d", priv->dcb_wait, carrier, timeout); @@ -1332,7 +1343,7 @@ deactivate (NMDevice *device) nm_clear_g_source (&priv->pppoe_wait_id); if (priv->ppp_manager) { - nm_ppp_manager_stop_sync (priv->ppp_manager); + nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL); g_clear_object (&priv->ppp_manager); } @@ -1361,7 +1372,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMSettingWired *s_wired; @@ -1425,7 +1436,9 @@ new_default_connection (NMDevice *self) NMConnection *connection; NMSettingsConnection *const*connections; NMSetting *setting; + struct udev_device *dev; const char *perm_hw_addr; + const char *uprop = "0"; gs_free char *defname = NULL; gs_free char *uuid = NULL; gs_free char *machine_id = NULL; @@ -1470,6 +1483,26 @@ new_default_connection (NMDevice *self) g_object_set (setting, NM_SETTING_WIRED_MAC_ADDRESS, perm_hw_addr, NULL); nm_connection_add_setting (connection, setting); + /* Check if we should create a Link-Local only connection */ + dev = nm_platform_link_get_udev_device (nm_device_get_platform (NM_DEVICE (self)), nm_device_get_ip_ifindex (self)); + if (dev) + uprop = udev_device_get_property_value (dev, "NM_AUTO_DEFAULT_LINK_LOCAL_ONLY"); + + if (nm_udev_utils_property_as_boolean (uprop)) { + setting = nm_setting_ip4_config_new (); + g_object_set (setting, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL, + NULL); + nm_connection_add_setting (connection, setting); + + setting = nm_setting_ip6_config_new (); + g_object_set (setting, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL, + NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE, + NULL); + nm_connection_add_setting (connection, setting); + } + return connection; } @@ -1701,10 +1734,28 @@ set_property (GObject *object, guint prop_id, } } +static const NMDBusInterfaceInfoExtended interface_info_device_wired = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_WIRED, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("PermHwAddress", "s", NM_DEVICE_PERM_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Speed", "u", NM_DEVICE_ETHERNET_SPEED), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("S390Subchannels", "as", NM_DEVICE_ETHERNET_S390_SUBCHANNELS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier", "b", NM_DEVICE_CARRIER), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); g_type_class_add_private (object_class, sizeof (NMDeviceEthernetPrivate)); @@ -1716,6 +1767,8 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) object_class->get_property = get_property; object_class->set_property = set_property; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_wired); + parent_class->get_generic_capabilities = get_generic_capabilities; parent_class->check_connection_compatible = check_connection_compatible; parent_class->complete_connection = complete_connection; @@ -1749,10 +1802,6 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_ETHERNET_SKELETON, - NULL); } /*****************************************************************************/ diff --git a/src/devices/nm-device-ethernet.h b/src/devices/nm-device-ethernet.h index a50a7d85..22fae293 100644 --- a/src/devices/nm-device-ethernet.h +++ b/src/devices/nm-device-ethernet.h @@ -24,12 +24,12 @@ #include "nm-device.h" -#define NM_TYPE_DEVICE_ETHERNET (nm_device_ethernet_get_type ()) -#define NM_DEVICE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernet)) -#define NM_DEVICE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass)) -#define NM_IS_DEVICE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_ETHERNET)) -#define NM_IS_DEVICE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_ETHERNET)) -#define NM_DEVICE_ETHERNET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass)) +#define NM_TYPE_DEVICE_ETHERNET (nm_device_ethernet_get_type ()) +#define NM_DEVICE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernet)) +#define NM_DEVICE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass)) +#define NM_IS_DEVICE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_ETHERNET)) +#define NM_IS_DEVICE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_ETHERNET)) +#define NM_DEVICE_ETHERNET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass)) #define NM_DEVICE_ETHERNET_SPEED "speed" #define NM_DEVICE_ETHERNET_S390_SUBCHANNELS "s390-subchannels" @@ -37,12 +37,12 @@ struct _NMDeviceEthernetPrivate; typedef struct { - NMDevice parent; - struct _NMDeviceEthernetPrivate *_priv; + NMDevice parent; + struct _NMDeviceEthernetPrivate *_priv; } NMDeviceEthernet; typedef struct { - NMDeviceClass parent; + NMDeviceClass parent; } NMDeviceEthernetClass; GType nm_device_ethernet_get_type (void); diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c index 97f011c5..d8e30346 100644 --- a/src/devices/nm-device-factory.c +++ b/src/devices/nm-device-factory.c @@ -340,42 +340,16 @@ factories_list_unref (GSList *list) g_slist_free_full (list, g_object_unref); } -void -nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc callback, - gpointer user_data) +static void +load_factories_from_dir (const char *dirname, + NMDeviceFactoryManagerFactoryFunc callback, + gpointer user_data) { NMDeviceFactory *factory; GError *error = NULL; char **path, **paths; - g_return_if_fail (factories_by_link == NULL); - g_return_if_fail (factories_by_setting == NULL); - - factories_by_link = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); - factories_by_setting = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, (GDestroyNotify) factories_list_unref); - -#define _ADD_INTERNAL(get_type_fcn) \ - G_STMT_START { \ - GType get_type_fcn (void); \ - _load_internal_factory (get_type_fcn (), \ - callback, user_data); \ - } G_STMT_END - - _ADD_INTERNAL (nm_bond_device_factory_get_type); - _ADD_INTERNAL (nm_bridge_device_factory_get_type); - _ADD_INTERNAL (nm_dummy_device_factory_get_type); - _ADD_INTERNAL (nm_ethernet_device_factory_get_type); - _ADD_INTERNAL (nm_infiniband_device_factory_get_type); - _ADD_INTERNAL (nm_ip_tunnel_device_factory_get_type); - _ADD_INTERNAL (nm_macsec_device_factory_get_type); - _ADD_INTERNAL (nm_macvlan_device_factory_get_type); - _ADD_INTERNAL (nm_ppp_device_factory_get_type); - _ADD_INTERNAL (nm_tun_device_factory_get_type); - _ADD_INTERNAL (nm_veth_device_factory_get_type); - _ADD_INTERNAL (nm_vlan_device_factory_get_type); - _ADD_INTERNAL (nm_vxlan_device_factory_get_type); - - paths = nm_utils_read_plugin_paths (NMPLUGINDIR, PLUGIN_PREFIX); + paths = nm_utils_read_plugin_paths (dirname, PLUGIN_PREFIX); if (!paths) return; @@ -420,3 +394,36 @@ nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc call g_strfreev (paths); } +void +nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc callback, + gpointer user_data) +{ + g_return_if_fail (factories_by_link == NULL); + g_return_if_fail (factories_by_setting == NULL); + + factories_by_link = g_hash_table_new_full (nm_direct_hash, NULL, NULL, g_object_unref); + factories_by_setting = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, (GDestroyNotify) factories_list_unref); + +#define _ADD_INTERNAL(get_type_fcn) \ + G_STMT_START { \ + GType get_type_fcn (void); \ + _load_internal_factory (get_type_fcn (), \ + callback, user_data); \ + } G_STMT_END + + _ADD_INTERNAL (nm_bond_device_factory_get_type); + _ADD_INTERNAL (nm_bridge_device_factory_get_type); + _ADD_INTERNAL (nm_dummy_device_factory_get_type); + _ADD_INTERNAL (nm_ethernet_device_factory_get_type); + _ADD_INTERNAL (nm_infiniband_device_factory_get_type); + _ADD_INTERNAL (nm_ip_tunnel_device_factory_get_type); + _ADD_INTERNAL (nm_macsec_device_factory_get_type); + _ADD_INTERNAL (nm_macvlan_device_factory_get_type); + _ADD_INTERNAL (nm_ppp_device_factory_get_type); + _ADD_INTERNAL (nm_tun_device_factory_get_type); + _ADD_INTERNAL (nm_veth_device_factory_get_type); + _ADD_INTERNAL (nm_vlan_device_factory_get_type); + _ADD_INTERNAL (nm_vxlan_device_factory_get_type); + + load_factories_from_dir (NMPLUGINDIR, callback, user_data); +} diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c index f6a670b2..27eaf8d6 100644 --- a/src/devices/nm-device-generic.c +++ b/src/devices/nm-device-generic.c @@ -26,8 +26,6 @@ #include "platform/nm-platform.h" #include "nm-core-internal.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Generic.h" - /*****************************************************************************/ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( @@ -203,10 +201,25 @@ dispose (GObject *object) G_OBJECT_CLASS (nm_device_generic_parent_class)->dispose (object); } +static const NMDBusInterfaceInfoExtended interface_info_device_generic = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_GENERIC, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("TypeDescription", "s", NM_DEVICE_GENERIC_TYPE_DESCRIPTION), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_generic_class_init (NMDeviceGenericClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_GENERIC_SETTING_NAME, NM_LINK_TYPE_ANY) @@ -216,6 +229,8 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass) object_class->get_property = get_property; object_class->set_property = set_property; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_generic); + parent_class->realize_start_notify = realize_start_notify; parent_class->get_generic_capabilities = get_generic_capabilities; parent_class->get_type_description = get_type_description; @@ -229,8 +244,4 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_GENERIC_SKELETON, - NULL); } diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index 09ad2855..781bbd69 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -32,8 +32,6 @@ #include "nm-device-factory.h" #include "nm-core-internal.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Infiniband.h" - #define NM_DEVICE_INFINIBAND_IS_PARTITION "is-partition" /*****************************************************************************/ @@ -177,7 +175,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMSettingInfiniband *s_infiniband; @@ -368,10 +366,25 @@ nm_device_infiniband_init (NMDeviceInfiniband * self) { } +static const NMDBusInterfaceInfoExtended interface_info_device_infiniband = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_INFINIBAND, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier", "b", NM_DEVICE_CARRIER), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_INFINIBAND_SETTING_NAME, NM_LINK_TYPE_INFINIBAND) @@ -379,6 +392,8 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass) object_class->get_property = get_property; object_class->set_property = set_property; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_infiniband); + parent_class->create_and_realize = create_and_realize; parent_class->unrealize = unrealize; parent_class->get_generic_capabilities = get_generic_capabilities; @@ -396,10 +411,6 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_INFINIBAND_SKELETON, - NULL); } /*****************************************************************************/ @@ -433,7 +444,7 @@ create_device (NMDeviceFactory *factory, NM_DEVICE_TYPE_DESC, "InfiniBand", NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND, NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_INFINIBAND, - /* XXX: Partition should probably be a different link type! */ + /* NOTE: Partition should probably be a different link type! */ NM_DEVICE_INFINIBAND_IS_PARTITION, is_partition, NULL); } diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c index af3cfe4c..59ca9ed5 100644 --- a/src/devices/nm-device-ip-tunnel.c +++ b/src/devices/nm-device-ip-tunnel.c @@ -37,8 +37,6 @@ #include "nm-act-request.h" #include "nm-ip4-config.h" -#include "introspection/org.freedesktop.NetworkManager.Device.IPTunnel.h" - #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceIPTunnel); @@ -55,6 +53,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceIPTunnel, PROP_OUTPUT_KEY, PROP_ENCAPSULATION_LIMIT, PROP_FLOW_LABEL, + PROP_FLAGS, ); typedef struct { @@ -69,6 +68,7 @@ typedef struct { char *output_key; guint8 encap_limit; guint32 flow_label; + NMIPTunnelFlags flags; } NMDeviceIPTunnelPrivate; struct _NMDeviceIPTunnel { @@ -86,6 +86,30 @@ G_DEFINE_TYPE (NMDeviceIPTunnel, nm_device_ip_tunnel, NM_TYPE_DEVICE) /*****************************************************************************/ +static guint32 +ip6tnl_flags_setting_to_plat (NMIPTunnelFlags flags) +{ + G_STATIC_ASSERT (NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT == IP6_TNL_F_IGN_ENCAP_LIMIT); + G_STATIC_ASSERT (NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS == IP6_TNL_F_USE_ORIG_TCLASS); + G_STATIC_ASSERT (NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL == IP6_TNL_F_USE_ORIG_FLOWLABEL); + G_STATIC_ASSERT (NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV == IP6_TNL_F_MIP6_DEV); + G_STATIC_ASSERT (NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY == IP6_TNL_F_RCV_DSCP_COPY); + G_STATIC_ASSERT (NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK == IP6_TNL_F_USE_ORIG_FWMARK); + + /* NOTE: "accidentally", the numeric values correspond. + * For flags added in the future, that might no longer + * be the case. */ + return flags & _NM_IP_TUNNEL_FLAG_ALL_IP6TNL; +} + +static NMIPTunnelFlags +ip6tnl_flags_plat_to_setting (guint32 flags) +{ + return flags & ((guint32) _NM_IP_TUNNEL_FLAG_ALL_IP6TNL); +} + +/*****************************************************************************/ + static gboolean address_equal_pp (int family, const char *a, const char *b) { @@ -129,6 +153,7 @@ update_properties_from_ifindex (NMDevice *device, int ifindex) guint8 ttl = 0, tos = 0, encap_limit = 0; gboolean pmtud = FALSE; guint32 flow_label = 0; + NMIPTunnelFlags flags = NM_IP_TUNNEL_FLAG_NONE; char *key; if (ifindex <= 0) { @@ -246,6 +271,7 @@ clear: tos = lnk->tclass; encap_limit = lnk->encap_limit; flow_label = lnk->flow_label; + flags = ip6tnl_flags_plat_to_setting (lnk->flags); } else g_return_if_reached (); @@ -307,6 +333,11 @@ out: priv->flow_label = flow_label; _notify (self, PROP_FLOW_LABEL); } + + if (priv->flags != flags) { + priv->flags = flags; + _notify (self, PROP_FLAGS); + } } static void @@ -327,7 +358,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMSettingIPTunnel *s_ip_tunnel; @@ -685,6 +716,7 @@ create_and_realize (NMDevice *device, lnk_ip6tnl.encap_limit = nm_setting_ip_tunnel_get_encapsulation_limit (s_ip_tunnel); lnk_ip6tnl.flow_label = nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel); lnk_ip6tnl.proto = nm_setting_ip_tunnel_get_mode (s_ip_tunnel) == NM_IP_TUNNEL_MODE_IPIP6 ? IPPROTO_IPIP : IPPROTO_IPV6; + lnk_ip6tnl.flags = ip6tnl_flags_setting_to_plat (nm_setting_ip_tunnel_get_flags (s_ip_tunnel)); plerr = nm_platform_link_ip6tnl_add (nm_device_get_platform (device), iface, &lnk_ip6tnl, out_plink); if (plerr != NM_PLATFORM_ERROR_SUCCESS) { @@ -814,6 +846,9 @@ get_property (GObject *object, guint prop_id, case PROP_FLOW_LABEL: g_value_set_uint (value, priv->flow_label); break; + case PROP_FLAGS: + g_value_set_uint (value, priv->flags); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -870,10 +905,35 @@ dispose (GObject *object) G_OBJECT_CLASS (nm_device_ip_tunnel_parent_class)->dispose (object); } +static const NMDBusInterfaceInfoExtended interface_info_device_ip_tunnel = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode", "u", NM_DEVICE_IP_TUNNEL_MODE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Parent", "o", NM_DEVICE_PARENT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Local", "s", NM_DEVICE_IP_TUNNEL_LOCAL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Remote", "s", NM_DEVICE_IP_TUNNEL_REMOTE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ttl", "y", NM_DEVICE_IP_TUNNEL_TTL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Tos", "y", NM_DEVICE_IP_TUNNEL_TOS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("PathMtuDiscovery", "b", NM_DEVICE_IP_TUNNEL_PATH_MTU_DISCOVERY), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("InputKey", "s", NM_DEVICE_IP_TUNNEL_INPUT_KEY), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("OutputKey", "s", NM_DEVICE_IP_TUNNEL_OUTPUT_KEY), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("EncapsulationLimit", "y", NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("FlowLabel", "u", NM_DEVICE_IP_TUNNEL_FLOW_LABEL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Flags", "u", NM_DEVICE_IP_TUNNEL_FLAGS), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); object_class->constructed = constructed; @@ -881,6 +941,8 @@ nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass) object_class->get_property = get_property; object_class->set_property = set_property; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ip_tunnel); + device_class->link_changed = link_changed; device_class->can_reapply_change = can_reapply_change; device_class->complete_connection = complete_connection; @@ -959,11 +1021,13 @@ nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); + obj_properties[PROP_FLAGS] = + g_param_spec_uint (NM_DEVICE_IP_TUNNEL_FLAGS, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_IPTUNNEL_SKELETON, - NULL); + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); } /*****************************************************************************/ diff --git a/src/devices/nm-device-ip-tunnel.h b/src/devices/nm-device-ip-tunnel.h index 4bff6e33..1109ace4 100644 --- a/src/devices/nm-device-ip-tunnel.h +++ b/src/devices/nm-device-ip-tunnel.h @@ -41,9 +41,7 @@ #define NM_DEVICE_IP_TUNNEL_OUTPUT_KEY "output-key" #define NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT "encapsulation-limit" #define NM_DEVICE_IP_TUNNEL_FLOW_LABEL "flow-label" - -/* defined in the parent class, but exposed on D-Bus by the subclass. */ -#define NM_DEVICE_IP_TUNNEL_PARENT NM_DEVICE_PARENT +#define NM_DEVICE_IP_TUNNEL_FLAGS "flags" typedef struct _NMDeviceIPTunnel NMDeviceIPTunnel; typedef struct _NMDeviceIPTunnelClass NMDeviceIPTunnelClass; diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c index 5ef8f938..4eaf5469 100644 --- a/src/devices/nm-device-macsec.c +++ b/src/devices/nm-device-macsec.c @@ -33,8 +33,6 @@ #include "supplicant/nm-supplicant-interface.h" #include "supplicant/nm-supplicant-config.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Macsec.h" - #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceMacsec); @@ -186,8 +184,12 @@ update_properties (NMDevice *device) nm_device_parent_set_ifindex (device, props->parent_ifindex); #define CHECK_PROPERTY_CHANGED(field, prop) \ - if (props->field != priv->props.field) \ - _notify (self, prop) + G_STMT_START { \ + if (priv->props.field != props->field) { \ + priv->props.field = props->field; \ + _notify (self, prop); \ + } \ + } G_STMT_END CHECK_PROPERTY_CHANGED (sci, PROP_SCI); CHECK_PROPERTY_CHANGED (cipher_suite, PROP_CIPHER_SUITE); @@ -202,7 +204,6 @@ update_properties (NMDevice *device) CHECK_PROPERTY_CHANGED (scb, PROP_SCB); CHECK_PROPERTY_CHANGED (replay_protect, PROP_REPLAY_PROTECT); - priv->props = *props; g_object_thaw_notify ((GObject *) device); } @@ -222,7 +223,7 @@ build_supplicant_config (NMDeviceMacsec *self, GError **error) mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)), nm_device_get_ifindex (NM_DEVICE (self))); - config = nm_supplicant_config_new (); + config = nm_supplicant_config_new (FALSE, FALSE); s_macsec = (NMSettingMacsec *) nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_MACSEC); @@ -477,12 +478,9 @@ handle_auth_or_fail (NMDeviceMacsec *self, NMActRequest *req, gboolean new_secrets) { - NMDeviceMacsecPrivate *priv; const char *setting_name; NMConnection *applied_connection; - priv = NM_DEVICE_MACSEC_GET_PRIVATE (self); - if (!nm_device_auth_retries_try_next (NM_DEVICE (self))) return NM_ACT_STAGE_RETURN_FAILURE; @@ -492,13 +490,14 @@ handle_auth_or_fail (NMDeviceMacsec *self, applied_connection = nm_act_request_get_applied_connection (req); setting_name = nm_connection_need_secrets (applied_connection, NULL); - if (setting_name) { - macsec_secrets_get_secrets (self, setting_name, - NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION - | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0)); - } else + if (!setting_name) { _LOGI (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets."); + return NM_ACT_STAGE_RETURN_FAILURE; + } + macsec_secrets_get_secrets (self, setting_name, + NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION + | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0)); return NM_ACT_STAGE_RETURN_POSTPONE; } @@ -732,7 +731,6 @@ link_changed (NMDevice *device, update_properties (device); } - static void device_state_changed (NMDevice *device, NMDeviceState new_state, @@ -812,10 +810,36 @@ dispose (GObject *object) G_OBJECT_CLASS (nm_device_macsec_parent_class)->dispose (object); } +static const NMDBusInterfaceInfoExtended interface_info_device_macsec = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_MACSEC, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Parent", "o", NM_DEVICE_PARENT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Sci", "t", NM_DEVICE_MACSEC_SCI), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("IcvLength", "y", NM_DEVICE_MACSEC_ICV_LENGTH), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("CipherSuite", "t", NM_DEVICE_MACSEC_CIPHER_SUITE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Window", "u", NM_DEVICE_MACSEC_WINDOW), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("EncodingSa", "y", NM_DEVICE_MACSEC_ENCODING_SA), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Validation", "s", NM_DEVICE_MACSEC_VALIDATION), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Encrypt", "b", NM_DEVICE_MACSEC_ENCRYPT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Protect", "b", NM_DEVICE_MACSEC_PROTECT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("IncludeSci", "b", NM_DEVICE_MACSEC_INCLUDE_SCI), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Es", "b", NM_DEVICE_MACSEC_ES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Scb", "b", NM_DEVICE_MACSEC_SCB), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ReplayProtect", "b", NM_DEVICE_MACSEC_REPLAY_PROTECT), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_macsec_class_init (NMDeviceMacsecClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_MACSEC) @@ -823,6 +847,8 @@ nm_device_macsec_class_init (NMDeviceMacsecClass *klass) object_class->get_property = get_property; object_class->dispose = dispose; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_macsec); + parent_class->act_stage2_config = act_stage2_config; parent_class->check_connection_compatible = check_connection_compatible; parent_class->create_and_realize = create_and_realize; @@ -886,10 +912,6 @@ nm_device_macsec_class_init (NMDeviceMacsecClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_MACSEC_SKELETON, - NULL); } /*************************************************************/ diff --git a/src/devices/nm-device-macsec.h b/src/devices/nm-device-macsec.h index 17b33bf5..23e9d2c9 100644 --- a/src/devices/nm-device-macsec.h +++ b/src/devices/nm-device-macsec.h @@ -43,9 +43,6 @@ #define NM_DEVICE_MACSEC_SCB "scb" #define NM_DEVICE_MACSEC_REPLAY_PROTECT "replay-protect" -/* defined in the parent class, but exposed on D-Bus by the subclass. */ -#define NM_DEVICE_MACSEC_PARENT NM_DEVICE_PARENT - typedef struct _NMDeviceMacsec NMDeviceMacsec; typedef struct _NMDeviceMacsecClass NMDeviceMacsecClass; diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index 2a461543..dc4fb3e8 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -36,8 +36,6 @@ #include "nm-ip4-config.h" #include "nm-utils.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Macvlan.h" - #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceMacvlan); @@ -197,12 +195,17 @@ update_properties (NMDevice *device) g_object_freeze_notify (object); nm_device_parent_set_ifindex (device, plink->parent); - if (priv->props.mode != props->mode) - _notify (self, PROP_MODE); - if (priv->props.no_promisc != props->no_promisc) - _notify (self, PROP_NO_PROMISC); - priv->props = *props; +#define CHECK_PROPERTY_CHANGED(field, prop) \ + G_STMT_START { \ + if (priv->props.field != props->field) { \ + priv->props.field = props->field; \ + _notify (self, prop); \ + } \ + } G_STMT_END + + CHECK_PROPERTY_CHANGED (mode, PROP_MODE); + CHECK_PROPERTY_CHANGED (no_promisc, PROP_NO_PROMISC); g_object_thaw_notify (object); } @@ -331,7 +334,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMSettingMacvlan *s_macvlan; @@ -386,7 +389,6 @@ update_connection (NMDevice *device, NMConnection *connection) if (priv->props.no_promisc == nm_setting_macvlan_get_promiscuous (s_macvlan)) g_object_set (s_macvlan, NM_SETTING_MACVLAN_PROMISCUOUS, !priv->props.no_promisc, NULL); - if (priv->props.tap != nm_setting_macvlan_get_tap (s_macvlan)) g_object_set (s_macvlan, NM_SETTING_MACVLAN_TAP, !!priv->props.tap, NULL); @@ -470,10 +472,27 @@ nm_device_macvlan_init (NMDeviceMacvlan *self) { } +static const NMDBusInterfaceInfoExtended interface_info_device_macvlan = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_MACVLAN, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Parent", "o", NM_DEVICE_PARENT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode", "s", NM_DEVICE_MACVLAN_MODE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("NoPromisc", "b", NM_DEVICE_MACVLAN_NO_PROMISC), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Tab", "b", NM_DEVICE_MACVLAN_TAP), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP) @@ -481,6 +500,8 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass) object_class->get_property = get_property; object_class->set_property = set_property; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_macvlan); + device_class->act_stage1_prepare = act_stage1_prepare; device_class->check_connection_compatible = check_connection_compatible; device_class->complete_connection = complete_connection; @@ -513,10 +534,6 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_MACVLAN_SKELETON, - NULL); } /*****************************************************************************/ diff --git a/src/devices/nm-device-macvlan.h b/src/devices/nm-device-macvlan.h index e6d3a333..c7d4be7c 100644 --- a/src/devices/nm-device-macvlan.h +++ b/src/devices/nm-device-macvlan.h @@ -34,9 +34,6 @@ #define NM_DEVICE_MACVLAN_NO_PROMISC "no-promisc" #define NM_DEVICE_MACVLAN_TAP "tap" -/* defined in the parent class, but exposed on D-Bus by the subclass. */ -#define NM_DEVICE_MACVLAN_PARENT NM_DEVICE_PARENT - typedef struct _NMDeviceMacvlan NMDeviceMacvlan; typedef struct _NMDeviceMacvlanClass NMDeviceMacvlanClass; diff --git a/src/devices/nm-device-ppp.c b/src/devices/nm-device-ppp.c index 639ec44a..a6abb228 100644 --- a/src/devices/nm-device-ppp.c +++ b/src/devices/nm-device-ppp.c @@ -26,8 +26,6 @@ #include "ppp/nm-ppp-manager-call.h" #include "ppp/nm-ppp-status.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Ppp.h" - #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDevicePpp); @@ -35,8 +33,7 @@ _LOG_DECLARE_SELF(NMDevicePpp); typedef struct _NMDevicePppPrivate { NMPPPManager *ppp_manager; - NMIP4Config *pending_ip4_config; - char *pending_ifname; + NMIP4Config *ip4_config; } NMDevicePppPrivate; struct _NMDevicePpp { @@ -88,46 +85,52 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_ case NM_PPP_STATUS_DEAD: nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED); break; - case NM_PPP_STATUS_RUNNING: - nm_device_activate_schedule_stage3_ip_config_start (device); - 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); + gs_free char *old_name = NULL; + + if (!nm_device_take_over_link (device, ifindex, &old_name)) { + 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 (NMPPPManager *ppp_manager, - const char *iface, NMIP4Config *config, gpointer user_data) { NMDevice *device = NM_DEVICE (user_data); NMDevicePpp *self = NM_DEVICE_PPP (device); NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self); - gboolean renamed; _LOGT (LOGD_DEVICE | LOGD_PPP, "received IPv4 config from pppd"); if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) { if (nm_device_activate_ip4_state_in_conf (device)) { - if (!nm_device_take_over_link (device, iface, &renamed)) { - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return; - } - if (renamed) - nm_manager_remove_device (nm_manager_get (), iface, NM_DEVICE_TYPE_PPP); - nm_device_activate_schedule_ip4_config_result (device, config); return; } } else { - if (priv->pending_ip4_config) - g_object_unref (priv->pending_ip4_config); - priv->pending_ip4_config = g_object_ref (config); - g_free (priv->pending_ifname); - priv->pending_ifname = g_strdup (iface); + if (priv->ip4_config) + g_object_unref (priv->ip4_config); + priv->ip4_config = g_object_ref (config); } } @@ -146,8 +149,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) s_pppoe = (NMSettingPppoe *) nm_device_get_applied_setting ((NMDevice *) self, NM_TYPE_SETTING_PPPOE); g_return_val_if_fail (s_pppoe, NM_ACT_STAGE_RETURN_FAILURE); - g_clear_object (&priv->pending_ip4_config); - nm_clear_g_free (&priv->pending_ifname); + g_clear_object (&priv->ip4_config); priv->ppp_manager = nm_ppp_manager_create (nm_setting_pppoe_get_parent (s_pppoe), &error); @@ -175,6 +177,9 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) 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); @@ -189,17 +194,12 @@ act_stage3_ip4_config_start (NMDevice *device, { NMDevicePpp *self = NM_DEVICE_PPP (device); NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self); - gboolean renamed; - if (priv->pending_ip4_config) { - if (!nm_device_take_over_link (device, priv->pending_ifname, &renamed)) - return NM_ACT_STAGE_RETURN_FAILURE; - if (renamed) - nm_manager_remove_device (nm_manager_get (), priv->pending_ifname, NM_DEVICE_TYPE_PPP); + if (priv->ip4_config) { if (out_config) - *out_config = g_steal_pointer (&priv->pending_ip4_config); + *out_config = g_steal_pointer (&priv->ip4_config); else - g_clear_object (&priv->pending_ip4_config); + g_clear_object (&priv->ip4_config); return NM_ACT_STAGE_RETURN_SUCCESS; } @@ -207,14 +207,6 @@ act_stage3_ip4_config_start (NMDevice *device, return NM_ACT_STAGE_RETURN_POSTPONE; } -static NMActStageReturn -act_stage3_ip6_config_start (NMDevice *self, - NMIP6Config **out_config, - NMDeviceStateReason *out_failure_reason) -{ - return NM_ACT_STAGE_RETURN_IP_FAIL; -} - static gboolean create_and_realize (NMDevice *device, NMConnection *connection, @@ -247,7 +239,7 @@ deactivate (NMDevice *device) NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self); if (priv->ppp_manager) { - nm_ppp_manager_stop_sync (priv->ppp_manager); + nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL); g_clear_object (&priv->ppp_manager); } } @@ -263,33 +255,40 @@ dispose (GObject *object) NMDevicePpp *self = NM_DEVICE_PPP (object); NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self); - g_clear_object (&priv->pending_ip4_config); - nm_clear_g_free (&priv->pending_ifname); + g_clear_object (&priv->ip4_config); G_OBJECT_CLASS (nm_device_ppp_parent_class)->dispose (object); } +static const NMDBusInterfaceInfoExtended interface_info_device_ppp = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_PPP, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_ppp_class_init (NMDevicePppClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_PPPOE_SETTING_NAME, NM_LINK_TYPE_PPP) object_class->dispose = dispose; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ppp); + parent_class->act_stage2_config = act_stage2_config; parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; - parent_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; parent_class->check_connection_compatible = check_connection_compatible; parent_class->create_and_realize = create_and_realize; parent_class->deactivate = deactivate; parent_class->get_generic_capabilities = get_generic_capabilities; - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_PPP_SKELETON, - NULL); } /*****************************************************************************/ diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index f1486c54..0b844411 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -43,8 +43,12 @@ enum NMActStageReturn { #define NM_DEVICE_CAP_INTERNAL_MASK 0xc0000000 +void nm_device_arp_announce (NMDevice *self); + NMSettings *nm_device_get_settings (NMDevice *self); +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); @@ -57,7 +61,7 @@ gboolean nm_device_bring_up (NMDevice *self, gboolean wait, gboolean *no_firmwar void nm_device_take_down (NMDevice *self, gboolean block); -gboolean nm_device_take_over_link (NMDevice *self, const char *ifname, gboolean *renamed); +gboolean nm_device_take_over_link (NMDevice *self, int ifindex, char **old_name); gboolean nm_device_hw_addr_set (NMDevice *device, const char *addr, @@ -139,4 +143,4 @@ gboolean nm_device_match_hwaddr (NMDevice *device, NMConnection *connection, gboolean fail_if_no_hwaddr); -#endif /* NM_DEVICE_PRIVATE_H */ +#endif /* NM_DEVICE_PRIVATE_H */ diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index a7d7c0bf..3846676c 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "nm-act-request.h" #include "nm-device-private.h" @@ -34,8 +35,6 @@ #include "nm-setting-tun.h" #include "nm-core-internal.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Tun.h" - #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceTun); @@ -51,8 +50,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceTun, ); typedef struct { - NMPlatformTunProperties props; - const char *mode; + NMPlatformLnkTun props; } NMDeviceTunPrivate; struct _NMDeviceTun { @@ -71,48 +69,62 @@ G_DEFINE_TYPE (NMDeviceTun, nm_device_tun, NM_TYPE_DEVICE) /*****************************************************************************/ static void -update_properties (NMDeviceTun *self) +update_properties_from_struct (NMDeviceTun *self, + const NMPlatformLnkTun *props) { NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); - GObject *object = G_OBJECT (self); - NMPlatformTunProperties props; + const NMPlatformLnkTun props0 = { }; + + if (!props) { + /* allow passing %NULL to reset all properties. */ + props = &props0; + } + + g_object_freeze_notify (G_OBJECT (self)); + +#define CHECK_PROPERTY_CHANGED_VALID(field, prop) \ + G_STMT_START { \ + if ( priv->props.field != props->field \ + || priv->props.field##_valid != props->field##_valid) { \ + priv->props.field##_valid = props->field##_valid; \ + priv->props.field = props->field; \ + _notify (self, prop); \ + } \ + } G_STMT_END + +#define CHECK_PROPERTY_CHANGED(field, prop) \ + G_STMT_START { \ + if (priv->props.field != props->field) { \ + priv->props.field = props->field; \ + _notify (self, prop); \ + } \ + } G_STMT_END + + CHECK_PROPERTY_CHANGED_VALID (owner, PROP_OWNER); + CHECK_PROPERTY_CHANGED_VALID (group, PROP_GROUP); + CHECK_PROPERTY_CHANGED (type, PROP_MODE); + CHECK_PROPERTY_CHANGED (pi, PROP_NO_PI); + CHECK_PROPERTY_CHANGED (vnet_hdr, PROP_VNET_HDR); + CHECK_PROPERTY_CHANGED (multi_queue, PROP_MULTI_QUEUE); + + g_object_thaw_notify (G_OBJECT (self)); +} + +static void +update_properties (NMDeviceTun *self) +{ + NMPlatformLnkTun props_storage; + const NMPlatformLnkTun *props = NULL; int ifindex; ifindex = nm_device_get_ifindex (NM_DEVICE (self)); - if (ifindex > 0) { - if (!nm_platform_link_tun_get_properties (nm_device_get_platform (NM_DEVICE (self)), ifindex, &props)) { - _LOGD (LOGD_DEVICE, "tun-properties: cannot loading tun properties from platform for ifindex %d", ifindex); - ifindex = 0; - } else if (g_strcmp0 (priv->mode, props.mode) != 0) { - /* if the mode differs, we ignore what we loaded. A NMDeviceTun cannot - * change the mode after construction. */ - _LOGD (LOGD_DEVICE, "tun-properties: loading tun properties yielded tun-mode %s%s%s, but %s%s%s expected (ifindex %d)", - NM_PRINT_FMT_QUOTE_STRING (props.mode), - NM_PRINT_FMT_QUOTE_STRING (priv->mode), - ifindex); - ifindex = 0; - } - } else - _LOGD (LOGD_DEVICE, "tun-properties: ignore loading properties due to missing ifindex"); - if (ifindex <= 0) - memset (&props, 0, sizeof (props)); - - g_object_freeze_notify (object); - - if (priv->props.owner != props.owner) - _notify (self, PROP_OWNER); - if (priv->props.group != props.group) - _notify (self, PROP_GROUP); - if (priv->props.no_pi != props.no_pi) - _notify (self, PROP_NO_PI); - if (priv->props.vnet_hdr != props.vnet_hdr) - _notify (self, PROP_VNET_HDR); - if (priv->props.multi_queue != props.multi_queue) - _notify (self, PROP_MULTI_QUEUE); - - memcpy (&priv->props, &props, sizeof (NMPlatformTunProperties)); - - g_object_thaw_notify (object); + if ( ifindex > 0 + && nm_platform_link_tun_get_properties (nm_device_get_platform (NM_DEVICE (self)), + ifindex, + &props_storage)) + props = &props_storage; + + update_properties_from_struct (self, props); } static NMDeviceCapabilities @@ -133,7 +145,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMSettingTun *s_tun; @@ -157,61 +169,57 @@ complete_connection (NMDevice *device, return TRUE; } -static int -tun_mode_from_string (const char *string) -{ - if (!g_strcmp0 (string, "tap")) - return NM_SETTING_TUN_MODE_TAP; - else - return NM_SETTING_TUN_MODE_TUN; -} - static void update_connection (NMDevice *device, NMConnection *connection) { NMDeviceTun *self = NM_DEVICE_TUN (device); - NMSettingTun *s_tun = nm_connection_get_setting_tun (connection); - NMPlatformTunProperties props; + NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); + NMSettingTun *s_tun; NMSettingTunMode mode; - gint64 user, group; - char *str; + char s_buf[100]; + const char *str; - if (!s_tun) { - s_tun = (NMSettingTun *) nm_setting_tun_new (); - nm_connection_add_setting (connection, (NMSetting *) s_tun); - } + /* Note: since we read tun properties from sysctl for older kernels, + * we don't get proper change notifications. Make sure that all our + * tun properties are up to date at this point. We should not do this, + * if we would entirely rely on netlink events. */ + update_properties (NM_DEVICE_TUN (device)); - if (!nm_platform_link_tun_get_properties (nm_device_get_platform (device), nm_device_get_ifindex (device), &props)) { - _LOGW (LOGD_PLATFORM, "failed to get TUN interface info while updating connection."); + switch (priv->props.type) { + case IFF_TUN: mode = NM_SETTING_TUN_MODE_TUN; break; + case IFF_TAP: mode = NM_SETTING_TUN_MODE_TAP; break; + default: + /* Huh? */ return; } - mode = tun_mode_from_string (props.mode); + s_tun = nm_connection_get_setting_tun (connection); + if (!s_tun) { + s_tun = (NMSettingTun *) nm_setting_tun_new (); + nm_connection_add_setting (connection, (NMSetting *) s_tun); + } if (mode != nm_setting_tun_get_mode (s_tun)) - g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_MODE, mode, NULL); - - user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1); - group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1); + g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_MODE, (guint) mode, NULL); - if (props.owner != user) { - str = props.owner >= 0 ? g_strdup_printf ("%" G_GINT32_FORMAT, (gint32) props.owner) : NULL; + str = priv->props.owner_valid + ? nm_sprintf_buf (s_buf, "%" G_GINT32_FORMAT, priv->props.owner) + : NULL; + if (!nm_streq0 (str, nm_setting_tun_get_owner (s_tun))) g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_OWNER, str, NULL); - g_free (str); - } - if (props.group != group) { - str = props.group >= 0 ? g_strdup_printf ("%" G_GINT32_FORMAT, (gint32) props.group) : NULL; + str = priv->props.group_valid + ? nm_sprintf_buf (s_buf, "%" G_GINT32_FORMAT, priv->props.group) + : NULL; + if (!nm_streq0 (str, nm_setting_tun_get_group (s_tun))) g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_GROUP, str, NULL); - g_free (str); - } - if ((!props.no_pi) != nm_setting_tun_get_pi (s_tun)) - g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_PI, !props.no_pi, NULL); - if (props.vnet_hdr != nm_setting_tun_get_vnet_hdr (s_tun)) - g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_VNET_HDR, props.vnet_hdr, NULL); - if (props.multi_queue != nm_setting_tun_get_multi_queue (s_tun)) - g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_MULTI_QUEUE, props.multi_queue, NULL); + if (priv->props.pi != nm_setting_tun_get_pi (s_tun)) + g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_PI, (gboolean) priv->props.pi, NULL); + if (priv->props.vnet_hdr != nm_setting_tun_get_vnet_hdr (s_tun)) + g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_VNET_HDR, (gboolean) priv->props.vnet_hdr, NULL); + if (priv->props.multi_queue != nm_setting_tun_get_multi_queue (s_tun)) + g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_MULTI_QUEUE, (gboolean) priv->props.multi_queue, NULL); } static gboolean @@ -222,23 +230,42 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); + NMPlatformLnkTun props = { }; NMPlatformError plerr; NMSettingTun *s_tun; - gint64 user, group; + gint64 owner, group; s_tun = nm_connection_get_setting_tun (connection); - g_assert (s_tun); + g_return_val_if_fail (s_tun, FALSE); + + switch (nm_setting_tun_get_mode (s_tun)) { + case NM_SETTING_TUN_MODE_TAP: props.type = IFF_TAP; break; + case NM_SETTING_TUN_MODE_TUN: props.type = IFF_TUN; break; + default: + g_return_val_if_reached (FALSE); + } - user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1); + owner = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1); + if (owner != -1) { + props.owner_valid = TRUE; + props.owner = owner; + } group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1); + if (group != -1) { + props.group_valid = TRUE; + props.group = group; + } - plerr = nm_platform_link_tun_add (nm_device_get_platform (device), iface, - nm_setting_tun_get_mode (s_tun) == NM_SETTING_TUN_MODE_TAP, - user, group, - nm_setting_tun_get_pi (s_tun), - nm_setting_tun_get_vnet_hdr (s_tun), - nm_setting_tun_get_multi_queue (s_tun), - out_plink); + props.pi = nm_setting_tun_get_pi (s_tun); + props.vnet_hdr = nm_setting_tun_get_vnet_hdr (s_tun); + props.multi_queue = nm_setting_tun_get_multi_queue (s_tun); + props.persist = TRUE; + + plerr = nm_platform_link_tun_add (nm_device_get_platform (device), + iface, + &props, + out_plink, + NULL); if (plerr != NM_PLATFORM_ERROR_SUCCESS) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create TUN/TAP interface '%s' for '%s': %s", @@ -251,6 +278,16 @@ create_and_realize (NMDevice *device, return TRUE; } +static gboolean +_same_og (const char *str, gboolean og_valid, guint32 og_num) +{ + gint64 v; + + v = _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXINT32, -1); + return (!og_valid && ( v == (gint64) -1)) + || ( og_valid && (((guint32) v) == og_num )); +} + static gboolean check_connection_compatible (NMDevice *device, NMConnection *connection) { @@ -258,7 +295,6 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); NMSettingTunMode mode; NMSettingTun *s_tun; - gint64 user, group; if (!NM_DEVICE_CLASS (nm_device_tun_parent_class)->check_connection_compatible (device, connection)) return FALSE; @@ -268,18 +304,21 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) return FALSE; if (nm_device_is_real (device)) { - mode = tun_mode_from_string (priv->mode); - if (mode != nm_setting_tun_get_mode (s_tun)) + switch (priv->props.type) { + case IFF_TUN: mode = NM_SETTING_TUN_MODE_TUN; break; + case IFF_TAP: mode = NM_SETTING_TUN_MODE_TAP; break; + default: + /* Huh? */ return FALSE; + } - user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1); - group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1); - - if (user != priv->props.owner) + if (mode != nm_setting_tun_get_mode (s_tun)) + return FALSE; + if (!_same_og (nm_setting_tun_get_owner (s_tun), priv->props.owner_valid, priv->props.owner)) return FALSE; - if (group != priv->props.group) + if (!_same_og (nm_setting_tun_get_group (s_tun), priv->props.group_valid, priv->props.group)) return FALSE; - if (nm_setting_tun_get_pi (s_tun) == priv->props.no_pi) + if (nm_setting_tun_get_pi (s_tun) != priv->props.pi) return FALSE; if (nm_setting_tun_get_vnet_hdr (s_tun) != priv->props.vnet_hdr) return FALSE; @@ -302,7 +341,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) return ret; /* Nothing to do for TUN devices */ - if (g_strcmp0 (priv->mode, "tap")) + if (priv->props.type == IFF_TUN) return NM_ACT_STAGE_RETURN_SUCCESS; if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE)) @@ -314,16 +353,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) static void unrealize_notify (NMDevice *device) { - NMDeviceTun *self = NM_DEVICE_TUN (device); - NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); - guint i; - NM_DEVICE_CLASS (nm_device_tun_parent_class)->unrealize_notify (device); - - memset (&priv->props, 0, sizeof (NMPlatformTunProperties)); - - for (i = 1; i < _PROPERTY_ENUMS_LAST; i++) - g_object_notify_by_pspec ((GObject *) self, obj_properties[i]); + update_properties_from_struct (NM_DEVICE_TUN (device), NULL); } /*****************************************************************************/ @@ -334,19 +365,25 @@ get_property (GObject *object, guint prop_id, { NMDeviceTun *self = NM_DEVICE_TUN (object); NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); + const char *s; switch (prop_id) { case PROP_OWNER: - g_value_set_int64 (value, priv->props.owner); + g_value_set_int64 (value, priv->props.owner_valid ? (gint64) priv->props.owner : (gint64) -1); break; case PROP_GROUP: - g_value_set_int64 (value, priv->props.group); + g_value_set_int64 (value, priv->props.group_valid ? (gint64) priv->props.group : (gint64) -1); break; case PROP_MODE: - g_value_set_string (value, priv->mode); + switch (priv->props.type) { + case IFF_TUN: s = "tun"; break; + case IFF_TAP: s = "tap"; break; + default: s = NULL; break; + } + g_value_set_static_string (value, s); break; case PROP_NO_PI: - g_value_set_boolean (value, priv->props.no_pi); + g_value_set_boolean (value, !priv->props.pi); break; case PROP_VNET_HDR: g_value_set_boolean (value, priv->props.vnet_hdr); @@ -360,33 +397,6 @@ get_property (GObject *object, guint prop_id, } } -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMDeviceTun *self = NM_DEVICE_TUN (object); - NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); - const char *str; - - switch (prop_id) { - case PROP_MODE: - /* construct-only */ - str = g_value_get_string (value); - - /* mode is G_PARAM_STATIC_STRINGS */ - if (g_strcmp0 (str, "tun") == 0) - priv->mode = "tun"; - else if (g_strcmp0 (str, "tap") == 0) - priv->mode = "tap"; - else - g_return_if_fail (FALSE); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - /*****************************************************************************/ static void @@ -394,16 +404,37 @@ nm_device_tun_init (NMDeviceTun *self) { } +static const NMDBusInterfaceInfoExtended interface_info_device_tun = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_TUN, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Owner", "x", NM_DEVICE_TUN_OWNER), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Group", "x", NM_DEVICE_TUN_GROUP), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode", "s", NM_DEVICE_TUN_MODE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("NoPi", "b", NM_DEVICE_TUN_NO_PI), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("VnetHdr", "b", NM_DEVICE_TUN_VNET_HDR), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("MultiQueue", "b", NM_DEVICE_TUN_MULTI_QUEUE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_tun_class_init (NMDeviceTunClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); - NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_TUN, NM_LINK_TYPE_TAP) + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_TUN) object_class->get_property = get_property; - object_class->set_property = set_property; + + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_tun); device_class->connection_type = NM_SETTING_TUN_SETTING_NAME; device_class->link_changed = link_changed; @@ -428,9 +459,8 @@ nm_device_tun_class_init (NMDeviceTunClass *klass) obj_properties[PROP_MODE] = g_param_spec_string (NM_DEVICE_TUN_MODE, "", "", - "tun", - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_NO_PI] = g_param_spec_boolean (NM_DEVICE_TUN_NO_PI, "", "", @@ -448,13 +478,8 @@ nm_device_tun_class_init (NMDeviceTunClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_TUN_SKELETON, - NULL); } - /*****************************************************************************/ #define NM_TYPE_TUN_DEVICE_FACTORY (nm_tun_device_factory_get_type ()) @@ -467,42 +492,19 @@ create_device (NMDeviceFactory *factory, NMConnection *connection, gboolean *out_ignore) { - NMSettingTun *s_tun; - NMLinkType link_type = NM_LINK_TYPE_UNKNOWN; - const char *mode; - - if (plink) { - link_type = plink->type; - } else if (connection) { - s_tun = nm_connection_get_setting_tun (connection); - if (!s_tun) - return NULL; - switch (nm_setting_tun_get_mode (s_tun)) { - case NM_SETTING_TUN_MODE_TUN: - link_type = NM_LINK_TYPE_TUN; - break; - case NM_SETTING_TUN_MODE_TAP: - link_type = NM_LINK_TYPE_TAP; - break; - case NM_SETTING_TUN_MODE_UNKNOWN: - g_return_val_if_reached (NULL); - } - } - - g_return_val_if_fail (link_type != NM_LINK_TYPE_UNKNOWN, NULL); - mode = link_type == NM_LINK_TYPE_TUN ? "tun" : "tap"; + g_return_val_if_fail (!plink || plink->type == NM_LINK_TYPE_TUN, NULL); + g_return_val_if_fail (!connection || nm_streq0 (nm_connection_get_connection_type (connection), NM_SETTING_TUN_SETTING_NAME), NULL); return (NMDevice *) g_object_new (NM_TYPE_DEVICE_TUN, NM_DEVICE_IFACE, iface, NM_DEVICE_TYPE_DESC, "Tun", NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_TUN, - NM_DEVICE_LINK_TYPE, link_type, - NM_DEVICE_TUN_MODE, mode, + NM_DEVICE_LINK_TYPE, (guint) NM_LINK_TYPE_TUN, NULL); } NM_DEVICE_FACTORY_DEFINE_INTERNAL (TUN, Tun, tun, - NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_TUN, NM_LINK_TYPE_TAP) + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_TUN) NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_TUN_SETTING_NAME), factory_class->create_device = create_device; ); diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c index a8c4bcc8..186173eb 100644 --- a/src/devices/nm-device-veth.c +++ b/src/devices/nm-device-veth.c @@ -30,8 +30,6 @@ #include "platform/nm-platform.h" #include "nm-device-factory.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Veth.h" - #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceVeth); @@ -101,11 +99,18 @@ nm_device_veth_init (NMDeviceVeth *self) } static void -notify (GObject *object, GParamSpec *pspec) +parent_changed_notify (NMDevice *device, + int old_ifindex, + NMDevice *old_parent, + int new_ifindex, + NMDevice *new_parent) { - if (nm_streq (pspec->name, NM_DEVICE_PARENT)) - _notify (NM_DEVICE_VETH (object), PROP_PEER); - G_OBJECT_CLASS (nm_device_veth_parent_class)->notify (object, pspec); + NM_DEVICE_CLASS (nm_device_veth_parent_class)->parent_changed_notify (device, + old_ifindex, + old_parent, + new_ifindex, + new_parent); + _notify (NM_DEVICE_VETH (device), PROP_PEER); } static void @@ -120,7 +125,7 @@ get_property (GObject *object, guint prop_id, peer = nm_device_parent_get_device (NM_DEVICE (self)); if (peer && !NM_IS_DEVICE_VETH (peer)) peer = NULL; - nm_utils_g_value_set_object_path (value, peer); + nm_dbus_utils_g_value_set_object_path (value, peer); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -128,19 +133,35 @@ get_property (GObject *object, guint prop_id, } } +static const NMDBusInterfaceInfoExtended interface_info_device_veth = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_VETH, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Peer", "o", NM_DEVICE_VETH_PEER), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_veth_class_init (NMDeviceVethClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_VETH) object_class->get_property = get_property; - object_class->notify = notify; + + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_veth); device_class->can_unmanaged_external_down = can_unmanaged_external_down; device_class->link_changed = link_changed; + device_class->parent_changed_notify = parent_changed_notify; obj_properties[PROP_PEER] = g_param_spec_string (NM_DEVICE_VETH_PEER, "", "", @@ -149,10 +170,6 @@ nm_device_veth_class_init (NMDeviceVethClass *klass) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_VETH_SKELETON, - NULL); } /*****************************************************************************/ diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index 81512335..a7f4c4bc 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -37,8 +37,6 @@ #include "nm-core-internal.h" #include "platform/nmp-object.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Vlan.h" - #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceVlan); @@ -112,15 +110,15 @@ parent_hwaddr_maybe_changed (NMDevice *parent, if (nm_device_sys_iface_state_is_external_or_assume (device)) return; - connection = nm_device_get_applied_connection ((NMDevice *) self); + connection = nm_device_get_applied_connection (device); if (!connection) return; /* Update the VLAN MAC only if configuration does not specify one */ - if (nm_device_hw_addr_is_explict ((NMDevice *) self)) + if (nm_device_hw_addr_is_explict (device)) return; - old_mac = nm_device_get_hw_address ((NMDevice *) self); + old_mac = nm_device_get_hw_address (device); new_mac = nm_device_get_hw_address (parent); if (nm_streq0 (old_mac, new_mac)) return; @@ -128,13 +126,14 @@ parent_hwaddr_maybe_changed (NMDevice *parent, _LOGD (LOGD_VLAN, "parent hardware address changed to %s%s%s", NM_PRINT_FMT_QUOTE_STRING (new_mac)); if (new_mac) { - nm_device_hw_addr_set ((NMDevice *) self, new_mac, "vlan-parent", TRUE); + 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_ip6_config (NM_DEVICE (self), s_ip6, s_ip6, FALSE); + nm_device_reactivate_ip6_config (device, s_ip6, s_ip6); } } @@ -381,7 +380,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMSettingVlan *s_vlan; @@ -577,16 +576,35 @@ nm_device_vlan_init (NMDeviceVlan * self) { } +static const NMDBusInterfaceInfoExtended interface_info_device_vlan = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_VLAN, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier", "b", NM_DEVICE_CARRIER), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Parent", "o", NM_DEVICE_PARENT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("VlanId", "u", NM_DEVICE_VLAN_ID), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_vlan_class_init (NMDeviceVlanClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_VLAN_SETTING_NAME, NM_LINK_TYPE_VLAN) object_class->get_property = get_property; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_vlan); + parent_class->create_and_realize = create_and_realize; parent_class->link_changed = link_changed; parent_class->unrealize_notify = unrealize_notify; @@ -608,10 +626,6 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass) | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_VLAN_SKELETON, - NULL); } /*****************************************************************************/ diff --git a/src/devices/nm-device-vlan.h b/src/devices/nm-device-vlan.h index 0d788940..375e8fa4 100644 --- a/src/devices/nm-device-vlan.h +++ b/src/devices/nm-device-vlan.h @@ -36,12 +36,8 @@ typedef enum { NM_VLAN_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/ } NMVlanError; -/* D-Bus exported properties */ #define NM_DEVICE_VLAN_ID "vlan-id" -/* defined in the parent class, but exposed on D-Bus by the subclass. */ -#define NM_DEVICE_VLAN_PARENT NM_DEVICE_PARENT - typedef struct _NMDeviceVlan NMDeviceVlan; typedef struct _NMDeviceVlanClass NMDeviceVlanClass; diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index d9efe840..e1252223 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -35,8 +35,6 @@ #include "nm-act-request.h" #include "nm-ip4-config.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Vxlan.h" - #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceVxlan); @@ -97,42 +95,40 @@ update_properties (NMDevice *device) if (priv->props.parent_ifindex != props->parent_ifindex) nm_device_parent_set_ifindex (device, props->parent_ifindex); - if (priv->props.id != props->id) - _notify (self, PROP_ID); - if (priv->props.local != props->local) - _notify (self, PROP_LOCAL); - if (memcmp (&priv->props.local6, &props->local6, sizeof (props->local6)) != 0) - _notify (self, PROP_LOCAL); - if (priv->props.group != props->group) - _notify (self, PROP_GROUP); - if (memcmp (&priv->props.group6, &props->group6, sizeof (props->group6)) != 0) - _notify (self, PROP_GROUP); - if (priv->props.tos != props->tos) - _notify (self, PROP_TOS); - if (priv->props.ttl != props->ttl) - _notify (self, PROP_TTL); - if (priv->props.learning != props->learning) - _notify (self, PROP_LEARNING); - if (priv->props.ageing != props->ageing) - _notify (self, PROP_AGEING); - if (priv->props.limit != props->limit) - _notify (self, PROP_LIMIT); - if (priv->props.src_port_min != props->src_port_min) - _notify (self, PROP_SRC_PORT_MIN); - if (priv->props.src_port_max != props->src_port_max) - _notify (self, PROP_SRC_PORT_MAX); - if (priv->props.dst_port != props->dst_port) - _notify (self, PROP_DST_PORT); - if (priv->props.proxy != props->proxy) - _notify (self, PROP_PROXY); - if (priv->props.rsc != props->rsc) - _notify (self, PROP_RSC); - if (priv->props.l2miss != props->l2miss) - _notify (self, PROP_L2MISS); - if (priv->props.l3miss != props->l3miss) - _notify (self, PROP_L3MISS); - - priv->props = *props; + +#define CHECK_PROPERTY_CHANGED(field, prop) \ + G_STMT_START { \ + if (priv->props.field != props->field) { \ + priv->props.field = props->field; \ + _notify (self, prop); \ + } \ + } G_STMT_END + +#define CHECK_PROPERTY_CHANGED_IN6ADDR(field, prop) \ + G_STMT_START { \ + if (memcmp (&priv->props.field, &props->field, sizeof (props->field)) != 0) { \ + priv->props.field = props->field; \ + _notify (self, prop); \ + } \ + } G_STMT_END + + CHECK_PROPERTY_CHANGED (id, PROP_ID); + CHECK_PROPERTY_CHANGED (local, PROP_LOCAL); + CHECK_PROPERTY_CHANGED_IN6ADDR (local6, PROP_LOCAL); + CHECK_PROPERTY_CHANGED (group, PROP_GROUP); + CHECK_PROPERTY_CHANGED_IN6ADDR (group6, PROP_GROUP); + CHECK_PROPERTY_CHANGED (tos, PROP_TOS); + CHECK_PROPERTY_CHANGED (ttl, PROP_TTL); + CHECK_PROPERTY_CHANGED (learning, PROP_LEARNING); + CHECK_PROPERTY_CHANGED (ageing, PROP_AGEING); + CHECK_PROPERTY_CHANGED (limit, PROP_LIMIT); + CHECK_PROPERTY_CHANGED (src_port_min, PROP_SRC_PORT_MIN); + CHECK_PROPERTY_CHANGED (src_port_max, PROP_SRC_PORT_MAX); + CHECK_PROPERTY_CHANGED (dst_port, PROP_DST_PORT); + CHECK_PROPERTY_CHANGED (proxy, PROP_PROXY); + CHECK_PROPERTY_CHANGED (rsc, PROP_RSC); + CHECK_PROPERTY_CHANGED (l2miss, PROP_L2MISS); + CHECK_PROPERTY_CHANGED (l3miss, PROP_L3MISS); g_object_thaw_notify (object); } @@ -318,7 +314,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMSettingVxlan *s_vxlan; @@ -545,16 +541,48 @@ nm_device_vxlan_init (NMDeviceVxlan *self) { } +static const NMDBusInterfaceInfoExtended interface_info_device_vxlan = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_VXLAN, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Parent", "o", NM_DEVICE_PARENT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Id", "u", NM_DEVICE_VXLAN_ID), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Group", "s", NM_DEVICE_VXLAN_GROUP), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Local", "s", NM_DEVICE_VXLAN_LOCAL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Tos", "y", NM_DEVICE_VXLAN_TOS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ttl", "y", NM_DEVICE_VXLAN_TTL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Learning", "b", NM_DEVICE_VXLAN_LEARNING), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ageing", "u", NM_DEVICE_VXLAN_AGEING), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Limit", "u", NM_DEVICE_VXLAN_LIMIT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("DstPort", "q", NM_DEVICE_VXLAN_DST_PORT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("SrcPortMin", "q", NM_DEVICE_VXLAN_SRC_PORT_MIN), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("SrcPortMax", "q", NM_DEVICE_VXLAN_SRC_PORT_MAX), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Proxy", "b", NM_DEVICE_VXLAN_PROXY), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Rsc", "b", NM_DEVICE_VXLAN_RSC), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("L2miss", "b", NM_DEVICE_VXLAN_L2MISS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("L3miss", "b", NM_DEVICE_VXLAN_L3MISS), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_vxlan_class_init (NMDeviceVxlanClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_VXLAN) object_class->get_property = get_property; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_vxlan); + device_class->link_changed = link_changed; device_class->unrealize_notify = unrealize_notify; device_class->connection_type = NM_SETTING_VXLAN_SETTING_NAME; @@ -657,10 +685,6 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_VXLAN_SKELETON, - NULL); } /*****************************************************************************/ diff --git a/src/devices/nm-device-vxlan.h b/src/devices/nm-device-vxlan.h index 6f0102dc..511b7156 100644 --- a/src/devices/nm-device-vxlan.h +++ b/src/devices/nm-device-vxlan.h @@ -46,9 +46,6 @@ #define NM_DEVICE_VXLAN_L2MISS "l2miss" #define NM_DEVICE_VXLAN_L3MISS "l3miss" -/* defined in the parent class, but exposed on D-Bus by the subclass. */ -#define NM_DEVICE_VXLAN_PARENT NM_DEVICE_PARENT - typedef struct _NMDeviceVxlan NMDeviceVxlan; typedef struct _NMDeviceVxlanClass NMDeviceVxlanClass; diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index e79bc541..9a40a0dd 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,7 @@ #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" @@ -64,13 +66,13 @@ #include "nm-netns.h" #include "nm-dispatcher.h" #include "nm-config.h" -#include "nm-utils/c-list.h" +#include "c-list/src/c-list.h" #include "dns/nm-dns-manager.h" +#include "nm-acd-manager.h" #include "nm-core-internal.h" #include "systemd/nm-sd.h" #include "nm-lldp-listener.h" #include "nm-audit-manager.h" -#include "nm-arping-manager.h" #include "nm-connectivity.h" #include "nm-dbus-interface.h" #include "nm-device-vlan.h" @@ -78,9 +80,6 @@ #include "nm-device-logging.h" _LOG_DECLARE_SELF (NMDevice); -#include "introspection/org.freedesktop.NetworkManager.Device.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Statistics.h" - /*****************************************************************************/ #define DEFAULT_AUTOCONNECT TRUE @@ -130,13 +129,13 @@ typedef struct { int ifindex; } DeleteOnDeactivateData; -typedef void (*ArpingCallback) (NMDevice *, NMIP4Config **, gboolean); +typedef void (*AcdCallback) (NMDevice *, NMIP4Config **, gboolean); typedef struct { - ArpingCallback callback; + AcdCallback callback; NMDevice *device; NMIP4Config **configs; -} ArpingData; +} AcdData; typedef enum { HW_ADDR_TYPE_UNSET = 0, @@ -152,6 +151,24 @@ typedef enum { FIREWALL_STATE_WAIT_IP_CONFIG, } 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; + +struct _NMDeviceConnectivityHandle { + CList concheck_lst; + NMDevice *self; + NMDeviceConnectivityCallback callback; + gpointer user_data; + NMConnectivityCheckHandle *c_handle; + guint64 seq; + bool is_periodic:1; + bool is_periodic_bump:1; + bool is_periodic_bump_on_complete:1; +}; + /*****************************************************************************/ enum { @@ -165,6 +182,7 @@ enum { REMOVED, RECHECK_AUTO_ACTIVATE, RECHECK_ASSUME, + CONNECTIVITY_CHANGED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -229,12 +247,18 @@ typedef struct _NMDevicePrivate { NMDeviceStateReason reason; } queued_state; - guint queued_ip4_config_id; - guint queued_ip6_config_id; + union { + struct { + guint queued_ip_config_id_6; + guint queued_ip_config_id_4; + }; + guint queued_ip_config_id_x[2]; + }; + GSList *pending_actions; GSList *dad6_failed_addrs; - NMDevice *parent_device; + NMDBusTrackObjPath parent_device; char * udi; char * iface; /* may change, could be renamed by user */ @@ -258,11 +282,13 @@ typedef struct _NMDevicePrivate { bool queued_ip4_config_pending:1; bool queued_ip6_config_pending:1; + bool update_ip_config_completed_v4:1; + bool update_ip_config_completed_v6:1; + char * ip_iface; int ip_ifindex; NMDeviceType type; char * type_desc; - char * type_description; NMLinkType link_type; NMDeviceCapabilities capabilities; char * driver; @@ -297,9 +323,7 @@ typedef struct _NMDevicePrivate { NMActRequest * queued_act_request; bool queued_act_request_is_waiting_for_carrier:1; - bool act_request_public:1; - NMActRequest *act_request; - gulong act_request_id; + NMDBusTrackObjPath act_request; ActivationHandleData act_handle4; /* for layer2 and IPv4. */ ActivationHandleData act_handle6; guint recheck_assume_id; @@ -356,6 +380,12 @@ typedef struct _NMDevicePrivate { NMDeviceAutoconnectBlockedFlags autoconnect_blocked_flags:4; + bool is_enslaved:1; + bool master_ready_handled:1; + + bool ipv6ll_handle:1; /* TRUE if NM handles the device's IPv6LL address */ + bool ipv6ll_has:1; + /* Generic DHCP stuff */ char * dhcp_anycast_address; @@ -366,17 +396,56 @@ typedef struct _NMDevicePrivate { NMPacrunnerManager *pacrunner_manager; NMPacrunnerCallId *pacrunner_call_id; - /* IP4 configuration info */ - NMIP4Config * ip4_config; /* Combined config from VPN, settings, and device */ + /* 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]; + }; + union { const IpState ip4_state; IpState ip4_state_; }; - NMIP4Config * con_ip4_config; /* config from the setting */ - NMIP4Config * dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */ - NMIP4Config * ext_ip4_config; /* Stuff added outside NM */ - NMIP4Config * wwan_ip4_config; /* WWAN configuration */ - GSList * vpn4_configs; /* VPNs which use this device */ + AppliedConfig dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */ + + /* 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]; + }; + + /* WWAN configuration */ + union { + struct { + AppliedConfig wwan_ip_config_6; + AppliedConfig wwan_ip_config_4; + }; + AppliedConfig wwan_ip_config_x[2]; + }; bool v4_has_shadowed_routes; const char *ip4_rp_filter; @@ -396,8 +465,8 @@ typedef struct _NMDevicePrivate { guint timeout; guint watch; GPid pid; - const char *binary; - const char *address; + char *binary; + char *address; guint deadline; } gw_ping; @@ -418,22 +487,17 @@ typedef struct _NMDevicePrivate { /* IPv4 DAD stuff */ struct { GSList * dad_list; - NMArpingManager * announcing; - } arping; + NMAcdManager * announcing; + } acd; - /* IP6 configuration info */ - NMIP6Config * ip6_config; union { const IpState ip6_state; IpState ip6_state_; }; - NMIP6Config * con_ip6_config; /* config from the setting */ - NMIP6Config * wwan_ip6_config; - NMIP6Config * ext_ip6_config; /* Stuff added outside NM */ + AppliedConfig ac_ip6_config; /* config from IPv6 autoconfiguration */ NMIP6Config * ext_ip6_config_captured; /* Configuration captured from platform. */ - GSList * vpn6_configs; /* VPNs which use this device */ - bool nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */ NMIP6Config * dad6_ip6_config; + struct in6_addr ipv6ll_addr; GHashTable * rt6_temporary_not_available; @@ -441,8 +505,6 @@ typedef struct _NMDevicePrivate { gulong ndisc_changed_id; gulong ndisc_timeout_id; NMSettingIP6ConfigPrivacy ndisc_use_tempaddr; - /* IP6 config from autoconf */ - NMIP6Config * ac_ip6_config; guint linklocal6_timeout_id; guint8 linklocal6_dad_counter; @@ -456,7 +518,7 @@ typedef struct _NMDevicePrivate { gulong prefix_sigid; NMDhcp6Config * config; /* IP6 config from DHCP */ - NMIP6Config * ip6_config; + AppliedConfig ip6_config; /* Event ID of the current IP6 config from DHCP */ char * event_id; guint needed_prefixes; @@ -468,8 +530,6 @@ typedef struct _NMDevicePrivate { /* master interface for bridge/bond/team slave */ NMDevice * master; - bool is_enslaved; - bool master_ready_handled; gulong master_ready_id; /* slave management */ @@ -482,9 +542,26 @@ typedef struct _NMDevicePrivate { NMNetns *netns; NMLldpListener *lldp_listener; + + NMConnectivity *concheck_mgr; + + /* if periodic checks are enabled, this is the source id for the next check. */ + guint concheck_p_cur_id; + + /* the currently configured max periodic interval. */ + guint concheck_p_max_interval; + + /* the current interval. If we are probing, the interval might be lower + * then the configured max interval. */ + guint concheck_p_cur_interval; + + /* the timestamp, when we last scheduled the timer concheck_p_cur_id with current interval + * concheck_p_cur_interval. */ + gint64 concheck_p_cur_basetime_ns; + NMConnectivityState connectivity_state; - gulong concheck_periodic_id; - guint64 concheck_seq; + + CList concheck_lst_head; guint check_delete_unrealized_id; @@ -497,35 +574,35 @@ typedef struct _NMDevicePrivate { } NMDevicePrivate; -G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, NM_TYPE_EXPORTED_OBJECT) +G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, NM_TYPE_DBUS_OBJECT) #define NM_DEVICE_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMDevice, NM_IS_DEVICE) /*****************************************************************************/ +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 gboolean update_ext_ip_config (NMDevice *self, int addr_family, gboolean initial, gboolean intersect_configs); +static gboolean update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_configs); -static gboolean nm_device_set_ip4_config (NMDevice *self, - NMIP4Config *config, - gboolean commit, - GPtrArray *ip4_dev_route_blacklist); -static gboolean ip4_config_merge_and_apply (NMDevice *self, - gboolean commit); +static gboolean nm_device_set_ip_config (NMDevice *self, + int addr_family, + NMIPConfig *config, + gboolean commit, + GPtrArray *ip4_dev_route_blacklist); -static gboolean nm_device_set_ip6_config (NMDevice *self, - NMIP6Config *config, - gboolean commit); -static gboolean ip6_config_merge_and_apply (NMDevice *self, - gboolean commit); +static gboolean ip_config_merge_and_apply (NMDevice *self, + int addr_family, + gboolean commit); 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 gboolean addrconf6_start_with_link_ready (NMDevice *self); -static NMActStageReturn linklocal6_start (NMDevice *self); +static void addrconf6_start_with_link_ready (NMDevice *self); +static gboolean linklocal6_start (NMDevice *self); static void _carrier_wait_check_queued_act_request (NMDevice *self); static gint64 _get_carrier_wait_ms (NMDevice *self); @@ -555,6 +632,8 @@ 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 concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean is_periodic); + /*****************************************************************************/ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (queued_state_to_string, NMDeviceState, @@ -679,6 +758,16 @@ nm_device_get_platform (NMDevice *self) return nm_netns_get_platform (nm_device_get_netns (self)); } +static NMConnectivity * +concheck_get_mgr (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (G_UNLIKELY (!priv->concheck_mgr)) + priv->concheck_mgr = g_object_ref (nm_connectivity_get ()); + return priv->concheck_mgr; +} + static NMIP4Config * _ip4_config_new (NMDevice *self) { @@ -693,6 +782,105 @@ _ip6_config_new (NMDevice *self) nm_device_get_ip_ifindex (self)); } +static NMIPConfig * +_ip_config_new (NMDevice *self, int addr_family) +{ + nm_assert_addr_family (addr_family); + + return addr_family == AF_INET + ? (gpointer) _ip4_config_new (self) + : (gpointer) _ip6_config_new (self); +} + +static void +applied_config_clear (AppliedConfig *config) +{ + g_clear_object (&config->current); + g_clear_object (&config->orig); +} + +static void +applied_config_init (AppliedConfig *config, gpointer ip_config) +{ + nm_g_object_ref (ip_config); + applied_config_clear (config); + config->orig = ip_config; +} + +static void +applied_config_init_new (AppliedConfig *config, NMDevice *self, int addr_family) +{ + gs_unref_object NMIPConfig *c = _ip_config_new (self, addr_family); + + 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); +} + /*****************************************************************************/ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_sys_iface_state_to_str, NMDeviceSysIfaceState, @@ -822,29 +1010,18 @@ nm_device_assume_state_reset (NMDevice *self) /*****************************************************************************/ static void -init_ip4_config_dns_priority (NMDevice *self, NMIP4Config *config) -{ - gs_free char *value = NULL; - gint priority; - - value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - "ipv4.dns-priority", - self); - priority = _nm_utils_ascii_str_to_int64 (value, 10, G_MININT, G_MAXINT, 0); - nm_ip4_config_set_dns_priority (config, priority ?: NM_DNS_PRIORITY_DEFAULT_NORMAL); -} - -static void -init_ip6_config_dns_priority (NMDevice *self, NMIP6Config *config) +init_ip_config_dns_priority (NMDevice *self, NMIPConfig *config) { gs_free char *value = NULL; gint priority; value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - "ipv6.dns-priority", + (nm_ip_config_get_addr_family (config) == AF_INET) + ? "ipv4.dns-priority" + : "ipv6.dns-priority", self); priority = _nm_utils_ascii_str_to_int64 (value, 10, G_MININT, G_MAXINT, 0); - nm_ip6_config_set_dns_priority (config, priority ?: NM_DNS_PRIORITY_DEFAULT_NORMAL); + nm_ip_config_set_dns_priority (config, priority ?: NM_DNS_PRIORITY_DEFAULT_NORMAL); } /*****************************************************************************/ @@ -883,6 +1060,11 @@ nm_device_ipv4_sysctl_get_effective_uint32 (NMDevice *self, const char *property if (!nm_device_get_ip_ifindex (self)) return fallback; + /* for this kind of sysctl (e.g. "rp_filter"), kernel effectively uses the + * MAX of the per-device value and the "all" value. + * + * Also do that, by reading both sysctls and return the maximum. */ + v = nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, buf, @@ -995,8 +1177,9 @@ _get_stable_id (NMDevice *self, uuid = nm_connection_get_uuid (connection); stable_type = nm_utils_stable_id_parse (stable_id, - uuid, + nm_device_get_ip_iface (self), NULL, + uuid, &generated); /* current_stable_id_type is a bitfield! */ @@ -1048,7 +1231,7 @@ _set_ip_state (NMDevice *self, int addr_family, IpState new_state) p = (addr_family == AF_INET) ? &priv->ip4_state_ - : &priv->ip6_state_; + : &priv->ip6_state_; if (*p != new_state) { _LOGT (LOGD_DEVICE, "ip%c-state: set to %d (%s)", @@ -1089,28 +1272,26 @@ nm_device_get_iface (NMDevice *self) } gboolean -nm_device_take_over_link (NMDevice *self, const char *ifname, gboolean *renamed) +nm_device_take_over_link (NMDevice *self, int ifindex, char **old_name) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const NMPlatformLink *plink; NMPlatform *platform; gboolean up, success = TRUE; - int ifindex; + gs_free char *name = NULL; g_return_val_if_fail (priv->ifindex <= 0, FALSE); - g_return_val_if_fail (ifname, FALSE); - NM_SET_OUT (renamed, FALSE); + NM_SET_OUT (old_name, NULL); platform = nm_device_get_platform (self); - plink = nm_platform_link_get_by_ifname (platform, ifname); + plink = nm_platform_link_get (platform, ifindex); if (!plink) return FALSE; - ifindex = plink->ifindex; - - if (!nm_streq (ifname, nm_device_get_iface (self))) { + if (!nm_streq (plink->name, nm_device_get_iface (self))) { up = NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP); + name = g_strdup (plink->name); /* Rename the link to the device ifname */ if (up) @@ -1119,7 +1300,8 @@ nm_device_take_over_link (NMDevice *self, const char *ifname, gboolean *renamed) if (up) nm_platform_link_set_up (platform, ifindex, NULL); - NM_SET_OUT (renamed, success); + if (success) + NM_SET_OUT (old_name, g_steal_pointer (&name)); } if (success) { @@ -1176,7 +1358,7 @@ nm_device_get_ip_iface (NMDevice *self) priv = NM_DEVICE_GET_PRIVATE (self); /* If it's not set, default to iface */ - return priv->ip_iface ? priv->ip_iface : priv->iface; + return priv->ip_iface ?: priv->iface; } int @@ -1191,71 +1373,102 @@ nm_device_get_ip_ifindex (const NMDevice *self) return priv->ip_iface ? priv->ip_ifindex : priv->ifindex; } -/** - * nm_device_set_ip_iface: - * @self: the #NMDevice - * @iface: the new IP interface name - * - * Updates the IP interface name and possibly the ifindex. - * - * Returns: %TRUE if the anything (name or ifindex) changed, %FALSE if nothing - * changed. - */ -gboolean -nm_device_set_ip_iface (NMDevice *self, const char *iface) +static void +_set_ip_ifindex (NMDevice *self, + int ifindex, + const char *ifname) { - NMDevicePrivate *priv; - int ifindex; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMPlatform *platform; + gboolean eq_name; - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + /* normalize arguments */ + if (ifindex <= 0) { + ifindex = 0; + ifname = NULL; + } - priv = NM_DEVICE_GET_PRIVATE (self); - if (nm_streq0 (iface, priv->ip_iface)) { - if (!iface) - return FALSE; - ifindex = nm_platform_if_nametoindex (nm_device_get_platform (self), iface); - if ( ifindex <= 0 - || priv->ip_ifindex == ifindex) - return FALSE; + eq_name = nm_streq0 (priv->ip_iface, ifname); - priv->ip_ifindex = ifindex; - _LOGD (LOGD_DEVICE, "ip-ifname: update ifindex for ifname '%s': %d", iface, priv->ip_ifindex); - } else { + if ( eq_name + && priv->ip_ifindex == ifindex) + return; + + _LOGD (LOGD_DEVICE, "ip-ifindex: update ip-interface to %s%s%s, ifindex %d", + NM_PRINT_FMT_QUOTE_STRING (ifname), + ifindex); + + priv->ip_ifindex = ifindex; + if (!eq_name) { g_free (priv->ip_iface); - priv->ip_iface = g_strdup (iface); - - if (iface) { - /* The @iface name is not in sync with the platform cache. - * So, there is no point asking the platform cache to resolve - * the ifindex. Instead, we can only hope that the interface - * with this name still exists and we resolve the ifindex - * anew. - */ - priv->ip_ifindex = nm_platform_if_nametoindex (nm_device_get_platform (self), iface); - if (priv->ip_ifindex > 0) - _LOGD (LOGD_DEVICE, "ip-ifname: set ifname '%s', ifindex %d", iface, priv->ip_ifindex); - else - _LOGW (LOGD_DEVICE, "ip-ifname: set ifname '%s', unknown ifindex", iface); - } else { - priv->ip_ifindex = 0; - _LOGD (LOGD_DEVICE, "ip-ifname: clear ifname"); - } + priv->ip_iface = g_strdup (ifname); + _notify (self, PROP_IP_IFACE); } if (priv->ip_ifindex > 0) { - if (nm_platform_check_kernel_support (nm_device_get_platform (self), + platform = nm_device_get_platform (self); + + nm_platform_process_events_ensure_link (platform, + priv->ip_ifindex, + priv->ip_iface); + + if (nm_platform_check_kernel_support (platform, NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) - nm_platform_link_set_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ip_ifindex, TRUE); + nm_platform_link_set_user_ipv6ll_enabled (platform, priv->ip_ifindex, TRUE); - if (!nm_platform_link_is_up (nm_device_get_platform (self), priv->ip_ifindex)) - nm_platform_link_set_up (nm_device_get_platform (self), priv->ip_ifindex, NULL); + if (!nm_platform_link_is_up (platform, priv->ip_ifindex)) + nm_platform_link_set_up (platform, priv->ip_ifindex, NULL); } /* We don't care about any saved values from the old iface */ g_hash_table_remove_all (priv->ip6_saved_properties); +} - _notify (self, PROP_IP_IFACE); - return TRUE; +gboolean +nm_device_set_ip_ifindex (NMDevice *self, int ifindex) +{ + char ifname_buf[IFNAMSIZ]; + const char *ifname = NULL; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (nm_device_is_activating (self), FALSE); + + if (ifindex > 0) { + ifname = nm_platform_if_indextoname (nm_device_get_platform (self), ifindex, ifname_buf); + if (!ifname) + _LOGW (LOGD_DEVICE, "ip-ifindex: ifindex %d not found", ifindex); + } + + _set_ip_ifindex (self, ifindex, ifname); + return ifindex > 0; +} + +/** + * nm_device_set_ip_iface: + * @self: the #NMDevice + * @ifname: the new IP interface name + * + * Updates the IP interface name and possibly the ifindex. + * + * Returns: %TRUE if an interface with name @ifname exists, + * and %FALSE, if @ifname is %NULL or no such interface exists. + */ +gboolean +nm_device_set_ip_iface (NMDevice *self, const char *ifname) +{ + int ifindex = 0; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (nm_device_is_activating (self), FALSE); + + if (ifname) { + ifindex = nm_platform_if_nametoindex (nm_device_get_platform (self), ifname); + if (ifindex <= 0) + _LOGW (LOGD_DEVICE, "ip-ifindex: ifname %s not found", ifname); + } + + _set_ip_ifindex (self, ifindex, ifname); + return ifindex > 0; } static gboolean @@ -1302,12 +1515,9 @@ nm_device_parent_get_ifindex (NMDevice *self) NMDevice * nm_device_parent_get_device (NMDevice *self) { - NMDevicePrivate *priv; - g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - priv = NM_DEVICE_GET_PRIVATE (self); - return priv->parent_device; + return NM_DEVICE_GET_PRIVATE (self)->parent_device.obj; } static void @@ -1329,7 +1539,7 @@ _parent_set_ifindex (NMDevice *self, NMDevice *parent_device; gboolean changed = FALSE; int old_ifindex; - NMDevice *old_device; + gs_unref_object NMDevice *old_device = NULL; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); @@ -1339,16 +1549,15 @@ _parent_set_ifindex (NMDevice *self, parent_ifindex = 0; old_ifindex = priv->parent_ifindex; - old_device = priv->parent_device; if (priv->parent_ifindex == parent_ifindex) { if (parent_ifindex > 0) { if ( !force_check - && priv->parent_device - && nm_device_get_ifindex (priv->parent_device) == parent_ifindex) + && priv->parent_device.obj + && nm_device_get_ifindex (priv->parent_device.obj) == parent_ifindex) return FALSE; } else { - if (!priv->parent_device) + if (!priv->parent_device.obj) return FALSE; } } else { @@ -1363,24 +1572,23 @@ _parent_set_ifindex (NMDevice *self, } else parent_device = NULL; - if (parent_device != priv->parent_device) { - priv->parent_device = parent_device; + if (parent_device != priv->parent_device.obj) { + old_device = nm_g_object_ref (priv->parent_device.obj); + nm_dbus_track_obj_path_set (&priv->parent_device, parent_device, TRUE); changed = TRUE; } if (changed) { if (priv->parent_ifindex <= 0) _LOGD (LOGD_DEVICE, "parent: clear"); - else if (!priv->parent_device) + else if (!priv->parent_device.obj) _LOGD (LOGD_DEVICE, "parent: ifindex %d, no device", priv->parent_ifindex); else { _LOGD (LOGD_DEVICE, "parent: ifindex %d, device %p, %s", priv->parent_ifindex, - priv->parent_device, nm_device_get_iface (priv->parent_device)); + priv->parent_device.obj, nm_device_get_iface (priv->parent_device.obj)); } - NM_DEVICE_GET_CLASS (self)->parent_changed_notify (self, old_ifindex, old_device, priv->parent_ifindex, priv->parent_device); - - _notify (self, PROP_PARENT); + NM_DEVICE_GET_CLASS (self)->parent_changed_notify (self, old_ifindex, old_device, priv->parent_ifindex, priv->parent_device.obj); } return changed; } @@ -1405,7 +1613,7 @@ nm_device_parent_notify_changed (NMDevice *self, priv = NM_DEVICE_GET_PRIVATE (self); if (priv->parent_ifindex > 0) { - if ( priv->parent_device == change_candidate + if ( priv->parent_device.obj == change_candidate || priv->parent_ifindex == nm_device_get_ifindex (change_candidate)) return _parent_set_ifindex (self, priv->parent_ifindex, device_removed); } @@ -1711,16 +1919,13 @@ nm_device_get_route_metric_default (NMDeviceType device_type) static gboolean default_route_metric_penalty_detect (NMDevice *self) { -#if WITH_CONCHECK NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); /* currently we don't differentiate between IPv4 and IPv6 when detecting * connectivity. */ if ( priv->connectivity_state != NM_CONNECTIVITY_FULL - && nm_connectivity_check_enabled (nm_connectivity_get ())) { + && nm_connectivity_check_enabled (concheck_get_mgr (self))) return TRUE; - } -#endif return FALSE; } @@ -1788,6 +1993,34 @@ out: return nm_utils_ip_route_metric_normalize (addr_family, route_metric); } +static NMSettingConnectionMdns +_get_mdns (NMDevice *self) +{ + NMConnection *connection; + NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT; + + g_return_val_if_fail (NM_IS_DEVICE (self), NM_SETTING_CONNECTION_MDNS_DEFAULT); + + connection = nm_device_get_applied_connection (self); + if (connection) + mdns = nm_setting_connection_get_mdns (nm_connection_get_setting_connection (connection)); + + if (mdns == NM_SETTING_CONNECTION_MDNS_DEFAULT) { + gs_free char *value = NULL; + + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "connection.mdns", + self); + mdns = _nm_utils_ascii_str_to_int64 (value, + 10, + NM_SETTING_CONNECTION_MDNS_NO, + NM_SETTING_CONNECTION_MDNS_YES, + NM_SETTING_CONNECTION_MDNS_DEFAULT); + } + + return mdns; +} + guint32 nm_device_get_route_table (NMDevice *self, int addr_family, @@ -1864,12 +2097,12 @@ nm_device_get_best_default_route (NMDevice *self, switch (addr_family) { case AF_INET: - return priv->ip4_config ? nm_ip4_config_best_default_route_get (priv->ip4_config) : NULL; + return priv->ip_config_4 ? nm_ip4_config_best_default_route_get (priv->ip_config_4) : NULL; case AF_INET6: - return priv->ip6_config ? nm_ip6_config_best_default_route_get (priv->ip6_config) : NULL; + return priv->ip_config_6 ? nm_ip6_config_best_default_route_get (priv->ip_config_6) : NULL; case AF_UNSPEC: - return (priv->ip4_config ? nm_ip4_config_best_default_route_get (priv->ip4_config) : NULL) - ?: (priv->ip6_config ? nm_ip6_config_best_default_route_get (priv->ip6_config) : NULL); + 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); } @@ -1897,18 +2130,37 @@ nm_device_get_type_description (NMDevice *self) static const char * get_type_description (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDeviceClass *klass; + + nm_assert (NM_IS_DEVICE (self)); - if (!priv->type_description) { + /* the default implementation for the description just returns the (modified) + * class name and depends entirely on the type of self. Note that we cache the + * description in the klass itself. + * + * Also note, that as the GObject class gets inited, it inherrits the fields + * of the parent class. That means, if NMDeviceVethClass was initialized after + * NMDeviceEthernetClass already has the description cached in the class + * (because we already fetched the description for an ethernet device), + * then default_type_description will wrongly contain "ethernet". + * To avoid that, and catch the situation, also cache the klass for + * which the description was cached. If that doesn't match, it was + * inherited and we need to reset it. */ + klass = NM_DEVICE_GET_CLASS (self); + if (G_UNLIKELY (klass->default_type_description_klass != klass)) { const char *typename; + gs_free char *s = NULL; typename = G_OBJECT_TYPE_NAME (self); if (g_str_has_prefix (typename, "NMDevice")) typename += 8; - priv->type_description = g_ascii_strdown (typename, -1); + s = g_ascii_strdown (typename, -1); + klass->default_type_description = g_intern_string (s); + klass->default_type_description_klass = klass; } - return priv->type_description; + nm_assert (klass->default_type_description); + return klass->default_type_description; } gboolean @@ -1922,7 +2174,7 @@ nm_device_get_act_request (NMDevice *self) { g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - return NM_DEVICE_GET_PRIVATE (self)->act_request; + return NM_DEVICE_GET_PRIVATE (self)->act_request.obj; } NMSettingsConnection * @@ -1930,7 +2182,7 @@ nm_device_get_settings_connection (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - return priv->act_request ? nm_act_request_get_settings_connection (priv->act_request) : NULL; + return priv->act_request.obj ? nm_act_request_get_settings_connection (priv->act_request.obj) : NULL; } NMConnection * @@ -1942,7 +2194,7 @@ nm_device_get_applied_connection (NMDevice *self) priv = NM_DEVICE_GET_PRIVATE (self); - return priv->act_request ? nm_act_request_get_applied_connection (priv->act_request) : NULL; + return priv->act_request.obj ? nm_act_request_get_applied_connection (priv->act_request.obj) : NULL; } gboolean @@ -1950,10 +2202,10 @@ nm_device_has_unmodified_applied_connection (NMDevice *self, NMSettingCompareFla { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (!priv->act_request) + if (!priv->act_request.obj) return FALSE; - return nm_active_connection_has_unmodified_applied_connection ((NMActiveConnection *) priv->act_request, compare_flags); + return nm_active_connection_has_unmodified_applied_connection ((NMActiveConnection *) priv->act_request.obj, compare_flags); } NMSetting * @@ -1981,172 +2233,582 @@ nm_device_get_physical_port_id (NMDevice *self) /*****************************************************************************/ +typedef enum { + CONCHECK_SCHEDULE_UPDATE_INTERVAL, + CONCHECK_SCHEDULE_UPDATE_INTERVAL_RESTART, + CONCHECK_SCHEDULE_CHECK_EXTERNAL, + CONCHECK_SCHEDULE_CHECK_PERIODIC, + CONCHECK_SCHEDULE_RETURNED_MIN, + CONCHECK_SCHEDULE_RETURNED_BUMP, + CONCHECK_SCHEDULE_RETURNED_MAX, +} ConcheckScheduleMode; + +static NMDeviceConnectivityHandle *concheck_start (NMDevice *self, + NMDeviceConnectivityCallback callback, + gpointer user_data, + gboolean is_periodic); + +static void concheck_periodic_schedule_set (NMDevice *self, + ConcheckScheduleMode mode); + +static gboolean +concheck_periodic_timeout_cb (gpointer user_data) +{ + NMDevice *self = user_data; + + _LOGt (LOGD_CONCHECK, "connectivity: periodic timeout"); + concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_CHECK_PERIODIC); + return G_SOURCE_REMOVE; +} + +static gboolean +concheck_is_possible (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if ( !nm_device_is_real (self) + || NM_FLAGS_HAS (priv->unmanaged_flags, NM_UNMANAGED_LOOPBACK)) + return FALSE; + + /* we enable periodic checks for every device state (except UNKNOWN). Especially with + * unmanaged devices, it is interesting to know whether we have connectivity on that device. */ + if (priv->state == NM_DEVICE_STATE_UNKNOWN) + return FALSE; + + return TRUE; +} + +static gboolean +concheck_periodic_schedule_do (NMDevice *self, gint64 now_ns) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean periodic_check_disabled = FALSE; + gint64 expiry, tdiff; + + /* we always cancel whatever was pending. */ + if (nm_clear_g_source (&priv->concheck_p_cur_id)) + periodic_check_disabled = TRUE; + + if (priv->concheck_p_max_interval == 0) { + /* periodic checks are disabled */ + goto out; + } + + if (!concheck_is_possible (self)) + goto out; + + nm_assert (now_ns > 0); + nm_assert (priv->concheck_p_cur_interval > 0); + + /* we schedule the timeout based on our current settings cur-interval and cur-basetime. + * Before calling concheck_periodic_schedule_do(), make sure that these properties are + * correct. */ + + expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + tdiff = expiry - now_ns; + + _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: %sscheduled in %lld milliseconds (%u seconds interval)", + periodic_check_disabled ? "re-" : "", + (long long) (tdiff / NM_UTILS_NS_PER_MSEC), + priv->concheck_p_cur_interval); + + priv->concheck_p_cur_id = g_timeout_add (NM_MAX ((gint64) 0, tdiff) / NM_UTILS_NS_PER_MSEC, + concheck_periodic_timeout_cb, + self); + return TRUE; +out: + if (periodic_check_disabled) + _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: unscheduled"); + return FALSE; +} + +#define CONCHECK_P_PROBE_INTERVAL 1 + static void -update_connectivity_state (NMDevice *self, NMConnectivityState state) +concheck_periodic_schedule_set (NMDevice *self, + ConcheckScheduleMode mode) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gint64 new_expiry, exp_expiry, cur_expiry, tdiff; + gint64 now_ns = 0; - /* If the connectivity check is disabled, make an optimistic guess. */ - if (state == NM_CONNECTIVITY_UNKNOWN) { - if (priv->state == NM_DEVICE_STATE_ACTIVATED) { - if (nm_device_get_best_default_route (self, AF_UNSPEC)) - state = NM_CONNECTIVITY_FULL; - else - state = NM_CONNECTIVITY_LIMITED; + if (priv->concheck_p_max_interval == 0) { + /* periodic check is disabled. Nothing to do. */ + return; + } + + if (!priv->concheck_p_cur_id) { + /* we currently don't have a timeout scheduled. No need to reschedule + * another one... */ + if (NM_IN_SET (mode, CONCHECK_SCHEDULE_UPDATE_INTERVAL, + CONCHECK_SCHEDULE_UPDATE_INTERVAL_RESTART)) { + /* ... unless, we are about to start periodic checks after update-interval. + * In this case, fall through and restart the periodic checks below. */ + mode = CONCHECK_SCHEDULE_UPDATE_INTERVAL_RESTART; + } else + return; + } + + switch (mode) { + case CONCHECK_SCHEDULE_UPDATE_INTERVAL_RESTART: + priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_max_interval, CONCHECK_P_PROBE_INTERVAL); + priv->concheck_p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); + if (concheck_periodic_schedule_do (self, now_ns)) + concheck_start (self, NULL, NULL, TRUE); + return; + + case CONCHECK_SCHEDULE_UPDATE_INTERVAL: + /* called with "UPDATE_INTERVAL" and already have a concheck_p_cur_id scheduled. */ + + nm_assert (priv->concheck_p_max_interval > 0); + nm_assert (priv->concheck_p_cur_interval > 0); + + if (priv->concheck_p_cur_interval <= priv->concheck_p_max_interval) { + /* we currently have a shorter interval set, than what we now have. Either, + * because we are probing, or because the previous max interval was shorter. + * + * Either way, the current timer is set just fine. Nothing to do, we will + * probe our way up. */ + return; + } + + cur_expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_max_interval * NM_UTILS_NS_PER_SECOND); + nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); + + priv->concheck_p_cur_interval = priv->concheck_p_max_interval; + if (cur_expiry <= now_ns) { + /* Since the last time we scheduled a periodic check, already more than the + * new max_interval passed. We need to start a check right away (and + * schedule a timeout in cur-interval in the future). */ + priv->concheck_p_cur_basetime_ns = now_ns; + if (concheck_periodic_schedule_do (self, now_ns)) + concheck_start (self, NULL, NULL, TRUE); } else { - state = NM_CONNECTIVITY_NONE; + /* we are reducing the max-interval to a shorter interval that we have currently + * scheduled (with cur_interval). + * + * However, since the last time we scheduled the check, not even the new max-interval + * expired. All we need to do, is reschedule the timer to expire sooner. The cur_basetime + * is unchanged. */ + concheck_periodic_schedule_do (self, now_ns); } - } + return; - if (priv->connectivity_state != state) { -#if WITH_CONCHECK - _LOGD (LOGD_CONCHECK, "state changed from %s to %s", - nm_connectivity_state_to_string (priv->connectivity_state), - nm_connectivity_state_to_string (state)); -#endif - priv->connectivity_state = state; - _notify (self, PROP_CONNECTIVITY); + case CONCHECK_SCHEDULE_CHECK_EXTERNAL: + /* a external connectivity check delays our periodic check. We reset the counter. */ + priv->concheck_p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); + concheck_periodic_schedule_do (self, now_ns); + return; - 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) - && !ip4_config_merge_and_apply (self, TRUE)) - _LOGW (LOGD_IP4, "Failed to update IPv4 route metric"); - if ( nm_device_get_best_default_route (self, AF_INET6) - && !ip6_config_merge_and_apply (self, TRUE)) - _LOGW (LOGD_IP6, "Failed to update IPv6 route metric"); + case CONCHECK_SCHEDULE_CHECK_PERIODIC: + { + gboolean any_periodic_pending; + NMDeviceConnectivityHandle *handle; + guint old_interval = priv->concheck_p_cur_interval; + + any_periodic_pending = FALSE; + c_list_for_each_entry (handle, &priv->concheck_lst_head, concheck_lst) { + if (handle->is_periodic_bump) { + handle->is_periodic_bump = FALSE; + handle->is_periodic_bump_on_complete = FALSE; + any_periodic_pending = TRUE; + } } + if (any_periodic_pending) { + /* we reached a timeout to schedule a new periodic request, however we still + * have period requests pending that didn't complete yet. We need to bump the + * interval already. */ + priv->concheck_p_cur_interval = NM_MIN (old_interval * 2, priv->concheck_p_max_interval); + } + + /* we just reached a timeout. The expected expiry (exp_expiry) should be + * pretty close to now_ns. + * + * We want to reschedule the timeout at exp_expiry (aka now) + cur_interval. */ + nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); + exp_expiry = priv->concheck_p_cur_basetime_ns + (old_interval * NM_UTILS_NS_PER_SECOND); + new_expiry = exp_expiry + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + tdiff = NM_MAX (new_expiry - now_ns, 0); + priv->concheck_p_cur_basetime_ns = (now_ns + tdiff) - (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + if (concheck_periodic_schedule_do (self, now_ns)) { + handle = concheck_start (self, NULL, NULL, TRUE); + if (old_interval != priv->concheck_p_cur_interval) { + /* we just bumped the interval already when scheduling this check. + * When the handle returns, don't bump a second time. + * + * But if we reach the timeout again before the handle returns (this + * code here) we will still bump the interval. */ + handle->is_periodic_bump_on_complete = FALSE; + } + } + return; } -} -typedef struct { - NMDevice *self; - NMDeviceConnectivityCallback callback; - gpointer user_data; - guint64 seq; -} ConnectivityCheckData; + /* we just got an event that we lost connectivity (that is, concheck returned). We reset + * the interval to min/max or increase the probe interval (bump). */ + case CONCHECK_SCHEDULE_RETURNED_MIN: + priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_max_interval, CONCHECK_P_PROBE_INTERVAL); + break; + case CONCHECK_SCHEDULE_RETURNED_MAX: + priv->concheck_p_cur_interval = priv->concheck_p_max_interval; + break; + case CONCHECK_SCHEDULE_RETURNED_BUMP: + priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_cur_interval * 2, priv->concheck_p_max_interval); + break; + } + + /* we are here, because we returned from a connectivity check and adjust the current interval. + * + * But note that we calculate the new timeout based on the time when we scheduled the + * last check, instead of counting from now. The reaons is, that we want that the times + * when we schedule checks be at precise intervals, without including the time it took for + * the connectivity check. */ + new_expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + tdiff = NM_MAX (new_expiry - nm_utils_get_monotonic_timestamp_ns_cached (&now_ns), 0); + priv->concheck_p_cur_basetime_ns = now_ns + tdiff - (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + concheck_periodic_schedule_do (self, now_ns); +} static void -concheck_done (ConnectivityCheckData *data) +concheck_update_interval (NMDevice *self, gboolean check_now) { - NMDevice *self = data->self; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + guint new_interval; + + new_interval = nm_connectivity_get_interval (concheck_get_mgr (self)); + + new_interval = NM_MIN (new_interval, 7 *24 * 3600); + + if (new_interval != priv->concheck_p_max_interval) { + _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: set interval to %u seconds", new_interval); + priv->concheck_p_max_interval = new_interval; + } - /* The unsolicited connectivity checks don't hook a callback. */ - if (data->callback) - data->callback (data->self, priv->connectivity_state, data->user_data); - g_object_unref (data->self); - g_slice_free (ConnectivityCheckData, data); + if (!new_interval) { + /* this will cancel any potentially pending timeout because max-interval is zero. + * But it logs a nice message... */ + concheck_periodic_schedule_do (self, 0); + + /* also update the fake connectivity state. */ + concheck_update_state (self, NM_CONNECTIVITY_FAKE, TRUE); + return; + } + + concheck_periodic_schedule_set (self, + check_now + ? CONCHECK_SCHEDULE_UPDATE_INTERVAL_RESTART + : CONCHECK_SCHEDULE_UPDATE_INTERVAL); +} + +void +nm_device_check_connectivity_update_interval (NMDevice *self) +{ + concheck_update_interval (self, FALSE); } -#if WITH_CONCHECK static void -concheck_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) +concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow_periodic_bump) { - ConnectivityCheckData *data = user_data; - NMDevice *self = data->self; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnectivity *connectivity = NM_CONNECTIVITY (source_object); - NMConnectivityState state; - GError *error = NULL; - state = nm_connectivity_check_finish (connectivity, result, &error); - if (error) { - _LOGW (LOGD_DEVICE, "connectivity checking on '%s' failed: %s", - nm_device_get_iface (self), error->message); - g_error_free (error); + /* @state is a result of the connectivity check. We only expect a precise + * number of possible values. */ + nm_assert (NM_IN_SET (state, NM_CONNECTIVITY_LIMITED, + NM_CONNECTIVITY_PORTAL, + NM_CONNECTIVITY_FULL, + NM_CONNECTIVITY_FAKE, + NM_CONNECTIVITY_ERROR)); + + if (state == NM_CONNECTIVITY_ERROR) { + /* on error, we don't change the current connectivity state, + * except making UNKNOWN to NONE. */ + state = priv->connectivity_state; + if (state == NM_CONNECTIVITY_UNKNOWN) + state = NM_CONNECTIVITY_NONE; + } else if (state == NM_CONNECTIVITY_FAKE) { + /* If the connectivity check is disabled and we obtain a fake + * result, make an optimistic guess. */ + if (priv->state == NM_DEVICE_STATE_ACTIVATED) { + /* FIXME: the fake connectivity state depends on the availablility of + * a default route. However, we have no mechanism that rechecks the + * value if a device route appears/disappears after the device + * was activated. */ + if (nm_device_get_best_default_route (self, AF_UNSPEC)) + state = NM_CONNECTIVITY_FULL; + else + state = NM_CONNECTIVITY_LIMITED; + } else + state = NM_CONNECTIVITY_NONE; + } + + if (priv->connectivity_state == state) { + /* we got a connectivty update, but the state didn't change. If we were probing, + * we bump the probe frequency. */ + if (allow_periodic_bump) + concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_BUMP); + return; } + /* we need to update the probe interval before emitting signals. Emitting + * a signal might call back into NMDevice and change the probe settings. + * So, do that first. */ + if (state == NM_CONNECTIVITY_FULL) { + /* we reached full connectivity state. Stop probing by setting the + * interval to the max. */ + concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_MAX); + } else if (priv->connectivity_state == NM_CONNECTIVITY_FULL) { + /* we are about to loose connectivity. (re)start probing by setting + * the timeout interval to the min. */ + concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_MIN); + } else { + if (allow_periodic_bump) + concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_BUMP); + } + + _LOGD (LOGD_CONCHECK, "connectivity state changed from %s to %s", + nm_connectivity_state_to_string (priv->connectivity_state), + nm_connectivity_state_to_string (state)); + priv->connectivity_state = state; + + _notify (self, PROP_CONNECTIVITY); + g_signal_emit (self, signals[CONNECTIVITY_CHANGED], 0); - if (data->seq == priv->concheck_seq) - update_connectivity_state (data->self, state); - concheck_done (data); + 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"); + } } -#endif /* WITH_CONCHECK */ -static gboolean -no_concheck (gpointer user_data) +static void +concheck_handle_complete (NMDeviceConnectivityHandle *handle, + GError *error) { - ConnectivityCheckData *data = user_data; + /* The moment we invoke the callback, we unlink it. It signals + * that @handle is handled -- as far as the callee of callback + * is concerned. */ + c_list_unlink (&handle->concheck_lst); - concheck_done (data); - return G_SOURCE_REMOVE; + if (handle->c_handle) + nm_connectivity_check_cancel (handle->c_handle); + + if (handle->callback) { + handle->callback (handle->self, + handle, + NM_DEVICE_GET_PRIVATE (handle->self)->connectivity_state, + error, + handle->user_data); + } + + g_slice_free (NMDeviceConnectivityHandle, handle); } -void -nm_device_check_connectivity (NMDevice *self, - NMDeviceConnectivityCallback callback, - gpointer user_data) +static void +concheck_cb (NMConnectivity *connectivity, + NMConnectivityCheckHandle *c_handle, + NMConnectivityState state, + GError *error, + gpointer user_data) { - ConnectivityCheckData *data; -#if WITH_CONCHECK - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); -#endif + _nm_unused gs_unref_object NMDevice *self_keep_alive = NULL; + NMDevice *self; + NMDevicePrivate *priv; + NMDeviceConnectivityHandle *handle; + NMDeviceConnectivityHandle *other_handle; + gboolean handle_is_alive; + gboolean allow_periodic_bump; + gboolean any_periodic_before; + gboolean any_periodic_after; + guint64 seq; - data = g_slice_new0 (ConnectivityCheckData); - data->self = g_object_ref (self); - data->callback = callback; - data->user_data = user_data; + handle = user_data; + nm_assert (handle->c_handle == c_handle); + nm_assert (NM_IS_DEVICE (handle->self)); -#if WITH_CONCHECK - if (priv->concheck_periodic_id) { - data->seq = ++priv->concheck_seq; + handle->c_handle = NULL; + self = handle->self; - /* Kick off a real connectivity check. */ - nm_connectivity_check_async (nm_connectivity_get (), - nm_device_get_ip_iface (self), - concheck_cb, - data); + if (nm_utils_error_is_cancelled (error, FALSE)) { + /* the only place where we nm_connectivity_check_cancel(@c_handle), is + * from inside concheck_handle_complete(). This is a recursive call, + * nothing to do. */ + _LOGT (LOGD_CONCHECK, "connectivity: complete check (seq:%llu, cancelled)", + (long long unsigned) handle->seq); return; } -#endif - /* Fake one. */ - g_idle_add (no_concheck, data); + self_keep_alive = g_object_ref (self); + + _LOGT (LOGD_CONCHECK, "connectivity: complete check (seq:%llu, state:%s%s%s%s)", + (long long unsigned) handle->seq, + nm_connectivity_state_to_string (state), + NM_PRINT_FMT_QUOTED (error, ", error: ", error->message, "", "")); + + /* we keep NMConnectivity instance alive. It cannot be disposing. */ + nm_assert (!nm_utils_error_is_cancelled (error, TRUE)); + + /* keep @self alive, while we invoke callbacks. */ + priv = NM_DEVICE_GET_PRIVATE (self); + + nm_assert (!handle || c_list_contains (&priv->concheck_lst_head, &handle->concheck_lst)); + + seq = handle->seq; + + /* find out, if there are any periodic checks pending (either whether they + * were scheduled before or after @handle. */ + any_periodic_before = FALSE; + any_periodic_after = FALSE; + c_list_for_each_entry (other_handle, &priv->concheck_lst_head, concheck_lst) { + if (other_handle->is_periodic_bump_on_complete) { + if (other_handle->seq < seq) + any_periodic_before = TRUE; + else if (other_handle->seq > seq) + any_periodic_after = TRUE; + } + } + if (NM_IN_SET (state, NM_CONNECTIVITY_ERROR)) { + /* the request failed. We consider this periodic check only as completed if + * this was a periodic check, and there are not checks pending (either + * before or after this one). + * + * We allow_periodic_bump, if the request failed and there are + * still other requests periodic pending. */ + allow_periodic_bump = handle->is_periodic_bump_on_complete + && !any_periodic_before + && !any_periodic_after; + } else { + /* the request succeeded. This marks the completion of a periodic check, + * if this handle was periodic, or any previously scheduled one (that + * we are going to complete below). */ + allow_periodic_bump = handle->is_periodic_bump_on_complete + || any_periodic_before; + } + + /* first update the new state, and emit signals. */ + concheck_update_state (self, state, allow_periodic_bump); + + handle_is_alive = FALSE; + + /* we might have invoked callbacks during concheck_update_state(). The caller might have + * cancelled and thus destroyed @handle. We have to check whether handle is still alive, + * by searching it in the list of alive handles. + * + * Also, we might want to complete all pending callbacks that were started before + * @handle, as they are automatically obsoleted. */ +check_handles: + c_list_for_each_entry (other_handle, &priv->concheck_lst_head, concheck_lst) { + if (other_handle->seq >= seq) { + /* it's not guaranteed that @handle is still in the list. It might already + * be canceled while invoking callbacks for a previous other_handle. + * If it is already cancelled, @handle is a dangling pointer. + * + * Since @seq is assigned uniquely and increasing, either @other_handle is + * @handle (and thus, handle is alive), or it isn't. */ + if (other_handle == handle) + handle_is_alive = TRUE; + break; + } + + nm_assert (other_handle != handle); + + if (!NM_IN_SET (state, NM_CONNECTIVITY_ERROR)) { + /* we also want to complete handles that were started before the current + * @handle. Their response is out-dated. */ + concheck_handle_complete (other_handle, NULL); + + /* we invoked callbacks, other handles might be cancelled and removed from the list. + * Need to iterate the list from the start. */ + goto check_handles; + } + } + + if (!handle_is_alive) { + /* We didn't find @handle in the list of alive handles. Thus, the handles + * was cancelled while we were invoking events. Nothing to do, and don't + * touch the dangling pointer. */ + return; + } + + concheck_handle_complete (handle, NULL); } -NMConnectivityState -nm_device_get_connectivity_state (NMDevice *self) +static NMDeviceConnectivityHandle * +concheck_start (NMDevice *self, + NMDeviceConnectivityCallback callback, + gpointer user_data, + gboolean is_periodic) { - g_return_val_if_fail (NM_IS_DEVICE (self), NM_CONNECTIVITY_UNKNOWN); + static guint64 seq_counter = 0; + NMDevicePrivate *priv; + NMDeviceConnectivityHandle *handle; - return NM_DEVICE_GET_PRIVATE (self)->connectivity_state; + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + priv = NM_DEVICE_GET_PRIVATE (self); + + handle = g_slice_new0 (NMDeviceConnectivityHandle); + handle->seq = ++seq_counter; + handle->self = self; + handle->callback = callback; + handle->user_data = user_data; + handle->is_periodic = is_periodic; + handle->is_periodic_bump = is_periodic; + handle->is_periodic_bump_on_complete = is_periodic; + + c_list_link_tail (&priv->concheck_lst_head, &handle->concheck_lst); + + _LOGT (LOGD_CONCHECK, "connectivity: start check (seq:%llu%s)", + (long long unsigned) handle->seq, + is_periodic ? ", periodic-check" : ""); + + handle->c_handle = nm_connectivity_check_start (concheck_get_mgr (self), + nm_device_get_ip_iface (self), + concheck_cb, + handle); + return handle; } -#if WITH_CONCHECK -static void -concheck_periodic (NMConnectivity *connectivity, NMDevice *self) +NMDeviceConnectivityHandle * +nm_device_check_connectivity (NMDevice *self, + NMDeviceConnectivityCallback callback, + gpointer user_data) { - nm_device_check_connectivity (self, NULL, NULL); + NMDeviceConnectivityHandle *handle; + + if (!concheck_is_possible (self)) + return NULL; + + concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_CHECK_EXTERNAL); + handle = concheck_start (self, callback, user_data, FALSE); + return handle; } -#endif -static void -concheck_periodic_update (NMDevice *self) +void +nm_device_check_connectivity_cancel (NMDeviceConnectivityHandle *handle) { -#if WITH_CONCHECK - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean check_enable; + gs_free_error GError *cancelled_error = NULL; - check_enable = (priv->state == NM_DEVICE_STATE_ACTIVATED) - && nm_device_get_best_default_route (self, AF_UNSPEC); + g_return_if_fail (handle); + g_return_if_fail (NM_IS_DEVICE (handle->self)); + g_return_if_fail (!c_list_is_empty (&handle->concheck_lst)); - if (check_enable && !priv->concheck_periodic_id) { - /* We just gained a default route. Enable periodic checking. */ - priv->concheck_periodic_id = g_signal_connect (nm_connectivity_get (), - NM_CONNECTIVITY_PERIODIC_CHECK, - G_CALLBACK (concheck_periodic), self); - /* Also kick off a check right away. */ - nm_device_check_connectivity (self, NULL, NULL); - } else if (!check_enable && priv->concheck_periodic_id) { - /* The default route has gone off, and so has connectivity. */ - nm_clear_g_signal_handler (nm_connectivity_get (), &priv->concheck_periodic_id); - update_connectivity_state (self, NM_CONNECTIVITY_NONE); - } -#else - /* update_connectivity_state() figures out how to lie about - * connectivity state if the actual state is not really known. */ - update_connectivity_state (self, NM_CONNECTIVITY_UNKNOWN); -#endif + /* nobody has access to periodic handles, and cannot cancel + * them externally. */ + nm_assert (!handle->is_periodic); + + nm_utils_error_set_cancelled (&cancelled_error, FALSE, "NMDevice"); + concheck_handle_complete (handle, cancelled_error); +} + +NMConnectivityState +nm_device_get_connectivity_state (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), NM_CONNECTIVITY_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE (self)->connectivity_state; } /*****************************************************************************/ @@ -2226,7 +2888,7 @@ nm_device_master_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *c /* Since slave devices don't have their own IP configuration, * set the MTU here. */ - _commit_mtu (slave, NM_DEVICE_GET_PRIVATE (slave)->ip4_config); + _commit_mtu (slave, NM_DEVICE_GET_PRIVATE (slave)->ip_config_4); return success; } @@ -2364,8 +3026,6 @@ nm_device_update_dynamic_ip_setup (NMDevice *self) { NMDevicePrivate *priv; GError *error = NULL; - gconstpointer addr; - size_t addr_length; g_return_if_fail (NM_IS_DEVICE (self)); @@ -2398,8 +3058,6 @@ nm_device_update_dynamic_ip_setup (NMDevice *self) if (priv->lldp_listener && nm_lldp_listener_is_running (priv->lldp_listener)) { nm_lldp_listener_stop (priv->lldp_listener); - addr = nm_platform_link_get_address (nm_device_get_platform (self), priv->ifindex, &addr_length); - if (!nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), &error)) { _LOGD (LOGD_DEVICE, "LLDP listener %p could not be restarted: %s", priv->lldp_listener, error->message); @@ -2636,12 +3294,14 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self) now = nm_utils_get_monotonic_timestamp_s (); - head_entry = nm_ip6_config_lookup_addresses (priv->ip6_config); + 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, preferred; + gint32 base; if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) continue; @@ -2653,19 +3313,35 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self) if (addr->plen != 64) continue; + /* resolve the timestamps relative to a new base. + * + * Note that for convenience, platform @addr might have timestamp and/or + * lifetime unset. We don't allow that flexibility for ndisc and require + * well defined timestamps. */ + if (addr->timestamp) { + nm_assert (addr->timestamp < G_MAXINT32); + base = addr->timestamp; + } else + base = now; + + lifetime = nm_utils_lifetime_get (addr->timestamp, addr->lifetime, addr->preferred, + base, &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->timestamp = addr->timestamp; - ndisc_addr->lifetime = addr->lifetime; - ndisc_addr->preferred = addr->preferred; + ndisc_addr->timestamp = base; + ndisc_addr->lifetime = lifetime; + ndisc_addr->preferred = preferred; } - len = nm_ip6_config_get_num_nameservers (priv->ip6_config); + 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->ip6_config, 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); @@ -2674,11 +3350,11 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self) ndisc_nameserver->lifetime = NM_NDISC_ROUTER_LIFETIME; } - len = nm_ip6_config_get_num_searches (priv->ip6_config); + 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->ip6_config, 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); @@ -2804,11 +3480,11 @@ device_link_changed (NMDevice *self) /* the link was down and just came up. That happens for example, while changing MTU. * We must restore IP configuration. */ if (priv->ip4_state == IP_DONE) { - if (!ip4_config_merge_and_apply (self, TRUE)) + if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) _LOGW (LOGD_IP4, "failed applying IP4 config after link comes up again"); } if (priv->ip6_state == IP_DONE) { - if (!ip6_config_merge_and_apply (self, TRUE)) + if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) _LOGW (LOGD_IP6, "failed applying IP6 config after link comes up again"); } } @@ -3312,8 +3988,8 @@ 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_ip4_config_id); - g_return_if_fail (!priv->queued_ip6_config_id); + 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", G_OBJECT_TYPE_NAME (self), plink ? plink->ifindex : 0); @@ -3360,7 +4036,7 @@ realize_start_setup (NMDevice *self, if (nm_platform_check_kernel_support (nm_device_get_platform (self), NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) - priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ifindex); + priv->ipv6ll_handle = nm_platform_link_get_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ifindex); if (nm_platform_link_supports_sriov (nm_device_get_platform (self), priv->ifindex)) capabilities |= NM_DEVICE_CAP_SRIOV; @@ -3459,6 +4135,9 @@ 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); @@ -3540,7 +4219,6 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) g_return_val_if_fail (priv->iface != NULL, FALSE); g_return_val_if_fail (priv->real, FALSE); - ifindex = nm_device_get_ifindex (self); _LOGD (LOGD_DEVICE, "unrealize (ifindex %d)", ifindex > 0 ? ifindex : 0); @@ -3684,7 +4362,7 @@ nm_device_notify_component_added (NMDevice *self, GObject *component) * because that ethernet interface is controlled by the WWAN device and cannot * be used independently of the WWAN device. * - * Returns: %TRUE if @self or it's components owns the interface name, + * Returns: %TRUE if @self or its components own the interface name, * %FALSE if not */ gboolean @@ -3974,7 +4652,7 @@ check_ip_state (NMDevice *self, gboolean may_fail, gboolean full_state_update) /* 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)) + if ( nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request.obj)) && !priv->is_enslaved) return; @@ -4162,8 +4840,8 @@ nm_device_removed (NMDevice *self, gboolean unconfigure_ip_config) if (!unconfigure_ip_config) return; - nm_device_set_ip4_config (self, NULL, FALSE, NULL); - nm_device_set_ip6_config (self, NULL, FALSE); + nm_device_set_ip_config (self, AF_INET, NULL, FALSE, NULL); + nm_device_set_ip_config (self, AF_INET6, NULL, FALSE, NULL); } static gboolean @@ -4415,9 +5093,9 @@ device_has_config (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); /* Check for IP configuration. */ - if (priv->ip4_config && nm_ip4_config_get_num_addresses (priv->ip4_config)) + if (priv->ip_config_4 && nm_ip4_config_get_num_addresses (priv->ip_config_4)) return TRUE; - if (priv->ip6_config && nm_ip6_config_get_num_addresses (priv->ip6_config)) + if (priv->ip_config_6 && nm_ip6_config_get_num_addresses (priv->ip_config_6)) return TRUE; /* The existence of a software device is good enough. */ @@ -4542,10 +5220,10 @@ 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->ip4_config); + s_ip4 = nm_ip4_config_create_setting (priv->ip_config_4); nm_connection_add_setting (connection, s_ip4); - s_ip6 = nm_ip6_config_create_setting (priv->ip6_config); + s_ip6 = nm_ip6_config_create_setting (priv->ip_config_6); nm_connection_add_setting (connection, s_ip6); nm_connection_add_setting (connection, nm_setting_proxy_new ()); @@ -4602,34 +5280,45 @@ nm_device_generate_connection (NMDevice *self, return g_steal_pointer (&connection); } +/** + * nm_device_complete_connection: + * + * Complete the connection. This is solely used for AddAndActivate where the user + * may pass in an incomplete connection and a device, and the device tries to + * make sense of it and complete it for activation. Otherwise, this is not + * used. + * + * Returns: success or failure. + */ gboolean nm_device_complete_connection (NMDevice *self, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { - gboolean success = FALSE; + NMDeviceClass *klass; - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (connection != NULL, FALSE); + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); - if (!NM_DEVICE_GET_CLASS (self)->complete_connection) { + klass = NM_DEVICE_GET_CLASS (self); + + if (!klass->complete_connection) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "Device class %s had no complete_connection method", G_OBJECT_TYPE_NAME (self)); return FALSE; } - success = NM_DEVICE_GET_CLASS (self)->complete_connection (self, - connection, - specific_object, - existing_connections, - error); - if (success) - success = nm_connection_verify (connection, error); + if (!klass->complete_connection (self, + connection, + specific_object, + existing_connections, + error)) + return FALSE; - return success; + return nm_connection_verify (connection, error); } gboolean @@ -4738,15 +5427,12 @@ nm_device_check_connection_compatible (NMDevice *self, NMConnection *connection) gboolean nm_device_check_slave_connection_compatible (NMDevice *self, NMConnection *slave) { - NMDevicePrivate *priv; NMSettingConnection *s_con; const char *connection_type, *slave_type; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); g_return_val_if_fail (NM_IS_CONNECTION (slave), FALSE); - priv = NM_DEVICE_GET_PRIVATE (self); - if (!nm_device_is_master (self)) return FALSE; @@ -5159,8 +5845,9 @@ activate_stage1_device_prepare (NMDevice *self) _set_ip_state (self, AF_INET6, IP_NONE); /* Notify the new ActiveConnection along with the state change */ - priv->act_request_public = TRUE; - _notify (self, PROP_ACTIVE_CONNECTION); + nm_dbus_track_obj_path_set (&priv->act_request, + priv->act_request.obj, + TRUE); nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); @@ -5181,7 +5868,6 @@ activate_stage1_device_prepare (NMDevice *self) nm_device_activate_schedule_stage2_device_config (self); } - /* * nm_device_activate_schedule_stage1_device_prepare * @@ -5196,7 +5882,7 @@ nm_device_activate_schedule_stage1_device_prepare (NMDevice *self) g_return_if_fail (NM_IS_DEVICE (self)); priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->act_request); + g_return_if_fail (priv->act_request.obj); activation_source_schedule (self, activate_stage1_device_prepare, AF_INET); } @@ -5214,8 +5900,6 @@ lldp_init (NMDevice *self, gboolean restart) if (priv->ifindex > 0 && lldp_rx_enabled (self)) { gs_free_error GError *error = NULL; - gconstpointer addr; - size_t addr_length; if (priv->lldp_listener) { if (restart && nm_lldp_listener_is_running (priv->lldp_listener)) @@ -5229,8 +5913,6 @@ lldp_init (NMDevice *self, gboolean restart) } if (!nm_lldp_listener_is_running (priv->lldp_listener)) { - addr = nm_platform_link_get_address (nm_device_get_platform (self), priv->ifindex, &addr_length); - if (nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), &error)) _LOGD (LOGD_DEVICE, "LLDP listener %p started", priv->lldp_listener); else { @@ -5377,7 +6059,7 @@ activate_stage2_device_config (NMDevice *self) if (slave_state == NM_DEVICE_STATE_IP_CONFIG) nm_device_master_enslave_slave (self, info->slave, nm_device_get_applied_connection (info->slave)); - else if ( priv->act_request + else if ( priv->act_request.obj && nm_device_sys_iface_state_is_external (self) && slave_state <= NM_DEVICE_STATE_DISCONNECTED) nm_device_queue_recheck_assume (info->slave); @@ -5387,7 +6069,6 @@ activate_stage2_device_config (NMDevice *self) nm_device_activate_schedule_stage3_ip_config_start (self); } - /* * nm_device_activate_schedule_stage2_device_config * @@ -5402,10 +6083,10 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self) g_return_if_fail (NM_IS_DEVICE (self)); priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->act_request); + g_return_if_fail (priv->act_request.obj); if (!priv->master_ready_handled) { - NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request); + NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request.obj); NMActiveConnection *master; master = nm_active_connection_get_master (active); @@ -5443,13 +6124,9 @@ nm_device_ip_method_failed (NMDevice *self, int addr_family, NMDeviceStateReason reason) { - NMDevicePrivate *priv; - g_return_if_fail (NM_IS_DEVICE (self)); g_return_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - priv = NM_DEVICE_GET_PRIVATE (self); - _set_ip_state (self, addr_family, IP_FAIL); if (get_ip_config_may_fail (self, addr_family)) @@ -5490,16 +6167,16 @@ get_ipv4_dad_timeout (NMDevice *self) } static void -arping_data_destroy (gpointer ptr, GClosure *closure) +acd_data_destroy (gpointer ptr, GClosure *closure) { - ArpingData *data = ptr; + AcdData *data = ptr; int i; if (data) { for (i = 0; data->configs && data->configs[i]; i++) g_object_unref (data->configs[i]); g_free (data->configs); - g_slice_free (ArpingData, data); + g_slice_free (AcdData, data); } } @@ -5519,7 +6196,7 @@ ipv4_manual_method_apply (NMDevice *self, NMIP4Config **configs, gboolean succes } static void -arping_manager_probe_terminated (NMArpingManager *arping_manager, ArpingData *data) +acd_manager_probe_terminated (NMAcdManager *acd_manager, AcdData *data) { NMDevice *self; NMDevicePrivate *priv; @@ -5534,7 +6211,7 @@ arping_manager_probe_terminated (NMArpingManager *arping_manager, ArpingData *da for (i = 0; data->configs && data->configs[i]; i++) { nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, data->configs[i], &address) { - result = nm_arping_manager_check_address (arping_manager, address->address); + result = nm_acd_manager_check_address (acd_manager, address->address); success &= result; _NMLOG (result ? LOGL_DEBUG : LOGL_WARN, @@ -5547,8 +6224,8 @@ arping_manager_probe_terminated (NMArpingManager *arping_manager, ArpingData *da data->callback (self, data->configs, success); - priv->arping.dad_list = g_slist_remove (priv->arping.dad_list, arping_manager); - nm_arping_manager_destroy (arping_manager); + priv->acd.dad_list = g_slist_remove (priv->acd.dad_list, acd_manager); + nm_acd_manager_destroy (acd_manager); } /** @@ -5562,18 +6239,17 @@ arping_manager_probe_terminated (NMArpingManager *arping_manager, ArpingData *da * be started. @configs will be unreferenced after @cb has been called. */ static void -ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb) +ipv4_dad_start (NMDevice *self, NMIP4Config **configs, AcdCallback cb) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMArpingManager *arping_manager; + NMAcdManager *acd_manager; const NMPlatformIP4Address *address; NMDedupMultiIter ipconf_iter; - ArpingData *data; + AcdData *data; guint timeout; gboolean ret, addr_found; - const guint8 *hw_addr; - size_t hw_addr_len = 0; - GError *error = NULL; + const guint8 *hwaddr_arr; + size_t length; guint i; g_return_if_fail (NM_IS_DEVICE (self)); @@ -5588,14 +6264,14 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb) } timeout = get_ipv4_dad_timeout (self); - hw_addr = nm_platform_link_get_address (nm_device_get_platform (self), - nm_device_get_ip_ifindex (self), - &hw_addr_len); + hwaddr_arr = nm_platform_link_get_address (nm_device_get_platform (self), + nm_device_get_ip_ifindex (self), + &length); if ( !timeout - || !hw_addr - || !hw_addr_len + || !hwaddr_arr || !addr_found + || length != ETH_ALEN || nm_device_sys_iface_state_is_external_or_assume (self)) { /* DAD not needed, signal success */ @@ -5608,36 +6284,36 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb) return; } - /* don't take additional references of @arping_manager that outlive @self. + /* don't take additional references of @acd_manager that outlive @self. * Otherwise, the callback can be invoked on a dangling pointer as we don't * disconnect the handler. */ - arping_manager = nm_arping_manager_new (nm_device_get_ip_ifindex (self)); - priv->arping.dad_list = g_slist_append (priv->arping.dad_list, arping_manager); + acd_manager = nm_acd_manager_new (nm_device_get_ip_ifindex (self), hwaddr_arr, length); + priv->acd.dad_list = g_slist_append (priv->acd.dad_list, acd_manager); - data = g_slice_new0 (ArpingData); + data = g_slice_new0 (AcdData); data->configs = configs; data->callback = cb; data->device = self; for (i = 0; configs[i]; i++) { nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, configs[i], &address) - nm_arping_manager_add_address (arping_manager, address->address); + nm_acd_manager_add_address (acd_manager, address->address); } - g_signal_connect_data (arping_manager, NM_ARPING_MANAGER_PROBE_TERMINATED, - G_CALLBACK (arping_manager_probe_terminated), data, - arping_data_destroy, 0); + g_signal_connect_data (acd_manager, NM_ACD_MANAGER_PROBE_TERMINATED, + G_CALLBACK (acd_manager_probe_terminated), data, + acd_data_destroy, 0); - ret = nm_arping_manager_start_probe (arping_manager, timeout, &error); + ret = nm_acd_manager_start_probe (acd_manager, timeout); if (!ret) { - _LOGW (LOGD_DEVICE, "arping probe failed: %s", error->message); + _LOGW (LOGD_DEVICE, "acd probe failed"); /* DAD could not be started, signal success */ cb (self, configs, TRUE); - priv->arping.dad_list = g_slist_remove (priv->arping.dad_list, arping_manager); - nm_arping_manager_destroy (arping_manager); + priv->acd.dad_list = g_slist_remove (priv->acd.dad_list, acd_manager); + nm_acd_manager_destroy (acd_manager); } } @@ -5699,10 +6375,10 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) NMIP4Config *config; int r; - if (priv->act_request == NULL) + if (priv->act_request.obj == NULL) return; - connection = nm_act_request_get_applied_connection (priv->act_request); + connection = nm_act_request_get_applied_connection (priv->act_request.obj); g_assert (connection); /* Ignore if the connection isn't an AutoIP connection */ @@ -5736,9 +6412,8 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) nm_clear_g_source (&priv->ipv4ll_timeout); nm_device_activate_schedule_ip4_config_result (self, config); } else if (priv->ip4_state == IP_DONE) { - g_clear_object (&priv->dev_ip4_config); - priv->dev_ip4_config = g_object_ref (config); - if (!ip4_config_merge_and_apply (self, TRUE)) { + applied_config_init (&priv->dev_ip4_config, 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); } @@ -5834,55 +6509,42 @@ ipv4ll_start (NMDevice *self) /*****************************************************************************/ static void -ensure_con_ip4_config (NMDevice *self) +ensure_con_ip_config (NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; + const gboolean IS_IPv4 = (addr_family == AF_INET); + NMIPConfig *con_ip_config; - if (priv->con_ip4_config) + if (priv->con_ip_config_x[IS_IPv4]) return; connection = nm_device_get_applied_connection (self); if (!connection) return; - priv->con_ip4_config = _ip4_config_new (self); - nm_ip4_config_merge_setting (priv->con_ip4_config, - nm_connection_get_setting_ip4_config (connection), - nm_device_get_route_table (self, AF_INET, TRUE), - nm_device_get_route_metric (self, AF_INET)); + con_ip_config = _ip_config_new (self, addr_family); - if (nm_device_sys_iface_state_is_external_or_assume (self)) { - /* For assumed connections ignore all addresses and routes. */ - nm_ip4_config_reset_addresses (priv->con_ip4_config); - nm_ip4_config_reset_routes (priv->con_ip4_config); + if (IS_IPv4) { + nm_ip4_config_merge_setting (NM_IP4_CONFIG (con_ip_config), + nm_connection_get_setting_ip4_config (connection), + _get_mdns (self), + nm_device_get_route_table (self, addr_family, TRUE), + nm_device_get_route_metric (self, addr_family)); + } 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, TRUE), + nm_device_get_route_metric (self, addr_family)); } -} - -static void -ensure_con_ip6_config (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; - - if (priv->con_ip6_config) - return; - - connection = nm_device_get_applied_connection (self); - if (!connection) - return; - - priv->con_ip6_config = _ip6_config_new (self); - nm_ip6_config_merge_setting (priv->con_ip6_config, - nm_connection_get_setting_ip6_config (connection), - nm_device_get_route_table (self, AF_INET6, TRUE), - nm_device_get_route_metric (self, AF_INET6)); if (nm_device_sys_iface_state_is_external_or_assume (self)) { /* For assumed connections ignore all addresses and routes. */ - nm_ip6_config_reset_addresses (priv->con_ip6_config); - nm_ip6_config_reset_routes (priv->con_ip6_config); + nm_ip_config_reset_addresses (con_ip_config); + nm_ip_config_reset_routes (con_ip_config); } + + priv->con_ip_config_x[IS_IPv4] = con_ip_config; } /*****************************************************************************/ @@ -5910,105 +6572,222 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) } if (priv->dhcp4.config) { - nm_exported_object_clear_and_unexport (&priv->dhcp4.config); + nm_dbus_object_clear_and_unexport (&priv->dhcp4.config); _notify (self, PROP_DHCP4_CONFIG); } } static gboolean -ip4_config_merge_and_apply (NMDevice *self, - gboolean commit) +ip_config_merge_and_apply (NMDevice *self, + int addr_family, + gboolean commit) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; gboolean success; - NMIP4Config *composite; + 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; - gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL; + const char *ip6_addr_gen_token = NULL; + const gboolean IS_IPv4 = (addr_family == AF_INET); if (nm_device_sys_iface_state_is_external (self)) - commit = 0; + commit = FALSE; - /* Apply ignore-auto-routes and ignore-auto-dns settings */ connection = nm_device_get_applied_connection (self); + + /* Apply ignore-auto-routes and ignore-auto-dns settings */ if (connection) { - NMSettingIPConfig *s_ip4 = nm_connection_get_setting_ip4_config (connection); + NMSettingIPConfig *s_ip = IS_IPv4 + ? nm_connection_get_setting_ip4_config (connection) + : nm_connection_get_setting_ip6_config (connection); - if (s_ip4) { - ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes (s_ip4); - ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns (s_ip4); + 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_ip4) - || nm_setting_ip_config_get_gateway (s_ip4); + 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 = _ip4_config_new (self); - init_ip4_config_dns_priority (self, composite); + composite = _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_ip4_config_id) - update_ext_ip_config (self, AF_INET, FALSE, FALSE); - ensure_con_ip4_config (self); + 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 (commit) - priv->default_route_metric_penalty_ip4_has = default_route_metric_penalty_detect (self); + 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, + }; - if (priv->dev_ip4_config) { - nm_ip4_config_merge (composite, priv->dev_ip4_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, AF_INET)); + 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); + 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_ip4_config); + 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)); + } } - for (iter = priv->vpn4_configs; iter; iter = iter->next) - nm_ip4_config_merge (composite, iter->data, NM_IP_CONFIG_MERGE_DEFAULT, 0); + 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 (priv->ext_ip4_config) - nm_ip4_config_merge (composite, priv->ext_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT, 0); + 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_DEFAULT, 0); /* Merge WWAN config *last* to ensure modem-given settings overwrite * any external stuff set by pppd or other scripts. */ - if (priv->wwan_ip4_config) { - nm_ip4_config_merge (composite, priv->wwan_ip4_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, AF_INET)); + config = applied_config_get_current (&priv->wwan_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_ip4_config is empty. */ - if (priv->con_ip4_config) { - nm_ip4_config_merge (composite, priv->con_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT, - default_route_metric_penalty_get (self, AF_INET)); + * 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) { - nm_ip4_config_add_dependent_routes (composite, - nm_device_get_route_table (self, AF_INET, TRUE), - nm_device_get_route_metric (self, AF_INET), - &ip4_dev_route_blacklist); + if (IS_IPv4) { + nm_ip4_config_add_dependent_routes (NM_IP4_CONFIG (composite), + nm_device_get_route_table (self, addr_family, TRUE), + nm_device_get_route_metric (self, addr_family), + &ip4_dev_route_blacklist); + } else { + nm_ip6_config_add_dependent_routes (NM_IP6_CONFIG (composite), + nm_device_get_route_table (self, addr_family, TRUE), + nm_device_get_route_metric (self, addr_family)); + } } + 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) { + if (commit) { + NMUtilsIPv6IfaceId iid; + + if ( ip6_addr_gen_token + && nm_utils_ipv6_interface_identifier_get_from_token (&iid, ip6_addr_gen_token)) { + nm_platform_link_set_ipv6_token (nm_device_get_platform (self), + nm_device_get_ip_ifindex (self), + iid); + } + } + } + + success = nm_device_set_ip_config (self, addr_family, composite, commit, ip4_dev_route_blacklist); if (commit) { - if (NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit) - NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, composite); + if (IS_IPv4) + priv->v4_commit_first_time = FALSE; + else + priv->v6_commit_first_time = FALSE; } - success = nm_device_set_ip4_config (self, composite, commit, ip4_dev_route_blacklist); - g_object_unref (composite); - - if (commit) - priv->v4_commit_first_time = FALSE; return success; } @@ -6019,10 +6798,9 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config) g_return_val_if_fail (config, FALSE); - g_clear_object (&priv->dev_ip4_config); - priv->dev_ip4_config = g_object_ref (config); + applied_config_init (&priv->dev_ip4_config, config); - if (!ip4_config_merge_and_apply (self, TRUE)) { + if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) { _LOGW (LOGD_DHCP4, "failed to update IPv4 config for DHCP change."); return FALSE; } @@ -6062,8 +6840,8 @@ dhcp4_fail (NMDevice *self, gboolean timeout) * on the interface. */ if ( priv->ip4_state == IP_DONE - && priv->con_ip4_config - && nm_ip4_config_get_num_addresses (priv->con_ip4_config) > 0) + && priv->con_ip_config_4 + && nm_ip4_config_get_num_addresses (priv->con_ip_config_4) > 0) goto clear_config; /* Fail the method in case of timeout or failure during initial @@ -6094,7 +6872,7 @@ dhcp4_fail (NMDevice *self, gboolean timeout) clear_config: /* The previous configuration is no longer valid */ if (priv->dhcp4.config) { - nm_exported_object_clear_and_unexport (&priv->dhcp4.config); + nm_dbus_object_clear_and_unexport (&priv->dhcp4.config); priv->dhcp4.config = nm_dhcp4_config_new (); _notify (self, PROP_DHCP4_CONFIG); } @@ -6159,6 +6937,7 @@ dhcp4_state_changed (NMDhcpClient *client, manual = _ip4_config_new (self); nm_ip4_config_merge_setting (manual, nm_connection_get_setting_ip4_config (connection), + NM_SETTING_CONNECTION_MDNS_DEFAULT, nm_device_get_route_table (self, AF_INET, TRUE), nm_device_get_route_metric (self, AF_INET)); @@ -6234,14 +7013,105 @@ get_dhcp_timeout (NMDevice *self, int addr_family) return timeout ?: NM_DHCP_TIMEOUT_DEFAULT; } +static GBytes * +dhcp4_get_client_id (NMDevice *self, NMConnection *connection) +{ + NMSettingIPConfig *s_ip4; + const char *client_id; + gs_free char *client_id_default = NULL; + guint8 *client_id_buf; + gboolean is_mac; + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + client_id = nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4)); + + if (!client_id) { + client_id_default = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "ipv4.dhcp-client-id", self); + if (client_id_default && client_id_default[0]) + client_id = client_id_default; + } + + if (!client_id) + return NULL; + + if ( (is_mac = nm_streq (client_id, "mac")) + || nm_streq (client_id, "perm-mac")) { + const char *hwaddr; + char addr_buf[NM_UTILS_HWADDR_LEN_MAX]; + gsize addr_len; + guint8 addr_type; + + hwaddr = is_mac + ? nm_device_get_hw_address (self) + : nm_device_get_permanent_hw_address (self); + if (!hwaddr) + return NULL; + + if (!_nm_utils_hwaddr_aton (hwaddr, addr_buf, sizeof (addr_buf), &addr_len)) + g_return_val_if_reached (NULL); + + switch (addr_len) { + case ETH_ALEN: + addr_type = ARPHRD_ETHER; + break; + default: + /* unsupported type. */ + return NULL; + } + + client_id_buf = g_malloc (addr_len + 1); + client_id_buf[0] = addr_type; + memcpy (&client_id_buf[1], addr_buf, addr_len); + return g_bytes_new_take (client_id_buf, addr_len + 1); + } + + if (nm_streq (client_id, "stable")) { + NMUtilsStableType stable_type; + const char *stable_id; + GChecksum *sum; + guint8 buf[20]; + gsize buf_size; + guint32 salted_header; + const guint8 *secret_key; + gsize secret_key_len; + + stable_id = _get_stable_id (self, connection, &stable_type); + if (!stable_id) + g_return_val_if_reached (NULL); + + salted_header = htonl (2011610591 + stable_type); + + nm_utils_secret_key_get (&secret_key, &secret_key_len); + + sum = g_checksum_new (G_CHECKSUM_SHA1); + + g_checksum_update (sum, (const guchar *) &salted_header, sizeof (salted_header)); + g_checksum_update (sum, (const guchar *) stable_id, strlen (stable_id) + 1); + g_checksum_update (sum, (const guchar *) secret_key, secret_key_len); + + buf_size = sizeof (buf); + g_checksum_get_digest (sum, buf, &buf_size); + nm_assert (buf_size == sizeof (buf)); + + g_checksum_free (sum); + + client_id_buf = g_malloc (1 + 15); + client_id_buf[0] = 0; + memcpy (&client_id_buf[1], buf, 15); + return g_bytes_new_take (client_id_buf, 1 + 15); + } + + return nm_dhcp_utils_client_id_string_to_bytes (client_id); +} + static NMActStageReturn dhcp4_start (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMSettingIPConfig *s_ip4; - const guint8 *hw_addr; - size_t hw_addr_len = 0; - GByteArray *tmp = NULL; + gs_unref_bytes GBytes *hwaddr = NULL; + gs_unref_bytes GBytes *client_id = NULL; NMConnection *connection; connection = nm_device_get_applied_connection (self); @@ -6250,36 +7120,31 @@ dhcp4_start (NMDevice *self) s_ip4 = nm_connection_get_setting_ip4_config (connection); /* Clear old exported DHCP options */ - nm_exported_object_clear_and_unexport (&priv->dhcp4.config); + nm_dbus_object_clear_and_unexport (&priv->dhcp4.config); priv->dhcp4.config = nm_dhcp4_config_new (); - hw_addr = nm_platform_link_get_address (nm_device_get_platform (self), nm_device_get_ip_ifindex (self), &hw_addr_len); - if (hw_addr_len) { - tmp = g_byte_array_sized_new (hw_addr_len); - g_byte_array_append (tmp, hw_addr, hw_addr_len); - } + hwaddr = nm_platform_link_get_address_as_bytes (nm_device_get_platform (self), + nm_device_get_ip_ifindex (self)); + + client_id = dhcp4_get_client_id (self, connection); - /* Begin DHCP on the interface */ g_warn_if_fail (priv->dhcp4.client == NULL); priv->dhcp4.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), - tmp, + hwaddr, nm_connection_get_uuid (connection), nm_device_get_route_table (self, AF_INET, TRUE), nm_device_get_route_metric (self, AF_INET), 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)), - nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4)), + client_id, get_dhcp_timeout (self, AF_INET), priv->dhcp_anycast_address, NULL); - if (tmp) - g_byte_array_free (tmp, TRUE); - if (!priv->dhcp4.client) return NM_ACT_STAGE_RETURN_FAILURE; @@ -6352,7 +7217,7 @@ shared4_new_config (NMDevice *self, NMConnection *connection) guint32 count = 0; if (G_UNLIKELY (!shared_ips)) - shared_ips = g_hash_table_new (g_direct_hash, g_direct_equal); + shared_ips = g_hash_table_new (nm_direct_hash, NULL); else { while (g_hash_table_lookup (shared_ips, GUINT_TO_POINTER (start + count))) { count += ntohl (0x100); @@ -6537,6 +7402,7 @@ act_stage3_ip4_config_start (NMDevice *self, config = _ip4_config_new (self); nm_ip4_config_merge_setting (config, nm_connection_get_setting_ip4_config (connection), + NM_SETTING_CONNECTION_MDNS_DEFAULT, nm_device_get_route_table (self, AF_INET, TRUE), nm_device_get_route_metric (self, AF_INET)); @@ -6573,7 +7439,7 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_NONE; - g_clear_object (&priv->dhcp6.ip6_config); + applied_config_clear (&priv->dhcp6.ip6_config); g_clear_pointer (&priv->dhcp6.event_id, g_free); nm_clear_g_source (&priv->dhcp6.grace_id); @@ -6591,148 +7457,18 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) nm_device_remove_pending_action (self, NM_PENDING_ACTION_DHCP6, FALSE); if (priv->dhcp6.config) { - nm_exported_object_clear_and_unexport (&priv->dhcp6.config); + nm_dbus_object_clear_and_unexport (&priv->dhcp6.config); _notify (self, PROP_DHCP6_CONFIG); } } -static gboolean -ip6_config_merge_and_apply (NMDevice *self, - gboolean commit) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; - gboolean success; - NMIP6Config *composite; - gboolean ignore_auto_routes = FALSE; - gboolean ignore_auto_dns = FALSE; - gboolean ignore_default_routes = FALSE; - const char *token = NULL; - GSList *iter; - - if (nm_device_sys_iface_state_is_external (self)) - commit = 0; - - /* Apply ignore-auto-routes and ignore-auto-dns settings */ - connection = nm_device_get_applied_connection (self); - if (connection) { - NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection); - - if (s_ip6) { - NMSettingIP6Config *ip6 = NM_SETTING_IP6_CONFIG (s_ip6); - - ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes (s_ip6); - ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns (s_ip6); - - /* 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_ip6) - || nm_setting_ip_config_get_gateway (s_ip6); - - if (nm_setting_ip6_config_get_addr_gen_mode (ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) - token = nm_setting_ip6_config_get_token (ip6); - } - } - - composite = _ip6_config_new (self); - nm_ip6_config_set_privacy (composite, - priv->ndisc ? - priv->ndisc_use_tempaddr : - NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - init_ip6_config_dns_priority (self, composite); - - if (commit) { - if (priv->queued_ip6_config_id) - update_ext_ip_config (self, AF_INET6, FALSE, FALSE); - ensure_con_ip6_config (self); - } - - if (commit) - priv->default_route_metric_penalty_ip6_has = default_route_metric_penalty_detect (self); - - /* Merge all the IP configs into the composite config */ - if (priv->ac_ip6_config) { - nm_ip6_config_merge (composite, priv->ac_ip6_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, AF_INET6)); - } - if (priv->dhcp6.ip6_config) { - nm_ip6_config_merge (composite, priv->dhcp6.ip6_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, AF_INET6)); - } - - for (iter = priv->vpn6_configs; iter; iter = iter->next) - nm_ip6_config_merge (composite, iter->data, NM_IP_CONFIG_MERGE_DEFAULT, 0); - - if (priv->ext_ip6_config) - nm_ip6_config_merge (composite, priv->ext_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT, 0); - - /* Merge WWAN config *last* to ensure modem-given settings overwrite - * any external stuff set by pppd or other scripts. - */ - if (priv->wwan_ip6_config) { - nm_ip6_config_merge (composite, priv->wwan_ip6_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, AF_INET6)); - } - - 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 (composite, - NMP_OBJECT_CAST_IP6_ROUTE (o), - NULL); - } - } - - /* Merge user overrides into the composite config. For assumed connections, - * con_ip6_config is empty. */ - if (priv->con_ip6_config) { - nm_ip6_config_merge (composite, priv->con_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT, - default_route_metric_penalty_get (self, AF_INET6)); - } - - if (commit) { - nm_ip6_config_add_dependent_routes (composite, - nm_device_get_route_table (self, AF_INET6, TRUE), - nm_device_get_route_metric (self, AF_INET6)); - } - - /* Allow setting MTU etc */ - if (commit) { - NMUtilsIPv6IfaceId iid; - - if (token && nm_utils_ipv6_interface_identifier_get_from_token (&iid, token)) { - nm_platform_link_set_ipv6_token (nm_device_get_platform (self), - nm_device_get_ip_ifindex (self), - iid); - } - } - - success = nm_device_set_ip6_config (self, composite, commit); - g_object_unref (composite); - if (commit) - priv->v6_commit_first_time = FALSE; - return success; -} - static gboolean dhcp6_lease_change (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMSettingsConnection *settings_connection; - if (priv->dhcp6.ip6_config == NULL) { + if (!applied_config_get_current (&priv->dhcp6.ip6_config)) { _LOGW (LOGD_DHCP6, "failed to get DHCPv6 config for rebind"); return FALSE; } @@ -6743,7 +7479,7 @@ dhcp6_lease_change (NMDevice *self) g_assert (settings_connection); /* Apply the updated config */ - if (!ip6_config_merge_and_apply (self, TRUE)) { + 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; } @@ -6787,8 +7523,8 @@ dhcp6_fail (NMDevice *self, gboolean timeout) * on the interface. */ if ( priv->ip6_state == IP_DONE - && priv->con_ip6_config - && nm_ip6_config_get_num_addresses (priv->con_ip6_config)) + && priv->con_ip_config_6 + && nm_ip6_config_get_num_addresses (priv->con_ip_config_6)) goto clear_config; /* Fail the method in case of timeout or failure during initial @@ -6825,7 +7561,7 @@ dhcp6_fail (NMDevice *self, gboolean timeout) clear_config: /* The previous configuration is no longer valid */ if (priv->dhcp6.config) { - nm_exported_object_clear_and_unexport (&priv->dhcp6.config); + nm_dbus_object_clear_and_unexport (&priv->dhcp6.config); priv->dhcp6.config = nm_dhcp6_config_new (); _notify (self, PROP_DHCP6_CONFIG); } @@ -6877,16 +7613,16 @@ dhcp6_state_changed (NMDhcpClient *client, const NMPlatformIP6Address *a; nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6_config, &a) - nm_ip6_config_add_address (priv->dhcp6.ip6_config, a); + applied_config_add_address (&priv->dhcp6.ip6_config, NM_PLATFORM_IP_ADDRESS_CAST (a)); } else { - g_clear_object (&priv->dhcp6.ip6_config); g_clear_pointer (&priv->dhcp6.event_id, g_free); if (ip6_config) { - priv->dhcp6.ip6_config = g_object_ref (ip6_config); + applied_config_init (&priv->dhcp6.ip6_config, ip6_config); priv->dhcp6.event_id = g_strdup (event_id); nm_dhcp6_config_set_options (priv->dhcp6.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: @@ -6896,7 +7632,7 @@ dhcp6_state_changed (NMDhcpClient *client, _set_ip_state (self, AF_INET6, IP_CONF); if (priv->ip6_state == IP_CONF) { - if (priv->dhcp6.ip6_config == NULL) { + if (!applied_config_get_current (&priv->dhcp6.ip6_config)) { nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_DHCP_FAILED); break; } @@ -6947,34 +7683,32 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMSettingIPConfig *s_ip6; - GByteArray *tmp = NULL; - const guint8 *hw_addr; - size_t hw_addr_len = 0; + gs_unref_bytes GBytes *hwaddr = NULL; const NMPlatformIP6Address *ll_addr = NULL; g_assert (connection); s_ip6 = nm_connection_get_setting_ip6_config (connection); g_assert (s_ip6); - if (priv->ext_ip6_config_captured) - ll_addr = nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE); + if (priv->ext_ip6_config_captured) { + ll_addr = nm_ip6_config_find_first_address (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; } - hw_addr = nm_platform_link_get_address (nm_device_get_platform (self), nm_device_get_ip_ifindex (self), &hw_addr_len); - if (hw_addr_len) { - tmp = g_byte_array_sized_new (hw_addr_len); - g_byte_array_append (tmp, hw_addr, hw_addr_len); - } + hwaddr = nm_platform_link_get_address_as_bytes (nm_device_get_platform (self), + nm_device_get_ip_ifindex (self)); priv->dhcp6.client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (), nm_device_get_multi_index (self), nm_device_get_ip_iface (self), nm_device_get_ip_ifindex (self), - tmp, + hwaddr, &ll_addr->address, nm_connection_get_uuid (connection), nm_device_get_route_table (self, AF_INET6, TRUE), @@ -6986,8 +7720,6 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE, nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)), priv->dhcp6.needed_prefixes); - if (tmp) - g_byte_array_free (tmp, TRUE); if (priv->dhcp6.client) { priv->dhcp6.state_sigid = g_signal_connect (priv->dhcp6.client, @@ -7013,11 +7745,11 @@ dhcp6_start (NMDevice *self, gboolean wait_for_ll) NMConnection *connection; NMSettingIPConfig *s_ip6; - nm_exported_object_clear_and_unexport (&priv->dhcp6.config); + nm_dbus_object_clear_and_unexport (&priv->dhcp6.config); priv->dhcp6.config = nm_dhcp6_config_new (); - g_warn_if_fail (priv->dhcp6.ip6_config == NULL); - g_clear_object (&priv->dhcp6.ip6_config); + nm_assert (!applied_config_get_current (&priv->dhcp6.ip6_config)); + applied_config_clear (&priv->dhcp6.ip6_config); g_clear_pointer (&priv->dhcp6.event_id, g_free); connection = nm_device_get_applied_connection (self); @@ -7028,17 +7760,12 @@ dhcp6_start (NMDevice *self, gboolean wait_for_ll) nm_device_add_pending_action (self, NM_PENDING_ACTION_DHCP6, TRUE); if (wait_for_ll) { - NMActStageReturn ret; - /* ensure link local is ready... */ - ret = linklocal6_start (self); - if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { - /* success; wait for the LL address to show up */ + if (!linklocal6_start (self)) { + /* wait for the LL address to show up */ return TRUE; } - - /* success; already have the LL address; kick off DHCP */ - g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); + /* already have the LL address; kick off DHCP */ } if (!dhcp6_start_with_link_ready (self, connection)) @@ -7100,19 +7827,19 @@ nm_device_use_ip6_subnet (NMDevice *self, const NMPlatformIP6Address *subnet) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMPlatformIP6Address address = *subnet; - if (!priv->ac_ip6_config) - priv->ac_ip6_config = _ip6_config_new (self); + if (!applied_config_get_current (&priv->ac_ip6_config)) + applied_config_init_new (&priv->ac_ip6_config, self, AF_INET6); /* Assign a ::1 address in the subnet for us. */ address.address.s6_addr32[3] |= htonl (1); - nm_ip6_config_add_address (priv->ac_ip6_config, &address); + applied_config_add_address (&priv->ac_ip6_config, NM_PLATFORM_IP_ADDRESS_CAST (&address)); _LOGD (LOGD_IP6, "ipv6-pd: using %s address (preferred for %u seconds)", nm_utils_inet6_ntop (&address.address, NULL), subnet->preferred); /* This also updates the ndisc if there are actual changes. */ - if (!ip6_config_merge_and_apply (self, TRUE)) + if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) _LOGW (LOGD_IP6, "ipv6-pd: failed applying IP6 config for connection sharing"); } @@ -7127,11 +7854,11 @@ nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device) NMIP6Config *from_config = NULL; guint i, len; - if (priv->ac_ip6_config) { - nm_ip6_config_reset_nameservers (priv->ac_ip6_config); - nm_ip6_config_reset_searches (priv->ac_ip6_config); + 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); } else - priv->ac_ip6_config = _ip6_config_new (self); + applied_config_init_new (&priv->ac_ip6_config, self, AF_INET6); if (from_device) from_config = nm_device_get_ip6_config (from_device); @@ -7140,34 +7867,28 @@ nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device) len = nm_ip6_config_get_num_nameservers (from_config); for (i = 0; i < len; i++) { - nm_ip6_config_add_nameserver (priv->ac_ip6_config, - nm_ip6_config_get_nameserver (from_config, i)); + applied_config_add_nameserver (&priv->ac_ip6_config, + (const NMIPAddr *) nm_ip6_config_get_nameserver (from_config, i)); } len = nm_ip6_config_get_num_searches (from_config); for (i = 0; i < len; i++) { - nm_ip6_config_add_search (priv->ac_ip6_config, - nm_ip6_config_get_search (from_config, i)); + applied_config_add_search (&priv->ac_ip6_config, + nm_ip6_config_get_search (from_config, i)); } - if (!ip6_config_merge_and_apply (self, TRUE)) + if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) _LOGW (LOGD_IP6, "ipv6-pd: failed applying DNS config for connection sharing"); } /*****************************************************************************/ static void -linklocal6_cleanup (NMDevice *self) +linklocal6_failed (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); nm_clear_g_source (&priv->linklocal6_timeout_id); -} - -static void -linklocal6_failed (NMDevice *self) -{ - linklocal6_cleanup (self); nm_device_activate_schedule_ip6_config_timeout (self); } @@ -7182,17 +7903,26 @@ linklocal6_timeout_cb (gpointer user_data) } static void -linklocal6_complete (NMDevice *self) +linklocal6_check_complete (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; const char *method; - g_assert (priv->linklocal6_timeout_id); - g_assert (priv->ext_ip6_config_captured); - g_assert (nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE)); + if (!priv->linklocal6_timeout_id) { + /* we are not waiting for linklocal to complete. Nothing to do. */ + return; + } + + if ( !priv->ext_ip6_config_captured + || !nm_ip6_config_find_first_address (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; + } - linklocal6_cleanup (self); + nm_clear_g_source (&priv->linklocal6_timeout_id); connection = nm_device_get_applied_connection (self); g_assert (connection); @@ -7202,12 +7932,9 @@ linklocal6_complete (NMDevice *self) _LOGD (LOGD_DEVICE, "linklocal6: waiting for link-local addresses successful, continue with method %s", method); if ( strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 - || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) { - if (!addrconf6_start_with_link_ready (self)) { - /* Time out IPv6 instead of failing the entire activation */ - nm_device_activate_schedule_ip6_config_timeout (self); - } - } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) { + || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) + addrconf6_start_with_link_ready (self); + else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) { if (!dhcp6_start_with_link_ready (self, connection)) { /* Time out IPv6 instead of failing the entire activation */ nm_device_activate_schedule_ip6_config_timeout (self); @@ -7222,28 +7949,27 @@ static void check_and_add_ipv6ll_addr (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ip_ifindex = nm_device_get_ip_ifindex (self); struct in6_addr lladdr; NMConnection *connection; NMSettingIP6Config *s_ip6 = NULL; GError *error = NULL; + const char *addr_type; - if (priv->nm_ipv6ll == FALSE) + if (!priv->ipv6ll_handle) return; - if (priv->ext_ip6_config_captured) { - NMDedupMultiIter ipconf_iter; - const NMPlatformIP6Address *addr; - - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, priv->ext_ip6_config_captured, &addr) { - if ( IN6_IS_ADDR_LINKLOCAL (&addr->address) - && !(addr->n_ifa_flags & IFA_F_DADFAILED)) { - /* Already have an LL address, nothing to do */ - return; - } - } + if ( priv->ext_ip6_config_captured + && nm_ip6_config_find_first_address (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; } + priv->ipv6ll_has = FALSE; + memset (&priv->ipv6ll_addr, 0, sizeof (priv->ipv6ll_addr)); + memset (&lladdr, 0, sizeof (lladdr)); lladdr.s6_addr16[0] = htons (0xfe80); @@ -7268,7 +7994,7 @@ check_and_add_ipv6ll_addr (NMDevice *self) linklocal6_failed (self); return; } - _LOGD (LOGD_IP6, "linklocal6: using IPv6 stable-privacy addressing"); + addr_type = "stable-privacy"; } else { NMUtilsIPv6IfaceId iid; @@ -7284,37 +8010,30 @@ check_and_add_ipv6ll_addr (NMDevice *self) _LOGW (LOGD_IP6, "linklocal6: failed to get interface identifier; IPv6 cannot continue"); return; } - _LOGD (LOGD_IP6, "linklocal6: using EUI-64 identifier to generate IPv6LL address"); - nm_utils_ipv6_addr_set_interface_identifier (&lladdr, iid); + addr_type = "EUI-64"; } - _LOGD (LOGD_IP6, "linklocal6: adding IPv6LL address %s", nm_utils_inet6_ntop (&lladdr, NULL)); - if (!nm_platform_ip6_address_add (nm_device_get_platform (self), - ip_ifindex, - lladdr, - 64, - in6addr_any, - NM_PLATFORM_LIFETIME_PERMANENT, - NM_PLATFORM_LIFETIME_PERMANENT, - 0)) { - _LOGW (LOGD_IP6, "failed to add IPv6 link-local address %s", - nm_utils_inet6_ntop (&lladdr, NULL)); - } + _LOGD (LOGD_IP6, "linklocal6: generated %s IPv6LL address %s", addr_type, nm_utils_inet6_ntop (&lladdr, NULL)); + priv->ipv6ll_has = TRUE; + priv->ipv6ll_addr = lladdr; + ip_config_merge_and_apply (self, AF_INET6, TRUE); } -static NMActStageReturn +static gboolean linklocal6_start (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; const char *method; - linklocal6_cleanup (self); + nm_clear_g_source (&priv->linklocal6_timeout_id); if ( priv->ext_ip6_config_captured - && nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE)) - return NM_ACT_STAGE_RETURN_SUCCESS; + && nm_ip6_config_find_first_address (priv->ext_ip6_config_captured, + NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL + | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) + return TRUE; connection = nm_device_get_applied_connection (self); g_assert (connection); @@ -7330,8 +8049,7 @@ linklocal6_start (NMDevice *self) * (rh #1101809) */ priv->linklocal6_timeout_id = g_timeout_add_seconds (15, linklocal6_timeout_cb, self); - - return NM_ACT_STAGE_RETURN_POSTPONE; + return FALSE; } /*****************************************************************************/ @@ -7396,7 +8114,7 @@ _set_mtu (NMDevice *self, guint32 mtu) if (priv->master) { /* changing the MTU of a slave, might require the master to reset - * it's MTU. Note that the master usually cannot set a MTU larger + * its MTU. Note that the master usually cannot set a MTU larger * then the slave's. Hence, when the slave increases the MTU, * master might want to retry setting the MTU. */ nm_device_commit_mtu (priv->master); @@ -7573,7 +8291,7 @@ nm_device_commit_mtu (NMDevice *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)->ip4_config); + _commit_mtu (self, NM_DEVICE_GET_PRIVATE (self)->ip_config_4); } else _LOGT (LOGD_DEVICE, "mtu: commit-mtu... skip due to state %s", nm_device_state_to_str (state)); } @@ -7585,10 +8303,10 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); guint i; - g_return_if_fail (priv->act_request); + g_return_if_fail (priv->act_request.obj); - if (!priv->ac_ip6_config) - priv->ac_ip6_config = _ip6_config_new (self); + if (!applied_config_get_current (&priv->ac_ip6_config)) + applied_config_init_new (&priv->ac_ip6_config, self, AF_INET6); if (changed & NM_NDISC_CONFIG_ADDRESSES) { guint8 plen; @@ -7609,16 +8327,23 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in } else plen = 128; - nm_ip6_config_reset_addresses_ndisc (priv->ac_ip6_config, + nm_ip6_config_reset_addresses_ndisc ((NMIP6Config *) priv->ac_ip6_config.orig, rdata->addresses, rdata->addresses_n, plen, 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, + plen, + ifa_flags); + } } if (NM_FLAGS_ANY (changed, NM_NDISC_CONFIG_ROUTES | NM_NDISC_CONFIG_GATEWAYS)) { - nm_ip6_config_reset_routes_ndisc (priv->ac_ip6_config, + nm_ip6_config_reset_routes_ndisc ((NMIP6Config *) priv->ac_ip6_config.orig, rdata->gateways, rdata->gateways_n, rdata->routes, @@ -7627,22 +8352,34 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in nm_device_get_route_metric (self, AF_INET6), nm_platform_check_kernel_support (nm_device_get_platform (self), NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF)); + 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, TRUE), + nm_device_get_route_metric (self, AF_INET6), + nm_platform_check_kernel_support (nm_device_get_platform (self), + NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF)); + } + } if (changed & NM_NDISC_CONFIG_DNS_SERVERS) { /* Rebuild DNS server list from neighbor discovery cache. */ - nm_ip6_config_reset_nameservers (priv->ac_ip6_config); + applied_config_reset_nameservers (&priv->ac_ip6_config); for (i = 0; i < rdata->dns_servers_n; i++) - nm_ip6_config_add_nameserver (priv->ac_ip6_config, &rdata->dns_servers[i].address); + 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. */ - nm_ip6_config_reset_searches (priv->ac_ip6_config); + applied_config_reset_searches (&priv->ac_ip6_config); for (i = 0; i < rdata->dns_domains_n; i++) - nm_ip6_config_add_search (priv->ac_ip6_config, rdata->dns_domains[i].domain); + applied_config_add_search (&priv->ac_ip6_config, rdata->dns_domains[i].domain); } if (changed & NM_NDISC_CONFIG_DHCP_LEVEL) { @@ -7692,16 +8429,21 @@ ndisc_ra_timeout (NMNDisc *ndisc, NMDevice *self) * 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->ip6_config - && nm_ip6_config_get_address_first_nontentative (priv->ip6_config, FALSE)) + if ( priv->ip_config_6 + && nm_ip6_config_find_first_address (priv->ip_config_6, + NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL + | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)) nm_device_activate_schedule_ip6_config_result (self); else nm_device_activate_schedule_ip6_config_timeout (self); } } -static gboolean +static void addrconf6_start_with_link_ready (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -7719,12 +8461,10 @@ addrconf6_start_with_link_ready (NMDevice *self) } /* Apply any manual configuration before starting RA */ - if (!ip6_config_merge_and_apply (self, TRUE)) { + if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) _LOGW (LOGD_IP6, "failed to apply manual IPv6 configuration"); - g_clear_object (&priv->con_ip6_config); - } - /* XXX: These sysctls would probably be better set by the lndp ndisc itself. */ + /* FIXME: These sysctls would probably be better set by the lndp ndisc itself. */ switch (nm_ndisc_get_node_type (priv->ndisc)) { case NM_NDISC_NODE_TYPE_HOST: /* Accepting prefixes from discovered routers. */ @@ -7755,7 +8495,7 @@ addrconf6_start_with_link_ready (NMDevice *self) ndisc_set_router_config (priv->ndisc, self); nm_ndisc_start (priv->ndisc); - return TRUE; + return; } static NMNDiscNodeType @@ -7778,7 +8518,6 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; - NMActStageReturn ret; NMSettingIP6Config *s_ip6 = NULL; GError *error = NULL; NMUtilsStableType stable_type; @@ -7787,11 +8526,8 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) connection = nm_device_get_applied_connection (self); g_assert (connection); - g_warn_if_fail (priv->ac_ip6_config == NULL); - if (priv->ac_ip6_config) { - g_object_unref (priv->ac_ip6_config); - priv->ac_ip6_config = NULL; - } + nm_assert (!applied_config_get_current (&priv->ac_ip6_config)); + applied_config_clear (&priv->ac_ip6_config); g_clear_pointer (&priv->rt6_temporary_not_available, g_hash_table_unref); nm_clear_g_source (&priv->rt6_temporary_not_available_id); @@ -7828,15 +8564,14 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) nm_device_add_pending_action (self, NM_PENDING_ACTION_AUTOCONF6, TRUE); /* ensure link local is ready... */ - ret = linklocal6_start (self); - if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { - /* success; wait for the LL address to show up */ + if (!linklocal6_start (self)) { + /* wait for the LL address to show up */ return TRUE; } - /* success; already have the LL address; kick off neighbor discovery */ - g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); - return addrconf6_start_with_link_ready (self); + /* already have the LL address; kick off neighbor discovery */ + addrconf6_start_with_link_ready (self); + return TRUE; } static void @@ -7849,7 +8584,7 @@ addrconf6_cleanup (NMDevice *self) nm_device_remove_pending_action (self, NM_PENDING_ACTION_AUTOCONF6, FALSE); - g_clear_object (&priv->ac_ip6_config); + applied_config_clear (&priv->ac_ip6_config); g_clear_pointer (&priv->rt6_temporary_not_available, g_hash_table_unref); nm_clear_g_source (&priv->rt6_temporary_not_available_id); g_clear_object (&priv->ndisc); @@ -7903,7 +8638,8 @@ restore_ip6_properties (NMDevice *self) 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->nm_ipv6ll && strcmp (key, "disable_ipv6") == 0) + if ( priv->ipv6ll_handle + && nm_streq (key, "disable_ipv6")) continue; nm_device_ipv6_sysctl_set (self, key, value); } @@ -7913,7 +8649,7 @@ static inline 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)->nm_ipv6ll == FALSE) + if (!NM_DEVICE_GET_PRIVATE (self)->ipv6ll_handle) nm_device_ipv6_sysctl_set (self, "disable_ipv6", value); } @@ -7928,7 +8664,7 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) return; - priv->nm_ipv6ll = enable; + priv->ipv6ll_handle = enable; if (ifindex > 0) { NMPlatformError plerr; const char *detail = enable ? "enable" : "disable"; @@ -8077,14 +8813,14 @@ act_stage3_ip6_config_start (NMDevice *self, if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) { if ( !priv->master && !nm_device_sys_iface_state_is_external (self)) { - gboolean old_nm_ipv6ll = priv->nm_ipv6ll; + 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 (old_nm_ipv6ll == TRUE) + if (ipv6ll_handle_old) nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); restore_ip6_properties (self); } @@ -8095,7 +8831,7 @@ act_stage3_ip6_config_start (NMDevice *self, * 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->ip4_config); + _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 @@ -8116,7 +8852,6 @@ act_stage3_ip6_config_start (NMDevice *self, 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), - FALSE, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); ip6_privacy = _ip6_privacy_get (self); @@ -8129,7 +8864,9 @@ act_stage3_ip6_config_start (NMDevice *self, } else ret = NM_ACT_STAGE_RETURN_POSTPONE; } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) { - ret = linklocal6_start (self); + ret = linklocal6_start (self) + ? NM_ACT_STAGE_RETURN_SUCCESS + : NM_ACT_STAGE_RETURN_POSTPONE; } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) { priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_MANAGED; if (!dhcp6_start (self, TRUE)) { @@ -8239,8 +8976,8 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) /* Here we get a static IPv6 config, like for Shared where it's * autogenerated or from modems where it comes from ModemManager. */ - g_warn_if_fail (priv->ac_ip6_config == NULL); - priv->ac_ip6_config = ip6_config; + nm_assert (!applied_config_get_current (&priv->ac_ip6_config)); + applied_config_init (&priv->ac_ip6_config, ip6_config); nm_device_activate_schedule_ip6_config_result (self); } else if (ret == NM_ACT_STAGE_RETURN_IP_DONE) { _set_ip_state (self, AF_INET6, IP_DONE); @@ -8378,7 +9115,7 @@ nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self) g_return_if_fail (NM_IS_DEVICE (self)); priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->act_request); + g_return_if_fail (priv->act_request.obj); /* Add the interface to the specified firewall zone */ if (priv->fw_state == FIREWALL_STATE_UNMANAGED) { @@ -8450,7 +9187,7 @@ nm_device_activate_schedule_ip4_config_timeout (NMDevice *self) g_return_if_fail (NM_IS_DEVICE (self)); priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->act_request); + g_return_if_fail (priv->act_request.obj); activation_source_schedule (self, activate_stage4_ip4_config_timeout, AF_INET); } @@ -8506,7 +9243,7 @@ nm_device_activate_schedule_ip6_config_timeout (NMDevice *self) g_return_if_fail (NM_IS_DEVICE (self)); priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->act_request); + g_return_if_fail (priv->act_request.obj); activation_source_schedule (self, activate_stage4_ip6_config_timeout, AF_INET6); } @@ -8624,14 +9361,14 @@ arp_cleanup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->arping.announcing) { - nm_arping_manager_destroy (priv->arping.announcing); - priv->arping.announcing = NULL; + if (priv->acd.announcing) { + nm_acd_manager_destroy (priv->acd.announcing); + priv->acd.announcing = NULL; } } -static void -arp_announce (NMDevice *self) +void +nm_device_arp_announce (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; @@ -8646,7 +9383,7 @@ arp_announce (NMDevice *self) nm_device_get_ip_ifindex (self), &hw_addr_len); - if (!hw_addr_len || !hw_addr) + if (!hw_addr || hw_addr_len != ETH_ALEN) return; /* We only care about manually-configured addresses; DHCP- and autoip-configured @@ -8662,19 +9399,19 @@ arp_announce (NMDevice *self) if (num == 0) return; - priv->arping.announcing = nm_arping_manager_new (nm_device_get_ip_ifindex (self)); + priv->acd.announcing = nm_acd_manager_new (nm_device_get_ip_ifindex (self), hw_addr, hw_addr_len); 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_arping_manager_add_address (priv->arping.announcing, addr); + nm_acd_manager_add_address (priv->acd.announcing, addr); else g_warn_if_reached (); } - nm_arping_manager_announce_addresses (priv->arping.announcing); + nm_acd_manager_announce_addresses (priv->acd.announcing); } static void @@ -8699,8 +9436,7 @@ activate_stage5_ip4_config_result (NMDevice *self) _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); } - /* NULL to use the existing priv->dev_ip4_config */ - if (!ip4_config_merge_and_apply (self, TRUE)) { + if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) { _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Commit) failed"); nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_CONFIG_FAILED); return; @@ -8712,7 +9448,7 @@ activate_stage5_ip4_config_result (NMDevice *self) if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) { gs_free_error GError *error = NULL; - if (!start_sharing (self, priv->ip4_config, &error)) { + 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; @@ -8731,8 +9467,7 @@ activate_stage5_ip4_config_result (NMDevice *self) NULL, NULL, NULL); } - arp_announce (self); - + nm_device_arp_announce (self); nm_device_remove_pending_action (self, NM_PENDING_ACTION_DHCP4, FALSE); /* Enter the IP_CHECK state if this is the first method to complete */ @@ -8748,10 +9483,7 @@ nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *conf g_return_if_fail (NM_IS_DEVICE (self)); priv = NM_DEVICE_GET_PRIVATE (self); - g_clear_object (&priv->dev_ip4_config); - if (config) - priv->dev_ip4_config = g_object_ref (config); - + applied_config_init (&priv->dev_ip4_config, config); activation_source_schedule (self, activate_stage5_ip4_config_result, AF_INET); } @@ -8776,6 +9508,32 @@ nm_device_activate_ip4_state_done (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_DONE; } +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 = _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. @@ -8784,42 +9542,43 @@ static NMIP6Config * dad6_get_pending_addresses (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMIP6Config *confs[] = { priv->ac_ip6_config, - priv->dhcp6.ip6_config, - priv->con_ip6_config, - priv->wwan_ip6_config }; - const NMPlatformIP6Address *addr, *pl_addr; + 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->wwan_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]) { - - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, confs[i], &addr) { - pl_addr = nm_platform_ip6_address_get (nm_device_get_platform (self), - ifindex, - addr->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 = _ip6_config_new (self); - - nm_ip6_config_add_address (dad6_config, pl_addr); - } - } + 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); } } @@ -8851,10 +9610,10 @@ activate_stage5_ip6_config_commit (NMDevice *self) _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); } - if (ip6_config_merge_and_apply (self, TRUE)) { + if (ip_config_merge_and_apply (self, AF_INET6, TRUE)) { if ( priv->dhcp6.mode != NM_NDISC_DHCP_LEVEL_NONE && priv->ip6_state == IP_CONF) { - if (priv->dhcp6.ip6_config) { + 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. */ @@ -8940,44 +9699,27 @@ nm_device_activate_ip6_state_done (NMDevice *self) /*****************************************************************************/ -static void -act_request_set_cb (NMActRequest *act_request, - GParamSpec *pspec, - NMDevice *self) -{ - _notify (self, PROP_ACTIVE_CONNECTION); -} - static void act_request_set (NMDevice *self, NMActRequest *act_request) { NMDevicePrivate *priv; - gs_unref_object NMActRequest *old_act_requst = NULL; nm_assert (NM_IS_DEVICE (self)); nm_assert (!act_request || NM_IS_ACT_REQUEST (act_request)); priv = NM_DEVICE_GET_PRIVATE (self); - if ( !priv->act_request_public - && priv->act_request == act_request) + if ( !priv->act_request.visible + && priv->act_request.obj == act_request) return; /* always clear the public flag. The few callers that set a new @act_request * don't want that the property is public yet. */ - priv->act_request_public = FALSE; - - nm_clear_g_signal_handler (priv->act_request, &priv->act_request_id); - - old_act_requst = priv->act_request; - priv->act_request = nm_g_object_ref (act_request); + nm_dbus_track_obj_path_set (&priv->act_request, + act_request, + FALSE); if (act_request) { - priv->act_request_id = g_signal_connect (act_request, - "notify::"NM_EXPORTED_OBJECT_PATH, - G_CALLBACK (act_request_set_cb), - self); - switch (nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (act_request))) { case NM_ACTIVATION_TYPE_EXTERNAL: break; @@ -8994,8 +9736,6 @@ act_request_set (NMDevice *self, NMActRequest *act_request) break; } } - - _notify (self, PROP_ACTIVE_CONNECTION); } static void @@ -9021,9 +9761,9 @@ _update_ip4_address (NMDevice *self) g_return_if_fail (NM_IS_DEVICE (self)); - if ( priv->ip4_config + if ( priv->ip_config_4 && ip_config_valid (priv->state) - && (address = nm_ip4_config_get_first_address (priv->ip4_config))) { + && (address = nm_ip4_config_get_first_address (priv->ip_config_4))) { if (address->address != priv->ip4_address) { priv->ip4_address = address->address; _notify (self, PROP_IP4_ADDRESS); @@ -9117,37 +9857,31 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex) } static void -_cleanup_ip4_pre (NMDevice *self, CleanupType cleanup_type) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - _set_ip_state (self, AF_INET, IP_NONE); - - if (nm_clear_g_source (&priv->queued_ip4_config_id)) - _LOGD (LOGD_DEVICE, "clearing queued IP4 config change"); - priv->queued_ip4_config_pending = FALSE; - - dhcp4_cleanup (self, cleanup_type, FALSE); - arp_cleanup (self); - dnsmasq_cleanup (self); - ipv4ll_cleanup (self); -} - -static void -_cleanup_ip6_pre (NMDevice *self, CleanupType cleanup_type) +_cleanup_ip_pre (NMDevice *self, int addr_family, CleanupType cleanup_type) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const gboolean IS_IPv4 = (addr_family == AF_INET); - _set_ip_state (self, AF_INET6, IP_NONE); + _set_ip_state (self, addr_family, IP_NONE); - if (nm_clear_g_source (&priv->queued_ip6_config_id)) - _LOGD (LOGD_DEVICE, "clearing queued IP6 config change"); - priv->queued_ip6_config_pending = FALSE; + 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)); + } - g_clear_object (&priv->dad6_ip6_config); - dhcp6_cleanup (self, cleanup_type, FALSE); - linklocal6_cleanup (self); - addrconf6_cleanup (self); + if (IS_IPv4) { + priv->queued_ip4_config_pending = FALSE; + dhcp4_cleanup (self, cleanup_type, FALSE); + arp_cleanup (self); + dnsmasq_cleanup (self); + ipv4ll_cleanup (self); + } else { + priv->queued_ip6_config_pending = FALSE; + g_clear_object (&priv->dad6_ip6_config); + dhcp6_cleanup (self, cleanup_type, FALSE); + nm_clear_g_source (&priv->linklocal6_timeout_id); + addrconf6_cleanup (self); + } } gboolean @@ -9166,7 +9900,7 @@ _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, gs_unref_hashtable GHashTable *check_dups = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL); for (i = 0; argv[i]; i++) { - if (!nm_g_hash_table_add (check_dups, (char *) argv[i])) + if (!g_hash_table_add (check_dups, (char *) argv[i])) nm_assert (FALSE); } nm_assert (g_hash_table_size (check_dups) > 0); @@ -9217,8 +9951,7 @@ _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, void nm_device_reactivate_ip4_config (NMDevice *self, NMSettingIPConfig *s_ip4_old, - NMSettingIPConfig *s_ip4_new, - gboolean force_restart) + NMSettingIPConfig *s_ip4_new) { NMDevicePrivate *priv; const char *method_old, *method_new; @@ -9227,31 +9960,31 @@ nm_device_reactivate_ip4_config (NMDevice *self, priv = NM_DEVICE_GET_PRIVATE (self); if (priv->ip4_state != IP_NONE) { - g_clear_object (&priv->con_ip4_config); - g_clear_object (&priv->ext_ip4_config); - priv->con_ip4_config = _ip4_config_new (self); - nm_ip4_config_merge_setting (priv->con_ip4_config, + g_clear_object (&priv->con_ip_config_4); + g_clear_object (&priv->ext_ip_config_4); + g_clear_object (&priv->dev_ip4_config.current); + g_clear_object (&priv->wwan_ip_config_4.current); + priv->con_ip_config_4 = _ip4_config_new (self); + nm_ip4_config_merge_setting (priv->con_ip_config_4, s_ip4_new, + _get_mdns (self), nm_device_get_route_table (self, AF_INET, TRUE), nm_device_get_route_metric (self, AF_INET)); - if (!force_restart) { - method_old = s_ip4_old - ? nm_setting_ip_config_get_method (s_ip4_old) - : NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - method_new = s_ip4_new - ? nm_setting_ip_config_get_method (s_ip4_new) - : NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - force_restart = !nm_streq0 (method_old, method_new); - } + method_old = s_ip4_old + ? nm_setting_ip_config_get_method (s_ip4_old) + : NM_SETTING_IP4_CONFIG_METHOD_DISABLED; + method_new = s_ip4_new + ? nm_setting_ip_config_get_method (s_ip4_new) + : NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - if (force_restart) { - _cleanup_ip4_pre (self, CLEANUP_TYPE_DECONFIGURE); + if (!nm_streq0 (method_old, method_new)) { + _cleanup_ip_pre (self, AF_INET, CLEANUP_TYPE_DECONFIGURE); _set_ip_state (self, AF_INET, IP_WAIT); if (!nm_device_activate_stage3_ip4_start (self)) _LOGW (LOGD_IP4, "Failed to apply IPv4 configuration"); } else { - if (!ip4_config_merge_and_apply (self, TRUE)) + if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) _LOGW (LOGD_IP4, "Failed to reapply IPv4 configuration"); } } @@ -9260,8 +9993,7 @@ nm_device_reactivate_ip4_config (NMDevice *self, void nm_device_reactivate_ip6_config (NMDevice *self, NMSettingIPConfig *s_ip6_old, - NMSettingIPConfig *s_ip6_new, - gboolean force_restart) + NMSettingIPConfig *s_ip6_new) { NMDevicePrivate *priv; const char *method_old, *method_new; @@ -9270,31 +10002,34 @@ nm_device_reactivate_ip6_config (NMDevice *self, priv = NM_DEVICE_GET_PRIVATE (self); if (priv->ip6_state != IP_NONE) { - g_clear_object (&priv->con_ip6_config); - g_clear_object (&priv->ext_ip6_config); - priv->con_ip6_config = _ip6_config_new (self); - nm_ip6_config_merge_setting (priv->con_ip6_config, + g_clear_object (&priv->con_ip_config_6); + g_clear_object (&priv->ext_ip_config_6); + g_clear_object (&priv->ac_ip6_config.current); + g_clear_object (&priv->dhcp6.ip6_config.current); + g_clear_object (&priv->wwan_ip_config_6.current); + if ( priv->ipv6ll_handle + && !IN6_IS_ADDR_UNSPECIFIED (&priv->ipv6ll_addr)) + priv->ipv6ll_has = TRUE; + priv->con_ip_config_6 = _ip6_config_new (self); + nm_ip6_config_merge_setting (priv->con_ip_config_6, s_ip6_new, nm_device_get_route_table (self, AF_INET6, TRUE), nm_device_get_route_metric (self, AF_INET6)); - if (!force_restart) { - method_old = s_ip6_old - ? nm_setting_ip_config_get_method (s_ip6_old) - : NM_SETTING_IP6_CONFIG_METHOD_IGNORE; - method_new = s_ip6_new - ? nm_setting_ip_config_get_method (s_ip6_new) - : NM_SETTING_IP6_CONFIG_METHOD_IGNORE; - force_restart = !nm_streq0 (method_old, method_new); - } + method_old = s_ip6_old + ? nm_setting_ip_config_get_method (s_ip6_old) + : NM_SETTING_IP6_CONFIG_METHOD_IGNORE; + method_new = s_ip6_new + ? nm_setting_ip_config_get_method (s_ip6_new) + : NM_SETTING_IP6_CONFIG_METHOD_IGNORE; - if (force_restart) { - _cleanup_ip6_pre (self, CLEANUP_TYPE_DECONFIGURE); + if (!nm_streq0 (method_old, method_new)) { + _cleanup_ip_pre (self, AF_INET6, CLEANUP_TYPE_DECONFIGURE); _set_ip_state (self, AF_INET6, IP_WAIT); if (!nm_device_activate_stage3_ip6_start (self)) _LOGW (LOGD_IP6, "Failed to apply IPv6 configuration"); } else { - if (!ip6_config_merge_and_apply (self, TRUE)) + if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) _LOGW (LOGD_IP4, "Failed to reapply IPv6 configuration"); } } @@ -9339,7 +10074,7 @@ can_reapply_change (NMDevice *self, const char *setting_name, * allowed to differ. * * This includes UUID, there is no principal problem with reapplying a - * connection and changing it's UUID. In fact, disallowing it makes it + * connection and changing its UUID. In fact, disallowing it makes it * cumbersome for the user to reapply any connection but the original * settings-connection. */ return nm_device_hash_check_invalid_keys (diffs, @@ -9465,7 +10200,7 @@ check_and_reapply_connection (NMDevice *self, } if ( version_id != 0 - && version_id != nm_active_connection_version_id_get ((NMActiveConnection *) priv->act_request)) { + && 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, @@ -9478,10 +10213,10 @@ check_and_reapply_connection (NMDevice *self, *************************************************************************/ if (diffs) - nm_active_connection_version_id_bump ((NMActiveConnection *) priv->act_request); + nm_active_connection_version_id_bump ((NMActiveConnection *) priv->act_request.obj); _LOGD (LOGD_DEVICE, "reapply (version-id %llu%s)", - (unsigned long long) nm_active_connection_version_id_get (((NMActiveConnection *) priv->act_request)), + (unsigned long long) nm_active_connection_version_id_get (((NMActiveConnection *) priv->act_request.obj)), diffs ? "" : " (unmodified)"); if (diffs) { @@ -9537,8 +10272,8 @@ check_and_reapply_connection (NMDevice *self, s_ip6_old = nm_connection_get_setting_ip6_config (con_old); s_ip6_new = nm_connection_get_setting_ip6_config (con_new); - nm_device_reactivate_ip4_config (self, s_ip4_old, s_ip4_new, TRUE); - nm_device_reactivate_ip6_config (self, s_ip6_old, s_ip6_new, TRUE); + nm_device_reactivate_ip4_config (self, s_ip4_old, s_ip4_new); + nm_device_reactivate_ip6_config (self, s_ip6_old, s_ip6_new); reactivate_proxy_config (self); @@ -9607,25 +10342,33 @@ reapply_cb (NMDevice *self, } static void -impl_device_reapply (NMDevice *self, - GDBusMethodInvocation *context, - GVariant *settings, - guint64 version_id, - guint32 flags) +impl_device_reapply (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *dbus_connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { + NMDevice *self = NM_DEVICE (obj); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMSettingsConnection *settings_connection; NMConnection *connection = NULL; GError *error = NULL; ReapplyData *reapply_data; + gs_unref_variant GVariant *settings = NULL; + guint64 version_id; + guint32 flags; + + g_variant_get (parameters, "(@a{sa{sv}}tu)", &settings, &version_id, &flags); /* No flags supported as of now. */ if (flags != 0) { error = g_error_new_literal (NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "Invalid flags specified"); - nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, context, error->message); - g_dbus_method_invocation_take_error (context, error); + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, invocation, error->message); + g_dbus_method_invocation_take_error (invocation, error); return; } @@ -9633,8 +10376,8 @@ impl_device_reapply (NMDevice *self, error = g_error_new_literal (NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ACTIVE, "Device is not activated"); - nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, context, error->message); - g_dbus_method_invocation_take_error (context, error); + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, invocation, error->message); + g_dbus_method_invocation_take_error (invocation, error); return; } @@ -9649,8 +10392,8 @@ impl_device_reapply (NMDevice *self, &error); if (!connection) { g_prefix_error (&error, "The settings specified are invalid: "); - nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, context, error->message); - g_dbus_method_invocation_take_error (context, error); + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, invocation, error->message); + g_dbus_method_invocation_take_error (invocation, error); return; } nm_connection_clear_secrets (connection); @@ -9663,9 +10406,8 @@ impl_device_reapply (NMDevice *self, } else reapply_data = NULL; - /* Ask the manager to authenticate this request for us */ g_signal_emit (self, signals[AUTH_REQUEST], 0, - context, + invocation, nm_device_get_applied_connection (self), NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE, @@ -9724,40 +10466,44 @@ get_applied_connection_cb (NMDevice *self, g_dbus_method_invocation_return_value (context, g_variant_new ("(@a{sa{sv}}t)", settings, - nm_active_connection_version_id_get ((NMActiveConnection *) priv->act_request))); + nm_active_connection_version_id_get ((NMActiveConnection *) priv->act_request.obj))); } static void -impl_device_get_applied_connection (NMDevice *self, - GDBusMethodInvocation *context, - guint32 flags) +impl_device_get_applied_connection (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { + NMDevice *self = NM_DEVICE (obj); NMConnection *applied_connection; - GError *error = NULL; + guint32 flags; - g_return_if_fail (NM_IS_DEVICE (self)); + g_variant_get (parameters, "(u)", &flags); /* No flags supported as of now. */ if (flags != 0) { - error = g_error_new_literal (NM_DEVICE_ERROR, - NM_DEVICE_ERROR_FAILED, - "Invalid flags specified"); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_return_error_literal (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + "Invalid flags specified"); return; } applied_connection = nm_device_get_applied_connection (self); if (!applied_connection) { - error = g_error_new_literal (NM_DEVICE_ERROR, - NM_DEVICE_ERROR_NOT_ACTIVE, - "Device is not activated"); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_return_error_literal (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ACTIVE, + "Device is not activated"); return; } - /* Ask the manager to authenticate this request for us */ g_signal_emit (self, signals[AUTH_REQUEST], 0, - context, + invocation, applied_connection, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE, @@ -9906,25 +10652,31 @@ _clear_queued_act_request (NMDevicePrivate *priv) } static void -impl_device_disconnect (NMDevice *self, GDBusMethodInvocation *context) +impl_device_disconnect (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *dbus_connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { + NMDevice *self = NM_DEVICE (obj); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; - GError *error = NULL; - if (NM_DEVICE_GET_PRIVATE (self)->act_request == NULL) { - error = g_error_new_literal (NM_DEVICE_ERROR, - NM_DEVICE_ERROR_NOT_ACTIVE, - "This device is not active"); - g_dbus_method_invocation_take_error (context, error); + if (!priv->act_request.obj) { + g_dbus_method_invocation_return_error_literal (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ACTIVE, + "This device is not active"); return; } connection = nm_device_get_applied_connection (self); - g_assert (connection); + nm_assert (connection); - /* Ask the manager to authenticate this request for us */ g_signal_emit (self, signals[AUTH_REQUEST], 0, - context, + invocation, connection, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE, @@ -9956,21 +10708,27 @@ delete_cb (NMDevice *self, } static void -impl_device_delete (NMDevice *self, GDBusMethodInvocation *context) +impl_device_delete (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { - GError *error = NULL; + NMDevice *self = NM_DEVICE (obj); - if (!nm_device_is_software (self) || !nm_device_is_real (self)) { - error = g_error_new_literal (NM_DEVICE_ERROR, - NM_DEVICE_ERROR_NOT_SOFTWARE, - "This device is not a software device or is not realized"); - g_dbus_method_invocation_take_error (context, error); + if ( !nm_device_is_software (self) + || !nm_device_is_real (self)) { + g_dbus_method_invocation_return_error_literal (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_SOFTWARE, + "This device is not a software device or is not realized"); return; } - /* Ask the manager to authenticate this request for us */ g_signal_emit (self, signals[AUTH_REQUEST], 0, - context, + invocation, NULL, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE, @@ -10083,23 +10841,37 @@ _carrier_wait_check_act_request_must_queue (NMDevice *self, NMActRequest *req) } void -nm_device_steal_connection (NMDevice *self, NMSettingsConnection *connection) +nm_device_disconnect_active_connection (NMActiveConnection *active) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevice *self; + NMDevicePrivate *priv; - _LOGI (LOGD_DEVICE, "disconnecting connection '%s' for new activation request", - nm_settings_connection_get_id (connection)); + g_return_if_fail (NM_IS_ACTIVE_CONNECTION (active)); - if ( priv->queued_act_request - && connection == nm_active_connection_get_settings_connection (NM_ACTIVE_CONNECTION (priv->queued_act_request))) - _clear_queued_act_request (priv); + self = nm_active_connection_get_device (active); - if ( priv->act_request - && connection == nm_active_connection_get_settings_connection (NM_ACTIVE_CONNECTION (priv->act_request)) - && priv->state < NM_DEVICE_STATE_DEACTIVATING) { - nm_device_state_changed (self, - NM_DEVICE_STATE_DEACTIVATING, - NM_DEVICE_STATE_REASON_NEW_ACTIVATION); + if (!self) { + /* hm, no device? Just fail the active connection. */ + nm_active_connection_set_state_fail (active, + NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN, + NULL); + return; + } + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (NM_ACTIVE_CONNECTION (priv->queued_act_request) == active) { + _clear_queued_act_request (priv); + return; + } + if (NM_ACTIVE_CONNECTION (priv->act_request.obj) == active) { + if (priv->state < NM_DEVICE_STATE_DEACTIVATING) { + nm_device_state_changed (self, + NM_DEVICE_STATE_DEACTIVATING, + NM_DEVICE_STATE_REASON_NEW_ACTIVATION); + } else { + /* it's going down already... */ + } } } @@ -10111,7 +10883,7 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req) must_queue = _carrier_wait_check_act_request_must_queue (self, req); - if ( !priv->act_request + if ( !priv->act_request.obj && !must_queue && nm_device_is_real (self)) { _device_activate (self, req); @@ -10126,7 +10898,7 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req) _LOGD (LOGD_DEVICE, "queue activation request waiting for %s", must_queue ? "carrier" : "currently active connection to disconnect"); /* Deactivate existing activation request first */ - if (priv->act_request) { + if (priv->act_request.obj) { _LOGI (LOGD_DEVICE, "disconnecting for new activation request."); nm_device_state_changed (self, NM_DEVICE_STATE_DEACTIVATING, @@ -10211,116 +10983,154 @@ nm_device_get_ip4_config (NMDevice *self) { g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - return NM_DEVICE_GET_PRIVATE (self)->ip4_config; + return NM_DEVICE_GET_PRIVATE (self)->ip_config_4; } - static gboolean -nm_device_set_ip4_config (NMDevice *self, - NMIP4Config *new_config, - gboolean commit, - GPtrArray *ip4_dev_route_blacklist) +nm_device_set_ip_config (NMDevice *self, + int addr_family, + NMIPConfig *new_config, + gboolean commit, + GPtrArray *ip4_dev_route_blacklist) { - NMDevicePrivate *priv; - NMIP4Config *old_config = NULL; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const gboolean IS_IPv4 = (addr_family == AF_INET); + NMIPConfig *old_config; gboolean has_changes = FALSE; gboolean success = TRUE; + NMSettingsConnection *settings_connection; - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - - _LOGD (LOGD_IP4, "ip4-config: update (commit=%d, new-config=%p)", - commit, new_config); - + 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_ip4_config_get_ifindex (new_config)); + && ip_ifindex == nm_ip_config_get_ifindex (new_config)); }))); + nm_assert (IS_IPv4 || !ip4_dev_route_blacklist); - priv = NM_DEVICE_GET_PRIVATE (self); - - old_config = priv->ip4_config; + _LOGD (LOGD_IP_from_af (addr_family), + "ip%c-config: update (commit=%d, new-config=%p)", + nm_utils_addr_family_to_char (addr_family), + commit, + new_config); /* Always commit to nm-platform to update lifetimes */ if (commit && new_config) { - _commit_mtu (self, new_config); - success = nm_ip4_config_commit (new_config, - nm_device_get_platform (self), - nm_device_get_route_table (self, AF_INET, FALSE) - ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL - : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN); - nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self), - nm_ip4_config_get_ifindex (new_config), - ip4_dev_route_blacklist); - } - - if (new_config) { - if (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_ip4_config_replace (old_config, new_config, &has_changes); - if (has_changes) { - _LOGD (LOGD_IP4, "ip4-config: update IP4Config instance (%s)", - nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_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), + nm_device_get_route_table (self, addr_family, FALSE) + ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL + : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN); + nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self), + nm_ip_config_get_ifindex (new_config), + ip4_dev_route_blacklist); } else { - has_changes = TRUE; - priv->ip4_config = g_object_ref (new_config); + gs_unref_ptrarray GPtrArray *temporary_not_available = NULL; - if (success && !nm_exported_object_is_exported (NM_EXPORTED_OBJECT (new_config))) - nm_exported_object_export (NM_EXPORTED_OBJECT (new_config)); + success = nm_ip6_config_commit (NM_IP6_CONFIG (new_config), + nm_device_get_platform (self), + nm_device_get_route_table (self, addr_family, FALSE) + ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL + : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN, + &temporary_not_available); - _LOGD (LOGD_IP4, "ip4-config: set IP4Config instance (%s)", - nm_exported_object_get_path (NM_EXPORTED_OBJECT (new_config))); + if (!_rt6_temporary_not_available_set (self, temporary_not_available)) + success = FALSE; } - } else if (old_config) { - has_changes = TRUE; - priv->ip4_config = NULL; - _LOGD (LOGD_IP4, "ip4-config: clear IP4Config instance (%s)", - nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config))); - /* Device config is invalid if combined config is invalid */ - g_clear_object (&priv->dev_ip4_config); } - concheck_periodic_update (self); + old_config = priv->ip_config_x[IS_IPv4]; - if (!nm_device_sys_iface_state_is_external_or_assume (self)) - ip4_rp_filter_update (self); + 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_IP_from_af (addr_family), + "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_IP_from_af (addr_family), + "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_IP_from_af (addr_family), + "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_ip4_config); + } else + priv->needs_ip6_subnet = FALSE; + } + + if (IS_IPv4) { + if (!nm_device_sys_iface_state_is_external_or_assume (self)) + ip4_rp_filter_update (self); + } if (has_changes) { - NMSettingsConnection *settings_connection; - _update_ip4_address (self); + if (IS_IPv4) + _update_ip4_address (self); + + if (old_config != priv->ip_config_x[IS_IPv4]) + _notify (self, IS_IPv4 ? PROP_IP4_CONFIG : PROP_IP6_CONFIG); - if (old_config != priv->ip4_config) - _notify (self, PROP_IP4_CONFIG); - g_signal_emit (self, signals[IP4_CONFIG_CHANGED], 0, priv->ip4_config, old_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->ip4_config) - nm_exported_object_clear_and_unexport (&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_FLAGS_NM_GENERATED) - && nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (priv->act_request)) == NM_ACTIVATION_TYPE_EXTERNAL) { - NMSetting *s_ip4; - + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED) + && nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (priv->act_request.obj)) == NM_ACTIVATION_TYPE_EXTERNAL) { g_object_freeze_notify (G_OBJECT (settings_connection)); - - nm_connection_remove_setting (NM_CONNECTION (settings_connection), NM_TYPE_SETTING_IP4_CONFIG); - s_ip4 = nm_ip4_config_create_setting (priv->ip4_config); - nm_connection_add_setting (NM_CONNECTION (settings_connection), s_ip4); - + nm_connection_add_setting (NM_CONNECTION (settings_connection), + IS_IPv4 + ? nm_ip4_config_create_setting (priv->ip_config_4) + : nm_ip6_config_create_setting (priv->ip_config_6)); g_object_thaw_notify (G_OBJECT (settings_connection)); } 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; } @@ -10367,11 +11177,11 @@ nm_device_replace_vpn4_config (NMDevice *self, NMIP4Config *old, NMIP4Config *co 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->vpn4_configs, (GObject *) old, (GObject *) config)) + if (!_replace_vpn_config_in_list (&priv->vpn_configs_4, (GObject *) old, (GObject *) config)) return; /* NULL to use existing configs */ - if (!ip4_config_merge_and_apply (self, TRUE)) + if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) _LOGW (LOGD_IP4, "failed to set VPN routes for device"); } @@ -10380,125 +11190,11 @@ nm_device_set_wwan_ip4_config (NMDevice *self, NMIP4Config *config) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->wwan_ip4_config == config) - return; - - g_clear_object (&priv->wwan_ip4_config); - if (config) - priv->wwan_ip4_config = g_object_ref (config); - - /* NULL to use existing configs */ - if (!ip4_config_merge_and_apply (self, TRUE)) + applied_config_init (&priv->wwan_ip_config_4, config); + if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) _LOGW (LOGD_IP4, "failed to set WWAN IPv4 configuration"); } -static gboolean -nm_device_set_ip6_config (NMDevice *self, - NMIP6Config *new_config, - gboolean commit) -{ - NMDevicePrivate *priv; - NMIP6Config *old_config = NULL; - gboolean has_changes = FALSE; - gboolean success = TRUE; - - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - - _LOGD (LOGD_IP6, "ip6-config: update (commit=%d, new-config=%p)", - commit, new_config); - - nm_assert ( !new_config - || ( new_config - && ({ - int ip_ifindex = nm_device_get_ip_ifindex (self); - - ( ip_ifindex > 0 - && ip_ifindex == nm_ip6_config_get_ifindex (new_config)); - }))); - - priv = NM_DEVICE_GET_PRIVATE (self); - - old_config = priv->ip6_config; - - /* Always commit to nm-platform to update lifetimes */ - if (commit && new_config) { - gs_unref_ptrarray GPtrArray *temporary_not_available = NULL; - - _commit_mtu (self, priv->ip4_config); - - success = nm_ip6_config_commit (new_config, - nm_device_get_platform (self), - nm_device_get_route_table (self, AF_INET6, FALSE) - ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL - : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN, - &temporary_not_available); - - if (!_rt6_temporary_not_available_set (self, temporary_not_available)) - success = FALSE; - } - - if (new_config) { - if (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_ip6_config_replace (old_config, new_config, &has_changes); - if (has_changes) { - _LOGD (LOGD_IP6, "ip6-config: update IP6Config instance (%s)", - nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config))); - } - } else { - has_changes = TRUE; - priv->ip6_config = g_object_ref (new_config); - - if (success && !nm_exported_object_is_exported (NM_EXPORTED_OBJECT (new_config))) - nm_exported_object_export (NM_EXPORTED_OBJECT (new_config)); - - _LOGD (LOGD_IP6, "ip6-config: set IP6Config instance (%s)", - nm_exported_object_get_path (NM_EXPORTED_OBJECT (new_config))); - } - } else if (old_config) { - has_changes = TRUE; - priv->ip6_config = NULL; - priv->needs_ip6_subnet = FALSE; - _LOGD (LOGD_IP6, "ip6-config: clear IP6Config instance (%s)", - nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config))); - } - - if (has_changes) { - NMSettingsConnection *settings_connection; - - if (old_config != priv->ip6_config) - _notify (self, PROP_IP6_CONFIG); - g_signal_emit (self, signals[IP6_CONFIG_CHANGED], 0, priv->ip6_config, old_config); - - if (old_config != priv->ip6_config) - nm_exported_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_FLAGS_NM_GENERATED) - && nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (priv->act_request)) == NM_ACTIVATION_TYPE_EXTERNAL) { - NMSetting *s_ip6; - - g_object_freeze_notify (G_OBJECT (settings_connection)); - - nm_connection_remove_setting (NM_CONNECTION (settings_connection), NM_TYPE_SETTING_IP6_CONFIG); - s_ip6 = nm_ip6_config_create_setting (priv->ip6_config); - nm_connection_add_setting (NM_CONNECTION (settings_connection), s_ip6); - - g_object_thaw_notify (G_OBJECT (settings_connection)); - } - - nm_device_queue_recheck_assume (self); - - if (priv->ndisc) - ndisc_set_router_config (priv->ndisc, self); - } - - return success; -} - void nm_device_replace_vpn6_config (NMDevice *self, NMIP6Config *old, NMIP6Config *config) { @@ -10509,11 +11205,11 @@ nm_device_replace_vpn6_config (NMDevice *self, NMIP6Config *old, NMIP6Config *co nm_assert (!old || nm_ip6_config_get_ifindex (old) == nm_device_get_ip_ifindex (self)); nm_assert (!config || nm_ip6_config_get_ifindex (config) == nm_device_get_ip_ifindex (self)); - if (!_replace_vpn_config_in_list (&priv->vpn6_configs, (GObject *) old, (GObject *) config)) + if (!_replace_vpn_config_in_list (&priv->vpn_configs_6, (GObject *) old, (GObject *) config)) return; /* NULL to use existing configs */ - if (!ip6_config_merge_and_apply (self, TRUE)) + if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) _LOGW (LOGD_IP6, "failed to set VPN routes for device"); } @@ -10522,15 +11218,8 @@ nm_device_set_wwan_ip6_config (NMDevice *self, NMIP6Config *config) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->wwan_ip6_config == config) - return; - - g_clear_object (&priv->wwan_ip6_config); - if (config) - priv->wwan_ip6_config = g_object_ref (config); - - /* NULL to use existing configs */ - if (!ip6_config_merge_and_apply (self, TRUE)) + applied_config_init (&priv->wwan_ip_config_6, config); + if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) _LOGW (LOGD_IP6, "failed to set WWAN IPv6 configuration"); } @@ -10547,7 +11236,7 @@ nm_device_get_ip6_config (NMDevice *self) { g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - return NM_DEVICE_GET_PRIVATE (self)->ip6_config; + return NM_DEVICE_GET_PRIVATE (self)->ip_config_6; } /*****************************************************************************/ @@ -10738,7 +11427,7 @@ start_ping (NMDevice *self, priv->gw_ping.log_domain = log_domain; priv->gw_ping.address = g_strdup (address); priv->gw_ping.binary = g_strdup (binary); - priv->gw_ping.deadline = timeout + 10; /* the proper termination is enforced by a timer */ + priv->gw_ping.deadline = timeout + 10; /* the proper termination is enforced by a timer */ if (spawn_ping (self)) { priv->gw_ping.watch = g_child_watch_add (priv->gw_ping.pid, ip_check_ping_watch_cb, self); @@ -10781,15 +11470,15 @@ nm_device_start_ip_check (NMDevice *self) if (timeout) { const NMPObject *gw; - if (priv->ip4_config && priv->ip4_state == IP_DONE) { - gw = nm_ip4_config_best_default_route_get (priv->ip4_config); + if (priv->ip_config_4 && priv->ip4_state == IP_DONE) { + gw = nm_ip4_config_best_default_route_get (priv->ip_config_4); 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->ip6_config && priv->ip6_state == IP_DONE) { - gw = nm_ip6_config_best_default_route_get (priv->ip6_config); + } else if (priv->ip_config_6 && priv->ip6_state == IP_DONE) { + gw = nm_ip6_config_best_default_route_get (priv->ip_config_6); 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); @@ -10929,11 +11618,11 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) /* when the link comes up, we must restore IP configuration if necessary. */ if (priv->ip4_state == IP_DONE) { - if (!ip4_config_merge_and_apply (self, TRUE)) + if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) _LOGW (LOGD_IP4, "failed applying IP4 config after bringing link up"); } if (priv->ip6_state == IP_DONE) { - if (!ip6_config_merge_and_apply (self, TRUE)) + if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) _LOGW (LOGD_IP6, "failed applying IP6 config after bringing link up"); } @@ -10998,122 +11687,37 @@ nm_device_get_firmware_missing (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->firmware_missing; } -static NMIP4Config * -find_ip4_lease_config (NMDevice *self, - NMConnection *connection, - NMIP4Config *ext_ip4_config) -{ - const char *ip_iface = nm_device_get_ip_iface (self); - int ip_ifindex = nm_device_get_ip_ifindex (self); - GSList *leases, *liter; - NMIP4Config *found = NULL; - - g_return_val_if_fail (NM_IS_IP4_CONFIG (ext_ip4_config), NULL); - g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); - - leases = nm_dhcp_manager_get_lease_ip_configs (nm_dhcp_manager_get (), - nm_device_get_multi_index (self), - AF_INET, - ip_iface, - ip_ifindex, - nm_connection_get_uuid (connection), - nm_device_get_route_table (self, AF_INET, TRUE), - nm_device_get_route_metric (self, AF_INET)); - for (liter = leases; liter && !found; liter = liter->next) { - NMIP4Config *lease_config = liter->data; - const NMPlatformIP4Address *address = nm_ip4_config_get_first_address (lease_config); - const NMPObject *gw1, *gw2; - - g_assert (address); - if (!nm_ip4_config_address_exists (ext_ip4_config, address)) - continue; - gw1 = nm_ip4_config_best_default_route_get (lease_config); - if (!gw1) - continue; - gw2 = nm_ip4_config_best_default_route_get (ext_ip4_config); - if (!gw2) - continue; - if (NMP_OBJECT_CAST_IP4_ROUTE (gw1)->gateway != NMP_OBJECT_CAST_IP4_ROUTE (gw2)->gateway) - continue; - found = g_object_ref (lease_config); - } - - g_slist_free_full (leases, g_object_unref); - return found; -} - static void -capture_lease_config (NMDevice *self, - NMIP4Config *ext_ip4_config, - NMIP4Config **out_ip4_config, - NMIP6Config *ext_ip6_config, - NMIP6Config **out_ip6_config) +intersect_ext_config (NMDevice *self, AppliedConfig *config) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMSettingsConnection *const*connections; - guint i; - gboolean dhcp_used = FALSE; - NMDedupMultiIter ipconf_iter; - - /* Ensure at least one address on the device has a non-infinite lifetime, - * otherwise DHCP cannot possibly be active on the device right now. - */ - if (ext_ip4_config && out_ip4_config) { - const NMPlatformIP4Address *addr; + NMIPConfig *ext; + guint32 penalty; + int family; - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ext_ip4_config, &addr) { - if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { - dhcp_used = TRUE; - break; - } - } - } else if (ext_ip6_config && out_ip6_config) { - const NMPlatformIP6Address *addr; - - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ext_ip6_config, &addr) { - if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { - dhcp_used = TRUE; - break; - } - } - } else { - g_return_if_fail ( (ext_ip6_config && out_ip6_config) - || (ext_ip4_config && out_ip4_config)); - } - - if (!dhcp_used) + if (!config->orig) return; - connections = nm_settings_get_connections (priv->settings, NULL); - for (i = 0; connections[i]; i++) { - NMConnection *candidate = (NMConnection *) connections[i]; - const char *method; - - if (!nm_device_check_connection_compatible (self, candidate)) - continue; - - /* IPv4 leases */ - method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP4_CONFIG); - if (out_ip4_config && strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) { - *out_ip4_config = find_ip4_lease_config (self, candidate, ext_ip4_config); - if (*out_ip4_config) - 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; - /* IPv6 leases */ - method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP6_CONFIG); - if (out_ip6_config && strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) { - /* FIXME: implement find_ip6_lease_config() */ - } + if (config->current) + nm_ip_config_intersect (config->current, ext, penalty); + else { + config->current = nm_ip_config_intersect_alloc (config->orig, + ext, + penalty); } } static gboolean -update_ext_ip_config (NMDevice *self, int addr_family, gboolean initial, gboolean intersect_configs) +update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_configs) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int ifindex; - gboolean capture_resolv_conf; GSList *iter; nm_assert_addr_family (addr_family); @@ -11122,122 +11726,110 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean initial, gboolea if (!ifindex) return FALSE; - capture_resolv_conf = initial - && nm_dns_manager_get_resolv_conf_explicit (nm_dns_manager_get ()); - if (addr_family == AF_INET) { - g_clear_object (&priv->ext_ip4_config); - priv->ext_ip4_config = nm_ip4_config_capture (nm_device_get_multi_index (self), + 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, - capture_resolv_conf); - if (priv->ext_ip4_config) { - if (initial) { - g_clear_object (&priv->dev_ip4_config); - capture_lease_config (self, priv->ext_ip4_config, &priv->dev_ip4_config, NULL, NULL); - } - + 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_ip4_config) { - nm_ip4_config_intersect (priv->con_ip4_config, priv->ext_ip4_config, - default_route_metric_penalty_get (self, AF_INET)); - } - if (priv->dev_ip4_config) { - nm_ip4_config_intersect (priv->dev_ip4_config, priv->ext_ip4_config, + if (priv->con_ip_config_4) { + nm_ip4_config_intersect (priv->con_ip_config_4, priv->ext_ip_config_4, default_route_metric_penalty_get (self, AF_INET)); } - if (priv->wwan_ip4_config) { - nm_ip4_config_intersect (priv->wwan_ip4_config, priv->ext_ip4_config, - default_route_metric_penalty_get (self, AF_INET)); - } - for (iter = priv->vpn4_configs; iter; iter = iter->next) - nm_ip4_config_intersect (iter->data, priv->ext_ip4_config, 0); + + intersect_ext_config (self, &priv->dev_ip4_config); + intersect_ext_config (self, &priv->wwan_ip_config_4); + + for (iter = priv->vpn_configs_4; iter; iter = iter->next) + nm_ip4_config_intersect (iter->data, priv->ext_ip_config_4, 0); } - /* Remove parts from ext_ip4_config to only contain the information that + /* 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_ip4_config) { - nm_ip4_config_subtract (priv->ext_ip4_config, priv->con_ip4_config, + 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 (priv->dev_ip4_config) { - nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config, - default_route_metric_penalty_get (self, AF_INET)); + if (applied_config_get_current (&priv->dev_ip4_config)) { + nm_ip_config_subtract ((NMIPConfig *) priv->ext_ip_config_4, + applied_config_get_current (&priv->dev_ip4_config), + default_route_metric_penalty_get (self, AF_INET)); } - if (priv->wwan_ip4_config) { - nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config, - default_route_metric_penalty_get (self, AF_INET)); + if (applied_config_get_current (&priv->wwan_ip_config_4)) { + nm_ip_config_subtract ((NMIPConfig *) priv->ext_ip_config_4, + applied_config_get_current (&priv->wwan_ip_config_4), + default_route_metric_penalty_get (self, AF_INET)); } - for (iter = priv->vpn4_configs; iter; iter = iter->next) - nm_ip4_config_subtract (priv->ext_ip4_config, iter->data, 0); + 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 (addr_family == AF_INET6); - g_clear_object (&priv->ext_ip6_config); + 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, - capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); if (priv->ext_ip6_config_captured) { - priv->ext_ip6_config = nm_ip6_config_new_cloned (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_ip6_config) { - nm_ip6_config_intersect (priv->con_ip6_config, priv->ext_ip6_config, - default_route_metric_penalty_get (self, AF_INET6)); - } - if (priv->ac_ip6_config) { - nm_ip6_config_intersect (priv->ac_ip6_config, priv->ext_ip6_config, + if (priv->con_ip_config_6) { + nm_ip6_config_intersect (priv->con_ip_config_6, priv->ext_ip_config_6, default_route_metric_penalty_get (self, AF_INET6)); } - if (priv->dhcp6.ip6_config) { - nm_ip6_config_intersect (priv->dhcp6.ip6_config, priv->ext_ip6_config, - default_route_metric_penalty_get (self, AF_INET6)); - } - if (priv->wwan_ip6_config) { - nm_ip6_config_intersect (priv->wwan_ip6_config, priv->ext_ip6_config, - default_route_metric_penalty_get (self, AF_INET6)); - } - for (iter = priv->vpn6_configs; iter; iter = iter->next) - nm_ip6_config_intersect (iter->data, priv->ext_ip6_config, 0); + + intersect_ext_config (self, &priv->ac_ip6_config); + intersect_ext_config (self, &priv->dhcp6.ip6_config); + intersect_ext_config (self, &priv->wwan_ip_config_6); + + for (iter = priv->vpn_configs_6; iter; iter = iter->next) + nm_ip6_config_intersect (iter->data, priv->ext_ip_config_6, 0); + + if ( priv->ipv6ll_has + && !nm_ip6_config_lookup_address (priv->ext_ip_config_6, &priv->ipv6ll_addr)) + priv->ipv6ll_has = FALSE; } - /* Remove parts from ext_ip6_config to only contain the information that + /* 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_ip6_config) { - nm_ip6_config_subtract (priv->ext_ip6_config, priv->con_ip6_config, + 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 (priv->ac_ip6_config) { - nm_ip6_config_subtract (priv->ext_ip6_config, priv->ac_ip6_config, - 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 (priv->dhcp6.ip6_config) { - nm_ip6_config_subtract (priv->ext_ip6_config, priv->dhcp6.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 (priv->wwan_ip6_config) { - nm_ip6_config_subtract (priv->ext_ip6_config, priv->wwan_ip6_config, - default_route_metric_penalty_get (self, AF_INET6)); + if (applied_config_get_current (&priv->wwan_ip_config_6)) { + nm_ip_config_subtract ((NMIPConfig *) priv->ext_ip_config_6, + applied_config_get_current (&priv->wwan_ip_config_6), + default_route_metric_penalty_get (self, AF_INET6)); } - for (iter = priv->vpn6_configs; iter; iter = iter->next) - nm_ip6_config_subtract (priv->ext_ip6_config, iter->data, 0); + for (iter = priv->vpn_configs_6; iter; iter = iter->next) + nm_ip6_config_subtract (priv->ext_ip_config_6, iter->data, 0); } } @@ -11245,156 +11837,139 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean initial, gboolea } static void -update_ip_config (NMDevice *self, int addr_family, gboolean initial) +update_ip_config (NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); nm_assert_addr_family (addr_family); - if (update_ext_ip_config (self, addr_family, initial, TRUE)) { + if (addr_family == AF_INET) + 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 (addr_family == AF_INET) { - if (priv->ext_ip4_config) - ip4_config_merge_and_apply (self, FALSE); + if (priv->ext_ip_config_4) + ip_config_merge_and_apply (self, AF_INET, FALSE); } else { if (priv->ext_ip6_config_captured) - ip6_config_merge_and_apply (self, FALSE); + ip_config_merge_and_apply (self, AF_INET6, FALSE); } } - - if ( addr_family == AF_INET6 - && priv->linklocal6_timeout_id - && priv->ext_ip6_config_captured - && nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE)) { - /* linklocal6 is ready now, do the state transition... we are also - * invoked as g_idle_add, so no problems with reentrance doing it now. - */ - linklocal6_complete (self); - } } void nm_device_capture_initial_config (NMDevice *self) { - update_ip_config (self, AF_INET, TRUE); - update_ip_config (self, AF_INET6, TRUE); + 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_ip4_config_change (gpointer user_data) +queued_ip_config_change (NMDevice *self, int addr_family) { - NMDevice *self = user_data; NMDevicePrivate *priv; + gboolean need_ipv6ll = FALSE; + const gboolean IS_IPv4 = (addr_family == AF_INET); + NMPlatform *platform; g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE); priv = NM_DEVICE_GET_PRIVATE (self); - nm_assert (!priv->queued_ip4_config_pending); + nm_assert (IS_IPv4 ? !priv->queued_ip4_config_pending : !priv->queued_ip6_config_pending); /* Wait for any queued state changes */ if (priv->queued_state.id) - return TRUE; + return G_SOURCE_CONTINUE; - priv->queued_ip4_config_id = 0; + priv->queued_ip_config_id_x[IS_IPv4] = 0; /* 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 (activation_source_is_scheduled (self, - activate_stage5_ip4_config_result, - AF_INET)) { - priv->queued_ip4_config_pending = FALSE; - priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); - _LOGT (LOGD_DEVICE, "IP4 update was postponed"); - } else - update_ip_config (self, AF_INET, FALSE); - - set_unmanaged_external_down (self, TRUE); + IS_IPv4 + ? activate_stage5_ip4_config_result + : activate_stage5_ip6_config_commit, + addr_family)) { + if (IS_IPv4) { + priv->queued_ip4_config_pending = FALSE; + priv->queued_ip_config_id_4 = g_idle_add (queued_ip4_config_change, self); + } else { + priv->queued_ip6_config_pending = FALSE; + priv->queued_ip_config_id_6 = g_idle_add (queued_ip6_config_change, self); + } + _LOGT (LOGD_DEVICE, "IP%c update was postponed", + nm_utils_addr_family_to_char (addr_family)); + } else { + update_ip_config (self, addr_family); - if (!nm_device_sys_iface_state_is_external_or_assume (self)) { - priv->v4_has_shadowed_routes = _v4_has_shadowed_routes_detect (self);; - ip4_rp_filter_update (self); + 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); + } } - return FALSE; -} - -static gboolean -queued_ip6_config_change (gpointer user_data) -{ - NMDevice *self = user_data; - NMDevicePrivate *priv; - GSList *iter; - gboolean need_ipv6ll = FALSE; - - g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE); - - priv = NM_DEVICE_GET_PRIVATE (self); - - nm_assert (!priv->queued_ip4_config_pending); - - /* Wait for any queued state changes */ - if (priv->queued_state.id) - return TRUE; + if (!IS_IPv4) { + if ( priv->state < NM_DEVICE_STATE_DEACTIVATING + && (platform = nm_device_get_platform (self)) + && nm_platform_link_get (platform, priv->ifindex)) { + /* Handle DAD failures */ + while (priv->dad6_failed_addrs) { + nm_auto_nmpobj const NMPObject *obj = NULL; + const NMPlatformIP6Address *addr; - priv->queued_ip6_config_id = 0; + obj = priv->dad6_failed_addrs->data; + priv->dad6_failed_addrs = g_slist_delete_link (priv->dad6_failed_addrs, priv->dad6_failed_addrs); - /* 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 (activation_source_is_scheduled (self, - activate_stage5_ip6_config_commit, - AF_INET6)) { - priv->queued_ip6_config_pending = FALSE; - priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); - _LOGT (LOGD_DEVICE, "IP6 update was postponed"); - } else - update_ip_config (self, AF_INET6, FALSE); + if (!nm_ndisc_dad_addr_is_fail_candidate (platform, obj)) + continue; - if (priv->state < NM_DEVICE_STATE_DEACTIVATING - && nm_platform_link_get (nm_device_get_platform (self), priv->ifindex)) { - /* Handle DAD failures */ - for (iter = priv->dad6_failed_addrs; iter; iter = g_slist_next (iter)) { - NMPlatformIP6Address *addr = iter->data; + addr = NMP_OBJECT_CAST_IP6_ADDRESS (obj); - if (addr->addr_source >= NM_IP_CONFIG_SOURCE_USER) - continue; + _LOGI (LOGD_IP6, "ipv6: duplicate address check failed for the %s address", + nm_platform_ip6_address_to_string (addr, NULL, 0)); - _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) + nm_ndisc_dad_failed (priv->ndisc, &addr->address); + } - if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) + /* 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; - else if (priv->ndisc) - nm_ndisc_dad_failed (priv->ndisc, &addr->address); - } - - /* 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->ip6_config && nm_ip6_config_get_num_addresses (priv->ip6_config)) - need_ipv6ll = TRUE; - - if (need_ipv6ll) - check_and_add_ipv6ll_addr (self); - } - g_slist_free_full (priv->dad6_failed_addrs, g_free); - priv->dad6_failed_addrs = NULL; + if (need_ipv6ll) + check_and_add_ipv6ll_addr (self); + } else { + g_slist_free_full (priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref); + priv->dad6_failed_addrs = NULL; + } - /* Check if DAD is still pending */ - if ( priv->ip6_state == IP_CONF - && priv->dad6_ip6_config - && priv->ext_ip6_config_captured) { - if (!nm_ip6_config_has_any_dad_pending (priv->ext_ip6_config_captured, - priv->dad6_ip6_config)) { + /* Check if DAD is still pending */ + if ( priv->ip6_state == IP_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, AF_INET6, IP_DONE); + _set_ip_state (self, addr_family, IP_DONE); check_ip_state (self, FALSE, TRUE); if (priv->rt6_temporary_not_available) nm_device_activate_schedule_ip6_config_result (self); @@ -11403,21 +11978,40 @@ queued_ip6_config_change (gpointer user_data) set_unmanaged_external_down (self, TRUE); - return FALSE; + if (IS_IPv4) { + if (!nm_device_sys_iface_state_is_external_or_assume (self)) { + priv->v4_has_shadowed_routes = _v4_has_shadowed_routes_detect (self);; + ip4_rp_filter_update (self); + } + } + + 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, - gpointer platform_object, + 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; - NMPlatformIP6Address *addr; + const NMPlatformIP6Address *addr; if (nm_device_get_ip_ifindex (self) != ifindex) return; @@ -11429,10 +12023,10 @@ device_ipx_changed (NMPlatform *platform, case NMP_OBJECT_TYPE_IP4_ROUTE: if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) { priv->queued_ip4_config_pending = TRUE; - nm_assert_se (!nm_clear_g_source (&priv->queued_ip4_config_id)); - } else if (!priv->queued_ip4_config_id) { + nm_assert_se (!nm_clear_g_source (&priv->queued_ip_config_id_4)); + } else if (!priv->queued_ip_config_id_4) { priv->queued_ip4_config_pending = FALSE; - priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); + priv->queued_ip_config_id_4 = g_idle_add (queued_ip4_config_change, self); _LOGD (LOGD_DEVICE, "queued IP4 config change"); } break; @@ -11441,19 +12035,18 @@ device_ipx_changed (NMPlatform *platform, if ( priv->state > NM_DEVICE_STATE_DISCONNECTED && priv->state < NM_DEVICE_STATE_DEACTIVATING - && ( (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->n_ifa_flags & IFA_F_DADFAILED) - || (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->n_ifa_flags & IFA_F_TENTATIVE))) { - priv->dad6_failed_addrs = g_slist_append (priv->dad6_failed_addrs, - g_memdup (addr, sizeof (NMPlatformIP6Address))); + && 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 (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) { priv->queued_ip6_config_pending = TRUE; - nm_assert_se (!nm_clear_g_source (&priv->queued_ip6_config_id)); - } else if (!priv->queued_ip6_config_id) { + nm_assert_se (!nm_clear_g_source (&priv->queued_ip_config_id_6)); + } else if (!priv->queued_ip_config_id_6) { priv->queued_ip6_config_pending = FALSE; - priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); + priv->queued_ip_config_id_6 = g_idle_add (queued_ip6_config_change, self); _LOGD (LOGD_DEVICE, "queued IP6 config change"); } break; @@ -11721,14 +12314,14 @@ _set_unmanaged_flags (NMDevice *self, if (priv->queued_ip4_config_pending) { priv->queued_ip4_config_pending = FALSE; - nm_assert_se (!nm_clear_g_source (&priv->queued_ip4_config_id)); - priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); + nm_assert_se (!nm_clear_g_source (&priv->queued_ip_config_id_4)); + priv->queued_ip_config_id_4 = g_idle_add (queued_ip4_config_change, self); } if (priv->queued_ip6_config_pending) { priv->queued_ip6_config_pending = FALSE; - nm_assert_se (!nm_clear_g_source (&priv->queued_ip6_config_id)); - priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); + nm_assert_se (!nm_clear_g_source (&priv->queued_ip_config_id_6)); + priv->queued_ip_config_id_6 = g_idle_add (queued_ip6_config_change, self); } if (!priv->pending_actions) { @@ -11994,7 +12587,7 @@ nm_device_reapply_settings_immediately (NMDevice *self) if (g_strcmp0 ((zone = nm_setting_connection_get_zone (s_con_settings)), nm_setting_connection_get_zone (s_con_applied)) != 0) { - version_id = nm_active_connection_version_id_bump ((NMActiveConnection *) self->_priv->act_request); + version_id = nm_active_connection_version_id_bump ((NMActiveConnection *) self->_priv->act_request.obj); _LOGD (LOGD_DEVICE, "reapply setting: zone = %s%s%s (version-id %llu)", NM_PRINT_FMT_QUOTE_STRING (zone), (unsigned long long) version_id); g_object_set (G_OBJECT (s_con_applied), @@ -12006,7 +12599,7 @@ nm_device_reapply_settings_immediately (NMDevice *self) if ((metered = nm_setting_connection_get_metered (s_con_settings)) != nm_setting_connection_get_metered (s_con_applied)) { - version_id = nm_active_connection_version_id_bump ((NMActiveConnection *) self->_priv->act_request); + version_id = nm_active_connection_version_id_bump ((NMActiveConnection *) self->_priv->act_request.obj); _LOGD (LOGD_DEVICE, "reapply setting: metered = %d (version-id %llu)", (int) metered, (unsigned long long) version_id); g_object_set (G_OBJECT (s_con_applied), @@ -12062,13 +12655,27 @@ 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->ip4_config + if ( priv->ip_config_4 && priv->ip4_state == IP_DONE - && nm_ip4_config_get_metered (priv->ip4_config)) + && nm_ip4_config_get_metered (priv->ip_config_4)) + value = NM_METERED_GUESS_YES; + } + + /* Otherwise look at connection type. For Bluetooth, we look at the type of + * Bluetooth sharing: for PANU/DUN (where we are receiving internet from + * another device) we set GUESS_YES; for NAP (where we are sharing internet + * to another device) we set GUESS_NO. We ignore WiMAX here as it’s no + * longer supported by NetworkManager. */ + if ( value == NM_METERED_INVALID + && nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME)) { + + if (_nm_connection_get_setting_bluetooth_for_nap (connection)) { + /* NAP types are not metered, but other types are. */ + value = NM_METERED_GUESS_NO; + } else value = NM_METERED_GUESS_YES; } - /* Otherwise look at connection type */ if (value == NM_METERED_INVALID) { if ( nm_connection_is_type (connection, NM_SETTING_GSM_SETTING_NAME) || nm_connection_is_type (connection, NM_SETTING_CDMA_SETTING_NAME)) @@ -12159,7 +12766,7 @@ nm_device_check_connection_available (NMDevice *self, for (i = 0; i <= NM_DEVICE_CHECK_CON_AVAILABLE_ALL; i++) { for (j = 1; j <= NM_DEVICE_CHECK_CON_AVAILABLE_ALL; j <<= 1) { - if (NM_FLAGS_HAS (i, j)) { + if (NM_FLAGS_ANY (i, j)) { k = i & ~j; nm_assert ( available_all[i] == available_all[k] || available_all[i]); @@ -12184,7 +12791,7 @@ available_connections_del_all (NMDevice *self) static gboolean available_connections_add (NMDevice *self, NMConnection *connection) { - return nm_g_hash_table_add (self->_priv->available_connections, g_object_ref (connection)); + return g_hash_table_add (self->_priv->available_connections, g_object_ref (connection)); } static gboolean @@ -12242,7 +12849,7 @@ nm_device_recheck_available_connections (NMDevice *self) priv = NM_DEVICE_GET_PRIVATE(self); if (g_hash_table_size (priv->available_connections) > 0) { - prune_list = g_hash_table_new (g_direct_hash, g_direct_equal); + prune_list = g_hash_table_new (nm_direct_hash, NULL); g_hash_table_iter_init (&h_iter, priv->available_connections); while (g_hash_table_iter_next (&h_iter, (gpointer *) &connection, NULL)) g_hash_table_add (prune_list, connection); @@ -12351,19 +12958,19 @@ cp_connection_added_or_updated (NMDevice *self, NMConnection *connection) } static void -cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) +cp_connection_added (NMSettings *settings, NMConnection *connection, gpointer user_data) { cp_connection_added_or_updated (user_data, connection); } static void -cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gboolean by_user, gpointer user_data) +cp_connection_updated (NMSettings *settings, NMConnection *connection, gboolean by_user, gpointer user_data) { cp_connection_added_or_updated (user_data, connection); } static void -cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) +cp_connection_removed (NMSettings *settings, NMConnection *connection, gpointer user_data) { NMDevice *self = user_data; @@ -12545,8 +13152,8 @@ _cleanup_generic_pre (NMDevice *self, CleanupType cleanup_type) queued_state_clear (self); - _cleanup_ip4_pre (self, cleanup_type); - _cleanup_ip6_pre (self, cleanup_type); + _cleanup_ip_pre (self, AF_INET, cleanup_type); + _cleanup_ip_pre (self, AF_INET6, cleanup_type); } static void @@ -12568,39 +13175,41 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) /* Clean up IP configs; this does not actually deconfigure the * interface; the caller must flush routes and addresses explicitly. */ - nm_device_set_ip4_config (self, NULL, TRUE, NULL); - nm_device_set_ip6_config (self, NULL, TRUE); + 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_ip4_config); - g_clear_object (&priv->dev_ip4_config); - g_clear_object (&priv->ext_ip4_config); - g_clear_object (&priv->wwan_ip4_config); - g_clear_object (&priv->ip4_config); - g_clear_object (&priv->con_ip6_config); - g_clear_object (&priv->ac_ip6_config); - g_clear_object (&priv->ext_ip6_config); + g_clear_object (&priv->con_ip_config_4); + applied_config_clear (&priv->dev_ip4_config); + applied_config_clear (&priv->wwan_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); - g_clear_object (&priv->wwan_ip6_config); - g_clear_object (&priv->ip6_config); + applied_config_clear (&priv->wwan_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)); g_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->vpn4_configs, g_object_unref); - priv->vpn4_configs = NULL; - g_slist_free_full (priv->vpn6_configs, g_object_unref); - priv->vpn6_configs = NULL; + 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_ip6_config(NULL) + /* We no longer accept the delegations. nm_device_set_ip_config(NULL) * above disables them. */ nm_assert (priv->needs_ip6_subnet == FALSE); - if (priv->act_request) { - nm_active_connection_set_default (NM_ACTIVE_CONNECTION (priv->act_request), AF_INET, FALSE); + if (priv->act_request.obj) { + nm_active_connection_set_default (NM_ACTIVE_CONNECTION (priv->act_request.obj), AF_INET, FALSE); priv->master_ready_handled = FALSE; - nm_clear_g_signal_handler (priv->act_request, &priv->master_ready_id); + nm_clear_g_signal_handler (priv->act_request.obj, &priv->master_ready_id); act_request_set (self, NULL); } @@ -12622,7 +13231,7 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) * those are identified by ip_iface, not by iface (which might be a tty * or ATM device). */ - nm_device_set_ip_iface (self, NULL); + _set_ip_ifindex (self, 0, NULL); } /* @@ -12742,10 +13351,10 @@ find_dhcp4_address (NMDevice *self) const NMPlatformIP4Address *a; NMDedupMultiIter ipconf_iter; - if (!priv->ip4_config) + if (!priv->ip_config_4) return NULL; - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, priv->ip4_config, &a) { + 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 g_strdup (nm_utils_inet4_ntop (a->address, NULL)); } @@ -13052,7 +13661,7 @@ _set_state_full (NMDevice *self, g_cancellable_cancel (priv->deactivating_cancellable); /* Cache the activation request for the dispatcher */ - req = nm_g_object_ref (priv->act_request); + req = nm_g_object_ref (priv->act_request.obj); if ( state > NM_DEVICE_STATE_UNMANAGED && state <= NM_DEVICE_STATE_ACTIVATED @@ -13160,8 +13769,8 @@ _set_state_full (NMDevice *self, /* Clean up any half-done IP operations if the device's layer2 * finds out it needs authentication during IP config. */ - _cleanup_ip4_pre (self, CLEANUP_TYPE_DECONFIGURE); - _cleanup_ip6_pre (self, CLEANUP_TYPE_DECONFIGURE); + _cleanup_ip_pre (self, AF_INET, CLEANUP_TYPE_DECONFIGURE); + _cleanup_ip_pre (self, AF_INET6, CLEANUP_TYPE_DECONFIGURE); } break; default: @@ -13175,6 +13784,13 @@ _set_state_full (NMDevice *self, _notify (self, PROP_STATE); _notify (self, PROP_STATE_REASON); + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), + &interface_info_device, + &signal_info_state_changed, + "(uuu)", + (guint32) state, + (guint32) old_state, + (guint32) reason); g_signal_emit (self, signals[STATE_CHANGED], 0, (guint) state, (guint) old_state, (guint) reason); /* Post-process the event after internal notification */ @@ -13325,7 +13941,8 @@ _set_state_full (NMDevice *self, if (ip_config_valid (old_state) && !ip_config_valid (state)) notify_ip_properties (self); - concheck_periodic_update (self); + concheck_update_interval (self, + state == NM_DEVICE_STATE_ACTIVATED); /* Dispose of the cached activation request */ if (req) @@ -14287,8 +14904,15 @@ nm_device_init (NMDevice *self) self->_priv = priv; + c_list_init (&priv->concheck_lst_head); + c_list_init (&self->devices_lst); c_list_init (&priv->slaves); + priv->connectivity_state = NM_CONNECTIVITY_UNKNOWN; + + nm_dbus_track_obj_path_init (&priv->parent_device, G_OBJECT (self), obj_properties[PROP_PARENT]); + nm_dbus_track_obj_path_init (&priv->act_request, G_OBJECT (self), obj_properties[PROP_ACTIVE_CONNECTION]); + priv->netns = g_object_ref (NM_NETNS_GET); priv->autoconnect_blocked_flags = DEFAULT_AUTOCONNECT @@ -14303,7 +14927,7 @@ nm_device_init (NMDevice *self) priv->rfkill_type = RFKILL_TYPE_UNKNOWN; priv->unmanaged_flags = NM_UNMANAGED_PLATFORM_INIT; priv->unmanaged_mask = priv->unmanaged_flags; - priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); + priv->available_connections = g_hash_table_new_full (nm_direct_hash, NULL, g_object_unref, NULL); 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; @@ -14402,9 +15026,19 @@ dispose (GObject *object) NMDevice *self = NM_DEVICE (object); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMPlatform *platform; + NMDeviceConnectivityHandle *con_handle; + gs_free_error GError *cancelled_error = NULL; _LOGD (LOGD_DEVICE, "disposing"); + nm_assert (c_list_is_empty (&self->devices_lst)); + + while ((con_handle = c_list_first_entry (&priv->concheck_lst_head, NMDeviceConnectivityHandle, concheck_lst))) { + if (!cancelled_error) + nm_utils_error_set_cancelled (&cancelled_error, FALSE, "NMDevice"); + concheck_handle_complete (con_handle, cancelled_error); + } + nm_clear_g_cancellable (&priv->deactivating_cancellable); nm_device_assume_state_reset (self); @@ -14415,8 +15049,8 @@ dispose (GObject *object) 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); - g_slist_free_full (priv->arping.dad_list, (GDestroyNotify) nm_arping_manager_destroy); - priv->arping.dad_list = NULL; + g_slist_free_full (priv->acd.dad_list, (GDestroyNotify) nm_acd_manager_destroy); + priv->acd.dad_list = NULL; arp_cleanup (self); @@ -14478,6 +15112,8 @@ dispose (GObject *object) g_clear_object (&priv->lldp_listener); } + nm_clear_g_source (&priv->concheck_p_cur_id); + G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); if (nm_clear_g_source (&priv->queued_state.id)) { @@ -14500,7 +15136,7 @@ finalize (GObject *object) g_free (priv->hw_addr_perm); g_free (priv->hw_addr_initial); g_slist_free (priv->pending_actions); - g_slist_free_full (priv->dad6_failed_addrs, g_free); + g_slist_free_full (priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref); g_clear_pointer (&priv->physical_port_id, g_free); g_free (priv->udi); g_free (priv->iface); @@ -14509,19 +15145,22 @@ finalize (GObject *object) g_free (priv->driver_version); g_free (priv->firmware_version); g_free (priv->type_desc); - g_free (priv->type_description); g_free (priv->dhcp_anycast_address); g_free (priv->current_stable_id); g_hash_table_unref (priv->ip6_saved_properties); g_hash_table_unref (priv->available_connections); + nm_dbus_track_obj_path_deinit (&priv->parent_device); + nm_dbus_track_obj_path_deinit (&priv->act_request); + G_OBJECT_CLASS (nm_device_parent_class)->finalize (object); /* for testing, NMDeviceTest does not invoke NMDevice::constructed, * and thus @settings might be unset. */ - if (priv->settings) - g_object_unref (priv->settings); + nm_g_object_unref (priv->settings); + + nm_g_object_unref (priv->concheck_mgr); g_object_unref (priv->netns); } @@ -14631,9 +15270,6 @@ get_property (GObject *object, guint prop_id, { NMDevice *self = NM_DEVICE (object); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - GPtrArray *array; - GHashTableIter iter; - NMConnection *connection; GVariantBuilder array_builder; switch (prop_id) { @@ -14689,16 +15325,16 @@ get_property (GObject *object, guint prop_id, g_value_set_uint (value, priv->mtu); break; case PROP_IP4_CONFIG: - nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip4_config : NULL); + nm_dbus_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip_config_4 : NULL); break; case PROP_DHCP4_CONFIG: - nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp4.config : NULL); + nm_dbus_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp4.config : NULL); break; case PROP_IP6_CONFIG: - nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip6_config : NULL); + nm_dbus_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip_config_6 : NULL); break; case PROP_DHCP6_CONFIG: - nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp6.config : NULL); + nm_dbus_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp6.config : NULL); break; case PROP_STATE: g_value_set_uint (value, priv->state); @@ -14708,7 +15344,7 @@ get_property (GObject *object, guint prop_id, g_variant_new ("(uu)", priv->state, priv->state_reason)); break; case PROP_ACTIVE_CONNECTION: - nm_utils_g_value_set_object_path (value, priv->act_request_public ? priv->act_request : NULL); + g_value_set_string (value, nm_dbus_track_obj_path_get (&priv->act_request)); break; case PROP_DEVICE_TYPE: g_value_set_uint (value, priv->type); @@ -14739,12 +15375,9 @@ get_property (GObject *object, guint prop_id, g_value_set_uint (value, priv->rfkill_type); break; case PROP_AVAILABLE_CONNECTIONS: - array = g_ptr_array_sized_new (g_hash_table_size (priv->available_connections)); - g_hash_table_iter_init (&iter, priv->available_connections); - while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL)) - g_ptr_array_add (array, g_strdup (nm_connection_get_path (connection))); - g_ptr_array_add (array, NULL); - g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE)); + nm_dbus_utils_g_value_set_object_path_from_hash (value, + priv->available_connections, + TRUE); break; case PROP_PHYSICAL_PORT_ID: g_value_set_string (value, priv->physical_port_id); @@ -14753,7 +15386,7 @@ get_property (GObject *object, guint prop_id, g_value_set_object (value, nm_device_get_master (self)); break; case PROP_PARENT: - nm_utils_g_value_set_object_path (value, priv->parent_device); + g_value_set_string (value, nm_dbus_track_obj_path_get (&priv->parent_device)); break; case PROP_HW_ADDRESS: g_value_set_string (value, priv->hw_addr); @@ -14798,7 +15431,7 @@ get_property (GObject *object, guint prop_id, if (!NM_DEVICE_GET_PRIVATE (info->slave)->is_enslaved) continue; - path = nm_exported_object_get_path ((NMExportedObject *) info->slave); + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (info->slave)); if (path) slave_list[i++] = g_strdup (path); } @@ -14825,15 +15458,115 @@ get_property (GObject *object, guint prop_id, } } +static const GDBusSignalInfo signal_info_state_changed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "StateChanged", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("new_state", "u"), + NM_DEFINE_GDBUS_ARG_INFO ("old_state", "u"), + NM_DEFINE_GDBUS_ARG_INFO ("reason", "u"), + ), +); + +static const NMDBusInterfaceInfoExtended interface_info_device = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "Reapply", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO ("version_id", "t"), + NM_DEFINE_GDBUS_ARG_INFO ("flags", "u"), + ), + ), + .handle = impl_device_reapply, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "GetAppliedConnection", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("flags", "u"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO ("version_id", "t"), + ), + ), + .handle = impl_device_get_applied_connection, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "Disconnect", + ), + .handle = impl_device_disconnect, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "Delete", + ), + .handle = impl_device_delete, + ), + ), + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &signal_info_state_changed, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Udi", "s", NM_DEVICE_UDI), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Interface", "s", NM_DEVICE_IFACE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("IpInterface", "s", NM_DEVICE_IP_IFACE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Driver", "s", NM_DEVICE_DRIVER), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("DriverVersion", "s", NM_DEVICE_DRIVER_VERSION), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("FirmwareVersion", "s", NM_DEVICE_FIRMWARE_VERSION), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Capabilities", "u", NM_DEVICE_CAPABILITIES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ip4Address", "u", NM_DEVICE_IP4_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("State", "u", NM_DEVICE_STATE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("StateReason", "(uu)", NM_DEVICE_STATE_REASON), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ActiveConnection", "o", NM_DEVICE_ACTIVE_CONNECTION), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ip4Config", "o", NM_DEVICE_IP4_CONFIG), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Dhcp4Config", "o", NM_DEVICE_DHCP4_CONFIG), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ip6Config", "o", NM_DEVICE_IP6_CONFIG), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Dhcp6Config", "o", NM_DEVICE_DHCP6_CONFIG), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("Managed", "b", NM_DEVICE_MANAGED, NM_AUTH_PERMISSION_NETWORK_CONTROL, NM_AUDIT_OP_DEVICE_MANAGED), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("Autoconnect", "b", NM_DEVICE_AUTOCONNECT, NM_AUTH_PERMISSION_NETWORK_CONTROL, NM_AUDIT_OP_DEVICE_AUTOCONNECT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("FirmwareMissing", "b", NM_DEVICE_FIRMWARE_MISSING), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("NmPluginMissing", "b", NM_DEVICE_NM_PLUGIN_MISSING), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("DeviceType", "u", NM_DEVICE_DEVICE_TYPE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("AvailableConnections", "ao", NM_DEVICE_AVAILABLE_CONNECTIONS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("PhysicalPortId", "s", NM_DEVICE_PHYSICAL_PORT_ID), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mtu", "u", NM_DEVICE_MTU), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Metered", "u", NM_DEVICE_METERED), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("LldpNeighbors", "aa{sv}", NM_DEVICE_LLDP_NEIGHBORS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Real", "b", NM_DEVICE_REAL), + ), + ), +}; + +const NMDBusInterfaceInfoExtended nm_interface_info_device_statistics = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_STATISTICS, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE ("RefreshRateMs", "u", NM_DEVICE_STATISTICS_REFRESH_RATE_MS, NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS, NM_AUDIT_OP_STATISTICS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("TxBytes", "t", NM_DEVICE_STATISTICS_TX_BYTES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("RxBytes", "t", NM_DEVICE_STATISTICS_RX_BYTES), + ), + ), +}; + static void nm_device_class_init (NMDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (NMDevicePrivate)); - exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/Devices"); + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/Devices"); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device, + &nm_interface_info_device_statistics); object_class->dispose = dispose; object_class->finalize = finalize; @@ -15065,7 +15798,6 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - /* Statistics */ obj_properties[PROP_REFRESH_RATE_MS] = g_param_spec_uint (NM_DEVICE_STATISTICS_REFRESH_RATE_MS, "", "", 0, UINT32_MAX, 0, @@ -15082,7 +15814,6 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - /* Connectivity */ obj_properties[PROP_CONNECTIVITY] = g_param_spec_uint (NM_DEVICE_CONNECTIVITY, "", "", NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, @@ -15165,15 +15896,11 @@ nm_device_class_init (NMDeviceClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 0); - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_SKELETON, - "Reapply", impl_device_reapply, - "GetAppliedConnection", impl_device_get_applied_connection, - "Disconnect", impl_device_disconnect, - "Delete", impl_device_delete, - NULL); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_STATISTICS_SKELETON, - NULL); + signals[CONNECTIVITY_CHANGED] = + g_signal_new (NM_DEVICE_CONNECTIVITY_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index ac73ee0c..0d4d5cf8 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -24,7 +24,8 @@ #include -#include "nm-exported-object.h" +#include "nm-setting-connection.h" +#include "nm-dbus-object.h" #include "nm-dbus-interface.h" #include "nm-connection.h" #include "nm-rfkill-manager.h" @@ -72,7 +73,6 @@ nm_device_state_reason_check (NMDeviceStateReason reason) #define NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "queued-state-change-" #define NM_PENDING_ACTIONPREFIX_ACTIVATION "activation-" - /* Properties */ #define NM_DEVICE_UDI "udi" #define NM_DEVICE_IFACE "interface" @@ -113,8 +113,7 @@ nm_device_state_reason_check (NMDeviceStateReason reason) #define NM_DEVICE_PARENT "parent" /* the "slaves" property is internal in the parent class, but exposed - * by the derived classes NMDeviceBond, NMDeviceBridge and NMDeviceTeam. - * It is thus important that the property name matches. */ + * by the derived classes NMDeviceBond, NMDeviceBridge and NMDeviceTeam. */ #define NM_DEVICE_SLAVES "slaves" /* partially internal */ #define NM_DEVICE_TYPE_DESC "type-desc" /* Internal only */ @@ -135,6 +134,7 @@ nm_device_state_reason_check (NMDeviceStateReason reason) #define NM_DEVICE_STATE_CHANGED "state-changed" #define NM_DEVICE_LINK_INITIALIZED "link-initialized" #define NM_DEVICE_AUTOCONNECT_ALLOWED "autoconnect-allowed" +#define NM_DEVICE_CONNECTIVITY_CHANGED "connectivity-changed" #define NM_DEVICE_STATISTICS_REFRESH_RATE_MS "refresh-rate-ms" #define NM_DEVICE_STATISTICS_TX_BYTES "tx-bytes" @@ -172,10 +172,9 @@ typedef enum { /*< skip >*/ struct _NMDevicePrivate; struct _NMDevice { - NMExportedObject parent; - - /* private */ + NMDBusObject parent; struct _NMDevicePrivate *_priv; + CList devices_lst; }; /* The flags have an relaxing meaning, that means, specifying more flags, can make @@ -189,8 +188,11 @@ typedef enum { /*< skip >*/ NM_DEVICE_CHECK_DEV_AVAILABLE_ALL = (1L << 1) - 1, } NMDeviceCheckDevAvailableFlags; -typedef struct { - NMExportedObjectClass parent; +typedef struct _NMDeviceClass { + NMDBusObjectClass parent; + + struct _NMDeviceClass *default_type_description_klass; + const char *default_type_description; const char *connection_type; const NMLinkType *link_types; @@ -312,7 +314,7 @@ typedef struct { gboolean (* complete_connection) (NMDevice *self, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error); NMActStageReturn (* act_stage1_prepare) (NMDevice *self, @@ -520,7 +522,7 @@ gboolean nm_device_can_auto_connect (NMDevice *self, gboolean nm_device_complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connection, + NMConnection *const*existing_connections, GError **error); gboolean nm_device_check_connection_compatible (NMDevice *device, NMConnection *connection); @@ -715,7 +717,7 @@ void nm_device_queue_state (NMDevice *self, gboolean nm_device_get_firmware_missing (NMDevice *self); -void nm_device_steal_connection (NMDevice *device, NMSettingsConnection *connection); +void nm_device_disconnect_active_connection (NMActiveConnection *active); void nm_device_queue_activation (NMDevice *device, NMActRequest *req); @@ -754,12 +756,10 @@ void nm_device_update_firewall_zone (NMDevice *self); void nm_device_update_metered (NMDevice *self); void nm_device_reactivate_ip4_config (NMDevice *device, NMSettingIPConfig *s_ip4_old, - NMSettingIPConfig *s_ip4_new, - gboolean force_restart); + NMSettingIPConfig *s_ip4_new); void nm_device_reactivate_ip6_config (NMDevice *device, NMSettingIPConfig *s_ip6_old, - NMSettingIPConfig *s_ip6_new, - gboolean force_restart); + NMSettingIPConfig *s_ip6_new); gboolean nm_device_update_hw_address (NMDevice *self); void nm_device_update_initial_hw_address (NMDevice *self); @@ -776,12 +776,22 @@ gboolean nm_device_hw_addr_get_cloned (NMDevice *self, gboolean *preserve, GError **error); +typedef struct _NMDeviceConnectivityHandle NMDeviceConnectivityHandle; + typedef void (*NMDeviceConnectivityCallback) (NMDevice *self, + NMDeviceConnectivityHandle *handle, NMConnectivityState state, + GError *error, gpointer user_data); -void nm_device_check_connectivity (NMDevice *self, - NMDeviceConnectivityCallback callback, - gpointer user_data); + +void nm_device_check_connectivity_update_interval (NMDevice *self); + +NMDeviceConnectivityHandle *nm_device_check_connectivity (NMDevice *self, + NMDeviceConnectivityCallback callback, + gpointer user_data); + +void nm_device_check_connectivity_cancel (NMDeviceConnectivityHandle *handle); + NMConnectivityState nm_device_get_connectivity_state (NMDevice *self); typedef struct _NMBtVTableNetworkServer NMBtVTableNetworkServer; diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c index 2ed2a7d9..f637825b 100644 --- a/src/devices/nm-lldp-listener.c +++ b/src/devices/nm-lldp-listener.c @@ -286,19 +286,21 @@ lldp_neighbor_id_hash (gconstpointer ptr) } static int -lldp_neighbor_id_cmp (gconstpointer a, gconstpointer b) +lldp_neighbor_id_cmp (const LldpNeighbor *x, const LldpNeighbor *y) { - const LldpNeighbor *x = a, *y = b; - int c; - - if (x->chassis_id_type != y->chassis_id_type) - return x->chassis_id_type < y->chassis_id_type ? -1 : 1; - if (x->port_id_type != y->port_id_type) - return x->port_id_type < y->port_id_type ? -1 : 1; - c = g_strcmp0 (x->chassis_id, y->chassis_id); - if (c == 0) - c = g_strcmp0 (x->port_id, y->port_id); - return c < 0 ? -1 : (c > 0 ? 1 : 0); + NM_CMP_SELF (x, y); + NM_CMP_FIELD (x, y, chassis_id_type); + NM_CMP_FIELD (x, y, port_id_type); + NM_CMP_FIELD_STR0 (x, y, chassis_id); + NM_CMP_FIELD_STR0 (x, y, port_id); + return 0; +} + +static int +lldp_neighbor_id_cmp_p (gconstpointer a, gconstpointer b, gpointer user_data) +{ + return lldp_neighbor_id_cmp (*((const LldpNeighbor *const*) a), + *((const LldpNeighbor *const*) b)); } static gboolean @@ -838,20 +840,23 @@ GVariant * nm_lldp_listener_get_neighbors (NMLldpListener *self) { NMLldpListenerPrivate *priv; - GVariantBuilder array_builder; - GList *neighbors, *iter; g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), FALSE); priv = NM_LLDP_LISTENER_GET_PRIVATE (self); - if (!priv->variant) { + if (G_UNLIKELY (!priv->variant)) { + GVariantBuilder array_builder; + gs_free LldpNeighbor **neighbors = NULL; + guint i, n; + g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}")); - neighbors = g_hash_table_get_keys (priv->lldp_neighbors); - neighbors = g_list_sort (neighbors, lldp_neighbor_id_cmp); - for (iter = neighbors; iter; iter = iter->next) - g_variant_builder_add_value (&array_builder, lldp_neighbor_to_variant (iter->data)); - g_list_free (neighbors); + neighbors = (LldpNeighbor **) nm_utils_hash_keys_to_array (priv->lldp_neighbors, + lldp_neighbor_id_cmp_p, + NULL, + &n); + for (i = 0; i < n; i++) + g_variant_builder_add_value (&array_builder, lldp_neighbor_to_variant (neighbors[i])); priv->variant = g_variant_ref_sink (g_variant_builder_end (&array_builder)); } return priv->variant; diff --git a/src/devices/ovs/meson.build b/src/devices/ovs/meson.build new file mode 100644 index 00000000..f4edd57c --- /dev/null +++ b/src/devices/ovs/meson.build @@ -0,0 +1,38 @@ +sources = files( + 'nm-device-ovs-bridge.c', + 'nm-device-ovs-interface.c', + 'nm-device-ovs-port.c', + 'nm-ovsdb.c', + 'nm-ovs-factory.c' +) + +deps = [ + jansson_dep, + nm_dep +] + +libnm_device_plugin_ovs = shared_module( + 'nm-device-plugin-ovs', + sources: sources, + dependencies: deps, + c_args: '-DRUNSTATEDIR="@0@"'.format(nm_runstatedir), + link_args: ldflags_linker_script_devices, + link_depends: linker_script_devices, + install: true, + install_dir: nm_plugindir +) + +core_plugins += libnm_device_plugin_ovs + +run_target( + 'check-local-devices-ovs', + command: [check_exports, libnm_device_plugin_ovs.full_path(), linker_script_devices], + depends: libnm_device_plugin_ovs +) + +# FIXME: check_so_symbols replacement +''' +check-local-devices-ovs: src/devices/ovs/libnm-device-plugin-ovs.la + $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/ovs/.libs/libnm-device-plugin-ovs.so "$(srcdir)/linker-script-devices.ver" + $(call check_so_symbols,$(builddir)/src/devices/ovs/.libs/libnm-device-plugin-ovs.so) +''' diff --git a/src/devices/ovs/nm-device-ovs-bridge.c b/src/devices/ovs/nm-device-ovs-bridge.c index 53ea2b82..5244ca8b 100644 --- a/src/devices/ovs/nm-device-ovs-bridge.c +++ b/src/devices/ovs/nm-device-ovs-bridge.c @@ -28,10 +28,8 @@ #include "nm-setting-connection.h" #include "nm-setting-ovs-bridge.h" -#include "introspection/org.freedesktop.NetworkManager.Device.OvsBridge.h" - #include "devices/nm-device-logging.h" -_LOG_DECLARE_SELF(NMDeviceOvsBridge); +_LOG_DECLARE_SELF (NMDeviceOvsBridge); /*****************************************************************************/ @@ -133,11 +131,24 @@ nm_device_ovs_bridge_init (NMDeviceOvsBridge *self) { } +static const NMDBusInterfaceInfoExtended interface_info_device_ovs_bridge = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_ovs_bridge_class_init (NMDeviceOvsBridgeClass *klass) { + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ovs_bridge); + device_class->connection_type = NM_SETTING_OVS_BRIDGE_SETTING_NAME; device_class->is_master = TRUE; device_class->get_type_description = get_type_description; @@ -149,8 +160,4 @@ nm_device_ovs_bridge_class_init (NMDeviceOvsBridgeClass *klass) device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; device_class->enslave_slave = enslave_slave; device_class->release_slave = release_slave; - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_OVS_BRIDGE_SKELETON, - NULL); } diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c index ce32c2dd..eefe38c8 100644 --- a/src/devices/ovs/nm-device-ovs-interface.c +++ b/src/devices/ovs/nm-device-ovs-interface.c @@ -28,8 +28,6 @@ #include "nm-setting-ovs-interface.h" #include "nm-setting-ovs-port.h" -#include "introspection/org.freedesktop.NetworkManager.Device.OvsInterface.h" - #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceOvsInterface); @@ -185,13 +183,26 @@ nm_device_ovs_interface_init (NMDeviceOvsInterface *self) { } +static const NMDBusInterfaceInfoExtended interface_info_device_ovs_interface = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *klass) { + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_OPENVSWITCH); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ovs_interface); + device_class->connection_type = NM_SETTING_OVS_INTERFACE_SETTING_NAME; device_class->get_type_description = get_type_description; device_class->create_and_realize = create_and_realize; @@ -202,8 +213,4 @@ nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *klass) device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; device_class->can_unmanaged_external_down = can_unmanaged_external_down; - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_OVS_INTERFACE_SKELETON, - NULL); } diff --git a/src/devices/ovs/nm-device-ovs-port.c b/src/devices/ovs/nm-device-ovs-port.c index cb0915af..91cf4f4d 100644 --- a/src/devices/ovs/nm-device-ovs-port.c +++ b/src/devices/ovs/nm-device-ovs-port.c @@ -28,10 +28,8 @@ #include "nm-setting-ovs-port.h" #include "nm-setting-ovs-port.h" -#include "introspection/org.freedesktop.NetworkManager.Device.OvsPort.h" - #include "devices/nm-device-logging.h" -_LOG_DECLARE_SELF(NMDeviceOvsPort); +_LOG_DECLARE_SELF (NMDeviceOvsPort); /*****************************************************************************/ @@ -72,7 +70,6 @@ get_generic_capabilities (NMDevice *device) return NM_DEVICE_CAP_IS_SOFTWARE; } - static gboolean check_connection_compatible (NMDevice *device, NMConnection *connection) { @@ -180,11 +177,24 @@ nm_device_ovs_port_init (NMDeviceOvsPort *self) { } +static const NMDBusInterfaceInfoExtended interface_info_device_ovs_port = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_OVS_PORT, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_ovs_port_class_init (NMDeviceOvsPortClass *klass) { + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ovs_port); + device_class->connection_type = NM_SETTING_OVS_PORT_SETTING_NAME; device_class->is_master = TRUE; device_class->get_type_description = get_type_description; @@ -195,8 +205,4 @@ nm_device_ovs_port_class_init (NMDeviceOvsPortClass *klass) device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; device_class->enslave_slave = enslave_slave; device_class->release_slave = release_slave; - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_OVS_PORT_SKELETON, - NULL); } diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c index 92fcfa01..a9cb5703 100644 --- a/src/devices/ovs/nm-ovsdb.c +++ b/src/devices/ovs/nm-ovsdb.c @@ -22,37 +22,20 @@ #include "nm-ovsdb.h" #include -#include #include #include +#include "nm-utils/nm-jansson.h" #include "devices/nm-device.h" #include "platform/nm-platform.h" #include "nm-core-internal.h" -/* Added in Jansson v2.4 (released Sep 23 2012), but travis.ci has v2.2. */ -#ifndef json_boolean -#define json_boolean(val) ((val) ? json_true() : json_false()) -#endif - -/* Added in Jansson v2.5 (released Sep 19 2013), but travis.ci has v2.2. */ -#ifndef json_array_foreach -#define json_array_foreach(array, index, value) \ - for (index = 0; \ - index < json_array_size(array) && (value = json_array_get(array, index)); \ - index++) -#endif +/*****************************************************************************/ -/* Added in Jansson v2.3 (released Jan 27 2012) */ -#ifndef json_object_foreach -#define json_object_foreach(object, key, value) \ - for(key = json_object_iter_key(json_object_iter(object)); \ - key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ - key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key)))) +#if JANSSON_VERSION_HEX < 0x020400 +#warning "requires at least libjansson 2.4" #endif -/*****************************************************************************/ - typedef struct { char *name; char *connection_uuid; @@ -95,7 +78,7 @@ typedef struct { GHashTable *interfaces; /* interface uuid => OpenvswitchInterface */ GHashTable *ports; /* port uuid => OpenvswitchPort */ GHashTable *bridges; /* bridge uuid => OpenvswitchBridge */ - const char *db_uuid; + char *db_uuid; } NMOvsdbPrivate; struct _NMOvsdb { @@ -144,7 +127,7 @@ typedef struct { OvsdbMethodCallback callback; gpointer user_data; union { - const char *ifname; + char *ifname; struct { NMConnection *bridge; NMConnection *port; @@ -380,7 +363,7 @@ _insert_interface (json_t *params, NMConnection *interface) json_pack ("{s:s, s:s, s:{s:s, s:s, s:o, s:[s, [[s, s]]]}, s:s}", "op", "insert", "table", "Interface", "row", "name", nm_connection_get_interface_name (interface), - "type", type ? type : "", + "type", type ?: "", "options", options, "external_ids", "map", "NM.connection.uuid", nm_connection_get_uuid (interface), "uuid-name", "rowInterface")); @@ -884,7 +867,7 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg) if (ovs) { iter = json_object_iter (ovs); - priv->db_uuid = g_strdup (iter ? json_object_iter_key (iter) : NULL); + priv->db_uuid = iter ? g_strdup (json_object_iter_key (iter)) : NULL; } /* Interfaces */ @@ -908,7 +891,7 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg) _LOGT ("removed an '%s' interface: %s%s%s", ovs_interface->type, ovs_interface->name, ovs_interface->connection_uuid ? ", " : "", - ovs_interface->connection_uuid ? ovs_interface->connection_uuid : ""); + ovs_interface->connection_uuid ?: ""); if (g_strcmp0 (ovs_interface->type, "internal") == 0) { /* Currently the factory only creates NMDevices for * internal interfaces. Ignore the rest. */ @@ -927,14 +910,14 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg) if (old) { _LOGT ("changed an '%s' interface: %s%s%s", type, ovs_interface->name, ovs_interface->connection_uuid ? ", " : "", - ovs_interface->connection_uuid ? ovs_interface->connection_uuid : ""); + ovs_interface->connection_uuid ?: ""); g_signal_emit (self, signals[DEVICE_CHANGED], 0, "ovs-interface", ovs_interface->name); } else { _LOGT ("added an '%s' interface: %s%s%s", ovs_interface->type, ovs_interface->name, ovs_interface->connection_uuid ? ", " : "", - ovs_interface->connection_uuid ? ovs_interface->connection_uuid : ""); + ovs_interface->connection_uuid ?: ""); if (g_strcmp0 (ovs_interface->type, "internal") == 0) { /* Currently the factory only creates NMDevices for * internal interfaces. Ignore the rest. */ @@ -966,7 +949,7 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg) old = FALSE; _LOGT ("removed a port: %s%s%s", ovs_port->name, ovs_port->connection_uuid ? ", " : "", - ovs_port->connection_uuid ? ovs_port->connection_uuid : ""); + ovs_port->connection_uuid ?: ""); g_signal_emit (self, signals[DEVICE_REMOVED], 0, ovs_port->name, NM_DEVICE_TYPE_OVS_PORT); } @@ -982,13 +965,13 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg) if (old) { _LOGT ("changed a port: %s%s%s", ovs_port->name, ovs_port->connection_uuid ? ", " : "", - ovs_port->connection_uuid ? ovs_port->connection_uuid : ""); + ovs_port->connection_uuid ?: ""); g_signal_emit (self, signals[DEVICE_CHANGED], 0, NM_SETTING_OVS_PORT_SETTING_NAME, ovs_port->name); } else { _LOGT ("added a port: %s%s%s", ovs_port->name, ovs_port->connection_uuid ? ", " : "", - ovs_port->connection_uuid ? ovs_port->connection_uuid : ""); + ovs_port->connection_uuid ?: ""); g_signal_emit (self, signals[DEVICE_ADDED], 0, ovs_port->name, NM_DEVICE_TYPE_OVS_PORT); } @@ -1016,7 +999,7 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg) old = FALSE; _LOGT ("removed a bridge: %s%s%s", ovs_bridge->name, ovs_bridge->connection_uuid ? ", " : "", - ovs_bridge->connection_uuid ? ovs_bridge->connection_uuid : ""); + ovs_bridge->connection_uuid ?: ""); g_signal_emit (self, signals[DEVICE_REMOVED], 0, ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE); } @@ -1032,13 +1015,13 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg) if (old) { _LOGT ("changed a bridge: %s%s%s", ovs_bridge->name, ovs_bridge->connection_uuid ? ", " : "", - ovs_bridge->connection_uuid ? ovs_bridge->connection_uuid : ""); + ovs_bridge->connection_uuid ?: ""); g_signal_emit (self, signals[DEVICE_CHANGED], 0, NM_SETTING_OVS_BRIDGE_SETTING_NAME, ovs_bridge->name); } else { _LOGT ("added a bridge: %s%s%s", ovs_bridge->name, ovs_bridge->connection_uuid ? ", " : "", - ovs_bridge->connection_uuid ? ovs_bridge->connection_uuid : ""); + ovs_bridge->connection_uuid ?: ""); g_signal_emit (self, signals[DEVICE_ADDED], 0, ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE); } @@ -1170,7 +1153,6 @@ ovsdb_got_msg (NMOvsdb *self, json_t *msg) return; } - /* This is a message we are not interested in. */ _LOGW ("got an unknown message, ignoring"); } diff --git a/src/devices/team/meson.build b/src/devices/team/meson.build new file mode 100644 index 00000000..159c182a --- /dev/null +++ b/src/devices/team/meson.build @@ -0,0 +1,35 @@ +sources = files( + 'nm-device-team.c', + 'nm-team-factory.c' +) + +deps = [ + jansson_dep, + libteamdctl_dep, + nm_dep +] + +libnm_device_plugin_team = shared_module( + 'nm-device-plugin-team', + sources: sources, + dependencies: deps, + link_args: ldflags_linker_script_devices, + link_depends: linker_script_devices, + install: true, + install_dir: nm_plugindir +) + +core_plugins += libnm_device_plugin_team + +run_target( + 'check-local-devices-team', + command: [check_exports, libnm_device_plugin_team.full_path(), linker_script_devices], + depends: libnm_device_plugin_team +) + +# FIXME: check_so_symbols replacement +''' +check-local-devices-team: src/devices/team/libnm-device-plugin-team.la + $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/team/.libs/libnm-device-plugin-team.so "$(srcdir)/linker-script-devices.ver" + $(call check_so_symbols,$(builddir)/src/devices/team/.libs/libnm-device-plugin-team.so) +''' diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c index 098cd437..f83ba22b 100644 --- a/src/devices/team/nm-device-team.c +++ b/src/devices/team/nm-device-team.c @@ -28,8 +28,8 @@ #include #include #include -#include +#include "nm-utils/nm-jansson.h" #include "NetworkManagerUtils.h" #include "devices/nm-device-private.h" #include "platform/nm-platform.h" @@ -37,8 +37,6 @@ #include "nm-ip4-config.h" #include "nm-dbus-compat.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Team.h" - #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceTeam); @@ -106,7 +104,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMSettingTeam *s_team; @@ -891,10 +889,27 @@ dispose (GObject *object) G_OBJECT_CLASS (nm_device_team_parent_class)->dispose (object); } +static const NMDBusInterfaceInfoExtended interface_info_device_team = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_TEAM, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier", "b", NM_DEVICE_CARRIER), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Slaves", "ao", NM_DEVICE_SLAVES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Config", "s", NM_DEVICE_TEAM_CONFIG), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_team_class_init (NMDeviceTeamClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_TEAM_SETTING_NAME, NM_LINK_TYPE_TEAM) @@ -903,6 +918,8 @@ nm_device_team_class_init (NMDeviceTeamClass *klass) object_class->dispose = dispose; object_class->get_property = get_property; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_team); + parent_class->is_master = TRUE; parent_class->create_and_realize = create_and_realize; parent_class->get_generic_capabilities = get_generic_capabilities; @@ -924,8 +941,4 @@ nm_device_team_class_init (NMDeviceTeamClass *klass) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_TEAM_SKELETON, - NULL); } diff --git a/src/devices/tests/meson.build b/src/devices/tests/meson.build new file mode 100644 index 00000000..02c61ced --- /dev/null +++ b/src/devices/tests/meson.build @@ -0,0 +1,18 @@ +test_units = [ + 'test-acd', + 'test-lldp' +] + +foreach test_unit: test_units + exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep + ) + + test( + 'devices/' + test_unit, + test_script, + args: test_args + [exe.full_path()] + ) +endforeach diff --git a/src/devices/tests/test-acd.c b/src/devices/tests/test-acd.c new file mode 100644 index 00000000..8a2852a2 --- /dev/null +++ b/src/devices/tests/test-acd.c @@ -0,0 +1,185 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-platform.c - Handle runtime kernel networking configuration + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "nm-default.h" + +#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 + +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_set_up (NM_PLATFORM_GET, fixture->ifindex0, NULL)); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex1, NULL)); + + 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, GMainLoop *loop) +{ + g_main_loop_quit (loop); +} + +static void +test_acd_common (test_fixture *fixture, TestInfo *info) +{ + gs_unref_object NMAcdManager *manager = NULL; + GMainLoop *loop; + int i; + const guint WAIT_TIME_OPTIMISTIC = 50; + guint wait_time; + gulong signal_id; + + /* 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: + + manager = nm_acd_manager_new (fixture->ifindex0, fixture->hwaddr0, fixture->hwaddr0_len); + 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); + } + + loop = g_main_loop_new (NULL, FALSE); + signal_id = g_signal_connect (manager, NM_ACD_MANAGER_PROBE_TERMINATED, + G_CALLBACK (acd_manager_probe_terminated), loop); + g_assert (nm_acd_manager_start_probe (manager, wait_time)); + g_assert (nmtst_main_loop_run (loop, 2000)); + g_signal_handler_disconnect (manager, signal_id); + g_main_loop_unref (loop); + + for (i = 0; info->addresses[i]; i++) { + gboolean val; + + 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; + g_clear_object (&manager); + goto again; + } + + g_error ("expected check for address #%d (%s) to %s, but it didn't", + i, nm_utils_inet4_ntop (info->addresses[i], NULL), + 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) +{ + gs_unref_object NMAcdManager *manager = NULL; + GMainLoop *loop; + + manager = nm_acd_manager_new (fixture->ifindex0, fixture->hwaddr0, fixture->hwaddr0_len); + 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); + nm_acd_manager_announce_addresses (manager); + g_assert (!nmtst_main_loop_run (loop, 200)); + g_main_loop_unref (loop); +} + +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/devices/tests/test-arping.c b/src/devices/tests/test-arping.c deleted file mode 100644 index f0537545..00000000 --- a/src/devices/tests/test-arping.c +++ /dev/null @@ -1,164 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* nm-platform.c - Handle runtime kernel networking configuration - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2015 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "devices/nm-arping-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 - -typedef struct { - int ifindex0; - int ifindex1; -} 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_set_up (NM_PLATFORM_GET, fixture->ifindex0, NULL)); - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex1, NULL)); -} - -typedef struct { - in_addr_t addresses[8]; - in_addr_t peer_addresses[8]; - gboolean expected_result[8]; -} TestInfo; - -static void -arping_manager_probe_terminated (NMArpingManager *arping_manager, GMainLoop *loop) -{ - g_main_loop_quit (loop); -} - -static void -test_arping_common (test_fixture *fixture, TestInfo *info) -{ - gs_unref_object NMArpingManager *manager = NULL; - GMainLoop *loop; - int i; - const guint WAIT_TIME_OPTIMISTIC = 50; - guint wait_time; - gulong signal_id; - - if (!nm_utils_find_helper ("arping", NULL, NULL)) { - g_test_skip ("arping binary is missing"); - 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: - - manager = nm_arping_manager_new (fixture->ifindex0); - g_assert (manager != NULL); - - for (i = 0; info->addresses[i]; i++) - g_assert (nm_arping_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); - } - - loop = g_main_loop_new (NULL, FALSE); - signal_id = g_signal_connect (manager, NM_ARPING_MANAGER_PROBE_TERMINATED, - G_CALLBACK (arping_manager_probe_terminated), loop); - g_assert (nm_arping_manager_start_probe (manager, wait_time, NULL)); - g_assert (nmtst_main_loop_run (loop, 2000)); - g_signal_handler_disconnect (manager, signal_id); - g_main_loop_unref (loop); - - for (i = 0; info->addresses[i]; i++) { - gboolean val; - - val = nm_arping_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; - g_clear_object (&manager); - goto again; - } - - g_error ("expected check for address #%d (%s) to %s, but it didn't", - i, nm_utils_inet4_ntop (info->addresses[i], NULL), - info->expected_result[i] ? "detect no duplicated" : "detect a duplicate"); - } -} - -static void -test_arping_1 (test_fixture *fixture, gconstpointer user_data) -{ - TestInfo info = { .addresses = { ADDR1, ADDR2, ADDR3 }, - .peer_addresses = { ADDR4 }, - .expected_result = { TRUE, TRUE, TRUE } }; - - test_arping_common (fixture, &info); -} - -static void -test_arping_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_arping_common (fixture, &info); -} - -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 ("/arping/1", test_fixture, NULL, fixture_setup, test_arping_1, fixture_teardown); - g_test_add ("/arping/2", test_fixture, NULL, fixture_setup, test_arping_2, fixture_teardown); -} diff --git a/src/devices/tests/test-lldp.c b/src/devices/tests/test-lldp.c index c3c4f0d2..c2ac8e19 100644 --- a/src/devices/tests/test-lldp.c +++ b/src/devices/tests/test-lldp.c @@ -169,7 +169,6 @@ _test_recv_data0_check (GMainLoop *loop, NMLldpListener *listener) TEST_RECV_DATA_DEFINE (_test_recv_data0, 1, _test_recv_data0_check, &_test_recv_data0_frame0); TEST_RECV_DATA_DEFINE (_test_recv_data0_twice, 1, _test_recv_data0_check, &_test_recv_data0_frame0, &_test_recv_data0_frame0); - TEST_RECV_FRAME_DEFINE (_test_recv_data1_frame0, /* lldp.detailed.pcap from * https://wiki.wireshark.org/SampleCaptures#Link_Layer_Discovery_Protocol_.28LLDP.29 */ @@ -347,8 +346,7 @@ static void _test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data) { const NMPlatformLink *link; - struct ifreq ifr = { }; - int fd, s; + nm_auto_close int fd = -1; fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC); if (fd == -1) { @@ -357,20 +355,45 @@ _test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data) return; } - ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - nm_utils_ifname_cpy (ifr.ifr_name, TEST_IFNAME); - g_assert (ioctl (fd, TUNSETIFF, &ifr) >= 0); - - /* Bring the interface up */ - s = socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - g_assert (s >= 0); - ifr.ifr_flags |= IFF_UP; - g_assert (ioctl (s, SIOCSIFFLAGS, &ifr) >= 0); - nm_close (s); + if (nmtst_get_rand_bool ()) { + const NMPlatformLnkTun lnk = { + .type = IFF_TAP, + .pi = FALSE, + .vnet_hdr = FALSE, + .multi_queue = FALSE, + .persist = FALSE, + }; + + nm_close (nm_steal_fd (&fd)); + + link = nmtstp_link_tun_add (NM_PLATFORM_GET, + FALSE, + TEST_IFNAME, + &lnk, + &fd); + g_assert (link); + nmtstp_link_set_updown (NM_PLATFORM_GET, -1, link->ifindex, TRUE); + link = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, TEST_IFNAME, NM_LINK_TYPE_TUN, 0); + } else { + int s; + struct ifreq ifr = { }; + + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + nm_utils_ifname_cpy (ifr.ifr_name, TEST_IFNAME); + g_assert (ioctl (fd, TUNSETIFF, &ifr) >= 0); + + /* Bring the interface up */ + s = socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + g_assert (s >= 0); + ifr.ifr_flags |= IFF_UP; + g_assert (ioctl (s, SIOCSIFFLAGS, &ifr) >= 0); + nm_close (s); + + link = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, TEST_IFNAME, NM_LINK_TYPE_TUN, 100); + } - link = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, TEST_IFNAME, NM_LINK_TYPE_TAP, 100); fixture->ifindex = link->ifindex; - fixture->fd = fd; + fixture->fd = nm_steal_fd (&fd); memcpy (fixture->mac, link->addr.data, ETH_ALEN); } diff --git a/src/devices/wifi/meson.build b/src/devices/wifi/meson.build new file mode 100644 index 00000000..a27f8e6a --- /dev/null +++ b/src/devices/wifi/meson.build @@ -0,0 +1,51 @@ +common_sources = files( + 'nm-wifi-ap.c', + 'nm-wifi-utils.c' +) + +sources = common_sources + files( + 'nm-wifi-factory.c', + 'nm-wifi-common.c', + 'nm-device-wifi.c', + 'nm-device-olpc-mesh.c' +) + +if enable_iwd + sources += files( + 'nm-device-iwd.c', + 'nm-iwd-manager.c', + ) +endif + +deps = [ + nm_dep +] + +libnm_device_plugin_wifi = shared_module( + 'nm-device-plugin-wifi', + sources: sources, + dependencies: deps, + link_args: ldflags_linker_script_devices, + link_depends: linker_script_devices, + install: true, + install_dir: nm_plugindir +) + +core_plugins += libnm_device_plugin_wifi + +run_target( + 'check-local-devices-wifi', + command: [check_exports, libnm_device_plugin_wifi.full_path(), linker_script_devices], + depends: libnm_device_plugin_wifi +) + +# FIXME: check_so_symbols replacement +''' +check-local-devices-wifi: src/devices/wifi/libnm-device-plugin-wifi.la + $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/wifi/.libs/libnm-device-plugin-wifi.so "$(srcdir)/linker-script-devices.ver" + $(call check_so_symbols,$(builddir)/src/devices/wifi/.libs/libnm-device-plugin-wifi.so) +''' + +if enable_tests + subdir('tests') +endif diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c new file mode 100644 index 00000000..d3c5ae9a --- /dev/null +++ b/src/devices/wifi/nm-device-iwd.c @@ -0,0 +1,1921 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2017 Intel Corporation + */ + +#include "nm-default.h" + +#include "nm-device-iwd.h" + +#include + +#include "nm-common-macros.h" +#include "devices/nm-device.h" +#include "devices/nm-device-private.h" +#include "nm-utils.h" +#include "nm-act-request.h" +#include "nm-setting-connection.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" +#include "nm-setting-8021x.h" +#include "settings/nm-settings-connection.h" +#include "settings/nm-settings.h" +#include "nm-wifi-utils.h" +#include "nm-wifi-common.h" +#include "nm-core-internal.h" +#include "nm-config.h" +#include "nm-iwd-manager.h" +#include "nm-dbus-manager.h" + +#include "devices/nm-device-logging.h" +_LOG_DECLARE_SELF(NMDeviceIwd); + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceIwd, + PROP_MODE, + PROP_BITRATE, + PROP_ACCESS_POINTS, + PROP_ACTIVE_ACCESS_POINT, + PROP_CAPABILITIES, + PROP_SCANNING, +); + +enum { + SCANNING_PROHIBITED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct { + GDBusObject * dbus_obj; + GDBusProxy * dbus_proxy; + CList aps_lst_head; + NMWifiAP * current_ap; + GCancellable * cancellable; + NMDeviceWifiCapabilities capabilities; + NMActRequestGetSecretsCallId *wifi_secrets_id; + GDBusMethodInvocation *secrets_request; + guint periodic_scan_id; + bool enabled:1; + bool can_scan:1; + bool can_connect:1; + bool scanning:1; + bool scan_requested:1; +} NMDeviceIwdPrivate; + +struct _NMDeviceIwd { + NMDevice parent; + NMDeviceIwdPrivate _priv; +}; + +struct _NMDeviceIwdClass { + NMDeviceClass parent; + + /* Signals */ + gboolean (*scanning_prohibited) (NMDeviceIwd *device, gboolean periodic); +}; + +/*****************************************************************************/ + +G_DEFINE_TYPE (NMDeviceIwd, nm_device_iwd, NM_TYPE_DEVICE) + +#define NM_DEVICE_IWD_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDeviceIwd, NM_IS_DEVICE_IWD) + +/*****************************************************************************/ + +static void schedule_periodic_scan (NMDeviceIwd *self, + NMDeviceState current_state); + +/*****************************************************************************/ + +static void +_ap_dump (NMDeviceIwd *self, + NMLogLevel log_level, + const NMWifiAP *ap, + const char *prefix, + gint32 now_s) +{ + char buf[1024]; + + buf[0] = '\0'; + _NMLOG (log_level, LOGD_WIFI_SCAN, "wifi-ap: %-7s %s", + prefix, + nm_wifi_ap_to_string (ap, buf, sizeof (buf), now_s)); +} + +/* Callers ensure we're not removing current_ap */ +static void +ap_add_remove (NMDeviceIwd *self, + gboolean is_adding, /* or else is removing */ + NMWifiAP *ap, + gboolean recheck_available_connections) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + if (is_adding) { + g_object_ref (ap); + ap->wifi_device = NM_DEVICE (self); + c_list_link_tail (&priv->aps_lst_head, &ap->aps_lst); + nm_dbus_object_export (NM_DBUS_OBJECT (ap)); + _ap_dump (self, LOGL_DEBUG, ap, "added", 0); + nm_device_wifi_emit_signal_access_point (NM_DEVICE (self), ap, TRUE); + } else { + ap->wifi_device = NULL; + c_list_unlink (&ap->aps_lst); + _ap_dump (self, LOGL_DEBUG, ap, "removed", 0); + } + + _notify (self, PROP_ACCESS_POINTS); + + if (!is_adding) { + nm_device_wifi_emit_signal_access_point (NM_DEVICE (self), ap, FALSE); + nm_dbus_object_clear_and_unexport (&ap); + } + + nm_device_emit_recheck_auto_activate (NM_DEVICE (self)); + if (recheck_available_connections) + nm_device_recheck_available_connections (NM_DEVICE (self)); +} + +static void +set_current_ap (NMDeviceIwd *self, NMWifiAP *new_ap, gboolean recheck_available_connections) +{ + NMDeviceIwdPrivate *priv; + NMWifiAP *old_ap; + + g_return_if_fail (NM_IS_DEVICE_IWD (self)); + + priv = NM_DEVICE_IWD_GET_PRIVATE (self); + old_ap = priv->current_ap; + + if (old_ap == new_ap) + return; + + if (new_ap) + priv->current_ap = g_object_ref (new_ap); + else + priv->current_ap = NULL; + + if (old_ap) { + if (nm_wifi_ap_get_fake (old_ap)) + ap_add_remove (self, FALSE, old_ap, recheck_available_connections); + g_object_unref (old_ap); + } + + _notify (self, PROP_ACTIVE_ACCESS_POINT); + _notify (self, PROP_MODE); +} + +static void +remove_all_aps (NMDeviceIwd *self) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMWifiAP *ap, *ap_safe; + + if (c_list_is_empty (&priv->aps_lst_head)) + return; + + set_current_ap (self, NULL, FALSE); + + c_list_for_each_entry_safe (ap, ap_safe, &priv->aps_lst_head, aps_lst) + ap_add_remove (self, FALSE, ap, FALSE); + + nm_device_emit_recheck_auto_activate (NM_DEVICE (self)); + nm_device_recheck_available_connections (NM_DEVICE (self)); +} + +static GVariant * +vardict_from_network_type (const gchar *type) +{ + GVariantBuilder builder; + const gchar *key_mgmt = ""; + const gchar *pairwise = "ccmp"; + + if (!strcmp (type, "psk")) + key_mgmt = "wpa-psk"; + else if (!strcmp (type, "8021x")) + key_mgmt = "wpa-eap"; + else + return NULL; + + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&builder, "{sv}", "KeyMgmt", + g_variant_new_strv (&key_mgmt, 1)); + g_variant_builder_add (&builder, "{sv}", "Pairwise", + g_variant_new_strv (&pairwise, 1)); + g_variant_builder_add (&builder, "{sv}", "Group", + g_variant_new_string ("ccmp")); + return g_variant_new ("a{sv}", &builder); +} + +static void +get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *variant = NULL; + GVariantIter *networks; + const gchar *path, *name, *type; + int16_t signal; + NMWifiAP *ap, *ap_safe, *new_ap; + gboolean changed = FALSE; + GHashTableIter ap_iter; + gs_unref_hashtable GHashTable *new_aps = NULL; + + variant = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, + G_VARIANT_TYPE ("(a(osns))"), + &error); + if (!variant) { + _LOGE (LOGD_WIFI, "Device.GetOrderedNetworks failed: %s", + error->message); + return; + } + + new_aps = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref); + + g_variant_get (variant, "(a(osns))", &networks); + + while (g_variant_iter_next (networks, "(&o&sn&s)", &path, &name, &signal, &type)) { + GVariantBuilder builder; + gs_unref_variant GVariant *props = NULL; + GVariant *rsn; + static uint32_t ap_id = 0; + uint8_t bssid[6]; + + /* + * What we get from IWD are networks, or ESSs, that may + * contain multiple APs, or BSSs, each. We don't get + * information about any specific BSSs within an ESS but + * we can safely present each ESS as an individual BSS to + * NM, which will be seen as ESSs comprising a single BSS + * each. NM won't be able to handle roaming but IWD already + * does that. We fake the BSSIDs as they don't play any + * role either. + */ + bssid[0] = 0x00; + bssid[1] = 0x01; + bssid[2] = 0x02; + bssid[3] = ap_id >> 16; + bssid[4] = ap_id >> 8; + bssid[5] = ap_id++; + + /* WEP not supported */ + if (!strcmp (type, "wep")) + continue; + + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&builder, "{sv}", "BSSID", + g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, bssid, 6, 1)); + g_variant_builder_add (&builder, "{sv}", "Mode", + g_variant_new_string ("infrastructure")); + + rsn = vardict_from_network_type (type); + if (rsn) + g_variant_builder_add (&builder, "{sv}", "RSN", rsn); + + props = g_variant_new ("a{sv}", &builder); + + ap = nm_wifi_ap_new_from_properties (path, props); + if (name[0] != '\0') + nm_wifi_ap_set_ssid (ap, (const guint8 *) name, strlen (name)); + nm_wifi_ap_set_strength (ap, nm_wifi_utils_level_to_quality (signal / 100)); + nm_wifi_ap_set_freq (ap, 2417); + nm_wifi_ap_set_max_bitrate (ap, 65000); + g_hash_table_insert (new_aps, + (gpointer) nm_wifi_ap_get_supplicant_path (ap), + ap); + } + + g_variant_iter_free (networks); + + c_list_for_each_entry_safe (ap, ap_safe, &priv->aps_lst_head, aps_lst) { + + new_ap = g_hash_table_lookup (new_aps, + nm_wifi_ap_get_supplicant_path (ap)); + if (new_ap) { + if (nm_wifi_ap_set_strength (ap, nm_wifi_ap_get_strength (new_ap))) { + _ap_dump (self, LOGL_TRACE, ap, "updated", 0); + changed = TRUE; + } + g_hash_table_remove (new_aps, + nm_wifi_ap_get_supplicant_path (ap)); + continue; + } + + if (ap == priv->current_ap) { + /* Normally IWD will prevent the current AP from being + * removed from the list and set a low signal strength, + * but just making sure. + */ + continue; + } + + ap_add_remove (self, FALSE, ap, FALSE); + changed = TRUE; + } + + g_hash_table_iter_init (&ap_iter, new_aps); + while (g_hash_table_iter_next (&ap_iter, NULL, (gpointer) &ap)) { + ap_add_remove (self, TRUE, ap, FALSE); + g_hash_table_iter_remove (&ap_iter); + changed = TRUE; + } + + if (changed) { + nm_device_emit_recheck_auto_activate (NM_DEVICE (self)); + nm_device_recheck_available_connections (NM_DEVICE (self)); + } +} + +static void +update_aps (NMDeviceIwd *self) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + if (!priv->cancellable) + priv->cancellable = g_cancellable_new (); + + g_dbus_proxy_call (priv->dbus_proxy, "GetOrderedNetworks", + g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, + 2000, priv->cancellable, + get_ordered_networks_cb, self); +} + +static void +send_disconnect (NMDeviceIwd *self) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + g_dbus_proxy_call (priv->dbus_proxy, "Disconnect", g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); +} + +static void +wifi_secrets_cancel (NMDeviceIwd *self) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + if (priv->wifi_secrets_id) + nm_act_request_cancel_secrets (NULL, priv->wifi_secrets_id); + nm_assert (!priv->wifi_secrets_id); + + if (priv->secrets_request) { + g_dbus_method_invocation_return_error_literal (priv->secrets_request, NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "NM secrets request cancelled"); + priv->secrets_request = NULL; + } + +} + +static void +cleanup_association_attempt (NMDeviceIwd *self, gboolean disconnect) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + wifi_secrets_cancel (self); + + set_current_ap (self, NULL, TRUE); + + if (disconnect && priv->dbus_obj) + send_disconnect (self); +} + +static void +deactivate (NMDevice *device) +{ + cleanup_association_attempt (NM_DEVICE_IWD (device), TRUE); +} + +static gboolean +deactivate_async_finish (NMDevice *device, GAsyncResult *res, GError **error) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (NM_DEVICE_IWD (device)); + gs_unref_variant GVariant *variant = NULL; + + variant = g_dbus_proxy_call_finish (priv->dbus_proxy, res, error); + return variant != NULL; +} + +typedef struct { + NMDeviceIwd *self; + GAsyncReadyCallback callback; + gpointer user_data; +} DeactivateContext; + +static void +disconnect_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + DeactivateContext *ctx = user_data; + + ctx->callback (G_OBJECT (ctx->self), res, ctx->user_data); + + g_object_unref (ctx->self); + g_slice_free (DeactivateContext, ctx); +} + +static void +deactivate_async (NMDevice *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + DeactivateContext *ctx; + + ctx = g_slice_new0 (DeactivateContext); + ctx->self = g_object_ref (self); + ctx->callback = callback; + ctx->user_data = user_data; + + g_dbus_proxy_call (priv->dbus_proxy, "Disconnect", g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, -1, cancellable, disconnect_cb, ctx); +} + +static NMIwdNetworkSecurity +get_connection_iwd_security (NMConnection *connection) +{ + NMSettingWirelessSecurity *s_wireless_sec; + const char *key_mgmt = NULL; + + s_wireless_sec = nm_connection_get_setting_wireless_security (connection); + if (!s_wireless_sec) + return NM_IWD_NETWORK_SECURITY_NONE; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec); + nm_assert (key_mgmt); + + if (!strcmp (key_mgmt, "none") || !strcmp (key_mgmt, "ieee8021x")) + return NM_IWD_NETWORK_SECURITY_WEP; + + if (!strcmp (key_mgmt, "wpa-psk")) + return NM_IWD_NETWORK_SECURITY_PSK; + + nm_assert (!strcmp (key_mgmt, "wpa-eap")); + return NM_IWD_NETWORK_SECURITY_8021X; +} + +static gboolean +is_connection_known_network (NMConnection *connection) +{ + NMSettingWireless *s_wireless; + GBytes *ssid; + gs_free gchar *str_ssid = NULL; + + s_wireless = nm_connection_get_setting_wireless (connection); + if (!s_wireless) + return FALSE; + + ssid = nm_setting_wireless_get_ssid (s_wireless); + if (!ssid) + return FALSE; + + str_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), + g_bytes_get_size (ssid)); + + return nm_iwd_manager_is_known_network (nm_iwd_manager_get (), + str_ssid, + get_connection_iwd_security (connection)); +} + +static gboolean +check_connection_compatible (NMDevice *device, NMConnection *connection) +{ + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + const char *mac; + const char * const *mac_blacklist; + int i; + const char *mode; + const char *perm_hw_addr; + + if (!NM_DEVICE_CLASS (nm_device_iwd_parent_class)->check_connection_compatible (device, connection)) + return FALSE; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRELESS_SETTING_NAME)) + return FALSE; + + s_wireless = nm_connection_get_setting_wireless (connection); + if (!s_wireless) + return FALSE; + + perm_hw_addr = nm_device_get_permanent_hw_address (device); + mac = nm_setting_wireless_get_mac_address (s_wireless); + if (perm_hw_addr) { + if (mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1)) + return FALSE; + + /* Check for MAC address blacklist */ + mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless); + for (i = 0; mac_blacklist[i]; i++) { + if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) { + g_warn_if_reached (); + return FALSE; + } + + if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_hw_addr, -1)) + return FALSE; + } + } else if (mac) + return FALSE; + + mode = nm_setting_wireless_get_mode (s_wireless); + if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0) + return FALSE; + + /* 8021x networks can only be used if they've been provisioned on the IWD side and + * thus are Known Networks. + */ + if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) { + if (!is_connection_known_network (connection)) + return FALSE; + } + + return TRUE; +} + +static gboolean +check_connection_available (NMDevice *device, + NMConnection *connection, + NMDeviceCheckConAvailableFlags flags, + const char *specific_object) +{ + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMSettingWireless *s_wifi; + const char *mode; + + s_wifi = nm_connection_get_setting_wireless (connection); + g_return_val_if_fail (s_wifi, FALSE); + + /* Only Infrastrusture mode at this time */ + mode = nm_setting_wireless_get_mode (s_wifi); + if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0) + return FALSE; + + /* Hidden SSIDs not supported yet */ + if (nm_setting_wireless_get_hidden (s_wifi)) + return FALSE; + + /* 8021x networks can only be used if they've been provisioned on the IWD side and + * thus are Known Networks. + */ + if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) { + if (!is_connection_known_network (connection)) + return FALSE; + } + + /* a connection that is available for a certain @specific_object, MUST + * also be available in general (without @specific_object). */ + + if (specific_object) { + NMWifiAP *ap; + + ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object); + return ap ? nm_wifi_ap_check_compatible (ap, connection) : FALSE; + } + + if (NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP)) + return TRUE; + + /* Check at least one AP is compatible with this connection */ + return !!nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); +} + +static gboolean +complete_connection (NMDevice *device, + NMConnection *connection, + const char *specific_object, + NMConnection *const*existing_connections, + GError **error) +{ + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMSettingWireless *s_wifi; + const char *setting_mac; + char *str_ssid = NULL; + NMWifiAP *ap; + const GByteArray *ssid = NULL; + GByteArray *tmp_ssid = NULL; + GBytes *setting_ssid = NULL; + const char *perm_hw_addr; + const char *mode; + + s_wifi = nm_connection_get_setting_wireless (connection); + + mode = s_wifi ? nm_setting_wireless_get_mode (s_wifi) : NULL; + + if (s_wifi && !nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_INFRA)) { + g_set_error_literal (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "Only Infrastructure mode is supported."); + return FALSE; + } + + if (!specific_object) { + /* If not given a specific object, we need at minimum an SSID */ + if (!s_wifi) { + g_set_error_literal (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "A 'wireless' setting is required if no AP path was given."); + return FALSE; + } + + setting_ssid = nm_setting_wireless_get_ssid (s_wifi); + if (!setting_ssid || g_bytes_get_size (setting_ssid) == 0) { + g_set_error_literal (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "A 'wireless' setting with a valid SSID is required if no AP path was given."); + return FALSE; + } + + /* Find a compatible AP in the scan list */ + ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); + if (!ap) { + g_set_error_literal (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "No compatible AP in the scan list and hidden SSIDs not supported."); + return FALSE; + } + } else { + ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object); + if (!ap) { + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_SPECIFIC_OBJECT_NOT_FOUND, + "The access point %s was not in the scan list.", + specific_object); + return FALSE; + } + } + + /* Add a wifi setting if one doesn't exist yet */ + if (!s_wifi) { + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + } + + ssid = nm_wifi_ap_get_ssid (ap); + + if (ssid == NULL) { + g_set_error_literal (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "A 'wireless' setting with a valid SSID is required."); + return FALSE; + } + + if (!nm_wifi_ap_complete_connection (ap, + connection, + nm_wifi_utils_is_manf_default_ssid (ssid), + error)) { + if (tmp_ssid) + g_byte_array_unref (tmp_ssid); + return FALSE; + } + + str_ssid = nm_utils_ssid_to_utf8 (ssid->data, ssid->len); + + nm_utils_complete_generic (nm_device_get_platform (device), + connection, + NM_SETTING_WIRELESS_SETTING_NAME, + existing_connections, + str_ssid, + str_ssid, + NULL, + TRUE); + g_free (str_ssid); + if (tmp_ssid) + g_byte_array_unref (tmp_ssid); + + /* 8021x networks can only be used if they've been provisioned on the IWD side and + * thus are Known Networks. + */ + if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) { + if (!is_connection_known_network (connection)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "This 8021x network has not been provisioned on this machine"); + return FALSE; + } + } + + perm_hw_addr = nm_device_get_permanent_hw_address (device); + if (perm_hw_addr) { + setting_mac = nm_setting_wireless_get_mac_address (s_wifi); + if (setting_mac) { + /* Make sure the setting MAC (if any) matches the device's permanent MAC */ + if (!nm_utils_hwaddr_matches (setting_mac, -1, perm_hw_addr, -1)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + "connection does not match device"); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS); + return FALSE; + } + } else { + guint8 tmp[ETH_ALEN]; + + /* Lock the connection to this device by default if it uses a + * permanent MAC address (ie not a 'locally administered' one) + */ + nm_utils_hwaddr_aton (perm_hw_addr, tmp, ETH_ALEN); + if (!(tmp[0] & 0x02)) { + g_object_set (G_OBJECT (s_wifi), + NM_SETTING_WIRELESS_MAC_ADDRESS, perm_hw_addr, + NULL); + } + } + } + + return TRUE; +} + +static gboolean +is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) +{ + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + gs_unref_variant GVariant *value = NULL; + + if (!priv->enabled || !priv->dbus_obj) + return FALSE; + + value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "Powered"); + return g_variant_get_boolean (value); +} + +static gboolean +get_autoconnect_allowed (NMDevice *device) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (NM_DEVICE_IWD (device)); + + return is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE) + && priv->can_connect; +} + +static gboolean +can_auto_connect (NMDevice *device, + NMConnection *connection, + char **specific_object) +{ + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMSettingWireless *s_wifi; + NMWifiAP *ap; + const char *mode; + guint64 timestamp = 0; + + nm_assert (!specific_object || !*specific_object); + + if (!NM_DEVICE_CLASS (nm_device_iwd_parent_class)->can_auto_connect (device, connection, NULL)) + return FALSE; + + s_wifi = nm_connection_get_setting_wireless (connection); + g_return_val_if_fail (s_wifi, FALSE); + + /* Only Infrastrusture mode */ + mode = nm_setting_wireless_get_mode (s_wifi); + if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0) + return FALSE; + + /* Don't autoconnect to networks that have been tried at least once + * but haven't been successful, since these are often accidental choices + * from the menu and the user may not know the password. + */ + if (nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), ×tamp)) { + if (timestamp == 0) + return FALSE; + } + + /* 8021x networks can only be used if they've been provisioned on the IWD side and + * thus are Known Networks. + */ + if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) { + if (!is_connection_known_network (connection)) + return FALSE; + } + + ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); + if (ap) { + /* All good; connection is usable */ + NM_SET_OUT (specific_object, g_strdup (nm_dbus_object_get_path (NM_DBUS_OBJECT (ap)))); + return TRUE; + } + + return FALSE; +} + +const CList * +_nm_device_iwd_get_aps (NMDeviceIwd *self) +{ + return &NM_DEVICE_IWD_GET_PRIVATE (self)->aps_lst_head; +} + +static gboolean +check_scanning_prohibited (NMDeviceIwd *self, gboolean periodic) +{ + gboolean prohibited = FALSE; + + g_signal_emit (self, signals[SCANNING_PROHIBITED], 0, periodic, &prohibited); + return prohibited; +} + +static void +scan_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv; + gs_free_error GError *error = NULL; + + if ( !_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, + G_VARIANT_TYPE ("()"), &error) + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + priv = NM_DEVICE_IWD_GET_PRIVATE (self); + priv->scan_requested = FALSE; + + /* On success, priv->scanning becomes true right before or right + * after this callback, so the next automatic scan will be + * scheduled when priv->scanning goes back to false. On error, + * schedule a retry now. + */ + if (error && !priv->scanning) { + NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); + + schedule_periodic_scan (self, state); + } +} + +static void +dbus_request_scan_cb (NMDevice *device, + GDBusMethodInvocation *context, + NMAuthSubject *subject, + GError *error, + gpointer user_data) +{ + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv; + gs_unref_variant GVariant *scan_options = user_data; + + if (error) { + g_dbus_method_invocation_return_gerror (context, error); + return; + } + + if (check_scanning_prohibited (self, FALSE)) { + g_dbus_method_invocation_return_error_literal (context, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ALLOWED, + "Scanning not allowed at this time"); + return; + } + + priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + if ( !priv->enabled + || !priv->dbus_obj + || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED + || nm_device_is_activating (device)) { + g_dbus_method_invocation_return_error_literal (context, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ALLOWED, + "Scanning not allowed while unavailable"); + return; + } + + if (scan_options) { + gs_unref_variant GVariant *val = g_variant_lookup_value (scan_options, "ssids", NULL); + + if (val) { + g_dbus_method_invocation_return_error_literal (context, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ALLOWED, + "'ssid' scan option not supported"); + return; + } + } + + if (!priv->scanning && !priv->scan_requested) { + g_dbus_proxy_call (priv->dbus_proxy, "Scan", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, -1, + priv->cancellable, scan_cb, self); + priv->scan_requested = TRUE; + } + + g_dbus_method_invocation_return_value (context, NULL); +} + +void +_nm_device_iwd_request_scan (NMDeviceIwd *self, + GVariant *options, + GDBusMethodInvocation *invocation) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + + if ( !priv->enabled + || !priv->dbus_obj + || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED + || nm_device_is_activating (device)) { + g_dbus_method_invocation_return_error_literal (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ALLOWED, + "Scanning not allowed while unavailable"); + return; + } + + g_signal_emit_by_name (device, + NM_DEVICE_AUTH_REQUEST, + invocation, + NULL, + NM_AUTH_PERMISSION_NETWORK_CONTROL, + TRUE, + dbus_request_scan_cb, + options ? g_variant_ref (options) : NULL); +} + +static gboolean +scanning_prohibited (NMDeviceIwd *self, gboolean periodic) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + g_return_val_if_fail (priv->dbus_obj != NULL, TRUE); + + switch (nm_device_get_state (NM_DEVICE (self))) { + case NM_DEVICE_STATE_UNKNOWN: + case NM_DEVICE_STATE_UNMANAGED: + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_PREPARE: + case NM_DEVICE_STATE_CONFIG: + case NM_DEVICE_STATE_NEED_AUTH: + case NM_DEVICE_STATE_IP_CONFIG: + case NM_DEVICE_STATE_IP_CHECK: + case NM_DEVICE_STATE_SECONDARIES: + case NM_DEVICE_STATE_DEACTIVATING: + /* Prohibit scans when unusable or activating */ + return TRUE; + case NM_DEVICE_STATE_DISCONNECTED: + case NM_DEVICE_STATE_FAILED: + /* Can always scan when disconnected */ + return FALSE; + case NM_DEVICE_STATE_ACTIVATED: + break; + } + + /* Prohibit scans if IWD is busy */ + return !priv->can_scan; +} + +static void +wifi_secrets_cb (NMActRequest *req, + NMActRequestGetSecretsCallId *call_id, + NMSettingsConnection *s_connection, + GError *error, + gpointer user_data) +{ + NMDevice *device = user_data; + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv; + NMSettingWirelessSecurity *s_wireless_sec; + const gchar *psk; + + g_return_if_fail (NM_IS_DEVICE_IWD (self)); + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + + priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + g_return_if_fail (priv->wifi_secrets_id == call_id); + + priv->wifi_secrets_id = NULL; + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + g_return_if_fail (priv->secrets_request); + g_return_if_fail (req == nm_device_get_act_request (device)); + g_return_if_fail (nm_act_request_get_settings_connection (req) == s_connection); + + if (nm_device_get_state (device) != NM_DEVICE_STATE_NEED_AUTH) + goto secrets_error; + + if (error) { + _LOGW (LOGD_WIFI, "%s", error->message); + goto secrets_error; + } + + s_wireless_sec = nm_connection_get_setting_wireless_security (nm_act_request_get_applied_connection (req)); + if (!s_wireless_sec) + goto secrets_error; + + psk = nm_setting_wireless_security_get_psk (s_wireless_sec); + if (!psk) + goto secrets_error; + + _LOGD (LOGD_DEVICE | LOGD_WIFI, + "Returning a new PSK to the IWD Agent"); + + g_dbus_method_invocation_return_value (priv->secrets_request, + g_variant_new ("(s)", psk)); + priv->secrets_request = NULL; + + /* Change state back to what it was before NEED_AUTH */ + nm_device_state_changed (device, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE); + return; + +secrets_error: + if (priv->secrets_request) { + g_dbus_method_invocation_return_error_literal (priv->secrets_request, NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "NM secrets request failed"); + priv->secrets_request = NULL; + } + + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + + cleanup_association_attempt (self, TRUE); +} + +static void +wifi_secrets_get_secrets (NMDeviceIwd *self, + const char *setting_name, + NMSecretAgentGetSecretsFlags flags) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMActRequest *req; + + wifi_secrets_cancel (self); + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + + priv->wifi_secrets_id = nm_act_request_get_secrets (req, + TRUE, + setting_name, + flags, + NULL, + wifi_secrets_cb, + self); +} + +static void +network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDevice *device = NM_DEVICE (self); + gs_free_error GError *error = NULL; + NMConnection *connection; + NMSettingWireless *s_wifi; + GBytes *ssid; + gs_free gchar *str_ssid = NULL; + + if (!_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, + G_VARIANT_TYPE ("()"), + &error)) { + gs_free gchar *dbus_error = NULL; + + /* Connection failed; radio problems or if the network wasn't + * open, the passwords or certificates may be wrong. + */ + + _LOGE (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) Network.Connect failed: %s", + error->message); + + connection = nm_device_get_applied_connection (device); + if (!connection) + goto failed; + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR)) + dbus_error = g_dbus_error_get_remote_error (error); + + /* If secrets were wrong, we'd be getting a net.connman.iwd.Failed */ + if (nm_streq0 (dbus_error, "net.connman.iwd.Failed")) { + nm_connection_clear_secrets (connection); + + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + } else if ( !nm_utils_error_is_cancelled (error, TRUE) + && nm_device_is_activating (device)) + goto failed; + + /* Call Disconnect to make sure IWD's autoconnect is disabled */ + cleanup_association_attempt (self, TRUE); + + return; + } + + nm_assert (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG); + + connection = nm_device_get_applied_connection (device); + if (!connection) + goto failed; + + s_wifi = nm_connection_get_setting_wireless (connection); + if (!s_wifi) + goto failed; + + ssid = nm_setting_wireless_get_ssid (s_wifi); + if (!ssid) + goto failed; + + str_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), + g_bytes_get_size (ssid)); + + _LOGI (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) Stage 2 of 5 (Device Configure) successful. Connected to '%s'.", + str_ssid); + nm_device_activate_schedule_stage3_ip_config_start (device); + + nm_iwd_manager_network_connected (nm_iwd_manager_get (), str_ssid, + get_connection_iwd_security (connection)); + + return; + +failed: + cleanup_association_attempt (self, FALSE); + nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); +} + +static void +set_powered (NMDeviceIwd *self, gboolean powered) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + g_dbus_proxy_call (priv->dbus_proxy, + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE, + "Powered", + g_variant_new ("b", powered)), + G_DBUS_CALL_FLAGS_NONE, 2000, + NULL, NULL, NULL); +} + +/*****************************************************************************/ + +static NMActStageReturn +act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) +{ + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMActStageReturn ret; + NMWifiAP *ap = NULL; + NMActRequest *req; + NMConnection *connection; + NMSettingWireless *s_wireless; + const char *ap_path; + + ret = NM_DEVICE_CLASS (nm_device_iwd_parent_class)->act_stage1_prepare (device, out_failure_reason); + if (ret != NM_ACT_STAGE_RETURN_SUCCESS) + return ret; + + 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); + + s_wireless = nm_connection_get_setting_wireless (connection); + g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE); + + ap_path = nm_active_connection_get_specific_object (NM_ACTIVE_CONNECTION (req)); + ap = ap_path ? nm_wifi_ap_lookup_for_device (NM_DEVICE (self), ap_path) : NULL; + if (!ap) { + ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); + + /* TODO: assuming hidden networks aren't supported do we need + * to consider the case of APs that are not in the scan list + * yet, for which nm-device-wifi.c creates the temporary fake + * AP object? + */ + + nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), + nm_dbus_object_get_path (NM_DBUS_OBJECT (ap))); + } + + set_current_ap (self, ap, FALSE); + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static NMActStageReturn +act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) +{ + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + NMActRequest *req; + NMWifiAP *ap; + NMConnection *connection; + GError *error = NULL; + GDBusProxy *network_proxy; + + req = nm_device_get_act_request (device); + g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); + + ap = priv->current_ap; + if (!ap) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + goto out; + } + + connection = nm_act_request_get_applied_connection (req); + g_assert (connection); + + /* 802.1x networks that are not IWD Known Networks will definitely + * fail, for other combinations we will let the Connect call fail + * or ask us for any missing secrets through the Agent. + */ + if ( !is_connection_known_network (connection) + && nm_connection_get_setting_802_1x (connection)) { + _LOGI (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) access point '%s' has 802.1x security, but is not configured.", + nm_connection_get_id (connection)); + + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); + ret = NM_ACT_STAGE_RETURN_FAILURE; + goto out; + } + + /* Locate the IWD Network object */ + network_proxy = g_dbus_proxy_new_for_bus_sync (NM_IWD_BUS_TYPE, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + NM_IWD_SERVICE, + nm_wifi_ap_get_supplicant_path (ap), + NM_IWD_NETWORK_INTERFACE, + NULL, &error); + if (!network_proxy) { + _LOGE (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) could not get Network interface proxy for %s: %s", + nm_wifi_ap_get_supplicant_path (ap), + error->message); + g_clear_error (&error); + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + goto out; + } + + if (!priv->cancellable) + priv->cancellable = g_cancellable_new (); + + /* Call Network.Connect. No timeout because IWD already handles + * timeouts. + */ + g_dbus_proxy_call (network_proxy, "Connect", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, G_MAXINT, + priv->cancellable, network_connect_cb, self); + + g_object_unref (network_proxy); + + /* We'll get stage3 started when the supplicant connects */ + ret = NM_ACT_STAGE_RETURN_POSTPONE; + +out: + if (ret == NM_ACT_STAGE_RETURN_FAILURE) + cleanup_association_attempt (self, FALSE); + + return ret; +} + +static guint32 +get_configured_mtu (NMDevice *device, gboolean *out_is_user_config) +{ + NMSettingWireless *setting; + gint64 mtu_default; + guint32 mtu; + + nm_assert (NM_IS_DEVICE (device)); + nm_assert (out_is_user_config); + + setting = NM_SETTING_WIRELESS (nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS)); + if (!setting) + g_return_val_if_reached (0); + + mtu = nm_setting_wireless_get_mtu (setting); + if (mtu == 0) { + mtu_default = nm_device_get_configured_mtu_from_connection_default (device, "wifi.mtu"); + if (mtu_default >= 0) { + *out_is_user_config = TRUE; + return (guint32) mtu_default; + } + } + *out_is_user_config = (mtu != 0); + return mtu; +} + +static gboolean +periodic_scan_timeout_cb (gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + priv->periodic_scan_id = 0; + + if (priv->scanning || priv->scan_requested) + return FALSE; + + g_dbus_proxy_call (priv->dbus_proxy, "Scan", g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, -1, + priv->cancellable, scan_cb, self); + priv->scan_requested = TRUE; + + return FALSE; +} + +static void +schedule_periodic_scan (NMDeviceIwd *self, NMDeviceState current_state) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + guint interval; + + if (current_state <= NM_DEVICE_STATE_UNAVAILABLE) + return; + + if (current_state == NM_DEVICE_STATE_DISCONNECTED) + interval = 10; + else + interval = 20; + + nm_clear_g_source (&priv->periodic_scan_id); + priv->periodic_scan_id = g_timeout_add_seconds (interval, + periodic_scan_timeout_cb, + self); +} + +static void +device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason) +{ + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) { + remove_all_aps (self); + nm_clear_g_source (&priv->periodic_scan_id); + } else if (old_state <= NM_DEVICE_STATE_UNAVAILABLE) { + update_aps (self); + schedule_periodic_scan (self, new_state); + } + + switch (new_state) { + case NM_DEVICE_STATE_UNMANAGED: + break; + case NM_DEVICE_STATE_UNAVAILABLE: + /* + * If the device is enabled and the IWD manager is ready, + * transition to DISCONNECTED because the device is now + * ready to use. + */ + if (priv->enabled && priv->dbus_obj) { + nm_device_queue_recheck_available (device, + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + } + break; + case NM_DEVICE_STATE_NEED_AUTH: + break; + case NM_DEVICE_STATE_IP_CHECK: + break; + case NM_DEVICE_STATE_ACTIVATED: + break; + case NM_DEVICE_STATE_FAILED: + break; + case NM_DEVICE_STATE_DISCONNECTED: + break; + default: + break; + } +} + +static gboolean +get_enabled (NMDevice *device) +{ + return NM_DEVICE_IWD_GET_PRIVATE ((NMDeviceIwd *) device)->enabled; +} + +static void +set_enabled (NMDevice *device, gboolean enabled) +{ + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDeviceState state; + + enabled = !!enabled; + + if (priv->enabled == enabled) + return; + + priv->enabled = enabled; + + _LOGD (LOGD_WIFI, "device now %s", enabled ? "enabled" : "disabled"); + + state = nm_device_get_state (device); + if (state < NM_DEVICE_STATE_UNAVAILABLE) { + _LOGD (LOGD_WIFI, "(%s): device blocked by UNMANAGED state", + enabled ? "enable" : "disable"); + return; + } + + if (priv->dbus_proxy) + set_powered (self, enabled); + + if (enabled) { + if (state != NM_DEVICE_STATE_UNAVAILABLE) + _LOGW (LOGD_CORE, "not in expected unavailable state!"); + + if (priv->dbus_obj) + nm_device_queue_recheck_available (device, + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + } else { + nm_device_state_changed (device, + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_NONE); + } +} + +static gboolean +can_reapply_change (NMDevice *device, + const char *setting_name, + NMSetting *s_old, + NMSetting *s_new, + GHashTable *diffs, + GError **error) +{ + NMDeviceClass *device_class; + + /* Only handle wireless setting here, delegate other settings to parent class */ + if (nm_streq (setting_name, NM_SETTING_WIRELESS_SETTING_NAME)) { + return nm_device_hash_check_invalid_keys (diffs, + NM_SETTING_WIRELESS_SETTING_NAME, + error, + NM_SETTING_WIRELESS_MTU); /* reapplied with IP config */ + } + + device_class = NM_DEVICE_CLASS (nm_device_iwd_parent_class); + return device_class->can_reapply_change (device, + setting_name, + s_old, + s_new, + diffs, + error); +} + +/*****************************************************************************/ + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDeviceIwd *self = NM_DEVICE_IWD (object); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + const char **list; + + switch (prop_id) { + case PROP_MODE: + if (priv->current_ap) + g_value_set_uint (value, NM_802_11_MODE_INFRA); + else + g_value_set_uint (value, NM_802_11_MODE_UNKNOWN); + break; + case PROP_BITRATE: + g_value_set_uint (value, 65000); + break; + case PROP_CAPABILITIES: + g_value_set_uint (value, priv->capabilities); + break; + case PROP_ACCESS_POINTS: + list = nm_wifi_aps_get_paths (&priv->aps_lst_head, TRUE); + g_value_take_boxed (value, nm_utils_strv_make_deep_copied (list)); + break; + case PROP_ACTIVE_ACCESS_POINT: + nm_dbus_utils_g_value_set_object_path (value, priv->current_ap); + break; + case PROP_SCANNING: + g_value_set_boolean (value, priv->scanning); + 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) +{ + NMDeviceIwd *device = NM_DEVICE_IWD (object); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (device); + + switch (prop_id) { + case PROP_CAPABILITIES: + /* construct-only */ + priv->capabilities = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +state_changed (NMDeviceIwd *self, const gchar *new_state) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + NMDeviceState dev_state = nm_device_get_state (device); + gboolean iwd_connection = FALSE; + gboolean can_connect; + + _LOGI (LOGD_DEVICE | LOGD_WIFI, "new IWD device state is %s", new_state); + + if ( dev_state >= NM_DEVICE_STATE_CONFIG + && dev_state <= NM_DEVICE_STATE_ACTIVATED) + iwd_connection = TRUE; + + /* Don't allow scanning while connecting, disconnecting or roaming */ + priv->can_scan = NM_IN_STRSET (new_state, "connected", "disconnected"); + + /* Don't allow new connection until iwd exits disconnecting */ + can_connect = NM_IN_STRSET (new_state, "disconnected"); + if (can_connect != priv->can_connect) { + priv->can_connect = can_connect; + nm_device_emit_recheck_auto_activate (device); + } + + if (NM_IN_STRSET (new_state, "connecting", "connected", "roaming")) { + /* If we were connecting, do nothing, the confirmation of + * a connection success is handled in the Device.Connect + * method return callback. Otherwise IWD must have connected + * without Network Manager's will so for simplicity force a + * disconnect. + */ + if (iwd_connection) + return; + + _LOGW (LOGD_DEVICE | LOGD_WIFI, + "Unsolicited connection success, asking IWD to disconnect"); + send_disconnect (self); + + return; + } else if (NM_IN_STRSET (new_state, "disconnecting", "disconnected")) { + if (!iwd_connection) + return; + + /* Call Disconnect on the IWD device object to make sure it + * disables its own autoconnect. + * + * Note we could instead call net.connman.iwd.KnownNetworks.ForgetNetwork + * and leave the device in autoconnect. This way if NetworkManager + * changes any settings for this connection, they'd be taken into + * account on the next connection attempt. But both methods are + * a hack, we'll perhaps need an IWD API to "connect once" without + * storing anything. + */ + send_disconnect (self); + + /* + * If IWD is still handling the Connect call, let our callback + * for the dbus method handle the failure. + */ + if (dev_state == NM_DEVICE_STATE_CONFIG) + return; + + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + + return; + } + + _LOGE (LOGD_WIFI, "State %s unknown", new_state); +} + +static void +scanning_changed (NMDeviceIwd *self, gboolean new_scanning) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); + + if (new_scanning == priv->scanning) + return; + + priv->scanning = new_scanning; + + _notify (self, PROP_SCANNING); + + if (!priv->scanning) { + update_aps (self); + + if (!priv->scan_requested) + schedule_periodic_scan (self, state); + } +} + +static void +powered_changed (NMDeviceIwd *self, gboolean new_powered) +{ + nm_device_queue_recheck_available (NM_DEVICE (self), + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); +} + +static void +properties_changed (GDBusProxy *proxy, GVariant *changed_properties, + GStrv invalidate_properties, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + GVariantIter *iter; + const gchar *key; + GVariant *value; + + g_variant_get (changed_properties, "a{sv}", &iter); + while (g_variant_iter_next (iter, "{&sv}", &key, &value)) { + if (!strcmp (key, "State")) + state_changed (self, g_variant_get_string (value, NULL)); + + if (!strcmp (key, "Scanning")) + scanning_changed (self, g_variant_get_boolean (value)); + + if (!strcmp (key, "Powered")) + powered_changed (self, g_variant_get_boolean (value)); + + g_variant_unref (value); + } + + g_variant_iter_free (iter); +} + +void +nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + GDBusInterface *interface; + GVariant *value; + + if (!nm_g_object_ref_set ((GObject **) &priv->dbus_obj, (GObject *) object)) + return; + + if (priv->dbus_proxy) { + g_signal_handlers_disconnect_by_func (priv->dbus_proxy, + properties_changed, self); + + g_clear_object (&priv->dbus_proxy); + } + + if (priv->enabled) + nm_device_queue_recheck_available (NM_DEVICE (self), + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + + if (!object) { + priv->can_scan = FALSE; + + cleanup_association_attempt (self, FALSE); + return; + } + + interface = g_dbus_object_get_interface (object, NM_IWD_DEVICE_INTERFACE); + priv->dbus_proxy = G_DBUS_PROXY (interface); + + value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "Scanning"); + priv->scanning = g_variant_get_boolean (value); + g_variant_unref (value); + priv->scan_requested = FALSE; + + value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "State"); + state_changed (self, g_variant_get_string (value, NULL)); + g_variant_unref (value); + + g_signal_connect (priv->dbus_proxy, "g-properties-changed", + G_CALLBACK (properties_changed), self); + + set_powered (self, priv->enabled); + + /* Call Disconnect to make sure IWD's autoconnect is disabled. + * Autoconnect is the default state after device is brought UP. + */ + if (priv->enabled) + send_disconnect (self); +} + +gboolean +nm_device_iwd_agent_psk_query (NMDeviceIwd *self, + GDBusMethodInvocation *invocation) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMActRequest *req; + NMSettingWirelessSecurity *s_wireless_sec; + const gchar *psk; + + req = nm_device_get_act_request (NM_DEVICE (self)); + if (!req) + return FALSE; + + s_wireless_sec = nm_connection_get_setting_wireless_security (nm_act_request_get_applied_connection (req)); + if (!s_wireless_sec) + return FALSE; + + psk = nm_setting_wireless_security_get_psk (s_wireless_sec); + if (psk) { + _LOGD (LOGD_DEVICE | LOGD_WIFI, + "Returning the PSK to the IWD Agent"); + + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", psk)); + return TRUE; + } + + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, + NM_DEVICE_STATE_REASON_NO_SECRETS); + wifi_secrets_get_secrets (self, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION + | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW); + + priv->secrets_request = invocation; + return TRUE; +} + +/*****************************************************************************/ + +static const char * +get_type_description (NMDevice *device) +{ + nm_assert (NM_IS_DEVICE_IWD (device)); + + return "wifi"; +} + +/*****************************************************************************/ + +static void +nm_device_iwd_init (NMDeviceIwd *self) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + c_list_init (&priv->aps_lst_head); + + /* Make sure the manager is running */ + (void) nm_iwd_manager_get (); +} + +NMDevice * +nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities) +{ + return g_object_new (NM_TYPE_DEVICE_IWD, + NM_DEVICE_IFACE, iface, + NM_DEVICE_TYPE_DESC, "802.11 WiFi", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI, + NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI, + NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN, + NM_DEVICE_IWD_CAPABILITIES, (guint) capabilities, + NULL); +} + +static void +dispose (GObject *object) +{ + NMDeviceIwd *self = NM_DEVICE_IWD (object); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + nm_clear_g_cancellable (&priv->cancellable); + + nm_clear_g_source (&priv->periodic_scan_id); + + cleanup_association_attempt (self, TRUE); + + g_clear_object (&priv->dbus_proxy); + g_clear_object (&priv->dbus_obj); + + remove_all_aps (self); + + G_OBJECT_CLASS (nm_device_iwd_parent_class)->dispose (object); + + nm_assert (c_list_is_empty (&priv->aps_lst_head)); +} + +static void +nm_device_iwd_class_init (NMDeviceIwdClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); + NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); + + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_WIRELESS_SETTING_NAME, NM_LINK_TYPE_WIFI) + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&nm_interface_info_device_wireless); + + parent_class->can_auto_connect = can_auto_connect; + parent_class->is_available = is_available; + parent_class->get_autoconnect_allowed = get_autoconnect_allowed; + parent_class->check_connection_compatible = check_connection_compatible; + parent_class->check_connection_available = check_connection_available; + parent_class->complete_connection = complete_connection; + parent_class->get_enabled = get_enabled; + parent_class->set_enabled = set_enabled; + parent_class->get_type_description = get_type_description; + + parent_class->act_stage1_prepare = act_stage1_prepare; + parent_class->act_stage2_config = act_stage2_config; + parent_class->get_configured_mtu = get_configured_mtu; + parent_class->deactivate = deactivate; + parent_class->deactivate_async = deactivate_async; + parent_class->deactivate_async_finish = deactivate_async_finish; + parent_class->can_reapply_change = can_reapply_change; + + parent_class->state_changed = device_state_changed; + + klass->scanning_prohibited = scanning_prohibited; + + obj_properties[PROP_MODE] = + g_param_spec_uint (NM_DEVICE_IWD_MODE, "", "", + NM_802_11_MODE_UNKNOWN, + NM_802_11_MODE_AP, + NM_802_11_MODE_INFRA, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_BITRATE] = + g_param_spec_uint (NM_DEVICE_IWD_BITRATE, "", "", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_ACCESS_POINTS] = + g_param_spec_boxed (NM_DEVICE_IWD_ACCESS_POINTS, "", "", + G_TYPE_STRV, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_ACTIVE_ACCESS_POINT] = + g_param_spec_string (NM_DEVICE_IWD_ACTIVE_ACCESS_POINT, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_CAPABILITIES] = + g_param_spec_uint (NM_DEVICE_IWD_CAPABILITIES, "", "", + 0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_SCANNING] = + g_param_spec_boolean (NM_DEVICE_IWD_SCANNING, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + signals[SCANNING_PROHIBITED] = + g_signal_new (NM_DEVICE_IWD_SCANNING_PROHIBITED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMDeviceIwdClass, scanning_prohibited), + NULL, NULL, NULL, + G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN); +} diff --git a/src/devices/wifi/nm-device-iwd.h b/src/devices/wifi/nm-device-iwd.h new file mode 100644 index 00000000..699ba1bb --- /dev/null +++ b/src/devices/wifi/nm-device-iwd.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2017 Intel Corporation + */ + +#ifndef __NETWORKMANAGER_DEVICE_IWD_H__ +#define __NETWORKMANAGER_DEVICE_IWD_H__ + +#include "devices/nm-device.h" +#include "nm-wifi-ap.h" +#include "nm-device-wifi.h" + +#define NM_TYPE_DEVICE_IWD (nm_device_iwd_get_type ()) +#define NM_DEVICE_IWD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_IWD, NMDeviceIwd)) +#define NM_DEVICE_IWD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_IWD, NMDeviceIwdClass)) +#define NM_IS_DEVICE_IWD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_IWD)) +#define NM_IS_DEVICE_IWD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_IWD)) +#define NM_DEVICE_IWD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_IWD, NMDeviceIwdClass)) + +#define NM_DEVICE_IWD_MODE NM_DEVICE_WIFI_MODE +#define NM_DEVICE_IWD_BITRATE NM_DEVICE_WIFI_BITRATE +#define NM_DEVICE_IWD_ACCESS_POINTS NM_DEVICE_WIFI_ACCESS_POINTS +#define NM_DEVICE_IWD_ACTIVE_ACCESS_POINT NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT +#define NM_DEVICE_IWD_CAPABILITIES NM_DEVICE_WIFI_CAPABILITIES +#define NM_DEVICE_IWD_SCANNING NM_DEVICE_WIFI_SCANNING + +#define NM_DEVICE_IWD_SCANNING_PROHIBITED NM_DEVICE_WIFI_SCANNING_PROHIBITED + +typedef struct _NMDeviceIwd NMDeviceIwd; +typedef struct _NMDeviceIwdClass NMDeviceIwdClass; + +GType nm_device_iwd_get_type (void); + +NMDevice *nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities); + +void nm_device_iwd_set_dbus_object (NMDeviceIwd *device, GDBusObject *object); + +gboolean nm_device_iwd_agent_psk_query (NMDeviceIwd *device, + GDBusMethodInvocation *invocation); + +const CList *_nm_device_iwd_get_aps (NMDeviceIwd *self); + +void _nm_device_iwd_request_scan (NMDeviceIwd *self, + GVariant *options, + GDBusMethodInvocation *invocation); + +#endif /* __NETWORKMANAGER_DEVICE_IWD_H__ */ diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c index 3a4a027a..fd7bf3f7 100644 --- a/src/devices/wifi/nm-device-olpc-mesh.c +++ b/src/devices/wifi/nm-device-olpc-mesh.c @@ -48,11 +48,6 @@ #include "nm-manager.h" #include "platform/nm-platform.h" -/* This is a bug; but we can't really change API now... */ -#include "nm-vpn-dbus-interface.h" - -#include "introspection/org.freedesktop.NetworkManager.Device.OlpcMesh.h" - #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceOlpcMesh); @@ -118,7 +113,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMSettingOlpcMesh *s_mesh; @@ -182,7 +177,6 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) nm_device_get_iface (priv->companion)); } - /* wait with continuing configuration untill the companion device is done scanning */ g_object_get (priv->companion, NM_DEVICE_WIFI_SCANNING, &scanning, NULL); if (scanning) { @@ -392,7 +386,8 @@ static void find_companion (NMDeviceOlpcMesh *self) { NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); - const GSList *list; + const CList *tmp_lst; + NMDevice *candidate; if (priv->companion) return; @@ -400,8 +395,8 @@ find_companion (NMDeviceOlpcMesh *self) nm_device_add_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_COMPANION, TRUE); /* Try to find the companion if it's already known to the NMManager */ - for (list = nm_manager_get_devices (priv->manager); list ; list = g_slist_next (list)) { - if (check_companion (self, NM_DEVICE (list->data))) { + nm_manager_for_each_device (priv->manager, candidate, tmp_lst) { + if (check_companion (self, candidate)) { nm_device_queue_recheck_available (NM_DEVICE (self), NM_DEVICE_STATE_REASON_NONE, NM_DEVICE_STATE_REASON_NONE); @@ -440,7 +435,7 @@ get_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_COMPANION: - nm_utils_g_value_set_object_path (value, priv->companion); + nm_dbus_utils_g_value_set_object_path (value, priv->companion); break; case PROP_ACTIVE_CHANNEL: g_value_set_uint (value, nm_platform_mesh_get_channel (nm_device_get_platform (device), nm_device_get_ifindex (device))); @@ -500,10 +495,26 @@ dispose (GObject *object) G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object); } +static const NMDBusInterfaceInfoExtended interface_info_device_olpc_mesh = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_OLPC_MESH, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Companion", "o", NM_DEVICE_OLPC_MESH_COMPANION), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ActiveChannel", "u", NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_OLPC_MESH_SETTING_NAME, NM_LINK_TYPE_OLPC_MESH) @@ -512,6 +523,8 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass) object_class->get_property = get_property; object_class->dispose = dispose; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_olpc_mesh); + parent_class->check_connection_compatible = check_connection_compatible; parent_class->get_autoconnect_allowed = get_autoconnect_allowed; parent_class->complete_connection = complete_connection; @@ -534,9 +547,5 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_OLPC_MESH_SKELETON, - NULL); } diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index bf021095..90f94557 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -28,9 +28,11 @@ #include #include +#include "nm-wifi-ap.h" #include "nm-common-macros.h" #include "devices/nm-device.h" #include "devices/nm-device-private.h" +#include "nm-dbus-manager.h" #include "nm-utils.h" #include "NetworkManagerUtils.h" #include "nm-act-request.h" @@ -48,11 +50,11 @@ #include "nm-auth-utils.h" #include "settings/nm-settings-connection.h" #include "settings/nm-settings.h" +#include "nm-wifi-utils.h" +#include "nm-wifi-common.h" #include "nm-core-internal.h" #include "nm-config.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Wireless.h" - #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceWifi); @@ -75,8 +77,6 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWifi, ); enum { - ACCESS_POINT_ADDED, - ACCESS_POINT_REMOVED, SCANNING_PROHIBITED, LAST_SIGNAL @@ -87,7 +87,8 @@ static guint signals[LAST_SIGNAL] = { 0 }; typedef struct { gint8 invalid_strength_counter; - GHashTable * aps; + CList aps_lst_head; + NMWifiAP * current_ap; guint32 rate; bool enabled:1; /* rfkilled or not */ @@ -187,7 +188,7 @@ static void request_wireless_scan (NMDeviceWifi *self, const GPtrArray *ssids); static void ap_add_remove (NMDeviceWifi *self, - guint signum, + gboolean is_adding, NMWifiAP *ap, gboolean recheck_available_connections); @@ -345,30 +346,6 @@ supplicant_interface_release (NMDeviceWifi *self) _notify_scanning (self); } -static NMWifiAP * -get_ap_by_path (NMDeviceWifi *self, const char *path) -{ - g_return_val_if_fail (path != NULL, NULL); - return g_hash_table_lookup (NM_DEVICE_WIFI_GET_PRIVATE (self)->aps, path); - -} - -static NMWifiAP * -get_ap_by_supplicant_path (NMDeviceWifi *self, const char *path) -{ - GHashTableIter iter; - NMWifiAP *ap; - - g_return_val_if_fail (path != NULL, NULL); - - g_hash_table_iter_init (&iter, NM_DEVICE_WIFI_GET_PRIVATE (self)->aps); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) { - if (g_strcmp0 (path, nm_wifi_ap_get_supplicant_path (ap)) == 0) - return ap; - } - return NULL; -} - static void update_seen_bssids_cache (NMDeviceWifi *self, NMWifiAP *ap) { @@ -415,7 +392,7 @@ set_current_ap (NMDeviceWifi *self, NMWifiAP *new_ap, gboolean recheck_available /* Remove any AP from the internal list if it was created by NM or isn't known to the supplicant */ if (mode == NM_802_11_MODE_ADHOC || mode == NM_802_11_MODE_AP || nm_wifi_ap_get_fake (old_ap)) - ap_add_remove (self, ACCESS_POINT_REMOVED, old_ap, recheck_available_connections); + ap_add_remove (self, FALSE, old_ap, recheck_available_connections); g_object_unref (old_ap); } @@ -482,32 +459,32 @@ periodic_update_cb (gpointer user_data) static void ap_add_remove (NMDeviceWifi *self, - guint signum, + gboolean is_adding, /* or else removing */ NMWifiAP *ap, gboolean recheck_available_connections) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - nm_assert (NM_IN_SET (signum, ACCESS_POINT_ADDED, ACCESS_POINT_REMOVED)); - - if (signum == ACCESS_POINT_ADDED) { - g_hash_table_insert (priv->aps, - (gpointer) nm_exported_object_export ((NMExportedObject *) ap), - g_object_ref (ap)); + if (is_adding) { + g_object_ref (ap); + ap->wifi_device = NM_DEVICE (self); + c_list_link_tail (&priv->aps_lst_head, &ap->aps_lst); + nm_dbus_object_export (NM_DBUS_OBJECT (ap)); _ap_dump (self, LOGL_DEBUG, ap, "added", 0); - } else + nm_device_wifi_emit_signal_access_point (NM_DEVICE (self), ap, TRUE); + } else { + ap->wifi_device = NULL; + c_list_unlink (&ap->aps_lst); _ap_dump (self, LOGL_DEBUG, ap, "removed", 0); - - g_signal_emit (self, signals[signum], 0, ap); - - if (signum == ACCESS_POINT_REMOVED) { - g_hash_table_remove (priv->aps, nm_exported_object_get_path ((NMExportedObject *) ap)); - nm_exported_object_unexport ((NMExportedObject *) ap); - g_object_unref (ap); } _notify (self, PROP_ACCESS_POINTS); + if (!is_adding) { + nm_device_wifi_emit_signal_access_point (NM_DEVICE (self), ap, FALSE); + nm_dbus_object_clear_and_unexport (&ap); + } + nm_device_emit_recheck_auto_activate (NM_DEVICE (self)); if (recheck_available_connections) nm_device_recheck_available_connections (NM_DEVICE (self)); @@ -517,20 +494,15 @@ static void remove_all_aps (NMDeviceWifi *self) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - GHashTableIter iter; NMWifiAP *ap; - if (!g_hash_table_size (priv->aps)) + if (c_list_is_empty (&priv->aps_lst_head)) return; set_current_ap (self, NULL, FALSE); -again: - g_hash_table_iter_init (&iter, priv->aps); - if (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) { - ap_add_remove (self, ACCESS_POINT_REMOVED, ap, FALSE); - goto again; - } + while ((ap = c_list_first_entry (&priv->aps_lst_head, NMWifiAP, aps_lst))) + ap_add_remove (self, FALSE, ap, FALSE); nm_device_recheck_available_connections (NM_DEVICE (self)); } @@ -680,35 +652,14 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) return TRUE; } -static NMWifiAP * -find_first_compatible_ap (NMDeviceWifi *self, - NMConnection *connection, - gboolean allow_unstable_order) -{ - GHashTableIter iter; - NMWifiAP *ap; - NMWifiAP *cand_ap = NULL; - - g_return_val_if_fail (connection != NULL, NULL); - - g_hash_table_iter_init (&iter, NM_DEVICE_WIFI_GET_PRIVATE (self)->aps); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) { - if (!nm_wifi_ap_check_compatible (ap, connection)) - continue; - if (allow_unstable_order) - return ap; - if (!cand_ap || (nm_wifi_ap_get_id (cand_ap) < nm_wifi_ap_get_id (ap))) - cand_ap = ap; - } - return cand_ap; -} - static gboolean check_connection_available (NMDevice *device, NMConnection *connection, NMDeviceCheckConAvailableFlags flags, const char *specific_object) { + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMSettingWireless *s_wifi; const char *mode; @@ -721,7 +672,7 @@ check_connection_available (NMDevice *device, if (specific_object) { NMWifiAP *ap; - ap = get_ap_by_path (NM_DEVICE_WIFI (device), specific_object); + ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object); return ap ? nm_wifi_ap_check_compatible (ap, connection) : FALSE; } @@ -745,50 +696,18 @@ check_connection_available (NMDevice *device, return TRUE; /* check at least one AP is compatible with this connection */ - return !!find_first_compatible_ap (NM_DEVICE_WIFI (device), connection, TRUE); -} - -static gboolean -is_manf_default_ssid (const GByteArray *ssid) -{ - int i; - /* - * List of manufacturer default SSIDs that are often unchanged by users. - * - * NOTE: this list should *not* contain networks that you would like to - * automatically roam to like "Starbucks" or "AT&T" or "T-Mobile HotSpot". - */ - static const char *manf_defaults[] = { - "linksys", - "linksys-a", - "linksys-g", - "default", - "belkin54g", - "NETGEAR", - "o2DSL", - "WLAN", - "ALICE-WLAN", - "Speedport W 501V", - "TURBONETT", - }; - - for (i = 0; i < G_N_ELEMENTS (manf_defaults); i++) { - if (ssid->len == strlen (manf_defaults[i])) { - if (memcmp (manf_defaults[i], ssid->data, ssid->len) == 0) - return TRUE; - } - } - return FALSE; + return !!nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); } static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMSettingWireless *s_wifi; const char *setting_mac; char *str_ssid = NULL; @@ -825,7 +744,7 @@ complete_connection (NMDevice *device, if (!nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) { /* Find a compatible AP in the scan list */ - ap = find_first_compatible_ap (self, connection, FALSE); + ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); /* If we still don't have an AP, then the WiFI settings needs to be * fully specified by the client. Might not be able to find an AP @@ -847,7 +766,7 @@ complete_connection (NMDevice *device, return FALSE; ap = NULL; } else { - ap = get_ap_by_path (self, specific_object); + ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object); if (!ap) { g_set_error (error, NM_DEVICE_ERROR, @@ -900,7 +819,7 @@ complete_connection (NMDevice *device, */ if (!nm_wifi_ap_complete_connection (ap, connection, - is_manf_default_ssid (ssid), + nm_wifi_utils_is_manf_default_ssid (ssid), error)) { if (tmp_ssid) g_byte_array_unref (tmp_ssid); @@ -1007,6 +926,7 @@ can_auto_connect (NMDevice *device, char **specific_object) { NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMSettingWireless *s_wifi; NMWifiAP *ap; const char *method, *mode; @@ -1038,102 +958,20 @@ can_auto_connect (NMDevice *device, return FALSE; } - ap = find_first_compatible_ap (self, connection, FALSE); + ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); if (ap) { /* All good; connection is usable */ - NM_SET_OUT (specific_object, g_strdup (nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap)))); + NM_SET_OUT (specific_object, g_strdup (nm_dbus_object_get_path (NM_DBUS_OBJECT (ap)))); return TRUE; } return FALSE; } -static int -ap_id_compare (gconstpointer p_a, gconstpointer p_b, gpointer user_data) +const CList * +_nm_device_wifi_get_aps (NMDeviceWifi *self) { - guint64 a_id = nm_wifi_ap_get_id (*((NMWifiAP **) p_a)); - guint64 b_id = nm_wifi_ap_get_id (*((NMWifiAP **) p_b)); - - return a_id < b_id ? -1 : (a_id == b_id ? 0 : 1); -} - -static NMWifiAP ** -ap_list_get_sorted (NMDeviceWifi *self, gboolean include_without_ssid) -{ - NMDeviceWifiPrivate *priv; - NMWifiAP **list; - GHashTableIter iter; - NMWifiAP *ap; - gsize i, n; - - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - n = g_hash_table_size (priv->aps); - list = g_new (NMWifiAP *, n + 1); - - i = 0; - if (n > 0) { - g_hash_table_iter_init (&iter, priv->aps); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) { - nm_assert (i < n); - if ( include_without_ssid - || nm_wifi_ap_get_ssid (ap)) - list[i++] = ap; - } - nm_assert (i <= n); - nm_assert (!include_without_ssid || i == n); - - g_qsort_with_data (list, - i, - sizeof (gpointer), - ap_id_compare, - NULL); - } - list[i] = NULL; - return list; -} - -static const char ** -ap_list_get_sorted_paths (NMDeviceWifi *self, gboolean include_without_ssid) -{ - gpointer *list; - gsize i, j; - - list = (gpointer *) ap_list_get_sorted (self, include_without_ssid); - for (i = 0, j = 0; list[i]; i++) { - NMWifiAP *ap = list[i]; - const char *path; - - /* update @list inplace to hold instead the export-path. */ - path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap)); - nm_assert (path); - list[j++] = (gpointer) path; - } - return (const char **) list; -} - -static void -impl_device_wifi_get_access_points (NMDeviceWifi *self, - GDBusMethodInvocation *context) -{ - gs_free const char **list = NULL; - GVariant *v; - - list = ap_list_get_sorted_paths (self, FALSE); - v = g_variant_new_objv (list, -1); - g_dbus_method_invocation_return_value (context, g_variant_new_tuple (&v, 1)); -} - -static void -impl_device_wifi_get_all_access_points (NMDeviceWifi *self, - GDBusMethodInvocation *context) -{ - gs_free const char **list = NULL; - GVariant *v; - - list = ap_list_get_sorted_paths (self, TRUE); - v = g_variant_new_objv (list, -1); - g_dbus_method_invocation_return_value (context, g_variant_new_tuple (&v, 1)); + return &NM_DEVICE_WIFI_GET_PRIVATE (self)->aps_lst_head; } static void @@ -1153,7 +991,7 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset) priv = NM_DEVICE_WIFI_GET_PRIVATE (self); randomize = nm_config_data_get_device_config_boolean (NM_CONFIG_GET_DATA, - "wifi.scan-rand-mac-address", + NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS, device, TRUE, TRUE); @@ -1239,7 +1077,6 @@ dbus_request_scan_cb (NMDevice *device, gpointer user_data) { NMDeviceWifi *self = NM_DEVICE_WIFI (device); - NMDeviceWifiPrivate *priv; gs_unref_variant GVariant *scan_options = user_data; gs_unref_ptrarray GPtrArray *ssids = NULL; @@ -1256,8 +1093,6 @@ dbus_request_scan_cb (NMDevice *device, return; } - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - if (scan_options) { gs_unref_variant GVariant *val = g_variant_lookup_value (scan_options, "ssids", NULL); @@ -1284,10 +1119,10 @@ dbus_request_scan_cb (NMDevice *device, g_dbus_method_invocation_return_value (context, NULL); } -static void -impl_device_wifi_request_scan (NMDeviceWifi *self, - GDBusMethodInvocation *context, - GVariant *options) +void +_nm_device_wifi_request_scan (NMDeviceWifi *self, + GVariant *options, + GDBusMethodInvocation *invocation) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMDevice *device = NM_DEVICE (self); @@ -1297,7 +1132,7 @@ impl_device_wifi_request_scan (NMDeviceWifi *self, || !priv->sup_iface || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED || nm_device_is_activating (device)) { - g_dbus_method_invocation_return_error_literal (context, + g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED, "Scanning not allowed while unavailable or activating"); @@ -1305,7 +1140,7 @@ impl_device_wifi_request_scan (NMDeviceWifi *self, } if (nm_supplicant_interface_get_scanning (priv->sup_iface)) { - g_dbus_method_invocation_return_error_literal (context, + g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED, "Scanning not allowed while already scanning"); @@ -1314,17 +1149,16 @@ impl_device_wifi_request_scan (NMDeviceWifi *self, last_scan = nm_supplicant_interface_get_last_scan_time (priv->sup_iface); if (last_scan && (nm_utils_get_monotonic_timestamp_s () - last_scan) < 10) { - g_dbus_method_invocation_return_error_literal (context, + g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED, "Scanning not allowed immediately following previous scan"); return; } - /* Ask the manager to authenticate this request for us */ g_signal_emit_by_name (device, NM_DEVICE_AUTH_REQUEST, - context, + invocation, NULL, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE, @@ -1608,17 +1442,15 @@ ap_list_dump (gpointer user_data) priv->ap_dump_id = 0; if (_LOGD_ENABLED (LOGD_WIFI_SCAN)) { - gs_free NMWifiAP **list = NULL; - gsize i; + NMWifiAP *ap; gint32 now_s = nm_utils_get_monotonic_timestamp_s (); _LOGD (LOGD_WIFI_SCAN, "APs: [now:%u last:%u next:%u]", now_s, priv->last_scan, priv->scheduled_scan_time); - list = ap_list_get_sorted (self, TRUE); - for (i = 0; list[i]; i++) - _ap_dump (self, LOGL_DEBUG, list[i], "dump", now_s); + c_list_for_each_entry (ap, &priv->aps_lst_head, aps_lst) + _ap_dump (self, LOGL_DEBUG, ap, "dump", now_s); } return G_SOURCE_REMOVE; } @@ -1689,7 +1521,7 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface, if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP) return; - found_ap = get_ap_by_supplicant_path (self, object_path); + found_ap = nm_wifi_aps_find_by_supplicant_path (&priv->aps_lst_head, object_path); if (found_ap) { if (!nm_wifi_ap_update_from_properties (found_ap, object_path, properties)) return; @@ -1721,7 +1553,7 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface, } } - ap_add_remove (self, ACCESS_POINT_ADDED, ap, TRUE); + ap_add_remove (self, TRUE, ap, TRUE); } /* Update the current AP if the supplicant notified a current BSS change @@ -1745,7 +1577,7 @@ supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface, g_return_if_fail (object_path != NULL); priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - ap = get_ap_by_supplicant_path (self, object_path); + ap = nm_wifi_aps_find_by_supplicant_path (&priv->aps_lst_head, object_path); if (!ap) return; @@ -1758,7 +1590,7 @@ supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface, if (nm_wifi_ap_set_fake (ap, TRUE)) _ap_dump (self, LOGL_DEBUG, ap, "updated", 0); } else { - ap_add_remove (self, ACCESS_POINT_REMOVED, ap, TRUE); + ap_add_remove (self, FALSE, ap, TRUE); schedule_ap_list_dump (self); } } @@ -2289,7 +2121,7 @@ supplicant_iface_notify_current_bss (NMSupplicantInterface *iface, current_bss = nm_supplicant_interface_get_current_bss (iface); if (current_bss) - new_ap = get_ap_by_supplicant_path (self, current_bss); + new_ap = nm_wifi_aps_find_by_supplicant_path (&priv->aps_lst_head, current_bss); if (new_ap != priv->current_ap) { const char *new_bssid = NULL; @@ -2315,9 +2147,9 @@ supplicant_iface_notify_current_bss (NMSupplicantInterface *iface, } _LOGD (LOGD_WIFI, "roamed from BSSID %s (%s) to %s (%s)", - old_bssid ? old_bssid : "(none)", + old_bssid ?: "(none)", old_ssid ? nm_utils_escape_ssid (old_ssid->data, old_ssid->len) : "(none)", - new_bssid ? new_bssid : "(none)", + new_bssid ?: "(none)", new_ssid ? nm_utils_escape_ssid (new_ssid->data, new_ssid->len) : "(none)"); set_current_ap (self, new_ap, TRUE); @@ -2497,6 +2329,7 @@ build_supplicant_config (NMDeviceWifi *self, NMSettingWireless *s_wireless; NMSettingWirelessSecurity *s_wireless_sec; NMSettingWirelessSecurityPmf pmf; + NMSettingWirelessSecurityFils fils; gs_free char *value = NULL; g_return_val_if_fail (priv->sup_iface, NULL); @@ -2504,7 +2337,9 @@ build_supplicant_config (NMDeviceWifi *self, s_wireless = nm_connection_get_setting_wireless (connection); g_return_val_if_fail (s_wireless != NULL, NULL); - config = nm_supplicant_config_new (); + config = nm_supplicant_config_new ( + nm_supplicant_interface_get_pmf_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES, + nm_supplicant_interface_get_fils_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES); /* Warn if AP mode may not be supported */ if ( g_strcmp0 (nm_setting_wireless_get_mode (s_wireless), NM_SETTING_WIRELESS_MODE_AP) == 0 @@ -2546,24 +2381,16 @@ build_supplicant_config (NMDeviceWifi *self, NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL); } - /* Don't try to enable PMF on non-WPA networks */ - if (!NM_IN_STRSET (nm_setting_wireless_security_get_key_mgmt (s_wireless_sec), - "wpa-eap", - "wpa-psk")) - pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE; - - /* Check if we actually support PMF */ - if (nm_supplicant_interface_get_pmf_support (priv->sup_iface) != NM_SUPPLICANT_FEATURE_YES) { - if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) { - g_set_error_literal (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, - "Supplicant does not support PMF"); - goto error; - } else if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL) { - /* To be on the safe side, assume no support if we can't determine - * capabilities. - */ - pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE; - } + /* Configure FILS (802.11ai) */ + fils = nm_setting_wireless_security_get_fils (s_wireless_sec); + if (fils == NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT) { + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "wifi-sec.fils", + NM_DEVICE (self)); + fils = _nm_utils_ascii_str_to_int64 (value, 10, + NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE, + NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED, + NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL); } s_8021x = nm_connection_get_setting_802_1x (connection); @@ -2573,6 +2400,7 @@ build_supplicant_config (NMDeviceWifi *self, con_uuid, mtu, pmf, + fils, error)) { g_prefix_error (error, "802-11-wireless-security: "); goto error; @@ -2654,16 +2482,16 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) /* AP mode never uses a specific object or existing scanned AP */ if (priv->mode != NM_802_11_MODE_AP) { ap_path = nm_active_connection_get_specific_object (NM_ACTIVE_CONNECTION (req)); - ap = ap_path ? get_ap_by_path (self, ap_path) : NULL; + ap = ap_path ? nm_wifi_ap_lookup_for_device (NM_DEVICE (self), ap_path) : NULL; if (ap) goto done; - ap = find_first_compatible_ap (self, connection, FALSE); + ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); } if (ap) { nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), - nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap))); + nm_dbus_object_get_path (NM_DBUS_OBJECT (ap))); goto done; } @@ -2680,11 +2508,11 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) nm_wifi_ap_set_address (ap, nm_device_get_hw_address (device)); g_object_freeze_notify (G_OBJECT (self)); - ap_add_remove (self, ACCESS_POINT_ADDED, ap, TRUE); + ap_add_remove (self, TRUE, ap, TRUE); g_object_thaw_notify (G_OBJECT (self)); set_current_ap (self, ap, FALSE); nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), - nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap))); + nm_dbus_object_get_path (NM_DBUS_OBJECT (ap))); return NM_ACT_STAGE_RETURN_SUCCESS; done: @@ -3005,7 +2833,6 @@ handle_ip_config_timeout (NMDeviceWifi *self, return ret; } - static NMActStageReturn act_stage4_ip4_config_timeout (NMDevice *device, NMDeviceStateReason *out_failure_reason) { @@ -3055,13 +2882,10 @@ activation_success_handler (NMDevice *device) NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); int ifindex = nm_device_get_ifindex (device); NMActRequest *req; - NMConnection *applied_connection; req = nm_device_get_act_request (device); g_assert (req); - applied_connection = nm_act_request_get_applied_connection (req); - /* Clear any critical protocol notification in the wifi stack */ nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), ifindex, FALSE); @@ -3099,7 +2923,7 @@ activation_success_handler (NMDevice *device) } nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), - nm_exported_object_get_path (NM_EXPORTED_OBJECT (priv->current_ap))); + nm_dbus_object_get_path (NM_DBUS_OBJECT (priv->current_ap))); } periodic_update (self); @@ -3277,8 +3101,7 @@ get_property (GObject *object, guint prop_id, { NMDeviceWifi *self = NM_DEVICE_WIFI (object); NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - gsize i; - char **list; + const char **list; switch (prop_id) { case PROP_MODE: @@ -3291,13 +3114,11 @@ get_property (GObject *object, guint prop_id, g_value_set_uint (value, priv->capabilities); break; case PROP_ACCESS_POINTS: - list = (char **) ap_list_get_sorted_paths (self, TRUE); - for (i = 0; list[i]; i++) - list[i] = g_strdup (list[i]); - g_value_take_boxed (value, list); + list = nm_wifi_aps_get_paths (&priv->aps_lst_head, TRUE); + g_value_take_boxed (value, nm_utils_strv_make_deep_copied (list)); break; case PROP_ACTIVE_ACCESS_POINT: - nm_utils_g_value_set_object_path (value, priv->current_ap); + nm_dbus_utils_g_value_set_object_path (value, priv->current_ap); break; case PROP_SCANNING: g_value_set_boolean (value, priv->is_scanning); @@ -3333,8 +3154,9 @@ nm_device_wifi_init (NMDeviceWifi *self) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + c_list_init (&priv->aps_lst_head); + priv->mode = NM_802_11_MODE_INFRA; - priv->aps = g_hash_table_new (nm_str_hash, g_str_equal); } static void @@ -3392,9 +3214,7 @@ finalize (GObject *object) NMDeviceWifi *self = NM_DEVICE_WIFI (object); NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - nm_assert (g_hash_table_size (priv->aps) == 0); - - g_hash_table_unref (priv->aps); + nm_assert (c_list_is_empty (&priv->aps_lst_head)); G_OBJECT_CLASS (nm_device_wifi_parent_class)->finalize (object); } @@ -3403,6 +3223,7 @@ static void nm_device_wifi_class_init (NMDeviceWifiClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_WIRELESS_SETTING_NAME, NM_LINK_TYPE_WIFI) @@ -3413,6 +3234,8 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) object_class->dispose = dispose; object_class->finalize = finalize; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&nm_interface_info_device_wireless); + parent_class->can_auto_connect = can_auto_connect; parent_class->get_autoconnect_allowed = get_autoconnect_allowed; parent_class->is_available = is_available; @@ -3479,24 +3302,6 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - signals[ACCESS_POINT_ADDED] = - g_signal_new (NM_DEVICE_WIFI_ACCESS_POINT_ADDED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 1, - NM_TYPE_WIFI_AP); - - signals[ACCESS_POINT_REMOVED] = - g_signal_new (NM_DEVICE_WIFI_ACCESS_POINT_REMOVED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 1, - NM_TYPE_WIFI_AP); - signals[SCANNING_PROHIBITED] = g_signal_new (NM_DEVICE_WIFI_SCANNING_PROHIBITED, G_OBJECT_CLASS_TYPE (object_class), @@ -3504,13 +3309,4 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_prohibited), NULL, NULL, NULL, G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DEVICE_WIFI_SKELETON, - "GetAccessPoints", impl_device_wifi_get_access_points, - "GetAllAccessPoints", impl_device_wifi_get_all_access_points, - "RequestScan", impl_device_wifi_request_scan, - NULL); } - - diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h index 09707d4f..c13b00de 100644 --- a/src/devices/wifi/nm-device-wifi.h +++ b/src/devices/wifi/nm-device-wifi.h @@ -23,7 +23,6 @@ #define __NETWORKMANAGER_DEVICE_WIFI_H__ #include "devices/nm-device.h" -#include "nm-wifi-ap.h" #define NM_TYPE_DEVICE_WIFI (nm_device_wifi_get_type ()) #define NM_DEVICE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WIFI, NMDeviceWifi)) @@ -39,11 +38,6 @@ #define NM_DEVICE_WIFI_CAPABILITIES "wireless-capabilities" #define NM_DEVICE_WIFI_SCANNING "scanning" -/* signals */ -#define NM_DEVICE_WIFI_ACCESS_POINT_ADDED "access-point-added" -#define NM_DEVICE_WIFI_ACCESS_POINT_REMOVED "access-point-removed" - -/* internal signals */ #define NM_DEVICE_WIFI_SCANNING_PROHIBITED "scanning-prohibited" typedef struct _NMDeviceWifi NMDeviceWifi; @@ -53,4 +47,10 @@ GType nm_device_wifi_get_type (void); NMDevice * nm_device_wifi_new (const char *iface, NMDeviceWifiCapabilities capabilities); +const CList *_nm_device_wifi_get_aps (NMDeviceWifi *self); + +void _nm_device_wifi_request_scan (NMDeviceWifi *self, + GVariant *options, + GDBusMethodInvocation *invocation); + #endif /* __NETWORKMANAGER_DEVICE_WIFI_H__ */ diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c new file mode 100644 index 00000000..39db3a04 --- /dev/null +++ b/src/devices/wifi/nm-iwd-manager.c @@ -0,0 +1,693 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2017 Intel Corporation + */ + +#include "nm-default.h" + +#include "nm-iwd-manager.h" + +#include +#include + +#include "nm-logging.h" +#include "nm-core-internal.h" +#include "nm-manager.h" +#include "nm-device-iwd.h" +#include "nm-utils/nm-random-utils.h" + +/*****************************************************************************/ + +typedef struct { + gchar *name; + NMIwdNetworkSecurity security; +} KnownNetworkData; + +typedef struct { + NMManager *manager; + GCancellable *cancellable; + gboolean running; + GDBusObjectManager *object_manager; + guint agent_id; + gchar *agent_path; + GSList *known_networks; +} NMIwdManagerPrivate; + +struct _NMIwdManager { + GObject parent; + NMIwdManagerPrivate _priv; +}; + +struct _NMIwdManagerClass { + GObjectClass parent; +}; + +G_DEFINE_TYPE (NMIwdManager, nm_iwd_manager, G_TYPE_OBJECT) + +#define NM_IWD_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMIwdManager, NM_IS_IWD_MANAGER) + +/*****************************************************************************/ + +#define _NMLOG_PREFIX_NAME "iwd-manager" +#define _NMLOG_DOMAIN LOGD_WIFI + +#define _NMLOG(level, ...) \ + G_STMT_START { \ + if (nm_logging_enabled (level, _NMLOG_DOMAIN)) { \ + char __prefix[32]; \ + \ + if (self) \ + g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", ""_NMLOG_PREFIX_NAME"", (self)); \ + else \ + g_strlcpy (__prefix, _NMLOG_PREFIX_NAME, sizeof (__prefix)); \ + _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \ + "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } G_STMT_END + +/*****************************************************************************/ + +static void +psk_agent_dbus_method_cb (GDBusConnection *connection, + const gchar *sender, const gchar *object_path, + const gchar *interface_name, const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + NMIwdManager *self = user_data; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + GDBusObjectManagerClient *omc = G_DBUS_OBJECT_MANAGER_CLIENT (priv->object_manager); + const gchar *network_path, *device_path, *ifname; + gs_unref_object GDBusInterface *network = NULL, *device_obj = NULL; + gs_unref_variant GVariant *value = NULL; + gint ifindex; + NMDevice *device; + + /* Be paranoid and check the sender address */ + if (!nm_streq0 (g_dbus_object_manager_client_get_name_owner (omc), sender)) + goto return_error; + + g_variant_get (parameters, "(&o)", &network_path); + + network = g_dbus_object_manager_get_interface (priv->object_manager, + network_path, + NM_IWD_NETWORK_INTERFACE); + value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (network), "Device"); + device_path = g_variant_get_string (value, NULL); + + if (!device_path) { + _LOGE ("Device not cached for network %s in IWD Agent request", + network_path); + goto return_error; + } + + device_obj = g_dbus_object_manager_get_interface (priv->object_manager, + device_path, + NM_IWD_DEVICE_INTERFACE); + g_variant_unref (value); + value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (device_obj), "Name"); + ifname = g_variant_get_string (value, NULL); + + if (!ifname) { + _LOGE ("Name not cached for device %s in IWD Agent request", + device_path); + goto return_error; + } + + ifindex = if_nametoindex (ifname); + if (!ifindex) { + _LOGE ("if_nametoindex failed for Name %s for Device at %s: %i", + ifname, device_path, errno); + goto return_error; + } + + device = nm_manager_get_device_by_ifindex (priv->manager, ifindex); + if (!NM_IS_DEVICE_IWD (device)) { + _LOGE ("IWD device named %s is not a Wifi device in IWD Agent request", + ifname); + goto return_error; + } + + if (nm_device_iwd_agent_psk_query (NM_DEVICE_IWD (device), invocation)) + return; + + _LOGE ("Device %s did not handle the IWD Agent request", ifname); + +return_error: + /* IWD doesn't look at the specific error */ + g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "No PSK available for this connection"); +} + +static guint +psk_agent_export (GDBusConnection *connection, gpointer user_data, + gchar **agent_path, GError **error) +{ + static const GDBusArgInfo request_passphrase_arg_network = { + -1, + (gchar *) "network", + (gchar *) "o", + NULL, + }; + static const GDBusArgInfo *const request_passphrase_in_args[] = { + &request_passphrase_arg_network, + NULL, + }; + static const GDBusArgInfo request_passphrase_arg_passphrase = { + -1, + (gchar *) "passphrase", + (gchar *) "s", + NULL, + }; + static const GDBusArgInfo *const request_passphrase_out_args[] = { + &request_passphrase_arg_passphrase, + NULL, + }; + static const GDBusMethodInfo request_passphrase_info = { + -1, + (gchar *) "RequestPassphrase", + (GDBusArgInfo **) &request_passphrase_in_args, + (GDBusArgInfo **) &request_passphrase_out_args, + NULL, + }; + static const GDBusMethodInfo *const method_info[] = { + &request_passphrase_info, + NULL, + }; + static GDBusInterfaceInfo interface_info = { + -1, + (gchar *) "net.connman.iwd.Agent", + (GDBusMethodInfo **) &method_info, + NULL, + NULL, + NULL, + }; + static GDBusInterfaceVTable vtable = { + psk_agent_dbus_method_cb, + NULL, + NULL, + }; + + gchar path[50]; + unsigned int rnd; + guint id; + + if (!nm_utils_random_bytes (&rnd, sizeof (rnd))) { + g_set_error_literal (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + "Can't read urandom."); + return 0; + } + + nm_sprintf_buf (path, "/agent/%u", rnd); + + id = g_dbus_connection_register_object (connection, path, + &interface_info, &vtable, + user_data, NULL, error); + + if (id) + *agent_path = g_strdup (path); + return id; +} + +static void +register_agent (NMIwdManager *self) +{ + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + GDBusInterface *agent_manager; + + agent_manager = g_dbus_object_manager_get_interface (priv->object_manager, + "/", + NM_IWD_AGENT_MANAGER_INTERFACE); + + /* Register our agent */ + g_dbus_proxy_call (G_DBUS_PROXY (agent_manager), + "RegisterAgent", + g_variant_new ("(o)", priv->agent_path), + G_DBUS_CALL_FLAGS_NONE, -1, + NULL, NULL, NULL); + + g_object_unref (agent_manager); +} + +/*****************************************************************************/ + +static void +set_device_dbus_object (NMIwdManager *self, GDBusInterface *interface, + GDBusObject *object) +{ + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + GDBusProxy *proxy; + GVariant *value; + const char *ifname; + gint ifindex; + NMDevice *device; + + if (!priv->running) + return; + + g_return_if_fail (G_IS_DBUS_PROXY (interface)); + + proxy = G_DBUS_PROXY (interface); + + if (strcmp (g_dbus_proxy_get_interface_name (proxy), + NM_IWD_DEVICE_INTERFACE)) + return; + + value = g_dbus_proxy_get_cached_property (proxy, "Name"); + if (!value) { + _LOGE ("Name not cached for Device at %s", + g_dbus_proxy_get_object_path (proxy)); + return; + } + + ifname = g_variant_get_string (value, NULL); + ifindex = if_nametoindex (ifname); + g_variant_unref (value); + + if (!ifindex) { + _LOGE ("if_nametoindex failed for Name %s for Device at %s: %i", + ifname, g_dbus_proxy_get_object_path (proxy), errno); + return; + } + + device = nm_manager_get_device_by_ifindex (priv->manager, ifindex); + if (!NM_IS_DEVICE_IWD (device)) { + _LOGE ("IWD device named %s is not a Wifi device", ifname); + return; + } + + nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), object); +} + +static void +interface_added (GDBusObjectManager *object_manager, GDBusObject *object, + GDBusInterface *interface, gpointer user_data) +{ + NMIwdManager *self = user_data; + + set_device_dbus_object (self, interface, object); +} + +static void +interface_removed (GDBusObjectManager *object_manager, GDBusObject *object, + GDBusInterface *interface, gpointer user_data) +{ + NMIwdManager *self = user_data; + + /* + * TODO: we may need to save the GDBusInterface or GDBusObject + * pointer in the hash table because we may be no longer able to + * access the Name property or map the name to ifindex with + * if_nametoindex at this point. + */ + + set_device_dbus_object (self, interface, NULL); +} + +static gboolean +_om_has_name_owner (GDBusObjectManager *object_manager) +{ + gs_free char *name_owner = NULL; + + nm_assert (G_IS_DBUS_OBJECT_MANAGER_CLIENT (object_manager)); + + name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (object_manager)); + return !!name_owner; +} + +static void +object_added (NMIwdManager *self, GDBusObject *object) +{ + GList *interfaces, *iter; + + interfaces = g_dbus_object_get_interfaces (object); + for (iter = interfaces; iter; iter = iter->next) { + GDBusInterface *interface = G_DBUS_INTERFACE (iter->data); + + set_device_dbus_object (self, interface, object); + } + + g_list_free_full (interfaces, g_object_unref); +} + +static void +known_network_free (KnownNetworkData *network) +{ + g_free (network->name); + g_free (network); +} + +static void +list_known_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMIwdManager *self = user_data; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *variant = NULL; + GVariantIter *networks, *props; + + variant = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, + G_VARIANT_TYPE ("(aa{sv})"), + &error); + if (!variant) { + _LOGE ("ListKnownNetworks() failed: %s", error->message); + return; + } + + g_slist_free_full (priv->known_networks, (GDestroyNotify) known_network_free); + priv->known_networks = NULL; + + g_variant_get (variant, "(aa{sv})", &networks); + + while (g_variant_iter_next (networks, "a{sv}", &props)) { + const gchar *key; + const gchar *name = NULL; + const gchar *type = NULL; + GVariant *val; + KnownNetworkData *network_data; + + while (g_variant_iter_next (props, "{&sv}", &key, &val)) { + if (!strcmp (key, "Name")) + name = g_variant_get_string (val, NULL); + + if (!strcmp (key, "Type")) + type = g_variant_get_string (val, NULL); + + g_variant_unref (val); + } + + if (!name || !type) + goto next; + + network_data = g_new (KnownNetworkData, 1); + network_data->name = g_strdup (name); + if (!strcmp (type, "open")) + network_data->security = NM_IWD_NETWORK_SECURITY_NONE; + else if (!strcmp (type, "psk")) + network_data->security = NM_IWD_NETWORK_SECURITY_PSK; + else if (!strcmp (type, "8021x")) + network_data->security = NM_IWD_NETWORK_SECURITY_8021X; + + priv->known_networks = g_slist_append (priv->known_networks, + network_data); + +next: + g_variant_iter_free (props); + } + + g_variant_iter_free (networks); + + /* For completness we may want to call nm_device_emit_recheck_auto_activate + * and nm_device_recheck_available_connections for all affected devices + * now but the ListKnownNetworks call should have been really fast, + * faster than any scan on any newly created devices could have happened. + */ +} + +static void +update_known_networks (NMIwdManager *self) +{ + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + GDBusInterface *known_networks_if; + + known_networks_if = g_dbus_object_manager_get_interface (priv->object_manager, + "/", + NM_IWD_KNOWN_NETWORKS_INTERFACE); + + g_dbus_proxy_call (G_DBUS_PROXY (known_networks_if), + "ListKnownNetworks", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, -1, + priv->cancellable, list_known_networks_cb, self); + + g_object_unref (known_networks_if); +} + +static void prepare_object_manager (NMIwdManager *self); + +static void +name_owner_changed (GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NMIwdManager *self = user_data; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + GDBusObjectManager *object_manager = G_DBUS_OBJECT_MANAGER (object); + + nm_assert (object_manager == priv->object_manager); + + if (_om_has_name_owner (object_manager)) { + g_signal_handlers_disconnect_by_data (object_manager, self); + g_clear_object (&priv->object_manager); + prepare_object_manager (self); + } else { + const CList *tmp_lst; + NMDevice *device; + + if (!priv->running) + return; + + priv->running = false; + + nm_manager_for_each_device (priv->manager, device, tmp_lst) { + if (NM_IS_DEVICE_IWD (device)) { + nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), + NULL); + } + } + } +} + +static void +device_added (NMManager *manager, NMDevice *device, gpointer user_data) +{ + NMIwdManager *self = user_data; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + GList *objects, *iter; + + if (!NM_IS_DEVICE_IWD (device)) + return; + + if (!priv->running) + return; + + objects = g_dbus_object_manager_get_objects (priv->object_manager); + for (iter = objects; iter; iter = iter->next) { + GDBusObject *object = G_DBUS_OBJECT (iter->data); + GDBusInterface *interface; + GDBusProxy *proxy; + GVariant *value; + const char *obj_ifname; + + interface = g_dbus_object_get_interface (object, + NM_IWD_DEVICE_INTERFACE); + if (!interface) + continue; + + proxy = G_DBUS_PROXY (interface); + value = g_dbus_proxy_get_cached_property (proxy, "Name"); + if (!value) { + g_object_unref (interface); + continue; + } + + obj_ifname = g_variant_get_string (value, NULL); + g_variant_unref (value); + g_object_unref (interface); + + if (strcmp (nm_device_get_iface (device), obj_ifname)) + continue; + + nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), object); + break; + } + + g_list_free_full (objects, g_object_unref); +} + +static void +got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMIwdManager *self = user_data; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + GError *error = NULL; + GDBusObjectManager *object_manager; + GDBusConnection *connection; + + object_manager = g_dbus_object_manager_client_new_for_bus_finish (result, &error); + if (object_manager == NULL) { + _LOGE ("failed to acquire IWD Object Manager: Wi-Fi will not be available (%s)", + NM_G_ERROR_MSG (error)); + g_clear_error (&error); + return; + } + + priv->object_manager = object_manager; + + g_signal_connect (priv->object_manager, "notify::name-owner", + G_CALLBACK (name_owner_changed), self); + + nm_assert (G_IS_DBUS_OBJECT_MANAGER_CLIENT (object_manager)); + + connection = g_dbus_object_manager_client_get_connection (G_DBUS_OBJECT_MANAGER_CLIENT (object_manager)); + + priv->agent_id = psk_agent_export (connection, self, + &priv->agent_path, &error); + if (!priv->agent_id) { + _LOGE ("failed to export the IWD Agent: PSK/8021x WiFi networks will not work: %s", + NM_G_ERROR_MSG (error)); + g_clear_error (&error); + } + + if (_om_has_name_owner (object_manager)) { + GList *objects, *iter; + + priv->running = true; + + g_signal_connect (priv->object_manager, "interface-added", + G_CALLBACK (interface_added), self); + g_signal_connect (priv->object_manager, "interface-removed", + G_CALLBACK (interface_removed), self); + + objects = g_dbus_object_manager_get_objects (object_manager); + for (iter = objects; iter; iter = iter->next) + object_added (self, G_DBUS_OBJECT (iter->data)); + + g_list_free_full (objects, g_object_unref); + + if (priv->agent_id) + register_agent (self); + + update_known_networks (self); + } +} + +static void +prepare_object_manager (NMIwdManager *self) +{ + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + + g_dbus_object_manager_client_new_for_bus (NM_IWD_BUS_TYPE, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, + NM_IWD_SERVICE, "/", + NULL, NULL, NULL, + priv->cancellable, + got_object_manager, self); +} + +gboolean +nm_iwd_manager_is_known_network (NMIwdManager *self, const gchar *name, + NMIwdNetworkSecurity security) +{ + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + const GSList *iter; + + for (iter = priv->known_networks; iter; iter = g_slist_next (iter)) { + const KnownNetworkData *network = iter->data; + + if (!strcmp (network->name, name) && network->security == security) + return true; + } + + return false; +} + +void +nm_iwd_manager_network_connected (NMIwdManager *self, const gchar *name, + NMIwdNetworkSecurity security) +{ + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + KnownNetworkData *network_data; + + if (nm_iwd_manager_is_known_network (self, name, security)) + return; + + network_data = g_new (KnownNetworkData, 1); + network_data->name = g_strdup (name); + network_data->security = security; + priv->known_networks = g_slist_append (priv->known_networks, network_data); +} + +/*****************************************************************************/ + +NM_DEFINE_SINGLETON_GETTER (NMIwdManager, nm_iwd_manager_get, + NM_TYPE_IWD_MANAGER); + +static void +nm_iwd_manager_init (NMIwdManager *self) +{ + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + + priv->manager = g_object_ref (nm_manager_get ()); + g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED, + G_CALLBACK (device_added), self); + + priv->cancellable = g_cancellable_new (); + + prepare_object_manager (self); +} + +static void +dispose (GObject *object) +{ + NMIwdManager *self = (NMIwdManager *) object; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + + if (priv->object_manager) { + if (priv->agent_id) { + GDBusConnection *connection; + GDBusObjectManagerClient *omc = G_DBUS_OBJECT_MANAGER_CLIENT (priv->object_manager); + + /* No need to unregister the agent as IWD will detect + * our DBus connection being closed. + */ + + connection = g_dbus_object_manager_client_get_connection (omc); + + g_dbus_connection_unregister_object (connection, priv->agent_id); + priv->agent_id = 0; + } + + g_clear_object (&priv->object_manager); + } + + nm_clear_g_free (&priv->agent_path); + + nm_clear_g_cancellable (&priv->cancellable); + + g_slist_free_full (priv->known_networks, (GDestroyNotify) known_network_free); + priv->known_networks = NULL; + + if (priv->manager) { + g_signal_handlers_disconnect_by_data (priv->manager, self); + g_clear_object (&priv->manager); + } + + G_OBJECT_CLASS (nm_iwd_manager_parent_class)->dispose (object); +} + +static void +nm_iwd_manager_class_init (NMIwdManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = dispose; +} diff --git a/src/devices/wifi/nm-iwd-manager.h b/src/devices/wifi/nm-iwd-manager.h new file mode 100644 index 00000000..8e6b66ff --- /dev/null +++ b/src/devices/wifi/nm-iwd-manager.h @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2017 Intel Corporation + */ + +#ifndef __NETWORKMANAGER_IWD_MANAGER_H__ +#define __NETWORKMANAGER_IWD_MANAGER_H__ + +#include "devices/nm-device.h" + +#define NM_IWD_BUS_TYPE G_BUS_TYPE_SYSTEM +#define NM_IWD_SERVICE "net.connman.iwd" + +#define NM_IWD_AGENT_MANAGER_INTERFACE "net.connman.iwd.AgentManager" +#define NM_IWD_WIPHY_INTERFACE "net.connman.iwd.Adapter" +#define NM_IWD_DEVICE_INTERFACE "net.connman.iwd.Device" +#define NM_IWD_NETWORK_INTERFACE "net.connman.iwd.Network" +#define NM_IWD_AGENT_INTERFACE "net.connman.iwd.Agent" +#define NM_IWD_WSC_INTERFACE \ + "net.connman.iwd.WiFiSimpleConfiguration" +#define NM_IWD_KNOWN_NETWORKS_INTERFACE "net.connman.iwd.KnownNetworks" +#define NM_IWD_SIGNAL_AGENT_INTERFACE "net.connman.iwd.SignalLevelAgent" + +typedef enum { + NM_IWD_NETWORK_SECURITY_NONE, + NM_IWD_NETWORK_SECURITY_WEP, + NM_IWD_NETWORK_SECURITY_PSK, + NM_IWD_NETWORK_SECURITY_8021X, +} NMIwdNetworkSecurity; + +#define NM_TYPE_IWD_MANAGER (nm_iwd_manager_get_type ()) +#define NM_IWD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IWD_MANAGER, NMIwdManager)) +#define NM_IWD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IWD_MANAGER, NMIwdManagerClass)) +#define NM_IS_IWD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IWD_MANAGER)) +#define NM_IS_IWD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IWD_MANAGER)) +#define NM_IWD_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IWD_MANAGER, NMIwdManagerClass)) + +typedef struct _NMIwdManager NMIwdManager; +typedef struct _NMIwdManagerClass NMIwdManagerClass; + +GType nm_iwd_manager_get_type (void); + +NMIwdManager *nm_iwd_manager_get (void); + +gboolean nm_iwd_manager_is_known_network (NMIwdManager *self, const gchar *name, + NMIwdNetworkSecurity security); +void nm_iwd_manager_network_connected (NMIwdManager *self, const gchar *name, + NMIwdNetworkSecurity security); + +#endif /* __NETWORKMANAGER_IWD_MANAGER_H__ */ diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c index bc823af0..dd6d1deb 100644 --- a/src/devices/wifi/nm-wifi-ap.c +++ b/src/devices/wifi/nm-wifi-ap.c @@ -21,19 +21,20 @@ #include "nm-default.h" +#include "nm-wifi-ap.h" + #include #include -#include "nm-wifi-ap.h" +#include "nm-setting-wireless.h" + #include "nm-wifi-utils.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-core-internal.h" #include "platform/nm-platform.h" - -#include "nm-setting-wireless.h" - -#include "introspection/org.freedesktop.NetworkManager.AccessPoint.h" +#include "devices/nm-device.h" +#include "nm-dbus-manager.h" #define PROTO_WPA "wpa" #define PROTO_RSN "rsn" @@ -53,7 +54,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMWifiAP, PROP_LAST_SEEN, ); -typedef struct { +struct _NMWifiAPPrivate { char *supplicant_path; /* D-Bus object path of this AP from wpa_supplicant */ /* Scanned or cached values */ @@ -72,20 +73,17 @@ typedef struct { bool fake:1; /* Whether or not the AP is from a scan */ bool hotspot:1; /* Whether the AP is a local device's hotspot network */ gint32 last_seen; /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */ -} NMWifiAPPrivate; - -struct _NMWifiAP { - NMExportedObject parent; - NMWifiAPPrivate _priv; }; +typedef struct _NMWifiAPPrivate NMWifiAPPrivate; + struct _NMWifiAPClass { - NMExportedObjectClass parent; + NMDBusObjectClass parent; }; -G_DEFINE_TYPE (NMWifiAP, nm_wifi_ap, NM_TYPE_EXPORTED_OBJECT) +G_DEFINE_TYPE (NMWifiAP, nm_wifi_ap, NM_TYPE_DBUS_OBJECT) -#define NM_WIFI_AP_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMWifiAP, NM_IS_WIFI_AP) +#define NM_WIFI_AP_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMWifiAP, NM_IS_WIFI_AP) /*****************************************************************************/ @@ -97,30 +95,24 @@ nm_wifi_ap_get_supplicant_path (NMWifiAP *ap) return NM_WIFI_AP_GET_PRIVATE (ap)->supplicant_path; } -guint64 -nm_wifi_ap_get_id (NMWifiAP *ap) +const GByteArray * +nm_wifi_ap_get_ssid (const NMWifiAP *ap) { - const char *path; - guint64 i; - - g_return_val_if_fail (NM_IS_WIFI_AP (ap), 0); - - path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap)); - g_return_val_if_fail (path, 0); - - nm_assert (g_str_has_prefix (path, NM_DBUS_PATH_ACCESS_POINT"/")); - - i = _nm_utils_ascii_str_to_int64 (&path[NM_STRLEN (NM_DBUS_PATH_ACCESS_POINT"/")], 10, 1, G_MAXINT64, 0); + g_return_val_if_fail (NM_IS_WIFI_AP (ap), NULL); - nm_assert (i); - return i; + return NM_WIFI_AP_GET_PRIVATE (ap)->ssid; } -const GByteArray * nm_wifi_ap_get_ssid (const NMWifiAP *ap) +static GVariant * +nm_wifi_ap_get_ssid_as_variant (const NMWifiAP *self) { - g_return_val_if_fail (NM_IS_WIFI_AP (ap), NULL); + const NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (self); - return NM_WIFI_AP_GET_PRIVATE (ap)->ssid; + if (priv->ssid) { + return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + priv->ssid->data, priv->ssid->len, 1); + } else + return g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0); } gboolean @@ -330,7 +322,7 @@ guint32 nm_wifi_ap_get_max_bitrate (NMWifiAP *ap) { g_return_val_if_fail (NM_IS_WIFI_AP (ap), 0); - g_return_val_if_fail (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (ap)), 0); + g_return_val_if_fail (nm_dbus_object_is_exported (NM_DBUS_OBJECT (ap)), 0); return NM_WIFI_AP_GET_PRIVATE (ap)->max_bitrate; } @@ -415,7 +407,9 @@ security_from_vardict (GVariant *security) && array) { if (g_strv_contains (array, "wpa-psk")) flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK; - if (g_strv_contains (array, "wpa-eap")) + if (g_strv_contains (array, "wpa-eap") || + g_strv_contains (array, "wpa-fils-sha256") || + g_strv_contains (array, "wpa-fils-sha384")) flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X; g_free (array); } @@ -636,7 +630,7 @@ get_max_rate_vht_160_ss3 (int mcs) static gboolean get_max_rate_ht (const guint8 *bytes, guint len, guint32 *out_maxrate) { - guint32 mcs, i; + guint32 i; guint8 ht_cap_info; const guint8 *supported_mcs_set; guint32 rate; @@ -653,7 +647,6 @@ get_max_rate_ht (const guint8 *bytes, guint len, guint32 *out_maxrate) *out_maxrate = 0; /* Find the maximum supported mcs rate */ - mcs = -1; for (i = 0; i <= 76; i++) { unsigned int mcs_octet = i / 8; unsigned int MCS_RATE_BIT = 1 << i % 8; @@ -977,7 +970,7 @@ nm_wifi_ap_to_string (const NMWifiAP *self, if (priv->supplicant_path) supplicant_id = strrchr (priv->supplicant_path, '/') ?: supplicant_id; - export_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self)); + export_path = nm_dbus_object_get_path (NM_DBUS_OBJECT (self)); if (export_path) export_path = strrchr (export_path, '/') ?: export_path; else @@ -1119,8 +1112,8 @@ static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE ((NMWifiAP *) object); - GVariant *ssid; + NMWifiAP *self = NM_WIFI_AP (object); + NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (self); switch (prop_id) { case PROP_FLAGS: @@ -1133,12 +1126,7 @@ get_property (GObject *object, guint prop_id, g_value_set_uint (value, priv->rsn_flags); break; case PROP_SSID: - if (priv->ssid) { - ssid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, - priv->ssid->data, priv->ssid->len, 1); - } else - ssid = g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0); - g_value_take_variant (value, ssid); + g_value_take_variant (value, nm_wifi_ap_get_ssid_as_variant (self)); break; case PROP_FREQUENCY: g_value_set_uint (value, priv->freq); @@ -1170,9 +1158,15 @@ get_property (GObject *object, guint prop_id, /*****************************************************************************/ static void -nm_wifi_ap_init (NMWifiAP *ap) +nm_wifi_ap_init (NMWifiAP *self) { - NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap); + NMWifiAPPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_WIFI_AP, NMWifiAPPrivate); + + self->_priv = priv; + + c_list_init (&self->aps_lst); priv->mode = NM_802_11_MODE_INFRA; priv->flags = NM_802_11_AP_FLAGS_NONE; @@ -1332,7 +1326,11 @@ error: static void finalize (GObject *object) { - NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE ((NMWifiAP *) object); + NMWifiAP *self = NM_WIFI_AP (object); + NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (self); + + nm_assert (!self->wifi_device); + nm_assert (c_list_is_empty (&self->aps_lst)); g_free (priv->supplicant_path); if (priv->ssid) @@ -1342,6 +1340,28 @@ finalize (GObject *object) G_OBJECT_CLASS (nm_wifi_ap_parent_class)->finalize (object); } +static const NMDBusInterfaceInfoExtended interface_info_access_point = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_ACCESS_POINT, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Flags", "u", NM_WIFI_AP_FLAGS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("WpaFlags", "u", NM_WIFI_AP_WPA_FLAGS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("RsnFlags", "u", NM_WIFI_AP_RSN_FLAGS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ssid", "ay", NM_WIFI_AP_SSID), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Frequency", "u", NM_WIFI_AP_FREQUENCY), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_WIFI_AP_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode", "u", NM_WIFI_AP_MODE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("MaxBitrate", "u", NM_WIFI_AP_MAX_BITRATE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Strength", "y", NM_WIFI_AP_STRENGTH), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("LastSeen", "i", NM_WIFI_AP_LAST_SEEN), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_wifi_ap_class_init (NMWifiAPClass *ap_class) { @@ -1359,9 +1379,12 @@ nm_wifi_ap_class_init (NMWifiAPClass *ap_class) | NM_802_11_AP_SEC_KEY_MGMT_802_1X ) GObjectClass *object_class = G_OBJECT_CLASS (ap_class); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (ap_class); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (ap_class); + + g_type_class_add_private (object_class, sizeof (NMWifiAPPrivate)); - exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH_ACCESS_POINT); + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH_ACCESS_POINT); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_access_point); object_class->get_property = get_property; object_class->finalize = finalize; @@ -1424,9 +1447,85 @@ nm_wifi_ap_class_init (NMWifiAPClass *ap_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} + +/*****************************************************************************/ - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (ap_class), - NMDBUS_TYPE_ACCESS_POINT_SKELETON, - NULL); +const char ** +nm_wifi_aps_get_paths (const CList *aps_lst_head, gboolean include_without_ssid) +{ + NMWifiAP *ap; + gsize i, n; + const char **list; + const char *path; + + n = c_list_length (aps_lst_head); + list = g_new (const char *, n + 1); + + i = 0; + if (n > 0) { + c_list_for_each_entry (ap, aps_lst_head, aps_lst) { + nm_assert (i < n); + if ( !include_without_ssid + && !nm_wifi_ap_get_ssid (ap)) + continue; + + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (ap)); + nm_assert (path); + + list[i++] = path; + } + nm_assert (i <= n); + nm_assert (!include_without_ssid || i == n); + } + list[i] = NULL; + return list; } +NMWifiAP * +nm_wifi_aps_find_first_compatible (const CList *aps_lst_head, + NMConnection *connection) +{ + NMWifiAP *ap; + + g_return_val_if_fail (connection, NULL); + + c_list_for_each_entry (ap, aps_lst_head, aps_lst) { + if (nm_wifi_ap_check_compatible (ap, connection)) + return ap; + } + return NULL; +} + +NMWifiAP * +nm_wifi_aps_find_by_supplicant_path (const CList *aps_lst_head, const char *path) +{ + NMWifiAP *ap; + + g_return_val_if_fail (path != NULL, NULL); + + c_list_for_each_entry (ap, aps_lst_head, aps_lst) { + if (nm_streq0 (path, nm_wifi_ap_get_supplicant_path (ap))) + return ap; + } + return NULL; +} + +/*****************************************************************************/ + +NMWifiAP * +nm_wifi_ap_lookup_for_device (NMDevice *device, const char *exported_path) +{ + NMWifiAP *ap; + + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + ap = (NMWifiAP *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (device)), + exported_path); + if ( !ap + || !NM_IS_WIFI_AP (ap) + || ap->wifi_device != device) + return NULL; + + return ap; +} diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h index dd5a4ad1..4fdeee93 100644 --- a/src/devices/wifi/nm-wifi-ap.h +++ b/src/devices/wifi/nm-wifi-ap.h @@ -22,7 +22,7 @@ #ifndef __NM_WIFI_AP_H__ #define __NM_WIFI_AP_H__ -#include "nm-exported-object.h" +#include "nm-dbus-object.h" #include "nm-dbus-interface.h" #include "nm-connection.h" @@ -44,7 +44,13 @@ #define NM_WIFI_AP_STRENGTH "strength" #define NM_WIFI_AP_LAST_SEEN "last-seen" -typedef struct _NMWifiAP NMWifiAP; +typedef struct { + NMDBusObject parent; + NMDevice *wifi_device; + CList aps_lst; + struct _NMWifiAPPrivate *_priv; +} NMWifiAP; + typedef struct _NMWifiAPClass NMWifiAPClass; GType nm_wifi_ap_get_type (void); @@ -66,7 +72,6 @@ gboolean nm_wifi_ap_complete_connection (NMWifiAP *self, GError **error); const char * nm_wifi_ap_get_supplicant_path (NMWifiAP *ap); -guint64 nm_wifi_ap_get_id (NMWifiAP *ap); const GByteArray *nm_wifi_ap_get_ssid (const NMWifiAP *ap); gboolean nm_wifi_ap_set_ssid (NMWifiAP *ap, const guint8 *ssid, @@ -95,4 +100,14 @@ const char *nm_wifi_ap_to_string (const NMWifiAP *self, gulong buf_len, gint32 now_s); +const char **nm_wifi_aps_get_paths (const CList *aps_lst_head, + gboolean include_without_ssid); + +NMWifiAP *nm_wifi_aps_find_first_compatible (const CList *aps_lst_head, + NMConnection *connection); + +NMWifiAP *nm_wifi_aps_find_by_supplicant_path (const CList *aps_lst_head, const char *path); + +NMWifiAP *nm_wifi_ap_lookup_for_device (NMDevice *device, const char *exported_path); + #endif /* __NM_WIFI_AP_H__ */ diff --git a/src/devices/wifi/nm-wifi-common.c b/src/devices/wifi/nm-wifi-common.c new file mode 100644 index 00000000..e5e16f03 --- /dev/null +++ b/src/devices/wifi/nm-wifi-common.c @@ -0,0 +1,206 @@ +/*-*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-wifi-common.h" + +#include "devices/nm-device.h" +#include "nm-wifi-ap.h" +#include "nm-device-wifi.h" +#include "nm-dbus-manager.h" + +#if WITH_IWD +#include "nm-device-iwd.h" +#endif + +/*****************************************************************************/ + +void +nm_device_wifi_emit_signal_access_point (NMDevice *device, + NMWifiAP *ap, + gboolean is_added /* or else is_removed */) +{ + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (device), + &nm_interface_info_device_wireless, + is_added + ? &nm_signal_info_wireless_access_point_added + : &nm_signal_info_wireless_access_point_removed, + "(o)", + nm_dbus_object_get_path (NM_DBUS_OBJECT (ap))); +} + +/*****************************************************************************/ + +static const CList * +_dispatch_get_aps (NMDevice *device) +{ +#if WITH_IWD + if (NM_IS_DEVICE_IWD (device)) + return _nm_device_iwd_get_aps (NM_DEVICE_IWD (device)); +#endif + return _nm_device_wifi_get_aps (NM_DEVICE_WIFI (device)); +} + +static void +_dispatch_request_scan (NMDevice *device, + GVariant *options, + GDBusMethodInvocation *invocation) +{ +#if WITH_IWD + if (NM_IS_DEVICE_IWD (device)) { + _nm_device_iwd_request_scan (NM_DEVICE_IWD (device), + options, + invocation); + return; + } +#endif + _nm_device_wifi_request_scan (NM_DEVICE_WIFI (device), + options, + invocation); +} + +static void +impl_device_wifi_get_access_points (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + gs_free const char **list = NULL; + GVariant *v; + const CList *all_aps; + + /* NOTE: this handler is called both for NMDevicwWifi and NMDeviceIwd. */ + + all_aps = _dispatch_get_aps (NM_DEVICE (obj)); + list = nm_wifi_aps_get_paths (all_aps, FALSE); + v = g_variant_new_objv (list, -1); + g_dbus_method_invocation_return_value (invocation, + g_variant_new_tuple (&v, 1)); +} + +static void +impl_device_wifi_get_all_access_points (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + gs_free const char **list = NULL; + GVariant *v; + const CList *all_aps; + + /* NOTE: this handler is called both for NMDevicwWifi and NMDeviceIwd. */ + + all_aps = _dispatch_get_aps (NM_DEVICE (obj)); + list = nm_wifi_aps_get_paths (all_aps, TRUE); + v = g_variant_new_objv (list, -1); + g_dbus_method_invocation_return_value (invocation, + g_variant_new_tuple (&v, 1)); +} + +static void +impl_device_wifi_request_scan (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + gs_unref_variant GVariant *options = NULL; + + /* NOTE: this handler is called both for NMDevicwWifi and NMDeviceIwd. */ + + g_variant_get (parameters, "(@a{sv})", &options); + + _dispatch_request_scan (NM_DEVICE (obj), + options, + invocation); +} + +const GDBusSignalInfo nm_signal_info_wireless_access_point_added = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "AccessPointAdded", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("access_point", "o"), + ), +); + +const GDBusSignalInfo nm_signal_info_wireless_access_point_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "AccessPointRemoved", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("access_point", "o"), + ), +); + +const NMDBusInterfaceInfoExtended nm_interface_info_device_wireless = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_WIRELESS, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "GetAccessPoints", + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("access_points", "ao"), + ), + ), + .handle = impl_device_wifi_get_access_points, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "GetAllAccessPoints", + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("access_points", "ao"), + ), + ), + .handle = impl_device_wifi_get_all_access_points, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "RequestScan", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("options", "a{sv}"), + ), + ), + .handle = impl_device_wifi_request_scan, + ), + ), + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + &nm_signal_info_wireless_access_point_added, + &nm_signal_info_wireless_access_point_removed, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("PermHwAddress", "s", NM_DEVICE_PERM_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode", "u", NM_DEVICE_WIFI_MODE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("BitRate", "u", NM_DEVICE_WIFI_BITRATE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("AccessPoints", "ao", NM_DEVICE_WIFI_ACCESS_POINTS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ActiveAccessPoint", "o", NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("WirelessCapabilities", "u", NM_DEVICE_WIFI_CAPABILITIES), + ), + ), + .legacy_property_changed = TRUE, +}; diff --git a/src/devices/wifi/nm-wifi-common.h b/src/devices/wifi/nm-wifi-common.h new file mode 100644 index 00000000..91cbeb55 --- /dev/null +++ b/src/devices/wifi/nm-wifi-common.h @@ -0,0 +1,37 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_WIFI_COMMON_H__ +#define __NM_WIFI_COMMON_H__ + +#include "nm-dbus-utils.h" +#include "nm-wifi-ap.h" + +/*****************************************************************************/ + +void nm_device_wifi_emit_signal_access_point (NMDevice *device, + NMWifiAP *ap, + gboolean is_added /* or else is_removed */); + +extern const NMDBusInterfaceInfoExtended nm_interface_info_device_wireless; +extern const GDBusSignalInfo nm_signal_info_wireless_access_point_added; +extern const GDBusSignalInfo nm_signal_info_wireless_access_point_removed; + +#endif /* __NM_WIFI_COMMON_H__ */ diff --git a/src/devices/wifi/nm-wifi-factory.c b/src/devices/wifi/nm-wifi-factory.c index a1752634..6b8e5fb8 100644 --- a/src/devices/wifi/nm-wifi-factory.c +++ b/src/devices/wifi/nm-wifi-factory.c @@ -27,8 +27,10 @@ #include "nm-setting-olpc-mesh.h" #include "nm-device-wifi.h" #include "nm-device-olpc-mesh.h" +#include "nm-device-iwd.h" #include "settings/nm-settings-connection.h" #include "platform/nm-platform.h" +#include "nm-config.h" /*****************************************************************************/ @@ -75,6 +77,7 @@ create_device (NMDeviceFactory *factory, { NMDeviceWifiCapabilities capabilities; NM80211Mode mode; + gs_free char *backend = NULL; g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (plink != NULL, NULL); @@ -98,10 +101,30 @@ create_device (NMDeviceFactory *factory, return NULL; } - if (plink->type == NM_LINK_TYPE_WIFI) - return nm_device_wifi_new (iface, capabilities); - else + if (plink->type != NM_LINK_TYPE_WIFI) return nm_device_olpc_mesh_new (iface); + + backend = nm_config_data_get_device_config_by_pllink (NM_CONFIG_GET_DATA, + NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND, + plink, + "wifi", + NULL); + nm_strstrip (backend); + + nm_log_dbg (LOGD_PLATFORM | LOGD_WIFI, + "(%s) config: backend is %s%s%s%s", + iface, + NM_PRINT_FMT_QUOTE_STRING (backend), + WITH_IWD ? " (iwd support enabled)" : ""); + if (!backend || !strcasecmp (backend, "wpa_supplicant")) + return nm_device_wifi_new (iface, capabilities); +#if WITH_IWD + else if (!strcasecmp (backend, "iwd")) + return nm_device_iwd_new (iface, capabilities); +#endif + + nm_log_warn (LOGD_PLATFORM | LOGD_WIFI, "(%s) config: unknown or unsupported wifi-backend %s", iface, backend); + return NULL; } /*****************************************************************************/ diff --git a/src/devices/wifi/nm-wifi-utils.c b/src/devices/wifi/nm-wifi-utils.c index 3ff82004..044bd392 100644 --- a/src/devices/wifi/nm-wifi-utils.c +++ b/src/devices/wifi/nm-wifi-utils.c @@ -782,3 +782,35 @@ nm_wifi_utils_level_to_quality (gint val) return CLAMP (val, 0, 100); } +gboolean +nm_wifi_utils_is_manf_default_ssid (const GByteArray *ssid) +{ + int i; + /* + * List of manufacturer default SSIDs that are often unchanged by users. + * + * NOTE: this list should *not* contain networks that you would like to + * automatically roam to like "Starbucks" or "AT&T" or "T-Mobile HotSpot". + */ + static const char *manf_defaults[] = { + "linksys", + "linksys-a", + "linksys-g", + "default", + "belkin54g", + "NETGEAR", + "o2DSL", + "WLAN", + "ALICE-WLAN", + "Speedport W 501V", + "TURBONETT", + }; + + for (i = 0; i < G_N_ELEMENTS (manf_defaults); i++) { + if (ssid->len == strlen (manf_defaults[i])) { + if (memcmp (manf_defaults[i], ssid->data, ssid->len) == 0) + return TRUE; + } + } + return FALSE; +} diff --git a/src/devices/wifi/nm-wifi-utils.h b/src/devices/wifi/nm-wifi-utils.h index 1b6c2f4b..def64dd6 100644 --- a/src/devices/wifi/nm-wifi-utils.h +++ b/src/devices/wifi/nm-wifi-utils.h @@ -39,4 +39,6 @@ gboolean nm_wifi_utils_complete_connection (const GByteArray *ssid, guint32 nm_wifi_utils_level_to_quality (gint val); +gboolean nm_wifi_utils_is_manf_default_ssid (const GByteArray *ssid); + #endif /* __NM_WIFI_UTILS_H__ */ diff --git a/src/devices/wifi/tests/meson.build b/src/devices/wifi/tests/meson.build new file mode 100644 index 00000000..bb8f7c27 --- /dev/null +++ b/src/devices/wifi/tests/meson.build @@ -0,0 +1,13 @@ +test_unit = 'test-general' + +exe = executable( + 'wifi-' + test_unit, + [test_unit + '.c'] + common_sources, + dependencies: test_nm_dep +) + +test( + 'devices/wifi/' + test_unit, + test_script, + args: test_args + [exe.full_path()] +) diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver index 6efcb03f..70b954c5 100644 --- a/src/devices/wwan/libnm-wwan.ver +++ b/src/devices/wwan/libnm-wwan.ver @@ -11,10 +11,10 @@ global: nm_modem_get_capabilities; nm_modem_get_configured_mtu; nm_modem_get_control_port; - nm_modem_get_data_port; nm_modem_get_driver; nm_modem_get_iid; nm_modem_get_path; + nm_modem_get_ip_ifindex; nm_modem_get_secrets; nm_modem_get_state; nm_modem_get_type; diff --git a/src/devices/wwan/meson.build b/src/devices/wwan/meson.build new file mode 100644 index 00000000..abf58594 --- /dev/null +++ b/src/devices/wwan/meson.build @@ -0,0 +1,75 @@ +sources = files( + 'nm-modem-broadband.c', + 'nm-modem.c', + 'nm-modem-manager.c' +) + +deps = [ + libsystemd_dep, + mm_glib_dep, + nm_dep +] + +if enable_ofono + sources += files('nm-modem-ofono.c') +endif + +linker_script = join_paths(meson.current_source_dir(), 'libnm-wwan.ver') + +libnm_wwan = shared_module( + 'nm-wwan', + sources: sources, + dependencies: deps, + link_args: [ + '-Wl,--version-script,@0@'.format(linker_script), + ], + link_depends: linker_script, + install: true, + install_dir: nm_plugindir +) + +libnm_wwan_dep = declare_dependency( + include_directories: include_directories('.'), + link_with: libnm_wwan +) + +core_plugins += libnm_wwan + +run_target( + 'check-wwan', + command: [check_exports, libnm_wwan.full_path(), linker_script], + depends: libnm_wwan +) + +sources = files( + 'nm-device-modem.c', + 'nm-wwan-factory.c' +) + +libnm_device_plugin_wwan = shared_module( + 'nm-device-plugin-wwan', + sources: sources, + dependencies: deps, + link_with: libnm_wwan, + link_args: ldflags_linker_script_devices, + link_depends: linker_script_devices, + install: true, + install_dir: nm_plugindir +) + +core_plugins += libnm_device_plugin_wwan + +run_target( + 'check-local-devices-wwan', + command: [check_exports, libnm_device_plugin_wwan.full_path(), linker_script_devices], + depends: libnm_device_plugin_wwan +) + +# FIXME: check_so_symbols replacement +''' +check-local-devices-wwan: src/devices/wwan/libnm-device-plugin-wwan.la src/devices/wwan/libnm-wwan.la + $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/wwan/.libs/libnm-device-plugin-wwan.so "$(srcdir)/linker-script-devices.ver" + $(call check_so_symbols,$(builddir)/src/devices/wwan/.libs/libnm-device-plugin-wwan.so) + $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/wwan/.libs/libnm-wwan.so "$(srcdir)/src/devices/wwan/libnm-wwan.ver" + $(call check_so_symbols,$(builddir)/src/devices/wwan/.libs/libnm-wwan.so) +''' diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index b79d145d..2a3e9ebe 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -32,8 +32,6 @@ #include "NetworkManagerUtils.h" #include "nm-core-internal.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Modem.h" - #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceModem); @@ -260,21 +258,26 @@ modem_ip6_config_result (NMModem *modem, } static void -data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) +ip_ifindex_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) { - NMDevice *self = NM_DEVICE (user_data); - gboolean changed; + NMDevice *device = NM_DEVICE (user_data); + + if (!nm_device_is_activating (device)) + return; - /* We set the IP iface in the device as soon as we know it, so that we - * properly ifup it if needed */ - changed = nm_device_set_ip_iface (self, nm_modem_get_data_port (modem)); + if (!nm_device_set_ip_ifindex (device, + nm_modem_get_ip_ifindex (modem))) { + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + return; + } /* Disable IPv6 immediately on the interface since NM handles IPv6 * internally, and leaving it enabled could allow the kernel's IPv6 * RA handling code to run before NM is ready. */ - if (changed) - nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); + nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1"); } static void @@ -431,7 +434,7 @@ static gboolean complete_connection (NMDevice *device, NMConnection *connection, const char *specific_object, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device); @@ -629,11 +632,7 @@ set_modem (NMDeviceModem *self, NMModem *modem) g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self); g_signal_connect (modem, NM_MODEM_REMOVED, G_CALLBACK (modem_removed_cb), self); - /* In the old ModemManager the data port is known from the very beginning; - * while in the new ModemManager the data port is set afterwards when the bearer gets - * created */ - g_signal_connect (modem, "notify::" NM_MODEM_DATA_PORT, G_CALLBACK (data_port_changed_cb), self); - + g_signal_connect (modem, "notify::" NM_MODEM_IP_IFINDEX, G_CALLBACK (ip_ifindex_changed_cb), self); g_signal_connect (modem, "notify::" NM_MODEM_DEVICE_ID, G_CALLBACK (ids_changed_cb), self); g_signal_connect (modem, "notify::" NM_MODEM_SIM_ID, G_CALLBACK (ids_changed_cb), self); g_signal_connect (modem, "notify::" NM_MODEM_SIM_OPERATOR_ID, G_CALLBACK (ids_changed_cb), self); @@ -708,34 +707,23 @@ nm_device_modem_new (NMModem *modem) { NMDeviceModemCapabilities caps = NM_DEVICE_MODEM_CAPABILITY_NONE; NMDeviceModemCapabilities current_caps = NM_DEVICE_MODEM_CAPABILITY_NONE; - NMDevice *device; - const char *data_port; g_return_val_if_fail (NM_IS_MODEM (modem), NULL); /* Load capabilities */ nm_modem_get_capabilities (modem, &caps, ¤t_caps); - device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_MODEM, - NM_DEVICE_UDI, nm_modem_get_path (modem), - NM_DEVICE_IFACE, nm_modem_get_uid (modem), - NM_DEVICE_DRIVER, nm_modem_get_driver (modem), - NM_DEVICE_TYPE_DESC, "Broadband", - NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_MODEM, - NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WWAN, - NM_DEVICE_MODEM_MODEM, modem, - NM_DEVICE_MODEM_CAPABILITIES, caps, - NM_DEVICE_MODEM_CURRENT_CAPABILITIES, current_caps, - NULL); - - /* If the data port is known, set it as the IP interface immediately */ - data_port = nm_modem_get_data_port (modem); - if (data_port) { - nm_device_set_ip_iface (device, data_port); - nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1"); - } - - return device; + return g_object_new (NM_TYPE_DEVICE_MODEM, + NM_DEVICE_UDI, nm_modem_get_path (modem), + NM_DEVICE_IFACE, nm_modem_get_uid (modem), + NM_DEVICE_DRIVER, nm_modem_get_driver (modem), + NM_DEVICE_TYPE_DESC, "Broadband", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_MODEM, + NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WWAN, + NM_DEVICE_MODEM_MODEM, modem, + NM_DEVICE_MODEM_CAPABILITIES, caps, + NM_DEVICE_MODEM_CURRENT_CAPABILITIES, current_caps, + NULL); } static void @@ -743,23 +731,41 @@ dispose (GObject *object) { NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) object); - if (priv->modem) + if (priv->modem) { g_signal_handlers_disconnect_by_data (priv->modem, NM_DEVICE_MODEM (object)); - g_clear_object (&priv->modem); + g_clear_object (&priv->modem); + } G_OBJECT_CLASS (nm_device_modem_parent_class)->dispose (object); } +static const NMDBusInterfaceInfoExtended interface_info_device_modem = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_MODEM, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ModemCapabilities", "u", NM_DEVICE_MODEM_CAPABILITIES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("CurrentCapabilities", "u", NM_DEVICE_MODEM_CURRENT_CAPABILITIES), + ), + ), + .legacy_property_changed = TRUE, +}; + static void -nm_device_modem_class_init (NMDeviceModemClass *mclass) +nm_device_modem_class_init (NMDeviceModemClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS (mclass); - NMDeviceClass *device_class = NM_DEVICE_CLASS (mclass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); object_class->dispose = dispose; object_class->get_property = get_property; object_class->set_property = set_property; + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_modem); + device_class->get_generic_capabilities = get_generic_capabilities; device_class->get_type_description = get_type_description; device_class->check_connection_compatible = check_connection_compatible; @@ -802,8 +808,4 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (mclass), - NMDBUS_TYPE_DEVICE_MODEM_SKELETON, - NULL); } diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index dc0ce303..e5678b96 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -286,7 +286,7 @@ create_gsm_connect_properties (NMConnection *connection) /* Blank APN ("") means the default subscription APN */ str = nm_setting_gsm_get_apn (setting); - mm_simple_connect_properties_set_apn (properties, str ? str : ""); + mm_simple_connect_properties_set_apn (properties, str ?: ""); str = nm_setting_gsm_get_network_id (setting); if (str) @@ -339,7 +339,7 @@ connect_context_clear (NMModemBroadband *self) ConnectContext *ctx = self->_priv.ctx; g_clear_error (&ctx->first_error); - g_clear_pointer (&ctx->ip_types, (GDestroyNotify) g_array_unref); + g_clear_pointer (&ctx->ip_types, g_array_unref); nm_clear_g_cancellable (&ctx->cancellable); g_clear_object (&ctx->connection); g_clear_object (&ctx->connect_properties); @@ -385,9 +385,10 @@ connect_ready (MMModemSimple *simple_iface, g_dbus_error_strip_remote_error (error); ctx->first_error = error; } else - g_error_free (error); + g_clear_error (&error); - if (ctx->ip_type_tries == 0 && g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)) { + if ( ctx->ip_type_tries == 0 + && g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)) { /* Try one more time */ ctx->ip_type_tries++; } else { @@ -410,21 +411,20 @@ connect_ready (MMModemSimple *simple_iface, if (self->_priv.ipv6_config) ip6_method = get_bearer_ip_method (self->_priv.ipv6_config); - if (ip4_method == NM_MODEM_IP_METHOD_UNKNOWN && - ip6_method == NM_MODEM_IP_METHOD_UNKNOWN) { - _LOGW ("failed to connect modem: invalid bearer IP configuration"); + if (!nm_modem_set_data_port (NM_MODEM (self), + NM_PLATFORM_GET, + mm_bearer_get_interface (self->_priv.bearer), + ip4_method, + ip6_method, + mm_bearer_get_ip_timeout (self->_priv.bearer), + &error)) { + _LOGW ("failed to connect modem: %s", error->message); + g_error_free (error); nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED); connect_context_clear (self); return; } - g_object_set (self, - NM_MODEM_DATA_PORT, mm_bearer_get_interface (self->_priv.bearer), - NM_MODEM_IP4_METHOD, ip4_method, - NM_MODEM_IP6_METHOD, ip6_method, - NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (self->_priv.bearer), - NULL); - ctx->step++; connect_context_step (self); } @@ -663,7 +663,7 @@ check_connection_compatible (NMModem *_self, NMConnection *connection) static gboolean complete_connection (NMModem *_self, NMConnection *connection, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { NMModemBroadband *self = NM_MODEM_BROADBAND (_self); @@ -924,7 +924,6 @@ static_stage3_ip4_done (NMModemBroadband *self) _LOGI (" address %s/%d", address_string, address.plen); - nm_modem_get_route_parameters (NM_MODEM (self), &ip4_route_table, &ip4_route_metric, @@ -1283,7 +1282,6 @@ get_sim_ready (MMModem *modem, GError *error = NULL; MMSim *new_sim; - new_sim = mm_modem_get_sim_finish (modem, res, &error); if (new_sim != self->_priv.sim_iface) { g_clear_object (&self->_priv.sim_iface); @@ -1409,35 +1407,34 @@ nm_modem_broadband_init (NMModemBroadband *self) NMModem * nm_modem_broadband_new (GObject *object, GError **error) { - NMModem *modem; MMObject *modem_object; MMModem *modem_iface; - gchar *drivers; + const char *const*drivers; + gs_free char *driver = NULL; g_return_val_if_fail (MM_IS_OBJECT (object), NULL); modem_object = MM_OBJECT (object); /* Ensure we have the 'Modem' interface and the primary port at least */ modem_iface = mm_object_peek_modem (modem_object); - g_return_val_if_fail (!!modem_iface, NULL); - g_return_val_if_fail (!!mm_modem_get_primary_port (modem_iface), NULL); + g_return_val_if_fail (modem_iface, NULL); + g_return_val_if_fail (mm_modem_get_primary_port (modem_iface), NULL); /* Build a single string with all drivers listed */ - drivers = g_strjoinv (", ", (gchar **)mm_modem_get_drivers (modem_iface)); - - modem = g_object_new (NM_TYPE_MODEM_BROADBAND, - NM_MODEM_PATH, mm_object_get_path (modem_object), - NM_MODEM_UID, mm_modem_get_primary_port (modem_iface), - NM_MODEM_CONTROL_PORT, mm_modem_get_primary_port (modem_iface), - NM_MODEM_DATA_PORT, NULL, /* We don't know it until bearer created */ - NM_MODEM_IP_TYPES, mm_ip_family_to_nm (mm_modem_get_supported_ip_families (modem_iface)), - NM_MODEM_STATE, (int) mm_state_to_nm (mm_modem_get_state (modem_iface)), - NM_MODEM_DEVICE_ID, mm_modem_get_device_identifier (modem_iface), - NM_MODEM_BROADBAND_MODEM, modem_object, - NM_MODEM_DRIVER, drivers, - NULL); - g_free (drivers); - return modem; + drivers = mm_modem_get_drivers (modem_iface); + if (drivers) + driver = g_strjoinv (", ", (char **) drivers); + + return g_object_new (NM_TYPE_MODEM_BROADBAND, + NM_MODEM_PATH, mm_object_get_path (modem_object), + NM_MODEM_UID, mm_modem_get_primary_port (modem_iface), + NM_MODEM_CONTROL_PORT, mm_modem_get_primary_port (modem_iface), + NM_MODEM_IP_TYPES, mm_ip_family_to_nm (mm_modem_get_supported_ip_families (modem_iface)), + NM_MODEM_STATE, (int) mm_state_to_nm (mm_modem_get_state (modem_iface)), + NM_MODEM_DEVICE_ID, mm_modem_get_device_identifier (modem_iface), + NM_MODEM_BROADBAND_MODEM, modem_object, + NM_MODEM_DRIVER, driver, + NULL); } static void diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c index 811c3afb..8b81ac41 100644 --- a/src/devices/wwan/nm-modem-ofono.c +++ b/src/devices/wwan/nm-modem-ofono.c @@ -278,7 +278,6 @@ deactivate_cleanup (NMModem *modem, NMDevice *device) NM_MODEM_CLASS (nm_modem_ofono_parent_class)->deactivate_cleanup (modem, device); } - static gboolean check_connection_compatible (NMModem *modem, NMConnection *connection) @@ -836,6 +835,7 @@ context_property_changed (GDBusProxy *proxy, guint32 address_network, gateway_network; guint32 ip4_route_table, ip4_route_metric; int ifindex; + GError *error = NULL; _LOGD ("PropertyChanged: %s", property); @@ -860,27 +860,26 @@ context_property_changed (GDBusProxy *proxy, _LOGW ("Settings 'Interface' missing"); goto out; } - if (!interface || !interface[0]) { - _LOGW ("Settings 'Interface'; empty"); - goto out; - } - ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, interface); - if (ifindex <= 0) { - _LOGW ("Interface \"%s\" not found", interface); + _LOGD ("Interface: %s", interface); + if (!nm_modem_set_data_port (NM_MODEM (self), + NM_PLATFORM_GET, + interface, + NM_MODEM_IP_METHOD_STATIC, + NM_MODEM_IP_METHOD_UNKNOWN, + 0, + &error)) { + _LOGW ("failed to connect to modem: %s", error->message); + g_clear_error (&error); goto out; } - _LOGD ("Interface: %s", interface); - g_object_set (self, - NM_MODEM_DATA_PORT, interface, - NM_MODEM_IP4_METHOD, NM_MODEM_IP_METHOD_STATIC, - NULL); + ifindex = nm_modem_get_ip_ifindex (NM_MODEM (self)); + nm_assert (ifindex > 0); /* TODO: verify handling of ip4_config; check other places it's used... */ g_clear_object (&priv->ip4_config); - priv->ip4_config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), ifindex); diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 010a2b60..fbe99cc3 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -44,13 +44,10 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMModem, PROP_CONTROL_PORT, - PROP_DATA_PORT, + PROP_IP_IFINDEX, PROP_PATH, PROP_UID, PROP_DRIVER, - PROP_IP4_METHOD, - PROP_IP6_METHOD, - PROP_IP_TIMEOUT, PROP_STATE, PROP_DEVICE_ID, PROP_SIM_ID, @@ -79,7 +76,12 @@ typedef struct _NMModemPrivate { char *driver; char *control_port; char *data_port; - char *ppp_iface; + + /* 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; @@ -96,7 +98,7 @@ typedef struct _NMModemPrivate { guint32 secrets_tries; NMActRequestGetSecretsCallId *secrets_id; - guint32 mm_ip_timeout; + guint mm_ip_timeout; guint32 ip4_route_table; guint32 ip4_route_metric; @@ -152,6 +154,10 @@ _nmlog_prefix (char *prefix, NMModem *self) _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ } G_STMT_END +/*****************************************************************************/ + +static void _set_ip_ifindex (NMModem *self, int ifindex, const char *ifname); + /*****************************************************************************/ /* State/enabled/connected */ @@ -199,7 +205,7 @@ nm_modem_set_state (NMModem *self, _LOGI ("modem state changed, '%s' --> '%s' (reason: %s)", nm_modem_state_to_string (old_state), nm_modem_state_to_string (new_state), - reason ? reason : "none"); + reason ?: "none"); priv->state = new_state; _notify (self, PROP_STATE); @@ -450,20 +456,28 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_ } static void -set_data_port (NMModem *self, const char *new_data_port) +ppp_ifindex_set (NMPPPManager *ppp_manager, + int ifindex, + const char *iface, + gpointer user_data) { - NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + NMModem *self = NM_MODEM (user_data); + + nm_assert (ifindex >= 0); + nm_assert (NM_MODEM_GET_PRIVATE (self)->ppp_manager == ppp_manager); - if (g_strcmp0 (priv->data_port, new_data_port) != 0) { - g_free (priv->data_port); - priv->data_port = g_strdup (new_data_port); - _notify (self, PROP_DATA_PORT); + 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; } + _set_ip_ifindex (self, ifindex, iface); } static void ppp_ip4_config (NMPPPManager *ppp_manager, - const char *iface, NMIP4Config *config, gpointer user_data) { @@ -475,9 +489,6 @@ ppp_ip4_config (NMPPPManager *ppp_manager, guint32 good_dns2 = htonl (0x04020202); /* GTE nameserver */ gboolean dns_workaround = FALSE; - /* Notify about the new data port to use */ - set_data_port (self, iface); - /* Work around a PPP bug (#1732) which causes many mobile broadband * providers to return 10.11.12.13 and 10.11.12.14 for the DNS servers. * Apparently fixed in ppp-2.4.5 but we've had some reports that this is @@ -519,16 +530,12 @@ ppp_ip4_config (NMPPPManager *ppp_manager, static void ppp_ip6_config (NMPPPManager *ppp_manager, - const char *iface, const NMUtilsIPv6IfaceId *iid, NMIP6Config *config, gpointer user_data) { NMModem *self = NM_MODEM (user_data); - /* Notify about the new data port to use */ - set_data_port (self, iface); - NM_MODEM_GET_PRIVATE (self)->iid = *iid; nm_modem_emit_ip6_config_result (self, config, NULL); @@ -557,6 +564,18 @@ port_speed_is_zero (const char *port) { struct termios options; nm_auto_close int fd = -1; + gs_free char *path = NULL; + + nm_assert (port); + + if (port[0] != '/') { + if ( !port[0] + || strchr (port, '/') + || NM_IN_STRSET (port, ".", "..")) + return FALSE; + path = g_build_path ("/sys/class/tty", port, NULL); + port = path; + } fd = open (port, O_RDWR | O_NONBLOCK | O_NOCTTY | O_CLOEXEC); if (fd < 0) @@ -598,6 +617,12 @@ ppp_stage3_ip_config_start (NMModem *self, return NM_ACT_STAGE_RETURN_FAILURE; } + 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; + } + /* 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) { @@ -629,9 +654,7 @@ ppp_stage3_ip_config_start (NMModem *self, 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; } @@ -639,6 +662,9 @@ ppp_stage3_ip_config_start (NMModem *self, 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); @@ -1065,12 +1091,20 @@ nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection) gboolean nm_modem_complete_connection (NMModem *self, NMConnection *connection, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error) { - if (NM_MODEM_GET_CLASS (self)->complete_connection) - return NM_MODEM_GET_CLASS (self)->complete_connection (self, connection, existing_connections, error); - return FALSE; + NMModemClass *klass; + + klass = NM_MODEM_GET_CLASS (self); + if (!klass->complete_connection) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, + "Modem class %s had no complete_connection method", + G_OBJECT_TYPE_NAME (self)); + return FALSE; + } + + return klass->complete_connection (self, connection, existing_connections, error); } /*****************************************************************************/ @@ -1095,7 +1129,11 @@ deactivate_cleanup (NMModem *self, NMDevice *device) priv->in_bytes = priv->out_bytes = 0; - g_clear_object (&priv->ppp_manager); + if (priv->ppp_manager) { + g_signal_handlers_disconnect_by_data (priv->ppp_manager, self); + nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL); + g_clear_object (&priv->ppp_manager); + } if (device) { g_return_if_fail (NM_IS_DEVICE (device)); @@ -1114,11 +1152,12 @@ deactivate_cleanup (NMModem *self, NMDevice *device) } } } + + nm_clear_g_free (&priv->data_port); + priv->mm_ip_timeout = 0; priv->ip4_method = NM_MODEM_IP_METHOD_UNKNOWN; priv->ip6_method = NM_MODEM_IP_METHOD_UNKNOWN; - - g_free (priv->ppp_iface); - priv->ppp_iface = NULL; + _set_ip_ifindex (self, -1, NULL); } /*****************************************************************************/ @@ -1138,11 +1177,19 @@ typedef struct { GSimpleAsyncResult *result; DeactivateContextStep step; NMPPPManager *ppp_manager; + NMPPPManagerStopHandle *ppp_stop_handle; + gulong ppp_stop_cancellable_id; } DeactivateContext; static void deactivate_context_complete (DeactivateContext *ctx) { + if (ctx->ppp_stop_handle) + nm_ppp_manager_stop_cancel (ctx->ppp_stop_handle); + + nm_assert (!ctx->ppp_stop_handle); + nm_assert (ctx->ppp_stop_cancellable_id == 0); + if (ctx->ppp_manager) g_object_unref (ctx->ppp_manager); if (ctx->cancellable) @@ -1184,25 +1231,36 @@ disconnect_ready (NMModem *self, static void ppp_manager_stop_ready (NMPPPManager *ppp_manager, - GAsyncResult *res, - DeactivateContext *ctx) + NMPPPManagerStopHandle *handle, + gboolean was_cancelled, + gpointer user_data) { - NMModem *self = ctx->self; - GError *error = NULL; + DeactivateContext *ctx = user_data; - if (!nm_ppp_manager_stop_finish (ppp_manager, res, &error)) { - _LOGW ("cannot stop PPP manager: %s", - error->message); - g_simple_async_result_take_error (ctx->result, error); - deactivate_context_complete (ctx); - return; + nm_assert (ctx->ppp_stop_handle == handle); + ctx->ppp_stop_handle = NULL; + + if (ctx->ppp_stop_cancellable_id) { + g_cancellable_disconnect (ctx->cancellable, + nm_steal_int (&ctx->ppp_stop_cancellable_id)); } - /* Go on */ + if (was_cancelled) + return; + ctx->step++; deactivate_step (ctx); } +static void +ppp_manager_stop_cancelled (GCancellable *cancellable, + gpointer user_data) +{ + DeactivateContext *ctx = user_data; + + nm_ppp_manager_stop_cancel (ctx->ppp_stop_handle); +} + static void deactivate_step (DeactivateContext *ctx) { @@ -1232,10 +1290,16 @@ deactivate_step (DeactivateContext *ctx) case DEACTIVATE_CONTEXT_STEP_PPP_MANAGER_STOP: /* If we have a PPP manager, stop it */ if (ctx->ppp_manager) { - nm_ppp_manager_stop_async (ctx->ppp_manager, - ctx->cancellable, - (GAsyncReadyCallback) ppp_manager_stop_ready, - ctx); + nm_assert (!ctx->ppp_stop_handle); + if (ctx->cancellable) { + ctx->ppp_stop_cancellable_id = g_cancellable_connect (ctx->cancellable, + G_CALLBACK (ppp_manager_stop_cancelled), + ctx, + NULL); + } + ctx->ppp_stop_handle = nm_ppp_manager_stop (ctx->ppp_manager, + ppp_manager_stop_ready, + ctx); return; } ctx->step++; @@ -1274,7 +1338,9 @@ nm_modem_deactivate_async (NMModem *self, callback, user_data, nm_modem_deactivate_async); - ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + /* FIXME(shutdown): we always require a cancellable, otherwise we cannot + * do a coordinated shutdown. */ + ctx->cancellable = nm_g_object_ref (cancellable); /* Start */ ctx->step = DEACTIVATE_CONTEXT_STEP_FIRST; @@ -1369,17 +1435,117 @@ nm_modem_get_control_port (NMModem *self) return NM_MODEM_GET_PRIVATE (self)->control_port; } -const char * -nm_modem_get_data_port (NMModem *self) +int +nm_modem_get_ip_ifindex (NMModem *self) { - g_return_val_if_fail (NM_IS_MODEM (self), NULL); + NMModemPrivate *priv; - /* The ppp_iface takes precedence over the data interface when PPP is used, - * since data_iface is the TTY over which PPP is run, and that TTY can't - * do IP. The caller really wants the thing that's doing IP. - */ - return NM_MODEM_GET_PRIVATE (self)->ppp_iface ? - NM_MODEM_GET_PRIVATE (self)->ppp_iface : NM_MODEM_GET_PRIVATE (self)->data_port; + g_return_val_if_fail (NM_IS_MODEM (self), 0); + + priv = NM_MODEM_GET_PRIVATE (self); + + /* internally we track an unset ip_ifindex as -1. + * For the caller of nm_modem_get_ip_ifindex(), this + * shall be zero too. */ + return priv->ip_ifindex != -1 ? priv->ip_ifindex : 0; +} + +static void +_set_ip_ifindex (NMModem *self, int ifindex, const char *ifname) +{ + 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) { + priv->ip_ifindex = ifindex; + _notify (self, PROP_IP_IFINDEX); + } +} + +gboolean +nm_modem_set_data_port (NMModem *self, + NMPlatform *platform, + const char *data_port, + NMModemIPMethod ip4_method, + NMModemIPMethod ip6_method, + guint timeout, + GError **error) +{ + NMModemPrivate *priv; + gboolean is_ppp; + int ifindex = -1; + + g_return_val_if_fail (NM_IS_MODEM (self), FALSE); + g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + + priv = NM_MODEM_GET_PRIVATE (self); + + if ( priv->ppp_manager + || priv->data_port + || priv->ip_ifindex != -1) { + g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "cannot set data port in activated state"); + /* this really shouldn't happen. Assert. */ + g_return_val_if_reached (FALSE); + } + + if (!data_port) { + g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "missing data port"); + return FALSE; + } + + is_ppp = (ip4_method == NM_MODEM_IP_METHOD_PPP) + || (ip6_method == NM_MODEM_IP_METHOD_PPP); + if (is_ppp) { + if ( !NM_IN_SET (ip4_method, NM_MODEM_IP_METHOD_UNKNOWN, NM_MODEM_IP_METHOD_PPP) + || !NM_IN_SET (ip6_method, NM_MODEM_IP_METHOD_UNKNOWN, NM_MODEM_IP_METHOD_PPP)) { + g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "conflicting ip methods"); + return FALSE; + } + } else if ( !NM_IN_SET (ip4_method, NM_MODEM_IP_METHOD_UNKNOWN, NM_MODEM_IP_METHOD_STATIC, NM_MODEM_IP_METHOD_AUTO) + || !NM_IN_SET (ip6_method, NM_MODEM_IP_METHOD_UNKNOWN, NM_MODEM_IP_METHOD_STATIC, NM_MODEM_IP_METHOD_AUTO) + || ( ip4_method == NM_MODEM_IP_METHOD_UNKNOWN + && ip6_method == NM_MODEM_IP_METHOD_UNKNOWN)) { + g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "invalid ip methods"); + return FALSE; + } + + if (!is_ppp) { + ifindex = nm_platform_if_nametoindex (platform, data_port); + if (ifindex <= 0) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "cannot find network interface %s", data_port); + return FALSE; + } + if (!nm_platform_process_events_ensure_link (platform, ifindex, data_port)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "cannot find network interface %s in platform cache", data_port); + return FALSE; + } + } + + priv->mm_ip_timeout = timeout; + priv->ip4_method = ip4_method; + priv->ip6_method = ip6_method; + if (is_ppp) { + priv->data_port = g_strdup (data_port); + _set_ip_ifindex (self, -1, NULL); + } else { + priv->data_port = NULL; + _set_ip_ifindex (self, ifindex, data_port); + } + return TRUE; } gboolean @@ -1392,15 +1558,10 @@ nm_modem_owns_port (NMModem *self, const char *iface) if (NM_MODEM_GET_CLASS (self)->owns_port) return NM_MODEM_GET_CLASS (self)->owns_port (self, iface); - /* Fall back to data/control ports */ - if (priv->ppp_iface && (strcmp (priv->ppp_iface, iface) == 0)) - return TRUE; - if (priv->data_port && (strcmp (priv->data_port, iface) == 0)) - return TRUE; - if (priv->control_port && (strcmp (priv->control_port, iface) == 0)) - return TRUE; - - return FALSE; + return NM_IN_STRSET (iface, + priv->ip_iface, + priv->data_port, + priv->control_port); } gboolean @@ -1500,7 +1661,8 @@ static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - NMModemPrivate *priv = NM_MODEM_GET_PRIVATE ((NMModem *) object); + NMModem *self = NM_MODEM (object); + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); switch (prop_id) { case PROP_PATH: @@ -1512,21 +1674,12 @@ get_property (GObject *object, guint prop_id, case PROP_CONTROL_PORT: g_value_set_string (value, priv->control_port); break; - case PROP_DATA_PORT: - g_value_set_string (value, nm_modem_get_data_port (NM_MODEM (object))); + case PROP_IP_IFINDEX: + g_value_set_int (value, nm_modem_get_ip_ifindex (self)); break; case PROP_UID: g_value_set_string (value, priv->uid); break; - case PROP_IP4_METHOD: - g_value_set_uint (value, priv->ip4_method); - break; - case PROP_IP6_METHOD: - g_value_set_uint (value, priv->ip6_method); - break; - case PROP_IP_TIMEOUT: - g_value_set_uint (value, priv->mm_ip_timeout); - break; case PROP_STATE: g_value_set_int (value, priv->state); break; @@ -1569,23 +1722,10 @@ set_property (GObject *object, guint prop_id, /* construct-only */ priv->control_port = g_value_dup_string (value); break; - case PROP_DATA_PORT: - g_free (priv->data_port); - priv->data_port = g_value_dup_string (value); - break; case PROP_UID: /* construct-only */ priv->uid = g_value_dup_string (value); break; - case PROP_IP4_METHOD: - priv->ip4_method = g_value_get_uint (value); - break; - case PROP_IP6_METHOD: - priv->ip6_method = g_value_get_uint (value); - break; - case PROP_IP_TIMEOUT: - priv->mm_ip_timeout = g_value_get_uint (value); - break; case PROP_STATE: /* construct-only */ priv->state = g_value_get_int (value); @@ -1623,6 +1763,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; @@ -1638,7 +1779,7 @@ constructed (GObject *object) priv = NM_MODEM_GET_PRIVATE (NM_MODEM (object)); - g_return_if_fail (priv->data_port || priv->control_port); + g_return_if_fail (priv->control_port); } /*****************************************************************************/ @@ -1663,6 +1804,7 @@ 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); @@ -1711,33 +1853,11 @@ nm_modem_class_init (NMModemClass *klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_DATA_PORT] = - g_param_spec_string (NM_MODEM_DATA_PORT, "", "", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_IP4_METHOD] = - g_param_spec_uint (NM_MODEM_IP4_METHOD, "", "", - NM_MODEM_IP_METHOD_UNKNOWN, - NM_MODEM_IP_METHOD_AUTO, - NM_MODEM_IP_METHOD_UNKNOWN, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_IP6_METHOD] = - g_param_spec_uint (NM_MODEM_IP6_METHOD, "", "", - NM_MODEM_IP_METHOD_UNKNOWN, - NM_MODEM_IP_METHOD_AUTO, - NM_MODEM_IP_METHOD_UNKNOWN, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_IP_TIMEOUT] = - g_param_spec_uint (NM_MODEM_IP_TIMEOUT, "", "", - 0, 360, 20, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); + obj_properties[PROP_IP_IFINDEX] = + g_param_spec_int (NM_MODEM_IP_IFINDEX, "", "", + 0, G_MAXINT, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); obj_properties[PROP_STATE] = g_param_spec_int (NM_MODEM_STATE, "", "", diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h index 9546e4a1..3e281c0c 100644 --- a/src/devices/wwan/nm-modem.h +++ b/src/devices/wwan/nm-modem.h @@ -37,10 +37,7 @@ #define NM_MODEM_PATH "path" #define NM_MODEM_DRIVER "driver" #define NM_MODEM_CONTROL_PORT "control-port" -#define NM_MODEM_DATA_PORT "data-port" -#define NM_MODEM_IP4_METHOD "ip4-method" -#define NM_MODEM_IP6_METHOD "ip6-method" -#define NM_MODEM_IP_TIMEOUT "ip-timeout" +#define NM_MODEM_IP_IFINDEX "ip-ifindex" #define NM_MODEM_STATE "state" #define NM_MODEM_DEVICE_ID "device-id" #define NM_MODEM_SIM_ID "sim-id" @@ -129,7 +126,7 @@ typedef struct { gboolean (*complete_connection) (NMModem *modem, NMConnection *connection, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error); NMActStageReturn (*act_stage1_prepare) (NMModem *modem, @@ -167,13 +164,21 @@ GType nm_modem_get_type (void); const char *nm_modem_get_path (NMModem *modem); const char *nm_modem_get_uid (NMModem *modem); const char *nm_modem_get_control_port (NMModem *modem); -const char *nm_modem_get_data_port (NMModem *modem); +int nm_modem_get_ip_ifindex (NMModem *modem); 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); +gboolean nm_modem_set_data_port (NMModem *self, + NMPlatform *platform, + const char *data_port, + NMModemIPMethod ip4_method, + NMModemIPMethod ip6_method, + guint timeout, + GError **error); + gboolean nm_modem_owns_port (NMModem *modem, const char *iface); void nm_modem_get_capabilities (NMModem *self, @@ -184,7 +189,7 @@ gboolean nm_modem_check_connection_compatible (NMModem *self, NMConnection *conn gboolean nm_modem_complete_connection (NMModem *self, NMConnection *connection, - const GSList *existing_connections, + NMConnection *const*existing_connections, GError **error); void nm_modem_get_route_parameters (NMModem *self, diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c index 663102de..a561b58a 100644 --- a/src/devices/wwan/nm-wwan-factory.c +++ b/src/devices/wwan/nm-wwan-factory.c @@ -80,7 +80,7 @@ modem_added_cb (NMModemManager *manager, { NMWwanFactory *self = NM_WWAN_FACTORY (user_data); NMDevice *device; - const char *driver, *port; + const char *driver; /* Do nothing if the modem was consumed by some other plugin */ if (nm_device_factory_emit_component_added (NM_DEVICE_FACTORY (self), G_OBJECT (modem))) @@ -93,10 +93,8 @@ modem_added_cb (NMModemManager *manager, * by the Bluetooth code during the connection process. */ if (driver && strstr (driver, "bluetooth")) { - port = nm_modem_get_data_port (modem); - if (!port) - port = nm_modem_get_control_port (modem); - nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)", port); + nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)", + nm_modem_get_control_port (modem)); return; } @@ -107,7 +105,6 @@ modem_added_cb (NMModemManager *manager, g_object_unref (device); } - static NMDevice * create_device (NMDeviceFactory *factory, const char *iface, diff --git a/src/dhcp/meson.build b/src/dhcp/meson.build new file mode 100644 index 00000000..289a16ca --- /dev/null +++ b/src/dhcp/meson.build @@ -0,0 +1,22 @@ +name = 'nm-dhcp-helper' + +cflags = [ + '-DG_LOG_DOMAIN="@0@"'.format(name), + '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_GLIB', + '-DNMRUNDIR="@0@"'.format(nm_pkgrundir), +] + +executable( + name, + name + '.c', + dependencies: nm_core_dep, + c_args: cflags, + link_args: ldflags_linker_script_binary, + link_depends: linker_script_binary, + install: true, + install_dir: nm_libexecdir +) + +if enable_tests + subdir('tests') +endif diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c index ea3938d6..ba517606 100644 --- a/src/dhcp/nm-dhcp-client.c +++ b/src/dhcp/nm-dhcp-client.c @@ -52,23 +52,24 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; NM_GOBJECT_PROPERTIES_DEFINE_BASE ( - PROP_MULTI_IDX, PROP_ADDR_FAMILY, + PROP_FLAGS, + PROP_HWADDR, PROP_IFACE, PROP_IFINDEX, - PROP_HWADDR, - PROP_UUID, - PROP_ROUTE_TABLE, + PROP_MULTI_IDX, PROP_ROUTE_METRIC, + PROP_ROUTE_TABLE, PROP_TIMEOUT, + PROP_UUID, ); typedef struct _NMDhcpClientPrivate { NMDedupMultiIndex *multi_idx; char * iface; - GByteArray * hwaddr; + GBytes * hwaddr; char * uuid; - GByteArray * duid; + GBytes * duid; GBytes * client_id; char * hostname; pid_t pid; @@ -138,7 +139,7 @@ nm_dhcp_client_get_uuid (NMDhcpClient *self) return NM_DHCP_CLIENT_GET_PRIVATE (self)->uuid; } -const GByteArray * +GBytes * nm_dhcp_client_get_duid (NMDhcpClient *self) { g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); @@ -146,7 +147,7 @@ nm_dhcp_client_get_duid (NMDhcpClient *self) return NM_DHCP_CLIENT_GET_PRIVATE (self)->duid; } -const GByteArray * +GBytes * nm_dhcp_client_get_hw_addr (NMDhcpClient *self) { g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); @@ -238,20 +239,6 @@ nm_dhcp_client_set_client_id_bin (NMDhcpClient *self, _set_client_id (self, b, TRUE); } -void -nm_dhcp_client_set_client_id_str (NMDhcpClient *self, - const char *dhcp_client_id) -{ - g_return_if_fail (NM_IS_DHCP_CLIENT (self)); - g_return_if_fail (!dhcp_client_id || dhcp_client_id[0]); - - _set_client_id (self, - dhcp_client_id - ? nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id) - : NULL, - TRUE); -} - const char * nm_dhcp_client_get_hostname (NMDhcpClient *self) { @@ -260,6 +247,14 @@ nm_dhcp_client_get_hostname (NMDhcpClient *self) return NM_DHCP_CLIENT_GET_PRIVATE (self)->hostname; } +gboolean +nm_dhcp_client_get_info_only (NMDhcpClient *self) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->info_only; +} + gboolean nm_dhcp_client_get_use_fqdn (NMDhcpClient *self) { @@ -339,13 +334,13 @@ nm_dhcp_client_stop_pid (pid_t pid, const char *iface) g_return_if_fail (pid > 1); - nm_utils_kill_child_sync (pid, SIGTERM, LOGD_DHCP, name ? name : "dhcp-client", NULL, + nm_utils_kill_child_sync (pid, SIGTERM, LOGD_DHCP, name ?: "dhcp-client", NULL, 1000 / 2, 1000 / 20); g_free (name); } static void -stop (NMDhcpClient *self, gboolean release, const GByteArray *duid) +stop (NMDhcpClient *self, gboolean release, GBytes *duid) { NMDhcpClientPrivate *priv; @@ -359,32 +354,30 @@ stop (NMDhcpClient *self, gboolean release, const GByteArray *duid) nm_dhcp_client_stop_pid (priv->pid, priv->iface); } priv->pid = -1; - priv->info_only = FALSE; } void nm_dhcp_client_set_state (NMDhcpClient *self, NMDhcpState new_state, - GObject *ip_config, + NMIPConfig *ip_config, GHashTable *options) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); gs_free char *event_id = NULL; + if (new_state == NM_DHCP_STATE_BOUND) { + g_return_if_fail (NM_IS_IP_CONFIG (ip_config, priv->addr_family)); + g_return_if_fail (options); + } else { + g_return_if_fail (!ip_config); + g_return_if_fail (!options); + } + if (new_state >= NM_DHCP_STATE_BOUND) timeout_cleanup (self); if (new_state >= NM_DHCP_STATE_TIMEOUT) watch_cleanup (self); - if (new_state == NM_DHCP_STATE_BOUND) { - g_assert ( (priv->addr_family == AF_INET && NM_IS_IP4_CONFIG (ip_config)) - || (priv->addr_family == AF_INET6 && NM_IS_IP6_CONFIG (ip_config))); - g_assert (options); - } else { - g_assert (ip_config == NULL); - g_assert (options == NULL); - } - /* The client may send same-state transitions for RENEW/REBIND events and * the lease may have changed, so handle same-state transitions for the * BOUND state. Ignore same-state transitions for other events since @@ -492,10 +485,9 @@ nm_dhcp_client_watch_child (NMDhcpClient *self, pid_t pid) gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self, - const char *dhcp_client_id, + GBytes *client_id, const char *dhcp_anycast_addr, const char *hostname, - gboolean use_fqdn, const char *last_ip4_address) { NMDhcpClientPrivate *priv; @@ -512,19 +504,19 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self, else _LOGI ("activation: beginning transaction (timeout in %u seconds)", (guint) priv->timeout); - nm_dhcp_client_set_client_id_str (self, dhcp_client_id); + nm_dhcp_client_set_client_id (self, client_id); g_clear_pointer (&priv->hostname, g_free); priv->hostname = g_strdup (hostname); - priv->use_fqdn = use_fqdn; return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_anycast_addr, last_ip4_address); } -static GByteArray * +static GBytes * generate_duid_from_machine_id (void) { - GByteArray *duid; + const int DUID_SIZE = 18; + guint8 *duid_buffer; GChecksum *sum; guint8 buffer[32]; /* SHA256 digest size */ gsize sumlen = sizeof (buffer); @@ -532,6 +524,7 @@ generate_duid_from_machine_id (void) uuid_t uuid; gs_free char *machine_id_s = NULL; gs_free char *str = NULL; + GBytes *duid; machine_id_s = nm_utils_machine_id_read (); if (nm_utils_machine_id_parse (machine_id_s, uuid)) { @@ -554,36 +547,31 @@ generate_duid_from_machine_id (void) * u16: type (DUID-UUID = 4) * u8[16]: UUID bytes */ - duid = g_byte_array_sized_new (18); - g_byte_array_append (duid, (guint8 *) &duid_type, sizeof (duid_type)); + duid_buffer = g_malloc (DUID_SIZE); + + G_STATIC_ASSERT_EXPR (sizeof (duid_type) == 2); + memcpy (&duid_buffer[0], &duid_type, 2); /* Since SHA256 is 256 bits, but UUID is 128 bits, we just take the first * 128 bits of the SHA256 as the DUID-UUID. */ - g_byte_array_append (duid, buffer, 16); + memcpy (&duid_buffer[2], buffer, 16); + duid = g_bytes_new_take (duid_buffer, DUID_SIZE); nm_log_dbg (LOGD_DHCP, "dhcp: generated DUID %s", (str = nm_dhcp_utils_duid_to_string (duid))); return duid; } -static GByteArray * +static GBytes * get_duid (NMDhcpClient *self) { - static GByteArray *duid = NULL; - GByteArray *copy = NULL; + static GBytes *duid = NULL; - if (G_UNLIKELY (duid == NULL)) { + if (G_UNLIKELY (!duid)) duid = generate_duid_from_machine_id (); - g_assert (duid); - } - if (G_LIKELY (duid)) { - copy = g_byte_array_sized_new (duid->len); - g_byte_array_append (copy, duid->data, duid->len); - } - - return copy; + return g_bytes_ref (duid); } gboolean @@ -591,7 +579,6 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, const char *hostname, - gboolean info_only, NMSettingIP6ConfigPrivacy privacy, guint needed_prefixes) { @@ -616,8 +603,6 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self, g_clear_pointer (&priv->hostname, g_free); priv->hostname = g_strdup (hostname); - priv->info_only = info_only; - if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) _LOGI ("activation: beginning transaction (no timeout)"); else @@ -626,7 +611,6 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self, return NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self, dhcp_anycast_addr, ll_addr, - info_only, privacy, priv->duid, needed_prefixes); @@ -675,8 +659,10 @@ nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name) out: if (remove (pid_file) == -1) { - nm_log_dbg (LOGD_DHCP, "dhcp: could not remove pid file \"%s\": %d (%s)", - pid_file, errno, g_strerror (errno)); + int errsv = errno; + + nm_log_dbg (LOGD_DHCP, "dhcp: could not remove pid file \"%s\": %s (%d)", + pid_file, g_strerror (errsv), errsv); } } @@ -751,14 +737,6 @@ maybe_add_option (NMDhcpClient *self, GVariant *value) { char *str_value = NULL; - const char **p; - static const char *ignored_keys[] = { - "interface", - "pid", - "reason", - "dhcp_message_type", - NULL - }; g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE_BYTESTRING)); @@ -766,10 +744,11 @@ maybe_add_option (NMDhcpClient *self, return; /* Filter out stuff that's not actually new DHCP options */ - for (p = ignored_keys; *p; p++) { - if (!strcmp (*p, key)) - return; - } + if (NM_IN_STRSET (key, "interface", + "pid", + "reason", + "dhcp_message_type")) + return; if (g_str_has_prefix (key, NEW_TAG)) key += NM_STRLEN (NEW_TAG); @@ -792,8 +771,8 @@ nm_dhcp_client_handle_event (gpointer unused, NMDhcpClientPrivate *priv; guint32 old_state; guint32 new_state; - GHashTable *str_options = NULL; - GObject *ip_config = NULL; + gs_unref_hashtable GHashTable *str_options = NULL; + gs_unref_object NMIPConfig *ip_config = NULL; NMPlatformIP6Address prefix = { 0, }; g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); @@ -811,8 +790,8 @@ nm_dhcp_client_handle_event (gpointer unused, old_state = priv->state; new_state = reason_to_state (self, priv->iface, reason); - _LOGD ("DHCP reason '%s' -> state '%s'", - reason, state_to_string (new_state)); + _LOGD ("DHCP state '%s' -> '%s' (reason: '%s')", + state_to_string (old_state), state_to_string (new_state), reason); if (new_state == NM_DHCP_STATE_BOUND) { GVariantIter iter; @@ -837,24 +816,24 @@ nm_dhcp_client_handle_event (gpointer unused, } /* Create the IP config */ - g_warn_if_fail (g_hash_table_size (str_options)); - if (g_hash_table_size (str_options)) { + if (g_hash_table_size (str_options) > 0) { if (priv->addr_family == AF_INET) { - ip_config = (GObject *) 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); + 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)); } else { prefix = nm_dhcp_utils_ip6_prefix_from_options (str_options); - ip_config = (GObject *) nm_dhcp_utils_ip6_config_from_options (nm_dhcp_client_get_multi_idx (self), - priv->ifindex, - priv->iface, - str_options, - priv->info_only); + ip_config = NM_IP_CONFIG_CAST (nm_dhcp_utils_ip6_config_from_options (nm_dhcp_client_get_multi_idx (self), + priv->ifindex, + priv->iface, + str_options, + priv->info_only)); } - } + } else + g_warn_if_reached (); } if (!IN6_IS_ADDR_UNSPECIFIED (&prefix.address)) { @@ -866,7 +845,8 @@ nm_dhcp_client_handle_event (gpointer unused, &prefix); } else { /* Fail if no valid IP config was received */ - if (new_state == NM_DHCP_STATE_BOUND && ip_config == NULL) { + if ( new_state == NM_DHCP_STATE_BOUND + && !ip_config) { _LOGW ("client bound but IP config not received"); new_state = NM_DHCP_STATE_FAIL; g_clear_pointer (&str_options, g_hash_table_unref); @@ -875,10 +855,6 @@ nm_dhcp_client_handle_event (gpointer unused, nm_dhcp_client_set_state (self, new_state, ip_config, str_options); } - if (str_options) - g_hash_table_destroy (str_options); - g_clear_object (&ip_config); - return TRUE; } @@ -923,8 +899,16 @@ set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE ((NMDhcpClient *) object); + guint flags; switch (prop_id) { + case PROP_FLAGS: + /* construct-only */ + flags = g_value_get_uint (value); + nm_assert ((flags & ~((guint) (NM_DHCP_CLIENT_FLAGS_INFO_ONLY | NM_DHCP_CLIENT_FLAGS_USE_FQDN))) == 0); + priv->info_only = NM_FLAGS_HAS (flags, NM_DHCP_CLIENT_FLAGS_INFO_ONLY); + priv->use_fqdn = NM_FLAGS_HAS (flags, NM_DHCP_CLIENT_FLAGS_USE_FQDN); + break; case PROP_MULTI_IDX: /* construct-only */ priv->multi_idx = g_value_get_pointer (value); @@ -983,6 +967,8 @@ nm_dhcp_client_init (NMDhcpClient *self) priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_DHCP_CLIENT, NMDhcpClientPrivate); self->_priv = priv; + c_list_init (&self->dhcp_client_lst); + priv->pid = -1; } @@ -997,6 +983,8 @@ dispose (GObject *object) * the DHCP client. */ + nm_assert (c_list_is_empty (&self->dhcp_client_lst)); + watch_cleanup (self); timeout_cleanup (self); @@ -1004,16 +992,8 @@ dispose (GObject *object) g_clear_pointer (&priv->hostname, g_free); g_clear_pointer (&priv->uuid, g_free); g_clear_pointer (&priv->client_id, g_bytes_unref); - - if (priv->hwaddr) { - g_byte_array_free (priv->hwaddr, TRUE); - priv->hwaddr = NULL; - } - - if (priv->duid) { - g_byte_array_free (priv->duid, TRUE); - priv->duid = NULL; - } + g_clear_pointer (&priv->hwaddr, g_bytes_unref); + g_clear_pointer (&priv->duid, g_bytes_unref); G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object); @@ -1054,7 +1034,7 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) obj_properties[PROP_HWADDR] = g_param_spec_boxed (NM_DHCP_CLIENT_HWADDR, "", "", - G_TYPE_BYTE_ARRAY, + G_TYPE_BYTES, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); @@ -1088,6 +1068,12 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) 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); + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); signals[SIGNAL_STATE_CHANGED] = diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h index 2c634168..111b063b 100644 --- a/src/dhcp/nm-dhcp-client.h +++ b/src/dhcp/nm-dhcp-client.h @@ -34,20 +34,20 @@ #define NM_IS_DHCP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DHCP_CLIENT)) #define NM_DHCP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass)) -#define NM_DHCP_CLIENT_INTERFACE "iface" -#define NM_DHCP_CLIENT_ADDR_FAMILY "addr-family" -#define NM_DHCP_CLIENT_IFINDEX "ifindex" -#define NM_DHCP_CLIENT_HWADDR "hwaddr" -#define NM_DHCP_CLIENT_UUID "uuid" -#define NM_DHCP_CLIENT_ROUTE_TABLE "route-table" +#define NM_DHCP_CLIENT_ADDR_FAMILY "addr-family" +#define NM_DHCP_CLIENT_FLAGS "flags" +#define NM_DHCP_CLIENT_HWADDR "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_ROUTE_METRIC "route-metric" -#define NM_DHCP_CLIENT_TIMEOUT "timeout" -#define NM_DHCP_CLIENT_MULTI_IDX "multi-idx" +#define NM_DHCP_CLIENT_ROUTE_TABLE "route-table" +#define NM_DHCP_CLIENT_TIMEOUT "timeout" +#define NM_DHCP_CLIENT_UUID "uuid" #define NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED "state-changed" #define NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED "prefix-delegated" - typedef enum { NM_DHCP_STATE_UNKNOWN = 0, NM_DHCP_STATE_BOUND, /* new lease or lease changed */ @@ -64,8 +64,14 @@ struct _NMDhcpClientPrivate; typedef struct { GObject parent; struct _NMDhcpClientPrivate *_priv; + CList dhcp_client_lst; } NMDhcpClient; +typedef enum { + NM_DHCP_CLIENT_FLAGS_INFO_ONLY = (1LL << 0), + NM_DHCP_CLIENT_FLAGS_USE_FQDN = (1LL << 1), +} NMDhcpClientFlags; + typedef struct { GObjectClass parent; @@ -78,14 +84,13 @@ typedef struct { gboolean (*ip6_start) (NMDhcpClient *self, const char *anycast_addr, const struct in6_addr *ll_addr, - gboolean info_only, NMSettingIP6ConfigPrivacy privacy, - const GByteArray *duid, + GBytes *duid, guint needed_prefixes); void (*stop) (NMDhcpClient *self, gboolean release, - const GByteArray *duid); + GBytes *duid); /** * get_duid: @@ -96,7 +101,7 @@ typedef struct { * representation of the DUID. If no DUID is found, %NULL should be * returned. */ - GByteArray * (*get_duid) (NMDhcpClient *self); + GBytes *(*get_duid) (NMDhcpClient *self); /* Signals */ void (*state_changed) (NMDhcpClient *self, @@ -119,9 +124,9 @@ int nm_dhcp_client_get_ifindex (NMDhcpClient *self); const char *nm_dhcp_client_get_uuid (NMDhcpClient *self); -const GByteArray *nm_dhcp_client_get_duid (NMDhcpClient *self); +GBytes *nm_dhcp_client_get_duid (NMDhcpClient *self); -const GByteArray *nm_dhcp_client_get_hw_addr (NMDhcpClient *self); +GBytes *nm_dhcp_client_get_hw_addr (NMDhcpClient *self); guint32 nm_dhcp_client_get_route_table (NMDhcpClient *self); @@ -133,20 +138,20 @@ GBytes *nm_dhcp_client_get_client_id (NMDhcpClient *self); const char *nm_dhcp_client_get_hostname (NMDhcpClient *self); +gboolean nm_dhcp_client_get_info_only (NMDhcpClient *self); + gboolean nm_dhcp_client_get_use_fqdn (NMDhcpClient *self); gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self, - const char *dhcp_client_id, + GBytes *client_id, const char *dhcp_anycast_addr, const char *hostname, - gboolean use_fqdn, const char *last_ip4_address); gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, const char *hostname, - gboolean info_only, NMSettingIP6ConfigPrivacy privacy, guint needed_prefixes); @@ -163,7 +168,7 @@ void nm_dhcp_client_watch_child (NMDhcpClient *self, pid_t pid); void nm_dhcp_client_set_state (NMDhcpClient *self, NMDhcpState new_state, - GObject *ip_config, /* NMIP4Config or NMIP6Config */ + NMIPConfig *ip_config, GHashTable *options); /* str:str hash */ gboolean nm_dhcp_client_handle_event (gpointer unused, @@ -179,8 +184,6 @@ void nm_dhcp_client_set_client_id_bin (NMDhcpClient *self, guint8 type, const guint8 *client_id, gsize len); -void nm_dhcp_client_set_client_id_str (NMDhcpClient *self, - const char *dhcp_client_id); /***************************************************************************** * Client data @@ -190,13 +193,6 @@ typedef struct { GType (*get_type)(void); const char *name; const char *(*get_path) (void); - GSList *(*get_lease_ip_configs) (struct _NMDedupMultiIndex *multi_idx, - int addr_family, - const char *iface, - int ifindex, - const char *uuid, - guint32 route_table, - guint32 route_metric); } NMDhcpClientFactory; extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon; diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c index 4df90d76..16a76d23 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.c +++ b/src/dhcp/nm-dhcp-dhclient-utils.c @@ -95,7 +95,6 @@ grab_request_options (GPtrArray *store, const char* line) return end; } - static void add_hostname4 (GString *str, const char *hostname, gboolean use_fqdn) { @@ -125,7 +124,7 @@ add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean * as long as all the characters are printable. */ for (i = 1; (p[0] == 0) && i < l; i++) { - if (!g_ascii_isprint (p[i])) + if (!g_ascii_isprint (p[i]) || p[i] == '\\' || p[i] == '"') break; } @@ -138,8 +137,9 @@ add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean g_string_append_printf (str, "%02x", (guint8) p[i]); } } else { - /* Printable; just add to the line minus the 'type' */ + /* Printable; just add to the line with type 0 */ g_string_append_c (str, '"'); + g_string_append (str, "\\x00"); g_string_append_len (str, p + 1, l - 1); g_string_append_c (str, '"'); } @@ -177,31 +177,60 @@ read_client_id (const char *str) { gs_free char *s = NULL; char *p; + int i = 0, j = 0; nm_assert (!strncmp (str, CLIENTID_TAG, NM_STRLEN (CLIENTID_TAG))); - str += NM_STRLEN (CLIENTID_TAG); + + if (!g_ascii_isspace (*str)) + return NULL; while (g_ascii_isspace (*str)) str++; if (*str == '"') { + /* Parse string literal with escape sequences */ s = g_strdup (str + 1); p = strrchr (s, '"'); if (p) *p = '\0'; else return NULL; - } else - s = g_strdup (str); + if (!s[0]) + return NULL; + + while (s[i]) { + if ( s[i] == '\\' + && s[i + 1] == 'x' + && g_ascii_isxdigit (s[i + 2]) + && g_ascii_isxdigit (s[i + 3])) { + s[j++] = (g_ascii_xdigit_value (s[i + 2]) << 4) + + g_ascii_xdigit_value (s[i + 3]); + i += 4; + continue; + } + if ( s[i] == '\\' + && s[i + 1] >= '0' && s[i + 1] <= '7' + && s[1 + 2] >= '0' && s[i + 2] <= '7' + && s[1 + 3] >= '0' && s[i + 3] <= '7') { + s[j++] = ((s[i + 1] - '0') << 6) + + ((s[i + 2] - '0') << 3) + + ( s[i + 3] - '0'); + i += 4; + continue; + } + s[j++] = s[i++]; + } + return g_bytes_new_take (g_steal_pointer (&s), j); + } + + /* Otherwise, try to read a hexadecimal sequence */ + s = g_strdup (str); g_strchomp (s); if (s[strlen (s) - 1] == ';') s[strlen (s) - 1] = '\0'; - if (!s[0]) - return NULL; - - return nm_dhcp_utils_client_id_string_to_bytes (s); + return nm_utils_hexstr2bin (s); } GBytes * @@ -279,6 +308,7 @@ nm_dhcp_dhclient_create_config (const char *interface, g_return_val_if_fail (!anycast_addr || nm_utils_hwaddr_valid (anycast_addr, ETH_ALEN), NULL); g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), NULL); + nm_assert (!out_new_client_id || !*out_new_client_id); new_contents = g_string_new (_("# Created by NetworkManager\n")); fqdn_opts = g_ptr_array_sized_new (5); @@ -332,6 +362,8 @@ nm_dhcp_dhclient_create_config (const char *interface, continue; /* Otherwise capture and return the existing client id */ + if (out_new_client_id) + g_clear_pointer (out_new_client_id, g_bytes_unref); NM_SET_OUT (out_new_client_id, read_client_id (p)); } @@ -444,14 +476,20 @@ nm_dhcp_dhclient_create_config (const char *interface, /* Roughly follow what dhclient's quotify_buf() and pretty_escape() functions do */ char * -nm_dhcp_dhclient_escape_duid (const GByteArray *duid) +nm_dhcp_dhclient_escape_duid (GBytes *duid) { char *escaped; - const guint8 *s = duid->data; + const guint8 *s, *s0; + gsize len; char *d; - d = escaped = g_malloc0 ((duid->len * 4) + 1); - while (s < (duid->data + duid->len)) { + g_return_val_if_fail (duid, NULL); + + s0 = g_bytes_get_data (duid, &len); + s = s0; + + d = escaped = g_malloc ((len * 4) + 1); + while (s < (s0 + len)) { if (!g_ascii_isprint (*s)) { *d++ = '\\'; *d++ = '0' + ((*s >> 6) & 0x7); @@ -465,6 +503,7 @@ nm_dhcp_dhclient_escape_duid (const GByteArray *duid) } else *d++ = *s++; } + *d++ = '\0'; return escaped; } @@ -476,7 +515,7 @@ isoctal (const guint8 *p) && p[2] >= '0' && p[2] <= '7'); } -GByteArray * +GBytes * nm_dhcp_dhclient_unescape_duid (const char *duid) { GByteArray *unescaped; @@ -507,7 +546,7 @@ nm_dhcp_dhclient_unescape_duid (const char *duid) g_byte_array_append (unescaped, &p[i], 1); } - return unescaped; + return g_byte_array_free_to_bytes (unescaped); error: g_byte_array_free (unescaped, TRUE); @@ -516,10 +555,10 @@ error: #define DUID_PREFIX "default-duid \"" -GByteArray * +GBytes * nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error) { - GByteArray *duid = NULL; + GBytes *duid = NULL; char *contents; char **line, **split, *p, *e; @@ -603,259 +642,3 @@ nm_dhcp_dhclient_save_duid (const char *leasefile, g_string_free (s, TRUE); return success; } - -static void -add_lease_option (GHashTable *hash, char *line) -{ - char *spc; - size_t len; - - /* Find the space after "option" */ - spc = strchr (line, ' '); - if (!spc) - return; - - /* Find the option tag's data, which is after the second space */ - if (g_str_has_prefix (line, "option ")) { - while (g_ascii_isspace (*spc)) - spc++; - spc = strchr (spc + 1, ' '); - if (!spc) - return; - } - - /* Split the line at the space */ - *spc = '\0'; - spc++; - - /* Kill the ';' at the end of the line, if any */ - len = strlen (spc); - if (*(spc + len - 1) == ';') - *(spc + len - 1) = '\0'; - - /* Strip leading quote */ - while (g_ascii_isspace (*spc)) - spc++; - if (*spc == '"') - spc++; - - /* Strip trailing quote */ - len = strlen (spc); - if (len > 0 && spc[len - 1] == '"') - spc[len - 1] = '\0'; - - if (spc[0]) - g_hash_table_insert (hash, g_strdup (line), g_strdup (spc)); -} - -#define LEASE_INVALID G_MININT64 -static GTimeSpan -lease_validity_span (const char *str_expire, GDateTime *now) -{ - GDateTime *expire = NULL; - struct tm expire_tm; - GTimeSpan span; - - g_return_val_if_fail (now != NULL, LEASE_INVALID); - g_return_val_if_fail (str_expire != NULL, LEASE_INVALID); - - /* Skip initial number (day of week?) */ - if (!isdigit (*str_expire++)) - return LEASE_INVALID; - if (!isspace (*str_expire++)) - return LEASE_INVALID; - /* Read lease expiration (in UTC) */ - if (!strptime (str_expire, "%t%Y/%m/%d %H:%M:%S", &expire_tm)) - return LEASE_INVALID; - - expire = g_date_time_new_utc (expire_tm.tm_year + 1900, - expire_tm.tm_mon + 1, - expire_tm.tm_mday, - expire_tm.tm_hour, - expire_tm.tm_min, - expire_tm.tm_sec); - if (!expire) - return LEASE_INVALID; - - span = g_date_time_difference (expire, now); - g_date_time_unref (expire); - - /* GDateTime only supports a range of less then 10000 years, so span can - * not overflow or be equal to LEASE_INVALID */ - return span; -} - -/** - * nm_dhcp_dhclient_read_lease_ip_configs: - * @multi_idx: the multi index instance for the ip config object - * @addr_family: whether to read IPv4 or IPv6 leases - * @iface: the interface name to match leases with - * @ifindex: interface index of @iface - * @route_table: the route table for the default route. - * @route_metric: the route metric for the default route. - * @contents: the contents of a dhclient leasefile - * @now: the current UTC date/time; pass %NULL to automatically use current - * UTC time. Testcases may need a different value for 'now' - * - * Reads dhclient leases from @contents and parses them into either - * #NMIP4Config or #NMIP6Config objects depending on the value of @addr_family. - * - * Returns: a #GSList of #NMIP4Config objects (if @addr_family is %AF_INET) or a list of - * #NMIP6Config objects (if @addr_family is %AF_INET6) containing the lease data. - */ -GSList * -nm_dhcp_dhclient_read_lease_ip_configs (NMDedupMultiIndex *multi_idx, - int addr_family, - const char *iface, - int ifindex, - guint32 route_table, - guint32 route_metric, - const char *contents, - GDateTime *now) -{ - GSList *parsed = NULL, *iter, *leases = NULL; - char **line, **split = NULL; - GHashTable *hash = NULL; - gint32 now_monotonic_ts; - - g_return_val_if_fail (contents != NULL, NULL); - nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - - split = g_strsplit_set (contents, "\n\r", -1); - if (!split) - return NULL; - - for (line = split; line && *line; line++) { - *line = g_strstrip (*line); - - if (*line[0] == '#') { - /* Comment */ - } else if (!strcmp (*line, "}")) { - /* Lease ends */ - parsed = g_slist_append (parsed, hash); - hash = NULL; - } else if (!strcmp (*line, "lease {")) { - /* Beginning of a new lease */ - if (hash) { - /* Ignore malformed lease that doesn't end before new one starts */ - g_hash_table_destroy (hash); - } - - hash = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); - } else if (hash && strlen (*line)) - add_lease_option (hash, *line); - } - g_strfreev (split); - - /* Check if the last lease in the file was properly ended */ - if (hash) { - /* Ignore malformed lease that doesn't end before new one starts */ - g_hash_table_destroy (hash); - hash = NULL; - } - - if (now) - g_date_time_ref (now); - else - now = g_date_time_new_now_utc (); - now_monotonic_ts = nm_utils_get_monotonic_timestamp_s (); - - for (iter = parsed; iter; iter = g_slist_next (iter)) { - NMIP4Config *ip4; - NMPlatformIP4Address address; - const char *value; - GTimeSpan expiry; - guint32 tmp, gw = 0; - - hash = iter->data; - - /* Make sure this lease is for the interface we want */ - value = g_hash_table_lookup (hash, "interface"); - if (!value || strcmp (value, iface)) - continue; - - value = g_hash_table_lookup (hash, "expire"); - if (!value) - continue; - expiry = lease_validity_span (value, now); - if (expiry == LEASE_INVALID) - continue; - - /* scale expiry to seconds (and CLAMP into the range of guint32) */ - expiry = CLAMP (expiry / G_TIME_SPAN_SECOND, 0, NM_PLATFORM_LIFETIME_PERMANENT-1); - if (expiry <= 0) { - /* the address is already expired. Don't even add it. */ - continue; - } - - memset (&address, 0, sizeof (address)); - - /* IP4 address */ - value = g_hash_table_lookup (hash, "fixed-address"); - if (!value) - continue; - if (!inet_pton (AF_INET, value, &address.address)) - continue; - address.peer_address = address.address; - - /* Gateway */ - value = g_hash_table_lookup (hash, "option routers"); - if (!value) - continue; - if (!inet_pton (AF_INET, value, &gw)) - continue; - - /* Netmask */ - value = g_hash_table_lookup (hash, "option subnet-mask"); - if (value && inet_pton (AF_INET, value, &tmp)) - address.plen = nm_utils_ip4_netmask_to_prefix (tmp); - - /* Get default netmask for the IP according to appropriate class. */ - if (!address.plen) - address.plen = _nm_utils_ip4_get_default_prefix (address.address); - - address.timestamp = now_monotonic_ts; - address.lifetime = address.preferred = expiry; - address.addr_source = NM_IP_CONFIG_SOURCE_DHCP; - - ip4 = nm_ip4_config_new (multi_idx, ifindex); - nm_ip4_config_add_address (ip4, &address); - - { - const NMPlatformIP4Route r = { - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, - .gateway = gw, - .table_coerced = nm_platform_route_table_coerce (route_table), - .metric = route_metric, - }; - - nm_ip4_config_add_route (ip4, &r, NULL); - } - - value = g_hash_table_lookup (hash, "option domain-name-servers"); - if (value) { - char **dns, **dns_iter; - - dns = g_strsplit_set (value, ",", -1); - for (dns_iter = dns; dns_iter && *dns_iter; dns_iter++) { - if (inet_pton (AF_INET, *dns_iter, &tmp)) - nm_ip4_config_add_nameserver (ip4, tmp); - } - if (dns) - g_strfreev (dns); - } - - value = g_hash_table_lookup (hash, "option domain-name"); - if (value && value[0]) - nm_ip4_config_add_domain (ip4, value); - - /* FIXME: static routes */ - - leases = g_slist_append (leases, ip4); - } - - g_date_time_unref (now); - g_slist_free_full (parsed, (GDestroyNotify) g_hash_table_destroy); - return leases; -} - diff --git a/src/dhcp/nm-dhcp-dhclient-utils.h b/src/dhcp/nm-dhcp-dhclient-utils.h index 94de1963..fab9196a 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.h +++ b/src/dhcp/nm-dhcp-dhclient-utils.h @@ -33,25 +33,16 @@ char *nm_dhcp_dhclient_create_config (const char *interface, const char *orig_contents, GBytes **out_new_client_id); -char *nm_dhcp_dhclient_escape_duid (const GByteArray *duid); +char *nm_dhcp_dhclient_escape_duid (GBytes *duid); -GByteArray *nm_dhcp_dhclient_unescape_duid (const char *duid); +GBytes *nm_dhcp_dhclient_unescape_duid (const char *duid); -GByteArray *nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error); +GBytes *nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error); gboolean nm_dhcp_dhclient_save_duid (const char *leasefile, const char *escaped_duid, GError **error); -GSList *nm_dhcp_dhclient_read_lease_ip_configs (struct _NMDedupMultiIndex *multi_idx, - int addr_family, - const char *iface, - int ifindex, - guint32 route_table, - guint32 route_metric, - const char *contents, - GDateTime *now); - GBytes *nm_dhcp_dhclient_get_client_id_from_config_file (const char *path); #endif /* __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__ */ diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c index 74d920a8..93306ddd 100644 --- a/src/dhcp/nm-dhcp-dhclient.c +++ b/src/dhcp/nm-dhcp-dhclient.c @@ -127,11 +127,13 @@ get_dhclient_leasefile (int addr_family, _addr_family_to_path_part (addr_family), uuid, iface); - if (out_preferred_path) - *out_preferred_path = g_strdup (path); - if (g_file_test (path, G_FILE_TEST_EXISTS)) + if (g_file_test (path, G_FILE_TEST_EXISTS)) { + NM_SET_OUT (out_preferred_path, g_strdup (path)); return path; + } + + NM_SET_OUT (out_preferred_path, g_steal_pointer (&path)); /* If the leasefile we're looking for doesn't exist yet in the new location * (eg, /var/lib/NetworkManager) then look in old locations to maintain @@ -158,32 +160,6 @@ get_dhclient_leasefile (int addr_family, return NULL; } -static GSList * -nm_dhcp_dhclient_get_lease_ip_configs (NMDedupMultiIndex *multi_idx, - int addr_family, - const char *iface, - int ifindex, - const char *uuid, - guint32 route_table, - guint32 route_metric) -{ - gs_free char *contents = NULL; - gs_free char *leasefile = NULL; - - leasefile = get_dhclient_leasefile (addr_family, iface, uuid, NULL); - if (!leasefile) - return NULL; - - if ( g_file_test (leasefile, G_FILE_TEST_EXISTS) - && g_file_get_contents (leasefile, &contents, NULL, NULL) - && contents - && contents[0]) { - return nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, addr_family, iface, ifindex, - route_table, route_metric, contents, NULL); - } - return NULL; -} - static gboolean merge_dhclient_config (NMDhcpDhclient *self, int addr_family, @@ -288,7 +264,6 @@ find_existing_config (NMDhcpDhclient *self, int addr_family, const char *iface, return NULL; } - /* NM provides interface-specific options; thus the same dhclient config * file cannot be used since DHCP transactions can happen in parallel. * Since some distros don't have default per-interface dhclient config files, @@ -334,31 +309,33 @@ create_dhclient_config (NMDhcpDhclient *self, return new; } - static gboolean dhclient_start (NMDhcpClient *client, const char *mode_opt, - const GByteArray *duid, + GBytes *duid, gboolean release, pid_t *out_pid, int prefixes) { NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client); NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self); - GPtrArray *argv = NULL; + gs_unref_ptrarray GPtrArray *argv = NULL; pid_t pid; GError *error = NULL; - const char *iface, *uuid, *system_bus_address, *dhclient_path = NULL; - char *binary_name, *cmd_str, *pid_file = NULL, *system_bus_address_env = NULL; - int addr_family; - gboolean success; - char *escaped, *preferred_leasefile_path = 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); - g_return_val_if_fail (priv->pid_file == NULL, FALSE); + g_return_val_if_fail (!priv->pid_file, FALSE); - iface = nm_dhcp_client_get_iface (client); - uuid = nm_dhcp_client_get_uuid (client); - addr_family = nm_dhcp_client_get_addr_family (client); + NM_SET_OUT (out_pid, 0); dhclient_path = nm_dhcp_dhclient_get_path (); if (!dhclient_path) { @@ -366,6 +343,9 @@ dhclient_start (NMDhcpClient *client, return FALSE; } + iface = nm_dhcp_client_get_iface (client); + uuid = nm_dhcp_client_get_uuid (client); + pid_file = g_strdup_printf (RUNSTATEDIR "/dhclient%s-%s.pid", _addr_family_to_path_part (addr_family), iface); @@ -377,18 +357,18 @@ dhclient_start (NMDhcpClient *client, if (release) { /* release doesn't use the pidfile after killing an old client */ - g_free (pid_file); - pid_file = NULL; + nm_clear_g_free (&pid_file); } g_free (priv->lease_file); priv->lease_file = get_dhclient_leasefile (addr_family, iface, uuid, &preferred_leasefile_path); + nm_assert (preferred_leasefile_path); if (!priv->lease_file) { /* No existing leasefile, dhclient will create one at the preferred path */ - priv->lease_file = g_strdup (preferred_leasefile_path); - } else if (g_strcmp0 (priv->lease_file, preferred_leasefile_path) != 0) { - GFile *src = g_file_new_for_path (priv->lease_file); - GFile *dst = g_file_new_for_path (preferred_leasefile_path); + priv->lease_file = g_steal_pointer (&preferred_leasefile_path); + } else if (!nm_streq0 (priv->lease_file, preferred_leasefile_path)) { + gs_unref_object GFile *src = g_file_new_for_path (priv->lease_file); + gs_unref_object GFile *dst = g_file_new_for_path (preferred_leasefile_path); /* Try to copy the existing leasefile to the preferred location */ if (g_file_copy (src, dst, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error)) { @@ -402,19 +382,16 @@ dhclient_start (NMDhcpClient *client, error->message); g_clear_error (&error); } - g_object_unref (src); - g_object_unref (dst); } - g_free (preferred_leasefile_path); /* Save the DUID to the leasefile dhclient will actually use */ if (addr_family == AF_INET6) { + gs_free char *escaped = NULL; + escaped = nm_dhcp_dhclient_escape_duid (duid); - success = nm_dhcp_dhclient_save_duid (priv->lease_file, escaped, &error); - g_free (escaped); - if (!success) { + if (!nm_dhcp_dhclient_save_duid (priv->lease_file, escaped, &error)) { _LOGW ("failed to save DUID to %s: %s", priv->lease_file, error->message); - g_free (pid_file); + g_clear_error (&error); return FALSE; } } @@ -439,19 +416,19 @@ dhclient_start (NMDhcpClient *client, while (prefixes--) g_ptr_array_add (argv, (gpointer) "-P"); } - g_ptr_array_add (argv, (gpointer) "-sf"); /* Set script file */ + g_ptr_array_add (argv, (gpointer) "-sf"); /* Set script file */ g_ptr_array_add (argv, (gpointer) nm_dhcp_helper_path); if (pid_file) { - g_ptr_array_add (argv, (gpointer) "-pf"); /* Set pid file */ + g_ptr_array_add (argv, (gpointer) "-pf"); /* Set pid file */ g_ptr_array_add (argv, (gpointer) pid_file); } - g_ptr_array_add (argv, (gpointer) "-lf"); /* Set lease file */ + g_ptr_array_add (argv, (gpointer) "-lf"); /* Set lease file */ g_ptr_array_add (argv, (gpointer) priv->lease_file); if (priv->conf_file) { - g_ptr_array_add (argv, (gpointer) "-cf"); /* Set interface config file */ + g_ptr_array_add (argv, (gpointer) "-cf"); /* Set interface config file */ g_ptr_array_add (argv, (gpointer) priv->conf_file); } @@ -466,34 +443,29 @@ dhclient_start (NMDhcpClient *client, g_ptr_array_add (argv, (gpointer) system_bus_address_env); } - g_ptr_array_add (argv, (gpointer) iface); g_ptr_array_add (argv, NULL); - cmd_str = g_strjoinv (" ", (gchar **) argv->pdata); - _LOGD ("running: %s", cmd_str); - g_free (cmd_str); - - if (g_spawn_async (NULL, (char **) argv->pdata, NULL, - G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, - nm_utils_setpgid, NULL, &pid, &error)) { - g_assert (pid > 0); - _LOGI ("dhclient started with pid %d", pid); - if (release == FALSE) - nm_dhcp_client_watch_child (client, pid); - priv->pid_file = pid_file; - } else { + _LOGD ("running: %s", + (cmd_str = g_strjoinv (" ", (gchar **) argv->pdata))); + + if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, + G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, + nm_utils_setpgid, NULL, &pid, &error)) { _LOGW ("dhclient failed to start: '%s'", error->message); g_error_free (error); - g_free (pid_file); + return FALSE; } - if (out_pid) - *out_pid = pid; + _LOGI ("dhclient started with pid %lld", (long long int) pid); + + if (!release) + nm_dhcp_client_watch_child (client, pid); - g_ptr_array_free (argv, TRUE); - g_free (system_bus_address_env); - return pid > 0 ? TRUE : FALSE; + priv->pid_file = g_steal_pointer (&pid_file); + + NM_SET_OUT (out_pid, pid); + return TRUE; } static gboolean @@ -518,8 +490,10 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last priv->conf_file = create_dhclient_config (self, AF_INET, iface, uuid, client_id, dhcp_anycast_addr, hostname, timeout, use_fqdn, &new_client_id); if (priv->conf_file) { - if (new_client_id) + if (new_client_id) { + nm_assert (!client_id); nm_dhcp_client_set_client_id (client, new_client_id); + } success = dhclient_start (client, NULL, NULL, FALSE, NULL, 0); } else _LOGW ("error creating dhclient configuration file"); @@ -531,9 +505,8 @@ static gboolean ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, - gboolean info_only, NMSettingIP6ConfigPrivacy privacy, - const GByteArray *duid, + GBytes *duid, guint needed_prefixes) { NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client); @@ -553,26 +526,31 @@ ip6_start (NMDhcpClient *client, return FALSE; } - return dhclient_start (client, info_only ? "-S" : "-N", duid, FALSE, NULL, needed_prefixes); + return dhclient_start (client, + nm_dhcp_client_get_info_only (NM_DHCP_CLIENT (self)) + ? "-S" + : "-N", + duid, FALSE, NULL, needed_prefixes); } static void -stop (NMDhcpClient *client, gboolean release, const GByteArray *duid) +stop (NMDhcpClient *client, gboolean release, GBytes *duid) { NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client); NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self); - /* Chain up to parent */ NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release, duid); if (priv->conf_file) if (remove (priv->conf_file) == -1) _LOGD ("could not remove dhcp config file \"%s\": %d (%s)", priv->conf_file, errno, g_strerror (errno)); if (priv->pid_file) { - if (remove (priv->pid_file) == -1) - _LOGD ("could not remove dhcp pid file \"%s\": %d (%s)", priv->pid_file, errno, g_strerror (errno)); - g_free (priv->pid_file); - priv->pid_file = NULL; + if (remove (priv->pid_file) == -1) { + int errsv = errno; + + _LOGD ("could not remove dhcp pid file \"%s\": %s (%d)", priv->pid_file, g_strerror (errsv), errsv); + } + nm_clear_g_free (&priv->pid_file); } if (release) { @@ -603,12 +581,12 @@ state_changed (NMDhcpClient *client, nm_dhcp_client_set_client_id (client, client_id); } -static GByteArray * +static GBytes * get_duid (NMDhcpClient *client) { NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client); NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self); - GByteArray *duid = NULL; + GBytes *duid = NULL; char *leasefile; GError *error = NULL; @@ -629,7 +607,7 @@ get_duid (NMDhcpClient *client) g_free (leasefile); } - if (!duid && priv->def_leasefile) { + if (!duid) { /* Otherwise read the default machine-wide DUID */ _LOGD ("looking for default DUID in '%s'", priv->def_leasefile); duid = nm_dhcp_dhclient_read_duid (priv->def_leasefile, &error); @@ -642,36 +620,30 @@ get_duid (NMDhcpClient *client) } /* return our DUID, otherwise let the parent class make a default DUID */ - return duid ? duid : NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->get_duid (client); + return duid ?: NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->get_duid (client); } /*****************************************************************************/ -static const char *def_leasefiles[] = { - SYSCONFDIR "/dhclient6.leases", - LOCALSTATEDIR "/lib/dhcp/dhclient6.leases", - LOCALSTATEDIR "/lib/dhclient/dhclient6.leases", - NULL -}; - static void nm_dhcp_dhclient_init (NMDhcpDhclient *self) { + static const char *const FILES[] = { + SYSCONFDIR "/dhclient6.leases", /* default */ + LOCALSTATEDIR "/lib/dhcp/dhclient6.leases", + LOCALSTATEDIR "/lib/dhclient/dhclient6.leases", + }; NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self); - const char **iter = &def_leasefiles[0]; + int i; - while (iter && *iter) { - if (g_file_test (*iter, G_FILE_TEST_EXISTS)) { - priv->def_leasefile = *iter; + priv->def_leasefile = FILES[0]; + for (i = 0; i < G_N_ELEMENTS (FILES); i++) { + if (g_file_test (FILES[i], G_FILE_TEST_EXISTS)) { + priv->def_leasefile = FILES[i]; break; } - iter++; } - /* Fallback option */ - if (!priv->def_leasefile) - priv->def_leasefile = SYSCONFDIR "/dhclient6.leases"; - priv->dhcp_listener = g_object_ref (nm_dhcp_listener_get ()); g_signal_connect (priv->dhcp_listener, NM_DHCP_LISTENER_EVENT, @@ -717,7 +689,6 @@ const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient = { .name = "dhclient", .get_type = nm_dhcp_dhclient_get_type, .get_path = nm_dhcp_dhclient_get_path, - .get_lease_ip_configs = nm_dhcp_dhclient_get_lease_ip_configs, }; #endif /* WITH_DHCLIENT */ diff --git a/src/dhcp/nm-dhcp-dhcpcanon.c b/src/dhcp/nm-dhcp-dhcpcanon.c index d7ddd194..12aa57aa 100644 --- a/src/dhcp/nm-dhcp-dhcpcanon.c +++ b/src/dhcp/nm-dhcp-dhcpcanon.c @@ -76,11 +76,10 @@ nm_dhcp_dhcpcanon_get_path (void) return nm_utils_find_helper ("dhcpcanon", DHCPCANON_PATH, NULL); } - static gboolean dhcpcanon_start (NMDhcpClient *client, const char *mode_opt, - const GByteArray *duid, + GBytes *duid, gboolean release, pid_t *out_pid, int prefixes) @@ -118,16 +117,16 @@ dhcpcanon_start (NMDhcpClient *client, argv = g_ptr_array_new (); g_ptr_array_add (argv, (gpointer) dhcpcanon_path); - g_ptr_array_add (argv, (gpointer) "-sf"); /* Set script file */ + g_ptr_array_add (argv, (gpointer) "-sf"); /* Set script file */ g_ptr_array_add (argv, (gpointer) nm_dhcp_helper_path); if (pid_file) { - g_ptr_array_add (argv, (gpointer) "-pf"); /* Set pid file */ + g_ptr_array_add (argv, (gpointer) "-pf"); /* Set pid file */ g_ptr_array_add (argv, (gpointer) pid_file); } if (priv->conf_file) { - g_ptr_array_add (argv, (gpointer) "-cf"); /* Set interface config file */ + g_ptr_array_add (argv, (gpointer) "-cf"); /* Set interface config file */ g_ptr_array_add (argv, (gpointer) priv->conf_file); } @@ -142,7 +141,6 @@ dhcpcanon_start (NMDhcpClient *client, g_ptr_array_add (argv, (gpointer) system_bus_address_env); } - g_ptr_array_add (argv, (gpointer) iface); g_ptr_array_add (argv, NULL); @@ -179,9 +177,8 @@ static gboolean ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, - gboolean info_only, NMSettingIP6ConfigPrivacy privacy, - const GByteArray *duid, + GBytes *duid, guint needed_prefixes) { NMDhcpDhcpcanon *self = NM_DHCP_DHCPCANON (client); @@ -190,7 +187,7 @@ ip6_start (NMDhcpClient *client, return FALSE; } static void -stop (NMDhcpClient *client, gboolean release, const GByteArray *duid) +stop (NMDhcpClient *client, gboolean release, GBytes *duid) { NMDhcpDhcpcanon *self = NM_DHCP_DHCPCANON (client); NMDhcpDhcpcanonPrivate *priv = NM_DHCP_DHCPCANON_GET_PRIVATE (self); @@ -266,7 +263,6 @@ const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon = { .name = "dhcpcanon", .get_type = nm_dhcp_dhcpcanon_get_type, .get_path = nm_dhcp_dhcpcanon_get_path, - .get_lease_ip_configs = NULL, }; #endif /* WITH_DHCPCANON */ diff --git a/src/dhcp/nm-dhcp-dhcpcd.c b/src/dhcp/nm-dhcp-dhcpcd.c index 66a31acf..c4bcb084 100644 --- a/src/dhcp/nm-dhcp-dhcpcd.c +++ b/src/dhcp/nm-dhcp-dhcpcd.c @@ -114,18 +114,18 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last argv = g_ptr_array_new (); g_ptr_array_add (argv, (gpointer) dhcpcd_path); - g_ptr_array_add (argv, (gpointer) "-B"); /* Don't background on lease (disable fork()) */ + g_ptr_array_add (argv, (gpointer) "-B"); /* Don't background on lease (disable fork()) */ - g_ptr_array_add (argv, (gpointer) "-K"); /* Disable built-in carrier detection */ + g_ptr_array_add (argv, (gpointer) "-K"); /* Disable built-in carrier detection */ - g_ptr_array_add (argv, (gpointer) "-L"); /* Disable built-in IPv4LL */ + g_ptr_array_add (argv, (gpointer) "-L"); /* Disable built-in IPv4LL */ /* --noarp. Don't request or claim the address by ARP; this also disables IPv4LL. */ g_ptr_array_add (argv, (gpointer) "-A"); - g_ptr_array_add (argv, (gpointer) "-G"); /* Let NM handle routing */ + g_ptr_array_add (argv, (gpointer) "-G"); /* Let NM handle routing */ - g_ptr_array_add (argv, (gpointer) "-c"); /* Set script file */ + g_ptr_array_add (argv, (gpointer) "-c"); /* Set script file */ g_ptr_array_add (argv, (gpointer) nm_dhcp_helper_path); #ifdef DHCPCD_SUPPORTS_IPV6 @@ -177,9 +177,8 @@ static gboolean ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, - gboolean info_only, NMSettingIP6ConfigPrivacy privacy, - const GByteArray *duid, + GBytes *duid, guint needed_prefixes) { NMDhcpDhcpcd *self = NM_DHCP_DHCPCD (client); @@ -189,12 +188,11 @@ ip6_start (NMDhcpClient *client, } static void -stop (NMDhcpClient *client, gboolean release, const GByteArray *duid) +stop (NMDhcpClient *client, gboolean release, GBytes *duid) { NMDhcpDhcpcd *self = NM_DHCP_DHCPCD (client); NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (self); - /* Chain up to parent */ NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client, release, duid); if (priv->pid_file) { @@ -253,7 +251,6 @@ const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcd = { .name = "dhcpcd", .get_type = nm_dhcp_dhcpcd_get_type, .get_path = nm_dhcp_dhcpcd_get_path, - .get_lease_ip_configs = NULL, }; #endif /* WITH_DHCPCD */ diff --git a/src/dhcp/nm-dhcp-helper.c b/src/dhcp/nm-dhcp-helper.c index f50c5cec..8ea55061 100644 --- a/src/dhcp/nm-dhcp-helper.c +++ b/src/dhcp/nm-dhcp-helper.c @@ -134,8 +134,6 @@ main (int argc, char *argv[]) guint try_count = 0; gint64 time_end; - nm_g_type_init (); - /* FIXME: g_dbus_connection_new_for_address_sync() tries to connect to the socket in * non-blocking mode, which can easily fail with EAGAIN, causing the creation of the * socket to fail with "Could not connect: Resource temporarily unavailable". diff --git a/src/dhcp/nm-dhcp-listener.c b/src/dhcp/nm-dhcp-listener.c index 1cce5a1c..1770ead3 100644 --- a/src/dhcp/nm-dhcp-listener.c +++ b/src/dhcp/nm-dhcp-listener.c @@ -33,7 +33,7 @@ #include "nm-dhcp-client.h" #include "nm-dhcp-manager.h" #include "nm-core-internal.h" -#include "nm-bus-manager.h" +#include "nm-dbus-manager.h" #include "NetworkManagerUtils.h" #define PRIV_SOCK_PATH NMRUNDIR "/private-dhcp" @@ -60,7 +60,7 @@ const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4] = { /*****************************************************************************/ typedef struct { - NMBusManager * dbus_mgr; + NMDBusManager * dbus_mgr; gulong new_conn_id; gulong dis_conn_id; GHashTable * connections; @@ -160,7 +160,7 @@ _method_call_handle (NMDhcpListener *self, pid_str = get_option (options, "pid"); pid = _nm_utils_ascii_str_to_int64 (pid_str, 10, 0, G_MAXINT32, -1); if (pid == -1) { - _LOGW ("dhcp-event: couldn't convert PID '%s' to an integer", pid_str ? pid_str : "(null)"); + _LOGW ("dhcp-event: couldn't convert PID '%s' to an integer", pid_str ?: "(null)"); return; } @@ -192,70 +192,52 @@ _method_call (GDBusConnection *connection, { NMDhcpListener *self = NM_DHCP_LISTENER (user_data); - if (!nm_streq0 (interface_name, NM_DHCP_HELPER_SERVER_INTERFACE_NAME)) - g_return_if_reached (); - if (!nm_streq0 (method_name, NM_DHCP_HELPER_SERVER_METHOD_NOTIFY)) - g_return_if_reached (); - if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(a{sv})"))) - g_return_if_reached (); + if ( !nm_streq (interface_name, NM_DHCP_HELPER_SERVER_INTERFACE_NAME) + || !nm_streq (method_name, NM_DHCP_HELPER_SERVER_METHOD_NOTIFY)) { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method %s", + method_name); + return; + } _method_call_handle (self, parameters); - g_dbus_method_invocation_return_value (invocation, NULL); } +static GDBusInterfaceInfo *const interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO ( + NM_DHCP_HELPER_SERVER_INTERFACE_NAME, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( + NM_DEFINE_GDBUS_METHOD_INFO ( + NM_DHCP_HELPER_SERVER_METHOD_NOTIFY, + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("data", "a{sv}"), + ), + ), + ), +); + static guint _dbus_connection_register_object (NMDhcpListener *self, GDBusConnection *connection, GError **error) { - static GDBusArgInfo arg_info_notify_in = { - .ref_count = -1, - .name = "data", - .signature = "a{sv}", - .annotations = NULL, - }; - static GDBusArgInfo *arg_infos_notify[] = { - &arg_info_notify_in, - NULL, - }; - static GDBusMethodInfo method_info_notify = { - .ref_count = -1, - .name = NM_DHCP_HELPER_SERVER_METHOD_NOTIFY, - .in_args = arg_infos_notify, - .out_args = NULL, - .annotations = NULL, - }; - static GDBusMethodInfo *method_infos[] = { - &method_info_notify, - NULL, - }; - static GDBusInterfaceInfo interface_info = { - .ref_count = -1, - .name = NM_DHCP_HELPER_SERVER_INTERFACE_NAME, - .methods = method_infos, - .signals = NULL, - .properties = NULL, - .annotations = NULL, - }; - - static GDBusInterfaceVTable interface_vtable = { + static const GDBusInterfaceVTable interface_vtable = { .method_call = _method_call, - .get_property = NULL, - .set_property = NULL, }; return g_dbus_connection_register_object (connection, NM_DHCP_HELPER_SERVER_OBJECT_PATH, - &interface_info, - &interface_vtable, + interface_info, + NM_UNCONST_PTR (GDBusInterfaceVTable, &interface_vtable), self, NULL, error); } static void -new_connection_cb (NMBusManager *mgr, +new_connection_cb (NMDBusManager *mgr, GDBusConnection *connection, GDBusObjectManager *manager, NMDhcpListener *self) @@ -278,7 +260,7 @@ new_connection_cb (NMBusManager *mgr, } static void -dis_connection_cb (NMBusManager *mgr, +dis_connection_cb (NMDBusManager *mgr, GDBusConnection *connection, NMDhcpListener *self) { @@ -300,18 +282,18 @@ nm_dhcp_listener_init (NMDhcpListener *self) NMDhcpListenerPrivate *priv = NM_DHCP_LISTENER_GET_PRIVATE (self); /* Maps GDBusConnection :: signal-id */ - priv->connections = g_hash_table_new (NULL, NULL); + priv->connections = g_hash_table_new (nm_direct_hash, NULL); - priv->dbus_mgr = nm_bus_manager_get (); + priv->dbus_mgr = nm_dbus_manager_get (); /* Register the socket our DHCP clients will return lease info on */ - nm_bus_manager_private_server_register (priv->dbus_mgr, PRIV_SOCK_PATH, PRIV_SOCK_TAG); + nm_dbus_manager_private_server_register (priv->dbus_mgr, PRIV_SOCK_PATH, PRIV_SOCK_TAG); priv->new_conn_id = g_signal_connect (priv->dbus_mgr, - NM_BUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG, + NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG, G_CALLBACK (new_connection_cb), self); priv->dis_conn_id = g_signal_connect (priv->dbus_mgr, - NM_BUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "::" PRIV_SOCK_TAG, + NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "::" PRIV_SOCK_TAG, G_CALLBACK (dis_connection_cb), self); } diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c index f5c7c84b..aa40e803 100644 --- a/src/dhcp/nm-dhcp-manager.c +++ b/src/dhcp/nm-dhcp-manager.c @@ -43,8 +43,8 @@ typedef struct { const NMDhcpClientFactory *client_factory; - GHashTable * clients; - char * default_hostname; + char *default_hostname; + CList dhcp_client_lst_head; } NMDhcpManagerPrivate; struct _NMDhcpManager { @@ -98,21 +98,17 @@ static NMDhcpClient * get_client_for_ifindex (NMDhcpManager *manager, int addr_family, int ifindex) { NMDhcpManagerPrivate *priv; - GHashTableIter iter; - gpointer value; + NMDhcpClient *client; g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL); g_return_val_if_fail (ifindex > 0, NULL); priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); - g_hash_table_iter_init (&iter, priv->clients); - while (g_hash_table_iter_next (&iter, NULL, &value)) { - NMDhcpClient *candidate = NM_DHCP_CLIENT (value); - - if ( nm_dhcp_client_get_ifindex (candidate) == ifindex - && nm_dhcp_client_get_addr_family (candidate) == addr_family) - return candidate; + c_list_for_each_entry (client, &priv->dhcp_client_lst_head, dhcp_client_lst) { + if ( nm_dhcp_client_get_ifindex (client) == ifindex + && nm_dhcp_client_get_addr_family (client) == addr_family) + return client; } return NULL; @@ -129,13 +125,19 @@ static void remove_client (NMDhcpManager *self, NMDhcpClient *client) { g_signal_handlers_disconnect_by_func (client, client_state_changed, self); + c_list_unlink (&client->dhcp_client_lst); /* Stopping the client is left up to the controlling device * explicitly since we may want to quit NetworkManager but not terminate * the DHCP client. */ +} - g_hash_table_remove (NM_DHCP_MANAGER_GET_PRIVATE (self)->clients, client); +static void +remove_client_unref (NMDhcpManager *self, NMDhcpClient *client) +{ + remove_client (self, client); + g_object_unref (client); } static void @@ -147,7 +149,7 @@ client_state_changed (NMDhcpClient *client, NMDhcpManager *self) { if (state >= NM_DHCP_STATE_TIMEOUT) - remove_client (self, client); + remove_client_unref (self, client); } static NMDhcpClient * @@ -156,12 +158,12 @@ client_start (NMDhcpManager *self, NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, - const GByteArray *hwaddr, + GBytes *hwaddr, const char *uuid, guint32 route_table, guint32 route_metric, const struct in6_addr *ipv6_ll_addr, - const char *dhcp_client_id, + GBytes *dhcp_client_id, guint32 timeout, const char *dhcp_anycast_addr, const char *hostname, @@ -179,23 +181,21 @@ client_start (NMDhcpManager *self, g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), 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); priv = NM_DHCP_MANAGER_GET_PRIVATE (self); - /* Ensure we have a usable DHCP client */ if (!priv->client_factory) return NULL; /* Kill any old client instance */ client = get_client_for_ifindex (self, addr_family, ifindex); if (client) { - g_object_ref (client); remove_client (self, client); nm_dhcp_client_stop (client, FALSE); g_object_unref (client); } - /* And make a new one */ client = g_object_new (priv->client_factory->get_type (), NM_DHCP_CLIENT_MULTI_IDX, multi_idx, NM_DHCP_CLIENT_ADDR_FAMILY, addr_family, @@ -206,21 +206,26 @@ client_start (NMDhcpManager *self, 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_FLAGS, (guint) (0 + | (hostname_use_fqdn ? NM_DHCP_CLIENT_FLAGS_USE_FQDN : 0) + | (info_only ? NM_DHCP_CLIENT_FLAGS_INFO_ONLY : 0) + ), NULL); - g_hash_table_insert (NM_DHCP_MANAGER_GET_PRIVATE (self)->clients, client, g_object_ref (client)); + nm_assert (client && c_list_is_empty (&client->dhcp_client_lst)); + c_list_link_tail (&priv->dhcp_client_lst_head, &client->dhcp_client_lst); g_signal_connect (client, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (client_state_changed), self); if (addr_family == AF_INET) - success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, hostname_use_fqdn, last_ip4_address); + success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, last_ip4_address); else - success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, ipv6_ll_addr, hostname, info_only, privacy, needed_prefixes); + success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, ipv6_ll_addr, hostname, privacy, needed_prefixes); if (!success) { - remove_client (self, client); - client = NULL; + remove_client_unref (self, client); + return NULL; } - return client; + return g_object_ref (client); } /* Caller owns a reference to the NMDhcpClient on return */ @@ -229,14 +234,14 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self, NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, - const GByteArray *hwaddr, + GBytes *hwaddr, const char *uuid, guint32 route_table, guint32 route_metric, gboolean send_hostname, const char *dhcp_hostname, const char *dhcp_fqdn, - const char *dhcp_client_id, + GBytes *dhcp_client_id, guint32 timeout, const char *dhcp_anycast_addr, const char *last_ip_address) @@ -285,7 +290,7 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self, NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, - const GByteArray *hwaddr, + GBytes *hwaddr, const struct in6_addr *ll_addr, const char *uuid, guint32 route_table, @@ -306,7 +311,7 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self, if (send_hostname) { /* Always prefer the explicit dhcp-hostname if given */ - hostname = dhcp_hostname ? dhcp_hostname : priv->default_hostname; + hostname = dhcp_hostname ?: priv->default_hostname; } return client_start (self, AF_INET6, multi_idx, iface, ifindex, hwaddr, uuid, route_table, route_metric, ll_addr, @@ -328,31 +333,6 @@ nm_dhcp_manager_set_default_hostname (NMDhcpManager *manager, const char *hostna priv->default_hostname = g_strdup (hostname); } -GSList * -nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self, - NMDedupMultiIndex *multi_idx, - int addr_family, - const char *iface, - int ifindex, - const char *uuid, - guint32 route_table, - guint32 route_metric) -{ - NMDhcpManagerPrivate *priv; - - g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); - g_return_val_if_fail (iface != NULL, NULL); - g_return_val_if_fail (ifindex >= -1, NULL); - g_return_val_if_fail (uuid != NULL, NULL); - g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), NULL); - - priv = NM_DHCP_MANAGER_GET_PRIVATE (self); - if ( priv->client_factory - && priv->client_factory->get_lease_ip_configs) - return priv->client_factory->get_lease_ip_configs (multi_idx, addr_family, iface, ifindex, uuid, route_table, route_metric); - return NULL; -} - const char * nm_dhcp_manager_get_config (NMDhcpManager *self) { @@ -378,6 +358,8 @@ nm_dhcp_manager_init (NMDhcpManager *self) int i; const NMDhcpClientFactory *client_factory = NULL; + c_list_init (&priv->dhcp_client_lst_head); + for (i = 0; i < G_N_ELEMENTS (_nm_dhcp_manager_factories); i++) { const NMDhcpClientFactory *f = _nm_dhcp_manager_factories[i]; @@ -429,38 +411,21 @@ nm_dhcp_manager_init (NMDhcpManager *self) nm_log_info (LOGD_DHCP, "dhcp-init: Using DHCP client '%s'", client_factory->name); priv->client_factory = client_factory; - priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal, - NULL, - (GDestroyNotify) g_object_unref); } static void dispose (GObject *object) { - NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE ((NMDhcpManager *) object); - GList *values, *iter; - - if (priv->clients) { - values = g_hash_table_get_values (priv->clients); - for (iter = values; iter; iter = g_list_next (iter)) - remove_client (NM_DHCP_MANAGER (object), NM_DHCP_CLIENT (iter->data)); - g_list_free (values); - } - - G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->dispose (object); -} - -static void -finalize (GObject *object) -{ - NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE ((NMDhcpManager *) object); + NMDhcpManager *self = NM_DHCP_MANAGER (object); + NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self); + NMDhcpClient *client, *client_safe; - g_free (priv->default_hostname); + c_list_for_each_entry_safe (client, client_safe, &priv->dhcp_client_lst_head, dhcp_client_lst) + remove_client_unref (self, client); - if (priv->clients) - g_hash_table_destroy (priv->clients); + G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->dispose (object); - G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->finalize (object); + nm_clear_g_free (&priv->default_hostname); } static void @@ -468,6 +433,5 @@ nm_dhcp_manager_class_init (NMDhcpManagerClass *manager_class) { GObjectClass *object_class = G_OBJECT_CLASS (manager_class); - object_class->finalize = finalize; object_class->dispose = dispose; } diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h index 078117ff..f8a7e31d 100644 --- a/src/dhcp/nm-dhcp-manager.h +++ b/src/dhcp/nm-dhcp-manager.h @@ -49,14 +49,14 @@ NMDhcpClient * nm_dhcp_manager_start_ip4 (NMDhcpManager *manager, struct _NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, - const GByteArray *hwaddr, + GBytes *hwaddr, const char *uuid, guint32 route_table, guint32 route_metric, gboolean send_hostname, const char *dhcp_hostname, const char *dhcp_fqdn, - const char *dhcp_client_id, + GBytes *dhcp_client_id, guint32 timeout, const char *dhcp_anycast_addr, const char *last_ip_address); @@ -65,7 +65,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *manager, struct _NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, - const GByteArray *hwaddr, + GBytes *hwaddr, const struct in6_addr *ll_addr, const char *uuid, guint32 route_table, @@ -78,15 +78,6 @@ NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *manager, NMSettingIP6ConfigPrivacy privacy, guint needed_prefixes); -GSList * nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self, - struct _NMDedupMultiIndex *multi_idx, - int addr_family, - const char *iface, - int ifindex, - const char *uuid, - guint32 route_table, - guint32 route_metric); - /* For testing only */ extern const char* nm_dhcp_helper_path; diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c index f79b7cb1..2d0202bb 100644 --- a/src/dhcp/nm-dhcp-systemd.c +++ b/src/dhcp/nm-dhcp-systemd.c @@ -29,6 +29,7 @@ #include #include "nm-utils/nm-dedup-multi.h" +#include "nm-utils/unaligned.h" #include "nm-utils.h" #include "nm-dhcp-utils.h" @@ -60,8 +61,7 @@ typedef struct { guint request_count; - gboolean privacy; - gboolean info_only; + bool privacy:1; } NMDhcpSystemdPrivate; struct _NMDhcpSystemd { @@ -451,36 +451,6 @@ get_leasefile_path (int addr_family, const char *iface, const char *uuid) iface); } -static GSList * -nm_dhcp_systemd_get_lease_ip_configs (NMDedupMultiIndex *multi_idx, - int addr_family, - const char *iface, - int ifindex, - const char *uuid, - guint32 route_table, - guint32 route_metric) -{ - GSList *leases = NULL; - gs_free char *path = NULL; - sd_dhcp_lease *lease = NULL; - NMIP4Config *ip4_config; - int r; - - if (addr_family != AF_INET) - return NULL; - - path = get_leasefile_path (addr_family, iface, uuid); - r = dhcp_lease_load (&lease, path); - if (r == 0 && lease) { - ip4_config = lease_to_ip4_config (multi_idx, iface, ifindex, lease, NULL, route_table, route_metric, FALSE, NULL); - if (ip4_config) - leases = g_slist_append (leases, ip4_config); - sd_dhcp_lease_unref (lease); - } - - return leases; -} - /*****************************************************************************/ static void @@ -543,7 +513,7 @@ bound4_handle (NMDhcpSystemd *self) nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_BOUND, - G_OBJECT (ip4_config), + NM_IP_CONFIG_CAST (ip4_config), options); } else { _LOGW ("%s", error->message); @@ -584,14 +554,16 @@ dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data) } static guint16 -get_arp_type (const GByteArray *hwaddr) +get_arp_type (GBytes *hwaddr) { - if (hwaddr->len == ETH_ALEN) + switch (g_bytes_get_size (hwaddr)) { + case ETH_ALEN: return ARPHRD_ETHER; - else if (hwaddr->len == INFINIBAND_ALEN) + case INFINIBAND_ALEN: return ARPHRD_INFINIBAND; - else + default: return ARPHRD_NONE; + } } static gboolean @@ -600,7 +572,7 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); const char *iface = nm_dhcp_client_get_iface (client); - const GByteArray *hwaddr; + GBytes *hwaddr; sd_dhcp_lease *lease = NULL; GBytes *override_client_id; const uint8_t *client_id = NULL; @@ -609,7 +581,6 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last const char *hostname; int r, i; gboolean success = FALSE; - guint16 arp_type; g_assert (priv->client4 == NULL); g_assert (priv->client6 == NULL); @@ -633,16 +604,14 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last hwaddr = nm_dhcp_client_get_hw_addr (client); if (hwaddr) { - arp_type= get_arp_type (hwaddr); - if (arp_type == ARPHRD_NONE) { - _LOGW ("failed to determine ARP type"); - goto error; - } + const uint8_t *data; + gsize len; + data = g_bytes_get_data (hwaddr, &len); r = sd_dhcp_client_set_mac (priv->client4, - hwaddr->data, - hwaddr->len, - arp_type); + data, + len, + get_arp_type (hwaddr)); if (r < 0) { _LOGW ("failed to set MAC address (%d)", r); goto error; @@ -661,12 +630,6 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last goto error; } - r = sd_dhcp_client_set_request_broadcast (priv->client4, true); - if (r < 0) { - _LOGW ("failed to enable broadcast mode (%d)", r); - goto error; - } - dhcp_lease_load (&lease, priv->lease_file); if (last_ip4_address) @@ -704,7 +667,6 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last } } - /* Add requested options */ for (i = 0; dhcp4_requests[i].name; i++) { if (dhcp4_requests[i].include) @@ -854,13 +816,13 @@ bound6_handle (NMDhcpSystemd *self) lease, options, TRUE, - priv->info_only, + nm_dhcp_client_get_info_only (NM_DHCP_CLIENT (self)), &error); if (ip6_config) { nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_BOUND, - G_OBJECT (ip6_config), + NM_IP_CONFIG_CAST (ip6_config), options); } else { _LOGW ("%s", error->message); @@ -900,24 +862,29 @@ static gboolean ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, - gboolean info_only, NMSettingIP6ConfigPrivacy privacy, - const GByteArray *duid, + GBytes *duid, guint needed_prefixes) { NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); const char *iface = nm_dhcp_client_get_iface (client); - const GByteArray *hwaddr; + GBytes *hwaddr; + const char *hostname; int r, i; + const guint8 *duid_arr; + gsize duid_len; g_assert (priv->client4 == NULL); g_assert (priv->client6 == NULL); g_return_val_if_fail (duid != NULL, FALSE); + duid_arr = g_bytes_get_data (duid, &duid_len); + if (!duid_arr || duid_len < 2) + g_return_val_if_reached (FALSE); + g_free (priv->lease_file); priv->lease_file = get_leasefile_path (AF_INET6, iface, nm_dhcp_client_get_uuid (client)); - priv->info_only = info_only; r = sd_dhcp6_client_new (&priv->client6); if (r < 0) { @@ -932,16 +899,13 @@ ip6_start (NMDhcpClient *client, _LOGT ("dhcp-client6: set %p", priv->client6); - if (info_only) - sd_dhcp6_client_set_information_request (priv->client6, 1); + if (nm_dhcp_client_get_info_only (client)) + sd_dhcp6_client_set_information_request (priv->client6, 1); - /* NM stores the entire DUID which includes the uint16 "type", while systemd - * wants the type passed separately from the following data. - */ r = sd_dhcp6_client_set_duid (priv->client6, - ntohs (((const guint16 *) duid->data)[0]), - duid->data + 2, - duid->len - 2); + unaligned_read_be16 (&duid_arr[0]), + &duid_arr[2], + duid_len - 2); if (r < 0) { _LOGW ("failed to set DUID (%d)", r); return FALSE; @@ -955,9 +919,13 @@ ip6_start (NMDhcpClient *client, hwaddr = nm_dhcp_client_get_hw_addr (client); if (hwaddr) { + const uint8_t *data; + gsize len; + + data = g_bytes_get_data (hwaddr, &len); r = sd_dhcp6_client_set_mac (priv->client6, - hwaddr->data, - hwaddr->len, + data, + len, get_arp_type (hwaddr)); if (r < 0) { _LOGW ("failed to set MAC address (%d)", r); @@ -989,6 +957,13 @@ ip6_start (NMDhcpClient *client, goto error; } + hostname = nm_dhcp_client_get_hostname (client); + r = sd_dhcp6_client_set_fqdn (priv->client6, hostname); + if (r < 0) { + _LOGW ("failed to set DHCP hostname to '%s' (%d)", hostname, r); + goto error; + } + r = sd_dhcp6_client_start (priv->client6); if (r < 0) { _LOGW ("failed to start client (%d)", r); @@ -1006,12 +981,14 @@ error: } static void -stop (NMDhcpClient *client, gboolean release, const GByteArray *duid) +stop (NMDhcpClient *client, gboolean release, GBytes *duid) { NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); int r = 0; + NM_DHCP_CLIENT_CLASS (nm_dhcp_systemd_parent_class)->stop (client, release, duid); + _LOGT ("dhcp-client%d: stop %p", priv->client4 ? '4' : '6', priv->client4 ? (gpointer) priv->client4 : (gpointer) priv->client6); @@ -1074,5 +1051,4 @@ const NMDhcpClientFactory _nm_dhcp_client_factory_internal = { .name = "internal", .get_type = nm_dhcp_systemd_get_type, .get_path = NULL, - .get_lease_ip_configs = nm_dhcp_systemd_get_lease_ip_configs, }; diff --git a/src/dhcp/nm-dhcp-utils.c b/src/dhcp/nm-dhcp-utils.c index 50ca2abe..9185a135 100644 --- a/src/dhcp/nm-dhcp-utils.c +++ b/src/dhcp/nm-dhcp-utils.c @@ -721,11 +721,15 @@ error: } char * -nm_dhcp_utils_duid_to_string (const GByteArray *duid) +nm_dhcp_utils_duid_to_string (GBytes *duid) { + gconstpointer data; + gsize len; + g_return_val_if_fail (duid != NULL, NULL); - return _nm_utils_bin2str (duid->data, duid->len, FALSE); + data = g_bytes_get_data (duid, &len); + return _nm_utils_bin2str (data, len, FALSE); } /** diff --git a/src/dhcp/nm-dhcp-utils.h b/src/dhcp/nm-dhcp-utils.h index 32140f48..5c127bd1 100644 --- a/src/dhcp/nm-dhcp-utils.h +++ b/src/dhcp/nm-dhcp-utils.h @@ -39,7 +39,7 @@ NMIP6Config *nm_dhcp_utils_ip6_config_from_options (struct _NMDedupMultiIndex *m NMPlatformIP6Address nm_dhcp_utils_ip6_prefix_from_options (GHashTable *options); -char * nm_dhcp_utils_duid_to_string (const GByteArray *duid); +char *nm_dhcp_utils_duid_to_string (GBytes *duid); GBytes * nm_dhcp_utils_client_id_string_to_bytes (const char *client_id); diff --git a/src/dhcp/tests/leases/basic.leases b/src/dhcp/tests/leases/basic.leases deleted file mode 100644 index 703d9247..00000000 --- a/src/dhcp/tests/leases/basic.leases +++ /dev/null @@ -1,31 +0,0 @@ -lease { - interface "wlan0"; - fixed-address 192.168.1.180; - option subnet-mask 255.255.255.0; - option routers 192.168.1.1; - option dhcp-lease-time 600; - option dhcp-message-type 5; - option domain-name-servers 192.168.1.1; - option dhcp-server-identifier 192.168.1.1; - option broadcast-address 192.168.1.255; - renew 5 2013/11/01 19:56:15; - rebind 5 2013/11/01 20:00:44; - expire 5 2013/11/01 20:01:59; -} -lease { - interface "wlan0"; - fixed-address 10.77.52.141; - option subnet-mask 255.0.0.0; - option dhcp-lease-time 1200; - option routers 10.77.52.254; - option dhcp-message-type 5; - option dhcp-server-identifier 10.77.52.254; - option domain-name-servers 8.8.8.8,8.8.4.4; - option dhcp-renewal-time 600; - option dhcp-rebinding-time 1050; - option domain-name "morriesguest.local"; - renew 5 2013/11/01 20:01:08; - rebind 5 2013/11/01 20:05:00; - expire 5 2013/11/01 20:06:15; -} - diff --git a/src/dhcp/tests/leases/malformed1.leases b/src/dhcp/tests/leases/malformed1.leases deleted file mode 100644 index 401d982a..00000000 --- a/src/dhcp/tests/leases/malformed1.leases +++ /dev/null @@ -1,15 +0,0 @@ -# missing fixed-address option -lease { - interface "wlan0"; - option subnet-mask 255.255.255.0; - option routers 192.168.1.1; - option dhcp-lease-time 600; - option dhcp-message-type 5; - option domain-name-servers 192.168.1.1; - option dhcp-server-identifier 192.168.1.1; - option broadcast-address 192.168.1.255; - renew 5 2013/11/01 19:56:15; - rebind 5 2013/11/01 20:00:44; - expire 5 2013/11/01 20:01:59; -} - diff --git a/src/dhcp/tests/leases/malformed2.leases b/src/dhcp/tests/leases/malformed2.leases deleted file mode 100644 index adf5f6de..00000000 --- a/src/dhcp/tests/leases/malformed2.leases +++ /dev/null @@ -1,15 +0,0 @@ -# missing routers option -lease { - interface "wlan0"; - fixed-address 192.168.1.180; - option subnet-mask 255.255.255.0; - option dhcp-lease-time 600; - option dhcp-message-type 5; - option domain-name-servers 192.168.1.1; - option dhcp-server-identifier 192.168.1.1; - option broadcast-address 192.168.1.255; - renew 5 2013/11/01 19:56:15; - rebind 5 2013/11/01 20:00:44; - expire 5 2013/11/01 20:01:59; -} - diff --git a/src/dhcp/tests/leases/malformed3.leases b/src/dhcp/tests/leases/malformed3.leases deleted file mode 100644 index a2afc8b6..00000000 --- a/src/dhcp/tests/leases/malformed3.leases +++ /dev/null @@ -1,15 +0,0 @@ -# missing expire time -lease { - interface "wlan0"; - fixed-address 192.168.1.180; - option subnet-mask 255.255.255.0; - option routers 192.168.1.1; - option dhcp-lease-time 600; - option dhcp-message-type 5; - option domain-name-servers 192.168.1.1; - option dhcp-server-identifier 192.168.1.1; - option broadcast-address 192.168.1.255; - renew 5 2013/11/01 19:56:15; - rebind 5 2013/11/01 20:00:44; -} - diff --git a/src/dhcp/tests/meson.build b/src/dhcp/tests/meson.build new file mode 100644 index 00000000..9d8be427 --- /dev/null +++ b/src/dhcp/tests/meson.build @@ -0,0 +1,19 @@ +test_units = [ + 'test-dhcp-dhclient', + 'test-dhcp-utils' +] + +foreach test_unit: test_units + exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, + c_args: nm_build_cflags, + ) + + test( + 'dhcp/' + test_unit, + test_script, + args: test_args + [exe.full_path()] + ) +endforeach diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c index f2e1f321..a8284b23 100644 --- a/src/dhcp/tests/test-dhcp-dhclient.c +++ b/src/dhcp/tests/test-dhcp-dhclient.c @@ -36,11 +36,7 @@ #include "nm-test-utils-core.h" -#define DEBUG 1 - -static const int IFINDEX = 5; -static const guint32 ROUTE_TABLE = RT_TABLE_MAIN; -static const guint32 ROUTE_METRIC = 100; +#define TEST_DIR NM_BUILD_SRCDIR"/src/dhcp/tests" static void test_config (const char *orig, @@ -154,7 +150,7 @@ test_override_client_id (void) static const char *quote_client_id_expected = \ "# Created by NetworkManager\n" "\n" - "send dhcp-client-identifier \"1234\"; # added by NetworkManager\n" + "send dhcp-client-identifier \"\\x00abcd\"; # added by NetworkManager\n" "\n" "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n" "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n" @@ -172,7 +168,65 @@ test_quote_client_id (void) { test_config (NULL, quote_client_id_expected, AF_INET, NULL, 0, FALSE, - "1234", + "abcd", + NULL, + "eth0", + NULL); +} + +/*****************************************************************************/ + +static const char *quote_client_id_expected_2 = \ + "# Created by NetworkManager\n" + "\n" + "send dhcp-client-identifier 00:61:5c:62:63; # added by NetworkManager\n" + "\n" + "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n" + "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n" + "option wpad code 252 = string;\n" + "\n" + "also request rfc3442-classless-static-routes;\n" + "also request ms-classless-static-routes;\n" + "also request static-routes;\n" + "also request wpad;\n" + "also request ntp-servers;\n" + "\n"; + +static void +test_quote_client_id_2 (void) +{ + test_config (NULL, quote_client_id_expected_2, + AF_INET, NULL, 0, FALSE, + "a\\bc", + NULL, + "eth0", + NULL); +} + +/*****************************************************************************/ + +static const char *hex_zero_client_id_expected = \ + "# Created by NetworkManager\n" + "\n" + "send dhcp-client-identifier 00:11:22:33; # added by NetworkManager\n" + "\n" + "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n" + "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n" + "option wpad code 252 = string;\n" + "\n" + "also request rfc3442-classless-static-routes;\n" + "also request ms-classless-static-routes;\n" + "also request static-routes;\n" + "also request wpad;\n" + "also request ntp-servers;\n" + "\n"; + +static void +test_hex_zero_client_id (void) +{ + test_config (NULL, hex_zero_client_id_expected, + AF_INET, NULL, 0, FALSE, + "00:11:22:33", NULL, "eth0", NULL); @@ -183,7 +237,7 @@ test_quote_client_id (void) static const char *ascii_client_id_expected = \ "# Created by NetworkManager\n" "\n" - "send dhcp-client-identifier \"qb:cd:ef:12:34:56\"; # added by NetworkManager\n" + "send dhcp-client-identifier \"\\x00qb:cd:ef:12:34:56\"; # added by NetworkManager\n" "\n" "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n" "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n" @@ -239,13 +293,13 @@ test_hex_single_client_id (void) /*****************************************************************************/ static const char *existing_hex_client_id_orig = \ - "send dhcp-client-identifier 00:30:04:20:7A:08;\n"; + "send dhcp-client-identifier 10:30:04:20:7A:08;\n"; static const char *existing_hex_client_id_expected = \ "# Created by NetworkManager\n" "# Merged from /path/to/dhclient.conf\n" "\n" - "send dhcp-client-identifier 00:30:04:20:7A:08;\n" + "send dhcp-client-identifier 10:30:04:20:7A:08;\n" "\n" "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n" "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n" @@ -262,7 +316,7 @@ static void test_existing_hex_client_id (void) { gs_unref_bytes GBytes *new_client_id = NULL; - const guint8 bytes[] = { 0x00, 0x30, 0x04,0x20, 0x7A, 0x08 }; + const guint8 bytes[] = { 0x10, 0x30, 0x04, 0x20, 0x7A, 0x08 }; new_client_id = g_bytes_new (bytes, sizeof (bytes)); test_config (existing_hex_client_id_orig, existing_hex_client_id_expected, @@ -275,16 +329,52 @@ test_existing_hex_client_id (void) /*****************************************************************************/ +static const char *existing_escaped_client_id_orig = \ + "send dhcp-client-identifier \"\\044test\\xfe\";\n"; + +static const char *existing_escaped_client_id_expected = \ + "# Created by NetworkManager\n" + "# Merged from /path/to/dhclient.conf\n" + "\n" + "send dhcp-client-identifier \"\\044test\\xfe\";\n" + "\n" + "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n" + "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n" + "option wpad code 252 = string;\n" + "\n" + "also request rfc3442-classless-static-routes;\n" + "also request ms-classless-static-routes;\n" + "also request static-routes;\n" + "also request wpad;\n" + "also request ntp-servers;\n" + "\n"; + +static void +test_existing_escaped_client_id (void) +{ + gs_unref_bytes GBytes *new_client_id = NULL; + + new_client_id = g_bytes_new ("$test\xfe", 6); + test_config (existing_escaped_client_id_orig, existing_escaped_client_id_expected, + AF_INET, NULL, 0, FALSE, + NULL, + new_client_id, + "eth0", + NULL); +} + +/*****************************************************************************/ + #define EACID "qb:cd:ef:12:34:56" static const char *existing_ascii_client_id_orig = \ - "send dhcp-client-identifier \"" EACID "\";\n"; + "send dhcp-client-identifier \"\\x00" EACID "\";\n"; static const char *existing_ascii_client_id_expected = \ "# Created by NetworkManager\n" "# Merged from /path/to/dhclient.conf\n" "\n" - "send dhcp-client-identifier \"" EACID "\";\n" + "send dhcp-client-identifier \"\\x00" EACID "\";\n" "\n" "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n" "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n" @@ -585,23 +675,26 @@ test_existing_multiline_alsoreq (void) static void test_one_duid (const char *escaped, const guint8 *unescaped, guint len) { - GByteArray *t; + GBytes *t; char *w; + gsize t_len; + gconstpointer t_arr; t = nm_dhcp_dhclient_unescape_duid (escaped); g_assert (t); - g_assert_cmpint (t->len, ==, len); - g_assert_cmpint (memcmp (t->data, unescaped, len), ==, 0); - g_byte_array_free (t, TRUE); + t_arr = g_bytes_get_data (t, &t_len); + g_assert (t_arr); + g_assert_cmpint (t_len, ==, len); + g_assert_cmpint (memcmp (t_arr, unescaped, len), ==, 0); + g_bytes_unref (t); - t = g_byte_array_sized_new (len); - g_byte_array_append (t, unescaped, len); + t = g_bytes_new_static (unescaped, len); w = nm_dhcp_dhclient_escape_duid (t); g_assert (w); g_assert_cmpint (strlen (escaped), ==, strlen (w)); g_assert_cmpstr (escaped, ==, w); - g_byte_array_free (t, TRUE); + g_bytes_unref (t); g_free (w); } @@ -640,25 +733,26 @@ test_read_duid_from_leasefile (void) { const guint8 expected[] = { 0x00, 0x01, 0x00, 0x01, 0x18, 0x79, 0xa6, 0x13, 0x60, 0x67, 0x20, 0xec, 0x4c, 0x70 }; - GByteArray *duid; + gs_unref_bytes GBytes *duid = NULL; GError *error = NULL; + gconstpointer duid_arr; + gsize duid_len; - duid = nm_dhcp_dhclient_read_duid (TESTDIR "/test-dhclient-duid.leases", &error); + duid = nm_dhcp_dhclient_read_duid (TEST_DIR"/test-dhclient-duid.leases", &error); g_assert_no_error (error); g_assert (duid); - g_assert_cmpint (duid->len, ==, sizeof (expected)); - g_assert_cmpint (memcmp (duid->data, expected, duid->len), ==, 0); - - g_byte_array_free (duid, TRUE); + duid_arr = g_bytes_get_data (duid, &duid_len); + g_assert_cmpint (duid_len, ==, sizeof (expected)); + g_assert_cmpint (memcmp (duid_arr, expected, duid_len), ==, 0); } static void test_read_commented_duid_from_leasefile (void) { - GByteArray *duid; + GBytes *duid; GError *error = NULL; - duid = nm_dhcp_dhclient_read_duid (TESTDIR "/test-dhclient-commented-duid.leases", &error); + duid = nm_dhcp_dhclient_read_duid (TEST_DIR"/test-dhclient-commented-duid.leases", &error); g_assert_no_error (error); g_assert (duid == NULL); } @@ -754,12 +848,12 @@ test_write_existing_commented_duid (void) static const char *interface1_orig = \ "interface \"eth0\" {\n" - " also request my-option;\n" - " initial-delay 5;\n" + "\talso request my-option;\n" + "\tinitial-delay 5;\n" "}\n" "interface \"eth1\" {\n" - " also request another-option;\n" - " initial-delay 0;\n" + "\talso request another-option;\n" + "\tinitial-delay 0;\n" "}\n" "\n" "also request yet-another-option;\n"; @@ -798,12 +892,12 @@ test_interface1 (void) static const char *interface2_orig = \ "interface eth0 {\n" - " also request my-option;\n" - " initial-delay 5;\n" + "\talso request my-option;\n" + "\tinitial-delay 5;\n" " }\n" "interface eth1 {\n" - " initial-delay 0;\n" - " request another-option;\n" + "\tinitial-delay 0;\n" + "\trequest another-option;\n" " } \n" "\n" "also request yet-another-option;\n"; @@ -844,12 +938,12 @@ test_config_req_intf (void) { static const char *const orig = \ "request subnet-mask, broadcast-address, routers,\n" - " rfc3442-classless-static-routes,\n" - " interface-mtu, host-name, domain-name, domain-search,\n" - " domain-name-servers, nis-domain, nis-servers,\n" - " nds-context, nds-servers, nds-tree-name,\n" - " netbios-name-servers, netbios-dd-server,\n" - " netbios-node-type, netbios-scope, ntp-servers;\n" + "\trfc3442-classless-static-routes,\n" + "\tinterface-mtu, host-name, domain-name, domain-search,\n" + "\tdomain-name-servers, nis-domain, nis-servers,\n" + "\tnds-context, nds-servers, nds-tree-name,\n" + "\tnetbios-name-servers, netbios-dd-server,\n" + "\tnetbios-node-type, netbios-scope, ntp-servers;\n" ""; static const char *const expected = \ "# Created by NetworkManager\n" @@ -895,133 +989,6 @@ test_config_req_intf (void) /*****************************************************************************/ -static void -test_read_lease_ip4_config_basic (void) -{ - nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); - GError *error = NULL; - char *contents = NULL; - gboolean success; - const char *path = TESTDIR "/leases/basic.leases"; - GSList *leases; - GDateTime *now; - NMIP4Config *config; - const NMPlatformIP4Address *addr; - guint32 expected_addr; - - success = g_file_get_contents (path, &contents, NULL, &error); - g_assert_no_error (error); - g_assert (success); - - /* Date from before the least expiration */ - now = g_date_time_new_utc (2013, 11, 1, 19, 55, 32); - leases = nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, AF_INET, "wlan0", IFINDEX, ROUTE_TABLE, ROUTE_METRIC, contents, now); - g_assert_cmpint (g_slist_length (leases), ==, 2); - - /* IP4Config #1 */ - config = g_slist_nth_data (leases, 0); - g_assert (NM_IS_IP4_CONFIG (config)); - - /* Address */ - g_assert_cmpint (nm_ip4_config_get_num_addresses (config), ==, 1); - expected_addr = nmtst_inet4_from_string ("192.168.1.180"); - addr = _nmtst_ip4_config_get_address (config, 0); - g_assert_cmpint (addr->address, ==, expected_addr); - g_assert_cmpint (addr->peer_address, ==, expected_addr); - g_assert_cmpint (addr->plen, ==, 24); - - /* Gateway */ - expected_addr = nmtst_inet4_from_string ("192.168.1.1"); - g_assert_cmpint (nmtst_ip4_config_get_gateway (config), ==, expected_addr); - - /* DNS */ - g_assert_cmpint (nm_ip4_config_get_num_nameservers (config), ==, 1); - expected_addr = nmtst_inet4_from_string ("192.168.1.1"); - g_assert_cmpint (nm_ip4_config_get_nameserver (config, 0), ==, expected_addr); - - g_assert_cmpint (nm_ip4_config_get_num_domains (config), ==, 0); - - /* IP4Config #2 */ - config = g_slist_nth_data (leases, 1); - g_assert (NM_IS_IP4_CONFIG (config)); - - /* Address */ - g_assert_cmpint (nm_ip4_config_get_num_addresses (config), ==, 1); - expected_addr = nmtst_inet4_from_string ("10.77.52.141"); - addr = _nmtst_ip4_config_get_address (config, 0); - g_assert_cmpint (addr->address, ==, expected_addr); - g_assert_cmpint (addr->peer_address, ==, expected_addr); - g_assert_cmpint (addr->plen, ==, 8); - - /* Gateway */ - expected_addr = nmtst_inet4_from_string ("10.77.52.254"); - g_assert_cmpint (nmtst_ip4_config_get_gateway (config), ==, expected_addr); - - /* DNS */ - g_assert_cmpint (nm_ip4_config_get_num_nameservers (config), ==, 2); - expected_addr = nmtst_inet4_from_string ("8.8.8.8"); - g_assert_cmpint (nm_ip4_config_get_nameserver (config, 0), ==, expected_addr); - expected_addr = nmtst_inet4_from_string ("8.8.4.4"); - g_assert_cmpint (nm_ip4_config_get_nameserver (config, 1), ==, expected_addr); - - /* Domains */ - g_assert_cmpint (nm_ip4_config_get_num_domains (config), ==, 1); - g_assert_cmpstr (nm_ip4_config_get_domain (config, 0), ==, "morriesguest.local"); - - g_slist_free_full (leases, g_object_unref); - g_date_time_unref (now); - g_free (contents); -} - -static void -test_read_lease_ip4_config_expired (void) -{ - nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); - GError *error = NULL; - char *contents = NULL; - gboolean success; - const char *path = TESTDIR "/leases/basic.leases"; - GSList *leases; - GDateTime *now; - - success = g_file_get_contents (path, &contents, NULL, &error); - g_assert_no_error (error); - g_assert (success); - - /* Date from *after* the lease expiration */ - now = g_date_time_new_utc (2013, 12, 1, 19, 55, 32); - leases = nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, AF_INET, "wlan0", IFINDEX, ROUTE_TABLE, ROUTE_METRIC, contents, now); - g_assert (leases == NULL); - - g_date_time_unref (now); - g_free (contents); -} - -static void -test_read_lease_ip4_config_expect_failure (gconstpointer user_data) -{ - nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new (); - GError *error = NULL; - char *contents = NULL; - gboolean success; - GSList *leases; - GDateTime *now; - - success = g_file_get_contents ((const char *) user_data, &contents, NULL, &error); - g_assert_no_error (error); - g_assert (success); - - /* Date from before the least expiration */ - now = g_date_time_new_utc (2013, 11, 1, 1, 1, 1); - leases = nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, AF_INET, "wlan0", IFINDEX, ROUTE_TABLE, ROUTE_METRIC, contents, now); - g_assert (leases == NULL); - - g_date_time_unref (now); - g_free (contents); -} - -/*****************************************************************************/ - NMTST_DEFINE (); int @@ -1031,10 +998,13 @@ main (int argc, char **argv) g_test_add_func ("/dhcp/dhclient/orig_missing", test_orig_missing); g_test_add_func ("/dhcp/dhclient/override_client_id", test_override_client_id); - g_test_add_func ("/dhcp/dhclient/quote_client_id", test_quote_client_id); + g_test_add_func ("/dhcp/dhclient/quote_client_id/1", test_quote_client_id); + g_test_add_func ("/dhcp/dhclient/quote_client_id/2", test_quote_client_id_2); + g_test_add_func ("/dhcp/dhclient/hex_zero_client_id", test_hex_zero_client_id); g_test_add_func ("/dhcp/dhclient/ascii_client_id", test_ascii_client_id); g_test_add_func ("/dhcp/dhclient/hex_single_client_id", test_hex_single_client_id); g_test_add_func ("/dhcp/dhclient/existing-hex-client-id", test_existing_hex_client_id); + g_test_add_func ("/dhcp/dhclient/existing-client-id", test_existing_escaped_client_id); g_test_add_func ("/dhcp/dhclient/existing-ascii-client-id", test_existing_ascii_client_id); g_test_add_func ("/dhcp/dhclient/fqdn", test_fqdn); g_test_add_func ("/dhcp/dhclient/fqdn_options_override", test_fqdn_options_override); @@ -1056,18 +1026,6 @@ main (int argc, char **argv) g_test_add_func ("/dhcp/dhclient/write_existing_duid", test_write_existing_duid); g_test_add_func ("/dhcp/dhclient/write_existing_commented_duid", test_write_existing_commented_duid); - g_test_add_func ("/dhcp/dhclient/leases/ip4-config/basic", test_read_lease_ip4_config_basic); - g_test_add_func ("/dhcp/dhclient/leases/ip4-config/expired", test_read_lease_ip4_config_expired); - g_test_add_data_func ("/dhcp/dhclient/leases/ip4-config/missing-address", - TESTDIR "/leases/malformed1.leases", - test_read_lease_ip4_config_expect_failure); - g_test_add_data_func ("/dhcp/dhclient/leases/ip4-config/missing-gateway", - TESTDIR "/leases/malformed2.leases", - test_read_lease_ip4_config_expect_failure); - g_test_add_data_func ("/dhcp/dhclient/leases/ip4-config/missing-expire", - TESTDIR "/leases/malformed3.leases", - test_read_lease_ip4_config_expect_failure); - return g_test_run (); } diff --git a/src/dhcp/tests/test-dhcp-utils.c b/src/dhcp/tests/test-dhcp-utils.c index 72f31191..617a3c6c 100644 --- a/src/dhcp/tests/test-dhcp-utils.c +++ b/src/dhcp/tests/test-dhcp-utils.c @@ -349,8 +349,7 @@ test_dhclient_invalid_classless_routes_1 (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ignoring invalid classless static routes*"); + NMTST_EXPECT_NM_WARN ("*ignoring invalid classless static routes*"); ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); @@ -380,8 +379,7 @@ test_dhcpcd_invalid_classless_routes_1 (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ignoring invalid classless static routes*"); + NMTST_EXPECT_NM_WARN ("*ignoring invalid classless static routes*"); ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); @@ -413,8 +411,7 @@ test_dhclient_invalid_classless_routes_2 (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ignoring invalid classless static routes*"); + NMTST_EXPECT_NM_WARN ("*ignoring invalid classless static routes*"); ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); @@ -446,8 +443,7 @@ test_dhcpcd_invalid_classless_routes_2 (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ignoring invalid classless static routes*"); + NMTST_EXPECT_NM_WARN ("*ignoring invalid classless static routes*"); ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); @@ -479,8 +475,7 @@ test_dhclient_invalid_classless_routes_3 (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ignoring invalid classless static routes*"); + NMTST_EXPECT_NM_WARN ("*ignoring invalid classless static routes*"); ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); @@ -507,8 +502,7 @@ test_dhcpcd_invalid_classless_routes_3 (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*DHCP provided invalid classless static route*"); + NMTST_EXPECT_NM_WARN ("*DHCP provided invalid classless static route*"); ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); @@ -615,8 +609,7 @@ test_invalid_escaped_domain_searches (void) options = fill_table (generic_options, NULL); options = fill_table (data, options); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*invalid domain search*"); + NMTST_EXPECT_NM_WARN ("*invalid domain search*"); ip4_config = _ip4_config_from_options (1, "eth0", options, 0); g_test_assert_expected_messages (); diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c index e6436c79..b5b93280 100644 --- a/src/dns/nm-dns-dnsmasq.c +++ b/src/dns/nm-dns-dnsmasq.c @@ -35,7 +35,7 @@ #include "nm-utils.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" -#include "nm-bus-manager.h" +#include "nm-dbus-manager.h" #include "NetworkManagerUtils.h" #define PIDFILE NMRUNDIR "/dnsmasq.pid" @@ -75,66 +75,6 @@ G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN) /*****************************************************************************/ -static char ** -get_ip4_rdns_domains (NMIP4Config *ip4) -{ - char **strv; - GPtrArray *domains = NULL; - NMDedupMultiIter ipconf_iter; - const NMPlatformIP4Address *address; - const NMPlatformIP4Route *route; - - g_return_val_if_fail (ip4 != NULL, NULL); - - domains = g_ptr_array_sized_new (5); - - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) - nm_utils_get_reverse_dns_domains_ip4 (address->address, address->plen, domains); - - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) { - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) - nm_utils_get_reverse_dns_domains_ip4 (route->network, route->plen, domains); - } - - /* Terminating NULL so we can use g_strfreev() to free it */ - g_ptr_array_add (domains, NULL); - - /* Free the array and return NULL if the only element was the ending NULL */ - strv = (char **) g_ptr_array_free (domains, (domains->len == 1)); - - return _nm_utils_strv_cleanup (strv, FALSE, FALSE, TRUE); -} - -static char ** -get_ip6_rdns_domains (NMIP6Config *ip6) -{ - char **strv; - GPtrArray *domains = NULL; - NMDedupMultiIter ipconf_iter; - const NMPlatformIP6Address *address; - const NMPlatformIP6Route *route; - - g_return_val_if_fail (ip6 != NULL, NULL); - - domains = g_ptr_array_sized_new (5); - - nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address) - nm_utils_get_reverse_dns_domains_ip6 (&address->address, address->plen, domains); - - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) { - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) - nm_utils_get_reverse_dns_domains_ip6 (&route->network, route->plen, domains); - } - - /* Terminating NULL so we can use g_strfreev() to free it */ - g_ptr_array_add (domains, NULL); - - /* Free the array and return NULL if the only element was the ending NULL */ - strv = (char **) g_ptr_array_free (domains, (domains->len == 1)); - - return _nm_utils_strv_cleanup (strv, FALSE, FALSE, TRUE); -} - static void add_dnsmasq_nameserver (NMDnsDnsmasq *self, GVariantBuilder *servers, @@ -155,96 +95,43 @@ add_dnsmasq_nameserver (NMDnsDnsmasq *self, g_variant_builder_close (servers); } -static gboolean -add_ip4_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP4Config *ip4, - const char *iface, gboolean split) -{ - char buf[INET_ADDRSTRLEN + 1 + IFNAMSIZ]; - char buf2[INET_ADDRSTRLEN]; - in_addr_t addr; - int nnameservers, i_nameserver, n, i; - gboolean added = FALSE; - - g_return_val_if_fail (iface, FALSE); - nnameservers = nm_ip4_config_get_num_nameservers (ip4); - - if (split) { - char **domains, **iter; - - if (nnameservers == 0) - return FALSE; - - for (i_nameserver = 0; i_nameserver < nnameservers; i_nameserver++) { - addr = nm_ip4_config_get_nameserver (ip4, i_nameserver); - g_snprintf (buf, sizeof (buf), "%s@%s", - nm_utils_inet4_ntop (addr, buf2), iface); - - /* searches are preferred over domains */ - n = nm_ip4_config_get_num_searches (ip4); - for (i = 0; i < n; i++) { - add_dnsmasq_nameserver (self, - servers, - buf, - nm_ip4_config_get_search (ip4, i)); - added = TRUE; - } - - if (n == 0) { - /* If not searches, use any domains */ - n = nm_ip4_config_get_num_domains (ip4); - for (i = 0; i < n; i++) { - add_dnsmasq_nameserver (self, - servers, - buf, - nm_ip4_config_get_domain (ip4, i)); - added = TRUE; - } - } - - /* Ensure reverse-DNS works by directing queries for in-addr.arpa - * domains to the split domain's nameserver. - */ - domains = get_ip4_rdns_domains (ip4); - if (domains) { - for (iter = domains; iter && *iter; iter++) - add_dnsmasq_nameserver (self, servers, buf, *iter); - g_strfreev (domains); - } - } - } +#define IP_ADDR_TO_STRING_BUFLEN (NM_UTILS_INET_ADDRSTRLEN + 1 + IFNAMSIZ) - /* If no searches or domains, just add the nameservers */ - if (!added) { - for (i = 0; i < nnameservers; i++) { - addr = nm_ip4_config_get_nameserver (ip4, i); - g_snprintf (buf, sizeof (buf), "%s@%s", - nm_utils_inet4_ntop (addr, buf2), iface); - add_dnsmasq_nameserver (self, servers, buf, NULL); - } - } +static const char * +ip_addr_to_string (int addr_family, gconstpointer addr, const char *iface, char *out_buf) +{ + int n_written; + char buf2[NM_UTILS_INET_ADDRSTRLEN]; + const char *separator; - return TRUE; -} + nm_assert_addr_family (addr_family); + nm_assert (addr); + nm_assert (out_buf); -static char * -ip6_addr_to_string (const struct in6_addr *addr, const char *iface) -{ - char buf[NM_UTILS_INET_ADDRSTRLEN]; + if (addr_family == AF_INET) { + nm_utils_inet_ntop (addr_family, addr, buf2); + separator = "@"; + } else { + if (IN6_IS_ADDR_V4MAPPED (addr)) + nm_utils_inet4_ntop (((const struct in6_addr *) addr)->s6_addr32[3], buf2); + else + nm_utils_inet6_ntop (addr, buf2); + /* Need to scope link-local addresses with %. Before dnsmasq 2.58, + * only '@' was supported as delimiter. Since 2.58, '@' and '%' are + * supported. Due to a bug, since 2.73 only '%' works properly as "server" + * address. + */ + separator = IN6_IS_ADDR_LINKLOCAL (addr) ? "%" : "@"; + } - if (IN6_IS_ADDR_V4MAPPED (addr)) - nm_utils_inet4_ntop (addr->s6_addr32[3], buf); - else - nm_utils_inet6_ntop (addr, buf); - - /* Need to scope link-local addresses with %. Before dnsmasq 2.58, - * only '@' was supported as delimiter. Since 2.58, '@' and '%' are - * supported. Due to a bug, since 2.73 only '%' works properly as "server" - * address. - */ - return g_strdup_printf ("%s%c%s", - buf, - IN6_IS_ADDR_LINKLOCAL (addr) ? '%' : '@', - iface); + n_written = g_snprintf (out_buf, + IP_ADDR_TO_STRING_BUFLEN, + "%s%s%s", + buf2, + iface ? separator : "", + iface ?: ""); + nm_assert (n_written < IP_ADDR_TO_STRING_BUFLEN); + return out_buf; } static void @@ -271,103 +158,43 @@ add_global_config (NMDnsDnsmasq *self, GVariantBuilder *dnsmasq_servers, const N } } -static gboolean -add_ip6_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP6Config *ip6, - const char *iface, gboolean split) +static void +add_ip_config (NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsIPConfigData *ip_data) { - const struct in6_addr *addr; - char *buf = NULL; - int nnameservers, i_nameserver, n, i; - gboolean added = FALSE; - - g_return_val_if_fail (iface, FALSE); - nnameservers = nm_ip6_config_get_num_nameservers (ip6); - - if (split) { - char **domains, **iter; - - if (nnameservers == 0) - return FALSE; - - for (i_nameserver = 0; i_nameserver < nnameservers; i_nameserver++) { - addr = nm_ip6_config_get_nameserver (ip6, i_nameserver); - buf = ip6_addr_to_string (addr, iface); - - /* searches are preferred over domains */ - n = nm_ip6_config_get_num_searches (ip6); - for (i = 0; i < n; i++) { - add_dnsmasq_nameserver (self, - servers, - buf, - nm_ip6_config_get_search (ip6, i)); - added = TRUE; - } - - if (n == 0) { - /* If not searches, use any domains */ - n = nm_ip6_config_get_num_domains (ip6); - for (i = 0; i < n; i++) { - add_dnsmasq_nameserver (self, - servers, - buf, - nm_ip6_config_get_domain (ip6, i)); - added = TRUE; - } - } - - /* Ensure reverse-DNS works by directing queries for ip6.arpa - * domains to the split domain's nameserver. - */ - domains = get_ip6_rdns_domains (ip6); - if (domains) { - for (iter = domains; iter && *iter; iter++) - add_dnsmasq_nameserver (self, servers, buf, *iter); - g_strfreev (domains); - } - - g_free (buf); + NMIPConfig *ip_config = ip_data->ip_config; + gconstpointer addr; + const char *iface, *domain; + char ip_addr_to_string_buf[IP_ADDR_TO_STRING_BUFLEN]; + int addr_family; + guint i, j, num; + + iface = nm_platform_link_get_name (NM_PLATFORM_GET, ip_data->data->ifindex); + addr_family = nm_ip_config_get_addr_family (ip_config); + + num = nm_ip_config_get_num_nameservers (ip_config); + 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); + for (j = 0; ip_data->domains.search[j]; j++) { + domain = nm_utils_parse_dns_domain (ip_data->domains.search[j], NULL); + add_dnsmasq_nameserver (self, + servers, + ip_addr_to_string_buf, + domain[0] ? domain : NULL); } - } - /* If no searches or domains, just add the nameservers */ - if (!added) { - for (i = 0; i < nnameservers; i++) { - addr = nm_ip6_config_get_nameserver (ip6, i); - buf = ip6_addr_to_string (addr, iface); - if (buf) { - add_dnsmasq_nameserver (self, servers, buf, NULL); - g_free (buf); - } + for (j = 0; ip_data->domains.reverse[j]; j++) { + add_dnsmasq_nameserver (self, servers, + ip_addr_to_string_buf, + ip_data->domains.reverse[j]); } } - - return TRUE; -} - -static gboolean -add_ip_config_data (NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsIPConfigData *data) -{ - if (NM_IS_IP4_CONFIG (data->config)) { - return add_ip4_config (self, - servers, - (NMIP4Config *) data->config, - data->iface, - data->type == NM_DNS_IP_CONFIG_TYPE_VPN); - } else if (NM_IS_IP6_CONFIG (data->config)) { - return add_ip6_config (self, - servers, - (NMIP6Config *) data->config, - data->iface, - data->type == NM_DNS_IP_CONFIG_TYPE_VPN); - } else - g_return_val_if_reached (FALSE); } static void dnsmasq_update_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) { NMDnsDnsmasq *self; - NMDnsDnsmasqPrivate *priv; gs_free_error GError *error = NULL; gs_unref_variant GVariant *response = NULL; @@ -376,7 +203,6 @@ dnsmasq_update_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) return; self = NM_DNS_DNSMASQ (user_data); - priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); if (!response) _LOGW ("dnsmasq update failed: %s", error->message); @@ -478,7 +304,7 @@ start_dnsmasq (NMDnsDnsmasq *self) const char *argv[15]; GPid pid = 0; guint idx = 0; - NMBusManager *dbus_mgr; + NMDBusManager *dbus_mgr; GDBusConnection *connection; if (priv->running) { @@ -530,10 +356,10 @@ start_dnsmasq (NMDnsDnsmasq *self) return; } - dbus_mgr = nm_bus_manager_get (); + dbus_mgr = nm_dbus_manager_get (); g_return_if_fail (dbus_mgr); - connection = nm_bus_manager_get_connection (dbus_mgr); + connection = nm_dbus_manager_get_connection (dbus_mgr); g_return_if_fail (connection); priv->dnsmasq_cancellable = g_cancellable_new (); @@ -550,15 +376,14 @@ start_dnsmasq (NMDnsDnsmasq *self) static gboolean update (NMDnsPlugin *plugin, - const GPtrArray *configs, const NMGlobalDnsConfig *global_config, + const CList *ip_config_lst_head, const char *hostname) { NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin); NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); GVariantBuilder servers; - guint i; - int prio, first_prio; + const NMDnsIPConfigData *ip_data; start_dnsmasq (self); @@ -567,16 +392,8 @@ update (NMDnsPlugin *plugin, if (global_config) add_global_config (self, &servers, global_config); else { - for (i = 0; i < configs->len; i++) { - const NMDnsIPConfigData *data = configs->pdata[i]; - - prio = nm_ip_config_get_dns_priority (data->config); - if (i == 0) - first_prio = prio; - else if (first_prio < 0 && first_prio != prio) - break; - add_ip_config_data (self, &servers, data); - } + c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) + add_ip_config (self, &servers, ip_data); } g_clear_pointer (&priv->set_server_ex_args, g_variant_unref); diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index dc545470..f5500ee7 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -46,6 +46,7 @@ #include "nm-ip6-config.h" #include "NetworkManagerUtils.h" #include "nm-config.h" +#include "nm-dbus-object.h" #include "devices/nm-device.h" #include "nm-manager.h" @@ -54,8 +55,6 @@ #include "nm-dns-systemd-resolved.h" #include "nm-dns-unbound.h" -#include "introspection/org.freedesktop.NetworkManager.DnsManager.h" - #define HASH_LEN 20 #ifndef RESOLVCONF_PATH @@ -70,6 +69,24 @@ #define PLUGIN_RATELIMIT_BURST 5 #define PLUGIN_RATELIMIT_DELAY 300 +/*****************************************************************************/ + +typedef enum { + SR_SUCCESS, + SR_NOTFOUND, + SR_ERROR +} SpawnResult; + +typedef struct { + GPtrArray *nameservers; + GPtrArray *searches; + GPtrArray *options; + const char *nis_domain; + GPtrArray *nis_servers; +} NMResolvConfData; + +/*****************************************************************************/ + enum { CONFIG_CHANGED, @@ -84,44 +101,16 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDnsManager, static guint signals[LAST_SIGNAL] = { 0 }; -typedef enum { - SR_SUCCESS, - SR_NOTFOUND, - SR_ERROR -} SpawnResult; - -NM_DEFINE_SINGLETON_GETTER (NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER); - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "dns-mgr" -#define _NMLOG_DOMAIN LOGD_DNS -#define _NMLOG(level, ...) \ - G_STMT_START { \ - const NMLogLevel __level = (level); \ - \ - if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \ - char __prefix[20]; \ - const NMDnsManager *const __self = (self); \ - \ - _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - ((!__self || __self == singleton_instance) \ - ? "" \ - : nm_sprintf_buf (__prefix, "[%p]", __self)) \ - _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ - } \ - } G_STMT_END - -/*****************************************************************************/ - typedef struct { - GPtrArray *configs; + GHashTable *configs; + CList ip_config_lst_head; GVariant *config_variant; - NMDnsIPConfigData *best_conf4, *best_conf6; - bool need_sort:1; + NMDnsIPConfigData *best_ip_config_4; + NMDnsIPConfigData *best_ip_config_6; + + bool ip_config_lst_need_sort:1; + bool dns_touched:1; bool is_stopped:1; @@ -145,20 +134,52 @@ typedef struct { } NMDnsManagerPrivate; struct _NMDnsManager { - NMExportedObject parent; + NMDBusObject parent; NMDnsManagerPrivate _priv; }; struct _NMDnsManagerClass { - NMExportedObjectClass parent; + NMDBusObjectClass parent; }; -G_DEFINE_TYPE (NMDnsManager, nm_dns_manager, NM_TYPE_EXPORTED_OBJECT) +G_DEFINE_TYPE (NMDnsManager, nm_dns_manager, NM_TYPE_DBUS_OBJECT) #define NM_DNS_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDnsManager, NM_IS_DNS_MANAGER) +NM_DEFINE_SINGLETON_GETTER (NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER); + +/*****************************************************************************/ + +#define _NMLOG_PREFIX_NAME "dns-mgr" +#define _NMLOG_DOMAIN LOGD_DNS +#define _NMLOG(level, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + \ + if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \ + char __prefix[20]; \ + const NMDnsManager *const __self = (self); \ + \ + _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \ + "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + ((!__self || __self == singleton_instance) \ + ? "" \ + : nm_sprintf_buf (__prefix, "[%p]", __self)) \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END + +/*****************************************************************************/ + +static void _ip_config_dns_priority_changed (gpointer config, + GParamSpec *pspec, + NMDnsIPConfigData *ip_data); + +/*****************************************************************************/ + static gboolean -domain_is_valid (const gchar *domain, gboolean check_public_suffix) +domain_is_valid (const char *domain, gboolean check_public_suffix) { if (*domain == '\0') return FALSE; @@ -169,15 +190,13 @@ domain_is_valid (const gchar *domain, gboolean check_public_suffix) return TRUE; } -/*****************************************************************************/ +static gboolean +domain_is_routing (const char *domain) +{ + return domain[0] == '~'; +} -typedef struct { - GPtrArray *nameservers; - GPtrArray *searches; - GPtrArray *options; - const char *nis_domain; - GPtrArray *nis_servers; -} NMResolvConfData; +/*****************************************************************************/ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_rc_manager_to_string, NMDnsManagerResolvConfManager, NM_UTILS_LOOKUP_DEFAULT_WARN (NULL), @@ -192,44 +211,112 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_rc_manager_to_string, NMDnsManagerResolvConf NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_config_type_to_string, NMDnsIPConfigType, NM_UTILS_LOOKUP_DEFAULT_WARN (""), + NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_REMOVED, "removed"), NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_DEFAULT, "default"), NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE, "best"), NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_VPN, "vpn"), ); -static NMDnsIPConfigData * -ip_config_data_new (gpointer config, NMDnsIPConfigType type, const char *iface) +/*****************************************************************************/ + +static void +_ASSERT_config_data (const NMDnsConfigData *data) { - NMDnsIPConfigData *data; + nm_assert (data); + nm_assert (NM_IS_DNS_MANAGER (data->self)); + nm_assert (data->ifindex > 0); +} - data = g_slice_new0 (NMDnsIPConfigData); - data->config = g_object_ref (config); - data->iface = g_strdup (iface); - data->type = type; +static void +_ASSERT_ip_config_data (const NMDnsIPConfigData *ip_data) +{ + nm_assert (ip_data); + _ASSERT_config_data (ip_data->data); + nm_assert (NM_IS_IP_CONFIG (ip_data->ip_config, AF_UNSPEC)); + 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)); +} - return data; +static NMDnsIPConfigData * +_ip_config_data_new (NMDnsConfigData *data, + NMIPConfig *ip_config, + NMDnsIPConfigType ip_config_type) +{ + NMDnsIPConfigData *ip_data; + + _ASSERT_config_data (data); + nm_assert (NM_IS_IP_CONFIG (ip_config, AF_UNSPEC)); + nm_assert (ip_config_type != NM_DNS_IP_CONFIG_TYPE_REMOVED); + + ip_data = g_slice_new0 (NMDnsIPConfigData); + ip_data->data = data; + ip_data->ip_config = g_object_ref (ip_config); + ip_data->ip_config_type = ip_config_type; + 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); + + g_signal_connect (ip_config, + NM_IS_IP4_CONFIG (ip_config) + ? "notify::" NM_IP4_CONFIG_DNS_PRIORITY + : "notify::" NM_IP6_CONFIG_DNS_PRIORITY, + (GCallback) _ip_config_dns_priority_changed, ip_data); + + _ASSERT_ip_config_data (ip_data); + return ip_data; } static void -ip_config_data_destroy (gpointer ptr) +_ip_config_data_free (NMDnsIPConfigData *ip_data) { - NMDnsIPConfigData *data = ptr; + _ASSERT_ip_config_data (ip_data); - if (!data) - return; + c_list_unlink_stale (&ip_data->data_lst); + c_list_unlink_stale (&ip_data->ip_config_lst); + + g_free (ip_data->domains.search); + g_strfreev (ip_data->domains.reverse); - g_object_unref (data->config); - g_free (data->iface); - g_slice_free (NMDnsIPConfigData, data); + g_signal_handlers_disconnect_by_func (ip_data->ip_config, + _ip_config_dns_priority_changed, + ip_data); + + g_object_unref (ip_data->ip_config); + g_slice_free (NMDnsIPConfigData, ip_data); +} + +static NMDnsIPConfigData * +_config_data_find_ip_config (NMDnsConfigData *data, + NMIPConfig *ip_config) +{ + NMDnsIPConfigData *ip_data; + + _ASSERT_config_data (data); + + c_list_for_each_entry (ip_data, &data->data_lst_head, data_lst) { + _ASSERT_ip_config_data (ip_data); + + if (ip_data->ip_config == ip_config) + return ip_data; + } + return NULL; +} + +static void +_config_data_free (NMDnsConfigData *data) +{ + _ASSERT_config_data (data); + + nm_assert (c_list_is_empty (&data->data_lst_head)); + g_slice_free (NMDnsConfigData, data); } static gint -ip_config_data_compare (const NMDnsIPConfigData *a, const NMDnsIPConfigData *b) +_ip_config_data_cmp (const NMDnsIPConfigData *a, const NMDnsIPConfigData *b) { int a_prio, b_prio; - a_prio = nm_ip_config_get_dns_priority (a->config); - b_prio = nm_ip_config_get_dns_priority (b->config); + a_prio = nm_ip_config_get_dns_priority (a->ip_config); + b_prio = nm_ip_config_get_dns_priority (b->ip_config); /* Configurations with lower priority value first */ if (a_prio < b_prio) @@ -238,24 +325,40 @@ ip_config_data_compare (const NMDnsIPConfigData *a, const NMDnsIPConfigData *b) return 1; /* Sort also according to type */ - if (a->type > b->type) + if (a->ip_config_type > b->ip_config_type) return -1; - else if (a->type < b->type) + else if (a->ip_config_type < b->ip_config_type) return 1; return 0; } static gint -ip_config_data_ptr_compare (gconstpointer a, gconstpointer b) +_ip_config_lst_cmp (const CList *a, + const CList *b, + const void *user_data) +{ + return _ip_config_data_cmp (c_list_entry (a, NMDnsIPConfigData, ip_config_lst), + c_list_entry (b, NMDnsIPConfigData, ip_config_lst)); +} + +static CList * +_ip_config_lst_head (NMDnsManager *self) { - const NMDnsIPConfigData *const *ptr_a = a, *const *ptr_b = b; + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); - return ip_config_data_compare (*ptr_a, *ptr_b); + if (priv->ip_config_lst_need_sort) { + priv->ip_config_lst_need_sort = FALSE; + c_list_sort (&priv->ip_config_lst_head, _ip_config_lst_cmp, NULL); + } + + return &priv->ip_config_lst_head; } +/*****************************************************************************/ + static void -add_string_item (GPtrArray *array, const char *str) +add_string_item (GPtrArray *array, const char *str, gboolean dup) { int i; @@ -271,7 +374,7 @@ add_string_item (GPtrArray *array, const char *str) } /* No dupes, add the new item */ - g_ptr_array_add (array, g_strdup (str)); + g_ptr_array_add (array, dup ? g_strdup (str): (gpointer) str); } static void @@ -281,25 +384,56 @@ add_dns_option_item (GPtrArray *array, const char *str) g_ptr_array_add (array, g_strdup (str)); } +static void +add_dns_domains (GPtrArray *array, const NMIPConfig *ip_config, + gboolean include_routing, gboolean dup) +{ + guint num_domains, num_searches, i; + const char *str; + + num_domains = nm_ip_config_get_num_domains (ip_config); + num_searches = nm_ip_config_get_num_searches (ip_config); + + for (i = 0; i < num_searches; i++) { + str = nm_ip_config_get_search (ip_config, 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) { + for (i = 0; i < num_domains; i++) { + str = nm_ip_config_get_domain (ip_config, 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); + } + } +} + static void merge_one_ip_config (NMResolvConfData *rc, - const NMIPConfig *config, - const char *iface) + int ifindex, + const NMIPConfig *ip_config) { int addr_family; - guint num, num_domains, num_searches, i; + guint num, i; char buf[NM_UTILS_INET_ADDRSTRLEN + 50]; - const char *str; - addr_family = nm_ip_config_get_addr_family (config); + addr_family = nm_ip_config_get_addr_family (ip_config); nm_assert_addr_family (addr_family); + nm_assert (ifindex > 0); + nm_assert (ifindex == nm_ip_config_get_ifindex (ip_config)); - num = nm_ip_config_get_num_nameservers (config); + num = nm_ip_config_get_num_nameservers (ip_config); for (i = 0; i < num; i++) { const NMIPAddr *addr; - addr = nm_ip_config_get_nameserver (config, i); + addr = nm_ip_config_get_nameserver (ip_config, i); if (addr_family == AF_INET) nm_utils_inet_ntop (addr_family, addr, buf); else if (IN6_IS_ADDR_V4MAPPED (addr)) @@ -307,49 +441,42 @@ merge_one_ip_config (NMResolvConfData *rc, else { nm_utils_inet6_ntop (&addr->addr6, buf); if (IN6_IS_ADDR_LINKLOCAL (addr)) { - g_strlcat (buf, "%", sizeof (buf)); - g_strlcat (buf, iface, sizeof (buf)); + const char *ifname; + + ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); + if (ifname) { + g_strlcat (buf, "%", sizeof (buf)); + g_strlcat (buf, ifname, sizeof (buf)); + } } } - add_string_item (rc->nameservers, buf); + add_string_item (rc->nameservers, buf, TRUE); } - num_domains = nm_ip_config_get_num_domains (config); - num_searches = nm_ip_config_get_num_searches (config); - for (i = 0; i < num_searches; i++) { - str = nm_ip_config_get_search (config, i); - if (domain_is_valid (str, FALSE)) - add_string_item (rc->searches, str); - } - if (num_domains > 1 || !num_searches) { - for (i = 0; i < num_domains; i++) { - str = nm_ip_config_get_domain (config, i); - if (domain_is_valid (str, FALSE)) - add_string_item (rc->searches, str); - } - } + add_dns_domains (rc->searches, ip_config, FALSE, TRUE); - num = nm_ip_config_get_num_dns_options (config); + num = nm_ip_config_get_num_dns_options (ip_config); for (i = 0; i < num; i++) { add_dns_option_item (rc->options, - nm_ip_config_get_dns_option (config, i)); + nm_ip_config_get_dns_option (ip_config, i)); } if (addr_family == AF_INET) { - const NMIP4Config *config4 = (const NMIP4Config *) config; + const NMIP4Config *ip4_config = (const NMIP4Config *) ip_config; /* NIS stuff */ - num = nm_ip4_config_get_num_nis_servers (config4); + 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 (config4, i), buf)); + nm_utils_inet4_ntop (nm_ip4_config_get_nis_server (ip4_config, i), buf), + TRUE); } - if (nm_ip4_config_get_nis_domain (config4)) { + if (nm_ip4_config_get_nis_domain (ip4_config)) { /* FIXME: handle multiple domains */ if (!rc->nis_domain) - rc->nis_domain = nm_ip4_config_get_nis_domain (config4); + rc->nis_domain = nm_ip4_config_get_nis_domain (ip4_config); } } } @@ -378,58 +505,59 @@ run_netconfig (NMDnsManager *self, GError **error, gint *stdin_fd) } static void -write_to_netconfig (NMDnsManager *self, gint fd, const char *key, const char *value) +netconfig_construct_str (NMDnsManager *self, GString *str, const char *key, const char *value) +{ + if (value) { + _LOGD ("writing to netconfig: %s='%s'", key, value); + g_string_append_printf (str, "%s='%s'\n", key, value); + } +} + +static void +netconfig_construct_strv (NMDnsManager *self, GString *str, const char *key, const char *const*values) { - char *str; - int x; + if (values) { + gs_free char *value = NULL; - str = g_strdup_printf ("%s='%s'\n", key, value); - _LOGD ("writing to netconfig: %s", str); - x = write (fd, str, strlen (str)); - g_free (str); + value = g_strjoinv (" ", (char **) values); + netconfig_construct_str (self, str, key, value); + } } static SpawnResult dispatch_netconfig (NMDnsManager *self, - char **searches, - char **nameservers, + const char *const*searches, + const char *const*nameservers, const char *nis_domain, - char **nis_servers, + const char *const*nis_servers, GError **error) { - char *str; GPid pid; gint fd; int status; + gssize l; + nm_auto_free_gstring GString *str = NULL; pid = run_netconfig (self, error, &fd); if (pid <= 0) return SR_NOTFOUND; + str = g_string_new (""); + /* NM is writing already-merged DNS information to netconfig, so it * does not apply to a specific network interface. */ - write_to_netconfig (self, fd, "INTERFACE", "NetworkManager"); - - if (searches) { - str = g_strjoinv (" ", searches); - write_to_netconfig (self, fd, "DNSSEARCH", str); - g_free (str); - } - - if (nameservers) { - str = g_strjoinv (" ", nameservers); - write_to_netconfig (self, fd, "DNSSERVERS", str); - g_free (str); - } + netconfig_construct_str (self, str, "INTERFACE", "NetworkManager"); + netconfig_construct_strv (self, str, "DNSSEARCH", searches); + netconfig_construct_strv (self, str, "DNSSERVERS", nameservers); + netconfig_construct_str (self, str, "NISDOMAIN", nis_domain); + netconfig_construct_strv (self, str, "NISSERVERS", nis_servers); - if (nis_domain) - write_to_netconfig (self, fd, "NISDOMAIN", nis_domain); - - if (nis_servers) { - str = g_strjoinv (" ", nis_servers); - write_to_netconfig (self, fd, "NISSERVERS", str); - g_free (str); +again: + l = write (fd, str->str, str->len); + if (l == -1) { + if (errno == EINTR) + goto again; } nm_close (fd); @@ -743,7 +871,7 @@ update_resolv_conf (NMDnsManager *self, if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) { _LOGT ("update-resolv-conf: write internal file %s succeeded (rc-manager=%s)", - rc_path, _rc_manager_to_string (rc_manager)); + MY_RESOLV_CONF, _rc_manager_to_string (rc_manager)); return write_file_result; } @@ -817,25 +945,21 @@ update_resolv_conf (NMDnsManager *self, static void compute_hash (NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[HASH_LEN]) { - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); GChecksum *sum; gsize len = HASH_LEN; - guint i; + NMDnsIPConfigData *ip_data; sum = g_checksum_new (G_CHECKSUM_SHA1); - g_assert (len == g_checksum_type_get_length (G_CHECKSUM_SHA1)); + nm_assert (len == g_checksum_type_get_length (G_CHECKSUM_SHA1)); if (global) nm_global_dns_config_update_checksum (global, sum); else { - for (i = 0; i < priv->configs->len; i++) { - NMDnsIPConfigData *data = priv->configs->pdata[i]; + const CList *head; - if (NM_IS_IP4_CONFIG (data->config)) - nm_ip4_config_hash ((NMIP4Config *) data->config, sum, TRUE); - else if (NM_IS_IP6_CONFIG (data->config)) - nm_ip6_config_hash ((NMIP6Config *) data->config, sum, TRUE); - } + head = _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); } g_checksum_get_digest (sum, buffer, &len); @@ -849,27 +973,36 @@ merge_global_dns_config (NMResolvConfData *rc, NMGlobalDnsConfig *global_conf) const char *const *searches; const char *const *options; const char *const *servers; - gint i; + guint i; if (!global_conf) return FALSE; searches = nm_global_dns_config_get_searches (global_conf); - options = nm_global_dns_config_get_options (global_conf); - - for (i = 0; searches && searches[i]; i++) { - if (domain_is_valid (searches[i], FALSE)) - add_string_item (rc->searches, searches[i]); + if (searches) { + for (i = 0; searches[i]; i++) { + if (domain_is_routing (searches[i])) + continue; + if (!domain_is_valid (searches[i], FALSE)) + continue; + add_string_item (rc->searches, searches[i], TRUE); + } } - for (i = 0; options && options[i]; i++) - add_string_item (rc->options, options[i]); + options = nm_global_dns_config_get_options (global_conf); + if (options) { + for (i = 0; options[i]; i++) + add_string_item (rc->options, options[i], TRUE); + } default_domain = nm_global_dns_config_lookup_domain (global_conf, "*"); - g_assert (default_domain); + nm_assert (default_domain); + servers = nm_global_dns_domain_get_servers (default_domain); - for (i = 0; servers && servers[i]; i++) - add_string_item (rc->nameservers, servers[i]); + if (servers) { + for (i = 0; servers[i]; i++) + add_string_item (rc->nameservers, servers[i], TRUE); + } return TRUE; } @@ -909,17 +1042,16 @@ _ptrarray_to_strv (GPtrArray *parray) } static void -_collect_resolv_conf_data (NMDnsManager *self, /* only for logging context, no other side-effects */ +_collect_resolv_conf_data (NMDnsManager *self, NMGlobalDnsConfig *global_config, - const GPtrArray *configs, - const char *hostname, char ***out_searches, char ***out_options, char ***out_nameservers, char ***out_nis_servers, const char **out_nis_domain) { - guint i, j, num, len; + NMDnsManagerPrivate *priv; + guint i, num, len; NMResolvConfData rc = { .nameservers = g_ptr_array_new (), .searches = g_ptr_array_new (), @@ -928,37 +1060,44 @@ _collect_resolv_conf_data (NMDnsManager *self, /* only for logging context, no o .nis_servers = g_ptr_array_new (), }; + priv = NM_DNS_MANAGER_GET_PRIVATE (self); + if (global_config) merge_global_dns_config (&rc, global_config); else { nm_auto_free_gstring GString *tmp_gstring = NULL; int prio, first_prio = 0; - NMDnsIPConfigData *current; + const NMDnsIPConfigData *ip_data; + const CList *head; + gboolean is_first = TRUE; - for (i = 0, j = 0; i < configs->len; i++) { + head = _ip_config_lst_head (self); + c_list_for_each_entry (ip_data, head, ip_config_lst) { gboolean skip = FALSE; - current = configs->pdata[i]; + _ASSERT_ip_config_data (ip_data); - prio = nm_ip_config_get_dns_priority (current->config); + prio = nm_ip_config_get_dns_priority (ip_data->ip_config); - if (i == 0) + if (is_first) { + is_first = FALSE; first_prio = prio; - else if (first_prio < 0 && first_prio != prio) + } else if ( first_prio < 0 + && first_prio != prio) skip = TRUE; - if (nm_ip_config_get_num_nameservers (current->config)) { - _LOGT ("config: %8d %-7s v%c %-16s %s: %s", + if (nm_ip_config_get_num_nameservers (ip_data->ip_config)) { + _LOGT ("config: %8d %-7s v%c %-5d %s: %s", prio, - _config_type_to_string (current->type), - nm_utils_addr_family_to_char (nm_ip_config_get_addr_family (current->config)), - current->iface, + _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)), + ip_data->data->ifindex, skip ? "" : "", - get_nameserver_list (current->config, &tmp_gstring)); + get_nameserver_list (ip_data->ip_config, &tmp_gstring)); } if (!skip) - merge_one_ip_config (&rc, current->config, current->iface); + merge_one_ip_config (&rc, ip_data->data->ifindex, ip_data->ip_config); } } @@ -969,16 +1108,16 @@ _collect_resolv_conf_data (NMDnsManager *self, /* only for logging context, no o * (eg, "example.com"), then use the hostname itself as the search (since the user is * unlikely to want "com" as a search domain). */ - if (hostname) { - const char *hostdomain = strchr (hostname, '.'); + if (priv->hostname) { + const char *hostdomain = strchr (priv->hostname, '.'); if ( hostdomain - && !nm_utils_ipaddr_valid (AF_UNSPEC, hostname)) { + && !nm_utils_ipaddr_valid (AF_UNSPEC, priv->hostname)) { hostdomain++; if (domain_is_valid (hostdomain, TRUE)) - add_string_item (rc.searches, hostdomain); - else if (domain_is_valid (hostname, TRUE)) - add_string_item (rc.searches, hostname); + add_string_item (rc.searches, hostdomain, TRUE); + else if (domain_is_valid (priv->hostname, TRUE)) + add_string_item (rc.searches, priv->hostname, TRUE); } } @@ -1000,6 +1139,197 @@ _collect_resolv_conf_data (NMDnsManager *self, /* only for logging context, no o *out_nis_domain = rc.nis_domain; } +static char ** +get_ip_rdns_domains (NMIPConfig *ip_config) +{ + int addr_family = nm_ip_config_get_addr_family (ip_config); + char **strv; + GPtrArray *domains = NULL; + NMDedupMultiIter ipconf_iter; + + nm_assert_addr_family (addr_family); + + domains = g_ptr_array_sized_new (5); + + if (addr_family == AF_INET) { + NMIP4Config *ip4 = (gpointer) ip_config; + const NMPlatformIP4Address *address; + const NMPlatformIP4Route *route; + + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) + nm_utils_get_reverse_dns_domains_ip4 (address->address, address->plen, domains); + + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) { + if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + nm_utils_get_reverse_dns_domains_ip4 (route->network, route->plen, domains); + } + } else { + NMIP6Config *ip6 = (gpointer) ip_config; + const NMPlatformIP6Address *address; + const NMPlatformIP6Route *route; + + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address) + nm_utils_get_reverse_dns_domains_ip6 (&address->address, address->plen, domains); + + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) { + if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + nm_utils_get_reverse_dns_domains_ip6 (&route->network, route->plen, domains); + } + } + + /* Terminating NULL so we can use g_strfreev() to free it */ + g_ptr_array_add (domains, NULL); + + /* Free the array and return NULL if the only element was the ending NULL */ + strv = (char **) g_ptr_array_free (domains, (domains->len == 1)); + + return _nm_utils_strv_cleanup (strv, FALSE, FALSE, TRUE); +} + +/* Check if the domain is shadowed by a parent domain with more negative priority */ +static gboolean +domain_is_shadowed (GHashTable *ht, + const char *domain, int priority, + const char **out_parent, int *out_parent_priority) +{ + char *parent; + int parent_priority; + + nm_assert (!g_hash_table_contains (ht, domain)); + + parent_priority = GPOINTER_TO_INT (g_hash_table_lookup (ht, "")); + if (parent_priority < 0 && parent_priority < priority) { + *out_parent = ""; + *out_parent_priority = parent_priority; + return TRUE; + } + + parent = strchr (domain, '.'); + while (parent && parent[1]) { + parent++; + parent_priority = GPOINTER_TO_INT (g_hash_table_lookup (ht, parent)); + if (parent_priority < 0 && parent_priority < priority) { + *out_parent = parent; + *out_parent_priority = parent_priority; + return TRUE; + } + parent = strchr (parent, '.'); + } + + return FALSE; +} + +static void +rebuild_domain_lists (NMDnsManager *self) +{ + NMDnsIPConfigData *ip_data; + gs_unref_hashtable GHashTable *ht = NULL; + gboolean default_route_found = FALSE; + CList *head; + + ht = g_hash_table_new (nm_str_hash, g_str_equal); + + head = _ip_config_lst_head (self); + c_list_for_each_entry (ip_data, head, ip_config_lst) { + NMIPConfig *ip_config = ip_data->ip_config; + + if (!nm_ip_config_get_num_nameservers (ip_config)) + continue; + if (nm_ip_config_best_default_route_get (ip_config)) { + default_route_found = TRUE; + break; + } + } + + c_list_for_each_entry (ip_data, head, ip_config_lst) { + NMIPConfig *ip_config = ip_data->ip_config; + int priority, old_priority; + guint i, n, n_domains = 0; + const char **domains; + + if (!nm_ip_config_get_num_nameservers (ip_config)) + continue; + + priority = nm_ip_config_get_dns_priority (ip_config); + nm_assert (priority != 0); + g_free (ip_data->domains.search); + domains = g_new0 (const char *, + 2 + NM_MAX (nm_ip_config_get_num_searches (ip_config), + nm_ip_config_get_num_domains (ip_config))); + ip_data->domains.search = domains; + + /* Add wildcard lookup domain to connections with the default route. + * If there is no default route, add the wildcard domain to all non-VPN + * connections */ + if (default_route_found) { + if (nm_ip_config_best_default_route_get (ip_config)) + domains[n_domains++] = "~"; + } else { + if (ip_data->ip_config_type != NM_DNS_IP_CONFIG_TYPE_VPN) + domains[n_domains++] = "~"; + } + + /* searches are preferred over domains */ + n = nm_ip_config_get_num_searches (ip_config); + for (i = 0; i < n; i++) + domains[n_domains++] = nm_ip_config_get_search (ip_config, i); + + if (n == 0) { + /* If not searches, use any domains */ + n = nm_ip_config_get_num_domains (ip_config); + for (i = 0; i < n; i++) + domains[n_domains++] = nm_ip_config_get_domain (ip_config, i); + } + + n = 0; + for (i = 0; i < n_domains; i++) { + const char *domain_clean; + const char *parent; + int parent_priority; + + domain_clean = nm_utils_parse_dns_domain (domains[i], NULL); + + /* Remove domains with lower priority */ + old_priority = GPOINTER_TO_INT (g_hash_table_lookup (ht, domain_clean)); + if (old_priority) { + if (old_priority < priority) { + _LOGT ("plugin: drop domain '%s' (i=%d, p=%d) because it already exists with p=%d", + domains[i], ip_data->data->ifindex, + priority, old_priority); + continue; + } + } else if (domain_is_shadowed (ht, domain_clean, priority, &parent, &parent_priority)) { + _LOGT ("plugin: drop domain '%s' (i=%d, p=%d) shadowed by '%s' (p=%d)", + domains[i], + ip_data->data->ifindex, priority, + parent, parent_priority); + continue; + } + + _LOGT ("plugin: add domain '%s' (i=%d, p=%d)", domains[i], ip_data->data->ifindex, priority); + g_hash_table_insert (ht, (gpointer) domain_clean, GINT_TO_POINTER (priority)); + domains[n++] = domains[i]; + } + domains[n] = NULL; + + g_strfreev (ip_data->domains.reverse); + ip_data->domains.reverse = get_ip_rdns_domains (ip_config); + } +} + +static void +clear_domain_lists (NMDnsManager *self) +{ + NMDnsIPConfigData *ip_data; + CList *head; + + head = _ip_config_lst_head (self); + c_list_for_each_entry (ip_data, head, ip_config_lst) { + g_clear_pointer (&ip_data->domains.search, g_free); + g_clear_pointer (&ip_data->domains.reverse, g_strfreev); + } +} + static gboolean update_dns (NMDnsManager *self, gboolean no_caching, @@ -1040,16 +1370,12 @@ update_dns (NMDnsManager *self, data = nm_config_get_data (priv->config); global_config = nm_config_data_get_global_dns_config (data); - if (priv->need_sort) { - g_ptr_array_sort (priv->configs, ip_config_data_ptr_compare); - priv->need_sort = FALSE; - } - /* Update hash with config we're applying */ compute_hash (self, global_config, priv->hash); - _collect_resolv_conf_data (self, global_config, priv->configs, priv->hostname, - &searches, &options, &nameservers, &nis_servers, &nis_domain); + _collect_resolv_conf_data (self, global_config, + &searches, &options, &nameservers, + &nis_servers, &nis_domain); /* Let any plugins do their thing first */ if (priv->plugin) { @@ -1066,9 +1392,10 @@ update_dns (NMDnsManager *self, } _LOGD ("update-dns: updating plugin %s", plugin_name); + rebuild_domain_lists (self); if (!nm_dns_plugin_update (plugin, - priv->configs, global_config, + _ip_config_lst_head (self), priv->hostname)) { _LOGW ("update-dns: plugin %s update failed", plugin_name); @@ -1077,6 +1404,10 @@ update_dns (NMDnsManager *self, */ caching = FALSE; } + /* Clear the generated search list as it points to + * strings owned by IP configurations and we can't + * guarantee they stay alive. */ + clear_domain_lists (self); skip: ; @@ -1114,8 +1445,12 @@ update_dns (NMDnsManager *self, result = dispatch_resolvconf (self, searches, nameservers, options, error); break; case NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG: - result = dispatch_netconfig (self, searches, nameservers, nis_domain, - nis_servers, error); + result = dispatch_netconfig (self, + (const char *const*) searches, + (const char *const*) nameservers, + nis_domain, + (const char *const*) nis_servers, + error); break; default: g_assert_not_reached (); @@ -1205,82 +1540,96 @@ plugin_child_quit (NMDnsPlugin *plugin, int exit_status, gpointer user_data) } static void -ip_config_dns_priority_changed (gpointer config, - GParamSpec *pspec, - NMDnsManager *self) +_ip_config_dns_priority_changed (gpointer config, + GParamSpec *pspec, + NMDnsIPConfigData *ip_data) { - NM_DNS_MANAGER_GET_PRIVATE (self)->need_sort = TRUE; -} + _ASSERT_ip_config_data (ip_data); -static void -forget_data (NMDnsManager *self, NMDnsIPConfigData *data) -{ - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); - - if (data == priv->best_conf4) - priv->best_conf4 = NULL; - else if (data == priv->best_conf6) - priv->best_conf6 = NULL; - - g_signal_handlers_disconnect_by_func (data->config, ip_config_dns_priority_changed, self); + NM_DNS_MANAGER_GET_PRIVATE (ip_data->data->self)->ip_config_lst_need_sort = TRUE; } gboolean -nm_dns_manager_add_ip_config (NMDnsManager *self, - const char *iface, - gpointer config, - NMDnsIPConfigType cfg_type) +nm_dns_manager_set_ip_config (NMDnsManager *self, + NMIPConfig *ip_config, + NMDnsIPConfigType ip_config_type) { NMDnsManagerPrivate *priv; GError *error = NULL; - NMDnsIPConfigData *data; - gboolean v4 = NM_IS_IP4_CONFIG (config); - guint i; + NMDnsIPConfigData *ip_data; + NMDnsConfigData *data; + int ifindex; + NMDnsIPConfigData **p_best; g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE); - g_return_val_if_fail (config, FALSE); - g_return_val_if_fail (iface && iface[0], FALSE); - nm_assert (NM_IS_IP_CONFIG (config)); + g_return_val_if_fail (NM_IS_IP_CONFIG (ip_config, AF_UNSPEC), FALSE); + + ifindex = nm_ip_config_get_ifindex (ip_config); + g_return_val_if_fail (ifindex > 0, FALSE); priv = NM_DNS_MANAGER_GET_PRIVATE (self); - for (i = 0; i < priv->configs->len; i++) { - data = priv->configs->pdata[i]; - if (data->config == config) { - if ( nm_streq (data->iface, iface) - && data->type == cfg_type) - return FALSE; - else { - forget_data (self, data); - g_ptr_array_remove_index_fast (priv->configs, i); - break; - } - } + data = g_hash_table_lookup (priv->configs, GINT_TO_POINTER (ifindex)); + if (!data) + ip_data = NULL; + else + ip_data = _config_data_find_ip_config (data, ip_config); + + if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_REMOVED) { + if (!ip_data) + return FALSE; + if (priv->best_ip_config_4 == ip_data) + priv->best_ip_config_4 = NULL; + if (priv->best_ip_config_6 == ip_data) + priv->best_ip_config_6 = NULL; + /* deleting a config doesn't invalidate the configs' sort order. */ + _ip_config_data_free (ip_data); + if (c_list_is_empty (&data->data_lst_head)) + g_hash_table_remove (priv->configs, GINT_TO_POINTER (ifindex)); + goto changed; + } + + if ( ip_data + && ip_data->ip_config_type == ip_config_type) { + /* nothing to do. */ + return FALSE; + } + + if (!data) { + data = g_slice_new0 (NMDnsConfigData); + data->ifindex = ifindex; + data->self = self; + c_list_init (&data->data_lst_head); + _ASSERT_config_data (data); + g_hash_table_insert (priv->configs, GINT_TO_POINTER (ifindex), data); } - data = ip_config_data_new (config, cfg_type, iface); - g_ptr_array_add (priv->configs, data); - g_signal_connect (config, - v4 ? - "notify::" NM_IP4_CONFIG_DNS_PRIORITY : - "notify::" NM_IP6_CONFIG_DNS_PRIORITY, - (GCallback) ip_config_dns_priority_changed, self); - priv->need_sort = TRUE; + if (!ip_data) + ip_data = _ip_config_data_new (data, ip_config, ip_config_type); + else + ip_data->ip_config_type = ip_config_type; + + priv->ip_config_lst_need_sort = TRUE; - if (cfg_type == NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE) { + p_best = NM_IS_IP4_CONFIG (ip_config) + ? &priv->best_ip_config_4 + : &priv->best_ip_config_6; + + if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE) { /* Only one best-device per IP version is allowed */ - if (v4) { - if (priv->best_conf4) - priv->best_conf4->type = NM_DNS_IP_CONFIG_TYPE_DEFAULT; - priv->best_conf4 = data; - } else { - if (priv->best_conf6) - priv->best_conf6->type = NM_DNS_IP_CONFIG_TYPE_DEFAULT; - priv->best_conf6 = data; + if (*p_best != ip_data) { + if (*p_best) + (*p_best)->ip_config_type = NM_DNS_IP_CONFIG_TYPE_DEFAULT; + *p_best = ip_data; } + } else { + if (*p_best == ip_data) + *p_best = NULL; } - if (!priv->updates_queue && !update_dns (self, FALSE, &error)) { +changed: + if ( !priv->updates_queue + && !update_dns (self, FALSE, &error)) { _LOGW ("could not commit DNS changes: %s", error->message); g_clear_error (&error); } @@ -1288,38 +1637,6 @@ nm_dns_manager_add_ip_config (NMDnsManager *self, return TRUE; } -gboolean -nm_dns_manager_remove_ip_config (NMDnsManager *self, gpointer config) -{ - NMDnsManagerPrivate *priv; - GError *error = NULL; - NMDnsIPConfigData *data; - guint i; - - g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE); - g_return_val_if_fail (config, FALSE); - nm_assert (NM_IS_IP_CONFIG (config)); - - priv = NM_DNS_MANAGER_GET_PRIVATE (self); - - for (i = 0; i < priv->configs->len; i++) { - data = priv->configs->pdata[i]; - - if (data->config == config) { - forget_data (self, data); - g_ptr_array_remove_index (priv->configs, i); - - if (!priv->updates_queue && !update_dns (self, FALSE, &error)) { - _LOGW ("could not commit DNS changes: %s", error->message); - g_clear_error (&error); - } - - return TRUE; - } - } - return FALSE; -} - void nm_dns_manager_set_initial_hostname (NMDnsManager *self, const char *hostname) @@ -1362,23 +1679,6 @@ nm_dns_manager_set_hostname (NMDnsManager *self, } } -gboolean -nm_dns_manager_get_resolv_conf_explicit (NMDnsManager *self) -{ - NMDnsManagerPrivate *priv; - - g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE); - - priv = NM_DNS_MANAGER_GET_PRIVATE (self); - - if ( NM_IN_SET (priv->rc_manager, NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED, - NM_DNS_MANAGER_RESOLV_CONF_MAN_IMMUTABLE) - || priv->plugin) - return FALSE; - - return TRUE; -} - void nm_dns_manager_begin_updates (NMDnsManager *self, const char *func) { @@ -1409,11 +1709,6 @@ nm_dns_manager_end_updates (NMDnsManager *self, const char *func) priv = NM_DNS_MANAGER_GET_PRIVATE (self); g_return_if_fail (priv->updates_queue > 0); - if (priv->need_sort) { - g_ptr_array_sort (priv->configs, ip_config_data_ptr_compare); - priv->need_sort = FALSE; - } - 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"); @@ -1810,14 +2105,14 @@ _get_config_variant (NMDnsManager *self) NMGlobalDnsConfig *global_config; gs_free char *str = NULL; GVariantBuilder builder; - NMConfigData *data; - guint i, j; + NMDnsIPConfigData *ip_data; + const CList *head; + gs_unref_ptrarray GPtrArray *array_domains = NULL; if (priv->config_variant) return priv->config_variant; - data = nm_config_get_data (priv->config); - global_config = nm_config_data_get_global_dns_config (data); + global_config = nm_config_data_get_global_dns_config (nm_config_get_data (priv->config)); if (global_config) { priv->config_variant = _get_global_config_variant (global_config); _LOGT ("current configuration: %s", (str = g_variant_print (priv->config_variant, TRUE))); @@ -1826,25 +2121,26 @@ _get_config_variant (NMDnsManager *self) g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); - for (i = 0; i < priv->configs->len; i++) { - NMDnsIPConfigData *current = priv->configs->pdata[i]; - const NMIPConfig *config = current->config; + head = _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 num; - const int addr_family = nm_ip_config_get_addr_family (config); + 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; - num = nm_ip_config_get_num_nameservers (config); + num = nm_ip_config_get_num_nameservers (ip_config); if (!num) continue; g_variant_builder_init (&entry_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as")); - for (j = 0; j < num; j++) { - addr = nm_ip_config_get_nameserver (config, j); + 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)); @@ -1854,36 +2150,46 @@ _get_config_variant (NMDnsManager *self) "nameservers", g_variant_builder_end (&strv_builder)); - num = nm_ip_config_get_num_domains (config); + num = nm_ip_config_get_num_domains (ip_config); + num += nm_ip_config_get_num_searches (ip_config); if (num > 0) { - g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as")); - for (j = 0; j < num; j++) { - g_variant_builder_add (&strv_builder, - "s", - nm_ip_config_get_domain (config, j)); + 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); + 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_builder_add (&entry_builder, - "{sv}", - "domains", - g_variant_builder_end (&strv_builder)); } - if (current->iface) { + ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ip_data->data->ifindex); + if (ifname) { g_variant_builder_add (&entry_builder, "{sv}", "interface", - g_variant_new_string (current->iface)); + g_variant_new_string (ifname)); } g_variant_builder_add (&entry_builder, "{sv}", "priority", - g_variant_new_int32 (nm_ip_config_get_dns_priority (config))); + g_variant_new_int32 (nm_ip_config_get_dns_priority (ip_config))); g_variant_builder_add (&entry_builder, "{sv}", "vpn", - g_variant_new_boolean (current->type == NM_DNS_IP_CONFIG_TYPE_VPN)); + g_variant_new_boolean (ip_data->ip_config_type == NM_DNS_IP_CONFIG_TYPE_VPN)); g_variant_builder_add (&builder, "a{sv}", &entry_builder); } @@ -1924,8 +2230,12 @@ nm_dns_manager_init (NMDnsManager *self) _LOGT ("creating..."); + c_list_init (&priv->ip_config_lst_head); + priv->config = g_object_ref (nm_config_get ()); - priv->configs = g_ptr_array_new_full (8, ip_config_data_destroy); + + priv->configs = g_hash_table_new_full (nm_direct_hash, NULL, + NULL, (GDestroyNotify) _config_data_free); /* Set the initial hash */ compute_hash (self, NULL, NM_DNS_MANAGER_GET_PRIVATE (self)->hash); @@ -1942,33 +2252,33 @@ dispose (GObject *object) { NMDnsManager *self = NM_DNS_MANAGER (object); NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); - NMDnsIPConfigData *data; - guint i; + NMDnsIPConfigData *ip_data, *ip_data_safe; _LOGT ("disposing"); if (!priv->is_stopped) nm_dns_manager_stop (self); + if (priv->config) + g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self); + _clear_plugin (self); - if (priv->config) { - g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self); - g_clear_object (&priv->config); - } + priv->best_ip_config_4 = NULL; + priv->best_ip_config_6 = NULL; - if (priv->configs) { - for (i = 0; i < priv->configs->len; i++) { - data = priv->configs->pdata[i]; - forget_data (self, data); - } - g_ptr_array_free (priv->configs, TRUE); - priv->configs = NULL; - } + c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_config_lst_head, ip_config_lst) + _ip_config_data_free (ip_data); + + g_clear_pointer (&priv->configs, g_hash_table_destroy); nm_clear_g_source (&priv->plugin_ratelimit.timer); + g_clear_object (&priv->config); + G_OBJECT_CLASS (nm_dns_manager_parent_class)->dispose (object); + + g_clear_pointer (&priv->config_variant, g_variant_unref); } static void @@ -1983,18 +2293,30 @@ finalize (GObject *object) G_OBJECT_CLASS (nm_dns_manager_parent_class)->finalize (object); } +static const NMDBusInterfaceInfoExtended interface_info_dns_manager = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DNS_MANAGER, + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode", "s", NM_DNS_MANAGER_MODE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("RcManager", "s", NM_DNS_MANAGER_RC_MANAGER), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Configuration", "aa{sv}", NM_DNS_MANAGER_CONFIGURATION), + ), + ), +}; + static void nm_dns_manager_class_init (NMDnsManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); object_class->dispose = dispose; object_class->finalize = finalize; object_class->get_property = get_property; - exported_object_class->export_path = NM_DBUS_PATH "/DnsManager"; - exported_object_class->export_on_construction = TRUE; + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_STATIC (NM_DBUS_PATH "/DnsManager"); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_dns_manager); + dbus_object_class->export_on_construction = TRUE; obj_properties[PROP_MODE] = g_param_spec_string (NM_DNS_MANAGER_MODE, "", "", @@ -2024,9 +2346,4 @@ nm_dns_manager_class_init (NMDnsManagerClass *klass) 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), - NMDBUS_TYPE_DNS_MANAGER_SKELETON, - NULL); } - diff --git a/src/dns/nm-dns-manager.h b/src/dns/nm-dns-manager.h index b38ea701..ed1974a5 100644 --- a/src/dns/nm-dns-manager.h +++ b/src/dns/nm-dns-manager.h @@ -26,11 +26,14 @@ #include "nm-ip4-config.h" #include "nm-ip6-config.h" +#include "nm-setting-connection.h" typedef enum { + NM_DNS_IP_CONFIG_TYPE_REMOVED = -1, + NM_DNS_IP_CONFIG_TYPE_DEFAULT = 0, NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE, - NM_DNS_IP_CONFIG_TYPE_VPN + NM_DNS_IP_CONFIG_TYPE_VPN, } NMDnsIPConfigType; enum { @@ -38,12 +41,27 @@ enum { NM_DNS_PRIORITY_DEFAULT_VPN = 50, }; +struct _NMDnsConfigData; +struct _NMDnsManager; + typedef struct { - gpointer config; - NMDnsIPConfigType type; - char *iface; + struct _NMDnsConfigData *data; + NMIPConfig *ip_config; + CList data_lst; + CList ip_config_lst; + NMDnsIPConfigType ip_config_type; + struct { + const char **search; + char **reverse; + } domains; } NMDnsIPConfigData; +typedef struct _NMDnsConfigData { + struct _NMDnsManager *self; + CList data_lst_head; + int ifindex; +} NMDnsConfigData; + #define NM_TYPE_DNS_MANAGER (nm_dns_manager_get_type ()) #define NM_DNS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_DNS_MANAGER, NMDnsManager)) #define NM_DNS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_DNS_MANAGER, NMDnsManagerClass)) @@ -70,12 +88,9 @@ 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_add_ip_config (NMDnsManager *self, - const char *iface, - gpointer config, - NMDnsIPConfigType cfg_type); - -gboolean nm_dns_manager_remove_ip_config (NMDnsManager *self, gpointer config); +gboolean nm_dns_manager_set_ip_config (NMDnsManager *self, + NMIPConfig *ip_config, + NMDnsIPConfigType ip_config_type); void nm_dns_manager_set_initial_hostname (NMDnsManager *self, const char *hostname); @@ -112,8 +127,6 @@ typedef enum { NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG, } NMDnsManagerResolvConfManager; -gboolean nm_dns_manager_get_resolv_conf_explicit (NMDnsManager *self); - void nm_dns_manager_stop (NMDnsManager *self); #endif /* __NETWORKMANAGER_DNS_MANAGER_H__ */ diff --git a/src/dns/nm-dns-plugin.c b/src/dns/nm-dns-plugin.c index 5805b7d8..d9400e3e 100644 --- a/src/dns/nm-dns-plugin.c +++ b/src/dns/nm-dns-plugin.c @@ -77,15 +77,15 @@ G_DEFINE_TYPE_EXTENDED (NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT, G_TYPE_FLAG_A gboolean nm_dns_plugin_update (NMDnsPlugin *self, - const GPtrArray *configs, const NMGlobalDnsConfig *global_config, + const CList *ip_config_lst_head, const char *hostname) { g_return_val_if_fail (NM_DNS_PLUGIN_GET_CLASS (self)->update != NULL, FALSE); return NM_DNS_PLUGIN_GET_CLASS (self)->update (self, - configs, global_config, + ip_config_lst_head, hostname); } diff --git a/src/dns/nm-dns-plugin.h b/src/dns/nm-dns-plugin.h index 996695c0..80b77d95 100644 --- a/src/dns/nm-dns-plugin.h +++ b/src/dns/nm-dns-plugin.h @@ -50,8 +50,8 @@ typedef struct { * configuration. */ gboolean (*update) (NMDnsPlugin *self, - const GPtrArray *configs, const NMGlobalDnsConfig *global_config, + const CList *ip_config_lst_head, const char *hostname); /* Subclasses should override and return TRUE if they start a local @@ -80,8 +80,8 @@ gboolean nm_dns_plugin_is_caching (NMDnsPlugin *self); const char *nm_dns_plugin_get_name (NMDnsPlugin *self); gboolean nm_dns_plugin_update (NMDnsPlugin *self, - const GPtrArray *configs, const NMGlobalDnsConfig *global_config, + const CList *ip_config_lst_head, const char *hostname); void nm_dns_plugin_stop (NMDnsPlugin *self); diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c index 6ab2ea18..1a73aae1 100644 --- a/src/dns/nm-dns-systemd-resolved.c +++ b/src/dns/nm-dns-systemd-resolved.c @@ -31,13 +31,15 @@ #include #include +#include "nm-utils/nm-c-list.h" #include "nm-core-internal.h" #include "platform/nm-platform.h" #include "nm-utils.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" -#include "nm-bus-manager.h" +#include "nm-dbus-manager.h" #include "nm-manager.h" +#include "nm-setting-connection.h" #include "devices/nm-device.h" #include "NetworkManagerUtils.h" @@ -48,17 +50,23 @@ typedef struct { int ifindex; - GList *configs; + CList configs_lst_head; } InterfaceConfig; +typedef struct { + CList request_queue_lst; + const char *operation; + GVariant *argument; +} RequestItem; + /*****************************************************************************/ typedef struct { GDBusProxy *resolve; GCancellable *init_cancellable; GCancellable *update_cancellable; - GQueue dns_updates; - GQueue domain_updates; + GCancellable *mdns_cancellable; + CList request_queue_lst_head; } NMDnsSystemdResolvedPrivate; struct _NMDnsSystemdResolved { @@ -82,130 +90,85 @@ G_DEFINE_TYPE (NMDnsSystemdResolved, nm_dns_systemd_resolved, NM_TYPE_DNS_PLUGIN /*****************************************************************************/ static void -call_done (GObject *source, GAsyncResult *r, gpointer user_data) +_request_item_free (RequestItem *request_item) { - GVariant *v; - GError *error = NULL; - NMDnsSystemdResolved *self = (NMDnsSystemdResolved *) user_data; - - v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), r, &error); + c_list_unlink_stale (&request_item->request_queue_lst); + g_variant_unref (request_item->argument); + g_slice_free (RequestItem, request_item); +} - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; +static void +_request_item_append (CList *request_queue_lst_head, + const char *operation, + GVariant *argument) +{ + RequestItem *request_item; - if (error != NULL) { - _LOGW ("Failed: %s\n", error->message); - g_error_free (error); - } + request_item = g_slice_new (RequestItem); + request_item->operation = operation; + request_item->argument = g_variant_ref_sink (argument); + c_list_link_tail (request_queue_lst_head, &request_item->request_queue_lst); } +/*****************************************************************************/ + static void -add_interface_configuration (NMDnsSystemdResolved *self, - GArray *interfaces, - const NMDnsIPConfigData *data, - gboolean skip) +_interface_config_free (InterfaceConfig *config) { - int i; - InterfaceConfig *ic = NULL; - int ifindex; + nm_c_list_elem_free_all (&config->configs_lst_head, NULL); + g_slice_free (InterfaceConfig, config); +} - if (NM_IS_IP4_CONFIG (data->config)) - ifindex = nm_ip4_config_get_ifindex (data->config); - else if (NM_IS_IP6_CONFIG (data->config)) - ifindex = nm_ip6_config_get_ifindex (data->config); - else - g_return_if_reached (); - - for (i = 0; i < interfaces->len; i++) { - InterfaceConfig *tic = &g_array_index (interfaces, InterfaceConfig, i); - if (ifindex == tic->ifindex) { - ic = tic; - break; - } - } +static void +call_done (GObject *source, GAsyncResult *r, gpointer user_data) +{ + GVariant *v; + GError *error = NULL; + NMDnsSystemdResolved *self = (NMDnsSystemdResolved *) user_data; - if (!ic) { - g_array_set_size (interfaces, interfaces->len + 1); - ic = &g_array_index (interfaces, InterfaceConfig, - interfaces->len - 1); - ic->ifindex = ifindex; + v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), r, &error); + if (!v) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + _LOGW ("Failed: %s\n", error->message); + g_error_free (error); } - - if (!skip) - ic->configs = g_list_append (ic->configs, data->config); } static void update_add_ip_config (NMDnsSystemdResolved *self, GVariantBuilder *dns, GVariantBuilder *domains, - gpointer config) + NMDnsIPConfigData *data) { int addr_family; gsize addr_size; guint i, n; - gboolean route_only; - - if (NM_IS_IP4_CONFIG (config)) - addr_family = AF_INET; - else if (NM_IS_IP6_CONFIG (config)) - addr_family = AF_INET6; - else - g_return_if_reached (); + gboolean is_routing; + const char **iter; + const char *domain; + addr_family = nm_ip_config_get_addr_family (data->ip_config); addr_size = nm_utils_addr_family_to_size (addr_family); - n = addr_family == AF_INET - ? nm_ip4_config_get_num_nameservers (config) - : nm_ip6_config_get_num_nameservers (config); - for (i = 0 ; i < n; i++) { - in_addr_t ns4; - gconstpointer ns; - - if (addr_family == AF_INET) { - ns4 = nm_ip4_config_get_nameserver (config, i); - ns = &ns4; - } else - ns = nm_ip6_config_get_nameserver (config, i); + if (!data->domains.search || !data->domains.search[0]) + return; + n = nm_ip_config_get_num_nameservers (data->ip_config); + 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_value (dns, g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, - ns, + nm_ip_config_get_nameserver (data->ip_config, i), addr_size, 1)); g_variant_builder_close (dns); } - /* If this link is never the default (e.g. only used for resources on this - * network) add a routing domain. */ - route_only = addr_family == AF_INET - ? !nm_ip4_config_best_default_route_get (config) - : !nm_ip6_config_best_default_route_get (config); - - n = addr_family == AF_INET - ? nm_ip4_config_get_num_searches (config) - : nm_ip6_config_get_num_searches (config); - if (n > 0) { - for (i = 0; i < n; i++) { - g_variant_builder_add (domains, "(sb)", - addr_family == AF_INET - ? nm_ip4_config_get_search (config, i) - : nm_ip6_config_get_search (config, i), - route_only); - } - } else { - n = addr_family == AF_INET - ? nm_ip4_config_get_num_domains (config) - : nm_ip6_config_get_num_domains (config); - for (i = 0; i < n; i++) { - g_variant_builder_add (domains, "(sb)", - addr_family == AF_INET - ? nm_ip4_config_get_domain (config, i) - : nm_ip6_config_get_domain (config, i), - route_only); - } + for (iter = data->domains.search; *iter; iter++) { + domain = nm_utils_parse_dns_domain (*iter, &is_routing); + g_variant_builder_add (domains, "(sb)", domain[0] ? domain : ".", is_routing); } } @@ -213,13 +176,13 @@ static void free_pending_updates (NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); - GVariant *v; + RequestItem *request_item, *request_item_safe; - while ((v = g_queue_pop_head (&priv->dns_updates)) != NULL) - g_variant_unref (v); - - while ((v = g_queue_pop_head (&priv->domain_updates)) != NULL) - g_variant_unref (v); + c_list_for_each_entry_safe (request_item, + request_item_safe, + &priv->request_queue_lst_head, + request_queue_lst) + _request_item_free (request_item); } static void @@ -227,7 +190,9 @@ prepare_one_interface (NMDnsSystemdResolved *self, InterfaceConfig *ic) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); GVariantBuilder dns, domains; - GList *l; + NMCListElem *elem; + NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT; + const char *mdns_arg = NULL; g_variant_builder_init (&dns, G_VARIANT_TYPE ("(ia(iay))")); g_variant_builder_add (&dns, "i", ic->ifindex); @@ -237,23 +202,51 @@ prepare_one_interface (NMDnsSystemdResolved *self, InterfaceConfig *ic) g_variant_builder_add (&domains, "i", ic->ifindex); g_variant_builder_open (&domains, G_VARIANT_TYPE ("a(sb)")); - for (l = ic->configs; l; l = l->next) - update_add_ip_config (self, &dns, &domains, l->data); + c_list_for_each_entry (elem, &ic->configs_lst_head, lst) { + NMDnsIPConfigData *data = elem->data; + NMIPConfig *ip_config = data->ip_config; + + update_add_ip_config (self, &dns, &domains, data); + + if (NM_IS_IP4_CONFIG (ip_config)) + mdns = NM_MAX (mdns, nm_ip4_config_mdns_get (NM_IP4_CONFIG (ip_config))); + } g_variant_builder_close (&dns); g_variant_builder_close (&domains); - g_queue_push_tail (&priv->dns_updates, - g_variant_ref_sink (g_variant_builder_end (&dns))); - g_queue_push_tail (&priv->domain_updates, - g_variant_ref_sink (g_variant_builder_end (&domains))); + switch (mdns) { + case NM_SETTING_CONNECTION_MDNS_NO: + mdns_arg = "no"; + break; + case NM_SETTING_CONNECTION_MDNS_RESOLVE: + mdns_arg = "resolve"; + break; + case NM_SETTING_CONNECTION_MDNS_YES: + mdns_arg = "yes"; + break; + case NM_SETTING_CONNECTION_MDNS_DEFAULT: + mdns_arg = ""; + break; + } + nm_assert (mdns_arg); + + _request_item_append (&priv->request_queue_lst_head, + "SetLinkDNS", + g_variant_builder_end (&dns)); + _request_item_append (&priv->request_queue_lst_head, + "SetLinkDomains", + g_variant_builder_end (&domains)); + _request_item_append (&priv->request_queue_lst_head, + "SetLinkMulticastDNS", + g_variant_new ("(is)", ic->ifindex, mdns_arg ?: "")); } static void send_updates (NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); - GVariant *v; + RequestItem *request_item, *request_item_safe; nm_clear_g_cancellable (&priv->update_cancellable); @@ -262,55 +255,69 @@ send_updates (NMDnsSystemdResolved *self) priv->update_cancellable = g_cancellable_new (); - while ((v = g_queue_pop_head (&priv->dns_updates)) != NULL) { - g_dbus_proxy_call (priv->resolve, "SetLinkDNS", v, + c_list_for_each_entry_safe (request_item, + request_item_safe, + &priv->request_queue_lst_head, + request_queue_lst) { + g_dbus_proxy_call (priv->resolve, + request_item->operation, + request_item->argument, G_DBUS_CALL_FLAGS_NONE, - -1, priv->update_cancellable, call_done, self); - g_variant_unref (v); - } - - while ((v = g_queue_pop_head (&priv->domain_updates)) != NULL) { - g_dbus_proxy_call (priv->resolve, "SetLinkDomains", v, - G_DBUS_CALL_FLAGS_NONE, - -1, priv->update_cancellable, call_done, self); - g_variant_unref (v); + -1, + priv->update_cancellable, + call_done, + self); + _request_item_free (request_item); } } static gboolean update (NMDnsPlugin *plugin, - const GPtrArray *configs, const NMGlobalDnsConfig *global_config, + const CList *ip_config_lst_head, const char *hostname) { NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED (plugin); - GArray *interfaces = g_array_new (TRUE, TRUE, sizeof (InterfaceConfig)); + gs_unref_hashtable GHashTable *interfaces = NULL; + gs_free gpointer *interfaces_keys = NULL; + guint interfaces_len; guint i; - int prio, first_prio = 0; - - for (i = 0; i < configs->len; i++) { - const NMDnsIPConfigData *data = configs->pdata[i]; - gboolean skip = FALSE; - - prio = nm_ip_config_get_dns_priority (data->config); - if (i == 0) - first_prio = prio; - else if (first_prio < 0 && first_prio != prio) - skip = TRUE; - add_interface_configuration (self, interfaces, data, skip); + NMDnsIPConfigData *ip_data; + + 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) { + InterfaceConfig *ic = NULL; + int ifindex; + + ifindex = ip_data->data->ifindex; + nm_assert (ifindex == nm_ip_config_get_ifindex (ip_data->ip_config)); + + ic = g_hash_table_lookup (interfaces, GINT_TO_POINTER (ifindex)); + if (!ic) { + ic = g_slice_new (InterfaceConfig); + ic->ifindex = ifindex; + c_list_init (&ic->configs_lst_head); + g_hash_table_insert (interfaces, GINT_TO_POINTER (ifindex), ic); + } + + c_list_link_tail (&ic->configs_lst_head, + &nm_c_list_elem_new_stale (ip_data)->lst); } free_pending_updates (self); - for (i = 0; i < interfaces->len; i++) { - InterfaceConfig *ic = &g_array_index (interfaces, InterfaceConfig, i); + interfaces_keys = nm_utils_hash_keys_to_array (interfaces, + nm_cmp_int2ptr_p_with_data, + NULL, + &interfaces_len); + for (i = 0; i < interfaces_len; i++) { + InterfaceConfig *ic = g_hash_table_lookup (interfaces, GINT_TO_POINTER (interfaces_keys[i])); prepare_one_interface (self, ic); - g_list_free (ic->configs); } - g_array_free (interfaces, TRUE); - send_updates (self); return TRUE; @@ -363,16 +370,15 @@ static void nm_dns_systemd_resolved_init (NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); - NMBusManager *dbus_mgr; + NMDBusManager *dbus_mgr; GDBusConnection *connection; - g_queue_init (&priv->dns_updates); - g_queue_init (&priv->domain_updates); + c_list_init (&priv->request_queue_lst_head); - dbus_mgr = nm_bus_manager_get (); + dbus_mgr = nm_dbus_manager_get (); g_return_if_fail (dbus_mgr); - connection = nm_bus_manager_get_connection (dbus_mgr); + connection = nm_dbus_manager_get_connection (dbus_mgr); g_return_if_fail (connection); priv->init_cancellable = g_cancellable_new (); @@ -404,6 +410,7 @@ dispose (GObject *object) g_clear_object (&priv->resolve); nm_clear_g_cancellable (&priv->init_cancellable); nm_clear_g_cancellable (&priv->update_cancellable); + nm_clear_g_cancellable (&priv->mdns_cancellable); G_OBJECT_CLASS (nm_dns_systemd_resolved_parent_class)->dispose (object); } diff --git a/src/dns/nm-dns-unbound.c b/src/dns/nm-dns-unbound.c index 0b80055f..e06128aa 100644 --- a/src/dns/nm-dns-unbound.c +++ b/src/dns/nm-dns-unbound.c @@ -39,8 +39,8 @@ G_DEFINE_TYPE (NMDnsUnbound, nm_dns_unbound, NM_TYPE_DNS_PLUGIN) static gboolean update (NMDnsPlugin *plugin, - const GPtrArray *configs, const NMGlobalDnsConfig *global_config, + const CList *ip_config_lst_head, const char *hostname) { char *argv[] = { DNSSEC_TRIGGER_SCRIPT, "--async", "--update", NULL }; diff --git a/src/dnsmasq/nm-dnsmasq-manager.c b/src/dnsmasq/nm-dnsmasq-manager.c index 323ef781..041e75ea 100644 --- a/src/dnsmasq/nm-dnsmasq-manager.c +++ b/src/dnsmasq/nm-dnsmasq-manager.c @@ -181,7 +181,7 @@ create_dm_cmd_line (const char *iface, nm_cmd_line_add_string (cmd, "--log-queries"); } - /* dnsmasq may read from it's default config file location, which if that + /* dnsmasq may read from its default config file location, which if that * location is a valid config file, it will combine with the options here * and cause undesirable side-effects. Like sending bogus IP addresses * as the gateway or whatever. So tell dnsmasq not to use any config file diff --git a/src/dnsmasq/tests/meson.build b/src/dnsmasq/tests/meson.build new file mode 100644 index 00000000..12106c52 --- /dev/null +++ b/src/dnsmasq/tests/meson.build @@ -0,0 +1,13 @@ +test_unit = 'test-dnsmasq-utils' + +exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, +) + +test( + 'dnsmasq/' + test_unit, + test_script, + args: test_args + [exe.full_path()] +) diff --git a/src/main.c b/src/main.c index d59da052..c1267b27 100644 --- a/src/main.c +++ b/src/main.c @@ -39,7 +39,7 @@ #include "NetworkManagerUtils.h" #include "nm-manager.h" #include "platform/nm-linux-platform.h" -#include "nm-bus-manager.h" +#include "nm-dbus-manager.h" #include "devices/nm-device.h" #include "dhcp/nm-dhcp-manager.h" #include "nm-config.h" @@ -48,7 +48,7 @@ #include "settings/nm-settings.h" #include "nm-auth-manager.h" #include "nm-core-internal.h" -#include "nm-exported-object.h" +#include "nm-dbus-object.h" #include "nm-connectivity.h" #include "dns/nm-dns-manager.h" #include "systemd/nm-sd.h" @@ -93,8 +93,10 @@ static void _init_nm_debug (NMConfig *config) { gs_free char *debug = NULL; - const guint D_RLIMIT_CORE = 1; - const guint D_FATAL_WARNINGS = 2; + enum { + D_RLIMIT_CORE = (1 << 0), + D_FATAL_WARNINGS = (1 << 1), + }; GDebugKey keys[] = { { "RLIMIT_CORE", D_RLIMIT_CORE }, { "fatal-warnings", D_FATAL_WARNINGS }, @@ -212,7 +214,7 @@ do_early_setup (int *argc, char **argv[], NMConfigCmdLineOptions *config_cli) _("NetworkManager monitors all network connections and automatically\nchooses the best connection to use. It also allows the user to\nspecify wireless access points which wireless cards in the computer\nshould associate with."))) exit (1); - global_opt.pidfile = global_opt.pidfile ? global_opt.pidfile : g_strdup (NM_DEFAULT_PID_FILE); + global_opt.pidfile = global_opt.pidfile ?: g_strdup(NM_DEFAULT_PID_FILE); } /* @@ -223,6 +225,7 @@ int main (int argc, char *argv[]) { gboolean success = FALSE; + NMManager *manager = NULL; NMConfig *config; GError *error = NULL; gboolean wrote_pidfile = FALSE; @@ -230,13 +233,11 @@ main (int argc, char *argv[]) NMConfigCmdLineOptions *config_cli; guint sd_id = 0; - nm_g_type_init (); - /* Known to cause a possible deadlock upon GDBus initialization: * https://bugzilla.gnome.org/show_bug.cgi?id=674885 */ g_type_ensure (G_TYPE_SOCKET); g_type_ensure (G_TYPE_DBUS_CONNECTION); - g_type_ensure (NM_TYPE_BUS_MANAGER); + g_type_ensure (NM_TYPE_DBUS_MANAGER); _nm_utils_is_manager_process = TRUE; @@ -394,27 +395,19 @@ main (int argc, char *argv[]) NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT, NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_BOOL)); - nm_manager_setup (); + if (!nm_dbus_manager_acquire_bus (nm_dbus_manager_get ())) + goto done_no_manager; - if (!nm_bus_manager_get_connection (nm_bus_manager_get ())) { - nm_log_warn (LOGD_CORE, "Failed to connect to D-Bus; only private bus is available"); - } else { - /* Start our DBus service */ - if (!nm_bus_manager_start_service (nm_bus_manager_get ())) { - nm_log_err (LOGD_CORE, "failed to start the dbus service."); - goto done; - } - } - -#if WITH_CONCHECK - NM_UTILS_KEEP_ALIVE (nm_manager_get (), nm_connectivity_get (), "NMManager-depends-on-NMConnectivity"); -#endif + manager = nm_manager_setup (); + nm_dbus_manager_start (nm_dbus_manager_get(), + nm_manager_dbus_set_property_handle, + manager); nm_dispatcher_init (); - g_signal_connect (nm_manager_get (), NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config); + g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config); - if (!nm_manager_start (nm_manager_get (), &error)) { + if (!nm_manager_start (manager, &error)) { nm_log_err (LOGD_CORE, "failed to initialize: %s", error->message); goto done; } @@ -448,16 +441,15 @@ done: * state here. We don't bother updating the state as devices * change during regular operation. If NM is killed with SIGKILL, * it misses to update the state. */ - nm_manager_write_device_state (nm_manager_get ()); - - nm_exported_object_class_set_quitting (); + nm_manager_write_device_state (manager); - nm_manager_stop (nm_manager_get ()); + nm_manager_stop (manager); nm_config_state_set (config, TRUE, TRUE); nm_dns_manager_stop (nm_dns_manager_get ()); +done_no_manager: if (global_opt.pidfile && wrote_pidfile) unlink (global_opt.pidfile); diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 00000000..e60ab93f --- /dev/null +++ b/src/meson.build @@ -0,0 +1,319 @@ +src_inc = include_directories('.') + +install_data( + 'org.freedesktop.NetworkManager.conf', + install_dir: dbus_conf_dir +) + +subdir('systemd') + +core_plugins = [] + +nm_cflags = ['-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_DAEMON'] + +nm_dep = declare_dependency( + include_directories: src_inc, + dependencies: nm_core_dep, + compile_args: nm_cflags +) + +cflags = nm_cflags + [ + '-DPREFIX="@0@"'.format(nm_prefix), + '-DBINDIR="@0@"'.format(nm_bindir), + '-DDATADIR="@0@"'.format(nm_datadir), + '-DLIBEXECDIR="@0@"'.format(nm_libexecdir), + '-DLOCALSTATEDIR="@0@"'.format(nm_localstatedir), + '-DRUNSTATEDIR="@0@"'.format(nm_runstatedir), + '-DSBINDIR="@0@"'.format(nm_sbindir), + '-DSYSCONFDIR="@0@"'.format(nm_sysconfdir), + '-DRUNDIR="@0@"'.format(nm_pkgrundir), + '-DNMCONFDIR="@0@"'.format(nm_pkgconfdir), + '-DNMLOCALEDIR="@0@"'.format(nm_localedir), + '-DNMPLUGINDIR="@0@"'.format(nm_plugindir), + '-DNMRUNDIR="@0@"'.format(nm_pkgrundir), + '-DNMSTATEDIR="@0@"'.format(nm_pkgstatedir), + '-DNMLIBDIR="@0@"'.format(nm_pkglibdir) +] + +if enable_dhcpcanon + cflags += '-DDHCPCANON_PATH="@0@"'.format(dhcpcanon.path()) +endif + +if enable_dhclient + cflags += '-DDHCLIENT_PATH="@0@"'.format(dhclient.path()) +endif + +if enable_dhcpcd + cflags += '-DDHCPCD_PATH="@0@"'.format(dhcpcd.path()) +endif + +sources = files( + 'dhcp/nm-dhcp-client.c', + 'dhcp/nm-dhcp-manager.c', + 'dhcp/nm-dhcp-systemd.c', + 'dhcp/nm-dhcp-utils.c', + 'ndisc/nm-lndp-ndisc.c', + 'ndisc/nm-ndisc.c', + 'platform/nm-netlink.c', + 'platform/wifi/wifi-utils-nl80211.c', + 'platform/wifi/wifi-utils.c', + 'platform/nm-linux-platform.c', + 'platform/nm-platform.c', + 'platform/nm-platform-utils.c', + 'platform/nmp-netns.c', + 'platform/nmp-object.c', + 'main-utils.c', + 'NetworkManagerUtils.c', + 'nm-core-utils.c', + 'nm-dbus-object.c', + 'nm-dbus-utils.c', + 'nm-ip4-config.c', + 'nm-ip6-config.c', + 'nm-logging.c' +) + +deps = [ + libsystemd_dep, + libudev_dep, + nm_core_dep +] + +if enable_wext + sources += files('platform/wifi/wifi-utils-wext.c') +endif + +libnetwork_manager_base = static_library( + nm_name + 'Base', + sources: sources, + dependencies: deps, + c_args: cflags, + link_with: libnm_core +) + +sources = files( + 'devices/nm-acd-manager.c', + 'devices/nm-device-bond.c', + 'devices/nm-device-bridge.c', + 'devices/nm-device.c', + 'devices/nm-device-dummy.c', + 'devices/nm-device-ethernet.c', + 'devices/nm-device-ethernet-utils.c', + 'devices/nm-device-factory.c', + 'devices/nm-device-generic.c', + 'devices/nm-device-infiniband.c', + 'devices/nm-device-ip-tunnel.c', + 'devices/nm-device-macsec.c', + 'devices/nm-device-macvlan.c', + 'devices/nm-device-ppp.c', + 'devices/nm-device-tun.c', + 'devices/nm-device-veth.c', + 'devices/nm-device-vlan.c', + 'devices/nm-device-vxlan.c', + 'devices/nm-lldp-listener.c', + 'dhcp/nm-dhcp-dhclient.c', + 'dhcp/nm-dhcp-dhclient-utils.c', + 'dhcp/nm-dhcp-dhcpcanon.c', + 'dhcp/nm-dhcp-dhcpcd.c', + 'dhcp/nm-dhcp-listener.c', + 'dns/nm-dns-dnsmasq.c', + 'dns/nm-dns-manager.c', + 'dns/nm-dns-plugin.c', + 'dns/nm-dns-systemd-resolved.c', + 'dns/nm-dns-unbound.c', + 'dnsmasq/nm-dnsmasq-manager.c', + 'dnsmasq/nm-dnsmasq-utils.c', + 'ppp/nm-ppp-manager-call.c', + 'settings/plugins/keyfile/nms-keyfile-connection.c', + 'settings/plugins/keyfile/nms-keyfile-plugin.c', + 'settings/plugins/keyfile/nms-keyfile-reader.c', + 'settings/plugins/keyfile/nms-keyfile-utils.c', + 'settings/plugins/keyfile/nms-keyfile-writer.c', + 'settings/nm-agent-manager.c', + 'settings/nm-secret-agent.c', + 'settings/nm-settings.c', + 'settings/nm-settings-connection.c', + 'settings/nm-settings-plugin.c', + 'supplicant/nm-supplicant-config.c', + 'supplicant/nm-supplicant-interface.c', + 'supplicant/nm-supplicant-manager.c', + 'supplicant/nm-supplicant-settings-verify.c', + 'vpn/nm-vpn-connection.c', + 'vpn/nm-vpn-manager.c', + 'nm-active-connection.c', + 'nm-act-request.c', + 'nm-audit-manager.c', + 'nm-auth-manager.c', + 'nm-auth-subject.c', + 'nm-auth-utils.c', + 'nm-dbus-manager.c', + 'nm-checkpoint.c', + 'nm-checkpoint-manager.c', + 'nm-config.c', + 'nm-config-data.c', + 'nm-connectivity.c', + 'nm-dcb.c', + 'nm-dhcp4-config.c', + 'nm-dhcp6-config.c', + 'nm-dispatcher.c', + 'nm-firewall-manager.c', + 'nm-hostname-manager.c', + 'nm-manager.c', + 'nm-netns.c', + 'nm-pacrunner-manager.c', + 'nm-policy.c', + 'nm-proxy-config.c', + 'nm-rfkill-manager.c', + 'nm-session-monitor.c', + 'nm-sleep-monitor.c' +) + +deps = [ + dl_dep, + libndp_dep, + # FIXME: Some files use introspection/dbus* headers, so + # this dependency might be needed + #libnmdbus_dep, + libudev_dep, + nm_core_dep, + shared_n_acd_dep +] + +if enable_concheck + deps += libcurl_dep +endif + +if enable_libaudit + deps += libaudit_dep +endif + +if enable_libpsl + deps += libpsl_dep +endif + +if enable_selinux + deps += selinux_dep +endif + +if enable_session_tracking + deps += logind_dep +endif + +libnetwork_manager = static_library( + nm_name, + sources: sources, + dependencies: deps, + c_args: cflags, + link_with: [libnetwork_manager_base, libsystemd_nm] +) + +ldflags = ['-rdynamic'] + +# FIXME: this doesn't work and it depends on libtool +''' +src/NetworkManager.ver: src/libNetworkManager.la $(core_plugins) + $(AM_V_GEN) NM="$(NM)" "$(srcdir)/tools/create-exports-NetworkManager.sh" --called-from-make "$(srcdir)" + +src_NetworkManager_LDFLAGS = \ + -rdynamic \ + -Wl,--version-script="src/NetworkManager.ver" + +nm = find_program('gcc-nm', 'nm') +create_exports_networkmanager = join_paths(meson.source_root(), 'tools', 'create-exports-NetworkManager.sh') + +symbol_map_name = 'NetworkManager.ver' + +linker_script = custom_target( + symbol_map_name, + input: meson.source_root(), + output: symbol_map_name, + capture: true, + #command: ['NM=' + nm.path(), create_exports_networkmanager, '--called-from-make', '@INPUT@'] + command: [create_exports_networkmanager, '--called-from-make', '@INPUT@'] +) + +ldflags += '-Wl,--version-script,@0@'.format(linker_script) +''' + +network_manager = executable( + nm_name, + 'main.c', + dependencies: deps, + c_args: cflags, + link_with: libnetwork_manager, + link_args: ldflags, + #FIXME + #link_depends: linker_script, + install: true, + install_dir: nm_sbindir +) + +deps = [ + dl_dep, + libndp_dep, + libudev_dep, + nm_core_dep +] + +name = 'nm-iface-helper' + +executable( + name, + name + '.c', + dependencies: deps, + c_args: cflags, + link_with: [libnetwork_manager_base, libsystemd_nm], + link_args: ldflags_linker_script_binary, + link_depends: linker_script_binary, + install: true, + install_dir: nm_libexecdir +) + +if enable_tests + sources = files( + 'ndisc/nm-fake-ndisc.c', + 'platform/tests/test-common.c', + 'platform/nm-fake-platform.c' + ) + + deps = [ + libudev_dep, + nm_core_dep + ] + + test_cflags = ['-DNETWORKMANAGER_COMPILATION_TEST'] + if require_root_tests + test_cflags += ['-DREQUIRE_ROOT_TESTS=1'] + endif + + platform = (host_machine.system().contains('linux') ? 'linux' : 'fake') + test_cflags_platform = '-DSETUP=nm_' + platform + '_platform_setup' + + libnetwork_manager_test = static_library( + nm_name + 'Test', + sources: sources, + dependencies: deps, + c_args: cflags + test_cflags, + link_with: libnetwork_manager + ) + + test_nm_dep = declare_dependency( + dependencies: nm_dep, + compile_args: test_cflags, + link_with: libnetwork_manager_test + ) + + subdir('dnsmasq/tests') + subdir('ndisc/tests') + subdir('platform/tests') + subdir('supplicant/tests') + subdir('tests') +endif + +subdir('dhcp') + +if enable_ppp + subdir('ppp') +endif + +subdir('devices') +subdir('settings/plugins') diff --git a/src/ndisc/nm-lndp-ndisc.c b/src/ndisc/nm-lndp-ndisc.c index 70200ed3..c0a0cd40 100644 --- a/src/ndisc/nm-lndp-ndisc.c +++ b/src/ndisc/nm-lndp-ndisc.c @@ -173,10 +173,10 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) */ { const NMNDiscGateway gateway = { - .address = gateway_addr, - .timestamp = now, - .lifetime = ndp_msgra_router_lifetime (msgra), - .preference = _route_preference_coerce (ndp_msgra_route_preference (msgra)), + .address = gateway_addr, + .timestamp = now, + .lifetime = ndp_msgra_router_lifetime (msgra), + .preference = _route_preference_coerce (ndp_msgra_route_preference (msgra)), }; if (nm_ndisc_add_gateway (ndisc, &gateway)) @@ -195,12 +195,16 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) continue; nm_utils_ip6_address_clear_host_address (&r_network, ndp_msg_opt_prefix (msg, offset), r_plen); + if ( IN6_IS_ADDR_UNSPECIFIED (&r_network) + || IN6_IS_ADDR_LINKLOCAL (&r_network)) + continue; + if (ndp_msg_opt_prefix_flag_on_link (msg, offset)) { - NMNDiscRoute route = { - .network = r_network, - .plen = r_plen, - .timestamp = now, - .lifetime = ndp_msg_opt_prefix_valid_time (msg, offset), + const NMNDiscRoute route = { + .network = r_network, + .plen = r_plen, + .timestamp = now, + .lifetime = ndp_msg_opt_prefix_valid_time (msg, offset), }; if (nm_ndisc_add_route (ndisc, &route)) @@ -211,10 +215,10 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) if ( r_plen == 64 && ndp_msg_opt_prefix_flag_auto_addr_conf (msg, offset)) { NMNDiscAddress address = { - .address = r_network, - .timestamp = now, - .lifetime = ndp_msg_opt_prefix_valid_time (msg, offset), - .preferred = ndp_msg_opt_prefix_preferred_time (msg, offset), + .address = r_network, + .timestamp = now, + .lifetime = ndp_msg_opt_prefix_valid_time (msg, offset), + .preferred = ndp_msg_opt_prefix_preferred_time (msg, offset), }; if (address.preferred > address.lifetime) @@ -225,11 +229,11 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) } ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_ROUTE) { NMNDiscRoute route = { - .gateway = gateway_addr, - .plen = ndp_msg_opt_route_prefix_len (msg, offset), - .timestamp = now, - .lifetime = ndp_msg_opt_route_lifetime (msg, offset), - .preference = _route_preference_coerce (ndp_msg_opt_route_preference (msg, offset)), + .gateway = gateway_addr, + .plen = ndp_msg_opt_route_prefix_len (msg, offset), + .timestamp = now, + .lifetime = ndp_msg_opt_route_lifetime (msg, offset), + .preference = _route_preference_coerce (ndp_msg_opt_route_preference (msg, offset)), }; if (route.plen == 0 || route.plen > 128) @@ -248,9 +252,9 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) ndp_msg_opt_rdnss_for_each_addr (addr, addr_index, msg, offset) { NMNDiscDNSServer dns_server = { - .address = *addr, - .timestamp = now, - .lifetime = ndp_msg_opt_rdnss_lifetime (msg, offset), + .address = *addr, + .timestamp = now, + .lifetime = ndp_msg_opt_rdnss_lifetime (msg, offset), }; /* Pad the lifetime somewhat to give a bit of slack in cases @@ -270,9 +274,9 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) ndp_msg_opt_dnssl_for_each_domain (domain, domain_index, msg, offset) { NMNDiscDNSDomain dns_domain = { - .domain = domain, - .timestamp = now, - .lifetime = ndp_msg_opt_rdnss_lifetime (msg, offset), + .domain = domain, + .timestamp = now, + .lifetime = ndp_msg_opt_rdnss_lifetime (msg, offset), }; /* Pad the lifetime somewhat to give a bit of slack in cases diff --git a/src/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c index 6b44a96c..ba61cb11 100644 --- a/src/ndisc/nm-ndisc.c +++ b/src/ndisc/nm-ndisc.c @@ -89,7 +89,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( ); enum { - CONFIG_CHANGED, + CONFIG_RECEIVED, RA_TIMEOUT, LAST_SIGNAL }; @@ -235,7 +235,7 @@ static void _emit_config_change (NMNDisc *self, NMNDiscConfigMap changed) { _config_changed_log (self, changed); - g_signal_emit (self, signals[CONFIG_CHANGED], 0, + g_signal_emit (self, signals[CONFIG_RECEIVED], 0, _data_complete (&NM_NDISC_GET_PRIVATE (self)->rdata), (guint) changed); } @@ -349,6 +349,11 @@ nm_ndisc_add_address (NMNDisc *ndisc, const NMNDiscAddress *new) NMNDiscDataInternal *rdata = &priv->rdata; guint i; + nm_assert (new); + nm_assert (new->timestamp > 0 && new->timestamp < G_MAXINT32); + nm_assert (!IN6_IS_ADDR_UNSPECIFIED (&new->address)); + nm_assert (!IN6_IS_ADDR_LINKLOCAL (&new->address)); + for (i = 0; i < rdata->addresses->len; i++) { NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i); @@ -792,7 +797,7 @@ nm_ndisc_start (NMNDisc *ndisc) } void -nm_ndisc_dad_failed (NMNDisc *ndisc, struct in6_addr *address) +nm_ndisc_dad_failed (NMNDisc *ndisc, const struct in6_addr *address) { NMNDiscDataInternal *rdata; guint i; @@ -887,6 +892,23 @@ get_expiry_time (guint32 timestamp, guint32 lifetime) : (_item->lifetime) / 2); \ }) +static const char * +_get_exp (char *buf, gsize buf_size, gint64 now_ns, gint32 expiry_time) +{ + int l; + + if (expiry_time == G_MAXINT32) + return "permanent"; + l = g_snprintf (buf, buf_size, + "%.4f", + ((double) ((expiry_time * NM_UTILS_NS_PER_SECOND) - now_ns)) / ((double) NM_UTILS_NS_PER_SECOND)); + nm_assert (l < buf_size); + return buf; +} + +#define get_exp(buf, now_ns, item) \ + _get_exp ((buf), G_N_ELEMENTS (buf), (now_ns), (get_expiry (item))) + static void _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed) { @@ -896,10 +918,14 @@ _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed) char changedstr[CONFIG_MAP_MAX_STR]; char addrstr[INET6_ADDRSTRLEN]; char str_pref[35]; + char str_exp[100]; + gint64 now_ns; if (!_LOGD_ENABLED ()) return; + now_ns = nm_utils_get_monotonic_timestamp_ns (); + priv = NM_NDISC_GET_PRIVATE (ndisc); rdata = &priv->rdata; @@ -910,35 +936,38 @@ _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed) NMNDiscGateway *gateway = &g_array_index (rdata->gateways, NMNDiscGateway, i); inet_ntop (AF_INET6, &gateway->address, addrstr, sizeof (addrstr)); - _LOGD (" gateway %s pref %s exp %d", addrstr, + _LOGD (" gateway %s pref %s exp %s", addrstr, nm_icmpv6_router_pref_to_string (gateway->preference, str_pref, sizeof (str_pref)), - get_expiry (gateway)); + get_exp (str_exp, now_ns, gateway)); } for (i = 0; i < rdata->addresses->len; i++) { - NMNDiscAddress *address = &g_array_index (rdata->addresses, NMNDiscAddress, i); + const NMNDiscAddress *address = &g_array_index (rdata->addresses, NMNDiscAddress, i); inet_ntop (AF_INET6, &address->address, addrstr, sizeof (addrstr)); - _LOGD (" address %s exp %d", addrstr, get_expiry (address)); + _LOGD (" address %s exp %s", addrstr, + get_exp (str_exp, now_ns, address)); } for (i = 0; i < rdata->routes->len; i++) { NMNDiscRoute *route = &g_array_index (rdata->routes, NMNDiscRoute, i); inet_ntop (AF_INET6, &route->network, addrstr, sizeof (addrstr)); - _LOGD (" route %s/%u via %s pref %s exp %d", addrstr, (guint) route->plen, + _LOGD (" route %s/%u via %s pref %s exp %s", addrstr, (guint) route->plen, nm_utils_inet6_ntop (&route->gateway, NULL), nm_icmpv6_router_pref_to_string (route->preference, str_pref, sizeof (str_pref)), - get_expiry (route)); + get_exp (str_exp, now_ns, route)); } for (i = 0; i < rdata->dns_servers->len; i++) { NMNDiscDNSServer *dns_server = &g_array_index (rdata->dns_servers, NMNDiscDNSServer, i); inet_ntop (AF_INET6, &dns_server->address, addrstr, sizeof (addrstr)); - _LOGD (" dns_server %s exp %d", addrstr, get_expiry (dns_server)); + _LOGD (" dns_server %s exp %s", addrstr, + get_exp (str_exp, now_ns, dns_server)); } for (i = 0; i < rdata->dns_domains->len; i++) { NMNDiscDNSDomain *dns_domain = &g_array_index (rdata->dns_domains, NMNDiscDNSDomain, i); - _LOGD (" dns_domain %s exp %d", dns_domain->domain, get_expiry (dns_domain)); + _LOGD (" dns_domain %s exp %s", dns_domain->domain, + get_exp (str_exp, now_ns, dns_domain)); } } @@ -979,7 +1008,7 @@ clean_addresses (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 * rdata = &NM_NDISC_GET_PRIVATE (ndisc)->rdata; for (i = 0; i < rdata->addresses->len; ) { - NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i); + const NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i); if (item->lifetime != NM_NDISC_INFINITY) { gint32 expiry = get_expiry (item); @@ -1354,7 +1383,7 @@ nm_ndisc_class_init (NMNDiscClass *klass) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - signals[CONFIG_CHANGED] = + signals[CONFIG_RECEIVED] = g_signal_new (NM_NDISC_CONFIG_RECEIVED, G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST, diff --git a/src/ndisc/nm-ndisc.h b/src/ndisc/nm-ndisc.h index b66c2289..9a8a27d7 100644 --- a/src/ndisc/nm-ndisc.h +++ b/src/ndisc/nm-ndisc.h @@ -27,6 +27,9 @@ #include "nm-setting-ip6-config.h" #include "NetworkManagerUtils.h" +#include "platform/nm-platform.h" +#include "platform/nmp-object.h" + #define NM_TYPE_NDISC (nm_ndisc_get_type ()) #define NM_NDISC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_NDISC, NMNDisc)) #define NM_NDISC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_NDISC, NMNDiscClass)) @@ -174,7 +177,7 @@ NMNDiscNodeType nm_ndisc_get_node_type (NMNDisc *self); gboolean nm_ndisc_set_iid (NMNDisc *ndisc, const NMUtilsIPv6IfaceId iid); void nm_ndisc_start (NMNDisc *ndisc); -void nm_ndisc_dad_failed (NMNDisc *ndisc, struct in6_addr *address); +void nm_ndisc_dad_failed (NMNDisc *ndisc, const struct in6_addr *address); void nm_ndisc_set_config (NMNDisc *ndisc, const GArray *addresses, const GArray *dns_servers, @@ -184,4 +187,32 @@ NMPlatform *nm_ndisc_get_platform (NMNDisc *self); NMPNetns *nm_ndisc_netns_get (NMNDisc *self); gboolean nm_ndisc_netns_push (NMNDisc *self, NMPNetns **netns); +static inline gboolean +nm_ndisc_dad_addr_is_fail_candidate_event (NMPlatformSignalChangeType change_type, + const NMPlatformIP6Address *addr) +{ + return !NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_TEMPORARY) + && ( (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->n_ifa_flags & IFA_F_DADFAILED) + || (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->n_ifa_flags & IFA_F_TENTATIVE)); +} + +static inline gboolean +nm_ndisc_dad_addr_is_fail_candidate (NMPlatform *platform, + const NMPObject *obj) +{ + const NMPlatformIP6Address *addr; + + addr = NMP_OBJECT_CAST_IP6_ADDRESS (nm_platform_lookup_obj (platform, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + obj)); + if ( addr + && ( NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_TEMPORARY) + || !NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_DADFAILED))) { + /* the address still/again exists and is not in DADFAILED state. Skip it. */ + return FALSE; + } + + return TRUE; +} + #endif /* __NETWORKMANAGER_NDISC_H__ */ diff --git a/src/ndisc/tests/meson.build b/src/ndisc/tests/meson.build new file mode 100644 index 00000000..2f479c2d --- /dev/null +++ b/src/ndisc/tests/meson.build @@ -0,0 +1,23 @@ +test_unit = 'test-ndisc-fake' + +exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, + c_args: test_cflags_platform +) + +test( + 'ndisc/' + test_unit, + test_script, + args: test_args + [exe.full_path()] +) + +test = 'test-ndisc-linux' + +exe = executable( + test, + test + '.c', + dependencies: test_nm_dep, + c_args: test_cflags_platform +) diff --git a/src/nm-act-request.c b/src/nm-act-request.c index dd73947d..0ac20b85 100644 --- a/src/nm-act-request.c +++ b/src/nm-act-request.c @@ -28,7 +28,7 @@ #include #include -#include "nm-utils/c-list.h" +#include "c-list/src/c-list.h" #include "nm-setting-wireless-security.h" #include "nm-setting-8021x.h" @@ -242,8 +242,6 @@ _do_cancel_secrets (NMActRequest *self, NMActRequestGetSecretsCallId *call_id, g void nm_act_request_cancel_secrets (NMActRequest *self, NMActRequestGetSecretsCallId *call_id) { - NMActRequestPrivate *priv; - g_return_if_fail (call_id); if (self) { @@ -255,8 +253,6 @@ nm_act_request_cancel_secrets (NMActRequest *self, NMActRequestGetSecretsCallId self = call_id->self; } - priv = NM_ACT_REQUEST_GET_PRIVATE (self); - if (!c_list_is_linked (&call_id->call_ids_lst)) g_return_if_reached (); @@ -518,7 +514,7 @@ get_property (GObject *object, guint prop_id, || !NM_IN_SET (nm_active_connection_get_state (active), NM_ACTIVE_CONNECTION_STATE_ACTIVATED, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING)) { - g_value_set_string (value, "/"); + g_value_set_string (value, NULL); return; } diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index c3f92464..aa83d6d5 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -28,14 +28,15 @@ #include "settings/nm-settings-connection.h" #include "nm-simple-connection.h" #include "nm-auth-utils.h" +#include "nm-auth-manager.h" #include "nm-auth-subject.h" #include "NetworkManagerUtils.h" #include "nm-core-internal.h" -#include "introspection/org.freedesktop.NetworkManager.Connection.Active.h" +#define AUTH_CALL_ID_SHARED_WIFI_PERMISSION_FAILED ((NMAuthManagerCallId *) GINT_TO_POINTER (1)) typedef struct _NMActiveConnectionPrivate { - NMSettingsConnection *settings_connection; + NMDBusTrackObjPath settings_connection; NMConnection *applied_connection; char *specific_object; NMDevice *device; @@ -54,18 +55,26 @@ typedef struct _NMActiveConnectionPrivate { bool master_ready:1; NMActivationType activation_type:3; - NMActivationReason activation_reason:3; + + /* capture the original reason why the connection was activated. + * For example with NM_ACTIVATION_REASON_ASSUME, the connection + * will later change to become fully managed. But the original + * reason never changes. */ + NMActivationReason activation_reason:4; NMAuthSubject *subject; NMActiveConnection *master; NMActiveConnection *parent; - NMAuthChain *chain; - const char *wifi_shared_permission; - NMActiveConnectionAuthResultFunc result_func; - gpointer user_data1; - gpointer user_data2; + struct { + NMAuthManagerCallId *call_id_network_control; + NMAuthManagerCallId *call_id_wifi_shared_permission; + + NMActiveConnectionAuthResultFunc result_func; + gpointer user_data; + } auth; + } NMActiveConnectionPrivate; NM_GOBJECT_PROPERTIES_DEFINE (NMActiveConnection, @@ -105,19 +114,23 @@ enum { }; static guint signals[LAST_SIGNAL] = { 0 }; -G_DEFINE_ABSTRACT_TYPE (NMActiveConnection, nm_active_connection, NM_TYPE_EXPORTED_OBJECT) +G_DEFINE_ABSTRACT_TYPE (NMActiveConnection, nm_active_connection, NM_TYPE_DBUS_OBJECT) #define NM_ACTIVE_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMActiveConnection, NM_IS_ACTIVE_CONNECTION) /*****************************************************************************/ +static const NMDBusInterfaceInfoExtended interface_info_active_connection; +static const GDBusSignalInfo signal_info_state_changed; + static void check_master_ready (NMActiveConnection *self); static void _device_cleanup (NMActiveConnection *self); -static void _settings_connection_notify_flags (NMSettingsConnection *settings_connection, - GParamSpec *param, - NMActiveConnection *self); +static void _settings_connection_flags_changed (NMSettingsConnection *settings_connection, + NMActiveConnection *self); static void _set_activation_type_managed (NMActiveConnection *self); +static void auth_complete (NMActiveConnection *self, gboolean result, const char *message); + /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_DEVICE @@ -182,41 +195,25 @@ _settings_connection_updated (NMSettingsConnection *connection, * settings-connection. */ } -static void -_settings_connection_removed (NMSettingsConnection *connection, - gpointer user_data) -{ - NMActiveConnection *self = user_data; - - /* Our settings connection is about to drop off. The next active connection - * cleanup is going to tear us down (at least until we grow the capability to - * re-link; in that case we'd just clean the references to the old connection here). - * Let's remove ourselves from the bus so that we're not exposed with a dangling - * reference to the setting connection once it's gone. */ - if (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (self))) - nm_exported_object_unexport (NM_EXPORTED_OBJECT (self)); -} - static void _set_settings_connection (NMActiveConnection *self, NMSettingsConnection *connection) { NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); - if (priv->settings_connection == connection) + if (priv->settings_connection.obj == connection) return; - if (priv->settings_connection) { - g_signal_handlers_disconnect_by_func (priv->settings_connection, _settings_connection_updated, self); - g_signal_handlers_disconnect_by_func (priv->settings_connection, _settings_connection_removed, self); - g_signal_handlers_disconnect_by_func (priv->settings_connection, _settings_connection_notify_flags, self); - g_clear_object (&priv->settings_connection); + + if (priv->settings_connection.obj) { + g_signal_handlers_disconnect_by_func (priv->settings_connection.obj, _settings_connection_updated, self); + g_signal_handlers_disconnect_by_func (priv->settings_connection.obj, _settings_connection_flags_changed, self); } if (connection) { - priv->settings_connection = g_object_ref (connection); g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL, (GCallback) _settings_connection_updated, self); - g_signal_connect (connection, NM_SETTINGS_CONNECTION_REMOVED, (GCallback) _settings_connection_removed, self); if (nm_active_connection_get_activation_type (self) == NM_ACTIVATION_TYPE_EXTERNAL) - g_signal_connect (connection, "notify::"NM_SETTINGS_CONNECTION_FLAGS, (GCallback) _settings_connection_notify_flags, self); + g_signal_connect (connection, NM_SETTINGS_CONNECTION_FLAGS_CHANGED, (GCallback) _settings_connection_flags_changed, self); } + + nm_dbus_track_obj_path_set (&priv->settings_connection, connection, TRUE); } NMActiveConnectionState @@ -225,6 +222,18 @@ nm_active_connection_get_state (NMActiveConnection *self) return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->state; } +static void +emit_state_changed (NMActiveConnection *self, guint state, guint reason) +{ + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), + &interface_info_active_connection, + &signal_info_state_changed, + "(uu)", + (guint32) state, + (guint32) reason); + g_signal_emit (self, signals[STATE_CHANGED], 0, state, reason); +} + void nm_active_connection_set_state (NMActiveConnection *self, NMActiveConnectionState new_state, @@ -233,13 +242,13 @@ nm_active_connection_set_state (NMActiveConnection *self, NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); NMActiveConnectionState old_state; + /* DEACTIVATED is a terminal state */ + g_return_if_fail ( priv->state != NM_ACTIVE_CONNECTION_STATE_DEACTIVATED + || new_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED); + if (priv->state == new_state) return; - /* DEACTIVATED is a terminal state */ - if (priv->state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) - g_return_if_fail (new_state != NM_ACTIVE_CONNECTION_STATE_DEACTIVATED); - _LOGD ("set state %s (was %s)", state_to_string (new_state), state_to_string (priv->state)); @@ -255,14 +264,14 @@ nm_active_connection_set_state (NMActiveConnection *self, old_state = priv->state; priv->state = new_state; priv->state_set = TRUE; - g_signal_emit (self, signals[STATE_CHANGED], 0, (guint) new_state, (guint) reason); + emit_state_changed (self, new_state, reason); _notify (self, PROP_STATE); check_master_ready (self); if ( new_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED || old_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { - nm_settings_connection_update_timestamp (priv->settings_connection, + nm_settings_connection_update_timestamp (priv->settings_connection.obj, (guint64) time (NULL), TRUE); } @@ -285,6 +294,10 @@ nm_active_connection_set_state (NMActiveConnection *self, } if (priv->state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) { + _nm_unused gs_unref_object NMActiveConnection *self_keep_alive = g_object_ref (self); + + auth_complete (self, FALSE, "Authorization request cancelled"); + /* Device is no longer relevant when deactivated. So remove it and * emit property change notification so clients re-read the value, * which will be NULL due to conditions in get_property(). @@ -358,7 +371,7 @@ nm_active_connection_get_settings_connection_id (NMActiveConnection *self) g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), NULL); - con = NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->settings_connection; + con = NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->settings_connection.obj; return con ? nm_connection_get_id (NM_CONNECTION (con)) : NULL; @@ -369,7 +382,7 @@ _nm_active_connection_get_settings_connection (NMActiveConnection *self) { g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), NULL); - return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->settings_connection; + return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->settings_connection.obj; } NMSettingsConnection * @@ -423,10 +436,7 @@ _set_applied_connection_take (NMActiveConnection *self, if (nm_setting_connection_get_master (s_con)) flags_val |= NM_ACTIVATION_STATE_FLAG_IS_SLAVE; - if (NM_IN_STRSET (nm_setting_connection_get_connection_type (s_con), - NM_SETTING_BOND_SETTING_NAME, - NM_SETTING_BRIDGE_SETTING_NAME, - NM_SETTING_TEAM_SETTING_NAME)) + if (_nm_connection_type_is_master (nm_setting_connection_get_connection_type (s_con))) flags_val |= NM_ACTIVATION_STATE_FLAG_IS_MASTER; nm_active_connection_set_state_flags_full (self, @@ -446,7 +456,7 @@ nm_active_connection_set_settings_connection (NMActiveConnection *self, priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection)); - g_return_if_fail (!priv->settings_connection); + g_return_if_fail (!priv->settings_connection.obj); g_return_if_fail (!priv->applied_connection); /* Can't change connection after the ActiveConnection is exported over D-Bus. @@ -456,12 +466,12 @@ nm_active_connection_set_settings_connection (NMActiveConnection *self, * never changes (once it's set). That has effects for NMVpnConnection and * NMActivationRequest. * For example, we'd have to cancel all pending seret requests. */ - g_return_if_fail (!nm_exported_object_is_exported (NM_EXPORTED_OBJECT (self))); + g_return_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (self))); _set_settings_connection (self, connection); _set_applied_connection_take (self, - nm_simple_connection_new_clone (NM_CONNECTION (priv->settings_connection))); + nm_simple_connection_new_clone (NM_CONNECTION (priv->settings_connection.obj))); } gboolean @@ -473,9 +483,9 @@ nm_active_connection_has_unmodified_applied_connection (NMActiveConnection *self priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); - g_return_val_if_fail (priv->settings_connection, FALSE); + g_return_val_if_fail (priv->settings_connection.obj, FALSE); - return nm_settings_connection_has_unmodified_applied_connection (priv->settings_connection, + return nm_settings_connection_has_unmodified_applied_connection (priv->settings_connection.obj, priv->applied_connection, compare_flags); } @@ -491,10 +501,10 @@ nm_active_connection_clear_secrets (NMActiveConnection *self) priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); - if (nm_settings_connection_has_unmodified_applied_connection (priv->settings_connection, + if (nm_settings_connection_has_unmodified_applied_connection (priv->settings_connection.obj, priv->applied_connection, NM_SETTING_COMPARE_FLAG_NONE)) - nm_connection_clear_secrets ((NMConnection *) priv->settings_connection); + nm_connection_clear_secrets ((NMConnection *) priv->settings_connection.obj); nm_connection_clear_secrets (priv->applied_connection); } @@ -515,9 +525,9 @@ nm_active_connection_set_specific_object (NMActiveConnection *self, /* Nothing that calls this function should be using paths from D-Bus, * where NM uses "/" to mean NULL. */ - g_assert (g_strcmp0 (specific_object, "/") != 0); + nm_assert (!nm_streq0 (specific_object, "/")); - if (g_strcmp0 (priv->specific_object, specific_object) == 0) + if (nm_streq0 (priv->specific_object, specific_object)) return; g_free (priv->specific_object); @@ -815,7 +825,7 @@ nm_active_connection_set_master (NMActiveConnection *self, NMActiveConnection *m /* Master is write-once, and must be set before exporting the object */ g_return_if_fail (priv->master == NULL); - g_return_if_fail (!nm_exported_object_is_exported (NM_EXPORTED_OBJECT (self))); + g_return_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (self))); if (priv->device) { /* Note, the master ActiveConnection may not yet have a device */ g_return_if_fail (priv->device != nm_active_connection_get_device (master)); @@ -854,11 +864,11 @@ _set_activation_type (NMActiveConnection *self, priv->activation_type = activation_type; - if (priv->settings_connection) { + if (priv->settings_connection.obj) { if (activation_type == NM_ACTIVATION_TYPE_EXTERNAL) - g_signal_connect (priv->settings_connection, "notify::"NM_SETTINGS_CONNECTION_FLAGS, (GCallback) _settings_connection_notify_flags, self); + g_signal_connect (priv->settings_connection.obj, NM_SETTINGS_CONNECTION_FLAGS_CHANGED, (GCallback) _settings_connection_flags_changed, self); else - g_signal_handlers_disconnect_by_func (priv->settings_connection, _settings_connection_notify_flags, self); + g_signal_handlers_disconnect_by_func (priv->settings_connection.obj, _settings_connection_flags_changed, self); } } @@ -895,19 +905,18 @@ nm_active_connection_get_activation_reason (NMActiveConnection *self) /*****************************************************************************/ static void -_settings_connection_notify_flags (NMSettingsConnection *settings_connection, - GParamSpec *param, - NMActiveConnection *self) +_settings_connection_flags_changed (NMSettingsConnection *settings_connection, + NMActiveConnection *self) { GError *error = NULL; nm_assert (NM_IS_ACTIVE_CONNECTION (self)); nm_assert (NM_IS_SETTINGS_CONNECTION (settings_connection)); nm_assert (nm_active_connection_get_activation_type (self) == NM_ACTIVATION_TYPE_EXTERNAL); - nm_assert (NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->settings_connection == settings_connection); + nm_assert (NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->settings_connection.obj == settings_connection); if (NM_FLAGS_HAS (nm_settings_connection_get_flags (settings_connection), - NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED)) + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) return; _set_activation_type_managed (self); @@ -987,62 +996,99 @@ nm_active_connection_set_parent (NMActiveConnection *self, NMActiveConnection *p /*****************************************************************************/ static void -auth_done (NMAuthChain *chain, +auth_complete (NMActiveConnection *self, gboolean result, const char *message) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + NMActiveConnectionAuthResultFunc result_func; + gpointer user_data; + + if (priv->auth.call_id_network_control) + nm_auth_manager_check_authorization_cancel (priv->auth.call_id_network_control); + if (priv->auth.call_id_wifi_shared_permission) { + if (priv->auth.call_id_wifi_shared_permission == AUTH_CALL_ID_SHARED_WIFI_PERMISSION_FAILED) + priv->auth.call_id_wifi_shared_permission = NULL; + else + nm_auth_manager_check_authorization_cancel (priv->auth.call_id_wifi_shared_permission); + } + + nm_assert (!priv->auth.call_id_network_control); + nm_assert (!priv->auth.call_id_wifi_shared_permission); + if (priv->auth.result_func) { + result_func = priv->auth.result_func; + priv->auth.result_func = NULL; + user_data = g_steal_pointer (&priv->auth.user_data); + + result_func (self, + result, + message, + user_data); + } +} + +static void +auth_complete_keep_alive (NMActiveConnection *self, gboolean result, const char *message) +{ + _nm_unused gs_unref_object NMActiveConnection *self_keep_alive = g_object_ref (self); + + auth_complete (self, result, message); +} + +static void +auth_done (NMAuthManager *auth_mgr, + NMAuthManagerCallId *auth_call_id, + gboolean is_authorized, + gboolean is_challenge, GError *error, - GDBusMethodInvocation *unused, gpointer user_data) + { NMActiveConnection *self = NM_ACTIVE_CONNECTION (user_data); NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); NMAuthCallResult result; - g_assert (priv->chain == chain); - g_assert (priv->result_func != NULL); + nm_assert (auth_call_id); + nm_assert (priv->auth.result_func); - /* Must stay alive over the callback */ - g_object_ref (self); - - if (error) { - priv->result_func (self, FALSE, error->message, priv->user_data1, priv->user_data2); - goto done; + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + if (auth_call_id == priv->auth.call_id_network_control) + priv->auth.call_id_network_control = NULL; + else { + nm_assert (auth_call_id == priv->auth.call_id_wifi_shared_permission); + priv->auth.call_id_wifi_shared_permission = NULL; + } + return; } - /* Caller has had a chance to obtain authorization, so we only need to - * check for 'yes' here. - */ - result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL); - if (result != NM_AUTH_CALL_RESULT_YES) { - priv->result_func (self, - FALSE, - "Not authorized to control networking.", - priv->user_data1, - priv->user_data2); - goto done; - } + result = nm_auth_call_result_eval (is_authorized, is_challenge, error); - if (priv->wifi_shared_permission) { - result = nm_auth_chain_get_result (chain, priv->wifi_shared_permission); + if (auth_call_id == priv->auth.call_id_network_control) { + priv->auth.call_id_network_control = NULL; if (result != NM_AUTH_CALL_RESULT_YES) { - priv->result_func (self, - FALSE, - "Not authorized to share connections via wifi.", - priv->user_data1, - priv->user_data2); - goto done; + auth_complete_keep_alive (self, FALSE, "Not authorized to control networking."); + return; } + } else { + nm_assert (auth_call_id == priv->auth.call_id_wifi_shared_permission); + if (result != NM_AUTH_CALL_RESULT_YES) { + /* we don't fail right away. Instead, we mark that wifi-shared-permissions + * are missing. We prefer to report the failure about network-control. + * Below, we will wait longer for call_id_network_control (if it's still + * pending). */ + priv->auth.call_id_wifi_shared_permission = AUTH_CALL_ID_SHARED_WIFI_PERMISSION_FAILED; + } else + priv->auth.call_id_wifi_shared_permission = NULL; } - /* Otherwise authorized and available to activate */ - priv->result_func (self, TRUE, NULL, priv->user_data1, priv->user_data2); + if (priv->auth.call_id_network_control) + return; -done: - nm_auth_chain_unref (chain); - priv->chain = NULL; - priv->result_func = NULL; - priv->user_data1 = NULL; - priv->user_data2 = NULL; + if (priv->auth.call_id_wifi_shared_permission) { + if (priv->auth.call_id_wifi_shared_permission == AUTH_CALL_ID_SHARED_WIFI_PERMISSION_FAILED) + auth_complete_keep_alive (self, FALSE, "Not authorized to share connections via wifi."); + return; + } - g_object_unref (self); + auth_complete_keep_alive (self, TRUE, NULL); } /** @@ -1052,8 +1098,7 @@ done: * is no @settings_connection available when creating the active connection. * Instead pass an alternative connection. * @result_func: function to be called on success or error - * @user_data1: pointer passed to @result_func - * @user_data2: additional pointer passed to @result_func + * @user_data: pointer passed to @result_func * * Checks whether the subject that initiated the active connection (read from * the #NMActiveConnection::subject property) is authorized to complete this @@ -1063,44 +1108,47 @@ void nm_active_connection_authorize (NMActiveConnection *self, NMConnection *initial_connection, NMActiveConnectionAuthResultFunc result_func, - gpointer user_data1, - gpointer user_data2) + gpointer user_data) { NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); const char *wifi_permission = NULL; NMConnection *con; - g_return_if_fail (result_func != NULL); - g_return_if_fail (priv->chain == NULL); + g_return_if_fail (result_func); + g_return_if_fail (!priv->auth.call_id_network_control); + nm_assert (!priv->auth.call_id_wifi_shared_permission); if (initial_connection) { g_return_if_fail (NM_IS_CONNECTION (initial_connection)); - g_return_if_fail (!priv->settings_connection); + g_return_if_fail (!priv->settings_connection.obj); g_return_if_fail (!priv->applied_connection); con = initial_connection; } else { - g_return_if_fail (NM_IS_SETTINGS_CONNECTION (priv->settings_connection)); + g_return_if_fail (NM_IS_SETTINGS_CONNECTION (priv->settings_connection.obj)); g_return_if_fail (NM_IS_CONNECTION (priv->applied_connection)); con = priv->applied_connection; } - priv->chain = nm_auth_chain_new_subject (priv->subject, NULL, auth_done, self); - g_assert (priv->chain); - - /* Check that the subject is allowed to use networking at all */ - nm_auth_chain_add_call (priv->chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); + priv->auth.call_id_network_control = nm_auth_manager_check_authorization (nm_auth_manager_get (), + priv->subject, + NM_AUTH_PERMISSION_NETWORK_CONTROL, + TRUE, + auth_done, + self); /* Shared wifi connections require special permissions too */ wifi_permission = nm_utils_get_shared_wifi_permission (con); if (wifi_permission) { - priv->wifi_shared_permission = wifi_permission; - nm_auth_chain_add_call (priv->chain, wifi_permission, TRUE); + priv->auth.call_id_wifi_shared_permission = nm_auth_manager_check_authorization (nm_auth_manager_get (), + priv->subject, + wifi_permission, + TRUE, + auth_done, + self); } - /* Wait for authorization */ - priv->result_func = result_func; - priv->user_data1 = user_data1; - priv->user_data2 = user_data2; + priv->auth.result_func = result_func; + priv->auth.user_data = user_data; } /*****************************************************************************/ @@ -1162,31 +1210,40 @@ get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE ((NMActiveConnection *) object); - GPtrArray *devices; + char **strv; NMDevice *master_device = NULL; switch (prop_id) { + + /* note that while priv->settings_connection.obj might not be set initially, + * it will be set before the object is exported on D-Bus. Hence, + * nobody is calling these property getters before the object is + * exported, at which point we will have a valid settings-connection. + * + * Therefore, intentionally not check whether priv->settings_connection.obj + * is set, to get an assertion failure if somebody tries to access the + * getters at the wrong time. */ case PROP_CONNECTION: - g_value_set_string (value, nm_connection_get_path (NM_CONNECTION (priv->settings_connection))); + g_value_set_string (value, nm_dbus_track_obj_path_get (&priv->settings_connection)); break; case PROP_ID: - g_value_set_string (value, nm_connection_get_id (NM_CONNECTION (priv->settings_connection))); + g_value_set_string (value, nm_connection_get_id (NM_CONNECTION (priv->settings_connection.obj))); break; case PROP_UUID: - g_value_set_string (value, nm_connection_get_uuid (NM_CONNECTION (priv->settings_connection))); + g_value_set_string (value, nm_connection_get_uuid (NM_CONNECTION (priv->settings_connection.obj))); break; case PROP_TYPE: - g_value_set_string (value, nm_connection_get_connection_type (NM_CONNECTION (priv->settings_connection))); + g_value_set_string (value, nm_connection_get_connection_type (NM_CONNECTION (priv->settings_connection.obj))); break; + case PROP_SPECIFIC_OBJECT: - g_value_set_string (value, priv->specific_object ? priv->specific_object : "/"); + g_value_set_string (value, priv->specific_object); break; case PROP_DEVICES: - devices = g_ptr_array_sized_new (2); + strv = g_new0 (char *, 2); if (priv->device && priv->state < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) - g_ptr_array_add (devices, g_strdup (nm_exported_object_get_path (NM_EXPORTED_OBJECT (priv->device)))); - g_ptr_array_add (devices, NULL); - g_value_take_boxed (value, (char **) g_ptr_array_free (devices, FALSE)); + strv[0] = g_strdup (nm_dbus_object_get_path (NM_DBUS_OBJECT (priv->device))); + g_value_take_boxed (value, strv); break; case PROP_STATE: if (priv->state_set) @@ -1206,19 +1263,19 @@ get_property (GObject *object, guint prop_id, break; case PROP_IP4_CONFIG: /* The IP and DHCP config properties may be overridden by a subclass */ - g_value_set_string (value, "/"); + g_value_set_string (value, NULL); break; case PROP_DHCP4_CONFIG: - g_value_set_string (value, "/"); + g_value_set_string (value, NULL); break; case PROP_DEFAULT6: g_value_set_boolean (value, priv->is_default6); break; case PROP_IP6_CONFIG: - g_value_set_string (value, "/"); + g_value_set_string (value, NULL); break; case PROP_DHCP6_CONFIG: - g_value_set_string (value, "/"); + g_value_set_string (value, NULL); break; case PROP_VPN: g_value_set_boolean (value, priv->vpn); @@ -1226,7 +1283,7 @@ get_property (GObject *object, guint prop_id, case PROP_MASTER: if (priv->master) master_device = nm_active_connection_get_device (priv->master); - nm_utils_g_value_set_object_path (value, master_device); + nm_dbus_utils_g_value_set_object_path (value, master_device); break; case PROP_INT_SUBJECT: g_value_set_object (value, priv->subject); @@ -1300,9 +1357,8 @@ set_property (GObject *object, guint prop_id, case PROP_SPECIFIC_OBJECT: /* construct-only */ tmp = g_value_get_string (value); - /* NM uses "/" to mean NULL */ - if (g_strcmp0 (tmp, "/") != 0) - priv->specific_object = g_strdup (tmp); + tmp = nm_utils_dbus_normalize_object_path (tmp); + priv->specific_object = g_strdup (tmp); break; case PROP_DEFAULT: priv->is_default = g_value_get_boolean (value); @@ -1332,6 +1388,10 @@ nm_active_connection_init (NMActiveConnection *self) priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_ACTIVE_CONNECTION, NMActiveConnectionPrivate); self->_priv = priv; + nm_dbus_track_obj_path_init (&priv->settings_connection, + G_OBJECT (self), + obj_properties[PROP_CONNECTION]); + c_list_init (&self->active_connections_lst); _LOGT ("creating"); @@ -1349,8 +1409,8 @@ constructed (GObject *object) G_OBJECT_CLASS (nm_active_connection_parent_class)->constructed (object); if ( !priv->applied_connection - && priv->settings_connection) - priv->applied_connection = nm_simple_connection_new_clone (NM_CONNECTION (priv->settings_connection)); + && priv->settings_connection.obj) + priv->applied_connection = nm_simple_connection_new_clone (NM_CONNECTION (priv->settings_connection.obj)); _LOGD ("constructed (%s, version-id %llu, type %s)", G_OBJECT_TYPE_NAME (self), @@ -1368,6 +1428,7 @@ constructed (GObject *object) } g_return_if_fail (priv->subject); + g_return_if_fail (priv->activation_reason != NM_ACTIVATION_REASON_UNSET); } static void @@ -1380,13 +1441,9 @@ dispose (GObject *object) _LOGD ("disposing"); - if (priv->chain) { - nm_auth_chain_unref (priv->chain); - priv->chain = NULL; - } + auth_complete (self, FALSE, "Authorization aborted"); - g_free (priv->specific_object); - priv->specific_object = NULL; + nm_clear_g_free (&priv->specific_object); _set_settings_connection (self, NULL); g_clear_object (&priv->applied_connection); @@ -1408,22 +1465,71 @@ dispose (GObject *object) G_OBJECT_CLASS (nm_active_connection_parent_class)->dispose (object); } +static void +finalize (GObject *object) +{ + NMActiveConnection *self = NM_ACTIVE_CONNECTION (object); + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + + nm_dbus_track_obj_path_set (&priv->settings_connection, NULL, FALSE); + + G_OBJECT_CLASS (nm_active_connection_parent_class)->finalize (object); +} + +static const GDBusSignalInfo signal_info_state_changed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "StateChanged", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("state", "u"), + NM_DEFINE_GDBUS_ARG_INFO ("reason", "u"), + ), +); + +static const NMDBusInterfaceInfoExtended interface_info_active_connection = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_ACTIVE_CONNECTION, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + &signal_info_state_changed, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Connection", "o", NM_ACTIVE_CONNECTION_CONNECTION), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("SpecificObject", "o", NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Id", "s", NM_ACTIVE_CONNECTION_ID), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Uuid", "s", NM_ACTIVE_CONNECTION_UUID), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Type", "s", NM_ACTIVE_CONNECTION_TYPE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Devices", "ao", NM_ACTIVE_CONNECTION_DEVICES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("State", "u", NM_ACTIVE_CONNECTION_STATE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("StateFlags", "u", NM_ACTIVE_CONNECTION_STATE_FLAGS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Default", "b", NM_ACTIVE_CONNECTION_DEFAULT), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ip4Config", "o", NM_ACTIVE_CONNECTION_IP4_CONFIG), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Dhcp4Config", "o", NM_ACTIVE_CONNECTION_DHCP4_CONFIG), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Default6", "b", NM_ACTIVE_CONNECTION_DEFAULT6), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ip6Config", "o", NM_ACTIVE_CONNECTION_IP6_CONFIG), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Dhcp6Config", "o", NM_ACTIVE_CONNECTION_DHCP6_CONFIG), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Vpn", "b", NM_ACTIVE_CONNECTION_VPN), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Master", "o", NM_ACTIVE_CONNECTION_MASTER), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_active_connection_class_init (NMActiveConnectionClass *ac_class) { GObjectClass *object_class = G_OBJECT_CLASS (ac_class); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (ac_class); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (ac_class); g_type_class_add_private (ac_class, sizeof (NMActiveConnectionPrivate)); - exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/ActiveConnection"); + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/ActiveConnection"); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_active_connection); object_class->get_property = get_property; object_class->set_property = set_property; object_class->constructed = constructed; object_class->dispose = dispose; + object_class->finalize = finalize; - /* D-Bus exported properties */ obj_properties[PROP_CONNECTION] = g_param_spec_string (NM_ACTIVE_CONNECTION_CONNECTION, "", "", NULL, @@ -1608,9 +1714,4 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class) G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (ac_class), - NMDBUS_TYPE_ACTIVE_CONNECTION_SKELETON, - NULL); } - diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h index 9b6a49ef..b28c2b02 100644 --- a/src/nm-active-connection.h +++ b/src/nm-active-connection.h @@ -21,10 +21,9 @@ #ifndef __NETWORKMANAGER_ACTIVE_CONNECTION_H__ #define __NETWORKMANAGER_ACTIVE_CONNECTION_H__ -#include "nm-exported-object.h" +#include "c-list/src/c-list.h" #include "nm-connection.h" - -#include "nm-utils/c-list.h" +#include "nm-dbus-object.h" #define NM_TYPE_ACTIVE_CONNECTION (nm_active_connection_get_type ()) #define NM_ACTIVE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnection)) @@ -72,7 +71,7 @@ struct _NMActiveConnectionPrivate; struct _NMActiveConnection { - NMExportedObject parent; + NMDBusObject parent; struct _NMActiveConnectionPrivate *_priv; /* active connection can be tracked in a list by NMManager. This is @@ -81,7 +80,7 @@ struct _NMActiveConnection { }; typedef struct { - NMExportedObjectClass parent; + NMDBusObjectClass parent; /* re-emits device state changes as a convenience for subclasses for * device states >= DISCONNECTED. @@ -110,14 +109,12 @@ GType nm_active_connection_get_type (void); typedef void (*NMActiveConnectionAuthResultFunc) (NMActiveConnection *self, gboolean success, const char *error_desc, - gpointer user_data1, - gpointer user_data2); + gpointer user_data); void nm_active_connection_authorize (NMActiveConnection *self, NMConnection *initial_connection, NMActiveConnectionAuthResultFunc result_func, - gpointer user_data1, - gpointer user_data2); + gpointer user_data); NMSettingsConnection *nm_active_connection_get_settings_connection (NMActiveConnection *self); NMConnection *nm_active_connection_get_applied_connection (NMActiveConnection *self); diff --git a/src/nm-audit-manager.c b/src/nm-audit-manager.c index 2d75c0e7..93bd9551 100644 --- a/src/nm-audit-manager.c +++ b/src/nm-audit-manager.c @@ -123,7 +123,7 @@ build_message (GPtrArray *fields, AuditBackend backend) for (i = 0; i < fields->len; i++) { field = fields->pdata[i]; - if (!NM_FLAGS_HAS (field->backends, backend)) + if (!NM_FLAGS_ANY (field->backends, backend)) continue; if (first) @@ -157,7 +157,6 @@ build_message (GPtrArray *fields, AuditBackend backend) return g_string_free (string, FALSE); } - static void nm_audit_log (NMAuditManager *self, GPtrArray *fields, const char *file, guint line, const char *func, gboolean success) diff --git a/src/nm-audit-manager.h b/src/nm-audit-manager.h index 56e26584..59b8048f 100644 --- a/src/nm-audit-manager.h +++ b/src/nm-audit-manager.h @@ -57,6 +57,7 @@ typedef struct _NMAuditManagerClass NMAuditManagerClass; #define NM_AUDIT_OP_CHECKPOINT_CREATE "checkpoint-create" #define NM_AUDIT_OP_CHECKPOINT_ROLLBACK "checkpoint-rollback" #define NM_AUDIT_OP_CHECKPOINT_DESTROY "checkpoint-destroy" +#define NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT "checkpoint-adjust-rollback-timeout" GType nm_audit_manager_get_type (void); NMAuditManager *nm_audit_manager_get (void); diff --git a/src/nm-auth-manager.c b/src/nm-auth-manager.c index 003d9975..09a217ea 100644 --- a/src/nm-auth-manager.c +++ b/src/nm-auth-manager.c @@ -22,6 +22,7 @@ #include "nm-auth-manager.h" +#include "c-list/src/c-list.h" #include "nm-errors.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" @@ -30,6 +31,9 @@ #define POLKIT_OBJECT_PATH "/org/freedesktop/PolicyKit1/Authority" #define POLKIT_INTERFACE "org.freedesktop.PolicyKit1.Authority" +#define CANCELLATION_ID_PREFIX "cancellation-id-" +#define CANCELLATION_TIMEOUT_MS 5000 + /*****************************************************************************/ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( @@ -44,13 +48,14 @@ enum { static guint signals[LAST_SIGNAL] = {0}; typedef struct { - gboolean polkit_enabled; -#if WITH_POLKIT - guint call_id_counter; - GCancellable *new_proxy_cancellable; - GSList *queued_calls; + CList calls_lst_head; GDBusProxy *proxy; -#endif + GCancellable *new_proxy_cancellable; + GCancellable *cancel_cancellable; + guint64 call_numid_counter; + bool polkit_enabled:1; + bool disposing:1; + bool shutting_down:1; } NMAuthManagerPrivate; struct _NMAuthManager { @@ -85,6 +90,22 @@ NM_DEFINE_SINGLETON_REGISTER (NMAuthManager); } \ } G_STMT_END +#define _NMLOG2(level, call_id, ...) \ + G_STMT_START { \ + if (nm_logging_enabled ((level), (_NMLOG_DOMAIN))) { \ + NMAuthManagerCallId *_call_id = (call_id); \ + char __prefix[30] = _NMLOG_PREFIX_NAME; \ + \ + if (_call_id->self != singleton_instance) \ + g_snprintf (__prefix, sizeof (__prefix), ""_NMLOG_PREFIX_NAME"[%p]", _call_id->self); \ + _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \ + "%s: call[%"G_GUINT64_FORMAT"]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + __prefix, \ + _call_id->call_numid \ + _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } G_STMT_END + /*****************************************************************************/ gboolean @@ -97,251 +118,323 @@ nm_auth_manager_get_polkit_enabled (NMAuthManager *self) /*****************************************************************************/ -#if WITH_POLKIT - typedef enum { POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE = 0, POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION = (1<<0), } PolkitCheckAuthorizationFlags; -typedef struct { - guint call_id; +typedef enum { + IDLE_REASON_AUTHORIZED, + IDLE_REASON_NO_DBUS, +} IdleReason; + +struct _NMAuthManagerCallId { + CList calls_lst; NMAuthManager *self; - GSimpleAsyncResult *simple; - gchar *cancellation_id; GVariant *dbus_parameters; - GCancellable *cancellable; -} CheckAuthData; + GCancellable *dbus_cancellable; + NMAuthManagerCheckAuthorizationCallback callback; + gpointer user_data; + guint64 call_numid; + guint idle_id; + IdleReason idle_reason:8; +}; + +#define cancellation_id_to_str_a(call_numid) \ + nm_sprintf_bufa (NM_STRLEN (CANCELLATION_ID_PREFIX) + 20, \ + CANCELLATION_ID_PREFIX"%"G_GUINT64_FORMAT, \ + (call_numid)) static void -_check_auth_data_free (CheckAuthData *data) +_call_id_free (NMAuthManagerCallId *call_id) { - if (data->dbus_parameters) - g_variant_unref (data->dbus_parameters); - g_object_unref (data->self); - g_object_unref (data->simple); - g_clear_object (&data->cancellable); - g_free (data->cancellation_id); - g_free (data); + c_list_unlink (&call_id->calls_lst); + nm_clear_g_source (&call_id->idle_id); + if (call_id->dbus_parameters) + g_variant_unref (g_steal_pointer (&call_id->dbus_parameters)); + + if (call_id->dbus_cancellable) { + /* we have a pending D-Bus call. We keep the call-id instance alive + * for _call_check_authorize_cb() */ + g_cancellable_cancel (call_id->dbus_cancellable); + return; + } + + g_object_unref (call_id->self); + g_slice_free (NMAuthManagerCallId, call_id); } static void -_call_check_authorization_complete_with_error (CheckAuthData *data, - const char *error_message) +_call_id_invoke_callback (NMAuthManagerCallId *call_id, + gboolean is_authorized, + gboolean is_challenge, + GError *error) { - NMAuthManager *self = data->self; - - _LOGD ("call[%u]: CheckAuthorization failed due to internal error: %s", data->call_id, error_message); - g_simple_async_result_set_error (data->simple, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "Authorization check failed: %s", - error_message); - - g_simple_async_result_complete_in_idle (data->simple); - - _check_auth_data_free (data); + c_list_unlink (&call_id->calls_lst); + + call_id->callback (call_id->self, + call_id, + is_authorized, + is_challenge, + error, + call_id->user_data); + _call_id_free (call_id); } static void -cancel_check_authorization_cb (GDBusProxy *proxy, +cancel_check_authorization_cb (GObject *proxy, GAsyncResult *res, gpointer user_data) { - NMAuthManager *self = user_data; - GVariant *value; - GError *error= NULL; - - value = g_dbus_proxy_call_finish (proxy, res, &error); - if (value == NULL) { - g_dbus_error_strip_remote_error (error); - _LOGD ("Error cancelling authorization check: %s", error->message); - g_error_free (error); - } else - g_variant_unref (value); + NMAuthManagerCallId *call_id = user_data; + gs_unref_variant GVariant *value = NULL; + gs_free_error GError *error= NULL; + + value = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + _LOG2T (call_id, "cancel request was cancelled"); + else if (error) + _LOG2T (call_id, "cancel request failed: %s", error->message); + else + _LOG2T (call_id, "cancel request succeeded"); - g_object_unref (self); + _call_id_free (call_id); } -typedef struct { - gboolean is_authorized; - gboolean is_challenge; -} CheckAuthorizationResult; - static void -check_authorization_cb (GDBusProxy *proxy, - GAsyncResult *res, - gpointer user_data) +_call_check_authorize_cb (GObject *proxy, + GAsyncResult *res, + gpointer user_data) { - CheckAuthData *data = user_data; - NMAuthManager *self = data->self; - NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); - GVariant *value; - GError *error = NULL; - - value = _nm_dbus_proxy_call_finish (proxy, res, G_VARIANT_TYPE ("((bba{ss}))"), &error); - if (value == NULL) { - if (data->cancellation_id != NULL && - ( g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) - && !g_dbus_error_is_remote_error (error))) { - _LOGD ("call[%u]: CheckAuthorization cancelled", data->call_id); - g_dbus_proxy_call (priv->proxy, - "CancelCheckAuthorization", - g_variant_new ("(s)", data->cancellation_id), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, /* GCancellable */ - (GAsyncReadyCallback) cancel_check_authorization_cb, - g_object_ref (self)); - } else - _LOGD ("call[%u]: CheckAuthorization failed: %s", data->call_id, error->message); - g_dbus_error_strip_remote_error (error); - g_simple_async_result_set_error (data->simple, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "Authorization check failed: %s", - error->message); - g_error_free (error); - } else { - CheckAuthorizationResult *result; + NMAuthManagerCallId *call_id = user_data; + NMAuthManager *self; + NMAuthManagerPrivate *priv; + gs_unref_variant GVariant *value = NULL; + gs_free_error GError *error = NULL; + gboolean is_authorized = FALSE; + gboolean is_challenge = FALSE; - result = g_new0 (CheckAuthorizationResult, 1); + /* we need to clear the cancelable, to signal for _call_id_free() that we + * are not in a pending call. + * + * Note how _call_id_free() kept call-id alive, even if the request was + * already cancelled. */ + g_clear_object (&call_id->dbus_cancellable); - g_variant_get (value, - "((bb@a{ss}))", - &result->is_authorized, - &result->is_challenge, - NULL); - g_variant_unref (value); + self = call_id->self; + priv = NM_AUTH_MANAGER_GET_PRIVATE (self); + + value = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, G_VARIANT_TYPE ("((bba{ss}))"), &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + /* call_id was cancelled externally, but _call_id_free() kept call_id + * alive (and it has still the reference on @self. */ + + if (!priv->cancel_cancellable) { + /* we do a forced shutdown. There is no more time for cancelling... */ + _call_id_free (call_id); + + /* this shouldn't really happen, because: + * _call_check_authorize() only scheduled the D-Bus request at a time when + * cancel_cancellable was still set. It means, somebody called force-shutdown + * after call-id was schedule. + * force-shutdown should only be called after: + * - cancel all pending requests + * - give enough time to cancel the request and schedule a D-Bus call + * to CancelCheckAuthorization (below), before issuing force-shutdown. */ + g_return_if_reached (); + } - _LOGD ("call[%u]: CheckAuthorization succeeded: (is_authorized=%d, is_challenge=%d)", data->call_id, result->is_authorized, result->is_challenge); - g_simple_async_result_set_op_res_gpointer (data->simple, result, g_free); + g_dbus_proxy_call (priv->proxy, + "CancelCheckAuthorization", + g_variant_new ("(s)", + cancellation_id_to_str_a (call_id->call_numid)), + G_DBUS_CALL_FLAGS_NONE, + CANCELLATION_TIMEOUT_MS, + priv->cancel_cancellable, + cancel_check_authorization_cb, + call_id); + return; } - g_simple_async_result_complete (data->simple); + if (!error) { + g_variant_get (value, + "((bb@a{ss}))", + &is_authorized, + &is_challenge, + NULL); + _LOG2T (call_id, "completed: authorized=%d, challenge=%d", + is_authorized, is_challenge); + } else + _LOG2T (call_id, "completed: failed: %s", error->message); - _check_auth_data_free (data); + _call_id_invoke_callback (call_id, is_authorized, is_challenge, error); } static void -_call_check_authorization (CheckAuthData *data) +_call_check_authorize (NMAuthManagerCallId *call_id) { - NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (data->self); + NMAuthManager *self = call_id->self; + NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); + + nm_assert (call_id->dbus_parameters); + nm_assert (g_variant_is_floating (call_id->dbus_parameters)); + nm_assert (!call_id->dbus_cancellable); + + call_id->dbus_cancellable = g_cancellable_new (); + + nm_assert (priv->cancel_cancellable); g_dbus_proxy_call (priv->proxy, "CheckAuthorization", - data->dbus_parameters, + g_steal_pointer (&call_id->dbus_parameters), G_DBUS_CALL_FLAGS_NONE, G_MAXINT, /* no timeout */ - data->cancellable, - (GAsyncReadyCallback) check_authorization_cb, - data); - g_clear_object (&data->cancellable); - data->dbus_parameters = NULL; + call_id->dbus_cancellable, + _call_check_authorize_cb, + call_id); } -void -nm_auth_manager_polkit_authority_check_authorization (NMAuthManager *self, - NMAuthSubject *subject, - const char *action_id, - gboolean allow_user_interaction, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +static gboolean +_call_on_idle (gpointer user_data) +{ + NMAuthManagerCallId *call_id = user_data; + gs_free_error GError *error = NULL; + gboolean is_authorized = FALSE; + gboolean is_challenge = FALSE; + const char *error_msg = NULL; + + call_id->idle_id = 0; + if (call_id->idle_reason == IDLE_REASON_AUTHORIZED) { + is_authorized = TRUE; + _LOG2T (call_id, "completed: authorized=%d, challenge=%d (simulated)", + is_authorized, is_challenge); + } else { + nm_assert (call_id->idle_reason == IDLE_REASON_NO_DBUS); + error_msg = "failure creating GDBusProxy for authorization request"; + _LOG2T (call_id, "completed: failed due to no D-Bus proxy"); + } + + if (error_msg) + g_set_error_literal (&error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, error_msg); + _call_id_invoke_callback (call_id, is_authorized, is_challenge, error); + return G_SOURCE_REMOVE; +} + +/* + * @callback must never be invoked synchronously. + * + * @callback is always invoked exactly once, and never synchronously. + * You may cancel the invocation with nm_auth_manager_check_authorization_cancel(), + * but: you may only do so exactly once, and only before @callback is + * invoked. Even if you cancel the request, @callback will still be invoked + * (synchronously, during the _cancel() callback). + * + * The request keeps @self alive (it needs to do so, because when cancelling a + * request we might need to do an additional CancelCheckAuthorization call, for + * which @self must be live long enough). + */ +NMAuthManagerCallId * +nm_auth_manager_check_authorization (NMAuthManager *self, + NMAuthSubject *subject, + const char *action_id, + gboolean allow_user_interaction, + NMAuthManagerCheckAuthorizationCallback callback, + gpointer user_data) { NMAuthManagerPrivate *priv; + PolkitCheckAuthorizationFlags flags; char subject_buf[64]; GVariantBuilder builder; - PolkitCheckAuthorizationFlags flags; GVariant *subject_value; GVariant *details_value; - CheckAuthData *data; + NMAuthManagerCallId *call_id; - g_return_if_fail (NM_IS_AUTH_MANAGER (self)); - g_return_if_fail (NM_IS_AUTH_SUBJECT (subject)); - g_return_if_fail (nm_auth_subject_is_unix_process (subject)); - g_return_if_fail (action_id != NULL); - g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_val_if_fail (NM_IS_AUTH_MANAGER (self), NULL); + g_return_val_if_fail (NM_IN_SET (nm_auth_subject_get_subject_type (subject), + NM_AUTH_SUBJECT_TYPE_INTERNAL, + NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS), + NULL); + g_return_val_if_fail (action_id, NULL); priv = NM_AUTH_MANAGER_GET_PRIVATE (self); - g_return_if_fail (priv->polkit_enabled); + g_return_val_if_fail (!priv->disposing, NULL); + g_return_val_if_fail (!priv->shutting_down, NULL); flags = allow_user_interaction ? POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION : POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE; - subject_value = nm_auth_subject_unix_process_to_polkit_gvariant (subject); - nm_assert (g_variant_is_floating (subject_value)); - - /* ((PolkitDetails *)NULL) */ - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}")); - details_value = g_variant_builder_end (&builder); - - data = g_new0 (CheckAuthData, 1); - data->call_id = ++priv->call_id_counter; - data->self = g_object_ref (self); - data->simple = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - nm_auth_manager_polkit_authority_check_authorization); - if (cancellable != NULL) { - data->cancellation_id = g_strdup_printf ("cancellation-id-%u", data->call_id); - data->cancellable = g_object_ref (cancellable); - } - - data->dbus_parameters = g_variant_new ("(@(sa{sv})s@a{ss}us)", - subject_value, - action_id, - details_value, - (guint32) flags, - data->cancellation_id != NULL ? data->cancellation_id : ""); - - if (priv->new_proxy_cancellable) { - _LOGD ("call[%u]: CheckAuthorization(%s), subject=%s (wait for proxy)", data->call_id, action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); - - priv->queued_calls = g_slist_prepend (priv->queued_calls, data); - } else if (!priv->proxy) { - _LOGD ("call[%u]: CheckAuthorization(%s), subject=%s (fails due to invalid DBUS proxy)", data->call_id, action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); - - _call_check_authorization_complete_with_error (data, "invalid DBUS proxy"); + call_id = g_slice_new0 (NMAuthManagerCallId); + call_id->self = g_object_ref (self); + call_id->callback = callback; + call_id->user_data = user_data; + call_id->call_numid = ++priv->call_numid_counter; + c_list_link_tail (&priv->calls_lst_head, &call_id->calls_lst); + + if (!priv->polkit_enabled) { + _LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding due to polkit authorization disabled)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); + call_id->idle_reason = IDLE_REASON_AUTHORIZED; + call_id->idle_id = g_idle_add (_call_on_idle, call_id); + } else if (nm_auth_subject_is_internal (subject)) { + _LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding for internal request)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); + call_id->idle_reason = IDLE_REASON_AUTHORIZED; + call_id->idle_id = g_idle_add (_call_on_idle, call_id); + } else if (nm_auth_subject_get_unix_process_uid (subject) == 0) { + _LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding for root)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); + call_id->idle_reason = IDLE_REASON_AUTHORIZED; + call_id->idle_id = g_idle_add (_call_on_idle, call_id); + } else if ( !priv->proxy + && !priv->new_proxy_cancellable) { + _LOG2T (call_id, "CheckAuthorization(%s), subject=%s (failing due to invalid DBUS proxy)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); + call_id->idle_reason = IDLE_REASON_NO_DBUS; + call_id->idle_id = g_idle_add (_call_on_idle, call_id); } else { - _LOGD ("call[%u]: CheckAuthorization(%s), subject=%s", data->call_id, action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); - - _call_check_authorization (data); + subject_value = nm_auth_subject_unix_process_to_polkit_gvariant (subject); + nm_assert (g_variant_is_floating (subject_value)); + + /* ((PolkitDetails *)NULL) */ + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}")); + details_value = g_variant_builder_end (&builder); + + call_id->dbus_parameters = g_variant_new ("(@(sa{sv})s@a{ss}us)", + subject_value, + action_id, + details_value, + (guint32) flags, + cancellation_id_to_str_a (call_id->call_numid)); + if (!priv->proxy) { + _LOG2T (call_id, "CheckAuthorization(%s), subject=%s (wait for proxy)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); + } else { + _LOG2T (call_id, "CheckAuthorization(%s), subject=%s", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); + _call_check_authorize (call_id); + } } + + return call_id; } -gboolean -nm_auth_manager_polkit_authority_check_authorization_finish (NMAuthManager *self, - GAsyncResult *res, - gboolean *out_is_authorized, - gboolean *out_is_challenge, - GError **error) +void +nm_auth_manager_check_authorization_cancel (NMAuthManagerCallId *call_id) { - gboolean success = FALSE; - gboolean is_authorized = FALSE; - gboolean is_challenge = FALSE; + NMAuthManager *self; + gs_free_error GError *error = NULL; - g_return_val_if_fail (NM_IS_AUTH_MANAGER (self), FALSE); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_if_fail (call_id); - if (!g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) { - CheckAuthorizationResult *result; + self = call_id->self; - result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)); - is_authorized = !!result->is_authorized; - is_challenge = !!result->is_challenge; - success = TRUE; - } - g_assert ((success && !error) || (!success || error)); + g_return_if_fail (NM_IS_AUTH_MANAGER (self)); + g_return_if_fail (!c_list_is_empty (&call_id->calls_lst)); + + nm_assert (c_list_contains (&NM_AUTH_MANAGER_GET_PRIVATE (self)->calls_lst_head, &call_id->calls_lst)); - if (out_is_authorized) - *out_is_authorized = is_authorized; - if (out_is_challenge) - *out_is_challenge = is_challenge; - return success; + nm_utils_error_set_cancelled (&error, FALSE, "NMAuthManager"); + _LOG2T (call_id, "completed: failed due to call cancelled"); + _call_id_invoke_callback (call_id, + FALSE, + FALSE, + error); } /*****************************************************************************/ @@ -350,14 +443,14 @@ static void _emit_changed_signal (NMAuthManager *self) { _LOGD ("emit changed signal"); - g_signal_emit_by_name (self, NM_AUTH_MANAGER_SIGNAL_CHANGED); + g_signal_emit (self, signals[CHANGED_SIGNAL], 0); } static void _log_name_owner (NMAuthManager *self, char **out_name_owner) { NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); - char *name_owner; + gs_free char *name_owner = NULL; name_owner = g_dbus_proxy_get_name_owner (priv->proxy); if (name_owner) @@ -365,10 +458,7 @@ _log_name_owner (NMAuthManager *self, char **out_name_owner) else _LOGD ("dbus name owner: none"); - if (out_name_owner) - *out_name_owner = name_owner; - else - g_free (name_owner); + NM_SET_OUT (out_name_owner, g_steal_pointer (&name_owner)); } static void @@ -377,20 +467,16 @@ _dbus_on_name_owner_notify_cb (GObject *object, gpointer user_data) { NMAuthManager *self = user_data; - NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); - char *name_owner; + gs_free char *name_owner = NULL; - g_return_if_fail (priv->proxy == (void *) object); + nm_assert (NM_AUTH_MANAGER_GET_PRIVATE (self)->proxy == (GDBusProxy *) object); _log_name_owner (self, &name_owner); - if (!name_owner) { /* when the name disappears, we also want to raise a emit signal. * When it appears, we raise one already. */ _emit_changed_signal (self); } - - g_free (name_owner); } static void @@ -398,9 +484,8 @@ _dbus_on_changed_signal_cb (GDBusProxy *proxy, gpointer user_data) { NMAuthManager *self = user_data; - NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); - g_return_if_fail (priv->proxy == proxy); + nm_assert (NM_AUTH_MANAGER_GET_PRIVATE (self)->proxy == proxy); _LOGD ("dbus signal: \"Changed\""); _emit_changed_signal (self); @@ -411,49 +496,39 @@ _dbus_new_proxy_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { - NMAuthManager **p_self = user_data; - NMAuthManager *self = NULL; + NMAuthManager *self; NMAuthManagerPrivate *priv; - GError *error = NULL; + gs_free_error GError *error = NULL; GDBusProxy *proxy; - CheckAuthData *data; + NMAuthManagerCallId *call_id; proxy = g_dbus_proxy_new_for_bus_finish (res, &error); - if (!*p_self) { - _LOGD ("_dbus_new_proxy_cb(): manager destroyed before callback finished. Abort"); - g_clear_object (&proxy); - g_clear_error (&error); - g_free (p_self); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; - } - self = *p_self; - g_object_remove_weak_pointer (G_OBJECT (self), (void **)p_self); - g_free (p_self); + self = user_data; priv = NM_AUTH_MANAGER_GET_PRIVATE (self); - g_return_if_fail (priv->new_proxy_cancellable); - g_return_if_fail (!priv->proxy); - + priv->proxy = proxy; g_clear_object (&priv->new_proxy_cancellable); - priv->queued_calls = g_slist_reverse (priv->queued_calls); - - priv->proxy = proxy; if (!priv->proxy) { - _LOGE ("could not get polkit proxy: %s", error->message); - g_clear_error (&error); - - while (priv->queued_calls) { - data = priv->queued_calls->data; - priv->queued_calls = g_slist_remove (priv->queued_calls, data); - - _call_check_authorization_complete_with_error (data, "error creating DBUS proxy"); + _LOGE ("could not create polkit proxy: %s", error->message); + +again: + c_list_for_each_entry (call_id, &priv->calls_lst_head, calls_lst) { + if (call_id->dbus_parameters) { + _LOG2T (call_id, "completed: failed due to no D-Bus proxy after startup"); + _call_id_invoke_callback (call_id, FALSE, FALSE, error); + goto again; + } } return; } + priv->cancel_cancellable = g_cancellable_new (); + g_signal_connect (priv->proxy, "notify::g-name-owner", G_CALLBACK (_dbus_on_name_owner_notify_cb), @@ -464,17 +539,16 @@ _dbus_new_proxy_cb (GObject *source_object, _log_name_owner (self, NULL); - while (priv->queued_calls) { - data = priv->queued_calls->data; - priv->queued_calls = g_slist_remove (priv->queued_calls, data); - _LOGD ("call[%u]: CheckAuthorization invoke now", data->call_id); - _call_check_authorization (data); + c_list_for_each_entry (call_id, &priv->calls_lst_head, calls_lst) { + if (call_id->dbus_parameters) { + _LOG2T (call_id, "CheckAuthorization invoke now"); + _call_check_authorize (call_id); + } } + _emit_changed_signal (self); } -#endif - /*****************************************************************************/ NMAuthManager * @@ -485,23 +559,42 @@ nm_auth_manager_get () return singleton_instance; } -/*****************************************************************************/ - -static void -get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +void +nm_auth_manager_force_shutdown (NMAuthManager *self) { - NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE ((NMAuthManager *) object); + NMAuthManagerPrivate *priv; - switch (prop_id) { - case PROP_POLKIT_ENABLED: - g_value_set_boolean (value, priv->polkit_enabled); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } + g_return_if_fail (NM_IS_AUTH_MANAGER (self)); + + priv = NM_AUTH_MANAGER_GET_PRIVATE (self); + + /* while we have pending requests (NMAuthManagerCallId), the instance + * is kept alive. + * + * Even if the caller cancells all pending call-ids, we still need to keep + * a reference to self, in order to handle pending CancelCheckAuthorization + * requests. + * + * To do a corrdinated shutdown, do the following: + * - cancel all pending NMAuthManagerCallId requests. + * - ensure everybody unrefs the NMAuthManager instance. If by that, the instance + * gets destroyed, the shutdown already completed successfully. + * - Otherwise, the object is kept alive by pending CancelCheckAuthorization requests. + * wait a certain timeout (1 second) for all requests to complete (by watching + * for destruction of NMAuthManager). + * - if that doesn't happen within timeout, issue nm_auth_manager_force_shutdown() and + * wait longer. After that, soon the instance should be destroyed and you + * did a successful shutdown. + * - if the instance was still not destroyed within a short timeout, you leaked + * resources. You cannot properly shutdown. + */ + + priv->shutting_down = TRUE; + nm_clear_g_cancellable (&priv->cancel_cancellable); } +/*****************************************************************************/ + static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { @@ -523,6 +616,9 @@ set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *p static void nm_auth_manager_init (NMAuthManager *self) { + NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); + + c_list_init (&priv->calls_lst_head); } static void @@ -533,16 +629,10 @@ constructed (GObject *object) G_OBJECT_CLASS (nm_auth_manager_parent_class)->constructed (object); -#if WITH_POLKIT _LOGD ("create auth-manager: polkit %s", priv->polkit_enabled ? "enabled" : "disabled"); if (priv->polkit_enabled) { - NMAuthManager **p_self; - priv->new_proxy_cancellable = g_cancellable_new (); - p_self = g_new (NMAuthManager *, 1); - *p_self = self; - g_object_add_weak_pointer (G_OBJECT (self), (void **) p_self); g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, @@ -551,14 +641,8 @@ constructed (GObject *object) POLKIT_INTERFACE, priv->new_proxy_cancellable, _dbus_new_proxy_cb, - p_self); + self); } -#else - if (priv->polkit_enabled) - _LOGW ("create auth-manager: polkit disabled at compile time. All authentication requests will fail"); - else - _LOGD ("create auth-manager: polkit disabled at compile time"); -#endif } NMAuthManager * @@ -585,23 +669,21 @@ static void dispose (GObject *object) { NMAuthManager* self = NM_AUTH_MANAGER (object); -#if WITH_POLKIT NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); -#endif _LOGD ("dispose"); -#if WITH_POLKIT - /* since we take a reference for each queued call, we don't expect to have any queued calls in dispose() */ - g_assert (!priv->queued_calls); + nm_assert (c_list_is_empty (&priv->calls_lst_head)); + + priv->disposing = TRUE; nm_clear_g_cancellable (&priv->new_proxy_cancellable); + nm_clear_g_cancellable (&priv->cancel_cancellable); if (priv->proxy) { g_signal_handlers_disconnect_by_data (priv->proxy, self); g_clear_object (&priv->proxy); } -#endif G_OBJECT_CLASS (nm_auth_manager_parent_class)->dispose (object); } @@ -611,7 +693,6 @@ nm_auth_manager_class_init (NMAuthManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->get_property = get_property; object_class->set_property = set_property; object_class->constructed = constructed; object_class->dispose = dispose; @@ -619,7 +700,7 @@ nm_auth_manager_class_init (NMAuthManagerClass *klass) obj_properties[PROP_POLKIT_ENABLED] = g_param_spec_boolean (NM_AUTH_MANAGER_POLKIT_ENABLED, "", "", FALSE, - G_PARAM_READWRITE | + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); @@ -628,11 +709,7 @@ nm_auth_manager_class_init (NMAuthManagerClass *klass) signals[CHANGED_SIGNAL] = g_signal_new (NM_AUTH_MANAGER_SIGNAL_CHANGED, NM_TYPE_AUTH_MANAGER, G_SIGNAL_RUN_LAST, - 0, /* class offset */ - NULL, /* accumulator */ - NULL, /* accumulator data */ + 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); + G_TYPE_NONE, 0); } - diff --git a/src/nm-auth-manager.h b/src/nm-auth-manager.h index e66ef78c..fe7ee787 100644 --- a/src/nm-auth-manager.h +++ b/src/nm-auth-manager.h @@ -23,6 +23,31 @@ #include "nm-auth-subject.h" +/*****************************************************************************/ + +typedef enum { + NM_AUTH_CALL_RESULT_UNKNOWN, + NM_AUTH_CALL_RESULT_YES, + NM_AUTH_CALL_RESULT_AUTH, + NM_AUTH_CALL_RESULT_NO, +} NMAuthCallResult; + +static inline NMAuthCallResult +nm_auth_call_result_eval (gboolean is_authorized, + gboolean is_challenge, + GError *error) +{ + if (error) + return NM_AUTH_CALL_RESULT_UNKNOWN; + if (is_authorized) + return NM_AUTH_CALL_RESULT_YES; + if (is_challenge) + return NM_AUTH_CALL_RESULT_AUTH; + return NM_AUTH_CALL_RESULT_NO; +} + +/*****************************************************************************/ + #define NM_TYPE_AUTH_MANAGER (nm_auth_manager_get_type ()) #define NM_AUTH_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_AUTH_MANAGER, NMAuthManager)) #define NM_AUTH_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_AUTH_MANAGER, NMAuthManagerClass)) @@ -42,23 +67,28 @@ GType nm_auth_manager_get_type (void); NMAuthManager *nm_auth_manager_setup (gboolean polkit_enabled); NMAuthManager *nm_auth_manager_get (void); +void nm_auth_manager_force_shutdown (NMAuthManager *self); + gboolean nm_auth_manager_get_polkit_enabled (NMAuthManager *self); -#if WITH_POLKIT - -void nm_auth_manager_polkit_authority_check_authorization (NMAuthManager *self, - NMAuthSubject *subject, - const char *action_id, - gboolean allow_user_interaction, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean nm_auth_manager_polkit_authority_check_authorization_finish (NMAuthManager *self, - GAsyncResult *res, - gboolean *out_is_authorized, - gboolean *out_is_challenge, - GError **error); - -#endif +/*****************************************************************************/ + +typedef struct _NMAuthManagerCallId NMAuthManagerCallId; + +typedef void (*NMAuthManagerCheckAuthorizationCallback) (NMAuthManager *self, + NMAuthManagerCallId *call_id, + gboolean is_authorized, + gboolean is_challenge, + GError *error, + gpointer user_data); + +NMAuthManagerCallId *nm_auth_manager_check_authorization (NMAuthManager *self, + NMAuthSubject *subject, + const char *action_id, + gboolean allow_user_interaction, + NMAuthManagerCheckAuthorizationCallback callback, + gpointer user_data); + +void nm_auth_manager_check_authorization_cancel (NMAuthManagerCallId *call_id); #endif /* NM_AUTH_MANAGER_H */ diff --git a/src/nm-auth-subject.c b/src/nm-auth-subject.c index 0f40ff7c..117a3815 100644 --- a/src/nm-auth-subject.c +++ b/src/nm-auth-subject.c @@ -33,8 +33,7 @@ #include #include -#include "nm-bus-manager.h" -#include "NetworkManagerUtils.h" +#include "nm-dbus-manager.h" enum { PROP_0, @@ -93,17 +92,15 @@ nm_auth_subject_to_string (NMAuthSubject *self, char *buf, gsize buf_len) (unsigned long long) priv->unix_process.start_time); break; case NM_AUTH_SUBJECT_TYPE_INTERNAL: - g_strlcat (buf, "internal", buf_len); + g_strlcpy (buf, "internal", buf_len); break; default: - g_strlcat (buf, "invalid", buf_len); + g_strlcpy (buf, "invalid", buf_len); break; } return buf; } -#if WITH_POLKIT - /* returns a floating variant */ GVariant * nm_auth_subject_unix_process_to_polkit_gvariant (NMAuthSubject *self) @@ -125,8 +122,6 @@ nm_auth_subject_unix_process_to_polkit_gvariant (NMAuthSubject *self) return ret; } -#endif - NMAuthSubjectType nm_auth_subject_get_subject_type (NMAuthSubject *subject) { @@ -186,20 +181,20 @@ _new_unix_process (GDBusMethodInvocation *context, g_return_val_if_fail (context || (connection && message), NULL); if (context) { - success = nm_bus_manager_get_caller_info (nm_bus_manager_get (), - context, - &dbus_sender, - &uid, - &pid); - } else if (message) { - success = nm_bus_manager_get_caller_info_from_message (nm_bus_manager_get (), - connection, - message, - &dbus_sender, - &uid, - &pid); - } else - g_assert_not_reached (); + success = nm_dbus_manager_get_caller_info (nm_dbus_manager_get (), + context, + &dbus_sender, + &uid, + &pid); + } else { + nm_assert (message); + success = nm_dbus_manager_get_caller_info_from_message (nm_dbus_manager_get (), + connection, + message, + &dbus_sender, + &uid, + &pid); + } if (!success) return NULL; diff --git a/src/nm-auth-subject.h b/src/nm-auth-subject.h index a0b6d14b..9267dfd4 100644 --- a/src/nm-auth-subject.h +++ b/src/nm-auth-subject.h @@ -49,29 +49,20 @@ NMAuthSubject *nm_auth_subject_new_unix_process_from_context (GDBusMethodInvocat NMAuthSubject *nm_auth_subject_new_unix_process_from_message (GDBusConnection *connection, GDBusMessage *message); - NMAuthSubjectType nm_auth_subject_get_subject_type (NMAuthSubject *subject); - gboolean nm_auth_subject_is_internal (NMAuthSubject *subject); - gboolean nm_auth_subject_is_unix_process (NMAuthSubject *subject); gulong nm_auth_subject_get_unix_process_pid (NMAuthSubject *subject); const char *nm_auth_subject_get_unix_process_dbus_sender (NMAuthSubject *subject); - gulong nm_auth_subject_get_unix_process_uid (NMAuthSubject *subject); - const char *nm_auth_subject_to_string (NMAuthSubject *self, char *buf, gsize buf_len); -#if WITH_POLKIT - GVariant * nm_auth_subject_unix_process_to_polkit_gvariant (NMAuthSubject *self); -#endif - #endif /* __NETWORKMANAGER_AUTH_SUBJECT_H__ */ diff --git a/src/nm-auth-utils.c b/src/nm-auth-utils.c index f1aff430..b41f6efa 100644 --- a/src/nm-auth-utils.c +++ b/src/nm-auth-utils.c @@ -24,48 +24,83 @@ #include -#include "nm-utils/nm-hash-utils.h" +#include "nm-utils/nm-c-list.h" + #include "nm-setting-connection.h" #include "nm-auth-subject.h" #include "nm-auth-manager.h" #include "nm-session-monitor.h" +/*****************************************************************************/ + struct NMAuthChain { - guint32 refcount; - GSList *calls; - GHashTable *data; + GHashTable *data_hash; + + CList auth_call_lst_head; GDBusMethodInvocation *context; NMAuthSubject *subject; - GError *error; - - guint idle_id; - gboolean done; NMAuthChainResultFunc done_func; gpointer user_data; + + guint32 refcount; + + bool done:1; }; typedef struct { + CList auth_call_lst; NMAuthChain *chain; - GCancellable *cancellable; + NMAuthManagerCallId *call_id; char *permission; - guint call_idle_id; } AuthCall; +/*****************************************************************************/ + +static void +_ASSERT_call (AuthCall *call) +{ + nm_assert (call); + nm_assert (call->chain); + nm_assert (nm_c_list_contains_entry (&call->chain->auth_call_lst_head, call, auth_call_lst)); +} + +/*****************************************************************************/ + +static void +auth_call_free (AuthCall *call) +{ + if (call->call_id) + nm_auth_manager_check_authorization_cancel (call->call_id); + c_list_unlink_stale (&call->auth_call_lst); + g_free (call->permission); + g_slice_free (AuthCall, call); +} + +/*****************************************************************************/ + typedef struct { + + /* must be the first field. */ + const char *tag; + gpointer data; GDestroyNotify destroy; + char tag_data[]; } ChainData; static ChainData * -chain_data_new (gpointer data, GDestroyNotify destroy) +chain_data_new (const char *tag, gpointer data, GDestroyNotify destroy) { ChainData *tmp; + gsize l = strlen (tag); - tmp = g_slice_new (ChainData); + tmp = g_malloc (sizeof (ChainData) + l + 1); + tmp->tag = &tmp->tag_data[0]; tmp->data = data; tmp->destroy = destroy; + memcpy (&tmp->tag_data[0], tag, l + 1); return tmp; } @@ -76,69 +111,7 @@ chain_data_free (gpointer data) if (tmp->destroy) tmp->destroy (tmp->data); - memset (tmp, 0, sizeof (ChainData)); - g_slice_free (ChainData, tmp); -} - -static gboolean -auth_chain_finish (gpointer user_data) -{ - NMAuthChain *self = user_data; - - self->idle_id = 0; - self->done = TRUE; - - /* Ensure we stay alive across the callback */ - self->refcount++; - self->done_func (self, self->error, self->context, self->user_data); - nm_auth_chain_unref (self); - return FALSE; -} - -/* Creates the NMAuthSubject automatically */ -NMAuthChain * -nm_auth_chain_new_context (GDBusMethodInvocation *context, - NMAuthChainResultFunc done_func, - gpointer user_data) -{ - NMAuthSubject *subject; - NMAuthChain *chain; - - g_return_val_if_fail (context != NULL, NULL); - - subject = nm_auth_subject_new_unix_process_from_context (context); - if (!subject) - return NULL; - - chain = nm_auth_chain_new_subject (subject, - context, - done_func, - user_data); - g_object_unref (subject); - return chain; -} - -/* Requires an NMAuthSubject */ -NMAuthChain * -nm_auth_chain_new_subject (NMAuthSubject *subject, - GDBusMethodInvocation *context, - NMAuthChainResultFunc done_func, - gpointer user_data) -{ - NMAuthChain *self; - - g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); - g_return_val_if_fail (nm_auth_subject_is_unix_process (subject) || nm_auth_subject_is_internal (subject), NULL); - - self = g_slice_new0 (NMAuthChain); - self->refcount = 1; - self->data = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, chain_data_free); - self->done_func = done_func; - self->user_data = user_data; - self->context = context ? g_object_ref (context) : NULL; - self->subject = g_object_ref (subject); - - return self; + g_free (tmp); } static gpointer @@ -146,15 +119,17 @@ _get_data (NMAuthChain *self, const char *tag) { ChainData *tmp; - tmp = g_hash_table_lookup (self->data, tag); + if (!self->data_hash) + return NULL; + tmp = g_hash_table_lookup (self->data_hash, &tag); return tmp ? tmp->data : NULL; } gpointer nm_auth_chain_get_data (NMAuthChain *self, const char *tag) { - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (tag != NULL, NULL); + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (tag, NULL); return _get_data (self, tag); } @@ -175,19 +150,22 @@ nm_auth_chain_steal_data (NMAuthChain *self, const char *tag) { ChainData *tmp; gpointer value = NULL; - void *orig_key; - - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (tag != NULL, NULL); - - if (g_hash_table_lookup_extended (self->data, tag, &orig_key, (gpointer)&tmp)) { - g_hash_table_steal (self->data, tag); - value = tmp->data; - /* Make sure the destroy handler isn't called when freeing */ - tmp->destroy = NULL; - chain_data_free (tmp); - g_free (orig_key); - } + + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (tag, NULL); + + if (!self->data_hash) + return NULL; + + tmp = g_hash_table_lookup (self->data_hash, &tag); + if (!tmp) + return NULL; + + value = tmp->data; + + /* Make sure the destroy handler isn't called when freeing */ + tmp->destroy = NULL; + g_hash_table_remove (self->data_hash, tmp); return value; } @@ -197,165 +175,105 @@ nm_auth_chain_set_data (NMAuthChain *self, gpointer data, GDestroyNotify data_destroy) { - g_return_if_fail (self != NULL); - g_return_if_fail (tag != NULL); - - if (data == NULL) - g_hash_table_remove (self->data, tag); - else { - g_hash_table_insert (self->data, - g_strdup (tag), - chain_data_new (data, data_destroy)); + g_return_if_fail (self); + g_return_if_fail (tag); + + if (data == NULL) { + if (self->data_hash) + g_hash_table_remove (self->data_hash, &tag); + } else { + if (!self->data_hash) { + self->data_hash = g_hash_table_new_full (nm_pstr_hash, nm_pstr_equal, + NULL, chain_data_free); + } + g_hash_table_add (self->data_hash, + chain_data_new (tag, data, data_destroy)); } } -gulong -nm_auth_chain_get_data_ulong (NMAuthChain *self, const char *tag) -{ - gulong *data; +/*****************************************************************************/ - g_return_val_if_fail (self != NULL, 0); - g_return_val_if_fail (tag != NULL, 0); - - data = _get_data (self, tag); - return data ? *data : 0ul; -} - -void -nm_auth_chain_set_data_ulong (NMAuthChain *self, - const char *tag, - gulong data) +NMAuthCallResult +nm_auth_chain_get_result (NMAuthChain *self, const char *permission) { - gulong *ptr; + gpointer data; - g_return_if_fail (self != NULL); - g_return_if_fail (tag != NULL); + g_return_val_if_fail (self, NM_AUTH_CALL_RESULT_UNKNOWN); + g_return_val_if_fail (permission, NM_AUTH_CALL_RESULT_UNKNOWN); - ptr = g_malloc (sizeof (*ptr)); - *ptr = data; - nm_auth_chain_set_data (self, tag, ptr, g_free); + data = _get_data (self, permission); + return data ? GPOINTER_TO_UINT (data) : NM_AUTH_CALL_RESULT_UNKNOWN; } NMAuthSubject * nm_auth_chain_get_subject (NMAuthChain *self) { - g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (self, NULL); return self->subject; } -NMAuthCallResult -nm_auth_chain_get_result (NMAuthChain *self, const char *permission) -{ - gpointer data; - - g_return_val_if_fail (self != NULL, NM_AUTH_CALL_RESULT_UNKNOWN); - g_return_val_if_fail (permission != NULL, NM_AUTH_CALL_RESULT_UNKNOWN); +/*****************************************************************************/ - data = _get_data (self, permission); - return data ? GPOINTER_TO_UINT (data) : NM_AUTH_CALL_RESULT_UNKNOWN; -} - -static AuthCall * -auth_call_new (NMAuthChain *chain, const char *permission) +static gboolean +auth_chain_finish (NMAuthChain *self) { - AuthCall *call; + self->done = TRUE; - call = g_slice_new0 (AuthCall); - call->chain = chain; - call->permission = g_strdup (permission); - return call; + /* Ensure we stay alive across the callback */ + nm_assert (self->refcount == 1); + self->refcount++; + self->done_func (self, NULL, self->context, self->user_data); + nm_assert (NM_IN_SET (self->refcount, 1, 2)); + nm_auth_chain_destroy (self); + return FALSE; } static void -auth_call_free (AuthCall *call) -{ - g_free (call->permission); - g_clear_object (&call->cancellable); - g_slice_free (AuthCall, call); -} - -static gboolean auth_call_complete (AuthCall *call) { NMAuthChain *self; - g_return_val_if_fail (call, G_SOURCE_REMOVE); + _ASSERT_call (call); self = call->chain; - g_return_val_if_fail (self, G_SOURCE_REMOVE); - g_return_val_if_fail (g_slist_find (self->calls, call), G_SOURCE_REMOVE); - - self->calls = g_slist_remove (self->calls, call); + nm_assert (!self->done); - if (!self->calls) { - g_assert (!self->idle_id && !self->done); - self->idle_id = g_idle_add (auth_chain_finish, self); - } auth_call_free (call); - return FALSE; -} -static void -auth_call_cancel (gpointer user_data) -{ - AuthCall *call = user_data; - - if (nm_clear_g_cancellable (&call->cancellable)) { - /* we don't free call immediately. Instead we cancel the async operation - * and set cancellable to NULL. pk_call_cb() will check for this and - * do the final cleanup. */ - } else { - g_source_remove (call->call_idle_id); - auth_call_free (call); + if (c_list_is_empty (&self->auth_call_lst_head)) { + /* we are on an idle-handler or a clean call-stack (non-reentrant). */ + auth_chain_finish (self); } } -#if WITH_POLKIT static void -pk_call_cb (GObject *object, GAsyncResult *result, gpointer user_data) +pk_call_cb (NMAuthManager *auth_manager, + NMAuthManagerCallId *call_id, + gboolean is_authorized, + gboolean is_challenge, + GError *error, + gpointer user_data) { - AuthCall *call = user_data; - GError *error = NULL; - gboolean is_authorized = FALSE, is_challenge = FALSE; - guint call_result = NM_AUTH_CALL_RESULT_UNKNOWN; - - nm_auth_manager_polkit_authority_check_authorization_finish (NM_AUTH_MANAGER (object), - result, - &is_authorized, - &is_challenge, - &error); - - /* If the call is already canceled do nothing */ - if (!call->cancellable) { - nm_log_dbg (LOGD_CORE, "callback already cancelled"); - g_clear_error (&error); - auth_call_free (call); + AuthCall *call; + NMAuthCallResult call_result; + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; - } - if (error) { - /* Don't ruin the chain. Just leave the result unknown. */ - nm_log_warn (LOGD_CORE, "error requesting auth for %s: %s", - call->permission, error->message); - g_clear_error (&error); - } else { - if (is_authorized) { - /* Caller has the permission */ - call_result = NM_AUTH_CALL_RESULT_YES; - } else if (is_challenge) { - /* Caller could authenticate to get the permission */ - call_result = NM_AUTH_CALL_RESULT_AUTH; - } else - call_result = NM_AUTH_CALL_RESULT_NO; - } + call = user_data; + + nm_assert (call->call_id == call_id); + + call->call_id = NULL; + + call_result = nm_auth_call_result_eval (is_authorized, is_challenge, error); nm_auth_chain_set_data (call->chain, call->permission, GUINT_TO_POINTER (call_result), NULL); auth_call_complete (call); } -#endif void nm_auth_chain_add_call (NMAuthChain *self, @@ -365,81 +283,109 @@ nm_auth_chain_add_call (NMAuthChain *self, AuthCall *call; NMAuthManager *auth_manager = nm_auth_manager_get (); - g_return_if_fail (self != NULL); - g_return_if_fail (permission && *permission); + g_return_if_fail (self); g_return_if_fail (self->subject); + g_return_if_fail (!self->done); + g_return_if_fail (permission && *permission); g_return_if_fail (nm_auth_subject_is_unix_process (self->subject) || nm_auth_subject_is_internal (self->subject)); - g_return_if_fail (!self->idle_id && !self->done); - call = auth_call_new (self, permission); - self->calls = g_slist_append (self->calls, call); + call = g_slice_new0 (AuthCall); + call->chain = self; + call->permission = g_strdup (permission); + c_list_link_tail (&self->auth_call_lst_head, &call->auth_call_lst); + call->call_id = nm_auth_manager_check_authorization (auth_manager, + self->subject, + permission, + allow_interaction, + pk_call_cb, + call); +} - if ( nm_auth_subject_is_internal (self->subject) - || nm_auth_subject_get_unix_process_uid (self->subject) == 0 - || !nm_auth_manager_get_polkit_enabled (auth_manager)) { - /* Root user or non-polkit always gets the permission */ - nm_auth_chain_set_data (self, permission, GUINT_TO_POINTER (NM_AUTH_CALL_RESULT_YES), NULL); - call->call_idle_id = g_idle_add ((GSourceFunc) auth_call_complete, call); - } else { - /* Non-root always gets authenticated when using polkit */ -#if WITH_POLKIT - call->cancellable = g_cancellable_new (); - nm_auth_manager_polkit_authority_check_authorization (auth_manager, - self->subject, - permission, - allow_interaction, - call->cancellable, - pk_call_cb, - call); -#else - if (!call->chain->error) { - call->chain->error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "Polkit support is disabled at compile time"); - } - call->call_idle_id = g_idle_add ((GSourceFunc) auth_call_complete, call); -#endif - } +/*****************************************************************************/ + +/* Creates the NMAuthSubject automatically */ +NMAuthChain * +nm_auth_chain_new_context (GDBusMethodInvocation *context, + NMAuthChainResultFunc done_func, + gpointer user_data) +{ + NMAuthSubject *subject; + NMAuthChain *chain; + + g_return_val_if_fail (context, NULL); + + subject = nm_auth_subject_new_unix_process_from_context (context); + if (!subject) + return NULL; + + chain = nm_auth_chain_new_subject (subject, + context, + done_func, + user_data); + g_object_unref (subject); + return chain; +} + +/* Requires an NMAuthSubject */ +NMAuthChain * +nm_auth_chain_new_subject (NMAuthSubject *subject, + GDBusMethodInvocation *context, + NMAuthChainResultFunc done_func, + gpointer user_data) +{ + NMAuthChain *self; + + g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); + nm_assert (nm_auth_subject_is_unix_process (subject) || nm_auth_subject_is_internal (subject)); + + self = g_slice_new0 (NMAuthChain); + c_list_init (&self->auth_call_lst_head); + self->refcount = 1; + self->done_func = done_func; + self->user_data = user_data; + self->context = context ? g_object_ref (context) : NULL; + self->subject = g_object_ref (subject); + return self; } /** - * nm_auth_chain_unref: + * nm_auth_chain_destroy: * @self: the auth-chain * - * Unrefs the auth-chain. By unrefing the auth-chain, you also cancel + * Destroys the auth-chain. By destroying the auth-chain, you also cancel * the receipt of the done-callback. IOW, the callback will not be invoked. * - * The only exception is, if you call nm_auth_chain_unref() from inside + * The only exception is, if may call nm_auth_chain_destroy() from inside * the callback. In this case, @self stays alive until the callback returns. + * + * Note that you might only destroy an auth-chain exactly once, and never + * after the callback was handled. */ void -nm_auth_chain_unref (NMAuthChain *self) +nm_auth_chain_destroy (NMAuthChain *self) { - g_return_if_fail (self != NULL); - g_return_if_fail (self->refcount > 0); - - self->refcount--; - if (self->refcount > 0) - return; + AuthCall *call; - if (self->idle_id) - g_source_remove (self->idle_id); + g_return_if_fail (self); + g_return_if_fail (NM_IN_SET (self->refcount, 1, 2)); - g_object_unref (self->subject); + if (--self->refcount > 0) + return; - if (self->context) - g_object_unref (self->context); + nm_clear_g_object (&self->subject); + nm_clear_g_object (&self->context); - g_slist_free_full (self->calls, auth_call_cancel); + while ((call = c_list_first_entry (&self->auth_call_lst_head, AuthCall, auth_call_lst))) + auth_call_free (call); - g_clear_error (&self->error); - g_hash_table_destroy (self->data); + nm_clear_pointer (&self->data_hash, g_hash_table_destroy); - memset (self, 0, sizeof (NMAuthChain)); g_slice_free (NMAuthChain, self); } -/************ utils **************/ +/****************************************************************************** + * utils + *****************************************************************************/ gboolean nm_auth_is_subject_in_acl (NMConnection *connection, @@ -450,7 +396,7 @@ nm_auth_is_subject_in_acl (NMConnection *connection, const char *user = NULL; gulong uid; - g_return_val_if_fail (connection != NULL, FALSE); + g_return_val_if_fail (connection, FALSE); g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), FALSE); g_return_val_if_fail (nm_auth_subject_is_internal (subject) || nm_auth_subject_is_unix_process (subject), FALSE); @@ -464,8 +410,8 @@ nm_auth_is_subject_in_acl (NMConnection *connection, return TRUE; if (!nm_session_monitor_uid_to_user (uid, &user)) { - if (out_error_desc) - *out_error_desc = g_strdup_printf ("Could not determine username for uid %lu", uid); + NM_SET_OUT (out_error_desc, + g_strdup_printf ("Could not determine username for uid %lu", uid)); return FALSE; } @@ -479,12 +425,31 @@ nm_auth_is_subject_in_acl (NMConnection *connection, /* Match the username returned by the session check to a user in the ACL */ if (!nm_setting_connection_permissions_user_allowed (s_con, user)) { - if (out_error_desc) - *out_error_desc = g_strdup_printf ("uid %lu has no permission to perform this operation", uid); + NM_SET_OUT (out_error_desc, + g_strdup_printf ("uid %lu has no permission to perform this operation", uid)); return FALSE; } return TRUE; } +gboolean +nm_auth_is_subject_in_acl_set_error (NMConnection *connection, + NMAuthSubject *subject, + GQuark err_domain, + int err_code, + GError **error) +{ + char *error_desc = NULL; + + nm_assert (!error || !*error); + + if (nm_auth_is_subject_in_acl (connection, + subject, + error ? &error_desc : NULL)) + return TRUE; + g_set_error_literal (error, err_domain, err_code, error_desc); + g_free (error_desc); + return FALSE; +} diff --git a/src/nm-auth-utils.h b/src/nm-auth-utils.h index 89ed79cc..5f9823b6 100644 --- a/src/nm-auth-utils.h +++ b/src/nm-auth-utils.h @@ -23,14 +23,9 @@ #include "nm-connection.h" -typedef struct NMAuthChain NMAuthChain; +#include "nm-auth-manager.h" -typedef enum { - NM_AUTH_CALL_RESULT_UNKNOWN, - NM_AUTH_CALL_RESULT_YES, - NM_AUTH_CALL_RESULT_AUTH, - NM_AUTH_CALL_RESULT_NO, -} NMAuthCallResult; +typedef struct NMAuthChain NMAuthChain; typedef void (*NMAuthChainResultFunc) (NMAuthChain *chain, GError *error, @@ -55,12 +50,6 @@ void nm_auth_chain_set_data (NMAuthChain *chain, gpointer data, GDestroyNotify data_destroy); -void nm_auth_chain_set_data_ulong (NMAuthChain *chain, - const char *tag, - gulong data); - -gulong nm_auth_chain_get_data_ulong (NMAuthChain *chain, const char *tag); - NMAuthCallResult nm_auth_chain_get_result (NMAuthChain *chain, const char *permission); @@ -68,14 +57,20 @@ void nm_auth_chain_add_call (NMAuthChain *chain, const char *permission, gboolean allow_interaction); -void nm_auth_chain_unref (NMAuthChain *chain); +void nm_auth_chain_destroy (NMAuthChain *chain); + +NMAuthSubject *nm_auth_chain_get_subject (NMAuthChain *self); /* Caller must free returned error description */ gboolean nm_auth_is_subject_in_acl (NMConnection *connection, NMAuthSubject *subect, char **out_error_desc); -NMAuthSubject *nm_auth_chain_get_subject (NMAuthChain *self); +gboolean nm_auth_is_subject_in_acl_set_error (NMConnection *connection, + NMAuthSubject *subject, + GQuark err_domain, + int err_code, + GError **error); #endif /* __NETWORKMANAGER_MANAGER_AUTH_H__ */ diff --git a/src/nm-bus-manager.c b/src/nm-bus-manager.c deleted file mode 100644 index f6b86e90..00000000 --- a/src/nm-bus-manager.c +++ /dev/null @@ -1,1024 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2006 - 2013 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#include "nm-default.h" - -#include "nm-bus-manager.h" - -#include -#include -#include -#include -#include - -#include "nm-dbus-interface.h" -#include "nm-core-internal.h" -#include "nm-dbus-compat.h" -#include "nm-exported-object.h" -#include "NetworkManagerUtils.h" - -/* The base path for our GDBusObjectManagerServers. They do not contain - * "NetworkManager" because GDBusObjectManagerServer requires that all - * exported objects be *below* the base path, and eg the Manager object - * is the base path already. - */ -#define OBJECT_MANAGER_SERVER_BASE_PATH "/org/freedesktop" - -/*****************************************************************************/ - -enum { - DBUS_CONNECTION_CHANGED = 0, - PRIVATE_CONNECTION_NEW, - PRIVATE_CONNECTION_DISCONNECTED, - NUMBER_OF_SIGNALS, -}; - -static guint signals[NUMBER_OF_SIGNALS]; - -typedef struct { - GDBusConnection *connection; - GDBusObjectManagerServer *obj_manager; - gboolean started; - - GSList *private_servers; - - GDBusProxy *proxy; - - gulong bus_closed_id; - guint reconnect_id; -} NMBusManagerPrivate; - -struct _NMBusManager { - GObject parent; - NMBusManagerPrivate _priv; -}; - -struct _NMBusManagerClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE(NMBusManager, nm_bus_manager, G_TYPE_OBJECT) - -#define NM_BUS_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBusManager, NM_IS_BUS_MANAGER) - -/*****************************************************************************/ - -#define _NMLOG_DOMAIN LOGD_CORE -#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bus-manager", __VA_ARGS__) - -/*****************************************************************************/ - -static gboolean nm_bus_manager_init_bus (NMBusManager *self); -static void nm_bus_manager_cleanup (NMBusManager *self); -static void start_reconnection_timeout (NMBusManager *self); - -/*****************************************************************************/ - -NM_DEFINE_SINGLETON_REGISTER (NMBusManager); - -NMBusManager * -nm_bus_manager_get (void) -{ - if (G_UNLIKELY (!singleton_instance)) { - nm_bus_manager_setup (g_object_new (NM_TYPE_BUS_MANAGER, NULL)); - if (!nm_bus_manager_init_bus (singleton_instance)) - start_reconnection_timeout (singleton_instance); - } - return singleton_instance; -} - -void -nm_bus_manager_setup (NMBusManager *instance) -{ - static char already_setup = FALSE; - - g_assert (NM_IS_BUS_MANAGER (instance)); - g_assert (!already_setup); - g_assert (!singleton_instance); - - already_setup = TRUE; - singleton_instance = instance; - nm_singleton_instance_register (); - _LOGD ("setup %s singleton (%p)", "NMBusManager", singleton_instance); -} - -/*****************************************************************************/ - -typedef struct { - const char *tag; - GQuark detail; - char *address; - GDBusServer *server; - - /* With peer bus connections, we'll get a new connection for each - * client. For each connection we create an ObjectManager for - * that connection to handle exporting our objects. This table - * maps GDBusObjectManager :: 'fake sender'. - * - * Note that even for connections that don't export any objects - * we'll still create GDBusObjectManager since that's where we store - * the pointer to the GDBusConnection. - */ - GHashTable *obj_managers; - - NMBusManager *manager; -} PrivateServer; - -typedef struct { - GDBusConnection *connection; - PrivateServer *server; - gboolean remote_peer_vanished; -} CloseConnectionInfo; - -static gboolean -close_connection_in_idle (gpointer user_data) -{ - CloseConnectionInfo *info = user_data; - PrivateServer *server = info->server; - GHashTableIter iter; - GDBusObjectManagerServer *manager; - - /* Emit this for the manager */ - g_signal_emit (server->manager, - signals[PRIVATE_CONNECTION_DISCONNECTED], - server->detail, - info->connection); - - /* FIXME: there's a bug (754730) in GLib for which the connection - * is marked as closed when the remote peer vanishes but its - * resources are not cleaned up. Work around it by explicitly - * closing the connection in that case. */ - if (info->remote_peer_vanished) - g_dbus_connection_close (info->connection, NULL, NULL, NULL); - - g_hash_table_iter_init (&iter, server->obj_managers); - while (g_hash_table_iter_next (&iter, (gpointer) &manager, NULL)) { - gs_unref_object GDBusConnection *connection = NULL; - - connection = g_dbus_object_manager_server_get_connection (manager); - if (connection == info->connection) { - g_hash_table_iter_remove (&iter); - break; - } - } - - g_object_unref (server->manager); - g_slice_free (CloseConnectionInfo, info); - - return G_SOURCE_REMOVE; -} - -static void -private_server_closed_connection (GDBusConnection *conn, - gboolean remote_peer_vanished, - GError *error, - gpointer user_data) -{ - PrivateServer *s = user_data; - CloseConnectionInfo *info; - - /* Clean up after the connection */ - _LOGD ("(%s) closed connection %p on private socket", s->tag, conn); - - info = g_slice_new0 (CloseConnectionInfo); - info->connection = conn; - info->server = s; - info->remote_peer_vanished = remote_peer_vanished; - - g_object_ref (s->manager); - - /* Delay the close of connection to ensure that D-Bus signals - * are handled */ - g_idle_add (close_connection_in_idle, info); -} - -static gboolean -private_server_new_connection (GDBusServer *server, - GDBusConnection *conn, - gpointer user_data) -{ - PrivateServer *s = user_data; - static guint32 counter = 0; - GDBusObjectManagerServer *manager; - char *sender; - - g_signal_connect (conn, "closed", G_CALLBACK (private_server_closed_connection), s); - - /* Fake a sender since private connections don't have one */ - sender = g_strdup_printf ("x:y:%d", counter++); - - manager = g_dbus_object_manager_server_new (OBJECT_MANAGER_SERVER_BASE_PATH); - g_dbus_object_manager_server_set_connection (manager, conn); - g_hash_table_insert (s->obj_managers, manager, sender); - - _LOGD ("(%s) accepted connection %p on private socket", s->tag, conn); - - /* Emit this for the manager. - * - * It is essential to do this from the "new-connection" signal handler, as - * at that point no messages from the connection are yet processed - * (which avoids races with registering objects). */ - g_signal_emit (s->manager, - signals[PRIVATE_CONNECTION_NEW], - s->detail, - conn, - manager); - return TRUE; -} - -static void -private_server_manager_destroy (GDBusObjectManagerServer *manager) -{ - GDBusConnection *connection = g_dbus_object_manager_server_get_connection (manager); - - if (!g_dbus_connection_is_closed (connection)) - g_dbus_connection_close (connection, NULL, NULL, NULL); - g_dbus_object_manager_server_set_connection (manager, NULL); - g_object_unref (manager); - g_object_unref (connection); -} - -static gboolean -private_server_authorize (GDBusAuthObserver *observer, - GIOStream *stream, - GCredentials *credentials, - gpointer user_data) -{ - return g_credentials_get_unix_user (credentials, NULL) == 0; -} - -static PrivateServer * -private_server_new (const char *path, - const char *tag, - NMBusManager *manager) -{ - PrivateServer *s; - GDBusAuthObserver *auth_observer; - GDBusServer *server; - GError *error = NULL; - char *address, *guid; - - unlink (path); - address = g_strdup_printf ("unix:path=%s", path); - - _LOGD ("(%s) creating private socket %s", tag, address); - - guid = g_dbus_generate_guid (); - auth_observer = g_dbus_auth_observer_new (); - g_signal_connect (auth_observer, "authorize-authenticated-peer", - G_CALLBACK (private_server_authorize), NULL); - server = g_dbus_server_new_sync (address, - G_DBUS_SERVER_FLAGS_NONE, - guid, - auth_observer, - NULL, &error); - g_free (guid); - g_object_unref (auth_observer); - - if (!server) { - _LOGW ("(%s) failed to set up private socket %s: %s", - tag, address, error->message); - g_error_free (error); - g_free (address); - return NULL; - } - - s = g_malloc0 (sizeof (*s)); - s->address = address; - s->server = server; - g_signal_connect (server, "new-connection", - G_CALLBACK (private_server_new_connection), s); - - s->obj_managers = g_hash_table_new_full (g_direct_hash, g_direct_equal, - (GDestroyNotify) private_server_manager_destroy, - g_free); - s->manager = manager; - s->detail = g_quark_from_string (tag); - s->tag = g_quark_to_string (s->detail); - - g_dbus_server_start (server); - - return s; -} - -static void -private_server_free (gpointer ptr) -{ - PrivateServer *s = ptr; - - unlink (s->address); - g_free (s->address); - g_hash_table_destroy (s->obj_managers); - - g_dbus_server_stop (s->server); - g_object_unref (s->server); - - memset (s, 0, sizeof (*s)); - g_free (s); -} - -void -nm_bus_manager_private_server_register (NMBusManager *self, - const char *path, - const char *tag) -{ - NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - PrivateServer *s; - GSList *iter; - - g_return_if_fail (self != NULL); - g_return_if_fail (path != NULL); - g_return_if_fail (tag != NULL); - - /* Only one instance per tag; but don't warn */ - for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) { - s = iter->data; - if (g_strcmp0 (tag, s->tag) == 0) - return; - } - - s = private_server_new (path, tag, self); - if (s) - priv->private_servers = g_slist_append (priv->private_servers, s); -} - -static const char * -private_server_get_connection_owner (PrivateServer *s, GDBusConnection *connection) -{ - GHashTableIter iter; - GDBusObjectManagerServer *manager; - const char *owner; - - g_return_val_if_fail (s != NULL, NULL); - g_return_val_if_fail (connection != NULL, NULL); - - g_hash_table_iter_init (&iter, s->obj_managers); - while (g_hash_table_iter_next (&iter, (gpointer) &manager, (gpointer) &owner)) { - gs_unref_object GDBusConnection *c = NULL; - - c = g_dbus_object_manager_server_get_connection (manager); - if (c == connection) - return owner; - } - return NULL; -} - -static GDBusConnection * -private_server_get_connection_by_owner (PrivateServer *s, const char *owner) -{ - GHashTableIter iter; - GDBusObjectManagerServer *manager; - const char *priv_sender; - - g_hash_table_iter_init (&iter, s->obj_managers); - while (g_hash_table_iter_next (&iter, (gpointer) &manager, (gpointer) &priv_sender)) { - if (g_strcmp0 (owner, priv_sender) == 0) - return g_dbus_object_manager_server_get_connection (manager); - } - return NULL; -} - -/*****************************************************************************/ - -static gboolean -_bus_get_unix_pid (NMBusManager *self, - const char *sender, - gulong *out_pid, - GError **error) -{ - guint32 unix_pid = G_MAXUINT32; - gs_unref_variant GVariant *ret = NULL; - - ret = _nm_dbus_proxy_call_sync (NM_BUS_MANAGER_GET_PRIVATE (self)->proxy, - "GetConnectionUnixProcessID", - g_variant_new ("(s)", sender), - G_VARIANT_TYPE ("(u)"), - G_DBUS_CALL_FLAGS_NONE, 2000, - NULL, error); - if (!ret) - return FALSE; - - g_variant_get (ret, "(u)", &unix_pid); - - *out_pid = (gulong) unix_pid; - return TRUE; -} - -static gboolean -_bus_get_unix_user (NMBusManager *self, - const char *sender, - gulong *out_user, - GError **error) -{ - guint32 unix_uid = G_MAXUINT32; - gs_unref_variant GVariant *ret = NULL; - - ret = _nm_dbus_proxy_call_sync (NM_BUS_MANAGER_GET_PRIVATE (self)->proxy, - "GetConnectionUnixUser", - g_variant_new ("(s)", sender), - G_VARIANT_TYPE ("(u)"), - G_DBUS_CALL_FLAGS_NONE, 2000, - NULL, error); - if (!ret) - return FALSE; - - g_variant_get (ret, "(u)", &unix_uid); - - *out_user = (gulong) unix_uid; - return TRUE; -} - -/** - * _get_caller_info(): - * - * Given a GDBus method invocation, or a GDBusConnection + GDBusMessage, - * return the sender and the UID of the sender. - */ -static gboolean -_get_caller_info (NMBusManager *self, - GDBusMethodInvocation *context, - GDBusConnection *connection, - GDBusMessage *message, - char **out_sender, - gulong *out_uid, - gulong *out_pid) -{ - NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - const char *sender; - GSList *iter; - - if (context) { - connection = g_dbus_method_invocation_get_connection (context); - - /* only bus connections will have a sender */ - sender = g_dbus_method_invocation_get_sender (context); - } else { - g_assert (message); - sender = g_dbus_message_get_sender (message); - } - g_assert (connection); - - if (!sender) { - /* Might be a private connection, for which we fake a sender */ - for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) { - PrivateServer *s = iter->data; - - sender = private_server_get_connection_owner (s, connection); - if (sender) { - if (out_uid) - *out_uid = 0; - if (out_sender) - *out_sender = g_strdup (sender); - if (out_pid) { - GCredentials *creds; - - creds = g_dbus_connection_get_peer_credentials (connection); - if (creds) { - pid_t pid; - - pid = g_credentials_get_unix_pid (creds, NULL); - if (pid == -1) - *out_pid = G_MAXULONG; - else - *out_pid = pid; - } else - *out_pid = G_MAXULONG; - } - return TRUE; - } - } - return FALSE; - } - - /* Bus connections always have a sender */ - g_assert (sender); - if (out_uid) { - if (!_bus_get_unix_user (self, sender, out_uid, NULL)) { - *out_uid = G_MAXULONG; - return FALSE; - } - } - - if (out_pid) { - if (!_bus_get_unix_pid (self, sender, out_pid, NULL)) { - *out_pid = G_MAXULONG; - return FALSE; - } - } - - if (out_sender) - *out_sender = g_strdup (sender); - - return TRUE; -} - -gboolean -nm_bus_manager_get_caller_info (NMBusManager *self, - GDBusMethodInvocation *context, - char **out_sender, - gulong *out_uid, - gulong *out_pid) -{ - return _get_caller_info (self, context, NULL, NULL, out_sender, out_uid, out_pid); -} - -gboolean -nm_bus_manager_get_caller_info_from_message (NMBusManager *self, - GDBusConnection *connection, - GDBusMessage *message, - char **out_sender, - gulong *out_uid, - gulong *out_pid) -{ - return _get_caller_info (self, NULL, connection, message, out_sender, out_uid, out_pid); -} - -/** - * nm_bus_manager_ensure_uid: - * - * @self: bus manager instance - * @context: D-Bus method invocation - * @uid: a user-id - * @error_domain: error domain to return on failure - * @error_code: error code to return on failure - * - * Retrieves the uid of the D-Bus method caller and - * checks that it matches @uid, unless @uid is G_MAXULONG. - * In case of failure the function returns FALSE and finishes - * handling the D-Bus method with an error. - * - * Returns: %TRUE if the check succeeded, %FALSE otherwise - */ -gboolean -nm_bus_manager_ensure_uid (NMBusManager *self, - GDBusMethodInvocation *context, - gulong uid, - GQuark error_domain, - int error_code) -{ - gulong caller_uid; - GError *error = NULL; - - g_return_val_if_fail (NM_IS_BUS_MANAGER (self), FALSE); - g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (context), FALSE); - - if (!nm_bus_manager_get_caller_info (self, context, NULL, &caller_uid, NULL)) { - error = g_error_new_literal (error_domain, - error_code, - "Unable to determine request UID."); - g_dbus_method_invocation_take_error (context, error); - return FALSE; - } - - if (uid != G_MAXULONG && caller_uid != uid) { - error = g_error_new_literal (error_domain, - error_code, - "Permission denied"); - g_dbus_method_invocation_take_error (context, error); - return FALSE; - } - - return TRUE; -} - -gboolean -nm_bus_manager_get_unix_user (NMBusManager *self, - const char *sender, - gulong *out_uid) -{ - NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - GSList *iter; - GError *error = NULL; - - g_return_val_if_fail (sender != NULL, FALSE); - g_return_val_if_fail (out_uid != NULL, FALSE); - - /* Check if it's a private connection sender, which we fake */ - for (iter = priv->private_servers; iter; iter = iter->next) { - gs_unref_object GDBusConnection *connection = NULL; - - connection = private_server_get_connection_by_owner (iter->data, sender); - if (connection) { - *out_uid = 0; - return TRUE; - } - } - - /* Otherwise, a bus connection */ - if (!_bus_get_unix_user (self, sender, out_uid, &error)) { - _LOGW ("failed to get unix user for dbus sender '%s': %s", - sender, error->message); - g_error_free (error); - return FALSE; - } - - return TRUE; -} - -/*****************************************************************************/ - -/* Only cleanup a specific dbus connection, not all our private data */ -static void -nm_bus_manager_cleanup (NMBusManager *self) -{ - NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - - g_clear_object (&priv->proxy); - - if (priv->connection) { - g_signal_handler_disconnect (priv->connection, priv->bus_closed_id); - priv->bus_closed_id = 0; - g_clear_object (&priv->connection); - } - - g_dbus_object_manager_server_set_connection (priv->obj_manager, NULL); - priv->started = FALSE; -} - -static gboolean -nm_bus_manager_reconnect (gpointer user_data) -{ - NMBusManager *self = NM_BUS_MANAGER (user_data); - NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - - g_assert (self != NULL); - - if (nm_bus_manager_init_bus (self)) { - if (nm_bus_manager_start_service (self)) { - _LOGI ("reconnected to the system bus"); - g_signal_emit (self, signals[DBUS_CONNECTION_CHANGED], - 0, priv->connection); - priv->reconnect_id = 0; - return FALSE; - } - } - - /* Try again */ - nm_bus_manager_cleanup (self); - return TRUE; -} - -static void -start_reconnection_timeout (NMBusManager *self) -{ - NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - - if (priv->reconnect_id) - g_source_remove (priv->reconnect_id); - - /* Schedule timeout for reconnection attempts */ - priv->reconnect_id = g_timeout_add_seconds (3, nm_bus_manager_reconnect, self); -} - -static void -closed_cb (GDBusConnection *connection, - gboolean remote_peer_vanished, - GError *error, - gpointer user_data) -{ - NMBusManager *self = NM_BUS_MANAGER (user_data); - - /* Clean up existing connection */ - _LOGW ("disconnected by the system bus"); - - nm_bus_manager_cleanup (self); - - g_signal_emit (G_OBJECT (self), signals[DBUS_CONNECTION_CHANGED], 0, NULL); - - start_reconnection_timeout (self); -} - -static gboolean -nm_bus_manager_init_bus (NMBusManager *self) -{ - NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - GError *error = NULL; - - if (priv->connection) { - _LOGW ("DBus Manager already has a valid connection"); - return FALSE; - } - - priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); - if (!priv->connection) { - /* Log with 'info' severity; there won't be a bus daemon in minimal - * environments (eg, initrd) where we only want to use the private - * socket. - */ - _LOGI ("could not connect to the system bus (%s); only the " - "private D-Bus socket will be available", - error->message); - g_error_free (error); - return FALSE; - } - - g_dbus_connection_set_exit_on_close (priv->connection, FALSE); - priv->bus_closed_id = g_signal_connect (priv->connection, "closed", - G_CALLBACK (closed_cb), self); - - priv->proxy = g_dbus_proxy_new_sync (priv->connection, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, - NULL, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - NULL, &error); - if (!priv->proxy) { - g_clear_object (&priv->connection); - _LOGW ("could not create org.freedesktop.DBus proxy (%s); only the " - "private D-Bus socket will be available", - error->message); - g_error_free (error); - return FALSE; - } - - g_dbus_object_manager_server_set_connection (priv->obj_manager, priv->connection); - return TRUE; -} - -/* Register our service on the bus; shouldn't be called until - * all necessary message handlers have been registered, because - * when we register on the bus, clients may start to call. - */ -gboolean -nm_bus_manager_start_service (NMBusManager *self) -{ - NMBusManagerPrivate *priv; - gs_unref_variant GVariant *ret = NULL; - int result; - GError *err = NULL; - - g_return_val_if_fail (NM_IS_BUS_MANAGER (self), FALSE); - - priv = NM_BUS_MANAGER_GET_PRIVATE (self); - - if (priv->started) { - _LOGE ("service has already started"); - return FALSE; - } - - /* Pointless to request a name when we aren't connected to the bus */ - if (!priv->proxy) - return FALSE; - - ret = _nm_dbus_proxy_call_sync (priv->proxy, - "RequestName", - g_variant_new ("(su)", - NM_DBUS_SERVICE, - DBUS_NAME_FLAG_DO_NOT_QUEUE), - G_VARIANT_TYPE ("(u)"), - G_DBUS_CALL_FLAGS_NONE, -1, - NULL, &err); - if (!ret) { - _LOGE ("could not acquire the NetworkManager service: '%s'", err->message); - g_error_free (err); - return FALSE; - } - - g_variant_get (ret, "(u)", &result); - - if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - _LOGE ("could not acquire the NetworkManager service as it is already taken"); - return FALSE; - } - - priv->started = TRUE; - return priv->started; -} - -GDBusConnection * -nm_bus_manager_get_connection (NMBusManager *self) -{ - g_return_val_if_fail (NM_IS_BUS_MANAGER (self), NULL); - - return NM_BUS_MANAGER_GET_PRIVATE (self)->connection; -} - -void -nm_bus_manager_register_object (NMBusManager *self, - GDBusObjectSkeleton *object) -{ - NMBusManagerPrivate *priv; - - g_return_if_fail (NM_IS_BUS_MANAGER (self)); - g_return_if_fail (NM_IS_EXPORTED_OBJECT (object)); - - priv = NM_BUS_MANAGER_GET_PRIVATE (self); - -#if NM_MORE_ASSERTS >= 1 -#if GLIB_CHECK_VERSION(2,34,0) - if (g_dbus_object_manager_server_is_exported (priv->obj_manager, object)) - g_return_if_reached (); -#endif -#endif - - g_dbus_object_manager_server_export (priv->obj_manager, object); -} - -GDBusObjectSkeleton * -nm_bus_manager_get_registered_object (NMBusManager *self, - const char *path) -{ - NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - - return G_DBUS_OBJECT_SKELETON (g_dbus_object_manager_get_object ((GDBusObjectManager *) priv->obj_manager, path)); -} - -void -nm_bus_manager_unregister_object (NMBusManager *self, - GDBusObjectSkeleton *object) -{ - NMBusManagerPrivate *priv; - gs_free char *path = NULL; - - g_return_if_fail (NM_IS_BUS_MANAGER (self)); - g_return_if_fail (NM_IS_EXPORTED_OBJECT (object)); - - priv = NM_BUS_MANAGER_GET_PRIVATE (self); - -#if NM_MORE_ASSERTS >= 1 -#if GLIB_CHECK_VERSION(2,34,0) - if (!g_dbus_object_manager_server_is_exported (priv->obj_manager, object)) - g_return_if_reached (); -#endif -#endif - - g_object_get (G_OBJECT (object), "g-object-path", &path, NULL); - g_return_if_fail (path != NULL); - - g_dbus_object_manager_server_unexport (priv->obj_manager, path); -} - -const char * -nm_bus_manager_connection_get_private_name (NMBusManager *self, - GDBusConnection *connection) -{ - NMBusManagerPrivate *priv; - GSList *iter; - const char *owner; - - g_return_val_if_fail (NM_IS_BUS_MANAGER (self), FALSE); - g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); - - if (g_dbus_connection_get_unique_name (connection)) { - /* Shortcut. The connection is not a private connection. */ - return NULL; - } - - priv = NM_BUS_MANAGER_GET_PRIVATE (self); - for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) { - PrivateServer *s = iter->data; - - if ((owner = private_server_get_connection_owner (s, connection))) - return owner; - } - g_return_val_if_reached (NULL); -} - -/** - * nm_bus_manager_new_proxy: - * @self: the #NMBusManager - * @connection: the GDBusConnection for which this connection should be created - * @proxy_type: the type of #GDBusProxy to create - * @name: any name on the message bus - * @path: name of the object instance to call methods on - * @iface: name of the interface to call methods on - * - * Creates a new proxy (of type @proxy_type) for a name on a given bus. Since - * the process which called the D-Bus method could be coming from a private - * connection or the system bus connection, different proxies must be created - * for each case. This function abstracts that. - * - * Returns: a #GDBusProxy capable of calling D-Bus methods of the calling process - */ -GDBusProxy * -nm_bus_manager_new_proxy (NMBusManager *self, - GDBusConnection *connection, - GType proxy_type, - const char *name, - const char *path, - const char *iface) -{ - const char *owner; - GDBusProxy *proxy; - GError *error = NULL; - - g_return_val_if_fail (g_type_is_a (proxy_type, G_TYPE_DBUS_PROXY), NULL); - g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); - - /* Might be a private connection, for which @name is fake */ - owner = nm_bus_manager_connection_get_private_name (self, connection); - if (owner) { - g_return_val_if_fail (!g_strcmp0 (owner, name), NULL); - name = NULL; - } - - proxy = g_initable_new (proxy_type, NULL, &error, - "g-connection", connection, - "g-flags", (G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS), - "g-name", name, - "g-object-path", path, - "g-interface-name", iface, - NULL); - if (!proxy) { - _LOGW ("could not create proxy for %s on connection %s: %s", - iface, name, error->message); - g_error_free (error); - } - return proxy; -} - -/*****************************************************************************/ - -static void -nm_bus_manager_init (NMBusManager *self) -{ - NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - - priv->obj_manager = g_dbus_object_manager_server_new (OBJECT_MANAGER_SERVER_BASE_PATH); -} - -static void -dispose (GObject *object) -{ - NMBusManager *self = NM_BUS_MANAGER (object); - NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self); - GList *exported, *iter; - - g_slist_free_full (priv->private_servers, private_server_free); - priv->private_servers = NULL; - - nm_bus_manager_cleanup (self); - - if (priv->obj_manager) { - /* The ObjectManager owns the last reference to many exported - * objects, and when that reference is dropped the objects unregister - * themselves via nm_bus_manager_unregister_object(). By that time - * priv->obj_manager is already NULL and that prints warnings. Unregister - * them before clearing the ObjectManager instead. - */ - exported = g_dbus_object_manager_get_objects ((GDBusObjectManager *) priv->obj_manager); - for (iter = exported; iter; iter = iter->next) { - nm_bus_manager_unregister_object (self, iter->data); - g_object_unref (iter->data); - } - g_list_free (exported); - g_clear_object (&priv->obj_manager); - } - - nm_clear_g_source (&priv->reconnect_id); - - G_OBJECT_CLASS (nm_bus_manager_parent_class)->dispose (object); -} - -static void -nm_bus_manager_class_init (NMBusManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = dispose; - - signals[DBUS_CONNECTION_CHANGED] = - g_signal_new (NM_BUS_MANAGER_DBUS_CONNECTION_CHANGED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 1, G_TYPE_POINTER); - - signals[PRIVATE_CONNECTION_NEW] = - g_signal_new (NM_BUS_MANAGER_PRIVATE_CONNECTION_NEW, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 2, G_TYPE_DBUS_CONNECTION, G_TYPE_DBUS_OBJECT_MANAGER_SERVER); - - signals[PRIVATE_CONNECTION_DISCONNECTED] = - g_signal_new (NM_BUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 1, G_TYPE_POINTER); -} - - - diff --git a/src/nm-bus-manager.h b/src/nm-bus-manager.h deleted file mode 100644 index 153f7cdc..00000000 --- a/src/nm-bus-manager.h +++ /dev/null @@ -1,93 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2006 - 2008 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#ifndef __NM_BUS_MANAGER_H__ -#define __NM_BUS_MANAGER_H__ - -#define NM_TYPE_BUS_MANAGER (nm_bus_manager_get_type ()) -#define NM_BUS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_BUS_MANAGER, NMBusManager)) -#define NM_BUS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_BUS_MANAGER, NMBusManagerClass)) -#define NM_IS_BUS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NM_TYPE_BUS_MANAGER)) -#define NM_IS_BUS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NM_TYPE_BUS_MANAGER)) -#define NM_BUS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_BUS_MANAGER, NMBusManagerClass)) - -#define NM_BUS_MANAGER_DBUS_CONNECTION_CHANGED "dbus-connection-changed" -#define NM_BUS_MANAGER_PRIVATE_CONNECTION_NEW "private-connection-new" -#define NM_BUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "private-connection-disconnected" - -typedef struct _NMBusManagerClass NMBusManagerClass; - -GType nm_bus_manager_get_type (void); - -NMBusManager * nm_bus_manager_get (void); -void nm_bus_manager_setup (NMBusManager *instance); - -gboolean nm_bus_manager_start_service (NMBusManager *self); - -GDBusConnection * nm_bus_manager_get_connection (NMBusManager *self); - -gboolean nm_bus_manager_get_caller_info (NMBusManager *self, - GDBusMethodInvocation *context, - char **out_sender, - gulong *out_uid, - gulong *out_pid); - -gboolean nm_bus_manager_ensure_uid (NMBusManager *self, - GDBusMethodInvocation *context, - gulong uid, - GQuark error_domain, - int error_code); - -const char *nm_bus_manager_connection_get_private_name (NMBusManager *self, - GDBusConnection *connection); - -gboolean nm_bus_manager_get_unix_user (NMBusManager *self, - const char *sender, - gulong *out_uid); - -gboolean nm_bus_manager_get_caller_info_from_message (NMBusManager *self, - GDBusConnection *connection, - GDBusMessage *message, - char **out_sender, - gulong *out_uid, - gulong *out_pid); - -void nm_bus_manager_register_object (NMBusManager *self, - GDBusObjectSkeleton *object); - -void nm_bus_manager_unregister_object (NMBusManager *self, - GDBusObjectSkeleton *object); - -GDBusObjectSkeleton *nm_bus_manager_get_registered_object (NMBusManager *self, - const char *path); - -void nm_bus_manager_private_server_register (NMBusManager *self, - const char *path, - const char *tag); - -GDBusProxy *nm_bus_manager_new_proxy (NMBusManager *self, - GDBusConnection *connection, - GType proxy_type, - const char *name, - const char *path, - const char *iface); - -#endif /* __NM_BUS_MANAGER_H__ */ diff --git a/src/nm-checkpoint-manager.c b/src/nm-checkpoint-manager.c index 6da220c4..7e8c8aea 100644 --- a/src/nm-checkpoint-manager.c +++ b/src/nm-checkpoint-manager.c @@ -26,16 +26,16 @@ #include "nm-connection.h" #include "nm-core-utils.h" #include "devices/nm-device.h" -#include "nm-exported-object.h" #include "nm-manager.h" #include "nm-utils.h" +#include "c-list/src/c-list.h" /*****************************************************************************/ struct _NMCheckpointManager { NMManager *_manager; - GHashTable *checkpoints; - guint rollback_timeout_id; + GParamSpec *property_spec; + CList checkpoints_lst_head; }; #define GET_MANAGER(self) \ @@ -56,80 +56,67 @@ struct _NMCheckpointManager { /*****************************************************************************/ -static void update_rollback_timeout (NMCheckpointManager *self); - static void -checkpoint_destroy (gpointer checkpoint) -{ - nm_exported_object_unexport (NM_EXPORTED_OBJECT (checkpoint)); - g_object_unref (G_OBJECT (checkpoint)); +notify_checkpoints (NMCheckpointManager *self) { + g_object_notify_by_pspec ((GObject *) GET_MANAGER (self), + self->property_spec); } -static gboolean -rollback_timeout_cb (NMCheckpointManager *self) +static void +destroy_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint, gboolean log_destroy) { - NMCheckpoint *checkpoint; - GHashTableIter iter; - GVariant *result; - gint64 ts, now; - - now = nm_utils_get_monotonic_timestamp_ms (); - - g_hash_table_iter_init (&iter, self->checkpoints); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &checkpoint)) { - ts = nm_checkpoint_get_rollback_ts (checkpoint); - if (ts && ts <= now) { - result = nm_checkpoint_rollback (checkpoint); - if (result) - g_variant_unref (result); - g_hash_table_iter_remove (&iter); - } - } + nm_assert (NM_IS_CHECKPOINT (checkpoint)); + nm_assert (nm_dbus_object_is_exported (NM_DBUS_OBJECT (checkpoint))); + nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst)); + + nm_checkpoint_set_timeout_callback (checkpoint, NULL, NULL); + + c_list_unlink (&checkpoint->checkpoints_lst); - self->rollback_timeout_id = 0; - update_rollback_timeout (self); + if (log_destroy) + nm_checkpoint_log_destroy (checkpoint); - return G_SOURCE_REMOVE; + notify_checkpoints (self); + + nm_dbus_object_unexport (NM_DBUS_OBJECT (checkpoint)); + g_object_unref (checkpoint); } -static void -update_rollback_timeout (NMCheckpointManager *self) +static GVariant * +rollback_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint) { - NMCheckpoint *checkpoint; - GHashTableIter iter; - gint64 ts, delta, next = G_MAXINT64; - - g_hash_table_iter_init (&iter, self->checkpoints); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &checkpoint)) { - ts = nm_checkpoint_get_rollback_ts (checkpoint); - if (ts && ts < next) - next = ts; - } + GVariant *result; + const CList *iter; - nm_clear_g_source (&self->rollback_timeout_id); + nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst)); - if (next != G_MAXINT64) { - delta = MAX (next - nm_utils_get_monotonic_timestamp_ms (), 0); - self->rollback_timeout_id = g_timeout_add (delta, - (GSourceFunc) rollback_timeout_cb, - self); - _LOGT ("update timeout: next check in %" G_GINT64_FORMAT " ms", delta); + /* we destroy first all overlapping checkpoints that are younger/newer. */ + for (iter = checkpoint->checkpoints_lst.next; + iter != &self->checkpoints_lst_head; + ) { + NMCheckpoint *cp = c_list_entry (iter, NMCheckpoint, checkpoints_lst); + + iter = iter->next; + if (nm_checkpoint_includes_devices_of (cp, checkpoint)) { + /* the younger checkpoint has overlapping devices and gets obsoleted. + * Destroy it. */ + destroy_checkpoint (self, cp, TRUE); + } } + + result = nm_checkpoint_rollback (checkpoint); + destroy_checkpoint (self, checkpoint, FALSE); + return result; } -static NMCheckpoint * -find_checkpoint_for_device (NMCheckpointManager *self, NMDevice *device) +static void +rollback_timeout_cb (NMCheckpoint *checkpoint, + gpointer user_data) { - GHashTableIter iter; - NMCheckpoint *checkpoint; + NMCheckpointManager *self = user_data; + gs_unref_variant GVariant *result = NULL; - g_hash_table_iter_init (&iter, self->checkpoints); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &checkpoint)) { - if (nm_checkpoint_includes_device (checkpoint, device)) - return checkpoint; - } - - return NULL; + result = rollback_checkpoint (self, checkpoint); } NMCheckpoint * @@ -141,152 +128,200 @@ nm_checkpoint_manager_create (NMCheckpointManager *self, { NMManager *manager; NMCheckpoint *checkpoint; - const char * const *path; gs_unref_ptrarray GPtrArray *devices = NULL; NMDevice *device; - const char *checkpoint_path; - gs_free const char **device_paths_free = NULL; - guint i; g_return_val_if_fail (self, FALSE); g_return_val_if_fail (!error || !*error, FALSE); manager = GET_MANAGER (self); + devices = g_ptr_array_new (); + if (!device_paths || !device_paths[0]) { - const char *device_path; - const GSList *iter; - GPtrArray *paths; - - paths = g_ptr_array_new (); - for (iter = nm_manager_get_devices (manager); - iter; - iter = g_slist_next (iter)) { - device = NM_DEVICE (iter->data); + const CList *tmp_lst; + + nm_manager_for_each_device (manager, device, tmp_lst) { + /* FIXME: there is no strong reason to skip over unrealized devices. + * Also, NMCheckpoint anticipates to handle them (in parts). */ if (!nm_device_is_real (device)) continue; - device_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device)); - if (device_path) - g_ptr_array_add (paths, (gpointer) device_path); + nm_assert (nm_dbus_object_get_path (NM_DBUS_OBJECT (device))); + g_ptr_array_add (devices, device); } - g_ptr_array_add (paths, NULL); - device_paths_free = (const char **) g_ptr_array_free (paths, FALSE); - device_paths = (const char *const *) device_paths_free; } else if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES)) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS, "the DISCONNECT_NEW_DEVICES flag can only be used with an empty device list"); return NULL; + } else { + for (; *device_paths; device_paths++) { + device = nm_manager_get_device_by_path (manager, *device_paths); + if (!device) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "device %s does not exist", *device_paths); + return NULL; + } + if (!nm_device_is_real (device)) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "device %s is not realized", *device_paths); + return NULL; + } + g_ptr_array_add (devices, device); + } } - devices = g_ptr_array_new (); - for (path = device_paths; *path; path++) { - device = nm_manager_get_device_by_path (manager, *path); - if (!device) { - g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "device %s does not exist", *path); - return NULL; - } - g_ptr_array_add (devices, device); + if (!devices->len) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_INVALID_ARGUMENTS, + "no device available"); + return NULL; } - if (!NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL)) { - for (i = 0; i < devices->len; i++) { - device = devices->pdata[i]; - checkpoint = find_checkpoint_for_device (self, device); - if (checkpoint) { + if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL)) + nm_checkpoint_manager_destroy_all (self); + else if (!NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING)) { + c_list_for_each_entry (checkpoint, &self->checkpoints_lst_head, checkpoints_lst) { + device = nm_checkpoint_includes_devices (checkpoint, (NMDevice *const*) devices->pdata, devices->len); + if (device) { g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS, "device '%s' is already included in checkpoint %s", nm_device_get_iface (device), - nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint))); + nm_dbus_object_get_path (NM_DBUS_OBJECT (checkpoint))); return NULL; } } } - checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, flags, error); - if (!checkpoint) - return NULL; - - if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL)) - g_hash_table_remove_all (self->checkpoints); - - nm_exported_object_export (NM_EXPORTED_OBJECT (checkpoint)); - checkpoint_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint)); + checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, flags); - if (!nm_g_hash_table_insert (self->checkpoints, - (gpointer) checkpoint_path, - checkpoint)) - g_return_val_if_reached (NULL); - - update_rollback_timeout (self); + nm_dbus_object_export (NM_DBUS_OBJECT (checkpoint)); + nm_checkpoint_set_timeout_callback (checkpoint, rollback_timeout_cb, self); + c_list_link_tail (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst); + notify_checkpoints (self); return checkpoint; } -gboolean -nm_checkpoint_manager_destroy_all (NMCheckpointManager *self, - GError **error) +void +nm_checkpoint_manager_destroy_all (NMCheckpointManager *self) { - g_return_val_if_fail (self, FALSE); + NMCheckpoint *checkpoint; - g_hash_table_remove_all (self->checkpoints); + g_return_if_fail (self); - return TRUE; + while ((checkpoint = c_list_first_entry (&self->checkpoints_lst_head, NMCheckpoint, checkpoints_lst))) + destroy_checkpoint (self, checkpoint, TRUE); } gboolean nm_checkpoint_manager_destroy (NMCheckpointManager *self, - const char *checkpoint_path, + const char *path, GError **error) { - gboolean ret; + NMCheckpoint *checkpoint; g_return_val_if_fail (self, FALSE); - g_return_val_if_fail (checkpoint_path && checkpoint_path[0] == '/', FALSE); + g_return_val_if_fail (path && path[0] == '/', FALSE); g_return_val_if_fail (!error || !*error, FALSE); - if (!nm_streq (checkpoint_path, "/")) { - ret = g_hash_table_remove (self->checkpoints, checkpoint_path); - if (!ret) { - g_set_error (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_INVALID_ARGUMENTS, - "checkpoint %s does not exist", checkpoint_path); - } - return ret; - } else - return nm_checkpoint_manager_destroy_all (self, error); + if (nm_streq (path, "/")) { + nm_checkpoint_manager_destroy_all (self); + return TRUE; + } + + checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error); + if (!checkpoint) + return FALSE; + + destroy_checkpoint (self, checkpoint, TRUE); + return TRUE; } gboolean nm_checkpoint_manager_rollback (NMCheckpointManager *self, - const char *checkpoint_path, + const char *path, GVariant **results, GError **error) { - NMCheckpoint *cp; + NMCheckpoint *checkpoint; g_return_val_if_fail (self, FALSE); - g_return_val_if_fail (checkpoint_path && checkpoint_path[0] == '/', FALSE); + g_return_val_if_fail (path && path[0] == '/', FALSE); g_return_val_if_fail (results, FALSE); g_return_val_if_fail (!error || !*error, FALSE); - cp = g_hash_table_lookup (self->checkpoints, checkpoint_path); - if (!cp) { - g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, - "checkpoint %s does not exist", checkpoint_path); + checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error); + if (!checkpoint) return FALSE; + + *results = rollback_checkpoint (self, checkpoint); + return TRUE; +} + +NMCheckpoint * +nm_checkpoint_manager_lookup_by_path (NMCheckpointManager *self, const char *path, GError **error) +{ + NMCheckpoint *checkpoint; + + g_return_val_if_fail (self, NULL); + + checkpoint = (NMCheckpoint *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (GET_MANAGER (self))), + path); + if ( !checkpoint + || !NM_IS_CHECKPOINT (checkpoint)) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS, + "checkpoint %s does not exist", path); + return NULL; } - *results = nm_checkpoint_rollback (cp); - g_hash_table_remove (self->checkpoints, checkpoint_path); + nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst)); + return checkpoint; +} + +const char ** +nm_checkpoint_manager_get_checkpoint_paths (NMCheckpointManager *self, guint *out_length) +{ + NMCheckpoint *checkpoint; + const char **strv; + guint num, i = 0; + num = c_list_length (&self->checkpoints_lst_head); + NM_SET_OUT (out_length, num); + if (!num) + return NULL; + + strv = g_new (const char *, num + 1); + c_list_for_each_entry (checkpoint, &self->checkpoints_lst_head, checkpoints_lst) + strv[i++] = nm_dbus_object_get_path (NM_DBUS_OBJECT (checkpoint)); + nm_assert (i == num); + strv[i] = NULL; + return strv; +} + +gboolean +nm_checkpoint_manager_adjust_rollback_timeout (NMCheckpointManager *self, + const char *path, + guint32 add_timeout, + GError **error) +{ + NMCheckpoint *checkpoint; + + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (path && path[0] == '/', FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + + checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error); + if (!checkpoint) + return FALSE; + + nm_checkpoint_adjust_rollback_timeout (checkpoint, add_timeout); return TRUE; } /*****************************************************************************/ NMCheckpointManager * -nm_checkpoint_manager_new (NMManager *manager) +nm_checkpoint_manager_new (NMManager *manager, GParamSpec *spec) { NMCheckpointManager *self; @@ -301,20 +336,17 @@ nm_checkpoint_manager_new (NMManager *manager) * of NMManager shall surpass the lifetime of the NMCheckpointManager * instance. */ self->_manager = manager; - self->checkpoints = g_hash_table_new_full (nm_str_hash, g_str_equal, - NULL, checkpoint_destroy); - + self->property_spec = spec; + c_list_init (&self->checkpoints_lst_head); return self; } void -nm_checkpoint_manager_unref (NMCheckpointManager *self) +nm_checkpoint_manager_free (NMCheckpointManager *self) { if (!self) return; - nm_clear_g_source (&self->rollback_timeout_id); - g_hash_table_destroy (self->checkpoints); - + nm_checkpoint_manager_destroy_all (self); g_slice_free (NMCheckpointManager, self); } diff --git a/src/nm-checkpoint-manager.h b/src/nm-checkpoint-manager.h index 30e49041..ca66ef16 100644 --- a/src/nm-checkpoint-manager.h +++ b/src/nm-checkpoint-manager.h @@ -27,8 +27,13 @@ typedef struct _NMCheckpointManager NMCheckpointManager; -NMCheckpointManager *nm_checkpoint_manager_new (NMManager *manager); -void nm_checkpoint_manager_unref (NMCheckpointManager *self); +NMCheckpointManager *nm_checkpoint_manager_new (NMManager *manager, GParamSpec *spec); + +void nm_checkpoint_manager_free (NMCheckpointManager *self); + +NMCheckpoint *nm_checkpoint_manager_lookup_by_path (NMCheckpointManager *self, + const char *path, + GError **error); NMCheckpoint *nm_checkpoint_manager_create (NMCheckpointManager *self, const char *const*device_names, @@ -36,15 +41,22 @@ NMCheckpoint *nm_checkpoint_manager_create (NMCheckpointManager *self, NMCheckpointCreateFlags flags, GError **error); -gboolean nm_checkpoint_manager_destroy_all (NMCheckpointManager *self, - GError **error); +void nm_checkpoint_manager_destroy_all (NMCheckpointManager *self); gboolean nm_checkpoint_manager_destroy (NMCheckpointManager *self, - const char *checkpoint_path, + const char *path, GError **error); gboolean nm_checkpoint_manager_rollback (NMCheckpointManager *self, - const char *checkpoint_path, + const char *path, GVariant **results, GError **error); +gboolean nm_checkpoint_manager_adjust_rollback_timeout (NMCheckpointManager *self, + const char *path, + guint32 add_timeout, + GError **error); + +const char **nm_checkpoint_manager_get_checkpoint_paths (NMCheckpointManager *self, + guint *out_length); + #endif /* __NM_CHECKPOINT_MANAGER_H__ */ diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c index bc57d449..a17f7eda 100644 --- a/src/nm-checkpoint.c +++ b/src/nm-checkpoint.c @@ -25,6 +25,7 @@ #include #include "nm-active-connection.h" +#include "nm-act-request.h" #include "nm-auth-subject.h" #include "nm-core-utils.h" #include "nm-dbus-interface.h" @@ -34,7 +35,6 @@ #include "settings/nm-settings-connection.h" #include "nm-simple-connection.h" #include "nm-utils.h" -#include "introspection/org.freedesktop.NetworkManager.Checkpoint.h" /*****************************************************************************/ @@ -50,36 +50,35 @@ typedef struct { NMActivationReason activation_reason; } DeviceCheckpoint; -NM_GOBJECT_PROPERTIES_DEFINE_BASE ( +NM_GOBJECT_PROPERTIES_DEFINE (NMCheckpoint, PROP_DEVICES, PROP_CREATED, PROP_ROLLBACK_TIMEOUT, ); -typedef struct { +struct _NMCheckpointPrivate { /* properties */ GHashTable *devices; - gint64 created; - guint32 rollback_timeout; + gint64 created_at_ms; + guint32 rollback_timeout_s; + guint timeout_id; + /* private members */ /* private members */ NMManager *manager; - gint64 rollback_ts; NMCheckpointCreateFlags flags; GHashTable *connection_uuids; -} NMCheckpointPrivate; -struct _NMCheckpoint { - NMExportedObject parent; - NMCheckpointPrivate _priv; + NMCheckpointTimeoutCallback timeout_cb; + gpointer timeout_data; }; struct _NMCheckpointClass { - NMExportedObjectClass parent; + NMDBusObjectClass parent; }; -G_DEFINE_TYPE (NMCheckpoint, nm_checkpoint, NM_TYPE_EXPORTED_OBJECT) +G_DEFINE_TYPE (NMCheckpoint, nm_checkpoint, NM_TYPE_DBUS_OBJECT) -#define NM_CHECKPOINT_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMCheckpoint, NM_IS_CHECKPOINT) +#define NM_CHECKPOINT_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMCheckpoint, NM_IS_CHECKPOINT) /*****************************************************************************/ @@ -103,20 +102,53 @@ G_DEFINE_TYPE (NMCheckpoint, nm_checkpoint, NM_TYPE_EXPORTED_OBJECT) /*****************************************************************************/ -guint64 -nm_checkpoint_get_rollback_ts (NMCheckpoint *self) +void +nm_checkpoint_log_destroy (NMCheckpoint *self) { - g_return_val_if_fail (NM_IS_CHECKPOINT (self), 0); + _LOGI ("destroy %s", nm_dbus_object_get_path (NM_DBUS_OBJECT (self))); +} - return NM_CHECKPOINT_GET_PRIVATE (self)->rollback_ts; +void +nm_checkpoint_set_timeout_callback (NMCheckpoint *self, + NMCheckpointTimeoutCallback callback, + gpointer user_data) +{ + NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self); + + /* in glib world, we would have a GSignal for this. But as there + * is only one subscriber, it's simpler to just set and unset(!) + * the callback this way. */ + priv->timeout_cb = callback; + priv->timeout_data = user_data; } -gboolean -nm_checkpoint_includes_device (NMCheckpoint *self, NMDevice *device) +NMDevice * +nm_checkpoint_includes_devices (NMCheckpoint *self, NMDevice *const*devices, guint n_devices) { NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self); + guint i; - return g_hash_table_contains (priv->devices, device); + for (i = 0; i < n_devices; i++) { + if (g_hash_table_contains (priv->devices, devices[i])) + return devices[i]; + } + return NULL; +} + +NMDevice * +nm_checkpoint_includes_devices_of (NMCheckpoint *self, NMCheckpoint *cp_for_devices) +{ + NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self); + NMCheckpointPrivate *priv2 = NM_CHECKPOINT_GET_PRIVATE (cp_for_devices); + GHashTableIter iter; + NMDevice *device; + + g_hash_table_iter_init (&iter, priv2->devices); + while (g_hash_table_iter_next (&iter, (gpointer *) &device, NULL)) { + if (g_hash_table_contains (priv->devices, device)) + return device; + } + return NULL; } static NMSettingsConnection * @@ -184,7 +216,7 @@ nm_checkpoint_rollback (NMCheckpoint *self) GError *local_error = NULL; GVariantBuilder builder; - _LOGI ("rollback of %s", nm_exported_object_get_path ((NMExportedObject *) self)); + _LOGI ("rollback of %s", nm_dbus_object_get_path (NM_DBUS_OBJECT (self))); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{su}")); /* Start rolling-back each device */ @@ -306,7 +338,7 @@ activate: &local_error)) { _LOGW ("rollback: reactivation of connection %s/%s failed: %s", nm_connection_get_id ((NMConnection *) connection), - nm_connection_get_uuid ((NMConnection * ) connection), + nm_connection_get_uuid ((NMConnection *) connection), local_error->message); g_clear_error (&local_error); result = NM_ROLLBACK_RESULT_ERR_FAILED; @@ -351,21 +383,19 @@ next_dev: } if (NM_FLAGS_HAS (priv->flags, NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES)) { - const GSList *list; + const CList *tmp_lst; NMDeviceState state; - NMDevice *dev; - - for (list = nm_manager_get_devices (priv->manager); list ; list = g_slist_next (list)) { - dev = list->data; - if (!g_hash_table_contains (priv->devices, dev)) { - state = nm_device_get_state (dev); - if ( state > NM_DEVICE_STATE_DISCONNECTED - && state < NM_DEVICE_STATE_DEACTIVATING) { - _LOGD ("rollback: disconnecting new device %s", nm_device_get_iface (dev)); - nm_device_state_changed (dev, - NM_DEVICE_STATE_DEACTIVATING, - NM_DEVICE_STATE_REASON_USER_REQUESTED); - } + + nm_manager_for_each_device (priv->manager, device, tmp_lst) { + if (g_hash_table_contains (priv->devices, device)) + continue; + state = nm_device_get_state (device); + if ( state > NM_DEVICE_STATE_DISCONNECTED + && state < NM_DEVICE_STATE_DEACTIVATING) { + _LOGD ("rollback: disconnecting new device %s", nm_device_get_iface (device)); + nm_device_state_changed (device, + NM_DEVICE_STATE_DEACTIVATING, + NM_DEVICE_STATE_REASON_USER_REQUESTED); } } @@ -375,8 +405,7 @@ next_dev: } static DeviceCheckpoint * -device_checkpoint_create (NMDevice *device, - GError **error) +device_checkpoint_create (NMDevice *device) { DeviceCheckpoint *dev_checkpoint; NMConnection *applied_connection; @@ -384,7 +413,10 @@ device_checkpoint_create (NMDevice *device, const char *path; NMActRequest *act_request; - path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device)); + nm_assert (NM_IS_DEVICE (device)); + nm_assert (nm_device_is_real (device)); + + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (device)); dev_checkpoint = g_slice_new0 (DeviceCheckpoint); dev_checkpoint->device = g_object_ref (device); @@ -393,25 +425,21 @@ device_checkpoint_create (NMDevice *device, dev_checkpoint->realized = nm_device_is_real (device); if (nm_device_get_unmanaged_mask (device, NM_UNMANAGED_USER_EXPLICIT)) { - dev_checkpoint->unmanaged_explicit = - !!nm_device_get_unmanaged_flags (device, NM_UNMANAGED_USER_EXPLICIT); + dev_checkpoint->unmanaged_explicit = !!nm_device_get_unmanaged_flags (device, + NM_UNMANAGED_USER_EXPLICIT); } else dev_checkpoint->unmanaged_explicit = NM_UNMAN_FLAG_OP_FORGET; - applied_connection = nm_device_get_applied_connection (device); - if (applied_connection) { - dev_checkpoint->applied_connection = - nm_simple_connection_new_clone (applied_connection); + act_request = nm_device_get_act_request (device); + if (act_request) { + settings_connection = nm_act_request_get_settings_connection (act_request); + applied_connection = nm_act_request_get_applied_connection (act_request); - settings_connection = nm_device_get_settings_connection (device); - g_return_val_if_fail (settings_connection, NULL); + dev_checkpoint->applied_connection = nm_simple_connection_new_clone (applied_connection); dev_checkpoint->settings_connection = - nm_simple_connection_new_clone (NM_CONNECTION (settings_connection)); - - act_request = nm_device_get_act_request (device); - g_return_val_if_fail (act_request, NULL); + nm_simple_connection_new_clone (NM_CONNECTION (settings_connection)); dev_checkpoint->ac_version_id = - nm_active_connection_version_id_get (NM_ACTIVE_CONNECTION (act_request)); + nm_active_connection_version_id_get (NM_ACTIVE_CONNECTION (act_request)); dev_checkpoint->activation_reason = nm_active_connection_get_activation_reason (NM_ACTIVE_CONNECTION (act_request)); } @@ -432,6 +460,57 @@ device_checkpoint_destroy (gpointer data) g_slice_free (DeviceCheckpoint, dev_checkpoint); } +static gboolean +_timeout_cb (gpointer user_data) +{ + NMCheckpoint *self = user_data; + NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self); + + priv->timeout_id = 0; + + if (priv->timeout_cb) + priv->timeout_cb (self, priv->timeout_data); + + /* beware, @self likely got destroyed! */ + return G_SOURCE_REMOVE; +} + +void +nm_checkpoint_adjust_rollback_timeout (NMCheckpoint *self, guint32 add_timeout) +{ + guint32 rollback_timeout_s; + gint64 now_ms, add_timeout_ms, rollback_timeout_ms; + + NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self); + + nm_clear_g_source (&priv->timeout_id); + + if (add_timeout == 0) + rollback_timeout_s = 0; + else { + now_ms = nm_utils_get_monotonic_timestamp_ms (); + add_timeout_ms = ((gint64) add_timeout) * 1000; + rollback_timeout_ms = (now_ms - priv->created_at_ms) + add_timeout_ms; + + /* round to nearest integer second. Since NM_CHECKPOINT_ROLLBACK_TIMEOUT is + * in units seconds, it will be able to exactly express the timeout. */ + rollback_timeout_s = NM_MIN ((rollback_timeout_ms + 500) / 1000, (gint64) G_MAXUINT32); + + /* we expect the timeout to be positive, because add_timeout_ms is positive. + * We cannot accept a zero, because it means "infinity". */ + nm_assert (rollback_timeout_s > 0); + + priv->timeout_id = g_timeout_add (NM_MIN (add_timeout_ms, (gint64) G_MAXUINT32), + _timeout_cb, + self); + } + + if (rollback_timeout_s != priv->rollback_timeout_s) { + priv->rollback_timeout_s = rollback_timeout_s; + _notify (self, PROP_ROLLBACK_TIMEOUT); + } +} + /*****************************************************************************/ static void @@ -440,22 +519,20 @@ get_property (GObject *object, guint prop_id, { NMCheckpoint *self = NM_CHECKPOINT (object); NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self); - gs_free_slist GSList *devices = NULL; - GHashTableIter iter; - NMDevice *device; switch (prop_id) { case PROP_DEVICES: - g_hash_table_iter_init (&iter, priv->devices); - while (g_hash_table_iter_next (&iter, (gpointer *) &device, NULL)) - devices = g_slist_append (devices, device); - nm_utils_g_value_set_object_path_array (value, devices, NULL, NULL); + nm_dbus_utils_g_value_set_object_path_from_hash (value, + priv->devices, + FALSE); break; case PROP_CREATED: - g_value_set_int64 (value, priv->created); + g_value_set_int64 (value, + nm_utils_monotonic_timestamp_as_boottime (priv->created_at_ms, + NM_UTILS_NS_PER_MSEC)); break; case PROP_ROLLBACK_TIMEOUT: - g_value_set_uint (value, priv->rollback_timeout); + g_value_set_uint (value, priv->rollback_timeout_s); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -468,47 +545,47 @@ get_property (GObject *object, guint prop_id, static void nm_checkpoint_init (NMCheckpoint *self) { - NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self); + NMCheckpointPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_CHECKPOINT, NMCheckpointPrivate); - priv->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal, + self->_priv = priv; + + c_list_init (&self->checkpoints_lst); + + priv->devices = g_hash_table_new_full (nm_direct_hash, NULL, NULL, device_checkpoint_destroy); } NMCheckpoint * -nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout, - NMCheckpointCreateFlags flags, GError **error) +nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout_s, + NMCheckpointCreateFlags flags) { NMCheckpoint *self; NMCheckpointPrivate *priv; NMSettingsConnection *const *con; - DeviceCheckpoint *dev_checkpoint; - NMDevice *device; + gint64 rollback_timeout_ms; guint i; g_return_val_if_fail (manager, NULL); g_return_val_if_fail (devices, NULL); - g_return_val_if_fail (!error || !*error, NULL); - - if (!devices->len) { - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_INVALID_ARGUMENTS, - "no device available"); - return NULL; - } + g_return_val_if_fail (devices->len > 0, NULL); self = g_object_new (NM_TYPE_CHECKPOINT, NULL); priv = NM_CHECKPOINT_GET_PRIVATE (self); priv->manager = manager; - priv->created = nm_utils_monotonic_timestamp_as_boottime (nm_utils_get_monotonic_timestamp_ms (), - NM_UTILS_NS_PER_MSEC); - priv->rollback_timeout = rollback_timeout; - priv->rollback_ts = rollback_timeout ? - (nm_utils_get_monotonic_timestamp_ms () + ((gint64) rollback_timeout * 1000)) : - 0; + priv->rollback_timeout_s = rollback_timeout_s; + priv->created_at_ms = nm_utils_get_monotonic_timestamp_ms (); priv->flags = flags; + if (rollback_timeout_s != 0) { + rollback_timeout_ms = ((gint64) rollback_timeout_s) * 1000; + priv->timeout_id = g_timeout_add (NM_MIN (rollback_timeout_ms, (gint64) G_MAXUINT32), + _timeout_cb, + self); + } + if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) { priv->connection_uuids = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); for (con = nm_settings_get_connections (nm_settings_get (), NULL); *con; con++) { @@ -518,13 +595,15 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time } for (i = 0; i < devices->len; i++) { - device = (NMDevice *) devices->pdata[i]; - dev_checkpoint = device_checkpoint_create (device, error); - if (!dev_checkpoint) { - g_object_unref (self); - return NULL; - } - g_hash_table_insert (priv->devices, device, dev_checkpoint); + NMDevice *device = devices->pdata[i]; + + /* FIXME: as long as the check point instance exists, it won't let go + * of the device. That is a bug, for example, if you have a ethernet + * device that gets removed (rmmod), the checkpoint will reference + * a non-existing D-Bus path of a device. */ + g_hash_table_insert (priv->devices, + device, + device_checkpoint_create (device)); } return self; @@ -536,20 +615,41 @@ dispose (GObject *object) NMCheckpoint *self = NM_CHECKPOINT (object); NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self); + nm_assert (c_list_is_empty (&self->checkpoints_lst)); + g_clear_pointer (&priv->devices, g_hash_table_unref); g_clear_pointer (&priv->connection_uuids, g_hash_table_unref); + nm_clear_g_source (&priv->timeout_id); + G_OBJECT_CLASS (nm_checkpoint_parent_class)->dispose (object); } +static const NMDBusInterfaceInfoExtended interface_info_checkpoint = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_CHECKPOINT, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Devices", "ao", NM_CHECKPOINT_DEVICES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Created", "x", NM_CHECKPOINT_CREATED), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("RollbackTimeout", "u", NM_CHECKPOINT_ROLLBACK_TIMEOUT), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_checkpoint_class_init (NMCheckpointClass *checkpoint_class) { GObjectClass *object_class = G_OBJECT_CLASS (checkpoint_class); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (checkpoint_class); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (checkpoint_class); - exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/Checkpoint"); - exported_object_class->export_on_construction = FALSE; + g_type_class_add_private (object_class, sizeof (NMCheckpointPrivate)); + + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/Checkpoint"); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_checkpoint); object_class->dispose = dispose; object_class->get_property = get_property; @@ -573,8 +673,4 @@ nm_checkpoint_class_init (NMCheckpointClass *checkpoint_class) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (checkpoint_class), - NMDBUS_TYPE_CHECKPOINT_SKELETON, - NULL); } diff --git a/src/nm-checkpoint.h b/src/nm-checkpoint.h index fccf8af3..c8598f38 100644 --- a/src/nm-checkpoint.h +++ b/src/nm-checkpoint.h @@ -21,7 +21,7 @@ #ifndef __NETWORKMANAGER_CHECKPOINT_H__ #define __NETWORKMANAGER_CHECKPOINT_H__ -#include "nm-exported-object.h" +#include "nm-dbus-object.h" #include "nm-dbus-interface.h" #define NM_TYPE_CHECKPOINT (nm_checkpoint_get_type ()) @@ -35,16 +35,35 @@ #define NM_CHECKPOINT_CREATED "created" #define NM_CHECKPOINT_ROLLBACK_TIMEOUT "rollback-timeout" -typedef struct _NMCheckpoint NMCheckpoint; +typedef struct _NMCheckpointPrivate NMCheckpointPrivate; + +typedef struct { + NMDBusObject parent; + NMCheckpointPrivate *_priv; + CList checkpoints_lst; +} NMCheckpoint; + typedef struct _NMCheckpointClass NMCheckpointClass; GType nm_checkpoint_get_type (void); NMCheckpoint *nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout, - NMCheckpointCreateFlags flags, GError **error); + NMCheckpointCreateFlags flags); + +typedef void (*NMCheckpointTimeoutCallback) (NMCheckpoint *self, + gpointer user_data); + +void nm_checkpoint_log_destroy (NMCheckpoint *self); + +void nm_checkpoint_set_timeout_callback (NMCheckpoint *self, + NMCheckpointTimeoutCallback callback, + gpointer user_data); -guint64 nm_checkpoint_get_rollback_ts (NMCheckpoint *checkpoint); -gboolean nm_checkpoint_includes_device (NMCheckpoint *checkpoint, NMDevice *device); GVariant *nm_checkpoint_rollback (NMCheckpoint *self); +void nm_checkpoint_adjust_rollback_timeout (NMCheckpoint *self, guint32 add_timeout); + +NMDevice *nm_checkpoint_includes_devices (NMCheckpoint *self, NMDevice *const*devices, guint n_devices); +NMDevice *nm_checkpoint_includes_devices_of (NMCheckpoint *self, NMCheckpoint *cp_for_devices); + #endif /* __NETWORKMANAGER_CHECKPOINT_H__ */ diff --git a/src/nm-config-data.c b/src/nm-config-data.c index 00e5c635..813ef9c8 100644 --- a/src/nm-config-data.c +++ b/src/nm-config-data.c @@ -54,7 +54,7 @@ struct _NMGlobalDnsConfig { char **searches; char **options; GHashTable *domains; - char **domain_list; + const char **domain_list; gboolean internal; }; @@ -238,7 +238,7 @@ nm_config_data_get_plugins (const NMConfigData *self, gboolean allow_default) if (!list && allow_default) { gs_unref_keyfile GKeyFile *kf = nm_config_create_keyfile (); - /* let keyfile split the default string according to it's own escaping rules. */ + /* let keyfile split the default string according to its own escaping rules. */ g_key_file_set_value (kf, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", NM_CONFIG_DEFAULT_MAIN_PLUGINS); list = g_key_file_get_string_list (kf, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", NULL, NULL); } @@ -705,54 +705,53 @@ nm_config_data_log (const NMConfigData *self, /*****************************************************************************/ -const char *const * -nm_global_dns_config_get_searches (const NMGlobalDnsConfig *dns) +const char *const* +nm_global_dns_config_get_searches (const NMGlobalDnsConfig *dns_config) { - g_return_val_if_fail (dns, NULL); + g_return_val_if_fail (dns_config, NULL); - return (const char *const *) dns->searches; + return (const char *const*) dns_config->searches; } const char *const * -nm_global_dns_config_get_options (const NMGlobalDnsConfig *dns) +nm_global_dns_config_get_options (const NMGlobalDnsConfig *dns_config) { - g_return_val_if_fail (dns, NULL); + g_return_val_if_fail (dns_config, NULL); - return (const char *const *) dns->options; + return (const char *const*) dns_config->options; } guint -nm_global_dns_config_get_num_domains (const NMGlobalDnsConfig *dns) +nm_global_dns_config_get_num_domains (const NMGlobalDnsConfig *dns_config) { - g_return_val_if_fail (dns, 0); - g_return_val_if_fail (dns->domains, 0); + g_return_val_if_fail (dns_config, 0); - return g_hash_table_size (dns->domains); + return dns_config->domains ? g_hash_table_size (dns_config->domains) : 0; } NMGlobalDnsDomain * -nm_global_dns_config_get_domain (const NMGlobalDnsConfig *dns, guint i) +nm_global_dns_config_get_domain (const NMGlobalDnsConfig *dns_config, guint i) { NMGlobalDnsDomain *domain; - g_return_val_if_fail (dns, NULL); - g_return_val_if_fail (dns->domains, NULL); - g_return_val_if_fail (dns->domain_list, NULL); - g_return_val_if_fail (i < g_strv_length (dns->domain_list), NULL); + g_return_val_if_fail (dns_config, NULL); + g_return_val_if_fail (dns_config->domains, NULL); + g_return_val_if_fail (i < g_hash_table_size (dns_config->domains), NULL); - domain = g_hash_table_lookup (dns->domains, dns->domain_list[i]); - g_return_val_if_fail (domain, NULL); + nm_assert (NM_PTRARRAY_LEN (dns_config->domain_list) == g_hash_table_size (dns_config->domains)); + domain = g_hash_table_lookup (dns_config->domains, dns_config->domain_list[i]); + + nm_assert (domain); return domain; } -NMGlobalDnsDomain *nm_global_dns_config_lookup_domain (const NMGlobalDnsConfig *dns, const char *name) +NMGlobalDnsDomain *nm_global_dns_config_lookup_domain (const NMGlobalDnsConfig *dns_config, const char *name) { - g_return_val_if_fail (dns, NULL); - g_return_val_if_fail (dns->domains, NULL); + g_return_val_if_fail (dns_config, NULL); g_return_val_if_fail (name, NULL); - return g_hash_table_lookup (dns->domains, name); + return dns_config->domains ? g_hash_table_lookup (dns_config->domains, name) : NULL; } const char * @@ -775,55 +774,73 @@ const char *const * nm_global_dns_domain_get_options (const NMGlobalDnsDomain *domain) { g_return_val_if_fail (domain, NULL); + return (const char *const *) domain->options; } gboolean -nm_global_dns_config_is_internal (const NMGlobalDnsConfig *dns) +nm_global_dns_config_is_internal (const NMGlobalDnsConfig *dns_config) { - return dns->internal; + return dns_config->internal; } gboolean -nm_global_dns_config_is_empty (const NMGlobalDnsConfig *dns) +nm_global_dns_config_is_empty (const NMGlobalDnsConfig *dns_config) { - g_return_val_if_fail (dns, TRUE); - g_return_val_if_fail (dns->domains, TRUE); + g_return_val_if_fail (dns_config, TRUE); - return (!dns->searches || g_strv_length (dns->searches) == 0) - && (!dns->options || g_strv_length (dns->options) == 0) - && g_hash_table_size (dns->domains) == 0; + return !dns_config->searches + && !dns_config->options + && !dns_config->domain_list; } void -nm_global_dns_config_update_checksum (const NMGlobalDnsConfig *dns, GChecksum *sum) +nm_global_dns_config_update_checksum (const NMGlobalDnsConfig *dns_config, GChecksum *sum) { NMGlobalDnsDomain *domain; - GList *keys, *key; - guint i; + guint i, j; + guint8 v8; - g_return_if_fail (dns); - g_return_if_fail (dns->domains); + g_return_if_fail (dns_config); g_return_if_fail (sum); - for (i = 0; dns->searches && dns->searches[i]; i++) - g_checksum_update (sum, (guchar *) dns->searches[i], strlen (dns->searches[i])); - for (i = 0; dns->options && dns->options[i]; i++) - g_checksum_update (sum, (guchar *) dns->options[i], strlen (dns->options[i])); + 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); - keys = g_list_sort (g_hash_table_get_keys (dns->domains), (GCompareFunc) strcmp); - for (key = keys; key; key = g_list_next (key)) { + v8 = NM_HASH_COMBINE_BOOLS (guint8, + !domain->servers, + !domain->options); + g_checksum_update (sum, (guchar *) &v8, 1); - domain = g_hash_table_lookup (dns->domains, key->data); - g_assert (domain != NULL); - g_checksum_update (sum, (guchar *) domain->name, strlen (domain->name)); + g_checksum_update (sum, (guchar *) domain->name, strlen (domain->name) + 1); - for (i = 0; domain->servers && domain->servers[i]; i++) - g_checksum_update (sum, (guchar *) domain->servers[i], strlen (domain->servers[i])); - for (i = 0; domain->options && domain->options[i]; i++) - g_checksum_update (sum, (guchar *) domain->options[i], strlen (domain->options[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); + } + } } - g_list_free (keys); } static void @@ -838,14 +855,15 @@ global_dns_domain_free (NMGlobalDnsDomain *domain) } void -nm_global_dns_config_free (NMGlobalDnsConfig *conf) -{ - if (conf) { - g_strfreev (conf->searches); - g_strfreev (conf->options); - g_free (conf->domain_list); - g_hash_table_unref (conf->domains); - g_free (conf); +nm_global_dns_config_free (NMGlobalDnsConfig *dns_config) +{ + if (dns_config) { + g_strfreev (dns_config->searches); + g_strfreev (dns_config->options); + g_free (dns_config->domain_list); + if (dns_config->domains) + g_hash_table_unref (dns_config->domains); + g_free (dns_config); } } @@ -858,18 +876,22 @@ nm_config_data_get_global_dns_config (const NMConfigData *self) } static void -global_dns_config_update_domain_list (NMGlobalDnsConfig *dns) +global_dns_config_seal_domains (NMGlobalDnsConfig *dns_config) { - guint length; + nm_assert (dns_config); + nm_assert (dns_config->domains); + nm_assert (!dns_config->domain_list); - g_free (dns->domain_list); - dns->domain_list = (char **) g_hash_table_get_keys_as_array (dns->domains, &length); + if (g_hash_table_size (dns_config->domains) == 0) + nm_clear_pointer (&dns_config->domains, g_hash_table_unref); + else + dns_config->domain_list = nm_utils_strdict_get_keys (dns_config->domains, TRUE, NULL); } static NMGlobalDnsConfig * load_global_dns (GKeyFile *keyfile, gboolean internal) { - NMGlobalDnsConfig *conf; + NMGlobalDnsConfig *dns_config; char *group, *domain_prefix; gs_strfreev char **groups = NULL; int g, i, j, domain_prefix_len; @@ -887,13 +909,18 @@ load_global_dns (GKeyFile *keyfile, gboolean internal) if (!nm_config_keyfile_has_global_dns_config (keyfile, internal)) return NULL; - conf = g_malloc0 (sizeof (NMGlobalDnsConfig)); - conf->domains = g_hash_table_new_full (nm_str_hash, g_str_equal, - g_free, (GDestroyNotify) global_dns_domain_free); + dns_config = g_malloc0 (sizeof (NMGlobalDnsConfig)); + dns_config->domains = g_hash_table_new_full (nm_str_hash, g_str_equal, + g_free, (GDestroyNotify) global_dns_domain_free); strv = g_key_file_get_string_list (keyfile, group, "searches", NULL, NULL); - if (strv) - conf->searches = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); + if (strv) { + _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); + if (!strv[0]) + g_free (strv); + else + dns_config->searches = strv; + } strv = g_key_file_get_string_list (keyfile, group, "options", NULL, NULL); if (strv) { @@ -904,8 +931,12 @@ load_global_dns (GKeyFile *keyfile, gboolean internal) else g_free (strv[i]); } - strv[j] = NULL; - conf->options = strv; + if (j == 0) + g_free (strv); + else { + strv[j] = NULL; + dns_config->options = strv; + } } groups = g_key_file_get_groups (keyfile, NULL); @@ -928,20 +959,23 @@ load_global_dns (GKeyFile *keyfile, gboolean internal) else g_free (strv[i]); } - if (j) { + if (j == 0) + g_free (strv); + else { strv[j] = NULL; servers = strv; } - else - g_free (strv); } if (!servers) continue; strv = g_key_file_get_string_list (keyfile, groups[g], "options", NULL, NULL); - if (strv) + if (strv) { options = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); + if (!options[0]) + nm_clear_g_free (&options); + } name = strdup (&groups[g][domain_prefix_len]); domain = g_malloc0 (sizeof (NMGlobalDnsDomain)); @@ -949,7 +983,7 @@ load_global_dns (GKeyFile *keyfile, gboolean internal) domain->servers = servers; domain->options = options; - g_hash_table_insert (conf->domains, strdup (name), domain); + g_hash_table_insert (dns_config->domains, strdup (name), domain); if (!strcmp (name, "*")) default_found = TRUE; @@ -958,59 +992,60 @@ load_global_dns (GKeyFile *keyfile, gboolean internal) if (!default_found) { nm_log_dbg (LOGD_CORE, "%s global DNS configuration is missing default domain, ignore it", internal ? "internal" : "user"); - nm_global_dns_config_free (conf); + nm_global_dns_config_free (dns_config); return NULL; } - conf->internal = internal; - global_dns_config_update_domain_list (conf); - return conf; + dns_config->internal = internal; + global_dns_config_seal_domains (dns_config); + return dns_config; } - void -nm_global_dns_config_to_dbus (const NMGlobalDnsConfig *dns, GValue *value) +nm_global_dns_config_to_dbus (const NMGlobalDnsConfig *dns_config, GValue *value) { GVariantBuilder conf_builder, domains_builder, domain_builder; - NMGlobalDnsDomain *domain; - GHashTableIter iter; + guint i; g_variant_builder_init (&conf_builder, G_VARIANT_TYPE ("a{sv}")); - if (!dns) + if (!dns_config) goto out; - if (dns->searches) { + if (dns_config->searches) { g_variant_builder_add (&conf_builder, "{sv}", "searches", - g_variant_new_strv ((const char *const *) dns->searches, -1)); + g_variant_new_strv ((const char *const *) dns_config->searches, -1)); } - if (dns->options) { + if (dns_config->options) { g_variant_builder_add (&conf_builder, "{sv}", "options", - g_variant_new_strv ((const char *const *) dns->options, -1)); + g_variant_new_strv ((const char *const *) dns_config->options, -1)); } g_variant_builder_init (&domains_builder, G_VARIANT_TYPE ("a{sv}")); + if (dns_config->domain_list) { + for (i = 0; dns_config->domain_list[i]; i++) { + NMGlobalDnsDomain *domain; - g_hash_table_iter_init (&iter, dns->domains); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &domain)) { + domain = g_hash_table_lookup (dns_config->domains, dns_config->domain_list[i]); - g_variant_builder_init (&domain_builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_init (&domain_builder, G_VARIANT_TYPE ("a{sv}")); - if (domain->servers) { - g_variant_builder_add (&domain_builder, "{sv}", "servers", - g_variant_new_strv ((const char *const *) domain->servers, -1)); - } - if (domain->options) { - g_variant_builder_add (&domain_builder, "{sv}", "options", - g_variant_new_strv ((const char *const *) domain->options, -1)); - } + if (domain->servers) { + g_variant_builder_add (&domain_builder, "{sv}", "servers", + g_variant_new_strv ((const char *const *) domain->servers, -1)); + } + if (domain->options) { + g_variant_builder_add (&domain_builder, "{sv}", "options", + g_variant_new_strv ((const char *const *) domain->options, -1)); + } - g_variant_builder_add (&domains_builder, "{sv}", domain->name, - g_variant_builder_end (&domain_builder)); + g_variant_builder_add (&domains_builder, "{sv}", domain->name, + g_variant_builder_end (&domain_builder)); + } } - g_variant_builder_add (&conf_builder, "{sv}", "domains", g_variant_builder_end (&domains_builder)); + out: g_value_take_variant (value, g_variant_builder_end (&conf_builder)); } @@ -1044,15 +1079,20 @@ global_dns_domain_from_dbus (char *name, GVariant *variant) else g_free (strv[i]); } - if (j) { + if (j == 0) + g_free (strv); + else { strv[j] = NULL; + g_strfreev (domain->servers); domain->servers = strv; - } else - g_free (strv); + } } else if ( !g_strcmp0 (key, "options") && g_variant_is_of_type (val, G_VARIANT_TYPE ("as"))) { strv = g_variant_dup_strv (val, NULL); + g_strfreev (domain->options); domain->options = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); + if (!domain->options[0]) + nm_clear_g_free (&domain->options); } g_variant_unref (val); @@ -1111,11 +1151,12 @@ nm_global_dns_config_from_dbus (const GValue *value, GError **error) else g_free (strv[i]); } - - if (strv) + if (j == 0) + g_free (strv); + else { strv[j] = NULL; - - dns_config->options = strv; + dns_config->options = strv; + } } else if ( !g_strcmp0 (key, "domains") && g_variant_is_of_type (val, G_VARIANT_TYPE ("a{sv}"))) { NMGlobalDnsDomain *domain; @@ -1145,7 +1186,7 @@ nm_global_dns_config_from_dbus (const GValue *value, GError **error) return NULL; } - global_dns_config_update_domain_list (dns_config); + global_dns_config_seal_domains (dns_config); return dns_config; } @@ -1196,6 +1237,8 @@ _match_section_infos_lookup (const MatchSectionInfo *match_section_infos, GKeyFile *keyfile, const char *property, NMDevice *device, + const NMPlatformLink *pllink, + const char *match_device_type, char **out_value) { if (!match_section_infos) @@ -1216,9 +1259,15 @@ _match_section_infos_lookup (const MatchSectionInfo *match_section_infos, if (!value && !match_section_infos->stop_match) continue; - match = TRUE; - if (match_section_infos->match_device.has) - match = device && nm_device_spec_match_list (device, match_section_infos->match_device.spec); + if (match_section_infos->match_device.has) { + if (device) + match = nm_device_spec_match_list (device, match_section_infos->match_device.spec); + else if (pllink) + match = nm_match_spec_device_by_pllink (pllink, match_device_type, match_section_infos->match_device.spec, FALSE); + else + match = FALSE; + } else + match = TRUE; if (match) { *out_value = value; @@ -1248,6 +1297,35 @@ nm_config_data_get_device_config (const NMConfigData *self, priv->keyfile, property, device, + NULL, + NULL, + &value); + NM_SET_OUT (has_match, !!connection_info); + return value; +} + +char * +nm_config_data_get_device_config_by_pllink (const NMConfigData *self, + const char *property, + const NMPlatformLink *pllink, + const char *match_device_type, + gboolean *has_match) +{ + const NMConfigDataPrivate *priv; + const MatchSectionInfo *connection_info; + char *value = NULL; + + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (property && *property, NULL); + + priv = NM_CONFIG_DATA_GET_PRIVATE (self); + + connection_info = _match_section_infos_lookup (&priv->device_infos[0], + priv->keyfile, + property, + NULL, + pllink, + match_device_type, &value); NM_SET_OUT (has_match, !!connection_info); return value; @@ -1287,6 +1365,8 @@ nm_config_data_get_connection_default (const NMConfigData *self, priv->keyfile, property, device, + NULL, + NULL, &value); return value; } diff --git a/src/nm-config-data.h b/src/nm-config-data.h index 3092a87f..650fbe14 100644 --- a/src/nm-config-data.h +++ b/src/nm-config-data.h @@ -100,7 +100,6 @@ typedef enum { /*< flags >*/ /* main-file or config-description changed */ NM_CONFIG_CHANGE_CONFIG_FILES = (1L << 10), - /* any configuration on disk changed */ NM_CONFIG_CHANGE_VALUES = (1L << 11), @@ -110,7 +109,6 @@ typedef enum { /*< flags >*/ /* any internal configuration on disk changed (NetworkManager-intern.conf) */ NM_CONFIG_CHANGE_VALUES_INTERN = (1L << 13), - /* configuration regarding connectivity changed */ NM_CONFIG_CHANGE_CONNECTIVITY = (1L << 14), @@ -188,6 +186,12 @@ char *nm_config_data_get_device_config (const NMConfigData *self, NMDevice *device, gboolean *has_match); +char *nm_config_data_get_device_config_by_pllink (const NMConfigData *self, + const char *property, + const NMPlatformLink *pllink, + const char *match_device_type, + gboolean *has_match); + gboolean nm_config_data_get_device_config_boolean (const NMConfigData *self, const char *property, NMDevice *device, @@ -200,18 +204,18 @@ gboolean nm_config_data_is_intern_atomic_group (const NMConfigData *self, const GKeyFile *nm_config_data_clone_keyfile_intern (const NMConfigData *self); -const char *const *nm_global_dns_config_get_searches (const NMGlobalDnsConfig *dns); -const char *const *nm_global_dns_config_get_options (const NMGlobalDnsConfig *dns); -guint nm_global_dns_config_get_num_domains (const NMGlobalDnsConfig *dns); -NMGlobalDnsDomain *nm_global_dns_config_get_domain (const NMGlobalDnsConfig *dns, guint i); -NMGlobalDnsDomain *nm_global_dns_config_lookup_domain (const NMGlobalDnsConfig *dns, const char *name); +const char *const *nm_global_dns_config_get_searches (const NMGlobalDnsConfig *dns_config); +const char *const *nm_global_dns_config_get_options (const NMGlobalDnsConfig *dns_config); +guint nm_global_dns_config_get_num_domains (const NMGlobalDnsConfig *dns_config); +NMGlobalDnsDomain *nm_global_dns_config_get_domain (const NMGlobalDnsConfig *dns_config, guint i); +NMGlobalDnsDomain *nm_global_dns_config_lookup_domain (const NMGlobalDnsConfig *dns_config, const char *name); const char *nm_global_dns_domain_get_name (const NMGlobalDnsDomain *domain); const char *const *nm_global_dns_domain_get_servers (const NMGlobalDnsDomain *domain); const char *const *nm_global_dns_domain_get_options (const NMGlobalDnsDomain *domain); -gboolean nm_global_dns_config_is_internal (const NMGlobalDnsConfig *dns); -gboolean nm_global_dns_config_is_empty (const NMGlobalDnsConfig *dns); -void nm_global_dns_config_update_checksum (const NMGlobalDnsConfig *dns, GChecksum *sum); -void nm_global_dns_config_free (NMGlobalDnsConfig *conf); +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); 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/nm-config.c b/src/nm-config.c index 29f0d517..d4682ed6 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -553,7 +553,7 @@ nm_config_create_keyfile () /* this is an external variable, to make loading testable. Other then that, * no code is supposed to change this. */ -guint _nm_config_match_nm_version = NM_VERSION_CUR_STABLE; +guint _nm_config_match_nm_version = NM_VERSION; char *_nm_config_match_env = NULL; static gboolean @@ -579,7 +579,7 @@ ignore_config_snippet (GKeyFile *keyfile, gboolean is_base_config) const char *e; e = g_getenv ("NM_CONFIG_ENABLE_TAG"); - _nm_config_match_env = g_strdup (e ? e : ""); + _nm_config_match_env = g_strdup (e ?: ""); } /* second, interpret the value as match-spec. */ @@ -615,7 +615,7 @@ _sort_groups_cmp (const char **pa, const char **pb, gpointer dummy) if (a_is_connection) { /* both are [connection.\+] entries. Reverse their order. * One of the sections might be literally [connection]. That section - * is special and it's order will be fixed later. It doesn't actually + * is special and its order will be fixed later. It doesn't actually * matter here how it compares with [connection.\+] sections. */ return pa > pb ? -1 : 1; } @@ -633,7 +633,7 @@ _sort_groups_cmp (const char **pa, const char **pb, gpointer dummy) if (a_is_device) { /* both are [device.\+] entries. Reverse their order. * One of the sections might be literally [device]. That section - * is special and it's order will be fixed later. It doesn't actually + * is special and its order will be fixed later. It doesn't actually * matter here how it compares with [device.\+] sections. */ return pa > pb ? -1 : 1; } @@ -1383,7 +1383,6 @@ intern_config_write (const char *filename, GKeyFile *keyfile; gs_strfreev char **groups = NULL; guint g, k; - gboolean has_intern = FALSE; gboolean success = FALSE; GError *local = NULL; @@ -1449,10 +1448,7 @@ intern_config_write (const char *filename, value_set = g_key_file_get_value (keyfile_intern, group, key, NULL); - if (is_intern) { - has_intern = TRUE; - g_key_file_set_value (keyfile, group, key, value_set); - } else if (is_atomic) + if (is_intern || is_atomic) g_key_file_set_value (keyfile, group, key, value_set); else { gs_free char *value_was = NULL; @@ -2117,7 +2113,7 @@ nm_config_device_state_load_all (void) if (!state) continue; - if (!nm_g_hash_table_insert (states, GINT_TO_POINTER (ifindex), state)) + if (!g_hash_table_insert (states, GINT_TO_POINTER (ifindex), state)) nm_assert_not_reached (); } g_dir_close (dir); @@ -2401,17 +2397,17 @@ _set_config_data (NMConfig *self, NMConfigData *new_data, NMConfigChangeFlags re } if (new_data) { - _LOGI ("config: signal %s (%s)", + _LOGI ("signal: %s (%s)", nm_config_change_flags_to_string (changes, NULL, 0), nm_config_data_get_config_description (new_data)); nm_config_data_log (new_data, "CONFIG: ", " ", NULL); priv->config_data = new_data; } else if (had_new_data) - _LOGI ("config: signal %s (no changes from disk)", nm_config_change_flags_to_string (changes, NULL, 0)); + _LOGI ("signal: %s (no changes from disk)", nm_config_change_flags_to_string (changes, NULL, 0)); else - _LOGI ("config: signal %s", nm_config_change_flags_to_string (changes, NULL, 0)); + _LOGI ("signal: %s", nm_config_change_flags_to_string (changes, NULL, 0)); g_signal_emit (self, signals[SIGNAL_CONFIG_CHANGED], 0, - new_data ? new_data : old_data, + new_data ?: old_data, changes, old_data); if (new_data) g_object_unref (old_data); diff --git a/src/nm-config.h b/src/nm-config.h index 42ab4682..5d027ce0 100644 --- a/src/nm-config.h +++ b/src/nm-config.h @@ -57,7 +57,6 @@ #define NM_CONFIG_KEYFILE_GROUP_KEYFILE "keyfile" #define NM_CONFIG_KEYFILE_GROUP_IFUPDOWN "ifupdown" -#define NM_CONFIG_KEYFILE_GROUP_IFNET "ifnet" #define NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT "auth-polkit" #define NM_CONFIG_KEYFILE_KEY_MAIN_AUTOCONNECT_RETRIES_DEFAULT "autoconnect-retries-default" @@ -79,6 +78,8 @@ #define NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED "managed" #define NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER "ignore-carrier" #define NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS "sriov-num-vfs" +#define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND "wifi.backend" +#define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS "wifi.scan-rand-mac-address" #define NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT "carrier-wait-timeout" #define NM_CONFIG_KEYFILE_KEYPREFIX_WAS ".was." diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c index 8861f261..389e72ad 100644 --- a/src/nm-connectivity.c +++ b/src/nm-connectivity.c @@ -25,22 +25,82 @@ #include "nm-connectivity.h" #include + +#if WITH_CONCHECK #include +#endif +#include "c-list/src/c-list.h" #include "nm-config.h" #include "NetworkManagerUtils.h" +#define HEADER_STATUS_ONLINE "X-NetworkManager-Status: online\r\n" + /*****************************************************************************/ +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_state_to_string, int /*NMConnectivityState*/, + NM_UTILS_LOOKUP_DEFAULT_WARN ("???"), + NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_UNKNOWN, "UNKNOWN"), + NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_NONE, "NONE"), + NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_LIMITED, "LIMITED"), + NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_PORTAL, "PORTAL"), + NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_FULL, "FULL"), + + NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_ERROR, "ERROR"), + NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_FAKE, "FAKE"), +); + +const char * +nm_connectivity_state_to_string (NMConnectivityState state) +{ + return _state_to_string (state); +} + +/*****************************************************************************/ + +struct _NMConnectivityCheckHandle { + CList handles_lst; + NMConnectivity *self; + NMConnectivityCheckCallback callback; + gpointer user_data; + + char *ifspec; + +#if WITH_CONCHECK + struct { + char *response; + + CURL *curl_ehandle; + struct curl_slist *request_headers; + + GString *recv_msg; + } concheck; +#endif + + guint timeout_id; +}; + +enum { + CONFIG_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + typedef struct { + CList handles_lst_head; char *uri; char *response; gboolean enabled; guint interval; NMConfig *config; - guint periodic_check_id; - CURLM *curl_mhandle; - guint curl_timer; +#if WITH_CONCHECK + struct { + CURLM *curl_mhandle; + guint curl_timer; + } concheck; +#endif } NMConnectivityPrivate; struct _NMConnectivity { @@ -58,14 +118,6 @@ G_DEFINE_TYPE (NMConnectivity, nm_connectivity, G_TYPE_OBJECT) NM_DEFINE_SINGLETON_GETTER (NMConnectivity, nm_connectivity_get, NM_TYPE_CONNECTIVITY); -enum { - PERIODIC_CHECK, - - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_CONCHECK @@ -78,116 +130,172 @@ static guint signals[LAST_SIGNAL] = { 0 }; \ if (nm_logging_enabled (__level, _NMLOG2_DOMAIN)) { \ _nm_log (__level, _NMLOG2_DOMAIN, 0, \ - &cb_data->ifspec[3], NULL, \ - "connectivity: (%s) " \ - _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - &cb_data->ifspec[3] \ - _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + (cb_data->ifspec ? &cb_data->ifspec[3] : NULL), \ + NULL, \ + "connectivity: (%s) " \ + _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + (cb_data->ifspec ? &cb_data->ifspec[3] : "") \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ } \ } G_STMT_END /*****************************************************************************/ -NM_UTILS_LOOKUP_STR_DEFINE (nm_connectivity_state_to_string, NMConnectivityState, - NM_UTILS_LOOKUP_DEFAULT_WARN ("???"), - NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_UNKNOWN, "UNKNOWN"), - NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_NONE, "NONE"), - NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_LIMITED, "LIMITED"), - NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_PORTAL, "PORTAL"), - NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_FULL, "FULL"), -); +static void +cb_data_invoke_callback (NMConnectivityCheckHandle *cb_data, + NMConnectivityState state, + GError *error, + const char *log_message) +{ + NMConnectivityCheckCallback callback; -/*****************************************************************************/ + nm_assert (cb_data); + nm_assert (NM_IS_CONNECTIVITY (cb_data->self)); -typedef struct { - GSimpleAsyncResult *simple; - char *response; - CURL *curl_ehandle; - size_t msg_size; - char *msg; - struct curl_slist *request_headers; - guint timeout_id; - char *ifspec; -} ConCheckCbData; + callback = cb_data->callback; + if (!callback) + return; + + cb_data->callback = NULL; + + nm_assert (log_message); + + _LOG2D ("check completed: %s; %s", + nm_connectivity_state_to_string (state), + log_message); + + callback (cb_data->self, + cb_data, + state, + error, + cb_data->user_data); +} static void -finish_cb_data (ConCheckCbData *cb_data, NMConnectivityState new_state) +cb_data_free (NMConnectivityCheckHandle *cb_data, + NMConnectivityState state, + GError *error, + const char *log_message) { - /* Contrary to what cURL manual claim it is *not* safe to remove - * the easy handle "at any moment"; specifically not from the - * write function. Thus here we just dissociate the cb_data from - * the easy handle and the easy handle will be cleaned up when the - * message goes to CURLMSG_DONE in curl_check_connectivity(). */ - curl_easy_setopt (cb_data->curl_ehandle, CURLOPT_PRIVATE, NULL); - - g_simple_async_result_set_op_res_gssize (cb_data->simple, new_state); - g_simple_async_result_complete (cb_data->simple); - g_object_unref (cb_data->simple); - curl_slist_free_all (cb_data->request_headers); - g_free (cb_data->response); - g_free (cb_data->msg); + NMConnectivity *self; + + nm_assert (cb_data); + + self = cb_data->self; + + nm_assert (NM_IS_CONNECTIVITY (self)); + + c_list_unlink (&cb_data->handles_lst); + +#if WITH_CONCHECK + if (cb_data->concheck.curl_ehandle) { + NMConnectivityPrivate *priv; + + /* Contrary to what cURL manual claim it is *not* safe to remove + * the easy handle "at any moment"; specifically not from the + * write function. Thus here we just dissociate the cb_data from + * the easy handle and the easy handle will be cleaned up when the + * message goes to CURLMSG_DONE in curl_check_connectivity(). */ + curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_WRITEFUNCTION, NULL); + curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_WRITEDATA, NULL); + curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_HEADERFUNCTION, NULL); + curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_HEADERDATA, NULL); + curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_PRIVATE, NULL); + curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_HTTPHEADER, NULL); + + priv = NM_CONNECTIVITY_GET_PRIVATE (self); + + curl_multi_remove_handle (priv->concheck.curl_mhandle, cb_data->concheck.curl_ehandle); + curl_easy_cleanup (cb_data->concheck.curl_ehandle); + + curl_slist_free_all (cb_data->concheck.request_headers); + } +#endif + + nm_clear_g_source (&cb_data->timeout_id); + + cb_data_invoke_callback (cb_data, state, error, log_message); + +#if WITH_CONCHECK + g_free (cb_data->concheck.response); + if (cb_data->concheck.recv_msg) + g_string_free (cb_data->concheck.recv_msg, TRUE); +#endif g_free (cb_data->ifspec); - g_source_remove (cb_data->timeout_id); - g_slice_free (ConCheckCbData, cb_data); + g_slice_free (NMConnectivityCheckHandle, cb_data); +} + +/*****************************************************************************/ + +#if WITH_CONCHECK +static const char * +_check_handle_get_response (NMConnectivityCheckHandle *cb_data) +{ + return cb_data->concheck.response ?: NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE; } static void -curl_check_connectivity (CURLM *mhandle, CURLMcode ret) +curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask) { - ConCheckCbData *cb_data; + NMConnectivityCheckHandle *cb_data; CURLMsg *msg; CURLcode eret; gint m_left; + long response_code; + CURLMcode ret; + int running_handles; + ret = curl_multi_socket_action (mhandle, sockfd, ev_bitmask, &running_handles); if (ret != CURLM_OK) - _LOGW ("connectivity check failed"); + _LOGE ("connectivity check failed: %d", ret); while ((msg = curl_multi_info_read (mhandle, &m_left))) { + if (msg->msg != CURLMSG_DONE) continue; /* Here we have completed a session. Check easy session result. */ eret = curl_easy_getinfo (msg->easy_handle, CURLINFO_PRIVATE, (char **) &cb_data); if (eret != CURLE_OK) { - _LOG2E ("curl cannot extract cb_data for easy handle %p, skipping msg", msg->easy_handle); + _LOGE ("curl cannot extract cb_data for easy handle, skipping msg"); continue; } - if (cb_data) { - /* If cb_data is still there this message hasn't been - * taken care of. Do so now. */ - if (msg->data.result == CURLE_OK) { - /* If we get here, it means that easy_write_cb() didn't read enough - * bytes to be able to do a match. */ - _LOG2I ("response shorter than expected '%s'; assuming captive portal.", - cb_data->response); - finish_cb_data (cb_data, NM_CONNECTIVITY_PORTAL); - } else { - _LOG2D ("check failed (%d)", msg->data.result); - finish_cb_data (cb_data, NM_CONNECTIVITY_LIMITED); - } + if (!cb_data->callback) { + /* callback was already invoked earlier. */ + cb_data_free (cb_data, NM_CONNECTIVITY_UNKNOWN, NULL, NULL); + } else if (msg->data.result != CURLE_OK) { + gs_free char *log_message = NULL; + + log_message = g_strdup_printf ("check failed with curl status %d", msg->data.result); + cb_data_free (cb_data, NM_CONNECTIVITY_LIMITED, NULL, + log_message); + } else if ( !((_check_handle_get_response (cb_data))[0]) + && (curl_easy_getinfo (msg->easy_handle, CURLINFO_RESPONSE_CODE, &response_code) == CURLE_OK) + && response_code == 204) { + /* If we got a 204 response code (no content) and we actually + * requested no content, report full connectivity. */ + cb_data_free (cb_data, NM_CONNECTIVITY_FULL, NULL, + "no content, as expected"); + } else { + /* If we get here, it means that easy_write_cb() didn't read enough + * bytes to be able to do a match, or that we were asking for no content + * (204 response code) and we actually got some. Either way, that is + * an indication of a captive portal */ + cb_data_free (cb_data, NM_CONNECTIVITY_PORTAL, NULL, + "unexpected short response"); } - - curl_multi_remove_handle (mhandle, msg->easy_handle); - curl_easy_cleanup (msg->easy_handle); } } static gboolean curl_timeout_cb (gpointer user_data) { - NMConnectivity *self = NM_CONNECTIVITY (user_data); + gs_unref_object NMConnectivity *self = g_object_ref (NM_CONNECTIVITY (user_data)); NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - CURLMcode ret; - int pending_conn; - - priv->curl_timer = 0; - - ret = curl_multi_socket_action (priv->curl_mhandle, CURL_SOCKET_TIMEOUT, 0, &pending_conn); - _LOGT ("timeout elapsed - multi_socket_action (%d conn remaining)", pending_conn); - - curl_check_connectivity (priv->curl_mhandle, ret); + priv->concheck.curl_timer = 0; + curl_check_connectivity (priv->concheck.curl_mhandle, CURL_SOCKET_TIMEOUT, 0); return G_SOURCE_REMOVE; } @@ -197,21 +305,17 @@ multi_timer_cb (CURLM *multi, long timeout_ms, void *userdata) NMConnectivity *self = NM_CONNECTIVITY (userdata); NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - nm_clear_g_source (&priv->curl_timer); + nm_clear_g_source (&priv->concheck.curl_timer); if (timeout_ms != -1) - priv->curl_timer = g_timeout_add (timeout_ms, curl_timeout_cb, self); - + priv->concheck.curl_timer = g_timeout_add (timeout_ms, curl_timeout_cb, self); return 0; } static gboolean -curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer data) +curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_data) { - NMConnectivity *self = NM_CONNECTIVITY (data); + gs_unref_object NMConnectivity *self = g_object_ref (NM_CONNECTIVITY (user_data)); NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - CURLMcode ret; - int pending_conn = 0; - gboolean bret = TRUE; int fd = g_io_channel_unix_get_fd (ch); int action = 0; @@ -219,16 +323,11 @@ curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer data) action |= CURL_CSELECT_IN; if (condition & G_IO_OUT) action |= CURL_CSELECT_OUT; + if (condition & G_IO_ERR) + action |= CURL_CSELECT_ERR; - ret = curl_multi_socket_action (priv->curl_mhandle, fd, 0, &pending_conn); - - curl_check_connectivity (priv->curl_mhandle, ret); - - if (pending_conn == 0) { - nm_clear_g_source (&priv->curl_timer); - bret = FALSE; - } - return bret; + curl_check_connectivity (priv->concheck.curl_mhandle, fd, action); + return G_SOURCE_CONTINUE; } typedef struct { @@ -241,11 +340,12 @@ multi_socket_cb (CURL *e_handle, curl_socket_t s, int what, void *userdata, void { NMConnectivity *self = NM_CONNECTIVITY (userdata); NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - CurlSockData *fdp = (CurlSockData *) socketp; + CurlSockData *fdp = socketp; GIOCondition condition = 0; if (what == CURL_POLL_REMOVE) { if (fdp) { + curl_multi_assign (priv->concheck.curl_mhandle, s, NULL); nm_clear_g_source (&fdp->ev); g_io_channel_unref (fdp->ch); g_slice_free (CurlSockData, fdp); @@ -254,6 +354,7 @@ multi_socket_cb (CURL *e_handle, curl_socket_t s, int what, void *userdata, void if (!fdp) { fdp = g_slice_new0 (CurlSockData); fdp->ch = g_io_channel_unix_new (s); + curl_multi_assign (priv->concheck.curl_mhandle, s, fdp); } else nm_clear_g_source (&fdp->ev); @@ -261,29 +362,26 @@ multi_socket_cb (CURL *e_handle, curl_socket_t s, int what, void *userdata, void condition = G_IO_IN; else if (what == CURL_POLL_OUT) condition = G_IO_OUT; - else if (condition == CURL_POLL_INOUT) + else if (what == CURL_POLL_INOUT) condition = G_IO_IN | G_IO_OUT; if (condition) fdp->ev = g_io_add_watch (fdp->ch, condition, curl_socketevent_cb, self); - curl_multi_assign (priv->curl_mhandle, s, fdp); } return CURLM_OK; } -#define HEADER_STATUS_ONLINE "X-NetworkManager-Status: online\r\n" - static size_t easy_header_cb (char *buffer, size_t size, size_t nitems, void *userdata) { - ConCheckCbData *cb_data = userdata; + NMConnectivityCheckHandle *cb_data = userdata; size_t len = size * nitems; if ( len >= sizeof (HEADER_STATUS_ONLINE) - 1 && !g_ascii_strncasecmp (buffer, HEADER_STATUS_ONLINE, sizeof (HEADER_STATUS_ONLINE) - 1)) { - _LOG2D ("status header found, check successful"); - finish_cb_data (cb_data, NM_CONNECTIVITY_FULL); + cb_data_invoke_callback (cb_data, NM_CONNECTIVITY_FULL, + NULL, "status header found"); return 0; } @@ -293,23 +391,26 @@ easy_header_cb (char *buffer, size_t size, size_t nitems, void *userdata) static size_t easy_write_cb (void *buffer, size_t size, size_t nmemb, void *userdata) { - ConCheckCbData *cb_data = userdata; + NMConnectivityCheckHandle *cb_data = userdata; size_t len = size * nmemb; + const char *response = _check_handle_get_response (cb_data);; + + if (!cb_data->concheck.recv_msg) + cb_data->concheck.recv_msg = g_string_sized_new (len + 10); - cb_data->msg = g_realloc (cb_data->msg, cb_data->msg_size + len); - memcpy (cb_data->msg + cb_data->msg_size, buffer, len); - cb_data->msg_size += len; + g_string_append_len (cb_data->concheck.recv_msg, buffer, len); - if (cb_data->msg_size >= strlen (cb_data->response)) { + if ( response + && cb_data->concheck.recv_msg->len >= strlen (response)) { /* We already have enough data -- check response */ - if (g_str_has_prefix (cb_data->msg, cb_data->response)) { - _LOG2D ("check successful."); - finish_cb_data (cb_data, NM_CONNECTIVITY_FULL); + if (g_str_has_prefix (cb_data->concheck.recv_msg->str, response)) { + cb_data_invoke_callback (cb_data, NM_CONNECTIVITY_FULL, NULL, + "expected response"); } else { - _LOG2I ("response did not match expected response '%s'; assuming captive portal.", - cb_data->response); - finish_cb_data (cb_data, NM_CONNECTIVITY_PORTAL); + cb_data_invoke_callback (cb_data, NM_CONNECTIVITY_PORTAL, NULL, + "unexpected response"); } + return 0; } @@ -317,105 +418,139 @@ easy_write_cb (void *buffer, size_t size, size_t nmemb, void *userdata) } static gboolean -timeout_cb (gpointer user_data) +_timeout_cb (gpointer user_data) { - ConCheckCbData *cb_data = user_data; - NMConnectivity *self = NM_CONNECTIVITY (g_async_result_get_source_object (G_ASYNC_RESULT (cb_data->simple))); - NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - CURL *ehandle = cb_data->curl_ehandle; + NMConnectivityCheckHandle *cb_data = user_data; + NMConnectivity *self; + + nm_assert (NM_IS_CONNECTIVITY (cb_data->self)); - _LOG2I ("timed out"); - finish_cb_data (cb_data, NM_CONNECTIVITY_LIMITED); - curl_multi_remove_handle (priv->curl_mhandle, ehandle); - curl_easy_cleanup (ehandle); + self = cb_data->self; + nm_assert (c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (self)->handles_lst_head, &cb_data->handles_lst)); + + cb_data_free (cb_data, NM_CONNECTIVITY_LIMITED, NULL, "timeout"); return G_SOURCE_REMOVE; } +#endif -void -nm_connectivity_check_async (NMConnectivity *self, - const char *iface, - GAsyncReadyCallback callback, - gpointer user_data) +static gboolean +_idle_cb (gpointer user_data) { - NMConnectivityPrivate *priv; - GSimpleAsyncResult *simple; - CURL *ehandle = NULL; + NMConnectivityCheckHandle *cb_data = user_data; - g_return_if_fail (NM_IS_CONNECTIVITY (self)); - priv = NM_CONNECTIVITY_GET_PRIVATE (self); + nm_assert (NM_IS_CONNECTIVITY (cb_data->self)); + nm_assert (c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (cb_data->self)->handles_lst_head, &cb_data->handles_lst)); - simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, - nm_connectivity_check_async); + cb_data->timeout_id = 0; + if (!cb_data->ifspec) { + gs_free_error GError *error = NULL; - if (priv->enabled) - ehandle = curl_easy_init (); + /* the invocation was with an invalid ifname. It is a fail. */ + g_set_error (&error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + "no interface specified for connectivity check"); + cb_data_free (cb_data, NM_CONNECTIVITY_ERROR, NULL, "missing interface"); + } else + cb_data_free (cb_data, NM_CONNECTIVITY_FAKE, NULL, "fake result"); + return G_SOURCE_REMOVE; +} - if (ehandle) { - ConCheckCbData *cb_data = g_slice_new0 (ConCheckCbData); +NMConnectivityCheckHandle * +nm_connectivity_check_start (NMConnectivity *self, + const char *iface, + NMConnectivityCheckCallback callback, + gpointer user_data) +{ + NMConnectivityPrivate *priv; + NMConnectivityCheckHandle *cb_data; - cb_data->curl_ehandle = ehandle; - cb_data->request_headers = curl_slist_append (NULL, "Connection: close"); + g_return_val_if_fail (NM_IS_CONNECTIVITY (self), NULL); + g_return_val_if_fail (!iface || iface[0], NULL); + g_return_val_if_fail (callback, NULL); + + priv = NM_CONNECTIVITY_GET_PRIVATE (self); + + cb_data = g_slice_new0 (NMConnectivityCheckHandle); + cb_data->self = self; + c_list_link_tail (&priv->handles_lst_head, &cb_data->handles_lst); + cb_data->callback = callback; + cb_data->user_data = user_data; + + if (iface) cb_data->ifspec = g_strdup_printf ("if!%s", iface); - cb_data->simple = simple; - if (priv->response) - cb_data->response = g_strdup (priv->response); - else - cb_data->response = g_strdup (NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE); - - curl_easy_setopt (ehandle, CURLOPT_URL, priv->uri); - curl_easy_setopt (ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb); - curl_easy_setopt (ehandle, CURLOPT_WRITEDATA, cb_data); - curl_easy_setopt (ehandle, CURLOPT_HEADERFUNCTION, easy_header_cb); - curl_easy_setopt (ehandle, CURLOPT_HEADERDATA, cb_data); - curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data); - curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->request_headers); - curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec); - curl_multi_add_handle (priv->curl_mhandle, ehandle); - - cb_data->timeout_id = g_timeout_add_seconds (30, timeout_cb, cb_data); - - _LOG2D ("sending request to '%s'", priv->uri); - return; - } else { - _LOGD ("(%s) faking request. Connectivity check disabled", iface); + +#if WITH_CONCHECK + if (iface) { + CURL *ehandle; + + if ( priv->enabled + && (ehandle = curl_easy_init ())) { + + cb_data->concheck.response = g_strdup (priv->response); + cb_data->concheck.curl_ehandle = ehandle; + cb_data->concheck.request_headers = curl_slist_append (NULL, "Connection: close"); + curl_easy_setopt (ehandle, CURLOPT_URL, priv->uri); + curl_easy_setopt (ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb); + curl_easy_setopt (ehandle, CURLOPT_WRITEDATA, cb_data); + curl_easy_setopt (ehandle, CURLOPT_HEADERFUNCTION, easy_header_cb); + curl_easy_setopt (ehandle, CURLOPT_HEADERDATA, cb_data); + curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data); + curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->concheck.request_headers); + curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec); + curl_multi_add_handle (priv->concheck.curl_mhandle, ehandle); + + cb_data->timeout_id = g_timeout_add_seconds (20, _timeout_cb, cb_data); + + _LOG2D ("start request to '%s'", priv->uri); + return cb_data; + } } +#endif - g_simple_async_result_set_op_res_gssize (simple, NM_CONNECTIVITY_UNKNOWN); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); + _LOG2D ("start fake request"); + cb_data->timeout_id = g_idle_add (_idle_cb, cb_data); + return cb_data; } -NMConnectivityState -nm_connectivity_check_finish (NMConnectivity *self, - GAsyncResult *result, - GError **error) +void +nm_connectivity_check_cancel (NMConnectivityCheckHandle *cb_data) { - GSimpleAsyncResult *simple; + NMConnectivity *self; + gs_free_error GError *error = NULL; + + g_return_if_fail (cb_data); + + self = cb_data->self; - g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), nm_connectivity_check_async), NM_CONNECTIVITY_UNKNOWN); + g_return_if_fail (NM_IS_CONNECTIVITY (self)); + g_return_if_fail (!c_list_is_empty (&cb_data->handles_lst)); + g_return_if_fail (cb_data->callback); + + nm_assert (c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (self)->handles_lst_head, &cb_data->handles_lst)); + + nm_utils_error_set_cancelled (&error, FALSE, "NMConnectivity"); - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) - return NM_CONNECTIVITY_UNKNOWN; - return (NMConnectivityState) g_simple_async_result_get_op_res_gssize (simple); + cb_data_free (cb_data, NM_CONNECTIVITY_ERROR, error, "cancelled"); } +/*****************************************************************************/ + gboolean nm_connectivity_check_enabled (NMConnectivity *self) { - NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); + g_return_val_if_fail (NM_IS_CONNECTIVITY (self), FALSE); - return priv->enabled; + return NM_CONNECTIVITY_GET_PRIVATE (self)->enabled; } /*****************************************************************************/ -static gboolean -periodic_check (gpointer user_data) +guint +nm_connectivity_get_interval (NMConnectivity *self) { - g_signal_emit (NM_CONNECTIVITY (user_data), signals[PERIODIC_CHECK], 0); - return G_SOURCE_CONTINUE; + return nm_connectivity_check_enabled (self) + ? NM_CONNECTIVITY_GET_PRIVATE (self)->interval + : 0; } static void @@ -455,18 +590,22 @@ update_config (NMConnectivity *self, NMConfigData *config_data) /* Set the interval. */ interval = nm_config_data_get_connectivity_interval (config_data); + interval = MIN (interval, (7 * 24 * 3600)); if (priv->interval != interval) { priv->interval = interval; changed = TRUE; } - /* Set enabled flag. */ - enabled = nm_config_data_get_connectivity_enabled (config_data); + enabled = FALSE; +#if WITH_CONCHECK /* connectivity checking also requires a valid URI, interval and * curl_mhandle */ - if (!(priv->uri && priv->interval && priv->curl_mhandle)) { - enabled = FALSE; - } + if ( priv->uri + && priv->interval + && priv->concheck.curl_mhandle) + enabled = nm_config_data_get_connectivity_enabled (config_data); +#endif + if (priv->enabled != enabled) { priv->enabled = enabled; changed = TRUE; @@ -474,7 +613,7 @@ update_config (NMConnectivity *self, NMConfigData *config_data) /* Set the response. */ response = nm_config_data_get_connectivity_response (config_data); - if (g_strcmp0 (response, priv->response) != 0) { + if (!nm_streq0 (response, priv->response)) { /* a response %NULL means, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE. Any other response * (including "") is accepted. */ g_free (priv->response); @@ -482,11 +621,8 @@ update_config (NMConnectivity *self, NMConfigData *config_data) changed = TRUE; } - if (changed) { - nm_clear_g_source (&priv->periodic_check_id); - if (nm_connectivity_check_enabled (self)) - priv->periodic_check_id = g_timeout_add_seconds (priv->interval, periodic_check, self); - } + if (changed) + g_signal_emit (self, signals[CONFIG_CHANGED], 0); } static void @@ -499,34 +635,37 @@ config_changed_cb (NMConfig *config, update_config (self, config_data); } +/*****************************************************************************/ + static void nm_connectivity_init (NMConnectivity *self) { NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - CURLcode retv; - retv = curl_global_init (CURL_GLOBAL_ALL); - if (retv == CURLE_OK) - priv->curl_mhandle = curl_multi_init (); - - if (!priv->curl_mhandle) - _LOGE ("unable to init cURL, connectivity check will not work"); - else { - curl_multi_setopt (priv->curl_mhandle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb); - curl_multi_setopt (priv->curl_mhandle, CURLMOPT_SOCKETDATA, self); - curl_multi_setopt (priv->curl_mhandle, CURLMOPT_TIMERFUNCTION, multi_timer_cb); - curl_multi_setopt (priv->curl_mhandle, CURLMOPT_TIMERDATA, self); - curl_multi_setopt (priv->curl_mhandle, CURLOPT_VERBOSE, 1); - } + c_list_init (&priv->handles_lst_head); priv->config = g_object_ref (nm_config_get ()); - - update_config (self, nm_config_get_data (priv->config)); g_signal_connect (G_OBJECT (priv->config), NM_CONFIG_SIGNAL_CONFIG_CHANGED, G_CALLBACK (config_changed_cb), self); +#if WITH_CONCHECK + if (curl_global_init (CURL_GLOBAL_ALL) == CURLE_OK) + priv->concheck.curl_mhandle = curl_multi_init (); + + if (!priv->concheck.curl_mhandle) + _LOGE ("unable to init cURL, connectivity check will not work"); + else { + curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb); + curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_SOCKETDATA, self); + curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_TIMERFUNCTION, multi_timer_cb); + curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_TIMERDATA, self); + curl_multi_setopt (priv->concheck.curl_mhandle, CURLOPT_VERBOSE, 1); + } +#endif + + update_config (self, nm_config_get_data (priv->config)); } static void @@ -534,19 +673,33 @@ dispose (GObject *object) { NMConnectivity *self = NM_CONNECTIVITY (object); NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); + NMConnectivityCheckHandle *cb_data; + GError *error = NULL; + +again: + c_list_for_each_entry (cb_data, &priv->handles_lst_head, handles_lst) { + if (!error) + nm_utils_error_set_cancelled (&error, TRUE, "NMConnectivity"); + cb_data_free (cb_data, NM_CONNECTIVITY_ERROR, error, "shutting down"); + goto again; + } + g_clear_error (&error); g_clear_pointer (&priv->uri, g_free); g_clear_pointer (&priv->response, g_free); +#if WITH_CONCHECK + nm_clear_g_source (&priv->concheck.curl_timer); + + curl_multi_cleanup (priv->concheck.curl_mhandle); + curl_global_cleanup (); +#endif + if (priv->config) { g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self); g_clear_object (&priv->config); } - curl_multi_cleanup (priv->curl_mhandle); - curl_global_cleanup (); - nm_clear_g_source (&priv->periodic_check_id); - G_OBJECT_CLASS (nm_connectivity_parent_class)->dispose (object); } @@ -555,8 +708,8 @@ nm_connectivity_class_init (NMConnectivityClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - signals[PERIODIC_CHECK] = - g_signal_new (NM_CONNECTIVITY_PERIODIC_CHECK, + signals[CONFIG_CHANGED] = + g_signal_new (NM_CONNECTIVITY_CONFIG_CHANGED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h index d9a9f233..df9295e0 100644 --- a/src/nm-connectivity.h +++ b/src/nm-connectivity.h @@ -24,6 +24,9 @@ #include "nm-dbus-interface.h" +#define NM_CONNECTIVITY_ERROR ((NMConnectivityState) -1) +#define NM_CONNECTIVITY_FAKE ((NMConnectivityState) -2) + #define NM_TYPE_CONNECTIVITY (nm_connectivity_get_type ()) #define NM_CONNECTIVITY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CONNECTIVITY, NMConnectivity)) #define NM_CONNECTIVITY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_CONNECTIVITY, NMConnectivityClass)) @@ -31,7 +34,7 @@ #define NM_IS_CONNECTIVITY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CONNECTIVITY)) #define NM_CONNECTIVITY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CONNECTIVITY, NMConnectivityClass)) -#define NM_CONNECTIVITY_PERIODIC_CHECK "nm-connectivity-periodic-check" +#define NM_CONNECTIVITY_CONFIG_CHANGED "config-changed" typedef struct _NMConnectivityClass NMConnectivityClass; @@ -41,13 +44,23 @@ NMConnectivity *nm_connectivity_get (void); const char *nm_connectivity_state_to_string (NMConnectivityState state); -void nm_connectivity_check_async (NMConnectivity *self, - const char *iface, - GAsyncReadyCallback callback, - gpointer user_data); -NMConnectivityState nm_connectivity_check_finish (NMConnectivity *self, - GAsyncResult *result, - GError **error); gboolean nm_connectivity_check_enabled (NMConnectivity *self); +guint nm_connectivity_get_interval (NMConnectivity *self); + +typedef struct _NMConnectivityCheckHandle NMConnectivityCheckHandle; + +typedef void (*NMConnectivityCheckCallback) (NMConnectivity *self, + NMConnectivityCheckHandle *handle, + NMConnectivityState state, + GError *error, + gpointer user_data); + +NMConnectivityCheckHandle *nm_connectivity_check_start (NMConnectivity *self, + const char *iface, + NMConnectivityCheckCallback callback, + gpointer user_data); + +void nm_connectivity_check_cancel (NMConnectivityCheckHandle *handle); + #endif /* __NETWORKMANAGER_CONNECTIVITY_H__ */ diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index f6b33a14..b1a4cc25 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -452,83 +452,6 @@ nm_utils_modprobe (GError **error, gboolean suppress_error_logging, const char * return exit_status; } -/** - * nm_utils_get_start_time_for_pid: - * @pid: the process identifier - * @out_state: return the state character, like R, S, Z. See `man 5 proc`. - * @out_ppid: parent process id - * - * Originally copied from polkit source (src/polkit/polkitunixprocess.c) - * and adjusted. - * - * Returns: the timestamp when the process started (by parsing /proc/$PID/stat). - * If an error occurs (e.g. the process does not exist), 0 is returned. - * - * The returned start time counts since boot, in the unit HZ (with HZ usually being (1/100) seconds) - **/ -guint64 -nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid) -{ - guint64 start_time; - char filename[256]; - gs_free gchar *contents = NULL; - size_t length; - gs_strfreev gchar **tokens = NULL; - guint num_tokens; - gchar *p; - char state = ' '; - gint64 ppid = 0; - - start_time = 0; - contents = NULL; - - g_return_val_if_fail (pid > 0, 0); - - nm_sprintf_buf (filename, "/proc/%"G_GUINT64_FORMAT"/stat", (guint64) pid); - - if (!g_file_get_contents (filename, &contents, &length, NULL)) - goto fail; - - /* start time is the token at index 19 after the '(process name)' entry - since only this - * field can contain the ')' character, search backwards for this to avoid malicious - * processes trying to fool us - */ - p = strrchr (contents, ')'); - if (p == NULL) - goto fail; - p += 2; /* skip ') ' */ - if (p - contents >= (int) length) - goto fail; - - state = p[0]; - - tokens = g_strsplit (p, " ", 0); - - num_tokens = g_strv_length (tokens); - - if (num_tokens < 20) - goto fail; - - if (out_ppid) { - ppid = _nm_utils_ascii_str_to_int64 (tokens[1], 10, 1, G_MAXINT, 0); - if (ppid == 0) - goto fail; - } - - start_time = _nm_utils_ascii_str_to_int64 (tokens[19], 10, 1, G_MAXINT64, 0); - if (start_time == 0) - goto fail; - - NM_SET_OUT (out_state, state); - NM_SET_OUT (out_ppid, ppid); - return start_time; - -fail: - NM_SET_OUT (out_state, ' '); - NM_SET_OUT (out_ppid, 0); - return 0; -} - /*****************************************************************************/ typedef struct { @@ -1246,8 +1169,8 @@ typedef struct { static gboolean match_device_s390_subchannels_parse (const char *s390_subchannels, guint32 *out_a, guint32 *out_b, guint32 *out_c) { - const int BUFSIZE = 30; - char buf[BUFSIZE + 1]; + char buf[30 + 1]; + const int BUFSIZE = G_N_ELEMENTS (buf) - 1; guint i = 0; char *pa = NULL, *pb = NULL, *pc = NULL; gint64 a, b, c; @@ -1877,110 +1800,6 @@ nm_utils_new_infiniband_name (char *name, const char *parent_name, int p_key) /*****************************************************************************/ -gboolean -nm_utils_resolve_conf_parse (int addr_family, - const char *rc_contents, - GArray *nameservers, - GPtrArray *dns_options) -{ - guint i; - gboolean changed = FALSE; - gs_free const char **lines = NULL; - gsize l; - - g_return_val_if_fail (rc_contents, FALSE); - g_return_val_if_fail (nameservers, FALSE); - g_return_val_if_fail ( ( addr_family == AF_INET - && g_array_get_element_size (nameservers) == sizeof (in_addr_t)) - || ( addr_family == AF_INET6 - && g_array_get_element_size (nameservers) == sizeof (struct in6_addr)), FALSE); - - lines = nm_utils_strsplit_set (rc_contents, "\r\n"); - if (!lines) - return FALSE; - -/* like glibc's MATCH() macro in resolv/res_init.c. */ -#define RC_MATCH(line, option, out_arg) \ - ({ \ - const char *const _line = (line); \ - gboolean _match = FALSE; \ - \ - if ( (strncmp (_line, option, NM_STRLEN (option)) == 0) \ - && (NM_IN_SET (_line[NM_STRLEN (option)], ' ', '\t'))) { \ - _match = TRUE;\ - (out_arg) = &_line[NM_STRLEN (option) + 1]; \ - } \ - _match; \ - }) - - for (l = 0; lines[l]; l++) { - const char *const line = lines[l]; - const char *s = NULL; - - if (RC_MATCH (line, "nameserver", s)) { - gs_free char *s_cpy = NULL; - NMIPAddr ns; - - s = nm_strstrip_avoid_copy (s, &s_cpy); - if (inet_pton (addr_family, s, &ns) != 1) - continue; - - if (addr_family == AF_INET) { - if (!ns.addr4) - continue; - for (i = 0; i < nameservers->len; i++) { - if (g_array_index (nameservers, guint32, i) == ns.addr4) - break; - } - } else { - if (IN6_IS_ADDR_UNSPECIFIED (&ns.addr6)) - continue; - for (i = 0; i < nameservers->len; i++) { - struct in6_addr *t = &g_array_index (nameservers, struct in6_addr, i); - - if (IN6_ARE_ADDR_EQUAL (t, &ns.addr6)) - break; - } - } - - if (i == nameservers->len) { - g_array_append_val (nameservers, ns); - changed = TRUE; - } - continue; - } - - if (RC_MATCH (line, "options", s)) { - if (!dns_options) - continue; - - s = nm_str_skip_leading_spaces (s); - if (s[0]) { - gs_free const char **tokens = NULL; - gsize i_tokens; - - tokens = nm_utils_strsplit_set (s, " \t"); - for (i_tokens = 0; tokens && tokens[i_tokens]; i_tokens++) { - gs_free char *t = g_strstrip (g_strdup (tokens[i_tokens])); - - if ( _nm_utils_dns_option_validate (t, NULL, NULL, - addr_family == AF_INET6, - _nm_utils_dns_option_descs) - && _nm_utils_dns_option_find_idx (dns_options, t) < 0) { - g_ptr_array_add (dns_options, g_steal_pointer (&t)); - changed = TRUE; - } - } - } - continue; - } - } - - return changed; -} - -/*****************************************************************************/ - /** * nm_utils_cmp_connection_by_autoconnect_priority: * @a: @@ -2225,8 +2044,8 @@ _log_connection_sort_hashes_fcn (gconstpointer a, gconstpointer b) NMSettingPriority p1, p2; NMSetting *s1, *s2; - s1 = v1->setting ? v1->setting : v1->diff_base_setting; - s2 = v2->setting ? v2->setting : v2->diff_base_setting; + s1 = v1->setting ?: v1->diff_base_setting; + s2 = v2->setting ?: v2->diff_base_setting; g_assert (s1 && s2); @@ -2338,7 +2157,13 @@ _log_connection_sort_names (LogConnectionSettingData *setting_data, GArray *sort } void -nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix) +nm_utils_log_connection_diff (NMConnection *connection, + NMConnection *diff_base, + guint32 level, + guint64 domain, + const char *name, + const char *prefix, + const char *dbus_path) { GHashTable *connection_diff = NULL; GArray *sorted_hashes; @@ -2414,7 +2239,6 @@ nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, if (print_header) { GError *err_verify = NULL; - const char *path = nm_connection_get_path (connection); const char *t1, *t2; t1 = nm_connection_get_connection_type (connection); @@ -2424,12 +2248,12 @@ nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, prefix, name, connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1), diff_base, G_OBJECT_TYPE_NAME (diff_base), NM_PRINT_FMT_QUOTE_STRING (t2), - NM_PRINT_FMT_QUOTED (path, " [", path, "]", "")); + NM_PRINT_FMT_QUOTED (dbus_path, " [", dbus_path, "]", "")); } else { nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s/%s%s%s):%s%s%s", prefix, name, connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1), - NM_PRINT_FMT_QUOTED (path, " [", path, "]", "")); + NM_PRINT_FMT_QUOTED (dbus_path, " [", dbus_path, "]", "")); } print_header = FALSE; @@ -2455,13 +2279,13 @@ nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, g_string_printf (str1, "%s.%s", setting_data->name, item->item_name); switch (item->diff_result & (NM_SETTING_DIFF_RESULT_IN_A | NM_SETTING_DIFF_RESULT_IN_B)) { case NM_SETTING_DIFF_RESULT_IN_B: - nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s < %s", prefix, str1->str, str_diff ? str_diff : "NULL"); + nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s < %s", prefix, str1->str, str_diff ?: "NULL"); break; case NM_SETTING_DIFF_RESULT_IN_A: - nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s = %s", prefix, str1->str, str_conn ? str_conn : "NULL"); + nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s = %s", prefix, str1->str, str_conn ?: "NULL"); break; default: - nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s = %s < %s", prefix, str1->str, str_conn ? str_conn : "NULL", str_diff ? str_diff : "NULL"); + nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s = %s < %s", prefix, str1->str, str_conn ?: "NULL", str_diff ?: "NULL"); break; #undef _NM_LOG_ALIGN } @@ -2520,7 +2344,6 @@ nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_ return timestamp - offset; } - #define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/" #define IPV4_PROPERTY_DIR "/proc/sys/net/ipv4/conf/" G_STATIC_ASSERT (sizeof (IPV4_PROPERTY_DIR) == sizeof (IPV6_PROPERTY_DIR)); @@ -2747,7 +2570,7 @@ _get_contents_error (GError **error, int errsv, const char *format, ...) /** * nm_utils_fd_get_contents: * @fd: open file descriptor to read. The fd will not be closed, - * but don't rely on it's state afterwards. + * but don't rely on its state afterwards. * @close_fd: if %TRUE, @fd will be closed by the function. * Passing %TRUE here might safe a syscall for dup(). * @max_length: allocate at most @max_length bytes. If the @@ -2974,53 +2797,103 @@ nm_utils_file_get_contents (int dirfd, /*****************************************************************************/ -guint8 * -nm_utils_secret_key_read (gsize *out_key_len, GError **error) +static gboolean +_secret_key_read (guint8 **out_secret_key, + gsize *out_key_len) { - guint8 *secret_key = NULL; + guint8 *secret_key; + gboolean success = TRUE; gsize key_len; - - /* out_key_len is not optional, because without it you cannot safely - * access the returned memory. */ - *out_key_len = 0; + gs_free_error GError *error = NULL; /* Let's try to load a saved secret key first. */ - if (g_file_get_contents (NMSTATEDIR "/secret_key", (char **) &secret_key, &key_len, NULL)) { - if (key_len < 16) { - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "Key is too short to be usable"); - key_len = 0; - } + if (g_file_get_contents (NMSTATEDIR "/secret_key", (char **) &secret_key, &key_len, &error)) { + if (key_len >= 16) + goto out; + + /* the secret key is borked. Log a warning, but proceed below to generate + * a new one. */ + nm_log_warn (LOGD_CORE, "secret-key: too short secret key in \"%s\" (generate new key)", NMSTATEDIR "/secret_key"); + nm_clear_g_free (&secret_key); } else { - mode_t key_mask; + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { + nm_log_warn (LOGD_CORE, "secret-key: failure reading secret key in \"%s\": %s (generate new key)", + NMSTATEDIR "/secret_key", error->message); + } + g_clear_error (&error); + } - /* RFC7217 mandates the key SHOULD be at least 128 bits. - * Let's use twice as much. */ - key_len = 32; - secret_key = g_malloc (key_len); + /* RFC7217 mandates the key SHOULD be at least 128 bits. + * Let's use twice as much. */ + key_len = 32; + secret_key = g_malloc (key_len + 1); - if (!nm_utils_random_bytes (secret_key, key_len)) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "Can't get random data to generate secret key"); - key_len = 0; - goto out; - } + /* the secret-key is binary. Still, ensure that it's NULL terminated, just like + * g_file_set_contents() does. */ + secret_key[32] = '\0'; - key_mask = umask (0077); - if (!g_file_set_contents (NMSTATEDIR "/secret_key", (char *) secret_key, key_len, error)) { - g_prefix_error (error, "Can't write " NMSTATEDIR "/secret_key: "); - key_len = 0; - } - umask (key_mask); + if (!nm_utils_random_bytes (secret_key, key_len)) { + nm_log_warn (LOGD_CORE, "secret-key: failure to generate good random data for secret-key (use non-persistent key)"); + success = FALSE; + goto out; + } + + if (!nm_utils_file_set_contents (NMSTATEDIR "/secret_key", (char *) secret_key, key_len, 0077, &error)) { + nm_log_warn (LOGD_CORE, "secret-key: failure to persist secret key in \"%s\" (%s) (use non-persistent key)", + NMSTATEDIR "/secret_key", error->message); + success = FALSE; + goto out; } out: - if (key_len) { - *out_key_len = key_len; - return secret_key; + /* regardless of success or failue, we always return a secret-key. The + * caller may choose to ignore the error and proceed. */ + *out_key_len = key_len; + *out_secret_key = secret_key; + return success; +} + +typedef struct { + const guint8 *secret_key; + gsize key_len; + bool is_good:1; +} SecretKeyData; + +gboolean +nm_utils_secret_key_get (const guint8 **out_secret_key, + gsize *out_key_len) +{ + static volatile const SecretKeyData *secret_key_static; + const SecretKeyData *secret_key; + + secret_key = g_atomic_pointer_get (&secret_key_static); + if (G_UNLIKELY (!secret_key)) { + static gsize init_value = 0; + static SecretKeyData secret_key_data; + gboolean tmp_success; + gs_free guint8 *tmp_secret_key = NULL; + gsize tmp_key_len; + + tmp_success = _secret_key_read (&tmp_secret_key, &tmp_key_len); + if (g_once_init_enter (&init_value)) { + secret_key_data.secret_key = tmp_secret_key; + secret_key_data.key_len = tmp_key_len; + secret_key_data.is_good = tmp_success; + + if (g_atomic_pointer_compare_and_exchange (&secret_key_static, NULL, &secret_key_data)) { + g_steal_pointer (&tmp_secret_key); + secret_key = &secret_key_data; + } + + g_once_init_leave (&init_value, 1); + } + if (!secret_key) + secret_key = g_atomic_pointer_get (&secret_key_static); } - g_free (secret_key); - return NULL; + + *out_secret_key = secret_key->secret_key; + *out_key_len = secret_key->key_len; + return secret_key->is_good; } /*****************************************************************************/ @@ -3308,8 +3181,9 @@ _stable_id_append (GString *str, NMUtilsStableType nm_utils_stable_id_parse (const char *stable_id, - const char *uuid, + const char *deviceid, const char *bootid, + const char *uuid, char **out_generated) { gsize i, idx_start; @@ -3384,6 +3258,8 @@ nm_utils_stable_id_parse (const char *stable_id, _stable_id_append (str, uuid); else if (CHECK_PREFIX ("${BOOT}")) _stable_id_append (str, bootid ?: nm_utils_get_boot_id ()); + else if (CHECK_PREFIX ("${DEVICE}")) + _stable_id_append (str, deviceid); else if (g_str_has_prefix (&stable_id[i], "${RANDOM}")) { /* RANDOM makes not so much sense for cloned-mac-address * as the result is simmilar to specyifing "cloned-mac-address=random". @@ -3460,7 +3336,7 @@ _set_stable_privacy (NMUtilsStableType stable_type, const char *ifname, const char *network_id, guint32 dad_counter, - guint8 *secret_key, + const guint8 *secret_key, gsize key_len, GError **error) { @@ -3559,8 +3435,8 @@ nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type, guint32 dad_counter, GError **error) { - gs_free guint8 *secret_key = NULL; - gsize key_len = 0; + const guint8 *secret_key; + gsize key_len; g_return_val_if_fail (network_id, FALSE); @@ -3570,9 +3446,7 @@ nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type, return FALSE; } - secret_key = nm_utils_secret_key_read (&key_len, error); - if (!secret_key) - return FALSE; + nm_utils_secret_key_get (&secret_key, &key_len); return _set_stable_privacy (stable_type, addr, ifname, network_id, dad_counter, secret_key, key_len, error); @@ -3708,14 +3582,12 @@ nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, const char *current_mac_address, const char *generate_mac_address_mask) { - gs_free guint8 *secret_key = NULL; - gsize key_len = 0; + const guint8 *secret_key; + gsize key_len; g_return_val_if_fail (stable_id, NULL); - secret_key = nm_utils_secret_key_read (&key_len, NULL); - if (!secret_key) - return NULL; + nm_utils_secret_key_get (&secret_key, &key_len); return _hw_addr_gen_stable_eth (stable_type, stable_id, @@ -3749,7 +3621,8 @@ nm_utils_setpgid (gpointer unused G_GNUC_UNUSED) /** * nm_utils_g_value_set_strv: * @value: a #GValue, initialized to store a #G_TYPE_STRV - * @strings: a #GPtrArray of strings + * @strings: a #GPtrArray of strings. %NULL values are not + * allowed. * * Converts @strings to a #GStrv and stores it in @value. */ @@ -3757,11 +3630,13 @@ void nm_utils_g_value_set_strv (GValue *value, GPtrArray *strings) { char **strv; - int i; + guint i; strv = g_new (char *, strings->len + 1); - for (i = 0; i < strings->len; i++) + for (i = 0; i < strings->len; i++) { + nm_assert (strings->pdata[i]); strv[i] = g_strdup (strings->pdata[i]); + } strv[i] = NULL; g_value_take_boxed (value, strv); @@ -3891,12 +3766,11 @@ nm_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp, return t; } -gboolean +guint32 nm_utils_lifetime_get (guint32 timestamp, guint32 lifetime, guint32 preferred, gint32 now, - guint32 *out_lifetime, guint32 *out_preferred) { guint32 t_lifetime, t_preferred; @@ -3904,38 +3778,39 @@ nm_utils_lifetime_get (guint32 timestamp, nm_assert (now >= 0); if (timestamp == 0 && lifetime == 0) { - /* We treat lifetime==0 && timestamp == 0 addresses as permanent addresses to allow easy + /* We treat lifetime==0 && timestamp==0 addresses as permanent addresses to allow easy * creation of such addresses (without requiring to set the lifetime fields to * NM_PLATFORM_LIFETIME_PERMANENT). The real lifetime==0 addresses (E.g. DHCP6 telling us * to drop an address will have timestamp set. */ - *out_lifetime = NM_PLATFORM_LIFETIME_PERMANENT; - *out_preferred = NM_PLATFORM_LIFETIME_PERMANENT; - g_return_val_if_fail (preferred == 0, TRUE); - } else { - if (now <= 0) - now = nm_utils_get_monotonic_timestamp_s (); - t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now); - if (!t_lifetime) { - *out_lifetime = 0; - *out_preferred = 0; - return FALSE; - } - t_preferred = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now); + NM_SET_OUT (out_preferred, NM_PLATFORM_LIFETIME_PERMANENT); + g_return_val_if_fail (preferred == 0, NM_PLATFORM_LIFETIME_PERMANENT); + return NM_PLATFORM_LIFETIME_PERMANENT; + } - *out_lifetime = t_lifetime; - *out_preferred = MIN (t_preferred, t_lifetime); + if (now <= 0) + now = nm_utils_get_monotonic_timestamp_s (); - /* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now() - * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always - * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew. - */ - g_return_val_if_fail ( timestamp != 0 - || ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT - && preferred == NM_PLATFORM_LIFETIME_PERMANENT), TRUE); - g_return_val_if_fail (t_preferred <= t_lifetime, TRUE); + t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now); + if (!t_lifetime) { + NM_SET_OUT (out_preferred, 0); + return 0; } - return TRUE; + + t_preferred = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now); + + NM_SET_OUT (out_preferred, MIN (t_preferred, t_lifetime)); + + /* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now() + * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always + * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew. + */ + g_return_val_if_fail ( timestamp != 0 + || ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT + && preferred == NM_PLATFORM_LIFETIME_PERMANENT), t_lifetime); + g_return_val_if_fail (t_preferred <= t_lifetime, t_lifetime); + + return t_lifetime; } const char * @@ -4260,7 +4135,7 @@ nm_utils_read_plugin_paths (const char *dirname, const char *prefix) if (!g_str_has_prefix (item, prefix)) continue; - if (g_str_has_suffix (item, ".la")) + if (!g_str_has_suffix (item, ".so")) continue; data.path = g_build_filename (dirname, item, NULL); @@ -4329,6 +4204,43 @@ nm_utils_format_con_diff_for_audit (GHashTable *diff) return g_string_free (str, FALSE); } +const char * +nm_utils_parse_dns_domain (const char *domain, gboolean *is_routing) +{ + g_return_val_if_fail (domain, NULL); + g_return_val_if_fail (domain[0], NULL); + + if (domain[0] == '~') { + domain++; + NM_SET_OUT (is_routing, TRUE); + } else + NM_SET_OUT (is_routing, FALSE); + + return domain; +} + +/*****************************************************************************/ + +GVariant * +nm_utils_strdict_to_variant (GHashTable *options) +{ + GVariantBuilder builder; + gs_free const char **keys = NULL; + guint i; + guint nkeys; + + keys = nm_utils_strdict_get_keys (options, TRUE, &nkeys); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + for (i = 0; i < nkeys; i++) { + g_variant_builder_add (&builder, + "{sv}", + keys[i], + g_variant_new_string (g_hash_table_lookup (options, keys[i]))); + } + return g_variant_builder_end (&builder); +} + /*****************************************************************************/ NM_UTILS_ENUM2STR_DEFINE (nm_icmpv6_router_pref_to_string, NMIcmpv6RouterPref, diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index cc784724..7f406d21 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -25,8 +25,6 @@ #include #include -#include "nm-utils/nm-hash-utils.h" - #include "nm-connection.h" /*****************************************************************************/ @@ -160,7 +158,7 @@ double nm_utils_exp10 (gint16 e); static inline guint32 nm_utils_ip6_route_metric_normalize (guint32 metric) { - return metric ? metric : 1024 /*NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6*/; + return metric ?: 1024 /*NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6*/; } static inline guint32 @@ -180,8 +178,6 @@ nm_utils_ip_route_metric_penalize (int addr_family, guint32 metric, guint32 pena int nm_utils_modprobe (GError **error, gboolean suppress_error_loggin, const char *arg1, ...) G_GNUC_NULL_TERMINATED; -guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid); - void nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, guint64 log_domain, const char *log_name, guint32 wait_before_kill_msec, guint32 sleep_duration_msec, guint32 max_wait_msec); @@ -231,14 +227,14 @@ gboolean nm_utils_connection_has_default_route (NMConnection *connection, char *nm_utils_new_vlan_name (const char *parent_iface, guint32 vlan_id); const char *nm_utils_new_infiniband_name (char *name, const char *parent_name, int p_key); -gboolean nm_utils_resolve_conf_parse (int addr_family, - const char *rc_contents, - GArray *nameservers, - GPtrArray *dns_options); - int nm_utils_cmp_connection_by_autoconnect_priority (NMConnection *a, NMConnection *b); -void nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix); +void nm_utils_log_connection_diff (NMConnection *connection, + NMConnection *diff_base, + guint32 level, guint64 domain, + const char *name, + const char *prefix, + const char *dbus_path); gint64 nm_utils_get_monotonic_timestamp_ns (void); gint64 nm_utils_get_monotonic_timestamp_us (void); @@ -246,6 +242,13 @@ gint64 nm_utils_get_monotonic_timestamp_ms (void); gint32 nm_utils_get_monotonic_timestamp_s (void); gint64 nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ticks_per_ns); +static inline gint64 +nm_utils_get_monotonic_timestamp_ns_cached (gint64 *cache_now) +{ + return (*cache_now) + ?: (*cache_now = nm_utils_get_monotonic_timestamp_ns ()); +} + gboolean nm_utils_is_valid_path_component (const char *name); const char *NM_ASSERT_VALID_PATH_COMPONENT (const char *name); @@ -280,7 +283,8 @@ gboolean nm_utils_file_set_contents (const gchar *filename, char *nm_utils_machine_id_read (void); gboolean nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid); -guint8 *nm_utils_secret_key_read (gsize *out_key_len, GError **error); +gboolean nm_utils_secret_key_get (const guint8 **out_secret_key, + gsize *out_key_len); const char *nm_utils_get_boot_id (void); @@ -336,8 +340,9 @@ typedef enum { } NMUtilsStableType; NMUtilsStableType nm_utils_stable_id_parse (const char *stable_id, - const char *uuid, + const char *deviceid, const char *bootid, + const char *uuid, char **out_generated); char *nm_utils_stable_id_random (void); @@ -410,12 +415,11 @@ guint32 nm_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp, guint32 duration, gint32 now); -gboolean nm_utils_lifetime_get (guint32 timestamp, - guint32 lifetime, - guint32 preferred, - gint32 now, - guint32 *out_lifetime, - guint32 *out_preferred); +guint32 nm_utils_lifetime_get (guint32 timestamp, + guint32 lifetime, + guint32 preferred, + gint32 now, + guint32 *out_preferred); gboolean nm_utils_ip4_address_is_link_local (in_addr_t addr); @@ -430,6 +434,8 @@ gboolean nm_utils_validate_plugin (const char *path, struct stat *stat, GError * char **nm_utils_read_plugin_paths (const char *dirname, const char *prefix); char *nm_utils_format_con_diff_for_audit (GHashTable *diff); +GVariant *nm_utils_strdict_to_variant (GHashTable *options); + /*****************************************************************************/ /* this enum is compatible with ICMPV6_ROUTER_PREF_* (from , @@ -450,4 +456,6 @@ const char *nm_activation_type_to_string (NMActivationType activation_type); /*****************************************************************************/ +const char *nm_utils_parse_dns_domain (const char *domain, gboolean *is_routing); + #endif /* __NM_CORE_UTILS_H__ */ diff --git a/src/nm-dbus-manager.c b/src/nm-dbus-manager.c new file mode 100644 index 00000000..7f1121f6 --- /dev/null +++ b/src/nm-dbus-manager.c @@ -0,0 +1,1673 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2013 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include "nm-default.h" + +#include "nm-dbus-manager.h" + +#include +#include +#include +#include +#include + +#include "c-list/src/c-list.h" +#include "nm-dbus-interface.h" +#include "nm-core-internal.h" +#include "nm-dbus-compat.h" +#include "nm-dbus-object.h" +#include "NetworkManagerUtils.h" + +/* The base path for our GDBusObjectManagerServers. They do not contain + * "NetworkManager" because GDBusObjectManagerServer requires that all + * exported objects be *below* the base path, and eg the Manager object + * is the base path already. + */ +#define OBJECT_MANAGER_SERVER_BASE_PATH "/org/freedesktop" + +/*****************************************************************************/ + +typedef struct { + GVariant *value; +} PropertyCacheData; + +typedef struct { + CList registration_lst; + NMDBusObject *obj; + NMDBusObjectClass *klass; + guint info_idx; + guint registration_id; + PropertyCacheData property_cache[]; +} RegistrationData; + +/* we require that @path is the first member of NMDBusManagerData + * because _objects_by_path_hash() requires that. */ +G_STATIC_ASSERT (G_STRUCT_OFFSET (struct _NMDBusObjectInternal, path) == 0); + +enum { + PRIVATE_CONNECTION_NEW, + PRIVATE_CONNECTION_DISCONNECTED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +typedef struct { + GHashTable *objects_by_path; + CList objects_lst_head; + + CList private_servers_lst_head; + + NMDBusManagerSetPropertyHandler set_property_handler; + gpointer set_property_handler_data; + + GDBusConnection *connection; + GDBusProxy *proxy; + guint objmgr_registration_id; + bool started:1; + bool shutting_down:1; +} NMDBusManagerPrivate; + +struct _NMDBusManager { + GObject parent; + NMDBusManagerPrivate _priv; +}; + +struct _NMDBusManagerClass { + GObjectClass parent; +}; + +G_DEFINE_TYPE(NMDBusManager, nm_dbus_manager, G_TYPE_OBJECT) + +#define NM_DBUS_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDBusManager, NM_IS_DBUS_MANAGER) + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_CORE +#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bus-manager", __VA_ARGS__) + +NM_DEFINE_SINGLETON_GETTER (NMDBusManager, nm_dbus_manager_get, NM_TYPE_DBUS_MANAGER); + +/*****************************************************************************/ + +static const GDBusInterfaceInfo interface_info_objmgr; +static const GDBusSignalInfo signal_info_objmgr_interfaces_added; +static const GDBusSignalInfo signal_info_objmgr_interfaces_removed; +static GVariantBuilder *_obj_collect_properties_all (NMDBusObject *obj, + GVariantBuilder *builder); + +/*****************************************************************************/ + +static guint +_objects_by_path_hash (gconstpointer user_data) +{ + const char *const*p_data = user_data; + + nm_assert (p_data); + nm_assert (*p_data); + nm_assert ((*p_data)[0] == '/'); + + return nm_hash_str (*p_data); +} + +static gboolean +_objects_by_path_equal (gconstpointer user_data_a, gconstpointer user_data_b) +{ + const char *const*p_data_a = user_data_a; + const char *const*p_data_b = user_data_b; + + nm_assert (p_data_a); + nm_assert (*p_data_a); + nm_assert ((*p_data_a)[0] == '/'); + nm_assert (p_data_b); + nm_assert (*p_data_b); + nm_assert ((*p_data_b)[0] == '/'); + + return nm_streq (*p_data_a, *p_data_b); +} + +/*****************************************************************************/ + +typedef struct { + CList private_servers_lst; + + const char *tag; + GQuark detail; + char *address; + GDBusServer *server; + + /* With peer bus connections, we'll get a new connection for each + * client. For each connection we create an ObjectManager for + * that connection to handle exporting our objects. + * + * Note that even for connections that don't export any objects + * we'll still create GDBusObjectManager since that's where we store + * the pointer to the GDBusConnection. + */ + CList object_mgr_lst_head; + + NMDBusManager *manager; +} PrivateServer; + +typedef struct { + CList object_mgr_lst; + GDBusObjectManagerServer *manager; + char *fake_sender; +} ObjectMgrData; + +typedef struct { + GDBusConnection *connection; + PrivateServer *server; + gboolean remote_peer_vanished; +} CloseConnectionInfo; + +/*****************************************************************************/ + +static void +_object_mgr_data_free (ObjectMgrData *obj_mgr_data) +{ + GDBusConnection *connection; + + c_list_unlink_stale (&obj_mgr_data->object_mgr_lst); + + connection = g_dbus_object_manager_server_get_connection (obj_mgr_data->manager); + if (!g_dbus_connection_is_closed (connection)) + g_dbus_connection_close (connection, NULL, NULL, NULL); + g_dbus_object_manager_server_set_connection (obj_mgr_data->manager, NULL); + g_object_unref (obj_mgr_data->manager); + g_object_unref (connection); + + g_free (obj_mgr_data->fake_sender); + + g_slice_free (ObjectMgrData, obj_mgr_data); +} + +/*****************************************************************************/ + +static gboolean +close_connection_in_idle (gpointer user_data) +{ + CloseConnectionInfo *info = user_data; + PrivateServer *server = info->server; + ObjectMgrData *obj_mgr_data, *obj_mgr_data_safe; + + /* Emit this for the manager */ + g_signal_emit (server->manager, + signals[PRIVATE_CONNECTION_DISCONNECTED], + server->detail, + info->connection); + + /* FIXME: there's a bug (754730) in GLib for which the connection + * is marked as closed when the remote peer vanishes but its + * resources are not cleaned up. Work around it by explicitly + * closing the connection in that case. */ + if (info->remote_peer_vanished) + g_dbus_connection_close (info->connection, NULL, NULL, NULL); + + c_list_for_each_entry_safe (obj_mgr_data, obj_mgr_data_safe, &server->object_mgr_lst_head, object_mgr_lst) { + gs_unref_object GDBusConnection *connection = NULL; + + connection = g_dbus_object_manager_server_get_connection (obj_mgr_data->manager); + if (connection == info->connection) { + _object_mgr_data_free (obj_mgr_data); + break; + } + } + + g_object_unref (server->manager); + g_slice_free (CloseConnectionInfo, info); + + return G_SOURCE_REMOVE; +} + +static void +private_server_closed_connection (GDBusConnection *conn, + gboolean remote_peer_vanished, + GError *error, + gpointer user_data) +{ + PrivateServer *s = user_data; + CloseConnectionInfo *info; + + /* Clean up after the connection */ + _LOGD ("(%s) closed connection %p on private socket", s->tag, conn); + + info = g_slice_new0 (CloseConnectionInfo); + info->connection = conn; + info->server = s; + info->remote_peer_vanished = remote_peer_vanished; + + g_object_ref (s->manager); + + /* Delay the close of connection to ensure that D-Bus signals + * are handled */ + g_idle_add (close_connection_in_idle, info); +} + +static gboolean +private_server_new_connection (GDBusServer *server, + GDBusConnection *conn, + gpointer user_data) +{ + PrivateServer *s = user_data; + ObjectMgrData *obj_mgr_data; + static guint32 counter = 0; + GDBusObjectManagerServer *manager; + char *sender; + + g_signal_connect (conn, "closed", G_CALLBACK (private_server_closed_connection), s); + + /* Fake a sender since private connections don't have one */ + sender = g_strdup_printf ("x:y:%d", counter++); + + manager = g_dbus_object_manager_server_new (OBJECT_MANAGER_SERVER_BASE_PATH); + g_dbus_object_manager_server_set_connection (manager, conn); + + obj_mgr_data = g_slice_new (ObjectMgrData); + obj_mgr_data->manager = manager; + obj_mgr_data->fake_sender = sender; + c_list_link_tail (&s->object_mgr_lst_head, &obj_mgr_data->object_mgr_lst); + + _LOGD ("(%s) accepted connection %p on private socket", s->tag, conn); + + /* Emit this for the manager. + * + * It is essential to do this from the "new-connection" signal handler, as + * at that point no messages from the connection are yet processed + * (which avoids races with registering objects). */ + g_signal_emit (s->manager, + signals[PRIVATE_CONNECTION_NEW], + s->detail, + conn, + manager); + return TRUE; +} + +static gboolean +private_server_authorize (GDBusAuthObserver *observer, + GIOStream *stream, + GCredentials *credentials, + gpointer user_data) +{ + return g_credentials_get_unix_user (credentials, NULL) == 0; +} + +static gboolean +private_server_allow_mechanism (GDBusAuthObserver *observer, + const char *mechanism, + gpointer user_data) +{ + return NM_IN_STRSET (mechanism, "EXTERNAL"); +} + +static void +private_server_free (gpointer ptr) +{ + PrivateServer *s = ptr; + ObjectMgrData *obj_mgr_data, *obj_mgr_data_safe; + + c_list_unlink_stale (&s->private_servers_lst); + + unlink (s->address); + g_free (s->address); + + c_list_for_each_entry_safe (obj_mgr_data, obj_mgr_data_safe, &s->object_mgr_lst_head, object_mgr_lst) + _object_mgr_data_free (obj_mgr_data); + + g_dbus_server_stop (s->server); + + g_signal_handlers_disconnect_by_func (s->server, G_CALLBACK (private_server_new_connection), s); + + g_object_unref (s->server); + + g_slice_free (PrivateServer, s); +} + +void +nm_dbus_manager_private_server_register (NMDBusManager *self, + const char *path, + const char *tag) +{ + NMDBusManagerPrivate *priv; + PrivateServer *s; + gs_unref_object GDBusAuthObserver *auth_observer = NULL; + GDBusServer *server; + GError *error = NULL; + gs_free char *address = NULL; + gs_free char *guid = NULL; + + g_return_if_fail (NM_IS_DBUS_MANAGER (self)); + g_return_if_fail (path); + g_return_if_fail (tag); + + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + /* Only one instance per tag; but don't warn */ + c_list_for_each_entry (s, &priv->private_servers_lst_head, private_servers_lst) { + if (nm_streq0 (tag, s->tag)) + return; + } + + unlink (path); + address = g_strdup_printf ("unix:path=%s", path); + + _LOGD ("(%s) creating private socket %s", tag, address); + + guid = g_dbus_generate_guid (); + auth_observer = g_dbus_auth_observer_new (); + g_signal_connect (auth_observer, "authorize-authenticated-peer", + G_CALLBACK (private_server_authorize), NULL); + g_signal_connect (auth_observer, "allow-mechanism", + G_CALLBACK (private_server_allow_mechanism), NULL); + server = g_dbus_server_new_sync (address, + G_DBUS_SERVER_FLAGS_NONE, + guid, + auth_observer, + NULL, &error); + + if (!server) { + _LOGW ("(%s) failed to set up private socket %s: %s", + tag, address, error->message); + g_error_free (error); + return; + } + + s = g_slice_new0 (PrivateServer); + s->address = g_steal_pointer (&address); + s->server = server; + g_signal_connect (server, "new-connection", + G_CALLBACK (private_server_new_connection), s); + + c_list_init (&s->object_mgr_lst_head); + + s->manager = self; + s->detail = g_quark_from_string (tag); + s->tag = g_quark_to_string (s->detail); + + c_list_link_tail (&priv->private_servers_lst_head, &s->private_servers_lst); + + g_dbus_server_start (server); +} + +static const char * +private_server_get_connection_owner (PrivateServer *s, GDBusConnection *connection) +{ + ObjectMgrData *obj_mgr_data; + + nm_assert (s); + nm_assert (G_IS_DBUS_CONNECTION (connection)); + + c_list_for_each_entry (obj_mgr_data, &s->object_mgr_lst_head, object_mgr_lst) { + gs_unref_object GDBusConnection *c = NULL; + + c = g_dbus_object_manager_server_get_connection (obj_mgr_data->manager); + if (c == connection) + return obj_mgr_data->fake_sender; + } + return NULL; +} + +static GDBusConnection * +private_server_get_connection_by_owner (PrivateServer *s, const char *owner) +{ + ObjectMgrData *obj_mgr_data; + + nm_assert (s); + nm_assert (owner); + + c_list_for_each_entry (obj_mgr_data, &s->object_mgr_lst_head, object_mgr_lst) { + if (nm_streq (owner, obj_mgr_data->fake_sender)) + return g_dbus_object_manager_server_get_connection (obj_mgr_data->manager); + } + return NULL; +} + +/*****************************************************************************/ + +static gboolean +_bus_get_unix_pid (NMDBusManager *self, + const char *sender, + gulong *out_pid, + GError **error) +{ + guint32 unix_pid = G_MAXUINT32; + gs_unref_variant GVariant *ret = NULL; + + ret = _nm_dbus_proxy_call_sync (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy, + "GetConnectionUnixProcessID", + g_variant_new ("(s)", sender), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, 2000, + NULL, error); + if (!ret) + return FALSE; + + g_variant_get (ret, "(u)", &unix_pid); + + *out_pid = (gulong) unix_pid; + return TRUE; +} + +static gboolean +_bus_get_unix_user (NMDBusManager *self, + const char *sender, + gulong *out_user, + GError **error) +{ + guint32 unix_uid = G_MAXUINT32; + gs_unref_variant GVariant *ret = NULL; + + ret = _nm_dbus_proxy_call_sync (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy, + "GetConnectionUnixUser", + g_variant_new ("(s)", sender), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, 2000, + NULL, error); + if (!ret) + return FALSE; + + g_variant_get (ret, "(u)", &unix_uid); + + *out_user = (gulong) unix_uid; + return TRUE; +} + +/** + * _get_caller_info(): + * + * Given a GDBus method invocation, or a GDBusConnection + GDBusMessage, + * return the sender and the UID of the sender. + */ +static gboolean +_get_caller_info (NMDBusManager *self, + GDBusMethodInvocation *context, + GDBusConnection *connection, + GDBusMessage *message, + char **out_sender, + gulong *out_uid, + gulong *out_pid) +{ + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + const char *sender; + + if (context) { + connection = g_dbus_method_invocation_get_connection (context); + + /* only bus connections will have a sender */ + sender = g_dbus_method_invocation_get_sender (context); + } else { + g_assert (message); + sender = g_dbus_message_get_sender (message); + } + g_assert (connection); + + if (!sender) { + PrivateServer *s; + + /* Might be a private connection, for which we fake a sender */ + c_list_for_each_entry (s, &priv->private_servers_lst_head, private_servers_lst) { + sender = private_server_get_connection_owner (s, connection); + if (sender) { + if (out_uid) + *out_uid = 0; + if (out_sender) + *out_sender = g_strdup (sender); + if (out_pid) { + GCredentials *creds; + + creds = g_dbus_connection_get_peer_credentials (connection); + if (creds) { + pid_t pid; + + pid = g_credentials_get_unix_pid (creds, NULL); + if (pid == -1) + *out_pid = G_MAXULONG; + else + *out_pid = pid; + } else + *out_pid = G_MAXULONG; + } + return TRUE; + } + } + return FALSE; + } + + /* Bus connections always have a sender */ + g_assert (sender); + if (out_uid) { + if (!_bus_get_unix_user (self, sender, out_uid, NULL)) { + *out_uid = G_MAXULONG; + return FALSE; + } + } + + if (out_pid) { + if (!_bus_get_unix_pid (self, sender, out_pid, NULL)) { + *out_pid = G_MAXULONG; + return FALSE; + } + } + + if (out_sender) + *out_sender = g_strdup (sender); + + return TRUE; +} + +gboolean +nm_dbus_manager_get_caller_info (NMDBusManager *self, + GDBusMethodInvocation *context, + char **out_sender, + gulong *out_uid, + gulong *out_pid) +{ + return _get_caller_info (self, context, NULL, NULL, out_sender, out_uid, out_pid); +} + +gboolean +nm_dbus_manager_get_caller_info_from_message (NMDBusManager *self, + GDBusConnection *connection, + GDBusMessage *message, + char **out_sender, + gulong *out_uid, + gulong *out_pid) +{ + return _get_caller_info (self, NULL, connection, message, out_sender, out_uid, out_pid); +} + +/** + * nm_dbus_manager_ensure_uid: + * + * @self: bus manager instance + * @context: D-Bus method invocation + * @uid: a user-id + * @error_domain: error domain to return on failure + * @error_code: error code to return on failure + * + * Retrieves the uid of the D-Bus method caller and + * checks that it matches @uid, unless @uid is G_MAXULONG. + * In case of failure the function returns FALSE and finishes + * handling the D-Bus method with an error. + * + * Returns: %TRUE if the check succeeded, %FALSE otherwise + */ +gboolean +nm_dbus_manager_ensure_uid (NMDBusManager *self, + GDBusMethodInvocation *context, + gulong uid, + GQuark error_domain, + int error_code) +{ + gulong caller_uid; + GError *error = NULL; + + g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE); + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (context), FALSE); + + if (!nm_dbus_manager_get_caller_info (self, context, NULL, &caller_uid, NULL)) { + error = g_error_new_literal (error_domain, + error_code, + "Unable to determine request UID."); + g_dbus_method_invocation_take_error (context, error); + return FALSE; + } + + if (uid != G_MAXULONG && caller_uid != uid) { + error = g_error_new_literal (error_domain, + error_code, + "Permission denied"); + g_dbus_method_invocation_take_error (context, error); + return FALSE; + } + + return TRUE; +} + +gboolean +nm_dbus_manager_get_unix_user (NMDBusManager *self, + const char *sender, + gulong *out_uid) +{ + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + PrivateServer *s; + GError *error = NULL; + + g_return_val_if_fail (sender != NULL, FALSE); + g_return_val_if_fail (out_uid != NULL, FALSE); + + /* Check if it's a private connection sender, which we fake */ + c_list_for_each_entry (s, &priv->private_servers_lst_head, private_servers_lst) { + gs_unref_object GDBusConnection *connection = NULL; + + connection = private_server_get_connection_by_owner (s, sender); + if (connection) { + *out_uid = 0; + return TRUE; + } + } + + /* Otherwise, a bus connection */ + if (!_bus_get_unix_user (self, sender, out_uid, &error)) { + _LOGW ("failed to get unix user for dbus sender '%s': %s", + sender, error->message); + g_error_free (error); + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +const char * +nm_dbus_manager_connection_get_private_name (NMDBusManager *self, + GDBusConnection *connection) +{ + NMDBusManagerPrivate *priv; + PrivateServer *s; + const char *owner; + + g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE); + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + + if (g_dbus_connection_get_unique_name (connection)) { + /* Shortcut. The connection is not a private connection. */ + return NULL; + } + + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + c_list_for_each_entry (s, &priv->private_servers_lst_head, private_servers_lst) { + if ((owner = private_server_get_connection_owner (s, connection))) + return owner; + } + g_return_val_if_reached (NULL); +} + +/** + * nm_dbus_manager_new_proxy: + * @self: the #NMDBusManager + * @connection: the GDBusConnection for which this connection should be created + * @proxy_type: the type of #GDBusProxy to create + * @name: any name on the message bus + * @path: name of the object instance to call methods on + * @iface: name of the interface to call methods on + * + * Creates a new proxy (of type @proxy_type) for a name on a given bus. Since + * the process which called the D-Bus method could be coming from a private + * connection or the system bus connection, different proxies must be created + * for each case. This function abstracts that. + * + * Returns: a #GDBusProxy capable of calling D-Bus methods of the calling process + */ +GDBusProxy * +nm_dbus_manager_new_proxy (NMDBusManager *self, + GDBusConnection *connection, + GType proxy_type, + const char *name, + const char *path, + const char *iface) +{ + const char *owner; + GDBusProxy *proxy; + GError *error = NULL; + + g_return_val_if_fail (g_type_is_a (proxy_type, G_TYPE_DBUS_PROXY), NULL); + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + + /* Might be a private connection, for which @name is fake */ + owner = nm_dbus_manager_connection_get_private_name (self, connection); + if (owner) { + g_return_val_if_fail (!g_strcmp0 (owner, name), NULL); + name = NULL; + } + + proxy = g_initable_new (proxy_type, NULL, &error, + "g-connection", connection, + "g-flags", (G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS), + "g-name", name, + "g-object-path", path, + "g-interface-name", iface, + NULL); + if (!proxy) { + _LOGW ("could not create proxy for %s on connection %s: %s", + iface, name, error->message); + g_error_free (error); + } + return proxy; +} + +/*****************************************************************************/ + +GDBusConnection * +nm_dbus_manager_get_connection (NMDBusManager *self) +{ + g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL); + + return NM_DBUS_MANAGER_GET_PRIVATE (self)->connection; +} + +/*****************************************************************************/ + +static const NMDBusInterfaceInfoExtended * +_reg_data_get_interface_info (RegistrationData *reg_data) +{ + nm_assert (reg_data); + + return reg_data->klass->interface_infos[reg_data->info_idx]; +} + +/*****************************************************************************/ + +static void +dbus_vtable_method_call (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + NMDBusManager *self; + NMDBusManagerPrivate *priv; + RegistrationData *reg_data = user_data; + NMDBusObject *obj = reg_data->obj; + const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data); + const NMDBusMethodInfoExtended *method_info = NULL; + gboolean on_same_interface; + + on_same_interface = nm_streq (interface_info->parent.name, interface_name); + + /* handle property setter first... */ + if ( !on_same_interface + && nm_streq (interface_name, DBUS_INTERFACE_PROPERTIES) + && nm_streq (method_name, "Set")) { + const NMDBusPropertyInfoExtended *property_info = NULL; + const char *property_interface; + const char *property_name; + gs_unref_variant GVariant *value = NULL; + + self = nm_dbus_object_get_manager (obj); + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + g_variant_get (parameters, "(&s&sv)", &property_interface, &property_name, &value); + + nm_assert (nm_streq (property_interface, interface_info->parent.name)); + + property_info = (const NMDBusPropertyInfoExtended *) nm_dbus_utils_interface_info_lookup_property (&interface_info->parent, + property_name, + NULL); + if ( !property_info + || !NM_FLAGS_HAS (property_info->parent.flags, G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)) + g_return_if_reached (); + + if (!priv->set_property_handler) { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_AUTH_FAILED, + "Cannot authenticate setting property %s", + property_name); + return; + } + + priv->set_property_handler (obj, + interface_info, + property_info, + connection, + sender, + invocation, + value, + priv->set_property_handler_data); + return; + } + + if (on_same_interface) { + method_info = (const NMDBusMethodInfoExtended *) nm_dbus_utils_interface_info_lookup_method (&interface_info->parent, + method_name); + } + if (!method_info) { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method %s", + method_name); + return; + } + + self = nm_dbus_object_get_manager (obj); + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + if ( priv->shutting_down + && !method_info->allow_during_shutdown) { + g_dbus_method_invocation_return_error_literal (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "NetworkManager is exiting"); + return; + } + + method_info->handle (reg_data->obj, + interface_info, + method_info, + connection, + sender, + invocation, + parameters); +} + +static GVariant * +_obj_get_property (RegistrationData *reg_data, + guint property_idx, + gboolean refetch) +{ + const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data); + const NMDBusPropertyInfoExtended *property_info; + GVariant *value; + + property_info = (const NMDBusPropertyInfoExtended *) (interface_info->parent.properties[property_idx]); + + if (refetch) + nm_clear_g_variant (®_data->property_cache[property_idx].value); + else { + value = reg_data->property_cache[property_idx].value; + if (value) + goto out; + } + + value = nm_dbus_utils_get_property (G_OBJECT (reg_data->obj), + property_info->parent.signature, + property_info->property_name); + reg_data->property_cache[property_idx].value = value; +out: + return g_variant_ref (value); +} + +static GVariant * +dbus_vtable_get_property (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *property_name, + GError **error, + gpointer user_data) +{ + RegistrationData *reg_data = user_data; + const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data); + guint property_idx; + + if (!nm_dbus_utils_interface_info_lookup_property (&interface_info->parent, + property_name, + &property_idx)) + g_return_val_if_reached (NULL); + + return _obj_get_property (reg_data, property_idx, FALSE); +} + +static const GDBusInterfaceVTable dbus_vtable = { + .method_call = dbus_vtable_method_call, + .get_property = dbus_vtable_get_property, + + /* set_property is handled via method_call as well. We need to authenticate + * which requires an asynchronous handler. */ + .set_property = NULL, +}; + +static void +_obj_register (NMDBusManager *self, + NMDBusObject *obj) +{ + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + guint i, k; + guint n_klasses; + GType gtype; + NMDBusObjectClass *klasses[10]; + const NMDBusInterfaceInfoExtended *const*prev_interface_infos = NULL; + GVariantBuilder builder; + + nm_assert (c_list_is_empty (&obj->internal.registration_lst_head)); + nm_assert (priv->connection); + nm_assert (priv->started); + + n_klasses = 0; + gtype = G_OBJECT_TYPE (obj); + while (gtype != NM_TYPE_DBUS_OBJECT) { + nm_assert (n_klasses < G_N_ELEMENTS (klasses)); + klasses[n_klasses++] = g_type_class_ref (gtype); + gtype = g_type_parent (gtype); + } + + for (k = n_klasses; k > 0; ) { + NMDBusObjectClass *klass = NM_DBUS_OBJECT_CLASS (klasses[--k]); + + if (!klass->interface_infos) + continue; + + if (prev_interface_infos == klass->interface_infos) { + /* derived classes inherrit the interface-infos from the parent class. + * For convenience, we allow the subclass to leave interface-infos untouched, + * but it means we must ignore the parent's interface, because we already + * handled it. + * + * Note that the loop goes from the parent classes to child classes */ + continue; + } + prev_interface_infos = klass->interface_infos; + + for (i = 0; klass->interface_infos[i]; i++) { + const NMDBusInterfaceInfoExtended *interface_info = klass->interface_infos[i]; + RegistrationData *reg_data; + gs_free_error GError *error = NULL; + guint registration_id; + guint prop_len = NM_PTRARRAY_LEN (interface_info->parent.properties); + + reg_data = g_malloc0 (sizeof (RegistrationData) + (sizeof (PropertyCacheData) * prop_len)); + + registration_id = g_dbus_connection_register_object (priv->connection, + obj->internal.path, + NM_UNCONST_PTR (GDBusInterfaceInfo, &interface_info->parent), + &dbus_vtable, + reg_data, + NULL, + &error); + if (!registration_id) { + _LOGE ("failure to register object %s: %s", obj->internal.path, error->message); + g_free (reg_data); + continue; + } + + reg_data->obj = obj; + reg_data->klass = g_type_class_ref (G_TYPE_FROM_CLASS (klass)); + reg_data->info_idx = i; + reg_data->registration_id = registration_id; + c_list_link_tail (&obj->internal.registration_lst_head, ®_data->registration_lst); + } + } + + for (k = 0; k < n_klasses; k++) + g_type_class_unref (klasses[k]); + + nm_assert (!c_list_is_empty (&obj->internal.registration_lst_head)); + + /* Currently the interfaces of an object do not changed and strictly depend on the object glib type. + * We don't need more flixibility, and it simplifies the code. Hence, now emit interface-added + * signal for the new object. + * + * Warning: note that if @obj's notify signal is currently blocked via g_object_freeze_notify(), + * we might emit properties with an inconsistent (internal) state. There is no easy solution, + * because we have to emit the signal now, and we don't know what the correct desired state + * of the properties is. + * Another problem is, upon unfreezing the signals, we immediately send PropertiesChanged + * notifications out. Which is a bit odd, as we just export the object. + * + * In general, it's ok to export an object with frozen signals. But you better make sure + * that all properties are in a self-consistent state when exporting the object. */ + g_dbus_connection_emit_signal (priv->connection, + NULL, + OBJECT_MANAGER_SERVER_BASE_PATH, + interface_info_objmgr.name, + signal_info_objmgr_interfaces_added.name, + g_variant_new ("(oa{sa{sv}})", + obj->internal.path, + _obj_collect_properties_all (obj, &builder)), + NULL); +} + +static void +_obj_unregister (NMDBusManager *self, + NMDBusObject *obj) +{ + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + RegistrationData *reg_data; + GVariantBuilder builder; + + nm_assert (NM_IS_DBUS_OBJECT (obj)); + + if (!priv->connection) { + /* nothing to do for the moment. */ + nm_assert (c_list_is_empty (&obj->internal.registration_lst_head)); + return; + } + + nm_assert (!c_list_is_empty (&obj->internal.registration_lst_head)); + nm_assert (priv->objmgr_registration_id); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")); + + while ((reg_data = c_list_last_entry (&obj->internal.registration_lst_head, RegistrationData, registration_lst))) { + const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data); + guint i; + + g_variant_builder_add (&builder, + "s", + interface_info->parent.name); + c_list_unlink_stale (®_data->registration_lst); + if (!g_dbus_connection_unregister_object (priv->connection, reg_data->registration_id)) + nm_assert_not_reached (); + + if (interface_info->parent.properties) { + for (i = 0; interface_info->parent.properties[i]; i++) + nm_clear_g_variant (®_data->property_cache[i].value); + } + + g_type_class_unref (reg_data->klass); + g_free (reg_data); + } + + g_dbus_connection_emit_signal (priv->connection, + NULL, + OBJECT_MANAGER_SERVER_BASE_PATH, + interface_info_objmgr.name, + signal_info_objmgr_interfaces_removed.name, + g_variant_new ("(oas)", + obj->internal.path, + &builder), + NULL); +} + +NMDBusObject * +nm_dbus_manager_lookup_object (NMDBusManager *self, const char *path) +{ + NMDBusManagerPrivate *priv; + gpointer ptr; + NMDBusObject *obj; + + g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL); + g_return_val_if_fail (path, NULL); + + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + ptr = g_hash_table_lookup (priv->objects_by_path, &path); + if (!ptr) + return NULL; + + obj = (NMDBusObject *) (((char *) ptr) - G_STRUCT_OFFSET (NMDBusObject, internal)); + nm_assert (NM_IS_DBUS_OBJECT (obj)); + return obj; +} + +void +_nm_dbus_manager_obj_export (NMDBusObject *obj) +{ + NMDBusManager *self; + NMDBusManagerPrivate *priv; + + g_return_if_fail (NM_IS_DBUS_OBJECT (obj)); + g_return_if_fail (obj->internal.path); + g_return_if_fail (NM_IS_DBUS_MANAGER (obj->internal.bus_manager)); + g_return_if_fail (c_list_is_empty (&obj->internal.objects_lst)); + nm_assert (c_list_is_empty (&obj->internal.registration_lst_head)); + + self = obj->internal.bus_manager; + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + if (!g_hash_table_add (priv->objects_by_path, &obj->internal)) + nm_assert_not_reached (); + c_list_link_tail (&priv->objects_lst_head, &obj->internal.objects_lst); + + if (priv->connection && priv->started) + _obj_register (self, obj); +} + +void +_nm_dbus_manager_obj_unexport (NMDBusObject *obj) +{ + NMDBusManager *self; + NMDBusManagerPrivate *priv; + + g_return_if_fail (NM_IS_DBUS_OBJECT (obj)); + g_return_if_fail (obj->internal.path); + g_return_if_fail (NM_IS_DBUS_MANAGER (obj->internal.bus_manager)); + g_return_if_fail (!c_list_is_empty (&obj->internal.objects_lst)); + + self = obj->internal.bus_manager; + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + nm_assert (&obj->internal == g_hash_table_lookup (priv->objects_by_path, &obj->internal)); + nm_assert (c_list_contains (&priv->objects_lst_head, &obj->internal.objects_lst)); + + _obj_unregister (self, obj); + + if (!g_hash_table_remove (priv->objects_by_path, &obj->internal)) + nm_assert_not_reached (); + c_list_unlink (&obj->internal.objects_lst); +} + +void +_nm_dbus_manager_obj_notify (NMDBusObject *obj, + guint n_pspecs, + const GParamSpec *const*pspecs) +{ + NMDBusManager *self; + NMDBusManagerPrivate *priv; + RegistrationData *reg_data; + guint i, p; + gboolean any_legacy_signals = FALSE; + gboolean any_legacy_properties = FALSE; + GVariantBuilder legacy_builder; + GVariant *device_statistics_args = NULL; + + nm_assert (NM_IS_DBUS_OBJECT (obj)); + nm_assert (obj->internal.path); + nm_assert (NM_IS_DBUS_MANAGER (obj->internal.bus_manager)); + nm_assert (!c_list_is_empty (&obj->internal.objects_lst)); + + c_list_for_each_entry (reg_data, &obj->internal.registration_lst_head, registration_lst) { + if (_reg_data_get_interface_info (reg_data)->legacy_property_changed) { + any_legacy_signals = TRUE; + break; + } + } + + self = obj->internal.bus_manager; + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + /* do a naive search for the matching NMDBusPropertyInfoExtended infos. Since the number of + * (interaces x properties) is static and possibly small, this naive search is effectively + * O(1). We might wanna introduce some index to lookup the properties in question faster. + * + * The nice part of this implementation is however, that the order in which properties + * are added to the GVariant is strictly defined to be the order in which the D-Bus property-info + * is declared. Getting a defined ordering with some smart lookup would be hard. */ + c_list_for_each_entry (reg_data, &obj->internal.registration_lst_head, registration_lst) { + const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data); + gboolean has_properties = FALSE; + GVariantBuilder builder; + GVariantBuilder invalidated_builder; + GVariant *args; + + if (!interface_info->parent.properties) + continue; + + for (i = 0; interface_info->parent.properties[i]; i++) { + const NMDBusPropertyInfoExtended *property_info = (const NMDBusPropertyInfoExtended *) interface_info->parent.properties[i]; + + for (p = 0; p < n_pspecs; p++) { + const GParamSpec *pspec = pspecs[p]; + gs_unref_variant GVariant *value = NULL; + + if (!nm_streq (property_info->property_name, pspec->name)) + continue; + + value = _obj_get_property (reg_data, i, TRUE); + + if ( property_info->include_in_legacy_property_changed + && any_legacy_signals) { + /* also track the value in the legacy_builder to emit legacy signals below. */ + if (!any_legacy_properties) { + any_legacy_properties = TRUE; + g_variant_builder_init (&legacy_builder, G_VARIANT_TYPE ("a{sv}")); + } + g_variant_builder_add (&legacy_builder, "{sv}", property_info->parent.name, value); + } + + if (!has_properties) { + has_properties = TRUE; + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + } + g_variant_builder_add (&builder, "{sv}", property_info->parent.name, value); + } + } + + if (!has_properties) + continue; + + args = g_variant_builder_end (&builder); + + if (G_UNLIKELY (interface_info == &nm_interface_info_device_statistics)) { + /* we treat the Device.Statistics signal special, because we need to + * emit a signal also for it (below). */ + nm_assert (!device_statistics_args); + device_statistics_args = g_variant_ref_sink (args); + } + + g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as")); + g_dbus_connection_emit_signal (priv->connection, + NULL, + obj->internal.path, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + g_variant_new ("(s@a{sv}as)", + interface_info->parent.name, + args, + &invalidated_builder), + NULL); + } + + if (G_UNLIKELY (device_statistics_args)) { + /* this is a special interface: it has a legacy PropertiesChanged signal, + * however, contrary to other interfaces with ~regular~ legacy signals, + * we only notify about properties that actually belong to this interface. */ + g_dbus_connection_emit_signal (priv->connection, + NULL, + obj->internal.path, + nm_interface_info_device_statistics.parent.name, + "PropertiesChanged", + g_variant_new ("(@a{sv})", + device_statistics_args), + NULL); + g_variant_unref (device_statistics_args); + } + + if (any_legacy_properties) { + gs_unref_variant GVariant *args = NULL; + + /* The legacy PropertyChanged signal on the NetworkManager D-Bus interface is + * deprecated for the standard signal on org.freedesktop.DBus.Properties. However, + * for backward compatibility, we still need to emit it. + * + * Due to a bug in dbus-glib in NetworkManager <= 1.0, the signal would + * not only notify about properties that were actually on the corresponding + * D-Bus interface. Instead, it would notify about all relevant properties + * on all interfaces that had such a signal. + * + * For example, "HwAddress" gets emitted both on "fdo.NM.Device.Ethernet" + * and "fdo.NM.Device.Veth" for veth interfaces, although only the former + * actually has such a property. + * Also note that "fdo.NM.Device" interface has no legacy signal. All notifications + * about its properties are instead emitted on the interfaces of the subtypes. + * + * See bgo#770629 and commit bef26a2e69f51259095fa080221db73de09fd38d. + */ + args = g_variant_ref_sink (g_variant_new ("(a{sv})", + &legacy_builder)); + c_list_for_each_entry (reg_data, &obj->internal.registration_lst_head, registration_lst) { + const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data); + + if (interface_info->legacy_property_changed) { + g_dbus_connection_emit_signal (priv->connection, + NULL, + obj->internal.path, + interface_info->parent.name, + "PropertiesChanged", + args, + NULL); + } + } + } +} + +void +_nm_dbus_manager_obj_emit_signal (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const GDBusSignalInfo *signal_info, + GVariant *args) +{ + NMDBusManager *self; + NMDBusManagerPrivate *priv; + + g_return_if_fail (NM_IS_DBUS_OBJECT (obj)); + g_return_if_fail (obj->internal.path); + g_return_if_fail (NM_IS_DBUS_MANAGER (obj->internal.bus_manager)); + g_return_if_fail (!c_list_is_empty (&obj->internal.objects_lst)); + + self = obj->internal.bus_manager; + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + if (!priv->connection || !priv->started) { + nm_g_variant_unref_floating (args); + return; + } + + g_dbus_connection_emit_signal (priv->connection, + NULL, + obj->internal.path, + interface_info->parent.name, + signal_info->name, + args, + NULL); +} + +/*****************************************************************************/ + +static GVariantBuilder * +_obj_collect_properties_per_interface (NMDBusObject *obj, + RegistrationData *reg_data, + GVariantBuilder *builder) +{ + const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data); + guint i; + + g_variant_builder_init (builder, G_VARIANT_TYPE ("a{sv}")); + if (interface_info->parent.properties) { + for (i = 0; interface_info->parent.properties[i]; i++) { + const NMDBusPropertyInfoExtended *property_info = (const NMDBusPropertyInfoExtended *) interface_info->parent.properties[i]; + gs_unref_variant GVariant *variant = NULL; + + variant = _obj_get_property (reg_data, i, FALSE); + g_variant_builder_add (builder, + "{sv}", + property_info->parent.name, + variant); + } + } + return builder; +} + +static GVariantBuilder * +_obj_collect_properties_all (NMDBusObject *obj, + GVariantBuilder *builder) +{ + RegistrationData *reg_data; + + g_variant_builder_init (builder, G_VARIANT_TYPE ("a{sa{sv}}")); + + c_list_for_each_entry (reg_data, &obj->internal.registration_lst_head, registration_lst) { + GVariantBuilder properties_builder; + + g_variant_builder_add (builder, + "{sa{sv}}", + _reg_data_get_interface_info (reg_data)->parent.name, + _obj_collect_properties_per_interface (obj, + reg_data, + &properties_builder)); + } + + return builder; +} + +static void +dbus_vtable_objmgr_method_call (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + NMDBusManager *self = user_data; + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + GVariantBuilder array_builder; + NMDBusObject *obj; + + nm_assert (nm_streq0 (object_path, OBJECT_MANAGER_SERVER_BASE_PATH)); + + if ( !nm_streq (method_name, "GetManagedObjects") + || !nm_streq (interface_name, interface_info_objmgr.name)) { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method %s - only GetManagedObjects() is supported", + method_name); + return; + } + + g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a{oa{sa{sv}}}")); + c_list_for_each_entry (obj, &priv->objects_lst_head, internal.objects_lst) { + GVariantBuilder interfaces_builder; + + /* note that we are called on an idle handler. Hence, all properties are + * supposed to be in a consistent state. That is true, if you always + * g_object_thaw_notify() before returning to the mainloop. Keeping + * signals frozen between while returning from the current call stack + * is anyway a very fragile thing, easy to get wrong. Don't do that. */ + g_variant_builder_add (&array_builder, + "{oa{sa{sv}}}", + obj->internal.path, + _obj_collect_properties_all (obj, + &interfaces_builder)); + } + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(a{oa{sa{sv}}})", + &array_builder)); +} + +static const GDBusInterfaceVTable dbus_vtable_objmgr = { + .method_call = dbus_vtable_objmgr_method_call +}; + +static const GDBusSignalInfo signal_info_objmgr_interfaces_added = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "InterfacesAdded", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("object_path", "o"), + NM_DEFINE_GDBUS_ARG_INFO ("interfaces_and_properties", "a{sa{sv}}"), + ), +); + +static const GDBusSignalInfo signal_info_objmgr_interfaces_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "InterfacesRemoved", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("object_path", "o"), + NM_DEFINE_GDBUS_ARG_INFO ("interfaces", "as"), + ), +); + +static const GDBusInterfaceInfo interface_info_objmgr = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + "org.freedesktop.DBus.ObjectManager", + .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( + NM_DEFINE_GDBUS_METHOD_INFO ( + "GetManagedObjects", + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("object_paths_interfaces_and_properties", "a{oa{sa{sv}}}"), + ), + ), + ), + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &signal_info_objmgr_interfaces_added, + &signal_info_objmgr_interfaces_removed, + ), +); + +/*****************************************************************************/ + +void +nm_dbus_manager_start (NMDBusManager *self, + NMDBusManagerSetPropertyHandler set_property_handler, + gpointer set_property_handler_data) +{ + NMDBusManagerPrivate *priv; + NMDBusObject *obj; + + g_return_if_fail (NM_IS_DBUS_MANAGER (self)); + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + g_return_if_fail (priv->connection); + + priv->set_property_handler = set_property_handler; + priv->set_property_handler_data = set_property_handler_data; + priv->started = TRUE; + + c_list_for_each_entry (obj, &priv->objects_lst_head, internal.objects_lst) + _obj_register (self, obj); +} + +gboolean +nm_dbus_manager_acquire_bus (NMDBusManager *self) +{ + NMDBusManagerPrivate *priv; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *ret = NULL; + gs_unref_object GDBusConnection *connection = NULL; + gs_unref_object GDBusProxy *proxy = NULL; + guint32 result; + guint registration_id; + + g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE); + + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + /* we will create the D-Bus connection and registering the name synchronously. + * The reason why that is necessary is because: + * (1) if we are unable to create a D-Bus connection, it means D-Bus is not + * available and we run in D-Bus less mode. We do not support creating + * a D-Bus connection later on. This disconnected mode is useful for initrd + * (well, currently not yet, but will be). + * (2) if we are able to create the connection and register the name, + * all is good and we run with D-Bus. Note that D-Bus disconnects + * from D-Bus are ignored. Essentially, we do not support restarting + * D-Bus. + * (3) if we are able to create the connection but registration fails, + * it means that something is borked. Quite possibly another NetworkManager + * instance is running. We need to exit right away. + * To appease (1) and (3), we cannot initalize synchronously, because we need + * to know right away whether another NetworkManager instance is running (3). + **/ + + connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, + NULL, + &error); + if (!connection) { + _LOGI ("cannot connect to D-Bus and proceed without (%s)", error->message); + return TRUE; + } + + g_dbus_connection_set_exit_on_close (connection, FALSE); + + proxy = g_dbus_proxy_new_sync (connection, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES + | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + NULL, + &error); + if (!proxy) { + _LOGE ("fatal failure to initialize D-Bus: %s", error->message); + return FALSE; + } + + ret = _nm_dbus_proxy_call_sync (proxy, + "RequestName", + g_variant_new ("(su)", + NM_DBUS_SERVICE, + DBUS_NAME_FLAG_DO_NOT_QUEUE), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, -1, + NULL, + &error); + if (!ret) { + _LOGE ("fatal failure to acquire D-Bus service \"%s"": %s", + NM_DBUS_SERVICE, error->message); + return FALSE; + } + + g_variant_get (ret, "(u)", &result); + if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + _LOGE ("fatal failure to acquire D-Bus service \"%s\" (%u). Service already taken", + NM_DBUS_SERVICE, (guint) result); + return FALSE; + } + + registration_id = g_dbus_connection_register_object (connection, + OBJECT_MANAGER_SERVER_BASE_PATH, + NM_UNCONST_PTR (GDBusInterfaceInfo, &interface_info_objmgr), + &dbus_vtable_objmgr, + self, + NULL, + &error); + if (!registration_id) { + _LOGE ("failure to register object manager: %s", error->message); + return FALSE; + } + + priv->objmgr_registration_id = registration_id; + priv->connection = g_steal_pointer (&connection); + priv->proxy = g_steal_pointer (&proxy); + + _LOGI ("acquired D-Bus service \"%s\"", NM_DBUS_SERVICE); + + return TRUE; +} + +void +nm_dbus_manager_stop (NMDBusManager *self) +{ + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + priv->shutting_down = TRUE; + + /* during shutdown we also clear the set-property-handler. It's no longer + * possible to set a property, because doing so would require authorization, + * which is async, which is just complicated to get right. No more property + * setting from now on. */ + priv->set_property_handler = NULL; + priv->set_property_handler_data = NULL; +} + +gboolean +nm_dbus_manager_is_stopping (NMDBusManager *self) +{ + return NM_DBUS_MANAGER_GET_PRIVATE (self)->shutting_down; +} + +/*****************************************************************************/ + +static void +nm_dbus_manager_init (NMDBusManager *self) +{ + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + c_list_init (&priv->private_servers_lst_head); + c_list_init (&priv->objects_lst_head); + priv->objects_by_path = g_hash_table_new ((GHashFunc) _objects_by_path_hash, (GEqualFunc) _objects_by_path_equal); +} + +static void +dispose (GObject *object) +{ + NMDBusManager *self = NM_DBUS_MANAGER (object); + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + PrivateServer *s, *s_safe; + + /* All exported NMDBusObject instances keep the manager alive, so we don't + * expect any remaining objects. */ + nm_assert (!priv->objects_by_path || g_hash_table_size (priv->objects_by_path) == 0); + nm_assert (c_list_is_empty (&priv->objects_lst_head)); + + g_clear_pointer (&priv->objects_by_path, g_hash_table_destroy); + + c_list_for_each_entry_safe (s, s_safe, &priv->private_servers_lst_head, private_servers_lst) + private_server_free (s); + + if (priv->objmgr_registration_id) { + g_dbus_connection_unregister_object (priv->connection, + nm_steal_int (&priv->objmgr_registration_id)); + } + + g_clear_object (&priv->proxy); + g_clear_object (&priv->connection); + + G_OBJECT_CLASS (nm_dbus_manager_parent_class)->dispose (object); +} + +static void +nm_dbus_manager_class_init (NMDBusManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = dispose; + + signals[PRIVATE_CONNECTION_NEW] = + g_signal_new (NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_DBUS_CONNECTION, G_TYPE_DBUS_OBJECT_MANAGER_SERVER); + + signals[PRIVATE_CONNECTION_DISCONNECTED] = + g_signal_new (NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_POINTER); +} diff --git a/src/nm-dbus-manager.h b/src/nm-dbus-manager.h new file mode 100644 index 00000000..a0741316 --- /dev/null +++ b/src/nm-dbus-manager.h @@ -0,0 +1,113 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2008 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef __NM_DBUS_MANAGER_H__ +#define __NM_DBUS_MANAGER_H__ + +#include "nm-dbus-utils.h" + +#define NM_TYPE_DBUS_MANAGER (nm_dbus_manager_get_type ()) +#define NM_DBUS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_DBUS_MANAGER, NMDBusManager)) +#define NM_DBUS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_DBUS_MANAGER, NMDBusManagerClass)) +#define NM_IS_DBUS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NM_TYPE_DBUS_MANAGER)) +#define NM_IS_DBUS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NM_TYPE_DBUS_MANAGER)) +#define NM_DBUS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_DBUS_MANAGER, NMDBusManagerClass)) + +#define NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW "private-connection-new" +#define NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "private-connection-disconnected" + +typedef struct _NMDBusManagerClass NMDBusManagerClass; + +GType nm_dbus_manager_get_type (void); + +NMDBusManager *nm_dbus_manager_get (void); + +typedef void (*NMDBusManagerSetPropertyHandler) (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusPropertyInfoExtended *property_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *value, + gpointer user_data); + +gboolean nm_dbus_manager_acquire_bus (NMDBusManager *self); + +void nm_dbus_manager_start (NMDBusManager *self, + NMDBusManagerSetPropertyHandler set_property_handler, + gpointer set_property_handler_data); + +void nm_dbus_manager_stop (NMDBusManager *self); + +gboolean nm_dbus_manager_is_stopping (NMDBusManager *self); + +GDBusConnection *nm_dbus_manager_get_connection (NMDBusManager *self); + +NMDBusObject *nm_dbus_manager_lookup_object (NMDBusManager *self, const char *path); + +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); + +gboolean nm_dbus_manager_get_caller_info (NMDBusManager *self, + GDBusMethodInvocation *context, + char **out_sender, + gulong *out_uid, + gulong *out_pid); + +gboolean nm_dbus_manager_ensure_uid (NMDBusManager *self, + GDBusMethodInvocation *context, + gulong uid, + GQuark error_domain, + int error_code); + +const char *nm_dbus_manager_connection_get_private_name (NMDBusManager *self, + GDBusConnection *connection); + +gboolean nm_dbus_manager_get_unix_user (NMDBusManager *self, + const char *sender, + gulong *out_uid); + +gboolean nm_dbus_manager_get_caller_info_from_message (NMDBusManager *self, + GDBusConnection *connection, + GDBusMessage *message, + char **out_sender, + gulong *out_uid, + gulong *out_pid); + +void nm_dbus_manager_private_server_register (NMDBusManager *self, + const char *path, + const char *tag); + +GDBusProxy *nm_dbus_manager_new_proxy (NMDBusManager *self, + GDBusConnection *connection, + GType proxy_type, + const char *name, + const char *path, + const char *iface); + +#endif /* __NM_DBUS_MANAGER_H__ */ diff --git a/src/nm-dbus-object.c b/src/nm-dbus-object.c new file mode 100644 index 00000000..e92a8f5f --- /dev/null +++ b/src/nm-dbus-object.c @@ -0,0 +1,309 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-dbus-object.h" + +#include "nm-dbus-manager.h" + +/*****************************************************************************/ + +enum { + EXPORTED_CHANGED, + + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_ABSTRACT_TYPE (NMDBusObject, nm_dbus_object, G_TYPE_OBJECT); + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_CORE +#define _NMLOG(level, ...) __NMLOG_DEFAULT_WITH_ADDR (level, _NMLOG_DOMAIN, "dbus-object", __VA_ARGS__) + +#define _NMLOG2_DOMAIN LOGD_DBUS_PROPS +#define _NMLOG2(level, ...) __NMLOG_DEFAULT_WITH_ADDR (level, _NMLOG2_DOMAIN, "properties-changed", __VA_ARGS__) + +/*****************************************************************************/ + +static void +_emit_exported_changed (NMDBusObject *self) +{ + g_signal_emit (self, signals[EXPORTED_CHANGED], 0); +} + +static char * +_create_export_path (NMDBusObjectClass *klass) +{ + nm_assert (NM_IS_DBUS_OBJECT_CLASS (klass)); + nm_assert (klass->export_path.path); + +#if NM_MORE_ASSERTS + { + const char *p; + + p = strchr (klass->export_path.path, '%'); + if (klass->export_path.int_counter) { + nm_assert (p); + nm_assert (p[1] == 'l'); + nm_assert (p[2] == 'l'); + nm_assert (p[3] == 'u'); + nm_assert (p[4] == '\0'); + } else + nm_assert (!p); + } +#endif + + if (klass->export_path.int_counter) { + NM_PRAGMA_WARNING_DISABLE("-Wformat-nonliteral") + return g_strdup_printf (klass->export_path.path, + ++(*klass->export_path.int_counter)); + NM_PRAGMA_WARNING_REENABLE + } + return g_strdup (klass->export_path.path); +} + +/** + * nm_dbus_object_export: + * @self: an #NMDBusObject + * + * Exports @self on all active and future D-Bus connections. + * + * The path to export @self on is taken from its #NMObjectClass's %export_path + * member. If the %export_path contains "%u", then it will be replaced with a + * monotonically increasing integer ID (with each distinct %export_path having + * its own counter). Otherwise, %export_path will be used literally (implying + * that @self must be a singleton). + * + * Returns: the path @self was exported under + */ +const char * +nm_dbus_object_export (NMDBusObject *self) +{ + static guint64 id_counter = 0; + + g_return_val_if_fail (NM_IS_DBUS_OBJECT (self), NULL); + + g_return_val_if_fail (!self->internal.path, self->internal.path); + + nm_assert (!self->internal.is_unexporting); + + self->internal.path = _create_export_path (NM_DBUS_OBJECT_GET_CLASS (self)); + + self->internal.export_version_id = ++id_counter; + + _LOGT ("export: \"%s\"", self->internal.path); + + _nm_dbus_manager_obj_export (self); + + _emit_exported_changed (self); + return self->internal.path; +} + +/** + * nm_dbus_object_unexport: + * @self: an #NMDBusObject + * + * Unexports @self on all active D-Bus connections (and prevents it from being + * auto-exported on future connections). + */ +void +nm_dbus_object_unexport (NMDBusObject *self) +{ + g_return_if_fail (NM_IS_DBUS_OBJECT (self)); + + g_return_if_fail (self->internal.path); + + _LOGT ("unexport: \"%s\"", self->internal.path); + + /* note that we emit the signal *before* actually unexporting the object. + * The reason is, that listeners want to use this signal to know that + * the object goes away, and clear their D-Bus path to this object. + * + * But this must happen before we actually unregister the object, so + * that we first emit a D-Bus signal that other objects no longer + * reference this object, before finally unregistering the object itself. + * + * The inconvenient part is, that at this point nm_dbus_object_get_path() + * still returns the path. So, the callee needs to handle that. Possibly + * by using "nm_dbus_object_get_path_still_exported()". */ + self->internal.is_unexporting = TRUE; + + _emit_exported_changed (self); + + _nm_dbus_manager_obj_unexport (self); + + g_clear_pointer (&self->internal.path, g_free); + self->internal.export_version_id = 0; + + self->internal.is_unexporting = FALSE; +} + +/*****************************************************************************/ + +void +_nm_dbus_object_clear_and_unexport (NMDBusObject **location) +{ + NMDBusObject *self; + + g_return_if_fail (location); + if (!*location) + return; + + self = g_steal_pointer (location); + + g_return_if_fail (NM_IS_DBUS_OBJECT (self)); + + if (self->internal.path) + nm_dbus_object_unexport (self); + + g_object_unref (self); +} + +/*****************************************************************************/ + +void +nm_dbus_object_emit_signal_variant (NMDBusObject *self, + const NMDBusInterfaceInfoExtended *interface_info, + const GDBusSignalInfo *signal_info, + GVariant *args) +{ + if (!self->internal.path) { + nm_g_variant_unref_floating (args); + return; + } + _nm_dbus_manager_obj_emit_signal (self, interface_info, signal_info, args); +} + +void +nm_dbus_object_emit_signal (NMDBusObject *self, + const NMDBusInterfaceInfoExtended *interface_info, + const GDBusSignalInfo *signal_info, + const char *format, + ...) +{ + va_list ap; + + nm_assert (NM_IS_DBUS_OBJECT (self)); + nm_assert (format); + + if (!self->internal.path) + return; + + va_start (ap, format); + _nm_dbus_manager_obj_emit_signal (self, + interface_info, + signal_info, + g_variant_new_va (format, NULL, &ap)); + va_end (ap); +} + +/*****************************************************************************/ + +static void +dispatch_properties_changed (GObject *object, + guint n_pspecs, + GParamSpec **pspecs) +{ + NMDBusObject *self = NM_DBUS_OBJECT (object); + + if (self->internal.path) + _nm_dbus_manager_obj_notify (self, n_pspecs, (const GParamSpec *const*) pspecs); + + G_OBJECT_CLASS (nm_dbus_object_parent_class)->dispatch_properties_changed (object, n_pspecs, pspecs); +} + +/*****************************************************************************/ + +static void +nm_dbus_object_init (NMDBusObject *self) +{ + c_list_init (&self->internal.objects_lst); + c_list_init (&self->internal.registration_lst_head); + self->internal.bus_manager = nm_g_object_ref (nm_dbus_manager_get ()); +} + +static void +constructed (GObject *object) +{ + NMDBusObjectClass *klass; + + G_OBJECT_CLASS (nm_dbus_object_parent_class)->constructed (object); + + klass = NM_DBUS_OBJECT_GET_CLASS (object); + + if (klass->export_on_construction) + nm_dbus_object_export ((NMDBusObject *) object); + + /* NMDBusObject types should be very careful when overwriting notify(). + * It is possible to do, but this is a reminder that it's probably not + * a good idea. + * + * It's not a good idea, because NMDBusObject uses dispatch_properties_changed() + * to emit signals about a bunch of property changes. So, we want to make + * use of g_object_freeze_notify() / g_object_thaw_notify() to combine multiple + * property changes in one signal on D-Bus. Note that notify() is not invoked + * while the signal is frozen, that means, whatever you do inside notify() + * will not make it into the same batch of PropertiesChanged signal. That is + * confusing, and probably not what you want. + * + * Simple solution: don't overwrite notify(). */ + nm_assert (!G_OBJECT_CLASS (klass)->notify); +} + +static void +dispose (GObject *object) +{ + NMDBusObject *self = NM_DBUS_OBJECT (object); + + /* Objects should have already been unexported by their owner, unless + * we are quitting, where many objects stick around until exit. + */ + if (self->internal.path) { + if (!nm_dbus_manager_is_stopping (nm_dbus_object_get_manager (self))) + g_warn_if_reached (); + nm_dbus_object_unexport (self); + } + + G_OBJECT_CLASS (nm_dbus_object_parent_class)->dispose (object); + + g_clear_object (&self->internal.bus_manager); +} + +static void +nm_dbus_object_class_init (NMDBusObjectClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->dispatch_properties_changed = dispatch_properties_changed; + + signals[EXPORTED_CHANGED] = + g_signal_new (NM_DBUS_OBJECT_EXPORTED_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} diff --git a/src/nm-dbus-object.h b/src/nm-dbus-object.h new file mode 100644 index 00000000..8b3ffdbd --- /dev/null +++ b/src/nm-dbus-object.h @@ -0,0 +1,198 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_DBUS_OBJECT_H__ +#define __NM_DBUS_OBJECT_H__ + +/*****************************************************************************/ + +#include "c-list/src/c-list.h" +#include "nm-dbus-utils.h" + +/*****************************************************************************/ + +typedef struct { + const char *path; + + /* if path is of type NM_DBUS_EXPORT_PATH_NUMBERED(), we need a + * per-class counter when generating a new numbered path. + * + * Each NMDBusObjectClass instance has a shallow clone of the NMDBusObjectClass parent + * instance in every derived type. Hence we cannot embed the counter there directly, + * because it must be shared, e.g. between NMDeviceBond and NMDeviceEthernet. + * Make int_counter a pointer to the actual counter that is used by ever sibling + * class. */ + long long unsigned *int_counter; +} NMDBusExportPath; + +#define NM_DBUS_EXPORT_PATH_STATIC(basepath) \ + ({ \ + ((NMDBusExportPath) { \ + .path = ""basepath"", \ + }); \ + }) + +#define NM_DBUS_EXPORT_PATH_NUMBERED(basepath) \ + ({ \ + static long long unsigned _int_counter = 0; \ + ((NMDBusExportPath) { \ + .path = ""basepath"/%llu", \ + .int_counter = &_int_counter, \ + }); \ + }) + +/*****************************************************************************/ + +/* "org.freedesktop.NetworkManager.Device.Statistics" is a special interface, + * because although it has a legacy PropertiesChanged signal, it only notifies + * about properties that actually exist on that interface. That is, because it + * was added with 1.4.0 release, and thus didn't have the broken behavior like + * other legacy interfaces. Those notify about *all* properties, even if they + * are not part of that D-Bus interface. See also "include_in_legacy_property_changed" + * and "legacy_property_changed". */ +extern const NMDBusInterfaceInfoExtended nm_interface_info_device_statistics; + +/*****************************************************************************/ + +#define NM_TYPE_DBUS_OBJECT (nm_dbus_object_get_type ()) +#define NM_DBUS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DBUS_OBJECT, NMDBusObject)) +#define NM_DBUS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DBUS_OBJECT, NMDBusObjectClass)) +#define NM_IS_DBUS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DBUS_OBJECT)) +#define NM_IS_DBUS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DBUS_OBJECT)) +#define NM_DBUS_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DBUS_OBJECT, NMDBusObjectClass)) + +#define NM_DBUS_OBJECT_EXPORTED_CHANGED "exported-changed" + +/* NMDBusObject and NMDBusManager cooperate strongly. Hence, there is an + * internal data structure attached to the NMDBusObject accessible to both of them. */ +struct _NMDBusObjectInternal { + char *path; + NMDBusManager *bus_manager; + CList objects_lst; + CList registration_lst_head; + + /* we perform asynchronous operation on exported objects. For example, we receive + * a Set property call, and asynchronously validate the operation. We must make + * sure that when the authentication is complete, that we are still looking at + * the same (exported) object. In the meantime, the object could have been + * unexported, or even re-exported afterwards. If that happens, we want + * to fail the request. For that, we keep track of a version id. */ + guint64 export_version_id; + bool is_unexporting:1; +}; + +struct _NMDBusObject { + GObject parent; + struct _NMDBusObjectInternal internal; +}; + +#define NM_DEFINE_DBUS_INTERFACE_INFO(...) \ + ((NMDBusInterfaceInfo *) (&((const NMDBusInterfaceInfo) { \ + __VA_ARGS__ \ + }))) + +typedef struct { + GObjectClass parent; + + NMDBusExportPath export_path; + + const NMDBusInterfaceInfoExtended *const*interface_infos; + + bool export_on_construction; +} NMDBusObjectClass; + +GType nm_dbus_object_get_type (void); + +static inline NMDBusManager * +nm_dbus_object_get_manager (NMDBusObject *obj) +{ + nm_assert (NM_IS_DBUS_OBJECT (obj)); + + return obj->internal.bus_manager; +} + +static inline guint64 +nm_dbus_object_get_export_version_id (NMDBusObject *obj) +{ + nm_assert (NM_IS_DBUS_OBJECT (obj)); + + return obj->internal.export_version_id; +} + +/** + * nm_dbus_object_get_path: + * @self: an #NMDBusObject + * + * Gets @self's D-Bus path. + * + * Returns: @self's D-Bus path, or %NULL if @self is not exported. + */ +static inline const char * +nm_dbus_object_get_path (NMDBusObject *self) +{ + g_return_val_if_fail (NM_IS_DBUS_OBJECT (self), NULL); + + return self->internal.path; +} + +/** + * nm_dbus_object_is_exported: + * @self: an #NMDBusObject + * + * Checks if @self is exported + * + * Returns: %TRUE if @self is exported + */ +static inline gboolean +nm_dbus_object_is_exported (NMDBusObject *self) +{ + return !!nm_dbus_object_get_path (self); +} + +static inline const char * +nm_dbus_object_get_path_still_exported (NMDBusObject *self) +{ + g_return_val_if_fail (NM_IS_DBUS_OBJECT (self), NULL); + + /* like nm_dbus_object_get_path(), however, while unexporting + * (exported-changed signal), returns %NULL instead of the path. */ + return self->internal.is_unexporting + ? NULL + : self->internal.path; +} + +const char *nm_dbus_object_export (NMDBusObject *self); +void nm_dbus_object_unexport (NMDBusObject *self); + +void _nm_dbus_object_clear_and_unexport (NMDBusObject **location); +#define nm_dbus_object_clear_and_unexport(location) _nm_dbus_object_clear_and_unexport ((NMDBusObject **) (location)) + +void nm_dbus_object_emit_signal_variant (NMDBusObject *self, + const NMDBusInterfaceInfoExtended *interface_info, + const GDBusSignalInfo *signal_info, + GVariant *args); + +void nm_dbus_object_emit_signal (NMDBusObject *self, + const NMDBusInterfaceInfoExtended *interface_info, + const GDBusSignalInfo *signal_info, + const char *format, + ...); + +#endif /* __NM_DBUS_OBJECT_H__ */ diff --git a/src/nm-dbus-utils.c b/src/nm-dbus-utils.c new file mode 100644 index 00000000..8e7dd122 --- /dev/null +++ b/src/nm-dbus-utils.c @@ -0,0 +1,312 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-dbus-utils.h" + +#include "nm-dbus-object.h" + +/*****************************************************************************/ + +const GDBusSignalInfo nm_signal_info_property_changed_legacy = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "PropertiesChanged", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("properties", "a{sv}"), + ), +); + +GDBusPropertyInfo * +nm_dbus_utils_interface_info_lookup_property (const GDBusInterfaceInfo *interface_info, + const char *property_name, + guint *property_idx) +{ + guint i; + + nm_assert (interface_info); + nm_assert (property_name); + + /* there is also g_dbus_interface_info_lookup_property(), however that makes use + * of a global cache. */ + if (interface_info->properties) { + for (i = 0; interface_info->properties[i]; i++) { + GDBusPropertyInfo *info = interface_info->properties[i]; + + if (nm_streq (info->name, property_name)) { + NM_SET_OUT (property_idx, i); + return info; + } + } + } + + return NULL; +} + +GDBusMethodInfo * +nm_dbus_utils_interface_info_lookup_method (const GDBusInterfaceInfo *interface_info, + const char *method_name) +{ + guint i; + + nm_assert (interface_info); + nm_assert (method_name); + + /* there is also g_dbus_interface_info_lookup_property(), however that makes use + * of a global cache. */ + if (interface_info->methods) { + for (i = 0; interface_info->methods[i]; i++) { + GDBusMethodInfo *info = interface_info->methods[i]; + + if (nm_streq (info->name, method_name)) + return info; + } + } + + return NULL; +} + +GVariant * +nm_dbus_utils_get_property (GObject *obj, + const char *signature, + const char *property_name) +{ + GParamSpec *pspec; + nm_auto_unset_gvalue GValue value = G_VALUE_INIT; + + nm_assert (G_IS_OBJECT (obj)); + nm_assert (g_variant_type_string_is_valid (signature)); + nm_assert (property_name && property_name[0]); + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (obj), property_name); + if (!pspec) + g_return_val_if_reached (NULL); + + g_value_init (&value, pspec->value_type); + g_object_get_property (obj, property_name, &value); + /* returns never-floating variant */ + return g_dbus_gvalue_to_gvariant (&value, G_VARIANT_TYPE (signature)); +} + +/*****************************************************************************/ + +void +nm_dbus_utils_g_value_set_object_path (GValue *value, gpointer object) +{ + const char *path; + + g_return_if_fail (!object || NM_IS_DBUS_OBJECT (object)); + + if ( object + && (path = nm_dbus_object_get_path (object))) + g_value_set_string (value, path); + else + g_value_set_string (value, NULL); +} + +void +nm_dbus_utils_g_value_set_object_path_still_exported (GValue *value, gpointer object) +{ + const char *path; + + g_return_if_fail (!object || NM_IS_DBUS_OBJECT (object)); + + if ( object + && (path = nm_dbus_object_get_path_still_exported (object))) + g_value_set_string (value, path); + else + g_value_set_string (value, "/"); +} + +void +nm_dbus_utils_g_value_set_object_path_from_hash (GValue *value, + GHashTable *hash /* has keys of NMDBusObject type. */, + gboolean expect_all_exported) +{ + NMDBusObject *obj; + char **strv; + guint i, n; + GHashTableIter iter; + + nm_assert (value); + nm_assert (hash); + + n = g_hash_table_size (hash); + strv = g_new (char *, n + 1); + i = 0; + g_hash_table_iter_init (&iter, hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &obj, NULL)) { + const char *path; + + path = nm_dbus_object_get_path (obj); + if (!path) { + nm_assert (!expect_all_exported); + continue; + } + strv[i++] = g_strdup (path); + } + nm_assert (i <= n); + strv[i] = NULL; + + /* sort the names, to give a well-defined, stable order. */ + nm_utils_strv_sort (strv, i); + + g_value_take_boxed (value, strv); +} + +const char ** +nm_dbus_utils_get_paths_for_clist (const CList *lst_head, + gssize lst_len, + guint member_offset, + gboolean expect_all_exported) +{ + const CList *iter; + const char **strv; + const char *path; + gsize i, n; + + nm_assert (lst_head); + + if (lst_len < 0) + n = c_list_length (lst_head); + else { + n = lst_len; + nm_assert (n == c_list_length (lst_head)); + } + + i = 0; + strv = g_new (const char *, n + 1); + c_list_for_each (iter, lst_head) { + NMDBusObject *obj = (NMDBusObject *) (((const char *) iter) - member_offset); + + path = nm_dbus_object_get_path (obj); + if (!path) { + nm_assert (expect_all_exported); + continue; + } + + nm_assert (i < n); + strv[i++] = path; + } + nm_assert (i <= n); + strv[i] = NULL; + + return strv; +} + +/*****************************************************************************/ + +void +nm_dbus_track_obj_path_init (NMDBusTrackObjPath *track, + GObject *target, + const GParamSpec *pspec) +{ + nm_assert (track); + nm_assert (G_IS_OBJECT (target)); + nm_assert (G_IS_PARAM_SPEC (pspec)); + + track->_obj = NULL; + track->_notify_target = target; + track->_notify_pspec = pspec; + track->_notify_signal_id = 0; + track->_visible = FALSE; +} + +void +nm_dbus_track_obj_path_deinit (NMDBusTrackObjPath *track) +{ + /* we allow deinit() to be called multiple times (e.g. from + * dispose(), which must be re-entrant). */ + nm_assert (track); + nm_assert (!track->_notify_target || G_IS_OBJECT (track->_notify_target)); + + nm_clear_g_signal_handler (track->obj, &track->_notify_signal_id); + track->_notify_target = NULL; + track->_notify_pspec = NULL; + track->_visible = FALSE; + nm_clear_g_object (&track->_obj); +} + +void +nm_dbus_track_obj_path_notify (const NMDBusTrackObjPath *track) +{ + nm_assert (track); + nm_assert (G_IS_OBJECT (track->_notify_target)); + nm_assert (G_IS_PARAM_SPEC (track->_notify_pspec)); + + g_object_notify_by_pspec (track->_notify_target, + (GParamSpec *) track->_notify_pspec); +} + +const char * +nm_dbus_track_obj_path_get (const NMDBusTrackObjPath *track) +{ + nm_assert (track); + nm_assert (G_IS_OBJECT (track->_notify_target)); + + return track->obj && track->visible + ? nm_dbus_object_get_path_still_exported (track->obj) + : NULL; +} + +static void +_track_obj_exported_changed (NMDBusObject *obj, + NMDBusTrackObjPath *track) +{ + nm_dbus_track_obj_path_notify (track); +} + +void +nm_dbus_track_obj_path_set (NMDBusTrackObjPath *track, + gpointer obj, + gboolean visible) +{ + gs_unref_object NMDBusObject *old_obj = NULL; + const char *old_path; + + nm_assert (track); + nm_assert (G_IS_OBJECT (track->_notify_target)); + + g_return_if_fail (!obj || NM_IS_DBUS_OBJECT (obj)); + + if ( track->obj == obj + && track->visible == !!visible) + return; + + old_path = nm_dbus_track_obj_path_get (track); + + track->_visible = visible; + + if (track->obj != obj) { + nm_clear_g_signal_handler (track->obj, &track->_notify_signal_id); + + old_obj = track->obj; + track->_obj = nm_g_object_ref (obj); + + if (obj) { + track->_notify_signal_id = g_signal_connect (obj, + NM_DBUS_OBJECT_EXPORTED_CHANGED, + G_CALLBACK (_track_obj_exported_changed), + track); + } + } + + if (!nm_streq0 (old_path, nm_dbus_track_obj_path_get (track))) + nm_dbus_track_obj_path_notify (track); +} diff --git a/src/nm-dbus-utils.h b/src/nm-dbus-utils.h new file mode 100644 index 00000000..1f8e96c4 --- /dev/null +++ b/src/nm-dbus-utils.h @@ -0,0 +1,213 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_DBUS_UTILS_H__ +#define __NM_DBUS_UTILS_H__ + +/*****************************************************************************/ + +struct _NMDBusInterfaceInfoExtended; +struct _NMDBusMethodInfoExtended; + +struct _NMDBusPropertyInfoExtendedBase { + GDBusPropertyInfo _parent; + const char *property_name; + + /* Whether the properties needs to be notified on the legacy + * PropertyChanged signal. This is only to preserve API, new + * properties should not use this. */ + bool include_in_legacy_property_changed; +}; + +struct _NMDBusPropertyInfoExtendedReadWritable { + struct _NMDBusPropertyInfoExtendedBase _base; + + /* this is the polkit permission type for authenticating setting + * the property. */ + const char *permission; + + /* this is the audit operation type for writing the property. */ + const char *audit_op; +}; + +typedef struct { + union { + + GDBusPropertyInfo _parent; + struct _NMDBusPropertyInfoExtendedBase _base; + struct _NMDBusPropertyInfoExtendedReadWritable writable; + + /* duplicate the base structure in the union, so that the common fields + * are accessible directly in the parent struct. */ + struct { + GDBusPropertyInfo parent; + const char *property_name; + + /* Whether the properties needs to be notified on the legacy + * PropertyChanged signal. This is only to preserve API, new + * properties should not use this. */ + bool include_in_legacy_property_changed; + }; + }; +} NMDBusPropertyInfoExtended; + +G_STATIC_ASSERT (G_STRUCT_OFFSET (NMDBusPropertyInfoExtended, property_name) == G_STRUCT_OFFSET (struct _NMDBusPropertyInfoExtendedBase, property_name)); +G_STATIC_ASSERT (G_STRUCT_OFFSET (NMDBusPropertyInfoExtended, include_in_legacy_property_changed) == G_STRUCT_OFFSET (struct _NMDBusPropertyInfoExtendedBase, include_in_legacy_property_changed)); + +#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_FULL(m_name, m_signature, m_property_name, m_include_in_legacy_property_changed) \ + ((GDBusPropertyInfo *) &((const struct _NMDBusPropertyInfoExtendedBase) { \ + ._parent = { \ + .ref_count = -1, \ + .name = m_name, \ + .signature = m_signature, \ + .flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE, \ + }, \ + .property_name = m_property_name, \ + .include_in_legacy_property_changed = m_include_in_legacy_property_changed, \ + })) + +#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE(m_name, m_signature, m_property_name) \ + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_FULL (m_name, m_signature, m_property_name, FALSE) + +/* define a legacy property. Do not use for new code. */ +#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L(m_name, m_signature, m_property_name) \ + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_FULL (m_name, m_signature, m_property_name, TRUE) + +#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_FULL(m_name, m_signature, m_property_name, m_permission, m_audit_op, m_include_in_legacy_property_changed) \ + ((GDBusPropertyInfo *) &((const struct _NMDBusPropertyInfoExtendedReadWritable) { \ + ._base = { \ + ._parent = { \ + .ref_count = -1, \ + .name = m_name, \ + .signature = m_signature, \ + .flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE, \ + }, \ + .property_name = m_property_name, \ + .include_in_legacy_property_changed = m_include_in_legacy_property_changed, \ + }, \ + .permission = m_permission, \ + .audit_op = m_audit_op, \ + })) + +#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE(m_name, m_signature, m_property_name, m_permission, m_audit_op) \ + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_FULL (m_name, m_signature, m_property_name, m_permission, m_audit_op, FALSE) + +/* define a legacy property. Do not use for new code. */ +#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L(m_name, m_signature, m_property_name, m_permission, m_audit_op) \ + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_FULL (m_name, m_signature, m_property_name, m_permission, m_audit_op, TRUE) + +typedef struct _NMDBusMethodInfoExtended { + GDBusMethodInfo parent; + void (*handle) (NMDBusObject *obj, + const struct _NMDBusInterfaceInfoExtended *interface_info, + const struct _NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters); + bool allow_during_shutdown; +} NMDBusMethodInfoExtended; + +#define NM_DEFINE_DBUS_METHOD_INFO_EXTENDED(parent_, ...) \ + ((GDBusMethodInfo *) (&((const NMDBusMethodInfoExtended) { \ + .parent = parent_, \ + __VA_ARGS__ \ + }))) + +typedef struct _NMDBusInterfaceInfoExtended { + GDBusInterfaceInfo parent; + + /* Whether the interface has a legacy property changed signal (@nm_signal_info_property_changed_legacy). + * New interfaces should not use this. */ + bool legacy_property_changed:1; +} NMDBusInterfaceInfoExtended; + +extern const GDBusSignalInfo nm_signal_info_property_changed_legacy; + +#define NM_DBUS_INTERFACE_INFOS(...) \ + ({ \ + static const NMDBusInterfaceInfoExtended *const _interface_infos[] = { \ + __VA_ARGS__, \ + NULL, \ + }; \ + _interface_infos; \ + }); + +/*****************************************************************************/ + +GDBusPropertyInfo *nm_dbus_utils_interface_info_lookup_property (const GDBusInterfaceInfo *interface_info, + const char *property_name, + guint *property_idx); + +GDBusMethodInfo *nm_dbus_utils_interface_info_lookup_method (const GDBusInterfaceInfo *interface_info, + const char *method_name); + +GVariant *nm_dbus_utils_get_property (GObject *obj, + const char *signature, + const char *property_name); + +/*****************************************************************************/ + +struct CList; + +const char **nm_dbus_utils_get_paths_for_clist (const struct CList *lst_head, + gssize lst_len, + guint member_offset, + gboolean expect_all_exported); + +void nm_dbus_utils_g_value_set_object_path (GValue *value, gpointer object); + +void nm_dbus_utils_g_value_set_object_path_still_exported (GValue *value, gpointer object); + +void nm_dbus_utils_g_value_set_object_path_from_hash (GValue *value, + GHashTable *hash, + gboolean expect_all_exported); + +/*****************************************************************************/ + +typedef struct { + union { + gpointer const obj; + gpointer _obj; + }; + GObject *_notify_target; + const GParamSpec *_notify_pspec; + gulong _notify_signal_id; + union { + const bool visible; + bool _visible; + }; +} NMDBusTrackObjPath; + +void nm_dbus_track_obj_path_init (NMDBusTrackObjPath *track, + GObject *target, + const GParamSpec *pspec); + +void nm_dbus_track_obj_path_deinit (NMDBusTrackObjPath *track); + +void nm_dbus_track_obj_path_notify (const NMDBusTrackObjPath *track); + +const char *nm_dbus_track_obj_path_get (const NMDBusTrackObjPath *track); + +void nm_dbus_track_obj_path_set (NMDBusTrackObjPath *track, + gpointer obj, + gboolean visible); + +#endif /* __NM_DBUS_UTILS_H__ */ diff --git a/src/nm-dhcp4-config.c b/src/nm-dhcp4-config.c index 10f58bc6..ceafddf5 100644 --- a/src/nm-dhcp4-config.c +++ b/src/nm-dhcp4-config.c @@ -26,9 +26,8 @@ #include "nm-dbus-interface.h" #include "nm-utils.h" -#include "nm-exported-object.h" - -#include "introspection/org.freedesktop.NetworkManager.DHCP4Config.h" +#include "nm-dbus-object.h" +#include "nm-core-utils.h" /*****************************************************************************/ @@ -41,15 +40,15 @@ typedef struct { } NMDhcp4ConfigPrivate; struct _NMDhcp4Config { - NMExportedObject parent; + NMDBusObject parent; NMDhcp4ConfigPrivate _priv; }; struct _NMDhcp4ConfigClass { - NMExportedObjectClass parent; + NMDBusObjectClass parent; }; -G_DEFINE_TYPE (NMDhcp4Config, nm_dhcp4_config, NM_TYPE_EXPORTED_OBJECT) +G_DEFINE_TYPE (NMDhcp4Config, nm_dhcp4_config, NM_TYPE_DBUS_OBJECT) #define NM_DHCP4_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDhcp4Config, NM_IS_DHCP4_CONFIG) @@ -60,22 +59,14 @@ nm_dhcp4_config_set_options (NMDhcp4Config *self, GHashTable *options) { NMDhcp4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (self); - GHashTableIter iter; - const char *key, *value; - GVariantBuilder builder; + GVariant *val; g_return_if_fail (NM_IS_DHCP4_CONFIG (self)); - g_return_if_fail (options != NULL); + g_return_if_fail (options); + val = nm_utils_strdict_to_variant (options); g_variant_unref (priv->options); - - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); - g_hash_table_iter_init (&iter, options); - while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) - g_variant_builder_add (&builder, "{sv}", key, g_variant_new_string (value)); - - priv->options = g_variant_builder_end (&builder); - g_variant_ref_sink (priv->options); + priv->options = g_variant_ref_sink (val); _notify (self, PROP_OPTIONS); } @@ -147,17 +138,31 @@ finalize (GObject *object) G_OBJECT_CLASS (nm_dhcp4_config_parent_class)->finalize (object); } +static const NMDBusInterfaceInfoExtended interface_info_dhcp4_config = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DHCP4_CONFIG, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Options", "a{sv}", NM_DHCP4_CONFIG_OPTIONS), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_dhcp4_config_class_init (NMDhcp4ConfigClass *config_class) { GObjectClass *object_class = G_OBJECT_CLASS (config_class); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (config_class); object_class->get_property = get_property; object_class->finalize = finalize; - exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/DHCP4Config"); - exported_object_class->export_on_construction = TRUE; + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/DHCP4Config"); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_dhcp4_config); + dbus_object_class->export_on_construction = TRUE; obj_properties[PROP_OPTIONS] = g_param_spec_variant (NM_DHCP4_CONFIG_OPTIONS, "", "", @@ -167,8 +172,4 @@ nm_dhcp4_config_class_init (NMDhcp4ConfigClass *config_class) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (config_class), - NMDBUS_TYPE_DHCP4_CONFIG_SKELETON, - NULL); } diff --git a/src/nm-dhcp6-config.c b/src/nm-dhcp6-config.c index d51d77ef..d477521a 100644 --- a/src/nm-dhcp6-config.c +++ b/src/nm-dhcp6-config.c @@ -26,9 +26,8 @@ #include "nm-dbus-interface.h" #include "nm-utils.h" -#include "nm-exported-object.h" - -#include "introspection/org.freedesktop.NetworkManager.DHCP6Config.h" +#include "nm-dbus-object.h" +#include "nm-core-utils.h" /*****************************************************************************/ @@ -41,15 +40,15 @@ typedef struct { } NMDhcp6ConfigPrivate; struct _NMDhcp6Config { - NMExportedObject parent; + NMDBusObject parent; NMDhcp6ConfigPrivate _priv; }; struct _NMDhcp6ConfigClass { - NMExportedObjectClass parent; + NMDBusObjectClass parent; }; -G_DEFINE_TYPE (NMDhcp6Config, nm_dhcp6_config, NM_TYPE_EXPORTED_OBJECT) +G_DEFINE_TYPE (NMDhcp6Config, nm_dhcp6_config, NM_TYPE_DBUS_OBJECT) #define NM_DHCP6_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDhcp6Config, NM_IS_DHCP6_CONFIG) @@ -60,22 +59,14 @@ nm_dhcp6_config_set_options (NMDhcp6Config *self, GHashTable *options) { NMDhcp6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (self); - GHashTableIter iter; - const char *key, *value; - GVariantBuilder builder; + GVariant *val; g_return_if_fail (NM_IS_DHCP6_CONFIG (self)); - g_return_if_fail (options != NULL); + g_return_if_fail (options); + val = nm_utils_strdict_to_variant (options); g_variant_unref (priv->options); - - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); - g_hash_table_iter_init (&iter, options); - while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) - g_variant_builder_add (&builder, "{sv}", key, g_variant_new_string (value)); - - priv->options = g_variant_builder_end (&builder); - g_variant_ref_sink (priv->options); + priv->options = g_variant_ref_sink (val); _notify (self, PROP_OPTIONS); } @@ -145,17 +136,31 @@ finalize (GObject *object) G_OBJECT_CLASS (nm_dhcp6_config_parent_class)->finalize (object); } +static const NMDBusInterfaceInfoExtended interface_info_dhcp6_config = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DHCP6_CONFIG, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Options", "a{sv}", NM_DHCP6_CONFIG_OPTIONS), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_dhcp6_config_class_init (NMDhcp6ConfigClass *config_class) { GObjectClass *object_class = G_OBJECT_CLASS (config_class); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (config_class); object_class->get_property = get_property; object_class->finalize = finalize; - exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/DHCP6Config"); - exported_object_class->export_on_construction = TRUE; + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/DHCP6Config"); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_dhcp6_config); + dbus_object_class->export_on_construction = TRUE; obj_properties[PROP_OPTIONS] = g_param_spec_variant (NM_DHCP6_CONFIG_OPTIONS, "", "", @@ -165,8 +170,4 @@ nm_dhcp6_config_class_init (NMDhcp6ConfigClass *config_class) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (config_class), - NMDBUS_TYPE_DHCP6_CONFIG_SKELETON, - NULL); } diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index 237afdef..368b781a 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -284,9 +284,10 @@ fill_device_props (NMDevice *device, g_variant_new_uint32 (nm_device_get_device_type (device))); g_variant_builder_add (dev_builder, "{sv}", NMD_DEVICE_PROPS_STATE, g_variant_new_uint32 (nm_device_get_state (device))); - if (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (device))) + if (nm_dbus_object_is_exported (NM_DBUS_OBJECT (device))) { g_variant_builder_add (dev_builder, "{sv}", NMD_DEVICE_PROPS_PATH, - g_variant_new_object_path (nm_exported_object_get_path (NM_EXPORTED_OBJECT (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) @@ -345,8 +346,8 @@ static void _ensure_requests (void) { if (G_UNLIKELY (requests == NULL)) { - requests = g_hash_table_new_full (g_direct_hash, - g_direct_equal, + requests = g_hash_table_new_full (nm_direct_hash, + NULL, NULL, (GDestroyNotify) dispatcher_info_free); } @@ -546,7 +547,7 @@ _dispatcher_call (NMDispatcherAction action, _LOGD ("(%u) (%s) dispatching action '%s'%s", reqid, - vpn_iface ? vpn_iface : nm_device_get_iface (device), + vpn_iface ?: nm_device_get_iface(device), action_to_string (action), blocking ? " (blocking)" @@ -578,7 +579,7 @@ _dispatcher_call (NMDispatcherAction action, const char *connection_path; const char *filename; - connection_path = nm_connection_get_path (NM_CONNECTION (settings_connection)); + connection_path = nm_dbus_object_get_path (NM_DBUS_OBJECT (settings_connection)); if (connection_path) { g_variant_builder_add (&connection_props, "{sv}", NMD_CONNECTION_PROPS_PATH, @@ -630,9 +631,7 @@ _dispatcher_call (NMDispatcherAction action, if (!device_dhcp6_props) device_dhcp6_props = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0)); -#if WITH_CONCHECK connectivity_state_string = nm_connectivity_state_to_string (connectivity_state); -#endif /* Send the action to the dispatcher */ if (blocking) { @@ -651,7 +650,7 @@ _dispatcher_call (NMDispatcherAction action, device_dhcp4_props, device_dhcp6_props, connectivity_state_string, - vpn_iface ? vpn_iface : "", + vpn_iface ?: "", &vpn_proxy_props, &vpn_ip4_props, &vpn_ip6_props, @@ -689,7 +688,7 @@ _dispatcher_call (NMDispatcherAction action, device_dhcp4_props, device_dhcp6_props, connectivity_state_string, - vpn_iface ? vpn_iface : "", + vpn_iface ?: "", &vpn_proxy_props, &vpn_ip4_props, &vpn_ip6_props, diff --git a/src/nm-dispatcher.h b/src/nm-dispatcher.h index 4448e817..1cdeeb88 100644 --- a/src/nm-dispatcher.h +++ b/src/nm-dispatcher.h @@ -82,7 +82,6 @@ gboolean nm_dispatcher_call_connectivity (NMConnectivityState state, gpointer user_data, guint *out_call_id); - void nm_dispatcher_call_cancel (guint call_id); void nm_dispatcher_init (void); diff --git a/src/nm-exported-object.c b/src/nm-exported-object.c deleted file mode 100644 index 94264caa..00000000 --- a/src/nm-exported-object.c +++ /dev/null @@ -1,1055 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2014-2016 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-exported-object.h" - -#include -#include - -#include "nm-bus-manager.h" - -#include "devices/nm-device.h" -#include "nm-active-connection.h" -#include "introspection/org.freedesktop.NetworkManager.Device.Statistics.h" - -#if NM_MORE_ASSERTS >= 2 -#define _ASSERT_NO_EARLY_EXPORT -#endif - -/*****************************************************************************/ - -static gboolean quitting = FALSE; - -/*****************************************************************************/ - -NM_GOBJECT_PROPERTIES_DEFINE (NMExportedObject, - PROP_PATH, -); - -typedef struct { - GDBusInterfaceSkeleton *interface; - guint property_changed_signal_id; - GHashTable *pending_notifies; -} InterfaceData; - -typedef struct _NMExportedObjectPrivate { - NMBusManager *bus_mgr; - char *path; - - InterfaceData *interfaces; - guint num_interfaces; - - guint notify_idle_id; - -#ifdef _ASSERT_NO_EARLY_EXPORT - bool _constructed:1; -#endif -} NMExportedObjectPrivate; - -G_DEFINE_ABSTRACT_TYPE (NMExportedObject, nm_exported_object, G_TYPE_DBUS_OBJECT_SKELETON); - -#define NM_EXPORTED_OBJECT_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMExportedObject, NM_IS_EXPORTED_OBJECT) - -/*****************************************************************************/ - -typedef struct { - GHashTable *properties; - GSList *skeleton_types; - GArray *methods; -} NMExportedObjectClassInfo; - -static NM_CACHED_QUARK_FCN ("NMExportedObjectClassInfo", nm_exported_object_class_info_quark) - -/*****************************************************************************/ - -#define _NMLOG_DOMAIN LOGD_CORE -#define _NMLOG(level, ...) __NMLOG_DEFAULT_WITH_ADDR (level, _NMLOG_DOMAIN, "exported-object", __VA_ARGS__) - -#define _NMLOG2_DOMAIN LOGD_DBUS_PROPS -#define _NMLOG2(level, ...) __NMLOG_DEFAULT_WITH_ADDR (level, _NMLOG2_DOMAIN, "properties-changed", __VA_ARGS__) - -/*****************************************************************************/ - -/* "AddConnectionUnsaved" -> "handle-add-connection-unsaved" */ -char * -nm_exported_object_skeletonify_method_name (const char *dbus_method_name) -{ - GString *out; - const char *p; - - out = g_string_new ("handle"); - for (p = dbus_method_name; *p; p++) { - if (g_ascii_isupper (*p) || p == dbus_method_name) { - g_string_append_c (out, '-'); - g_string_append_c (out, g_ascii_tolower (*p)); - } else - g_string_append_c (out, *p); - } - - return g_string_free (out, FALSE); -} - -/* "can-modify" -> "CanModify" */ -static char * -dbusify_name (const char *gobject_name) -{ - GString *out; - const char *p; - gboolean capitalize = TRUE; - - out = g_string_new (""); - for (p = gobject_name; *p; p++) { - if (capitalize) { - g_string_append_c (out, g_ascii_toupper (*p)); - capitalize = FALSE; - } else if (*p == '-') - capitalize = TRUE; - else - g_string_append_c (out, *p); - } - - return g_string_free (out, FALSE); -} - -/* "can_modify" -> "can-modify". Returns %NULL if @gobject_name contains no underscores */ -static char * -hyphenify_name (const char *gobject_name) -{ - char *hyphen_name, *p; - - if (!strchr (gobject_name, '_')) - return NULL; - - hyphen_name = g_strdup (gobject_name); - for (p = hyphen_name; *p; p++) { - if (*p == '_') - *p = '-'; - } - return hyphen_name; -} - -/* Called when an #NMExportedObject emits a signal that corresponds to a D-Bus - * signal, and re-emits that signal on the correct skeleton object as well. - */ -static gboolean -nm_exported_object_signal_hook (GSignalInvocationHint *ihint, - guint n_param_values, - const GValue *param_values, - gpointer data) -{ - NMExportedObject *self = g_value_get_object (¶m_values[0]); - NMExportedObjectPrivate *priv; - GSignalQuery *signal_info = data; - GDBusInterfaceSkeleton *interface = NULL; - GValue *dbus_param_values; - guint i; - - priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self); - if (!priv->path) - return TRUE; - - for (i = 0; i < priv->num_interfaces; i++) { - InterfaceData *ifdata = &priv->interfaces[i]; - - if (g_type_is_a (G_OBJECT_TYPE (ifdata->interface), signal_info->itype)) { - interface = ifdata->interface; - break; - } - } - g_return_val_if_fail (interface != NULL, TRUE); - - dbus_param_values = g_newa (GValue, n_param_values); - memset (dbus_param_values, 0, sizeof (GValue) * n_param_values); - g_value_init (&dbus_param_values[0], G_OBJECT_TYPE (interface)); - g_value_set_object (&dbus_param_values[0], interface); - for (i = 1; i < n_param_values; i++) { - if (g_type_is_a (param_values[i].g_type, NM_TYPE_EXPORTED_OBJECT)) { - NMExportedObject *arg = g_value_get_object (¶m_values[i]); - - g_value_init (&dbus_param_values[i], G_TYPE_STRING); - if (arg && nm_exported_object_is_exported (arg)) - g_value_set_string (&dbus_param_values[i], nm_exported_object_get_path (arg)); - else - g_value_set_string (&dbus_param_values[i], "/"); - } else { - g_value_init (&dbus_param_values[i], param_values[i].g_type); - g_value_copy (¶m_values[i], &dbus_param_values[i]); - } - } - - g_signal_emitv (dbus_param_values, signal_info->signal_id, 0, NULL); - - for (i = 0; i < n_param_values; i++) - g_value_unset (&dbus_param_values[i]); - - return TRUE; -} - -/** - * nm_exported_object_class_add_interface: - * @object_class: an #NMExportedObjectClass - * @dbus_skeleton_type: the type of the #GDBusInterfaceSkeleton to add - * @...: method name / handler pairs, %NULL-terminated - * - * Adds @dbus_skeleton_type to the list of D-Bus interfaces implemented by - * @object_class. Instances of @object_class will automatically have a skeleton - * of that type created, which will be exported when you call - * nm_exported_object_export(). - * - * The skeleton's properties will be initialized from the #NMExportedObject's, - * and bidirectional bindings will be set up between them. When exported - * properties change, both the org.freedesktop.DBus.Properties.PropertiesChanged - * signal and the traditional NetworkManager PropertiesChanged signal will be - * emitted. - * - * When a signal is emitted on an #NMExportedObject that has the same name as a - * signal on @dbus_skeleton_type, it will automatically be emitted on the - * skeleton as well; #NMExportedObject arguments in the signal will be converted - * to D-Bus object paths in the skeleton signal. - * - * The arguments after @dbus_skeleton_type are pairs of D-Bus method names (in - * CamelCase), and the corresponding handlers for them (which must have the same - * prototype as the corresponding "handle-..." signal on @dbus_skeleton_type, - * except with no return value, and with the first argument being an object of - * @object_class's type, not of @dbus_skeleton_type). - * - * It is a programmer error if: - * - @object_class does not define a property of the same name and type as - * each of @dbus_skeleton_type's properties. - * - @object_class does not define a signal with the same name and arguments - * as each of @dbus_skeleton_type's signals. - * - the list of method names includes any names that do not correspond to - * "handle-" signals on @dbus_skeleton_type. - * - the list of method names does not include every method defined by - * @dbus_skeleton_type. - */ -void -nm_exported_object_class_add_interface (NMExportedObjectClass *object_class, - GType dbus_skeleton_type, - ...) -{ - NMExportedObjectClassInfo *classinfo; - NMExportedObjectDBusMethodImpl method; - va_list ap; - const char *method_name; - GCallback impl; - gs_free GType *interfaces = NULL; - guint n_interfaces; - guint n_signals, n_method_signals; - guint object_signal_id; - GSignalQuery query; - int i, s; - GObjectClass *dbus_object_class; - gs_free GParamSpec **dbus_properties = NULL; - GParamSpec *object_property; - guint n_dbus_properties; - - g_return_if_fail (NM_IS_EXPORTED_OBJECT_CLASS (object_class)); - g_return_if_fail (g_type_is_a (dbus_skeleton_type, G_TYPE_DBUS_INTERFACE_SKELETON)); - - classinfo = g_type_get_qdata (G_TYPE_FROM_CLASS (object_class), - nm_exported_object_class_info_quark ()); - if (!classinfo) { - classinfo = g_slice_new (NMExportedObjectClassInfo); - classinfo->skeleton_types = NULL; - classinfo->methods = g_array_new (FALSE, FALSE, sizeof (NMExportedObjectDBusMethodImpl)); - classinfo->properties = g_hash_table_new (nm_str_hash, g_str_equal); - g_type_set_qdata (G_TYPE_FROM_CLASS (object_class), - nm_exported_object_class_info_quark (), classinfo); - } - - classinfo->skeleton_types = g_slist_prepend (classinfo->skeleton_types, - GSIZE_TO_POINTER (dbus_skeleton_type)); - - /* Ensure @dbus_skeleton_type's class_init has run, so its signals/properties - * will be defined. - */ - dbus_object_class = g_type_class_ref (dbus_skeleton_type); - - /* Add method implementations from the varargs */ - va_start (ap, dbus_skeleton_type); - while ((method_name = va_arg (ap, const char *)) && (impl = va_arg (ap, GCallback))) { - method.dbus_skeleton_type = dbus_skeleton_type; - method.method_name = nm_exported_object_skeletonify_method_name (method_name); - g_assert (g_signal_lookup (method.method_name, dbus_skeleton_type) != 0); - method.impl = impl; - - g_array_append_val (classinfo->methods, method); - } - va_end (ap); - - /* Properties */ - dbus_properties = g_object_class_list_properties (dbus_object_class, &n_dbus_properties); - for (i = 0; i < n_dbus_properties; i++) { - char *hyphen_name; - - if (g_str_has_prefix (dbus_properties[i]->name, "g-")) - continue; - - object_property = g_object_class_find_property (G_OBJECT_CLASS (object_class), - dbus_properties[i]->name); - g_assert (object_property != NULL); - g_assert (object_property->value_type == dbus_properties[i]->value_type); - - g_assert (!g_hash_table_contains (classinfo->properties, dbus_properties[i]->name)); - g_hash_table_insert (classinfo->properties, - g_strdup (dbus_properties[i]->name), - dbusify_name (dbus_properties[i]->name)); - hyphen_name = hyphenify_name (dbus_properties[i]->name); - if (hyphen_name) { - g_assert (!g_hash_table_contains (classinfo->properties, hyphen_name)); - g_hash_table_insert (classinfo->properties, - hyphen_name, - dbusify_name (dbus_properties[i]->name)); - } - } - - /* Signals. Unlike g_object_class_list_properties(), g_signal_list_ids() is - * "shallow", so we need to query each implemented gdbus-generated interface - * separately. - */ - interfaces = g_type_interfaces (dbus_skeleton_type, &n_interfaces); - n_method_signals = 0; - for (i = 0; i < n_interfaces; i++) { - gs_free guint *dbus_signals = NULL; - - dbus_signals = g_signal_list_ids (interfaces[i], &n_signals); - for (s = 0; s < n_signals; s++) { - g_signal_query (dbus_signals[s], &query); - - /* PropertiesChanged is handled specially */ - if (!strcmp (query.signal_name, "properties-changed")) - continue; - - if (g_str_has_prefix (query.signal_name, "handle-")) { - n_method_signals++; - continue; - } - - object_signal_id = g_signal_lookup (query.signal_name, G_TYPE_FROM_CLASS (object_class)); - g_assert (object_signal_id != 0); - - g_signal_add_emission_hook (object_signal_id, 0, - nm_exported_object_signal_hook, - g_memdup (&query, sizeof (query)), - g_free); - } - } - - g_type_class_unref (dbus_object_class); -} - -/*****************************************************************************/ - -/* "meta-marshaller" that receives the skeleton "handle-foo" signal, replaces - * the skeleton object with an #NMExportedObject in the parameters, drops the - * user_data parameter, and adds a "TRUE" return value (indicating to gdbus that - * the signal was handled). - */ -static void -nm_exported_object_meta_marshal (GClosure *closure, GValue *return_value, - guint n_param_values, const GValue *param_values, - gpointer invocation_hint, gpointer marshal_data) -{ - GValue *local_param_values; - - local_param_values = g_new0 (GValue, n_param_values); - g_value_init (&local_param_values[0], G_TYPE_POINTER); - g_value_set_pointer (&local_param_values[0], closure->data); - memcpy (local_param_values + 1, param_values + 1, (n_param_values - 1) * sizeof (GValue)); - - g_cclosure_marshal_generic (closure, NULL, - n_param_values, local_param_values, - invocation_hint, - ((GCClosure *)closure)->callback); - g_value_set_boolean (return_value, TRUE); - - g_value_unset (&local_param_values[0]); - g_free (local_param_values); -} - -static NM_CACHED_QUARK_FCN ("skeleton-data", _skeleton_data_quark) - -typedef struct { - GBinding **prop_bindings; - gulong *method_signals; -} SkeletonData; - -GDBusInterfaceSkeleton * -nm_exported_object_skeleton_create (GType dbus_skeleton_type, - GObjectClass *object_class, - const NMExportedObjectDBusMethodImpl *methods, - guint methods_len, - GObject *target) -{ - GDBusInterfaceSkeleton *interface; - gs_free GParamSpec **properties = NULL; - SkeletonData *skeleton_data; - guint n_properties; - guint i, j; - - interface = G_DBUS_INTERFACE_SKELETON (g_object_new (dbus_skeleton_type, NULL)); - - skeleton_data = g_slice_new (SkeletonData); - - /* Bind properties */ - properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (interface), &n_properties); - skeleton_data->prop_bindings = g_new (GBinding *, n_properties + 1); - for (i = 0, j = 0; i < n_properties; i++) { - GParamSpec *nm_property; - GBindingFlags flags; - GBinding *prop_binding; - - nm_property = g_object_class_find_property (object_class, properties[i]->name); - if (!nm_property) - continue; - - flags = G_BINDING_SYNC_CREATE; - if ( (nm_property->flags & G_PARAM_WRITABLE) - && !(nm_property->flags & G_PARAM_CONSTRUCT_ONLY)) - flags |= G_BINDING_BIDIRECTIONAL; - prop_binding = g_object_bind_property (target, properties[i]->name, - interface, properties[i]->name, - flags); - if (prop_binding) - skeleton_data->prop_bindings[j++] = prop_binding; - } - skeleton_data->prop_bindings[j++] = NULL; - - /* Bind methods */ - skeleton_data->method_signals = g_new (gulong, methods_len + 1); - for (i = 0, j = 0; i < methods_len; i++) { - const NMExportedObjectDBusMethodImpl *method = &methods[i]; - GClosure *closure; - gulong method_signal; - - /* ignore methods that are for a different skeleton-type. */ - if ( method->dbus_skeleton_type - && method->dbus_skeleton_type != dbus_skeleton_type) - continue; - - closure = g_cclosure_new_swap (method->impl, target, NULL); - g_closure_set_meta_marshal (closure, NULL, nm_exported_object_meta_marshal); - method_signal = g_signal_connect_closure (interface, method->method_name, closure, FALSE); - - if (method_signal != 0) - skeleton_data->method_signals[j++] = method_signal; - } - skeleton_data->method_signals[j++] = 0; - - g_object_set_qdata ((GObject *) interface, _skeleton_data_quark (), skeleton_data); - - return interface; -} - -static void -nm_exported_object_create_skeletons (NMExportedObject *self, - GType object_type) -{ - NMExportedObjectPrivate *priv; - GObjectClass *object_class; - NMExportedObjectClassInfo *classinfo; - GSList *iter; - const NMExportedObjectDBusMethodImpl *methods; - guint i, methods_len; - guint num_interfaces; - InterfaceData *interfaces; - - classinfo = g_type_get_qdata (object_type, nm_exported_object_class_info_quark ()); - if (!classinfo) - return; - - object_class = g_type_class_peek (object_type); - priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self); - - methods = classinfo->methods->len ? &g_array_index (classinfo->methods, NMExportedObjectDBusMethodImpl, 0) : NULL; - methods_len = classinfo->methods->len; - - num_interfaces = g_slist_length (classinfo->skeleton_types); - g_return_if_fail (num_interfaces > 0); - - interfaces = g_slice_alloc (sizeof (InterfaceData) * (num_interfaces + priv->num_interfaces)); - - for (i = num_interfaces, iter = classinfo->skeleton_types; iter; iter = iter->next) { - InterfaceData *ifdata = &interfaces[--i]; - - ifdata->interface = nm_exported_object_skeleton_create (GPOINTER_TO_SIZE (iter->data), - object_class, - methods, - methods_len, - (GObject *) self); - g_dbus_object_skeleton_add_interface ((GDBusObjectSkeleton *) self, ifdata->interface); - - ifdata->property_changed_signal_id = g_signal_lookup ("properties-changed", G_OBJECT_TYPE (ifdata->interface)); - - ifdata->pending_notifies = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify) g_variant_unref); - } - nm_assert (i == 0); - - /* The list of interfaces priv->interfaces is to be sorted from parent-class to derived-class. - * On the other hand, if one class defines multiple interfaces, the interfaces are sorted in - * the order of calls to nm_exported_object_class_add_interface(). */ - if (priv->num_interfaces > 0) { - memcpy (&interfaces[num_interfaces], priv->interfaces, sizeof (InterfaceData) * priv->num_interfaces); - g_slice_free1 (sizeof (InterfaceData) * priv->num_interfaces, priv->interfaces); - } - - priv->num_interfaces = num_interfaces + priv->num_interfaces; - priv->interfaces = interfaces; -} - -void -nm_exported_object_skeleton_release (GDBusInterfaceSkeleton *interface) -{ - SkeletonData *skeleton_data; - guint j; - - g_return_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface)); - - skeleton_data = g_object_steal_qdata ((GObject *) interface, _skeleton_data_quark ()); - - for (j = 0; skeleton_data->prop_bindings[j]; j++) - g_object_unref (skeleton_data->prop_bindings[j]); - for (j = 0; skeleton_data->method_signals[j]; j++) - g_signal_handler_disconnect (interface, skeleton_data->method_signals[j]); - - g_free (skeleton_data->prop_bindings); - g_free (skeleton_data->method_signals); - g_slice_free (SkeletonData, skeleton_data); - - g_object_unref (interface); -} - -static void -nm_exported_object_destroy_skeletons (NMExportedObject *self) -{ - NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self); - guint n; - - g_return_if_fail (priv->num_interfaces > 0); - nm_assert (priv->interfaces); - - n = priv->num_interfaces; - - while (priv->num_interfaces > 0) { - InterfaceData *ifdata = &priv->interfaces[--priv->num_interfaces]; - - g_dbus_object_skeleton_remove_interface ((GDBusObjectSkeleton *) self, ifdata->interface); - nm_exported_object_skeleton_release (ifdata->interface); - g_hash_table_destroy (ifdata->pending_notifies); - } - - g_slice_free1 (sizeof (InterfaceData) * n, priv->interfaces); - priv->interfaces = NULL; -} - -static char * -_create_export_path (NMExportedObjectClass *klass) -{ - const char *class_export_path, *p; - static GHashTable *prefix_counters; - guint64 *counter; - - class_export_path = klass->export_path; - - nm_assert (class_export_path); - - p = strchr (class_export_path, '%'); - if (p) { - if (G_UNLIKELY (!prefix_counters)) - prefix_counters = g_hash_table_new (nm_str_hash, g_str_equal); - - nm_assert (p[1] == 'l'); - nm_assert (p[2] == 'l'); - nm_assert (p[3] == 'u'); - nm_assert (p[4] == '\0'); - - counter = g_hash_table_lookup (prefix_counters, class_export_path); - if (!counter) { - counter = g_slice_new0 (guint64); - g_hash_table_insert (prefix_counters, (char *) class_export_path, counter); - } - - NM_PRAGMA_WARNING_DISABLE("-Wformat-nonliteral") - return g_strdup_printf (class_export_path, (unsigned long long) (++(*counter))); - NM_PRAGMA_WARNING_REENABLE - } - - return g_strdup (class_export_path); -} - -/** - * nm_exported_object_get_path: - * @self: an #NMExportedObject - * - * Gets @self's D-Bus path. - * - * Returns: @self's D-Bus path, or %NULL if @self is not exported. - */ -const char * -nm_exported_object_get_path (NMExportedObject *self) -{ - g_return_val_if_fail (NM_IS_EXPORTED_OBJECT (self), NULL); - - return NM_EXPORTED_OBJECT_GET_PRIVATE (self)->path; -} - -/** - * nm_exported_object_is_exported: - * @self: an #NMExportedObject - * - * Checks if @self is exported - * - * Returns: %TRUE if @self is exported - */ -gboolean -nm_exported_object_is_exported (NMExportedObject *self) -{ - g_return_val_if_fail (NM_IS_EXPORTED_OBJECT (self), FALSE); - - return NM_EXPORTED_OBJECT_GET_PRIVATE (self)->path != NULL; -} - -/** - * nm_exported_object_export: - * @self: an #NMExportedObject - * - * Exports @self on all active and future D-Bus connections. - * - * The path to export @self on is taken from its #NMObjectClass's %export_path - * member. If the %export_path contains "%u", then it will be replaced with a - * monotonically increasing integer ID (with each distinct %export_path having - * its own counter). Otherwise, %export_path will be used literally (implying - * that @self must be a singleton). - * - * Returns: the path @self was exported under - */ -const char * -nm_exported_object_export (NMExportedObject *self) -{ - NMExportedObjectPrivate *priv; - GType type; - - g_return_val_if_fail (NM_IS_EXPORTED_OBJECT (self), NULL); - priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self); - - g_return_val_if_fail (!priv->path, priv->path); - g_return_val_if_fail (!priv->bus_mgr, priv->path); - -#ifdef _ASSERT_NO_EARLY_EXPORT - nm_assert (priv->_constructed); -#endif - - priv->bus_mgr = nm_bus_manager_get (); - if (!priv->bus_mgr) - g_return_val_if_reached (NULL); - g_object_add_weak_pointer ((GObject *) priv->bus_mgr, (gpointer *) &priv->bus_mgr); - - type = G_OBJECT_TYPE (self); - while (type != NM_TYPE_EXPORTED_OBJECT) { - nm_exported_object_create_skeletons (self, type); - type = g_type_parent (type); - } - - priv->path = _create_export_path (NM_EXPORTED_OBJECT_GET_CLASS (self)); - - _LOGT ("export: \"%s\"", priv->path); - g_dbus_object_skeleton_set_object_path (G_DBUS_OBJECT_SKELETON (self), priv->path); - - /* Important: priv->path and priv->interfaces must not change while - * the object is registered. */ - - nm_bus_manager_register_object (priv->bus_mgr, (GDBusObjectSkeleton *) self); - - _notify (self, PROP_PATH); - - return priv->path; -} - -/** - * nm_exported_object_unexport: - * @self: an #NMExportedObject - * - * Unexports @self on all active D-Bus connections (and prevents it from being - * auto-exported on future connections). - */ -void -nm_exported_object_unexport (NMExportedObject *self) -{ - NMExportedObjectPrivate *priv; - - g_return_if_fail (NM_IS_EXPORTED_OBJECT (self)); - priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self); - - g_return_if_fail (priv->path); - - /* Important: priv->path and priv->interfaces must not change while - * the object is registered. */ - - _LOGT ("unexport: \"%s\"", priv->path); - - if (priv->bus_mgr) { - nm_bus_manager_unregister_object (priv->bus_mgr, (GDBusObjectSkeleton *) self); - g_object_remove_weak_pointer ((GObject *) priv->bus_mgr, (gpointer *) &priv->bus_mgr); - priv->bus_mgr = NULL; - } - - nm_exported_object_destroy_skeletons (self); - - g_dbus_object_skeleton_set_object_path ((GDBusObjectSkeleton *) self, NULL); - - g_clear_pointer (&priv->path, g_free); - - nm_clear_g_source (&priv->notify_idle_id); - - _notify (self, PROP_PATH); -} - -/*****************************************************************************/ - -void -_nm_exported_object_clear_and_unexport (NMExportedObject **location) -{ - NMExportedObject *self; - NMExportedObjectPrivate *priv; - - if (!location || !*location) - return; - - self = *location; - *location = NULL; - - g_return_if_fail (NM_IS_EXPORTED_OBJECT (self)); - - priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self); - - if (priv->path) - nm_exported_object_unexport (self); - - g_object_unref (self); -} - -/*****************************************************************************/ - -GDBusInterfaceSkeleton * -nm_exported_object_get_interface_by_type (NMExportedObject *self, GType interface_type) -{ - NMExportedObjectPrivate *priv; - guint i; - - g_return_val_if_fail (NM_IS_EXPORTED_OBJECT (self), NULL); - - priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self); - - g_return_val_if_fail (priv->path, NULL); - g_return_val_if_fail (priv->num_interfaces > 0, NULL); - - nm_assert (priv->interfaces); - - for (i = 0; i < priv->num_interfaces; i++) { - InterfaceData *ifdata = &priv->interfaces[i]; - - if (G_TYPE_CHECK_INSTANCE_TYPE (ifdata->interface, interface_type)) - return ifdata->interface; - } - return NULL; -} - -/*****************************************************************************/ - -void -nm_exported_object_class_set_quitting (void) -{ - quitting = TRUE; -} - -/*****************************************************************************/ - -typedef struct { - const char *property_name; - GVariant *variant; -} PendingNotifiesItem; - -static int -_sort_pending_notifies (gconstpointer a, gconstpointer b, gpointer user_data) -{ - return strcmp (((const PendingNotifiesItem *) a)->property_name, - ((const PendingNotifiesItem *) b)->property_name); -} - -static gboolean -idle_emit_properties_changed (gpointer self) -{ - NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (NM_EXPORTED_OBJECT (self)); - guint k; - - priv->notify_idle_id = 0; - - for (k = 0; k < priv->num_interfaces; k++) { - InterfaceData *ifdata = &priv->interfaces[k]; - gs_unref_variant GVariant *variant = NULL; - PendingNotifiesItem *values; - GVariantBuilder notifies; - GHashTableIter hash_iter; - guint i, n; - - n = g_hash_table_size (ifdata->pending_notifies); - if (n == 0) - continue; - - nm_assert (ifdata->property_changed_signal_id); - - /* We use here alloca in a loop, something that is usually avoided. - * But the number of interfaces "priv->num_interfaces" is small (determined by - * the depth of the type inheritance) and the number of possible pending_notifies - * "n" is small (determined by the number of GObject properties). */ - values = g_alloca (sizeof (values[0]) * n); - - i = 0; - g_hash_table_iter_init (&hash_iter, ifdata->pending_notifies); - while (g_hash_table_iter_next (&hash_iter, (gpointer) &values[i].property_name, (gpointer) &values[i].variant)) - i++; - nm_assert (i == n); - - g_qsort_with_data (values, n, sizeof (values[0]), _sort_pending_notifies, NULL); - - g_variant_builder_init (¬ifies, G_VARIANT_TYPE_VARDICT); - for (i = 0; i < n; i++) - g_variant_builder_add (¬ifies, "{sv}", values[i].property_name, values[i].variant); - variant = g_variant_ref_sink (g_variant_builder_end (¬ifies)); - - - if (_LOG2D_ENABLED ()) { - gs_free char *notification = g_variant_print (variant, TRUE); - - _LOG2D ("type %s, iface %s: %s", - G_OBJECT_TYPE_NAME (self), G_OBJECT_TYPE_NAME (ifdata->interface), - notification); - } - - g_signal_emit (ifdata->interface, ifdata->property_changed_signal_id, 0, variant); - - g_hash_table_remove_all (ifdata->pending_notifies); - } - - return G_SOURCE_REMOVE; -} - -static void -nm_exported_object_notify (GObject *object, GParamSpec *pspec) -{ - NMExportedObject *self = (NMExportedObject *) object; - NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self); - NMExportedObjectClassInfo *classinfo; - GType type; - const char *dbus_property_name = NULL; - GValue value = G_VALUE_INIT; - GVariant *value_variant; - InterfaceData *ifdata = NULL; - const GVariantType *vtype; - guint i, j; - - /* Hook to emit deprecated "PropertiesChanged" signal on NetworkManager interfaces. - * This is to preserve deprecated D-Bus API, nowadays we use instead - * the "PropertiesChanged" signal of "org.freedesktop.DBus.Properties". */ - - if (priv->num_interfaces == 0) - return; - - for (type = G_OBJECT_TYPE (self); type; type = g_type_parent (type)) { - classinfo = g_type_get_qdata (type, nm_exported_object_class_info_quark ()); - if (!classinfo) - continue; - - dbus_property_name = g_hash_table_lookup (classinfo->properties, pspec->name); - if (dbus_property_name) - break; - } - if (!dbus_property_name) { - _LOG2T ("ignoring notification for prop %s on type %s", - pspec->name, G_OBJECT_TYPE_NAME (self)); - return; - } - - for (i = 0; i < priv->num_interfaces; i++) { - GDBusInterfaceInfo *iinfo; - - ifdata = &priv->interfaces[i]; - iinfo = g_dbus_interface_skeleton_get_info (ifdata->interface); - for (j = 0; iinfo->properties[j]; j++) { - if (nm_streq (iinfo->properties[j]->name, dbus_property_name)) { - vtype = G_VARIANT_TYPE (iinfo->properties[j]->signature); - goto vtype_found; - } - } - } - g_return_if_reached (); - -vtype_found: - g_value_init (&value, pspec->value_type); - g_object_get_property ((GObject *) self, pspec->name, &value); - value_variant = g_dbus_gvalue_to_gvariant (&value, vtype); - g_value_unset (&value); - - if ( ( NM_IS_DEVICE (self) - && !NMDBUS_IS_DEVICE_STATISTICS_SKELETON (ifdata->interface)) - || NM_IS_ACTIVE_CONNECTION (self)) { - /* This PropertiesChanged signal is nodaways deprecated in favor - * of "org.freedesktop.DBus.Properties"'s PropertiesChanged signal. - * This function solely exists to raise the NM version of PropertiesChanged. - * - * With types exported on D-Bus that are implemented as derived - * types in glib (NMDevice and NMActiveConnection), multiple types - * in the inheritance tree define a "PropertiesChanged" signal. - * - * In 1.0.0 and earlier, the signal was emitted once for every interface - * that had a "PropertiesChanged" signal. For example: - * - NMDeviceEthernet.HwAddress was emitted on "fdo.NM.Device.Ethernet" - * and "fdo.NM.Device.Veth" (if the device was of type NMDeviceVeth). - * - NMVpnConnection.VpnState was emitted on "fdo.NM.Connecion.Active" - * and "fdo.NM.VPN.Connection". - * - * NMDevice is special in that it didn't have a "PropertiesChanged" signal. - * Thus, a change to "NMDevice.StateReason" would be emitted on "fdo.NM.Device.Ethernet" - * and also on "fdo.NM.Device.Veth" (in case of a device of type NMDeviceVeth). - * - * The releases of 1.2.0 and 1.4.0 failed to realize above and broke this behavior. - * This special handling here is to bring back the 1.0.0 behavior. - * - * The Device.Statistics signal is special, because it was only added with 1.4.0 - * and didn't have above behavior. So let's save the overhead of emitting multiple - * deprecated signals for wrong interfaces. */ - for (i = 0, j = 0; i < priv->num_interfaces; i++) { - ifdata = &priv->interfaces[i]; - if ( ifdata->property_changed_signal_id - && !NMDBUS_IS_DEVICE_STATISTICS_SKELETON (ifdata->interface)) { - j++; - g_hash_table_insert (ifdata->pending_notifies, - (gpointer) dbus_property_name, - g_variant_ref (value_variant)); - } - } - nm_assert (j > 0); - g_variant_unref (value_variant); - } else if (ifdata->property_changed_signal_id) { - /* @dbus_property_name is inside classinfo and never freed, thus we don't clone it. - * Also, we do a pointer, not string comparison. */ - g_hash_table_insert (ifdata->pending_notifies, - (gpointer) dbus_property_name, - value_variant); - } else - g_variant_unref (value_variant); - - if (!priv->notify_idle_id) - priv->notify_idle_id = g_idle_add (idle_emit_properties_changed, self); -} - -/*****************************************************************************/ - -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - NMExportedObject *self = NM_EXPORTED_OBJECT (object); - NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self); - - switch (prop_id) { - case PROP_PATH: - g_value_set_string (value, priv->path); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -nm_exported_object_init (NMExportedObject *self) -{ - NMExportedObjectPrivate *priv; - - priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_EXPORTED_OBJECT, NMExportedObjectPrivate); - self->_priv = priv; -} - -static void -constructed (GObject *object) -{ - NMExportedObjectClass *klass; - - G_OBJECT_CLASS (nm_exported_object_parent_class)->constructed (object); - -#ifdef _ASSERT_NO_EARLY_EXPORT - NM_EXPORTED_OBJECT_GET_PRIVATE (NM_EXPORTED_OBJECT (object))->_constructed = TRUE; -#endif - - klass = NM_EXPORTED_OBJECT_GET_CLASS (object); - - if (klass->export_on_construction) - nm_exported_object_export ((NMExportedObject *) object); -} - -static void -dispose (GObject *object) -{ - NMExportedObject *self = NM_EXPORTED_OBJECT (object); - NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self); - - /* Objects should have already been unexported by their owner, unless - * we are quitting, where many objects stick around until exit. - */ - if (!quitting) { - if (priv->path) { - g_warn_if_reached (); - nm_exported_object_unexport (self); - } - } else if (nm_clear_g_free (&priv->path)) - _notify (self, PROP_PATH); - - nm_clear_g_source (&priv->notify_idle_id); - - G_OBJECT_CLASS (nm_exported_object_parent_class)->dispose (object); -} - -static void -nm_exported_object_class_init (NMExportedObjectClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (object_class, sizeof (NMExportedObjectPrivate)); - - object_class->constructed = constructed; - object_class->notify = nm_exported_object_notify; - object_class->dispose = dispose; - object_class->get_property = get_property; - - obj_properties[PROP_PATH] = - g_param_spec_string (NM_EXPORTED_OBJECT_PATH, "", "", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); -} diff --git a/src/nm-exported-object.h b/src/nm-exported-object.h deleted file mode 100644 index 47559a20..00000000 --- a/src/nm-exported-object.h +++ /dev/null @@ -1,85 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2014 Red Hat, Inc. - */ - -#ifndef NM_EXPORTED_OBJECT_H -#define NM_EXPORTED_OBJECT_H - -/*****************************************************************************/ - -#define NM_EXPORT_PATH_NUMBERED(basepath) ""basepath"/%llu" - -char *nm_exported_object_skeletonify_method_name (const char *dbus_method_name); - -typedef struct { - GType dbus_skeleton_type; - char *method_name; - GCallback impl; -} NMExportedObjectDBusMethodImpl; - -GDBusInterfaceSkeleton *nm_exported_object_skeleton_create (GType dbus_skeleton_type, - GObjectClass *object_class, - const NMExportedObjectDBusMethodImpl *methods, - guint methods_len, - GObject *target); -void nm_exported_object_skeleton_release (GDBusInterfaceSkeleton *interface); - -/*****************************************************************************/ - -#define NM_TYPE_EXPORTED_OBJECT (nm_exported_object_get_type ()) -#define NM_EXPORTED_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_EXPORTED_OBJECT, NMExportedObject)) -#define NM_EXPORTED_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_EXPORTED_OBJECT, NMExportedObjectClass)) -#define NM_IS_EXPORTED_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_EXPORTED_OBJECT)) -#define NM_IS_EXPORTED_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_EXPORTED_OBJECT)) -#define NM_EXPORTED_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_EXPORTED_OBJECT, NMExportedObjectClass)) - -#define NM_EXPORTED_OBJECT_PATH "path" - -struct _NMExportedObjectPrivate; - -struct _NMExportedObject { - GDBusObjectSkeleton parent; - struct _NMExportedObjectPrivate *_priv; -}; - -typedef struct { - GDBusObjectSkeletonClass parent; - - const char *export_path; - char export_on_construction; -} NMExportedObjectClass; - -GType nm_exported_object_get_type (void); - -void nm_exported_object_class_set_quitting (void); - -void nm_exported_object_class_add_interface (NMExportedObjectClass *object_class, - GType dbus_skeleton_type, - ...) G_GNUC_NULL_TERMINATED; - -const char *nm_exported_object_export (NMExportedObject *self); -const char *nm_exported_object_get_path (NMExportedObject *self); -gboolean nm_exported_object_is_exported (NMExportedObject *self); -void nm_exported_object_unexport (NMExportedObject *self); -GDBusInterfaceSkeleton *nm_exported_object_get_interface_by_type (NMExportedObject *self, GType interface_type); - -void _nm_exported_object_clear_and_unexport (NMExportedObject **location); -#define nm_exported_object_clear_and_unexport(location) _nm_exported_object_clear_and_unexport ((NMExportedObject **) (location)) - -#endif /* NM_EXPORTED_OBJECT_H */ diff --git a/src/nm-firewall-manager.c b/src/nm-firewall-manager.c index 216d5fc8..5b5e7cfa 100644 --- a/src/nm-firewall-manager.c +++ b/src/nm-firewall-manager.c @@ -25,7 +25,7 @@ #include #include "NetworkManagerUtils.h" -#include "nm-utils/c-list.h" +#include "c-list/src/c-list.h" /*****************************************************************************/ @@ -174,7 +174,7 @@ _cb_info_create (NMFirewallManager *self, if (priv->running || priv->proxy_cancellable) { info->mode_mutable = CB_INFO_MODE_DBUS_WAITING; - info->dbus.arg = g_variant_new ("(ss)", zone ? zone : "", iface); + info->dbus.arg = g_variant_new ("(ss)", zone ?: "", iface); } else info->mode_mutable = CB_INFO_MODE_IDLE; diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index 1493ef19..58d766c1 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -33,6 +33,8 @@ #include #include +#include "nm-utils/nm-c-list.h" + #include "main-utils.h" #include "NetworkManagerUtils.h" #include "platform/nm-linux-platform.h" @@ -55,6 +57,9 @@ static struct { GMainLoop *main_loop; int ifindex; + + guint dad_failed_id; + CList dad_failed_lst_head; } gl/*obal*/ = { .ifindex = -1, }; @@ -120,7 +125,7 @@ dhcp4_state_changed (NMDhcpClient *client, 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, FALSE); + NM_PLATFORM_GET, gl.ifindex); if (last_config) nm_ip4_config_subtract (existing, last_config, 0); @@ -166,7 +171,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in NMIP6Config *existing; existing = nm_ip6_config_capture (nm_platform_get_multi_idx (NM_PLATFORM_GET), - NM_PLATFORM_GET, gl.ifindex, FALSE, global_opt.tempaddr); + NM_PLATFORM_GET, gl.ifindex, global_opt.tempaddr); if (ndisc_config) nm_ip6_config_subtract (existing, ndisc_config, 0); else { @@ -316,19 +321,58 @@ do_early_setup (int *argc, char **argv[]) 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); + } + } + + gl.dad_failed_id = 0; + return G_SOURCE_REMOVE; +} + static void ip6_address_changed (NMPlatform *platform, int obj_type_i, int iface, - NMPlatformIP6Address *addr, + const NMPlatformIP6Address *addr, int change_type_i, NMNDisc *ndisc) { const NMPlatformSignalChangeType change_type = change_type_i; - - if ( (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->n_ifa_flags & IFA_F_DADFAILED) - || (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->n_ifa_flags & IFA_F_TENTATIVE)) - nm_ndisc_dad_failed (ndisc, &addr->address); + 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 @@ -340,14 +384,13 @@ main (int argc, char *argv[]) gs_free char *pidfile = NULL; gs_unref_object NMDhcpClient *dhcp4_client = NULL; gs_unref_object NMNDisc *ndisc = NULL; - GByteArray *hwaddr = NULL; - size_t hwaddr_len = 0; - gconstpointer tmp; + gs_unref_bytes GBytes *hwaddr = NULL; + gs_unref_bytes GBytes *client_id = NULL; gs_free NMUtilsIPv6IfaceId *iid = NULL; guint sd_id; char sysctl_path_buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - nm_g_type_init (); + c_list_init (&gl.dad_failed_lst_head); setpgid (getpid (), getpid ()); @@ -431,11 +474,7 @@ main (int argc, char *argv[]) /* Set up platform interaction layer */ nm_linux_platform_setup (); - tmp = nm_platform_link_get_address (NM_PLATFORM_GET, gl.ifindex, &hwaddr_len); - if (tmp) { - hwaddr = g_byte_array_sized_new (hwaddr_len); - g_byte_array_append (hwaddr, tmp, hwaddr_len); - } + hwaddr = nm_platform_link_get_address_as_bytes (NM_PLATFORM_GET, gl.ifindex); if (global_opt.iid_str) { GBytes *bytes; @@ -449,6 +488,16 @@ main (int argc, char *argv[]) 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_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, sysctl_path_buf, global_opt.ifname, "promote_secondaries")), "1"); @@ -463,7 +512,7 @@ main (int argc, char *argv[]) !!global_opt.dhcp4_hostname, global_opt.dhcp4_hostname, global_opt.dhcp4_fqdn, - global_opt.dhcp4_clientid, + client_id, NM_DHCP_TIMEOUT_DEFAULT, NULL, global_opt.dhcp4_address); @@ -523,7 +572,8 @@ main (int argc, char *argv[]) g_main_loop_run (gl.main_loop); - g_clear_pointer (&hwaddr, g_byte_array_unref); + 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); @@ -547,7 +597,7 @@ const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4] = { #include "nm-config.h" #include "devices/nm-device.h" #include "nm-active-connection.h" -#include "nm-bus-manager.h" +#include "nm-dbus-manager.h" void nm_main_config_reload (int signal) @@ -579,21 +629,40 @@ nm_config_get_configure_and_quit (NMConfig *config) return TRUE; } -NMBusManager * -nm_bus_manager_get (void) +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) { - return GUINT_TO_POINTER (1); } void -nm_bus_manager_register_object (NMBusManager *bus_manager, - GDBusObjectSkeleton *object) +_nm_dbus_manager_obj_notify (NMDBusObject *obj, + guint n_pspecs, + const GParamSpec *const*pspecs) { } void -nm_bus_manager_unregister_object (NMBusManager *bus_manager, - GDBusObjectSkeleton *object) +_nm_dbus_manager_obj_emit_signal (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const GDBusSignalInfo *signal_info, + GVariant *args) { } diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 15157739..6ae81b57 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -36,8 +36,7 @@ #include "platform/nm-platform-utils.h" #include "NetworkManagerUtils.h" #include "nm-core-internal.h" - -#include "introspection/org.freedesktop.NetworkManager.IP4Config.h" +#include "nm-dbus-object.h" /*****************************************************************************/ @@ -293,6 +292,7 @@ typedef struct { int ifindex; NMIPConfigSource mtu_source; gint dns_priority; + NMSettingConnectionMdns mdns; GArray *nameservers; GPtrArray *domains; GPtrArray *searches; @@ -317,15 +317,15 @@ typedef struct { } NMIP4ConfigPrivate; struct _NMIP4Config { - NMExportedObject parent; + NMDBusObject parent; NMIP4ConfigPrivate _priv; }; struct _NMIP4ConfigClass { - NMExportedObjectClass parent; + NMDBusObjectClass parent; }; -G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_EXPORTED_OBJECT) +G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_DBUS_OBJECT) #define NM_IP4_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMIP4Config, NM_IS_IP4_CONFIG) @@ -582,14 +582,24 @@ sort_captured_addresses (const CList *lst_a, const CList *lst_b, gconstpointer u } NMIP4Config * -nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf) +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; - gboolean has_addresses = FALSE; nm_assert (ifindex > 0); @@ -621,7 +631,6 @@ nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i nm_dedup_multi_head_entry_sort (head_entry, sort_captured_addresses, NULL); - has_addresses = TRUE; _notify_addresses (self); } @@ -633,23 +642,6 @@ nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i nmp_cache_iter_for_each (&iter, head_entry, &plobj) _add_route (self, plobj, NULL, NULL); - /* If the interface has the default route, and has IPv4 addresses, capture - * nameservers from /etc/resolv.conf. - */ - if ( has_addresses - && priv->best_default_route - && capture_resolv_conf) { - gs_free char *rc_contents = NULL; - - if (g_file_get_contents (_PATH_RESCONF, &rc_contents, NULL, NULL)) { - if (nm_utils_resolve_conf_parse (AF_INET, - rc_contents, - priv->nameservers, - priv->dns_options)) - _notify (self, PROP_NAMESERVERS); - } - } - return self; } @@ -659,7 +651,6 @@ nm_ip4_config_add_dependent_routes (NMIP4Config *self, guint32 route_metric, GPtrArray **out_ip4_dev_route_blacklist) { - const NMIP4ConfigPrivate *priv; GPtrArray *ip4_dev_route_blacklist = NULL; const NMPlatformIP4Address *my_addr; const NMPlatformIP4Route *my_route; @@ -668,8 +659,6 @@ nm_ip4_config_add_dependent_routes (NMIP4Config *self, g_return_if_fail (NM_IS_IP4_CONFIG (self)); - priv = NM_IP4_CONFIG_GET_PRIVATE (self); - ifindex = nm_ip4_config_get_ifindex (self); g_return_if_fail (ifindex > 0); @@ -893,10 +882,10 @@ _nm_ip_config_merge_route_attributes (int addr_family, void nm_ip4_config_merge_setting (NMIP4Config *self, NMSettingIPConfig *setting, + NMSettingConnectionMdns mdns, guint32 route_table, guint32 route_metric) { - NMIP4ConfigPrivate *priv; guint naddresses, nroutes, nnameservers, nsearches; int i, priority; const char *gateway_str; @@ -907,8 +896,6 @@ nm_ip4_config_merge_setting (NMIP4Config *self, g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); - priv = NM_IP4_CONFIG_GET_PRIVATE (self); - g_object_freeze_notify (G_OBJECT (self)); naddresses = nm_setting_ip_config_get_num_addresses (setting); @@ -946,7 +933,7 @@ nm_ip4_config_merge_setting (NMIP4Config *self, address.preferred = NM_PLATFORM_LIFETIME_PERMANENT; address.addr_source = NM_IP_CONFIG_SOURCE_USER; - label = nm_ip_address_get_attribute (s_addr, "label"); + 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)); @@ -1012,6 +999,8 @@ nm_ip4_config_merge_setting (NMIP4Config *self, if (priority) nm_ip4_config_set_dns_priority (self, priority); + nm_ip4_config_mdns_set (self, mdns); + g_object_thaw_notify (G_OBJECT (self)); } @@ -1058,7 +1047,7 @@ nm_ip4_config_create_setting (const NMIP4Config *self) s_addr = nm_ip_address_new_binary (AF_INET, &address->address, address->plen, NULL); if (*address->label) - nm_ip_address_set_attribute (s_addr, "label", g_variant_new_string (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); @@ -1227,6 +1216,11 @@ nm_ip4_config_merge (NMIP4Config *dst, 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))); + g_object_thaw_notify (G_OBJECT (dst)); } @@ -1463,13 +1457,18 @@ nm_ip4_config_subtract (NMIP4Config *dst, 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); + g_object_thaw_notify (G_OBJECT (dst)); } -void -nm_ip4_config_intersect (NMIP4Config *dst, - const NMIP4Config *src, - guint32 default_route_metric_penalty) +static gboolean +_nm_ip4_config_intersect_helper (NMIP4Config *dst, + const NMIP4Config *src, + guint32 default_route_metric_penalty, + gboolean update_dst) { NMIP4ConfigPrivate *dst_priv; const NMIP4ConfigPrivate *src_priv; @@ -1477,15 +1476,16 @@ nm_ip4_config_intersect (NMIP4Config *dst, const NMPlatformIP4Address *a; const NMPlatformIP4Route *r; const NMPObject *new_best_default_route; - gboolean changed; + gboolean changed, result = FALSE; - g_return_if_fail (src); - g_return_if_fail (dst); + 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); - g_object_freeze_notify (G_OBJECT (dst)); + if (update_dst) + g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ changed = FALSE; @@ -1495,13 +1495,18 @@ nm_ip4_config_intersect (NMIP4Config *dst, 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) + if (changed) { _notify_addresses (dst); + result = TRUE; + } /* ignore nameservers */ @@ -1533,6 +1538,9 @@ nm_ip4_config_intersect (NMIP4Config *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 (); @@ -1542,18 +1550,71 @@ nm_ip4_config_intersect (NMIP4Config *dst, nm_assert (changed); _notify (dst, PROP_GATEWAY); } - if (changed) + + if (changed) { _notify_routes (dst); + result = TRUE; + } /* ignore domains */ /* ignore dns searches */ /* ignore dns options */ /* ignore NIS */ /* ignore WINS */ + /* ignore mdns */ - g_object_thaw_notify (G_OBJECT (dst)); + 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 + * @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, + guint32 default_route_metric_penalty) +{ + _nm_ip4_config_intersect_helper (dst, src, default_route_metric_penalty, TRUE); } +/** + * nm_ip4_config_intersect_alloc: + * @a: a configuration + * @b: another configuration + * @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, + guint32 default_route_metric_penalty) +{ + NMIP4Config *a_copy; + + if (_nm_ip4_config_intersect_helper ((NMIP4Config *) a, b, + default_route_metric_penalty, FALSE)) { + a_copy = nm_ip4_config_clone (a); + _nm_ip4_config_intersect_helper (a_copy, b, default_route_metric_penalty, TRUE); + return a_copy; + } else + return NULL; +} /** * nm_ip4_config_replace: @@ -1779,6 +1840,8 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev has_relevant_changes = TRUE; } + dst_priv->mdns = src_priv->mdns; + /* DNS priority */ if (src_priv->dns_priority != dst_priv->dns_priority) { nm_ip4_config_set_dns_priority (dst, src_priv->dns_priority); @@ -1871,7 +1934,7 @@ nm_ip4_config_dump (const NMIP4Config *self, const char *detail) return; } - str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self)); + str = nm_dbus_object_get_path (NM_DBUS_OBJECT (self)); if (str) g_message (" path: %s", str); @@ -2270,6 +2333,31 @@ _nm_ip4_config_get_nameserver (const NMIP4Config *self, guint 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_utils_strv_find_first ((char **) array->pdata, array->len, 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) { @@ -2285,17 +2373,9 @@ void nm_ip4_config_add_domain (NMIP4Config *self, const char *domain) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); - int i; - - g_return_if_fail (domain != NULL); - g_return_if_fail (domain[0] != '\0'); - - for (i = 0; i < priv->domains->len; i++) - if (!g_strcmp0 (g_ptr_array_index (priv->domains, i), domain)) - return; - g_ptr_array_add (priv->domains, g_strdup (domain)); - _notify (self, PROP_DOMAINS); + if (_nm_ip_config_check_and_add_domain (priv->domains, domain)) + _notify (self, PROP_DOMAINS); } void @@ -2339,35 +2419,12 @@ nm_ip4_config_reset_searches (NMIP4Config *self) } void -nm_ip4_config_add_search (NMIP4Config *self, const char *new) +nm_ip4_config_add_search (NMIP4Config *self, const char *search) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); - char *search; - size_t len; - - g_return_if_fail (new != NULL); - g_return_if_fail (new[0] != '\0'); - search = g_strdup (new); - - /* Remove trailing dot as it has no effect */ - len = strlen (search); - if (search[len - 1] == '.') - search[len - 1] = 0; - - if (!search[0]) { - g_free (search); - return; - } - - if (nm_utils_strv_find_first ((char **) priv->searches->pdata, - priv->searches->len, search) >= 0) { - g_free (search); - return; - } - - g_ptr_array_add (priv->searches, search); - _notify (self, PROP_SEARCHES); + if (_nm_ip_config_check_and_add_domain (priv->searches, search)) + _notify (self, PROP_SEARCHES); } void @@ -2456,6 +2513,21 @@ nm_ip4_config_get_dns_option (const NMIP4Config *self, guint 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; +} + +/*****************************************************************************/ + void nm_ip4_config_set_dns_priority (NMIP4Config *self, gint priority) { @@ -2806,9 +2878,9 @@ nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b) { GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1); GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1); - gsize a_len = g_checksum_type_get_length (G_CHECKSUM_SHA1); - gsize b_len = g_checksum_type_get_length (G_CHECKSUM_SHA1); - guchar a_data[a_len], b_data[b_len]; + guchar a_data[20], b_data[20]; + gsize a_len = sizeof (a_data); + gsize b_len = sizeof (b_data); gboolean equal; if (a) @@ -2819,7 +2891,8 @@ nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b) g_checksum_get_digest (a_checksum, a_data, &a_len); g_checksum_get_digest (b_checksum, b_data, &b_len); - g_assert (a_len == b_len); + nm_assert (a_len == sizeof (a_data)); + nm_assert (b_len == sizeof (b_data)); equal = !memcmp (a_data, b_data, a_len); g_checksum_free (a_checksum); @@ -2889,7 +2962,7 @@ get_property (GObject *object, guint prop_id, if (*address->label) { g_variant_builder_add (&addr_builder, "{sv}", - "label", + NM_IP_ADDRESS_ATTRIBUTE_LABEL, g_variant_new_string (address->label)); } @@ -3065,6 +3138,7 @@ nm_ip4_config_init (NMIP4Config *self) 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->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); @@ -3112,13 +3186,37 @@ finalize (GObject *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, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Addresses", "aau", NM_IP4_CONFIG_ADDRESSES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("AddressData", "aa{sv}", NM_IP4_CONFIG_ADDRESS_DATA), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Gateway", "s", NM_IP4_CONFIG_GATEWAY), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Routes", "aau", NM_IP4_CONFIG_ROUTES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("RouteData", "aa{sv}", NM_IP4_CONFIG_ROUTE_DATA), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Nameservers", "au", NM_IP4_CONFIG_NAMESERVERS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Domains", "as", NM_IP4_CONFIG_DOMAINS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Searches", "as", NM_IP4_CONFIG_SEARCHES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("DnsOptions", "as", NM_IP4_CONFIG_DNS_OPTIONS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("DnsPriority", "i", NM_IP4_CONFIG_DNS_PRIORITY), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("WinsServers", "au", NM_IP4_CONFIG_WINS_SERVERS), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_ip4_config_class_init (NMIP4ConfigClass *config_class) { GObjectClass *object_class = G_OBJECT_CLASS (config_class); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (config_class); - exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/IP4Config"); + 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; @@ -3198,8 +3296,4 @@ nm_ip4_config_class_init (NMIP4ConfigClass *config_class) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (config_class), - NMDBUS_TYPE_IP4_CONFIG_SKELETON, - NULL); } diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index 7c345d20..78aca14e 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -21,7 +21,8 @@ #ifndef __NETWORKMANAGER_IP4_CONFIG_H__ #define __NETWORKMANAGER_IP4_CONFIG_H__ -#include "nm-exported-object.h" +#include "nm-setting-connection.h" + #include "nm-setting-ip4-config.h" #include "nm-utils/nm-dedup-multi.h" @@ -150,15 +151,15 @@ typedef struct _NMIP4ConfigClass NMIP4ConfigClass; 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, gboolean capture_resolv_conf); +NMIP4Config *nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex); void nm_ip4_config_add_dependent_routes (NMIP4Config *self, guint32 route_table, @@ -171,11 +172,11 @@ gboolean nm_ip4_config_commit (const NMIP4Config *self, void nm_ip4_config_merge_setting (NMIP4Config *self, NMSettingIPConfig *setting, + NMSettingConnectionMdns mdns, 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, @@ -186,6 +187,9 @@ void nm_ip4_config_subtract (NMIP4Config *dst, void nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src, guint32 default_route_metric_penalty); +NMIP4Config *nm_ip4_config_intersect_alloc (const NMIP4Config *a, + const NMIP4Config *b, + guint32 default_route_metric_penalty); gboolean nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relevant_changes); void nm_ip4_config_dump (const NMIP4Config *self, const char *detail); @@ -194,6 +198,10 @@ 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); + 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); @@ -218,6 +226,13 @@ const NMPlatformIP4Route *nm_ip4_config_get_direct_route_for_host (const NMIP4Co 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); @@ -273,14 +288,22 @@ gboolean nm_ip4_config_nmpobj_remove (NMIP4Config *self, 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); + /*****************************************************************************/ #include "nm-ip6-config.h" static inline gboolean -NM_IS_IP_CONFIG (gconstpointer config) +NM_IS_IP_CONFIG (gconstpointer config, int addr_family) { - return NM_IS_IP4_CONFIG (config) || NM_IS_IP6_CONFIG (config); + 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 @@ -317,7 +340,7 @@ NM_IS_IP_CONFIG (gconstpointer config) NMIP6Config * : (NM_IS_IP6_CONFIG (_config))); \ }) #else -#define _NM_IS_IP_CONFIG(typeexpr, config) NM_IS_IP_CONFIG(config) +#define _NM_IS_IP_CONFIG(typeexpr, config) NM_IS_IP_CONFIG(config, AF_UNSPEC) #endif #define NM_IP_CONFIG_CAST(config) \ @@ -338,7 +361,7 @@ nm_ip_config_get_addr_family (const NMIPConfig *config) g_return_val_if_reached (AF_UNSPEC); } -#define _NM_IP_CONFIG_DISPATCH(config, v4_func, v6_func, dflt, ...) \ +#define _NM_IP_CONFIG_DISPATCH(config, v4_func, v6_func, ...) \ G_STMT_START { \ gconstpointer _config = (config); \ \ @@ -350,58 +373,223 @@ nm_ip_config_get_addr_family (const NMIPConfig *config) } \ } G_STMT_END +#define _NM_IP_CONFIG_DISPATCH_VOID(config, v4_func, v6_func, ...) \ + G_STMT_START { \ + gconstpointer _config = (config); \ + \ + if (NM_IS_IP4_CONFIG (_config)) { \ + v4_func ((NMIP4Config *) _config, ##__VA_ARGS__); \ + } else { \ + nm_assert (NM_IS_IP6_CONFIG (_config)); \ + 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 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, 0); + _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, gint 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, 0); + _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, 0, 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, 0); + _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, NULL, 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, 0); + _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, NULL, 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, 0); + _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, NULL, 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); +} + +#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_IS_IP4_CONFIG (_dst)) { \ + nm_assert (NM_IS_IP4_CONFIG (_src)); \ + _return v4_func ((NMIP4Config *) _dst, (const NMIP4Config *) _src, ##__VA_ARGS__); \ + } else { \ + nm_assert (NM_IS_IP6_CONFIG (_src)); \ + _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, + guint32 default_route_metric_penalty) +{ + _NM_IP_CONFIG_DISPATCH_SET_OP (, dst, src, + nm_ip4_config_intersect, + nm_ip6_config_intersect, + 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, + guint32 default_route_metric_penalty) +{ + if (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, + 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, + default_route_metric_penalty); + } } #endif /* __NETWORKMANAGER_IP4_CONFIG_H__ */ diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index ea3a2029..9807d388 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -38,8 +38,7 @@ #include "NetworkManagerUtils.h" #include "nm-ip4-config.h" #include "ndisc/nm-ndisc.h" - -#include "introspection/org.freedesktop.NetworkManager.IP6Config.h" +#include "nm-dbus-object.h" /*****************************************************************************/ @@ -82,15 +81,15 @@ typedef struct { } NMIP6ConfigPrivate; struct _NMIP6Config { - NMExportedObject parent; + NMDBusObject parent; NMIP6ConfigPrivate _priv; }; struct _NMIP6ConfigClass { - NMExportedObjectClass parent; + NMDBusObjectClass parent; }; -G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, NM_TYPE_EXPORTED_OBJECT) +G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, NM_TYPE_DBUS_OBJECT) #define NM_IP6_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMIP6Config, NM_IS_IP6_CONFIG) @@ -366,14 +365,24 @@ _nmtst_ip6_config_addresses_sort (NMIP6Config *self) } NMIP6Config * -nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary) +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; - gboolean has_addresses = FALSE; nm_assert (ifindex > 0); @@ -399,7 +408,6 @@ nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i NULL, NULL)) nm_assert_not_reached (); - has_addresses = TRUE; } head_entry = nm_ip6_config_lookup_addresses (self); nm_assert (head_entry); @@ -416,23 +424,6 @@ nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i nmp_cache_iter_for_each (&iter, head_entry, &plobj) _add_route (self, plobj, NULL, NULL); - /* If the interface has the default route, and has IPv6 addresses, capture - * nameservers from /etc/resolv.conf. - */ - if ( has_addresses - && priv->best_default_route - && capture_resolv_conf) { - gs_free char *rc_contents = NULL; - - if (g_file_get_contents (_PATH_RESCONF, &rc_contents, NULL, NULL)) { - if (nm_utils_resolve_conf_parse (AF_INET6, - rc_contents, - priv->nameservers, - priv->dns_options)) - _notify (self, PROP_NAMESERVERS); - } - } - return self; } @@ -441,7 +432,6 @@ nm_ip6_config_add_dependent_routes (NMIP6Config *self, guint32 route_table, guint32 route_metric) { - const NMIP6ConfigPrivate *priv; const NMPlatformIP6Address *my_addr; const NMPlatformIP6Route *my_route; int ifindex; @@ -449,8 +439,6 @@ nm_ip6_config_add_dependent_routes (NMIP6Config *self, g_return_if_fail (NM_IS_IP6_CONFIG (self)); - priv = NM_IP6_CONFIG_GET_PRIVATE (self); - ifindex = nm_ip6_config_get_ifindex (self); g_return_if_fail (ifindex > 0); @@ -558,7 +546,7 @@ nm_ip6_config_commit (const NMIP6Config *self, ifindex, route_table_sync); - nm_platform_ip6_address_sync (platform, ifindex, addresses, TRUE); + nm_platform_ip6_address_sync (platform, ifindex, addresses, FALSE); if (!nm_platform_ip_route_sync (platform, AF_INET6, @@ -577,7 +565,6 @@ nm_ip6_config_merge_setting (NMIP6Config *self, guint32 route_table, guint32 route_metric) { - NMIP6ConfigPrivate *priv; guint naddresses, nroutes, nnameservers, nsearches; const char *gateway_str; struct in6_addr gateway_bin; @@ -588,8 +575,6 @@ nm_ip6_config_merge_setting (NMIP6Config *self, g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting)); - priv = NM_IP6_CONFIG_GET_PRIVATE (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); @@ -817,8 +802,6 @@ nm_ip6_config_merge (NMIP6Config *dst, NMIPConfigMergeFlags merge_flags, guint32 default_route_metric_penalty) { - NMIP6ConfigPrivate *dst_priv; - const NMIP6ConfigPrivate *src_priv; guint32 i; NMDedupMultiIter ipconf_iter; const NMPlatformIP6Address *address = NULL; @@ -826,9 +809,6 @@ nm_ip6_config_merge (NMIP6Config *dst, g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); - dst_priv = NM_IP6_CONFIG_GET_PRIVATE (dst); - src_priv = NM_IP6_CONFIG_GET_PRIVATE (src); - g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ @@ -1070,26 +1050,28 @@ nm_ip6_config_subtract (NMIP6Config *dst, g_object_thaw_notify (G_OBJECT (dst)); } -void -nm_ip6_config_intersect (NMIP6Config *dst, - const NMIP6Config *src, - guint32 default_route_metric_penalty) +static gboolean +_nm_ip6_config_intersect_helper (NMIP6Config *dst, + const NMIP6Config *src, + 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; + gboolean changed, result = FALSE; const NMPObject *new_best_default_route; - g_return_if_fail (src); - g_return_if_fail (dst); + 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); - g_object_freeze_notify (G_OBJECT (dst)); + if (update_dst) + g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ changed = FALSE; @@ -1099,13 +1081,18 @@ nm_ip6_config_intersect (NMIP6Config *dst, 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) + if (changed) { _notify_addresses (dst); + result = TRUE; + } /* ignore nameservers */ @@ -1137,6 +1124,9 @@ nm_ip6_config_intersect (NMIP6Config *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 (); @@ -1146,14 +1136,67 @@ nm_ip6_config_intersect (NMIP6Config *dst, nm_assert (changed); _notify (dst, PROP_GATEWAY); } - if (changed) + if (changed) { _notify_routes (dst); + result = TRUE; + } /* ignore domains */ /* ignore dns searches */ /* ignore dns options */ - g_object_thaw_notify (G_OBJECT (dst)); + 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 + * @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, + guint32 default_route_metric_penalty) +{ + _nm_ip6_config_intersect_helper (dst, src, default_route_metric_penalty, TRUE); +} + +/** + * nm_ip6_config_intersect_alloc: + * @a: a configuration + * @b: another configuration + * @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, + guint32 default_route_metric_penalty) +{ + NMIP6Config *a_copy; + + if (_nm_ip6_config_intersect_helper ((NMIP6Config *) a, b, + default_route_metric_penalty, FALSE)) { + a_copy = nm_ip6_config_clone (a); + _nm_ip6_config_intersect_helper (a_copy, b, default_route_metric_penalty, TRUE); + return a_copy; + } else + return NULL; } /** @@ -1423,7 +1466,7 @@ nm_ip6_config_dump (const NMIP6Config *self, const char *detail) g_message ("--------- NMIP6Config %p (%s)", self, detail); - str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self)); + str = nm_dbus_object_get_path (NM_DBUS_OBJECT (self)); if (str) g_message (" path: %s", str); @@ -1629,22 +1672,43 @@ nm_ip6_config_lookup_address (const NMIP6Config *self, } const NMPlatformIP6Address * -nm_ip6_config_get_address_first_nontentative (const NMIP6Config *self, gboolean linklocal) +nm_ip6_config_find_first_address (const NMIP6Config *self, + NMPlatformMatchFlags match_flag) { - const NMIP6ConfigPrivate *priv; const NMPlatformIP6Address *addr; NMDedupMultiIter iter; g_return_val_if_fail (NM_IS_IP6_CONFIG (self), NULL); - priv = NM_IP6_CONFIG_GET_PRIVATE (self); + nm_assert (!NM_FLAGS_ANY (match_flag, ~( NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY + | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY))); - linklocal = !!linklocal; + nm_assert (NM_FLAGS_ANY (match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY)); + nm_assert (NM_FLAGS_ANY (match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)); nm_ip_config_iter_ip6_address_for_each (&iter, self, &addr) { - if ( ((!!IN6_IS_ADDR_LINKLOCAL (&addr->address)) == linklocal) - && !(addr->n_ifa_flags & IFA_F_TENTATIVE)) - return addr; + + if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) { + if (!NM_FLAGS_HAS (match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL)) + continue; + } else { + if (!NM_FLAGS_HAS (match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL)) + continue; + } + + if (NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_DADFAILED)) { + if (!NM_FLAGS_HAS (match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED)) + continue; + } else if ( NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_TENTATIVE) + && !NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_OPTIMISTIC)) { + if (!NM_FLAGS_HAS (match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE)) + continue; + } else { + if (!NM_FLAGS_HAS (match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) + continue; + } + + return addr; } return NULL; @@ -1980,7 +2044,6 @@ nm_ip6_config_get_subnet_for_host (const NMIP6Config *self, const struct in6_add return subnet; } - /*****************************************************************************/ void @@ -2054,17 +2117,9 @@ void nm_ip6_config_add_domain (NMIP6Config *self, const char *domain) { NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); - int i; - - g_return_if_fail (domain != NULL); - g_return_if_fail (domain[0] != '\0'); - - for (i = 0; i < priv->domains->len; i++) - if (!g_strcmp0 (g_ptr_array_index (priv->domains, i), domain)) - return; - g_ptr_array_add (priv->domains, g_strdup (domain)); - _notify (self, PROP_DOMAINS); + if (_nm_ip_config_check_and_add_domain (priv->domains, domain)) + _notify (self, PROP_DOMAINS); } void @@ -2108,35 +2163,12 @@ nm_ip6_config_reset_searches (NMIP6Config *self) } void -nm_ip6_config_add_search (NMIP6Config *self, const char *new) +nm_ip6_config_add_search (NMIP6Config *self, const char *search) { NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); - char *search; - size_t len; - - g_return_if_fail (new != NULL); - g_return_if_fail (new[0] != '\0'); - - search = g_strdup (new); - - /* Remove trailing dot as it has no effect */ - len = strlen (search); - if (search[len - 1] == '.') - search[len - 1] = 0; - if (!search[0]) { - g_free (search); - return; - } - - if (nm_utils_strv_find_first ((char **) priv->searches->pdata, - priv->searches->len, search) >= 0) { - g_free (search); - return; - } - - g_ptr_array_add (priv->searches, search); - _notify (self, PROP_SEARCHES); + if (_nm_ip_config_check_and_add_domain (priv->searches, search)) + _notify (self, PROP_SEARCHES); } void @@ -2403,9 +2435,9 @@ nm_ip6_config_equal (const NMIP6Config *a, const NMIP6Config *b) { GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1); GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1); - gsize a_len = g_checksum_type_get_length (G_CHECKSUM_SHA1); - gsize b_len = g_checksum_type_get_length (G_CHECKSUM_SHA1); - guchar a_data[a_len], b_data[b_len]; + guchar a_data[20], b_data[20]; + gsize a_len = sizeof (a_data); + gsize b_len = sizeof (b_data); gboolean equal; if (a) @@ -2416,7 +2448,8 @@ nm_ip6_config_equal (const NMIP6Config *a, const NMIP6Config *b) g_checksum_get_digest (a_checksum, a_data, &a_len); g_checksum_get_digest (b_checksum, b_data, &b_len); - g_assert (a_len == b_len); + nm_assert (a_len == sizeof (a_data)); + nm_assert (b_len == sizeof (b_data)); equal = !memcmp (a_data, b_data, a_len); g_checksum_free (a_checksum); @@ -2712,13 +2745,36 @@ finalize (GObject *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, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Addresses", "a(ayuay)", NM_IP6_CONFIG_ADDRESSES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("AddressData", "aa{sv}", NM_IP6_CONFIG_ADDRESS_DATA), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Gateway", "s", NM_IP6_CONFIG_GATEWAY), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Routes", "a(ayuayu)", NM_IP6_CONFIG_ROUTES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("RouteData", "aa{sv}", NM_IP6_CONFIG_ROUTE_DATA), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Nameservers", "aay", NM_IP6_CONFIG_NAMESERVERS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Domains", "as", NM_IP6_CONFIG_DOMAINS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Searches", "as", NM_IP6_CONFIG_SEARCHES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("DnsOptions", "as", NM_IP6_CONFIG_DNS_OPTIONS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("DnsPriority", "i", NM_IP6_CONFIG_DNS_PRIORITY), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_ip6_config_class_init (NMIP6ConfigClass *config_class) { GObjectClass *object_class = G_OBJECT_CLASS (config_class); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (config_class); - exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/IP6Config"); + 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; @@ -2792,8 +2848,4 @@ nm_ip6_config_class_init (NMIP6ConfigClass *config_class) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (config_class), - NMDBUS_TYPE_IP6_CONFIG_SKELETON, - NULL); } diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index 2fb8b8a4..9762ef48 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -23,7 +23,6 @@ #include -#include "nm-exported-object.h" #include "nm-setting-ip6-config.h" #include "nm-utils/nm-dedup-multi.h" @@ -97,16 +96,16 @@ typedef struct _NMIP6ConfigClass NMIP6ConfigClass; 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, - gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary); + NMSettingIP6ConfigPrivacy use_temporary); void nm_ip6_config_add_dependent_routes (NMIP6Config *self, guint32 route_table, @@ -122,7 +121,6 @@ void nm_ip6_config_merge_setting (NMIP6Config *self, guint32 route_metric); NMSetting *nm_ip6_config_create_setting (const NMIP6Config *self); - void nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFlags merge_flags, @@ -133,6 +131,9 @@ void nm_ip6_config_subtract (NMIP6Config *dst, void nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src, guint32 default_route_metric_penalty); +NMIP6Config *nm_ip6_config_intersect_alloc (const NMIP6Config *a, + const NMIP6Config *b, + guint32 default_route_metric_penalty); gboolean nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relevant_changes); void nm_ip6_config_dump (const NMIP6Config *self, const char *detail); @@ -146,7 +147,8 @@ 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); -const NMPlatformIP6Address *nm_ip6_config_get_address_first_nontentative (const NMIP6Config *self, gboolean linklocal); +const NMPlatformIP6Address *nm_ip6_config_find_first_address (const NMIP6Config *self, + NMPlatformMatchFlags match_flag); 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); diff --git a/src/nm-logging.c b/src/nm-logging.c index f19f0de1..11e05c31 100644 --- a/src/nm-logging.c +++ b/src/nm-logging.c @@ -663,7 +663,7 @@ _nm_log_impl (const char *file, NMLogDomain dom = dom_all & _nm_logging_enabled_state[level]; for (diter = &global.domain_desc[0]; diter->name; diter++) { - if (!NM_FLAGS_HAS (dom_all, diter->num)) + if (!NM_FLAGS_ANY (dom_all, diter->num)) continue; /* construct a list of all domains (not only the enabled ones). @@ -681,7 +681,7 @@ _nm_log_impl (const char *file, g_string_append (s_domain_all, diter->name); } - if (NM_FLAGS_HAS (dom, diter->num)) { + if (NM_FLAGS_ANY (dom, diter->num)) { if (i_domain > 0) { /* SYSLOG_FACILITY is specified multiple times for each domain that is actually enabled. */ _iovec_set_format_a (iov++, _MAX_LEN (30, diter->name), "SYSLOG_FACILITY=%s", diter->name); @@ -768,6 +768,9 @@ nm_log_handler (const gchar *log_domain, break; } + if (global.debug_stderr) + g_printerr ("%s%s\n", global.prefix, message ?: ""); + switch (global.log_backend) { #if SYSTEMD_JOURNAL case LOG_BACKEND_JOURNAL: diff --git a/src/nm-logging.h b/src/nm-logging.h index 8fcbc8cc..069d1d0d 100644 --- a/src/nm-logging.h +++ b/src/nm-logging.h @@ -83,6 +83,16 @@ typedef enum { /*< skip >*/ LOGD_IP = LOGD_IP4 | LOGD_IP6, } NMLogDomain; +static inline NMLogDomain +LOGD_IP_from_af (int addr_family) +{ + switch (addr_family) { + case AF_INET: return LOGD_IP4; + case AF_INET6: return LOGD_IP6; + } + g_return_val_if_reached (LOGD_NONE); +} + /* Log levels */ typedef enum { /*< skip >*/ LOGL_TRACE, @@ -123,7 +133,7 @@ typedef enum { /*< skip >*/ ""__VA_ARGS__); \ } G_STMT_END -/* nm_log() only evaluates it's argument list after checking +/* nm_log() only evaluates its argument list after checking * whether logging for the given level/domain is enabled. */ #define nm_log(level, domain, ifname, con_uuid, ...) \ G_STMT_START { \ @@ -132,7 +142,6 @@ typedef enum { /*< skip >*/ } \ } G_STMT_END - #define _nm_log_ptr(level, domain, ifname, con_uuid, self, prefix, ...) \ nm_log ((level), \ (domain), \ @@ -172,7 +181,6 @@ _nm_log_ptr_is_debug (NMLogLevel level) } \ } G_STMT_END - #define _nm_log_obj(level, domain, ifname, con_uuid, self, prefix, ...) \ _nm_log_ptr ((level), \ (domain), \ diff --git a/src/nm-manager.c b/src/nm-manager.c index f3bbebd0..9f0891a7 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -29,8 +29,10 @@ #include #include +#include "nm-utils/nm-c-list.h" + #include "nm-common-macros.h" -#include "nm-bus-manager.h" +#include "nm-dbus-manager.h" #include "vpn/nm-vpn-manager.h" #include "devices/nm-device.h" #include "devices/nm-device-generic.h" @@ -56,12 +58,10 @@ #include "nm-dbus-compat.h" #include "nm-checkpoint.h" #include "nm-checkpoint-manager.h" +#include "nm-dbus-object.h" #include "nm-dispatcher.h" #include "NetworkManagerUtils.h" -#include "introspection/org.freedesktop.NetworkManager.h" -#include "introspection/org.freedesktop.NetworkManager.Device.h" - /*****************************************************************************/ typedef struct { @@ -75,13 +75,37 @@ typedef struct { const char *hw_prop; } RadioState; +typedef enum { + ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_INTERNAL, + ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_USER, + ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE, +} AsyncOpType; + +typedef struct { + CList async_op_lst; + NMManager *self; + AsyncOpType async_op_type; + union { + struct { + NMActiveConnection *active; + union { + struct { + GDBusMethodInvocation *invocation; + } activate_user; + struct { + GDBusMethodInvocation *invocation; + NMConnection *connection; + } add_and_activate; + }; + } ac_auth; + }; +} AsyncOpData; + enum { DEVICE_ADDED, INTERNAL_DEVICE_ADDED, DEVICE_REMOVED, INTERNAL_DEVICE_REMOVED, - STATE_CHANGED, - CHECK_PERMISSIONS, ACTIVE_CONNECTION_ADDED, ACTIVE_CONNECTION_REMOVED, CONFIGURE_QUIT, @@ -114,6 +138,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMManager, PROP_METERED, PROP_GLOBAL_DNS_CONFIGURATION, PROP_ALL_DEVICES, + PROP_CHECKPOINTS, /* Not exported */ PROP_SLEEPING, @@ -125,22 +150,20 @@ typedef struct { GArray *capabilities; CList active_connections_lst_head; - GSList *authorizing_connections; + CList async_op_lst_head; guint ac_cleanup_id; NMActiveConnection *primary_connection; NMActiveConnection *activating_connection; NMMetered metered; - GSList *devices; + CList devices_lst_head; + NMState state; NMConfig *config; - NMConnectivityState connectivity_state; - + NMConnectivity *concheck_mgr; NMPolicy *policy; - NMHostnameManager *hostname_manager; - NMBusManager *dbus_mgr; struct { GDBusConnection *connection; guint id; @@ -173,26 +196,30 @@ typedef struct { guint devices_inited_id; + NMConnectivityState connectivity_state; + bool startup:1; bool devices_inited:1; bool sleeping:1; bool net_enabled:1; + unsigned connectivity_check_enabled_last:2; + guint delete_volatile_connection_idle_id; CList delete_volatile_connection_lst_head; } NMManagerPrivate; struct _NMManager { - NMExportedObject parent; + NMDBusObject parent; NMManagerPrivate _priv; }; typedef struct { - NMExportedObjectClass parent; + NMDBusObjectClass parent; } NMManagerClass; -G_DEFINE_TYPE (NMManager, nm_manager, NM_TYPE_EXPORTED_OBJECT) +G_DEFINE_TYPE (NMManager, nm_manager, NM_TYPE_DBUS_OBJECT) #define NM_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMManager, NM_IS_MANAGER) @@ -269,9 +296,20 @@ NM_DEFINE_SINGLETON_INSTANCE (NMManager); /*****************************************************************************/ +static const NMDBusInterfaceInfoExtended interface_info_manager; +static const GDBusSignalInfo signal_info_check_permissions; +static const GDBusSignalInfo signal_info_state_changed; +static const GDBusSignalInfo signal_info_device_added; +static const GDBusSignalInfo signal_info_device_removed; + static gboolean add_device (NMManager *self, NMDevice *device, GError **error); +static void _emit_device_added_removed (NMManager *self, + NMDevice *device, + gboolean is_added); + static NMActiveConnection *_new_active_connection (NMManager *self, + gboolean is_vpn, NMConnection *connection, NMConnection *applied, const char *specific_object, @@ -314,19 +352,201 @@ static void active_connection_parent_active (NMActiveConnection *active, NMActiveConnection *parent_ac, NMManager *self); -static NMActiveConnection *active_connection_find_first (NMManager *self, - NMSettingsConnection *settings_connection, - const char *uuid, - NMActiveConnectionState max_state); +static NMActiveConnection *active_connection_find (NMManager *self, + NMSettingsConnection *settings_connection, + const char *uuid, + NMActiveConnectionState max_state, + GPtrArray **out_all_matching); + +static NMConnectivity *concheck_get_mgr (NMManager *self); + +static void _internal_activation_auth_done (NMManager *self, + NMActiveConnection *active, + gboolean success, + const char *error_desc); +static void _add_and_activate_auth_done (NMManager *self, + NMActiveConnection *active, + NMConnection *connection, + GDBusMethodInvocation *invocation, + gboolean success, + const char *error_desc); +static void _activation_auth_done (NMManager *self, + NMActiveConnection *active, + GDBusMethodInvocation *invocation, + gboolean success, + const char *error_desc); /*****************************************************************************/ -static NM_CACHED_QUARK_FCN ("active-connection-add-and-activate", active_connection_add_and_activate_quark) - static NM_CACHED_QUARK_FCN ("autoconnect-root", autoconnect_root_quark) /*****************************************************************************/ +static gboolean +_connection_is_vpn (NMConnection *connection) +{ + const char *type; + + type = nm_connection_get_connection_type (connection); + if (type) + return nm_streq (type, NM_SETTING_VPN_SETTING_NAME); + + /* we have an incomplete (invalid) connection at hand. That can only + * happen during AddAndActivate. Determine whether it's VPN type based + * on the existance of a [vpn] section. */ + return !!nm_connection_get_setting_vpn (connection); +} + +/*****************************************************************************/ + +static gboolean +concheck_enabled (NMManager *self, gboolean *out_changed) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + guint check_enabled; + + check_enabled = nm_connectivity_check_enabled (concheck_get_mgr (self)) + ? 1 : 2; + if (priv->connectivity_check_enabled_last == check_enabled) + NM_SET_OUT (out_changed, FALSE); + else { + NM_SET_OUT (out_changed, TRUE); + priv->connectivity_check_enabled_last = check_enabled; + } + return check_enabled == 1; +} + +static void +concheck_config_changed_cb (NMConnectivity *connectivity, + NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *device; + gboolean changed; + + concheck_enabled (self, &changed); + if (changed) + _notify (self, PROP_CONNECTIVITY_CHECK_ENABLED); + + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) + nm_device_check_connectivity_update_interval (device); +} + +static NMConnectivity * +concheck_get_mgr (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + + if (G_UNLIKELY (!priv->concheck_mgr)) { + priv->concheck_mgr = g_object_ref (nm_connectivity_get ()); + g_signal_connect (priv->concheck_mgr, + NM_CONNECTIVITY_CONFIG_CHANGED, + G_CALLBACK (concheck_config_changed_cb), + self); + } + return priv->concheck_mgr; +} + +/*****************************************************************************/ + +static AsyncOpData * +_async_op_data_new_authorize_activate_internal (NMManager *self, NMActiveConnection *active_take) +{ + AsyncOpData *async_op_data; + + async_op_data = g_slice_new0 (AsyncOpData); + async_op_data->async_op_type = ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_INTERNAL; + async_op_data->self = g_object_ref (self); + async_op_data->ac_auth.active = active_take; + c_list_link_tail (&NM_MANAGER_GET_PRIVATE (self)->async_op_lst_head, &async_op_data->async_op_lst); + return async_op_data; +} + +static AsyncOpData * +_async_op_data_new_ac_auth_activate_user (NMManager *self, + NMActiveConnection *active_take, + GDBusMethodInvocation *invocation_take) +{ + AsyncOpData *async_op_data; + + async_op_data = g_slice_new0 (AsyncOpData); + async_op_data->async_op_type = ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_USER; + async_op_data->self = g_object_ref (self); + async_op_data->ac_auth.active = active_take; + async_op_data->ac_auth.activate_user.invocation = invocation_take; + c_list_link_tail (&NM_MANAGER_GET_PRIVATE (self)->async_op_lst_head, &async_op_data->async_op_lst); + return async_op_data; +} + +static AsyncOpData * +_async_op_data_new_ac_auth_add_and_activate (NMManager *self, + NMActiveConnection *active_take, + GDBusMethodInvocation *invocation_take, + NMConnection *connection_take) +{ + AsyncOpData *async_op_data; + + async_op_data = g_slice_new0 (AsyncOpData); + async_op_data->async_op_type = ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE; + async_op_data->self = g_object_ref (self); + async_op_data->ac_auth.active = active_take; + async_op_data->ac_auth.add_and_activate.invocation = invocation_take; + async_op_data->ac_auth.add_and_activate.connection = connection_take; + c_list_link_tail (&NM_MANAGER_GET_PRIVATE (self)->async_op_lst_head, &async_op_data->async_op_lst); + return async_op_data; +} + +static void +_async_op_complete_ac_auth_cb (NMActiveConnection *active, + gboolean success, + const char *error_desc, + gpointer user_data) +{ + AsyncOpData *async_op_data = user_data; + + nm_assert (async_op_data); + nm_assert (NM_IS_MANAGER (async_op_data->self)); + nm_assert (nm_c_list_contains_entry (&NM_MANAGER_GET_PRIVATE (async_op_data->self)->async_op_lst_head, async_op_data, async_op_lst)); + nm_assert (NM_IS_ACTIVE_CONNECTION (active)); + nm_assert (active == async_op_data->ac_auth.active); + + c_list_unlink (&async_op_data->async_op_lst); + + switch (async_op_data->async_op_type) { + case ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_INTERNAL: + _internal_activation_auth_done (async_op_data->self, + async_op_data->ac_auth.active, + success, + error_desc); + break; + case ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_USER: + _activation_auth_done (async_op_data->self, + async_op_data->ac_auth.active, + async_op_data->ac_auth.activate_user.invocation, + success, + error_desc); + break; + case ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE: + _add_and_activate_auth_done (async_op_data->self, + async_op_data->ac_auth.active, + async_op_data->ac_auth.add_and_activate.connection, + async_op_data->ac_auth.add_and_activate.invocation, + success, + error_desc); + g_object_unref (async_op_data->ac_auth.add_and_activate.connection); + break; + default: + nm_assert_not_reached (); + break; + } + + g_object_unref (async_op_data->ac_auth.active); + g_object_unref (async_op_data->self); + g_slice_free (AsyncOpData, async_op_data); +} + +/*****************************************************************************/ + typedef struct { int ifindex; guint32 aspired_metric; @@ -435,10 +655,10 @@ _device_route_metric_get (NMManager *self, * hence we skip it. */ continue; } - if (!nm_g_hash_table_add (priv->device_route_metrics, - _device_route_metric_data_new (device_state->ifindex, - device_state->route_metric_default_aspired, - device_state->route_metric_default_effective))) + if (!g_hash_table_add (priv->device_route_metrics, + _device_route_metric_data_new (device_state->ifindex, + device_state->route_metric_default_aspired, + device_state->route_metric_default_effective))) nm_assert_not_reached (); } } @@ -531,7 +751,7 @@ again: _LOGT (LOGD_DEVICE, "default-route-metric: ifindex %d reserves metric %u (aspired %u)", data->ifindex, data->effective_metric, data->aspired_metric); - if (!nm_g_hash_table_add (priv->device_route_metrics, data)) + if (!g_hash_table_add (priv->device_route_metrics, data)) nm_assert_not_reached (); out: @@ -578,12 +798,13 @@ _delete_volatile_connection_do (NMManager *self, NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (connection), - NM_SETTINGS_CONNECTION_FLAGS_VOLATILE)) + NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)) return; - if (active_connection_find_first (self, - connection, - NULL, - NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)) + if (active_connection_find (self, + connection, + NULL, + NM_ACTIVE_CONNECTION_STATE_DEACTIVATED, + NULL)) return; if (!nm_settings_has_connection (priv->settings, connection)) return; @@ -604,7 +825,7 @@ active_connection_remove (NMManager *self, NMActiveConnection *active) nm_assert (NM_IS_ACTIVE_CONNECTION (active)); nm_assert (c_list_contains (&priv->active_connections_lst_head, &active->active_connections_lst)); - notify = nm_exported_object_is_exported (NM_EXPORTED_OBJECT (active)); + notify = nm_dbus_object_is_exported (NM_DBUS_OBJECT (active)); c_list_unlink (&active->active_connections_lst); g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active); @@ -614,7 +835,7 @@ active_connection_remove (NMManager *self, NMActiveConnection *active) connection = nm_g_object_ref (nm_active_connection_get_settings_connection (active)); - nm_exported_object_clear_and_unexport (&active); + nm_dbus_object_clear_and_unexport (&active); if (connection) _delete_volatile_connection_do (self, connection); @@ -710,8 +931,8 @@ active_connection_add (NMManager *self, G_CALLBACK (active_connection_default_changed), self); - if (!nm_exported_object_is_exported (NM_EXPORTED_OBJECT (active))) - nm_exported_object_export (NM_EXPORTED_OBJECT (active)); + if (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (active))) + nm_dbus_object_export (NM_DBUS_OBJECT (active)); g_signal_emit (self, signals[ACTIVE_CONNECTION_ADDED], 0, active); @@ -725,15 +946,19 @@ nm_manager_get_active_connections (NMManager *manager) } static NMActiveConnection * -active_connection_find_first (NMManager *self, - NMSettingsConnection *settings_connection, - const char *uuid, - NMActiveConnectionState max_state) +active_connection_find (NMManager *self, + NMSettingsConnection *settings_connection, + const char *uuid, + NMActiveConnectionState max_state /* candidates in state @max_state will be found */, + GPtrArray **out_all_matching) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMActiveConnection *ac; + NMActiveConnection *best_ac = NULL; + GPtrArray *all = NULL; nm_assert (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection)); + nm_assert (!out_all_matching || !*out_all_matching); c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) { NMSettingsConnection *con; @@ -745,15 +970,44 @@ active_connection_find_first (NMManager *self, continue; if (nm_active_connection_get_state (ac) > max_state) continue; - return ac; + + if (!out_all_matching) + return ac; + + if (!best_ac) { + best_ac = ac; + continue; + } + + if (!all) { + all = g_ptr_array_new_with_free_func (g_object_unref); + g_ptr_array_add (all, g_object_ref (best_ac)); + } + g_ptr_array_add (all, g_object_ref (ac)); } - return NULL; + if (!best_ac) + return NULL; + + /* as an optimization, we only allocate out_all_matching, if there are more + * than one result. If there is only one result, we only return the single + * element and don't bother allocating an array. That's the common case. + * + * Also, in case we have multiple results, we return the *first* one + * as @best_ac. */ + nm_assert ( !all + || ( all->len >= 2 + && all->pdata[0] == best_ac)); + + *out_all_matching = all; + return best_ac; } static NMActiveConnection * -active_connection_find_first_by_connection (NMManager *self, - NMConnection *connection) +active_connection_find_by_connection (NMManager *self, + NMConnection *connection, + NMActiveConnectionState max_state, + GPtrArray **out_all_matching) { gboolean is_settings_connection; @@ -763,10 +1017,11 @@ active_connection_find_first_by_connection (NMManager *self, is_settings_connection = NM_IS_SETTINGS_CONNECTION (connection); /* Depending on whether connection is a settings connection, * either lookup by object-identity of @connection, or compare the UUID */ - return active_connection_find_first (self, - is_settings_connection ? NM_SETTINGS_CONNECTION (connection) : NULL, - is_settings_connection ? NULL : nm_connection_get_uuid (connection), - NM_ACTIVE_CONNECTION_STATE_DEACTIVATING); + return active_connection_find (self, + is_settings_connection ? NM_SETTINGS_CONNECTION (connection) : NULL, + is_settings_connection ? NULL : nm_connection_get_uuid (connection), + max_state, + out_all_matching); } static gboolean @@ -775,9 +1030,12 @@ _get_activatable_connections_filter (NMSettings *settings, gpointer user_data) { if (NM_FLAGS_HAS (nm_settings_connection_get_flags (connection), - NM_SETTINGS_CONNECTION_FLAGS_VOLATILE)) + NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)) return FALSE; - return !active_connection_find_first (user_data, connection, NULL, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING); + + /* the connection is activatable, if it has no active-connections that are in state + * activated, activating, or waiting to be activated. */ + return !active_connection_find (user_data, connection, NULL, NM_ACTIVE_CONNECTION_STATE_ACTIVATED, NULL); } NMSettingsConnection ** @@ -793,18 +1051,20 @@ nm_manager_get_activatable_connections (NMManager *manager, guint *out_len, gboo } static NMActiveConnection * -active_connection_get_by_path (NMManager *manager, const char *path) +active_connection_get_by_path (NMManager *self, const char *path) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMActiveConnection *ac; - nm_assert (path); + ac = (NMActiveConnection *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)), + path); + if ( !ac + || !NM_IS_ACTIVE_CONNECTION (ac) + || c_list_is_empty (&ac->active_connections_lst)) + return NULL; - c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) { - if (nm_streq0 (path, nm_exported_object_get_path (NM_EXPORTED_OBJECT (ac)))) - return ac; - } - return NULL; + nm_assert (c_list_contains (&priv->active_connections_lst_head, &ac->active_connections_lst)); + return ac; } /*****************************************************************************/ @@ -812,8 +1072,14 @@ active_connection_get_by_path (NMManager *manager, const char *path) static void _config_changed_cb (NMConfig *config, NMConfigData *config_data, NMConfigChangeFlags changes, NMConfigData *old_data, NMManager *self) { + g_object_freeze_notify (G_OBJECT (self)); + if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG)) _notify (self, PROP_GLOBAL_DNS_CONFIGURATION); + if ((!nm_config_data_get_connectivity_uri (config_data)) != (!nm_config_data_get_connectivity_uri (old_data))) + _notify (self, PROP_CONNECTIVITY_CHECK_AVAILABLE); + + g_object_thaw_notify (G_OBJECT (self)); } static void @@ -884,28 +1150,31 @@ _reload_auth_cb (NMAuthChain *chain, g_dbus_method_invocation_return_value (context, NULL); out: - nm_auth_chain_unref (chain); + nm_auth_chain_destroy (chain); } static void -impl_manager_reload (NMManager *self, - GDBusMethodInvocation *context, - guint32 flags) -{ - NMManagerPrivate *priv; +impl_manager_reload (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMAuthChain *chain; - GError *error = NULL; - - g_return_if_fail (NM_IS_MANAGER (self)); + guint32 flags; - priv = NM_MANAGER_GET_PRIVATE (self); + g_variant_get (parameters, "(u)", &flags); - chain = nm_auth_chain_new_context (context, _reload_auth_cb, self); + chain = nm_auth_chain_new_context (invocation, _reload_auth_cb, self); if (!chain) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Unable to authenticate request"); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_return_error_literal (invocation, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Unable to authenticate request"); return; } @@ -917,27 +1186,31 @@ impl_manager_reload (NMManager *self, /*****************************************************************************/ NMDevice * -nm_manager_get_device_by_path (NMManager *manager, const char *path) +nm_manager_get_device_by_path (NMManager *self, const char *path) { - GSList *iter; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *device; - g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (path, NULL); - for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { - if (!strcmp (nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data)), path)) - return NM_DEVICE (iter->data); - } - return NULL; + device = (NMDevice *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)), + path); + if ( !device + || !NM_IS_DEVICE (device) + || c_list_is_empty (&device->devices_lst)) + return NULL; + + nm_assert (c_list_contains (&priv->devices_lst_head, &device->devices_lst)); + return device; } NMDevice * -nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex) +nm_manager_get_device_by_ifindex (NMManager *self, int ifindex) { - GSList *iter; - - for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { - NMDevice *device = NM_DEVICE (iter->data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *device; + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { if (nm_device_get_ifindex (device) == ifindex) return device; } @@ -946,19 +1219,24 @@ nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex) } static NMDevice * -find_device_by_permanent_hw_addr (NMManager *manager, const char *hwaddr) +find_device_by_permanent_hw_addr (NMManager *self, const char *hwaddr) { - GSList *iter; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *device; const char *device_addr; + guint8 hwaddr_bin[NM_UTILS_HWADDR_LEN_MAX]; + gsize hwaddr_len; g_return_val_if_fail (hwaddr != NULL, NULL); - if (nm_utils_hwaddr_valid (hwaddr, -1)) { - for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { - device_addr = nm_device_get_permanent_hw_address (NM_DEVICE (iter->data)); - if (device_addr && nm_utils_hwaddr_matches (hwaddr, -1, device_addr, -1)) - return NM_DEVICE (iter->data); - } + if (!_nm_utils_hwaddr_aton (hwaddr, hwaddr_bin, sizeof (hwaddr_bin), &hwaddr_len)) + return NULL; + + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { + device_addr = nm_device_get_permanent_hw_address (device); + if ( device_addr + && nm_utils_hwaddr_matches (hwaddr_bin, hwaddr_len, device_addr, -1)) + return device; } return NULL; } @@ -966,16 +1244,15 @@ find_device_by_permanent_hw_addr (NMManager *manager, const char *hwaddr) static NMDevice * find_device_by_ip_iface (NMManager *self, const gchar *iface) { - GSList *iter; - - g_return_val_if_fail (iface != NULL, NULL); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *device; - for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = g_slist_next (iter)) { - NMDevice *candidate = iter->data; + g_return_val_if_fail (iface, NULL); - if ( nm_device_is_real (candidate) - && g_strcmp0 (nm_device_get_ip_iface (candidate), iface) == 0) - return candidate; + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { + if ( nm_device_is_real (device) + && nm_streq0 (nm_device_get_ip_iface (device), iface)) + return device; } return NULL; } @@ -1003,12 +1280,11 @@ find_device_by_iface (NMManager *self, { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMDevice *fallback = NULL; - GSList *iter; + NMDevice *candidate; g_return_val_if_fail (iface != NULL, NULL); - for (iter = priv->devices; iter; iter = iter->next) { - NMDevice *candidate = iter->data; + c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { if (strcmp (nm_device_get_iface (candidate), iface)) continue; @@ -1063,22 +1339,6 @@ _nm_state_to_string (NMState state) } } -static void -set_state (NMManager *self, NMState state) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - - if (priv->state == state) - return; - - priv->state = state; - - _LOGI (LOGD_CORE, "NetworkManager state is now %s", _nm_state_to_string (state)); - - _notify (self, PROP_STATE); - g_signal_emit (self, signals[STATE_CHANGED], 0, priv->state); -} - static NMState find_best_device_state (NMManager *manager) { @@ -1149,26 +1409,38 @@ nm_manager_update_metered (NMManager *self) } static void -nm_manager_update_state (NMManager *manager) +nm_manager_update_state (NMManager *self) { NMManagerPrivate *priv; NMState new_state = NM_STATE_DISCONNECTED; - g_return_if_fail (NM_IS_MANAGER (manager)); + g_return_if_fail (NM_IS_MANAGER (self)); - priv = NM_MANAGER_GET_PRIVATE (manager); + priv = NM_MANAGER_GET_PRIVATE (self); - if (manager_sleeping (manager)) + if (manager_sleeping (self)) new_state = NM_STATE_ASLEEP; else - new_state = find_best_device_state (manager); + new_state = find_best_device_state (self); if ( new_state >= NM_STATE_CONNECTED_LOCAL && priv->connectivity_state == NM_CONNECTIVITY_FULL) { new_state = NM_STATE_CONNECTED_GLOBAL; } - set_state (manager, new_state); + if (priv->state == new_state) + return; + + priv->state = new_state; + + _LOGI (LOGD_CORE, "NetworkManager state is now %s", _nm_state_to_string (new_state)); + + _notify (self, PROP_STATE); + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), + &interface_info_manager, + &signal_info_state_changed, + "(u)", + (guint32) priv->state); } static void @@ -1210,7 +1482,7 @@ static void check_if_startup_complete (NMManager *self) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GSList *iter; + NMDevice *device; if (!priv->startup) return; @@ -1223,12 +1495,10 @@ check_if_startup_complete (NMManager *self) return; } - for (iter = priv->devices; iter; iter = iter->next) { - NMDevice *dev = iter->data; - - if (nm_device_has_pending_action (dev)) { + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { + if (nm_device_has_pending_action (device)) { _LOGD (LOGD_CORE, "check_if_startup_complete returns FALSE because of %s", - nm_device_get_iface (dev)); + nm_device_get_iface (device)); return; } } @@ -1240,8 +1510,8 @@ check_if_startup_complete (NMManager *self) /* we no longer care about these signals. Startup-complete only * happens once. */ g_signal_handlers_disconnect_by_func (priv->settings, G_CALLBACK (settings_startup_complete_changed), self); - for (iter = priv->devices; iter; iter = iter->next) { - g_signal_handlers_disconnect_by_func (iter->data, + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { + g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_has_pending_action_changed), self); } @@ -1273,18 +1543,18 @@ _parent_notify_changed (NMManager *self, NMDevice *device, gboolean device_removed) { - GSList *iter; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *candidate; nm_assert (NM_IS_DEVICE (device)); - nm_assert (NM_IS_MANAGER (self)); - for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; ) { - if (nm_device_parent_notify_changed (iter->data, device, device_removed)) { +again: + c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { + if (nm_device_parent_notify_changed (candidate, device, device_removed)) { /* in the unlikely event that this changes anything, we start iterating * again, to be sure that the device list is up-to-date. */ - iter = NM_MANAGER_GET_PRIVATE (self)->devices; - } else - iter = iter->next; + goto again; + } } } @@ -1324,7 +1594,8 @@ remove_device (NMManager *self, g_signal_handlers_disconnect_matched (device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self); nm_settings_device_removed (priv->settings, device, quitting); - priv->devices = g_slist_remove (priv->devices, device); + + c_list_unlink (&device->devices_lst); _parent_notify_changed (self, device, TRUE); @@ -1342,8 +1613,7 @@ remove_device (NMManager *self, * Control that by passing @unconfigure_ip_config. */ nm_device_removed (device, unconfigure_ip_config); - g_signal_emit (self, signals[DEVICE_REMOVED], 0, device); - _notify (self, PROP_DEVICES); + _emit_device_added_removed (self, device, FALSE); } else { /* unrealize() does not release a slave device from master and * clear IP configurations, do it here */ @@ -1353,7 +1623,7 @@ remove_device (NMManager *self, g_signal_emit (self, signals[INTERNAL_DEVICE_REMOVED], 0, device); _notify (self, PROP_ALL_DEVICES); - nm_exported_object_clear_and_unexport (&device); + nm_dbus_object_clear_and_unexport (&device); check_if_startup_complete (self); } @@ -1382,7 +1652,7 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM const char *parent_name = NULL; NMSettingsConnection *parent_connection; NMDevice *parent, *first_compatible = NULL; - GSList *iter; + NMDevice *candidate; g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); @@ -1415,9 +1685,7 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM /* Check if the parent connection is currently activated or is comaptible * with some known device. */ - for (iter = priv->devices; iter; iter = iter->next) { - NMDevice *candidate = iter->data; - + c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { /* Unmanaged devices are not compatible with any connection */ if (!nm_device_get_managed (candidate, FALSE)) continue; @@ -1533,18 +1801,15 @@ NMDevice * nm_manager_get_device (NMManager *self, const char *ifname, NMDeviceType device_type) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GSList *iter; - NMDevice *d; + NMDevice *device; g_return_val_if_fail (ifname, NULL); g_return_val_if_fail (device_type != NM_DEVICE_TYPE_UNKNOWN, NULL); - for (iter = priv->devices; iter; iter = iter->next) { - d = iter->data; - - if ( nm_device_get_device_type (d) == device_type - && nm_streq0 (nm_device_get_iface (d), ifname)) - return d; + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { + if ( nm_device_get_device_type (device) == device_type + && nm_streq0 (nm_device_get_iface (device), ifname)) + return device; } return NULL; @@ -1580,9 +1845,9 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) NMDeviceFactory *factory; gs_free NMSettingsConnection **connections = NULL; guint i; - GSList *iter; gs_free char *iface = NULL; NMDevice *device = NULL, *parent = NULL; + NMDevice *dev_candidate; GError *error = NULL; NMLogLevel log_level; @@ -1598,17 +1863,15 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) } /* See if there's a device that is already compatible with this connection */ - for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - NMDevice *candidate = iter->data; - - if (nm_device_check_connection_compatible (candidate, connection)) { - if (nm_device_is_real (candidate)) { + c_list_for_each_entry (dev_candidate, &priv->devices_lst_head, devices_lst) { + if (nm_device_check_connection_compatible (dev_candidate, connection)) { + if (nm_device_is_real (dev_candidate)) { _LOG3D (LOGD_DEVICE, connection, "already created virtual interface name %s", iface); return NULL; } - device = candidate; + device = dev_candidate; break; } } @@ -1804,11 +2067,11 @@ connection_flags_changed (NMSettings *settings, DeleteVolatileConnectionData *data; if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (connection), - NM_SETTINGS_CONNECTION_FLAGS_VOLATILE)) + NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)) return; - if (active_connection_find_first (self, connection, NULL, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)) { - /* the connection still have an active-connection. It will be purged + if (active_connection_find (self, connection, NULL, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED, NULL)) { + /* the connection still has an active-connection. It will be purged * when the active connection(s) get(s) removed. */ return; } @@ -1829,10 +2092,10 @@ system_unmanaged_devices_changed_cb (NMSettings *settings, { NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - const GSList *iter; + NMDevice *device; - for (iter = priv->devices; iter; iter = g_slist_next (iter)) - nm_device_set_unmanaged_by_user_settings (NM_DEVICE (iter->data)); + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) + nm_device_set_unmanaged_by_user_settings (device); } static void @@ -1878,7 +2141,7 @@ manager_update_radio_enabled (NMManager *self, gboolean enabled) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GSList *iter; + NMDevice *device; /* Do nothing for radio types not yet implemented */ if (!rstate->prop) @@ -1891,9 +2154,7 @@ manager_update_radio_enabled (NMManager *self, return; /* enable/disable wireless devices as required */ - for (iter = priv->devices; iter; iter = iter->next) { - NMDevice *device = NM_DEVICE (iter->data); - + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { if (nm_device_get_rfkill_type (device) == rstate->rtype) { _LOG2D (LOGD_RFKILL, device, "rfkill: setting radio %s", enabled ? "enabled" : "disabled"); nm_device_set_enabled (device, enabled); @@ -2036,7 +2297,7 @@ device_auth_done_cb (NMAuthChain *chain, nm_auth_chain_get_data (chain, "user-data")); g_clear_error (&error); - nm_auth_chain_unref (chain); + nm_auth_chain_destroy (chain); } static void @@ -2052,7 +2313,6 @@ device_auth_request_cb (NMDevice *device, NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GError *error = NULL; NMAuthSubject *subject = NULL; - char *error_desc = NULL; NMAuthChain *chain; /* Validate the caller */ @@ -2065,15 +2325,13 @@ device_auth_request_cb (NMDevice *device, } /* Ensure the subject has permissions for this connection */ - if (connection && !nm_auth_is_subject_in_acl (connection, - subject, - &error_desc)) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); - g_free (error_desc); + if ( connection + && !nm_auth_is_subject_in_acl_set_error (connection, + subject, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + &error)) goto done; - } /* Validate the request */ chain = nm_auth_chain_new_subject (subject, context, device_auth_done_cb, self); @@ -2186,8 +2444,9 @@ get_existing_connection (NMManager *self, */ if ( assume_state_connection_uuid && (connection_checked = nm_settings_get_connection_by_uuid (priv->settings, assume_state_connection_uuid)) - && !active_connection_find_first (self, connection_checked, NULL, - NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) + && !active_connection_find (self, connection_checked, NULL, + NM_ACTIVE_CONNECTION_STATE_ACTIVATED, + NULL) && nm_device_check_connection_compatible (device, NM_CONNECTION (connection_checked))) { if (connection) { @@ -2270,8 +2529,8 @@ get_existing_connection (NMManager *self, } nm_settings_connection_set_flags (NM_SETTINGS_CONNECTION (added), - NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED | - NM_SETTINGS_CONNECTION_FLAGS_VOLATILE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED | + NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE, TRUE); NM_SET_OUT (out_generated, TRUE); return added; @@ -2335,15 +2594,20 @@ recheck_assume_connection (NMManager *self, GError *error = NULL; subject = nm_auth_subject_new_internal (); - active = _new_active_connection (self, NM_CONNECTION (connection), NULL, NULL, - device, subject, + active = _new_active_connection (self, + FALSE, + NM_CONNECTION (connection), + NULL, + NULL, + device, + subject, generated ? NM_ACTIVATION_TYPE_EXTERNAL : NM_ACTIVATION_TYPE_ASSUME, - NM_ACTIVATION_REASON_AUTOCONNECT, + generated ? NM_ACTIVATION_REASON_EXTERNAL : NM_ACTIVATION_REASON_ASSUME, &error); if (!active) { _LOGW (LOGD_DEVICE, "assume: assumed connection %s failed to activate: %s", - nm_connection_get_path (NM_CONNECTION (connection)), + nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)), error->message); g_error_free (error); @@ -2394,18 +2658,17 @@ device_ip_iface_changed (NMDevice *device, GParamSpec *pspec, NMManager *self) { + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); const char *ip_iface = nm_device_get_ip_iface (device); NMDeviceType device_type = nm_device_get_device_type (device); - GSList *iter; + NMDevice *candidate; /* Remove NMDevice objects that are actually child devices of others, * when the other device finally knows its IP interface name. For example, * remove the PPP interface that's a child of a WWAN device, since it's * not really a standalone NMDevice. */ - for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) { - NMDevice *candidate = NM_DEVICE (iter->data); - + c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { if ( candidate != device && g_strcmp0 (nm_device_get_iface (candidate), ip_iface) == 0 && nm_device_get_device_type (candidate) == device_type @@ -2427,35 +2690,56 @@ device_iface_changed (NMDevice *device, retry_connections_for_parent_device (self, device); } +static void +_emit_device_added_removed (NMManager *self, + NMDevice *device, + gboolean is_added) +{ + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), + &interface_info_manager, + is_added + ? &signal_info_device_added + : &signal_info_device_removed, + "(o)", + nm_dbus_object_get_path (NM_DBUS_OBJECT (device))); + g_signal_emit (self, + signals[is_added ? DEVICE_ADDED : DEVICE_REMOVED], + 0, + device); + _notify (self, PROP_DEVICES); +} static void device_realized (NMDevice *device, GParamSpec *pspec, NMManager *self) { - gboolean real = nm_device_is_real (device); - - /* Emit D-Bus signals */ - g_signal_emit (self, signals[real ? DEVICE_ADDED : DEVICE_REMOVED], 0, device); - _notify (self, PROP_DEVICES); + _emit_device_added_removed (self, device, nm_device_is_real (device)); } -#if WITH_CONCHECK static void device_connectivity_changed (NMDevice *device, - GParamSpec *pspec, NMManager *self) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMConnectivityState best_state = NM_CONNECTIVITY_UNKNOWN; NMConnectivityState state; - const GSList *devices; + NMDevice *dev; - for (devices = priv->devices; devices; devices = devices->next) { - state = nm_device_get_connectivity_state (NM_DEVICE (devices->data)); - if (state > best_state) + best_state = nm_device_get_connectivity_state (device); + if (best_state < NM_CONNECTIVITY_FULL) { + c_list_for_each_entry (dev, &priv->devices_lst_head, devices_lst) { + state = nm_device_get_connectivity_state (dev); + if (state <= best_state) + continue; best_state = state; + if (best_state >= NM_CONNECTIVITY_FULL) { + /* it doesn't get better than this. */ + break; + } + } } + nm_assert (best_state <= NM_CONNECTIVITY_FULL); if (best_state != priv->connectivity_state) { priv->connectivity_state = best_state; @@ -2468,7 +2752,6 @@ device_connectivity_changed (NMDevice *device, nm_dispatcher_call_connectivity (priv->connectivity_state, NULL, NULL, NULL); } } -#endif static void _device_realize_finish (NMManager *self, @@ -2514,6 +2797,7 @@ add_device (NMManager *self, NMDevice *device, GError **error) GSList *iter, *remove = NULL; int ifindex; const char *dbus_path; + NMDevice *candidate; /* No duplicates */ ifindex = nm_device_get_ifindex (device); @@ -2530,18 +2814,20 @@ add_device (NMManager *self, NMDevice *device, GError **error) * FIXME: use parent/child device relationships instead of removing * the child NMDevice entirely */ - for (iter = priv->devices; iter; iter = iter->next) { - NMDevice *candidate = iter->data; - - iface = nm_device_get_ip_iface (candidate); - if (nm_device_is_real (candidate) && nm_device_owns_iface (device, iface)) + c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { + if ( nm_device_is_real (candidate) + && (iface = nm_device_get_ip_iface (candidate)) + && nm_device_owns_iface (device, iface)) remove = g_slist_prepend (remove, candidate); } for (iter = remove; iter; iter = iter->next) remove_device (self, NM_DEVICE (iter->data), FALSE, FALSE); g_slist_free (remove); - priv->devices = g_slist_append (priv->devices, g_object_ref (device)); + g_object_ref (device); + + nm_assert (c_list_is_empty (&device->devices_lst)); + c_list_link_tail (&priv->devices_lst_head, &device->devices_lst); g_signal_connect (device, NM_DEVICE_STATE_CHANGED, G_CALLBACK (manager_device_state_changed), @@ -2575,11 +2861,9 @@ add_device (NMManager *self, NMDevice *device, GError **error) G_CALLBACK (device_realized), self); -#if WITH_CONCHECK - g_signal_connect (device, "notify::" NM_DEVICE_CONNECTIVITY, + g_signal_connect (device, NM_DEVICE_CONNECTIVITY_CHANGED, G_CALLBACK (device_connectivity_changed), self); -#endif if (priv->startup) { g_signal_connect (device, "notify::" NM_DEVICE_HAS_PENDING_ACTION, @@ -2608,7 +2892,7 @@ add_device (NMManager *self, NMDevice *device, GError **error) NM_UNMANAGED_SLEEPING, manager_sleeping (self)); - dbus_path = nm_exported_object_export (NM_EXPORTED_OBJECT (device)); + dbus_path = nm_dbus_object_export (NM_DBUS_OBJECT (device)); _LOG2I (LOGD_DEVICE, device, "new %s device (%s)", type_desc, dbus_path); nm_settings_device_added (priv->settings, device); @@ -2655,12 +2939,11 @@ factory_component_added_cb (NMDeviceFactory *factory, gpointer user_data) { NMManager *self = user_data; - GSList *iter; - - g_return_val_if_fail (self, FALSE); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *device; - for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) { - if (nm_device_notify_component_added ((NMDevice *) iter->data, component)) + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { + if (nm_device_notify_component_added (device, component)) return TRUE; } return FALSE; @@ -2690,9 +2973,10 @@ platform_link_added (NMManager *self, gboolean guess_assume, const NMConfigDeviceStateData *dev_state) { + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMDeviceFactory *factory; NMDevice *device = NULL; - GSList *iter; + NMDevice *candidate; g_return_if_fail (ifindex > 0); @@ -2700,8 +2984,7 @@ platform_link_added (NMManager *self, return; /* Let unrealized devices try to realize themselves with the link */ - for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) { - NMDevice *candidate = iter->data; + c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { gboolean compatible = TRUE; gs_free_error GError *error = NULL; @@ -2928,12 +3211,12 @@ rfkill_manager_rfkill_changed_cb (NMRfkillManager *rfkill_mgr, nm_manager_rfkill_update (NM_MANAGER (user_data), rtype); } -const GSList * +const CList * nm_manager_get_devices (NMManager *manager) { g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); - return NM_MANAGER_GET_PRIVATE (manager)->devices; + return &NM_MANAGER_GET_PRIVATE (manager)->devices_lst_head; } static NMDevice * @@ -2942,24 +3225,87 @@ nm_manager_get_best_device_for_connection (NMManager *self, gboolean for_user_request, GHashTable *unavailable_devices) { - const GSList *devices, *iter; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMActiveConnectionState ac_state; NMActiveConnection *ac; - NMDevice *act_device; + NMDevice *ac_device; + NMDevice *device; NMDeviceCheckConAvailableFlags flags; + gs_unref_ptrarray GPtrArray *all_ac_arr = NULL; + + flags = for_user_request ? NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST : NM_DEVICE_CHECK_CON_AVAILABLE_NONE; - ac = active_connection_find_first_by_connection (self, connection); + ac = active_connection_find_by_connection (self, connection, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING, &all_ac_arr); if (ac) { - act_device = nm_active_connection_get_device (ac); - if (act_device) - return act_device; - } - flags = for_user_request ? NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST : NM_DEVICE_CHECK_CON_AVAILABLE_NONE; + ac_device = nm_active_connection_get_device (ac); + if ( ac_device + && ( (unavailable_devices && g_hash_table_contains (unavailable_devices, ac_device)) + || !nm_device_check_connection_available (ac_device, connection, flags, NULL))) + ac_device = NULL; + + if (all_ac_arr) { + guint i; + + ac_state = nm_active_connection_get_state (ac); + + /* we found several active connections. See which one is the most suitable... */ + nm_assert (ac == all_ac_arr->pdata[0]); + for (i = 1; i < all_ac_arr->len; i++) { + NMActiveConnection *ac2 = all_ac_arr->pdata[i]; + NMDevice *ac_device2 = nm_active_connection_get_device (ac2); + NMActiveConnectionState ac_state2; + + if ( !ac_device2 + || (unavailable_devices && g_hash_table_contains (unavailable_devices, ac_device2)) + || !nm_device_check_connection_available (ac_device2, connection, flags, NULL)) + continue; + + ac_state2 = nm_active_connection_get_state (ac2); + + if (!ac_device) + goto found_better; + + if (ac_state == ac_state2) { + /* active-connections are in their list in the order in which they are connected. + * If we have two with same state, the later (newer) one is preferred. */ + goto found_better; + } + + switch (ac_state) { + case NM_ACTIVE_CONNECTION_STATE_UNKNOWN: + if (NM_IN_SET (ac_state2, NM_ACTIVE_CONNECTION_STATE_ACTIVATING, NM_ACTIVE_CONNECTION_STATE_ACTIVATED, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING)) + goto found_better; + break; + case NM_ACTIVE_CONNECTION_STATE_ACTIVATING: + if (NM_IN_SET (ac_state2, NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) + goto found_better; + break; + case NM_ACTIVE_CONNECTION_STATE_ACTIVATED: + break; + case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING: + if (NM_IN_SET (ac_state2, NM_ACTIVE_CONNECTION_STATE_ACTIVATING, NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) + goto found_better; + break; + default: + nm_assert_not_reached (); + goto found_better; + } + + continue; +found_better: + ac = ac2; + ac_state = ac_state2; + ac_device = ac_device2; + } + } + + if (ac_device) + return ac_device; + } /* Pick the first device that's compatible with the connection. */ - devices = nm_manager_get_devices (self); - for (iter = devices; iter; iter = g_slist_next (iter)) { - NMDevice *device = NM_DEVICE (iter->data); + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { if (unavailable_devices && g_hash_table_contains (unavailable_devices, device)) continue; @@ -2972,67 +3318,100 @@ nm_manager_get_best_device_for_connection (NMManager *self, return NULL; } -static void -_get_devices (NMManager *self, - GDBusMethodInvocation *context, - gboolean all_devices) +static const char ** +_get_devices_paths (NMManager *self, + gboolean all_devices) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - gs_free const char **paths = NULL; + const char **paths = NULL; guint i; - GSList *iter; + NMDevice *device; - paths = g_new (const char *, g_slist_length (priv->devices) + 1); + paths = g_new (const char *, c_list_length (&priv->devices_lst_head) + 1); - for (i = 0, iter = priv->devices; iter; iter = iter->next) { + i = 0; + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { const char *path; - path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data)); - if ( path - && (all_devices || nm_device_is_real (iter->data))) - paths[i++] = path; + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (device)); + if (!path) + continue; + + if ( !all_devices + && !nm_device_is_real (device)) + continue; + + paths[i++] = path; } paths[i++] = NULL; - g_dbus_method_invocation_return_value (context, - g_variant_new ("(^ao)", (char **) paths)); + return paths; } static void -impl_manager_get_devices (NMManager *self, - GDBusMethodInvocation *context) -{ - _get_devices (self, context, FALSE); +impl_manager_get_devices (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); + gs_free const char **paths = NULL; + + paths = _get_devices_paths (self, FALSE); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(^ao)", (char **) paths)); } static void -impl_manager_get_all_devices (NMManager *self, - GDBusMethodInvocation *context) -{ - _get_devices (self, context, TRUE); +impl_manager_get_all_devices (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); + gs_free const char **paths = NULL; + + paths = _get_devices_paths (self, TRUE); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(^ao)", (char **) paths)); } static void -impl_manager_get_device_by_ip_iface (NMManager *self, - GDBusMethodInvocation *context, - const char *iface) -{ +impl_manager_get_device_by_ip_iface (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); NMDevice *device; const char *path = NULL; + const char *iface; + + g_variant_get (parameters, "(&s)", &iface); device = find_device_by_ip_iface (self, iface); if (device) - path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device)); + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (device)); - if (path == NULL) { - g_dbus_method_invocation_return_error (context, + if (!path) { + g_dbus_method_invocation_return_error (invocation, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, "No device found for the requested iface."); - } else { - g_dbus_method_invocation_return_value (context, - g_variant_new ("(o)", path)); + return; } + + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(o)", path)); } static gboolean @@ -3105,7 +3484,6 @@ find_master (NMManager *self, const char *master; NMDevice *master_device = NULL; NMSettingsConnection *master_connection = NULL; - GSList *iter; s_con = nm_connection_get_setting_connection (connection); g_assert (s_con); @@ -3134,10 +3512,10 @@ find_master (NMManager *self, /* Try master as a connection UUID */ master_connection = nm_settings_get_connection_by_uuid (priv->settings, master); if (master_connection) { - /* Check if the master connection is activated on some device already */ - for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - NMDevice *candidate = NM_DEVICE (iter->data); + NMDevice *candidate; + /* Check if the master connection is activated on some device already */ + c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { if (candidate == device) continue; @@ -3154,8 +3532,9 @@ find_master (NMManager *self, if (out_master_device) *out_master_device = master_device; if (out_master_ac && master_connection) { - *out_master_ac = active_connection_find_first (self, master_connection, NULL, - NM_ACTIVE_CONNECTION_STATE_DEACTIVATING); + *out_master_ac = active_connection_find (self, master_connection, NULL, + NM_ACTIVE_CONNECTION_STATE_DEACTIVATING, + NULL); } if (master_device || master_connection) @@ -3209,7 +3588,6 @@ ensure_master_active_connection (NMManager *self, NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMActiveConnection *master_ac = NULL; NMDeviceState master_state; - GSList *iter; g_assert (connection); g_assert (master_connection || master_device); @@ -3286,12 +3664,10 @@ ensure_master_active_connection (NMManager *self, NM_MANAGER_ERROR_DEPENDENCY_FAILED, "Device unmanaged or not available for activation"); } else if (master_connection) { - gboolean found_device = FALSE; + NMDevice *candidate; /* Find a compatible device and activate it using this connection */ - for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - NMDevice *candidate = NM_DEVICE (iter->data); - + c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { if (candidate == device) { /* A device obviously can't be its own master */ continue; @@ -3300,7 +3676,6 @@ ensure_master_active_connection (NMManager *self, if (!nm_device_check_connection_available (candidate, NM_CONNECTION (master_connection), NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, NULL)) continue; - found_device = TRUE; if (!nm_device_is_software (candidate)) { master_state = nm_device_get_state (candidate); if (nm_device_is_real (candidate) && master_state != NM_DEVICE_STATE_DISCONNECTED) @@ -3366,7 +3741,7 @@ find_slaves (NMManager *manager, s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection)); g_return_val_if_fail (s_con, NULL); - devices = g_hash_table_new (g_direct_hash, g_direct_equal); + devices = g_hash_table_new (nm_direct_hash, NULL); /* Search through all connections, not only inactive ones, because * even if a slave was already active, it might be deactivated during @@ -3552,11 +3927,11 @@ _internal_activate_vpn (NMManager *self, NMActiveConnection *active, GError **er { nm_assert (NM_IS_VPN_CONNECTION (active)); - nm_exported_object_export (NM_EXPORTED_OBJECT (active)); + nm_dbus_object_export (NM_DBUS_OBJECT (active)); if (!nm_vpn_manager_activate_connection (NM_MANAGER_GET_PRIVATE (self)->vpn_manager, NM_VPN_CONNECTION (active), error)) { - nm_exported_object_unexport (NM_EXPORTED_OBJECT (active)); + nm_dbus_object_unexport (NM_DBUS_OBJECT (active)); return FALSE; } @@ -3643,15 +4018,13 @@ active_connection_parent_active (NMActiveConnection *active, static gboolean _internal_activate_device (NMManager *self, NMActiveConnection *active, GError **error) { - NMDevice *device, *existing, *master_device = NULL; - NMActiveConnection *existing_ac; + NMDevice *device, *master_device = NULL; NMConnection *applied; NMSettingsConnection *connection; NMSettingsConnection *master_connection = NULL; NMConnection *existing_connection = NULL; NMActiveConnection *master_ac = NULL; NMAuthSubject *subject; - char *error_desc = NULL; g_return_val_if_fail (NM_IS_MANAGER (self), FALSE); g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (active), FALSE); @@ -3659,14 +4032,14 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * g_assert (NM_IS_VPN_CONNECTION (active) == FALSE); + device = nm_active_connection_get_device (active); + g_return_val_if_fail (device != NULL, FALSE); + connection = nm_active_connection_get_settings_connection (active); - g_assert (connection); + nm_assert (connection); applied = nm_active_connection_get_applied_connection (active); - device = nm_active_connection_get_device (active); - g_return_val_if_fail (device != NULL, FALSE); - /* If the device is active and its connection is not visible to the * user that's requesting this new activation, fail, since other users * should not be allowed to implicitly deactivate private connections @@ -3674,16 +4047,13 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * */ existing_connection = nm_device_get_applied_connection (device); subject = nm_active_connection_get_subject (active); - if (existing_connection && - !nm_auth_is_subject_in_acl (existing_connection, - subject, - &error_desc)) { - g_set_error (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Private connection already active on the device: %s", - error_desc); - g_free (error_desc); + if ( existing_connection + && !nm_auth_is_subject_in_acl_set_error (existing_connection, + subject, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + error)) { + g_prefix_error (error, "Private connection already active on the device: "); return FALSE; } @@ -3695,6 +4065,9 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * return FALSE; } + if (nm_active_connection_get_activation_type (active) == NM_ACTIVATION_TYPE_MANAGED) + nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_MANAGED); + /* Create any backing resources the device needs */ if (!nm_device_is_real (device)) { NMDevice *parent; @@ -3803,18 +4176,29 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * _LOGD (LOGD_CORE, "Activation of '%s' depends on active connection %p %s", nm_settings_connection_get_id (connection), master_ac, - nm_exported_object_get_path (NM_EXPORTED_OBJECT (master_ac)) ?: ""); + nm_dbus_object_get_path (NM_DBUS_OBJECT (master_ac)) ?: ""); } /* Check slaves for master connection and possibly activate them */ autoconnect_slaves (self, connection, device, nm_active_connection_get_subject (active)); - /* Disconnect the connection if connected or queued on another device */ - existing_ac = active_connection_find_first (self, connection, NULL, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING); - if (existing_ac) { - existing = nm_active_connection_get_device (existing_ac); - if (existing) - nm_device_steal_connection (existing, connection); + { + gs_unref_ptrarray GPtrArray *all_ac_arr = NULL; + NMActiveConnection *ac; + guint i, n_all; + + /* Disconnect the connection if already connected or queued for activation. + * The connection cannot be active multiple times (at the same time). */ + ac = active_connection_find (self, connection, NULL, NM_ACTIVE_CONNECTION_STATE_ACTIVATED, + &all_ac_arr); + if (ac) { + n_all = all_ac_arr ? all_ac_arr->len : ((guint) 1); + for (i = 0; i < n_all; i++) { + nm_device_disconnect_active_connection ( all_ac_arr + ? all_ac_arr->pdata[i] + : ac); + } + } } /* If the device is there, we can ready it for the activation. */ @@ -3874,53 +4258,9 @@ _internal_activate_generic (NMManager *self, NMActiveConnection *active, GError return success; } -static NMActiveConnection * -_new_vpn_active_connection (NMManager *self, - NMSettingsConnection *settings_connection, - const char *specific_object, - NMAuthSubject *subject, - NMActivationReason activation_reason, - GError **error) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMActiveConnection *parent = NULL; - NMDevice *device = NULL; - - g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL); - - if (specific_object) { - /* Find the specific connection the client requested we use */ - parent = active_connection_get_by_path (self, specific_object); - if (!parent) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, - "Base connection for VPN connection not active."); - return NULL; - } - } else - parent = priv->primary_connection; - - if (!parent) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION, - "Could not find source connection."); - return NULL; - } - - device = nm_active_connection_get_device (parent); - if (!device) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Source connection had no active device."); - return NULL; - } - - return (NMActiveConnection *) nm_vpn_connection_new (settings_connection, - device, - nm_exported_object_get_path (NM_EXPORTED_OBJECT (parent)), - activation_reason, - subject); -} - static NMActiveConnection * _new_active_connection (NMManager *self, + gboolean is_vpn, NMConnection *connection, NMConnection *applied, const char *specific_object, @@ -3930,44 +4270,77 @@ _new_active_connection (NMManager *self, NMActivationReason activation_reason, GError **error) { + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMSettingsConnection *settings_connection = NULL; - NMActiveConnection *existing_ac; - gboolean is_vpn; + NMDevice *parent_device; g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); - /* Can't create new AC for already-active connection */ - existing_ac = active_connection_find_first_by_connection (self, connection); - if (NM_IS_VPN_CONNECTION (existing_ac)) { - g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE, - "Connection '%s' is already active", - nm_connection_get_id (connection)); - return NULL; - } - - /* Normalize the specific object */ - if (specific_object && g_strcmp0 (specific_object, "/") == 0) - specific_object = NULL; - - is_vpn = nm_connection_is_type (NM_CONNECTION (connection), NM_SETTING_VPN_SETTING_NAME); + nm_assert (is_vpn == _connection_is_vpn (connection)); + nm_assert (is_vpn || NM_IS_DEVICE (device)); + nm_assert (!nm_streq0 (specific_object, "/")); if (NM_IS_SETTINGS_CONNECTION (connection)) settings_connection = (NMSettingsConnection *) connection; if (is_vpn) { + NMActiveConnection *parent; + + /* FIXME: for VPN connections, we don't allow re-activating an + * already active connection. It's a bug, and should be fixed together + * when reworking VPN handling. */ + if (active_connection_find_by_connection (self, connection, NM_ACTIVE_CONNECTION_STATE_ACTIVATED, NULL)) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE, + "Connection '%s' is already active", + nm_connection_get_id (connection)); + return NULL; + } + + /* FIXME: apparently, activation here only works if @connection is + * a settings-connection. Which is not the case during AddAndActivatate. + * Probably, AddAndActivate is broken for VPN. */ if (activation_type != NM_ACTIVATION_TYPE_MANAGED) g_return_val_if_reached (NULL); - return _new_vpn_active_connection (self, - settings_connection, - specific_object, - subject, - activation_reason, - error); - } - if (device && (activation_type == NM_ACTIVATION_TYPE_MANAGED)) - nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_MANAGED); + g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL); + + if (specific_object) { + /* Find the specific connection the client requested we use */ + parent = active_connection_get_by_path (self, specific_object); + if (!parent) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, + "Base connection for VPN connection not active."); + return NULL; + } + } else + parent = priv->primary_connection; + + if (!parent) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "Could not find source connection."); + return NULL; + } + + parent_device = nm_active_connection_get_device (parent); + if (!parent_device) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Source connection had no active device"); + return NULL; + } + + if (device && device != parent_device) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "The device doesn't match the active connection."); + return NULL; + } + + return (NMActiveConnection *) nm_vpn_connection_new (settings_connection, + parent_device, + nm_dbus_object_get_path (NM_DBUS_OBJECT (parent)), + activation_reason, + subject); + } return (NMActiveConnection *) nm_act_request_new (settings_connection, applied, @@ -3979,29 +4352,28 @@ _new_active_connection (NMManager *self, } static void -_internal_activation_auth_done (NMActiveConnection *active, +_internal_activation_auth_done (NMManager *self, + NMActiveConnection *active, gboolean success, - const char *error_desc, - gpointer user_data1, - gpointer user_data2) + const char *error_desc) { - _nm_unused gs_unref_object NMActiveConnection *active_to_free = active; - NMManager *self = user_data1; NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMActiveConnection *ac; gs_free_error GError *error = NULL; - priv->authorizing_connections = g_slist_remove (priv->authorizing_connections, active); + nm_assert (NM_IS_ACTIVE_CONNECTION (active)); + + if (!success) + goto fail; - /* Don't continue with an internal activation if an equivalent active - * connection already exists. Note that slave autoconnections always force a - * reconnection. We also check this earlier, but there we may fail to - * detect a duplicate if the existing active connection is undergoing - * authorization in impl_manager_activate_connection(). + /* Don't continue with an autoconnect-activation if a more important activation + * already exists. + * We also check this earlier, but there we may fail to detect a duplicate + * if the existing active connection was undergoing authorization. */ - if ( success - && nm_auth_subject_is_internal (nm_active_connection_get_subject (active)) - && nm_active_connection_get_activation_reason (active) != NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES) { + if (NM_IN_SET (nm_active_connection_get_activation_reason (active), NM_ACTIVATION_REASON_EXTERNAL, + NM_ACTIVATION_REASON_ASSUME, + NM_ACTIVATION_REASON_AUTOCONNECT)) { c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) { if ( nm_active_connection_get_device (ac) == nm_active_connection_get_device (active) && nm_active_connection_get_settings_connection (ac) == nm_active_connection_get_settings_connection (active) @@ -4013,17 +4385,15 @@ _internal_activation_auth_done (NMActiveConnection *active, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE, "Connection '%s' is already active", nm_active_connection_get_settings_connection_id (active)); - success = FALSE; - break; + goto fail; } } } - if (success) { - if (_internal_activate_generic (self, active, &error)) - return; - } + if (_internal_activate_generic (self, active, &error)) + return; +fail: nm_assert (error_desc || error); nm_active_connection_set_state_fail (active, NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN, @@ -4036,7 +4406,7 @@ _internal_activation_auth_done (NMActiveConnection *active, * @connection: the #NMSettingsConnection to activate on @device * @applied: (allow-none): the applied connection to activate on @device * @specific_object: the specific object path, if any, for the activation - * @device: the #NMDevice to activate @connection on + * @device: the #NMDevice to activate @connection on. Can be %NULL for VPNs. * @subject: the subject which requested activation * @activation_type: whether to assume the connection. That is, take over gracefully, * non-destructible. @@ -4064,39 +4434,41 @@ nm_manager_activate_connection (NMManager *self, NMActivationReason activation_reason, GError **error) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMManagerPrivate *priv; NMActiveConnection *active; - char *error_desc = NULL; - GSList *iter; + AsyncOpData *async_op_data; + gboolean is_vpn; - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (connection != NULL, NULL); - g_return_val_if_fail (error != NULL, NULL); - g_return_val_if_fail (*error == NULL, NULL); + g_return_val_if_fail (NM_IS_MANAGER (self), NULL); + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), NULL); + is_vpn = _connection_is_vpn (NM_CONNECTION (connection)); + g_return_val_if_fail (is_vpn || NM_IS_DEVICE (device), NULL); + g_return_val_if_fail (!error || !*error, NULL); + nm_assert (!nm_streq0 (specific_object, "/")); - /* Ensure the subject has permissions for this connection */ - if (!nm_auth_is_subject_in_acl (NM_CONNECTION (connection), - subject, - &error_desc)) { - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); - g_free (error_desc); + priv = NM_MANAGER_GET_PRIVATE (self); + + if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (connection), + subject, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + error)) return NULL; - } /* Look for a active connection that's equivalent and is already pending authorization * and eventual activation. This is used to de-duplicate concurrent activations which would * otherwise race and cause the device to disconnect and reconnect repeatedly. * In particular, this allows the master and multiple slaves to concurrently auto-activate * while all the slaves would use the same active-connection. */ - for (iter = priv->authorizing_connections; iter; iter = g_slist_next (iter)) { - active = iter->data; + c_list_for_each_entry (async_op_data, &priv->async_op_lst_head, async_op_lst) { + + if (async_op_data->async_op_type != ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_INTERNAL) + continue; + active = async_op_data->ac_auth.active; if ( connection == nm_active_connection_get_settings_connection (active) - && g_strcmp0 (nm_active_connection_get_specific_object (active), specific_object) == 0 - && nm_active_connection_get_device (active) == device + && nm_streq0 (nm_active_connection_get_specific_object (active), specific_object) + && (!device || nm_active_connection_get_device (active) == device) && nm_auth_subject_is_internal (nm_active_connection_get_subject (active)) && nm_auth_subject_is_internal (subject) && nm_active_connection_get_activation_reason (active) == activation_reason) @@ -4104,6 +4476,7 @@ nm_manager_activate_connection (NMManager *self, } active = _new_active_connection (self, + is_vpn, NM_CONNECTION (connection), applied, specific_object, @@ -4112,10 +4485,14 @@ nm_manager_activate_connection (NMManager *self, activation_type, activation_reason, error); - if (active) { - priv->authorizing_connections = g_slist_prepend (priv->authorizing_connections, active); - nm_active_connection_authorize (active, NULL, _internal_activation_auth_done, self, NULL); - } + if (!active) + return NULL; + + nm_active_connection_authorize (active, + NULL, + _async_op_complete_ac_auth_cb, + _async_op_data_new_authorize_activate_internal (self, + active)); return active; } @@ -4124,9 +4501,12 @@ nm_manager_activate_connection (NMManager *self, * @self: the #NMManager * @context: the D-Bus context of the requestor * @connection: the partial or complete #NMConnection to be activated - * @device_path: the object path of the device to be activated, or "/" + * @device_path: the object path of the device to be activated, or NULL * @out_device: on successful reutrn, the #NMDevice to be activated with @connection - * @out_vpn: on successful return, %TRUE if @connection is a VPN connection + * The caller may pass in a device which shortcuts the lookup by path. + * In this case, the passed in device must have the matching @device_path + * already. + * @out_is_vpn: on successful return, %TRUE if @connection is a VPN connection * @error: location to store an error on failure * * Performs basic validation on an activation request, including ensuring that @@ -4142,17 +4522,16 @@ validate_activation_request (NMManager *self, NMConnection *connection, const char *device_path, NMDevice **out_device, - gboolean *out_vpn, + gboolean *out_is_vpn, GError **error) { NMDevice *device = NULL; - gboolean vpn = FALSE; - NMAuthSubject *subject = NULL; - char *error_desc = NULL; + gboolean is_vpn = FALSE; + gs_unref_object NMAuthSubject *subject = NULL; - g_assert (connection); - g_assert (out_device); - g_assert (out_vpn); + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (out_device); + nm_assert (out_is_vpn); /* Validate the caller */ subject = nm_auth_subject_new_unix_process_from_context (context); @@ -4164,131 +4543,138 @@ validate_activation_request (NMManager *self, return NULL; } - /* Ensure the subject has permissions for this connection */ - if (!nm_auth_is_subject_in_acl (connection, - subject, - &error_desc)) { - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); - g_free (error_desc); - goto error; - } - - /* Check whether it's a VPN or not */ - if ( nm_connection_get_setting_vpn (connection) - || nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME)) - vpn = TRUE; + if (!nm_auth_is_subject_in_acl_set_error (connection, + subject, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + error)) + return NULL; - /* Normalize device path */ - if (device_path && g_strcmp0 (device_path, "/") == 0) - device_path = NULL; + is_vpn = _connection_is_vpn (connection); - /* And validate it */ - if (device_path) { + if (*out_device) { + device = *out_device; + nm_assert (NM_IS_DEVICE (device)); + nm_assert (device_path); + nm_assert (nm_streq0 (device_path, nm_dbus_object_get_path (NM_DBUS_OBJECT (device)))); + nm_assert (device == nm_manager_get_device_by_path (self, device_path)); + } else if (device_path) { device = nm_manager_get_device_by_path (self, device_path); if (!device) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, "Device not found"); - goto error; + return NULL; } - } else + } else if (!is_vpn) { device = nm_manager_get_best_device_for_connection (self, connection, TRUE, NULL); + if (!device) { + gs_free char *iface = NULL; + + /* VPN and software-device connections don't need a device yet, + * but non-virtual connections do ... */ + if (!nm_connection_is_virtual (connection)) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "No suitable device found for this connection."); + return NULL; + } - if (!device && !vpn) { - gs_free char *iface = NULL; + /* Look for an existing device with the connection's interface name */ + iface = nm_manager_get_connection_iface (self, connection, NULL, error); + if (!iface) + return NULL; - /* VPN and software-device connections don't need a device yet, - * but non-virtual connections do ... */ - if (!nm_connection_is_virtual (connection)) { - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "No suitable device found for this connection."); - goto error; + device = find_device_by_iface (self, iface, connection, NULL); + if (!device) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Failed to find a compatible device for this connection"); + return NULL; + } } - - /* Look for an existing device with the connection's interface name */ - iface = nm_manager_get_connection_iface (self, connection, NULL, error); - if (!iface) - goto error; - - device = find_device_by_iface (self, iface, connection, NULL); } - if ((!vpn || device_path) && !device) { + if (is_vpn && device) { + /* VPN's are treated specially. Maybe the should accept a device as well, + * however, later on during activation, we don't handle the device. + * + * Maybe we should, and maybe it makes sense to specify a device + * when activating a VPN. But for now, just error out. */ g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Failed to find a compatible device for this connection"); - goto error; + "Cannot specify device when activating VPN"); + return NULL; } - *out_device = device; - *out_vpn = vpn; - return subject; + nm_assert ( ( is_vpn && !device) + || (!is_vpn && NM_IS_DEVICE (device))); -error: - g_object_unref (subject); - return NULL; + *out_device = device; + *out_is_vpn = is_vpn; + return g_steal_pointer (&subject); } /*****************************************************************************/ static void -_activation_auth_done (NMActiveConnection *active, +_activation_auth_done (NMManager *self, + NMActiveConnection *active, + GDBusMethodInvocation *invocation, gboolean success, - const char *error_desc, - gpointer user_data1, - gpointer user_data2) + const char *error_desc) { - NMManager *self = user_data1; - GDBusMethodInvocation *context = user_data2; GError *error = NULL; NMAuthSubject *subject; NMSettingsConnection *connection; - _nm_unused gs_unref_object NMActiveConnection *active_free = active; subject = nm_active_connection_get_subject (active); connection = nm_active_connection_get_settings_connection (active); - if (success) { - if (_internal_activate_generic (self, active, &error)) { - nm_settings_connection_autoconnect_blocked_reason_set (connection, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST, - FALSE); - g_dbus_method_invocation_return_value (context, - g_variant_new ("(o)", - nm_exported_object_get_path (NM_EXPORTED_OBJECT (active)))); - nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, TRUE, NULL, - subject, NULL); - return; - } - } else { + if (!success) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, error_desc); + goto fail; } + if (!_internal_activate_generic (self, active, &error)) + goto fail; + + nm_settings_connection_autoconnect_blocked_reason_set (connection, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST, + FALSE); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(o)", + nm_dbus_object_get_path (NM_DBUS_OBJECT (active)))); + nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, TRUE, NULL, + subject, NULL); + return; + +fail: nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE, NULL, subject, error->message); nm_active_connection_set_state_fail (active, NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN, error->message); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_take_error (invocation, error); } static void -impl_manager_activate_connection (NMManager *self, - GDBusMethodInvocation *context, - const char *connection_path, - const char *device_path, - const char *specific_object_path) -{ +impl_manager_activate_connection (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *dbus_connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); gs_unref_object NMActiveConnection *active = NULL; gs_unref_object NMAuthSubject *subject = NULL; @@ -4296,14 +4682,15 @@ impl_manager_activate_connection (NMManager *self, NMDevice *device = NULL; gboolean is_vpn = FALSE; GError *error = NULL; + const char *connection_path; + const char *device_path; + const char *specific_object_path; - /* Normalize object paths */ - if (g_strcmp0 (connection_path, "/") == 0) - connection_path = NULL; - if (g_strcmp0 (specific_object_path, "/") == 0) - specific_object_path = NULL; - if (g_strcmp0 (device_path, "/") == 0) - device_path = NULL; + g_variant_get (parameters, "(&o&o&o)", &connection_path, &device_path, &specific_object_path); + + connection_path = nm_utils_dbus_normalize_object_path (connection_path); + specific_object_path = nm_utils_dbus_normalize_object_path (specific_object_path); + device_path = nm_utils_dbus_normalize_object_path (device_path); /* If the connection path is given and valid, that connection is activated. * Otherwise the "best" connection for the device is chosen and activated, @@ -4338,7 +4725,7 @@ impl_manager_activate_connection (NMManager *self, } subject = validate_activation_request (self, - context, + invocation, NM_CONNECTION (connection), device_path, &device, @@ -4348,6 +4735,7 @@ impl_manager_activate_connection (NMManager *self, goto error; active = _new_active_connection (self, + is_vpn, NM_CONNECTION (connection), NULL, specific_object_path, @@ -4359,14 +4747,12 @@ impl_manager_activate_connection (NMManager *self, if (!active) goto error; - /* FIXME: nm_active_connection_authorize() is not cancellable, - * and we pass on the only reference to @active. This construct - * is unsuitable for a coordinated shutdown. */ nm_active_connection_authorize (g_steal_pointer (&active), NULL, - _activation_auth_done, - self, - context); + _async_op_complete_ac_auth_cb, + _async_op_data_new_ac_auth_activate_user (self, + active, + invocation)); return; error: @@ -4374,16 +4760,11 @@ error: nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE, NULL, subject, error->message); } - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_take_error (invocation, error); } /*****************************************************************************/ -typedef struct { - NMManager *manager; - NMActiveConnection *active; -} AddAndActivateInfo; - static void activation_add_done (NMSettings *settings, NMSettingsConnection *new_connection, @@ -4392,14 +4773,11 @@ activation_add_done (NMSettings *settings, NMAuthSubject *subject, gpointer user_data) { - AddAndActivateInfo *info = user_data; NMManager *self; gs_unref_object NMActiveConnection *active = NULL; - GError *local = NULL; + gs_free_error GError *local = NULL; - self = info->manager; - active = info->active; - g_slice_free (AddAndActivateInfo, info); + nm_utils_user_data_unpack (user_data, &self, &active); if (!error) { nm_active_connection_set_settings_connection (active, new_connection); @@ -4414,8 +4792,8 @@ activation_add_done (NMSettings *settings, g_dbus_method_invocation_return_value ( context, g_variant_new ("(oo)", - nm_connection_get_path (NM_CONNECTION (new_connection)), - nm_exported_object_get_path (NM_EXPORTED_OBJECT (active)))); + nm_dbus_object_get_path (NM_DBUS_OBJECT (new_connection)), + nm_dbus_object_get_path (NM_DBUS_OBJECT (active)))); nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE, nm_active_connection_get_settings_connection (active), TRUE, @@ -4428,6 +4806,7 @@ activation_add_done (NMSettings *settings, } nm_assert (error); + nm_active_connection_set_state_fail (active, NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN, error->message); @@ -4440,42 +4819,20 @@ activation_add_done (NMSettings *settings, NULL, nm_active_connection_get_subject (active), error->message); - g_clear_error (&local); } static void -_add_and_activate_auth_done (NMActiveConnection *active, +_add_and_activate_auth_done (NMManager *self, + NMActiveConnection *active, + NMConnection *connection, + GDBusMethodInvocation *invocation, gboolean success, - const char *error_desc, - gpointer user_data1, - gpointer user_data2) + const char *error_desc) { - NMManager *self = user_data1; - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GDBusMethodInvocation *context = user_data2; - AddAndActivateInfo *info; + NMManagerPrivate *priv; GError *error = NULL; - if (success) { - NMConnection *connection; - - connection = g_object_steal_qdata (G_OBJECT (active), - active_connection_add_and_activate_quark ()); - - info = g_slice_new (AddAndActivateInfo); - info->manager = self; - info->active = g_object_ref (active); - - /* Basic sender auth checks performed; try to add the connection */ - nm_settings_add_connection_dbus (priv->settings, - connection, - FALSE, - context, - activation_add_done, - info); - g_object_unref (connection); - } else { - g_assert (error_desc); + if (!success) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, error_desc); @@ -4485,33 +4842,50 @@ _add_and_activate_auth_done (NMActiveConnection *active, NULL, nm_active_connection_get_subject (active), error->message); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_take_error (invocation, error); + return; } - g_object_unref (active); + priv = NM_MANAGER_GET_PRIVATE (self); + + /* FIXME(shutdown): nm_settings_add_connection_dbus() cannot be cancelled. It should be made + * cancellable and tracked via AsyncOpData to be able to do a clean + * shutdown. */ + nm_settings_add_connection_dbus (priv->settings, + connection, + FALSE, + nm_active_connection_get_subject (active), + invocation, + activation_add_done, + nm_utils_user_data_pack (self, + g_object_ref (active))); } static void -impl_manager_add_and_activate_connection (NMManager *self, - GDBusMethodInvocation *context, - GVariant *settings, - const char *device_path, - const char *specific_object_path) -{ +impl_manager_add_and_activate_connection (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *dbus_connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMConnection *connection = NULL; - GSList *all_connections = NULL; + gs_unref_object NMConnection *connection = NULL; NMActiveConnection *active = NULL; - NMAuthSubject *subject = NULL; + gs_unref_object NMAuthSubject *subject = NULL; GError *error = NULL; NMDevice *device = NULL; - gboolean vpn = FALSE; + gboolean is_vpn = FALSE; + gs_unref_variant GVariant *settings = NULL; + const char *device_path; + const char *specific_object_path; + + g_variant_get (parameters, "(@a{sa{sv}}&o&o)", &settings, &device_path, &specific_object_path); - /* Normalize object paths */ - if (g_strcmp0 (specific_object_path, "/") == 0) - specific_object_path = NULL; - if (g_strcmp0 (device_path, "/") == 0) - device_path = NULL; + specific_object_path = nm_utils_dbus_normalize_object_path (specific_object_path); + device_path = nm_utils_dbus_normalize_object_path (device_path); /* Try to create a new connection with the given settings. * We allow empty settings for AddAndActivateConnection(). In that case, @@ -4525,29 +4899,16 @@ impl_manager_add_and_activate_connection (NMManager *self, _nm_connection_replace_settings (connection, settings, NM_SETTING_PARSE_FLAGS_STRICT, NULL); subject = validate_activation_request (self, - context, + invocation, connection, device_path, &device, - &vpn, + &is_vpn, &error); if (!subject) goto error; - { - gs_free NMSettingsConnection **connections = NULL; - guint i, len; - - connections = nm_settings_get_connections_clone (priv->settings, &len, - NULL, NULL, - nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL); - all_connections = NULL; - for (i = len; i > 0; ) { - i--; - all_connections = g_slist_prepend (all_connections, connections[i]); - } - } - if (vpn) { + if (is_vpn) { /* Try to fill the VPN's connection setting and name at least */ if (!nm_connection_get_setting_vpn (connection)) { error = g_error_new_literal (NM_CONNECTION_ERROR, @@ -4560,7 +4921,7 @@ impl_manager_add_and_activate_connection (NMManager *self, nm_utils_complete_generic (priv->platform, connection, NM_SETTING_VPN_SETTING_NAME, - all_connections, + (NMConnection *const*) nm_settings_get_connections (priv->settings, NULL), NULL, _("VPN connection"), NULL, @@ -4570,14 +4931,13 @@ impl_manager_add_and_activate_connection (NMManager *self, if (!nm_device_complete_connection (device, connection, specific_object_path, - all_connections, + (NMConnection *const*) nm_settings_get_connections (priv->settings, NULL), &error)) goto error; } - g_slist_free (all_connections); - all_connections = NULL; active = _new_active_connection (self, + is_vpn, connection, NULL, specific_object_path, @@ -4589,24 +4949,21 @@ impl_manager_add_and_activate_connection (NMManager *self, if (!active) goto error; - g_object_set_qdata_full (G_OBJECT (active), - active_connection_add_and_activate_quark (), - connection, - g_object_unref); + nm_active_connection_authorize (active, connection, + _async_op_complete_ac_auth_cb, + _async_op_data_new_ac_auth_add_and_activate (self, + active, + invocation, + connection)); - nm_active_connection_authorize (active, connection, _add_and_activate_auth_done, self, context); - g_object_unref (subject); + /* we passed the pointers on to _async_op_data_new_ac_auth_add_and_activate() */ + g_steal_pointer (&connection); + g_steal_pointer (&active); return; error: nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE, NULL, FALSE, NULL, subject, error->message); - g_clear_object (&connection); - g_slist_free (all_connections); - g_clear_object (&subject); - g_clear_object (&active); - - g_assert (error); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_take_error (invocation, error); } /*****************************************************************************/ @@ -4702,21 +5059,28 @@ deactivate_net_auth_done_cb (NMAuthChain *chain, else g_dbus_method_invocation_return_value (context, NULL); - nm_auth_chain_unref (chain); + nm_auth_chain_destroy (chain); } static void -impl_manager_deactivate_connection (NMManager *self, - GDBusMethodInvocation *context, - const char *active_path) -{ +impl_manager_deactivate_connection (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *dbus_connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMActiveConnection *ac; NMSettingsConnection *connection = NULL; GError *error = NULL; NMAuthSubject *subject = NULL; NMAuthChain *chain; - char *error_desc = NULL; + const char *active_path; + + g_variant_get (parameters, "(&o)", &active_path); /* Find the connection by its object path */ ac = active_connection_get_by_path (self, active_path); @@ -4731,7 +5095,7 @@ impl_manager_deactivate_connection (NMManager *self, } /* Validate the caller */ - subject = nm_auth_subject_new_unix_process_from_context (context); + subject = nm_auth_subject_new_unix_process_from_context (invocation); if (!subject) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, @@ -4739,19 +5103,15 @@ impl_manager_deactivate_connection (NMManager *self, goto done; } - /* Ensure the subject has permissions for this connection */ - if (!nm_auth_is_subject_in_acl (NM_CONNECTION (connection), - subject, - &error_desc)) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); - g_free (error_desc); + if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (connection), + subject, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + &error)) goto done; - } /* Validate the user request */ - chain = nm_auth_chain_new_subject (subject, context, deactivate_net_auth_done_cb, self); + chain = nm_auth_chain_new_subject (subject, invocation, deactivate_net_auth_done_cb, self); if (!chain) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, @@ -4769,7 +5129,7 @@ done: nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DEACTIVATE, connection, FALSE, NULL, subject, error->message); } - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_take_error (invocation, error); } g_clear_object (&subject); } @@ -4877,7 +5237,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); gboolean suspending, waking_from_suspend; - GSList *iter; + NMDevice *device; suspending = sleeping_changed && priv->sleeping; waking_from_suspend = sleeping_changed && !priv->sleeping; @@ -4888,9 +5248,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed) /* FIXME: are there still hardware devices that need to be disabled around * suspend/resume? */ - for (iter = priv->devices; iter; iter = iter->next) { - NMDevice *device = iter->data; - + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { /* FIXME: shouldn't we be unmanaging software devices if !suspending? */ if (nm_device_is_software (device)) continue; @@ -4918,9 +5276,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed) if (waking_from_suspend) { sleep_devices_clear (self); - for (iter = priv->devices; iter; iter = iter->next) { - NMDevice *device = iter->data; - + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { if (nm_device_is_software (device)) continue; @@ -4947,8 +5303,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed) nm_manager_rfkill_update (self, RFKILL_TYPE_UNKNOWN); /* Re-manage managed devices */ - for (iter = priv->devices; iter; iter = iter->next) { - NMDevice *device = NM_DEVICE (iter->data); + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { guint i; if (nm_device_is_software (device)) { @@ -5041,27 +5396,28 @@ sleep_auth_done_cb (NMAuthChain *chain, g_dbus_method_invocation_return_value (context, NULL); } - nm_auth_chain_unref (chain); + nm_auth_chain_destroy (chain); } #endif static void -impl_manager_sleep (NMManager *self, - GDBusMethodInvocation *context, - gboolean do_sleep) -{ - NMManagerPrivate *priv; +impl_manager_sleep (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GError *error = NULL; gs_unref_object NMAuthSubject *subject = NULL; -#if 0 - NMAuthChain *chain; - const char *error_desc = NULL; -#endif + gboolean do_sleep; - g_return_if_fail (NM_IS_MANAGER (self)); + g_variant_get (parameters, "(b)", &do_sleep); - priv = NM_MANAGER_GET_PRIVATE (self); - subject = nm_auth_subject_new_unix_process_from_context (context); + subject = nm_auth_subject_new_unix_process_from_context (invocation); if (priv->sleeping == do_sleep) { error = g_error_new (NM_MANAGER_ERROR, @@ -5069,7 +5425,7 @@ impl_manager_sleep (NMManager *self, "Already %s", do_sleep ? "asleep" : "awake"); nm_audit_log_control_op (NM_AUDIT_OP_SLEEP_CONTROL, do_sleep ? "on" : "off", FALSE, subject, error->message); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_take_error (invocation, error); return; } @@ -5083,22 +5439,8 @@ impl_manager_sleep (NMManager *self, */ _internal_sleep (self, do_sleep); nm_audit_log_control_op (NM_AUDIT_OP_SLEEP_CONTROL, do_sleep ? "on" : "off", TRUE, subject, NULL); - g_dbus_method_invocation_return_value (context, NULL); + g_dbus_method_invocation_return_value (invocation, NULL); return; - -#if 0 - chain = nm_auth_chain_new (context, sleep_auth_done_cb, self, &error_desc); - if (chain) { - priv->auth_chains = g_slist_append (priv->auth_chains, chain); - nm_auth_chain_set_data (chain, "sleep", GUINT_TO_POINTER (do_sleep), NULL); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, TRUE); - } else { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); - g_dbus_method_invocation_take_error (context, error); - } -#endif } static void @@ -5174,21 +5516,25 @@ enable_net_done_cb (NMAuthChain *chain, g_dbus_method_invocation_take_error (context, ret_error); } - nm_auth_chain_unref (chain); + nm_auth_chain_destroy (chain); } static void -impl_manager_enable (NMManager *self, - GDBusMethodInvocation *context, - gboolean enable) -{ - NMManagerPrivate *priv; +impl_manager_enable (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMAuthChain *chain; GError *error = NULL; + gboolean enable; - g_return_if_fail (NM_IS_MANAGER (self)); - - priv = NM_MANAGER_GET_PRIVATE (self); + g_variant_get (parameters, "(b)", &enable); if (priv->net_enabled == enable) { error = g_error_new (NM_MANAGER_ERROR, @@ -5197,7 +5543,7 @@ impl_manager_enable (NMManager *self, goto done; } - chain = nm_auth_chain_new_context (context, enable_net_done_cb, self); + chain = nm_auth_chain_new_context (invocation, enable_net_done_cb, self); if (!chain) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, @@ -5211,7 +5557,7 @@ impl_manager_enable (NMManager *self, done: if (error) - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_take_error (invocation, error); } /* Permissions */ @@ -5278,23 +5624,28 @@ get_permissions_done_cb (NMAuthChain *chain, g_variant_new ("(a{ss})", &results)); } - nm_auth_chain_unref (chain); + nm_auth_chain_destroy (chain); } static void -impl_manager_get_permissions (NMManager *self, - GDBusMethodInvocation *context) -{ +impl_manager_get_permissions (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMAuthChain *chain; - GError *error = NULL; - chain = nm_auth_chain_new_context (context, get_permissions_done_cb, self); + chain = nm_auth_chain_new_context (invocation, get_permissions_done_cb, self); if (!chain) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Unable to authenticate request."); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_return_error_literal (invocation, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Unable to authenticate request."); return; } @@ -5318,83 +5669,123 @@ impl_manager_get_permissions (NMManager *self, } static void -impl_manager_get_state (NMManager *self, - GDBusMethodInvocation *context) +impl_manager_state (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { + NMManager *self = NM_MANAGER (obj); + nm_manager_update_state (self); - g_dbus_method_invocation_return_value (context, + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", NM_MANAGER_GET_PRIVATE (self)->state)); } static void -impl_manager_set_logging (NMManager *self, - GDBusMethodInvocation *context, - const char *level, - const char *domains) -{ +impl_manager_set_logging (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); GError *error = NULL; + const char *level; + const char *domains; /* The permission is already enforced by the D-Bus daemon, but we ensure * that the caller is still alive so that clients are forced to wait and * we'll be able to switch to polkit without breaking behavior. */ - if (!nm_bus_manager_ensure_uid (nm_bus_manager_get (), - context, - G_MAXULONG, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED)) + if (!nm_dbus_manager_ensure_uid (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)), + invocation, + G_MAXULONG, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED)) return; + g_variant_get (parameters, "(&s&s)", &level, &domains); + if (nm_logging_setup (level, domains, NULL, &error)) { _LOGI (LOGD_CORE, "logging: level '%s' domains '%s'", nm_logging_level_to_string (), nm_logging_domains_to_string ()); } if (error) - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_take_error (invocation, error); else - g_dbus_method_invocation_return_value (context, NULL); + g_dbus_method_invocation_return_value (invocation, NULL); } static void -impl_manager_get_logging (NMManager *manager, - GDBusMethodInvocation *context) -{ - g_dbus_method_invocation_return_value (context, +impl_manager_get_logging (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(ss)", nm_logging_level_to_string (), nm_logging_domains_to_string ())); } typedef struct { - guint remaining; + NMManager *self; GDBusMethodInvocation *context; - NMConnectivityState state; + guint remaining; } ConnectivityCheckData; static void -device_connectivity_done (NMDevice *device, NMConnectivityState state, gpointer user_data) +device_connectivity_done (NMDevice *device, + NMDeviceConnectivityHandle *handle, + NMConnectivityState state, + GError *error, + gpointer user_data) { ConnectivityCheckData *data = user_data; + NMManager *self; + NMManagerPrivate *priv; - data->remaining--; + nm_assert (data); + nm_assert (data->remaining > 0); + nm_assert (NM_IS_MANAGER (data->self)); - /* We check if the state is already FULL so that we can provide the - * response without waiting for slower devices that are not going to - * affect the overall state anyway. */ + data->remaining--; - if (data->state != NM_CONNECTIVITY_FULL) { - if (state > data->state) - data->state = state; + self = data->self; + priv = NM_MANAGER_GET_PRIVATE (self); - if (data->state == NM_CONNECTIVITY_FULL || !data->remaining) { - g_dbus_method_invocation_return_value (data->context, - g_variant_new ("(u)", data->state)); - } + if ( data->context + && ( data->remaining == 0 + || ( state == NM_CONNECTIVITY_FULL + && priv->connectivity_state == NM_CONNECTIVITY_FULL))) { + /* despite having a @handle and @state returned by the requests, we always + * return the current connectivity_state. That is, because the connectivity_state + * and the answer to the connectivity check shall agree. + * + * However, if one of the requests (early) returns full connectivity and agrees with + * the accumulated connectivity state, we no longer have to wait. The result is set. + * + * This also works well, because NMDevice first emits change signals to its own + * connectivity state, which is then taken into account for the accumulated global + * state. All this happens, before the callback is invoked. */ + g_dbus_method_invocation_return_value (g_steal_pointer (&data->context), + g_variant_new ("(u)", + (guint) priv->connectivity_state)); } - if (!data->remaining) + if (data->remaining == 0) { + g_object_unref (self); g_slice_free (ConnectivityCheckData, data); + } } static void @@ -5408,7 +5799,7 @@ check_connectivity_auth_done_cb (NMAuthChain *chain, GError *error = NULL; NMAuthCallResult result; ConnectivityCheckData *data; - const GSList *devices; + NMDevice *device; priv->auth_chains = g_slist_remove (priv->auth_chains, chain); @@ -5424,39 +5815,59 @@ check_connectivity_auth_done_cb (NMAuthChain *chain, error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, "Not authorized to recheck connectivity"); - } else { - /* it's allowed */ - data = g_slice_new0 (ConnectivityCheckData); - data->context = context; + } - for (devices = priv->devices; devices; devices = devices->next) { + if (error) { + g_dbus_method_invocation_take_error (context, error); + goto out; + } + + data = g_slice_new (ConnectivityCheckData); + data->self = g_object_ref (self); + data->context = context; + data->remaining = 0; + + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { + if (nm_device_check_connectivity (device, + device_connectivity_done, + data)) data->remaining++; - nm_device_check_connectivity (NM_DEVICE (devices->data), - device_connectivity_done, - data); - } } - if (error) - g_dbus_method_invocation_take_error (context, error); - nm_auth_chain_unref (chain); + if (data->remaining == 0) { + /* call the handler at least once. */ + data->remaining = 1; + device_connectivity_done (NULL, + NULL, + NM_CONNECTIVITY_UNKNOWN, + NULL, + data); + /* @data got destroyed. */ + } + +out: + nm_auth_chain_destroy (chain); } static void -impl_manager_check_connectivity (NMManager *self, - GDBusMethodInvocation *context) -{ +impl_manager_check_connectivity (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMAuthChain *chain; - GError *error = NULL; - /* Validate the request */ - chain = nm_auth_chain_new_context (context, check_connectivity_auth_done_cb, self); + chain = nm_auth_chain_new_context (invocation, check_connectivity_auth_done_cb, self); if (!chain) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Unable to authenticate request."); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_return_error_literal(invocation, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Unable to authenticate request."); return; } @@ -5473,15 +5884,14 @@ start_factory (NMDeviceFactory *factory, gpointer user_data) void nm_manager_write_device_state (NMManager *self) { - const GSList *devices; NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *device; gs_unref_hashtable GHashTable *seen_ifindexes = NULL; gint nm_owned; - seen_ifindexes = g_hash_table_new (NULL, NULL); + seen_ifindexes = g_hash_table_new (nm_direct_hash, NULL); - for (devices = priv->devices; devices; devices = devices->next) { - NMDevice *device = NM_DEVICE (devices->data); + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { int ifindex; gboolean managed; NMConfigDeviceStateManagedType managed_type; @@ -5622,10 +6032,25 @@ void nm_manager_stop (NMManager *self) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *device; + + /* FIXME(shutdown): we don't do a proper shutdown yet: + * - need to ensure that all pending async operations are cancelled + * - e.g. operations in priv->async_op_lst_head + * - need to ensure that no more asynchronous requests are started, + * or that they complete quickly, or that they fail quickly. + * - note that cancelling some operations is not possible synchronously. + * Hence, stop() only prepares shutdown and tells everybody to not + * accept new work, and to complete in a timely manner. + * We need to still iterate the mainloop for a bit, to give everybody + * the chance to complete. + * - e.g. see comment at nm_auth_manager_force_shutdown() + */ + + nm_dbus_manager_stop (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self))); - /* Remove all devices */ - while (priv->devices) - remove_device (self, NM_DEVICE (priv->devices->data), TRUE, TRUE); + while ((device = c_list_first_entry (&priv->devices_lst_head, NMDevice, devices_lst))) + remove_device (self, device, TRUE, TRUE); _active_connection_cleanup (self); @@ -5637,21 +6062,20 @@ handle_firmware_changed (gpointer user_data) { NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GSList *iter; + NMDevice *device; priv->fw_changed_id = 0; /* Try to re-enable devices with missing firmware */ - for (iter = priv->devices; iter; iter = iter->next) { - NMDevice *candidate = NM_DEVICE (iter->data); - NMDeviceState state = nm_device_get_state (candidate); + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { + NMDeviceState state = nm_device_get_state (device); - if ( nm_device_get_firmware_missing (candidate) + if ( nm_device_get_firmware_missing (device) && (state == NM_DEVICE_STATE_UNAVAILABLE)) { - _LOG2I (LOGD_CORE, candidate, "firmware may now be available"); + _LOG2I (LOGD_CORE, device, "firmware may now be available"); /* Re-set unavailable state to try bringing the device up again */ - nm_device_state_changed (candidate, + nm_device_state_changed (device, NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_NONE); } @@ -5770,318 +6194,133 @@ policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer u } } -#define NM_PERM_DENIED_ERROR "org.freedesktop.NetworkManager.PermissionDenied" +/*****************************************************************************/ typedef struct { NMManager *self; - GDBusConnection *connection; - GDBusMessage *message; - NMAuthSubject *subject; - const char *permission; - const char *audit_op; - char *audit_prop_value; - GType interface_type; - const char *glib_propname; -} PropertyFilterData; + NMDBusObject *obj; + const NMDBusInterfaceInfoExtended *interface_info; + const NMDBusPropertyInfoExtended *property_info; + GVariant *value; + guint64 export_version_id; +} DBusSetPropertyHandle; -static void -free_property_filter_data (PropertyFilterData *pfd) -{ - g_object_unref (pfd->self); - g_object_unref (pfd->connection); - g_object_unref (pfd->message); - g_clear_object (&pfd->subject); - g_free (pfd->audit_prop_value); - g_slice_free (PropertyFilterData, pfd); -} +#define NM_PERM_DENIED_ERROR "org.freedesktop.NetworkManager.PermissionDenied" static void -prop_set_auth_done_cb (NMAuthChain *chain, - GError *error, - GDBusMethodInvocation *context, /* NULL */ - gpointer user_data) +_dbus_set_property_auth_cb (NMAuthChain *chain, + GError *error, + GDBusMethodInvocation *invocation, + gpointer user_data) { - PropertyFilterData *pfd = user_data; - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pfd->self); + DBusSetPropertyHandle *handle_data = user_data; + gs_unref_object NMDBusObject *obj = handle_data->obj; + const NMDBusInterfaceInfoExtended *interface_info = handle_data->interface_info; + const NMDBusPropertyInfoExtended *property_info = handle_data->property_info; + gs_unref_variant GVariant *value = handle_data->value; + guint64 export_version_id = handle_data->export_version_id; + gs_unref_object NMManager *self = handle_data->self; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMAuthCallResult result; - GDBusMessage *reply = NULL; - const char *error_message; - gs_unref_object NMExportedObject *object = NULL; - const NMGlobalDnsConfig *global_dns; - gs_unref_variant GVariant *value = NULL; - GVariant *args; + const char *error_name = NULL; + const char *error_message = NULL; + GValue gvalue; + + g_slice_free (DBusSetPropertyHandle, handle_data); priv->auth_chains = g_slist_remove (priv->auth_chains, chain); - result = nm_auth_chain_get_result (chain, pfd->permission); - if (error || (result != NM_AUTH_CALL_RESULT_YES)) { - reply = g_dbus_message_new_method_error_literal (pfd->message, - NM_PERM_DENIED_ERROR, - (error_message = "Not authorized to perform this operation")); - if (error) - error_message = error->message; - goto done; - } + result = nm_auth_chain_get_result (chain, property_info->writable.permission); - object = NM_EXPORTED_OBJECT (nm_bus_manager_get_registered_object (priv->dbus_mgr, - g_dbus_message_get_path (pfd->message))); - if (!object) { - reply = g_dbus_message_new_method_error_literal (pfd->message, - "org.freedesktop.DBus.Error.UnknownObject", - (error_message = "Object doesn't exist.")); - goto done; + if ( error + || result != NM_AUTH_CALL_RESULT_YES) { + error_name = NM_PERM_DENIED_ERROR; + error_message = error ? error->message : "Not authorized to perform this operation"; + goto out; } - /* do some extra type checking... */ - if (!nm_exported_object_get_interface_by_type (object, pfd->interface_type)) { - reply = g_dbus_message_new_method_error_literal (pfd->message, - "org.freedesktop.DBus.Error.InvalidArgs", - (error_message = "Object is of unexpected type.")); - goto done; + if (export_version_id != nm_dbus_object_get_export_version_id (obj)) { + error_name = "org.freedesktop.DBus.Error.UnknownObject"; + error_message = "Object was deleted while authenticating"; + goto out; } - args = g_dbus_message_get_body (pfd->message); - g_variant_get (args, "(&s&sv)", NULL, NULL, &value); - g_assert (pfd->glib_propname); + /* Handle some properties specially *sigh* */ + if ( interface_info == &interface_info_manager + && nm_streq (property_info->property_name, NM_MANAGER_GLOBAL_DNS_CONFIGURATION)) { + const NMGlobalDnsConfig *global_dns; - if (!strcmp (pfd->glib_propname, NM_MANAGER_GLOBAL_DNS_CONFIGURATION)) { - g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("a{sv}"))); global_dns = nm_config_data_get_global_dns_config (nm_config_get_data (priv->config)); - - if (global_dns && !nm_global_dns_config_is_internal (global_dns)) { - reply = g_dbus_message_new_method_error_literal (pfd->message, - NM_PERM_DENIED_ERROR, - (error_message = "Global DNS configuration already set via configuration file")); - goto done; + if ( global_dns + && !nm_global_dns_config_is_internal (global_dns)) { + error_name = NM_PERM_DENIED_ERROR; + error_message = "Global DNS configuration already set via configuration file"; + goto out; } - /* ... but set the property on the @object itself. It would be correct to set the property - * on the skeleton interface, but as it is now, the result is the same. */ - g_object_set (object, pfd->glib_propname, value, NULL); - } else if (!strcmp (pfd->glib_propname, NM_DEVICE_STATISTICS_REFRESH_RATE_MS)) { - g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_UINT32)); - /* the same here */ - g_object_set (object, pfd->glib_propname, (guint) g_variant_get_uint32 (value), NULL); - } else { - g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)); - /* the same here */ - g_object_set (object, pfd->glib_propname, g_variant_get_boolean (value), NULL); } - reply = g_dbus_message_new_method_reply (pfd->message); - g_dbus_message_set_body (reply, g_variant_new_tuple (NULL, 0)); - error_message = NULL; -done: - nm_audit_log_control_op (pfd->audit_op, pfd->audit_prop_value, !error_message, pfd->subject, error_message); - - g_dbus_connection_send_message (pfd->connection, reply, - G_DBUS_SEND_MESSAGE_FLAGS_NONE, - NULL, NULL); - g_object_unref (reply); - nm_auth_chain_unref (chain); + g_dbus_gvariant_to_gvalue (value, &gvalue); + g_object_set_property (G_OBJECT (obj), property_info->property_name, &gvalue); + g_value_unset (&gvalue); - free_property_filter_data (pfd); +out: + nm_audit_log_control_op (property_info->writable.audit_op, + property_info->property_name, + !error_message, + nm_auth_chain_get_subject (chain), + error_message); + if (error_message) + g_dbus_method_invocation_return_dbus_error (invocation, error_name, error_message); + else + g_dbus_method_invocation_return_value (invocation, NULL); + nm_auth_chain_destroy (chain); } -static gboolean -do_set_property_check (gpointer user_data) +void +nm_manager_dbus_set_property_handle (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusPropertyInfoExtended *property_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *value, + gpointer user_data) { - PropertyFilterData *pfd = user_data; - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pfd->self); - GDBusMessage *reply = NULL; + NMManager *self = user_data; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMAuthChain *chain; const char *error_message = NULL; + gs_unref_object NMAuthSubject *subject = NULL; + DBusSetPropertyHandle *handle_data; - pfd->subject = nm_auth_subject_new_unix_process_from_message (pfd->connection, pfd->message); - if (!pfd->subject) { - reply = g_dbus_message_new_method_error_literal (pfd->message, - NM_PERM_DENIED_ERROR, - (error_message = "Could not determine request UID.")); - goto out; + subject = nm_auth_subject_new_unix_process_from_context (invocation); + if (!subject) { + error_message = "Could not determine request UID"; + goto err; } - /* Validate the user request */ - chain = nm_auth_chain_new_subject (pfd->subject, NULL, prop_set_auth_done_cb, pfd); - if (!chain) { - reply = g_dbus_message_new_method_error_literal (pfd->message, - NM_PERM_DENIED_ERROR, - (error_message = "Could not authenticate request.")); - goto out; - } + handle_data = g_slice_new0 (DBusSetPropertyHandle); + handle_data->self = g_object_ref (self); + handle_data->obj = g_object_ref (obj); + handle_data->interface_info = interface_info; + handle_data->property_info = property_info; + handle_data->value = g_variant_ref (value); + handle_data->export_version_id = nm_dbus_object_get_export_version_id (obj); + chain = nm_auth_chain_new_subject (subject, invocation, _dbus_set_property_auth_cb, handle_data); priv->auth_chains = g_slist_append (priv->auth_chains, chain); - nm_auth_chain_add_call (chain, pfd->permission, TRUE); - -out: - if (reply) { - nm_audit_log_control_op (pfd->audit_op, pfd->audit_prop_value, FALSE, pfd->subject, error_message); - g_dbus_connection_send_message (pfd->connection, reply, - G_DBUS_SEND_MESSAGE_FLAGS_NONE, - NULL, NULL); - g_object_unref (reply); - free_property_filter_data (pfd); - } - - return FALSE; -} - -static GDBusMessage * -prop_filter (GDBusConnection *connection, - GDBusMessage *message, - gboolean incoming, - gpointer user_data) -{ - gs_unref_object NMManager *self = NULL; - GVariant *args; - const char *propiface = NULL; - const char *propname = NULL; - const char *glib_propname = NULL, *permission = NULL; - const char *audit_op = NULL; - GType interface_type = G_TYPE_INVALID; - PropertyFilterData *pfd; - const GVariantType *expected_type = G_VARIANT_TYPE_BOOLEAN; - gs_unref_variant GVariant *value = NULL; - - self = g_weak_ref_get (user_data); - if (!self) - return message; - - /* The sole purpose of this function is to validate property accesses on the - * NMManager object since gdbus doesn't give us this functionality. - */ - - /* Only filter org.freedesktop.DBus.Properties.Set calls */ - if ( !incoming - || g_dbus_message_get_message_type (message) != G_DBUS_MESSAGE_TYPE_METHOD_CALL - || g_strcmp0 (g_dbus_message_get_interface (message), DBUS_INTERFACE_PROPERTIES) != 0 - || g_strcmp0 (g_dbus_message_get_member (message), "Set") != 0) - return message; - - args = g_dbus_message_get_body (message); - if (!g_variant_is_of_type (args, G_VARIANT_TYPE ("(ssv)"))) - return message; - g_variant_get (args, "(&s&sv)", &propiface, &propname, &value); - - /* Only filter calls to filtered properties, on existing objects */ - if (!strcmp (propiface, NM_DBUS_INTERFACE)) { - if (!strcmp (propname, "WirelessEnabled")) { - glib_propname = NM_MANAGER_WIRELESS_ENABLED; - permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI; - audit_op = NM_AUDIT_OP_RADIO_CONTROL; - } else if (!strcmp (propname, "WwanEnabled")) { - glib_propname = NM_MANAGER_WWAN_ENABLED; - permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN; - audit_op = NM_AUDIT_OP_RADIO_CONTROL; - } else if (!strcmp (propname, "WimaxEnabled")) { - glib_propname = NM_MANAGER_WIMAX_ENABLED; - permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX; - audit_op = NM_AUDIT_OP_RADIO_CONTROL; - } else if (!strcmp (propname, "GlobalDnsConfiguration")) { - glib_propname = NM_MANAGER_GLOBAL_DNS_CONFIGURATION; - permission = NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS; - audit_op = NM_AUDIT_OP_NET_CONTROL; - expected_type = G_VARIANT_TYPE ("a{sv}"); - } else if (!strcmp (propname, "ConnectivityCheckEnabled")) { - glib_propname = NM_MANAGER_CONNECTIVITY_CHECK_ENABLED; - permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK; - audit_op = NM_AUDIT_OP_NET_CONTROL; - } else - return message; - interface_type = NMDBUS_TYPE_MANAGER_SKELETON; - } else if (!strcmp (propiface, NM_DBUS_INTERFACE_DEVICE)) { - if (!strcmp (propname, "Autoconnect")) { - glib_propname = NM_DEVICE_AUTOCONNECT; - permission = NM_AUTH_PERMISSION_NETWORK_CONTROL; - audit_op = NM_AUDIT_OP_DEVICE_AUTOCONNECT; - } else if (!strcmp (propname, "Managed")) { - glib_propname = NM_DEVICE_MANAGED; - permission = NM_AUTH_PERMISSION_NETWORK_CONTROL; - audit_op = NM_AUDIT_OP_DEVICE_MANAGED; - } else - return message; - interface_type = NMDBUS_TYPE_DEVICE_SKELETON; - } else if (!strcmp (propiface, NM_DBUS_INTERFACE_DEVICE_STATISTICS)) { - if (!strcmp (propname, "RefreshRateMs")) { - glib_propname = NM_DEVICE_STATISTICS_REFRESH_RATE_MS; - permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS; - audit_op = NM_AUDIT_OP_STATISTICS; - expected_type = G_VARIANT_TYPE ("u"); - } else - return message; - interface_type = NMDBUS_TYPE_DEVICE_SKELETON; - } else - return message; - - if (!g_variant_is_of_type (value, expected_type)) - return message; - - /* This filter function is called from a gdbus worker thread which we can't - * make other D-Bus calls from. In particular, we cannot call - * org.freedesktop.DBus.GetConnectionUnixUser to find the remote UID. - */ - pfd = g_slice_new0 (PropertyFilterData); - pfd->self = self; - self = NULL; - pfd->connection = g_object_ref (connection); - pfd->message = message; - pfd->permission = permission; - pfd->interface_type = interface_type; - pfd->glib_propname = glib_propname; - pfd->audit_op = audit_op; - if (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)) { - pfd->audit_prop_value = g_strdup_printf ("%s:%d", pfd->glib_propname, - g_variant_get_boolean (value)); - } else - pfd->audit_prop_value = g_strdup (pfd->glib_propname); - - g_idle_add (do_set_property_check, pfd); - - return NULL; -} - -/*****************************************************************************/ - -static int -_set_prop_filter_free2 (gpointer user_data) -{ - g_slice_free (GWeakRef, user_data); - return G_SOURCE_REMOVE; -} - -static void -_set_prop_filter_free (gpointer user_data) -{ - g_weak_ref_clear (user_data); - - /* Delay the final deletion of the user_data. There is a race when - * calling g_dbus_connection_remove_filter() that the callback and user_data - * might have been copied and being executed after the destroy function - * runs (bgo #704568). - * This doesn't really fix the race, but it should work well enough. */ - g_timeout_add_seconds (2, _set_prop_filter_free2, user_data); -} - -static void -_set_prop_filter (NMManager *self, GDBusConnection *connection) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - - nm_assert ((!priv->prop_filter.connection) == (!priv->prop_filter.id)); - - if (priv->prop_filter.connection == connection) - return; - - if (priv->prop_filter.connection) { - g_dbus_connection_remove_filter (priv->prop_filter.connection, priv->prop_filter.id); - priv->prop_filter.id = 0; - g_clear_object (&priv->prop_filter.connection); - } - if (connection) { - GWeakRef *wptr; + nm_auth_chain_add_call (chain, property_info->writable.permission, TRUE); + return; - wptr = g_slice_new (GWeakRef); - g_weak_ref_init (wptr, self); - priv->prop_filter.id = g_dbus_connection_add_filter (connection, prop_filter, wptr, _set_prop_filter_free); - priv->prop_filter.connection = g_object_ref (connection); - } +err: + nm_audit_log_control_op (property_info->writable.audit_op, + property_info->property_name, + FALSE, + invocation, + error_message); + g_dbus_method_invocation_return_error_literal (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_AUTH_FAILED, + error_message); } /*****************************************************************************/ @@ -6092,7 +6331,7 @@ _checkpoint_mgr_get (NMManager *self, gboolean create_as_needed) NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); if (G_UNLIKELY (!priv->checkpoint_mgr) && create_as_needed) - priv->checkpoint_mgr = nm_checkpoint_manager_new (self); + priv->checkpoint_mgr = nm_checkpoint_manager_new (self, obj_properties[PROP_CHECKPOINTS]); return priv->checkpoint_mgr; } @@ -6111,13 +6350,15 @@ checkpoint_auth_done_cb (NMAuthChain *chain, GVariant *variant = NULL; GError *error = NULL; const char *arg = NULL; + guint32 add_timeout; op = nm_auth_chain_get_data (chain, "audit-op"); priv->auth_chains = g_slist_remove (priv->auth_chains, chain); result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK); - if ( nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_DESTROY) - || nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ROLLBACK)) + if (NM_IN_STRSET (op, NM_AUDIT_OP_CHECKPOINT_DESTROY, + NM_AUDIT_OP_CHECKPOINT_ROLLBACK, + NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT)) arg = checkpoint_path = nm_auth_chain_get_data (chain, "checkpoint_path"); if (auth_error) { @@ -6141,7 +6382,7 @@ checkpoint_auth_done_cb (NMAuthChain *chain, (NMCheckpointCreateFlags) flags, &error); if (checkpoint) { - arg = nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint)); + arg = nm_dbus_object_get_path (NM_DBUS_OBJECT (checkpoint)); variant = g_variant_new ("(o)", arg); } } else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_DESTROY)) { @@ -6150,6 +6391,10 @@ checkpoint_auth_done_cb (NMAuthChain *chain, } else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ROLLBACK)) { nm_checkpoint_manager_rollback (_checkpoint_mgr_get (self, TRUE), checkpoint_path, &variant, &error); + } else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT)) { + add_timeout = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "add_timeout")); + nm_checkpoint_manager_adjust_rollback_timeout (_checkpoint_mgr_get (self, TRUE), + checkpoint_path, add_timeout, &error); } else g_return_if_reached (); } @@ -6162,63 +6407,71 @@ checkpoint_auth_done_cb (NMAuthChain *chain, else g_dbus_method_invocation_return_value (context, variant); - - nm_auth_chain_unref (chain); + nm_auth_chain_destroy (chain); } static void -impl_manager_checkpoint_create (NMManager *self, - GDBusMethodInvocation *context, - const char *const *devices, - guint32 rollback_timeout, - guint32 flags) -{ - NMManagerPrivate *priv; +impl_manager_checkpoint_create (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMAuthChain *chain; - GError *error = NULL; + char **devices; + guint32 rollback_timeout; + guint32 flags; G_STATIC_ASSERT_EXPR (sizeof (flags) <= sizeof (NMCheckpointCreateFlags)); - g_return_if_fail (NM_IS_MANAGER (self)); - priv = NM_MANAGER_GET_PRIVATE (self); - chain = nm_auth_chain_new_context (context, checkpoint_auth_done_cb, self); + chain = nm_auth_chain_new_context (invocation, checkpoint_auth_done_cb, self); if (!chain) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Unable to authenticate request."); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_return_error_literal (invocation, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Unable to authenticate request."); return; } + g_variant_get (parameters, "(^aouu)", &devices, &rollback_timeout, &flags); + priv->auth_chains = g_slist_append (priv->auth_chains, chain); nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_CREATE, NULL); - nm_auth_chain_set_data (chain, "devices", g_strdupv ((char **) devices), (GDestroyNotify) g_strfreev); + nm_auth_chain_set_data (chain, "devices", devices, (GDestroyNotify) g_strfreev); nm_auth_chain_set_data (chain, "flags", GUINT_TO_POINTER (flags), NULL); nm_auth_chain_set_data (chain, "timeout", GUINT_TO_POINTER (rollback_timeout), NULL); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE); } static void -impl_manager_checkpoint_destroy (NMManager *self, - GDBusMethodInvocation *context, - const char *checkpoint_path) -{ - NMManagerPrivate *priv; - GError *error = NULL; +impl_manager_checkpoint_destroy (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMAuthChain *chain; + const char *checkpoint_path; - g_return_if_fail (NM_IS_MANAGER (self)); - priv = NM_MANAGER_GET_PRIVATE (self); - - chain = nm_auth_chain_new_context (context, checkpoint_auth_done_cb, self); + chain = nm_auth_chain_new_context (invocation, checkpoint_auth_done_cb, self); if (!chain) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Unable to authenticate request."); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_return_error_literal (invocation, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Unable to authenticate request."); return; } + g_variant_get (parameters, "(&o)", &checkpoint_path); + priv->auth_chains = g_slist_append (priv->auth_chains, chain); nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_DESTROY, NULL); nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free); @@ -6226,39 +6479,79 @@ impl_manager_checkpoint_destroy (NMManager *self, } static void -impl_manager_checkpoint_rollback (NMManager *self, - GDBusMethodInvocation *context, - const char *checkpoint_path) -{ - NMManagerPrivate *priv; - GError *error = NULL; +impl_manager_checkpoint_rollback (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMAuthChain *chain; + const char *checkpoint_path; - g_return_if_fail (NM_IS_MANAGER (self)); - priv = NM_MANAGER_GET_PRIVATE (self); - - chain = nm_auth_chain_new_context (context, checkpoint_auth_done_cb, self); + chain = nm_auth_chain_new_context (invocation, checkpoint_auth_done_cb, self); if (!chain) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Unable to authenticate request."); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_return_error_literal (invocation, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Unable to authenticate request."); return; } + g_variant_get (parameters, "(&o)", &checkpoint_path); + priv->auth_chains = g_slist_append (priv->auth_chains, chain); nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_ROLLBACK, NULL); nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE); } +static void +impl_manager_checkpoint_adjust_rollback_timeout (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMManager *self = NM_MANAGER (obj); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMAuthChain *chain; + const char *checkpoint_path; + guint32 add_timeout; + + chain = nm_auth_chain_new_context (invocation, checkpoint_auth_done_cb, self); + if (!chain) { + g_dbus_method_invocation_return_error_literal (invocation, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Unable to authenticate request."); + return; + } + + g_variant_get (parameters, "(&ou)", &checkpoint_path, &add_timeout); + + priv->auth_chains = g_slist_append (priv->auth_chains, chain); + nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT, NULL); + nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free); + nm_auth_chain_set_data (chain, "add_timeout", GUINT_TO_POINTER (add_timeout), NULL); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE); +} + /*****************************************************************************/ static void auth_mgr_changed (NMAuthManager *auth_manager, gpointer user_data) { /* Let clients know they should re-check their authorization */ - g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0); + nm_dbus_object_emit_signal (user_data, + &interface_info_manager, + &signal_info_check_permissions, + "()"); } #define KERN_RFKILL_OP_CHANGE_ALL 3 @@ -6383,14 +6676,6 @@ periodic_update_active_connection_timestamps (gpointer user_data) return G_SOURCE_CONTINUE; } -static void -dbus_connection_changed_cb (NMBusManager *dbus_mgr, - GDBusConnection *connection, - gpointer user_data) -{ - _set_prop_filter (NM_MANAGER (user_data), connection); -} - /*****************************************************************************/ void @@ -6457,8 +6742,7 @@ nm_manager_setup (void) nm_singleton_instance_register (); _LOGD (LOGD_CORE, "setup %s singleton (%p)", "NMManager", singleton_instance); - nm_exported_object_export ((NMExportedObject *) self); - + nm_dbus_object_export (NM_DBUS_OBJECT (self)); return self; } @@ -6471,11 +6755,9 @@ constructed (GObject *object) G_OBJECT_CLASS (nm_manager_parent_class)->constructed (object); - _set_prop_filter (self, nm_bus_manager_get_connection (priv->dbus_mgr)); - priv->settings = nm_settings_new (); - nm_exported_object_export (NM_EXPORTED_OBJECT (priv->settings)); + nm_dbus_object_export (NM_DBUS_OBJECT (priv->settings)); g_signal_connect (priv->settings, "notify::" NM_SETTINGS_STARTUP_COMPLETE, G_CALLBACK (settings_startup_complete_changed), self); @@ -6543,7 +6825,9 @@ nm_manager_init (NMManager *self) GFile *file; c_list_init (&priv->link_cb_lst); + c_list_init (&priv->devices_lst_head); c_list_init (&priv->active_connections_lst_head); + c_list_init (&priv->async_op_lst_head); c_list_init (&priv->delete_volatile_connection_lst_head); priv->platform = g_object_ref (NM_PLATFORM_GET); @@ -6574,12 +6858,6 @@ nm_manager_init (NMManager *self) priv->state = NM_STATE_DISCONNECTED; priv->startup = TRUE; - priv->dbus_mgr = g_object_ref (nm_bus_manager_get ()); - g_signal_connect (priv->dbus_mgr, - NM_BUS_MANAGER_DBUS_CONNECTION_CHANGED, - G_CALLBACK (dbus_connection_changed_cb), - self); - /* sleep/wake handling */ priv->sleep_monitor = nm_sleep_monitor_new (); g_signal_connect (priv->sleep_monitor, NM_SLEEP_MONITOR_SLEEPING, @@ -6614,13 +6892,7 @@ nm_manager_init (NMManager *self) priv->timestamp_update_id = g_timeout_add_seconds (300, (GSourceFunc) periodic_update_active_connection_timestamps, self); priv->metered = NM_METERED_UNKNOWN; - priv->sleep_devices = g_hash_table_new (g_direct_hash, g_direct_equal); -} - -static gboolean -device_is_real (GObject *device, gpointer user_data) -{ - return nm_device_is_real (NM_DEVICE (device)); + priv->sleep_devices = g_hash_table_new (nm_direct_hash, NULL); } static void @@ -6635,7 +6907,6 @@ get_property (GObject *object, guint prop_id, const char *path; NMActiveConnection *ac; GPtrArray *ptrarr; - gboolean vbool; switch (prop_id) { case PROP_VERSION: @@ -6677,7 +6948,7 @@ get_property (GObject *object, guint prop_id, case PROP_ACTIVE_CONNECTIONS: ptrarr = g_ptr_array_new (); c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) { - path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (ac)); + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (ac)); if (path) g_ptr_array_add (ptrarr, g_strdup (path)); } @@ -6692,15 +6963,10 @@ get_property (GObject *object, guint prop_id, g_value_set_boolean (value, nm_config_data_get_connectivity_uri (config_data) != NULL); break; case PROP_CONNECTIVITY_CHECK_ENABLED: -#if WITH_CONCHECK - vbool = nm_connectivity_check_enabled (nm_connectivity_get ()); -#else - vbool = FALSE; -#endif - g_value_set_boolean (value, vbool); + g_value_set_boolean (value, concheck_enabled (self, NULL)); break; case PROP_PRIMARY_CONNECTION: - nm_utils_g_value_set_object_path (value, priv->primary_connection); + nm_dbus_utils_g_value_set_object_path (value, priv->primary_connection); break; case PROP_PRIMARY_CONNECTION_TYPE: type = NULL; @@ -6711,16 +6977,18 @@ get_property (GObject *object, guint prop_id, if (con) type = nm_connection_get_connection_type (con); } - g_value_set_string (value, type ? type : ""); + g_value_set_string (value, type ?: ""); break; case PROP_ACTIVATING_CONNECTION: - nm_utils_g_value_set_object_path (value, priv->activating_connection); + nm_dbus_utils_g_value_set_object_path (value, priv->activating_connection); break; case PROP_SLEEPING: g_value_set_boolean (value, priv->sleeping); break; case PROP_DEVICES: - nm_utils_g_value_set_object_path_array (value, priv->devices, device_is_real, NULL); + g_value_take_boxed (value, + nm_utils_strv_make_deep_copied (_get_devices_paths (self, + FALSE))); break; case PROP_METERED: g_value_set_uint (value, priv->metered); @@ -6731,7 +6999,16 @@ get_property (GObject *object, guint prop_id, nm_global_dns_config_to_dbus (dns_config, value); break; case PROP_ALL_DEVICES: - nm_utils_g_value_set_object_path_array (value, priv->devices, NULL, NULL); + g_value_take_boxed (value, + nm_utils_strv_make_deep_copied (_get_devices_paths (self, + TRUE))); + break; + case PROP_CHECKPOINTS: + g_value_take_boxed (value, + priv->checkpoint_mgr + ? nm_utils_strv_make_deep_copied (nm_checkpoint_manager_get_checkpoint_paths (priv->checkpoint_mgr, + NULL)) + : NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -6798,10 +7075,7 @@ dispose (GObject *object) CList *iter, *iter_safe; NMActiveConnection *ac, *ac_safe; - nm_clear_g_source (&priv->delete_volatile_connection_idle_id); - _delete_volatile_connection_all (self, FALSE); - nm_assert (!priv->delete_volatile_connection_idle_id); - nm_assert (c_list_is_empty (&priv->delete_volatile_connection_lst_head)); + nm_assert (c_list_is_empty (&priv->async_op_lst_head)); g_signal_handlers_disconnect_by_func (priv->platform, G_CALLBACK (platform_link_cb), @@ -6814,14 +7088,18 @@ dispose (GObject *object) g_slice_free (PlatformLinkCbData, data); } - g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_unref); + g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_destroy); priv->auth_chains = NULL; nm_clear_g_source (&priv->devices_inited_id); - if (priv->checkpoint_mgr) { - nm_checkpoint_manager_destroy_all (priv->checkpoint_mgr, NULL); - g_clear_pointer (&priv->checkpoint_mgr, nm_checkpoint_manager_unref); + g_clear_pointer (&priv->checkpoint_mgr, nm_checkpoint_manager_free); + + if (priv->concheck_mgr) { + g_signal_handlers_disconnect_by_func (priv->concheck_mgr, + G_CALLBACK (concheck_config_changed_cb), + self); + g_clear_object (&priv->concheck_mgr); } if (priv->auth_mgr) { @@ -6831,7 +7109,7 @@ dispose (GObject *object) g_clear_object (&priv->auth_mgr); } - g_assert (priv->devices == NULL); + nm_assert (c_list_is_empty (&priv->devices_lst_head)); nm_clear_g_source (&priv->ac_cleanup_id); @@ -6868,13 +7146,6 @@ dispose (GObject *object) g_clear_object (&priv->vpn_manager); - /* Unregister property filter */ - if (priv->dbus_mgr) { - g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, self); - g_clear_object (&priv->dbus_mgr); - } - _set_prop_filter (self, NULL); - sleep_devices_clear (self); g_clear_pointer (&priv->sleep_devices, g_hash_table_unref); @@ -6897,6 +7168,11 @@ dispose (GObject *object) g_clear_object (&priv->rfkill_mgr); } + nm_clear_g_source (&priv->delete_volatile_connection_idle_id); + _delete_volatile_connection_all (self, FALSE); + nm_assert (!priv->delete_volatile_connection_idle_id); + nm_assert (c_list_is_empty (&priv->delete_volatile_connection_lst_head)); + nm_device_factory_manager_for_each_factory (_deinit_device_factory, self); nm_clear_g_source (&priv->timestamp_update_id); @@ -6918,22 +7194,274 @@ finalize (GObject *object) g_object_unref (priv->platform); } +static const GDBusSignalInfo signal_info_check_permissions = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "CheckPermissions", +); + +static const GDBusSignalInfo signal_info_state_changed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "StateChanged", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("state", "u"), + ), +); + +static const GDBusSignalInfo signal_info_device_added = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "DeviceAdded", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("device_path", "o"), + ), +); + +static const GDBusSignalInfo signal_info_device_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "DeviceRemoved", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("device_path", "o"), + ), +); + +static const NMDBusInterfaceInfoExtended interface_info_manager = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "Reload", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("flags", "u"), + ), + ), + .handle = impl_manager_reload, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "GetDevices", + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("devices", "ao"), + ), + ), + .handle = impl_manager_get_devices, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "GetAllDevices", + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("devices", "ao"), + ), + ), + .handle = impl_manager_get_all_devices, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "GetDeviceByIpIface", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("iface", "s"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("device", "o"), + ), + ), + .handle = impl_manager_get_device_by_ip_iface, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "ActivateConnection", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("connection", "o"), + NM_DEFINE_GDBUS_ARG_INFO ("device", "o"), + NM_DEFINE_GDBUS_ARG_INFO ("specific_object", "o"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("active_connection", "o"), + ), + ), + .handle = impl_manager_activate_connection, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "AddAndActivateConnection", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO ("device", "o"), + NM_DEFINE_GDBUS_ARG_INFO ("specific_object", "o"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("path", "o"), + NM_DEFINE_GDBUS_ARG_INFO ("active_connection", "o"), + ), + ), + .handle = impl_manager_add_and_activate_connection, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "DeactivateConnection", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("active_connection", "o"), + ), + ), + .handle = impl_manager_deactivate_connection, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "Sleep", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("sleep", "b"), + ), + ), + .handle = impl_manager_sleep, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "Enable", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("enable", "b"), + ), + ), + .handle = impl_manager_enable, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "GetPermissions", + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("permissions", "a{ss}"), + ), + ), + .handle = impl_manager_get_permissions, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "SetLogging", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("level", "s"), + NM_DEFINE_GDBUS_ARG_INFO ("domains", "s"), + ), + ), + .handle = impl_manager_set_logging, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "GetLogging", + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("level", "s"), + NM_DEFINE_GDBUS_ARG_INFO ("domains", "s"), + ), + ), + .handle = impl_manager_get_logging, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "CheckConnectivity", + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("connectivity", "u"), + ), + ), + .handle = impl_manager_check_connectivity, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "state", + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("state", "u"), + ), + ), + .handle = impl_manager_state, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "CheckpointCreate", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("devices", "ao"), + NM_DEFINE_GDBUS_ARG_INFO ("rollback_timeout", "u"), + NM_DEFINE_GDBUS_ARG_INFO ("flags", "u"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("checkpoint", "o"), + ), + ), + .handle = impl_manager_checkpoint_create, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "CheckpointDestroy", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("checkpoint", "o"), + ), + ), + .handle = impl_manager_checkpoint_destroy, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "CheckpointRollback", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("checkpoint", "o"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("result", "a{su}"), + ), + ), + .handle = impl_manager_checkpoint_rollback, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "CheckpointAdjustRollbackTimeout", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("checkpoint", "o"), + NM_DEFINE_GDBUS_ARG_INFO ("add_timeout", "u"), + ), + ), + .handle = impl_manager_checkpoint_adjust_rollback_timeout, + ), + ), + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + &signal_info_check_permissions, + &signal_info_state_changed, + &signal_info_device_added, + &signal_info_device_removed, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Devices", "ao", NM_MANAGER_DEVICES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("AllDevices", "ao", NM_MANAGER_ALL_DEVICES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Checkpoints", "ao", NM_MANAGER_CHECKPOINTS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("NetworkingEnabled", "b", NM_MANAGER_NETWORKING_ENABLED), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("WirelessEnabled", "b", NM_MANAGER_WIRELESS_ENABLED, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI, NM_AUDIT_OP_RADIO_CONTROL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("WirelessHardwareEnabled", "b", NM_MANAGER_WIRELESS_HARDWARE_ENABLED), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("WwanEnabled", "b", NM_MANAGER_WWAN_ENABLED, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN, NM_AUDIT_OP_RADIO_CONTROL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("WwanHardwareEnabled", "b", NM_MANAGER_WWAN_HARDWARE_ENABLED), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("WimaxEnabled", "b", NM_MANAGER_WIMAX_ENABLED, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX, NM_AUDIT_OP_RADIO_CONTROL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("WimaxHardwareEnabled", "b", NM_MANAGER_WIMAX_HARDWARE_ENABLED), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ActiveConnections", "ao", NM_MANAGER_ACTIVE_CONNECTIONS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("PrimaryConnection", "o", NM_MANAGER_PRIMARY_CONNECTION), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("PrimartConnectionType", "s", NM_MANAGER_PRIMARY_CONNECTION_TYPE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Metered", "u", NM_MANAGER_METERED), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ActivatingConnection", "o", NM_MANAGER_ACTIVATING_CONNECTION), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Startup", "b", NM_MANAGER_STARTUP), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Version", "s", NM_MANAGER_VERSION), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Capabilities", "u", NM_MANAGER_CAPABILITIES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("State", "u", NM_MANAGER_STATE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Connectivity", "u", NM_MANAGER_CONNECTIVITY), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ConnectivityCheckAvailable", "b", NM_MANAGER_CONNECTIVITY_CHECK_AVAILABLE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("ConnectivityCheckEnabled", "b", NM_MANAGER_CONNECTIVITY_CHECK_ENABLED, NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK, NM_AUDIT_OP_NET_CONTROL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("GlobalDnsConfiguration", "a{sv}", NM_MANAGER_GLOBAL_DNS_CONFIGURATION, NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS, NM_AUDIT_OP_NET_CONTROL), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_manager_class_init (NMManagerClass *manager_class) { GObjectClass *object_class = G_OBJECT_CLASS (manager_class); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (manager_class); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (manager_class); - exported_object_class->export_path = NM_DBUS_PATH; + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_STATIC (NM_DBUS_PATH); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_manager); - /* virtual methods */ object_class->constructed = constructed; object_class->set_property = set_property; object_class->get_property = get_property; object_class->dispose = dispose; object_class->finalize = finalize; - /* properties */ obj_properties[PROP_VERSION] = g_param_spec_string (NM_MANAGER_VERSION, "", "", NULL, @@ -6941,11 +7469,11 @@ nm_manager_class_init (NMManagerClass *manager_class) G_PARAM_STATIC_STRINGS); obj_properties[PROP_CAPABILITIES] = - g_param_spec_variant (NM_MANAGER_CAPABILITIES, "", "", - G_VARIANT_TYPE ("au"), - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); + g_param_spec_variant (NM_MANAGER_CAPABILITIES, "", "", + G_VARIANT_TYPE ("au"), + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); obj_properties[PROP_STATE] = g_param_spec_uint (NM_MANAGER_STATE, "", "", @@ -7096,11 +7624,17 @@ nm_manager_class_init (NMManagerClass *manager_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_CHECKPOINTS] = + g_param_spec_boxed (NM_MANAGER_CHECKPOINTS, "", "", + G_TYPE_STRV, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); /* signals */ - /* D-Bus exported; emitted only for realized devices */ + /* emitted only for realized devices */ signals[DEVICE_ADDED] = g_signal_new (NM_MANAGER_DEVICE_ADDED, G_OBJECT_CLASS_TYPE (object_class), @@ -7116,7 +7650,7 @@ nm_manager_class_init (NMManagerClass *manager_class) NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); - /* D-Bus exported; emitted only for realized devices when a device + /* emitted only for realized devices when a device * becomes unrealized or removed */ signals[DEVICE_REMOVED] = g_signal_new (NM_MANAGER_DEVICE_REMOVED, @@ -7133,20 +7667,6 @@ nm_manager_class_init (NMManagerClass *manager_class) NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); - signals[STATE_CHANGED] = - g_signal_new (NM_MANAGER_STATE_CHANGED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 1, G_TYPE_UINT); - - signals[CHECK_PERMISSIONS] = - g_signal_new (NM_MANAGER_CHECK_PERMISSIONS, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); - signals[ACTIVE_CONNECTION_ADDED] = g_signal_new (NM_MANAGER_ACTIVE_CONNECTION_ADDED, G_OBJECT_CLASS_TYPE (object_class), @@ -7167,25 +7687,4 @@ nm_manager_class_init (NMManagerClass *manager_class) G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (manager_class), - NMDBUS_TYPE_MANAGER_SKELETON, - "Reload", impl_manager_reload, - "GetDevices", impl_manager_get_devices, - "GetAllDevices", impl_manager_get_all_devices, - "GetDeviceByIpIface", impl_manager_get_device_by_ip_iface, - "ActivateConnection", impl_manager_activate_connection, - "AddAndActivateConnection", impl_manager_add_and_activate_connection, - "DeactivateConnection", impl_manager_deactivate_connection, - "Sleep", impl_manager_sleep, - "Enable", impl_manager_enable, - "GetPermissions", impl_manager_get_permissions, - "SetLogging", impl_manager_set_logging, - "GetLogging", impl_manager_get_logging, - "CheckConnectivity", impl_manager_check_connectivity, - "state", impl_manager_get_state, - "CheckpointCreate", impl_manager_checkpoint_create, - "CheckpointDestroy", impl_manager_checkpoint_destroy, - "CheckpointRollback", impl_manager_checkpoint_rollback, - NULL); } diff --git a/src/nm-manager.h b/src/nm-manager.h index da838532..5a462846 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -22,9 +22,9 @@ #ifndef __NETWORKMANAGER_MANAGER_H__ #define __NETWORKMANAGER_MANAGER_H__ -#include "nm-exported-object.h" #include "settings/nm-settings-connection.h" -#include "nm-utils/c-list.h" +#include "c-list/src/c-list.h" +#include "nm-dbus-manager.h" #define NM_TYPE_MANAGER (nm_manager_get_type ()) #define NM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MANAGER, NMManager)) @@ -55,25 +55,22 @@ #define NM_MANAGER_METERED "metered" #define NM_MANAGER_GLOBAL_DNS_CONFIGURATION "global-dns-configuration" #define NM_MANAGER_ALL_DEVICES "all-devices" +#define NM_MANAGER_CHECKPOINTS "checkpoints" /* Not exported */ #define NM_MANAGER_SLEEPING "sleeping" -/* signals */ -#define NM_MANAGER_CHECK_PERMISSIONS "check-permissions" +/* Signals */ #define NM_MANAGER_DEVICE_ADDED "device-added" #define NM_MANAGER_DEVICE_REMOVED "device-removed" -#define NM_MANAGER_STATE_CHANGED "state-changed" #define NM_MANAGER_USER_PERMISSIONS_CHANGED "user-permissions-changed" -/* Internal signals */ #define NM_MANAGER_ACTIVE_CONNECTION_ADDED "active-connection-added" #define NM_MANAGER_ACTIVE_CONNECTION_REMOVED "active-connection-removed" #define NM_MANAGER_CONFIGURE_QUIT "configure-quit" #define NM_MANAGER_INTERNAL_DEVICE_ADDED "internal-device-added" #define NM_MANAGER_INTERNAL_DEVICE_REMOVED "internal-device-removed" - GType nm_manager_get_type (void); /* nm_manager_setup() should only be used by main.c */ @@ -85,13 +82,14 @@ gboolean nm_manager_start (NMManager *manager, GError **error); void nm_manager_stop (NMManager *manager); NMState nm_manager_get_state (NMManager *manager); + const CList * nm_manager_get_active_connections (NMManager *manager); #define nm_manager_for_each_active_connection(manager, iter, tmp_list) \ for (tmp_list = nm_manager_get_active_connections (manager), \ iter = c_list_entry (tmp_list->next, NMActiveConnection, active_connections_lst); \ ({ \ - gboolean _has_next = (&iter->active_connections_lst != tmp_list); \ + const gboolean _has_next = (&iter->active_connections_lst != tmp_list); \ \ if (!_has_next) \ iter = NULL; \ @@ -107,7 +105,19 @@ void nm_manager_write_device_state (NMManager *manager); /* Device handling */ -const GSList * nm_manager_get_devices (NMManager *manager); +const CList * nm_manager_get_devices (NMManager *manager); + +#define nm_manager_for_each_device(manager, iter, tmp_list) \ + for (tmp_list = nm_manager_get_devices (manager), \ + iter = c_list_entry (tmp_list->next, NMDevice, devices_lst); \ + ({ \ + const gboolean _has_next = (&iter->devices_lst != tmp_list); \ + \ + if (!_has_next) \ + iter = NULL; \ + _has_next; \ + }); \ + iter = c_list_entry (iter->devices_lst.next, NMDevice, devices_lst)) NMDevice * nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex); @@ -153,4 +163,13 @@ gboolean nm_manager_remove_device (NMManager *self, const char *ifname, NMDeviceType device_type); +void nm_manager_dbus_set_property_handle (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusPropertyInfoExtended *property_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *value, + gpointer user_data); + #endif /* __NETWORKMANAGER_MANAGER_H__ */ diff --git a/src/nm-netns.c b/src/nm-netns.c index 96ab2b35..5952a490 100644 --- a/src/nm-netns.c +++ b/src/nm-netns.c @@ -38,7 +38,6 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( typedef struct { NMPlatform *platform; NMPNetns *platform_netns; - bool log_with_ptr; } NMNetnsPrivate; struct _NMNetns { @@ -113,13 +112,10 @@ constructed (GObject *object) { NMNetns *self = NM_NETNS (object); NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE (self); - gboolean log_with_ptr; if (!priv->platform) g_return_if_reached (); - log_with_ptr = nm_platform_get_log_with_ptr (priv->platform); - priv->platform_netns = nm_platform_netns_get (priv->platform); G_OBJECT_CLASS (nm_netns_parent_class)->constructed (object); diff --git a/src/nm-pacrunner-manager.c b/src/nm-pacrunner-manager.c index 36f517a2..af43edbe 100644 --- a/src/nm-pacrunner-manager.c +++ b/src/nm-pacrunner-manager.c @@ -27,7 +27,7 @@ #include "nm-proxy-config.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" -#include "nm-utils/c-list.h" +#include "c-list/src/c-list.h" #define PACRUNNER_DBUS_SERVICE "org.pacrunner" #define PACRUNNER_DBUS_INTERFACE "org.pacrunner.Manager" @@ -421,7 +421,6 @@ nm_pacrunner_manager_send (NMPacrunnerManager *self, g_variant_new_string ("direct")); } - /* Extract stuff from configs */ add_proxy_config (&proxy_data, proxy_config); @@ -466,19 +465,18 @@ static void pacrunner_remove_done (GObject *source, GAsyncResult *res, gpointer user_data) { Config *config = user_data; - NMPacrunnerManager *self; gs_free_error GError *error = NULL; gs_unref_variant GVariant *ret = NULL; ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + if (!ret) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + goto out; + _LOG2D (config, "remove failed: %s", error->message); goto out; + } - self = NM_PACRUNNER_MANAGER (config->manager_maybe_dangling); - if (!ret) - _LOG2D (config, "remove failed: %s", error->message); - else - _LOG2D (config, "removed"); + _LOG2D (config, "removed"); out: config_unref (config); diff --git a/src/nm-policy.c b/src/nm-policy.c index 2bf25d50..fe16cda5 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -337,7 +337,7 @@ device_ip6_prefix_delegated (NMDevice *device, /* Allocate a delegation delegation for new prefix. */ g_array_set_size (priv->ip6_prefix_delegations, i + 1); delegation = &g_array_index (priv->ip6_prefix_delegations, IP6PrefixDelegation, i); - delegation->subnets = g_hash_table_new (NULL, NULL); + delegation->subnets = g_hash_table_new (nm_direct_hash, NULL); delegation->next_subnet = 0; } @@ -385,7 +385,8 @@ get_best_ip_device (NMPolicy *self, gboolean fully_activated) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - const GSList *iter; + const CList *tmp_lst; + NMDevice *device; NMDevice *best_device; NMDevice *prev_device; guint32 best_metric = G_MAXUINT32; @@ -400,8 +401,7 @@ get_best_ip_device (NMPolicy *self, ? (fully_activated ? priv->default_device4 : priv->activating_device4) : (fully_activated ? priv->default_device6 : priv->activating_device6); - for (iter = nm_manager_get_devices (priv->manager); iter; iter = iter->next) { - NMDevice *device = NM_DEVICE (iter->data); + nm_manager_for_each_device (priv->manager, device, tmp_lst) { NMDeviceState state; const NMPObject *r; NMConnection *connection; @@ -418,7 +418,7 @@ get_best_ip_device (NMPolicy *self, r = nm_device_get_best_default_route (device, addr_family); if (r) { - /* XXX: the best route might have rt_source NM_IP_CONFIG_SOURCE_VPN, + /* NOTE: the best route might have rt_source NM_IP_CONFIG_SOURCE_VPN, * which means it was injected by a VPN, not added by device. * * In this case, is it really the best device? Why do we even need the best @@ -461,15 +461,15 @@ static gboolean all_devices_not_active (NMPolicy *self) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - const GSList *iter = nm_manager_get_devices (priv->manager); + const CList *tmp_lst; + NMDevice *device; - while (iter != NULL) { + nm_manager_for_each_device (priv->manager, device, tmp_lst) { NMDeviceState state; - state = nm_device_get_state (NM_DEVICE (iter->data)); + state = nm_device_get_state (device); if ( state <= NM_DEVICE_STATE_DISCONNECTED || state >= NM_DEVICE_STATE_DEACTIVATING) { - iter = g_slist_next (iter); continue; } return FALSE; @@ -1065,22 +1065,19 @@ update_ip_dns (NMPolicy *self, int addr_family) gpointer ip_config; const char *ip_iface = NULL; NMVpnConnection *vpn = NULL; - NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE; nm_assert_addr_family (addr_family); ip_config = get_best_ip_config (self, addr_family, &ip_iface, NULL, NULL, &vpn); if (ip_config) { - if (vpn) - dns_type = NM_DNS_IP_CONFIG_TYPE_VPN; - /* Tell the DNS manager this config is preferred by re-adding it with * a different IP config type. */ - nm_dns_manager_add_ip_config (NM_POLICY_GET_PRIVATE (self)->dns_manager, - ip_iface, + nm_dns_manager_set_ip_config (NM_POLICY_GET_PRIVATE (self)->dns_manager, ip_config, - dns_type); + vpn + ? NM_DNS_IP_CONFIG_TYPE_VPN + : NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE); } if (addr_family == AF_INET6) @@ -1172,9 +1169,11 @@ pending_ac_state_changed (NMActiveConnection *ac, guint state, guint reason, NMP * device, but block the current connection to avoid an activation * loop. */ - con = nm_active_connection_get_settings_connection (ac); - nm_settings_connection_autoconnect_blocked_reason_set (con, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED, TRUE); - schedule_activate_check (self, nm_active_connection_get_device (ac)); + if (reason != NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED) { + con = nm_active_connection_get_settings_connection (ac); + nm_settings_connection_autoconnect_blocked_reason_set (con, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED, TRUE); + schedule_activate_check (self, nm_active_connection_get_device (ac)); + } /* Cleanup */ g_signal_handlers_disconnect_by_func (ac, pending_ac_state_changed, self); @@ -1195,7 +1194,7 @@ auto_activate_device (NMPolicy *self, gs_free NMSettingsConnection **connections = NULL; guint i, len; gs_free_error GError *error = NULL; - NMAuthSubject *subject; + gs_unref_object NMAuthSubject *subject = NULL; NMActiveConnection *ac; nm_assert (NM_IS_POLICY (self)); @@ -1272,13 +1271,11 @@ auto_activate_device (NMPolicy *self, * activation fails in early stages without changing device * state. */ - if (nm_g_hash_table_add (priv->pending_active_connections, ac)) { + if (g_hash_table_add (priv->pending_active_connections, ac)) { g_signal_connect (ac, NM_ACTIVE_CONNECTION_STATE_CHANGED, G_CALLBACK (pending_ac_state_changed), g_object_ref (self)); g_object_weak_ref (G_OBJECT (ac), (GWeakNotify) pending_ac_gone, self); } - - g_object_unref (subject); } static gboolean @@ -1362,7 +1359,7 @@ process_secondaries (NMPolicy *self, if (connected) { _LOGD (LOGD_DEVICE, "secondary connection '%s' succeeded; active path '%s'", nm_active_connection_get_settings_connection_id (active), - nm_exported_object_get_path (NM_EXPORTED_OBJECT (active))); + nm_dbus_object_get_path (NM_DBUS_OBJECT (active))); /* Secondary connection activated */ secondary_data->secondaries = g_slist_remove (secondary_data->secondaries, secondary_active); @@ -1378,7 +1375,7 @@ process_secondaries (NMPolicy *self, } else { _LOGD (LOGD_DEVICE, "secondary connection '%s' failed; active path '%s'", nm_active_connection_get_settings_connection_id (active), - nm_exported_object_get_path (NM_EXPORTED_OBJECT (active))); + nm_dbus_object_get_path (NM_DBUS_OBJECT (active))); /* Secondary connection failed -> do not watch other connections */ priv->pending_secondaries = g_slist_remove (priv->pending_secondaries, secondary_data); @@ -1674,7 +1671,7 @@ activate_secondary_connections (NMPolicy *self, ac = nm_manager_activate_connection (priv->manager, settings_con, NULL, - nm_exported_object_get_path (NM_EXPORTED_OBJECT (req)), + nm_dbus_object_get_path (NM_DBUS_OBJECT (req)), device, nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (req)), NM_ACTIVATION_TYPE_MANAGED, @@ -1713,7 +1710,6 @@ device_state_changed (NMDevice *device, NMPolicy *self = _PRIV_TO_SELF (priv); NMActiveConnection *ac; NMSettingsConnection *connection = nm_device_get_settings_connection (device); - const char *ip_iface = nm_device_get_ip_iface (device); NMIP4Config *ip4_config; NMIP6Config *ip6_config; NMSettingConnection *s_con = NULL; @@ -1810,10 +1806,10 @@ device_state_changed (NMDevice *device, ip4_config = nm_device_get_ip4_config (device); if (ip4_config) - nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, ip4_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT); + nm_dns_manager_set_ip_config (priv->dns_manager, NM_IP_CONFIG_CAST (ip4_config), NM_DNS_IP_CONFIG_TYPE_DEFAULT); ip6_config = nm_device_get_ip6_config (device); if (ip6_config) - nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, ip6_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT); + nm_dns_manager_set_ip_config (priv->dns_manager, NM_IP_CONFIG_CAST (ip6_config), NM_DNS_IP_CONFIG_TYPE_DEFAULT); update_routing_and_dns (self, FALSE); @@ -1906,50 +1902,24 @@ device_state_changed (NMDevice *device, } static void -device_ip4_config_changed (NMDevice *device, - NMIP4Config *new_config, - NMIP4Config *old_config, - gpointer user_data) +device_ip_config_changed (NMDevice *device, + NMIPConfig *new_config, + NMIPConfig *old_config, + gpointer user_data) { NMPolicyPrivate *priv = user_data; NMPolicy *self = _PRIV_TO_SELF (priv); - const char *ip_iface = nm_device_get_ip_iface (device); - - nm_dns_manager_begin_updates (priv->dns_manager, __func__); + int addr_family; - /* 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): - * ignore IP config changes but when the device is in activated state. - * Prevents unecessary changes to DNS information. - */ - if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) { - if (old_config != new_config) { - if (old_config) - nm_dns_manager_remove_ip_config (priv->dns_manager, old_config); - if (new_config) - nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT); - } - update_ip_dns (self, AF_INET); - update_ip4_routing (self, TRUE); - update_system_hostname (self, "ip4 conf"); - } else { - /* Old configs get removed immediately */ - if (old_config) - nm_dns_manager_remove_ip_config (priv->dns_manager, old_config); - } + nm_assert (new_config || old_config); + nm_assert (!new_config || NM_IS_IP_CONFIG (new_config, AF_UNSPEC)); + nm_assert (!old_config || NM_IS_IP_CONFIG (old_config, AF_UNSPEC)); - nm_dns_manager_end_updates (priv->dns_manager, __func__); -} - -static void -device_ip6_config_changed (NMDevice *device, - NMIP6Config *new_config, - NMIP6Config *old_config, - gpointer user_data) -{ - NMPolicyPrivate *priv = user_data; - NMPolicy *self = _PRIV_TO_SELF (priv); - const char *ip_iface = nm_device_get_ip_iface (device); + 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_dns_manager_begin_updates (priv->dns_manager, __func__); @@ -1960,18 +1930,24 @@ device_ip6_config_changed (NMDevice *device, */ if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) { if (old_config != new_config) { - if (old_config) - nm_dns_manager_remove_ip_config (priv->dns_manager, old_config); if (new_config) - nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT); + nm_dns_manager_set_ip_config (priv->dns_manager, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT); + if (old_config) + nm_dns_manager_set_ip_config (priv->dns_manager, old_config, NM_DNS_IP_CONFIG_TYPE_REMOVED); } - update_ip_dns (self, AF_INET6); - update_ip6_routing (self, TRUE); - update_system_hostname (self, "ip6 conf"); + update_ip_dns (self, addr_family); + 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"); } else { /* Old configs get removed immediately */ if (old_config) - nm_dns_manager_remove_ip_config (priv->dns_manager, old_config); + nm_dns_manager_set_ip_config (priv->dns_manager, old_config, NM_DNS_IP_CONFIG_TYPE_REMOVED); } nm_dns_manager_end_updates (priv->dns_manager, __func__); @@ -2014,8 +1990,8 @@ 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, (GCallback) device_state_changed, priv); - g_signal_connect (device, NM_DEVICE_IP4_CONFIG_CHANGED, (GCallback) device_ip4_config_changed, priv); - g_signal_connect (device, NM_DEVICE_IP6_CONFIG_CHANGED, (GCallback) device_ip6_config_changed, priv); + g_signal_connect (device, NM_DEVICE_IP4_CONFIG_CHANGED, (GCallback) device_ip_config_changed, priv); + g_signal_connect (device, NM_DEVICE_IP6_CONFIG_CHANGED, (GCallback) device_ip_config_changed, priv); g_signal_connect (device, NM_DEVICE_IP6_PREFIX_DELEGATED, (GCallback) device_ip6_prefix_delegated, priv); g_signal_connect (device, NM_DEVICE_IP6_SUBNET_NEEDED, (GCallback) device_ip6_subnet_needed, priv); g_signal_connect (device, "notify::" NM_DEVICE_AUTOCONNECT, (GCallback) device_autoconnect_changed, priv); @@ -2032,7 +2008,7 @@ device_added (NMManager *manager, NMDevice *device, gpointer user_data) priv = NM_POLICY_GET_PRIVATE (self); - if (!nm_g_hash_table_add (priv->devices, device)) + if (!g_hash_table_add (priv->devices, device)) g_return_if_reached (); devices_list_register (self, device); @@ -2070,21 +2046,16 @@ vpn_connection_activated (NMPolicy *self, NMVpnConnection *vpn) NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); NMIP4Config *ip4_config; NMIP6Config *ip6_config; - const char *ip_iface; nm_dns_manager_begin_updates (priv->dns_manager, __func__); - ip_iface = nm_vpn_connection_get_ip_iface (vpn, TRUE); - - /* Add the VPN connection's IP configs from DNS */ - ip4_config = nm_vpn_connection_get_ip4_config (vpn); if (ip4_config) - nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, ip4_config, NM_DNS_IP_CONFIG_TYPE_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_add_ip_config (priv->dns_manager, ip_iface, ip6_config, NM_DNS_IP_CONFIG_TYPE_VPN); + 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); @@ -2101,16 +2072,12 @@ vpn_connection_deactivated (NMPolicy *self, NMVpnConnection *vpn) nm_dns_manager_begin_updates (priv->dns_manager, __func__); ip4_config = nm_vpn_connection_get_ip4_config (vpn); - if (ip4_config) { - /* Remove the VPN connection's IP4 config from DNS */ - nm_dns_manager_remove_ip_config (priv->dns_manager, ip4_config); - } + 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) { - /* Remove the VPN connection's IP6 config from DNS */ - nm_dns_manager_remove_ip_config (priv->dns_manager, ip6_config); - } + if (ip6_config) + nm_dns_manager_set_ip_config (priv->dns_manager, NM_IP_CONFIG_CAST (ip6_config), NM_DNS_IP_CONFIG_TYPE_REMOVED); update_routing_and_dns (self, TRUE); @@ -2154,7 +2121,7 @@ vpn_connection_retry_after_failure (NMVpnConnection *vpn, NMPolicy *self) &error)) { _LOGW (LOGD_DEVICE, "VPN '%s' reconnect failed: %s", nm_settings_connection_get_id (connection), - error->message ? error->message : "unknown"); + error->message ?: "unknown"); g_clear_error (&error); } } @@ -2220,12 +2187,13 @@ schedule_activate_all_cb (gpointer user_data) { NMPolicy *self = user_data; NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - const GSList *iter; + const CList *tmp_lst; + NMDevice *device; priv->schedule_activate_all_id = 0; - for (iter = nm_manager_get_devices (priv->manager); iter; iter = g_slist_next (iter)) - schedule_activate_check (self, iter->data); + nm_manager_for_each_device (priv->manager, device, tmp_lst) + schedule_activate_check (self, device); return G_SOURCE_REMOVE; } @@ -2259,7 +2227,8 @@ firewall_state_changed (NMFirewallManager *manager, { NMPolicy *self = (NMPolicy *) user_data; NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - const GSList *iter; + const CList *tmp_lst; + NMDevice *device; if (initialized_now) { /* the firewall manager was initializing, but all requests @@ -2272,8 +2241,8 @@ firewall_state_changed (NMFirewallManager *manager, return; /* add interface of each device to correct zone */ - for (iter = nm_manager_get_devices (priv->manager); iter; iter = g_slist_next (iter)) - nm_device_update_firewall_zone (iter->data); + nm_manager_for_each_device (priv->manager, device, tmp_lst) + nm_device_update_firewall_zone (device); } static void @@ -2318,14 +2287,13 @@ connection_updated (NMSettings *settings, { NMPolicyPrivate *priv = user_data; NMPolicy *self = _PRIV_TO_SELF (priv); - const GSList *iter; + const CList *tmp_lst; NMDevice *device = NULL; + NMDevice *dev; if (by_user) { /* find device with given connection */ - for (iter = nm_manager_get_devices (priv->manager); iter; iter = g_slist_next (iter)) { - NMDevice *dev = NM_DEVICE (iter->data); - + nm_manager_for_each_device (priv->manager, dev, tmp_lst) { if (nm_device_get_settings_connection (dev) == connection) { device = dev; break; @@ -2353,10 +2321,9 @@ _deactivate_if_active (NMPolicy *self, NMSettingsConnection *connection) nm_assert (NM_IS_SETTINGS_CONNECTION (connection)); nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) { - NMActiveConnectionState state = nm_active_connection_get_state (ac); if ( nm_active_connection_get_settings_connection (ac) == connection - && (state <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) { + && (nm_active_connection_get_state (ac) <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) { if (!nm_manager_deactivate_connection (priv->manager, ac, NM_DEVICE_STATE_REASON_CONNECTION_REMOVED, @@ -2391,7 +2358,7 @@ connection_flags_changed (NMSettings *settings, NMPolicy *self = _PRIV_TO_SELF (priv); if (NM_FLAGS_HAS (nm_settings_connection_get_flags (connection), - NM_SETTINGS_CONNECTION_FLAGS_VISIBLE)) { + NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)) { if (!nm_settings_connection_autoconnect_is_blocked (connection)) schedule_activate_all (self); } else @@ -2523,8 +2490,8 @@ nm_policy_init (NMPolicy *self) else /* default - full mode */ priv->hostname_mode = NM_POLICY_HOSTNAME_MODE_FULL; - priv->devices = g_hash_table_new (NULL, NULL); - priv->pending_active_connections = g_hash_table_new (NULL, NULL); + priv->devices = g_hash_table_new (nm_direct_hash, NULL); + priv->pending_active_connections = g_hash_table_new (nm_direct_hash, NULL); priv->ip6_prefix_delegations = g_array_new (FALSE, FALSE, sizeof (IP6PrefixDelegation)); g_array_set_clear_func (priv->ip6_prefix_delegations, clear_ip6_prefix_delegation); } diff --git a/src/nm-rfkill-manager.c b/src/nm-rfkill-manager.c index e655e67c..b2909e9d 100644 --- a/src/nm-rfkill-manager.c +++ b/src/nm-rfkill-manager.c @@ -296,7 +296,7 @@ add_one_killswitch (NMRfkillManager *self, struct udev_device *device) rfkill_type_to_desc (rtype), ks->path, ks->platform ? "platform " : "", - ks->driver ? ks->driver : ""); + ks->driver ?: ""); } static void diff --git a/src/nm-session-monitor.c b/src/nm-session-monitor.c index e7d1d742..23d41d7e 100644 --- a/src/nm-session-monitor.c +++ b/src/nm-session-monitor.c @@ -141,7 +141,7 @@ st_sd_finalize (NMSessionMonitor *monitor) sd_login_monitor_unref (monitor->sd.monitor); monitor->sd.monitor = NULL; } - g_source_remove (monitor->sd.watch); + nm_clear_g_source (&monitor->sd.watch); } #endif /* SESSION_TRACKING_SYSTEMD */ @@ -261,7 +261,7 @@ ck_init (NMSessionMonitor *monitor) if (g_file_query_exists (file, NULL)) { if ((monitor->ck.monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error))) { - monitor->ck.cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); + monitor->ck.cache = g_hash_table_new_full (nm_direct_hash, NULL, NULL, g_free); g_signal_connect (monitor->ck.monitor, "changed", G_CALLBACK (ck_changed), diff --git a/src/nm-sleep-monitor.c b/src/nm-sleep-monitor.c index 7a373874..a7e3a7c3 100644 --- a/src/nm-sleep-monitor.c +++ b/src/nm-sleep-monitor.c @@ -29,7 +29,6 @@ #include "nm-core-internal.h" #include "NetworkManagerUtils.h" - #if defined (SUSPEND_RESUME_UPOWER) #define SUSPEND_DBUS_NAME "org.freedesktop.UPower" diff --git a/src/nm-test-utils-core.h b/src/nm-test-utils-core.h index 58beadcd..c76f510f 100644 --- a/src/nm-test-utils-core.h +++ b/src/nm-test-utils-core.h @@ -30,6 +30,14 @@ /*****************************************************************************/ +#define NMTST_EXPECT_NM(level, msg) NMTST_EXPECT ("NetworkManager", level, msg) + +#define NMTST_EXPECT_NM_ERROR(msg) NMTST_EXPECT_NM (G_LOG_LEVEL_MESSAGE, "* [*] "msg) +#define NMTST_EXPECT_NM_WARN(msg) NMTST_EXPECT_NM (G_LOG_LEVEL_MESSAGE, "* [*] "msg) +#define NMTST_EXPECT_NM_INFO(msg) NMTST_EXPECT_NM (G_LOG_LEVEL_INFO, "* [*] "msg) +#define NMTST_EXPECT_NM_DEBUG(msg) NMTST_EXPECT_NM (G_LOG_LEVEL_DEBUG, "* [*] "msg) +#define NMTST_EXPECT_NM_TRACE(msg) NMTST_EXPECT_NM (G_LOG_LEVEL_DEBUG, "* [*] "msg) + static inline void nmtst_init_with_logging (int *argc, char ***argv, const char *log_level, const char *log_domains) { @@ -300,7 +308,6 @@ nmtst_platform_ip6_routes_equal_aptr (const NMPObject *const*a, const NMPlatform #endif - #ifdef __NETWORKMANAGER_IP4_CONFIG_H__ #include "nm-utils/nm-dedup-multi.h" @@ -313,21 +320,8 @@ nmtst_ip4_config_new (int ifindex) return nm_ip4_config_new (multi_idx, ifindex); } -static inline NMIP4Config * -nmtst_ip4_config_clone (NMIP4Config *config) -{ - NMIP4Config *copy; - - g_assert (config); - copy = nm_ip4_config_new (nm_ip4_config_get_multi_idx (config), -1); - g_assert (copy); - nm_ip4_config_replace (copy, config, NULL); - return copy; -} - #endif - #ifdef __NETWORKMANAGER_IP6_CONFIG_H__ #include "nm-utils/nm-dedup-multi.h" @@ -340,18 +334,6 @@ nmtst_ip6_config_new (int ifindex) return nm_ip6_config_new (multi_idx, ifindex); } -static inline NMIP6Config * -nmtst_ip6_config_clone (NMIP6Config *config) -{ - NMIP6Config *copy; - - g_assert (config); - copy = nm_ip6_config_new (nm_ip6_config_get_multi_idx (config), -1); - g_assert (copy); - nm_ip6_config_replace (copy, config, NULL); - return copy; -} - #endif #endif /* __NM_TEST_UTILS_CORE_H__ */ diff --git a/src/nm-types.h b/src/nm-types.h index 794b0a1c..1477d226 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -21,24 +21,23 @@ #ifndef __NETWORKMANAGER_TYPES_H__ #define __NETWORKMANAGER_TYPES_H__ -#ifdef __NM_UTILS_PRIVATE_H__ -#error "nm-utils-private.h" must not be used outside of libnm-core/. Do you want "nm-core-internal.h"? +#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_DAEMON) +#error Cannot use this header. #endif #define _NM_SD_MAX_CLIENT_ID_LEN (sizeof (guint32) + 128) /* core */ -typedef struct _NMExportedObject NMExportedObject; +typedef struct _NMDBusObject NMDBusObject; typedef struct _NMActiveConnection NMActiveConnection; typedef struct _NMAuditManager NMAuditManager; typedef struct _NMVpnConnection NMVpnConnection; typedef struct _NMActRequest NMActRequest; typedef struct _NMAuthSubject NMAuthSubject; -typedef struct _NMBusManager NMBusManager; +typedef struct _NMDBusManager NMDBusManager; typedef struct _NMConfig NMConfig; typedef struct _NMConfigData NMConfigData; -typedef struct _NMArpingManager NMArpingManager; -typedef struct _NMConnectionProvider NMConnectionProvider; +typedef struct _NMAcdManager NMAcdManager; typedef struct _NMConnectivity NMConnectivity; typedef struct _NMDevice NMDevice; typedef struct _NMDhcp4Config NMDhcp4Config; @@ -76,10 +75,12 @@ typedef enum { } NMActivationType; typedef enum { - NM_ACTIVATION_REASON_UNSET = 0, - NM_ACTIVATION_REASON_AUTOCONNECT = 1, - NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES = 2, - NM_ACTIVATION_REASON_USER_REQUEST = 3, + NM_ACTIVATION_REASON_UNSET, + NM_ACTIVATION_REASON_EXTERNAL, + NM_ACTIVATION_REASON_ASSUME, + NM_ACTIVATION_REASON_AUTOCONNECT, + NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES, + NM_ACTIVATION_REASON_USER_REQUEST, } NMActivationReason; typedef enum { @@ -102,6 +103,7 @@ typedef enum { NM_IP_CONFIG_SOURCE_KERNEL, NM_IP_CONFIG_SOURCE_SHARED, NM_IP_CONFIG_SOURCE_IP4LL, + NM_IP_CONFIG_SOURCE_IP6LL, NM_IP_CONFIG_SOURCE_PPP, NM_IP_CONFIG_SOURCE_WWAN, NM_IP_CONFIG_SOURCE_VPN, @@ -164,7 +166,6 @@ typedef enum { NM_LINK_TYPE_OPENVSWITCH, NM_LINK_TYPE_PPP, NM_LINK_TYPE_SIT, - NM_LINK_TYPE_TAP, NM_LINK_TYPE_TUN, NM_LINK_TYPE_VETH, NM_LINK_TYPE_VLAN, @@ -198,6 +199,7 @@ typedef enum { NMP_OBJECT_TYPE_LNK_MACVLAN, NMP_OBJECT_TYPE_LNK_MACVTAP, NMP_OBJECT_TYPE_LNK_SIT, + NMP_OBJECT_TYPE_LNK_TUN, NMP_OBJECT_TYPE_LNK_VLAN, NMP_OBJECT_TYPE_LNK_VXLAN, @@ -212,7 +214,6 @@ typedef enum { NM_IP_CONFIG_MERGE_NO_DNS = (1LL << 2), } NMIPConfigMergeFlags; - /** * NMIPRouteTableSyncMode: * @NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN: only the main table is synced. For all @@ -228,7 +229,6 @@ typedef enum { NM_IP_ROUTE_TABLE_SYNC_MODE_ALL = 3, } NMIPRouteTableSyncMode; - /* settings */ typedef struct _NMAgentManager NMAgentManager; typedef struct _NMSecretAgent NMSecretAgent; @@ -238,4 +238,6 @@ typedef struct _NMSettingsConnection NMSettingsConnection; /* utils */ typedef struct _NMUtilsIPv6IfaceId NMUtilsIPv6IfaceId; +#define NM_SETTING_CONNECTION_MDNS_UNKNOWN ((NMSettingConnectionMdns) -42) + #endif /* NM_TYPES_H */ diff --git a/src/org.freedesktop.NetworkManager.conf b/src/org.freedesktop.NetworkManager.conf index 6be1feb6..fa74b280 100644 --- a/src/org.freedesktop.NetworkManager.conf +++ b/src/org.freedesktop.NetworkManager.conf @@ -30,7 +30,7 @@ diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index be430152..06dd7e13 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -938,7 +938,6 @@ wifi_find_frequency (NMPlatform *platform, int ifindex, const guint32 *freqs) static void wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean running) { - ; } static guint32 diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index e5961c7e..3a671acf 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -21,6 +21,7 @@ #include "nm-linux-platform.h" +#include #include #include #include @@ -36,14 +37,14 @@ #include #include #include -#include -#include +#include #include #include "nm-utils.h" #include "nm-core-internal.h" #include "nm-setting-vlan.h" +#include "nm-netlink.h" #include "nm-core-utils.h" #include "nmp-object.h" #include "nmp-netns.h" @@ -79,10 +80,6 @@ enum { #define VLAN_FLAG_MVRP 0x8 -/* nm-internal error codes for libnl. Make sure they don't overlap. */ -#define _NLE_NM_NOBUFS 500 -#define _NLE_MSG_TRUNC 501 - /*****************************************************************************/ #define IFQDISCSIZ 32 @@ -127,6 +124,18 @@ enum { #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) #endif +#define IFLA_TUN_UNSPEC 0 +#define IFLA_TUN_OWNER 1 +#define IFLA_TUN_GROUP 2 +#define IFLA_TUN_TYPE 3 +#define IFLA_TUN_PI 4 +#define IFLA_TUN_VNET_HDR 5 +#define IFLA_TUN_PERSIST 6 +#define IFLA_TUN_MULTI_QUEUE 7 +#define IFLA_TUN_NUM_QUEUES 8 +#define IFLA_TUN_NUM_DISABLED_QUEUES 9 +#define __IFLA_TUN_MAX 10 +#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1) static const gboolean RTA_PREF_SUPPORTED_AT_COMPILETIME = (RTA_MAX >= 20 /* RTA_PREF */); @@ -172,7 +181,6 @@ G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); #define _NMLOG2(level, ...) _LOG ( level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__) #define _NMLOG2_err(errsv, level, ...) _LOG_err (errsv, level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__) - #define _LOG_print(__level, __domain, __errsv, self, ...) \ G_STMT_START { \ char __prefix[32]; \ @@ -217,7 +225,6 @@ G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); } \ } G_STMT_END - #define LOG_FMT_IP_TUNNEL "adding %s '%s' parent %u local %s remote %s" /****************************************************************** @@ -283,7 +290,7 @@ typedef enum { #define FOR_EACH_DELAYED_ACTION(iflags, flags_all) \ for ((iflags) = (DelayedActionType) 0x1LL; (iflags) <= DELAYED_ACTION_TYPE_MAX; (iflags) <<= 1) \ - if (NM_FLAGS_HAS (flags_all, iflags)) + if (NM_FLAGS_ANY (flags_all, iflags)) typedef enum { /* Negative values are errors from kernel. Add dummy member to @@ -297,13 +304,9 @@ typedef enum { WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS, } WaitForNlResponseResult; -typedef void (*WaitForNlResponseCallback) (NMPlatform *platform, - guint32 seq_number, - WaitForNlResponseResult seq_result, - gpointer user_data); - static void delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gpointer user_data); static gboolean delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink); static void do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const char *name); @@ -328,7 +331,9 @@ wait_for_nl_response_to_plerr (WaitForNlResponseResult seq_result) } static const char * -wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, char *buf, gsize buf_size) +wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, + const char *errmsg, + char *buf, gsize buf_size) { char *buf0 = buf; @@ -343,8 +348,13 @@ wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, char *buf, g nm_utils_strbuf_append_str (&buf, &buf_size, "failure"); break; default: - if (seq_result < 0) - nm_utils_strbuf_append (&buf, &buf_size, "failure %d (%s)", -((int) seq_result), g_strerror (-((int) seq_result))); + if (seq_result < 0) { + nm_utils_strbuf_append (&buf, &buf_size, "failure %d (%s%s%s)", + -((int) seq_result), + g_strerror (-((int) seq_result)), + errmsg ? " - " : "", + errmsg ?: ""); + } else nm_utils_strbuf_append (&buf, &buf_size, "internal failure %d", (int) seq_result); break; @@ -538,8 +548,7 @@ static const LinkDesc linktypes[] = { { NM_LINK_TYPE_OPENVSWITCH, "openvswitch", "openvswitch", NULL }, { NM_LINK_TYPE_PPP, "ppp", NULL, "ppp" }, { NM_LINK_TYPE_SIT, "sit", "sit", NULL }, - { NM_LINK_TYPE_TAP, "tap", NULL, NULL }, - { NM_LINK_TYPE_TUN, "tun", NULL, NULL }, + { NM_LINK_TYPE_TUN, "tun", "tun", NULL }, { NM_LINK_TYPE_VETH, "veth", "veth", NULL }, { NM_LINK_TYPE_VLAN, "vlan", "vlan", "vlan" }, { NM_LINK_TYPE_VXLAN, "vxlan", "vxlan", "vxlan" }, @@ -800,36 +809,25 @@ _linktype_get_type (NMPlatform *platform, && !NM_IN_SET (obj->link.type, NM_LINK_TYPE_UNKNOWN, NM_LINK_TYPE_NONE) && nm_streq (ifname, obj->link.name) && ( !kind - || !g_strcmp0 (kind, obj->link.kind))) { + || nm_streq0 (kind, obj->link.kind))) { nm_assert (obj->link.kind == g_intern_string (obj->link.kind)); *out_kind = obj->link.kind; return obj->link.type; } } + /* we intern kind to not require us to keep the pointer alive. Essentially + * leaking it in a global cache. That should be safe enough, because the + * kind comes only from kernel messages, which depend on the number of + * available drivers. So, there is not the danger that we leak uncontrolled + * many kinds. */ *out_kind = g_intern_string (kind); if (kind) { for (i = 0; i < G_N_ELEMENTS (linktypes); i++) { - if (g_strcmp0 (kind, linktypes[i].rtnl_type) == 0) + if (nm_streq0 (kind, linktypes[i].rtnl_type)) { return linktypes[i].nm_type; - } - - if (!strcmp (kind, "tun")) { - NMPlatformTunProperties props; - - if ( platform - && nm_platform_link_tun_get_properties (platform, ifindex, &props)) { - if (!g_strcmp0 (props.mode, "tap")) - return NM_LINK_TYPE_TAP; - if (!g_strcmp0 (props.mode, "tun")) - return NM_LINK_TYPE_TUN; } - - /* try guessing the type using the link flags instead... */ - if (flags & IFF_POINTOPOINT) - return NM_LINK_TYPE_TUN; - return NM_LINK_TYPE_TAP; } } @@ -951,137 +949,6 @@ _nl_addattr_l (struct nlmsghdr *n, return TRUE; } -static void -_nm_auto_nl_msg_cleanup (void *ptr) -{ - nlmsg_free (*((struct nl_msg **) ptr)); -} -#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup) - -static const char * -_nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len) -{ - const char *b; - const char *s; - guint flags, flags_before; - const char *prefix; - - nm_utils_to_string_buffer_init (&buf, &len); - b = buf; - - switch (hdr->nlmsg_type) { - case RTM_NEWLINK: s = "RTM_NEWLINK"; break; - case RTM_DELLINK: s = "RTM_DELLINK"; break; - case RTM_NEWADDR: s = "RTM_NEWADDR"; break; - case RTM_DELADDR: s = "RTM_DELADDR"; break; - case RTM_NEWROUTE: s = "RTM_NEWROUTE"; break; - case RTM_DELROUTE: s = "RTM_DELROUTE"; break; - case RTM_NEWQDISC: s = "RTM_NEWQDISC"; break; - case RTM_DELQDISC: s = "RTM_DELQDISC"; break; - case RTM_NEWTFILTER: s = "RTM_NEWTFILTER"; break; - case RTM_DELTFILTER: s = "RTM_DELTFILTER"; break; - case NLMSG_NOOP: s = "NLMSG_NOOP"; break; - case NLMSG_ERROR: s = "NLMSG_ERROR"; break; - case NLMSG_DONE: s = "NLMSG_DONE"; break; - case NLMSG_OVERRUN: s = "NLMSG_OVERRUN"; break; - default: s = NULL; break; - } - - if (s) - nm_utils_strbuf_append_str (&buf, &len, s); - else - nm_utils_strbuf_append (&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type); - - flags = hdr->nlmsg_flags; - - if (!flags) { - nm_utils_strbuf_append_str (&buf, &len, ", flags 0"); - goto flags_done; - } - -#define _F(f, n) \ - G_STMT_START { \ - if (NM_FLAGS_ALL (flags, f)) { \ - flags &= ~(f); \ - nm_utils_strbuf_append (&buf, &len, "%s%s", prefix, n); \ - if (!flags) \ - goto flags_done; \ - prefix = ","; \ - } \ - } G_STMT_END - - prefix = ", flags "; - flags_before = flags; - _F (NLM_F_REQUEST, "request"); - _F (NLM_F_MULTI, "multi"); - _F (NLM_F_ACK, "ack"); - _F (NLM_F_ECHO, "echo"); - _F (NLM_F_DUMP_INTR, "dump_intr"); - _F (0x20 /*NLM_F_DUMP_FILTERED*/, "dump_filtered"); - - if (flags_before != flags) - prefix = ";"; - - switch (hdr->nlmsg_type) { - case RTM_NEWLINK: - case RTM_NEWADDR: - case RTM_NEWROUTE: - case RTM_NEWQDISC: - case RTM_NEWTFILTER: - _F (NLM_F_REPLACE, "replace"); - _F (NLM_F_EXCL, "excl"); - _F (NLM_F_CREATE, "create"); - _F (NLM_F_APPEND, "append"); - break; - case RTM_GETLINK: - case RTM_GETADDR: - case RTM_GETROUTE: - case RTM_DELQDISC: - case RTM_DELTFILTER: - _F (NLM_F_DUMP, "dump"); - _F (NLM_F_ROOT, "root"); - _F (NLM_F_MATCH, "match"); - _F (NLM_F_ATOMIC, "atomic"); - break; - } - -#undef _F - - if (flags_before != flags) - prefix = ";"; - nm_utils_strbuf_append (&buf, &len, "%s0x%04x", prefix, flags); - -flags_done: - - nm_utils_strbuf_append (&buf, &len, ", seq %u", (unsigned) hdr->nlmsg_seq); - - return b; -} - -static int -_nl_nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, - const struct nla_policy *policy) -{ - return nla_parse (tb, maxtype, head, len, (struct nla_policy *) policy); -} -#define nla_parse(...) _nl_nla_parse(__VA_ARGS__) - -static int -_nl_nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], - int maxtype, const struct nla_policy *policy) -{ - return nlmsg_parse (nlh, hdrlen, tb, maxtype, (struct nla_policy *) policy); -} -#define nlmsg_parse(...) _nl_nlmsg_parse(__VA_ARGS__) - -static int -_nl_nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla, - const struct nla_policy *policy) -{ - return nla_parse_nested (tb, maxtype, nla, (struct nla_policy *) policy); -} -#define nla_parse_nested(...) _nl_nla_parse_nested(__VA_ARGS__) - /****************************************************************** * NMPObject/netlink functions ******************************************************************/ @@ -1292,6 +1159,7 @@ _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data) [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NLA_U8 }, [IFLA_IPTUN_FLOWINFO] = { .type = NLA_U32 }, [IFLA_IPTUN_PROTO] = { .type = NLA_U8 }, + [IFLA_IPTUN_FLAGS] = { .type = NLA_U32 }, }; struct nlattr *tb[IFLA_IPTUN_MAX + 1]; int err; @@ -1326,6 +1194,8 @@ _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data) } if (tb[IFLA_IPTUN_PROTO]) props->proto = nla_get_u8 (tb[IFLA_IPTUN_PROTO]); + if (tb[IFLA_IPTUN_FLAGS]) + props->flags = nla_get_u32 (tb[IFLA_IPTUN_FLAGS]); return obj; } @@ -1505,6 +1375,60 @@ _parse_lnk_sit (const char *kind, struct nlattr *info_data) /*****************************************************************************/ +static NMPObject * +_parse_lnk_tun (const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[IFLA_TUN_MAX + 1] = { + [IFLA_TUN_OWNER] = { .type = NLA_U32 }, + [IFLA_TUN_GROUP] = { .type = NLA_U32 }, + [IFLA_TUN_TYPE] = { .type = NLA_U8 }, + [IFLA_TUN_PI] = { .type = NLA_U8 }, + [IFLA_TUN_VNET_HDR] = { .type = NLA_U8 }, + [IFLA_TUN_PERSIST] = { .type = NLA_U8 }, + [IFLA_TUN_MULTI_QUEUE] = { .type = NLA_U8 }, + [IFLA_TUN_NUM_QUEUES] = { .type = NLA_U32 }, + [IFLA_TUN_NUM_DISABLED_QUEUES] = { .type = NLA_U32 }, + }; + struct nlattr *tb[IFLA_TUN_MAX + 1]; + int err; + NMPObject *obj; + NMPlatformLnkTun *props; + + if (!info_data || !nm_streq0 (kind, "tun")) + return NULL; + + err = nla_parse_nested (tb, IFLA_TUN_MAX, info_data, policy); + if (err < 0) + return NULL; + + if (!tb[IFLA_TUN_TYPE]) { + /* we require at least a type. */ + return NULL; + } + + obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_TUN, NULL); + props = &obj->lnk_tun; + + props->type = nla_get_u8 (tb[IFLA_TUN_TYPE]); + + props->pi = !!nla_get_u8_cond (tb, IFLA_TUN_PI, FALSE); + props->vnet_hdr = !!nla_get_u8_cond (tb, IFLA_TUN_VNET_HDR, FALSE); + props->multi_queue = !!nla_get_u8_cond (tb, IFLA_TUN_MULTI_QUEUE, FALSE); + props->persist = !!nla_get_u8_cond (tb, IFLA_TUN_PERSIST, FALSE); + + if (tb[IFLA_TUN_OWNER]) { + props->owner_valid = TRUE; + props->owner = nla_get_u32 (tb[IFLA_TUN_OWNER]); + } + if (tb[IFLA_TUN_GROUP]) { + props->group_valid = TRUE; + props->group = nla_get_u32 (tb[IFLA_TUN_GROUP]); + } + return obj; +} + +/*****************************************************************************/ + static gboolean _vlan_qos_mapping_from_nla (struct nlattr *nlattr, const NMVlanQosMapping **out_map, @@ -1610,7 +1534,6 @@ _parse_lnk_vlan (const char *kind, struct nlattr *info_data) &obj->_lnk_vlan.n_egress_qos_map)) return NULL; - obj_result = obj; obj = NULL; return obj_result; @@ -1936,6 +1859,9 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr case NM_LINK_TYPE_SIT: lnk_data = _parse_lnk_sit (nl_info_kind, nl_info_data); break; + case NM_LINK_TYPE_TUN: + lnk_data = _parse_lnk_tun (nl_info_kind, nl_info_data); + break; case NM_LINK_TYPE_VLAN: lnk_data = _parse_lnk_vlan (nl_info_kind, nl_info_data); break; @@ -2665,8 +2591,7 @@ _nl_msg_new_link (int nlmsg_type, nm_assert (NM_IN_SET (nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK)); - if (!(msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags))) - g_return_val_if_reached (NULL); + msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); if (nlmsg_append (msg, &ifi, sizeof (ifi), NLMSG_ALIGNTO) < 0) goto nla_put_failure; @@ -2708,8 +2633,6 @@ _nl_msg_new_address (int nlmsg_type, nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWADDR, RTM_DELADDR)); msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); - if (!msg) - g_return_val_if_reached (NULL); if (scope == -1) { /* Allow having scope unset, and detect the scope (including IPv4 compatibility hack). */ @@ -2822,8 +2745,6 @@ _nl_msg_new_route (int nlmsg_type, nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE)); msg = nlmsg_alloc_simple (nlmsg_type, (int) nlmsgflags); - if (!msg) - g_return_val_if_reached (NULL); if (nlmsg_append (msg, &rtmsg, sizeof (rtmsg), NLMSG_ALIGNTO) < 0) goto nla_put_failure; @@ -2921,8 +2842,6 @@ _nl_msg_new_qdisc (int nlmsg_type, }; msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); - if (!msg) - return NULL; if (nlmsg_append (msg, &tcm, sizeof (tcm), NLMSG_ALIGNTO) < 0) goto nla_put_failure; @@ -2997,8 +2916,6 @@ _nl_msg_new_tfilter (int nlmsg_type, }; msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); - if (!msg) - return NULL; if (nlmsg_append (msg, &tcm, sizeof (tcm), NLMSG_ALIGNTO) < 0) goto nla_put_failure; @@ -3040,6 +2957,7 @@ typedef struct { DelayedActionWaitForNlResponseType response_type; gint64 timeout_abs_ns; WaitForNlResponseResult *out_seq_result; + char **out_errmsg; union { gint *out_refresh_all_in_progess; NMPObject **out_route_get; @@ -3500,7 +3418,7 @@ delayed_action_to_string_full (DelayedActionType action_type, gpointer user_data (timeout < 0 ? -timeout : timeout) % NM_UTILS_NS_PER_SECOND, (int) data->response_type, data->seq_result ? ", " : "", - data->seq_result ? wait_for_nl_response_to_string (data->seq_result, b, sizeof (b)) : ""); + data->seq_result ? wait_for_nl_response_to_string (data->seq_result, NULL, b, sizeof (b)) : ""); } else nm_utils_strbuf_append_str (&buf, &buf_size, " (any)"); break; @@ -3582,27 +3500,58 @@ delayed_action_wait_for_nl_response_complete (NMPlatform *platform, } static void -delayed_action_wait_for_nl_response_complete_all (NMPlatform *platform, - WaitForNlResponseResult fallback_result) +delayed_action_wait_for_nl_response_complete_check (NMPlatform *platform, + WaitForNlResponseResult force_result, + guint32 *out_next_seq_number, + gint64 *out_next_timeout_abs_ns, + gint64 *p_now_ns) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + guint i; + guint32 next_seq_number = 0; + gint64 next_timeout_abs_ns = 0; + gint now_ns = 0; + + for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; ) { + const DelayedActionWaitForNlResponseData *data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); + + if (data->seq_result) + delayed_action_wait_for_nl_response_complete (platform, i, data->seq_result); + else if ( p_now_ns + && ((now_ns ?: (now_ns = nm_utils_get_monotonic_timestamp_ns ())) >= data->timeout_abs_ns)) { + /* the caller can optionally check for timeout by providing a p_now_ns argument. */ + delayed_action_wait_for_nl_response_complete (platform, i, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT); + } else if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) + delayed_action_wait_for_nl_response_complete (platform, i, force_result); + else { + if ( next_seq_number == 0 + || next_timeout_abs_ns > data->timeout_abs_ns) { + next_seq_number = data->seq_number; + next_timeout_abs_ns = data->timeout_abs_ns; + } + i++; + } + } - if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { - while (priv->delayed_action.list_wait_for_nl_response->len > 0) { - const DelayedActionWaitForNlResponseData *data; - guint idx = priv->delayed_action.list_wait_for_nl_response->len - 1; - WaitForNlResponseResult r; - - data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, idx); + if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) { + nm_assert (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); + nm_assert (priv->delayed_action.list_wait_for_nl_response->len == 0); + } - /* prefer the result that we already have. */ - r = data->seq_result ? : fallback_result; + NM_SET_OUT (out_next_seq_number, next_seq_number); + NM_SET_OUT (out_next_timeout_abs_ns, next_timeout_abs_ns); + NM_SET_OUT (p_now_ns, now_ns); +} - delayed_action_wait_for_nl_response_complete (platform, idx, r); - } - } - nm_assert (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); - nm_assert (priv->delayed_action.list_wait_for_nl_response->len == 0); +static void +delayed_action_wait_for_nl_response_complete_all (NMPlatform *platform, + WaitForNlResponseResult fallback_result) +{ + delayed_action_wait_for_nl_response_complete_check (platform, + fallback_result, + NULL, + NULL, + NULL); } /*****************************************************************************/ @@ -3785,6 +3734,7 @@ static void delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResult *out_seq_result, + char **out_errmsg, DelayedActionWaitForNlResponseType response_type, gpointer response_out_data) { @@ -3792,6 +3742,7 @@ delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform, .seq_number = seq_number, .timeout_abs_ns = nm_utils_get_monotonic_timestamp_ns () + (200 * (NM_UTILS_NS_PER_SECOND / 1000)), .out_seq_result = out_seq_result, + .out_errmsg = out_errmsg, .response_type = response_type, .response.out_data = response_out_data, }; @@ -3976,40 +3927,46 @@ cache_on_change (NMPlatform *platform, && (obj_new && obj_new->_link.netlink.is_in_netlink) && (!obj_old || !obj_old->_link.netlink.is_in_netlink)) { - if (!obj_new->_link.netlink.lnk) { + gboolean re_request_link = FALSE; + const NMPlatformLnkTun *lnk_tun; + + if ( !obj_new->_link.netlink.lnk + && NM_IN_SET (obj_new->link.type, NM_LINK_TYPE_GRE, + NM_LINK_TYPE_IP6TNL, + NM_LINK_TYPE_INFINIBAND, + NM_LINK_TYPE_MACVLAN, + NM_LINK_TYPE_MACVLAN, + NM_LINK_TYPE_SIT, + NM_LINK_TYPE_TUN, + NM_LINK_TYPE_VLAN, + NM_LINK_TYPE_VXLAN)) { /* certain link-types also come with a IFLA_INFO_DATA/lnk_data. It may happen that * kernel didn't send this notification, thus when we first learn about a link * that lacks an lnk_data we re-request it again. * * For example https://bugzilla.redhat.com/show_bug.cgi?id=1284001 */ - switch (obj_new->link.type) { - case NM_LINK_TYPE_GRE: - case NM_LINK_TYPE_IP6TNL: - case NM_LINK_TYPE_INFINIBAND: - case NM_LINK_TYPE_MACVLAN: - case NM_LINK_TYPE_MACVTAP: - case NM_LINK_TYPE_SIT: - case NM_LINK_TYPE_VLAN: - case NM_LINK_TYPE_VXLAN: - delayed_action_schedule (platform, - DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (obj_new->link.ifindex)); - break; - default: - break; - } - } - if ( obj_new->link.type == NM_LINK_TYPE_VETH - && obj_new->link.parent == 0) { + re_request_link = TRUE; + } else if ( obj_new->link.type == NM_LINK_TYPE_TUN + && obj_new->_link.netlink.lnk + && (lnk_tun = &(obj_new->_link.netlink.lnk)->lnk_tun) + && !lnk_tun->persist + && lnk_tun->pi + && !lnk_tun->vnet_hdr + && !lnk_tun->multi_queue + && !lnk_tun->owner_valid + && !lnk_tun->group_valid) { + /* kernel has/had a know issue that the first notification for TUN device would + * be sent with invalid parameters. The message looks like that kind, so refetch + * it. */ + re_request_link = TRUE; + } else if ( obj_new->link.type == NM_LINK_TYPE_VETH + && obj_new->link.parent == 0) { /* the initial notification when adding a veth pair can lack the parent/IFLA_LINK * (https://bugzilla.redhat.com/show_bug.cgi?id=1285827). * Request it again. */ - delayed_action_schedule (platform, - DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (obj_new->link.ifindex)); - } - if ( obj_new->link.type == NM_LINK_TYPE_ETHERNET - && obj_new->link.addr.len == 0) { + re_request_link = TRUE; + } else if ( obj_new->link.type == NM_LINK_TYPE_ETHERNET + && obj_new->link.addr.len == 0) { /* Due to a kernel bug, we sometimes receive spurious NEWLINK * messages after a wifi interface has disappeared. Since the * link is not present anymore we can't determine its type and @@ -4017,6 +3974,9 @@ cache_on_change (NMPlatform *platform, * specified. Request the link again to check if it really * exists. https://bugzilla.redhat.com/show_bug.cgi?id=1302037 */ + re_request_link = TRUE; + } + if (re_request_link) { delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (obj_new->link.ifindex)); @@ -4067,8 +4027,10 @@ cache_on_change (NMPlatform *platform, static guint32 _nlh_seq_next_get (NMLinuxPlatformPrivate *priv) { - /* generate a new sequence number, but skip zero. */ - return priv->nlh_seq_next++ ?: priv->nlh_seq_next++; + /* generate a new sequence number, but never return zero. + * Wrapping numbers are not a problem, because we don't rely + * on strictly increasing sequence numbers. */ + return (++priv->nlh_seq_next) ?: (++priv->nlh_seq_next); } /** @@ -4079,12 +4041,13 @@ _nlh_seq_next_get (NMLinuxPlatformPrivate *priv) * @response_type: * @response_out_data: * - * Returns: 0 on success or a negative errno. Beware, it's an errno, not nlerror. + * Returns: 0 on success or a negative errno. */ static int _nl_send_nlmsghdr (NMPlatform *platform, struct nlmsghdr *nlhdr, WaitForNlResponseResult *out_seq_result, + char **out_errmsg, DelayedActionWaitForNlResponseType response_type, gpointer response_out_data) { @@ -4129,7 +4092,7 @@ again: } } - delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, + delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, out_errmsg, response_type, response_out_data); return 0; } @@ -4148,6 +4111,7 @@ static int _nl_send_nlmsg (NMPlatform *platform, struct nl_msg *nlmsg, WaitForNlResponseResult *out_seq_result, + char **out_errmsg, DelayedActionWaitForNlResponseType response_type, gpointer response_out_data) { @@ -4166,7 +4130,7 @@ _nl_send_nlmsg (NMPlatform *platform, return nle; } - delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, + delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, out_errmsg, response_type, response_out_data); return 0; } @@ -4204,7 +4168,7 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha 0, 0); if (nlmsg) { - nle = _nl_send_nlmsg (platform, nlmsg, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-request-link: %d %s: failed sending netlink request \"%s\" (%d)", ifindex, name ?: "", @@ -4263,8 +4227,6 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio * because we need the sequence number. */ nlmsg = nlmsg_alloc_simple (klass->rtm_gettype, NLM_F_DUMP); - if (!nlmsg) - continue; if ( klass->obj_type == NMP_OBJECT_TYPE_QDISC || klass->obj_type == NMP_OBJECT_TYPE_TFILTER) { @@ -4281,7 +4243,7 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio if (nle < 0) continue; - if (_nl_send_nlmsg (platform, nlmsg, NULL, DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, out_refresh_all_in_progess) < 0) { + if (_nl_send_nlmsg (platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, out_refresh_all_in_progess) < 0) { nm_assert (*out_refresh_all_in_progess > 0); *out_refresh_all_in_progess -= 1; } @@ -4325,7 +4287,7 @@ event_seq_check_refresh_all (NMPlatform *platform, guint32 seq_number) } static void -event_seq_check (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResult seq_result) +event_seq_check (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResult seq_result, const char *msg) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); DelayedActionWaitForNlResponseData *data; @@ -4344,11 +4306,13 @@ event_seq_check (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResu /* We potentially receive many parts partial responses for the same sequence number. * Thus, we only remember the result, and collect it later. */ if (data->seq_result < 0) { - /* we already saw an error for this seqence number. + /* we already saw an error for this sequence number. * Preserve it. */ } else if ( seq_result != WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN || data->seq_result == WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) data->seq_result = seq_result; + if (data->out_errmsg && !*data->out_errmsg) + *data->out_errmsg = g_strdup (msg); return; } } @@ -4391,7 +4355,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event obj = nmp_object_new_from_nl (platform, cache, msg, id_only); if (!obj) { _LOGT ("event-notification: %s: ignore", - _nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); + nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); return; } @@ -4409,7 +4373,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event } _LOGT ("event-notification: %s%s: %s", - _nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)), + nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)), is_dump ? ", in-dump" : "", nmp_object_to_string (obj, id_only ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, @@ -4547,13 +4511,14 @@ do_add_link_with_lookup (NMPlatform *platform, { const NMPObject *obj = NULL; WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + gs_free char *errmsg = NULL; int nle; char s_buf[256]; NMPCache *cache = nm_platform_get_cache (platform); event_handler_read_netlink (platform, FALSE); - nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)", name, @@ -4573,7 +4538,7 @@ do_add_link_with_lookup (NMPlatform *platform, "do-add-link[%s/%s]: %s", name, nm_link_type_to_string (link_type), - wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); + wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf))); if (out_link) { obj = nmp_cache_lookup_link_full (cache, 0, name, FALSE, link_type, NULL, NULL); @@ -4590,6 +4555,7 @@ do_add_addrroute (NMPlatform *platform, gboolean suppress_netlink_failure) { WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + gs_free char *errmsg = NULL; int nle; char s_buf[256]; @@ -4599,7 +4565,7 @@ do_add_addrroute (NMPlatform *platform, event_handler_read_netlink (platform, FALSE); - nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, @@ -4620,7 +4586,7 @@ do_add_addrroute (NMPlatform *platform, "do-add-%s[%s]: %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), - wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); + wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf))); if (NMP_OBJECT_GET_TYPE (obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) { /* In rare cases, the object is not yet ready as we received the ACK from @@ -4641,6 +4607,7 @@ static gboolean do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) { WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + gs_free char *errmsg = NULL; int nle; char s_buf[256]; gboolean success; @@ -4648,7 +4615,7 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * event_handler_read_netlink (platform, FALSE); - nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, @@ -4680,7 +4647,7 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * "do-delete-%s[%s]: %s%s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), - wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)), + wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf)), log_detail); if (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), @@ -4711,6 +4678,7 @@ do_change_link (NMPlatform *platform, nm_auto_pop_netns NMPNetns *netns = NULL; int nle; WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + gs_free char *errmsg = NULL; char s_buf[256]; NMPlatformError result = NM_PLATFORM_ERROR_SUCCESS; NMLogLevel log_level = LOGL_DEBUG; @@ -4726,7 +4694,7 @@ do_change_link (NMPlatform *platform, } retry: - nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { log_level = LOGL_ERR; log_detail_free = g_strdup_printf (", failure sending netlink request: %s (%d)", @@ -4783,7 +4751,7 @@ out: "do-change-link[%d]: %s changing link: %s%s", ifindex, log_result, - wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)), + wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf)), log_detail); return result; } @@ -4860,6 +4828,12 @@ link_refresh (NMPlatform *platform, int ifindex) return !!nm_platform_link_get_obj (platform, ifindex, TRUE); } +static void +refresh_all (NMPlatform *platform, NMPObjectType obj_type) +{ + do_request_one_type (platform, obj_type); +} + static gboolean link_set_netns (NMPlatform *platform, int ifindex, @@ -5392,6 +5366,7 @@ link_ip6tnl_add (NMPlatform *platform, & IP6_FLOWINFO_TCLASS_MASK; NLA_PUT_U32 (nlmsg, IFLA_IPTUN_FLOWINFO, htonl (flowinfo)); NLA_PUT_U8 (nlmsg, IFLA_IPTUN_PROTO, props->proto); + NLA_PUT_U32 (nlmsg, IFLA_IPTUN_FLAGS, props->flags); nla_nest_end (nlmsg, data); nla_nest_end (nlmsg, info); @@ -5614,6 +5589,62 @@ nla_put_failure: g_return_val_if_reached (FALSE); } +static gboolean +link_tun_add (NMPlatform *platform, + const char *name, + const NMPlatformLnkTun *props, + const NMPlatformLink **out_link, + int *out_fd) +{ + const NMPObject *obj; + struct ifreq ifr = { }; + nm_auto_close int fd = -1; + + nm_assert (NM_IN_SET (props->type, IFF_TAP, IFF_TUN)); + nm_assert (props->persist || out_fd); + + fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC); + if (fd < 0) + return FALSE; + + nm_utils_ifname_cpy (ifr.ifr_name, name); + ifr.ifr_flags = ((short) props->type) + | ((short) IFF_TUN_EXCL) + | (!props->pi ? (short) IFF_NO_PI : (short) 0) + | ( props->vnet_hdr ? (short) IFF_VNET_HDR : (short) 0) + | ( props->multi_queue ? (short) NM_IFF_MULTI_QUEUE : (short) 0); + if (ioctl (fd, TUNSETIFF, &ifr)) + return FALSE; + + if (props->owner_valid) { + if (ioctl (fd, TUNSETOWNER, (uid_t) props->owner)) + return FALSE; + } + + if (props->group_valid) { + if (ioctl (fd, TUNSETGROUP, (gid_t) props->group)) + return FALSE; + } + + if (props->persist) { + if (ioctl (fd, TUNSETPERSIST, 1)) + return FALSE; + } + + do_request_link (platform, 0, name); + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), + 0, name, FALSE, + NM_LINK_TYPE_TUN, + NULL, NULL); + + if (!obj) + return FALSE; + + NM_SET_OUT (out_link, &obj->link); + NM_SET_OUT (out_fd, nm_steal_fd (&fd)); + return TRUE; +} + static gboolean link_vxlan_add (NMPlatform *platform, const char *name, @@ -5854,64 +5885,6 @@ link_vlan_change (NMPlatform *platform, return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; } -static int -tun_add (NMPlatform *platform, const char *name, gboolean tap, - gint64 owner, gint64 group, gboolean pi, gboolean vnet_hdr, - gboolean multi_queue, const NMPlatformLink **out_link) -{ - const NMPObject *obj; - struct ifreq ifr = { }; - int fd; - - fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC); - if (fd < 0) - return FALSE; - - nm_utils_ifname_cpy (ifr.ifr_name, name); - ifr.ifr_flags = tap ? IFF_TAP : IFF_TUN; - - if (!pi) - ifr.ifr_flags |= IFF_NO_PI; - if (vnet_hdr) - ifr.ifr_flags |= IFF_VNET_HDR; - if (multi_queue) - ifr.ifr_flags |= NM_IFF_MULTI_QUEUE; - - if (ioctl (fd, TUNSETIFF, &ifr)) { - nm_close (fd); - return FALSE; - } - - if (owner >= 0 && owner < G_MAXINT32) { - if (ioctl (fd, TUNSETOWNER, (uid_t) owner)) { - nm_close (fd); - return FALSE; - } - } - - if (group >= 0 && group < G_MAXINT32) { - if (ioctl (fd, TUNSETGROUP, (gid_t) group)) { - nm_close (fd); - return FALSE; - } - } - - if (ioctl (fd, TUNSETPERSIST, 1)) { - nm_close (fd); - return FALSE; - } - do_request_link (platform, 0, name); - obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), - 0, name, FALSE, - tap ? NM_LINK_TYPE_TAP : NM_LINK_TYPE_TUN, - NULL, NULL); - if (out_link) - *out_link = obj ? &obj->link : NULL; - - nm_close (fd); - return !!obj; -} - static gboolean link_enslave (NMPlatform *platform, int master, int slave) { @@ -6453,7 +6426,7 @@ ip_route_get (NMPlatform *platform, } seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - nle = _nl_send_nlmsghdr (platform, &req.n, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET, &route); + nle = _nl_send_nlmsghdr (platform, &req.n, &seq_result, NULL, DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET, &route); if (nle < 0) { _LOGE ("get-route: failure sending netlink request \"%s\" (%d)", g_strerror (-nle), -nle); @@ -6492,6 +6465,7 @@ qdisc_add (NMPlatform *platform, const NMPlatformQdisc *qdisc) { WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + gs_free char *errmsg = NULL; int nle; char s_buf[256]; nm_auto_nlmsg struct nl_msg *msg = NULL; @@ -6500,7 +6474,7 @@ qdisc_add (NMPlatform *platform, event_handler_read_netlink (platform, FALSE); - nle = _nl_send_nlmsg (platform, msg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + nle = _nl_send_nlmsg (platform, msg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-add-qdisc: failed sending netlink request \"%s\" (%d)", nl_geterror (nle), -nle); @@ -6515,7 +6489,7 @@ qdisc_add (NMPlatform *platform, ? LOGL_DEBUG : LOGL_WARN, "do-add-qdisc: %s", - wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); + wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf))); if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) return NM_PLATFORM_ERROR_SUCCESS; @@ -6531,6 +6505,7 @@ tfilter_add (NMPlatform *platform, const NMPlatformTfilter *tfilter) { WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + gs_free char *errmsg = NULL; int nle; char s_buf[256]; nm_auto_nlmsg struct nl_msg *msg = NULL; @@ -6539,7 +6514,7 @@ tfilter_add (NMPlatform *platform, event_handler_read_netlink (platform, FALSE); - nle = _nl_send_nlmsg (platform, msg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + nle = _nl_send_nlmsg (platform, msg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-add-tfilter: failed sending netlink request \"%s\" (%d)", nl_geterror (nle), -nle); @@ -6554,7 +6529,7 @@ tfilter_add (NMPlatform *platform, ? LOGL_DEBUG : LOGL_WARN, "do-add-tfilter: %s", - wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); + wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf))); if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) return NM_PLATFORM_ERROR_SUCCESS; @@ -6585,15 +6560,12 @@ event_handler_recvmsgs (NMPlatform *platform, gboolean handle_events) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); struct nl_sock *sk = priv->nlh; - int n, err = 0, multipart = 0, interrupted = 0; + int n; + int err = 0; + gboolean multipart = 0; + gboolean interrupted = FALSE; struct nlmsghdr *hdr; WaitForNlResponseResult seq_result; - - /* - nla is passed on to not only to nl_recv() but may also be passed - to a function pointer provided by the caller which may or may not - initialize the variable. Thomas Graf. - */ struct sockaddr_nl nla = {0}; nm_auto_free struct ucred *creds = NULL; nm_auto_free unsigned char *buf = NULL; @@ -6601,56 +6573,28 @@ event_handler_recvmsgs (NMPlatform *platform, gboolean handle_events) continue_reading: g_clear_pointer (&buf, free); g_clear_pointer (&creds, free); - errno = 0; n = nl_recv (sk, &nla, &buf, &creds); if (n <= 0) { - /* workaround libnl3 <= 3.2.15 returning danling pointers in case nl_recv() - * fails. Fixed by libnl3 69468517d0de1675d80f24661ff57a5dbac7275c. */ - buf = NULL; - creds = NULL; - } - - switch (n) { - case 0: - /* Work around a libnl bug fixed in 3.2.22 (375a6294) */ - if (errno == EAGAIN) { - /* EAGAIN is equal to EWOULDBLOCK. If it would not be, we'd have to - * workaround libnl3 mapping EWOULDBLOCK to -NLE_FAILURE. */ - G_STATIC_ASSERT (EAGAIN == EWOULDBLOCK); - n = -NLE_AGAIN; - } - break; - case -NLE_MSG_TRUNC: { - int buf_size; - - /* the message receive buffer was too small. We lost one message, which - * is unfortunate. Try to double the buffer size for the next time. */ - buf_size = nl_socket_get_msg_buf_size (sk); - if (buf_size < 512*1024) { - buf_size *= 2; - _LOGT ("netlink: recvmsg: increase message buffer size for recvmsg() to %d bytes", buf_size); - if (nl_socket_set_msg_buf_size (sk, buf_size) < 0) - nm_assert_not_reached (); - if (!handle_events) - goto continue_reading; - } - n = -_NLE_MSG_TRUNC; - break; - } - case -NLE_NOMEM: - if (errno == ENOBUFS) { - /* we are very much interested in a overrun of the receive buffer. - * nl_recv() maps all kinds of errors to NLE_NOMEM, so check also - * for errno explicitly. And if so, hack our own return code to signal - * the overrun. */ - n = -_NLE_NM_NOBUFS; + + if (n == -NLE_MSG_TRUNC) { + int buf_size; + + /* the message receive buffer was too small. We lost one message, which + * is unfortunate. Try to double the buffer size for the next time. */ + buf_size = nl_socket_get_msg_buf_size (sk); + if (buf_size < 512*1024) { + buf_size *= 2; + _LOGT ("netlink: recvmsg: increase message buffer size for recvmsg() to %d bytes", buf_size); + if (nl_socket_set_msg_buf_size (sk, buf_size) < 0) + nm_assert_not_reached (); + if (!handle_events) + goto continue_reading; + } } - break; - } - if (n <= 0) return n; + } hdr = (struct nlmsghdr *) buf; while (nlmsg_ok (hdr, n)) { @@ -6659,12 +6603,9 @@ continue_reading: gboolean process_valid_msg = FALSE; guint32 seq_number; char buf_nlmsghdr[400]; + const char *extack_msg = NULL; - msg = nlmsg_convert (hdr); - if (!msg) { - err = -NLE_NOMEM; - goto out; - } + msg = nlmsg_alloc_convert (hdr); nlmsg_set_proto (msg, NETLINK_ROUTE); nlmsg_set_src (msg, &nla); @@ -6679,13 +6620,13 @@ continue_reading: } _LOGt ("netlink: recvmsg: new message %s", - _nl_nlmsghdr_to_str (hdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); + nl_nlmsghdr_to_str (hdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); if (creds) nlmsg_set_creds (msg, creds); if (hdr->nlmsg_flags & NLM_F_MULTI) - multipart = 1; + multipart = TRUE; if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { /* @@ -6693,7 +6634,7 @@ continue_reading: * all messages until a NLMSG_DONE is * received and report the inconsistency. */ - interrupted = 1; + interrupted = TRUE; } /* Other side wishes to see an ack for this message */ @@ -6708,7 +6649,7 @@ continue_reading: * usually the end of a message and therefore we slip * out of the loop by default. the user may overrule * this action by skipping this packet. */ - multipart = 0; + multipart = FALSE; seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; } else if (hdr->nlmsg_type == NLMSG_NOOP) { /* Message to be ignored, the default action is to @@ -6735,10 +6676,28 @@ continue_reading: } else if (e->error) { int errsv = e->error > 0 ? e->error : -e->error; + if ( NM_FLAGS_HAS (hdr->nlmsg_flags, NLM_F_ACK_TLVS) + && hdr->nlmsg_len >= sizeof (*e) + e->msg.nlmsg_len) { + static const struct nla_policy policy[NLMSGERR_ATTR_MAX + 1] = { + [NLMSGERR_ATTR_MSG] = { .type = NLA_STRING }, + [NLMSGERR_ATTR_OFFS] = { .type = NLA_U32 }, + }; + struct nlattr *tb[NLMSGERR_ATTR_MAX + 1]; + struct nlattr *tlvs; + + tlvs = (struct nlattr *) ((char *) e + sizeof (*e) + e->msg.nlmsg_len - NLMSG_HDRLEN); + if (!nla_parse (tb, NLMSGERR_ATTR_MAX, tlvs, + hdr->nlmsg_len - sizeof (*e) - e->msg.nlmsg_len, policy)) { + if (tb[NLMSGERR_ATTR_MSG]) + extack_msg = nla_get_string (tb[NLMSGERR_ATTR_MSG]); + } + } + /* Error message reported back from kernel. */ - _LOGD ("netlink: recvmsg: error message from kernel: %s (%d) for request %d", + _LOGD ("netlink: recvmsg: error message from kernel: %s (%d)%s%s%s for request %d", strerror (errsv), errsv, + NM_PRINT_FMT_QUOTED (extack_msg, " \"", extack_msg, "\"", ""), nlmsg_hdr (msg)->nlmsg_seq); seq_result = -errsv; } else @@ -6767,7 +6726,7 @@ continue_reading: seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; } - event_seq_check (platform, seq_number, seq_result); + event_seq_check (platform, seq_number, seq_result, extack_msg); if (abort_parsing) goto stop; @@ -6787,9 +6746,9 @@ stop: * Repeat reading. */ goto continue_reading; } -out: + if (interrupted) - err = -NLE_DUMP_INTR; + return -NLE_DUMP_INTR; return err; } @@ -6800,46 +6759,50 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) { nm_auto_pop_netns NMPNetns *netns = NULL; NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int r, nle; + int r; struct pollfd pfd; gboolean any = FALSE; - gint64 now_ns; int timeout_ms; - guint i; struct { guint32 seq_number; gint64 timeout_abs_ns; - } data_next; + gint64 now_ns; + } next; - if (!nm_platform_netns_push (platform, &netns)) + if (!nm_platform_netns_push (platform, &netns)) { + delayed_action_wait_for_nl_response_complete_all (platform, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS); return FALSE; + } - while (TRUE) { - - while (TRUE) { + for (;;) { + for (;;) { + int nle; nle = event_handler_recvmsgs (platform, TRUE); if (nle < 0) { switch (nle) { - case -NLE_AGAIN: + case -EAGAIN: goto after_read; case -NLE_DUMP_INTR: _LOGD ("netlink: read: uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); break; - case -_NLE_MSG_TRUNC: - case -_NLE_NM_NOBUFS: + case -NLE_MSG_TRUNC: + case -ENOBUFS: _LOGI ("netlink: read: %s. Need to resynchronize platform cache", ({ const char *_reason = "unknown"; switch (nle) { - case -_NLE_MSG_TRUNC: _reason = "message truncated"; break; - case -_NLE_NM_NOBUFS: _reason = "too many netlink events"; break; + case -NLE_MSG_TRUNC: _reason = "message truncated"; break; + case -ENOBUFS: _reason = "too many netlink events"; break; } _reason; })); event_handler_recvmsgs (platform, FALSE); - delayed_action_wait_for_nl_response_complete_all (platform, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC); + delayed_action_wait_for_nl_response_complete_all (platform, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC); + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | @@ -6863,39 +6826,23 @@ after_read: if (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) return any; - now_ns = 0; - data_next.seq_number = 0; - data_next.timeout_abs_ns = 0; - - for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; ) { - DelayedActionWaitForNlResponseData *data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); - - if (data->seq_result) - delayed_action_wait_for_nl_response_complete (platform, i, data->seq_result); - else if ((now_ns ?: (now_ns = nm_utils_get_monotonic_timestamp_ns ())) > data->timeout_abs_ns) - delayed_action_wait_for_nl_response_complete (platform, i, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT); - else { - i++; - - if ( data_next.seq_number == 0 - || data_next.timeout_abs_ns > data->timeout_abs_ns) { - data_next.seq_number = data->seq_number; - data_next.timeout_abs_ns = data->timeout_abs_ns; - } - } - } + delayed_action_wait_for_nl_response_complete_check (platform, + WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN, + &next.seq_number, + &next.timeout_abs_ns, + &next.now_ns); if ( !wait_for_acks || !NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) return any; - nm_assert (data_next.seq_number); - nm_assert (data_next.timeout_abs_ns > 0); - nm_assert (now_ns > 0); + nm_assert (next.seq_number); + nm_assert (next.now_ns > 0); + nm_assert (next.timeout_abs_ns > next.now_ns); - _LOGT ("netlink: read: wait for ACK for sequence number %u...", data_next.seq_number); + _LOGT ("netlink: read: wait for ACK for sequence number %u...", next.seq_number); - timeout_ms = (data_next.timeout_abs_ns - now_ns) / (NM_UTILS_NS_PER_SECOND / 1000); + timeout_ms = (next.timeout_abs_ns - next.now_ns) / (NM_UTILS_NS_PER_SECOND / 1000); memset (&pfd, 0, sizeof (pfd)); pfd.fd = nl_socket_get_fd (priv->nlh); @@ -6906,6 +6853,7 @@ after_read: /* timeout and there is nothing to read. */ goto after_read; } + if (r < 0) { int errsv = errno; @@ -7032,7 +6980,7 @@ handle_udev_event (NMUdevClient *udev_client, seqnum = udev_device_get_seqnum (udevice); _LOGD ("UDEV event: action '%s' subsys '%s' device '%s' (%s); seqnum=%" G_GUINT64_FORMAT, action, subsys, udev_device_get_sysname (udevice), - ifindex ? ifindex : "unknown", seqnum); + ifindex ?: "unknown", seqnum); if (NM_IN_STRSET (action, "add", "move")) udev_device_added (platform, udevice); @@ -7047,11 +6995,10 @@ nm_linux_platform_init (NMLinuxPlatform *self) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (self); - priv->nlh_seq_next = 1; priv->delayed_action.list_master_connected = g_ptr_array_new (); priv->delayed_action.list_refresh_link = g_ptr_array_new (); priv->delayed_action.list_wait_for_nl_response = g_array_new (FALSE, TRUE, sizeof (DelayedActionWaitForNlResponseData)); - priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); + priv->wifi_data = g_hash_table_new_full (nm_direct_hash, NULL, NULL, (GDestroyNotify) wifi_utils_unref); } static void @@ -7097,6 +7044,10 @@ constructed (GObject *_object) nle = nl_socket_set_buffer_size (priv->nlh, 8*1024*1024, 0); g_assert (!nle); + nle = nl_socket_set_ext_ack (priv->nlh, TRUE); + if (nle) + _LOGD ("could not enable extended acks on netlink socket"); + /* explicitly set the msg buffer size and disable MSG_PEEK. * If we later encounter NLE_MSG_TRUNC, we will adjust the buffer size. */ nl_socket_disable_msg_peek (priv->nlh); @@ -7176,7 +7127,8 @@ dispose (GObject *object) _LOGD ("dispose"); - delayed_action_wait_for_nl_response_complete_all (platform, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING); + delayed_action_wait_for_nl_response_complete_all (platform, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING); priv->delayed_action.flags = DELAYED_ACTION_TYPE_NONE; g_ptr_array_set_size (priv->delayed_action.list_master_connected, 0); @@ -7226,6 +7178,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_add = link_add; platform_class->link_delete = link_delete; + platform_class->refresh_all = refresh_all; platform_class->link_refresh = link_refresh; platform_class->link_set_netns = link_set_netns; @@ -7264,8 +7217,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_vlan_change = link_vlan_change; platform_class->link_vxlan_add = link_vxlan_add; - platform_class->tun_add = tun_add; - platform_class->infiniband_partition_add = infiniband_partition_add; platform_class->infiniband_partition_delete = infiniband_partition_delete; @@ -7290,6 +7241,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_macvlan_add = link_macvlan_add; platform_class->link_ipip_add = link_ipip_add; platform_class->link_sit_add = link_sit_add; + platform_class->link_tun_add = link_tun_add; platform_class->object_delete = object_delete; platform_class->ip4_address_add = ip4_address_add; diff --git a/src/platform/nm-netlink.c b/src/platform/nm-netlink.c new file mode 100644 index 00000000..4cb19780 --- /dev/null +++ b/src/platform/nm-netlink.c @@ -0,0 +1,1491 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-platform.c - Handle runtime kernel networking configuration + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-netlink.h" + +#include +#include + +/*****************************************************************************/ + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +/*****************************************************************************/ + +#define NL_SOCK_PASSCRED (1<<1) +#define NL_MSG_PEEK (1<<3) +#define NL_MSG_PEEK_EXPLICIT (1<<4) +#define NL_NO_AUTO_ACK (1<<5) + +#ifndef NETLINK_EXT_ACK +#define NETLINK_EXT_ACK 11 +#endif + +#define NL_MSG_CRED_PRESENT 1 + +struct nl_msg { + int nm_protocol; + int nm_flags; + struct sockaddr_nl nm_src; + struct sockaddr_nl nm_dst; + struct ucred nm_creds; + struct nlmsghdr * nm_nlh; + size_t nm_size; + int nm_refcnt; +}; + +struct nl_sock { + struct sockaddr_nl s_local; + struct sockaddr_nl s_peer; + int s_fd; + int s_proto; + unsigned int s_seq_next; + unsigned int s_seq_expect; + int s_flags; + size_t s_bufsize; +}; + +/*****************************************************************************/ + +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_geterror, int, + NM_UTILS_LOOKUP_DEFAULT (NULL), + NM_UTILS_LOOKUP_ITEM (NLE_UNSPEC, "NLE_UNSPEC"), + NM_UTILS_LOOKUP_ITEM (NLE_BUG, "NLE_BUG"), + NM_UTILS_LOOKUP_ITEM (NLE_NATIVE_ERRNO, "NLE_NATIVE_ERRNO"), + + NM_UTILS_LOOKUP_ITEM (NLE_ATTRSIZE, "NLE_ATTRSIZE"), + NM_UTILS_LOOKUP_ITEM (NLE_BAD_SOCK, "NLE_BAD_SOCK"), + NM_UTILS_LOOKUP_ITEM (NLE_DUMP_INTR, "NLE_DUMP_INTR"), + NM_UTILS_LOOKUP_ITEM (NLE_MSG_OVERFLOW, "NLE_MSG_OVERFLOW"), + NM_UTILS_LOOKUP_ITEM (NLE_MSG_TOOSHORT, "NLE_MSG_TOOSHORT"), + NM_UTILS_LOOKUP_ITEM (NLE_MSG_TRUNC, "NLE_MSG_TRUNC"), + NM_UTILS_LOOKUP_ITEM (NLE_SEQ_MISMATCH, "NLE_SEQ_MISMATCH"), +) + +const char * +nl_geterror (int err) +{ + const char *s; + + err = nl_errno (err); + + if (err >= _NLE_BASE) { + s = _geterror (err); + if (s) + return s; + } + return g_strerror (err); +} + +/*****************************************************************************/ + +NM_UTILS_ENUM2STR_DEFINE (nl_nlmsgtype2str, int, + NM_UTILS_ENUM2STR (NLMSG_NOOP, "NOOP"), + NM_UTILS_ENUM2STR (NLMSG_ERROR, "ERROR"), + NM_UTILS_ENUM2STR (NLMSG_DONE, "DONE"), + NM_UTILS_ENUM2STR (NLMSG_OVERRUN, "OVERRUN"), +); + +NM_UTILS_FLAGS2STR_DEFINE (nl_nlmsg_flags2str, int, + NM_UTILS_FLAGS2STR (NLM_F_REQUEST, "REQUEST"), + NM_UTILS_FLAGS2STR (NLM_F_MULTI, "MULTI"), + NM_UTILS_FLAGS2STR (NLM_F_ACK, "ACK"), + NM_UTILS_FLAGS2STR (NLM_F_ECHO, "ECHO"), + NM_UTILS_FLAGS2STR (NLM_F_ROOT, "ROOT"), + NM_UTILS_FLAGS2STR (NLM_F_MATCH, "MATCH"), + NM_UTILS_FLAGS2STR (NLM_F_ATOMIC, "ATOMIC"), + NM_UTILS_FLAGS2STR (NLM_F_REPLACE, "REPLACE"), + NM_UTILS_FLAGS2STR (NLM_F_EXCL, "EXCL"), + NM_UTILS_FLAGS2STR (NLM_F_CREATE, "CREATE"), + NM_UTILS_FLAGS2STR (NLM_F_APPEND, "APPEND"), +); + +/*****************************************************************************/ + +const char * +nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len) +{ + const char *b; + const char *s; + guint flags, flags_before; + const char *prefix; + + if (!nm_utils_to_string_buffer_init_null (hdr, &buf, &len)) + return buf; + + b = buf; + + switch (hdr->nlmsg_type) { + case RTM_NEWLINK: s = "RTM_NEWLINK"; break; + case RTM_DELLINK: s = "RTM_DELLINK"; break; + case RTM_NEWADDR: s = "RTM_NEWADDR"; break; + case RTM_DELADDR: s = "RTM_DELADDR"; break; + case RTM_NEWROUTE: s = "RTM_NEWROUTE"; break; + case RTM_DELROUTE: s = "RTM_DELROUTE"; break; + case RTM_NEWQDISC: s = "RTM_NEWQDISC"; break; + case RTM_DELQDISC: s = "RTM_DELQDISC"; break; + case RTM_NEWTFILTER: s = "RTM_NEWTFILTER"; break; + case RTM_DELTFILTER: s = "RTM_DELTFILTER"; break; + case NLMSG_NOOP: s = "NLMSG_NOOP"; break; + case NLMSG_ERROR: s = "NLMSG_ERROR"; break; + case NLMSG_DONE: s = "NLMSG_DONE"; break; + case NLMSG_OVERRUN: s = "NLMSG_OVERRUN"; break; + default: s = NULL; break; + } + + if (s) + nm_utils_strbuf_append_str (&buf, &len, s); + else + nm_utils_strbuf_append (&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type); + + flags = hdr->nlmsg_flags; + + if (!flags) { + nm_utils_strbuf_append_str (&buf, &len, ", flags 0"); + goto flags_done; + } + +#define _F(f, n) \ + G_STMT_START { \ + if (NM_FLAGS_ALL (flags, f)) { \ + flags &= ~(f); \ + nm_utils_strbuf_append (&buf, &len, "%s%s", prefix, n); \ + if (!flags) \ + goto flags_done; \ + prefix = ","; \ + } \ + } G_STMT_END + + prefix = ", flags "; + flags_before = flags; + _F (NLM_F_REQUEST, "request"); + _F (NLM_F_MULTI, "multi"); + _F (NLM_F_ACK, "ack"); + _F (NLM_F_ECHO, "echo"); + _F (NLM_F_DUMP_INTR, "dump_intr"); + _F (0x20 /*NLM_F_DUMP_FILTERED*/, "dump_filtered"); + + if (flags_before != flags) + prefix = ";"; + + switch (hdr->nlmsg_type) { + case RTM_NEWLINK: + case RTM_NEWADDR: + case RTM_NEWROUTE: + case RTM_NEWQDISC: + case RTM_NEWTFILTER: + _F (NLM_F_REPLACE, "replace"); + _F (NLM_F_EXCL, "excl"); + _F (NLM_F_CREATE, "create"); + _F (NLM_F_APPEND, "append"); + break; + case RTM_GETLINK: + case RTM_GETADDR: + case RTM_GETROUTE: + case RTM_DELQDISC: + case RTM_DELTFILTER: + _F (NLM_F_DUMP, "dump"); + _F (NLM_F_ROOT, "root"); + _F (NLM_F_MATCH, "match"); + _F (NLM_F_ATOMIC, "atomic"); + break; + } + +#undef _F + + if (flags_before != flags) + prefix = ";"; + nm_utils_strbuf_append (&buf, &len, "%s0x%04x", prefix, flags); + +flags_done: + + nm_utils_strbuf_append (&buf, &len, ", seq %u", (unsigned) hdr->nlmsg_seq); + + return b; +} + +/*****************************************************************************/ + +struct nlmsghdr * +nlmsg_hdr (struct nl_msg *n) +{ + return n->nm_nlh; +} + +void * +nlmsg_reserve (struct nl_msg *n, size_t len, int pad) +{ + char *buf = (char *) n->nm_nlh; + size_t nlmsg_len = n->nm_nlh->nlmsg_len; + size_t tlen; + + if (len > n->nm_size) + return NULL; + + tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len; + + if ((tlen + nlmsg_len) > n->nm_size) + return NULL; + + buf += nlmsg_len; + n->nm_nlh->nlmsg_len += tlen; + + if (tlen > len) + memset(buf + len, 0, tlen - len); + + return buf; +} + +/*****************************************************************************/ + +static int + get_default_page_size (void) +{ + static int val = 0; + int v; + + if (G_UNLIKELY (val == 0)) { + v = getpagesize (); + g_assert (v > 0); + val = v; + } + return val; +} + +struct nlattr * +nla_reserve (struct nl_msg *msg, int attrtype, int attrlen) +{ + struct nlattr *nla; + int tlen; + + if (attrlen < 0) + return NULL; + + tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen); + + if (tlen > msg->nm_size) + return NULL; + + nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh); + nla->nla_type = attrtype; + nla->nla_len = nla_attr_size(attrlen); + + if (attrlen) + memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); + msg->nm_nlh->nlmsg_len = tlen; + + return nla; +} + +struct nl_msg * +nlmsg_alloc_size (size_t len) +{ + struct nl_msg *nm; + + if (len < sizeof (struct nlmsghdr)) + len = sizeof (struct nlmsghdr); + + nm = g_slice_new0 (struct nl_msg); + + nm->nm_refcnt = 1; + nm->nm_protocol = -1; + nm->nm_size = len; + nm->nm_nlh = g_malloc0 (len); + nm->nm_nlh->nlmsg_len = nlmsg_total_size (0); + return nm; +} + +/** + * Allocate a new netlink message with the default maximum payload size. + * + * Allocates a new netlink message without any further payload. The + * maximum payload size defaults to PAGESIZE or as otherwise specified + * with nlmsg_set_default_size(). + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg * +nlmsg_alloc (void) +{ + return nlmsg_alloc_size (get_default_page_size ()); +} + +/** + * Allocate a new netlink message with maximum payload size specified. + */ +struct nl_msg * +nlmsg_alloc_inherit (struct nlmsghdr *hdr) +{ + struct nl_msg *nm; + + nm = nlmsg_alloc (); + if (hdr) { + struct nlmsghdr *new = nm->nm_nlh; + + new->nlmsg_type = hdr->nlmsg_type; + new->nlmsg_flags = hdr->nlmsg_flags; + new->nlmsg_seq = hdr->nlmsg_seq; + new->nlmsg_pid = hdr->nlmsg_pid; + } + + return nm; +} + +struct nl_msg * +nlmsg_alloc_convert (struct nlmsghdr *hdr) +{ + struct nl_msg *nm; + + nm = nlmsg_alloc_size (NLMSG_ALIGN (hdr->nlmsg_len)); + memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len); + return nm; +} + +struct nl_msg * +nlmsg_alloc_simple (int nlmsgtype, int flags) +{ + struct nlmsghdr nlh = { + .nlmsg_type = nlmsgtype, + .nlmsg_flags = flags, + }; + + return nlmsg_alloc_inherit (&nlh); +} + +int +nlmsg_append (struct nl_msg *n, void *data, size_t len, int pad) +{ + void *tmp; + + tmp = nlmsg_reserve (n, len, pad); + if (tmp == NULL) + return -ENOMEM; + + memcpy(tmp, data, len); + return 0; +} + +int +nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, const struct nla_policy *policy) +{ + if (!nlmsg_valid_hdr(nlh, hdrlen)) + return -NLE_MSG_TOOSHORT; + + return nla_parse (tb, maxtype, nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), policy); +} + +struct nlmsghdr * +nlmsg_put (struct nl_msg *n, uint32_t pid, uint32_t seq, + int type, int payload, int flags) +{ + struct nlmsghdr *nlh; + + if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN) + g_return_val_if_reached (NULL); + + nlh = (struct nlmsghdr *) n->nm_nlh; + nlh->nlmsg_type = type; + nlh->nlmsg_flags = flags; + nlh->nlmsg_pid = pid; + nlh->nlmsg_seq = seq; + + if (payload > 0 && + nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL) + return NULL; + + return nlh; +} + +uint64_t +nla_get_u64 (const struct nlattr *nla) +{ + uint64_t tmp = 0; + + if (nla && nla_len(nla) >= sizeof (tmp)) + memcpy(&tmp, nla_data(nla), sizeof (tmp)); + + return tmp; +} + +size_t +nla_strlcpy (char *dst, const struct nlattr *nla, size_t dstsize) +{ + size_t srclen = nla_len(nla); + const char *src = nla_data(nla); + + if (srclen > 0 && src[srclen - 1] == '\0') + srclen--; + + if (dstsize > 0) { + size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen; + + memset(dst, 0, dstsize); + memcpy(dst, src, len); + } + + return srclen; +} + +int +nla_memcpy (void *dest, const struct nlattr *src, int count) +{ + int minlen; + + if (!src) + return 0; + + minlen = NM_MIN (count, (int) nla_len (src)); + memcpy(dest, nla_data(src), minlen); + + return minlen; +} + +int +nla_put (struct nl_msg *msg, int attrtype, int datalen, const void *data) +{ + struct nlattr *nla; + + nla = nla_reserve(msg, attrtype, datalen); + if (!nla) { + if (datalen < 0) + g_return_val_if_reached (-NLE_BUG); + + return -ENOMEM; + } + + if (datalen > 0) + memcpy (nla_data(nla), data, datalen); + + return 0; +} + +struct nlattr * +nla_find (const struct nlattr *head, int len, int attrtype) +{ + const struct nlattr *nla; + int rem; + + nla_for_each_attr (nla, head, len, rem) { + if (nla_type (nla) == attrtype) + return (struct nlattr*)nla; + } + + return NULL; +} + +void +nla_nest_cancel (struct nl_msg *msg, const struct nlattr *attr) +{ + ssize_t len; + + len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) attr; + if (len < 0) + g_return_if_reached (); + else if (len > 0) { + msg->nm_nlh->nlmsg_len -= len; + memset(nlmsg_tail(msg->nm_nlh), 0, len); + } +} + +struct nlattr * +nla_nest_start (struct nl_msg *msg, int attrtype) +{ + struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh); + + if (nla_put(msg, attrtype, 0, NULL) < 0) + return NULL; + + return start; +} + +static int +_nest_end (struct nl_msg *msg, struct nlattr *start, int keep_empty) +{ + size_t pad, len; + + len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) start; + + if ( len > USHRT_MAX + || (!keep_empty && len == NLA_HDRLEN)) { + /* + * Max nlattr size exceeded or empty nested attribute, trim the + * attribute header again + */ + nla_nest_cancel(msg, start); + + /* Return error only if nlattr size was exceeded */ + return (len == NLA_HDRLEN) ? 0 : -NLE_ATTRSIZE; + } + + start->nla_len = len; + + pad = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len; + if (pad > 0) { + /* + * Data inside attribute does not end at a alignment boundry. + * Pad accordingly and accoun for the additional space in + * the message. nlmsg_reserve() may never fail in this situation, + * the allocate message buffer must be a multiple of NLMSG_ALIGNTO. + */ + if (!nlmsg_reserve(msg, pad, 0)) + g_return_val_if_reached (-NLE_BUG); + } + + return 0; +} + +int +nla_nest_end (struct nl_msg *msg, struct nlattr *start) +{ + return _nest_end (msg, start, 0); +} + +static const uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = { + [NLA_U8] = sizeof (uint8_t), + [NLA_U16] = sizeof (uint16_t), + [NLA_U32] = sizeof (uint32_t), + [NLA_U64] = sizeof (uint64_t), + [NLA_STRING] = 1, + [NLA_FLAG] = 0, +}; + +static int +validate_nla (const struct nlattr *nla, int maxtype, + const struct nla_policy *policy) +{ + const struct nla_policy *pt; + unsigned int minlen = 0; + int type = nla_type(nla); + + if (type < 0 || type > maxtype) + return 0; + + pt = &policy[type]; + + if (pt->type > NLA_TYPE_MAX) + g_return_val_if_reached (-NLE_BUG); + + if (pt->minlen) + minlen = pt->minlen; + else if (pt->type != NLA_UNSPEC) + minlen = nla_attr_minlen[pt->type]; + + if (nla_len(nla) < minlen) + return -NLE_UNSPEC; + + if (pt->maxlen && nla_len(nla) > pt->maxlen) + return -NLE_UNSPEC; + + if (pt->type == NLA_STRING) { + const char *data = nla_data(nla); + if (data[nla_len(nla) - 1] != '\0') + return -NLE_UNSPEC; + } + + return 0; +} + +int +nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, + const struct nla_policy *policy) +{ + struct nlattr *nla; + int rem, err; + + memset(tb, 0, sizeof (struct nlattr *) * (maxtype + 1)); + + nla_for_each_attr(nla, head, len, rem) { + int type = nla_type(nla); + + if (type > maxtype) + continue; + + if (policy) { + err = validate_nla(nla, maxtype, policy); + if (err < 0) + goto errout; + } + + tb[type] = nla; + } + + err = 0; +errout: + return err; +} + +/*****************************************************************************/ + +void nlmsg_free (struct nl_msg *msg) +{ + if (!msg) + return; + + if (msg->nm_refcnt < 1) + g_return_if_reached (); + + msg->nm_refcnt--; + + if (msg->nm_refcnt <= 0) { + g_free (msg->nm_nlh); + g_slice_free (struct nl_msg, msg); + } +} + +int +nlmsg_get_proto (struct nl_msg *msg) +{ + return msg->nm_protocol; +} + +void +nlmsg_set_proto (struct nl_msg *msg, int protocol) +{ + msg->nm_protocol = protocol; +} + +void +nlmsg_set_src (struct nl_msg *msg, struct sockaddr_nl *addr) +{ + memcpy (&msg->nm_src, addr, sizeof (*addr)); +} + +struct ucred * +nlmsg_get_creds (struct nl_msg *msg) +{ + if (msg->nm_flags & NL_MSG_CRED_PRESENT) + return &msg->nm_creds; + return NULL; +} + +void +nlmsg_set_creds (struct nl_msg *msg, struct ucred *creds) +{ + memcpy (&msg->nm_creds, creds, sizeof (*creds)); + msg->nm_flags |= NL_MSG_CRED_PRESENT; +} + +/*****************************************************************************/ + +void * +genlmsg_put (struct nl_msg *msg, uint32_t port, uint32_t seq, int family, + int hdrlen, int flags, uint8_t cmd, uint8_t version) +{ + struct nlmsghdr *nlh; + struct genlmsghdr hdr = { + .cmd = cmd, + .version = version, + }; + + nlh = nlmsg_put (msg, port, seq, family, GENL_HDRLEN + hdrlen, flags); + if (nlh == NULL) + return NULL; + + memcpy (nlmsg_data (nlh), &hdr, sizeof (hdr)); + + return (char *) nlmsg_data (nlh) + GENL_HDRLEN; +} + +void * +genlmsg_data (const struct genlmsghdr *gnlh) +{ + return ((unsigned char *) gnlh + GENL_HDRLEN); +} + +void * +genlmsg_user_hdr (const struct genlmsghdr *gnlh) +{ + return genlmsg_data (gnlh); +} + +struct genlmsghdr * +genlmsg_hdr (struct nlmsghdr *nlh) +{ + return nlmsg_data (nlh); +} + +void * +genlmsg_user_data (const struct genlmsghdr *gnlh, const int hdrlen) +{ + return (char *) genlmsg_user_hdr (gnlh) + NLMSG_ALIGN (hdrlen); +} + +struct nlattr * +genlmsg_attrdata (const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_user_data (gnlh, hdrlen); +} + +int +genlmsg_len (const struct genlmsghdr *gnlh) +{ + const struct nlmsghdr *nlh; + + nlh = (const struct nlmsghdr *) ((const unsigned char *) gnlh - NLMSG_HDRLEN); + return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); +} + +int +genlmsg_attrlen (const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_len (gnlh) - NLMSG_ALIGN (hdrlen); +} + +int +genlmsg_valid_hdr (struct nlmsghdr *nlh, int hdrlen) +{ + struct genlmsghdr *ghdr; + + if (!nlmsg_valid_hdr (nlh, GENL_HDRLEN)) + return 0; + + ghdr = nlmsg_data (nlh); + if (genlmsg_len (ghdr) < NLMSG_ALIGN (hdrlen)) + return 0; + + return 1; +} + +int +genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, const struct nla_policy *policy) +{ + struct genlmsghdr *ghdr; + + if (!genlmsg_valid_hdr (nlh, hdrlen)) + return -NLE_MSG_TOOSHORT; + + ghdr = nlmsg_data (nlh); + return nla_parse (tb, maxtype, genlmsg_attrdata (ghdr, hdrlen), + genlmsg_attrlen (ghdr, hdrlen), policy); +} + +static int +_genl_parse_getfamily (struct nl_msg *msg, void *arg) +{ + static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { + [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, + [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING, + .maxlen = GENL_NAMSIZ }, + [CTRL_ATTR_VERSION] = { .type = NLA_U32 }, + [CTRL_ATTR_HDRSIZE] = { .type = NLA_U32 }, + [CTRL_ATTR_MAXATTR] = { .type = NLA_U32 }, + [CTRL_ATTR_OPS] = { .type = NLA_NESTED }, + [CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED }, + }; + struct nlattr *tb[CTRL_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr (msg); + gint32 *response_data = arg; + + if (genlmsg_parse (nlh, 0, tb, CTRL_ATTR_MAX, ctrl_policy)) + return NL_SKIP; + + if (tb[CTRL_ATTR_FAMILY_ID]) + *response_data = nla_get_u16 (tb[CTRL_ATTR_FAMILY_ID]); + + return NL_STOP; +} + +int +genl_ctrl_resolve (struct nl_sock *sk, const char *name) +{ + nm_auto_nlmsg struct nl_msg *msg = NULL; + int result = -ENOMEM; + gint32 response_data = -1; + const struct nl_cb cb = { + .valid_cb = _genl_parse_getfamily, + .valid_arg = &response_data, + }; + + msg = nlmsg_alloc (); + + if (!genlmsg_put (msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, + 0, 0, CTRL_CMD_GETFAMILY, 1)) + goto out; + + if (nla_put_string (msg, CTRL_ATTR_FAMILY_NAME, name) < 0) + goto out; + + result = nl_send_auto (sk, msg); + if (result < 0) + goto out; + + result = nl_recvmsgs (sk, &cb); + if (result < 0) + goto out; + + /* If search was successful, request may be ACKed after data */ + result = nl_wait_for_ack (sk, NULL); + if (result < 0) + goto out; + + if (response_data > 0) + result = response_data; + else + result = -ENOENT; + +out: + return result; +} + +/*****************************************************************************/ + +struct nl_sock * +nl_socket_alloc (void) +{ + struct nl_sock *sk; + + sk = g_slice_new0 (struct nl_sock); + + sk->s_fd = -1; + sk->s_local.nl_family = AF_NETLINK; + sk->s_peer.nl_family = AF_NETLINK; + sk->s_seq_expect = sk->s_seq_next = time(NULL); + + return sk; +} + +void +nl_socket_free (struct nl_sock *sk) +{ + if (!sk) + return; + + if (sk->s_fd >= 0) + nm_close (sk->s_fd); + g_slice_free (struct nl_sock, sk); +} + +int +nl_socket_get_fd (const struct nl_sock *sk) +{ + return sk->s_fd; +} + +uint32_t +nl_socket_get_local_port (const struct nl_sock *sk) +{ + return sk->s_local.nl_pid; +} + +size_t +nl_socket_get_msg_buf_size (struct nl_sock *sk) +{ + return sk->s_bufsize; +} + +int +nl_socket_set_passcred (struct nl_sock *sk, int state) +{ + int err; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + err = setsockopt (sk->s_fd, SOL_SOCKET, SO_PASSCRED, + &state, sizeof (state)); + if (err < 0) + return -nl_syserr2nlerr (errno); + + if (state) + sk->s_flags |= NL_SOCK_PASSCRED; + else + sk->s_flags &= ~NL_SOCK_PASSCRED; + + return 0; +} + +int +nl_socket_set_msg_buf_size (struct nl_sock *sk, size_t bufsize) +{ + sk->s_bufsize = bufsize; + + return 0; +} + +struct sockaddr_nl * +nlmsg_get_dst (struct nl_msg *msg) +{ + return &msg->nm_dst; +} + +int +nl_socket_set_nonblocking (const struct nl_sock *sk) +{ + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0) + return -nl_syserr2nlerr (errno); + + return 0; +} + +int +nl_socket_set_buffer_size (struct nl_sock *sk, int rxbuf, int txbuf) +{ + int err; + + if (rxbuf <= 0) + rxbuf = 32768; + + if (txbuf <= 0) + txbuf = 32768; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + err = setsockopt (sk->s_fd, SOL_SOCKET, SO_SNDBUF, + &txbuf, sizeof (txbuf)); + if (err < 0) { + return -nl_syserr2nlerr (errno); + } + + err = setsockopt (sk->s_fd, SOL_SOCKET, SO_RCVBUF, + &rxbuf, sizeof (rxbuf)); + if (err < 0) { + return -nl_syserr2nlerr (errno); + } + + return 0; +} + +int +nl_socket_add_memberships (struct nl_sock *sk, int group, ...) +{ + int err; + va_list ap; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + va_start(ap, group); + + while (group != 0) { + if (group < 0) { + va_end(ap); + g_return_val_if_reached (-NLE_BUG); + } + + err = setsockopt (sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &group, sizeof (group)); + if (err < 0) { + va_end(ap); + return -nl_syserr2nlerr (errno); + } + + group = va_arg(ap, int); + } + + va_end(ap); + + return 0; +} + +int +nl_socket_set_ext_ack (struct nl_sock *sk, gboolean enable) +{ + int err, val; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + val = !!enable; + err = setsockopt (sk->s_fd, SOL_NETLINK, NETLINK_EXT_ACK, &val, sizeof (val)); + if (err < 0) + return -nl_syserr2nlerr (errno); + + return 0; +} + +void nl_socket_disable_msg_peek (struct nl_sock *sk) +{ + sk->s_flags |= NL_MSG_PEEK_EXPLICIT; + sk->s_flags &= ~NL_MSG_PEEK; +} + +int +nl_connect (struct nl_sock *sk, int protocol) +{ + int err; + socklen_t addrlen; + struct sockaddr_nl local = { 0 }; + + if (sk->s_fd != -1) + return -NLE_BAD_SOCK; + + sk->s_fd = socket (AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol); + if (sk->s_fd < 0) { + err = -nl_syserr2nlerr (errno); + goto errout; + } + + err = nl_socket_set_buffer_size(sk, 0, 0); + if (err < 0) + goto errout; + + nm_assert (sk->s_local.nl_pid == 0); + + err = bind (sk->s_fd, (struct sockaddr*) &sk->s_local, + sizeof (sk->s_local)); + if (err != 0) { + err = -nl_syserr2nlerr (errno); + goto errout; + } + + addrlen = sizeof (local); + err = getsockname (sk->s_fd, (struct sockaddr *) &local, + &addrlen); + if (err < 0) { + err = -nl_syserr2nlerr (errno); + goto errout; + } + + if (addrlen != sizeof (local)) { + err = -NLE_UNSPEC; + goto errout; + } + + if (local.nl_family != AF_NETLINK) { + err = -NLE_UNSPEC; + goto errout; + } + + sk->s_local = local; + sk->s_proto = protocol; + + return 0; + +errout: + if (sk->s_fd != -1) { + close(sk->s_fd); + sk->s_fd = -1; + } + return err; +} + +/*****************************************************************************/ + +static void +_cb_init (struct nl_cb *dst, const struct nl_cb *src) +{ + nm_assert (dst); + + if (src) + *dst = *src; + else + memset (dst, 0, sizeof (*dst)); +} + +static int ack_wait_handler(struct nl_msg *msg, void *arg) +{ + return NL_STOP; +} + +int +nl_wait_for_ack (struct nl_sock *sk, + const struct nl_cb *cb) +{ + struct nl_cb cb2; + + _cb_init (&cb2, cb); + cb2.ack_cb = ack_wait_handler; + return nl_recvmsgs (sk, &cb2); +} + +#define NL_CB_CALL(cb, type, msg) \ +do { \ + const struct nl_cb *_cb = (cb); \ + \ + if (_cb->type##_cb) { \ + err = _cb->type##_cb ((msg), _cb->type##_arg); \ + switch (err) { \ + case NL_OK: \ + err = 0; \ + break; \ + case NL_SKIP: \ + goto skip; \ + case NL_STOP: \ + goto stop; \ + default: \ + goto out; \ + } \ + } \ +} while (0) + +int +nl_recvmsgs (struct nl_sock *sk, const struct nl_cb *cb) +{ + int n, err = 0, multipart = 0, interrupted = 0, nrecv = 0; + gs_free unsigned char *buf = NULL; + struct nlmsghdr *hdr; + struct sockaddr_nl nla = { 0 }; + gs_free struct ucred *creds = NULL; + +continue_reading: + n = nl_recv (sk, &nla, &buf, &creds); + if (n <= 0) + return n; + + hdr = (struct nlmsghdr *) buf; + while (nlmsg_ok (hdr, n)) { + nm_auto_nlmsg struct nl_msg *msg = NULL; + + msg = nlmsg_alloc_convert (hdr); + + nlmsg_set_proto (msg, sk->s_proto); + nlmsg_set_src (msg, &nla); + if (creds) + nlmsg_set_creds (msg, creds); + + nrecv++; + + /* Only do sequence checking if auto-ack mode is enabled */ + if (!(sk->s_flags & NL_NO_AUTO_ACK)) { + if (hdr->nlmsg_seq != sk->s_seq_expect) { + err = -NLE_SEQ_MISMATCH; + goto out; + } + } + + if (hdr->nlmsg_type == NLMSG_DONE || + hdr->nlmsg_type == NLMSG_ERROR || + hdr->nlmsg_type == NLMSG_NOOP || + hdr->nlmsg_type == NLMSG_OVERRUN) { + /* We can't check for !NLM_F_MULTI since some netlink + * users in the kernel are broken. */ + sk->s_seq_expect++; + } + + if (hdr->nlmsg_flags & NLM_F_MULTI) + multipart = 1; + + if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { + /* + * We have to continue reading to clear + * all messages until a NLMSG_DONE is + * received and report the inconsistency. + */ + interrupted = 1; + } + + /* messages terminates a multipart message, this is + * usually the end of a message and therefore we slip + * out of the loop by default. the user may overrule + * this action by skipping this packet. */ + if (hdr->nlmsg_type == NLMSG_DONE) { + multipart = 0; + NL_CB_CALL(cb, finish, msg); + } + + /* Message to be ignored, the default action is to + * skip this message if no callback is specified. The + * user may overrule this action by returning + * NL_PROCEED. */ + else if (hdr->nlmsg_type == NLMSG_NOOP) + goto skip; + + /* Data got lost, report back to user. The default action is to + * quit parsing. The user may overrule this action by retuning + * NL_SKIP or NL_PROCEED (dangerous) */ + else if (hdr->nlmsg_type == NLMSG_OVERRUN) { + err = -NLE_MSG_OVERFLOW; + goto out; + } + + /* Message carries a nlmsgerr */ + else if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *e = nlmsg_data(hdr); + + if (hdr->nlmsg_len < nlmsg_size(sizeof (*e))) { + /* Truncated error message, the default action + * is to stop parsing. The user may overrule + * this action by returning NL_SKIP or + * NL_PROCEED (dangerous) */ + err = -NLE_MSG_TRUNC; + goto out; + } + if (e->error) { + /* Error message reported back from kernel. */ + if (cb->err_cb) { + err = cb->err_cb (&nla, e, + cb->err_arg); + if (err < 0) + goto out; + else if (err == NL_SKIP) + goto skip; + else if (err == NL_STOP) { + err = -e->error; + goto out; + } + } else { + err = -e->error; + goto out; + } + } else + NL_CB_CALL(cb, ack, msg); + } else { + /* Valid message (not checking for MULTIPART bit to + * get along with broken kernels. NL_SKIP has no + * effect on this. */ + NL_CB_CALL(cb, valid, msg); + } +skip: + err = 0; + hdr = nlmsg_next(hdr, &n); + } + + if (multipart) { + /* Multipart message not yet complete, continue reading */ + nm_clear_g_free (&creds); + nm_clear_g_free (&buf); + + goto continue_reading; + } + +stop: + err = 0; + +out: + if (interrupted) + err = -NLE_DUMP_INTR; + + return err ?: nrecv; +} + +int +nl_sendmsg (struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) +{ + int ret; + + if (sk->s_fd < 0) + return -NLE_BAD_SOCK; + + nlmsg_set_src (msg, &sk->s_local); + + ret = sendmsg(sk->s_fd, hdr, 0); + if (ret < 0) + return -nl_syserr2nlerr (errno); + + return ret; +} + +int +nl_send_iovec (struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen) +{ + struct sockaddr_nl *dst; + struct ucred *creds; + struct msghdr hdr = { + .msg_name = (void *) &sk->s_peer, + .msg_namelen = sizeof (struct sockaddr_nl), + .msg_iov = iov, + .msg_iovlen = iovlen, + }; + char buf[CMSG_SPACE(sizeof (struct ucred))]; + + /* Overwrite destination if specified in the message itself, defaults + * to the peer address of the socket. + */ + dst = nlmsg_get_dst(msg); + if (dst->nl_family == AF_NETLINK) + hdr.msg_name = dst; + + /* Add credentials if present. */ + creds = nlmsg_get_creds(msg); + if (creds != NULL) { + struct cmsghdr *cmsg; + + hdr.msg_control = buf; + hdr.msg_controllen = sizeof (buf); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof (struct ucred)); + memcpy(CMSG_DATA(cmsg), creds, sizeof (struct ucred)); + } + + return nl_sendmsg(sk, msg, &hdr); +} + +void +nl_complete_msg (struct nl_sock *sk, struct nl_msg *msg) +{ + struct nlmsghdr *nlh; + + nlh = nlmsg_hdr(msg); + if (nlh->nlmsg_pid == NL_AUTO_PORT) + nlh->nlmsg_pid = nl_socket_get_local_port(sk); + + if (nlh->nlmsg_seq == NL_AUTO_SEQ) + nlh->nlmsg_seq = sk->s_seq_next++; + + if (msg->nm_protocol == -1) + msg->nm_protocol = sk->s_proto; + + nlh->nlmsg_flags |= NLM_F_REQUEST; + + if (!(sk->s_flags & NL_NO_AUTO_ACK)) + nlh->nlmsg_flags |= NLM_F_ACK; +} + +int +nl_send (struct nl_sock *sk, struct nl_msg *msg) +{ + struct iovec iov = { + .iov_base = (void *) nlmsg_hdr(msg), + .iov_len = nlmsg_hdr(msg)->nlmsg_len, + }; + + return nl_send_iovec(sk, msg, &iov, 1); +} + +int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg) +{ + nl_complete_msg(sk, msg); + + return nl_send(sk, msg); +} + +int +nl_recv (struct nl_sock *sk, struct sockaddr_nl *nla, + unsigned char **buf, struct ucred **creds) +{ + ssize_t n; + int flags = 0; + static int page_size = 0; + struct iovec iov; + struct msghdr msg = { + .msg_name = (void *) nla, + .msg_namelen = sizeof (struct sockaddr_nl), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + gs_free struct ucred* tmpcreds = NULL; + int retval; + + nm_assert (nla); + nm_assert (buf && !*buf); + nm_assert (!creds || !*creds); + + if ( (sk->s_flags & NL_MSG_PEEK) + || ( !(sk->s_flags & NL_MSG_PEEK_EXPLICIT) + && sk->s_bufsize == 0)) + flags |= MSG_PEEK | MSG_TRUNC; + + if (page_size == 0) + page_size = getpagesize() * 4; + + iov.iov_len = sk->s_bufsize ? : page_size; + iov.iov_base = g_malloc (iov.iov_len); + + if ( creds + && (sk->s_flags & NL_SOCK_PASSCRED)) { + msg.msg_controllen = CMSG_SPACE (sizeof (struct ucred)); + msg.msg_control = g_malloc (msg.msg_controllen); + } + +retry: + n = recvmsg(sk->s_fd, &msg, flags); + if (!n) { + retval = 0; + goto abort; + } + + if (n < 0) { + if (errno == EINTR) + goto retry; + + retval = -nl_syserr2nlerr (errno); + goto abort; + } + + if (msg.msg_flags & MSG_CTRUNC) { + if (msg.msg_controllen == 0) { + retval = -NLE_MSG_TRUNC; + goto abort; + } + + msg.msg_controllen *= 2; + msg.msg_control = g_realloc (msg.msg_control, msg.msg_controllen); + goto retry; + } + + if ( iov.iov_len < n + || (msg.msg_flags & MSG_TRUNC)) { + /* respond with error to an incomplete message */ + if (flags == 0) { + retval = -NLE_MSG_TRUNC; + goto abort; + } + + /* Provided buffer is not long enough, enlarge it + * to size of n (which should be total length of the message) + * and try again. */ + iov.iov_base = g_realloc (iov.iov_base, n); + iov.iov_len = n; + flags = 0; + goto retry; + } + + if (flags != 0) { + /* Buffer is big enough, do the actual reading */ + flags = 0; + goto retry; + } + + if (msg.msg_namelen != sizeof (struct sockaddr_nl)) { + retval = -NLE_UNSPEC; + goto abort; + } + + if (creds && (sk->s_flags & NL_SOCK_PASSCRED)) { + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + if (cmsg->cmsg_type != SCM_CREDENTIALS) + continue; + tmpcreds = g_memdup (CMSG_DATA(cmsg), sizeof (*tmpcreds)); + break; + } + } + + retval = n; + +abort: + g_free (msg.msg_control); + + if (retval <= 0) { + g_free (iov.iov_base); + return retval; + } + + *buf = iov.iov_base; + NM_SET_OUT (creds, g_steal_pointer (&tmpcreds)); + return retval; +} diff --git a/src/platform/nm-netlink.h b/src/platform/nm-netlink.h new file mode 100644 index 00000000..c0dc09c4 --- /dev/null +++ b/src/platform/nm-netlink.h @@ -0,0 +1,511 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-platform.c - Handle runtime kernel networking configuration + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#ifndef __NM_NETLINK_H__ +#define __NM_NETLINK_H__ + +#include +#include +#include + +/*****************************************************************************/ +#define _NLE_BASE 100000 +#define NLE_UNSPEC (_NLE_BASE + 0) +#define NLE_BUG (_NLE_BASE + 1) +#define NLE_NATIVE_ERRNO (_NLE_BASE + 2) +#define NLE_SEQ_MISMATCH (_NLE_BASE + 3) +#define NLE_MSG_TRUNC (_NLE_BASE + 4) +#define NLE_MSG_TOOSHORT (_NLE_BASE + 5) +#define NLE_DUMP_INTR (_NLE_BASE + 6) +#define NLE_ATTRSIZE (_NLE_BASE + 7) +#define NLE_BAD_SOCK (_NLE_BASE + 8) +#define NLE_NOADDR (_NLE_BASE + 9) +#define NLE_MSG_OVERFLOW (_NLE_BASE + 10) + +#define _NLE_BASE_END (_NLE_BASE + 11) + +#define NLMSGERR_ATTR_UNUSED 0 +#define NLMSGERR_ATTR_MSG 1 +#define NLMSGERR_ATTR_OFFS 2 +#define NLMSGERR_ATTR_COOKIE 3 +#define NLMSGERR_ATTR_MAX 3 + +#ifndef NLM_F_ACK_TLVS +#define NLM_F_ACK_TLVS 0x200 +#endif + +static inline int +nl_errno (int err) +{ + /* the error codes from our netlink implementation are plain errno + * extended with our own error in a particular range starting from + * _NLE_BASE. + * + * However, often we encode errors as negative values. This function + * normalizes the error and returns its positive value. */ + return err >= 0 + ? err + : ((err == G_MININT) ? NLE_BUG : -errno); +} + +static inline int +nl_syserr2nlerr (int err) +{ + if (err == G_MININT) + return NLE_NATIVE_ERRNO; + if (err < 0) + err = -err; + return (err >= _NLE_BASE && err < _NLE_BASE_END) + ? NLE_NATIVE_ERRNO + : err; +} + +const char *nl_geterror (int err); + +/*****************************************************************************/ + +/* Basic attribute data types */ +enum { + NLA_UNSPEC, /* Unspecified type, binary data chunk */ + NLA_U8, /* 8 bit integer */ + NLA_U16, /* 16 bit integer */ + NLA_U32, /* 32 bit integer */ + NLA_U64, /* 64 bit integer */ + NLA_STRING, /* NUL terminated character string */ + NLA_FLAG, /* Flag */ + NLA_MSECS, /* Micro seconds (64bit) */ + NLA_NESTED, /* Nested attributes */ + NLA_NESTED_COMPAT, + NLA_NUL_STRING, + NLA_BINARY, + NLA_S8, + NLA_S16, + NLA_S32, + NLA_S64, + __NLA_TYPE_MAX, +}; + +#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1) + +struct nl_msg; + +/*****************************************************************************/ + +const char *nl_nlmsgtype2str (int type, char *buf, size_t size); + +const char *nl_nlmsg_flags2str (int flags, char *buf, size_t len); + +const char *nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len); + +/*****************************************************************************/ + +struct nla_policy { + /* Type of attribute or NLA_UNSPEC */ + uint16_t type; + + /* Minimal length of payload required */ + uint16_t minlen; + + /* Maximal length of payload allowed */ + uint16_t maxlen; +}; + +/*****************************************************************************/ + +static inline int +nla_attr_size(int payload) +{ + nm_assert (payload >= 0); + + return NLA_HDRLEN + payload; +} + +static inline int +nla_total_size (int payload) +{ + return NLA_ALIGN (nla_attr_size (payload)); +} + +static inline int +nla_padlen (int payload) +{ + return nla_total_size(payload) - nla_attr_size(payload); +} + +struct nlattr *nla_reserve (struct nl_msg *msg, int attrtype, int attrlen); + +static inline int +nla_len (const struct nlattr *nla) +{ + return nla->nla_len - NLA_HDRLEN; +} + +static inline int +nla_type (const struct nlattr *nla) +{ + return nla->nla_type & NLA_TYPE_MASK; +} + +static inline void * +nla_data (const struct nlattr *nla) +{ + nm_assert (nla); + return (char *) nla + NLA_HDRLEN; +} + +static inline uint8_t +nla_get_u8 (const struct nlattr *nla) +{ + return *(const uint8_t *) nla_data (nla); +} + +static inline uint8_t +nla_get_u8_cond (/*const*/ struct nlattr *const*tb, int attr, uint8_t default_val) +{ + nm_assert (tb); + nm_assert (attr >= 0); + + return tb[attr] ? nla_get_u8 (tb[attr]) : default_val; +} + +static inline uint16_t +nla_get_u16 (const struct nlattr *nla) +{ + return *(const uint16_t *) nla_data (nla); +} + +static inline uint32_t +nla_get_u32(const struct nlattr *nla) +{ + return *(const uint32_t *) nla_data (nla); +} + +uint64_t nla_get_u64 (const struct nlattr *nla); + +static inline char * +nla_get_string (const struct nlattr *nla) +{ + return (char *) nla_data (nla); +} + +size_t nla_strlcpy (char *dst, const struct nlattr *nla, size_t dstsize); + +int nla_memcpy (void *dest, const struct nlattr *src, int count); + +int nla_put (struct nl_msg *msg, int attrtype, int datalen, const void *data); + +static inline int +nla_put_string (struct nl_msg *msg, int attrtype, const char *str) +{ + return nla_put(msg, attrtype, strlen(str) + 1, str); +} + +#define NLA_PUT(msg, attrtype, attrlen, data) \ + do { \ + if (nla_put(msg, attrtype, attrlen, data) < 0) \ + goto nla_put_failure; \ + } while(0) + +#define NLA_PUT_TYPE(msg, type, attrtype, value) \ + do { \ + type __tmp = value; \ + NLA_PUT(msg, attrtype, sizeof(type), &__tmp); \ + } while(0) + +#define NLA_PUT_U8(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint8_t, attrtype, value) + +#define NLA_PUT_U16(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint16_t, attrtype, value) + +#define NLA_PUT_U32(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint32_t, attrtype, value) + +#define NLA_PUT_U64(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint64_t, attrtype, value) + +#define NLA_PUT_STRING(msg, attrtype, value) \ + NLA_PUT(msg, attrtype, (int) strlen(value) + 1, value) + +struct nlattr *nla_find (const struct nlattr *head, int len, int attrtype); + +static inline int +nla_ok (const struct nlattr *nla, int remaining) +{ + return remaining >= sizeof(*nla) && + nla->nla_len >= sizeof(*nla) && + nla->nla_len <= remaining; +} + +static inline struct nlattr * +nla_next(const struct nlattr *nla, int *remaining) +{ + int totlen = NLA_ALIGN(nla->nla_len); + + *remaining -= totlen; + return (struct nlattr *) ((char *) nla + totlen); +} + +#define nla_for_each_attr(pos, head, len, rem) \ + for (pos = head, rem = len; \ + nla_ok(pos, rem); \ + pos = nla_next(pos, &(rem))) + +#define nla_for_each_nested(pos, nla, rem) \ + for (pos = (struct nlattr *) nla_data(nla), rem = nla_len(nla); \ + nla_ok(pos, rem); \ + pos = nla_next(pos, &(rem))) + +void nla_nest_cancel (struct nl_msg *msg, const struct nlattr *attr); +struct nlattr *nla_nest_start (struct nl_msg *msg, int attrtype); +int nla_nest_end (struct nl_msg *msg, struct nlattr *start); + +int nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, + const struct nla_policy *policy); + +static inline int +nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla, + const struct nla_policy *policy) +{ + return nla_parse (tb, maxtype, nla_data(nla), nla_len(nla), policy); +} + +/*****************************************************************************/ + +struct nl_msg *nlmsg_alloc (void); + +struct nl_msg *nlmsg_alloc_size (size_t max); + +struct nl_msg *nlmsg_alloc_inherit (struct nlmsghdr *hdr); + +struct nl_msg *nlmsg_alloc_convert (struct nlmsghdr *hdr); + +struct nl_msg *nlmsg_alloc_simple (int nlmsgtype, int flags); + +void *nlmsg_reserve (struct nl_msg *n, size_t len, int pad); + +int nlmsg_append (struct nl_msg *n, void *data, size_t len, int pad); + +void nlmsg_free (struct nl_msg *msg); + +static inline int +nlmsg_size (int payload) +{ + nm_assert (payload >= 0 && payload < G_MAXINT - NLMSG_HDRLEN - 4); + return NLMSG_HDRLEN + payload; +} + +static inline int +nlmsg_total_size (int payload) +{ + return NLMSG_ALIGN (nlmsg_size (payload)); +} + +static inline int +nlmsg_ok (const struct nlmsghdr *nlh, int remaining) +{ + return (remaining >= (int)sizeof(struct nlmsghdr) && + nlh->nlmsg_len >= sizeof(struct nlmsghdr) && + nlh->nlmsg_len <= remaining); +} + +static inline struct nlmsghdr * +nlmsg_next (struct nlmsghdr *nlh, int *remaining) +{ + int totlen = NLMSG_ALIGN(nlh->nlmsg_len); + + *remaining -= totlen; + + return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); +} + +int nlmsg_get_proto (struct nl_msg *msg); +void nlmsg_set_proto (struct nl_msg *msg, int protocol); + +void nlmsg_set_src (struct nl_msg *msg, struct sockaddr_nl *addr); + +struct ucred *nlmsg_get_creds (struct nl_msg *msg); +void nlmsg_set_creds (struct nl_msg *msg, struct ucred *creds); + +static inline void +_nm_auto_nl_msg_cleanup (struct nl_msg **ptr) +{ + nlmsg_free (*ptr); +} +#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup) + +static inline void * +nlmsg_data (const struct nlmsghdr *nlh) +{ + return (unsigned char *) nlh + NLMSG_HDRLEN; +} + +static inline void * +nlmsg_tail (const struct nlmsghdr *nlh) +{ + return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len); +} + +struct nlmsghdr *nlmsg_hdr (struct nl_msg *n); + +static inline int +nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen) +{ + if (nlh->nlmsg_len < nlmsg_size (hdrlen)) + return 0; + + return 1; +} + +static inline int +nlmsg_datalen (const struct nlmsghdr *nlh) +{ + return nlh->nlmsg_len - NLMSG_HDRLEN; +} + +static inline int +nlmsg_attrlen (const struct nlmsghdr *nlh, int hdrlen) +{ + return NM_MAX ((int) (nlmsg_datalen (nlh) - NLMSG_ALIGN (hdrlen)), 0); +} + +static inline struct nlattr * +nlmsg_attrdata (const struct nlmsghdr *nlh, int hdrlen) +{ + unsigned char *data = nlmsg_data(nlh); + return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); +} + +static inline struct nlattr * +nlmsg_find_attr (struct nlmsghdr *nlh, int hdrlen, int attrtype) +{ + return nla_find (nlmsg_attrdata (nlh, hdrlen), + nlmsg_attrlen (nlh, hdrlen), + attrtype); +} + +int nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, const struct nla_policy *policy); + +struct nlmsghdr *nlmsg_put (struct nl_msg *n, uint32_t pid, uint32_t seq, + int type, int payload, int flags); + +/*****************************************************************************/ + +#define NL_AUTO_PORT 0 +#define NL_AUTO_SEQ 0 + +struct nl_sock; + +struct nl_sock *nl_socket_alloc (void); + +void nl_socket_free (struct nl_sock *sk); + +int nl_socket_get_fd (const struct nl_sock *sk); + +struct sockaddr_nl *nlmsg_get_dst (struct nl_msg *msg); + +size_t nl_socket_get_msg_buf_size (struct nl_sock *sk); +int nl_socket_set_msg_buf_size (struct nl_sock *sk, size_t bufsize); + +int nl_socket_set_buffer_size (struct nl_sock *sk, int rxbuf, int txbuf); + +int nl_socket_set_passcred (struct nl_sock *sk, int state); + +int nl_socket_set_nonblocking (const struct nl_sock *sk); + +void nl_socket_disable_msg_peek (struct nl_sock *sk); + +uint32_t nl_socket_get_local_port (const struct nl_sock *sk); + +int nl_socket_add_memberships (struct nl_sock *sk, int group, ...); + +int nl_connect (struct nl_sock *sk, int protocol); + +int nl_recv (struct nl_sock *sk, struct sockaddr_nl *nla, + unsigned char **buf, struct ucred **creds); + +int nl_send (struct nl_sock *sk, struct nl_msg *msg); + +int nl_send_auto (struct nl_sock *sk, struct nl_msg *msg); + +/*****************************************************************************/ + +enum nl_cb_action { + /* Proceed with wathever would come next */ + NL_OK, + /* Skip this message */ + NL_SKIP, + /* Stop parsing altogether and discard remaining messages */ + NL_STOP, +}; + +typedef int (*nl_recvmsg_msg_cb_t) (struct nl_msg *msg, void *arg); + +typedef int (*nl_recvmsg_err_cb_t) (struct sockaddr_nl *nla, + struct nlmsgerr *nlerr, void *arg); + +struct nl_cb { + nl_recvmsg_msg_cb_t valid_cb; + void * valid_arg; + + nl_recvmsg_msg_cb_t finish_cb; + void * finish_arg; + + nl_recvmsg_msg_cb_t ack_cb; + void * ack_arg; + + nl_recvmsg_err_cb_t err_cb; + void * err_arg; +}; + +int nl_sendmsg (struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr); + +int nl_send_iovec (struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen); + +void nl_complete_msg (struct nl_sock *sk, struct nl_msg *msg); + +int nl_recvmsgs (struct nl_sock *sk, const struct nl_cb *cb); + +int nl_wait_for_ack (struct nl_sock *sk, + const struct nl_cb *cb); + +int nl_socket_set_ext_ack (struct nl_sock *sk, gboolean enable); + +/*****************************************************************************/ + +void *genlmsg_put (struct nl_msg *msg, uint32_t port, uint32_t seq, int family, + int hdrlen, int flags, uint8_t cmd, uint8_t version); +void *genlmsg_data (const struct genlmsghdr *gnlh); +void *genlmsg_user_hdr (const struct genlmsghdr *gnlh); +struct genlmsghdr *genlmsg_hdr (struct nlmsghdr *nlh); +void *genlmsg_user_data (const struct genlmsghdr *gnlh, const int hdrlen); +struct nlattr *genlmsg_attrdata (const struct genlmsghdr *gnlh, int hdrlen); +int genlmsg_len (const struct genlmsghdr *gnlh); +int genlmsg_attrlen (const struct genlmsghdr *gnlh, int hdrlen); +int genlmsg_valid_hdr (struct nlmsghdr *nlh, int hdrlen); +int genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, const struct nla_policy *policy); + +int genl_ctrl_resolve (struct nl_sock *sk, const char *name); + +/*****************************************************************************/ + +#endif /* __NM_NETLINK_H__ */ diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c index b664e8a9..114bf4b3 100644 --- a/src/platform/nm-platform-utils.c +++ b/src/platform/nm-platform-utils.c @@ -589,6 +589,8 @@ nmp_utils_ip_config_source_coerce_to_rtprot (NMIPConfigSource source) switch (source) { case NM_IP_CONFIG_SOURCE_KERNEL: return RTPROT_KERNEL; + case NM_IP_CONFIG_SOURCE_IP6LL: + return RTPROT_KERNEL; case NM_IP_CONFIG_SOURCE_DHCP: return RTPROT_DHCP; case NM_IP_CONFIG_SOURCE_NDISC: @@ -656,6 +658,7 @@ nmp_utils_ip_config_source_to_string (NMIPConfigSource source, char *buf, gsize case NM_IP_CONFIG_SOURCE_KERNEL: s = "kernel"; break; case NM_IP_CONFIG_SOURCE_SHARED: s = "shared"; break; case NM_IP_CONFIG_SOURCE_IP4LL: s = "ipv4ll"; break; + case NM_IP_CONFIG_SOURCE_IP6LL: s = "ipv6ll"; break; case NM_IP_CONFIG_SOURCE_PPP: s = "ppp"; break; case NM_IP_CONFIG_SOURCE_WWAN: s = "wwan"; break; case NM_IP_CONFIG_SOURCE_VPN: s = "vpn"; break; diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h index ea25470e..97f5630a 100644 --- a/src/platform/nm-platform-utils.h +++ b/src/platform/nm-platform-utils.h @@ -24,7 +24,6 @@ #include "nm-platform.h" #include "nm-setting-wired.h" - const char *nmp_utils_ethtool_get_driver (int ifindex); gboolean nmp_utils_ethtool_supports_carrier_detect (int ifindex); gboolean nmp_utils_ethtool_supports_vlans (int ifindex); @@ -60,10 +59,8 @@ gboolean nmp_utils_ethtool_get_permanent_address (int ifindex, guint8 *buf, size_t *length); - gboolean nmp_utils_mii_supports_carrier_detect (int ifindex); - struct udev_device; const char *nmp_utils_udev_get_driver (struct udev_device *udevice); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index c7ed90e3..1a94ce28 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -353,6 +353,45 @@ nm_platform_process_events (NMPlatform *self) klass->process_events (self); } +const NMPlatformLink * +nm_platform_process_events_ensure_link (NMPlatform *self, + int ifindex, + const char *ifname) +{ + const NMPObject *obj; + gboolean refreshed = FALSE; + + g_return_val_if_fail (NM_IS_PLATFORM (self), NULL); + + if (ifindex <= 0 && !ifname) + return NULL; + + /* we look into the cache, whether a link for given ifindex/ifname + * exits. If not, we poll the netlink socket, maybe the event + * with the link is waiting. + * + * Then we try again to find the object. + * + * If the link is already cached the first time, we avoid polling + * the netlink socket. */ +again: + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (self), + ifindex, + ifname, + FALSE, /* also invisible. We don't care here whether udev is ready */ + NM_LINK_TYPE_NONE, + NULL, NULL); + if (obj) + return NMP_OBJECT_CAST_LINK (obj); + if (!refreshed) { + refreshed = TRUE; + nm_platform_process_events (self); + goto again; + } + + return NULL; +} + /*****************************************************************************/ /** @@ -597,11 +636,11 @@ nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name) * further by moving children/slaves to the end. */ g_ptr_array_sort_with_data (links, _link_get_all_presort, GINT_TO_POINTER (sort_by_name)); - unseen = g_hash_table_new (g_direct_hash, g_direct_equal); + unseen = g_hash_table_new (nm_direct_hash, NULL); for (i = 0; i < links->len; i++) { item = NMP_OBJECT_CAST_LINK (links->pdata[i]); nm_assert (item->ifindex > 0); - if (!nm_g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL)) + if (!g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL)) nm_assert_not_reached (); } @@ -1117,6 +1156,21 @@ nm_platform_link_supports_slaves (NMPlatform *self, int ifindex) return (nm_platform_link_get_type (self, ifindex) & 0x20000); } +/** + * nm_platform_refresh_all: + * @self: platform instance + * @obj_type: The object type to request. + * + * Resync and re-request all objects from kernel of a certain @obj_type. + */ +void +nm_platform_refresh_all (NMPlatform *self, NMPObjectType obj_type) +{ + _CHECK_SELF_VOID (self, klass); + + klass->refresh_all (self, obj_type); +} + /** * nm_platform_link_refresh: * @self: platform instance @@ -1325,30 +1379,26 @@ gconstpointer nm_platform_link_get_address (NMPlatform *self, int ifindex, size_t *length) { const NMPlatformLink *pllink; - gconstpointer a = NULL; - guint8 l = 0; _CHECK_SELF (self, klass, NULL); - if (length) - *length = 0; - g_return_val_if_fail (ifindex > 0, NULL); pllink = nm_platform_link_get (self, ifindex); - if (pllink && pllink->addr.len > 0) { - if (pllink->addr.len > NM_UTILS_HWADDR_LEN_MAX) { - if (length) - *length = 0; - g_return_val_if_reached (NULL); - } - a = pllink->addr.data; - l = pllink->addr.len; + + if ( !pllink + || pllink->addr.len <= 0) { + NM_SET_OUT (length, 0); + return NULL; } - if (length) - *length = l; - return a; + if (pllink->addr.len > NM_UTILS_HWADDR_LEN_MAX) { + NM_SET_OUT (length, 0); + g_return_val_if_reached (NULL); + } + + NM_SET_OUT (length, pllink->addr.len); + return pllink->addr.data; } /** @@ -1844,6 +1894,12 @@ nm_platform_link_get_lnk_sit (NMPlatform *self, int ifindex, const NMPlatformLin return _link_get_lnk (self, ifindex, NM_LINK_TYPE_SIT, out_link); } +const NMPlatformLnkTun * +nm_platform_link_get_lnk_tun (NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk (self, ifindex, NM_LINK_TYPE_TUN, out_link); +} + const NMPlatformLnkVlan * nm_platform_link_get_lnk_vlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link) { @@ -1990,33 +2046,43 @@ nm_platform_link_vxlan_add (NMPlatform *self, * @vnet_hdr: whether to set the IFF_VNET_HDR flag * @multi_queue: whether to set the IFF_MULTI_QUEUE flag * @out_link: on success, the link object + * @out_fd: (allow-none): if give, return the file descriptor for the + * created device. Note that when creating a non-persistent device, + * this argument is mandatory, otherwise it makes no sense + * to create such an interface. + * The caller is responsible for closing this file descriptor. * * Create a TUN or TAP interface. */ NMPlatformError nm_platform_link_tun_add (NMPlatform *self, const char *name, - gboolean tap, - gint64 owner, - gint64 group, - gboolean pi, - gboolean vnet_hdr, - gboolean multi_queue, - const NMPlatformLink **out_link) + const NMPlatformLnkTun *props, + const NMPlatformLink **out_link, + int *out_fd) { + char b[255]; NMPlatformError plerr; _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (NM_IN_SET (props->type, IFF_TUN, IFF_TAP), NM_PLATFORM_ERROR_BUG); + + /* creating a non-persistant device requires that the caller handles + * the file descriptor. */ + g_return_val_if_fail (props->persist || out_fd, NM_PLATFORM_ERROR_BUG); - plerr = _link_add_check_existing (self, name, tap ? NM_LINK_TYPE_TAP : NM_LINK_TYPE_TUN, out_link); + NM_SET_OUT (out_fd, -1); + + plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_TUN, out_link); if (plerr != NM_PLATFORM_ERROR_SUCCESS) return plerr; - _LOGD ("link: adding %s '%s' owner %" G_GINT64_FORMAT " group %" G_GINT64_FORMAT, - tap ? "tap" : "tun", name, owner, group); - if (!klass->tun_add (self, name, tap, owner, group, pi, vnet_hdr, multi_queue, out_link)) + _LOGD ("link: adding tun '%s' %s", + name, nm_platform_lnk_tun_to_string (props, b, sizeof (b))); + if (!klass->link_tun_add (self, name, props, out_link, out_fd)) return NM_PLATFORM_ERROR_UNSPECIFIED; return NM_PLATFORM_ERROR_SUCCESS; } @@ -2330,7 +2396,6 @@ nm_platform_link_infiniband_delete (NMPlatform *self, return _infiniband_add_add_or_delete (self, parent, p_key, FALSE, NULL); } - gboolean nm_platform_link_infiniband_get_properties (NMPlatform *self, int ifindex, @@ -2620,44 +2685,100 @@ nm_platform_link_veth_get_properties (NMPlatform *self, int ifindex, int *out_pe return TRUE; } +/** + * nm_platform_link_tun_get_properties: + * @self: the #NMPlatform instance + * @ifindex: the ifindex to look up + * @out_properties: (out): (allow-none): return the read properties + * + * Only recent versions of kernel export tun properties via netlink. + * So, if that's the case, then we have the NMPlatformLnkTun instance + * in the platform cache ready to return. Otherwise, this function + * falls back reading sysctl to obtain the tun properties. That + * is racy, because querying sysctl means that the object might + * be already removed from cache (while NM didn't yet process the + * netlink message). + * + * Hence, to lookup the tun properties, you always need to use this + * function, and use it with care knowing that it might obtain its + * data by reading sysctl. Note that we don't want to add this workaround + * to the platform cache itself, because the cache should (mainly) + * contain data from netlink. To access the sysctl side channel, the + * user needs to do explicitly. + * + * Returns: #TRUE, if the properties could be read. */ gboolean -nm_platform_link_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *props) +nm_platform_link_tun_get_properties (NMPlatform *self, + int ifindex, + NMPlatformLnkTun *out_properties) { - nm_auto_close int dirfd = -1; + const NMPObject *plobj; + const NMPObject *pllnk; char ifname[IFNAMSIZ]; + gint64 owner; + gint64 group; gint64 flags; - gboolean success = TRUE; + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (props, FALSE); - memset (props, 0, sizeof (*props)); - props->owner = -1; - props->group = -1; - - dirfd = nm_platform_sysctl_open_netdir (self, ifindex, ifname); - if (dirfd < 0) + /* we consider also invisible links (those that are not yet in udev). */ + plobj = nm_platform_link_get_obj (self, ifindex, FALSE); + if (!plobj) + return FALSE; + if (NMP_OBJECT_CAST_LINK (plobj)->type != NM_LINK_TYPE_TUN) return FALSE; - props->owner = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "owner"), 10, -1, G_MAXINT64, -1); - if (errno) - success = FALSE; + pllnk = plobj->_link.netlink.lnk; + if (pllnk) { + nm_assert (NMP_OBJECT_GET_TYPE (pllnk) == NMP_OBJECT_TYPE_LNK_TUN); + nm_assert (NMP_OBJECT_GET_CLASS (pllnk)->lnk_link_type == NM_LINK_TYPE_TUN); - props->group = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "group"), 10, -1, G_MAXINT64, -1); - if (errno) - success = FALSE; + /* recent kernels expose tun properties via netlink and thus we have them + * in the platform cache. */ + NM_SET_OUT (out_properties, pllnk->lnk_tun); + return TRUE; + } - flags = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "tun_flags"), 16, 0, G_MAXINT64, -1); - if (flags >= 0) { - props->mode = ((flags & (IFF_TUN | IFF_TAP)) == IFF_TUN) ? "tun" : "tap"; - props->no_pi = !!(flags & IFF_NO_PI); - props->vnet_hdr = !!(flags & IFF_VNET_HDR); - props->multi_queue = !!(flags & NM_IFF_MULTI_QUEUE); - } else - success = FALSE; + /* fallback to reading sysctl. */ + { + nm_auto_close int dirfd = -1; - return success; + dirfd = nm_platform_sysctl_open_netdir (self, ifindex, ifname); + if (dirfd < 0) + return FALSE; + + owner = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "owner"), 10, -1, G_MAXUINT32, -2); + if (owner == -2) + return FALSE; + + group = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "group"), 10, -1, G_MAXUINT32, -2); + if (group == -2) + return FALSE; + + flags = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "tun_flags"), 16, 0, G_MAXINT64, -1); + if (flags == -1) + return FALSE; + } + + if (out_properties) { + memset (out_properties, 0, sizeof (*out_properties)); + if (owner != -1) { + out_properties->owner_valid = TRUE; + out_properties->owner = owner; + } + if (group != -1) { + out_properties->group_valid = TRUE; + out_properties->group = group; + } + out_properties->type = (flags & TUN_TYPE_MASK); + out_properties->pi = !(flags & IFF_NO_PI); + out_properties->vnet_hdr = !!(flags & IFF_VNET_HDR); + out_properties->multi_queue = !!(flags & NM_IFF_MULTI_QUEUE); + out_properties->persist = !!(flags & IFF_PERSIST); + } + return TRUE; } gboolean @@ -2932,7 +3053,7 @@ nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel (const NMPObject *ob * @user_data: user data for @predicate * * Returns the result of lookup in a GPtrArray. The result array contains - * references objects from the cache, it's destroy function will unref them. + * references objects from the cache, its destroy function will unref them. * * The user must unref the GPtrArray, which will also unref the NMPObject * elements. @@ -3111,24 +3232,68 @@ nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr addr } static gboolean -array_contains_ip6_address (const GPtrArray *addresses, const NMPlatformIP6Address *address, gint32 now) +_addr_array_clean_expired (int addr_family, int ifindex, GPtrArray *array, guint32 now, GHashTable **idx) { - guint len = addresses ? addresses->len : 0; guint i; + gboolean any_addrs = FALSE; - for (i = 0; i < len; i++) { - NMPlatformIP6Address *candidate = NMP_OBJECT_CAST_IP6_ADDRESS (addresses->pdata[i]); + nm_assert_addr_family (addr_family); + nm_assert (ifindex > 0); + nm_assert (now > 0); + + if (!array) + return FALSE; + + /* remove all addresses that are already expired. */ + for (i = 0; i < array->len; i++) { + const NMPlatformIPAddress *a = NMP_OBJECT_CAST_IP_ADDRESS (array->pdata[i]); - if (IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address) && candidate->plen == address->plen) { - guint32 lifetime, preferred; +#if NM_MORE_ASSERTS > 10 + nm_assert (a); + nm_assert (a->ifindex == ifindex); + { + const NMPObject *o = NMP_OBJECT_UP_CAST (a); + guint j; - if (nm_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred, - now, &lifetime, &preferred)) - return TRUE; + nm_assert (NMP_OBJECT_GET_CLASS (o)->addr_family == addr_family); + for (j = i + 1; j < array->len; j++) { + const NMPObject *o2 = array->pdata[j]; + + nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_GET_TYPE (o2)); + nm_assert (!nmp_object_id_equal (o, o2)); + } + } +#endif + + if ( addr_family == AF_INET6 + && NM_FLAGS_HAS (a->n_ifa_flags, IFA_F_TEMPORARY)) { + /* temporary addresses are never added explicitly by NetworkManager but + * kernel adds them via mngtempaddr flag. + * + * We drop them from this list. */ + goto clear_and_next; + } + + if (!nm_utils_lifetime_get (a->timestamp, a->lifetime, a->preferred, + now, NULL)) + goto clear_and_next; + + if (idx) { + if (G_UNLIKELY (!*idx)) { + *idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal); + } + if (!g_hash_table_add (*idx, (gpointer) NMP_OBJECT_UP_CAST (a))) + nm_assert_not_reached (); } + any_addrs = TRUE; + continue; + +clear_and_next: + nmp_object_unref (g_steal_pointer (&array->pdata[i])); } - return FALSE; + return any_addrs; } static gboolean @@ -3179,7 +3344,7 @@ ip4_addr_subnets_build_index (const GPtrArray *addresses, nm_assert (addresses && addresses->len); - subnets = g_hash_table_new (NULL, NULL); + subnets = g_hash_table_new (nm_direct_hash, NULL); /* Build a hash table of all addresses per subnet */ for (i = 0; i < addresses->len; i++) { @@ -3312,39 +3477,8 @@ nm_platform_ip4_address_sync (NMPlatform *self, _CHECK_SELF (self, klass, FALSE); - if (known_addresses) { - /* remove all addresses that are already expired. */ - for (i = 0; i < known_addresses->len; i++) { - const NMPObject *o; - - o = known_addresses->pdata[i]; - nm_assert (o); - - known_address = NMP_OBJECT_CAST_IP4_ADDRESS (known_addresses->pdata[i]); - - if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, - now, &lifetime, &preferred)) - goto delete_and_next; - - if (G_UNLIKELY (!known_addresses_idx)) { - known_addresses_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal); - } - if (!nm_g_hash_table_insert (known_addresses_idx, (gpointer) o, (gpointer) o)) { - /* duplicate? Keep only the first instance. */ - goto delete_and_next; - } - - continue; -delete_and_next: - nmp_object_unref (o); - known_addresses->pdata[i] = NULL; - } - - if ( !known_addresses_idx - || g_hash_table_size (known_addresses_idx) == 0) - known_addresses = NULL; - } + if (!_addr_array_clean_expired (AF_INET, ifindex, known_addresses, now, &known_addresses_idx)) + known_addresses = NULL; plat_addresses = nm_platform_lookup_clone (self, nmp_lookup_init_object (&lookup, @@ -3441,8 +3575,9 @@ delete_and_next: known_address = NMP_OBJECT_CAST_IP4_ADDRESS (o); - if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, - now, &lifetime, &preferred)) + lifetime = nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, + now, &preferred); + if (!lifetime) goto delete_and_next2; if (!nm_platform_ip4_address_add (self, ifindex, known_address->address, known_address->plen, @@ -3460,13 +3595,38 @@ delete_and_next2: return TRUE; } +static guint +ip6_address_scope_priority (const struct in6_addr *addr) +{ + if (IN6_IS_ADDR_LINKLOCAL (addr)) + return 1; + if (IN6_IS_ADDR_SITELOCAL (addr)) + return 2; + return 3; +} + +static gint +ip6_address_scope_cmp (gconstpointer a, gconstpointer b) +{ + const NMPlatformIP6Address *x = NMP_OBJECT_CAST_IP6_ADDRESS (*(const void **) a); + const NMPlatformIP6Address *y = NMP_OBJECT_CAST_IP6_ADDRESS (*(const void **) b); + + return ip6_address_scope_priority (&x->address) - ip6_address_scope_priority (&y->address); +} + /** * nm_platform_ip6_address_sync: * @self: platform instance * @ifindex: Interface index - * @known_addresses: List of IPv6 addresses, as NMPObject. The list - * is not modified. - * @keep_link_local: Don't remove link-local address + * @known_addresses: List of addresses. The list will be modified and only + * addresses that were successfully added will be kept in the list. + * That means, expired addresses and addresses that could not be added + * will be dropped. + * Hence, the input argument @known_addresses is also an output argument + * telling which addresses were succesfully added. + * Addresses are removed by unrefing the instance via nmp_object_unref() + * and leaving a NULL tombstone. + * @full_sync: Also remove link-local and temporary addresses. * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -3477,32 +3637,124 @@ delete_and_next2: gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, - const GPtrArray *known_addresses, - gboolean keep_link_local) + GPtrArray *known_addresses, + gboolean full_sync) { gs_unref_ptrarray GPtrArray *plat_addresses = NULL; - NMPlatformIP6Address *address; gint32 now = nm_utils_get_monotonic_timestamp_s (); - guint i; + guint i_plat, i_know; + gs_unref_hashtable GHashTable *known_addresses_idx = NULL; NMPLookup lookup; guint32 ifa_flags; - /* Delete unknown addresses */ + /* The order we want to enforce is only among addresses with the same + * scope, as the kernel keeps addresses sorted by scope. Therefore, + * apply the same sorting to known addresses, so that we don't try to + * unnecessary change the order of addresses with different scopes. */ + if (known_addresses) + g_ptr_array_sort (known_addresses, ip6_address_scope_cmp); + + if (!_addr_array_clean_expired (AF_INET6, ifindex, known_addresses, now, &known_addresses_idx)) + known_addresses = NULL; + + /* @plat_addresses is in decreasing priority order (highest priority addresses first), contrary to + * @known_addresses which is in increasing priority order (lowest priority addresses first). */ plat_addresses = nm_platform_lookup_clone (self, nmp_lookup_init_object (&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex), NULL, NULL); + if (plat_addresses) { - for (i = 0; i < plat_addresses->len; i++) { - address = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[i]); + guint known_addresses_len; + + known_addresses_len = known_addresses ? known_addresses->len : 0; + + /* First, compare every address whether it is still a "known address", that is, whether + * to keep it or to delete it. + * + * If we don't find a matching valid address in @known_addresses, we will delete + * plat_addr. + * + * Certain addresses, like temporary addresses, are ignored by this function + * if not run with full_sync. These addresses are usually not managed by NetworkManager + * directly, or at least, they are not managed via nm_platform_ip6_address_sync(). + * Only in full_sync mode, we really want to get rid of them (usually, when we take + * the interface down). + * + * Note that we mark handled addresses by setting it to %NULL in @plat_addresses array. */ + for (i_plat = 0; i_plat < plat_addresses->len; i_plat++) { + const NMPObject *plat_obj = plat_addresses->pdata[i_plat]; + const NMPObject *know_obj; + const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_obj); + + if (NM_FLAGS_HAS (plat_addr->n_ifa_flags, IFA_F_TEMPORARY)) { + if (!full_sync) { + /* just mark as handled, without actually deleting the address. */ + goto clear_and_next; + } + } else if (known_addresses_idx) { + know_obj = g_hash_table_lookup (known_addresses_idx, plat_obj); + if ( know_obj + && plat_addr->plen == NMP_OBJECT_CAST_IP6_ADDRESS (know_obj)->plen) { + /* technically, plen is not part of the ID for IPv6 addresses and thus + * @plat_addr is essentially the same address as @know_addr (regrading + * its identity, not its other attributes). + * However, we cannot modify an existing addresses' plen without + * removing and readding it. Thus, only keep plat_addr, if the plen + * matches. + * + * keep this one, and continue */ + continue; + } + } + + nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen); +clear_and_next: + nmp_object_unref (g_steal_pointer (&plat_addresses->pdata[i_plat])); + } - /* Leave link local address management to the kernel */ - if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address)) + /* Next, we must preserve the priority of the routes. That is, source address + * selection will choose addresses in the order as they are reported by kernel. + * Note that the order in @plat_addresses of the remaining matches is highest + * priority first. + * We need to compare this to the order in @known_addresses (which has lowest + * priority first). + * + * If we find a first discrepancy, we need to delete all remaining addresses + * from that point on, because below we must re-add all the addresses in the + * right order to get their priority right. */ + i_plat = plat_addresses->len; + i_know = 0; + while (i_plat > 0) { + const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[--i_plat]); + + if (!plat_addr) continue; - if (!array_contains_ip6_address (known_addresses, address, now)) - nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen); + for (; i_know < known_addresses_len; i_know++) { + const NMPlatformIP6Address *know_addr = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i_know]); + + if (!know_addr) + continue; + + if (IN6_ARE_ADDR_EQUAL (&plat_addr->address, &know_addr->address)) { + /* we have a match. Mark address as handled. */ + i_know++; + goto next_plat; + } + + /* all remainging addresses need to be removed as well, so that we can + * re-add them in the correct order. Signal that, by setting @i_know + * so that the next @i_plat iteration, we won't enter the loop and + * delete the address right away */ + i_know = known_addresses_len; + break; + } + + nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen); +next_plat: + ; } } @@ -3513,19 +3765,18 @@ nm_platform_ip6_address_sync (NMPlatform *self, ? IFA_F_NOPREFIXROUTE : 0; - /* Add missing addresses */ - for (i = 0; i < known_addresses->len; i++) { - const NMPlatformIP6Address *known_address = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i]); + /* Add missing addresses. New addresses are added by kernel with top + * priority. + */ + for (i_know = 0; i_know < known_addresses->len; i_know++) { + const NMPlatformIP6Address *known_address = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i_know]); guint32 lifetime, preferred; - if (NM_FLAGS_HAS (known_address->n_ifa_flags, IFA_F_TEMPORARY)) { - /* Kernel manages these */ + if (!known_address) continue; - } - if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, - now, &lifetime, &preferred)) - continue; + lifetime = nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, + now, &preferred); if (!nm_platform_ip6_address_add (self, ifindex, known_address->address, known_address->plen, known_address->peer_address, @@ -3553,7 +3804,7 @@ nm_platform_ip_address_flush (NMPlatform *self, if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) success &= nm_platform_ip4_address_sync (self, ifindex, NULL); if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) - success &= nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE); + success &= nm_platform_ip6_address_sync (self, ifindex, NULL, TRUE); return success; } @@ -3692,7 +3943,8 @@ nm_platform_ip_route_sync (NMPlatform *self, for (i_type = 0; routes && i_type < 2; i_type++) { for (i = 0; i < routes->len; i++) { - NMPlatformError plerr; + NMPlatformError plerr, plerr2; + gboolean gateway_route_added = FALSE; conf_o = routes->pdata[i]; @@ -3712,7 +3964,7 @@ nm_platform_ip_route_sync (NMPlatform *self, routes_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash, (GEqualFunc) nmp_object_id_equal); } - if (!nm_g_hash_table_insert (routes_idx, (gpointer) conf_o, (gpointer) conf_o)) { + if (!g_hash_table_insert (routes_idx, (gpointer) conf_o, (gpointer) conf_o)) { _LOGD ("route-sync: skip adding duplicate route %s", nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1))); continue; @@ -3738,6 +3990,7 @@ nm_platform_ip_route_sync (NMPlatform *self, } } +sync_route_add: plerr = nm_platform_ip_route_add (self, NMP_NLM_FLAG_APPEND | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE, @@ -3762,6 +4015,11 @@ nm_platform_ip_route_sync (NMPlatform *self, nmp_object_to_string (plat_entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf2, sizeof (sbuf2))); } } + } else if (NMP_OBJECT_CAST_IP_ROUTE (conf_o)->rt_source < NM_IP_CONFIG_SOURCE_USER) { + _LOGD ("route-sync: ignore failure to add IPv%c route: %s: %s", + vt->is_ip4 ? '4' : '6', + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err))); } else if ( -((int) plerr) == EINVAL && out_temporary_not_available && _err_inval_due_to_ipv6_tentative_pref_src (self, conf_o)) { @@ -3771,25 +4029,68 @@ nm_platform_ip_route_sync (NMPlatform *self, if (!*out_temporary_not_available) *out_temporary_not_available = g_ptr_array_new_full (0, (GDestroyNotify) nmp_object_unref); g_ptr_array_add (*out_temporary_not_available, (gpointer) nmp_object_ref (conf_o)); - } else if (NMP_OBJECT_CAST_IP_ROUTE (conf_o)->rt_source < NM_IP_CONFIG_SOURCE_USER) { - _LOGD ("route-sync: ignore failure to add IPv%c route: %s: %s", + } else if ( !gateway_route_added + && ( ( -((int) plerr) == ENETUNREACH + && vt->is_ip4 + && !!NMP_OBJECT_CAST_IP4_ROUTE (conf_o)->gateway) + || ( -((int) plerr) == EHOSTUNREACH + && !vt->is_ip4 + && !IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (conf_o)->gateway)))) { + NMPObject oo; + + if (vt->is_ip4) { + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (conf_o); + + nmp_object_stackinit (&oo, + NMP_OBJECT_TYPE_IP4_ROUTE, + &((NMPlatformIP4Route) { + .ifindex = r->ifindex, + .network = r->gateway, + .plen = 32, + .metric = r->metric, + .rt_source = r->rt_source, + .table_coerced = r->table_coerced, + })); + } else { + const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (conf_o); + + nmp_object_stackinit (&oo, + NMP_OBJECT_TYPE_IP6_ROUTE, + &((NMPlatformIP6Route) { + .ifindex = r->ifindex, + .network = r->gateway, + .plen = 128, + .metric = r->metric, + .rt_source = r->rt_source, + .table_coerced = r->table_coerced, + })); + } + + _LOGD ("route-sync: failure to add IPv%c route: %s: %s; try adding direct route to gateway %s", vt->is_ip4 ? '4' : '6', nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), - nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err))); - } else { - const char *reason = ""; - - if ( -((int) plerr) == ENETUNREACH - && ( vt->is_ip4 - ? !!NMP_OBJECT_CAST_IP4_ROUTE (conf_o)->gateway - : !IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (conf_o)->gateway))) - reason = "; is the gateway directly reachable?"; + nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err)), + nmp_object_to_string (&oo, NMP_OBJECT_TO_STRING_PUBLIC, sbuf2, sizeof (sbuf2))); + + plerr2 = nm_platform_ip_route_add (self, + NMP_NLM_FLAG_APPEND + | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE, + &oo); + + if (plerr2 != NM_PLATFORM_ERROR_SUCCESS) { + _LOGD ("route-sync: failure to add gateway IPv%c route: %s: %s", + vt->is_ip4 ? '4' : '6', + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err))); + } - _LOGW ("route-sync: failure to add IPv%c route: %s: %s%s", + gateway_route_added = TRUE; + goto sync_route_add; + } else { + _LOGW ("route-sync: failure to add IPv%c route: %s: %s", vt->is_ip4 ? '4' : '6', nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), - nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err)), - reason); + nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err))); success = FALSE; } } @@ -4377,7 +4678,6 @@ nm_platform_qdisc_sync (NMPlatform *self, ifindex), NULL, NULL); - if (plat_qdiscs) { for (i = 0; i < plat_qdiscs->len; i++) { const NMPObject *q = g_ptr_array_index (plat_qdiscs, i); @@ -4443,7 +4743,6 @@ nm_platform_tfilter_sync (NMPlatform *self, ifindex), NULL, NULL); - if (plat_tfilters) { for (i = 0; i < plat_tfilters->len; i++) { const NMPObject *q = g_ptr_array_index (plat_tfilters, i); @@ -4512,7 +4811,6 @@ _lifetime_to_string (guint32 timestamp, guint32 lifetime, gint32 now, char *buf, return buf; } - static const char * _lifetime_summary_to_string (gint32 now, guint32 timestamp, guint32 preferred, guint32 lifetime, char *buf, size_t buf_size) { @@ -4610,11 +4908,11 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) link->inet6_addr_gen_mode_inv ? " addrgenmode " : "", link->inet6_addr_gen_mode_inv ? nm_platform_link_inet6_addrgenmode2str (_nm_platform_uint8_inv (link->inet6_addr_gen_mode_inv), str_addrmode, sizeof (str_addrmode)) : "", str_addr ? " addr " : "", - str_addr ? str_addr : "", + str_addr ?: "", link->inet6_token.id ? " inet6token " : "", link->inet6_token.id ? nm_utils_inet6_interface_identifier_to_token (link->inet6_token, str_inet6_token) : "", link->driver ? " driver " : "", - link->driver ? link->driver : "", + link->driver ?: "", link->rx_packets, link->rx_bytes, link->tx_packets, link->tx_bytes); g_string_free (str_flags, TRUE); @@ -4713,6 +5011,7 @@ nm_platform_lnk_ip6tnl_to_string (const NMPlatformLnkIp6Tnl *lnk, char *buf, gsi "%s" /* encap limit */ "%s" /* flow label */ "%s" /* proto */ + " flags 0x%x" "", nm_sprintf_buf (str_remote, " remote %s", nm_utils_inet6_ntop (&lnk->remote, str_remote1)), nm_sprintf_buf (str_local, " local %s", nm_utils_inet6_ntop (&lnk->local, str_local1)), @@ -4721,7 +5020,8 @@ nm_platform_lnk_ip6tnl_to_string (const NMPlatformLnkIp6Tnl *lnk, char *buf, gsi lnk->tclass == 1 ? " tclass inherit" : nm_sprintf_buf (str_tclass, " tclass 0x%x", lnk->tclass), nm_sprintf_buf (str_encap, " encap-limit %u", lnk->encap_limit), nm_sprintf_buf (str_flow, " flow-label 0x05%x", lnk->flow_label), - nm_sprintf_buf (str_proto, " proto %u", lnk->proto)); + nm_sprintf_buf (str_proto, " proto %u", lnk->proto), + (guint) lnk->flags); return buf; } @@ -4841,6 +5141,43 @@ nm_platform_lnk_sit_to_string (const NMPlatformLnkSit *lnk, char *buf, gsize len return buf; } +const char * +nm_platform_lnk_tun_to_string (const NMPlatformLnkTun *lnk, char *buf, gsize len) +{ + char str_owner[50]; + char str_group[50]; + char str_type[50]; + const char *type; + + if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len)) + return buf; + + if (lnk->type == IFF_TUN) + type = "tun"; + else if (lnk->type == IFF_TAP) + type = "tap"; + else + type = nm_sprintf_buf (str_type, "tun type %u", (guint) lnk->type); + + g_snprintf (buf, len, + "%s" /* type */ + "%s" /* pi */ + "%s" /* vnet_hdr */ + "%s" /* multi_queue */ + "%s" /* persist */ + "%s" /* owner */ + "%s" /* group */ + "", + type, + lnk->pi ? " pi" : "", + lnk->vnet_hdr ? " vnet_hdr" : "", + lnk->multi_queue ? " multi_queue" : "", + lnk->persist ? " persist" : "", + lnk->owner_valid ? nm_sprintf_buf (str_owner, " owner %u", (guint) lnk->owner) : "", + lnk->group_valid ? nm_sprintf_buf (str_group, " group %u", (guint) lnk->group) : ""); + return buf; +} + const char * nm_platform_lnk_vlan_to_string (const NMPlatformLnkVlan *lnk, char *buf, gsize len) { @@ -4987,7 +5324,7 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *bu str_label[0] = 0; str_lft_p = _lifetime_to_string (address->timestamp, - address->lifetime ? address->lifetime : NM_PLATFORM_LIFETIME_PERMANENT, + address->lifetime ?: NM_PLATFORM_LIFETIME_PERMANENT, now, str_lft, sizeof (str_lft)), str_pref_p = (address->lifetime == address->preferred) ? str_lft_p @@ -4999,7 +5336,7 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *bu g_snprintf (buf, len, "%s/%d lft %s pref %s%s%s%s%s%s src %s", s_address, address->plen, str_lft_p, str_pref_p, str_time_p, - str_peer ? str_peer : "", + str_peer ?: "", str_dev, _to_string_ifa_flags (address->n_ifa_flags, s_flags, sizeof (s_flags)), str_label, @@ -5043,10 +5380,10 @@ NM_UTILS_FLAGS2STR_DEFINE (nm_platform_addr_flags2str, unsigned, NM_UTILS_FLAGS2STR (IFA_F_OPTIMISTIC, "optimistic"), NM_UTILS_FLAGS2STR (IFA_F_HOMEADDRESS, "homeaddress"), NM_UTILS_FLAGS2STR (IFA_F_DEPRECATED, "deprecated"), - NM_UTILS_FLAGS2STR (IFA_F_TENTATIVE, "tentative"), NM_UTILS_FLAGS2STR (IFA_F_PERMANENT, "permanent"), NM_UTILS_FLAGS2STR (IFA_F_MANAGETEMPADDR, "mngtmpaddr"), NM_UTILS_FLAGS2STR (IFA_F_NOPREFIXROUTE, "noprefixroute"), + NM_UTILS_FLAGS2STR (IFA_F_TENTATIVE, "tentative"), ); NM_UTILS_ENUM2STR_DEFINE (nm_platform_route_scope2str, int, @@ -5094,7 +5431,7 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *bu _to_string_dev (NULL, address->ifindex, str_dev, sizeof (str_dev)); str_lft_p = _lifetime_to_string (address->timestamp, - address->lifetime ? address->lifetime : NM_PLATFORM_LIFETIME_PERMANENT, + address->lifetime ?: NM_PLATFORM_LIFETIME_PERMANENT, now, str_lft, sizeof (str_lft)), str_pref_p = (address->lifetime == address->preferred) ? str_lft_p @@ -5106,7 +5443,7 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *bu g_snprintf (buf, len, "%s/%d lft %s pref %s%s%s%s%s src %s", s_address, address->plen, str_lft_p, str_pref_p, str_time_p, - str_peer ? str_peer : "", + str_peer ?: "", str_dev, _to_string_ifa_flags (address->n_ifa_flags, s_flags, sizeof (s_flags)), nmp_utils_ip_config_source_to_string (address->addr_source, s_source, sizeof (s_source))); @@ -5179,7 +5516,6 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); - g_snprintf (buf, len, "%s" /* table */ "%s/%d" @@ -5537,7 +5873,8 @@ nm_platform_lnk_ip6tnl_hash_update (const NMPlatformLnkIp6Tnl *obj, NMHashState obj->tclass, obj->encap_limit, obj->proto, - obj->flow_label); + obj->flow_label, + obj->flags); } int @@ -5552,6 +5889,7 @@ nm_platform_lnk_ip6tnl_cmp (const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6 NM_CMP_FIELD (a, b, encap_limit); NM_CMP_FIELD (a, b, flow_label); NM_CMP_FIELD (a, b, proto); + NM_CMP_FIELD (a, b, flags); return 0; } @@ -5669,6 +6007,38 @@ nm_platform_lnk_sit_cmp (const NMPlatformLnkSit *a, const NMPlatformLnkSit *b) return 0; } +void +nm_platform_lnk_tun_hash_update (const NMPlatformLnkTun *obj, NMHashState *h) +{ + nm_hash_update_vals (h, + obj->type, + obj->owner, + obj->group, + NM_HASH_COMBINE_BOOLS (guint8, + obj->owner_valid, + obj->group_valid, + obj->pi, + obj->vnet_hdr, + obj->multi_queue, + obj->persist)); +} + +int +nm_platform_lnk_tun_cmp (const NMPlatformLnkTun *a, const NMPlatformLnkTun *b) +{ + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, type); + NM_CMP_FIELD (a, b, owner); + NM_CMP_FIELD (a, b, group); + NM_CMP_FIELD_BOOL (a, b, owner_valid); + NM_CMP_FIELD_BOOL (a, b, group_valid); + NM_CMP_FIELD_BOOL (a, b, pi); + NM_CMP_FIELD_BOOL (a, b, vnet_hdr); + NM_CMP_FIELD_BOOL (a, b, multi_queue); + NM_CMP_FIELD_BOOL (a, b, persist); + return 0; +} + void nm_platform_lnk_vlan_hash_update (const NMPlatformLnkVlan *obj, NMHashState *h) { @@ -6260,14 +6630,13 @@ nm_platform_cache_update_emit_signal (NMPlatform *self, const NMPObject *o; const NMPClass *klass; - nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, (NMPlatformSignalChangeType) NMP_CACHE_OPS_UNCHANGED, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED)); + nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, NM_PLATFORM_SIGNAL_NONE, + NM_PLATFORM_SIGNAL_ADDED, + NM_PLATFORM_SIGNAL_CHANGED, + NM_PLATFORM_SIGNAL_REMOVED)); ASSERT_nmp_cache_ops (nm_platform_get_cache (self), cache_op, obj_old, obj_new); - nm_assert (NM_IN_SET (nm_platform_netns_get (self), - NULL, - nmp_netns_get_current ())); - NMTST_ASSERT_PLATFORM_NETNS_CURRENT (self); switch (cache_op) { diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index f6bf02bf..2d2bfaab 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "nm-dbus-interface.h" #include "nm-core-types-internal.h" @@ -32,6 +33,7 @@ #include "nm-core-utils.h" #include "nm-setting-vlan.h" #include "nm-setting-wired.h" +#include "nm-setting-ip-tunnel.h" #define NM_TYPE_PLATFORM (nm_platform_get_type ()) #define NM_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PLATFORM, NMPlatform)) @@ -169,6 +171,34 @@ typedef enum { /*< skip >*/ NM_PLATFORM_ERROR_CANT_SET_MTU, } NMPlatformError; +typedef enum { + + /* match-flags are strictly inclusive. That means, + * by default nothing is matched, but if you enable a particular + * flag, a candidate that matches passes the check. + * + * In other words: adding more flags can only extend the result + * set of matching objects. + * + * Also, the flags form partitions. Like, an address can be either of + * ADDRTYPE_NORMAL or ADDRTYPE_LINKLOCAL, but never both. Same for + * the ADDRSTATE match types. + */ + NM_PLATFORM_MATCH_WITH_NONE = 0, + + NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL = (1LL << 0), + NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL = (1LL << 1), + NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY = NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL + | NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL, + + NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL = (1LL << 2), + NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE = (1LL << 3), + NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED = (1LL << 4), + NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY = NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL + | NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE + | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED, +} NMPlatformMatchFlags; + #define NM_PLATFORM_LINK_OTHER_NETNS (-1) #define __NMPlatformObject_COMMON \ @@ -254,6 +284,8 @@ struct _NMPlatformObject { __NMPlatformObject_COMMON; }; +#define NM_PLATFORM_IP_ADDRESS_CAST(address) \ + NM_CONSTCAST (NMPlatformIPAddress, (address), NMPlatformIPXAddress, NMPlatformIP4Address, NMPlatformIP6Address) #define __NMPlatformIPAddress_COMMON \ __NMPlatformObject_COMMON; \ @@ -343,7 +375,6 @@ typedef union { #undef __NMPlatformIPAddress_COMMON - /* Default value for adding an IPv4 route. This is also what iproute2 does. * Note that contrary to IPv6, you can add routes with metric 0 and it is even * the default. @@ -402,7 +433,7 @@ typedef union { * do not exist from the point-of-view of platform users. * Such a route is not alive, according to nmp_object_is_alive(). * - * XXX: currently we ignore all flags except RTM_F_CLONED + * NOTE: currently we ignore all flags except RTM_F_CLONED * and RTNH_F_ONLINK for IPv4. * We also may not properly consider the flags as part of the ID * in route-cmp. */ \ @@ -439,7 +470,6 @@ typedef union { \ /*end*/ - typedef struct { __NMPlatformIPRoute_COMMON; union { @@ -563,7 +593,6 @@ typedef struct { #undef __NMPlatformObject_COMMON - typedef struct { gboolean is_ip4; NMPObjectType obj_type; @@ -604,6 +633,7 @@ typedef struct { guint8 encap_limit; guint8 proto; guint flow_label; + guint32 flags; } NMPlatformLnkIp6Tnl; typedef struct { @@ -650,6 +680,21 @@ typedef struct { bool path_mtu_discovery:1; } NMPlatformLnkSit; +typedef struct { + guint32 owner; + guint32 group; + + guint8 type; + + bool owner_valid:1; + bool group_valid:1; + + bool pi:1; + bool vnet_hdr:1; + bool multi_queue:1; + bool persist:1; +} NMPlatformLnkTun; + typedef struct { /* rtnl_link_vlan_get_id(), IFLA_VLAN_ID */ guint16 id; @@ -677,15 +722,6 @@ typedef struct { bool l3miss:1; } NMPlatformLnkVxlan; -typedef struct { - gint64 owner; - gint64 group; - const char *mode; - bool no_pi:1; - bool vnet_hdr:1; - bool multi_queue:1; -} NMPlatformTunProperties; - typedef enum { NM_PLATFORM_LINK_DUPLEX_UNKNOWN, NM_PLATFORM_LINK_DUPLEX_HALF, @@ -714,6 +750,8 @@ typedef struct { gboolean (*sysctl_set) (NMPlatform *, const char *pathid, int dirfd, const char *path, const char *value); char * (*sysctl_get) (NMPlatform *, const char *pathid, int dirfd, const char *path); + void (*refresh_all) (NMPlatform *self, NMPObjectType obj_type); + gboolean (*link_add) (NMPlatform *, const char *name, NMLinkType type, @@ -809,12 +847,15 @@ typedef struct { const NMPlatformLnkSit *props, const NMPlatformLink **out_link); + gboolean (*link_tun_add) (NMPlatform *platform, + const char *name, + const NMPlatformLnkTun *props, + const NMPlatformLink **out_link, + int *out_fd); + gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key, const NMPlatformLink **out_link); gboolean (*infiniband_partition_delete) (NMPlatform *, int parent, int p_key); - gboolean (*tun_add) (NMPlatform *platform, const char *name, gboolean tap, gint64 owner, gint64 group, gboolean pi, - gboolean vnet_hdr, gboolean multi_queue, const NMPlatformLink **out_link); - gboolean (*wifi_get_capabilities) (NMPlatform *, int ifindex, NMDeviceWifiCapabilities *caps); gboolean (*wifi_get_bssid) (NMPlatform *, int ifindex, guint8 *bssid); GByteArray *(*wifi_get_ssid) (NMPlatform *, int ifindex); @@ -1028,6 +1069,8 @@ gboolean nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char const char *nm_platform_if_indextoname (NMPlatform *self, int ifindex, char *out_ifname/* of size IFNAMSIZ */); int nm_platform_if_nametoindex (NMPlatform *self, const char *ifname); +void nm_platform_refresh_all (NMPlatform *self, NMPObjectType obj_type); + const NMPObject *nm_platform_link_get_obj (NMPlatform *self, int ifindex, gboolean visible_only); @@ -1071,7 +1114,21 @@ gboolean nm_platform_link_is_connected (NMPlatform *self, int ifindex); gboolean nm_platform_link_uses_arp (NMPlatform *self, int ifindex); guint32 nm_platform_link_get_mtu (NMPlatform *self, int ifindex); gboolean nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex); + gconstpointer nm_platform_link_get_address (NMPlatform *self, int ifindex, size_t *length); + +static inline GBytes * +nm_platform_link_get_address_as_bytes (NMPlatform *self, int ifindex) +{ + gconstpointer p; + gsize l; + + p = nm_platform_link_get_address (self, ifindex, &l); + return p + ? g_bytes_new (p, l) + : NULL; +} + int nm_platform_link_get_master (NMPlatform *self, int slave); gboolean nm_platform_link_can_assume (NMPlatform *self, int ifindex); @@ -1083,6 +1140,10 @@ const char *nm_platform_link_get_type_name (NMPlatform *self, int ifindex); gboolean nm_platform_link_refresh (NMPlatform *self, int ifindex); void nm_platform_process_events (NMPlatform *self); +const NMPlatformLink *nm_platform_process_events_ensure_link (NMPlatform *self, + int ifindex, + const char *ifname); + gboolean nm_platform_link_set_up (NMPlatform *self, int ifindex, gboolean *out_no_firmware); gboolean nm_platform_link_set_down (NMPlatform *self, int ifindex); gboolean nm_platform_link_set_arp (NMPlatform *self, int ifindex); @@ -1132,6 +1193,7 @@ const NMPlatformLnkMacsec *nm_platform_link_get_lnk_macsec (NMPlatform *self, in const NMPlatformLnkMacvlan *nm_platform_link_get_lnk_macvlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkMacvtap *nm_platform_link_get_lnk_macvtap (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkSit *nm_platform_link_get_lnk_sit (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkTun *nm_platform_link_get_lnk_tun (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkVlan *nm_platform_link_get_lnk_vlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkVxlan *nm_platform_link_get_lnk_vxlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); @@ -1159,16 +1221,6 @@ NMPlatformError nm_platform_link_vxlan_add (NMPlatform *self, const NMPlatformLnkVxlan *props, const NMPlatformLink **out_link); -NMPlatformError nm_platform_link_tun_add (NMPlatform *self, - const char *name, - gboolean tap, - gint64 owner, - gint64 group, - gboolean pi, - gboolean vnet_hdr, - gboolean multi_queue, - const NMPlatformLink **out_link); - NMPlatformError nm_platform_link_infiniband_add (NMPlatform *self, int parent, int p_key, @@ -1179,7 +1231,9 @@ NMPlatformError nm_platform_link_infiniband_delete (NMPlatform *self, gboolean nm_platform_link_infiniband_get_properties (NMPlatform *self, int ifindex, int *parent, int *p_key, const char **mode); gboolean nm_platform_link_veth_get_properties (NMPlatform *self, int ifindex, int *out_peer_ifindex); -gboolean nm_platform_link_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *properties); +gboolean nm_platform_link_tun_get_properties (NMPlatform *self, + int ifindex, + NMPlatformLnkTun *out_properties); gboolean nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps); gboolean nm_platform_wifi_get_bssid (NMPlatform *self, int ifindex, guint8 *bssid); @@ -1227,6 +1281,11 @@ NMPlatformError nm_platform_link_sit_add (NMPlatform *self, const char *name, const NMPlatformLnkSit *props, const NMPlatformLink **out_link); +NMPlatformError nm_platform_link_tun_add (NMPlatform *self, + const char *name, + const NMPlatformLnkTun *props, + const NMPlatformLink **out_link, + int *out_fd); const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address); @@ -1251,8 +1310,8 @@ gboolean nm_platform_ip6_address_add (NMPlatform *self, guint32 flags); gboolean nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); gboolean nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen); -gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresse); -gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GPtrArray *known_addresses, gboolean keep_link_local); +gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresses); +gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresses, gboolean full_sync); gboolean nm_platform_ip_address_flush (NMPlatform *self, int addr_family, int ifindex); @@ -1310,6 +1369,7 @@ const char *nm_platform_lnk_ipip_to_string (const NMPlatformLnkIpIp *lnk, char * const char *nm_platform_lnk_macsec_to_string (const NMPlatformLnkMacsec *lnk, char *buf, gsize len); const char *nm_platform_lnk_macvlan_to_string (const NMPlatformLnkMacvlan *lnk, char *buf, gsize len); const char *nm_platform_lnk_sit_to_string (const NMPlatformLnkSit *lnk, char *buf, gsize len); +const char *nm_platform_lnk_tun_to_string (const NMPlatformLnkTun *lnk, char *buf, gsize len); const char *nm_platform_lnk_vlan_to_string (const NMPlatformLnkVlan *lnk, char *buf, gsize len); const char *nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize len); const char *nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *buf, gsize len); @@ -1333,6 +1393,7 @@ int nm_platform_lnk_ipip_cmp (const NMPlatformLnkIpIp *a, const NMPlatformLnkIpI int nm_platform_lnk_macsec_cmp (const NMPlatformLnkMacsec *a, const NMPlatformLnkMacsec *b); int nm_platform_lnk_macvlan_cmp (const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b); int nm_platform_lnk_sit_cmp (const NMPlatformLnkSit *a, const NMPlatformLnkSit *b); +int nm_platform_lnk_tun_cmp (const NMPlatformLnkTun *a, const NMPlatformLnkTun *b); int nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b); int nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b); int nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b); @@ -1368,6 +1429,7 @@ void nm_platform_lnk_ipip_hash_update (const NMPlatformLnkIpIp *obj, NMHashState void nm_platform_lnk_macsec_hash_update (const NMPlatformLnkMacsec *obj, NMHashState *h); void nm_platform_lnk_macvlan_hash_update (const NMPlatformLnkMacvlan *obj, NMHashState *h); void nm_platform_lnk_sit_hash_update (const NMPlatformLnkSit *obj, NMHashState *h); +void nm_platform_lnk_tun_hash_update (const NMPlatformLnkTun *obj, NMHashState *h); void nm_platform_lnk_vlan_hash_update (const NMPlatformLnkVlan *obj, NMHashState *h); void nm_platform_lnk_vxlan_hash_update (const NMPlatformLnkVxlan *obj, NMHashState *h); diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c index d8561aef..f1092fe9 100644 --- a/src/platform/nmp-netns.c +++ b/src/platform/nmp-netns.c @@ -200,8 +200,8 @@ _stack_current_ns_types (NMPNetns *netns, int ns_types) } for (i = 0; i < G_N_ELEMENTS (ns_types_check); i++) { - if ( NM_FLAGS_HAS (ns_types, ns_types_check[i]) - && NM_FLAGS_HAS (info->ns_types, ns_types_check[i])) { + if ( NM_FLAGS_ANY (ns_types, ns_types_check[i]) + && NM_FLAGS_ANY (info->ns_types, ns_types_check[i])) { res = NM_FLAGS_SET (res, ns_types_check[i]); ns_types = NM_FLAGS_UNSET (ns_types, ns_types_check[i]); } diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 1a9b9325..50e6825b 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -325,7 +325,7 @@ _vlan_xgress_qos_mappings_cmp (guint n_map, static void _vlan_xgress_qos_mappings_cpy (guint *dst_n_map, - const NMVlanQosMapping **dst_map, + NMVlanQosMapping **dst_map, guint src_n_map, const NMVlanQosMapping *src_map) { @@ -388,7 +388,7 @@ _nmp_object_fixup_link_udev_fields (NMPObject **obj_new, NMPObject *obj_orig, gb /* The link contains internal fields that are combined by * properties from netlink and udev. Update those properties */ - /* When a link is not in netlink, it's udev fields don't matter. */ + /* When a link is not in netlink, its udev fields don't matter. */ if (obj->_link.netlink.is_in_netlink) { driver = _link_get_driver (obj->_link.udev.device, obj->link.kind, @@ -532,7 +532,7 @@ _nmp_object_stackinit_from_type (NMPObject *obj, NMPObjectType obj_type) } const NMPObject * -nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, const NMPlatformObject *plobj) +nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, gconstpointer plobj) { const NMPClass *klass = nmp_class_from_type (obj_type); @@ -865,12 +865,6 @@ _vt_cmd_obj_cmp_lnk_vlan (const NMPObject *obj1, const NMPObject *obj2) return c; } -gboolean -nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2) -{ - return nmp_object_cmp (obj1, obj2) == 0; -} - /* @src is a const object, which is not entirely correct for link types, where * we increase the ref count for src->_link.udev.device. * Hence, nmp_object_copy() can violate the const promise of @src. @@ -922,11 +916,11 @@ _vt_cmd_obj_copy_lnk_vlan (NMPObject *dst, const NMPObject *src) { dst->lnk_vlan = src->lnk_vlan; _vlan_xgress_qos_mappings_cpy (&dst->_lnk_vlan.n_ingress_qos_map, - &dst->_lnk_vlan.ingress_qos_map, + NM_UNCONST_PPTR (NMVlanQosMapping, &dst->_lnk_vlan.ingress_qos_map), src->_lnk_vlan.n_ingress_qos_map, src->_lnk_vlan.ingress_qos_map); _vlan_xgress_qos_mappings_cpy (&dst->_lnk_vlan.n_egress_qos_map, - &dst->_lnk_vlan.egress_qos_map, + NM_UNCONST_PPTR (NMVlanQosMapping, &dst->_lnk_vlan.egress_qos_map), src->_lnk_vlan.n_egress_qos_map, src->_lnk_vlan.egress_qos_map); } @@ -1088,7 +1082,7 @@ nmp_object_id_hash (const NMPObject *obj) NMHashState h; if (!obj) - return 0; + return nm_hash_static (914932607u); nm_hash_init (&h, 914932607u); nmp_object_id_hash_update (obj, &h); @@ -1532,8 +1526,6 @@ const NMPLookup * nmp_lookup_init_obj_type (NMPLookup *lookup, NMPObjectType obj_type) { - NMPObject *o; - nm_assert (lookup); switch (obj_type) { @@ -1544,7 +1536,7 @@ nmp_lookup_init_obj_type (NMPLookup *lookup, case NMP_OBJECT_TYPE_IP6_ROUTE: case NMP_OBJECT_TYPE_QDISC: case NMP_OBJECT_TYPE_TFILTER: - o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); + _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_TYPE; return _L (lookup); default: @@ -1776,6 +1768,54 @@ nmp_cache_lookup_link_full (const NMPCache *cache, /*****************************************************************************/ +static NMDedupMultiIdxMode +_obj_get_add_mode (const NMPObject *obj) +{ + /* new objects are usually appended to the list. Except for + * addresses, which are prepended during `ip address add`. + * + * Actually, for routes it is more complicated, because depending on + * `ip route append`, `ip route replace`, `ip route prepend`, the object + * will be added at the tail, at the front, or even replace an element + * in the list. However, that is handled separately by nmp_cache_update_netlink_route() + * and of no concern here. */ + if (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), + NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS)) + return NM_DEDUP_MULTI_IDX_MODE_PREPEND; + return NM_DEDUP_MULTI_IDX_MODE_APPEND; +} + +static void +_idxcache_update_order_for_dump (NMPCache *cache, + const NMDedupMultiEntry *entry) +{ + const NMPClass *klass; + const guint8 *i_idx_type; + const NMDedupMultiEntry *entry2; + + nm_dedup_multi_entry_reorder (entry, NULL, TRUE); + + klass = NMP_OBJECT_GET_CLASS (entry->obj); + for (i_idx_type = klass->supported_cache_ids; *i_idx_type; i_idx_type++) { + NMPCacheIdType id_type = *i_idx_type; + + if (id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE) + continue; + + entry2 = nm_dedup_multi_index_lookup_obj (cache->multi_idx, + _idx_type_get (cache, id_type), + entry->obj); + if (!entry2) + continue; + + nm_assert (entry2 != entry); + nm_assert (entry2->obj == entry->obj); + + nm_dedup_multi_entry_reorder (entry2, NULL, TRUE); + } +} + static void _idxcache_update_other_cache_ids (NMPCache *cache, NMPCacheIdType cache_id_type, @@ -1835,7 +1875,7 @@ _idxcache_update_other_cache_ids (NMPCache *cache, obj_new, is_dump ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE - : NM_DEDUP_MULTI_IDX_MODE_APPEND, + : _obj_get_add_mode (obj_new), is_dump ? NULL : entry_order, @@ -1913,7 +1953,7 @@ _idxcache_update (NMPCache *cache, obj_new, is_dump ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE - : NM_DEDUP_MULTI_IDX_MODE_APPEND, + : _obj_get_add_mode (obj_new), NULL, entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING, NULL, @@ -2170,6 +2210,8 @@ nmp_cache_update_netlink (NMPCache *cache, } if (nmp_object_equal (obj_old, obj_hand_over)) { + if (is_dump) + _idxcache_update_order_for_dump (cache, entry_old); nm_dedup_multi_entry_set_dirty (entry_old, FALSE); NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old)); return NMP_CACHE_OPS_UNCHANGED; @@ -2243,6 +2285,8 @@ nmp_cache_update_netlink_route (NMPCache *cache, } if (nmp_object_equal (entry_old->obj, obj_hand_over)) { + if (is_dump) + _idxcache_update_order_for_dump (cache, entry_old); nm_dedup_multi_entry_set_dirty (entry_old, FALSE); goto update_done; } @@ -2266,9 +2310,8 @@ update_done: * properly find @obj_replaced. */ resync_required = FALSE; entry_replace = NULL; - if (is_dump) { + if (is_dump) goto out; - } if (!entry_new) { if ( NM_FLAGS_HAS (nlmsgflags, NLM_F_REPLACE) @@ -2283,6 +2326,8 @@ update_done: goto out; } + /* FIXME: for routes, we only maintain the order correctly for the BY_WEAK_ID + * index. For all other indexes their order becomes messed up. */ entry_cur = _lookup_entry_with_idx_type (cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, entry_new->obj); @@ -2342,7 +2387,6 @@ out: return ops_type; } - NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, int ifindex, @@ -2735,6 +2779,17 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_sit_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_sit_cmp, }, + [NMP_OBJECT_TYPE_LNK_TUN - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_LNK_TUN, + .sizeof_data = sizeof (NMPObjectLnkTun), + .sizeof_public = sizeof (NMPlatformLnkTun), + .obj_type_name = "tun", + .lnk_link_type = NM_LINK_TYPE_TUN, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_tun_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_tun_hash_update, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_tun_cmp, + }, [NMP_OBJECT_TYPE_LNK_VLAN - 1] = { .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_VLAN, diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index e17b17b0..f473f462 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -88,7 +88,7 @@ typedef enum { /*< skip >*/ /* Consider all the destination fields of a route, that is, the ID without the ifindex * and gateway (meaning: network/plen,metric). * The reason for this is that `ip route change` can replace an existing route - * and modify it's ifindex/gateway. Effectively, that means it deletes an existing + * and modify its ifindex/gateway. Effectively, that means it deletes an existing * route and adds a different one (as the ID of the route changes). However, it only * sends one RTM_NEWADDR notification without notifying about the deletion. We detect * that by having this index to contain overlapping routes which require special @@ -196,6 +196,10 @@ typedef struct { NMPlatformLnkSit _public; } NMPObjectLnkSit; +typedef struct { + NMPlatformLnkTun _public; +} NMPObjectLnkTun; + typedef struct { NMPlatformLnkVlan _public; @@ -265,6 +269,9 @@ struct _NMPObject { NMPlatformLnkSit lnk_sit; NMPObjectLnkSit _lnk_sit; + NMPlatformLnkTun lnk_tun; + NMPObjectLnkTun _lnk_tun; + NMPlatformLnkVlan lnk_vlan; NMPObjectLnkVlan _lnk_vlan; @@ -460,6 +467,8 @@ nmp_object_ref (const NMPObject *obj) static inline void nmp_object_unref (const NMPObject *obj) { + nm_assert (!obj || NMP_OBJECT_IS_VALID (obj)); + nm_dedup_multi_obj_unref ((const NMDedupMultiObj *) obj); } @@ -481,7 +490,7 @@ nmp_object_unref (const NMPObject *obj) NMPObject *nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plob); NMPObject *nmp_object_new_link (int ifindex); -const NMPObject *nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, const NMPlatformObject *plobj); +const NMPObject *nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, gconstpointer plobj); static inline NMPObject * nmp_object_stackinit_obj (NMPObject *obj, const NMPObject *src) @@ -499,7 +508,13 @@ const NMPObject *nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifinde const char *nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size); void nmp_object_hash_update (const NMPObject *obj, NMHashState *h); int nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2); -gboolean nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2); + +static inline gboolean +nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2) +{ + return nmp_object_cmp (obj1, obj2) == 0; +} + void nmp_object_copy (NMPObject *dst, const NMPObject *src, gboolean id_only); NMPObject *nmp_object_clone (const NMPObject *obj, gboolean id_only); @@ -724,6 +739,16 @@ const NMDedupMultiEntry *nm_platform_lookup_entry (NMPlatform *platform, NMPCacheIdType cache_id_type, const NMPObject *obj); +static inline const NMPObject * +nm_platform_lookup_obj (NMPlatform *platform, + NMPCacheIdType cache_id_type, + const NMPObject *obj) +{ + return nm_dedup_multi_entry_get_obj (nm_platform_lookup_entry (platform, + cache_id_type, + obj)); +} + static inline const NMDedupMultiHeadEntry * nm_platform_lookup_obj_type (NMPlatform *platform, NMPObjectType obj_type) diff --git a/src/platform/tests/meson.build b/src/platform/tests/meson.build new file mode 100644 index 00000000..0571efac --- /dev/null +++ b/src/platform/tests/meson.build @@ -0,0 +1,37 @@ +test_units = [ + ['test-link-fake', 'test-link.c', 60], + ['test-link-linux', 'test-link.c', 60], + ['test-address-fake', 'test-address.c'], + ['test-address-linux', 'test-address.c'], + ['test-general', 'test-general.c'], + ['test-nmp-object', 'test-nmp-object.c'], + ['test-route-fake', 'test-route.c'], + ['test-route-linux', 'test-route.c'], + ['test-cleanup-fake', 'test-cleanup.c'], + ['test-cleanup-linux', 'test-cleanup.c'], +] + +foreach test_unit: test_units + exe = executable( + 'platform-' + test_unit[0], + test_unit[1], + dependencies: test_nm_dep, + c_args: test_cflags_platform + ) + + test( + 'platform/' + test_unit[0], + test_script, + timeout: test_unit.length() > 2 ? test_unit[2] : 30, + args: test_args + [exe.full_path()] + ) +endforeach + +test = 'monitor' + +executable( + test, + test + '.c', + dependencies: test_nm_dep, + c_args: test_cflags_platform +) diff --git a/src/platform/tests/monitor.c b/src/platform/tests/monitor.c index e1220052..f0e3e6cf 100644 --- a/src/platform/tests/monitor.c +++ b/src/platform/tests/monitor.c @@ -1,6 +1,5 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager audit support - * +/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index 93851ff7..ddef8853 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -1,6 +1,5 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager audit support - * +/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -149,7 +148,7 @@ test_ip6_address_general (void) /* Add address again (aka update) */ nmtstp_ip6_address_add (NULL, EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, flags); - accept_signals (address_changed, 0, 1); + accept_signals (address_changed, 0, 2); /* Test address listing */ addresses = nmtstp_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 937cd12c..a213b31f 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -1,6 +1,5 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager audit support - * +/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index d56e681e..42569d5b 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -1,6 +1,5 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager audit support - * +/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -24,11 +23,12 @@ #include #include #include +#include #include "test-common.h" #define SIGNAL_DATA_FMT "'%s-%s' ifindex %d%s%s%s (%d times received)" -#define SIGNAL_DATA_ARG(data) (data)->name, nm_platform_signal_change_type_to_string ((data)->change_type), (data)->ifindex, (data)->ifname ? " ifname '" : "", (data)->ifname ? (data)->ifname : "", (data)->ifname ? "'" : "", (data)->received_count +#define SIGNAL_DATA_ARG(data) (data)->name, nm_platform_signal_change_type_to_string ((data)->change_type), (data)->ifindex, (data)->ifname ? " ifname '" : "", (data)->ifname ?: "", (data)->ifname ? "'" : "", (data)->received_count int NMTSTP_ENV1_IFINDEX = -1; int NMTSTP_ENV1_EX = -1; @@ -628,7 +628,10 @@ nmtstp_wait_for_signal_until (NMPlatform *platform, gint64 until_ms) const NMPlatformLink * nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 timeout_ms) { - return nmtstp_wait_for_link_until (platform, ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms); + return nmtstp_wait_for_link_until (platform, ifname, expected_link_type, + timeout_ms + ? nm_utils_get_monotonic_timestamp_ms () + timeout_ms + : 0); } const NMPlatformLink * @@ -636,6 +639,7 @@ nmtstp_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType { const NMPlatformLink *plink; gint64 now; + gboolean waited_once = FALSE; _init_platform (&platform, FALSE); @@ -647,29 +651,24 @@ nmtstp_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType && (expected_link_type == NM_LINK_TYPE_NONE || plink->type == expected_link_type)) return plink; - if (until_ms < now) + if (until_ms == 0) { + /* don't wait, don't even poll the socket. */ + return NULL; + } + + if ( waited_once + && until_ms < now) { + /* timeout reached (+ we already waited for a signal at least once). */ return NULL; + } + waited_once = TRUE; + /* regardless of whether timeout is already reached, we poll the netlink + * socket a bit. */ nmtstp_wait_for_signal (platform, until_ms - now); } } -const NMPlatformLink * -nmtstp_assert_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms) -{ - return nmtstp_assert_wait_for_link_until (platform, ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms); -} - -const NMPlatformLink * -nmtstp_assert_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 until_ms) -{ - const NMPlatformLink *plink; - - plink = nmtstp_wait_for_link_until (platform, ifname, expected_link_type, until_ms); - g_assert (plink); - return plink; -} - /*****************************************************************************/ int @@ -1259,7 +1258,7 @@ nmtstp_link_gre_add (NMPlatform *platform, success = !nmtstp_run_command ("ip tunnel add %s mode gre %s local %s remote %s ttl %u tos %02x %s", name, - dev ? dev : "", + dev ?: "", nm_utils_inet4_ntop (lnk->local, NULL), nm_utils_inet4_ntop (lnk->remote, buffer), lnk->ttl, @@ -1284,6 +1283,10 @@ nmtstp_link_ip6tnl_add (NMPlatform *platform, const NMPlatformLink *pllink = NULL; gboolean success; char buffer[INET6_ADDRSTRLEN]; + char encap[20]; + char tclass[20]; + gboolean encap_ignore; + gboolean tclass_inherit; g_assert (nm_utils_is_valid_iface_name (name, NULL)); @@ -1309,15 +1312,18 @@ nmtstp_link_ip6tnl_add (NMPlatform *platform, g_assert_not_reached (); } - success = !nmtstp_run_command ("ip -6 tunnel add %s mode %s %s local %s remote %s ttl %u tclass %02x encaplimit %u flowlabel %x", + encap_ignore = NM_FLAGS_HAS (lnk->flags, IP6_TNL_F_IGN_ENCAP_LIMIT); + tclass_inherit = NM_FLAGS_HAS (lnk->flags, IP6_TNL_F_USE_ORIG_TCLASS); + + success = !nmtstp_run_command ("ip -6 tunnel add %s mode %s %s local %s remote %s ttl %u tclass %s encaplimit %s flowlabel %x", name, mode, dev, nm_utils_inet6_ntop (&lnk->local, NULL), nm_utils_inet6_ntop (&lnk->remote, buffer), lnk->ttl, - lnk->tclass, - lnk->encap_limit, + tclass_inherit ? "inherit" : nm_sprintf_buf (tclass, "%02x", lnk->tclass), + encap_ignore ? "none" : nm_sprintf_buf (encap, "%u", lnk->encap_limit), lnk->flow_label); if (success) pllink = nmtstp_assert_wait_for_link (platform, name, NM_LINK_TYPE_IP6TNL, 100); @@ -1462,6 +1468,70 @@ nmtstp_link_sit_add (NMPlatform *platform, return pllink; } +const NMPlatformLink * +nmtstp_link_tun_add (NMPlatform *platform, + gboolean external_command, + const char *name, + const NMPlatformLnkTun *lnk, + int *out_fd) +{ + const NMPlatformLink *pllink = NULL; + NMPlatformError plerr; + int err; + + g_assert (nm_utils_is_valid_iface_name (name, NULL)); + g_assert (lnk); + g_assert (NM_IN_SET (lnk->type, IFF_TUN, IFF_TAP)); + g_assert (!out_fd || *out_fd == -1); + + if (!lnk->persist) { + /* ip tuntap does not support non-persistent devices. + * + * Add this device only via NMPlatform. */ + if (external_command == -1) + external_command = FALSE; + } + + external_command = nmtstp_run_command_check_external (external_command); + + _init_platform (&platform, external_command); + + if (external_command) { + g_assert (lnk->persist); + + err = nmtstp_run_command ("ip tuntap add" + " mode %s" + "%s" /* user */ + "%s" /* group */ + "%s" /* pi */ + "%s" /* vnet_hdr */ + "%s" /* multi_queue */ + " name %s", + lnk->type == IFF_TUN ? "tun" : "tap", + lnk->owner_valid ? nm_sprintf_bufa (100, " user %u", (guint) lnk->owner) : "", + lnk->group_valid ? nm_sprintf_bufa (100, " group %u", (guint) lnk->group) : "", + lnk->pi ? " pi" : "", + lnk->vnet_hdr ? " vnet_hdr" : "", + lnk->multi_queue ? " multi_queue" : "", + name); + /* Older versions of iproute2 don't support adding devices. + * On failure, fallback to using platform code. */ + if (err == 0) + pllink = nmtstp_assert_wait_for_link (platform, name, NM_LINK_TYPE_TUN, 100); + else + g_error ("failure to add tun/tap device via ip-route"); + } else { + g_assert (lnk->persist || out_fd); + plerr = nm_platform_link_tun_add (platform, name, lnk, &pllink, out_fd); + g_assert_cmpint (plerr, ==, NM_PLATFORM_ERROR_SUCCESS); + } + + g_assert (pllink); + g_assert_cmpint (pllink->type, ==, NM_LINK_TYPE_TUN); + g_assert_cmpstr (pllink->name, ==, name); + return pllink; +} + const NMPlatformLink * nmtstp_link_vxlan_add (NMPlatform *platform, gboolean external_command, @@ -1498,7 +1568,7 @@ nmtstp_link_vxlan_add (NMPlatform *platform, err = nmtstp_run_command ("ip link add %s type vxlan id %u %s local %s group %s ttl %u tos %02x dstport %u srcport %u %u ageing %u", name, lnk->id, - dev ? dev : "", + dev ?: "", local, remote, lnk->ttl, diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index fb406a3f..bd02b0d7 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -1,3 +1,21 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2016 - 2017 Red Hat, Inc. + */ + #include #include #include @@ -111,8 +129,11 @@ const NMPlatformLink *nmtstp_wait_for_link_until (NMPlatform *platform, const ch g_assert_not_reached (); \ } G_STMT_END -const NMPlatformLink *nmtstp_assert_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms); -const NMPlatformLink *nmtstp_assert_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 until_ms); +#define nmtstp_assert_wait_for_link(platform, ifname, expected_link_type, timeout_ms) \ + nmtst_assert_nonnull (nmtstp_wait_for_link (platform, ifname, expected_link_type, timeout_ms)) + +#define nmtstp_assert_wait_for_link_until(platform, ifname, expected_link_type, until_ms) \ + nmtst_assert_nonnull (nmtstp_wait_for_link_until (platform, ifname, expected_link_type, until_ms)) /*****************************************************************************/ @@ -289,6 +310,11 @@ const NMPlatformLink *nmtstp_link_sit_add (NMPlatform *platform, gboolean external_command, const char *name, const NMPlatformLnkSit *lnk); +const NMPlatformLink *nmtstp_link_tun_add (NMPlatform *platform, + gboolean external_command, + const char *name, + const NMPlatformLnkTun *lnk, + int *out_fd); const NMPlatformLink *nmtstp_link_vxlan_add (NMPlatform *platform, gboolean external_command, const char *name, diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c index 342aa0d6..8708c80f 100644 --- a/src/platform/tests/test-general.c +++ b/src/platform/tests/test-general.c @@ -27,7 +27,6 @@ #include "nm-test-utils-core.h" - /*****************************************************************************/ static void diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index ef78cc24..cd5880e3 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -1,6 +1,5 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager audit support - * +/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -24,6 +23,7 @@ #include #include #include +#include #include "platform/nmp-object.h" #include "platform/nmp-netns.h" @@ -698,6 +698,8 @@ test_software_detect (gconstpointer user_data) const NMPObject *lnk; guint i_step; const gboolean ext = test_data->external_command; + NMPlatformLnkTun lnk_tun; + nm_auto_close int tun_fd = -1; nmtstp_run_command_check ("ip link add %s type dummy", PARENT_NAME); ifindex_parent = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex; @@ -761,13 +763,27 @@ test_software_detect (gconstpointer user_data) gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip6_tunnel", NULL) != 0; } - lnk_ip6tnl.local = *nmtst_inet6_from_string ("fd01::15"); - lnk_ip6tnl.remote = *nmtst_inet6_from_string ("fd01::16"); - lnk_ip6tnl.parent_ifindex = ifindex_parent; - lnk_ip6tnl.tclass = 20; - lnk_ip6tnl.encap_limit = 6; - lnk_ip6tnl.flow_label = 1337; - lnk_ip6tnl.proto = IPPROTO_IPV6; + switch (test_data->test_mode) { + case 0: + lnk_ip6tnl.local = *nmtst_inet6_from_string ("fd01::15"); + lnk_ip6tnl.remote = *nmtst_inet6_from_string ("fd01::16"); + lnk_ip6tnl.parent_ifindex = ifindex_parent; + lnk_ip6tnl.tclass = 20; + lnk_ip6tnl.encap_limit = 6; + lnk_ip6tnl.flow_label = 1337; + lnk_ip6tnl.proto = IPPROTO_IPV6; + break; + case 1: + lnk_ip6tnl.local = *nmtst_inet6_from_string ("fd01::17"); + lnk_ip6tnl.remote = *nmtst_inet6_from_string ("fd01::18"); + lnk_ip6tnl.parent_ifindex = ifindex_parent; + lnk_ip6tnl.tclass = 0; + lnk_ip6tnl.encap_limit = 0; + lnk_ip6tnl.flow_label = 1338; + lnk_ip6tnl.proto = IPPROTO_IPV6; + lnk_ip6tnl.flags = IP6_TNL_F_IGN_ENCAP_LIMIT | IP6_TNL_F_USE_ORIG_TCLASS; + break; + } if (!nmtstp_link_ip6tnl_add (NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) { if (gracefully_skip) { @@ -879,6 +895,38 @@ test_software_detect (gconstpointer user_data) g_assert (nmtstp_link_vxlan_add (NULL, ext, DEVICE_NAME, &lnk_vxlan)); break; } + case NM_LINK_TYPE_TUN: { + gboolean owner_valid = nmtst_get_rand_bool (); + gboolean group_valid = nmtst_get_rand_bool (); + + switch (test_data->test_mode) { + case 0: + lnk_tun = (NMPlatformLnkTun) { + .type = nmtst_get_rand_bool () ? IFF_TUN : IFF_TAP, + .owner = owner_valid ? getuid () : 0, + .owner_valid = owner_valid, + .group = group_valid ? getgid () : 0, + .group_valid = group_valid, + .pi = nmtst_get_rand_bool (), + .vnet_hdr = nmtst_get_rand_bool (), + .multi_queue = nmtst_get_rand_bool (), + + /* if we add the device via iproute2 (external), we can only + * create persistent devices. */ + .persist = (ext == 1) ? TRUE : nmtst_get_rand_bool (), + }; + break; + default: + g_assert_not_reached (); + break; + } + + g_assert (nmtstp_link_tun_add (NULL, ext, DEVICE_NAME, &lnk_tun, + (!lnk_tun.persist || nmtst_get_rand_bool ()) + ? &tun_fd + : NULL)); + break; + } default: g_assert_not_reached (); } @@ -909,7 +957,13 @@ test_software_detect (gconstpointer user_data) lnk = nm_platform_link_get_lnk (NM_PLATFORM_GET, ifindex, test_data->link_type, &plink); g_assert (plink); g_assert_cmpint (plink->ifindex, ==, ifindex); - g_assert (lnk); + + if ( !lnk + && test_data->link_type == NM_LINK_TYPE_TUN) { + /* this is ok. Kernel apparently does not support tun properties via netlink. We + * fetch them from sysfs below. */ + } else + g_assert (lnk); switch (test_data->link_type) { case NM_LINK_TYPE_GRE: { @@ -931,15 +985,31 @@ test_software_detect (gconstpointer user_data) case NM_LINK_TYPE_IP6TNL: { const NMPlatformLnkIp6Tnl *plnk = &lnk->lnk_ip6tnl; - g_assert (plnk == nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, ifindex, NULL)); - g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent); - nmtst_assert_ip6_address (&plnk->local, "fd01::15"); - nmtst_assert_ip6_address (&plnk->remote, "fd01::16"); - g_assert_cmpint (plnk->ttl, ==, 0); - g_assert_cmpint (plnk->tclass, ==, 20); - g_assert_cmpint (plnk->encap_limit, ==, 6); - g_assert_cmpint (plnk->flow_label, ==, 1337); - g_assert_cmpint (plnk->proto, ==, IPPROTO_IPV6); + switch (test_data->test_mode) { + case 0: + g_assert (plnk == nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent); + nmtst_assert_ip6_address (&plnk->local, "fd01::15"); + nmtst_assert_ip6_address (&plnk->remote, "fd01::16"); + g_assert_cmpint (plnk->ttl, ==, 0); + g_assert_cmpint (plnk->tclass, ==, 20); + g_assert_cmpint (plnk->encap_limit, ==, 6); + g_assert_cmpint (plnk->flow_label, ==, 1337); + g_assert_cmpint (plnk->proto, ==, IPPROTO_IPV6); + break; + case 1: + g_assert (plnk == nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent); + nmtst_assert_ip6_address (&plnk->local, "fd01::17"); + nmtst_assert_ip6_address (&plnk->remote, "fd01::18"); + g_assert_cmpint (plnk->ttl, ==, 0); + g_assert_cmpint (plnk->flow_label, ==, 1338); + g_assert_cmpint (plnk->proto, ==, IPPROTO_IPV6); + g_assert_cmpint (plnk->flags & 0xFFFF, /* ignore kernel internal flags */ + ==, + IP6_TNL_F_IGN_ENCAP_LIMIT | IP6_TNL_F_USE_ORIG_TCLASS); + break; + } break; } case NM_LINK_TYPE_IPIP: { @@ -982,6 +1052,35 @@ test_software_detect (gconstpointer user_data) g_assert_cmpint (plnk->path_mtu_discovery, ==, FALSE); break; } + case NM_LINK_TYPE_TUN: { + const NMPlatformLnkTun *plnk; + NMPlatformLnkTun lnk_tun2; + + g_assert ((lnk ? &lnk->lnk_tun : NULL) == nm_platform_link_get_lnk_tun (NM_PLATFORM_GET, ifindex, NULL)); + + /* kernel might not expose tun options via netlink. Either way, try + * to read them (either from platform cache, or fallback to sysfs). + * See also: rh#1547213. */ + if (!nm_platform_link_tun_get_properties (NM_PLATFORM_GET, + ifindex, + &lnk_tun2)) + g_assert_not_reached (); + + plnk = lnk ? &lnk->lnk_tun : &lnk_tun2; + if (lnk) + g_assert (memcmp (plnk, &lnk_tun2, sizeof (NMPlatformLnkTun)) == 0); + + if (i_step == 0) { + /* Before we upped the device for the first time the kernel didn't notify + * us of the owner set after the link creation: + * https://bugzilla.redhat.com/show_bug.cgi?id=1566062 + */ + break; + } + + g_assert (nm_platform_lnk_tun_cmp (plnk, &lnk_tun) == 0); + break; + } case NM_LINK_TYPE_VLAN: { const NMPlatformLnkVlan *plnk = &lnk->lnk_vlan; @@ -1422,7 +1521,6 @@ test_vlan_set_xgress (void) 6, 7); } - { const NMVlanQosMapping ingress_map[] = { { .from = 1, .to = 5 }, @@ -1735,14 +1833,12 @@ test_nl_bugs_veth (void) } g_assert_cmpint (pllink_veth0->parent, ==, ifindex_veth1); - /* The following tests whether we have a workaround for kernel bug * https://bugzilla.redhat.com/show_bug.cgi?id=1285827 in place. */ pllink_veth1 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth1); g_assert (pllink_veth1); g_assert_cmpint (pllink_veth1->parent, ==, ifindex_veth0); - /* move one veth peer to another namespace and check that the * parent/IFLA_LINK of the remaining peer properly updates * (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */ @@ -2248,7 +2344,7 @@ test_netns_push (gpointer fixture, gconstpointer test_data) p = pl_base; for (j = nstack; j >= 1; ) { j--; - if (NM_FLAGS_HAS (stack[j].ns_types, ns_type)) { + if (NM_FLAGS_ANY (stack[j].ns_types, ns_type)) { p = stack[j].pl; break; } @@ -2290,7 +2386,6 @@ test_netns_push (gpointer fixture, gconstpointer test_data) g_assert_not_reached (); } - for (i = nstack; i >= 1; ) { i--; nmp_netns_pop (nm_platform_netns_get (stack[i].pl->platform)); @@ -2546,11 +2641,13 @@ _nmtstp_setup_tests (void) g_test_add_func ("/link/external", test_external); test_software_detect_add ("/link/software/detect/gre", NM_LINK_TYPE_GRE, 0); - test_software_detect_add ("/link/software/detect/ip6tnl", NM_LINK_TYPE_IP6TNL, 0); + test_software_detect_add ("/link/software/detect/ip6tnl/0", NM_LINK_TYPE_IP6TNL, 0); + test_software_detect_add ("/link/software/detect/ip6tnl/1", NM_LINK_TYPE_IP6TNL, 1); test_software_detect_add ("/link/software/detect/ipip", NM_LINK_TYPE_IPIP, 0); test_software_detect_add ("/link/software/detect/macvlan", NM_LINK_TYPE_MACVLAN, 0); test_software_detect_add ("/link/software/detect/macvtap", NM_LINK_TYPE_MACVTAP, 0); test_software_detect_add ("/link/software/detect/sit", NM_LINK_TYPE_SIT, 0); + test_software_detect_add ("/link/software/detect/tun", NM_LINK_TYPE_TUN, 0); test_software_detect_add ("/link/software/detect/vlan", NM_LINK_TYPE_VLAN, 0); test_software_detect_add ("/link/software/detect/vxlan/0", NM_LINK_TYPE_VXLAN, 0); test_software_detect_add ("/link/software/detect/vxlan/1", NM_LINK_TYPE_VXLAN, 1); diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c index 3228de83..047d7a85 100644 --- a/src/platform/tests/test-nmp-object.c +++ b/src/platform/tests/test-nmp-object.c @@ -54,27 +54,20 @@ test_obj_base (void) gs_unref_object GCancellable *obj_cancellable = g_cancellable_new (); nm_auto_nmpobj NMPObject *obj_link = nmp_object_new_link (10); -#define STATIC_ASSERT(cond) \ - G_STMT_START { \ - G_STATIC_ASSERT (cond); \ - G_STATIC_ASSERT_EXPR (cond); \ - g_assert (cond); \ - } G_STMT_END + g_assert (&g->g_type_instance == (void *) &o->_class); + g_assert (&g->g_type_instance.g_class == (void *) &o->_class); - STATIC_ASSERT (&g->g_type_instance == (void *) &o->_class); - STATIC_ASSERT (&g->g_type_instance.g_class == (void *) &o->_class); + g_assert (sizeof (o->parent.parent) == sizeof (GTypeInstance)); - STATIC_ASSERT (sizeof (o->parent.parent) == sizeof (GTypeInstance)); + g_assert (&c->parent == (void *) c); + g_assert (&c->parent.parent.g_type_class == (void *) c); + g_assert (&c->parent.parent.g_type == (void *) c); + g_assert (&c->parent.parent.g_type == &k->g_type); - STATIC_ASSERT (&c->parent == (void *) c); - STATIC_ASSERT (&c->parent.parent.g_type_class == (void *) c); - STATIC_ASSERT (&c->parent.parent.g_type == (void *) c); - STATIC_ASSERT (&c->parent.parent.g_type == &k->g_type); + g_assert (sizeof (c->parent.parent) == sizeof (GTypeClass)); - STATIC_ASSERT (sizeof (c->parent.parent) == sizeof (GTypeClass)); - - STATIC_ASSERT (&o->parent == (void *) o); - STATIC_ASSERT (&o->parent.klass == (void *) &o->_class); + g_assert (&o->parent == (void *) o); + g_assert (&o->parent.klass == (void *) &o->_class); obj = (NMObjBaseInst *) obj_cancellable; g_assert (!NMP_CLASS_IS_VALID ((NMPClass *) obj->klass)); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 13648f16..85b14b57 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -1,6 +1,5 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager audit support - * +/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/src/platform/wifi/wifi-utils-nl80211.c b/src/platform/wifi/wifi-utils-nl80211.c index a5f25b02..767e23a9 100644 --- a/src/platform/wifi/wifi-utils-nl80211.c +++ b/src/platform/wifi/wifi-utils-nl80211.c @@ -22,17 +22,17 @@ #include "nm-default.h" +#include "wifi-utils-nl80211.h" + #include #include #include #include #include -#include -#include #include +#include "platform/nm-netlink.h" #include "wifi-utils-private.h" -#include "wifi-utils-nl80211.h" #include "platform/nm-platform.h" #include "platform/nm-platform-utils.h" #include "nm-utils.h" @@ -46,223 +46,14 @@ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ } G_STMT_END -/*****************************************************************************/ - -static int -_nl_nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, - const struct nla_policy *policy) -{ - return nla_parse (tb, maxtype, head, len, (struct nla_policy *) policy); -} -#define nla_parse(...) _nl_nla_parse(__VA_ARGS__) - -static int -_nl_nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla, - const struct nla_policy *policy) -{ - return nla_parse_nested (tb, maxtype, nla, (struct nla_policy *) policy); -} -#define nla_parse_nested(...) _nl_nla_parse_nested(__VA_ARGS__) - -/***************************************************************************** - * Copied from libnl3/genl: - *****************************************************************************/ - -static void * -genlmsg_put (struct nl_msg *msg, uint32_t port, uint32_t seq, int family, - int hdrlen, int flags, uint8_t cmd, uint8_t version) -{ - struct nlmsghdr *nlh; - struct genlmsghdr hdr = { - .cmd = cmd, - .version = version, - }; - - nlh = nlmsg_put (msg, port, seq, family, GENL_HDRLEN + hdrlen, flags); - if (nlh == NULL) - return NULL; - - memcpy (nlmsg_data (nlh), &hdr, sizeof (hdr)); - - return (char *) nlmsg_data (nlh) + GENL_HDRLEN; -} - -static void * -genlmsg_data (const struct genlmsghdr *gnlh) -{ - return ((unsigned char *) gnlh + GENL_HDRLEN); -} - -static void * -genlmsg_user_hdr (const struct genlmsghdr *gnlh) -{ - return genlmsg_data (gnlh); -} - -static struct genlmsghdr * -genlmsg_hdr (struct nlmsghdr *nlh) -{ - return nlmsg_data (nlh); -} - -static void * -genlmsg_user_data (const struct genlmsghdr *gnlh, const int hdrlen) -{ - return (char *) genlmsg_user_hdr (gnlh) + NLMSG_ALIGN (hdrlen); -} - -static struct nlattr * -genlmsg_attrdata (const struct genlmsghdr *gnlh, int hdrlen) -{ - return genlmsg_user_data (gnlh, hdrlen); -} - -static int -genlmsg_len (const struct genlmsghdr *gnlh) -{ - const struct nlmsghdr *nlh; - - nlh = (const struct nlmsghdr *) ((const unsigned char *) gnlh - NLMSG_HDRLEN); - return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); -} - -static int -genlmsg_attrlen (const struct genlmsghdr *gnlh, int hdrlen) -{ - return genlmsg_len (gnlh) - NLMSG_ALIGN (hdrlen); -} - -static int -genlmsg_valid_hdr (struct nlmsghdr *nlh, int hdrlen) -{ - struct genlmsghdr *ghdr; - - if (!nlmsg_valid_hdr (nlh, GENL_HDRLEN)) - return 0; - - ghdr = nlmsg_data (nlh); - if (genlmsg_len (ghdr) < NLMSG_ALIGN (hdrlen)) - return 0; - - return 1; -} - -static int -genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], - int maxtype, const struct nla_policy *policy) -{ - struct genlmsghdr *ghdr; - - if (!genlmsg_valid_hdr (nlh, hdrlen)) - return -NLE_MSG_TOOSHORT; - - ghdr = nlmsg_data (nlh); - return nla_parse (tb, maxtype, genlmsg_attrdata (ghdr, hdrlen), - genlmsg_attrlen (ghdr, hdrlen), policy); -} - -/***************************************************************************** - * Reimplementation of libnl3/genl functions: - *****************************************************************************/ - -static int -probe_response (struct nl_msg *msg, void *arg) -{ - static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { - [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, - [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING, - .maxlen = GENL_NAMSIZ }, - [CTRL_ATTR_VERSION] = { .type = NLA_U32 }, - [CTRL_ATTR_HDRSIZE] = { .type = NLA_U32 }, - [CTRL_ATTR_MAXATTR] = { .type = NLA_U32 }, - [CTRL_ATTR_OPS] = { .type = NLA_NESTED }, - [CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED }, - }; - struct nlattr *tb[CTRL_ATTR_MAX+1]; - struct nlmsghdr *nlh = nlmsg_hdr (msg); - gint32 *response_data = arg; - - if (genlmsg_parse (nlh, 0, tb, CTRL_ATTR_MAX, ctrl_policy)) - return NL_SKIP; - - if (tb[CTRL_ATTR_FAMILY_ID]) - *response_data = nla_get_u16 (tb[CTRL_ATTR_FAMILY_ID]); - - return NL_STOP; -} - -static int -genl_ctrl_resolve (struct nl_sock *sk, const char *name) -{ - struct nl_msg *msg; - struct nl_cb *cb, *orig; - int rc; - int result = -NLE_OBJ_NOTFOUND; - gint32 response_data = -1; - - if (!(orig = nl_socket_get_cb (sk))) - goto out; - - cb = nl_cb_clone (orig); - nl_cb_put (orig); - if (!cb) - goto out; - - msg = nlmsg_alloc (); - if (!msg) - goto out_cb_free; - - if (!genlmsg_put (msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, - 0, 0, CTRL_CMD_GETFAMILY, 1)) - goto out_msg_free; - - if (nla_put_string (msg, CTRL_ATTR_FAMILY_NAME, name) < 0) - goto out_msg_free; - - rc = nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, probe_response, &response_data); - if (rc < 0) - goto out_msg_free; - - rc = nl_send_auto_complete (sk, msg); - if (rc < 0) - goto out_msg_free; - - rc = nl_recvmsgs (sk, cb); - if (rc < 0) - goto out_msg_free; - - /* If search was successful, request may be ACKed after data */ - rc = nl_wait_for_ack (sk); - if (rc < 0) - goto out_msg_free; - - if (response_data > 0) - result = response_data; - -out_msg_free: - nlmsg_free (msg); -out_cb_free: - nl_cb_put (cb); -out: - if (result >= 0) - _LOGD (LOGD_WIFI, "genl_ctrl_resolve: resolved \"%s\" as 0x%x", name, result); - else - _LOGE (LOGD_WIFI, "genl_ctrl_resolve: failed resolve \"%s\"", name); - return result; -} - -/***************************************************************************** - * - *****************************************************************************/ - typedef struct { WifiData parent; struct nl_sock *nl_sock; - int id; - struct nl_cb *nl_cb; guint32 *freqs; + int id; int num_freqs; int phy; + bool can_wowlan:1; } WifiDataNl80211; static int @@ -292,19 +83,16 @@ error_handler (struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) static struct nl_msg * _nl80211_alloc_msg (int id, int ifindex, int phy, guint32 cmd, guint32 flags) { - struct nl_msg *msg; + nm_auto_nlmsg struct nl_msg *msg = NULL; msg = nlmsg_alloc (); - if (msg) { - genlmsg_put (msg, 0, 0, id, 0, flags, cmd, 0); - NLA_PUT_U32 (msg, NL80211_ATTR_IFINDEX, ifindex); - if (phy != -1) - NLA_PUT_U32 (msg, NL80211_ATTR_WIPHY, phy); - } - return msg; + genlmsg_put (msg, 0, 0, id, 0, flags, cmd, 0); + NLA_PUT_U32 (msg, NL80211_ATTR_IFINDEX, ifindex); + if (phy != -1) + NLA_PUT_U32 (msg, NL80211_ATTR_WIPHY, phy); + return g_steal_pointer (&msg); - nla_put_failure: - nlmsg_free (msg); +nla_put_failure: return NULL; } @@ -314,42 +102,37 @@ nl80211_alloc_msg (WifiDataNl80211 *nl80211, guint32 cmd, guint32 flags) return _nl80211_alloc_msg (nl80211->id, nl80211->parent.ifindex, nl80211->phy, cmd, flags); } -/* NOTE: this function consumes 'msg' */ static int _nl80211_send_and_recv (struct nl_sock *nl_sock, - struct nl_cb *nl_cb, struct nl_msg *msg, int (*valid_handler) (struct nl_msg *, void *), void *valid_data) { - struct nl_cb *cb; - int err, done; + int err; + int done = 0; + const struct nl_cb cb = { + .err_cb = error_handler, + .err_arg = &done, + .finish_cb = finish_handler, + .finish_arg = &done, + .ack_cb = ack_handler, + .ack_arg = &done, + .valid_cb = valid_handler, + .valid_arg = valid_data, + }; g_return_val_if_fail (msg != NULL, -ENOMEM); - cb = nl_cb_clone (nl_cb); - if (!cb) { - err = -ENOMEM; - goto out; - } - - err = nl_send_auto_complete (nl_sock, msg); + err = nl_send_auto (nl_sock, msg); if (err < 0) - goto out; - - done = 0; - nl_cb_err (cb, NL_CB_CUSTOM, error_handler, &done); - nl_cb_set (cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &done); - nl_cb_set (cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &done); - if (valid_handler) - nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, valid_data); + return err; /* Loop until one of our NL callbacks says we're done; on success * done will be 1, on error it will be < 0. */ while (!done) { - err = nl_recvmsgs (nl_sock, cb); - if (err && err != -NLE_AGAIN) { + err = nl_recvmsgs (nl_sock, &cb); + if (err < 0 && err != -EAGAIN) { /* Kernel scan list can change while we are dumping it, as new scan * results from H/W can arrive. BSS info is assured to be consistent * and we don't need consistent view of whole scan list. Hence do @@ -364,12 +147,9 @@ _nl80211_send_and_recv (struct nl_sock *nl_sock, break; } } - if (err == 0 && done < 0) - err = done; - out: - nl_cb_put (cb); - nlmsg_free (msg); + if (err >= 0 && done < 0) + err = done; return err; } @@ -379,7 +159,7 @@ nl80211_send_and_recv (WifiDataNl80211 *nl80211, int (*valid_handler) (struct nl_msg *, void *), void *valid_data) { - return _nl80211_send_and_recv (nl80211->nl_sock, nl80211->nl_cb, msg, + return _nl80211_send_and_recv (nl80211->nl_sock, msg, valid_handler, valid_data); } @@ -390,8 +170,6 @@ wifi_nl80211_deinit (WifiData *parent) if (nl80211->nl_sock) nl_socket_free (nl80211->nl_sock); - if (nl80211->nl_cb) - nl_cb_put (nl80211->nl_cb); g_free (nl80211->freqs); } @@ -435,12 +213,12 @@ wifi_nl80211_get_mode (WifiData *data) struct nl80211_iface_info iface_info = { .mode = NM_802_11_MODE_UNKNOWN, }; - struct nl_msg *msg; + nm_auto_nlmsg struct nl_msg *msg = NULL; msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_INTERFACE, 0); if (nl80211_send_and_recv (nl80211, msg, nl80211_iface_info_handler, - &iface_info) < 0) + &iface_info) < 0) return NM_802_11_MODE_UNKNOWN; return iface_info.mode; @@ -450,7 +228,7 @@ static gboolean wifi_nl80211_set_mode (WifiData *data, const NM80211Mode mode) { WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; - struct nl_msg *msg; + nm_auto_nlmsg struct nl_msg *msg = NULL; int err; msg = nl80211_alloc_msg (nl80211, NL80211_CMD_SET_INTERFACE, 0); @@ -470,10 +248,9 @@ wifi_nl80211_set_mode (WifiData *data, const NM80211Mode mode) } err = nl80211_send_and_recv (nl80211, msg, NULL, NULL); - return err ? FALSE : TRUE; + return err >= 0; - nla_put_failure: - nlmsg_free (msg); +nla_put_failure: return FALSE; } @@ -481,17 +258,16 @@ static gboolean wifi_nl80211_set_powersave (WifiData *data, guint32 powersave) { WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; - struct nl_msg *msg; + nm_auto_nlmsg struct nl_msg *msg = NULL; int err; msg = nl80211_alloc_msg (nl80211, NL80211_CMD_SET_POWER_SAVE, 0); NLA_PUT_U32 (msg, NL80211_ATTR_PS_STATE, powersave == 1 ? NL80211_PS_ENABLED : NL80211_PS_DISABLED); err = nl80211_send_and_recv (nl80211, msg, NULL, NULL); - return err ? FALSE : TRUE; + return err >= 0; nla_put_failure: - nlmsg_free (msg); return FALSE; } @@ -518,7 +294,7 @@ struct nl80211_bss_info { gboolean valid; }; -#define WLAN_EID_SSID 0 +#define WLAN_EID_SSID 0 static void find_ssid (guint8 *ies, guint32 ies_len, @@ -618,7 +394,7 @@ static void nl80211_get_bss_info (WifiDataNl80211 *nl80211, struct nl80211_bss_info *bss_info) { - struct nl_msg *msg; + nm_auto_nlmsg struct nl_msg *msg = NULL; memset (bss_info, 0, sizeof (*bss_info)); @@ -742,7 +518,7 @@ static void nl80211_get_ap_info (WifiDataNl80211 *nl80211, struct nl80211_station_info *sta_info) { - struct nl_msg *msg; + nm_auto_nlmsg struct nl_msg *msg = NULL; struct nl80211_bss_info bss_info; memset (sta_info, 0, sizeof (*sta_info)); @@ -764,8 +540,7 @@ nl80211_get_ap_info (WifiDataNl80211 *nl80211, return; - nla_put_failure: - nlmsg_free (msg); +nla_put_failure: return; } @@ -790,35 +565,37 @@ wifi_nl80211_get_qual (WifiData *data) return sta_info.signal; } -#if HAVE_NL80211_CRITICAL_PROTOCOL_CMDS static gboolean wifi_nl80211_indicate_addressing_running (WifiData *data, gboolean running) { WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; - struct nl_msg *msg; + nm_auto_nlmsg struct nl_msg *msg = NULL; int err; msg = nl80211_alloc_msg (nl80211, - running ? NL80211_CMD_CRIT_PROTOCOL_START : - NL80211_CMD_CRIT_PROTOCOL_STOP, + running + ? 98 /* NL80211_CMD_CRIT_PROTOCOL_START */ + : 99 /* NL80211_CMD_CRIT_PROTOCOL_STOP */, 0); /* Despite the DHCP name, we're using this for any type of IP addressing, * DHCPv4, DHCPv6, and IPv6 SLAAC. */ - NLA_PUT_U16 (msg, NL80211_ATTR_CRIT_PROT_ID, NL80211_CRIT_PROTO_DHCP); + NLA_PUT_U16 (msg, + 179 /* NL80211_ATTR_CRIT_PROT_ID */, + 1 /* NL80211_CRIT_PROTO_DHCP */); if (running) { /* Give DHCP 5 seconds to complete */ - NLA_PUT_U16 (msg, NL80211_ATTR_MAX_CRIT_PROT_DURATION, 5000); + NLA_PUT_U16 (msg, + 180 /* NL80211_ATTR_MAX_CRIT_PROT_DURATION */, + 5000); } err = nl80211_send_and_recv (nl80211, msg, NULL, NULL); - return err ? FALSE : TRUE; + return err >= 0; nla_put_failure: - nlmsg_free (msg); return FALSE; } -#endif struct nl80211_wowlan_info { gboolean enabled; @@ -847,12 +624,14 @@ static gboolean wifi_nl80211_get_wowlan (WifiData *data) { WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; - struct nl_msg *msg; + nm_auto_nlmsg struct nl_msg *msg = NULL; struct nl80211_wowlan_info info; + if (!nl80211->can_wowlan) + return FALSE; + msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_WOWLAN, 0); nl80211_send_and_recv (nl80211, msg, nl80211_wowlan_handler, &info); - return info.enabled; } @@ -1062,8 +841,22 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) WifiData * wifi_nl80211_init (int ifindex) { + static const WifiDataClass klass = { + .struct_size = sizeof (WifiDataNl80211), + .get_mode = wifi_nl80211_get_mode, + .set_mode = wifi_nl80211_set_mode, + .set_powersave = wifi_nl80211_set_powersave, + .get_freq = wifi_nl80211_get_freq, + .find_freq = wifi_nl80211_find_freq, + .get_bssid = wifi_nl80211_get_bssid, + .get_rate = wifi_nl80211_get_rate, + .get_qual = wifi_nl80211_get_qual, + .get_wowlan = wifi_nl80211_get_wowlan, + .indicate_addressing_running = wifi_nl80211_indicate_addressing_running, + .deinit = wifi_nl80211_deinit, + }; WifiDataNl80211 *nl80211; - struct nl_msg *msg; + nm_auto_nlmsg struct nl_msg *msg = NULL; struct nl80211_device_info device_info = {}; char ifname[IFNAMSIZ]; @@ -1073,19 +866,7 @@ wifi_nl80211_init (int ifindex) nm_sprintf_buf (ifname, "if %d", ifindex); } - nl80211 = wifi_data_new (ifindex, sizeof (*nl80211)); - nl80211->parent.get_mode = wifi_nl80211_get_mode; - nl80211->parent.set_mode = wifi_nl80211_set_mode; - nl80211->parent.set_powersave = wifi_nl80211_set_powersave; - nl80211->parent.get_freq = wifi_nl80211_get_freq; - nl80211->parent.find_freq = wifi_nl80211_find_freq; - nl80211->parent.get_bssid = wifi_nl80211_get_bssid; - nl80211->parent.get_rate = wifi_nl80211_get_rate; - nl80211->parent.get_qual = wifi_nl80211_get_qual; -#if HAVE_NL80211_CRITICAL_PROTOCOL_CMDS - nl80211->parent.indicate_addressing_running = wifi_nl80211_indicate_addressing_running; -#endif - nl80211->parent.deinit = wifi_nl80211_deinit; + nl80211 = wifi_data_new (&klass, ifindex); nl80211->nl_sock = nl_socket_alloc (); if (nl80211->nl_sock == NULL) @@ -1095,12 +876,10 @@ wifi_nl80211_init (int ifindex) goto error; nl80211->id = genl_ctrl_resolve (nl80211->nl_sock, "nl80211"); - if (nl80211->id < 0) - goto error; - - nl80211->nl_cb = nl_cb_alloc (NL_CB_DEFAULT); - if (nl80211->nl_cb == NULL) + if (nl80211->id < 0) { + _LOGD (LOGD_WIFI, "genl_ctrl_resolve: failed to resolve \"nl80211\""); goto error; + } nl80211->phy = -1; @@ -1153,18 +932,15 @@ wifi_nl80211_init (int ifindex) nl80211->freqs = device_info.freqs; nl80211->num_freqs = device_info.num_freqs; nl80211->parent.caps = device_info.caps; - - if (device_info.can_wowlan) - nl80211->parent.get_wowlan = wifi_nl80211_get_wowlan; + nl80211->can_wowlan = device_info.can_wowlan; _LOGI (LOGD_PLATFORM | LOGD_WIFI, "(%s): using nl80211 for WiFi device control", ifname); - return (WifiData *) nl80211; error: - wifi_utils_deinit ((WifiData *) nl80211); + wifi_utils_unref ((WifiData *) nl80211); return NULL; } diff --git a/src/platform/wifi/wifi-utils-private.h b/src/platform/wifi/wifi-utils-private.h index 11a0f060..59386514 100644 --- a/src/platform/wifi/wifi-utils-private.h +++ b/src/platform/wifi/wifi-utils-private.h @@ -24,9 +24,8 @@ #include "nm-dbus-interface.h" #include "wifi-utils.h" -struct WifiData { - int ifindex; - NMDeviceWifiCapabilities caps; +typedef struct { + gsize struct_size; NM80211Mode (*get_mode) (WifiData *data); @@ -66,9 +65,14 @@ struct WifiData { gboolean (*set_mesh_ssid) (WifiData *data, const guint8 *ssid, gsize len); gboolean (*indicate_addressing_running) (WifiData *data, gboolean running); +} WifiDataClass; + +struct WifiData { + const WifiDataClass *klass; + int ifindex; + NMDeviceWifiCapabilities caps; }; -gpointer wifi_data_new (int ifindex, gsize len); -void wifi_data_free (WifiData *data); +gpointer wifi_data_new (const WifiDataClass *klass, int ifindex); #endif /* __WIFI_UTILS_PRIVATE_H__ */ diff --git a/src/platform/wifi/wifi-utils-wext.c b/src/platform/wifi/wifi-utils-wext.c index c4d3c999..c8744f79 100644 --- a/src/platform/wifi/wifi-utils-wext.c +++ b/src/platform/wifi/wifi-utils-wext.c @@ -21,17 +21,14 @@ #include "nm-default.h" +#include "wifi-utils-wext.h" + #include #include #include #include #include -#include "wifi-utils-private.h" -#include "wifi-utils-wext.h" -#include "nm-utils.h" -#include "platform/nm-platform-utils.h" - /* Hacks necessary to #include wireless.h; yay for WEXT */ #ifndef __user #define __user @@ -41,6 +38,10 @@ #include #include +#include "wifi-utils-private.h" +#include "nm-utils.h" +#include "platform/nm-platform-utils.h" + typedef struct { WifiData parent; int fd; @@ -628,6 +629,21 @@ wext_get_caps (WifiDataWext *wext, const char *ifname, struct iw_range *range) WifiData * wifi_wext_init (int ifindex, gboolean check_scan) { + static const WifiDataClass klass = { + .struct_size = sizeof (WifiDataWext), + .get_mode = wifi_wext_get_mode, + .set_mode = wifi_wext_set_mode, + .set_powersave = wifi_wext_set_powersave, + .get_freq = wifi_wext_get_freq, + .find_freq = wifi_wext_find_freq, + .get_bssid = wifi_wext_get_bssid, + .get_rate = wifi_wext_get_rate, + .get_qual = wifi_wext_get_qual, + .deinit = wifi_wext_deinit, + .get_mesh_channel = wifi_wext_get_mesh_channel, + .set_mesh_channel = wifi_wext_set_mesh_channel, + .set_mesh_ssid = wifi_wext_set_mesh_ssid, + }; WifiDataWext *wext; struct iw_range range; guint32 response_len = 0; @@ -642,19 +658,7 @@ wifi_wext_init (int ifindex, gboolean check_scan) return NULL; } - wext = wifi_data_new (ifindex, sizeof (*wext)); - wext->parent.get_mode = wifi_wext_get_mode; - wext->parent.set_mode = wifi_wext_set_mode; - wext->parent.set_powersave = wifi_wext_set_powersave; - wext->parent.get_freq = wifi_wext_get_freq; - wext->parent.find_freq = wifi_wext_find_freq; - wext->parent.get_bssid = wifi_wext_get_bssid; - wext->parent.get_rate = wifi_wext_get_rate; - wext->parent.get_qual = wifi_wext_get_qual; - wext->parent.deinit = wifi_wext_deinit; - wext->parent.get_mesh_channel = wifi_wext_get_mesh_channel; - wext->parent.set_mesh_channel = wifi_wext_set_mesh_channel; - wext->parent.set_mesh_ssid = wifi_wext_set_mesh_ssid; + wext = wifi_data_new (&klass, ifindex); wext->fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (wext->fd < 0) @@ -730,7 +734,7 @@ wifi_wext_init (int ifindex, gboolean check_scan) return (WifiData *) wext; error: - wifi_utils_deinit ((WifiData *) wext); + wifi_utils_unref ((WifiData *) wext); return NULL; } diff --git a/src/platform/wifi/wifi-utils.c b/src/platform/wifi/wifi-utils.c index d0052121..8818dc9d 100644 --- a/src/platform/wifi/wifi-utils.c +++ b/src/platform/wifi/wifi-utils.c @@ -38,22 +38,19 @@ #include "platform/nm-platform-utils.h" gpointer -wifi_data_new (int ifindex, gsize len) +wifi_data_new (const WifiDataClass *klass, int ifindex) { WifiData *data; - data = g_malloc0 (len); + nm_assert (klass); + nm_assert (klass->struct_size > sizeof (WifiData)); + + data = g_malloc0 (klass->struct_size); + data->klass = klass; data->ifindex = ifindex; return data; } -void -wifi_data_free (WifiData *data) -{ - memset (data, 0, sizeof (*data)); - g_free (data); -} - /*****************************************************************************/ WifiData * @@ -85,14 +82,14 @@ wifi_utils_get_caps (WifiData *data) { g_return_val_if_fail (data != NULL, NM_WIFI_DEVICE_CAP_NONE); - return data->caps; + return data->caps; } NM80211Mode wifi_utils_get_mode (WifiData *data) { g_return_val_if_fail (data != NULL, NM_802_11_MODE_UNKNOWN); - return data->get_mode (data); + return data->klass->get_mode (data); } gboolean @@ -104,7 +101,7 @@ wifi_utils_set_mode (WifiData *data, const NM80211Mode mode) || (mode == NM_802_11_MODE_ADHOC), FALSE); /* nl80211 probably doesn't need this */ - return data->set_mode ? data->set_mode (data, mode) : TRUE; + return data->klass->set_mode ? data->klass->set_mode (data, mode) : TRUE; } gboolean @@ -112,14 +109,14 @@ wifi_utils_set_powersave (WifiData *data, guint32 powersave) { g_return_val_if_fail (data != NULL, FALSE); - return data->set_powersave ? data->set_powersave (data, powersave) : TRUE; + return data->klass->set_powersave ? data->klass->set_powersave (data, powersave) : TRUE; } guint32 wifi_utils_get_freq (WifiData *data) { g_return_val_if_fail (data != NULL, 0); - return data->get_freq (data); + return data->klass->get_freq (data); } guint32 @@ -127,7 +124,7 @@ wifi_utils_find_freq (WifiData *data, const guint32 *freqs) { g_return_val_if_fail (data != NULL, 0); g_return_val_if_fail (freqs != NULL, 0); - return data->find_freq (data, freqs); + return data->klass->find_freq (data, freqs); } gboolean @@ -137,38 +134,40 @@ wifi_utils_get_bssid (WifiData *data, guint8 *out_bssid) g_return_val_if_fail (out_bssid != NULL, FALSE); memset (out_bssid, 0, ETH_ALEN); - return data->get_bssid (data, out_bssid); + return data->klass->get_bssid (data, out_bssid); } guint32 wifi_utils_get_rate (WifiData *data) { g_return_val_if_fail (data != NULL, 0); - return data->get_rate (data); + return data->klass->get_rate (data); } int wifi_utils_get_qual (WifiData *data) { g_return_val_if_fail (data != NULL, 0); - return data->get_qual (data); + return data->klass->get_qual (data); } gboolean wifi_utils_get_wowlan (WifiData *data) { g_return_val_if_fail (data != NULL, 0); - if (!data->get_wowlan) + + if (!data->klass->get_wowlan) return FALSE; - return data->get_wowlan (data); + return data->klass->get_wowlan (data); } void -wifi_utils_deinit (WifiData *data) +wifi_utils_unref (WifiData *data) { g_return_if_fail (data != NULL); - data->deinit (data); - wifi_data_free (data); + + data->klass->deinit (data); + g_free (data); } gboolean @@ -191,8 +190,8 @@ guint32 wifi_utils_get_mesh_channel (WifiData *data) { g_return_val_if_fail (data != NULL, FALSE); - g_return_val_if_fail (data->get_mesh_channel != NULL, FALSE); - return data->get_mesh_channel (data); + g_return_val_if_fail (data->klass->get_mesh_channel != NULL, FALSE); + return data->klass->get_mesh_channel (data); } gboolean @@ -200,24 +199,24 @@ wifi_utils_set_mesh_channel (WifiData *data, guint32 channel) { g_return_val_if_fail (data != NULL, FALSE); g_return_val_if_fail (channel <= 13, FALSE); - g_return_val_if_fail (data->set_mesh_channel != NULL, FALSE); - return data->set_mesh_channel (data, channel); + g_return_val_if_fail (data->klass->set_mesh_channel != NULL, FALSE); + return data->klass->set_mesh_channel (data, channel); } gboolean wifi_utils_set_mesh_ssid (WifiData *data, const guint8 *ssid, gsize len) { g_return_val_if_fail (data != NULL, FALSE); - g_return_val_if_fail (data->set_mesh_ssid != NULL, FALSE); - return data->set_mesh_ssid (data, ssid, len); + g_return_val_if_fail (data->klass->set_mesh_ssid != NULL, FALSE); + return data->klass->set_mesh_ssid (data, ssid, len); } gboolean wifi_utils_indicate_addressing_running (WifiData *data, gboolean running) { g_return_val_if_fail (data != NULL, FALSE); - if (data->indicate_addressing_running) - return data->indicate_addressing_running (data, running); + if (data->klass->indicate_addressing_running) + return data->klass->indicate_addressing_running (data, running); return FALSE; } diff --git a/src/platform/wifi/wifi-utils.h b/src/platform/wifi/wifi-utils.h index 705717b0..8acc35fc 100644 --- a/src/platform/wifi/wifi-utils.h +++ b/src/platform/wifi/wifi-utils.h @@ -34,7 +34,7 @@ WifiData *wifi_utils_init (int ifindex, gboolean check_scan); int wifi_utils_get_ifindex (WifiData *data); -void wifi_utils_deinit (WifiData *data); +void wifi_utils_unref (WifiData *data); NMDeviceWifiCapabilities wifi_utils_get_caps (WifiData *data); @@ -66,7 +66,6 @@ gboolean wifi_utils_get_wowlan (WifiData *data); gboolean wifi_utils_set_powersave (WifiData *data, guint32 powersave); - /* OLPC Mesh-only functions */ guint32 wifi_utils_get_mesh_channel (WifiData *data); diff --git a/src/ppp/meson.build b/src/ppp/meson.build new file mode 100644 index 00000000..38f9a0f1 --- /dev/null +++ b/src/ppp/meson.build @@ -0,0 +1,41 @@ +name = 'nm-pppd-plugin' + +deps = [ + dl_dep, + nm_core_dep +] + +nm_pppd_plugin = shared_module( + name, + name_prefix: '', + sources: name + '.c', + include_directories: src_inc, + dependencies: deps, + c_args: [ + '-DG_LOG_DOMAIN="@0@"'.format(name), + '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_GLIB', + ], + install: true, + install_dir: pppd_plugin_dir +) + +name = 'nm-ppp-plugin' + +deps = [ + nm_dep +] + +linker_script = join_paths(meson.current_source_dir(), 'nm-ppp-plugin.ver') + +core_plugins += shared_module( + name, + sources: 'nm-ppp-manager.c', + dependencies: deps, + c_args: '-DPPPD_PLUGIN_DIR="@0@"'.format(pppd_plugin_dir), + link_args: [ + '-Wl,--version-script,@0@'.format(linker_script), + ], + link_depends: linker_script, + install: true, + install_dir: nm_plugindir +) diff --git a/src/ppp/nm-ppp-manager-call.c b/src/ppp/nm-ppp-manager-call.c index ad3307a9..3d6fee49 100644 --- a/src/ppp/nm-ppp-manager-call.c +++ b/src/ppp/nm-ppp-manager-call.c @@ -44,11 +44,9 @@ nm_ppp_manager_create (const char *iface, GError **error) GError *error_local = NULL; NMPPPOps *ops; struct stat st; - int errsv; if (G_UNLIKELY (!ppp_ops)) { if (stat (PPP_PLUGIN_PATH, &st) != 0) { - errsv = errno; g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_MISSING_PLUGIN, "the PPP plugin " PPP_PLUGIN_PATH " is not installed"); @@ -84,9 +82,8 @@ nm_ppp_manager_create (const char *iface, GError **error) nm_assert (ops); nm_assert (ops->create); nm_assert (ops->start); - nm_assert (ops->stop_async); - nm_assert (ops->stop_finish); - nm_assert (ops->stop_sync); + nm_assert (ops->stop); + nm_assert (ops->stop_cancel); ppp_ops = ops; @@ -127,32 +124,21 @@ nm_ppp_manager_start (NMPPPManager *self, return ppp_ops->start (self, req, ppp_name, timeout_secs, baud_override, err); } -void -nm_ppp_manager_stop_async (NMPPPManager *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +NMPPPManagerStopHandle * +nm_ppp_manager_stop (NMPPPManager *self, + NMPPPManagerStopCallback callback, + gpointer user_data) { - g_return_if_fail (ppp_ops); + g_return_val_if_fail (ppp_ops, NULL); - ppp_ops->stop_async (self, cancellable, callback, user_data); -} - -gboolean -nm_ppp_manager_stop_finish (NMPPPManager *self, - GAsyncResult *res, - GError **error) -{ - g_return_val_if_fail (ppp_ops, FALSE); - - return ppp_ops->stop_finish (self, res, error); + return ppp_ops->stop (self, callback, user_data); } void -nm_ppp_manager_stop_sync (NMPPPManager *self) +nm_ppp_manager_stop_cancel (NMPPPManagerStopHandle *handle) { g_return_if_fail (ppp_ops); + g_return_if_fail (handle); - ppp_ops->stop_sync (self); + ppp_ops->stop_cancel (handle); } - diff --git a/src/ppp/nm-ppp-manager-call.h b/src/ppp/nm-ppp-manager-call.h index 2258ae08..daf8a82e 100644 --- a/src/ppp/nm-ppp-manager-call.h +++ b/src/ppp/nm-ppp-manager-call.h @@ -38,13 +38,11 @@ gboolean nm_ppp_manager_start (NMPPPManager *self, guint32 timeout_secs, guint baud_override, GError **error); -void nm_ppp_manager_stop_async (NMPPPManager *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean nm_ppp_manager_stop_finish (NMPPPManager *self, - GAsyncResult *res, - GError **error); -void nm_ppp_manager_stop_sync (NMPPPManager *self); + +NMPPPManagerStopHandle *nm_ppp_manager_stop (NMPPPManager *self, + NMPPPManagerStopCallback callback, + gpointer user_data); + +void nm_ppp_manager_stop_cancel (NMPPPManagerStopHandle *handle); #endif /* __NM_PPP_MANAGER_CALL_H__ */ diff --git a/src/ppp/nm-ppp-manager.c b/src/ppp/nm-ppp-manager.c index 743f80a2..fc658bec 100644 --- a/src/ppp/nm-ppp-manager.c +++ b/src/ppp/nm-ppp-manager.c @@ -50,13 +50,12 @@ #include "nm-act-request.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" +#include "nm-dbus-object.h" #include "nm-pppd-plugin.h" #include "nm-ppp-plugin-api.h" #include "nm-ppp-status.h" -#include "introspection/org.freedesktop.NetworkManager.PPP.h" - #define NM_PPPD_PLUGIN PPPD_PLUGIN_DIR "/nm-pppd-plugin.so" static NM_CACHED_QUARK_FCN ("ppp-manager-secret-tries", ppp_manager_secret_tries_quark) @@ -76,6 +75,7 @@ GType nm_ppp_manager_get_type (void); enum { STATE_CHANGED, + IFINDEX_SET, IP4_CONFIG, IP6_CONFIG, STATS, @@ -93,6 +93,8 @@ typedef struct { GPid pid; char *parent_iface; + char *ip_iface; + int ifindex; NMActRequest *act_req; GDBusMethodInvocation *pending_secrets_context; @@ -103,7 +105,6 @@ typedef struct { guint ppp_timeout_handler; /* Monitoring */ - char *ip_iface; int monitor_fd; guint monitor_id; @@ -114,17 +115,17 @@ typedef struct { } NMPPPManagerPrivate; struct _NMPPPManager { - NMExportedObject parent; + NMDBusObject parent; NMPPPManagerPrivate _priv; }; typedef struct { - NMExportedObjectClass parent; + NMDBusObjectClass parent; } NMPPPManagerClass; -G_DEFINE_TYPE (NMPPPManager, nm_ppp_manager, NM_TYPE_EXPORTED_OBJECT) +G_DEFINE_TYPE (NMPPPManager, nm_ppp_manager, NM_TYPE_DBUS_OBJECT) -#define NM_PPP_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMPPPManager, NM_IS_PPP_MANAGER) +#define NM_PPP_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMPPPManager, NM_IS_PPP_MANAGER, NMDBusObject) /*****************************************************************************/ @@ -133,8 +134,11 @@ G_DEFINE_TYPE (NMPPPManager, nm_ppp_manager, NM_TYPE_EXPORTED_OBJECT) /*****************************************************************************/ -static void _ppp_cleanup (NMPPPManager *manager); -static void _ppp_kill (NMPPPManager *manager); +static void _ppp_cleanup (NMPPPManager *self); + +static NMPPPManagerStopHandle *_ppp_manager_stop (NMPPPManager *self, + NMPPPManagerStopCallback callback, + gpointer user_data); /*****************************************************************************/ @@ -172,32 +176,36 @@ _ppp_manager_set_route_parameters (NMPPPManager *self, static gboolean monitor_cb (gpointer user_data) { - NMPPPManager *manager = NM_PPP_MANAGER (user_data); - NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); - struct ifreq req; - struct ppp_stats stats; - - memset (&req, 0, sizeof (req)); - memset (&stats, 0, sizeof (stats)); - req.ifr_data = (caddr_t) &stats; - - strncpy (req.ifr_name, priv->ip_iface, sizeof (req.ifr_name)); - if (ioctl (priv->monitor_fd, SIOCGPPPSTATS, &req) < 0) { - if (errno != ENODEV) - _LOGW ("could not read ppp stats: %s", strerror (errno)); - } else { - g_signal_emit (manager, signals[STATS], 0, - (guint) stats.p.ppp_ibytes, - (guint) stats.p.ppp_obytes); + NMPPPManager *self = NM_PPP_MANAGER (user_data); + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self); + const char *ifname; + + ifname = nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex); + + if (ifname) { + struct ppp_stats stats = { }; + struct ifreq req = { + .ifr_data = (caddr_t) &stats, + }; + + nm_utils_ifname_cpy (req.ifr_name, ifname); + if (ioctl (priv->monitor_fd, SIOCGPPPSTATS, &req) < 0) { + if (errno != ENODEV) + _LOGW ("could not read ppp stats: %s", strerror (errno)); + } else { + g_signal_emit (self, signals[STATS], 0, + (guint) stats.p.ppp_ibytes, + (guint) stats.p.ppp_obytes); + } } - return TRUE; + return G_SOURCE_CONTINUE; } static void -monitor_stats (NMPPPManager *manager) +monitor_stats (NMPPPManager *self) { - NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self); /* already monitoring */ if (priv->monitor_fd >= 0) @@ -208,7 +216,7 @@ monitor_stats (NMPPPManager *manager) g_warn_if_fail (priv->monitor_id == 0); if (priv->monitor_id) g_source_remove (priv->monitor_id); - priv->monitor_id = g_timeout_add_seconds (5, monitor_cb, manager); + priv->monitor_id = g_timeout_add_seconds (5, monitor_cb, self); } else _LOGW ("could not monitor PPP stats: %s", strerror (errno)); } @@ -326,21 +334,28 @@ ppp_secrets_cb (NMActRequest *req, * against libnm just to parse this. So instead, let's just send what * it needs. */ - g_dbus_method_invocation_return_value ( - priv->pending_secrets_context, - g_variant_new ("(ss)", username ? username : "", password ? password : "")); + g_dbus_method_invocation_return_value (priv->pending_secrets_context, + g_variant_new ("(ss)", + username ?: "", + password ?: "")); - out: +out: priv->pending_secrets_context = NULL; priv->secrets_id = NULL; priv->secrets_setting_name = NULL; } static void -impl_ppp_manager_need_secrets (NMPPPManager *manager, - GDBusMethodInvocation *context) +impl_ppp_manager_need_secrets (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { - NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + NMPPPManager *self = NM_PPP_MANAGER (obj); + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self); NMConnection *applied_connection; const char *username = NULL; const char *password = NULL; @@ -358,8 +373,8 @@ impl_ppp_manager_need_secrets (NMPPPManager *manager, /* Use existing secrets from the connection */ if (extract_details_from_connection (applied_connection, NULL, &username, &password, &error)) { /* Send existing secrets to the PPP plugin */ - priv->pending_secrets_context = context; - ppp_secrets_cb (priv->act_req, priv->secrets_id, NULL, NULL, manager); + priv->pending_secrets_context = invocation; + ppp_secrets_cb (priv->act_req, priv->secrets_id, NULL, NULL, self); } else { _LOGW ("%s", error->message); g_dbus_method_invocation_take_error (priv->pending_secrets_context, error); @@ -381,41 +396,87 @@ impl_ppp_manager_need_secrets (NMPPPManager *manager, flags, hints ? g_ptr_array_index (hints, 0) : NULL, ppp_secrets_cb, - manager); + self); g_object_set_qdata (G_OBJECT (applied_connection), ppp_manager_secret_tries_quark (), GUINT_TO_POINTER (++tries)); - priv->pending_secrets_context = context; + priv->pending_secrets_context = invocation; if (hints) g_ptr_array_free (hints, TRUE); } static void -impl_ppp_manager_set_state (NMPPPManager *manager, - GDBusMethodInvocation *context, - guint32 state) +impl_ppp_manager_set_state (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { - g_signal_emit (manager, signals[STATE_CHANGED], 0, (guint) state); + NMPPPManager *self = NM_PPP_MANAGER (obj); + guint32 state; - g_dbus_method_invocation_return_value (context, NULL); + g_variant_get (parameters, "(u)", &state); + g_signal_emit (self, signals[STATE_CHANGED], 0, (guint) state); + g_dbus_method_invocation_return_value (invocation, NULL); +} + +static void +impl_ppp_manager_set_ifindex (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMPPPManager *self = NM_PPP_MANAGER (obj); + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self); + const NMPlatformLink *plink = NULL; + nm_auto_nmpobj const NMPObject *obj_keep_alive = NULL; + gint32 ifindex; + + g_variant_get (parameters, "(i)", &ifindex); + + _LOGD ("set-ifindex %d", (int) ifindex); + + if (priv->ifindex >= 0) { + _LOGW ("can't change the ifindex from %d to %d", priv->ifindex, (int) ifindex); + return; + } + + if (ifindex > 0) { + plink = nm_platform_link_get (NM_PLATFORM_GET, ifindex); + if (!plink) { + nm_platform_process_events (NM_PLATFORM_GET); + plink = nm_platform_link_get (NM_PLATFORM_GET, ifindex); + } + } + + if (!plink) { + _LOGW ("unknown interface with ifindex %d", ifindex); + ifindex = 0; + } + + priv->ifindex = ifindex; + + obj_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (plink)); + + g_signal_emit (self, signals[IFINDEX_SET], 0, ifindex, plink->name); + g_dbus_method_invocation_return_value (invocation, NULL); } static gboolean set_ip_config_common (NMPPPManager *self, GVariant *config_dict, - const char *iface_prop, guint32 *out_mtu) { NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self); NMConnection *applied_connection; NMSettingPpp *s_ppp; - const char *iface; - if (!g_variant_lookup (config_dict, iface_prop, "&s", &iface)) { - _LOGE ("no interface received!"); + if (priv->ifindex <= 0) return FALSE; - } - if (priv->ip_iface == NULL) - priv->ip_iface = g_strdup (iface); /* Got successful IP config; obviously the secrets worked */ applied_connection = nm_act_request_get_applied_connection (priv->act_req); @@ -432,29 +493,32 @@ set_ip_config_common (NMPPPManager *self, } static void -impl_ppp_manager_set_ip4_config (NMPPPManager *manager, - GDBusMethodInvocation *context, - GVariant *config_dict) +impl_ppp_manager_set_ip4_config (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { - NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + 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; - int ifindex; + gs_unref_variant GVariant *config_dict = NULL; _LOGI ("(IPv4 Config Get) reply received."); - nm_clear_g_source (&priv->ppp_timeout_handler); + g_variant_get (parameters, "(@a{sv})", &config_dict); - if (!set_ip_config_common (manager, config_dict, NM_PPP_IP4_CONFIG_INTERFACE, &mtu)) - goto out; + nm_clear_g_source (&priv->ppp_timeout_handler); - ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface); - if (ifindex <= 0) + if (!set_ip_config_common (self, config_dict, &mtu)) goto out; - config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), ifindex); + config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), priv->ifindex); if (mtu) nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_PPP); @@ -467,7 +531,7 @@ impl_ppp_manager_set_ip4_config (NMPPPManager *manager, if (g_variant_lookup (config_dict, NM_PPP_IP4_CONFIG_GATEWAY, "u", &u32)) { const NMPlatformIP4Route r = { - .ifindex = ifindex, + .ifindex = priv->ifindex, .rt_source = NM_IP_CONFIG_SOURCE_PPP, .gateway = u32, .table_coerced = nm_platform_route_table_coerce (priv->ip4_route_table), @@ -503,10 +567,10 @@ impl_ppp_manager_set_ip4_config (NMPPPManager *manager, } /* Push the IP4 config up to the device */ - g_signal_emit (manager, signals[IP4_CONFIG], 0, priv->ip_iface, config); + g_signal_emit (self, signals[IP4_CONFIG], 0, config); out: - g_dbus_method_invocation_return_value (context, NULL); + g_dbus_method_invocation_return_value (invocation, NULL); } /* Converts the named Interface Identifier item to an IPv6 LL address and @@ -539,37 +603,40 @@ iid_value_to_ll6_addr (GVariant *dict, } static void -impl_ppp_manager_set_ip6_config (NMPPPManager *manager, - GDBusMethodInvocation *context, - GVariant *config_dict) +impl_ppp_manager_set_ip6_config (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { - NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + 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; - int ifindex; + gs_unref_variant GVariant *config_dict = NULL; _LOGI ("(IPv6 Config Get) reply received."); - nm_clear_g_source (&priv->ppp_timeout_handler); + g_variant_get (parameters, "(@a{sv})", &config_dict); - if (!set_ip_config_common (manager, config_dict, NM_PPP_IP6_CONFIG_INTERFACE, NULL)) - goto out; + nm_clear_g_source (&priv->ppp_timeout_handler); - ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface); - if (ifindex <= 0) + if (!set_ip_config_common (self, config_dict, NULL)) goto out; - config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), ifindex); + config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), priv->ifindex); memset (&addr, 0, sizeof (addr)); addr.plen = 64; if (iid_value_to_ll6_addr (config_dict, NM_PPP_IP6_CONFIG_PEER_IID, &a, NULL)) { const NMPlatformIP6Route r = { - .ifindex = ifindex, + .ifindex = priv->ifindex, .rt_source = NM_IP_CONFIG_SOURCE_PPP, .gateway = a, .table_coerced = nm_platform_route_table_coerce (priv->ip6_route_table), @@ -587,12 +654,12 @@ impl_ppp_manager_set_ip6_config (NMPPPManager *manager, nm_ip6_config_add_address (config, &addr); /* Push the IPv6 config and interface identifier up to the device */ - g_signal_emit (manager, signals[IP6_CONFIG], 0, priv->ip_iface, &iid, config); + g_signal_emit (self, signals[IP6_CONFIG], 0, &iid, config); } else _LOGE ("invalid IPv6 address received!"); out: - g_dbus_method_invocation_return_value (context, NULL); + g_dbus_method_invocation_return_value (invocation, NULL); } /*****************************************************************************/ @@ -680,8 +747,8 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (pppd_exit_code_to_str, int, static void ppp_watch_cb (GPid pid, int status, gpointer user_data) { - NMPPPManager *manager = NM_PPP_MANAGER (user_data); - NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + NMPPPManager *self = NM_PPP_MANAGER (user_data); + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self); int err; const long long lpid = (long long) pid; @@ -706,20 +773,19 @@ ppp_watch_cb (GPid pid, int status, gpointer user_data) priv->pid = 0; priv->ppp_watch_id = 0; - _ppp_cleanup (manager); - g_signal_emit (manager, signals[STATE_CHANGED], 0, (guint) NM_PPP_STATUS_DEAD); + _ppp_cleanup (self); + g_signal_emit (self, signals[STATE_CHANGED], 0, (guint) NM_PPP_STATUS_DEAD); } static gboolean pppd_timed_out (gpointer data) { - NMPPPManager *manager = NM_PPP_MANAGER (data); + NMPPPManager *self = NM_PPP_MANAGER (data); _LOGW ("pppd timed out or didn't initialize our dbus module"); - _ppp_cleanup (manager); - _ppp_kill (manager); + _ppp_manager_stop (self, NULL, NULL); - g_signal_emit (manager, signals[STATE_CHANGED], 0, (guint) NM_PPP_STATUS_DEAD); + g_signal_emit (self, signals[STATE_CHANGED], 0, (guint) NM_PPP_STATUS_DEAD); return FALSE; } @@ -899,7 +965,7 @@ create_pppd_cmd_line (NMPPPManager *self, nm_cmd_line_add_int (cmd, 0); nm_cmd_line_add_string (cmd, "ipparam"); - nm_cmd_line_add_string (cmd, nm_exported_object_get_path (NM_EXPORTED_OBJECT (self))); + nm_cmd_line_add_string (cmd, nm_dbus_object_get_path (NM_DBUS_OBJECT (self))); nm_cmd_line_add_string (cmd, "plugin"); nm_cmd_line_add_string (cmd, NM_PPPD_PLUGIN); @@ -944,7 +1010,7 @@ pppoe_fill_defaults (NMSettingPpp *setting) } static gboolean -_ppp_manager_start (NMPPPManager *manager, +_ppp_manager_start (NMPPPManager *self, NMActRequest *req, const char *ppp_name, guint32 timeout_secs, @@ -964,10 +1030,10 @@ _ppp_manager_start (NMPPPManager *manager, gboolean ip6_enabled = FALSE; gboolean ip4_enabled = FALSE; - g_return_val_if_fail (NM_IS_PPP_MANAGER (manager), FALSE); + g_return_val_if_fail (NM_IS_PPP_MANAGER (self), FALSE); g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); - priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + priv = NM_PPP_MANAGER_GET_PRIVATE (self); #if !WITH_PPP /* PPP support disabled */ @@ -978,7 +1044,7 @@ _ppp_manager_start (NMPPPManager *manager, return FALSE; #endif - nm_exported_object_export (NM_EXPORTED_OBJECT (manager)); + nm_dbus_object_export (NM_DBUS_OBJECT (self)); priv->pid = 0; @@ -1013,7 +1079,7 @@ _ppp_manager_start (NMPPPManager *manager, ip6_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); ip6_enabled = g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0; - ppp_cmd = create_pppd_cmd_line (manager, + ppp_cmd = create_pppd_cmd_line (self, s_ppp, pppoe_setting, adsl_setting, @@ -1043,8 +1109,8 @@ _ppp_manager_start (NMPPPManager *manager, _LOGI ("pppd started with pid %lld", (long long) priv->pid); - priv->ppp_watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) ppp_watch_cb, manager); - priv->ppp_timeout_handler = g_timeout_add_seconds (timeout_secs, pppd_timed_out, manager); + 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); out: @@ -1052,42 +1118,27 @@ out: nm_cmd_line_destroy (ppp_cmd); if (priv->pid <= 0) - nm_exported_object_unexport (NM_EXPORTED_OBJECT (manager)); + nm_dbus_object_unexport (NM_DBUS_OBJECT (self)); return priv->pid > 0; } static void -_ppp_kill (NMPPPManager *manager) -{ - NMPPPManagerPrivate *priv; - - g_return_if_fail (NM_IS_PPP_MANAGER (manager)); - - priv = NM_PPP_MANAGER_GET_PRIVATE (manager); - - if (priv->pid) { - nm_utils_kill_child_async (priv->pid, SIGTERM, LOGD_PPP, "pppd", 2000, NULL, NULL); - priv->pid = 0; - } -} - -static void -_ppp_cleanup (NMPPPManager *manager) +_ppp_cleanup (NMPPPManager *self) { NMPPPManagerPrivate *priv; - g_return_if_fail (NM_IS_PPP_MANAGER (manager)); + g_return_if_fail (NM_IS_PPP_MANAGER (self)); - priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + priv = NM_PPP_MANAGER_GET_PRIVATE (self); - cancel_get_secrets (manager); + cancel_get_secrets (self); nm_clear_g_source (&priv->monitor_id); if (priv->monitor_fd >= 0) { /* Get the stats one last time */ - monitor_cb (manager); + monitor_cb (self); nm_close (priv->monitor_fd); priv->monitor_fd = -1; } @@ -1098,108 +1149,133 @@ _ppp_cleanup (NMPPPManager *manager) /*****************************************************************************/ -typedef struct { - NMPPPManager *manager; - GSimpleAsyncResult *result; - GCancellable *cancellable; -} StopContext; +struct _NMPPPManagerStopHandle { + NMPPPManager *self; + NMPPPManagerStopCallback callback; + gpointer user_data; + + /* this object delays shutdown, because we still need to wait until + * pppd process terminated. */ + GObject *shutdown_waitobj; + + guint idle_id; +}; static void -stop_context_complete (StopContext *ctx) +_stop_handle_complete (NMPPPManagerStopHandle *handle, gboolean was_cancelled) { - if (ctx->cancellable) - g_object_unref (ctx->cancellable); - g_simple_async_result_complete_in_idle (ctx->result); - g_object_unref (ctx->result); - g_object_unref (ctx->manager); - g_slice_free (StopContext, ctx); -} + gs_unref_object NMPPPManager *self = NULL; + NMPPPManagerStopCallback callback; -static gboolean -stop_context_complete_if_cancelled (StopContext *ctx) -{ - GError *error = NULL; + self = g_steal_pointer (&handle->self); + if (!self) + return; - if (g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) { - g_simple_async_result_take_error (ctx->result, error); - stop_context_complete (ctx); - return TRUE; - } - return FALSE; + if (!handle->callback) + return; + + callback = handle->callback; + handle->callback = NULL; + callback (self, handle, was_cancelled, handle->user_data); } -static gboolean -_ppp_manager_stop_finish (NMPPPManager *manager, - GAsyncResult *res, - GError **error) +static void +_stop_handle_destroy (NMPPPManagerStopHandle *handle, gboolean was_cancelled) { - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); + _stop_handle_complete (handle, was_cancelled); + nm_clear_g_source (&handle->idle_id); + g_clear_object (&handle->shutdown_waitobj); + g_slice_free (NMPPPManagerStopHandle, handle); } static void -kill_child_ready (pid_t pid, - gboolean success, - int child_status, - StopContext *ctx) +_stop_child_cb (pid_t pid, + gboolean success, + int child_status, + gpointer user_data) { - if (stop_context_complete_if_cancelled (ctx)) - return; - stop_context_complete (ctx); + _stop_handle_destroy (user_data, FALSE); } -static void -_ppp_manager_stop_async (NMPPPManager *manager, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +static gboolean +_stop_idle_cb (gpointer user_data) { - NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); - StopContext *ctx; + NMPPPManagerStopHandle *handle = user_data; - nm_exported_object_unexport (NM_EXPORTED_OBJECT (manager)); + handle->idle_id = 0; + _stop_handle_destroy (handle, FALSE); + return G_SOURCE_REMOVE; +} - ctx = g_slice_new0 (StopContext); - ctx->manager = g_object_ref (manager); - ctx->result = g_simple_async_result_new (G_OBJECT (manager), - callback, - user_data, - _ppp_manager_stop_async); +static NMPPPManagerStopHandle * +_ppp_manager_stop (NMPPPManager *self, + NMPPPManagerStopCallback callback, + gpointer user_data) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self); + NMDBusObject *dbus = NM_DBUS_OBJECT (self); + NMPPPManagerStopHandle *handle; - /* Setup cancellable */ - ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; - if (stop_context_complete_if_cancelled (ctx)) - return; + if (nm_dbus_object_is_exported (dbus)) + nm_dbus_object_unexport (dbus); + + _ppp_cleanup (self); + + if ( !priv->pid + && !callback) { + /* nothing to do further... + * + * In this case, we return a %NULL handle. The caller cannot cancel this + * event, but clearly he is not waiting for a callback anyway. */ + return NULL; + } - /* Cleanup internals */ - _ppp_cleanup (manager); + handle = g_slice_new0 (NMPPPManagerStopHandle); + handle->self = g_object_ref (self); + handle->callback = callback; + handle->user_data = user_data; - /* If no pppd running, we're done */ if (!priv->pid) { - stop_context_complete (ctx); - return; + /* No PID. There is nothing to kill, however, invoke the callback in + * an idle handler. + * + * Note that we don't register nm_shutdown_wait_obj_register(). + * In order for shutdown to work properly, the caller must always + * explicitly cancel the action to go down. With the idle-handler, + * cancelling the handle completes the request. */ + handle->idle_id = g_idle_add (_stop_idle_cb, handle); + return handle; } - /* No cancellable operation, so just wait until it returns always */ - nm_utils_kill_child_async (priv->pid, - SIGTERM, - LOGD_PPP, - "pppd", - 2000, - (NMUtilsKillChildAsyncCb) kill_child_ready, - ctx); - priv->pid = 0; + /* we really want to kill the process and delay shutdown of NetworkManager + * until the process terminated. We do that, by registering an object + * that delays shutdown. */ + handle->shutdown_waitobj = g_object_new (G_TYPE_OBJECT, NULL); + nm_shutdown_wait_obj_register (handle->shutdown_waitobj, "ppp-manager-wait-kill-pppd"); + nm_utils_kill_child_async (nm_steal_int (&priv->pid), + SIGTERM, LOGD_PPP, "pppd", + NM_SHUTDOWN_TIMEOUT_MS, + _stop_child_cb, handle); + + return handle; } static void -_ppp_manager_stop_sync (NMPPPManager *manager) +_ppp_manager_stop_cancel (NMPPPManagerStopHandle *handle) { - NMExportedObject *exported = NM_EXPORTED_OBJECT (manager); + g_return_if_fail (handle); + g_return_if_fail (NM_IS_PPP_MANAGER (handle->self)); - if (nm_exported_object_is_exported (exported)) - nm_exported_object_unexport (exported); + if (handle->idle_id) { + /* we can complete this fake handle right away. */ + _stop_handle_destroy (handle, TRUE); + return; + } - _ppp_cleanup (manager); - _ppp_kill (manager); + /* a real handle. Only invoke the callback (synchronously). This marks + * the handle as handled, but it keeps shutdown_waitobj around, until + * nm_utils_kill_child_async() returns. */ + _stop_handle_complete (handle, TRUE); } /*****************************************************************************/ @@ -1240,10 +1316,11 @@ set_property (GObject *object, guint prop_id, /*****************************************************************************/ static void -nm_ppp_manager_init (NMPPPManager *manager) +nm_ppp_manager_init (NMPPPManager *self) { - NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + 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; @@ -1265,14 +1342,13 @@ static void dispose (GObject *object) { NMPPPManager *self = (NMPPPManager *) object; - NMExportedObject *exported = NM_EXPORTED_OBJECT (self); NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self); - if (nm_exported_object_is_exported (exported)) - nm_exported_object_unexport (exported); - - _ppp_cleanup (self); - _ppp_kill (self); + /* we expect the user to first stop the manager. As fallback, + * still stop. */ + g_warn_if_fail (!priv->pid); + g_warn_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (self))); + _ppp_manager_stop (self, NULL, NULL); g_clear_object (&priv->act_req); @@ -1284,24 +1360,78 @@ finalize (GObject *object) { NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE ((NMPPPManager *) object); - g_free (priv->ip_iface); g_free (priv->parent_iface); G_OBJECT_CLASS (nm_ppp_manager_parent_class)->finalize (object); } +static const NMDBusInterfaceInfoExtended interface_info_ppp = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_PPP, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "NeedSecrets", + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("username", "s"), + NM_DEFINE_GDBUS_ARG_INFO ("password", "s"), + ), + ), + .handle = impl_ppp_manager_need_secrets, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "SetIp4Config", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("config", "a{sv}"), + ), + ), + .handle = impl_ppp_manager_set_ip4_config, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "SetIp6Config", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("config", "a{sv}"), + ), + ), + .handle = impl_ppp_manager_set_ip6_config, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "SetState", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("state", "u"), + ), + ), + .handle = impl_ppp_manager_set_state, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "SetIfindex", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("ifindex", "i"), + ), + ), + .handle = impl_ppp_manager_set_ifindex, + ), + ), + ), +}; + static void nm_ppp_manager_class_init (NMPPPManagerClass *manager_class) { GObjectClass *object_class = G_OBJECT_CLASS (manager_class); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (manager_class); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (manager_class); object_class->dispose = dispose; object_class->finalize = finalize; object_class->get_property = get_property; object_class->set_property = set_property; - exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/PPP"); + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/PPP"); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_ppp); obj_properties[PROP_PARENT_IFACE] = g_param_spec_string (NM_PPP_MANAGER_PARENT_IFACE, "", "", @@ -1320,14 +1450,23 @@ nm_ppp_manager_class_init (NMPPPManagerClass *manager_class) G_TYPE_NONE, 1, G_TYPE_UINT); + signals[IFINDEX_SET] = + g_signal_new (NM_PPP_MANAGER_SIGNAL_IFINDEX_SET, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 2, + G_TYPE_INT, + G_TYPE_STRING); + signals[IP4_CONFIG] = g_signal_new (NM_PPP_MANAGER_SIGNAL_IP4_CONFIG, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 2, - G_TYPE_STRING, + G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[IP6_CONFIG] = @@ -1336,7 +1475,9 @@ nm_ppp_manager_class_init (NMPPPManagerClass *manager_class) G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_OBJECT); + G_TYPE_NONE, 2, + G_TYPE_POINTER, + G_TYPE_OBJECT); signals[STATS] = g_signal_new (NM_PPP_MANAGER_SIGNAL_STATS, @@ -1347,21 +1488,12 @@ nm_ppp_manager_class_init (NMPPPManagerClass *manager_class) G_TYPE_NONE, 2, G_TYPE_UINT /*guint32 in_bytes*/, G_TYPE_UINT /*guint32 out_bytes*/); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (manager_class), - NMDBUS_TYPE_PPP_MANAGER_SKELETON, - "NeedSecrets", impl_ppp_manager_need_secrets, - "SetIp4Config", impl_ppp_manager_set_ip4_config, - "SetIp6Config", impl_ppp_manager_set_ip6_config, - "SetState", impl_ppp_manager_set_state, - NULL); } NMPPPOps ppp_ops = { .create = _ppp_manager_new, .set_route_parameters = _ppp_manager_set_route_parameters, .start = _ppp_manager_start, - .stop_async = _ppp_manager_stop_async, - .stop_finish = _ppp_manager_stop_finish, - .stop_sync = _ppp_manager_stop_sync, + .stop = _ppp_manager_stop, + .stop_cancel = _ppp_manager_stop_cancel, }; diff --git a/src/ppp/nm-ppp-manager.h b/src/ppp/nm-ppp-manager.h index 35fb1b60..ec0ca46e 100644 --- a/src/ppp/nm-ppp-manager.h +++ b/src/ppp/nm-ppp-manager.h @@ -25,10 +25,18 @@ #define NM_PPP_MANAGER_PARENT_IFACE "parent-iface" #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_STATS "stats" typedef struct _NMPPPManager NMPPPManager; +typedef struct _NMPPPManagerStopHandle NMPPPManagerStopHandle; + +typedef void (*NMPPPManagerStopCallback) (NMPPPManager *manager, + NMPPPManagerStopHandle *handle, + gboolean was_cancelled, + gpointer user_data); + #endif /* __NM_PPP_MANAGER_H__ */ diff --git a/src/ppp/nm-ppp-plugin-api.h b/src/ppp/nm-ppp-plugin-api.h index bb53690c..558de2c2 100644 --- a/src/ppp/nm-ppp-plugin-api.h +++ b/src/ppp/nm-ppp-plugin-api.h @@ -21,6 +21,8 @@ #ifndef __NM_PPP_PLUGIN_API_H__ #define __NM_PPP_PLUGIN_API_H__ +#include "nm-ppp-manager.h" + typedef const struct { NMPPPManager *(*create) (const char *iface); @@ -37,16 +39,12 @@ typedef const struct { guint baud_override, GError **err); - void (*stop_async) (NMPPPManager *manager, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); + NMPPPManagerStopHandle *(*stop) (NMPPPManager *manager, + NMPPPManagerStopCallback callback, + gpointer user_data); - gboolean (*stop_finish) (NMPPPManager *manager, - GAsyncResult *res, - GError **error); + void (*stop_cancel) (NMPPPManagerStopHandle *handle); - void (*stop_sync) (NMPPPManager *manager); } NMPPPOps; #endif /* __NM_PPP_PLUGIN_API_H__ */ diff --git a/src/ppp/nm-pppd-plugin.c b/src/ppp/nm-pppd-plugin.c index 0ac8f907..989f7433 100644 --- a/src/ppp/nm-pppd-plugin.c +++ b/src/ppp/nm-pppd-plugin.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -36,7 +37,9 @@ #include #include "nm-default.h" + #include "nm-dbus-interface.h" + #include "nm-pppd-plugin.h" #include "nm-ppp-status.h" @@ -50,7 +53,9 @@ static void nm_phasechange (void *data, int arg) { NMPPPStatus ppp_status = NM_PPP_STATUS_UNKNOWN; + char new_name[IF_NAMESIZE]; char *ppp_phase; + int index; g_return_if_fail (G_IS_DBUS_PROXY (proxy)); @@ -126,6 +131,25 @@ nm_phasechange (void *data, int arg) NULL, NULL, NULL); } + + if (ppp_status == PHASE_RUNNING) { + index = if_nametoindex (ifname); + /* Make a sync call to ensure that when the call + * terminates the interface already has its final + * name. */ + g_dbus_proxy_call_sync (proxy, + "SetIfindex", + g_variant_new ("(i)", index), + G_DBUS_CALL_FLAGS_NONE, + 25000, + NULL, NULL); + /* Update the name in pppd if NM changed it */ + if ( if_indextoname (index, new_name) + && !nm_streq0 (ifname, new_name)) { + g_message ("nm-ppp-plugin: interface name changed from '%s' to '%s'", ifname, new_name); + strncpy (ifname, new_name, IF_NAMESIZE); + } + } } static void @@ -148,6 +172,9 @@ nm_ip_up (void *data, int arg) g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + /* Keep sending the interface name to be backwards compatible + * with older versions of NM during a package upgrade, where + * NM is not restarted and the pppd plugin was not loaded. */ g_variant_builder_add (&builder, "{sv}", NM_PPP_IP4_CONFIG_INTERFACE, g_variant_new_string (ifname)); @@ -242,6 +269,9 @@ nm_ip6_up (void *data, int arg) g_message ("nm-ppp-plugin: (%s): ip6-up event", __func__); g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + /* Keep sending the interface name to be backwards compatible + * with older versions of NM during a package upgrade, where + * NM is not restarted and the pppd plugin was not loaded. */ g_variant_builder_add (&builder, "{sv}", NM_PPP_IP6_CONFIG_INTERFACE, g_variant_new_string (ifname)); @@ -361,8 +391,6 @@ plugin_init (void) GDBusConnection *bus; GError *err = NULL; - nm_g_type_init (); - g_message ("nm-ppp-plugin: (%s): initializing", __func__); bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &err); diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c index a68db47e..453136e4 100644 --- a/src/settings/nm-agent-manager.c +++ b/src/settings/nm-agent-manager.c @@ -31,14 +31,12 @@ #include "nm-auth-utils.h" #include "nm-setting-vpn.h" #include "nm-auth-manager.h" -#include "nm-bus-manager.h" +#include "nm-dbus-manager.h" #include "nm-session-monitor.h" #include "nm-simple-connection.h" #include "NetworkManagerUtils.h" #include "nm-core-internal.h" -#include "nm-utils/c-list.h" - -#include "introspection/org.freedesktop.NetworkManager.AgentManager.h" +#include "c-list/src/c-list.h" /*****************************************************************************/ @@ -67,15 +65,15 @@ typedef struct { } NMAgentManagerPrivate; struct _NMAgentManager { - NMExportedObject parent; + NMDBusObject parent; NMAgentManagerPrivate _priv; }; struct _NMAgentManagerClass { - NMExportedObjectClass parent; + NMDBusObjectClass parent; }; -G_DEFINE_TYPE (NMAgentManager, nm_agent_manager, NM_TYPE_EXPORTED_OBJECT) +G_DEFINE_TYPE (NMAgentManager, nm_agent_manager, NM_TYPE_DBUS_OBJECT) #define NM_AGENT_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMAgentManager, NM_IS_AGENT_MANAGER) @@ -362,7 +360,7 @@ agent_register_permissions_done (NMAuthChain *chain, request_add_agent (c_list_entry (iter, Request, lst_request), agent); } - nm_auth_chain_unref (chain); + nm_auth_chain_destroy (chain); } static NMSecretAgent * @@ -392,10 +390,10 @@ agent_disconnected_cb (NMSecretAgent *agent, gpointer user_data) } static void -impl_agent_manager_register_with_capabilities (NMAgentManager *self, - GDBusMethodInvocation *context, - const char *identifier, - guint32 capabilities) +agent_manager_register_with_capabilities (NMAgentManager *self, + GDBusMethodInvocation *context, + const char *identifier, + guint32 capabilities) { NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); NMAuthSubject *subject; @@ -460,45 +458,56 @@ done: } static void -impl_agent_manager_register (NMAgentManager *self, - GDBusMethodInvocation *context, - const char *identifier) +impl_agent_manager_register (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { - impl_agent_manager_register_with_capabilities (self, context, identifier, 0); + const char *identifier; + + g_variant_get (parameters, "(&s)", &identifier); + agent_manager_register_with_capabilities (NM_AGENT_MANAGER (obj), invocation, identifier, 0); } static void -impl_agent_manager_unregister (NMAgentManager *self, - GDBusMethodInvocation *context) +impl_agent_manager_register_with_capabilities (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { - GError *error = NULL; - char *sender = NULL; + const char *identifier; + guint32 capabilities; - if (!nm_bus_manager_get_caller_info (nm_bus_manager_get (), - context, - &sender, - NULL, - NULL)) { - error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_PERMISSION_DENIED, - "Unable to determine request sender."); - goto done; - } + g_variant_get (parameters, "(&su)", &identifier, &capabilities); + agent_manager_register_with_capabilities (NM_AGENT_MANAGER (obj), invocation, identifier, capabilities); +} + +static void +impl_agent_manager_unregister (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMAgentManager *self = NM_AGENT_MANAGER (obj); - /* Found the agent, unregister and remove it */ if (!remove_agent (self, sender)) { - error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_NOT_REGISTERED, - "Caller is not registered as an Agent"); - goto done; + g_dbus_method_invocation_return_error_literal (invocation, + NM_AGENT_MANAGER_ERROR, + NM_AGENT_MANAGER_ERROR_NOT_REGISTERED, + "Caller is not registered as an Agent"); + return; } - g_dbus_method_invocation_return_value (context, NULL); - -done: - if (error) - g_dbus_method_invocation_take_error (context, error); - g_free (sender); + g_dbus_method_invocation_return_value (invocation, NULL); } /*****************************************************************************/ @@ -530,7 +539,7 @@ request_free (Request *req) g_object_unref (req->con.connection); g_free (req->con.path); if (req->con.chain) - nm_auth_chain_unref (req->con.chain); + nm_auth_chain_destroy (req->con.chain); if (req->request_type == REQUEST_TYPE_CON_GET) { g_free (req->con.get.setting_name); g_strfreev (req->con.get.hints); @@ -801,7 +810,7 @@ request_remove_agent (Request *req, NMSecretAgent *agent) case REQUEST_TYPE_CON_DEL: if (req->con.chain) { /* This cancels the pending authorization requests. */ - nm_auth_chain_unref (req->con.chain); + nm_auth_chain_destroy (req->con.chain); req->con.chain = NULL; } break; @@ -1038,7 +1047,7 @@ _con_get_request_start_validated (NMAuthChain *chain, _con_get_request_start_proceed (req, req->con.current_has_modify); } - nm_auth_chain_unref (chain); + nm_auth_chain_destroy (chain); } static void @@ -1532,7 +1541,7 @@ agent_permissions_changed_done (NMAuthChain *chain, nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, share_protected); nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, share_open); - nm_auth_chain_unref (chain); + nm_auth_chain_destroy (chain); } static void @@ -1586,7 +1595,7 @@ constructed (GObject *object) priv->auth_mgr = g_object_ref (nm_auth_manager_get ()); priv->session_monitor = g_object_ref (nm_session_monitor_get ()); - nm_exported_object_export (NM_EXPORTED_OBJECT (object)); + nm_dbus_object_export (NM_DBUS_OBJECT (object)); g_signal_connect (priv->auth_mgr, NM_AUTH_MANAGER_SIGNAL_CHANGED, @@ -1607,7 +1616,7 @@ cancel_more: goto cancel_more; } - g_slist_free_full (priv->chains, (GDestroyNotify) nm_auth_chain_unref); + g_slist_free_full (priv->chains, (GDestroyNotify) nm_auth_chain_destroy); priv->chains = NULL; if (priv->agents) { @@ -1622,20 +1631,64 @@ cancel_more: g_clear_object (&priv->auth_mgr); } - nm_exported_object_unexport (NM_EXPORTED_OBJECT (object)); + nm_dbus_object_unexport (NM_DBUS_OBJECT (object)); g_clear_object (&priv->session_monitor); G_OBJECT_CLASS (nm_agent_manager_parent_class)->dispose (object); } +static const NMDBusInterfaceInfoExtended interface_info_agent_manager = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_AGENT_MANAGER, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "Register", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("identifier", "s"), + ), + ), + .handle = impl_agent_manager_register, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "RegisterWithCapabilities", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("identifier", "s"), + NM_DEFINE_GDBUS_ARG_INFO ("capabilities", "u"), + ), + ), + .handle = impl_agent_manager_register_with_capabilities, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "RegisterWithCapabilities", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("identifier", "s"), + NM_DEFINE_GDBUS_ARG_INFO ("capabilities", "u"), + ), + ), + .handle = impl_agent_manager_register_with_capabilities, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "Unregister", + ), + .handle = impl_agent_manager_unregister, + ), + ), + ), +}; + static void nm_agent_manager_class_init (NMAgentManagerClass *agent_manager_class) { GObjectClass *object_class = G_OBJECT_CLASS (agent_manager_class); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (agent_manager_class); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (agent_manager_class); - exported_object_class->export_path = NM_DBUS_PATH_AGENT_MANAGER; + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_STATIC (NM_DBUS_PATH_AGENT_MANAGER); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_agent_manager); object_class->constructed = constructed; object_class->dispose = dispose; @@ -1649,11 +1702,4 @@ nm_agent_manager_class_init (NMAgentManagerClass *agent_manager_class) g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (agent_manager_class), - NMDBUS_TYPE_AGENT_MANAGER_SKELETON, - "Register", impl_agent_manager_register, - "RegisterWithCapabilities", impl_agent_manager_register_with_capabilities, - "Unregister", impl_agent_manager_unregister, - NULL); } diff --git a/src/settings/nm-agent-manager.h b/src/settings/nm-agent-manager.h index f6845818..8c27f26b 100644 --- a/src/settings/nm-agent-manager.h +++ b/src/settings/nm-agent-manager.h @@ -23,7 +23,7 @@ #include "nm-connection.h" -#include "nm-exported-object.h" +#include "nm-dbus-object.h" #include "nm-secret-agent.h" #define NM_TYPE_AGENT_MANAGER (nm_agent_manager_get_type ()) diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c index 192e9877..af6d7017 100644 --- a/src/settings/nm-secret-agent.c +++ b/src/settings/nm-secret-agent.c @@ -26,13 +26,12 @@ #include #include "nm-dbus-interface.h" -#include "nm-bus-manager.h" +#include "nm-dbus-manager.h" +#include "nm-core-internal.h" #include "nm-auth-subject.h" #include "nm-simple-connection.h" #include "NetworkManagerUtils.h" -#include "nm-utils/c-list.h" - -#include "introspection/org.freedesktop.NetworkManager.SecretAgent.h" +#include "c-list/src/c-list.h" /*****************************************************************************/ @@ -51,8 +50,8 @@ typedef struct { char *dbus_owner; NMSecretAgentCapabilities capabilities; GSList *permissions; - NMDBusSecretAgent *proxy; - NMBusManager *bus_mgr; + GDBusProxy *proxy; + NMDBusManager *bus_mgr; GDBusConnection *connection; CList requests; gulong on_disconnected_id; @@ -300,7 +299,7 @@ nm_secret_agent_add_permission (NMSecretAgent *agent, * @permission: The name of the permission to check for * * Returns whether or not the agent has the given permission. - * + * * Returns: %TRUE if the agent has the given permission, %FALSE if it does not * or if the permission was not previous recorded with * nm_secret_agent_add_permission(). @@ -336,11 +335,17 @@ get_callback (GObject *proxy, if (request_check_return (r)) { NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (r->agent); gs_free_error GError *error = NULL; + gs_unref_variant GVariant *ret = NULL; gs_unref_variant GVariant *secrets = NULL; - nmdbus_secret_agent_call_get_secrets_finish (priv->proxy, &secrets, result, &error); - if (error) + ret = _nm_dbus_proxy_call_finish (priv->proxy, result, G_VARIANT_TYPE ("(a{sa{sv}})"), &error); + if (!ret) g_dbus_error_strip_remote_error (error); + else { + g_variant_get (ret, + "(@a{sa{sv}})", + &secrets); + } r->callback (r->agent, r, secrets, error, r->callback_data); } @@ -358,7 +363,6 @@ nm_secret_agent_get_secrets (NMSecretAgent *self, gpointer callback_data) { NMSecretAgentPrivate *priv; - static const char *no_hints[] = { NULL }; GVariant *dict; NMSecretAgentCallId *r; @@ -379,16 +383,20 @@ nm_secret_agent_get_secrets (NMSecretAgent *self, r = request_new (self, "GetSecrets", path, setting_name, callback, callback_data); r->is_get_secrets = TRUE; - /* Increase the timeout only for this call */ - g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (priv->proxy), 120000); - nmdbus_secret_agent_call_get_secrets (priv->proxy, - dict, - path, - setting_name, - hints ? hints : no_hints, - flags, - r->cancellable, - get_callback, r); + g_dbus_proxy_call (priv->proxy, + "GetSecrets", + g_variant_new ("(@a{sa{sv}}os^asu)", + dict, + path, + setting_name, + hints ?: NM_PTRARRAY_EMPTY (const char *), + (guint32) flags), + G_DBUS_CALL_FLAGS_NONE, + 120000, + r->cancellable, + get_callback, + r); + g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (priv->proxy), -1); return r; @@ -399,17 +407,16 @@ nm_secret_agent_get_secrets (NMSecretAgent *self, static void cancel_done (GObject *proxy, GAsyncResult *result, gpointer user_data) { - char *description = user_data; - GError *error = NULL; + gs_free char *description = user_data; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *ret = NULL; - if (!nmdbus_secret_agent_call_cancel_get_secrets_finish (NMDBUS_SECRET_AGENT (proxy), result, &error)) { + ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, G_VARIANT_TYPE ("()"), &error); + if (!ret) { nm_log_dbg (LOGD_AGENTS, "%s%s%s: agent failed to cancel secrets: %s", NM_PRINT_FMT_QUOTED (description, "(", description, ")", "???"), error->message); - g_clear_error (&error); } - - g_free (description); } static void @@ -426,11 +433,16 @@ do_cancel_secrets (NMSecretAgent *self, NMSecretAgentCallId *r, gboolean disposi if ( r->is_get_secrets && priv->proxy) { /* for GetSecrets call, we must cancel the request. */ - nmdbus_secret_agent_call_cancel_get_secrets (priv->proxy, - r->path, r->setting_name, - NULL, - cancel_done, - g_strdup (nm_secret_agent_get_description (self))); + g_dbus_proxy_call (G_DBUS_PROXY (priv->proxy), + "CancelGetSecrets", + g_variant_new ("(os)", + r->path, + r->setting_name), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + cancel_done, + g_strdup (nm_secret_agent_get_description (self))); } cancellable = r->cancellable; @@ -494,11 +506,11 @@ agent_save_cb (GObject *proxy, NMSecretAgentCallId *r = user_data; if (request_check_return (r)) { - NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (r->agent); gs_free_error GError *error = NULL; + gs_unref_variant GVariant *ret = NULL; - nmdbus_secret_agent_call_save_secrets_finish (priv->proxy, result, &error); - if (error) + ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, G_VARIANT_TYPE ("()"), &error); + if (!ret) g_dbus_error_strip_remote_error (error); r->callback (r->agent, r, NULL, error, r->callback_data); } @@ -527,11 +539,16 @@ nm_secret_agent_save_secrets (NMSecretAgent *self, dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL); r = request_new (self, "SaveSecrets", path, NULL, callback, callback_data); - nmdbus_secret_agent_call_save_secrets (priv->proxy, - dict, - path, - NULL, /* cancelling the request does *not* cancel the D-Bus call. */ - agent_save_cb, r); + g_dbus_proxy_call (priv->proxy, + "SaveSecrets", + g_variant_new ("(@a{sa{sv}}o)", + dict, + path), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* cancelling the request does *not* cancel the D-Bus call. */ + agent_save_cb, + r); return r; } @@ -546,11 +563,11 @@ agent_delete_cb (GObject *proxy, NMSecretAgentCallId *r = user_data; if (request_check_return (r)) { - NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (r->agent); gs_free_error GError *error = NULL; + gs_unref_variant GVariant *ret = NULL; - nmdbus_secret_agent_call_delete_secrets_finish (priv->proxy, result, &error); - if (error) + ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, G_VARIANT_TYPE ("()"), &error); + if (!ret) g_dbus_error_strip_remote_error (error); r->callback (r->agent, r, NULL, error, r->callback_data); } @@ -579,12 +596,16 @@ nm_secret_agent_delete_secrets (NMSecretAgent *self, dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_NO_SECRETS); r = request_new (self, "DeleteSecrets", path, NULL, callback, callback_data); - nmdbus_secret_agent_call_delete_secrets (priv->proxy, - dict, - path, - NULL, /* cancelling the request does *not* cancel the D-Bus call. */ - agent_delete_cb, r); - + g_dbus_proxy_call (priv->proxy, + "DeleteSecrets", + g_variant_new ("(@a{sa{sv}}o)", + dict, + path), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* cancelling the request does *not* cancel the D-Bus call. */ + agent_delete_cb, + r); return r; } @@ -610,7 +631,7 @@ _on_disconnected_cleanup (NMSecretAgentPrivate *priv) } static void -_on_disconnected_private_connection (NMBusManager *mgr, +_on_disconnected_private_connection (NMDBusManager *mgr, GDBusConnection *connection, NMSecretAgent *self) { @@ -666,7 +687,6 @@ nm_secret_agent_new (GDBusMethodInvocation *context, NMSecretAgentPrivate *priv; const char *dbus_owner; struct passwd *pw; - GDBusProxy *proxy; char *owner_username = NULL; char *description = NULL; char buf_subject[64]; @@ -695,9 +715,9 @@ nm_secret_agent_new (GDBusMethodInvocation *context, priv = NM_SECRET_AGENT_GET_PRIVATE (self); - priv->bus_mgr = g_object_ref (nm_bus_manager_get ()); + priv->bus_mgr = g_object_ref (nm_dbus_manager_get ()); priv->connection = g_object_ref (connection); - priv->connection_is_private = !!nm_bus_manager_connection_get_private_name (priv->bus_mgr, connection); + priv->connection_is_private = !!nm_dbus_manager_connection_get_private_name (priv->bus_mgr, connection); _LOGt ("constructed: %s, owner=%s%s%s (%s), private-connection=%d, unique-name=%s%s%s, capabilities=%s", (description = _create_description (dbus_owner, identifier, uid)), @@ -714,20 +734,18 @@ nm_secret_agent_new (GDBusMethodInvocation *context, priv->capabilities = capabilities; priv->subject = g_object_ref (subject); - proxy = nm_bus_manager_new_proxy (priv->bus_mgr, - priv->connection, - NMDBUS_TYPE_SECRET_AGENT_PROXY, - priv->dbus_owner, - NM_DBUS_PATH_SECRET_AGENT, - NM_DBUS_INTERFACE_SECRET_AGENT); - g_assert (proxy); - priv->proxy = NMDBUS_SECRET_AGENT (proxy); + priv->proxy = nm_dbus_manager_new_proxy (priv->bus_mgr, + priv->connection, + G_TYPE_DBUS_PROXY, + priv->dbus_owner, + NM_DBUS_PATH_SECRET_AGENT, + NM_DBUS_INTERFACE_SECRET_AGENT); /* we cannot subscribe to notify::g-name-owner because that doesn't work * for unique names and it doesn't work for private connections. */ if (priv->connection_is_private) { priv->on_disconnected_id = g_signal_connect (priv->bus_mgr, - NM_BUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED, + NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED, G_CALLBACK (_on_disconnected_private_connection), self); } else { diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 58626372..6526abc7 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -25,13 +25,14 @@ #include -#include "nm-utils/c-list.h" +#include "c-list/src/c-list.h" #include "nm-common-macros.h" #include "nm-config.h" #include "nm-config-data.h" #include "nm-dbus-interface.h" #include "nm-session-monitor.h" +#include "nm-auth-manager.h" #include "nm-auth-utils.h" #include "nm-auth-subject.h" #include "nm-agent-manager.h" @@ -39,8 +40,6 @@ #include "nm-core-internal.h" #include "nm-audit-manager.h" -#include "introspection/org.freedesktop.NetworkManager.Settings.Connection.h" - #define SETTINGS_TIMESTAMPS_FILE NMSTATEDIR "/timestamps" #define SETTINGS_SEEN_BSSIDS_FILE NMSTATEDIR "/seen-bssids" @@ -60,9 +59,9 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMSettingsConnection, ); enum { - UPDATED, REMOVED, UPDATED_INTERNAL, + FLAGS_CHANGED, LAST_SIGNAL }; @@ -74,7 +73,7 @@ typedef struct _NMSettingsConnectionPrivate { NMSessionMonitor *session_monitor; gulong session_changed_id; - NMSettingsConnectionFlags flags:5; + NMSettingsConnectionIntFlags flags:5; bool removed:1; bool ready:1; @@ -83,7 +82,8 @@ typedef struct _NMSettingsConnectionPrivate { NMSettingsAutoconnectBlockedReason autoconnect_blocked_reason:4; - GSList *pending_auths; /* List of pending authentication requests */ + /* List of pending authentication requests */ + CList auth_lst_head; CList call_ids_lst_head; /* in-progress secrets requests */ @@ -115,7 +115,7 @@ typedef struct _NMSettingsConnectionPrivate { } NMSettingsConnectionPrivate; -G_DEFINE_TYPE_WITH_CODE (NMSettingsConnection, nm_settings_connection, NM_TYPE_EXPORTED_OBJECT, +G_DEFINE_TYPE_WITH_CODE (NMSettingsConnection, nm_settings_connection, NM_TYPE_DBUS_OBJECT, G_IMPLEMENT_INTERFACE (NM_TYPE_CONNECTION, nm_settings_connection_connection_interface_init) ) @@ -135,7 +135,7 @@ G_DEFINE_TYPE_WITH_CODE (NMSettingsConnection, nm_settings_connection, NM_TYPE_E const char *__uuid = (self) ? nm_settings_connection_get_uuid (self) : NULL; \ \ if (self) { \ - g_snprintf (__prefix, sizeof (__prefix), "%s[%p%s%s]", _NMLOG_PREFIX_NAME, self, __uuid ? "," : "", __uuid ? __uuid : ""); \ + g_snprintf (__prefix, sizeof (__prefix), "%s[%p%s%s]", _NMLOG_PREFIX_NAME, self, __uuid ? "," : "", __uuid ?: ""); \ __p_prefix = __prefix; \ } \ _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, __uuid, \ @@ -146,12 +146,9 @@ G_DEFINE_TYPE_WITH_CODE (NMSettingsConnection, nm_settings_connection, NM_TYPE_E /*****************************************************************************/ -static void -_emit_updated (NMSettingsConnection *self, gboolean by_user) -{ - g_signal_emit (self, signals[UPDATED], 0); - g_signal_emit (self, signals[UPDATED_INTERNAL], 0, by_user); -} +static const GDBusSignalInfo signal_info_updated; +static const GDBusSignalInfo signal_info_removed; +static const NMDBusInterfaceInfoExtended interface_info_settings_connection; /*****************************************************************************/ @@ -320,7 +317,7 @@ static void set_visible (NMSettingsConnection *self, gboolean new_visible) { nm_settings_connection_set_flags (self, - NM_SETTINGS_CONNECTION_FLAGS_VISIBLE, + NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE, new_visible); } @@ -390,7 +387,7 @@ nm_settings_connection_check_permission (NMSettingsConnection *self, priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (self), - NM_SETTINGS_CONNECTION_FLAGS_VISIBLE)) + NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)) return FALSE; s_con = nm_connection_get_setting_connection (NM_CONNECTION (self)); @@ -466,7 +463,7 @@ update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new) if (priv->agent_secrets) g_object_unref (priv->agent_secrets); - priv->agent_secrets = nm_simple_connection_new_clone (new ? new : NM_CONNECTION (self)); + priv->agent_secrets = nm_simple_connection_new_clone (new ?: NM_CONNECTION(self)); /* Clear out non-system-owned secrets */ nm_connection_clear_secrets_with_flags (priv->agent_secrets, @@ -490,30 +487,30 @@ secrets_cleared_cb (NMSettingsConnection *self) static void set_persist_mode (NMSettingsConnection *self, NMSettingsConnectionPersistMode persist_mode) { - NMSettingsConnectionFlags flags = NM_SETTINGS_CONNECTION_FLAGS_NONE; - const NMSettingsConnectionFlags ALL = NM_SETTINGS_CONNECTION_FLAGS_UNSAVED - | NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED - | NM_SETTINGS_CONNECTION_FLAGS_VOLATILE; + NMSettingsConnectionIntFlags flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE; + const NMSettingsConnectionIntFlags ALL = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED + | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE; switch (persist_mode) { case NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK: - flags = NM_SETTINGS_CONNECTION_FLAGS_NONE; + flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE; break; case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY: case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED: case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY: - flags = NM_SETTINGS_CONNECTION_FLAGS_UNSAVED; + flags = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED; break; case NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED: case NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY: - flags = NM_SETTINGS_CONNECTION_FLAGS_UNSAVED | - NM_SETTINGS_CONNECTION_FLAGS_VOLATILE; + flags = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED | + NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE; break; case NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED: /* only set the connection as unsaved, but preserve the nm-generated * and volatile flag. */ nm_settings_connection_set_flags (self, - NM_SETTINGS_CONNECTION_FLAGS_UNSAVED, + NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED, TRUE); return; case NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP: @@ -525,6 +522,16 @@ set_persist_mode (NMSettingsConnection *self, NMSettingsConnectionPersistMode pe nm_settings_connection_set_flags_full (self, ALL, flags); } +static void +_emit_updated (NMSettingsConnection *self, gboolean by_user) +{ + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), + &interface_info_settings_connection, + &signal_info_updated, + "()"); + g_signal_emit (self, signals[UPDATED_INTERNAL], 0, by_user); +} + static void connection_changed_cb (NMSettingsConnection *self, gpointer unused) { @@ -571,17 +578,13 @@ _update_prepare (NMSettingsConnection *self, NMConnection *new_connection, GError **error) { - NMSettingsConnectionPrivate *priv; - g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE); - priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - if (!nm_connection_normalize (new_connection, NULL, NULL, error)) return FALSE; - if ( nm_connection_get_path (NM_CONNECTION (self)) + if ( nm_dbus_object_get_path (NM_DBUS_OBJECT (self)) && g_strcmp0 (nm_settings_connection_get_uuid (self), nm_connection_get_uuid (new_connection)) != 0) { /* Updating the UUID is not allowed once the path is exported. */ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, @@ -661,8 +664,10 @@ nm_settings_connection_update (NMSettingsConnection *self, NM_SETTING_COMPARE_FLAG_EXACT)) { gs_unref_object NMConnection *simple = NULL; - if (log_diff_name) - nm_utils_log_connection_diff (replace_connection, NM_CONNECTION (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ "); + if (log_diff_name) { + nm_utils_log_connection_diff (replace_connection, NM_CONNECTION (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ", + nm_dbus_object_get_path (NM_DBUS_OBJECT (self))); + } /* Make a copy of agent-owned secrets because they won't be present in * the connection returned by plugins, as plugins return only what was @@ -679,7 +684,7 @@ nm_settings_connection_update (NMSettingsConnection *self, } nm_settings_connection_set_flags (self, - NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_FLAGS_VOLATILE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE, FALSE); if (replaced) { @@ -798,7 +803,7 @@ nm_settings_connection_delete (NMSettingsConnection *self, for_agents = nm_simple_connection_new_clone (NM_CONNECTION (self)); nm_connection_clear_secrets (for_agents); nm_agent_manager_delete_secrets (priv->agent_mgr, - nm_connection_get_path (NM_CONNECTION (self)), + nm_dbus_object_get_path (NM_DBUS_OBJECT (self)), for_agents); g_object_unref (for_agents); @@ -812,10 +817,8 @@ nm_settings_connection_delete (NMSettingsConnection *self, return TRUE; } - /*****************************************************************************/ - typedef enum { CALL_ID_TYPE_REQ, CALL_ID_TYPE_IDLE, @@ -1295,7 +1298,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self, priv->last_secret_agent_version_id = nm_agent_manager_get_agent_version_id (priv->agent_mgr); call_id_a = nm_agent_manager_get_secrets (priv->agent_mgr, - nm_connection_get_path (NM_CONNECTION (self)), + nm_dbus_object_get_path (NM_DBUS_OBJECT (self)), NM_CONNECTION (self), subject, existing_secrets, @@ -1359,7 +1362,7 @@ nm_settings_connection_cancel_secrets (NMSettingsConnection *self, _get_secrets_cancel (self, call_id, FALSE); } -/**** User authorization **************************************/ +/*****************************************************************************/ typedef void (*AuthCallback) (NMSettingsConnection *self, GDBusMethodInvocation *context, @@ -1367,46 +1370,61 @@ typedef void (*AuthCallback) (NMSettingsConnection *self, GError *error, gpointer data); -static void -pk_auth_cb (NMAuthChain *chain, - GError *chain_error, - GDBusMethodInvocation *context, - gpointer user_data) -{ - NMSettingsConnection *self = NM_SETTINGS_CONNECTION (user_data); - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - GError *error = NULL; - NMAuthCallResult result; - const char *perm; +typedef struct { + CList auth_lst; + NMAuthManagerCallId *call_id; + NMSettingsConnection *self; AuthCallback callback; gpointer callback_data; + GDBusMethodInvocation *invocation; NMAuthSubject *subject; +} AuthData; - priv->pending_auths = g_slist_remove (priv->pending_auths, chain); +static void +pk_auth_cb (NMAuthManager *auth_manager, + NMAuthManagerCallId *auth_call_id, + gboolean is_authorized, + gboolean is_challenge, + GError *auth_error, + gpointer user_data) +{ + AuthData *auth_data = user_data; + NMSettingsConnection *self; + gs_free_error GError *error = NULL; + + nm_assert (auth_data); + nm_assert (NM_IS_SETTINGS_CONNECTION (auth_data->self)); + + self = auth_data->self; - perm = nm_auth_chain_get_data (chain, "perm"); - g_assert (perm); - result = nm_auth_chain_get_result (chain, perm); + auth_data->call_id = NULL; - /* If our NMSettingsConnection is already gone, do nothing */ - if (chain_error) { + c_list_unlink (&auth_data->auth_lst); + + if (g_error_matches (auth_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + error = g_error_new (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Error checking authorization: connection was deleted"); + } else if (auth_error) { error = g_error_new (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "Error checking authorization: %s", - chain_error->message ? chain_error->message : "(unknown)"); - } else if (result != NM_AUTH_CALL_RESULT_YES) { + auth_error->message); + } else if (nm_auth_call_result_eval (is_authorized, is_challenge, auth_error) != NM_AUTH_CALL_RESULT_YES) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, - "Insufficient privileges."); + "Insufficient privileges"); } - callback = nm_auth_chain_get_data (chain, "callback"); - callback_data = nm_auth_chain_get_data (chain, "callback-data"); - subject = nm_auth_chain_get_data (chain, "subject"); - callback (self, context, subject, error, callback_data); + auth_data->callback (self, + auth_data->invocation, + auth_data->subject, + error, + auth_data->callback_data); - g_clear_error (&error); - nm_auth_chain_unref (chain); + g_object_unref (auth_data->invocation); + g_object_unref (auth_data->subject); + g_slice_free (AuthData, auth_data); } /** @@ -1434,59 +1452,52 @@ _new_auth_subject (GDBusMethodInvocation *context, GError **error) return subject; } +/* may either invoke callback synchronously or asynchronously. */ static void auth_start (NMSettingsConnection *self, - GDBusMethodInvocation *context, + GDBusMethodInvocation *invocation, NMAuthSubject *subject, const char *check_permission, AuthCallback callback, gpointer callback_data) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - NMAuthChain *chain; + AuthData *auth_data; GError *error = NULL; - char *error_desc = NULL; - g_return_if_fail (context != NULL); - g_return_if_fail (NM_IS_AUTH_SUBJECT (subject)); + nm_assert (nm_dbus_object_is_exported (NM_DBUS_OBJECT (self))); + nm_assert (G_IS_DBUS_METHOD_INVOCATION (invocation)); + nm_assert (NM_IS_AUTH_SUBJECT (subject)); - /* Ensure the caller can view this connection */ - if (!nm_auth_is_subject_in_acl (NM_CONNECTION (self), - subject, - &error_desc)) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - error_desc); - g_free (error_desc); - - callback (self, context, subject, error, callback_data); + if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (self), + subject, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + &error)) { + callback (self, invocation, subject, error, callback_data); g_clear_error (&error); return; } if (!check_permission) { /* Don't need polkit auth, automatic success */ - callback (self, context, subject, NULL, callback_data); + callback (self, invocation, subject, NULL, callback_data); return; } - chain = nm_auth_chain_new_subject (subject, context, pk_auth_cb, self); - if (!chain) { - g_set_error_literal (&error, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - "Unable to authenticate the request."); - callback (self, context, subject, error, callback_data); - g_clear_error (&error); - return; - } - - priv->pending_auths = g_slist_append (priv->pending_auths, chain); - nm_auth_chain_set_data (chain, "perm", (gpointer) check_permission, NULL); - nm_auth_chain_set_data (chain, "callback", callback, NULL); - nm_auth_chain_set_data (chain, "callback-data", callback_data, NULL); - nm_auth_chain_set_data (chain, "subject", g_object_ref (subject), g_object_unref); - nm_auth_chain_add_call (chain, check_permission, TRUE); + auth_data = g_slice_new (AuthData); + auth_data->self = self; + auth_data->callback = callback; + auth_data->callback_data = callback_data; + auth_data->invocation = g_object_ref (invocation); + auth_data->subject = g_object_ref (subject); + c_list_link_tail (&priv->auth_lst_head, &auth_data->auth_lst); + auth_data->call_id = nm_auth_manager_check_authorization (nm_auth_manager_get (), + subject, + check_permission, + TRUE, + pk_auth_cb, + auth_data); } /**** DBus method handlers ************************************/ @@ -1523,7 +1534,7 @@ check_writable (NMConnection *self, GError **error) } static void -get_settings_auth_cb (NMSettingsConnection *self, +get_settings_auth_cb (NMSettingsConnection *self, GDBusMethodInvocation *context, NMAuthSubject *subject, GError *error, @@ -1577,18 +1588,25 @@ get_settings_auth_cb (NMSettingsConnection *self, } static void -impl_settings_connection_get_settings (NMSettingsConnection *self, - GDBusMethodInvocation *context) -{ - NMAuthSubject *subject; +impl_settings_connection_get_settings (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj); + gs_unref_object NMAuthSubject *subject = NULL; GError *error = NULL; - subject = _new_auth_subject (context, &error); - if (subject) { - auth_start (self, context, subject, NULL, get_settings_auth_cb, NULL); - g_object_unref (subject); - } else - g_dbus_method_invocation_take_error (context, error); + subject = _new_auth_subject (invocation, &error); + if (!subject) { + g_dbus_method_invocation_take_error (invocation, error); + return; + } + + auth_start (self, invocation, subject, NULL, get_settings_auth_cb, NULL); } typedef struct { @@ -1782,7 +1800,7 @@ update_auth_cb (NMSettingsConnection *self, secrets_filter_cb, GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); nm_agent_manager_save_secrets (info->agent_mgr, - nm_connection_get_path (NM_CONNECTION (self)), + nm_dbus_object_get_path (NM_DBUS_OBJECT (self)), for_agent, info->subject); } @@ -1790,7 +1808,6 @@ update_auth_cb (NMSettingsConnection *self, update_complete (self, info, local); } - static const char * get_update_modify_permission (NMConnection *old, NMConnection *new) { @@ -1830,7 +1847,6 @@ settings_connection_update (NMSettingsConnection *self, GError *error = NULL; UpdateInfo *info; const char *permission; - char *error_desc = NULL; /* If the connection is read-only, that has to be changed at the source of * the problem (ex a system settings plugin that can't write connections out) @@ -1867,15 +1883,12 @@ settings_connection_update (NMSettingsConnection *self, * that's sending the update request. You can't make a connection * invisible to yourself. */ - if (!nm_auth_is_subject_in_acl (tmp ? tmp : NM_CONNECTION (self), - subject, - &error_desc)) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - error_desc); - g_free (error_desc); + if (!nm_auth_is_subject_in_acl_set_error (tmp ?: NM_CONNECTION(self), + subject, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + &error)) goto error; - } info = g_slice_new0 (UpdateInfo); info->is_update2 = is_update2; @@ -1886,7 +1899,7 @@ settings_connection_update (NMSettingsConnection *self, info->new_settings = tmp; permission = get_update_modify_permission (NM_CONNECTION (self), - tmp ? tmp : NM_CONNECTION (self)); + tmp ?: NM_CONNECTION(self)); auth_start (self, context, subject, permission, update_auth_cb, info); return; @@ -1901,54 +1914,87 @@ error: } static void -impl_settings_connection_update (NMSettingsConnection *self, - GDBusMethodInvocation *context, - GVariant *new_settings) +impl_settings_connection_update (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { - settings_connection_update (self, FALSE, context, new_settings, NM_SETTINGS_UPDATE2_FLAG_TO_DISK); + NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj); + gs_unref_variant GVariant *settings = NULL; + + g_variant_get (parameters, "(@a{sa{sv}})", &settings); + settings_connection_update (self, FALSE, invocation, settings, NM_SETTINGS_UPDATE2_FLAG_TO_DISK); } static void -impl_settings_connection_update_unsaved (NMSettingsConnection *self, - GDBusMethodInvocation *context, - GVariant *new_settings) +impl_settings_connection_update_unsaved (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { - settings_connection_update (self, FALSE, context, new_settings, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY); + NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj); + gs_unref_variant GVariant *settings = NULL; + + g_variant_get (parameters, "(@a{sa{sv}})", &settings); + settings_connection_update (self, FALSE, invocation, settings, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY); } static void -impl_settings_connection_save (NMSettingsConnection *self, - GDBusMethodInvocation *context) +impl_settings_connection_save (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { - settings_connection_update (self, FALSE, context, NULL, NM_SETTINGS_UPDATE2_FLAG_TO_DISK); + NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj); + + settings_connection_update (self, FALSE, invocation, NULL, NM_SETTINGS_UPDATE2_FLAG_TO_DISK); } static void -impl_settings_connection_update2 (NMSettingsConnection *self, - GDBusMethodInvocation *context, - GVariant *settings, - guint32 flags_u, - GVariant *args) -{ +impl_settings_connection_update2 (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj); + gs_unref_variant GVariant *settings = NULL; + gs_unref_variant GVariant *args = NULL; + guint32 flags_u; GError *error = NULL; GVariantIter iter; const char *args_name; - const NMSettingsUpdate2Flags flags = (NMSettingsUpdate2Flags) flags_u; + NMSettingsUpdate2Flags flags; const NMSettingsUpdate2Flags ALL_PERSIST_MODES = NM_SETTINGS_UPDATE2_FLAG_TO_DISK | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY; + g_variant_get (parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args); + if (NM_FLAGS_ANY (flags_u, ~((guint32) (ALL_PERSIST_MODES | NM_SETTINGS_UPDATE2_FLAG_VOLATILE | NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT)))) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_ARGUMENTS, "Unknown flags"); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_take_error (invocation, error); return; } + flags = (NMSettingsUpdate2Flags) flags_u; + if ( ( NM_FLAGS_ANY (flags, ALL_PERSIST_MODES) && !nm_utils_is_power_of_two (flags & ALL_PERSIST_MODES)) || ( NM_FLAGS_HAS (flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE) @@ -1957,7 +2003,7 @@ impl_settings_connection_update2 (NMSettingsConnection *self, error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_ARGUMENTS, "Conflicting flags"); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_take_error (invocation, error); return; } @@ -1965,7 +2011,7 @@ impl_settings_connection_update2 (NMSettingsConnection *self, error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_ARGUMENTS, "args is of invalid type"); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_take_error (invocation, error); return; } @@ -1974,13 +2020,13 @@ impl_settings_connection_update2 (NMSettingsConnection *self, error = g_error_new (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_ARGUMENTS, "Unsupported argument '%s'", args_name); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_take_error (invocation, error); return; } settings_connection_update (self, TRUE, - context, + invocation, settings, flags); } @@ -2025,7 +2071,7 @@ get_modify_permission_basic (NMSettingsConnection *self) * request affects more than just the caller, require 'modify.system'. */ s_con = nm_connection_get_setting_connection (NM_CONNECTION (self)); - g_assert (s_con); + nm_assert (s_con); if (nm_setting_connection_get_num_permissions (s_con) == 1) return NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN; @@ -2033,26 +2079,30 @@ get_modify_permission_basic (NMSettingsConnection *self) } static void -impl_settings_connection_delete (NMSettingsConnection *self, - GDBusMethodInvocation *context) -{ - NMAuthSubject *subject = NULL; +impl_settings_connection_delete (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj); + gs_unref_object NMAuthSubject *subject = NULL; GError *error = NULL; if (!check_writable (NM_CONNECTION (self), &error)) - goto out_err; + goto err; - subject = _new_auth_subject (context, &error); - if (subject) { - auth_start (self, context, subject, get_modify_permission_basic (self), delete_auth_cb, NULL); - g_object_unref (subject); - } else - goto out_err; + subject = _new_auth_subject (invocation, &error); + if (!subject) + goto err; + auth_start (self, invocation, subject, get_modify_permission_basic (self), delete_auth_cb, NULL); return; -out_err: +err: nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, FALSE, NULL, subject, error->message); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_take_error (invocation, error); } /*****************************************************************************/ @@ -2111,24 +2161,33 @@ dbus_get_secrets_auth_cb (NMSettingsConnection *self, } static void -impl_settings_connection_get_secrets (NMSettingsConnection *self, - GDBusMethodInvocation *context, - const gchar *setting_name) -{ - NMAuthSubject *subject; +impl_settings_connection_get_secrets (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj); + gs_unref_object NMAuthSubject *subject = NULL; GError *error = NULL; + const char *setting_name; - subject = _new_auth_subject (context, &error); - if (subject) { - auth_start (self, - context, - subject, - get_modify_permission_basic (self), - dbus_get_secrets_auth_cb, - g_strdup (setting_name)); - g_object_unref (subject); - } else - g_dbus_method_invocation_take_error (context, error); + subject = _new_auth_subject (invocation, &error); + if (!subject) { + g_dbus_method_invocation_take_error (invocation, error); + return; + } + + g_variant_get (parameters, "(&s)", &setting_name); + + auth_start (self, + invocation, + subject, + get_modify_permission_basic (self), + dbus_get_secrets_auth_cb, + g_strdup (setting_name)); } static void @@ -2157,7 +2216,7 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self, /* Tell agents to remove secrets for this connection */ nm_agent_manager_delete_secrets (priv->agent_mgr, - nm_connection_get_path (NM_CONNECTION (self)), + nm_dbus_object_get_path (NM_DBUS_OBJECT (self)), NM_CONNECTION (self)); nm_settings_connection_update (self, @@ -2177,26 +2236,31 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self, } static void -impl_settings_connection_clear_secrets (NMSettingsConnection *self, - GDBusMethodInvocation *context) -{ - NMAuthSubject *subject; +impl_settings_connection_clear_secrets (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj); + gs_unref_object NMAuthSubject *subject = NULL; GError *error = NULL; - subject = _new_auth_subject (context, &error); - if (subject) { - auth_start (self, - context, - subject, - get_modify_permission_basic (self), - dbus_clear_secrets_auth_cb, - NULL); - g_object_unref (subject); - } else { + subject = _new_auth_subject (invocation, &error); + if (!subject) { nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self, FALSE, NULL, NULL, error->message); - g_dbus_method_invocation_take_error (context, error); + g_dbus_method_invocation_take_error (invocation, error); + return; } + auth_start (self, + invocation, + subject, + get_modify_permission_basic (self), + dbus_clear_secrets_auth_cb, + NULL); } /*****************************************************************************/ @@ -2216,55 +2280,64 @@ void nm_settings_connection_signal_remove (NMSettingsConnection *self) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + AuthData *auth_data; if (priv->removed) return; priv->removed = TRUE; - g_signal_emit_by_name (self, NM_SETTINGS_CONNECTION_REMOVED); + + while ((auth_data = c_list_first_entry (&priv->auth_lst_head, AuthData, auth_lst))) + nm_auth_manager_check_authorization_cancel (auth_data->call_id); + + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), + &interface_info_settings_connection, + &signal_info_removed, + "()"); + g_signal_emit (self, signals[REMOVED], 0); } gboolean nm_settings_connection_get_unsaved (NMSettingsConnection *self) { - return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_UNSAVED); + return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED); } /*****************************************************************************/ -NM_UTILS_FLAGS2STR_DEFINE_STATIC (_settings_connection_flags_to_string, NMSettingsConnectionFlags, - NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_NONE, "none"), - NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_UNSAVED, "unsaved"), - NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED, "nm-generated"), - NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_VOLATILE, "volatile"), - NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_VISIBLE, "visible"), +NM_UTILS_FLAGS2STR_DEFINE_STATIC (_settings_connection_flags_to_string, NMSettingsConnectionIntFlags, + NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, "none"), + NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED, "unsaved"), + NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, "nm-generated"), + NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE, "volatile"), + NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE, "visible"), ); -NMSettingsConnectionFlags +NMSettingsConnectionIntFlags nm_settings_connection_get_flags (NMSettingsConnection *self) { - g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE); + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_INT_FLAGS_NONE); return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->flags; } -NMSettingsConnectionFlags -nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionFlags flags, gboolean set) +NMSettingsConnectionIntFlags +nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionIntFlags flags, gboolean set) { return nm_settings_connection_set_flags_full (self, flags, - set ? flags : NM_SETTINGS_CONNECTION_FLAGS_NONE); + set ? flags : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE); } -NMSettingsConnectionFlags +NMSettingsConnectionIntFlags nm_settings_connection_set_flags_full (NMSettingsConnection *self, - NMSettingsConnectionFlags mask, - NMSettingsConnectionFlags value) + NMSettingsConnectionIntFlags mask, + NMSettingsConnectionIntFlags value) { NMSettingsConnectionPrivate *priv; - NMSettingsConnectionFlags old_flags; + NMSettingsConnectionIntFlags old_flags; - g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE); - nm_assert (mask && !NM_FLAGS_ANY (mask, ~NM_SETTINGS_CONNECTION_FLAGS_ALL)); + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_INT_FLAGS_NONE); + nm_assert (mask && !NM_FLAGS_ANY (mask, ~NM_SETTINGS_CONNECTION_INT_FLAGS_ALL)); nm_assert (!NM_FLAGS_ANY (value, ~mask)); priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); @@ -2273,6 +2346,7 @@ nm_settings_connection_set_flags_full (NMSettingsConnection *self, old_flags = priv->flags; if (old_flags != value) { + gboolean notify_unsaved = FALSE; char buf1[255], buf2[255]; _LOGT ("update settings-connection flags to %s (was %s)", @@ -2280,9 +2354,17 @@ nm_settings_connection_set_flags_full (NMSettingsConnection *self, _settings_connection_flags_to_string (priv->flags, buf2, sizeof (buf2))); priv->flags = value; nm_assert (priv->flags == value); - _notify (self, PROP_FLAGS); - if (NM_FLAGS_HAS (old_flags, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED) != NM_FLAGS_HAS (value, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED)) + + if (NM_FLAGS_HAS (old_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED) != NM_FLAGS_HAS (value, NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED)) { + g_object_freeze_notify (G_OBJECT (self)); _notify (self, PROP_UNSAVED); + notify_unsaved = TRUE; + } + _notify (self, PROP_FLAGS); + if (notify_unsaved) + g_object_thaw_notify (G_OBJECT (self)); + + g_signal_emit (self, signals[FLAGS_CHANGED], 0); } return old_flags; } @@ -2703,7 +2785,7 @@ _autoconnect_retries_set (NMSettingsConnection *self, if (retries) priv->autoconnect_retries_blocked_until = 0; else { - /* XXX: the blocked time must be identical for all connections, otherwise + /* NOTE: the blocked time must be identical for all connections, otherwise * the tracking of resetting the retry count in NMPolicy needs adjustment * in _connection_autoconnect_retries_set() (as it would need to re-evaluate * the next-timeout everytime a connection gets blocked). */ @@ -2798,7 +2880,7 @@ gboolean nm_settings_connection_autoconnect_is_blocked (NMSettingsConnection *self) { NMSettingsConnectionPrivate *priv; - NMSettingsConnectionFlags flags; + NMSettingsConnectionIntFlags flags; g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), TRUE); @@ -2810,9 +2892,9 @@ nm_settings_connection_autoconnect_is_blocked (NMSettingsConnection *self) return TRUE; flags = priv->flags; - if (NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_FLAGS_VOLATILE)) + if (NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)) return TRUE; - if (!NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_FLAGS_VISIBLE)) + if (!NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)) return TRUE; return FALSE; @@ -2900,8 +2982,11 @@ nm_settings_connection_init (NMSettingsConnection *self) priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnectionPrivate); self->_priv = priv; + c_list_init (&self->_connections_lst); + priv->ready = TRUE; c_list_init (&priv->call_ids_lst_head); + c_list_init (&priv->auth_lst_head); priv->session_monitor = g_object_ref (nm_session_monitor_get ()); priv->session_changed_id = g_signal_connect (priv->session_monitor, @@ -2937,6 +3022,9 @@ dispose (GObject *object) _LOGD ("disposing"); + nm_assert (c_list_is_empty (&self->_connections_lst)); + nm_assert (c_list_is_empty (&priv->auth_lst_head)); + /* Cancel in-progress secrets requests */ if (priv->agent_mgr) { c_list_for_each_entry_safe (call_id, call_id_safe, &priv->call_ids_lst_head, call_ids_lst) @@ -2954,11 +3042,7 @@ dispose (GObject *object) g_clear_object (&priv->system_secrets); g_clear_object (&priv->agent_secrets); - /* Cancel PolicyKit requests */ - g_slist_free_full (priv->pending_auths, (GDestroyNotify) nm_auth_chain_unref); - priv->pending_auths = NULL; - - g_clear_pointer (&priv->seen_bssids, (GDestroyNotify) g_hash_table_destroy); + g_clear_pointer (&priv->seen_bssids, g_hash_table_destroy); set_visible (self, FALSE); @@ -2986,7 +3070,8 @@ get_property (GObject *object, guint prop_id, g_value_set_boolean (value, nm_settings_connection_get_ready (self)); break; case PROP_FLAGS: - g_value_set_uint (value, nm_settings_connection_get_flags (self)); + g_value_set_uint (value, + nm_settings_connection_get_flags (self) & NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK); break; case PROP_FILENAME: g_value_set_string (value, nm_settings_connection_get_filename (self)); @@ -3014,22 +3099,120 @@ set_property (GObject *object, guint prop_id, } } +static const GDBusSignalInfo signal_info_updated = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "Updated", +); + +static const GDBusSignalInfo signal_info_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "Removed", +); + +static const NMDBusInterfaceInfoExtended interface_info_settings_connection = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_SETTINGS_CONNECTION, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "Update", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("properties", "a{sa{sv}}"), + ), + ), + .handle = impl_settings_connection_update, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "UpdateUnsaved", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("properties", "a{sa{sv}}"), + ), + ), + .handle = impl_settings_connection_update_unsaved, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "Delete", + ), + .handle = impl_settings_connection_delete, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "GetSettings", + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("settings", "a{sa{sv}}"), + ), + ), + .handle = impl_settings_connection_get_settings, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "GetSecrets", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("setting_name", "s"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("secrets", "a{sa{sv}}"), + ), + ), + .handle = impl_settings_connection_get_secrets, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "ClearSecrets", + ), + .handle = impl_settings_connection_clear_secrets, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "Save", + ), + .handle = impl_settings_connection_save, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "Update2", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("settings", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO ("flags", "u"), + NM_DEFINE_GDBUS_ARG_INFO ("args", "a{sv}"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("result", "a{sv}"), + ), + ), + .handle = impl_settings_connection_update2, + ), + ), + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + &signal_info_updated, + &signal_info_removed, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Unsaved", "b", NM_SETTINGS_CONNECTION_UNSAVED), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Flags", "u", NM_SETTINGS_CONNECTION_FLAGS), + ), + ), + .legacy_property_changed = TRUE, +}; + static void -nm_settings_connection_class_init (NMSettingsConnectionClass *class) +nm_settings_connection_class_init (NMSettingsConnectionClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS (class); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (class); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); - g_type_class_add_private (class, sizeof (NMSettingsConnectionPrivate)); + g_type_class_add_private (klass, sizeof (NMSettingsConnectionPrivate)); - exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH_SETTINGS); + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH_SETTINGS); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_settings_connection); object_class->constructed = constructed; object_class->dispose = dispose; object_class->get_property = get_property; object_class->set_property = set_property; - class->supports_secrets = supports_secrets; + klass->supports_secrets = supports_secrets; obj_properties[PROP_UNSAVED] = g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED, "", "", @@ -3045,9 +3228,7 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class) obj_properties[PROP_FLAGS] = g_param_spec_uint (NM_SETTINGS_CONNECTION_FLAGS, "", "", - NM_SETTINGS_CONNECTION_FLAGS_NONE, - NM_SETTINGS_CONNECTION_FLAGS_ALL, - NM_SETTINGS_CONNECTION_FLAGS_NONE, + 0, G_MAXUINT32, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); @@ -3060,20 +3241,10 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class) g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - signals[UPDATED] = - g_signal_new (NM_SETTINGS_CONNECTION_UPDATED, - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - /* internal signal, with an argument (gboolean by_user). */ signals[UPDATED_INTERNAL] = g_signal_new (NM_SETTINGS_CONNECTION_UPDATED_INTERNAL, - G_TYPE_FROM_CLASS (class), + G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, @@ -3081,24 +3252,20 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class) signals[REMOVED] = g_signal_new (NM_SETTINGS_CONNECTION_REMOVED, - G_TYPE_FROM_CLASS (class), + G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (class), - NMDBUS_TYPE_SETTINGS_CONNECTION_SKELETON, - "Update", impl_settings_connection_update, - "UpdateUnsaved", impl_settings_connection_update_unsaved, - "Delete", impl_settings_connection_delete, - "GetSettings", impl_settings_connection_get_settings, - "GetSecrets", impl_settings_connection_get_secrets, - "ClearSecrets", impl_settings_connection_clear_secrets, - "Save", impl_settings_connection_save, - "Update2", impl_settings_connection_update2, - NULL); + signals[FLAGS_CHANGED] = + g_signal_new (NM_SETTINGS_CONNECTION_FLAGS_CHANGED, + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h index 29ec05dd..70518687 100644 --- a/src/settings/nm-settings-connection.h +++ b/src/settings/nm-settings-connection.h @@ -24,7 +24,7 @@ #include -#include "nm-exported-object.h" +#include "nm-dbus-object.h" #include "nm-connection.h" #define NM_TYPE_SETTINGS_CONNECTION (nm_settings_connection_get_type ()) @@ -34,14 +34,11 @@ #define NM_IS_SETTINGS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTINGS_CONNECTION)) #define NM_SETTINGS_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnectionClass)) -/* Signals */ -#define NM_SETTINGS_CONNECTION_UPDATED "updated" #define NM_SETTINGS_CONNECTION_REMOVED "removed" #define NM_SETTINGS_CONNECTION_GET_SECRETS "get-secrets" #define NM_SETTINGS_CONNECTION_CANCEL_SECRETS "cancel-secrets" - -/* Internal signals */ #define NM_SETTINGS_CONNECTION_UPDATED_INTERNAL "updated-internal" +#define NM_SETTINGS_CONNECTION_FLAGS_CHANGED "flags-changed" /* Properties */ #define NM_SETTINGS_CONNECTION_UNSAVED "unsaved" @@ -51,34 +48,46 @@ #define NM_SETTINGS_CONNECTION_FLAGS "flags" #define NM_SETTINGS_CONNECTION_FILENAME "filename" - /** - * NMSettingsConnectionFlags: - * @NM_SETTINGS_CONNECTION_FLAGS_NONE: no flag set - * @NM_SETTINGS_CONNECTION_FLAGS_UNSAVED: the connection is not saved to disk - * @NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED: A connection is "nm-generated" if + * NMSettingsConnectionIntFlags: + * @NM_SETTINGS_CONNECTION_INT_FLAGS_NONE: no flag set + * @NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED: the connection is not saved to disk. + * See also #NM_SETTINGS_CONNECTION_FLAG_UNSAVED. + * @NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED: A connection is "nm-generated" if * it was generated by NetworkManger. If the connection gets modified or saved * by the user, the flag gets cleared. A nm-generated is implicitly unsaved. - * @NM_SETTINGS_CONNECTION_FLAGS_VOLATILE: The connection will be deleted + * See also #NM_SETTINGS_CONNECTION_FLAG_NM_GENERATED. + * @NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE: The connection will be deleted * when it disconnects. That is for in-memory connections (unsaved), which are * currently active but cleanup on disconnect. - * @NM_SETTINGS_CONNECTION_FLAGS_VISIBLE: The connection is visible - * @NM_SETTINGS_CONNECTION_FLAGS_ALL: special mask, for all known flags + * See also #NM_SETTINGS_CONNECTION_FLAG_VOLATILE. + * @NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE: The connection is visible + * @NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK: the entire enum is + * internal, however, parts of it is public API as #NMSettingsConnectionFlags. + * This mask, are the public flags. + * @NM_SETTINGS_CONNECTION_INT_FLAGS_ALL: special mask, for all known flags * * #NMSettingsConnection flags. **/ typedef enum { - NM_SETTINGS_CONNECTION_FLAGS_NONE = 0, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE = 0, + + NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED = NM_SETTINGS_CONNECTION_FLAG_UNSAVED, + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED = NM_SETTINGS_CONNECTION_FLAG_NM_GENERATED, + NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE = NM_SETTINGS_CONNECTION_FLAG_VOLATILE, + + NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE = (1LL << 3), - NM_SETTINGS_CONNECTION_FLAGS_UNSAVED = (1LL << 0), - NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED = (1LL << 1), - NM_SETTINGS_CONNECTION_FLAGS_VOLATILE = (1LL << 2), + __NM_SETTINGS_CONNECTION_INT_FLAGS_LAST, - NM_SETTINGS_CONNECTION_FLAGS_VISIBLE = (1LL << 3), + NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK = 0 + | NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED + | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE + | 0, - __NM_SETTINGS_CONNECTION_FLAGS_LAST, - NM_SETTINGS_CONNECTION_FLAGS_ALL = ((__NM_SETTINGS_CONNECTION_FLAGS_LAST - 1) << 1) - 1, -} NMSettingsConnectionFlags; + NM_SETTINGS_CONNECTION_INT_FLAGS_ALL = ((__NM_SETTINGS_CONNECTION_INT_FLAGS_LAST - 1) << 1) - 1, +} NMSettingsConnectionIntFlags; typedef enum { NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE = 0, @@ -106,12 +115,13 @@ typedef struct _NMSettingsConnectionClass NMSettingsConnectionClass; struct _NMSettingsConnectionPrivate; struct _NMSettingsConnection { - NMExportedObject parent; + NMDBusObject parent; struct _NMSettingsConnectionPrivate *_priv; + CList _connections_lst; }; struct _NMSettingsConnectionClass { - NMExportedObjectClass parent; + NMDBusObjectClass parent; gboolean (*commit_changes) (NMSettingsConnection *self, NMConnection *new_connection, @@ -199,9 +209,9 @@ void nm_settings_connection_signal_remove (NMSettingsConnection *self); gboolean nm_settings_connection_get_unsaved (NMSettingsConnection *self); -NMSettingsConnectionFlags nm_settings_connection_get_flags (NMSettingsConnection *self); -NMSettingsConnectionFlags nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionFlags flags, gboolean set); -NMSettingsConnectionFlags nm_settings_connection_set_flags_full (NMSettingsConnection *self, NMSettingsConnectionFlags mask, NMSettingsConnectionFlags value); +NMSettingsConnectionIntFlags nm_settings_connection_get_flags (NMSettingsConnection *self); +NMSettingsConnectionIntFlags nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionIntFlags flags, gboolean set); +NMSettingsConnectionIntFlags nm_settings_connection_set_flags_full (NMSettingsConnection *self, NMSettingsConnectionIntFlags mask, NMSettingsConnectionIntFlags value); int nm_settings_connection_cmp_timestamp (NMSettingsConnection *ac, NMSettingsConnection *ab); int nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data); diff --git a/src/settings/nm-settings-plugin.c b/src/settings/nm-settings-plugin.c index 234e345f..f6dd2cfb 100644 --- a/src/settings/nm-settings-plugin.c +++ b/src/settings/nm-settings-plugin.c @@ -35,30 +35,6 @@ nm_settings_plugin_default_init (NMSettingsPluginInterface *g_iface) if (initialized) return; - /* Properties */ - g_object_interface_install_property - (g_iface, - g_param_spec_string (NM_SETTINGS_PLUGIN_NAME, "", "", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property - (g_iface, - g_param_spec_string (NM_SETTINGS_PLUGIN_INFO, "", "", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property - (g_iface, - g_param_spec_uint (NM_SETTINGS_PLUGIN_CAPABILITIES, "", "", - NM_SETTINGS_PLUGIN_CAP_NONE, - NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS, - NM_SETTINGS_PLUGIN_CAP_NONE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - /* Signals */ g_signal_new (NM_SETTINGS_PLUGIN_CONNECTION_ADDED, iface_type, diff --git a/src/settings/nm-settings-plugin.h b/src/settings/nm-settings-plugin.h index 97bb7b65..6fba25e7 100644 --- a/src/settings/nm-settings-plugin.h +++ b/src/settings/nm-settings-plugin.h @@ -34,33 +34,10 @@ GObject * nm_settings_plugin_factory (void); #define NM_IS_SETTINGS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTINGS_PLUGIN)) #define NM_SETTINGS_PLUGIN_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_SETTINGS_PLUGIN, NMSettingsPluginInterface)) - -#define NM_SETTINGS_PLUGIN_NAME "name" -#define NM_SETTINGS_PLUGIN_INFO "info" -#define NM_SETTINGS_PLUGIN_CAPABILITIES "capabilities" - #define NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED "unmanaged-specs-changed" #define NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED "unrecognized-specs-changed" #define NM_SETTINGS_PLUGIN_CONNECTION_ADDED "connection-added" -typedef enum { - NM_SETTINGS_PLUGIN_CAP_NONE = 0x00000000, - NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS = 0x00000001, - - /* When adding more capabilities, be sure to update the "Capabilities" - * property max value in nm-settings-plugin.c. - */ -} NMSettingsPluginCapabilities; - -typedef enum { - NM_SETTINGS_PLUGIN_PROP_FIRST = 0x1000, - - NM_SETTINGS_PLUGIN_PROP_NAME = NM_SETTINGS_PLUGIN_PROP_FIRST, - NM_SETTINGS_PLUGIN_PROP_INFO, - NM_SETTINGS_PLUGIN_PROP_CAPABILITIES, -} NMSettingsPluginProp; - - typedef struct _NMSettingsPlugin NMSettingsPlugin; typedef struct { diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 8e3fc582..94018734 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -62,10 +62,12 @@ #include "nm-utils.h" #include "nm-core-internal.h" +#include "nm-utils/nm-c-list.h" +#include "nm-dbus-object.h" #include "devices/nm-device-ethernet.h" #include "nm-settings-connection.h" #include "nm-settings-plugin.h" -#include "nm-bus-manager.h" +#include "nm-dbus-manager.h" #include "nm-auth-utils.h" #include "nm-auth-subject.h" #include "nm-session-monitor.h" @@ -77,8 +79,6 @@ #include "nm-dispatcher.h" #include "nm-hostname-manager.h" -#include "introspection/org.freedesktop.NetworkManager.Settings.h" - /*****************************************************************************/ #define EXPORT(sym) void * __export_##sym = &sym; @@ -107,7 +107,6 @@ enum { CONNECTION_UPDATED, CONNECTION_REMOVED, CONNECTION_FLAGS_CHANGED, - NEW_CONNECTION, /* exported, not used internally */ LAST_SIGNAL }; @@ -121,29 +120,33 @@ typedef struct { GSList *auths; GSList *plugins; - gboolean connections_loaded; - GHashTable *connections; + + CList connections_lst_head; + NMSettingsConnection **connections_cached_list; GSList *unmanaged_specs; GSList *unrecognized_specs; - gboolean started; - gboolean startup_complete; - NMHostnameManager *hostname_manager; + guint connections_len; + + bool started:1; + bool startup_complete:1; + bool connections_loaded:1; + } NMSettingsPrivate; struct _NMSettings { - NMExportedObject parent; + NMDBusObject parent; NMSettingsPrivate _priv; }; struct _NMSettingsClass { - NMExportedObjectClass parent; + NMDBusObjectClass parent; }; -G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_EXPORTED_OBJECT); +G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_DBUS_OBJECT); #define NM_SETTINGS_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSettings, NM_IS_SETTINGS) @@ -154,6 +157,10 @@ G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_EXPORTED_OBJECT); /*****************************************************************************/ +static const NMDBusInterfaceInfoExtended interface_info_settings; +static const GDBusSignalInfo signal_info_new_connection; +static const GDBusSignalInfo signal_info_connection_removed; + static void claim_connection (NMSettings *self, NMSettingsConnection *connection); @@ -164,27 +171,29 @@ static void connection_ready_changed (NMSettingsConnection *conn, GParamSpec *pspec, gpointer user_data); +static void default_wired_clear_tag (NMSettings *self, + NMDevice *device, + NMSettingsConnection *connection, + gboolean add_to_no_auto_default); + /*****************************************************************************/ static void check_startup_complete (NMSettings *self) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GHashTableIter iter; NMSettingsConnection *conn; if (priv->startup_complete) return; - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &conn)) { + c_list_for_each_entry (conn, &priv->connections_lst_head, _connections_lst) { if (!nm_settings_connection_get_ready (conn)) return; } /* the connection_ready_changed signal handler is no longer needed. */ - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &conn)) + c_list_for_each_entry (conn, &priv->connections_lst_head, _connections_lst) g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (connection_ready_changed), self); priv->startup_complete = TRUE; @@ -247,43 +256,25 @@ load_connections (NMSettings *self) unrecognized_specs_changed (NULL, self); } -void -nm_settings_for_each_connection (NMSettings *self, - NMSettingsForEachFunc for_each_func, - gpointer user_data) -{ - NMSettingsPrivate *priv; - GHashTableIter iter; - gpointer data; - - g_return_if_fail (NM_IS_SETTINGS (self)); - g_return_if_fail (for_each_func != NULL); - - priv = NM_SETTINGS_GET_PRIVATE (self); - - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, &data)) - for_each_func (self, NM_SETTINGS_CONNECTION (data), user_data); -} - static void -impl_settings_list_connections (NMSettings *self, - GDBusMethodInvocation *context) +impl_settings_list_connections (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *dbus_connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { + NMSettings *self = NM_SETTINGS (obj); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GPtrArray *connections; - GHashTableIter iter; - gpointer key; - - connections = g_ptr_array_sized_new (g_hash_table_size (priv->connections) + 1); - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, &key, NULL)) - g_ptr_array_add (connections, key); - g_ptr_array_add (connections, NULL); - - g_dbus_method_invocation_return_value (context, - g_variant_new ("(^ao)", connections->pdata)); - g_ptr_array_unref (connections); + gs_free const char **strv = NULL; + + strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head, + priv->connections_len, + G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst), + TRUE); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(^ao)", strv)); } NMSettingsConnection * @@ -291,16 +282,14 @@ nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid) { NMSettingsPrivate *priv; NMSettingsConnection *candidate; - GHashTableIter iter; g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); g_return_val_if_fail (uuid != NULL, NULL); priv = NM_SETTINGS_GET_PRIVATE (self); - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) { - if (g_strcmp0 (uuid, nm_settings_connection_get_uuid (candidate)) == 0) + c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) { + if (nm_streq (uuid, nm_settings_connection_get_uuid (candidate))) return candidate; } @@ -308,14 +297,21 @@ nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid) } static void -impl_settings_get_connection_by_uuid (NMSettings *self, - GDBusMethodInvocation *context, - const char *uuid) +impl_settings_get_connection_by_uuid (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *dbus_connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { + NMSettings *self = NM_SETTINGS (obj); NMSettingsConnection *connection = NULL; - NMAuthSubject *subject = NULL; + gs_unref_object NMAuthSubject *subject = NULL; GError *error = NULL; - char *error_desc = NULL; + const char *uuid; + + g_variant_get (parameters, "(&s)", &uuid); connection = nm_settings_get_connection_by_uuid (self, uuid); if (!connection) { @@ -325,7 +321,7 @@ impl_settings_get_connection_by_uuid (NMSettings *self, goto error; } - subject = nm_auth_subject_new_unix_process_from_context (context); + subject = nm_auth_subject_new_unix_process_from_context (invocation); if (!subject) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, @@ -333,26 +329,40 @@ impl_settings_get_connection_by_uuid (NMSettings *self, goto error; } - if (!nm_auth_is_subject_in_acl (NM_CONNECTION (connection), - subject, - &error_desc)) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - error_desc); - g_free (error_desc); + if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (connection), + subject, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + &error)) goto error; - } - g_clear_object (&subject); - g_dbus_method_invocation_return_value ( - context, - g_variant_new ("(o)", nm_connection_get_path (NM_CONNECTION (connection)))); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(o)", + nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)))); return; error: - g_assert (error); - g_dbus_method_invocation_take_error (context, error); - g_clear_object (&subject); + g_dbus_method_invocation_take_error (invocation, error); +} + +static void +_clear_connections_cached_list (NMSettingsPrivate *priv) +{ + if (!priv->connections_cached_list) + return; + + nm_assert (priv->connections_len == NM_PTRARRAY_LEN (priv->connections_cached_list)); + +#if NM_MORE_ASSERTS + /* set the pointer to a bogus value. This makes it more apparent + * if somebody has a reference to the cached list and still uses + * it. That is a bug, this code just tries to make it blow up + * more eagerly. */ + memset (priv->connections_cached_list, + 0xdeaddead, + sizeof (NMSettingsConnection *) * (priv->connections_len + 1)); +#endif + nm_clear_g_free (&priv->connections_cached_list); } /** @@ -370,37 +380,33 @@ error: NMSettingsConnection *const* nm_settings_get_connections (NMSettings *self, guint *out_len) { - GHashTableIter iter; NMSettingsPrivate *priv; - guint l, i; NMSettingsConnection **v; NMSettingsConnection *con; + guint i; g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); priv = NM_SETTINGS_GET_PRIVATE (self); - if (G_LIKELY (priv->connections_cached_list)) { - NM_SET_OUT (out_len, g_hash_table_size (priv->connections)); - return priv->connections_cached_list; - } + nm_assert (priv->connections_len == c_list_length (&priv->connections_lst_head)); - l = g_hash_table_size (priv->connections); + if (G_UNLIKELY (!priv->connections_cached_list)) { + v = g_new (NMSettingsConnection *, priv->connections_len + 1); - v = g_new (NMSettingsConnection *, (gsize) l + 1); + i = 0; + c_list_for_each_entry (con, &priv->connections_lst_head, _connections_lst) { + nm_assert (i < priv->connections_len); + v[i++] = con; + } + nm_assert (i == priv->connections_len); + v[i] = NULL; - i = 0; - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &con)) { - nm_assert (i < l); - v[i++] = con; + priv->connections_cached_list = v; } - nm_assert (i == l); - v[i] = NULL; - NM_SET_OUT (out_len, l); - priv->connections_cached_list = v; - return v; + NM_SET_OUT (out_len, priv->connections_len); + return priv->connections_cached_list; } /** @@ -467,28 +473,48 @@ NMSettingsConnection * nm_settings_get_connection_by_path (NMSettings *self, const char *path) { NMSettingsPrivate *priv; + NMSettingsConnection *connection; g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); - g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (path, NULL); priv = NM_SETTINGS_GET_PRIVATE (self); - return (NMSettingsConnection *) g_hash_table_lookup (priv->connections, path); + connection = (NMSettingsConnection *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)), + path); + if ( !connection + || !NM_IS_SETTINGS_CONNECTION (connection)) + return NULL; + + nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst)); + return connection; } gboolean nm_settings_has_connection (NMSettings *self, NMSettingsConnection *connection) { - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GHashTableIter iter; - gpointer data; + gboolean has; - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, &data)) - if (data == connection) - return TRUE; + g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE); + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE); - return FALSE; + has = !c_list_is_empty (&connection->_connections_lst); + + nm_assert (has == nm_c_list_contains_entry (&NM_SETTINGS_GET_PRIVATE (self)->connections_lst_head, + connection, + _connections_lst)); + nm_assert (({ + NMSettingsConnection *candidate = NULL; + const char *path; + + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)); + if (path) + candidate = nm_settings_get_connection_by_path (self, path); + + (has == (connection == candidate)); + })); + + return has; } const GSList * @@ -500,7 +526,7 @@ nm_settings_get_unmanaged_specs (NMSettings *self) } static NMSettingsPlugin * -get_plugin (NMSettings *self, guint32 capability) +get_plugin (NMSettings *self, gboolean has_add_connection) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); GSList *iter; @@ -509,11 +535,13 @@ get_plugin (NMSettings *self, guint32 capability) /* Do any of the plugins support the given capability? */ for (iter = priv->plugins; iter; iter = iter->next) { - NMSettingsPluginCapabilities caps = NM_SETTINGS_PLUGIN_CAP_NONE; + NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data); + + if (!has_add_connection) + return plugin; - g_object_get (G_OBJECT (iter->data), NM_SETTINGS_PLUGIN_CAPABILITIES, &caps, NULL); - if (NM_FLAGS_ALL (caps, capability)) - return NM_SETTINGS_PLUGIN (iter->data); + if (NM_SETTINGS_PLUGIN_GET_INTERFACE (iter->data)->add_connection != NULL) + return plugin; } return NULL; @@ -583,8 +611,6 @@ static gboolean add_plugin (NMSettings *self, NMSettingsPlugin *plugin) { NMSettingsPrivate *priv; - char *pname = NULL; - char *pinfo = NULL; const char *path; g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE); @@ -600,44 +626,102 @@ add_plugin (NMSettings *self, NMSettingsPlugin *plugin) priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin)); nm_settings_plugin_init (plugin); - g_object_get (G_OBJECT (plugin), - NM_SETTINGS_PLUGIN_NAME, &pname, - NM_SETTINGS_PLUGIN_INFO, &pinfo, - NULL); path = g_object_get_qdata (G_OBJECT (plugin), plugin_module_path_quark ()); - _LOGI ("loaded plugin %s: %s%s%s%s", pname, pinfo, - NM_PRINT_FMT_QUOTED (path, " (", path, ")", "")); - g_free (pname); - g_free (pinfo); + _LOGI ("Loaded settings plugin: %s (%s)", G_OBJECT_TYPE_NAME (plugin), path ?: "internal"); return TRUE; } -static GObject * -find_plugin (GSList *list, const char *pname) +static gboolean +plugin_loaded (GSList *list, const char *path) { GSList *iter; - GObject *obj = NULL; - g_return_val_if_fail (pname != NULL, NULL); + g_return_val_if_fail (path != NULL, TRUE); - for (iter = list; iter && !obj; iter = g_slist_next (iter)) { - NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data); - char *list_pname = NULL; + for (iter = list; iter; iter = g_slist_next (iter)) { + const char *list_path = g_object_get_qdata (G_OBJECT (iter->data), + plugin_module_path_quark ()); + + if (g_strcmp0 (path, list_path) == 0) + return TRUE; + } + + return FALSE; +} + +static gboolean +load_plugin (NMSettings *self, GSList *list, const char *pname, GError **error) +{ + gs_free char *full_name = NULL; + gs_free char *path = NULL; + gs_unref_object GObject *obj = NULL; + GModule *plugin; + GObject * (*factory_func) (void); + struct stat st; + int errsv; + + full_name = g_strdup_printf ("nm-settings-plugin-%s", pname); + path = g_module_build_path (NMPLUGINDIR, full_name); + + if (plugin_loaded (list, path)) + return TRUE; + + if (stat (path, &st) != 0) { + errsv = errno; + _LOGW ("could not load plugin '%s' from file '%s': %s", pname, path, strerror (errsv)); + return TRUE; + } + if (!S_ISREG (st.st_mode)) { + _LOGW ("could not load plugin '%s' from file '%s': not a file", pname, path); + return TRUE; + } + if (st.st_uid != 0) { + _LOGW ("could not load plugin '%s' from file '%s': file must be owned by root", pname, path); + return TRUE; + } + if (st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) { + _LOGW ("could not load plugin '%s' from file '%s': invalid file permissions", pname, path); + return TRUE; + } + + plugin = g_module_open (path, G_MODULE_BIND_LOCAL); + if (!plugin) { + _LOGW ("could not load plugin '%s' from file '%s': %s", + pname, path, g_module_error ()); + return TRUE; + } - g_object_get (G_OBJECT (plugin), - NM_SETTINGS_PLUGIN_NAME, - &list_pname, - NULL); - if (list_pname && !strcmp (pname, list_pname)) - obj = G_OBJECT (plugin); + /* errors after this point are fatal, because we loaded the shared library already. */ - g_free (list_pname); + if (!g_module_symbol (plugin, "nm_settings_plugin_factory", (gpointer) (&factory_func))) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Could not find plugin '%s' factory function.", + pname); + g_module_close (plugin); + return FALSE; } - return obj; + /* after accessing the plugin we cannot unload it anymore, because the glib + * types cannot be properly unregistered. */ + g_module_make_resident (plugin); + + obj = (*factory_func) (); + if (!obj || !NM_IS_SETTINGS_PLUGIN (obj)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Plugin '%s' returned invalid system config object.", + pname); + return FALSE; + } + + g_object_set_qdata_full (obj, plugin_module_path_quark (), path, g_free); + path = NULL; + if (add_plugin (self, NM_SETTINGS_PLUGIN (obj))) + list = g_slist_append (list, g_steal_pointer (&obj)); + + return TRUE; } static void @@ -670,15 +754,14 @@ load_plugins (NMSettings *self, const char **plugins, GError **error) for (iter = plugins; iter && *iter; iter++) { const char *pname = *iter; - GObject *obj; if (!*pname || strchr (pname, '/')) { _LOGW ("ignore invalid plugin \"%s\"", pname); continue; } - if (!strcmp (pname, "ifcfg-suse")) { - _LOGW ("skipping deprecated plugin ifcfg-suse"); + if (NM_IN_STRSET (pname, "ifcfg-suse", "ifnet")) { + _LOGW ("skipping deprecated plugin %s", pname); continue; } @@ -704,84 +787,19 @@ load_plugins (NMSettings *self, const char **plugins, GError **error) continue; } - if (find_plugin (list, pname)) - continue; - -load_plugin: - { - GModule *plugin; - gs_free char *full_name = NULL; - gs_free char *path = NULL; - GObject * (*factory_func) (void); - struct stat st; - int errsv; - - full_name = g_strdup_printf ("nm-settings-plugin-%s", pname); - path = g_module_build_path (NMPLUGINDIR, full_name); - - if (stat (path, &st) != 0) { - errsv = errno; - _LOGW ("could not load plugin '%s' from file '%s': %s", pname, path, strerror (errsv)); - goto next; - } - if (!S_ISREG (st.st_mode)) { - _LOGW ("could not load plugin '%s' from file '%s': not a file", pname, path); - goto next; - } - if (st.st_uid != 0) { - _LOGW ("could not load plugin '%s' from file '%s': file must be owned by root", pname, path); - goto next; - } - if (st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) { - _LOGW ("could not load plugin '%s' from file '%s': invalid file permissions", pname, path); - goto next; - } - - plugin = g_module_open (path, G_MODULE_BIND_LOCAL); - if (!plugin) { - _LOGW ("could not load plugin '%s' from file '%s': %s", - pname, path, g_module_error ()); - goto next; - } - - /* errors after this point are fatal, because we loaded the shared library already. */ + success = load_plugin (self, list, pname, error); + if (!success) + break; - if (!g_module_symbol (plugin, "nm_settings_plugin_factory", (gpointer) (&factory_func))) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not find plugin '%s' factory function.", - pname); - success = FALSE; - g_module_close (plugin); - break; - } - - /* after accessing the plugin we cannot unload it anymore, because the glib - * types cannot be properly unregistered. */ - g_module_make_resident (plugin); - - obj = (*factory_func) (); - if (!obj || !NM_IS_SETTINGS_PLUGIN (obj)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Plugin '%s' returned invalid system config object.", - pname); - success = FALSE; - break; - } - - g_object_set_qdata_full (obj, plugin_module_path_quark (), path, g_free); - path = NULL; - if (add_plugin (self, NM_SETTINGS_PLUGIN (obj))) - list = g_slist_append (list, obj); - else - g_object_unref (obj); - } -next: if (add_ibft && !strcmp (pname, "ifcfg-rh")) { /* The plugin ibft is not explicitly mentioned but we just enabled "ifcfg-rh". * Enable "ibft" by default after "ifcfg-rh". */ pname = "ibft"; add_ibft = FALSE; - goto load_plugin; + + success = load_plugin (self, list, "ibft", error); + if (!success) + break; } } @@ -806,7 +824,6 @@ connection_updated (NMSettingsConnection *connection, gboolean by_user, gpointer static void connection_flags_changed (NMSettingsConnection *connection, - GParamSpec *pspec, gpointer user_data) { g_signal_emit (NM_SETTINGS (user_data), @@ -820,11 +837,20 @@ connection_removed (NMSettingsConnection *connection, gpointer user_data) { NMSettings *self = NM_SETTINGS (user_data); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - const char *cpath = nm_connection_get_path (NM_CONNECTION (connection)); + NMDevice *device; - if (!g_hash_table_lookup (priv->connections, cpath)) - g_return_if_reached (); - g_object_ref (connection); + g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection)); + g_return_if_fail (!c_list_is_empty (&connection->_connections_lst)); + nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst)); + + /* When the default wired connection is removed (either deleted or saved to + * a new persistent connection by a plugin), write the MAC address of the + * wired device to the config file and don't create a new default wired + * connection for that device again. + */ + device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ()); + if (device) + default_wired_clear_tag (self, device, connection, TRUE); /* Disconnect signal handlers, as plugins might still keep references * to the connection (and thus the signal handlers would still be live) @@ -836,23 +862,30 @@ connection_removed (NMSettingsConnection *connection, gpointer user_data) g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_flags_changed), self); if (!priv->startup_complete) g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_ready_changed), self); - g_object_unref (self); /* Forget about the connection internally */ - g_hash_table_remove (priv->connections, (gpointer) cpath); - g_clear_pointer (&priv->connections_cached_list, g_free); + _clear_connections_cached_list (priv); + priv->connections_len--; + c_list_unlink (&connection->_connections_lst); + + if (priv->connections_loaded) { + _notify (self, PROP_CONNECTIONS); - /* Notify D-Bus */ - g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection); + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), + &interface_info_settings, + &signal_info_connection_removed, + "(o)", + nm_dbus_object_get_path (NM_DBUS_OBJECT (connection))); + } - /* Re-emit for listeners like NMPolicy */ - _notify (self, PROP_CONNECTIONS); - if (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (connection))) - nm_exported_object_unexport (NM_EXPORTED_OBJECT (connection)); + nm_dbus_object_unexport (NM_DBUS_OBJECT (connection)); - check_startup_complete (self); + if (priv->connections_loaded) + g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection); g_object_unref (connection); + + check_startup_complete (self); } #define NM_DBUS_SERVICE_OPENCONNECT "org.freedesktop.NetworkManager.openconnect" @@ -900,19 +933,16 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); GError *error = NULL; - GHashTableIter iter; - gpointer data; const char *path; NMSettingsConnection *existing; g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection)); - g_return_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL); + g_return_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (connection))); - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, &data)) { - /* prevent duplicates */ - if (data == connection) - return; + /* prevent duplicates */ + if (!c_list_is_empty (&connection->_connections_lst)) { + nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst)); + return; } if (!nm_connection_normalize (NM_CONNECTION (connection), NULL, NULL, &error)) { @@ -944,20 +974,19 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection) /* Read seen-bssids from look-aside file and put it into the connection's data */ nm_settings_connection_read_and_fill_seen_bssids (connection); - /* Ensure it's initial visibility is up-to-date */ + /* Ensure its initial visibility is up-to-date */ nm_settings_connection_recheck_visibility (connection); /* Evil openconnect migration hack */ openconnect_migrate_hack (NM_CONNECTION (connection)); - g_object_ref (self); /* This one unexports the connection, it needs to run late to give the active * connection a chance to deal with its reference to this settings connection. */ g_signal_connect_after (connection, NM_SETTINGS_CONNECTION_REMOVED, G_CALLBACK (connection_removed), self); g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL, G_CALLBACK (connection_updated), self); - g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_FLAGS, + g_signal_connect (connection, NM_SETTINGS_CONNECTION_FLAGS_CHANGED, G_CALLBACK (connection_flags_changed), self); if (!priv->startup_complete) { @@ -966,28 +995,29 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection) self); } - /* Export the connection over D-Bus */ - g_warn_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL); - path = nm_exported_object_export (NM_EXPORTED_OBJECT (connection)); - nm_connection_set_path (NM_CONNECTION (connection), path); + _clear_connections_cached_list (priv); + + g_object_ref (connection); + priv->connections_len++; + c_list_link_tail (&priv->connections_lst_head, &connection->_connections_lst); - g_hash_table_insert (priv->connections, - (gpointer) nm_connection_get_path (NM_CONNECTION (connection)), - g_object_ref (connection)); - g_clear_pointer (&priv->connections_cached_list, g_free); + path = nm_dbus_object_export (NM_DBUS_OBJECT (connection)); - nm_utils_log_connection_diff (NM_CONNECTION (connection), NULL, LOGL_DEBUG, LOGD_CORE, "new connection", "++ "); + nm_utils_log_connection_diff (NM_CONNECTION (connection), NULL, LOGL_DEBUG, LOGD_CORE, "new connection", "++ ", + path); /* Only emit the individual connection-added signal after connections * have been initially loaded. */ if (priv->connections_loaded) { - /* Internal added signal */ + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), + &interface_info_settings, + &signal_info_new_connection, + "(o)", + nm_dbus_object_get_path (NM_DBUS_OBJECT (connection))); + g_signal_emit (self, signals[CONNECTION_ADDED], 0, connection); _notify (self, PROP_CONNECTIONS); - - /* Exported D-Bus signal */ - g_signal_emit (self, signals[NEW_CONNECTION], 0, connection); } nm_settings_connection_added (connection); @@ -1035,14 +1065,14 @@ nm_settings_add_connection (NMSettings *self, NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); GSList *iter; NMSettingsConnection *added = NULL; - GHashTableIter citer; - NMConnection *candidate = NULL; + NMSettingsConnection *candidate = NULL; + const char *uuid; + + uuid = nm_connection_get_uuid (connection); /* Make sure a connection with this UUID doesn't already exist */ - g_hash_table_iter_init (&citer, priv->connections); - while (g_hash_table_iter_next (&citer, NULL, (gpointer *) &candidate)) { - if (g_strcmp0 (nm_connection_get_uuid (connection), - nm_connection_get_uuid (candidate)) == 0) { + c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) { + if (nm_streq0 (uuid, nm_connection_get_uuid (NM_CONNECTION (candidate)))) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_UUID_EXISTS, @@ -1099,7 +1129,7 @@ send_agent_owned_secrets (NMSettings *self, NMAuthSubject *subject) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - NMConnection *for_agent; + gs_unref_object NMConnection *for_agent = NULL; /* Dupe the connection so we can clear out non-agent-owned secrets, * as agent-owned secrets are the only ones we send back to be saved. @@ -1110,10 +1140,9 @@ send_agent_owned_secrets (NMSettings *self, secrets_filter_cb, GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); nm_agent_manager_save_secrets (priv->agent_mgr, - nm_connection_get_path (NM_CONNECTION (connection)), + nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)), for_agent, subject); - g_object_unref (for_agent); } static void @@ -1170,7 +1199,7 @@ pk_add_cb (NMAuthChain *chain, send_agent_owned_secrets (self, added, subject); g_clear_error (&error); - nm_auth_chain_unref (chain); + nm_auth_chain_destroy (chain); } /* FIXME: remove if/when kernel supports adhoc wpa */ @@ -1209,20 +1238,20 @@ void nm_settings_add_connection_dbus (NMSettings *self, NMConnection *connection, gboolean save_to_disk, + NMAuthSubject *subject, GDBusMethodInvocation *context, NMSettingsAddCallback callback, gpointer user_data) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); NMSettingConnection *s_con; - NMAuthSubject *subject = NULL; NMAuthChain *chain; GError *error = NULL, *tmp_error = NULL; - char *error_desc = NULL; const char *perm; - g_return_if_fail (connection != NULL); - g_return_if_fail (context != NULL); + g_return_if_fail (NM_IS_CONNECTION (connection)); + g_return_if_fail (NM_IS_AUTH_SUBJECT (subject)); + g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (context)); /* Connection must be valid, of course */ if (!nm_connection_verify (connection, &tmp_error)) { @@ -1246,33 +1275,19 @@ nm_settings_add_connection_dbus (NMSettings *self, } /* Do any of the plugins support adding? */ - if (!get_plugin (self, NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS)) { + if (!get_plugin (self, TRUE)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, "None of the registered plugins support add."); goto done; } - subject = nm_auth_subject_new_unix_process_from_context (context); - if (!subject) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - "Unable to determine UID of request."); + if (!nm_auth_is_subject_in_acl_set_error (connection, + subject, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + &error)) goto done; - } - - /* Ensure the caller's username exists in the connection's permissions, - * or that the permissions is empty (ie, visible by everyone). - */ - if (!nm_auth_is_subject_in_acl (connection, - subject, - &error_desc)) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - error_desc); - g_free (error_desc); - goto done; - } /* If the caller is the only user in the connection's permissions, then * we use the 'modify.own' permission instead of 'modify.system'. If the @@ -1295,50 +1310,50 @@ nm_settings_add_connection_dbus (NMSettings *self, } priv->auths = g_slist_append (priv->auths, chain); - nm_auth_chain_add_call (chain, perm, TRUE); nm_auth_chain_set_data (chain, "perm", (gpointer) perm, NULL); nm_auth_chain_set_data (chain, "connection", g_object_ref (connection), g_object_unref); nm_auth_chain_set_data (chain, "callback", callback, NULL); nm_auth_chain_set_data (chain, "callback-data", user_data, NULL); nm_auth_chain_set_data (chain, "subject", g_object_ref (subject), g_object_unref); nm_auth_chain_set_data (chain, "save-to-disk", GUINT_TO_POINTER (save_to_disk), NULL); + nm_auth_chain_add_call (chain, perm, TRUE); + return; done: - if (error) - callback (self, NULL, error, context, subject, user_data); - - g_clear_error (&error); - g_clear_object (&subject); + nm_assert (error); + callback (self, NULL, error, context, subject, user_data); + g_error_free (error); } static void -impl_settings_add_connection_add_cb (NMSettings *self, - NMSettingsConnection *connection, - GError *error, - GDBusMethodInvocation *context, - NMAuthSubject *subject, - gpointer user_data) +settings_add_connection_add_cb (NMSettings *self, + NMSettingsConnection *connection, + GError *error, + GDBusMethodInvocation *context, + NMAuthSubject *subject, + gpointer user_data) { if (error) { g_dbus_method_invocation_return_gerror (context, error); nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, NULL, FALSE, NULL, subject, error->message); } else { - g_dbus_method_invocation_return_value ( - context, - g_variant_new ("(o)", nm_connection_get_path (NM_CONNECTION (connection)))); + g_dbus_method_invocation_return_value (context, + g_variant_new ("(o)", + nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)))); nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, connection, TRUE, NULL, subject, NULL); } } static void -impl_settings_add_connection_helper (NMSettings *self, - GDBusMethodInvocation *context, - GVariant *settings, - gboolean save_to_disk) +settings_add_connection_helper (NMSettings *self, + GDBusMethodInvocation *context, + GVariant *settings, + gboolean save_to_disk) { gs_unref_object NMConnection *connection = NULL; GError *error = NULL; + gs_unref_object NMAuthSubject *subject = NULL; connection = _nm_simple_connection_new_from_dbus (settings, NM_SETTING_PARSE_FLAGS_STRICT @@ -1351,81 +1366,125 @@ impl_settings_add_connection_helper (NMSettings *self, return; } + subject = nm_auth_subject_new_unix_process_from_context (context); + if (!subject) { + g_dbus_method_invocation_return_error_literal (context, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + "Unable to determine UID of request."); + return; + } + nm_settings_add_connection_dbus (self, connection, save_to_disk, + subject, context, - impl_settings_add_connection_add_cb, + settings_add_connection_add_cb, NULL); } static void -impl_settings_add_connection (NMSettings *self, - GDBusMethodInvocation *context, - GVariant *settings) +impl_settings_add_connection (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { - impl_settings_add_connection_helper (self, context, settings, TRUE); + NMSettings *self = NM_SETTINGS (obj); + gs_unref_variant GVariant *settings = NULL; + + g_variant_get (parameters, "(@a{sa{sv}})", &settings); + settings_add_connection_helper (self, invocation, settings, TRUE); } static void -impl_settings_add_connection_unsaved (NMSettings *self, - GDBusMethodInvocation *context, - GVariant *settings) +impl_settings_add_connection_unsaved (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { - impl_settings_add_connection_helper (self, context, settings, FALSE); + NMSettings *self = NM_SETTINGS (obj); + gs_unref_variant GVariant *settings = NULL; + + g_variant_get (parameters, "(@a{sa{sv}})", &settings); + settings_add_connection_helper (self, invocation, settings, FALSE); } static void -impl_settings_load_connections (NMSettings *self, - GDBusMethodInvocation *context, - char **filenames) +impl_settings_load_connections (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { + NMSettings *self = NM_SETTINGS (obj); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GPtrArray *failures; + gs_unref_ptrarray GPtrArray *failures = NULL; GSList *iter; - int i; + guint i; + gs_free const char **filenames = NULL; + + g_variant_get (parameters, "(^a&s)", &filenames); /* The permission is already enforced by the D-Bus daemon, but we ensure * that the caller is still alive so that clients are forced to wait and * we'll be able to switch to polkit without breaking behavior. */ - if (!nm_bus_manager_ensure_uid (nm_bus_manager_get (), - context, - G_MAXULONG, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED)) + if (!nm_dbus_manager_ensure_uid (nm_dbus_object_get_manager (obj), + invocation, + G_MAXULONG, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED)) return; - failures = g_ptr_array_new (); + if (filenames) { + for (i = 0; filenames[i]; i++) { + for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { + NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data); - for (i = 0; filenames[i]; i++) { - for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { - NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data); - - if (nm_settings_plugin_load_connection (plugin, filenames[i])) - break; - } + if (nm_settings_plugin_load_connection (plugin, filenames[i])) + break; + } - if (!iter) { - if (!g_path_is_absolute (filenames[i])) - _LOGW ("connection filename '%s' is not an absolute path", filenames[i]); - g_ptr_array_add (failures, (char *) filenames[i]); + if (!iter) { + if (!g_path_is_absolute (filenames[i])) + _LOGW ("connection filename '%s' is not an absolute path", filenames[i]); + if (!failures) + failures = g_ptr_array_new (); + g_ptr_array_add (failures, (char *) filenames[i]); + } } } - g_ptr_array_add (failures, NULL); - g_dbus_method_invocation_return_value ( - context, - g_variant_new ("(b^as)", - failures->len == 1, - failures->pdata)); - g_ptr_array_unref (failures); + if (failures) + g_ptr_array_add (failures, NULL); + + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(b^as)", + (gboolean) (!!failures), + failures + ? (const char **) failures->pdata + : NM_PTRARRAY_EMPTY (const char *))); } static void -impl_settings_reload_connections (NMSettings *self, - GDBusMethodInvocation *context) +impl_settings_reload_connections (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { + NMSettings *self = NM_SETTINGS (obj); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); GSList *iter; @@ -1433,11 +1492,11 @@ impl_settings_reload_connections (NMSettings *self, * that the caller is still alive so that clients are forced to wait and * we'll be able to switch to polkit without breaking behavior. */ - if (!nm_bus_manager_ensure_uid (nm_bus_manager_get (), - context, - G_MAXULONG, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED)) + if (!nm_dbus_manager_ensure_uid (nm_dbus_object_get_manager (obj), + invocation, + G_MAXULONG, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED)) return; for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { @@ -1446,7 +1505,7 @@ impl_settings_reload_connections (NMSettings *self, nm_settings_plugin_reload_connections (plugin); } - g_dbus_method_invocation_return_value (context, g_variant_new ("(b)", TRUE)); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE)); } /*****************************************************************************/ @@ -1494,41 +1553,46 @@ pk_hostname_cb (NMAuthChain *chain, else g_dbus_method_invocation_return_value (context, NULL); - nm_auth_chain_unref (chain); + nm_auth_chain_destroy (chain); } static void -impl_settings_save_hostname (NMSettings *self, - GDBusMethodInvocation *context, - const char *hostname) +impl_settings_save_hostname (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) { + NMSettings *self = NM_SETTINGS (obj); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); NMAuthChain *chain; - GError *error = NULL; + const char *hostname; + + g_variant_get (parameters, "(&s)", &hostname); /* Minimal validation of the hostname */ if (!nm_hostname_manager_validate_hostname (hostname)) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_INVALID_HOSTNAME, - "The hostname was too long or contained invalid characters."); - goto done; + g_dbus_method_invocation_return_error_literal (invocation, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_HOSTNAME, + "The hostname was too long or contained invalid characters."); + return; } - chain = nm_auth_chain_new_context (context, pk_hostname_cb, self); + chain = nm_auth_chain_new_context (invocation, pk_hostname_cb, self); if (!chain) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - "Unable to authenticate the request."); - goto done; + g_dbus_method_invocation_return_error_literal (invocation, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + "Unable to authenticate the request."); + return; } priv->auths = g_slist_append (priv->auths, chain); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, TRUE); nm_auth_chain_set_data (chain, "hostname", g_strdup (hostname), g_free); - -done: - if (error) - g_dbus_method_invocation_take_error (context, error); } /*****************************************************************************/ @@ -1537,27 +1601,24 @@ static gboolean have_connection_for_device (NMSettings *self, NMDevice *device) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GHashTableIter iter; - gpointer data; NMSettingConnection *s_con; NMSettingWired *s_wired; const char *setting_hwaddr; const char *perm_hw_addr; + NMSettingsConnection *connection; g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE); perm_hw_addr = nm_device_get_permanent_hw_address (device); /* Find a wired connection locked to the given MAC address, if any */ - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, &data)) { - NMConnection *connection = NM_CONNECTION (data); + c_list_for_each_entry (connection, &priv->connections_lst_head, _connections_lst) { const char *ctype, *iface; - if (!nm_device_check_connection_compatible (device, connection)) + if (!nm_device_check_connection_compatible (device, NM_CONNECTION (connection))) continue; - s_con = nm_connection_get_setting_connection (connection); + s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection)); iface = nm_setting_connection_get_interface_name (s_con); if (iface && strcmp (iface, nm_device_get_iface (device)) != 0) @@ -1568,7 +1629,7 @@ have_connection_for_device (NMSettings *self, NMDevice *device) && strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME)) continue; - s_wired = nm_connection_get_setting_wired (connection); + s_wired = nm_connection_get_setting_wired (NM_CONNECTION (connection)); if (!s_wired && !strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME)) { /* No wired setting; therefore the PPPoE connection applies to any device */ @@ -1596,26 +1657,6 @@ have_connection_for_device (NMSettings *self, NMDevice *device) return FALSE; } -static void default_wired_clear_tag (NMSettings *self, - NMDevice *device, - NMSettingsConnection *connection, - gboolean add_to_no_auto_default); - -static void -default_wired_connection_removed_cb (NMSettingsConnection *connection, NMSettings *self) -{ - NMDevice *device; - - /* When the default wired connection is removed (either deleted or saved to - * a new persistent connection by a plugin), write the MAC address of the - * wired device to the config file and don't create a new default wired - * connection for that device again. - */ - device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ()); - if (device) - default_wired_clear_tag (self, device, connection, TRUE); -} - static void default_wired_connection_updated_by_user_cb (NMSettingsConnection *connection, gboolean by_user, NMSettings *self) { @@ -1648,7 +1689,6 @@ default_wired_clear_tag (NMSettings *self, g_object_set_qdata (G_OBJECT (connection), _default_wired_device_quark (), NULL); g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), NULL); - g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_removed_cb), self); g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_updated_by_user_cb), self); if (add_to_no_auto_default) @@ -1700,8 +1740,6 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self) g_signal_connect (added, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL, G_CALLBACK (default_wired_connection_updated_by_user_cb), self); - g_signal_connect (added, NM_SETTINGS_CONNECTION_REMOVED, - G_CALLBACK (default_wired_connection_removed_cb), self); _LOGI ("(%s): created default wired connection '%s'", nm_device_get_iface (device), @@ -1774,10 +1812,8 @@ nm_settings_start (NMSettings *self, GError **error) /* Load the plugins; fail if a plugin is not found. */ plugins = nm_config_data_get_plugins (nm_config_get_data_orig (priv->config), TRUE); - if (!load_plugins (self, (const char **) plugins, error)) { - g_object_unref (self); + if (!load_plugins (self, (const char **) plugins, error)) return FALSE; - } load_connections (self); check_startup_complete (self); @@ -1802,18 +1838,19 @@ get_property (GObject *object, guint prop_id, NMSettings *self = NM_SETTINGS (object); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); const GSList *specs, *iter; - GHashTableIter citer; - GPtrArray *array; - const char *path; + guint i; + char **strvs; + const char **strv; switch (prop_id) { case PROP_UNMANAGED_SPECS: - array = g_ptr_array_new (); specs = nm_settings_get_unmanaged_specs (self); - for (iter = specs; iter; iter = g_slist_next (iter)) - g_ptr_array_add (array, g_strdup (iter->data)); - g_ptr_array_add (array, NULL); - g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE)); + strvs = g_new (char *, g_slist_length ((GSList *) specs) + 1); + i = 0; + for (iter = specs; iter; iter = iter->next) + strvs[i++] = g_strdup (iter->data); + strvs[i] = NULL; + g_value_take_boxed (value, strvs); break; case PROP_HOSTNAME: g_value_set_string (value, @@ -1822,15 +1859,17 @@ get_property (GObject *object, guint prop_id, : NULL); break; case PROP_CAN_MODIFY: - g_value_set_boolean (value, !!get_plugin (self, NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS)); + g_value_set_boolean (value, !!get_plugin (self, TRUE)); break; case PROP_CONNECTIONS: - array = g_ptr_array_sized_new (g_hash_table_size (priv->connections) + 1); - g_hash_table_iter_init (&citer, priv->connections); - while (g_hash_table_iter_next (&citer, (gpointer) &path, NULL)) - g_ptr_array_add (array, g_strdup (path)); - g_ptr_array_add (array, NULL); - g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE)); + if (priv->connections_loaded) { + strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head, + priv->connections_len, + G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst), + TRUE); + g_value_take_boxed (value, nm_utils_strv_make_deep_copied (strv)); + } else + g_value_set_boxed (value, NULL); break; case PROP_STARTUP_COMPLETE: g_value_set_boolean (value, nm_settings_get_startup_complete (self)); @@ -1848,7 +1887,7 @@ nm_settings_init (NMSettings *self) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref); + c_list_init (&priv->connections_lst_head); priv->agent_mgr = g_object_ref (nm_agent_manager_get ()); priv->config = g_object_ref (nm_config_get ()); @@ -1866,11 +1905,9 @@ dispose (GObject *object) NMSettings *self = NM_SETTINGS (object); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - g_slist_free_full (priv->auths, (GDestroyNotify) nm_auth_chain_unref); + g_slist_free_full (priv->auths, (GDestroyNotify) nm_auth_chain_destroy); priv->auths = NULL; - g_object_unref (priv->agent_mgr); - if (priv->hostname_manager) { g_signal_handlers_disconnect_by_func (priv->hostname_manager, G_CALLBACK (_hostname_changed_cb), @@ -1887,26 +1924,139 @@ finalize (GObject *object) NMSettings *self = NM_SETTINGS (object); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - g_hash_table_destroy (priv->connections); - g_clear_pointer (&priv->connections_cached_list, g_free); + _clear_connections_cached_list (priv); + + nm_assert (c_list_is_empty (&priv->connections_lst_head)); g_slist_free_full (priv->unmanaged_specs, g_free); g_slist_free_full (priv->unrecognized_specs, g_free); g_slist_free_full (priv->plugins, g_object_unref); + g_clear_object (&priv->agent_mgr); + g_clear_object (&priv->config); G_OBJECT_CLASS (nm_settings_parent_class)->finalize (object); } +static const GDBusSignalInfo signal_info_new_connection = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "NewConnection", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("connection", "o"), + ), +); + +static const GDBusSignalInfo signal_info_connection_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "ConnectionRemoved", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("connection", "o"), + ), +); + +static const NMDBusInterfaceInfoExtended interface_info_settings = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_SETTINGS, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "ListConnections", + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("connections", "ao"), + ), + ), + .handle = impl_settings_list_connections, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "GetConnectionByUuid", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("uuid", "s"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("connection", "o"), + ), + ), + .handle = impl_settings_get_connection_by_uuid, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "AddConnection", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("path", "o"), + ), + ), + .handle = impl_settings_add_connection, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "AddConnectionUnsaved", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("path", "o"), + ), + ), + .handle = impl_settings_add_connection_unsaved, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "LoadConnections", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("filenames", "as"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("status", "b"), + NM_DEFINE_GDBUS_ARG_INFO ("failures", "as"), + ), + ), + .handle = impl_settings_load_connections, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "ReloadConnections", + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("status", "b"), + ), + ), + .handle = impl_settings_reload_connections, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "SaveHostname", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("hostname", "s"), + ), + ), + .handle = impl_settings_save_hostname, + ), + ), + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + &signal_info_new_connection, + &signal_info_connection_removed, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Connections", "ao", NM_SETTINGS_CONNECTIONS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Hostname", "s", NM_SETTINGS_HOSTNAME), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("CanModify", "b", NM_SETTINGS_CAN_MODIFY), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_settings_class_init (NMSettingsClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); - NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (class); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (class); - exported_object_class->export_path = NM_DBUS_PATH_SETTINGS; + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_STATIC (NM_DBUS_PATH_SETTINGS); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_settings); object_class->get_property = get_property; object_class->dispose = dispose; @@ -1975,23 +2125,4 @@ nm_settings_class_init (NMSettingsClass *class) 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION); - - signals[NEW_CONNECTION] = - g_signal_new ("new-connection", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, 0, NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (class), - NMDBUS_TYPE_SETTINGS_SKELETON, - "ListConnections", impl_settings_list_connections, - "GetConnectionByUuid", impl_settings_get_connection_by_uuid, - "AddConnection", impl_settings_add_connection, - "AddConnectionUnsaved", impl_settings_add_connection_unsaved, - "LoadConnections", impl_settings_load_connections, - "ReloadConnections", impl_settings_reload_connections, - "SaveHostname", impl_settings_save_hostname, - NULL); } - diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h index 0ecffb70..021512fd 100644 --- a/src/settings/nm-settings.h +++ b/src/settings/nm-settings.h @@ -28,8 +28,6 @@ #include "nm-connection.h" -#include "nm-exported-object.h" - #define NM_TYPE_SETTINGS (nm_settings_get_type ()) #define NM_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS, NMSettings)) #define NM_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTINGS, NMSettingsClass)) @@ -72,14 +70,6 @@ NMSettings *nm_settings_get (void); NMSettings *nm_settings_new (void); gboolean nm_settings_start (NMSettings *self, GError **error); -typedef void (*NMSettingsForEachFunc) (NMSettings *settings, - NMSettingsConnection *connection, - gpointer user_data); - -void nm_settings_for_each_connection (NMSettings *settings, - NMSettingsForEachFunc for_each_func, - gpointer user_data); - typedef void (*NMSettingsAddCallback) (NMSettings *settings, NMSettingsConnection *connection, GError *error, @@ -90,6 +80,7 @@ typedef void (*NMSettingsAddCallback) (NMSettings *settings, void nm_settings_add_connection_dbus (NMSettings *self, NMConnection *connection, gboolean save_to_disk, + NMAuthSubject *subject, GDBusMethodInvocation *context, NMSettingsAddCallback callback, gpointer user_data); diff --git a/src/settings/plugins/ibft/meson.build b/src/settings/plugins/ibft/meson.build new file mode 100644 index 00000000..1b6ff28a --- /dev/null +++ b/src/settings/plugins/ibft/meson.build @@ -0,0 +1,48 @@ +name = 'nm-settings-plugin-ibft' + +cflags = [ + '-DSBINDIR="@0@"'.format(nm_sbindir), + '-DSYSCONFDIR="@0@"'.format(nm_sysconfdir) +] + +libnms_ibft_core = static_library( + 'nms-ibft-core', + 'nms-ibft-reader.c', + dependencies: nm_dep, + c_args: cflags +) + +sources = files( + 'nms-ibft-connection.c', + 'nms-ibft-plugin.c' +) + +libnm_settings_plugin_ibft = shared_module( + name, + sources: sources, + dependencies: nm_dep, + c_args: cflags, + link_with: libnms_ibft_core, + link_args: ldflags_linker_script_settings, + link_depends: linker_script_settings, + install: true, + install_dir: nm_plugindir +) + +core_plugins += libnm_settings_plugin_ibft + +# FIXME: check_so_symbols replacement +''' +run_target( + 'check-local-symbols-settings-ibft', + command: [check_so_symbols, libnm_settings_plugin_ibft.full_path()], + depends: libnm_settings_plugin_ibft +) + +check-local-symbols-settings-ibft: src/settings/plugins/ibft/libnm-settings-plugin-ibft.la + $(call check_so_symbols,$(builddir)/src/settings/plugins/ibft/.libs/libnm-settings-plugin-ibft.so) +''' + +if enable_tests + subdir('tests') +endif diff --git a/src/settings/plugins/ibft/nms-ibft-plugin.c b/src/settings/plugins/ibft/nms-ibft-plugin.c index ea4d1524..77ce208f 100644 --- a/src/settings/plugins/ibft/nms-ibft-plugin.c +++ b/src/settings/plugins/ibft/nms-ibft-plugin.c @@ -120,28 +120,6 @@ get_connections (NMSettingsPlugin *config) /*****************************************************************************/ -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - switch (prop_id) { - case NM_SETTINGS_PLUGIN_PROP_NAME: - g_value_set_string (value, "iBFT"); - break; - case NM_SETTINGS_PLUGIN_PROP_INFO: - g_value_set_string (value, "(c) 2014 Red Hat, Inc. To report bugs please use the NetworkManager mailing list."); - break; - case NM_SETTINGS_PLUGIN_PROP_CAPABILITIES: - g_value_set_uint (value, NM_SETTINGS_PLUGIN_CAP_NONE); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/*****************************************************************************/ - static void init (NMSettingsPlugin *config) { @@ -175,19 +153,6 @@ nms_ibft_plugin_class_init (NMSIbftPluginClass *req_class) GObjectClass *object_class = G_OBJECT_CLASS (req_class); object_class->dispose = dispose; - object_class->get_property = get_property; - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_NAME, - NM_SETTINGS_PLUGIN_NAME); - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_INFO, - NM_SETTINGS_PLUGIN_INFO); - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_CAPABILITIES, - NM_SETTINGS_PLUGIN_CAPABILITIES); } static void diff --git a/src/settings/plugins/ibft/nms-ibft-reader.c b/src/settings/plugins/ibft/nms-ibft-reader.c index d26c8334..cf849e28 100644 --- a/src/settings/plugins/ibft/nms-ibft-reader.c +++ b/src/settings/plugins/ibft/nms-ibft-reader.c @@ -128,7 +128,7 @@ nms_ibft_reader_load_blocks (const char *iscsiadm_path, } g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "iBFT: %s exited with error %d. Message: '%s'", - iscsiadm_path, WEXITSTATUS (status), err ? err : "(none)"); + iscsiadm_path, WEXITSTATUS (status), err ?: "(none)"); goto done; } @@ -392,15 +392,15 @@ connection_setting_add (const GPtrArray *block, id = g_strdup_printf ("iBFT%s%s %s", prefix ? " " : "", - prefix ? prefix : "", + prefix ?: "", iface); uuid = _nm_utils_uuid_generate_from_strings ("ibft", s_hwaddr, s_vlanid ? "V" : "v", - s_vlanid ? s_vlanid : "", + s_vlanid ?: "", s_ip4addr ? "A" : "DHCP", - s_ip4addr ? s_ip4addr : "", + s_ip4addr ?: "", NULL); s_con = nm_setting_connection_new (); diff --git a/src/settings/plugins/ibft/tests/meson.build b/src/settings/plugins/ibft/tests/meson.build new file mode 100644 index 00000000..59f49c31 --- /dev/null +++ b/src/settings/plugins/ibft/tests/meson.build @@ -0,0 +1,17 @@ +test_unit = 'test-ibft' + +test_ibft_dir = meson.current_source_dir() + +exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, + c_args: nm_build_cflags, + link_with: libnms_ibft_core +) + +test( + 'ibft/' + test_unit, + test_script, + args: test_args + [exe.full_path()] +) diff --git a/src/settings/plugins/ibft/tests/test-ibft.c b/src/settings/plugins/ibft/tests/test-ibft.c index 72d1a7db..5e46be2e 100644 --- a/src/settings/plugins/ibft/tests/test-ibft.c +++ b/src/settings/plugins/ibft/tests/test-ibft.c @@ -36,6 +36,8 @@ #include "nm-test-utils-core.h" +#define TEST_DIR NM_BUILD_SRCDIR"/src/settings/plugins/ibft/tests" + static GPtrArray * read_block (const char *iscsiadm_path, const char *expected_mac) { @@ -78,7 +80,7 @@ test_read_ibft_dhcp (void) const char *expected_mac_address = "00:33:21:98:b9:f1"; GPtrArray *block; - block = read_block (TEST_IBFT_DIR "/iscsiadm-test-dhcp", expected_mac_address); + block = read_block (TEST_DIR "/iscsiadm-test-dhcp", expected_mac_address); connection = nms_ibft_reader_get_connection_from_block (block, &error); g_assert_no_error (error); @@ -125,7 +127,7 @@ test_read_ibft_static (void) NMIPAddress *ip4_addr; GPtrArray *block; - block = read_block (TEST_IBFT_DIR "/iscsiadm-test-static", expected_mac_address); + block = read_block (TEST_DIR "/iscsiadm-test-static", expected_mac_address); connection = nms_ibft_reader_get_connection_from_block (block, &error); g_assert_no_error (error); @@ -181,7 +183,7 @@ test_read_ibft_malformed (gconstpointer user_data) g_assert (g_file_test (iscsiadm_path, G_FILE_TEST_EXISTS)); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*malformed iscsiadm record*"); + NMTST_EXPECT_NM_WARN ("*malformed iscsiadm record*"); success = nms_ibft_reader_load_blocks (iscsiadm_path, &blocks, &error); g_assert_no_error (error); @@ -227,7 +229,7 @@ test_read_ibft_vlan (void) GError *error = NULL; GPtrArray *block; - block = read_block (TEST_IBFT_DIR "/iscsiadm-test-vlan", expected_mac_address); + block = read_block (TEST_DIR "/iscsiadm-test-vlan", expected_mac_address); connection = nms_ibft_reader_get_connection_from_block (block, &error); g_assert_no_error (error); @@ -280,12 +282,12 @@ int main (int argc, char **argv) g_test_add_func (TPATH "ibft/dhcp", test_read_ibft_dhcp); g_test_add_func (TPATH "ibft/static", test_read_ibft_static); g_test_add_func (TPATH "ibft/vlan", test_read_ibft_vlan); - g_test_add_data_func (TPATH "ibft/bad-record-read", TEST_IBFT_DIR "/iscsiadm-test-bad-record", test_read_ibft_malformed); - g_test_add_data_func (TPATH "ibft/bad-entry-read", TEST_IBFT_DIR "/iscsiadm-test-bad-entry", test_read_ibft_malformed); - g_test_add_data_func (TPATH "ibft/bad-ipaddr-read", TEST_IBFT_DIR "/iscsiadm-test-bad-ipaddr", test_read_ibft_bad_address); - g_test_add_data_func (TPATH "ibft/bad-gateway-read", TEST_IBFT_DIR "/iscsiadm-test-bad-gateway", test_read_ibft_bad_address); - g_test_add_data_func (TPATH "ibft/bad-dns1-read", TEST_IBFT_DIR "/iscsiadm-test-bad-dns1", test_read_ibft_bad_address); - g_test_add_data_func (TPATH "ibft/bad-dns2-read", TEST_IBFT_DIR "/iscsiadm-test-bad-dns2", test_read_ibft_bad_address); + g_test_add_data_func (TPATH "ibft/bad-record-read", TEST_DIR "/iscsiadm-test-bad-record", test_read_ibft_malformed); + g_test_add_data_func (TPATH "ibft/bad-entry-read", TEST_DIR "/iscsiadm-test-bad-entry", test_read_ibft_malformed); + g_test_add_data_func (TPATH "ibft/bad-ipaddr-read", TEST_DIR "/iscsiadm-test-bad-ipaddr", test_read_ibft_bad_address); + g_test_add_data_func (TPATH "ibft/bad-gateway-read", TEST_DIR "/iscsiadm-test-bad-gateway", test_read_ibft_bad_address); + g_test_add_data_func (TPATH "ibft/bad-dns1-read", TEST_DIR "/iscsiadm-test-bad-dns1", test_read_ibft_bad_address); + g_test_add_data_func (TPATH "ibft/bad-dns2-read", TEST_DIR "/iscsiadm-test-bad-dns2", test_read_ibft_bad_address); return g_test_run (); } diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build new file mode 100644 index 00000000..964ce22d --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/meson.build @@ -0,0 +1,77 @@ +install_data( + 'nm-ifcfg-rh.conf', + install_dir: dbus_conf_dir +) + +cflags = [ + '-DSBINDIR="@0@"'.format(nm_sbindir), + '-DSYSCONFDIR="@0@"'.format(nm_sysconfdir) +] + +name = 'nmdbus-ifcfg-rh' + +dbus_sources = gnome.gdbus_codegen( + name, + 'nm-ifcfg-rh.xml', + interface_prefix: 'com.redhat', + namespace: 'NMDBus' +) + +libnmdbus_ifcfg_rh = static_library( + name, + sources: dbus_sources, + dependencies: glib_dep, + c_args: cflags +) + +sources = files( + 'nm-inotify-helper.c', + 'nms-ifcfg-rh-reader.c', + 'nms-ifcfg-rh-utils.c', + 'nms-ifcfg-rh-writer.c', + 'shvar.c' +) + +deps = [ + crypto_dep, + nm_dep +] + +libnms_ifcfg_rh_core = static_library( + 'nms-ifcfg-rh-core', + sources: sources, + dependencies: deps, + c_args: cflags +) + +sources = [dbus_sources] + files('nms-ifcfg-rh-connection.c') + +libnm_settings_plugin_ifcfg_rh = shared_module( + 'nm-settings-plugin-ifcfg-rh', + sources: sources, + dependencies: deps, + c_args: cflags, + link_with: [libnms_ifcfg_rh_core], + link_args: ldflags_linker_script_settings, + link_depends: linker_script_settings, + install: true, + install_dir: nm_plugindir +) + +core_plugins += libnm_settings_plugin_ifcfg_rh + +# FIXME: check_so_symbols replacement +''' +run_target( + 'check-local-symbols-settings-ifcfg-rh', + command: [check_so_symbols, libnm_settings_plugin_ifcfg_rh.full_path()], + depends: libnm_settings_plugin_ifcfg_rh +) + +check-local-symbols-settings-ifcfg-rh: src/settings/plugins/ifcfg-rh/libnm-settings-plugin-ifcfg-rh.la + $(call check_so_symbols,$(builddir)/src/settings/plugins/ifcfg-rh/.libs/libnm-settings-plugin-ifcfg-rh.so) +''' + +if enable_tests + subdir('tests') +endif diff --git a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c index 97417db9..2863df64 100644 --- a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c +++ b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c @@ -172,7 +172,7 @@ nm_inotify_helper_init (NMInotifyHelper *self) { NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); - priv->wd_refs = g_hash_table_new (g_direct_hash, g_direct_equal); + priv->wd_refs = g_hash_table_new (nm_direct_hash, NULL); } static void diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h index d850ac4e..eaff2db4 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h @@ -38,9 +38,6 @@ #define IFCFG_DIR SYSCONFDIR "/sysconfig/network-scripts" -#define IFCFG_PLUGIN_NAME "ifcfg-rh" -#define IFCFG_PLUGIN_INFO "(c) 2007 - 2015 Red Hat, Inc. To report bugs please use the NetworkManager mailing list." - #define TYPE_ETHERNET "Ethernet" #define TYPE_WIRELESS "Wireless" #define TYPE_INFINIBAND "InfiniBand" diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c index 0743fc9f..0fb77b9d 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c @@ -37,7 +37,6 @@ #include "settings/nm-settings-plugin.h" #include "nm-config.h" #include "NetworkManagerUtils.h" -#include "nm-exported-object.h" #include "nms-ifcfg-rh-connection.h" #include "nms-ifcfg-rh-common.h" @@ -46,10 +45,10 @@ #include "nms-ifcfg-rh-utils.h" #include "shvar.h" -#include "settings/plugins/ifcfg-rh/nmdbus-ifcfg-rh.h" - -#define IFCFGRH1_DBUS_SERVICE_NAME "com.redhat.ifcfgrh1" -#define IFCFGRH1_DBUS_OBJECT_PATH "/com/redhat/ifcfgrh1" +#define IFCFGRH1_BUS_NAME "com.redhat.ifcfgrh1" +#define IFCFGRH1_OBJECT_PATH "/com/redhat/ifcfgrh1" +#define IFCFGRH1_IFACE1_NAME "com.redhat.ifcfgrh1" +#define IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS "GetIfcfgDetails" /*****************************************************************************/ @@ -58,9 +57,9 @@ typedef struct { struct { GDBusConnection *connection; - GDBusInterfaceSkeleton *interface; GCancellable *cancellable; gulong signal_id; + guint regist_id; } dbus; GHashTable *connections; /* uuid::connection */ @@ -126,7 +125,6 @@ connection_ifcfg_changed (NMIfcfgConnection *connection, gpointer user_data) path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection)); g_return_if_fail (path != NULL); - if (!priv->ifcfg_monitor) { _LOGD ("connection_ifcfg_changed("NM_IFCFG_CONNECTION_LOG_FMTD"): %s", NM_IFCFG_CONNECTION_LOG_ARGD (connection), "ignore event"); return; @@ -327,21 +325,21 @@ update_connection (SettingsPluginIfcfg *self, if (new_unmanaged || new_unrecognized) { if (!old_unmanaged && !old_unrecognized) { + /* ref connection first, because we put it into priv->connections below. + * Emitting signal-removed might otherwise delete it. */ g_object_ref (connection_by_uuid); + /* Unexport the connection by telling the settings service it's * been removed. */ nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection_by_uuid)); - /* Remove the path so that claim_connection() doesn't complain later when - * interface gets managed and connection is re-added. */ - nm_connection_set_path (NM_CONNECTION (connection_by_uuid), NULL); /* signal_remove() will end up removing the connection from our hash, * so add it back now. */ g_hash_table_insert (priv->connections, g_strdup (nm_connection_get_uuid (NM_CONNECTION (connection_by_uuid))), - connection_by_uuid); + connection_by_uuid /* we took reference above and pass it on */); } } else { if (old_unmanaged /* && !new_unmanaged */) { @@ -373,7 +371,9 @@ update_connection (SettingsPluginIfcfg *self, _LOGI ("add connection "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); else _LOGI ("new connection "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - g_hash_table_insert (priv->connections, g_strdup (uuid), connection_new); + g_hash_table_insert (priv->connections, + g_strdup (uuid), + connection_new /* take reference */); g_signal_connect (connection_new, NM_SETTINGS_CONNECTION_REMOVED, G_CALLBACK (connection_removed_cb), @@ -418,7 +418,7 @@ ifcfg_dir_changed (GFileMonitor *monitor, path = g_file_get_path (file); ifcfg_path = utils_detect_ifcfg_path (path, FALSE); - _LOGD ("ifcfg_dir_changed(%s) = %d // %s", path, event_type, ifcfg_path ? ifcfg_path : "(none)"); + _LOGD ("ifcfg_dir_changed(%s) = %d // %s", path, event_type, ifcfg_path ?: "(none)"); if (ifcfg_path) { connection = find_by_path (plugin, ifcfg_path); switch (event_type) { @@ -516,7 +516,7 @@ read_connections (SettingsPluginIfcfg *plugin) return; } - alive_connections = g_hash_table_new (NULL, NULL); + alive_connections = g_hash_table_new (nm_direct_hash, NULL); filenames = g_ptr_array_new_with_free_func (g_free); while ((item = g_dir_read_name (dir))) { @@ -749,7 +749,7 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin, return; } - path = nm_connection_get_path (NM_CONNECTION (connection)); + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)); if (!path) { g_dbus_method_invocation_return_error (context, NM_SETTINGS_ERROR, @@ -768,15 +768,15 @@ static void _dbus_clear (SettingsPluginIfcfg *self) { SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + guint id; nm_clear_g_signal_handler (priv->dbus.connection, &priv->dbus.signal_id); nm_clear_g_cancellable (&priv->dbus.cancellable); - if (priv->dbus.interface) { - g_dbus_interface_skeleton_unexport (priv->dbus.interface); - nm_exported_object_skeleton_release (priv->dbus.interface); - priv->dbus.interface = NULL; + if ((id = nm_steal_int (&priv->dbus.regist_id))) { + if (!g_dbus_connection_unregister_object (priv->dbus.connection, id)) + _LOGW ("dbus: unexpected failure to unregister object"); } g_clear_object (&priv->dbus.connection); @@ -788,12 +788,55 @@ _dbus_connection_closed (GDBusConnection *connection, GError *error, gpointer user_data) { - _LOGW ("dbus: %s bus closed", IFCFGRH1_DBUS_SERVICE_NAME); + _LOGW ("dbus: %s bus closed", IFCFGRH1_BUS_NAME); _dbus_clear (SETTINGS_PLUGIN_IFCFG (user_data)); /* Retry or recover? */ } +static void +_method_call (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (user_data); + const char *ifcfg; + + if ( !nm_streq (interface_name, IFCFGRH1_IFACE1_NAME) + || !nm_streq (method_name, IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS)) { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method %s", + method_name); + return; + } + + g_variant_get (parameters, "(&s)", &ifcfg); + impl_ifcfgrh_get_ifcfg_details (self, invocation, ifcfg); +} + +static GDBusInterfaceInfo *const interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO ( + IFCFGRH1_BUS_NAME, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( + NM_DEFINE_GDBUS_METHOD_INFO ( + IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS, + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("ifcfg", "s"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("uuid", "s"), + NM_DEFINE_GDBUS_ARG_INFO ("path", "o"), + ), + ), + ), +); + static void _dbus_request_name_done (GObject *source_object, GAsyncResult *res, @@ -830,36 +873,27 @@ _dbus_request_name_done (GObject *source_object, } { - GType skeleton_type = NMDBUS_TYPE_IFCFGRH1_SKELETON; - gs_free char *method_name_get_ifcfg_details = NULL; - NMExportedObjectDBusMethodImpl methods[] = { - { - .method_name = (method_name_get_ifcfg_details = nm_exported_object_skeletonify_method_name ("GetIfcfgDetails")), - .impl = G_CALLBACK (impl_ifcfgrh_get_ifcfg_details), - }, + static const GDBusInterfaceVTable interface_vtable = { + .method_call = _method_call, }; - priv->dbus.interface = nm_exported_object_skeleton_create (skeleton_type, - g_type_class_peek (SETTINGS_TYPE_PLUGIN_IFCFG), - methods, - G_N_ELEMENTS (methods), - (GObject *) self); - - if (!g_dbus_interface_skeleton_export (priv->dbus.interface, - priv->dbus.connection, - IFCFGRH1_DBUS_OBJECT_PATH, - &error)) { - nm_exported_object_skeleton_release (priv->dbus.interface); - priv->dbus.interface = NULL; - _LOGW ("dbus: failed exporting interface: %s", error->message); + priv->dbus.regist_id = g_dbus_connection_register_object (connection, + IFCFGRH1_OBJECT_PATH, + interface_info, + NM_UNCONST_PTR (GDBusInterfaceVTable, &interface_vtable), + self, + NULL, + &error); + if (!priv->dbus.regist_id) { + _LOGW ("dbus: couldn't register D-Bus service: %s", error->message); _dbus_clear (self); return; } } - _LOGD ("dbus: aquired D-Bus service %s and exported %s object", - IFCFGRH1_DBUS_SERVICE_NAME, - IFCFGRH1_DBUS_OBJECT_PATH); + _LOGD ("dbus: acquired D-Bus service %s and exported %s object", + IFCFGRH1_BUS_NAME, + IFCFGRH1_OBJECT_PATH); } static void @@ -900,7 +934,7 @@ _dbus_create_done (GObject *source_object, DBUS_INTERFACE_DBUS, "RequestName", g_variant_new ("(su)", - IFCFGRH1_DBUS_SERVICE_NAME, + IFCFGRH1_BUS_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE), G_VARIANT_TYPE ("(u)"), G_DBUS_CALL_FLAGS_NONE, @@ -917,7 +951,7 @@ _dbus_setup (SettingsPluginIfcfg *self) gs_free char *address = NULL; gs_free_error GError *error = NULL; - g_return_if_fail (!priv->dbus.connection); + _dbus_clear (self); address = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (address == NULL) { @@ -943,39 +977,22 @@ config_changed_cb (NMConfig *config, NMConfigData *old_data, SettingsPluginIfcfg *self) { + SettingsPluginIfcfgPrivate *priv; + /* If the dbus connection for some reason is borked the D-Bus service * won't be offered. * * On SIGHUP and SIGUSR1 try to re-connect to D-Bus. So in the unlikely * event that the D-Bus conneciton is broken, that allows for recovery * without need for restarting NetworkManager. */ - if (NM_FLAGS_ANY (changes, NM_CONFIG_CHANGE_CAUSE_SIGHUP - | NM_CONFIG_CHANGE_CAUSE_SIGUSR1)) { - if (!SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self)->dbus.connection) - _dbus_setup (self); - } -} - -/*****************************************************************************/ + if (!NM_FLAGS_ANY (changes, NM_CONFIG_CHANGE_CAUSE_SIGHUP + | NM_CONFIG_CHANGE_CAUSE_SIGUSR1)) + return; -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - switch (prop_id) { - case NM_SETTINGS_PLUGIN_PROP_NAME: - g_value_set_string (value, IFCFG_PLUGIN_NAME); - break; - case NM_SETTINGS_PLUGIN_PROP_INFO: - g_value_set_string (value, IFCFG_PLUGIN_INFO); - break; - case NM_SETTINGS_PLUGIN_PROP_CAPABILITIES: - g_value_set_uint (value, NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } + priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + if ( !priv->dbus.connection + && !priv->dbus.cancellable) + _dbus_setup (self); } /*****************************************************************************/ @@ -1048,19 +1065,6 @@ settings_plugin_ifcfg_class_init (SettingsPluginIfcfgClass *req_class) object_class->constructed = constructed; object_class->dispose = dispose; - object_class->get_property = get_property; - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_NAME, - NM_SETTINGS_PLUGIN_NAME); - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_INFO, - NM_SETTINGS_PLUGIN_INFO); - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_CAPABILITIES, - NM_SETTINGS_PLUGIN_CAPABILITIES); } static void diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index c91cd253..722d4e36 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -181,7 +181,7 @@ make_connection_setting (const char *file, const char *v; gs_free char *stable_id = NULL; const char *const *iter; - int vint64; + int vint64, i_val; ifcfg_name = utils_get_ifcfg_name (file, TRUE); if (!ifcfg_name) @@ -256,7 +256,6 @@ make_connection_setting (const char *file, } } - nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "ZONE", &value); g_object_set (s_con, NM_SETTING_CONNECTION_ZONE, v, NULL); @@ -338,6 +337,13 @@ make_connection_setting (const char *file, vint64 = svGetValueInt64 (ifcfg, "AUTH_RETRIES", 10, -1, G_MAXINT32, -1); g_object_set (s_con, NM_SETTING_CONNECTION_AUTH_RETRIES, (gint) vint64, NULL); + i_val = NM_SETTING_CONNECTION_MDNS_DEFAULT; + if (!svGetValueEnum (ifcfg, "MDNS", + nm_setting_connection_mdns_get_type (), + &i_val, NULL)) + PARSE_WARNING ("invalid MDNS setting"); + g_object_set (s_con, NM_SETTING_CONNECTION_MDNS, i_val, NULL); + return NM_SETTING (s_con); } @@ -380,7 +386,7 @@ is_any_ip4_address_defined (shvarFile *ifcfg, int *idx) { int i, ignore, *ret_idx; - ret_idx = idx ? idx : &ignore; + ret_idx = idx ?: &ignore; for (i = -1; i <= 2; i++) { gs_free char *value = NULL; @@ -496,7 +502,7 @@ parse_route_line_is_comment (const char *line) * * initscripts compares: "$line" =~ '^[[:space:]]*(\#.*)?$' */ - while (NM_IN_SET (line[0], ' ', '\t')) + while (nm_utils_is_separator (line[0])) line++; if (NM_IN_SET (line[0], '\0', '#')) return TRUE; @@ -927,7 +933,7 @@ next: : "")); break; case PARSE_LINE_TYPE_FLAG: - /* XXX: the flag (for "onlink") only allows to explictly set "TRUE". + /* NOTE: the flag (for "onlink") only allows to explictly set "TRUE". * There is no way to express an explicit "FALSE" setting * of this attribute, hence, the file format cannot encode * that configuration. */ @@ -1338,29 +1344,7 @@ make_ip4_setting (shvarFile *ifcfg, } else if (!g_ascii_strcasecmp (v, "autoip")) { method = NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL; } else if (!g_ascii_strcasecmp (v, "shared")) { - int idx; - - g_object_set (s_ip4, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_SHARED, - NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default, - NULL); - /* 1 IP address is allowed for shared connections. Read it. */ - if (is_any_ip4_address_defined (ifcfg, &idx)) { - guint32 gw; - NMIPAddress *addr = NULL; - - if (!read_full_ip4_address (ifcfg, idx, NULL, &addr, NULL, error)) - return NULL; - if (!read_ip4_address (ifcfg, "GATEWAY", NULL, &gw, error)) - return NULL; - (void) nm_setting_ip_config_add_address (s_ip4, addr); - nm_ip_address_unref (addr); - if (never_default) - PARSE_WARNING ("GATEWAY will be ignored when DEFROUTE is disabled"); - gateway = g_strdup (nm_utils_inet4_ntop (gw, inet_buf)); - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway, NULL); - } - return g_steal_pointer (&s_ip4); + method = NM_SETTING_IP4_CONFIG_METHOD_SHARED; } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Unknown BOOTPROTO '%s'", v); @@ -1387,7 +1371,7 @@ make_ip4_setting (shvarFile *ifcfg, NM_SETTING_IP_CONFIG_ROUTE_TABLE, (guint) route_table, NULL); - if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) + if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) return g_steal_pointer (&s_ip4); /* Handle DHCP settings */ @@ -1464,39 +1448,47 @@ make_ip4_setting (shvarFile *ifcfg, if (gateway && never_default) PARSE_WARNING ("GATEWAY will be ignored when DEFROUTE is disabled"); - /* DNS servers - * Pick up just IPv4 addresses (IPv6 addresses are taken by make_ip6_setting()) - */ - for (i = 1; i <= 10; i++) { - char tag[256]; - - numbered_tag (tag, "DNS", i); - nm_clear_g_free (&value); - v = svGetValueStr (ifcfg, tag, &value); - if (v) { - if (nm_utils_ipaddr_valid (AF_INET, v)) { - if (!nm_setting_ip_config_add_dns (s_ip4, v)) - PARSE_WARNING ("duplicate DNS server %s", tag); - } else if (nm_utils_ipaddr_valid (AF_INET6, v)) { - /* Ignore IPv6 addresses */ - } else { - PARSE_WARNING ("invalid DNS server address %s", v); - return NULL; + /* We used to skip saving a lot of unused properties for the ipv4 shared method. + * We want now to persist them but... unfortunately loading DNS or DOMAIN options + * would cause a fail in the ipv4 verify() function. As we don't want any regression + * in the unlikely event that someone has a working ifcfg file for an IPv4 shared ip + * connection with a crafted "DNS" entry... don't load it. So we will avoid failing + * the connection) */ + if (!nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { + /* DNS servers + * Pick up just IPv4 addresses (IPv6 addresses are taken by make_ip6_setting()) + */ + for (i = 1; i <= 10; i++) { + char tag[256]; + + numbered_tag (tag, "DNS", i); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, tag, &value); + if (v) { + if (nm_utils_ipaddr_valid (AF_INET, v)) { + if (!nm_setting_ip_config_add_dns (s_ip4, v)) + PARSE_WARNING ("duplicate DNS server %s", tag); + } else if (nm_utils_ipaddr_valid (AF_INET6, v)) { + /* Ignore IPv6 addresses */ + } else { + PARSE_WARNING ("invalid DNS server address %s", v); + return NULL; + } } } - } - /* DNS searches */ - nm_clear_g_free (&value); - v = svGetValueStr (ifcfg, "DOMAIN", &value); - if (v) { - gs_free const char **searches = NULL; + /* DNS searches */ + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DOMAIN", &value); + if (v) { + gs_free const char **searches = NULL; - searches = nm_utils_strsplit_set (v, " "); - if (searches) { - for (item = searches; *item; item++) { - if (!nm_setting_ip_config_add_dns_search (s_ip4, *item)) - PARSE_WARNING ("duplicate DNS domain '%s'", *item); + searches = nm_utils_strsplit_set (v, " "); + if (searches) { + for (item = searches; *item; item++) { + if (!nm_setting_ip_config_add_dns_search (s_ip4, *item)) + PARSE_WARNING ("duplicate DNS domain '%s'", *item); + } } } } @@ -1545,7 +1537,8 @@ make_ip4_setting (shvarFile *ifcfg, } /* Legacy value NM used for a while but is incorrect (rh #459370) */ - if (!nm_setting_ip_config_get_num_dns_searches (s_ip4)) { + if ( !nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) + && !nm_setting_ip_config_get_num_dns_searches (s_ip4)) { nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "SEARCH", &value); if (v) { @@ -1561,10 +1554,14 @@ make_ip4_setting (shvarFile *ifcfg, } } - timeout = svGetValueInt64 (ifcfg, "ARPING_WAIT", 10, -1, - NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX / 1000, -1); - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DAD_TIMEOUT, - (gint) (timeout <= 0 ? timeout : timeout * 1000), NULL); + timeout = svGetValueInt64 (ifcfg, "ACD_TIMEOUT", 10, -1, NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX, -2); + if (timeout == -2) { + timeout = svGetValueInt64 (ifcfg, "ARPING_WAIT", 10, -1, + NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX / 1000, -1); + if (timeout > 0) + timeout *= 1000; + } + g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DAD_TIMEOUT, (gint) timeout, NULL); return g_steal_pointer (&s_ip4); } @@ -1642,7 +1639,7 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file read_defroute ? &gateway : NULL, &err); if (ok) { - nm_ip_address_set_attribute (addr, "label", g_variant_new_string (device)); + nm_ip_address_set_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL, g_variant_new_string (device)); if (!nm_setting_ip_config_add_address (s_ip4, addr)) PARSE_WARNING ("duplicate IP4 address in alias file %s", item); if (nm_streq0 (nm_setting_ip_config_get_method (s_ip4), NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) @@ -1850,8 +1847,8 @@ make_ip6_setting (shvarFile *ifcfg, ipv6addr_secondaries = svGetValueStr_cp (ifcfg, "IPV6ADDR_SECONDARIES"); value = g_strjoin (ipv6addr && ipv6addr_secondaries ? " " : NULL, - ipv6addr ? ipv6addr : "", - ipv6addr_secondaries ? ipv6addr_secondaries : "", + ipv6addr ?: "", + ipv6addr_secondaries ?: "", NULL); g_free (ipv6addr); g_free (ipv6addr_secondaries); @@ -2001,11 +1998,15 @@ make_tc_setting (shvarFile *ifcfg) break; qdisc = nm_utils_tc_qdisc_from_str (value, &local); - if (!qdisc) - PARSE_WARNING ("ignoring bad qdisc: '%s': %s", value, local->message); + if (!qdisc) { + PARSE_WARNING ("ignoring bad tc qdisc: '%s': %s", value, local->message); + continue; + } if (!nm_setting_tc_config_add_qdisc (s_tc, qdisc)) - PARSE_WARNING ("duplicate qdisc"); + PARSE_WARNING ("duplicate tc qdisc"); + + nm_tc_qdisc_unref (qdisc); } for (i = 1;; i++) { @@ -2019,11 +2020,15 @@ make_tc_setting (shvarFile *ifcfg) break; tfilter = nm_utils_tc_tfilter_from_str (value, &local); - if (!tfilter) - PARSE_WARNING ("ignoring bad tfilter: '%s': %s", value, local->message); + if (!tfilter) { + PARSE_WARNING ("ignoring bad tc filter: '%s': %s", value, local->message); + continue; + } if (!nm_setting_tc_config_add_tfilter (s_tc, tfilter)) - PARSE_WARNING ("duplicate filter"); + PARSE_WARNING ("duplicate tc filter"); + + nm_tc_tfilter_unref (tfilter); } if ( nm_setting_tc_config_get_num_qdiscs (s_tc) > 0 @@ -3552,6 +3557,13 @@ make_wpa_setting (shvarFile *ifcfg, return NULL; g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PMF, i_val, NULL); + i_val = NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT; + if (!svGetValueEnum (ifcfg, "FILS", + nm_setting_wireless_security_fils_get_type (), + &i_val, error)) + return NULL; + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_FILS, i_val, NULL); + nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "SECURITYMODE", &value); if (NM_IN_STRSET (v, NULL, "open")) @@ -4407,7 +4419,6 @@ parse_infiniband_p_key (shvarFile *ifcfg, return ret; } - static NMSetting * make_infiniband_setting (shvarFile *ifcfg, const char *file, @@ -4513,9 +4524,9 @@ handle_bond_option (NMSettingBond *s_bond, } } - if (!nm_setting_bond_add_option (s_bond, key, sanitized ? sanitized : value)) + if (!nm_setting_bond_add_option (s_bond, key, sanitized ?: value)) PARSE_WARNING ("invalid bonding option '%s' = %s", - key, sanitized ? sanitized : value); + key, sanitized ?: value); g_free (sanitized); } @@ -5321,6 +5332,8 @@ connection_from_file_full (const char *filename, g_return_val_if_fail (filename != NULL, NULL); g_return_val_if_fail (out_unhandled && !*out_unhandled, NULL); + NM_SET_OUT (out_ignore_error, FALSE); + /* Non-NULL only for unit tests; normally use /etc/sysconfig/network */ if (!network_file) network_file = SYSCONFDIR "/sysconfig/network"; @@ -5341,6 +5354,7 @@ connection_from_file_full (const char *filename, if (!svGetValueBoolean (parsed, "NM_CONTROLLED", TRUE)) { connection = create_unhandled_connection (filename, parsed, "unmanaged", out_unhandled); if (!connection) { + NM_SET_OUT (out_ignore_error, TRUE); g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "NM_CONTROLLED was false but device was not uniquely identified; device will be managed"); } @@ -5350,8 +5364,7 @@ connection_from_file_full (const char *filename, /* iBFT is handled by the iBFT settings plugin */ bootproto = svGetValueStr_cp (parsed, "BOOTPROTO"); if (bootproto && !g_ascii_strcasecmp (bootproto, "ibft")) { - if (out_ignore_error) - *out_ignore_error = TRUE; + NM_SET_OUT (out_ignore_error, TRUE); g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Ignoring iBFT configuration"); g_free (bootproto); @@ -5397,8 +5410,7 @@ connection_from_file_full (const char *filename, char *device; if ((tmp = svGetValueStr_cp (parsed, "IPV6TUNNELIPV4"))) { - if (out_ignore_error) - *out_ignore_error = TRUE; + NM_SET_OUT (out_ignore_error, TRUE); g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Ignoring unsupported connection due to IPV6TUNNELIPV4"); return NULL; @@ -5412,8 +5424,7 @@ connection_from_file_full (const char *filename, } if (!strcmp (device, "lo")) { - if (out_ignore_error) - *out_ignore_error = TRUE; + NM_SET_OUT (out_ignore_error, TRUE); g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Ignoring loopback device config."); g_free (device); @@ -5460,8 +5471,7 @@ connection_from_file_full (const char *filename, memcpy (p_path, IFUP_PATH_PREFIX, NM_STRLEN (IFUP_PATH_PREFIX)); if (access (p_path, X_OK) == 0) { /* for all other types, this is not something we want to handle. */ - if (out_ignore_error) - *out_ignore_error = TRUE; + NM_SET_OUT (out_ignore_error, TRUE); g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Ignore script for unknown device type which has a matching %s script", p_path); diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index e9dd08b7..bdad7389 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -148,16 +148,7 @@ write_secrets (shvarFile *ifcfg, /* we purge all existing secrets. */ svUnsetAll (keyfile, SV_KEY_TYPE_ANY); - /* sort the keys. */ - secrets_keys = (const char **) g_hash_table_get_keys_as_array (secrets, &secrets_keys_n); - if (secrets_keys_n > 1) { - g_qsort_with_data (secrets_keys, - secrets_keys_n, - sizeof (const char *), - nm_strcmp_p_with_data, - NULL); - } - + secrets_keys = nm_utils_strdict_get_keys (secrets, TRUE, &secrets_keys_n); for (i = 0; i < secrets_keys_n; i++) { const char *k = secrets_keys[i]; const char *v = g_hash_table_lookup (secrets, k); @@ -786,7 +777,6 @@ write_wireless_security_setting (NMConnection *connection, "WPA_PSK_FLAGS", wpa ? nm_setting_wireless_security_get_psk_flags (s_wsec) : NM_SETTING_SECRET_FLAG_NONE); - if (nm_setting_wireless_security_get_pmf (s_wsec) == NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT) svUnsetValue (ifcfg, "PMF"); else { @@ -794,6 +784,13 @@ write_wireless_security_setting (NMConnection *connection, nm_setting_wireless_security_get_pmf (s_wsec)); } + if (nm_setting_wireless_security_get_fils (s_wsec) == NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT) + svUnsetValue (ifcfg, "FILS"); + else { + svSetValueEnum (ifcfg, "FILS", nm_setting_wireless_security_fils_get_type (), + nm_setting_wireless_security_get_fils (s_wsec)); + } + return TRUE; } @@ -899,14 +896,16 @@ write_wireless_setting (NMConnection *connection, } mode = nm_setting_wireless_get_mode (s_wireless); - if (!mode || !strcmp (mode, "infrastructure")) { + if (!mode) + svUnsetValue(ifcfg, "MODE"); + else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_INFRA)) svSetValueStr (ifcfg, "MODE", "Managed"); - } else if (!strcmp (mode, "adhoc")) { + else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) { svSetValueStr (ifcfg, "MODE", "Ad-Hoc"); adhoc = TRUE; - } else if (!strcmp (mode, "ap")) { + } else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_AP)) svSetValueStr (ifcfg, "MODE", "Ap"); - } else { + else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "Invalid mode '%s' in '%s' setting", mode, NM_SETTING_WIRELESS_SETTING_NAME); @@ -1728,6 +1727,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) GString *str; const char *master, *master_iface = NULL, *type; gint vint; + NMSettingConnectionMdns mdns; guint32 vuint32; const char *tmp; @@ -1749,9 +1749,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) /* Only save the value for master connections */ type = nm_setting_connection_get_connection_type (s_con); - if ( !g_strcmp0 (type, NM_SETTING_BOND_SETTING_NAME) - || !g_strcmp0 (type, NM_SETTING_TEAM_SETTING_NAME) - || !g_strcmp0 (type, NM_SETTING_BRIDGE_SETTING_NAME)) { + if (_nm_connection_type_is_master (type)) { NMSettingConnectionAutoconnectSlaves autoconnect_slaves; autoconnect_slaves = nm_setting_connection_get_autoconnect_slaves (s_con); svSetValueStr (ifcfg, "AUTOCONNECT_SLAVES", @@ -1888,6 +1886,13 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) vint = nm_setting_connection_get_auth_retries (s_con); svSetValueInt64_cond (ifcfg, "AUTH_RETRIES", vint >= 0, vint); + + mdns = nm_setting_connection_get_mdns (s_con); + if (mdns != NM_SETTING_CONNECTION_MDNS_DEFAULT) { + svSetValueEnum (ifcfg, "MDNS", nm_setting_connection_mdns_get_type (), + mdns); + } else + svUnsetValue (ifcfg, "MDNS"); } static char * @@ -2158,7 +2163,6 @@ write_tc_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) n++; } - num = nm_setting_tc_config_get_num_tfilters (s_tc); for (n = 1, i = 0; i < num; i++) { NMTCTfilter *tfilter; @@ -2281,7 +2285,7 @@ write_ip4_setting (NMConnection *connection, if (i > 0) { GVariant *label; - label = nm_ip_address_get_attribute (addr, "label"); + label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL); if (label) continue; } @@ -2411,12 +2415,15 @@ write_ip4_setting (NMConnection *connection, NM_SET_OUT (out_route_content, write_route_file (s_ip4)); timeout = nm_setting_ip_config_get_dad_timeout (s_ip4); - if (timeout < 0) + if (timeout < 0) { + svUnsetValue (ifcfg, "ACD_TIMEOUT"); svUnsetValue (ifcfg, "ARPING_WAIT"); - else if (timeout == 0) + } else if (timeout == 0) { + svSetValueStr (ifcfg, "ACD_TIMEOUT", "0"); svSetValueStr (ifcfg, "ARPING_WAIT", "0"); - else { - /* Round the value up to next integer */ + } else { + svSetValueInt64 (ifcfg, "ACD_TIMEOUT", timeout); + /* Round the value up to next integer for initscripts */ svSetValueInt64 (ifcfg, "ARPING_WAIT", (timeout - 1) / 1000 + 1); } @@ -2484,7 +2491,7 @@ write_ip4_aliases (NMConnection *connection, const char *base_ifcfg_path) addr = nm_setting_ip_config_get_address (s_ip4, i); - label_var = nm_ip_address_get_attribute (addr, "label"); + label_var = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL); if (!label_var) continue; label = g_variant_get_string (label_var, NULL); @@ -2987,7 +2994,7 @@ do_write_to_disk (NMConnection *connection, { /* From here on, we persist data to disk. Before, it was all in-memory * only. But we loaded the ifcfg files from disk, and managled our - * new settings (in-momory). */ + * new settings (in-memory). */ if (!svWriteFile (ifcfg, 0644, error)) return FALSE; @@ -3138,10 +3145,10 @@ nms_ifcfg_rh_writer_write_connection (NMConnection *connection, * does not yet allow to inject the configuration. */ if (out_reread || out_reread_same) { if (!do_write_reread (connection, - svFileGetName (ifcfg), - out_reread, - out_reread_same, - &local)) { + svFileGetName (ifcfg), + out_reread, + out_reread_same, + &local)) { _LOGW ("write: failure to re-read connection \"%s\": %s", svFileGetName (ifcfg), local->message); g_clear_error (&local); @@ -3188,4 +3195,3 @@ nms_ifcfg_rh_writer_can_write_connection (NMConnection *connection, GError **err NM_PRINT_FMT_QUOTE_STRING (type)); return FALSE; } - diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index 2b64f3fc..9120b870 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -39,7 +39,7 @@ #include "nm-core-internal.h" #include "nm-core-utils.h" #include "nm-utils/nm-enum-utils.h" -#include "nm-utils/c-list.h" +#include "c-list/src/c-list.h" /*****************************************************************************/ @@ -1278,7 +1278,7 @@ svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value) { gs_free char *v = NULL; - v = _nm_utils_enum_to_str_full (gtype, value, " "); + v = _nm_utils_enum_to_str_full (gtype, value, " ", NULL); return svSetValueStr (s, key, v); } diff --git a/src/settings/plugins/ifcfg-rh/tests/meson.build b/src/settings/plugins/ifcfg-rh/tests/meson.build new file mode 100644 index 00000000..3d72d892 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/meson.build @@ -0,0 +1,17 @@ +test_unit = 'test-ifcfg-rh' + +test_ifcfg_dir = meson.current_source_dir() + +exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, + c_args: nm_build_cflags, + link_with: libnms_ifcfg_rh_core +) + +test( + 'ifcfg-rh/' + test_unit, + test_script, + args: test_args + [exe.full_path()] +) diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected index 026993b8..cf325f35 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected @@ -1,5 +1,4 @@ ESSID="Test SSID" -MODE=Managed SSID_HIDDEN=yes MAC_ADDRESS_RANDOMIZATION=default TYPE=Wireless diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected index c0e47c48..cd8fc96f 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected @@ -14,6 +14,8 @@ DNS2=4.2.2.2 DOMAIN="foobar.com lab.foobar.com" DEFROUTE=yes IPV4_FAILURE_FATAL=no +ACD_TIMEOUT=400 +ARPING_WAIT=1 IPV6INIT=no NAME="Test Write Wired Static Routes" UUID=${UUID} diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc new file mode 100644 index 00000000..d0a3c254 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc @@ -0,0 +1,16 @@ +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:55 +BOOTPROTO=none +ONBOOT=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=192.168.1.5 +PREFIX=24 +NETMASK=255.255.255.0 +GATEWAY=192.168.1.1 +IPV6INIT=no +QDISC1="root fq_codel" +FILTER1="parent 1234: matchall action simple sdata Hello" +NAME=ethernet-tc +UUID=a42c8d4e-11a2-4144-92d2-5cbce8c6b2c4 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected new file mode 100644 index 00000000..a67ca598 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected @@ -0,0 +1,16 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +QDISC1="parent 2468:2 pfifo_fast" +FILTER1="parent 1234: matchall action simple sdata Hello" +BOOTPROTO=none +IPADDR=1.1.1.3 +PREFIX=24 +GATEWAY=1.1.1.1 +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME="Test Write TC config" +UUID=${UUID} +DEVICE=eth0 +ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-1 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-1 index 1bc3d524..db09afdb 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-1 +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-1 @@ -10,3 +10,5 @@ PREFIX1=16 IPADDR2=3.3.3.3 PREFIX2=8 GATEWAY=1.1.1.1 +ACD_TIMEOUT=2000 +ARPING_WAIT=1 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-2 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-2 index d7273e36..6972e279 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-2 +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-2 @@ -9,3 +9,5 @@ IPADDR2=9.8.7.6 PREFIX2=16 IPADDR3=3.3.3.3 PREFIX3=8 +ACD_TIMEOUT=2000 +ARPING_WAIT=1 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-3 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-3 index f2457bd2..d9065994 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-3 +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-3 @@ -9,3 +9,5 @@ IPADDR3=9.8.7.6 PREFIX3=16 IPADDR4=3.3.3.3 PREFIX4=8 +ACD_TIMEOUT=2000 +ARPING_WAIT=1 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-4 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-4 index e6b77141..935267f7 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-4 +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-4 @@ -9,3 +9,5 @@ IPADDR1=9.8.7.6 PREFIX1=16 IPADDR2=3.3.3.3 PREFIX2=8 +ACD_TIMEOUT=2000 +ARPING_WAIT=1 diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 6bf27556..1331391d 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -58,7 +59,11 @@ #include "nm-test-utils-core.h" -#define TEST_SCRATCH_DIR_TMP TEST_SCRATCH_DIR"/network-scripts/tmp" +#define TEST_IFCFG_DIR NM_BUILD_SRCDIR"/src/settings/plugins/ifcfg-rh/tests/network-scripts" +#define TEST_SCRATCH_DIR NM_BUILD_BUILDDIR"/src/settings/plugins/ifcfg-rh/tests/network-scripts" +#define TEST_SCRATCH_DIR_TMP TEST_SCRATCH_DIR"/tmp" + +#define TEST_SCRATCH_ALIAS_BASE TEST_SCRATCH_DIR"/ifcfg-alias0" /*****************************************************************************/ @@ -410,7 +415,7 @@ test_read_netmask_1 (void) NMSettingConnection *s_con; NMSettingIPConfig *s_ip4; NMIPAddress *ip4_addr; - const char *FILENAME = TEST_IFCFG_DIR "/network-scripts/ifcfg-netmask-1"; + const char *FILENAME = TEST_IFCFG_DIR"/ifcfg-netmask-1"; connection = _connection_from_file (FILENAME, NULL, TYPE_ETHERNET, NULL); @@ -430,14 +435,14 @@ test_read_netmask_1 (void) content = nmtst_file_get_contents (FILENAME); - testfile = g_strdup (TEST_SCRATCH_DIR "/network-scripts/ifcfg-netmask-1.copy"); + testfile = g_strdup (TEST_SCRATCH_DIR"/ifcfg-netmask-1.copy"); nmtst_file_set_contents (testfile, content); _writer_update_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, testfile, - TEST_IFCFG_DIR "/network-scripts/ifcfg-netmask-1.cexpected"); + TEST_IFCFG_DIR"/ifcfg-netmask-1.cexpected"); } /*****************************************************************************/ @@ -541,7 +546,6 @@ verify_cert_or_key (NMSetting8021x *s_compare, return TRUE; } - static void test_read_basic (void) { @@ -553,7 +557,7 @@ test_read_basic (void) const char *mac; char expected_mac_address[ETH_ALEN] = { 0x00, 0x16, 0x41, 0x11, 0x22, 0x33 }; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-minimal", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-minimal", NULL, TYPE_ETHERNET, NULL); /* ===== CONNECTION SETTING ===== */ @@ -605,9 +609,8 @@ test_read_miscellaneous_variables (void) int mac_blacklist_num, i; guint64 expected_timestamp = 0; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*invalid MAC in HWADDR_BLACKLIST 'XX:aa:invalid'*"); - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-misc-variables", + NMTST_EXPECT_NM_WARN ("*invalid MAC in HWADDR_BLACKLIST 'XX:aa:invalid'*"); + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-misc-variables", NULL, TYPE_ETHERNET, NULL); g_test_assert_expected_messages (); @@ -648,7 +651,7 @@ test_read_variables_corner_cases (void) const char *mac; char expected_mac_address[ETH_ALEN] = { 0x00, 0x16, 0x41, 0x11, 0x22, 0x33 }; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-variables-corner-cases-1", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-variables-corner-cases-1", NULL, TYPE_ETHERNET, NULL); /* ===== CONNECTION SETTING ===== */ @@ -686,7 +689,7 @@ test_read_unmanaged (void) char *unhandled_spec = NULL; guint64 expected_timestamp = 0; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-nm-controlled", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-nm-controlled", NULL, TYPE_ETHERNET, &unhandled_spec); g_assert_cmpstr (unhandled_spec, ==, "unmanaged:mac:00:11:22:33:f8:9f"); @@ -710,7 +713,7 @@ test_read_unmanaged_unrecognized (void) gs_free char *unhandled_spec = NULL; guint64 expected_timestamp = 0; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-nm-controlled-unrecognized", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-nm-controlled-unrecognized", NULL, NULL, &unhandled_spec); g_assert_cmpstr (unhandled_spec, ==, "unmanaged:interface-name:ipoac0"); @@ -732,7 +735,7 @@ test_read_unrecognized (void) gs_free char *unhandled_spec = NULL; guint64 expected_timestamp = 0; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-unrecognized", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-unrecognized", NULL, NULL, &unhandled_spec); g_assert_cmpstr (unhandled_spec, ==, "unrecognized:mac:00:11:22:33"); @@ -856,11 +859,10 @@ test_read_wired_static_no_prefix (gconstpointer user_data) NMIPAddress *ip4_addr; char *file, *expected_id; - file = g_strdup_printf (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-static-no-prefix-%u", expected_prefix); + file = g_strdup_printf (TEST_IFCFG_DIR"/ifcfg-test-wired-static-no-prefix-%u", expected_prefix); expected_id = g_strdup_printf ("System test-wired-static-no-prefix-%u", expected_prefix); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*missing PREFIX, assuming*"); + NMTST_EXPECT_NM_WARN ("*missing PREFIX, assuming*"); connection = _connection_from_file (file, NULL, TYPE_ETHERNET, NULL); g_test_assert_expected_messages (); @@ -898,7 +900,7 @@ test_read_wired_dhcp (void) char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0xee }; const char *mac; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-dhcp", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-dhcp", NULL, TYPE_ETHERNET, &unmanaged); g_assert (unmanaged == NULL); @@ -941,7 +943,7 @@ test_read_wired_dhcp_plus_ip (void) NMIPAddress *ip4_addr; NMIPAddress *ip6_addr; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-dhcp-plus-ip", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-dhcp-plus-ip", NULL, TYPE_ETHERNET, NULL); /* ===== IPv4 SETTING ===== */ @@ -1008,7 +1010,7 @@ test_read_wired_shared_plus_ip (void) NMSettingIPConfig *s_ip4; NMIPAddress *ip4_addr; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-shared-plus-ip", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-shared-plus-ip", NULL, TYPE_ETHERNET, NULL); /* ===== IPv4 SETTING ===== */ @@ -1040,8 +1042,8 @@ test_read_wired_global_gateway (void) NMIPAddress *ip4_addr; char *unmanaged = NULL; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-global-gateway", - TEST_IFCFG_DIR"/network-scripts/network-test-wired-global-gateway", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-global-gateway", + TEST_IFCFG_DIR"/network-test-wired-global-gateway", TYPE_ETHERNET, &unmanaged); g_assert (unmanaged == NULL); @@ -1081,10 +1083,9 @@ test_read_wired_global_gateway_ignore (void) NMSettingIPConfig *s_ip4; char *unmanaged = NULL; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ignoring GATEWAY (/etc/sysconfig/network) for * because the connection has no static addresses"); - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-global-gateway-ignore", - TEST_IFCFG_DIR"/network-scripts/network-test-wired-global-gateway-ignore", + NMTST_EXPECT_NM_WARN ("*ignoring GATEWAY (/etc/sysconfig/network) for * because the connection has no static addresses"); + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-global-gateway-ignore", + TEST_IFCFG_DIR"/network-test-wired-global-gateway-ignore", TYPE_ETHERNET, &unmanaged); g_test_assert_expected_messages (); g_assert (unmanaged == NULL); @@ -1119,7 +1120,7 @@ test_read_wired_obsolete_gateway_n (void) NMSettingIPConfig *s_ip4; NMIPAddress *ip4_addr; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-obsolete-gateway-n", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-obsolete-gateway-n", NULL, TYPE_ETHERNET, NULL); /* ===== IPv4 SETTING ===== */ @@ -1187,8 +1188,8 @@ test_user_1 (void) nm_connection_add_setting (connection, NM_SETTING (s_user)); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_User_1.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_User_1.cexpected", &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -1203,8 +1204,8 @@ test_read_wired_never_default (void) NMSettingIPConfig *s_ip4; NMSettingIPConfig *s_ip6; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-never-default", - TEST_IFCFG_DIR"/network-scripts/network-test-wired-never-default", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-never-default", + TEST_IFCFG_DIR"/network-test-wired-never-default", TYPE_ETHERNET, NULL); /* ===== WIRED SETTING ===== */ @@ -1235,7 +1236,7 @@ test_read_wired_defroute_no (void) NMSettingIPConfig *s_ip6; char *unmanaged = NULL; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-defroute-no", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-defroute-no", NULL, TYPE_ETHERNET, &unmanaged); g_assert (unmanaged == NULL); @@ -1267,8 +1268,8 @@ test_read_wired_defroute_no_gatewaydev_yes (void) NMSettingIPConfig *s_ip6; char *unmanaged = NULL; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-defroute-no-gatewaydev-yes", - TEST_IFCFG_DIR"/network-scripts/network-test-wired-defroute-no-gatewaydev-yes", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-defroute-no-gatewaydev-yes", + TEST_IFCFG_DIR"/network-test-wired-defroute-no-gatewaydev-yes", TYPE_ETHERNET, &unmanaged); @@ -1300,7 +1301,7 @@ test_read_wired_static_routes (void) NMSettingIPConfig *s_ip4; NMIPRoute *ip4_route; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-static-routes", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-static-routes", NULL, TYPE_ETHERNET, NULL); /* ===== CONNECTION SETTING ===== */ @@ -1373,7 +1374,7 @@ test_read_wired_static_routes_legacy (void) char *unmanaged = NULL; NMIPRoute *ip4_route; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-static-routes-legacy", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-static-routes-legacy", NULL, TYPE_ETHERNET, &unmanaged); g_assert (!unmanaged); @@ -1476,6 +1477,7 @@ test_read_wired_ipv4_manual (gconstpointer data) s_ip4 = nm_connection_get_setting_ip4_config (connection); g_assert (s_ip4); g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL); + g_assert_cmpint (nm_setting_ip_config_get_dad_timeout (s_ip4), ==, 2000); /* IP addresses */ g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 3); @@ -1513,9 +1515,8 @@ test_read_wired_ipv6_manual (void) NMIPAddress *ip6_addr; NMIPRoute *ip6_route; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ignoring manual default route*"); - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-ipv6-manual", + NMTST_EXPECT_NM_WARN ("*ignoring manual default route*"); + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-ipv6-manual", NULL, TYPE_ETHERNET, &unmanaged); g_test_assert_expected_messages (); g_assert (!unmanaged); @@ -1691,7 +1692,7 @@ test_read_wired_dhcp6_only (void) char *unmanaged = NULL; const char *method; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-dhcp6-only", NULL, TYPE_ETHERNET, &unmanaged); + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-dhcp6-only", NULL, TYPE_ETHERNET, &unmanaged); g_assert (!unmanaged); /* ===== CONNECTION SETTING ===== */ @@ -1729,7 +1730,7 @@ test_read_wired_autoip (void) NMSettingIPConfig *s_ip4; char *unmanaged = NULL; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-autoip", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-autoip", NULL, TYPE_ETHERNET, &unmanaged); g_assert (unmanaged == NULL); @@ -1748,7 +1749,7 @@ test_read_onboot_no (void) NMSettingConnection *s_con; char *unmanaged = NULL; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-onboot-no", NULL, TYPE_ETHERNET, &unmanaged); + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-onboot-no", NULL, TYPE_ETHERNET, &unmanaged); g_assert (!unmanaged); s_con = nm_connection_get_setting_connection (connection); @@ -1766,7 +1767,7 @@ test_read_noip (void) NMSettingIPConfig *s_ip4; NMSettingIPConfig *s_ip6; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-noip", NULL, TYPE_ETHERNET, NULL); + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-noip", NULL, TYPE_ETHERNET, NULL); s_ip4 = nm_connection_get_setting_ip4_config (connection); g_assert (s_ip4); @@ -1781,7 +1782,7 @@ test_read_noip (void) g_object_unref (connection); } -#define TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2_CA_CERT TEST_IFCFG_DIR "/network-scripts/test_ca_cert.pem" +#define TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2_CA_CERT TEST_IFCFG_DIR"/test_ca_cert.pem" static void test_read_wired_8021x_peap_mschapv2 (void) @@ -1797,7 +1798,7 @@ test_read_wired_8021x_peap_mschapv2 (void) const char *expected_ca_cert_path; const char *read_ca_cert_path; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-8021x-peap-mschapv2", NULL, TYPE_ETHERNET, &unmanaged); g_assert (!unmanaged); @@ -1897,9 +1898,8 @@ test_read_write_802_1X_subj_matches (void) gs_unref_object NMConnection *reread = NULL; NMSetting8021x *s_8021x; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*missing IEEE_8021X_CA_CERT*peap*"); - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-802-1X-subj-matches", + NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT*peap*"); + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-802-1X-subj-matches", NULL, TYPE_ETHERNET, NULL); g_test_assert_expected_messages (); @@ -1919,16 +1919,14 @@ test_read_write_802_1X_subj_matches (void) g_assert_cmpstr (nm_setting_802_1x_get_phase2_altsubject_match (s_8021x, 0), ==, "x.yourdomain.tld"); g_assert_cmpstr (nm_setting_802_1x_get_phase2_altsubject_match (s_8021x, 1), ==, "y.yourdomain.tld"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!"); + NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!"); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-System_test-wired-802-1X-subj-matches.cexpected", &testfile); g_test_assert_expected_messages (); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!"); + NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!"); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); g_test_assert_expected_messages (); @@ -1961,7 +1959,7 @@ test_read_802_1x_ttls_eapgtc (void) * NMSetting8021x::autheap property. */ - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-802-1x-ttls-eapgtc", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-802-1x-ttls-eapgtc", NULL, TYPE_WIRELESS, NULL); /* ===== 802.1x SETTING ===== */ @@ -1993,7 +1991,7 @@ test_read_write_802_1x_password_raw (void) /* Test that the 802-1x.password-raw is correctly read and written. */ - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-802-1x-password-raw", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-802-1x-password-raw", NULL, TYPE_ETHERNET, NULL); /* ===== 802.1x SETTING ===== */ @@ -2010,7 +2008,7 @@ test_read_write_802_1x_password_raw (void) NM_SETTING_SECRET_FLAG_NONE); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); keyfile = utils_get_keys_path (testfile); @@ -2040,7 +2038,7 @@ test_read_wired_aliases_good (gconstpointer test_data) expected_label = N == 0 ? expected_label_0 : expected_label_3; expected_num_addresses = g_strv_length ((char **) expected_address); - nm_sprintf_buf (path, TEST_IFCFG_DIR "/network-scripts/ifcfg-aliasem%d", N); + nm_sprintf_buf (path, TEST_IFCFG_DIR"/ifcfg-aliasem%d", N); connection = _connection_from_file (path, NULL, TYPE_ETHERNET, NULL); @@ -2079,7 +2077,7 @@ test_read_wired_aliases_good (gconstpointer test_data) g_assert (j < expected_num_addresses); g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 24); - label = nm_ip_address_get_attribute (ip4_addr, "label"); + label = nm_ip_address_get_attribute (ip4_addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL); if (expected_label[j]) g_assert_cmpstr (g_variant_get_string (label, NULL), ==, expected_label[j]); else @@ -2131,7 +2129,7 @@ test_read_wired_aliases_bad (const char *base, const char *expected_id) g_assert (ip4_addr != NULL); g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "192.168.1.5"); g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 24); - g_assert (nm_ip_address_get_attribute (ip4_addr, "label") == NULL); + g_assert (nm_ip_address_get_attribute (ip4_addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL) == NULL); /* Gateway */ g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "192.168.1.1"); @@ -2142,17 +2140,15 @@ test_read_wired_aliases_bad (const char *base, const char *expected_id) static void test_read_wired_aliases_bad_1 (void) { - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*aliasem1:1*has no DEVICE*"); - test_read_wired_aliases_bad (TEST_IFCFG_DIR "/network-scripts/ifcfg-aliasem1", "System aliasem1"); + NMTST_EXPECT_NM_WARN ("*aliasem1:1*has no DEVICE*"); + test_read_wired_aliases_bad (TEST_IFCFG_DIR"/ifcfg-aliasem1", "System aliasem1"); } static void test_read_wired_aliases_bad_2 (void) { - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*aliasem2:1*has invalid DEVICE*"); - test_read_wired_aliases_bad (TEST_IFCFG_DIR "/network-scripts/ifcfg-aliasem2", "System aliasem2"); + NMTST_EXPECT_NM_WARN ("*aliasem2:1*has invalid DEVICE*"); + test_read_wired_aliases_bad (TEST_IFCFG_DIR"/ifcfg-aliasem2", "System aliasem2"); } static void @@ -2166,7 +2162,7 @@ test_read_dns_options (void) const char *options6[] = { "inet6" }; guint32 i, num; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dns-options", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-dns-options", NULL, TYPE_ETHERNET, &unmanaged); g_assert_cmpstr (unmanaged, ==, NULL); @@ -2206,7 +2202,7 @@ test_clear_master (void) shvarFile *f; /* 1. load the bridge slave connection from disk */ - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-bridge-component", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-bridge-component", NULL, TYPE_ETHERNET, &unmanaged); g_assert_cmpstr (unmanaged, ==, NULL); @@ -2218,8 +2214,8 @@ test_clear_master (void) /* 2. write the connection to a new file */ _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-System_test-bridge-component-a.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-System_test-bridge-component-a.cexpected", &testfile); /* 3. clear master and slave-type */ @@ -2235,9 +2231,9 @@ test_clear_master (void) /* 4. update the connection on disk */ _writer_update_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, testfile, - TEST_IFCFG_DIR "/network-scripts/ifcfg-System_test-bridge-component-b.cexpected"); + TEST_IFCFG_DIR"/ifcfg-System_test-bridge-component-b.cexpected"); keyfile = utils_get_keys_path (testfile); g_assert (!g_file_test (keyfile, G_FILE_TEST_EXISTS)); @@ -2322,7 +2318,7 @@ test_write_dns_options (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -2343,7 +2339,7 @@ test_read_wifi_open (void) char expected_mac_address[ETH_ALEN] = { 0x00, 0x16, 0x41, 0x11, 0x22, 0x33 }; const char *expected_ssid = "blahblah"; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-open", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-open", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -2406,7 +2402,7 @@ test_read_wifi_open_auto (void) NMSettingConnection *s_con; NMSettingWireless *s_wireless; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-open-auto", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-open-auto", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -2431,7 +2427,7 @@ test_read_wifi_open_ssid_hex (void) GBytes *ssid; const char *expected_ssid = "blahblah"; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-open-ssid-hex", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-open-ssid-hex", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -2461,7 +2457,7 @@ test_read_wifi_open_ssid_hex_bad (void) GBytes *ssid; const char *expected_ssid = "0x626cxx"; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-bad-hex", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-open-ssid-bad-hex", NULL, TYPE_WIRELESS, NULL); s_con = nm_connection_get_setting_connection (connection); @@ -2491,7 +2487,7 @@ test_read_wifi_open_ssid_quoted (void) GBytes *ssid; const char *expected_ssid = "foo\"bar\\"; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-open-ssid-quoted", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-open-ssid-quoted", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -2526,7 +2522,7 @@ test_read_wifi_wep (void) const char *expected_ssid = "blahblah"; NMWepKeyType key_type; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wep", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wep", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -2600,7 +2596,7 @@ test_read_wifi_wep_adhoc (void) GBytes *ssid; const char *expected_ssid = "blahblah"; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wep-adhoc", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wep-adhoc", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -2667,7 +2663,7 @@ test_read_wifi_wep_passphrase (void) NMSettingWireless *s_wireless; NMSettingWirelessSecurity *s_wsec; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wep-passphrase", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wep-passphrase", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -2704,7 +2700,7 @@ test_read_wifi_wep_40_ascii (void) NMSettingWirelessSecurity *s_wsec; NMWepKeyType key_type; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wep-40-ascii", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wep-40-ascii", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -2744,7 +2740,7 @@ test_read_wifi_wep_104_ascii (void) NMSettingWirelessSecurity *s_wsec; NMWepKeyType key_type; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wep-104-ascii", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wep-104-ascii", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -2783,7 +2779,7 @@ test_read_wifi_leap (void) NMSettingWireless *s_wireless; NMSettingWirelessSecurity *s_wsec; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-leap", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-leap", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -2845,7 +2841,7 @@ test_ifcfg_no_trailing_newline (void) { shvarFile *sv; - sv = _svOpenFile (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wpa-psk"); + sv = _svOpenFile (TEST_IFCFG_DIR"/ifcfg-test-wifi-wpa-psk"); _svGetValue_check (sv, "LAST_ENTRY", "no-newline"); svCloseFile (sv); } @@ -2872,7 +2868,7 @@ test_read_wifi_wpa_psk (void) gboolean found_proto_wpa = FALSE; gboolean found_proto_rsn = FALSE; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wpa-psk", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wpa-psk", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -2981,7 +2977,7 @@ test_read_wifi_wpa_psk_2 (void) NMSettingWireless *s_wireless; NMSettingWirelessSecurity *s_wsec; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wpa-psk-2", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wpa-psk-2", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -3012,7 +3008,7 @@ test_read_wifi_wpa_psk_unquoted (void) NMSettingWireless *s_wireless; NMSettingWirelessSecurity *s_wsec; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wpa-psk-unquoted", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wpa-psk-unquoted", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -3040,7 +3036,7 @@ test_read_wifi_wpa_psk_unquoted2 (void) { gs_unref_object NMConnection *connection = NULL; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wpa-psk-unquoted2", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wpa-psk-unquoted2", NULL, TYPE_WIRELESS, NULL); } @@ -3053,7 +3049,7 @@ test_read_wifi_wpa_psk_adhoc (void) NMSettingWirelessSecurity *s_wsec; NMSettingIPConfig *s_ip4; - connection = _connection_from_file(TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wpa-psk-adhoc", + connection = _connection_from_file(TEST_IFCFG_DIR"/ifcfg-test-wifi-wpa-psk-adhoc", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -3105,7 +3101,7 @@ test_read_wifi_wpa_psk_hex (void) GBytes *ssid; const char *expected_ssid = "blahblah"; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wpa-psk-hex", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wpa-psk-hex", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -3140,9 +3136,9 @@ test_read_wifi_wpa_psk_hex (void) g_object_unref (connection); } -#define TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT TEST_IFCFG_DIR "/network-scripts/test_ca_cert.pem" -#define TEST_IFCFG_WIFI_WPA_EAP_TLS_CLIENT_CERT TEST_IFCFG_DIR "/network-scripts/test1_key_and_cert.pem" -#define TEST_IFCFG_WIFI_WPA_EAP_TLS_PRIVATE_KEY TEST_IFCFG_DIR "/network-scripts/test1_key_and_cert.pem" +#define TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT TEST_IFCFG_DIR"/test_ca_cert.pem" +#define TEST_IFCFG_WIFI_WPA_EAP_TLS_CLIENT_CERT TEST_IFCFG_DIR"/test1_key_and_cert.pem" +#define TEST_IFCFG_WIFI_WPA_EAP_TLS_PRIVATE_KEY TEST_IFCFG_DIR"/test1_key_and_cert.pem" static void test_read_wifi_wpa_eap_tls (void) @@ -3154,7 +3150,7 @@ test_read_wifi_wpa_eap_tls (void) char *unmanaged = NULL; const char *expected_privkey_password = "test1"; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wpa-eap-tls", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wpa-eap-tls", NULL, TYPE_ETHERNET, &unmanaged); g_assert (!unmanaged); @@ -3212,7 +3208,7 @@ test_read_wifi_wpa_eap_ttls_tls (void) char *unmanaged = NULL; const char *expected_privkey_password = "test1"; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wpa-eap-ttls-tls", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wpa-eap-ttls-tls", NULL, TYPE_WIRELESS, &unmanaged); g_assert (!unmanaged); @@ -3236,7 +3232,7 @@ test_read_wifi_wpa_eap_ttls_tls (void) /* CA Cert */ verify_cert_or_key (s_8021x, - TEST_IFCFG_DIR "/network-scripts/test_ca_cert.pem", + TEST_IFCFG_DIR"/test_ca_cert.pem", NULL, NM_SETTING_802_1X_CA_CERT); @@ -3275,7 +3271,7 @@ test_read_wifi_dynamic_wep_leap (void) NMSettingWirelessSecurity *s_wsec; NMSetting8021x *s_8021x; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-dynamic-wep-leap", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-dynamic-wep-leap", NULL, TYPE_WIRELESS, NULL); /* ===== WIRELESS SETTING ===== */ @@ -3324,7 +3320,7 @@ test_read_wifi_wep_eap_ttls_chap (void) NMSetting8021x *s_8021x; char *unmanaged = NULL; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wep-eap-ttls-chap", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wep-eap-ttls-chap", NULL, TYPE_WIRELESS, &unmanaged); g_assert (!unmanaged); @@ -3354,7 +3350,7 @@ test_read_wifi_wep_eap_ttls_chap (void) /* CA Cert */ verify_cert_or_key (s_8021x, - TEST_IFCFG_DIR "/network-scripts/test_ca_cert.pem", + TEST_IFCFG_DIR"/test_ca_cert.pem", NULL, NM_SETTING_802_1X_CA_CERT); @@ -3372,7 +3368,7 @@ test_read_wired_wake_on_lan (void) NMSettingConnection *s_con; NMSettingWired *s_wired; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-wake-on-lan", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-wake-on-lan", NULL, TYPE_WIRELESS, NULL); s_con = nm_connection_get_setting_connection (connection); @@ -3401,7 +3397,7 @@ test_read_wired_auto_negotiate_off (void) NMSettingConnection *s_con; NMSettingWired *s_wired; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-wake-on-lan", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-wake-on-lan", NULL, TYPE_ETHERNET, NULL); s_con = nm_connection_get_setting_connection (connection); @@ -3423,7 +3419,7 @@ test_read_wired_auto_negotiate_on (void) NMSettingConnection *s_con; NMSettingWired *s_wired; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-auto-negotiate-on", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-auto-negotiate-on", NULL, TYPE_ETHERNET, NULL); s_con = nm_connection_get_setting_connection (connection); @@ -3445,7 +3441,7 @@ test_read_wired_unknown_ethtool_opt (void) NMSettingConnection *s_con; NMSettingWired *s_wired; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-unknown-ethtool-opt", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-unknown-ethtool-opt", NULL, TYPE_ETHERNET, NULL); s_con = nm_connection_get_setting_connection (connection); @@ -3476,7 +3472,7 @@ test_read_wifi_hidden (void) NMSettingConnection *s_con; NMSettingWireless *s_wifi; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-hidden", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-hidden", NULL, TYPE_WIRELESS, NULL); s_con = nm_connection_get_setting_connection (connection); @@ -3522,7 +3518,6 @@ test_write_wifi_hidden (void) g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid, - NM_SETTING_WIRELESS_MODE, "infrastructure", NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL); @@ -3531,8 +3526,8 @@ test_write_wifi_hidden (void) nmtst_assert_connection_verifies (connection); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_WiFi_Hidden.cexpected", &testfile); f = _svOpenFile (testfile); @@ -3557,7 +3552,7 @@ test_read_wifi_mac_random (gconstpointer user_data) nmtst_test_data_unpack (user_data, &name, &value_p); value = GPOINTER_TO_INT (value_p); - path = g_strdup_printf (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-mac-random-%s", name); + path = g_strdup_printf (TEST_IFCFG_DIR"/ifcfg-test-wifi-mac-random-%s", name); connection = _connection_from_file (path, NULL, TYPE_WIRELESS, NULL); s_wifi = nm_connection_get_setting_wireless (connection); @@ -3616,8 +3611,8 @@ test_write_wifi_mac_random (gconstpointer user_data) nmtst_assert_connection_verifies (connection); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - nm_sprintf_buf (cexpected, TEST_IFCFG_DIR"/network-scripts/ifcfg-Test_Write_WiFi_MAC_%s.cexpected", name), + TEST_SCRATCH_DIR, + nm_sprintf_buf (cexpected, TEST_IFCFG_DIR"/ifcfg-Test_Write_WiFi_MAC_%s.cexpected", name), &testfile); f = _svOpenFile (testfile); @@ -3669,8 +3664,8 @@ test_write_wired_wake_on_lan (void) nmtst_assert_connection_verifies (connection); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected", &testfile); f = _svOpenFile (testfile); @@ -3705,8 +3700,8 @@ test_write_wired_auto_negotiate_off (void) NULL); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected", &testfile); f = _svOpenFile (testfile); @@ -3740,7 +3735,7 @@ test_write_wired_auto_negotiate_on (void) NULL); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); f = _svOpenFile (testfile); @@ -3764,7 +3759,7 @@ test_read_wifi_band_a (void) NMSettingConnection *s_con; NMSettingWireless *s_wifi; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-band-a", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-band-a", NULL, TYPE_WIRELESS, NULL); s_con = nm_connection_get_setting_connection (connection); @@ -3819,8 +3814,8 @@ test_write_wifi_band_a (void) nmtst_assert_connection_verifies (connection); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_WiFi_Band_A.cexpected", &testfile); f = _svOpenFile (testfile); @@ -3837,7 +3832,7 @@ test_read_wifi_band_a_channel_mismatch (void) { gs_free_error GError *error = NULL; - _connection_from_file_fail (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-band-a-channel-mismatch", + _connection_from_file_fail (TEST_IFCFG_DIR"/ifcfg-test-wifi-band-a-channel-mismatch", NULL, TYPE_WIRELESS, &error); g_assert_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); } @@ -3847,7 +3842,7 @@ test_read_wifi_band_bg_channel_mismatch (void) { gs_free_error GError *error = NULL; - _connection_from_file_fail (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-band-bg-channel-mismatch", + _connection_from_file_fail (TEST_IFCFG_DIR"/ifcfg-test-wifi-band-bg-channel-mismatch", NULL, TYPE_WIRELESS, &error); g_assert_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); } @@ -3862,7 +3857,7 @@ test_read_wired_qeth_static (void) char *unmanaged = NULL; const char * const *subchannels; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-qeth-static", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-qeth-static", NULL, TYPE_ETHERNET, &unmanaged); g_assert (!unmanaged); @@ -3911,7 +3906,7 @@ test_read_wired_ctc_static (void) char *unmanaged = NULL; const char * const *subchannels; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-ctc-static", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-ctc-static", NULL, TYPE_ETHERNET, &unmanaged); g_assert (unmanaged == NULL); @@ -3949,7 +3944,7 @@ test_read_wifi_wep_no_keys (void) NMSettingWirelessSecurity *s_wsec; NMWepKeyType key_type; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wep-no-keys", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wep-no-keys", NULL, TYPE_WIRELESS, NULL); /* ===== CONNECTION SETTING ===== */ @@ -3998,7 +3993,7 @@ test_read_permissions (void) guint32 num; const char *tmp; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-permissions", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-permissions", NULL, TYPE_ETHERNET, NULL); /* ===== CONNECTION SETTING ===== */ @@ -4037,7 +4032,7 @@ test_read_wifi_wep_agent_keys (void) NMWepKeyType key_type; NMSettingSecretFlags flags; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-wep-agent-keys", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-wep-agent-keys", NULL, TYPE_WIRELESS, NULL); /* Ensure the connection is still marked for wifi security even though @@ -4188,7 +4183,7 @@ test_write_wired_static (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); route6file = utils_get_route6_path (testfile); @@ -4328,7 +4323,7 @@ test_write_wired_static_with_generic (void) nmtst_assert_connection_verifies (connection); _writer_new_connection_FIXME (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); route6file = utils_get_route6_path (testfile); @@ -4413,7 +4408,7 @@ test_write_wired_dhcp (void) NULL); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -4428,11 +4423,11 @@ test_write_wired_dhcp_plus_ip (void) gs_unref_object NMConnection *connection = NULL; gs_unref_object NMConnection *reread = NULL; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-dhcp-plus-ip", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-dhcp-plus-ip", NULL, TYPE_ETHERNET, NULL); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -4450,7 +4445,7 @@ test_read_write_wired_dhcp_send_hostname (void) NMSettingIPConfig *s_ip6; const char * dhcp_hostname = "kamil-patka"; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-dhcp-send-hostname", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-dhcp-send-hostname", NULL, TYPE_ETHERNET, NULL); /* Check dhcp-hostname and dhcp-send-hostname */ @@ -4468,7 +4463,7 @@ test_read_write_wired_dhcp_send_hostname (void) g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, dhcp_hostname, NULL); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -4491,7 +4486,7 @@ test_read_wired_dhcpv6_hostname_fallback (void) gs_unref_object NMConnection *connection = NULL; NMSettingIPConfig *s_ip6; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-dhcpv6-hostname-fallback", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-dhcpv6-hostname-fallback", NULL, TYPE_ETHERNET, NULL); s_ip6 = nm_connection_get_setting_ip6_config (connection); @@ -4560,7 +4555,7 @@ test_write_wired_static_ip6_only (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -4599,7 +4594,7 @@ test_write_wired_static_ip6_only_gw (gconstpointer user_data) s_con = (NMSettingConnection *) nm_setting_connection_new (); nm_connection_add_setting (connection, NM_SETTING (s_con)); - id = g_strdup_printf ("Test Write Wired Static IP6 Only With Gateway %s", gateway6 ? gateway6 : "NULL"); + id = g_strdup_printf ("Test Write Wired Static IP6 Only With Gateway %s", gateway6 ?: "NULL"); g_object_set (s_con, NM_SETTING_CONNECTION_ID, id, NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (), @@ -4643,7 +4638,7 @@ test_write_wired_static_ip6_only_gw (gconstpointer user_data) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -4684,7 +4679,7 @@ test_read_write_static_routes_legacy (void) NMSettingIPConfig *s_ip4; const char *tmp; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-static-routes-legacy", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-static-routes-legacy", NULL, TYPE_ETHERNET, NULL); /* ===== CONNECTION SETTING ===== */ @@ -4718,7 +4713,7 @@ test_read_write_static_routes_legacy (void) */ _writer_new_connec_exp (connection, TEST_SCRATCH_DIR_TMP, - TEST_IFCFG_DIR "/network-scripts/ifcfg-test-static-routes-legacy.cexpected", + TEST_IFCFG_DIR"/ifcfg-test-static-routes-legacy.cexpected", &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -4774,6 +4769,7 @@ test_write_wired_static_routes (void) g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NM_SETTING_IP_CONFIG_GATEWAY, "1.1.1.1", + NM_SETTING_IP_CONFIG_DAD_TIMEOUT, 400, NULL); addr = nm_ip_address_new (AF_INET, "1.1.1.3", 24, &error); @@ -4819,9 +4815,9 @@ test_write_wired_static_routes (void) nmtst_assert_connection_verifies (connection); _writer_new_connection_reread (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile, - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected", + TEST_IFCFG_DIR"/ifcfg-Test_Write_Wired_Static_Routes.cexpected", &reread, &reread_same); /* ifcfg does not support setting onlink=0. It gets lost during write+re-read. @@ -4914,7 +4910,7 @@ test_write_wired_dhcp_8021x_peap_mschapv2 (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -4988,7 +4984,7 @@ test_write_wired_8021x_tls (gconstpointer test_data) /* CA cert */ success = nm_setting_802_1x_set_ca_cert (s_8021x, - TEST_IFCFG_DIR "/network-scripts/test_ca_cert.pem", + TEST_IFCFG_DIR"/test_ca_cert.pem", scheme, &format, &error); @@ -4999,7 +4995,7 @@ test_write_wired_8021x_tls (gconstpointer test_data) /* Client cert */ format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; success = nm_setting_802_1x_set_client_cert (s_8021x, - TEST_IFCFG_DIR "/network-scripts/test1_key_and_cert.pem", + TEST_IFCFG_DIR"/test1_key_and_cert.pem", scheme, &format, &error); @@ -5010,7 +5006,7 @@ test_write_wired_8021x_tls (gconstpointer test_data) /* Private key */ format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; success = nm_setting_802_1x_set_private_key (s_8021x, - TEST_IFCFG_DIR "/network-scripts/test1_key_and_cert.pem", + TEST_IFCFG_DIR"/test1_key_and_cert.pem", "test1", scheme, &format, @@ -5025,7 +5021,7 @@ test_write_wired_8021x_tls (gconstpointer test_data) nmtst_assert_connection_verifies (connection); _writer_new_connection_FIXME (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -5087,8 +5083,6 @@ test_write_wired_8021x_tls (gconstpointer test_data) g_free (tmp); } -#define TEST_SCRATCH_ALIAS_BASE TEST_SCRATCH_DIR "/network-scripts/ifcfg-alias0" - static void test_write_wired_aliases (void) { @@ -5141,7 +5135,7 @@ test_write_wired_aliases (void) addr = nm_ip_address_new (AF_INET, ip[i], 24, &error); g_assert_no_error (error); if (label[i]) - nm_ip_address_set_attribute (addr, "label", g_variant_new_string (label[i])); + nm_ip_address_set_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL, g_variant_new_string (label[i])); nm_setting_ip_config_add_address (s_ip4, addr); nm_ip_address_unref (addr); } @@ -5164,7 +5158,7 @@ test_write_wired_aliases (void) g_assert (g_file_test (TEST_SCRATCH_ALIAS_BASE ":5", G_FILE_TEST_EXISTS)); _writer_new_connection_FIXME (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); /* Re-check the alias files */ @@ -5200,9 +5194,9 @@ test_write_wired_aliases (void) else { g_assert_cmpint (nm_ip_address_get_prefix (addr), ==, 24); if (label[j]) - g_assert_cmpstr (g_variant_get_string (nm_ip_address_get_attribute (addr, "label"), NULL), ==, label[j]); + g_assert_cmpstr (g_variant_get_string (nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL), NULL), ==, label[j]); else - g_assert (nm_ip_address_get_attribute (addr, "label") == NULL); + g_assert (nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL) == NULL); ip[j] = NULL; } } @@ -5266,7 +5260,7 @@ test_write_gateway (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); f = _svOpenFile (testfile); @@ -5286,7 +5280,6 @@ test_write_gateway (void) nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); } - static void test_write_wifi_open (void) { @@ -5350,7 +5343,7 @@ test_write_wifi_open (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -5419,7 +5412,7 @@ test_write_wifi_open_hex_ssid (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -5501,7 +5494,7 @@ test_write_wifi_wep (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -5594,7 +5587,7 @@ test_write_wifi_wep_adhoc (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -5679,7 +5672,7 @@ test_write_wifi_wep_passphrase (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -5767,7 +5760,7 @@ test_write_wifi_wep_40_ascii (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -5855,8 +5848,8 @@ test_write_wifi_wep_104_ascii (void) nmtst_assert_connection_verifies (connection); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected", &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -5940,8 +5933,8 @@ test_write_wifi_leap (void) nmtst_assert_connection_verifies (connection); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_Wifi_LEAP.cexpected", &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -6023,7 +6016,7 @@ test_write_wifi_leap_secret_flags (gconstpointer data) nmtst_assert_connection_verifies (connection); _writer_new_connection_FIXME (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -6131,7 +6124,7 @@ test_write_wifi_wpa_psk (gconstpointer test_data) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -6227,7 +6220,7 @@ test_write_wifi_wpa_psk_adhoc (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -6285,7 +6278,9 @@ test_write_wifi_wpa_eap_tls (void) s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); nm_connection_add_setting (connection, NM_SETTING (s_wsec)); - g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", NULL); + g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", + NM_SETTING_WIRELESS_SECURITY_FILS, (int) NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED, + NULL); nm_setting_wireless_security_add_proto (s_wsec, "wpa"); nm_setting_wireless_security_add_pairwise (s_wsec, "tkip"); nm_setting_wireless_security_add_group (s_wsec, "tkip"); @@ -6343,7 +6338,7 @@ test_write_wifi_wpa_eap_tls (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -6470,7 +6465,7 @@ test_write_wifi_wpa_eap_ttls_tls (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -6574,7 +6569,7 @@ test_write_wifi_wpa_eap_ttls_mschapv2 (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -6662,7 +6657,7 @@ test_write_wifi_wpa_then_open (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -6678,9 +6673,9 @@ test_write_wifi_wpa_then_open (void) /* Write it back out */ _writer_update_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, testfile, - TEST_IFCFG_DIR "/network-scripts/ifcfg-random_wifi_connection.cexpected"); + TEST_IFCFG_DIR"/ifcfg-random_wifi_connection.cexpected"); keyfile = utils_get_keys_path (testfile); g_assert (!g_file_test (keyfile, G_FILE_TEST_EXISTS)); @@ -6776,7 +6771,7 @@ test_write_wifi_wpa_then_wep_with_perms (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -6798,9 +6793,9 @@ test_write_wifi_wpa_then_wep_with_perms (void) /* Write it back out */ _writer_update_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, testfile, - TEST_IFCFG_DIR "/network-scripts/ifcfg-random_wifi_connection_2.cexpected"); + TEST_IFCFG_DIR"/ifcfg-random_wifi_connection_2.cexpected"); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -6888,7 +6883,7 @@ test_write_wifi_dynamic_wep_leap (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -6968,7 +6963,7 @@ test_write_wired_qeth_dhcp (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -7030,10 +7025,10 @@ test_write_wired_ctc_dhcp (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); - /* Ensure the CTCPROT item gets written out as it's own option */ + /* Ensure the CTCPROT item gets written out as its own option */ ifcfg = _svOpenFile (testfile); _svGetValue_check (ifcfg, "CTCPROT", "0"); @@ -7100,8 +7095,8 @@ test_write_permissions (void) nmtst_assert_connection_verifies (connection); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Permissions.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_Permissions.cexpected", &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -7175,7 +7170,7 @@ test_write_wifi_wep_agent_keys (void) nmtst_assert_connection_verifies (connection); _writer_new_connection_FIXME (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL); @@ -7246,7 +7241,7 @@ test_write_wired_pppoe (void) nmtst_assert_connection_verifies (connection); _writer_new_connection_fail (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, NULL); g_object_unref (connection); @@ -7298,7 +7293,7 @@ test_write_vpn (void) nmtst_assert_connection_verifies (connection); _writer_new_connection_fail (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, NULL); g_object_unref (connection); @@ -7371,7 +7366,7 @@ test_write_mobile_broadband (gconstpointer data) nmtst_assert_connection_verifies (connection); _writer_new_connection_fail (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, NULL); g_object_unref (connection); @@ -7387,7 +7382,7 @@ test_read_bridge_main (void) const char *mac; char expected_mac_address[ETH_ALEN] = { 0x00, 0x16, 0x41, 0x11, 0x22, 0x33 }; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-bridge-main", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-bridge-main", NULL, TYPE_ETHERNET, NULL); g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "br0"); @@ -7485,7 +7480,7 @@ test_write_bridge_main (void) nmtst_assert_connection_verifies_without_normalization (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_BRIDGE, NULL); @@ -7500,7 +7495,7 @@ test_read_bridge_component (void) NMSettingConnection *s_con; NMSettingBridgePort *s_port; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-bridge-component", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-bridge-component", NULL, TYPE_ETHERNET, NULL); s_con = nm_connection_get_setting_connection (connection); @@ -7565,8 +7560,8 @@ test_write_bridge_component (void) nmtst_assert_connection_verifies (connection); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_Bridge_Component.cexpected", &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -7580,7 +7575,7 @@ test_read_bridge_missing_stp (void) NMConnection *connection; NMSettingBridge *s_bridge; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-bridge-missing-stp", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-bridge-missing-stp", NULL, TYPE_BRIDGE, NULL); g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "br0"); @@ -7594,7 +7589,7 @@ test_read_bridge_missing_stp (void) g_object_unref (connection); } -#define TEST_IFCFG_VLAN_INTERFACE TEST_IFCFG_DIR"/network-scripts/ifcfg-test-vlan-interface" +#define TEST_IFCFG_VLAN_INTERFACE TEST_IFCFG_DIR"/ifcfg-test-vlan-interface" static void test_read_vlan_interface (void) @@ -7645,7 +7640,7 @@ test_read_vlan_interface (void) g_object_unref (connection); } -#define TEST_IFCFG_VLAN_ONLY_VLANID TEST_IFCFG_DIR"/network-scripts/ifcfg-test-vlan-only-vlanid" +#define TEST_IFCFG_VLAN_ONLY_VLANID TEST_IFCFG_DIR"/ifcfg-test-vlan-only-vlanid" static void test_read_vlan_only_vlan_id (void) @@ -7673,7 +7668,7 @@ test_read_vlan_only_device (void) NMConnection *connection; NMSettingVlan *s_vlan; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-vlan-only-device", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-vlan-only-device", NULL, TYPE_ETHERNET, NULL); g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "eth0.9"); @@ -7693,7 +7688,7 @@ test_read_vlan_physdev (void) NMConnection *connection; NMSettingVlan *s_vlan; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-vlan-physdev", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-vlan-physdev", NULL, TYPE_ETHERNET, NULL); g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "vlan0.3"); @@ -7713,9 +7708,8 @@ test_read_vlan_reorder_hdr_1 (void) NMConnection *connection; NMSettingVlan *s_vlan; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*REORDER_HDR key is deprecated, use VLAN_FLAGS*"); - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-vlan-reorder-hdr-1", + NMTST_EXPECT_NM_WARN ("*REORDER_HDR key is deprecated, use VLAN_FLAGS*"); + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-vlan-reorder-hdr-1", NULL, TYPE_ETHERNET, NULL); g_test_assert_expected_messages (); @@ -7738,7 +7732,7 @@ test_read_vlan_reorder_hdr_2 (void) NMConnection *connection; NMSettingVlan *s_vlan; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-vlan-reorder-hdr-2", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-vlan-reorder-hdr-2", NULL, TYPE_ETHERNET, NULL); g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "vlan0.3"); @@ -7760,7 +7754,7 @@ test_read_vlan_flags_1 (void) NMConnection *connection; NMSettingVlan *s_vlan; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-vlan-flags-1", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-vlan-flags-1", NULL, TYPE_ETHERNET, NULL); g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "super-vlan"); @@ -7783,7 +7777,7 @@ test_read_vlan_flags_2 (void) NMConnection *connection; NMSettingVlan *s_vlan; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-vlan-flags-2", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-vlan-flags-2", NULL, TYPE_ETHERNET, NULL); g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "super-vlan"); @@ -7811,8 +7805,8 @@ test_write_vlan (void) NULL, TYPE_VLAN, NULL); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Vlan_test-vlan-interface.cexpected", &testfile); } @@ -7823,11 +7817,11 @@ test_write_vlan_flags (void) gs_unref_object NMConnection *connection = NULL; gs_unref_object NMConnection *reread = NULL; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-vlan-flags-2", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-vlan-flags-2", NULL, TYPE_VLAN, NULL); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -7846,7 +7840,7 @@ test_write_vlan_only_vlanid (void) NULL, TYPE_VLAN, NULL); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -7892,8 +7886,8 @@ test_write_vlan_reorder_hdr (void) NULL); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected", &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -7948,7 +7942,7 @@ test_write_ethernet_missing_ipv6 (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -7961,7 +7955,7 @@ test_read_ibft_ignored (void) { gs_free_error GError *error = NULL; - _connection_from_file_fail (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-ibft", + _connection_from_file_fail (TEST_IFCFG_DIR"/ifcfg-test-ibft", NULL, TYPE_ETHERNET, &error); g_assert_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); } @@ -7972,7 +7966,7 @@ test_read_bond_main (void) NMConnection *connection; NMSettingBond *s_bond; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-bond-main", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-bond-main", NULL, TYPE_ETHERNET,NULL); g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "bond0"); @@ -7993,7 +7987,7 @@ test_read_bond_eth_type (void) NMConnection *connection; NMSettingBond *s_bond; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-bond-eth-type", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-bond-eth-type", NULL, TYPE_ETHERNET,NULL); g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "bond0"); @@ -8077,8 +8071,8 @@ test_write_bond_main (void) nmtst_assert_connection_verifies_without_normalization (connection); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_Bond_Main.cexpected", &testfile); reread = _connection_from_file (testfile, NULL, TYPE_BOND, NULL); @@ -8092,7 +8086,7 @@ test_read_bond_slave (void) NMConnection *connection; NMSettingConnection *s_con; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-bond-slave", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-bond-slave", NULL, TYPE_ETHERNET, NULL); g_test_assert_expected_messages (); @@ -8144,7 +8138,7 @@ test_write_bond_slave (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -8162,7 +8156,7 @@ test_read_infiniband (void) char expected_mac_address[INFINIBAND_ALEN] = { 0x80, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22 }; const char *transport_mode; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-infiniband", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-infiniband", NULL, TYPE_INFINIBAND, &unmanaged); g_assert (!unmanaged); @@ -8248,7 +8242,7 @@ test_write_infiniband (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, TYPE_INFINIBAND, NULL); @@ -8262,7 +8256,7 @@ test_read_bond_slave_ib (void) gs_unref_object NMConnection *connection = NULL; NMSettingConnection *s_con; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-bond-slave-ib", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-bond-slave-ib", NULL, NULL, NULL); s_con = nm_connection_get_setting_connection (connection); @@ -8310,7 +8304,7 @@ test_write_bond_slave_ib (void) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); reread = _connection_from_file (testfile, NULL, NULL, NULL); @@ -8325,7 +8319,7 @@ test_read_bond_opts_mode_numeric (void) NMSettingConnection *s_con; NMSettingBond *s_bond; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-bond-mode-numeric", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-bond-mode-numeric", NULL, TYPE_ETHERNET, NULL); g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "bond0"); @@ -8358,7 +8352,7 @@ test_read_dcb_basic (void) guint expected_traffic_classes[8] = { 7, 6, 5, 4, 3, 2, 1, 0 }; gboolean expected_pfcs[8] = { TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE }; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-dcb", NULL, TYPE_ETHERNET, NULL); s_dcb = nm_connection_get_setting_dcb (connection); @@ -8472,8 +8466,8 @@ test_write_dcb_basic (void) nmtst_assert_connection_verifies (connection); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts//ifcfg-dcb-test.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"//ifcfg-dcb-test.cexpected", &testfile); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); @@ -8487,7 +8481,7 @@ test_read_dcb_default_app_priorities (void) gs_unref_object NMConnection *connection = NULL; NMSettingDcb *s_dcb; - connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-default-app-priorities", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-dcb-default-app-priorities", NULL, TYPE_ETHERNET, NULL); s_dcb = nm_connection_get_setting_dcb (connection); @@ -8508,9 +8502,8 @@ test_read_dcb_bad_booleans (void) { gs_free_error GError *error = NULL; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*invalid DCB_PG_STRICT value*not all 0s and 1s*"); - _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-bad-booleans", + NMTST_EXPECT_NM_WARN ("*invalid DCB_PG_STRICT value*not all 0s and 1s*"); + _connection_from_file_fail (TEST_IFCFG_DIR"/ifcfg-test-dcb-bad-booleans", NULL, TYPE_ETHERNET, &error); g_test_assert_expected_messages (); @@ -8523,9 +8516,8 @@ test_read_dcb_short_booleans (void) { gs_free_error GError *error = NULL; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*DCB_PG_STRICT value*8 characters*"); - _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-short-booleans", + NMTST_EXPECT_NM_WARN ("*DCB_PG_STRICT value*8 characters*"); + _connection_from_file_fail (TEST_IFCFG_DIR"/ifcfg-test-dcb-short-booleans", NULL, TYPE_ETHERNET, &error); g_test_assert_expected_messages (); @@ -8538,9 +8530,8 @@ test_read_dcb_bad_uints (void) { gs_free_error GError *error = NULL; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*invalid DCB_PG_UP2TC value*not 0 - 7*"); - _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-bad-uints", + NMTST_EXPECT_NM_WARN ("*invalid DCB_PG_UP2TC value*not 0 - 7*"); + _connection_from_file_fail (TEST_IFCFG_DIR"/ifcfg-test-dcb-bad-uints", NULL, TYPE_ETHERNET, &error); g_test_assert_expected_messages (); @@ -8553,9 +8544,8 @@ test_read_dcb_short_uints (void) { gs_free_error GError *error = NULL; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*DCB_PG_UP2TC value*8 characters*"); - _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-short-uints", + NMTST_EXPECT_NM_WARN ("*DCB_PG_UP2TC value*8 characters*"); + _connection_from_file_fail (TEST_IFCFG_DIR"/ifcfg-test-dcb-short-uints", NULL, TYPE_ETHERNET, &error); g_test_assert_expected_messages (); @@ -8568,9 +8558,8 @@ test_read_dcb_bad_percent (void) { gs_free_error GError *error = NULL; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*invalid DCB_PG_PCT percentage value*"); - _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-bad-percent", + NMTST_EXPECT_NM_WARN ("*invalid DCB_PG_PCT percentage value*"); + _connection_from_file_fail (TEST_IFCFG_DIR"/ifcfg-test-dcb-bad-percent", NULL, TYPE_ETHERNET, &error); g_test_assert_expected_messages (); @@ -8583,9 +8572,8 @@ test_read_dcb_short_percent (void) { gs_free_error GError *error = NULL; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*invalid DCB_PG_PCT percentage list value*"); - _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-short-percent", + NMTST_EXPECT_NM_WARN ("*invalid DCB_PG_PCT percentage list value*"); + _connection_from_file_fail (TEST_IFCFG_DIR"/ifcfg-test-dcb-short-percent", NULL, TYPE_ETHERNET, &error); g_test_assert_expected_messages (); @@ -8598,9 +8586,8 @@ test_read_dcb_pgpct_not_100 (void) { gs_free_error GError *error = NULL; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*DCB_PG_PCT percentages do not equal 100*"); - _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-pgpct-not-100", + NMTST_EXPECT_NM_WARN ("*DCB_PG_PCT percentages do not equal 100*"); + _connection_from_file_fail (TEST_IFCFG_DIR"/ifcfg-test-dcb-pgpct-not-100", NULL, TYPE_ETHERNET, &error); g_test_assert_expected_messages (); @@ -8616,7 +8603,7 @@ test_read_fcoe_mode (gconstpointer user_data) const char *expected_mode = user_data; NMSettingDcb *s_dcb; - file = g_strdup_printf (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-fcoe-%s", expected_mode); + file = g_strdup_printf (TEST_IFCFG_DIR"/ifcfg-test-fcoe-%s", expected_mode); connection = _connection_from_file (file, NULL, TYPE_ETHERNET, NULL); s_dcb = nm_connection_get_setting_dcb (connection); @@ -8676,7 +8663,7 @@ test_write_fcoe_mode (gconstpointer user_data) nmtst_assert_connection_verifies (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); ifcfg = _svOpenFile (testfile); @@ -8720,7 +8707,7 @@ test_read_team_master_invalid (gconstpointer user_data) NMSettingConnection *s_con; NMSettingTeam *s_team; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*ignoring invalid team configuration*"); + NMTST_EXPECT_NM_WARN ("*ignoring invalid team configuration*"); connection = _connection_from_file (PATH_NAME, NULL, TYPE_ETHERNET, NULL); g_test_assert_expected_messages (); @@ -8797,7 +8784,7 @@ test_write_team_master (void) nmtst_assert_connection_verifies_without_normalization (connection); _writer_new_connection (connection, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile); f = _svOpenFile (testfile); @@ -8871,8 +8858,8 @@ test_write_team_port (void) nmtst_assert_connection_verifies (connection); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Team_Port.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_Team_Port.cexpected", &testfile); f = _svOpenFile (testfile); @@ -8928,8 +8915,8 @@ test_write_team_infiniband_port (void) nmtst_assert_connection_verifies (connection); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_Team_Infiniband_Port.cexpected", &testfile); f = _svOpenFile (testfile); @@ -8951,7 +8938,7 @@ test_read_team_port_empty_config (void) NMConnection *connection; NMSettingConnection *s_con; - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-team-port-empty-config", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-team-port-empty-config", NULL, TYPE_ETHERNET, NULL); s_con = nm_connection_get_setting_connection (connection); @@ -9016,9 +9003,9 @@ test_team_reread_slave (void) nmtst_assert_connection_equals (connection_1, FALSE, connection_2, FALSE); _writer_new_connection_reread ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, - TEST_SCRATCH_DIR "/network-scripts/", + TEST_SCRATCH_DIR, &testfile, - TEST_IFCFG_DIR "/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected", + TEST_IFCFG_DIR"/ifcfg-team-slave-enp31s0f1-142.cexpected", &reread, &reread_same); _assert_reread_same ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, reread); @@ -9039,7 +9026,7 @@ test_read_proxy_basic (void) /* Test basic proxy configuration */ - connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-read-proxy-basic", + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-read-proxy-basic", NULL, TYPE_ETHERNET, NULL); /* ===== Proxy setting ===== */ @@ -9091,8 +9078,8 @@ test_write_proxy_basic (void) nmtst_assert_connection_verifies (connection); _writer_new_connec_exp (connection, - TEST_SCRATCH_DIR "/network-scripts/", - TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected", + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_Proxy_Basic.cexpected", &testfile); f = _svOpenFile (testfile); @@ -9487,7 +9474,7 @@ test_write_unknown (gconstpointer test_data) static void test_read_vlan_trailing_spaces (void) { - const char *testfile = TEST_IFCFG_DIR"/network-scripts/ifcfg-test-vlan-trailing-spaces"; + const char *testfile = TEST_IFCFG_DIR"/ifcfg-test-vlan-trailing-spaces"; NMConnection *connection; gboolean success; GError *error = NULL; @@ -9525,7 +9512,7 @@ test_sit_read_ignore (void) { gs_free_error GError *error = NULL; - _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-sit-ignore", + _connection_from_file_fail (TEST_IFCFG_DIR"/ifcfg-test-sit-ignore", NULL, TYPE_ETHERNET, &error); nmtst_assert_error (error, 0, 0, "*Ignoring unsupported connection due to IPV6TUNNELIPV4*"); } @@ -9645,12 +9632,131 @@ test_utils_ignore (void) do_test_utils_ignored ("ignored-augtmp", "ifcfg-FooBar" AUGTMP_TAG, TRUE); } +static void +test_tc_read (void) +{ + NMConnection *connection; + NMSettingTCConfig *s_tc; + NMTCQdisc *qdisc; + NMTCTfilter *filter; + char *str; + + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-tc", + NULL, TYPE_ETHERNET,NULL); + + g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "eth0"); + + s_tc = nm_connection_get_setting_tc_config (connection); + g_assert (s_tc); + + g_assert_cmpint (nm_setting_tc_config_get_num_qdiscs (s_tc), ==, 1); + qdisc = nm_setting_tc_config_get_qdisc (s_tc, 0); + g_assert (qdisc); + g_assert_cmpint (nm_tc_qdisc_get_parent (qdisc), ==, TC_H_ROOT); + g_assert_cmpint (nm_tc_qdisc_get_handle (qdisc), ==, TC_H_UNSPEC); + g_assert_cmpstr (nm_tc_qdisc_get_kind (qdisc), ==, "fq_codel"); + + g_assert_cmpint (nm_setting_tc_config_get_num_tfilters (s_tc), ==, 1); + filter = nm_setting_tc_config_get_tfilter (s_tc, 0); + g_assert (filter); + str = nm_utils_tc_tfilter_to_str (filter, NULL); + g_assert_cmpstr (str, ==, "parent 1234: matchall action simple sdata Hello"); + g_free (str); + + g_object_unref (connection); +} + +static void +test_tc_write (void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection *s_con; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + NMSettingWired *s_wired; + NMSettingTCConfig *s_tc; + NMTCQdisc *qdisc; + NMTCTfilter *tfilter; + NMIPAddress *addr; + GError *error = NULL; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write TC config", + NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (), + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_INTERFACE_NAME, "eth0", + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, "1.1.1.1", + NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE, + NULL); + + addr = nm_ip_address_new (AF_INET, "1.1.1.3", 24, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_address (s_ip4, addr); + nm_ip_address_unref (addr); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NULL); + + /* TC setting */ + s_tc = (NMSettingTCConfig *) nm_setting_tc_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_tc)); + + qdisc = nm_tc_qdisc_new ("pfifo_fast", TC_H_MAKE (0x2468 << 16, 0x2), &error); + g_assert_no_error (error); + nm_setting_tc_config_add_qdisc (s_tc, qdisc); + nm_tc_qdisc_unref (qdisc); + + tfilter = nm_utils_tc_tfilter_from_str ("parent 1234: matchall action simple sdata Hello", &error); + g_assert_no_error (error); + nm_setting_tc_config_add_tfilter (s_tc, tfilter); + nm_tc_tfilter_unref (tfilter); + + nm_connection_add_setting (connection, nm_setting_proxy_new ()); + + nmtst_assert_connection_verifies_without_normalization (connection); + + _writer_new_connec_exp (connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-test-tc-write.cexpected", + &testfile); + + reread = _connection_from_file (testfile, NULL, TYPE_BOND, NULL); + + nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); +} + /*****************************************************************************/ #define TPATH "/settings/plugins/ifcfg-rh/" -#define TEST_IFCFG_WIFI_OPEN_SSID_LONG_QUOTED TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-long-quoted" -#define TEST_IFCFG_WIFI_OPEN_SSID_LONG_HEX TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-long-hex" +#define TEST_IFCFG_WIFI_OPEN_SSID_LONG_QUOTED TEST_IFCFG_DIR"/ifcfg-test-wifi-open-ssid-long-quoted" +#define TEST_IFCFG_WIFI_OPEN_SSID_LONG_HEX TEST_IFCFG_DIR"/ifcfg-test-wifi-open-ssid-long-hex" #define DEFAULT_HEX_PSK "7d308b11df1b4243b0f78e5f3fc68cdbb9a264ed0edf4c188edf329ff5b467f0" @@ -9665,10 +9771,10 @@ int main (int argc, char **argv) g_test_add_func (TPATH "svUnescape", test_svUnescape); - g_test_add_data_func (TPATH "write-unknown/1", TEST_IFCFG_DIR"/network-scripts/ifcfg-test-write-unknown-1", test_write_unknown); - g_test_add_data_func (TPATH "write-unknown/2", TEST_IFCFG_DIR"/network-scripts/ifcfg-test-write-unknown-2", test_write_unknown); - g_test_add_data_func (TPATH "write-unknown/3", TEST_IFCFG_DIR"/network-scripts/ifcfg-test-write-unknown-3", test_write_unknown); - g_test_add_data_func (TPATH "write-unknown/4", TEST_IFCFG_DIR"/network-scripts/ifcfg-test-write-unknown-4", test_write_unknown); + g_test_add_data_func (TPATH "write-unknown/1", TEST_IFCFG_DIR"/ifcfg-test-write-unknown-1", test_write_unknown); + g_test_add_data_func (TPATH "write-unknown/2", TEST_IFCFG_DIR"/ifcfg-test-write-unknown-2", test_write_unknown); + g_test_add_data_func (TPATH "write-unknown/3", TEST_IFCFG_DIR"/ifcfg-test-write-unknown-3", test_write_unknown); + g_test_add_data_func (TPATH "write-unknown/4", TEST_IFCFG_DIR"/ifcfg-test-write-unknown-4", test_write_unknown); g_test_add_func (TPATH "vlan-trailing-spaces", test_read_vlan_trailing_spaces); @@ -9688,8 +9794,8 @@ int main (int argc, char **argv) g_test_add_func (TPATH "read-dns-options", test_read_dns_options); g_test_add_func (TPATH "clear-master", test_clear_master); - nmtst_add_test_func (TPATH "read-static", test_read_wired_static, TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-static", "System test-wired-static", GINT_TO_POINTER (TRUE)); - nmtst_add_test_func (TPATH "read-static-bootproto", test_read_wired_static, TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-static-bootproto", "System test-wired-static-bootproto", GINT_TO_POINTER (FALSE)); + nmtst_add_test_func (TPATH "read-static", test_read_wired_static, TEST_IFCFG_DIR"/ifcfg-test-wired-static", "System test-wired-static", GINT_TO_POINTER (TRUE)); + nmtst_add_test_func (TPATH "read-static-bootproto", test_read_wired_static, TEST_IFCFG_DIR"/ifcfg-test-wired-static-bootproto", "System test-wired-static-bootproto", GINT_TO_POINTER (FALSE)); g_test_add_func (TPATH "read-netmask-1", test_read_netmask_1); @@ -9707,17 +9813,17 @@ int main (int argc, char **argv) g_test_add_func (TPATH "routes/read-static", test_read_wired_static_routes); g_test_add_func (TPATH "routes/read-static-legacy", test_read_wired_static_routes_legacy); - nmtst_add_test_func (TPATH "wired/read/manual/1", test_read_wired_ipv4_manual, TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-ipv4-manual-1", "System test-wired-ipv4-manual-1"); - nmtst_add_test_func (TPATH "wired/read/manual/2", test_read_wired_ipv4_manual, TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-ipv4-manual-2", "System test-wired-ipv4-manual-2"); - nmtst_add_test_func (TPATH "wired/read/manual/3", test_read_wired_ipv4_manual, TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-ipv4-manual-3", "System test-wired-ipv4-manual-3"); - nmtst_add_test_func (TPATH "wired/read/manual/4", test_read_wired_ipv4_manual, TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-ipv4-manual-4", "System test-wired-ipv4-manual-4"); + nmtst_add_test_func (TPATH "wired/read/manual/1", test_read_wired_ipv4_manual, TEST_IFCFG_DIR"/ifcfg-test-wired-ipv4-manual-1", "System test-wired-ipv4-manual-1"); + nmtst_add_test_func (TPATH "wired/read/manual/2", test_read_wired_ipv4_manual, TEST_IFCFG_DIR"/ifcfg-test-wired-ipv4-manual-2", "System test-wired-ipv4-manual-2"); + nmtst_add_test_func (TPATH "wired/read/manual/3", test_read_wired_ipv4_manual, TEST_IFCFG_DIR"/ifcfg-test-wired-ipv4-manual-3", "System test-wired-ipv4-manual-3"); + nmtst_add_test_func (TPATH "wired/read/manual/4", test_read_wired_ipv4_manual, TEST_IFCFG_DIR"/ifcfg-test-wired-ipv4-manual-4", "System test-wired-ipv4-manual-4"); g_test_add_func (TPATH "user/1", test_user_1); g_test_add_func (TPATH "wired/ipv6-manual", test_read_wired_ipv6_manual); - nmtst_add_test_func (TPATH "wired-ipv6-only/0", test_read_wired_ipv6_only, TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-ipv6-only", "System test-wired-ipv6-only"); - nmtst_add_test_func (TPATH "wired-ipv6-only/1", test_read_wired_ipv6_only, TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-ipv6-only-1", "System test-wired-ipv6-only-1"); + nmtst_add_test_func (TPATH "wired-ipv6-only/0", test_read_wired_ipv6_only, TEST_IFCFG_DIR"/ifcfg-test-wired-ipv6-only", "System test-wired-ipv6-only"); + nmtst_add_test_func (TPATH "wired-ipv6-only/1", test_read_wired_ipv6_only, TEST_IFCFG_DIR"/ifcfg-test-wired-ipv6-only-1", "System test-wired-ipv6-only-1"); g_test_add_func (TPATH "wired/dhcpv6-only", test_read_wired_dhcp6_only); g_test_add_func (TPATH "wired/autoip", test_read_wired_autoip); @@ -9725,8 +9831,8 @@ int main (int argc, char **argv) g_test_add_func (TPATH "wired/no-ip", test_read_noip); g_test_add_func (TPATH "802-1x/peap/mschapv2", test_read_wired_8021x_peap_mschapv2); - nmtst_add_test_func (TPATH "test-wired-8021x-tls/agent", test_read_wired_8021x_tls_secret_flags, TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-8021x-tls-agent", GINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); - nmtst_add_test_func (TPATH "test-wired-8021x-tls/always", test_read_wired_8021x_tls_secret_flags, TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-8021x-tls-always", GINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED)); + nmtst_add_test_func (TPATH "test-wired-8021x-tls/agent", test_read_wired_8021x_tls_secret_flags, TEST_IFCFG_DIR"/ifcfg-test-wired-8021x-tls-agent", GINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); + nmtst_add_test_func (TPATH "test-wired-8021x-tls/always", test_read_wired_8021x_tls_secret_flags, TEST_IFCFG_DIR"/ifcfg-test-wired-8021x-tls-always", GINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED)); g_test_add_func (TPATH "802-1x/subj-matches", test_read_write_802_1X_subj_matches); g_test_add_func (TPATH "802-1x/ttls-eapgtc", test_read_802_1x_ttls_eapgtc); @@ -9749,8 +9855,8 @@ int main (int argc, char **argv) g_test_add_func (TPATH "wifi/read/wep/104-ascii", test_read_wifi_wep_104_ascii); g_test_add_func (TPATH "wifi/read/leap", test_read_wifi_leap); - nmtst_add_test_func (TPATH "wifi-leap-secret-flags/agent", test_read_wifi_leap_secret_flags, TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-leap-agent", GINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); - nmtst_add_test_func (TPATH "wifi-leap-secret-flags/ask", test_read_wifi_leap_secret_flags, TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wifi-leap-always-ask", GINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED)); + nmtst_add_test_func (TPATH "wifi-leap-secret-flags/agent", test_read_wifi_leap_secret_flags, TEST_IFCFG_DIR"/ifcfg-test-wifi-leap-agent", GINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); + nmtst_add_test_func (TPATH "wifi-leap-secret-flags/ask", test_read_wifi_leap_secret_flags, TEST_IFCFG_DIR"/ifcfg-test-wifi-leap-always-ask", GINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED)); g_test_add_func (TPATH "wifi/read/wpa-psk", test_read_wifi_wpa_psk); g_test_add_func (TPATH "wifi/read/wpa-psk/2", test_read_wifi_wpa_psk_2); @@ -9899,12 +10005,12 @@ int main (int argc, char **argv) g_test_add_func (TPATH "bridge/write-component", test_write_bridge_component); g_test_add_func (TPATH "bridge/read-missing-stp", test_read_bridge_missing_stp); - g_test_add_data_func (TPATH "team/read-master-1", TEST_IFCFG_DIR"/network-scripts/ifcfg-test-team-master-1", test_read_team_master); - g_test_add_data_func (TPATH "team/read-master-2", TEST_IFCFG_DIR"/network-scripts/ifcfg-test-team-master-2", test_read_team_master); - g_test_add_data_func (TPATH "team/read-master-invalid", TEST_IFCFG_DIR"/network-scripts/ifcfg-test-team-master-invalid", test_read_team_master_invalid); + g_test_add_data_func (TPATH "team/read-master-1", TEST_IFCFG_DIR"/ifcfg-test-team-master-1", test_read_team_master); + g_test_add_data_func (TPATH "team/read-master-2", TEST_IFCFG_DIR"/ifcfg-test-team-master-2", test_read_team_master); + g_test_add_data_func (TPATH "team/read-master-invalid", TEST_IFCFG_DIR"/ifcfg-test-team-master-invalid", test_read_team_master_invalid); g_test_add_func (TPATH "team/write-master", test_write_team_master); - g_test_add_data_func (TPATH "team/read-port-1", TEST_IFCFG_DIR"/network-scripts/ifcfg-test-team-port-1", test_read_team_port); - g_test_add_data_func (TPATH "team/read-port-2", TEST_IFCFG_DIR"/network-scripts/ifcfg-test-team-port-2", test_read_team_port); + g_test_add_data_func (TPATH "team/read-port-1", TEST_IFCFG_DIR"/ifcfg-test-team-port-1", test_read_team_port); + g_test_add_data_func (TPATH "team/read-port-2", TEST_IFCFG_DIR"/ifcfg-test-team-port-2", test_read_team_port); g_test_add_func (TPATH "team/write-port", test_write_team_port); g_test_add_func (TPATH "team/write-infiniband-port", test_write_team_infiniband_port); g_test_add_func (TPATH "team/read-port-empty-config", test_read_team_port_empty_config); @@ -9927,5 +10033,8 @@ int main (int argc, char **argv) g_test_add_func (TPATH "utils/path", test_utils_path); g_test_add_func (TPATH "utils/ignore", test_utils_ignore); + g_test_add_func (TPATH "tc/read", test_tc_read); + g_test_add_func (TPATH "tc/write", test_tc_write); + return g_test_run (); } diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c deleted file mode 100644 index ed0a757f..00000000 --- a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c +++ /dev/null @@ -1,2912 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * Mu Qiao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 1999-2010 Gentoo Foundation, Inc. - */ - -#include "nm-default.h" - -#include "nms-ifnet-connection-parser.h" - -#include -#include -#include -#include - -#include "settings/nm-settings-plugin.h" -#include "nm-core-internal.h" -#include "NetworkManagerUtils.h" -#include "nm-meta-setting.h" - -#include "nms-ifnet-net-utils.h" -#include "nms-ifnet-wpa-parser.h" -#include "nms-ifnet-connection.h" - -static char * -connection_id_from_ifnet_name (const char *conn_name) -{ - int name_len = strlen (conn_name); - - /* Convert a hex-encoded conn_name (only used for wifi SSIDs) to human-readable one */ - if ((name_len > 2) && (g_str_has_prefix (conn_name, "0x"))) { - GBytes *bytes = nm_utils_hexstr2bin (conn_name); - char *buf; - - if (bytes) { - buf = g_strndup (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes)); - g_bytes_unref (bytes); - return buf; - } - } - - return g_strdup (conn_name); -} - -static gboolean eap_simple_reader (const char *eap_method, - const char *ssid, - NMSetting8021x *s_8021x, - gboolean phase2, - const char *basepath, - GError **error); - -static gboolean eap_tls_reader (const char *eap_method, - const char *ssid, - NMSetting8021x *s_8021x, - gboolean phase2, - const char *basepath, - GError **error); - -static gboolean eap_peap_reader (const char *eap_method, - const char *ssid, - NMSetting8021x *s_8021x, - gboolean phase2, - const char *basepath, - GError **error); - -static gboolean eap_ttls_reader (const char *eap_method, - const char *ssid, - NMSetting8021x *s_8021x, - gboolean phase2, - const char *basepath, - GError **error); - -typedef struct { - const char *method; - gboolean (*reader) (const char *eap_method, - const char *ssid, - NMSetting8021x *s_8021x, - gboolean phase2, - const char *basepath, - GError **error); - gboolean wifi_phase2_only; -} EAPReader; - -static EAPReader eap_readers[] = { - {"md5", eap_simple_reader, TRUE}, - {"pwd", eap_simple_reader, TRUE}, - {"pap", eap_simple_reader, TRUE}, - {"chap", eap_simple_reader, TRUE}, - {"mschap", eap_simple_reader, TRUE}, - {"mschapv2", eap_simple_reader, TRUE}, - {"leap", eap_simple_reader, TRUE}, - {"tls", eap_tls_reader, FALSE}, - {"peap", eap_peap_reader, FALSE}, - {"ttls", eap_ttls_reader, FALSE}, - {NULL, NULL} -}; - -/* reading identity and password */ -static gboolean -eap_simple_reader (const char *eap_method, - const char *ssid, - NMSetting8021x *s_8021x, - gboolean phase2, - const char *basepath, - GError **error) -{ - const char *value; - - /* identity */ - value = wpa_get_value (ssid, "identity"); - if (!value) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing IEEE_8021X_IDENTITY for EAP method '%s'.", - eap_method); - return FALSE; - } - g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL); - - /* password */ - value = wpa_get_value (ssid, "password"); - if (!value) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing IEEE_8021X_PASSWORD for EAP method '%s'.", - eap_method); - return FALSE; - } - - g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD, value, NULL); - - return TRUE; -} - -static char * -get_cert (const char *ssid, const char *key, const char *basepath) -{ - const char *orig; - - /* If it's a relative path, convert to absolute using 'basepath' */ - orig = wpa_get_value (ssid, key); - if (g_path_is_absolute (orig)) - return g_strdup (orig); - return g_strdup_printf ("%s/%s", basepath, orig); -} - -static gboolean -eap_tls_reader (const char *eap_method, - const char *ssid, - NMSetting8021x *s_8021x, - gboolean phase2, - const char *basepath, - GError **error) -{ - const char *value; - char *ca_cert = NULL; - char *client_cert = NULL; - char *privkey = NULL; - const char *privkey_password = NULL; - gboolean success = FALSE; - NMSetting8021xCKFormat privkey_format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - - /* identity */ - value = wpa_get_value (ssid, "identity"); - if (!value) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing IEEE_8021X_IDENTITY for EAP method '%s'.", - eap_method); - return FALSE; - } - g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL); - - /* ca cert */ - ca_cert = get_cert (ssid, phase2 ? "ca_cert2" : "ca_cert", basepath); - if (ca_cert) { - if (phase2) { - if (!nm_setting_802_1x_set_phase2_ca_cert (s_8021x, - ca_cert, - NM_SETTING_802_1X_CK_SCHEME_PATH, - NULL, error)) - goto done; - } else { - if (!nm_setting_802_1x_set_ca_cert (s_8021x, - ca_cert, - NM_SETTING_802_1X_CK_SCHEME_PATH, - NULL, error)) - goto done; - } - } else { - nm_log_warn (LOGD_SETTINGS, " missing %s for EAP method '%s'; this is insecure!", - phase2 ? "IEEE_8021X_INNER_CA_CERT" : - "IEEE_8021X_CA_CERT", eap_method); - } - - /* Private key password */ - privkey_password = wpa_get_value (ssid, - phase2 ? "private_key2_passwd" : - "private_key_passwd"); - - if (!privkey_password) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing %s for EAP method '%s'.", - phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD" : - "IEEE_8021X_PRIVATE_KEY_PASSWORD", eap_method); - goto done; - } - - /* The private key itself */ - privkey = get_cert (ssid, phase2 ? "private_key2" : "private_key", basepath); - if (!privkey) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing %s for EAP method '%s'.", - phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : - "IEEE_8021X_PRIVATE_KEY", eap_method); - goto done; - } - - if (phase2) { - if (!nm_setting_802_1x_set_phase2_private_key (s_8021x, - privkey, - privkey_password, - NM_SETTING_802_1X_CK_SCHEME_PATH, - &privkey_format, - error)) - goto done; - } else { - if (!nm_setting_802_1x_set_private_key (s_8021x, - privkey, - privkey_password, - NM_SETTING_802_1X_CK_SCHEME_PATH, - &privkey_format, error)) - goto done; - } - - /* Only set the client certificate if the private key is not PKCS#12 format, - * as NM (due to supplicant restrictions) requires. If the key was PKCS#12, - * then nm_setting_802_1x_set_private_key() already set the client certificate - * to the same value as the private key. - */ - if (privkey_format == NM_SETTING_802_1X_CK_FORMAT_RAW_KEY - || privkey_format == NM_SETTING_802_1X_CK_FORMAT_X509) { - client_cert = get_cert (ssid, phase2 ? "client_cert2" : "client_cert", basepath); - if (!client_cert) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing %s for EAP method '%s'.", - phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : - "IEEE_8021X_CLIENT_CERT", eap_method); - goto done; - } - - if (phase2) { - if (!nm_setting_802_1x_set_phase2_client_cert (s_8021x, - client_cert, - NM_SETTING_802_1X_CK_SCHEME_PATH, - NULL, - error)) - goto done; - } else { - if (!nm_setting_802_1x_set_client_cert (s_8021x, - client_cert, - NM_SETTING_802_1X_CK_SCHEME_PATH, - NULL, error)) - goto done; - } - } - - success = TRUE; - -done: - g_free (ca_cert); - g_free (client_cert); - g_free (privkey); - return success; -} - -static gboolean -eap_peap_reader (const char *eap_method, - const char *ssid, - NMSetting8021x *s_8021x, - gboolean phase2, - const char *basepath, - GError **error) -{ - char *ca_cert = NULL; - const char *inner_auth = NULL; - const char *peapver = NULL; - char **list = NULL, **iter, *lower; - gboolean success = FALSE; - - ca_cert = get_cert (ssid, "ca_cert", basepath); - if (ca_cert) { - if (!nm_setting_802_1x_set_ca_cert (s_8021x, - ca_cert, - NM_SETTING_802_1X_CK_SCHEME_PATH, - NULL, error)) - goto done; - } else { - nm_log_warn (LOGD_SETTINGS, " missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!", - eap_method); - } - - peapver = wpa_get_value (ssid, "phase1"); - /* peap version, default is automatic */ - if (peapver && strstr (peapver, "peapver")) { - if (strstr (peapver, "peapver=0")) - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, - "0", NULL); - else if (strstr (peapver, "peapver=1")) - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, - "1", NULL); - else { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Unknown IEEE_8021X_PEAP_VERSION value '%s'", - peapver); - goto done; - } - } - - /* peaplabel */ - if (peapver && strstr (peapver, "peaplabel=1")) - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", - NULL); - - inner_auth = wpa_get_value (ssid, "phase2"); - if (!inner_auth) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing IEEE_8021X_INNER_AUTH_METHODS."); - goto done; - } - /* Handle options for the inner auth method */ - list = g_strsplit (inner_auth, " ", 0); - for (iter = list; iter && *iter; iter++) { - gchar *pos = NULL; - - if (!strlen (*iter)) - continue; - - if (!(pos = strstr (*iter, "MSCHAPV2")) - || !(pos = strstr (*iter, "MD5")) - || !(pos = strstr (*iter, "GTC"))) { - if (!eap_simple_reader (pos, ssid, s_8021x, TRUE, basepath, error)) - goto done; - } else if (!(pos = strstr (*iter, "TLS"))) { - if (!eap_tls_reader (pos, ssid, s_8021x, TRUE, basepath, error)) - goto done; - } else { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.", - *iter); - goto done; - } - - pos = strchr (*iter, '='); - if (pos && *pos) { - pos++; - lower = g_ascii_strdown (pos, -1); - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, - NULL); - g_free (lower); - } else { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "No IEEE_8021X_INNER_AUTH_METHOD."); - goto done; - } - break; - } - - if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "No valid IEEE_8021X_INNER_AUTH_METHODS found."); - goto done; - } - - success = TRUE; - -done: - g_free (ca_cert); - if (list) - g_strfreev (list); - return success; -} - -static gboolean -eap_ttls_reader (const char *eap_method, - const char *ssid, - NMSetting8021x *s_8021x, - gboolean phase2, - const char *basepath, - GError **error) -{ - gboolean success = FALSE; - const char *anon_ident = NULL; - char *ca_cert = NULL; - const char *tmp; - char **list = NULL, **iter, *inner_auth = NULL; - - /* ca cert */ - ca_cert = get_cert (ssid, "ca_cert", basepath); - if (ca_cert) { - if (!nm_setting_802_1x_set_ca_cert (s_8021x, - ca_cert, - NM_SETTING_802_1X_CK_SCHEME_PATH, - NULL, error)) - goto done; - } else { - nm_log_warn (LOGD_SETTINGS, " missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!", - eap_method); - } - - /* anonymous indentity for tls */ - anon_ident = wpa_get_value (ssid, "anonymous_identity"); - if (anon_ident && strlen (anon_ident)) - g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, - anon_ident, NULL); - - tmp = wpa_get_value (ssid, "phase2"); - if (!tmp) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing IEEE_8021X_INNER_AUTH_METHODS."); - goto done; - } - - /* Handle options for the inner auth method */ - inner_auth = g_ascii_strdown (tmp, -1); - list = g_strsplit (inner_auth, " ", 0); - for (iter = list; iter && *iter; iter++) { - gchar *pos = NULL; - - if (!strlen (*iter)) - continue; - if ((pos = strstr (*iter, "mschapv2")) != NULL - || (pos = strstr (*iter, "mschap")) != NULL - || (pos = strstr (*iter, "pap")) != NULL - || (pos = strstr (*iter, "chap")) != NULL) { - if (!eap_simple_reader (pos, ssid, s_8021x, TRUE, basepath, error)) - goto done; - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, - pos, NULL); - } else if ((pos = strstr (*iter, "tls")) != NULL) { - if (!eap_tls_reader (pos, ssid, s_8021x, TRUE, basepath, error)) - goto done; - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, - "tls", NULL); - } else if ((pos = strstr (*iter, "mschapv2")) != NULL - || (pos = strstr (*iter, "md5")) != NULL) { - if (!eap_simple_reader (pos, ssid, s_8021x, TRUE, basepath, error)) { - nm_log_warn (LOGD_SETTINGS, "SIMPLE ERROR"); - goto done; - } - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, - pos, NULL); - } else { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.", - *iter); - goto done; - } - break; - } - - success = TRUE; -done: - g_free (ca_cert); - if (list) - g_strfreev (list); - g_free (inner_auth); - return success; -} - -/* type is already decided by net_parser, this function is just used to - * doing tansformation*/ -static const gchar * -guess_connection_type (const char *conn_name) -{ - const gchar *type = ifnet_get_data (conn_name, "type"); - const gchar *ret_type = NULL; - - if (!g_strcmp0 (type, "ppp")) - ret_type = NM_SETTING_PPPOE_SETTING_NAME; - - if (!g_strcmp0 (type, "wireless")) - ret_type = NM_SETTING_WIRELESS_SETTING_NAME; - - if (!ret_type) - ret_type = NM_SETTING_WIRED_SETTING_NAME; - - nm_log_info (LOGD_SETTINGS, "guessed connection type (%s) = %s", conn_name, ret_type); - return ret_type; -} - -/* Reading mac address for setting connection option. - * Unmanaged device mac address is required by NetworkManager*/ -static gboolean -read_mac_address (const char *conn_name, const char **mac, GError **error) -{ - const char *value = ifnet_get_data (conn_name, "mac"); - - if (!value || !strlen (value)) - return TRUE; - - if (!nm_utils_hwaddr_valid (value, ETH_ALEN)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "The MAC address '%s' was invalid.", value); - return FALSE; - } - - *mac = value; - return TRUE; -} - -static gboolean -make_wired_connection_setting (NMConnection *connection, - const char *conn_name, - GError **error) -{ - const char *mac = NULL; - NMSettingWired *s_wired = NULL; - const char *value = NULL; - - s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); - - /* mtu_xxx */ - value = ifnet_get_data (conn_name, "mtu"); - if (value) { - long int mtu; - - errno = 0; - mtu = strtol (value, NULL, 10); - if (errno || mtu < 0 || mtu > 65535) { - nm_log_warn (LOGD_SETTINGS, " invalid MTU '%s' for %s", value, conn_name); - } else - g_object_set (s_wired, NM_SETTING_WIRED_MTU, - (guint32) mtu, NULL); - } - - if (!read_mac_address (conn_name, &mac, error)) { - g_object_unref (s_wired); - return FALSE; - } - - if (mac) - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL); - nm_connection_add_setting (connection, NM_SETTING (s_wired)); - - return TRUE; -} - -/* add NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, - * NM_SETTING_IP_CONFIG_DHCP_CLIENT_ID in future*/ -static gboolean -make_ip4_setting (NMConnection *connection, - const char *conn_name, - GError **error) -{ - - NMSettingIPConfig *ip4_setting = - NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); - const char *value, *method = NULL; - gboolean is_static_block = is_static_ip4 (conn_name); - ip_block *iblock = NULL; - - /* set dhcp options (dhcp_xxx) */ - value = ifnet_get_data (conn_name, "dhcp"); - g_object_set (ip4_setting, NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, value - && strstr (value, "nodns") ? TRUE : FALSE, - NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, value - && strstr (value, "nogateway") ? TRUE : FALSE, NULL); - - if (!is_static_block) { - method = ifnet_get_data (conn_name, "config"); - if (!method){ - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Unknown config for %s", conn_name); - g_object_unref (ip4_setting); - return FALSE; - } - if (strstr (method, "dhcp")) - g_object_set (ip4_setting, - NM_SETTING_IP_CONFIG_METHOD, - NM_SETTING_IP4_CONFIG_METHOD_AUTO, - NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL); - else if (strstr (method, "autoip")) { - g_object_set (ip4_setting, - NM_SETTING_IP_CONFIG_METHOD, - NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL, - NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL); - nm_connection_add_setting (connection, NM_SETTING (ip4_setting)); - return TRUE; - } else if (strstr (method, "shared")) { - g_object_set (ip4_setting, - NM_SETTING_IP_CONFIG_METHOD, - NM_SETTING_IP4_CONFIG_METHOD_SHARED, - NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL); - nm_connection_add_setting (connection, NM_SETTING (ip4_setting)); - return TRUE; - } else { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Unknown config for %s", conn_name); - g_object_unref (ip4_setting); - return FALSE; - } - nm_log_info (LOGD_SETTINGS, "Using %s method for %s", method, conn_name); - }else { - iblock = convert_ip4_config_block (conn_name); - if (!iblock) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Ifnet plugin: can't aquire ip configuration for %s", - conn_name); - g_object_unref (ip4_setting); - return FALSE; - } - /************** add all ip settings to the connection**********/ - while (iblock) { - ip_block *current_iblock; - NMIPAddress *ip4_addr; - GError *local = NULL; - - ip4_addr = nm_ip_address_new (AF_INET, iblock->ip, iblock->prefix, &local); - if (iblock->next_hop) - g_object_set (ip4_setting, - NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, - TRUE, NULL); - - if (ip4_addr) { - if (!nm_setting_ip_config_add_address (ip4_setting, ip4_addr)) - nm_log_warn (LOGD_SETTINGS, "ignoring duplicate IP4 address"); - nm_ip_address_unref (ip4_addr); - } else { - nm_log_warn (LOGD_SETTINGS, " ignoring invalid address entry: %s", local->message); - g_clear_error (&local); - } - - current_iblock = iblock; - iblock = iblock->next; - destroy_ip_block (current_iblock); - - } - g_object_set (ip4_setting, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, - NM_SETTING_IP_CONFIG_NEVER_DEFAULT, !has_default_ip4_route (conn_name), - NULL); - } - - /* add dhcp hostname and client id */ - if (!is_static_block && strstr (method, "dhcp")) { - gchar *dhcp_hostname, *client_id; - - get_dhcp_hostname_and_client_id (&dhcp_hostname, &client_id); - if (dhcp_hostname) { - g_object_set (ip4_setting, - NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, - dhcp_hostname, NULL); - nm_log_info (LOGD_SETTINGS, "DHCP hostname: %s", dhcp_hostname); - g_free (dhcp_hostname); - } - if (client_id) { - g_object_set (ip4_setting, - NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, - client_id, NULL); - nm_log_info (LOGD_SETTINGS, "DHCP client id: %s", client_id); - g_free (client_id); - } - } - - /* add all IPv4 dns servers, IPv6 servers will be ignored */ - set_ip4_dns_servers (ip4_setting, conn_name); - - /* DNS searches */ - value = ifnet_get_data (conn_name, "dns_search"); - if (value) { - gs_free char *stripped = g_strdup (value); - char **searches = NULL; - - strip_string (stripped, '"'); - - searches = g_strsplit (stripped, " ", 0); - if (searches) { - char **item; - - for (item = searches; *item; item++) { - if (strlen (*item)) { - if (!nm_setting_ip_config_add_dns_search (ip4_setting, *item)) - nm_log_warn (LOGD_SETTINGS, " duplicate DNS domain '%s'", *item); - } - } - g_strfreev (searches); - } - } - - /* static routes */ - iblock = convert_ip4_routes_block (conn_name); - while (iblock) { - ip_block *current_iblock = iblock; - const char *metric_str; - char *stripped; - gint64 metric; - NMIPRoute *route; - GError *local = NULL; - - if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL) { - metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); - } else { - metric_str = ifnet_get_global_data ("metric"); - if (metric_str) { - stripped = g_strdup (metric_str); - strip_string (stripped, '"'); - metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); - g_free (stripped); - } else - metric = -1; - } - - route = nm_ip_route_new (AF_INET, iblock->ip, iblock->prefix, iblock->next_hop, metric, &local); - if (route) { - if (nm_setting_ip_config_add_route (ip4_setting, route)) - nm_log_info (LOGD_SETTINGS, "new IP4 route:%s\n", iblock->ip); - else - nm_log_warn (LOGD_SETTINGS, "duplicate IP4 route"); - nm_ip_route_unref (route); - } else { - nm_log_warn (LOGD_SETTINGS, " ignoring invalid route entry: %s", local->message); - g_clear_error (&local); - } - - current_iblock = iblock; - iblock = iblock->next; - destroy_ip_block (current_iblock); - } - - /* Finally add setting to connection */ - nm_connection_add_setting (connection, NM_SETTING (ip4_setting)); - - return TRUE; -} - -static gboolean -make_ip6_setting (NMConnection *connection, - const char *conn_name, - GError **error) -{ - NMSettingIPConfig *s_ip6 = NULL; - gboolean is_static_block = is_static_ip6 (conn_name); - - // used to disable IPv6 - gboolean ipv6_enabled = FALSE; - gchar *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; - const char *value; - ip_block *iblock; - gboolean never_default = !has_default_ip6_route (conn_name); - - s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); - - value = ifnet_get_data (conn_name, "enable_ipv6"); - if (value && is_true (value)) - ipv6_enabled = TRUE; - - //FIXME Handle other methods that NM supports in future - // Currently only Manual and DHCP are supported - if (!ipv6_enabled) { - g_object_set (s_ip6, - NM_SETTING_IP_CONFIG_METHOD, - NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); - goto done; - } else if (!is_static_block) { - // config_eth* contains "dhcp6" - method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; - never_default = FALSE; - } - // else if (!has_ip6_address(conn_name)) - // doesn't have "dhcp6" && doesn't have any ipv6 address - // method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; - else - // doesn't have "dhcp6" && has at least one ipv6 address - method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; - nm_log_info (LOGD_SETTINGS, "IPv6 for %s enabled, using %s", conn_name, method); - - g_object_set (s_ip6, - NM_SETTING_IP_CONFIG_METHOD, method, - NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, FALSE, - NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, FALSE, - NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default, NULL); - - /* Make manual settings */ - if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { - ip_block *current_iblock; - - iblock = convert_ip6_config_block (conn_name); - if (!iblock) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Ifnet plugin: can't aquire ip6 configuration for %s", - conn_name); - goto error; - } - /* add all IPv6 addresses */ - while (iblock) { - NMIPAddress *ip6_addr; - GError *local = NULL; - - ip6_addr = nm_ip_address_new (AF_INET6, iblock->ip, iblock->prefix, &local); - if (ip6_addr) { - if (nm_setting_ip_config_add_address (s_ip6, ip6_addr)) { - nm_log_info (LOGD_SETTINGS, "ipv6 addresses count: %d", - nm_setting_ip_config_get_num_addresses (s_ip6)); - } else { - nm_log_warn (LOGD_SETTINGS, "ignoring duplicate IP6 address"); - } - nm_ip_address_unref (ip6_addr); - } else { - nm_log_warn (LOGD_SETTINGS, " ignoring invalid address entry: %s", local->message); - g_clear_error (&local); - } - - current_iblock = iblock; - iblock = iblock->next; - destroy_ip_block (current_iblock); - } - - } else if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { - /* - autoconf or DHCPv6 stuff goes here */ - } - // DNS Servers, set NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS TRUE here - set_ip6_dns_servers (s_ip6, conn_name); - - /* DNS searches ('DOMAIN' key) are read by make_ip4_setting() and included in NMSettingIPConfig */ - - // Add routes - iblock = convert_ip6_routes_block (conn_name); - if (iblock) - g_object_set (s_ip6, NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, - TRUE, NULL); - /* Add all IPv6 routes */ - while (iblock) { - ip_block *current_iblock = iblock; - const char *metric_str; - char *stripped; - gint64 metric; - NMIPRoute *route; - GError *local = NULL; - - /* metric is not per routes configuration right now - * global metric is also supported (metric="x") */ - if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL) - metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); - else { - metric_str = ifnet_get_global_data ("metric"); - if (metric_str) { - stripped = g_strdup (metric_str); - strip_string (stripped, '"'); - metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); - g_free (stripped); - } else - metric = 1; - } - - route = nm_ip_route_new (AF_INET6, iblock->ip, iblock->prefix, iblock->next_hop, metric, &local); - if (route) { - if (nm_setting_ip_config_add_route (s_ip6, route)) - nm_log_info (LOGD_SETTINGS, " new IP6 route"); - else - nm_log_warn (LOGD_SETTINGS, " duplicate IP6 route"); - nm_ip_route_unref (route); - } else { - nm_log_warn (LOGD_SETTINGS, " ignoring invalid route entry: %s", local->message); - g_clear_error (&local); - } - - current_iblock = iblock; - iblock = iblock->next; - destroy_ip_block (current_iblock); - } - -done: - nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - return TRUE; - -error: - g_object_unref (s_ip6); - nm_log_warn (LOGD_SETTINGS, " Ignore IPv6 for %s", conn_name); - return FALSE; -} - -static NMSetting * -make_wireless_connection_setting (const char *conn_name, - NMSetting8021x **s_8021x, - GError **error) -{ - const char *mac = NULL; - NMSettingWireless *wireless_setting = NULL; - gboolean adhoc = FALSE; - const char *value; - const char *type; - - /* PPP over WIFI is not supported yet */ - g_return_val_if_fail (conn_name != NULL - && strcmp (ifnet_get_data (conn_name, "type"), - "ppp") != 0, NULL); - type = ifnet_get_data (conn_name, "type"); - if (strcmp (type, "ppp") == 0) { - nm_log_warn (LOGD_SETTINGS, "PPP over WIFI is not supported yet"); - return NULL; - } - - wireless_setting = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); - if (read_mac_address (conn_name, &mac, error)) { - if (mac) { - g_object_set (wireless_setting, - NM_SETTING_WIRELESS_MAC_ADDRESS, mac, - NULL); - } - } else { - g_object_unref (wireless_setting); - return NULL; - } - - /* handle ssid (hex and ascii) */ - if (conn_name) { - GBytes *bytes; - gsize ssid_len = 0, value_len = strlen (conn_name); - - ssid_len = value_len; - if ((value_len > 2) && (g_str_has_prefix (conn_name, "0x"))) { - /* Hex representation */ - if (value_len % 2) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid SSID '%s' size (looks like hex but length not multiple of 2)", - conn_name); - goto error; - } - - bytes = nm_utils_hexstr2bin (conn_name); - if (!bytes) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid SSID '%s' (looks like hex SSID but isn't)", - conn_name); - goto error; - } - } else - bytes = g_bytes_new (conn_name, value_len); - - ssid_len = g_bytes_get_size (bytes); - if (ssid_len > 32 || ssid_len == 0) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)", - conn_name, ssid_len); - goto error; - } - - g_object_set (wireless_setting, NM_SETTING_WIRELESS_SSID, bytes, NULL); - g_bytes_unref (bytes); - } else { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing SSID"); - goto error; - } - - /* mode=0: infrastructure - * mode=1: adhoc */ - value = wpa_get_value (conn_name, "mode"); - if (value) - adhoc = strcmp (value, "1") == 0 ? TRUE : FALSE; - - if (exist_ssid (conn_name)) { - const char *mode = adhoc ? "adhoc" : "infrastructure"; - - g_object_set (wireless_setting, NM_SETTING_WIRELESS_MODE, mode, - NULL); - nm_log_info (LOGD_SETTINGS, "Using mode: %s", mode); - } - - /* BSSID setting */ - value = wpa_get_value (conn_name, "bssid"); - if (value) { - if (!nm_utils_hwaddr_valid (value, ETH_ALEN)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid BSSID '%s'", value); - goto error; - } - - g_object_set (wireless_setting, NM_SETTING_WIRELESS_BSSID, - value, NULL); - - } - - /* mtu_ssid="xx" */ - value = ifnet_get_data (conn_name, "mtu"); - if (value) { - long int mtu; - - errno = 0; - mtu = strtol (value, NULL, 10); - if (errno || mtu < 0 || mtu > 50000) { - nm_log_warn (LOGD_SETTINGS, " invalid MTU '%s' for %s", value, conn_name); - } else - g_object_set (wireless_setting, NM_SETTING_WIRELESS_MTU, - (guint32) mtu, NULL); - - } - - nm_log_info (LOGD_SETTINGS, "wireless_setting added for %s", conn_name); - return NM_SETTING (wireless_setting); -error: - if (wireless_setting) - g_object_unref (wireless_setting); - return NULL; - -} - -static NMSettingWirelessSecurity * -make_leap_setting (const char *ssid, GError **error) -{ - NMSettingWirelessSecurity *wsec; - const char *value; - - wsec = - NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); - - value = wpa_get_value (ssid, "password"); - if (value && strlen (value)) - g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, - value, NULL); - - value = wpa_get_value (ssid, "identity"); - if (!value || !strlen (value)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing LEAP identity"); - goto error; - } - g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, value, - NULL); - - g_object_set (wsec, - NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", - NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", NULL); - - return wsec; -error: - if (wsec) - g_object_unref (wsec); - return NULL; -} - -static gboolean -add_one_wep_key (const char *ssid, - const char *key, - int key_idx, - NMSettingWirelessSecurity *s_wsec, - GError **error) -{ - const char *value; - char *converted = NULL; - gboolean success = FALSE; - - g_return_val_if_fail (ssid != NULL, FALSE); - g_return_val_if_fail (key != NULL, FALSE); - g_return_val_if_fail (key_idx >= 0 && key_idx <= 3, FALSE); - g_return_val_if_fail (s_wsec != NULL, FALSE); - - value = wpa_get_value (ssid, key); - if (!value) - return TRUE; - - /* Validate keys */ - if (strlen (value) == 10 || strlen (value) == 26) { - /* Hexadecimal WEP key */ - if (!is_hex (value)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid hexadecimal WEP key."); - goto out; - } - converted = g_strdup (value); - } else if (value[0] == '"' - && (strlen (value) == 7 || strlen (value) == 15)) { - /* ASCII passphrase */ - char *tmp = g_strdup (value); - char *p = strip_string (tmp, '"'); - - if (!is_ascii (p)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid ASCII WEP passphrase."); - g_free (tmp); - goto out; - - } - - converted = nm_utils_bin2hexstr (tmp, strlen (tmp), -1); - g_free (tmp); - } else { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid WEP key length. Key: %s", value); - goto out; - } - - if (converted) { - nm_setting_wireless_security_set_wep_key (s_wsec, key_idx, converted); - g_free (converted); - success = TRUE; - } - -out: - return success; -} - -static gboolean -add_wep_keys (const char *ssid, - NMSettingWirelessSecurity *s_wsec, - GError **error) -{ - if (!add_one_wep_key (ssid, "wep_key0", 0, s_wsec, error)) - return FALSE; - if (!add_one_wep_key (ssid, "wep_key1", 1, s_wsec, error)) - return FALSE; - if (!add_one_wep_key (ssid, "wep_key2", 2, s_wsec, error)) - return FALSE; - if (!add_one_wep_key (ssid, "wep_key3", 3, s_wsec, error)) - return FALSE; - return TRUE; - -} - -static NMSettingWirelessSecurity * -make_wep_setting (const char *ssid, GError **error) -{ - const char *auth_alg, *value; - int default_key_idx = 0; - NMSettingWirelessSecurity *s_wireless_sec; - - s_wireless_sec = - NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); - g_object_set (s_wireless_sec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, - "none", NULL); - - /* default key index */ - value = wpa_get_value (ssid, "wep_tx_keyidx"); - if (value) { - default_key_idx = atoi (value); - if (default_key_idx >= 0 && default_key_idx <= 3) { - g_object_set (s_wireless_sec, - NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, - default_key_idx, NULL); - nm_log_info (LOGD_SETTINGS, "Default key index: %d", default_key_idx); - } else { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid default WEP key '%s'", value); - goto error; - } - } - - if (!add_wep_keys (ssid, s_wireless_sec, error)) - goto error; - - /* If there's a default key, ensure that key exists */ - if ((default_key_idx == 1) - && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Default WEP key index was 2, but no valid KEY2 exists."); - goto error; - } else if ((default_key_idx == 2) - && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, - 2)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Default WEP key index was 3, but no valid KEY3 exists."); - goto error; - } else if ((default_key_idx == 3) - && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, - 3)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Default WEP key index was 4, but no valid KEY4 exists."); - goto error; - } - - /* authentication algorithms */ - auth_alg = wpa_get_value (ssid, "auth_alg"); - if (auth_alg) { - if (strcmp (auth_alg, "OPEN") == 0) { - g_object_set (s_wireless_sec, - NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, - "open", NULL); - nm_log_info (LOGD_SETTINGS, "WEP: Use open system authentication"); - } else if (strcmp (auth_alg, "SHARED") == 0) { - g_object_set (s_wireless_sec, - NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, - "shared", NULL); - nm_log_info (LOGD_SETTINGS, "WEP: Use shared system authentication"); - } else { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid WEP authentication algorithm '%s'", - auth_alg); - goto error; - } - - } - - if (!nm_setting_wireless_security_get_wep_key (s_wireless_sec, 0) - && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1) - && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 2) - && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 3) - && !nm_setting_wireless_security_get_wep_tx_keyidx (s_wireless_sec)) { - if (auth_alg && !strcmp (auth_alg, "shared")) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "WEP Shared Key authentication is invalid for " - "unencrypted connections."); - goto error; - } - /* Unencrypted */ - g_object_unref (s_wireless_sec); - s_wireless_sec = NULL; - } - return s_wireless_sec; - -error: - if (s_wireless_sec) - g_object_unref (s_wireless_sec); - return NULL; -} - -static char * -parse_wpa_psk (const char *psk, GError **error) -{ - char *hashed = NULL; - gboolean quoted = FALSE; - - if (!psk) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing WPA_PSK for WPA-PSK key management"); - return NULL; - } - - /* Passphrase must be between 10 and 66 characters in length because WPA - * hex keys are exactly 64 characters (no quoting), and WPA passphrases - * are between 8 and 63 characters (inclusive), plus optional quoting if - * the passphrase contains spaces. - */ - - if (psk[0] == '"' && psk[strlen (psk) - 1] == '"') - quoted = TRUE; - if (!quoted && (strlen (psk) == 64)) { - /* Verify the hex PSK; 64 digits */ - if (!is_hex (psk)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid WPA_PSK (contains non-hexadecimal characters)"); - goto out; - } - hashed = g_strdup (psk); - } else { - char *stripped = g_strdup (psk); - - strip_string (stripped, '"'); - - /* Length check */ - if (strlen (stripped) < 8 || strlen (stripped) > 63) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid WPA_PSK (passphrases must be between " - "8 and 63 characters long (inclusive))"); - g_free (stripped); - goto out; - } - - hashed = g_strdup (stripped); - g_free (stripped); - } - - if (!hashed) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid WPA_PSK (doesn't look like a passphrase or hex key)"); - goto out; - } - -out: - return hashed; -} - -static gboolean -fill_wpa_ciphers (const char *ssid, - NMSettingWirelessSecurity *wsec, - gboolean group, - gboolean adhoc) -{ - const char *value; - char **list = NULL, **iter; - int i = 0; - - value = wpa_get_value (ssid, group ? "group" : "pairwise"); - if (!value) - return TRUE; - - list = g_strsplit_set (value, " ", 0); - for (iter = list; iter && *iter; iter++, i++) { - /* Ad-Hoc configurations cannot have pairwise ciphers, and can only - * have one group cipher. Ignore any additional group ciphers and - * any pairwise ciphers specified. - */ - if (adhoc) { - if (group && (i > 0)) { - nm_log_warn (LOGD_SETTINGS, " ignoring group cipher '%s' (only one group cipher allowed in Ad-Hoc mode)", - *iter); - continue; - } else if (!group) { - nm_log_warn (LOGD_SETTINGS, " ignoring pairwise cipher '%s' (pairwise not used in Ad-Hoc mode)", - *iter); - continue; - } - } - - if (!strcmp (*iter, "CCMP")) { - if (group) - nm_setting_wireless_security_add_group (wsec, - "ccmp"); - else - nm_setting_wireless_security_add_pairwise (wsec, - "ccmp"); - } else if (!strcmp (*iter, "TKIP")) { - if (group) - nm_setting_wireless_security_add_group (wsec, - "tkip"); - else - nm_setting_wireless_security_add_pairwise (wsec, - "tkip"); - } else if (group && !strcmp (*iter, "WEP104")) - nm_setting_wireless_security_add_group (wsec, "wep104"); - else if (group && !strcmp (*iter, "WEP40")) - nm_setting_wireless_security_add_group (wsec, "wep40"); - else { - nm_log_warn (LOGD_SETTINGS, " ignoring invalid %s cipher '%s'", - group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE", - *iter); - } - } - - if (list) - g_strfreev (list); - return TRUE; -} - -static NMSetting8021x * -fill_8021x (const char *ssid, - const char *key_mgmt, - gboolean wifi, - const char *basepath, - GError **error) -{ - NMSetting8021x *s_8021x; - const char *value; - char **list, **iter; - - value = wpa_get_value (ssid, "eap"); - if (!value) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing IEEE_8021X_EAP_METHODS for key management '%s'", - key_mgmt); - return NULL; - } - - list = g_strsplit (value, " ", 0); - - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - /* Validate and handle each EAP method */ - for (iter = list; iter && *iter; iter++) { - EAPReader *eap = &eap_readers[0]; - gboolean found = FALSE; - char *lower = NULL; - - lower = g_ascii_strdown (*iter, -1); - while (eap->method) { - if (strcmp (eap->method, lower)) - goto next; - - /* Some EAP methods don't provide keying material, thus they - * cannot be used with WiFi unless they are an inner method - * used with TTLS or PEAP or whatever. - */ - if (wifi && eap->wifi_phase2_only) { - nm_log_warn (LOGD_SETTINGS, " ignored invalid IEEE_8021X_EAP_METHOD '%s'; not allowed for wifi.", - lower); - goto next; - } - - /* Parse EAP method specific options */ - if (!(*eap->reader) (lower, ssid, s_8021x, FALSE, basepath, error)) { - g_free (lower); - goto error; - } - nm_setting_802_1x_add_eap_method (s_8021x, lower); - found = TRUE; - break; - - next: - eap++; - } - - if (!found) { - nm_log_warn (LOGD_SETTINGS, " ignored unknown IEEE_8021X_EAP_METHOD '%s'.", lower); - } - g_free (lower); - } - g_strfreev (list); - - if (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 0) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "No valid EAP methods found in IEEE_8021X_EAP_METHODS."); - goto error; - } - - return s_8021x; - -error: - g_object_unref (s_8021x); - return NULL; -} - -static NMSettingWirelessSecurity * -make_wpa_setting (const char *ssid, - const char *basepath, - NMSetting8021x **s_8021x, - GError **error) -{ - NMSettingWirelessSecurity *wsec; - const char *value; - char *lower; - gboolean adhoc = FALSE; - - if (!exist_ssid (ssid)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "No security info found for ssid: %s", ssid); - return NULL; - } - - wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); - - /* mode=1: adhoc - * mode=0: infrastructure */ - value = wpa_get_value (ssid, "mode"); - if (value) - adhoc = strcmp (value, "1") == 0 ? TRUE : FALSE; - - /* Pairwise and Group ciphers */ - fill_wpa_ciphers (ssid, wsec, FALSE, adhoc); - fill_wpa_ciphers (ssid, wsec, TRUE, adhoc); - - /* WPA and/or RSN */ - if (adhoc) { - /* Ad-Hoc mode only supports WPA proto for now */ - nm_setting_wireless_security_add_proto (wsec, "wpa"); - } else { - nm_setting_wireless_security_add_proto (wsec, "wpa"); - nm_setting_wireless_security_add_proto (wsec, "rsn"); - - } - - value = wpa_get_value (ssid, "key_mgmt"); - if (!strcmp (value, "WPA-PSK")) { - char *psk = parse_wpa_psk (wpa_get_value (ssid, "psk"), error); - - if (!psk) - goto error; - g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PSK, psk, - NULL); - g_free (psk); - - if (adhoc) - g_object_set (wsec, - NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, - "wpa-none", NULL); - else - g_object_set (wsec, - NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, - "wpa-psk", NULL); - } else if (!strcmp (value, "WPA-EAP") || !strcmp (value, "IEEE8021X")) { - if (adhoc) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Ad-Hoc mode cannot be used with KEY_MGMT type '%s'", - value); - goto error; - } - *s_8021x = fill_8021x (ssid, value, TRUE, basepath, error); - if (!*s_8021x) - goto error; - - lower = g_ascii_strdown (value, -1); - g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, - lower, NULL); - g_free (lower); - } else { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Unknown wireless KEY_MGMT type '%s'", value); - goto error; - } - return wsec; -error: - if (wsec) - g_object_unref (wsec); - return NULL; -} - -static NMSettingWirelessSecurity * -make_wireless_security_setting (const char *conn_name, - const char *basepath, - NMSetting8021x **s_8021x, - GError ** error) -{ - NMSettingWirelessSecurity *wsec = NULL; - const char *ssid; - gboolean adhoc = FALSE; - const char *value; - - g_return_val_if_fail (conn_name != NULL - && strcmp (ifnet_get_data (conn_name, "type"), - "ppp") != 0, NULL); - nm_log_info (LOGD_SETTINGS, "updating wireless security settings (%s).", conn_name); - - ssid = conn_name; - value = wpa_get_value (ssid, "mode"); - if (value) - adhoc = strcmp (value, "1") == 0 ? TRUE : FALSE; - - value = wpa_get_value (ssid, "key_mgmt"); - if (!adhoc && g_strcmp0 (value, "IEEE8021X") == 0) { - value = wpa_get_value (ssid, "eap"); - if (value && strcasecmp (value, "LEAP") == 0) { - wsec = make_leap_setting (ssid, error); - if (wsec == NULL) - goto error; - } - } else if (g_strcmp0 (value, "WPA-PSK") == 0 || g_strcmp0 (value, "WPA-EAP") == 0) { - wsec = make_wpa_setting (ssid, basepath, s_8021x, error); - if (wsec == NULL) - goto error; - } - if (!wsec) { - wsec = make_wep_setting (ssid, error); - if (wsec == NULL) - goto error; - } - return wsec; - -error: - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Can't handle security information for ssid: %s", - conn_name); - return NULL; -} - -/* Currently only support username and password */ -static gboolean -make_pppoe_connection_setting (NMConnection *connection, - const char *conn_name, - GError **error) -{ - NMSettingPppoe *s_pppoe; - NMSettingPpp *s_ppp; - const char *value; - - s_pppoe = NM_SETTING_PPPOE (nm_setting_pppoe_new ()); - - /* username */ - value = ifnet_get_data (conn_name, "username"); - if (!value) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "ppp requires at lease a username"); - return FALSE; - } - g_object_set (s_pppoe, NM_SETTING_PPPOE_USERNAME, value, NULL); - - /* password */ - value = ifnet_get_data (conn_name, "password"); - if (!value) { - value = ""; - } - - g_object_set (s_pppoe, NM_SETTING_PPPOE_PASSWORD, value, NULL); - nm_connection_add_setting (connection, NM_SETTING (s_pppoe)); - - /* PPP setting */ - s_ppp = (NMSettingPpp *) nm_setting_ppp_new (); - nm_connection_add_setting (connection, NM_SETTING (s_ppp)); - - return TRUE; -} - -NMConnection * -ifnet_update_connection_from_config_block (const char *conn_name, - const char *basepath, - GError **error) -{ - const gchar *type = NULL; - NMConnection *connection = NULL; - NMSettingConnection *setting = NULL; - NMSetting8021x *s_8021x = NULL; - NMSettingWirelessSecurity *wsec = NULL; - gboolean auto_conn = TRUE; - const char *value = NULL; - gchar *id, *uuid; - gboolean success = FALSE; - - connection = nm_simple_connection_new (); - setting = nm_connection_get_setting_connection (connection); - if (!setting) { - setting = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - g_assert (setting); - nm_connection_add_setting (connection, NM_SETTING (setting)); - } - - type = guess_connection_type (conn_name); - value = ifnet_get_data (conn_name, "auto"); - if (value && !strcmp (value, "false")) - auto_conn = FALSE; - - /* Try to read UUID from the ifnet block, otherwise generate UUID from - * the connection ID. - */ - id = connection_id_from_ifnet_name (conn_name); - uuid = g_strdup (ifnet_get_data (conn_name, "uuid")); - if (!uuid) - uuid = nm_utils_uuid_generate_from_string (id, -1, NM_UTILS_UUID_TYPE_LEGACY, NULL); - - g_object_set (setting, - NM_SETTING_CONNECTION_TYPE, type, - NM_SETTING_CONNECTION_ID, id, - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_INTERFACE_NAME, conn_name, - NM_SETTING_CONNECTION_READ_ONLY, FALSE, - NM_SETTING_CONNECTION_AUTOCONNECT, auto_conn, - NULL); - nm_log_info (LOGD_SETTINGS, "name:%s, id:%s, uuid: %s", conn_name, id, uuid); - g_free (id); - g_free (uuid); - - if (!strcmp (NM_SETTING_WIRED_SETTING_NAME, type) - || !strcmp (NM_SETTING_PPPOE_SETTING_NAME, type)) { - /* wired setting */ - if (!make_wired_connection_setting (connection, conn_name, error)) - goto error; - - /* pppoe setting */ - if (!strcmp (NM_SETTING_PPPOE_SETTING_NAME, type)) { - if (!make_pppoe_connection_setting (connection, conn_name, error)) - goto error; - } - } else if (!strcmp (NM_SETTING_WIRELESS_SETTING_NAME, type)) { - /* wireless setting */ - NMSetting *wireless_setting; - - wireless_setting = make_wireless_connection_setting (conn_name, &s_8021x, error); - if (!wireless_setting) - goto error; - nm_connection_add_setting (connection, wireless_setting); - - /* wireless security setting */ - if (wpa_get_value (conn_name, "ssid")) { - wsec = make_wireless_security_setting (conn_name, basepath, &s_8021x, error); - if (!wsec) - goto error; - nm_connection_add_setting (connection, NM_SETTING (wsec)); - if (s_8021x) - nm_connection_add_setting (connection, NM_SETTING (s_8021x)); - } - } else - goto error; - - /* IPv4 setting */ - if (!make_ip4_setting (connection, conn_name, error)) - goto error; - - /* IPv6 setting */ - if (!make_ip6_setting (connection, conn_name, error)) - goto error; - - if (nm_connection_verify (connection, error)) { - nm_log_info (LOGD_SETTINGS, "Connection verified %s:%d", conn_name, success); - } else { - goto error; - } - - return connection; -error: - g_object_unref (connection); - return NULL; -} - -typedef struct Setting8021xSchemeVtable { - const NMSetting8021xSchemeVtable *vtable; - const char *ifnet_key; -} Setting8021xSchemeVtable; - -static const Setting8021xSchemeVtable setting_8021x_scheme_vtable[] = { - [NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT], - .ifnet_key = "ca_cert", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT], - .ifnet_key = "ca_cert2", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT], - .ifnet_key = "client_cert", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT], - .ifnet_key = "client_cert2", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY], - .ifnet_key = "private_key", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY], - .ifnet_key = "private_key2", - }, -}; - -static gboolean -write_object (NMSetting8021x *s_8021x, - const char *conn_name, - GBytes *override_data, - const Setting8021xSchemeVtable *objtype, - GError **error) -{ - NMSetting8021xCKScheme scheme; - const char *path = NULL; - GBytes *blob = NULL; - - g_return_val_if_fail (conn_name != NULL, FALSE); - g_return_val_if_fail (objtype != NULL, FALSE); - if (override_data) - /* if given explicit data to save, always use that instead of asking - * the setting what to do. - */ - blob = override_data; - else { - scheme = (*(objtype->vtable->scheme_func)) (s_8021x); - switch (scheme) { - case NM_SETTING_802_1X_CK_SCHEME_BLOB: - blob = (*(objtype->vtable->blob_func)) (s_8021x); - break; - case NM_SETTING_802_1X_CK_SCHEME_PATH: - path = (*(objtype->vtable->path_func)) (s_8021x); - break; - default: - break; - } - } - - /* If the object path was specified, prefer that over any raw cert data that - * may have been sent. - */ - if (path) { - wpa_set_data (conn_name, (gchar *) objtype->ifnet_key, - (gchar *) path); - return TRUE; - } - - /* does not support writing encryption data now */ - if (blob) - nm_log_warn (LOGD_SETTINGS, " Currently we do not support cert writing."); - - return TRUE; -} - -static gboolean -write_8021x_certs (NMSetting8021x *s_8021x, - gboolean phase2, - const char *conn_name, - GError **error) -{ - char *password = NULL; - const Setting8021xSchemeVtable *otype = NULL; - gboolean is_pkcs12 = FALSE, success = FALSE; - GBytes *blob = NULL; - GBytes *enc_key = NULL; - gchar *generated_pw = NULL; - - /* CA certificate */ - otype = phase2 - ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] - : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT]; - - if (!write_object (s_8021x, conn_name, NULL, otype, error)) - return FALSE; - - /* Private key */ - if (phase2) { - if (nm_setting_802_1x_get_phase2_private_key_scheme (s_8021x) != - NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) { - if (nm_setting_802_1x_get_phase2_private_key_format - (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) - is_pkcs12 = TRUE; - } - password = (char *) - nm_setting_802_1x_get_phase2_private_key_password (s_8021x); - } else { - if (nm_setting_802_1x_get_private_key_scheme (s_8021x) != - NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) { - if (nm_setting_802_1x_get_private_key_format (s_8021x) - == NM_SETTING_802_1X_CK_FORMAT_PKCS12) - is_pkcs12 = TRUE; - } - password = (char *) - nm_setting_802_1x_get_private_key_password (s_8021x); - } - - otype = phase2 - ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] - : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY]; - - if ((*(otype->vtable->scheme_func)) (s_8021x) == - NM_SETTING_802_1X_CK_SCHEME_BLOB) - blob = (*(otype->vtable->blob_func)) (s_8021x); - - /* Only do the private key re-encrypt dance if we got the raw key data, which - * by definition will be unencrypted. If we're given a direct path to the - * private key file, it'll be encrypted, so we don't need to re-encrypt. - */ - if (blob && !is_pkcs12) { - GByteArray *tmp_enc_key; - - /* Encrypt the unencrypted private key with the fake password */ - tmp_enc_key = - nm_utils_rsa_key_encrypt (g_bytes_get_data (blob, NULL), g_bytes_get_size (blob), - password, &generated_pw, error); - if (!tmp_enc_key) - goto out; - - enc_key = g_byte_array_free_to_bytes (tmp_enc_key); - - if (generated_pw) - password = generated_pw; - } - - /* Save the private key */ - if (!write_object - (s_8021x, conn_name, enc_key ? enc_key : blob, otype, error)) - goto out; - - if (phase2) - wpa_set_data (conn_name, "private_key2_passwd", password); - else - wpa_set_data (conn_name, "private_key_passwd", password); - - /* Client certificate */ - if (is_pkcs12) { - wpa_set_data (conn_name, - phase2 ? "client_cert2" : "client_cert", NULL); - } else { - otype = phase2 - ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] - : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT]; - - /* Save the client certificate */ - if (!write_object (s_8021x, conn_name, NULL, otype, error)) - goto out; - } - - success = TRUE; -out: - if (generated_pw) { - memset (generated_pw, 0, strlen (generated_pw)); - g_free (generated_pw); - } - if (enc_key) { - memset ((gpointer) g_bytes_get_data (enc_key, NULL), 0, g_bytes_get_size (enc_key)); - g_bytes_unref (enc_key); - } - return success; -} - -static gboolean -write_8021x_setting (NMConnection *connection, - const char *conn_name, - gboolean wired, - GError **error) -{ - NMSetting8021x *s_8021x; - const char *value; - char *tmp = NULL; - gboolean success = FALSE; - GString *phase2_auth; - GString *phase1; - - s_8021x = nm_connection_get_setting_802_1x (connection); - if (!s_8021x) { - return TRUE; - } - - nm_log_info (LOGD_SETTINGS, "Adding 8021x setting for %s", conn_name); - - /* If wired, write KEY_MGMT */ - if (wired) - wpa_set_data (conn_name, "key_mgmt", "IEEE8021X"); - - /* EAP method */ - if (nm_setting_802_1x_get_num_eap_methods (s_8021x)) { - value = nm_setting_802_1x_get_eap_method (s_8021x, 0); - if (value) - tmp = g_ascii_strup (value, -1); - } - wpa_set_data (conn_name, "eap", tmp ? tmp : NULL); - g_free (tmp); - - wpa_set_data (conn_name, "identity", - (gchar *) nm_setting_802_1x_get_identity (s_8021x)); - - wpa_set_data (conn_name, "anonymous_identity", (gchar *) - nm_setting_802_1x_get_anonymous_identity (s_8021x)); - - wpa_set_data (conn_name, "password", - (gchar *) nm_setting_802_1x_get_password (s_8021x)); - - phase1 = g_string_new (NULL); - - /* PEAP version */ - wpa_set_data (conn_name, "phase1", NULL); - value = nm_setting_802_1x_get_phase1_peapver (s_8021x); - if (value && (!strcmp (value, "0") || !strcmp (value, "1"))) - g_string_append_printf (phase1, "peapver=%s ", value); - - /* PEAP label */ - value = nm_setting_802_1x_get_phase1_peaplabel (s_8021x); - if (value && !strcmp (value, "1")) - g_string_append_printf (phase1, "peaplabel=%s ", value); - if (phase1->len) { - tmp = g_strstrip (g_strdup (phase1->str)); - wpa_set_data (conn_name, "phase1", tmp); - g_free (tmp); - } - - /* Phase2 auth methods */ - wpa_set_data (conn_name, "phase2", NULL); - phase2_auth = g_string_new (NULL); - - value = nm_setting_802_1x_get_phase2_auth (s_8021x); - if (value) { - tmp = g_ascii_strup (value, -1); - g_string_append_printf (phase2_auth, "auth=%s ", tmp); - g_free (tmp); - } - - /* Phase2 auth heap */ - value = nm_setting_802_1x_get_phase2_autheap (s_8021x); - if (value) { - tmp = g_ascii_strup (value, -1); - g_string_append_printf (phase2_auth, "autheap=%s ", tmp); - g_free (tmp); - } - tmp = g_strstrip (g_strdup (phase2_auth->str)); - wpa_set_data (conn_name, "phase2", phase2_auth->len ? tmp : NULL); - g_free (tmp); - - g_string_free (phase2_auth, TRUE); - g_string_free (phase1, TRUE); - - success = write_8021x_certs (s_8021x, FALSE, conn_name, error); - if (success) { - /* phase2/inner certs */ - success = write_8021x_certs (s_8021x, TRUE, conn_name, error); - } - - return success; -} - -static gboolean -write_wireless_security_setting (NMConnection * connection, - gchar * conn_name, - gboolean adhoc, - gboolean * no_8021x, GError ** error) -{ - NMSettingWirelessSecurity *s_wsec; - const char *key_mgmt, *auth_alg, *key, *cipher, *psk; - gboolean wep = FALSE, wpa = FALSE; - char *tmp; - guint32 i, num; - GString *str; - - s_wsec = nm_connection_get_setting_wireless_security (connection); - if (!s_wsec) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing '%s' setting", - NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); - return FALSE; - } - - key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); - g_assert (key_mgmt); - - auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); - - if (!strcmp (key_mgmt, "none")) { - wpa_set_data (conn_name, "key_mgmt", "NONE"); - wep = TRUE; - *no_8021x = TRUE; - } else if (!strcmp (key_mgmt, "wpa-none") - || !strcmp (key_mgmt, "wpa-psk")) { - wpa_set_data (conn_name, "key_mgmt", "WPA-PSK"); - wpa = TRUE; - *no_8021x = TRUE; - } else if (!strcmp (key_mgmt, "ieee8021x")) { - wpa_set_data (conn_name, "key_mgmt", "IEEE8021X"); - } else if (!strcmp (key_mgmt, "wpa-eap")) { - wpa_set_data (conn_name, "key_mgmt", "WPA-EAP"); - wpa = TRUE; - } else - nm_log_warn (LOGD_SETTINGS, "Unknown key_mgmt: %s", key_mgmt); - - if (auth_alg) { - if (!strcmp (auth_alg, "shared")) - wpa_set_data (conn_name, "auth_alg", "SHARED"); - else if (!strcmp (auth_alg, "open")) - wpa_set_data (conn_name, "auth_alg", "OPEN"); - else if (!strcmp (auth_alg, "leap")) { - wpa_set_data (conn_name, "auth_alg", "LEAP"); - wpa_set_data (conn_name, "eap", "LEAP"); - wpa_set_data (conn_name, "identity", (gchar *) - nm_setting_wireless_security_get_leap_username - (s_wsec)); - wpa_set_data (conn_name, "password", (gchar *) - nm_setting_wireless_security_get_leap_password - (s_wsec)); - *no_8021x = TRUE; - } - } else - wpa_set_data (conn_name, "auth_alg", NULL); - - /* Default WEP TX key index */ - if (wep) { - tmp = - g_strdup_printf ("%d", - nm_setting_wireless_security_get_wep_tx_keyidx - (s_wsec)); - wpa_set_data (conn_name, "wep_tx_keyidx", tmp); - g_free (tmp); - } else - wpa_set_data (conn_name, "wep_tx_keyidx", NULL); - - /* WEP keys */ - for (i = 0; i < 4; i++) { - int length; - - key = nm_setting_wireless_security_get_wep_key (s_wsec, i); - if (!key) - continue; - tmp = g_strdup_printf ("wep_key%d", i); - length = strlen (key); - if (length == 10 || length == 26 || length == 58) - wpa_set_data (conn_name, tmp, (gchar *) key); - else { - gchar *tmp_key = g_strdup_printf ("\"%s\"", key); - - wpa_set_data (conn_name, tmp, tmp_key); - g_free (tmp_key); - } - g_free (tmp); - } - - /* WPA Pairwise ciphers */ - wpa_set_data (conn_name, "pairwise", NULL); - str = g_string_new (NULL); - num = nm_setting_wireless_security_get_num_pairwise (s_wsec); - for (i = 0; i < num; i++) { - if (i > 0) - g_string_append_c (str, ' '); - cipher = nm_setting_wireless_security_get_pairwise (s_wsec, i); - tmp = g_ascii_strup (cipher, -1); - g_string_append (str, tmp); - g_free (tmp); - } - if (strlen (str->str)) - wpa_set_data (conn_name, "pairwise", str->str); - g_string_free (str, TRUE); - - /* WPA Group ciphers */ - wpa_set_data (conn_name, "group", NULL); - str = g_string_new (NULL); - num = nm_setting_wireless_security_get_num_groups (s_wsec); - for (i = 0; i < num; i++) { - if (i > 0) - g_string_append_c (str, ' '); - cipher = nm_setting_wireless_security_get_group (s_wsec, i); - tmp = g_ascii_strup (cipher, -1); - g_string_append (str, tmp); - g_free (tmp); - } - if (strlen (str->str)) - wpa_set_data (conn_name, "group", str->str); - g_string_free (str, TRUE); - - /* WPA Passphrase */ - if (wpa) { - GString *quoted = NULL; - - psk = nm_setting_wireless_security_get_psk (s_wsec); - if (psk && (strlen (psk) != 64)) { - quoted = g_string_sized_new (strlen (psk) + 2); - g_string_append_c (quoted, '"'); - g_string_append (quoted, psk); - g_string_append_c (quoted, '"'); - } - /* psk will be lost here if we don't check it for NULL */ - if (psk) - wpa_set_data (conn_name, "psk", - quoted ? quoted->str : (gchar *) psk); - if (quoted) - g_string_free (quoted, TRUE); - } else - wpa_set_data (conn_name, "psk", NULL); - - return TRUE; -} - -/* remove old ssid and add new one*/ -static void -update_wireless_ssid (NMConnection *connection, - const char *conn_name, - const char *ssid, - gboolean hex) -{ - if(strcmp (conn_name, ssid)){ - ifnet_delete_network (conn_name); - wpa_delete_security (conn_name); - } - - ifnet_add_network (ssid, "wireless"); - wpa_add_security (ssid); -} - -static gboolean -write_wireless_setting (NMConnection *connection, - const char *conn_name, - gboolean *no_8021x, - const char **out_new_name, - GError **error) -{ - NMSettingWireless *s_wireless; - GBytes *ssid; - const guint8 *ssid_data; - gsize ssid_len; - const char *mac, *bssid, *mode; - char buf[33]; - guint32 mtu, i; - gboolean adhoc = FALSE, hex_ssid = FALSE; - gchar *ssid_str, *tmp; - - s_wireless = nm_connection_get_setting_wireless (connection); - if (!s_wireless) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing '%s' setting", - NM_SETTING_WIRELESS_SETTING_NAME); - return FALSE; - } - - ssid = nm_setting_wireless_get_ssid (s_wireless); - if (!ssid) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing SSID in '%s' setting", - NM_SETTING_WIRELESS_SETTING_NAME); - return FALSE; - } - ssid_data = g_bytes_get_data (ssid, &ssid_len); - if (!ssid_len || ssid_len > 32) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid SSID in '%s' setting", - NM_SETTING_WIRELESS_SETTING_NAME); - return FALSE; - } - - /* If the SSID contains any non-alnum characters, we need to use - * the hex notation of the SSID instead. (Because openrc doesn't - * support these characters, see bug #356337) - */ - for (i = 0; i < ssid_len; i++) { - if (!g_ascii_isalnum (ssid_data[i])) { - hex_ssid = TRUE; - break; - } - } - - if (hex_ssid) { - GString *str; - - /* Hex SSIDs don't get quoted */ - str = g_string_sized_new (ssid_len * 2 + 3); - g_string_append (str, "0x"); - for (i = 0; i < ssid_len; i++) - g_string_append_printf (str, "%02X", ssid_data[i]); - update_wireless_ssid (connection, conn_name, str->str, hex_ssid); - ssid_str = g_string_free (str, FALSE); - } else { - /* Printable SSIDs get quoted */ - memset (buf, 0, sizeof (buf)); - memcpy (buf, ssid_data, ssid_len); - g_strstrip (buf); - update_wireless_ssid (connection, conn_name, buf, hex_ssid); - ssid_str = g_strdup (buf); - } - - ifnet_set_data (ssid_str, "mac", NULL); - mac = nm_setting_wireless_get_mac_address (s_wireless); - if (mac) - ifnet_set_data (ssid_str, "mac", mac); - - ifnet_set_data (ssid_str, "mtu", NULL); - mtu = nm_setting_wireless_get_mtu (s_wireless); - if (mtu) { - tmp = g_strdup_printf ("%u", mtu); - ifnet_set_data (ssid_str, "mtu", tmp); - g_free (tmp); - } - - ifnet_set_data (ssid_str, "mode", NULL); - mode = nm_setting_wireless_get_mode (s_wireless); - if (!mode || !strcmp (mode, "infrastructure")) { - wpa_set_data (ssid_str, "mode", "0"); - } else if (!strcmp (mode, "adhoc")) { - wpa_set_data (ssid_str, "mode", "1"); - adhoc = TRUE; - } else { - nm_log_warn (LOGD_SETTINGS, "Invalid mode '%s' in '%s' setting", - mode, NM_SETTING_WIRELESS_SETTING_NAME); - return FALSE; - } - - wpa_set_data (ssid_str, "bssid", NULL); - bssid = nm_setting_wireless_get_bssid (s_wireless); - if (bssid) - wpa_set_data (ssid_str, "bssid", bssid); - - if (nm_connection_get_setting_wireless_security (connection)) { - if (!write_wireless_security_setting - (connection, ssid_str, adhoc, no_8021x, error)) - return FALSE; - } else - wpa_delete_security (ssid_str); - - if (out_new_name) - *out_new_name = ifnet_get_data (ssid_str, "name"); - g_free (ssid_str); - return TRUE; -} - -static gboolean -write_wired_setting (NMConnection *connection, - const char *conn_name, - GError **error) -{ - NMSettingWired *s_wired; - const char *mac; - char *tmp; - guint32 mtu; - - s_wired = nm_connection_get_setting_wired (connection); - if (!s_wired) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing '%s' setting", - NM_SETTING_WIRED_SETTING_NAME); - return FALSE; - } - - ifnet_set_data (conn_name, "mac", NULL); - mac = nm_setting_wired_get_mac_address (s_wired); - if (mac) - ifnet_set_data (conn_name, "mac", mac); - - ifnet_set_data (conn_name, "mtu", NULL); - mtu = nm_setting_wired_get_mtu (s_wired); - if (mtu) { - tmp = g_strdup_printf ("%u", mtu); - ifnet_set_data (conn_name, "mtu", tmp); - g_free (tmp); - } - //FIXME may add connection type in future - //ifnet_set_data (conn_name, "TYPE", TYPE_ETHERNET); - - return TRUE; -} - -static gboolean -write_ip4_setting (NMConnection *connection, const char *conn_name, GError **error) -{ - NMSettingIPConfig *s_ip4; - const char *value; - guint32 i, num; - GString *searches; - GString *ips; - GString *routes; - GString *dns; - gboolean success = FALSE; - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (!s_ip4) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing '%s' setting", - NM_SETTING_IP4_CONFIG_SETTING_NAME); - return FALSE; - } - routes = g_string_new (NULL); - - value = nm_setting_ip_config_get_method (s_ip4); - g_assert (value); - if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { - - num = nm_setting_ip_config_get_num_addresses (s_ip4); - ips = g_string_new (NULL); - /* IPv4 addresses */ - for (i = 0; i < num; i++) { - NMIPAddress *addr; - - addr = nm_setting_ip_config_get_address (s_ip4, i); - - g_string_append_printf (ips, "\"%s/%u", - nm_ip_address_get_address (addr), - nm_ip_address_get_prefix (addr)); - - /* only the first gateway will be written */ - if (i == 0 && nm_setting_ip_config_get_gateway (s_ip4)) { - g_string_append_printf (routes, - "\"default via %s\" ", - nm_setting_ip_config_get_gateway (s_ip4)); - } - } - ifnet_set_data (conn_name, "config", ips->str); - g_string_free (ips, TRUE); - } else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) - ifnet_set_data (conn_name, "config", "shared"); - else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) - ifnet_set_data (conn_name, "config", "autoip"); - else - ifnet_set_data (conn_name, "config", "dhcp"); - - /* DNS Servers */ - num = nm_setting_ip_config_get_num_dns (s_ip4); - if (num > 0) { - dns = g_string_new (NULL); - for (i = 0; i < num; i++) { - const char *ip; - - ip = nm_setting_ip_config_get_dns (s_ip4, i); - g_string_append_printf (dns, " %s", ip); - } - ifnet_set_data (conn_name, "dns_servers", dns->str); - g_string_free (dns, TRUE); - } else - ifnet_set_data (conn_name, "dns_servers", NULL); - - /* DNS Searches */ - num = nm_setting_ip_config_get_num_dns_searches (s_ip4); - if (num > 0) { - searches = g_string_new (NULL); - for (i = 0; i < num; i++) { - if (i > 0) - g_string_append_c (searches, ' '); - g_string_append (searches, - nm_setting_ip_config_get_dns_search - (s_ip4, i)); - } - ifnet_set_data (conn_name, "dns_search", searches->str); - g_string_free (searches, TRUE); - } else - ifnet_set_data (conn_name, "dns_search", NULL); - /* FIXME Will be implemented when configuration supports it - if (!strcmp(value, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { - value = nm_setting_ip_config_get_dhcp_hostname(s_ip4); - if (value) - ifnet_set_data(conn_name, "DHCP_HOSTNAME", value, - FALSE); - - value = nm_setting_ip_config_get_dhcp_client_id(s_ip4); - if (value) - ifnet_set_data(conn_name, "DHCP_CLIENT_ID", value, - FALSE); - } - */ - - /* Static routes */ - num = nm_setting_ip_config_get_num_routes (s_ip4); - if (num > 0) { - for (i = 0; i < num; i++) { - NMIPRoute *route; - const char *next_hop; - - route = nm_setting_ip_config_get_route (s_ip4, i); - - next_hop = nm_ip_route_get_next_hop (route); - if (!next_hop) - next_hop = "0.0.0.0"; - - g_string_append_printf (routes, "\"%s/%u via %s\" ", - nm_ip_route_get_dest (route), - nm_ip_route_get_prefix (route), - next_hop); - } - } - if (routes->len > 0) - ifnet_set_data (conn_name, "routes", routes->str); - else - ifnet_set_data (conn_name, "routes", NULL); - g_string_free (routes, TRUE); - - success = TRUE; - - return success; -} - -static void -write_route6_file (NMSettingIPConfig *s_ip6, const char *conn_name) -{ - NMIPRoute *route; - const char *next_hop; - guint32 i, num; - GString *routes_string; - const char *old_routes; - - g_return_if_fail (s_ip6 != NULL); - num = nm_setting_ip_config_get_num_routes (s_ip6); - if (num == 0) - return; - - old_routes = ifnet_get_data (conn_name, "routes"); - routes_string = g_string_new (old_routes); - if (old_routes) - g_string_append (routes_string, "\" "); - for (i = 0; i < num; i++) { - route = nm_setting_ip_config_get_route (s_ip6, i); - - next_hop = nm_ip_route_get_next_hop (route); - if (!next_hop) - next_hop = "::"; - - g_string_append_printf (routes_string, "\"%s/%u via %s\" ", - nm_ip_route_get_dest (route), - nm_ip_route_get_prefix (route), - next_hop); - } - if (num > 0) - ifnet_set_data (conn_name, "routes", routes_string->str); - g_string_free (routes_string, TRUE); -} - -static gboolean -write_ip6_setting (NMConnection *connection, const char *conn_name, GError **error) -{ - NMSettingIPConfig *s_ip6; - const char *value; - guint32 i, num; - GString *searches; - NMIPAddress *addr; - - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (!s_ip6) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing '%s' setting", - NM_SETTING_IP6_CONFIG_SETTING_NAME); - return FALSE; - } - - value = nm_setting_ip_config_get_method (s_ip6); - g_assert (value); - if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { - ifnet_set_data (conn_name, "enable_ipv6", "false"); - return TRUE; - } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { - /* nothing to do now */ - } else { - // if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { - const char *config = ifnet_get_data (conn_name, "config"); - gchar *tmp; - - if (!config) - tmp = g_strdup_printf ("dhcp6"); - else - tmp = g_strdup_printf ("%s\" \"dhcp6\"", config); - ifnet_set_data (conn_name, "config", tmp); - g_free (tmp); - } - /* else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { - } else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) { - } else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { - } */ - - /* Remember to set IPv6 enabled */ - ifnet_set_data (conn_name, "enable_ipv6", "true"); - - if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { - const char *config = ifnet_get_data (conn_name, "config"); - gchar *tmp; - GString *ip_str; - - if (!config) - config = ""; - num = nm_setting_ip_config_get_num_addresses (s_ip6); - - /* IPv6 addresses */ - ip_str = g_string_new (NULL); - for (i = 0; i < num; i++) { - addr = nm_setting_ip_config_get_address (s_ip6, i); - - g_string_append_printf (ip_str, "\"%s/%u\"", - nm_ip_address_get_address (addr), - nm_ip_address_get_prefix (addr)); - } - tmp = g_strdup_printf ("%s\" %s", config, ip_str->str); - ifnet_set_data (conn_name, "config", tmp); - g_free (tmp); - g_string_free (ip_str, TRUE); - } - - /* DNS Servers */ - num = nm_setting_ip_config_get_num_dns (s_ip6); - if (num > 0) { - const char *dns_servers = ifnet_get_data (conn_name, "dns_servers"); - gchar *tmp; - GString *dns_string = g_string_new (NULL); - const char *dns; - - if (!dns_servers) - dns_servers = ""; - for (i = 0; i < num; i++) { - dns = nm_setting_ip_config_get_dns (s_ip6, i); - - if (!strstr (dns_servers, dns)) - g_string_append_printf (dns_string, "%s ", dns); - } - tmp = g_strdup_printf ("%s %s", dns_servers, dns_string->str); - ifnet_set_data (conn_name, "dns_servers", tmp); - g_free (tmp); - g_string_free (dns_string, TRUE); - - } else - /* DNS Searches */ - num = nm_setting_ip_config_get_num_dns_searches (s_ip6); - if (num > 0) { - const char *ip4_domains; - - ip4_domains = ifnet_get_data (conn_name, "dns_search"); - if (!ip4_domains) - ip4_domains = ""; - searches = g_string_new (ip4_domains); - for (i = 0; i < num; i++) { - const gchar *search = NULL; - - search = - nm_setting_ip_config_get_dns_search (s_ip6, i); - if (search && !strstr (searches->str, search)) { - if (searches->len > 0) - g_string_append_c (searches, ' '); - g_string_append (searches, search); - } - } - ifnet_set_data (conn_name, "dns_search", searches->str); - g_string_free (searches, TRUE); - } - - write_route6_file (s_ip6, conn_name); - return TRUE; -} - -static gboolean -write_pppoe_setting (const char *conn_name, NMSettingPppoe * s_pppoe) -{ - const gchar *value; - - value = nm_setting_pppoe_get_username (s_pppoe); - if (!value) { - return FALSE; - } - ifnet_set_data (conn_name, "username", (gchar *) value); - - value = nm_setting_pppoe_get_password (s_pppoe); - /* password could be NULL here */ - if (value) { - ifnet_set_data (conn_name, "password", (gchar *) value); - } - return TRUE; -} - -gboolean -ifnet_update_parsers_by_connection (NMConnection *connection, - const char *conn_name, - const char *config_file, - const char *wpa_file, - gchar **out_new_name, - gchar **out_backup, - GError **error) -{ - NMSettingConnection *s_con; - NMSettingIPConfig *s_ip6; - gboolean success = FALSE; - const char *type; - gboolean no_8021x = FALSE; - gboolean wired = FALSE, pppoe = TRUE; - const char *new_name = NULL; - - if (!ifnet_can_write_connection (connection, error)) - return FALSE; - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - - type = nm_setting_connection_get_connection_type (s_con); - if (!type) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing connection type!"); - goto out; - } - - if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) { - /* Writing wired setting */ - if (!write_wired_setting (connection, conn_name, error)) - goto out; - wired = TRUE; - no_8021x = TRUE; - } else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) { - /* Writing wireless setting */ - if (!write_wireless_setting (connection, conn_name, &no_8021x, &new_name, error)) - goto out; - } else if (!strcmp (type, NM_SETTING_PPPOE_SETTING_NAME)) { - NMSettingPppoe *s_pppoe; - - /* Writing pppoe setting */ - s_pppoe = nm_connection_get_setting_pppoe (connection); - if (!write_pppoe_setting (conn_name, s_pppoe)) - goto out; - pppoe = TRUE; - wired = TRUE; - no_8021x = TRUE; - } else { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, - "Can't write connection type '%s'", type); - goto out; - } - - /* connection name may have been updated; use it when writing out - * the rest of the settings. - */ - if (new_name) - conn_name = new_name; - - //FIXME wired connection doesn't support 8021x now - if (!no_8021x) { - if (!write_8021x_setting (connection, conn_name, wired, error)) - goto out; - } - - /* IPv4 Setting */ - if (!write_ip4_setting (connection, conn_name, error)) - goto out; - - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (s_ip6) { - /* IPv6 Setting */ - if (!write_ip6_setting (connection, conn_name, error)) - goto out; - } - - /* Connection Setting */ - ifnet_set_data (conn_name, "auto", - nm_setting_connection_get_autoconnect (s_con) ? "true" : "false"); - ifnet_set_data (conn_name, "uuid", nm_connection_get_uuid (connection)); - - /* Write changes to disk */ - success = ifnet_flush_to_file (config_file, out_backup); - if (success) - wpa_flush_to_file (wpa_file); - - if (out_new_name) - *out_new_name = g_strdup (conn_name); - -out: - return success; -} - -gboolean -ifnet_delete_connection_in_parsers (const char *conn_name, - const char *config_file, - const char *wpa_file, - gchar **out_backup) -{ - gboolean result = FALSE; - - ifnet_delete_network (conn_name); - result = ifnet_flush_to_file (config_file, out_backup); - if (result) { - /* connection may not have security information - * so simply ignore the return value*/ - wpa_delete_security (conn_name); - wpa_flush_to_file (wpa_file); - } - - return result; -} - -static void -check_unsupported_secrets (NMSetting *setting, - const char *key, - const GValue *value, - GParamFlags flags, - gpointer user_data) -{ - gboolean *unsupported_secret = user_data; - - if (flags & NM_SETTING_PARAM_SECRET) { - NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; - - if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL)) - g_return_if_reached (); - if (secret_flags != NM_SETTING_SECRET_FLAG_NONE) - *unsupported_secret = TRUE; - } -} - -gboolean -ifnet_can_write_connection (NMConnection *connection, GError **error) -{ - NMSettingConnection *s_con; - gboolean has_unsupported_secrets = FALSE; - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - - /* If the connection is not available for all users, ignore - * it as this plugin only deals with System Connections */ - if (nm_setting_connection_get_num_permissions (s_con)) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, - "The ifnet plugin does not support non-system-wide connections."); - return FALSE; - } - - /* Only support wired, wifi, and PPPoE */ - if ( !nm_connection_is_type (connection, NM_SETTING_WIRED_SETTING_NAME) - && !nm_connection_is_type (connection, NM_SETTING_WIRELESS_SETTING_NAME) - && !nm_connection_is_type (connection, NM_SETTING_PPPOE_SETTING_NAME)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, - "The ifnet plugin cannot write the connection '%s' (type '%s')", - nm_connection_get_id (connection), - nm_setting_connection_get_connection_type (s_con)); - return FALSE; - } - - /* If the connection has flagged secrets, ignore - * it as this plugin does not deal with user agent service */ - nm_connection_for_each_setting_value (connection, - check_unsupported_secrets, - &has_unsupported_secrets); - if (has_unsupported_secrets) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, - "The ifnet plugin only supports persistent system secrets."); - return FALSE; - } - - return TRUE; -} - -/* get the available wired name(eth*). */ -static gchar * -get_wired_name (void) -{ - int i = 0; - - for (; i < 256; i++) { - gchar *conn_name = g_strdup_printf ("eth%d", i); - - if (!ifnet_has_network (conn_name)) { - return conn_name; - } else - g_free (conn_name); - } - return NULL; -} - -/* get the available pppoe name(ppp*). */ -static gchar * -get_ppp_name (void) -{ - int i = 0; - - for (; i < 256; i++) { - gchar *conn_name = g_strdup_printf ("ppp%d", i); - - if (!ifnet_has_network (conn_name)) { - return conn_name; - } else - g_free (conn_name); - } - return NULL; -} - -/* get wireless ssid */ -static gchar * -get_wireless_name (NMConnection * connection) -{ - NMSettingWireless *s_wireless; - GBytes *ssid; - const guint8 *ssid_data; - gsize ssid_len; - gboolean hex_ssid = FALSE; - gchar *result = NULL; - char buf[33]; - int i = 0; - - s_wireless = nm_connection_get_setting_wireless (connection); - if (!s_wireless) - return NULL; - - ssid = nm_setting_wireless_get_ssid (s_wireless); - ssid_data = g_bytes_get_data (ssid, &ssid_len); - if (!ssid_len || ssid_len > 32) { - return NULL; - } - - for (i = 0; i < ssid_len; i++) { - if (!g_ascii_isprint (ssid_data[i])) { - hex_ssid = TRUE; - break; - } - } - - if (hex_ssid) { - GString *str; - - str = g_string_sized_new (ssid_len * 2 + 3); - g_string_append (str, "0x"); - for (i = 0; i < ssid_len; i++) - g_string_append_printf (str, "%02X", ssid_data[i]); - result = g_strdup (str->str); - g_string_free (str, TRUE); - } else { - memset (buf, 0, sizeof (buf)); - memcpy (buf, ssid_data, ssid_len); - result = g_strdup_printf ("%s", buf); - g_strstrip (result); - } - - return result; -} - -gboolean -ifnet_add_new_connection (NMConnection *connection, - const char *config_file, - const char *wpa_file, - gchar **out_new_name, - gchar **out_backup, - GError **error) -{ - NMSettingConnection *s_con; - gboolean success = FALSE; - const char *type; - gchar *new_type, *new_name = NULL; - - if (!ifnet_can_write_connection (connection, error)) - return FALSE; - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - type = nm_setting_connection_get_connection_type (s_con); - g_assert (type); - - nm_log_info (LOGD_SETTINGS, "Adding %s connection", type); - - /* get name and type - * Wireless type: wireless - * Wired type: wired - * PPPoE type: ppp*/ - if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) { - new_name = get_wired_name (); - if (!new_name) - goto out; - new_type = "wired"; - } else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) { - new_name = get_wireless_name (connection); - new_type = "wireless"; - } else if (!strcmp (type, NM_SETTING_PPPOE_SETTING_NAME)) { - new_name = get_ppp_name (); - if (!new_name) - goto out; - new_type = "ppp"; - } else { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, - "Can't write connection type '%s'", type); - goto out; - } - - if (ifnet_add_network (new_name, new_type)) { - success = ifnet_update_parsers_by_connection (connection, - new_name, - config_file, - wpa_file, - NULL, - out_backup, - error); - } - - nm_log_info (LOGD_SETTINGS, "Added new connection: %s, result: %s", - new_name, success ? "success" : "fail"); - -out: - if (!success || !out_new_name) - g_free (new_name); - else if (out_new_name) - *out_new_name = new_name; - return success; -} - diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h deleted file mode 100644 index 51bc34b9..00000000 --- a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * Mu Qiao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 1999-2010 Gentoo Foundation, Inc. - */ - -#ifndef _CONNECTION_PARSER_H -#define _CONNECTION_PARSER_H - -#include "nm-connection.h" - -#include "nms-ifnet-net-parser.h" - -gboolean ifnet_can_write_connection (NMConnection *connection, GError **error); - -NMConnection *ifnet_update_connection_from_config_block (const char *conn_name, - const char *basepath, - GError **error); - -/* nm_conn_name is used to update nm_ifnet_connection's priv data */ -gboolean ifnet_update_parsers_by_connection (NMConnection *connection, - const char *conn_name, - const char *config_file, - const char *wpa_file, - gchar **out_new_name, - gchar **out_backup, - GError **error); - -gboolean ifnet_delete_connection_in_parsers (const char *conn_name, - const char *config_file, - const char *wpa_file, - gchar **out_backup); - -gboolean ifnet_add_new_connection (NMConnection *connection, - const char *config_file, - const char *wpa_file, - gchar **out_new_name, - gchar **out_backup, - GError ** error); -#endif diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection.c b/src/settings/plugins/ifnet/nms-ifnet-connection.c deleted file mode 100644 index ce0b3f2b..00000000 --- a/src/settings/plugins/ifnet/nms-ifnet-connection.c +++ /dev/null @@ -1,233 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * Mu Qiao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 1999-2010 Gentoo Foundation, Inc. - */ - -#include "nm-default.h" - -#include "nms-ifnet-connection.h" - -#include -#include - -#include "nm-dbus-interface.h" -#include "nm-utils.h" -#include "nm-setting-wireless-security.h" -#include "settings/nm-settings-connection.h" -#include "settings/nm-settings-plugin.h" - -#include "nms-ifnet-connection-parser.h" -#include "nms-ifnet-net-parser.h" -#include "nms-ifnet-net-utils.h" -#include "nms-ifnet-wpa-parser.h" -#include "nms-ifnet-plugin.h" - -/*****************************************************************************/ - -enum { - IFNET_SETUP_MONITORS, - IFNET_CANCEL_MONITORS, - IFNET_LAST_SIGNAL -}; - -static guint signals[IFNET_LAST_SIGNAL] = { 0 }; - -typedef struct { - gchar *conn_name; - NMSettingsPlugin *config; -} NMIfnetConnectionPrivate; - -struct _NMIfnetConnection { - NMSettingsConnection parent; - NMIfnetConnectionPrivate _priv; -}; - -struct _NMIfnetConnectionClass { - NMSettingsConnectionClass parent; -}; - -G_DEFINE_TYPE (NMIfnetConnection, nm_ifnet_connection, NM_TYPE_SETTINGS_CONNECTION) - -#define NM_IFNET_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMIfnetConnection, NM_IS_IFNET_CONNECTION) - -/*****************************************************************************/ - -const char * -nm_ifnet_connection_get_conn_name (NMIfnetConnection *connection) -{ - return NM_IFNET_CONNECTION_GET_PRIVATE (connection)->conn_name; -} - -static gboolean -commit_changes (NMSettingsConnection *connection, - NMConnection *new_connection, - NMSettingsConnectionCommitReason commit_reason, - NMConnection **out_reread_connection, - char **out_logmsg_change, - GError **error) -{ - NMIfnetConnectionPrivate *priv = NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) connection); - char *new_name = NULL; - gboolean success = FALSE; - gboolean added = FALSE; - - nm_assert (out_reread_connection && !*out_reread_connection); - nm_assert (!out_logmsg_change || !*out_logmsg_change); - - g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0); - - if (priv->conn_name) { - success = ifnet_update_parsers_by_connection (new_connection, - priv->conn_name, - CONF_NET_FILE, - WPA_SUPPLICANT_CONF, - &new_name, - NULL, - error); - } else { - added = TRUE; - success = ifnet_add_new_connection (new_connection, - CONF_NET_FILE, - WPA_SUPPLICANT_CONF, - &new_name, - NULL, - error); - } - - g_assert (!!success == (new_name != NULL)); - if (success) { - g_free (priv->conn_name); - priv->conn_name = new_name; - } - - reload_parsers (); - - g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); - - if (success) { - NM_SET_OUT (out_logmsg_change, - g_strdup_printf ("ifcfg-rh: %s %s", - added ? "persist" : "updated", - new_name)); - } - return success; -} - -static gboolean -delete (NMSettingsConnection *connection, - GError **error) -{ - NMIfnetConnectionPrivate *priv = NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) connection); - - /* Only connections which exist in /etc/conf.d/net will have a conn_name */ - if (priv->conn_name) { - g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0); - - if (!ifnet_delete_connection_in_parsers (priv->conn_name, CONF_NET_FILE, WPA_SUPPLICANT_CONF, NULL)) { - nm_log_warn (LOGD_SETTINGS, "Failed to delete %s", priv->conn_name); - reload_parsers (); - /* let's not return an error. */ - } - - g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0); - } - - return TRUE; -} - -/*****************************************************************************/ - -static void -nm_ifnet_connection_init (NMIfnetConnection * connection) -{ -} - -NMIfnetConnection * -nm_ifnet_connection_new (NMConnection *source, const char *conn_name) -{ - NMConnection *tmp; - GObject *object; - GError *error = NULL; - gboolean update_unsaved = TRUE; - - g_return_val_if_fail (source || conn_name, NULL); - - if (source) - tmp = g_object_ref (source); - else { - tmp = ifnet_update_connection_from_config_block (conn_name, NULL, &error); - if (!tmp) { - nm_log_warn (LOGD_SETTINGS, "Could not read connection '%s': %s", - conn_name, error->message); - g_error_free (error); - return NULL; - } - - /* If we just read the connection from disk, it's clearly not Unsaved */ - update_unsaved = FALSE; - } - - object = (GObject *) g_object_new (NM_TYPE_IFNET_CONNECTION, NULL); - - NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) object)->conn_name = g_strdup (conn_name); - if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (object), - tmp, - update_unsaved - ? NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED - : NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - NULL, - NULL)) { - g_object_unref (object); - return NULL; - } - g_object_unref (tmp); - - return NM_IFNET_CONNECTION (object); -} - -static void -finalize (GObject * object) -{ - g_free (NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) object)->conn_name); - G_OBJECT_CLASS (nm_ifnet_connection_parent_class)->finalize (object); -} - -static void -nm_ifnet_connection_class_init (NMIfnetConnectionClass * ifnet_connection_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (ifnet_connection_class); - NMSettingsConnectionClass *settings_class = NM_SETTINGS_CONNECTION_CLASS (ifnet_connection_class); - - object_class->finalize = finalize; - - settings_class->delete = delete; - settings_class->commit_changes = commit_changes; - - signals[IFNET_SETUP_MONITORS] = - g_signal_new ("ifnet_setup_monitors", - G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, - 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - signals[IFNET_CANCEL_MONITORS] = - g_signal_new ("ifnet_cancel_monitors", - G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, - 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection.h b/src/settings/plugins/ifnet/nms-ifnet-connection.h deleted file mode 100644 index 1bc06644..00000000 --- a/src/settings/plugins/ifnet/nms-ifnet-connection.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * Mu Qiao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 1999-2010 Gentoo Foundation, Inc. - */ - -#ifndef __NETWORKMANAGER_IFNET_CONNECTION_H__ -#define __NETWORKMANAGER_IFNET_CONNECTION_H__ - -#include "settings/nm-settings-connection.h" - -#include "nms-ifnet-net-parser.h" - -#define NM_TYPE_IFNET_CONNECTION (nm_ifnet_connection_get_type ()) -#define NM_IFNET_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IFNET_CONNECTION, NMIfnetConnection)) -#define NM_IFNET_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IFNET_CONNECTION, NMIfnetConnectionClass)) -#define NM_IS_IFNET_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IFNET_CONNECTION)) -#define NM_IS_IFNET_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IFNET_CONNECTION)) -#define NM_IFNET_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IFNET_CONNECTION, NMIfnetConnectionClass)) - -typedef struct _NMIfnetConnection NMIfnetConnection; -typedef struct _NMIfnetConnectionClass NMIfnetConnectionClass; - -GType nm_ifnet_connection_get_type (void); - -NMIfnetConnection *nm_ifnet_connection_new (NMConnection *source, - const char *conn_name); - -const char *nm_ifnet_connection_get_conn_name (NMIfnetConnection *connection); - -#endif /* __NETWORKMANAGER_IFNET_CONNECTION_H__ */ diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-parser.c b/src/settings/plugins/ifnet/nms-ifnet-net-parser.c deleted file mode 100644 index d3e47219..00000000 --- a/src/settings/plugins/ifnet/nms-ifnet-net-parser.c +++ /dev/null @@ -1,734 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * Mu Qiao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 1999-2010 Gentoo Foundation, Inc. - */ - -#include "nm-default.h" - -#include "nms-ifnet-net-parser.h" - -#include -#include -#include -#include - -#include "settings/nm-settings-plugin.h" -#include "platform/nm-platform.h" - -#include "nms-ifnet-plugin.h" -#include "nms-ifnet-net-utils.h" - -/* Save all the connection information */ -static GHashTable *conn_table; - -/* Save global settings which are used for writing*/ -static GHashTable *global_settings_table; - -/* Save functions */ -static GList *functions_list; - -/* Used to decide whether to write changes to file*/ -static gboolean net_parser_data_changed = FALSE; - -static GHashTable * -add_new_connection_config (const gchar * type, const gchar * name) -{ - GHashTable *new_conn; - gchar *new_name; - - if (!name) - return NULL; - - /* Return existing connection */ - if ((new_conn = g_hash_table_lookup (conn_table, name)) != NULL) - return new_conn; - new_conn = g_hash_table_new (nm_str_hash, g_str_equal); - new_name = g_strdup (name); - g_hash_table_insert (new_conn, g_strdup ("name"), new_name); - g_hash_table_insert (new_conn, g_strdup ("type"), g_strdup (type)); - g_hash_table_insert (conn_table, new_name, new_conn); - return new_conn; -} - -gboolean -ifnet_add_network (const char *name, const char *type) -{ - if (ifnet_has_network (name)) - return TRUE; - if (add_new_connection_config (type, name)) { - nm_log_info (LOGD_SETTINGS, "Adding network for %s", name); - net_parser_data_changed = TRUE; - return TRUE; - } - return FALSE; -} - -gboolean -ifnet_has_network (const char *conn_name) -{ - return g_hash_table_lookup (conn_table, conn_name) != NULL; -} - -static GHashTable * -get_connection_config (const char *name) -{ - return g_hash_table_lookup (conn_table, name); -} - -/* Ignored name won't be treated as wireless ssid */ -static gchar *ignore_name[] = { - "vlan", "bond", "atm", "ath", "ippp", "vpn", "tap", "tun", "1", - "br", "nas", "6to4", "timeout", "kvm", "force", NULL -}; - -static gboolean -ignore_connection_name (const char *name) -{ - gboolean result = FALSE; - guint i = 0; - - /* check ignore_name list */ - while (ignore_name[i] != NULL) { - if (g_ascii_strncasecmp - (name, ignore_name[i], strlen (ignore_name[i])) == 0) { - return TRUE; - } - i++; - } - /* Ignore mac address based configuration */ - if (strlen (name) == 12 && is_hex (name)) - result = TRUE; - return result; -} - -static gboolean -is_global_setting (char *key) -{ - static gchar *global_settings[] = { "wpa_supplicant_", NULL }; - int i; - - for (i = 0; global_settings[i] != NULL; i++) { - if (strstr (key, global_settings[i])) - return 1; - } - return 0; -} - -/* Parse a complete line */ -/* Connection type is determined here */ -static void -init_block_by_line (gchar * buf) -{ - gchar **key_value; - gchar *pos; - gchar *data; - gchar *tmp; - GHashTable *conn; - - key_value = g_strsplit (buf, "=", 2); - if (g_strv_length (key_value) != 2) { - nm_log_warn (LOGD_SETTINGS, "Can't handle this line: %s\n", buf); - g_strfreev (key_value); - return; - } - pos = g_strrstr (key_value[0], "_"); - if (pos == NULL || is_global_setting (key_value[0])) { - /* global data */ - data = g_strdup (key_value[1]); - tmp = strip_string (data, '"'); - strip_string (tmp, '\''); - nm_log_info (LOGD_SETTINGS, "global:%s-%s\n", key_value[0], tmp); - g_hash_table_insert (global_settings_table, g_strdup (key_value[0]), g_strdup (tmp)); - g_strfreev (key_value); - g_free (data); - return; - } - *pos++ = '\0'; - if ((conn = get_connection_config (pos)) == NULL) { - if (g_ascii_strncasecmp (pos, "eth", 3) == 0 - && strlen (pos) == 4) - /* wired connection */ - conn = add_new_connection_config ("wired", pos); - else if (g_ascii_strncasecmp (pos, "ppp", 3) == 0 - && strlen (pos) == 4) - /* pppoe connection */ - conn = add_new_connection_config ("ppp", pos); - else if (ignore_connection_name (pos)) { - /* ignored connection */ - conn = add_new_connection_config ("ignore", pos); - } else { - int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, pos); - - if (ifindex && nm_platform_link_get_type (NM_PLATFORM_GET, ifindex) != NM_LINK_TYPE_WIFI) - /* wired connection */ - conn = add_new_connection_config ("wired", pos); - else - /* wireless connection */ - conn = add_new_connection_config ("wireless", pos); - } - } - data = g_strdup (key_value[1]); - tmp = strip_string (data, '"'); - strip_string (tmp, '\''); - if (conn) - g_hash_table_insert (conn, strip_string (g_strdup (key_value[0]), ' '), - g_strdup (tmp)); - g_free (data); - g_strfreev (key_value); -} - -static void -destroy_connection_config (GHashTable * conn) -{ - gpointer key, value; - GHashTableIter iter; - - g_hash_table_iter_init (&iter, conn); - while (g_hash_table_iter_next (&iter, &key, &value)) { - g_free (key); - g_free (value); - } - - g_hash_table_destroy (conn); -} - -static void -strip_function (GIOChannel * channel, gchar * line) -{ - - int counter = 0; - gchar *p, *tmp; - gboolean begin = FALSE; - GString *function_str = g_string_new (line); - - g_string_append (function_str, "\n"); - while (1) { - p = line; - while (*p != '\0') { - if (*p == '{') { - counter++; - begin = TRUE; - } else if (*p == '}') - counter--; - p++; - } - if (begin && counter == 0) { - g_free (line); - goto done; - } - while (1) { - g_free (line); - if (g_io_channel_read_line - (channel, &line, NULL, NULL, - NULL) == G_IO_STATUS_EOF) - goto done; - g_string_append (function_str, line); - tmp = g_strdup (line); - g_strstrip (tmp); - if (tmp[0] != '#' && tmp[0] != '\0') { - g_free (tmp); - break; - } else - g_free (tmp); - } - } -done: - functions_list = - g_list_append (functions_list, g_strdup (function_str->str)); - g_string_free (function_str, TRUE); -} - -static gboolean -is_function (gchar * line) -{ - static gchar *func_names[] = - { "preup", "predown", "postup", "postdown", "failup", "faildown", - NULL, - }; - int i; - - for (i = 0; func_names[i]; i++) { - if (g_str_has_prefix (line, func_names[i])) { - nm_log_info (LOGD_SETTINGS, "Ignoring function: %s", func_names[i]); - return TRUE; - } - } - return FALSE; -} - -static void -append_line (GString *buf, gchar* line) -{ - gchar *pos = NULL; - - if ((pos = strchr (line, '#')) != NULL) - *pos = '\0'; - g_strstrip (line); - - if (line[0] != '\0') - g_string_append_printf (buf, " %s", line); - g_free (line); -} - -gboolean -ifnet_init (gchar * config_file) -{ - GIOChannel *channel = NULL; - gchar *line; - - /* Handle multiple lines with brackets */ - gboolean complete = TRUE; - - gboolean openrc_style = TRUE; - - /* line buffer */ - GString *buf; - - net_parser_data_changed = FALSE; - - conn_table = g_hash_table_new (nm_str_hash, g_str_equal); - global_settings_table = g_hash_table_new (nm_str_hash, g_str_equal); - functions_list = NULL; - - if (g_file_test (config_file, G_FILE_TEST_IS_REGULAR)) - channel = g_io_channel_new_file (config_file, "r", NULL); - if (channel == NULL) { - nm_log_warn (LOGD_SETTINGS, "Can't open %s", config_file); - return FALSE; - } - - buf = g_string_new (NULL); - while (g_io_channel_read_line - (channel, &line, NULL, NULL, NULL) != G_IO_STATUS_EOF) { - g_strstrip (line); - /* convert multiple lines to a complete line and - * pass it to init_block_by_line() */ - if (is_function (line)) { - strip_function (channel, line); - continue; - } - - // New openrc style, bash arrays are not allowed. We only care about '"' - if (openrc_style && line[0] != '#' && line[0] != '\0' - && !strchr (line, '(') && !strchr (line, ')')) { - gchar *tmp = line; - - while ((tmp = strchr (tmp, '"')) != NULL) { - complete = !complete; - ++tmp; - } - - append_line (buf, line); - // Add "(separator) for routes. It will be easier for later parsing - if (strstr (buf->str, "via")) - g_string_append_printf (buf, "\""); - - if (!complete) - continue; - - strip_string (buf->str, '"'); - - init_block_by_line (buf->str); - g_string_free (buf, TRUE); - buf = g_string_new (NULL); - } - // Old bash arrays for baselayout-1, to be deleted - else if (line[0] != '#' && line[0] != '\0') { - if (!complete) { - complete = - g_strrstr (line, - ")") == NULL ? FALSE : TRUE; - - append_line (buf, line); - if (!complete) { - openrc_style = FALSE; - continue; - } - else { - openrc_style = TRUE; - } - } else { - complete = - (g_strrstr (line, "(") != NULL - && g_strrstr (line, ")") != NULL) - || g_strrstr (line, "(") == NULL; - - append_line (buf, line); - if (!complete) - { - openrc_style = FALSE; - continue; - } else { - openrc_style = TRUE; - } - } - init_block_by_line (buf->str); - g_string_free (buf, TRUE); - buf = g_string_new (NULL); - } else - /* Blank line or comment line */ - g_free (line); - } - - g_string_free (buf, TRUE); - g_io_channel_shutdown (channel, FALSE, NULL); - g_io_channel_unref (channel); - return TRUE; -} - -const char * -ifnet_get_data (const char *conn_name, const char *key) -{ - GHashTable *conn; - - g_return_val_if_fail (conn_name && key, NULL); - - conn = g_hash_table_lookup (conn_table, conn_name); - - if (conn) - return g_hash_table_lookup (conn, key); - return NULL; -} - -/* format ip values for comparison */ -static gchar* -format_ip_for_comparison (const gchar * value) -{ - gchar **ipset; - guint length, i; - GString *formated_string = g_string_new (NULL); - gchar *formatted = NULL; - - ipset = g_strsplit (value, "\"", 0); - length = g_strv_length (ipset); - - for (i = 0; i < length; i++) - { - strip_string (ipset[i], ' '); - if (ipset[i][0] != '\0') - g_string_append_printf (formated_string, - "%s ", ipset[i]); - } - formatted = g_strdup (formated_string->str); - formatted[formated_string->len - 1] = '\0'; - - g_string_free (formated_string, TRUE); - g_strfreev (ipset); - - return formatted; -} - -void -ifnet_set_data (const char *conn_name, const char *key, const char *value) -{ - gpointer old_key = NULL, old_value = NULL; - GHashTable *conn = g_hash_table_lookup (conn_table, conn_name); - gchar * stripped = NULL; - - if (!conn) { - nm_log_warn (LOGD_SETTINGS, "%s does not exist!", conn_name); - return; - } - if (value){ - stripped = g_strdup (value); - strip_string (stripped, '"'); - } - /* Remove existing key value pair */ - if (g_hash_table_lookup_extended (conn, key, &old_key, &old_value)) { - - /* This ugly hack is due to baselayout compatibility. We have to - * deal with different ip format. So sometimes we have the same ips - * but different strings. - */ - if (stripped && - (!strcmp (key, "config") - || !strcmp (key, "routes") - || !strcmp (key, "pppd") - || !strcmp (key, "chat"))) - { - gchar *old_ips = format_ip_for_comparison (old_value); - gchar *new_ips = format_ip_for_comparison (value); - if(!strcmp (old_ips, new_ips)) - { - g_free (stripped); - g_free (old_ips); - g_free (new_ips); - return; - } - g_free (old_ips); - g_free (new_ips); - } - - if (stripped && !strcmp (old_value, stripped)) { - g_free (stripped); - return; - } - g_hash_table_remove (conn, old_key); - g_free (old_key); - g_free (old_value); - } else if (!value) - return; - if (stripped) - g_hash_table_insert (conn, g_strdup (key), stripped); - net_parser_data_changed = TRUE; -} - -// Remember to free return value -const char * -ifnet_get_global_data (const gchar * key) -{ - return g_hash_table_lookup (global_settings_table, key); -} - -// Return names of legal connections -GList * -ifnet_get_connection_names (void) -{ - GList *names = g_hash_table_get_keys (conn_table); - GList *iter, *result = NULL; - - for (iter = names; iter; iter = iter->next) { - if (!ignore_connection_name (iter->data)) - result = g_list_prepend (result, iter->data); - } - - g_list_free (names); - return g_list_reverse (result); -} - -/* format IP and route for writing */ -static void -format_ips (gchar * value, gchar ** out_line, gchar * key, gchar * name) -{ - gchar **ipset; - guint length, i; - GString *formated_string = g_string_new (NULL); - - strip_string (value, '('); - strip_string (value, ')'); - strip_string (value, '"'); - ipset = g_strsplit (value, "\"", 0); - length = g_strv_length (ipset); - - //only one line - if (length < 2) { - *out_line = - g_strdup_printf ("%s_%s=\"%s\"\n", key, name, value); - goto done; - } - // Multiple lines - g_string_append_printf (formated_string, "%s_%s=\"\n", key, name); - for (i = 0; i < length; i++) - { - strip_string (ipset[i], ' '); - if (ipset[i][0] != '\0') - g_string_append_printf (formated_string, - "%s\n", ipset[i]); - } - g_string_append (formated_string, "\"\n"); - *out_line = g_strdup (formated_string->str); -done: - g_string_free (formated_string, TRUE); - g_strfreev (ipset); -} - -gboolean -ifnet_flush_to_file (const char *config_file, gchar **out_backup) -{ - GIOChannel *channel; - GError *error = NULL; - gpointer key, value, name, network; - GHashTableIter iter, iter_network; - GList *list_iter; - gchar *out_line = NULL; - gsize bytes_written; - gboolean result = FALSE; - gchar *backup; - - if (!net_parser_data_changed) - return TRUE; - if (!conn_table || !global_settings_table) - return FALSE; - - backup = backup_file (config_file); - - channel = g_io_channel_new_file (config_file, "w", NULL); - if (!channel) { - nm_log_warn (LOGD_SETTINGS, "Can't open file %s for writing", config_file); - g_free (backup); - return FALSE; - } - g_hash_table_iter_init (&iter, global_settings_table); - nm_log_info (LOGD_SETTINGS, "Writing to %s", config_file); - g_io_channel_write_chars (channel, - "#Generated by NetworkManager\n" - "###### Global Configuration ######\n", - -1, &bytes_written, &error); - if (error) - goto done; - - /* Writing global data */ - while (g_hash_table_iter_next (&iter, &key, &value)) { - out_line = - g_strdup_printf ("%s=\"%s\"\n", (gchar *) key, (gchar *) value); - g_io_channel_write_chars (channel, out_line, -1, - &bytes_written, &error); - if (bytes_written == 0 || error) - goto done; - g_free (out_line); - } - - /* Writing connection data */ - g_io_channel_write_chars (channel, - "\n###### Connection Configuration ######\n", - -1, &bytes_written, &error); - if (error) - goto done; - - g_hash_table_iter_init (&iter, conn_table); - while (g_hash_table_iter_next (&iter, &name, &network)) { - g_hash_table_iter_init (&iter_network, (GHashTable *) network); - g_io_channel_write_chars (channel, - "#----------------------------------\n", - -1, &bytes_written, &error); - if (error) - goto done; - - while (g_hash_table_iter_next (&iter_network, &key, &value)) { - if (!g_str_has_prefix ((gchar *) key, "name") - && !g_str_has_prefix ((gchar *) key, "type")) { - /* These keys contain brackets */ - if (strcmp - ((gchar *) key, - "config") == 0 - || strcmp ((gchar *) key, - "routes") == 0 - || strcmp ((gchar *) key, - "pppd") == 0 - || strcmp ((gchar *) key, "chat") == 0) - format_ips (value, &out_line, (gchar *) - key, (gchar *) - name); - else - out_line = - g_strdup_printf - ("%s_%s=\"%s\"\n", - (gchar *) key, - (gchar *) name, (gchar *) value); - g_io_channel_write_chars (channel, out_line, -1, &bytes_written, &error); - if (bytes_written == 0 || error) - goto done; - g_free (out_line); - } - } - } - - /* Writing reserved functions */ - if (functions_list) { - g_io_channel_write_chars (channel, - "\n###### Reserved Functions ######\n", - -1, &bytes_written, &error); - if (error) - goto done; - - /* Writing functions */ - for (list_iter = functions_list; list_iter; - list_iter = g_list_next (list_iter)) { - out_line = - g_strdup_printf ("%s\n", (gchar *) list_iter->data); - g_io_channel_write_chars (channel, out_line, -1, - &bytes_written, &error); - if (bytes_written == 0 || error) - goto done; - g_free (out_line); - } - } - - g_io_channel_flush (channel, &error); - if (error) - goto done; - result = TRUE; - net_parser_data_changed = FALSE; - -done: - if (error) { - nm_log_warn (LOGD_SETTINGS, "Error writing the configuration file: %s", error->message); - g_error_free (error); - } - - if (result && out_backup) - *out_backup = backup; - else - g_free (backup); - - g_io_channel_shutdown (channel, FALSE, NULL); - g_io_channel_unref (channel); - return result; -} - -gboolean -ifnet_delete_network (const char *conn_name) -{ - GHashTable *network = NULL; - - g_return_val_if_fail (conn_table != NULL && conn_name != NULL, FALSE); - nm_log_info (LOGD_SETTINGS, "Deleting network for %s", conn_name); - network = g_hash_table_lookup (conn_table, conn_name); - if (!network) - return FALSE; - g_hash_table_remove (conn_table, conn_name); - destroy_connection_config (network); - net_parser_data_changed = TRUE; - return TRUE; -} - -void -ifnet_destroy (void) -{ - GHashTableIter iter; - gpointer key; - gpointer value; - GList *list_iter; - - /* Destroy connection setting */ - if (conn_table) { - g_hash_table_iter_init (&iter, conn_table); - while (g_hash_table_iter_next (&iter, &key, &value)) { - destroy_connection_config ((GHashTable *) - value); - } - g_hash_table_destroy (conn_table); - conn_table = NULL; - } - - /* Destroy global data */ - if (global_settings_table) { - g_hash_table_iter_init (&iter, global_settings_table); - while (g_hash_table_iter_next (&iter, &key, &value)) { - g_free (key); - g_free (value); - } - g_hash_table_destroy (global_settings_table); - global_settings_table = NULL; - } - for (list_iter = functions_list; list_iter; - list_iter = g_list_next (list_iter)) - g_free (list_iter->data); - g_list_free (functions_list); -} diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-parser.h b/src/settings/plugins/ifnet/nms-ifnet-net-parser.h deleted file mode 100644 index 31fc9ead..00000000 --- a/src/settings/plugins/ifnet/nms-ifnet-net-parser.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * Mu Qiao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 1999-2010 Gentoo Foundation, Inc. - */ - -#ifndef _NET_PARSER_H -#define _NET_PARSER_H - -#define CONF_NET_FILE SYSCONFDIR "/conf.d/net" - -gboolean ifnet_init (gchar * config_file); -void ifnet_destroy (void); - -/* Reader functions */ -GList *ifnet_get_connection_names (void); -const char *ifnet_get_data (const char *conn_name, const char *key); -const char *ifnet_get_global_data (const char *key); -gboolean ifnet_has_network (const char *conn_name); - -/* Writer functions */ -gboolean ifnet_flush_to_file (const char *config_file, gchar **out_backup); -void ifnet_set_data (const char *conn_name, const char *key, const char *value); -gboolean ifnet_add_network (const char *name, const char *type); -gboolean ifnet_delete_network (const char *conn_name); - -#endif diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-utils.c b/src/settings/plugins/ifnet/nms-ifnet-net-utils.c deleted file mode 100644 index 6531a238..00000000 --- a/src/settings/plugins/ifnet/nms-ifnet-net-utils.c +++ /dev/null @@ -1,830 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * Mu Qiao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 1999-2010 Gentoo Foundation, Inc. - */ - -#include "nm-default.h" - -#include "nms-ifnet-net-utils.h" - -#include -#include -#include -#include - -#include "nm-utils.h" -#include "NetworkManagerUtils.h" -#include "settings/nm-settings-plugin.h" -#include "nm-config.h" -#include "dhcp/nm-dhcp-manager.h" - -#include "nms-ifnet-wpa-parser.h" -#include "nms-ifnet-net-parser.h" - -/* emit heading and tailing blank space, tab, character t */ -gchar * -strip_string (gchar * str, gchar t) -{ - gchar *ret = str; - gint length = 0; - guint i = 0; - - while (ret[i] != '\0' - && (ret[i] == '\t' || ret[i] == ' ' || ret[i] == t)) { - length++; - i++; - } - i = 0; - while (ret[i + length] != '\0') { - ret[i] = ret[i + length]; - i++; - } - ret[i] = '\0'; - length = strlen (ret); - while ((length - 1) >= 0 - && (ret[length - 1] == ' ' || ret[length - 1] == '\n' - || ret[length - 1] == '\t' || ret[length - 1] == t)) - length--; - ret[length] = '\0'; - return ret; -} - -gboolean -is_hex (const char *value) -{ - const char *p = value; - - if (!p) - return FALSE; - while (*p) { - if (!g_ascii_isxdigit (*p++)) - return FALSE; - } - return TRUE; -} - -gboolean -is_ascii (const char *value) -{ - const char *p = value; - - while (*p) { - if (!g_ascii_isprint (*p++)) - return FALSE; - } - return TRUE; - -} - -gboolean -is_true (const char *str) -{ - if (!g_ascii_strcasecmp (str, "yes") - || !g_ascii_strcasecmp (str, "true")) - return TRUE; - return FALSE; -} - -static char * -find_default_gateway_str (char *str) -{ - char *tmp; - - if ((tmp = strstr (str, "default via ")) != NULL) { - return tmp + strlen ("default via "); - } else if ((tmp = strstr (str, "default gw ")) != NULL) { - return tmp + strlen ("default gw "); - } - return NULL; -} - -static char * -find_gateway_str (char *str) -{ - char *tmp; - - if ((tmp = strstr (str, "via ")) != NULL) { - return tmp + strlen ("via "); - } else if ((tmp = strstr (str, "gw ")) != NULL) { - return tmp + strlen ("gw "); - } - return NULL; -} - -gboolean -reload_parsers (void) -{ - ifnet_destroy (); - wpa_parser_destroy (); - if (!ifnet_init (CONF_NET_FILE)) - return FALSE; - wpa_parser_init (WPA_SUPPLICANT_CONF); - return TRUE; -} - -gboolean -is_static_ip4 (const char *conn_name) -{ - const char *data = ifnet_get_data (conn_name, "config"); - const char *dhcp6; - - if (!data) - return FALSE; - if (!strcmp (data, "shared")) - return FALSE; - if (!strcmp (data, "autoip")) - return FALSE; - dhcp6 = strstr (data, "dhcp6"); - if (dhcp6) { - gchar *dhcp4; - - if (strstr (data, "dhcp ")) - return FALSE; - dhcp4 = strstr (data, "dhcp"); - if (!dhcp4) - return TRUE; - if (dhcp4[4] == '\0') - return FALSE; - return TRUE; - } - return strstr (data, "dhcp") == NULL ? TRUE : FALSE; -} - -gboolean -is_static_ip6 (const char *conn_name) -{ - const char *data = ifnet_get_data (conn_name, "config"); - - if (!data) - return TRUE; - return strstr (data, "dhcp6") == NULL ? TRUE : FALSE; -} - -gboolean -is_ip4_address (const char *in_address) -{ - const char *pattern = - "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.((\\{\\d{1,3}\\.\\.\\d{1,3}\\})|\\d{1,3})$"; - gchar *address = g_strdup (in_address); - gboolean result = FALSE; - gchar *tmp; - GRegex *regex = g_regex_new (pattern, 0, 0, NULL); - GMatchInfo *match_info = NULL; - - if (!address) - goto done; - g_strstrip (address); - if ((tmp = strstr (address, "/")) != NULL) - *tmp = '\0'; - if ((tmp = strstr (address, " ")) != NULL) - *tmp = '\0'; - g_regex_match (regex, address, 0, &match_info); - result = g_match_info_matches (match_info); -done: - if (match_info) - g_match_info_free (match_info); - g_regex_unref (regex); - g_free (address); - return result; -} - -gboolean -is_ip6_address (const char *in_address) -{ - struct in6_addr tmp_ip6_addr; - gchar *tmp, *address; - gboolean result = FALSE; - - if (!in_address) - return FALSE; - address = g_strdup (in_address); - g_strstrip (address); - if ((tmp = strchr (address, '/')) != NULL) - *tmp = '\0'; - if (inet_pton (AF_INET6, address, &tmp_ip6_addr)) - result = TRUE; - g_free (address); - return result; - -} - -// 'c' is only used for openrc style -static gchar ** -split_addresses_by_char (const gchar *addresses, const gchar *c) -{ - gchar **ipset; - - if (addresses == NULL) - return NULL; - - if (strchr (addresses, '(') != NULL) { // old baselayout style - gchar *tmp = g_strdup (addresses); - strip_string (tmp, '('); - strip_string (tmp, ')'); - strip_string (tmp, '"'); - strip_string (tmp, '\''); - ipset = g_strsplit (tmp, "\" \"", 0); - g_free(tmp); - } else { // openrc style - if (strstr (addresses, "netmask")) - // There is only one ip address if "netmask" is specified. - // '\n' is not used in config so there will be only one split. - ipset = g_strsplit (addresses, "\n", 0); - else - ipset = g_strsplit (addresses, c, 0); - } - - return ipset; -} - -static gchar ** -split_addresses (const gchar* addresses) -{ - // " " is only used by openrc style - return split_addresses_by_char (addresses, " "); -} - -static gchar ** -split_routes (const gchar* routes) -{ - // "\"" is only used by openrc style - return split_addresses_by_char (routes, "\""); -} - -gboolean -has_ip6_address (const char *conn_name) -{ - gchar **ipset; - guint length; - guint i; - - g_return_val_if_fail (conn_name != NULL, FALSE); - ipset = split_addresses (ifnet_get_data (conn_name, "config")); - length = ipset ? g_strv_length (ipset) : 0; - for (i = 0; i < length; i++) { - if (!is_ip6_address (ipset[i])) - continue; - else { - g_strfreev (ipset); - return TRUE; - } - - } - g_strfreev (ipset); - return FALSE; -} - -gboolean -has_default_route (const char *conn_name, gboolean (*check_fn) (const char *)) -{ - char *routes = NULL, *end, *tmp; - gboolean success = FALSE; - - g_return_val_if_fail (conn_name != NULL, FALSE); - - routes = g_strdup (ifnet_get_data (conn_name, "routes")); - if (!routes) - return FALSE; - tmp = find_default_gateway_str (routes); - if (tmp) { - g_strstrip (tmp); - if ((end = strstr (tmp, "\"")) != NULL) - *end = '\0'; - if (check_fn (tmp)) - success = TRUE; - } - - g_free (routes); - return success; -} - -static ip_block * -create_ip4_block (gchar * ip) -{ - ip_block *iblock = g_slice_new0 (ip_block); - guint32 tmp_ip4_addr; - int i; - guint length; - gchar **ip_mask; - - /* prefix format */ - if (strstr (ip, "/")) { - gchar *prefix; - - ip_mask = g_strsplit (ip, "/", 0); - length = g_strv_length (ip_mask); - if (!nm_utils_ipaddr_valid (AF_INET, ip_mask[0])) - goto error; - iblock->ip = g_strdup (ip_mask[0]); - prefix = ip_mask[1]; - i = 0; - while (i < length && g_ascii_isdigit (prefix[i])) - i++; - prefix[i] = '\0'; - iblock->prefix = (guint32) atoi (ip_mask[1]); - } else if (strstr (ip, "netmask")) { - ip_mask = g_strsplit (ip, " ", 0); - length = g_strv_length (ip_mask); - if (!nm_utils_ipaddr_valid (AF_INET, ip_mask[0])) - goto error; - iblock->ip = g_strdup (ip_mask[0]); - i = 0; - while (i < length && !strstr (ip_mask[++i], "netmask")) ; - while (i < length && ip_mask[++i][0] == '\0') ; - if (i >= length) - goto error; - if (!inet_pton (AF_INET, ip_mask[i], &tmp_ip4_addr)) - goto error; - iblock->prefix = nm_utils_ip4_netmask_to_prefix (tmp_ip4_addr); - } else { - g_slice_free (ip_block, iblock); - if (!is_ip6_address (ip) && !strstr (ip, "dhcp")) - nm_log_warn (LOGD_SETTINGS, "Can't handle ipv4 address: %s, missing netmask or prefix", ip); - return NULL; - } - if (iblock->prefix == 0 || iblock->prefix > 32) { - nm_log_warn (LOGD_SETTINGS, "Can't handle ipv4 address: %s, invalid prefix", ip); - goto error; - } - g_strfreev (ip_mask); - return iblock; -error: - if (!is_ip6_address (ip)) - nm_log_warn (LOGD_SETTINGS, "Can't handle IPv4 address: %s", ip); - g_strfreev (ip_mask); - g_free (iblock->ip); - g_slice_free (ip_block, iblock); - return NULL; -} - -static ip_block * -create_ip_block (gchar * ip) -{ - ip_block *iblock = g_slice_new0 (ip_block); - gchar *dup_ip = g_strdup (ip); - gchar *prefix = NULL; - - if ((prefix = strstr (dup_ip, "/")) != NULL) { - *prefix = '\0'; - prefix++; - } - if (!nm_utils_ipaddr_valid (AF_INET6, dup_ip)) - goto error; - iblock->ip = dup_ip; - if (prefix) { - errno = 0; - iblock->prefix = strtol (prefix, NULL, 10); - if (errno || iblock->prefix <= 0 || iblock->prefix > 128) { - goto error; - } - } else - iblock->prefix = 64; - return iblock; -error: - if (!is_ip4_address (ip)) - nm_log_warn (LOGD_SETTINGS, "Can't handle IPv6 address: %s", ip); - g_slice_free (ip_block, iblock); - g_free (dup_ip); - return NULL; -} - -static char * -get_ip4_gateway (gchar * gateway) -{ - gchar *tmp, *split; - - if (!gateway) - return NULL; - tmp = find_gateway_str (gateway); - if (!tmp) { - nm_log_warn (LOGD_SETTINGS, "Couldn't obtain gateway in \"%s\"", gateway); - return NULL; - } - tmp = g_strdup (tmp); - strip_string (tmp, ' '); - strip_string (tmp, '"'); - - // Only one gateway is selected - if ((split = strstr (tmp, "\"")) != NULL) - *split = '\0'; - - if (!nm_utils_ipaddr_valid (AF_INET, tmp)) - goto error; - return tmp; -error: - if (!is_ip6_address (tmp)) - nm_log_warn (LOGD_SETTINGS, "Can't handle IPv4 gateway: %s", tmp); - g_free (tmp); - return NULL; -} - -static char * -get_ip6_next_hop (gchar * next_hop) -{ - gchar *tmp; - - if (!next_hop) - return NULL; - tmp = find_gateway_str (next_hop); - if (!tmp) { - nm_log_warn (LOGD_SETTINGS, "Couldn't obtain next_hop in \"%s\"", next_hop); - return NULL; - } - tmp = g_strdup (tmp); - strip_string (tmp, ' '); - strip_string (tmp, '"'); - g_strstrip (tmp); - if (!nm_utils_ipaddr_valid (AF_INET6, tmp)) - goto error; - return tmp; -error: - if (!is_ip4_address (tmp)) - nm_log_warn (LOGD_SETTINGS, "Can't handle IPv6 next_hop: %s", tmp); - g_free (tmp); - - return NULL; -} - -ip_block * -convert_ip4_config_block (const char *conn_name) -{ - gchar **ipset; - guint length; - guint i; - gchar *ip; - char *def_gateway = NULL; - const char *routes; - ip_block *start = NULL, *current = NULL, *iblock = NULL; - - g_return_val_if_fail (conn_name != NULL, NULL); - - ipset = split_addresses (ifnet_get_data (conn_name, "config")); - length = ipset ? g_strv_length (ipset) : 0; - - routes = ifnet_get_data (conn_name, "routes"); - if (routes) - def_gateway = get_ip4_gateway (strstr (routes, "default")); - - for (i = 0; i < length; i++) { - ip = ipset[i]; - ip = strip_string (ip, '"'); - iblock = create_ip4_block (ip); - if (iblock == NULL) - continue; - if (!iblock->next_hop && def_gateway != NULL) - iblock->next_hop = g_strdup (def_gateway); - if (start == NULL) - start = current = iblock; - else { - current->next = iblock; - current = iblock; - } - } - g_strfreev (ipset); - g_free (def_gateway); - return start; -} - -ip_block * -convert_ip6_config_block (const char *conn_name) -{ - gchar **ipset; - guint length; - guint i; - gchar *ip; - ip_block *start = NULL, *current = NULL, *iblock = NULL; - - g_return_val_if_fail (conn_name != NULL, NULL); - ipset = split_addresses (ifnet_get_data (conn_name, "config")); - length = ipset ? g_strv_length (ipset) : 0; - for (i = 0; i < length; i++) { - ip = ipset[i]; - ip = strip_string (ip, '"'); - iblock = create_ip_block (ip); - if (iblock == NULL) - continue; - if (start == NULL) - start = current = iblock; - else { - current->next = iblock; - current = iblock; - } - } - g_strfreev (ipset); - return start; -} - -ip_block * -convert_ip4_routes_block (const char *conn_name) -{ - gchar **ipset; - guint length; - guint i; - gchar *ip; - ip_block *start = NULL, *current = NULL, *iblock = NULL; - - g_return_val_if_fail (conn_name != NULL, NULL); - - ipset = split_routes (ifnet_get_data (conn_name, "routes")); - length = ipset ? g_strv_length (ipset) : 0; - for (i = 0; i < length; i++) { - ip = ipset[i]; - if (find_default_gateway_str (ip) || strstr (ip, "::") - || !find_gateway_str (ip)) - continue; - ip = strip_string (ip, '"'); - iblock = create_ip4_block (ip); - if (iblock == NULL) - continue; - iblock->next_hop = get_ip4_gateway (ip); - if (start == NULL) - start = current = iblock; - else { - current->next = iblock; - current = iblock; - } - } - g_strfreev (ipset); - return start; -} - -ip_block * -convert_ip6_routes_block (const char *conn_name) -{ - gchar **ipset; - guint length; - guint i; - gchar *ip, *tmp_addr; - ip_block *start = NULL, *current = NULL, *iblock = NULL; - - g_return_val_if_fail (conn_name != NULL, NULL); - ipset = split_routes (ifnet_get_data (conn_name, "routes")); - length = ipset ? g_strv_length (ipset) : 0; - for (i = 0; i < length; i++) { - ip = ipset[i]; - ip = strip_string (ip, '"'); - if (ip[0] == '\0') - continue; - if ((tmp_addr = find_default_gateway_str (ip)) != NULL) { - if (!is_ip6_address (tmp_addr)) - continue; - else { - iblock = g_slice_new0 (ip_block); - iblock->ip = g_strdup ("::"); - iblock->prefix = 128; - } - } else - iblock = create_ip_block (ip); - if (iblock == NULL) - continue; - iblock->next_hop = get_ip6_next_hop (ip); - if (iblock->next_hop == NULL) { - destroy_ip_block (iblock); - continue; - } - if (start == NULL) - start = current = iblock; - else { - current->next = iblock; - current = iblock; - } - } - g_strfreev (ipset); - return start; -} - -void -destroy_ip_block (ip_block * iblock) -{ - g_free (iblock->ip); - g_free (iblock->next_hop); - g_slice_free (ip_block, iblock); -} - -void -set_ip4_dns_servers (NMSettingIPConfig *s_ip4, const char *conn_name) -{ - const char *dns_servers; - gchar **server_list, *stripped; - guint length, i; - guint32 tmp_ip4_addr; - - dns_servers = ifnet_get_data (conn_name, "dns_servers"); - if (!dns_servers) - return; - stripped = g_strdup (dns_servers); - strip_string (stripped, '"'); - server_list = g_strsplit (stripped, " ", 0); - g_free (stripped); - - length = g_strv_length (server_list); - if (length) - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, - TRUE, NULL); - for (i = 0; i < length; i++) { - g_strstrip (server_list[i]); - if (server_list[i][0] == '\0') - continue; - if (!inet_pton (AF_INET, server_list[i], &tmp_ip4_addr)) { - if (!is_ip6_address (server_list[i])) - nm_log_warn (LOGD_SETTINGS, "ignored dns: %s\n", server_list[i]); - continue; - } - if (!nm_setting_ip_config_add_dns (s_ip4, server_list[i])) - nm_log_warn (LOGD_SETTINGS, "warning: duplicate DNS server %s", server_list[i]); - } - g_strfreev (server_list); -} - -void -set_ip6_dns_servers (NMSettingIPConfig *s_ip6, const char *conn_name) -{ - const char *dns_servers; - gchar **server_list, *stripped; - guint length, i; - struct in6_addr tmp_ip6_addr; - - dns_servers = ifnet_get_data (conn_name, "dns_servers"); - if (!dns_servers) - return; - - stripped = g_strdup (dns_servers); - strip_string (stripped, '"'); - server_list = g_strsplit (stripped, " ", 0); - g_free (stripped); - - length = g_strv_length (server_list); - if (length) - g_object_set (s_ip6, NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, - TRUE, NULL); - for (i = 0; i < length; i++) { - g_strstrip (server_list[i]); - if (server_list[i][0] == '\0') - continue; - if (!inet_pton (AF_INET6, server_list[i], &tmp_ip6_addr)) { - if (is_ip6_address (server_list[i])) - nm_log_warn (LOGD_SETTINGS, "ignored dns: %s\n", server_list[i]); - continue; - } - if (!nm_setting_ip_config_add_dns (s_ip6, server_list[i])) - nm_log_warn (LOGD_SETTINGS, "warning: duplicate DNS server %s", server_list[i]); - } - g_strfreev (server_list); -} - -gboolean -is_managed (const char *conn_name) -{ - gchar *config; - - g_return_val_if_fail (conn_name != NULL, FALSE); - config = (gchar *) ifnet_get_data (conn_name, "managed"); - if (!config) - return TRUE; - if (strcmp (config, "false") == 0) - return FALSE; - return TRUE; -} - -static char * -_has_prefix_impl (char *str, const char *prefix, gsize prefix_len) -{ - if (!g_str_has_prefix (str, prefix)) - return NULL; - str += prefix_len; - if (!g_ascii_isspace (str[0])) - return NULL; - do { - str++; - } while (g_ascii_isspace (str[0])); - return str; -} -#define _has_prefix(STR, PREFIX) _has_prefix_impl (STR, PREFIX, NM_STRLEN (PREFIX)) - -void -get_dhcp_hostname_and_client_id (char **hostname, char **client_id) -{ - const char *dhcp_client; - const gchar *dhcpcd_conf = SYSCONFDIR "/dhcpcd.conf"; - const gchar *dhclient_conf = SYSCONFDIR "/dhcp/dhclient.conf"; - gchar *line = NULL, *tmp = NULL, *contents = NULL, *tmp1; - gchar **all_lines; - guint line_num, i; - gboolean use_dhclient = FALSE; - - *hostname = NULL; - *client_id = NULL; - dhcp_client = nm_dhcp_manager_get_config (nm_dhcp_manager_get ()); - if (dhcp_client) { - if (!strcmp (dhcp_client, "dhclient")) { - g_file_get_contents (dhclient_conf, &contents, NULL, - NULL); - use_dhclient = TRUE; - } else if (!strcmp (dhcp_client, "dhcpcd")) { - g_file_get_contents (dhcpcd_conf, &contents, NULL, - NULL); - } - } else { - if (g_file_test (dhclient_conf, G_FILE_TEST_IS_REGULAR)) { - g_file_get_contents (dhclient_conf, &contents, NULL, - NULL); - use_dhclient = TRUE; - } else if (g_file_test (dhcpcd_conf, G_FILE_TEST_IS_REGULAR)) { - g_file_get_contents (dhcpcd_conf, &contents, NULL, - NULL); - } - } - if (!contents) - return; - all_lines = g_strsplit (contents, "\n", 0); - line_num = g_strv_length (all_lines); - for (i = 0; i < line_num; i++) { - line = all_lines[i]; - g_strstrip (line); - if (line[0] == '#' || line[0] == '\0') - continue; - if (!use_dhclient) { - // dhcpcd.conf - if ((tmp = _has_prefix (line, "hostname"))) { - if (tmp[0] != '\0') { - g_free (*hostname); - *hostname = g_strdup (tmp); - } else - nm_log_info (LOGD_SETTINGS, "dhcpcd hostname not defined, ignoring"); - } else if ((tmp = _has_prefix (line, "clientid"))) { - if (tmp[0] != '\0') { - g_free (*client_id); - *client_id = g_strdup (tmp); - } else - nm_log_info (LOGD_SETTINGS, "dhcpcd clientid not defined, ignoring"); - } - } else { - // dhclient.conf - if ((tmp1 = _has_prefix (line, "send"))) { - if ((tmp = _has_prefix (tmp1, "host-name"))) { - strip_string (tmp, ';'); - strip_string (tmp, '"'); - if (tmp[0] != '\0') { - g_free (*hostname); - *hostname = g_strdup (tmp); - } else - nm_log_info (LOGD_SETTINGS, "dhclient hostname not defined, ignoring"); - } else if ((tmp = _has_prefix (tmp1, "dhcp-client-identifier"))) { - strip_string (tmp, ';'); - if (tmp[0] != '\0') { - g_free (*client_id); - *client_id = g_strdup (tmp); - } else - nm_log_info (LOGD_SETTINGS, "dhclient clientid not defined, ignoring"); - } - } - } - } - g_strfreev (all_lines); - g_free (contents); -} - -gchar *backup_file (const gchar* target) -{ - GFile *source, *backup; - gchar* backup_path; - GError *error = NULL; - - source = g_file_new_for_path (target); - - if (!g_file_query_exists (source, NULL)) { - g_object_unref (source); - return NULL; - } - - backup_path = g_strdup_printf ("%s.bak", target); - backup = g_file_new_for_path (backup_path); - - if (!g_file_copy (source, backup, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error)) { - nm_log_warn (LOGD_SETTINGS, "Backup failed: %s", error->message); - g_free (backup_path); - backup_path = NULL; - g_error_free (error); - } - - g_object_unref (source); - g_object_unref (backup); - - return backup_path; -} diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-utils.h b/src/settings/plugins/ifnet/nms-ifnet-net-utils.h deleted file mode 100644 index cc273aa7..00000000 --- a/src/settings/plugins/ifnet/nms-ifnet-net-utils.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * Mu Qiao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 1999-2010 Gentoo Foundation, Inc. - */ - -#ifndef _IFNET_UTILS_H -#define _IFNET_UTILS_H - -#define IFNET_PLUGIN_NAME "SettingsPlugin-Ifnet" - -#include - -#include "nm-setting-ip6-config.h" -#include "nm-setting-ip4-config.h" - -#include "nms-ifnet-net-parser.h" - -#define has_default_ip4_route(conn_name) has_default_route((conn_name), &is_ip4_address) -#define has_default_ip6_route(conn_name) has_default_route((conn_name), &is_ip6_address) - -typedef struct _ip_block { - char *ip; - guint32 prefix; - char *next_hop; - struct _ip_block *next; -} ip_block; - -gboolean is_static_ip4 (const char *conn_name); -gboolean is_static_ip6 (const char *conn_name); -gboolean is_ip4_address (const char *in_address); -gboolean is_ip6_address (const char *in_address); -gboolean has_ip6_address (const char *conn_name); -gboolean has_default_route (const char *conn_name, gboolean (*check_fn) (const char *)); -gboolean reload_parsers (void); - -ip_block *convert_ip4_config_block (const char *conn_name); -ip_block *convert_ip6_config_block (const char *conn_name); -ip_block *convert_ip4_routes_block (const char *conn_name); -ip_block *convert_ip6_routes_block (const char *conn_name); -void destroy_ip_block (ip_block * iblock); - -void set_ip4_dns_servers (NMSettingIPConfig * s_ip4, const char *conn_name); -void set_ip6_dns_servers (NMSettingIPConfig * s_ip6, const char *conn_name); - -gchar *strip_string (gchar *str, gchar t); -gboolean is_managed (const char *conn_name); - -gboolean is_hex (const char *value); -gboolean is_ascii (const char *value); -gboolean is_true (const char *str); - -void get_dhcp_hostname_and_client_id (char **hostname, char **client_id); - -gchar *backup_file (const gchar* target); -#endif diff --git a/src/settings/plugins/ifnet/nms-ifnet-plugin.c b/src/settings/plugins/ifnet/nms-ifnet-plugin.c deleted file mode 100644 index 8332358f..00000000 --- a/src/settings/plugins/ifnet/nms-ifnet-plugin.c +++ /dev/null @@ -1,524 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service (ifnet) - * - * Mu Qiao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 1999-2010 Gentoo Foundation, Inc. - */ - -#include "nm-default.h" - -#include "nms-ifnet-plugin.h" - -#include -#include - -#include "nm-utils.h" -#include "nm-setting-connection.h" -#include "nm-dbus-interface.h" -#include "settings/nm-settings-plugin.h" -#include "nm-config.h" -#include "NetworkManagerUtils.h" - -#include "nms-ifnet-connection.h" -#include "nms-ifnet-net-utils.h" -#include "nms-ifnet-net-parser.h" -#include "nms-ifnet-wpa-parser.h" -#include "nms-ifnet-connection-parser.h" - -#define IFNET_PLUGIN_NAME_PRINT "ifnet" -#define IFNET_PLUGIN_INFO "(C) 1999-2010 Gentoo Foundation, Inc. To report bugs please use bugs.gentoo.org with [networkmanager] or [qiaomuf] prefix." -#define IFNET_MANAGE_WELL_KNOWN_DEFAULT TRUE - -/*****************************************************************************/ - -typedef void (*FileChangedFn) (gpointer user_data); - -typedef struct { - FileChangedFn callback; - gpointer user_data; -} FileMonitorInfo; - -/*****************************************************************************/ - -typedef struct { - GHashTable *connections; /* uuid::connection */ - gboolean unmanaged_well_known; - - GFileMonitor *net_monitor; - GFileMonitor *wpa_monitor; -} SettingsPluginIfnetPrivate; - -struct _SettingsPluginIfnet { - GObject parent; - SettingsPluginIfnetPrivate _priv; -}; - -struct _SettingsPluginIfnetClass { - GObjectClass parent; -}; - -static void settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface); - -G_DEFINE_TYPE_EXTENDED (SettingsPluginIfnet, settings_plugin_ifnet, G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE (NM_TYPE_SETTINGS_PLUGIN, - settings_plugin_interface_init)) - -#define SETTINGS_PLUGIN_IFNET_GET_PRIVATE(self) _NM_GET_PRIVATE (self, SettingsPluginIfnet, SETTINGS_IS_PLUGIN_IFNET) - -/*****************************************************************************/ - -static SettingsPluginIfnet *settings_plugin_ifnet_get (void); - -NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfnet, settings_plugin_ifnet_get, SETTINGS_TYPE_PLUGIN_IFNET); - -/*****************************************************************************/ - -static void reload_connections (NMSettingsPlugin *config); - -/*****************************************************************************/ - -static gboolean -is_managed_plugin (void) -{ - return nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG, - NM_CONFIG_KEYFILE_GROUP_IFNET, NM_CONFIG_KEYFILE_KEY_IFNET_MANAGED, - IFNET_MANAGE_WELL_KNOWN_DEFAULT); -} - -static void -file_changed (GFileMonitor * monitor, - GFile * file, - GFile * other_file, - GFileMonitorEvent event_type, gpointer user_data) -{ - FileMonitorInfo *info; - - switch (event_type) { - case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: - info = (FileMonitorInfo *) user_data; - info->callback (info->user_data); - break; - default: - break; - } -} - -static GFileMonitor * -monitor_file_changes (const char *filename, - FileChangedFn callback, gpointer user_data) -{ - GFile *file; - GFileMonitor *monitor; - FileMonitorInfo *info; - GError **error = NULL; - - if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) - return NULL; - file = g_file_new_for_path (filename); - monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, error); - g_object_unref (file); - - if (monitor) { - info = g_new0 (FileMonitorInfo, 1); - info->callback = callback; - info->user_data = user_data; - g_object_weak_ref (G_OBJECT (monitor), (GWeakNotify) g_free, - info); - g_signal_connect (monitor, "changed", G_CALLBACK (file_changed), - info); - } else { - nm_log_warn (LOGD_SETTINGS, "Monitoring %s failed, error: %s", filename, - error == NULL ? "nothing" : (*error)->message); - } - - return monitor; -} - -static void -setup_monitors (NMIfnetConnection *connection, gpointer user_data) -{ - SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (user_data); - SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self); - - if (!nm_config_get_monitor_connection_files (nm_config_get ())) - return; - - if (priv->net_monitor || priv->wpa_monitor) - return; - - priv->net_monitor = monitor_file_changes (CONF_NET_FILE, - (FileChangedFn) reload_connections, - user_data); - priv->wpa_monitor = monitor_file_changes (WPA_SUPPLICANT_CONF, - (FileChangedFn) reload_connections, - user_data); -} - -static void -cancel_monitors (NMIfnetConnection *connection, gpointer user_data) -{ - SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (user_data); - SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self); - - if (priv->net_monitor) { - g_file_monitor_cancel (priv->net_monitor); - g_clear_object (&priv->net_monitor); - } - if (priv->wpa_monitor) { - g_file_monitor_cancel (priv->wpa_monitor); - g_clear_object (&priv->wpa_monitor); - } -} - -static void -connection_removed_cb (NMSettingsConnection *obj, gpointer user_data) -{ - g_hash_table_remove (SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) user_data)->connections, - nm_connection_get_uuid (NM_CONNECTION (obj))); -} - -static void -track_new_connection (SettingsPluginIfnet *self, NMIfnetConnection *connection) -{ - g_hash_table_insert (SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self)->connections, - g_strdup (nm_connection_get_uuid (NM_CONNECTION (connection))), - g_object_ref (connection)); - g_signal_connect (connection, NM_SETTINGS_CONNECTION_REMOVED, - G_CALLBACK (connection_removed_cb), - self); -} - -static void -reload_connections (NMSettingsPlugin *config) -{ - SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (config); - SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self); - GList *conn_names = NULL, *n_iter = NULL; - gboolean auto_refresh; - GError *error = NULL; - - /* save names for removing unused connections */ - GHashTable *new_connections = NULL; - GHashTableIter iter; - const char *uuid; - NMSettingsConnection *candidate; - - if (priv->unmanaged_well_known) - return; - - if (!reload_parsers ()) - return; - - nm_log_info (LOGD_SETTINGS, "Loading connections"); - - auto_refresh = nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG, - NM_CONFIG_KEYFILE_GROUP_IFNET, NM_CONFIG_KEYFILE_KEY_IFNET_AUTO_REFRESH, - FALSE); - - new_connections = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref); - - /* Reread on-disk data and refresh in-memory connections from it */ - conn_names = ifnet_get_connection_names (); - for (n_iter = conn_names; n_iter; n_iter = g_list_next (n_iter)) { - NMIfnetConnection *new; - NMIfnetConnection *old; - const char *conn_name = n_iter->data; - - /* read the new connection */ - new = nm_ifnet_connection_new (NULL, conn_name); - if (!new) - continue; - - g_signal_connect (G_OBJECT (new), "ifnet_setup_monitors", - G_CALLBACK (setup_monitors), config); - g_signal_connect (G_OBJECT (new), "ifnet_cancel_monitors", - G_CALLBACK (cancel_monitors), config); - - old = g_hash_table_lookup (priv->connections, - nm_connection_get_uuid (NM_CONNECTION (new))); - if (old && new) { - if (auto_refresh) { - /* If connection has changed, remove the old one and add the - * new one to force a disconnect/reconnect with new settings - */ - if (!nm_connection_compare (NM_CONNECTION (old), - NM_CONNECTION (new), - NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS | - NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) { - nm_log_info (LOGD_SETTINGS, "Auto refreshing %s", conn_name); - - nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (old)); - track_new_connection (self, new); - if (is_managed_plugin () && is_managed (conn_name)) - g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_CONNECTION_ADDED, new); - } - } else { - /* Update existing connection with new settings */ - if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (old), - NM_CONNECTION (new), - NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - "ifnet-update", - &error)) { - /* Shouldn't ever get here as 'new' was verified by the reader already - * and the UUID did not change. */ - g_assert_not_reached (); - } - g_assert_no_error (error); - nm_log_info (LOGD_SETTINGS, "Connection %s updated", - nm_connection_get_id (NM_CONNECTION (new))); - } - g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED); - } else if (new) { - track_new_connection (self, new); - if (is_managed_plugin () && is_managed (conn_name)) - g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_CONNECTION_ADDED, new); - } - - /* Track all valid connections so we can remove deleted ones later */ - g_hash_table_insert (new_connections, - (gpointer) nm_connection_get_uuid (NM_CONNECTION (new)), - new); - } - - /* remove deleted/unused connections */ - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, (gpointer) &uuid, (gpointer) &candidate)) { - /* only saved connections (which have a conn_name) get removed; unsaved - * ones obviously don't exist in /etc/conf.d/net yet and shouldn't get - * blown away by net file changes. - */ - if ( nm_ifnet_connection_get_conn_name (NM_IFNET_CONNECTION (candidate)) - && !g_hash_table_lookup (new_connections, uuid)) { - nm_settings_connection_signal_remove (candidate); - g_hash_table_iter_remove (&iter); - } - } - g_hash_table_destroy (new_connections); - g_list_free (conn_names); -} - -static NMSettingsConnection * -add_connection (NMSettingsPlugin *config, - NMConnection *source, - gboolean save_to_disk, - GError **error) -{ - SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) config); - NMIfnetConnection *new = NULL; - - /* Ensure we reject attempts to add the connection long before we're - * asked to write it to disk. - */ - if (!ifnet_can_write_connection (source, error)) - goto out; - - if (save_to_disk) { - if (!ifnet_add_new_connection (source, CONF_NET_FILE, WPA_SUPPLICANT_CONF, NULL, NULL, error)) - goto out; - reload_connections (config); - new = g_hash_table_lookup (priv->connections, nm_connection_get_uuid (source)); - } else { - new = nm_ifnet_connection_new (source, NULL); - if (new) { - track_new_connection (SETTINGS_PLUGIN_IFNET (config), new); - /* track_new_connection refs 'new' */ - g_object_unref (new); - } - } - -out: - if (!new && error && !*error) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "The ifnet plugin cannot add the connection (unknown error)."); - } - return (NMSettingsConnection *) new; -} - -static void -check_unmanaged (gpointer key, gpointer data, gpointer user_data) -{ - NMIfnetConnection *connection = NM_IFNET_CONNECTION (data); - GSList **list = (GSList **) user_data; - const char *mac, *conn_name; - char *unmanaged_spec; - GSList *iter; - - conn_name = nm_ifnet_connection_get_conn_name (connection); - - if (!conn_name || is_managed (conn_name)) - return; - - nm_log_info (LOGD_SETTINGS, "Checking unmanaged: %s", conn_name); - mac = ifnet_get_data (conn_name, "mac"); - if (mac) - unmanaged_spec = g_strdup_printf ("mac:%s", mac); - else - unmanaged_spec = g_strdup_printf ("interface-name:%s", conn_name); - - /* Just return if the unmanaged spec is already in the list */ - for (iter = *list; iter; iter = g_slist_next (iter)) { - if (g_str_equal (iter->data, unmanaged_spec)) { - g_free (unmanaged_spec); - return; - } - } - - nm_log_info (LOGD_SETTINGS, "Add unmanaged: %s", unmanaged_spec); - *list = g_slist_prepend (*list, unmanaged_spec); -} - -static GSList * -get_unmanaged_specs (NMSettingsPlugin * config) -{ - SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) config); - GSList *list = NULL; - - nm_log_info (LOGD_SETTINGS, "getting unmanaged specs..."); - g_hash_table_foreach (priv->connections, check_unmanaged, &list); - return list; -} - -static GSList * -get_connections (NMSettingsPlugin *config) -{ - SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) config); - GSList *connections = NULL; - GHashTableIter iter; - NMIfnetConnection *connection; - - nm_log_info (LOGD_SETTINGS, "(%p) ... get_connections.", config); - - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) { - const char *conn_name = nm_ifnet_connection_get_conn_name (connection); - - if (!conn_name || (!priv->unmanaged_well_known && is_managed (conn_name))) - connections = g_slist_prepend (connections, connection); - } - nm_log_info (LOGD_SETTINGS, "(%p) connections count: %d", - config, g_slist_length (connections)); - return connections; -} - -/*****************************************************************************/ - -static void -get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - switch (prop_id) { - case NM_SETTINGS_PLUGIN_PROP_NAME: - g_value_set_string (value, IFNET_PLUGIN_NAME_PRINT); - break; - case NM_SETTINGS_PLUGIN_PROP_INFO: - g_value_set_string (value, IFNET_PLUGIN_INFO); - break; - case NM_SETTINGS_PLUGIN_PROP_CAPABILITIES: - g_value_set_uint (value, - NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/*****************************************************************************/ - -static void -init (NMSettingsPlugin *config) -{ - SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (config); - SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self); - - nm_log_info (LOGD_SETTINGS, "Initializing!"); - - priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); - priv->unmanaged_well_known = !is_managed_plugin (); - nm_log_info (LOGD_SETTINGS, "management mode: %s", - priv->unmanaged_well_known ? "unmanaged" : "managed"); - - setup_monitors (NULL, config); - reload_connections (config); - - nm_log_info (LOGD_SETTINGS, "Initialzation complete!"); -} - -/*****************************************************************************/ - -static void -settings_plugin_ifnet_init (SettingsPluginIfnet * plugin) -{ -} - -static void -dispose (GObject * object) -{ - SettingsPluginIfnet *plugin = SETTINGS_PLUGIN_IFNET (object); - SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) plugin); - - cancel_monitors (NULL, object); - if (priv->connections) { - g_hash_table_destroy (priv->connections); - priv->connections = NULL; - } - - ifnet_destroy (); - wpa_parser_destroy (); - G_OBJECT_CLASS (settings_plugin_ifnet_parent_class)->dispose (object); -} - -static void -settings_plugin_ifnet_class_init (SettingsPluginIfnetClass * req_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (req_class); - - object_class->dispose = dispose; - object_class->get_property = get_property; - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_NAME, - NM_SETTINGS_PLUGIN_NAME); - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_INFO, - NM_SETTINGS_PLUGIN_INFO); - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_CAPABILITIES, - NM_SETTINGS_PLUGIN_CAPABILITIES); -} - -static void -settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface) -{ - plugin_iface->init = init; - plugin_iface->get_connections = get_connections; - plugin_iface->get_unmanaged_specs = get_unmanaged_specs; - plugin_iface->add_connection = add_connection; - plugin_iface->reload_connections = reload_connections; -} - -/*****************************************************************************/ - -G_MODULE_EXPORT GObject * -nm_settings_plugin_factory (void) -{ - return G_OBJECT (g_object_ref (settings_plugin_ifnet_get ())); -} diff --git a/src/settings/plugins/ifnet/nms-ifnet-plugin.h b/src/settings/plugins/ifnet/nms-ifnet-plugin.h deleted file mode 100644 index f006e7e4..00000000 --- a/src/settings/plugins/ifnet/nms-ifnet-plugin.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service (ifnet) - * - * Mu Qiao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 1999-2010 Gentoo Foundation, Inc. - */ - -#ifndef _PLUGIN_H_ -#define _PLUGIN_H_ - -#define SETTINGS_TYPE_PLUGIN_IFNET (settings_plugin_ifnet_get_type ()) -#define SETTINGS_PLUGIN_IFNET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SETTINGS_TYPE_PLUGIN_IFNET, SettingsPluginIfnet)) -#define SETTINGS_PLUGIN_IFNET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SETTINGS_TYPE_PLUGIN_IFNET, SettingsPluginIfnetClass)) -#define SETTINGS_IS_PLUGIN_IFNET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SETTINGS_TYPE_PLUGIN_IFNET)) -#define SETTINGS_IS_PLUGIN_IFNET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SETTINGS_TYPE_PLUGIN_IFNET)) -#define SETTINGS_PLUGIN_IFNET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SETTINGS_TYPE_PLUGIN_IFNET, SettingsPluginIfnetClass)) - -typedef struct _SettingsPluginIfnet SettingsPluginIfnet; -typedef struct _SettingsPluginIfnetClass SettingsPluginIfnetClass; - -GType settings_plugin_ifnet_get_type (void); - -#endif diff --git a/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c b/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c deleted file mode 100644 index 2b62e886..00000000 --- a/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c +++ /dev/null @@ -1,584 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * Mu Qiao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 1999-2010 Gentoo Foundation, Inc. - */ - -#include "nm-default.h" - -#include "nms-ifnet-wpa-parser.h" - -#include -#include - -#include "nm-utils/nm-hash-utils.h" -#include "settings/nm-settings-plugin.h" - -#include "nms-ifnet-net-parser.h" -#include "nms-ifnet-net-utils.h" - -/* Security information */ -static GHashTable *wsec_table = NULL; - -/* Global information used for writing */ -static GHashTable *wsec_global_table = NULL; - -static gboolean wpa_parser_data_changed = FALSE; - -static long -wpa_get_long (GHashTable *table, const char *key) -{ - return atol (g_hash_table_lookup (table, key)); -} - -static void -destroy_security (GHashTable * network) -{ - gpointer key, value; - GHashTableIter iter; - - g_return_if_fail (network); - g_hash_table_iter_init (&iter, network); - while (g_hash_table_iter_next (&iter, &key, &value)) { - g_free (key); - g_free (value); - } - - g_hash_table_destroy (network); -} - -static GHashTable * -add_security (GHashTable *security) -{ - GHashTable *oldsecurity; - const char *ssid, *value; - char *ssid_key; - gboolean is_hex_ssid; - - /* Every security information should have a ssid */ - ssid = g_hash_table_lookup (security, "ssid"); - if (!ssid) { - destroy_security (security); - return NULL; - } - - /* Hex format begins with " */ - is_hex_ssid = (ssid[0] != '"'); - if ((value = g_hash_table_lookup (security, "disabled")) != NULL) { - if (strcmp (value, "1") == 0) { - destroy_security (security); - return NULL; - } - } - - /* Default priority is 1 */ - if (g_hash_table_lookup (security, "priority") == NULL) - g_hash_table_insert (security, g_strdup ("priority"), - g_strdup ("1")); - - oldsecurity = g_hash_table_lookup (wsec_table, ssid); - /* Security with lower priority will be ignored */ - if (oldsecurity != NULL) { - if (wpa_get_long (oldsecurity, "priority") >= - wpa_get_long (security, "priority")) { - destroy_security (security); - return NULL; - } else { - g_hash_table_remove (wsec_table, ssid); - destroy_security (oldsecurity); - } - } - - /* format ssid */ - ssid_key = - is_hex_ssid ? g_strdup_printf ("0x%s", - ssid) : - strip_string (g_strdup (ssid), '"'); - g_hash_table_insert (wsec_table, ssid_key, security); - return security; -} - -static void -add_key_value (GHashTable * network, gchar * line) -{ - gpointer orig_key, orig_value; - gchar **key_value; - - if (g_str_has_prefix (line, "network={")) - line += 9; - strip_string (line, '{'); - strip_string (line, '}'); - if (line[0] == '\0') - return; - key_value = g_strsplit (line, "=", 2); - if (g_strv_length (key_value) != 2) { - g_strfreev (key_value); - return; - } - g_strstrip (key_value[0]); - g_strstrip (key_value[1]); - - /* Reserve quotes for psk, wep_key, ssid - * Quotes will determine whether they are hex format */ - if (strcmp (key_value[0], "psk") != 0 - && !g_str_has_prefix (key_value[0], "wep_key") - && strcmp (key_value[0], "ssid") != 0) - strip_string (key_value[1], '"'); - - /* This sucks */ - if (g_hash_table_lookup_extended (network, key_value[0], &orig_key, &orig_value)) { - g_hash_table_remove (network, orig_key); - g_free (orig_key); - g_free (orig_value); - } - - g_hash_table_insert (network, g_strdup (key_value[0]), - g_strdup (key_value[1])); - g_strfreev (key_value); -} - -static void -add_one_wep_key (GHashTable * table, int key_num, gchar * one_wep_key) -{ - if (one_wep_key[0] == 's') { - //asc key - g_hash_table_insert (table, - g_strdup_printf ("wep_key%d", key_num - 1), - g_strdup_printf ("\"%s\"", - one_wep_key + 2)); - } else { - gchar buf[30]; - int i = 0, j = 0; - - //hex key - while (one_wep_key[i] != '\0') { - if (one_wep_key[i] != '-') - buf[j++] = one_wep_key[i]; - i++; - } - buf[j] = '\0'; - g_hash_table_insert (table, - g_strdup_printf ("wep_key%d", key_num - 1), - g_strdup (buf)); - - } -} - -/* Reading wep security information from /etc/conf.d/net. - * This should not be used in future, use wpa_supplicant instead. */ -static void -add_keys_from_net (void) -{ - GList *names = ifnet_get_connection_names (); - GList *iter = names; - gchar *wep_keys = "(\\[([1-4])\\]\\s+(s:\\w{5}|s:\\w{13}|" - "([\\da-fA-F]{4}\\-){2}[\\da-fA-F]{2}|" - "([\\da-fA-F]{4}\\-){6}[\\da-fA-F]{2})\\s+)"; - gchar *key_method = - "\\s+key\\s+\\[([1-4])\\]\\s+enc\\s+(open|restricted)"; - GRegex *regex_keys = g_regex_new (wep_keys, 0, 0, NULL); - GRegex *regex_method = g_regex_new (key_method, 0, 0, NULL); - GMatchInfo *keys_info; - GMatchInfo *method_info; - - while (iter) { - gchar *conn_name = iter->data; - GHashTable *table; - const char *key_str; - - if ((key_str = ifnet_get_data (conn_name, "key")) == NULL) { - iter = g_list_next (iter); - continue; - } - - wpa_add_security (conn_name); - table = _get_hash_table (conn_name); - /* Give lowest priority */ - wpa_set_data (conn_name, "priority", "0"); - g_regex_match (regex_keys, key_str, 0, &keys_info); - /* add wep keys */ - while (g_match_info_matches (keys_info)) { - gchar *key_num = g_match_info_fetch (keys_info, 2); - gchar *one_wep_key = g_match_info_fetch (keys_info, 3); - - add_one_wep_key (table, atoi (key_num), one_wep_key); - g_free (key_num); - g_free (one_wep_key); - g_match_info_next (keys_info, NULL); - } - g_match_info_free (keys_info); - - g_regex_match (regex_method, key_str, 0, &method_info); - /* set default key index and auth alg */ - if (g_match_info_matches (method_info)) { - gchar *default_idx = - g_match_info_fetch (method_info, 1); - gchar *method = g_match_info_fetch (method_info, 2); - - default_idx[0]--; - g_hash_table_insert (table, g_strdup ("wep_tx_keyidx"), - default_idx); - g_hash_table_insert (table, g_strdup ("auth_alg"), - g_ascii_strup (method, -1)); - } - g_match_info_free (method_info); - add_security (table); - iter = g_list_next (iter); - } - g_list_free (names); - g_regex_unref (regex_keys); - g_regex_unref (regex_method); -} - -static void -add_global_data (gchar * line) -{ - gchar **key_value; - - g_strstrip (line); - key_value = g_strsplit (line, "=", 2); - if (g_strv_length (key_value) != 2) { - nm_log_warn (LOGD_SETTINGS, "Can't handle this line: %s\n", line); - g_strfreev (key_value); - return; - } - g_hash_table_insert (wsec_global_table, - g_strdup (g_strstrip (key_value[0])), - g_strdup (g_strstrip (key_value[1]))); - g_strfreev (key_value); -} - -void -wpa_parser_init (const char *wpa_supplicant_conf) -{ - GIOChannel *channel = NULL; - gchar *line; - gboolean complete = FALSE; - - wpa_parser_data_changed = FALSE; - wsec_table = g_hash_table_new (nm_str_hash, g_str_equal); - wsec_global_table = g_hash_table_new (nm_str_hash, g_str_equal); - - if (g_file_test (wpa_supplicant_conf, G_FILE_TEST_IS_REGULAR)) - channel = - g_io_channel_new_file (wpa_supplicant_conf, "r", NULL); - if (channel == NULL) { - nm_log_warn (LOGD_SETTINGS, "Can't open %s for wireless security", - wpa_supplicant_conf); - return; - } - - while (g_io_channel_read_line (channel, &line, NULL, NULL, NULL) - != G_IO_STATUS_EOF) { - g_strstrip (line); - if (line[0] != '#' && line[0] != '\0') { - if (strstr (line, "network={") == NULL) { - add_global_data (line); - g_free (line); - continue; - } else { - GHashTable *network = - g_hash_table_new (nm_str_hash, g_str_equal); - - do { - gchar *quote_start, *quote_end = NULL, *comment; - - if (line[0] == '#' || line[0] == '\0') { - g_free (line); - continue; - } - /* ignore inline comments unless inside - a double-quoted string */ - if ((quote_start = strchr (line, '"')) != NULL) - quote_end = strrchr (quote_start + 1, '"'); - if ((comment = strchr ((quote_end != NULL) ? - quote_end : line, '#')) != NULL) - *comment = '\0'; - if (strstr (line, "}") != NULL) - complete = TRUE; - add_key_value (network, line); - g_free (line); - } while (complete == FALSE - && - g_io_channel_read_line - (channel, &line, NULL, - NULL, NULL) != G_IO_STATUS_EOF); - add_security (network); - //EOF in inner loop - if (complete == FALSE) { - g_free (line); - break; - } - complete = FALSE; - } - } else - g_free (line); - } - - g_io_channel_shutdown (channel, FALSE, NULL); - g_io_channel_unref (channel); - - add_keys_from_net (); -} - -const char * -wpa_get_value (const char *ssid, const char *key) -{ - GHashTable *target = g_hash_table_lookup (wsec_table, ssid); - - if (target) - return g_hash_table_lookup (target, key); - return NULL; -} - -gboolean -exist_ssid (const char *ssid) -{ - return g_hash_table_lookup (wsec_table, ssid) != NULL; -} - -GHashTable * -_get_hash_table (const char *ssid) -{ - return g_hash_table_lookup (wsec_table, ssid); -} - -static gchar *quoted_keys[] = - { "identity", "cert", "private", "phase", "password", NULL }; - -/* tell whether the key needs quotes when writing is performed */ -static gboolean -need_quote (gchar * key) -{ - int i = 0; - - while (quoted_keys[i] != NULL) { - if (strstr (key, quoted_keys[i])) - return TRUE; - i++; - } - return FALSE; -} - -gboolean -wpa_flush_to_file (const char *config_file) -{ - GIOChannel *channel; - GError *error = NULL; - gpointer key, value, ssid, security; - GHashTableIter iter, iter_security; - gchar *out_line; - gsize bytes_written; - gboolean result = FALSE; - - if (!wpa_parser_data_changed) - return TRUE; - if (!wsec_table || !wsec_global_table) - return FALSE; - - backup_file (config_file); - - channel = g_io_channel_new_file (config_file, "w", NULL); - if (!channel) { - nm_log_warn (LOGD_SETTINGS, "Can't open file %s for writing", config_file); - return FALSE; - } - g_hash_table_iter_init (&iter, wsec_global_table); - nm_log_info (LOGD_SETTINGS, "Writing to %s", config_file); - g_io_channel_write_chars (channel, - "#Generated by NetworkManager\n" - "###### Global Configuration ######\n", - -1, &bytes_written, &error); - if (error) - goto done; - - /* Writing global information */ - while (g_hash_table_iter_next (&iter, &key, &value)) { - out_line = - g_strdup_printf ("%s=%s\n", (gchar *) key, (gchar *) value); - g_io_channel_write_chars (channel, out_line, -1, &bytes_written, - &error); - if (bytes_written == 0 || error) - break; - g_free (out_line); - } - if (error) - goto done; - g_io_channel_write_chars (channel, - "\n###### Security Configuration ######\n", - -1, &bytes_written, &error); - if (error) - goto done; - - g_hash_table_iter_init (&iter, wsec_table); - /* Writing security */ - while (g_hash_table_iter_next (&iter, &ssid, &security)) { - g_hash_table_iter_init (&iter_security, - (GHashTable *) security); - g_io_channel_write_chars (channel, "network={\n", -1, - &bytes_written, &error); - if (error) - goto done; - while (g_hash_table_iter_next (&iter_security, &key, &value)) { - out_line = - g_strdup_printf (need_quote ((gchar *) key) ? - "\t%s=\"%s\"\n" : "\t%s=%s\n", - (gchar *) key, (gchar *) value); - g_io_channel_write_chars (channel, out_line, -1, - &bytes_written, &error); - if (bytes_written == 0 || error) - goto done; - g_free (out_line); - } - g_io_channel_write_chars (channel, "}\n\n", -1, &bytes_written, &error); - - } - g_io_channel_flush (channel, &error); - if (error) - goto done; - - wpa_parser_data_changed = FALSE; - result = TRUE; -done: - if (error) { - nm_log_warn (LOGD_SETTINGS, "Error writing WPA configuration: %s", error->message); - g_error_free (error); - } - g_io_channel_shutdown (channel, FALSE, NULL); - g_io_channel_unref (channel); - return result; -} - -/* If value is NULL, this method will delete old key value pair */ -void -wpa_set_data (const char *ssid, const char *key, const char *value) -{ - gpointer old_key = NULL, old_value = NULL; - GHashTable *security = g_hash_table_lookup (wsec_table, ssid); - gchar * stripped = NULL; - - g_return_if_fail (security != NULL); - - if (value){ - stripped = g_strdup(value); - if (strcmp (key, "ssid") != 0 && strcmp (key, "psk") != 0 - && !g_str_has_prefix (key, "wep_key")) - strip_string (stripped, '"'); - } - - /* Remove old key value pairs */ - if (g_hash_table_lookup_extended - (security, key, &old_key, &old_value)) { - if (stripped && !strcmp(old_value, stripped)){ - g_free (stripped); - return; - } - g_hash_table_remove (security, old_key); - g_free (old_key); - g_free (old_value); - } else if (!value) - return; - - /* Add new key value */ - if (stripped) - g_hash_table_insert (security, g_strdup (key), stripped); - wpa_parser_data_changed = TRUE; -} - -gboolean -wpa_has_security (const char *ssid) -{ - return g_hash_table_lookup (wsec_table, ssid) != NULL; -} - -gboolean -wpa_add_security (const char *ssid) -{ - if (wpa_has_security (ssid)) - return TRUE; - else { - GHashTable *security = - g_hash_table_new (nm_str_hash, g_str_equal); - gchar *ssid_i; - - nm_log_info (LOGD_SETTINGS, "Adding security for %s", ssid); - if (g_str_has_prefix (ssid, "0x")) { - /* hex ssid */ - ssid_i = g_strdup (ssid + 2); - } else { - /* ascii ssid requires quotes */ - ssid_i = g_strdup_printf ("\"%s\"", ssid); - } - g_hash_table_insert (security, strdup ("ssid"), ssid_i); - g_hash_table_insert (security, strdup ("priority"), - strdup ("1")); - g_hash_table_insert (wsec_table, g_strdup (ssid), security); - wpa_parser_data_changed = TRUE; - return TRUE; - } -} - -gboolean -wpa_delete_security (const char *ssid) -{ - gpointer old_key, old_value; - - g_return_val_if_fail (wsec_table != NULL && ssid != NULL, FALSE); - nm_log_info (LOGD_SETTINGS, "Deleting security for %s", ssid); - if (!g_hash_table_lookup_extended - (wsec_table, ssid, &old_key, &old_value)) - return FALSE; - g_hash_table_remove (wsec_table, old_key); - g_free (old_key); - destroy_security ((GHashTable *) old_value); - wpa_parser_data_changed = TRUE; - return TRUE; - -} - -void -wpa_parser_destroy (void) -{ - GHashTableIter iter; - gpointer key; - gpointer value; - - /* Destroy security */ - if (wsec_table) { - g_hash_table_iter_init (&iter, wsec_table); - while (g_hash_table_iter_next (&iter, &key, &value)) { - destroy_security ((GHashTable *) value); - g_free (key); - } - - g_hash_table_destroy (wsec_table); - wsec_table = NULL; - } - - /* Destroy global data */ - if (wsec_global_table) { - g_hash_table_iter_init (&iter, wsec_global_table); - while (g_hash_table_iter_next (&iter, &key, &value)) { - g_free (key); - g_free (value); - } - - g_hash_table_destroy (wsec_global_table); - wsec_global_table = NULL; - } -} diff --git a/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.h b/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.h deleted file mode 100644 index d096f468..00000000 --- a/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * Mu Qiao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 1999-2010 Gentoo Foundation, Inc. - */ - -#ifndef _WPA_PARSER_H -#define _WPA_PARSER_H - -#define WPA_SUPPLICANT_CONF SYSCONFDIR "/wpa_supplicant/wpa_supplicant.conf" - -void wpa_parser_init (const char *wpa_supplicant_conf); -void wpa_parser_destroy (void); - -/* reader functions */ -const char *wpa_get_value (const char *ssid, const char *key); -gboolean exist_ssid (const char *ssid); -GHashTable *_get_hash_table (const char *ssid); -gboolean wpa_has_security (const char *ssid); - -/* writer functions */ -gboolean wpa_flush_to_file (const char *config_file); -void wpa_set_data (const char *ssid, const char *key, const char *value); -gboolean wpa_add_security (const char *ssid); -gboolean wpa_delete_security (const char *ssid); -#endif diff --git a/src/settings/plugins/ifnet/tests/net b/src/settings/plugins/ifnet/tests/net deleted file mode 100644 index a5ac9ca2..00000000 --- a/src/settings/plugins/ifnet/tests/net +++ /dev/null @@ -1,158 +0,0 @@ -# This blank configuration will automatically use DHCP for any net.* -# scripts in /etc/init.d. To create a more complete configuration, -# please review /etc/conf.d/net.example and save your configuration -# in /etc/conf.d/net (this file :]!). - -modules="!wpa_supplicant" - -config_eth0=( -"202.117.16.121 netmask 255.255.255.0 brd 202.117.16.255" -"192.168.4.121/24" -"dhcp6" -) -routes_eth0=( "default via 202.117.16.1" - "192.168.4.0/24 via 192.168.4.1") -dns_servers_eth0="202.117.0.20 202.117.0.21" -dns_search_eth0="p12.edu.cn p13.edu.cn" - -config_eth1=( - "dhcp" -) -enable_ipv6_eth1="true" -routes_eth1=( "default via 202.117.16.1" ) -dns_servers_eth1="202.117.0.20 202.117.0.21" -config_eth2=( -"202.117.16.1211 netmask 255.255.255.0 brd 202.117.16.255" -"192.168.4.121/24" -"4321:0:1:2:3:4:567:89ab/64" -) -routes_eth2=("default via 4321:0:1:2:3:4:567:89ab") -enable_ipv6_eth2="true" -config_eth3=("nufjlsjlll") -managed_eth4="false" -routes_eth4=("default via 4321:0:1:2:3:4:567:89ab") -config_eth5=("dhcp") - -config_eth7=( "dhcp" ) -auto_eth7="true" - -# missing config_eth8 -auto_eth8="true" - -#new openrc style -config_eth9="202.117.16.10/24 202.117.17.10/24" -routes_eth9="default via 202.117.16.1 -10.0.0.0/8 via 192.168.0.1 -" -config_eth10="202.117.16.2 netmask 255.255.255.0" -routes_eth10="10.0.0.0/8 via 192.168.0.1" - -config_myxjtu2=("202.117.16.121/24 brd 202.117.16.255") -routes_myxjtu2=("default via 202.117.16.1") -dns_servers_myxjtu2="202.117.0.20 202.117.0.21" -#key_myxjtu2="[1] s:xjtud key [1] enc restricted" -#key_eth6="[1] aaaa-4444-3d [2] s:xjtudlc key [1] enc open" - - -username_ppp0='user' -password_ppp0='password' - -config_qiaomuf=("dhcp") - -config_1xtest=("dhcp") - -config_0xab3ace=("dhcp") - -modules=( "iproute2" ) - config_kvm0=( "null" ) - config_kvm1=( "null" ) - - tuntap_kvm0="tap" - tuntap_kvm1="tap" - tunctl_kvm0="-u user" - tunctl_kvm1="-u user" - -bridge_br0="eth0 kvm0 kvm1" -config_br0=( "192.168.1.10/24" ) - brctl_br0=( "setfd 0") - dhcp_eth1="nosendhost nontp -I" - -predown() { - # The default in the script is to test for NFS root and disallow - # downing interfaces in that case. Note that if you specify a - # predown() function you will override that logic. Here it is, in - # case you still want it... - if is_net_fs /; then - eerror "root filesystem is network mounted -- can't stop ${IFACE}" - return 1 - fi - - # Remember to return 0 on success - return 0 -} - -postup() { - # This function could be used, for example, to register with a - # dynamic DNS service. Another possibility would be to - # send/receive mail once the interface is brought up. - - # Here is an example that allows the use of iproute rules - # which have been configured using the rules_eth0 variable. - #rules_eth0=" \ - # 'from 24.80.102.112/32 to 192.168.1.0/24 table localnet priority 100' \ - # 'from 216.113.223.51/32 to 192.168.1.0/24 table localnet priority 100' \ - #" - eval set -- \$rules_${IFVAR} - if [ $# != 0 ]; then - einfo "Adding IP policy routing rules" - eindent - # Ensure that the kernel supports policy routing - if ! ip rule list | grep -q "^"; then - eerror "You need to enable IP Policy Routing (CONFIG_IP_MULTIPLE_TABLES)" - eerror "in your kernel to use ip rules" - else - for x; do - ebegin "${x}" - ip rule add ${x} - eend $? - done - fi - eoutdent - # Flush the cache - ip route flush cache dev "${IFACE}" - fi - -} - -postdown() { - # Enable Wake-On-LAN for every interface except for lo - # Probably a good idea to set ifdown="no" in /etc/conf.d/net - # as well ;) - [ "${IFACE}" != "lo" ] && ethtool -s "${IFACE}" wol g - - Automatically erase any ip rules created in the example postup above - if interface_exists "${IFACE}"; then - # Remove any rules for this interface - local rule - ip rule list | grep " iif ${IFACE}[ ]*" | { - while read rule; do - rule="${rule#*:}" - ip rule del ${rule} - done - } - # Flush the route cache - ip route flush cache dev "${IFACE}" - fi - - # Return 0 always - return 0 -} - -failup() { - # This function is mostly here for completeness... I haven't - # thought of anything nifty to do with it yet ;-) -} - -faildown() -{} - diff --git a/src/settings/plugins/ifnet/tests/net.all b/src/settings/plugins/ifnet/tests/net.all deleted file mode 100644 index 285a4cdf..00000000 --- a/src/settings/plugins/ifnet/tests/net.all +++ /dev/null @@ -1,864 +0,0 @@ -############################################################################## -# QUICK-START -# -# The quickest start is if you want to use DHCP. -# In that case, everything should work out of the box, no configuration -# necessary, though the startup script will warn you that you haven't -# specified anything. - -# WARNING :- some examples have a mixture of IPv4 (ie 192.168.0.1) and IPv6 -# (ie 4321:0:1:2:3:4:567:89ab) internet addresses. They only work if you have -# the relevant kernel option enabled. So if you don't have an IPv6 enabled -# kernel then remove the IPv6 address from your config. - -# If you want to use a static address or use DHCP explicitly, jump -# down to the section labelled INTERFACE HANDLERS. -# -# If you want to do anything more fancy, you should take the time to -# read through the rest of this file. - -############################################################################## -# MODULES -# -# We now support modular networking scripts which means we can easily -# add support for new interface types and modules while keeping -# compatability with existing ones. -# -# Modules load by default if the package they need is installed. If -# you specify a module here that doesn't have it's package installed -# then you get an error stating which package you need to install. -# Ideally, you only use the modules setting when you have two or more -# packages installed that supply the same service. -# -# In other words, you probably should DO NOTHING HERE... - -# Prefer ifconfig over iproute2 -modules=( "ifconfig" ) - -# You can also specify other modules for an interface -# In this case we prefer udhcpc over dhcpcd -modules_eth0=( "udhcpc" ) - -# You can also specify which modules not to use - for example you may be -# using a supplicant or linux-wlan-ng to control wireless configuration but -# you still want to configure network settings per ESSID associated with. -modules=( "!iwconfig" "!wpa_supplicant" ) -# IMPORTANT: If you need the above, please disable modules in that order - - -############################################################################## -# INTERFACE HANDLERS -# -# We provide two interface handlers presently: ifconfig and iproute2. -# You need one of these to do any kind of network configuration. -# For ifconfig support, emerge sys-apps/net-tools -# For iproute2 support, emerge sys-apps/iproute2 - -# If you don't specify an interface then we prefer iproute2 if it's installed -# To prefer ifconfig over iproute2 -modules=( "ifconfig" ) - -# For a static configuration, use something like this -# (They all do exactly the same thing btw) -config_eth0=( "192.168.0.2/24" ) -config_eth0=( "192.168.0.2 netmask 255.255.255.0" ) - -# We can also specify a broadcast -config_eth0=( "192.168.0.2/24 brd 192.168.0.255" ) -config_eth0=( "192.168.0.2 netmask 255.255.255.0 broadcast 192.168.0.255" ) - -# If you need more than one address, you can use something like this -# NOTE: ifconfig creates an aliased device for each extra IPv4 address -# (eth0:1, eth0:2, etc) -# iproute2 does not do this as there is no need to -config_eth0=( - "192.168.0.2/24" - "192.168.0.3/24" - "192.168.0.4/24" -) -# Or you can use sequence expressions -config_eth0=( "192.168.0.{2..4}/24" ) -# which does the same as above. Be careful though as if you use this and -# fallbacks, you have to ensure that both end up with the same number of -# values otherwise your fallback won't work correctly. - -# You can also use IPv6 addresses -# (you should always specify a prefix length with IPv6 here) -config_eth0=( - "192.168.0.2/24" - "4321:0:1:2:3:4:567:89ab/64" - "4321:0:1:2:3:4:567:89ac/64" -) - -# If you wish to keep existing addresses + routing and the interface is up, -# you can specify a noop (no operation). If the interface is down or there -# are no addresses assigned, then we move onto the next step (default dhcp) -# This is useful when configuring your interface with a kernel command line -# or similar -config_eth0=( "noop" "192.168.0.2/24" ) - -# If you don't want ANY address (only useful when calling for advanced stuff) -config_eth0=( "null" ) - -# Here's how to do routing if you need it -routes_eth0=( - "default via 192.168.0.1" # IPv4 default route - "10.0.0.0/8 via 192.168.0.1" # IPv4 subnet route - "::/0" # IPv6 unicast -) - -# If a specified module fails (like dhcp - see below), you can specify a -# fallback like so -fallback_eth0=( "192.168.0.2 netmask 255.255.255.0" ) -fallback_route_eth0=( "default via 192.168.0.1" ) - -# NOTE: fallback entry must match the entry location in config_eth0 -# As such you can only have one fallback route. - -# Some users may need to alter the MTU - here's how -mtu_eth0="1500" - -# Each module described below can set a default base metric, lower is -# preferred over higher. This is so we can prefer a wired route over a -# wireless route automaticaly. You can override this by setting -metric_eth0="100" -# or on a global basis -metric="100" -# The only downside of the global setting is that you have to ensure that -# there are no conflicting routes yourself. For users with large routing -# tables you may have to set a global metric as the due to a simple read of -# the routing table taking over a minute at a time. - -############################################################################## -# OPTIONAL MODULES - -# INTERFACE RENAMING -# There is no consistent device renaming scheme for Linux. -# The preferred way of naming devices is via the kernel module directly or -# by using udev (http://www.reactivated.net/udevrules.php) - -# If you are unable to write udev rules, then we do provide a way of renaming -# the interface based on it's MAC address, but it is not optimal. -# Here is how to rename an interface whose MAC address is 00:11:22:33:44:55 -# to foo1 -rename_001122334455="foo1" - -# You can also do this based on current device name - although this is not -# recommended. Here we rename eth1 to foo2. -rename_eth1="foo2" - -#----------------------------------------------------------------------------- -# WIRELESS (802.11 support) -# Wireless can be provided by iwconfig or wpa_supplicant - -# iwconfig -# emerge net-wireless/wireless-tools -# Wireless options are held in /etc/conf.d/wireless - but could be here too -# Consult the sample file /etc/conf.d/wireless.example for instructions -# iwconfig is the default - -# wpa_supplicant -# emerge net-wireless/wpa-supplicant -# Wireless options are held in /etc/wpa_supplicant.conf -# Consult the sample file /etc/wpa_supplicant.conf.example for instructions -# To choose wpa_supplicant over iwconfig -modules=( "wpa_supplicant" ) -# To configure wpa_supplicant -wpa_supplicant_eth0="-Dwext" # For generic wireless -wpa_supplicant_ath0="-Dmadwifi" # For Atheros based cards -# Consult wpa_supplicant for more drivers -# By default don't wait for wpa_suppliant to associate and authenticate. -# If you would like to, so can specify how long in seconds -associate_timeout_eth0=60 -# A value of 0 means wait forever. - -# GENERIC WIRELESS OPTIONS -# PLEASE READ THE INSTRUCTIONS IN /etc/conf.d/wireless.example FOR -# HOW TO USE THIS ESSID VARIABLE -# You can also override any settings found here per ESSID - which is very -# handy if you use different networks a lot -config_ESSID=( "dhcp" ) -dhcpcd_ESSID="-t 5" - -# Setting name/domain server causes /etc/resolv.conf to be overwritten -# Note that if DHCP is used, and you want this to take precedence then - set dhcp_ESSID="nodns" -dns_servers_ESSID=( "192.168.0.1" "192.168.0.2" ) -dns_domain_ESSID="some.domain" -dns_search_ESSID="search.this.domain search.that.domain" -# Please check the man page for resolv.conf for more information -# as domain and search are mutually exclusive. - -# You can also override any settings found here per MAC address of the AP -# in case you use Access Points with the same ESSID but need different -# networking configs. Below is an example - of course you use the same -# method with other variables -mac_config_001122334455=( "dhcp" ) -mac_dhcpcd_001122334455="-t 10" -mac_dns_servers_001122334455=( "192.168.0.1" "192.168.0.2" ) - -# When an interface has been associated with an Access Point, a global -# variable called ESSID is set to the Access Point's ESSID for use in the -# pre/post user functions below (although it's not available in preup as you -# won't have associated then) - -# If you're using anything else to configure wireless on your interface AND -# you have installed any of the above packages, you need to disable them -modules=( "!iwconfig" "!wpa_supplicant" ) - -#----------------------------------------------------------------------------- -# DHCP -# DHCP can be provided by dhclient, dhcpcd, pump or udhcpc. -# -# dhclient: emerge net-misc/dhcp -# dhcpcd: emerge net-misc/dhcpcd -# pump: emerge net-misc/pump -# udhcpc: emerge net-misc/udhcp - -# If you have more than one DHCP client installed, you need to specify which -# one to use - otherwise we default to dhcpcd if available. -modules=( "dhclient" ) # to select dhclient over dhcpcd -# -# Notes: -# - All clients send the current hostname to the DHCP server by default -# - dhcpcd does not daemonize when the lease time is infinite -# - udhcp-0.9.3-r3 and earlier do not support getting NTP servers -# - pump does not support getting NIS servers -# - DHCP tends to erase any existing device information - so add -# static addresses after dhcp if you need them -# - dhclient and udhcpc can set other resolv.conf options such as "option" -# and "sortlist"- see the System module for more details - -# Regardless of which DHCP client you prefer, you configure them the -# same way using one of following depending on which interface modules -# you're using. -config_eth0=( "dhcp" ) - -# For passing custom options to dhcpcd use something like the following. This -# example reduces the timeout for retrieving an address from 60 seconds (the -# default) to 10 seconds. -dhcpcd_eth0="-t 10" - -# dhclient, udhcpc and pump don't have many runtime options -# You can pass options to them in a similar manner to dhcpcd though -dhclient_eth0="..." -udhcpc_eth0="..." -pump_eth0="..." - -# GENERIC DHCP OPTIONS -# Set generic DHCP options like so -dhcp_eth0="release nodns nontp nonis nogateway nosendhost" - -# This tells the dhcp client to release it's lease when it stops, not to -# overwrite dns, ntp and nis settings, not to set a default route and not to -# send the current hostname to the dhcp server and when it starts. -# You can use any combination of the above options - the default is not to -# use any of them. - -#----------------------------------------------------------------------------- -# For APIPA support, emerge net-misc/iputils or net-analyzer/arping - -# APIPA is a module that tries to find a free address in the range -# 169.254.0.0-169.254.255.255 by arping a random address in that range on the -# interface. If no reply is found then we assign that address to the interface - -# This is only useful for LANs where there is no DHCP server and you don't -# connect directly to the internet. -config_eth0=( "dhcp" ) -fallback_eth0=( "apipa" ) - -#----------------------------------------------------------------------------- -# ARPING Gateway configuration -# and -# Automatic Private IP Addressing (APIPA) -# For arpingnet / apipa support, emerge net-misc/iputils or net-analyzer/arping -# -# This is a module that tries to find a gateway IP. If it exists then we use -# that gateways configuration for our own. For the configuration variables -# simply ensure that each octet is zero padded and the dots are removed. -# Below is an example. -# -gateways_eth0="192.168.0.1 10.0.0.1" -config_192168000001=( "192.168.0.2/24" ) -routes_192168000001=( "default via 192.168.0.1" ) -dns_servers_192168000001=( "192.168.0.1" ) -config_010000000001=( "10.0.0.254/8" ) -routes_010000000001=( "default via 10.0.0.1" ) -dns_servers_010000000001=( "10.0.0.1" ) - -# We can also specify a specific MAC address for each gateway if different -# networks have the same gateway. -gateways_eth0="192.168.0.1,00:11:22:AA:BB:CC 10.0.0.1,33:44:55:DD:EE:FF" -config_192168000001_001122AABBCC=( "192.168.0.2/24" ) -routes_192168000001_001122AABBCC=( "default via 192.168.0.1" ) -dns_servers_192168000001_001122AABBCC=( "192.168.0.1" ) -config_010000000001_334455DDEEFF=( "10.0.0.254/8" ) -routes_010000000001_334455DDEEFF=( "default via 10.0.0.1" ) -dns_servers_010000000001_334455DDEEFF=( "10.0.0.1" ) - -# If we don't find any gateways (or there are none configured) then we try and -# use APIPA to find a free address in the range 169.254.0.0-169.254.255.255 -# by arping a random address in that range on the interface. If no reply is -# found then we assign that address to the interface. - -# This is only useful for LANs where there is no DHCP server. -config_eth0=( "arping" ) - -# or if no DHCP server can be found -config_eth0=( "dhcp" ) -fallback_eth0=( "arping" ) - -# NOTE: We default to sleeping for 1 second the first time we attempt an -# arping to give the interface time to settle on the LAN. This appears to -# be a good default for most instances, but if not you can alter it here. -arping_sleep=5 -arping_sleep_lan=7 - -# NOTE: We default to waiting 3 seconds to get an arping response. You can -# change the default wait like so. -arping_wait=3 -arping_wait_lan=2 - -#----------------------------------------------------------------------------- -# VLAN (802.1q support) -# For VLAN support, emerge net-misc/vconfig - -# Specify the VLAN numbers for the interface like so -# Please ensure your VLAN IDs are NOT zero-padded -vlans_eth0="1 2" - -# You may not want to assign an IP to the physical interface, but we still -# need it up. -config_eth0=( "null" ) - -# You can also configure the VLAN - see for vconfig man page for more details -vconfig_eth0=( "set_name_type VLAN_PLUS_VID_NO_PAD" ) -vconfig_vlan1=( "set_flag 1" "set_egress_map 2 6" ) -config_vlan1=( "172.16.3.1 netmask 255.255.254.0" ) -config_vlan2=( "172.16.2.1 netmask 255.255.254.0" ) - -# NOTE: Vlans can be configured with a . in their interface names -# When configuring vlans with this name type, you need to replace . with a _ -config_eth0.1=( "dhcp" ) - does not work -config_eth0_1=( "dhcp" ) - does work - -# NOTE: Vlans are controlled by their physical interface and not per vlan -# This means you do not need to create init scripts in /etc/init.d for each -# vlan, you must need to create one for the physical interface. -# If you wish to control the configuration of each vlan through a separate -# script, or wish to rename the vlan interface to something that vconfig -# cannot then you need to do this. -vlan_start_eth0="no" - -# If you do the above then you may want to depend on eth0 like so - RC_NEED_vlan1="net.eth0" -# NOTE: depend functions only work in /etc/conf.d/net -# and not in profile configs such as /etc/conf.d/net.foo - -#----------------------------------------------------------------------------- -# Bonding -# For link bonding/trunking emerge net-misc/ifenslave - -# To bond interfaces together -slaves_bond0="eth0 eth1 eth2" -config_bond0=( "null" ) # You may not want to assign an IP to the bond - -# If any of the slaves require extra configuration - for example wireless or -# ppp devices - we need to depend function on the bonded interfaces -RC_NEED_bond0="net.eth0 net.eth1" - - -#----------------------------------------------------------------------------- -# Classical IP over ATM -# For CLIP support emerge net-dialup/linux-atm - -# Ensure that you have /etc/atmsigd.conf setup correctly -# Now setup each clip interface like so -clip_atm0=( "peer_ip [if.]vpi.vci [opts]" ... ) -# where "peer_ip" is the IP address of a PVC peer (in case of an ATM connection -# with your ISP, your only peer is usually the ISP gateway closest to you), -# "if" is the number of the ATM interface which will carry the PVC, "vpi.vci" -# is the ATM VC address, and "opts" may optionally specify VC parameters like -# qos, pcr, and the like (see "atmarp -s" for further reference). Please also -# note quoting: it is meant to distinguish the VCs you want to create. You may, -# in example, create an atm0 interface to more peers, like this: -clip_atm0=( "1.1.1.254 0.8.35" "1.1.1.253 1.8.35" ) - -# By default, the PVC will use the LLC/SNAP encapsulation. If you rather need a -# null encapsulation (aka "VC mode"), please add the keyword "null" to opts. - - -#----------------------------------------------------------------------------- -# PPP -# For PPP support, emerge net-dialup/ppp -# PPP is used for most dialup connections, including ADSL. -# The older ADSL module is documented below, but you are encouraged to try -# this module first. -# -# You need to create the PPP net script yourself. Make it like so -#ln -s net.lo /etc/init.d/net.ppp0 -# -# We have to instruct ppp0 to actually use ppp -config_ppp0=( "ppp" ) -# -# Each PPP interface requires an interface to use as a "Link" -link_ppp0="/dev/ttyS0" # Most PPP links will use a serial port -link_ppp0="eth0" # PPPoE requires an ethernet interface -link_ppp0="[itf.]vpi.vci" # PPPoA requires the ATM VC's address -link_ppp0="/dev/null" # ISDN links should have this -link_ppp0="pty 'your_link_command'" # PPP links over ssh, rsh, etc -# -# Here you should specify what pppd plugins you want to use -# Available plugins are: pppoe, pppoa, capi, dhcpc, minconn, radius, -# radattr, radrealms and winbind -plugins_ppp0=( - "pppoe" # Required plugin for PPPoE - "pppoa vc-encaps" # Required plugin for PPPoA with an option - "capi" # Required plugin for ISDN -) -# -# PPP requires at least a username. You can optionally set a password here too -# If you don't, then it will use the password specified in /etc/ppp/*-secrets -# against the specified username -username_ppp0='user' -password_ppp0='password' -# NOTE: You can set a blank password like so -password_ppp0= -# -# The PPP daemon has many options you can specify - although there are many -# and may seem daunting, it is recommended that you read the pppd man page -# before enabling any of them -pppd_ppp0=( - "maxfail 0" # WARNING: It's not recommended you use this - # if you don't specify maxfail then we assume 0 - "updetach" # If not set, "/etc/init.d/net.ppp0 start" will return - # immediately, without waiting the link to come up - # for the first time. - # Do not use it for dial-on-demand links! - "debug" # Enables syslog debugging - "noauth" # Do not require the peer to authenticate itself - "defaultroute" # Make this PPP interface the default route - "usepeerdns" # Use the DNS settings provided by PPP - -# On demand options - "demand" # Enable dial on demand - "idle 30" # Link goes down after 30 seconds of inactivity - "10.112.112.112:10.112.112.113" # Phony IP addresses - "ipcp-accept-remote" # Accept the peers idea of remote address - "ipcp-accept-local" # Accept the peers idea of local address - "holdoff 3" # Wait 3 seconds after link dies before re-starting - -# Dead peer detection - "lcp-echo-interval 15" # Send a LCP echo every 15 seconds - "lcp-echo-failure 3" # Make peer dead after 3 consective - # echo-requests - -# Compression options - use these to completely disable compression -# noaccomp noccp nobsdcomp nodeflate nopcomp novj novjccomp - -# Dial-up settings - "lock" # Lock serial port - "115200" # Set the serial port baud rate - "modem crtscts" # Enable hardware flow control - "192.168.0.1:192.168.0.2" # Local and remote IP addresses -) -# -# Dial-up PPP users need to specify at least one telephone number -phone_number_ppp0=( "12345689" ) # Maximum 2 phone numbers are supported -# They will also need a chat script - here's a good one -chat_ppp0=( -# 'ABORT' 'BUSY' -# 'ABORT' 'ERROR' -# 'ABORT' 'NO ANSWER' -# 'ABORT' 'NO CARRIER' -# 'ABORT' 'NO DIALTONE' -# 'ABORT' 'Invalid Login' -# 'ABORT' 'Login incorrect' -# 'TIMEOUT' '5' -# '' 'ATZ' -# 'OK' 'AT' # Put your modem initialization string here -# 'OK' 'ATDT\T' -# 'TIMEOUT' '60' -# 'CONNECT' '' -# 'TIMEOUT' '5' -# '~--' '' -) - -# If the link require extra configuration - for example wireless or -# RFC 268 bridge - we need to depend on the bridge so they get -# configured correctly. -RC_NEED_ppp0="net.nas0" - -#WARNING: if MTU of the PPP interface is less than 1500 and you use this -#machine as a router, you should add the following rule to your firewall -# -#iptables -I FORWARD 1 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu - -#----------------------------------------------------------------------------- -# ADSL -# For ADSL support, emerge net-dialup/rp-pppoe -# WARNING: This ADSL module is being deprecated in favour of the PPP module -# above. -# You should make the following settings and also put your -# username/password information in /etc/ppp/pap-secrets - -# Configure the interface to use ADSL -config_eth0=( "adsl" ) - -# You probably won't need to edit /etc/ppp/pppoe.conf if you set this -adsl_user_eth0="my-adsl-username" - -#----------------------------------------------------------------------------- -# ISDN -# For ISDN support, emerge net-dialup/isdn4k-utils -# You should make the following settings and also put your -# username/password information in /etc/ppp/pap-secrets - -# Configure the interface to use ISDN -config_ippp0=( "dhcp" ) -# It's important to specify dhcp if you need it! -config_ippp0=( "192.168.0.1/24" ) -# Otherwise, you can use a static IP - -# NOTE: The interface name must be either ippp or isdn followed by a number - -# You may need this option to set the default route -ipppd_eth0="defaultroute" - -#----------------------------------------------------------------------------- -# MAC changer -# To set a specific MAC address -mac_eth0="00:11:22:33:44:55" - -# For changing MAC addresses using the below, emerge net-analyzer/macchanger -# - to randomize the last 3 bytes only -mac_eth0="random-ending" -# - to randomize between the same physical type of connection (e.g. fibre, -# copper, wireless) , all vendors -mac_eth0="random-samekind" -# - to randomize between any physical type of connection (e.g. fibre, copper, -# wireless) , all vendors -mac_eth0="random-anykind" -# - full randomization - WARNING: some MAC addresses generated by this may NOT -# act as expected -mac_eth0="random-full" -# custom - passes all parameters directly to net-analyzer/macchanger -mac_eth0="some custom set of parameters" - -# You can also set other options based on the MAC address of your network card -# Handy if you use different docking stations with laptops -config_001122334455=( "dhcp" ) - -#----------------------------------------------------------------------------- -# TUN/TAP -# For TUN/TAP support emerge net-misc/openvpn or sys-apps/usermode-utilities -# -# You must specify if we're a tun or tap device. Then you can give it any -# name you like - such as vpn -tuntap_vpn="tun" -config_vpn=( "192.168.0.1/24") - -# Or stick wit the generic names - like tap0 -tuntap_tap0="tap" -config_tap0=( "192.168.0.1/24") - -# For passing custom options to tunctl use something like the following. This -# example sets the owner to adm -tunctl_tun1="-u adm" -# When using openvpn, there are no options - -#----------------------------------------------------------------------------- -# Bridging (802.1d) -# For bridging support emerge net-misc/bridge-utils - -# To add ports to bridge br0 -bridge_br0="eth0 eth1" -# or dynamically add them when the interface comes up -bridge_add_eth0="br0" -bridge_add_eth1="br0" - -# You need to configure the ports to null values so dhcp does not get started -config_eth0=( "null" ) -config_eth1=( "null" ) - -# Finally give the bridge an address - dhcp or a static IP -config_br0=( "dhcp" ) # may not work when adding ports dynamically -config_br0=( "192.168.0.1/24" ) - -# If any of the ports require extra configuration - for example wireless or -# ppp devices - we need to depend on them like so. -RC_NEED_br0="net.eth0 net.eth1" - -# Below is an example of configuring the bridge -# Consult "man brctl" for more details -brctl_br0=( "setfd 0" "sethello 0" "stp off" ) - -#----------------------------------------------------------------------------- -# RFC 2684 Bridge Support -# For RFC 2684 bridge support emerge net-misc/br2684ctl - -# Interface names have to be of the form nas0, nas1, nas2, etc. -# You have to specify a VPI and VCI for the interface like so -br2684ctl_nas0="-a 0.38" # UK VPI and VCI - -# You may want to configure the encapsulation method as well by adding the -e -# option to the command above (may need to be before the -a command) -# -e 0 # LLC (default) -# -e 1 # VC mux - -# Then you can configure the interface as normal -config_nas0=( "192.168.0.1/24" ) - -#----------------------------------------------------------------------------- -# Tunnelling -# WARNING: For tunnelling it is highly recommended that you -# emerge sys-apps/iproute2 -# -# For GRE tunnels -iptunnel_vpn0="mode gre remote 207.170.82.1 key 0xffffffff ttl 255" - -# For IPIP tunnels -iptunnel_vpn0="mode ipip remote 207.170.82.2 ttl 255" - -# To configure the interface -config_vpn0=( "192.168.0.2 pointopoint 192.168.1.2" ) # ifconfig style -config_vpn0=( "192.168.0.2 peer 192.168.1.1" ) # iproute2 style - -# 6to4 Tunnels allow IPv6 to work over IPv4 addresses, provided you -# have a non-private address configured on an interface. - link_6to4="eth0" # Interface to base it's addresses on - config_6to4=( "ip6to4" ) -# You may want to depend on eth0 like so -RC_NEED_6to4="net.eth0" -# To ensure that eth0 is configured before 6to4. Of course, the tunnel could be -# any name and this also works for any configured interface. -# NOTE: If you're not using iproute2 then your 6to4 tunnel has to be called -# sit0 - otherwise use a different name like 6to4 in the example above. - - -#----------------------------------------------------------------------------- -# System -# For configuring system specifics such as domain, dns, ntp and nis servers -# It's rare that you would need todo this, but you can anyway. -# This is most benefit to wireless users who don't use DHCP so they can change -# their configs based on ESSID. See wireless.example for more details - -# To use dns settings such as these, dns_servers_eth0 must be set! -# If you omit the _eth0 suffix, then it applies to all interfaces unless -# overridden by the interface suffix. -dns_domain_eth0="your.domain" -dns_servers_eth0="192.168.0.2 192.168.0.3" -dns_search_eth0="this.domain that.domain" -dns_options_eth0=( "timeout 1" "rotate" ) -dns_sortlist_eth0="130.155.160.0/255.255.240.0 130.155.0.0" -# See the man page for resolv.conf for details about the options and sortlist -# directives - -ntp_servers_eth0="192.168.0.2 192.168.0.3" - -nis_domain_eth0="domain" -nis_servers_eth0="192.168.0.2 192.168.0.3" - -# NOTE: Setting any of these will stamp on the files in question. So if you -# don't specify dns_servers but you do specify dns_domain then no nameservers -# will be listed in /etc/resolv.conf even if there were any there to start -# with. -# If this is an issue for you then maybe you should look into a resolv.conf -# manager like resolvconf-gentoo to manage this file for you. All packages -# that baselayout supports use resolvconf-gentoo if installed. - -#----------------------------------------------------------------------------- -# Cable in/out detection -# Sometimes the cable is in, others it's out. Obviously you don't want to -# restart net.eth0 every time when you plug it in either. -# -# netplug is a package that detects this and requires no extra configuration -# on your part. -# emerge sys-apps/netplug -# or -# emerge sys-apps/ifplugd -# and you're done :) - -# By default we don't wait for netplug/ifplugd to configure the interface. -# If you would like it to wait so that other services now that network is up -# then you can specify a timeout here. -plug_timeout="10" -# A value of 0 means wait forever. - -# If you don't want to use netplug on a specific interface but you have it -# installed, you can disable it for that interface via the modules statement -modules_eth0=( "!netplug" ) -# You can do the same for ifplugd -# -# You can disable them both with the generic plug -modules_eth0=( "!plug" ) - -# To use specific ifplugd options, fex specifying wireless mode -ifplugd_eth0="--api-mode=wlan" -# man ifplugd for more options - -############################################################################## -# ADVANCED CONFIGURATION -# -# Four functions can be defined which will be called surrounding the -# start/stop operations. The functions are called with the interface -# name first so that one function can control multiple adapters. An extra two -# functions can be defined when an interface fails to start or stop. -# -# The return values for the preup and predown functions should be 0 -# (success) to indicate that configuration or deconfiguration of the -# interface can continue. If preup returns a non-zero value, then -# interface configuration will be aborted. If predown returns a -# non-zero value, then the interface will not be allowed to continue -# deconfiguration. -# -# The return values for the postup, postdown, failup and faildown functions are -# ignored since there's nothing to do if they indicate failure. -# -# ${IFACE} is set to the interface being brought up/down -# ${IFVAR} is ${IFACE} converted to variable name bash allows - -#preup() { -# # Test for link on the interface prior to bringing it up. This -# # only works on some network adapters and requires the mii-diag -# # package to be installed. -# if mii-tool "${IFACE}" 2> /dev/null | grep -q 'no link'; then -# ewarn "No link on ${IFACE}, aborting configuration" -# return 1 -# fi -# -# # Test for link on the interface prior to bringing it up. This -# # only works on some network adapters and requires the ethtool -# # package to be installed. -# if ethtool "${IFACE}" | grep -q 'Link detected: no'; then -# ewarn "No link on ${IFACE}, aborting configuration" -# return 1 -# fi -# -# -# # Remember to return 0 on success -# return 0 -#} - -#predown() { -# # The default in the script is to test for NFS root and disallow -# # downing interfaces in that case. Note that if you specify a -# # predown() function you will override that logic. Here it is, in -# # case you still want it... -# if is_net_fs /; then -# eerror "root filesystem is network mounted -- can't stop ${IFACE}" -# return 1 -# fi -# -# # Remember to return 0 on success -# return 0 -#} - -#postup() { -# # This function could be used, for example, to register with a -# # dynamic DNS service. Another possibility would be to -# # send/receive mail once the interface is brought up. - -# # Here is an example that allows the use of iproute rules -# # which have been configured using the rules_eth0 variable. -# #rules_eth0=( -# # "from 24.80.102.112/32 to 192.168.1.0/24 table localnet priority 100" -# # "from 216.113.223.51/32 to 192.168.1.0/24 table localnet priority 100" -# #) -# local x="rules_${IFVAR}[@]" -# local -a rules=( "${!x}" ) -# if [[ -n ${rules} ]] ; then -# einfo "Adding IP policy routing rules" -# eindent -# # Ensure that the kernel supports policy routing -# if ! ip rule list | grep -q "^" ; then -# eerror "You need to enable IP Policy Routing (CONFIG_IP_MULTIPLE_TABLES)" -# eerror "in your kernel to use ip rules" -# else -# for x in "${rules[@]}" ; do -# ebegin "${x}" -# ip rule add ${x} dev "${IFACE}" -# eend $? -# done -# fi -# eoutdent -# # Flush the cache -# ip route flush cache dev "${IFACE}" -# fi - -#} - -#postdown() { -# # Enable Wake-On-LAN for every interface except for lo -# # Probably a good idea to set RC_DOWN_INTERFACE="no" in /etc/conf.d/rc -# # as well ;) -# [[ ${IFACE} != "lo" ]] && ethtool -s "${IFACE}" wol g - -# Automatically erase any ip rules created in the example postup above -# if interface_exists "${IFACE}" ; then -# # Remove any rules for this interface -# local rule -# ip rule list | grep " iif ${IFACE}[ ]*" | { -# while read rule ; do -# rule="${rule#*:}" -# ip rule del ${rule} -# done -# } -# # Flush the route cache -# ip route flush cache dev "${IFACE}" -# fi - -# # Return 0 always -# return 0 -#} - -#failup() { -# # This function is mostly here for completeness... I haven't -# # thought of anything nifty to do with it yet ;-) -#} - -#faildown() { -# # This function is mostly here for completeness... I haven't -# # thought of anything nifty to do with it yet ;-) -#} - -############################################################################## -# FORCING MODULES -# The Big Fat Warning :- If you use module forcing do not complain to us or -# file bugs about it not working! -# -# Loading modules is a slow affair - we have to check each one for the following -# 1) Code sanity -# 2) Has the required package been emerged? -# 3) Has it modified anything? -# 4) Have all the dependant modules been loaded? - -# Then we have to strip out the conflicting modules based on user preference -# and default configuration and sort them into the correct order. -# Finally we check the end result for dependencies. - -# This, of course, takes valuable CPU time so we provide module forcing as a -# means to speed things up. We still do *some* checking but not much. - -# It is essential that you force modules in the correct order and supply all -# the modules you need. You must always supply an interface module - we -# supply ifconfig or iproute2. - -# The Big Fat Warning :- If you use module forcing do not complain to us or -# file bugs about it not working! - -# Now that we've warned you twice, here's how to do it -modules_force=( "ifconfig" ) -modules_force=( "iproute2" "dhcpcd" ) - -# We can also apply this to a specific interface -modules_force_eth1=( "iproute2" ) - -# The below will not work -modules_force=( "dhcpcd" ) -# No interface (ifconfig/iproute2) -modules_force=( "ifconfig" "essidnet" "iwconfig" ) -# Although it will not crash, essidnet will not work as it has to come after -# iwconfig -modules_force=( "iproute2" "ifconfig" ) -# The interface will be setup twice which will cause problems diff --git a/src/settings/plugins/ifnet/tests/nm-system-settings.conf b/src/settings/plugins/ifnet/tests/nm-system-settings.conf deleted file mode 100644 index 39bc87b8..00000000 --- a/src/settings/plugins/ifnet/tests/nm-system-settings.conf +++ /dev/null @@ -1,5 +0,0 @@ -[main] -plugins=ifnet,keyfile - -[ifnet] -managed=false diff --git a/src/settings/plugins/ifnet/tests/test-ifnet.c b/src/settings/plugins/ifnet/tests/test-ifnet.c deleted file mode 100644 index 68f2b928..00000000 --- a/src/settings/plugins/ifnet/tests/test-ifnet.c +++ /dev/null @@ -1,396 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service (ifnet) - * - * Mu Qiao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 1999-2010 Gentoo Foundation, Inc. - */ - -#include "nm-default.h" - -#include -#include -#include -#include -#include - -#include "nm-utils.h" - -#include "platform/nm-linux-platform.h" -#include "dhcp/nm-dhcp-manager.h" - -#include "settings/plugins/ifnet/nms-ifnet-net-parser.h" -#include "settings/plugins/ifnet/nms-ifnet-net-utils.h" -#include "settings/plugins/ifnet/nms-ifnet-wpa-parser.h" -#include "settings/plugins/ifnet/nms-ifnet-connection-parser.h" - -#include "nm-test-utils-core.h" - -/* Fake config handling; the values it returns don't matter, so this - * is easier than forcing it to read our own config file, etc. - */ -NMDhcpManager * -nm_dhcp_manager_get (void) -{ - return NULL; -} - -const char * -nm_dhcp_manager_get_config (NMDhcpManager *dhcp_manager) -{ - return "dhclient"; -} - -static void -test_getdata (void) -{ - g_assert (ifnet_get_data ("eth1", "config") && - strcmp (ifnet_get_data ("eth1", "config"), "( \"dhcp\" )") == 0); - g_assert (ifnet_get_data ("ppp0", "username") && - strcmp (ifnet_get_data ("ppp0", "username"), "user") == 0); - g_assert (ifnet_get_data ("ppp0", "password") && - strcmp (ifnet_get_data ("ppp0", "password"), "password") == 0); - g_assert (ifnet_get_global_data ("modules") && - strcmp ("!wpa_supplicant", ifnet_get_global_data ("modules")) == 0); -} - -static void -test_is_static (void) -{ - g_assert (!is_static_ip4 ("eth1")); - g_assert (is_static_ip4 ("eth0")); - g_assert (!is_static_ip6 ("eth0")); -} - -static void -test_has_default_route (void) -{ - g_assert (has_default_ip4_route ("eth0")); - g_assert (has_default_ip6_route ("eth4")); - g_assert (!has_default_ip4_route ("eth5") && - !has_default_ip6_route ("eth5")); -} - -static void -test_has_ip6_address (void) -{ - g_assert (has_ip6_address ("eth2")); - g_assert (!has_ip6_address ("eth0")); -} - -static void -test_is_ip4_address (void) -{ - gchar *address1 = "192.168.4.232/24"; - gchar *address2 = "192.168.100.{1..254}/24"; - gchar *address3 = "192.168.4.2555/24"; - - g_assert (is_ip4_address (address1)); - g_assert (is_ip4_address (address2)); - g_assert (!is_ip4_address (address3)); -} - -static void -test_is_ip6_address (void) -{ - gchar *address1 = "4321:0:1:2:3:4:567:89ac/24"; - - g_assert (is_ip6_address (address1)); -} - -static void -check_ip_block (ip_block * iblock, gchar * ip, guint32 prefix, gchar * gateway) -{ - g_assert_cmpstr (ip, ==, iblock->ip); - g_assert (prefix == iblock->prefix); - g_assert_cmpstr (gateway, ==, iblock->next_hop); -} - -static void -test_convert_ipv4_config_block (void) -{ - ip_block *iblock = convert_ip4_config_block ("eth0"); - ip_block *tmp = iblock; - - g_assert (iblock != NULL); - check_ip_block (iblock, "202.117.16.121", 24, "202.117.16.1"); - iblock = iblock->next; - destroy_ip_block (tmp); - g_assert (iblock != NULL); - check_ip_block (iblock, "192.168.4.121", 24, "202.117.16.1"); - destroy_ip_block (iblock); - - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*Can't handle IPv4 address*202.117.16.1211*"); - iblock = convert_ip4_config_block ("eth2"); - g_test_assert_expected_messages (); - g_assert (iblock != NULL && iblock->next == NULL); - check_ip_block (iblock, "192.168.4.121", 24, NULL); - destroy_ip_block (iblock); - - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*missing netmask or prefix*"); - iblock = convert_ip4_config_block ("eth3"); - g_assert (iblock == NULL); -} - -static void -test_convert_ipv4_routes_block (void) -{ - ip_block *iblock = convert_ip4_routes_block ("eth0"); - ip_block *tmp = iblock; - - g_assert (iblock != NULL); - check_ip_block (iblock, "192.168.4.0", 24, "192.168.4.1"); - iblock = iblock->next; - destroy_ip_block (tmp); - g_assert (iblock == NULL); - - iblock = convert_ip4_routes_block ("eth9"); - tmp = iblock; - - g_assert (iblock != NULL); - check_ip_block (iblock, "10.0.0.0", 8, "192.168.0.1"); - iblock = iblock->next; - destroy_ip_block (tmp); - g_assert (iblock == NULL); -} - -static void -test_wpa_parser (void) -{ - const char *value; - - g_assert (exist_ssid ("example")); - - g_assert (exist_ssid ("static-wep-test")); - value = wpa_get_value ("static-wep-test", "key_mgmt"); - g_assert_cmpstr (value, ==, "NONE"); - value = wpa_get_value ("static-wep-test", "wep_key0"); - g_assert_cmpstr (value, ==, "\"abcde\""); - - g_assert (exist_ssid ("leap-example")); - - value = wpa_get_value ("test-with-hash-in-psk", "psk"); - g_assert_cmpstr (value, ==, "\"xjtudlc3731###asdfasdfasdf\""); -} - -static void -test_strip_string (void) -{ - gchar *str = "( \"default via 202.117.16.1\" )"; - gchar *result = g_strdup (str); - gchar *result_b = result; - - result = strip_string (result, '('); - result = strip_string (result, ')'); - result = strip_string (result, '"'); - g_assert_cmpstr (result, ==, "default via 202.117.16.1"); - g_free (result_b); -} - -static void -test_is_unmanaged (void) -{ - g_assert (is_managed ("eth0")); - g_assert (!is_managed ("eth4")); -} - -static void -test_new_connection (void) -{ - GError *error = NULL; - NMConnection *connection; - - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*Can't handle IPv4 address*202.117.16.1211*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*Can't handle IPv6 address*202.117.16.1211*"); - connection = ifnet_update_connection_from_config_block ("eth2", NULL, &error); - g_test_assert_expected_messages (); - g_assert (connection != NULL); - g_object_unref (connection); - - connection = ifnet_update_connection_from_config_block ("qiaomuf", NULL, &error); - g_assert (connection != NULL); - g_object_unref (connection); - - connection = ifnet_update_connection_from_config_block ("myxjtu2", NULL, &error); - g_assert (connection != NULL); - g_object_unref (connection); - - connection = ifnet_update_connection_from_config_block ("eth9", NULL, &error); - g_assert (connection != NULL); - g_object_unref (connection); - - connection = ifnet_update_connection_from_config_block ("eth10", NULL, &error); - g_assert (connection != NULL); - g_object_unref (connection); -} - -static void -kill_backup (char **path) -{ - if (*path) { - unlink (*path); - g_free (*path); - *path = NULL; - } -} - -#define NET_GEN_NAME "net.generate" -#define SUP_GEN_NAME "wpa_supplicant.conf.generate" - -static void -test_update_connection (void) -{ - GError *error = NULL; - NMConnection *connection; - gboolean success; - char *backup = NULL; - char *basepath = TEST_IFNET_DIR; - - connection = ifnet_update_connection_from_config_block ("eth0", basepath, &error); - g_assert (connection != NULL); - - success = ifnet_update_parsers_by_connection (connection, "eth0", - NET_GEN_NAME, - SUP_GEN_NAME, - NULL, - &backup, - &error); - kill_backup (&backup); - g_assert (success); - g_object_unref (connection); - - connection = ifnet_update_connection_from_config_block ("0xab3ace", basepath, &error); - g_assert (connection != NULL); - - success = ifnet_update_parsers_by_connection (connection, "0xab3ace", - NET_GEN_NAME, - SUP_GEN_NAME, - NULL, - &backup, - &error); - kill_backup (&backup); - g_assert (success); - g_object_unref (connection); - - unlink (NET_GEN_NAME); - unlink (SUP_GEN_NAME); -} - -static void -test_add_connection (void) -{ - NMConnection *connection; - char *backup = NULL; - const char *basepath = TEST_IFNET_DIR; - - connection = ifnet_update_connection_from_config_block ("eth0", basepath, NULL); - g_assert (ifnet_add_new_connection (connection, NET_GEN_NAME, SUP_GEN_NAME, NULL, &backup, NULL)); - kill_backup (&backup); - g_object_unref (connection); - - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*Can't handle ipv4 address: brd, missing netmask or prefix*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*Can't handle ipv4 address: 202.117.16.255, missing netmask or prefix*"); - connection = ifnet_update_connection_from_config_block ("myxjtu2", basepath, NULL); - g_test_assert_expected_messages (); - g_assert (ifnet_add_new_connection (connection, NET_GEN_NAME, SUP_GEN_NAME, NULL, &backup, NULL)); - kill_backup (&backup); - g_object_unref (connection); - - unlink (NET_GEN_NAME); - unlink (SUP_GEN_NAME); -} - -static void -test_delete_connection (void) -{ - GError *error = NULL; - NMConnection *connection; - char *backup = NULL; - - connection = ifnet_update_connection_from_config_block ("eth7", NULL, &error); - g_assert (connection != NULL); - g_assert (ifnet_delete_connection_in_parsers ("eth7", NET_GEN_NAME, SUP_GEN_NAME, &backup)); - kill_backup (&backup); - g_object_unref (connection); - - connection = ifnet_update_connection_from_config_block ("qiaomuf", NULL, &error); - g_assert (connection != NULL); - g_assert (ifnet_delete_connection_in_parsers ("qiaomuf", NET_GEN_NAME, SUP_GEN_NAME, &backup)); - kill_backup (&backup); - g_object_unref (connection); - - unlink (NET_GEN_NAME); - unlink (SUP_GEN_NAME); -} - -static void -test_missing_config (void) -{ - gs_free_error GError *error = NULL; - NMConnection *connection; - - connection = ifnet_update_connection_from_config_block ("eth8", NULL, &error); - g_assert_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); - g_assert (connection == NULL && error != NULL); -} - -NMTST_DEFINE (); - -#define TPATH "/settings/plugins/ifnet/" - -int -main (int argc, char **argv) -{ - int ret; - - nm_linux_platform_setup (); - - nmtst_init_assert_logging (&argc, &argv, "WARN", "DEFAULT"); - - ifnet_init (TEST_IFNET_DIR "/net"); - wpa_parser_init (TEST_IFNET_DIR "/wpa_supplicant.conf"); - - g_test_add_func (TPATH "strip-string", test_strip_string); - g_test_add_func (TPATH "is-static", test_is_static); - g_test_add_func (TPATH "has-ip6-address", test_has_ip6_address); - g_test_add_func (TPATH "has-default-route", test_has_default_route); - g_test_add_func (TPATH "get-data", test_getdata); - g_test_add_func (TPATH "is-ip4-address", test_is_ip4_address); - g_test_add_func (TPATH "is-ip6-address", test_is_ip6_address); - g_test_add_func (TPATH "convert-ip4-config", test_convert_ipv4_config_block); - g_test_add_func (TPATH "convert-ip4-routes", test_convert_ipv4_routes_block); - g_test_add_func (TPATH "is-unmanaged", test_is_unmanaged); - g_test_add_func (TPATH "wpa-parser", test_wpa_parser); - g_test_add_func (TPATH "new-connection", test_new_connection); - g_test_add_func (TPATH "update-connection", test_update_connection); - g_test_add_func (TPATH "add-connection", test_add_connection); - g_test_add_func (TPATH "delete-connection", test_delete_connection); - g_test_add_func (TPATH "missing-config", test_missing_config); - - ret = g_test_run (); - - ifnet_destroy (); - wpa_parser_destroy (); - - return ret; -} diff --git a/src/settings/plugins/ifnet/tests/test_ca_cert.pem b/src/settings/plugins/ifnet/tests/test_ca_cert.pem deleted file mode 100644 index ef1be20d..00000000 --- a/src/settings/plugins/ifnet/tests/test_ca_cert.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEjzCCA3egAwIBAgIJAOvnZPt59yIZMA0GCSqGSIb3DQEBBQUAMIGLMQswCQYD -VQQGEwJVUzESMBAGA1UECBMJQmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcw -FQYDVQQKEw5NeSBDb21wYW55IEx0ZDEQMA4GA1UECxMHVGVzdGluZzENMAsGA1UE -AxMEdGVzdDEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTAeFw0wOTAzMTAx -NTEyMTRaFw0xOTAzMDgxNTEyMTRaMIGLMQswCQYDVQQGEwJVUzESMBAGA1UECBMJ -QmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcwFQYDVQQKEw5NeSBDb21wYW55 -IEx0ZDEQMA4GA1UECxMHVGVzdGluZzENMAsGA1UEAxMEdGVzdDEcMBoGCSqGSIb3 -DQEJARYNdGVzdEB0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAKot9j+/+CX1/gZLgJHIXCRgCItKLGnf7qGbgqB9T2ACBqR0jllKWwDKrcWU -xjXNIc+GF9Wnv+lX6G0Okn4Zt3/uRNobL+2b/yOF7M3Td3/9W873zdkQQX930YZc -Rr8uxdRPP5bxiCgtcw632y21sSEbG9mjccAUnV/0jdvfmMNj0i8gN6E0fMBiJ9S3 -FkxX/KFvt9JWE9CtoyL7ki7UIDq+6vj7Gd5N0B3dOa1y+rRHZzKlJPcSXQSEYUS4 -HmKDwiKSVahft8c4tDn7KPi0vex91hlgZVd3usL2E/Vq7o5D9FAZ5kZY0AdFXwdm -J4lO4Mj7ac7GE4vNERNcXVIX59sCAwEAAaOB8zCB8DAdBgNVHQ4EFgQUuDU3Mr7P -T3n1e3Sy8hBauoDFahAwgcAGA1UdIwSBuDCBtYAUuDU3Mr7PT3n1e3Sy8hBauoDF -ahChgZGkgY4wgYsxCzAJBgNVBAYTAlVTMRIwEAYDVQQIEwlCZXJrc2hpcmUxEDAO -BgNVBAcTB05ld2J1cnkxFzAVBgNVBAoTDk15IENvbXBhbnkgTHRkMRAwDgYDVQQL -EwdUZXN0aW5nMQ0wCwYDVQQDEwR0ZXN0MRwwGgYJKoZIhvcNAQkBFg10ZXN0QHRl -c3QuY29tggkA6+dk+3n3IhkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOC -AQEAVRG4aALIvCXCiKfe7K+iJxjBVRDFPEf7JWA9LGgbFOn6pNvbxonrR+0BETdc -JV1ET4ct2xsE7QNFIkp9GKRC+6J32zCo8qtLCD5+v436r8TUG2/t2JRMkb9I2XVT -p7RJoot6M0Ltf8KNQUPYh756xmKZ4USfQUwc58MOSDGY8VWEXJOYij9Pf0e0c52t -qiCEjXH7uXiS8Pgq9TYm7AkWSOrglYhSa83x0f8mtT8Q15nBESIHZ6o8FAS2bBgn -B0BkrKRjtBUkuJG3vTox+bYINh2Gxi1JZHWSV1tN5z3hd4VFcKqanW5OgQwToBqp -3nniskIjbH0xjgZf/nVMyLnjxg== ------END CERTIFICATE----- diff --git a/src/settings/plugins/ifnet/tests/wpa_supplicant.conf b/src/settings/plugins/ifnet/tests/wpa_supplicant.conf deleted file mode 100644 index 917d495d..00000000 --- a/src/settings/plugins/ifnet/tests/wpa_supplicant.conf +++ /dev/null @@ -1,70 +0,0 @@ -# Only WPA-PSK is used. Any valid cipher combination is accepted. -network={ - ssid="example" - proto=WPA - key_mgmt=WPA-PSK - pairwise=CCMP TKIP - group=CCMP TKIP WEP104 WEP40 - psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb - priority=2 -} - -# LEAP with dynamic WEP keys -network={ - ssid="leap-example" - key_mgmt=IEEE8021X - eap=LEAP - identity="user" - password="foobar" -} - -# Shared WEP key connection (no WPA, no IEEE 802.1X) -network={ - ssid="static-wep-test" - key_mgmt=NONE - wep_key0="abcde" - wep_key1=0102030405 - wep_key2="1234567890123" - wep_tx_keyidx=0 - priority=5 -} - -# Wildcard match for SSID (plaintext APs only). This example select any -# open AP regardless of its SSID. -network={ - key_mgmt=NONE -} - -network={ - ssid="myxjtu2" - scan_ssid=1 - key_mgmt=WPA-PSK - psk="xjtudlc3731" - disabled=0 - key_mgmt=NONE - wep_key0="12345" - wep_key1=1234567890 - wep_key2="zxcvb" - wep_tx_keyidx=1 - auth_alg=OPEN - mode=1 -} - -network={ - ssid=ab3ace - key_mgmt=WPA-EAP - eap=TTLS - identity="user@example.com" - anonymous_identity="anonymous@example.com" - password="foobar" - ca_cert="test_ca_cert.pem" - phase2="auth=CHAP" - priority=20 -} - -network={ - ssid="test-with-hash-in-psk" - key_mgmt=WPA-PSK - psk="xjtudlc3731###asdfasdfasdf" -} - diff --git a/src/settings/plugins/ifupdown/meson.build b/src/settings/plugins/ifupdown/meson.build new file mode 100644 index 00000000..fd028f4d --- /dev/null +++ b/src/settings/plugins/ifupdown/meson.build @@ -0,0 +1,53 @@ +sources = files( + 'nms-ifupdown-interface-parser.c', + 'nms-ifupdown-parser.c' +) + +deps = [ + libudev_dep, + nm_dep +] + +cflags = '-DSYSCONFDIR="@0@"'.format(nm_sysconfdir) + +libnms_ifupdown_core = static_library( + 'nms-ifupdown-core', + sources: sources, + dependencies: deps, + c_args: cflags +) + +sources = files( + 'nms-ifupdown-connection.c', + 'nms-ifupdown-plugin.c' +) + +libnm_settings_plugin_ifupdown = shared_module( + 'nm-settings-plugin-ifupdown', + sources: sources, + dependencies: deps, + c_args: cflags, + link_with: libnms_ifupdown_core, + link_args: ldflags_linker_script_settings, + link_depends: linker_script_settings, + install: true, + install_dir: nm_plugindir +) + +core_plugins += libnm_settings_plugin_ifupdown + +# FIXME: check_so_symbols replacement +''' +run_target( + 'check-local-symbols-settings-ifupdown', + command: [check_so_symbols, libnm_settings_plugin_ifupdown.full_path()], + depends: libnm_settings_plugin_ifupdown +) + +check-local-symbols-settings-ifupdown: src/settings/plugins/ifupdown/libnm-settings-plugin-ifupdown.la + $(call check_so_symbols,$(builddir)/src/settings/plugins/ifupdown/.libs/libnm-settings-plugin-ifupdown.so) +''' + +if enable_tests + subdir('tests') +endif diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c index 8421afc2..94a65ecb 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c @@ -126,11 +126,10 @@ _recursive_ifparser (const char *eni_file, int quiet) if (!quiet) nm_log_info (LOGD_SETTINGS, " interface-parser: parsing file %s\n", eni_file); - while (!feof(inp)) { - char *token[128]; /* 255 chars can only be split into 127 tokens */ - char value[255]; /* large enough to join previously split tokens */ + char *token[128]; /* 255 chars can only be split into 127 tokens */ + char value[255]; /* large enough to join previously split tokens */ char *safeptr; int toknum; int len = 0; @@ -169,7 +168,7 @@ _recursive_ifparser (const char *eni_file, int quiet) continue; } -#define SPACES " \t" +#define SPACES " \t" /* tokenize input; */ for (toknum = 0, token[toknum] = strtok_r(line, SPACES, &safeptr); token[toknum] != NULL; diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h index bf9c4465..7e6c8e34 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h +++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h @@ -20,7 +20,6 @@ * (C) Copyright 2004 Tom Parker */ - #ifndef _INTERFACE_PARSER_H #define _INTERFACE_PARSER_H diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c index 99879c92..884c6591 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c @@ -37,7 +37,6 @@ #include "nms-ifupdown-plugin.h" #include "nms-ifupdown-parser.h" - static const gchar* _ifupdownplugin_guess_connection_type (if_block *block) { @@ -63,7 +62,6 @@ _ifupdownplugin_guess_connection_type (if_block *block) return ret_type; } - struct _Mapping { const gchar *domain; const gpointer target; @@ -340,7 +338,7 @@ update_wireless_security_setting_from_if_block(NMConnection *connection, } g_object_set(wireless_security_setting, - newkey, typed_property_value ? typed_property_value : property_value, + newkey, typed_property_value ?: property_value, NULL); security = TRUE; @@ -388,7 +386,7 @@ update_wireless_security_setting_from_if_block(NMConnection *connection, } g_object_set(wireless_security_setting, - newkey, typed_property_value ? typed_property_value : property_value, + newkey, typed_property_value ?: property_value, NULL); security = TRUE; @@ -401,7 +399,6 @@ update_wireless_security_setting_from_if_block(NMConnection *connection, curr = curr->next; } - if (security) nm_connection_add_setting (connection, NM_SETTING (wireless_security_setting)); } diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c index bfad9d15..8bd72d02 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c @@ -50,9 +50,6 @@ #define ENI_INTERFACES_FILE "/etc/network/interfaces" -#define IFUPDOWN_PLUGIN_NAME "ifupdown" -#define IFUPDOWN_PLUGIN_INFO "(C) 2008 Canonical Ltd. To report bugs please use the NetworkManager mailing list." - #define IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT TRUE /* #define ALWAYS_UNMANAGE TRUE */ @@ -302,28 +299,6 @@ get_unmanaged_specs (NMSettingsPlugin *config) /*****************************************************************************/ -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - switch (prop_id) { - case NM_SETTINGS_PLUGIN_PROP_NAME: - g_value_set_string (value, IFUPDOWN_PLUGIN_NAME); - break; - case NM_SETTINGS_PLUGIN_PROP_INFO: - g_value_set_string (value, IFUPDOWN_PLUGIN_INFO); - break; - case NM_SETTINGS_PLUGIN_PROP_CAPABILITIES: - g_value_set_uint (value, NM_SETTINGS_PLUGIN_CAP_NONE); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/*****************************************************************************/ - static void _udev_device_unref (gpointer ptr) { @@ -513,19 +488,6 @@ settings_plugin_ifupdown_class_init (SettingsPluginIfupdownClass *req_class) GObjectClass *object_class = G_OBJECT_CLASS (req_class); object_class->dispose = dispose; - object_class->get_property = get_property; - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_NAME, - NM_SETTINGS_PLUGIN_NAME); - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_INFO, - NM_SETTINGS_PLUGIN_INFO); - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_CAPABILITIES, - NM_SETTINGS_PLUGIN_CAPABILITIES); } static void diff --git a/src/settings/plugins/ifupdown/tests/meson.build b/src/settings/plugins/ifupdown/tests/meson.build new file mode 100644 index 00000000..ee3b6a34 --- /dev/null +++ b/src/settings/plugins/ifupdown/tests/meson.build @@ -0,0 +1,15 @@ +test_unit = 'test-ifupdown' + +exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, + c_args: nm_build_cflags, + link_with: libnms_ifupdown_core +) + +test( + 'ifupdown/' + test_unit, + test_script, + args: test_args + [exe.full_path()] +) diff --git a/src/settings/plugins/ifupdown/tests/test-ifupdown.c b/src/settings/plugins/ifupdown/tests/test-ifupdown.c index d037b8a6..18bad65e 100644 --- a/src/settings/plugins/ifupdown/tests/test-ifupdown.c +++ b/src/settings/plugins/ifupdown/tests/test-ifupdown.c @@ -29,6 +29,10 @@ #include "nm-test-utils-core.h" +#define TEST_DIR NM_BUILD_SRCDIR"/src/settings/plugins/ifupdown/tests" + +/*****************************************************************************/ + typedef struct { char *key; char *data; @@ -168,7 +172,7 @@ dump_blocks (void) for (n = ifparser_getfirst (); n != NULL; n = n->next) { if_data *m; - // each block start with its type & name + // each block start with its type & name // (single quotes used to show typ & name baoundaries) g_print("'%s' '%s'\n", n->type, n->name); @@ -659,45 +663,45 @@ main (int argc, char **argv) if (0) dump_blocks (); - g_test_add_data_func ("/ifupdate/ignore_line_before_first_block", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/ignore_line_before_first_block", TEST_DIR, (GTestDataFunc) test1_ignore_line_before_first_block); - g_test_add_data_func ("/ifupdate/wrapped_line", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/wrapped_line", TEST_DIR, (GTestDataFunc) test2_wrapped_line); - g_test_add_data_func ("/ifupdate/wrapped_multiline_multiarg", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/wrapped_multiline_multiarg", TEST_DIR, (GTestDataFunc) test3_wrapped_multiline_multiarg); - g_test_add_data_func ("/ifupdate/allow_auto_is_auto", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/allow_auto_is_auto", TEST_DIR, (GTestDataFunc) test4_allow_auto_is_auto); - g_test_add_data_func ("/ifupdate/allow_auto_multiarg", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/allow_auto_multiarg", TEST_DIR, (GTestDataFunc) test5_allow_auto_multiarg); - g_test_add_data_func ("/ifupdate/mixed_whitespace", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/mixed_whitespace", TEST_DIR, (GTestDataFunc) test6_mixed_whitespace); - g_test_add_data_func ("/ifupdate/long_line", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/long_line", TEST_DIR, (GTestDataFunc) test7_long_line); - g_test_add_data_func ("/ifupdate/long_line_wrapped", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/long_line_wrapped", TEST_DIR, (GTestDataFunc) test8_long_line_wrapped); - g_test_add_data_func ("/ifupdate/wrapped_lines_in_block", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/wrapped_lines_in_block", TEST_DIR, (GTestDataFunc) test9_wrapped_lines_in_block); - g_test_add_data_func ("/ifupdate/complex_wrap", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/complex_wrap", TEST_DIR, (GTestDataFunc) test11_complex_wrap); - g_test_add_data_func ("/ifupdate/complex_wrap_split_word", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/complex_wrap_split_word", TEST_DIR, (GTestDataFunc) test12_complex_wrap_split_word); - g_test_add_data_func ("/ifupdate/more_mixed_whitespace", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/more_mixed_whitespace", TEST_DIR, (GTestDataFunc) test13_more_mixed_whitespace); - g_test_add_data_func ("/ifupdate/mixed_whitespace_block_start", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/mixed_whitespace_block_start", TEST_DIR, (GTestDataFunc) test14_mixed_whitespace_block_start); - g_test_add_data_func ("/ifupdate/trailing_space", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/trailing_space", TEST_DIR, (GTestDataFunc) test15_trailing_space); - g_test_add_data_func ("/ifupdate/missing_newline", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/missing_newline", TEST_DIR, (GTestDataFunc) test16_missing_newline); - g_test_add_data_func ("/ifupdate/read_static_ipv4", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/read_static_ipv4", TEST_DIR, (GTestDataFunc) test17_read_static_ipv4); - g_test_add_data_func ("/ifupdate/read_static_ipv6", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/read_static_ipv6", TEST_DIR, (GTestDataFunc) test18_read_static_ipv6); - g_test_add_data_func ("/ifupdate/read_static_ipv4_plen", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/read_static_ipv4_plen", TEST_DIR, (GTestDataFunc) test19_read_static_ipv4_plen); - g_test_add_data_func ("/ifupdate/source_stanza", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/source_stanza", TEST_DIR, (GTestDataFunc) test20_source_stanza); - g_test_add_data_func ("/ifupdate/source_dir_stanza", TEST_ENI_DIR, + g_test_add_data_func ("/ifupdate/source_dir_stanza", TEST_DIR, (GTestDataFunc) test21_source_dir_stanza); return g_test_run (); diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c index e6299d1e..3723db94 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c @@ -436,7 +436,7 @@ read_connections (NMSettingsPlugin *config) return; } - alive_connections = g_hash_table_new (NULL, NULL); + alive_connections = g_hash_table_new (nm_direct_hash, NULL); filenames = g_ptr_array_new_with_free_func (g_free); while ((item = g_dir_read_name (dir))) { @@ -561,28 +561,6 @@ get_unmanaged_specs (NMSettingsPlugin *config) /*****************************************************************************/ -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - switch (prop_id) { - case NM_SETTINGS_PLUGIN_PROP_NAME: - g_value_set_string (value, NMS_KEYFILE_PLUGIN_NAME); - break; - case NM_SETTINGS_PLUGIN_PROP_INFO: - g_value_set_string (value, NMS_KEYFILE_PLUGIN_INFO); - break; - case NM_SETTINGS_PLUGIN_PROP_CAPABILITIES: - g_value_set_uint (value, NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/*****************************************************************************/ - static void nms_keyfile_plugin_init (NMSKeyfilePlugin *plugin) { @@ -644,19 +622,6 @@ nms_keyfile_plugin_class_init (NMSKeyfilePluginClass *req_class) object_class->constructed = constructed; object_class->dispose = dispose; - object_class->get_property = get_property; - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_NAME, - NM_SETTINGS_PLUGIN_NAME); - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_INFO, - NM_SETTINGS_PLUGIN_INFO); - - g_object_class_override_property (object_class, - NM_SETTINGS_PLUGIN_PROP_CAPABILITIES, - NM_SETTINGS_PLUGIN_CAPABILITIES); } static void diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.h b/src/settings/plugins/keyfile/nms-keyfile-utils.h index be7d668a..cd3f42b9 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.h +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h @@ -23,9 +23,6 @@ #include "NetworkManagerUtils.h" -#define NMS_KEYFILE_PLUGIN_NAME "keyfile" -#define NMS_KEYFILE_PLUGIN_INFO "(c) 2007 - 2016 Red Hat, Inc. To report bugs please use the NetworkManager mailing list." - #define NMS_KEYFILE_CONNECTION_LOG_PATH(path) ((path) ?: "in-memory") #define NMS_KEYFILE_CONNECTION_LOG_FMT "%s (%s,\"%s\")" #define NMS_KEYFILE_CONNECTION_LOG_ARG(con) NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_connection_get_uuid ((NMConnection *) (con)), nm_connection_get_id ((NMConnection *) (con)) diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection index 5cb4d726..1e62f4b3 100644 --- a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection +++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection @@ -15,7 +15,7 @@ mtu=1400 [ipv4] method=manual -dns=4.2.2.1;4.2.2.2; +dns=4.2.2.1;bogus;4.2.2.2; addresses1=192.168.0.5;24;192.168.0.1; addresses2=1.2.3.4;16;1.2.1.1; address=2.3.4.5/24,2.3.4.6 @@ -26,15 +26,21 @@ routes1=1.2.3.0/24,2.3.4.8,99 route=5.6.7.8/32 routes2=1.1.1.2/12, routes3=1.1.1.3/13,, +routes7=1.1.1.7/17,0.0.0.0 routes4=1.1.1.4/14,2.2.2.4 +address30=1.2.3.130/24 routes5=1.1.1.5/15,2.2.2.5, routes6=1.1.1.6/16,2.2.2.6,0 -routes7=1.1.1.7/17,0.0.0.0 routes8=1.1.1.8/18,0.0.0.0, routes9=1.1.1.9/19,0.0.0.0,0 +route10=1.1.1.10/21,,0 routes10=1.1.1.10/20,,0 routes11=1.1.1.11/21,,21 routes11_options=cwnd=10,lock-cwnd=true,mtu=1430,src=7.7.7.7 +address30=1.2.3.30/24 +addresses30=1.2.3.30/25 +addresses31=1.2.3.31/25 +address31=1.2.3.31/24 ignore-auto-routes=false ignore-auto-dns=false diff --git a/src/settings/plugins/keyfile/tests/meson.build b/src/settings/plugins/keyfile/tests/meson.build new file mode 100644 index 00000000..33aaa264 --- /dev/null +++ b/src/settings/plugins/keyfile/tests/meson.build @@ -0,0 +1,16 @@ +test_unit = 'test-keyfile' + +test_keyfiles_dir = join_paths(meson.current_source_dir(), 'keyfiles') + +exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, + c_args: nm_build_cflags, +) + +test( + 'keyfile/' + test_unit, + test_script, + args: test_args + [exe.full_path()] +) diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c index f27efddd..a3045f7d 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile.c @@ -37,6 +37,9 @@ #include "nm-test-utils-core.h" +#define TEST_KEYFILES_DIR NM_BUILD_SRCDIR"/src/settings/plugins/keyfile/tests/keyfiles" +#define TEST_SCRATCH_DIR NM_BUILD_BUILDDIR"/src/settings/plugins/keyfile/tests/keyfiles" + /*****************************************************************************/ static void @@ -231,34 +234,21 @@ test_read_valid_wired_connection (void) char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; gboolean success; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*ipv4.addresses:*semicolon at the end*addresses1*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*ipv4.addresses:*semicolon at the end*addresses2*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*missing prefix length*address4*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*missing prefix length*address5*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*ipv4.routes*semicolon at the end*routes2*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*ipv4.routes*semicolon at the end*routes3*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*ipv4.routes*semicolon at the end*routes5*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*ipv4.routes*semicolon at the end*routes8*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*missing prefix length*address4*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*ipv6.address*semicolon at the end*address5*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*missing prefix length*address5*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*ipv6.address*semicolon at the end*address7*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*ipv6.routes*semicolon at the end*routes1*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*ipv6.route*semicolon at the end*route6*"); + NMTST_EXPECT_NM_INFO ("*ipv4.addresses:*semicolon at the end*addresses1*"); + NMTST_EXPECT_NM_INFO ("*ipv4.addresses:*semicolon at the end*addresses2*"); + NMTST_EXPECT_NM_WARN ("*missing prefix length*address4*"); + NMTST_EXPECT_NM_WARN ("*missing prefix length*address5*"); + NMTST_EXPECT_NM_WARN ("*ipv4.dns: ignoring invalid DNS server IPv4 address 'bogus'*"); + NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes2*"); + NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes3*"); + NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes5*"); + NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes8*"); + NMTST_EXPECT_NM_WARN ("*missing prefix length*address4*"); + NMTST_EXPECT_NM_INFO ("*ipv6.address*semicolon at the end*address5*"); + NMTST_EXPECT_NM_WARN ("*missing prefix length*address5*"); + NMTST_EXPECT_NM_INFO ("*ipv6.address*semicolon at the end*address7*"); + NMTST_EXPECT_NM_INFO ("*ipv6.routes*semicolon at the end*routes1*"); + NMTST_EXPECT_NM_INFO ("*ipv6.route*semicolon at the end*route6*"); connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection", &error); g_assert_no_error (error); g_test_assert_expected_messages (); @@ -294,19 +284,23 @@ test_read_valid_wired_connection (void) g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 1), ==, "4.2.2.2"); /* IPv4 addresses */ - g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 6); + g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 10); check_ip_address (s_ip4, 0, "2.3.4.5", 24); check_ip_address (s_ip4, 1, "192.168.0.5", 24); check_ip_address (s_ip4, 2, "1.2.3.4", 16); check_ip_address (s_ip4, 3, "3.4.5.6", 16); check_ip_address (s_ip4, 4, "4.5.6.7", 24); check_ip_address (s_ip4, 5, "5.6.7.8", 24); + check_ip_address (s_ip4, 6, "1.2.3.30", 24); + check_ip_address (s_ip4, 7, "1.2.3.30", 25); + check_ip_address (s_ip4, 8, "1.2.3.31", 24); + check_ip_address (s_ip4, 9, "1.2.3.31", 25); /* IPv4 gateway */ g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "2.3.4.6"); /* IPv4 routes */ - g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 12); + g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 13); check_ip_route (s_ip4, 0, "5.6.7.8", 32, NULL, -1); check_ip_route (s_ip4, 1, "1.2.3.0", 24, "2.3.4.8", 99); check_ip_route (s_ip4, 2, "1.1.1.2", 12, NULL, -1); @@ -317,11 +311,12 @@ test_read_valid_wired_connection (void) check_ip_route (s_ip4, 7, "1.1.1.7", 17, NULL, -1); check_ip_route (s_ip4, 8, "1.1.1.8", 18, NULL, -1); check_ip_route (s_ip4, 9, "1.1.1.9", 19, NULL, 0); - check_ip_route (s_ip4, 10, "1.1.1.10", 20, NULL, 0); - check_ip_route (s_ip4, 11, "1.1.1.11", 21, NULL, 21); + check_ip_route (s_ip4, 10, "1.1.1.10", 21, NULL, 0); + check_ip_route (s_ip4, 11, "1.1.1.10", 20, NULL, 0); + check_ip_route (s_ip4, 12, "1.1.1.11", 21, NULL, 21); /* Route attributes */ - route = nm_setting_ip_config_get_route (s_ip4, 11); + route = nm_setting_ip_config_get_route (s_ip4, 12); g_assert (route); nmtst_assert_route_attribute_uint32 (route, NM_IP_ROUTE_ATTRIBUTE_CWND, 10); @@ -408,7 +403,6 @@ add_one_ip_route (NMSettingIPConfig *s_ip, nm_ip_route_unref (route); } - static void test_write_wired_connection (void) { @@ -649,12 +643,9 @@ test_read_wired_mac_case (void) char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0xaa, 0xbb, 0xcc, 0x55 }; gboolean success; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*ipv4.addresses*semicolon at the end*addresses1*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*ipv4.addresses*semicolon at the end*addresses2*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, - "*ipv6.routes*semicolon at the end*routes1*"); + NMTST_EXPECT_NM_INFO ("*ipv4.addresses*semicolon at the end*addresses1*"); + NMTST_EXPECT_NM_INFO ("*ipv4.addresses*semicolon at the end*addresses2*"); + NMTST_EXPECT_NM_INFO ("*ipv6.routes*semicolon at the end*routes1*"); connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection_MAC_Case", NULL); g_test_assert_expected_messages (); g_assert (connection); @@ -1415,10 +1406,8 @@ test_read_wired_8021x_tls_blob_connection (void) gboolean success; GBytes *blob; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "* * keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "* * keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); + NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); + NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Blob", &error); g_assert_no_error (error); g_assert (connection); @@ -1447,8 +1436,7 @@ test_read_wired_8021x_tls_blob_connection (void) g_assert_cmpint (nm_setting_802_1x_get_ca_cert_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_BLOB); /* Make sure it's not a path, since it's a blob */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, - NMTST_G_RETURN_MSG (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)); + NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)); tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x); g_test_assert_expected_messages (); g_assert (tmp == NULL); @@ -1476,8 +1464,7 @@ test_read_wired_8021x_tls_bad_path_connection (void) char *tmp2; gboolean success; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*does not exist*"); + NMTST_EXPECT_NM_WARN ("*does not exist*"); connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Path_Missing", &error); g_test_assert_expected_messages (); g_assert_no_error (error); @@ -1530,12 +1517,9 @@ test_read_wired_8021x_tls_old_connection (void) const char *tmp; gboolean success; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "* * keyfile: 802-1x.ca-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem' does not exist*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "* * keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "* * keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); + NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.ca-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem' does not exist*"); + NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); + NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Old", &error); g_assert_no_error (error); g_assert (connection); @@ -2433,7 +2417,6 @@ test_read_minimal (void) &s_con); nmtst_connection_normalize (con_archetype); - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_1"); g_object_set (s_con, NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), @@ -2442,7 +2425,6 @@ test_read_minimal (void) nmtst_assert_connection_equals (con_archetype, FALSE, connection, FALSE); g_clear_object (&connection); - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_2"); g_object_set (s_con, NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), @@ -2469,7 +2451,6 @@ test_read_minimal_slave (void) NULL); nmtst_connection_normalize (con_archetype); - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_slave_1"); g_object_set (s_con, NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), @@ -2478,7 +2459,6 @@ test_read_minimal_slave (void) nmtst_assert_connection_equals (con_archetype, FALSE, connection, FALSE); g_clear_object (&connection); - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_slave_2"); g_object_set (s_con, NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), diff --git a/src/settings/plugins/meson.build b/src/settings/plugins/meson.build new file mode 100644 index 00000000..a1aa7823 --- /dev/null +++ b/src/settings/plugins/meson.build @@ -0,0 +1,15 @@ +if enable_ibft + subdir('ibft') +endif + +if enable_ifcfg_rh + subdir('ifcfg-rh') +endif + +if enable_ifupdown + subdir('ifupdown') +endif + +if enable_tests + subdir('keyfile/tests') +endif diff --git a/src/supplicant/nm-supplicant-config.c b/src/supplicant/nm-supplicant-config.c index 16e7851a..80db5baa 100644 --- a/src/supplicant/nm-supplicant-config.c +++ b/src/supplicant/nm-supplicant-config.c @@ -47,6 +47,8 @@ typedef struct { guint32 ap_scan; gboolean fast_required; gboolean dispose_has_run; + gboolean support_pmf; + gboolean support_fils; } NMSupplicantConfigPrivate; struct _NMSupplicantConfig { @@ -65,9 +67,18 @@ G_DEFINE_TYPE (NMSupplicantConfig, nm_supplicant_config, G_TYPE_OBJECT) /*****************************************************************************/ NMSupplicantConfig * -nm_supplicant_config_new (void) +nm_supplicant_config_new (gboolean support_pmf, gboolean support_fils) { - return g_object_new (NM_TYPE_SUPPLICANT_CONFIG, NULL); + NMSupplicantConfigPrivate *priv; + NMSupplicantConfig *self; + + self = g_object_new (NM_TYPE_SUPPLICANT_CONFIG, NULL); + priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self); + + priv->support_pmf = support_pmf; + priv->support_fils = support_fils; + + return self; } static void @@ -133,7 +144,7 @@ nm_supplicant_config_add_option_with_type (NMSupplicantConfig *self, memset (&buf[0], 0, sizeof (buf)); memcpy (&buf[0], value, len > 254 ? 254 : len); g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, - "key '%s' and/or value '%s' invalid", key, hidden ? hidden : buf); + "key '%s' and/or value '%s' invalid", key, hidden ?: buf); return FALSE; } } @@ -157,7 +168,7 @@ nm_supplicant_config_add_option_with_type (NMSupplicantConfig *self, char buf[255]; memset (&buf[0], 0, sizeof (buf)); memcpy (&buf[0], opt->value, opt->len > 254 ? 254 : opt->len); - nm_log_info (LOGD_SUPPLICANT, "Config: added '%s' value '%s'", key, hidden ? hidden : &buf[0]); + nm_log_info (LOGD_SUPPLICANT, "Config: added '%s' value '%s'", key, hidden ?: &buf[0]); } g_hash_table_insert (priv->config, g_strdup (key), opt); @@ -264,7 +275,6 @@ nm_supplicant_config_finalize (GObject *object) G_OBJECT_CLASS (nm_supplicant_config_parent_class)->finalize (object); } - static void nm_supplicant_config_class_init (NMSupplicantConfigClass *klass) { @@ -369,7 +379,6 @@ nm_supplicant_config_add_setting_macsec (NMSupplicantConfig * self, NMSettingMacsec * setting, GError **error) { - NMSupplicantConfigPrivate *priv; gs_unref_bytes GBytes *bytes = NULL; const char *value; char buf[32]; @@ -379,8 +388,6 @@ nm_supplicant_config_add_setting_macsec (NMSupplicantConfig * self, g_return_val_if_fail (setting != NULL, FALSE); g_return_val_if_fail (!error || !*error, FALSE); - priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self); - if (!nm_supplicant_config_add_option (self, "macsec_policy", "1", -1, NULL, error)) return FALSE; @@ -736,8 +743,10 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, const char *con_uuid, guint32 mtu, NMSettingWirelessSecurityPmf pmf, + NMSettingWirelessSecurityFils fils, GError **error) { + NMSupplicantConfigPrivate *priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self); const char *key_mgmt, *key_mgmt_conf, *auth_alg; const char *psk; @@ -746,18 +755,37 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, g_return_val_if_fail (con_uuid != NULL, FALSE); g_return_val_if_fail (!error || !*error, FALSE); + /* Check if we actually support FILS */ + if (!priv->support_fils) { + if (fils == NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED) { + g_set_error_literal (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, + "Supplicant does not support FILS"); + return FALSE; + } else if (fils == NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL) + fils = NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE; + } + key_mgmt = key_mgmt_conf = nm_setting_wireless_security_get_key_mgmt (setting); - if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL) { - if (nm_streq (key_mgmt_conf, "wpa-psk")) + if (nm_streq (key_mgmt, "wpa-psk")) { + if (priv->support_pmf) key_mgmt_conf = "wpa-psk wpa-psk-sha256"; - else if (nm_streq (key_mgmt_conf, "wpa-eap")) - key_mgmt_conf = "wpa-eap wpa-eap-sha256"; - } else if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) { - if (nm_streq (key_mgmt_conf, "wpa-psk")) - key_mgmt_conf = "wpa-psk-sha256"; - else if (nm_streq (key_mgmt_conf, "wpa-eap")) - key_mgmt_conf = "wpa-eap-sha256"; + } else if (nm_streq (key_mgmt, "wpa-eap")) { + switch (fils) { + case NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL: + key_mgmt_conf = priv->support_pmf + ? "wpa-eap wpa-eap-sha256 fils-sha256 fils-sha384" + : "wpa-eap fils-sha256 fils-sha384"; + break; + case NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED: + key_mgmt_conf = "fils-sha256 fils-sha384"; + break; + default: + if (priv->support_pmf) + key_mgmt_conf = "wpa-eap wpa-eap-sha256"; + break; + } } + if (!add_string_val (self, key_mgmt_conf, "key_mgmt", TRUE, NULL, error)) return FALSE; @@ -803,6 +831,20 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, } } + /* Don't try to enable PMF on non-WPA networks */ + if (!NM_IN_STRSET (key_mgmt, "wpa-eap", "wpa-psk")) + pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE; + + /* Check if we actually support PMF */ + if (!priv->support_pmf) { + if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) { + g_set_error_literal (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, + "Supplicant does not support PMF"); + return FALSE; + } else if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL) + pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE; + } + /* Only WPA-specific things when using WPA */ if ( !strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk") @@ -937,7 +979,7 @@ add_pkcs11_uri_with_pin (NMSupplicantConfig *self, tmp = g_strdup_printf ("%s%s%s", split[0], (pin_qattr ? "?" : ""), - (pin_qattr ? pin_qattr : "")); + (pin_qattr ?: "")); tmp_log = g_strdup_printf ("%s%s%s", split[0], (pin_qattr ? "?" : ""), @@ -1129,7 +1171,7 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self, /* CA path */ path = nm_setting_802_1x_get_ca_path (setting); - path = ca_path_override ? ca_path_override : path; + path = ca_path_override ?: path; if (path) { if (!add_string_val (self, path, "ca_path", FALSE, NULL, error)) return FALSE; @@ -1137,7 +1179,7 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self, /* Phase2 CA path */ path = nm_setting_802_1x_get_phase2_ca_path (setting); - path = ca_path_override ? ca_path_override : path; + path = ca_path_override ?: path; if (path) { if (!add_string_val (self, path, "ca_path2", FALSE, NULL, error)) return FALSE; diff --git a/src/supplicant/nm-supplicant-config.h b/src/supplicant/nm-supplicant-config.h index d90d82b8..f6c845a3 100644 --- a/src/supplicant/nm-supplicant-config.h +++ b/src/supplicant/nm-supplicant-config.h @@ -40,7 +40,7 @@ typedef struct _NMSupplicantConfigClass NMSupplicantConfigClass; GType nm_supplicant_config_get_type (void); -NMSupplicantConfig *nm_supplicant_config_new (void); +NMSupplicantConfig *nm_supplicant_config_new (gboolean support_pmf, gboolean support_fils); guint32 nm_supplicant_config_get_ap_scan (NMSupplicantConfig *self); @@ -65,6 +65,7 @@ gboolean nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig const char *con_uuid, guint32 mtu, NMSettingWirelessSecurityPmf pmf, + NMSettingWirelessSecurityFils fils, GError **error); gboolean nm_supplicant_config_add_no_security (NMSupplicantConfig *self, diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c index 44f887cb..3511b151 100644 --- a/src/supplicant/nm-supplicant-interface.c +++ b/src/supplicant/nm-supplicant-interface.c @@ -93,6 +93,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMSupplicantInterface, PROP_FAST_SUPPORT, PROP_AP_SUPPORT, PROP_PMF_SUPPORT, + PROP_FILS_SUPPORT, ); typedef struct { @@ -102,6 +103,7 @@ typedef struct { NMSupplicantFeature fast_support; NMSupplicantFeature ap_support; /* Lightweight AP mode support */ NMSupplicantFeature pmf_support; + NMSupplicantFeature fils_support; guint32 max_scan_ssids; guint32 ready_count; @@ -565,6 +567,12 @@ nm_supplicant_interface_get_pmf_support (NMSupplicantInterface *self) return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->pmf_support; } +NMSupplicantFeature +nm_supplicant_interface_get_fils_support (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->fils_support; +} + void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, NMSupplicantFeature ap_support) @@ -596,6 +604,15 @@ nm_supplicant_interface_set_pmf_support (NMSupplicantInterface *self, priv->pmf_support = pmf_support; } +void +nm_supplicant_interface_set_fils_support (NMSupplicantInterface *self, + NMSupplicantFeature fils_support) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + priv->fils_support = fils_support; +} + /*****************************************************************************/ static void @@ -1057,9 +1074,8 @@ props_changed_cb (GDBusProxy *proxy, } if (g_variant_lookup (changed_properties, "CurrentBSS", "&o", &s)) { - if (strcmp (s, "/") == 0) - s = NULL; - if (g_strcmp0 (s, priv->current_bss) != 0) { + s = nm_utils_dbus_normalize_object_path (s); + if (!nm_streq0 (s, priv->current_bss)) { g_free (priv->current_bss); priv->current_bss = g_strdup (s); _notify (self, PROP_CURRENT_BSS); @@ -1198,7 +1214,6 @@ static void interface_get_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { NMSupplicantInterface *self; - NMSupplicantInterfacePrivate *priv; gs_unref_variant GVariant *variant = NULL; gs_free_error GError *error = NULL; const char *path; @@ -1210,7 +1225,6 @@ interface_get_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) return; self = NM_SUPPLICANT_INTERFACE (user_data); - priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); if (variant) { g_variant_get (variant, "(&o)", &path); @@ -1900,6 +1914,10 @@ set_property (GObject *object, /* construct-only */ priv->pmf_support = g_value_get_int (value); break; + case PROP_FILS_SUPPORT: + /* construct-only */ + priv->fils_support = g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1920,7 +1938,8 @@ nm_supplicant_interface_new (const char *ifname, NMSupplicantDriver driver, NMSupplicantFeature fast_support, NMSupplicantFeature ap_support, - NMSupplicantFeature pmf_support) + NMSupplicantFeature pmf_support, + NMSupplicantFeature fils_support) { g_return_val_if_fail (ifname != NULL, NULL); @@ -1930,6 +1949,7 @@ nm_supplicant_interface_new (const char *ifname, NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, (int) fast_support, NM_SUPPLICANT_INTERFACE_AP_SUPPORT, (int) ap_support, NM_SUPPLICANT_INTERFACE_PMF_SUPPORT, (int) pmf_support, + NM_SUPPLICANT_INTERFACE_FILS_SUPPORT, (int) fils_support, NULL); } @@ -1964,7 +1984,7 @@ dispose (GObject *object) nm_clear_g_cancellable (&priv->other_cancellable); g_clear_object (&priv->wpas_proxy); - g_clear_pointer (&priv->bss_proxies, (GDestroyNotify) g_hash_table_destroy); + g_clear_pointer (&priv->bss_proxies, g_hash_table_destroy); g_clear_pointer (&priv->net_path, g_free); g_clear_pointer (&priv->dev, g_free); @@ -2029,6 +2049,14 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_FILS_SUPPORT] = + g_param_spec_int (NM_SUPPLICANT_INTERFACE_FILS_SUPPORT, "", "", + NM_SUPPLICANT_FEATURE_UNKNOWN, + NM_SUPPLICANT_FEATURE_YES, + NM_SUPPLICANT_FEATURE_UNKNOWN, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h index 567cf96f..f32ad8dd 100644 --- a/src/supplicant/nm-supplicant-interface.h +++ b/src/supplicant/nm-supplicant-interface.h @@ -61,6 +61,7 @@ typedef enum { #define NM_SUPPLICANT_INTERFACE_FAST_SUPPORT "fast-support" #define NM_SUPPLICANT_INTERFACE_AP_SUPPORT "ap-support" #define NM_SUPPLICANT_INTERFACE_PMF_SUPPORT "pmf-support" +#define NM_SUPPLICANT_INTERFACE_FILS_SUPPORT "fils-support" /* Signals */ #define NM_SUPPLICANT_INTERFACE_STATE "state" @@ -79,7 +80,8 @@ NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname, NMSupplicantDriver driver, NMSupplicantFeature fast_support, NMSupplicantFeature ap_support, - NMSupplicantFeature pmf_support); + NMSupplicantFeature pmf_support, + NMSupplicantFeature fils_support); void nm_supplicant_interface_set_supplicant_available (NMSupplicantInterface *self, gboolean available); @@ -123,6 +125,7 @@ gboolean nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self, NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self); NMSupplicantFeature nm_supplicant_interface_get_pmf_support (NMSupplicantInterface *self); +NMSupplicantFeature nm_supplicant_interface_get_fils_support (NMSupplicantInterface *self); void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, NMSupplicantFeature apmode); @@ -133,6 +136,9 @@ void nm_supplicant_interface_set_fast_support (NMSupplicantInterface *self, void nm_supplicant_interface_set_pmf_support (NMSupplicantInterface *self, NMSupplicantFeature pmf_support); +void nm_supplicant_interface_set_fils_support (NMSupplicantInterface *self, + NMSupplicantFeature fils_support); + void nm_supplicant_interface_enroll_wps (NMSupplicantInterface *self, const char *const type, const char *bssid, diff --git a/src/supplicant/nm-supplicant-manager.c b/src/supplicant/nm-supplicant-manager.c index 0f2eb63a..5ab96f88 100644 --- a/src/supplicant/nm-supplicant-manager.c +++ b/src/supplicant/nm-supplicant-manager.c @@ -40,6 +40,7 @@ typedef struct { NMSupplicantFeature fast_support; NMSupplicantFeature ap_support; NMSupplicantFeature pmf_support; + NMSupplicantFeature fils_support; guint die_count_reset_id; guint die_count; } NMSupplicantManagerPrivate; @@ -161,7 +162,8 @@ nm_supplicant_manager_create_interface (NMSupplicantManager *self, driver, priv->fast_support, priv->ap_support, - priv->pmf_support); + priv->pmf_support, + priv->fils_support); priv->ifaces = g_slist_prepend (priv->ifaces, iface); g_object_add_toggle_ref ((GObject *) iface, _sup_iface_last_ref, self); @@ -196,6 +198,7 @@ update_capabilities (NMSupplicantManager *self) */ priv->ap_support = NM_SUPPLICANT_FEATURE_UNKNOWN; priv->pmf_support = NM_SUPPLICANT_FEATURE_UNKNOWN; + priv->fils_support = NM_SUPPLICANT_FEATURE_UNKNOWN; value = g_dbus_proxy_get_cached_property (priv->proxy, "Capabilities"); if (value) { @@ -203,21 +206,25 @@ update_capabilities (NMSupplicantManager *self) array = g_variant_get_strv (value, NULL); priv->ap_support = NM_SUPPLICANT_FEATURE_NO; priv->pmf_support = NM_SUPPLICANT_FEATURE_NO; + priv->fils_support = NM_SUPPLICANT_FEATURE_NO; if (array) { if (g_strv_contains (array, "ap")) priv->ap_support = NM_SUPPLICANT_FEATURE_YES; if (g_strv_contains (array, "pmf")) priv->pmf_support = NM_SUPPLICANT_FEATURE_YES; + if (g_strv_contains (array, "fils")) + priv->fils_support = NM_SUPPLICANT_FEATURE_YES; g_free (array); } } g_variant_unref (value); } - /* Tell all interfaces about results of the AP/PMF check */ + /* Tell all interfaces about results of the AP/PMF/FILS check */ for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) { nm_supplicant_interface_set_ap_support (ifaces->data, priv->ap_support); nm_supplicant_interface_set_pmf_support (ifaces->data, priv->pmf_support); + nm_supplicant_interface_set_fils_support (ifaces->data, priv->fils_support); } _LOGD ("AP mode is %ssupported", @@ -226,6 +233,9 @@ update_capabilities (NMSupplicantManager *self) _LOGD ("PMF is %ssupported", (priv->pmf_support == NM_SUPPLICANT_FEATURE_YES) ? "" : (priv->pmf_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); + _LOGD ("FILS is %ssupported", + (priv->fils_support == NM_SUPPLICANT_FEATURE_YES) ? "" : + (priv->fils_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); /* EAP-FAST */ priv->fast_support = NM_SUPPLICANT_FEATURE_NO; @@ -349,6 +359,7 @@ name_owner_cb (GDBusProxy *proxy, GParamSpec *pspec, gpointer user_data) priv->ap_support = NM_SUPPLICANT_FEATURE_UNKNOWN; priv->fast_support = NM_SUPPLICANT_FEATURE_UNKNOWN; priv->pmf_support = NM_SUPPLICANT_FEATURE_UNKNOWN; + priv->fils_support = NM_SUPPLICANT_FEATURE_UNKNOWN; set_running (self, FALSE); } diff --git a/src/supplicant/nm-supplicant-settings-verify.c b/src/supplicant/nm-supplicant-settings-verify.c index 14daf693..317afff9 100644 --- a/src/supplicant/nm-supplicant-settings-verify.c +++ b/src/supplicant/nm-supplicant-settings-verify.c @@ -36,7 +36,6 @@ struct Opt { const char ** str_allowed; }; - static gboolean validate_type_int (const struct Opt * opt, const char * value, const guint32 len); @@ -67,12 +66,12 @@ static const struct validate_entry validate_table[] = { { TYPE_KEYWORD, validate_type_keyword }, }; - const char * pairwise_allowed[] = { "CCMP", "TKIP", "NONE", NULL }; const char * group_allowed[] = { "CCMP", "TKIP", "WEP104", "WEP40", NULL }; const char * proto_allowed[] = { "WPA", "RSN", NULL }; const char * key_mgmt_allowed[] = { "WPA-PSK", "WPA-PSK-SHA256", "WPA-EAP", "WPA-EAP-SHA256", + "FILS-SHA256", "FILS-SHA384", "IEEE8021X", "WPA-NONE", "NONE", NULL }; const char * auth_alg_allowed[] = { "OPEN", "SHARED", "LEAP", NULL }; @@ -154,7 +153,6 @@ static const struct Opt opt_table[] = { { "ieee80211w", TYPE_INT, 0, 2, FALSE, NULL }, }; - static gboolean validate_type_int (const struct Opt * opt, const char * value, @@ -189,7 +187,7 @@ validate_type_bytes (const struct Opt * opt, g_return_val_if_fail (opt != NULL, FALSE); g_return_val_if_fail (value != NULL, FALSE); - check_len = opt->int_high ? opt->int_high : 255; + check_len = opt->int_high ?: 255; if (len > check_len) return FALSE; @@ -206,7 +204,7 @@ validate_type_utf8 (const struct Opt *opt, g_return_val_if_fail (opt != NULL, FALSE); g_return_val_if_fail (value != NULL, FALSE); - check_len = opt->int_high ? opt->int_high : 255; + check_len = opt->int_high ?: 255; /* Note that we deliberately don't validate the UTF-8, because some "UTF-8" fields, such as 8021x.password, do not actually have to be valid UTF-8 */ @@ -221,10 +219,10 @@ validate_type_keyword (const struct Opt * opt, const char * value, const guint32 len) { - char ** allowed; - gchar ** candidates = NULL; - char ** candidate; - gboolean found = FALSE; + char **allowed; + gchar **candidates = NULL; + char **candidate; + gboolean found = FALSE; g_return_val_if_fail (opt != NULL, FALSE); g_return_val_if_fail (value != NULL, FALSE); diff --git a/src/supplicant/nm-supplicant-settings-verify.h b/src/supplicant/nm-supplicant-settings-verify.h index 920343ba..1eec136d 100644 --- a/src/supplicant/nm-supplicant-settings-verify.h +++ b/src/supplicant/nm-supplicant-settings-verify.h @@ -34,5 +34,4 @@ OptType nm_supplicant_settings_verify_setting (const char * key, const char * value, const guint32 len); - #endif /* __NETWORKMANAGER_SUPPLICANT_SETTINGS_VERIFY_H__ */ diff --git a/src/supplicant/nm-supplicant-types.h b/src/supplicant/nm-supplicant-types.h index f75827ec..747cf152 100644 --- a/src/supplicant/nm-supplicant-types.h +++ b/src/supplicant/nm-supplicant-types.h @@ -21,9 +21,9 @@ #ifndef __NETWORKMANAGER_SUPPLICANT_TYPES_H__ #define __NETWORKMANAGER_SUPPLICANT_TYPES_H__ -#define WPAS_DBUS_SERVICE "fi.w1.wpa_supplicant1" -#define WPAS_DBUS_PATH "/fi/w1/wpa_supplicant1" -#define WPAS_DBUS_INTERFACE "fi.w1.wpa_supplicant1" +#define WPAS_DBUS_SERVICE "fi.w1.wpa_supplicant1" +#define WPAS_DBUS_PATH "/fi/w1/wpa_supplicant1" +#define WPAS_DBUS_INTERFACE "fi.w1.wpa_supplicant1" typedef struct _NMSupplicantManager NMSupplicantManager; typedef struct _NMSupplicantInterface NMSupplicantInterface; diff --git a/src/supplicant/tests/meson.build b/src/supplicant/tests/meson.build new file mode 100644 index 00000000..b8bad7f3 --- /dev/null +++ b/src/supplicant/tests/meson.build @@ -0,0 +1,14 @@ +test_unit = 'test-supplicant-config' + +exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, + c_args: nm_build_cflags, +) + +test( + 'supplicant/' + test_unit, + test_script, + args: test_args + [exe.full_path()] +) diff --git a/src/supplicant/tests/test-supplicant-config.c b/src/supplicant/tests/test-supplicant-config.c index 4b4a4935..36831e67 100644 --- a/src/supplicant/tests/test-supplicant-config.c +++ b/src/supplicant/tests/test-supplicant-config.c @@ -37,6 +37,10 @@ #include "nm-test-utils-core.h" +#define TEST_CERT_DIR NM_BUILD_SRCDIR"/src/supplicant/tests/certs" + +/*****************************************************************************/ + static gboolean validate_opt (const char *detail, GVariant *config, @@ -95,7 +99,11 @@ validate_opt (const char *detail, } static GVariant * -build_supplicant_config (NMConnection *connection, guint mtu, guint fixed_freq) +build_supplicant_config (NMConnection *connection, + guint mtu, + guint fixed_freq, + gboolean support_pmf, + gboolean support_fils) { gs_unref_object NMSupplicantConfig *config = NULL; gs_free_error GError *error = NULL; @@ -104,7 +112,7 @@ build_supplicant_config (NMConnection *connection, guint mtu, guint fixed_freq) NMSetting8021x *s_8021x; gboolean success; - config = nm_supplicant_config_new (); + config = nm_supplicant_config_new (support_pmf, support_fils); s_wifi = nm_connection_get_setting_wireless (connection); g_assert (s_wifi); @@ -118,6 +126,7 @@ build_supplicant_config (NMConnection *connection, guint mtu, guint fixed_freq) s_wsec = nm_connection_get_setting_wireless_security (connection); if (s_wsec) { NMSettingWirelessSecurityPmf pmf = nm_setting_wireless_security_get_pmf (s_wsec); + NMSettingWirelessSecurityFils fils = nm_setting_wireless_security_get_fils (s_wsec); s_8021x = nm_connection_get_setting_802_1x (connection); success = nm_supplicant_config_add_setting_wireless_security (config, s_wsec, @@ -125,6 +134,7 @@ build_supplicant_config (NMConnection *connection, guint mtu, guint fixed_freq) nm_connection_get_uuid (connection), mtu, pmf, + fils, &error); } else { success = nm_supplicant_config_add_no_security (config, &error); @@ -132,7 +142,6 @@ build_supplicant_config (NMConnection *connection, guint mtu, guint fixed_freq) g_assert_no_error (error); g_assert (success); - success = nm_supplicant_config_add_bgscan (config, connection, &error); g_assert_no_error (error); g_assert (success); @@ -140,8 +149,6 @@ build_supplicant_config (NMConnection *connection, guint mtu, guint fixed_freq) return nm_supplicant_config_to_variant (config); } -#define EXPECT(msg) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, msg) - static NMConnection * new_basic_connection (const char *id, GBytes *ssid, @@ -199,12 +206,12 @@ test_wifi_open (void) g_assert_no_error (error); g_assert (success); - EXPECT ("*added 'ssid' value 'Test SSID'*"); - EXPECT ("*added 'scan_ssid' value '1'*"); - EXPECT ("*added 'bssid' value '11:22:33:44:55:66'*"); - EXPECT ("*added 'freq_list' value *"); - EXPECT ("*added 'key_mgmt' value 'NONE'"); - config_dict = build_supplicant_config (connection, 1500, 0); + NMTST_EXPECT_NM_INFO ("Config: added 'ssid' value 'Test SSID'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'scan_ssid' value '1'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'bssid' value '11:22:33:44:55:66'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'freq_list' value *"); + NMTST_EXPECT_NM_INFO ("Config: added 'key_mgmt' value 'NONE'"); + config_dict = build_supplicant_config (connection, 1500, 0, TRUE, TRUE); g_test_assert_expected_messages (); g_assert (config_dict); @@ -249,19 +256,19 @@ test_wifi_wep_key (const char *detail, g_assert_no_error (error); g_assert (success); - EXPECT ("*added 'ssid' value 'Test SSID'*"); - EXPECT ("*added 'scan_ssid' value '1'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'ssid' value 'Test SSID'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'scan_ssid' value '1'*"); if (test_bssid) - EXPECT ("*added 'bssid' value '11:22:33:44:55:66'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'bssid' value '11:22:33:44:55:66'*"); - EXPECT ("*added 'freq_list' value *"); - EXPECT ("*added 'key_mgmt' value 'NONE'"); - EXPECT ("*added 'wep_key0' value *"); - EXPECT ("*added 'wep_tx_keyidx' value '0'"); + NMTST_EXPECT_NM_INFO ("Config: added 'freq_list' value *"); + NMTST_EXPECT_NM_INFO ("Config: added 'key_mgmt' value 'NONE'"); + NMTST_EXPECT_NM_INFO ("Config: added 'wep_key0' value *"); + NMTST_EXPECT_NM_INFO ("Config: added 'wep_tx_keyidx' value '0'"); if (!test_bssid) - EXPECT ("*added 'bgscan' value 'simple:30:-80:86400'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'bgscan' value 'simple:30:-80:86400'*"); - config_dict = build_supplicant_config (connection, 1500, 0); + config_dict = build_supplicant_config (connection, 1500, 0, TRUE, TRUE); g_test_assert_expected_messages (); g_assert (config_dict); @@ -308,7 +315,8 @@ test_wifi_wpa_psk (const char *detail, OptType key_type, const char *key_data, const unsigned char *expected, - size_t expected_size) + size_t expected_size, + NMSettingWirelessSecurityPmf pmf) { gs_unref_object NMConnection *connection = NULL; gs_unref_variant GVariant *config_dict = NULL; @@ -328,7 +336,7 @@ test_wifi_wpa_psk (const char *detail, g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", NM_SETTING_WIRELESS_SECURITY_PSK, key_data, - NM_SETTING_WIRELESS_SECURITY_PMF, (int) NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL, + NM_SETTING_WIRELESS_SECURITY_PMF, (int) pmf, NULL); nm_setting_wireless_security_add_proto (s_wsec, "wpa"); nm_setting_wireless_security_add_proto (s_wsec, "rsn"); @@ -341,17 +349,26 @@ test_wifi_wpa_psk (const char *detail, g_assert_no_error (error); g_assert (success); - EXPECT ("*added 'ssid' value 'Test SSID'*"); - EXPECT ("*added 'scan_ssid' value '1'*"); - EXPECT ("*added 'bssid' value '11:22:33:44:55:66'*"); - EXPECT ("*added 'freq_list' value *"); - EXPECT ("*added 'key_mgmt' value 'WPA-PSK WPA-PSK-SHA256'"); - EXPECT ("*added 'psk' value *"); - EXPECT ("*added 'proto' value 'WPA RSN'"); - EXPECT ("*added 'pairwise' value 'TKIP CCMP'"); - EXPECT ("*added 'group' value 'TKIP CCMP'"); - EXPECT ("*added 'ieee80211w' value '1'"); - config_dict = build_supplicant_config (connection, 1500, 0); + NMTST_EXPECT_NM_INFO ("Config: added 'ssid' value 'Test SSID'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'scan_ssid' value '1'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'bssid' value '11:22:33:44:55:66'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'freq_list' value *"); + NMTST_EXPECT_NM_INFO ("Config: added 'key_mgmt' value 'WPA-PSK WPA-PSK-SHA256'"); + NMTST_EXPECT_NM_INFO ("Config: added 'psk' value *"); + NMTST_EXPECT_NM_INFO ("Config: added 'proto' value 'WPA RSN'"); + NMTST_EXPECT_NM_INFO ("Config: added 'pairwise' value 'TKIP CCMP'"); + NMTST_EXPECT_NM_INFO ("Config: added 'group' value 'TKIP CCMP'"); + switch (pmf) { + case NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL: + NMTST_EXPECT_NM_INFO ("Config: added 'ieee80211w' value '1'"); + break; + case NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED: + NMTST_EXPECT_NM_INFO ("Config: added 'ieee80211w' value '2'"); + break; + default: + break; + } + config_dict = build_supplicant_config (connection, 1500, 0, TRUE, TRUE); g_test_assert_expected_messages (); g_assert (config_dict); @@ -381,12 +398,16 @@ test_wifi_wpa_psk_types (void) 0x6c, 0x2f, 0x11, 0x60, 0x5a, 0x16, 0x08, 0x93 }; const char *key2 = "r34lly l33t wp4 p4ssphr4s3 for t3st1ng"; - test_wifi_wpa_psk ("wifi-wpa-psk-hex", TYPE_BYTES, key1, key1_expected, sizeof (key1_expected)); - test_wifi_wpa_psk ("wifi-wep-psk-passphrase", TYPE_STRING, key2, (gconstpointer) key2, strlen (key2)); + test_wifi_wpa_psk ("wifi-wpa-psk-hex", TYPE_BYTES, key1, key1_expected, + sizeof (key1_expected), NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL); + test_wifi_wpa_psk ("wifi-wep-psk-passphrase", TYPE_STRING, key2, + (gconstpointer) key2, strlen (key2), NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED); + test_wifi_wpa_psk ("pmf-disabled", TYPE_STRING, key2, + (gconstpointer) key2, strlen (key2), NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE); } static NMConnection * -generate_wifi_eap_connection (const char *id, GBytes *ssid, const char *bssid_str) +generate_wifi_eap_connection (const char *id, GBytes *ssid, const char *bssid_str, NMSettingWirelessSecurityFils fils) { NMConnection *connection = NULL; NMSettingWirelessSecurity *s_wsec; @@ -401,6 +422,7 @@ generate_wifi_eap_connection (const char *id, GBytes *ssid, const char *bssid_st nm_connection_add_setting (connection, NM_SETTING (s_wsec)); g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", + NM_SETTING_WIRELESS_SECURITY_FILS, (int) fils, NULL); nm_setting_wireless_security_add_proto (s_wsec, "wpa"); nm_setting_wireless_security_add_proto (s_wsec, "rsn"); @@ -434,22 +456,22 @@ test_wifi_eap_locked_bssid (void) const char *bssid_str = "11:22:33:44:55:66"; guint32 mtu = 1100; - connection = generate_wifi_eap_connection ("Test Wifi EAP-TLS Locked", ssid, bssid_str); - - EXPECT ("*added 'ssid' value 'Test SSID'*"); - EXPECT ("*added 'scan_ssid' value '1'*"); - EXPECT ("*added 'bssid' value '11:22:33:44:55:66'*"); - EXPECT ("*added 'freq_list' value *"); - EXPECT ("*added 'key_mgmt' value 'WPA-EAP'"); - EXPECT ("*added 'proto' value 'WPA RSN'"); - EXPECT ("*added 'pairwise' value 'TKIP CCMP'"); - EXPECT ("*added 'group' value 'TKIP CCMP'"); - EXPECT ("*Config: added 'eap' value 'TLS'"); - EXPECT ("*Config: added 'fragment_size' value '1086'"); - EXPECT ("* Config: added 'ca_cert' value '*/test-ca-cert.pem'"); - EXPECT ("* Config: added 'private_key' value '*/test-cert.p12'"); - EXPECT ("*Config: added 'proactive_key_caching' value '1'"); - config_dict = build_supplicant_config (connection, mtu, 0); + connection = generate_wifi_eap_connection ("Test Wifi EAP-TLS Locked", ssid, bssid_str, NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL); + + NMTST_EXPECT_NM_INFO ("Config: added 'ssid' value 'Test SSID'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'scan_ssid' value '1'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'bssid' value '11:22:33:44:55:66'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'freq_list' value *"); + NMTST_EXPECT_NM_INFO ("Config: added 'key_mgmt' value 'WPA-EAP'"); + NMTST_EXPECT_NM_INFO ("Config: added 'proto' value 'WPA RSN'"); + NMTST_EXPECT_NM_INFO ("Config: added 'pairwise' value 'TKIP CCMP'"); + NMTST_EXPECT_NM_INFO ("Config: added 'group' value 'TKIP CCMP'"); + NMTST_EXPECT_NM_INFO ("Config: added 'eap' value 'TLS'"); + NMTST_EXPECT_NM_INFO ("Config: added 'fragment_size' value '1086'"); + NMTST_EXPECT_NM_INFO ("Config: added 'ca_cert' value '*/test-ca-cert.pem'"); + NMTST_EXPECT_NM_INFO ("Config: added 'private_key' value '*/test-cert.p12'"); + NMTST_EXPECT_NM_INFO ("Config: added 'proactive_key_caching' value '1'"); + config_dict = build_supplicant_config (connection, mtu, 0, FALSE, FALSE); g_test_assert_expected_messages (); g_assert (config_dict); @@ -475,28 +497,69 @@ test_wifi_eap_unlocked_bssid (void) gs_unref_bytes GBytes *bgscan = g_bytes_new (bgscan_data, strlen (bgscan_data)); guint32 mtu = 1100; - connection = generate_wifi_eap_connection ("Test Wifi EAP-TLS Unlocked", ssid, NULL); - - EXPECT ("*added 'ssid' value 'Test SSID'*"); - EXPECT ("*added 'scan_ssid' value '1'*"); - EXPECT ("*added 'freq_list' value *"); - EXPECT ("*added 'key_mgmt' value 'WPA-EAP'"); - EXPECT ("*added 'proto' value 'WPA RSN'"); - EXPECT ("*added 'pairwise' value 'TKIP CCMP'"); - EXPECT ("*added 'group' value 'TKIP CCMP'"); - EXPECT ("*Config: added 'eap' value 'TLS'"); - EXPECT ("*Config: added 'fragment_size' value '1086'"); - EXPECT ("* Config: added 'ca_cert' value '*/test-ca-cert.pem'"); - EXPECT ("* Config: added 'private_key' value '*/test-cert.p12'"); - EXPECT ("*Config: added 'proactive_key_caching' value '1'"); - EXPECT ("*Config: added 'bgscan' value 'simple:30:-65:300'"); - config_dict = build_supplicant_config (connection, mtu, 0); + connection = generate_wifi_eap_connection ("Test Wifi EAP-TLS Unlocked", ssid, NULL, NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED); + + NMTST_EXPECT_NM_INFO ("Config: added 'ssid' value 'Test SSID'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'scan_ssid' value '1'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'freq_list' value *"); + NMTST_EXPECT_NM_INFO ("Config: added 'key_mgmt' value 'FILS-SHA256 FILS-SHA384'"); + NMTST_EXPECT_NM_INFO ("Config: added 'proto' value 'WPA RSN'"); + NMTST_EXPECT_NM_INFO ("Config: added 'pairwise' value 'TKIP CCMP'"); + NMTST_EXPECT_NM_INFO ("Config: added 'group' value 'TKIP CCMP'"); + NMTST_EXPECT_NM_INFO ("Config: added 'eap' value 'TLS'"); + NMTST_EXPECT_NM_INFO ("Config: added 'fragment_size' value '1086'"); + NMTST_EXPECT_NM_INFO ("Config: added 'ca_cert' value '*/test-ca-cert.pem'"); + NMTST_EXPECT_NM_INFO ("Config: added 'private_key' value '*/test-cert.p12'"); + NMTST_EXPECT_NM_INFO ("Config: added 'proactive_key_caching' value '1'"); + NMTST_EXPECT_NM_INFO ("Config: added 'bgscan' value 'simple:30:-65:300'"); + config_dict = build_supplicant_config (connection, mtu, 0, FALSE, TRUE); g_test_assert_expected_messages (); g_assert (config_dict); validate_opt ("wifi-eap", config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1)); validate_opt ("wifi-eap", config_dict, "ssid", TYPE_BYTES, ssid); - validate_opt ("wifi-eap", config_dict, "key_mgmt", TYPE_KEYWORD, "WPA-EAP"); + validate_opt ("wifi-eap", config_dict, "key_mgmt", TYPE_KEYWORD, "FILS-SHA256 FILS-SHA384"); + validate_opt ("wifi-eap", config_dict, "eap", TYPE_KEYWORD, "TLS"); + validate_opt ("wifi-eap", config_dict, "proto", TYPE_KEYWORD, "WPA RSN"); + validate_opt ("wifi-eap", config_dict, "pairwise", TYPE_KEYWORD, "TKIP CCMP"); + validate_opt ("wifi-eap", config_dict, "group", TYPE_KEYWORD, "TKIP CCMP"); + validate_opt ("wifi-eap", config_dict, "fragment_size", TYPE_INT, GINT_TO_POINTER(mtu-14)); + validate_opt ("wifi-eap", config_dict, "bgscan", TYPE_BYTES, bgscan); +} + +static void +test_wifi_eap_fils_disabled (void) +{ + gs_unref_object NMConnection *connection = NULL; + gs_unref_variant GVariant *config_dict = NULL; + const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 }; + gs_unref_bytes GBytes *ssid = g_bytes_new (ssid_data, sizeof (ssid_data)); + const char *bgscan_data = "simple:30:-65:300"; + gs_unref_bytes GBytes *bgscan = g_bytes_new (bgscan_data, strlen (bgscan_data)); + guint32 mtu = 1100; + + connection = generate_wifi_eap_connection ("Test Wifi FILS disabled", ssid, NULL, NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE); + + NMTST_EXPECT_NM_INFO ("Config: added 'ssid' value 'Test SSID'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'scan_ssid' value '1'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'freq_list' value *"); + NMTST_EXPECT_NM_INFO ("Config: added 'key_mgmt' value 'WPA-EAP WPA-EAP-SHA256'"); + NMTST_EXPECT_NM_INFO ("Config: added 'proto' value 'WPA RSN'"); + NMTST_EXPECT_NM_INFO ("Config: added 'pairwise' value 'TKIP CCMP'"); + NMTST_EXPECT_NM_INFO ("Config: added 'group' value 'TKIP CCMP'"); + NMTST_EXPECT_NM_INFO ("Config: added 'eap' value 'TLS'"); + NMTST_EXPECT_NM_INFO ("Config: added 'fragment_size' value '1086'"); + NMTST_EXPECT_NM_INFO ("Config: added 'ca_cert' value '*/test-ca-cert.pem'"); + NMTST_EXPECT_NM_INFO ("Config: added 'private_key' value '*/test-cert.p12'"); + NMTST_EXPECT_NM_INFO ("Config: added 'proactive_key_caching' value '1'"); + NMTST_EXPECT_NM_INFO ("Config: added 'bgscan' value 'simple:30:-65:300'"); + config_dict = build_supplicant_config (connection, mtu, 0, TRUE, TRUE); + g_test_assert_expected_messages (); + g_assert (config_dict); + + validate_opt ("wifi-eap", config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1)); + validate_opt ("wifi-eap", config_dict, "ssid", TYPE_BYTES, ssid); + validate_opt ("wifi-eap", config_dict, "key_mgmt", TYPE_KEYWORD, "WPA-EAP WPA-EAP-SHA256"); validate_opt ("wifi-eap", config_dict, "eap", TYPE_KEYWORD, "TLS"); validate_opt ("wifi-eap", config_dict, "proto", TYPE_KEYWORD, "WPA RSN"); validate_opt ("wifi-eap", config_dict, "pairwise", TYPE_KEYWORD, "TKIP CCMP"); @@ -516,6 +579,7 @@ int main (int argc, char **argv) g_test_add_func ("/supplicant-config/wifi-wpa-psk-types", test_wifi_wpa_psk_types); g_test_add_func ("/supplicant-config/wifi-eap/locked-bssid", test_wifi_eap_locked_bssid); g_test_add_func ("/supplicant-config/wifi-eap/unlocked-bssid", test_wifi_eap_unlocked_bssid); + g_test_add_func ("/supplicant-config/wifi-eap/fils-disabled", test_wifi_eap_fils_disabled); return g_test_run (); } diff --git a/src/systemd/meson.build b/src/systemd/meson.build new file mode 100644 index 00000000..0c7d0aec --- /dev/null +++ b/src/systemd/meson.build @@ -0,0 +1,71 @@ +sources = files( + 'sd-adapt/nm-sd-adapt.c', + 'src/basic/alloc-util.c', + 'src/basic/escape.c', + 'src/basic/ether-addr-util.c', + 'src/basic/extract-word.c', + 'src/basic/fd-util.c', + 'src/basic/fileio.c', + 'src/basic/fs-util.c', + 'src/basic/hash-funcs.c', + 'src/basic/hashmap.c', + 'src/basic/hexdecoct.c', + 'src/basic/hostname-util.c', + 'src/basic/in-addr-util.c', + 'src/basic/io-util.c', + 'src/basic/mempool.c', + 'src/basic/parse-util.c', + 'src/basic/path-util.c', + 'src/basic/prioq.c', + 'src/basic/process-util.c', + 'src/basic/random-util.c', + 'src/basic/socket-util.c', + 'src/basic/stat-util.c', + 'src/basic/string-table.c', + 'src/basic/string-util.c', + 'src/basic/strv.c', + 'src/basic/time-util.c', + 'src/basic/utf8.c', + 'src/basic/util.c', + 'src/libsystemd-network/arp-util.c', + 'src/libsystemd-network/dhcp-identifier.c', + 'src/libsystemd-network/dhcp-network.c', + 'src/libsystemd-network/dhcp-option.c', + 'src/libsystemd-network/dhcp-packet.c', + 'src/libsystemd-network/dhcp6-network.c', + 'src/libsystemd-network/dhcp6-option.c', + 'src/libsystemd-network/lldp-neighbor.c', + 'src/libsystemd-network/lldp-network.c', + 'src/libsystemd-network/network-internal.c', + 'src/libsystemd-network/sd-dhcp-client.c', + '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.c', + 'src/libsystemd/sd-event/sd-event.c', + 'src/libsystemd/sd-id128/id128-util.c', + 'src/libsystemd/sd-id128/sd-id128.c', + 'src/shared/dns-domain.c', + 'nm-sd.c' +) + +incs = [ + src_inc, + include_directories( + 'sd-adapt', + 'src/basic', + 'src/libsystemd-network', + 'src/shared', + 'src/systemd' + ) +] + +libsystemd_nm = static_library( + 'systemd-nm', + sources: sources, + include_directories: incs, + dependencies: nm_core_dep, + c_args: '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD' +) diff --git a/src/systemd/nm-sd.c b/src/systemd/nm-sd.c index 43ae0a8b..e05b345b 100644 --- a/src/systemd/nm-sd.c +++ b/src/systemd/nm-sd.c @@ -116,7 +116,6 @@ event_attach (sd_event *event, GMainContext *context) id = g_source_attach ((GSource *) source, context); g_source_unref ((GSource *) source); - if (!event) { *p_default_source_id = id; sd_event_unref (e); diff --git a/src/systemd/sd-adapt/device-nodes.h b/src/systemd/sd-adapt/device-nodes.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/src/systemd/sd-adapt/device-nodes.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/sd-adapt/errno-list.h b/src/systemd/sd-adapt/errno-list.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/src/systemd/sd-adapt/errno-list.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/sd-adapt/locale-util.h b/src/systemd/sd-adapt/locale-util.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/src/systemd/sd-adapt/locale-util.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/sd-adapt/memfd-util.h b/src/systemd/sd-adapt/memfd-util.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/src/systemd/sd-adapt/memfd-util.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/sd-adapt/nm-sd-adapt.h b/src/systemd/sd-adapt/nm-sd-adapt.h index 0d291e26..380cbc60 100644 --- a/src/systemd/sd-adapt/nm-sd-adapt.h +++ b/src/systemd/sd-adapt/nm-sd-adapt.h @@ -26,12 +26,16 @@ #include #include -#define noreturn G_GNUC_NORETURN - #ifndef CLOCK_BOOTTIME #define CLOCK_BOOTTIME 7 #endif +#if defined(HAVE_DECL_REALLOCARRAY) && HAVE_DECL_REALLOCARRAY == 1 +#define HAVE_REALLOCARRAY 1 +#else +#define HAVE_REALLOCARRAY 0 +#endif + #if defined(HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO == 1 #define HAVE_EXPLICIT_BZERO 1 #else @@ -105,7 +109,7 @@ G_STMT_START { \ * itself. *****************************************************************************/ -#if (NETWORKMANAGER_COMPILATION) == NM_NETWORKMANAGER_COMPILATION_SYSTEMD +#if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD #include #include @@ -138,6 +142,17 @@ G_STMT_START { \ # endif #endif +#define VALGRIND 0 + +static inline pid_t +raw_getpid (void) { +#if defined(__alpha__) + return (pid_t) syscall (__NR_getxpid); +#else + return (pid_t) syscall (__NR_getpid); +#endif +} + /*****************************************************************************/ /* work around missing uchar.h */ @@ -146,8 +161,6 @@ typedef guint32 char32_t; /*****************************************************************************/ -#define PID_TO_PTR(p) ((void*) ((uintptr_t) p)) - static inline int sd_notify (int unset_environment, const char *state) { @@ -167,7 +180,6 @@ sd_notify (int unset_environment, const char *state) #define MAX_HANDLE_SZ 128 #endif - /* * Some toolchains (E.G. uClibc 0.9.33 and earlier) don't export * CLOCK_BOOTTIME even though the kernel supports it, so provide a @@ -186,7 +198,7 @@ static inline pid_t gettid(void) { return (pid_t) syscall(SYS_gettid); } -#endif /* (NETWORKMANAGER_COMPILATION) == NM_NETWORKMANAGER_COMPILATION_SYSTEMD */ +#endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */ #endif /* NM_SD_ADAPT_H */ diff --git a/src/systemd/sd-adapt/procfs-util.h b/src/systemd/sd-adapt/procfs-util.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/src/systemd/sd-adapt/procfs-util.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/sd-adapt/terminal-util.h b/src/systemd/sd-adapt/terminal-util.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/src/systemd/sd-adapt/terminal-util.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/sd-adapt/unaligned.h b/src/systemd/sd-adapt/unaligned.h new file mode 100644 index 00000000..17dc0444 --- /dev/null +++ b/src/systemd/sd-adapt/unaligned.h @@ -0,0 +1,3 @@ +#pragma once + +#include "nm-utils/unaligned.h" diff --git a/src/systemd/src/basic/alloc-util.c b/src/systemd/src/basic/alloc-util.c index 97588312..79b823de 100644 --- a/src/systemd/src/basic/alloc-util.c +++ b/src/systemd/src/basic/alloc-util.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -39,7 +27,7 @@ void* memdup(const void *p, size_t l) { return ret; } -void* memdup_suffix0(const void*p, size_t l) { +void* memdup_suffix0(const void *p, size_t l) { void *ret; assert(l == 0 || p); diff --git a/src/systemd/src/basic/alloc-util.h b/src/systemd/src/basic/alloc-util.h index 0a89691b..bae6a284 100644 --- a/src/systemd/src/basic/alloc-util.h +++ b/src/systemd/src/basic/alloc-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 @@ -30,9 +18,17 @@ #define new0(t, n) ((t*) calloc((n), sizeof(t))) -#define newa(t, n) ((t*) alloca(sizeof(t)*(n))) +#define newa(t, n) \ + ({ \ + assert(!size_multiply_overflow(sizeof(t), n)); \ + (t*) alloca(sizeof(t)*(n)); \ + }) -#define newa0(t, n) ((t*) alloca0(sizeof(t)*(n))) +#define newa0(t, n) \ + ({ \ + assert(!size_multiply_overflow(sizeof(t), n)); \ + (t*) alloca0(sizeof(t)*(n)); \ + }) #define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) @@ -54,7 +50,7 @@ static inline void *mfree(void *memory) { }) void* memdup(const void *p, size_t l) _alloc_(2); -void* memdup_suffix0(const void*p, size_t l) _alloc_(2); +void* memdup_suffix0(const void *p, size_t l) _alloc_(2); static inline void freep(void *p) { free(*(void**) p); @@ -73,12 +69,14 @@ _malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t return malloc(size * need); } -_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t size, size_t need) { +#if !HAVE_REALLOCARRAY +_alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size) { if (size_multiply_overflow(size, need)) return NULL; return realloc(p, size * need); } +#endif _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) { if (size_multiply_overflow(size, need)) @@ -127,3 +125,12 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); _new_ = alloca_align(_size_, (align)); \ (void*)memset(_new_, 0, _size_); \ }) + +/* Takes inspiration from Rusts's Option::take() method: reads and returns a pointer, but at the same time resets it to + * NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */ +#define TAKE_PTR(ptr) \ + ({ \ + typeof(ptr) _ptr_ = (ptr); \ + (ptr) = NULL; \ + _ptr_; \ + }) diff --git a/src/systemd/src/basic/async.h b/src/systemd/src/basic/async.h index 9bd13ff6..36f33377 100644 --- a/src/systemd/src/basic/async.h +++ b/src/systemd/src/basic/async.h @@ -1,25 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2013 Lennart Poettering - - 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 . ***/ int asynchronous_job(void* (*func)(void *p), void *arg); -int asynchronous_sync(void); +int asynchronous_sync(pid_t *ret_pid); int asynchronous_close(int fd); diff --git a/src/systemd/src/basic/escape.c b/src/systemd/src/basic/escape.c index 27a20702..f51de788 100644 --- a/src/systemd/src/basic/escape.c +++ b/src/systemd/src/basic/escape.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -202,7 +190,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) /* C++11 style 16bit unicode */ int a[4]; - unsigned i; + size_t i; uint32_t c; if (length != (size_t) -1 && length < 5) @@ -229,7 +217,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) /* C++11 style 32bit unicode */ int a[8]; - unsigned i; + size_t i; char32_t c; if (length != (size_t) -1 && length < 9) diff --git a/src/systemd/src/basic/escape.h b/src/systemd/src/basic/escape.h index e62347af..80b0946e 100644 --- a/src/systemd/src/basic/escape.h +++ b/src/systemd/src/basic/escape.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 diff --git a/src/systemd/src/basic/ether-addr-util.c b/src/systemd/src/basic/ether-addr-util.c index a793219c..75de9a19 100644 --- a/src/systemd/src/basic/ether-addr-util.c +++ b/src/systemd/src/basic/ether-addr-util.c @@ -1,24 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2014 Tom Gundersen - - 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 "nm-sd-adapt.h" +#include #include #include #include @@ -46,19 +35,23 @@ char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR return buffer; } -bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) { +int ether_addr_compare(const void *a, const void *b) { assert(a); assert(b); - return a->ether_addr_octet[0] == b->ether_addr_octet[0] && - a->ether_addr_octet[1] == b->ether_addr_octet[1] && - a->ether_addr_octet[2] == b->ether_addr_octet[2] && - a->ether_addr_octet[3] == b->ether_addr_octet[3] && - a->ether_addr_octet[4] == b->ether_addr_octet[4] && - a->ether_addr_octet[5] == b->ether_addr_octet[5]; + return memcmp(a, b, ETH_ALEN); } -int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset) { +static void ether_addr_hash_func(const void *p, struct siphash *state) { + siphash24_compress(p, sizeof(struct ether_addr), state); +} + +const struct hash_ops ether_addr_hash_ops = { + .hash = ether_addr_hash_func, + .compare = ether_addr_compare +}; + +int ether_addr_from_string(const char *s, struct ether_addr *ret) { size_t pos = 0, n, field; char sep = '\0'; const char *hex = HEXDIGITS, *hexoff; @@ -72,7 +65,7 @@ int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset if (s[pos] == '\0') \ break; \ hexoff = strchr(hex, s[pos]); \ - if (hexoff == NULL) \ + if (!hexoff) \ break; \ assert(hexoff >= hex); \ x = hexoff - hex; \ @@ -97,31 +90,35 @@ int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset assert(s); assert(ret); + s += strspn(s, WHITESPACE); sep = s[strspn(s, hex)]; - if (sep == '\n') - return -EINVAL; - if (strchr(":.-", sep) == NULL) - return -EINVAL; if (sep == '.') { uint16_t shorts[3] = { 0 }; parse_fields(shorts); + if (s[pos] != '\0') + return -EINVAL; + for (n = 0; n < ELEMENTSOF(shorts); n++) { ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8); ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff); } - } else { - struct ether_addr out = { .ether_addr_octet = { 0 } }; + + } else if (IN_SET(sep, ':', '-')) { + struct ether_addr out = ETHER_ADDR_NULL; parse_fields(out.ether_addr_octet); + if (s[pos] != '\0') + return -EINVAL; + for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++) ret->ether_addr_octet[n] = out.ether_addr_octet[n]; - } - if (offset) - *offset = pos; + } else + return -EINVAL; + return 0; } diff --git a/src/systemd/src/basic/ether-addr-util.h b/src/systemd/src/basic/ether-addr-util.h index 74e125a9..f7e0de54 100644 --- a/src/systemd/src/basic/ether-addr-util.h +++ b/src/systemd/src/basic/ether-addr-util.h @@ -1,34 +1,27 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2014 Tom Gundersen - - 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 "hash-funcs.h" + #define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X" #define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5] #define ETHER_ADDR_TO_STRING_MAX (3*6) char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]); -bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b); +int ether_addr_compare(const void *a, const void *b); +static inline bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) { + return ether_addr_compare(a, b) == 0; +} #define ETHER_ADDR_NULL ((const struct ether_addr){}) @@ -36,4 +29,6 @@ static inline bool ether_addr_is_null(const struct ether_addr *addr) { return ether_addr_equal(addr, ÐER_ADDR_NULL); } -int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset); +int ether_addr_from_string(const char *s, struct ether_addr *ret); + +extern const struct hash_ops ether_addr_hash_ops; diff --git a/src/systemd/src/basic/extract-word.c b/src/systemd/src/basic/extract-word.c index 69d8c48d..2adb8966 100644 --- a/src/systemd/src/basic/extract-word.c +++ b/src/systemd/src/basic/extract-word.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -195,8 +183,7 @@ finish: finish_force_next: s[sz] = 0; - *ret = s; - s = NULL; + *ret = TAKE_PTR(s); return 1; } diff --git a/src/systemd/src/basic/extract-word.h b/src/systemd/src/basic/extract-word.h index 04746c6d..d2113948 100644 --- a/src/systemd/src/basic/extract-word.h +++ b/src/systemd/src/basic/extract-word.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "macro.h" diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c index 1c327d83..6a753195 100644 --- a/src/systemd/src/basic/fd-util.c +++ b/src/systemd/src/basic/fd-util.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -28,8 +16,10 @@ #include "dirent-util.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "macro.h" +#include "memfd-util.h" #include "missing.h" #include "parse-util.h" #include "path-util.h" @@ -97,8 +87,8 @@ void safe_close_pair(int p[]) { p[1] = safe_close(p[1]); } -void close_many(const int fds[], unsigned n_fd) { - unsigned i; +void close_many(const int fds[], size_t n_fd) { + size_t i; assert(fds || n_fd <= 0); @@ -191,14 +181,8 @@ int fd_cloexec(int fd, bool cloexec) { } #if 0 /* NM_IGNORED */ -void stdio_unset_cloexec(void) { - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_cloexec(STDERR_FILENO, false); -} - -_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) { - unsigned i; +_pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) { + size_t i; assert(n_fdset == 0 || fdset); @@ -209,7 +193,7 @@ _pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) { return false; } -int close_all_fds(const int except[], unsigned n_except) { +int close_all_fds(const int except[], size_t n_except) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; int r = 0; @@ -218,29 +202,37 @@ int close_all_fds(const int except[], unsigned n_except) { d = opendir("/proc/self/fd"); if (!d) { - int fd; struct rlimit rl; + int fd, max_fd; - /* When /proc isn't available (for example in chroots) - * the fallback is brute forcing through the fd + /* When /proc isn't available (for example in chroots) the fallback is brute forcing through the fd * table */ assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0); - for (fd = 3; fd < (int) rl.rlim_max; fd ++) { + + if (rl.rlim_max == 0) + return -EINVAL; + + /* Let's take special care if the resource limit is set to unlimited, or actually larger than the range + * of 'int'. Let's avoid implicit overflows. */ + max_fd = (rl.rlim_max == RLIM_INFINITY || rl.rlim_max > INT_MAX) ? INT_MAX : (int) (rl.rlim_max - 1); + + for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { + int q; if (fd_in_set(fd, except, n_except)) continue; - if (close_nointr(fd) < 0) - if (errno != EBADF && r == 0) - r = -errno; + q = close_nointr(fd); + if (q < 0 && q != -EBADF && r >= 0) + r = q; } return r; } FOREACH_DIRENT(de, d, return -errno) { - int fd = -1; + int fd = -1, q; if (safe_atoi(de->d_name, &fd) < 0) /* Let's better ignore this, just in case */ @@ -255,11 +247,9 @@ int close_all_fds(const int except[], unsigned n_except) { if (fd_in_set(fd, except, n_except)) continue; - if (close_nointr(fd) < 0) { - /* Valgrind has its own FD and doesn't want to have it closed */ - if (errno != EBADF && r == 0) - r = -errno; - } + q = close_nointr(fd); + if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */ + r = q; } return r; @@ -369,15 +359,384 @@ bool fdname_is_valid(const char *s) { } int fd_get_path(int fd, char **ret) { - char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + _cleanup_close_ int dir = -1; + char fdname[DECIMAL_STR_MAX(int)]; int r; - xsprintf(procfs_path, "/proc/self/fd/%i", fd); + dir = open("/proc/self/fd/", O_CLOEXEC | O_DIRECTORY | O_PATH); + if (dir < 0) + /* /proc is not available or not set up properly, we're most likely + * in some chroot environment. */ + return errno == ENOENT ? -EOPNOTSUPP : -errno; - r = readlink_malloc(procfs_path, ret); + xsprintf(fdname, "%i", fd); - if (r == -ENOENT) /* If the file doesn't exist the fd is invalid */ + r = readlinkat_malloc(dir, fdname, ret); + if (r == -ENOENT) + /* If the file doesn't exist the fd is invalid */ return -EBADF; return r; } + +#if 0 /* NM_IGNORED */ +int move_fd(int from, int to, int cloexec) { + int r; + + /* Move fd 'from' to 'to', make sure FD_CLOEXEC remains equal if requested, and release the old fd. If + * 'cloexec' is passed as -1, the original FD_CLOEXEC is inherited for the new fd. If it is 0, it is turned + * off, if it is > 0 it is turned on. */ + + if (from < 0) + return -EBADF; + if (to < 0) + return -EBADF; + + if (from == to) { + + if (cloexec >= 0) { + r = fd_cloexec(to, cloexec); + if (r < 0) + return r; + } + + return to; + } + + if (cloexec < 0) { + int fl; + + fl = fcntl(from, F_GETFD, 0); + if (fl < 0) + return -errno; + + cloexec = !!(fl & FD_CLOEXEC); + } + + r = dup3(from, to, cloexec ? O_CLOEXEC : 0); + if (r < 0) + return -errno; + + assert(r == to); + + safe_close(from); + + return to; +} + +int acquire_data_fd(const void *data, size_t size, unsigned flags) { + + _cleanup_close_pair_ int pipefds[2] = { -1, -1 }; + char pattern[] = "/dev/shm/data-fd-XXXXXX"; + _cleanup_close_ int fd = -1; + int isz = 0, r; + ssize_t n; + off_t f; + + assert(data || size == 0); + + /* Acquire a read-only file descriptor that when read from returns the specified data. This is much more + * complex than I wish it was. But here's why: + * + * a) First we try to use memfds. They are the best option, as we can seal them nicely to make them + * read-only. Unfortunately they require kernel 3.17, and – at the time of writing – we still support 3.14. + * + * b) Then, we try classic pipes. They are the second best options, as we can close the writing side, retaining + * a nicely read-only fd in the reading side. However, they are by default quite small, and unprivileged + * clients can only bump their size to a system-wide limit, which might be quite low. + * + * c) Then, we try an O_TMPFILE file in /dev/shm (that dir is the only suitable one known to exist from + * earliest boot on). To make it read-only we open the fd a second time with O_RDONLY via + * /proc/self/. Unfortunately O_TMPFILE is not available on older kernels on tmpfs. + * + * d) Finally, we try creating a regular file in /dev/shm, which we then delete. + * + * It sucks a bit that depending on the situation we return very different objects here, but that's Linux I + * figure. */ + + if (size == 0 && ((flags & ACQUIRE_NO_DEV_NULL) == 0)) { + /* As a special case, return /dev/null if we have been called for an empty data block */ + r = open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (r < 0) + return -errno; + + return r; + } + + if ((flags & ACQUIRE_NO_MEMFD) == 0) { + fd = memfd_new("data-fd"); + if (fd < 0) + goto try_pipe; + + n = write(fd, data, size); + if (n < 0) + return -errno; + if ((size_t) n != size) + return -EIO; + + f = lseek(fd, 0, SEEK_SET); + if (f != 0) + return -errno; + + r = memfd_set_sealed(fd); + if (r < 0) + return r; + + return TAKE_FD(fd); + } + +try_pipe: + if ((flags & ACQUIRE_NO_PIPE) == 0) { + if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0) + return -errno; + + isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); + if (isz < 0) + return -errno; + + if ((size_t) isz < size) { + isz = (int) size; + if (isz < 0 || (size_t) isz != size) + return -E2BIG; + + /* Try to bump the pipe size */ + (void) fcntl(pipefds[1], F_SETPIPE_SZ, isz); + + /* See if that worked */ + isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); + if (isz < 0) + return -errno; + + if ((size_t) isz < size) + goto try_dev_shm; + } + + n = write(pipefds[1], data, size); + if (n < 0) + return -errno; + if ((size_t) n != size) + return -EIO; + + (void) fd_nonblock(pipefds[0], false); + + return TAKE_FD(pipefds[0]); + } + +try_dev_shm: + if ((flags & ACQUIRE_NO_TMPFILE) == 0) { + fd = open("/dev/shm", O_RDWR|O_TMPFILE|O_CLOEXEC, 0500); + if (fd < 0) + goto try_dev_shm_without_o_tmpfile; + + n = write(fd, data, size); + if (n < 0) + return -errno; + if ((size_t) n != size) + return -EIO; + + /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */ + return fd_reopen(fd, O_RDONLY|O_CLOEXEC); + } + +try_dev_shm_without_o_tmpfile: + if ((flags & ACQUIRE_NO_REGULAR) == 0) { + fd = mkostemp_safe(pattern); + if (fd < 0) + return fd; + + n = write(fd, data, size); + if (n < 0) { + r = -errno; + goto unlink_and_return; + } + if ((size_t) n != size) { + r = -EIO; + goto unlink_and_return; + } + + /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */ + r = open(pattern, O_RDONLY|O_CLOEXEC); + if (r < 0) + r = -errno; + + unlink_and_return: + (void) unlink(pattern); + return r; + } + + return -EOPNOTSUPP; +} +#endif /* NM_IGNORED */ + +int fd_move_above_stdio(int fd) { + int flags, copy; + PROTECT_ERRNO; + + /* Moves the specified file descriptor if possible out of the range [0…2], i.e. the range of + * stdin/stdout/stderr. If it can't be moved outside of this range the original file descriptor is + * returned. This call is supposed to be used for long-lasting file descriptors we allocate in our code that + * might get loaded into foreign code, and where we want ensure our fds are unlikely used accidentally as + * stdin/stdout/stderr of unrelated code. + * + * Note that this doesn't fix any real bugs, it just makes it less likely that our code will be affected by + * buggy code from others that mindlessly invokes 'fprintf(stderr, …' or similar in places where stderr has + * been closed before. + * + * This function is written in a "best-effort" and "least-impact" style. This means whenever we encounter an + * error we simply return the original file descriptor, and we do not touch errno. */ + + if (fd < 0 || fd > 2) + return fd; + + flags = fcntl(fd, F_GETFD, 0); + if (flags < 0) + return fd; + + if (flags & FD_CLOEXEC) + copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); + else + copy = fcntl(fd, F_DUPFD, 3); + if (copy < 0) + return fd; + + assert(copy > 2); + + (void) close(fd); + return copy; +} + +#if 0 /* NM_IGNORED */ +int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd) { + + int fd[3] = { /* Put together an array of fds we work on */ + original_input_fd, + original_output_fd, + original_error_fd + }; + + int r, i, + null_fd = -1, /* if we open /dev/null, we store the fd to it here */ + copy_fd[3] = { -1, -1, -1 }; /* This contains all fds we duplicate here temporarily, and hence need to close at the end */ + bool null_readable, null_writable; + + /* Sets up stdin, stdout, stderr with the three file descriptors passed in. If any of the descriptors is + * specified as -1 it will be connected with /dev/null instead. If any of the file descriptors is passed as + * itself (e.g. stdin as STDIN_FILENO) it is left unmodified, but the O_CLOEXEC bit is turned off should it be + * on. + * + * Note that if any of the passed file descriptors are > 2 they will be closed — both on success and on + * failure! Thus, callers should assume that when this function returns the input fds are invalidated. + * + * Note that when this function fails stdin/stdout/stderr might remain half set up! + * + * O_CLOEXEC is turned off for all three file descriptors (which is how it should be for + * stdin/stdout/stderr). */ + + null_readable = original_input_fd < 0; + null_writable = original_output_fd < 0 || original_error_fd < 0; + + /* First step, open /dev/null once, if we need it */ + if (null_readable || null_writable) { + + /* Let's open this with O_CLOEXEC first, and convert it to non-O_CLOEXEC when we move the fd to the final position. */ + null_fd = open("/dev/null", (null_readable && null_writable ? O_RDWR : + null_readable ? O_RDONLY : O_WRONLY) | O_CLOEXEC); + if (null_fd < 0) { + r = -errno; + goto finish; + } + + /* If this fd is in the 0…2 range, let's move it out of it */ + if (null_fd < 3) { + int copy; + + copy = fcntl(null_fd, F_DUPFD_CLOEXEC, 3); /* Duplicate this with O_CLOEXEC set */ + if (copy < 0) { + r = -errno; + goto finish; + } + + safe_close(null_fd); + null_fd = copy; + } + } + + /* Let's assemble fd[] with the fds to install in place of stdin/stdout/stderr */ + for (i = 0; i < 3; i++) { + + if (fd[i] < 0) + fd[i] = null_fd; /* A negative parameter means: connect this one to /dev/null */ + else if (fd[i] != i && fd[i] < 3) { + /* This fd is in the 0…2 territory, but not at its intended place, move it out of there, so that we can work there. */ + copy_fd[i] = fcntl(fd[i], F_DUPFD_CLOEXEC, 3); /* Duplicate this with O_CLOEXEC set */ + if (copy_fd[i] < 0) { + r = -errno; + goto finish; + } + + fd[i] = copy_fd[i]; + } + } + + /* At this point we now have the fds to use in fd[], and they are all above the stdio range, so that we + * have freedom to move them around. If the fds already were at the right places then the specific fds are + * -1. Let's now move them to the right places. This is the point of no return. */ + for (i = 0; i < 3; i++) { + + if (fd[i] == i) { + + /* fd is already in place, but let's make sure O_CLOEXEC is off */ + r = fd_cloexec(i, false); + if (r < 0) + goto finish; + + } else { + assert(fd[i] > 2); + + if (dup2(fd[i], i) < 0) { /* Turns off O_CLOEXEC on the new fd. */ + r = -errno; + goto finish; + } + } + } + + r = 0; + +finish: + /* Close the original fds, but only if they were outside of the stdio range. Also, properly check for the same + * fd passed in multiple times. */ + safe_close_above_stdio(original_input_fd); + if (original_output_fd != original_input_fd) + safe_close_above_stdio(original_output_fd); + if (original_error_fd != original_input_fd && original_error_fd != original_output_fd) + safe_close_above_stdio(original_error_fd); + + /* Close the copies we moved > 2 */ + for (i = 0; i < 3; i++) + safe_close(copy_fd[i]); + + /* Close our null fd, if it's > 2 */ + safe_close_above_stdio(null_fd); + + return r; +} + +int fd_reopen(int fd, int flags) { + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + int new_fd; + + /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to + * turn O_RDWR fds into O_RDONLY fds. + * + * This doesn't work on sockets (since they cannot be open()ed, ever). + * + * This implicitly resets the file read index to 0. */ + + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + new_fd = open(procfs_path, flags); + if (new_fd < 0) + return -errno; + + return new_fd; +} +#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/fd-util.h b/src/systemd/src/basic/fd-util.h index 34b98d4a..89c3f34c 100644 --- a/src/systemd/src/basic/fd-util.h +++ b/src/systemd/src/basic/fd-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 @@ -34,7 +22,14 @@ int close_nointr(int fd); int safe_close(int fd); void safe_close_pair(int p[]); -void close_many(const int fds[], unsigned n_fd); +static inline int safe_close_above_stdio(int fd) { + if (fd < 3) /* Don't close stdin/stdout/stderr, but still invalidate the fd by returning -1 */ + return -1; + + return safe_close(fd); +} + +void close_many(const int fds[], size_t n_fd); int fclose_nointr(FILE *f); FILE* safe_fclose(FILE *f); @@ -63,9 +58,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir); int fd_nonblock(int fd, bool nonblock); int fd_cloexec(int fd, bool cloexec); -void stdio_unset_cloexec(void); -int close_all_fds(const int except[], unsigned n_except); +int close_all_fds(const int except[], size_t n_except); int same_fd(int a, int b); @@ -75,6 +69,40 @@ bool fdname_is_valid(const char *s); int fd_get_path(int fd, char **ret); +int move_fd(int from, int to, int cloexec); + +enum { + ACQUIRE_NO_DEV_NULL = 1 << 0, + ACQUIRE_NO_MEMFD = 1 << 1, + ACQUIRE_NO_PIPE = 1 << 2, + ACQUIRE_NO_TMPFILE = 1 << 3, + ACQUIRE_NO_REGULAR = 1 << 4, +}; + +int acquire_data_fd(const void *data, size_t size, unsigned flags); + /* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */ #define ERRNO_IS_DISCONNECT(r) \ IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH) + +/* Resource exhaustion, could be our fault or general system trouble */ +#define ERRNO_IS_RESOURCE(r) \ + IN_SET(r, ENOMEM, EMFILE, ENFILE) + +int fd_move_above_stdio(int fd); + +int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd); + +static inline int make_null_stdio(void) { + return rearrange_stdio(-1, -1, -1); +} + +/* Like TAKE_PTR() but for file descriptors, resetting them to -1 */ +#define TAKE_FD(fd) \ + ({ \ + int _fd_ = (fd); \ + (fd) = -1; \ + _fd_; \ + }) + +int fd_reopen(int fd, int flags); diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c index 51d1c052..4f71384e 100644 --- a/src/systemd/src/basic/fileio.c +++ b/src/systemd/src/basic/fileio.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -24,8 +12,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -62,12 +52,38 @@ int write_string_stream_ts( WriteStringFileFlags flags, struct timespec *ts) { + bool needs_nl; + int r; + assert(f); assert(line); - fputs(line, f); - if (!(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n")) - fputc('\n', f); + if (ferror(f)) + return -EIO; + + needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"); + + if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) { + /* If STDIO buffering was disabled, then let's append the newline character to the string itself, so + * that the write goes out in one go, instead of two */ + + line = strjoina(line, "\n"); + needs_nl = false; + } + + if (fputs(line, f) == EOF) + return -errno; + + if (needs_nl) + if (fputc('\n', f) == EOF) + return -errno; + + if (flags & WRITE_STRING_FILE_SYNC) + r = fflush_sync_and_check(f); + else + r = fflush_and_check(f); + if (r < 0) + return r; if (ts) { struct timespec twice[2] = {*ts, *ts}; @@ -76,10 +92,7 @@ int write_string_stream_ts( return -errno; } - if (flags & WRITE_STRING_FILE_SYNC) - return fflush_sync_and_check(f); - else - return fflush_and_check(f); + return 0; } static int write_string_file_atomic( @@ -99,6 +112,7 @@ static int write_string_file_atomic( if (r < 0) return r; + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); (void) fchmod_umask(fileno(f), 0644); r = write_string_stream_ts(f, line, flags, ts); @@ -141,7 +155,7 @@ int write_string_file_ts( return r; } else - assert(ts == NULL); + assert(!ts); if (flags & WRITE_STRING_FILE_CREATE) { f = fopen(fn, "we"); @@ -168,6 +182,11 @@ int write_string_file_ts( } } + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + + if (flags & WRITE_STRING_FILE_DISABLE_BUFFER) + setvbuf(f, NULL, _IONBF, 0); + r = write_string_stream_ts(f, line, flags, ts); if (r < 0) goto fail; @@ -201,10 +220,11 @@ int read_one_line_file(const char *fn, char **line) { if (!f) return -errno; + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + r = read_line(f, LONG_LINE_MAX, line); return r < 0 ? r : 0; } -#endif /* NM_IGNORED */ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { _cleanup_fclose_ FILE *f = NULL; @@ -227,6 +247,8 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { if (!f) return -errno; + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + /* We try to read one byte more than we need, so that we know whether we hit eof */ errno = 0; k = fread(buf, 1, l + accept_extra_nl + 1, f); @@ -242,6 +264,7 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { return 1; } +#endif /* NM_IGNORED */ int read_full_stream(FILE *f, char **contents, size_t *size) { size_t n, l; @@ -303,8 +326,7 @@ int read_full_stream(FILE *f, char **contents, size_t *size) { } buf[l] = 0; - *contents = buf; - buf = NULL; /* do not free */ + *contents = TAKE_PTR(buf); if (size) *size = l; @@ -322,6 +344,8 @@ int read_full_file(const char *fn, char **contents, size_t *size) { if (!f) return -errno; + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + return read_full_stream(f, contents, size); } @@ -334,11 +358,11 @@ static int parse_env_file_internal( void *userdata, int *n_pushed) { - _cleanup_free_ char *contents = NULL, *key = NULL; size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1; - char *p, *value = NULL; - int r; + _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL; unsigned line = 1; + char *p; + int r; enum { PRE_KEY, @@ -375,10 +399,8 @@ static int parse_env_file_internal( state = KEY; last_key_whitespace = (size_t) -1; - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) { - r = -ENOMEM; - goto fail; - } + if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) + return -ENOMEM; key[n_key++] = c; } @@ -398,10 +420,8 @@ static int parse_env_file_internal( else if (last_key_whitespace == (size_t) -1) last_key_whitespace = n_key; - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) { - r = -ENOMEM; - goto fail; - } + if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) + return -ENOMEM; key[n_key++] = c; } @@ -423,7 +443,7 @@ static int parse_env_file_internal( r = push(fname, line, key, value, userdata, n_pushed); if (r < 0) - goto fail; + return r; n_key = 0; value = NULL; @@ -438,10 +458,8 @@ static int parse_env_file_internal( else if (!strchr(WHITESPACE, c)) { state = VALUE; - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; value[n_value++] = c; } @@ -468,7 +486,7 @@ static int parse_env_file_internal( r = push(fname, line, key, value, userdata, n_pushed); if (r < 0) - goto fail; + return r; n_key = 0; value = NULL; @@ -483,10 +501,8 @@ static int parse_env_file_internal( else if (last_value_whitespace == (size_t) -1) last_value_whitespace = n_value; - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; value[n_value++] = c; } @@ -498,10 +514,8 @@ static int parse_env_file_internal( if (!strchr(newline, c)) { /* Escaped newlines we eat up entirely */ - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; value[n_value++] = c; } @@ -513,10 +527,8 @@ static int parse_env_file_internal( else if (c == '\\') state = SINGLE_QUOTE_VALUE_ESCAPE; else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; value[n_value++] = c; } @@ -527,10 +539,8 @@ static int parse_env_file_internal( state = SINGLE_QUOTE_VALUE; if (!strchr(newline, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; value[n_value++] = c; } @@ -542,10 +552,8 @@ static int parse_env_file_internal( else if (c == '\\') state = DOUBLE_QUOTE_VALUE_ESCAPE; else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; value[n_value++] = c; } @@ -556,10 +564,8 @@ static int parse_env_file_internal( state = DOUBLE_QUOTE_VALUE; if (!strchr(newline, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; value[n_value++] = c; } @@ -604,14 +610,12 @@ static int parse_env_file_internal( r = push(fname, line, key, value, userdata, n_pushed); if (r < 0) - goto fail; + return r; + + value = NULL; } return 0; - -fail: - free(value); - return r; } static int check_utf8ness_and_warn( @@ -879,7 +883,8 @@ int write_env_file(const char *fname, char **l) { if (r < 0) return r; - fchmod_umask(fileno(f), 0644); + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + (void) fchmod_umask(fileno(f), 0644); STRV_FOREACH(i, l) write_env_var(f, *i); @@ -897,14 +902,16 @@ int write_env_file(const char *fname, char **l) { } int executable_is_script(const char *path, char **interpreter) { - int r; _cleanup_free_ char *line = NULL; - int len; + size_t len; char *ans; + int r; assert(path); r = read_one_line_file(path, &line); + if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */ + return 0; if (r < 0) return r; @@ -1147,6 +1154,7 @@ int fflush_and_check(FILE *f) { return 0; } +#if 0 /* NM_IGNORED */ int fflush_sync_and_check(FILE *f) { int r; @@ -1159,8 +1167,13 @@ int fflush_sync_and_check(FILE *f) { if (fsync(fileno(f)) < 0) return -errno; + r = fsync_directory_of_file(fileno(f)); + if (r < 0) + return r; + return 0; } +#endif /* NM_IGNORED */ /* This is much like mkostemp() but is subject to umask(). */ int mkostemp_safe(char *pattern) { @@ -1197,8 +1210,7 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { if (!filename_is_valid(fn)) return -EINVAL; - if (extra == NULL) - extra = ""; + extra = strempty(extra); t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1); if (!t) @@ -1232,8 +1244,7 @@ int tempfn_random(const char *p, const char *extra, char **ret) { if (!filename_is_valid(fn)) return -EINVAL; - if (!extra) - extra = ""; + extra = strempty(extra); t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1); if (!t) @@ -1273,8 +1284,7 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { return r; } - if (!extra) - extra = ""; + extra = strempty(extra); t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1); if (!t) @@ -1426,8 +1436,7 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { if (fd < 0) return -errno; - *ret_path = tmp; - tmp = NULL; + *ret_path = TAKE_PTR(tmp); return fd; } @@ -1467,7 +1476,7 @@ int link_tmpfile(int fd, const char *path, const char *target) { if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0) return -errno; } else { - char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; + char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; xsprintf(proc_fd_path, "/proc/self/fd/%i", fd); @@ -1513,8 +1522,7 @@ int read_nul_string(FILE *f, char **ret) { return -ENOMEM; } - *ret = x; - x = NULL; + *ret = TAKE_PTR(x); return 0; } @@ -1538,9 +1546,7 @@ int mkdtemp_malloc(const char *template, char **ret) { return 0; } -static inline void funlockfilep(FILE **f) { - funlockfile(*f); -} +DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile); int read_line(FILE *f, size_t limit, char **ret) { _cleanup_free_ char *buffer = NULL; @@ -1567,7 +1573,7 @@ int read_line(FILE *f, size_t limit, char **ret) { } { - _cleanup_(funlockfilep) FILE *flocked = f; + _unused_ _cleanup_(funlockfilep) FILE *flocked = f; flockfile(f); for (;;) { @@ -1605,8 +1611,7 @@ int read_line(FILE *f, size_t limit, char **ret) { if (ret) { buffer[n] = 0; - *ret = buffer; - buffer = NULL; + *ret = TAKE_PTR(buffer); } return (int) count; diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h index eba05be2..bed7c8fb 100644 --- a/src/systemd/src/basic/fileio.h +++ b/src/systemd/src/basic/fileio.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 @@ -29,11 +17,17 @@ #include "time-util.h" typedef enum { - WRITE_STRING_FILE_CREATE = 1<<0, - WRITE_STRING_FILE_ATOMIC = 1<<1, - WRITE_STRING_FILE_AVOID_NEWLINE = 1<<2, + WRITE_STRING_FILE_CREATE = 1<<0, + WRITE_STRING_FILE_ATOMIC = 1<<1, + WRITE_STRING_FILE_AVOID_NEWLINE = 1<<2, WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1<<3, - WRITE_STRING_FILE_SYNC = 1<<4, + WRITE_STRING_FILE_SYNC = 1<<4, + WRITE_STRING_FILE_DISABLE_BUFFER = 1<<5, + + /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one + more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file() + and friends. */ + } WriteStringFileFlags; int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts); diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c index ff4ad5ab..049b238e 100644 --- a/src/systemd/src/basic/fs-util.c +++ b/src/systemd/src/basic/fs-util.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -40,6 +28,7 @@ #include "mkdir.h" #include "parse-util.h" #include "path-util.h" +#include "process-util.h" #include "stat-util.h" #include "stdio-util.h" #include "string-util.h" @@ -107,7 +96,6 @@ int rmdir_parents(const char *path, const char *stop) { return 0; } - int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { struct stat buf; int ret; @@ -229,49 +217,6 @@ int readlink_and_make_absolute(const char *p, char **r) { return 0; } -int readlink_and_canonicalize(const char *p, const char *root, char **ret) { - char *t, *s; - int r; - - assert(p); - assert(ret); - - r = readlink_and_make_absolute(p, &t); - if (r < 0) - return r; - - r = chase_symlinks(t, root, 0, &s); - if (r < 0) - /* If we can't follow up, then let's return the original string, slightly cleaned up. */ - *ret = path_kill_slashes(t); - else { - *ret = s; - free(t); - } - - return 0; -} - -int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) { - _cleanup_free_ char *target = NULL, *t = NULL; - const char *full; - int r; - - full = prefix_roota(root, path); - r = readlink_malloc(full, &target); - if (r < 0) - return r; - - t = file_in_same_dir(path, target); - if (!t) - return -ENOMEM; - - *ret = t; - t = NULL; - - return 0; -} - int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { assert(path); @@ -303,6 +248,21 @@ int fchmod_umask(int fd, mode_t m) { } #if 0 /* NM_IGNORED */ +int fchmod_opath(int fd, mode_t m) { + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + + /* This function operates also on fd that might have been opened with + * O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like + * fchownat() does. */ + + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + + if (chmod(procfs_path, m) < 0) + return -errno; + + return 0; +} + int fd_warn_permissions(const char *path, int fd) { struct stat st; @@ -322,43 +282,60 @@ int fd_warn_permissions(const char *path, int fd) { } int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { - _cleanup_close_ int fd; - int r; + char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + _cleanup_close_ int fd = -1; + int r, ret = 0; assert(path); - if (parents) - mkdir_parents(path, 0755); - - fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, - IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode); - if (fd < 0) - return -errno; + /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink + * itself which is updated, not its target + * + * Returns the first error we encounter, but tries to apply as much as possible. */ - if (mode != MODE_INVALID) { - r = fchmod(fd, mode); - if (r < 0) + if (parents) + (void) mkdir_parents(path, 0755); + + /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in + * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and + * won't trigger any driver magic or so. */ + fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); + if (fd < 0) { + if (errno != ENOENT) return -errno; - } - if (uid != UID_INVALID || gid != GID_INVALID) { - r = fchown(fd, uid, gid); - if (r < 0) + /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file + * here, and nothing else */ + fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode); + if (fd < 0) return -errno; } + /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode, + * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is + * something fchown(), fchmod(), futimensat() don't allow. */ + xsprintf(fdpath, "/proc/self/fd/%i", fd); + + if (mode != MODE_INVALID) + if (chmod(fdpath, mode) < 0) + ret = -errno; + + if (uid_is_valid(uid) || gid_is_valid(gid)) + if (chown(fdpath, uid, gid) < 0 && ret >= 0) + ret = -errno; + if (stamp != USEC_INFINITY) { struct timespec ts[2]; timespec_store(&ts[0], stamp); ts[1] = ts[0]; - r = futimens(fd, ts); + r = utimensat(AT_FDCWD, fdpath, ts, 0); } else - r = futimens(fd, NULL); - if (r < 0) + r = utimensat(AT_FDCWD, fdpath, NULL, 0); + if (r < 0 && ret >= 0) return -errno; - return 0; + return ret; } int touch(const char *path) { @@ -490,10 +467,8 @@ int get_files_in_directory(const char *path, char ***list) { n++; } - if (list) { - *list = l; - l = NULL; /* avoid freeing */ - } + if (list) + *list = TAKE_PTR(l); return n; } @@ -516,7 +491,7 @@ static int getenv_tmp_dir(const char **ret_path) { r = -ENOTDIR; goto next; } - if (!path_is_safe(e)) { + if (!path_is_normalized(e)) { r = -EPERM; goto next; } @@ -586,8 +561,19 @@ int tmp_dir(const char **ret) { return tmp_dir_internal("/tmp", ret); } +int unlink_or_warn(const char *filename) { + if (unlink(filename) < 0 && errno != ENOENT) + /* If the file doesn't exist and the fs simply was read-only (in which + * case unlink() returns EROFS even if the file doesn't exist), don't + * complain */ + if (errno != EROFS || access(filename, F_OK) >= 0) + return log_error_errno(errno, "Failed to remove \"%s\": %m", filename); + + return 0; +} + int inotify_add_watch_fd(int fd, int what, uint32_t mask) { - char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; + char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; int r; /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ @@ -600,16 +586,38 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) { return r; } +static bool safe_transition(const struct stat *a, const struct stat *b) { + /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to + * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files + * making us believe we read something safe even though it isn't safe in the specific context we open it in. */ + + if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */ + return true; + + return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */ +} + int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) { _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL; _cleanup_close_ int fd = -1; - unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */ + unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */ + struct stat previous_stat; bool exists = true; char *todo; int r; assert(path); + /* Either the file may be missing, or we return an fd to the final object, but both make no sense */ + if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN)) + return -EINVAL; + + if ((flags & (CHASE_STEP|CHASE_OPEN)) == (CHASE_STEP|CHASE_OPEN)) + return -EINVAL; + + if (isempty(path)) + return -EINVAL; + /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following * symlinks relative to a root directory, instead of the root of the host. * @@ -630,16 +638,54 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, * function what to do when encountering a symlink with an absolute path as directory: prefix it by the * specified path. * - * Note: there's also chase_symlinks_prefix() (see below), which as first step prefixes the passed path by the - * passed root. */ + * There are three ways to invoke this function: + * + * 1. Without CHASE_STEP or CHASE_OPEN: in this case the path is resolved and the normalized path is returned + * in `ret`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set 0 is returned if the file + * doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set >= 0 is returned if the destination was + * found, -ENOENT if it doesn't. + * + * 2. With CHASE_OPEN: in this case the destination is opened after chasing it as O_PATH and this file + * descriptor is returned as return value. This is useful to open files relative to some root + * directory. Note that the returned O_PATH file descriptors must be converted into a regular one (using + * fd_reopen() or such) before it can be used for reading/writing. CHASE_OPEN may not be combined with + * CHASE_NONEXISTENT. + * + * 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first + * symlink or ".." component of the path is resolved, and the resulting path is returned. This is useful if + * a caller wants to trace the a path through the file system verbosely. Returns < 0 on error, > 0 if the + * path is fully normalized, and == 0 for each normalization step. This may be combined with + * CHASE_NONEXISTENT, in which case 1 is returned when a component is not found. + * + * */ + + /* A root directory of "/" or "" is identical to none */ + if (empty_or_root(original_root)) + original_root = NULL; + + if (!original_root && !ret && (flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_OPEN|CHASE_STEP)) == CHASE_OPEN) { + /* Shortcut the CHASE_OPEN case if the caller isn't interested in the actual path and has no root set + * and doesn't care about any of the other special features we provide either. */ + r = open(path, O_PATH|O_CLOEXEC); + if (r < 0) + return -errno; + + return r; + } if (original_root) { r = path_make_absolute_cwd(original_root, &root); if (r < 0) return r; - if (flags & CHASE_PREFIX_ROOT) + if (flags & CHASE_PREFIX_ROOT) { + + /* We don't support relative paths in combination with a root directory */ + if (!path_is_absolute(path)) + return -EINVAL; + path = prefix_roota(root, path); + } } r = path_make_absolute_cwd(path, &buffer); @@ -650,6 +696,11 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fd < 0) return -errno; + if (flags & CHASE_SAFE) { + if (fstat(fd, &previous_stat) < 0) + return -errno; + } + todo = buffer; for (;;) { _cleanup_free_ char *first = NULL; @@ -668,9 +719,20 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, todo += m; + /* Empty? Then we reached the end. */ + if (isempty(first)) + break; + /* Just a single slash? Then we reached the end. */ - if (isempty(first) || path_equal(first, "/")) + if (path_equal(first, "/")) { + /* Preserve the trailing slash */ + + if (flags & CHASE_TRAIL_SLASH) + if (!strextend(&done, "/", NULL)) + return -ENOMEM; + break; + } /* Just a dot? Then let's eat this up. */ if (path_equal(first, "/.")) @@ -679,11 +741,11 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, /* Two dots? Then chop off the last bit of what we already found out. */ if (path_equal(first, "/..")) { _cleanup_free_ char *parent = NULL; - int fd_parent = -1; + _cleanup_close_ int fd_parent = -1; /* If we already are at the top, then going up will not change anything. This is in-line with * how the kernel handles this. */ - if (isempty(done) || path_equal(done, "/")) + if (empty_or_root(done)) continue; parent = dirname_malloc(done); @@ -698,12 +760,25 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, free_and_replace(done, parent); + if (flags & CHASE_STEP) + goto chased_one; + fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH); if (fd_parent < 0) return -errno; + if (flags & CHASE_SAFE) { + if (fstat(fd_parent, &st) < 0) + return -errno; + + if (!safe_transition(&previous_stat, &st)) + return -EPERM; + + previous_stat = st; + } + safe_close(fd); - fd = fd_parent; + fd = TAKE_FD(fd_parent); continue; } @@ -714,12 +789,16 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (errno == ENOENT && (flags & CHASE_NONEXISTENT) && - (isempty(todo) || path_is_safe(todo))) { + (isempty(todo) || path_is_normalized(todo))) { /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return * what we got so far. But don't allow this if the remaining path contains "../ or "./" * or something else weird. */ + /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */ + if (streq_ptr(done, "/")) + *done = '\0'; + if (!strextend(&done, first, todo, NULL)) return -ENOMEM; @@ -732,8 +811,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(child, &st) < 0) return -errno; + if ((flags & CHASE_SAFE) && + !safe_transition(&previous_stat, &st)) + return -EPERM; + + previous_stat = st; + if ((flags & CHASE_NO_AUTOFS) && - fd_check_fstype(child, AUTOFS_SUPER_MAGIC) > 0) + fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) return -EREMOTE; if (S_ISLNK(st.st_mode)) { @@ -762,6 +847,16 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fd < 0) return -errno; + if (flags & CHASE_SAFE) { + if (fstat(fd, &st) < 0) + return -errno; + + if (!safe_transition(&previous_stat, &st)) + return -EPERM; + + previous_stat = st; + } + free(done); /* Note that we do not revalidate the root, we take it as is. */ @@ -773,34 +868,38 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, return -ENOMEM; } - } - - /* Prefix what's left to do with what we just read, and start the loop again, - * but remain in the current directory. */ - - joined = strjoin("/", destination, todo); + /* Prefix what's left to do with what we just read, and start the loop again, but + * remain in the current directory. */ + joined = strjoin(destination, todo); + } else + joined = strjoin("/", destination, todo); if (!joined) return -ENOMEM; free(buffer); todo = buffer = joined; + if (flags & CHASE_STEP) + goto chased_one; + continue; } /* If this is not a symlink, then let's just add the name we read to what we already verified. */ - if (!done) { - done = first; - first = NULL; - } else { + if (!done) + done = TAKE_PTR(first); + else { + /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */ + if (streq(done, "/")) + *done = '\0'; + if (!strextend(&done, first, NULL)) return -ENOMEM; } /* And iterate again, but go one directory further down. */ safe_close(fd); - fd = child; - child = -1; + fd = TAKE_FD(child); } if (!done) { @@ -810,11 +909,277 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, return -ENOMEM; } - if (ret) { - *ret = done; - done = NULL; + if (ret) + *ret = TAKE_PTR(done); + + if (flags & CHASE_OPEN) { + /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by + * opening /proc/self/fd/xyz. */ + + assert(fd >= 0); + return TAKE_FD(fd); } + if (flags & CHASE_STEP) + return 1; + return exists; + +chased_one: + if (ret) { + char *c; + + c = strjoin(strempty(done), todo); + if (!c) + return -ENOMEM; + + *ret = c; + } + + return 0; +} + +int chase_symlinks_and_open( + const char *path, + const char *root, + unsigned chase_flags, + int open_flags, + char **ret_path) { + + _cleanup_close_ int path_fd = -1; + _cleanup_free_ char *p = NULL; + int r; + + if (chase_flags & CHASE_NONEXISTENT) + return -EINVAL; + + if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { + /* Shortcut this call if none of the special features of this call are requested */ + r = open(path, open_flags); + if (r < 0) + return -errno; + + return r; + } + + path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL); + if (path_fd < 0) + return path_fd; + + r = fd_reopen(path_fd, open_flags); + if (r < 0) + return r; + + if (ret_path) + *ret_path = TAKE_PTR(p); + + return r; +} + +int chase_symlinks_and_opendir( + const char *path, + const char *root, + unsigned chase_flags, + char **ret_path, + DIR **ret_dir) { + + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + _cleanup_close_ int path_fd = -1; + _cleanup_free_ char *p = NULL; + DIR *d; + + if (!ret_dir) + return -EINVAL; + if (chase_flags & CHASE_NONEXISTENT) + return -EINVAL; + + if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { + /* Shortcut this call if none of the special features of this call are requested */ + d = opendir(path); + if (!d) + return -errno; + + *ret_dir = d; + return 0; + } + + path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL); + if (path_fd < 0) + return path_fd; + + xsprintf(procfs_path, "/proc/self/fd/%i", path_fd); + d = opendir(procfs_path); + if (!d) + return -errno; + + if (ret_path) + *ret_path = TAKE_PTR(p); + + *ret_dir = d; + return 0; +} + +int chase_symlinks_and_stat( + const char *path, + const char *root, + unsigned chase_flags, + char **ret_path, + struct stat *ret_stat) { + + _cleanup_close_ int path_fd = -1; + _cleanup_free_ char *p = NULL; + + assert(path); + assert(ret_stat); + + if (chase_flags & CHASE_NONEXISTENT) + return -EINVAL; + + if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { + /* Shortcut this call if none of the special features of this call are requested */ + if (stat(path, ret_stat) < 0) + return -errno; + + return 1; + } + + path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL); + if (path_fd < 0) + return path_fd; + + if (fstat(path_fd, ret_stat) < 0) + return -errno; + + if (ret_path) + *ret_path = TAKE_PTR(p); + + if (chase_flags & CHASE_OPEN) + return TAKE_FD(path_fd); + + return 1; +} + +int access_fd(int fd, int mode) { + char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; + int r; + + /* Like access() but operates on an already open fd */ + + xsprintf(p, "/proc/self/fd/%i", fd); + r = access(p, mode); + if (r < 0) + return -errno; + + return r; +} + +int unlinkat_deallocate(int fd, const char *name, int flags) { + _cleanup_close_ int truncate_fd = -1; + struct stat st; + off_t l, bs; + + /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other + * link to it. This is useful to ensure that other processes that might have the file open for reading won't be + * able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up + * jobs ("vacuuming"), where we want to make sure the data is really gone and the disk space released and + * returned to the free pool. + * + * Deallocation is preferably done by FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE (👊) if supported, which means + * the file won't change size. That's a good thing since we shouldn't needlessly trigger SIGBUS in other + * programs that have mmap()ed the file. (The assumption here is that changing file contents to all zeroes + * underneath those programs is the better choice than simply triggering SIGBUS in them which truncation does.) + * However if hole punching is not implemented in the kernel or file system we'll fall back to normal file + * truncation (🔪), as our goal of deallocating the data space trumps our goal of being nice to readers (💐). + * + * Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the + * primary job – to delete the file – is accomplished. */ + + if ((flags & AT_REMOVEDIR) == 0) { + truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK); + if (truncate_fd < 0) { + + /* If this failed because the file doesn't exist propagate the error right-away. Also, + * AT_REMOVEDIR wasn't set, and we tried to open the file for writing, which means EISDIR is + * returned when this is a directory but we are not supposed to delete those, hence propagate + * the error right-away too. */ + if (IN_SET(errno, ENOENT, EISDIR)) + return -errno; + + if (errno != ELOOP) /* don't complain if this is a symlink */ + log_debug_errno(errno, "Failed to open file '%s' for deallocation, ignoring: %m", name); + } + } + + if (unlinkat(fd, name, flags) < 0) + return -errno; + + if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */ + return 0; + + if (fstat(truncate_fd, &st) < 0) { + log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring.", name); + return 0; + } + + if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0) + return 0; + + /* If this is a regular file, it actually took up space on disk and there are no other links it's time to + * punch-hole/truncate this to release the disk space. */ + + bs = MAX(st.st_blksize, 512); + l = DIV_ROUND_UP(st.st_size, bs) * bs; /* Round up to next block size */ + + if (fallocate(truncate_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, 0, l) >= 0) + return 0; /* Successfully punched a hole! 😊 */ + + /* Fall back to truncation */ + if (ftruncate(truncate_fd, 0) < 0) { + log_debug_errno(errno, "Failed to truncate file to 0, ignoring: %m"); + return 0; + } + + return 0; +} + +int fsync_directory_of_file(int fd) { + _cleanup_free_ char *path = NULL, *dn = NULL; + _cleanup_close_ int dfd = -1; + int r; + + r = fd_verify_regular(fd); + if (r < 0) + return r; + + r = fd_get_path(fd, &path); + if (r < 0) { + log_debug("Failed to query /proc/self/fd/%d%s: %m", + fd, + r == -EOPNOTSUPP ? ", ignoring" : ""); + + if (r == -EOPNOTSUPP) + /* If /proc is not available, we're most likely running in some + * chroot environment, and syncing the directory is not very + * important in that case. Let's just silently do nothing. */ + return 0; + + return r; + } + + if (!path_is_absolute(path)) + return -EINVAL; + + dn = dirname_malloc(path); + if (!dn) + return -ENOMEM; + + dfd = open(dn, O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (dfd < 0) + return -errno; + + if (fsync(dfd) < 0) + return -errno; + + return 0; } #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h index d3342d5c..6157fe81 100644 --- a/src/systemd/src/basic/fs-util.h +++ b/src/systemd/src/basic/fs-util.h @@ -1,24 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 @@ -28,6 +17,7 @@ #include #include "time-util.h" +#include "util.h" int unlink_noerrno(const char *path); @@ -39,12 +29,11 @@ int readlinkat_malloc(int fd, const char *p, char **ret); int readlink_malloc(const char *p, char **r); int readlink_value(const char *p, char **ret); int readlink_and_make_absolute(const char *p, char **r); -int readlink_and_canonicalize(const char *p, const char *root, char **r); -int readlink_and_make_absolute_root(const char *root, const char *path, char **ret); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); int fchmod_umask(int fd, mode_t mode); +int fchmod_opath(int fd, mode_t m); int fd_warn_permissions(const char *path, int fd); @@ -64,6 +53,8 @@ int get_files_in_directory(const char *path, char ***list); int tmp_dir(const char **ret); int var_tmp_dir(const char **ret); +int unlink_or_warn(const char *filename); + #define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1) #define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ @@ -79,22 +70,40 @@ union inotify_event_buffer { int inotify_add_watch_fd(int fd, int what, uint32_t mask); enum { - CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */ - CHASE_NONEXISTENT = 2, /* If set, it's OK if the path doesn't actually exist. */ - CHASE_NO_AUTOFS = 4, /* If set, return -EREMOTE if autofs mount point found */ + CHASE_PREFIX_ROOT = 1U << 0, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */ + CHASE_NONEXISTENT = 1U << 1, /* If set, it's OK if the path doesn't actually exist. */ + CHASE_NO_AUTOFS = 1U << 2, /* If set, return -EREMOTE if autofs mount point found */ + CHASE_SAFE = 1U << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */ + CHASE_OPEN = 1U << 4, /* If set, return an O_PATH object to the final component */ + CHASE_TRAIL_SLASH = 1U << 5, /* If set, any trailing slash will be preserved */ + CHASE_STEP = 1U << 6, /* If set, just execute a single step of the normalization */ }; +/* How many iterations to execute before returning -ELOOP */ +#define CHASE_SYMLINKS_MAX 32 + int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret); +int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path); +int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir); +int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat); + /* Useful for usage with _cleanup_(), removes a directory and frees the pointer */ static inline void rmdir_and_free(char *p) { + PROTECT_ERRNO; (void) rmdir(p); free(p); } DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free); static inline void unlink_and_free(char *p) { - (void) unlink(p); + (void) unlink_noerrno(p); free(p); } DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free); + +int access_fd(int fd, int mode); + +int unlinkat_deallocate(int fd, const char *name, int flags); + +int fsync_directory_of_file(int fd); diff --git a/src/systemd/src/basic/hash-funcs.c b/src/systemd/src/basic/hash-funcs.c index 4a29f43c..cb763f56 100644 --- a/src/systemd/src/basic/hash-funcs.c +++ b/src/systemd/src/basic/hash-funcs.c @@ -1,31 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering Copyright 2014 Michal Schmidt - - 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 "nm-sd-adapt.h" +#include + #include "hash-funcs.h" +#include "path-util.h" void string_hash_func(const void *p, struct siphash *state) { siphash24_compress(p, strlen(p) + 1, state); } +#if 0 /* NM_IGNORED */ int string_compare_func(const void *a, const void *b) { return strcmp(a, b); } @@ -35,6 +27,55 @@ const struct hash_ops string_hash_ops = { .compare = string_compare_func }; +void path_hash_func(const void *p, struct siphash *state) { + const char *q = p; + size_t n; + + assert(q); + assert(state); + + /* Calculates a hash for a path in a way this duplicate inner slashes don't make a differences, and also + * whether there's a trailing slash or not. This fits well with the semantics of path_compare(), which does + * similar checks and also doesn't care for trailing slashes. Note that relative and absolute paths (i.e. those + * which begin in a slash or not) will hash differently though. */ + + n = strspn(q, "/"); + if (n > 0) { /* Eat up initial slashes, and add one "/" to the hash for all of them */ + siphash24_compress(q, 1, state); + q += n; + } + + for (;;) { + /* Determine length of next component */ + n = strcspn(q, "/"); + if (n == 0) /* Reached the end? */ + break; + + /* Add this component to the hash and skip over it */ + siphash24_compress(q, n, state); + q += n; + + /* How many slashes follow this component? */ + n = strspn(q, "/"); + if (q[n] == 0) /* Is this a trailing slash? If so, we are at the end, and don't care about the slashes anymore */ + break; + + /* We are not add the end yet. Hash exactly one slash for all of the ones we just encountered. */ + siphash24_compress(q, 1, state); + q += n; + } +} + +int path_compare_func(const void *a, const void *b) { + return path_compare(a, b); +} + +const struct hash_ops path_hash_ops = { + .hash = path_hash_func, + .compare = path_compare_func +}; +#endif /* NM_IGNORED */ + void trivial_hash_func(const void *p, struct siphash *state) { siphash24_compress(&p, sizeof(p), state); } @@ -64,6 +105,7 @@ const struct hash_ops uint64_hash_ops = { .compare = uint64_compare_func }; +#if 0 /* NM_IGNORED */ #if SIZEOF_DEV_T != 8 void devt_hash_func(const void *p, struct siphash *state) { siphash24_compress(p, sizeof(dev_t), state); @@ -81,3 +123,4 @@ const struct hash_ops devt_hash_ops = { .compare = devt_compare_func }; #endif +#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/hash-funcs.h b/src/systemd/src/basic/hash-funcs.h index 299189d1..0c963af8 100644 --- a/src/systemd/src/basic/hash-funcs.h +++ b/src/systemd/src/basic/hash-funcs.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -5,19 +6,6 @@ Copyright 2010 Lennart Poettering Copyright 2014 Michal Schmidt - - 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 "macro.h" @@ -35,29 +23,28 @@ void string_hash_func(const void *p, struct siphash *state); int string_compare_func(const void *a, const void *b) _pure_; extern const struct hash_ops string_hash_ops; -/* This will compare the passed pointers directly, and will not - * dereference them. This is hence not useful for strings or - * suchlike. */ +void path_hash_func(const void *p, struct siphash *state); +int path_compare_func(const void *a, const void *b) _pure_; +extern const struct hash_ops path_hash_ops; + +/* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings + * or suchlike. */ void trivial_hash_func(const void *p, struct siphash *state); int trivial_compare_func(const void *a, const void *b) _const_; extern const struct hash_ops trivial_hash_ops; -/* 32bit values we can always just embed in the pointer itself, but - * in order to support 32bit archs we need store 64bit values - * indirectly, since they don't fit in a pointer. */ +/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit + * values indirectly, since they don't fit in a pointer. */ void uint64_hash_func(const void *p, struct siphash *state); int uint64_compare_func(const void *a, const void *b) _pure_; extern const struct hash_ops uint64_hash_ops; -/* On some archs dev_t is 32bit, and on others 64bit. And sometimes - * it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */ +/* On some archs dev_t is 32bit, and on others 64bit. And sometimes it's 64bit on 32bit archs, and sometimes 32bit on + * 64bit archs. Yuck! */ #if SIZEOF_DEV_T != 8 void devt_hash_func(const void *p, struct siphash *state) _pure_; int devt_compare_func(const void *a, const void *b) _pure_; -extern const struct hash_ops devt_hash_ops = { - .hash = devt_hash_func, - .compare = devt_compare_func -}; +extern const struct hash_ops devt_hash_ops; #else #define devt_hash_func uint64_hash_func #define devt_compare_func uint64_compare_func diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c index c95d4991..b8a8e37b 100644 --- a/src/systemd/src/basic/hashmap.c +++ b/src/systemd/src/basic/hashmap.c @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering Copyright 2014 Michal Schmidt - - 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 "nm-sd-adapt.h" @@ -27,12 +15,14 @@ #include "alloc-util.h" #include "hashmap.h" +#include "fileio.h" #include "macro.h" #include "mempool.h" #include "process-util.h" #include "random-util.h" #include "set.h" #include "siphash24.h" +#include "string-util.h" #include "strv.h" #include "util.h" @@ -228,6 +218,8 @@ struct HashmapBase { unsigned n_direct_entries:3; /* Number of entries in direct storage. * Only valid if !has_indirect. */ bool from_pool:1; /* whether was allocated from mempool */ + bool dirty:1; /* whether dirtied since last iterated_cache_get() */ + bool cached:1; /* whether this hashmap is being cached */ HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */ }; @@ -247,6 +239,17 @@ struct Set { struct HashmapBase b; }; +typedef struct CacheMem { + const void **ptr; + size_t n_populated, n_allocated; + bool active:1; +} CacheMem; + +struct IteratedCache { + HashmapBase *hashmap; + CacheMem keys, values; +}; + DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8); DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8); /* No need for a separate Set pool */ @@ -280,6 +283,28 @@ static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { }, }; +#if VALGRIND +__attribute__((destructor)) static void cleanup_pools(void) { + _cleanup_free_ char *t = NULL; + int r; + + /* Be nice to valgrind */ + + /* The pool is only allocated by the main thread, but the memory can + * be passed to other threads. Let's clean up if we are the main thread + * and no other threads are live. */ + if (!is_main_thread()) + return; + + r = get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t); + if (r < 0 || !streq(t, "1")) + return; + + mempool_drop(&hashmap_pool); + mempool_drop(&ordered_hashmap_pool); +} +#endif + static unsigned n_buckets(HashmapBase *h) { return h->has_indirect ? h->indirect.n_buckets : hashmap_type_info[h->type].n_direct_buckets; @@ -328,6 +353,11 @@ static unsigned base_bucket_hash(HashmapBase *h, const void *p) { } #define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p) +static inline void base_set_dirty(HashmapBase *h) { + h->dirty = true; +} +#define hashmap_set_dirty(h) base_set_dirty(HASHMAP_BASE(h)) + static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { static uint8_t current[HASH_KEY_SIZE]; static bool current_initialized = false; @@ -545,6 +575,7 @@ static void base_remove_entry(HashmapBase *h, unsigned idx) { bucket_mark_free(h, prev); n_entries_dec(h); + base_set_dirty(h); } #define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx) @@ -714,6 +745,25 @@ bool set_iterate(Set *s, Iterator *i, void **value) { (idx != IDX_NIL); \ (idx) = hashmap_iterate_entry((h), &(i))) +IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h) { + IteratedCache *cache; + + assert(h); + assert(!h->cached); + + if (h->cached) + return NULL; + + cache = new0(IteratedCache, 1); + if (!cache) + return NULL; + + cache->hashmap = h; + h->cached = true; + + return cache; +} + static void reset_direct_storage(HashmapBase *h) { const struct hashmap_type_info *hi = &hashmap_type_info[h->type]; void *p; @@ -874,6 +924,8 @@ void internal_hashmap_clear(HashmapBase *h) { OrderedHashmap *lh = (OrderedHashmap*) h; lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; } + + base_set_dirty(h); } void internal_hashmap_clear_free(HashmapBase *h) { @@ -1018,6 +1070,8 @@ static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx, h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h)); #endif + base_set_dirty(h); + return 1; } #define hashmap_put_boldly(h, idx, swap, may_resize) \ @@ -1254,6 +1308,8 @@ int hashmap_replace(Hashmap *h, const void *key, void *value) { #endif e->b.key = key; e->value = value; + hashmap_set_dirty(h); + return 0; } @@ -1276,6 +1332,8 @@ int hashmap_update(Hashmap *h, const void *key, void *value) { e = plain_bucket_at(h, idx); e->value = value; + hashmap_set_dirty(h); + return 0; } @@ -1828,3 +1886,95 @@ int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags return r; } } + +/* expand the cachemem if needed, return true if newly (re)activated. */ +static int cachemem_maintain(CacheMem *mem, unsigned size) { + assert(mem); + + if (!GREEDY_REALLOC(mem->ptr, mem->n_allocated, size)) { + if (size > 0) + return -ENOMEM; + } + + if (!mem->active) { + mem->active = true; + return true; + } + + return false; +} + +int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries) { + bool sync_keys = false, sync_values = false; + unsigned size; + int r; + + assert(cache); + assert(cache->hashmap); + + size = n_entries(cache->hashmap); + + if (res_keys) { + r = cachemem_maintain(&cache->keys, size); + if (r < 0) + return r; + + sync_keys = r; + } else + cache->keys.active = false; + + if (res_values) { + r = cachemem_maintain(&cache->values, size); + if (r < 0) + return r; + + sync_values = r; + } else + cache->values.active = false; + + if (cache->hashmap->dirty) { + if (cache->keys.active) + sync_keys = true; + if (cache->values.active) + sync_values = true; + + cache->hashmap->dirty = false; + } + + if (sync_keys || sync_values) { + unsigned i, idx; + Iterator iter; + + i = 0; + HASHMAP_FOREACH_IDX(idx, cache->hashmap, iter) { + struct hashmap_base_entry *e; + + e = bucket_at(cache->hashmap, idx); + + if (sync_keys) + cache->keys.ptr[i] = e->key; + if (sync_values) + cache->values.ptr[i] = entry_value(cache->hashmap, e); + i++; + } + } + + if (res_keys) + *res_keys = cache->keys.ptr; + if (res_values) + *res_values = cache->values.ptr; + if (res_n_entries) + *res_n_entries = size; + + return 0; +} + +IteratedCache *iterated_cache_free(IteratedCache *cache) { + if (cache) { + free(cache->keys.ptr); + free(cache->values.ptr); + free(cache); + } + + return NULL; +} diff --git a/src/systemd/src/basic/hashmap.h b/src/systemd/src/basic/hashmap.h index c1089652..701eecbd 100644 --- a/src/systemd/src/basic/hashmap.h +++ b/src/systemd/src/basic/hashmap.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -5,19 +6,6 @@ Copyright 2010 Lennart Poettering Copyright 2014 Michal Schmidt - - 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 @@ -52,6 +40,8 @@ typedef struct Hashmap Hashmap; /* Maps keys to values */ typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */ typedef struct Set Set; /* Stores just keys */ +typedef struct IteratedCache IteratedCache; /* Caches the iterated order of one of the above */ + /* Ideally the Iterator would be an opaque struct, but it is instantiated * by hashmap users, so the definition has to be here. Do not use its fields * directly. */ @@ -125,6 +115,9 @@ static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h)); } +IteratedCache *iterated_cache_free(IteratedCache *cache); +int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries); + HashmapBase *internal_hashmap_copy(HashmapBase *h); static inline Hashmap *hashmap_copy(Hashmap *h) { return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); @@ -138,6 +131,14 @@ int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct h #define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) #define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h); +static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) { + return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h)); +} +static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) { + return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h)); +} + int hashmap_put(Hashmap *h, const void *key, void *value); static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) { return hashmap_put(PLAIN_HASHMAP(h), key, value); @@ -328,6 +329,29 @@ static inline void *ordered_hashmap_first(OrderedHashmap *h) { return internal_hashmap_first(HASHMAP_BASE(h)); } +#define hashmap_clear_with_destructor(_s, _f) \ + ({ \ + void *_item; \ + while ((_item = hashmap_steal_first(_s))) \ + _f(_item); \ + }) +#define hashmap_free_with_destructor(_s, _f) \ + ({ \ + hashmap_clear_with_destructor(_s, _f); \ + hashmap_free(_s); \ + }) +#define ordered_hashmap_clear_with_destructor(_s, _f) \ + ({ \ + void *_item; \ + while ((_item = ordered_hashmap_steal_first(_s))) \ + _f(_item); \ + }) +#define ordered_hashmap_free_with_destructor(_s, _f) \ + ({ \ + ordered_hashmap_clear_with_destructor(_s, _f); \ + ordered_hashmap_free(_s); \ + }) + /* no hashmap_next */ void *ordered_hashmap_next(OrderedHashmap *h, const void *key); @@ -370,3 +394,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free); #define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep) #define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep) #define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep) + +DEFINE_TRIVIAL_CLEANUP_FUNC(IteratedCache*, iterated_cache_free); + +#define _cleanup_iterated_cache_free_ _cleanup_(iterated_cache_freep) diff --git a/src/systemd/src/basic/hexdecoct.c b/src/systemd/src/basic/hexdecoct.c index e0ae83c1..1fe410f5 100644 --- a/src/systemd/src/basic/hexdecoct.c +++ b/src/systemd/src/basic/hexdecoct.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -91,30 +79,69 @@ char *hexmem(const void *p, size_t l) { return r; } -int unhexmem(const char *p, size_t l, void **mem, size_t *len) { - _cleanup_free_ uint8_t *r = NULL; - uint8_t *z; - const char *x; +static int unhex_next(const char **p, size_t *l) { + int r; - assert(mem); - assert(len); assert(p); + assert(l); - if (l % 2 != 0) - return -EINVAL; + /* Find the next non-whitespace character, and decode it. We + * greedily skip all preceeding and all following whitespace. */ - z = r = malloc((l + 1) / 2 + 1); - if (!r) + for (;;) { + if (*l == 0) + return -EPIPE; + + if (!strchr(WHITESPACE, **p)) + break; + + /* Skip leading whitespace */ + (*p)++, (*l)--; + } + + r = unhexchar(**p); + if (r < 0) + return r; + + for (;;) { + (*p)++, (*l)--; + + if (*l == 0 || !strchr(WHITESPACE, **p)) + break; + + /* Skip following whitespace */ + } + + return r; +} + +int unhexmem(const char *p, size_t l, void **ret, size_t *ret_len) { + _cleanup_free_ uint8_t *buf = NULL; + const char *x; + uint8_t *z; + + assert(ret); + assert(ret_len); + assert(p || l == 0); + + if (l == (size_t) -1) + l = strlen(p); + + /* Note that the calculation of memory size is an upper boundary, as we ignore whitespace while decoding */ + buf = malloc((l + 1) / 2 + 1); + if (!buf) return -ENOMEM; - for (x = p; x < p + l; x += 2) { + for (x = p, z = buf;;) { int a, b; - a = unhexchar(x[0]); + a = unhex_next(&x, &l); + if (a == -EPIPE) /* End of string */ + break; if (a < 0) return a; - b = unhexchar(x[1]); + b = unhex_next(&x, &l); if (b < 0) return b; @@ -123,13 +150,13 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) { *z = 0; - *mem = r; - r = NULL; - *len = (l + 1) / 2; + *ret_len = (size_t) (z - buf); + *ret = TAKE_PTR(buf); return 0; } +#if 0 /* NM_IGNORED */ /* https://tools.ietf.org/html/rfc4648#section-6 * Notice that base32hex differs from base32 in the alphabet it uses. * The distinction is that the base32hex representation preserves the @@ -162,6 +189,8 @@ char *base32hexmem(const void *p, size_t l, bool padding) { const uint8_t *x; size_t len; + assert(p || l == 0); + if (padding) /* five input bytes makes eight output bytes, padding is added so we must round up */ len = 8 * (l + 4) / 5; @@ -191,7 +220,7 @@ char *base32hexmem(const void *p, size_t l, bool padding) { for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) { /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ - x[3] == QQQQQQQQ; x[4] == WWWWWWWW */ + * x[3] == QQQQQQQQ; x[4] == WWWWWWWW */ *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ @@ -271,7 +300,12 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l size_t len; unsigned pad = 0; - assert(p); + assert(p || l == 0); + assert(mem); + assert(_len); + + if (l == (size_t) -1) + l = strlen(p); /* padding ensures any base32hex input has input divisible by 8 */ if (padding && l % 8 != 0) @@ -286,7 +320,7 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l } /* a group of eight input bytes needs five output bytes, in case of - padding we need to add some extra bytes */ + * padding we need to add some extra bytes */ len = (l / 8) * 5; switch (l % 8) { @@ -314,7 +348,7 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l for (x = p; x < p + (l / 8) * 8; x += 8) { /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW - e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */ + * e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */ a = unbase32hexchar(x[0]); if (a < 0) return -EINVAL; @@ -473,8 +507,7 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l *z = 0; - *mem = r; - r = NULL; + *mem = TAKE_PTR(r); *_len = len; return 0; @@ -521,6 +554,9 @@ ssize_t base64mem(const void *p, size_t l, char **out) { char *r, *z; const uint8_t *x; + assert(p || l == 0); + assert(out); + /* three input bytes makes four output bytes, padding is added so we must round up */ z = r = malloc(4 * (l + 2) / 3 + 1); if (!r) @@ -556,10 +592,11 @@ ssize_t base64mem(const void *p, size_t l, char **out) { return z - r; } -static int base64_append_width(char **prefix, int plen, - const char *sep, int indent, - const void *p, size_t l, - int width) { +static int base64_append_width( + char **prefix, int plen, + const char *sep, int indent, + const void *p, size_t l, + int width) { _cleanup_free_ char *x = NULL; char *t, *s; @@ -570,7 +607,7 @@ static int base64_append_width(char **prefix, int plen, if (len <= 0) return len; - lines = (len + width - 1) / width; + lines = DIV_ROUND_UP(len, width); slen = strlen_ptr(sep); t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines); @@ -598,118 +635,145 @@ static int base64_append_width(char **prefix, int plen, return 0; } -int base64_append(char **prefix, int plen, - const void *p, size_t l, - int indent, int width) { +int base64_append( + char **prefix, int plen, + const void *p, size_t l, + int indent, int width) { + if (plen > width / 2 || plen + indent > width) /* leave indent on the left, keep last column free */ return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1); else /* leave plen on the left, keep last column free */ return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1); -}; - +} -int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) { - _cleanup_free_ uint8_t *r = NULL; - int a, b, c, d; - uint8_t *z; - const char *x; - size_t len; +static int unbase64_next(const char **p, size_t *l) { + int ret; assert(p); + assert(l); - /* padding ensures any base63 input has input divisible by 4 */ - if (l % 4 != 0) - return -EINVAL; + /* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We + * greedily skip all preceeding and all following whitespace. */ - /* strip the padding */ - if (l > 0 && p[l - 1] == '=') - l--; - if (l > 0 && p[l - 1] == '=') - l--; + for (;;) { + if (*l == 0) + return -EPIPE; - /* a group of four input bytes needs three output bytes, in case of - padding we need to add two or three extra bytes */ - len = (l / 4) * 3 + (l % 4 ? (l % 4) - 1 : 0); + if (!strchr(WHITESPACE, **p)) + break; - z = r = malloc(len + 1); - if (!r) - return -ENOMEM; + /* Skip leading whitespace */ + (*p)++, (*l)--; + } - for (x = p; x < p + (l / 4) * 4; x += 4) { - /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */ - a = unbase64char(x[0]); - if (a < 0) - return -EINVAL; + if (**p == '=') + ret = INT_MAX; /* return padding as INT_MAX */ + else { + ret = unbase64char(**p); + if (ret < 0) + return ret; + } - b = unbase64char(x[1]); - if (b < 0) - return -EINVAL; + for (;;) { + (*p)++, (*l)--; - c = unbase64char(x[2]); - if (c < 0) - return -EINVAL; - - d = unbase64char(x[3]); - if (d < 0) - return -EINVAL; + if (*l == 0) + break; + if (!strchr(WHITESPACE, **p)) + break; - *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ - *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ - *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */ + /* Skip following whitespace */ } - switch (l % 4) { - case 3: - a = unbase64char(x[0]); + return ret; +} + +int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) { + _cleanup_free_ uint8_t *buf = NULL; + const char *x; + uint8_t *z; + size_t len; + + assert(p || l == 0); + assert(ret); + assert(ret_size); + + if (l == (size_t) -1) + l = strlen(p); + + /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra + * bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */ + len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0); + + buf = malloc(len + 1); + if (!buf) + return -ENOMEM; + + for (x = p, z = buf;;) { + int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */ + + a = unbase64_next(&x, &l); + if (a == -EPIPE) /* End of string */ + break; if (a < 0) + return a; + if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */ return -EINVAL; - b = unbase64char(x[1]); + b = unbase64_next(&x, &l); if (b < 0) + return b; + if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */ return -EINVAL; - c = unbase64char(x[2]); + c = unbase64_next(&x, &l); if (c < 0) - return -EINVAL; + return c; - /* c == 00ZZZZ00 */ - if (c & 3) - return -EINVAL; + d = unbase64_next(&x, &l); + if (d < 0) + return d; - *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ - *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ + if (c == INT_MAX) { /* Padding at the third character */ - break; - case 2: - a = unbase64char(x[0]); - if (a < 0) - return -EINVAL; + if (d != INT_MAX) /* If the third character is padding, the fourth must be too */ + return -EINVAL; - b = unbase64char(x[1]); - if (b < 0) - return -EINVAL; + /* b == 00YY0000 */ + if (b & 15) + return -EINVAL; - /* b == 00YY0000 */ - if (b & 15) - return -EINVAL; + if (l > 0) /* Trailing rubbish? */ + return -ENAMETOOLONG; - *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ + *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ + break; + } - break; - case 0: + if (d == INT_MAX) { + /* c == 00ZZZZ00 */ + if (c & 3) + return -EINVAL; - break; - default: - return -EINVAL; + if (l > 0) /* Trailing rubbish? */ + return -ENAMETOOLONG; + + *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ + *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ + break; + } + + *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ + *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ + *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */ } *z = 0; - *mem = r; - r = NULL; - *_len = len; + *ret_size = (size_t) (z - buf); + *ret = TAKE_PTR(buf); return 0; } @@ -718,7 +782,10 @@ void hexdump(FILE *f, const void *p, size_t s) { const uint8_t *b = p; unsigned n = 0; - assert(s == 0 || b); + assert(b || s == 0); + + if (!f) + f = stdout; while (s > 0) { size_t i; @@ -756,3 +823,4 @@ void hexdump(FILE *f, const void *p, size_t s) { s -= 16; } } +#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/hexdecoct.h b/src/systemd/src/basic/hexdecoct.h index 1ba2f69e..c2f11013 100644 --- a/src/systemd/src/basic/hexdecoct.h +++ b/src/systemd/src/basic/hexdecoct.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 diff --git a/src/systemd/src/basic/hostname-util.c b/src/systemd/src/basic/hostname-util.c index be6e9e58..0feeaa5d 100644 --- a/src/systemd/src/basic/hostname-util.c +++ b/src/systemd/src/basic/hostname-util.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2015 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -26,12 +14,15 @@ #include #include +#include "alloc-util.h" +#include "def.h" #include "fd-util.h" #include "fileio.h" #include "hostname-util.h" #include "macro.h" #include "string-util.h" +#if 0 /* NM_IGNORED */ bool hostname_is_set(void) { struct utsname u; @@ -47,7 +38,6 @@ bool hostname_is_set(void) { return true; } -#if 0 /* NM_IGNORED */ char* gethostname_malloc(void) { struct utsname u; @@ -223,36 +213,88 @@ int sethostname_idempotent(const char *s) { return 1; } -int read_hostname_config(const char *path, char **hostname) { - _cleanup_fclose_ FILE *f = NULL; - char l[LINE_MAX]; - char *name = NULL; +int shorten_overlong(const char *s, char **ret) { + char *h, *p; - assert(path); - assert(hostname); + /* Shorten an overlong name to HOST_NAME_MAX or to the first dot, + * whatever comes earlier. */ - f = fopen(path, "re"); - if (!f) - return -errno; + assert(s); + + h = strdup(s); + if (!h) + return -ENOMEM; + + if (hostname_is_valid(h, false)) { + *ret = h; + return 0; + } + + p = strchr(h, '.'); + if (p) + *p = 0; + + strshorten(h, HOST_NAME_MAX); + + if (!hostname_is_valid(h, false)) { + free(h); + return -EDOM; + } + + *ret = h; + return 1; +} + +int read_etc_hostname_stream(FILE *f, char **ret) { + int r; + + assert(f); + assert(ret); + + for (;;) { + _cleanup_free_ char *line = NULL; + char *p; + + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return r; + if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */ + return -ENOENT; - /* may have comments, ignore them */ - FOREACH_LINE(l, f, return -errno) { - truncate_nl(l); - if (!IN_SET(l[0], '\0', '#')) { - /* found line with value */ - name = hostname_cleanup(l); - name = strdup(name); - if (!name) + p = strstrip(line); + + /* File may have empty lines or comments, ignore them */ + if (!IN_SET(*p, '\0', '#')) { + char *copy; + + hostname_cleanup(p); /* normalize the hostname */ + + if (!hostname_is_valid(p, true)) /* check that the hostname we return is valid */ + return -EBADMSG; + + copy = strdup(p); + if (!copy) return -ENOMEM; - break; + + *ret = copy; + return 0; } } +} - if (!name) - /* no non-empty line found */ - return -ENOENT; +int read_etc_hostname(const char *path, char **ret) { + _cleanup_fclose_ FILE *f = NULL; + + assert(ret); + + if (!path) + path = "/etc/hostname"; + + f = fopen(path, "re"); + if (!f) + return -errno; + + return read_etc_hostname_stream(f, ret); - *hostname = name; - return 0; } #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/hostname-util.h b/src/systemd/src/basic/hostname-util.h index 7af4e6c7..8e29e9e1 100644 --- a/src/systemd/src/basic/hostname-util.h +++ b/src/systemd/src/basic/hostname-util.h @@ -1,25 +1,14 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010-2015 Lennart Poettering - - 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 "macro.h" @@ -38,4 +27,7 @@ bool is_gateway_hostname(const char *hostname); int sethostname_idempotent(const char *s); -int read_hostname_config(const char *path, char **hostname); +int shorten_overlong(const char *s, char **ret); + +int read_etc_hostname_stream(FILE *f, char **ret); +int read_etc_hostname(const char *path, char **ret); diff --git a/src/systemd/src/basic/in-addr-util.c b/src/systemd/src/basic/in-addr-util.c index 2a02d90b..65748615 100644 --- a/src/systemd/src/basic/in-addr-util.c +++ b/src/systemd/src/basic/in-addr-util.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2014 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -332,6 +320,7 @@ int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union return -EINVAL; } +#if 0 /* NM_IGNORED */ int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) { const char *suffix; int r, ifi = 0; @@ -372,6 +361,7 @@ int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_u return r; } +#endif /* NM_IGNORED */ unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) { assert(addr); diff --git a/src/systemd/src/basic/in-addr-util.h b/src/systemd/src/basic/in-addr-util.h index 59f8eb7e..acd567c9 100644 --- a/src/systemd/src/basic/in-addr-util.h +++ b/src/systemd/src/basic/in-addr-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2014 Lennart Poettering - - 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 diff --git a/src/systemd/src/basic/io-util.c b/src/systemd/src/basic/io-util.c index 61b667f0..1bc5c345 100644 --- a/src/systemd/src/basic/io-util.c +++ b/src/systemd/src/basic/io-util.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -34,6 +22,7 @@ int flush_fd(int fd) { .fd = fd, .events = POLLIN, }; + int count = 0; /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything * read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read @@ -53,7 +42,7 @@ int flush_fd(int fd) { return -errno; } else if (r == 0) - return 0; + return count; l = read(fd, buf, sizeof(buf)); if (l < 0) { @@ -62,11 +51,13 @@ int flush_fd(int fd) { continue; if (errno == EAGAIN) - return 0; + return count; return -errno; } else if (l == 0) - return 0; + return count; + + count += (int) l; } } @@ -135,7 +126,7 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { assert(fd >= 0); assert(buf); - if (nbytes > (size_t) SSIZE_MAX) + if (_unlikely_(nbytes > (size_t) SSIZE_MAX)) return -EINVAL; do { @@ -201,7 +192,6 @@ int fd_wait_for_event(int fd, int event, usec_t t) { r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL); if (r < 0) return -errno; - if (r == 0) return 0; diff --git a/src/systemd/src/basic/io-util.h b/src/systemd/src/basic/io-util.h index d9b69add..e4717b6f 100644 --- a/src/systemd/src/basic/io-util.h +++ b/src/systemd/src/basic/io-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 @@ -40,9 +28,8 @@ int fd_wait_for_event(int fd, int event, usec_t timeout); ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length); -static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) { - unsigned j; - size_t r = 0; +static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, size_t n) { + size_t j, r = 0; for (j = 0; j < n; j++) r += i[j].iov_len; @@ -50,8 +37,8 @@ static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) { return r; } -static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) { - unsigned j; +static inline size_t IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) { + size_t j; for (j = 0; j < n; j++) { size_t sub; diff --git a/src/systemd/src/basic/list.h b/src/systemd/src/basic/list.h index c3771a17..3ec7e73f 100644 --- a/src/systemd/src/basic/list.h +++ b/src/systemd/src/basic/list.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 . ***/ /* The head of the linked list. Use this in the structure that shall @@ -182,3 +170,6 @@ for ((i) = (p)->name##_next ? (p)->name##_next : (head); \ (i) != (p); \ (i) = (i)->name##_next ? (i)->name##_next : (head)) + +#define LIST_IS_EMPTY(head) \ + (!(head)) diff --git a/src/systemd/src/basic/log.h b/src/systemd/src/basic/log.h index 67fda3f9..e7d7c8b1 100644 --- a/src/systemd/src/basic/log.h +++ b/src/systemd/src/basic/log.h @@ -1,36 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . ***/ -#include #include #include #include -#include -#include #include -#include "sd-id128.h" - #include "macro.h" -#include "process-util.h" + +/* Some structures we reference but don't want to pull in headers for */ +struct iovec; +struct signalfd_siginfo; typedef enum LogRealm { LOG_REALM_SYSTEMD, @@ -51,7 +37,6 @@ typedef enum LogTarget{ LOG_TARGET_SYSLOG, LOG_TARGET_SYSLOG_OR_KMSG, LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */ - LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */ LOG_TARGET_NULL, _LOG_TARGET_MAX, _LOG_TARGET_INVALID = -1 @@ -98,11 +83,6 @@ int log_open(void); void log_close(void); void log_forget_fds(void); -void log_close_syslog(void); -void log_close_journal(void); -void log_close_kmsg(void); -void log_close_console(void); - void log_parse_environment_realm(LogRealm realm); #define log_parse_environment() \ log_parse_environment_realm(LOG_REALM) @@ -156,19 +136,6 @@ int log_object_internal( const char *extra, const char *format, ...) _printf_(10,11); -int log_object_internalv( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *extra_field, - const char *extra, - const char *format, - va_list ap) _printf_(10,0); - int log_struct_internal( int level, int error, @@ -185,8 +152,8 @@ int log_oom_internal( int log_format_iovec( struct iovec *iovec, - unsigned iovec_len, - unsigned *n, + size_t iovec_len, + size_t *n, bool newline_separator, int error, const char *format, @@ -198,7 +165,7 @@ int log_struct_iovec_internal( const char *file, int line, const char *func, - const struct iovec input_iovec[], + const struct iovec *input_iovec, size_t n_input_iovec); /* This modifies the buffer passed! */ @@ -211,7 +178,7 @@ int log_dump_internal( char *buffer); /* Logging for various assertions */ -noreturn void log_assert_failed_realm( +_noreturn_ void log_assert_failed_realm( LogRealm realm, const char *text, const char *file, @@ -220,7 +187,7 @@ noreturn void log_assert_failed_realm( #define log_assert_failed(text, ...) \ log_assert_failed_realm(LOG_REALM, (text), __VA_ARGS__) -noreturn void log_assert_failed_unreachable_realm( +_noreturn_ void log_assert_failed_unreachable_realm( LogRealm realm, const char *text, const char *file, @@ -245,9 +212,9 @@ void log_assert_failed_return_realm( /* Logging with level */ #define log_full_errno_realm(realm, level, error, ...) \ ({ \ - int _level = (level), _e = (error); \ - (log_get_max_level_realm((realm)) >= LOG_PRI(_level)) \ - ? log_internal_realm(LOG_REALM_PLUS_LEVEL((realm), _level), _e, \ + int _level = (level), _e = (error), _realm = (realm); \ + (log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \ + ? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \ __FILE__, __LINE__, __func__, __VA_ARGS__) \ : -abs(_e); \ }) @@ -257,13 +224,15 @@ void log_assert_failed_return_realm( #define log_full(level, ...) log_full_errno((level), 0, __VA_ARGS__) +int log_emergency_level(void); + /* Normal logging */ #define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) #define log_info(...) log_full(LOG_INFO, __VA_ARGS__) #define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) #define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) #define log_error(...) log_full(LOG_ERR, __VA_ARGS__) -#define log_emergency(...) log_full(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__) +#define log_emergency(...) log_full(log_emergency_level(), __VA_ARGS__) /* Logging triggered by an errno-like error */ #define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__) @@ -271,7 +240,7 @@ void log_assert_failed_return_realm( #define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__) #define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__) #define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) -#define log_emergency_errno(error, ...) log_full_errno(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__) +#define log_emergency_errno(error, ...) log_full_errno(log_emergency_level(), error, __VA_ARGS__) #ifdef LOG_TRACE # define log_trace(...) log_debug(__VA_ARGS__) @@ -307,10 +276,20 @@ LogTarget log_target_from_string(const char *s) _pure_; void log_received_signal(int level, const struct signalfd_siginfo *si); +/* If turned on, any requests for a log target involving "syslog" will be implicitly upgraded to the equivalent journal target */ void log_set_upgrade_syslog_to_journal(bool b); + +/* If turned on, and log_open() is called, we'll not use STDERR_FILENO for logging ever, but rather open /dev/console */ void log_set_always_reopen_console(bool b); + +/* If turned on, we'll open the log stream implicitly if needed on each individual log call. This is normally not + * desired as we want to reuse our logging streams. It is useful however */ void log_set_open_when_needed(bool b); +/* If turned on, then we'll never use IPC-based logging, i.e. never log to syslog or the journal. We'll only log to + * stderr, the console or kmsg */ +void log_set_prohibit_ipc(bool b); + int log_syntax_internal( const char *unit, int level, @@ -322,6 +301,16 @@ int log_syntax_internal( const char *func, const char *format, ...) _printf_(9, 10); +int log_syntax_invalid_utf8_internal( + const char *unit, + int level, + const char *config_file, + unsigned config_line, + const char *file, + int line, + const char *func, + const char *rvalue); + #define log_syntax(unit, level, config_file, config_line, error, ...) \ ({ \ int _level = (level), _e = (error); \ @@ -333,10 +322,9 @@ int log_syntax_internal( #define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \ ({ \ int _level = (level); \ - if (log_get_max_level() >= LOG_PRI(_level)) { \ - _cleanup_free_ char *_p = NULL; \ - _p = utf8_escape_invalid(rvalue); \ - log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \ - "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \ - } \ + (log_get_max_level() >= LOG_PRI(_level)) \ + ? log_syntax_invalid_utf8_internal(unit, _level, config_file, config_line, __FILE__, __LINE__, __func__, rvalue) \ + : -EINVAL; \ }) + +#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG) diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h index afcde459..c89fb84e 100644 --- a/src/systemd/src/basic/macro.h +++ b/src/systemd/src/basic/macro.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 @@ -47,6 +35,31 @@ #define _weakref_(x) __attribute__((weakref(#x))) #define _alignas_(x) __attribute__((aligned(__alignof(x)))) #define _cleanup_(x) __attribute__((cleanup(x))) +#if __GNUC__ >= 7 +#define _fallthrough_ __attribute__((fallthrough)) +#else +#define _fallthrough_ +#endif +/* Define C11 noreturn without and even on older gcc + * compiler versions */ +#ifndef _noreturn_ +#if __STDC_VERSION__ >= 201112L +#define _noreturn_ _Noreturn +#else +#define _noreturn_ __attribute__((noreturn)) +#endif +#endif + +#if !defined(HAS_FEATURE_MEMORY_SANITIZER) +# if defined(__has_feature) +# if __has_feature(memory_sanitizer) +# define HAS_FEATURE_MEMORY_SANITIZER 1 +# endif +# endif +# if !defined(HAS_FEATURE_MEMORY_SANITIZER) +# define HAS_FEATURE_MEMORY_SANITIZER 0 +# endif +#endif #if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__) /* Temporarily disable some warnings */ @@ -142,11 +155,25 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL)); } +#ifndef __COVERITY__ +# define VOID_0 ((void)0) +#else +# define VOID_0 ((void*)0) +#endif + #define ELEMENTSOF(x) \ __extension__ (__builtin_choose_expr( \ !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ sizeof(x)/sizeof((x)[0]), \ - (void)0)) + VOID_0)) + +/* + * STRLEN - return the length of a string literal, minus the trailing NUL byte. + * Contrary to strlen(), this is a constant expression. + * @x: a string literal. + */ +#define STRLEN(x) (sizeof(""x"") - 1) + /* * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. @@ -176,7 +203,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { __builtin_constant_p(_B) && \ __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ ((_A) > (_B)) ? (_A) : (_B), \ - (void)0)) + VOID_0)) /* takes two types and returns the size of the larger one */ #define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; })) @@ -332,7 +359,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { ({ \ typeof(x) _x_ = (x); \ unsigned ans = 1; \ - while (_x_ /= 10) \ + while ((_x_ /= 10) != 0) \ ans++; \ ans; \ }) @@ -403,21 +430,10 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { #endif #endif -/* Define C11 noreturn without and even on older gcc - * compiler versions */ -#ifndef noreturn -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L -#define noreturn _Noreturn -#else -#define noreturn __attribute__((noreturn)) -#endif -#endif - #define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ static inline void func##p(type *p) { \ if (*p) \ func(*p); \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ + } #include "log.h" diff --git a/src/systemd/src/basic/mempool.c b/src/systemd/src/basic/mempool.c index c1635964..07096766 100644 --- a/src/systemd/src/basic/mempool.c +++ b/src/systemd/src/basic/mempool.c @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010-2014 Lennart Poettering Copyright 2014 Michal Schmidt - - 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 "nm-sd-adapt.h" @@ -29,12 +17,12 @@ struct pool { struct pool *next; - unsigned n_tiles; - unsigned n_used; + size_t n_tiles; + size_t n_used; }; void* mempool_alloc_tile(struct mempool *mp) { - unsigned i; + size_t i; /* When a tile is released we add it to the list and simply * place the next pointer at its offset 0. */ @@ -52,8 +40,7 @@ void* mempool_alloc_tile(struct mempool *mp) { if (_unlikely_(!mp->first_pool) || _unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) { - unsigned n; - size_t size; + size_t size, n; struct pool *p; n = mp->first_pool ? mp->first_pool->n_tiles : 0; @@ -91,7 +78,7 @@ void mempool_free_tile(struct mempool *mp, void *p) { mp->freelist = p; } -#ifdef VALGRIND +#if VALGRIND void mempool_drop(struct mempool *mp) { struct pool *p = mp->first_pool; diff --git a/src/systemd/src/basic/mempool.h b/src/systemd/src/basic/mempool.h index 0618b8dd..68249cd8 100644 --- a/src/systemd/src/basic/mempool.h +++ b/src/systemd/src/basic/mempool.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -5,19 +6,6 @@ Copyright 2011-2014 Lennart Poettering Copyright 2014 Michal Schmidt - - 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 @@ -42,6 +30,6 @@ static struct mempool pool_name = { \ } -#ifdef VALGRIND +#if VALGRIND void mempool_drop(struct mempool *mp); #endif diff --git a/src/systemd/src/basic/parse-util.c b/src/systemd/src/basic/parse-util.c index 6d978e93..ae43a041 100644 --- a/src/systemd/src/basic/parse-util.c +++ b/src/systemd/src/basic/parse-util.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -25,10 +13,14 @@ #include #include #include +#include #include "alloc-util.h" +#include "errno-list.h" #include "extract-word.h" +#include "locale-util.h" #include "macro.h" +#include "missing.h" #include "parse-util.h" #include "process-util.h" #include "string-util.h" @@ -68,7 +60,6 @@ int parse_pid(const char *s, pid_t* ret_pid) { *ret_pid = pid; return 0; } -#endif /* NM_IGNORED */ int parse_mode(const char *s, mode_t *ret) { char *x; @@ -85,7 +76,7 @@ int parse_mode(const char *s, mode_t *ret) { l = strtol(s, &x, 8); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; if (l < 0 || l > 07777) return -ERANGE; @@ -107,6 +98,30 @@ int parse_ifindex(const char *s, int *ret) { return 0; } +int parse_mtu(int family, const char *s, uint32_t *ret) { + uint64_t u; + size_t m; + int r; + + r = parse_size(s, 1024, &u); + if (r < 0) + return r; + + if (u > UINT32_MAX) + return -ERANGE; + + if (family == AF_INET6) + m = IPV6_MIN_MTU; /* This is 1280 */ + else + m = IPV4_MIN_MTU; /* For all other protocols, including 'unspecified' we assume the IPv4 minimal MTU */ + + if (u < m) + return -ERANGE; + + *ret = (uint32_t) u; + return 0; +} + int parse_size(const char *t, uint64_t base, uint64_t *size) { /* Soo, sometimes we want to parse IEC binary suffixes, and @@ -235,7 +250,6 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) { return 0; } -#if 0 /* NM_IGNORED */ int parse_range(const char *t, unsigned *lower, unsigned *upper) { _cleanup_free_ char *word = NULL; unsigned l, u; @@ -272,7 +286,64 @@ int parse_range(const char *t, unsigned *lower, unsigned *upper) { *upper = u; return 0; } -#endif /* NM_IGNORED */ + +int parse_errno(const char *t) { + int r, e; + + assert(t); + + r = errno_from_name(t); + if (r > 0) + return r; + + r = safe_atoi(t, &e); + if (r < 0) + return r; + + /* 0 is also allowed here */ + if (!errno_is_valid(e) && e != 0) + return -ERANGE; + + return e; +} + +int parse_syscall_and_errno(const char *in, char **name, int *error) { + _cleanup_free_ char *n = NULL; + char *p; + int e = -1; + + assert(in); + assert(name); + assert(error); + + /* + * This parse "syscall:errno" like "uname:EILSEQ", "@sync:255". + * If errno is omitted, then error is set to -1. + * Empty syscall name is not allowed. + * Here, we do not check that the syscall name is valid or not. + */ + + p = strchr(in, ':'); + if (p) { + e = parse_errno(p + 1); + if (e < 0) + return e; + + n = strndup(in, p - in); + } else + n = strdup(in); + + if (!n) + return -ENOMEM; + + if (isempty(n)) + return -EINVAL; + + *error = e; + *name = TAKE_PTR(n); + + return 0; +} char *format_bytes(char *buf, size_t l, uint64_t t) { unsigned i; @@ -314,13 +385,15 @@ finish: return buf; } +#endif /* NM_IGNORED */ -int safe_atou(const char *s, unsigned *ret_u) { +int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { char *x = NULL; unsigned long l; assert(s); assert(ret_u); + assert(base <= 16); /* strtoul() is happy to parse negative values, and silently * converts them to unsigned values without generating an @@ -333,10 +406,10 @@ int safe_atou(const char *s, unsigned *ret_u) { s += strspn(s, WHITESPACE); errno = 0; - l = strtoul(s, &x, 0); + l = strtoul(s, &x, base); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; if (s[0] == '-') return -ERANGE; @@ -358,7 +431,7 @@ int safe_atoi(const char *s, int *ret_i) { l = strtol(s, &x, 0); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; if ((long) (int) l != l) return -ERANGE; @@ -380,7 +453,7 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) { l = strtoull(s, &x, 0); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; if (*s == '-') return -ERANGE; @@ -400,7 +473,7 @@ int safe_atolli(const char *s, long long int *ret_lli) { l = strtoll(s, &x, 0); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; *ret_lli = l; @@ -420,7 +493,7 @@ int safe_atou8(const char *s, uint8_t *ret) { l = strtoul(s, &x, 0); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; if (s[0] == '-') return -ERANGE; @@ -431,20 +504,21 @@ int safe_atou8(const char *s, uint8_t *ret) { return 0; } -int safe_atou16(const char *s, uint16_t *ret) { +int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { char *x = NULL; unsigned long l; assert(s); assert(ret); + assert(base <= 16); s += strspn(s, WHITESPACE); errno = 0; - l = strtoul(s, &x, 0); + l = strtoul(s, &x, base); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; if (s[0] == '-') return -ERANGE; @@ -466,7 +540,7 @@ int safe_atoi16(const char *s, int16_t *ret) { l = strtol(s, &x, 0); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; if ((long) (int16_t) l != l) return -ERANGE; @@ -475,10 +549,11 @@ int safe_atoi16(const char *s, int16_t *ret) { return 0; } +#if 0 /* NM_IGNORED */ int safe_atod(const char *s, double *ret_d) { + _cleanup_(freelocalep) locale_t loc = (locale_t) 0; char *x = NULL; double d = 0; - locale_t loc; assert(s); assert(ret_d); @@ -489,16 +564,11 @@ int safe_atod(const char *s, double *ret_d) { errno = 0; d = strtod_l(s, &x, loc); - if (errno > 0) { - freelocale(loc); + if (errno > 0) return -errno; - } - if (!x || x == s || *x) { - freelocale(loc); + if (!x || x == s || *x != 0) return -EINVAL; - } - freelocale(loc); *ret_d = (double) d; return 0; } @@ -541,19 +611,20 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { int parse_percent_unbounded(const char *p) { const char *pc, *n; - unsigned v; - int r; + int r, v; pc = endswith(p, "%"); if (!pc) return -EINVAL; n = strndupa(p, pc - p); - r = safe_atou(n, &v); + r = safe_atoi(n, &v); if (r < 0) return r; + if (v < 0) + return -ERANGE; - return (int) v; + return v; } int parse_percent(const char *p) { @@ -566,7 +637,6 @@ int parse_percent(const char *p) { return v; } -#if 0 /* NM_IGNORED */ int parse_nice(const char *p, int *ret) { int n, r; @@ -611,4 +681,21 @@ int parse_dev(const char *s, dev_t *ret) { *ret = d; return 0; } + +int parse_oom_score_adjust(const char *s, int *ret) { + int r, v; + + assert(s); + assert(ret); + + r = safe_atoi(s, &v); + if (r < 0) + return r; + + if (v < OOM_SCORE_ADJ_MIN || v > OOM_SCORE_ADJ_MAX) + return -ERANGE; + + *ret = v; + return 0; +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/parse-util.h b/src/systemd/src/basic/parse-util.h index dc09782c..2b75b938 100644 --- a/src/systemd/src/basic/parse-util.h +++ b/src/systemd/src/basic/parse-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 @@ -34,21 +22,38 @@ int parse_dev(const char *s, dev_t *ret); int parse_pid(const char *s, pid_t* ret_pid); int parse_mode(const char *s, mode_t *ret); int parse_ifindex(const char *s, int *ret); +int parse_mtu(int family, const char *s, uint32_t *ret); int parse_size(const char *t, uint64_t base, uint64_t *size); int parse_range(const char *t, unsigned *lower, unsigned *upper); +int parse_errno(const char *t); +int parse_syscall_and_errno(const char *in, char **name, int *error); #define FORMAT_BYTES_MAX 8 char *format_bytes(char *buf, size_t l, uint64_t t); -int safe_atou(const char *s, unsigned *ret_u); +int safe_atou_full(const char *s, unsigned base, unsigned *ret_u); + +static inline int safe_atou(const char *s, unsigned *ret_u) { + return safe_atou_full(s, 0, ret_u); +} + int safe_atoi(const char *s, int *ret_i); int safe_atollu(const char *s, unsigned long long *ret_u); int safe_atolli(const char *s, long long int *ret_i); int safe_atou8(const char *s, uint8_t *ret); -int safe_atou16(const char *s, uint16_t *ret); +int safe_atou16_full(const char *s, unsigned base, uint16_t *ret); + +static inline int safe_atou16(const char *s, uint16_t *ret) { + return safe_atou16_full(s, 0, ret); +} + +static inline int safe_atoux16(const char *s, uint16_t *ret) { + return safe_atou16_full(s, 16, ret); +} + int safe_atoi16(const char *s, int16_t *ret); static inline int safe_atou32(const char *s, uint32_t *ret_u) { @@ -113,3 +118,5 @@ int parse_percent(const char *p); int parse_nice(const char *p, int *ret); int parse_ip_port(const char *s, uint16_t *ret); + +int parse_oom_score_adjust(const char *s, int *ret); diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c index 1bdcc653..567fc61c 100644 --- a/src/systemd/src/basic/path-util.c +++ b/src/systemd/src/basic/path-util.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010-2012 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -83,14 +71,36 @@ char *path_make_absolute(const char *p, const char *prefix) { /* Makes every item in the list an absolute path by prepending * the prefix, if specified and necessary */ - if (path_is_absolute(p) || !prefix) + if (path_is_absolute(p) || isempty(prefix)) return strdup(p); - return strjoin(prefix, "/", p); + if (endswith(prefix, "/")) + return strjoin(prefix, p); + else + return strjoin(prefix, "/", p); +} + +int safe_getcwd(char **ret) { + char *cwd; + + cwd = get_current_dir_name(); + if (!cwd) + return negative_errno(); + + /* Let's make sure the directory is really absolute, to protect us from the logic behind + * CVE-2018-1000001 */ + if (cwd[0] != '/') { + free(cwd); + return -ENOMEDIUM; + } + + *ret = cwd; + return 0; } int path_make_absolute_cwd(const char *p, char **ret) { char *c; + int r; assert(p); assert(ret); @@ -103,11 +113,14 @@ int path_make_absolute_cwd(const char *p, char **ret) { else { _cleanup_free_ char *cwd = NULL; - cwd = get_current_dir_name(); - if (!cwd) - return negative_errno(); + r = safe_getcwd(&cwd); + if (r < 0) + return r; - c = strjoin(cwd, "/", p); + if (endswith(cwd, "/")) + c = strjoin(cwd, p); + else + c = strjoin(cwd, "/", p); } if (!c) return -ENOMEM; @@ -225,8 +238,8 @@ int path_strv_make_absolute_cwd(char **l) { if (r < 0) return r; - free(*s); - *s = t; + path_kill_slashes(t); + free_and_replace(*s, t); } return 0; @@ -267,8 +280,7 @@ char **path_strv_resolve(char **l, const char *root) { r = chase_symlinks(t, root, 0, &u); if (r == -ENOENT) { if (root) { - u = orig; - orig = NULL; + u = TAKE_PTR(orig); free(t); } else u = t; @@ -542,7 +554,7 @@ bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool upd assert(timestamp); - if (paths == NULL) + if (!paths) return false; STRV_FOREACH(i, paths) { @@ -633,7 +645,7 @@ char *prefix_root(const char *root, const char *path) { while (path[0] == '/' && path[1] == '/') path++; - if (isempty(root) || path_equal(root, "/")) + if (empty_or_root(root)) return strdup(path); l = strlen(root) + 1 + strlen(path) + 1; @@ -678,11 +690,12 @@ int parse_path_argument_and_warn(const char *path, bool suppress_root, char **ar return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path); path_kill_slashes(p); - if (suppress_root && path_equal(p, "/")) + if (suppress_root && empty_or_root(p)) p = mfree(p); free(*arg); *arg = p; + return 0; } @@ -706,6 +719,44 @@ char* dirname_malloc(const char *path) { return dir2; } + +const char *last_path_component(const char *path) { + + /* Finds the last component of the path, preserving the optional trailing slash that signifies a directory. + * + * a/b/c → c + * a/b/c/ → c/ + * x → x + * x/ → x/ + * /y → y + * /y/ → y/ + * / → / + * // → / + * /foo/a → a + * /foo/a/ → a/ + * + * Also, the empty string is mapped to itself. + * + * This is different than basename(), which returns "" when a trailing slash is present. + */ + + unsigned l, k; + + l = k = strlen(path); + if (l == 0) /* special case — an empty string */ + return path; + + while (k > 0 && path[k-1] == '/') + k--; + + if (k == 0) /* the root directory */ + return path + l - 1; + + while (k > 0 && path[k-1] != '/') + k--; + + return path + k; +} #endif /* NM_IGNORED */ bool filename_is_valid(const char *p) { @@ -728,7 +779,7 @@ bool filename_is_valid(const char *p) { } #if 0 /* NM_IGNORED */ -bool path_is_safe(const char *p) { +bool path_is_normalized(const char *p) { if (isempty(p)) return false; @@ -742,7 +793,6 @@ bool path_is_safe(const char *p) { if (strlen(p)+1 > PATH_MAX) return false; - /* The following two checks are not really dangerous, but hey, they still are confusing */ if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) return false; @@ -858,7 +908,9 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version) * for Gentoo which does a merge without making /lib a symlink. */ "lib/systemd/libsystemd-shared-*.so\0" - "usr/lib/systemd/libsystemd-shared-*.so\0") { + "lib64/systemd/libsystemd-shared-*.so\0" + "usr/lib/systemd/libsystemd-shared-*.so\0" + "usr/lib64/systemd/libsystemd-shared-*.so\0") { _cleanup_strv_free_ char **names = NULL; _cleanup_free_ char *path = NULL; @@ -922,3 +974,14 @@ bool dot_or_dot_dot(const char *path) { return path[2] == 0; } + +bool empty_or_root(const char *root) { + + /* For operations relative to some root directory, returns true if the specified root directory is redundant, + * i.e. either / or NULL or the empty string or any equivalent. */ + + if (!root) + return true; + + return root[strspn(root, "/")] == 0; +} diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h index 399ed5f9..79db819e 100644 --- a/src/systemd/src/basic/path-util.h +++ b/src/systemd/src/basic/path-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010-2012 Lennart Poettering - - 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 @@ -28,13 +16,32 @@ #include "time-util.h" #if 0 /* NM_IGNORED */ -#define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" -#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":/sbin:/bin" +#define PATH_SPLIT_SBIN_BIN(x) x "sbin:" x "bin" +#define PATH_SPLIT_SBIN_BIN_NULSTR(x) x "sbin\0" x "bin\0" + +#define PATH_NORMAL_SBIN_BIN(x) x "bin" +#define PATH_NORMAL_SBIN_BIN_NULSTR(x) x "bin\0" + +#if HAVE_SPLIT_BIN +# define PATH_SBIN_BIN(x) PATH_SPLIT_SBIN_BIN(x) +# define PATH_SBIN_BIN_NULSTR(x) PATH_SPLIT_SBIN_BIN_NULSTR(x) +#else +# define PATH_SBIN_BIN(x) PATH_NORMAL_SBIN_BIN(x) +# define PATH_SBIN_BIN_NULSTR(x) PATH_NORMAL_SBIN_BIN_NULSTR(x) +#endif + +#define DEFAULT_PATH_NORMAL PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/") +#define DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/usr/local/") PATH_SBIN_BIN_NULSTR("/usr/") +#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":" PATH_SBIN_BIN("/") +#define DEFAULT_PATH_SPLIT_USR_NULSTR DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/") +#define DEFAULT_PATH_COMPAT PATH_SPLIT_SBIN_BIN("/usr/local/") ":" PATH_SPLIT_SBIN_BIN("/usr/") ":" PATH_SPLIT_SBIN_BIN("/") #if HAVE_SPLIT_USR # define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR +# define DEFAULT_PATH_NULSTR DEFAULT_PATH_SPLIT_USR_NULSTR #else # define DEFAULT_PATH DEFAULT_PATH_NORMAL +# define DEFAULT_PATH_NULSTR DEFAULT_PATH_NORMAL_NULSTR #endif #endif /* NM_IGNORED */ @@ -42,6 +49,7 @@ bool is_path(const char *p) _pure_; int path_split_and_make_absolute(const char *p, char ***ret); bool path_is_absolute(const char *p) _pure_; char* path_make_absolute(const char *p, const char *prefix); +int safe_getcwd(char **ret); int path_make_absolute_cwd(const char *p, char **ret); int path_make_relative(const char *from_dir, const char *to_path, char **_r); char* path_kill_slashes(char *path); @@ -112,7 +120,7 @@ char *prefix_root(const char *root, const char *path); size_t _l; \ while (_path[0] == '/' && _path[1] == '/') \ _path ++; \ - if (isempty(_root) || path_equal(_root, "/")) \ + if (empty_or_root(_root)) \ _ret = _path; \ else { \ _l = strlen(_root) + 1 + strlen(_path) + 1; \ @@ -131,9 +139,10 @@ char *prefix_root(const char *root, const char *path); int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg); char* dirname_malloc(const char *path); +const char *last_path_component(const char *path); bool filename_is_valid(const char *p) _pure_; -bool path_is_safe(const char *p) _pure_; +bool path_is_normalized(const char *p) _pure_; char *file_in_same_dir(const char *path, const char *filename); @@ -155,3 +164,8 @@ static inline const char *skip_dev_prefix(const char *p) { return e ?: p; } + +bool empty_or_root(const char *root); +static inline const char *empty_to_root(const char *path) { + return isempty(path) ? "/" : path; +} diff --git a/src/systemd/src/basic/prioq.c b/src/systemd/src/basic/prioq.c index 64ad638e..805a6174 100644 --- a/src/systemd/src/basic/prioq.c +++ b/src/systemd/src/basic/prioq.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2013 Lennart Poettering - - 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 . ***/ /* @@ -174,7 +162,7 @@ int prioq_put(Prioq *q, void *data, unsigned *idx) { struct prioq_item *j; n = MAX((q->n_items+1) * 2, 16u); - j = realloc(q->items, sizeof(struct prioq_item) * n); + j = reallocarray(q->items, n, sizeof(struct prioq_item)); if (!j) return -ENOMEM; diff --git a/src/systemd/src/basic/prioq.h b/src/systemd/src/basic/prioq.h index 113c73d0..e348c646 100644 --- a/src/systemd/src/basic/prioq.h +++ b/src/systemd/src/basic/prioq.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2013 Lennart Poettering - - 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 diff --git a/src/systemd/src/basic/process-util.c b/src/systemd/src/basic/process-util.c index 272030d1..4af99957 100644 --- a/src/systemd/src/basic/process-util.c +++ b/src/systemd/src/basic/process-util.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -27,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +47,7 @@ #include "stat-util.h" #include "string-table.h" #include "string-util.h" +#include "terminal-util.h" #include "user-util.h" #include "util.h" @@ -134,6 +124,8 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * return -errno; } + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + if (max_length == 1) { /* If there's only room for one byte, return the empty string */ @@ -297,10 +289,17 @@ int rename_process(const char name[]) { if (isempty(name)) return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ + if (!is_main_thread()) + return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we + * cache things without locking, and we make assumptions that PR_SET_NAME sets the + * process name that isn't correct on any other threads */ + l = strlen(name); - /* First step, change the comm field. */ - (void) prctl(PR_SET_NAME, name); + /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we + * can use PR_SET_NAME, which sets the thread name for the calling thread. */ + if (prctl(PR_SET_NAME, name) < 0) + log_debug_errno(errno, "PR_SET_NAME failed: %m"); if (l > 15) /* Linux process names can be 15 chars at max */ truncated = true; @@ -391,35 +390,61 @@ use_saved_argv: } int is_kernel_thread(pid_t pid) { + _cleanup_free_ char *line = NULL; + unsigned long long flags; + size_t l, i; const char *p; - size_t count; - char c; - bool eof; - FILE *f; + char *q; + int r; if (IN_SET(pid, 0, 1) || pid == getpid_cached()) /* pid 1, and we ourselves certainly aren't a kernel thread */ return 0; + if (!pid_is_valid(pid)) + return -EINVAL; - assert(pid > 1); + p = procfs_file_alloca(pid, "stat"); + r = read_one_line_file(p, &line); + if (r == -ENOENT) + return -ESRCH; + if (r < 0) + return r; - p = procfs_file_alloca(pid, "cmdline"); - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; + /* Skip past the comm field */ + q = strrchr(line, ')'); + if (!q) + return -EINVAL; + q++; + + /* Skip 6 fields to reach the flags field */ + for (i = 0; i < 6; i++) { + l = strspn(q, WHITESPACE); + if (l < 1) + return -EINVAL; + q += l; + + l = strcspn(q, WHITESPACE); + if (l < 1) + return -EINVAL; + q += l; } - count = fread(&c, 1, 1, f); - eof = feof(f); - fclose(f); + /* Skip preceeding whitespace */ + l = strspn(q, WHITESPACE); + if (l < 1) + return -EINVAL; + q += l; - /* Kernel threads have an empty cmdline */ + /* Truncate the rest */ + l = strcspn(q, WHITESPACE); + if (l < 1) + return -EINVAL; + q[l] = 0; - if (count <= 0) - return eof ? 1 : -errno; + r = safe_atollu(q, &flags); + if (r < 0) + return r; - return 0; + return !!(flags & PF_KTHREAD); } int get_process_capeff(pid_t pid, char **capeff) { @@ -491,6 +516,8 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) { return -errno; } + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + FOREACH_LINE(line, f, return -errno) { char *l; @@ -569,6 +596,8 @@ int get_process_environ(pid_t pid, char **env) { return -errno; } + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + while ((c = fgetc(f)) != EOF) { if (!GREEDY_REALLOC(outcome, allocated, sz + 5)) return -ENOMEM; @@ -586,8 +615,7 @@ int get_process_environ(pid_t pid, char **env) { } else outcome[sz] = '\0'; - *env = outcome; - outcome = NULL; + *env = TAKE_PTR(outcome); return 0; } @@ -674,32 +702,104 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) { * A warning is emitted if the process terminates abnormally, * and also if it returns non-zero unless check_exit_code is true. */ -int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) { - int r; +int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) { + _cleanup_free_ char *buffer = NULL; siginfo_t status; + int r, prio; - assert(name); assert(pid > 1); + if (!name) { + r = get_process_comm(pid, &buffer); + if (r < 0) + log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid); + else + name = buffer; + } + + prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG; + r = wait_for_terminate(pid, &status); if (r < 0) - return log_warning_errno(r, "Failed to wait for %s: %m", name); + return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name)); if (status.si_code == CLD_EXITED) { - if (status.si_status != 0) - log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG, - "%s failed with error code %i.", name, status.si_status); + if (status.si_status != EXIT_SUCCESS) + log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG, + "%s failed with exit status %i.", strna(name), status.si_status); else log_debug("%s succeeded.", name); return status.si_status; + } else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) { - log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status)); + log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status)); return -EPROTO; } - log_warning("%s failed due to unknown reason.", name); + log_full(prio, "%s failed due to unknown reason.", strna(name)); + return -EPROTO; +} + +/* + * Return values: + * < 0 : wait_for_terminate_with_timeout() failed to get the state of the + * process, the process timed out, the process was terminated by a + * signal, or failed for an unknown reason. + * >=0 : The process terminated normally with no failures. + * + * Success is indicated by a return value of zero, a timeout is indicated + * by ETIMEDOUT, and all other child failure states are indicated by error + * is indicated by a non-zero value. + */ +int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) { + sigset_t mask; + int r; + usec_t until; + + assert_se(sigemptyset(&mask) == 0); + assert_se(sigaddset(&mask, SIGCHLD) == 0); + + /* Drop into a sigtimewait-based timeout. Waiting for the + * pid to exit. */ + until = now(CLOCK_MONOTONIC) + timeout; + for (;;) { + usec_t n; + siginfo_t status = {}; + struct timespec ts; + + n = now(CLOCK_MONOTONIC); + if (n >= until) + break; + + r = sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)) < 0 ? -errno : 0; + /* Assuming we woke due to the child exiting. */ + if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) { + if (status.si_pid == pid) { + /* This is the correct child.*/ + if (status.si_code == CLD_EXITED) + return (status.si_status == 0) ? 0 : -EPROTO; + else + return -EPROTO; + } + } + /* Not the child, check for errors and proceed appropriately */ + if (r < 0) { + switch (r) { + case -EAGAIN: + /* Timed out, child is likely hung. */ + return -ETIMEDOUT; + case -EINTR: + /* Received a different signal and should retry */ + continue; + default: + /* Return any unexpected errors */ + return r; + } + } + } + return -EPROTO; } @@ -711,6 +811,8 @@ void sigkill_wait(pid_t pid) { } void sigkill_waitp(pid_t *pid) { + PROTECT_ERRNO; + if (!pid) return; if (*pid <= 1) @@ -719,6 +821,13 @@ void sigkill_waitp(pid_t *pid) { sigkill_wait(*pid); } +void sigterm_wait(pid_t pid) { + assert(pid > 1); + + if (kill_and_sigcont(pid, SIGTERM) > 0) + (void) wait_for_terminate(pid, NULL); +} + int kill_and_sigcont(pid_t pid, int sig) { int r; @@ -732,17 +841,33 @@ int kill_and_sigcont(pid_t pid, int sig) { return r; } -int getenv_for_pid(pid_t pid, const char *field, char **_value) { +int getenv_for_pid(pid_t pid, const char *field, char **ret) { _cleanup_fclose_ FILE *f = NULL; char *value = NULL; - int r; bool done = false; - size_t l; const char *path; + size_t l; assert(pid >= 0); assert(field); - assert(_value); + assert(ret); + + if (pid == 0 || pid == getpid_cached()) { + const char *e; + + e = getenv(field); + if (!e) { + *ret = NULL; + return 0; + } + + value = strdup(e); + if (!value) + return -ENOMEM; + + *ret = value; + return 1; + } path = procfs_file_alloca(pid, "environ"); @@ -750,15 +875,17 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) { if (!f) { if (errno == ENOENT) return -ESRCH; + return -errno; } + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + l = strlen(field); - r = 0; do { char line[LINE_MAX]; - unsigned i; + size_t i; for (i = 0; i < sizeof(line)-1; i++) { int c; @@ -779,14 +906,14 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) { if (!value) return -ENOMEM; - r = 1; - break; + *ret = value; + return 1; } } while (!done); - *_value = value; - return r; + *ret = NULL; + return 0; } bool pid_is_unwaited(pid_t pid) { @@ -853,7 +980,7 @@ bool is_main_thread(void) { } #if 0 /* NM_IGNORED */ -noreturn void freeze(void) { +_noreturn_ void freeze(void) { log_close(); @@ -862,6 +989,17 @@ noreturn void freeze(void) { sync(); + /* Let's not freeze right away, but keep reaping zombies. */ + for (;;) { + int r; + siginfo_t si = {}; + + r = waitid(P_ALL, 0, &si, WEXITED); + if (r < 0 && errno != EINTR) + break; + } + + /* waitid() failed with an unexpected error, things are really borked. Freeze now! */ for (;;) pause(); } @@ -1010,7 +1148,7 @@ int ioprio_parse_priority(const char *s, int *ret) { static pid_t cached_pid = CACHED_PID_UNSET; -static void reset_cached_pid(void) { +void reset_cached_pid(void) { /* Invoked in the child after a fork(), i.e. at the first moment the PID changed */ cached_pid = CACHED_PID_UNSET; } @@ -1022,6 +1160,7 @@ extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void extern void* __dso_handle __attribute__ ((__weak__)); pid_t getpid_cached(void) { + static bool installed = false; pid_t current_value; /* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a @@ -1040,12 +1179,20 @@ pid_t getpid_cached(void) { case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */ pid_t new_pid; - new_pid = getpid(); + new_pid = raw_getpid(); + + if (!installed) { + /* __register_atfork() either returns 0 or -ENOMEM, in its glibc implementation. Since it's + * only half-documented (glibc doesn't document it but LSB does — though only superficially) + * we'll check for errors only in the most generic fashion possible. */ + + if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) { + /* OOM? Let's try again later */ + cached_pid = CACHED_PID_UNSET; + return new_pid; + } - if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) { - /* OOM? Let's try again later */ - cached_pid = CACHED_PID_UNSET; - return new_pid; + installed = true; } cached_pid = new_pid; @@ -1053,7 +1200,7 @@ pid_t getpid_cached(void) { } case CACHED_PID_BUSY: /* Somebody else is currently initializing */ - return getpid(); + return raw_getpid(); default: /* Properly initialized */ return current_value; @@ -1061,6 +1208,261 @@ pid_t getpid_cached(void) { } #if 0 /* NM_IGNORED */ +int must_be_root(void) { + + if (geteuid() == 0) + return 0; + + log_error("Need to be root."); + return -EPERM; +} + +int safe_fork_full( + const char *name, + const int except_fds[], + size_t n_except_fds, + ForkFlags flags, + pid_t *ret_pid) { + + pid_t original_pid, pid; + sigset_t saved_ss, ss; + bool block_signals = false; + int prio, r; + + /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always + * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */ + + prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG; + + original_pid = getpid_cached(); + + if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) { + + /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can + * be sure that SIGTERMs are not lost we might send to the child. */ + + if (sigfillset(&ss) < 0) + return log_full_errno(prio, errno, "Failed to reset signal set: %m"); + + block_signals = true; + + } else if (flags & FORK_WAIT) { + + /* Let's block SIGCHLD at least, so that we can safely watch for the child process */ + + if (sigemptyset(&ss) < 0) + return log_full_errno(prio, errno, "Failed to clear signal set: %m"); + + if (sigaddset(&ss, SIGCHLD) < 0) + return log_full_errno(prio, errno, "Failed to add SIGCHLD to signal set: %m"); + + block_signals = true; + } + + if (block_signals) + if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0) + return log_full_errno(prio, errno, "Failed to set signal mask: %m"); + + if (flags & FORK_NEW_MOUNTNS) + pid = raw_clone(SIGCHLD|CLONE_NEWNS); + else + pid = fork(); + if (pid < 0) { + r = -errno; + + if (block_signals) /* undo what we did above */ + (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); + + return log_full_errno(prio, r, "Failed to fork: %m"); + } + if (pid > 0) { + /* We are in the parent process */ + + log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid); + + if (flags & FORK_WAIT) { + r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0)); + if (r < 0) + return r; + if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */ + return -EPROTO; + } + + if (block_signals) /* undo what we did above */ + (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); + + if (ret_pid) + *ret_pid = pid; + + return 1; + } + + /* We are in the child process */ + + if (flags & FORK_REOPEN_LOG) { + /* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */ + log_close(); + log_set_open_when_needed(true); + } + + if (name) { + r = rename_process(name); + if (r < 0) + log_full_errno(flags & FORK_LOG ? LOG_WARNING : LOG_DEBUG, + r, "Failed to rename process, ignoring: %m"); + } + + if (flags & FORK_DEATHSIG) + if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) { + log_full_errno(prio, errno, "Failed to set death signal: %m"); + _exit(EXIT_FAILURE); + } + + if (flags & FORK_RESET_SIGNALS) { + r = reset_all_signal_handlers(); + if (r < 0) { + log_full_errno(prio, r, "Failed to reset signal handlers: %m"); + _exit(EXIT_FAILURE); + } + + /* This implicitly undoes the signal mask stuff we did before the fork()ing above */ + r = reset_signal_mask(); + if (r < 0) { + log_full_errno(prio, r, "Failed to reset signal mask: %m"); + _exit(EXIT_FAILURE); + } + } else if (block_signals) { /* undo what we did above */ + if (sigprocmask(SIG_SETMASK, &saved_ss, NULL) < 0) { + log_full_errno(prio, errno, "Failed to restore signal mask: %m"); + _exit(EXIT_FAILURE); + } + } + + if (flags & FORK_DEATHSIG) { + pid_t ppid; + /* Let's see if the parent PID is still the one we started from? If not, then the parent + * already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */ + + ppid = getppid(); + if (ppid == 0) + /* Parent is in a differn't PID namespace. */; + else if (ppid != original_pid) { + log_debug("Parent died early, raising SIGTERM."); + (void) raise(SIGTERM); + _exit(EXIT_FAILURE); + } + } + + if (flags & FORK_CLOSE_ALL_FDS) { + /* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */ + log_close(); + + r = close_all_fds(except_fds, n_except_fds); + if (r < 0) { + log_full_errno(prio, r, "Failed to close all file descriptors: %m"); + _exit(EXIT_FAILURE); + } + } + + /* When we were asked to reopen the logs, do so again now */ + if (flags & FORK_REOPEN_LOG) { + log_open(); + log_set_open_when_needed(false); + } + + if (flags & FORK_NULL_STDIO) { + r = make_null_stdio(); + if (r < 0) { + log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m"); + _exit(EXIT_FAILURE); + } + } + + if (ret_pid) + *ret_pid = getpid_cached(); + + return 0; +} + +int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) { + bool stdout_is_tty, stderr_is_tty; + size_t n, i; + va_list ap; + char **l; + int r; + + assert(path); + + /* Spawns a temporary TTY agent, making sure it goes away when we go away */ + + r = safe_fork_full(name, except, n_except, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, ret_pid); + if (r < 0) + return r; + if (r > 0) + return 0; + + /* In the child: */ + + stdout_is_tty = isatty(STDOUT_FILENO); + stderr_is_tty = isatty(STDERR_FILENO); + + if (!stdout_is_tty || !stderr_is_tty) { + int fd; + + /* Detach from stdout/stderr. and reopen + * /dev/tty for them. This is important to + * ensure that when systemctl is started via + * popen() or a similar call that expects to + * read EOF we actually do generate EOF and + * not delay this indefinitely by because we + * keep an unused copy of stdin around. */ + fd = open("/dev/tty", O_WRONLY); + if (fd < 0) { + log_error_errno(errno, "Failed to open /dev/tty: %m"); + _exit(EXIT_FAILURE); + } + + if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) { + log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); + _exit(EXIT_FAILURE); + } + + if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) { + log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); + _exit(EXIT_FAILURE); + } + + safe_close_above_stdio(fd); + } + + /* Count arguments */ + va_start(ap, path); + for (n = 0; va_arg(ap, char*); n++) + ; + va_end(ap); + + /* Allocate strv */ + l = newa(char*, n + 1); + + /* Fill in arguments */ + va_start(ap, path); + for (i = 0; i <= n; i++) + l[i] = va_arg(ap, char*); + va_end(ap); + + execv(path, l); + _exit(EXIT_FAILURE); +} + +int set_oom_score_adjust(int value) { + char t[DECIMAL_STR_MAX(int)]; + + sprintf(t, "%i", value); + + return write_string_file("/proc/self/oom_score_adj", t, + WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER); +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", @@ -1068,7 +1470,7 @@ static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_IDLE] = "idle" }; -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX); +DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES); static const char *const sigchld_code_table[] = { [CLD_EXITED] = "exited", diff --git a/src/systemd/src/basic/process-util.h b/src/systemd/src/basic/process-util.h index e1bd2c5b..e3bf36bf 100644 --- a/src/systemd/src/basic/process-util.h +++ b/src/systemd/src/basic/process-util.h @@ -1,25 +1,14 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . ***/ #include +#include #include #include #include @@ -32,6 +21,7 @@ #include "format-util.h" #include "ioprio.h" #include "macro.h" +#include "time-util.h" #define procfs_file_alloca(pid, field) \ ({ \ @@ -40,7 +30,7 @@ if (_pid_ == 0) { \ _r_ = ("/proc/self/" field); \ } else { \ - _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ + _r_ = alloca(STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \ } \ _r_; \ @@ -59,10 +49,21 @@ int get_process_environ(pid_t pid, char **environ); int get_process_ppid(pid_t pid, pid_t *ppid); int wait_for_terminate(pid_t pid, siginfo_t *status); -int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code); + +typedef enum WaitFlags { + WAIT_LOG_ABNORMAL = 1U << 0, + WAIT_LOG_NON_ZERO_EXIT_STATUS = 1U << 1, + + /* A shortcut for requesting the most complete logging */ + WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS, +} WaitFlags; + +int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags); +int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout); void sigkill_wait(pid_t pid); void sigkill_waitp(pid_t *pid); +void sigterm_wait(pid_t pid); int kill_and_sigcont(pid_t pid, int sig); @@ -77,7 +78,7 @@ int pid_from_same_root_fs(pid_t pid); bool is_main_thread(void); -noreturn void freeze(void); +_noreturn_ void freeze(void); bool oom_score_adjust_is_valid(int oa); @@ -103,8 +104,13 @@ int sigchld_code_from_string(const char *s) _pure_; int sched_policy_to_string_alloc(int i, char **s); int sched_policy_from_string(const char *s); -#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p)) -#define PID_TO_PTR(p) ((void*) ((uintptr_t) p)) +static inline pid_t PTR_TO_PID(const void *p) { + return (pid_t) ((uintptr_t) p); +} + +static inline void* PID_TO_PTR(pid_t pid) { + return (void*) ((uintptr_t) pid); +} void valgrind_summary_hack(void); @@ -134,8 +140,66 @@ static inline bool ioprio_priority_is_valid(int i) { static inline bool pid_is_valid(pid_t p) { return p > 0; } + +static inline int sched_policy_to_string_alloc_with_check(int n, char **s) { + if (!sched_policy_is_valid(n)) + return -EINVAL; + + return sched_policy_to_string_alloc(n, s); +} #endif /* NM_IGNORED */ int ioprio_parse_priority(const char *s, int *ret); pid_t getpid_cached(void); +void reset_cached_pid(void); + +int must_be_root(void); + +typedef enum ForkFlags { + FORK_RESET_SIGNALS = 1U << 0, + FORK_CLOSE_ALL_FDS = 1U << 1, + FORK_DEATHSIG = 1U << 2, + FORK_NULL_STDIO = 1U << 3, + FORK_REOPEN_LOG = 1U << 4, + FORK_LOG = 1U << 5, + FORK_WAIT = 1U << 6, + FORK_NEW_MOUNTNS = 1U << 7, +} ForkFlags; + +int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); + +static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) { + return safe_fork_full(name, NULL, 0, flags, ret_pid); +} + +int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...); + +int set_oom_score_adjust(int value); + +#if SIZEOF_PID_T == 4 +/* The highest possibly (theoretic) pid_t value on this architecture. */ +#define PID_T_MAX ((pid_t) INT32_MAX) +/* The maximum number of concurrent processes Linux allows on this architecture, as well as the highest valid PID value + * the kernel will potentially assign. This reflects a value compiled into the kernel (PID_MAX_LIMIT), and sets the + * upper boundary on what may be written to the /proc/sys/kernel/pid_max sysctl (but do note that the sysctl is off by + * 1, since PID 0 can never exist and there can hence only be one process less than the limit would suggest). Since + * these values are documented in proc(5) we feel quite confident that they are stable enough for the near future at + * least to define them here too. */ +#define TASKS_MAX 4194303U +#elif SIZEOF_PID_T == 2 +#define PID_T_MAX ((pid_t) INT16_MAX) +#define TASKS_MAX 32767U +#else +#error "Unknown pid_t size" +#endif + +assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX) + +/* Like TAKE_PTR() but for child PIDs, resetting them to 0 */ +#define TAKE_PID(pid) \ + ({ \ + pid_t _pid_ = (pid); \ + (pid) = 0; \ + _pid_; \ + }) diff --git a/src/systemd/src/basic/random-util.c b/src/systemd/src/basic/random-util.c index 3b6ddb7d..d80751cf 100644 --- a/src/systemd/src/basic/random-util.c +++ b/src/systemd/src/basic/random-util.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -22,11 +10,12 @@ #include #include #include +#include #include +#include #include +#include #include -#include -#include #if HAVE_SYS_AUXV_H # include @@ -48,7 +37,7 @@ int acquire_random_bytes(void *p, size_t n, bool high_quality_required) { static int have_syscall = -1; _cleanup_close_ int fd = -1; - unsigned already_done = 0; + size_t already_done = 0; int r; /* Gathers some randomness from the kernel. This call will never block. If @@ -129,7 +118,6 @@ void initialize_srand(void) { #endif x = 0; - x ^= (unsigned) now(CLOCK_REALTIME); x ^= (unsigned) gettid(); diff --git a/src/systemd/src/basic/random-util.h b/src/systemd/src/basic/random-util.h index 804e225f..72391705 100644 --- a/src/systemd/src/basic/random-util.h +++ b/src/systemd/src/basic/random-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 diff --git a/src/systemd/src/basic/refcnt.h b/src/systemd/src/basic/refcnt.h index 1d77a644..0d5b3f4e 100644 --- a/src/systemd/src/basic/refcnt.h +++ b/src/systemd/src/basic/refcnt.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2013 Lennart Poettering - - 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 . ***/ /* A type-safe atomic refcounter. diff --git a/src/systemd/src/basic/set.h b/src/systemd/src/basic/set.h index 12d0fda1..dc0f1e17 100644 --- a/src/systemd/src/basic/set.h +++ b/src/systemd/src/basic/set.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "extract-word.h" @@ -107,6 +95,18 @@ static inline void *set_steal_first(Set *s) { return internal_hashmap_steal_first(HASHMAP_BASE(s)); } +#define set_clear_with_destructor(_s, _f) \ + ({ \ + void *_item; \ + while ((_item = set_steal_first(_s))) \ + _f(_item); \ + }) +#define set_free_with_destructor(_s, _f) \ + ({ \ + set_clear_with_destructor(_s, _f); \ + set_free(_s); \ + }) + /* no set_steal_first_key */ /* no set_first_key */ @@ -136,5 +136,3 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); #define _cleanup_set_free_ _cleanup_(set_freep) #define _cleanup_set_free_free_ _cleanup_(set_free_freep) - -int set_make(Set **ret, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS, void *add, ...); diff --git a/src/systemd/src/basic/signal-util.h b/src/systemd/src/basic/signal-util.h index dfd6eb56..0c43467b 100644 --- a/src/systemd/src/basic/signal-util.h +++ b/src/systemd/src/basic/signal-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010-2015 Lennart Poettering - - 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 @@ -36,21 +24,26 @@ int sigprocmask_many(int how, sigset_t *old, ...); const char *signal_to_string(int i) _const_; int signal_from_string(const char *s) _pure_; -int signal_from_string_try_harder(const char *s); - void nop_signal_handler(int sig); static inline void block_signals_reset(sigset_t *ss) { assert_se(sigprocmask(SIG_SETMASK, ss, NULL) >= 0); } -#define BLOCK_SIGNALS(...) \ - _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \ - sigset_t t; \ - assert_se(sigprocmask_many(SIG_BLOCK, &t, __VA_ARGS__, -1) >= 0); \ - t; \ +#define BLOCK_SIGNALS(...) \ + _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \ + sigset_t _t; \ + assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__, -1) >= 0); \ + _t; \ }) static inline bool SIGNAL_VALID(int signo) { return signo > 0 && signo < _NSIG; } + +static inline const char* signal_to_string_with_check(int n) { + if (!SIGNAL_VALID(n)) + return NULL; + + return signal_to_string(n); +} diff --git a/src/systemd/src/basic/siphash24.h b/src/systemd/src/basic/siphash24.h new file mode 100644 index 00000000..77bb9a8c --- /dev/null +++ b/src/systemd/src/basic/siphash24.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include +#include + +#if 0 /* NM_IGNORED */ +struct siphash { + uint64_t v0; + uint64_t v1; + uint64_t v2; + uint64_t v3; + uint64_t padding; + size_t inlen; +}; +#else /* NM_IGNORED */ +struct siphash { + CSipHash _csiphash; +}; + +static inline void +siphash24_init (struct siphash *state, const uint8_t k[16]) +{ + c_siphash_init ((CSipHash *) state, k); +} + +static inline void +siphash24_compress (const void *in, size_t inlen, struct siphash *state) +{ + c_siphash_append ((CSipHash *) state, in, inlen); +} + +static inline uint64_t +siphash24_finalize (struct siphash *state) +{ + return c_siphash_finalize ((CSipHash *) state); +} + +static inline uint64_t +siphash24 (const void *in, size_t inlen, const uint8_t k[16]) +{ + return c_siphash_hash (k, in, inlen); +} +#endif /* NM_IGNORED */ + +void siphash24_init(struct siphash *state, const uint8_t k[16]); +void siphash24_compress(const void *in, size_t inlen, struct siphash *state); +#define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state)) + +uint64_t siphash24_finalize(struct siphash *state); + +uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]); diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c index 798ab16e..aa06070e 100644 --- a/src/systemd/src/basic/socket-util.c +++ b/src/systemd/src/basic/socket-util.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -42,6 +30,7 @@ #include "missing.h" #include "parse-util.h" #include "path-util.h" +#include "process-util.h" #include "socket-util.h" #include "string-table.h" #include "string-util.h" @@ -52,14 +41,24 @@ #if 0 /* NM_IGNORED */ #if ENABLE_IDN -# define IDN_FLAGS (NI_IDN|NI_IDN_USE_STD3_ASCII_RULES) +# define IDN_FLAGS NI_IDN #else # define IDN_FLAGS 0 #endif +static const char* const socket_address_type_table[] = { + [SOCK_STREAM] = "Stream", + [SOCK_DGRAM] = "Datagram", + [SOCK_RAW] = "Raw", + [SOCK_RDM] = "ReliableDatagram", + [SOCK_SEQPACKET] = "SequentialPacket", + [SOCK_DCCP] = "DatagramCongestionControl", +}; + +DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int); + int socket_address_parse(SocketAddress *a, const char *s) { char *e, *n; - unsigned u; int r; assert(a); @@ -69,6 +68,8 @@ int socket_address_parse(SocketAddress *a, const char *s) { a->type = SOCK_STREAM; if (*s == '[') { + uint16_t port; + /* IPv6 in [x:.....:z]:p notation */ e = strchr(s+1, ']'); @@ -86,15 +87,12 @@ int socket_address_parse(SocketAddress *a, const char *s) { return -EINVAL; e++; - r = safe_atou(e, &u); + r = parse_ip_port(e, &port); if (r < 0) return r; - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->sockaddr.in6.sin6_port = htobe16(port); a->size = sizeof(struct sockaddr_in6); } else if (*s == '/') { @@ -124,13 +122,14 @@ int socket_address_parse(SocketAddress *a, const char *s) { } else if (startswith(s, "vsock:")) { /* AF_VSOCK socket in vsock:cid:port notation */ - const char *cid_start = s + strlen("vsock:"); + const char *cid_start = s + STRLEN("vsock:"); + unsigned port; e = strchr(cid_start, ':'); if (!e) return -EINVAL; - r = safe_atou(e+1, &u); + r = safe_atou(e+1, &port); if (r < 0) return r; @@ -143,19 +142,18 @@ int socket_address_parse(SocketAddress *a, const char *s) { a->sockaddr.vm.svm_cid = VMADDR_CID_ANY; a->sockaddr.vm.svm_family = AF_VSOCK; - a->sockaddr.vm.svm_port = u; + a->sockaddr.vm.svm_port = port; a->size = sizeof(struct sockaddr_vm); } else { + uint16_t port; + e = strchr(s, ':'); if (e) { - r = safe_atou(e+1, &u); + r = parse_ip_port(e + 1, &port); if (r < 0) return r; - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - n = strndupa(s, e-s); /* IPv4 in w.x.y.z:p notation? */ @@ -166,7 +164,7 @@ int socket_address_parse(SocketAddress *a, const char *s) { if (r > 0) { /* Gotcha, it's a traditional IPv4 address */ a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16((uint16_t)u); + a->sockaddr.in.sin_port = htobe16(port); a->size = sizeof(struct sockaddr_in); } else { unsigned idx; @@ -180,7 +178,7 @@ int socket_address_parse(SocketAddress *a, const char *s) { return -EINVAL; a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->sockaddr.in6.sin6_port = htobe16(port); a->sockaddr.in6.sin6_scope_id = idx; a->sockaddr.in6.sin6_addr = in6addr_any; a->size = sizeof(struct sockaddr_in6); @@ -188,21 +186,18 @@ int socket_address_parse(SocketAddress *a, const char *s) { } else { /* Just a port */ - r = safe_atou(s, &u); + r = parse_ip_port(s, &port); if (r < 0) return r; - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - if (socket_ipv6_is_supported()) { a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->sockaddr.in6.sin6_port = htobe16(port); a->sockaddr.in6.sin6_addr = in6addr_any; a->size = sizeof(struct sockaddr_in6); } else { a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16((uint16_t)u); + a->sockaddr.in.sin_port = htobe16(port); a->sockaddr.in.sin_addr.s_addr = INADDR_ANY; a->size = sizeof(struct sockaddr_in); } @@ -530,22 +525,25 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) { return socket_address_equal(a, &b); } -int sockaddr_port(const struct sockaddr *_sa, unsigned *port) { +int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) { union sockaddr_union *sa = (union sockaddr_union*) _sa; + /* Note, this returns the port as 'unsigned' rather than 'uint16_t', as AF_VSOCK knows larger ports */ + assert(sa); switch (sa->sa.sa_family) { + case AF_INET: - *port = be16toh(sa->in.sin_port); + *ret_port = be16toh(sa->in.sin_port); return 0; case AF_INET6: - *port = be16toh(sa->in6.sin6_port); + *ret_port = be16toh(sa->in6.sin6_port); return 0; case AF_VSOCK: - *port = sa->vm.svm_port; + *ret_port = sa->vm.svm_port; return 0; default: @@ -669,7 +667,6 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ return -EOPNOTSUPP; } - *ret = p; return 0; } @@ -750,19 +747,6 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) return 0; } -int getnameinfo_pretty(int fd, char **ret) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - - assert(fd >= 0); - assert(ret); - - if (getsockname(fd, &sa.sa, &salen) < 0) - return -errno; - - return socknameinfo_pretty(&sa, salen, ret); -} - int socket_address_unlink(SocketAddress *a) { assert(a); @@ -809,6 +793,18 @@ static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIN DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); +SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *n) { + int r; + + r = parse_boolean(n); + if (r > 0) + return SOCKET_ADDRESS_IPV6_ONLY; + if (r == 0) + return SOCKET_ADDRESS_BOTH; + + return socket_address_bind_ipv6_only_from_string(n); +} + bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) { assert(a); assert(b); @@ -943,56 +939,76 @@ int getpeercred(int fd, struct ucred *ucred) { if (n != sizeof(struct ucred)) return -EIO; - /* Check if the data is actually useful and not suppressed due - * to namespacing issues */ - if (u.pid <= 0) - return -ENODATA; - if (u.uid == UID_INVALID) - return -ENODATA; - if (u.gid == GID_INVALID) + /* Check if the data is actually useful and not suppressed due to namespacing issues */ + if (!pid_is_valid(u.pid)) return -ENODATA; + /* Note that we don't check UID/GID here, as namespace translation works differently there: instead of + * receiving in "invalid" user/group we get the overflow UID/GID. */ + *ucred = u; return 0; } int getpeersec(int fd, char **ret) { + _cleanup_free_ char *s = NULL; socklen_t n = 64; - char *s; - int r; assert(fd >= 0); assert(ret); - s = new0(char, n); - if (!s) - return -ENOMEM; + for (;;) { + s = new0(char, n+1); + if (!s) + return -ENOMEM; - r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); - if (r < 0) { - free(s); + if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) + break; if (errno != ERANGE) return -errno; - s = new0(char, n); - if (!s) + s = mfree(s); + } + + if (isempty(s)) + return -EOPNOTSUPP; + + *ret = TAKE_PTR(s); + + return 0; +} + +int getpeergroups(int fd, gid_t **ret) { + socklen_t n = sizeof(gid_t) * 64; + _cleanup_free_ gid_t *d = NULL; + + assert(fd >= 0); + assert(ret); + + for (;;) { + d = malloc(n); + if (!d) return -ENOMEM; - r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); - if (r < 0) { - free(s); + if (getsockopt(fd, SOL_SOCKET, SO_PEERGROUPS, d, &n) >= 0) + break; + + if (errno != ERANGE) return -errno; - } - } - if (isempty(s)) { - free(s); - return -EOPNOTSUPP; + d = mfree(d); } - *ret = s; - return 0; + assert_se(n % sizeof(gid_t) == 0); + n /= sizeof(gid_t); + + if ((socklen_t) (int) n != n) + return -E2BIG; + + *ret = TAKE_PTR(d); + + return (int) n; } int send_one_fd_sa( @@ -1115,7 +1131,6 @@ int flush_accept(int fd) { }; int r; - /* Similar to flush_fd() but flushes all incoming connection by accepting them and immediately closing them. */ for (;;) { diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h index d7e2d85f..c2d21b45 100644 --- a/src/systemd/src/basic/socket-util.h +++ b/src/systemd/src/basic/socket-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 @@ -35,18 +23,28 @@ #include "util.h" union sockaddr_union { + /* The minimal, abstract version */ struct sockaddr sa; + + /* The libc provided version that allocates "enough room" for every protocol */ + struct sockaddr_storage storage; + + /* Protoctol-specific implementations */ struct sockaddr_in in; struct sockaddr_in6 in6; struct sockaddr_un un; struct sockaddr_nl nl; - struct sockaddr_storage storage; struct sockaddr_ll ll; #if 0 /* NM_IGNORED */ struct sockaddr_vm vm; #endif /* NM_IGNORED */ + /* Ensure there is enough space to store Infiniband addresses */ uint8_t ll_buffer[offsetof(struct sockaddr_ll, sll_addr) + CONST_MAX(ETH_ALEN, INFINIBAND_ALEN)]; + + /* Ensure there is enough space after the AF_UNIX sun_path for one more NUL byte, just to be sure that the path + * component is always followed by at least one NUL byte. */ + uint8_t un_buffer[sizeof(struct sockaddr_un) + 1]; }; typedef struct SocketAddress { @@ -73,6 +71,9 @@ typedef enum SocketAddressBindIPv6Only { #define socket_address_family(a) ((a)->sockaddr.sa.sa_family) +const char* socket_address_type_to_string(int t) _const_; +int socket_address_type_from_string(const char *s) _pure_; + int socket_address_parse(SocketAddress *a, const char *s); int socket_address_parse_and_warn(SocketAddress *a, const char *s); int socket_address_parse_netlink(SocketAddress *a, const char *s); @@ -114,10 +115,10 @@ int getpeername_pretty(int fd, bool include_port, char **ret); int getsockname_pretty(int fd, char **ret); int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret); -int getnameinfo_pretty(int fd, char **ret); const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_; SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_; +SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *s); int netlink_family_to_string_alloc(int b, char **s); int netlink_family_from_string(const char *s) _pure_; @@ -135,6 +136,7 @@ bool address_label_valid(const char *p); int getpeercred(int fd, struct ucred *ucred); int getpeersec(int fd, char **ret); +int getpeergroups(int fd, gid_t **ret); int send_one_fd_sa(int transport_fd, int fd, diff --git a/src/systemd/src/basic/sparse-endian.h b/src/systemd/src/basic/sparse-endian.h index a3573b84..5e59de54 100644 --- a/src/systemd/src/basic/sparse-endian.h +++ b/src/systemd/src/basic/sparse-endian.h @@ -1,4 +1,6 @@ -/* Copyright (c) 2012 Josh Triplett +/* SPDX-License-Identifier: MIT + * + * Copyright (c) 2012 Josh Triplett * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to diff --git a/src/systemd/src/basic/stat-util.c b/src/systemd/src/basic/stat-util.c new file mode 100644 index 00000000..28e75618 --- /dev/null +++ b/src/systemd/src/basic/stat-util.c @@ -0,0 +1,292 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + This file is part of systemd. + + Copyright 2010-2012 Lennart Poettering +***/ + +#include "nm-sd-adapt.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dirent-util.h" +#include "fd-util.h" +#include "fs-util.h" +#include "macro.h" +#include "missing.h" +#include "stat-util.h" +#include "string-util.h" + +#if 0 /* NM_IGNORED */ +int is_symlink(const char *path) { + struct stat info; + + assert(path); + + if (lstat(path, &info) < 0) + return -errno; + + return !!S_ISLNK(info.st_mode); +} + +int is_dir(const char* path, bool follow) { + struct stat st; + int r; + + assert(path); + + if (follow) + r = stat(path, &st); + else + r = lstat(path, &st); + if (r < 0) + return -errno; + + return !!S_ISDIR(st.st_mode); +} + +int is_device_node(const char *path) { + struct stat info; + + assert(path); + + if (lstat(path, &info) < 0) + return -errno; + + return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode)); +} + +int dir_is_empty(const char *path) { + _cleanup_closedir_ DIR *d; + struct dirent *de; + + d = opendir(path); + if (!d) + return -errno; + + FOREACH_DIRENT(de, d, return -errno) + return 0; + + return 1; +} + +bool null_or_empty(struct stat *st) { + assert(st); + + if (S_ISREG(st->st_mode) && st->st_size <= 0) + return true; + + /* We don't want to hardcode the major/minor of /dev/null, + * hence we do a simpler "is this a device node?" check. */ + + if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) + return true; + + return false; +} + +int null_or_empty_path(const char *fn) { + struct stat st; + + assert(fn); + + if (stat(fn, &st) < 0) + return -errno; + + return null_or_empty(&st); +} + +int null_or_empty_fd(int fd) { + struct stat st; + + assert(fd >= 0); + + if (fstat(fd, &st) < 0) + return -errno; + + return null_or_empty(&st); +} + +int path_is_read_only_fs(const char *path) { + struct statvfs st; + + assert(path); + + if (statvfs(path, &st) < 0) + return -errno; + + if (st.f_flag & ST_RDONLY) + return true; + + /* On NFS, statvfs() might not reflect whether we can actually + * write to the remote share. Let's try again with + * access(W_OK) which is more reliable, at least sometimes. */ + if (access(path, W_OK) < 0 && errno == EROFS) + return true; + + return false; +} + +int path_is_os_tree(const char *path) { + int r; + + assert(path); + + /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir + * always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from + * the case where just the os-release file is missing. */ + if (laccess(path, F_OK) < 0) + return -errno; + + /* We use /usr/lib/os-release as flag file if something is an OS */ + r = chase_symlinks("/usr/lib/os-release", path, CHASE_PREFIX_ROOT, NULL); + if (r == -ENOENT) { + + /* Also check for the old location in /etc, just in case. */ + r = chase_symlinks("/etc/os-release", path, CHASE_PREFIX_ROOT, NULL); + if (r == -ENOENT) + return 0; /* We got nothing */ + } + if (r < 0) + return r; + + return 1; +} + +int files_same(const char *filea, const char *fileb, int flags) { + struct stat a, b; + + assert(filea); + assert(fileb); + + if (fstatat(AT_FDCWD, filea, &a, flags) < 0) + return -errno; + + if (fstatat(AT_FDCWD, fileb, &b, flags) < 0) + return -errno; + + return a.st_dev == b.st_dev && + a.st_ino == b.st_ino; +} + +bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { + assert(s); + assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type)); + + return F_TYPE_EQUAL(s->f_type, magic_value); +} + +int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { + struct statfs s; + + if (fstatfs(fd, &s) < 0) + return -errno; + + return is_fs_type(&s, magic_value); +} + +int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { + _cleanup_close_ int fd = -1; + + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); + if (fd < 0) + return -errno; + + return fd_is_fs_type(fd, magic_value); +} + +bool is_temporary_fs(const struct statfs *s) { + return is_fs_type(s, TMPFS_MAGIC) || + is_fs_type(s, RAMFS_MAGIC); +} + +bool is_network_fs(const struct statfs *s) { + return is_fs_type(s, CIFS_MAGIC_NUMBER) || + is_fs_type(s, CODA_SUPER_MAGIC) || + is_fs_type(s, NCP_SUPER_MAGIC) || + is_fs_type(s, NFS_SUPER_MAGIC) || + is_fs_type(s, SMB_SUPER_MAGIC) || + is_fs_type(s, V9FS_MAGIC) || + is_fs_type(s, AFS_SUPER_MAGIC) || + is_fs_type(s, OCFS2_SUPER_MAGIC); +} + +int fd_is_temporary_fs(int fd) { + struct statfs s; + + if (fstatfs(fd, &s) < 0) + return -errno; + + return is_temporary_fs(&s); +} + +int fd_is_network_fs(int fd) { + struct statfs s; + + if (fstatfs(fd, &s) < 0) + return -errno; + + return is_network_fs(&s); +} + +int fd_is_network_ns(int fd) { + int r; + + r = fd_is_fs_type(fd, NSFS_MAGIC); + if (r <= 0) + return r; + + r = ioctl(fd, NS_GET_NSTYPE); + if (r < 0) + return -errno; + + return r == CLONE_NEWNET; +} + +int path_is_temporary_fs(const char *path) { + _cleanup_close_ int fd = -1; + + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); + if (fd < 0) + return -errno; + + return fd_is_temporary_fs(fd); +} +#endif /* NM_IGNORED */ + +int stat_verify_regular(const struct stat *st) { + assert(st); + + /* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error + * code. */ + + if (S_ISDIR(st->st_mode)) + return -EISDIR; + + if (S_ISLNK(st->st_mode)) + return -ELOOP; + + if (!S_ISREG(st->st_mode)) + return -EBADFD; + + return 0; +} + +int fd_verify_regular(int fd) { + struct stat st; + + assert(fd >= 0); + + if (fstat(fd, &st) < 0) + return -errno; + + return stat_verify_regular(&st); +} diff --git a/src/systemd/src/basic/stat-util.h b/src/systemd/src/basic/stat-util.h new file mode 100644 index 00000000..d1e8d330 --- /dev/null +++ b/src/systemd/src/basic/stat-util.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010-2012 Lennart Poettering +***/ + +#include +#include +#include +#include +#include +#include + +#include "macro.h" + +int is_symlink(const char *path); +int is_dir(const char *path, bool follow); +int is_device_node(const char *path); + +int dir_is_empty(const char *path); + +static inline int dir_is_populated(const char *path) { + int r; + r = dir_is_empty(path); + if (r < 0) + return r; + return !r; +} + +bool null_or_empty(struct stat *st) _pure_; +int null_or_empty_path(const char *fn); +int null_or_empty_fd(int fd); + +int path_is_read_only_fs(const char *path); +int path_is_os_tree(const char *path); + +int files_same(const char *filea, const char *fileb, int flags); + +/* The .f_type field of struct statfs is really weird defined on + * different archs. Let's give its type a name. */ +typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t; + +bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_; +int fd_is_fs_type(int fd, statfs_f_type_t magic_value); +int path_is_fs_type(const char *path, statfs_f_type_t magic_value); + +bool is_temporary_fs(const struct statfs *s) _pure_; +bool is_network_fs(const struct statfs *s) _pure_; + +int fd_is_temporary_fs(int fd); +int fd_is_network_fs(int fd); + +int fd_is_network_ns(int fd); + +int path_is_temporary_fs(const char *path); + +/* Because statfs.t_type can be int on some architectures, we have to cast + * the const magic to the type, otherwise the compiler warns about + * signed/unsigned comparison, because the magic can be 32 bit unsigned. + */ +#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b) + +int stat_verify_regular(const struct stat *st); +int fd_verify_regular(int fd); diff --git a/src/systemd/src/basic/stdio-util.h b/src/systemd/src/basic/stdio-util.h index bd1144b4..5330789a 100644 --- a/src/systemd/src/basic/stdio-util.h +++ b/src/systemd/src/basic/stdio-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 @@ -26,9 +14,11 @@ #include "macro.h" -#define xsprintf(buf, fmt, ...) \ - assert_message_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf), "xsprintf: " #buf "[] must be big enough") +#define snprintf_ok(buf, len, fmt, ...) \ + ((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len)) +#define xsprintf(buf, fmt, ...) \ + assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough") #define VA_FORMAT_ADVANCE(format, ap) \ do { \ diff --git a/src/systemd/src/basic/string-table.c b/src/systemd/src/basic/string-table.c index df26d5fb..34723b4c 100644 --- a/src/systemd/src/basic/string-table.c +++ b/src/systemd/src/basic/string-table.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" diff --git a/src/systemd/src/basic/string-table.h b/src/systemd/src/basic/string-table.h index 369610ef..a39206fb 100644 --- a/src/systemd/src/basic/string-table.h +++ b/src/systemd/src/basic/string-table.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once @@ -5,19 +6,6 @@ This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 @@ -92,13 +80,11 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k #define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ - struct __useless_struct_to_allow_trailing_semicolon__ + _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) #define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope) \ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ - struct __useless_struct_to_allow_trailing_semicolon__ + _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) #define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static) @@ -110,8 +96,7 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k /* For string conversions where numbers are also acceptable */ #define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,) \ - struct __useless_struct_to_allow_trailing_semicolon__ + _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static) diff --git a/src/systemd/src/basic/string-util.c b/src/systemd/src/basic/string-util.c index 047eb162..a60c7dcc 100644 --- a/src/systemd/src/basic/string-util.c +++ b/src/systemd/src/basic/string-util.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -23,15 +11,19 @@ #include #include #include +#include #include #include #include "alloc-util.h" #include "gunicode.h" +#include "locale-util.h" #include "macro.h" #include "string-util.h" +#include "terminal-util.h" #include "utf8.h" #include "util.h" +#include "fileio.h" int strcmp_ptr(const char *a, const char *b) { @@ -220,6 +212,7 @@ char *strappend(const char *s, const char *suffix) { return strnappend(s, suffix, strlen_ptr(suffix)); } +#if 0 /* NM_IGNORED */ char *strjoin_real(const char *x, ...) { va_list ap; size_t l; @@ -280,6 +273,9 @@ char *strjoin_real(const char *x, ...) { char *strstrip(char *s) { char *e; + if (!s) + return NULL; + /* Drops trailing whitespace. Modifies the string in * place. Returns pointer to first non-space character */ @@ -297,7 +293,13 @@ char *strstrip(char *s) { char *delete_chars(char *s, const char *bad) { char *f, *t; - /* Drops all whitespace, regardless where in the string */ + /* Drops all specified bad characters, regardless where in the string */ + + if (!s) + return NULL; + + if (!bad) + bad = WHITESPACE; for (f = s, t = s; *f; f++) { if (strchr(bad, *f)) @@ -311,6 +313,27 @@ char *delete_chars(char *s, const char *bad) { return s; } +char *delete_trailing_chars(char *s, const char *bad) { + char *p, *c = s; + + /* Drops all specified bad characters, at the end of the string */ + + if (!s) + return NULL; + + if (!bad) + bad = WHITESPACE; + + for (p = s; *p; p++) + if (!strchr(bad, *p)) + c = p + 1; + + *c = 0; + + return s; +} +#endif /* NM_IGNORED */ + char *truncate_nl(char *s) { assert(s); @@ -436,58 +459,104 @@ bool string_has_cc(const char *p, const char *ok) { #if 0 /* NM_IGNORED */ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { - size_t x; + size_t x, need_space; char *r; assert(s); assert(percent <= 100); - assert(new_length >= 3); + assert(new_length != (size_t) -1); - if (old_length <= 3 || old_length <= new_length) + if (old_length <= new_length) return strndup(s, old_length); - r = new0(char, new_length+3); + /* Special case short ellipsations */ + switch (new_length) { + + case 0: + return strdup(""); + + case 1: + if (is_locale_utf8()) + return strdup("…"); + else + return strdup("."); + + case 2: + if (!is_locale_utf8()) + return strdup(".."); + + break; + + default: + break; + } + + /* Calculate how much space the ellipsis will take up. If we are in UTF-8 mode we only need space for one + * character ("…"), otherwise for three characters ("..."). Note that in both cases we need 3 bytes of storage, + * either for the UTF-8 encoded character or for three ASCII characters. */ + need_space = is_locale_utf8() ? 1 : 3; + + r = new(char, new_length+3); if (!r) return NULL; - x = (new_length * percent) / 100; + assert(new_length >= need_space); - if (x > new_length - 3) - x = new_length - 3; + x = ((new_length - need_space) * percent + 50) / 100; + assert(x <= new_length - need_space); memcpy(r, s, x); - r[x] = 0xe2; /* tri-dot ellipsis: … */ - r[x+1] = 0x80; - r[x+2] = 0xa6; + + if (is_locale_utf8()) { + r[x+0] = 0xe2; /* tri-dot ellipsis: … */ + r[x+1] = 0x80; + r[x+2] = 0xa6; + } else { + r[x+0] = '.'; + r[x+1] = '.'; + r[x+2] = '.'; + } + memcpy(r + x + 3, - s + old_length - (new_length - x - 1), - new_length - x - 1); + s + old_length - (new_length - x - need_space), + new_length - x - need_space + 1); return r; } char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { - size_t x; - char *e; + size_t x, k, len, len2; const char *i, *j; - unsigned k, len, len2; + char *e; int r; + /* Note that 'old_length' refers to bytes in the string, while 'new_length' refers to character cells taken up + * on screen. This distinction doesn't matter for ASCII strings, but it does matter for non-ASCII UTF-8 + * strings. + * + * Ellipsation is done in a locale-dependent way: + * 1. If the string passed in is fully ASCII and the current locale is not UTF-8, three dots are used ("...") + * 2. Otherwise, a unicode ellipsis is used ("…") + * + * In other words: you'll get a unicode ellipsis as soon as either the string contains non-ASCII characters or + * the current locale is UTF-8. + */ + assert(s); assert(percent <= 100); - assert(new_length >= 3); - - /* if no multibyte characters use ascii_ellipsize_mem for speed */ - if (ascii_is_valid(s)) - return ascii_ellipsize_mem(s, old_length, new_length, percent); - if (old_length <= 3 || old_length <= new_length) + if (new_length == (size_t) -1) return strndup(s, old_length); - x = (new_length * percent) / 100; + if (new_length == 0) + return strdup(""); + + /* If no multibyte characters use ascii_ellipsize_mem for speed */ + if (ascii_is_valid(s)) + return ascii_ellipsize_mem(s, old_length, new_length, percent); - if (x > new_length - 3) - x = new_length - 3; + x = ((new_length - 1) * percent) / 100; + assert(x <= new_length - 1); k = 0; for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) { @@ -532,7 +601,7 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne */ memcpy(e, s, len); - e[len] = 0xe2; /* tri-dot ellipsis: … */ + e[len + 0] = 0xe2; /* tri-dot ellipsis: … */ e[len + 1] = 0x80; e[len + 2] = 0xa6; @@ -542,6 +611,10 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne } char *ellipsize(const char *s, size_t length, unsigned percent) { + + if (length == (size_t) -1) + return strdup(s); + return ellipsize_mem(s, strlen(s), length, percent); } #endif /* NM_IGNORED */ @@ -569,26 +642,26 @@ char* strshorten(char *s, size_t l) { } char *strreplace(const char *text, const char *old_string, const char *new_string) { + size_t l, old_len, new_len, allocated = 0; + char *t, *ret = NULL; const char *f; - char *t, *r; - size_t l, old_len, new_len; - assert(text); assert(old_string); assert(new_string); + if (!text) + return NULL; + old_len = strlen(old_string); new_len = strlen(new_string); l = strlen(text); - r = new(char, l+1); - if (!r) + if (!GREEDY_REALLOC(ret, allocated, l+1)) return NULL; f = text; - t = r; + t = ret; while (*f) { - char *a; size_t d, nl; if (!startswith(f, old_string)) { @@ -596,42 +669,59 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin continue; } - d = t - r; + d = t - ret; nl = l - old_len + new_len; - a = realloc(r, nl + 1); - if (!a) - goto oom; + + if (!GREEDY_REALLOC(ret, allocated, nl + 1)) + return mfree(ret); l = nl; - r = a; - t = r + d; + t = ret + d; t = stpcpy(t, new_string); f += old_len; } *t = 0; - return r; + return ret; +} -oom: - return mfree(r); +static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) { + if (!offsets) + return; + + if ((size_t) diff < offsets[0]) + shift[0] += size; + if ((size_t) diff < offsets[1]) + shift[1] += size; } -char *strip_tab_ansi(char **ibuf, size_t *_isz) { +char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) { const char *i, *begin = NULL; enum { STATE_OTHER, STATE_ESCAPE, - STATE_BRACKET + STATE_CSI, + STATE_CSO, } state = STATE_OTHER; char *obuf = NULL; - size_t osz = 0, isz; + size_t osz = 0, isz, shift[2] = {}; FILE *f; assert(ibuf); assert(*ibuf); - /* Strips ANSI color and replaces TABs by 8 spaces */ + /* This does three things: + * + * 1. Replaces TABs by 8 spaces + * 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences + * 3. Strips ANSI operating system sequences (CSO), i.e. ESC ']' … BEL sequences + * + * Everything else will be left as it is. In particular other ANSI sequences are left as they are, as are any + * other special characters. Truncated ANSI sequences are left-as is too. This call is supposed to suppress the + * most basic formatting noise, but nothing else. + * + * Why care for CSO sequences? Well, to undo what terminal_urlify() and friends generate. */ isz = _isz ? *_isz : strlen(*ibuf); @@ -639,10 +729,10 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { if (!f) return NULL; - /* Note we use the _unlocked() stdio variants on f for performance - * reasons. It's safe to do so since we created f here and it - * doesn't leave our scope. - */ + /* Note we turn off internal locking on f for performance reasons. It's safe to do so since we created f here + * and it doesn't leave our scope. */ + + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); for (i = *ibuf; i < *ibuf + isz + 1; i++) { @@ -653,42 +743,65 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { break; else if (*i == '\x1B') state = STATE_ESCAPE; - else if (*i == '\t') - fputs_unlocked(" ", f); - else - fputc_unlocked(*i, f); + else if (*i == '\t') { + fputs(" ", f); + advance_offsets(i - *ibuf, highlight, shift, 7); + } else + fputc(*i, f); + break; case STATE_ESCAPE: if (i >= *ibuf + isz) { /* EOT */ - fputc_unlocked('\x1B', f); + fputc('\x1B', f); + advance_offsets(i - *ibuf, highlight, shift, 1); break; - } else if (*i == '[') { - state = STATE_BRACKET; + } else if (*i == '[') { /* ANSI CSI */ + state = STATE_CSI; + begin = i + 1; + } else if (*i == ']') { /* ANSI CSO */ + state = STATE_CSO; begin = i + 1; } else { - fputc_unlocked('\x1B', f); - fputc_unlocked(*i, f); + fputc('\x1B', f); + fputc(*i, f); + advance_offsets(i - *ibuf, highlight, shift, 1); state = STATE_OTHER; } break; - case STATE_BRACKET: + case STATE_CSI: - if (i >= *ibuf + isz || /* EOT */ - (!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) { - fputc_unlocked('\x1B', f); - fputc_unlocked('[', f); + if (i >= *ibuf + isz || /* EOT … */ + !strchr("01234567890;m", *i)) { /* … or invalid chars in sequence */ + fputc('\x1B', f); + fputc('[', f); + advance_offsets(i - *ibuf, highlight, shift, 2); state = STATE_OTHER; i = begin-1; } else if (*i == 'm') state = STATE_OTHER; + + break; + + case STATE_CSO: + + if (i >= *ibuf + isz || /* EOT … */ + (*i != '\a' && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */ + fputc('\x1B', f); + fputc(']', f); + advance_offsets(i - *ibuf, highlight, shift, 2); + state = STATE_OTHER; + i = begin-1; + } else if (*i == '\a') + state = STATE_OTHER; + break; } } - if (ferror(f)) { + if (fflush_and_check(f) < 0) { fclose(f); return mfree(obuf); } @@ -701,19 +814,29 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { if (_isz) *_isz = osz; + if (highlight) { + highlight[0] += shift[0]; + highlight[1] += shift[1]; + } + return obuf; } -char *strextend(char **x, ...) { - va_list ap; - size_t f, l; +#if 0 /* NM_IGNORED */ +char *strextend_with_separator(char **x, const char *separator, ...) { + bool need_separator; + size_t f, l, l_separator; char *r, *p; + va_list ap; assert(x); l = f = strlen_ptr(*x); - va_start(ap, x); + need_separator = !isempty(*x); + l_separator = strlen_ptr(separator); + + va_start(ap, separator); for (;;) { const char *t; size_t n; @@ -723,22 +846,29 @@ char *strextend(char **x, ...) { break; n = strlen(t); + + if (need_separator) + n += l_separator; + if (n > ((size_t) -1) - l) { va_end(ap); return NULL; } l += n; + need_separator = true; } va_end(ap); + need_separator = !isempty(*x); + r = realloc(*x, l+1); if (!r) return NULL; p = r + f; - va_start(ap, x); + va_start(ap, separator); for (;;) { const char *t; @@ -746,15 +876,23 @@ char *strextend(char **x, ...) { if (!t) break; + if (need_separator && separator) + p = stpcpy(p, separator); + p = stpcpy(p, t); + + need_separator = true; } va_end(ap); + assert(p == r + l); + *p = 0; *x = r; return r + l; } +#endif /* NM_IGNORED */ char *strrep(const char *s, unsigned n) { size_t l; diff --git a/src/systemd/src/basic/string-util.h b/src/systemd/src/basic/string-util.h index 4c94b182..5a10eeab 100644 --- a/src/systemd/src/basic/string-util.h +++ b/src/systemd/src/basic/string-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 @@ -51,15 +39,15 @@ static inline bool streq_ptr(const char *a, const char *b) { } static inline const char* strempty(const char *s) { - return s ? s : ""; + return s ?: ""; } static inline const char* strnull(const char *s) { - return s ? s : "(null)"; + return s ?: "(null)"; } static inline const char *strna(const char *s) { - return s ? s : "n/a"; + return s ?: "n/a"; } static inline bool isempty(const char *p) { @@ -70,7 +58,7 @@ static inline const char *empty_to_null(const char *p) { return isempty(p) ? NULL : p; } -static inline const char *strdash_if_empty(const char *str) { +static inline const char *empty_to_dash(const char *str) { return isempty(str) ? "-" : str; } @@ -121,7 +109,7 @@ char *strjoin_real(const char *x, ...) _sentinel_; const char *_appendees_[] = { a, __VA_ARGS__ }; \ char *_d_, *_p_; \ size_t _len_ = 0; \ - unsigned _i_; \ + size_t _i_; \ for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \ _len_ += strlen(_appendees_[_i_]); \ _p_ = _d_ = alloca(_len_ + 1); \ @@ -133,8 +121,20 @@ char *strjoin_real(const char *x, ...) _sentinel_; char *strstrip(char *s); char *delete_chars(char *s, const char *bad); +char *delete_trailing_chars(char *s, const char *bad); char *truncate_nl(char *s); +static inline char *skip_leading_chars(const char *s, const char *bad) { + + if (!s) + return NULL; + + if (!bad) + bad = WHITESPACE; + + return (char*) s + strspn(s, bad); +} + char ascii_tolower(char x); char *ascii_strlower(char *s); char *ascii_strlower_n(char *s, size_t n); @@ -164,9 +164,11 @@ char* strshorten(char *s, size_t l); char *strreplace(const char *text, const char *old_string, const char *new_string); -char *strip_tab_ansi(char **p, size_t *l); +char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]); + +char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_; -char *strextend(char **x, ...) _sentinel_; +#define strextend(x, ...) strextend_with_separator(x, NULL, __VA_ARGS__) char *strrep(const char *s, unsigned n); diff --git a/src/systemd/src/basic/strv.c b/src/systemd/src/basic/strv.c index 08bcff6e..9d5000aa 100644 --- a/src/systemd/src/basic/strv.c +++ b/src/systemd/src/basic/strv.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -121,8 +109,8 @@ char **strv_copy(char * const *l) { return r; } -unsigned strv_length(char * const *l) { - unsigned n = 0; +size_t strv_length(char * const *l) { + size_t n = 0; if (!l) return 0; @@ -135,8 +123,8 @@ unsigned strv_length(char * const *l) { char **strv_new_ap(const char *x, va_list ap) { const char *s; - char **a; - unsigned n = 0, i = 0; + _cleanup_strv_free_ char **a = NULL; + size_t n = 0, i = 0; va_list aq; /* As a special trick we ignore all listed strings that equal @@ -166,7 +154,7 @@ char **strv_new_ap(const char *x, va_list ap) { if (x != STRV_IGNORE) { a[i] = strdup(x); if (!a[i]) - goto fail; + return NULL; i++; } @@ -177,7 +165,7 @@ char **strv_new_ap(const char *x, va_list ap) { a[i] = strdup(s); if (!a[i]) - goto fail; + return NULL; i++; } @@ -185,11 +173,7 @@ char **strv_new_ap(const char *x, va_list ap) { a[i] = NULL; - return a; - -fail: - strv_free(a); - return NULL; + return TAKE_PTR(a); } char **strv_new(const char *x, ...) { @@ -215,7 +199,7 @@ int strv_extend_strv(char ***a, char **b, bool filter_duplicates) { p = strv_length(*a); q = strv_length(b); - t = realloc(*a, sizeof(char*) * (p + q + 1)); + t = reallocarray(*a, p + q + 1, sizeof(char *)); if (!t) return -ENOMEM; @@ -271,7 +255,7 @@ int strv_extend_strv_concat(char ***a, char **b, const char *suffix) { char **strv_split(const char *s, const char *separator) { const char *word, *state; size_t l; - unsigned n, i; + size_t n, i; char **r; assert(s); @@ -301,7 +285,7 @@ char **strv_split(const char *s, const char *separator) { char **strv_split_newlines(const char *s) { char **l; - unsigned n; + size_t n; assert(s); @@ -343,8 +327,7 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract if (!GREEDY_REALLOC(l, allocated, n + 2)) return -ENOMEM; - l[n++] = word; - word = NULL; + l[n++] = TAKE_PTR(word); l[n] = NULL; } @@ -355,8 +338,7 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract return -ENOMEM; } - *t = l; - l = NULL; + *t = TAKE_PTR(l); return (int) n; } @@ -396,45 +378,9 @@ char *strv_join(char **l, const char *separator) { return r; } -char *strv_join_quoted(char **l) { - char *buf = NULL; - char **s; - size_t allocated = 0, len = 0; - - STRV_FOREACH(s, l) { - /* assuming here that escaped string cannot be more - * than twice as long, and reserving space for the - * separator and quotes. - */ - _cleanup_free_ char *esc = NULL; - size_t needed; - - if (!GREEDY_REALLOC(buf, allocated, - len + strlen(*s) * 2 + 3)) - goto oom; - - esc = cescape(*s); - if (!esc) - goto oom; - - needed = snprintf(buf + len, allocated - len, "%s\"%s\"", - len > 0 ? " " : "", esc); - assert(needed < allocated - len); - len += needed; - } - - if (!buf) - buf = malloc0(1); - - return buf; - - oom: - return mfree(buf); -} - int strv_push(char ***l, char *value) { char **c; - unsigned n, m; + size_t n, m; if (!value) return 0; @@ -446,7 +392,7 @@ int strv_push(char ***l, char *value) { if (m < n) return -ENOMEM; - c = realloc_multiply(*l, sizeof(char*), m); + c = reallocarray(*l, m, sizeof(char*)); if (!c) return -ENOMEM; @@ -459,7 +405,7 @@ int strv_push(char ***l, char *value) { int strv_push_pair(char ***l, char *a, char *b) { char **c; - unsigned n, m; + size_t n, m; if (!a && !b) return 0; @@ -471,7 +417,7 @@ int strv_push_pair(char ***l, char *a, char *b) { if (m < n) return -ENOMEM; - c = realloc_multiply(*l, sizeof(char*), m); + c = reallocarray(*l, m, sizeof(char*)); if (!c) return -ENOMEM; @@ -485,14 +431,15 @@ int strv_push_pair(char ***l, char *a, char *b) { return 0; } -int strv_push_prepend(char ***l, char *value) { +int strv_insert(char ***l, size_t position, char *value) { char **c; - unsigned n, m, i; + size_t n, m, i; if (!value) return 0; n = strv_length(*l); + position = MIN(position, n); /* increase and check for overflow */ m = n + 2; @@ -503,10 +450,12 @@ int strv_push_prepend(char ***l, char *value) { if (!c) return -ENOMEM; - for (i = 0; i < n; i++) + for (i = 0; i < position; i++) + c[i] = (*l)[i]; + c[position] = value; + for (i = position; i < n; i++) c[i+1] = (*l)[i]; - c[0] = value; c[n+1] = NULL; free(*l); @@ -582,7 +531,7 @@ int strv_extend_front(char ***l, const char *value) { if (!v) return -ENOMEM; - c = realloc_multiply(*l, sizeof(char*), m); + c = reallocarray(*l, m, sizeof(char*)); if (!c) { free(v); return -ENOMEM; @@ -652,7 +601,7 @@ char **strv_parse_nulstr(const char *s, size_t l) { */ const char *p; - unsigned c = 0, i = 0; + size_t c = 0, i = 0; char **v; assert(s || l <= 0); @@ -816,7 +765,7 @@ int strv_extendf(char ***l, const char *format, ...) { } char **strv_reverse(char **l) { - unsigned n, i; + size_t n, i; n = strv_length(l); if (n <= 1) @@ -897,7 +846,7 @@ int strv_extend_n(char ***l, const char *value, size_t n) { k = strv_length(*l); - nl = realloc(*l, sizeof(char*) * (k + n + 1)); + nl = reallocarray(*l, k + n + 1, sizeof(char *)); if (!nl) return -ENOMEM; diff --git a/src/systemd/src/basic/strv.h b/src/systemd/src/basic/strv.h index 385ad177..958c5f3a 100644 --- a/src/systemd/src/basic/strv.h +++ b/src/systemd/src/basic/strv.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 @@ -44,7 +32,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase); void strv_clear(char **l); char **strv_copy(char * const *l); -unsigned strv_length(char * const *l) _pure_; +size_t strv_length(char * const *l) _pure_; int strv_extend_strv(char ***a, char **b, bool filter_duplicates); int strv_extend_strv_concat(char ***a, char **b, const char *suffix); @@ -53,7 +41,12 @@ int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); int strv_extend_front(char ***l, const char *value); int strv_push(char ***l, char *value); int strv_push_pair(char ***l, char *a, char *b); -int strv_push_prepend(char ***l, char *value); +int strv_insert(char ***l, size_t position, char *value); + +static inline int strv_push_prepend(char ***l, char *value) { + return strv_insert(l, 0, value); +} + int strv_consume(char ***l, char *value); int strv_consume_pair(char ***l, char *a, char *b); int strv_consume_prepend(char ***l, char *value); @@ -85,7 +78,6 @@ char **strv_split_newlines(const char *s); int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags); char *strv_join(char **l, const char *separator); -char *strv_join_quoted(char **l); char **strv_parse_nulstr(const char *s, size_t l); char **strv_split_nulstr(const char *s); @@ -121,7 +113,7 @@ void strv_print(char **l); if (!first) \ _l = (char**) &first; \ else { \ - unsigned _n; \ + size_t _n; \ va_list _ap; \ \ _n = 1; \ @@ -174,9 +166,18 @@ static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, i } char ***strv_free_free(char ***l); +DEFINE_TRIVIAL_CLEANUP_FUNC(char***, strv_free_free); char **strv_skip(char **l, size_t n); int strv_extend_n(char ***l, const char *value, size_t n); int fputstrv(FILE *f, char **l, const char *separator, bool *space); + +#define strv_free_and_replace(a, b) \ + ({ \ + strv_free(a); \ + (a) = (b); \ + (b) = NULL; \ + 0; \ + }) diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c index 7f1c3f7c..fa9f66f2 100644 --- a/src/systemd/src/basic/time-util.c +++ b/src/systemd/src/basic/time-util.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -35,10 +23,13 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "io-util.h" #include "log.h" #include "macro.h" #include "parse-util.h" #include "path-util.h" +#include "process-util.h" +#include "stat-util.h" #include "string-util.h" #include "strv.h" #include "time-util.h" @@ -449,7 +440,7 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { { "us", 1 }, }; - unsigned i; + size_t i; char *p = buf; bool something = false; @@ -628,10 +619,9 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { time_t x; usec_t x_usec, plus = 0, minus = 0, ret; int r, weekday = -1, dst = -1; - unsigned i; + size_t i; - /* - * Allowed syntaxes: + /* Allowed syntaxes: * * 2012-09-22 16:34:22 * 2012-09-22 16:34 (seconds will be set to 0) @@ -645,7 +635,6 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { * +5min * -5days * @2147483647 (seconds since epoch) - * */ assert(t); @@ -704,10 +693,10 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { tzset(); /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only - * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because - * there are no nice APIs available to cover this. By accepting the local time zone strings, we make - * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't - * support arbitrary timezone specifications. */ + * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because + * there are no nice APIs available to cover this. By accepting the local time zone strings, we make + * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't + * support arbitrary timezone specifications. */ for (j = 0; j <= 1; j++) { @@ -891,28 +880,24 @@ int parse_timestamp(const char *t, usec_t *usec) { char *last_space, *tz = NULL; ParseTimestampResult *shared, tmp; int r; - pid_t pid; last_space = strrchr(t, ' '); - if (last_space != NULL && timezone_is_valid(last_space + 1)) + if (last_space != NULL && timezone_is_valid(last_space + 1, LOG_DEBUG)) tz = last_space + 1; - if (tz == NULL || endswith_no_case(t, " UTC")) + if (!tz || endswith_no_case(t, " UTC")) return parse_timestamp_impl(t, usec, false); shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); if (shared == MAP_FAILED) return negative_errno(); - pid = fork(); - - if (pid == -1) { - int fork_errno = errno; + r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL); + if (r < 0) { (void) munmap(shared, sizeof *shared); - return -fork_errno; + return r; } - - if (pid == 0) { + if (r == 0) { bool with_tz = true; if (setenv("TZ", tz, 1) != 0) { @@ -923,10 +908,10 @@ int parse_timestamp(const char *t, usec_t *usec) { tzset(); /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation. - * Otherwise just cut it off */ + * Otherwise just cut it off. */ with_tz = !STR_IN_SET(tz, tzname[0], tzname[1]); - /*cut off the timezone if we dont need it*/ + /* Cut off the timezone if we dont need it. */ if (with_tz) t = strndupa(t, last_space - t); @@ -935,12 +920,6 @@ int parse_timestamp(const char *t, usec_t *usec) { _exit(EXIT_SUCCESS); } - r = wait_for_terminate(pid, NULL); - if (r < 0) { - (void) munmap(shared, sizeof *shared); - return r; - } - tmp = *shared; if (munmap(shared, sizeof *shared) != 0) return negative_errno(); @@ -986,7 +965,7 @@ static char* extract_multiplier(char *p, usec_t *multiplier) { { "us", 1ULL }, { "µs", 1ULL }, }; - unsigned i; + size_t i; for (i = 0; i < ELEMENTSOF(table); i++) { char *e; @@ -1160,8 +1139,8 @@ int parse_nsec(const char *t, nsec_t *nsec) { for (;;) { long long l, z = 0; + size_t n = 0, i; char *e; - unsigned i, n = 0; p += strspn(p, WHITESPACE); @@ -1296,17 +1275,18 @@ int get_timezones(char ***ret) { } else if (errno != ENOENT) return -errno; - *ret = zones; - zones = NULL; + *ret = TAKE_PTR(zones); return 0; } #endif /* NM_IGNORED */ -bool timezone_is_valid(const char *name) { +bool timezone_is_valid(const char *name, int log_level) { bool slash = false; const char *p, *t; - struct stat st; + _cleanup_close_ int fd = -1; + char buf[4]; + int r; if (isempty(name)) return false; @@ -1335,11 +1315,30 @@ bool timezone_is_valid(const char *name) { return false; t = strjoina("/usr/share/zoneinfo/", name); - if (stat(t, &st) < 0) + + fd = open(t, O_RDONLY|O_CLOEXEC); + if (fd < 0) { + log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t); return false; + } + + r = fd_verify_regular(fd); + if (r < 0) { + log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t); + return false; + } + + r = loop_read_exact(fd, buf, 4, false); + if (r < 0) { + log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t); + return false; + } - if (!S_ISREG(st.st_mode)) + /* Magic from tzfile(5) */ + if (memcmp(buf, "TZif", 4) != 0) { + log_full(log_level, "Timezone file '%s' has wrong magic bytes", t); return false; + } return true; } @@ -1387,8 +1386,7 @@ bool clock_supported(clockid_t clock) { if (!clock_boottime_supported()) return false; - /* fall through */ - + _fallthrough_; default: /* For everything else, check properly */ return clock_gettime(clock, &ts) >= 0; @@ -1412,7 +1410,7 @@ int get_timezone(char **tz) { if (!e) return -EINVAL; - if (!timezone_is_valid(e)) + if (!timezone_is_valid(e, LOG_DEBUG)) return -EINVAL; z = strdup(e); @@ -1463,4 +1461,10 @@ usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) { /* x lies in the past */ return usec_sub_unsigned(b, usec_sub_unsigned(a, x)); } + +bool in_utc_timezone(void) { + tzset(); + + return timezone == 0 && daylight == 0; +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h index 73f7e400..e720688c 100644 --- a/src/systemd/src/basic/time-util.h +++ b/src/systemd/src/basic/time-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 @@ -99,12 +87,12 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) #define TRIPLE_TIMESTAMP_HAS_CLOCK(clock) \ IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) -static inline bool dual_timestamp_is_set(dual_timestamp *ts) { +static inline bool dual_timestamp_is_set(const dual_timestamp *ts) { return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY)); } -static inline bool triple_timestamp_is_set(triple_timestamp *ts) { +static inline bool triple_timestamp_is_set(const triple_timestamp *ts) { return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY) || (ts->boottime > 0 && ts->boottime != USEC_INFINITY)); @@ -140,7 +128,7 @@ int parse_nsec(const char *t, nsec_t *nsec); bool ntp_synced(void); int get_timezones(char ***l); -bool timezone_is_valid(const char *name); +bool timezone_is_valid(const char *name, int log_level); bool clock_boottime_supported(void); bool clock_supported(clockid_t clock); @@ -155,6 +143,8 @@ struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc); unsigned long usec_to_jiffies(usec_t usec); +bool in_utc_timezone(void); + static inline usec_t usec_add(usec_t a, usec_t b) { usec_t c; diff --git a/src/systemd/src/basic/umask-util.h b/src/systemd/src/basic/umask-util.h index 359d87d2..ce705247 100644 --- a/src/systemd/src/basic/umask-util.h +++ b/src/systemd/src/basic/umask-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 diff --git a/src/systemd/src/basic/utf8.c b/src/systemd/src/basic/utf8.c index ff281e43..7a382204 100644 --- a/src/systemd/src/basic/utf8.c +++ b/src/systemd/src/basic/utf8.c @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2008-2011 Kay Sievers Copyright 2012 Lennart Poettering - - 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 . ***/ /* Parts of this file are based on the GLIB utf8 validation functions. The @@ -49,6 +37,7 @@ #include #include "alloc-util.h" +#include "gunicode.h" #include "hexdecoct.h" #include "macro.h" #include "utf8.h" @@ -409,3 +398,44 @@ int utf8_encoded_valid_unichar(const char *str) { return len; } + +size_t utf8_n_codepoints(const char *str) { + size_t n = 0; + + /* Returns the number of UTF-8 codepoints in this string, or (size_t) -1 if the string is not valid UTF-8. */ + + while (*str != 0) { + int k; + + k = utf8_encoded_valid_unichar(str); + if (k < 0) + return (size_t) -1; + + str += k; + n++; + } + + return n; +} + +#if 0 /* NM_IGNORED */ +size_t utf8_console_width(const char *str) { + size_t n = 0; + + /* Returns the approximate width a string will take on screen when printed on a character cell + * terminal/console. */ + + while (*str != 0) { + char32_t c; + + if (utf8_encoded_to_unichar(str, &c) < 0) + return (size_t) -1; + + str = utf8_next_char(str); + + n += unichar_iswide(c) ? 2 : 1; + } + + return n; +} +#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/utf8.h b/src/systemd/src/basic/utf8.h index 322dac20..0ebe36d9 100644 --- a/src/systemd/src/basic/utf8.h +++ b/src/systemd/src/basic/utf8.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2012 Lennart Poettering - - 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 @@ -60,3 +48,6 @@ static inline bool utf16_is_trailing_surrogate(char16_t c) { static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t trail) { return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000; } + +size_t utf8_n_codepoints(const char *str); +size_t utf8_console_width(const char *str); diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c index c8a22d68..12e2f451 100644 --- a/src/systemd/src/basic/util.c +++ b/src/systemd/src/basic/util.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -40,6 +28,7 @@ #include "build.h" #include "cgroup-util.h" #include "def.h" +#include "device-nodes.h" #include "dirent-util.h" #include "fd-util.h" #include "fileio.h" @@ -52,6 +41,7 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" +#include "procfs-util.h" #include "set.h" #include "signal-util.h" #include "stat-util.h" @@ -61,6 +51,7 @@ #include "umask-util.h" #include "user-util.h" #include "util.h" +#include "virt.h" #if 0 /* NM_IGNORED */ int saved_argc = 0; @@ -108,7 +99,7 @@ int socket_from_display(const char *display, char **path) { k = strspn(display+1, "0123456789"); - f = new(char, strlen("/tmp/.X11-unix/X") + k + 1); + f = new(char, STRLEN("/tmp/.X11-unix/X") + k + 1); if (!f) return -ENOMEM; @@ -121,66 +112,6 @@ int socket_from_display(const char *display, char **path) { return 0; } -int block_get_whole_disk(dev_t d, dev_t *ret) { - char *p, *s; - int r; - unsigned n, m; - - assert(ret); - - /* If it has a queue this is good enough for us */ - if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0) - return -ENOMEM; - - r = access(p, F_OK); - free(p); - - if (r >= 0) { - *ret = d; - return 0; - } - - /* If it is a partition find the originating device */ - if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0) - return -ENOMEM; - - r = access(p, F_OK); - free(p); - - if (r < 0) - return -ENOENT; - - /* Get parent dev_t */ - if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0) - return -ENOMEM; - - r = read_one_line_file(p, &s); - free(p); - - if (r < 0) - return r; - - r = sscanf(s, "%u:%u", &m, &n); - free(s); - - if (r != 2) - return -EINVAL; - - /* Only return this if it is really good enough for us. */ - if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0) - return -ENOMEM; - - r = access(p, F_OK); - free(p); - - if (r >= 0) { - *ret = makedev(m, n); - return 0; - } - - return -ENOENT; -} - bool kexec_loaded(void) { _cleanup_free_ char *s = NULL; @@ -208,112 +139,6 @@ int prot_from_flags(int flags) { } } -int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) { - bool stdout_is_tty, stderr_is_tty; - pid_t parent_pid, agent_pid; - sigset_t ss, saved_ss; - unsigned n, i; - va_list ap; - char **l; - - assert(pid); - assert(path); - - /* Spawns a temporary TTY agent, making sure it goes away when - * we go away */ - - parent_pid = getpid_cached(); - - /* First we temporarily block all signals, so that the new - * child has them blocked initially. This way, we can be sure - * that SIGTERMs are not lost we might send to the agent. */ - assert_se(sigfillset(&ss) >= 0); - assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0); - - agent_pid = fork(); - if (agent_pid < 0) { - assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0); - return -errno; - } - - if (agent_pid != 0) { - assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0); - *pid = agent_pid; - return 0; - } - - /* In the child: - * - * Make sure the agent goes away when the parent dies */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - _exit(EXIT_FAILURE); - - /* Make sure we actually can kill the agent, if we need to, in - * case somebody invoked us from a shell script that trapped - * SIGTERM or so... */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - /* Check whether our parent died before we were able - * to set the death signal and unblock the signals */ - if (getppid() != parent_pid) - _exit(EXIT_SUCCESS); - - /* Don't leak fds to the agent */ - close_all_fds(except, n_except); - - stdout_is_tty = isatty(STDOUT_FILENO); - stderr_is_tty = isatty(STDERR_FILENO); - - if (!stdout_is_tty || !stderr_is_tty) { - int fd; - - /* Detach from stdout/stderr. and reopen - * /dev/tty for them. This is important to - * ensure that when systemctl is started via - * popen() or a similar call that expects to - * read EOF we actually do generate EOF and - * not delay this indefinitely by because we - * keep an unused copy of stdin around. */ - fd = open("/dev/tty", O_WRONLY); - if (fd < 0) { - log_error_errno(errno, "Failed to open /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (fd > STDERR_FILENO) - close(fd); - } - - /* Count arguments */ - va_start(ap, path); - for (n = 0; va_arg(ap, char*); n++) - ; - va_end(ap); - - /* Allocate strv */ - l = alloca(sizeof(char *) * (n + 1)); - - /* Fill in arguments */ - va_start(ap, path); - for (i = 0; i <= n; i++) - l[i] = va_arg(ap, char*); - va_end(ap); - - execv(path, l); - _exit(EXIT_FAILURE); -} - bool in_initrd(void) { struct statfs s; @@ -348,11 +173,13 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, const void *p; int comparison; + assert(!size_multiply_overflow(nmemb, size)); + l = 0; u = nmemb; while (l < u) { idx = (l + u) / 2; - p = (const char *) base + idx * size; + p = (const uint8_t*) base + idx * size; comparison = compar(key, p, arg); if (comparison < 0) u = idx; @@ -641,31 +468,22 @@ uint64_t physical_memory_scale(uint64_t v, uint64_t max) { uint64_t system_tasks_max(void) { -#if SIZEOF_PID_T == 4 -#define TASKS_MAX ((uint64_t) (INT32_MAX-1)) -#elif SIZEOF_PID_T == 2 -#define TASKS_MAX ((uint64_t) (INT16_MAX-1)) -#else -#error "Unknown pid_t size" -#endif - - _cleanup_free_ char *value = NULL, *root = NULL; uint64_t a = TASKS_MAX, b = TASKS_MAX; + _cleanup_free_ char *root = NULL; /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this * limit: * - * a) the maximum value for the pid_t type + * a) the maximum tasks value the kernel allows on this architecture * b) the cgroups pids_max attribute for the system - * c) the kernel's configure maximum PID value + * c) the kernel's configured maximum PID value * * And then pick the smallest of the three */ - if (read_one_line_file("/proc/sys/kernel/pid_max", &value) >= 0) - (void) safe_atou64(value, &a); + (void) procfs_tasks_get_limit(&a); if (cg_get_root_path(&root) >= 0) { - value = mfree(value); + _cleanup_free_ char *value = NULL; if (cg_get_attribute("pids", root, "pids.max", &value) >= 0) (void) safe_atou64(value, &b); @@ -694,162 +512,83 @@ uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) { return m / max; } -int update_reboot_parameter_and_warn(const char *param) { - int r; - - if (isempty(param)) { - if (unlink("/run/systemd/reboot-param") < 0) { - if (errno == ENOENT) - return 0; - - return log_warning_errno(errno, "Failed to unlink reboot parameter file: %m"); - } - - return 0; - } - - RUN_WITH_UMASK(0022) { - r = write_string_file("/run/systemd/reboot-param", param, WRITE_STRING_FILE_CREATE); - if (r < 0) - return log_warning_errno(r, "Failed to write reboot parameter file: %m"); - } - - return 0; -} - int version(void) { puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES); return 0; } -int get_block_device(const char *path, dev_t *dev) { - struct stat st; - struct statfs sfs; - - assert(path); - assert(dev); - - /* Get's the block device directly backing a file system. If - * the block device is encrypted, returns the device mapper - * block device. */ - - if (lstat(path, &st)) - return -errno; - - if (major(st.st_dev) != 0) { - *dev = st.st_dev; - return 1; - } - - if (statfs(path, &sfs) < 0) - return -errno; - - if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) - return btrfs_get_block_device(path, dev); - - return 0; +/* This is a direct translation of str_verscmp from boot.c */ +static bool is_digit(int c) { + return c >= '0' && c <= '9'; } -int get_block_device_harder(const char *path, dev_t *dev) { - _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *p = NULL, *t = NULL; - struct dirent *de, *found = NULL; - const char *q; - unsigned maj, min; - dev_t dt; - int r; - - assert(path); - assert(dev); +static int c_order(int c) { + if (c == 0 || is_digit(c)) + return 0; - /* Gets the backing block device for a file system, and - * handles LUKS encrypted file systems, looking for its - * immediate parent, if there is one. */ + if ((c >= 'a') && (c <= 'z')) + return c; - r = get_block_device(path, &dt); - if (r <= 0) - return r; + return c + 0x10000; +} - if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0) - return -ENOMEM; +int str_verscmp(const char *s1, const char *s2) { + const char *os1, *os2; - d = opendir(p); - if (!d) { - if (errno == ENOENT) - goto fallback; + assert(s1); + assert(s2); - return -errno; - } + os1 = s1; + os2 = s2; - FOREACH_DIRENT_ALL(de, d, return -errno) { + while (*s1 || *s2) { + int first; - if (dot_or_dot_dot(de->d_name)) - continue; + while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) { + int order; - if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) - continue; - - if (found) { - _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL; - - /* We found a device backed by multiple other devices. We don't really support automatic - * discovery on such setups, with the exception of dm-verity partitions. In this case there are - * two backing devices: the data partition and the hash partition. We are fine with such - * setups, however, only if both partitions are on the same physical device. Hence, let's - * verify this. */ - - u = strjoin(p, "/", de->d_name, "/../dev"); - if (!u) - return -ENOMEM; - - v = strjoin(p, "/", found->d_name, "/../dev"); - if (!v) - return -ENOMEM; - - r = read_one_line_file(u, &a); - if (r < 0) { - log_debug_errno(r, "Failed to read %s: %m", u); - goto fallback; - } - - r = read_one_line_file(v, &b); - if (r < 0) { - log_debug_errno(r, "Failed to read %s: %m", v); - goto fallback; - } - - /* Check if the parent device is the same. If not, then the two backing devices are on - * different physical devices, and we don't support that. */ - if (!streq(a, b)) - goto fallback; + order = c_order(*s1) - c_order(*s2); + if (order != 0) + return order; + s1++; + s2++; } - found = de; - } - - if (!found) - goto fallback; + while (*s1 == '0') + s1++; + while (*s2 == '0') + s2++; + + first = 0; + while (is_digit(*s1) && is_digit(*s2)) { + if (first == 0) + first = *s1 - *s2; + s1++; + s2++; + } - q = strjoina(p, "/", found->d_name, "/dev"); + if (is_digit(*s1)) + return 1; + if (is_digit(*s2)) + return -1; - r = read_one_line_file(q, &t); - if (r == -ENOENT) - goto fallback; - if (r < 0) - return r; + if (first != 0) + return first; + } - if (sscanf(t, "%u:%u", &maj, &min) != 2) - return -EINVAL; + return strcmp(os1, os2); +} - if (maj == 0) - goto fallback; +/* Turn off core dumps but only if we're running outside of a container. */ +void disable_coredumps(void) { + int r; - *dev = makedev(maj, min); - return 1; + if (detect_container() > 0) + return; -fallback: - *dev = dt; - return 1; + r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0); + if (r < 0) + log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m"); } #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h index b31dfd1c..ad1931f7 100644 --- a/src/systemd/src/basic/util.h +++ b/src/systemd/src/basic/util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2010 Lennart Poettering - - 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 @@ -70,8 +58,6 @@ bool plymouth_running(void); bool display_is_local(const char *display) _pure_; int socket_from_display(const char *display, char **path); -int block_get_whole_disk(dev_t d, dev_t *ret); - #define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) @@ -85,8 +71,6 @@ bool kexec_loaded(void); int prot_from_flags(int flags) _const_; -int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...); - bool in_initrd(void); void in_initrd_force(bool value); @@ -94,6 +78,19 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, int (*compar) (const void *, const void *, void *), void *arg); +/** + * Normal bsearch requires base to be nonnull. Here were require + * that only if nmemb > 0. + */ +static inline void* bsearch_safe(const void *key, const void *base, + size_t nmemb, size_t size, comparison_fn_t compar) { + if (nmemb <= 0) + return NULL; + + assert(base); + return bsearch(key, base, nmemb, size, compar); +} + /** * Normal qsort requires base to be nonnull. Here were require * that only if nmemb > 0. @@ -106,6 +103,22 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_ qsort(base, nmemb, size, compar); } +/* A wrapper around the above, but that adds typesafety: the element size is automatically derived from the type and so + * is the prototype for the comparison function */ +#define typesafe_qsort(p, n, func) \ + ({ \ + int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ + qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ + }) + +static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, int (*compar)(const void*, const void*, void*), void *userdata) { + if (nmemb <= 1) + return; + + assert(base); + qsort_r(base, nmemb, size, compar, userdata); +} + /** * Normal memcpy requires src to be nonnull. We do nothing if n is 0. */ @@ -189,9 +202,8 @@ uint64_t physical_memory_scale(uint64_t v, uint64_t max); uint64_t system_tasks_max(void); uint64_t system_tasks_max_scale(uint64_t v, uint64_t max); -int update_reboot_parameter_and_warn(const char *param); - int version(void); -int get_block_device(const char *path, dev_t *dev); -int get_block_device_harder(const char *path, dev_t *dev); +int str_verscmp(const char *s1, const char *s2); + +void disable_coredumps(void); diff --git a/src/systemd/src/libsystemd-network/arp-util.c b/src/systemd/src/libsystemd-network/arp-util.c index 69bd3e75..26db7ea8 100644 --- a/src/systemd/src/libsystemd-network/arp-util.c +++ b/src/systemd/src/libsystemd-network/arp-util.c @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2014 Axis Communications AB. All rights reserved. Copyright (C) 2015 Tom Gundersen - - 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 "nm-sd-adapt.h" @@ -25,6 +13,7 @@ #include "arp-util.h" #include "fd-util.h" +#include "unaligned.h" #include "util.h" int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) { @@ -49,12 +38,12 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ /* Sender Hardware Address must be different from our own */ - BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) eth_mac))), /* A <- 4 bytes of client's MAC */ + BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(ð_mac->ether_addr_octet[0])),/* A <- 4 bytes of client's MAC */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */ - BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */ + BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(ð_mac->ether_addr_octet[4])),/* A <- remainder of client's MAC */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ @@ -103,10 +92,7 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_ if (r < 0) return -errno; - r = s; - s = -1; - - return r; + return TAKE_FD(s); } static int arp_send_packet(int fd, int ifindex, diff --git a/src/systemd/src/libsystemd-network/arp-util.h b/src/systemd/src/libsystemd-network/arp-util.h index 3ef56b00..86864e9f 100644 --- a/src/systemd/src/libsystemd-network/arp-util.h +++ b/src/systemd/src/libsystemd-network/arp-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright (C) 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 diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c index c1fa8763..6f8adfa6 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.c +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2015 Tom Gundersen - - 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 "nm-sd-adapt.h" @@ -98,7 +86,7 @@ int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_i #if 0 /* NM_IGNORED */ /* name is a pointer to memory in the udev_device struct, so must have the same scope */ - _cleanup_udev_device_unref_ struct udev_device *device = NULL; + _cleanup_(udev_device_unrefp) struct udev_device *device = NULL; #else /* NM_IGNORED */ char name_buf[IF_NAMESIZE]; #endif /* NM_IGNORED */ @@ -108,7 +96,7 @@ int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_i #if 0 /* NM_IGNORED */ if (detect_container() <= 0) { /* not in a container, udev will be around */ - _cleanup_udev_unref_ struct udev *udev; + _cleanup_(udev_unrefp) struct udev *udev; char ifindex_str[2 + DECIMAL_STR_MAX(int)]; udev = udev_new(); diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.h b/src/systemd/src/libsystemd-network/dhcp-identifier.h index 1cc0f9fb..c0a10b6c 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.h +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright (C) 2015 Tom Gundersen - - 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 "sd-id128.h" diff --git a/src/systemd/src/libsystemd-network/dhcp-internal.h b/src/systemd/src/libsystemd-network/dhcp-internal.h index 3fdf02da..f8276222 100644 --- a/src/systemd/src/libsystemd-network/dhcp-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp-internal.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -5,19 +6,6 @@ Copyright (C) 2013 Intel Corporation. All rights reserved. Copyright (C) 2014 Tom Gundersen - - 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 diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h index 7847ce07..66c213e2 100644 --- a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -5,19 +6,6 @@ Copyright (C) 2013 Intel Corporation. All rights reserved. Copyright (C) 2014 Tom Gundersen - - 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 @@ -33,6 +21,8 @@ struct sd_dhcp_route { struct in_addr dst_addr; struct in_addr gw_addr; unsigned char dst_prefixlen; + + uint8_t option; }; struct sd_dhcp_raw_option { diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c index f01b2cfe..37c1ba19 100644 --- a/src/systemd/src/libsystemd-network/dhcp-network.c +++ b/src/systemd/src/libsystemd-network/dhcp-network.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2013 Intel Corporation. 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 "nm-sd-adapt.h" @@ -33,6 +21,7 @@ #include "dhcp-internal.h" #include "fd-util.h" #include "socket-util.h" +#include "unaligned.h" static int _bind_raw_socket(int ifindex, union sockaddr_union *link, uint32_t xid, const uint8_t *mac_addr, @@ -71,13 +60,13 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((unsigned int *) eth_mac))), /* A <- 4 bytes of client's MAC */ + BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(ð_mac->ether_addr_octet[0])), /* A <- 4 bytes of client's MAC */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((unsigned short *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */ + BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(ð_mac->ether_addr_octet[4])), /* A <- remainder of client's MAC */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ @@ -123,10 +112,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, if (r < 0) return -errno; - r = s; - s = -1; - - return r; + return TAKE_FD(s); } int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, @@ -211,10 +197,7 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) { if (r < 0) return -errno; - r = s; - s = -1; - - return r; + return TAKE_FD(s); } int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, diff --git a/src/systemd/src/libsystemd-network/dhcp-option.c b/src/systemd/src/libsystemd-network/dhcp-option.c index e003e088..86e67358 100644 --- a/src/systemd/src/libsystemd-network/dhcp-option.c +++ b/src/systemd/src/libsystemd-network/dhcp-option.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2013 Intel Corporation. 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 "nm-sd-adapt.h" @@ -26,6 +14,7 @@ #include "alloc-util.h" #include "utf8.h" +#include "strv.h" #include "dhcp-internal.h" @@ -49,6 +38,34 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, *offset += 1; break; + case SD_DHCP_OPTION_USER_CLASS: { + size_t len = 0; + char **s; + + STRV_FOREACH(s, (char **) optval) + len += strlen(*s) + 1; + + if (size < *offset + len + 2) + return -ENOBUFS; + + options[*offset] = code; + options[*offset + 1] = len; + *offset += 2; + + STRV_FOREACH(s, (char **) optval) { + len = strlen(*s); + + if (len > 255) + return -ENAMETOOLONG; + + options[*offset] = len; + + memcpy_safe(&options[*offset + 1], *s, len); + *offset += len + 1; + } + + break; + } default: if (size < *offset + optlen + 2) return -ENOBUFS; @@ -192,9 +209,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo if (!ascii_is_valid(string)) return -EINVAL; - free(*error_message); - *error_message = string; - string = NULL; + free_and_replace(*error_message, string); } break; @@ -256,10 +271,8 @@ int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t c if (message_type == 0) return -ENOMSG; - if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE)) { - *_error_message = error_message; - error_message = NULL; - } + if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE)) + *_error_message = TAKE_PTR(error_message); return message_type; } diff --git a/src/systemd/src/libsystemd-network/dhcp-packet.c b/src/systemd/src/libsystemd-network/dhcp-packet.c index 1bb1bfcf..136bab9d 100644 --- a/src/systemd/src/libsystemd-network/dhcp-packet.c +++ b/src/systemd/src/libsystemd-network/dhcp-packet.c @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2013 Intel Corporation. All rights reserved. Copyright (C) 2014 Tom Gundersen - - 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 "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/dhcp-protocol.h b/src/systemd/src/libsystemd-network/dhcp-protocol.h index 5cf7abbf..2230e094 100644 --- a/src/systemd/src/libsystemd-network/dhcp-protocol.h +++ b/src/systemd/src/libsystemd-network/dhcp-protocol.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright (C) 2013 Intel Corporation. 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 diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h index 945c3b97..33c43320 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright (C) 2014-2015 Intel Corporation. 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 @@ -28,25 +16,65 @@ #include "macro.h" #include "sparse-endian.h" +/* Common option header */ +typedef struct DHCP6Option { + be16_t code; + be16_t len; + uint8_t data[]; +} _packed_ DHCP6Option; + +/* Address option */ +struct iaaddr { + struct in6_addr address; + be32_t lifetime_preferred; + be32_t lifetime_valid; +} _packed_; + +/* Prefix Delegation Prefix option */ +struct iapdprefix { + be32_t lifetime_preferred; + be32_t lifetime_valid; + uint8_t prefixlen; + struct in6_addr address; +} _packed_; + typedef struct DHCP6Address DHCP6Address; struct DHCP6Address { LIST_FIELDS(DHCP6Address, addresses); - struct { - struct in6_addr address; - be32_t lifetime_preferred; - be32_t lifetime_valid; - } iaaddr _packed_; + union { + struct iaaddr iaaddr; + struct iapdprefix iapdprefix; + }; }; +/* Non-temporary Address option */ +struct ia_na { + be32_t id; + be32_t lifetime_t1; + be32_t lifetime_t2; +} _packed_; + +/* Prefix Delegation option */ +struct ia_pd { + be32_t id; + be32_t lifetime_t1; + be32_t lifetime_t2; +} _packed_; + +/* Temporary Address option */ +struct ia_ta { + be32_t id; +} _packed_; + struct DHCP6IA { uint16_t type; - struct { - be32_t id; - be32_t lifetime_t1; - be32_t lifetime_t2; - } _packed_; + union { + struct ia_na ia_na; + struct ia_pd ia_pd; + struct ia_ta ia_ta; + }; sd_event_source *timeout_t1; sd_event_source *timeout_t2; @@ -61,10 +89,12 @@ typedef struct DHCP6IA DHCP6IA; int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, size_t optlen, const void *optval); int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia); +int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd); +int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); -int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, - DHCP6IA *ia); +int dhcp6_option_parse_status(DHCP6Option *option); +int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia); int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, struct in6_addr **addrs, size_t count, size_t *allocated); diff --git a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h index 14e708ef..2bf54997 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -5,19 +6,6 @@ Copyright (C) 2014 Tom Gundersen Copyright (C) 2014-2015 Intel Corporation. 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 @@ -35,8 +23,10 @@ struct sd_dhcp6_lease { bool rapid_commit; DHCP6IA ia; + DHCP6IA pd; DHCP6Address *addr_iter; + DHCP6Address *prefix_iter; struct in6_addr *dns; size_t dns_count; diff --git a/src/systemd/src/libsystemd-network/dhcp6-network.c b/src/systemd/src/libsystemd-network/dhcp6-network.c index e4883019..2c6dc713 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-network.c +++ b/src/systemd/src/libsystemd-network/dhcp6-network.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2014 Intel Corporation. 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 "nm-sd-adapt.h" @@ -68,9 +56,7 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { if (r < 0) return -errno; - r = s; - s = -1; - return r; + return TAKE_FD(s); } int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c index 3a77e34d..87ae96a2 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-option.c +++ b/src/systemd/src/libsystemd-network/dhcp6-option.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2014-2015 Intel Corporation. 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 "nm-sd-adapt.h" @@ -27,6 +15,7 @@ #include "alloc-util.h" #include "dhcp6-internal.h" +#include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" #include "dns-domain.h" #include "sparse-endian.h" @@ -34,14 +23,27 @@ #include "unaligned.h" #include "util.h" -#define DHCP6_OPTION_IA_NA_LEN 12 -#define DHCP6_OPTION_IA_TA_LEN 4 +typedef struct DHCP6StatusOption { + struct DHCP6Option option; + be16_t status; + char msg[]; +} _packed_ DHCP6StatusOption; + +typedef struct DHCP6AddressOption { + struct DHCP6Option option; + struct iaaddr iaaddr; + uint8_t options[]; +} _packed_ DHCP6AddressOption; -typedef struct DHCP6Option { - be16_t code; - be16_t len; - uint8_t data[]; -} _packed_ DHCP6Option; +typedef struct DHCP6PDPrefixOption { + struct DHCP6Option option; + struct iapdprefix iapdprefix; + uint8_t options[]; +} _packed_ DHCP6PDPrefixOption; + +#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na)) +#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd)) +#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta)) static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { @@ -84,7 +86,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { uint16_t len; uint8_t *ia_hdr; - size_t ia_buflen, ia_addrlen = 0; + size_t iaid_offset, ia_buflen, ia_addrlen = 0; DHCP6Address *addr; int r; @@ -93,10 +95,12 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { switch (ia->type) { case SD_DHCP6_OPTION_IA_NA: len = DHCP6_OPTION_IA_NA_LEN; + iaid_offset = offsetof(DHCP6IA, ia_na); break; case SD_DHCP6_OPTION_IA_TA: len = DHCP6_OPTION_IA_TA_LEN; + iaid_offset = offsetof(DHCP6IA, ia_ta); break; default: @@ -112,7 +116,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { *buf += sizeof(DHCP6Option); *buflen -= sizeof(DHCP6Option); - memcpy(*buf, &ia->id, len); + memcpy(*buf, (char*) ia + iaid_offset, len); *buf += len; *buflen -= len; @@ -138,6 +142,69 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { return 0; } +int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { + uint8_t buffer[1 + DNS_WIRE_FOMAT_HOSTNAME_MAX]; + int r; + + assert_return(buf && *buf && buflen && fqdn, -EINVAL); + + buffer[0] = DHCP6_FQDN_FLAG_S; /* Request server to perform AAAA RR DNS updates */ + + /* Store domain name after flags field */ + r = dns_name_to_wire_format(fqdn, buffer + 1, sizeof(buffer) - 1, false); + if (r <= 0) + return r; + + /* + * According to RFC 4704, chapter 4.2 only add terminating zero-length + * label in case a FQDN is provided. Since dns_name_to_wire_format + * always adds terminating zero-length label remove if only a hostname + * is provided. + */ + if (dns_name_is_single_label(fqdn)) + r--; + + r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_FQDN, 1 + r, buffer); + + return r; +} + +int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd) { + DHCP6Option *option = (DHCP6Option *)buf; + size_t i = sizeof(*option) + sizeof(pd->ia_pd); + DHCP6Address *prefix; + + assert_return(buf, -EINVAL); + assert_return(pd, -EINVAL); + assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL); + + if (len < i) + return -ENOBUFS; + + option->code = htobe16(SD_DHCP6_OPTION_IA_PD); + + memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd)); + + LIST_FOREACH(addresses, prefix, pd->addresses) { + DHCP6PDPrefixOption *prefix_opt; + + if (len < i + sizeof(*prefix_opt)) + return -ENOBUFS; + + prefix_opt = (DHCP6PDPrefixOption *)&buf[i]; + prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX); + prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix)); + + memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix, + sizeof(struct iapdprefix)); + + i += sizeof(*prefix_opt); + } + + option->len = htobe16(i - sizeof(*option)); + + return i; +} static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) { DHCP6Option *option = (DHCP6Option*) *buf; @@ -184,35 +251,147 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, return 0; } -int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, - DHCP6IA *ia) { +int dhcp6_option_parse_status(DHCP6Option *option) { + DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option; + + if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*statusopt)) + return -ENOBUFS; + + return be16toh(statusopt->status); +} + +static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, + uint32_t *lifetime_valid) { + DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option; + DHCP6Address *addr; + uint32_t lt_valid, lt_pref; int r; - uint16_t opt, status; - size_t optlen; + + if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*addr_option)) + return -ENOBUFS; + + lt_valid = be32toh(addr_option->iaaddr.lifetime_valid); + lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred); + + if (lt_valid == 0 || lt_pref > lt_valid) { + log_dhcp6_client(client, "Valid lifetime of an IA address is zero or preferred lifetime %d > valid lifetime %d", + lt_pref, lt_valid); + + return 0; + } + + if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*addr_option)) { + r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options); + if (r != 0) + return r < 0 ? r: 0; + } + + addr = new0(DHCP6Address, 1); + if (!addr) + return -ENOMEM; + + LIST_INIT(addresses, addr); + memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr)); + + LIST_PREPEND(addresses, ia->addresses, addr); + + *lifetime_valid = be32toh(addr->iaaddr.lifetime_valid); + + return 0; +} + +static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, + uint32_t *lifetime_valid) { + DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option; + DHCP6Address *prefix; + uint32_t lt_valid, lt_pref; + int r; + + if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*pdprefix_option)) + return -ENOBUFS; + + lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid); + lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred); + + if (lt_valid == 0 || lt_pref > lt_valid) { + log_dhcp6_client(client, "Valid lifetieme of a PD prefix is zero or preferred lifetime %d > valid lifetime %d", + lt_pref, lt_valid); + + return 0; + } + + if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*pdprefix_option)) { + r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options); + if (r != 0) + return r < 0 ? r: 0; + } + + prefix = new0(DHCP6Address, 1); + if (!prefix) + return -ENOMEM; + + LIST_INIT(addresses, prefix); + memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix)); + + LIST_PREPEND(addresses, ia->addresses, prefix); + + *lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid); + + return 0; +} + +int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { + uint16_t iatype, optlen; + size_t i, len; + int r = 0, status; + uint16_t opt; size_t iaaddr_offset; - DHCP6Address *addr; - uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0; + uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX; assert_return(ia, -EINVAL); assert_return(!ia->addresses, -EINVAL); + iatype = be16toh(iaoption->code); + len = be16toh(iaoption->len); + switch (iatype) { case SD_DHCP6_OPTION_IA_NA: - if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) + - sizeof(addr->iaaddr)) { + if (len < DHCP6_OPTION_IA_NA_LEN) { r = -ENOBUFS; goto error; } iaaddr_offset = DHCP6_OPTION_IA_NA_LEN; - memcpy(&ia->id, *buf, iaaddr_offset); + memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na)); - lt_t1 = be32toh(ia->lifetime_t1); - lt_t2 = be32toh(ia->lifetime_t2); + lt_t1 = be32toh(ia->ia_na.lifetime_t1); + lt_t2 = be32toh(ia->ia_na.lifetime_t2); if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { - log_dhcp6_client(client, "IA T1 %ds > T2 %ds", + log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds", + lt_t1, lt_t2); + r = -EINVAL; + goto error; + } + + break; + + case SD_DHCP6_OPTION_IA_PD: + + if (len < sizeof(ia->ia_pd)) { + r = -ENOBUFS; + goto error; + } + + iaaddr_offset = sizeof(ia->ia_pd); + memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd)); + + lt_t1 = be32toh(ia->ia_pd.lifetime_t1); + lt_t2 = be32toh(ia->ia_pd.lifetime_t2); + + if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { + log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds", lt_t1, lt_t2); r = -EINVAL; goto error; @@ -221,17 +400,13 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, break; case SD_DHCP6_OPTION_IA_TA: - if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) + - sizeof(addr->iaaddr)) { + if (len < DHCP6_OPTION_IA_TA_LEN) { r = -ENOBUFS; goto error; } iaaddr_offset = DHCP6_OPTION_IA_TA_LEN; - memcpy(&ia->id, *buf, iaaddr_offset); - - ia->lifetime_t1 = 0; - ia->lifetime_t2 = 0; + memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta)); break; @@ -241,48 +416,63 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, } ia->type = iatype; + i = iaaddr_offset; - *buflen -= iaaddr_offset; - *buf += iaaddr_offset; + while (i < len) { + DHCP6Option *option = (DHCP6Option *)&iaoption->data[i]; - while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) { + if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) { + r = -ENOBUFS; + goto error; + } + + opt = be16toh(option->code); + optlen = be16toh(option->len); switch (opt) { case SD_DHCP6_OPTION_IAADDR: - addr = new0(DHCP6Address, 1); - if (!addr) { - r = -ENOMEM; + if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) { + log_dhcp6_client(client, "IA Address option not in IA NA or TA option"); + r = -EINVAL; goto error; } - LIST_INIT(addresses, addr); + r = dhcp6_option_parse_address(option, ia, <_valid); + if (r < 0) + goto error; + + if (lt_valid < lt_min) + lt_min = lt_valid; - memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr)); + break; - lt_valid = be32toh(addr->iaaddr.lifetime_valid); - lt_pref = be32toh(addr->iaaddr.lifetime_valid); + case SD_DHCP6_OPTION_IA_PD_PREFIX: - if (!lt_valid || lt_pref > lt_valid) { - log_dhcp6_client(client, "IA preferred %ds > valid %ds", - lt_pref, lt_valid); - free(addr); - } else { - LIST_PREPEND(addresses, ia->addresses, addr); - if (lt_valid < lt_min) - lt_min = lt_valid; + if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) { + log_dhcp6_client(client, "IA PD Prefix option not in IA PD option"); + r = -EINVAL; + goto error; } + r = dhcp6_option_parse_pdprefix(option, ia, <_valid); + if (r < 0) + goto error; + + if (lt_valid < lt_min) + lt_min = lt_valid; + break; case SD_DHCP6_OPTION_STATUS_CODE: - if (optlen < sizeof(status)) - break; - status = (*buf)[0] << 8 | (*buf)[1]; + status = dhcp6_option_parse_status(option); if (status) { log_dhcp6_client(client, "IA status %d", status); + + dhcp6_lease_free_ia(ia); + r = -EINVAL; goto error; } @@ -294,30 +484,41 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, break; } - *buflen -= optlen; - *buf += optlen; + i += sizeof(*option) + optlen; } - if (r == -ENOMSG) - r = 0; + switch(iatype) { + case SD_DHCP6_OPTION_IA_NA: + if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) { + lt_t1 = lt_min / 2; + lt_t2 = lt_min / 10 * 8; + ia->ia_na.lifetime_t1 = htobe32(lt_t1); + ia->ia_na.lifetime_t2 = htobe32(lt_t2); + + log_dhcp6_client(client, "Computed IA NA T1 %ds and T2 %ds as both were zero", + lt_t1, lt_t2); + } - if (!ia->lifetime_t1 && !ia->lifetime_t2) { - lt_t1 = lt_min / 2; - lt_t2 = lt_min / 10 * 8; - ia->lifetime_t1 = htobe32(lt_t1); - ia->lifetime_t2 = htobe32(lt_t2); + break; - log_dhcp6_client(client, "Computed IA T1 %ds and T2 %ds as both were zero", - lt_t1, lt_t2); - } + case SD_DHCP6_OPTION_IA_PD: + if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2) { + lt_t1 = lt_min / 2; + lt_t2 = lt_min / 10 * 8; + ia->ia_pd.lifetime_t1 = htobe32(lt_t1); + ia->ia_pd.lifetime_t2 = htobe32(lt_t2); - if (*buflen) - r = -ENOMSG; + log_dhcp6_client(client, "Computed IA PD T1 %ds and T2 %ds as both were zero", + lt_t1, lt_t2); + } -error: - *buf += *buflen; - *buflen = 0; + break; + default: + break; + } + +error: return r; } @@ -405,8 +606,7 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * idx++; } - *str_arr = names; - names = NULL; + *str_arr = TAKE_PTR(names); return idx; diff --git a/src/systemd/src/libsystemd-network/dhcp6-protocol.h b/src/systemd/src/libsystemd-network/dhcp6-protocol.h index 2487c470..18417062 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-protocol.h +++ b/src/systemd/src/libsystemd-network/dhcp6-protocol.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright (C) 2014 Intel Corporation. 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 @@ -33,6 +21,7 @@ struct DHCP6Message { } _packed_; be32_t transaction_id; }; + uint8_t options[]; } _packed_; typedef struct DHCP6Message DHCP6Message; @@ -104,3 +93,9 @@ enum { DHCP6_STATUS_USE_MULTICAST = 5, _DHCP6_STATUS_MAX = 6, }; + +enum { + DHCP6_FQDN_FLAG_S = (1 << 0), + DHCP6_FQDN_FLAG_O = (1 << 1), + DHCP6_FQDN_FLAG_N = (1 << 2), +}; diff --git a/src/systemd/src/libsystemd-network/lldp-internal.h b/src/systemd/src/libsystemd-network/lldp-internal.h index becc162f..06cf7e8d 100644 --- a/src/systemd/src/libsystemd-network/lldp-internal.h +++ b/src/systemd/src/libsystemd-network/lldp-internal.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -5,19 +6,6 @@ Copyright (C) 2014 Tom Gundersen Copyright (C) 2014 Susant Sahani - - 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 "sd-event.h" diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c index c560a864..373e34c8 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.c +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2016 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -342,7 +330,6 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { break; } - p += length, left -= length; } @@ -675,8 +662,7 @@ _public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, if (r < 0) return r; - *ret = n; - n = NULL; + *ret = TAKE_PTR(n); return r; } diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.h b/src/systemd/src/libsystemd-network/lldp-neighbor.h index c1a7606d..fae49c59 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.h +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2016 Lennart Poettering - - 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 diff --git a/src/systemd/src/libsystemd-network/lldp-network.c b/src/systemd/src/libsystemd-network/lldp-network.c index 4466a050..307cf6c3 100644 --- a/src/systemd/src/libsystemd-network/lldp-network.c +++ b/src/systemd/src/libsystemd-network/lldp-network.c @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2014 Tom Gundersen Copyright (C) 2014 Susant Sahani - - 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 "nm-sd-adapt.h" @@ -93,8 +81,5 @@ int lldp_network_bind_raw_socket(int ifindex) { if (r < 0) return -errno; - r = fd; - fd = -1; - - return r; + return TAKE_FD(fd); } diff --git a/src/systemd/src/libsystemd-network/lldp-network.h b/src/systemd/src/libsystemd-network/lldp-network.h index c4cf8c79..7a1e8b10 100644 --- a/src/systemd/src/libsystemd-network/lldp-network.h +++ b/src/systemd/src/libsystemd-network/lldp-network.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -5,19 +6,6 @@ Copyright (C) 2014 Tom Gundersen Copyright (C) 2014 Susant Sahani - - 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 "sd-event.h" diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c index de37b9f0..e05f52d4 100644 --- a/src/systemd/src/libsystemd-network/network-internal.c +++ b/src/systemd/src/libsystemd-network/network-internal.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2013 Tom Gundersen - - 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 "nm-sd-adapt.h" @@ -23,6 +11,7 @@ #include #include +#include "sd-id128.h" #include "sd-ndisc.h" #include "alloc-util.h" @@ -97,7 +86,7 @@ static bool net_condition_test_strv(char * const *raw_patterns, /* If the patterns begin with "!", edit it out and negate the test. */ if (raw_patterns[0][0] == '!') { char **patterns; - unsigned i, length; + size_t i, length; length = strv_length(raw_patterns) + 1; /* Include the NULL. */ patterns = newa(char*, length); @@ -111,14 +100,15 @@ static bool net_condition_test_strv(char * const *raw_patterns, return string && strv_fnmatch(raw_patterns, string, 0); } -bool net_match_config(const struct ether_addr *match_mac, +bool net_match_config(Set *match_mac, char * const *match_paths, char * const *match_drivers, char * const *match_types, char * const *match_names, Condition *match_host, Condition *match_virt, - Condition *match_kernel, + Condition *match_kernel_cmdline, + Condition *match_kernel_version, Condition *match_arch, const struct ether_addr *dev_mac, const char *dev_path, @@ -133,13 +123,16 @@ bool net_match_config(const struct ether_addr *match_mac, if (match_virt && condition_test(match_virt) <= 0) return false; - if (match_kernel && condition_test(match_kernel) <= 0) + if (match_kernel_cmdline && condition_test(match_kernel_cmdline) <= 0) + return false; + + if (match_kernel_version && condition_test(match_kernel_version) <= 0) return false; if (match_arch && condition_test(match_arch) <= 0) return false; - if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN))) + if (match_mac && dev_mac && !set_contains(match_mac, dev_mac)) return false; if (!net_condition_test_strv(match_paths, dev_path)) @@ -273,10 +266,9 @@ int config_parse_ifalias(const char *unit, } free(*s); - if (*n) { - *s = n; - n = NULL; - } else + if (*n) + *s = TAKE_PTR(n); + else *s = NULL; return 0; @@ -292,10 +284,9 @@ int config_parse_hwaddr(const char *unit, const char *rvalue, void *data, void *userdata) { + + _cleanup_free_ struct ether_addr *n = NULL; struct ether_addr **hwaddr = data; - struct ether_addr *n; - const char *start; - size_t offset; int r; assert(filename); @@ -307,17 +298,86 @@ int config_parse_hwaddr(const char *unit, if (!n) return log_oom(); - start = rvalue + strspn(rvalue, WHITESPACE); - r = ether_addr_from_string(start, n, &offset); + r = ether_addr_from_string(rvalue, n); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue); + return 0; + } - if (r || (start[offset + strspn(start + offset, WHITESPACE)] != '\0')) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue); - free(n); + *hwaddr = TAKE_PTR(n); + + return 0; +} + +int config_parse_hwaddrs(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_set_free_free_ Set *s = NULL; + const char *p = rvalue; + Set **hwaddrs = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + *hwaddrs = set_free_free(*hwaddrs); return 0; } - free(*hwaddr); - *hwaddr = n; + s = set_new(ðer_addr_hash_ops); + if (!s) + return log_oom(); + + for (;;) { + _cleanup_free_ char *word = NULL; + _cleanup_free_ struct ether_addr *n = NULL; + + r = extract_first_word(&p, &word, NULL, 0); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); + return 0; + } + + n = new(struct ether_addr, 1); + if (!n) + return log_oom(); + + r = ether_addr_from_string(word, n); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring: %s", word); + continue; + } + + r = set_put(s, n); + if (r < 0) + return log_oom(); + if (r > 0) + n = NULL; /* avoid cleanup */ + } + + r = set_ensure_allocated(hwaddrs, ðer_addr_hash_ops); + if (r < 0) + return log_oom(); + + r = set_move(*hwaddrs, s); + if (r < 0) + return log_oom(); return 0; } @@ -391,7 +451,6 @@ int config_parse_bridge_port_priority( } #endif /* NM_IGNORED */ - void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) { unsigned i; @@ -422,7 +481,7 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) { if (r == 0) break; - new_addresses = realloc(addresses, (size + 1) * sizeof(struct in_addr)); + new_addresses = reallocarray(addresses, size + 1, sizeof(struct in_addr)); if (!new_addresses) return -ENOMEM; else @@ -435,8 +494,7 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) { size++; } - *ret = addresses; - addresses = NULL; + *ret = TAKE_PTR(addresses); return size; } @@ -476,7 +534,7 @@ int deserialize_in6_addrs(struct in6_addr **ret, const char *string) { if (r == 0) break; - new_addresses = realloc(addresses, (size + 1) * sizeof(struct in6_addr)); + new_addresses = reallocarray(addresses, size + 1, sizeof(struct in6_addr)); if (!new_addresses) return -ENOMEM; else @@ -489,8 +547,7 @@ int deserialize_in6_addrs(struct in6_addr **ret, const char *string) { size++; } - *ret = addresses; - addresses = NULL; + *ret = TAKE_PTR(addresses); return size; } @@ -583,8 +640,7 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_size = size; *ret_allocated = allocated; - *ret = routes; - routes = NULL; + *ret = TAKE_PTR(routes); return 0; } @@ -604,14 +660,3 @@ int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t siz return 0; } - -int deserialize_dhcp_option(void **data, size_t *data_len, const char *string) { - assert(data); - assert(data_len); - assert(string); - - if (strlen(string) % 2) - return -EINVAL; - - return unhexmem(string, strlen(string), (void **)data, data_len); -} diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h index 4666f174..d0076f45 100644 --- a/src/systemd/src/libsystemd-network/network-internal.h +++ b/src/systemd/src/libsystemd-network/network-internal.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright (C) 2013 Tom Gundersen - - 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 @@ -24,19 +12,21 @@ #include "sd-dhcp-lease.h" #include "condition.h" +#include "set.h" #include "udev.h" #define LINK_BRIDGE_PORT_PRIORITY_INVALID 128 #define LINK_BRIDGE_PORT_PRIORITY_MAX 63 -bool net_match_config(const struct ether_addr *match_mac, +bool net_match_config(Set *match_mac, char * const *match_path, char * const *match_driver, char * const *match_type, char * const *match_name, Condition *match_host, Condition *match_virt, - Condition *match_kernel, + Condition *match_kernel_cmdline, + Condition *match_kernel_version, Condition *match_arch, const struct ether_addr *dev_mac, const char *dev_path, @@ -53,6 +43,10 @@ int config_parse_hwaddr(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_hwaddrs(const char *unit, const char *filename, unsigned line, + const char *section, unsigned section_line, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata); + int config_parse_ifnames(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); @@ -84,5 +78,5 @@ struct sd_dhcp_route; void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size); int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string); +/* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */ int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size); -int deserialize_dhcp_option(void **data, size_t *data_len, const char *string); diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c index 5eab0050..ef2620f4 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2013 Intel Corporation. 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 "nm-sd-adapt.h" @@ -41,6 +29,7 @@ #include "random-util.h" #include "string-util.h" #include "util.h" +#include "strv.h" #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */ #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN) @@ -97,6 +86,7 @@ struct sd_dhcp_client { size_t client_id_len; char *hostname; char *vendor_class_identifier; + char **user_class; uint32_t mtu; uint32_t xid; usec_t start_time; @@ -357,9 +347,10 @@ int sd_dhcp_client_set_client_id( * without further modification. Otherwise, if duid_type is supported, DUID * is set based on that type. Otherwise, an error is returned. */ -int sd_dhcp_client_set_iaid_duid( +static int dhcp_client_set_iaid_duid( sd_dhcp_client *client, uint32_t iaid, + bool append_iaid, uint16_t duid_type, const void *duid, size_t duid_len) { @@ -380,15 +371,17 @@ int sd_dhcp_client_set_iaid_duid( zero(client->client_id); client->client_id.type = 255; - /* If IAID is not configured, generate it. */ - if (iaid == 0) { - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, - client->mac_addr_len, - &client->client_id.ns.iaid); - if (r < 0) - return r; - } else - client->client_id.ns.iaid = htobe32(iaid); + if (append_iaid) { + /* If IAID is not configured, generate it. */ + if (iaid == 0) { + r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, + client->mac_addr_len, + &client->client_id.ns.iaid); + if (r < 0) + return r; + } else + client->client_id.ns.iaid = htobe32(iaid); + } if (duid != NULL) { client->client_id.ns.duid.type = htobe16(duid_type); @@ -402,7 +395,7 @@ int sd_dhcp_client_set_iaid_duid( return -EOPNOTSUPP; client->client_id_len = sizeof(client->client_id.type) + len + - sizeof(client->client_id.ns.iaid); + (append_iaid ? sizeof(client->client_id.ns.iaid) : 0); if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { log_dhcp_client(client, "Configured IAID+DUID, restarting."); @@ -412,6 +405,23 @@ int sd_dhcp_client_set_iaid_duid( return 0; } + +int sd_dhcp_client_set_iaid_duid( + sd_dhcp_client *client, + uint32_t iaid, + uint16_t duid_type, + const void *duid, + size_t duid_len) { + return dhcp_client_set_iaid_duid(client, iaid, true, duid_type, duid, duid_len); +} + +int sd_dhcp_client_set_duid( + sd_dhcp_client *client, + uint16_t duid_type, + const void *duid, + size_t duid_len) { + return dhcp_client_set_iaid_duid(client, 0, false, duid_type, duid, duid_len); +} #endif /* NM_IGNORED */ int sd_dhcp_client_set_hostname( @@ -420,9 +430,9 @@ int sd_dhcp_client_set_hostname( assert_return(client, -EINVAL); - /* Refuse hostnames that neither qualify as DNS nor as Linux hosntames */ + /* Make sure hostnames qualify as DNS and as Linux hostnames */ if (hostname && - !(hostname_is_valid(hostname, false) || dns_name_is_valid(hostname) > 0)) + !(hostname_is_valid(hostname, false) && dns_name_is_valid(hostname) > 0)) return -EINVAL; return free_and_strdup(&client->hostname, hostname); @@ -437,6 +447,26 @@ int sd_dhcp_client_set_vendor_class_identifier( return free_and_strdup(&client->vendor_class_identifier, vci); } +int sd_dhcp_client_set_user_class( + sd_dhcp_client *client, + const char* const* user_class) { + + _cleanup_strv_free_ char **s = NULL; + char **p; + + STRV_FOREACH(p, (char **) user_class) + if (strlen(*p) > 255) + return -ENAMETOOLONG; + + s = strv_copy((char **) user_class); + if (!s) + return -ENOMEM; + + client->user_class = TAKE_PTR(s); + + return 0; +} + int sd_dhcp_client_set_client_port( sd_dhcp_client *client, uint16_t port) { @@ -666,8 +696,7 @@ static int client_message_init( *_optlen = optlen; *_optoffset = optoffset; - *ret = packet; - packet = NULL; + *ret = TAKE_PTR(packet); return 0; } @@ -761,6 +790,15 @@ static int client_send_discover(sd_dhcp_client *client) { return r; } + if (client->user_class) { + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_USER_CLASS, + strv_length(client->user_class), + client->user_class); + if (r < 0) + return r; + } + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL); if (r < 0) @@ -833,7 +871,6 @@ static int client_send_request(sd_dhcp_client *client) { client’s IP address. */ - /* fall through */ case DHCP_STATE_REBINDING: /* ’server identifier’ MUST NOT be filled in, ’requested IP address’ option MUST NOT be filled in, ’ciaddr’ MUST be filled in with @@ -1265,16 +1302,15 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_ if (!lease->have_subnet_mask) { r = dhcp_lease_set_default_subnet_mask(lease); if (r < 0) { - log_dhcp_client(client, "received lease lacks subnet " - "mask, and a fallback one can not be " - "generated, ignoring"); + log_dhcp_client(client, + "received lease lacks subnet mask, " + "and a fallback one cannot be generated, ignoring"); return -ENOMSG; } } sd_dhcp_lease_unref(client->lease); - client->lease = lease; - lease = NULL; + client->lease = TAKE_PTR(lease); log_dhcp_client(client, "OFFER"); @@ -1336,9 +1372,9 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t le if (lease->subnet_mask == INADDR_ANY) { r = dhcp_lease_set_default_subnet_mask(lease); if (r < 0) { - log_dhcp_client(client, "received lease lacks subnet " - "mask, and a fallback one can not be " - "generated, ignoring"); + log_dhcp_client(client, + "received lease lacks subnet mask, " + "and a fallback one cannot be generated, ignoring"); return -ENOMSG; } } @@ -1355,8 +1391,7 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t le client->lease = sd_dhcp_lease_unref(client->lease); } - client->lease = lease; - lease = NULL; + client->lease = TAKE_PTR(lease); log_dhcp_client(client, "ACK"); @@ -1919,6 +1954,7 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) { free(client->req_opts); free(client->hostname); free(client->vendor_class_identifier); + client->user_class = strv_free(client->user_class); return mfree(client); } @@ -1951,8 +1987,7 @@ int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) { if (!client->req_opts) return -ENOMEM; - *ret = client; - client = NULL; + *ret = TAKE_PTR(client); return 0; } diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index c00190b5..d92c5126 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2013 Intel Corporation. All rights reserved. Copyright (C) 2014 Tom Gundersen - - 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 "nm-sd-adapt.h" @@ -23,6 +11,7 @@ #include #include #include +#include #include #include @@ -234,7 +223,7 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) { } int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) { - unsigned r; + size_t r; assert_return(lease, -EINVAL); assert_return(domains, -EINVAL); @@ -394,9 +383,7 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) { if (dns_name_is_root(normalized)) return -EINVAL; - free(*ret); - *ret = normalized; - normalized = NULL; + free_and_replace(*ret, normalized); return 0; } @@ -473,6 +460,7 @@ static int lease_parse_routes( struct sd_dhcp_route *route = *routes + *routes_size; int r; + route->option = SD_DHCP_OPTION_STATIC_ROUTE; r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen); if (r < 0) { log_debug("Failed to determine destination prefix length from class based IP, ignoring"); @@ -516,6 +504,7 @@ static int lease_parse_classless_routes( return -ENOMEM; route = *routes + *routes_size; + route->option = SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE; dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1); route->dst_prefixlen = *option; @@ -680,14 +669,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void return 0; } - if (!timezone_is_valid(tz)) { + if (!timezone_is_valid(tz, LOG_DEBUG)) { log_debug_errno(r, "Timezone is not valid, ignoring: %m"); return 0; } - free(lease->timezone); - lease->timezone = tz; - tz = NULL; + free_and_replace(lease->timezone, tz); break; } @@ -809,8 +796,7 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d pos = next_chunk; } - *domains = names; - names = NULL; + *domains = TAKE_PTR(names); return cnt; } @@ -881,7 +867,8 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { if (r < 0) goto fail; - fchmod(fileno(f), 0644); + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + (void) fchmod(fileno(f), 0644); fprintf(f, "# This is private data. Do not parse.\n"); @@ -928,16 +915,16 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { r = sd_dhcp_lease_get_dns(lease, &addresses); if (r > 0) { - fputs_unlocked("DNS=", f); + fputs("DNS=", f); serialize_in_addrs(f, addresses, r); - fputs_unlocked("\n", f); + fputs("\n", f); } r = sd_dhcp_lease_get_ntp(lease, &addresses); if (r > 0) { - fputs_unlocked("NTP=", f); + fputs("NTP=", f); serialize_in_addrs(f, addresses, r); - fputs_unlocked("\n", f); + fputs("\n", f); } r = sd_dhcp_lease_get_domainname(lease, &string); @@ -946,9 +933,9 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { r = sd_dhcp_lease_get_search_domains(lease, &search_domains); if (r > 0) { - fputs_unlocked("DOMAIN_SEARCH_LIST=", f); + fputs("DOMAIN_SEARCH_LIST=", f); fputstrv(f, search_domains, NULL, NULL); - fputs_unlocked("\n", f); + fputs("\n", f); } r = sd_dhcp_lease_get_hostname(lease, &string); @@ -992,7 +979,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { } LIST_FOREACH(options, option, lease->private_options) { - char key[strlen("OPTION_000")+1]; + char key[STRLEN("OPTION_000")+1]; xsprintf(key, "OPTION_%" PRIu8, option->tag); r = serialize_dhcp_option(f, key, option->data, option->length); @@ -1208,13 +1195,13 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { } if (client_id_hex) { - r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex); + r = unhexmem(client_id_hex, (size_t) -1, &lease->client_id, &lease->client_id_len); if (r < 0) log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex); } if (vendor_specific_hex) { - r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex); + r = unhexmem(vendor_specific_hex, (size_t) -1, &lease->vendor_specific, &lease->vendor_specific_len); if (r < 0) log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex); } @@ -1226,7 +1213,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { if (!options[i]) continue; - r = deserialize_dhcp_option(&data, &len, options[i]); + r = unhexmem(options[i], (size_t) -1, &data, &len); if (r < 0) { log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]); continue; @@ -1237,8 +1224,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { return r; } - *ret = lease; - lease = NULL; + *ret = TAKE_PTR(lease); return 0; } diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c index f512b65b..1cec6022 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2014-2015 Intel Corporation. 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 "nm-sd-adapt.h" @@ -31,7 +19,9 @@ #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" +#include "dns-domain.h" #include "fd-util.h" +#include "hostname-util.h" #include "in-addr-util.h" #include "network-internal.h" #include "random-util.h" @@ -53,6 +43,8 @@ struct sd_dhcp6_client { size_t mac_addr_len; uint16_t arp_type; DHCP6IA ia_na; + DHCP6IA ia_pd; + bool prefix_delegation; be32_t transaction_id; usec_t transaction_start; struct sd_dhcp6_lease *lease; @@ -61,6 +53,7 @@ struct sd_dhcp6_client { be16_t *req_opts; size_t req_opts_allocated; size_t req_opts_len; + char *fqdn; sd_event_source *receive_message; usec_t retransmit_time; uint8_t retransmit_count; @@ -229,12 +222,27 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { assert_return(client, -EINVAL); assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); - client->ia_na.id = htobe32(iaid); + client->ia_na.ia_na.id = htobe32(iaid); + client->ia_pd.ia_pd.id = htobe32(iaid); return 0; } #endif /* NM_IGNORED */ +int sd_dhcp6_client_set_fqdn( + sd_dhcp6_client *client, + const char *fqdn) { + + assert_return(client, -EINVAL); + + /* Make sure FQDN qualifies as DNS and as Linux hostname */ + if (fqdn && + !(hostname_is_valid(fqdn, false) && dns_name_is_valid(fqdn) > 0)) + return -EINVAL; + + return free_and_strdup(&client->fqdn, fqdn); +} + int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) { assert_return(client, -EINVAL); assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); @@ -265,6 +273,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) case SD_DHCP6_OPTION_DOMAIN_LIST: case SD_DHCP6_OPTION_SNTP_SERVERS: case SD_DHCP6_OPTION_NTP_SERVER: + case SD_DHCP6_OPTION_RAPID_COMMIT: break; default: @@ -284,6 +293,14 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) return 0; } +int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, bool delegation) { + assert_return(client, -EINVAL); + + client->prefix_delegation = delegation; + + return 0; +} + int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { assert_return(client, -EINVAL); @@ -322,8 +339,6 @@ static int client_reset(sd_dhcp6_client *client) { client->receive_message = sd_event_source_unref(client->receive_message); - client->fd = safe_close(client->fd); - client->transaction_id = 0; client->transaction_start = 0; @@ -393,6 +408,21 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; + if (client->fqdn) { + r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); + if (r < 0) + return r; + } + + if (client->prefix_delegation) { + r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd); + if (r < 0) + return r; + + opt += r; + optlen -= r; + } + break; case DHCP6_STATE_REQUEST: @@ -413,6 +443,21 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; + if (client->fqdn) { + r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); + if (r < 0) + return r; + } + + if (client->prefix_delegation) { + r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd); + if (r < 0) + return r; + + opt += r; + optlen -= r; + } + break; case DHCP6_STATE_REBIND: @@ -422,6 +467,21 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; + if (client->fqdn) { + r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); + if (r < 0) + return r; + } + + if (client->prefix_delegation) { + r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd); + if (r < 0) + return r; + + opt += r; + optlen -= r; + } + break; case DHCP6_STATE_STOPPED: @@ -435,7 +495,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; - assert (client->duid_len); + assert(client->duid_len); r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID, client->duid_len, &client->duid); if (r < 0) @@ -677,16 +737,20 @@ error: static int client_ensure_iaid(sd_dhcp6_client *client) { int r; + be32_t iaid; assert(client); - if (client->ia_na.id) + if (client->ia_na.ia_na.id) return 0; - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id); + r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &iaid); if (r < 0) return r; + client->ia_na.ia_na.id = iaid; + client->ia_pd.ia_pd.id = iaid; + return 0; } @@ -695,23 +759,33 @@ static int client_parse_message( DHCP6Message *message, size_t len, sd_dhcp6_lease *lease) { + size_t pos = 0; int r; - uint8_t *optval, *option, *id = NULL; - uint16_t optcode, status; - size_t optlen, id_len; bool clientid = false; - be32_t iaid_lease; + uint32_t lt_t1 = ~0, lt_t2 = ~0; assert(client); assert(message); assert(len >= sizeof(DHCP6Message)); assert(lease); - option = (uint8_t *)message + sizeof(DHCP6Message); len -= sizeof(DHCP6Message); - while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen, - &optval)) >= 0) { + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&message->options[pos]; + uint16_t optcode, optlen; + int status; + uint8_t *optval; + be32_t iaid_lease; + + if (len < offsetof(DHCP6Option, data) || + len < offsetof(DHCP6Option, data) + be16toh(option->len)) + return -ENOBUFS; + + optcode = be16toh(option->code); + optlen = be16toh(option->len); + optval = option->data; + switch (optcode) { case SD_DHCP6_OPTION_CLIENTID: if (clientid) { @@ -732,8 +806,8 @@ static int client_parse_message( break; case SD_DHCP6_OPTION_SERVERID: - r = dhcp6_lease_get_serverid(lease, &id, &id_len); - if (r >= 0 && id) { + r = dhcp6_lease_get_serverid(lease, NULL, NULL); + if (r >= 0) { log_dhcp6_client(client, "%s contains multiple serverids", dhcp6_message_type_to_string(message->type)); return -EINVAL; @@ -749,21 +823,21 @@ static int client_parse_message( if (optlen != 1) return -EINVAL; - r = dhcp6_lease_set_preference(lease, *optval); + r = dhcp6_lease_set_preference(lease, optval[0]); if (r < 0) return r; break; case SD_DHCP6_OPTION_STATUS_CODE: - if (optlen < 2) - return -EINVAL; - - status = optval[0] << 8 | optval[1]; + status = dhcp6_option_parse_status(option); if (status) { log_dhcp6_client(client, "%s Status %s", dhcp6_message_type_to_string(message->type), dhcp6_message_status_to_string(status)); + dhcp6_lease_free_ia(&lease->ia); + dhcp6_lease_free_ia(&lease->pd); + return -EINVAL; } @@ -776,8 +850,7 @@ static int client_parse_message( break; } - r = dhcp6_option_parse_ia(&optval, &optlen, optcode, - &lease->ia); + r = dhcp6_option_parse_ia(option, &lease->ia); if (r < 0 && r != -ENOMSG) return r; @@ -785,12 +858,45 @@ static int client_parse_message( if (r < 0) return r; - if (client->ia_na.id != iaid_lease) { - log_dhcp6_client(client, "%s has wrong IAID", + if (client->ia_na.ia_na.id != iaid_lease) { + log_dhcp6_client(client, "%s has wrong IAID for IA NA", dhcp6_message_type_to_string(message->type)); return -EINVAL; } + if (lease->ia.addresses) { + lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1)); + lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1)); + } + + break; + + case SD_DHCP6_OPTION_IA_PD: + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + log_dhcp6_client(client, "Information request ignoring IA PD option"); + + break; + } + + r = dhcp6_option_parse_ia(option, &lease->pd); + if (r < 0 && r != -ENOMSG) + return r; + + r = dhcp6_lease_get_iaid(lease, &iaid_lease); + if (r < 0) + return r; + + if (client->ia_pd.ia_pd.id != iaid_lease) { + log_dhcp6_client(client, "%s has wrong IAID for IA PD", + dhcp6_message_type_to_string(message->type)); + return -EINVAL; + } + + if (lease->pd.addresses) { + lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1)); + lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2)); + } + break; case SD_DHCP6_OPTION_RAPID_COMMIT: @@ -829,25 +935,36 @@ static int client_parse_message( break; } + pos += sizeof(*option) + optlen; } - if (r == -ENOMSG) - r = 0; - - if (r < 0 || !clientid) { + if (!clientid) { log_dhcp6_client(client, "%s has incomplete options", dhcp6_message_type_to_string(message->type)); return -EINVAL; } if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { - r = dhcp6_lease_get_serverid(lease, &id, &id_len); - if (r < 0) + r = dhcp6_lease_get_serverid(lease, NULL, NULL); + if (r < 0) { log_dhcp6_client(client, "%s has no server id", dhcp6_message_type_to_string(message->type)); + return -EINVAL; + } + + } else { + if (lease->ia.addresses) { + lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1); + lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2); + } + + if (lease->pd.addresses) { + lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1); + lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2); + } } - return r; + return 0; } static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) { @@ -1003,7 +1120,7 @@ static int client_receive_message( break; } - /* fall through */ /* for Soliciation Rapid Commit option check */ + _fallthrough_; /* for Soliciation Rapid Commit option check */ case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: case DHCP6_STATE_REBIND: @@ -1060,6 +1177,24 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { if (r < 0) return r; + if (!client->receive_message) { + r = sd_event_add_io(client->event, &client->receive_message, + client->fd, EPOLLIN, client_receive_message, + client); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(client->receive_message, + client->event_priority); + if (r < 0) + goto error; + + r = sd_event_source_set_description(client->receive_message, + "dhcp6-receive-message"); + if (r < 0) + goto error; + } + switch (state) { case DHCP6_STATE_STOPPED: if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { @@ -1068,7 +1203,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { return 0; } - /* fall through */ + _fallthrough_; case DHCP6_STATE_SOLICITATION: client->state = DHCP6_STATE_SOLICITATION; @@ -1085,17 +1220,17 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { case DHCP6_STATE_BOUND: - if (client->lease->ia.lifetime_t1 == 0xffffffff || - client->lease->ia.lifetime_t2 == 0xffffffff) { + if (client->lease->ia.ia_na.lifetime_t1 == 0xffffffff || + client->lease->ia.ia_na.lifetime_t2 == 0xffffffff) { log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x", - be32toh(client->lease->ia.lifetime_t1), - be32toh(client->lease->ia.lifetime_t2)); + be32toh(client->lease->ia.ia_na.lifetime_t1), + be32toh(client->lease->ia.ia_na.lifetime_t2)); return 0; } - timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC); + timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t1) * USEC_PER_SEC); log_dhcp6_client(client, "T1 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); @@ -1106,18 +1241,18 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { 10 * USEC_PER_SEC, client_timeout_t1, client); if (r < 0) - return r; + goto error; r = sd_event_source_set_priority(client->lease->ia.timeout_t1, client->event_priority); if (r < 0) - return r; + goto error; r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout"); if (r < 0) - return r; + goto error; - timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC); + timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t2) * USEC_PER_SEC); log_dhcp6_client(client, "T2 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); @@ -1128,16 +1263,16 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { 10 * USEC_PER_SEC, client_timeout_t2, client); if (r < 0) - return r; + goto error; r = sd_event_source_set_priority(client->lease->ia.timeout_t2, client->event_priority); if (r < 0) - return r; + goto error; r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout"); if (r < 0) - return r; + goto error; client->state = state; @@ -1151,18 +1286,22 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { clock_boottime_or_monotonic(), 0, 0, client_timeout_resend, client); if (r < 0) - return r; + goto error; r = sd_event_source_set_priority(client->timeout_resend, client->event_priority); if (r < 0) - return r; + goto error; r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout"); if (r < 0) - return r; + goto error; return 0; + + error: + client_reset(client); + return r; } int sd_dhcp6_client_stop(sd_dhcp6_client *client) { @@ -1170,6 +1309,8 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) { client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP); + client->fd = safe_close(client->fd); + return 0; } @@ -1203,32 +1344,18 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { if (r < 0) return r; - r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address); - if (r < 0) { - _cleanup_free_ char *p = NULL; - - (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p); - return log_dhcp6_client_errno(client, r, - "Failed to bind to UDP socket at address %s: %m", strna(p)); - } - - client->fd = r; + if (client->fd < 0) { + r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address); + if (r < 0) { + _cleanup_free_ char *p = NULL; - r = sd_event_add_io(client->event, &client->receive_message, - client->fd, EPOLLIN, client_receive_message, - client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->receive_message, - client->event_priority); - if (r < 0) - goto error; + (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p); + return log_dhcp6_client_errno(client, r, + "Failed to bind to UDP socket at address %s: %m", strna(p)); + } - r = sd_event_source_set_description(client->receive_message, - "dhcp6-receive-message"); - if (r < 0) - goto error; + client->fd = r; + } if (client->information_request) state = DHCP6_STATE_INFORMATION_REQUEST; @@ -1238,10 +1365,6 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { "Managed"); return client_start(client, state); - -error: - client_reset(client); - return r; } int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) { @@ -1301,9 +1424,12 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { client_reset(client); + client->fd = safe_close(client->fd); + sd_dhcp6_client_detach_event(client); free(client->req_opts); + free(client->fqdn); return mfree(client); } @@ -1319,6 +1445,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) { client->n_ref = 1; client->ia_na.type = SD_DHCP6_OPTION_IA_NA; + client->ia_pd.type = SD_DHCP6_OPTION_IA_PD; client->ifindex = -1; client->fd = -1; @@ -1330,8 +1457,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) { for (t = 0; t < client->req_opts_len; t++) client->req_opts[t] = htobe16(default_req_opts[t]); - *ret = client; - client = NULL; + *ret = TAKE_PTR(client); return 0; } diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c index 63a813aa..3952cd99 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2014 Tom Gundersen Copyright (C) 2014-2015 Intel Corporation. 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 "nm-sd-adapt.h" @@ -50,7 +38,7 @@ int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { valid = t; } - t = be32toh(ia->lifetime_t2); + t = be32toh(ia->ia_na.lifetime_t2); if (t > valid) return -EINVAL; @@ -96,11 +84,14 @@ int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len) { assert_return(lease, -EINVAL); - assert_return(id, -EINVAL); - assert_return(len, -EINVAL); - *id = lease->serverid; - *len = lease->serverid_len; + if (!lease->serverid) + return -ENOMSG; + + if (id) + *id = lease->serverid; + if (len) + *len = lease->serverid_len; return 0; } @@ -145,7 +136,7 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) { assert_return(lease, -EINVAL); assert_return(iaid, -EINVAL); - *iaid = lease->ia.id; + *iaid = lease->ia.ia_na.id; return 0; } @@ -177,6 +168,37 @@ void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { lease->addr_iter = lease->ia.addresses; } +int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, + uint8_t *prefix_len, + uint32_t *lifetime_preferred, + uint32_t *lifetime_valid) { + assert_return(lease, -EINVAL); + assert_return(prefix, -EINVAL); + assert_return(prefix_len, -EINVAL); + assert_return(lifetime_preferred, -EINVAL); + assert_return(lifetime_valid, -EINVAL); + + if (!lease->prefix_iter) + return -ENOMSG; + + memcpy(prefix, &lease->prefix_iter->iapdprefix.address, + sizeof(struct in6_addr)); + *prefix_len = lease->prefix_iter->iapdprefix.prefixlen; + *lifetime_preferred = + be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred); + *lifetime_valid = + be32toh(lease->prefix_iter->iapdprefix.lifetime_valid); + + lease->prefix_iter = lease->prefix_iter->addresses_next; + + return 0; +} + +void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) { + if (lease) + lease->prefix_iter = lease->pd.addresses; +} + int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { int r; @@ -228,8 +250,7 @@ int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, if (r < 0) return 0; - strv_free(lease->domains); - lease->domains = domains; + strv_free_and_replace(lease->domains, domains); lease->domains_count = r; return r; @@ -288,8 +309,7 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { if (r < 0) return 0; - lease->ntp_fqdn = strv_free(lease->ntp_fqdn); - lease->ntp_fqdn = servers; + strv_free_and_replace(lease->ntp_fqdn, servers); lease->ntp_fqdn_count = r; break; @@ -383,6 +403,7 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { free(lease->serverid); dhcp6_lease_free_ia(&lease->ia); + dhcp6_lease_free_ia(&lease->pd); free(lease->dns); diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c index 694384b5..ed98c0b6 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4acd.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2014 Axis Communications AB. All rights reserved. Copyright (C) 2015 Tom Gundersen - - 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 "nm-sd-adapt.h" @@ -154,8 +142,7 @@ int sd_ipv4acd_new(sd_ipv4acd **ret) { acd->ifindex = -1; acd->fd = -1; - *ret = acd; - acd = NULL; + *ret = TAKE_PTR(acd); return 0; } @@ -208,8 +195,7 @@ static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_u (void) sd_event_source_set_description(timer, "ipv4acd-timer"); sd_event_source_unref(acd->timer_event_source); - acd->timer_event_source = timer; - timer = NULL; + acd->timer_event_source = TAKE_PTR(timer); return 0; } @@ -289,8 +275,7 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) break; } - /* fall through */ - + _fallthrough_; case IPV4ACD_STATE_WAITING_ANNOUNCE: /* Send announcement packet */ r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr); diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c index 47fc141c..001ebbd8 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2014 Axis Communications AB. All rights reserved. Copyright (C) 2015 Tom Gundersen - - 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 "nm-sd-adapt.h" @@ -26,6 +14,7 @@ #include #include +#include "sd-id128.h" #include "sd-ipv4acd.h" #include "sd-ipv4ll.h" @@ -115,8 +104,7 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) { if (r < 0) return r; - *ret = ll; - ll = NULL; + *ret = TAKE_PTR(ll); return 0; } diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c index 31e24486..d88a0c6f 100644 --- a/src/systemd/src/libsystemd-network/sd-lldp.c +++ b/src/systemd/src/libsystemd-network/sd-lldp.c @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright (C) 2014 Tom Gundersen Copyright (C) 2014 Susant Sahani - - 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 "nm-sd-adapt.h" @@ -403,8 +391,7 @@ _public_ int sd_lldp_new(sd_lldp **ret) { if (r < 0) return r; - *ret = lldp; - lldp = NULL; + *ret = TAKE_PTR(lldp); return 0; } @@ -417,15 +404,15 @@ static int neighbor_compare_func(const void *a, const void *b) { static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) { sd_lldp *lldp = userdata; - int r, q; + int r; r = lldp_make_space(lldp, 0); if (r < 0) return log_lldp_errno(r, "Failed to make space: %m"); - q = lldp_start_timer(lldp, NULL); - if (q < 0) - return log_lldp_errno(q, "Failed to restart timer: %m"); + r = lldp_start_timer(lldp, NULL); + if (r < 0) + return log_lldp_errno(r, "Failed to restart timer: %m"); return 0; } diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c index 9dfe6847..8a9b8055 100644 --- a/src/systemd/src/libsystemd/sd-event/sd-event.c +++ b/src/systemd/src/libsystemd/sd-event/sd-event.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2013 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -123,6 +111,7 @@ struct sd_event_source { uint32_t events; uint32_t revents; bool registered:1; + bool owned:1; } io; struct { sd_event_time_handler_t callback; @@ -241,8 +230,14 @@ struct sd_event { unsigned delays[sizeof(usec_t) * 8]; }; +static thread_local sd_event *default_event = NULL; + static void source_disconnect(sd_event_source *s); +static sd_event *event_resolve(sd_event *e) { + return e == SD_EVENT_DEFAULT ? default_event : e; +} + static int pending_prioq_compare(const void *a, const void *b) { const sd_event_source *x = a, *y = b; @@ -451,6 +446,8 @@ _public_ int sd_event_new(sd_event** ret) { goto fail; } + e->epoll_fd = fd_move_above_stdio(e->epoll_fd); + if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) { log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 ... 2^63 us will be logged every 5s."); e->profile_delays = true; @@ -689,7 +686,7 @@ static int event_make_signal_data( return 0; } - d->fd = r; + d->fd = fd_move_above_stdio(r); ev.events = EPOLLIN; ev.data.ptr = d; @@ -884,6 +881,10 @@ static void source_free(sd_event_source *s) { assert(s); source_disconnect(s); + + if (s->type == SOURCE_IO && s->io.owned) + safe_close(s->io.fd); + free(s->description); free(s); } @@ -968,6 +969,7 @@ _public_ int sd_event_add_io( int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(fd >= 0, -EBADF); assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL); assert_return(callback, -EINVAL); @@ -1034,6 +1036,8 @@ static int event_setup_timer_fd( if (fd < 0) return -errno; + fd = fd_move_above_stdio(fd); + ev.events = EPOLLIN; ev.data.ptr = d; @@ -1068,6 +1072,7 @@ _public_ int sd_event_add_time( int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(accuracy != (uint64_t) -1, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -1150,6 +1155,7 @@ _public_ int sd_event_add_signal( int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(SIGNAL_VALID(sig), -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -1210,6 +1216,7 @@ _public_ int sd_event_add_child( int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(pid > 1, -EINVAL); assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL); assert_return(options != 0, -EINVAL); @@ -1267,6 +1274,7 @@ _public_ int sd_event_add_defer( int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(callback, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -1301,6 +1309,7 @@ _public_ int sd_event_add_post( int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(callback, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -1339,6 +1348,7 @@ _public_ int sd_event_add_exit( int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(callback, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -1484,6 +1494,21 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) { return 0; } +_public_ int sd_event_source_get_io_fd_own(sd_event_source *s) { + assert_return(s, -EINVAL); + assert_return(s->type == SOURCE_IO, -EDOM); + + return s->io.owned; +} + +_public_ int sd_event_source_set_io_fd_own(sd_event_source *s, int own) { + assert_return(s, -EINVAL); + assert_return(s->type == SOURCE_IO, -EDOM); + + s->io.owned = own; + return 0; +} + _public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) { assert_return(s, -EINVAL); assert_return(events, -EINVAL); @@ -2369,6 +2394,7 @@ static int event_prepare(sd_event *e) { static int dispatch_exit(sd_event *e) { sd_event_source *p; + _cleanup_(sd_event_unrefp) sd_event *ref = NULL; int r; assert(e); @@ -2379,15 +2405,11 @@ static int dispatch_exit(sd_event *e) { return 0; } - sd_event_ref(e); + ref = sd_event_ref(e); e->iteration++; e->state = SD_EVENT_EXITING; - r = source_dispatch(p); - e->state = SD_EVENT_INITIAL; - sd_event_unref(e); - return r; } @@ -2452,6 +2474,7 @@ _public_ int sd_event_prepare(sd_event *e) { int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); @@ -2509,6 +2532,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { int r, m, i; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state == SD_EVENT_ARMED, -EBUSY); @@ -2615,6 +2639,7 @@ _public_ int sd_event_dispatch(sd_event *e) { int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state == SD_EVENT_PENDING, -EBUSY); @@ -2624,14 +2649,12 @@ _public_ int sd_event_dispatch(sd_event *e) { p = event_next_pending(e); if (p) { - sd_event_ref(e); + _cleanup_(sd_event_unrefp) sd_event *ref = NULL; + ref = sd_event_ref(e); e->state = SD_EVENT_RUNNING; r = source_dispatch(p); e->state = SD_EVENT_INITIAL; - - sd_event_unref(e); - return r; } @@ -2656,6 +2679,7 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) { int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); @@ -2697,30 +2721,29 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) { } _public_ int sd_event_loop(sd_event *e) { + _cleanup_(sd_event_unrefp) sd_event *ref = NULL; int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); - sd_event_ref(e); + ref = sd_event_ref(e); while (e->state != SD_EVENT_FINISHED) { r = sd_event_run(e, (uint64_t) -1); if (r < 0) - goto finish; + return r; } - r = e->exit_code; - -finish: - sd_event_unref(e); - return r; + return e->exit_code; } _public_ int sd_event_get_fd(sd_event *e) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); return e->epoll_fd; @@ -2728,6 +2751,7 @@ _public_ int sd_event_get_fd(sd_event *e) { _public_ int sd_event_get_state(sd_event *e) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); return e->state; @@ -2735,6 +2759,7 @@ _public_ int sd_event_get_state(sd_event *e) { _public_ int sd_event_get_exit_code(sd_event *e, int *code) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(code, -EINVAL); assert_return(!event_pid_changed(e), -ECHILD); @@ -2747,6 +2772,7 @@ _public_ int sd_event_get_exit_code(sd_event *e, int *code) { _public_ int sd_event_exit(sd_event *e, int code) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -2758,6 +2784,7 @@ _public_ int sd_event_exit(sd_event *e, int code) { _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(usec, -EINVAL); assert_return(!event_pid_changed(e), -ECHILD); @@ -2782,8 +2809,6 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) { } _public_ int sd_event_default(sd_event **ret) { - - static thread_local sd_event *default_event = NULL; sd_event *e = NULL; int r; @@ -2810,6 +2835,7 @@ _public_ int sd_event_default(sd_event **ret) { #if 0 /* NM_IGNORED */ _public_ int sd_event_get_tid(sd_event *e, pid_t *tid) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(tid, -EINVAL); assert_return(!event_pid_changed(e), -ECHILD); @@ -2825,6 +2851,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) { int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); if (e->watchdog == !!b) @@ -2875,6 +2902,7 @@ fail: _public_ int sd_event_get_watchdog(sd_event *e) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); return e->watchdog; @@ -2882,6 +2910,7 @@ _public_ int sd_event_get_watchdog(sd_event *e) { _public_ int sd_event_get_iteration(sd_event *e, uint64_t *ret) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); *ret = e->iteration; diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.c b/src/systemd/src/libsystemd/sd-id128/id128-util.c index 19277021..26eef0fd 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.c +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.c @@ -1,28 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2016 Lennart Poettering - - 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 "nm-sd-adapt.h" +#include #include #include #include "fd-util.h" +#include "fs-util.h" #include "hexdecoct.h" #include "id128-util.h" #include "io-util.h" @@ -120,7 +110,7 @@ int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) { if (buffer[32] != '\n') return -EINVAL; - /* fall through */ + _fallthrough_; case 32: /* plain UUID without trailing newline */ if (f == ID128_UUID) return -EINVAL; @@ -132,7 +122,7 @@ int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) { if (buffer[36] != '\n') return -EINVAL; - /* fall through */ + _fallthrough_; case 36: /* RFC UUID without trailing newline */ if (f == ID128_PLAIN) return -EINVAL; @@ -183,9 +173,13 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) { if (do_sync) { if (fsync(fd) < 0) return -errno; + + r = fsync_directory_of_file(fd); + if (r < 0) + return r; } - return r; + return 0; } int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) { diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.h b/src/systemd/src/libsystemd/sd-id128/id128-util.h index 6b3855ac..9aeb27a2 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.h +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2016 Lennart Poettering - - 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 diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c index 052110d5..de0ae600 100644 --- a/src/systemd/src/libsystemd/sd-id128/sd-id128.c +++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2011 Lennart Poettering - - 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 "nm-sd-adapt.h" diff --git a/src/systemd/src/shared/dns-domain.c b/src/systemd/src/shared/dns-domain.c index c313a033..3f41be62 100644 --- a/src/systemd/src/shared/dns-domain.c +++ b/src/systemd/src/shared/dns-domain.c @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. Copyright 2014 Lennart Poettering - - 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 "nm-sd-adapt.h" @@ -301,8 +289,7 @@ int dns_label_escape_new(const char *p, size_t l, char **ret) { if (r < 0) return r; - *ret = s; - s = NULL; + *ret = TAKE_PTR(s); return r; } @@ -416,10 +403,9 @@ int dns_name_concat(const char *a, const char *b, char **_ret) { if (a) p = a; - else if (b) { - p = b; - b = NULL; - } else + else if (b) + p = TAKE_PTR(b); + else goto finish; for (;;) { @@ -434,8 +420,7 @@ int dns_name_concat(const char *a, const char *b, char **_ret) { if (b) { /* Now continue with the second string, if there is one */ - p = b; - b = NULL; + p = TAKE_PTR(b); continue; } @@ -485,8 +470,7 @@ finish: } ret[n] = 0; - *_ret = ret; - ret = NULL; + *_ret = TAKE_PTR(ret); } return 0; @@ -609,8 +593,7 @@ int dns_name_endswith(const char *name, const char *suffix) { /* Not the same, let's jump back, and try with the next label again */ s = suffix; - n = saved_n; - saved_n = NULL; + n = TAKE_PTR(saved_n); } } } @@ -685,8 +668,8 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char /* Not the same, let's jump back, and try with the next label again */ s = old_suffix; - n = saved_after; - saved_after = saved_before = NULL; + n = TAKE_PTR(saved_after); + saved_before = NULL; } } @@ -701,23 +684,26 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char } int dns_name_between(const char *a, const char *b, const char *c) { - int n; - /* Determine if b is strictly greater than a and strictly smaller than c. We consider the order of names to be circular, so that if a is strictly greater than c, we consider b to be between them if it is either greater than a or smaller than c. This is how the canonical DNS name order used in NSEC records work. */ - n = dns_name_compare_func(a, c); - if (n == 0) - return -EINVAL; - else if (n < 0) - /* a<---b--->c */ + if (dns_name_compare_func(a, c) < 0) + /* + a and c are properly ordered: + a<---b--->c + */ return dns_name_compare_func(a, b) < 0 && dns_name_compare_func(b, c) < 0; else - /* <--b--c a--b--> */ + /* + a and c are equal or 'reversed': + <--b--c a-----> + or: + <-----c a--b--> + */ return dns_name_compare_func(b, c) < 0 || dns_name_compare_func(a, b) < 0; } @@ -969,6 +955,12 @@ bool dns_srv_type_is_valid(const char *name) { return c == 2; /* exactly two labels */ } +bool dnssd_srv_type_is_valid(const char *name) { + return dns_srv_type_is_valid(name) && + ((dns_name_endswith(name, "_tcp") > 0) || + (dns_name_endswith(name, "_udp") > 0)); /* Specific to DNS-SD. RFC 6763, Section 7 */ +} + bool dns_service_name_is_valid(const char *name) { size_t l; @@ -1114,20 +1106,14 @@ finish: if (r < 0) return r; - if (_domain) { - *_domain = domain; - domain = NULL; - } + if (_domain) + *_domain = TAKE_PTR(domain); - if (_type) { - *_type = type; - type = NULL; - } + if (_type) + *_type = TAKE_PTR(type); - if (_name) { - *_name = name; - name = NULL; - } + if (_name) + *_name = TAKE_PTR(name); return 0; } @@ -1310,8 +1296,8 @@ int dns_name_apply_idna(const char *name, char **ret) { } } - *ret = t; - t = NULL; + *ret = TAKE_PTR(t); + return 1; /* *ret has been written */ } @@ -1368,8 +1354,7 @@ int dns_name_apply_idna(const char *name, char **ret) { return -ENOMEM; buf[n] = 0; - *ret = buf; - buf = NULL; + *ret = TAKE_PTR(buf); return 1; #else diff --git a/src/systemd/src/shared/dns-domain.h b/src/systemd/src/shared/dns-domain.h index d1a99be7..47bd5357 100644 --- a/src/systemd/src/shared/dns-domain.h +++ b/src/systemd/src/shared/dns-domain.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** This file is part of systemd. Copyright 2014 Lennart Poettering - - 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 @@ -97,6 +85,7 @@ bool dns_name_is_single_label(const char *name); int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical); bool dns_srv_type_is_valid(const char *name); +bool dnssd_srv_type_is_valid(const char *name); bool dns_service_name_is_valid(const char *name); int dns_service_join(const char *name, const char *type, const char *domain, char **ret); diff --git a/src/systemd/src/systemd/_sd-common.h b/src/systemd/src/systemd/_sd-common.h index 97c39438..b4400e7b 100644 --- a/src/systemd/src/systemd/_sd-common.h +++ b/src/systemd/src/systemd/_sd-common.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef foosdcommonhfoo #define foosdcommonhfoo diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h index 5e46d8d0..fd0a5693 100644 --- a/src/systemd/src/systemd/sd-dhcp-client.h +++ b/src/systemd/src/systemd/sd-dhcp-client.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef foosddhcpclienthfoo #define foosddhcpclienthfoo @@ -81,6 +82,7 @@ enum { SD_DHCP_OPTION_REBINDING_T2_TIME = 59, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61, + SD_DHCP_OPTION_USER_CLASS = 77, SD_DHCP_OPTION_FQDN = 81, SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, @@ -131,6 +133,11 @@ int sd_dhcp_client_set_iaid_duid( uint16_t duid_type, const void *duid, size_t duid_len); +int sd_dhcp_client_set_duid( + sd_dhcp_client *client, + uint16_t duid_type, + const void *duid, + size_t duid_len); int sd_dhcp_client_get_client_id( sd_dhcp_client *client, uint8_t *type, @@ -148,6 +155,9 @@ int sd_dhcp_client_set_hostname( int sd_dhcp_client_set_vendor_class_identifier( sd_dhcp_client *client, const char *vci); +int sd_dhcp_client_set_user_class( + sd_dhcp_client *client, + const char* const *user_class); int sd_dhcp_client_get_lease( sd_dhcp_client *client, sd_dhcp_lease **ret); diff --git a/src/systemd/src/systemd/sd-dhcp-lease.h b/src/systemd/src/systemd/sd-dhcp-lease.h index 7ab99ccc..3cc7fcab 100644 --- a/src/systemd/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/src/systemd/sd-dhcp-lease.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef foosddhcpleasehfoo #define foosddhcpleasehfoo diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h index 7819f0d2..cadb32a0 100644 --- a/src/systemd/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/src/systemd/sd-dhcp6-client.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef foosddhcp6clienthfoo #define foosddhcp6clienthfoo @@ -22,6 +23,7 @@ #include #include +#include #include #include "sd-dhcp6-lease.h" @@ -63,11 +65,15 @@ enum { SD_DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */ SD_DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */ + SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, prefix delegation */ + SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, prefix delegation */ SD_DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */ /* option code 35 is unassigned */ + SD_DHCP6_OPTION_FQDN = 39, /* RFC 4704 */ + SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */ /* option codes 89-142 are unassigned */ @@ -101,6 +107,9 @@ int sd_dhcp6_client_set_duid( int sd_dhcp6_client_set_iaid( sd_dhcp6_client *client, uint32_t iaid); +int sd_dhcp6_client_set_fqdn( + sd_dhcp6_client *client, + const char *fqdn); int sd_dhcp6_client_set_information_request( sd_dhcp6_client *client, int enabled); @@ -110,6 +119,8 @@ int sd_dhcp6_client_get_information_request( int sd_dhcp6_client_set_request_option( sd_dhcp6_client *client, uint16_t option); +int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, + bool delegation); int sd_dhcp6_client_get_lease( sd_dhcp6_client *client, diff --git a/src/systemd/src/systemd/sd-dhcp6-lease.h b/src/systemd/src/systemd/sd-dhcp6-lease.h index 184fbb8e..22a5f8ce 100644 --- a/src/systemd/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/src/systemd/sd-dhcp6-lease.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef foosddhcp6leasehfoo #define foosddhcp6leasehfoo @@ -35,6 +36,11 @@ int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr, uint32_t *lifetime_preferred, uint32_t *lifetime_valid); +void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease); +int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, + uint8_t *prefix_len, + uint32_t *lifetime_preferred, + uint32_t *lifetime_valid); int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs); int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains); diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h index f8cb8956..ec4b7bcf 100644 --- a/src/systemd/src/systemd/sd-event.h +++ b/src/systemd/src/systemd/sd-event.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef foosdeventhfoo #define foosdeventhfoo @@ -25,6 +26,7 @@ #include #include #include +#include #include "_sd-common.h" @@ -39,6 +41,8 @@ _SD_BEGIN_DECLARATIONS; +#define SD_EVENT_DEFAULT ((sd_event *) 1) + typedef struct sd_event sd_event; typedef struct sd_event_source sd_event_source; @@ -123,6 +127,8 @@ int sd_event_source_get_enabled(sd_event_source *s, int *enabled); int sd_event_source_set_enabled(sd_event_source *s, int enabled); int sd_event_source_get_io_fd(sd_event_source *s); int sd_event_source_set_io_fd(sd_event_source *s, int fd); +int sd_event_source_get_io_fd_own(sd_event_source *s); +int sd_event_source_set_io_fd_own(sd_event_source *s, int own); int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events); int sd_event_source_set_io_events(sd_event_source *s, uint32_t events); int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents); diff --git a/src/systemd/src/systemd/sd-id128.h b/src/systemd/src/systemd/sd-id128.h index 9b38969b..67fc5956 100644 --- a/src/systemd/src/systemd/sd-id128.h +++ b/src/systemd/src/systemd/sd-id128.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef foosdid128hfoo #define foosdid128hfoo diff --git a/src/systemd/src/systemd/sd-ipv4acd.h b/src/systemd/src/systemd/sd-ipv4acd.h index 16d99983..677ae3b2 100644 --- a/src/systemd/src/systemd/sd-ipv4acd.h +++ b/src/systemd/src/systemd/sd-ipv4acd.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef foosdipv4acdfoo #define foosdipv4acdfoo diff --git a/src/systemd/src/systemd/sd-ipv4ll.h b/src/systemd/src/systemd/sd-ipv4ll.h index 5ba92083..c330b0ae 100644 --- a/src/systemd/src/systemd/sd-ipv4ll.h +++ b/src/systemd/src/systemd/sd-ipv4ll.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef foosdipv4llfoo #define foosdipv4llfoo diff --git a/src/systemd/src/systemd/sd-lldp.h b/src/systemd/src/systemd/sd-lldp.h index 3f35eebe..0a76fa63 100644 --- a/src/systemd/src/systemd/sd-lldp.h +++ b/src/systemd/src/systemd/sd-lldp.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef foosdlldphfoo #define foosdlldphfoo diff --git a/src/systemd/src/systemd/sd-ndisc.h b/src/systemd/src/systemd/sd-ndisc.h index 9f7d4ef7..15211450 100644 --- a/src/systemd/src/systemd/sd-ndisc.h +++ b/src/systemd/src/systemd/sd-ndisc.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef foosdndiscfoo #define foosdndiscfoo diff --git a/src/tests/config/meson.build b/src/tests/config/meson.build new file mode 100644 index 00000000..f542c453 --- /dev/null +++ b/src/tests/config/meson.build @@ -0,0 +1,21 @@ +test_unit = 'test-config' + +sources = files( + 'nm-test-device.c', + 'test-config.c' +) + +test_config_dir = meson.current_source_dir() + +exe = executable( + test_unit, + sources, + dependencies: test_nm_dep, + c_args: nm_build_cflags, +) + +test( + 'config/' + test_unit, + test_script, + args: test_args + [exe.full_path()] +) diff --git a/src/tests/config/nm-test-device.c b/src/tests/config/nm-test-device.c index 3ec866f5..49631583 100644 --- a/src/tests/config/nm-test-device.c +++ b/src/tests/config/nm-test-device.c @@ -57,7 +57,7 @@ nm_test_device_init (NMTestDevice *self) } /* We jump over NMDevice's construct/destruct methods, which require NMPlatform - * and NMConnectionProvider to be initialized. + * and NMSettings to be initialized. */ static void constructed (GObject *object) diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c index 89b22a9f..75fef4fa 100644 --- a/src/tests/config/test-config.c +++ b/src/tests/config/test-config.c @@ -25,11 +25,14 @@ #include "nm-config.h" #include "nm-test-device.h" #include "platform/nm-fake-platform.h" -#include "nm-bus-manager.h" +#include "nm-dbus-manager.h" #include "nm-connectivity.h" #include "nm-test-utils-core.h" +#define TEST_DIR NM_BUILD_SRCDIR"/src/tests/config" +#define BUILD_DIR NM_BUILD_BUILDDIR"/src/tests/config" + /*****************************************************************************/ static void @@ -133,9 +136,9 @@ test_config_simple (void) gs_unref_object NMDevice *dev51 = nm_test_device_new ("00:00:00:00:00:51"); gs_unref_object NMDevice *dev52 = nm_test_device_new ("00:00:00:00:00:52"); - config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "", NULL); + config = setup_config (NULL, TEST_DIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "", NULL); - g_assert_cmpstr (nm_config_data_get_config_main_file (nm_config_get_data_orig (config)), ==, SRCDIR "/NetworkManager.conf"); + g_assert_cmpstr (nm_config_data_get_config_main_file (nm_config_get_data_orig (config)), ==, TEST_DIR "/NetworkManager.conf"); g_assert_cmpstr (_config_get_dhcp_client_a (config), ==, "dhclient"); g_assert_cmpstr (nm_config_get_log_level (config), ==, "INFO"); g_assert_cmpint (nm_config_data_get_connectivity_interval (nm_config_get_data_orig (config)), ==, 100); @@ -164,12 +167,10 @@ test_config_simple (void) g_assert_cmpstr (value, ==, "51"); g_free (value); - value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ipv6.route-metric", NULL); g_assert_cmpstr (value, ==, NULL); g_free (value); - value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ipv4.route-metric", NULL); g_assert_cmpstr (value, ==, "50"); g_free (value); @@ -186,7 +187,6 @@ test_config_simple (void) g_assert_cmpstr (value, ==, "52"); g_free (value); - value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "dummy.test1", dev51); g_assert_cmpstr (value, ==, "yes"); g_free (value); @@ -209,7 +209,7 @@ test_config_non_existent (void) { GError *error = NULL; - setup_config (&error, SRCDIR "/no-such-file", "", NULL, "/no/such/dir", "", NULL); + setup_config (&error, TEST_DIR "/no-such-file", "", NULL, "/no/such/dir", "", NULL); g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND); g_clear_error (&error); } @@ -219,7 +219,7 @@ test_config_parse_error (void) { GError *error = NULL; - setup_config (&error, SRCDIR "/bad.conf", "", NULL, "/no/such/dir", "", NULL); + setup_config (&error, TEST_DIR "/bad.conf", "", NULL, "/no/such/dir", "", NULL); g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE); g_clear_error (&error); } @@ -230,12 +230,12 @@ test_config_override (void) gs_unref_object NMConfig *config = NULL; gs_strfreev char **plugins = NULL; - config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "", + config = setup_config (NULL, TEST_DIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "", "--plugins", "alpha,beta,gamma,delta", "--connectivity-interval", "12", NULL); - g_assert_cmpstr (nm_config_data_get_config_main_file (nm_config_get_data_orig (config)), ==, SRCDIR "/NetworkManager.conf"); + g_assert_cmpstr (nm_config_data_get_config_main_file (nm_config_get_data_orig (config)), ==, TEST_DIR "/NetworkManager.conf"); g_assert_cmpstr (_config_get_dhcp_client_a (config), ==, "dhclient"); g_assert_cmpstr (nm_config_get_log_level (config), ==, "INFO"); g_assert_cmpint (nm_config_data_get_connectivity_interval (nm_config_get_data_orig (config)), ==, 12); @@ -254,9 +254,9 @@ test_config_global_dns (void) NMConfig *config; const NMGlobalDnsConfig *dns; NMGlobalDnsDomain *domain; - const char *const *strv; + const char *const*strv; - config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, + config = setup_config (NULL, TEST_DIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "", NULL); dns = nm_config_data_get_global_dns_config (nm_config_get_data_orig (config)); @@ -311,37 +311,37 @@ test_config_global_dns (void) g_object_unref (config); /* Check that a file without a default domain section gives a NULL configuration */ - config = setup_config (NULL, SRCDIR "/global-dns-invalid.conf", "", NULL, + config = setup_config (NULL, TEST_DIR "/global-dns-invalid.conf", "", NULL, "/no/such/dir", "", NULL); dns = nm_config_data_get_global_dns_config (nm_config_get_data_orig (config)); g_assert (!dns); g_object_unref (config); } -#if WITH_CONCHECK static void test_config_connectivity_check (void) { - const char *CONFIG_INTERN = BUILDDIR"/test-connectivity-check-intern.conf"; +#if WITH_CONCHECK + const char *CONFIG_INTERN = BUILD_DIR"/test-connectivity-check-intern.conf"; NMConfig *config; NMConnectivity *connectivity; g_assert (g_file_set_contents (CONFIG_INTERN, "", 0, NULL)); - config = setup_config (NULL, SRCDIR "/NetworkManager.conf", CONFIG_INTERN, NULL, + config = setup_config (NULL, TEST_DIR "/NetworkManager.conf", CONFIG_INTERN, NULL, "/no/such/dir", "", NULL); connectivity = nm_connectivity_get(); g_assert (nm_connectivity_check_enabled (connectivity)); /* disable connectivity checking */ - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal *"); + NMTST_EXPECT_NM_INFO ("config: signal: *"); nm_config_set_connectivity_check_enabled (config, FALSE); g_test_assert_expected_messages (); g_assert (!nm_connectivity_check_enabled (connectivity)); /* re-enable connectivity checking */ - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal *"); + NMTST_EXPECT_NM_INFO ("config: signal: *"); nm_config_set_connectivity_check_enabled (config, TRUE); g_test_assert_expected_messages (); @@ -351,8 +351,10 @@ test_config_connectivity_check (void) g_object_unref (config); g_assert (remove (CONFIG_INTERN) == 0); -} +#else + g_test_skip ("concheck disabled"); #endif +} static void test_config_no_auto_default (void) @@ -372,7 +374,7 @@ test_config_no_auto_default (void) g_assert_cmpint (nwrote, ==, 18); nm_close (fd); - config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "", + config = setup_config (NULL, TEST_DIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "", "--no-auto-default", state_file, NULL); @@ -386,7 +388,7 @@ test_config_no_auto_default (void) g_assert (!nm_config_get_no_auto_default_for_device (config, dev3)); g_assert (nm_config_get_no_auto_default_for_device (config, dev4)); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal NO_AUTO_DEFAULT,no-auto-default *"); + NMTST_EXPECT_NM_INFO ("config: signal: NO_AUTO_DEFAULT,no-auto-default *"); nm_config_set_no_auto_default_for_device (config, dev3); g_test_assert_expected_messages (); @@ -394,7 +396,7 @@ test_config_no_auto_default (void) g_object_unref (config); - config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "", + config = setup_config (NULL, TEST_DIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "", "--no-auto-default", state_file, NULL); @@ -422,9 +424,9 @@ test_config_confdir (void) char *value; GSList *specs; - config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, SRCDIR "/conf.d", "", NULL); + config = setup_config (NULL, TEST_DIR "/NetworkManager.conf", "", NULL, TEST_DIR "/conf.d", "", NULL); - g_assert_cmpstr (nm_config_data_get_config_main_file (nm_config_get_data_orig (config)), ==, SRCDIR "/NetworkManager.conf"); + g_assert_cmpstr (nm_config_data_get_config_main_file (nm_config_get_data_orig (config)), ==, TEST_DIR "/NetworkManager.conf"); g_assert_cmpstr (_config_get_dhcp_client_a (config), ==, "dhcpcd"); g_assert_cmpstr (nm_config_get_log_level (config), ==, "INFO"); g_assert_cmpstr (nm_config_get_log_domains (config), ==, "PLATFORM,DNS,WIFI"); @@ -525,8 +527,8 @@ test_config_confdir_parse_error (void) { GError *error = NULL; - /* Using SRCDIR as the conf dir will pick up bad.conf */ - setup_config (&error, SRCDIR "/NetworkManager.conf", "", NULL, SRCDIR, "", NULL); + /* Using TEST_DIR as the conf dir will pick up bad.conf */ + setup_config (&error, TEST_DIR "/NetworkManager.conf", "", NULL, TEST_DIR, "", NULL); g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE); g_clear_error (&error); } @@ -597,9 +599,9 @@ _set_values_user (NMConfig *config, config_data_before = g_object_ref (nm_config_get_data (config)); if (expected_changes != NM_CONFIG_CHANGE_NONE) - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal *"); + NMTST_EXPECT_NM_INFO ("config: signal: *"); else - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGHUP (no changes from disk)*"); + NMTST_EXPECT_NM_INFO ("config: signal: SIGHUP (no changes from disk)*"); nm_config_reload (config, NM_CONFIG_CHANGE_CAUSE_SIGHUP); @@ -641,7 +643,7 @@ _set_values_intern (NMConfig *config, &config_changed_data); if (expected_changes != NM_CONFIG_CHANGE_NONE) - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal *"); + NMTST_EXPECT_NM_INFO ("config: signal: *"); nm_config_set_values (config, keyfile_intern, TRUE, FALSE); @@ -809,8 +811,8 @@ static void test_config_set_values (void) { gs_unref_object NMConfig *config = NULL; - const char *CONFIG_USER = BUILDDIR"/test-set-values-user.conf"; - const char *CONFIG_INTERN = BUILDDIR"/test-set-values-intern.conf"; + const char *CONFIG_USER = BUILD_DIR"/test-set-values-user.conf"; + const char *CONFIG_INTERN = BUILD_DIR"/test-set-values-intern.conf"; const char *atomic_section_prefixes[] = { "atomic-prefix-1.", "atomic-prefix-2.", @@ -894,7 +896,7 @@ test_config_signal (void) NMConfigChangeFlags expected; gs_unref_object NMConfigData *config_data_orig = NULL; - config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, SRCDIR "/conf.d", "", NULL); + config = setup_config (NULL, TEST_DIR "/NetworkManager.conf", "", NULL, TEST_DIR "/conf.d", "", NULL); config_data_orig = g_object_ref (nm_config_get_data_orig (config)); @@ -904,18 +906,17 @@ test_config_signal (void) &expected); expected = NM_CONFIG_CHANGE_CAUSE_SIGUSR1; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGUSR1"); + NMTST_EXPECT_NM_INFO ("config: signal: SIGUSR1"); nm_config_reload (config, expected); expected = NM_CONFIG_CHANGE_CAUSE_SIGUSR2; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGUSR2"); + NMTST_EXPECT_NM_INFO ("config: signal: SIGUSR2"); nm_config_reload (config, expected); expected = NM_CONFIG_CHANGE_CAUSE_SIGHUP; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGHUP (no changes from disk)*"); + NMTST_EXPECT_NM_INFO ("config: signal: SIGHUP (no changes from disk)*"); nm_config_reload (config, expected); - /* test with subscribing two signals... * * This test exposes glib bug https://bugzilla.redhat.com/show_bug.cgi?id=1260577 @@ -925,11 +926,10 @@ test_config_signal (void) G_CALLBACK (_test_signal_config_changed_cb2), &expected); expected = NM_CONFIG_CHANGE_CAUSE_SIGUSR2; - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGUSR2"); + NMTST_EXPECT_NM_INFO ("config: signal: SIGUSR2"); nm_config_reload (config, NM_CONFIG_CHANGE_CAUSE_SIGUSR2); g_signal_handlers_disconnect_by_func (config, _test_signal_config_changed_cb2, &expected); - g_signal_handlers_disconnect_by_func (config, _test_signal_config_changed_cb, &expected); g_assert (config_data_orig == nm_config_get_data (config)); @@ -948,19 +948,19 @@ test_config_enable (void) _nm_config_match_env = g_strdup ("something-else"); _nm_config_match_nm_version = nm_encode_version (1, 3, 4); - config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, SRCDIR "/conf.d", "", NULL); + config = setup_config (NULL, TEST_DIR "/NetworkManager.conf", "", NULL, TEST_DIR "/conf.d", "", NULL); assert_config_value (nm_config_get_data_orig (config), "test-group-config-enable-1", "key1", NULL); g_clear_object (&config); _nm_config_match_nm_version = nm_encode_version (1, 5, 32); - config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, SRCDIR "/conf.d", "", NULL); + config = setup_config (NULL, TEST_DIR "/NetworkManager.conf", "", NULL, TEST_DIR "/conf.d", "", NULL); assert_config_value (nm_config_get_data_orig (config), "test-group-config-enable-1", "key1", "enabled"); g_clear_object (&config); _nm_config_match_nm_version = nm_encode_version (1, 5, 3); g_clear_pointer (&_nm_config_match_env, g_free); _nm_config_match_env = g_strdup ("test-match-env-1"); - config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, SRCDIR "/conf.d", "", NULL); + config = setup_config (NULL, TEST_DIR "/NetworkManager.conf", "", NULL, TEST_DIR "/conf.d", "", NULL); assert_config_value (nm_config_get_data_orig (config), "test-group-config-enable-1", "key1", "enabled"); g_clear_object (&config); @@ -980,14 +980,14 @@ test_config_state_file (void) gboolean ret; gs_free char *file_data = NULL; gsize file_size; - const char *const TMP_FILE = BUILDDIR "/tmp.state"; + const char *const TMP_FILE = BUILD_DIR "/tmp.state"; - ret = g_file_get_contents (SRCDIR "/NetworkManager.state", &file_data, &file_size, &error); + ret = g_file_get_contents (TEST_DIR "/NetworkManager.state", &file_data, &file_size, &error); nmtst_assert_success (ret, error); ret = g_file_set_contents (TMP_FILE, file_data, file_size, &error); nmtst_assert_success (ret, error); - config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, SRCDIR "/conf.d", "", + config = setup_config (NULL, TEST_DIR "/NetworkManager.conf", "", NULL, TEST_DIR "/conf.d", "", "--state-file", TMP_FILE, NULL); g_assert (config); @@ -1013,7 +1013,7 @@ test_config_state_file (void) g_object_unref (config); /* Reload configuration */ - config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, SRCDIR "/conf.d", "", + config = setup_config (NULL, TEST_DIR "/NetworkManager.conf", "", NULL, TEST_DIR "/conf.d", "", "--state-file", TMP_FILE, NULL); g_assert (config); @@ -1037,13 +1037,6 @@ main (int argc, char **argv) { nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); - /* Initialize the DBus manager singleton explicitly, because it is accessed by - * the class initializer of NMDevice (used by the NMTestDevice stub). - * This way, we skip calling nm_bus_manager_init_bus() which would - * either fail and/or cause unexpected actions in the test. - * */ - nm_bus_manager_setup (g_object_new (NM_TYPE_BUS_MANAGER, NULL)); - nm_fake_platform_setup (); g_test_add_func ("/config/simple", test_config_simple); @@ -1055,9 +1048,7 @@ main (int argc, char **argv) g_test_add_func ("/config/set-values", test_config_set_values); g_test_add_func ("/config/global-dns", test_config_global_dns); -#if WITH_CONCHECK g_test_add_func ("/config/connectivity-check", test_config_connectivity_check); -#endif g_test_add_func ("/config/signal", test_config_signal); diff --git a/src/tests/meson.build b/src/tests/meson.build new file mode 100644 index 00000000..430ffe43 --- /dev/null +++ b/src/tests/meson.build @@ -0,0 +1,47 @@ +subdir('config') + +test_units = [ + 'test-general', + 'test-general-with-expect', + 'test-ip4-config', + 'test-ip6-config', + 'test-dcb', + 'test-wired-defname', + 'test-utils' +] + +foreach test_unit: test_units + exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep + ) + + test( + 'src/' + test_unit, + test_script, + args: test_args + [exe.full_path()] + ) +endforeach + +test_unit = 'test-systemd' + +cflags = [ + '-DNETWORKMANAGER_COMPILATION_TEST', + '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD', +] + +exe = executable( + test_unit, + [test_unit + '.c'], + include_directories: src_inc, + dependencies: nm_core_dep, + c_args: cflags, + link_with: libsystemd_nm +) + +test( + 'src/' + test_unit, + test_script, + args: test_args + [exe.full_path()] +) diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c index 2911d84d..a78b5311 100644 --- a/src/tests/test-general-with-expect.c +++ b/src/tests/test-general-with-expect.c @@ -167,7 +167,7 @@ test_nm_utils_kill_child_spawn (char **argv, gboolean do_not_reap_child) } static pid_t -test_nm_utils_kill_child_create_and_join_pgroup (void) +do_test_nm_utils_kill_child_create_and_join_pgroup (void) { int err, tmp = 0; int pipefd[2]; @@ -177,10 +177,7 @@ test_nm_utils_kill_child_create_and_join_pgroup (void) g_assert (err == 0); pgid = fork(); - if (pgid < 0) { - g_assert_not_reached (); - return pgid; - } + g_assert (pgid >= 0); if (pgid == 0) { /* child process... */ @@ -206,7 +203,6 @@ test_nm_utils_kill_child_create_and_join_pgroup (void) err = setpgid (0, pgid); g_assert (err == 0); - do { err = waitpid (pgid, &tmp, 0); } while (err == -1 && errno == EINTR); @@ -219,9 +215,8 @@ test_nm_utils_kill_child_create_and_join_pgroup (void) #define TEST_TOKEN "nm_test_kill_child_process" static void -test_nm_utils_kill_child (void) +do_test_nm_utils_kill_child (void) { - int err; GLogLevelFlags fatal_mask; char *argv_watchdog[] = { "bash", @@ -255,7 +250,6 @@ test_nm_utils_kill_child (void) "trap \"while true; do :; done\" TERM; while true; do :; done; #" TEST_TOKEN, NULL, }; - pid_t gpid; pid_t pid1a_1, pid1a_2, pid1a_3, pid2a, pid3a, pid4a; pid_t pid1s_1, pid1s_2, pid1s_3, pid2s, pid3s, pid4s; @@ -263,8 +257,6 @@ test_nm_utils_kill_child (void) const int expected_signal_TERM = SIGTERM; const int expected_signal_KILL = SIGKILL; - gpid = test_nm_utils_kill_child_create_and_join_pgroup (); - test_nm_utils_kill_child_spawn (argv_watchdog, FALSE); pid1s_1 = test_nm_utils_kill_child_spawn (argv1, TRUE); @@ -284,92 +276,123 @@ test_nm_utils_kill_child (void) /* give processes time to start (and potentially block signals) ... */ g_usleep (G_USEC_PER_SEC / 10); - fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-1-1' (*): waiting up to 3000 milliseconds for process to terminate normally after sending SIGTERM (15)..."); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-1-1' (*): after sending SIGTERM (15), process * exited by signal 15 (* usec elapsed)"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-1-1' (*): waiting up to 3000 milliseconds for process to terminate normally after sending SIGTERM (15)..."); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-1-1' (*): after sending SIGTERM (15), process * exited by signal 15 (* usec elapsed)"); test_nm_utils_kill_child_sync_do ("test-s-1-1", pid1s_1, SIGTERM, 3000, TRUE, &expected_signal_TERM); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-1-2' (*): waiting for process to terminate after sending SIGKILL (9)..."); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-1-2' (*): after sending SIGKILL (9), process * exited by signal 9 (* usec elapsed)"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-1-2' (*): waiting for process to terminate after sending SIGKILL (9)..."); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-1-2' (*): after sending SIGKILL (9), process * exited by signal 9 (* usec elapsed)"); test_nm_utils_kill_child_sync_do ("test-s-1-2", pid1s_2, SIGKILL, 1000 / 2, TRUE, &expected_signal_KILL); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-1-3' (*): waiting up to 1 milliseconds for process to terminate normally after sending no signal (0)..."); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-1-3' (*): sending SIGKILL..."); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-1-3' (*): after sending no signal (0) and SIGKILL, process * exited by signal 9 (* usec elapsed)"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-1-3' (*): waiting up to 1 milliseconds for process to terminate normally after sending no signal (0)..."); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-1-3' (*): sending SIGKILL..."); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-1-3' (*): after sending no signal (0) and SIGKILL, process * exited by signal 9 (* usec elapsed)"); test_nm_utils_kill_child_sync_do ("test-s-1-3", pid1s_3, 0, 1, TRUE, &expected_signal_KILL); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-2' (*): process * already terminated normally with status 47"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-2' (*): process * already terminated normally with status 47"); test_nm_utils_kill_child_sync_do ("test-s-2", pid2s, SIGTERM, 3000, TRUE, &expected_exit_47); /* send invalid signal. */ - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*kill child process 'test-s-3-0' (*): failed to send Unexpected signal: Invalid argument (22)"); + NMTST_EXPECT_NM_ERROR ("kill child process 'test-s-3-0' (*): failed to send Unexpected signal: Invalid argument (22)"); test_nm_utils_kill_child_sync_do ("test-s-3-0", pid3s, -1, 0, FALSE, NULL); /* really kill pid3s */ - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-3-1' (*): waiting up to 3000 milliseconds for process to terminate normally after sending SIGTERM (15)..."); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-3-1' (*): after sending SIGTERM (15), process * exited normally with status 47 (* usec elapsed)"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-3-1' (*): waiting up to 3000 milliseconds for process to terminate normally after sending SIGTERM (15)..."); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-3-1' (*): after sending SIGTERM (15), process * exited normally with status 47 (* usec elapsed)"); test_nm_utils_kill_child_sync_do ("test-s-3-1", pid3s, SIGTERM, 3000, TRUE, &expected_exit_47); /* pid3s should not be a valid process, hence the call should fail. Note, that there * is a race here. */ - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*kill child process 'test-s-3-2' (*): failed due to unexpected return value -1 by waitpid (No child processes, 10) after sending no signal (0)"); + NMTST_EXPECT_NM_ERROR ("kill child process 'test-s-3-2' (*): failed due to unexpected return value -1 by waitpid (No child processes, 10) after sending no signal (0)"); test_nm_utils_kill_child_sync_do ("test-s-3-2", pid3s, 0, 0, FALSE, NULL); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-4' (*): waiting up to 1 milliseconds for process to terminate normally after sending SIGTERM (15)..."); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-4' (*): sending SIGKILL..."); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-4' (*): after sending SIGTERM (15) and SIGKILL, process * exited by signal 9 (* usec elapsed)"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-4' (*): waiting up to 1 milliseconds for process to terminate normally after sending SIGTERM (15)..."); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-4' (*): sending SIGKILL..."); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-4' (*): after sending SIGTERM (15) and SIGKILL, process * exited by signal 9 (* usec elapsed)"); test_nm_utils_kill_child_sync_do ("test-s-4", pid4s, SIGTERM, 1, TRUE, &expected_signal_KILL); - - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-1-1' (*): wait for process to terminate after sending SIGTERM (15) (send SIGKILL in 3000 milliseconds)..."); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-1-1' (*): terminated by signal 15 (* usec elapsed)"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-1-1' (*): wait for process to terminate after sending SIGTERM (15) (send SIGKILL in 3000 milliseconds)..."); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-1-1' (*): terminated by signal 15 (* usec elapsed)"); test_nm_utils_kill_child_async_do ("test-a-1-1", pid1a_1, SIGTERM, 3000, TRUE, &expected_signal_TERM); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-1-2' (*): wait for process to terminate after sending SIGKILL (9)..."); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-1-2' (*): terminated by signal 9 (* usec elapsed)"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-1-2' (*): wait for process to terminate after sending SIGKILL (9)..."); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-1-2' (*): terminated by signal 9 (* usec elapsed)"); test_nm_utils_kill_child_async_do ("test-a-1-2", pid1a_2, SIGKILL, 1000 / 2, TRUE, &expected_signal_KILL); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-1-3' (*): wait for process to terminate after sending no signal (0) (send SIGKILL in 1 milliseconds)..."); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-1-3' (*): process not terminated after * usec. Sending SIGKILL signal"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-1-3' (*): terminated by signal 9 (* usec elapsed)"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-1-3' (*): wait for process to terminate after sending no signal (0) (send SIGKILL in 1 milliseconds)..."); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-1-3' (*): process not terminated after * usec. Sending SIGKILL signal"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-1-3' (*): terminated by signal 9 (* usec elapsed)"); test_nm_utils_kill_child_async_do ("test-a-1-3", pid1a_3, 0, 1, TRUE, &expected_signal_KILL); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-2' (*): process * already terminated normally with status 47"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-2' (*): invoke callback: terminated normally with status 47"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-2' (*): process * already terminated normally with status 47"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-2' (*): invoke callback: terminated normally with status 47"); test_nm_utils_kill_child_async_do ("test-a-2", pid2a, SIGTERM, 3000, TRUE, &expected_exit_47); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*kill child process 'test-a-3-0' (*): unexpected error sending Unexpected signal: Invalid argument (22)"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-3-0' (*): invoke callback: killing child failed"); + NMTST_EXPECT_NM_ERROR ("kill child process 'test-a-3-0' (*): unexpected error sending Unexpected signal: Invalid argument (22)"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-3-0' (*): invoke callback: killing child failed"); /* coverity[negative_returns] */ test_nm_utils_kill_child_async_do ("test-a-3-0", pid3a, -1, 1000 / 2, FALSE, NULL); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-3-1' (*): wait for process to terminate after sending SIGTERM (15) (send SIGKILL in 3000 milliseconds)..."); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-3-1' (*): terminated normally with status 47 (* usec elapsed)"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-3-1' (*): wait for process to terminate after sending SIGTERM (15) (send SIGKILL in 3000 milliseconds)..."); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-3-1' (*): terminated normally with status 47 (* usec elapsed)"); test_nm_utils_kill_child_async_do ("test-a-3-1", pid3a, SIGTERM, 3000, TRUE, &expected_exit_47); /* pid3a should not be a valid process, hence the call should fail. Note, that there * is a race here. */ - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*kill child process 'test-a-3-2' (*): failed due to unexpected return value -1 by waitpid (No child processes, 10) after sending no signal (0)"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-3-2' (*): invoke callback: killing child failed"); + NMTST_EXPECT_NM_ERROR ("kill child process 'test-a-3-2' (*): failed due to unexpected return value -1 by waitpid (No child processes, 10) after sending no signal (0)"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-3-2' (*): invoke callback: killing child failed"); test_nm_utils_kill_child_async_do ("test-a-3-2", pid3a, 0, 0, FALSE, NULL); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-4' (*): wait for process to terminate after sending SIGTERM (15) (send SIGKILL in 1 milliseconds)..."); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-4' (*): process not terminated after * usec. Sending SIGKILL signal"); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-4' (*): terminated by signal 9 (* usec elapsed)"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-4' (*): wait for process to terminate after sending SIGTERM (15) (send SIGKILL in 1 milliseconds)..."); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-4' (*): process not terminated after * usec. Sending SIGKILL signal"); + NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-4' (*): terminated by signal 9 (* usec elapsed)"); test_nm_utils_kill_child_async_do ("test-a-4", pid4a, SIGTERM, 1, TRUE, &expected_signal_KILL); - err = setpgid (0, 0); - g_assert (err == 0); - - kill (-gpid, SIGKILL); - g_log_set_always_fatal (fatal_mask); g_test_assert_expected_messages (); } +static void +test_nm_utils_kill_child (void) +{ + int err; + int exit_status; + pid_t gpid; + pid_t child_pid; + + /* the tests spawns several processes, we want to clean them up + * by sending a SIGKILL to the process group. + * + * The current process might be a session leader, which prevents it from + * creating a new process group. Hence, first fork and let the child + * create a new process group, run the tests, and kill all pending + * processes. */ + child_pid = fork (); + g_assert (child_pid >= 0); + + if (child_pid == 0) { + gpid = do_test_nm_utils_kill_child_create_and_join_pgroup (); + + do_test_nm_utils_kill_child (); + + err = setpgid (0, 0); + g_assert (err == 0); + + kill (-gpid, SIGKILL); + + exit (0); + }; + + do { + err = waitpid (child_pid, &exit_status, 0); + } while (err == -1 && errno == EINTR); + g_assert (err == child_pid); + g_assert (WIFEXITED (exit_status) && WEXITSTATUS(exit_status) == 0); +} + /*****************************************************************************/ static void @@ -432,7 +455,7 @@ test_nm_utils_array_remove_at_indexes (void) idx = g_array_new (FALSE, FALSE, sizeof (guint)); array = g_array_new (FALSE, FALSE, sizeof (gssize)); - unique = g_hash_table_new (NULL, NULL); + unique = g_hash_table_new (nm_direct_hash, NULL); for (i_len = 1; i_len < 20; i_len++) { for (i_idx_len = 1; i_idx_len <= i_len; i_idx_len++) { for (i_rnd = 0; i_rnd < 20; i_rnd++) { diff --git a/src/tests/test-general.c b/src/tests/test-general.c index bfa2ea73..4db05ac8 100644 --- a/src/tests/test-general.c +++ b/src/tests/test-general.c @@ -232,13 +232,13 @@ test_nm_utils_log_connection_diff (void) connection = nm_simple_connection_new (); nm_connection_add_setting (connection, nm_setting_connection_new ()); - nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test1", ">>> "); + nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test1", ">>> ", NULL); nm_connection_add_setting (connection, nm_setting_wired_new ()); - nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test2", ">>> "); + nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test2", ">>> ", NULL); connection2 = nm_simple_connection_new_clone (connection); - nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test3", ">>> "); + nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test3", ">>> ", NULL); g_object_set (nm_connection_get_setting_connection (connection), NM_SETTING_CONNECTION_ID, "id", @@ -248,24 +248,24 @@ test_nm_utils_log_connection_diff (void) NM_SETTING_CONNECTION_ID, "id2", NM_SETTING_CONNECTION_MASTER, "master2", NULL); - nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test4", ">>> "); + nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test4", ">>> ", NULL); nm_connection_add_setting (connection, nm_setting_802_1x_new ()); - nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test5", ">>> "); + nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test5", ">>> ", NULL); g_object_set (nm_connection_get_setting_802_1x (connection), NM_SETTING_802_1X_PASSWORD, "id2", NM_SETTING_802_1X_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED, NULL); - nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test6", ">>> "); - nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test7", ">>> "); - nm_utils_log_connection_diff (connection2, connection, LOGL_DEBUG, LOGD_CORE, "test8", ">>> "); + nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test6", ">>> ", NULL); + nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test7", ">>> ", NULL); + nm_utils_log_connection_diff (connection2, connection, LOGL_DEBUG, LOGD_CORE, "test8", ">>> ", NULL); g_clear_object (&connection); g_clear_object (&connection2); connection = nmtst_create_minimal_connection ("id-vpn-1", NULL, NM_SETTING_VPN_SETTING_NAME, NULL); - nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test-vpn-1", ">>> "); + nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test-vpn-1", ">>> ", NULL); g_clear_object (&connection); } @@ -709,7 +709,6 @@ test_connection_no_match_ip4_addr (void) NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); - s_ip4 = nm_connection_get_setting_ip4_config (orig); g_assert (s_ip4); g_object_set (G_OBJECT (s_ip4), @@ -1232,7 +1231,6 @@ _do_test_match_spec_config (const char *file, gint line, const char *spec_str, g GSList *specs2 = g_slist_append (NULL, sss); NMMatchSpecMatchType match_result2; - match_result2 = nm_match_spec_config (specs2, version, NULL); if (match_result == NM_MATCH_SPEC_NO_MATCH) g_assert_cmpint (match_result2, ==, NM_MATCH_SPEC_NO_MATCH); @@ -1289,7 +1287,6 @@ test_match_spec_config (void) do_test_match_spec_config ("nm-version-min:1", 1, 3, 30, NM_MATCH_SPEC_MATCH); do_test_match_spec_config ("nm-version-min:1", 1, 4, 30, NM_MATCH_SPEC_MATCH); - do_test_match_spec_config ("nm-version-max:1.2.3", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH); do_test_match_spec_config ("nm-version-max:1.2.3", 1, 1, 1, NM_MATCH_SPEC_NO_MATCH); do_test_match_spec_config ("nm-version-max:1.2.3", 1, 2, 0, NM_MATCH_SPEC_MATCH); @@ -1460,6 +1457,11 @@ test_nm_utils_strbuf_append (void) static void test_duplicate_decl_specifier (void) { + /* We're intentionally assigning values to static arrays v_const + * and v_result without using it afterwards just so that valgrind + * doesn't complain about the leak. */ + NM_PRAGMA_WARNING_DISABLE("-Wunused-but-set-variable") + /* have some static variables, so that the result is certainly not optimized out. */ static const int v_const[1] = { 1 }; static int v_result[1] = { }; @@ -1477,6 +1479,8 @@ test_duplicate_decl_specifier (void) }) v_result[0] = TEST_MAX (v_const[0], nmtst_get_rand_int () % 5) + v2; + + NM_PRAGMA_WARNING_REENABLE } static void @@ -1617,7 +1621,7 @@ do_test_stable_id_parse (const char *stable_id, else g_assert (stable_id); - stable_type = nm_utils_stable_id_parse (stable_id, "_CONNECTION", "_BOOT", &generated); + stable_type = nm_utils_stable_id_parse (stable_id, "_DEVICE", "_BOOT", "_CONNECTION", &generated); g_assert_cmpint (expected_stable_type, ==, stable_type); diff --git a/src/tests/test-ip4-config.c b/src/tests/test-ip4-config.c index 4c3c344e..9fea6af5 100644 --- a/src/tests/test-ip4-config.c +++ b/src/tests/test-ip4-config.c @@ -319,11 +319,15 @@ test_strip_search_trailing_dot (void) 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), ==, 3); + 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); } diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c index bcbeee3e..a03d89b0 100644 --- a/src/tests/test-ip6-config.c +++ b/src/tests/test-ip6-config.c @@ -246,8 +246,10 @@ test_nm_ip6_config_addresses_sort_check (NMIP6Config *config, NMSettingIP6Config int *idx = g_new (int, addr_count); nm_ip6_config_set_privacy (config, use_tempaddr); - copy = nmtst_ip6_config_clone (config); - copy2 = nmtst_ip6_config_clone (config); + copy = nm_ip6_config_clone (config); + g_assert (copy); + copy2 = nm_ip6_config_clone (config); + g_assert (copy2); /* initialize the array of indeces, and keep shuffling them for every @repeat iteration. */ for (i = 0; i < addr_count; i++) @@ -296,9 +298,9 @@ test_nm_ip6_config_addresses_sort (void) 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("2607:f0d0:1002:51::3", NULL, 64, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, IFA_F_TEMPORARY); + ADDR_ADD("2607:f0d0:1002:51::8", NULL, 64, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, IFA_F_TEMPORARY); + ADDR_ADD("2607:f0d0:1002:51::0", NULL, 64, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, IFA_F_TEMPORARY); 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); @@ -309,11 +311,11 @@ test_nm_ip6_config_addresses_sort (void) 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::3", NULL, 64, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, IFA_F_TEMPORARY); 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::8", NULL, 64, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, IFA_F_TEMPORARY); + ADDR_ADD("2607:f0d0:1002:51::0", NULL, 64, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, IFA_F_TEMPORARY); 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); @@ -338,11 +340,15 @@ test_strip_search_trailing_dot (void) 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), ==, 3); + 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); } diff --git a/src/tests/test-resolvconf-capture.c b/src/tests/test-resolvconf-capture.c deleted file mode 100644 index 2c34ff74..00000000 --- a/src/tests/test-resolvconf-capture.c +++ /dev/null @@ -1,298 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2013 Red Hat, Inc. - * - */ - -#include "nm-default.h" - -#include -#include - -#include "NetworkManagerUtils.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" -#include "platform/nm-platform.h" - -#include "nm-test-utils-core.h" - -static void -test_capture_empty (void) -{ - GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32)); - GArray *ns6 = g_array_new (FALSE, FALSE, sizeof (struct in6_addr)); - - g_assert (!nm_utils_resolve_conf_parse (AF_INET, "", ns4, NULL)); - g_assert_cmpint (ns4->len, ==, 0); - - g_assert (!nm_utils_resolve_conf_parse (AF_INET6, "", ns6, NULL)); - g_assert_cmpint (ns6->len, ==, 0); - - g_array_free (ns4, TRUE); - g_array_free (ns6, TRUE); -} - -#define assert_dns4_entry(a, i, s) \ - g_assert_cmpint ((g_array_index ((a), guint32, (i))), ==, nmtst_inet4_from_string (s)); - -#define assert_dns6_entry(a, i, s) \ - g_assert (IN6_ARE_ADDR_EQUAL (&g_array_index ((a), struct in6_addr, (i)), nmtst_inet6_from_string (s))) - -#define assert_dns_option(a, i, s) \ - g_assert_cmpstr ((a)->pdata[(i)], ==, (s)); - -static void -test_capture_basic4 (void) -{ - GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32)); - const char *rc = -"# neato resolv.conf\r\n" -"domain foobar.com\r\n" -"search foobar.com\r\n" -"nameserver 4.2.2.1\r\n" -"nameserver 4.2.2.2\r\n"; - - g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, NULL)); - g_assert_cmpint (ns4->len, ==, 2); - assert_dns4_entry (ns4, 0, "4.2.2.1"); - assert_dns4_entry (ns4, 1, "4.2.2.2"); - - g_array_free (ns4, TRUE); -} - -static void -test_capture_dup4 (void) -{ - GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32)); - const char *rc = -"# neato resolv.conf\r\n" -"domain foobar.com\r\n" -"search foobar.com\r\n" -"nameserver 4.2.2.1\r\n" -"nameserver 4.2.2.1\r\n" -"nameserver 4.2.2.2\r\n"; - - /* Check that duplicates are ignored */ - g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, NULL)); - g_assert_cmpint (ns4->len, ==, 2); - assert_dns4_entry (ns4, 0, "4.2.2.1"); - assert_dns4_entry (ns4, 1, "4.2.2.2"); - - g_array_free (ns4, TRUE); -} - -static void -test_capture_basic6 (void) -{ - GArray *ns6 = g_array_new (FALSE, FALSE, sizeof (struct in6_addr)); - const char *rc = -"# neato resolv.conf\r\n" -"domain foobar.com\r\n" -"search foobar.com\r\n" -"nameserver 2001:4860:4860::8888\r\n" -"nameserver 2001:4860:4860::8844\r\n"; - - g_assert (nm_utils_resolve_conf_parse (AF_INET6, rc, ns6, NULL)); - g_assert_cmpint (ns6->len, ==, 2); - assert_dns6_entry (ns6, 0, "2001:4860:4860::8888"); - assert_dns6_entry (ns6, 1, "2001:4860:4860::8844"); - - g_array_free (ns6, TRUE); -} - -static void -test_capture_dup6 (void) -{ - GArray *ns6 = g_array_new (FALSE, FALSE, sizeof (struct in6_addr)); - const char *rc = -"# neato resolv.conf\r\n" -"domain foobar.com\r\n" -"search foobar.com\r\n" -"nameserver 2001:4860:4860::8888\r\n" -"nameserver 2001:4860:4860::8888\r\n" -"nameserver 2001:4860:4860::8844\r\n"; - - /* Check that duplicates are ignored */ - g_assert (nm_utils_resolve_conf_parse (AF_INET6, rc, ns6, NULL)); - g_assert_cmpint (ns6->len, ==, 2); - assert_dns6_entry (ns6, 0, "2001:4860:4860::8888"); - assert_dns6_entry (ns6, 1, "2001:4860:4860::8844"); - - g_array_free (ns6, TRUE); -} - -static void -test_capture_addr4_with_6 (void) -{ - GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32)); - const char *rc = -"# neato resolv.conf\r\n" -"domain foobar.com\r\n" -"search foobar.com\r\n" -"nameserver 4.2.2.1\r\n" -"nameserver 4.2.2.2\r\n" -"nameserver 2001:4860:4860::8888\r\n"; - - g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, NULL)); - g_assert_cmpint (ns4->len, ==, 2); - assert_dns4_entry (ns4, 0, "4.2.2.1"); - assert_dns4_entry (ns4, 1, "4.2.2.2"); - - g_array_free (ns4, TRUE); -} - -static void -test_capture_addr6_with_4 (void) -{ - GArray *ns6 = g_array_new (FALSE, FALSE, sizeof (struct in6_addr)); - const char *rc = -"# neato resolv.conf\r\n" -"domain foobar.com\r\n" -"search foobar.com\r\n" -"nameserver 4.2.2.1\r\n" -"nameserver 2001:4860:4860::8888\r\n" -"nameserver 2001:4860:4860::8844\r\n"; - - g_assert (nm_utils_resolve_conf_parse (AF_INET6, rc, ns6, NULL)); - g_assert_cmpint (ns6->len, ==, 2); - assert_dns6_entry (ns6, 0, "2001:4860:4860::8888"); - assert_dns6_entry (ns6, 1, "2001:4860:4860::8844"); - - g_array_free (ns6, TRUE); -} - -static void -test_capture_format (void) -{ - GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32)); - const char *rc = -" nameserver 4.2.2.1\r\n" /* bad */ -"nameserver4.2.2.1\r\n" /* bad */ -"nameserver 4.2.2.3\r" /* good */ -"nameserver\t\t4.2.2.4\r\n" /* good */ -"nameserver 4.2.2.5\t\t\r\n" /* good */ -"nameserver 4.2.2.6 \r\n"; /* good */ - - g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, NULL)); - g_assert_cmpint (ns4->len, ==, 4); - assert_dns4_entry (ns4, 0, "4.2.2.3"); - assert_dns4_entry (ns4, 1, "4.2.2.4"); - assert_dns4_entry (ns4, 2, "4.2.2.5"); - assert_dns4_entry (ns4, 3, "4.2.2.6"); - - g_array_free (ns4, TRUE); -} - -static void -test_capture_dns_options (void) -{ - GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32)); - GPtrArray *dns_options = g_ptr_array_new_with_free_func (g_free); - const char *rc = -"nameserver 4.2.2.1\r\n" -"options debug rotate timeout:5 \r\n" -"options edns0\r\n"; - - g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, dns_options)); - g_assert_cmpint (dns_options->len, ==, 4); - assert_dns_option (dns_options, 0, "debug"); - assert_dns_option (dns_options, 1, "rotate"); - assert_dns_option (dns_options, 2, "timeout:5"); - assert_dns_option (dns_options, 3, "edns0"); - - g_array_free (ns4, TRUE); - g_ptr_array_free (dns_options, TRUE); -} - -static void -test_capture_dns_options_dup (void) -{ - GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32)); - GPtrArray *dns_options = g_ptr_array_new_with_free_func (g_free); - const char *rc = -"options debug rotate timeout:3\r\n" -"options edns0 debug\r\n" -"options timeout:5\r\n"; - - g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, dns_options)); - g_assert_cmpint (dns_options->len, ==, 4); - assert_dns_option (dns_options, 0, "debug"); - assert_dns_option (dns_options, 1, "rotate"); - assert_dns_option (dns_options, 2, "timeout:3"); - assert_dns_option (dns_options, 3, "edns0"); - - g_array_free (ns4, TRUE); - g_ptr_array_free (dns_options, TRUE); -} - -static void -test_capture_dns_options_valid4 (void) -{ - GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32)); - GPtrArray *dns_options = g_ptr_array_new_with_free_func (g_free); - const char *rc = -"options debug: rotate:yes edns0 foobar : inet6\r\n"; - - g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, dns_options)); - g_assert_cmpint (dns_options->len, ==, 1); - assert_dns_option (dns_options, 0, "edns0"); - - g_array_free (ns4, TRUE); - g_ptr_array_free (dns_options, TRUE); -} - -static void -test_capture_dns_options_valid6 (void) -{ - GArray *ns6 = g_array_new (FALSE, FALSE, sizeof (struct in6_addr)); - GPtrArray *dns_options = g_ptr_array_new_with_free_func (g_free); - const char *rc = -"options inet6 debug foobar rotate:\r\n"; - - g_assert (nm_utils_resolve_conf_parse (AF_INET6, rc, ns6, dns_options)); - g_assert_cmpint (dns_options->len, ==, 2); - assert_dns_option (dns_options, 0, "inet6"); - assert_dns_option (dns_options, 1, "debug"); - - g_array_free (ns6, TRUE); - g_ptr_array_free (dns_options, TRUE); -} -/*****************************************************************************/ - -NMTST_DEFINE (); - -int -main (int argc, char **argv) -{ - nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); - - g_test_add_func ("/resolvconf-capture/empty", test_capture_empty); - g_test_add_func ("/resolvconf-capture/basic4", test_capture_basic4); - g_test_add_func ("/resolvconf-capture/dup4", test_capture_dup4); - g_test_add_func ("/resolvconf-capture/basic6", test_capture_basic6); - g_test_add_func ("/resolvconf-capture/dup6", test_capture_dup6); - g_test_add_func ("/resolvconf-capture/addr4-with-6", test_capture_addr4_with_6); - g_test_add_func ("/resolvconf-capture/addr6-with-4", test_capture_addr6_with_4); - g_test_add_func ("/resolvconf-capture/format", test_capture_format); - g_test_add_func ("/resolvconf-capture/dns-options", test_capture_dns_options); - g_test_add_func ("/resolvconf-capture/dns-options-dup", test_capture_dns_options_dup); - g_test_add_func ("/resolvconf-capture/dns-options-valid4", test_capture_dns_options_valid4); - g_test_add_func ("/resolvconf-capture/dns-options-valid6", test_capture_dns_options_valid6); - - return g_test_run (); -} - diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index 12ee0059..ad752c4c 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -51,8 +51,6 @@ #include "nm-vpn-manager.h" #include "dns/nm-dns-manager.h" -#include "introspection/org.freedesktop.NetworkManager.VPN.Connection.h" - typedef enum { /* Only system secrets */ SECRETS_REQ_SYSTEM = 0, @@ -82,7 +80,6 @@ typedef enum { } VpnState; enum { - VPN_STATE_CHANGED, INTERNAL_STATE_CHANGED, INTERNAL_RETRY_AFTER_FAILURE, @@ -159,19 +156,6 @@ struct _NMVpnConnection { struct _NMVpnConnectionClass { NMActiveConnectionClass parent; - - /* Signals */ - void (*vpn_state_changed) (NMVpnConnection *self, - NMVpnConnectionState new_state, - NMActiveConnectionStateReason reason); - - /* not exported over D-Bus */ - void (*internal_state_changed) (NMVpnConnection *self, - NMVpnConnectionState new_state, - NMVpnConnectionState old_state, - NMActiveConnectionStateReason reason); - - void (*internal_failed_retry) (NMVpnConnection *self); }; G_DEFINE_TYPE (NMVpnConnection, nm_vpn_connection, NM_TYPE_ACTIVE_CONNECTION) @@ -180,6 +164,9 @@ G_DEFINE_TYPE (NMVpnConnection, nm_vpn_connection, NM_TYPE_ACTIVE_CONNECTION) /*****************************************************************************/ +static const NMDBusInterfaceInfoExtended interface_info_vpn_connection; +static const GDBusSignalInfo signal_info_vpn_state_changed; + static NMSettingsConnection *_get_settings_connection (NMVpnConnection *self, gboolean allow_missing); @@ -511,7 +498,12 @@ _set_vpn_state (NMVpnConnection *self, old_external_state = _state_to_nm_vpn_state (old_vpn_state); new_external_state = _state_to_nm_vpn_state (priv->vpn_state); if (new_external_state != old_external_state) { - g_signal_emit (self, signals[VPN_STATE_CHANGED], 0, new_external_state, reason); + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), + &interface_info_vpn_connection, + &signal_info_vpn_state_changed, + "(uu)", + (guint32) new_external_state, + (guint32) reason); g_signal_emit (self, signals[INTERNAL_STATE_CHANGED], 0, new_external_state, old_external_state, @@ -863,6 +855,7 @@ nm_vpn_connection_new (NMSettingsConnection *settings_connection, { 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 (NMVpnConnection *) g_object_new (NM_TYPE_VPN_CONNECTION, NM_ACTIVE_CONNECTION_INT_SETTINGS_CONNECTION, settings_connection, @@ -1029,7 +1022,7 @@ print_vpn_config (NMVpnConnection *self) 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 ? dns_domain : "(none)"); + _LOGI ("Data: DNS Domain: '%s'", dns_domain ?: "(none)"); } else _LOGI ("Data: No IPv4 configuration"); @@ -1063,7 +1056,7 @@ print_vpn_config (NMVpnConnection *self) 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 ? dns_domain : "(none)"); + _LOGI ("Data: DNS Domain: '%s'", dns_domain ?: "(none)"); } else _LOGI ("Data: No IPv6 configuration"); @@ -1178,8 +1171,8 @@ _cleanup_failed_config (NMVpnConnection *self) { NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); - nm_exported_object_clear_and_unexport (&priv->ip4_config); - nm_exported_object_clear_and_unexport (&priv->ip6_config); + 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); @@ -1396,12 +1389,12 @@ nm_vpn_connection_config_get (NMVpnConnection *self, GVariant *dict) priv->has_ip4 = FALSE; if (g_variant_lookup (dict, NM_VPN_PLUGIN_CONFIG_HAS_IP4, "b", &b)) priv->has_ip4 = b; - nm_exported_object_clear_and_unexport (&priv->ip4_config); + 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_exported_object_clear_and_unexport (&priv->ip6_config); + nm_dbus_object_clear_and_unexport (&priv->ip6_config); nm_vpn_connection_config_maybe_complete (self, TRUE); } @@ -1462,6 +1455,7 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) NMPlatformIP4Address address; guint32 u32, route_metric; NMSettingIPConfig *s_ip; + NMSettingConnection *s_con; guint32 route_table; NMIP4Config *config; GVariantIter *iter; @@ -1566,6 +1560,7 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) 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 (nm_setting_ip_config_get_ignore_auto_routes (s_ip)) { /* ignore VPN routes */ @@ -1623,6 +1618,7 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) /* 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), route_table, route_metric); @@ -1652,7 +1648,7 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) g_object_unref (config); } else { priv->ip4_config = config; - nm_exported_object_export (NM_EXPORTED_OBJECT (config)); + nm_dbus_object_export (NM_DBUS_OBJECT (config)); g_object_notify ((GObject *) self, NM_ACTIVE_CONNECTION_IP4_CONFIG); } @@ -1841,7 +1837,7 @@ next: g_object_unref (config); } else { priv->ip6_config = config; - nm_exported_object_export (NM_EXPORTED_OBJECT (config)); + nm_dbus_object_export (NM_DBUS_OBJECT (config)); g_object_notify ((GObject *) self, NM_ACTIVE_CONNECTION_IP6_CONFIG); } @@ -2147,7 +2143,6 @@ _name_owner_changed (GObject *object, g_free (owner); } - static gboolean _daemon_exec_timeout (gpointer data) { @@ -2328,7 +2323,7 @@ nm_vpn_connection_activate (NMVpnConnection *self, if (nm_vpn_plugin_info_supports_multiple (plugin_info)) { const char *path; - path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self)); + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (self)); if (path) path = strrchr (path, '/'); g_return_if_fail (path); @@ -2551,7 +2546,6 @@ static void plugin_new_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; @@ -2560,7 +2554,6 @@ plugin_new_secrets_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_da return; self = NM_VPN_CONNECTION (user_data); - priv = NM_VPN_CONNECTION_GET_PRIVATE (self); if (error) { g_dbus_error_strip_remote_error (error); @@ -2780,8 +2773,8 @@ dispose (GObject *object) nm_clear_g_cancellable (&priv->cancellable); g_clear_object (&priv->proxy_config); - nm_exported_object_clear_and_unexport (&priv->ip4_config); - nm_exported_object_clear_and_unexport (&priv->ip6_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); @@ -2828,17 +2821,17 @@ get_property (GObject *object, guint prop_id, g_value_set_uint (value, _state_to_nm_vpn_state (priv->vpn_state)); break; case PROP_BANNER: - g_value_set_string (value, priv->banner ? priv->banner : ""); + g_value_set_string (value, priv->banner ?: ""); break; case PROP_IP4_CONFIG: - nm_utils_g_value_set_object_path (value, ip_config_valid (priv->vpn_state) ? priv->ip4_config : NULL); + 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_utils_g_value_set_object_path (value, ip_config_valid (priv->vpn_state) ? priv->ip6_config : NULL); + 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_utils_g_value_set_object_path (value, parent_dev); + nm_dbus_utils_g_value_set_object_path (value, parent_dev); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -2846,16 +2839,42 @@ get_property (GObject *object, guint prop_id, } } +static const GDBusSignalInfo signal_info_vpn_state_changed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "VpnStateChanged", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("state", "u"), + NM_DEFINE_GDBUS_ARG_INFO ("reason", "u"), + ), +); + +static const NMDBusInterfaceInfoExtended interface_info_vpn_connection = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_VPN_CONNECTION, + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_property_changed_legacy, + &signal_info_vpn_state_changed, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("VpnState", "u", NM_VPN_CONNECTION_VPN_STATE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Banner", "s", NM_VPN_CONNECTION_BANNER), + ), + ), + .legacy_property_changed = TRUE, +}; + static void nm_vpn_connection_class_init (NMVpnConnectionClass *connection_class) { 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); + + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_vpn_connection); - /* virtual methods */ object_class->get_property = get_property; object_class->dispose = dispose; object_class->finalize = finalize; + active_class->device_state_changed = device_state_changed; active_class->device_changed = device_changed; @@ -2882,13 +2901,6 @@ nm_vpn_connection_class_init (NMVpnConnectionClass *connection_class) g_object_class_override_property (object_class, PROP_IP6_CONFIG, NM_ACTIVE_CONNECTION_IP6_CONFIG); - signals[VPN_STATE_CHANGED] = - g_signal_new ("vpn-state-changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 2, 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), @@ -2902,8 +2914,4 @@ nm_vpn_connection_class_init (NMVpnConnectionClass *connection_class) G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); - - nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (connection_class), - NMDBUS_TYPE_VPN_CONNECTION_SKELETON, - NULL); } diff --git a/src/vpn/nm-vpn-connection.h b/src/vpn/nm-vpn-connection.h index b287c334..3046634d 100644 --- a/src/vpn/nm-vpn-connection.h +++ b/src/vpn/nm-vpn-connection.h @@ -42,7 +42,6 @@ #define NM_VPN_CONNECTION_BANNER "banner" /* Signals */ -/* not exported: includes old reason code */ #define NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED "internal-state-changed" #define NM_VPN_CONNECTION_INTERNAL_RETRY_AFTER_FAILURE "internal-retry-after-failure" -- cgit 1.3.0-6-gf8a5