From f60117b41d5433be1b4a96d82cd11d0c3dce9b63 Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Mon, 23 Apr 2018 18:00:21 +0200 Subject: New upstream version 1.11.3 --- src/NetworkManagerUtils.c | 185 +- src/NetworkManagerUtils.h | 21 +- src/devices/adsl/meson.build | 34 + src/devices/adsl/nm-device-adsl.c | 46 +- src/devices/bluetooth/meson.build | 45 + src/devices/bluetooth/nm-bluez-device.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 | 489 +++ 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 | 26 +- src/devices/nm-device-bridge.c | 26 +- src/devices/nm-device-dummy.c | 24 +- src/devices/nm-device-ethernet.c | 90 +- 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 | 59 +- src/devices/nm-device-macsec.h | 3 - src/devices/nm-device-macvlan.c | 42 +- src/devices/nm-device-macvlan.h | 3 - src/devices/nm-device-ppp.c | 93 +- src/devices/nm-device-private.h | 6 +- src/devices/nm-device-tun.c | 355 +- src/devices/nm-device-veth.c | 41 +- src/devices/nm-device-vlan.c | 29 +- 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 | 4030 ++++++++++++-------- src/devices/nm-device.h | 42 +- 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 | 21 +- src/devices/ovs/nm-ovsdb.c | 31 +- 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 | 140 - src/devices/tests/test-lldp.c | 52 +- 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 | 38 +- src/devices/wifi/nm-device-wifi.c | 381 +- src/devices/wifi/nm-device-wifi.h | 12 +- src/devices/wifi/nm-iwd-manager.c | 694 ++++ 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 | 205 + 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 | 65 +- src/devices/wwan/nm-modem-ofono.c | 26 +- src/devices/wwan/nm-modem.c | 294 +- src/devices/wwan/nm-modem.h | 19 +- src/devices/wwan/nm-wwan-factory.c | 8 +- src/dhcp/meson.build | 22 + src/dhcp/nm-dhcp-client.c | 119 +- src/dhcp/nm-dhcp-client.h | 49 +- src/dhcp/nm-dhcp-dhclient-utils.c | 330 +- src/dhcp/nm-dhcp-dhclient-utils.h | 15 +- src/dhcp/nm-dhcp-dhclient.c | 59 +- src/dhcp/nm-dhcp-dhcpcanon.c | 14 +- src/dhcp/nm-dhcp-dhcpcd.c | 17 +- src/dhcp/nm-dhcp-helper.c | 2 - src/dhcp/nm-dhcp-listener.c | 84 +- src/dhcp/nm-dhcp-manager.c | 118 +- src/dhcp/nm-dhcp-manager.h | 15 +- src/dhcp/nm-dhcp-systemd.c | 115 +- 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 | 318 +- src/dhcp/tests/test-dhcp-utils.c | 21 +- src/dns/nm-dns-dnsmasq.c | 344 +- src/dns/nm-dns-manager.c | 819 ++-- src/dns/nm-dns-manager.h | 33 +- src/dns/nm-dns-plugin.c | 4 +- src/dns/nm-dns-plugin.h | 4 +- src/dns/nm-dns-systemd-resolved.c | 289 +- src/dns/nm-dns-unbound.c | 2 +- src/dnsmasq/nm-dnsmasq-manager.c | 2 +- src/dnsmasq/tests/meson.build | 14 + src/main.c | 47 +- 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 | 13 +- src/nm-act-request.h | 1 + src/nm-active-connection.c | 409 +- src/nm-active-connection.h | 22 +- src/nm-audit-manager.c | 2 +- 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 | 4 - 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 | 292 +- src/nm-checkpoint.h | 29 +- src/nm-config-data.c | 307 +- src/nm-config-data.h | 24 +- src/nm-config.c | 20 +- src/nm-config.h | 3 +- src/nm-connectivity.c | 600 +-- src/nm-connectivity.h | 29 +- src/nm-core-utils.c | 278 +- src/nm-core-utils.h | 36 +- src/nm-dbus-manager.c | 1626 ++++++++ src/nm-dbus-manager.h | 107 + src/nm-dbus-object.c | 320 ++ src/nm-dbus-object.h | 202 + src/nm-dbus-utils.c | 312 ++ src/nm-dbus-utils.h | 212 + src/nm-dhcp4-config.c | 34 +- src/nm-dhcp6-config.c | 34 +- src/nm-dispatcher.c | 13 +- src/nm-exported-object.c | 1055 ----- src/nm-exported-object.h | 85 - src/nm-firewall-manager.c | 2 +- src/nm-iface-helper.c | 115 +- src/nm-ip4-config.c | 212 +- src/nm-ip4-config.h | 200 +- src/nm-ip6-config.c | 216 +- src/nm-ip6-config.h | 10 +- src/nm-logging.c | 7 +- src/nm-logging.h | 12 +- src/nm-manager.c | 2525 ++++++------ src/nm-manager.h | 37 +- src/nm-netns.c | 4 - src/nm-pacrunner-manager.c | 15 +- src/nm-policy.c | 178 +- src/nm-session-monitor.c | 7 +- src/nm-test-utils-core.h | 32 +- src/nm-types.h | 23 +- src/org.freedesktop.NetworkManager.conf | 2 +- src/platform/nm-fake-platform.c | 1 - src/platform/nm-linux-platform.c | 779 ++-- src/platform/nm-netlink.c | 1491 ++++++++ src/platform/nm-netlink.h | 511 +++ src/platform/nm-platform-utils.c | 3 + src/platform/nm-platform.c | 644 +++- src/platform/nm-platform.h | 117 +- src/platform/nmp-netns.c | 4 +- src/platform/nmp-object.c | 94 +- src/platform/nmp-object.h | 34 +- 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 | 116 +- src/platform/tests/test-common.h | 30 +- src/platform/tests/test-link.c | 135 +- src/platform/tests/test-nmp-object.c | 27 +- src/platform/tests/test-route.c | 3 +- src/platform/wifi/wifi-utils-nl80211.c | 372 +- 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 | 2 +- src/ppp/meson.build | 41 + src/ppp/nm-ppp-manager-call.c | 2 - src/ppp/nm-ppp-manager.c | 302 +- src/ppp/nm-ppp-manager.h | 1 + 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 | 664 ++-- src/settings/nm-settings-connection.h | 61 +- src/settings/nm-settings.c | 769 ++-- src/settings/nm-settings.h | 10 - src/settings/plugins/ibft/meson.build | 48 + src/settings/plugins/ibft/tests/meson.build | 22 + src/settings/plugins/ibft/tests/test-ibft.c | 2 +- src/settings/plugins/ifcfg-rh/meson.build | 77 + src/settings/plugins/ifcfg-rh/nm-inotify-helper.c | 2 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c | 134 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 162 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 66 +- src/settings/plugins/ifcfg-rh/shvar.c | 4 +- src/settings/plugins/ifcfg-rh/tests/meson.build | 22 + .../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 | 195 +- .../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 | 6 +- src/settings/plugins/ifupdown/tests/meson.build | 17 + .../plugins/ifupdown/tests/test-ifupdown.c | 2 +- src/settings/plugins/keyfile/nms-keyfile-plugin.c | 2 +- .../keyfile/tests/keyfiles/Test_Wired_Connection | 10 +- src/settings/plugins/keyfile/tests/meson.build | 21 + src/settings/plugins/keyfile/tests/test-keyfile.c | 88 +- src/settings/plugins/meson.build | 15 + src/supplicant/nm-supplicant-config.c | 71 +- 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 | 9 +- src/supplicant/nm-supplicant-types.h | 6 +- src/supplicant/tests/meson.build | 14 + src/supplicant/tests/test-supplicant-config.c | 196 +- src/systemd/meson.build | 70 + 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 | 23 +- src/systemd/sd-adapt/procfs-util.h | 3 + src/systemd/sd-adapt/terminal-util.h | 3 + src/systemd/src/basic/alloc-util.c | 3 +- src/systemd/src/basic/alloc-util.h | 16 +- src/systemd/src/basic/async.h | 3 +- src/systemd/src/basic/escape.c | 1 + src/systemd/src/basic/escape.h | 1 + src/systemd/src/basic/ether-addr-util.c | 6 +- src/systemd/src/basic/ether-addr-util.h | 1 + src/systemd/src/basic/extract-word.c | 4 +- src/systemd/src/basic/extract-word.h | 1 + src/systemd/src/basic/fd-util.c | 403 +- src/systemd/src/basic/fd-util.h | 39 +- src/systemd/src/basic/fileio.c | 156 +- src/systemd/src/basic/fileio.h | 15 +- src/systemd/src/basic/fs-util.c | 471 ++- src/systemd/src/basic/fs-util.h | 28 +- src/systemd/src/basic/hash-funcs.c | 57 + src/systemd/src/basic/hash-funcs.h | 24 +- src/systemd/src/basic/hashmap.c | 163 + src/systemd/src/basic/hashmap.h | 41 + src/systemd/src/basic/hexdecoct.c | 214 +- src/systemd/src/basic/hexdecoct.h | 1 + src/systemd/src/basic/hostname-util.c | 103 +- src/systemd/src/basic/hostname-util.h | 7 +- src/systemd/src/basic/in-addr-util.c | 3 + src/systemd/src/basic/in-addr-util.h | 1 + src/systemd/src/basic/io-util.c | 13 +- src/systemd/src/basic/io-util.h | 1 + src/systemd/src/basic/list.h | 4 + src/systemd/src/basic/log.h | 83 +- src/systemd/src/basic/macro.h | 57 +- src/systemd/src/basic/mempool.c | 1 + src/systemd/src/basic/mempool.h | 1 + src/systemd/src/basic/parse-util.c | 113 +- src/systemd/src/basic/parse-util.h | 21 +- src/systemd/src/basic/path-util.c | 85 +- src/systemd/src/basic/path-util.h | 15 +- src/systemd/src/basic/prioq.c | 3 +- src/systemd/src/basic/prioq.h | 1 + src/systemd/src/basic/process-util.c | 504 ++- src/systemd/src/basic/process-util.h | 77 +- src/systemd/src/basic/random-util.c | 6 +- src/systemd/src/basic/random-util.h | 1 + src/systemd/src/basic/refcnt.h | 1 + src/systemd/src/basic/set.h | 13 + src/systemd/src/basic/signal-util.h | 18 +- src/systemd/src/basic/socket-util.c | 163 +- src/systemd/src/basic/socket-util.h | 19 +- src/systemd/src/basic/sparse-endian.h | 4 +- src/systemd/src/basic/stdio-util.h | 7 +- src/systemd/src/basic/string-table.c | 1 + src/systemd/src/basic/string-table.h | 10 +- src/systemd/src/basic/string-util.c | 152 +- src/systemd/src/basic/string-util.h | 25 +- src/systemd/src/basic/strv.c | 62 +- src/systemd/src/basic/strv.h | 18 +- src/systemd/src/basic/time-util.c | 31 +- src/systemd/src/basic/time-util.h | 7 +- src/systemd/src/basic/umask-util.h | 1 + src/systemd/src/basic/utf8.c | 20 + src/systemd/src/basic/utf8.h | 3 + src/systemd/src/basic/util.c | 382 +- src/systemd/src/basic/util.h | 25 +- src/systemd/src/libsystemd-network/arp-util.c | 11 +- src/systemd/src/libsystemd-network/arp-util.h | 1 + .../src/libsystemd-network/dhcp-identifier.c | 1 + .../src/libsystemd-network/dhcp-identifier.h | 1 + src/systemd/src/libsystemd-network/dhcp-internal.h | 1 + .../src/libsystemd-network/dhcp-lease-internal.h | 3 + src/systemd/src/libsystemd-network/dhcp-network.c | 16 +- src/systemd/src/libsystemd-network/dhcp-option.c | 11 +- src/systemd/src/libsystemd-network/dhcp-packet.c | 1 + src/systemd/src/libsystemd-network/dhcp-protocol.h | 1 + .../src/libsystemd-network/dhcp6-internal.h | 67 +- .../src/libsystemd-network/dhcp6-lease-internal.h | 3 + src/systemd/src/libsystemd-network/dhcp6-network.c | 5 +- src/systemd/src/libsystemd-network/dhcp6-option.c | 347 +- .../src/libsystemd-network/dhcp6-protocol.h | 8 + src/systemd/src/libsystemd-network/lldp-internal.h | 1 + src/systemd/src/libsystemd-network/lldp-neighbor.c | 1 + src/systemd/src/libsystemd-network/lldp-neighbor.h | 1 + src/systemd/src/libsystemd-network/lldp-network.c | 6 +- src/systemd/src/libsystemd-network/lldp-network.h | 1 + .../src/libsystemd-network/network-internal.c | 30 +- .../src/libsystemd-network/network-internal.h | 4 +- .../src/libsystemd-network/sd-dhcp-client.c | 60 +- src/systemd/src/libsystemd-network/sd-dhcp-lease.c | 32 +- .../src/libsystemd-network/sd-dhcp6-client.c | 292 +- .../src/libsystemd-network/sd-dhcp6-lease.c | 48 +- src/systemd/src/libsystemd-network/sd-ipv4acd.c | 4 +- src/systemd/src/libsystemd-network/sd-ipv4ll.c | 2 + src/systemd/src/libsystemd-network/sd-lldp.c | 1 + src/systemd/src/libsystemd/sd-event/sd-event.c | 56 +- src/systemd/src/libsystemd/sd-id128/id128-util.c | 13 +- src/systemd/src/libsystemd/sd-id128/id128-util.h | 1 + src/systemd/src/libsystemd/sd-id128/sd-id128.c | 1 + src/systemd/src/shared/dns-domain.c | 32 +- src/systemd/src/shared/dns-domain.h | 2 + src/systemd/src/systemd/_sd-common.h | 1 + src/systemd/src/systemd/sd-dhcp-client.h | 6 + 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 | 26 + src/tests/config/nm-test-device.c | 2 +- src/tests/config/test-config.c | 39 +- src/tests/meson.build | 47 + src/tests/test-general-with-expect.c | 126 +- src/tests/test-general.c | 25 +- src/tests/test-ip6-config.c | 18 +- src/tests/test-resolvconf-capture.c | 298 -- src/vpn/nm-vpn-connection.c | 97 +- src/vpn/nm-vpn-connection.h | 2 +- 382 files changed, 26589 insertions(+), 22203 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/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..b208ffc6 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -31,7 +31,6 @@ #include "nm-core-internal.h" #include "platform/nm-platform.h" -#include "nm-exported-object.h" #include "nm-auth-utils.h" /*****************************************************************************/ @@ -68,98 +67,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; - id = nm_connection_get_id (candidate); - g_assert (id); - names = g_slist_append (names, (gpointer) id); + candidate = existing_connections[i]; + nm_assert (NM_IS_CONNECTION (candidate)); - if (preferred && !preferred_found && (strcmp (preferred, id) == 0)) - preferred_found = TRUE; + id = nm_connection_get_id (candidate); + nm_assert (id); + + existing_names[i] = id; + + 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 +245,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 +272,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 +874,37 @@ 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) -{ - g_return_if_fail (!object || NM_IS_EXPORTED_OBJECT (object)); - - 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, "/"); -} - -/** - * 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 - * - * Sets @value to an array of object paths of the objects in @objects. - */ -void -nm_utils_g_value_set_object_path_array (GValue *value, - GSList *objects, - NMUtilsObjectFunc filter_func, - gpointer user_data) +int +nm_match_spec_device_by_pllink (const NMPlatformLink *pllink, + const char *match_device_type, + const GSList *specs, + int no_match_value) { - char **paths; - guint i; - GSList *iter; - - 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; - - 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); + 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; } - paths[i] = NULL; - g_value_take_boxed (value, paths); + nm_assert_not_reached (); + return no_match_value; } -/*****************************************************************************/ + diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index e5f28b27..13bdb67e 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,10 @@ 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); - -/** - * NMUtilsObjectFunc: - * @object: the object to filter on - * @user_data: data passed to the function from the caller - * - * Returns: %TRUE if the object should be used, %FALSE if not - */ -typedef gboolean (*NMUtilsObjectFunc) (GObject *object, gpointer user_data); - -void nm_utils_g_value_set_object_path_array (GValue *value, - GSList *objects, - NMUtilsObjectFunc filter_func, - gpointer user_data); +int nm_match_spec_device_by_pllink (const NMPlatformLink *pllink, + const char *match_device_type, + const GSList *specs, + int no_match_value); /*****************************************************************************/ diff --git a/src/devices/adsl/meson.build b/src/devices/adsl/meson.build new file mode 100644 index 00000000..4b0fade0 --- /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_pkglibdir +) + +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-device-adsl.c b/src/devices/adsl/nm-device-adsl.c index e9bd41ae..91331376 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; } @@ -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); @@ -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..eb200679 --- /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_pkglibdir +) + +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..cc9e38c8 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; @@ -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-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..1bade4ff --- /dev/null +++ b/src/devices/nm-acd-manager.c @@ -0,0 +1,489 @@ +/* -*- 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; + } + + _LOGD ("start probe for %s", nm_utils_inet4_ntop (info->address, NULL)); + + 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..2dd9494a 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; @@ -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..f8bc8e75 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,10 +185,6 @@ 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); } diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 3ca86e06..9b46545b 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); @@ -1361,7 +1373,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 +1437,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 +1484,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 +1735,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 +1768,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 +1803,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..895ea34f 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; } @@ -812,10 +811,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 +848,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 +913,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..b8e748d6 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; @@ -470,10 +473,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 +501,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 +535,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..94df0cae 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, @@ -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..b0d3ffa4 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -45,6 +45,8 @@ enum NMActStageReturn { 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 +59,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 +141,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..c3ce4b73 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,10 +478,6 @@ 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); } @@ -467,42 +493,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..ae6a0f36 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); @@ -134,7 +132,7 @@ parent_hwaddr_maybe_changed (NMDevice *parent, */ 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 (NM_DEVICE (self), s_ip6, s_ip6); } } @@ -381,7 +379,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 +575,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 +625,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 b8828d14..815de29a 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,14 +80,10 @@ #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 DHCP_RESTART_TIMEOUT 120 -#define DHCP_NUM_TRIES_MAX 3 #define DEFAULT_AUTOCONNECT TRUE +#define DHCP_GRACE_PERIOD_SEC 480 #define CARRIER_WAIT_TIME_MS 6000 #define CARRIER_WAIT_TIME_AFTER_MTU_MS 10000 @@ -131,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, @@ -153,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 { @@ -166,6 +182,7 @@ enum { REMOVED, RECHECK_AUTO_ACTIVATE, RECHECK_ASSUME, + CONNECTIVITY_CHANGED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -230,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 */ @@ -259,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; @@ -298,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; @@ -357,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; @@ -367,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; @@ -387,10 +455,9 @@ typedef struct _NMDevicePrivate { NMDhcpClient * client; gulong state_sigid; NMDhcp4Config * config; - guint restart_id; - guint num_tries_left; char * pac_url; bool was_active; + guint grace_id; } dhcp4; struct { @@ -398,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; @@ -420,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; @@ -443,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; @@ -458,21 +518,18 @@ 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 restart_id; - guint num_tries_left; guint needed_prefixes; bool was_active; + guint grace_id; } dhcp6; gboolean needs_ip6_subnet; /* master interface for bridge/bond/team slave */ NMDevice * master; - bool is_enslaved; - bool master_ready_handled; gulong master_ready_id; /* slave management */ @@ -485,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; - guint concheck_periodic_id; - guint64 concheck_seq; + + CList concheck_lst_head; guint check_delete_unrealized_id; @@ -500,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); @@ -556,9 +630,10 @@ static void realize_start_setup (NMDevice *self, NMUnmanFlagOp unmanaged_user_explicit); static void _set_mtu (NMDevice *self, guint32 mtu); static void _commit_mtu (NMDevice *self, const NMIP4Config *config); -static void dhcp_schedule_restart (NMDevice *self, int addr_family, const char *reason); 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, @@ -683,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) { @@ -697,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, @@ -826,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); } /*****************************************************************************/ @@ -879,19 +1052,41 @@ nm_device_ipv4_sysctl_set (NMDevice *self, const char *property, const char *val } static guint32 -nm_device_ipv4_sysctl_get_uint32 (NMDevice *self, const char *property, guint32 fallback) +nm_device_ipv4_sysctl_get_effective_uint32 (NMDevice *self, const char *property, guint32 fallback) { char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + gint64 v, v_all; if (!nm_device_get_ip_ifindex (self)) return fallback; - return nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, buf, nm_device_get_ip_iface (self), property)), - 10, - 0, - G_MAXUINT32, - 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, + nm_device_get_ip_iface (self), + property)), + 10, + 0, + G_MAXUINT32, + -1); + + v_all = nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, + buf, + "all", + property)), + 10, + 0, + G_MAXUINT32, + -1); + + v = NM_MAX (v, v_all); + return v > -1 ? (guint32) v : fallback; } gboolean @@ -1035,7 +1230,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)", @@ -1076,28 +1271,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) @@ -1106,7 +1299,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) { @@ -1178,71 +1372,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 @@ -1289,12 +1514,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 @@ -1316,7 +1538,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); @@ -1326,16 +1548,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 { @@ -1350,24 +1571,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; } @@ -1392,7 +1612,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); } @@ -1698,16 +1918,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; } @@ -1775,6 +1992,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, @@ -1851,12 +2096,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); } @@ -1884,18 +2129,23 @@ 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) { + klass = NM_DEVICE_GET_CLASS (self); + if (G_UNLIKELY (!klass->default_type_description)) { 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); } - return priv->type_description; + return klass->default_type_description; } gboolean @@ -1909,7 +2159,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 * @@ -1917,7 +2167,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 * @@ -1929,7 +2179,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 @@ -1937,10 +2187,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 * @@ -1957,138 +2207,560 @@ nm_device_get_rfkill_type (NMDevice *self) { g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - return NM_DEVICE_GET_PRIVATE (self)->rfkill_type; -} + return NM_DEVICE_GET_PRIVATE (self)->rfkill_type; +} + +static const char * +nm_device_get_physical_port_id (NMDevice *self) +{ + return NM_DEVICE_GET_PRIVATE (self)->physical_port_id; +} + +/*****************************************************************************/ + +typedef enum { + CONCHECK_SCHEDULE_UPDATE_INTERVAL, + 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 interval_ns) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean periodic_check_disabled = FALSE; + + /* 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; + } + + nm_assert (interval_ns >= 0); + + if (!concheck_is_possible (self)) + goto out; + + _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: %sscheduled in %u milliseconds (%u seconds interval)", + periodic_check_disabled ? "re-" : "", + (guint) (interval_ns / NM_UTILS_NS_PER_MSEC), + priv->concheck_p_cur_interval); + + nm_assert (priv->concheck_p_cur_interval > 0); + priv->concheck_p_cur_id = g_timeout_add (interval_ns / 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 +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 (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 (mode == CONCHECK_SCHEDULE_UPDATE_INTERVAL) { + /* ... unless, we are initalizing. In this case, setup the current current + * interval and schedule a perform a check right away. */ + 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, priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND)) + concheck_start (self, NULL, NULL, TRUE); + } + return; + } + + switch (mode) { + 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, priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND)) + concheck_start (self, NULL, NULL, TRUE); + } else { + /* 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, cur_expiry - now_ns); + } + return; + + 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, priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + return; + + 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); + concheck_periodic_schedule_do (self, tdiff); + 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; + } + + /* 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, tdiff); +} + +void +nm_device_check_connectivity_update_interval (NMDevice *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; + } + + if (!new_interval) { + /* this will cancel any potentially pending timeout. */ + 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, CONCHECK_SCHEDULE_UPDATE_INTERVAL); +} + +static void +concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow_periodic_bump) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + /* @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 ( 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"); + } +} + +static void +concheck_handle_complete (NMDeviceConnectivityHandle *handle, + GError *error) +{ + /* 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); + + 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); +} + +static void +concheck_cb (NMConnectivity *connectivity, + NMConnectivityCheckHandle *c_handle, + NMConnectivityState state, + GError *error, + gpointer user_data) +{ + _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; + + handle = user_data; + nm_assert (handle->c_handle == c_handle); + nm_assert (NM_IS_DEVICE (handle->self)); + + handle->c_handle = NULL; + self = handle->self; + + 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; + } + + 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)); -static const char * -nm_device_get_physical_port_id (NMDevice *self) -{ - return NM_DEVICE_GET_PRIVATE (self)->physical_port_id; -} + /* 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)); -static void -update_connectivity_state (NMDevice *self, NMConnectivityState state) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + seq = handle->seq; - /* 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; - } else { - state = NM_CONNECTIVITY_NONE; + /* 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; + } - 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); + /* first update the new state, and emit signals. */ + concheck_update_state (self, state, allow_periodic_bump); - 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"); - } - } -} + handle_is_alive = FALSE; -typedef struct { - NMDevice *self; - NMDeviceConnectivityCallback callback; - gpointer user_data; - guint64 seq; -} ConnectivityCheckData; + /* 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; + } -static void -concheck_done (ConnectivityCheckData *data) -{ - NMDevice *self = data->self; - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + nm_assert (other_handle != handle); - /* 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 (!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); -#if WITH_CONCHECK -static void -concheck_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) -{ - 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; + /* we invoked callbacks, other handles might be cancelled and removed from the list. + * Need to iterate the list from the start. */ + goto check_handles; + } + } - 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); + 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; } - if (data->seq == priv->concheck_seq) - update_connectivity_state (data->self, state); - concheck_done (data); + concheck_handle_complete (handle, NULL); } -#endif /* WITH_CONCHECK */ -static gboolean -no_concheck (gpointer user_data) +static NMDeviceConnectivityHandle * +concheck_start (NMDevice *self, + NMDeviceConnectivityCallback callback, + gpointer user_data, + gboolean is_periodic) { - ConnectivityCheckData *data = user_data; + static guint64 seq_counter = 0; + NMDevicePrivate *priv; + NMDeviceConnectivityHandle *handle; - concheck_done (data); - return G_SOURCE_REMOVE; + 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; } -void +NMDeviceConnectivityHandle * nm_device_check_connectivity (NMDevice *self, NMDeviceConnectivityCallback callback, gpointer user_data) { - ConnectivityCheckData *data; -#if WITH_CONCHECK - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); -#endif + 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; +} - data = g_slice_new0 (ConnectivityCheckData); - data->self = g_object_ref (self); - data->callback = callback; - data->user_data = user_data; +void +nm_device_check_connectivity_cancel (NMDeviceConnectivityHandle *handle) +{ + gs_free_error GError *cancelled_error = NULL; -#if WITH_CONCHECK - if (priv->concheck_periodic_id) { - data->seq = ++priv->concheck_seq; + 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)); - /* Kick off a real connectivity check. */ - nm_connectivity_check_async (nm_connectivity_get (), - nm_device_get_ip_iface (self), - concheck_cb, - data); - return; - } -#endif + /* nobody has access to periodic handles, and cannot cancel + * them externally. */ + nm_assert (!handle->is_periodic); - /* Fake one. */ - g_idle_add (no_concheck, data); + nm_utils_error_set_cancelled (&cancelled_error, FALSE, "NMDevice"); + concheck_handle_complete (handle, cancelled_error); } NMConnectivityState @@ -2099,44 +2771,6 @@ nm_device_get_connectivity_state (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->connectivity_state; } -#if WITH_CONCHECK -static void -concheck_periodic (NMConnectivity *connectivity, NMDevice *self) -{ - nm_device_check_connectivity (self, NULL, NULL); -} -#endif - -static void -concheck_periodic_update (NMDevice *self) -{ -#if WITH_CONCHECK - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean check_enable; - - check_enable = (priv->state == NM_DEVICE_STATE_ACTIVATED) - && nm_device_get_best_default_route (self, AF_UNSPEC); - - 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. */ - g_signal_handler_disconnect (nm_connectivity_get (), priv->concheck_periodic_id); - priv->concheck_periodic_id = 0; - 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 -} - /*****************************************************************************/ static SlaveInfo * @@ -2214,7 +2848,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; } @@ -2352,8 +2986,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)); @@ -2386,8 +3018,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); @@ -2624,12 +3254,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; @@ -2641,19 +3273,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); @@ -2662,11 +3310,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); @@ -2792,11 +3440,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"); } } @@ -2981,7 +3629,7 @@ ip4_rp_filter_update (NMDevice *self) if ( priv->v4_has_shadowed_routes || nm_device_get_best_default_route (self, AF_INET)) { - if (nm_device_ipv4_sysctl_get_uint32 (self, "rp_filter", 0) != 1) { + if (nm_device_ipv4_sysctl_get_effective_uint32 (self, "rp_filter", 0) != 1) { /* Don't touch the rp_filter if it's not strict. */ return; } @@ -3300,8 +3948,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); @@ -3348,7 +3996,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; @@ -3447,6 +4095,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); @@ -3672,7 +4323,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 @@ -3962,7 +4613,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; @@ -4150,8 +4801,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 @@ -4403,9 +5054,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. */ @@ -4530,10 +5181,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 ()); @@ -4590,34 +5241,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); + + klass = NM_DEVICE_GET_CLASS (self); - if (!NM_DEVICE_GET_CLASS (self)->complete_connection) { + 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 @@ -4726,15 +5388,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; @@ -5147,8 +5806,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); @@ -5184,7 +5844,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); } @@ -5202,8 +5862,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)) @@ -5217,8 +5875,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 { @@ -5365,7 +6021,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); @@ -5390,10 +6046,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); @@ -5431,13 +6087,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)) @@ -5478,16 +6130,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); } } @@ -5507,7 +6159,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; @@ -5522,7 +6174,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, @@ -5535,8 +6187,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); } /** @@ -5550,18 +6202,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)); @@ -5576,14 +6227,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 */ @@ -5596,36 +6247,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); } } @@ -5687,10 +6338,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 */ @@ -5724,9 +6375,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); } @@ -5822,55 +6472,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; } /*****************************************************************************/ @@ -5881,7 +6518,7 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - nm_clear_g_source (&priv->dhcp4.restart_id); + nm_clear_g_source (&priv->dhcp4.grace_id); g_clear_pointer (&priv->dhcp4.pac_url, g_free); if (priv->dhcp4.client) { @@ -5898,105 +6535,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_ip_config_id_x[IS_IPv4]) + update_ext_ip_config (self, addr_family, FALSE); + ensure_con_ip_config (self, addr_family); + } + + if (!IS_IPv4) { + if ( commit + && priv->ipv6ll_has) { + const NMPlatformIP6Address ll_a = { + .address = priv->ipv6ll_addr, + .plen = 64, + .addr_source = NM_IP_CONFIG_SOURCE_IP6LL, + }; + const NMPlatformIP6Route ll_r = { + .network.s6_addr16[0] = htons (0xfe80u), + .plen = 64, + .metric = nm_device_get_route_metric (self, addr_family), + .rt_source = NM_IP_CONFIG_SOURCE_IP6LL, + }; + + nm_assert (IN6_IS_ADDR_LINKLOCAL (&priv->ipv6ll_addr)); + + nm_ip6_config_add_address (NM_IP6_CONFIG (composite), &ll_a); + nm_ip6_config_add_route (NM_IP6_CONFIG (composite), &ll_r, NULL); + } + } if (commit) { - if (priv->queued_ip4_config_id) - update_ext_ip_config (self, AF_INET, FALSE, FALSE); - ensure_con_ip4_config (self); + 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)); + } } - if (commit) - priv->default_route_metric_penalty_ip4_has = default_route_metric_penalty_detect (self); + 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->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)); + 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->vpn4_configs; iter; iter = iter->next) - nm_ip4_config_merge (composite, iter->data, NM_IP_CONFIG_MERGE_DEFAULT, 0); + 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_ip4_config) - nm_ip4_config_merge (composite, priv->ext_ip4_config, 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 (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) { + 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_ip4_config (self, composite, commit, ip4_dev_route_blacklist); - g_object_unref (composite); + success = nm_device_set_ip_config (self, addr_family, composite, commit, ip4_dev_route_blacklist); + if (commit) { + if (IS_IPv4) + priv->v4_commit_first_time = FALSE; + else + priv->v6_commit_first_time = FALSE; + } - if (commit) - priv->v4_commit_first_time = FALSE; return success; } @@ -6007,10 +6761,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; } @@ -6026,20 +6779,17 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config) } static gboolean -dhcp4_restart_cb (gpointer user_data) +dhcp4_grace_period_expired (gpointer user_data) { NMDevice *self = user_data; - NMDevicePrivate *priv; - - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - priv = NM_DEVICE_GET_PRIVATE (self); - priv->dhcp4.restart_id = 0; + _LOGI (LOGD_DHCP4, "DHCPv4: grace period expired"); - if (dhcp4_start (self) == NM_ACT_STAGE_RETURN_FAILURE) - dhcp_schedule_restart (self, AF_INET, NULL); + nm_device_ip_method_failed (self, AF_INET, + NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + /* If the device didn't fail, the DHCP client will continue */ - return FALSE; + return G_SOURCE_REMOVE; } static void @@ -6047,44 +6797,48 @@ dhcp4_fail (NMDevice *self, gboolean timeout) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - _LOGD (LOGD_DHCP4, "DHCPv4 failed: timeout %d, num tries left %u", - timeout, priv->dhcp4.num_tries_left); + _LOGD (LOGD_DHCP4, "DHCPv4 failed%s", timeout ? " (timeout)" : ""); - dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); - - /* Don't fail if there are static addresses configured on - * the device, instead retry after some time. + /* Keep client running if there are static addresses configured + * on the interface. */ if ( priv->ip4_state == IP_DONE - && priv->con_ip4_config - && nm_ip4_config_get_num_addresses (priv->con_ip4_config) > 0) { - dhcp_schedule_restart (self, AF_INET, "device has IP addresses"); + && 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 + * configuration. + */ + if ( !priv->dhcp4.was_active + && (timeout || priv->ip4_state == IP_CONF)) { + dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); + nm_device_activate_schedule_ip4_config_timeout (self); return; } - if ( priv->dhcp4.num_tries_left == DHCP_NUM_TRIES_MAX - && (timeout || (priv->ip4_state == IP_CONF)) - && !priv->dhcp4.was_active) - nm_device_activate_schedule_ip4_config_timeout (self); - else if ( priv->dhcp4.num_tries_left < DHCP_NUM_TRIES_MAX - || priv->ip4_state == IP_DONE - || priv->dhcp4.was_active) { - /* Don't fail immediately when the lease expires but try to - * restart DHCP for a predefined number of times. - */ - if (priv->dhcp4.num_tries_left) { - priv->dhcp4.num_tries_left--; - dhcp_schedule_restart (self, AF_INET, "lease expired"); - } else { - nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); - /* We failed the ipv4 method but schedule again the retries if the ipv6 method is - * configured, keeping the connection up. - */ - if (nm_device_get_state (self) != NM_DEVICE_STATE_FAILED) - dhcp_schedule_restart (self, AF_INET, "renewal failed"); - } - } else - g_warn_if_reached (); + /* In any other case (expired lease, assumed connection, etc.), + * start a grace period in which we keep the client running, + * hoping that it will regain a lease. + */ + if (!priv->dhcp4.grace_id) { + priv->dhcp4.grace_id = g_timeout_add_seconds (DHCP_GRACE_PERIOD_SEC, + dhcp4_grace_period_expired, + self); + _LOGI (LOGD_DHCP4, + "DHCPv4: %u seconds grace period started", + DHCP_GRACE_PERIOD_SEC); + goto clear_config; + } + return; + +clear_config: + /* The previous configuration is no longer valid */ + if (priv->dhcp4.config) { + nm_dbus_object_clear_and_unexport (&priv->dhcp4.config); + priv->dhcp4.config = nm_dhcp4_config_new (); + _notify (self, PROP_DHCP4_CONFIG); + } } static void @@ -6124,6 +6878,8 @@ dhcp4_state_changed (NMDhcpClient *client, break; } + nm_clear_g_source (&priv->dhcp4.grace_id); + /* After some failures, we have been able to renew the lease: * update the ip state */ @@ -6136,7 +6892,6 @@ dhcp4_state_changed (NMDhcpClient *client, nm_dhcp4_config_set_options (priv->dhcp4.config, options); _notify (self, PROP_DHCP4_CONFIG); - priv->dhcp4.num_tries_left = DHCP_NUM_TRIES_MAX; if (priv->ip4_state == IP_CONF) { connection = nm_device_get_applied_connection (self); @@ -6145,6 +6900,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)); @@ -6199,25 +6955,112 @@ get_dhcp_timeout (NMDevice *self, int addr_family) if (timeout) return timeout; - { - gs_free char *value = NULL; + { + gs_free char *value = NULL; + + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + addr_family == AF_INET + ? "ipv4.dhcp-timeout" + : "ipv6.dhcp-timeout", + self); + timeout = _nm_utils_ascii_str_to_int64 (value, 10, + 0, G_MAXINT32, 0); + if (timeout) + return timeout; + } + + klass = NM_DEVICE_GET_CLASS (self); + if (klass->get_dhcp_timeout) + timeout = klass->get_dhcp_timeout (self, 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; + + stable_id = _get_stable_id (self, connection, &stable_type); + if (!stable_id) + g_return_val_if_reached (NULL); + + salted_header = htonl (2011610591 + stable_type); + + 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)); + + buf_size = sizeof (buf); + g_checksum_get_digest (sum, buf, &buf_size); + nm_assert (buf_size == sizeof (buf)); - value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - addr_family == AF_INET - ? "ipv4.dhcp-timeout" - : "ipv6.dhcp-timeout", - self); - timeout = _nm_utils_ascii_str_to_int64 (value, 10, - 0, G_MAXINT32, 0); - if (timeout) - return timeout; - } + g_checksum_free (sum); - klass = NM_DEVICE_GET_CLASS (self); - if (klass->get_dhcp_timeout) - timeout = klass->get_dhcp_timeout (self, addr_family); + 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 timeout ?: NM_DHCP_TIMEOUT_DEFAULT; + return nm_dhcp_utils_client_id_string_to_bytes (client_id); } static NMActStageReturn @@ -6225,9 +7068,8 @@ 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); @@ -6236,36 +7078,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; @@ -6338,7 +7175,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); @@ -6507,7 +7344,6 @@ act_stage3_ip4_config_start (NMDevice *self, } method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - priv->dhcp4.num_tries_left = DHCP_NUM_TRIES_MAX; /* Start IPv4 addressing based on the method requested */ if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) { @@ -6524,6 +7360,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)); @@ -6560,9 +7397,9 @@ 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.restart_id); + nm_clear_g_source (&priv->dhcp6.grace_id); if (priv->dhcp6.client) { nm_clear_g_signal_handler (priv->dhcp6.client, &priv->dhcp6.state_sigid); @@ -6578,148 +7415,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; } @@ -6730,7 +7437,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; } @@ -6746,53 +7453,17 @@ dhcp6_lease_change (NMDevice *self) } static gboolean -dhcp6_restart_cb (gpointer user_data) +dhcp6_grace_period_expired (gpointer user_data) { NMDevice *self = user_data; - NMDevicePrivate *priv; - - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - - priv = NM_DEVICE_GET_PRIVATE (self); - priv->dhcp6.restart_id = 0; - - if (!dhcp6_start (self, FALSE)) - dhcp_schedule_restart (self, AF_INET6, NULL); - - return FALSE; -} - -static void -dhcp_schedule_restart (NMDevice *self, - int addr_family, - const char *reason) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - guint tries_left; - char tries_str[255]; - - nm_assert_addr_family (addr_family); - tries_left = (addr_family == AF_INET) - ? priv->dhcp4.num_tries_left - : priv->dhcp6.num_tries_left; + _LOGI (LOGD_DHCP6, "DHCPv6: grace period expired"); - _LOGI ((addr_family == AF_INET) ? LOGD_DHCP4 : LOGD_DHCP6, - "scheduling DHCPv%c restart in %u seconds%s%s%s%s", - nm_utils_addr_family_to_char (addr_family), - DHCP_RESTART_TIMEOUT, - (tries_left != DHCP_NUM_TRIES_MAX) - ? nm_sprintf_buf (tries_str, ", %u tries left", tries_left + 1) - : "", - NM_PRINT_FMT_QUOTED (reason, " (reason: ", reason, ")", "")); + nm_device_ip_method_failed (self, AF_INET6, + NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + /* If the device didn't fail, the DHCP client will continue */ - if (addr_family == AF_INET) { - priv->dhcp4.restart_id = g_timeout_add_seconds (DHCP_RESTART_TIMEOUT, - dhcp4_restart_cb, self); - } else { - priv->dhcp6.restart_id = g_timeout_add_seconds (DHCP_RESTART_TIMEOUT, - dhcp6_restart_cb, self); - } + return G_SOURCE_REMOVE; } static void @@ -6801,51 +7472,57 @@ dhcp6_fail (NMDevice *self, gboolean timeout) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean is_dhcp_managed; - _LOGD (LOGD_DHCP6, "DHCPv6 failed: timeout %d, num tries left %u", - timeout, priv->dhcp6.num_tries_left); + _LOGD (LOGD_DHCP6, "DHCPv6 failed%s", timeout ? " (timeout)" : ""); is_dhcp_managed = (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED); - dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); - if (is_dhcp_managed || priv->dhcp6.num_tries_left < DHCP_NUM_TRIES_MAX) { - /* Don't fail if there are static addresses configured on - * the device, instead retry after some time. + if (is_dhcp_managed) { + /* Keep client running if there are static addresses configured + * on the interface. */ if ( priv->ip6_state == IP_DONE - && priv->con_ip6_config - && nm_ip6_config_get_num_addresses (priv->con_ip6_config)) { - dhcp_schedule_restart (self, AF_INET6, "device has IP addresses"); + && 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 + * configuration. + */ + if ( !priv->dhcp6.was_active + && (timeout || priv->ip6_state == IP_CONF)) { + dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); + nm_device_activate_schedule_ip6_config_timeout (self); return; } - if ( priv->dhcp6.num_tries_left == DHCP_NUM_TRIES_MAX - && (timeout || (priv->ip6_state == IP_CONF)) - && !priv->dhcp6.was_active) - nm_device_activate_schedule_ip6_config_timeout (self); - else if ( priv->dhcp6.num_tries_left < DHCP_NUM_TRIES_MAX - || priv->ip6_state == IP_DONE - || priv->dhcp6.was_active) { - /* Don't fail immediately when the lease expires but try to - * restart DHCP for a predefined number of times. - */ - if (priv->dhcp6.num_tries_left) { - priv->dhcp6.num_tries_left--; - dhcp_schedule_restart (self, AF_INET6, "lease expired"); - } else { - nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); - /* We failed the ipv6 method but schedule again the retries if the ipv4 method is - * configured, keeping the connection up. - */ - if (nm_device_get_state (self) != NM_DEVICE_STATE_FAILED) - dhcp_schedule_restart (self, AF_INET6, "renewal failed"); - } - } else - g_warn_if_reached (); + /* In any other case (expired lease, assumed connection, etc.), + * start a grace period in which we keep the client running, + * hoping that it will regain a lease. + */ + if (!priv->dhcp6.grace_id) { + priv->dhcp6.grace_id = g_timeout_add_seconds (DHCP_GRACE_PERIOD_SEC, + dhcp6_grace_period_expired, + self); + _LOGI (LOGD_DHCP6, + "DHCPv6: %u seconds grace period started", + DHCP_GRACE_PERIOD_SEC); + goto clear_config; + } } else { /* not a hard failure; just live with the RA info */ + dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); if (priv->ip6_state == IP_CONF) nm_device_activate_schedule_ip6_config_result (self); } + return; + +clear_config: + /* The previous configuration is no longer valid */ + if (priv->dhcp6.config) { + nm_dbus_object_clear_and_unexport (&priv->dhcp6.config); + priv->dhcp6.config = nm_dhcp6_config_new (); + _notify (self, PROP_DHCP6_CONFIG); + } } static void @@ -6881,6 +7558,7 @@ dhcp6_state_changed (NMDhcpClient *client, switch (state) { case NM_DHCP_STATE_BOUND: + nm_clear_g_source (&priv->dhcp6.grace_id); /* If the server sends multiple IPv6 addresses, we receive a state * changed event for each of them. Use the event ID to merge IPv6 * addresses from the same transaction into a single configuration. @@ -6893,16 +7571,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: @@ -6911,10 +7589,8 @@ dhcp6_state_changed (NMDhcpClient *client, if (priv->ip6_state == IP_FAIL) _set_ip_state (self, AF_INET6, IP_CONF); - priv->dhcp6.num_tries_left = DHCP_NUM_TRIES_MAX; - 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; } @@ -6965,34 +7641,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), @@ -7004,8 +7678,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, @@ -7031,11 +7703,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); @@ -7046,17 +7718,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)) @@ -7118,19 +7785,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"); } @@ -7145,11 +7812,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); @@ -7158,34 +7825,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); } @@ -7200,17 +7861,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); @@ -7220,12 +7890,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); @@ -7240,28 +7907,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); @@ -7286,7 +7952,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; @@ -7302,37 +7968,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); @@ -7348,8 +8007,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; } /*****************************************************************************/ @@ -7414,7 +8072,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); @@ -7591,7 +8249,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)); } @@ -7603,10 +8261,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; @@ -7627,16 +8285,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, @@ -7645,22 +8310,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) { @@ -7710,16 +8387,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); @@ -7737,12 +8419,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. */ @@ -7773,7 +8453,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 @@ -7796,7 +8476,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; @@ -7805,11 +8484,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); @@ -7846,15 +8522,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 @@ -7867,7 +8542,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); @@ -7921,7 +8596,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); } @@ -7931,7 +8607,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); } @@ -7946,7 +8622,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"; @@ -8090,21 +8766,19 @@ act_stage3_ip6_config_start (NMDevice *self, } priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_NONE; - priv->dhcp6.num_tries_left = DHCP_NUM_TRIES_MAX; - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); 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); } @@ -8115,7 +8789,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 @@ -8136,7 +8810,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); @@ -8149,7 +8822,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)) { @@ -8259,8 +8934,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); @@ -8398,7 +9073,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) { @@ -8470,7 +9145,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); } @@ -8526,7 +9201,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); } @@ -8644,9 +9319,9 @@ 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; } } @@ -8666,7 +9341,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 @@ -8682,19 +9357,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 @@ -8719,8 +9394,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; @@ -8732,7 +9406,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; @@ -8768,10 +9442,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); } @@ -8796,6 +9467,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. @@ -8804,42 +9501,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); } } @@ -8871,10 +9569,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. */ @@ -8960,44 +9658,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; @@ -9014,8 +9695,6 @@ act_request_set (NMDevice *self, NMActRequest *act_request) break; } } - - _notify (self, PROP_ACTIVE_CONNECTION); } static void @@ -9041,9 +9720,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); @@ -9137,37 +9816,31 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex) } static void -_cleanup_ip4_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_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) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - _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 @@ -9186,7 +9859,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); @@ -9237,8 +9910,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; @@ -9247,31 +9919,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"); } } @@ -9280,8 +9952,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; @@ -9290,31 +9961,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"); } } @@ -9359,7 +10033,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, @@ -9485,7 +10159,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, @@ -9498,10 +10172,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) { @@ -9557,8 +10231,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); @@ -9627,25 +10301,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; } @@ -9653,8 +10335,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; } @@ -9669,8 +10351,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); @@ -9683,9 +10365,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, @@ -9744,40 +10425,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, @@ -9926,25 +10611,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, @@ -9976,21 +10667,27 @@ delete_cb (NMDevice *self, } static void -impl_device_delete (NMDevice *self, GDBusMethodInvocation *context) -{ - GError *error = NULL; - - 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); +impl_device_delete (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMDevice *self = NM_DEVICE (obj); + + 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, @@ -10114,8 +10811,8 @@ nm_device_steal_connection (NMDevice *self, NMSettingsConnection *connection) && connection == nm_active_connection_get_settings_connection (NM_ACTIVE_CONNECTION (priv->queued_act_request))) _clear_queued_act_request (priv); - if ( priv->act_request - && connection == nm_active_connection_get_settings_connection (NM_ACTIVE_CONNECTION (priv->act_request)) + if ( priv->act_request.obj + && connection == nm_active_connection_get_settings_connection (NM_ACTIVE_CONNECTION (priv->act_request.obj)) && priv->state < NM_DEVICE_STATE_DEACTIVATING) { nm_device_state_changed (self, NM_DEVICE_STATE_DEACTIVATING, @@ -10131,7 +10828,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); @@ -10146,7 +10843,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, @@ -10231,116 +10928,155 @@ 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; } @@ -10387,11 +11123,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"); } @@ -10400,125 +11136,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) { @@ -10529,11 +11151,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"); } @@ -10542,15 +11164,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"); } @@ -10567,7 +11182,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; } /*****************************************************************************/ @@ -10758,7 +11373,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); @@ -10801,15 +11416,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); @@ -10949,11 +11564,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"); } @@ -11018,122 +11633,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); @@ -11142,122 +11672,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); } } @@ -11265,156 +11783,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); @@ -11423,21 +11924,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; @@ -11449,10 +11969,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; @@ -11461,19 +11981,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; @@ -11741,14 +12260,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) { @@ -12014,7 +12533,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), @@ -12026,7 +12545,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), @@ -12082,13 +12601,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)) @@ -12179,7 +12712,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]); @@ -12204,7 +12737,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 @@ -12262,7 +12795,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); @@ -12371,19 +12904,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; @@ -12565,8 +13098,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 @@ -12588,39 +13121,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); } @@ -12642,7 +13177,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); } /* @@ -12762,10 +13297,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)); } @@ -13072,7 +13607,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 @@ -13180,8 +13715,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: @@ -13195,6 +13730,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 */ @@ -13345,7 +13887,7 @@ _set_state_full (NMDevice *self, if (ip_config_valid (old_state) && !ip_config_valid (state)) notify_ip_properties (self); - concheck_periodic_update (self); + nm_device_check_connectivity_update_interval (self); /* Dispose of the cached activation request */ if (req) @@ -14307,8 +14849,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 @@ -14323,7 +14872,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; @@ -14422,9 +14971,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); @@ -14435,8 +14994,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); @@ -14498,6 +15057,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)) { @@ -14520,7 +15081,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); @@ -14529,19 +15090,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); } @@ -14651,9 +15215,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) { @@ -14709,16 +15270,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); @@ -14728,7 +15289,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); @@ -14759,12 +15320,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); @@ -14773,7 +15331,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); @@ -14818,7 +15376,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); } @@ -14845,15 +15403,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; @@ -15085,7 +15743,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, @@ -15102,7 +15759,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, @@ -15185,15 +15841,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..66720f01 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" @@ -113,8 +114,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 +135,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 +173,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 @@ -190,7 +190,9 @@ typedef enum { /*< skip >*/ } NMDeviceCheckDevAvailableFlags; typedef struct { - NMExportedObjectClass parent; + NMDBusObjectClass parent; + + 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); @@ -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..7b1c4617 --- /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_pkglibdir +) + +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..3f1fe974 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); /*****************************************************************************/ @@ -180,11 +178,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 +206,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..b8f5a935 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; @@ -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 */ diff --git a/src/devices/team/meson.build b/src/devices/team/meson.build new file mode 100644 index 00000000..4a533bc5 --- /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_pkglibdir +) + +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 bcbc1140..00000000 --- a/src/devices/tests/test-arping.c +++ /dev/null @@ -1,140 +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; - - if (!nm_utils_find_helper ("arping", NULL, NULL)) { - g_test_skip ("arping binary is missing"); - return; - } - - 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); - g_signal_connect (manager, NM_ARPING_MANAGER_PROBE_TERMINATED, - G_CALLBACK (arping_manager_probe_terminated), loop); - g_assert (nm_arping_manager_start_probe (manager, 250, NULL)); - g_assert (nmtst_main_loop_run (loop, 2000)); - - for (i = 0; info->addresses[i]; i++) { - g_assert_cmpint (nm_arping_manager_check_address (manager, info->addresses[i]), - ==, - info->expected_result[i]); - } - - g_main_loop_unref (loop); -} - -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..5d0625f8 100644 --- a/src/devices/tests/test-lldp.c +++ b/src/devices/tests/test-lldp.c @@ -347,8 +347,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 +356,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..27eaeea6 --- /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_pkglibdir +) + +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..eeff5bd3 --- /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; + 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) { + + ap = g_hash_table_lookup (new_aps, + nm_wifi_ap_get_supplicant_path (ap)); + if (ap) { + if (nm_wifi_ap_set_strength (ap, nm_wifi_ap_get_strength (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..cd2c68af 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; @@ -392,7 +387,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 +396,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 +436,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 +496,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 +524,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 +548,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..de4af42c 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) -{ - 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) +const CList * +_nm_device_wifi_get_aps (NMDeviceWifi *self) { - 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; @@ -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: @@ -3055,13 +2883,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 +2924,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 +3102,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 +3115,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 +3155,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 +3215,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 +3224,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 +3235,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 +3303,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 +3310,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..450009f0 --- /dev/null +++ b/src/devices/wifi/nm-iwd-manager.c @@ -0,0 +1,694 @@ +/* -*- 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..47c0ce67 --- /dev/null +++ b/src/devices/wifi/nm-wifi-common.c @@ -0,0 +1,205 @@ +/*-*- 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); + } +#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..032b3585 --- /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_pkglibdir +) + +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_pkglibdir +) + +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..9a3744db 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -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); @@ -1409,35 +1409,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..a1c6aef2 100644 --- a/src/devices/wwan/nm-modem-ofono.c +++ b/src/devices/wwan/nm-modem-ofono.c @@ -836,6 +836,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 +861,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..61b7247e 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 */ @@ -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); - 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); + nm_assert (ifindex >= 0); + nm_assert (NM_MODEM_GET_PRIVATE (self)->ppp_manager == ppp_manager); + + 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,10 @@ 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); + g_clear_object (&priv->ppp_manager); + } if (device) { g_return_if_fail (NM_IS_DEVICE (device)); @@ -1114,11 +1151,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); } /*****************************************************************************/ @@ -1369,17 +1407,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 +1530,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 +1633,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 +1646,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 +1694,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 +1735,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 +1751,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 +1776,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 +1825,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..f0aae040 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; } 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..96c02653 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) { @@ -345,7 +340,7 @@ nm_dhcp_client_stop_pid (pid_t pid, const char *iface) } static void -stop (NMDhcpClient *self, gboolean release, const GByteArray *duid) +stop (NMDhcpClient *self, gboolean release, GBytes *duid) { NMDhcpClientPrivate *priv; @@ -359,7 +354,6 @@ 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 @@ -492,10 +486,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 +505,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 +525,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 +548,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 +580,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 +604,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 +612,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); @@ -811,8 +796,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; @@ -923,8 +908,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 +976,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 +992,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 +1001,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 +1043,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 +1077,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..0d92d743 100644 --- a/src/dhcp/nm-dhcp-client.h +++ b/src/dhcp/nm-dhcp-client.h @@ -34,15 +34,16 @@ #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" @@ -64,8 +65,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 +85,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 +102,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 +125,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 +139,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); @@ -179,8 +185,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 +194,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..52923310 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.c +++ b/src/dhcp/nm-dhcp-dhclient-utils.c @@ -125,7 +125,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 +138,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 +178,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 +309,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 +363,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 +477,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 +504,7 @@ nm_dhcp_dhclient_escape_duid (const GByteArray *duid) } else *d++ = *s++; } + *d++ = '\0'; return escaped; } @@ -476,7 +516,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 +547,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 +556,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 +643,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..738e9f91 100644 --- a/src/dhcp/nm-dhcp-dhclient.c +++ b/src/dhcp/nm-dhcp-dhclient.c @@ -158,32 +158,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, @@ -338,7 +312,7 @@ create_dhclient_config (NMDhcpDhclient *self, static gboolean dhclient_start (NMDhcpClient *client, const char *mode_opt, - const GByteArray *duid, + GBytes *duid, gboolean release, pid_t *out_pid, int prefixes) @@ -439,19 +413,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); } @@ -518,8 +492,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 +507,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,16 +528,19 @@ 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) @@ -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; @@ -642,7 +620,7 @@ 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); } /*****************************************************************************/ @@ -717,7 +695,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..82b3db4f 100644 --- a/src/dhcp/nm-dhcp-dhcpcanon.c +++ b/src/dhcp/nm-dhcp-dhcpcanon.c @@ -80,7 +80,7 @@ nm_dhcp_dhcpcanon_get_path (void) 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 +118,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); } @@ -179,9 +179,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 +189,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 +265,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..d7d38e54 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; @@ -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..bf22872d 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, @@ -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..4f37f069 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 @@ -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) @@ -854,7 +817,7 @@ bound6_handle (NMDhcpSystemd *self) lease, options, TRUE, - priv->info_only, + nm_dhcp_client_get_info_only (NM_DHCP_CLIENT (self)), &error); if (ip6_config) { @@ -900,24 +863,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 +900,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 +920,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 +958,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 +982,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 +1052,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..32badae8 --- /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: '-DTESTDIR="@0@"'.format(meson.current_source_dir()) + ) + + 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..25af51a1 100644 --- a/src/dhcp/tests/test-dhcp-dhclient.c +++ b/src/dhcp/tests/test-dhcp-dhclient.c @@ -36,12 +36,6 @@ #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; - static void test_config (const char *orig, const char *expected, @@ -154,7 +148,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 +166,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 +235,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 +291,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 +314,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 +327,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 +673,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,22 +731,23 @@ 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); 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); @@ -754,12 +846,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 +890,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 +936,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 +987,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 +996,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 +1024,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..d0753078 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" @@ -76,54 +76,41 @@ G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN) /*****************************************************************************/ static char ** -get_ip4_rdns_domains (NMIP4Config *ip4) +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; - const NMPlatformIP4Address *address; - const NMPlatformIP4Route *route; - g_return_val_if_fail (ip4 != NULL, NULL); + nm_assert_addr_family (addr_family); 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); + if (addr_family == AF_INET) { + NMIP4Config *ip4 = (gpointer) ip_config; + const NMPlatformIP4Address *address; + const NMPlatformIP4Route *route; - 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); -} + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) + nm_utils_get_reverse_dns_domains_ip4 (address->address, address->plen, domains); -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_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_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); + 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 */ @@ -155,96 +142,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; +#define IP_ADDR_TO_STRING_BUFLEN (NM_UTILS_INET_ADDRSTRLEN + 1 + IFNAMSIZ) - 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; - } - } +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; - /* 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); - } - } - } + nm_assert_addr_family (addr_family); + nm_assert (addr); + nm_assert (out_buf); - /* 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); - } + 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) ? "%" : "@"; } - return TRUE; -} - -static char * -ip6_addr_to_string (const struct in6_addr *addr, const char *iface) -{ - char buf[NM_UTILS_INET_ADDRSTRLEN]; - - 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 +205,100 @@ 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, + int ifindex, + NMIPConfig *ip_config, + gboolean split) { - const struct in6_addr *addr; - char *buf = NULL; - int nnameservers, i_nameserver, n, i; + int addr_family; + gconstpointer addr; gboolean added = FALSE; + guint nnameservers, i_nameserver, n, i; + char ip_addr_to_string_buf[IP_ADDR_TO_STRING_BUFLEN]; + char **domains, **iter; + gboolean iface_resolved = FALSE; + const char *iface = NULL, *domain; - g_return_val_if_fail (iface, FALSE); - nnameservers = nm_ip6_config_get_num_nameservers (ip6); + addr_family = nm_ip_config_get_addr_family (ip_config); + g_return_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - if (split) { - char **domains, **iter; + nm_assert (ifindex > 0); + nm_assert (ifindex == nm_ip_config_get_ifindex (ip_config)); + + nnameservers = nm_ip_config_get_num_nameservers (ip_config); + if (split) { 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; - } + return; - if (n == 0) { - /* If not searches, use any domains */ - n = nm_ip6_config_get_num_domains (ip6); + if (!iface_resolved) { + iface = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); + iface_resolved = TRUE; + } + + if (iface) { + for (i_nameserver = 0; i_nameserver < nnameservers; i_nameserver++) { + addr = nm_ip_config_get_nameserver (ip_config, i_nameserver); + + ip_addr_to_string (addr_family, addr, iface, ip_addr_to_string_buf); + + /* searches are preferred over domains */ + n = nm_ip_config_get_num_searches (ip_config); for (i = 0; i < n; i++) { + domain = nm_utils_parse_dns_domain (nm_ip_config_get_search (ip_config, i), NULL); add_dnsmasq_nameserver (self, servers, - buf, - nm_ip6_config_get_domain (ip6, i)); + ip_addr_to_string_buf, + domain); 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); - } + if (n == 0) { + /* If not searches, use any domains */ + n = nm_ip_config_get_num_domains (ip_config); + domain = nm_utils_parse_dns_domain (nm_ip_config_get_domain (ip_config, i), NULL); + for (i = 0; i < n; i++) { + add_dnsmasq_nameserver (self, + servers, + ip_addr_to_string_buf, + domain); + added = TRUE; + } + } - g_free (buf); + /* Ensure reverse-DNS works by directing queries for in-addr4.arpa/ip6.arpa + * domains to the split domain's nameserver. + */ + domains = get_ip_rdns_domains (ip_config); + if (domains) { + for (iter = domains; *iter; iter++) + add_dnsmasq_nameserver (self, servers, ip_addr_to_string_buf, *iter); + g_strfreev (domains); + } + } } } /* 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); + if (!iface_resolved) + iface = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); + if (iface) { + for (i = 0; i < nnameservers; i++) { + addr = nm_ip_config_get_nameserver (ip_config, i); + ip_addr_to_string (addr_family, addr, iface, ip_addr_to_string_buf); + add_dnsmasq_nameserver (self, servers, ip_addr_to_string_buf, NULL); } } } - - 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 +307,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 +408,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 +460,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 +480,16 @@ 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; + int prio, first_prio = 0; + const NMDnsIPConfigData *ip_data; + gboolean is_first = TRUE; start_dnsmasq (self); @@ -567,15 +498,18 @@ 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) + c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) { + prio = nm_ip_config_get_dns_priority (ip_data->ip_config); + 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) break; - add_ip_config_data (self, &servers, data); + add_ip_config (self, + &servers, + ip_data->data->ifindex, + ip_data->ip_config, + ip_data->ip_config_type == NM_DNS_IP_CONFIG_TYPE_VPN); } } diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index dc545470..10a16e57 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,109 @@ 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)); + 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)); + 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_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); - g_object_unref (data->config); - g_free (data->iface); - g_slice_free (NMDnsIPConfigData, 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 +322,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) { - const NMDnsIPConfigData *const *ptr_a = a, *const *ptr_b = b; + return _ip_config_data_cmp (c_list_entry (a, NMDnsIPConfigData, ip_config_lst), + c_list_entry (b, NMDnsIPConfigData, ip_config_lst)); +} - return ip_config_data_compare (*ptr_a, *ptr_b); +static CList * +_ip_config_lst_head (NMDnsManager *self) +{ + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); + + 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 +371,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 +381,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 +438,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 +502,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) { - char *str; - int x; + if (value) { + _LOGD ("writing to netconfig: %s='%s'", key, value); + g_string_append_printf (str, "%s='%s'\n", key, value); + } +} - str = g_strdup_printf ("%s='%s'\n", key, value); - _LOGD ("writing to netconfig: %s", str); - x = write (fd, str, strlen (str)); - g_free (str); +static void +netconfig_construct_strv (NMDnsManager *self, GString *str, const char *key, const char *const*values) +{ + if (values) { + gs_free char *value = NULL; + + 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"); + 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 (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); - } - - 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 +868,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 +942,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 +970,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 +1039,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 +1057,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 +1105,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); } } @@ -1040,16 +1176,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) { @@ -1067,8 +1199,8 @@ update_dns (NMDnsManager *self, _LOGD ("update-dns: updating plugin %s", plugin_name); 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); @@ -1114,8 +1246,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 +1341,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), 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; } - 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 (!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); + } + + 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; + + p_best = NM_IS_IP4_CONFIG (ip_config) + ? &priv->best_ip_config_4 + : &priv->best_ip_config_6; - if (cfg_type == NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE) { + 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 +1438,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 +1480,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 +1510,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 +1906,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 +1922,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 +1951,47 @@ _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 +2032,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 +2054,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 +2095,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 +2148,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..56889367 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,23 @@ 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; } 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 +84,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 +123,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..7da27e5f 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,129 +90,95 @@ 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) + NMIPConfig *config) { 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 *domain; + addr_family = nm_ip_config_get_addr_family (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); + n = nm_ip_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); - 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 (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); + n = nm_ip_config_get_num_searches (config); if (n > 0) { for (i = 0; i < n; i++) { + domain = nm_utils_parse_dns_domain (nm_ip_config_get_search (config, i), + &is_routing); 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); + domain, + is_routing); } } else { - n = addr_family == AF_INET - ? nm_ip4_config_get_num_domains (config) - : nm_ip6_config_get_num_domains (config); + n = nm_ip_config_get_num_domains (config); for (i = 0; i < n; i++) { + domain = nm_utils_parse_dns_domain (nm_ip_config_get_domain (config, i), + &is_routing); 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); + domain, + is_routing); } } } @@ -213,13 +187,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 +201,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 +213,50 @@ 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) { + NMIPConfig *ip_config = elem->data; + + update_add_ip_config (self, &dns, &domains, ip_config); + + 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 +265,81 @@ 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; + NMDnsIPConfigData *ip_data; + gboolean is_first = TRUE; + + interfaces = g_hash_table_new_full (nm_direct_hash, NULL, + NULL, (GDestroyNotify) _interface_config_free); - for (i = 0; i < configs->len; i++) { - const NMDnsIPConfigData *data = configs->pdata[i]; + c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) { gboolean skip = FALSE; + InterfaceConfig *ic = NULL; + int ifindex; - prio = nm_ip_config_get_dns_priority (data->config); - if (i == 0) + prio = nm_ip_config_get_dns_priority (ip_data->ip_config); + 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; - add_interface_configuration (self, interfaces, data, skip); + + 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); + } + + if (!skip) { + c_list_link_tail (&ic->configs_lst_head, + &nm_c_list_elem_new_stale (ip_data->ip_config)->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 +392,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 +432,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..40f42f6f --- /dev/null +++ b/src/dnsmasq/tests/meson.build @@ -0,0 +1,14 @@ +test_unit = 'test-dnsmasq-utils' + +exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, + c_args: '-DTESTDIR="@0@"'.format(meson.source_root()) +) + +test( + 'dnsmasq/' + test_unit, + test_script, + args: test_args + [exe.full_path()] +) diff --git a/src/main.c b/src/main.c index d59da052..bd8e26a5 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 }, @@ -223,6 +225,7 @@ int main (int argc, char *argv[]) { gboolean success = FALSE; + NMManager *manager; 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,18 @@ main (int argc, char *argv[]) NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT, NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_BOOL)); - nm_manager_setup (); + manager = nm_manager_setup (); - 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 + if (!nm_dbus_manager_start (nm_dbus_manager_get (), + nm_manager_dbus_set_property_handle, + manager)) + goto done; 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,11 +440,14 @@ 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_manager_write_device_state (manager); - nm_exported_object_class_set_quitting (); + /* FIXME: we don't properly shut down on exit. That is a bug. + * NMDBusObject have an assertion that they get unexported before disposing. + * We need this workaround and disable the assertion during our leaky shutdown. */ + nm_dbus_object_set_quitting (); - nm_manager_stop (nm_manager_get ()); + nm_manager_stop (manager); nm_config_state_set (config, TRUE, TRUE); diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 00000000..591c9c79 --- /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_pkglibdir), + '-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 87070794..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; } @@ -541,7 +537,8 @@ nm_act_request_init (NMActRequest *req) * @specific_object: the object path of the specific object (ie, WiFi access point, * etc) that will be used to activate @connection and @device * @subject: the #NMAuthSubject representing the requestor of the activation - * @activation_type: the #NMActivationType. + * @activation_type: the #NMActivationType + * @activation_reason: the reason for activation * @device: the device/interface to configure according to @connection * * Creates a new device-based activation request. If an applied connection is @@ -555,6 +552,7 @@ nm_act_request_new (NMSettingsConnection *settings_connection, const char *specific_object, NMAuthSubject *subject, NMActivationType activation_type, + NMActivationReason activation_reason, NMDevice *device) { g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL); @@ -568,6 +566,7 @@ nm_act_request_new (NMSettingsConnection *settings_connection, NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, specific_object, NM_ACTIVE_CONNECTION_INT_SUBJECT, subject, NM_ACTIVE_CONNECTION_INT_ACTIVATION_TYPE, (int) activation_type, + NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON, (int) activation_reason, NULL); } diff --git a/src/nm-act-request.h b/src/nm-act-request.h index 5d1c9dfd..a8f09271 100644 --- a/src/nm-act-request.h +++ b/src/nm-act-request.h @@ -41,6 +41,7 @@ NMActRequest *nm_act_request_new (NMSettingsConnection *settings_connec const char *specific_object, NMAuthSubject *subject, NMActivationType activation_type, + NMActivationReason activation_reason, NMDevice *device); NMSettingsConnection *nm_act_request_get_settings_connection (NMActRequest *req); diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index 65f57565..c6d6645d 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,17 +55,22 @@ typedef struct _NMActiveConnectionPrivate { bool master_ready:1; NMActivationType activation_type:3; + NMActivationReason activation_reason:3; 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_data1; + gpointer user_data2; + } auth; + } NMActiveConnectionPrivate; NM_GOBJECT_PROPERTIES_DEFINE (NMActiveConnection, @@ -92,6 +98,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMActiveConnection, PROP_INT_MASTER, PROP_INT_MASTER_READY, PROP_INT_ACTIVATION_TYPE, + PROP_INT_ACTIVATION_REASON, ); enum { @@ -103,17 +110,19 @@ 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); /*****************************************************************************/ @@ -180,41 +189,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 @@ -223,6 +216,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, @@ -253,14 +258,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); } @@ -356,7 +361,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; @@ -367,7 +372,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 * @@ -421,10 +426,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, @@ -444,7 +446,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. @@ -454,12 +456,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 @@ -471,9 +473,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); } @@ -489,10 +491,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); } @@ -513,9 +515,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); @@ -813,7 +815,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)); @@ -852,11 +854,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); } } @@ -882,22 +884,29 @@ _set_activation_type_managed (NMActiveConnection *self) nm_device_sys_iface_state_set (priv->device, NM_DEVICE_SYS_IFACE_STATE_MANAGED); } +NMActivationReason +nm_active_connection_get_activation_reason (NMActiveConnection *self) +{ + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), NM_ACTIVATION_REASON_UNSET); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->activation_reason; +} + /*****************************************************************************/ 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); @@ -977,62 +986,93 @@ nm_active_connection_set_parent (NMActiveConnection *self, NMActiveConnection *p /*****************************************************************************/ static void -auth_done (NMAuthChain *chain, +auth_cancel (NMActiveConnection *self) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + + 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); + } + priv->auth.result_func = NULL; + priv->auth.user_data1 = NULL; + priv->auth.user_data2 = NULL; +} + +static void +auth_complete (NMActiveConnection *self, gboolean result, const char *message) +{ + _nm_unused gs_unref_object NMActiveConnection *self_keep_alive = g_object_ref (self); + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + + priv->auth.result_func (self, + result, + message, + priv->auth.user_data1, + priv->auth.user_data2); + auth_cancel (self); +} + +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 (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 (self, FALSE, "Not authorized to share connections via wifi."); + return; + } - g_object_unref (self); + auth_complete (self, TRUE, NULL); } /** @@ -1060,37 +1100,43 @@ nm_active_connection_authorize (NMActiveConnection *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_data1 = user_data1; + priv->auth.user_data2 = user_data2; } /*****************************************************************************/ @@ -1152,31 +1198,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) @@ -1196,19 +1251,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); @@ -1216,7 +1271,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); @@ -1281,12 +1336,17 @@ set_property (GObject *object, guint prop_id, g_return_if_reached (); _set_activation_type (self, (NMActivationType) i); break; + case PROP_INT_ACTIVATION_REASON: + /* construct-only */ + i = g_value_get_int (value); + priv->activation_reason = i; + nm_assert (priv->activation_reason == ((NMActivationReason) i)); + break; 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); @@ -1316,6 +1376,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"); @@ -1333,8 +1397,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), @@ -1364,13 +1428,9 @@ dispose (GObject *object) _LOGD ("disposing"); - if (priv->chain) { - nm_auth_chain_unref (priv->chain); - priv->chain = NULL; - } + auth_cancel (self); - 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); @@ -1392,22 +1452,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, @@ -1551,6 +1660,15 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_INT_ACTIVATION_REASON] = + g_param_spec_int (NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON, "", "", + NM_ACTIVATION_REASON_UNSET, + NM_ACTIVATION_REASON_USER_REQUEST, + NM_ACTIVATION_REASON_UNSET, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); signals[DEVICE_CHANGED] = @@ -1583,9 +1701,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 3015b5c5..c3d08b64 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)) @@ -54,11 +53,12 @@ /* Internal non-exported properties */ #define NM_ACTIVE_CONNECTION_INT_SETTINGS_CONNECTION "int-settings-connection" #define NM_ACTIVE_CONNECTION_INT_APPLIED_CONNECTION "int-applied-connection" -#define NM_ACTIVE_CONNECTION_INT_DEVICE "int-device" -#define NM_ACTIVE_CONNECTION_INT_SUBJECT "int-subject" -#define NM_ACTIVE_CONNECTION_INT_MASTER "int-master" -#define NM_ACTIVE_CONNECTION_INT_MASTER_READY "int-master-ready" -#define NM_ACTIVE_CONNECTION_INT_ACTIVATION_TYPE "int-activation-type" +#define NM_ACTIVE_CONNECTION_INT_DEVICE "int-device" +#define NM_ACTIVE_CONNECTION_INT_SUBJECT "int-subject" +#define NM_ACTIVE_CONNECTION_INT_MASTER "int-master" +#define NM_ACTIVE_CONNECTION_INT_MASTER_READY "int-master-ready" +#define NM_ACTIVE_CONNECTION_INT_ACTIVATION_TYPE "int-activation-type" +#define NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON "int-activation-reason" /* Signals */ #define NM_ACTIVE_CONNECTION_STATE_CHANGED "state-changed" @@ -71,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 @@ -80,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. @@ -185,6 +185,8 @@ void nm_active_connection_set_parent (NMActiveConnection *self, NMActivationType nm_active_connection_get_activation_type (NMActiveConnection *self); +NMActivationReason nm_active_connection_get_activation_reason (NMActiveConnection *self); + void nm_active_connection_clear_secrets (NMActiveConnection *self); #endif /* __NETWORKMANAGER_ACTIVE_CONNECTION_H__ */ diff --git a/src/nm-audit-manager.c b/src/nm-audit-manager.c index 2d75c0e7..45d068f9 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) 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..199a2e40 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 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..a9921dbd 100644 --- a/src/nm-auth-subject.h +++ b/src/nm-auth-subject.h @@ -68,10 +68,6 @@ 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..8ba19db9 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 d606dee3..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" /*****************************************************************************/ @@ -47,38 +47,38 @@ typedef struct { NMDeviceState state; bool realized:1; NMUnmanFlagOp unmanaged_explicit; + 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) /*****************************************************************************/ @@ -102,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 * @@ -183,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 */ @@ -301,10 +334,11 @@ activate: device, subject, NM_ACTIVATION_TYPE_MANAGED, + dev_checkpoint->activation_reason, &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; @@ -349,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); } } @@ -373,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; @@ -382,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); @@ -391,25 +425,23 @@ 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)); } return dev_checkpoint; @@ -428,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 @@ -436,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); @@ -464,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++) { @@ -514,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; @@ -532,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; @@ -569,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..5f19eabe 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,61 @@ 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 +1080,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 +1152,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 +1187,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 +1238,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 +1260,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 +1298,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 +1366,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..a11b38b2 100644 --- a/src/nm-config-data.h +++ b/src/nm-config-data.h @@ -188,6 +188,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 +206,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..bef05fc7 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 @@ -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,15 +2397,15 @@ _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, changes, 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 4ccc5719..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 * 1000, 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,33 +354,34 @@ 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); - if (what & CURL_POLL_IN) - condition |= G_IO_IN; - if (what & CURL_POLL_OUT) - condition |= G_IO_OUT; + if (what == CURL_POLL_IN) + condition = G_IO_IN; + else if (what == CURL_POLL_OUT) + condition = G_IO_OUT; + else if (what == CURL_POLL_INOUT) + condition = G_IO_IN | G_IO_OUT; - fdp->ev = g_io_add_watch (fdp->ch, condition, curl_socketevent_cb, self); - curl_multi_assign (priv->curl_mhandle, s, fdp); + if (condition) + fdp->ev = g_io_add_watch (fdp->ch, condition, curl_socketevent_cb, self); } 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; } @@ -290,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; } @@ -314,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 @@ -452,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; @@ -471,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); @@ -479,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 @@ -496,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 @@ -531,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); } @@ -552,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..b97be0b8 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: @@ -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; @@ -2747,7 +2571,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 @@ -3749,7 +3573,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 +3582,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 +3718,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 +3730,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 * @@ -4329,6 +4156,21 @@ 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; +} + /*****************************************************************************/ 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..ec9e2947 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" /*****************************************************************************/ @@ -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); @@ -410,12 +413,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); @@ -450,4 +452,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..3e369129 --- /dev/null +++ b/src/nm-dbus-manager.c @@ -0,0 +1,1626 @@ +/* -*- 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; +} 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) +{ + 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")) { + NMDBusManager *self = nm_dbus_object_get_manager (obj); + NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + const NMDBusPropertyInfoExtended *property_info = NULL; + const char *property_interface; + const char *property_name; + gs_unref_variant GVariant *value = NULL; + + 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; + } + + 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); + + 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) + _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) { + 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, + ), +); + +/*****************************************************************************/ + +gboolean +nm_dbus_manager_start (NMDBusManager *self, + NMDBusManagerSetPropertyHandler set_property_handler, + gpointer set_property_handler_data) +{ + 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; + NMDBusObject *obj; + + g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE); + + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + priv->set_property_handler = set_property_handler; + priv->set_property_handler_data = set_property_handler_data; + + g_return_val_if_fail (!priv->connection, FALSE); + + /* 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 aquire 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 ("aquired D-Bus service \"%s\"", NM_DBUS_SERVICE); + + c_list_for_each_entry (obj, &priv->objects_lst_head, internal.objects_lst) + _obj_register (self, obj); + + return TRUE; +} + +/*****************************************************************************/ + +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..617f8a67 --- /dev/null +++ b/src/nm-dbus-manager.h @@ -0,0 +1,107 @@ +/* -*- 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_start (NMDBusManager *self, + NMDBusManagerSetPropertyHandler handler, + gpointer handler_data); + +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..514fda49 --- /dev/null +++ b/src/nm-dbus-object.c @@ -0,0 +1,320 @@ +/* -*- 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" + +/*****************************************************************************/ + +static gboolean quitting = FALSE; + +void +nm_dbus_object_set_quitting (void) +{ + nm_assert (!quitting); + quitting = TRUE; +} + +/*****************************************************************************/ + +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 (!quitting) + 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..43613630 --- /dev/null +++ b/src/nm-dbus-object.h @@ -0,0 +1,202 @@ +/* -*- 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" + +/*****************************************************************************/ + +void nm_dbus_object_set_quitting (void); + +/*****************************************************************************/ + +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..e7e930e9 --- /dev/null +++ b/src/nm-dbus-utils.h @@ -0,0 +1,212 @@ +/* -*- 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); +} 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..08becf3c 100644 --- a/src/nm-dhcp4-config.c +++ b/src/nm-dhcp4-config.c @@ -26,9 +26,7 @@ #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" /*****************************************************************************/ @@ -41,15 +39,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) @@ -147,17 +145,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 +179,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..5bb6c740 100644 --- a/src/nm-dhcp6-config.c +++ b/src/nm-dhcp6-config.c @@ -26,9 +26,7 @@ #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" /*****************************************************************************/ @@ -41,15 +39,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) @@ -145,17 +143,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 +177,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..235134c8 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); } @@ -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) { 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..134a46f7 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" /*****************************************************************************/ diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index 1493ef19..601c72ac 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,34 @@ nm_config_get_configure_and_quit (NMConfig *config) return TRUE; } -NMBusManager * -nm_bus_manager_get (void) +NMDBusManager * +nm_dbus_manager_get (void) +{ + return NULL; +} + +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..0a9591d2 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); @@ -2456,6 +2519,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 +2884,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 +2897,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 +2968,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 +3144,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 +3192,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 +3302,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..1c5222df 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" @@ -154,11 +155,12 @@ 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,6 +173,7 @@ 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); @@ -186,6 +189,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 +200,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 +228,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); @@ -338,7 +355,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 +367,217 @@ 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); +} + +#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..c76da995 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; @@ -2403,9 +2467,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 +2480,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 +2777,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 +2880,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..cd01ed02 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" @@ -101,12 +100,13 @@ 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, @@ -133,6 +133,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 +149,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..36ba6a5a 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 { \ diff --git a/src/nm-manager.c b/src/nm-manager.c index 8391dbf2..1ccfad8e 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -30,7 +30,7 @@ #include #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 +56,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 { @@ -80,8 +78,6 @@ enum { INTERNAL_DEVICE_ADDED, DEVICE_REMOVED, INTERNAL_DEVICE_REMOVED, - STATE_CHANGED, - CHECK_PERMISSIONS, ACTIVE_CONNECTION_ADDED, ACTIVE_CONNECTION_REMOVED, CONFIGURE_QUIT, @@ -114,6 +110,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMManager, PROP_METERED, PROP_GLOBAL_DNS_CONFIGURATION, PROP_ALL_DEVICES, + PROP_CHECKPOINTS, /* Not exported */ PROP_SLEEPING, @@ -131,16 +128,14 @@ typedef struct { 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 +168,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,15 +268,27 @@ 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, NMDevice *device, NMAuthSubject *subject, NMActivationType activation_type, + NMActivationReason activation_reason, GError **error); static void policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data); @@ -318,6 +329,8 @@ static NMActiveConnection *active_connection_find_first (NMManager *self, const char *uuid, NMActiveConnectionState max_state); +static NMConnectivity *concheck_get_mgr (NMManager *self); + /*****************************************************************************/ static NM_CACHED_QUARK_FCN ("active-connection-add-and-activate", active_connection_add_and_activate_quark) @@ -326,6 +339,73 @@ 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; +} + +/*****************************************************************************/ + typedef struct { int ifindex; guint32 aspired_metric; @@ -434,10 +514,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 (); } } @@ -530,7 +610,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: @@ -577,7 +657,7 @@ _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, @@ -603,7 +683,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); @@ -613,7 +693,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); @@ -709,8 +789,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); @@ -774,7 +854,7 @@ _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); } @@ -792,18 +872,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; } /*****************************************************************************/ @@ -811,8 +893,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 @@ -883,28 +971,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; } @@ -916,27 +1007,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; } @@ -945,19 +1040,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; } @@ -965,16 +1065,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; } @@ -1002,12 +1101,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; @@ -1062,22 +1160,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) { @@ -1148,26 +1230,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 @@ -1180,6 +1274,10 @@ manager_device_state_changed (NMDevice *device, NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + if ( old_state == NM_DEVICE_STATE_UNMANAGED + && new_state > NM_DEVICE_STATE_UNMANAGED) + retry_connections_for_parent_device (self, device); + switch (new_state) { case NM_DEVICE_STATE_UNMANAGED: case NM_DEVICE_STATE_UNAVAILABLE: @@ -1205,7 +1303,7 @@ static void check_if_startup_complete (NMManager *self) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GSList *iter; + NMDevice *device; if (!priv->startup) return; @@ -1218,12 +1316,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; } } @@ -1235,8 +1331,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); } @@ -1268,18 +1364,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; + } } } @@ -1319,7 +1415,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); @@ -1337,8 +1434,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 */ @@ -1348,7 +1444,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); } @@ -1377,7 +1473,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); @@ -1410,9 +1506,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; @@ -1528,18 +1622,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; @@ -1575,9 +1666,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; @@ -1593,17 +1684,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; } } @@ -1799,7 +1888,7 @@ 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)) { @@ -1824,10 +1913,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 @@ -1873,7 +1962,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) @@ -1886,9 +1975,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); @@ -2031,7 +2118,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 @@ -2047,7 +2134,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 */ @@ -2060,15 +2146,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); @@ -2112,13 +2196,14 @@ get_existing_connection (NMManager *self, gs_unref_object NMConnection *connection = NULL; NMSettingsConnection *added = NULL; GError *error = NULL; + gs_free_error GError *gen_error = NULL; NMDevice *master = NULL; int ifindex = nm_device_get_ifindex (device); NMSettingsConnection *matched; NMSettingsConnection *connection_checked = NULL; gboolean assume_state_guess_assume = FALSE; const char *assume_state_connection_uuid = NULL; - gboolean maybe_later; + gboolean maybe_later, only_by_uuid = FALSE; if (out_generated) *out_generated = FALSE; @@ -2151,14 +2236,18 @@ get_existing_connection (NMManager *self, * update_connection() implemented, otherwise nm_device_generate_connection() * returns NULL. */ - connection = nm_device_generate_connection (device, master, &maybe_later, &error); + connection = nm_device_generate_connection (device, master, &maybe_later, &gen_error); if (!connection) { - if (!maybe_later) + if (maybe_later) { + /* The device can generate a connection, but it failed for now. + * Give it a chance to match a connection from the state file. */ + only_by_uuid = TRUE; + } else { nm_device_assume_state_reset (device); - _LOG2D (LOGD_DEVICE, device, "assume: cannot generate connection: %s", - error->message); - g_error_free (error); - return NULL; + _LOG2D (LOGD_DEVICE, device, "assume: cannot generate connection: %s", + gen_error->message); + return NULL; + } } nm_device_assume_state_get (device, @@ -2179,21 +2268,31 @@ get_existing_connection (NMManager *self, && !active_connection_find_first (self, connection_checked, NULL, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) && nm_device_check_connection_compatible (device, NM_CONNECTION (connection_checked))) { - NMConnection *const connections[] = { - NM_CONNECTION (connection_checked), - NULL, - }; - - matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection (connections, - connection, - TRUE, - nm_device_has_carrier (device), - nm_device_get_route_metric (device, AF_INET), - nm_device_get_route_metric (device, AF_INET6), - NULL, NULL)); + + if (connection) { + NMConnection *const connections[] = { + NM_CONNECTION (connection_checked), + NULL, + }; + + matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection (connections, + connection, + TRUE, + nm_device_has_carrier (device), + nm_device_get_route_metric (device, AF_INET), + nm_device_get_route_metric (device, AF_INET6), + NULL, NULL)); + } else + matched = connection_checked; } else matched = NULL; + if (!matched && only_by_uuid) { + _LOG2D (LOGD_DEVICE, device, "assume: cannot generate connection: %s", + gen_error->message); + return NULL; + } + if (!matched && assume_state_guess_assume) { gs_free NMSettingsConnection **connections = NULL; guint len, i, j; @@ -2250,8 +2349,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; @@ -2315,14 +2414,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, &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); @@ -2373,18 +2478,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 @@ -2406,35 +2510,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; @@ -2447,7 +2572,6 @@ device_connectivity_changed (NMDevice *device, nm_dispatcher_call_connectivity (priv->connectivity_state, NULL, NULL, NULL); } } -#endif static void _device_realize_finish (NMManager *self, @@ -2493,6 +2617,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); @@ -2509,18 +2634,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), @@ -2554,11 +2681,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, @@ -2587,7 +2712,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); @@ -2634,12 +2759,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; @@ -2669,9 +2793,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); @@ -2679,8 +2804,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; @@ -2907,12 +3031,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 * @@ -2921,9 +3045,10 @@ 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); NMActiveConnection *ac; NMDevice *act_device; + NMDevice *device; NMDeviceCheckConAvailableFlags flags; ac = active_connection_find_first_by_connection (self, connection); @@ -2936,9 +3061,7 @@ nm_manager_get_best_device_for_connection (NMManager *self, flags = for_user_request ? NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST : NM_DEVICE_CHECK_CON_AVAILABLE_NONE; /* 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; @@ -2951,67 +3074,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 @@ -3084,7 +3240,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); @@ -3113,10 +3268,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,6 +3309,7 @@ find_master (NMManager *self, * @device: the #NMDevice, if any, which will activate @connection * @master_connection: the master connection, or %NULL * @master_device: the master device, or %NULL + * @activation_reason: the reason for activation * @error: the error, if an error occurred * * Determines whether a given #NMConnection depends on another connection to @@ -3181,12 +3337,12 @@ ensure_master_active_connection (NMManager *self, NMDevice *device, NMSettingsConnection *master_connection, NMDevice *master_device, + NMActivationReason activation_reason, GError **error) { 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); @@ -3244,6 +3400,7 @@ ensure_master_active_connection (NMManager *self, master_device, subject, NM_ACTIVATION_TYPE_MANAGED, + activation_reason, error); return master_ac; } @@ -3262,12 +3419,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; @@ -3276,7 +3431,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) @@ -3290,6 +3444,7 @@ ensure_master_active_connection (NMManager *self, candidate, subject, NM_ACTIVATION_TYPE_MANAGED, + activation_reason, error); return master_ac; } @@ -3341,7 +3496,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 @@ -3512,6 +3667,7 @@ autoconnect_slaves (NMManager *self, slave->device, subject, NM_ACTIVATION_TYPE_MANAGED, + NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES, &local_err); if (local_err) { _LOGW (LOGD_CORE, "Slave connection activation failed: %s", local_err->message); @@ -3526,11 +3682,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; } @@ -3625,7 +3781,6 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * 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); @@ -3633,14 +3788,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 @@ -3648,16 +3803,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; } @@ -3669,6 +3821,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; @@ -3686,7 +3841,10 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * } parent_ac = nm_manager_activate_connection (self, parent_con, NULL, NULL, parent, - subject, NM_ACTIVATION_TYPE_MANAGED, error); + subject, + NM_ACTIVATION_TYPE_MANAGED, + nm_active_connection_get_activation_reason (active), + error); if (!parent_ac) { g_prefix_error (error, "%s failed to activate parent: ", nm_device_get_iface (device)); return FALSE; @@ -3747,6 +3905,7 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * device, master_connection, master_device, + nm_active_connection_get_activation_reason (active), error); if (!master_ac) { if (master_device) { @@ -3773,7 +3932,7 @@ _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 */ @@ -3844,102 +4003,96 @@ _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, - 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)), - subject); -} - static NMActiveConnection * _new_active_connection (NMManager *self, + gboolean is_vpn, NMConnection *connection, NMConnection *applied, const char *specific_object, NMDevice *device, NMAuthSubject *subject, NMActivationType activation_type, + 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_first_by_connection (self, connection)) { + 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, - 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, specific_object, subject, activation_type, + activation_reason, device); } @@ -3958,13 +4111,17 @@ _internal_activation_auth_done (NMActiveConnection *active, priv->authorizing_connections = g_slist_remove (priv->authorizing_connections, active); - /* Don't continue with the activation if an equivalent active connection - * already exists. We also check this earlier, but there we may fail to + 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(). */ - if ( success - && nm_auth_subject_is_internal (nm_active_connection_get_subject (active))) { + if ( nm_auth_subject_is_internal (nm_active_connection_get_subject (active)) + && nm_active_connection_get_activation_reason (active) != NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES) { 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) @@ -3976,17 +4133,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, @@ -4003,6 +4158,7 @@ _internal_activation_auth_done (NMActiveConnection *active, * @subject: the subject which requested activation * @activation_type: whether to assume the connection. That is, take over gracefully, * non-destructible. + * @activation_reason: the reason for activation * @error: return location for an error * * Begins a new internally-initiated activation of @connection on @device. @@ -4023,29 +4179,27 @@ nm_manager_activate_connection (NMManager *self, NMDevice *device, NMAuthSubject *subject, NMActivationType activation_type, + NMActivationReason activation_reason, GError **error) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMManagerPrivate *priv; NMActiveConnection *active; - char *error_desc = NULL; GSList *iter; - 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); + g_return_val_if_fail (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 @@ -4056,25 +4210,29 @@ nm_manager_activate_connection (NMManager *self, active = iter->data; if ( connection == nm_active_connection_get_settings_connection (active) - && g_strcmp0 (nm_active_connection_get_specific_object (active), specific_object) == 0 + && nm_streq0 (nm_active_connection_get_specific_object (active), specific_object) && 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_auth_subject_is_internal (subject) + && nm_active_connection_get_activation_reason (active) == activation_reason) return active; } active = _new_active_connection (self, + _connection_is_vpn (NM_CONNECTION (connection)), NM_CONNECTION (connection), applied, specific_object, device, subject, 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; + + priv->authorizing_connections = g_slist_prepend (priv->authorizing_connections, active); + nm_active_connection_authorize (active, NULL, _internal_activation_auth_done, self, NULL); return active; } @@ -4083,9 +4241,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 @@ -4101,17 +4262,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); @@ -4123,76 +4283,80 @@ 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); } /*****************************************************************************/ @@ -4214,24 +4378,27 @@ _activation_auth_done (NMActiveConnection *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 (context, + 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, @@ -4242,12 +4409,15 @@ _activation_auth_done (NMActiveConnection *active, } 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; @@ -4255,14 +4425,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, @@ -4297,7 +4468,7 @@ impl_manager_activate_connection (NMManager *self, } subject = validate_activation_request (self, - context, + invocation, NM_CONNECTION (connection), device_path, &device, @@ -4307,12 +4478,14 @@ impl_manager_activate_connection (NMManager *self, goto error; active = _new_active_connection (self, + is_vpn, NM_CONNECTION (connection), NULL, specific_object_path, device, subject, NM_ACTIVATION_TYPE_MANAGED, + NM_ACTIVATION_REASON_USER_REQUEST, &error); if (!active) goto error; @@ -4324,7 +4497,7 @@ impl_manager_activate_connection (NMManager *self, NULL, _activation_auth_done, self, - context); + invocation); return; error: @@ -4332,7 +4505,7 @@ 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); } /*****************************************************************************/ @@ -4353,7 +4526,7 @@ activation_add_done (NMSettings *settings, 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; @@ -4372,8 +4545,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, @@ -4386,6 +4559,7 @@ activation_add_done (NMSettings *settings, } nm_assert (error); + nm_active_connection_set_state_fail (active, NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN, error->message); @@ -4398,7 +4572,6 @@ activation_add_done (NMSettings *settings, NULL, nm_active_connection_get_subject (active), error->message); - g_clear_error (&local); } static void @@ -4413,27 +4586,12 @@ _add_and_activate_auth_done (NMActiveConnection *active, GDBusMethodInvocation *context = user_data2; AddAndActivateInfo *info; GError *error = NULL; + gs_unref_object NMConnection *connection = 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); + connection = g_object_steal_qdata (G_OBJECT (active), + active_connection_add_and_activate_quark ()); - /* 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); @@ -4444,32 +4602,50 @@ _add_and_activate_auth_done (NMActiveConnection *active, nm_active_connection_get_subject (active), error->message); g_dbus_method_invocation_take_error (context, error); + g_object_unref (active); + return; } - g_object_unref (active); + info = g_slice_new (AddAndActivateInfo); + info->manager = self; + + /* we pass on the reference to @active. */ + info->active = 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); } 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, @@ -4483,29 +4659,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, @@ -4518,7 +4681,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, @@ -4528,42 +4691,44 @@ 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, device, subject, NM_ACTIVATION_TYPE_MANAGED, + NM_ACTIVATION_REASON_USER_REQUEST, &error); if (!active) goto error; + /* FIXME: nm_active_connection_authorize() already has two user-data pointers + * to piggyback additional data. Instead of attaching the third argument to + * @active's user-data, add a third paramter. + * Or alternatively, allocate a data structure to pass on additional data. + * Then we don't need two user-data pointers. */ g_object_set_qdata_full (G_OBJECT (active), active_connection_add_and_activate_quark (), connection, g_object_unref); - nm_active_connection_authorize (active, connection, _add_and_activate_auth_done, self, context); - g_object_unref (subject); + nm_active_connection_authorize (active, connection, _add_and_activate_auth_done, self, invocation); + + /* we passed the pointers on to the callback of authorize. */ + 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); } /*****************************************************************************/ @@ -4659,21 +4824,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); @@ -4688,7 +4860,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, @@ -4696,19 +4868,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, @@ -4726,7 +4894,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); } @@ -4834,7 +5002,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; @@ -4845,9 +5013,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; @@ -4875,9 +5041,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; @@ -4904,8 +5068,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)) { @@ -4998,27 +5161,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, @@ -5026,7 +5190,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; } @@ -5040,22 +5204,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 @@ -5131,21 +5281,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, @@ -5154,7 +5308,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, @@ -5168,7 +5322,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 */ @@ -5235,23 +5389,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; } @@ -5275,83 +5434,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 @@ -5365,7 +5564,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); @@ -5381,39 +5580,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; + } + + 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; - for (devices = priv->devices; devices; devices = devices->next) { + 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; } @@ -5430,15 +5649,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; @@ -5579,10 +5797,10 @@ void nm_manager_stop (NMManager *self) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *device; - /* 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); @@ -5594,21 +5812,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); } @@ -5727,318 +5944,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); } /*****************************************************************************/ @@ -6049,7 +6081,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; } @@ -6068,13 +6100,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) { @@ -6098,7 +6132,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)) { @@ -6107,6 +6141,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 (); } @@ -6119,63 +6157,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); @@ -6183,39 +6229,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 @@ -6340,14 +6426,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 @@ -6414,8 +6492,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; } @@ -6428,11 +6505,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); @@ -6500,6 +6575,7 @@ 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->delete_volatile_connection_lst_head); @@ -6531,12 +6607,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, @@ -6571,13 +6641,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 @@ -6592,7 +6656,6 @@ get_property (GObject *object, guint prop_id, const char *path; NMActiveConnection *ac; GPtrArray *ptrarr; - gboolean vbool; switch (prop_id) { case PROP_VERSION: @@ -6634,7 +6697,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)); } @@ -6649,15 +6712,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; @@ -6671,13 +6729,15 @@ get_property (GObject *object, guint prop_id, g_value_set_string (value, type ? 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); @@ -6688,7 +6748,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); @@ -6755,11 +6824,6 @@ 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)); - g_signal_handlers_disconnect_by_func (priv->platform, G_CALLBACK (platform_link_cb), self); @@ -6771,14 +6835,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) { @@ -6788,7 +6856,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); @@ -6825,13 +6893,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); @@ -6854,6 +6915,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); @@ -6875,22 +6941,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, @@ -6898,11 +7216,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, "", "", @@ -7053,11 +7371,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), @@ -7073,7 +7397,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, @@ -7090,20 +7414,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), @@ -7124,25 +7434,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 b4587e08..54444532 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,18 +55,16 @@ #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" @@ -85,13 +83,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 +106,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); @@ -136,6 +147,7 @@ NMActiveConnection *nm_manager_activate_connection (NMManager *manager, NMDevice *device, NMAuthSubject *subject, NMActivationType activation_type, + NMActivationReason activation_reason, GError **error); gboolean nm_manager_deactivate_connection (NMManager *manager, @@ -152,4 +164,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..caae8b20 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" @@ -466,19 +466,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 b73e04cb..55b6caf6 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) @@ -1195,7 +1192,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)); @@ -1255,6 +1252,7 @@ auto_activate_device (NMPolicy *self, device, subject, NM_ACTIVATION_TYPE_MANAGED, + NM_ACTIVATION_REASON_AUTOCONNECT, &error); if (!ac) { _LOGI (LOGD_DEVICE, "connection '%s' auto-activation failed: %s", @@ -1271,13 +1269,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 @@ -1361,7 +1357,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); @@ -1377,7 +1373,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); @@ -1673,10 +1669,11 @@ 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, + nm_active_connection_get_activation_reason (NM_ACTIVE_CONNECTION (req)), &error); if (ac) secondary_ac_list = g_slist_append (secondary_ac_list, g_object_ref (ac)); @@ -1711,7 +1708,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; @@ -1808,10 +1804,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); @@ -1904,50 +1900,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__); - - /* 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); - } + int addr_family; - nm_dns_manager_end_updates (priv->dns_manager, __func__); -} + nm_assert (new_config || old_config); + nm_assert (!new_config || NM_IS_IP_CONFIG (new_config)); + nm_assert (!old_config || NM_IS_IP_CONFIG (old_config)); -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__); @@ -1958,18 +1928,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__); @@ -2012,8 +1988,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); @@ -2030,7 +2006,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); @@ -2068,21 +2044,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); @@ -2099,16 +2070,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); @@ -2148,6 +2115,7 @@ vpn_connection_retry_after_failure (NMVpnConnection *vpn, NMPolicy *self) NULL, nm_active_connection_get_subject (ac), NM_ACTIVATION_TYPE_MANAGED, + nm_active_connection_get_activation_reason (ac), &error)) { _LOGW (LOGD_DEVICE, "VPN '%s' reconnect failed: %s", nm_settings_connection_get_id (connection), @@ -2217,12 +2185,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; } @@ -2256,7 +2225,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 @@ -2269,8 +2239,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 @@ -2315,14 +2285,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; @@ -2350,10 +2319,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, @@ -2388,7 +2356,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 @@ -2520,8 +2488,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-session-monitor.c b/src/nm-session-monitor.c index 20781bd4..13ccbd48 100644 --- a/src/nm-session-monitor.c +++ b/src/nm-session-monitor.c @@ -137,7 +137,10 @@ st_sd_init (NMSessionMonitor *monitor) static void st_sd_finalize (NMSessionMonitor *monitor) { - g_clear_pointer (&monitor->sd.monitor, sd_login_monitor_unref); + if (monitor->sd.monitor) { + sd_login_monitor_unref (monitor->sd.monitor); + monitor->sd.monitor = NULL; + } g_source_remove (monitor->sd.watch); } #endif /* SESSION_TRACKING_SYSTEMD */ @@ -258,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-test-utils-core.h b/src/nm-test-utils-core.h index 58beadcd..da24992e 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) { @@ -313,18 +321,6 @@ 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 @@ -340,18 +336,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 02163f87..e1991c28 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; @@ -75,6 +74,13 @@ typedef enum { NM_ACTIVATION_TYPE_EXTERNAL = 2, } 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, +} NMActivationReason; + typedef enum { /* In priority order; higher number == higher priority */ @@ -95,6 +101,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, @@ -157,7 +164,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, @@ -191,6 +197,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, @@ -231,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..0ed8fa06 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 */); @@ -283,7 +292,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 +306,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 +333,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 +350,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 +550,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 +811,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 +951,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 +1161,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 +1196,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 +1377,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, @@ -1936,6 +1862,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 +2594,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 +2636,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 +2748,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 +2845,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 +2919,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 +2960,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 +3421,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 +3503,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 +3737,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 +3745,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 +3930,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 +3977,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 +4030,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 +4044,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 +4095,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 +4114,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 +4133,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 +4171,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 +4230,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 +4246,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 +4290,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 +4309,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 +4358,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 +4376,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 +4514,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 +4541,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 +4558,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 +4568,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 +4589,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 +4610,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 +4618,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 +4650,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 +4681,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 +4697,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 +4754,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 +4831,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 +5369,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 +5592,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 +5888,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 +6429,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 +6468,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 +6477,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 +6492,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 +6508,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 +6517,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 +6532,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 +6563,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 +6576,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 +6606,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 +6623,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 +6637,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 +6652,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 +6679,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 +6729,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 +6749,9 @@ stop: * Repeat reading. */ goto continue_reading; } -out: + if (interrupted) - err = -NLE_DUMP_INTR; + return -NLE_DUMP_INTR; return err; } @@ -6800,46 +6762,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 +6829,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 +6856,7 @@ after_read: /* timeout and there is nothing to read. */ goto after_read; } + if (r < 0) { int errsv = errno; @@ -7047,11 +6998,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 +7047,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 +7130,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 +7181,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 +7220,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 +7244,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.c b/src/platform/nm-platform.c index c7ed90e3..84d862d2 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; } @@ -2620,44 +2686,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 +3054,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 +3233,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 (IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address) && candidate->plen == address->plen) { - guint32 lifetime, preferred; + 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 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 +3345,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 +3478,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 +3576,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, @@ -3464,9 +3600,15 @@ delete_and_next2: * 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 +3619,117 @@ 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 */ + 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 +3740,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 +3779,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 +3918,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 +3939,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 +3965,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 +3990,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 +4004,66 @@ 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) { + .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) { + .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; } } @@ -4713,6 +4987,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 +4996,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 +5117,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) { @@ -5043,10 +5356,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, @@ -5537,7 +5850,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 +5866,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 +5984,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 +6607,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..e6cef63b 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; \ @@ -402,7 +434,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. */ \ @@ -604,6 +636,7 @@ typedef struct { guint8 encap_limit; guint8 proto; guint flow_label; + guint32 flags; } NMPlatformLnkIp6Tnl; typedef struct { @@ -650,6 +683,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 +725,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 +753,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 +850,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 +1072,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 +1117,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 +1143,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 +1196,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 +1224,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 +1234,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 +1284,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 +1313,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 +1372,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 +1396,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 +1432,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..29bb999b 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); @@ -2735,6 +2780,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 e3862f2d..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; @@ -457,11 +464,12 @@ nmp_object_ref (const NMPObject *obj) return (const NMPObject *) nm_dedup_multi_obj_ref ((const NMDedupMultiObj *) obj); } -static inline const NMPObject * +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); - return NULL; } #define nm_clear_nmp_object(ptr) \ @@ -482,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) @@ -500,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); @@ -725,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..7885c083 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,6 +23,7 @@ #include #include #include +#include #include "test-common.h" @@ -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 @@ -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, 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-link.c b/src/platform/tests/test-link.c index ef78cc24..dcd600ee 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,27 @@ 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); + + g_assert (nm_platform_lnk_tun_cmp (plnk, &lnk_tun) == 0); + break; + } case NM_LINK_TYPE_VLAN: { const NMPlatformLnkVlan *plnk = &lnk->lnk_vlan; @@ -2248,7 +2339,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; } @@ -2546,11 +2637,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..db187a1f 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,7 +248,7 @@ 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); @@ -481,14 +259,14 @@ 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); @@ -518,7 +296,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 +396,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 +520,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)); @@ -790,35 +568,38 @@ 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 +628,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 +845,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 +870,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 +880,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 +936,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..2633e965 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); diff --git a/src/ppp/meson.build b/src/ppp/meson.build new file mode 100644 index 00000000..20edb9d0 --- /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_pkglibdir +) diff --git a/src/ppp/nm-ppp-manager-call.c b/src/ppp/nm-ppp-manager-call.c index ad3307a9..ed70f687 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"); diff --git a/src/ppp/nm-ppp-manager.c b/src/ppp/nm-ppp-manager.c index 743f80a2..7d1eb408 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) /*****************************************************************************/ @@ -174,24 +175,28 @@ 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; + const char *ifname; - memset (&req, 0, sizeof (req)); - memset (&stats, 0, sizeof (stats)); - req.ifr_data = (caddr_t) &stats; + ifname = nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex); - 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); + 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 (manager, signals[STATS], 0, + (guint) stats.p.ppp_ibytes, + (guint) stats.p.ppp_obytes); + } } - return TRUE; + return G_SOURCE_CONTINUE; } static void @@ -326,20 +331,27 @@ 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) { + NMPPPManager *manager = NM_PPP_MANAGER (obj); NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); NMConnection *applied_connection; const char *username = NULL; @@ -358,7 +370,7 @@ 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; + priv->pending_secrets_context = invocation; ppp_secrets_cb (priv->act_req, priv->secrets_id, NULL, NULL, manager); } else { _LOGW ("%s", error->message); @@ -383,39 +395,85 @@ impl_ppp_manager_need_secrets (NMPPPManager *manager, ppp_secrets_cb, manager); 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) { + NMPPPManager *manager = NM_PPP_MANAGER (obj); + guint32 state; + + g_variant_get (parameters, "(u)", &state); g_signal_emit (manager, signals[STATE_CHANGED], 0, (guint) state); + g_dbus_method_invocation_return_value (invocation, NULL); +} - g_dbus_method_invocation_return_value (context, 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 *manager = NM_PPP_MANAGER (obj); + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + 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 (manager, 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 +490,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) { + NMPPPManager *manager = NM_PPP_MANAGER (obj); NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); 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 (manager, 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 +528,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 +564,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 (manager, 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 +600,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) { + NMPPPManager *manager = NM_PPP_MANAGER (obj); NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); 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 (manager, 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 +651,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 (manager, 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); } /*****************************************************************************/ @@ -899,7 +963,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); @@ -978,7 +1042,7 @@ _ppp_manager_start (NMPPPManager *manager, return FALSE; #endif - nm_exported_object_export (NM_EXPORTED_OBJECT (manager)); + nm_dbus_object_export (NM_DBUS_OBJECT (manager)); priv->pid = 0; @@ -1052,7 +1116,7 @@ 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 (manager)); return priv->pid > 0; } @@ -1156,7 +1220,7 @@ _ppp_manager_stop_async (NMPPPManager *manager, NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); StopContext *ctx; - nm_exported_object_unexport (NM_EXPORTED_OBJECT (manager)); + nm_dbus_object_unexport (NM_DBUS_OBJECT (manager)); ctx = g_slice_new0 (StopContext); ctx->manager = g_object_ref (manager); @@ -1193,10 +1257,10 @@ _ppp_manager_stop_async (NMPPPManager *manager, static void _ppp_manager_stop_sync (NMPPPManager *manager) { - NMExportedObject *exported = NM_EXPORTED_OBJECT (manager); + NMDBusObject *dbus = NM_DBUS_OBJECT (manager); - if (nm_exported_object_is_exported (exported)) - nm_exported_object_unexport (exported); + if (nm_dbus_object_is_exported (dbus)) + nm_dbus_object_unexport (dbus); _ppp_cleanup (manager); _ppp_kill (manager); @@ -1244,6 +1308,7 @@ nm_ppp_manager_init (NMPPPManager *manager) { NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + priv->ifindex = -1; priv->monitor_fd = -1; priv->ip4_route_table = RT_TABLE_MAIN; priv->ip4_route_metric = 460; @@ -1265,11 +1330,11 @@ static void dispose (GObject *object) { NMPPPManager *self = (NMPPPManager *) object; - NMExportedObject *exported = NM_EXPORTED_OBJECT (self); + NMDBusObject *dbus = NM_DBUS_OBJECT (self); NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self); - if (nm_exported_object_is_exported (exported)) - nm_exported_object_unexport (exported); + if (nm_dbus_object_is_exported (dbus)) + nm_dbus_object_unexport (dbus); _ppp_cleanup (self); _ppp_kill (self); @@ -1284,24 +1349,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 +1439,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 +1464,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,14 +1477,6 @@ 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 = { diff --git a/src/ppp/nm-ppp-manager.h b/src/ppp/nm-ppp-manager.h index 35fb1b60..5457726e 100644 --- a/src/ppp/nm-ppp-manager.h +++ b/src/ppp/nm-ppp-manager.h @@ -25,6 +25,7 @@ #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" 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..0b998372 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) ) @@ -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)); @@ -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); @@ -1295,7 +1300,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 +1364,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 +1372,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; + + auth_data->call_id = NULL; - perm = nm_auth_chain_get_data (chain, "perm"); - g_assert (perm); - result = nm_auth_chain_get_result (chain, perm); + c_list_unlink (&auth_data->auth_lst); - /* If our NMSettingsConnection is already gone, do nothing */ - if (chain_error) { + 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 +1454,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)); - /* 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); + 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)); - 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); - 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); + callback (self, invocation, subject, NULL, callback_data); 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 +1536,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 +1590,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 +1802,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); } @@ -1830,7 +1850,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 +1886,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 ? 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; @@ -1901,54 +1917,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 +2006,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 +2014,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 +2023,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 +2074,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 +2082,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 +2164,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 +2219,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 +2239,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 +2283,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 +2349,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 +2357,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 +2788,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 +2883,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 +2895,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 +2985,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 +3025,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 +3045,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 +3073,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 +3102,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 +3231,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 +3244,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 +3255,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..58901699 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" @@ -53,32 +50,45 @@ /** - * 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 +116,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 +210,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.c b/src/settings/nm-settings.c index 8e3fc582..2c6b7101 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,41 @@ 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; + NMSettingsConnection *candidate = NULL; + const char *path; - 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; + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)); + if (path) + candidate = nm_settings_get_connection_by_path (self, path); + + nm_assert (!candidate || candidate == connection); + nm_assert (!!candidate == nm_c_list_contains_entry (&NM_SETTINGS_GET_PRIVATE (self)->connections_lst_head, + connection, + _connections_lst)); + return !!candidate; } const GSList * @@ -500,7 +519,7 @@ nm_settings_get_unmanaged_specs (NMSettings *self) } static NMSettingsPlugin * -get_plugin (NMSettings *self, guint32 capability) +get_plugin (NMSettings *self, NMSettingsPluginCapabilities capability) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); GSList *iter; @@ -677,8 +696,8 @@ load_plugins (NMSettings *self, const char **plugins, GError **error) 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; } @@ -806,7 +825,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 +838,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 +863,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 +934,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 +975,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 +996,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_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); + g_object_ref (connection); + priv->connections_len++; + c_list_link_tail (&priv->connections_lst_head, &connection->_connections_lst); + + 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 +1066,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 +1130,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 +1141,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 +1200,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 */ @@ -1218,7 +1248,6 @@ nm_settings_add_connection_dbus (NMSettings *self, NMAuthSubject *subject = NULL; NMAuthChain *chain; GError *error = NULL, *tmp_error = NULL; - char *error_desc = NULL; const char *perm; g_return_if_fail (connection != NULL); @@ -1261,18 +1290,12 @@ nm_settings_add_connection_dbus (NMSettings *self, 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); + if (!nm_auth_is_subject_in_acl_set_error (connection, + subject, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + &error)) 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 @@ -1312,30 +1335,30 @@ done: } 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; @@ -1355,77 +1378,111 @@ impl_settings_add_connection_helper (NMSettings *self, connection, save_to_disk, 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 (); - - for (i = 0; filenames[i]; i++) { - for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { - NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data); + 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); - 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 +1490,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 +1503,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 +1551,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 +1599,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 +1627,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 +1655,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 +1687,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 +1738,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 +1810,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 +1836,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, @@ -1825,12 +1860,14 @@ get_property (GObject *object, guint prop_id, g_value_set_boolean (value, !!get_plugin (self, NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS)); 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 +1885,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 +1903,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 +1922,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 +2123,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..7d56f7b4 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, diff --git a/src/settings/plugins/ibft/meson.build b/src/settings/plugins/ibft/meson.build new file mode 100644 index 00000000..da9f5566 --- /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_pkglibdir +) + +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/tests/meson.build b/src/settings/plugins/ibft/tests/meson.build new file mode 100644 index 00000000..7a9445dd --- /dev/null +++ b/src/settings/plugins/ibft/tests/meson.build @@ -0,0 +1,22 @@ +test_unit = 'test-ibft' + +test_ibft_dir = meson.current_source_dir() + +cflags = [ + '-DTEST_IBFT_DIR="@0@"'.format(test_ibft_dir), + '-DTEST_SCRATCH_DIR="@0@"'.format(test_ibft_dir) +] + +exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, + c_args: 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..fd2ec61b 100644 --- a/src/settings/plugins/ibft/tests/test-ibft.c +++ b/src/settings/plugins/ibft/tests/test-ibft.c @@ -181,7 +181,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); diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build new file mode 100644 index 00000000..fdf308be --- /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_pkglibdir +) + +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-plugin.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c index 0743fc9f..c7207297 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 */ @@ -327,21 +326,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 +372,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), @@ -516,7 +517,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 +750,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 +769,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 +789,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 +874,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); + IFCFGRH1_BUS_NAME, + IFCFGRH1_OBJECT_PATH); } static void @@ -900,7 +935,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 +952,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,17 +978,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; + + priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + if ( !priv->dbus.connection + && !priv->dbus.cancellable) + _dbus_setup (self); } /*****************************************************************************/ 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..6ef3f660 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) @@ -338,6 +338,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); } @@ -927,7 +934,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 +1345,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 +1372,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 +1449,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 +1538,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 +1555,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 +1640,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)) @@ -2001,11 +1999,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 +2021,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 +3558,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")) @@ -5321,6 +5334,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 +5356,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 +5366,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 +5412,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 +5426,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 +5473,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..8584772e 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); @@ -794,6 +785,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 +897,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 +1728,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 +1750,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 +1887,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 * @@ -2281,7 +2287,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 +2417,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 +2493,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 +2996,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 +3147,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 +3197,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..3596b642 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/meson.build @@ -0,0 +1,22 @@ +test_unit = 'test-ifcfg-rh' + +test_ifcfg_dir = meson.current_source_dir() + +cflags = [ + '-DTEST_IFCFG_DIR="@0@"'.format(test_ifcfg_dir), + '-DTEST_SCRATCH_DIR="@0@"'.format(test_ifcfg_dir) +] + +exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, + c_args: 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..57bd96e9 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 @@ -605,8 +606,7 @@ 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'*"); + NMTST_EXPECT_NM_WARN ("*invalid MAC in HWADDR_BLACKLIST 'XX:aa:invalid'*"); connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-misc-variables", NULL, TYPE_ETHERNET, NULL); g_test_assert_expected_messages (); @@ -859,8 +859,7 @@ test_read_wired_static_no_prefix (gconstpointer user_data) file = g_strdup_printf (TEST_IFCFG_DIR "/network-scripts/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 (); @@ -1081,8 +1080,7 @@ 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"); + NMTST_EXPECT_NM_WARN ("*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", TYPE_ETHERNET, &unmanaged); @@ -1476,6 +1474,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,8 +1512,7 @@ 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*"); + NMTST_EXPECT_NM_WARN ("*ignoring manual default route*"); connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-ipv6-manual", NULL, TYPE_ETHERNET, &unmanaged); g_test_assert_expected_messages (); @@ -1897,8 +1895,7 @@ 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*"); + NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT*peap*"); connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-802-1X-subj-matches", NULL, TYPE_ETHERNET, NULL); g_test_assert_expected_messages (); @@ -1919,16 +1916,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", &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 (); @@ -2079,7 +2074,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 +2126,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,16 +2137,14 @@ 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*"); + NMTST_EXPECT_NM_WARN ("*aliasem1:1*has no DEVICE*"); test_read_wired_aliases_bad (TEST_IFCFG_DIR "/network-scripts/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*"); + NMTST_EXPECT_NM_WARN ("*aliasem2:1*has invalid DEVICE*"); test_read_wired_aliases_bad (TEST_IFCFG_DIR "/network-scripts/ifcfg-aliasem2", "System aliasem2"); } @@ -3522,7 +3515,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); @@ -4774,6 +4766,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); @@ -5141,7 +5134,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); } @@ -5200,9 +5193,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; } } @@ -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"); @@ -7033,7 +7028,7 @@ test_write_wired_ctc_dhcp (void) TEST_SCRATCH_DIR "/network-scripts/", &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"); @@ -7713,8 +7708,7 @@ 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*"); + NMTST_EXPECT_NM_WARN ("*REORDER_HDR key is deprecated, use VLAN_FLAGS*"); connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-vlan-reorder-hdr-1", NULL, TYPE_ETHERNET, NULL); g_test_assert_expected_messages (); @@ -8508,8 +8502,7 @@ 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*"); + NMTST_EXPECT_NM_WARN ("*invalid DCB_PG_STRICT value*not all 0s and 1s*"); _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-bad-booleans", NULL, TYPE_ETHERNET, &error); g_test_assert_expected_messages (); @@ -8523,8 +8516,7 @@ 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*"); + NMTST_EXPECT_NM_WARN ("*DCB_PG_STRICT value*8 characters*"); _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-short-booleans", NULL, TYPE_ETHERNET, &error); g_test_assert_expected_messages (); @@ -8538,8 +8530,7 @@ 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*"); + NMTST_EXPECT_NM_WARN ("*invalid DCB_PG_UP2TC value*not 0 - 7*"); _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-bad-uints", NULL, TYPE_ETHERNET, &error); g_test_assert_expected_messages (); @@ -8553,8 +8544,7 @@ 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*"); + NMTST_EXPECT_NM_WARN ("*DCB_PG_UP2TC value*8 characters*"); _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-short-uints", NULL, TYPE_ETHERNET, &error); g_test_assert_expected_messages (); @@ -8568,8 +8558,7 @@ 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*"); + NMTST_EXPECT_NM_WARN ("*invalid DCB_PG_PCT percentage value*"); _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-bad-percent", NULL, TYPE_ETHERNET, &error); g_test_assert_expected_messages (); @@ -8583,8 +8572,7 @@ 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*"); + NMTST_EXPECT_NM_WARN ("*invalid DCB_PG_PCT percentage list value*"); _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-short-percent", NULL, TYPE_ETHERNET, &error); g_test_assert_expected_messages (); @@ -8598,8 +8586,7 @@ 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*"); + NMTST_EXPECT_NM_WARN ("*DCB_PG_PCT percentages do not equal 100*"); _connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-pgpct-not-100", NULL, TYPE_ETHERNET, &error); g_test_assert_expected_messages (); @@ -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 (); @@ -9645,6 +9632,125 @@ 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 "/network-scripts/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 "/network-scripts/", + TEST_IFCFG_DIR "/network-scripts/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/" @@ -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..87b5bea2 --- /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_pkglibdir +) + +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..014998a6 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c @@ -129,8 +129,8 @@ _recursive_ifparser (const char *eni_file, int quiet) 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 +169,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/tests/meson.build b/src/settings/plugins/ifupdown/tests/meson.build new file mode 100644 index 00000000..7f210034 --- /dev/null +++ b/src/settings/plugins/ifupdown/tests/meson.build @@ -0,0 +1,17 @@ +test_unit = 'test-ifupdown' + +cflags = '-DTEST_ENI_DIR="@0@"'.format(meson.current_source_dir()) + +exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, + c_args: 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..56515438 100644 --- a/src/settings/plugins/ifupdown/tests/test-ifupdown.c +++ b/src/settings/plugins/ifupdown/tests/test-ifupdown.c @@ -168,7 +168,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); diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c index e6299d1e..df2c05b7 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))) { 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..54b4ee0d --- /dev/null +++ b/src/settings/plugins/keyfile/tests/meson.build @@ -0,0 +1,21 @@ +test_unit = 'test-keyfile' + +test_keyfiles_dir = join_paths(meson.current_source_dir(), 'keyfiles') + +cflags = [ + '-DTEST_KEYFILES_DIR="@0@"'.format(test_keyfiles_dir), + '-DTEST_SCRATCH_DIR="@0@"'.format(test_keyfiles_dir) +] + +exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, + c_args: 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..b46475e8 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile.c @@ -231,34 +231,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 +281,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 +308,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); @@ -649,12 +641,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 +1404,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 +1434,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 +1462,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 +1515,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); 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..14f5cac8 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 @@ -369,7 +380,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 +389,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 +744,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 +756,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 +832,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") 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..5198d75f 100644 --- a/src/supplicant/nm-supplicant-settings-verify.c +++ b/src/supplicant/nm-supplicant-settings-verify.c @@ -73,6 +73,7 @@ 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 }; @@ -221,10 +222,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-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..e6a86b20 --- /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: '-DTEST_CERT_DIR="@0@"'.format(join_paths(meson.current_source_dir(), 'certs')) +) + +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..60ca5258 100644 --- a/src/supplicant/tests/test-supplicant-config.c +++ b/src/supplicant/tests/test-supplicant-config.c @@ -95,7 +95,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 +108,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 +122,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 +130,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); @@ -140,7 +146,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, @@ -199,12 +204,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 +254,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 +313,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 +334,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 +347,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 +396,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 +420,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 +454,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 +495,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 +577,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..fe3d88d3 --- /dev/null +++ b/src/systemd/meson.build @@ -0,0 +1,70 @@ +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/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/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..3a4125a0 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,15 @@ G_STMT_START { \ # endif #endif +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 +159,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) { @@ -186,7 +197,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/src/basic/alloc-util.c b/src/systemd/src/basic/alloc-util.c index 97588312..1a058d2f 100644 --- a/src/systemd/src/basic/alloc-util.c +++ b/src/systemd/src/basic/alloc-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -39,7 +40,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..b1e0edbb 100644 --- a/src/systemd/src/basic/alloc-util.h +++ b/src/systemd/src/basic/alloc-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -54,7 +55,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 +74,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 +130,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..01c975bb 100644 --- a/src/systemd/src/basic/async.h +++ b/src/systemd/src/basic/async.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -21,5 +22,5 @@ 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..fac618dd 100644 --- a/src/systemd/src/basic/escape.c +++ b/src/systemd/src/basic/escape.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/systemd/src/basic/escape.h b/src/systemd/src/basic/escape.h index e62347af..cd5c49fa 100644 --- a/src/systemd/src/basic/escape.h +++ b/src/systemd/src/basic/escape.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/basic/ether-addr-util.c b/src/systemd/src/basic/ether-addr-util.c index a793219c..93922a28 100644 --- a/src/systemd/src/basic/ether-addr-util.c +++ b/src/systemd/src/basic/ether-addr-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -19,6 +20,7 @@ #include "nm-sd-adapt.h" +#include #include #include #include @@ -72,7 +74,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; \ @@ -100,7 +102,7 @@ int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset sep = s[strspn(s, hex)]; if (sep == '\n') return -EINVAL; - if (strchr(":.-", sep) == NULL) + if (!strchr(":.-", sep)) return -EINVAL; if (sep == '.') { diff --git a/src/systemd/src/basic/ether-addr-util.h b/src/systemd/src/basic/ether-addr-util.h index 74e125a9..08d05a13 100644 --- a/src/systemd/src/basic/ether-addr-util.h +++ b/src/systemd/src/basic/ether-addr-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/basic/extract-word.c b/src/systemd/src/basic/extract-word.c index 69d8c48d..e52c376b 100644 --- a/src/systemd/src/basic/extract-word.c +++ b/src/systemd/src/basic/extract-word.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -195,8 +196,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..300c51bb 100644 --- a/src/systemd/src/basic/extract-word.h +++ b/src/systemd/src/basic/extract-word.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c index 1c327d83..ff480f51 100644 --- a/src/systemd/src/basic/fd-util.c +++ b/src/systemd/src/basic/fd-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -28,8 +29,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" @@ -191,12 +194,6 @@ 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; @@ -227,20 +224,21 @@ int close_all_fds(const int except[], unsigned n_except) { assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0); for (fd = 3; fd < (int) rl.rlim_max; fd ++) { + 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 +253,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 +365,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..163b096b 100644 --- a/src/systemd/src/basic/fd-util.h +++ b/src/systemd/src/basic/fd-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -34,6 +35,13 @@ int close_nointr(int fd); int safe_close(int fd); void safe_close_pair(int p[]); +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[], unsigned n_fd); int fclose_nointr(FILE *f); @@ -63,7 +71,6 @@ 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); @@ -75,6 +82,36 @@ 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) + +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..c7b4b241 100644 --- a/src/systemd/src/basic/fileio.c +++ b/src/systemd/src/basic/fileio.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -24,8 +25,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -62,12 +65,30 @@ int write_string_stream_ts( WriteStringFileFlags flags, struct timespec *ts) { + bool needs_nl; + 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 (ts) { struct timespec twice[2] = {*ts, *ts}; @@ -99,6 +120,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 +163,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 +190,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 +228,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 +255,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 +272,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 +334,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 +352,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 +366,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 +407,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 +428,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 +451,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 +466,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 +494,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 +509,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 +522,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 +535,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 +547,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 +560,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 +572,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 +618,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 +891,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 +910,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 +1162,7 @@ int fflush_and_check(FILE *f) { return 0; } +#if 0 /* NM_IGNORED */ int fflush_sync_and_check(FILE *f) { int r; @@ -1159,8 +1175,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 +1218,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 +1252,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 +1292,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 +1444,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 +1484,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 +1530,7 @@ int read_nul_string(FILE *f, char **ret) { return -ENOMEM; } - *ret = x; - x = NULL; + *ret = TAKE_PTR(x); return 0; } @@ -1538,9 +1554,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 +1581,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 (;;) { diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h index eba05be2..da5d5c66 100644 --- a/src/systemd/src/basic/fileio.h +++ b/src/systemd/src/basic/fileio.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -29,11 +30,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..ffd9996a 100644 --- a/src/systemd/src/basic/fs-util.c +++ b/src/systemd/src/basic/fs-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -40,6 +41,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 +109,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 +230,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); @@ -322,43 +280,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 +465,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 +489,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 +559,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 +584,39 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) { return r; } +static bool noop_root(const char *root) { + return isempty(root) || path_equal(root, "/"); +} + +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 */ + 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 (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. * @@ -628,18 +635,35 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this * 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. */ + * specified path. */ + + /* A root directory of "/" or "" is identical to none */ + if (noop_root(original_root)) + original_root = NULL; + + if (!original_root && !ret && (flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_OPEN)) == 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 +674,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 +697,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,7 +719,7 @@ 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. */ @@ -702,8 +742,18 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, 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 +764,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 +786,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 +822,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,12 +843,11 @@ 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; @@ -789,18 +858,20 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, } /* 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 +881,221 @@ 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); } return exists; } + +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 (noop_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 (noop_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 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..2225b7e7 100644 --- a/src/systemd/src/basic/fs-util.h +++ b/src/systemd/src/basic/fs-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -19,6 +20,7 @@ along with systemd; If not, see . ***/ +#include #include #include #include @@ -28,6 +30,7 @@ #include #include "time-util.h" +#include "util.h" int unlink_noerrno(const char *path); @@ -39,8 +42,6 @@ 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); @@ -64,6 +65,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 +82,35 @@ 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 */ }; 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); + /* 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..234565ed 100644 --- a/src/systemd/src/basic/hash-funcs.c +++ b/src/systemd/src/basic/hash-funcs.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -20,12 +21,16 @@ #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 +40,56 @@ 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 +119,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 +137,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..945b4c25 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 /*** @@ -35,29 +36,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..a76ec8e8 100644 --- a/src/systemd/src/basic/hashmap.c +++ b/src/systemd/src/basic/hashmap.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -27,12 +28,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 +231,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 +252,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 +296,28 @@ static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { }, }; +#ifdef 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 +366,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 +588,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 +758,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 +937,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 +1083,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 +1321,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 +1345,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 +1899,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..b6749103 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 /*** @@ -52,6 +53,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 +128,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 +144,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 +342,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 +407,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..e8e82b5f 100644 --- a/src/systemd/src/basic/hexdecoct.c +++ b/src/systemd/src/basic/hexdecoct.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -98,7 +99,10 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) { assert(mem); assert(len); - assert(p); + assert(p || l == 0); + + if (l == (size_t) -1) + l = strlen(p); if (l % 2 != 0) return -EINVAL; @@ -130,6 +134,7 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) { 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 +167,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; @@ -271,7 +278,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) @@ -521,6 +533,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 +571,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 +586,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 +614,148 @@ 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; - - /* strip the padding */ - if (l > 0 && p[l - 1] == '=') - l--; - if (l > 0 && p[l - 1] == '=') - l--; + /* 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. */ - /* 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); + for (;;) { + if (*l == 0) + return -EPIPE; - z = r = malloc(len + 1); - if (!r) - return -ENOMEM; + if (!strchr(WHITESPACE, **p)) + break; - 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; + /* Skip leading whitespace */ + (*p)++, (*l)--; + } - b = unbase64char(x[1]); - if (b < 0) - return -EINVAL; + if (**p == '=') + ret = INT_MAX; /* return padding as INT_MAX */ + else { + ret = unbase64char(**p); + if (ret < 0) + return ret; + } - c = unbase64char(x[2]); - if (c < 0) - return -EINVAL; + for (;;) { + (*p)++, (*l)--; - 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; + if (ret_size) + *ret_size = (size_t) (z - buf); + + *ret = buf; + buf = NULL; return 0; } @@ -718,7 +764,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 +805,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..08d0a522 100644 --- a/src/systemd/src/basic/hexdecoct.h +++ b/src/systemd/src/basic/hexdecoct.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/basic/hostname-util.c b/src/systemd/src/basic/hostname-util.c index be6e9e58..66b80727 100644 --- a/src/systemd/src/basic/hostname-util.c +++ b/src/systemd/src/basic/hostname-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -26,12 +27,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 +51,6 @@ bool hostname_is_set(void) { return true; } -#if 0 /* NM_IGNORED */ char* gethostname_malloc(void) { struct utsname u; @@ -223,36 +226,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; +} - /* 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) +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; + + 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..edae52e3 100644 --- a/src/systemd/src/basic/hostname-util.h +++ b/src/systemd/src/basic/hostname-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -20,6 +21,7 @@ ***/ #include +#include #include "macro.h" @@ -38,4 +40,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..b2b68089 100644 --- a/src/systemd/src/basic/in-addr-util.c +++ b/src/systemd/src/basic/in-addr-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -332,6 +333,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 +374,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..acaae6d2 100644 --- a/src/systemd/src/basic/in-addr-util.h +++ b/src/systemd/src/basic/in-addr-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/basic/io-util.c b/src/systemd/src/basic/io-util.c index 61b667f0..0f10ad7f 100644 --- a/src/systemd/src/basic/io-util.c +++ b/src/systemd/src/basic/io-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -34,6 +35,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 +55,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 +64,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 +139,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 +205,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..d81610ad 100644 --- a/src/systemd/src/basic/io-util.h +++ b/src/systemd/src/basic/io-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/basic/list.h b/src/systemd/src/basic/list.h index c3771a17..7006c3e2 100644 --- a/src/systemd/src/basic/list.h +++ b/src/systemd/src/basic/list.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -182,3 +183,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..b0e963b0 100644 --- a/src/systemd/src/basic/log.h +++ b/src/systemd/src/basic/log.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -19,18 +20,16 @@ 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 +50,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 +96,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 +149,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 +165,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 +178,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 +191,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 +200,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 +225,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 +237,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 +253,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 +289,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 +314,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 +335,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..025a5db7 100644 --- a/src/systemd/src/basic/macro.h +++ b/src/systemd/src/basic/macro.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -47,6 +48,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 +168,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 +216,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; })) @@ -403,21 +443,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..43ae59c9 100644 --- a/src/systemd/src/basic/mempool.c +++ b/src/systemd/src/basic/mempool.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/systemd/src/basic/mempool.h b/src/systemd/src/basic/mempool.h index 0618b8dd..c9235c83 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 /*** diff --git a/src/systemd/src/basic/parse-util.c b/src/systemd/src/basic/parse-util.c index 6d978e93..44fae932 100644 --- a/src/systemd/src/basic/parse-util.c +++ b/src/systemd/src/basic/parse-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -27,7 +28,9 @@ #include #include "alloc-util.h" +#include "errno-list.h" #include "extract-word.h" +#include "locale-util.h" #include "macro.h" #include "parse-util.h" #include "process-util.h" @@ -68,7 +71,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 +87,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; @@ -235,7 +237,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 +273,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 +372,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 +393,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 +418,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 +440,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 +460,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 +480,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 +491,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 +527,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 +536,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 +551,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 +598,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 +624,6 @@ int parse_percent(const char *p) { return v; } -#if 0 /* NM_IGNORED */ int parse_nice(const char *p, int *ret) { int n, r; diff --git a/src/systemd/src/basic/parse-util.h b/src/systemd/src/basic/parse-util.h index dc09782c..1605cc4f 100644 --- a/src/systemd/src/basic/parse-util.h +++ b/src/systemd/src/basic/parse-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -37,18 +38,34 @@ int parse_ifindex(const char *s, int *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) { diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c index 1bdcc653..23827c19 100644 --- a/src/systemd/src/basic/path-util.c +++ b/src/systemd/src/basic/path-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -83,14 +84,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 +126,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 +251,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 +293,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 +567,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) { @@ -706,6 +731,37 @@ 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/ + * / → / + * // → / + * /foo/a → a + * /foo/a/ → a/ + * 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 +784,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 +798,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 +913,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; diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h index 399ed5f9..8848abe9 100644 --- a/src/systemd/src/basic/path-util.h +++ b/src/systemd/src/basic/path-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -28,8 +29,14 @@ #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" +#if HAVE_SPLIT_BIN +# define PATH_SBIN_BIN(x) x "sbin:" x "bin" +#else +# define PATH_SBIN_BIN(x) x "bin" +#endif + +#define DEFAULT_PATH_NORMAL PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/") +#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":" PATH_SBIN_BIN("/") #if HAVE_SPLIT_USR # define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR @@ -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); @@ -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); diff --git a/src/systemd/src/basic/prioq.c b/src/systemd/src/basic/prioq.c index 64ad638e..1e81bc71 100644 --- a/src/systemd/src/basic/prioq.c +++ b/src/systemd/src/basic/prioq.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -174,7 +175,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..a222955d 100644 --- a/src/systemd/src/basic/prioq.h +++ b/src/systemd/src/basic/prioq.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/basic/process-util.c b/src/systemd/src/basic/process-util.c index 272030d1..4128ac54 100644 --- a/src/systemd/src/basic/process-util.c +++ b/src/systemd/src/basic/process-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +60,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 +137,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 +302,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 +403,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 +529,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 +609,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 +628,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 +715,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 +824,8 @@ void sigkill_wait(pid_t pid) { } void sigkill_waitp(pid_t *pid) { + PROTECT_ERRNO; + if (!pid) return; if (*pid <= 1) @@ -719,6 +834,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 +854,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,11 +888,13 @@ 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]; @@ -779,14 +919,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 +993,7 @@ bool is_main_thread(void) { } #if 0 /* NM_IGNORED */ -noreturn void freeze(void) { +_noreturn_ void freeze(void) { log_close(); @@ -862,6 +1002,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 +1161,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 +1173,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 +1192,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 +1213,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 +1221,252 @@ 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[], unsigned n_except, pid_t *ret_pid, const char *path, ...) { + bool stdout_is_tty, stderr_is_tty; + unsigned 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 = 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); +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", @@ -1068,7 +1474,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..3525c2d6 100644 --- a/src/systemd/src/basic/process-util.h +++ b/src/systemd/src/basic/process-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -20,6 +21,7 @@ ***/ #include +#include #include #include #include @@ -32,6 +34,7 @@ #include "format-util.h" #include "ioprio.h" #include "macro.h" +#include "time-util.h" #define procfs_file_alloca(pid, field) \ ({ \ @@ -40,7 +43,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 +62,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 +91,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 +117,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 +153,56 @@ 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[], unsigned n_except, pid_t *pid, const char *path, ...); + +#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) diff --git a/src/systemd/src/basic/random-util.c b/src/systemd/src/basic/random-util.c index 3b6ddb7d..29ee3b45 100644 --- a/src/systemd/src/basic/random-util.c +++ b/src/systemd/src/basic/random-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -22,11 +23,12 @@ #include #include #include +#include #include +#include #include +#include #include -#include -#include #if HAVE_SYS_AUXV_H # include diff --git a/src/systemd/src/basic/random-util.h b/src/systemd/src/basic/random-util.h index 804e225f..dd870151 100644 --- a/src/systemd/src/basic/random-util.h +++ b/src/systemd/src/basic/random-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/basic/refcnt.h b/src/systemd/src/basic/refcnt.h index 1d77a644..ae2e446d 100644 --- a/src/systemd/src/basic/refcnt.h +++ b/src/systemd/src/basic/refcnt.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/basic/set.h b/src/systemd/src/basic/set.h index 12d0fda1..156ab4b0 100644 --- a/src/systemd/src/basic/set.h +++ b/src/systemd/src/basic/set.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -107,6 +108,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 */ diff --git a/src/systemd/src/basic/signal-util.h b/src/systemd/src/basic/signal-util.h index dfd6eb56..f6c3396e 100644 --- a/src/systemd/src/basic/signal-util.h +++ b/src/systemd/src/basic/signal-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -44,13 +45,20 @@ 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/socket-util.c b/src/systemd/src/basic/socket-util.c index 798ab16e..8ae68b6f 100644 --- a/src/systemd/src/basic/socket-util.c +++ b/src/systemd/src/basic/socket-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -42,6 +43,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 +54,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 +81,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 +100,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 +135,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 +155,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 +177,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 +191,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 +199,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 +538,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: @@ -750,19 +761,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 +807,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 +953,77 @@ 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 = d; + d = NULL; + + return (int) n; } int send_one_fd_sa( diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h index d7e2d85f..ac957a76 100644 --- a/src/systemd/src/basic/socket-util.h +++ b/src/systemd/src/basic/socket-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -35,18 +36,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 +84,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 +128,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 +149,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/stdio-util.h b/src/systemd/src/basic/stdio-util.h index bd1144b4..d3fed365 100644 --- a/src/systemd/src/basic/stdio-util.h +++ b/src/systemd/src/basic/stdio-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -26,9 +27,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..8ec3b266 100644 --- a/src/systemd/src/basic/string-table.c +++ b/src/systemd/src/basic/string-table.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/systemd/src/basic/string-table.h b/src/systemd/src/basic/string-table.h index 369610ef..e78a6dbd 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 @@ -92,13 +93,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 +109,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..a8e595db 100644 --- a/src/systemd/src/basic/string-util.c +++ b/src/systemd/src/basic/string-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -23,6 +24,7 @@ #include #include #include +#include #include #include @@ -30,6 +32,7 @@ #include "gunicode.h" #include "macro.h" #include "string-util.h" +#include "terminal-util.h" #include "utf8.h" #include "util.h" @@ -220,6 +223,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 +284,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 +304,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 +324,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); @@ -475,6 +509,10 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne assert(s); assert(percent <= 100); + + if (new_length == (size_t) -1) + return strndup(s, old_length); + assert(new_length >= 3); /* if no multibyte characters use ascii_ellipsize_mem for speed */ @@ -542,6 +580,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 +611,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,28 +638,34 @@ 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, @@ -625,7 +673,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { STATE_BRACKET } state = STATE_OTHER; char *obuf = NULL; - size_t osz = 0, isz; + size_t osz = 0, isz, shift[2] = {}; FILE *f; assert(ibuf); @@ -639,10 +687,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,22 +701,26 @@ 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; 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; } @@ -678,8 +730,9 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { if (i >= *ibuf + isz || /* EOT */ (!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) { - fputc_unlocked('\x1B', f); - fputc_unlocked('[', f); + fputc('\x1B', f); + fputc('[', f); + advance_offsets(i - *ibuf, highlight, shift, 2); state = STATE_OTHER; i = begin-1; } else if (*i == 'm') @@ -701,19 +754,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 +786,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 +816,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..08eda4fc 100644 --- a/src/systemd/src/basic/string-util.h +++ b/src/systemd/src/basic/string-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -51,15 +52,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) { @@ -133,8 +134,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 +177,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..dee5bbd7 100644 --- a/src/systemd/src/basic/strv.c +++ b/src/systemd/src/basic/strv.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -215,7 +216,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; @@ -343,8 +344,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 +355,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,42 +395,6 @@ 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; @@ -446,7 +409,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; @@ -471,7 +434,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,7 +448,7 @@ 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, unsigned position, char *value) { char **c; unsigned n, m, i; @@ -493,6 +456,7 @@ int strv_push_prepend(char ***l, char *value) { return 0; n = strv_length(*l); + position = MIN(position, n); /* increase and check for overflow */ m = n + 2; @@ -503,10 +467,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 +548,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; @@ -897,7 +863,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..f169ac5d 100644 --- a/src/systemd/src/basic/strv.h +++ b/src/systemd/src/basic/strv.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -53,7 +54,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, unsigned 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 +91,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); @@ -174,9 +179,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..95fbbd91 100644 --- a/src/systemd/src/basic/time-util.c +++ b/src/systemd/src/basic/time-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -39,6 +40,7 @@ #include "macro.h" #include "parse-util.h" #include "path-util.h" +#include "process-util.h" #include "string-util.h" #include "strv.h" #include "time-util.h" @@ -891,28 +893,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)) 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) { @@ -935,12 +933,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(); @@ -1387,8 +1379,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; @@ -1463,4 +1454,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..a8c5a837 100644 --- a/src/systemd/src/basic/time-util.h +++ b/src/systemd/src/basic/time-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -99,12 +100,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)); @@ -155,6 +156,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..638b37d7 100644 --- a/src/systemd/src/basic/umask-util.h +++ b/src/systemd/src/basic/umask-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/basic/utf8.c b/src/systemd/src/basic/utf8.c index ff281e43..c88eef38 100644 --- a/src/systemd/src/basic/utf8.c +++ b/src/systemd/src/basic/utf8.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -409,3 +410,22 @@ 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; +} diff --git a/src/systemd/src/basic/utf8.h b/src/systemd/src/basic/utf8.h index 322dac20..c5243c24 100644 --- a/src/systemd/src/basic/utf8.h +++ b/src/systemd/src/basic/utf8.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -60,3 +61,5 @@ 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); diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c index c8a22d68..d8c9e82d 100644 --- a/src/systemd/src/basic/util.c +++ b/src/systemd/src/basic/util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -40,6 +41,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 +54,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 +64,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 +112,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 +125,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 +152,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 +186,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 +481,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 +525,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..19e9eae1 100644 --- a/src/systemd/src/basic/util.h +++ b/src/systemd/src/basic/util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -70,8 +71,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 +84,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 +91,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. @@ -189,9 +199,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..9b721ee6 100644 --- a/src/systemd/src/libsystemd-network/arp-util.c +++ b/src/systemd/src/libsystemd-network/arp-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -25,6 +26,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 +51,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 +105,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..decfce3f 100644 --- a/src/systemd/src/libsystemd-network/arp-util.h +++ b/src/systemd/src/libsystemd-network/arp-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c index c1fa8763..df4d8afb 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.c +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.h b/src/systemd/src/libsystemd-network/dhcp-identifier.h index 1cc0f9fb..0ccee7a7 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.h +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/libsystemd-network/dhcp-internal.h b/src/systemd/src/libsystemd-network/dhcp-internal.h index 3fdf02da..a5352695 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 /*** diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h index 7847ce07..65c182f4 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 /*** @@ -33,6 +34,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..d9067ffb 100644 --- a/src/systemd/src/libsystemd-network/dhcp-network.c +++ b/src/systemd/src/libsystemd-network/dhcp-network.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -33,6 +34,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 +73,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 +125,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 +210,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..08ed8a9f 100644 --- a/src/systemd/src/libsystemd-network/dhcp-option.c +++ b/src/systemd/src/libsystemd-network/dhcp-option.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -192,9 +193,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 +255,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..6d8f01f2 100644 --- a/src/systemd/src/libsystemd-network/dhcp-packet.c +++ b/src/systemd/src/libsystemd-network/dhcp-packet.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/systemd/src/libsystemd-network/dhcp-protocol.h b/src/systemd/src/libsystemd-network/dhcp-protocol.h index 5cf7abbf..73a9f75e 100644 --- a/src/systemd/src/libsystemd-network/dhcp-protocol.h +++ b/src/systemd/src/libsystemd-network/dhcp-protocol.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h index 945c3b97..13844a86 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -28,25 +29,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 +102,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..45e0e824 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 /*** @@ -35,8 +36,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..85231084 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-network.c +++ b/src/systemd/src/libsystemd-network/dhcp6-network.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -68,9 +69,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..d449a9c7 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-option.c +++ b/src/systemd/src/libsystemd-network/dhcp6-option.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -27,6 +28,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 +36,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 DHCP6Option { - be16_t code; - be16_t len; - uint8_t data[]; -} _packed_ DHCP6Option; +typedef struct DHCP6AddressOption { + struct DHCP6Option option; + struct iaaddr iaaddr; + uint8_t options[]; +} _packed_ DHCP6AddressOption; + +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 +99,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 +108,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 +129,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 +155,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 +264,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; + + 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; - uint16_t opt, status; - size_t optlen; + + 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->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 NA T1 %ds > T2 %ds", + lt_t1, lt_t2); + r = -EINVAL; + goto error; + } + + break; - lt_t1 = be32toh(ia->lifetime_t1); - lt_t2 = be32toh(ia->lifetime_t2); + 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 T1 %ds > T2 %ds", + log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds", lt_t1, lt_t2); r = -EINVAL; goto error; @@ -221,17 +413,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 +429,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]; + + if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) { + r = -ENOBUFS; + goto error; + } - while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) { + 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 +497,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 +619,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..5f7e809b 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-protocol.h +++ b/src/systemd/src/libsystemd-network/dhcp6-protocol.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -33,6 +34,7 @@ struct DHCP6Message { } _packed_; be32_t transaction_id; }; + uint8_t options[]; } _packed_; typedef struct DHCP6Message DHCP6Message; @@ -104,3 +106,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..2673aa1c 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 /*** diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c index c560a864..cffbfb98 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.c +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.h b/src/systemd/src/libsystemd-network/lldp-neighbor.h index c1a7606d..76d4c0c7 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.h +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/libsystemd-network/lldp-network.c b/src/systemd/src/libsystemd-network/lldp-network.c index 4466a050..b8a10d54 100644 --- a/src/systemd/src/libsystemd-network/lldp-network.c +++ b/src/systemd/src/libsystemd-network/lldp-network.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -93,8 +94,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..c0f8cbe1 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 /*** diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c index de37b9f0..a8a5658b 100644 --- a/src/systemd/src/libsystemd-network/network-internal.c +++ b/src/systemd/src/libsystemd-network/network-internal.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -23,6 +24,7 @@ #include #include +#include "sd-id128.h" #include "sd-ndisc.h" #include "alloc-util.h" @@ -118,7 +120,8 @@ bool net_match_config(const struct ether_addr *match_mac, 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,7 +136,10 @@ 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) @@ -273,10 +279,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; @@ -422,7 +427,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 +440,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 +480,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 +493,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 +586,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; } diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h index 4666f174..4e69f1a5 100644 --- a/src/systemd/src/libsystemd-network/network-internal.h +++ b/src/systemd/src/libsystemd-network/network-internal.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -36,7 +37,8 @@ bool net_match_config(const struct ether_addr *match_mac, 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, diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c index 5eab0050..b93b7334 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -357,9 +358,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 +382,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 +406,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 +416,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 +441,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); @@ -833,7 +854,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,9 +1285,9 @@ 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; } } @@ -1336,9 +1356,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; } } diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index c00190b5..c272a61e 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -23,6 +24,7 @@ #include #include #include +#include #include #include @@ -394,9 +396,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 +473,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 +517,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; @@ -685,9 +687,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void return 0; } - free(lease->timezone); - lease->timezone = tz; - tz = NULL; + free_and_replace(lease->timezone, tz); break; } @@ -809,8 +809,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 +880,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 +928,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 +946,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 +992,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); diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c index f512b65b..f22daa3c 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -31,7 +32,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 +56,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 +66,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 +235,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 +286,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 +306,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 +352,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 +421,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 +456,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 +480,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 +508,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 +750,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 +772,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 +819,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 +836,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 +863,35 @@ 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; + + r = dhcp6_lease_get_iaid(lease, &iaid_lease); + if (r < 0) + return r; + + 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; @@ -785,12 +899,17 @@ 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_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 +948,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 +1133,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 +1190,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 +1216,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 +1233,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 +1254,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 +1276,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 +1299,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 +1322,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 +1357,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 +1378,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 +1437,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 +1458,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; diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c index 63a813aa..00b9fdf0 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -50,7 +51,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 +97,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 +149,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 +181,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; @@ -383,6 +418,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..08f46dd2 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4acd.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -289,8 +290,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..f1e7b404 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -26,6 +27,7 @@ #include #include +#include "sd-id128.h" #include "sd-ipv4acd.h" #include "sd-ipv4ll.h" diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c index 31e24486..20956041 100644 --- a/src/systemd/src/libsystemd-network/sd-lldp.c +++ b/src/systemd/src/libsystemd-network/sd-lldp.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c index 9dfe6847..4fb8ed7f 100644 --- a/src/systemd/src/libsystemd/sd-event/sd-event.c +++ b/src/systemd/src/libsystemd/sd-event/sd-event.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -123,6 +124,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 +243,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 +459,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 +699,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 +894,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 +982,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 +1049,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 +1085,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 +1168,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 +1229,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 +1287,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 +1322,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 +1361,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 +1507,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); @@ -2452,6 +2490,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 +2548,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 +2655,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); @@ -2656,6 +2697,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); @@ -2700,6 +2742,7 @@ _public_ int sd_event_loop(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_INITIAL, -EBUSY); @@ -2721,6 +2764,7 @@ finish: _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 +2772,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 +2780,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 +2793,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 +2805,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 +2830,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 +2856,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 +2872,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 +2923,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 +2931,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..f1033346 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.c +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -19,10 +20,12 @@ #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 +123,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 +135,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 +186,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..9f3340e5 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.h +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c index 052110d5..e5bd447a 100644 --- a/src/systemd/src/libsystemd/sd-id128/sd-id128.c +++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/systemd/src/shared/dns-domain.c b/src/systemd/src/shared/dns-domain.c index c313a033..cfc1f3b3 100644 --- a/src/systemd/src/shared/dns-domain.c +++ b/src/systemd/src/shared/dns-domain.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -301,8 +302,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; } @@ -609,8 +609,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); } } } @@ -701,23 +700,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 +971,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; diff --git a/src/systemd/src/shared/dns-domain.h b/src/systemd/src/shared/dns-domain.h index d1a99be7..3ebd4018 100644 --- a/src/systemd/src/shared/dns-domain.h +++ b/src/systemd/src/shared/dns-domain.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -97,6 +98,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..789cc501 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 @@ -131,6 +132,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, 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..fb40d9a9 --- /dev/null +++ b/src/tests/config/meson.build @@ -0,0 +1,26 @@ +test_unit = 'test-config' + +sources = files( + 'nm-test-device.c', + 'test-config.c' +) + +test_config_dir = meson.current_source_dir() + +cflags = [ + '-DSRCDIR="@0@"'.format(test_config_dir), + '-DBUILDDIR="@0@"'.format(test_config_dir) +] + +exe = executable( + test_unit, + sources, + dependencies: test_nm_dep, + c_args: 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..50215756 100644 --- a/src/tests/config/test-config.c +++ b/src/tests/config/test-config.c @@ -25,7 +25,7 @@ #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" @@ -254,7 +254,7 @@ 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, "/no/such/dir", "", NULL); @@ -318,10 +318,10 @@ test_config_global_dns (void) g_object_unref (config); } -#if WITH_CONCHECK static void test_config_connectivity_check (void) { +#if WITH_CONCHECK const char *CONFIG_INTERN = BUILDDIR"/test-connectivity-check-intern.conf"; NMConfig *config; NMConnectivity *connectivity; @@ -334,14 +334,14 @@ test_config_connectivity_check (void) 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) @@ -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 (); @@ -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); @@ -904,15 +906,15 @@ 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); @@ -925,7 +927,7 @@ 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); @@ -1037,13 +1039,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 +1050,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..386ede87 --- /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'] + shared_siphash, + 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..f67c332b 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,124 @@ 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 +456,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..38eaf25e 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); } @@ -1460,6 +1460,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 +1482,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 diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c index bcbeee3e..816a816f 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); 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 6c600c46..436fc68b 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, @@ -858,16 +850,19 @@ NMVpnConnection * nm_vpn_connection_new (NMSettingsConnection *settings_connection, NMDevice *parent_device, const char *specific_object, + NMActivationReason activation_reason, NMAuthSubject *subject) { g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL); g_return_val_if_fail (NM_IS_DEVICE (parent_device), NULL); + g_return_val_if_fail (specific_object, NULL); return (NMVpnConnection *) g_object_new (NM_TYPE_VPN_CONNECTION, NM_ACTIVE_CONNECTION_INT_SETTINGS_CONNECTION, settings_connection, NM_ACTIVE_CONNECTION_INT_DEVICE, parent_device, NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, specific_object, NM_ACTIVE_CONNECTION_INT_SUBJECT, subject, + NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON, activation_reason, NM_ACTIVE_CONNECTION_VPN, TRUE, NULL); } @@ -1176,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); @@ -1394,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); } @@ -1460,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; @@ -1564,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 */ @@ -1621,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); @@ -1650,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); } @@ -1839,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); } @@ -2326,7 +2324,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); @@ -2549,7 +2547,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; @@ -2558,7 +2555,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); @@ -2778,8 +2774,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); @@ -2829,14 +2825,14 @@ get_property (GObject *object, guint prop_id, g_value_set_string (value, priv->banner ? 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); @@ -2844,16 +2840,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; @@ -2880,13 +2902,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), @@ -2900,8 +2915,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 038d0efd..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" @@ -53,6 +52,7 @@ GType nm_vpn_connection_get_type (void); NMVpnConnection * nm_vpn_connection_new (NMSettingsConnection *settings_connection, NMDevice *parent_device, const char *specific_object, + NMActivationReason activation_reason, NMAuthSubject *subject); void nm_vpn_connection_activate (NMVpnConnection *self, -- cgit 1.3.0-6-gf8a5