diff options
Diffstat (limited to 'src/core')
61 files changed, 1814 insertions, 616 deletions
diff --git a/src/core/NetworkManagerUtils.c b/src/core/NetworkManagerUtils.c index cced1717..2f447146 100644 --- a/src/core/NetworkManagerUtils.c +++ b/src/core/NetworkManagerUtils.c @@ -702,8 +702,8 @@ check_connection_controller(NMConnection *orig, NMConnection *candidate, GHashTa s_con_orig = nm_connection_get_setting_connection(orig); s_con_cand = nm_connection_get_setting_connection(candidate); - orig_controller = nm_setting_connection_get_master(s_con_orig); - cand_controller = nm_setting_connection_get_master(s_con_cand); + orig_controller = nm_setting_connection_get_controller(s_con_orig); + cand_controller = nm_setting_connection_get_controller(s_con_cand); /* A generated connection uses the UUID to specify the controller. Accept * candidates that specify as controller an interface name matching that diff --git a/src/core/README.l3cfg.md b/src/core/README.l3cfg.md new file mode 100644 index 00000000..901d40b4 --- /dev/null +++ b/src/core/README.l3cfg.md @@ -0,0 +1,368 @@ +L3Cfg Rework +============ + +NMDevice is complex. Together with NMManager, NMDevice does too much. + +The goal is to rework the IP configuration (Layer 3) to be a more separate +part of the code that is better maintainable, easier to understand and +extend and more correct. + +Current Situation +----------------- + +- [NMManager](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-manager.c): + this is the main object (a singleton) that drives most things. + Among many other things, it creates NMDevice instances and coordinates. + +- [NMDevice](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c): + this represents a device. This is a subclass of NMDBusObject, + it is thus directly exported on D-Bus (as D-Bus objects like + `/org/freedesktop/NetworkManager/Devices/1`). + It also manages all aspects of the device. It has an overall state + (`NM_DEVICE_STATE`) but lots of more specific states (e.g. current state + of DHCP configuration). As always, the hardest part in programming are + stateful objects, and NMDevice has *a lot* of state. The code is huge and + hard to understand and the class has (too) many responsibilities. \ + \ + NMDevice also has subclasses, which are determined based on the "device type". That + means, there are subclasses like NMDeviceEthernet and NMDeviceBridge. As such, the + subclasses also export additional D-Bus interfaces. These subclasses also handle + the Layer 2 specific aspects of the device. For this aspect, delegation probably + would have been a better choice. On the other hand, IP configuration is almost entirely + managed by the parent class. Which is good, because the IP configuration is common to all + device types, but is bad because NMDevice already does so many things. + +- [NMIP4Config](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-ip4-config.c) (and NMIP6Config): + these are also subclasses of NMDBusObject + and exported on D-Bus on paths like `/org/freedesktop/NetworkManager/IP4Config/1`. + The device's `IP4Config` property refers to these objects. They contain + the runtime IP information of that device. I don't think these objects + should exist on the D-Bus API, as NMDevice could directly expose these properties. + But for historic reasons, such is our D-Bus API. + Other than that, NMIP4Config objects are also used internally for tracking + IP configuration. For example, [when](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/dhcp/nm-dhcp-nettools.c#L563) + we receive a DHCP lease, we construct a NMIP4Config object with the addresses, DNS settings, + and so on. These + instances are then [tracked by](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L519) + NMDevice, and [merged](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L8928) + into an instance that is then exposed on D-Bus. As such, this class has two + mostly independent purposes. + +- [NMDhcpClient](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/dhcp/nm-dhcp-client.c): + our DHCP "library". It's a simple object with a clear API that + abstracts most of the complexity of handling DHCP. But still, NMDevice + needs to drive the DHCP client instance. Meaning, it needs to create (start) and stop + them and hook up signals for changes (new lease) and timeout. This is mostly + fine and unavoidable. The point is that while specific tasks are well abstracted + (like the details of DHCP), there is still some state in NMDevice that is related + to manage these tasks. DHCP is one of many such tasks, like also + link local addresses, SLAAC or LLDP. + This leads to the increased complexity of NMDevice, which manages a large variety + of such tasks. + +### Problems: + +1. first the sheer code size of [nm-device.c](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L19030). + It's hard to understand and maintain, and this results in misbehaviours. Also, features that should be easy to implement + are not. Also, there are inefficiencies that are hard to fix. + +2. NMDevice and NMIP4Config are both exported on D-Bus while having other responsibilities. + Being subclasses of NMDBusObject, they are glued to the D-Bus API. For example, NMIP4Config is + also used for other purposes (for tracking IP configuration internally). + +3. NMDevice simply does too much. IP configuration should be a separate, encapsulated + API to make allow NMDevice to be smaller and the IP configuration part better + testable, understandable and smaller too. + +4. in the current model, NMDevice can be related to zero, one or two ifindexes. For example, + for ethernet devices, there is commonly only one actual netdev device (and one ifindex). + For OVS devices, there is no ifindex. For NMDeviceModem or NMDeviceBluetooth there is + a NMDevice instance that has initially no ifindex (it represents the tty serial port + or the bluetooth device) but during activation it gets and ip ifindex. With PPPoE, + the ethernet device can even have two ifindexes (one for the underlying ethernet and + one for the PPP device). That is all utterly confusing, inconsistent and limited. + For example, not all interfaces you see in `ip link` can be found in the D-Bus API. + The D-Bus API also does not give access to the ifindex (which is the real identifier + for a netdev devices). It only exposes the IpInterface name. That should be improved too, + but even such seemingly simple things are not done for years, because it's not trivially + clear what the right ifindex is. + Also a device instance on D-Bus significantly changes its meaning when it activates/deactivates + and it starts/stops being responsible for an ifindex. + In the future there should be devices that represent exactly one netdev device (an ifindex) + and devices that don't have an ifindex. That is follow up work and hinted by + [rhbz#1066703](https://bugzilla.redhat.com/show_bug.cgi?id=1066703). But simplifying + the IP configuration is a requisite before addressing that rework. + With this we will have controller and controlled devices. That means, a controller devices + (that for example represents a bluetooth device) will need to configure IP address on the + controlled IP device. That would be doable by injecting the IP config on that device, + but as the device already does so much, it would be better if this would be a separate + IP configuration manager for that ifindex. + +5. NMIP4Config exports properties on D-Bus like [AddressData](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/introspection/org.freedesktop.NetworkManager.IP4Config.xml#L26). + which are the currently configured IP addresses. These should be directly obtained + from the NMPlatform cache, which contains the correct list of addresses as kernel + exposes them via rtnetlink. Instead, whenever there are changes in platform we + [generate](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L14223) + an NMIP4Config instance, then we merge, intersect and subtract this captured information + with the IP configs we want to configure. Finally we merge them together again + and sync the result to platform. This is bad, wrong and inefficient. + We must not mix "what is configured" with "what we want to configure". The current + approach also re-generates these IP config instance whenever something in platform changes. + That does not scale. If we have any hope to handle thousands of routes, this needs to change. + +6. The NMIP4Config objects are mutable, and they are heavily mutated. When we create an NMIP4Config + instance that represent a DHCP lease, we will [subtract](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L14236) + addresses that were externally removed. That is wrong, because during a reapply we + will need to know these addresses again. The solution for that is not to mutate this + data, but track whether IP addresses are removed separately. + +7. NMDevice also does ACD, but it can only do it for addresses received via DHCP. + It implicitly also does ACD for IPv4LL, but that is via using the n_ipv4ll library. + It would be good to have an option that we can configure IPv4LL for any address. + Also, if you manually configure an address like 192.168.2.5 (for which we don't do + ACD) and the same address is obtained via DHCP, then doing ACD for the address is wrong. + There needs to be link-wide view of the addresses, and not only looking at individual + addresses when deciding to do ACD. + +8. As IP configuration is done by NMDevice, VPN connections have limited capabilities + in this regard. + When a VPN has IP addresses, then it injects them into NMDevice by + [providing](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L13696) + an NMIP4Config. However, that means VPNs cannot do DHCP or IPv4LL, because it can + only inject known configuration. That would be very useful for example with a tap + device with openvpn. The real problem here is that NMVpnConnection are + treated special, when they should be more like devices. That should be reworked in the future, + by reworking VPN plugins. Regardless, having IP configuration handled by NMDevice is limiting. + +9. NetworkManager currently supports `ipv4.method` which can be "manual", "disabled" or + "auto". This scheme does not allow for example to enable IPv4LL together with DHCPv4. + As a special case, you can configure `ipv4.method=auto` together with static + addresses in `ipv4.addresses`, so combining DHCP and static addressing works. But in general, + this scheme is limited. In the future we should have more flexible schemes, where + addressing methods can be independently enabled or disabled. Also, we currently + have `ipv4.may-fail`, but that is limited as well. For example, + `ipv4.may-fail=yes` and `ipv6.may-fail=yes` still means that at least one of the + address families must succeed. That makes sense for certain use cases, but it + means, you cannot have truly best-effort, opportunistic DHCP with this way. + As workaround for that there is `ipv4.dhcp-timeout=infinity`. In + general it is not only useful to enable methods independently, we also configure + independently whether they are required or optional (and possibly, that they are optional + but at least one of several optional methods must succeed). Anyway. The point + is there is a need to make IP configuration more flexible. Currently it is not. + Such a seemingly simple extension would be surprisingly difficult to implement + because [the code](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L6616) is + all over the place. The way how NMDevice tracks the overall activation state is + hard to understand. This should be improved and possibly could be improved in a + smaller refactoring effort. But instead of a smaller effort, we will use the big hammer + with L3Cfg rework. + +10. There are two classes NMIP4Config and NMIP6Config. Handling both address families is + commonly similar, so there is lot of similar code in both. They should be unified + so that similar code can handle both address families. + + +Solution and Future +------------------- + +NML3Cfg work is supposed to simplify some part of NMDevice: the part related to +IP configuration. This is a huge rework of a core part of NetworkManager. Arguably, +some parts maybe could be done more evolutionary, but the fundamental problems require +to rip out NMIP4Config and replace it by something better. Doing that is a large rework +that changes NMDevice heavily. That is also the opportunity to get the smaller issues +right. + +There is already a new class [NML3Cfg](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.h#L141) +(currently unused). An NML3Cfg instance is responsible for handling IP configuration +of an ifindex. Consequently, we can ask NMNetns to [get](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-netns.c#L142) +(or create) a NML3Cfg instance for an ifindex. +The idea is that there can be multiple users (NMDevice and NMVpnConnection and future controller devices) +that use the same NML3Cfg instance. Especially with a future rework of NMDevice where +a NMDevice only manages one ifindex (or none), there is a need that multiple +devices manage the IP configuration on the same device. Independent users can cooperate +to configure IP configuration on the same device. Already now with Libreswan VPN, where the VPN "injects" +its NMIP4Config in NMDevice. Or with PPPoE, where the NMDeviceEthernet is both about IP configuration +for the PPPoE device. + +There is also a new class [NML3ConfigData](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3-config-data.h). +This replaces some aspect of NMIP4Config/NMIP6Config. A NML3ConfigData object is immutable and has no real logic +(or state). It has some "logic", like comparing two NML3ConfigData instances, logging it, or merging two (immutable) +instances into a new instance. But as the class is immutable, that logic is rather simple. This class is +used to track information. As it's immutable, anybody who is interested can keep a reference +for it's own purpose. For example, NMDhcpClient will generate a NML3ConfigData with the information +of the lease. It may keep the reference, but it will also tell NMDevice about it. The NMDevice +will then itself tell NML3Cfg to accept this configuration. This works by calling +[add()/remove()](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L2654). +One NML3ConfigData can also track both IPv4 and IPv6 information. It's a general set of IP related +configuration, that has some address specific properties. Those are then duplicated for both address +families and implemented in a way to minimize code duplication and encourage to treat them the same. +As this replaces an aspect of NMIP4Config, NMIP4Config can focus on it's other purpose: to expose data on D-Bus. + +What NML3Cfg then does, is to merge all NML3ConfigData, and "commit" it to platform. Thereby it knows +which addresses it configured the last time (if they no longer are to be configured, they must be removed). +This is done [here](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L3442). + +As independent users should be able to cooperate, it is not appropriate that they call "commit". +Instead, they set a commit type ([here](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L3476), +and whenever something changes, NML3Cfg knows the aggregated commit type. That is necessary +because when we activate a device, we may want to preserve the existing IP configuration (e.g. after +a restart of NetworkManager). During that time is the NML3Cfg instance set to a reduced commit +mode (ASSUME). + +NML3Cfg will also handle IPv4 ACD. Any user of NML3Cfg registers/unregisters NML3ConfigData instances +that should be configured. Thereby they also say whether ACD should be done for the IPv4 addresses. +NML3Cfg then keeps state for each IPv4 address, whether ACD should be performed, and whether the +address is ready to be configured. NML3Cfg does not do DHCP or similar. That is still the responsibility +of NMDevice to run a NMDhcpClient. But it does run ACD, because whether to perform ACD on an address +requires a holistic view of all addresses of that interface. For example, if you configure a static +IP address 192.168.2.5 (with ACD disabled) and you also get the same address via DHCP, then ACD should +not performed for that address (even if the user configured ACD with DHCP). Of course, that is a very +unlikely example. More likely is that NetworkManager is restarted and it leaves the addresses (that passed +ACD) configured. After restart, DHCP finds the same addresses and no new ACD should be performed. This shows +that the ACD state depends all the IP addresses on an interface, +and thus it's done by NML3Cfg. The API for this is very simple. Users enable/disable ACD during nm_l3cfg_add_config() +and receive events like [NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L303). +Another advantage is that ACD now works for any kinds of addresses. Currently it only works for addresses +from DHCP and link local addresses. + +NML3Cfg does not implement or drive DHCP. However, as it already does ACD it gained it's own IPv4LL +"library": [NML3IPv4LL](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L3624). +This will replace nettools' n-ipv4ll library, because that library also does ACD internally, while we want +to use the holistic view that NML3Cfg has. What this means, is that the user (NMDevice) +can request a NML3IPv4LL handle from the NML3Cfg instance, and it just does it with a simple API. +All the user might do is to enable/disable the handle and to react to signals (if it cares to find +out whether IPv4LL fails). + +The general parts of NML3Cfg are already implemented. It has unit tests and can be tested independently. +You might note that NML3Cfg is not trivial already, but the API that it provides is as simple as possible: +create immutable NML3ConfigData instance, and add/remove them. Optionally, handle the ACD events and +listen to some events. The complexity that NML3Cfg has, will lead in the same amount simplify NMDevice. + +What is missing is NMDevice using this new API. Instead of creating and tracking NMIP4Config instances, +it needs to track NML3ConfigData instances. In principle that sounds simple, in practice that changes +large part of "nm-device.c". + +Thereby also the state machine for NM_DEVICE_STATE will be improved. It's anyway a rewrite. This will lay the +groundwork for more flexible configuration of IP methods, with different failure modes (opportunistic or +mandatory). + +What then also should be easier, to combine IPv4LL with other addressing methods. In Windows AFAIK, if you +don't get a DHCP address it will configure a IPv4LL address. That is also what RFC suggests, but which we +currently don't support. + +In general, change the way how external IP addresses/routes are tracked. This merge, intersect, subtract +approach does not perform well. Currently we react on signals and it's hard to understand what happens +in response to that, or whether it's really the correct thing to do. See yourself starting from +[here](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L14214). + +### DHCP + +Currently, when NMDhcpClient receives a lease, it emits a signal with two things: the NMIP4Config +instance (containing addresses, routes, DNS settings and other information for later use), and a string +dictionary with the DHCP lease options (they are mainly used to expose them on D-Bus). The latter is +immutable (meaning, it's not changed afterwards). That does not significantly change with L3Cfg. The +difference is that instead of NMIP4Config a NML3ConfigData instance gets created. That instance then +references the (immutable) strdict. With that, any part of the code that has access to the NML3ConfigData, +also has access to the lease options. So instead of two separate +pieces of information, the result of a lease event will only be a NML3ConfigData instance (which internally +tracks the strdict with the DHCP lease options). + +Later, when NML3Cfg configures an interface, it takes all NML3ConfigData instances that were added to +it, and merges them. [Currently](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3-config-data.c#L2693), +the merged data will not contain the lease information, but it's probably not needed anyway. + +If it would be needed, the question is what happens if multiple lease informations are present +during the merge. Duplicate leases would not commonly happen, but in general, the merging algorithm +needs to take into account priorities and conflicting data. +That is done by users who call [add](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L2658) +to provide a priority for the NML3ConfigData instance. +Later, the instances get sorted by priority and merging is smart to take that into account +([here](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L2983)). + +Also, we currently inject the route-metric and table into the generated NMIP4Config. +Those settings come from the connection profiles and not from DHCP. We will avoid that +by allowing the routes in NML3ConfigData to be marked as metric\_any and table\_any. +That way,the NML3ConfigData is independent (and immutable) with respect to those settings. +The same happens for example with PPP, where the modem starts PPP, and currently the +route and metric needs to be passed several layers down. But worst, those settings +can change during reapply. Currently that means we need to hack NMIP4Config with +those changes. Later, we will only tell NML3Cfg to track the NML3ConfigData with +different settings. + +### DNS + +DNS information is currently set in the NMIP4Config instances. That happens for example with the DNS information +from a DHCP lease, but also with the static DNS settings from the connection profile. Later, the same information +will packed in NML3ConfigData. + +One nice difference is again the immutability. Currently, NMDnsManager keeps a reference to all relevant NMIP4Config instances, +but as they are mutable, it needs to [subscribe](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/dns/nm-dns-manager.c#L275) +to changes. Later, when a NML3ConfigData instance "changes", it means it was +replaced by a different one and NMDnsManager needs to update its list of tracked NML3ConfigData. I find that +cleaner, because adding and removal to the list of NMIP4Config/NML3ConfigData happens anyway and needs to be handled. + + +Related Bugs +------------ + +* Main bug: + + - [rh#1868254](https://bugzilla.redhat.com/show_bug.cgi?id=1868254): + "refactor NetworkManager's IP configuration done by NMDevice" + +* Follow up but to improve model of devices: + + - [rh#1066703](https://bugzilla.redhat.com/show_bug.cgi?id=1066703): + "\[RFE\] Handle parent/child relationships more cleanly" + +* Flexible IP methods: + + - [rh#1791624](https://bugzilla.redhat.com/show_bug.cgi?id=1791624): + "NetworkManager must not remove used bridge" + +* Improving performance issues, this will lay ground work: + + - [rh#1847125](https://bugzilla.redhat.com/show_bug.cgi?id=1847125): + "\[RFE\] Improve 20% performance on creating 1000 bridge over 1000 VLANs" + + - [rh#1861527](https://bugzilla.redhat.com/show_bug.cgi?id=1861527): + "Excessive memory and CPU usage on a router with IPv6 BGP feed" + + - [rh#1753677](https://bugzilla.redhat.com/show_bug.cgi?id=1753677): + "High cpu usage while non-controlled interface is mangling tc filters" + +TODO +---- + +- Before considering an IP method completely activated, check that all addresses + we want to configure are no longer tentative. + For example, when activating a `connection.type=pppoe`, we might get an IPv6 address + from the pppd daemon. We set that via + `nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, ip_data->l3cd)` + Note that this currently already is sufficient to make the IP method (as far as PPP + is concerned), to be "ready". + We should however wait for IPv6 DAD to complete. See how that is already done + for ipmanual (`_dev_ipmanual_check_ready()`). + +- NMDevicePPP fails to re-activate a profile that is currently active. This is not + new on "next" branch, but also happened before. Have a pppoe profile active, + and issue `nmcli connection up $PROFILE` again. This, as expected, brings down + the device but fails to re-activate it. It's not trivial to fix (I think), because + of the ip-ifindex madness. Check the logs. Test with contrib/scripts/test-ppp.sh + script. Possibly affects other types. + +- NMDevice's ip4_config_pre_commit() had only one effect, to call nm_modem_ip4_pre_commit() + which set IFF_NOARP. That is currently dropped. Find a different way to achieve that. + Theoretically, we still could do that from NML3Cfg's post-commit notification. But + this really should be handled by NML3Cfg. That means, to disable IFF_NOARP needs somehow + configured by NMDeviceModem -- possibly by setting a flag in NML3ConfigData that indicates + to do this. Or maybe NML3Cfg should detect automatically when to set IFF_NOARP. + +- `ipvx.method=auto` usually means autoconf6/DHCPv4, but it doesn't have to. For example + with PPP/VPN it might mean that the IP configuration is provided by pppd/VPN or with + Wi-Fi-P2P it might mean that the controller of a peer runs a DHCP server (shared). + Now the parents implementation activate_stage3_ip_config() can no longer be overwritten + by subclasses, and they always perform their steps. For subclasses to modify what a + method means there is klass->get_ip_method_auto(self(). NMDeviceModem does not yet + implement that, which is most certainly lacking. We need to test at least NMDeviceModem + whether IP methods work correctly in this regard. Possibly other subclasses are affected + too (NMDeviceBluetooth?). diff --git a/src/core/README.md b/src/core/README.md new file mode 100644 index 00000000..13779197 --- /dev/null +++ b/src/core/README.md @@ -0,0 +1,9 @@ +core +==== + +The source code of the NetworkManager daemon. + +NetworkManager is a daemon that provides a D-Bus API and a file-based +API for configuring the network on a Linux host. + +This is the daemon source code. diff --git a/src/core/README.next.ip-config.md b/src/core/README.next.ip-config.md new file mode 100644 index 00000000..e5be74f1 --- /dev/null +++ b/src/core/README.next.ip-config.md @@ -0,0 +1,59 @@ +Rework `NMIP[46]Config` for `next` branch +========================================= + +The `next` branch is a large rework of internals, how IP configuration is done by `NMDevice`. + +Previously, there are two `GObject`s named `NMIP4Config` and `NMIP6Config`. These +serve different purposes: + +1) They are data containers that can track IP configuration. As such, `NMDevice` + and various parts (like `NMDhcpClient`) create them, pass them around and + mutate/merge them to track the IP configuration. + +2) They are also subclasses of `NMDBusObject` and exported on D-Bus as + `/org/freedesktop/NetworkManager/IP4Config/1`, etc. As such, see their + [D-Bus API](../../introspection/org.freedesktop.NetworkManager.IP4Config.xml) + (and [for IPv6](../../introspection/org.freedesktop.NetworkManager.IP6Config.xml)). + +`next` branch will replace use 1) with `NML3ConfigData`. `NML3ConfigData` are immutable +(sealable) data containers with little logic. This leaves `NMIP4Config` to only +implement 2). + +This needs to be reworked. + +* Now `NMIP4Config` and `NMIP6Config` are subclasses of `NMIPConfig`. The goal + is to treat IPv4/IPv6 similar and generically. Probably there should be very + little code in the subclasses left and most should move to the parent classes. + We still need separate GObject types though, because that is how `NMDBusObject`'s + glue code can handle different D-Bus paths. + +* Now `NML3Cfg` is a handle for the IP configuration parameters of a device (ifindex). + As `NMIPConfig` mostly is about exporting the current IP configuration, it probably + can get most of it from there (and by listening to signals to that). + +* Note that `NMDevice`, `NMActiveConnection` refer `NMIP[46]Config`s, and most + importantly, the respective D-Bus objects refer to them. As `NMVpnConnection` + (and "org.freedesktop.NetworkManager.VPN.Connection" interface) are modeled + as "subclasses" of `NMActiveConnection`, they also have one. That means, + it's not entirely clear what these properties even are. For example, currently, + `NMDevice` does a (terrible) dance of tracking external `NMIP[46]Config` objects, + merging, intersecting and subtracting them with other `NMIP4Config` objects + to get the merged one, which is then exported on D-Bus. That merged object + does therefore not directly expose the IP addresses that are actually + configured on the interface (`ip addr`), but more what NetworkManager + wanted to configure and the (terrible) feedback loop where the platform + addresses get synced. With `next` branch and `NML3Cfg` there is a clear distinction + between what NetworkManager wants to configure vs. what is actually configured. + I think for `NMDevice` and `NMActiveConnection`, the IP addresses on + "org.freedesktop.NetworkManager.IP4Config" should expose the IP addresses + that are actually in platform (`ip addr`). If there is a need to expose + additional information (like things that NetworkManager wanted to configure), + then this should be different/new API. + On the other hand, currently `NMVpnConnection`'s `NMIP4Config` only tracks the + IP addresses that come from the VPN plugin. So it's much more what it wants + to configure (from the VPN plugin), and not at all about what is configured + on the interface. + I think that needs to change. A `NMIPConfig` object on D-Bus exposes IP configuration + information about an netdev interface. Period. That also means that a `NMVpnConnection` + (which currently is like a active connection associated with the device) links to + the same `NMIPConfig` object as the underlying device. diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c index a3467930..98670c8d 100644 --- a/src/core/devices/nm-device-bond.c +++ b/src/core/devices/nm-device-bond.c @@ -922,7 +922,7 @@ deactivate(NMDevice *device) static void nm_device_bond_init(NMDeviceBond *self) { - nm_assert(nm_device_is_master(NM_DEVICE(self))); + nm_assert(nm_device_is_controller(NM_DEVICE(self))); } static const NMDBusInterfaceInfoExtended interface_info_device_bond = { @@ -958,7 +958,7 @@ nm_device_bond_class_init(NMDeviceBondClass *klass) device_class->connection_type_check_compatible = NM_SETTING_BOND_SETTING_NAME; device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_BOND); - device_class->is_master = TRUE; + device_class->is_controller = TRUE; device_class->get_generic_capabilities = get_generic_capabilities; device_class->complete_connection = complete_connection; diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c index ab3a6be9..2405beea 100644 --- a/src/core/devices/nm-device-bridge.c +++ b/src/core/devices/nm-device-bridge.c @@ -1159,7 +1159,7 @@ reapply_connection(NMDevice *device, NMConnection *con_old, NMConnection *con_ne static void nm_device_bridge_init(NMDeviceBridge *self) { - nm_assert(nm_device_is_master(NM_DEVICE(self))); + nm_assert(nm_device_is_controller(NM_DEVICE(self))); } static const NMDBusInterfaceInfoExtended interface_info_device_bridge = { @@ -1194,7 +1194,7 @@ nm_device_bridge_class_init(NMDeviceBridgeClass *klass) device_class->connection_type_supported = NM_SETTING_BRIDGE_SETTING_NAME; device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_BRIDGE); - device_class->is_master = TRUE; + device_class->is_controller = TRUE; device_class->mtu_force_set = TRUE; device_class->get_generic_capabilities = get_generic_capabilities; device_class->check_connection_compatible = check_connection_compatible; diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c index 0f7f9d65..8c2a9643 100644 --- a/src/core/devices/nm-device-ethernet.c +++ b/src/core/devices/nm-device-ethernet.c @@ -370,7 +370,7 @@ check_connection_compatible(NMDevice *device, if (s_wired) { const char *mac, *perm_hw_addr; gboolean try_mac = TRUE; - const char *const *mac_blacklist; + const char *const *mac_denylist; int i; if (!match_subchans(self, s_wired, &try_mac)) { @@ -390,17 +390,17 @@ check_connection_compatible(NMDevice *device, return FALSE; } - /* Check for MAC address blacklist */ - mac_blacklist = nm_setting_wired_get_mac_address_blacklist(s_wired); - for (i = 0; mac_blacklist[i]; i++) { - if (!nm_utils_hwaddr_valid(mac_blacklist[i], ETH_ALEN)) { + /* Check for MAC address denylist */ + mac_denylist = nm_setting_wired_get_mac_address_denylist(s_wired); + for (i = 0; mac_denylist[i]; i++) { + if (!nm_utils_hwaddr_valid(mac_denylist[i], ETH_ALEN)) { nm_utils_error_set_literal(error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, "invalid MAC in blacklist"); return FALSE; } - if (nm_utils_hwaddr_matches(mac_blacklist[i], -1, perm_hw_addr, -1)) { + if (nm_utils_hwaddr_matches(mac_denylist[i], -1, perm_hw_addr, -1)) { nm_utils_error_set_literal(error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, "permanent MAC address of device blacklisted"); diff --git a/src/core/devices/nm-device-factory.c b/src/core/devices/nm-device-factory.c index 69c2a38f..22c8fa5a 100644 --- a/src/core/devices/nm-device-factory.c +++ b/src/core/devices/nm-device-factory.c @@ -183,7 +183,8 @@ nm_device_factory_class_init(NMDeviceFactoryClass *klass) static GHashTable *factories_by_link = NULL; static GHashTable *factories_by_setting = NULL; -static void __attribute__((destructor)) _cleanup(void) +static void __attribute__((destructor)) +_cleanup(void) { nm_clear_pointer(&factories_by_link, g_hash_table_unref); nm_clear_pointer(&factories_by_setting, g_hash_table_unref); diff --git a/src/core/devices/nm-device-utils.c b/src/core/devices/nm-device-utils.c index ed0a2738..80909805 100644 --- a/src/core/devices/nm-device-utils.c +++ b/src/core/devices/nm-device-utils.c @@ -128,8 +128,20 @@ NM_UTILS_LOOKUP_STR_DEFINE( NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED, "sriov-configuration-failed"), NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PEER_NOT_FOUND, "peer-not-found"), - NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED, - "device-handler-failed"), ); + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED, "device-handler-failed"), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_BY_DEFAULT, "unmanaged-by-default"), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_EXTERNAL_DOWN, + "unmanaged-external-down"), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_LINK_NOT_INIT, + "unmanaged-link-not-init"), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_QUITTING, "unmanaged-quitting"), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING, "unmanaged-sleeping"), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_CONF, "unmanaged-user-conf"), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_EXPLICIT, + "unmanaged-user-explicit"), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_SETTINGS, + "unmanaged-user-settings"), + NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_UDEV, "unmanaged-user-udev"), ); NM_UTILS_LOOKUP_STR_DEFINE(nm_device_mtu_source_to_string, NMDeviceMtuSource, diff --git a/src/core/devices/nm-device-vrf.c b/src/core/devices/nm-device-vrf.c index a13de1cb..6c7adc6c 100644 --- a/src/core/devices/nm-device-vrf.c +++ b/src/core/devices/nm-device-vrf.c @@ -329,7 +329,7 @@ nm_device_vrf_class_init(NMDeviceVrfClass *klass) device_class->connection_type_supported = NM_SETTING_VRF_SETTING_NAME; device_class->connection_type_check_compatible = NM_SETTING_VRF_SETTING_NAME; - device_class->is_master = TRUE; + device_class->is_controller = TRUE; device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_VRF); device_class->attach_port = attach_port; diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 34022efb..5b377529 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -95,6 +95,9 @@ #define CARRIER_WAIT_TIME_MS 6000 #define CARRIER_WAIT_TIME_AFTER_MTU_MSEC 10000 +#define SECONDS_PER_WEEK 604800 +#define SECONDS_PER_DAY 86400 + #define NM_DEVICE_AUTH_RETRIES_UNSET -1 #define NM_DEVICE_AUTH_RETRIES_INFINITY -2 #define NM_DEVICE_AUTH_RETRIES_DEFAULT 3 @@ -371,7 +374,6 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDevice, PROP_IFINDEX, PROP_AVAILABLE_CONNECTIONS, PROP_PHYSICAL_PORT_ID, - PROP_MASTER, PROP_PARENT, PROP_HW_ADDRESS, PROP_PERM_HW_ADDRESS, @@ -386,7 +388,8 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDevice, PROP_IP4_CONNECTIVITY, PROP_IP6_CONNECTIVITY, PROP_INTERFACE_FLAGS, - PROP_PORTS, ); + PROP_PORTS, + PROP_CONTROLLER, ); typedef struct _NMDevicePrivate { guint device_link_changed_id; @@ -707,10 +710,10 @@ typedef struct _NMDevicePrivate { NMPlatformLinkChangeFlags flags; } link_props_state; - /* master interface for bridge/bond/team slave */ - NMDevice *master; - gulong master_ready_id; - int master_ifindex; + /* controller interface for bridge/bond/team slave */ + NMDevice *controller; + gulong controller_ready_id; + int controller_ifindex; /* slave management */ CList slaves; /* list of SlaveInfo */ @@ -1523,11 +1526,12 @@ _prop_get_ipvx_route_table(NMDevice *self, int addr_family) if (route_table == 0u && connection && (s_con = nm_connection_get_setting_connection(connection)) && (nm_streq0(nm_setting_connection_get_port_type(s_con), NM_SETTING_VRF_SETTING_NAME) - && priv->master && nm_device_get_device_type(priv->master) == NM_DEVICE_TYPE_VRF)) { + && priv->controller + && nm_device_get_device_type(priv->controller) == NM_DEVICE_TYPE_VRF)) { const NMPlatformLnkVrf *lnk; lnk = nm_platform_link_get_lnk_vrf(nm_device_get_platform(self), - nm_device_get_ifindex(priv->master), + nm_device_get_ifindex(priv->controller), NULL); if (lnk) @@ -2269,6 +2273,7 @@ _prop_get_ipv4_dhcp_vendor_class_identifier(NMDevice *self, NMSettingIP4Config * static NMSettingIP6ConfigPrivacy _prop_get_ipv6_ip6_privacy(NMDevice *self) { + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); NMSettingIP6ConfigPrivacy ip6_privacy; NMConnection *connection; @@ -2302,16 +2307,100 @@ _prop_get_ipv6_ip6_privacy(NMDevice *self) if (!nm_device_get_ip_ifindex(self)) return NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; - /* 3.) No valid default-value configured. Fallback to reading sysctl. - * - * Instead of reading static config files in /etc, just read the current sysctl value. - * This works as NM only writes to "/proc/sys/net/ipv6/conf/IFNAME/use_tempaddr", but leaves - * the "default" entry untouched. */ - ip6_privacy = nm_platform_sysctl_get_int32( - nm_device_get_platform(self), - NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv6/conf/default/use_tempaddr"), - NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - return _ip6_privacy_clamp(ip6_privacy); + /* 3.) No valid default value configured. Fall back to the original value + * from before NM started. */ + return _ip6_privacy_clamp(_nm_utils_ascii_str_to_int64( + g_hash_table_lookup(priv->ip6_saved_properties, "use_tempaddr"), + 10, + G_MININT32, + G_MAXINT32, + NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN)); +} + +static gint32 +_prop_get_ipv6_temp_valid_lifetime(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gint32 temp_valid_lifetime; + NMConnection *connection; + + g_return_val_if_fail(self, 0); + + /* 1.) First look at the per-connection setting. If it is not 0 (unknown), use it. */ + connection = nm_device_get_applied_connection(self); + if (connection) { + NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config(connection); + + if (s_ip6) { + temp_valid_lifetime = + nm_setting_ip6_config_get_temp_valid_lifetime(NM_SETTING_IP6_CONFIG(s_ip6)); + if (temp_valid_lifetime) + return temp_valid_lifetime; + } + } + + /* 2.) Use the default value from the configuration. */ + temp_valid_lifetime = + nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, + NM_CON_DEFAULT("ipv6.temp-valid-lifetime"), + self, + 0, + G_MAXINT32, + 0); + if (temp_valid_lifetime) + return temp_valid_lifetime; + + /* 3.) No valid default value configured. Fall back to the original value + * from before NM started. */ + return _nm_utils_ascii_str_to_int64( + g_hash_table_lookup(priv->ip6_saved_properties, "temp_valid_lft"), + 10, + 0, + G_MAXINT32, + SECONDS_PER_WEEK /* final hardcoded fallback: 1 week */); +} + +static gint32 +_prop_get_ipv6_temp_preferred_lifetime(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gint32 temp_preferred_lifetime; + NMConnection *connection; + + g_return_val_if_fail(self, 0); + + /* 1.) First look at the per-connection setting. If it is not 0 (unknown), use it. */ + connection = nm_device_get_applied_connection(self); + if (connection) { + NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config(connection); + + if (s_ip6) { + temp_preferred_lifetime = + nm_setting_ip6_config_get_temp_preferred_lifetime(NM_SETTING_IP6_CONFIG(s_ip6)); + if (temp_preferred_lifetime) + return temp_preferred_lifetime; + } + } + + /* 2.) Use the default value from the configuration. */ + temp_preferred_lifetime = + nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, + NM_CON_DEFAULT("ipv6.temp-preferred-lifetime"), + self, + 0, + G_MAXINT32, + 0); + if (temp_preferred_lifetime) + return temp_preferred_lifetime; + + /* 3.) No valid default value configured. Fall back to the original value + * from before NM started. */ + return _nm_utils_ascii_str_to_int64( + g_hash_table_lookup(priv->ip6_saved_properties, "temp_prefered_lft"), + 10, + 0, + G_MAXINT32, + SECONDS_PER_DAY /* final hardcoded fallback: 1 day */); } static NMSettingIP6ConfigAddrGenMode @@ -2359,11 +2448,16 @@ _prop_get_ipv6_addr_gen_mode(NMDevice *self) } static const char * -_prop_get_x_cloned_mac_address(NMDevice *self, NMConnection *connection, gboolean is_wifi) +_prop_get_x_cloned_mac_address(NMDevice *self, + NMConnection *connection, + gboolean is_wifi, + gboolean *out_is_default_special) { NMSetting *setting; const char *addr = NULL; + NM_SET_OUT(out_is_default_special, FALSE); + setting = nm_connection_get_setting(connection, is_wifi ? NM_TYPE_SETTING_WIRELESS : NM_TYPE_SETTING_WIRED); if (setting) { @@ -2394,11 +2488,17 @@ _prop_get_x_cloned_mac_address(NMDevice *self, NMConnection *connection, gboolea NM_SETTING_MAC_RANDOMIZATION_DEFAULT, NM_SETTING_MAC_RANDOMIZATION_ALWAYS, NM_SETTING_MAC_RANDOMIZATION_DEFAULT); - if (v == NM_SETTING_MAC_RANDOMIZATION_ALWAYS) + if (v == NM_SETTING_MAC_RANDOMIZATION_ALWAYS) { addr = NM_CLONED_MAC_RANDOM; + NM_SET_OUT(out_is_default_special, TRUE); + } } - } else if (NM_CLONED_MAC_IS_SPECIAL(a, is_wifi) || nm_utils_hwaddr_valid(a, ETH_ALEN)) + } else if (NM_CLONED_MAC_IS_SPECIAL(a, is_wifi)) { + addr = a; + NM_SET_OUT(out_is_default_special, TRUE); + } else if (nm_utils_hwaddr_valid(a, ETH_ALEN)) { addr = a; + } } return addr; @@ -3726,7 +3826,7 @@ _dev_ip_state_check(NMDevice *self, int addr_family) } if (priv->ip_data_x[IS_IPv4].state == NM_DEVICE_IP_STATE_PENDING - && nm_active_connection_get_master(NM_ACTIVE_CONNECTION(priv->act_request.obj)) + && nm_active_connection_get_controller(NM_ACTIVE_CONNECTION(priv->act_request.obj)) && !priv->is_enslaved) { /* Don't progress into IP_CHECK or SECONDARIES if we're waiting for the * master to enslave us. */ @@ -6676,14 +6776,14 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co static void detach_port_cb(NMDevice *self, GError *error, gpointer user_data) { - nm_auto_unref_object NMDevice *slave = user_data; - NMDevicePrivate *slave_priv = NM_DEVICE_GET_PRIVATE(slave); + nm_auto_unref_object NMDevice *slave = user_data; + NMDevicePrivate *port_priv = NM_DEVICE_GET_PRIVATE(slave); - nm_assert(slave_priv->port_detach_count > 0); + nm_assert(port_priv->port_detach_count > 0); - if (--slave_priv->port_detach_count == 0) { - if (slave_priv->state == NM_DEVICE_STATE_DEACTIVATING) { - deactivate_ready(slave, slave_priv->port_detach_reason); + if (--port_priv->port_detach_count == 0) { + if (port_priv->state == NM_DEVICE_STATE_DEACTIVATING) { + deactivate_ready(slave, port_priv->port_detach_reason); } } } @@ -6708,7 +6808,7 @@ nm_device_master_release_slave(NMDevice *self, NMDeviceStateReason reason) { NMDevicePrivate *priv; - NMDevicePrivate *slave_priv; + NMDevicePrivate *port_priv; SlaveInfo *info; gs_unref_object NMDevice *self_free = NULL; gs_unref_object NMDevice *slave_free = NULL; @@ -6735,10 +6835,10 @@ nm_device_master_release_slave(NMDevice *self, if (!info) g_return_if_reached(); - priv = NM_DEVICE_GET_PRIVATE(self); - slave_priv = NM_DEVICE_GET_PRIVATE(slave); + priv = NM_DEVICE_GET_PRIVATE(self); + port_priv = NM_DEVICE_GET_PRIVATE(slave); - g_return_if_fail(self == slave_priv->master); + g_return_if_fail(self == port_priv->controller); nm_assert(slave == info->slave); nm_clear_g_cancellable(&info->cancellable); @@ -6754,8 +6854,8 @@ nm_device_master_release_slave(NMDevice *self, detach_port_cb, g_object_ref(slave)); if (ret == NM_TERNARY_DEFAULT) { - slave_priv->port_detach_count++; - slave_priv->port_detach_reason = reason; + port_priv->port_detach_count++; + port_priv->port_detach_reason = reason; } } @@ -6763,9 +6863,9 @@ nm_device_master_release_slave(NMDevice *self, nm_device_slave_notify_release(slave, reason, release_type); /* keep both alive until the end of the function. - * Transfers ownership from slave_priv->master. */ - nm_assert(self == slave_priv->master); - self_free = g_steal_pointer(&slave_priv->master); + * Transfers ownership from port_priv->controller. */ + nm_assert(self == port_priv->controller); + self_free = g_steal_pointer(&port_priv->controller); nm_assert(slave == info->slave); slave_free = g_steal_pointer(&info->slave); @@ -6900,7 +7000,7 @@ carrier_changed(NMDevice *self, gboolean carrier) if (priv->ignore_carrier && !carrier) return; - if (nm_device_is_master(self)) { + if (nm_device_is_controller(self)) { if (carrier) { /* If needed, also resume IP configuration that is * waiting for carrier. */ @@ -7069,7 +7169,7 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink) plink_master = nm_platform_link_get(nm_device_get_platform(self), plink->master); plink_master_keep_alive = nmp_object_ref(NMP_OBJECT_UP_CAST(plink_master)); } else { - if (priv->master_ifindex == 0) + if (priv->controller_ifindex == 0) goto out; master = NULL; plink_master = NULL; @@ -7082,17 +7182,17 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink) goto out; } - priv->master_ifindex = plink->master; + priv->controller_ifindex = plink->master; - if (priv->master) { - if (plink->master > 0 && plink->master == nm_device_get_ifindex(priv->master)) { + if (priv->controller) { + if (plink->master > 0 && plink->master == nm_device_get_ifindex(priv->controller)) { /* call add-slave again. We expect @self already to be added to * the master, but this also triggers a recheck-assume. */ - nm_device_master_add_slave(priv->master, self, FALSE); + nm_device_master_add_slave(priv->controller, self, FALSE); goto out; } - nm_device_master_release_slave(priv->master, + nm_device_master_release_slave(priv->controller, self, RELEASE_SLAVE_TYPE_NO_CONFIG, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); @@ -7133,9 +7233,9 @@ device_ifindex_changed_cb(NMManager *manager, NMDevice *device_changed, NMDevice { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - g_return_if_fail(priv->master_ifindex > 0); + g_return_if_fail(priv->controller_ifindex > 0); - if (priv->master_ifindex != nm_device_get_ifindex(device_changed)) + if (priv->controller_ifindex != nm_device_get_ifindex(device_changed)) return; _LOGD(LOGD_DEVICE, @@ -8157,7 +8257,7 @@ nm_device_unrealize(NMDevice *self, gboolean remove_resources, GError **error) if (nm_clear_g_free(&priv->ip_iface_)) update_prop_ip_iface(self); - priv->master_ifindex = 0; + priv->controller_ifindex = 0; _set_mtu(self, 0); @@ -8394,7 +8494,7 @@ static gboolean nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure) { NMDevicePrivate *priv; - NMDevicePrivate *slave_priv; + NMDevicePrivate *port_priv; SlaveInfo *info; gboolean changed = FALSE; @@ -8402,8 +8502,8 @@ nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure) g_return_val_if_fail(NM_IS_DEVICE(slave), FALSE); g_return_val_if_fail(NM_DEVICE_GET_CLASS(self)->attach_port, FALSE); - priv = NM_DEVICE_GET_PRIVATE(self); - slave_priv = NM_DEVICE_GET_PRIVATE(slave); + priv = NM_DEVICE_GET_PRIVATE(self); + port_priv = NM_DEVICE_GET_PRIVATE(slave); info = find_slave_info(self, slave); @@ -8417,8 +8517,8 @@ nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure) g_return_val_if_fail(nm_device_get_state(slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); if (!info) { - g_return_val_if_fail(!slave_priv->master, FALSE); - g_return_val_if_fail(!slave_priv->is_enslaved, FALSE); + g_return_val_if_fail(!port_priv->controller, FALSE); + g_return_val_if_fail(!port_priv->is_enslaved, FALSE); info = g_slice_new0(SlaveInfo); info->slave = g_object_ref(slave); @@ -8426,25 +8526,25 @@ nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure) info->watch_id = g_signal_connect(slave, NM_DEVICE_STATE_CHANGED, G_CALLBACK(slave_state_changed), self); c_list_link_tail(&priv->slaves, &info->lst_slave); - slave_priv->master = g_object_ref(self); + port_priv->controller = g_object_ref(self); _active_connection_set_state_flags(self, NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES); /* no need to emit * - * _notify (slave, PROP_MASTER); + * _notify (slave, PROP_CONTROLLER); * - * because slave_priv->is_enslaved is not true, thus the value + * because port_priv->is_enslaved is not true, thus the value * didn't change yet. */ - g_warn_if_fail(!NM_FLAGS_HAS(slave_priv->unmanaged_mask, NM_UNMANAGED_IS_SLAVE)); + g_warn_if_fail(!NM_FLAGS_HAS(port_priv->unmanaged_mask, NM_UNMANAGED_IS_SLAVE)); nm_device_set_unmanaged_by_flags(slave, NM_UNMANAGED_IS_SLAVE, NM_UNMAN_FLAG_OP_SET_MANAGED, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); changed = TRUE; } else - g_return_val_if_fail(slave_priv->master == self, FALSE); + g_return_val_if_fail(port_priv->controller == self, FALSE); nm_device_queue_recheck_assume(self); nm_device_queue_recheck_assume(slave); @@ -8525,38 +8625,40 @@ nm_device_master_release_slaves_all(NMDevice *self) } /** - * nm_device_is_master: + * nm_device_is_controller: * @self: the device * - * Returns: %TRUE if the device can have slaves + * Returns: %TRUE if the device can have ports */ gboolean -nm_device_is_master(NMDevice *self) +nm_device_is_controller(NMDevice *self) { g_return_val_if_fail(NM_IS_DEVICE(self), FALSE); - return NM_DEVICE_GET_CLASS(self)->is_master; + return NM_DEVICE_GET_CLASS(self)->is_controller; } /** - * nm_device_get_master: + * nm_device_get_controller: * @self: the device * - * If @self has been enslaved by another device, this returns that + * If @self has been set as port by another device, this returns that * device. Otherwise, it returns %NULL. (In particular, note that if - * @self is in the process of activating as a slave, but has not yet - * been enslaved by its master, this will return %NULL.) + * @self is in the process of activating as a port, but has not yet + * been set as port by its controller, this will return %NULL.) + * + * Returns: (transfer none): @self's controller, or %NULL * - * Returns: (transfer none): @self's master, or %NULL + * Since: 1.48 */ NMDevice * -nm_device_get_master(NMDevice *self) +nm_device_get_controller(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); if (priv->is_enslaved) { - g_return_val_if_fail(priv->master, NULL); - return priv->master; + g_return_val_if_fail(priv->controller, NULL); + return priv->controller; } return NULL; } @@ -8576,7 +8678,7 @@ nm_device_slave_notify_enslave(NMDevice *self, gboolean success) NMConnection *connection = nm_device_get_applied_connection(self); gboolean activating = (priv->state == NM_DEVICE_STATE_IP_CONFIG); - g_return_if_fail(priv->master); + g_return_if_fail(priv->controller); if (!priv->is_enslaved) { if (success) { @@ -8585,14 +8687,15 @@ nm_device_slave_notify_enslave(NMDevice *self, gboolean success) "Activation: connection '%s' enslaved, continuing activation", nm_connection_get_id(connection)); } else - _LOGI(LOGD_DEVICE, "enslaved to %s", nm_device_get_iface(priv->master)); + _LOGI(LOGD_DEVICE, "enslaved to %s", nm_device_get_iface(priv->controller)); priv->is_enslaved = TRUE; - _notify(self, PROP_MASTER); + _notify(priv->controller, PROP_CONTROLLER); - nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->master)->ports_variant, g_variant_unref); - nm_gobject_notify_together(priv->master, PROP_PORTS, PROP_SLAVES); + nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->controller)->ports_variant, + g_variant_unref); + nm_gobject_notify_together(priv->controller, PROP_PORTS, PROP_SLAVES); } else if (activating) { _LOGW(LOGD_DEVICE, "Activation: connection '%s' could not be enslaved", @@ -8629,7 +8732,7 @@ nm_device_slave_notify_release(NMDevice *self, NMConnection *connection = nm_device_get_applied_connection(self); const char *master_status; - g_return_if_fail(priv->master); + g_return_if_fail(priv->controller); if (!priv->is_enslaved && release_type == RELEASE_SLAVE_TYPE_NO_CONFIG) return; @@ -8661,14 +8764,14 @@ nm_device_slave_notify_release(NMDevice *self, _cancel_activation(self); nm_device_queue_state(self, NM_DEVICE_STATE_DEACTIVATING, reason); } else - _LOGI(LOGD_DEVICE, "released from master device %s", nm_device_get_iface(priv->master)); + _LOGI(LOGD_DEVICE, "released from master device %s", nm_device_get_iface(priv->controller)); priv->is_enslaved = FALSE; - _notify(self, PROP_MASTER); + _notify(priv->controller, PROP_CONTROLLER); - nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->master)->ports_variant, g_variant_unref); - nm_gobject_notify_together(priv->master, PROP_PORTS, PROP_SLAVES); + nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->controller)->ports_variant, g_variant_unref); + nm_gobject_notify_together(priv->controller, PROP_PORTS, PROP_SLAVES); } /** @@ -8692,10 +8795,10 @@ nm_device_removed(NMDevice *self, gboolean unconfigure_ip_config) _dev_ipdhcpx_cleanup(self, AF_INET6, TRUE, FALSE); priv = NM_DEVICE_GET_PRIVATE(self); - if (priv->master) { + if (priv->controller) { /* this is called when something externally messes with the slave or during shut-down. * Release the slave from master, but don't touch the device. */ - nm_device_master_release_slave(priv->master, + nm_device_master_release_slave(priv->controller, self, RELEASE_SLAVE_TYPE_NO_CONFIG, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); @@ -8728,7 +8831,7 @@ is_available(NMDevice *self, NMDeviceCheckDevAvailableFlags flags) return TRUE; /* master types are always available even without carrier. */ - if (nm_device_is_master(self)) + if (nm_device_is_controller(self)) return TRUE; return FALSE; @@ -8768,7 +8871,7 @@ gboolean nm_device_ignore_carrier_by_default(NMDevice *self) { /* master types ignore-carrier by default. */ - return nm_device_is_master(self); + return nm_device_is_controller(self); } gboolean @@ -9207,7 +9310,7 @@ nm_device_generate_connection(NMDevice *self, && NM_IN_STRSET(ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NM_SETTING_IP6_CONFIG_METHOD_DISABLED) - && !nm_setting_connection_get_master(NM_SETTING_CONNECTION(s_con)) + && !nm_setting_connection_get_controller(NM_SETTING_CONNECTION(s_con)) && c_list_is_empty(&priv->slaves)) { NM_SET_OUT(out_maybe_later, TRUE); g_set_error_literal( @@ -9223,7 +9326,7 @@ nm_device_generate_connection(NMDevice *self, */ if (nm_streq0(ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) && nm_streq0(ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) - && !nm_setting_connection_get_master(NM_SETTING_CONNECTION(s_con)) + && !nm_setting_connection_get_controller(NM_SETTING_CONNECTION(s_con)) && c_list_is_empty(&priv->slaves) && !nm_config_data_get_assume_ipv6ll_only(NM_CONFIG_GET_DATA, self)) { _LOGD(LOGD_DEVICE, @@ -9492,7 +9595,7 @@ nm_device_check_slave_connection_compatible(NMDevice *self, NMConnection *slave) g_return_val_if_fail(NM_IS_DEVICE(self), FALSE); g_return_val_if_fail(NM_IS_CONNECTION(slave), FALSE); - if (!nm_device_is_master(self)) + if (!nm_device_is_controller(self)) return FALSE; /* All masters should have connection type set */ @@ -9508,6 +9611,17 @@ nm_device_check_slave_connection_compatible(NMDevice *self, NMConnection *slave) return nm_streq(connection_type, slave_type); } +gboolean +nm_device_can_be_parent(NMDevice *self) +{ + NMDeviceType device_type = nm_device_get_device_type(self); + + if ((device_type == NM_DEVICE_TYPE_OVS_BRIDGE) || (device_type == NM_DEVICE_TYPE_OVS_PORT)) + return FALSE; + else + return TRUE; +} + /** * nm_device_can_assume_connections: * @self: #NMDevice instance @@ -9790,17 +9904,17 @@ master_ready(NMDevice *self, NMActiveConnection *active) NMActiveConnection *master_connection; NMDevice *master; - /* Notify a master device that it has a new slave */ - nm_assert(nm_active_connection_get_master_ready(active)); + /* Notify a controller device that it has a new port */ + nm_assert(nm_active_connection_get_controller_ready(active)); - master_connection = nm_active_connection_get_master(active); + master_connection = nm_active_connection_get_controller(active); master = nm_active_connection_get_device(master_connection); _LOGD(LOGD_DEVICE, "master connection ready; master device %s", nm_device_get_iface(master)); - if (priv->master && priv->master != master) - nm_device_master_release_slave(priv->master, + if (priv->controller && priv->controller != master) + nm_device_master_release_slave(priv->controller, self, RELEASE_SLAVE_TYPE_NO_CONFIG, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); @@ -9816,7 +9930,7 @@ master_ready_cb(NMActiveConnection *active, GParamSpec *pspec, NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - nm_assert(nm_active_connection_get_master_ready(active)); + nm_assert(nm_active_connection_get_controller_ready(active)); if (priv->state == NM_DEVICE_STATE_PREPARE) nm_device_activate_schedule_stage1_device_prepare(self, FALSE); @@ -10063,7 +10177,7 @@ activate_stage1_device_prepare(NMDevice *self) } active = NM_ACTIVE_CONNECTION(priv->act_request.obj); - master = nm_active_connection_get_master(active); + master = nm_active_connection_get_controller(active); if (master) { if (nm_active_connection_get_state(master) >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) { NMDevice *master_device = nm_active_connection_get_device(master); @@ -10078,24 +10192,24 @@ activate_stage1_device_prepare(NMDevice *self) nm_device_state_changed(self, NM_DEVICE_STATE_FAILED, failure_reason); return; } - /* If the master connection is ready for slaves, attach ourselves */ - if (!nm_active_connection_get_master_ready(active)) { - if (priv->master_ready_id == 0) { - _LOGD(LOGD_DEVICE, "waiting for master connection to become ready"); - priv->master_ready_id = + /* If the controller connection is ready for ports, attach ourselves */ + if (!nm_active_connection_get_controller_ready(active)) { + if (priv->controller_ready_id == 0) { + _LOGD(LOGD_DEVICE, "waiting for controller connection to become ready"); + priv->controller_ready_id = g_signal_connect(active, - "notify::" NM_ACTIVE_CONNECTION_INT_MASTER_READY, + "notify::" NM_ACTIVE_CONNECTION_INT_CONTROLLER_READY, G_CALLBACK(master_ready_cb), self); } return; } } - nm_clear_g_signal_handler(priv->act_request.obj, &priv->master_ready_id); + nm_clear_g_signal_handler(priv->act_request.obj, &priv->controller_ready_id); if (master) master_ready(self, active); - else if (priv->master) { - nm_device_master_release_slave(priv->master, + else if (priv->controller) { + nm_device_master_release_slave(priv->controller, self, RELEASE_SLAVE_TYPE_CONFIG_FORCE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); @@ -10804,13 +10918,40 @@ _dev_ipdhcpx_set_state(NMDevice *self, int addr_family, NMDeviceIPState state) } static void -_dev_ipdhcpx_cleanup(NMDevice *self, int addr_family, gboolean full_cleanup, gboolean release) +_dev_ipdhcpx_cleanup(NMDevice *self, int addr_family, gboolean full_cleanup, gboolean force_release) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMSettingIPConfig *s_ip4 = NULL; + NMSettingIPConfig *s_ip6 = NULL; + gboolean release; _dev_ipdhcpx_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE); + if (IS_IPv4) + s_ip4 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP4_CONFIG); + else + s_ip6 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP6_CONFIG); + + if ((IS_IPv4 && s_ip4) || (!IS_IPv4 && s_ip6)) { + if (nm_setting_ip_config_get_dhcp_send_release(IS_IPv4 ? s_ip4 : s_ip6) + == NM_TERNARY_DEFAULT) + release = nm_config_data_get_connection_default_int64( + NM_CONFIG_GET_DATA, + IS_IPv4 ? NM_CON_DEFAULT("ipv4.dhcp-send-release") + : NM_CON_DEFAULT("ipv6.dhcp-send-release"), + self, + NM_TERNARY_FALSE, + NM_TERNARY_TRUE, + NM_TERNARY_FALSE); + else + release = nm_setting_ip_config_get_dhcp_send_release(IS_IPv4 ? s_ip4 : s_ip6); + + release = force_release || (release && full_cleanup); + } else { + release = force_release; + } + if (full_cleanup && !IS_IPv4) { priv->ipdhcp_data_6.v6.mode = NM_NDISC_DHCP_LEVEL_NONE; priv->ipdhcp_data_6.v6.needed_prefixes = 0; @@ -11306,7 +11447,7 @@ connection_requires_carrier(NMConnection *connection) /* We can progress to IP_CONFIG now, so that we're enslaved. * That may actually cause carrier to go up and thus continue activation. */ s_con = nm_connection_get_setting_connection(connection); - if (nm_setting_connection_get_master(s_con)) + if (nm_setting_connection_get_controller(s_con)) return FALSE; ip4_carrier_wanted = connection_ip_method_requires_carrier(connection, AF_INET, &ip4_used); @@ -11373,25 +11514,32 @@ nm_device_needs_ip6_subnet(NMDevice *self) void nm_device_use_ip6_subnet(NMDevice *self, const NMPlatformIP6Address *subnet) { - nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; - char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; - NMPlatformIP6Address address; + NMConnection *connection = nm_device_get_applied_connection(self); - l3cd = nm_device_create_l3_config_data(self, NM_IP_CONFIG_SOURCE_SHARED); + if (connection) { + NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config(connection); - /* Assign a ::1 address in the subnet for us. */ - address = *subnet; - address.address.s6_addr32[3] |= htonl(1); + if (nm_streq(nm_setting_ip_config_get_method(s_ip6), NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + NMPlatformIP6Address address; + l3cd = nm_device_create_l3_config_data(self, NM_IP_CONFIG_SOURCE_SHARED); - nm_l3_config_data_add_address_6(l3cd, &address); + /* Assign a ::1 address in the subnet for us. */ + address = *subnet; + address.address.s6_addr32[3] |= htonl(1); - _LOGD(LOGD_IP6, - "ipv6-pd: using %s", - nm_platform_ip6_address_to_string(&address, sbuf, sizeof(sbuf))); + nm_l3_config_data_add_address_6(l3cd, &address); - _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_PD_6, l3cd, FALSE); - _dev_l3_cfg_commit(self, TRUE); - _dev_ipac6_ndisc_set_router_config(self); + _LOGD(LOGD_IP6, + "ipv6-pd: using %s", + nm_platform_ip6_address_to_string(&address, sbuf, sizeof(sbuf))); + + _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_PD_6, l3cd, FALSE); + _dev_l3_cfg_commit(self, TRUE); + _dev_ipac6_ndisc_set_router_config(self); + } + } } /* @@ -11768,12 +11916,12 @@ _set_mtu(NMDevice *self, guint32 mtu) priv->mtu = mtu; _notify(self, PROP_MTU); - if (priv->master) { + if (priv->controller) { /* changing the MTU of a slave, might require the master to reset * 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); + nm_device_commit_mtu(priv->controller); } } @@ -12377,6 +12525,8 @@ _dev_sysctl_save_ip6_properties(NMDevice *self) "disable_ipv6", "hop_limit", "use_tempaddr", + "temp_valid_lft", + "temp_prefered_lft", }; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); NMPlatform *platform = nm_device_get_platform(self); @@ -12476,6 +12626,17 @@ _dev_addrgenmode6_set(NMDevice *self, guint8 addr_gen_mode) } } + nm_device_sysctl_ip_conf_set( + self, + AF_INET6, + "temp_valid_lft", + nm_sprintf_buf(sbuf, "%u", (unsigned) _prop_get_ipv6_temp_valid_lifetime(self))); + nm_device_sysctl_ip_conf_set( + self, + AF_INET6, + "temp_prefered_lft", + nm_sprintf_buf(sbuf, "%u", (unsigned) _prop_get_ipv6_temp_preferred_lifetime(self))); + if (addr_gen_mode == NM_IN6_ADDR_GEN_MODE_NONE) { gs_free char *value = NULL; @@ -12536,7 +12697,7 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family, const ip_ifindex = nm_device_get_ip_ifindex(self); if (connection_ip_method_requires_carrier(connection, addr_family, NULL) - && nm_device_is_master(self) && !priv->carrier) { + && nm_device_is_controller(self) && !priv->carrier) { if (!priv->ip_data_x[IS_IPv4].wait_for_carrier) { _LOGT_ip(addr_family, "waiting until carrier is on"); priv->ip_data_x[IS_IPv4].wait_for_carrier = TRUE; @@ -12548,7 +12709,7 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family, const priv->ip_data_x[IS_IPv4].wait_for_carrier = FALSE; } - if (nm_device_is_master(self) && ip_requires_slaves(self, addr_family)) { + if (nm_device_is_controller(self) && ip_requires_slaves(self, addr_family)) { /* If the master has no ready slaves, and depends on slaves for * a successful IP configuration attempt, then postpone IP addressing. */ @@ -12595,7 +12756,7 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family, const } else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { if (!priv->ip_data_x[IS_IPv4].is_ignore) { priv->ip_data_x[IS_IPv4].is_ignore = TRUE; - if (priv->master) { + if (priv->controller) { /* If a device only has an IPv6 link-local address, * we don't generate an assumed connection. Therefore, * when a new slave connection (without IP configuration) @@ -14959,8 +15120,39 @@ _unmanaged_flags2str(NMUnmanagedFlags flags, NMUnmanagedFlags mask, char *buf, g return buf; } +static NMDeviceStateReason +unmanaged_flags_to_reason(NMUnmanagedFlags flags) +{ + /* Even if there are multiple flags, we can only return one reason. + * Return the most important reason. + */ + if (NM_FLAGS_HAS(flags, NM_UNMANAGED_SLEEPING)) + return NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING; + if (NM_FLAGS_HAS(flags, NM_UNMANAGED_QUITTING)) + return NM_DEVICE_STATE_REASON_UNMANAGED_QUITTING; + if (NM_FLAGS_HAS(flags, NM_UNMANAGED_USER_SETTINGS)) + return NM_DEVICE_STATE_REASON_UNMANAGED_USER_SETTINGS; + if (NM_FLAGS_HAS(flags, NM_UNMANAGED_PLATFORM_INIT)) + return NM_DEVICE_STATE_REASON_UNMANAGED_LINK_NOT_INIT; + if (NM_FLAGS_HAS(flags, NM_UNMANAGED_USER_UDEV)) + return NM_DEVICE_STATE_REASON_UNMANAGED_USER_UDEV; + if (NM_FLAGS_HAS(flags, NM_UNMANAGED_USER_EXPLICIT)) + return NM_DEVICE_STATE_REASON_UNMANAGED_USER_EXPLICIT; + if (NM_FLAGS_HAS(flags, NM_UNMANAGED_USER_CONF)) + return NM_DEVICE_STATE_REASON_UNMANAGED_USER_CONF; + if (NM_FLAGS_HAS(flags, NM_UNMANAGED_BY_DEFAULT)) + return NM_DEVICE_STATE_REASON_UNMANAGED_BY_DEFAULT; + if (NM_FLAGS_HAS(flags, NM_UNMANAGED_EXTERNAL_DOWN)) + return NM_DEVICE_STATE_REASON_UNMANAGED_EXTERNAL_DOWN; + + return NM_DEVICE_STATE_REASON_NOW_UNMANAGED; +} + static gboolean -_get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean for_user_request) +_get_managed_by_flags(NMUnmanagedFlags flags, + NMUnmanagedFlags mask, + gboolean for_user_request, + NMDeviceStateReason *unmanaged_reason) { /* Evaluate the managed state based on the unmanaged flags. * @@ -14979,7 +15171,7 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo * * Effectively, this check is redundant, as the code below already * already ensures that. Still, express this invariant explicitly here. */ - if (_get_managed_by_flags(flags, mask, FALSE)) + if (_get_managed_by_flags(flags, mask, FALSE, unmanaged_reason)) return TRUE; /* A for-user-request, is effectively the same as pretending @@ -15022,7 +15214,12 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo | NM_UNMANAGED_EXTERNAL_DOWN); } - return flags == NM_UNMANAGED_NONE; + if (flags == NM_UNMANAGED_NONE) { + return TRUE; + } else { + NM_SET_OUT(unmanaged_reason, unmanaged_flags_to_reason(flags)); + return FALSE; + } } /** @@ -15051,7 +15248,10 @@ nm_device_get_managed(NMDevice *self, gboolean for_user_request) priv = NM_DEVICE_GET_PRIVATE(self); - return _get_managed_by_flags(priv->unmanaged_flags, priv->unmanaged_mask, for_user_request); + return _get_managed_by_flags(priv->unmanaged_flags, + priv->unmanaged_mask, + for_user_request, + NULL); } /** @@ -15190,9 +15390,9 @@ _set_unmanaged_flags(NMDevice *self, (priv->unmanaged_flags | priv->unmanaged_mask) ? "=" : "", (guint) priv->unmanaged_flags, (guint) priv->unmanaged_mask, - (_get_managed_by_flags(priv->unmanaged_flags, priv->unmanaged_mask, FALSE) + (_get_managed_by_flags(priv->unmanaged_flags, priv->unmanaged_mask, FALSE, NULL) ? "managed" - : (_get_managed_by_flags(priv->unmanaged_flags, priv->unmanaged_mask, TRUE) + : (_get_managed_by_flags(priv->unmanaged_flags, priv->unmanaged_mask, TRUE, NULL) ? "manageable" : "unmanaged")), priv->real ? "" : "/unrealized", @@ -15211,6 +15411,9 @@ _set_unmanaged_flags(NMDevice *self, if (transition_state) { new_state = was_managed ? NM_DEVICE_STATE_UNMANAGED : NM_DEVICE_STATE_UNAVAILABLE; if (new_state == NM_DEVICE_STATE_UNMANAGED) { + /* In state UNMANAGED, the reason always depends on current flags, not on what + * the caller passed. */ + _get_managed_by_flags(priv->unmanaged_flags, priv->unmanaged_mask, FALSE, &reason); _cancel_activation(self); } else { /* The assume check should happen before the device transitions to @@ -15225,6 +15428,13 @@ _set_unmanaged_flags(NMDevice *self, nm_device_state_changed(self, new_state, reason); else nm_device_queue_state(self, new_state, reason); + } else { + /* No state change, but possibly update the reason in UNMANAGED */ + if (!_get_managed_by_flags(priv->unmanaged_flags, priv->unmanaged_mask, FALSE, &reason) + && reason != priv->state_reason) { + priv->state_reason = reason; + _notify(self, PROP_STATE_REASON); + } } } @@ -15483,7 +15693,7 @@ nm_device_update_firewall_zone(NMDevice *self) void nm_device_update_metered(NMDevice *self) { -#define NM_METERED_INVALID ((NMMetered) -1) +#define NM_METERED_INVALID ((NMMetered) - 1) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); NMSettingConnection *setting; NMMetered conn_value, value = NM_METERED_INVALID; @@ -15773,7 +15983,7 @@ check_connection_available(NMDevice *self, return TRUE; } - if (nm_device_is_master(self)) { + if (nm_device_is_controller(self)) { /* master types are always available even without carrier. * Making connection non-available would un-enslave slaves which * is not desired. */ @@ -16164,12 +16374,12 @@ _cleanup_generic_post(NMDevice *self, NMDeviceStateReason reason, CleanupType cl nm_active_connection_set_default(NM_ACTIVE_CONNECTION(priv->act_request.obj), AF_INET, FALSE); - nm_clear_g_signal_handler(priv->act_request.obj, &priv->master_ready_id); + nm_clear_g_signal_handler(priv->act_request.obj, &priv->controller_ready_id); act_request_set(self, NULL); } if (cleanup_type == CLEANUP_TYPE_DECONFIGURE - && ((reason == NM_DEVICE_STATE_REASON_CARRIER && nm_device_is_master(self)) + && ((reason == NM_DEVICE_STATE_REASON_CARRIER && nm_device_is_controller(self)) || !NM_IN_SET(reason, NM_DEVICE_STATE_REASON_NOW_MANAGED, NM_DEVICE_STATE_REASON_CARRIER))) { @@ -16253,9 +16463,9 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu nm_platform_ip4_dev_route_blacklist_set(nm_device_get_platform(self), ifindex, NULL); /* slave: mark no longer enslaved */ - if (priv->master && priv->ifindex > 0 + if (priv->controller && priv->ifindex > 0 && nm_platform_link_get_master(nm_device_get_platform(self), priv->ifindex) <= 0) { - nm_device_master_release_slave(priv->master, + nm_device_master_release_slave(priv->controller, self, RELEASE_SLAVE_TYPE_NO_CONFIG, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); @@ -16666,6 +16876,10 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, _cleanup_ip_pre(self, AF_INET6, CLEANUP_TYPE_DECONFIGURE, FALSE); } break; + case NM_DEVICE_STATE_DEACTIVATING: + /* If we are now deactivating we should enforce IP cleanup. */ + _cleanup_ip_pre(self, AF_INET, CLEANUP_TYPE_DECONFIGURE, FALSE); + _cleanup_ip_pre(self, AF_INET6, CLEANUP_TYPE_DECONFIGURE, FALSE); default: break; } @@ -17431,6 +17645,8 @@ nm_device_hw_addr_set(NMDevice *self, const char *addr, const char *detail, gboo * @hwaddr: (out): the cloned MAC address to set on interface * @hwaddr_type: (out): the type of address to set * @hwaddr_detail: (out): the detail (origin) of address to set + * @is_default_special (out): if %TRUE, the cloned MAC comes from + * global default configuration and is a special keyword * @error: on return, an error or %NULL * * Computes the MAC to be set on a interface. On success, one of the @@ -17451,6 +17667,7 @@ _hw_addr_get_cloned(NMDevice *self, char **hwaddr, HwAddrType *hwaddr_type, const char **hwaddr_detail, + gboolean *is_default_special, GError **error) { NMDevicePrivate *priv; @@ -17469,7 +17686,7 @@ _hw_addr_get_cloned(NMDevice *self, if (!connection) g_return_val_if_reached(FALSE); - addr_setting = _prop_get_x_cloned_mac_address(self, connection, is_wifi); + addr_setting = _prop_get_x_cloned_mac_address(self, connection, is_wifi, is_default_special); addr = addr_setting; @@ -17605,7 +17822,7 @@ nm_device_hw_addr_get_cloned(NMDevice *self, gboolean *preserve, GError **error) { - if (!_hw_addr_get_cloned(self, connection, is_wifi, preserve, hwaddr, NULL, NULL, error)) + if (!_hw_addr_get_cloned(self, connection, is_wifi, preserve, hwaddr, NULL, NULL, NULL, error)) return FALSE; return TRUE; @@ -17615,11 +17832,13 @@ gboolean nm_device_hw_addr_set_cloned(NMDevice *self, NMConnection *connection, gboolean is_wifi) { NMDevicePrivate *priv; - gboolean preserve = FALSE; - gs_free char *hwaddr = NULL; - const char *detail = NULL; - HwAddrType type = HW_ADDR_TYPE_UNSET; - gs_free_error GError *error = NULL; + gboolean preserve = FALSE; + gs_free char *hwaddr = NULL; + const char *detail = NULL; + HwAddrType type = HW_ADDR_TYPE_UNSET; + gs_free_error GError *error = NULL; + gboolean is_default_special = FALSE; + gboolean ret; g_return_val_if_fail(NM_IS_DEVICE(self), FALSE); priv = NM_DEVICE_GET_PRIVATE(self); @@ -17631,6 +17850,7 @@ nm_device_hw_addr_set_cloned(NMDevice *self, NMConnection *connection, gboolean &hwaddr, &type, &detail, + &is_default_special, &error)) { _LOGW(LOGD_DEVICE, "set-hw-addr: %s", error->message); return FALSE; @@ -17640,8 +17860,22 @@ nm_device_hw_addr_set_cloned(NMDevice *self, NMConnection *connection, gboolean return nm_device_hw_addr_reset(self, detail); if (hwaddr) { + ret = _hw_addr_set(self, hwaddr, "set-cloned", detail); + if (!ret && is_default_special) { + /* If the distro sets a global special value for the cloned MAC (for + * example, "stable-ssid") and the driver doesn't support changing the + * MAC, all activations will fail on the interface unless users know + * that they need to change the cloned MAC. Be more tolerant to errors + * in case the MAC is global and special. + */ + _LOGE(LOGD_DEVICE, + "ignore error changing the MAC address to globally configured value \"%s\"," + "the device does not support it", + detail); + return TRUE; + } priv->hw_addr_type = type; - return _hw_addr_set(self, hwaddr, "set-cloned", detail); + return ret; } return TRUE; @@ -18283,8 +18517,8 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) case PROP_PHYSICAL_PORT_ID: g_value_set_string(value, priv->physical_port_id); break; - case PROP_MASTER: - g_value_set_object(value, nm_device_get_master(self)); + case PROP_CONTROLLER: + g_value_set_object(value, nm_device_get_controller(self)); break; case PROP_PARENT: g_value_set_string(value, nm_dbus_track_obj_path_get(&priv->parent_device)); @@ -18602,7 +18836,7 @@ dispose(GObject *object) _cleanup_generic_post(self, NM_DEVICE_STATE_REASON_NONE, CLEANUP_TYPE_KEEP); - nm_assert(priv->master_ready_id == 0); + nm_assert(priv->controller_ready_id == 0); g_hash_table_remove_all(priv->ip6_saved_properties); @@ -19052,11 +19286,12 @@ nm_device_class_init(NMDeviceClass *klass) "", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_MASTER] = g_param_spec_object(NM_DEVICE_MASTER, - "", - "", - NM_TYPE_DEVICE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_CONTROLLER] = + g_param_spec_object(NM_DEVICE_CONTROLLER, + "", + "", + NM_TYPE_DEVICE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_PARENT] = g_param_spec_string(NM_DEVICE_PARENT, "", "", diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h index b096d23a..ffe6b1af 100644 --- a/src/core/devices/nm-device.h +++ b/src/core/devices/nm-device.h @@ -66,7 +66,7 @@ #define NM_DEVICE_TYPE_DESC "type-desc" /* Internal only */ #define NM_DEVICE_IFINDEX "ifindex" /* Internal only */ -#define NM_DEVICE_MASTER "master" /* Internal only */ +#define NM_DEVICE_CONTROLLER "controller" /* Internal only */ #define NM_DEVICE_HAS_PENDING_ACTION "has-pending-action" /* Internal only */ /* Internal signals */ @@ -186,9 +186,9 @@ typedef struct _NMDeviceClass { * a delta in the MTU allowed value due the encapsulation overhead */ guint16 mtu_parent_delta; - /* Whether the device type is a master-type. This depends purely on the + /* Whether the device type is a controller-type. This depends purely on the * type (NMDeviceClass), not the actual device instance. */ - bool is_master : 1; + bool is_controller : 1; /* Force setting the MTU actually means first setting the MTU * to (desired_MTU-1) and then setting the desired_MTU @@ -502,11 +502,11 @@ gboolean nm_device_parent_notify_changed(NMDevice *self, const char *nm_device_parent_find_for_connection(NMDevice *self, const char *current_setting_parent); -/* Master */ -gboolean nm_device_is_master(NMDevice *dev); +/* Controller */ +gboolean nm_device_is_controller(NMDevice *dev); -/* Slave */ -NMDevice *nm_device_get_master(NMDevice *dev); +/* Port */ +NMDevice *nm_device_get_controller(NMDevice *dev); NMActRequest *nm_device_get_act_request(NMDevice *dev); NMSettingsConnection *nm_device_get_settings_connection(NMDevice *dev); @@ -550,6 +550,7 @@ gboolean nm_device_check_connection_compatible(NMDevice *device, GError **error); gboolean nm_device_check_slave_connection_compatible(NMDevice *device, NMConnection *connection); +gboolean nm_device_can_be_parent(NMDevice *device); gboolean nm_device_can_assume_connections(NMDevice *self); gboolean nm_device_unmanage_on_quit(NMDevice *self); diff --git a/src/core/devices/ovs/nm-device-ovs-bridge.c b/src/core/devices/ovs/nm-device-ovs-bridge.c index 3bc03ac1..27d04003 100644 --- a/src/core/devices/ovs/nm-device-ovs-bridge.c +++ b/src/core/devices/ovs/nm-device-ovs-bridge.c @@ -171,7 +171,7 @@ nm_device_ovs_bridge_class_init(NMDeviceOvsBridgeClass *klass) device_class->connection_type_check_compatible = NM_SETTING_OVS_BRIDGE_SETTING_NAME; device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(); - device_class->is_master = TRUE; + device_class->is_controller = TRUE; device_class->get_type_description = get_type_description; device_class->create_and_realize = create_and_realize; device_class->unrealize = unrealize; diff --git a/src/core/devices/ovs/nm-device-ovs-interface.c b/src/core/devices/ovs/nm-device-ovs-interface.c index 17eb2c2d..778f230b 100644 --- a/src/core/devices/ovs/nm-device-ovs-interface.c +++ b/src/core/devices/ovs/nm-device-ovs-interface.c @@ -355,12 +355,12 @@ ovs_interface_is_netdev_datapath(NMDeviceOvsInterface *self) return FALSE; /* get ovs-port active-connection */ - ac = nm_active_connection_get_master(ac); + ac = nm_active_connection_get_controller(ac); if (!ac) return FALSE; /* get ovs-bridge active-connection */ - ac = nm_active_connection_get_master(ac); + ac = nm_active_connection_get_controller(ac); if (!ac) return FALSE; diff --git a/src/core/devices/ovs/nm-device-ovs-port.c b/src/core/devices/ovs/nm-device-ovs-port.c index 258c72f2..e1020280 100644 --- a/src/core/devices/ovs/nm-device-ovs-port.c +++ b/src/core/devices/ovs/nm-device-ovs-port.c @@ -160,7 +160,7 @@ attach_port(NMDevice *device, return TRUE; ac_port = NM_ACTIVE_CONNECTION(nm_device_get_act_request(device)); - ac_bridge = nm_active_connection_get_master(ac_port); + ac_bridge = nm_active_connection_get_controller(ac_port); if (!ac_bridge) { _LOGW(LOGD_DEVICE, "can't attach %s: bridge active-connection not found", @@ -285,7 +285,7 @@ nm_device_ovs_port_class_init(NMDeviceOvsPortClass *klass) device_class->connection_type_check_compatible = NM_SETTING_OVS_PORT_SETTING_NAME; device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(); - device_class->is_master = TRUE; + device_class->is_controller = TRUE; device_class->get_type_description = get_type_description; device_class->create_and_realize = create_and_realize; device_class->get_generic_capabilities = get_generic_capabilities; diff --git a/src/core/devices/team/nm-device-team.c b/src/core/devices/team/nm-device-team.c index d8c3c2c2..1f70537f 100644 --- a/src/core/devices/team/nm-device-team.c +++ b/src/core/devices/team/nm-device-team.c @@ -1006,7 +1006,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) static void nm_device_team_init(NMDeviceTeam *self) { - nm_assert(nm_device_is_master(NM_DEVICE(self))); + nm_assert(nm_device_is_controller(NM_DEVICE(self))); } static void @@ -1127,7 +1127,7 @@ nm_device_team_class_init(NMDeviceTeamClass *klass) device_class->connection_type_check_compatible = NM_SETTING_TEAM_SETTING_NAME; device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_TEAM); - device_class->is_master = TRUE; + device_class->is_controller = TRUE; device_class->create_and_realize = create_and_realize; device_class->get_generic_capabilities = get_generic_capabilities; device_class->complete_connection = complete_connection; diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c index 47407a1e..89444640 100644 --- a/src/core/devices/wifi/nm-device-iwd.c +++ b/src/core/devices/wifi/nm-device-iwd.c @@ -774,8 +774,8 @@ check_connection_compatible(NMDevice *device, return FALSE; } - /* Check for MAC address blacklist */ - mac_blacklist = nm_setting_wireless_get_mac_address_blacklist(s_wireless); + /* Check for MAC address denylist */ + mac_blacklist = nm_setting_wireless_get_mac_address_denylist(s_wireless); for (i = 0; mac_blacklist[i]; i++) { nm_assert(nm_utils_hwaddr_valid(mac_blacklist[i], ETH_ALEN)); diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c index 2cd41b27..206113e4 100644 --- a/src/core/devices/wifi/nm-device-wifi.c +++ b/src/core/devices/wifi/nm-device-wifi.c @@ -1013,8 +1013,8 @@ check_connection_compatible(NMDevice *device, return FALSE; } - /* Check for MAC address blacklist */ - mac_blacklist = nm_setting_wireless_get_mac_address_blacklist(s_wireless); + /* Check for MAC address denylist */ + mac_blacklist = nm_setting_wireless_get_mac_address_denylist(s_wireless); for (i = 0; mac_blacklist[i]; i++) { if (!nm_utils_hwaddr_valid(mac_blacklist[i], ETH_ALEN)) { g_warn_if_reached(); @@ -3374,7 +3374,7 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) /* Tell the supplicant in which bridge the interface is */ if ((request = nm_device_get_act_request(device)) - && (master_ac = nm_active_connection_get_master(NM_ACTIVE_CONNECTION(request))) + && (master_ac = nm_active_connection_get_controller(NM_ACTIVE_CONNECTION(request))) && (master = nm_active_connection_get_device(master_ac)) && nm_device_get_device_type(master) == NM_DEVICE_TYPE_BRIDGE) { nm_supplicant_interface_set_bridge(priv->sup_iface, nm_device_get_iface(master)); diff --git a/src/core/devices/wifi/nm-wifi-ap.c b/src/core/devices/wifi/nm-wifi-ap.c index d4d3815e..ceb954b7 100644 --- a/src/core/devices/wifi/nm-wifi-ap.c +++ b/src/core/devices/wifi/nm-wifi-ap.c @@ -719,13 +719,14 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) g_value_set_uchar(value, priv->strength); break; case PROP_LAST_SEEN: - g_value_set_int(value, - priv->last_seen_msec != G_MININT64 ? (int) NM_MAX( - nm_utils_monotonic_timestamp_as_boottime(priv->last_seen_msec, - NM_UTILS_NSEC_PER_MSEC) - / 1000, - 1) - : -1); + g_value_set_int( + value, + priv->last_seen_msec != G_MININT64 + ? (int) NM_MAX(nm_utils_monotonic_timestamp_as_boottime(priv->last_seen_msec, + NM_UTILS_NSEC_PER_MSEC) + / 1000, + 1) + : -1); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); diff --git a/src/core/devices/wwan/meson.build b/src/core/devices/wwan/meson.build index 37ef738c..acccb5fd 100644 --- a/src/core/devices/wwan/meson.build +++ b/src/core/devices/wwan/meson.build @@ -4,7 +4,7 @@ wwan_inc = include_directories('.') linker_script = join_paths(meson.current_source_dir(), 'libnm-wwan.ver') -libnm_wwan = shared_module( +libnm_wwan = shared_library( 'nm-wwan', sources: files( 'nm-service-providers.c', @@ -21,6 +21,7 @@ libnm_wwan = shared_module( link_depends: linker_script, install: true, install_dir: nm_plugindir, + override_options: ['b_lundef=false'], ) libnm_wwan_dep = declare_dependency( diff --git a/src/core/devices/wwan/nm-modem-broadband.c b/src/core/devices/wwan/nm-modem-broadband.c index a150040f..c03446b9 100644 --- a/src/core/devices/wwan/nm-modem-broadband.c +++ b/src/core/devices/wwan/nm-modem-broadband.c @@ -1155,6 +1155,8 @@ stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_metho #endif } else { NMPlatformIP6Address address; + NMPlatformIP6Address gw; + const char *gw_string; address_string = mm_bearer_ip_config_get_address(self->_priv.ipv6_config); if (!address_string) { @@ -1165,20 +1167,8 @@ stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_metho NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IPv6 configuration failed: no address given", nm_modem_get_uid(NM_MODEM(self))); + goto out; } - goto out; - } - - address = (NMPlatformIP6Address){}; - - if (!inet_pton(AF_INET6, address_string, &address.address)) { - g_set_error(&error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_INVALID_CONNECTION, - "(%s) retrieving IPv6 configuration failed: invalid address given '%s'", - nm_modem_get_uid(NM_MODEM(self)), - address_string); - goto out; } data_port = mm_bearer_get_interface(self->_priv.bearer); @@ -1202,43 +1192,57 @@ stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_metho NM_IP_CONFIG_SOURCE_WWAN); do_auto = TRUE; - address.plen = mm_bearer_ip_config_get_prefix(self->_priv.ipv6_config); - if (address.plen <= 128) { - if (IN6_IS_ADDR_LINKLOCAL(&address.address)) { - nm_utils_ipv6_interface_identifier_get_from_addr(&iid_data, &address.address); - iid = &iid_data; - } else - do_auto = FALSE; - nm_l3_config_data_add_address_6(l3cd, &address); + if (address_string) { + address = (NMPlatformIP6Address){}; + + if (!inet_pton(AF_INET6, address_string, &address.address)) { + g_set_error(&error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "(%s) retrieving IPv6 configuration failed: invalid address given '%s'", + nm_modem_get_uid(NM_MODEM(self)), + address_string); + goto out; + } + + address.plen = mm_bearer_ip_config_get_prefix(self->_priv.ipv6_config); + if (address.plen <= 128) { + if (IN6_IS_ADDR_LINKLOCAL(&address.address)) { + nm_utils_ipv6_interface_identifier_get_from_addr(&iid_data, &address.address); + iid = &iid_data; + } else + do_auto = FALSE; + nm_l3_config_data_add_address_6(l3cd, &address); + } + + _LOGI(" address %s", nm_platform_ip6_address_to_string(&address, sbuf, sizeof(sbuf))); } - _LOGI(" address %s (slaac %s)", - nm_platform_ip6_address_to_string(&address, sbuf, sizeof(sbuf)), - do_auto ? "enabled" : "disabled"); + _LOGI(" slaac %s", do_auto ? "enabled" : "disabled"); - address_string = mm_bearer_ip_config_get_gateway(self->_priv.ipv6_config); - if (address_string) { - if (inet_pton(AF_INET6, address_string, &address.address) != 1) { + gw_string = mm_bearer_ip_config_get_gateway(self->_priv.ipv6_config); + if (gw_string) { + if (inet_pton(AF_INET6, gw_string, &gw.address) != 1) { g_set_error(&error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IPv6 configuration failed: invalid gateway given '%s'", nm_modem_get_uid(NM_MODEM(self)), - address_string); + gw_string); goto out; } { const NMPlatformIP6Route r = { .rt_source = NM_IP_CONFIG_SOURCE_WWAN, - .gateway = address.address, + .gateway = gw.address, .table_any = TRUE, .table_coerced = 0, .metric_any = TRUE, .metric = 0, }; - _LOGI(" gateway %s", address_string); + _LOGI(" gateway %s", gw_string); nm_l3_config_data_add_route_6(l3cd, &r); } } else if (ip_method == NM_MODEM_IP_METHOD_STATIC) { diff --git a/src/core/dhcp/README.next.md b/src/core/dhcp/README.next.md new file mode 100644 index 00000000..88fa6683 --- /dev/null +++ b/src/core/dhcp/README.next.md @@ -0,0 +1,103 @@ +`NMDhcpClient` +============== + +Using `NMDhcpClient` still requires a lot of logic in `NMDevice`. The main goal +is to simplify `NMDevice`, so `NMDhcpClient` must become more complicated to +provide a simpler (but robust) API. + +NMDevice has basically two timeouts (talking about IPv4, but it applies +similarly to IPv6): `ipv4.dhcp-timeout` and `ipv4.required-timeout`. They +control how long NMDevice is willing to try, before failing the activation +altogether. Note that with `ipv4.may-fail=yes`, we may very well never want to +fail the activation entirely, regardless how DHCP is doing. In that case we +want to stay up, but also constantly retrying whether we cannot get a lease and +recover. + +Currently, if `NMDhcpClient` signals a failure, then it's basically up to +`NMDevice` to schedule and retry. That is complicated, and we should move the +complexity out of `NMDevice`. + +`NMDhcpClient` should have a simpler API: + +- `nm_dhcp_manager_start_ip[46]()`: creates (and starts) a `NMDhcpClient` + instance. The difference is, this function tries really hard not to fail + to create an `NMDhcpClient`. There is no explicit `start()`, but note that the + instance must not emit any signals before the next maincontext iteration. That is, + it only will call back the user after a timeout/idle or some other IO event, which + happens during a future iteration of the maincontext. + +- `nm_dhcp_client_stop()`: when `NMDevice` is done with the `NMDhcpClient` + instance, it will stop it and throw it away. This method exists because + `NMDhcpClient` is a `GObject` and ref-counted. Thus, we don't want to rely on + the last unref to stop the instance, but have an explicit stop. After stop, the + instance is defunct and won't emit any signals anymore. The class does not need + to support restarting a stopped instance. If `NMDevice` wants to restart DHCP, it + should create a new one. `NMDevice` would only want to do that, if the parameters + change, hence a new instance is in order (and no need for the complexity of + restart in `NMDhcpClient`). + +- as already now, `NMDhcpClient` is not very configurable. You provide most + (all) parameters during `nm_dhcp_manager_start_ip[46]()`, and then it keeps + running until stop. + +- `NMDhcpClient` exposes a simple state to the user: + + 1. "no lease, but good". When starting, there is no lease, but we are + optimistic to get one. This is the inital state, but we can also get back to + this state after we had a lease (which might expire). + + 1. "has a lease". Here there is no need to distinguish whether the current + lease was the first we received, or whether this was an update. In this state, + the instance has a lease and we are good. + + 1. "no lease, but bad". `NMDhcpClient` tries really hard, and "bad" does not + mean that it gave up. It will keep retrying, it's just that there is little + hope of getting a new lease. This happens, when you try to run DHCP on a Layer3 + link (WireGuard). There is little hope to succeed, but `NMDhcpClient` + (theoretically) will retry and may recover from this. Another example is when + we fail to start dhclient because it's not installed. In that case, we are not + optimistic to recover, however `NMDhcpDhclient` will retry (with backoff + timeout) and might still recover from this. For most cases, `NMDevice` will + treat the no-lease cases the same, but in case of "bad" it might give up + earlier. + +When a lease expires, that does not necessarily mean that we are now in a bad +state. It might mean that the DHCP server is temporarily down, but we might +recover from that easily. "bad" really means, something is wrong on our side +which prevents us from getting a lease. Also, imagine `dhclient` dies (we would +try to restart, but assume that fails too), but we still have a valid lease, +then possibly `NMDhcpClient` should still pretend all is good and we still have +a lease until it expires. It may be we can recover before that happens. The +point of all of this, is to hide errors as much as possibly and automatically +recover. `NMDevice` will decide to tear down, if we didn't get a lease after +`ipv4.dhcp-timeout`. That's the main criteria, and it might not even +distinguish between "no lease, but good" and "no lease, but bad". + +- `NMDhcpClient` will also take care of the `ipv4.dhcp-timeout` grace period. + That timeout is provided during start, and starts ticking whenever there is + no lease. When it expires, a timeout signal gets emitted. That's it. This is + independent from the 3 states above, and only saves `NMDevice` from scheduling + this timer themselves. + This is NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT notification. + +- for nettools, `nm_dhcp_client_can_accept()` indicates that when we receive a + lease, we need to accept/decline it first. In that case, `NMDevice` +optionally does ACD first, then configures the IP address first and calls +`nm_dhcp_client_accept()`. In case of ACD conflict, it will call +`nm_dhcp_client_decline()` (which optimally causes `NMDhcpClient` to get a +different lease). With this, the above state "has a lease" has actually three +flavors: "has a lease but not yet ACD probed" and "has a lease but +accepted/declined" (but `NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED` gets only emitted +when we get the lease, not when we accept/decline it). With `dhclient`, when we +receive a lease, it means "has a lease but accepted" right away. + +- for IPv6 prefix delegation, there is also `needed_prefixes` and + `NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED`. Currently `needed_prefixes` needs + to be specified during start (which simplifies things). Maybe `needed_prefixes` + should be changable at runtime. Otherwise, whether we have prefixes is similar + to whether we have a lease, and the simple 3 states apply. + +When NetworkManager quits, it may want to leave the interface up. In that case, +we still always want to stop the DHCP client, but possibly not deconfiguring +the interface. I don't think that this concerns `NMDhcpClient`, because `NMDhcpClient` +only provides the lease information and `NMDevice` is responsible to configure it. diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c index 8770656b..4ebc1754 100644 --- a/src/core/dhcp/nm-dhcp-client.c +++ b/src/core/dhcp/nm-dhcp-client.c @@ -824,9 +824,10 @@ _nm_dhcp_client_notify(NMDhcpClient *self, _acd_check_lease(self, &acd_state); - options = priv->l3cd_next ? nm_dhcp_lease_get_options( - nm_l3_config_data_get_dhcp_lease(priv->l3cd_next, priv->config.addr_family)) - : NULL; + options = priv->l3cd_next + ? nm_dhcp_lease_get_options( + nm_l3_config_data_get_dhcp_lease(priv->l3cd_next, priv->config.addr_family)) + : NULL; if (_LOGI_ENABLED()) { const char *req_str = diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c index ce1e9a45..b81ce77a 100644 --- a/src/core/dhcp/nm-dhcp-nettools.c +++ b/src/core/dhcp/nm-dhcp-nettools.c @@ -1483,6 +1483,11 @@ stop(NMDhcpClient *client, gboolean release) NMDhcpNettools *self = NM_DHCP_NETTOOLS(client); NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self); + if (release) { + if (n_dhcp4_client_probe_release(priv->probe)) + _LOGT("dhcp-client4: failed to send request with RELEASE message"); + } + NM_DHCP_CLIENT_CLASS(nm_dhcp_nettools_parent_class)->stop(client, release); _LOGT("dhcp-client4: stop " NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(priv->client)); diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c index 0fc5f928..5ede0df9 100644 --- a/src/core/dhcp/nm-dhcp-systemd.c +++ b/src/core/dhcp/nm-dhcp-systemd.c @@ -67,6 +67,15 @@ G_DEFINE_TYPE(NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT) /*****************************************************************************/ +static guint32 +lifetime_to_uint32(guint64 lft) +{ + if (lft == G_MAXUINT64) + return G_MAXUINT32; + + return lft / 1000000; +} + static NML3ConfigData * lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GError **error) { @@ -100,18 +109,19 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro if (!config->v6.info_only) { gboolean has_any_addresses = FALSE; - uint32_t lft_pref; - uint32_t lft_valid; + uint64_t lft_pref; + uint64_t lft_valid; - sd_dhcp6_lease_reset_address_iter(lease); + sd_dhcp6_lease_address_iterator_reset(lease); nm_gstring_prepare(&str); - while (sd_dhcp6_lease_get_address(lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) { - const NMPlatformIP6Address address = { + while (sd_dhcp6_lease_get_address(lease, &tmp_addr) >= 0 + && sd_dhcp6_lease_get_address_lifetime(lease, &lft_pref, &lft_valid) >= 0) { + NMPlatformIP6Address address = { .plen = 128, .address = tmp_addr, .timestamp = ts, - .lifetime = lft_valid, - .preferred = lft_pref, + .lifetime = lifetime_to_uint32(lft_valid), + .preferred = lifetime_to_uint32(lft_pref), .addr_source = NM_IP_CONFIG_SOURCE_DHCP, }; @@ -121,6 +131,7 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro g_string_append(nm_gstring_add_space_delimiter(str), addr_str); has_any_addresses = TRUE; + sd_dhcp6_lease_address_iterator_next(lease); } if (str->len) { @@ -160,11 +171,12 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro uint8_t prefix_len; nm_gstring_prepare(&str); - sd_dhcp6_lease_reset_pd_prefix_iter(lease); - while (!sd_dhcp6_lease_get_pd(lease, &prefix, &prefix_len, NULL, NULL)) { + sd_dhcp6_lease_pd_iterator_reset(lease); + while (!sd_dhcp6_lease_get_pd_prefix(lease, &prefix, &prefix_len)) { nm_gstring_add_space_delimiter(str); nm_inet6_ntop(&prefix, addr_str); g_string_append_printf(str, "%s/%u", addr_str, prefix_len); + sd_dhcp6_lease_pd_iterator_next(lease); } if (str->len > 0) { nm_dhcp_option_add_option(options, @@ -235,6 +247,8 @@ bound6_handle(NMDhcpSystemd *self) gs_free_error GError *error = NULL; NMPlatformIP6Address prefix = {0}; sd_dhcp6_lease *lease = NULL; + guint64 lft_valid; + guint64 lft_pref; if (sd_dhcp6_client_get_lease(priv->client6, &lease) < 0 || !lease) { _LOGW(" no lease!"); @@ -254,14 +268,14 @@ bound6_handle(NMDhcpSystemd *self) _nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_BOUND, l3cd); - sd_dhcp6_lease_reset_pd_prefix_iter(lease); - while (!sd_dhcp6_lease_get_pd(lease, - &prefix.address, - &prefix.plen, - &prefix.preferred, - &prefix.lifetime)) { + sd_dhcp6_lease_pd_iterator_reset(lease); + while (!sd_dhcp6_lease_get_pd_prefix(lease, &prefix.address, &prefix.plen) + && !sd_dhcp6_lease_get_pd_lifetime(lease, &lft_pref, &lft_valid)) { + prefix.preferred = lifetime_to_uint32(lft_pref); + prefix.lifetime = lifetime_to_uint32(lft_valid); prefix.timestamp = ts; nm_dhcp_client_emit_ipv6_prefix_delegated(NM_DHCP_CLIENT(self), &prefix); + sd_dhcp6_lease_pd_iterator_next(lease); } } @@ -339,10 +353,10 @@ ip6_start(NMDhcpClient *client, const struct in6_addr *ll_addr, GError **error) return FALSE; } - r = sd_dhcp6_client_set_duid(sd_client, - unaligned_read_be16(&duid_arr[0]), - &duid_arr[2], - duid_len - 2); + r = sd_dhcp6_client_set_duid_raw(sd_client, + unaligned_read_be16(&duid_arr[0]), + &duid_arr[2], + duid_len - 2); if (r < 0) { nm_utils_error_set_errno(error, r, "failed to set DUID: %s"); return FALSE; @@ -450,6 +464,10 @@ stop(NMDhcpClient *client, gboolean release) if (!priv->client6) return; + r = sd_dhcp6_client_set_send_release(priv->client6, release); + if (r) + _LOGT("dhcp-client6: failed setting send-release"); + sd_dhcp6_client_set_callback(priv->client6, NULL, NULL); r = sd_dhcp6_client_stop(priv->client6); if (r) diff --git a/src/core/meson.build b/src/core/meson.build index 45b068a6..4419ff62 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -171,7 +171,7 @@ libNetworkManager = static_library( 'nm-policy.c', 'nm-rfkill-manager.c', 'nm-session-monitor.c', - 'nm-sleep-monitor.c', + 'nm-power-monitor.c', 'nm-priv-helper-call.c', ), dependencies: nm_deps, diff --git a/src/core/ndisc/nm-ndisc.c b/src/core/ndisc/nm-ndisc.c index e6b1a94e..ca646a6d 100644 --- a/src/core/ndisc/nm-ndisc.c +++ b/src/core/ndisc/nm-ndisc.c @@ -1853,6 +1853,7 @@ _config_init(NMNDiscConfig *config, const NMNDiscConfig *src) g_return_if_fail( NM_IN_SET(config->node_type, NM_NDISC_NODE_TYPE_HOST, NM_NDISC_NODE_TYPE_ROUTER)); g_return_if_fail(NM_IN_SET(config->ip6_privacy, + NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR)); diff --git a/src/core/ndisc/nm-ndisc.h b/src/core/ndisc/nm-ndisc.h index 8f1a12a2..b8f8b06e 100644 --- a/src/core/ndisc/nm-ndisc.h +++ b/src/core/ndisc/nm-ndisc.h @@ -40,7 +40,7 @@ typedef enum { const char *nm_ndisc_dhcp_level_to_string(NMNDiscDHCPLevel level); -#define NM_NDISC_INFINITY_U32 ((uint32_t) -1) +#define NM_NDISC_INFINITY_U32 ((uint32_t) - 1) /* It's important that this is G_MAXINT64, so that we can meaningfully do * MIN(e1, e2) to find the minimum expiry time (and properly handle if any diff --git a/src/core/nm-active-connection.c b/src/core/nm-active-connection.c index eb7b1cca..b08d26c2 100644 --- a/src/core/nm-active-connection.c +++ b/src/core/nm-active-connection.c @@ -38,7 +38,7 @@ typedef struct _NMActiveConnectionPrivate { bool is_default6 : 1; bool state_set : 1; bool vpn : 1; - bool master_ready : 1; + bool controller_ready : 1; NMActivationType activation_type : 3; @@ -49,7 +49,7 @@ typedef struct _NMActiveConnectionPrivate { NMActivationReason activation_reason : 4; NMAuthSubject *subject; - NMActiveConnection *master; + NMActiveConnection *controller; NMActiveConnection *parent; @@ -87,8 +87,8 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMActiveConnection, PROP_INT_APPLIED_CONNECTION, PROP_INT_DEVICE, PROP_INT_SUBJECT, - PROP_INT_MASTER, - PROP_INT_MASTER_READY, + PROP_INT_CONTROLLER, + PROP_INT_CONTROLLER_READY, PROP_INT_ACTIVATION_TYPE, PROP_INT_ACTIVATION_REASON, ); @@ -105,7 +105,7 @@ G_DEFINE_ABSTRACT_TYPE(NMActiveConnection, nm_active_connection, NM_TYPE_DBUS_OB static const NMDBusInterfaceInfoExtended interface_info_active_connection; static const GDBusSignalInfo signal_info_state_changed; -static void check_master_ready(NMActiveConnection *self); +static void check_controller_ready(NMActiveConnection *self); static void _device_cleanup(NMActiveConnection *self); static void _settings_connection_flags_changed(NMSettingsConnection *settings_connection, NMActiveConnection *self); @@ -276,7 +276,7 @@ nm_active_connection_set_state(NMActiveConnection *self, emit_state_changed(self, new_state, reason); _notify(self, PROP_STATE); - check_master_ready(self); + check_controller_ready(self); if (new_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED || old_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { @@ -446,7 +446,7 @@ _set_applied_connection_take(NMActiveConnection *self, NMConnection *applied_con /* we determine whether the connection is a master/slave, based solely * on the connection properties itself. */ s_con = nm_connection_get_setting_connection(priv->applied_connection); - if (nm_setting_connection_get_master(s_con)) + if (nm_setting_connection_get_controller(s_con)) flags_val |= NM_ACTIVATION_STATE_FLAG_IS_SLAVE; if (_nm_connection_type_is_master(nm_setting_connection_get_connection_type(s_con))) @@ -650,13 +650,13 @@ device_master_changed(GObject *object, GParamSpec *pspec, gpointer user_data) if (NM_ACTIVE_CONNECTION(nm_device_get_act_request(device)) != self) return; - if (!nm_device_get_master(device)) + if (!nm_device_get_controller(device)) return; - if (!nm_active_connection_get_master(self)) + if (!nm_active_connection_get_controller(self)) return; g_signal_handlers_disconnect_by_func(device, G_CALLBACK(device_master_changed), self); - master = nm_active_connection_get_master(self); + master = nm_active_connection_get_controller(self); master_state = nm_active_connection_get_state(master); if (master_state >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) { /* Master failed before attaching the slave */ @@ -704,15 +704,15 @@ nm_active_connection_set_device(NMActiveConnection *self, NMDevice *device) if (device) { /* Device obviously can't be its own master */ - g_return_val_if_fail(!priv->master - || device != nm_active_connection_get_device(priv->master), + g_return_val_if_fail(!priv->controller + || device != nm_active_connection_get_device(priv->controller), FALSE); priv->device = g_object_ref(device); g_signal_connect(device, NM_DEVICE_STATE_CHANGED, G_CALLBACK(device_state_changed), self); g_signal_connect(device, - "notify::" NM_DEVICE_MASTER, + "notify::" NM_DEVICE_CONTROLLER, G_CALLBACK(device_master_changed), self); g_signal_connect(device, @@ -747,30 +747,30 @@ nm_active_connection_set_device(NMActiveConnection *self, NMDevice *device) } NMActiveConnection * -nm_active_connection_get_master(NMActiveConnection *self) +nm_active_connection_get_controller(NMActiveConnection *self) { g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(self), NULL); - return NM_ACTIVE_CONNECTION_GET_PRIVATE(self)->master; + return NM_ACTIVE_CONNECTION_GET_PRIVATE(self)->controller; } /** - * nm_active_connection_get_master_ready: + * nm_active_connection_get_controller_ready: * @self: the #NMActiveConnection * - * Returns: %TRUE if the connection has a master connection, and that - * master connection is ready to accept slaves. Otherwise, %FALSE. + * Returns: %TRUE if the connection has a controller connection, and that + * controller connection is ready to accept ports. Otherwise, %FALSE. */ gboolean -nm_active_connection_get_master_ready(NMActiveConnection *self) +nm_active_connection_get_controller_ready(NMActiveConnection *self) { g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(self), FALSE); - return NM_ACTIVE_CONNECTION_GET_PRIVATE(self)->master_ready; + return NM_ACTIVE_CONNECTION_GET_PRIVATE(self)->controller_ready; } static void -check_master_ready(NMActiveConnection *self) +check_controller_ready(NMActiveConnection *self) { NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(self); gboolean signalling = FALSE; @@ -780,30 +780,31 @@ check_master_ready(NMActiveConnection *self) * device will be ready to accept slaves when the master is in ACTIVATING * or higher states. */ - if (!priv->master_ready && priv->master && priv->state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING - && NM_IN_SET(nm_active_connection_get_state(priv->master), + if (!priv->controller_ready && priv->controller + && priv->state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING + && NM_IN_SET(nm_active_connection_get_state(priv->controller), NM_ACTIVE_CONNECTION_STATE_ACTIVATING, NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) { signalling = TRUE; } - _LOGD("check-master-ready: %s (state %s, %s)", - signalling ? "signal" : (priv->master_ready ? "already signalled" : "not signalling"), + _LOGD("check-controller-ready: %s (state %s, %s)", + signalling ? "signal" : (priv->controller_ready ? "already signalled" : "not signalling"), state_to_string_a(priv->state), - priv->master + priv->controller ? nm_sprintf_bufa(128, - "master %p is in state %s", - priv->master, - state_to_string_a(nm_active_connection_get_state(priv->master))) - : "no master"); + "controller %p is in state %s", + priv->controller, + state_to_string_a(nm_active_connection_get_state(priv->controller))) + : "no controller"); if (signalling) { - priv->master_ready = TRUE; - _notify(self, PROP_INT_MASTER_READY); + priv->controller_ready = TRUE; + _notify(self, PROP_INT_CONTROLLER_READY); - /* Also notify clients to recheck the exported 'master' property to - * ensure that if the master connection was created without a device - * that we notify clients when the master device is known. + /* Also notify clients to recheck the exported 'controller' property to + * ensure that if the controller connection was created without a device + * that we notify clients when the controller device is known. */ nm_gobject_notify_together(self, PROP_MASTER, PROP_CONTROLLER); } @@ -816,9 +817,9 @@ master_state_cb(NMActiveConnection *master, GParamSpec *pspec, gpointer user_dat NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(self); NMActiveConnectionState master_state = nm_active_connection_get_state(master); - check_master_ready(self); + check_controller_ready(self); - if (master_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATING && !priv->master_ready) { + if (master_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATING && !priv->controller_ready) { /* Master disconnected before the slave was added */ if (NM_ACTIVE_CONNECTION_GET_CLASS(self)->master_failed) NM_ACTIVE_CONNECTION_GET_CLASS(self)->master_failed(self); @@ -826,44 +827,44 @@ master_state_cb(NMActiveConnection *master, GParamSpec *pspec, gpointer user_dat } /** - * nm_active_connection_set_master: + * nm_active_connection_set_controller: * @self: the #NMActiveConnection - * @master: if the activation depends on another device (ie, bond or bridge - * master to which this device will be enslaved) pass the #NMActiveConnection + * @controller: if the activation depends on another device (ie, bond or bridge + * controller to which this device will be set as port) pass the #NMActiveConnection * that this activation request is a child of * - * Sets the master active connection of @self. + * Sets the controller active connection of @self. */ void -nm_active_connection_set_master(NMActiveConnection *self, NMActiveConnection *master) +nm_active_connection_set_controller(NMActiveConnection *self, NMActiveConnection *controller) { NMActiveConnectionPrivate *priv; g_return_if_fail(NM_IS_ACTIVE_CONNECTION(self)); - g_return_if_fail(NM_IS_ACTIVE_CONNECTION(master)); + g_return_if_fail(NM_IS_ACTIVE_CONNECTION(controller)); priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(self); - /* Master is write-once, and must be set before exporting the object */ - g_return_if_fail(priv->master == NULL); + /* Controller is write-once, and must be set before exporting the object */ + g_return_if_fail(priv->controller == NULL); 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)); + g_return_if_fail(priv->device != nm_active_connection_get_device(controller)); } - _LOGD("set master %p, %s, state %s", - master, - nm_active_connection_get_settings_connection_id(master), - state_to_string_a(nm_active_connection_get_state(master))); + _LOGD("set controller %p, %s, state %s", + controller, + nm_active_connection_get_settings_connection_id(controller), + state_to_string_a(nm_active_connection_get_state(controller))); - priv->master = g_object_ref(master); - g_signal_connect(priv->master, + priv->controller = g_object_ref(controller); + g_signal_connect(priv->controller, "notify::" NM_ACTIVE_CONNECTION_STATE, G_CALLBACK(master_state_cb), self); - check_master_ready(self); + check_controller_ready(self); } NMActivationType @@ -1347,15 +1348,15 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) break; case PROP_CONTROLLER: case PROP_MASTER: - if (priv->master) - master_device = nm_active_connection_get_device(priv->master); + if (priv->controller) + master_device = nm_active_connection_get_device(priv->controller); nm_dbus_utils_g_value_set_object_path(value, master_device); break; case PROP_INT_SUBJECT: g_value_set_object(value, priv->subject); break; - case PROP_INT_MASTER_READY: - g_value_set_boolean(value, priv->master_ready); + case PROP_INT_CONTROLLER_READY: + g_value_set_boolean(value, priv->controller_ready); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -1401,8 +1402,8 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps /* construct-only */ priv->subject = g_value_dup_object(value); break; - case PROP_INT_MASTER: - nm_active_connection_set_master(self, g_value_get_object(value)); + case PROP_INT_CONTROLLER: + nm_active_connection_set_controller(self, g_value_get_object(value)); break; case PROP_INT_ACTIVATION_TYPE: /* construct-only */ @@ -1529,10 +1530,10 @@ dispose(GObject *object) _device_cleanup(self); - if (priv->master) { - g_signal_handlers_disconnect_by_func(priv->master, G_CALLBACK(master_state_cb), self); + if (priv->controller) { + g_signal_handlers_disconnect_by_func(priv->controller, G_CALLBACK(master_state_cb), self); } - g_clear_object(&priv->master); + g_clear_object(&priv->controller); if (priv->parent) unwatch_parent(self, TRUE); @@ -1774,15 +1775,15 @@ nm_active_connection_class_init(NMActiveConnectionClass *ac_class) NM_TYPE_AUTH_SUBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_INT_MASTER] = - g_param_spec_object(NM_ACTIVE_CONNECTION_INT_MASTER, + obj_properties[PROP_INT_CONTROLLER] = + g_param_spec_object(NM_ACTIVE_CONNECTION_INT_CONTROLLER, "", "", NM_TYPE_ACTIVE_CONNECTION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_INT_MASTER_READY] = - g_param_spec_boolean(NM_ACTIVE_CONNECTION_INT_MASTER_READY, + obj_properties[PROP_INT_CONTROLLER_READY] = + g_param_spec_boolean(NM_ACTIVE_CONNECTION_INT_CONTROLLER_READY, "", "", FALSE, diff --git a/src/core/nm-active-connection.h b/src/core/nm-active-connection.h index 8032294f..12cb311c 100644 --- a/src/core/nm-active-connection.h +++ b/src/core/nm-active-connection.h @@ -45,8 +45,8 @@ #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_CONTROLLER "int-controller" +#define NM_ACTIVE_CONNECTION_INT_CONTROLLER_READY "int-controller-ready" #define NM_ACTIVE_CONNECTION_INT_ACTIVATION_TYPE "int-activation-type" #define NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON "int-activation-reason" @@ -170,11 +170,11 @@ NMAuthSubject *nm_active_connection_get_subject(NMActiveConnection *self); gboolean nm_active_connection_get_user_requested(NMActiveConnection *self); -NMActiveConnection *nm_active_connection_get_master(NMActiveConnection *self); +NMActiveConnection *nm_active_connection_get_controller(NMActiveConnection *self); -gboolean nm_active_connection_get_master_ready(NMActiveConnection *self); +gboolean nm_active_connection_get_controller_ready(NMActiveConnection *self); -void nm_active_connection_set_master(NMActiveConnection *self, NMActiveConnection *master); +void nm_active_connection_set_controller(NMActiveConnection *self, NMActiveConnection *controller); void nm_active_connection_set_parent(NMActiveConnection *self, NMActiveConnection *parent); diff --git a/src/core/nm-audit-manager.c b/src/core/nm-audit-manager.c index 4e134d1a..7cf52946 100644 --- a/src/core/nm-audit-manager.c +++ b/src/core/nm-audit-manager.c @@ -185,13 +185,16 @@ nm_audit_log(NMAuditManager *self, priv = NM_AUDIT_MANAGER_GET_PRIVATE(self); if (priv->auditd_fd >= 0) { - audit_log_user_message(priv->auditd_fd, - AUDIT_USYS_CONFIG, - build_message(&strbuf, BACKEND_AUDITD, fields), - NULL, - NULL, - NULL, - success); + int r; + + r = audit_log_user_message(priv->auditd_fd, + AUDIT_USYS_CONFIG, + build_message(&strbuf, BACKEND_AUDITD, fields), + NULL, + NULL, + NULL, + success); + (void) r; } #endif diff --git a/src/core/nm-checkpoint.c b/src/core/nm-checkpoint.c index 74adf484..ffcf6e3a 100644 --- a/src/core/nm-checkpoint.c +++ b/src/core/nm-checkpoint.c @@ -10,12 +10,15 @@ #include "nm-active-connection.h" #include "nm-act-request.h" #include "libnm-core-aux-intern/nm-auth-subject.h" +#include "libnm-core-intern/nm-keyfile-internal.h" #include "nm-core-utils.h" #include "nm-dbus-interface.h" #include "devices/nm-device.h" +#include "nm-config.h" #include "nm-manager.h" #include "settings/nm-settings.h" #include "settings/nm-settings-connection.h" +#include "settings/plugins/keyfile/nms-keyfile-storage.h" #include "nm-simple-connection.h" #include "nm-utils.h" @@ -28,11 +31,14 @@ typedef struct { NMDevice *device; NMConnection *applied_connection; NMConnection *settings_connection; + NMConnection *settings_connection_shadowed; guint64 ac_version_id; NMDeviceState state; bool is_software : 1; bool realized : 1; bool activation_lifetime_bound_to_profile_visibility : 1; + bool settings_connection_is_unsaved : 1; + bool settings_connection_is_shadowed_owned : 1; NMUnmanFlagOp unmanaged_explicit; NMActivationReason activation_reason; gulong dev_exported_change_id; @@ -55,6 +61,8 @@ struct _NMCheckpointPrivate { NMCheckpointTimeoutCallback timeout_cb; gpointer timeout_data; + + NMGlobalDnsConfig *global_dns_config; }; struct _NMCheckpointClass { @@ -147,37 +155,111 @@ nm_checkpoint_includes_devices_of(NMCheckpoint *self, NMCheckpoint *cp_for_devic return NULL; } +static NMConnection * +parse_connection_from_shadowed_file(const char *path, GError **error) +{ + nm_auto_unref_keyfile GKeyFile *keyfile = NULL; + gs_free char *base_dir = NULL; + char *sep; + + keyfile = g_key_file_new(); + if (!g_key_file_load_from_file(keyfile, path, G_KEY_FILE_NONE, error)) + return NULL; + + sep = strrchr(path, '/'); + base_dir = g_strndup(path, sep - path); + + return nm_keyfile_read(keyfile, base_dir, NM_KEYFILE_HANDLER_FLAGS_NONE, NULL, NULL, error); +} + static NMSettingsConnection * -find_settings_connection(NMCheckpoint *self, - DeviceCheckpoint *dev_checkpoint, - gboolean *need_update, - gboolean *need_activation) +find_settings_connection(NMCheckpoint *self, + DeviceCheckpoint *dev_checkpoint, + gboolean *need_update, + gboolean *need_update_shadowed, + gboolean *need_activation, + NMSettingsConnectionPersistMode *persist_mode) { NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE(self); NMActiveConnection *active; NMSettingsConnection *sett_conn; + const char *shadowed_file; + NMConnection *shadowed_connection = NULL; const char *uuid, *ac_uuid; const CList *tmp_clist; - - *need_activation = FALSE; - *need_update = FALSE; + gboolean sett_conn_unsaved; + NMSettingsStorage *storage; + + *need_activation = FALSE; + *need_update = FALSE; + *need_update_shadowed = FALSE; + + /* With regard to storage, there are 4 different possible states for the settings + * connection: 1) persistent; 2) in-memory only; 3) in-memory shadowing a persistent + * file; 4) in-memory shadowing a detached persistent file (i.e. the deletion of + * the connection doesn't delete the persistent file). + */ + if (dev_checkpoint->settings_connection_is_unsaved) { + if (dev_checkpoint->settings_connection_shadowed) { + if (dev_checkpoint->settings_connection_is_shadowed_owned) + *persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY; + else + *persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED; + } else + *persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY; + } else { + *persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; + } uuid = nm_connection_get_uuid(dev_checkpoint->settings_connection); sett_conn = nm_settings_get_connection_by_uuid(NM_SETTINGS_GET, uuid); - if (!sett_conn) - return NULL; - - /* Now check if the connection changed, ... */ - if (!nm_connection_compare(dev_checkpoint->settings_connection, - nm_settings_connection_get_connection(sett_conn), - NM_SETTING_COMPARE_FLAG_EXACT)) { + /* Check if the connection changed */ + if (sett_conn + && !nm_connection_compare(dev_checkpoint->settings_connection, + nm_settings_connection_get_connection(sett_conn), + NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP)) { _LOGT("rollback: settings connection %s changed", uuid); *need_update = TRUE; *need_activation = TRUE; } - /* ... is active, ... */ + storage = sett_conn ? nm_settings_connection_get_storage(sett_conn) : NULL; + shadowed_file = storage ? nm_settings_storage_get_shadowed_storage(storage, NULL) : NULL; + shadowed_connection = + shadowed_file ? parse_connection_from_shadowed_file(shadowed_file, NULL) : NULL; + + if (dev_checkpoint->settings_connection_shadowed) { + if (!shadowed_connection + || !nm_connection_compare(dev_checkpoint->settings_connection_shadowed, + shadowed_connection, + NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP)) { + _LOGT("rollback: shadowed connection changed for %s", uuid); + *need_update_shadowed = TRUE; + *need_update = TRUE; + } + } else { + if (shadowed_connection) { + _LOGT("rollback: shadowed connection changed for %s", uuid); + *need_update = TRUE; + } + } + + if (!sett_conn) + return NULL; + + /* Check if the connection unsaved flag changed */ + sett_conn_unsaved = NM_FLAGS_HAS(nm_settings_connection_get_flags(sett_conn), + NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED); + if (sett_conn_unsaved != dev_checkpoint->settings_connection_is_unsaved) { + _LOGT("rollback: storage changed for settings connection %s: unsaved (%d -> %d)", + uuid, + dev_checkpoint->settings_connection_is_unsaved, + sett_conn_unsaved); + *need_update = TRUE; + } + + /* Check if the active state changed */ nm_manager_for_each_active_connection (priv->manager, active, tmp_clist) { ac_uuid = nm_settings_connection_get_uuid(nm_active_connection_get_settings_connection(active)); @@ -193,7 +275,7 @@ find_settings_connection(NMCheckpoint *self, return sett_conn; } - /* ... or if the connection was reactivated/reapplied */ + /* Check if the connection was reactivated/reapplied */ if (nm_active_connection_version_id_get(active) != dev_checkpoint->ac_version_id) { _LOGT("rollback: active connection version id of %s changed", uuid); *need_activation = TRUE; @@ -209,12 +291,19 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp NMSettingsConnection *connection; gs_unref_object NMAuthSubject *subject = NULL; GError *local_error = NULL; - gboolean need_update, need_activation; + gboolean need_update; + gboolean need_update_shadowed; + gboolean need_activation; NMSettingsConnectionPersistMode persist_mode; NMSettingsConnectionIntFlags sett_flags; NMSettingsConnectionIntFlags sett_mask; - connection = find_settings_connection(self, dev_checkpoint, &need_update, &need_activation); + connection = find_settings_connection(self, + dev_checkpoint, + &need_update, + &need_update_shadowed, + &need_activation, + &persist_mode); /* FIXME: we need to ensure to re-create/update the profile for the * same settings plugin. E.g. if it was a keyfile in /run or /etc, @@ -226,9 +315,26 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp sett_mask = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE; if (connection) { + if (need_update_shadowed) { + _LOGD("rollback: updating shadowed file for connection %s", + nm_connection_get_uuid(dev_checkpoint->settings_connection)); + nm_settings_connection_update( + connection, + NULL, + dev_checkpoint->settings_connection_shadowed, + NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK, + sett_flags, + sett_mask, + NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS + | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET, + "checkpoint-rollback", + NULL); + } + if (need_update) { - _LOGD("rollback: updating connection %s", nm_settings_connection_get_uuid(connection)); - persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP; + _LOGD("rollback: updating connection %s with persist mode \"%s\"", + nm_connection_get_uuid(dev_checkpoint->settings_connection), + nm_settings_connection_persist_mode_to_string(persist_mode)); nm_settings_connection_update( connection, NULL, @@ -243,21 +349,54 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp } } else { /* The connection was deleted, recreate it */ - _LOGD("rollback: adding connection %s again", - nm_connection_get_uuid(dev_checkpoint->settings_connection)); - - persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; - if (!nm_settings_add_connection(NM_SETTINGS_GET, - NULL, - dev_checkpoint->settings_connection, - persist_mode, - NM_SETTINGS_CONNECTION_ADD_REASON_NONE, - sett_flags, - &connection, - &local_error)) { - _LOGD("rollback: connection add failure: %s", local_error->message); - g_clear_error(&local_error); - return FALSE; + if (need_update_shadowed) { + _LOGD("rollback: adding back shadowed file for connection %s", + nm_connection_get_uuid(dev_checkpoint->settings_connection)); + + if (!nm_settings_add_connection(NM_SETTINGS_GET, + NULL, + dev_checkpoint->settings_connection_shadowed, + NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK, + NM_SETTINGS_CONNECTION_ADD_REASON_NONE, + sett_flags, + &connection, + &local_error)) { + _LOGD("rollback: connection add failure: %s", local_error->message); + g_clear_error(&local_error); + return FALSE; + } + + _LOGD("rollback: updating connection %s with persist mode \"%s\"", + nm_connection_get_uuid(dev_checkpoint->settings_connection), + nm_settings_connection_persist_mode_to_string(persist_mode)); + + nm_settings_connection_update( + connection, + NULL, + dev_checkpoint->settings_connection, + persist_mode, + sett_flags, + sett_mask, + NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS + | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET, + "checkpoint-rollback", + NULL); + } else { + _LOGD("rollback: adding back connection %s with persist mode \"%s\"", + nm_connection_get_uuid(dev_checkpoint->settings_connection), + nm_settings_connection_persist_mode_to_string(persist_mode)); + if (!nm_settings_add_connection(NM_SETTINGS_GET, + NULL, + dev_checkpoint->settings_connection, + persist_mode, + NM_SETTINGS_CONNECTION_ADD_REASON_NONE, + sett_flags, + &connection, + &local_error)) { + _LOGD("rollback: connection add failure: %s", local_error->message); + g_clear_error(&local_error); + return FALSE; + } } need_activation = TRUE; } @@ -359,11 +498,15 @@ nm_checkpoint_rollback(NMCheckpoint *self) while (g_hash_table_iter_next(&iter, (gpointer *) &device, (gpointer *) &dev_checkpoint)) { guint32 result = NM_ROLLBACK_RESULT_OK; - _LOGD("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d)", + _LOGD("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d, " + "connection-unsaved %d, connection-shadowed %d, connection-shadowed-owned %d)", dev_checkpoint->original_dev_name, (int) dev_checkpoint->state, dev_checkpoint->realized, - dev_checkpoint->unmanaged_explicit); + dev_checkpoint->unmanaged_explicit, + dev_checkpoint->settings_connection_is_unsaved, + !!dev_checkpoint->settings_connection_shadowed, + dev_checkpoint->settings_connection_is_shadowed_owned); if (nm_device_is_real(device)) { if (!dev_checkpoint->realized) { @@ -491,6 +634,17 @@ next_dev: } } } + if (NM_FLAGS_HAS(priv->flags, NM_CHECKPOINT_CREATE_FLAG_TRACK_INTERNAL_GLOBAL_DNS) + && priv->global_dns_config) { + gs_free_error GError *error = NULL; + NMConfig *config; + + config = nm_manager_get_config(priv->manager); + nm_assert(config); + if (!nm_config_set_global_dns(config, priv->global_dns_config, &error)) { + _LOGE("set global DNS failed with error: %s", error->message); + } + } return g_variant_new("(a{su})", &builder); } @@ -504,6 +658,7 @@ device_checkpoint_destroy(gpointer data) g_clear_object(&dev_checkpoint->applied_connection); g_clear_object(&dev_checkpoint->settings_connection); g_clear_object(&dev_checkpoint->device); + g_clear_object(&dev_checkpoint->settings_connection_shadowed); g_free(dev_checkpoint->original_dev_path); g_free(dev_checkpoint->original_dev_name); @@ -541,7 +696,7 @@ _dev_exported_changed(NMDBusObject *obj, NMCheckpoint *checkpoint) } static DeviceCheckpoint * -device_checkpoint_create(NMCheckpoint *checkpoint, NMDevice *device) +device_checkpoint_create(NMCheckpoint *self, NMDevice *device) { DeviceCheckpoint *dev_checkpoint; NMConnection *applied_connection; @@ -565,7 +720,7 @@ device_checkpoint_create(NMCheckpoint *checkpoint, NMDevice *device) dev_checkpoint->dev_exported_change_id = g_signal_connect(device, NM_DBUS_OBJECT_EXPORTED_CHANGED, G_CALLBACK(_dev_exported_changed), - checkpoint); + self); if (nm_device_get_unmanaged_mask(device, NM_UNMANAGED_USER_EXPLICIT)) { dev_checkpoint->unmanaged_explicit = @@ -575,6 +730,11 @@ device_checkpoint_create(NMCheckpoint *checkpoint, NMDevice *device) act_request = nm_device_get_act_request(device); if (act_request) { + NMSettingsStorage *storage; + gboolean shadowed_owned = FALSE; + const char *shadowed_file; + gs_free_error GError *error = NULL; + settings_connection = nm_act_request_get_settings_connection(act_request); applied_connection = nm_act_request_get_applied_connection(act_request); @@ -588,6 +748,24 @@ device_checkpoint_create(NMCheckpoint *checkpoint, NMDevice *device) dev_checkpoint->activation_lifetime_bound_to_profile_visibility = NM_FLAGS_HAS(nm_active_connection_get_state_flags(NM_ACTIVE_CONNECTION(act_request)), NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY); + + dev_checkpoint->settings_connection_is_unsaved = + NM_FLAGS_HAS(nm_settings_connection_get_flags(settings_connection), + NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED); + + storage = nm_settings_connection_get_storage(settings_connection); + shadowed_file = + storage ? nm_settings_storage_get_shadowed_storage(storage, &shadowed_owned) : NULL; + if (shadowed_file) { + dev_checkpoint->settings_connection_is_shadowed_owned = shadowed_owned; + dev_checkpoint->settings_connection_shadowed = + parse_connection_from_shadowed_file(shadowed_file, &error); + if (!dev_checkpoint->settings_connection_shadowed) { + _LOGW("error reading shadowed connection file for %s: %s", + nm_device_get_iface(device), + error->message); + } + } } return dev_checkpoint; @@ -742,6 +920,19 @@ nm_checkpoint_new(NMManager *manager, NM_MANAGER_DEVICE_REMOVED, G_CALLBACK(_device_removed), self); + if (NM_FLAGS_HAS(flags, NM_CHECKPOINT_CREATE_FLAG_TRACK_INTERNAL_GLOBAL_DNS)) { + NMConfigData *config_data; + NMGlobalDnsConfig *dns_config = NULL; + + config_data = nm_config_get_data(nm_manager_get_config(manager)); + if (config_data) { + dns_config = nm_config_data_get_global_dns_config(config_data); + if (!dns_config || nm_global_dns_config_is_internal(dns_config)) { + priv->global_dns_config = nm_global_dns_config_clone(dns_config); + } + } + } + return self; } @@ -756,6 +947,7 @@ dispose(GObject *object) nm_clear_pointer(&priv->devices, g_hash_table_unref); nm_clear_pointer(&priv->connection_uuids, g_hash_table_unref); nm_clear_pointer(&priv->removed_devices, g_ptr_array_unref); + nm_global_dns_config_free(priv->global_dns_config); nm_clear_g_signal_handler(priv->manager, &priv->dev_removed_id); g_clear_object(&priv->manager); diff --git a/src/core/nm-config-data.c b/src/core/nm-config-data.c index 468e56b8..d4498edd 100644 --- a/src/core/nm-config-data.c +++ b/src/core/nm-config-data.c @@ -2436,3 +2436,50 @@ nm_config_data_class_init(NMConfigDataClass *config_class) g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); } + +static NMGlobalDnsDomain * +nm_global_dns_domain_clone(NMGlobalDnsDomain *old_domain) +{ + if (old_domain) { + NMGlobalDnsDomain *new_domain = g_malloc0(sizeof(NMGlobalDnsDomain)); + new_domain->name = g_strdup(old_domain->name); + new_domain->servers = (char **) nm_strv_dup(old_domain->servers, -1, TRUE); + new_domain->options = (char **) nm_strv_dup(old_domain->options, -1, TRUE); + return new_domain; + } else { + return NULL; + } +} + +NMGlobalDnsConfig * +nm_global_dns_config_clone(NMGlobalDnsConfig *old_dns_config) +{ + NMGlobalDnsConfig *new_dns_config; + gpointer key, value; + NMGlobalDnsDomain *old_domain; + GHashTableIter iter; + + new_dns_config = g_malloc0(sizeof(NMGlobalDnsConfig)); + new_dns_config->internal = TRUE; + + if (old_dns_config) { + new_dns_config->internal = old_dns_config->internal; + new_dns_config->searches = nm_strv_dup(old_dns_config->searches, -1, TRUE); + new_dns_config->options = nm_strv_dup(old_dns_config->options, -1, TRUE); + new_dns_config->domains = g_hash_table_new_full(nm_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) global_dns_domain_free); + if (old_dns_config->domains) { + g_hash_table_iter_init(&iter, old_dns_config->domains); + while (g_hash_table_iter_next(&iter, &key, &value)) { + old_domain = value; + g_hash_table_insert(new_dns_config->domains, + g_strdup(key), + nm_global_dns_domain_clone(old_domain)); + } + } + global_dns_config_seal_domains(new_dns_config); + } + return new_dns_config; +} diff --git a/src/core/nm-config-data.h b/src/core/nm-config-data.h index 9e7a50fc..0344ce90 100644 --- a/src/core/nm-config-data.h +++ b/src/core/nm-config-data.h @@ -280,7 +280,8 @@ int nm_global_dns_config_cmp(const NMGlobalDnsConfig *a, const NMGlobalDnsConfig *b, gboolean check_internal); 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_clone(NMGlobalDnsConfig *dns_config); +void nm_global_dns_config_free(NMGlobalDnsConfig *dns_config); NMGlobalDnsConfig *nm_global_dns_config_from_dbus(const GValue *value, GError **error); void nm_global_dns_config_to_dbus(const NMGlobalDnsConfig *dns_config, GValue *value); diff --git a/src/core/nm-connectivity.h b/src/core/nm-connectivity.h index d9ea95c2..e0353f9f 100644 --- a/src/core/nm-connectivity.h +++ b/src/core/nm-connectivity.h @@ -25,10 +25,10 @@ nm_connectivity_state_cmp(NMConnectivityState a, NMConnectivityState b) /*****************************************************************************/ -#define NM_CONNECTIVITY_ERROR ((NMConnectivityState) -1) -#define NM_CONNECTIVITY_FAKE ((NMConnectivityState) -2) -#define NM_CONNECTIVITY_CANCELLED ((NMConnectivityState) -3) -#define NM_CONNECTIVITY_DISPOSING ((NMConnectivityState) -4) +#define NM_CONNECTIVITY_ERROR ((NMConnectivityState) - 1) +#define NM_CONNECTIVITY_FAKE ((NMConnectivityState) - 2) +#define NM_CONNECTIVITY_CANCELLED ((NMConnectivityState) - 3) +#define NM_CONNECTIVITY_DISPOSING ((NMConnectivityState) - 4) #define NM_TYPE_CONNECTIVITY (nm_connectivity_get_type()) #define NM_CONNECTIVITY(obj) \ diff --git a/src/core/nm-core-utils.c b/src/core/nm-core-utils.c index 178ea3c4..dd1e9939 100644 --- a/src/core/nm-core-utils.c +++ b/src/core/nm-core-utils.c @@ -121,7 +121,8 @@ _nm_singleton_instance_weak_cb(gpointer data, GObject *where_the_object_was) _singletons = g_slist_remove(_singletons, where_the_object_was); } -static void __attribute__((destructor)) _nm_singleton_instance_destroy(void) +static void __attribute__((destructor)) +_nm_singleton_instance_destroy(void) { _singletons_shutdown = TRUE; diff --git a/src/core/nm-core-utils.h b/src/core/nm-core-utils.h index f015236d..fdfed5f6 100644 --- a/src/core/nm-core-utils.h +++ b/src/core/nm-core-utils.h @@ -306,7 +306,7 @@ typedef enum { NM_UTILS_STABLE_TYPE_RANDOM = 3, } NMUtilsStableType; -#define NM_UTILS_STABLE_TYPE_NONE ((NMUtilsStableType) -1) +#define NM_UTILS_STABLE_TYPE_NONE ((NMUtilsStableType) - 1) NMUtilsStableType nm_utils_stable_id_parse(const char *stable_id, const char *deviceid, diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index f428d04c..12357fba 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -4338,7 +4338,7 @@ _l3_commit_ndisc_params(NML3Cfg *self, NML3CfgCommitType commit_type) if (l3cd) { reachable_set = nm_l3_config_data_get_ndisc_reachable_time_msec(l3cd, &reachable); retrans_set = nm_l3_config_data_get_ndisc_retrans_timer_msec(l3cd, &retrans); - hop_limit = nm_l3_config_data_get_ndisc_hop_limit(l3cd, &hop_limit); + hop_limit_set = nm_l3_config_data_get_ndisc_hop_limit(l3cd, &hop_limit); } ifname = nm_l3cfg_get_ifname(self, TRUE); diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c index 730ba476..b2a827e3 100644 --- a/src/core/nm-manager.c +++ b/src/core/nm-manager.c @@ -46,7 +46,7 @@ #include "nm-priv-helper-call.h" #include "nm-rfkill-manager.h" #include "nm-session-monitor.h" -#include "nm-sleep-monitor.h" +#include "nm-power-monitor.h" #include "settings/nm-settings-connection.h" #include "settings/nm-settings.h" #include "vpn/nm-vpn-manager.h" @@ -214,7 +214,7 @@ typedef struct { NMVpnManager *vpn_manager; - NMSleepMonitor *sleep_monitor; + NMPowerMonitor *power_monitor; NMAuthManager *auth_mgr; @@ -1914,7 +1914,8 @@ find_device_by_ip_iface(NMManager *self, const char *iface) * is given, this function will only return master devices and will ensure * @slave, when activated, can be a slave of the returned master device. If * @connection is given, this function will only consider devices that are - * compatible with @connection. + * compatible with @connection. If @child is given, this function will only + * return parent device. * * Returns: the matching #NMDevice */ @@ -1922,7 +1923,8 @@ static NMDevice * find_device_by_iface(NMManager *self, const char *iface, NMConnection *connection, - NMConnection *slave) + NMConnection *slave, + NMConnection *child) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); NMDevice *fallback = NULL; @@ -1936,11 +1938,13 @@ find_device_by_iface(NMManager *self, if (connection && !nm_device_check_connection_compatible(candidate, connection, TRUE, NULL)) continue; if (slave) { - if (!nm_device_is_master(candidate)) + if (!nm_device_is_controller(candidate)) continue; if (!nm_device_check_slave_connection_compatible(candidate, slave)) continue; } + if (child && !nm_device_can_be_parent(candidate)) + continue; if (nm_device_is_real(candidate)) return candidate; @@ -2405,7 +2409,7 @@ find_parent_device_for_connection(NMManager *self, NM_SET_OUT(out_parent_spec, parent_name); /* Try as an interface name of a parent device */ - parent = find_device_by_iface(self, parent_name, NULL, NULL); + parent = find_device_by_iface(self, parent_name, NULL, NULL, connection); if (parent) return parent; @@ -3777,7 +3781,7 @@ recheck_assume_connection(NMManager *self, NMDevice *device) &master_ac, NULL) && master_ac) - nm_active_connection_set_master(active, master_ac); + nm_active_connection_set_controller(active, master_ac); active_connection_add(self, active); nm_device_queue_activation(device, NM_ACT_REQUEST(active)); @@ -4434,10 +4438,25 @@ platform_query_devices(NMManager *self) links = nm_platform_link_get_all(priv->platform); if (!links) return; + for (i = 0; i < links->len; i++) { - const NMPlatformLink *link = NMP_OBJECT_CAST_LINK(links->pdata[i]); + const NMPlatformLink *elem = NMP_OBJECT_CAST_LINK(links->pdata[i]); + const NMPlatformLink *link; const NMConfigDeviceStateData *dev_state; + /* + * @links is an immutable snapshot of the platform links captured before + * the loop was started. It's possible that in the meantime, while + * processing netlink events in platform_link_added(), a link was + * renamed. If that happens, we have 2 different views of the same + * ifindex: the one from @links and the one from platform. This can + * cause race conditions; make sure to use the latest known version of + * the link. + */ + link = nm_platform_link_get(priv->platform, elem->ifindex); + if (!link) + continue; + dev_state = nm_config_device_state_get(priv->config, link->ifindex); platform_link_added(self, link->ifindex, @@ -4921,7 +4940,7 @@ find_master(NMManager *self, nm_assert(!out_master_ac || !*out_master_ac); s_con = nm_connection_get_setting_connection(connection); - master = nm_setting_connection_get_master(s_con); + master = nm_setting_connection_get_controller(s_con); if (master == NULL) return TRUE; /* success, but no master */ @@ -5003,7 +5022,7 @@ find_master(NMManager *self, } if (!master_connection) { - master_device = find_device_by_iface(self, master, NULL, connection); + master_device = find_device_by_iface(self, master, NULL, connection, NULL); if (!master_device) { g_set_error(error, NM_MANAGER_ERROR, @@ -5924,7 +5943,7 @@ _internal_activate_device(NMManager *self, NMActiveConnection *active, GError ** NM_DEVICE_STATE_REASON_USER_REQUESTED); } - nm_active_connection_set_master(active, master_ac); + nm_active_connection_set_controller(active, master_ac); _LOGD(LOGD_CORE, "Activation of '%s' depends on active connection %p %s", nm_settings_connection_get_id(sett_conn), @@ -6445,7 +6464,7 @@ validate_activation_request(NMManager *self, if (!iface) return NULL; - device = find_device_by_iface(self, iface, connection, NULL); + device = find_device_by_iface(self, iface, connection, NULL, NULL); if (!device) { g_set_error_literal(error, NM_MANAGER_ERROR, @@ -7128,7 +7147,7 @@ static gboolean sleep_devices_add(NMManager *self, NMDevice *device, gboolean suspending) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMSleepMonitorInhibitorHandle *handle = NULL; + NMPowerMonitorInhibitorHandle *handle = NULL; if (g_hash_table_lookup_extended(priv->sleep_devices, device, NULL, (gpointer *) &handle)) { if (suspending) { @@ -7136,16 +7155,16 @@ sleep_devices_add(NMManager *self, NMDevice *device, gboolean suspending) * Even if we had an old handle, it might be stale by now. */ g_hash_table_insert(priv->sleep_devices, device, - nm_sleep_monitor_inhibit_take(priv->sleep_monitor)); + nm_power_monitor_inhibit_take(priv->power_monitor)); if (handle) - nm_sleep_monitor_inhibit_release(priv->sleep_monitor, handle); + nm_power_monitor_inhibit_release(priv->power_monitor, handle); } return FALSE; } g_hash_table_insert(priv->sleep_devices, g_object_ref(device), - suspending ? nm_sleep_monitor_inhibit_take(priv->sleep_monitor) : NULL); + suspending ? nm_power_monitor_inhibit_take(priv->power_monitor) : NULL); g_signal_connect(device, "notify::" NM_DEVICE_STATE, G_CALLBACK(device_sleep_cb), self); return TRUE; } @@ -7154,13 +7173,13 @@ static gboolean sleep_devices_remove(NMManager *self, NMDevice *device) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMSleepMonitorInhibitorHandle *handle; + NMPowerMonitorInhibitorHandle *handle; if (!g_hash_table_lookup_extended(priv->sleep_devices, device, NULL, (gpointer *) &handle)) return FALSE; if (handle) - nm_sleep_monitor_inhibit_release(priv->sleep_monitor, handle); + nm_power_monitor_inhibit_release(priv->power_monitor, handle); /* Remove device from hash */ g_signal_handlers_disconnect_by_func(device, device_sleep_cb, self); @@ -7177,14 +7196,14 @@ sleep_devices_clear(NMManager *self) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); NMDevice *device; - NMSleepMonitorInhibitorHandle *handle; + NMPowerMonitorInhibitorHandle *handle; GHashTableIter iter; g_hash_table_iter_init(&iter, priv->sleep_devices); while (g_hash_table_iter_next(&iter, (gpointer *) &device, (gpointer *) &handle)) { g_signal_handlers_disconnect_by_func(device, device_sleep_cb, self); if (handle) - nm_sleep_monitor_inhibit_release(priv->sleep_monitor, handle); + nm_power_monitor_inhibit_release(priv->power_monitor, handle); g_object_unref(device); g_hash_table_iter_remove(&iter); } @@ -7216,6 +7235,33 @@ device_sleep_cb(NMDevice *device, GParamSpec *pspec, NMManager *self) } static void +_handle_device_takedown(NMManager *self, + NMDevice *device, + gboolean suspending, + gboolean is_shutdown) +{ + nm_device_notify_sleeping(device); + + if (nm_device_is_activating(device) + || nm_device_get_state(device) == NM_DEVICE_STATE_ACTIVATED) { + _LOGD(LOGD_SUSPEND, + "%s: wait disconnection of device %s", + is_shutdown ? "shutdown" : "sleep", + nm_device_get_ip_iface(device)); + + if (sleep_devices_add(self, device, suspending)) + nm_device_queue_state(device, + NM_DEVICE_STATE_DEACTIVATING, + NM_DEVICE_STATE_REASON_SLEEPING); + } else { + nm_device_set_unmanaged_by_flags(device, + NM_UNMANAGED_SLEEPING, + NM_UNMAN_FLAG_OP_SET_UNMANAGED, + NM_DEVICE_STATE_REASON_SLEEPING); + } +} + +static void do_sleep_wake(NMManager *self, gboolean sleeping_changed) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); @@ -7249,24 +7295,7 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed) continue; } - nm_device_notify_sleeping(device); - - if (nm_device_is_activating(device) - || nm_device_get_state(device) == NM_DEVICE_STATE_ACTIVATED) { - _LOGD(LOGD_SUSPEND, - "sleep: wait disconnection of device %s", - nm_device_get_ip_iface(device)); - - if (sleep_devices_add(self, device, suspending)) - nm_device_queue_state(device, - NM_DEVICE_STATE_DEACTIVATING, - NM_DEVICE_STATE_REASON_SLEEPING); - } else { - nm_device_set_unmanaged_by_flags(device, - NM_UNMANAGED_SLEEPING, - NM_UNMAN_FLAG_OP_SET_UNMANAGED, - NM_DEVICE_STATE_REASON_SLEEPING); - } + _handle_device_takedown(self, device, suspending, FALSE); } } else { _LOGD(LOGD_SUSPEND, "sleep: %s...", waking_from_suspend ? "waking up" : "re-enabling"); @@ -7438,7 +7467,7 @@ impl_manager_sleep(NMDBusObject *obj, } static void -sleeping_cb(NMSleepMonitor *monitor, gboolean is_about_to_suspend, gpointer user_data) +sleeping_cb(NMPowerMonitor *monitor, gboolean is_about_to_suspend, gpointer user_data) { NMManager *self = user_data; @@ -7447,6 +7476,41 @@ sleeping_cb(NMSleepMonitor *monitor, gboolean is_about_to_suspend, gpointer user } static void +shutdown_cb(NMPowerMonitor *monitor, gpointer user_data) +{ + NMManager *self = user_data; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); + NMDevice *device; + + _LOGT(LOGD_SUSPEND, "shutdown: received shutdown signal"); + + c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { + NMSettingConnection *s_con; + gboolean take_down = FALSE; + + s_con = nm_device_get_applied_setting(device, NM_TYPE_SETTING_CONNECTION); + if (!s_con) + continue; + + if (nm_setting_connection_get_down_on_poweroff(s_con) + == NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_YES) + take_down = TRUE; + else if (nm_setting_connection_get_down_on_poweroff(s_con) + == NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_DEFAULT) + take_down = nm_config_data_get_connection_default_int64( + NM_CONFIG_GET_DATA, + NM_CON_DEFAULT("connection.down-on-poweroff"), + device, + NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_NO, + NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_YES, + NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_NO); + + if (take_down) + _handle_device_takedown(self, device, FALSE, TRUE); + } +} + +static void _internal_enable(NMManager *self, gboolean enable) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); @@ -8458,7 +8522,8 @@ impl_manager_checkpoint_create(NMDBusObject *obj, | NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS | NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES | NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING - | NM_CHECKPOINT_CREATE_FLAG_NO_PRESERVE_EXTERNAL_PORTS)))) { + | NM_CHECKPOINT_CREATE_FLAG_NO_PRESERVE_EXTERNAL_PORTS + | NM_CHECKPOINT_CREATE_FLAG_TRACK_INTERNAL_GLOBAL_DNS)))) { g_dbus_method_invocation_return_error_literal(invocation, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS, @@ -8834,8 +8899,9 @@ nm_manager_init(NMManager *self) priv->devcon_data_dict = g_hash_table_new(_devcon_data_hash, _devcon_data_equal); /* sleep/wake handling */ - priv->sleep_monitor = nm_sleep_monitor_new(); - g_signal_connect(priv->sleep_monitor, NM_SLEEP_MONITOR_SLEEPING, G_CALLBACK(sleeping_cb), self); + priv->power_monitor = nm_power_monitor_new(); + g_signal_connect(priv->power_monitor, NM_POWER_MONITOR_SLEEPING, G_CALLBACK(sleeping_cb), self); + g_signal_connect(priv->power_monitor, NM_POWER_MONITOR_SHUTDOWN, G_CALLBACK(shutdown_cb), self); /* Listen for authorization changes */ priv->auth_mgr = g_object_ref(nm_auth_manager_get()); @@ -8979,9 +9045,10 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) case PROP_CHECKPOINTS: g_value_take_boxed( value, - priv->checkpoint_mgr ? nm_strv_make_deep_copied( - nm_checkpoint_manager_get_checkpoint_paths(priv->checkpoint_mgr, NULL)) - : NULL); + priv->checkpoint_mgr + ? nm_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); @@ -9134,9 +9201,9 @@ dispose(GObject *object) nm_clear_pointer(&priv->sleep_devices, g_hash_table_unref); } - if (priv->sleep_monitor) { - g_signal_handlers_disconnect_by_func(priv->sleep_monitor, sleeping_cb, self); - g_clear_object(&priv->sleep_monitor); + if (priv->power_monitor) { + g_signal_handlers_disconnect_by_func(priv->power_monitor, sleeping_cb, self); + g_clear_object(&priv->power_monitor); } if (priv->fw_monitor) { @@ -9765,3 +9832,11 @@ nm_manager_class_init(NMManagerClass *manager_class) 1, NM_TYPE_DEVICE); } + +NMConfig * +nm_manager_get_config(NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); + + return priv->config; +} diff --git a/src/core/nm-manager.h b/src/core/nm-manager.h index 3028eb7e..3c5213c4 100644 --- a/src/core/nm-manager.h +++ b/src/core/nm-manager.h @@ -10,6 +10,7 @@ #include "settings/nm-settings-connection.h" #include "c-list/src/c-list.h" #include "nm-dbus-manager.h" +#include "nm-config-data.h" #define NM_TYPE_MANAGER (nm_manager_get_type()) #define NM_MANAGER(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MANAGER, NMManager)) @@ -266,4 +267,6 @@ gboolean nm_manager_devcon_autoconnect_blocked_reason_set(NMManager * NMSettingsAutoconnectBlockedReason value, gboolean set); +NMConfig *nm_manager_get_config(NMManager *self); + #endif /* __NETWORKMANAGER_MANAGER_H__ */ diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c index feea97b1..60d11c75 100644 --- a/src/core/nm-policy.c +++ b/src/core/nm-policy.c @@ -1805,7 +1805,7 @@ unblock_autoconnect_for_ports(NMPolicy *self, s_slave_con = nm_settings_connection_get_setting(sett_conn, NM_META_SETTING_TYPE_CONNECTION); - slave_master = nm_setting_connection_get_master(s_slave_con); + slave_master = nm_setting_connection_get_controller(s_slave_con); if (!slave_master) continue; diff --git a/src/core/nm-sleep-monitor.c b/src/core/nm-power-monitor.c index 66ea2f6c..289b0ff0 100644 --- a/src/core/nm-sleep-monitor.c +++ b/src/core/nm-power-monitor.c @@ -6,7 +6,7 @@ #include "src/core/nm-default-daemon.h" -#include "nm-sleep-monitor.h" +#include "nm-power-monitor.h" #include <sys/stat.h> #include <gio/gunixfdlist.h> @@ -15,24 +15,15 @@ #include "libnm-core-intern/nm-core-internal.h" #include "NetworkManagerUtils.h" -#if defined(SUSPEND_RESUME_UPOWER) - -#define SUSPEND_DBUS_NAME "org.freedesktop.UPower" -#define SUSPEND_DBUS_PATH "/org/freedesktop/UPower" -#define SUSPEND_DBUS_INTERFACE "org.freedesktop.UPower" -#define USE_UPOWER 1 -#define _NMLOG_PREFIX_NAME "sleep-monitor-up" - -#elif defined(SUSPEND_RESUME_SYSTEMD) || defined(SUSPEND_RESUME_ELOGIND) +#if defined(SUSPEND_RESUME_SYSTEMD) || defined(SUSPEND_RESUME_ELOGIND) #define SUSPEND_DBUS_NAME "org.freedesktop.login1" #define SUSPEND_DBUS_PATH "/org/freedesktop/login1" #define SUSPEND_DBUS_INTERFACE "org.freedesktop.login1.Manager" -#define USE_UPOWER 0 #if defined(SUSPEND_RESUME_SYSTEMD) -#define _NMLOG_PREFIX_NAME "sleep-monitor-sd" +#define _NMLOG_PREFIX_NAME "power-monitor-sd" #else -#define _NMLOG_PREFIX_NAME "sleep-monitor-el" +#define _NMLOG_PREFIX_NAME "power-monitor-el" #endif #elif defined(SUSPEND_RESUME_CONSOLEKIT) @@ -44,12 +35,11 @@ #define SUSPEND_DBUS_NAME "org.freedesktop.ConsoleKit" #define SUSPEND_DBUS_PATH "/org/freedesktop/ConsoleKit/Manager" #define SUSPEND_DBUS_INTERFACE "org.freedesktop.ConsoleKit.Manager" -#define USE_UPOWER 0 -#define _NMLOG_PREFIX_NAME "sleep-monitor-ck" +#define _NMLOG_PREFIX_NAME "power-monitor-ck" #else -#error define one of SUSPEND_RESUME_SYSTEMD, SUSPEND_RESUME_ELOGIND, SUSPEND_RESUME_CONSOLEKIT, or SUSPEND_RESUME_UPOWER +#error define one of SUSPEND_RESUME_SYSTEMD, SUSPEND_RESUME_ELOGIND, SUSPEND_RESUME_CONSOLEKIT #endif @@ -57,12 +47,13 @@ enum { SLEEPING, + SHUTDOWN, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = {0}; -struct _NMSleepMonitor { +struct _NMPowerMonitor { GObject parent; GDBusProxy *proxy; @@ -76,13 +67,14 @@ struct _NMSleepMonitor { gulong sig_id_1; gulong sig_id_2; + gulong sig_id_3; }; -struct _NMSleepMonitorClass { +struct _NMPowerMonitorClass { GObjectClass parent; }; -G_DEFINE_TYPE(NMSleepMonitor, nm_sleep_monitor, G_TYPE_OBJECT); +G_DEFINE_TYPE(NMPowerMonitor, nm_power_monitor, G_TYPE_OBJECT); /*****************************************************************************/ @@ -91,28 +83,8 @@ G_DEFINE_TYPE(NMSleepMonitor, nm_sleep_monitor, G_TYPE_OBJECT); /*****************************************************************************/ -static void sleep_signal(NMSleepMonitor *self, gboolean is_about_to_suspend); - -/*****************************************************************************/ - -#if USE_UPOWER - -static void -upower_sleeping_cb(GDBusProxy *proxy, gpointer user_data) -{ - sleep_signal(user_data, TRUE); -} - -static void -upower_resuming_cb(GDBusProxy *proxy, gpointer user_data) -{ - sleep_signal(user_data, FALSE); -} - -#else /* USE_UPOWER */ - static void -drop_inhibitor(NMSleepMonitor *self, gboolean force) +drop_inhibitor(NMPowerMonitor *self, gboolean force) { if (!force && self->handles_active) return; @@ -135,7 +107,7 @@ static void inhibit_done(GObject *source, GAsyncResult *result, gpointer user_data) { GDBusProxy *proxy = G_DBUS_PROXY(source); - NMSleepMonitor *self = user_data; + NMPowerMonitor *self = user_data; gs_free_error GError *error = NULL; gs_unref_variant GVariant *res = NULL; gs_unref_object GUnixFDList *fd_list = NULL; @@ -161,9 +133,9 @@ inhibit_done(GObject *source, GAsyncResult *result, gpointer user_data) } static void -take_inhibitor(NMSleepMonitor *self) +take_inhibitor(NMPowerMonitor *self) { - g_return_if_fail(NM_IS_SLEEP_MONITOR(self)); + g_return_if_fail(NM_IS_POWER_MONITOR(self)); g_return_if_fail(G_IS_DBUS_PROXY(self->proxy)); drop_inhibitor(self, TRUE); @@ -186,16 +158,10 @@ take_inhibitor(NMSleepMonitor *self) } static void -prepare_for_sleep_cb(GDBusProxy *proxy, gboolean is_about_to_suspend, gpointer data) -{ - sleep_signal(data, is_about_to_suspend); -} - -static void name_owner_cb(GObject *object, GParamSpec *pspec, gpointer user_data) { GDBusProxy *proxy = G_DBUS_PROXY(object); - NMSleepMonitor *self = NM_SLEEP_MONITOR(user_data); + NMPowerMonitor *self = NM_POWER_MONITOR(user_data); char *owner; g_assert(proxy == self->proxy); @@ -207,59 +173,64 @@ name_owner_cb(GObject *object, GParamSpec *pspec, gpointer user_data) drop_inhibitor(self, TRUE); g_free(owner); } -#endif /* USE_UPOWER */ static void -sleep_signal(NMSleepMonitor *self, gboolean is_about_to_suspend) +prepare_for_sleep_cb(GDBusProxy *proxy, gboolean is_about_to_suspend, NMPowerMonitor *self) { - g_return_if_fail(NM_IS_SLEEP_MONITOR(self)); + g_return_if_fail(NM_IS_POWER_MONITOR(self)); _LOGD("received %s signal", is_about_to_suspend ? "SLEEP" : "RESUME"); -#if !USE_UPOWER if (!is_about_to_suspend) take_inhibitor(self); -#endif g_signal_emit(self, signals[SLEEPING], 0, is_about_to_suspend); -#if !USE_UPOWER if (is_about_to_suspend) drop_inhibitor(self, FALSE); -#endif +} + +static void +prepare_for_shutdown_cb(GDBusProxy *proxy, gboolean is_about_to_shutdown, NMPowerMonitor *self) +{ + g_return_if_fail(NM_IS_POWER_MONITOR(self)); + + _LOGD("received SHUTDOWN signal"); + + g_signal_emit(self, signals[SHUTDOWN], 0); } /** - * nm_sleep_monitor_inhibit_take: - * @self: the #NMSleepMonitor instance + * nm_power_monitor_inhibit_take: + * @self: the #NMPowerMonitor instance * * Prevent the release of inhibitor lock * * Returns: an inhibitor handle that must be returned via - * nm_sleep_monitor_inhibit_release(). + * nm_power_monitor_inhibit_release(). **/ -NMSleepMonitorInhibitorHandle * -nm_sleep_monitor_inhibit_take(NMSleepMonitor *self) +NMPowerMonitorInhibitorHandle * +nm_power_monitor_inhibit_take(NMPowerMonitor *self) { - g_return_val_if_fail(NM_IS_SLEEP_MONITOR(self), NULL); + g_return_val_if_fail(NM_IS_POWER_MONITOR(self), NULL); self->handles_active = g_slist_prepend(self->handles_active, NULL); - return (NMSleepMonitorInhibitorHandle *) self->handles_active; + return (NMPowerMonitorInhibitorHandle *) self->handles_active; } /** - * nm_sleep_monitor_inhibit_release: - * @self: the #NMSleepMonitor instance - * @handle: the #NMSleepMonitorInhibitorHandle inhibitor handle. + * nm_power_monitor_inhibit_release: + * @self: the #NMPowerMonitor instance + * @handle: the #NMPowerMonitorInhibitorHandle inhibitor handle. * * Allow again the release of inhibitor lock **/ void -nm_sleep_monitor_inhibit_release(NMSleepMonitor *self, NMSleepMonitorInhibitorHandle *handle) +nm_power_monitor_inhibit_release(NMPowerMonitor *self, NMPowerMonitorInhibitorHandle *handle) { GSList *l; - g_return_if_fail(NM_IS_SLEEP_MONITOR(self)); + g_return_if_fail(NM_IS_POWER_MONITOR(self)); g_return_if_fail(handle); l = (GSList *) handle; @@ -273,13 +244,11 @@ nm_sleep_monitor_inhibit_release(NMSleepMonitor *self, NMSleepMonitorInhibitorHa self->handles_active = g_slist_delete_link(self->handles_active, l); -#if !USE_UPOWER drop_inhibitor(self, FALSE); -#endif } static void -on_proxy_acquired(GObject *object, GAsyncResult *res, NMSleepMonitor *self) +on_proxy_acquired(GObject *object, GAsyncResult *res, NMPowerMonitor *self) { GError *error = NULL; GDBusProxy *proxy; @@ -294,18 +263,6 @@ on_proxy_acquired(GObject *object, GAsyncResult *res, NMSleepMonitor *self) self->proxy = proxy; g_clear_object(&self->cancellable); -#if USE_UPOWER - self->sig_id_1 = _nm_dbus_proxy_signal_connect(self->proxy, - "Sleeping", - NULL, - G_CALLBACK(upower_sleeping_cb), - self); - self->sig_id_2 = _nm_dbus_proxy_signal_connect(self->proxy, - "Resuming", - NULL, - G_CALLBACK(upower_resuming_cb), - self); -#else self->sig_id_1 = g_signal_connect(self->proxy, "notify::g-name-owner", G_CALLBACK(name_owner_cb), self); self->sig_id_2 = _nm_dbus_proxy_signal_connect(self->proxy, @@ -313,6 +270,12 @@ on_proxy_acquired(GObject *object, GAsyncResult *res, NMSleepMonitor *self) G_VARIANT_TYPE("(b)"), G_CALLBACK(prepare_for_sleep_cb), self); + self->sig_id_3 = _nm_dbus_proxy_signal_connect(self->proxy, + "PrepareForShutdown", + G_VARIANT_TYPE("(b)"), + G_CALLBACK(prepare_for_shutdown_cb), + self); + { gs_free char *owner = NULL; @@ -320,13 +283,12 @@ on_proxy_acquired(GObject *object, GAsyncResult *res, NMSleepMonitor *self) if (owner) take_inhibitor(self); } -#endif } /*****************************************************************************/ static void -nm_sleep_monitor_init(NMSleepMonitor *self) +nm_power_monitor_init(NMPowerMonitor *self) { self->inhibit_fd = -1; self->cancellable = g_cancellable_new(); @@ -342,34 +304,33 @@ nm_sleep_monitor_init(NMSleepMonitor *self) self); } -NMSleepMonitor * -nm_sleep_monitor_new(void) +NMPowerMonitor * +nm_power_monitor_new(void) { - return g_object_new(NM_TYPE_SLEEP_MONITOR, NULL); + return g_object_new(NM_TYPE_POWER_MONITOR, NULL); } static void dispose(GObject *object) { - NMSleepMonitor *self = NM_SLEEP_MONITOR(object); + NMPowerMonitor *self = NM_POWER_MONITOR(object); -#if !USE_UPOWER drop_inhibitor(self, TRUE); -#endif nm_clear_g_cancellable(&self->cancellable); if (self->proxy) { nm_clear_g_signal_handler(self->proxy, &self->sig_id_1); nm_clear_g_signal_handler(self->proxy, &self->sig_id_2); + nm_clear_g_signal_handler(self->proxy, &self->sig_id_3); g_clear_object(&self->proxy); } - G_OBJECT_CLASS(nm_sleep_monitor_parent_class)->dispose(object); + G_OBJECT_CLASS(nm_power_monitor_parent_class)->dispose(object); } static void -nm_sleep_monitor_class_init(NMSleepMonitorClass *klass) +nm_power_monitor_class_init(NMPowerMonitorClass *klass) { GObjectClass *gobject_class; @@ -377,8 +338,8 @@ nm_sleep_monitor_class_init(NMSleepMonitorClass *klass) gobject_class->dispose = dispose; - signals[SLEEPING] = g_signal_new(NM_SLEEP_MONITOR_SLEEPING, - NM_TYPE_SLEEP_MONITOR, + signals[SLEEPING] = g_signal_new(NM_POWER_MONITOR_SLEEPING, + NM_TYPE_POWER_MONITOR, G_SIGNAL_RUN_LAST, 0, NULL, @@ -387,4 +348,13 @@ nm_sleep_monitor_class_init(NMSleepMonitorClass *klass) G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + signals[SHUTDOWN] = g_signal_new(NM_POWER_MONITOR_SHUTDOWN, + NM_TYPE_POWER_MONITOR, + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); } diff --git a/src/core/nm-power-monitor.h b/src/core/nm-power-monitor.h new file mode 100644 index 00000000..269bb98a --- /dev/null +++ b/src/core/nm-power-monitor.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2012 - 2016 Red Hat, Inc. + * Author: Matthias Clasen <mclasen@redhat.com> + */ + +#ifndef __NETWORKMANAGER_POWER_MONITOR_H__ +#define __NETWORKMANAGER_POWER_MONITOR_H__ + +#define NM_TYPE_POWER_MONITOR (nm_power_monitor_get_type()) +#define NM_POWER_MONITOR(o) \ + (_NM_G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_POWER_MONITOR, NMPowerMonitor)) +#define NM_POWER_MONITOR_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_POWER_MONITOR, NMPowerMonitorClass)) +#define NM_POWER_MONITOR_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS((o), NM_TYPE_POWER_MONITOR, NMPowerMonitorClass)) +#define NM_IS_POWER_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), NM_TYPE_POWER_MONITOR)) +#define NM_IS_POWER_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), NM_TYPE_POWER_MONITOR)) + +#define NM_POWER_MONITOR_SLEEPING "sleeping" +#define NM_POWER_MONITOR_SHUTDOWN "shutdown" + +typedef struct _NMPowerMonitorClass NMPowerMonitorClass; + +GType nm_power_monitor_get_type(void) G_GNUC_CONST; + +NMPowerMonitor *nm_power_monitor_new(void); + +typedef struct _NMPowerMonitorInhibitorHandle NMPowerMonitorInhibitorHandle; + +NMPowerMonitorInhibitorHandle *nm_power_monitor_inhibit_take(NMPowerMonitor *self); +void nm_power_monitor_inhibit_release(NMPowerMonitor *self, NMPowerMonitorInhibitorHandle *handle); + +#endif /* __NETWORKMANAGER_POWER_MONITOR_H__ */ diff --git a/src/core/nm-sleep-monitor.h b/src/core/nm-sleep-monitor.h deleted file mode 100644 index 0b7708db..00000000 --- a/src/core/nm-sleep-monitor.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2012 - 2016 Red Hat, Inc. - * Author: Matthias Clasen <mclasen@redhat.com> - */ - -#ifndef __NETWORKMANAGER_SLEEP_MONITOR_H__ -#define __NETWORKMANAGER_SLEEP_MONITOR_H__ - -#define NM_TYPE_SLEEP_MONITOR (nm_sleep_monitor_get_type()) -#define NM_SLEEP_MONITOR(o) \ - (_NM_G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_SLEEP_MONITOR, NMSleepMonitor)) -#define NM_SLEEP_MONITOR_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_SLEEP_MONITOR, NMSleepMonitorClass)) -#define NM_SLEEP_MONITOR_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS((o), NM_TYPE_SLEEP_MONITOR, NMSleepMonitorClass)) -#define NM_IS_SLEEP_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), NM_TYPE_SLEEP_MONITOR)) -#define NM_IS_SLEEP_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), NM_TYPE_SLEEP_MONITOR)) - -#define NM_SLEEP_MONITOR_SLEEPING "sleeping" - -typedef struct _NMSleepMonitorClass NMSleepMonitorClass; - -GType nm_sleep_monitor_get_type(void) G_GNUC_CONST; - -NMSleepMonitor *nm_sleep_monitor_new(void); - -typedef struct _NMSleepMonitorInhibitorHandle NMSleepMonitorInhibitorHandle; - -NMSleepMonitorInhibitorHandle *nm_sleep_monitor_inhibit_take(NMSleepMonitor *self); -void nm_sleep_monitor_inhibit_release(NMSleepMonitor *self, NMSleepMonitorInhibitorHandle *handle); - -#endif /* __NETWORKMANAGER_SLEEP_MONITOR_H__ */ diff --git a/src/core/nm-types.h b/src/core/nm-types.h index 104a2f4b..6dfdc8e2 100644 --- a/src/core/nm-types.h +++ b/src/core/nm-types.h @@ -33,7 +33,7 @@ typedef struct _NMRfkillManager NMRfkillManager; typedef struct _NMPacrunnerManager NMPacrunnerManager; typedef struct _NMSessionMonitor NMSessionMonitor; typedef struct _NMKeepAlive NMKeepAlive; -typedef struct _NMSleepMonitor NMSleepMonitor; +typedef struct _NMPowerMonitor NMPowerMonitor; typedef struct _NMLldpListener NMLldpListener; typedef struct _NMConfigDeviceStateData NMConfigDeviceStateData; @@ -96,6 +96,6 @@ typedef struct _NMSecretAgent NMSecretAgent; typedef struct _NMSettings NMSettings; typedef struct _NMSettingsConnection NMSettingsConnection; -#define NM_SETTING_CONNECTION_MDNS_UNKNOWN ((NMSettingConnectionMdns) -42) +#define NM_SETTING_CONNECTION_MDNS_UNKNOWN ((NMSettingConnectionMdns) - 42) #endif /* NM_TYPES_H */ diff --git a/src/core/settings/nm-settings-connection.c b/src/core/settings/nm-settings-connection.c index 176cc2c2..459c60ad 100644 --- a/src/core/settings/nm-settings-connection.c +++ b/src/core/settings/nm-settings-connection.c @@ -226,6 +226,29 @@ static guint _get_seen_bssids(NMSettingsConnection *self, /*****************************************************************************/ +char * +nm_settings_connection_persist_mode_to_string(NMSettingsConnectionPersistMode mode) +{ + switch (mode) { + case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY: + return "in-memory"; + case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED: + return "in-memory-detached"; + case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY: + return "in-memory-only"; + case NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP: + return "keep"; + case NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST: + return "no-persist"; + case NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK: + return "to-disk"; + } + + return nm_assert_unreachable_val(NULL); +} + +/*****************************************************************************/ + NMSettings * nm_settings_connection_get_settings(NMSettingsConnection *self) { diff --git a/src/core/settings/nm-settings-connection.h b/src/core/settings/nm-settings-connection.h index 835a978e..d15a75b7 100644 --- a/src/core/settings/nm-settings-connection.h +++ b/src/core/settings/nm-settings-connection.h @@ -379,4 +379,8 @@ void _nm_settings_connection_emit_signal_updated_internal( void _nm_settings_connection_cleanup_after_remove(NMSettingsConnection *self); +/*****************************************************************************/ + +char *nm_settings_connection_persist_mode_to_string(NMSettingsConnectionPersistMode mode); + #endif /* __NETWORKMANAGER_SETTINGS_CONNECTION_H__ */ diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 3bcbb71b..7c2b2026 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -297,7 +297,7 @@ check_if_bond_slave(shvarFile *ifcfg, NMSettingConnection *s_con) v = svGetValueStr(ifcfg, "MASTER", &value); if (v) { - master = nm_setting_connection_get_master(s_con); + master = nm_setting_connection_get_controller(s_con); if (master) { PARSE_WARNING("Already configured as slave of %s. Ignoring MASTER{_UUID}=\"%s\"", master, @@ -331,7 +331,7 @@ check_if_team_slave(shvarFile *ifcfg, NMSettingConnection *s_con) if (!v) return; - master = nm_setting_connection_get_master(s_con); + master = nm_setting_connection_get_controller(s_con); if (master) { PARSE_WARNING("Already configured as slave of %s. Ignoring TEAM_MASTER{_UUID}=\"%s\"", master, @@ -507,7 +507,7 @@ make_connection_setting(const char *file, if (v) { const char *old_value; - if ((old_value = nm_setting_connection_get_master(s_con))) { + if ((old_value = nm_setting_connection_get_controller(s_con))) { PARSE_WARNING("Already configured as slave of %s. Ignoring BRIDGE=\"%s\"", old_value, v); @@ -530,7 +530,7 @@ make_connection_setting(const char *file, if (v) { const char *old_value; - if ((old_value = nm_setting_connection_get_master(s_con))) { + if ((old_value = nm_setting_connection_get_controller(s_con))) { PARSE_WARNING("Already configured as slave of %s. Ignoring OVS_PORT=\"%s\"", old_value, v); @@ -550,7 +550,7 @@ make_connection_setting(const char *file, if (v) { const char *old_value; - if ((old_value = nm_setting_connection_get_master(s_con))) { + if ((old_value = nm_setting_connection_get_controller(s_con))) { PARSE_WARNING("Already configured as slave of %s. Ignoring VRF{_UUID}=\"%s\"", old_value, v); @@ -4129,6 +4129,10 @@ next: v = svGetValueStr(ifcfg, "IEEE_8021X_PHASE2_CA_PATH", &value); g_object_set(s_8021x, NM_SETTING_802_1X_PHASE2_CA_PATH, v, NULL); + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IEEE_8021X_OPENSSL_CIPHERS", &value); + g_object_set(s_8021x, NM_SETTING_802_1X_OPENSSL_CIPHERS, v, NULL); + g_object_set(s_8021x, NM_SETTING_802_1X_OPTIONAL, svGetValueBoolean(ifcfg, "IEEE_8021X_OPTIONAL", FALSE), diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c index b4edefbb..277d0d5f 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c @@ -828,6 +828,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = { _KEY_TYPE("IEEE_8021X_INNER_PRIVATE_KEY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_OPENSSL_CIPHERS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IEEE_8021X_OPTIONAL", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IEEE_8021X_PAC_FILE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), _KEY_TYPE("IEEE_8021X_PASSWORD", NMS_IFCFG_KEY_TYPE_IS_PLAIN), diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h index eb9e418a..4e56a258 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h @@ -33,7 +33,7 @@ typedef struct { NMSIfcfgKeyTypeFlags key_flags; } NMSIfcfgKeyTypeInfo; -extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[264]; +extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[265]; const NMSIfcfgKeyTypeInfo *nms_ifcfg_well_known_key_find_info(const char *key, gssize *out_idx); diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index 617c5ef6..f4598e2d 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -579,6 +579,10 @@ write_8021x_setting(NMConnection *connection, "IEEE_8021X_PIN_FLAGS", nm_setting_802_1x_get_pin_flags(s_8021x)); + svSetValueStr(ifcfg, + "IEEE_8021X_OPENSSL_CIPHERS", + nm_setting_802_1x_get_openssl_ciphers(s_8021x)); + if (!write_8021x_certs(s_8021x, secrets, blobs, FALSE, ifcfg, error)) return FALSE; @@ -1138,7 +1142,7 @@ write_wired_setting_impl(NMSettingWired *s_wired, shvarFile *ifcfg, gboolean is_ "GENERATE_MAC_ADDRESS_MASK", nm_setting_wired_get_generate_mac_address_mask(s_wired)); - macaddr_blacklist = nm_setting_wired_get_mac_address_blacklist(s_wired); + macaddr_blacklist = nm_setting_wired_get_mac_address_denylist(s_wired); if (macaddr_blacklist[0]) { gs_free char *blacklist_str = NULL; @@ -2247,7 +2251,7 @@ write_connection_setting(NMSettingConnection *s_con, shvarFile *ifcfg, const cha mud_url = nm_setting_connection_get_mud_url(s_con); svSetValue(ifcfg, "MUD_URL", mud_url); - master = nm_setting_connection_get_master(s_con); + master = nm_setting_connection_get_controller(s_con); if (master) { /* The reader prefers the *_UUID variants, however we still try to resolve * it into an interface name, so that legacy tooling is not confused. */ diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft-dhcp b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft-dhcp new file mode 100644 index 00000000..abfcd6e4 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft-dhcp @@ -0,0 +1,4 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +DEVICE=eth0 +HWADDR=00:33:21:98:b9:f1 +BOOTPROTO=ibft diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft-static b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft-static new file mode 100644 index 00000000..99b02e42 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft-static @@ -0,0 +1,4 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +DEVICE=eth0 +HWADDR=00:33:21:98:b9:f0 +BOOTPROTO=ibft diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2 index 27bcbbf9..f32f234a 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2 +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2 @@ -13,3 +13,4 @@ IEEE_8021X_PEAP_VERSION=1 IEEE_8021X_PEAP_FORCE_NEW_LABEL=yes IEEE_8021X_INNER_AUTH_METHODS=MSCHAPV2 IEEE_8021X_ANON_IDENTITY=somebody +IEEE_8021X_OPENSSL_CIPHERS=DEFAULT@SECLEVEL=0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 1e6b6c25..40a18853 100644 --- a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -2230,7 +2230,7 @@ test_clear_master(void) s_con = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_CONNECTION); - g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "br0"); + g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "br0"); g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, "bridge"); /* 2. write the connection to a new file */ @@ -2247,7 +2247,7 @@ test_clear_master(void) NULL, NULL); - g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, NULL); + g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, NULL); g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NULL); nmtst_assert_connection_verifies_after_normalization(connection, 0, 0); @@ -5317,6 +5317,8 @@ test_write_wired_dhcp_8021x_peap_mschapv2(void) "1", NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", + NM_SETTING_802_1X_OPENSSL_CIPHERS, + "DEFAULT@SECLEVEL=0", NULL); nm_setting_802_1x_add_eap_method(s_8021x, "peap"); @@ -7668,7 +7670,7 @@ test_read_bridge_component(void) NULL); s_con = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_CONNECTION); - g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "br0"); + g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "br0"); g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BRIDGE_SETTING_NAME); s_port = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_BRIDGE_PORT); @@ -8260,7 +8262,7 @@ test_read_bond_slave(void) s_con = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_CONNECTION); - g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "bond0"); + g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "bond0"); g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BOND_SETTING_NAME); } @@ -8317,7 +8319,7 @@ test_read_bond_port(void) _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-bond-port", NULL, TYPE_ETHERNET, NULL); s_con = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_CONNECTION); - g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "bond99"); + g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "bond99"); g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BOND_SETTING_NAME); s_port = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_BOND_PORT); @@ -8556,7 +8558,7 @@ test_read_bond_slave_ib(void) s_con = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_CONNECTION); - g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "bond0"); + g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "bond0"); g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BOND_SETTING_NAME); } @@ -9096,7 +9098,7 @@ test_read_team_port(gconstpointer user_data) g_assert_cmpstr(nm_setting_connection_get_connection_type(s_con), ==, NM_SETTING_WIRED_SETTING_NAME); - g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "team0"); + g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "team0"); s_team_port = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_TEAM_PORT); g_assert_cmpstr(nm_setting_team_port_get_config(s_team_port), ==, expected_config); @@ -9224,7 +9226,7 @@ test_read_team_port_empty_config(void) g_assert_cmpstr(nm_setting_connection_get_connection_type(s_con), ==, NM_SETTING_WIRED_SETTING_NAME); - g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "team0"); + g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "team0"); /* Normalization adds a team-port setting */ s_team_port = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_TEAM_PORT); diff --git a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c index b26bad69..e3e5f84f 100644 --- a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c +++ b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c @@ -91,7 +91,7 @@ assert_reread(NMConnection *connection, gboolean normalize_connection, const cha reread = keyfile_read_connection_from_file(testfile); if (!normalize_connection && (s_con = nm_connection_get_setting_connection(connection)) - && !nm_setting_connection_get_master(s_con) + && !nm_setting_connection_get_controller(s_con) && !nm_connection_get_setting_proxy(connection)) { connection_clone = nmtst_clone_connection(connection); connection = connection_clone; @@ -2003,7 +2003,7 @@ test_read_bridge_component(void) g_assert(s_con); g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, expected_id); g_assert_cmpstr(nm_setting_connection_get_uuid(s_con), ==, expected_uuid); - g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "br0"); + g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "br0"); g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BRIDGE_SETTING_NAME); s_wired = nm_connection_get_setting_wired(connection); diff --git a/src/core/supplicant/nm-supplicant-config.c b/src/core/supplicant/nm-supplicant-config.c index 9ad4a8f9..7db614a2 100644 --- a/src/core/supplicant/nm-supplicant-config.c +++ b/src/core/supplicant/nm-supplicant-config.c @@ -1811,6 +1811,9 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self, value = nm_setting_802_1x_get_anonymous_identity(setting); if (!add_string_val(self, value, "anonymous_identity", FALSE, NULL, error)) return FALSE; + value = nm_setting_802_1x_get_openssl_ciphers(setting); + if (value && !add_string_val(self, value, "openssl_ciphers", FALSE, NULL, error)) + return FALSE; return TRUE; } diff --git a/src/core/supplicant/nm-supplicant-settings-verify.c b/src/core/supplicant/nm-supplicant-settings-verify.c index 7842365c..c7aaf47d 100644 --- a/src/core/supplicant/nm-supplicant-settings-verify.c +++ b/src/core/supplicant/nm-supplicant-settings-verify.c @@ -93,6 +93,7 @@ static const struct Opt opt_table[] = { OPT_BYTES("mka_cak", 65536), OPT_BYTES("mka_ckn", 65536), OPT_BYTES("nai", 0), + OPT_BYTES("openssl_ciphers", 0), OPT_INT("owe_only", 0, 1), OPT_BYTES("pac_file", 0), OPT_KEYWORD("pairwise", NM_MAKE_STRV("CCMP", "TKIP", "GCMP-256", "NONE", )), diff --git a/src/core/tests/config/test-config.c b/src/core/tests/config/test-config.c index 054b9003..2b27d535 100644 --- a/src/core/tests/config/test-config.c +++ b/src/core/tests/config/test-config.c @@ -1076,7 +1076,7 @@ _set_values_intern_atomic_section_2_set(NMConfig *config, g_key_file_set_value(keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN "with-whitespace", "key2", - " b c\\, d "); + " b c\\\\, d "); *out_expected_changes = NM_CONFIG_CHANGE_CAUSE_SET_VALUES | NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_INTERN; } diff --git a/src/core/tests/test-dcb.c b/src/core/tests/test-dcb.c index f85e90f9..33437974 100644 --- a/src/core/tests/test-dcb.c +++ b/src/core/tests/test-dcb.c @@ -11,7 +11,7 @@ typedef struct { guint num; - const char *cmds[]; + const char *cmds[16]; } DcbExpected; static gboolean diff --git a/src/core/vpn/nm-vpn-connection.c b/src/core/vpn/nm-vpn-connection.c index 3dba9ff6..de0c9f71 100644 --- a/src/core/vpn/nm-vpn-connection.c +++ b/src/core/vpn/nm-vpn-connection.c @@ -2099,20 +2099,12 @@ _dbus_signal_ip_config_cb(NMVpnConnection *self, int addr_family, GVariant *dict NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)) nm_l3_config_data_add_route(l3cd, addr_family, route, NULL); } - } else if (IS_IPv4 ? g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, "aau", &var_iter) - : g_variant_lookup(dict, - NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, - "a(ayuayu)", - &var_iter)) { - _nm_unused nm_auto_free_variant_iter GVariantIter *var_iter_ref_owner = var_iter; - NMPlatformIPXRoute route = {}; - guint32 plen; - GVariant *next_hop; - GVariant *dest; - guint32 prefix; - guint32 metric; + } else if (IS_IPv4) { + if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, "aau", &var_iter)) { + _nm_unused nm_auto_free_variant_iter GVariantIter *var_iter_ref_owner = var_iter; + NMPlatformIPXRoute route = {}; + guint32 plen; - if (IS_IPv4) { while (g_variant_iter_next(var_iter, "@au", &v)) { _nm_unused gs_unref_variant GVariant *v_ref_owner = v; @@ -2151,42 +2143,84 @@ _dbus_signal_ip_config_cb(NMVpnConnection *self, int addr_family, GVariant *dict break; } } - } else { - while ( - g_variant_iter_next(var_iter, "(@ayu@ayu)", &dest, &prefix, &next_hop, &metric)) { - _nm_unused gs_unref_variant GVariant *next_hop_ref_owner = next_hop; - _nm_unused gs_unref_variant GVariant *dest_ref_owner = dest; + } + } else { + _nm_unused nm_auto_free_variant_iter GVariantIter *var_iter_ref_owner = NULL; + NMPlatformIPXRoute route = {}; + guint32 prefix; + guint32 metric; + NMOptionBool new_signature = NM_OPTION_BOOL_DEFAULT; + + /* IPv6 and no "preserve-routes" */ + + if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, "a(ayuayu)", &var_iter)) + new_signature = FALSE; + else if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, "a(ayuayuay)", &var_iter)) + new_signature = TRUE; + else + var_iter = NULL; + + var_iter_ref_owner = var_iter; + + while (TRUE) { + gs_unref_variant GVariant *next_hop = NULL; + gs_unref_variant GVariant *dest = NULL; + gs_unref_variant GVariant *pref_src = NULL; + + if (new_signature == NM_OPTION_BOOL_DEFAULT) { + break; + } else if (new_signature) { + if (!g_variant_iter_next(var_iter, + "(@ayu@ayu@ay)", + &dest, + &prefix, + &next_hop, + &metric, + &pref_src)) + break; + } else { + if (!g_variant_iter_next(var_iter, + "(@ayu@ayu)", + &dest, + &prefix, + &next_hop, + &metric)) + break; + } - if (prefix > 128) - continue; + if (prefix > 128) + continue; - route.r6 = (NMPlatformIP6Route){ - .plen = prefix, - .table_any = TRUE, - .metric_any = TRUE, - .rt_source = NM_IP_CONFIG_SOURCE_VPN, - }; + route.r6 = (NMPlatformIP6Route){ + .plen = prefix, + .table_any = TRUE, + .metric_any = TRUE, + .rt_source = NM_IP_CONFIG_SOURCE_VPN, + }; - if (!nm_ip_addr_set_from_variant(AF_INET6, &route.r6.network, dest, NULL)) - continue; + if (!nm_ip_addr_set_from_variant(AF_INET6, &route.r6.network, dest, NULL)) + continue; - nm_ip_addr_set_from_variant(AF_INET6, &route.r6.gateway, next_hop, NULL); + if (pref_src + && !nm_ip_addr_set_from_variant(AF_INET6, &route.r6.pref_src, pref_src, NULL)) + continue; - nm_ip6_addr_clear_host_address(&route.r6.network, &route.r6.network, route.r6.plen); + nm_ip_addr_set_from_variant(AF_INET6, &route.r6.gateway, next_hop, NULL); - if (!IN6_IS_ADDR_UNSPECIFIED(&priv->ip_data_6.gw_external.addr6) - && IN6_ARE_ADDR_EQUAL(&route.r6.network, &priv->ip_data_6.gw_external.addr6) - && route.r6.plen == 128) { - /* Ignore host routes to the VPN gateway since NM adds one itself. - * Since NM knows more about the routing situation than the VPN - * server, we want to use the NM created route instead of whatever - * the server provides. - */ - continue; - } + nm_ip6_addr_clear_host_address(&route.r6.network, &route.r6.network, route.r6.plen); - nm_l3_config_data_add_route_6(l3cd, &route.r6); + if (!IN6_IS_ADDR_UNSPECIFIED(&priv->ip_data_6.gw_external.addr6) + && IN6_ARE_ADDR_EQUAL(&route.r6.network, &priv->ip_data_6.gw_external.addr6) + && route.r6.plen == 128) { + /* Ignore host routes to the VPN gateway since NM adds one itself. + * Since NM knows more about the routing situation than the VPN + * server, we want to use the NM created route instead of whatever + * the server provides. + */ + continue; } + + nm_l3_config_data_add_route_6(l3cd, &route.r6); } } |