]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #17238 from keszybz/man-tmp-noexec
authorLennart Poettering <lennart@poettering.net>
Wed, 7 Oct 2020 07:45:58 +0000 (09:45 +0200)
committerGitHub <noreply@github.com>
Wed, 7 Oct 2020 07:45:58 +0000 (09:45 +0200)
Say that noexec should not be used for /tmp

107 files changed:
NEWS
README.md
TODO
coccinelle/equals-null.cocci
coccinelle/flags-set.cocci
coccinelle/in_set.cocci
coccinelle/not_in_set.cocci
coccinelle/run-coccinelle.sh
coccinelle/strjoin.cocci
coccinelle/systemd-definitions.iso [deleted file]
coccinelle/xsprintf.cocci
man/homectl.xml
man/logind.conf.xml
man/portablectl.xml
man/sd_bus_default.xml
man/sd_event_add_inotify.xml
man/systemd-detect-virt.xml
man/systemd.exec.xml
man/systemd.netdev.xml
man/systemd.service.xml
man/systemd.unit.xml
man/udev.conf.xml
shell-completion/bash/meson.build
src/basic/rm-rf.c
src/basic/terminal-util.c
src/basic/virt.c
src/basic/virt.h
src/boot/efi/measure.c
src/core/unit.c
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/netlink-util.h
src/network/meson.build
src/network/netdev/macsec.c
src/network/netdev/macsec.h
src/network/networkd-address-label.c
src/network/networkd-address-label.h
src/network/networkd-address-pool.c
src/network/networkd-address-pool.h
src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-brvlan.c
src/network/networkd-brvlan.h
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-common.h
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp4.c
src/network/networkd-dhcp4.h
src/network/networkd-dhcp6.c
src/network/networkd-dhcp6.h
src/network/networkd-fdb.c
src/network/networkd-fdb.h
src/network/networkd-ipv4ll.c
src/network/networkd-ipv4ll.h
src/network/networkd-ipv6-proxy-ndp.c
src/network/networkd-ipv6-proxy-ndp.h
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-lldp-rx.c
src/network/networkd-lldp-rx.h
src/network/networkd-lldp-tx.c
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-mdb.c
src/network/networkd-mdb.h
src/network/networkd-ndisc.c
src/network/networkd-ndisc.h
src/network/networkd-neighbor.c
src/network/networkd-neighbor.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-nexthop.c
src/network/networkd-nexthop.h
src/network/networkd-radv.c
src/network/networkd-radv.h
src/network/networkd-route.c
src/network/networkd-route.h
src/network/networkd-routing-policy-rule.c
src/network/networkd-routing-policy-rule.h
src/network/networkd-sriov.c
src/network/networkd-sriov.h
src/network/networkd-sysctl.c [new file with mode: 0644]
src/network/networkd-sysctl.h [new file with mode: 0644]
src/network/networkd-util.c
src/network/networkd-util.h
src/network/networkd-wifi.c
src/network/networkd.c
src/network/tc/tc.c
src/network/tc/tc.h
src/network/test-network.c
src/network/test-networkd-conf.c
src/network/test-routing-policy-rule.c
src/nspawn/nspawn-setuid.c
src/nspawn/nspawn-setuid.h
src/nspawn/nspawn.c
src/shared/calendarspec.c
src/shared/chown-recursive.c
src/shared/ptyfwd.c
src/shared/seccomp-util.c
src/test/test-namespace.c
src/udev/scsi_id/scsi_id.c
src/udev/udevadm-trigger.c
test/test-network/conf/25-address-static.network
test/test-network/conf/25-sriov.network
test/test-network/systemd-networkd-tests.py

diff --git a/NEWS b/NEWS
index 99194b02d0b068f3fd522eb9384925f510032881..d4afd85cf978d3a3c43b53b3dddbb38910a01e9b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,20 +2,21 @@ systemd System and Service Manager
 
 CHANGES WITH 247 in spe:
 
-        * KERNEL API INCOMPATIBILTY: Linux 4.12 introduced two new uevents
+        * KERNEL API INCOMPATIBILITY: Linux 4.12 introduced two new uevents
           "bind" and "unbind" to the Linux device model. When this kernel
           change was made, systemd-udevd was only minimally updated to handle
           and propagate these new event types. The introduction of these new
           uevents (which are typically generated for USB devices and devices
           needing a firmware upload before being functional) resulted in a
-          number of software issues, we so far didn't address (mostly because
-          there was hope the kernel maintainers would themeselves address these
-          issues in some form – which did not happen). To handle them properly,
-          many (if not most) udev rules files shipped in various packages need
-          updating, and so do many programs that monitor or enumerate devices
-          with libudev or sd-device, or otherwise process uevents. Please note
-          that this incompatibility is not fault of systemd or udev, but caused
-          by an incompatible kernel change that happened back in Linux 4.12.
+          number of issues which we so far didn't address. We hoped the kernel
+          maintainers would themselves address these issues in some form, but
+          that did not happen. To handle them properly, many (if not most) udev
+          rules files shipped in various packages need updating, and so do many
+          programs that monitor or enumerate devices with libudev or sd-device,
+          or otherwise process uevents. Please note that this incompatibility
+          is not fault of systemd or udev, but caused by an incompatible kernel
+          change that happened back in Linux 4.12, but is becoming more and
+          more visible as the new uvents are generated by more kernel drivers.
 
           To minimize issues resulting from this kernel change (but not avoid
           them entirely) starting with systemd-udevd 247 the udev "tags"
@@ -40,8 +41,8 @@ CHANGES WITH 247 in spe:
           device. To accommodate for this a new automatic property CURRENT_TAGS
           has been added that works similar to the existing TAGS property but
           only lists tags set by the most recent uevent/database
-          update. Similar, the libudev/sd-device API has been updated with new
-          functions to enumerate these 'current' tags, in addition to the
+          update. Similarly, the libudev/sd-device API has been updated with
+          new functions to enumerate these 'current' tags, in addition to the
           existing APIs that now enumerate the 'sticky' ones.
 
           To properly handle "bind"/"unbind" on Linux 4.12 and newer it is
@@ -53,12 +54,12 @@ CHANGES WITH 247 in spe:
             ACTION=="remove",GOTO="xyz_end" instead, so that the
             properties/tags they add are also applied whenever "bind" (or
             "unbind") is seen. (This is most important for all physical device
-            types — as that's for which "bind" and "unbind" are currently
-            usually generated, for all other device types this change is still
+            types — those for which "bind" and "unbind" are currently
+            generated, for all other device types this change is still
             recommended but not as important — but certainly prepares for
             future kernel uevent type additions).
 
-          • Similar, all code monitoring devices that contains an 'if' branch
+          • Similarly, all code monitoring devices that contains an 'if' branch
             discerning the "add" + "change" uevent actions from all other
             uevents actions (i.e. considering devices only relevant after "add"
             or "change", and irrelevant on all other events) should be reworked
@@ -74,8 +75,8 @@ CHANGES WITH 247 in spe:
           • Any code that uses device tags for deciding whether a device is
             relevant or not most likely needs to be updated to use the new
             udev_device_has_current_tag() API (or sd_device_has_current_tag()
-            in case sd-device is used), to check whether the tag is set
-            at the moment an uevent is seen (as opposed to the existing
+            in case sd-device is used), to check whether the tag is set at the
+            moment an uevent is seen (as opposed to the existing
             udev_device_has_tag() API which checks if the tag ever existed on
             the device, following the API concept redefinition explained
             above).
@@ -85,6 +86,12 @@ CHANGES WITH 247 in spe:
           this is not caused by systemd/udev changes, but result of a kernel
           behaviour change.
 
+        * The MountAPIVFS= service file setting now defaults to on if
+          RootImage= and RootDirectory= are used, which means that with those
+          two settings /proc/, /sys/ and /dev/ are automatically properly set
+          up for services. Previous behaviour may be restored by explicitly
+          setting MountAPIVFS=off.
+
         * Since PAM 1.2.0 (2015) configuration snippets may be placed in
           /usr/lib/pam.d/ in addition to /etc/pam.d/. If a file exists in the
           latter it takes precedence over the former, similar to how most of
@@ -97,9 +104,374 @@ CHANGES WITH 247 in spe:
           packages' vendor versions of their PAM stack definitions from
           /etc/pam.d/ to /usr/lib/pam.d/, but if such OS-wide migration is not
           desired the location to which systemd installs its PAM stack
-          configuration file may be changed via the "pamconfdir" meson variable
-          at build time, optionally undoing this change of default paths
-          introduced with systemd 247.
+          configuration may be changed via the -Dpamconfdir Meson option.
+
+        * The runtime dependencies on libqrencode, libpcre2, libpwquality and
+          libcryptsetup have been changed to be based on dlopen(): instead of
+          regular dynamic library dependencies declared in the binary ELF
+          headers, these libraries are now loaded on demand only, if they are
+          available. If the libraries cannot be found the relevant operations
+          will fail gracefully, or a suitable fallback logic is chosen. This is
+          supposed to be useful for general purpose distributions, as it allows
+          minimizing the list of dependencies the systemd packages pull in,
+          permitting building of more minimal OS images, while still making use
+          of these "weak" dependencies should they be installed. Since many
+          package managers automatically synthesize package dependencies from
+          ELF shared library dependencies, some additional manual packaging
+          work has to be done now to replace those (slightly downgraded from
+          "required" to "recommended" or whatever is conceptually suitable for
+          the package manager). Note that this change does not alter build-time
+          behaviour: as before the build-time dependencies have to be installed
+          during build, even if they now are optional during runtime.
+
+        * sd-event.h gained a new call sd_event_add_time_relative() for
+          installing timers relative to the current time. This is mostly a
+          convenience wrapper around the pre-existing sd_event_add_time() call
+          which installs absolute timers.
+
+        * A new per-unit setting RootImageOptions= has been added which allows
+          tweaking the mount options for any file system mounted as effect of
+          the RootImage= setting.
+
+        * Another new per-unit setting MountImages= has been added, that allows
+          mounting additional disk images into the file system tree accessible
+          to the service.
+
+        * systemd-repart now generates JSON output when requested with the new
+          --json= switch.
+
+        * systemd-machined's OpenMachineShell() bus call will now pass
+          additional policy metadata data fields to the PolicyKit
+          authentication request.
+
+        * systemd-tmpfiles gained a new -E switch, which is equivalent to
+          --exclude-prefix=/dev --exclude-prefix=/proc --exclude=/run
+          --exclude=/sys. It's particularly useful in combination with --root=,
+          when operating on OS trees that do not have any of these four runtime
+          directories mounted, as this means no files below these subtrees are
+          created or modified, since those mount points should probably remain
+          empty.
+
+        * systemd-tmpfiles gained a new --image= switch which is like --root=,
+          but takes a disk image instead of a directory as argument. The
+          specified disk image is mounted inside a temporary mount namespace
+          and the tmpfiles.d/ drop-ins stored in the image are executed and
+          applied to the image. systemd-sysusers similarly gained a new
+          --image= switch, that allows the sysusers.d/ drop-ins stored in the
+          image to be applied onto the image.
+
+        * Similarly, the journalctl command also gained an --image= switch,
+          which is a quick one-step solution to look at the log data included
+          in OS disk images.
+
+        * journalctl's --output=cat option (which outputs the log content
+          without any metadata, just the pure text messages) will now make use
+          of terminal colors when run on a suitable terminal, similarly to the
+          other output modes.
+
+        * JSON group records now support a "description" string that may be
+          used to add a human-readable textual description to such groups. This
+          is supposed to match the user's GECOS field which traditionally
+          didn't have a counterpart for group records.
+
+        * The "systemd-dissect" tool that may be used to inspect OS disk images
+          and that was previously installed to /usr/lib/systemd/ has now been
+          moved to /usr/bin/, reflecting its updated status of an officially
+          supported tool with a stable interface. It gained support for a new
+          --mkdir switch which when combined with --mount has the effect of
+          creating the directory to mount the image to if it is missing
+          first. It also gained two new commands --copy-from and --copy-to for
+          copying files and directories in and out of an OS image without the
+          need to manually mount it. It also acquired support for a new option
+          --json= to generate JSON output when inspecting an OS image.
+
+        * The cgroup2 file system is now mounted with the
+          "memory_recursiveprot" mount option, supported since kernel 5.7. This
+          means that the MemoryLow= and MemoryMin= unit file settings now apply
+          recursively to whole subtrees.
+
+        * systemd-homed now defaults to using the btrfs file system — if
+          available — when creating home directories in LUKS volumes. This may
+          be changed with the DefaultFileSystemType= setting in homed.conf.
+          It's now the default file system in various major distributions and
+          has the major benefit for homed that it can be grown and shrunk while
+          mounted, unlike the other contenders ext4 and xfs, which can both be
+          grown online, but not shrunk (in fact xfs is the technically most
+          limited option here, as it cannot be shrunk at all).
+
+        * JSON user records managed by systemd-homed gained support for
+          "recovery keys". These are basically secondary passphrases that can
+          unlock user accounts/home directories. They are computer-generated
+          rather than user-chosen, and typically have greater entropy.
+          homectl's --recovery-key= option may be used to add a recovery key to
+          a user account. The generated recovery key is displayed as a QR code,
+          so that it can be scanned to be kept in a safe place. This feature is
+          particularly useful in combination with systemd-homed's support for
+          FIDO2 or PKCS#11 authentication, as a secure fallback in case the
+          security tokens are lost. Recovery keys may be entered wherever the
+          system asks for a password.
+
+        * systemd-homed now maintains a "dirty" flag for each LUKS encrypted
+          home directory which indicates that a home directory has not been
+          deactivated cleanly when offline. This flag is useful to identify
+          home directories for which the offline discard logic did not run when
+          offlining, and where it would be a good idea to log in again to catch
+          up.
+
+        * systemctl gained a new parameter --timestamp= which may be used to
+          change the style in which timestamps are output, i.e. whether to show
+          them in local timezone or UTC, or whether to show µs granularity.
+
+        * Alibaba's "pouch" container manager is now detected by
+          systemd-detect-virt, ConditionVirtualization= and similar constructs.
+
+        * systemd-nspawn has been reworked to use the /run/host/incoming/ as
+          place to use for propagating external mounts into the
+          container. Similarly /run/host/notify is now used as the socket path
+          for container payloads to communicate with the container manager
+          using sd_notify(). The container manager now uses the
+          /run/host/inaccessible/ directory to place "inaccessible" file nodes
+          of all relevant types which may be used by the container payload as
+          bind mount source to over-mount inodes to make them inaccessible.
+          /run/host/container-manager will now be initialized with the same
+          string as the $container environment variable passed to the
+          container's PID 1. /run/host/container-uuid will be initialized with
+          the same string as $container_uuid. This means the /run/host/
+          hierarchy is now the primary way to make host resources available to
+          the container. The Container Interface documents these new files and
+          directories:
+
+          https://systemd.io/CONTAINER_INTERFACE
+
+        * Support for the "ConditionNull=" unit file condition has been
+          deprecated and undocumented for 6 years. systemd started to warn
+          about its use 1.5 years ago. It has now been removed entirely.
+
+        * If the $SYSTEMD_LOG_SECCOMP=1 environment variable is set for
+          systemd-nspawn all system call filter violations will be logged by
+          the kernel (audit). This is useful for tracking down system calls
+          invoked by container payloads that are prohibited by the container's
+          system call filter policy.
+
+        * sd-bus.h gained a new API call sd_bus_error_has_names(), which takes
+          a sd_bus_error struct and a list of error names, and checks if the
+          error matches one of these names. It's a convenience wrapper that is
+          useful in cases where multiple errors shall be handled the same way.
+
+        * A new system call filter list "@known" has been added, that contains
+          all system calls known at the time systemd was built.
+
+        * Behaviour of system call filter allow lists has changed slightly:
+          system calls that are contained in @known will result in a EPERM by
+          default, while those not contained in it result in ENOSYS. This
+          should improve compatibility because known syscalls will thus be
+          communicated as prohibited, while unknown (and thus newer ones) will
+          be communicated as not implemented, which hopefully has the greatest
+          chance of triggering the right fallback code paths in client
+          applications.
+
+        * Two new unit file settings ProtectProc= and ProcSubset= have been
+          added that expose the hidepid= and subset= mount options of procfs.
+          All processes of the unit will only see processes in /proc that are
+          are owned by the unit's user. This is an important new sandboxing
+          option that is recommended to be set on all system services. All
+          long-running system services that are included in systemd itself set
+          this option now. This option is only supported on kernel 5.8 and
+          above, since the hidepid= option supported on older kernels was not a
+          per-mount option but actually applied to the whole PID namespace.
+
+        * Socket units gained a new boolean setting FlushPending=. If enabled
+          all pending socket data/connections are flushed whenever the socket
+          unit enters the "listening" state, i.e. after the associated service
+          exited.
+
+        * The unit file setting NUMAMask= gained a new "all" value: when used,
+          all existing NUMA nodes are added to the NUMA mask.
+
+        * A new "credentials" logic has been added to system services. This is
+          a simple mechanism to pass privileged data to services in a safe and
+          secure way. It's supposed to be used to pass per-service secret data
+          such as passwords or cryptographic keys but also associated less
+          private information such as user names, certificates, and similar to
+          system services. Each credential is identified by a short user-chosen
+          name and may contain arbitrary binary data. Two new unit file
+          settings have been added: SetCredential= and LoadCredential=. The
+          former allows setting a credential to a literal string, the latter
+          sets a credential to the contents of a file (or data read from a
+          user-chosen AF_UNIX stream socket). Credentials are passed to the
+          service via a special credentials directory, one file for each
+          credential. The path to the credentials directory is passed in a new
+          $CREDENTIALS_DIRECTORY environment variable. Since the credentials
+          are passed in the file system they may be easily referenced in
+          ExecStart= command lines too, thus no explicit support for the
+          credentials logic in daemons is required (though ideally daemons
+          would look for the bits they need in $CREDENTIALS_DIRECTORY
+          themselves automatically, if set). The $CREDENTIALS_DIRECTORY is
+          backed by unswappable memory if privileges allow it, immutable if
+          privileges allow it, is accessible only to the service's UID, and is
+          automatically destroyed when the service stops.
+
+        * systemd-nspawn supports the same credentials logic. It can both
+          consume credentials passed to it via the aforementioned
+          $CREDENTIALS_DIRECTORY protocol as well as pass these credentials on
+          to its payload. The service manager/PID 1 has been updated to match
+          this: it can also accept credentials from the container manager that
+          invokes it (in fact: any process that invokes it), and passes them on
+          to its services. Thus, credentials can be propagated recursively down
+          the tree: from a system's service manager to a systemd-nspawn
+          service, to the service manager that runs as container payload and to
+          the service it runs below. Credentials may also be added on the
+          systemd-nspawn command line, using new --set-credential= and
+          --load-credential= command line switches that match the
+          aforementioned service settings.
+
+        * systemd-repart gained new settings Format=, Encrypt=, CopyFiles= in
+          the partition drop-ins which may be used to format/LUKS
+          encrypt/populate any created partitions. The partitions are
+          encrypted/formatted/populated before they are registered in the
+          partition table, so that they appear atomically: either the
+          partitions do not exist yet or they exist fully encrypted, formatted,
+          and populated — there is no time window where they are
+          "half-initialized". Thus the system is robust to abrupt shutdown: if
+          the tool is terminated half-way during its operations on next boot it
+          will start from the beginning.
+
+        * systemd-repart's --size= operation gained a new "auto" value. If
+          specified, and operating on a loopback file it is automatically sized
+          to the minimal size the size constraints permit. This is useful to
+          use "systemd-repart" as an image builder for minimally sized images.
+
+        * systemd-resolved now gained a third IPC interface for requesting name
+          resolution: besides D-Bus and local DNS to 127.0.0.53 a Varlink
+          interface is now supported. The nss-resolve NSS module has been
+          modified to use this new interface instead of D-Bus. Using Varlink
+          has a major benefit over D-Bus: it works without a broker service,
+          and thus already during earliest boot, before the dbus daemon has
+          been started. This means name resolution via systemd-resolved now
+          works at the same time systemd-networkd operates: from earliest boot
+          on, including in the initrd.
+
+        * systemd-resolved gained support for a new DNSStubListenerExtra=
+          configuration file setting which may be used to specify additional IP
+          addresses the built-in DNS stub shall listen on, in addition to the
+          main one on 127.0.0.53:53.
+
+        * Name lookups issued via systemd-resolved's D-Bus and Varlink
+          interfaces (and thus also via glibc NSS if nss-resolve is used) will
+          now honour a trailing dot in the hostname: if specified the search
+          path logic is turned off. Thus "resolvectl query foo." is now
+          equivalent to "resolvectl query --search=off foo.".
+
+        * systemd-resolved gained a new D-Bus property "ResolvConfMode" that
+          exposes how /etc/resolv.conf is currently managed: by resolved (and
+          in which mode if so) or another subsystem. "resolvctl" will display
+          this property in its status output.
+
+        * The resolv.conf snippets systemd-resolved provides will now set "."
+          as the search domain if no other search domain is known. This turns
+          off the derivation of an implicit search domain by nss-dns for the
+          hostname, when the hostname is set to an FQDN. This change is done to
+          make nss-dns using resolv.conf provided by systemd-resolved behave
+          more similarly to nss-resolve.
+
+        * systemd-tmpfiles' file "aging" logic (i.e. the automatic clean-up of
+          /tmp/ and /var/tmp/ based on file timestamps) now looks at the
+          "birth" time (btime) of a file in addition to the atime, mtime, and
+          ctime.
+
+        * systemd-analyze gained a new verb "capability" that lists all known
+          capabilities by the systemd build and by the kernel.
+
+        * If a file /usr/lib/clock-epoch exists, PID 1 will read its mtime and
+          advance the system clock to it at boot if it is noticed to be before
+          that time. Previously, PID 1 would only advance the time to an epoch
+          time that is set during build-time. With this new file OS builders
+          can change this epoch timestamp on individual OS images without
+          having to rebuild systemd.
+
+        * systemd-logind will now listen to the KEY_RESTART key from the Linux
+          input layer and reboot the system if it is pressed, similarly to how
+          it already handles KEY_POWER, KEY_SUSPEND or KEY_SLEEP. KEY_RESTART
+          was originally defined in the Multimedia context (to restart playback
+          of a song or film), but is now primarily used in various embedded
+          devices for "Reboot" buttons. Accordingly, systemd-logind will now
+          honour it as such. This may configured in more detail via the new
+          HandleRebootKey= and RebootKeyIgnoreInhibited=.
+
+        * systemd-nspawn/systemd-machined will now reconstruct hardlinks when
+          copying OS trees, for example in "systemd-nspawn --ephemeral",
+          "systemd-nspawn --template=", "machinectl clone" and similar. This is
+          useful when operating with OSTree images, which use hardlinks heavily
+          throughout, and where such copies previously resulting in "exploding"
+          hardlinks.
+
+        * systemd-nspawn's --console= setting gained support for a new
+          "autopipe" value, which is identical to "interactive" when invoked on
+          a TTY, and "pipe" otherwise.
+
+        * systemd-networkd's .network files gained support for explicitly
+          configuring the multicast membership entries of bridge devices in the
+          [BridgeMDB] section. It also gained support for the PIE queuing
+          discipline in the [FlowQueuePIE] sections.
+
+        * systemd-networkd's .netdev files may now be used to create "BareUDP"
+          tunnels, configured in the new [BareUDP] setting. VXLAN tunnels may
+          now be marked to be independent of any underlying network interface
+          via the new Independent= boolean setting.
+
+        * systemctl gained support for two new verbs: "service-log-level" and
+          "service-log-target" may be used on services that implement the
+          generic org.freedesktop.LogControl1 D-Bus interface to dynamically
+          adjust the log level and target. All of systemd's long-running
+          services support this now, but ideally all system services would
+          implement this interface to make the system more uniformly
+          debuggable.
+
+        * The SystemCallErrorNumber= unit file setting now accepts the new
+          "kill" and "log" actions, in addition to arbitrary error number
+          specifications as before. If "kill" the the processes are killed on
+          the event, if "log" the offending syscall is audit logged.
+
+        * A new SystemCallLog= unit file setting has been added that accepts a
+          list of syscalls that shall be logged about (audit).
+
+        * The OS image dissection logic (as used by RootImage= in unit files or
+          systemd-nspawn's --image= switch) has gained support for identifying
+          and mounting explicit /usr/ partitions, which are now defined in the
+          discoverable partition specification. This should be useful for
+          environments where the root file system is
+          generated/formatted/populated dynamically on first boot and combined
+          with an immutable /usr/ tree that is supplied by the vendor.
+
+        * In the final phase of shutdown, within the systemd-shutdown binary
+          we'll now try to detach MD devices (i.e software RAID) in addition to
+          loopback block devices and DM devices as before. This is supposed to
+          be a safety net only, in order to increase robustness if things go
+          wrong. Storage subsystems are expected to properly detach their
+          storage volumes during regular shutdown already (or in case of
+          storage backing the root file system: in the initrd hook we return to
+          later).
+
+        * If the SYSTEMD_LOG_TID environment variable is set all systemd tools
+          will now log the thread ID in their log output. This is useful when
+          working with heavily threaded programs.
+
+        * If the SYSTEMD_RDRAND enviroment variable is set to "0", systemd will
+          not use the RDRAND CPU instruction. This is useful in environments
+          such as replay debuggers where non-deterministic behaviour is not
+          desirable.
+
+        * When building systemd the Meson option
+          -Dcompat-mutable-uid-boundaries may now be specified. If enabled,
+          systemd reads the system UID boundaries from /etc/login.defs, instead
+          of using the built-in values selected during build-time. This is an
+          option to improve compatibility for upgrades from old systems. It's
+          strongly recommended not to make use of this functionality on new
+          systems (or even enable it during build), as it makes something
+          runtime-configurable that is mostly an implementation detail of the
+          OS, and permits avoidable differences in deployments that create all
+          kinds of problems in the long run.
+
 
 CHANGES WITH 246:
 
index f836d812b18c0672a332632a88d8ab251e6723ec..e8703ea6effdeb8d1412303c471bad69b03df38e 100644 (file)
--- a/README.md
+++ b/README.md
@@ -11,7 +11,9 @@ System and Service Manager
 [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1369/badge)](https://bestpractices.coreinfrastructure.org/projects/1369)<br/>
 [![Travis CI Build Status](https://travis-ci.org/systemd/systemd.svg?branch=master)](https://travis-ci.org/systemd/systemd)<br/>
 [![Language Grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/systemd/systemd.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/systemd/systemd/context:cpp)<br/>
-[![CentOS CI Build Status](https://ci.centos.org/buildStatus/icon?job=systemd-pr-build)](https://ci.centos.org/job/systemd-pr-build/)<br/>
+[![CentOS CI - CentOS 7](https://jenkins-systemd.apps.ocp.ci.centos.org/buildStatus/icon?subject=CentOS%20CI%20-%20CentOS%207&job=upstream-centos7)](https://jenkins-systemd.apps.ocp.ci.centos.org/job/upstream-centos7/)<br/>
+[![CentOS CI - Arch](https://jenkins-systemd.apps.ocp.ci.centos.org/buildStatus/icon?subject=CentOS%20CI%20-%20Arch&job=upstream-vagrant-archlinux)](https://jenkins-systemd.apps.ocp.ci.centos.org/job/upstream-vagrant-archlinux/)<br/>
+[![CentOS CI - Arch (sanitizers)](https://jenkins-systemd.apps.ocp.ci.centos.org/buildStatus/icon?subject=CentOS%20CI%20-%20Arch%20(sanitizers)&job=upstream-vagrant-archlinux-sanitizers)](https://jenkins-systemd.apps.ocp.ci.centos.org/job/upstream-vagrant-archlinux-sanitizers/)<br/>
 [![Build Status](https://dev.azure.com/evvers/systemd-systemd/_apis/build/status/systemd.systemd?branchName=master)](https://dev.azure.com/evvers/systemd-systemd/_build/latest?definitionId=1&branchName=master)<br/>
 [![Fossies codespell report](https://fossies.org/linux/test/systemd-master.tar.gz/codespell.svg)](https://fossies.org/linux/test/systemd-master.tar.gz/codespell.html)</br>
 [![Packaging status](https://repology.org/badge/tiny-repos/systemd.svg)](https://repology.org/project/systemd/versions)
diff --git a/TODO b/TODO
index a0864b321762282dd943315c34956ae97af96815..9bfaddb08f100fb5d7f241ee1e2657b6c6fa2d37 100644 (file)
--- a/TODO
+++ b/TODO
@@ -20,6 +20,10 @@ Janitorial Clean-ups:
 
 Features:
 
+* systemd-analyze syscall-filter should show a list of syscalls listed in
+  @known but not in other groups (at least at debug level), since they are
+  candidates to be added to them.
+
 * export action of device object on sd-device, so that monitor becomes useful
 
 * add root=tmpfs that mounts a tmpfs to /sysroot (to be used in combination
index 957d828a8376abc6ad0af8cfd9842995e819cb42..3fce0f4caaee1a98029276a786b1e6288c6b23f7 100644 (file)
@@ -2,13 +2,28 @@
 expression e;
 statement s;
 @@
-- if (e == NULL)
-+ if (!e)
-s
+if (
+(
+!e
+|
+- e == NULL
++ !e
+)
+   )
+   {...}
+else s
+
 @@
 expression e;
 statement s;
 @@
-- if (e != NULL)
-+ if (e)
-s
+if (
+(
+e
+|
+- e != NULL
++ e
+)
+   )
+   {...}
+else s
index 73966b02e5d27861900299f0a5adecd4f1fa0db2..f6cc8ba68a08aac2ddbb286e58171bf452bd45d5 100644 (file)
@@ -1,7 +1,13 @@
 @@
-/* Disable this transformation for the securebits-util.h, as it makes
- * the expression there confusing. */
-position p : script:python() { p[0].file != "src/shared/securebits-util.h" };
+/* Disable this transformation in cases where it doesn't make sense or
+ * where it makes the resulting expression more confusing
+ */
+position p : script:python() {
+            not (p[0].file == "src/shared/securebits-util.h" or
+                 p[0].file == "src/core/manager.h" or
+                 p[0].current_element == "log_set_max_level_realm" or
+                 p[0].current_element == "unichar_is_valid")
+        };
 expression x, y;
 @@
 (
index 2c9b94ceb67438418efdbc5e42604a598dc1caa6..1c17c7df1f4d9c1a990a011b37a2685f05638e76 100644 (file)
@@ -1,25 +1,13 @@
+/* Limit the number of expressions to 6 for performance reasons */
 @@
 expression e;
 /* Exclude JsonVariant * from the transformation, as it can't work with the
  * current version of the IN_SET macro */
 typedef JsonVariant;
 type T != JsonVariant*;
-constant T n0, n1, n2, n3, n4, n5, n6, n7, n8, n9;
+constant T n0, n1, n2, n3, n4, n5;
 @@
-
 (
-- e == n0 || e == n1 || e == n2 || e == n3 || e == n4 || e == n5 || e == n6 || e == n7 || e == n8 || e == n9
-+ IN_SET(e, n0, n1, n2, n3, n4, n5, n6, n7, n8, n9)
-|
-- e == n0 || e == n1 || e == n2 || e == n3 || e == n4 || e == n5 || e == n6 || e == n7 || e == n8
-+ IN_SET(e, n0, n1, n2, n3, n4, n5, n6, n7, n8)
-|
-- e == n0 || e == n1 || e == n2 || e == n3 || e == n4 || e == n5 || e == n6 || e == n7
-+ IN_SET(e, n0, n1, n2, n3, n4, n5, n6, n7)
-|
-- e == n0 || e == n1 || e == n2 || e == n3 || e == n4 || e == n5 || e == n6
-+ IN_SET(e, n0, n1, n2, n3, n4, n5, n6)
-|
 - e == n0 || e == n1 || e == n2 || e == n3 || e == n4 || e == n5
 + IN_SET(e, n0, n1, n2, n3, n4, n5)
 |
index aed2c3490c4f75485c2d5672058b285073322a3a..3486cff5df10901c3aaae2a2a6b1008e3e85e35a 100644 (file)
@@ -1,22 +1,11 @@
+/* Limit the number of expressions to 6 for performance reasons */
 @@
 expression e;
 typedef JsonVariant;
 type T != JsonVariant*;
-constant T n0, n1, n2, n3, n4, n5, n6, n7, n8, n9;
+constant T n0, n1, n2, n3, n4, n5;
 @@
 (
-- e != n0 && e != n1 && e != n2 && e != n3 && e != n4 && e != n5 && e != n6 && e != n7 && e != n8 && e != n9
-+ !IN_SET(e, n0, n1, n2, n3, n4, n5, n6, n7, n8, n9)
-|
-- e != n0 && e != n1 && e != n2 && e != n3 && e != n4 && e != n5 && e != n6 && e != n7 && e != n8
-+ !IN_SET(e, n0, n1, n2, n3, n4, n5, n6, n7, n8)
-|
-- e != n0 && e != n1 && e != n2 && e != n3 && e != n4 && e != n5 && e != n6 && e != n7
-+ !IN_SET(e, n0, n1, n2, n3, n4, n5, n6, n7)
-|
-- e != n0 && e != n1 && e != n2 && e != n3 && e != n4 && e != n5 && e != n6
-+ !IN_SET(e, n0, n1, n2, n3, n4, n5, n6)
-|
 - e != n0 && e != n1 && e != n2 && e != n3 && e != n4 && e != n5
 + !IN_SET(e, n0, n1, n2, n3, n4, n5)
 |
index be80a76a5f3b50cb0c60dd96e8d5bd3176541d4c..1fdef5f63ef6cc96a89a94f0251ead03dd2d42ac 100755 (executable)
@@ -10,7 +10,6 @@ EXCLUDED_PATHS=(
 )
 
 top="$(git rev-parse --show-toplevel)"
-iso_defs="$top/coccinelle/systemd-definitions.iso"
 args=
 
 # Create an array from files tracked by git...
@@ -37,7 +36,7 @@ for SCRIPT in ${@-$top/coccinelle/*.cocci} ; do
     TMPFILE=`mktemp`
     echo "+ spatch --sp-file $SCRIPT $args ..."
     parallel --halt now,fail=1 --keep-order --noswap --max-args=20 \
-             spatch --iso-file $iso_defs --sp-file $SCRIPT $args ::: "${files[@]}" \
+             spatch --sp-file $SCRIPT $args ::: "${files[@]}" \
              2>"$TMPFILE" || cat "$TMPFILE"
     echo -e "--x-- Processed $SCRIPT --x--\n"
 done
index 675760e37a41b6f8224547105d7768690c5056fd..46f70c4c23682a4d6027b43bc05d9e12c01caa82 100644 (file)
@@ -1,16 +1,15 @@
 @@
-expression list args;
-@@
-- strjoin(args, NULL);
-+ strjoin(args);
-@@
+position p : script:python() { p[0].current_element != "test_strjoin" };
 expression t;
 expression list args;
 @@
-- t = strjoin(args, NULL);
+(
+- strjoin@p(args, NULL);
++ strjoin(args);
+|
+- t = strjoin@p(args, NULL);
 + t = strjoin(args);
-@@
-expression list args;
-@@
-- return strjoin(args, NULL);
+|
+- return strjoin@p(args, NULL);
 + return strjoin(args);
+)
diff --git a/coccinelle/systemd-definitions.iso b/coccinelle/systemd-definitions.iso
deleted file mode 100644 (file)
index 92db763..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/* Statement isomorphisms - replace explicit checks against NULL with a
- * shorter variant, which relies on C's downgrade-to-bool feature.
- * The expression metavariables should be declared as pointers, however,
- * that doesn't work well with complex expressions like:
- * if (UNIT(p)->default_dependencies != NULL)
- */
-
-Statement
-@@
-expression X;
-statement S;
-@@
-if (X == NULL) S => if (!X) S
-
-Statement
-@@
-expression X;
-statement S;
-@@
-if (X != NULL) S => if (X) S
index 401216ad728a431b6cef5227c5ac5123c60b31c7..660a35e3f4d9b340a5e90db9a1138ff44b8cf635 100644 (file)
@@ -1,6 +1,7 @@
 @@
+position p : script:python() { not p[0].file.startswith("man/") };
 expression e, fmt;
 expression list vaargs;
 @@
-- snprintf(e, sizeof(e), fmt, vaargs);
+- snprintf@p(e, sizeof(e), fmt, vaargs);
 + xsprintf(e, fmt, vaargs);
index 4b792173a6c55647b13a652e469f1d6711989378..8afe993dac1bef87fe66598b3c14ea7d34fb8293 100644 (file)
 
         <listitem><para>Takes a path to a Linux <literal>hidraw</literal> device
         (e.g. <filename>/dev/hidraw1</filename>), referring to a FIDO2 security token implementing the
-        <literal>hmac-secret</literal> extension, that shall be able to unlock the user account. If used, a
-        random salt value is generated on the host, which is passed to the FIDO2 device, which calculates a
-        HMAC hash of it, keyed by its internal secret key. The result is then used as key for unlocking the
-        user account. The random salt is included in the user record, so that whenever authentication is
-        needed it can be passed again to the FIDO2 token, to retrieve the actual key.</para>
+        <literal>hmac-secret</literal> extension that shall be able to unlock the user account. A random salt
+        value is generated on the host and passed to the FIDO2 device, which calculates a HMAC hash of the
+        salt using an internal secret key. The result is then used as the key to unlock the user account. The
+        random salt is included in the user record, so that whenever authentication is needed it can be
+        passed to the FIDO2 token again.</para>
 
         <para>Instead of a valid path to a FIDO2 <literal>hidraw</literal> device the special strings
         <literal>list</literal> and <literal>auto</literal> may be specified. If <literal>list</literal> is
index 97d11da03fa436f9d10270bef618fc75d473112e..7857073acacaa00d7c3c49922f93f10c361ef237 100644 (file)
         <term><varname>KillOnlyUsers=</varname></term>
         <term><varname>KillExcludeUsers=</varname></term>
 
-        <listitem><para>These settings take space-separated lists of usernames that override
-        the <varname>KillUserProcesses=</varname> setting. A user name may be added to
-        <varname>KillExcludeUsers=</varname> to exclude the processes in the session scopes of
-        that user from being killed even if <varname>KillUserProcesses=yes</varname> is set. If
-        <varname>KillExcludeUsers=</varname> is not set, the <literal>root</literal> user is
-        excluded by default. <varname>KillExcludeUsers=</varname> may be set to an empty value
-        to override this default. If a user is not excluded, <varname>KillOnlyUsers=</varname>
-        is checked next. If this setting is specified, only the session scopes of those users
-        will be killed. Otherwise, users are subject to the
-        <varname>KillUserProcesses=yes</varname> setting.</para></listitem>
+        <listitem><para>These settings take space-separated lists of usernames that override the
+        <varname>KillUserProcesses=</varname> setting. A user name may be added to
+        <varname>KillExcludeUsers=</varname> to exclude the processes in the session scopes of that user from
+        being killed even if <varname>KillUserProcesses=yes</varname> is set. If
+        <varname>KillExcludeUsers=</varname> is not set, the <literal>root</literal> user is excluded by
+        default. <varname>KillExcludeUsers=</varname> may be set to an empty value to override this
+        default. If a user is not excluded, <varname>KillOnlyUsers=</varname> is checked next. If this
+        setting is specified, only the processes in the session scopes of those users will be
+        killed. Otherwise, users are subject to the <varname>KillUserProcesses=yes</varname> setting.
+        </para></listitem>
       </varlistentry>
 
       <varlistentry>
index 962429683d6b38fcbe3424713f575c0c798a1143..d24c010e2ce49564ee7457bf17446cf7fc2f2074 100644 (file)
         <command>attach</command> command above, and removes the unit file copies, drop-ins and image symlink
         again. This command expects an image name or path as parameter. Note that if a path is specified only the last
         component of it (i.e. the file or directory name itself, not the path to it) is used for finding matching unit
-        files. This is a convencience feature to allow all arguments passed as <command>attach</command> also to
+        files. This is a convenience feature to allow all arguments passed as <command>attach</command> also to
         <command>detach</command>.</para></listitem>
 
         <para>If <option>--now</option> and/or <option>--enable</option> are passed, the portable service(s) are
index 8532c2bf466c85c8c915434172146ce067b6af58..06ce1386ac79b380490a7b9ede40dafe025ba4a9 100644 (file)
     is equivalent to <function>sd_bus_open()</function>. This description string is used in log
     messages about the bus object, and including a "name" for the bus makes them easier to
     understand. Some messages are emitted during bus initialization, hence using this function is
-    prefereable to setting the description later with
+    preferable to setting the description later with
     <function>sd_bus_open_with_description()</function>. The argument is copied internally and will
     not be referenced after the function returns.</para>
 
index 8860699db3d085dd6f8c4e7f2c16b674874d3b88..826f4c4ada0a08361cf6c4c99a099ec121207155 100644 (file)
@@ -66,7 +66,7 @@
     function to call when the inode changes. The handler function will be passed the <parameter>userdata</parameter>
     pointer, which may be chosen freely by the caller. The handler also receives a pointer to a <structname>struct
     inotify_event</structname> structure containing information about the inode event. The <parameter>mask</parameter>
-    parameter specifie which types of inode events to watch specifically. It must contain an OR-ed combination of
+    parameter specifies which types of inode events to watch specifically. It must contain an OR-ed combination of
     <constant>IN_ACCESS</constant>, <constant>IN_ATTRIB</constant>, <constant>IN_CLOSE_WRITE</constant>, … flags. See
     <citerefentry project='man-pages'><refentrytitle>inotify</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
     further information.</para>
index ac5ffa56451fd766499e455581f523dbf2d57cb7..36c4602d6965159ed86e0af79caa3e4a31ca3c79 100644 (file)
@@ -62,7 +62,7 @@
         </thead>
         <tbody>
           <row>
-            <entry valign="top" morerows="12">VM</entry>
+            <entry valign="top" morerows="13">VM</entry>
             <entry><varname>qemu</varname></entry>
             <entry>QEMU software virtualization, without KVM</entry>
           </row>
             <entry>Oracle VM VirtualBox (historically marketed by innotek and Sun Microsystems), for legacy and KVM hypervisor</entry>
           </row>
 
+          <row>
+            <entry><varname>powervm</varname></entry>
+            <entry>IBM PowerVM hypervisor - comes as firmware with some IBM POWER servers</entry>
+          </row>
+
           <row>
             <entry><varname>xen</varname></entry>
             <entry>Xen hypervisor (only domU, not dom0)</entry>
           </row>
 
           <row>
-            <entry valign="top" morerows="8">Container</entry>
+            <entry valign="top" morerows="9">Container</entry>
             <entry><varname>openvz</varname></entry>
             <entry>OpenVZ/Virtuozzo</entry>
           </row>
index b6e0dd1eccaabfe1f84a5d0e2adb38a3ff717da0..9da919c3795b61edff4fd3e9f2a204cf69170217 100644 (file)
@@ -2029,7 +2029,7 @@ RestrictNamespaces=~cgroup net</programlisting>
               </row>
               <row>
                 <entry>@process</entry>
-                <entry>Process control, execution, namespaceing operations (<citerefentry project='man-pages'><refentrytitle>clone</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>kill</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>, …)</entry>
+                <entry>Process control, execution, namespacing operations (<citerefentry project='man-pages'><refentrytitle>clone</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>kill</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>, …)</entry>
               </row>
               <row>
                 <entry>@raw-io</entry>
@@ -2069,7 +2069,7 @@ RestrictNamespaces=~cgroup net</programlisting>
               </row>
               <row>
                 <entry>@known</entry>
-                <entry>All system calls defined by the kernel. This list is defined statically in systemd based on a kernel version that was available when this systmed version was released. It will become progressively more out-of-date as the kernel is updated.</entry>
+                <entry>All system calls defined by the kernel. This list is defined statically in systemd based on a kernel version that was available when this systemd version was released. It will become progressively more out-of-date as the kernel is updated.</entry>
               </row>
             </tbody>
           </tgroup>
index 694ee526141e282f5e89e13adb24984c6a580132..a95beb6f9fa14b57616202301e7e829b9d276fd0 100644 (file)
       <varlistentry>
         <term><varname>DestinationPort=</varname></term>
         <listitem>
-          <para>Configures the default destination UDP port on a per-device basis.
-          If destination port is not specified then Linux kernel default will be used.
-          Set destination port 4789 to get the IANA assigned value. If not set or if the
-          destination port is assigned the empty string the default port of 4789 is used.</para>
+          <para>Configures the default destination UDP port. If the destination port is not specified then
+          Linux kernel default will be used. Set to 4789 to get the IANA assigned value.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>PortRange=</varname></term>
         <listitem>
-          <para>Configures VXLAN port range. VXLAN bases source
-          UDP port based on flow to help the receiver to be able
-          to load balance based on outer header flow. It
-          restricts the port range to the normal UDP local
-          ports, and allows overriding via configuration.</para>
+          <para>Configures the source port range for the VXLAN. The kernel assigns the source UDP port based
+          on the flow to help the receiver to do load balancing. When this option is not set, the normal
+          range of local UDP ports is used.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
index 8f1a63a14e9208a42cebca7c6b824d0413a1f925..a020214ac62a2b234df2a3b084a43572078f27d4 100644 (file)
     into words, and afterwards removed.</para>
 
     <para>If the command is not a full (absolute) path, it will be resolved to a full path using a
-    fixed search path determinted at compilation time. Searched directories include
+    fixed search path determined at compilation time. Searched directories include
     <filename>/usr/local/bin/</filename>, <filename>/usr/bin/</filename>, <filename>/bin/</filename>
     on systems using split <filename>/usr/bin/</filename> and <filename>/bin/</filename>
     directories, and their <filename>sbin/</filename> counterparts on systems using split
index d52cd0a24c812a42922dd15c87442cb1a3e9c055..f23c903d069efc59da09601bd114a2d53f8370db 100644 (file)
           <literal>vmware</literal>,
           <literal>microsoft</literal>,
           <literal>oracle</literal>,
+          <literal>powervm</literal>,
           <literal>xen</literal>,
           <literal>bochs</literal>,
           <literal>uml</literal>,
index 0b5669b4425b8dfcbc0e14c0a320c351151ef058..aa695b5e04b8587bd9a3d0281c9e335375e5b045 100644 (file)
@@ -88,7 +88,7 @@
         <term><varname>resolve_names=</varname></term>
 
         <listitem>
-          <para>Specifes when systemd-udevd should resolve names of users and groups.  When set to
+          <para>Specifies when systemd-udevd should resolve names of users and groups.  When set to
           <option>early</option> (the default), names will be resolved when the rules are parsed.
           When set to <option>late</option>, names will be resolved for every event.  When set to
           <option>never</option>, names will never be resolved and all devices will be owned by
index 0d3022b1e963e78f7be6ae5fb0cbbc242bef8ea5..1df70147afcb5dd24904e5db954d94ad211acc91 100644 (file)
@@ -24,6 +24,7 @@ if bashcompletiondir != 'no'
                  ['systemd-cgtop',       ''],
                  ['systemd-delta',       ''],
                  ['systemd-detect-virt', ''],
+                 ['systemd-id128',       ''],
                  ['systemd-nspawn',      ''],
                  ['systemd-path',        ''],
                  ['systemd-run',         ''],
@@ -32,6 +33,7 @@ if bashcompletiondir != 'no'
                  [bash_systemctl,        ''],
                  ['bootctl',             'ENABLE_EFI'],
                  ['coredumpctl',         'ENABLE_COREDUMP'],
+                 ['homectl',             'ENABLE_HOMED'],
                  ['hostnamectl',         'ENABLE_HOSTNAMED'],
                  ['localectl',           'ENABLE_LOCALED'],
                  ['loginctl',            'ENABLE_LOGIND'],
index 01ff6bb331fd277d6c7de18eb35f5ea88257c52b..ab331cd677ac2207580198a42978f23838a59149 100644 (file)
@@ -45,7 +45,7 @@ static int unlinkat_harder(
                 return -errno;
         if (!S_ISDIR(st.st_mode))
                 return -ENOTDIR;
-        if ((st.st_mode & 0700) == 0700) /* Already set? */
+        if (FLAGS_SET(st.st_mode, 0700)) /* Already set? */
                 return -EACCES; /* original error */
         if (st.st_uid != geteuid())  /* this only works if the UID matches ours */
                 return -EACCES;
index 6cacde90bac6b58333368ed1cf6dfc301c338a17..ac83688531f2530eb27d271a78b19814caa1d1f1 100644 (file)
@@ -1349,7 +1349,7 @@ int vt_release(int fd, bool restore) {
 
 void get_log_colors(int priority, const char **on, const char **off, const char **highlight) {
         /* Note that this will initialize output variables only when there's something to output.
-         * The caller must pre-initalize to "" or NULL as appropriate. */
+         * The caller must pre-initialize to "" or NULL as appropriate. */
 
         if (priority <= LOG_ERR) {
                 if (on)
index 212b3b7039c42ea64969279ca6c9bf448a01089b..bb908847f5b1f2b056cfb19654a7db9c649d39f3 100644 (file)
@@ -93,6 +93,11 @@ static int detect_vm_device_tree(void) {
                 _cleanup_closedir_ DIR *dir = NULL;
                 struct dirent *dent;
 
+                if (access("/proc/device-tree/ibm,partition-name", F_OK) == 0 &&
+                    access("/proc/device-tree/hmc-managed?", F_OK) == 0 &&
+                    access("/proc/device-tree/chosen/qemu,graphic-width", F_OK) != 0)
+                        return VIRTUALIZATION_POWERVM;
+
                 dir = opendir("/proc/device-tree");
                 if (!dir) {
                         if (errno == ENOENT) {
@@ -499,7 +504,7 @@ int detect_container(void) {
                 goto translate_name;
         }
         if (!IN_SET(r, -ENOENT, 0))
-                return log_debug_errno(r, "Failed to read /run/systemd/container: %m");
+                return log_debug_errno(r, "Failed to read /run/systemd/container-manager: %m");
 
         if (getpid_cached() == 1) {
                 /* If we are PID 1 we can just check our own environment variable, and that's authoritative.
@@ -679,6 +684,7 @@ static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
         [VIRTUALIZATION_BHYVE] = "bhyve",
         [VIRTUALIZATION_QNX] = "qnx",
         [VIRTUALIZATION_ACRN] = "acrn",
+        [VIRTUALIZATION_POWERVM] = "powervm",
         [VIRTUALIZATION_VM_OTHER] = "vm-other",
 
         [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
index 18aa5eff15316f22896b9601b2feced7ebb7c8d3..2f7f7203d2948331be265ee48fca0b2f76862fc4 100644 (file)
@@ -22,6 +22,7 @@ enum {
         VIRTUALIZATION_BHYVE,
         VIRTUALIZATION_QNX,
         VIRTUALIZATION_ACRN,
+        VIRTUALIZATION_POWERVM,
         VIRTUALIZATION_VM_OTHER,
         VIRTUALIZATION_VM_LAST = VIRTUALIZATION_VM_OTHER,
 
index 0b712d856f00fa83cd3c848be44597df775ec360..4d87117b00cce9f666474e7278d9792456089a80 100644 (file)
@@ -266,7 +266,7 @@ static EFI_TCG * tcg1_interface_check(void) {
         return tcg;
 }
 
-static EFI_TCG2 * tcg2_interface_check() {
+static EFI_TCG2 * tcg2_interface_check(void) {
         EFI_GUID tpm2_guid = EFI_TCG2_PROTOCOL_GUID;
         EFI_STATUS status;
         EFI_TCG2 *tcg;
index c3d7d24eccdf369ff9301836d6b8977596f0d841..43373f35d4e1dbfdab936d2807406853f339bdc5 100644 (file)
@@ -5502,7 +5502,7 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) {
                         done = true;
 
                         HASHMAP_FOREACH_KEY(di.data, other, u->dependencies[d]) {
-                                if ((di.origin_mask & ~mask) == di.origin_mask)
+                                if (FLAGS_SET(~mask, di.origin_mask))
                                         continue;
                                 di.origin_mask &= ~mask;
                                 unit_update_dependency_mask(u, d, other, di);
@@ -5516,7 +5516,7 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) {
                                         UnitDependencyInfo dj;
 
                                         dj.data = hashmap_get(other->dependencies[q], u);
-                                        if ((dj.destination_mask & ~mask) == dj.destination_mask)
+                                        if (FLAGS_SET(~mask, dj.destination_mask))
                                                 continue;
                                         dj.destination_mask &= ~mask;
 
index 0bc5fa321018da09e37dbdee413d9b6471506c77..83e1b902916ad1e4ac87280ddf420bf8c47316f1 100644 (file)
@@ -779,7 +779,7 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d
                                         return r;
 
                                 n += r;
-                        } else if ((c & 0xc0) == 0xc0) {
+                        } else if (FLAGS_SET(c, 0xc0)) {
                                 /* Pointer */
 
                                 uint8_t d;
index 6bbc23f131f0093c7a1b86b748f5b44d6b6d1408..38025b63d92ce7439d8f1551c37444760d3a5422 100644 (file)
@@ -270,7 +270,7 @@ static int dhcp6_client_set_duid_internal(
         assert_return(duid_len == 0 || duid != NULL, -EINVAL);
         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
 
-        if (duid != NULL) {
+        if (duid) {
                 r = dhcp_validate_duid_len(duid_type, duid_len, true);
                 if (r < 0) {
                         r = dhcp_validate_duid_len(duid_type, duid_len, false);
index d6bf31efc7a196f3708fcbb2db1ea6c8f9700e65..0d9a57ee9eb225475e62c219aeeb90e512c61cd5 100644 (file)
@@ -862,11 +862,12 @@ int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short typ
         return 0;
 }
 
-int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) {
-        int r;
+int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short type, int family, union in_addr_union *data) {
         void *attr_data;
+        int r;
 
         assert_return(m, -EINVAL);
+        assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL);
 
         r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR);
         if (r < 0)
@@ -875,35 +876,35 @@ int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type,
         r = netlink_message_read_internal(m, type, &attr_data, NULL);
         if (r < 0)
                 return r;
-        else if ((size_t) r < sizeof(struct in_addr))
+        else if ((size_t) r < FAMILY_ADDRESS_SIZE(family))
                 return -EIO;
 
         if (data)
-                memcpy(data, attr_data, sizeof(struct in_addr));
+                memcpy(data, attr_data, FAMILY_ADDRESS_SIZE(family));
 
         return 0;
 }
 
-int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) {
+int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) {
+        union in_addr_union u;
         int r;
-        void *attr_data;
 
-        assert_return(m, -EINVAL);
+        r = netlink_message_read_in_addr_union(m, type, AF_INET, &u);
+        if (r >= 0 && data)
+                *data = u.in;
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR);
-        if (r < 0)
-                return r;
+        return r;
+}
 
-        r = netlink_message_read_internal(m, type, &attr_data, NULL);
-        if (r < 0)
-                return r;
-        else if ((size_t) r < sizeof(struct in6_addr))
-                return -EIO;
+int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) {
+        union in_addr_union u;
+        int r;
 
-        if (data)
-                memcpy(data, attr_data, sizeof(struct in6_addr));
+        r = netlink_message_read_in_addr_union(m, type, AF_INET6, &u);
+        if (r >= 0 && data)
+                *data = u.in6;
 
-        return 0;
+        return r;
 }
 
 int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container_type, unsigned short type_id, char ***ret) {
index ddf5686f1343d92873c2e135c3b64305df0a3111..fa17e7dae7964ee6bc088cc07084a7c1f8b3ccda 100644 (file)
@@ -77,18 +77,20 @@ int rtnl_log_create_error(int r);
                                       userdata, 0, __func__);           \
         })
 
-#define netlink_add_match(nl, ret_slot, metch, callback, destroy_callback, userdata) \
+#define netlink_add_match(nl, ret_slot, match, callback, destroy_callback, userdata, description) \
         ({                                                              \
                 int (*_callback_)(sd_netlink *, sd_netlink_message *, typeof(userdata)) = callback; \
                 void (*_destroy_)(typeof(userdata)) = destroy_callback; \
                 sd_netlink_add_match(nl, ret_slot, match,               \
                                      (sd_netlink_message_handler_t) _callback_, \
                                      (sd_netlink_destroy_t) _destroy_,  \
-                                     userdata, __func__);               \
+                                     userdata, description);            \
         })
 
 int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data);
 int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short type, const union sockaddr_union *data);
 
+int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short type, int family, union in_addr_union *data);
+
 void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length);
 int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length);
index 28941b44682905a2366e002e9f552ec94f163411..c2a197162f2c9931debb3267a9e7310f650ea466 100644 (file)
@@ -111,6 +111,8 @@ sources = files('''
         networkd-speed-meter.h
         networkd-sriov.c
         networkd-sriov.h
+        networkd-sysctl.c
+        networkd-sysctl.h
         networkd-util.c
         networkd-util.h
         networkd-wifi.c
index 9d3b8d6e4cd9b009506cd2448c4cd4fc5aa9f7e8..9f0e6f25c1e51739fc889fd33394b102d48a33b7 100644 (file)
@@ -13,7 +13,6 @@
 #include "memory-util.h"
 #include "netlink-util.h"
 #include "network-internal.h"
-#include "networkd-address.h"
 #include "networkd-manager.h"
 #include "path-util.h"
 #include "socket-util.h"
index 2a3443a6d4e6dcba45ee7926d5546194d3331095..26ad2b7cca9771c848de07267e0c9ca936660c8f 100644 (file)
@@ -4,6 +4,7 @@
 #include <netinet/in.h>
 #include <linux/if_macsec.h>
 
+#include "ether-addr-util.h"
 #include "in-addr-util.h"
 #include "netdev.h"
 #include "networkd-util.h"
index 0d53aa90429b333d7ebaa232160498130f2df03a..66b192256e98373735b99150786c995a8fe3b53c 100644 (file)
@@ -4,31 +4,28 @@
 #include <linux/if_addrlabel.h>
 
 #include "alloc-util.h"
-#include "conf-parser.h"
-#include "networkd-address-label.h"
 #include "netlink-util.h"
+#include "networkd-address-label.h"
+#include "networkd-link.h"
 #include "networkd-manager.h"
+#include "networkd-network.h"
 #include "parse-util.h"
-#include "socket-util.h"
 
-void address_label_free(AddressLabel *label) {
+AddressLabel *address_label_free(AddressLabel *label) {
         if (!label)
-                return;
+                return NULL;
 
         if (label->network) {
-                LIST_REMOVE(labels, label->network->address_labels, label);
-                assert(label->network->n_address_labels > 0);
-                label->network->n_address_labels--;
-
-                if (label->section) {
-                        hashmap_remove(label->network->address_labels_by_section, label->section);
-                        network_config_section_free(label->section);
-                }
+                assert(label->section);
+                hashmap_remove(label->network->address_labels_by_section, label->section);
         }
 
-        free(label);
+        network_config_section_free(label->section);
+        return mfree(label);
 }
 
+DEFINE_NETWORK_SECTION_FUNCTIONS(AddressLabel, address_label_free);
+
 static int address_label_new_static(Network *network, const char *filename, unsigned section_line, AddressLabel **ret) {
         _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
         _cleanup_(address_label_freep) AddressLabel *label = NULL;
@@ -36,19 +33,17 @@ static int address_label_new_static(Network *network, const char *filename, unsi
 
         assert(network);
         assert(ret);
-        assert(!!filename == (section_line > 0));
-
-        if (filename) {
-                r = network_config_section_new(filename, section_line, &n);
-                if (r < 0)
-                        return r;
+        assert(filename);
+        assert(section_line > 0);
 
-                label = hashmap_get(network->address_labels_by_section, n);
-                if (label) {
-                        *ret = TAKE_PTR(label);
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
 
-                        return 0;
-                }
+        label = hashmap_get(network->address_labels_by_section, n);
+        if (label) {
+                *ret = TAKE_PTR(label);
+                return 0;
         }
 
         label = new(AddressLabel, 1);
@@ -57,25 +52,18 @@ static int address_label_new_static(Network *network, const char *filename, unsi
 
         *label = (AddressLabel) {
                 .network = network,
+                .section = TAKE_PTR(n),
         };
 
-        LIST_APPEND(labels, network->address_labels, label);
-        network->n_address_labels++;
-
-        if (filename) {
-                label->section = TAKE_PTR(n);
-
-                r = hashmap_ensure_allocated(&network->address_labels_by_section, &network_config_hash_ops);
-                if (r < 0)
-                        return r;
+        r = hashmap_ensure_allocated(&network->address_labels_by_section, &network_config_hash_ops);
+        if (r < 0)
+                return r;
 
-                r = hashmap_put(network->address_labels_by_section, label->section, label);
-                if (r < 0)
-                        return r;
-        }
+        r = hashmap_put(network->address_labels_by_section, label->section, label);
+        if (r < 0)
+                return r;
 
         *ret = TAKE_PTR(label);
-
         return 0;
 }
 
@@ -107,12 +95,7 @@ static int address_label_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
         return 1;
 }
 
-int address_label_configure(
-                AddressLabel *label,
-                Link *link,
-                link_netlink_message_handler_t callback,
-                bool update) {
-
+static int address_label_configure(AddressLabel *label, Link *link) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         int r;
 
@@ -140,7 +123,7 @@ int address_label_configure(
                 return log_link_error_errno(link, r, "Could not append IFA_ADDRESS attribute: %m");
 
         r = netlink_call_async(link->manager->rtnl, NULL, req,
-                               callback ?: address_label_handler,
+                               address_label_handler,
                                link_netlink_destroy_callback, link);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
@@ -150,6 +133,34 @@ int address_label_configure(
         return 0;
 }
 
+int link_set_address_labels(Link *link) {
+        AddressLabel *label;
+        int r;
+
+        assert(link);
+        assert(link->network);
+
+        HASHMAP_FOREACH(label, link->network->address_labels_by_section) {
+                r = address_label_configure(label, link);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Could not set address label: %m");
+
+                link->address_label_messages++;
+        }
+
+        return 0;
+}
+
+void network_drop_invalid_address_labels(Network *network) {
+        AddressLabel *label;
+
+        assert(network);
+
+        HASHMAP_FOREACH(label, network->address_labels_by_section)
+                if (section_is_invalid(label->section))
+                        address_label_free(label);
+}
+
 int config_parse_address_label_prefix(const char *unit,
                                       const char *filename,
                                       unsigned line,
index 595072a17e6fc5992e9076e1e6536b319becd951..b92828c72e25084d14cf95cb7e2da98cdcdda7c7 100644 (file)
@@ -2,38 +2,28 @@
 #pragma once
 
 #include <inttypes.h>
-#include <stdbool.h>
 
 #include "conf-parser.h"
 #include "in-addr-util.h"
-
-typedef struct AddressLabel AddressLabel;
-
-#include "networkd-link.h"
-#include "networkd-network.h"
 #include "networkd-util.h"
 
 typedef struct Network Network;
 typedef struct Link Link;
-typedef struct NetworkConfigSection NetworkConfigSection;
 
-struct AddressLabel {
+typedef struct AddressLabel {
         Network *network;
         NetworkConfigSection *section;
 
         unsigned char prefixlen;
         uint32_t label;
-
         union in_addr_union in_addr;
+} AddressLabel;
 
-        LIST_FIELDS(AddressLabel, labels);
-};
-
-void address_label_free(AddressLabel *label);
+AddressLabel *address_label_free(AddressLabel *label);
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(AddressLabel, address_label_free);
+void network_drop_invalid_address_labels(Network *network);
 
-int address_label_configure(AddressLabel *address, Link *link, link_netlink_message_handler_t callback, bool update);
+int link_set_address_labels(Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_address_label);
 CONFIG_PARSER_PROTOTYPE(config_parse_address_label_prefix);
index b4a94f0728271cf613265475a0b8c7ae59610f67..c732b6c56e44d4817e3a3914cb666b73ad2a9e21 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "alloc-util.h"
 #include "networkd-address-pool.h"
+#include "networkd-address.h"
 #include "networkd-manager.h"
 #include "set.h"
 #include "string-util.h"
 
 static int address_pool_new(
                 Manager *m,
-                AddressPool **ret,
                 int family,
                 const union in_addr_union *u,
                 unsigned prefixlen) {
 
-        AddressPool *p;
+        _cleanup_free_ AddressPool *p = NULL;
+        int r;
 
         assert(m);
-        assert(ret);
         assert(u);
 
         p = new(AddressPool, 1);
@@ -32,15 +32,16 @@ static int address_pool_new(
                 .in_addr = *u,
         };
 
-        LIST_PREPEND(address_pools, m->address_pools, p);
+        r = ordered_set_ensure_put(&m->address_pools, NULL, p);
+        if (r < 0)
+                return r;
 
-        *ret = p;
+        TAKE_PTR(p);
         return 0;
 }
 
-int address_pool_new_from_string(
+static int address_pool_new_from_string(
                 Manager *m,
-                AddressPool **ret,
                 int family,
                 const char *p,
                 unsigned prefixlen) {
@@ -49,25 +50,38 @@ int address_pool_new_from_string(
         int r;
 
         assert(m);
-        assert(ret);
         assert(p);
 
         r = in_addr_from_string(family, p, &u);
         if (r < 0)
                 return r;
 
-        return address_pool_new(m, ret, family, &u, prefixlen);
+        return address_pool_new(m, family, &u, prefixlen);
 }
 
-void address_pool_free(AddressPool *p) {
+int address_pool_setup_default(Manager *m) {
+        int r;
 
-        if (!p)
-                return;
+        assert(m);
 
-        if (p->manager)
-                LIST_REMOVE(address_pools, p->manager->address_pools, p);
+        /* Add in the well-known private address ranges. */
+        r = address_pool_new_from_string(m, AF_INET6, "fd00::", 8);
+        if (r < 0)
+                return r;
+
+        r = address_pool_new_from_string(m, AF_INET, "192.168.0.0", 16);
+        if (r < 0)
+                return r;
+
+        r = address_pool_new_from_string(m, AF_INET, "172.16.0.0", 12);
+        if (r < 0)
+                return r;
+
+        r = address_pool_new_from_string(m, AF_INET, "10.0.0.0", 8);
+        if (r < 0)
+                return r;
 
-        free(p);
+        return 0;
 }
 
 static bool address_pool_prefix_is_taken(
@@ -94,7 +108,7 @@ static bool address_pool_prefix_is_taken(
                 }
 
                 /* Don't clash with addresses already pulled from the pool, but not assigned yet */
-                LIST_FOREACH(addresses, a, l->pool_addresses) {
+                SET_FOREACH(a, l->pool_addresses) {
                         if (a->family != p->family)
                                 continue;
 
@@ -107,7 +121,7 @@ static bool address_pool_prefix_is_taken(
         ORDERED_HASHMAP_FOREACH(n, p->manager->networks) {
                 Address *a;
 
-                LIST_FOREACH(addresses, a, n->static_addresses) {
+                ORDERED_HASHMAP_FOREACH(a, n->addresses_by_section) {
                         if (a->family != p->family)
                                 continue;
 
@@ -119,7 +133,7 @@ static bool address_pool_prefix_is_taken(
         return false;
 }
 
-int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found) {
+static int address_pool_acquire_one(AddressPool *p, int family, unsigned prefixlen, union in_addr_union *found) {
         union in_addr_union u;
         unsigned i;
         int r;
@@ -128,6 +142,9 @@ int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union
         assert(prefixlen > 0);
         assert(found);
 
+        if (p->family != family)
+                return 0;
+
         if (p->prefixlen >= prefixlen)
                 return 0;
 
@@ -153,3 +170,21 @@ int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union
 
         return 0;
 }
+
+int address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
+        AddressPool *p;
+        int r;
+
+        assert(m);
+        assert(IN_SET(family, AF_INET, AF_INET6));
+        assert(prefixlen > 0);
+        assert(found);
+
+        ORDERED_SET_FOREACH(p, m->address_pools) {
+                r = address_pool_acquire_one(p, family, prefixlen, found);
+                if (r != 0)
+                        return r;
+        }
+
+        return 0;
+}
index 7db1c4f26c17f6fee24a82b0c583ca25de107ef7..c53fe7407febbd3438444b985f2d840a1fc57aae 100644 (file)
@@ -1,25 +1,17 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
-typedef struct AddressPool AddressPool;
-
 #include "in-addr-util.h"
-#include "list.h"
 
 typedef struct Manager Manager;
 
-struct AddressPool {
+typedef struct AddressPool {
         Manager *manager;
 
         int family;
         unsigned prefixlen;
-
         union in_addr_union in_addr;
+} AddressPool;
 
-        LIST_FIELDS(AddressPool, address_pools);
-};
-
-int address_pool_new_from_string(Manager *m, AddressPool **ret, int family, const char *p, unsigned prefixlen);
-void address_pool_free(AddressPool *p);
-
-int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found);
+int address_pool_setup_default(Manager *m);
+int address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found);
index 76c91ef9dd2f2fb1b4d15ff5be47715e5a3d3956..7407eeec9bcdf89981c1d4135eab3e8ef280e6db 100644 (file)
@@ -3,20 +3,16 @@
 #include <net/if.h>
 
 #include "alloc-util.h"
-#include "conf-parser.h"
 #include "firewall-util.h"
 #include "memory-util.h"
-#include "missing_network.h"
 #include "netlink-util.h"
+#include "networkd-address-pool.h"
 #include "networkd-address.h"
 #include "networkd-manager.h"
-#include "networkd-ndisc.h"
+#include "networkd-network.h"
 #include "parse-util.h"
-#include "set.h"
-#include "socket-util.h"
 #include "string-util.h"
 #include "strv.h"
-#include "utf8.h"
 
 #define ADDRESSES_PER_LINK_MAX 2048U
 #define STATIC_ADDRESSES_PER_NETWORK_MAX 1024U
@@ -52,7 +48,6 @@ int address_new(Address **ret) {
                 .cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME,
                 .cinfo.ifa_valid = CACHE_INFO_INFINITY_LIFE_TIME,
                 .duplicate_address_detection = ADDRESS_FAMILY_IPV6,
-                .prefix_route = true,
         };
 
         *ret = TAKE_PTR(address);
@@ -67,22 +62,20 @@ static int address_new_static(Network *network, const char *filename, unsigned s
 
         assert(network);
         assert(ret);
-        assert(!!filename == (section_line > 0));
-
-        if (filename) {
-                r = network_config_section_new(filename, section_line, &n);
-                if (r < 0)
-                        return r;
+        assert(filename);
+        assert(section_line > 0);
 
-                address = hashmap_get(network->addresses_by_section, n);
-                if (address) {
-                        *ret = TAKE_PTR(address);
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
 
-                        return 0;
-                }
+        address = ordered_hashmap_get(network->addresses_by_section, n);
+        if (address) {
+                *ret = TAKE_PTR(address);
+                return 0;
         }
 
-        if (network->n_static_addresses >= STATIC_ADDRESSES_PER_NETWORK_MAX)
+        if (ordered_hashmap_size(network->addresses_by_section) >= STATIC_ADDRESSES_PER_NETWORK_MAX)
                 return -E2BIG;
 
         r = address_new(&address);
@@ -90,40 +83,30 @@ static int address_new_static(Network *network, const char *filename, unsigned s
                 return r;
 
         address->network = network;
-        LIST_APPEND(addresses, network->static_addresses, address);
-        network->n_static_addresses++;
+        address->section = TAKE_PTR(n);
 
-        if (filename) {
-                address->section = TAKE_PTR(n);
-
-                r = hashmap_ensure_allocated(&network->addresses_by_section, &network_config_hash_ops);
-                if (r < 0)
-                        return r;
+        r = ordered_hashmap_ensure_allocated(&network->addresses_by_section, &network_config_hash_ops);
+        if (r < 0)
+                return r;
 
-                r = hashmap_put(network->addresses_by_section, address->section, address);
-                if (r < 0)
-                        return r;
-        }
+        r = ordered_hashmap_put(network->addresses_by_section, address->section, address);
+        if (r < 0)
+                return r;
 
         *ret = TAKE_PTR(address);
-
         return 0;
 }
 
-void address_free(Address *address) {
+Address *address_free(Address *address) {
         if (!address)
-                return;
+                return NULL;
 
         if (address->network) {
-                LIST_REMOVE(addresses, address->network->static_addresses, address);
-                assert(address->network->n_static_addresses > 0);
-                address->network->n_static_addresses--;
-
-                if (address->section)
-                        hashmap_remove(address->network->addresses_by_section, address->section);
+                assert(address->section);
+                ordered_hashmap_remove(address->network->addresses_by_section, address->section);
         }
 
-        if (address->link && !address->acd) {
+        if (address->link) {
                 NDiscAddress *n;
 
                 set_remove(address->link->addresses, address);
@@ -149,7 +132,7 @@ void address_free(Address *address) {
 
         network_config_section_free(address->section);
         free(address->label);
-        free(address);
+        return mfree(address);
 }
 
 static uint32_t address_prefix(const Address *a) {
@@ -232,29 +215,61 @@ bool address_equal(Address *a1, Address *a2) {
         return address_compare_func(a1, a2) == 0;
 }
 
-static int address_establish(Address *address, Link *link) {
-        bool masq;
+static int address_copy(Address *dest, const Address *src) {
+        int r;
+
+        assert(dest);
+        assert(src);
+
+        r = free_and_strdup(&dest->label, src->label);
+        if (r < 0)
+                return r;
+
+        dest->family = src->family;
+        dest->prefixlen = src->prefixlen;
+        dest->scope = src->scope;
+        dest->flags = src->flags;
+        dest->broadcast = src->broadcast;
+        dest->cinfo = src->cinfo;
+        dest->in_addr = src->in_addr;
+        dest->in_addr_peer = src->in_addr_peer;
+        dest->duplicate_address_detection = src->duplicate_address_detection;
+
+        return 0;
+}
+
+static int address_set_masquerade(Address *address, bool add) {
+        union in_addr_union masked;
         int r;
 
         assert(address);
-        assert(link);
+        assert(address->link);
 
-        masq = link->network &&
-               link->network->ip_masquerade &&
-               address->family == AF_INET &&
-               address->scope < RT_SCOPE_LINK;
+        if (!address->link->network)
+                return 0;
 
-        /* Add firewall entry if this is requested */
-        if (address->ip_masquerade_done != masq) {
-                union in_addr_union masked = address->in_addr;
-                in_addr_mask(address->family, &masked, address->prefixlen);
+        if (!address->link->network->ip_masquerade)
+                return 0;
 
-                r = fw_add_masquerade(masq, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0);
-                if (r < 0)
-                        return r;
+        if (address->family != AF_INET)
+                return 0;
 
-                address->ip_masquerade_done = masq;
-        }
+        if (address->scope >= RT_SCOPE_LINK)
+                return 0;
+
+        if (address->ip_masquerade_done == add)
+                return 0;
+
+        masked = address->in_addr;
+        r = in_addr_mask(address->family, &masked, address->prefixlen);
+        if (r < 0)
+                return r;
+
+        r = fw_add_masquerade(add, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0);
+        if (r < 0)
+                return r;
+
+        address->ip_masquerade_done = add;
 
         return 0;
 }
@@ -295,11 +310,11 @@ static int address_add_internal(Link *link, Set **addresses,
         return 0;
 }
 
-int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) {
+static int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) {
         return address_add_internal(link, &link->addresses_foreign, family, in_addr, prefixlen, ret);
 }
 
-int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) {
+static int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) {
         Address *address;
         int r;
 
@@ -328,28 +343,7 @@ int address_add(Link *link, int family, const union in_addr_union *in_addr, unsi
         return 0;
 }
 
-static int address_release(Address *address) {
-        int r;
-
-        assert(address);
-        assert(address->link);
-
-        /* Remove masquerading firewall entry if it was added */
-        if (address->ip_masquerade_done) {
-                union in_addr_union masked = address->in_addr;
-                in_addr_mask(address->family, &masked, address->prefixlen);
-
-                r = fw_add_masquerade(false, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0);
-                if (r < 0)
-                        return r;
-
-                address->ip_masquerade_done = false;
-        }
-
-        return 0;
-}
-
-int address_update(
+static int address_update(
                 Address *address,
                 unsigned char flags,
                 unsigned char scope,
@@ -394,7 +388,7 @@ int address_update(
         return 0;
 }
 
-int address_drop(Address *address) {
+static int address_drop(Address *address) {
         Link *link;
         bool ready;
         int r;
@@ -404,7 +398,7 @@ int address_drop(Address *address) {
         ready = address_is_ready(address);
         link = address->link;
 
-        r = address_release(address);
+        r = address_set_masquerade(address, false);
         if (r < 0)
                 log_link_warning_errno(link, r, "Failed to disable IP masquerading, ignoring: %m");
 
@@ -542,7 +536,192 @@ int address_remove(
         return 0;
 }
 
-static int address_acquire(Link *link, Address *original, Address **ret) {
+static bool link_is_static_address_configured(Link *link, Address *address) {
+        Address *net_address;
+
+        assert(link);
+        assert(address);
+
+        if (!link->network)
+                return false;
+
+        ORDERED_HASHMAP_FOREACH(net_address, link->network->addresses_by_section)
+                if (address_equal(net_address, address))
+                        return true;
+                else if (address->family == AF_INET6 && net_address->family == AF_INET6 &&
+                         in_addr_equal(AF_INET6, &address->in_addr, &net_address->in_addr_peer) > 0)
+                        return true;
+
+        return false;
+}
+
+static bool link_address_is_dynamic(Link *link, Address *address) {
+        Route *route;
+
+        assert(link);
+        assert(address);
+
+        if (address->cinfo.ifa_prefered != CACHE_INFO_INFINITY_LIFE_TIME)
+                return true;
+
+        /* Even when the address is leased from a DHCP server, networkd assign the address
+         * without lifetime when KeepConfiguration=dhcp. So, let's check that we have
+         * corresponding routes with RTPROT_DHCP. */
+        SET_FOREACH(route, link->routes_foreign) {
+                if (route->protocol != RTPROT_DHCP)
+                        continue;
+
+                if (address->family != route->family)
+                        continue;
+
+                if (in_addr_equal(address->family, &address->in_addr, &route->prefsrc))
+                        return true;
+        }
+
+        return false;
+}
+
+static int link_enumerate_ipv6_tentative_addresses(Link *link) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+        sd_netlink_message *addr;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(link->manager->rtnl);
+
+        r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_GETADDR, 0, AF_INET6);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_call(link->manager->rtnl, req, 0, &reply);
+        if (r < 0)
+                return r;
+
+        for (addr = reply; addr; addr = sd_netlink_message_next(addr)) {
+                unsigned char flags;
+                int ifindex;
+
+                r = sd_rtnl_message_addr_get_ifindex(addr, &ifindex);
+                if (r < 0) {
+                        log_link_warning_errno(link, r, "rtnl: invalid ifindex, ignoring: %m");
+                        continue;
+                } else if (link->ifindex != ifindex)
+                        continue;
+
+                r = sd_rtnl_message_addr_get_flags(addr, &flags);
+                if (r < 0) {
+                        log_link_warning_errno(link, r, "rtnl: received address message with invalid flags, ignoring: %m");
+                        continue;
+                } else if (!(flags & IFA_F_TENTATIVE))
+                        continue;
+
+                log_link_debug(link, "Found tentative ipv6 link-local address");
+                (void) manager_rtnl_process_address(link->manager->rtnl, addr, link->manager);
+        }
+
+        return 0;
+}
+
+int link_drop_foreign_addresses(Link *link) {
+        Address *address;
+        int k, r = 0;
+
+        assert(link);
+
+        /* The kernel doesn't notify us about tentative addresses;
+         * so if ipv6ll is disabled, we need to enumerate them now so we can drop them below */
+        if (!link_ipv6ll_enabled(link)) {
+                r = link_enumerate_ipv6_tentative_addresses(link);
+                if (r < 0)
+                        return r;
+        }
+
+        SET_FOREACH(address, link->addresses_foreign) {
+                /* we consider IPv6LL addresses to be managed by the kernel */
+                if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1 && link_ipv6ll_enabled(link))
+                        continue;
+
+                if (link_address_is_dynamic(link, address)) {
+                        if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
+                                continue;
+                } else if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
+                        continue;
+
+                if (link_is_static_address_configured(link, address)) {
+                        k = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL);
+                        if (k < 0) {
+                                log_link_error_errno(link, k, "Failed to add address: %m");
+                                if (r >= 0)
+                                        r = k;
+                        }
+                } else {
+                        k = address_remove(address, link, NULL);
+                        if (k < 0 && r >= 0)
+                                r = k;
+                }
+        }
+
+        return r;
+}
+
+static int remove_static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(m);
+        assert(link);
+        assert(link->ifname);
+        assert(link->address_remove_messages > 0);
+
+        link->address_remove_messages--;
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EADDRNOTAVAIL)
+                log_link_message_warning_errno(link, m, r, "Could not drop address");
+        else if (r >= 0)
+                (void) manager_rtnl_process_address(rtnl, m, link->manager);
+
+        if (link->address_remove_messages == 0 && link->request_static_addresses) {
+                link_set_state(link, LINK_STATE_CONFIGURING);
+                r = link_set_addresses(link);
+                if (r < 0)
+                        link_enter_failed(link);
+        }
+
+        return 1;
+}
+
+int link_drop_addresses(Link *link) {
+        Address *address, *pool_address;
+        int k, r = 0;
+
+        assert(link);
+
+        SET_FOREACH(address, link->addresses) {
+                /* we consider IPv6LL addresses to be managed by the kernel */
+                if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1 && link_ipv6ll_enabled(link))
+                        continue;
+
+                k = address_remove(address, link, remove_static_address_handler);
+                if (k < 0 && r >= 0) {
+                        r = k;
+                        continue;
+                }
+
+                link->address_remove_messages++;
+
+                SET_FOREACH(pool_address, link->pool_addresses)
+                        if (address_equal(address, pool_address))
+                                address_free(set_remove(link->pool_addresses, pool_address));
+        }
+
+        return r;
+}
+
+static int address_acquire(Link *link, const Address *original, Address **ret) {
         union in_addr_union in_addr = IN_ADDR_NULL;
         struct in_addr broadcast = {};
         _cleanup_(address_freep) Address *na = NULL;
@@ -554,12 +733,16 @@ static int address_acquire(Link *link, Address *original, Address **ret) {
 
         /* Something useful was configured? just use it */
         r = in_addr_is_null(original->family, &original->in_addr);
-        if (r <= 0)
+        if (r < 0)
                 return r;
+        if (r == 0) {
+                *ret = NULL;
+                return 0;
+        }
 
         /* The address is configured to be 0.0.0.0 or [::] by the user?
          * Then let's acquire something more useful from the pool. */
-        r = manager_address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr);
+        r = address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -581,27 +764,25 @@ static int address_acquire(Link *link, Address *original, Address **ret) {
         if (r < 0)
                 return r;
 
-        na->family = original->family;
-        na->prefixlen = original->prefixlen;
-        na->scope = original->scope;
-        na->cinfo = original->cinfo;
-
-        if (original->label) {
-                na->label = strdup(original->label);
-                if (!na->label)
-                        return -ENOMEM;
-        }
+        r = address_copy(na, original);
+        if (r < 0)
+                return r;
 
         na->broadcast = broadcast;
         na->in_addr = in_addr;
 
-        LIST_PREPEND(addresses, link->pool_addresses, na);
+        r = set_ensure_put(&link->pool_addresses, &address_hash_ops, na);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EEXIST;
 
         *ret = TAKE_PTR(na);
-
-        return 0;
+        return 1;
 }
 
+static int ipv4_dad_configure(Address *address);
+
 int address_configure(
                 Address *address,
                 Link *link,
@@ -610,7 +791,8 @@ int address_configure(
                 Address **ret) {
 
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
-        Address *a;
+        Address *acquired_address, *a;
+        uint32_t flags;
         int r;
 
         assert(address);
@@ -627,9 +809,11 @@ int address_configure(
                 return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
                                             "Too many addresses are configured, refusing: %m");
 
-        r = address_acquire(link, address, &address);
+        r = address_acquire(link, address, &acquired_address);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to acquire an address from pool: %m");
+        if (acquired_address)
+                address = acquired_address;
 
         if (DEBUG_LOGGING) {
                 _cleanup_free_ char *str = NULL;
@@ -651,29 +835,13 @@ int address_configure(
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not set prefixlen: %m");
 
-        address->flags |= IFA_F_PERMANENT;
-
-        if (address->home_address)
-                address->flags |= IFA_F_HOMEADDRESS;
-
-        if (!FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV6))
-                address->flags |= IFA_F_NODAD;
-
-        if (address->manage_temporary_address)
-                address->flags |= IFA_F_MANAGETEMPADDR;
-
-        if (!address->prefix_route)
-                address->flags |= IFA_F_NOPREFIXROUTE;
-
-        if (address->autojoin)
-                address->flags |= IFA_F_MCAUTOJOIN;
-
-        r = sd_rtnl_message_addr_set_flags(req, (address->flags & 0xff));
+        flags = address->flags | IFA_F_PERMANENT;
+        r = sd_rtnl_message_addr_set_flags(req, flags & 0xff);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not set flags: %m");
 
-        if (address->flags & ~0xff) {
-                r = sd_netlink_message_append_u32(req, IFA_FLAGS, address->flags);
+        if (flags & ~0xff) {
+                r = sd_netlink_message_append_u32(req, IFA_FLAGS, flags);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Could not set extended flags: %m");
         }
@@ -706,37 +874,28 @@ int address_configure(
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not append IFA_CACHEINFO attribute: %m");
 
-        r = address_establish(address, link);
+        if (address->family == AF_INET6 && !in_addr_is_null(address->family, &address->in_addr_peer))
+                r = address_add(link, address->family, &address->in_addr_peer, address->prefixlen, &a);
+        else
+                r = address_add(link, address->family, &address->in_addr, address->prefixlen, &a);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not add address: %m");
+
+        a->scope = address->scope;
+        r = address_set_masquerade(a, true);
         if (r < 0)
                 log_link_warning_errno(link, r, "Could not enable IP masquerading, ignoring: %m");
 
         r = netlink_call_async(link->manager->rtnl, NULL, req, callback, link_netlink_destroy_callback, link);
         if (r < 0) {
-                address_release(address);
+                (void) address_set_masquerade(a, false);
                 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
         }
 
         link_ref(link);
 
-        if (address->family == AF_INET6 && !in_addr_is_null(address->family, &address->in_addr_peer))
-                r = address_add(link, address->family, &address->in_addr_peer, address->prefixlen, &a);
-        else
-                r = address_add(link, address->family, &address->in_addr, address->prefixlen, &a);
-        if (r < 0) {
-                address_release(address);
-                return log_link_error_errno(link, r, "Could not add address: %m");
-        }
-
-        if (address->acd) {
-                assert(address->family == AF_INET);
-                if (DEBUG_LOGGING) {
-                        _cleanup_free_ char *pretty = NULL;
-
-                        (void) in_addr_to_string(address->family, &address->in_addr, &pretty);
-                        log_link_debug(link, "Starting IPv4ACD client. Probing address %s", strna(pretty));
-                }
-
-                r = sd_ipv4acd_start(address->acd, true);
+        if (FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4)) {
+                r = ipv4_dad_configure(a);
                 if (r < 0)
                         log_link_warning_errno(link, r, "Failed to start IPv4ACD client, ignoring: %m");
         }
@@ -747,70 +906,475 @@ int address_configure(
         return 1;
 }
 
-static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
-        _cleanup_free_ char *pretty = NULL;
-        Address *address;
+static int static_address_ready_callback(Address *address) {
+        Address *a;
         Link *link;
-        int r;
 
-        assert(acd);
-        assert(userdata);
+        assert(address);
+        assert(address->link);
 
-        address = (Address *) userdata;
         link = address->link;
 
-        (void) in_addr_to_string(address->family, &address->in_addr, &pretty);
-        switch (event) {
-        case SD_IPV4ACD_EVENT_STOP:
-                log_link_debug(link, "Stopping ACD client...");
-                return;
-
-        case SD_IPV4ACD_EVENT_BIND:
-                log_link_debug(link, "Successfully claimed address %s", strna(pretty));
-                link_check_ready(link);
-                break;
+        if (!link->addresses_configured)
+                return 0;
 
-        case SD_IPV4ACD_EVENT_CONFLICT:
-                log_link_warning(link, "DAD conflict. Dropping address %s", strna(pretty));
-                r = address_remove(address, link, NULL);
-                if (r < 0)
-                        log_link_error_errno(link, r, "Failed to drop DAD conflicted address %s", strna(pretty));;
+        SET_FOREACH(a, link->static_addresses)
+                if (!address_is_ready(a)) {
+                        _cleanup_free_ char *str = NULL;
 
-                link_check_ready(link);
-                break;
+                        (void) in_addr_to_string(a->family, &a->in_addr, &str);
+                        log_link_debug(link, "an address %s/%u is not ready", strnull(str), a->prefixlen);
+                        return 0;
+                }
 
-        default:
-                assert_not_reached("Invalid IPv4ACD event.");
-        }
+        /* This should not be called again */
+        SET_FOREACH(a, link->static_addresses)
+                a->callback = NULL;
 
-        sd_ipv4acd_stop(acd);
+        link->addresses_ready = true;
 
-        return;
+        return link_set_routes(link);
 }
 
-int configure_ipv4_duplicate_address_detection(Link *link, Address *address) {
+static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
+        assert(rtnl);
+        assert(m);
         assert(link);
-        assert(address);
-        assert(address->family == AF_INET);
-        assert(!address->link && address->network);
+        assert(link->ifname);
+        assert(link->address_messages > 0);
+        assert(IN_SET(link->state, LINK_STATE_CONFIGURING,
+               LINK_STATE_FAILED, LINK_STATE_LINGER));
 
-        address->link = link;
+        link->address_messages--;
 
-        r = sd_ipv4acd_new(&address->acd);
-        if (r < 0)
-                return r;
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EEXIST) {
+                log_link_message_warning_errno(link, m, r, "Could not set address");
+                link_enter_failed(link);
+                return 1;
+        } else if (r >= 0)
+                (void) manager_rtnl_process_address(rtnl, m, link->manager);
+
+        if (link->address_messages == 0) {
+                Address *a;
+
+                log_link_debug(link, "Addresses set");
+                link->addresses_configured = true;
+
+                /* When all static addresses are already ready, then static_address_ready_callback()
+                 * will not be called automatically. So, call it here. */
+                a = set_first(link->static_addresses);
+                if (!a) {
+                        log_link_warning(link, "No static address is stored.");
+                        link_enter_failed(link);
+                        return 1;
+                }
+                if (!a->callback) {
+                        log_link_warning(link, "Address ready callback is not set.");
+                        link_enter_failed(link);
+                        return 1;
+                }
+                r = a->callback(a);
+                if (r < 0)
+                        link_enter_failed(link);
+        }
+
+        return 1;
+}
+
+static int static_address_configure(Address *address, Link *link, bool update) {
+        Address *ret;
+        int r;
+
+        assert(address);
+        assert(link);
 
-        r = sd_ipv4acd_attach_event(address->acd, NULL, 0);
+        r = address_configure(address, link, address_handler, update, &ret);
         if (r < 0)
-                return r;
+                return log_link_warning_errno(link, r, "Could not configure static address: %m");
+
+        link->address_messages++;
+
+        r = set_ensure_put(&link->static_addresses, &address_hash_ops, ret);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to store static address: %m");
+
+        ret->callback = static_address_ready_callback;
+
+        return 0;
+}
+
+int link_set_addresses(Link *link) {
+        Address *ad;
+        int r;
+
+        assert(link);
+        assert(link->network);
+
+        if (link->address_remove_messages != 0) {
+                log_link_debug(link, "Removing old addresses, new addresses will be configured later.");
+                link->request_static_addresses = true;
+                return 0;
+        }
+
+        ORDERED_HASHMAP_FOREACH(ad, link->network->addresses_by_section) {
+                bool update;
+
+                if (ad->family == AF_INET6 && !in_addr_is_null(ad->family, &ad->in_addr_peer))
+                        update = address_get(link, ad->family, &ad->in_addr_peer, ad->prefixlen, NULL) > 0;
+                else
+                        update = address_get(link, ad->family, &ad->in_addr, ad->prefixlen, NULL) > 0;
+
+                r = static_address_configure(ad, link, update);
+                if (r < 0)
+                        return r;
+        }
+
+        if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC) {
+                Prefix *p;
+
+                HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
+                        _cleanup_(address_freep) Address *address = NULL;
+
+                        if (!p->assign)
+                                continue;
+
+                        r = address_new(&address);
+                        if (r < 0)
+                                return log_oom();
+
+                        r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not get RA prefix: %m");
+
+                        r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not generate EUI64 address: %m");
+
+                        address->family = AF_INET6;
+                        r = static_address_configure(address, link, true);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        if (link->address_messages == 0) {
+                link->addresses_configured = true;
+                link->addresses_ready = true;
+                r = link_set_routes(link);
+                if (r < 0)
+                        return r;
+        } else {
+                log_link_debug(link, "Setting addresses");
+                link_set_state(link, LINK_STATE_CONFIGURING);
+        }
+
+        return 0;
+}
+
+int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
+        _cleanup_free_ char *buf = NULL;
+        Link *link = NULL;
+        uint16_t type;
+        unsigned char flags, prefixlen, scope;
+        union in_addr_union in_addr = IN_ADDR_NULL;
+        struct ifa_cacheinfo cinfo;
+        Address *address = NULL;
+        char valid_buf[FORMAT_TIMESPAN_MAX];
+        const char *valid_str = NULL;
+        int ifindex, family, r;
+
+        assert(rtnl);
+        assert(message);
+        assert(m);
+
+        if (sd_netlink_message_is_error(message)) {
+                r = sd_netlink_message_get_errno(message);
+                if (r < 0)
+                        log_message_warning_errno(message, r, "rtnl: failed to receive address message, ignoring");
+
+                return 0;
+        }
+
+        r = sd_netlink_message_get_type(message, &type);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
+                return 0;
+        } else if (!IN_SET(type, RTM_NEWADDR, RTM_DELADDR)) {
+                log_warning("rtnl: received unexpected message type %u when processing address, ignoring.", type);
+                return 0;
+        }
+
+        r = sd_rtnl_message_addr_get_ifindex(message, &ifindex);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get ifindex from message, ignoring: %m");
+                return 0;
+        } else if (ifindex <= 0) {
+                log_warning("rtnl: received address message with invalid ifindex %d, ignoring.", ifindex);
+                return 0;
+        }
+
+        r = link_get(m, ifindex, &link);
+        if (r < 0 || !link) {
+                /* when enumerating we might be out of sync, but we will get the address again, so just
+                 * ignore it */
+                if (!m->enumerating)
+                        log_warning("rtnl: received address for link '%d' we don't know about, ignoring.", ifindex);
+                return 0;
+        }
+
+        r = sd_rtnl_message_addr_get_family(message, &family);
+        if (r < 0) {
+                log_link_warning(link, "rtnl: received address message without family, ignoring.");
+                return 0;
+        } else if (!IN_SET(family, AF_INET, AF_INET6)) {
+                log_link_debug(link, "rtnl: received address message with invalid family '%i', ignoring.", family);
+                return 0;
+        }
+
+        r = sd_rtnl_message_addr_get_prefixlen(message, &prefixlen);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received address message without prefixlen, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_rtnl_message_addr_get_scope(message, &scope);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received address message without scope, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_rtnl_message_addr_get_flags(message, &flags);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received address message without flags, ignoring: %m");
+                return 0;
+        }
+
+        switch (family) {
+        case AF_INET:
+                r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &in_addr.in);
+                if (r < 0) {
+                        log_link_warning_errno(link, r, "rtnl: received address message without valid address, ignoring: %m");
+                        return 0;
+                }
+
+                break;
+
+        case AF_INET6:
+                r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &in_addr.in6);
+                if (r < 0) {
+                        log_link_warning_errno(link, r, "rtnl: received address message without valid address, ignoring: %m");
+                        return 0;
+                }
+
+                break;
+
+        default:
+                assert_not_reached("Received unsupported address family");
+        }
+
+        (void) in_addr_to_string(family, &in_addr, &buf);
+
+        r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo);
+        if (r < 0 && r != -ENODATA) {
+                log_link_warning_errno(link, r, "rtnl: cannot get IFA_CACHEINFO attribute, ignoring: %m");
+                return 0;
+        } else if (r >= 0 && cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME)
+                valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
+                                            cinfo.ifa_valid * USEC_PER_SEC,
+                                            USEC_PER_SEC);
+
+        (void) address_get(link, family, &in_addr, prefixlen, &address);
+
+        switch (type) {
+        case RTM_NEWADDR:
+                if (address)
+                        log_link_debug(link, "Remembering updated address: %s/%u (valid %s%s)",
+                                       strnull(buf), prefixlen,
+                                       valid_str ? "for " : "forever", strempty(valid_str));
+                else {
+                        /* An address appeared that we did not request */
+                        r = address_add_foreign(link, family, &in_addr, prefixlen, &address);
+                        if (r < 0) {
+                                log_link_warning_errno(link, r, "Failed to remember foreign address %s/%u, ignoring: %m",
+                                                       strnull(buf), prefixlen);
+                                return 0;
+                        } else
+                                log_link_debug(link, "Remembering foreign address: %s/%u (valid %s%s)",
+                                               strnull(buf), prefixlen,
+                                               valid_str ? "for " : "forever", strempty(valid_str));
+                }
+
+                /* address_update() logs internally, so we don't need to here. */
+                r = address_update(address, flags, scope, &cinfo);
+                if (r < 0)
+                        link_enter_failed(link);
+
+                break;
+
+        case RTM_DELADDR:
+                if (address) {
+                        log_link_debug(link, "Forgetting address: %s/%u (valid %s%s)",
+                                       strnull(buf), prefixlen,
+                                       valid_str ? "for " : "forever", strempty(valid_str));
+                        (void) address_drop(address);
+                } else
+                        log_link_debug(link, "Kernel removed an address we don't remember: %s/%u (valid %s%s), ignoring.",
+                                       strnull(buf), prefixlen,
+                                       valid_str ? "for " : "forever", strempty(valid_str));
+
+                break;
+
+        default:
+                assert_not_reached("Received invalid RTNL message type");
+        }
+
+        return 1;
+}
+
+int link_serialize_addresses(Link *link, FILE *f) {
+        bool space = false;
+        Address *a;
+
+        assert(link);
+
+        fputs("ADDRESSES=", f);
+        SET_FOREACH(a, link->addresses) {
+                _cleanup_free_ char *address_str = NULL;
+
+                if (in_addr_to_string(a->family, &a->in_addr, &address_str) < 0)
+                        continue;
+
+                fprintf(f, "%s%s/%u", space ? " " : "", address_str, a->prefixlen);
+                space = true;
+        }
+        fputc('\n', f);
+
+        return 0;
+}
+
+int link_deserialize_addresses(Link *link, const char *addresses) {
+        int r;
+
+        assert(link);
+
+        for (const char *p = addresses;; ) {
+                _cleanup_free_ char *address_str = NULL;
+                union in_addr_union address;
+                unsigned char prefixlen;
+                char *prefixlen_str;
+                int family;
+
+                r = extract_first_word(&p, &address_str, NULL, 0);
+                if (r < 0)
+                        return log_link_debug_errno(link, r, "Failed to parse ADDRESSES=: %m");
+                if (r == 0)
+                        return 0;
+
+                prefixlen_str = strchr(address_str, '/');
+                if (!prefixlen_str) {
+                        log_link_debug(link, "Failed to parse address and prefix length, ignoring: %s", address_str);
+                        continue;
+                }
+                *prefixlen_str++ = '\0';
+
+                r = sscanf(prefixlen_str, "%hhu", &prefixlen);
+                if (r != 1) {
+                        log_link_debug(link, "Failed to parse prefixlen: %s", prefixlen_str);
+                        continue;
+                }
+
+                r = in_addr_from_string_auto(address_str, &family, &address);
+                if (r < 0) {
+                        log_link_debug_errno(link, r, "Failed to parse address: %s", address_str);
+                        continue;
+                }
+
+                r = address_add(link, family, &address, prefixlen, NULL);
+                if (r < 0)
+                        log_link_debug_errno(link, r, "Failed to add address: %m");
+        }
+
+        return 0;
+}
+
+static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
+        _cleanup_free_ char *pretty = NULL;
+        Address *address;
+        Link *link;
+        int r;
+
+        assert(acd);
+        assert(userdata);
+
+        address = (Address *) userdata;
+        link = address->link;
+
+        (void) in_addr_to_string(address->family, &address->in_addr, &pretty);
+        switch (event) {
+        case SD_IPV4ACD_EVENT_STOP:
+                log_link_debug(link, "Stopping ACD client...");
+                return;
+
+        case SD_IPV4ACD_EVENT_BIND:
+                log_link_debug(link, "Successfully claimed address %s", strna(pretty));
+                link_check_ready(link);
+                break;
+
+        case SD_IPV4ACD_EVENT_CONFLICT:
+                log_link_warning(link, "DAD conflict. Dropping address %s", strna(pretty));
+                r = address_remove(address, link, NULL);
+                if (r < 0)
+                        log_link_error_errno(link, r, "Failed to drop DAD conflicted address %s", strna(pretty));;
 
-        r = sd_ipv4acd_set_ifindex(address->acd, link->ifindex);
+                link_check_ready(link);
+                break;
+
+        default:
+                assert_not_reached("Invalid IPv4ACD event.");
+        }
+
+        sd_ipv4acd_stop(acd);
+
+        return;
+}
+
+static int ipv4_dad_configure(Address *address) {
+        int r;
+
+        assert(address);
+        assert(address->link);
+
+        if (address->family != AF_INET)
+                return 0;
+
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *pretty = NULL;
+
+                (void) in_addr_to_string(address->family, &address->in_addr, &pretty);
+                log_link_debug(address->link, "Starting IPv4ACD client. Probing address %s", strna(pretty));
+        }
+
+        if (!address->acd) {
+                r = sd_ipv4acd_new(&address->acd);
+                if (r < 0)
+                        return r;
+
+                r = sd_ipv4acd_attach_event(address->acd, address->link->manager->event, 0);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_ipv4acd_set_ifindex(address->acd, address->link->ifindex);
         if (r < 0)
                 return r;
 
-        r = sd_ipv4acd_set_mac(address->acd, &link->mac);
+        r = sd_ipv4acd_set_mac(address->acd, &address->link->mac);
         if (r < 0)
                 return r;
 
@@ -822,9 +1386,72 @@ int configure_ipv4_duplicate_address_detection(Link *link, Address *address) {
         if (r < 0)
                 return r;
 
+        return sd_ipv4acd_start(address->acd, true);
+}
+
+static int ipv4_dad_update_mac_one(Address *address) {
+        bool running;
+        int r;
+
+        assert(address);
+
+        if (!address->acd)
+                return 0;
+
+        running = sd_ipv4acd_is_running(address->acd);
+
+        if (running) {
+                r = sd_ipv4acd_stop(address->acd);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_ipv4acd_set_mac(address->acd, &address->link->mac);
+        if (r < 0)
+                return r;
+
+        if (running) {
+                r = sd_ipv4acd_start(address->acd, true);
+                if (r < 0)
+                        return r;
+        }
+
         return 0;
 }
 
+int ipv4_dad_update_mac(Link *link) {
+        Address *address;
+        int k, r = 0;
+
+        assert(link);
+
+        SET_FOREACH(address, link->addresses) {
+                k = ipv4_dad_update_mac_one(address);
+                if (k < 0 && r >= 0)
+                        r = k;
+        }
+
+        return r;
+}
+
+int ipv4_dad_stop(Link *link) {
+        Address *address;
+        int k, r = 0;
+
+        assert(link);
+
+        SET_FOREACH(address, link->addresses) {
+                if (!address->acd)
+                        continue;
+
+                k = sd_ipv4acd_stop(address->acd);
+                if (k < 0 && r >= 0)
+                        r = k;
+        }
+
+        return r;
+}
+
 int config_parse_broadcast(
                 const char *unit,
                 const char *filename,
@@ -875,7 +1502,8 @@ int config_parse_broadcast(
         return 0;
 }
 
-int config_parse_address(const char *unit,
+int config_parse_address(
+                const char *unit,
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -898,11 +1526,10 @@ int config_parse_address(const char *unit,
         assert(rvalue);
         assert(data);
 
-        if (streq(section, "Network")) {
-                /* we are not in an Address section, so treat
-                 * this as the special '0' section */
-                r = address_new_static(network, NULL, 0, &n);
-        } else
+        if (streq(section, "Network"))
+                /* we are not in an Address section, so use line number instead. */
+                r = address_new_static(network, filename, line, &n);
+        else
                 r = address_new_static(network, filename, section_line, &n);
         if (r == -ENOMEM)
                 return log_oom();
@@ -1006,16 +1633,18 @@ int config_parse_label(
         return 0;
 }
 
-int config_parse_lifetime(const char *unit,
-                          const char *filename,
-                          unsigned line,
-                          const char *section,
-                          unsigned section_line,
-                          const char *lvalue,
-                          int ltype,
-                          const char *rvalue,
-                          void *data,
-                          void *userdata) {
+int config_parse_lifetime(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
         Network *network = userdata;
         _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
         uint32_t k;
@@ -1053,16 +1682,18 @@ int config_parse_lifetime(const char *unit,
         return 0;
 }
 
-int config_parse_address_flags(const char *unit,
-                               const char *filename,
-                               unsigned line,
-                               const char *section,
-                               unsigned section_line,
-                               const char *lvalue,
-                               int ltype,
-                               const char *rvalue,
-                               void *data,
-                               void *userdata) {
+int config_parse_address_flags(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
         Network *network = userdata;
         _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
         int r;
@@ -1089,33 +1720,27 @@ int config_parse_address_flags(const char *unit,
                 return 0;
         }
 
-        if (streq(lvalue, "HomeAddress"))
-                n->home_address = r;
-        else if (streq(lvalue, "ManageTemporaryAddress"))
-                n->manage_temporary_address = r;
-        else if (streq(lvalue, "PrefixRoute"))
-                n->prefix_route = !r;
-        else if (streq(lvalue, "AddPrefixRoute"))
-                n->prefix_route = r;
-        else if (streq(lvalue, "AutoJoin"))
-                n->autojoin = r;
-        else
-                assert_not_reached("Invalid address flag type.");
+        if (streq(lvalue, "AddPrefixRoute"))
+                r = !r;
+
+        SET_FLAG(n->flags, ltype, r);
 
         n = NULL;
         return 0;
 }
 
-int config_parse_address_scope(const char *unit,
-                               const char *filename,
-                               unsigned line,
-                               const char *section,
-                               unsigned section_line,
-                               const char *lvalue,
-                               int ltype,
-                               const char *rvalue,
-                               void *data,
-                               void *userdata) {
+int config_parse_address_scope(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
         Network *network = userdata;
         _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
         int r;
@@ -1166,6 +1791,7 @@ int config_parse_duplicate_address_detection(
                 const char *rvalue,
                 void *data,
                 void *userdata) {
+
         Network *network = userdata;
         _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
         AddressFamily a;
@@ -1215,7 +1841,7 @@ bool address_is_ready(const Address *a) {
         return !(a->flags & IFA_F_TENTATIVE);
 }
 
-int address_section_verify(Address *address) {
+static int address_section_verify(Address *address) {
         if (section_is_invalid(address->section))
                 return -EINVAL;
 
@@ -1228,8 +1854,32 @@ int address_section_verify(Address *address) {
                                          address->section->filename, address->section->line);
         }
 
-        if (!address->scope_set && in_addr_is_localhost(address->family, &address->in_addr) > 0)
+        if (in_addr_is_localhost(address->family, &address->in_addr) > 0 &&
+            (address->family == AF_INET || !address->scope_set)) {
+                /* For IPv4, scope must be always RT_SCOPE_HOST.
+                 * For IPv6, use RT_SCOPE_HOST only when it is not explicitly specified. */
+
+                if (address->scope_set && address->scope != RT_SCOPE_HOST)
+                        log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                          "%s: non-host scope is set in the [Address] section from line %u. "
+                                          "Ignoring Scope= setting.",
+                                          address->section->filename, address->section->line);
+
                 address->scope = RT_SCOPE_HOST;
+        }
+
+        if (!FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV6))
+                address->flags |= IFA_F_NODAD;
 
         return 0;
 }
+
+void network_drop_invalid_addresses(Network *network) {
+        Address *address;
+
+        assert(network);
+
+        ORDERED_HASHMAP_FOREACH(address, network->addresses_by_section)
+                if (address_section_verify(address) < 0)
+                        address_free(address);
+}
index 1378901b8b1c3361e685f36f8d0038cff493a921..431e507851c4735a41cb65fcd00a7744abd51a29 100644 (file)
@@ -3,26 +3,22 @@
 
 #include <inttypes.h>
 #include <stdbool.h>
+#include <stdio.h>
+
+#include "sd-ipv4acd.h"
 
 #include "conf-parser.h"
 #include "in-addr-util.h"
-
-typedef struct Address Address;
-
 #include "networkd-link.h"
-#include "networkd-network.h"
 #include "networkd-util.h"
 
-#include "sd-ipv4acd.h"
-
 #define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU
 
+typedef struct Manager Manager;
 typedef struct Network Network;
-typedef struct Link Link;
-typedef struct NetworkConfigSection NetworkConfigSection;
 typedef int (*address_ready_callback_t)(Address *address);
 
-struct Address {
+typedef struct Address {
         Network *network;
         NetworkConfigSection *section;
 
@@ -42,39 +38,40 @@ struct Address {
 
         bool scope_set:1;
         bool ip_masquerade_done:1;
-        bool manage_temporary_address:1;
-        bool home_address:1;
-        bool prefix_route:1;
-        bool autojoin:1;
         AddressFamily duplicate_address_detection;
 
         /* Called when address become ready */
         address_ready_callback_t callback;
 
         sd_ipv4acd *acd;
-
-        LIST_FIELDS(Address, addresses);
-};
+} Address;
 
 int address_new(Address **ret);
-void address_free(Address *address);
-int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
-int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
+Address *address_free(Address *address);
 int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
 bool address_exists(Link *link, int family, const union in_addr_union *in_addr);
-int address_update(Address *address, unsigned char flags, unsigned char scope, const struct ifa_cacheinfo *cinfo);
-int address_drop(Address *address);
 int address_configure(Address *address, Link *link, link_netlink_message_handler_t callback, bool update, Address **ret);
 int address_remove(Address *address, Link *link, link_netlink_message_handler_t callback);
 bool address_equal(Address *a1, Address *a2);
 bool address_is_ready(const Address *a);
-int address_section_verify(Address *a);
-int configure_ipv4_duplicate_address_detection(Link *link, Address *address);
 
 int generate_ipv6_eui_64_address(Link *link, struct in6_addr *ret);
 
 DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free);
 
+int link_set_addresses(Link *link);
+int link_drop_addresses(Link *link);
+int link_drop_foreign_addresses(Link *link);
+int link_serialize_addresses(Link *link, FILE *f);
+int link_deserialize_addresses(Link *link, const char *addresses);
+
+int ipv4_dad_stop(Link *link);
+int ipv4_dad_update_mac(Link *link);
+
+int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, Manager *m);
+
+void network_drop_invalid_addresses(Network *network);
+
 void address_hash_func(const Address *a, struct siphash *state);
 int address_compare_func(const Address *a1, const Address *a2);
 extern const struct hash_ops address_hash_ops;
index bbe01037d1773a47ebf62d570675ede2b60df19f..23ca4f9fac84af2dd8f15182fb51f4d9f616f5ee 100644 (file)
@@ -146,26 +146,26 @@ static int set_brvlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *lin
         return 1;
 }
 
-int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap) {
+int link_set_bridge_vlan(Link *link) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
-        sd_netlink *rtnl;
-        uint16_t flags;
         int r;
 
         assert(link);
         assert(link->manager);
-        assert(br_vid_bitmap);
-        assert(br_untagged_bitmap);
         assert(link->network);
 
-        /* pvid might not be in br_vid_bitmap yet */
-        if (pvid)
-                set_bit(pvid, br_vid_bitmap);
+        if (!link->network->use_br_vlan)
+                return 0;
 
-        rtnl = link->manager->rtnl;
+        if (!link->network->bridge && !streq_ptr(link->kind, "bridge"))
+                return 0;
+
+        /* pvid might not be in br_vid_bitmap yet */
+        if (link->network->pvid)
+                set_bit(link->network->pvid, link->network->br_vid_bitmap);
 
         /* create new RTM message */
-        r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, link->ifindex);
+        r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
 
@@ -179,14 +179,14 @@ int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32
 
         /* master needs flag self */
         if (!link->network->bridge) {
-                flags = BRIDGE_FLAGS_SELF;
-                r = sd_netlink_message_append_data(req, IFLA_BRIDGE_FLAGS, &flags, sizeof(uint16_t));
+                uint16_t flags = BRIDGE_FLAGS_SELF;
+                r = sd_netlink_message_append_data(req, IFLA_BRIDGE_FLAGS, &flags, sizeof(flags));
                 if (r < 0)
                         return log_link_error_errno(link, r, "Could not open IFLA_BRIDGE_FLAGS: %m");
         }
 
         /* add vlan info */
-        r = append_vlan_info_data(link, req, pvid, br_vid_bitmap, br_untagged_bitmap);
+        r = append_vlan_info_data(link, req, link->network->pvid, link->network->br_vid_bitmap, link->network->br_untagged_bitmap);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not append VLANs: %m");
 
@@ -195,7 +195,7 @@ int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32
                 return log_link_error_errno(link, r, "Could not close IFLA_AF_SPEC container: %m");
 
         /* send message to the kernel */
-        r = netlink_call_async(rtnl, NULL, req, set_brvlan_handler,
+        r = netlink_call_async(link->manager->rtnl, NULL, req, set_brvlan_handler,
                                link_netlink_destroy_callback, link);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
index 7533034021690c9ec7694e0b08109e17a385ff53..d13ea30151eae6ec72f6ba352f86f8eeae722d43 100644 (file)
@@ -5,8 +5,6 @@
   Copyright © 2016 BISDN GmbH. All rights reserved.
 ***/
 
-#include <stdint.h>
-
 #include "conf-parser.h"
 
 #define BRIDGE_VLAN_BITMAP_MAX 4096
@@ -14,7 +12,7 @@
 
 typedef struct Link Link;
 
-int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap);
+int link_set_bridge_vlan(Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_brvlan_pvid);
 CONFIG_PARSER_PROTOTYPE(config_parse_brvlan_vlan);
index ecf9bcea85728f1fa6d7b261bd8bcc01b00da889..d3053d0354a7a5d8fcb64d230c2d3fffeff9f951 100644 (file)
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
+#include <netinet/in.h>
+#include <linux/if_arp.h>
+
 #include "dhcp-internal.h"
 #include "dhcp6-internal.h"
 #include "escape.h"
 #include "in-addr-util.h"
 #include "networkd-dhcp-common.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
 #include "networkd-network.h"
 #include "parse-util.h"
+#include "socket-util.h"
 #include "string-table.h"
 #include "strv.h"
 
+bool link_dhcp_enabled(Link *link, int family) {
+        assert(link);
+        assert(IN_SET(family, AF_INET, AF_INET6));
+
+        if (family == AF_INET6 && !socket_ipv6_is_supported())
+                return false;
+
+        if (link->flags & IFF_LOOPBACK)
+                return false;
+
+        if (!link->network)
+                return false;
+
+        if (link->network->bond)
+                return false;
+
+        if (link->iftype == ARPHRD_CAN)
+                return false;
+
+        return link->network->dhcp & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6);
+}
+
+DUID* link_get_duid(Link *link) {
+        if (link->network->duid.type != _DUID_TYPE_INVALID)
+                return &link->network->duid;
+        else
+                return &link->manager->duid;
+}
+
+static int duid_set_uuid(DUID *duid, sd_id128_t uuid) {
+        assert(duid);
+
+        if (duid->raw_data_len > 0)
+                return 0;
+
+        if (duid->type != DUID_TYPE_UUID)
+                return -EINVAL;
+
+        memcpy(&duid->raw_data, &uuid, sizeof(sd_id128_t));
+        duid->raw_data_len = sizeof(sd_id128_t);
+
+        return 1;
+}
+
+static int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+        Manager *manager = userdata;
+        const sd_bus_error *e;
+        const void *a;
+        size_t sz;
+        DUID *duid;
+        Link *link;
+        int r;
+
+        assert(m);
+        assert(manager);
+
+        e = sd_bus_message_get_error(m);
+        if (e) {
+                log_error_errno(sd_bus_error_get_errno(e),
+                                "Could not get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %s",
+                                e->message);
+                goto configure;
+        }
+
+        r = sd_bus_message_read_array(m, 'y', &a, &sz);
+        if (r < 0)
+                goto configure;
+
+        if (sz != sizeof(sd_id128_t)) {
+                log_error("Invalid product UUID. Falling back to use machine-app-specific ID as DUID-UUID.");
+                goto configure;
+        }
+
+        memcpy(&manager->product_uuid, a, sz);
+        while ((duid = set_steal_first(manager->duids_requesting_uuid)))
+                (void) duid_set_uuid(duid, manager->product_uuid);
+
+        manager->duids_requesting_uuid = set_free(manager->duids_requesting_uuid);
+
+configure:
+        while ((link = set_steal_first(manager->links_requesting_uuid))) {
+                link_unref(link);
+
+                r = link_configure(link);
+                if (r < 0)
+                        link_enter_failed(link);
+        }
+
+        manager->links_requesting_uuid = set_free(manager->links_requesting_uuid);
+
+        /* To avoid calling GetProductUUID() bus method so frequently, set the flag below
+         * even if the method fails. */
+        manager->has_product_uuid = true;
+
+        return 1;
+}
+
+int manager_request_product_uuid(Manager *m, Link *link) {
+        int r;
+
+        assert(m);
+
+        if (m->has_product_uuid)
+                return 0;
+
+        log_debug("Requesting product UUID");
+
+        if (link) {
+                DUID *duid;
+
+                assert_se(duid = link_get_duid(link));
+
+                r = set_ensure_put(&m->links_requesting_uuid, NULL, link);
+                if (r < 0)
+                        return log_oom();
+                if (r > 0)
+                        link_ref(link);
+
+                r = set_ensure_put(&m->duids_requesting_uuid, NULL, duid);
+                if (r < 0)
+                        return log_oom();
+        }
+
+        if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
+                log_debug("Not connected to system bus, requesting product UUID later.");
+                return 0;
+        }
+
+        r = sd_bus_call_method_async(
+                        m->bus,
+                        NULL,
+                        "org.freedesktop.hostname1",
+                        "/org/freedesktop/hostname1",
+                        "org.freedesktop.hostname1",
+                        "GetProductUUID",
+                        get_product_uuid_handler,
+                        m,
+                        "b",
+                        false);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to get product UUID: %m");
+
+        return 0;
+}
+
+static bool link_requires_uuid(Link *link) {
+        const DUID *duid;
+
+        assert(link);
+        assert(link->manager);
+        assert(link->network);
+
+        duid = link_get_duid(link);
+        if (duid->type != DUID_TYPE_UUID || duid->raw_data_len != 0)
+                return false;
+
+        if (link_dhcp4_enabled(link) && IN_SET(link->network->dhcp_client_identifier, DHCP_CLIENT_ID_DUID, DHCP_CLIENT_ID_DUID_ONLY))
+                return true;
+
+        if (link_dhcp6_enabled(link) || link_ipv6_accept_ra_enabled(link))
+                return true;
+
+        return false;
+}
+
+int link_configure_duid(Link *link) {
+        Manager *m;
+        DUID *duid;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(link->network);
+
+        m = link->manager;
+        duid = link_get_duid(link);
+
+        if (!link_requires_uuid(link))
+                return 1;
+
+        if (m->has_product_uuid) {
+                (void) duid_set_uuid(duid, m->product_uuid);
+                return 1;
+        }
+
+        if (!m->links_requesting_uuid) {
+                r = manager_request_product_uuid(m, link);
+                if (r < 0) {
+                        if (r == -ENOMEM)
+                                return r;
+
+                        log_link_warning_errno(link, r,
+                                               "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
+                        return 1;
+                }
+        } else {
+                r = set_put(m->links_requesting_uuid, link);
+                if (r < 0)
+                        return log_oom();
+                if (r > 0)
+                        link_ref(link);
+
+                r = set_put(m->duids_requesting_uuid, duid);
+                if (r < 0)
+                        return log_oom();
+        }
+
+        return 0;
+}
+
 int config_parse_dhcp(
                 const char* unit,
                 const char *filename,
index 01400a23858d4cbe200800956560b5f598d43c20..e3982a7ef1617bea4f352922c15718f523138754 100644 (file)
@@ -7,6 +7,9 @@
 
 #define DHCP_ROUTE_METRIC 1024
 
+typedef struct Link Link;
+typedef struct Manager Manager;
+
 typedef enum DHCPUseDomains {
         DHCP_USE_DOMAINS_NO,
         DHCP_USE_DOMAINS_YES,
@@ -35,6 +38,18 @@ typedef struct DUID {
         usec_t llt_time;
 } DUID;
 
+bool link_dhcp_enabled(Link *link, int family);
+static inline bool link_dhcp4_enabled(Link *link) {
+        return link_dhcp_enabled(link, AF_INET);
+}
+static inline bool link_dhcp6_enabled(Link *link) {
+        return link_dhcp_enabled(link, AF_INET6);
+}
+
+DUID* link_get_duid(Link *link);
+int link_configure_duid(Link *link);
+int manager_request_product_uuid(Manager *m, Link *link);
+
 const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_;
 DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_;
 
index 890d9b55a11a5acdee282f821609a485f862025f..0155ab2de2a5128cef79276b6e5d0c26ab51e7e6 100644 (file)
@@ -1,9 +1,14 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
+#include <netinet/in.h>
+#include <linux/if_arp.h>
+#include <linux/if.h>
+
 #include "sd-dhcp-server.h"
 
 #include "fd-util.h"
 #include "fileio.h"
+#include "networkd-address.h"
 #include "networkd-dhcp-server.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "string-util.h"
 #include "strv.h"
 
+static bool link_dhcp4_server_enabled(Link *link) {
+        assert(link);
+
+        if (link->flags & IFF_LOOPBACK)
+                return false;
+
+        if (!link->network)
+                return false;
+
+        if (link->network->bond)
+                return false;
+
+        if (link->iftype == ARPHRD_CAN)
+                return false;
+
+        return link->network->dhcp_server;
+}
+
 static Address* link_find_dhcp_server_address(Link *link) {
         Address *address;
 
@@ -21,13 +44,13 @@ static Address* link_find_dhcp_server_address(Link *link) {
         assert(link->network);
 
         /* The first statically configured address if there is any */
-        LIST_FOREACH(addresses, address, link->network->static_addresses)
+        ORDERED_HASHMAP_FOREACH(address, link->network->addresses_by_section)
                 if (address->family == AF_INET &&
                     !in_addr_is_null(address->family, &address->in_addr))
                         return address;
 
         /* If that didn't work, find a suitable address we got from the pool */
-        LIST_FOREACH(addresses, address, link->pool_addresses)
+        SET_FOREACH(address, link->pool_addresses)
                 if (address->family == AF_INET)
                         return address;
 
@@ -230,6 +253,24 @@ int dhcp4_server_configure(Link *link) {
         Address *address;
         int r;
 
+        assert(link);
+
+        if (!link_dhcp4_server_enabled(link))
+                return 0;
+
+        if (!(link->flags & IFF_UP))
+                return 0;
+
+        if (!link->dhcp_server) {
+                r = sd_dhcp_server_new(&link->dhcp_server, link->ifindex);
+                if (r < 0)
+                        return r;
+
+                r = sd_dhcp_server_attach_event(link->dhcp_server, link->manager->event, 0);
+                if (r < 0)
+                        return r;
+        }
+
         address = link_find_dhcp_server_address(link);
         if (!address)
                 return log_link_error_errno(link, SYNTHETIC_ERRNO(EBUSY),
@@ -341,6 +382,8 @@ int dhcp4_server_configure(Link *link) {
                 r = sd_dhcp_server_start(link->dhcp_server);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Could not start DHCPv4 server instance: %m");
+
+                log_link_debug(link, "Offering DHCPv4 leases");
         }
 
         return 0;
index eddeb0d5ef727f2d144f108cc4a867f0ed41a23e..ed4d7c34e4a66f90b9197a86b31f4356cdf07b4c 100644 (file)
@@ -11,6 +11,7 @@
 #include "hostname-util.h"
 #include "parse-util.h"
 #include "network-internal.h"
+#include "networkd-address.h"
 #include "networkd-dhcp4.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
@@ -384,7 +385,7 @@ static int link_set_dhcp_routes(Link *link) {
                         if (r < 0)
                                 return log_link_error_errno(link, r, "Could not set router: %m");
 
-                        LIST_FOREACH(routes, rt, link->network->static_routes) {
+                        HASHMAP_FOREACH(rt, link->network->routes_by_section) {
                                 if (!rt->gateway_from_dhcp)
                                         continue;
 
@@ -622,7 +623,7 @@ static int configure_dhcpv4_duplicate_address_detection(Link *link) {
         if (r < 0)
                 return r;
 
-        r = sd_ipv4acd_attach_event(link->network->dhcp_acd, NULL, 0);
+        r = sd_ipv4acd_attach_event(link->network->dhcp_acd, link->manager->event, 0);
         if (r < 0)
                 return r;
 
@@ -698,7 +699,7 @@ static int dhcp4_address_ready_callback(Address *address) {
                 return r;
 
         /* Reconfigure static routes as kernel may remove some routes when lease expires. */
-        r = link_request_set_routes(link);
+        r = link_set_routes(link);
         if (r < 0)
                 return r;
 
@@ -755,9 +756,9 @@ static int dhcp4_update_address(Link *link, bool announce) {
         link_set_state(link, LINK_STATE_CONFIGURING);
         link->dhcp4_configured = false;
 
-        /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). Before they
-         * are called, the related flags must be cleared. Otherwise, the link becomes configured state
-         * before routes are configured. */
+        /* address_handler calls link_set_routes() and link_set_nexthop(). Before they are called, the
+         * related flags must be cleared. Otherwise, the link becomes configured state before routes
+         * are configured. */
         link->static_routes_configured = false;
         link->static_nexthops_configured = false;
 
@@ -814,7 +815,7 @@ static int dhcp4_update_address(Link *link, bool announce) {
         addr->cinfo.ifa_valid = lifetime;
         addr->prefixlen = prefixlen;
         addr->broadcast.s_addr = address.s_addr | ~netmask.s_addr;
-        addr->prefix_route = link_prefixroute(link);
+        SET_FLAG(addr->flags, IFA_F_NOPREFIXROUTE, !link_prefixroute(link));
 
         /* allow reusing an existing address and simply update its lifetime
          * in case it already exists */
@@ -1156,12 +1157,10 @@ static bool promote_secondaries_enabled(const char *ifname) {
  * the primary one expires it relies on the kernel to promote the
  * secondary IP. See also https://github.com/systemd/systemd/issues/7163
  */
-int dhcp4_set_promote_secondaries(Link *link) {
+static int dhcp4_set_promote_secondaries(Link *link) {
         int r;
 
         assert(link);
-        assert(link->network);
-        assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
 
         /* check if the kernel has promote_secondaries enabled for our
          * interface. If it is not globally enabled or enabled for the
@@ -1181,7 +1180,7 @@ int dhcp4_set_promote_secondaries(Link *link) {
         return 0;
 }
 
-int dhcp4_set_client_identifier(Link *link) {
+static int dhcp4_set_client_identifier(Link *link) {
         int r;
 
         assert(link);
@@ -1240,6 +1239,25 @@ int dhcp4_set_client_identifier(Link *link) {
         return 0;
 }
 
+static int dhcp4_init(Link *link) {
+        int r;
+
+        assert(link);
+
+        if (link->dhcp_client)
+                return 0;
+
+        r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
+        if (r < 0)
+                return r;
+
+        r = sd_dhcp_client_attach_event(link->dhcp_client, link->manager->event, 0);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 int dhcp4_configure(Link *link) {
         sd_dhcp_option *send_option;
         void *request_options;
@@ -1247,19 +1265,17 @@ int dhcp4_configure(Link *link) {
 
         assert(link);
         assert(link->network);
-        assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
 
-        if (!link->dhcp_client) {
-                r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
-                if (r == -ENOMEM)
-                        return log_oom();
-                if (r < 0)
-                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to create DHCP4 client: %m");
+        if (!link_dhcp4_enabled(link))
+                return 0;
 
-                r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to attach event: %m");
-        }
+        r = dhcp4_set_promote_secondaries(link);
+        if (r < 0)
+                return r;
+
+        r = dhcp4_init(link);
+        if (r < 0)
+                return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to initialize DHCP4 client: %m");
 
         r = sd_dhcp_client_set_mac(link->dhcp_client,
                                    (const uint8_t *) &link->mac,
@@ -1419,6 +1435,49 @@ int dhcp4_configure(Link *link) {
         return dhcp4_set_client_identifier(link);
 }
 
+int dhcp4_update_mac(Link *link) {
+        int r;
+
+        assert(link);
+
+        if (!link->dhcp_client)
+                return 0;
+
+        r = sd_dhcp_client_set_mac(link->dhcp_client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER);
+        if (r < 0)
+                return r;
+
+        r = dhcp4_set_client_identifier(link);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int link_deserialize_dhcp4(Link *link, const char *dhcp4_address) {
+        union in_addr_union address;
+        int r;
+
+        assert(link);
+
+        if (isempty(dhcp4_address))
+                return 0;
+
+        r = in_addr_from_string(AF_INET, dhcp4_address, &address);
+        if (r < 0)
+                return log_link_debug_errno(link, r, "Failed to parse DHCPv4 address: %s", dhcp4_address);
+
+        r = dhcp4_init(link);
+        if (r < 0)
+                return log_link_debug_errno(link, r, "Failed to initialize DHCPv4 client: %m");
+
+        r = sd_dhcp_client_set_request_address(link->dhcp_client, &address.in);
+        if (r < 0)
+                return log_link_debug_errno(link, r, "Failed to set initial DHCPv4 address %s: %m", dhcp4_address);
+
+        return 0;
+}
+
 int config_parse_dhcp_max_attempts(
                 const char *unit,
                 const char *filename,
index 7a80897ffcdd6e9f0bca2fd85ccee313474712cd..8aa6ac9453eacd271e9cf6ed836651c2208ad88d 100644 (file)
@@ -18,8 +18,9 @@ typedef enum DHCPClientIdentifier {
 } DHCPClientIdentifier;
 
 int dhcp4_configure(Link *link);
-int dhcp4_set_client_identifier(Link *link);
-int dhcp4_set_promote_secondaries(Link *link);
+int dhcp4_update_mac(Link *link);
+
+int link_deserialize_dhcp4(Link *link, const char *dhcp4_address);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_acl_ip_address);
index 7996e825c29a95d89b111cb84b9a9f8dd2d6fffe..ea331c95db45d34b9d58d258ba63d6a05da30f63 100644 (file)
@@ -14,6 +14,7 @@
 #include "hostname-util.h"
 #include "missing_network.h"
 #include "network-internal.h"
+#include "networkd-address.h"
 #include "networkd-dhcp6.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "radv-internal.h"
 #include "web-util.h"
 
+bool link_dhcp6_pd_is_enabled(Link *link) {
+        assert(link);
+
+        if (!link->network)
+                return false;
+
+        return link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_DHCP6;
+}
+
 static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) {
         uint32_t lifetime_preferred, lifetime_valid;
         union in_addr_union pd_prefix;
@@ -180,6 +190,9 @@ int dhcp6_pd_remove(Link *link) {
         assert(link);
         assert(link->manager);
 
+        if (!link_dhcp6_pd_is_enabled(link))
+                return 0;
+
         link->dhcp6_pd_address_configured = false;
         link->dhcp6_pd_route_configured = false;
 
@@ -342,7 +355,7 @@ static int dhcp6_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Lin
                         return 1;
                 }
 
-                r = link_request_set_routes(link);
+                r = link_set_routes(link);
                 if (r < 0) {
                         link_enter_failed(link);
                         return 1;
@@ -425,13 +438,6 @@ static int dhcp6_pd_assign_prefix(Link *link, const union in_addr_union *prefix,
         return 0;
 }
 
-bool link_dhcp6_pd_is_enabled(Link *link) {
-        if (!link->network)
-                return false;
-
-        return link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_DHCP6;
-}
-
 static bool link_has_preferred_subnet_id(Link *link) {
         if (!link->network)
                 return false;
@@ -607,9 +613,9 @@ static int dhcp6_pd_finalize(Link *link) {
                         link->dhcp6_pd_address_configured = true;
         } else {
                 log_link_debug(link, "Setting DHCPv6 PD addresses");
-                /* address_handler calls link_request_set_routes() and link_request_set_nexthop().
-                 * Before they are called, the related flags must be cleared. Otherwise, the link
-                 * becomes configured state before routes are configured. */
+                /* address_handler calls link_set_routes() and link_set_nexthop(). Before they are
+                 * called, the related flags must be cleared. Otherwise, the link becomes configured
+                 * state before routes are configured. */
                 link->static_routes_configured = false;
                 link->static_nexthops_configured = false;
         }
@@ -643,9 +649,6 @@ static void dhcp6_pd_prefix_lost(Link *dhcp6_link) {
                 if (link == dhcp6_link)
                         continue;
 
-                if (!link_dhcp6_pd_is_enabled(link))
-                        continue;
-
                 r = dhcp6_pd_remove(link);
                 if (r < 0)
                         link_enter_failed(link);
@@ -952,7 +955,7 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
                         return 1;
                 }
 
-                r = link_request_set_routes(link);
+                r = link_set_routes(link);
                 if (r < 0) {
                         link_enter_failed(link);
                         return 1;
@@ -1075,9 +1078,9 @@ static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) {
                 link->dhcp6_address_configured = true;
         else {
                 log_link_debug(link, "Setting DHCPv6 addresses");
-                /* address_handler calls link_request_set_routes() and link_request_set_nexthop().
-                 * Before they are called, the related flags must be cleared. Otherwise, the link
-                 * becomes configured state before routes are configured. */
+                /* address_handler calls link_set_routes() and link_set_nexthop(). Before they are
+                 * called, the related flags must be cleared. Otherwise, the link becomes configured
+                 * state before routes are configured. */
                 link->static_routes_configured = false;
                 link->static_nexthops_configured = false;
         }
@@ -1343,17 +1346,51 @@ static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
         return false;
 }
 
+static int dhcp6_set_identifier(Link *link, sd_dhcp6_client *client) {
+        const DUID *duid;
+        int r;
+
+        assert(link);
+        assert(link->network);
+        assert(client);
+
+        r = sd_dhcp6_client_set_mac(client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER);
+        if (r < 0)
+                return r;
+
+        if (link->network->iaid_set) {
+                r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
+                if (r < 0)
+                        return r;
+        }
+
+        duid = link_get_duid(link);
+        if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
+                r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
+        else
+                r = sd_dhcp6_client_set_duid(client,
+                                             duid->type,
+                                             duid->raw_data_len > 0 ? duid->raw_data : NULL,
+                                             duid->raw_data_len);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 int dhcp6_configure(Link *link) {
         _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
         sd_dhcp6_option *vendor_option;
         sd_dhcp6_option *send_option;
         void *request_options;
-        const DUID *duid;
         int r;
 
         assert(link);
         assert(link->network);
 
+        if (!link_dhcp6_enabled(link) && !link_ipv6_accept_ra_enabled(link))
+                return 0;
+
         if (link->dhcp6_client)
                 return 0;
 
@@ -1363,32 +1400,13 @@ int dhcp6_configure(Link *link) {
         if (r < 0)
                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
 
-        r = sd_dhcp6_client_attach_event(client, NULL, 0);
+        r = sd_dhcp6_client_attach_event(client, link->manager->event, 0);
         if (r < 0)
                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
 
-        r = sd_dhcp6_client_set_mac(client,
-                                    (const uint8_t *) &link->mac,
-                                    sizeof (link->mac), ARPHRD_ETHER);
+        r = dhcp6_set_identifier(link, client);
         if (r < 0)
-                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m");
-
-        if (link->network->iaid_set) {
-                r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m");
-        }
-
-        duid = link_get_duid(link);
-        if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
-                r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
-        else
-                r = sd_dhcp6_client_set_duid(client,
-                                             duid->type,
-                                             duid->raw_data_len > 0 ? duid->raw_data : NULL,
-                                             duid->raw_data_len);
-        if (r < 0)
-                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");
+                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set identifier: %m");
 
         ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp6_client_send_options) {
                 r = sd_dhcp6_client_add_option(client, send_option);
@@ -1471,6 +1489,57 @@ int dhcp6_configure(Link *link) {
         return 0;
 }
 
+int dhcp6_update_mac(Link *link) {
+        bool restart;
+        int r;
+
+        assert(link);
+
+        if (!link->dhcp6_client)
+                return 0;
+
+        restart = sd_dhcp6_client_is_running(link->dhcp6_client) > 0;
+
+        if (restart) {
+                r = sd_dhcp6_client_stop(link->dhcp6_client);
+                if (r < 0)
+                        return r;
+        }
+
+        r = dhcp6_set_identifier(link, link->dhcp6_client);
+        if (r < 0)
+                return r;
+
+        if (restart) {
+                r = sd_dhcp6_client_start(link->dhcp6_client);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Could not restart DHCPv6 client: %m");
+        }
+
+        return 0;
+}
+
+int link_serialize_dhcp6_client(Link *link, FILE *f) {
+        _cleanup_free_ char *duid = NULL;
+        uint32_t iaid;
+        int r;
+
+        assert(link);
+
+        if (!link->dhcp6_client)
+                return 0;
+
+        r = sd_dhcp6_client_get_iaid(link->dhcp6_client, &iaid);
+        if (r >= 0)
+                fprintf(f, "DHCP6_CLIENT_IAID=0x%x\n", iaid);
+
+        r = sd_dhcp6_client_duid_as_string(link->dhcp6_client, &duid);
+        if (r >= 0)
+                fprintf(f, "DHCP6_CLIENT_DUID=%s\n", duid);
+
+        return 0;
+}
+
 int config_parse_dhcp6_pd_hint(
                 const char* unit,
                 const char *filename,
index 214456096da7f9440a3ade1d2ab64bd37ec34640..4956c90915a14657233450cb68a62199ed173f76 100644 (file)
@@ -29,9 +29,12 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6DelegatedPrefix*, dhcp6_pd_free);
 bool link_dhcp6_pd_is_enabled(Link *link);
 int dhcp6_pd_remove(Link *link);
 int dhcp6_configure(Link *link);
+int dhcp6_update_mac(Link *link);
 int dhcp6_request_address(Link *link, int ir);
 int dhcp6_request_prefix_delegation(Link *link);
 
+int link_serialize_dhcp6_client(Link *link, FILE *f);
+
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_mud_url);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_client_start_mode);
index 628c3988accd5db2d1b7f2d6c3c16844179e7e00..2e35f7d1bf525a097699efaeb025c046f95428b0 100644 (file)
@@ -8,27 +8,33 @@
 
 #include "alloc-util.h"
 #include "bridge.h"
-#include "conf-parser.h"
 #include "netlink-util.h"
 #include "networkd-fdb.h"
+#include "networkd-link.h"
 #include "networkd-manager.h"
+#include "networkd-network.h"
 #include "parse-util.h"
-#include "string-util.h"
 #include "string-table.h"
-#include "util.h"
 #include "vlan-util.h"
 #include "vxlan.h"
 
 #define STATIC_FDB_ENTRIES_PER_NETWORK_MAX 1024U
 
-static const char* const fdb_ntf_flags_table[_NEIGHBOR_CACHE_ENTRY_FLAGS_MAX] = {
-        [NEIGHBOR_CACHE_ENTRY_FLAGS_USE] = "use",
-        [NEIGHBOR_CACHE_ENTRY_FLAGS_SELF] = "self",
-        [NEIGHBOR_CACHE_ENTRY_FLAGS_MASTER] = "master",
-        [NEIGHBOR_CACHE_ENTRY_FLAGS_ROUTER] = "router",
-};
+/* remove and FDB entry. */
+FdbEntry *fdb_entry_free(FdbEntry *fdb_entry) {
+        if (!fdb_entry)
+                return NULL;
+
+        if (fdb_entry->network) {
+                assert(fdb_entry->section);
+                hashmap_remove(fdb_entry->network->fdb_entries_by_section, fdb_entry->section);
+        }
+
+        network_config_section_free(fdb_entry->section);
+        return mfree(fdb_entry);
+}
 
-DEFINE_STRING_TABLE_LOOKUP(fdb_ntf_flags, NeighborCacheEntryFlags);
+DEFINE_NETWORK_SECTION_FUNCTIONS(FdbEntry, fdb_entry_free);
 
 /* create a new FDB entry or get an existing one. */
 static int fdb_entry_new_static(
@@ -43,23 +49,21 @@ static int fdb_entry_new_static(
 
         assert(network);
         assert(ret);
-        assert(!!filename == (section_line > 0));
-
-        /* search entry in hashmap first. */
-        if (filename) {
-                r = network_config_section_new(filename, section_line, &n);
-                if (r < 0)
-                        return r;
+        assert(filename);
+        assert(section_line > 0);
 
-                fdb_entry = hashmap_get(network->fdb_entries_by_section, n);
-                if (fdb_entry) {
-                        *ret = TAKE_PTR(fdb_entry);
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
 
-                        return 0;
-                }
+        /* search entry in hashmap first. */
+        fdb_entry = hashmap_get(network->fdb_entries_by_section, n);
+        if (fdb_entry) {
+                *ret = TAKE_PTR(fdb_entry);
+                return 0;
         }
 
-        if (network->n_static_fdb_entries >= STATIC_FDB_ENTRIES_PER_NETWORK_MAX)
+        if (hashmap_size(network->fdb_entries_by_section) >= STATIC_FDB_ENTRIES_PER_NETWORK_MAX)
                 return -E2BIG;
 
         /* allocate space for and FDB entry. */
@@ -70,24 +74,18 @@ static int fdb_entry_new_static(
         /* init FDB structure. */
         *fdb_entry = (FdbEntry) {
                 .network = network,
+                .section = TAKE_PTR(n),
                 .vni = VXLAN_VID_MAX + 1,
                 .fdb_ntf_flags = NEIGHBOR_CACHE_ENTRY_FLAGS_SELF,
         };
 
-        LIST_PREPEND(static_fdb_entries, network->static_fdb_entries, fdb_entry);
-        network->n_static_fdb_entries++;
-
-        if (filename) {
-                fdb_entry->section = TAKE_PTR(n);
-
-                r = hashmap_ensure_allocated(&network->fdb_entries_by_section, &network_config_hash_ops);
-                if (r < 0)
-                        return r;
+        r = hashmap_ensure_allocated(&network->fdb_entries_by_section, &network_config_hash_ops);
+        if (r < 0)
+                return r;
 
-                r = hashmap_put(network->fdb_entries_by_section, fdb_entry->section, fdb_entry);
-                if (r < 0)
-                        return r;
-        }
+        r = hashmap_put(network->fdb_entries_by_section, fdb_entry->section, fdb_entry);
+        if (r < 0)
+                return r;
 
         /* return allocated FDB structure. */
         *ret = TAKE_PTR(fdb_entry);
@@ -114,7 +112,7 @@ static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
 }
 
 /* send a request to the kernel to add a FDB entry in its static MAC table. */
-int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
+static int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         int r;
 
@@ -171,22 +169,30 @@ int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
         return 1;
 }
 
-/* remove and FDB entry. */
-void fdb_entry_free(FdbEntry *fdb_entry) {
-        if (!fdb_entry)
-                return;
+int link_set_bridge_fdb(Link *link) {
+        FdbEntry *fdb_entry;
+        int r;
 
-        if (fdb_entry->network) {
-                LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries, fdb_entry);
-                assert(fdb_entry->network->n_static_fdb_entries > 0);
-                fdb_entry->network->n_static_fdb_entries--;
+        assert(link);
+        assert(link->network);
 
-                if (fdb_entry->section)
-                        hashmap_remove(fdb_entry->network->fdb_entries_by_section, fdb_entry->section);
+        HASHMAP_FOREACH(fdb_entry, link->network->fdb_entries_by_section) {
+                r = fdb_entry_configure(link, fdb_entry);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m");
         }
 
-        network_config_section_free(fdb_entry->section);
-        free(fdb_entry);
+        return 0;
+}
+
+void network_drop_invalid_fdb_entries(Network *network) {
+        FdbEntry *fdb_entry;
+
+        assert(network);
+
+        HASHMAP_FOREACH(fdb_entry, network->fdb_entries_by_section)
+                if (section_is_invalid(fdb_entry->section))
+                        fdb_entry_free(fdb_entry);
 }
 
 /* parse the HW address from config files. */
@@ -352,6 +358,15 @@ int config_parse_fdb_vxlan_vni(
         return 0;
 }
 
+static const char* const fdb_ntf_flags_table[_NEIGHBOR_CACHE_ENTRY_FLAGS_MAX] = {
+        [NEIGHBOR_CACHE_ENTRY_FLAGS_USE] = "use",
+        [NEIGHBOR_CACHE_ENTRY_FLAGS_SELF] = "self",
+        [NEIGHBOR_CACHE_ENTRY_FLAGS_MASTER] = "master",
+        [NEIGHBOR_CACHE_ENTRY_FLAGS_ROUTER] = "router",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(fdb_ntf_flags, NeighborCacheEntryFlags);
+
 int config_parse_fdb_ntf_flags(
                 const char *unit,
                 const char *filename,
index 5e24ad6aee258d342371a7da2748abcf25b75f59..935406e022bd1282f71e4ff4ac7bbf4a09e41c69 100644 (file)
@@ -5,17 +5,16 @@
   Copyright © 2014 Intel Corporation. All rights reserved.
 ***/
 
+#include <inttypes.h>
 #include <linux/neighbour.h>
 
 #include "conf-parser.h"
-#include "list.h"
-#include "macro.h"
+#include "ether-addr-util.h"
+#include "in-addr-util.h"
 #include "networkd-util.h"
 
 typedef struct Network Network;
-typedef struct FdbEntry FdbEntry;
 typedef struct Link Link;
-typedef struct NetworkConfigSection NetworkConfigSection;
 
 typedef enum NeighborCacheEntryFlags {
         NEIGHBOR_CACHE_ENTRY_FLAGS_USE = NTF_USE,
@@ -26,7 +25,7 @@ typedef enum NeighborCacheEntryFlags {
         _NEIGHBOR_CACHE_ENTRY_FLAGS_INVALID = -1,
 } NeighborCacheEntryFlags;
 
-struct FdbEntry {
+typedef struct FdbEntry {
         Network *network;
         NetworkConfigSection *section;
 
@@ -38,17 +37,13 @@ struct FdbEntry {
         struct ether_addr mac_addr;
         union in_addr_union destination_addr;
         NeighborCacheEntryFlags fdb_ntf_flags;
+} FdbEntry;
 
-        LIST_FIELDS(FdbEntry, static_fdb_entries);
-};
+FdbEntry *fdb_entry_free(FdbEntry *fdb_entry);
 
-void fdb_entry_free(FdbEntry *fdb_entry);
-int fdb_entry_configure(Link *link, FdbEntry *fdb_entry);
+void network_drop_invalid_fdb_entries(Network *network);
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(FdbEntry, fdb_entry_free);
-
-const char* fdb_ntf_flags_to_string(NeighborCacheEntryFlags i) _const_;
-NeighborCacheEntryFlags fdb_ntf_flags_from_string(const char *s) _pure_;
+int link_set_bridge_fdb(Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_fdb_hwaddr);
 CONFIG_PARSER_PROTOTYPE(config_parse_fdb_vlan_id);
index e844799b5735393545d0aa2afde23a823792605d..b0e0a97ad8d9f6587147d40deb8b218ca4b9ed0c 100644 (file)
@@ -142,23 +142,37 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
         }
 }
 
+static int ipv4ll_init(Link *link) {
+        int r;
+
+        assert(link);
+
+        if (link->ipv4ll)
+                return 0;
+
+        r = sd_ipv4ll_new(&link->ipv4ll);
+        if (r < 0)
+                return r;
+
+        r = sd_ipv4ll_attach_event(link->ipv4ll, link->manager->event, 0);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 int ipv4ll_configure(Link *link) {
         uint64_t seed;
         int r;
 
         assert(link);
-        assert(link->network);
-        assert(link->network->link_local & (ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4));
 
-        if (!link->ipv4ll) {
-                r = sd_ipv4ll_new(&link->ipv4ll);
-                if (r < 0)
-                        return r;
+        if (!link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4))
+                return 0;
 
-                r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
-                if (r < 0)
-                        return r;
-        }
+        r = ipv4ll_init(link);
+        if (r < 0)
+                return r;
 
         if (link->sd_device &&
             net_get_unique_predictable_data(link->sd_device, true, &seed) >= 0) {
@@ -182,6 +196,82 @@ int ipv4ll_configure(Link *link) {
         return 0;
 }
 
+int ipv4ll_update_mac(Link *link) {
+        bool restart;
+        int r;
+
+        assert(link);
+
+        if (!link->ipv4ll)
+                return 0;
+
+        restart = sd_ipv4ll_is_running(link->ipv4ll) > 0;
+
+        if (restart) {
+                r = sd_ipv4ll_stop(link->ipv4ll);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
+        if (r < 0)
+                return r;
+
+        if (restart) {
+                r = sd_ipv4ll_start(link->ipv4ll);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int link_serialize_ipv4ll(Link *link, FILE *f) {
+        struct in_addr address;
+        int r;
+
+        assert(link);
+
+        if (!link->ipv4ll)
+                return 0;
+
+        r = sd_ipv4ll_get_address(link->ipv4ll, &address);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return r;
+
+        fputs("IPV4LL_ADDRESS=", f);
+        serialize_in_addrs(f, &address, 1, false, NULL);
+        fputc('\n', f);
+
+        return 0;
+}
+
+int link_deserialize_ipv4ll(Link *link, const char *ipv4ll_address) {
+        union in_addr_union address;
+        int r;
+
+        assert(link);
+
+        if (isempty(ipv4ll_address))
+                return 0;
+
+        r = in_addr_from_string(AF_INET, ipv4ll_address, &address);
+        if (r < 0)
+                return log_link_debug_errno(link, r, "Failed to parse IPv4LL address: %s", ipv4ll_address);
+
+        r = ipv4ll_init(link);
+        if (r < 0)
+                return log_link_debug_errno(link, r, "Failed to initialize IPv4LL client: %m");
+
+        r = sd_ipv4ll_set_address(link->ipv4ll, &address.in);
+        if (r < 0)
+                return log_link_debug_errno(link, r, "Failed to set initial IPv4LL address %s: %m", ipv4ll_address);
+
+        return 0;
+}
+
 int config_parse_ipv4ll(
                 const char* unit,
                 const char *filename,
index 49b6fb56ad594b5eaa8ebcbd91fb87c4db4d18ce..4833e304b6c480eafcd48492dbe8f248dd729350 100644 (file)
@@ -8,5 +8,8 @@
 typedef struct Link Link;
 
 int ipv4ll_configure(Link *link);
+int ipv4ll_update_mac(Link *link);
+int link_serialize_ipv4ll(Link *link, FILE *f);
+int link_deserialize_ipv4ll(Link *link, const char *ipv4ll_address);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll);
index 7051ba9dac4fc05f81cdc4b2ff37208b9d24ec32..fb8464ff2fa7cdeeb3769700f73396fb98d46d10 100644 (file)
@@ -2,9 +2,7 @@
 
 #include <netinet/in.h>
 #include <linux/if.h>
-#include <unistd.h>
 
-#include "fileio.h"
 #include "netlink-util.h"
 #include "networkd-ipv6-proxy-ndp.h"
 #include "networkd-link.h"
 #include "string-util.h"
 #include "sysctl-util.h"
 
+static int set_ipv6_proxy_ndp_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(link);
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EEXIST)
+                log_link_message_warning_errno(link, m, r, "Could not add IPv6 proxy ndp address entry, ignoring");
+
+        return 1;
+}
+
+/* send a request to the kernel to add a IPv6 Proxy entry to the neighbour table */
+static int ipv6_proxy_ndp_address_configure(Link *link, const struct in6_addr *address) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(address);
+
+        /* create new netlink message */
+        r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_INET6);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not create RTM_NEWNEIGH message: %m");
+
+        r = sd_rtnl_message_neigh_set_flags(req, NLM_F_REQUEST | NTF_PROXY);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not set neighbor flags: %m");
+
+        r = sd_netlink_message_append_in6_addr(req, NDA_DST, address);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
+
+        r = netlink_call_async(link->manager->rtnl, NULL, req, set_ipv6_proxy_ndp_address_handler,
+                               link_netlink_destroy_callback, link);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+        link_ref(link);
+
+        return 0;
+}
+
 static bool ipv6_proxy_ndp_is_needed(Link *link) {
         assert(link);
 
@@ -26,10 +68,7 @@ static bool ipv6_proxy_ndp_is_needed(Link *link) {
         if (link->network->ipv6_proxy_ndp >= 0)
                 return link->network->ipv6_proxy_ndp;
 
-        if (link->network->n_ipv6_proxy_ndp_addresses == 0)
-                return false;
-
-        return true;
+        return !set_isempty(link->network->ipv6_proxy_ndp_addresses);
 }
 
 static int ipv6_proxy_ndp_set(Link *link) {
@@ -45,47 +84,31 @@ static int ipv6_proxy_ndp_set(Link *link) {
 
         r = sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v);
         if (r < 0)
-                log_link_warning_errno(link, r, "Cannot configure proxy NDP for interface: %m");
+                return log_link_warning_errno(link, r, "Cannot configure proxy NDP for the interface: %m");
 
-        return 0;
+        return v;
 }
 
-static int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress **ret) {
-        _cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL;
-
-        assert(network);
-        assert(ret);
-
-        /* allocate space for IPv6ProxyNDPAddress entry */
-        ipv6_proxy_ndp_address = new(IPv6ProxyNDPAddress, 1);
-        if (!ipv6_proxy_ndp_address)
-                return -ENOMEM;
-
-        *ipv6_proxy_ndp_address = (IPv6ProxyNDPAddress) {
-                .network = network,
-        };
-
-        LIST_PREPEND(ipv6_proxy_ndp_addresses, network->ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address);
-        network->n_ipv6_proxy_ndp_addresses++;
-
-        *ret = TAKE_PTR(ipv6_proxy_ndp_address);
-
-        return 0;
-}
+/* configure all ipv6 proxy ndp addresses */
+int link_set_ipv6_proxy_ndp_addresses(Link *link) {
+        struct in6_addr *address;
+        int r;
 
-void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) {
-        if (!ipv6_proxy_ndp_address)
-                return;
+        assert(link);
+        assert(link->network);
 
-        if (ipv6_proxy_ndp_address->network) {
-                LIST_REMOVE(ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address->network->ipv6_proxy_ndp_addresses,
-                            ipv6_proxy_ndp_address);
+        /* enable or disable proxy_ndp itself depending on whether ipv6_proxy_ndp_addresses are set or not */
+        r = ipv6_proxy_ndp_set(link);
+        if (r <= 0)
+                return r;
 
-                assert(ipv6_proxy_ndp_address->network->n_ipv6_proxy_ndp_addresses > 0);
-                ipv6_proxy_ndp_address->network->n_ipv6_proxy_ndp_addresses--;
+        SET_FOREACH(address, link->network->ipv6_proxy_ndp_addresses) {
+                r = ipv6_proxy_ndp_address_configure(link, address);
+                if (r < 0)
+                        return r;
         }
 
-        free(ipv6_proxy_ndp_address);
+        return 0;
 }
 
 int config_parse_ipv6_proxy_ndp_address(
@@ -100,26 +123,24 @@ int config_parse_ipv6_proxy_ndp_address(
                 void *data,
                 void *userdata) {
 
+        _cleanup_free_ struct in6_addr *address = NULL;
         Network *network = userdata;
-        _cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL;
-        int r;
         union in_addr_union buffer;
+        int r;
 
         assert(filename);
-        assert(section);
-        assert(lvalue);
         assert(rvalue);
-        assert(data);
+        assert(network);
 
-        r = ipv6_proxy_ndp_address_new_static(network, &ipv6_proxy_ndp_address);
-        if (r < 0)
-                return log_oom();
+        if (isempty(rvalue)) {
+                network->ipv6_proxy_ndp_addresses = set_free_free(network->ipv6_proxy_ndp_addresses);
+                return 0;
+        }
 
         r = in_addr_from_string(AF_INET6, rvalue, &buffer);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse IPv6 proxy NDP address, ignoring: %s",
-                           rvalue);
+                           "Failed to parse IPv6 proxy NDP address, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -129,76 +150,15 @@ int config_parse_ipv6_proxy_ndp_address(
                 return 0;
         }
 
-        ipv6_proxy_ndp_address->in_addr = buffer.in6;
-        ipv6_proxy_ndp_address = NULL;
-
-        return 0;
-}
-
-static int set_ipv6_proxy_ndp_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        int r;
-
-        assert(link);
-
-        r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -EEXIST)
-                log_link_message_warning_errno(link, m, r, "Could not add IPv6 proxy ndp address entry, ignoring");
-
-        return 1;
-}
-
-/* send a request to the kernel to add a IPv6 Proxy entry to the neighbour table */
-int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
-        sd_netlink *rtnl;
-        int r;
-
-        assert(link);
-        assert(link->network);
-        assert(link->manager);
-        assert(ipv6_proxy_ndp_address);
-
-        rtnl = link->manager->rtnl;
-
-        /* create new netlink message */
-        r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_INET6);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not create RTM_NEWNEIGH message: %m");
-
-        r = sd_rtnl_message_neigh_set_flags(req, NLM_F_REQUEST | NTF_PROXY);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not set neighbor flags: %m");
-
-        r = sd_netlink_message_append_in6_addr(req, NDA_DST, &ipv6_proxy_ndp_address->in_addr);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
+        address = newdup(struct in6_addr, &buffer.in6, 1);
+        if (!address)
+                return log_oom();
 
-        r = netlink_call_async(rtnl, NULL, req, set_ipv6_proxy_ndp_address_handler,
-                               link_netlink_destroy_callback, link);
+        r = set_ensure_put(&network->ipv6_proxy_ndp_addresses, &in6_addr_hash_ops, address);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
-
-        link_ref(link);
-
-        return 0;
-}
-
-/* configure all ipv6 proxy ndp addresses */
-int ipv6_proxy_ndp_addresses_configure(Link *link) {
-        IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
-        int r;
-
-        assert(link);
-
-        /* enable or disable proxy_ndp itself depending on whether ipv6_proxy_ndp_addresses are set or not */
-        r = ipv6_proxy_ndp_set(link);
-        if (r != 0)
-                return r;
+                return log_oom();
+        if (r > 0)
+                TAKE_PTR(address);
 
-        LIST_FOREACH(ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address, link->network->ipv6_proxy_ndp_addresses) {
-                r = ipv6_proxy_ndp_address_configure(link, ipv6_proxy_ndp_address);
-                if (r != 0)
-                        return r;
-        }
         return 0;
 }
index d6666beab5ce76eeec9eb26b013b38977bb2a367..e58b17ec942585fe0dc96fd3fd0bcad33f39c509 100644 (file)
@@ -2,24 +2,9 @@
 #pragma once
 
 #include "conf-parser.h"
-#include "list.h"
-#include "macro.h"
 
-typedef struct Network Network;
-typedef struct IPv6ProxyNDPAddress IPv6ProxyNDPAddress;
 typedef struct Link Link;
 
-struct IPv6ProxyNDPAddress {
-        Network *network;
-        struct in6_addr in_addr;
-
-        LIST_FIELDS(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
-};
-
-void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address);
-int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy_ndp_address);
-int ipv6_proxy_ndp_addresses_configure(Link *link);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(IPv6ProxyNDPAddress*, ipv6_proxy_ndp_address_free);
+int link_set_ipv6_proxy_ndp_addresses(Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_proxy_ndp_address);
index 0cdd2b5121e064b7a61141cc8b2ee9ba8524a3f1..b32a436be50a6d7c2d573c037f3fc37e4e9cc91c 100644 (file)
 #include "missing_network.h"
 #include "netlink-util.h"
 #include "network-internal.h"
+#include "networkd-address-label.h"
+#include "networkd-address.h"
 #include "networkd-can.h"
 #include "networkd-dhcp-server.h"
 #include "networkd-dhcp4.h"
 #include "networkd-dhcp6.h"
+#include "networkd-fdb.h"
 #include "networkd-ipv4ll.h"
 #include "networkd-ipv6-proxy-ndp.h"
 #include "networkd-link-bus.h"
 #include "networkd-link.h"
 #include "networkd-lldp-tx.h"
 #include "networkd-manager.h"
+#include "networkd-mdb.h"
 #include "networkd-ndisc.h"
 #include "networkd-neighbor.h"
+#include "networkd-nexthop.h"
 #include "networkd-sriov.h"
+#include "networkd-sysctl.h"
 #include "networkd-radv.h"
 #include "networkd-routing-policy-rule.h"
 #include "networkd-wifi.h"
 #include "util.h"
 #include "vrf.h"
 
-uint32_t link_get_vrf_table(Link *link) {
-        return link->network->vrf ? VRF(link->network->vrf)->table : RT_TABLE_MAIN;
-}
-
-uint32_t link_get_dhcp_route_table(Link *link) {
-        /* When the interface is part of an VRF use the VRFs routing table, unless
-         * another table is explicitly specified. */
-        if (link->network->dhcp_route_table_set)
-                return link->network->dhcp_route_table;
-        return link_get_vrf_table(link);
-}
-
-uint32_t link_get_ipv6_accept_ra_route_table(Link *link) {
-        if (link->network->ipv6_accept_ra_route_table_set)
-                return link->network->ipv6_accept_ra_route_table;
-        return link_get_vrf_table(link);
-}
-
-DUID* link_get_duid(Link *link) {
-        if (link->network->duid.type != _DUID_TYPE_INVALID)
-                return &link->network->duid;
-        else
-                return &link->manager->duid;
-}
-
-static bool link_dhcp6_enabled(Link *link) {
-        assert(link);
-
-        if (!socket_ipv6_is_supported())
-                return false;
-
-        if (link->flags & IFF_LOOPBACK)
-                return false;
-
-        if (!link->network)
-                return false;
-
-        if (link->network->bond)
-                return false;
-
-        if (link->iftype == ARPHRD_CAN)
-                return false;
-
-        return link->network->dhcp & ADDRESS_FAMILY_IPV6;
-}
-
-static bool link_dhcp4_enabled(Link *link) {
-        assert(link);
-
-        if (link->flags & IFF_LOOPBACK)
-                return false;
-
-        if (!link->network)
-                return false;
-
-        if (link->network->bond)
-                return false;
-
-        if (link->iftype == ARPHRD_CAN)
-                return false;
-
-        return link->network->dhcp & ADDRESS_FAMILY_IPV4;
-}
-
-static bool link_dhcp4_server_enabled(Link *link) {
-        assert(link);
-
-        if (link->flags & IFF_LOOPBACK)
-                return false;
-
-        if (!link->network)
-                return false;
-
-        if (link->network->bond)
-                return false;
-
-        if (link->iftype == ARPHRD_CAN)
-                return false;
-
-        return link->network->dhcp_server;
-}
-
 bool link_ipv4ll_enabled(Link *link, AddressFamily mask) {
         assert(link);
         assert((mask & ~(ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)) == 0);
@@ -159,7 +83,7 @@ bool link_ipv4ll_enabled(Link *link, AddressFamily mask) {
         return link->network->link_local & mask;
 }
 
-static bool link_ipv6ll_enabled(Link *link) {
+bool link_ipv6ll_enabled(Link *link) {
         assert(link);
 
         if (!socket_ipv6_is_supported())
@@ -183,7 +107,7 @@ static bool link_ipv6ll_enabled(Link *link) {
         return link->network->link_local & ADDRESS_FAMILY_IPV6;
 }
 
-static bool link_ipv6_enabled(Link *link) {
+bool link_ipv6_enabled(Link *link) {
         assert(link);
 
         if (!socket_ipv6_is_supported())
@@ -205,127 +129,6 @@ static bool link_ipv6_enabled(Link *link) {
         return false;
 }
 
-static bool link_radv_enabled(Link *link) {
-        assert(link);
-
-        if (!link_ipv6ll_enabled(link))
-                return false;
-
-        return link->network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE;
-}
-
-static bool link_ipv4_forward_enabled(Link *link) {
-        assert(link);
-
-        if (link->flags & IFF_LOOPBACK)
-                return false;
-
-        if (!link->network)
-                return false;
-
-        if (link->network->ip_forward == _ADDRESS_FAMILY_INVALID)
-                return false;
-
-        return link->network->ip_forward & ADDRESS_FAMILY_IPV4;
-}
-
-static bool link_ipv6_forward_enabled(Link *link) {
-        assert(link);
-
-        if (!socket_ipv6_is_supported())
-                return false;
-
-        if (link->flags & IFF_LOOPBACK)
-                return false;
-
-        if (!link->network)
-                return false;
-
-        if (link->network->ip_forward == _ADDRESS_FAMILY_INVALID)
-                return false;
-
-        return link->network->ip_forward & ADDRESS_FAMILY_IPV6;
-}
-
-static bool link_proxy_arp_enabled(Link *link) {
-        assert(link);
-
-        if (link->flags & IFF_LOOPBACK)
-                return false;
-
-        if (!link->network)
-                return false;
-
-        if (link->network->proxy_arp < 0)
-                return false;
-
-        return true;
-}
-
-static bool link_ipv6_accept_ra_enabled(Link *link) {
-        assert(link);
-
-        if (!socket_ipv6_is_supported())
-                return false;
-
-        if (link->flags & IFF_LOOPBACK)
-                return false;
-
-        if (!link->network)
-                return false;
-
-        if (!link_ipv6ll_enabled(link))
-                return false;
-
-        /* If unset use system default (enabled if local forwarding is disabled.
-         * disabled if local forwarding is enabled).
-         * If set, ignore or enforce RA independent of local forwarding state.
-         */
-        if (link->network->ipv6_accept_ra < 0)
-                /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */
-                return !link_ipv6_forward_enabled(link);
-        else if (link->network->ipv6_accept_ra > 0)
-                /* accept RA even if ip_forward is enabled */
-                return true;
-        else
-                /* ignore RA */
-                return false;
-}
-
-static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) {
-        assert(link);
-
-        if (!socket_ipv6_is_supported())
-                return _IPV6_PRIVACY_EXTENSIONS_INVALID;
-
-        if (link->flags & IFF_LOOPBACK)
-                return _IPV6_PRIVACY_EXTENSIONS_INVALID;
-
-        if (!link->network)
-                return _IPV6_PRIVACY_EXTENSIONS_INVALID;
-
-        return link->network->ipv6_privacy_extensions;
-}
-
-static int link_update_ipv6_sysctl(Link *link) {
-        bool enabled;
-        int r;
-
-        if (link->flags & IFF_LOOPBACK)
-                return 0;
-
-        enabled = link_ipv6_enabled(link);
-        if (enabled) {
-                r = sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", false);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Cannot enable IPv6: %m");
-
-                log_link_info(link, "IPv6 successfully enabled");
-        }
-
-        return 0;
-}
-
 static bool link_is_enslaved(Link *link) {
         if (link->flags & IFF_SLAVE)
                 /* Even if the link is not managed by networkd, honor IFF_SLAVE flag. */
@@ -703,8 +506,6 @@ static void link_free_engines(Link *link) {
 }
 
 static Link *link_free(Link *link) {
-        Address *address;
-
         assert(link);
 
         link_ntp_settings_clear(link);
@@ -728,6 +529,7 @@ static Link *link_free(Link *link) {
 
         link->addresses = set_free(link->addresses);
         link->addresses_foreign = set_free(link->addresses_foreign);
+        link->pool_addresses = set_free(link->pool_addresses);
         link->static_addresses = set_free(link->static_addresses);
         link->dhcp6_addresses = set_free(link->dhcp6_addresses);
         link->dhcp6_addresses_old = set_free(link->dhcp6_addresses_old);
@@ -735,11 +537,6 @@ static Link *link_free(Link *link) {
         link->dhcp6_pd_addresses_old = set_free(link->dhcp6_pd_addresses_old);
         link->ndisc_addresses = set_free(link->ndisc_addresses);
 
-        while ((address = link->pool_addresses)) {
-                LIST_REMOVE(addresses, link->pool_addresses, address);
-                address_free(address);
-        }
-
         link_lldp_emit_stop(link);
         link_free_engines(link);
         free(link->lease_file);
@@ -809,7 +606,6 @@ static void link_enter_unmanaged(Link *link) {
 
 int link_stop_clients(Link *link, bool may_keep_dhcp) {
         int r = 0, k;
-        Address *ad;
 
         assert(link);
         assert(link->manager);
@@ -832,13 +628,9 @@ int link_stop_clients(Link *link, bool may_keep_dhcp) {
                         r = log_link_warning_errno(link, k, "Could not stop IPv4 link-local: %m");
         }
 
-        if (link->network)
-                LIST_FOREACH(addresses, ad, link->network->static_addresses)
-                        if (ad->acd && sd_ipv4acd_is_running(ad->acd) == 0) {
-                                k = sd_ipv4acd_stop(ad->acd);
-                                if (k < 0)
-                                        r = log_link_warning_errno(link, k, "Could not stop IPv4 ACD client: %m");
-                        }
+        k = ipv4_dad_stop(link);
+        if (k < 0)
+                r = log_link_warning_errno(link, k, "Could not stop IPv4 ACD client: %m");
 
         if (link->dhcp6_client) {
                 k = sd_dhcp6_client_stop(link->dhcp6_client);
@@ -846,11 +638,9 @@ int link_stop_clients(Link *link, bool may_keep_dhcp) {
                         r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m");
         }
 
-        if (link_dhcp6_pd_is_enabled(link)) {
-                k = dhcp6_pd_remove(link);
-                if (k < 0)
-                        r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m");
-        }
+        k = dhcp6_pd_remove(link);
+        if (k < 0)
+                r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m");
 
         if (link->ndisc) {
                 k = sd_ndisc_stop(link->ndisc);
@@ -925,251 +715,79 @@ static void link_enter_configured(Link *link) {
         link_dirty(link);
 }
 
-static int link_request_set_routing_policy_rule(Link *link) {
-        RoutingPolicyRule *rule, *rrule = NULL;
-        int r;
+void link_check_ready(Link *link) {
+        Address *a;
 
         assert(link);
-        assert(link->network);
-
-        link->routing_policy_rules_configured = false;
-
-        LIST_FOREACH(rules, rule, link->network->rules) {
-                r = routing_policy_rule_get(link->manager, rule, &rrule);
-                if (r >= 0) {
-                        if (r == 0)
-                                (void) routing_policy_rule_make_local(link->manager, rrule);
-                        continue;
-                }
 
-                r = routing_policy_rule_configure(rule, link, NULL);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Could not set routing policy rules: %m");
-                if (r > 0)
-                        link->routing_policy_rule_messages++;
-        }
+        if (link->state == LINK_STATE_CONFIGURED)
+                return;
 
-        routing_policy_rule_purge(link->manager, link);
-        if (link->routing_policy_rule_messages == 0)
-                link->routing_policy_rules_configured = true;
-        else {
-                log_link_debug(link, "Setting routing policy rules");
-                link_set_state(link, LINK_STATE_CONFIGURING);
+        if (link->state != LINK_STATE_CONFIGURING) {
+                log_link_debug(link, "%s(): link is in %s state.", __func__, link_state_to_string(link->state));
+                return;
         }
 
-        return 0;
-}
-
-static int nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        int r;
-
-        assert(link);
-        assert(link->nexthop_messages > 0);
-        assert(IN_SET(link->state, LINK_STATE_CONFIGURING,
-                      LINK_STATE_FAILED, LINK_STATE_LINGER));
-
-        link->nexthop_messages--;
-
-        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
-                return 1;
+        if (!link->network)
+                return;
 
-        r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -EEXIST) {
-                log_link_message_warning_errno(link, m, r, "Could not set nexthop");
-                link_enter_failed(link);
-                return 1;
+        if (!link->addresses_configured) {
+                log_link_debug(link, "%s(): static addresses are not configured.", __func__);
+                return;
         }
 
-        if (link->nexthop_messages == 0) {
-                log_link_debug(link, "Nexthop set");
-                link->static_nexthops_configured = true;
-                link_check_ready(link);
+        if (!link->neighbors_configured) {
+                log_link_debug(link, "%s(): static neighbors are not configured.", __func__);
+                return;
         }
 
-        return 1;
-}
-
-static int link_request_set_nexthop(Link *link) {
-        NextHop *nh;
-        int r;
+        SET_FOREACH(a, link->addresses)
+                if (!address_is_ready(a)) {
+                        _cleanup_free_ char *str = NULL;
 
-        link->static_nexthops_configured = false;
+                        (void) in_addr_to_string(a->family, &a->in_addr, &str);
+                        log_link_debug(link, "%s(): an address %s/%d is not ready.", __func__, strnull(str), a->prefixlen);
+                        return;
+                }
 
-        LIST_FOREACH(nexthops, nh, link->network->static_nexthops) {
-                r = nexthop_configure(nh, link, nexthop_handler);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Could not set nexthop: %m");
-                if (r > 0)
-                        link->nexthop_messages++;
+        if (!link->static_routes_configured) {
+                log_link_debug(link, "%s(): static routes are not configured.", __func__);
+                return;
         }
 
-        if (link->nexthop_messages == 0) {
-                link->static_nexthops_configured = true;
-                link_check_ready(link);
-        } else {
-                log_link_debug(link, "Setting nexthop");
-                link_set_state(link, LINK_STATE_CONFIGURING);
+        if (!link->static_nexthops_configured) {
+                log_link_debug(link, "%s(): static nexthops are not configured.", __func__);
+                return;
         }
 
-        return 1;
-}
-
-static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        int r;
-
-        assert(link);
-        assert(link->route_messages > 0);
-        assert(IN_SET(link->state, LINK_STATE_CONFIGURING,
-                      LINK_STATE_FAILED, LINK_STATE_LINGER));
-
-        link->route_messages--;
+        if (!link->routing_policy_rules_configured) {
+                log_link_debug(link, "%s(): static routing policy rules are not configured.", __func__);
+                return;
+        }
 
-        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
-                return 1;
+        if (!link->tc_configured) {
+                log_link_debug(link, "%s(): traffic controls are not configured.", __func__);
+                return;
+        }
 
-        r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -EEXIST) {
-                log_link_message_warning_errno(link, m, r, "Could not set route");
-                link_enter_failed(link);
-                return 1;
+        if (!link->sr_iov_configured) {
+                log_link_debug(link, "%s(): SR-IOV is not configured.", __func__);
+                return;
         }
 
-        if (link->route_messages == 0) {
-                log_link_debug(link, "Routes set");
-                link->static_routes_configured = true;
-                link_request_set_nexthop(link);
+        if (!link->bridge_mdb_configured) {
+                log_link_debug(link, "%s(): Bridge MDB is not configured.", __func__);
+                return;
         }
 
-        return 1;
-}
+        if (link_has_carrier(link) || !link->network->configure_without_carrier) {
+                bool has_ndisc_address = false;
+                NDiscAddress *n;
 
-int link_request_set_routes(Link *link) {
-        enum {
-                PHASE_NON_GATEWAY, /* First phase: Routes without a gateway */
-                PHASE_GATEWAY,     /* Second phase: Routes with a gateway */
-                _PHASE_MAX
-        } phase;
-        Route *rt;
-        int r;
-
-        assert(link);
-        assert(link->network);
-        assert(link->state != _LINK_STATE_INVALID);
-
-        link->static_routes_configured = false;
-
-        if (!link->addresses_ready)
-                return 0;
-
-        if (!link_has_carrier(link) && !link->network->configure_without_carrier)
-                /* During configuring addresses, the link lost its carrier. As networkd is dropping
-                 * the addresses now, let's not configure the routes either. */
-                return 0;
-
-        r = link_request_set_routing_policy_rule(link);
-        if (r < 0)
-                return r;
-
-        /* First add the routes that enable us to talk to gateways, then add in the others that need a gateway. */
-        for (phase = 0; phase < _PHASE_MAX; phase++)
-                LIST_FOREACH(routes, rt, link->network->static_routes) {
-                        if (rt->gateway_from_dhcp)
-                                continue;
-
-                        if ((in_addr_is_null(rt->family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY))
-                                continue;
-
-                        r = route_configure(rt, link, route_handler, NULL);
-                        if (r < 0)
-                                return log_link_warning_errno(link, r, "Could not set routes: %m");
-                        if (r > 0)
-                                link->route_messages++;
-                }
-
-        if (link->route_messages == 0) {
-                link->static_routes_configured = true;
-                link_request_set_nexthop(link);
-        } else {
-                log_link_debug(link, "Setting routes");
-                link_set_state(link, LINK_STATE_CONFIGURING);
-        }
-
-        return 0;
-}
-
-void link_check_ready(Link *link) {
-        Address *a;
-
-        assert(link);
-
-        if (link->state == LINK_STATE_CONFIGURED)
-                return;
-
-        if (link->state != LINK_STATE_CONFIGURING) {
-                log_link_debug(link, "%s(): link is in %s state.", __func__, link_state_to_string(link->state));
-                return;
-        }
-
-        if (!link->network)
-                return;
-
-        if (!link->addresses_configured) {
-                log_link_debug(link, "%s(): static addresses are not configured.", __func__);
-                return;
-        }
-
-        if (!link->neighbors_configured) {
-                log_link_debug(link, "%s(): static neighbors are not configured.", __func__);
-                return;
-        }
-
-        SET_FOREACH(a, link->addresses)
-                if (!address_is_ready(a)) {
-                        _cleanup_free_ char *str = NULL;
-
-                        (void) in_addr_to_string(a->family, &a->in_addr, &str);
-                        log_link_debug(link, "%s(): an address %s/%d is not ready.", __func__, strnull(str), a->prefixlen);
-                        return;
-                }
-
-        if (!link->static_routes_configured) {
-                log_link_debug(link, "%s(): static routes are not configured.", __func__);
-                return;
-        }
-
-        if (!link->static_nexthops_configured) {
-                log_link_debug(link, "%s(): static nexthops are not configured.", __func__);
-                return;
-        }
-
-        if (!link->routing_policy_rules_configured) {
-                log_link_debug(link, "%s(): static routing policy rules are not configured.", __func__);
-                return;
-        }
-
-        if (!link->tc_configured) {
-                log_link_debug(link, "%s(): traffic controls are not configured.", __func__);
-                return;
-        }
-
-        if (!link->sr_iov_configured) {
-                log_link_debug(link, "%s(): SR-IOV is not configured.", __func__);
-                return;
-        }
-
-        if (!link->bridge_mdb_configured) {
-                log_link_debug(link, "%s(): Bridge MDB is not configured.", __func__);
-                return;
-        }
-
-        if (link_has_carrier(link) || !link->network->configure_without_carrier) {
-                bool has_ndisc_address = false;
-                NDiscAddress *n;
-
-                if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address_configured) {
-                        log_link_debug(link, "%s(): IPv4LL is not configured.", __func__);
-                        return;
-                }
+                if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address_configured) {
+                        log_link_debug(link, "%s(): IPv4LL is not configured.", __func__);
+                        return;
+                }
 
                 if (link_ipv6ll_enabled(link) &&
                     in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address)) {
@@ -1219,165 +837,13 @@ void link_check_ready(Link *link) {
         return;
 }
 
-static int link_request_set_neighbors(Link *link) {
-        Neighbor *neighbor;
-        int r;
-
-        assert(link);
-        assert(link->network);
-        assert(link->state != _LINK_STATE_INVALID);
-
-        link->neighbors_configured = false;
-
-        LIST_FOREACH(neighbors, neighbor, link->network->neighbors) {
-                r = neighbor_configure(neighbor, link, NULL);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Could not set neighbor: %m");
-        }
-
-        if (link->neighbor_messages == 0) {
-                link->neighbors_configured = true;
-                link_check_ready(link);
-        } else {
-                log_link_debug(link, "Setting neighbors");
-                link_set_state(link, LINK_STATE_CONFIGURING);
-        }
-
-        return 0;
-}
-
-static int link_set_bridge_fdb(Link *link) {
-        FdbEntry *fdb_entry;
-        int r;
-
-        LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) {
-                r = fdb_entry_configure(link, fdb_entry);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m");
-        }
-
-        return 0;
-}
-
-static int static_address_ready_callback(Address *address) {
-        Address *a;
-        Link *link;
-
-        assert(address);
-        assert(address->link);
-
-        link = address->link;
-
-        if (!link->addresses_configured)
-                return 0;
-
-        SET_FOREACH(a, link->static_addresses)
-                if (!address_is_ready(a)) {
-                        _cleanup_free_ char *str = NULL;
-
-                        (void) in_addr_to_string(a->family, &a->in_addr, &str);
-                        log_link_debug(link, "an address %s/%u is not ready", strnull(str), a->prefixlen);
-                        return 0;
-                }
-
-        /* This should not be called again */
-        SET_FOREACH(a, link->static_addresses)
-                a->callback = NULL;
-
-        link->addresses_ready = true;
-
-        return link_request_set_routes(link);
-}
-
-static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        int r;
-
-        assert(rtnl);
-        assert(m);
-        assert(link);
-        assert(link->ifname);
-        assert(link->address_messages > 0);
-        assert(IN_SET(link->state, LINK_STATE_CONFIGURING,
-               LINK_STATE_FAILED, LINK_STATE_LINGER));
-
-        link->address_messages--;
-
-        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
-                return 1;
-
-        r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -EEXIST) {
-                log_link_message_warning_errno(link, m, r, "Could not set address");
-                link_enter_failed(link);
-                return 1;
-        } else if (r >= 0)
-                (void) manager_rtnl_process_address(rtnl, m, link->manager);
-
-        if (link->address_messages == 0) {
-                Address *a;
-
-                log_link_debug(link, "Addresses set");
-                link->addresses_configured = true;
-
-                /* When all static addresses are already ready, then static_address_ready_callback()
-                 * will not be called automatically. So, call it here. */
-                a = set_first(link->static_addresses);
-                if (!a) {
-                        log_link_warning(link, "No static address is stored.");
-                        link_enter_failed(link);
-                        return 1;
-                }
-                if (!a->callback) {
-                        log_link_warning(link, "Address ready callback is not set.");
-                        link_enter_failed(link);
-                        return 1;
-                }
-                r = a->callback(a);
-                if (r < 0)
-                        link_enter_failed(link);
-        }
-
-        return 1;
-}
-
-static int static_address_configure(Address *address, Link *link, bool update) {
-        Address *ret;
-        int r;
-
-        assert(address);
-        assert(link);
-
-        r = address_configure(address, link, address_handler, update, &ret);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Could not configure static address: %m");
-
-        link->address_messages++;
-
-        r = set_ensure_put(&link->static_addresses, &address_hash_ops, ret);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to store static address: %m");
-
-        ret->callback = static_address_ready_callback;
-
-        return 0;
-}
-
-static int link_request_set_addresses(Link *link) {
-        AddressLabel *label;
-        Address *ad;
-        Prefix *p;
+static int link_set_static_configs(Link *link) {
         int r;
 
         assert(link);
         assert(link->network);
         assert(link->state != _LINK_STATE_INVALID);
 
-        if (link->address_remove_messages != 0) {
-                log_link_debug(link, "Removing old addresses, new addresses will be configured later.");
-                link->request_static_addresses = true;
-                return 0;
-        }
-
         /* Reset all *_configured flags we are configuring. */
         link->request_static_addresses = false;
         link->addresses_configured = false;
@@ -1395,97 +861,22 @@ static int link_request_set_addresses(Link *link) {
         if (r < 0)
                 return r;
 
-        r = link_request_set_neighbors(link);
+        r = link_set_neighbors(link);
         if (r < 0)
                 return r;
 
-        LIST_FOREACH(addresses, ad, link->network->static_addresses) {
-                bool update;
-
-                if (ad->family == AF_INET6 && !in_addr_is_null(ad->family, &ad->in_addr_peer))
-                        update = address_get(link, ad->family, &ad->in_addr_peer, ad->prefixlen, NULL) > 0;
-                else
-                        update = address_get(link, ad->family, &ad->in_addr, ad->prefixlen, NULL) > 0;
-
-                r = static_address_configure(ad, link, update);
-                if (r < 0)
-                        return r;
-        }
-
-        if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC)
-                LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
-                        _cleanup_(address_freep) Address *address = NULL;
-
-                        if (!p->assign)
-                                continue;
-
-                        r = address_new(&address);
-                        if (r < 0)
-                                return log_oom();
-
-                        r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen);
-                        if (r < 0)
-                                return log_link_warning_errno(link, r, "Could not get RA prefix: %m");
-
-                        r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
-                        if (r < 0)
-                                return log_link_warning_errno(link, r, "Could not generate EUI64 address: %m");
-
-                        address->family = AF_INET6;
-                        r = static_address_configure(address, link, true);
-                        if (r < 0)
-                                return r;
-                }
-
-        LIST_FOREACH(labels, label, link->network->address_labels) {
-                r = address_label_configure(label, link, NULL, false);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Could not set address label: %m");
-
-                link->address_label_messages++;
-        }
-
-        /* now that we can figure out a default address for the dhcp server, start it */
-        if (link_dhcp4_server_enabled(link) && (link->flags & IFF_UP)) {
-                r = dhcp4_server_configure(link);
-                if (r < 0)
-                        return r;
-                log_link_debug(link, "Offering DHCPv4 leases");
-        }
-
-        if (link->address_messages == 0) {
-                link->addresses_configured = true;
-                link->addresses_ready = true;
-                r = link_request_set_routes(link);
-                if (r < 0)
-                        return r;
-        } else {
-                log_link_debug(link, "Setting addresses");
-                link_set_state(link, LINK_STATE_CONFIGURING);
-        }
-
-        return 0;
-}
-
-static int link_set_bridge_vlan(Link *link) {
-        int r;
-
-        r = br_vlan_configure(link, link->network->pvid, link->network->br_vid_bitmap, link->network->br_untagged_bitmap);
+        r = link_set_addresses(link);
         if (r < 0)
-                log_link_error_errno(link, r, "Failed to assign VLANs to bridge port: %m");
-
-        return r;
-}
-
-static int link_set_proxy_arp(Link *link) {
-        int r;
+                return r;
 
-        if (!link_proxy_arp_enabled(link))
-                return 0;
+        r = link_set_address_labels(link);
+        if (r < 0)
+                return r;
 
-        r = sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0);
+        /* now that we can figure out a default address for the dhcp server, start it */
+        r = dhcp4_server_configure(link);
         if (r < 0)
-                log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface: %m");
+                return r;
 
         return 0;
 }
@@ -1679,9 +1070,7 @@ static int link_acquire_ipv6_conf(Link *link) {
 
         assert(link);
 
-        if (link_ipv6_accept_ra_enabled(link)) {
-                assert(link->ndisc);
-
+        if (link->ndisc) {
                 log_link_debug(link, "Discovering IPv6 routers");
 
                 r = sd_ndisc_start(link->ndisc);
@@ -1689,7 +1078,7 @@ static int link_acquire_ipv6_conf(Link *link) {
                         return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m");
         }
 
-        if (link_radv_enabled(link)) {
+        if (link->radv) {
                 assert(link->radv);
                 assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
 
@@ -1741,9 +1130,7 @@ static int link_acquire_ipv4_conf(Link *link) {
                         return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
         }
 
-        if (link_dhcp4_enabled(link)) {
-                assert(link->dhcp_client);
-
+        if (link->dhcp_client) {
                 log_link_debug(link, "Acquiring DHCPv4 lease");
 
                 r = sd_dhcp_client_start(link->dhcp_client);
@@ -1769,11 +1156,9 @@ static int link_acquire_conf(Link *link) {
                         return r;
         }
 
-        if (link_lldp_emit_enabled(link)) {
-                r = link_lldp_emit_start(link);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m");
-        }
+        r = link_lldp_emit_start(link);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m");
 
         return 0;
 }
@@ -2362,12 +1747,9 @@ static int link_joined(Link *link) {
                         log_link_error_errno(link, r, "Failed to add to bond master's slave list: %m");
         }
 
-        if (link->network->use_br_vlan &&
-            (link->network->bridge || streq_ptr("bridge", link->kind))) {
-                r = link_set_bridge_vlan(link);
-                if (r < 0)
-                        log_link_error_errno(link, r, "Could not set bridge vlan: %m");
-        }
+        r = link_set_bridge_vlan(link);
+        if (r < 0)
+                log_link_error_errno(link, r, "Could not set bridge vlan: %m");
 
         /* Skip setting up addresses until it gets carrier,
            or it would try to set addresses twice,
@@ -2376,7 +1758,7 @@ static int link_joined(Link *link) {
                 return 0;
 
         link_set_state(link, LINK_STATE_CONFIGURING);
-        return link_request_set_addresses(link);
+        return link_set_static_configs(link);
 }
 
 static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
@@ -2516,519 +1898,41 @@ static int link_enter_join_netdev(Link *link) {
         return 0;
 }
 
-static int link_set_ipv4_forward(Link *link) {
+static int link_drop_foreign_config(Link *link) {
         int r;
 
-        if (!link_ipv4_forward_enabled(link))
-                return 0;
-
-        /* We propagate the forwarding flag from one interface to the
-         * global setting one way. This means: as long as at least one
-         * interface was configured at any time that had IP forwarding
-         * enabled the setting will stay on for good. We do this
-         * primarily to keep IPv4 and IPv6 packet forwarding behaviour
-         * somewhat in sync (see below). */
+        r = link_drop_foreign_addresses(link);
+        if (r < 0)
+                return r;
 
-        r = sysctl_write_ip_property(AF_INET, NULL, "ip_forward", "1");
+        r = link_drop_foreign_neighbors(link);
         if (r < 0)
-                log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m");
+                return r;
 
-        return 0;
+        return link_drop_foreign_routes(link);
 }
 
-static int link_set_ipv6_forward(Link *link) {
-        int r;
-
-        if (!link_ipv6_forward_enabled(link))
-                return 0;
-
-        /* On Linux, the IPv6 stack does not know a per-interface
-         * packet forwarding setting: either packet forwarding is on
-         * for all, or off for all. We hence don't bother with a
-         * per-interface setting, but simply propagate the interface
-         * flag, if it is set, to the global flag, one-way. Note that
-         * while IPv4 would allow a per-interface flag, we expose the
-         * same behaviour there and also propagate the setting from
-         * one to all, to keep things simple (see above). */
-
-        r = sysctl_write_ip_property(AF_INET6, "all", "forwarding", "1");
-        if (r < 0)
-                log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m");
-
-        return 0;
-}
-
-static int link_set_ipv6_privacy_extensions(Link *link) {
-        IPv6PrivacyExtensions s;
-        int r;
-
-        s = link_ipv6_privacy_extensions(link);
-        if (s < 0)
-                return 0;
-
-        r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) link->network->ipv6_privacy_extensions);
-        if (r < 0)
-                log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extension for interface: %m");
-
-        return 0;
-}
-
-static int link_set_ipv6_accept_ra(Link *link) {
-        int r;
-
-        /* Make this a NOP if IPv6 is not available */
-        if (!socket_ipv6_is_supported())
-                return 0;
-
-        if (link->flags & IFF_LOOPBACK)
-                return 0;
-
-        if (!link->network)
-                return 0;
-
-        r = sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0");
-        if (r < 0)
-                log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface: %m");
-
-        return 0;
-}
-
-static int link_set_ipv6_dad_transmits(Link *link) {
-        int r;
-
-        /* Make this a NOP if IPv6 is not available */
-        if (!socket_ipv6_is_supported())
-                return 0;
-
-        if (link->flags & IFF_LOOPBACK)
-                return 0;
-
-        if (!link->network)
-                return 0;
-
-        if (link->network->ipv6_dad_transmits < 0)
-                return 0;
-
-        r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits);
-        if (r < 0)
-                log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface: %m");
-
-        return 0;
-}
-
-static int link_set_ipv6_hop_limit(Link *link) {
-        int r;
-
-        /* Make this a NOP if IPv6 is not available */
-        if (!socket_ipv6_is_supported())
-                return 0;
-
-        if (link->flags & IFF_LOOPBACK)
-                return 0;
-
-        if (!link->network)
-                return 0;
-
-        if (link->network->ipv6_hop_limit < 0)
-                return 0;
-
-        r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit);
-        if (r < 0)
-                log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface: %m");
-
-        return 0;
-}
-
-static int link_set_ipv6_mtu(Link *link) {
-        int r;
-
-        /* Make this a NOP if IPv6 is not available */
-        if (!socket_ipv6_is_supported())
-                return 0;
-
-        if (link->flags & IFF_LOOPBACK)
-                return 0;
-
-        if (link->network->ipv6_mtu == 0)
-                return 0;
-
-        /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes
-         * on the interface. Bump up IPv6 MTU bytes to IPV6_MTU_MIN. */
-        if (link->network->ipv6_mtu < IPV6_MIN_MTU) {
-                log_link_notice(link, "Bumping IPv6 MTU to "STRINGIFY(IPV6_MIN_MTU)" byte minimum required");
-                link->network->ipv6_mtu = IPV6_MIN_MTU;
-        }
-
-        r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", link->network->ipv6_mtu);
-        if (r < 0) {
-                if (link->mtu < link->network->ipv6_mtu)
-                        log_link_warning(link, "Cannot set IPv6 MTU %"PRIu32" higher than device MTU %"PRIu32,
-                                         link->network->ipv6_mtu, link->mtu);
-                else
-                        log_link_warning_errno(link, r, "Cannot set IPv6 MTU for interface: %m");
-        }
-
-        link->ipv6_mtu_set = true;
-
-        return 0;
-}
-
-static int link_set_ipv4_accept_local(Link *link) {
-        int r;
-
-        if (link->flags & IFF_LOOPBACK)
-                return 0;
-
-        if (link->network->ipv4_accept_local < 0)
-                return 0;
-
-        r = sysctl_write_ip_property_boolean(AF_INET, link->ifname, "accept_local", link->network->ipv4_accept_local);
-        if (r < 0)
-                log_link_warning_errno(link, r, "Cannot set IPv4 accept_local flag for interface: %m");
-
-        return 0;
-}
-
-static bool link_is_static_address_configured(Link *link, Address *address) {
-        Address *net_address;
-
-        assert(link);
-        assert(address);
-
-        if (!link->network)
-                return false;
-
-        LIST_FOREACH(addresses, net_address, link->network->static_addresses)
-                if (address_equal(net_address, address))
-                        return true;
-                else if (address->family == AF_INET6 && net_address->family == AF_INET6 &&
-                         in_addr_equal(AF_INET6, &address->in_addr, &net_address->in_addr_peer) > 0)
-                        return true;
-
-        return false;
-}
-
-static bool link_is_neighbor_configured(Link *link, Neighbor *neighbor) {
-        Neighbor *net_neighbor;
-
-        assert(link);
-        assert(neighbor);
-
-        if (!link->network)
-                return false;
-
-        LIST_FOREACH(neighbors, net_neighbor, link->network->neighbors)
-                if (neighbor_equal(net_neighbor, neighbor))
-                        return true;
-
-        return false;
-}
-
-static bool link_is_static_route_configured(Link *link, Route *route) {
-        Route *net_route;
-
-        assert(link);
-        assert(route);
-
-        if (!link->network)
-                return false;
-
-        LIST_FOREACH(routes, net_route, link->network->static_routes)
-                if (route_equal(net_route, route))
-                        return true;
-
-        return false;
-}
-
-static bool link_address_is_dynamic(Link *link, Address *address) {
-        Route *route;
-
-        assert(link);
-        assert(address);
-
-        if (address->cinfo.ifa_prefered != CACHE_INFO_INFINITY_LIFE_TIME)
-                return true;
-
-        /* Even when the address is leased from a DHCP server, networkd assign the address
-         * without lifetime when KeepConfiguration=dhcp. So, let's check that we have
-         * corresponding routes with RTPROT_DHCP. */
-        SET_FOREACH(route, link->routes_foreign) {
-                if (route->protocol != RTPROT_DHCP)
-                        continue;
-
-                if (address->family != route->family)
-                        continue;
-
-                if (in_addr_equal(address->family, &address->in_addr, &route->prefsrc))
-                        return true;
-        }
-
-        return false;
-}
-
-static int link_enumerate_ipv6_tentative_addresses(Link *link) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
-        sd_netlink_message *addr;
+static int link_drop_config(Link *link) {
         int r;
 
-        assert(link);
-        assert(link->manager);
-        assert(link->manager->rtnl);
-
-        r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_GETADDR, 0, AF_INET6);
+        r = link_drop_addresses(link);
         if (r < 0)
                 return r;
 
-        r = sd_netlink_call(link->manager->rtnl, req, 0, &reply);
+        r = link_drop_neighbors(link);
         if (r < 0)
-                return r;
-
-        for (addr = reply; addr; addr = sd_netlink_message_next(addr)) {
-                unsigned char flags;
-                int ifindex;
-
-                r = sd_rtnl_message_addr_get_ifindex(addr, &ifindex);
-                if (r < 0) {
-                        log_link_warning_errno(link, r, "rtnl: invalid ifindex, ignoring: %m");
-                        continue;
-                } else if (link->ifindex != ifindex)
-                        continue;
-
-                r = sd_rtnl_message_addr_get_flags(addr, &flags);
-                if (r < 0) {
-                        log_link_warning_errno(link, r, "rtnl: received address message with invalid flags, ignoring: %m");
-                        continue;
-                } else if (!(flags & IFA_F_TENTATIVE))
-                        continue;
-
-                log_link_debug(link, "Found tentative ipv6 link-local address");
-                (void) manager_rtnl_process_address(link->manager->rtnl, addr, link->manager);
-        }
-
-        return 0;
-}
-
-static int link_drop_foreign_config(Link *link) {
-        Address *address;
-        Neighbor *neighbor;
-        Route *route;
-        int r;
-
-        /* The kernel doesn't notify us about tentative addresses;
-         * so if ipv6ll is disabled, we need to enumerate them now so we can drop them below */
-        if (!link_ipv6ll_enabled(link)) {
-                r = link_enumerate_ipv6_tentative_addresses(link);
-                if (r < 0)
-                        return r;
-        }
-
-        SET_FOREACH(address, link->addresses_foreign) {
-                /* we consider IPv6LL addresses to be managed by the kernel */
-                if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1 && link_ipv6ll_enabled(link))
-                        continue;
-
-                if (link_address_is_dynamic(link, address)) {
-                        if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
-                                continue;
-                } else if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
-                        continue;
-
-                if (link_is_static_address_configured(link, address)) {
-                        r = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL);
-                        if (r < 0)
-                                return log_link_error_errno(link, r, "Failed to add address: %m");
-                } else {
-                        r = address_remove(address, link, NULL);
-                        if (r < 0)
-                                return r;
-                }
-        }
-
-        SET_FOREACH(neighbor, link->neighbors_foreign) {
-                if (link_is_neighbor_configured(link, neighbor)) {
-                        r = neighbor_add(link, neighbor->family, &neighbor->in_addr, &neighbor->lladdr, neighbor->lladdr_size, NULL);
-                        if (r < 0)
-                                return r;
-                } else {
-                        r = neighbor_remove(neighbor, link, NULL);
-                        if (r < 0)
-                                return r;
-                }
-        }
-
-        SET_FOREACH(route, link->routes_foreign) {
-                /* do not touch routes managed by the kernel */
-                if (route->protocol == RTPROT_KERNEL)
-                        continue;
-
-                /* do not touch multicast route added by kernel */
-                /* FIXME: Why the kernel adds this route with protocol RTPROT_BOOT??? We need to investigate that.
-                 * https://tools.ietf.org/html/rfc4862#section-5.4 may explain why. */
-                if (route->protocol == RTPROT_BOOT &&
-                    route->family == AF_INET6 &&
-                    route->dst_prefixlen == 8 &&
-                    in_addr_equal(AF_INET6, &route->dst, &(union in_addr_union) { .in6 = {{{ 0xff,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }}} }))
-                        continue;
-
-                if (route->protocol == RTPROT_STATIC && link->network &&
-                    FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
-                        continue;
-
-                if (route->protocol == RTPROT_DHCP && link->network &&
-                    FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
-                        continue;
-
-                if (link_is_static_route_configured(link, route)) {
-                        r = route_add(link, route, NULL);
-                        if (r < 0)
-                                return r;
-                } else {
-                        r = route_remove(route, link, NULL);
-                        if (r < 0)
-                                return r;
-                }
-        }
-
-        return 0;
-}
-
-static int remove_static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        int r;
-
-        assert(m);
-        assert(link);
-        assert(link->ifname);
-        assert(link->address_remove_messages > 0);
-
-        link->address_remove_messages--;
-
-        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
-                return 1;
-
-        r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -EADDRNOTAVAIL)
-                log_link_message_warning_errno(link, m, r, "Could not drop address");
-        else if (r >= 0)
-                (void) manager_rtnl_process_address(rtnl, m, link->manager);
-
-        if (link->address_remove_messages == 0 && link->request_static_addresses) {
-                link_set_state(link, LINK_STATE_CONFIGURING);
-                r = link_request_set_addresses(link);
-                if (r < 0)
-                        link_enter_failed(link);
-        }
-
-        return 1;
-}
-
-static int link_drop_config(Link *link) {
-        Address *address, *pool_address;
-        Neighbor *neighbor;
-        Route *route;
-        int r;
-
-        SET_FOREACH(address, link->addresses) {
-                /* we consider IPv6LL addresses to be managed by the kernel */
-                if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1 && link_ipv6ll_enabled(link))
-                        continue;
-
-                r = address_remove(address, link, remove_static_address_handler);
-                if (r < 0)
-                        return r;
-
-                link->address_remove_messages++;
-
-                /* If this address came from an address pool, clean up the pool */
-                LIST_FOREACH(addresses, pool_address, link->pool_addresses)
-                        if (address_equal(address, pool_address)) {
-                                LIST_REMOVE(addresses, link->pool_addresses, pool_address);
-                                address_free(pool_address);
-                                break;
-                        }
-        }
-
-        SET_FOREACH(neighbor, link->neighbors) {
-                r = neighbor_remove(neighbor, link, NULL);
-                if (r < 0)
-                        return r;
-        }
-
-        SET_FOREACH(route, link->routes) {
-                /* do not touch routes managed by the kernel */
-                if (route->protocol == RTPROT_KERNEL)
-                        continue;
-
-                r = route_remove(route, link, NULL);
-                if (r < 0)
-                        return r;
-        }
-
-        ndisc_flush(link);
-
-        return 0;
-}
-
-static int link_configure_ipv4_dad(Link *link) {
-        Address *address;
-        int r;
-
-        assert(link);
-        assert(link->network);
-
-        LIST_FOREACH(addresses, address, link->network->static_addresses)
-                if (address->family == AF_INET &&
-                    FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4)) {
-                        r = configure_ipv4_duplicate_address_detection(link, address);
-                        if (r < 0)
-                                return log_link_error_errno(link, r, "Failed to configure IPv4ACD: %m");
-                }
-
-        return 0;
-}
-
-static int link_configure_traffic_control(Link *link) {
-        TrafficControl *tc;
-        int r;
-
-        link->tc_configured = false;
-        link->tc_messages = 0;
-
-        ORDERED_HASHMAP_FOREACH(tc, link->network->tc_by_section) {
-                r = traffic_control_configure(link, tc);
-                if (r < 0)
-                        return r;
-        }
-
-        if (link->tc_messages == 0)
-                link->tc_configured = true;
-        else
-                log_link_debug(link, "Configuring traffic control");
-
-        return 0;
-}
-
-static int link_configure_sr_iov(Link *link) {
-        SRIOV *sr_iov;
-        int r;
-
-        link->sr_iov_configured = false;
-        link->sr_iov_messages = 0;
-
-        ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section) {
-                r = sr_iov_configure(link, sr_iov);
-                if (r < 0)
-                        return r;
-        }
+                return r;
 
-        if (link->sr_iov_messages == 0)
-                link->sr_iov_configured = true;
-        else
-                log_link_debug(link, "Configuring SR-IOV");
+        r = link_drop_routes(link);
+        if (r < 0)
+                return r;
+
+        ndisc_flush(link);
 
         return 0;
 }
 
-static int link_configure(Link *link) {
+int link_configure(Link *link) {
         int r;
 
         assert(link);
@@ -3046,105 +1950,46 @@ static int link_configure(Link *link) {
         if (link->iftype == ARPHRD_CAN)
                 return link_configure_can(link);
 
-        /* If IPv6 configured that is static IPv6 address and IPv6LL autoconfiguration is enabled
-         * for this interface, then enable IPv6 */
-        (void) link_update_ipv6_sysctl(link);
-
-        r = link_set_proxy_arp(link);
-        if (r < 0)
-               return r;
-
-        r = ipv6_proxy_ndp_addresses_configure(link);
+        r = link_set_sysctl(link);
         if (r < 0)
                 return r;
 
-        r = link_set_ipv4_forward(link);
+        r = link_set_ipv6_proxy_ndp_addresses(link);
         if (r < 0)
                 return r;
 
-        r = link_set_ipv6_forward(link);
+        r = link_set_flags(link);
         if (r < 0)
                 return r;
 
-        r = link_set_ipv6_privacy_extensions(link);
+        r = link_set_group(link);
         if (r < 0)
                 return r;
 
-        r = link_set_ipv6_accept_ra(link);
+        r = ipv4ll_configure(link);
         if (r < 0)
                 return r;
 
-        r = link_set_ipv6_dad_transmits(link);
+        r = dhcp4_configure(link);
         if (r < 0)
                 return r;
 
-        r = link_set_ipv6_hop_limit(link);
+        r = dhcp6_configure(link);
         if (r < 0)
                 return r;
 
-        r = link_set_ipv4_accept_local(link);
+        r = ndisc_configure(link);
         if (r < 0)
                 return r;
 
-        r = link_set_flags(link);
+        r = radv_configure(link);
         if (r < 0)
                 return r;
 
-        r = link_set_group(link);
+        r = link_lldp_rx_configure(link);
         if (r < 0)
                 return r;
 
-        if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)) {
-                r = ipv4ll_configure(link);
-                if (r < 0)
-                        return r;
-        }
-
-        if (link_dhcp4_enabled(link)) {
-                r = dhcp4_set_promote_secondaries(link);
-                if (r < 0)
-                        return r;
-
-                r = dhcp4_configure(link);
-                if (r < 0)
-                        return r;
-        }
-
-        if (link_dhcp4_server_enabled(link)) {
-                r = sd_dhcp_server_new(&link->dhcp_server, link->ifindex);
-                if (r < 0)
-                        return r;
-
-                r = sd_dhcp_server_attach_event(link->dhcp_server, NULL, 0);
-                if (r < 0)
-                        return r;
-        }
-
-        if (link_dhcp6_enabled(link) ||
-            link_ipv6_accept_ra_enabled(link)) {
-                r = dhcp6_configure(link);
-                if (r < 0)
-                        return r;
-        }
-
-        if (link_ipv6_accept_ra_enabled(link)) {
-                r = ndisc_configure(link);
-                if (r < 0)
-                        return r;
-        }
-
-        if (link_radv_enabled(link)) {
-                r = radv_configure(link);
-                if (r < 0)
-                        return r;
-        }
-
-        if (link_lldp_rx_enabled(link)) {
-                r = link_lldp_rx_configure(link);
-                if (r < 0)
-                        return r;
-        }
-
         r = link_configure_mtu(link);
         if (r < 0)
                 return r;
@@ -3153,10 +1998,6 @@ static int link_configure(Link *link) {
         if (r < 0)
                 return r;
 
-        r = link_configure_ipv4_dad(link);
-        if (r < 0)
-                return r;
-
         return link_configure_continue(link);
 }
 
@@ -3195,7 +2036,7 @@ static int link_configure_continue(Link *link) {
          * we must set this here, after we've set device mtu */
         r = link_set_ipv6_mtu(link);
         if (r < 0)
-                return r;
+                log_link_warning_errno(link, r, "Cannot set IPv6 MTU for interface, ignoring: %m");
 
         if (link_has_carrier(link) || link->network->configure_without_carrier) {
                 r = link_acquire_conf(link);
@@ -3206,139 +2047,6 @@ static int link_configure_continue(Link *link) {
         return link_enter_join_netdev(link);
 }
 
-static int duid_set_uuid(DUID *duid, sd_id128_t uuid) {
-        assert(duid);
-
-        if (duid->raw_data_len > 0)
-                return 0;
-
-        if (duid->type != DUID_TYPE_UUID)
-                return -EINVAL;
-
-        memcpy(&duid->raw_data, &uuid, sizeof(sd_id128_t));
-        duid->raw_data_len = sizeof(sd_id128_t);
-
-        return 1;
-}
-
-int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
-        Manager *manager = userdata;
-        const sd_bus_error *e;
-        const void *a;
-        size_t sz;
-        DUID *duid;
-        Link *link;
-        int r;
-
-        assert(m);
-        assert(manager);
-
-        e = sd_bus_message_get_error(m);
-        if (e) {
-                log_error_errno(sd_bus_error_get_errno(e),
-                                "Could not get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %s",
-                                e->message);
-                goto configure;
-        }
-
-        r = sd_bus_message_read_array(m, 'y', &a, &sz);
-        if (r < 0)
-                goto configure;
-
-        if (sz != sizeof(sd_id128_t)) {
-                log_error("Invalid product UUID. Falling back to use machine-app-specific ID as DUID-UUID.");
-                goto configure;
-        }
-
-        memcpy(&manager->product_uuid, a, sz);
-        while ((duid = set_steal_first(manager->duids_requesting_uuid)))
-                (void) duid_set_uuid(duid, manager->product_uuid);
-
-        manager->duids_requesting_uuid = set_free(manager->duids_requesting_uuid);
-
-configure:
-        while ((link = set_steal_first(manager->links_requesting_uuid))) {
-                link_unref(link);
-
-                r = link_configure(link);
-                if (r < 0)
-                        link_enter_failed(link);
-        }
-
-        manager->links_requesting_uuid = set_free(manager->links_requesting_uuid);
-
-        /* To avoid calling GetProductUUID() bus method so frequently, set the flag below
-         * even if the method fails. */
-        manager->has_product_uuid = true;
-
-        return 1;
-}
-
-static bool link_requires_uuid(Link *link) {
-        const DUID *duid;
-
-        assert(link);
-        assert(link->manager);
-        assert(link->network);
-
-        duid = link_get_duid(link);
-        if (duid->type != DUID_TYPE_UUID || duid->raw_data_len != 0)
-                return false;
-
-        if (link_dhcp4_enabled(link) && IN_SET(link->network->dhcp_client_identifier, DHCP_CLIENT_ID_DUID, DHCP_CLIENT_ID_DUID_ONLY))
-                return true;
-
-        if (link_dhcp6_enabled(link) || link_ipv6_accept_ra_enabled(link))
-                return true;
-
-        return false;
-}
-
-static int link_configure_duid(Link *link) {
-        Manager *m;
-        DUID *duid;
-        int r;
-
-        assert(link);
-        assert(link->manager);
-        assert(link->network);
-
-        m = link->manager;
-        duid = link_get_duid(link);
-
-        if (!link_requires_uuid(link))
-                return 1;
-
-        if (m->has_product_uuid) {
-                (void) duid_set_uuid(duid, m->product_uuid);
-                return 1;
-        }
-
-        if (!m->links_requesting_uuid) {
-                r = manager_request_product_uuid(m, link);
-                if (r < 0) {
-                        if (r == -ENOMEM)
-                                return r;
-
-                        log_link_warning_errno(link, r,
-                                               "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
-                        return 1;
-                }
-        } else {
-                r = set_put(m->links_requesting_uuid, link);
-                if (r < 0)
-                        return log_oom();
-                if (r > 0)
-                        link_ref(link);
-
-                r = set_put(m->duids_requesting_uuid, duid);
-                if (r < 0)
-                        return log_oom();
-        }
-
-        return 0;
-}
-
 static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool force) {
         Network *network;
         int r;
@@ -3380,7 +2088,7 @@ static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool for
         if (r < 0)
                 return r;
 
-        if (link_dhcp4_server_enabled(link))
+        if (link->dhcp_server)
                 (void) sd_dhcp_server_stop(link->dhcp_server);
 
         r = link_drop_config(link);
@@ -3612,7 +2320,6 @@ static int link_load(Link *link) {
                             *routes = NULL,
                             *dhcp4_address = NULL,
                             *ipv4ll_address = NULL;
-        union in_addr_union address;
         int r;
 
         assert(link);
@@ -3651,145 +2358,21 @@ static int link_load(Link *link) {
 
 network_file_fail:
 
-        for (const char *p = addresses; p; ) {
-                _cleanup_free_ char *address_str = NULL;
-                char *prefixlen_str;
-                int family;
-                unsigned char prefixlen;
-
-                r = extract_first_word(&p, &address_str, NULL, 0);
-                if (r < 0)
-                        log_link_warning_errno(link, r, "failed to parse ADDRESSES: %m");
-                if (r <= 0)
-                        break;
-
-                prefixlen_str = strchr(address_str, '/');
-                if (!prefixlen_str) {
-                        log_link_debug(link, "Failed to parse address and prefix length %s", address_str);
-                        continue;
-                }
-                *prefixlen_str++ = '\0';
-
-                r = sscanf(prefixlen_str, "%hhu", &prefixlen);
-                if (r != 1) {
-                        log_link_error(link, "Failed to parse prefixlen %s", prefixlen_str);
-                        continue;
-                }
-
-                r = in_addr_from_string_auto(address_str, &family, &address);
-                if (r < 0) {
-                        log_link_debug_errno(link, r, "Failed to parse address %s: %m", address_str);
-                        continue;
-                }
-
-                r = address_add(link, family, &address, prefixlen, NULL);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to add address: %m");
-        }
-
-        for (const char *p = routes; p; ) {
-                _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
-                _cleanup_(route_freep) Route *tmp = NULL;
-                _cleanup_free_ char *route_str = NULL;
-                char *prefixlen_str;
-                Route *route;
-
-                r = extract_first_word(&p, &route_str, NULL, 0);
-                if (r < 0)
-                        log_link_debug_errno(link, r, "failed to parse ROUTES: %m");
-                if (r <= 0)
-                        break;
-
-                prefixlen_str = strchr(route_str, '/');
-                if (!prefixlen_str) {
-                        log_link_debug(link, "Failed to parse route %s", route_str);
-                        continue;
-                }
-                *prefixlen_str++ = '\0';
-
-                r = route_new(&tmp);
-                if (r < 0)
-                        return log_oom();
-
-                r = sscanf(prefixlen_str,
-                           "%hhu/%hhu/%"SCNu32"/%"PRIu32"/"USEC_FMT,
-                           &tmp->dst_prefixlen,
-                           &tmp->tos,
-                           &tmp->priority,
-                           &tmp->table,
-                           &tmp->lifetime);
-                if (r != 5) {
-                        log_link_debug(link,
-                                       "Failed to parse destination prefix length, tos, priority, table or expiration %s",
-                                       prefixlen_str);
-                        continue;
-                }
-
-                r = in_addr_from_string_auto(route_str, &tmp->family, &tmp->dst);
-                if (r < 0) {
-                        log_link_debug_errno(link, r, "Failed to parse route destination %s: %m", route_str);
-                        continue;
-                }
-
-                r = route_add(link, tmp, &route);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to add route: %m");
-
-                if (route->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
-                        r = sd_event_add_time(link->manager->event, &expire,
-                                              clock_boottime_or_monotonic(),
-                                              route->lifetime, 0, route_expire_handler, route);
-                        if (r < 0)
-                                log_link_warning_errno(link, r, "Could not arm route expiration handler: %m");
-                }
-
-                sd_event_source_unref(route->expire);
-                route->expire = TAKE_PTR(expire);
-        }
-
-        if (dhcp4_address) {
-                r = in_addr_from_string(AF_INET, dhcp4_address, &address);
-                if (r < 0) {
-                        log_link_debug_errno(link, r, "Failed to parse DHCPv4 address %s: %m", dhcp4_address);
-                        goto dhcp4_address_fail;
-                }
-
-                r = sd_dhcp_client_new(&link->dhcp_client, link->network ? link->network->dhcp_anonymize : 0);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to create DHCPv4 client: %m");
-
-                r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to attach DHCPv4 event: %m");
-
-                r = sd_dhcp_client_set_request_address(link->dhcp_client, &address.in);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to set initial DHCPv4 address %s: %m", dhcp4_address);
-        }
-
-dhcp4_address_fail:
-
-        if (ipv4ll_address) {
-                r = in_addr_from_string(AF_INET, ipv4ll_address, &address);
-                if (r < 0) {
-                        log_link_debug_errno(link, r, "Failed to parse IPv4LL address %s: %m", ipv4ll_address);
-                        goto ipv4ll_address_fail;
-                }
-
-                r = sd_ipv4ll_new(&link->ipv4ll);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to create IPv4LL client: %m");
+        r = link_deserialize_addresses(link, addresses);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Failed to load addresses from %s, ignoring: %m", link->state_file);
 
-                r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to attach IPv4LL event: %m");
+        r = link_deserialize_routes(link, routes);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Failed to load routes from %s, ignoring: %m", link->state_file);
 
-                r = sd_ipv4ll_set_address(link->ipv4ll, &address.in);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to set initial IPv4LL address %s: %m", ipv4ll_address);
-        }
+        r = link_deserialize_dhcp4(link, dhcp4_address);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Failed to load DHCPv4 address from %s, ignoring: %m", link->state_file);
 
-ipv4ll_address_fail:
+        r = link_deserialize_ipv4ll(link, ipv4ll_address);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Failed to load IPv4LL address from %s, ignoring: %m", link->state_file);
 
         return 0;
 }
@@ -3907,7 +2490,7 @@ static int link_carrier_gained(Link *link) {
                 }
 
                 link_set_state(link, LINK_STATE_CONFIGURING);
-                r = link_request_set_addresses(link);
+                r = link_set_static_configs(link);
                 if (r < 0)
                         return r;
         }
@@ -3958,7 +2541,7 @@ static int link_carrier_lost(Link *link) {
                 return r;
         }
 
-        if (link_dhcp4_server_enabled(link))
+        if (link->dhcp_server)
                 (void) sd_dhcp_server_stop(link->dhcp_server);
 
         r = link_drop_config(link);
@@ -4093,101 +2676,31 @@ int link_update(Link *link, sd_netlink_message *m) {
                                mac.ether_addr_octet[4],
                                mac.ether_addr_octet[5]);
 
-                if (link->ipv4ll) {
-                        bool restart = sd_ipv4ll_is_running(link->ipv4ll) > 0;
-
-                        if (restart) {
-                                r = sd_ipv4ll_stop(link->ipv4ll);
-                                if (r < 0)
-                                        return log_link_warning_errno(link, r, "Could not stop IPv4LL client: %m");
-                        }
-
-                        r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
-                        if (r < 0)
-                                return log_link_warning_errno(link, r, "Could not update MAC address in IPv4LL client: %m");
-
-                        if (restart) {
-                                r = sd_ipv4ll_start(link->ipv4ll);
-                                if (r < 0)
-                                        return log_link_warning_errno(link, r, "Could not restart IPv4LL client: %m");
-                        }
-                }
-
-                if (link->dhcp_client) {
-                        r = sd_dhcp_client_set_mac(link->dhcp_client,
-                                                   (const uint8_t *) &link->mac,
-                                                   sizeof (link->mac),
-                                                   ARPHRD_ETHER);
-                        if (r < 0)
-                                return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m");
-
-                        r = dhcp4_set_client_identifier(link);
-                        if (r < 0)
-                                return log_link_warning_errno(link, r, "Could not set DHCP client identifier: %m");
-                }
-
-                if (link->dhcp6_client) {
-                        const DUID* duid = link_get_duid(link);
-                        bool restart = sd_dhcp6_client_is_running(link->dhcp6_client) > 0;
-
-                        if (restart) {
-                                r = sd_dhcp6_client_stop(link->dhcp6_client);
-                                if (r < 0)
-                                        return log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m");
-                        }
-
-                        r = sd_dhcp6_client_set_mac(link->dhcp6_client,
-                                                    (const uint8_t *) &link->mac,
-                                                    sizeof (link->mac),
-                                                    ARPHRD_ETHER);
-                        if (r < 0)
-                                return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m");
-
-                        if (link->network->iaid_set) {
-                                r = sd_dhcp6_client_set_iaid(link->dhcp6_client, link->network->iaid);
-                                if (r < 0)
-                                        return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m");
-                        }
-
-                        r = sd_dhcp6_client_set_duid(link->dhcp6_client,
-                                                     duid->type,
-                                                     duid->raw_data_len > 0 ? duid->raw_data : NULL,
-                                                     duid->raw_data_len);
-                        if (r < 0)
-                                return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m");
-
-                        if (restart) {
-                                r = sd_dhcp6_client_start(link->dhcp6_client);
-                                if (r < 0)
-                                        return log_link_warning_errno(link, r, "Could not restart DHCPv6 client: %m");
-                        }
-                }
-
-                if (link->radv) {
-                        bool restart = sd_radv_is_running(link->radv);
+                r = ipv4ll_update_mac(link);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Could not update MAC address in IPv4LL client: %m");
 
-                        if (restart) {
-                                r = sd_radv_stop(link->radv);
-                                if (r < 0)
-                                        return log_link_warning_errno(link, r, "Could not stop Router Advertisement: %m");
-                        }
+                r = dhcp4_update_mac(link);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m");
 
-                        r = sd_radv_set_mac(link->radv, &link->mac);
-                        if (r < 0)
-                                return log_link_warning_errno(link, r, "Could not update MAC for Router Advertisement: %m");
+                r = dhcp6_update_mac(link);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m");
 
-                        if (restart) {
-                                r = sd_radv_start(link->radv);
-                                if (r < 0)
-                                        return log_link_warning_errno(link, r, "Could not restart Router Advertisement: %m");
-                        }
-                }
+                r = dhcp6_update_mac(link);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Could not update MAC address for Router Advertisement: %m");
 
                 if (link->ndisc) {
                         r = sd_ndisc_set_mac(link->ndisc, &link->mac);
                         if (r < 0)
                                 return log_link_warning_errno(link, r, "Could not update MAC for NDisc: %m");
                 }
+
+                r = ipv4_dad_update_mac(link);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Could not update MAC address in IPv4 ACD client: %m");
         }
 
         old_master = link->master_ifindex;
@@ -4327,8 +2840,6 @@ int link_save(Link *link) {
         const char *admin_state, *oper_state, *carrier_state, *address_state;
         _cleanup_free_ char *temp_path = NULL;
         _cleanup_fclose_ FILE *f = NULL;
-        Route *route;
-        Address *a;
         int r;
 
         assert(link);
@@ -4546,38 +3057,15 @@ int link_save(Link *link) {
 
                 /************************************************************/
 
-                fputs("ADDRESSES=", f);
-                space = false;
-                SET_FOREACH(a, link->addresses) {
-                        _cleanup_free_ char *address_str = NULL;
-
-                        r = in_addr_to_string(a->family, &a->in_addr, &address_str);
-                        if (r < 0)
-                                goto fail;
-
-                        fprintf(f, "%s%s/%u", space ? " " : "", address_str, a->prefixlen);
-                        space = true;
-                }
-                fputc('\n', f);
+                r = link_serialize_addresses(link, f);
+                if (r < 0)
+                        goto fail;
 
                 /************************************************************/
 
-                fputs("ROUTES=", f);
-                space = false;
-                SET_FOREACH(route, link->routes) {
-                        _cleanup_free_ char *route_str = NULL;
-
-                        r = in_addr_to_string(route->family, &route->dst, &route_str);
-                        if (r < 0)
-                                goto fail;
-
-                        fprintf(f, "%s%s/%hhu/%hhu/%"PRIu32"/%"PRIu32"/"USEC_FMT,
-                                space ? " " : "", route_str,
-                                route->dst_prefixlen, route->tos, route->priority, route->table, route->lifetime);
-                        space = true;
-                }
-
-                fputc('\n', f);
+                r = link_serialize_routes(link, f);
+                if (r < 0)
+                        goto fail;
         }
 
         print_link_hashmap(f, "CARRIER_BOUND_TO=", link->bound_to_links);
@@ -4594,29 +3082,13 @@ int link_save(Link *link) {
         } else
                 (void) unlink(link->lease_file);
 
-        if (link->ipv4ll) {
-                struct in_addr address;
-
-                r = sd_ipv4ll_get_address(link->ipv4ll, &address);
-                if (r >= 0) {
-                        fputs("IPV4LL_ADDRESS=", f);
-                        serialize_in_addrs(f, &address, 1, false, NULL);
-                        fputc('\n', f);
-                }
-        }
-
-        if (link->dhcp6_client) {
-                _cleanup_free_ char *duid = NULL;
-                uint32_t iaid;
-
-                r = sd_dhcp6_client_get_iaid(link->dhcp6_client, &iaid);
-                if (r >= 0)
-                        fprintf(f, "DHCP6_CLIENT_IAID=0x%x\n", iaid);
+        r = link_serialize_ipv4ll(link, f);
+        if (r < 0)
+                goto fail;
 
-                r = sd_dhcp6_client_duid_as_string(link->dhcp6_client, &duid);
-                if (r >= 0)
-                        fprintf(f, "DHCP6_CLIENT_DUID=%s\n", duid);
-        }
+        r = link_serialize_dhcp6_client(link, f);
+        if (r < 0)
+                goto fail;
 
         r = fflush_and_check(f);
         if (r < 0)
index 1550db8a23926edfd0f485300d063099812cd4de..08cfc60c1f42026e1de3d15d1072c3a9d143b04a 100644 (file)
@@ -15,7 +15,6 @@
 #include "sd-radv.h"
 #include "sd-netlink.h"
 
-#include "list.h"
 #include "log-link.h"
 #include "network-util.h"
 #include "networkd-util.h"
@@ -89,6 +88,7 @@ typedef struct Link {
 
         Set *addresses;
         Set *addresses_foreign;
+        Set *pool_addresses;
         Set *static_addresses;
         Set *neighbors;
         Set *neighbors_foreign;
@@ -127,8 +127,6 @@ typedef struct Link {
         bool ipv6_mtu_set:1;
         bool bridge_mdb_configured:1;
 
-        LIST_HEAD(Address, pool_addresses);
-
         sd_dhcp_server *dhcp_server;
 
         sd_ndisc *ndisc;
@@ -193,9 +191,6 @@ typedef struct Link {
 
 typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*, Link*);
 
-DUID *link_get_duid(Link *link);
-int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
-
 void link_ntp_settings_clear(Link *link);
 void link_dns_settings_clear(Link *link);
 Link *link_unref(Link *link);
@@ -226,6 +221,8 @@ int link_save_and_clean(Link *link);
 int link_carrier_reset(Link *link);
 bool link_has_carrier(Link *link);
 
+bool link_ipv6_enabled(Link *link);
+bool link_ipv6ll_enabled(Link *link);
 int link_ipv6ll_gained(Link *link, const struct in6_addr *address);
 
 int link_set_mtu(Link *link, uint32_t mtu);
@@ -237,11 +234,7 @@ int link_stop_clients(Link *link, bool may_keep_dhcp);
 const char* link_state_to_string(LinkState s) _const_;
 LinkState link_state_from_string(const char *s) _pure_;
 
-uint32_t link_get_vrf_table(Link *link);
-uint32_t link_get_dhcp_route_table(Link *link);
-uint32_t link_get_ipv6_accept_ra_route_table(Link *link);
-int link_request_set_routes(Link *link);
-
+int link_configure(Link *link);
 int link_reconfigure(Link *link, bool force);
 
 int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, int err, const char *msg);
index fe8877797719516e7cef4ee895f6ae94b7adb15f..65a8a314d602adf6412fdf74bc7b5d9492a8f52e 100644 (file)
@@ -9,6 +9,7 @@
 #include "networkd-link.h"
 #include "networkd-lldp-rx.h"
 #include "networkd-lldp-tx.h"
+#include "networkd-manager.h"
 #include "networkd-network.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -25,7 +26,7 @@ static const char* const lldp_mode_table[_LLDP_MODE_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES);
 
-bool link_lldp_rx_enabled(Link *link) {
+static bool link_lldp_rx_enabled(Link *link) {
         assert(link);
 
         if (link->flags & IFF_LOOPBACK)
@@ -68,9 +69,18 @@ static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n
 int link_lldp_rx_configure(Link *link) {
         int r;
 
-        r = sd_lldp_new(&link->lldp);
-        if (r < 0)
-                return r;
+        if (!link_lldp_rx_enabled(link))
+                return 0;
+
+        if (!link->lldp) {
+                r = sd_lldp_new(&link->lldp);
+                if (r < 0)
+                        return r;
+
+                r = sd_lldp_attach_event(link->lldp, link->manager->event, 0);
+                if (r < 0)
+                        return r;
+        }
 
         r = sd_lldp_set_ifindex(link->lldp, link->ifindex);
         if (r < 0)
@@ -87,10 +97,6 @@ int link_lldp_rx_configure(Link *link) {
         if (r < 0)
                 return r;
 
-        r = sd_lldp_attach_event(link->lldp, NULL, 0);
-        if (r < 0)
-                return r;
-
         r = sd_lldp_set_callback(link->lldp, lldp_handler, link);
         if (r < 0)
                 return r;
index 12f512f62870287d9ff767687102b87a78db3f53..8e1a6a0b62101bff6f4632e8a999f4bdaf6bc685 100644 (file)
@@ -1,8 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
-#include <stdbool.h>
-
 #include "conf-parser.h"
 
 typedef struct Link Link;
@@ -15,7 +13,6 @@ typedef enum LLDPMode {
         _LLDP_MODE_INVALID = -1,
 } LLDPMode;
 
-bool link_lldp_rx_enabled(Link *link);
 int link_lldp_rx_configure(Link *link);
 int link_update_lldp(Link *link);
 int link_lldp_save(Link *link);
index 2be7c27e1821182173ed3c8ef3fe2bbe9fa014fc..c8e56a5fece64efcf06a73531711a8f6978c67c3 100644 (file)
@@ -367,7 +367,7 @@ int link_lldp_emit_start(Link *link) {
 
         assert(link);
 
-        if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) {
+        if (!link_lldp_emit_enabled(link)) {
                 link_lldp_emit_stop(link);
                 return 0;
         }
index dbbc6b64bcc753d2f14dc81270e82b9b99d4c960..e8d270987d9667af896526d1e54b9abf1d983181 100644 (file)
 #include "local-addresses.h"
 #include "netlink-util.h"
 #include "network-internal.h"
+#include "networkd-address-pool.h"
 #include "networkd-dhcp-server-bus.h"
 #include "networkd-dhcp6.h"
 #include "networkd-link-bus.h"
 #include "networkd-manager-bus.h"
 #include "networkd-manager.h"
+#include "networkd-neighbor.h"
 #include "networkd-network-bus.h"
+#include "networkd-nexthop.h"
+#include "networkd-routing-policy-rule.h"
 #include "networkd-speed-meter.h"
 #include "ordered-set.h"
 #include "path-lookup.h"
 /* use 128 MB for receive socket kernel queue. */
 #define RCVBUF_SIZE    (128*1024*1024)
 
-static int log_message_warning_errno(sd_netlink_message *m, int err, const char *msg) {
-        const char *err_msg = NULL;
-
-        (void) sd_netlink_message_read_string(m, NLMSGERR_ATTR_MSG, &err_msg);
-        return log_warning_errno(err, "%s: %s%s%m", msg, strempty(err_msg), err_msg ? " " : "");
-}
-
-static int setup_default_address_pool(Manager *m) {
-        AddressPool *p;
-        int r;
-
-        assert(m);
-
-        /* Add in the well-known private address ranges. */
-
-        r = address_pool_new_from_string(m, &p, AF_INET6, "fd00::", 8);
-        if (r < 0)
-                return r;
-
-        r = address_pool_new_from_string(m, &p, AF_INET, "10.0.0.0", 8);
-        if (r < 0)
-                return r;
-
-        r = address_pool_new_from_string(m, &p, AF_INET, "172.16.0.0", 12);
-        if (r < 0)
-                return r;
-
-        r = address_pool_new_from_string(m, &p, AF_INET, "192.168.0.0", 16);
-        if (r < 0)
-                return r;
-
-        return 0;
-}
-
 static int manager_reset_all(Manager *m) {
         Link *link;
         int r;
@@ -261,961 +231,40 @@ static int manager_udev_process_link(sd_device_monitor *monitor, sd_device *devi
 static int manager_connect_udev(Manager *m) {
         int r;
 
-        /* udev does not initialize devices inside containers, so we rely on them being already
-         * initialized before entering the container. */
-        if (path_is_read_only_fs("/sys") > 0)
-                return 0;
-
-        r = sd_device_monitor_new(&m->device_monitor);
-        if (r < 0)
-                return log_error_errno(r, "Failed to initialize device monitor: %m");
-
-        r = sd_device_monitor_set_receive_buffer_size(m->device_monitor, RCVBUF_SIZE);
-        if (r < 0)
-                log_warning_errno(r, "Failed to increase buffer size for device monitor, ignoring: %m");
-
-        r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "net", NULL);
-        if (r < 0)
-                return log_error_errno(r, "Could not add device monitor filter: %m");
-
-        r = sd_device_monitor_attach_event(m->device_monitor, m->event);
-        if (r < 0)
-                return log_error_errno(r, "Failed to attach event to device monitor: %m");
-
-        r = sd_device_monitor_start(m->device_monitor, manager_udev_process_link, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to start device monitor: %m");
-
-        return 0;
-}
-
-int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
-        _cleanup_(route_freep) Route *tmp = NULL;
-        Route *route = NULL;
-        Manager *m = userdata;
-        Link *link = NULL;
-        uint32_t ifindex;
-        uint16_t type;
-        unsigned char table;
-        int r;
-
-        assert(rtnl);
-        assert(message);
-        assert(m);
-
-        if (sd_netlink_message_is_error(message)) {
-                r = sd_netlink_message_get_errno(message);
-                if (r < 0)
-                        log_message_warning_errno(message, r, "rtnl: failed to receive route message, ignoring");
-
-                return 0;
-        }
-
-        r = sd_netlink_message_get_type(message, &type);
-        if (r < 0) {
-                log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
-                return 0;
-        } else if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) {
-                log_warning("rtnl: received unexpected message type %u when processing route, ignoring.", type);
-                return 0;
-        }
-
-        r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
-        if (r == -ENODATA) {
-                log_debug("rtnl: received route message without ifindex, ignoring");
-                return 0;
-        } else if (r < 0) {
-                log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m");
-                return 0;
-        } else if (ifindex <= 0) {
-                log_warning("rtnl: received route message with invalid ifindex %d, ignoring.", ifindex);
-                return 0;
-        }
-
-        r = link_get(m, ifindex, &link);
-        if (r < 0 || !link) {
-                /* when enumerating we might be out of sync, but we will
-                 * get the route again, so just ignore it */
-                if (!m->enumerating)
-                        log_warning("rtnl: received route message for link (%d) we do not know about, ignoring", ifindex);
-                return 0;
-        }
-
-        r = route_new(&tmp);
-        if (r < 0)
-                return log_oom();
-
-        r = sd_rtnl_message_route_get_family(message, &tmp->family);
-        if (r < 0) {
-                log_link_warning(link, "rtnl: received route message without family, ignoring");
-                return 0;
-        } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
-                log_link_debug(link, "rtnl: received route message with invalid family '%i', ignoring", tmp->family);
-                return 0;
-        }
-
-        r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol);
-        if (r < 0) {
-                log_warning_errno(r, "rtnl: received route message without route protocol: %m");
-                return 0;
-        }
-
-        switch (tmp->family) {
-        case AF_INET:
-                r = sd_netlink_message_read_in_addr(message, RTA_DST, &tmp->dst.in);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m");
-                        return 0;
-                }
-
-                r = sd_netlink_message_read_in_addr(message, RTA_GATEWAY, &tmp->gw.in);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
-                        return 0;
-                }
-
-                r = sd_netlink_message_read_in_addr(message, RTA_SRC, &tmp->src.in);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m");
-                        return 0;
-                }
-
-                r = sd_netlink_message_read_in_addr(message, RTA_PREFSRC, &tmp->prefsrc.in);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m");
-                        return 0;
-                }
-
-                break;
-
-        case AF_INET6:
-                r = sd_netlink_message_read_in6_addr(message, RTA_DST, &tmp->dst.in6);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m");
-                        return 0;
-                }
-
-                r = sd_netlink_message_read_in6_addr(message, RTA_GATEWAY, &tmp->gw.in6);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
-                        return 0;
-                }
-
-                r = sd_netlink_message_read_in6_addr(message, RTA_SRC, &tmp->src.in6);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m");
-                        return 0;
-                }
-
-                r = sd_netlink_message_read_in6_addr(message, RTA_PREFSRC, &tmp->prefsrc.in6);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m");
-                        return 0;
-                }
-
-                break;
-
-        default:
-                assert_not_reached("Received route message with unsupported address family");
-                return 0;
-        }
-
-        r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid source prefixlen, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_rtnl_message_route_get_scope(message, &tmp->scope);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid scope, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_rtnl_message_route_get_tos(message, &tmp->tos);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid tos, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_rtnl_message_route_get_type(message, &tmp->type);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid type, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_rtnl_message_route_get_table(message, &table);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid table, ignoring: %m");
-                return 0;
-        }
-        tmp->table = table;
-
-        r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority);
-        if (r < 0 && r != -ENODATA) {
-                log_link_warning_errno(link, r, "rtnl: received route message with invalid priority, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_netlink_message_enter_container(message, RTA_METRICS);
-        if (r < 0 && r != -ENODATA) {
-                log_link_error_errno(link, r, "rtnl: Could not enter RTA_METRICS container: %m");
-                return 0;
-        }
-        if (r >= 0) {
-                r = sd_netlink_message_read_u32(message, RTAX_INITCWND, &tmp->initcwnd);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: received route message with invalid initcwnd, ignoring: %m");
-                        return 0;
-                }
-
-                r = sd_netlink_message_read_u32(message, RTAX_INITRWND, &tmp->initrwnd);
-                if (r < 0 && r != -ENODATA) {
-                        log_link_warning_errno(link, r, "rtnl: received route message with invalid initrwnd, ignoring: %m");
-                        return 0;
-                }
-
-                r = sd_netlink_message_exit_container(message);
-                if (r < 0) {
-                        log_link_error_errno(link, r, "rtnl: Could not exit from RTA_METRICS container: %m");
-                        return 0;
-                }
-        }
-
-        (void) route_get(link, tmp, &route);
-
-        if (DEBUG_LOGGING) {
-                _cleanup_free_ char *buf_dst = NULL, *buf_dst_prefixlen = NULL,
-                        *buf_src = NULL, *buf_gw = NULL, *buf_prefsrc = NULL;
-                char buf_scope[ROUTE_SCOPE_STR_MAX], buf_table[ROUTE_TABLE_STR_MAX],
-                        buf_protocol[ROUTE_PROTOCOL_STR_MAX];
-
-                if (!in_addr_is_null(tmp->family, &tmp->dst)) {
-                        (void) in_addr_to_string(tmp->family, &tmp->dst, &buf_dst);
-                        (void) asprintf(&buf_dst_prefixlen, "/%u", tmp->dst_prefixlen);
-                }
-                if (!in_addr_is_null(tmp->family, &tmp->src))
-                        (void) in_addr_to_string(tmp->family, &tmp->src, &buf_src);
-                if (!in_addr_is_null(tmp->family, &tmp->gw))
-                        (void) in_addr_to_string(tmp->family, &tmp->gw, &buf_gw);
-                if (!in_addr_is_null(tmp->family, &tmp->prefsrc))
-                        (void) in_addr_to_string(tmp->family, &tmp->prefsrc, &buf_prefsrc);
-
-                log_link_debug(link,
-                               "%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
-                               (!route && !link->manager->manage_foreign_routes) ? "Ignoring received foreign" :
-                               type == RTM_DELROUTE ? "Forgetting" :
-                               route ? "Received remembered" : "Remembering",
-                               strna(buf_dst), strempty(buf_dst_prefixlen),
-                               strna(buf_src), strna(buf_gw), strna(buf_prefsrc),
-                               format_route_scope(tmp->scope, buf_scope, sizeof buf_scope),
-                               format_route_table(tmp->table, buf_table, sizeof buf_table),
-                               format_route_protocol(tmp->protocol, buf_protocol, sizeof buf_protocol),
-                               strna(route_type_to_string(tmp->type)));
-        }
-
-        switch (type) {
-        case RTM_NEWROUTE:
-                if (!route && link->manager->manage_foreign_routes) {
-                        /* A route appeared that we did not request */
-                        r = route_add_foreign(link, tmp, &route);
-                        if (r < 0) {
-                                log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
-                                return 0;
-                        }
-                }
-
-                break;
-
-        case RTM_DELROUTE:
-                route_free(route);
-                break;
-
-        default:
-                assert_not_reached("Received route message with invalid RTNL message type");
-        }
-
-        return 1;
-}
-
-static int manager_rtnl_process_neighbor_lladdr(sd_netlink_message *message, union lladdr_union *lladdr, size_t *size, char **str) {
-        int r;
-
-        assert(message);
-        assert(lladdr);
-        assert(size);
-        assert(str);
-
-        *str = NULL;
-
-        r = sd_netlink_message_read(message, NDA_LLADDR, sizeof(lladdr->ip.in6), &lladdr->ip.in6);
-        if (r >= 0) {
-                *size = sizeof(lladdr->ip.in6);
-                if (in_addr_to_string(AF_INET6, &lladdr->ip, str) < 0)
-                        log_warning_errno(r, "Could not print lower address: %m");
-                return r;
-        }
-
-        r = sd_netlink_message_read(message, NDA_LLADDR, sizeof(lladdr->mac), &lladdr->mac);
-        if (r >= 0) {
-                *size = sizeof(lladdr->mac);
-                *str = new(char, ETHER_ADDR_TO_STRING_MAX);
-                if (!*str) {
-                        log_oom();
-                        return r;
-                }
-                ether_addr_to_string(&lladdr->mac, *str);
-                return r;
-        }
-
-        r = sd_netlink_message_read(message, NDA_LLADDR, sizeof(lladdr->ip.in), &lladdr->ip.in);
-        if (r >= 0) {
-                *size = sizeof(lladdr->ip.in);
-                if (in_addr_to_string(AF_INET, &lladdr->ip, str) < 0)
-                        log_warning_errno(r, "Could not print lower address: %m");
-                return r;
-        }
-
-        return r;
-}
-
-int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
-        Manager *m = userdata;
-        Link *link = NULL;
-        Neighbor *neighbor = NULL;
-        int ifindex, family, r;
-        uint16_t type, state;
-        union in_addr_union in_addr = IN_ADDR_NULL;
-        _cleanup_free_ char *addr_str = NULL;
-        union lladdr_union lladdr;
-        size_t lladdr_size = 0;
-        _cleanup_free_ char *lladdr_str = NULL;
-
-        assert(rtnl);
-        assert(message);
-        assert(m);
-
-        if (sd_netlink_message_is_error(message)) {
-                r = sd_netlink_message_get_errno(message);
-                if (r < 0)
-                        log_message_warning_errno(message, r, "rtnl: failed to receive neighbor message, ignoring");
-
-                return 0;
-        }
-
-        r = sd_netlink_message_get_type(message, &type);
-        if (r < 0) {
-                log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
-                return 0;
-        } else if (!IN_SET(type, RTM_NEWNEIGH, RTM_DELNEIGH)) {
-                log_warning("rtnl: received unexpected message type %u when processing neighbor, ignoring.", type);
-                return 0;
-        }
-
-        r = sd_rtnl_message_neigh_get_state(message, &state);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received neighbor message with invalid state, ignoring: %m");
-                return 0;
-        } else if (!FLAGS_SET(state, NUD_PERMANENT)) {
-                log_debug("rtnl: received non-static neighbor, ignoring.");
-                return 0;
-        }
-
-        r = sd_rtnl_message_neigh_get_ifindex(message, &ifindex);
-        if (r < 0) {
-                log_warning_errno(r, "rtnl: could not get ifindex from message, ignoring: %m");
-                return 0;
-        } else if (ifindex <= 0) {
-                log_warning("rtnl: received neighbor message with invalid ifindex %d, ignoring.", ifindex);
-                return 0;
-        }
-
-        r = link_get(m, ifindex, &link);
-        if (r < 0 || !link) {
-                /* when enumerating we might be out of sync, but we will get the neighbor again, so just
-                 * ignore it */
-                if (!m->enumerating)
-                        log_warning("rtnl: received neighbor for link '%d' we don't know about, ignoring.", ifindex);
-                return 0;
-        }
-
-        r = sd_rtnl_message_neigh_get_family(message, &family);
-        if (r < 0) {
-                log_link_warning(link, "rtnl: received neighbor message without family, ignoring.");
-                return 0;
-        } else if (!IN_SET(family, AF_INET, AF_INET6)) {
-                log_link_debug(link, "rtnl: received neighbor message with invalid family '%i', ignoring.", family);
-                return 0;
-        }
-
-        switch (family) {
-        case AF_INET:
-                r = sd_netlink_message_read_in_addr(message, NDA_DST, &in_addr.in);
-                if (r < 0) {
-                        log_link_warning_errno(link, r, "rtnl: received neighbor message without valid address, ignoring: %m");
-                        return 0;
-                }
-
-                break;
-
-        case AF_INET6:
-                r = sd_netlink_message_read_in6_addr(message, NDA_DST, &in_addr.in6);
-                if (r < 0) {
-                        log_link_warning_errno(link, r, "rtnl: received neighbor message without valid address, ignoring: %m");
-                        return 0;
-                }
-
-                break;
-
-        default:
-                assert_not_reached("Received unsupported address family");
-        }
-
-        if (in_addr_to_string(family, &in_addr, &addr_str) < 0)
-                log_link_warning_errno(link, r, "Could not print address: %m");
-
-        r = manager_rtnl_process_neighbor_lladdr(message, &lladdr, &lladdr_size, &lladdr_str);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received neighbor message with invalid lladdr, ignoring: %m");
-                return 0;
-        }
-
-        (void) neighbor_get(link, family, &in_addr, &lladdr, lladdr_size, &neighbor);
-
-        switch (type) {
-        case RTM_NEWNEIGH:
-                if (neighbor)
-                        log_link_debug(link, "Received remembered neighbor: %s->%s",
-                                       strnull(addr_str), strnull(lladdr_str));
-                else {
-                        /* A neighbor appeared that we did not request */
-                        r = neighbor_add_foreign(link, family, &in_addr, &lladdr, lladdr_size, &neighbor);
-                        if (r < 0) {
-                                log_link_warning_errno(link, r, "Failed to remember foreign neighbor %s->%s, ignoring: %m",
-                                                       strnull(addr_str), strnull(lladdr_str));
-                                return 0;
-                        } else
-                                log_link_debug(link, "Remembering foreign neighbor: %s->%s",
-                                               strnull(addr_str), strnull(lladdr_str));
-                }
-
-                break;
-
-        case RTM_DELNEIGH:
-                if (neighbor) {
-                        log_link_debug(link, "Forgetting neighbor: %s->%s",
-                                       strnull(addr_str), strnull(lladdr_str));
-                        (void) neighbor_free(neighbor);
-                } else
-                        log_link_debug(link, "Kernel removed a neighbor we don't remember: %s->%s, ignoring.",
-                                       strnull(addr_str), strnull(lladdr_str));
-
-                break;
-
-        default:
-                assert_not_reached("Received invalid RTNL message type");
-        }
-
-        return 1;
-}
-
-int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
-        _cleanup_free_ char *buf = NULL;
-        Manager *m = userdata;
-        Link *link = NULL;
-        uint16_t type;
-        unsigned char flags, prefixlen, scope;
-        union in_addr_union in_addr = IN_ADDR_NULL;
-        struct ifa_cacheinfo cinfo;
-        Address *address = NULL;
-        char valid_buf[FORMAT_TIMESPAN_MAX];
-        const char *valid_str = NULL;
-        int ifindex, family, r;
-
-        assert(rtnl);
-        assert(message);
-        assert(m);
-
-        if (sd_netlink_message_is_error(message)) {
-                r = sd_netlink_message_get_errno(message);
-                if (r < 0)
-                        log_message_warning_errno(message, r, "rtnl: failed to receive address message, ignoring");
-
-                return 0;
-        }
-
-        r = sd_netlink_message_get_type(message, &type);
-        if (r < 0) {
-                log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
-                return 0;
-        } else if (!IN_SET(type, RTM_NEWADDR, RTM_DELADDR)) {
-                log_warning("rtnl: received unexpected message type %u when processing address, ignoring.", type);
-                return 0;
-        }
-
-        r = sd_rtnl_message_addr_get_ifindex(message, &ifindex);
-        if (r < 0) {
-                log_warning_errno(r, "rtnl: could not get ifindex from message, ignoring: %m");
-                return 0;
-        } else if (ifindex <= 0) {
-                log_warning("rtnl: received address message with invalid ifindex %d, ignoring.", ifindex);
-                return 0;
-        }
-
-        r = link_get(m, ifindex, &link);
-        if (r < 0 || !link) {
-                /* when enumerating we might be out of sync, but we will get the address again, so just
-                 * ignore it */
-                if (!m->enumerating)
-                        log_warning("rtnl: received address for link '%d' we don't know about, ignoring.", ifindex);
-                return 0;
-        }
-
-        r = sd_rtnl_message_addr_get_family(message, &family);
-        if (r < 0) {
-                log_link_warning(link, "rtnl: received address message without family, ignoring.");
-                return 0;
-        } else if (!IN_SET(family, AF_INET, AF_INET6)) {
-                log_link_debug(link, "rtnl: received address message with invalid family '%i', ignoring.", family);
-                return 0;
-        }
-
-        r = sd_rtnl_message_addr_get_prefixlen(message, &prefixlen);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received address message with invalid prefixlen, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_rtnl_message_addr_get_scope(message, &scope);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received address message with invalid scope, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_rtnl_message_addr_get_flags(message, &flags);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received address message with invalid flags, ignoring: %m");
-                return 0;
-        }
-
-        switch (family) {
-        case AF_INET:
-                r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &in_addr.in);
-                if (r < 0) {
-                        log_link_warning_errno(link, r, "rtnl: received address message without valid address, ignoring: %m");
-                        return 0;
-                }
-
-                break;
-
-        case AF_INET6:
-                r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &in_addr.in6);
-                if (r < 0) {
-                        log_link_warning_errno(link, r, "rtnl: received address message without valid address, ignoring: %m");
-                        return 0;
-                }
-
-                break;
-
-        default:
-                assert_not_reached("Received unsupported address family");
-        }
-
-        r = in_addr_to_string(family, &in_addr, &buf);
-        if (r < 0)
-                log_link_warning_errno(link, r, "Could not print address: %m");
-
-        r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo);
-        if (r < 0 && r != -ENODATA) {
-                log_link_warning_errno(link, r, "rtnl: cannot get IFA_CACHEINFO attribute, ignoring: %m");
-                return 0;
-        } else if (r >= 0 && cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME)
-                valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
-                                            cinfo.ifa_valid * USEC_PER_SEC,
-                                            USEC_PER_SEC);
-
-        (void) address_get(link, family, &in_addr, prefixlen, &address);
-
-        switch (type) {
-        case RTM_NEWADDR:
-                if (address)
-                        log_link_debug(link, "Remembering updated address: %s/%u (valid %s%s)",
-                                       strnull(buf), prefixlen,
-                                       valid_str ? "for " : "forever", strempty(valid_str));
-                else {
-                        /* An address appeared that we did not request */
-                        r = address_add_foreign(link, family, &in_addr, prefixlen, &address);
-                        if (r < 0) {
-                                log_link_warning_errno(link, r, "Failed to remember foreign address %s/%u, ignoring: %m",
-                                                       strnull(buf), prefixlen);
-                                return 0;
-                        } else
-                                log_link_debug(link, "Remembering foreign address: %s/%u (valid %s%s)",
-                                               strnull(buf), prefixlen,
-                                               valid_str ? "for " : "forever", strempty(valid_str));
-                }
-
-                /* address_update() logs internally, so we don't need to here. */
-                r = address_update(address, flags, scope, &cinfo);
-                if (r < 0)
-                        link_enter_failed(link);
-
-                break;
-
-        case RTM_DELADDR:
-                if (address) {
-                        log_link_debug(link, "Forgetting address: %s/%u (valid %s%s)",
-                                       strnull(buf), prefixlen,
-                                       valid_str ? "for " : "forever", strempty(valid_str));
-                        (void) address_drop(address);
-                } else
-                        log_link_debug(link, "Kernel removed an address we don't remember: %s/%u (valid %s%s), ignoring.",
-                                       strnull(buf), prefixlen,
-                                       valid_str ? "for " : "forever", strempty(valid_str));
-
-                break;
-
-        default:
-                assert_not_reached("Received invalid RTNL message type");
-        }
-
-        return 1;
-}
-
-static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
-        Manager *m = userdata;
-        Link *link = NULL;
-        NetDev *netdev = NULL;
-        uint16_t type;
-        const char *name;
-        int r, ifindex;
-
-        assert(rtnl);
-        assert(message);
-        assert(m);
-
-        if (sd_netlink_message_is_error(message)) {
-                r = sd_netlink_message_get_errno(message);
-                if (r < 0)
-                        log_message_warning_errno(message, r, "rtnl: Could not receive link message, ignoring");
-
-                return 0;
-        }
-
-        r = sd_netlink_message_get_type(message, &type);
-        if (r < 0) {
-                log_warning_errno(r, "rtnl: Could not get message type, ignoring: %m");
-                return 0;
-        } else if (!IN_SET(type, RTM_NEWLINK, RTM_DELLINK)) {
-                log_warning("rtnl: Received unexpected message type %u when processing link, ignoring.", type);
-                return 0;
-        }
-
-        r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
-        if (r < 0) {
-                log_warning_errno(r, "rtnl: Could not get ifindex from link message, ignoring: %m");
-                return 0;
-        } else if (ifindex <= 0) {
-                log_warning("rtnl: received link message with invalid ifindex %d, ignoring.", ifindex);
-                return 0;
-        }
-
-        r = sd_netlink_message_read_string(message, IFLA_IFNAME, &name);
-        if (r < 0) {
-                log_warning_errno(r, "rtnl: Received link message without ifname, ignoring: %m");
-                return 0;
-        }
-
-        (void) link_get(m, ifindex, &link);
-        (void) netdev_get(m, name, &netdev);
-
-        switch (type) {
-        case RTM_NEWLINK:
-                if (!link) {
-                        /* link is new, so add it */
-                        r = link_add(m, message, &link);
-                        if (r < 0) {
-                                log_warning_errno(r, "Could not process new link message, ignoring: %m");
-                                return 0;
-                        }
-                }
-
-                if (netdev) {
-                        /* netdev exists, so make sure the ifindex matches */
-                        r = netdev_set_ifindex(netdev, message);
-                        if (r < 0) {
-                                log_warning_errno(r, "Could not process new link message for netdev, ignoring: %m");
-                                return 0;
-                        }
-                }
-
-                r = link_update(link, message);
-                if (r < 0) {
-                        log_warning_errno(r, "Could not process link message, ignoring: %m");
-                        return 0;
-                }
-
-                break;
-
-        case RTM_DELLINK:
-                link_drop(link);
-                netdev_drop(netdev);
-
-                break;
-
-        default:
-                assert_not_reached("Received link message with invalid RTNL message type.");
-        }
-
-        return 1;
-}
-
-int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
-        _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL;
-        _cleanup_free_ char *from = NULL, *to = NULL;
-        RoutingPolicyRule *rule = NULL;
-        const char *iif = NULL, *oif = NULL;
-        uint32_t suppress_prefixlen;
-        Manager *m = userdata;
-        unsigned flags;
-        uint16_t type;
-        int r;
-
-        assert(rtnl);
-        assert(message);
-        assert(m);
-
-        if (sd_netlink_message_is_error(message)) {
-                r = sd_netlink_message_get_errno(message);
-                if (r < 0)
-                        log_message_warning_errno(message, r, "rtnl: failed to receive rule message, ignoring");
-
-                return 0;
-        }
-
-        r = sd_netlink_message_get_type(message, &type);
-        if (r < 0) {
-                log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
-                return 0;
-        } else if (!IN_SET(type, RTM_NEWRULE, RTM_DELRULE)) {
-                log_warning("rtnl: received unexpected message type %u when processing rule, ignoring.", type);
-                return 0;
-        }
-
-        r = routing_policy_rule_new(&tmp);
-        if (r < 0) {
-                log_oom();
-                return 0;
-        }
-
-        r = sd_rtnl_message_get_family(message, &tmp->family);
-        if (r < 0) {
-                log_warning_errno(r, "rtnl: could not get rule family, ignoring: %m");
-                return 0;
-        } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
-                log_debug("rtnl: received rule message with invalid family %d, ignoring.", tmp->family);
-                return 0;
-        }
-
-        switch (tmp->family) {
-        case AF_INET:
-                r = sd_netlink_message_read_in_addr(message, FRA_SRC, &tmp->from.in);
-                if (r < 0 && r != -ENODATA) {
-                        log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m");
-                        return 0;
-                } else if (r >= 0) {
-                        r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &tmp->from_prefixlen);
-                        if (r < 0) {
-                                log_warning_errno(r, "rtnl: received rule message without valid source prefix length, ignoring: %m");
-                                return 0;
-                        }
-                }
-
-                r = sd_netlink_message_read_in_addr(message, FRA_DST, &tmp->to.in);
-                if (r < 0 && r != -ENODATA) {
-                        log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m");
-                        return 0;
-                } else if (r >= 0) {
-                        r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &tmp->to_prefixlen);
-                        if (r < 0) {
-                                log_warning_errno(r, "rtnl: received rule message without valid destination prefix length, ignoring: %m");
-                                return 0;
-                        }
-                }
-
-                break;
-
-        case AF_INET6:
-                r = sd_netlink_message_read_in6_addr(message, FRA_SRC, &tmp->from.in6);
-                if (r < 0 && r != -ENODATA) {
-                        log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m");
-                        return 0;
-                } else if (r >= 0) {
-                        r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &tmp->from_prefixlen);
-                        if (r < 0) {
-                                log_warning_errno(r, "rtnl: received rule message without valid source prefix length, ignoring: %m");
-                                return 0;
-                        }
-                }
-
-                r = sd_netlink_message_read_in6_addr(message, FRA_DST, &tmp->to.in6);
-                if (r < 0 && r != -ENODATA) {
-                        log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m");
-                        return 0;
-                } else if (r >= 0) {
-                        r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &tmp->to_prefixlen);
-                        if (r < 0) {
-                                log_warning_errno(r, "rtnl: received rule message without valid destination prefix length, ignoring: %m");
-                                return 0;
-                        }
-                }
-
-                break;
-
-        default:
-                assert_not_reached("Received rule message with unsupported address family");
-        }
-
-        r = sd_rtnl_message_routing_policy_rule_get_flags(message, &flags);
-        if (r < 0) {
-                log_warning_errno(r, "rtnl: received rule message without valid flag, ignoring: %m");
-                return 0;
-        }
-        tmp->invert_rule = flags & FIB_RULE_INVERT;
-
-        r = sd_netlink_message_read_u32(message, FRA_FWMARK, &tmp->fwmark);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get FRA_FWMARK attribute, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_netlink_message_read_u32(message, FRA_FWMASK, &tmp->fwmask);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get FRA_FWMASK attribute, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_netlink_message_read_u32(message, FRA_PRIORITY, &tmp->priority);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get FRA_PRIORITY attribute, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_netlink_message_read_u32(message, FRA_TABLE, &tmp->table);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get FRA_TABLE attribute, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_rtnl_message_routing_policy_rule_get_tos(message, &tmp->tos);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get ip rule TOS, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_netlink_message_read_string(message, FRA_IIFNAME, &iif);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get FRA_IIFNAME attribute, ignoring: %m");
-                return 0;
-        }
-        r = free_and_strdup(&tmp->iif, iif);
-        if (r < 0)
-                return log_oom();
-
-        r = sd_netlink_message_read_string(message, FRA_OIFNAME, &oif);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get FRA_OIFNAME attribute, ignoring: %m");
-                return 0;
-        }
-        r = free_and_strdup(&tmp->oif, oif);
-        if (r < 0)
-                return log_oom();
-
-        r = sd_netlink_message_read_u8(message, FRA_IP_PROTO, &tmp->protocol);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get FRA_IP_PROTO attribute, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_netlink_message_read(message, FRA_SPORT_RANGE, sizeof(tmp->sport), &tmp->sport);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get FRA_SPORT_RANGE attribute, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_netlink_message_read(message, FRA_DPORT_RANGE, sizeof(tmp->dport), &tmp->dport);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get FRA_DPORT_RANGE attribute, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_netlink_message_read(message, FRA_UID_RANGE, sizeof(tmp->uid_range), &tmp->uid_range);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get FRA_UID_RANGE attribute, ignoring: %m");
+        /* udev does not initialize devices inside containers, so we rely on them being already
+         * initialized before entering the container. */
+        if (path_is_read_only_fs("/sys") > 0)
                 return 0;
-        }
 
-        r = sd_netlink_message_read_u32(message, FRA_SUPPRESS_PREFIXLEN, &suppress_prefixlen);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get FRA_SUPPRESS_PREFIXLEN attribute, ignoring: %m");
-                return 0;
-        }
-        if (r >= 0)
-                tmp->suppress_prefixlen = (int) suppress_prefixlen;
+        r = sd_device_monitor_new(&m->device_monitor);
+        if (r < 0)
+                return log_error_errno(r, "Failed to initialize device monitor: %m");
 
-        (void) routing_policy_rule_get(m, tmp, &rule);
+        r = sd_device_monitor_set_receive_buffer_size(m->device_monitor, RCVBUF_SIZE);
+        if (r < 0)
+                log_warning_errno(r, "Failed to increase buffer size for device monitor, ignoring: %m");
 
-        if (DEBUG_LOGGING) {
-                (void) in_addr_to_string(tmp->family, &tmp->from, &from);
-                (void) in_addr_to_string(tmp->family, &tmp->to, &to);
-        }
+        r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "net", NULL);
+        if (r < 0)
+                return log_error_errno(r, "Could not add device monitor filter: %m");
 
-        switch (type) {
-        case RTM_NEWRULE:
-                if (rule)
-                        log_debug("Received remembered routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32,
-                                  tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table);
-                else {
-                        log_debug("Remembering foreign routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32,
-                                  tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table);
-                        r = routing_policy_rule_add_foreign(m, tmp, &rule);
-                        if (r < 0) {
-                                log_warning_errno(r, "Could not remember foreign rule, ignoring: %m");
-                                return 0;
-                        }
-                }
-                break;
-        case RTM_DELRULE:
-                if (rule) {
-                        log_debug("Forgetting routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32,
-                                  tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table);
-                        routing_policy_rule_free(rule);
-                } else
-                        log_debug("Kernel removed a routing policy rule we don't remember: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32", ignoring.",
-                                  tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table);
-                break;
+        r = sd_device_monitor_attach_event(m->device_monitor, m->event);
+        if (r < 0)
+                return log_error_errno(r, "Failed to attach event to device monitor: %m");
 
-        default:
-                assert_not_reached("Received invalid RTNL message type");
-        }
+        r = sd_device_monitor_start(m->device_monitor, manager_udev_process_link, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to start device monitor: %m");
 
-        return 1;
+        return 0;
 }
 
-int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
-        _cleanup_(nexthop_freep) NextHop *tmp = NULL;
-        _cleanup_free_ char *gateway = NULL;
-        NextHop *nexthop = NULL;
-        Manager *m = userdata;
+static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
         Link *link = NULL;
+        NetDev *netdev = NULL;
         uint16_t type;
-        int r;
+        const char *name;
+        int r, ifindex;
 
         assert(rtnl);
         assert(message);
@@ -1224,105 +273,74 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
         if (sd_netlink_message_is_error(message)) {
                 r = sd_netlink_message_get_errno(message);
                 if (r < 0)
-                        log_message_warning_errno(message, r, "rtnl: failed to receive rule message, ignoring");
+                        log_message_warning_errno(message, r, "rtnl: Could not receive link message, ignoring");
 
                 return 0;
         }
 
         r = sd_netlink_message_get_type(message, &type);
         if (r < 0) {
-                log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
+                log_warning_errno(r, "rtnl: Could not get message type, ignoring: %m");
                 return 0;
-        } else if (!IN_SET(type, RTM_NEWNEXTHOP, RTM_DELNEXTHOP)) {
-                log_warning("rtnl: received unexpected message type %u when processing nexthop, ignoring.", type);
+        } else if (!IN_SET(type, RTM_NEWLINK, RTM_DELLINK)) {
+                log_warning("rtnl: Received unexpected message type %u when processing link, ignoring.", type);
                 return 0;
         }
 
-        r = nexthop_new(&tmp);
-        if (r < 0)
-                return log_oom();
-
-        r = sd_rtnl_message_get_family(message, &tmp->family);
+        r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
         if (r < 0) {
-                log_warning_errno(r, "rtnl: could not get nexthop family, ignoring: %m");
-                return 0;
-        } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
-                log_debug("rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family);
-                return 0;
-        }
-
-        switch (tmp->family) {
-        case AF_INET:
-                r = sd_netlink_message_read_in_addr(message, NHA_GATEWAY, &tmp->gw.in);
-                if (r < 0 && r != -ENODATA) {
-                        log_warning_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
-                        return 0;
-                }
-                break;
-
-        case AF_INET6:
-                r = sd_netlink_message_read_in6_addr(message, NHA_GATEWAY, &tmp->gw.in6);
-                if (r < 0 && r != -ENODATA) {
-                        log_warning_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
-                        return 0;
-                }
-                break;
-
-        default:
-                assert_not_reached("Received rule message with unsupported address family");
-        }
-
-        r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get NHA_ID attribute, ignoring: %m");
-                return 0;
-        }
-
-        r = sd_netlink_message_read_u32(message, NHA_OIF, &tmp->oif);
-        if (r < 0 && r != -ENODATA) {
-                log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
+                log_warning_errno(r, "rtnl: Could not get ifindex from link message, ignoring: %m");
                 return 0;
-        } else if (tmp->oif <= 0) {
-                log_warning("rtnl: received nexthop message with invalid ifindex %d, ignoring.", tmp->oif);
+        } else if (ifindex <= 0) {
+                log_warning("rtnl: received link message with invalid ifindex %d, ignoring.", ifindex);
                 return 0;
         }
 
-        r = link_get(m, tmp->oif, &link);
-        if (r < 0 || !link) {
-                if (!m->enumerating)
-                        log_warning("rtnl: received nexthop message for link (%d) we do not know about, ignoring", tmp->oif);
+        r = sd_netlink_message_read_string(message, IFLA_IFNAME, &name);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: Received link message without ifname, ignoring: %m");
                 return 0;
         }
 
-        (void) nexthop_get(link, tmp, &nexthop);
-
-        if (DEBUG_LOGGING)
-                (void) in_addr_to_string(tmp->family, &tmp->gw, &gateway);
+        (void) link_get(m, ifindex, &link);
+        (void) netdev_get(m, name, &netdev);
 
         switch (type) {
-        case RTM_NEWNEXTHOP:
-                if (nexthop)
-                        log_link_debug(link, "Received remembered nexthop: %s, oif: %d, id: %d", strna(gateway), tmp->oif, tmp->id);
-                else {
-                        log_link_debug(link, "Remembering foreign nexthop: %s, oif: %d, id: %d", strna(gateway), tmp->oif, tmp->id);
-                        r = nexthop_add_foreign(link, tmp, &nexthop);
+        case RTM_NEWLINK:
+                if (!link) {
+                        /* link is new, so add it */
+                        r = link_add(m, message, &link);
+                        if (r < 0) {
+                                log_warning_errno(r, "Could not process new link message, ignoring: %m");
+                                return 0;
+                        }
+                }
+
+                if (netdev) {
+                        /* netdev exists, so make sure the ifindex matches */
+                        r = netdev_set_ifindex(netdev, message);
                         if (r < 0) {
-                                log_link_warning_errno(link, r, "Could not remember foreign nexthop, ignoring: %m");
+                                log_warning_errno(r, "Could not process new link message for netdev, ignoring: %m");
                                 return 0;
                         }
                 }
+
+                r = link_update(link, message);
+                if (r < 0) {
+                        log_warning_errno(r, "Could not process link message, ignoring: %m");
+                        return 0;
+                }
+
                 break;
-        case RTM_DELNEXTHOP:
-                if (nexthop) {
-                        log_link_debug(link, "Forgetting nexthop: %s, oif: %d, id: %d", strna(gateway), tmp->oif, tmp->id);
-                        nexthop_free(nexthop);
-                } else
-                        log_link_debug(link, "Kernel removed a nexthop we don't remember: %s, oif: %d, id: %d, ignoring.",
-                                       strna(gateway), tmp->oif, tmp->id);
+
+        case RTM_DELLINK:
+                link_drop(link);
+                netdev_drop(netdev);
+
                 break;
 
         default:
-                assert_not_reached("Received invalid RTNL message type");
+                assert_not_reached("Received link message with invalid RTNL message type.");
         }
 
         return 1;
@@ -1393,51 +411,51 @@ static int manager_connect_rtnl(Manager *m) {
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWLINK, &manager_rtnl_process_link, NULL, m, "network-rtnl_process_link");
+        r = netlink_add_match(m->rtnl, NULL, RTM_NEWLINK, &manager_rtnl_process_link, NULL, m, "network-rtnl_process_link");
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELLINK, &manager_rtnl_process_link, NULL, m, "network-rtnl_process_link");
+        r = netlink_add_match(m->rtnl, NULL, RTM_DELLINK, &manager_rtnl_process_link, NULL, m, "network-rtnl_process_link");
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWADDR, &manager_rtnl_process_address, NULL, m, "network-rtnl_process_address");
+        r = netlink_add_match(m->rtnl, NULL, RTM_NEWADDR, &manager_rtnl_process_address, NULL, m, "network-rtnl_process_address");
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELADDR, &manager_rtnl_process_address, NULL, m, "network-rtnl_process_address");
+        r = netlink_add_match(m->rtnl, NULL, RTM_DELADDR, &manager_rtnl_process_address, NULL, m, "network-rtnl_process_address");
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWNEIGH, &manager_rtnl_process_neighbor, NULL, m, "network-rtnl_process_neighbor");
+        r = netlink_add_match(m->rtnl, NULL, RTM_NEWNEIGH, &manager_rtnl_process_neighbor, NULL, m, "network-rtnl_process_neighbor");
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELNEIGH, &manager_rtnl_process_neighbor, NULL, m, "network-rtnl_process_neighbor");
+        r = netlink_add_match(m->rtnl, NULL, RTM_DELNEIGH, &manager_rtnl_process_neighbor, NULL, m, "network-rtnl_process_neighbor");
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWROUTE, &manager_rtnl_process_route, NULL, m, "network-rtnl_process_route");
+        r = netlink_add_match(m->rtnl, NULL, RTM_NEWROUTE, &manager_rtnl_process_route, NULL, m, "network-rtnl_process_route");
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELROUTE, &manager_rtnl_process_route, NULL, m, "network-rtnl_process_route");
+        r = netlink_add_match(m->rtnl, NULL, RTM_DELROUTE, &manager_rtnl_process_route, NULL, m, "network-rtnl_process_route");
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWRULE, &manager_rtnl_process_rule, NULL, m, "network-rtnl_process_rule");
+        r = netlink_add_match(m->rtnl, NULL, RTM_NEWRULE, &manager_rtnl_process_rule, NULL, m, "network-rtnl_process_rule");
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELRULE, &manager_rtnl_process_rule, NULL, m, "network-rtnl_process_rule");
+        r = netlink_add_match(m->rtnl, NULL, RTM_DELRULE, &manager_rtnl_process_rule, NULL, m, "network-rtnl_process_rule");
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWNEXTHOP, &manager_rtnl_process_nexthop, NULL, m, "network-rtnl_process_nexthop");
+        r = netlink_add_match(m->rtnl, NULL, RTM_NEWNEXTHOP, &manager_rtnl_process_nexthop, NULL, m, "network-rtnl_process_nexthop");
         if (r < 0)
                 return r;
 
-        r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELNEXTHOP, &manager_rtnl_process_nexthop, NULL, m, "network-rtnl_process_nexthop");
+        r = netlink_add_match(m->rtnl, NULL, RTM_DELNEXTHOP, &manager_rtnl_process_nexthop, NULL, m, "network-rtnl_process_nexthop");
         if (r < 0)
                 return r;
 
@@ -1824,7 +842,7 @@ int manager_new(Manager **ret) {
         if (r < 0)
                 return r;
 
-        r = setup_default_address_pool(m);
+        r = address_pool_setup_default(m);
         if (r < 0)
                 return r;
 
@@ -1838,7 +856,6 @@ int manager_new(Manager **ret) {
 }
 
 void manager_free(Manager *m) {
-        AddressPool *pool;
         Link *link;
 
         if (!m)
@@ -1861,8 +878,7 @@ void manager_free(Manager *m) {
 
         m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref);
 
-        while ((pool = m->address_pools))
-                address_pool_free(pool);
+        ordered_set_free_free(m->address_pools);
 
         /* routing_policy_rule_free() access m->rules and m->rules_foreign.
          * So, it is necessary to set NULL after the sets are freed. */
@@ -1932,33 +948,41 @@ bool manager_should_reload(Manager *m) {
         return paths_check_timestamp(NETWORK_DIRS, &m->network_dirs_ts_usec, false);
 }
 
-int manager_rtnl_enumerate_links(Manager *m) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
-        sd_netlink_message *link;
+static int manager_enumerate_internal(
+                Manager *m,
+                sd_netlink_message *req,
+                int (*process)(sd_netlink *, sd_netlink_message *, Manager *),
+                const char *name) {
+
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *reply = NULL;
         int r;
 
         assert(m);
         assert(m->rtnl);
-
-        r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0);
-        if (r < 0)
-                return r;
+        assert(req);
+        assert(process);
 
         r = sd_netlink_message_request_dump(req, true);
         if (r < 0)
                 return r;
 
         r = sd_netlink_call(m->rtnl, req, 0, &reply);
-        if (r < 0)
+        if (r < 0) {
+                if (r == -EOPNOTSUPP && name) {
+                        log_debug_errno(r, "%s are not supported by the kernel. Ignoring.", name);
+                        return 0;
+                }
+
                 return r;
+        }
 
-        for (link = reply; link; link = sd_netlink_message_next(link)) {
+        for (sd_netlink_message *reply_one = reply; reply_one; reply_one = sd_netlink_message_next(reply_one)) {
                 int k;
 
                 m->enumerating = true;
 
-                k = manager_rtnl_process_link(m->rtnl, link, m);
-                if (k < 0)
+                k = process(m->rtnl, reply_one, m);
+                if (k < 0 && r >= 0)
                         r = k;
 
                 m->enumerating = false;
@@ -1967,44 +991,36 @@ int manager_rtnl_enumerate_links(Manager *m) {
         return r;
 }
 
-int manager_rtnl_enumerate_addresses(Manager *m) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
-        sd_netlink_message *addr;
+static int manager_enumerate_links(Manager *m) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         int r;
 
         assert(m);
         assert(m->rtnl);
 
-        r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, 0);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_message_request_dump(req, true);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_call(m->rtnl, req, 0, &reply);
+        r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0);
         if (r < 0)
                 return r;
 
-        for (addr = reply; addr; addr = sd_netlink_message_next(addr)) {
-                int k;
+        return manager_enumerate_internal(m, req, manager_rtnl_process_link, NULL);
+}
 
-                m->enumerating = true;
+static int manager_enumerate_addresses(Manager *m) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        int r;
 
-                k = manager_rtnl_process_address(m->rtnl, addr, m);
-                if (k < 0)
-                        r = k;
+        assert(m);
+        assert(m->rtnl);
 
-                m->enumerating = false;
-        }
+        r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, 0);
+        if (r < 0)
+                return r;
 
-        return r;
+        return manager_enumerate_internal(m, req, manager_rtnl_process_address, NULL);
 }
 
-int manager_rtnl_enumerate_neighbors(Manager *m) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
-        sd_netlink_message *neigh;
+static int manager_enumerate_neighbors(Manager *m) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         int r;
 
         assert(m);
@@ -2014,32 +1030,11 @@ int manager_rtnl_enumerate_neighbors(Manager *m) {
         if (r < 0)
                 return r;
 
-        r = sd_netlink_message_request_dump(req, true);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_call(m->rtnl, req, 0, &reply);
-        if (r < 0)
-                return r;
-
-        for (neigh = reply; neigh; neigh = sd_netlink_message_next(neigh)) {
-                int k;
-
-                m->enumerating = true;
-
-                k = manager_rtnl_process_neighbor(m->rtnl, neigh, m);
-                if (k < 0)
-                        r = k;
-
-                m->enumerating = false;
-        }
-
-        return r;
+        return manager_enumerate_internal(m, req, manager_rtnl_process_neighbor, NULL);
 }
 
-int manager_rtnl_enumerate_routes(Manager *m) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
-        sd_netlink_message *route;
+static int manager_enumerate_routes(Manager *m) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         int r;
 
         assert(m);
@@ -2052,32 +1047,11 @@ int manager_rtnl_enumerate_routes(Manager *m) {
         if (r < 0)
                 return r;
 
-        r = sd_netlink_message_request_dump(req, true);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_call(m->rtnl, req, 0, &reply);
-        if (r < 0)
-                return r;
-
-        for (route = reply; route; route = sd_netlink_message_next(route)) {
-                int k;
-
-                m->enumerating = true;
-
-                k = manager_rtnl_process_route(m->rtnl, route, m);
-                if (k < 0)
-                        r = k;
-
-                m->enumerating = false;
-        }
-
-        return r;
+        return manager_enumerate_internal(m, req, manager_rtnl_process_route, NULL);
 }
 
-int manager_rtnl_enumerate_rules(Manager *m) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
-        sd_netlink_message *rule;
+static int manager_enumerate_rules(Manager *m) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         int r;
 
         assert(m);
@@ -2087,38 +1061,11 @@ int manager_rtnl_enumerate_rules(Manager *m) {
         if (r < 0)
                 return r;
 
-        r = sd_netlink_message_request_dump(req, true);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_call(m->rtnl, req, 0, &reply);
-        if (r < 0) {
-                if (r == -EOPNOTSUPP) {
-                        log_debug("FIB Rules are not supported by the kernel. Ignoring.");
-                        return 0;
-                }
-
-                return r;
-        }
-
-        for (rule = reply; rule; rule = sd_netlink_message_next(rule)) {
-                int k;
-
-                m->enumerating = true;
-
-                k = manager_rtnl_process_rule(m->rtnl, rule, m);
-                if (k < 0)
-                        r = k;
-
-                m->enumerating = false;
-        }
-
-        return r;
+        return manager_enumerate_internal(m, req, manager_rtnl_process_rule, "Routing policy rules");
 }
 
-int manager_rtnl_enumerate_nexthop(Manager *m) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
-        sd_netlink_message *nexthop;
+static int manager_enumerate_nexthop(Manager *m) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         int r;
 
         assert(m);
@@ -2128,51 +1075,35 @@ int manager_rtnl_enumerate_nexthop(Manager *m) {
         if (r < 0)
                 return r;
 
-        r = sd_netlink_message_request_dump(req, true);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_call(m->rtnl, req, 0, &reply);
-        if (r < 0) {
-                if (r == -EOPNOTSUPP) {
-                        log_debug("Nexthop are not supported by the kernel. Ignoring.");
-                        return 0;
-                }
-
-                return r;
-        }
-
-        for (nexthop = reply; nexthop; nexthop = sd_netlink_message_next(nexthop)) {
-                int k;
-
-                m->enumerating = true;
+        return manager_enumerate_internal(m, req, manager_rtnl_process_nexthop, "Nexthop rules");
+}
 
-                k = manager_rtnl_process_nexthop(m->rtnl, nexthop, m);
-                if (k < 0)
-                        r = k;
+int manager_enumerate(Manager *m) {
+        int r;
 
-                m->enumerating = false;
-        }
+        r = manager_enumerate_links(m);
+        if (r < 0)
+                return log_error_errno(r, "Could not enumerate links: %m");
 
-        return r;
-}
+        r = manager_enumerate_addresses(m);
+        if (r < 0)
+                return log_error_errno(r, "Could not enumerate addresses: %m");
 
-int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
-        AddressPool *p;
-        int r;
+        r = manager_enumerate_neighbors(m);
+        if (r < 0)
+                return log_error_errno(r, "Could not enumerate neighbors: %m");
 
-        assert(m);
-        assert(prefixlen > 0);
-        assert(found);
+        r = manager_enumerate_routes(m);
+        if (r < 0)
+                return log_error_errno(r, "Could not enumerate routes: %m");
 
-        LIST_FOREACH(address_pools, p, m->address_pools) {
-                if (p->family != family)
-                        continue;
+        r = manager_enumerate_rules(m);
+        if (r < 0)
+                return log_error_errno(r, "Could not enumerate routing policy rules: %m");
 
-                r = address_pool_acquire(p, prefixlen, found);
-                if (r != 0)
-                        return r;
-        }
+        r = manager_enumerate_nexthop(m);
+        if (r < 0)
+                return log_error_errno(r, "Could not enumerate nexthop rules: %m");
 
         return 0;
 }
@@ -2313,51 +1244,3 @@ int manager_set_timezone(Manager *m, const char *tz) {
 
         return 0;
 }
-
-int manager_request_product_uuid(Manager *m, Link *link) {
-        int r;
-
-        assert(m);
-
-        if (m->has_product_uuid)
-                return 0;
-
-        log_debug("Requesting product UUID");
-
-        if (link) {
-                DUID *duid;
-
-                assert_se(duid = link_get_duid(link));
-
-                r = set_ensure_put(&m->links_requesting_uuid, NULL, link);
-                if (r < 0)
-                        return log_oom();
-                if (r > 0)
-                        link_ref(link);
-
-                r = set_ensure_put(&m->duids_requesting_uuid, NULL, duid);
-                if (r < 0)
-                        return log_oom();
-        }
-
-        if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
-                log_debug("Not connected to system bus, requesting product UUID later.");
-                return 0;
-        }
-
-        r = sd_bus_call_method_async(
-                        m->bus,
-                        NULL,
-                        "org.freedesktop.hostname1",
-                        "/org/freedesktop/hostname1",
-                        "org.freedesktop.hostname1",
-                        "GetProductUUID",
-                        get_product_uuid_handler,
-                        m,
-                        "b",
-                        false);
-        if (r < 0)
-                return log_warning_errno(r, "Failed to get product UUID: %m");
-
-        return 0;
-}
index 255a0d965f9105be47429fbe8a80c633c0fe4160..142a6eb3582a7185cfd7700eec151b8169df47fe 100644 (file)
 
 #include "dhcp-identifier.h"
 #include "hashmap.h"
-#include "list.h"
-#include "time-util.h"
-
-#include "networkd-address-pool.h"
 #include "networkd-link.h"
 #include "networkd-network.h"
+#include "ordered-set.h"
+#include "set.h"
+#include "time-util.h"
 
 struct Manager {
         sd_netlink *rtnl;
@@ -45,7 +44,7 @@ struct Manager {
         OrderedHashmap *networks;
         Hashmap *dhcp6_prefixes;
         Set *dhcp6_pd_prefixes;
-        LIST_HEAD(AddressPool, address_pools);
+        OrderedSet *address_pools;
 
         usec_t network_dirs_ts_usec;
 
@@ -82,27 +81,13 @@ int manager_start(Manager *m);
 int manager_load_config(Manager *m);
 bool manager_should_reload(Manager *m);
 
-int manager_rtnl_enumerate_links(Manager *m);
-int manager_rtnl_enumerate_addresses(Manager *m);
-int manager_rtnl_enumerate_neighbors(Manager *m);
-int manager_rtnl_enumerate_routes(Manager *m);
-int manager_rtnl_enumerate_rules(Manager *m);
-int manager_rtnl_enumerate_nexthop(Manager *m);
-
-int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, void *userdata);
-int manager_rtnl_process_neighbor(sd_netlink *nl, sd_netlink_message *message, void *userdata);
-int manager_rtnl_process_route(sd_netlink *nl, sd_netlink_message *message, void *userdata);
-int manager_rtnl_process_rule(sd_netlink *nl, sd_netlink_message *message, void *userdata);
-int manager_rtnl_process_nexthop(sd_netlink *nl, sd_netlink_message *message, void *userdata);
+int manager_enumerate(Manager *m);
 
 void manager_dirty(Manager *m);
 
-int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found);
-
 Link* manager_find_uplink(Manager *m, Link *exclude);
 
 int manager_set_hostname(Manager *m, const char *hostname);
 int manager_set_timezone(Manager *m, const char *timezone);
-int manager_request_product_uuid(Manager *m, Link *link);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
index 542ba75ad0f8ab51a4218730f6f74c5911b7fb72..bca3e620dd4528597cfbc2a770f355ec04a3b99d 100644 (file)
@@ -3,13 +3,32 @@
 #include <net/if.h>
 
 #include "netlink-util.h"
+#include "networkd-link.h"
 #include "networkd-manager.h"
 #include "networkd-mdb.h"
+#include "networkd-network.h"
 #include "string-util.h"
 #include "vlan-util.h"
 
 #define STATIC_MDB_ENTRIES_PER_NETWORK_MAX 1024U
 
+/* remove MDB entry. */
+MdbEntry *mdb_entry_free(MdbEntry *mdb_entry) {
+        if (!mdb_entry)
+                return NULL;
+
+        if (mdb_entry->network) {
+                assert(mdb_entry->section);
+                hashmap_remove(mdb_entry->network->mdb_entries_by_section, mdb_entry->section);
+        }
+
+        network_config_section_free(mdb_entry->section);
+
+        return mfree(mdb_entry);
+}
+
+DEFINE_NETWORK_SECTION_FUNCTIONS(MdbEntry, mdb_entry_free);
+
 /* create a new MDB entry or get an existing one. */
 static int mdb_entry_new_static(
                 Network *network,
@@ -23,22 +42,21 @@ static int mdb_entry_new_static(
 
         assert(network);
         assert(ret);
-        assert(!!filename == (section_line > 0));
+        assert(filename);
+        assert(section_line > 0);
 
-        /* search entry in hashmap first. */
-        if (filename) {
-                r = network_config_section_new(filename, section_line, &n);
-                if (r < 0)
-                        return r;
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
 
-                mdb_entry = hashmap_get(network->mdb_entries_by_section, n);
-                if (mdb_entry) {
-                        *ret = TAKE_PTR(mdb_entry);
-                        return 0;
-                }
+        /* search entry in hashmap first. */
+        mdb_entry = hashmap_get(network->mdb_entries_by_section, n);
+        if (mdb_entry) {
+                *ret = TAKE_PTR(mdb_entry);
+                return 0;
         }
 
-        if (network->n_static_mdb_entries >= STATIC_MDB_ENTRIES_PER_NETWORK_MAX)
+        if (hashmap_size(network->mdb_entries_by_section) >= STATIC_MDB_ENTRIES_PER_NETWORK_MAX)
                 return -E2BIG;
 
         /* allocate space for an MDB entry. */
@@ -49,48 +67,22 @@ static int mdb_entry_new_static(
         /* init MDB structure. */
         *mdb_entry = (MdbEntry) {
                 .network = network,
+                .section = TAKE_PTR(n),
         };
 
-        LIST_PREPEND(static_mdb_entries, network->static_mdb_entries, mdb_entry);
-        network->n_static_mdb_entries++;
-
-        if (filename) {
-                mdb_entry->section = TAKE_PTR(n);
-
-                r = hashmap_ensure_allocated(&network->mdb_entries_by_section, &network_config_hash_ops);
-                if (r < 0)
-                        return r;
+        r = hashmap_ensure_allocated(&network->mdb_entries_by_section, &network_config_hash_ops);
+        if (r < 0)
+                return r;
 
-                r = hashmap_put(network->mdb_entries_by_section, mdb_entry->section, mdb_entry);
-                if (r < 0)
-                        return r;
-        }
+        r = hashmap_put(network->mdb_entries_by_section, mdb_entry->section, mdb_entry);
+        if (r < 0)
+                return r;
 
         /* return allocated MDB structure. */
         *ret = TAKE_PTR(mdb_entry);
-
         return 0;
 }
 
-/* remove and MDB entry. */
-MdbEntry *mdb_entry_free(MdbEntry *mdb_entry) {
-        if (!mdb_entry)
-                return NULL;
-
-        if (mdb_entry->network) {
-                LIST_REMOVE(static_mdb_entries, mdb_entry->network->static_mdb_entries, mdb_entry);
-                assert(mdb_entry->network->n_static_mdb_entries > 0);
-                mdb_entry->network->n_static_mdb_entries--;
-
-                if (mdb_entry->section)
-                        hashmap_remove(mdb_entry->network->mdb_entries_by_section, mdb_entry->section);
-        }
-
-        network_config_section_free(mdb_entry->section);
-
-        return mfree(mdb_entry);
-}
-
 static int set_mdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
@@ -212,7 +204,7 @@ int link_set_bridge_mdb(Link *link) {
         if (!link->network)
                 return 0;
 
-        if (LIST_IS_EMPTY(link->network->static_mdb_entries))
+        if (hashmap_isempty(link->network->mdb_entries_by_section))
                 goto finish;
 
         if (!link_has_carrier(link))
@@ -236,7 +228,7 @@ int link_set_bridge_mdb(Link *link) {
                 goto finish;
         }
 
-        LIST_FOREACH(static_mdb_entries, mdb_entry, link->network->static_mdb_entries) {
+        HASHMAP_FOREACH(mdb_entry, link->network->mdb_entries_by_section) {
                 r = mdb_entry_configure(link, mdb_entry);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Failed to add MDB entry to multicast group database: %m");
@@ -253,6 +245,49 @@ finish:
         return 0;
 }
 
+static int mdb_entry_verify(MdbEntry *mdb_entry) {
+        if (section_is_invalid(mdb_entry->section))
+                return -EINVAL;
+
+        if (mdb_entry->family == AF_UNSPEC)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: [BridgeMDB] section without MulticastGroupAddress= field configured. "
+                                         "Ignoring [BridgeMDB] section from line %u.",
+                                         mdb_entry->section->filename, mdb_entry->section->line);
+
+        if (!in_addr_is_multicast(mdb_entry->family, &mdb_entry->group_addr))
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: MulticastGroupAddress= is not a multicast address. "
+                                         "Ignoring [BridgeMDB] section from line %u.",
+                                         mdb_entry->section->filename, mdb_entry->section->line);
+
+        if (mdb_entry->family == AF_INET) {
+                if (in4_addr_is_local_multicast(&mdb_entry->group_addr.in))
+                        return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                 "%s: MulticastGroupAddress= is a local multicast address. "
+                                                 "Ignoring [BridgeMDB] section from line %u.",
+                                                 mdb_entry->section->filename, mdb_entry->section->line);
+        } else {
+                if (in6_addr_is_link_local_all_nodes(&mdb_entry->group_addr.in6))
+                        return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                 "%s: MulticastGroupAddress= is the multicast all nodes address. "
+                                                 "Ignoring [BridgeMDB] section from line %u.",
+                                                 mdb_entry->section->filename, mdb_entry->section->line);
+        }
+
+        return 0;
+}
+
+void network_drop_invalid_mdb_entries(Network *network) {
+        MdbEntry *mdb_entry;
+
+        assert(network);
+
+        HASHMAP_FOREACH(mdb_entry, network->mdb_entries_by_section)
+                if (mdb_entry_verify(mdb_entry) < 0)
+                        mdb_entry_free(mdb_entry);
+}
+
 /* parse the VLAN Id from config files. */
 int config_parse_mdb_vlan_id(
                 const char *unit,
@@ -328,36 +363,3 @@ int config_parse_mdb_group_address(
 
         return 0;
 }
-
-int mdb_entry_verify(MdbEntry *mdb_entry) {
-        if (section_is_invalid(mdb_entry->section))
-                return -EINVAL;
-
-        if (mdb_entry->family == AF_UNSPEC)
-                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "%s: [BridgeMDB] section without MulticastGroupAddress= field configured. "
-                                         "Ignoring [BridgeMDB] section from line %u.",
-                                         mdb_entry->section->filename, mdb_entry->section->line);
-
-        if (!in_addr_is_multicast(mdb_entry->family, &mdb_entry->group_addr))
-                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "%s: MulticastGroupAddress= is not a multicast address. "
-                                         "Ignoring [BridgeMDB] section from line %u.",
-                                         mdb_entry->section->filename, mdb_entry->section->line);
-
-        if (mdb_entry->family == AF_INET) {
-                if (in4_addr_is_local_multicast(&mdb_entry->group_addr.in))
-                        return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                 "%s: MulticastGroupAddress= is a local multicast address. "
-                                                 "Ignoring [BridgeMDB] section from line %u.",
-                                                 mdb_entry->section->filename, mdb_entry->section->line);
-        } else {
-                if (in6_addr_is_link_local_all_nodes(&mdb_entry->group_addr.in6))
-                        return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                 "%s: MulticastGroupAddress= is the multicast all nodes address. "
-                                                 "Ignoring [BridgeMDB] section from line %u.",
-                                                 mdb_entry->section->filename, mdb_entry->section->line);
-        }
-
-        return 0;
-}
index d46ab4a50e0ecee0d6490508aeb40cf6e559eca2..563a9e6d994a8cbb94e2f95c946c578501eabeac 100644 (file)
@@ -1,32 +1,29 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include <inttypes.h>
+
 #include "conf-parser.h"
-#include "list.h"
-#include "macro.h"
+#include "in-addr-util.h"
 #include "networkd-util.h"
 
 typedef struct Network Network;
-typedef struct MdbEntry MdbEntry;
 typedef struct Link Link;
-typedef struct NetworkConfigSection NetworkConfigSection;
 
-struct MdbEntry {
+typedef struct MdbEntry {
         Network *network;
         NetworkConfigSection *section;
 
         int family;
         union in_addr_union group_addr;
         uint16_t vlan_id;
+} MdbEntry;
 
-        LIST_FIELDS(MdbEntry, static_mdb_entries);
-};
-
-int mdb_entry_verify(MdbEntry *mdb_entry);
 MdbEntry *mdb_entry_free(MdbEntry *mdb_entry);
-int link_set_bridge_mdb(Link *link);
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(MdbEntry, mdb_entry_free);
+void network_drop_invalid_mdb_entries(Network *network);
+
+int link_set_bridge_mdb(Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_mdb_group_address);
 CONFIG_PARSER_PROTOTYPE(config_parse_mdb_vlan_id);
index 68a66649f027e9a853476649e1fe133686d7a50b..2599ec22324628263c13d3daebb65bb0899ff58c 100644 (file)
@@ -3,16 +3,18 @@
   Copyright © 2014 Intel Corporation. All rights reserved.
 ***/
 
-#include <netinet/icmp6.h>
 #include <arpa/inet.h>
+#include <netinet/icmp6.h>
+#include <linux/if.h>
 
 #include "sd-ndisc.h"
 
 #include "missing_network.h"
+#include "networkd-address.h"
 #include "networkd-dhcp6.h"
 #include "networkd-manager.h"
 #include "networkd-ndisc.h"
-#include "networkd-route.h"
+#include "networkd-sysctl.h"
 #include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
 
 #define NDISC_APP_ID SD_ID128_MAKE(13,ac,81,a7,d5,3f,49,78,92,79,5d,0c,29,3a,bc,7e)
 
+bool link_ipv6_accept_ra_enabled(Link *link) {
+        assert(link);
+
+        if (!socket_ipv6_is_supported())
+                return false;
+
+        if (link->flags & IFF_LOOPBACK)
+                return false;
+
+        if (!link->network)
+                return false;
+
+        if (!link_ipv6ll_enabled(link))
+                return false;
+
+        /* If unset use system default (enabled if local forwarding is disabled.
+         * disabled if local forwarding is enabled).
+         * If set, ignore or enforce RA independent of local forwarding state.
+         */
+        if (link->network->ipv6_accept_ra < 0)
+                /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */
+                return !link_ip_forward_enabled(link, AF_INET6);
+        else if (link->network->ipv6_accept_ra > 0)
+                /* accept RA even if ip_forward is enabled */
+                return true;
+        else
+                /* ignore RA */
+                return false;
+}
+
 static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool force);
 
 static int ndisc_address_callback(Address *address) {
@@ -368,7 +400,7 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
                         return 1;
                 }
 
-                r = link_request_set_routes(link);
+                r = link_set_routes(link);
                 if (r < 0) {
                         link_enter_failed(link);
                         return 1;
@@ -490,7 +522,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
                 return log_link_error_errno(link, r, "Could not set default route: %m");
 
         Route *route_gw;
-        LIST_FOREACH(routes, route_gw, link->network->static_routes) {
+        HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
                 if (!route_gw->gateway_from_dhcp)
                         continue;
 
@@ -1133,9 +1165,9 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
         else {
                 log_link_debug(link, "Setting SLAAC addresses.");
 
-                /* address_handler calls link_request_set_routes() and link_request_set_nexthop().
-                 * Before they are called, the related flags must be cleared. Otherwise, the link
-                 * becomes configured state before routes are configured. */
+                /* address_handler calls link_set_routes() and link_set_nexthop(). Before they are
+                 * called, the related flags must be cleared. Otherwise, the link becomes configured
+                 * state before routes are configured. */
                 link->static_routes_configured = false;
                 link->static_nexthops_configured = false;
         }
@@ -1194,13 +1226,18 @@ int ndisc_configure(Link *link) {
 
         assert(link);
 
-        r = sd_ndisc_new(&link->ndisc);
-        if (r < 0)
-                return r;
+        if (!link_ipv6_accept_ra_enabled(link))
+                return 0;
 
-        r = sd_ndisc_attach_event(link->ndisc, NULL, 0);
-        if (r < 0)
-                return r;
+        if (!link->ndisc) {
+                r = sd_ndisc_new(&link->ndisc);
+                if (r < 0)
+                        return r;
+
+                r = sd_ndisc_attach_event(link->ndisc, link->manager->event, 0);
+                if (r < 0)
+                        return r;
+        }
 
         r = sd_ndisc_set_mac(link->ndisc, &link->mac);
         if (r < 0)
index 927f555bc61014de6ff0fc96f550839cfbd4f03c..fd7bb0e97fcceb80f8b0f86a2e622f1a0dd2bc8f 100644 (file)
@@ -69,6 +69,8 @@ static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) {
         return ((char*) n) + ALIGN(sizeof(NDiscDNSSL));
 }
 
+bool link_ipv6_accept_ra_enabled(Link *link);
+
 int ndisc_configure(Link *link);
 void ndisc_vacuum(Link *link);
 void ndisc_flush(Link *link);
index 09ddb9c8a56b809db09ba702d404efc5cac8cfed..b553f1707ecfb45e8ce05843f4952fc8ea2df54e 100644 (file)
@@ -1,29 +1,21 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
-#include "sd-netlink.h"
-
 #include "alloc-util.h"
-#include "conf-parser.h"
-#include "ether-addr-util.h"
 #include "hashmap.h"
-#include "in-addr-util.h"
 #include "netlink-util.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "networkd-neighbor.h"
+#include "networkd-network.h"
 #include "set.h"
 
-void neighbor_free(Neighbor *neighbor) {
+Neighbor *neighbor_free(Neighbor *neighbor) {
         if (!neighbor)
-                return;
+                return NULL;
 
         if (neighbor->network) {
-                LIST_REMOVE(neighbors, neighbor->network->neighbors, neighbor);
-                assert(neighbor->network->n_neighbors > 0);
-                neighbor->network->n_neighbors--;
-
-                if (neighbor->section)
-                        hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section);
+                assert(neighbor->section);
+                hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section);
         }
 
         network_config_section_free(neighbor->section);
@@ -33,9 +25,11 @@ void neighbor_free(Neighbor *neighbor) {
                 set_remove(neighbor->link->neighbors_foreign, neighbor);
         }
 
-        free(neighbor);
+        return mfree(neighbor);
 }
 
+DEFINE_NETWORK_SECTION_FUNCTIONS(Neighbor, neighbor_free);
+
 static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) {
         _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
         _cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
@@ -43,19 +37,17 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned
 
         assert(network);
         assert(ret);
-        assert(!!filename == (section_line > 0));
-
-        if (filename) {
-                r = network_config_section_new(filename, section_line, &n);
-                if (r < 0)
-                        return r;
+        assert(filename);
+        assert(section_line > 0);
 
-                neighbor = hashmap_get(network->neighbors_by_section, n);
-                if (neighbor) {
-                        *ret = TAKE_PTR(neighbor);
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
 
-                        return 0;
-                }
+        neighbor = hashmap_get(network->neighbors_by_section, n);
+        if (neighbor) {
+                *ret = TAKE_PTR(neighbor);
+                return 0;
         }
 
         neighbor = new(Neighbor, 1);
@@ -65,28 +57,163 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned
         *neighbor = (Neighbor) {
                 .network = network,
                 .family = AF_UNSPEC,
+                .section = TAKE_PTR(n),
         };
 
-        LIST_APPEND(neighbors, network->neighbors, neighbor);
-        network->n_neighbors++;
+        r = hashmap_ensure_allocated(&network->neighbors_by_section, &network_config_hash_ops);
+        if (r < 0)
+                return r;
 
-        if (filename) {
-                neighbor->section = TAKE_PTR(n);
+        r = hashmap_put(network->neighbors_by_section, neighbor->section, neighbor);
+        if (r < 0)
+                return r;
 
-                r = hashmap_ensure_allocated(&network->neighbors_by_section, &network_config_hash_ops);
-                if (r < 0)
+        *ret = TAKE_PTR(neighbor);
+        return 0;
+}
+
+static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) {
+        assert(neighbor);
+
+        siphash24_compress(&neighbor->family, sizeof(neighbor->family), state);
+        siphash24_compress(&neighbor->lladdr_size, sizeof(neighbor->lladdr_size), state);
+
+        switch (neighbor->family) {
+        case AF_INET:
+        case AF_INET6:
+                /* Equality of neighbors are given by the pair (addr,lladdr) */
+                siphash24_compress(&neighbor->in_addr, FAMILY_ADDRESS_SIZE(neighbor->family), state);
+                break;
+        default:
+                /* treat any other address family as AF_UNSPEC */
+                break;
+        }
+
+        siphash24_compress(&neighbor->lladdr, neighbor->lladdr_size, state);
+}
+
+static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
+        int r;
+
+        r = CMP(a->family, b->family);
+        if (r != 0)
+                return r;
+
+        r = CMP(a->lladdr_size, b->lladdr_size);
+        if (r != 0)
+                return r;
+
+        switch (a->family) {
+        case AF_INET:
+        case AF_INET6:
+                r = memcmp(&a->in_addr, &b->in_addr, FAMILY_ADDRESS_SIZE(a->family));
+                if (r != 0)
                         return r;
+        }
+
+        return memcmp(&a->lladdr, &b->lladdr, a->lladdr_size);
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func, neighbor_free);
+
+static int neighbor_get(Link *link, const Neighbor *in, Neighbor **ret) {
+        Neighbor *existing;
+
+        assert(link);
+        assert(in);
+
+        existing = set_get(link->neighbors, in);
+        if (existing) {
+                if (ret)
+                        *ret = existing;
+                return 1;
+        }
+
+        existing = set_get(link->neighbors_foreign, in);
+        if (existing) {
+                if (ret)
+                        *ret = existing;
+                return 0;
+        }
+
+        return -ENOENT;
+}
+
+static int neighbor_add_internal(Link *link, Set **neighbors, const Neighbor *in, Neighbor **ret) {
+        _cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
+        int r;
+
+        assert(link);
+        assert(neighbors);
+        assert(in);
 
-                r = hashmap_put(network->neighbors_by_section, neighbor->section, neighbor);
+        neighbor = new(Neighbor, 1);
+        if (!neighbor)
+                return -ENOMEM;
+
+        *neighbor = (Neighbor) {
+                .family = in->family,
+                .in_addr = in->in_addr,
+                .lladdr = in->lladdr,
+                .lladdr_size = in->lladdr_size,
+        };
+
+        r = set_ensure_put(neighbors, &neighbor_hash_ops, neighbor);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EEXIST;
+
+        neighbor->link = link;
+
+        if (ret)
+                *ret = neighbor;
+
+        TAKE_PTR(neighbor);
+        return 0;
+}
+
+static int neighbor_add(Link *link, const Neighbor *in, Neighbor **ret) {
+        Neighbor *neighbor;
+        int r;
+
+        r = neighbor_get(link, in, &neighbor);
+        if (r == -ENOENT) {
+                /* Neighbor doesn't exist, make a new one */
+                r = neighbor_add_internal(link, &link->neighbors, in, &neighbor);
+                if (r < 0)
+                        return r;
+        } else if (r == 0) {
+                /* Neighbor is foreign, claim it as recognized */
+                r = set_ensure_put(&link->neighbors, &neighbor_hash_ops, neighbor);
                 if (r < 0)
                         return r;
-        }
 
-        *ret = TAKE_PTR(neighbor);
+                set_remove(link->neighbors_foreign, neighbor);
+        } else if (r == 1) {
+                /* Neighbor already exists */
+        } else
+                return r;
 
+        if (ret)
+                *ret = neighbor;
         return 0;
 }
 
+static int neighbor_add_foreign(Link *link, const Neighbor *in, Neighbor **ret) {
+        return neighbor_add_internal(link, &link->neighbors_foreign, in, ret);
+}
+
+static bool neighbor_equal(const Neighbor *n1, const Neighbor *n2) {
+        if (n1 == n2)
+                return true;
+
+        if (!n1 || !n2)
+                return false;
+
+        return neighbor_compare_func(n1, n2) == 0;
+}
+
 static int neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
@@ -113,7 +240,7 @@ static int neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, L
         return 1;
 }
 
-int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback) {
+static int neighbor_configure(Neighbor *neighbor, Link *link) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         int r;
 
@@ -124,7 +251,7 @@ int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_hand
         assert(link->manager->rtnl);
 
         r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH,
-                                          link->ifindex, neighbor->family);
+                                      link->ifindex, neighbor->family);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not allocate RTM_NEWNEIGH message: %m");
 
@@ -144,7 +271,7 @@ int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_hand
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
 
-        r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_configure_handler,
+        r = netlink_call_async(link->manager->rtnl, NULL, req, neighbor_configure_handler,
                                link_netlink_destroy_callback, link);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
@@ -152,13 +279,40 @@ int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_hand
         link->neighbor_messages++;
         link_ref(link);
 
-        r = neighbor_add(link, neighbor->family, &neighbor->in_addr, &neighbor->lladdr, neighbor->lladdr_size, NULL);
+        r = neighbor_add(link, neighbor, NULL);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not add neighbor: %m");
 
         return 0;
 }
 
+int link_set_neighbors(Link *link) {
+        Neighbor *neighbor;
+        int r;
+
+        assert(link);
+        assert(link->network);
+        assert(link->state != _LINK_STATE_INVALID);
+
+        link->neighbors_configured = false;
+
+        HASHMAP_FOREACH(neighbor, link->network->neighbors_by_section) {
+                r = neighbor_configure(neighbor, link);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Could not set neighbor: %m");
+        }
+
+        if (link->neighbor_messages == 0) {
+                link->neighbors_configured = true;
+                link_check_ready(link);
+        } else {
+                log_link_debug(link, "Setting neighbors");
+                link_set_state(link, LINK_STATE_CONFIGURING);
+        }
+
+        return 0;
+}
+
 static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
@@ -176,7 +330,7 @@ static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
         return 1;
 }
 
-int neighbor_remove(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback) {
+static int neighbor_remove(Neighbor *neighbor, Link *link) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         int r;
 
@@ -187,7 +341,7 @@ int neighbor_remove(Neighbor *neighbor, Link *link, link_netlink_message_handler
         assert(link->manager->rtnl);
 
         r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_DELNEIGH,
-                                          link->ifindex, neighbor->family);
+                                      link->ifindex, neighbor->family);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not allocate RTM_DELNEIGH message: %m");
 
@@ -195,7 +349,7 @@ int neighbor_remove(Neighbor *neighbor, Link *link, link_netlink_message_handler
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
 
-        r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_remove_handler,
+        r = netlink_call_async(link->manager->rtnl, NULL, req, neighbor_remove_handler,
                                link_netlink_destroy_callback, link);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
@@ -205,158 +359,220 @@ int neighbor_remove(Neighbor *neighbor, Link *link, link_netlink_message_handler
         return 0;
 }
 
-static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) {
+static bool link_is_neighbor_configured(Link *link, Neighbor *neighbor) {
+        Neighbor *net_neighbor;
+
+        assert(link);
         assert(neighbor);
 
-        siphash24_compress(&neighbor->family, sizeof(neighbor->family), state);
-        siphash24_compress(&neighbor->lladdr_size, sizeof(neighbor->lladdr_size), state);
+        if (!link->network)
+                return false;
 
-        switch (neighbor->family) {
-        case AF_INET:
-        case AF_INET6:
-                /* Equality of neighbors are given by the pair (addr,lladdr) */
-                siphash24_compress(&neighbor->in_addr, FAMILY_ADDRESS_SIZE(neighbor->family), state);
-                break;
-        default:
-                /* treat any other address family as AF_UNSPEC */
-                break;
-        }
+        HASHMAP_FOREACH(net_neighbor, link->network->neighbors_by_section)
+                if (neighbor_equal(net_neighbor, neighbor))
+                        return true;
 
-        siphash24_compress(&neighbor->lladdr, neighbor->lladdr_size, state);
+        return false;
 }
 
-static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
+int link_drop_foreign_neighbors(Link *link) {
+        Neighbor *neighbor;
         int r;
 
-        r = CMP(a->family, b->family);
-        if (r != 0)
-                return r;
+        assert(link);
 
-        r = CMP(a->lladdr_size, b->lladdr_size);
-        if (r != 0)
-                return r;
+        SET_FOREACH(neighbor, link->neighbors_foreign)
+                if (link_is_neighbor_configured(link, neighbor)) {
+                        r = neighbor_add(link, neighbor, NULL);
+                        if (r < 0)
+                                return r;
+                } else {
+                        r = neighbor_remove(neighbor, link);
+                        if (r < 0)
+                                return r;
+                }
 
-        switch (a->family) {
-        case AF_INET:
-        case AF_INET6:
-                r = memcmp(&a->in_addr, &b->in_addr, FAMILY_ADDRESS_SIZE(a->family));
-                if (r != 0)
-                        return r;
+        return 0;
+}
+
+int link_drop_neighbors(Link *link) {
+        Neighbor *neighbor;
+        int k, r = 0;
+
+        assert(link);
+
+        SET_FOREACH(neighbor, link->neighbors) {
+                k = neighbor_remove(neighbor, link);
+                if (k < 0 && r >= 0)
+                        r = k;
         }
 
-        return memcmp(&a->lladdr, &b->lladdr, a->lladdr_size);
+        return r;
 }
 
-DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func, neighbor_free);
-
-int neighbor_get(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) {
-        Neighbor neighbor, *existing;
+static int manager_rtnl_process_neighbor_lladdr(sd_netlink_message *message, union lladdr_union *lladdr, size_t *size, char **str) {
+        int r;
 
-        assert(link);
-        assert(addr);
+        assert(message);
         assert(lladdr);
+        assert(size);
+        assert(str);
 
-        neighbor = (Neighbor) {
-                .family = family,
-                .in_addr = *addr,
-                .lladdr = *lladdr,
-                .lladdr_size = lladdr_size,
-        };
+        *str = NULL;
 
-        existing = set_get(link->neighbors, &neighbor);
-        if (existing) {
-                if (ret)
-                        *ret = existing;
-                return 1;
+        r = sd_netlink_message_read(message, NDA_LLADDR, sizeof(lladdr->ip.in6), &lladdr->ip.in6);
+        if (r >= 0) {
+                *size = sizeof(lladdr->ip.in6);
+                if (in_addr_to_string(AF_INET6, &lladdr->ip, str) < 0)
+                        log_warning_errno(r, "Could not print lower address: %m");
+                return r;
         }
 
-        existing = set_get(link->neighbors_foreign, &neighbor);
-        if (existing) {
-                if (ret)
-                        *ret = existing;
-                return 0;
+        r = sd_netlink_message_read(message, NDA_LLADDR, sizeof(lladdr->mac), &lladdr->mac);
+        if (r >= 0) {
+                *size = sizeof(lladdr->mac);
+                *str = new(char, ETHER_ADDR_TO_STRING_MAX);
+                if (!*str) {
+                        log_oom();
+                        return r;
+                }
+                ether_addr_to_string(&lladdr->mac, *str);
+                return r;
         }
 
-        return -ENOENT;
+        r = sd_netlink_message_read(message, NDA_LLADDR, sizeof(lladdr->ip.in), &lladdr->ip.in);
+        if (r >= 0) {
+                *size = sizeof(lladdr->ip.in);
+                if (in_addr_to_string(AF_INET, &lladdr->ip, str) < 0)
+                        log_warning_errno(r, "Could not print lower address: %m");
+                return r;
+        }
+
+        return r;
 }
 
-static int neighbor_add_internal(Link *link, Set **neighbors, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) {
-        _cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
-        int r;
+int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
+        _cleanup_(neighbor_freep) Neighbor *tmp = NULL;
+        _cleanup_free_ char *addr_str = NULL, *lladdr_str = NULL;
+        Neighbor *neighbor = NULL;
+        uint16_t type, state;
+        int ifindex, r;
+        Link *link;
 
-        assert(link);
-        assert(neighbors);
-        assert(addr);
-        assert(lladdr);
+        assert(rtnl);
+        assert(message);
+        assert(m);
 
-        neighbor = new(Neighbor, 1);
-        if (!neighbor)
-                return -ENOMEM;
+        if (sd_netlink_message_is_error(message)) {
+                r = sd_netlink_message_get_errno(message);
+                if (r < 0)
+                        log_message_warning_errno(message, r, "rtnl: failed to receive neighbor message, ignoring");
 
-        *neighbor = (Neighbor) {
-                .family = family,
-                .in_addr = *addr,
-                .lladdr = *lladdr,
-                .lladdr_size = lladdr_size,
-        };
+                return 0;
+        }
 
-        r = set_ensure_put(neighbors, &neighbor_hash_ops, neighbor);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                return -EEXIST;
+        r = sd_netlink_message_get_type(message, &type);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
+                return 0;
+        } else if (!IN_SET(type, RTM_NEWNEIGH, RTM_DELNEIGH)) {
+                log_warning("rtnl: received unexpected message type %u when processing neighbor, ignoring.", type);
+                return 0;
+        }
 
-        neighbor->link = link;
+        r = sd_rtnl_message_neigh_get_state(message, &state);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: received neighbor message with invalid state, ignoring: %m");
+                return 0;
+        } else if (!FLAGS_SET(state, NUD_PERMANENT)) {
+                log_debug("rtnl: received non-static neighbor, ignoring.");
+                return 0;
+        }
 
-        if (ret)
-                *ret = neighbor;
-        TAKE_PTR(neighbor);
+        r = sd_rtnl_message_neigh_get_ifindex(message, &ifindex);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get ifindex from message, ignoring: %m");
+                return 0;
+        } else if (ifindex <= 0) {
+                log_warning("rtnl: received neighbor message with invalid ifindex %d, ignoring.", ifindex);
+                return 0;
+        }
 
-        return 0;
-}
+        r = link_get(m, ifindex, &link);
+        if (r < 0 || !link) {
+                /* when enumerating we might be out of sync, but we will get the neighbor again, so just
+                 * ignore it */
+                if (!m->enumerating)
+                        log_warning("rtnl: received neighbor for link '%d' we don't know about, ignoring.", ifindex);
+                return 0;
+        }
 
-int neighbor_add(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) {
-        Neighbor *neighbor;
-        int r;
+        tmp = new0(Neighbor, 1);
 
-        r = neighbor_get(link, family, addr, lladdr, lladdr_size, &neighbor);
-        if (r == -ENOENT) {
-                /* Neighbor doesn't exist, make a new one */
-                r = neighbor_add_internal(link, &link->neighbors, family, addr, lladdr, lladdr_size, &neighbor);
-                if (r < 0)
-                        return r;
-        } else if (r == 0) {
-                /* Neighbor is foreign, claim it as recognized */
-                r = set_ensure_put(&link->neighbors, &neighbor_hash_ops, neighbor);
-                if (r < 0)
-                        return r;
+        r = sd_rtnl_message_neigh_get_family(message, &tmp->family);
+        if (r < 0) {
+                log_link_warning(link, "rtnl: received neighbor message without family, ignoring.");
+                return 0;
+        } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
+                log_link_debug(link, "rtnl: received neighbor message with invalid family '%i', ignoring.", tmp->family);
+                return 0;
+        }
 
-                set_remove(link->neighbors_foreign, neighbor);
-        } else if (r == 1) {
-                /* Neighbor already exists */
-        } else
-                return r;
+        r = netlink_message_read_in_addr_union(message, NDA_DST, tmp->family, &tmp->in_addr);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received neighbor message without valid address, ignoring: %m");
+                return 0;
+        }
 
-        if (ret)
-                *ret = neighbor;
-        return 0;
-}
+        if (in_addr_to_string(tmp->family, &tmp->in_addr, &addr_str) < 0)
+                log_link_warning_errno(link, r, "Could not print address: %m");
 
-int neighbor_add_foreign(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) {
-        return neighbor_add_internal(link, &link->neighbors_foreign, family, addr, lladdr, lladdr_size, ret);
-}
+        r = manager_rtnl_process_neighbor_lladdr(message, &tmp->lladdr, &tmp->lladdr_size, &lladdr_str);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received neighbor message with invalid lladdr, ignoring: %m");
+                return 0;
+        }
 
-bool neighbor_equal(const Neighbor *n1, const Neighbor *n2) {
-        if (n1 == n2)
-                return true;
+        (void) neighbor_get(link, tmp, &neighbor);
+
+        switch (type) {
+        case RTM_NEWNEIGH:
+                if (neighbor)
+                        log_link_debug(link, "Received remembered neighbor: %s->%s",
+                                       strnull(addr_str), strnull(lladdr_str));
+                else {
+                        /* A neighbor appeared that we did not request */
+                        r = neighbor_add_foreign(link, tmp, NULL);
+                        if (r < 0) {
+                                log_link_warning_errno(link, r, "Failed to remember foreign neighbor %s->%s, ignoring: %m",
+                                                       strnull(addr_str), strnull(lladdr_str));
+                                return 0;
+                        } else
+                                log_link_debug(link, "Remembering foreign neighbor: %s->%s",
+                                               strnull(addr_str), strnull(lladdr_str));
+                }
 
-        if (!n1 || !n2)
-                return false;
+                break;
 
-        return neighbor_compare_func(n1, n2) == 0;
+        case RTM_DELNEIGH:
+                if (neighbor) {
+                        log_link_debug(link, "Forgetting neighbor: %s->%s",
+                                       strnull(addr_str), strnull(lladdr_str));
+                        (void) neighbor_free(neighbor);
+                } else
+                        log_link_debug(link, "Kernel removed a neighbor we don't remember: %s->%s, ignoring.",
+                                       strnull(addr_str), strnull(lladdr_str));
+
+                break;
+
+        default:
+                assert_not_reached("Received invalid RTNL message type");
+        }
+
+        return 1;
 }
 
-int neighbor_section_verify(Neighbor *neighbor) {
+static int neighbor_section_verify(Neighbor *neighbor) {
         if (section_is_invalid(neighbor->section))
                 return -EINVAL;
 
@@ -375,6 +591,17 @@ int neighbor_section_verify(Neighbor *neighbor) {
         return 0;
 }
 
+void network_drop_invalid_neighbors(Network *network) {
+        Neighbor *neighbor;
+
+        assert(network);
+
+        HASHMAP_FOREACH(neighbor, network->neighbors_by_section)
+                if (neighbor_section_verify(neighbor) < 0)
+                        neighbor_free(neighbor);
+}
+
+
 int config_parse_neighbor_address(
                 const char *unit,
                 const char *filename,
index 97ee1f6d73ea4931815dc7e7d431fdbbb6ddf475..bb403ef2da2e00c6613c92de91e40227daf5c6fc 100644 (file)
@@ -1,26 +1,25 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include <stdbool.h>
+
 #include "sd-netlink.h"
 
 #include "conf-parser.h"
 #include "ether-addr-util.h"
 #include "in-addr-util.h"
-#include "list.h"
-#include "macro.h"
-
-typedef struct Neighbor Neighbor;
-
-#include "networkd-link.h"
-#include "networkd-network.h"
 #include "networkd-util.h"
 
+typedef Manager Manager;
+typedef Network Network;
+typedef Link Link;
+
 union lladdr_union {
         struct ether_addr mac;
         union in_addr_union ip;
 };
 
-struct Neighbor {
+typedef struct Neighbor {
         Network *network;
         Link *link;
         NetworkConfigSection *section;
@@ -29,23 +28,17 @@ struct Neighbor {
         union in_addr_union in_addr;
         union lladdr_union lladdr;
         size_t lladdr_size;
+} Neighbor;
 
-        LIST_FIELDS(Neighbor, neighbors);
-};
-
-void neighbor_free(Neighbor *neighbor);
-
-DEFINE_NETWORK_SECTION_FUNCTIONS(Neighbor, neighbor_free);
+Neighbor *neighbor_free(Neighbor *neighbor);
 
-int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback);
-int neighbor_remove(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback);
+void network_drop_invalid_neighbors(Network *network);
 
-int neighbor_get(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret);
-int neighbor_add(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret);
-int neighbor_add_foreign(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret);
-bool neighbor_equal(const Neighbor *n1, const Neighbor *n2);
+int link_set_neighbors(Link *link);
+int link_drop_neighbors(Link *link);
+int link_drop_foreign_neighbors(Link *link);
 
-int neighbor_section_verify(Neighbor *neighbor);
+int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_address);
 CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_hwaddr);
index 1258203adf6889c815aefb531350ee34e5d55d9c..21d2e820e9cfa53859dc06d8aa5ad04b76149ac6 100644 (file)
@@ -6,15 +6,25 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
 #include "conf-parser.h"
 #include "netem.h"
 #include "network-internal.h"
+#include "networkd-address-label.h"
+#include "networkd-address.h"
 #include "networkd-can.h"
 #include "networkd-conf.h"
 #include "networkd-dhcp-common.h"
 #include "networkd-dhcp-server.h"
 #include "networkd-dhcp4.h"
 #include "networkd-dhcp6.h"
+#include "networkd-fdb.h"
 #include "networkd-ipv4ll.h"
+#include "networkd-ipv6-proxy-ndp.h"
+#include "networkd-mdb.h"
 #include "networkd-ndisc.h"
 #include "networkd-network.h"
+#include "networkd-neighbor.h"
+#include "networkd-nexthop.h"
+#include "networkd-radv.h"
+#include "networkd-route.h"
+#include "networkd-routing-policy-rule.h"
 #include "networkd-sriov.h"
 #include "qdisc.h"
 #include "tclass.h"
@@ -121,11 +131,11 @@ Address.Peer,                                config_parse_address,
 Address.Broadcast,                           config_parse_broadcast,                                   0,                             0
 Address.Label,                               config_parse_label,                                       0,                             0
 Address.PreferredLifetime,                   config_parse_lifetime,                                    0,                             0
-Address.HomeAddress,                         config_parse_address_flags,                               0,                             0
-Address.ManageTemporaryAddress,              config_parse_address_flags,                               0,                             0
-Address.PrefixRoute,                         config_parse_address_flags,                               0,                             0 /* deprecated */
-Address.AddPrefixRoute,                      config_parse_address_flags,                               0,                             0
-Address.AutoJoin,                            config_parse_address_flags,                               0,                             0
+Address.HomeAddress,                         config_parse_address_flags,                               IFA_F_HOMEADDRESS,             0
+Address.ManageTemporaryAddress,              config_parse_address_flags,                               IFA_F_MANAGETEMPADDR,          0
+Address.PrefixRoute,                         config_parse_address_flags,                               IFA_F_NOPREFIXROUTE,           0 /* deprecated */
+Address.AddPrefixRoute,                      config_parse_address_flags,                               IFA_F_NOPREFIXROUTE,           0
+Address.AutoJoin,                            config_parse_address_flags,                               IFA_F_MCAUTOJOIN,              0
 Address.DuplicateAddressDetection,           config_parse_duplicate_address_detection,                 0,                             0
 Address.Scope,                               config_parse_address_scope,                               0,                             0
 IPv6AddressLabel.Prefix,                     config_parse_address_label_prefix,                        0,                             0
index 49e6d62abb53688071a09050dba5f97d4067cdcb..1ef3a6a6d80c3a1924647a12df7128b56d8b93db 100644 (file)
 #include "in-addr-util.h"
 #include "networkd-dhcp-server.h"
 #include "network-internal.h"
+#include "networkd-address-label.h"
+#include "networkd-address.h"
+#include "networkd-fdb.h"
 #include "networkd-manager.h"
+#include "networkd-mdb.h"
+#include "networkd-neighbor.h"
 #include "networkd-network.h"
+#include "networkd-nexthop.h"
+#include "networkd-radv.h"
+#include "networkd-routing-policy-rule.h"
 #include "networkd-sriov.h"
 #include "parse-util.h"
 #include "path-lookup.h"
@@ -148,19 +156,6 @@ static int network_resolve_stacked_netdevs(Network *network) {
 }
 
 int network_verify(Network *network) {
-        RoutePrefix *route_prefix, *route_prefix_next;
-        RoutingPolicyRule *rule, *rule_next;
-        Neighbor *neighbor, *neighbor_next;
-        AddressLabel *label, *label_next;
-        NextHop *nexthop, *nextnop_next;
-        Address *address, *address_next;
-        Prefix *prefix, *prefix_next;
-        Route *route, *route_next;
-        FdbEntry *fdb, *fdb_next;
-        MdbEntry *mdb, *mdb_next;
-        TrafficControl *tc;
-        SRIOV *sr_iov;
-
         assert(network);
         assert(network->filename);
 
@@ -213,18 +208,15 @@ int network_verify(Network *network) {
                                     network->filename);
                         network->dhcp_server = false;
                 }
-                if (network->n_static_addresses > 0) {
+                if (!ordered_hashmap_isempty(network->addresses_by_section))
                         log_warning("%s: Cannot set addresses when Bond= is specified, ignoring addresses.",
                                     network->filename);
-                        while ((address = network->static_addresses))
-                                address_free(address);
-                }
-                if (network->n_static_routes > 0) {
+                if (!hashmap_isempty(network->routes_by_section))
                         log_warning("%s: Cannot set routes when Bond= is specified, ignoring routes.",
                                     network->filename);
-                        while ((route = network->static_routes))
-                                route_free(route);
-                }
+
+                network->addresses_by_section = ordered_hashmap_free_with_destructor(network->addresses_by_section, address_free);
+                network->routes_by_section = hashmap_free_with_destructor(network->routes_by_section, route_free);
         }
 
         if (network->link_local < 0)
@@ -290,54 +282,23 @@ int network_verify(Network *network) {
         if (network->keep_configuration < 0)
                 network->keep_configuration = KEEP_CONFIGURATION_NO;
 
-        LIST_FOREACH_SAFE(addresses, address, address_next, network->static_addresses)
-                if (address_section_verify(address) < 0)
-                        address_free(address);
-
-        LIST_FOREACH_SAFE(routes, route, route_next, network->static_routes)
-                if (route_section_verify(route, network) < 0)
-                        route_free(route);
-
-        LIST_FOREACH_SAFE(nexthops, nexthop, nextnop_next, network->static_nexthops)
-                if (nexthop_section_verify(nexthop) < 0)
-                        nexthop_free(nexthop);
-
-        LIST_FOREACH_SAFE(static_fdb_entries, fdb, fdb_next, network->static_fdb_entries)
-                if (section_is_invalid(fdb->section))
-                        fdb_entry_free(fdb);
-
-        LIST_FOREACH_SAFE(static_mdb_entries, mdb, mdb_next, network->static_mdb_entries)
-                if (mdb_entry_verify(mdb) < 0)
-                        mdb_entry_free(mdb);
-
-        LIST_FOREACH_SAFE(neighbors, neighbor, neighbor_next, network->neighbors)
-                if (neighbor_section_verify(neighbor) < 0)
-                        neighbor_free(neighbor);
-
-        LIST_FOREACH_SAFE(labels, label, label_next, network->address_labels)
-                if (section_is_invalid(label->section))
-                        address_label_free(label);
-
-        LIST_FOREACH_SAFE(prefixes, prefix, prefix_next, network->static_prefixes)
-                if (section_is_invalid(prefix->section))
-                        prefix_free(prefix);
-
-        LIST_FOREACH_SAFE(route_prefixes, route_prefix, route_prefix_next, network->static_route_prefixes)
-                if (section_is_invalid(route_prefix->section))
-                        route_prefix_free(route_prefix);
-
-        LIST_FOREACH_SAFE(rules, rule, rule_next, network->rules)
-                if (routing_policy_rule_section_verify(rule) < 0)
-                        routing_policy_rule_free(rule);
-
-        bool has_root = false, has_clsact = false;
-        ORDERED_HASHMAP_FOREACH(tc, network->tc_by_section)
-                if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0)
-                        traffic_control_free(tc);
+        if (network->ipv6_proxy_ndp == 0 && !set_isempty(network->ipv6_proxy_ndp_addresses)) {
+                log_warning("%s: IPv6ProxyNDP= is disabled. Ignoring IPv6ProxyNDPAddress=.", network->filename);
+                network->ipv6_proxy_ndp_addresses = set_free_free(network->ipv6_proxy_ndp_addresses);
+        }
 
-        ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section)
-                if (sr_iov_section_verify(sr_iov) < 0)
-                        sr_iov_free(sr_iov);
+        network_drop_invalid_addresses(network);
+        network_drop_invalid_routes(network);
+        network_drop_invalid_nexthops(network);
+        network_drop_invalid_fdb_entries(network);
+        network_drop_invalid_mdb_entries(network);
+        network_drop_invalid_neighbors(network);
+        network_drop_invalid_address_labels(network);
+        network_drop_invalid_prefixes(network);
+        network_drop_invalid_route_prefixes(network);
+        network_drop_invalid_routing_policy_rules(network);
+        network_drop_invalid_traffic_control(network);
+        network_drop_invalid_sr_iov(network);
 
         return 0;
 }
@@ -644,18 +605,6 @@ failure:
 }
 
 static Network *network_free(Network *network) {
-        IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
-        RoutePrefix *route_prefix;
-        RoutingPolicyRule *rule;
-        AddressLabel *label;
-        FdbEntry *fdb_entry;
-        MdbEntry *mdb_entry;
-        Neighbor *neighbor;
-        Address *address;
-        NextHop *nexthop;
-        Prefix *prefix;
-        Route *route;
-
         if (!network)
                 return NULL;
 
@@ -711,49 +660,17 @@ static Network *network_free(Network *network) {
         netdev_unref(network->vrf);
         hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref);
 
-        while ((route = network->static_routes))
-                route_free(route);
-
-        while ((nexthop = network->static_nexthops))
-                nexthop_free(nexthop);
-
-        while ((address = network->static_addresses))
-                address_free(address);
-
-        while ((fdb_entry = network->static_fdb_entries))
-                fdb_entry_free(fdb_entry);
-
-        while ((mdb_entry = network->static_mdb_entries))
-                mdb_entry_free(mdb_entry);
-
-        while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
-                ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
-
-        while ((neighbor = network->neighbors))
-                neighbor_free(neighbor);
-
-        while ((label = network->address_labels))
-                address_label_free(label);
-
-        while ((prefix = network->static_prefixes))
-                prefix_free(prefix);
-
-        while ((route_prefix = network->static_route_prefixes))
-                route_prefix_free(route_prefix);
-
-        while ((rule = network->rules))
-                routing_policy_rule_free(rule);
-
-        hashmap_free(network->addresses_by_section);
-        hashmap_free(network->routes_by_section);
-        hashmap_free(network->nexthops_by_section);
-        hashmap_free(network->fdb_entries_by_section);
-        hashmap_free(network->mdb_entries_by_section);
-        hashmap_free(network->neighbors_by_section);
-        hashmap_free(network->address_labels_by_section);
-        hashmap_free(network->prefixes_by_section);
-        hashmap_free(network->route_prefixes_by_section);
-        hashmap_free(network->rules_by_section);
+        set_free_free(network->ipv6_proxy_ndp_addresses);
+        ordered_hashmap_free_with_destructor(network->addresses_by_section, address_free);
+        hashmap_free_with_destructor(network->routes_by_section, route_free);
+        hashmap_free_with_destructor(network->nexthops_by_section, nexthop_free);
+        hashmap_free_with_destructor(network->fdb_entries_by_section, fdb_entry_free);
+        hashmap_free_with_destructor(network->mdb_entries_by_section, mdb_entry_free);
+        hashmap_free_with_destructor(network->neighbors_by_section, neighbor_free);
+        hashmap_free_with_destructor(network->address_labels_by_section, address_label_free);
+        hashmap_free_with_destructor(network->prefixes_by_section, prefix_free);
+        hashmap_free_with_destructor(network->route_prefixes_by_section, route_prefix_free);
+        hashmap_free_with_destructor(network->rules_by_section, routing_policy_rule_free);
         ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
         ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);
 
@@ -866,30 +783,33 @@ bool network_has_static_ipv6_configurations(Network *network) {
 
         assert(network);
 
-        LIST_FOREACH(addresses, address, network->static_addresses)
+        ORDERED_HASHMAP_FOREACH(address, network->addresses_by_section)
                 if (address->family == AF_INET6)
                         return true;
 
-        LIST_FOREACH(routes, route, network->static_routes)
+        HASHMAP_FOREACH(route, network->routes_by_section)
                 if (route->family == AF_INET6)
                         return true;
 
-        LIST_FOREACH(static_fdb_entries, fdb, network->static_fdb_entries)
+        HASHMAP_FOREACH(fdb, network->fdb_entries_by_section)
                 if (fdb->family == AF_INET6)
                         return true;
 
-        LIST_FOREACH(static_mdb_entries, mdb, network->static_mdb_entries)
+        HASHMAP_FOREACH(mdb, network->mdb_entries_by_section)
                 if (mdb->family == AF_INET6)
                         return true;
 
-        LIST_FOREACH(neighbors, neighbor, network->neighbors)
+        HASHMAP_FOREACH(neighbor, network->neighbors_by_section)
                 if (neighbor->family == AF_INET6)
                         return true;
 
-        if (!LIST_IS_EMPTY(network->address_labels))
+        if (!hashmap_isempty(network->address_labels_by_section))
+                return true;
+
+        if (!hashmap_isempty(network->prefixes_by_section))
                 return true;
 
-        if (!LIST_IS_EMPTY(network->static_prefixes))
+        if (!hashmap_isempty(network->route_prefixes_by_section))
                 return true;
 
         return false;
@@ -1026,95 +946,6 @@ int config_parse_domains(
         }
 }
 
-int config_parse_ipv6token(
-                const char* unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        union in_addr_union buffer;
-        struct in6_addr *token = data;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(token);
-
-        r = in_addr_from_string(AF_INET6, rvalue, &buffer);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse IPv6 token, ignoring: %s", rvalue);
-                return 0;
-        }
-
-        if (in_addr_is_null(AF_INET6, &buffer)) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "IPv6 token cannot be the ANY address, ignoring: %s", rvalue);
-                return 0;
-        }
-
-        if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue);
-                return 0;
-        }
-
-        *token = buffer.in6;
-
-        return 0;
-}
-
-static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
-        [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
-        [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
-        [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExtensions,
-                                        IPV6_PRIVACY_EXTENSIONS_YES);
-
-int config_parse_ipv6_privacy_extensions(
-                const char* unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        IPv6PrivacyExtensions s, *ipv6_privacy_extensions = data;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(ipv6_privacy_extensions);
-
-        s = ipv6_privacy_extensions_from_string(rvalue);
-        if (s < 0) {
-                if (streq(rvalue, "kernel"))
-                        s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
-                else {
-                        log_syntax(unit, LOG_WARNING, filename, line, 0,
-                                   "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
-                        return 0;
-                }
-        }
-
-        *ipv6_privacy_extensions = s;
-
-        return 0;
-}
-
 int config_parse_hostname(
                 const char *unit,
                 const char *filename,
index 5ba0bc705d2f326437ddb1294ad97929846cd16f..cbcbb73872518291876b3f7368497e95065dba48 100644 (file)
 #include "conf-parser.h"
 #include "hashmap.h"
 #include "netdev.h"
-#include "networkd-address-label.h"
-#include "networkd-address.h"
 #include "networkd-brvlan.h"
 #include "networkd-dhcp-common.h"
 #include "networkd-dhcp4.h"
 #include "networkd-dhcp6.h"
 #include "networkd-dhcp-server.h"
-#include "networkd-fdb.h"
-#include "networkd-ipv6-proxy-ndp.h"
 #include "networkd-lldp-rx.h"
 #include "networkd-lldp-tx.h"
-#include "networkd-mdb.h"
 #include "networkd-ndisc.h"
-#include "networkd-neighbor.h"
-#include "networkd-nexthop.h"
 #include "networkd-radv.h"
-#include "networkd-route.h"
-#include "networkd-routing-policy-rule.h"
+#include "networkd-sysctl.h"
 #include "networkd-util.h"
 #include "ordered-set.h"
 #include "resolve-util.h"
 #include "socket-netlink.h"
 
-typedef enum IPv6PrivacyExtensions {
-        /* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */
-        IPV6_PRIVACY_EXTENSIONS_NO,
-        IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC,
-        IPV6_PRIVACY_EXTENSIONS_YES, /* aka prefer-temporary */
-        _IPV6_PRIVACY_EXTENSIONS_MAX,
-        _IPV6_PRIVACY_EXTENSIONS_INVALID = -1,
-} IPv6PrivacyExtensions;
-
 typedef enum KeepConfiguration {
         KEEP_CONFIGURATION_NO            = 0,
         KEEP_CONFIGURATION_DHCP_ON_START = 1 << 0,
@@ -245,6 +228,7 @@ struct Network {
         int ipv6_dad_transmits;
         int ipv6_hop_limit;
         int ipv6_proxy_ndp;
+        Set *ipv6_proxy_ndp_addresses;
         int proxy_arp;
         uint32_t ipv6_mtu;
 
@@ -285,31 +269,7 @@ struct Network {
         LLDPEmit lldp_emit; /* LLDP transmission */
         char *lldp_mud;    /* LLDP MUD URL */
 
-        LIST_HEAD(Address, static_addresses);
-        LIST_HEAD(Route, static_routes);
-        LIST_HEAD(NextHop, static_nexthops);
-        LIST_HEAD(FdbEntry, static_fdb_entries);
-        LIST_HEAD(MdbEntry, static_mdb_entries);
-        LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
-        LIST_HEAD(Neighbor, neighbors);
-        LIST_HEAD(AddressLabel, address_labels);
-        LIST_HEAD(Prefix, static_prefixes);
-        LIST_HEAD(RoutePrefix, static_route_prefixes);
-        LIST_HEAD(RoutingPolicyRule, rules);
-
-        unsigned n_static_addresses;
-        unsigned n_static_routes;
-        unsigned n_static_nexthops;
-        unsigned n_static_fdb_entries;
-        unsigned n_static_mdb_entries;
-        unsigned n_ipv6_proxy_ndp_addresses;
-        unsigned n_neighbors;
-        unsigned n_address_labels;
-        unsigned n_static_prefixes;
-        unsigned n_static_route_prefixes;
-        unsigned n_rules;
-
-        Hashmap *addresses_by_section;
+        OrderedHashmap *addresses_by_section;
         Hashmap *routes_by_section;
         Hashmap *nexthops_by_section;
         Hashmap *fdb_entries_by_section;
@@ -360,8 +320,6 @@ bool network_has_static_ipv6_configurations(Network *network);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_stacked_netdev);
 CONFIG_PARSER_PROTOTYPE(config_parse_tunnel);
-CONFIG_PARSER_PROTOTYPE(config_parse_ipv6token);
-CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_privacy_extensions);
 CONFIG_PARSER_PROTOTYPE(config_parse_domains);
 CONFIG_PARSER_PROTOTYPE(config_parse_dns);
 CONFIG_PARSER_PROTOTYPE(config_parse_hostname);
@@ -374,9 +332,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode);
 
 const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
 
-const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_;
-IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;
-
 const char* keep_configuration_to_string(KeepConfiguration i) _const_;
 KeepConfiguration keep_configuration_from_string(const char *s) _pure_;
 
index 6d89be1a25d55a48f48d682959289b9a9c26967d..6edeaabf88afa0f2e76f92bbc6dea938432d7d8f 100644 (file)
@@ -5,17 +5,37 @@
 #include <linux/nexthop.h>
 
 #include "alloc-util.h"
-#include "conf-parser.h"
-#include "in-addr-util.h"
 #include "netlink-util.h"
+#include "networkd-link.h"
 #include "networkd-manager.h"
+#include "networkd-network.h"
 #include "networkd-nexthop.h"
 #include "parse-util.h"
 #include "set.h"
 #include "string-util.h"
-#include "util.h"
 
-int nexthop_new(NextHop **ret) {
+NextHop *nexthop_free(NextHop *nexthop) {
+        if (!nexthop)
+                return NULL;
+
+        if (nexthop->network) {
+                assert(nexthop->section);
+                hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section);
+        }
+
+        network_config_section_free(nexthop->section);
+
+        if (nexthop->link) {
+                set_remove(nexthop->link->nexthops, nexthop);
+                set_remove(nexthop->link->nexthops_foreign, nexthop);
+        }
+
+        return mfree(nexthop);
+}
+
+DEFINE_NETWORK_SECTION_FUNCTIONS(NextHop, nexthop_free);
+
+static int nexthop_new(NextHop **ret) {
         _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
 
         nexthop = new(NextHop, 1);
@@ -38,19 +58,17 @@ static int nexthop_new_static(Network *network, const char *filename, unsigned s
 
         assert(network);
         assert(ret);
-        assert(!!filename == (section_line > 0));
-
-        if (filename) {
-                r = network_config_section_new(filename, section_line, &n);
-                if (r < 0)
-                        return r;
+        assert(filename);
+        assert(section_line > 0);
 
-                nexthop = hashmap_get(network->nexthops_by_section, n);
-                if (nexthop) {
-                        *ret = TAKE_PTR(nexthop);
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
 
-                        return 0;
-                }
+        nexthop = hashmap_get(network->nexthops_by_section, n);
+        if (nexthop) {
+                *ret = TAKE_PTR(nexthop);
+                return 0;
         }
 
         r = nexthop_new(&nexthop);
@@ -59,55 +77,24 @@ static int nexthop_new_static(Network *network, const char *filename, unsigned s
 
         nexthop->protocol = RTPROT_STATIC;
         nexthop->network = network;
-        LIST_PREPEND(nexthops, network->static_nexthops, nexthop);
-        network->n_static_nexthops++;
+        nexthop->section = TAKE_PTR(n);
 
-        if (filename) {
-                nexthop->section = TAKE_PTR(n);
-
-                r = hashmap_ensure_allocated(&network->nexthops_by_section, &network_config_hash_ops);
-                if (r < 0)
-                        return r;
+        r = hashmap_ensure_allocated(&network->nexthops_by_section, &network_config_hash_ops);
+        if (r < 0)
+                return r;
 
-                r = hashmap_put(network->nexthops_by_section, nexthop->section, nexthop);
-                if (r < 0)
-                        return r;
-        }
+        r = hashmap_put(network->nexthops_by_section, nexthop->section, nexthop);
+        if (r < 0)
+                return r;
 
         *ret = TAKE_PTR(nexthop);
-
         return 0;
 }
 
-void nexthop_free(NextHop *nexthop) {
-        if (!nexthop)
-                return;
-
-        if (nexthop->network) {
-                LIST_REMOVE(nexthops, nexthop->network->static_nexthops, nexthop);
-
-                assert(nexthop->network->n_static_nexthops > 0);
-                nexthop->network->n_static_nexthops--;
-
-                if (nexthop->section)
-                        hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section);
-        }
-
-        network_config_section_free(nexthop->section);
-
-        if (nexthop->link) {
-                set_remove(nexthop->link->nexthops, nexthop);
-                set_remove(nexthop->link->nexthops_foreign, nexthop);
-        }
-
-        free(nexthop);
-}
-
 static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) {
         assert(nexthop);
 
         siphash24_compress(&nexthop->id, sizeof(nexthop->id), state);
-        siphash24_compress(&nexthop->oif, sizeof(nexthop->oif), state);
         siphash24_compress(&nexthop->family, sizeof(nexthop->family), state);
 
         switch (nexthop->family) {
@@ -129,27 +116,14 @@ static int nexthop_compare_func(const NextHop *a, const NextHop *b) {
         if (r != 0)
                 return r;
 
-        r = CMP(a->oif, b->oif);
-        if (r != 0)
-                return r;
-
         r = CMP(a->family, b->family);
         if (r != 0)
                 return r;
 
-        switch (a->family) {
-        case AF_INET:
-        case AF_INET6:
-
-                r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
-                if (r != 0)
-                        return r;
+        if (IN_SET(a->family, AF_INET, AF_INET6))
+                return memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
 
-                return 0;
-        default:
-                /* treat any other address family as AF_UNSPEC */
-                return 0;
-        }
+        return 0;
 }
 
 DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
@@ -159,17 +133,7 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
                 nexthop_compare_func,
                 nexthop_free);
 
-bool nexthop_equal(NextHop *r1, NextHop *r2) {
-        if (r1 == r2)
-                return true;
-
-        if (!r1 || !r2)
-                return false;
-
-        return nexthop_compare_func(r1, r2) == 0;
-}
-
-int nexthop_get(Link *link, NextHop *in, NextHop **ret) {
+static int nexthop_get(Link *link, NextHop *in, NextHop **ret) {
         NextHop *existing;
 
         assert(link);
@@ -205,7 +169,6 @@ static int nexthop_add_internal(Link *link, Set **nexthops, NextHop *in, NextHop
                 return r;
 
         nexthop->id = in->id;
-        nexthop->oif = in->oif;
         nexthop->family = in->family;
         nexthop->gw = in->gw;
 
@@ -225,11 +188,11 @@ static int nexthop_add_internal(Link *link, Set **nexthops, NextHop *in, NextHop
         return 0;
 }
 
-int nexthop_add_foreign(Link *link, NextHop *in, NextHop **ret) {
+static int nexthop_add_foreign(Link *link, NextHop *in, NextHop **ret) {
         return nexthop_add_internal(link, &link->nexthops_foreign, in, ret);
 }
 
-int nexthop_add(Link *link, NextHop *in, NextHop **ret) {
+static int nexthop_add(Link *link, NextHop *in, NextHop **ret) {
         NextHop *nexthop;
         int r;
 
@@ -258,71 +221,34 @@ int nexthop_add(Link *link, NextHop *in, NextHop **ret) {
         return 0;
 }
 
-static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
-        assert(m);
         assert(link);
-        assert(link->ifname);
+        assert(link->nexthop_messages > 0);
+
+        link->nexthop_messages--;
 
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                 return 1;
 
         r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -ESRCH)
-                log_link_message_warning_errno(link, m, r, "Could not drop nexthop, ignoring");
-
-        return 1;
-}
-
-int nexthop_remove(NextHop *nexthop, Link *link,
-                   link_netlink_message_handler_t callback) {
-
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
-        int r;
-
-        assert(link);
-        assert(link->manager);
-        assert(link->manager->rtnl);
-        assert(link->ifindex > 0);
-        assert(IN_SET(nexthop->family, AF_INET, AF_INET6));
-
-        r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &req,
-                                      RTM_DELNEXTHOP, nexthop->family,
-                                      nexthop->protocol);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not create RTM_DELNEXTHOP message: %m");
-
-        if (DEBUG_LOGGING) {
-                _cleanup_free_ char *gw = NULL;
-
-                if (!in_addr_is_null(nexthop->family, &nexthop->gw))
-                        (void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw);
-
-                log_link_debug(link, "Removing nexthop: gw: %s", strna(gw));
+        if (r < 0 && r != -EEXIST) {
+                log_link_message_warning_errno(link, m, r, "Could not set nexthop");
+                link_enter_failed(link);
+                return 1;
         }
 
-        if (in_addr_is_null(nexthop->family, &nexthop->gw) == 0) {
-                r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, nexthop->family, &nexthop->gw);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
+        if (link->nexthop_messages == 0) {
+                log_link_debug(link, "Nexthop set");
+                link->static_nexthops_configured = true;
+                link_check_ready(link);
         }
 
-        r = netlink_call_async(link->manager->rtnl, NULL, req,
-                               callback ?: nexthop_remove_handler,
-                               link_netlink_destroy_callback, link);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
-
-        link_ref(link);
-
-        return 0;
+        return 1;
 }
 
-int nexthop_configure(
-                NextHop *nexthop,
-                Link *link,
-                link_netlink_message_handler_t callback) {
+static int nexthop_configure(NextHop *nexthop, Link *link) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         int r;
 
@@ -331,7 +257,6 @@ int nexthop_configure(
         assert(link->manager->rtnl);
         assert(link->ifindex > 0);
         assert(IN_SET(nexthop->family, AF_INET, AF_INET6));
-        assert(callback);
 
         if (DEBUG_LOGGING) {
                 _cleanup_free_ char *gw = NULL;
@@ -366,7 +291,7 @@ int nexthop_configure(
                         return log_link_error_errno(link, r, "Could not set nexthop family: %m");
         }
 
-        r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
+        r = netlink_call_async(link->manager->rtnl, NULL, req, nexthop_handler,
                                link_netlink_destroy_callback, link);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
@@ -380,7 +305,141 @@ int nexthop_configure(
         return 1;
 }
 
-int nexthop_section_verify(NextHop *nh) {
+int link_set_nexthop(Link *link) {
+        NextHop *nh;
+        int r;
+
+        assert(link);
+        assert(link->network);
+
+        link->static_nexthops_configured = false;
+
+        HASHMAP_FOREACH(nh, link->network->nexthops_by_section) {
+                r = nexthop_configure(nh, link);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Could not set nexthop: %m");
+
+                link->nexthop_messages++;
+        }
+
+        if (link->nexthop_messages == 0) {
+                link->static_nexthops_configured = true;
+                link_check_ready(link);
+        } else {
+                log_link_debug(link, "Setting nexthop");
+                link_set_state(link, LINK_STATE_CONFIGURING);
+        }
+
+        return 1;
+}
+
+int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
+        _cleanup_(nexthop_freep) NextHop *tmp = NULL;
+        _cleanup_free_ char *gateway = NULL;
+        NextHop *nexthop = NULL;
+        uint32_t ifindex;
+        uint16_t type;
+        Link *link;
+        int r;
+
+        assert(rtnl);
+        assert(message);
+        assert(m);
+
+        if (sd_netlink_message_is_error(message)) {
+                r = sd_netlink_message_get_errno(message);
+                if (r < 0)
+                        log_message_warning_errno(message, r, "rtnl: failed to receive rule message, ignoring");
+
+                return 0;
+        }
+
+        r = sd_netlink_message_get_type(message, &type);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
+                return 0;
+        } else if (!IN_SET(type, RTM_NEWNEXTHOP, RTM_DELNEXTHOP)) {
+                log_warning("rtnl: received unexpected message type %u when processing nexthop, ignoring.", type);
+                return 0;
+        }
+
+        r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex);
+        if (r == -ENODATA) {
+                log_warning_errno(r, "rtnl: received nexthop message without NHA_OIF attribute, ignoring: %m");
+                return 0;
+        } else if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
+                return 0;
+        } else if (ifindex <= 0) {
+                log_warning("rtnl: received nexthop message with invalid ifindex %"PRIu32", ignoring.", ifindex);
+                return 0;
+        }
+
+        r = link_get(m, ifindex, &link);
+        if (r < 0 || !link) {
+                if (!m->enumerating)
+                        log_warning("rtnl: received nexthop message for link (%"PRIu32") we do not know about, ignoring", ifindex);
+                return 0;
+        }
+
+        r = nexthop_new(&tmp);
+        if (r < 0)
+                return log_oom();
+
+        r = sd_rtnl_message_get_family(message, &tmp->family);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: could not get nexthop family, ignoring: %m");
+                return 0;
+        } else if (!IN_SET(tmp->family, AF_INET, AF_INET6))
+                return log_link_debug(link, "rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family);
+
+        r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, tmp->family, &tmp->gw);
+        if (r < 0 && r != -ENODATA) {
+                log_link_warning_errno(link, r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id);
+        if (r < 0 && r != -ENODATA) {
+                log_link_warning_errno(link, r, "rtnl: could not get NHA_ID attribute, ignoring: %m");
+                return 0;
+        }
+
+        (void) nexthop_get(link, tmp, &nexthop);
+
+        if (DEBUG_LOGGING)
+                (void) in_addr_to_string(tmp->family, &tmp->gw, &gateway);
+
+        switch (type) {
+        case RTM_NEWNEXTHOP:
+                if (nexthop)
+                        log_link_debug(link, "Received remembered nexthop: %s, id: %d", strna(gateway), tmp->id);
+                else {
+                        log_link_debug(link, "Remembering foreign nexthop: %s, id: %d", strna(gateway), tmp->id);
+                        r = nexthop_add_foreign(link, tmp, &nexthop);
+                        if (r < 0) {
+                                log_link_warning_errno(link, r, "Could not remember foreign nexthop, ignoring: %m");
+                                return 0;
+                        }
+                }
+                break;
+        case RTM_DELNEXTHOP:
+                if (nexthop) {
+                        log_link_debug(link, "Forgetting nexthop: %s, id: %d", strna(gateway), tmp->id);
+                        nexthop_free(nexthop);
+                } else
+                        log_link_debug(link, "Kernel removed a nexthop we don't remember: %s, id: %d, ignoring.",
+                                       strna(gateway), tmp->id);
+                break;
+
+        default:
+                assert_not_reached("Received invalid RTNL message type");
+        }
+
+        return 1;
+}
+
+static int nexthop_section_verify(NextHop *nh) {
         if (section_is_invalid(nh->section))
                 return -EINVAL;
 
@@ -390,6 +449,16 @@ int nexthop_section_verify(NextHop *nh) {
         return 0;
 }
 
+void network_drop_invalid_nexthops(Network *network) {
+        NextHop *nh;
+
+        assert(network);
+
+        HASHMAP_FOREACH(nh, network->nexthops_by_section)
+                if (nexthop_section_verify(nh) < 0)
+                        nexthop_free(nh);
+}
+
 int config_parse_nexthop_id(
                 const char *unit,
                 const char *filename,
index 28cbdad738e770e42c56b31914932a3407f325f9..3cdb068efd73f9605785b86f4ee785db15de92e1 100644 (file)
@@ -4,16 +4,19 @@
 
 #pragma once
 
-#include "conf-parser.h"
-#include "macro.h"
+#include <inttypes.h>
 
-typedef struct NextHop NextHop;
-typedef struct NetworkConfigSection NetworkConfigSection;
+#include "sd-netlink.h"
 
-#include "networkd-network.h"
+#include "conf-parser.h"
+#include "in-addr-util.h"
 #include "networkd-util.h"
 
-struct NextHop {
+typedef struct Link Link;
+typedef struct Manager Manager;
+typedef struct Network Network;
+
+typedef struct NextHop {
         Network *network;
         NetworkConfigSection *section;
 
@@ -21,30 +24,18 @@ struct NextHop {
 
         unsigned char protocol;
 
-        int family;
-        uint32_t oif;
         uint32_t id;
-
+        int family;
         union in_addr_union gw;
+} NextHop;
 
-        LIST_FIELDS(NextHop, nexthops);
-};
-
-extern const struct hash_ops nexthop_hash_ops;
-
-int nexthop_new(NextHop **ret);
-void nexthop_free(NextHop *nexthop);
-int nexthop_configure(NextHop *nexthop, Link *link, link_netlink_message_handler_t callback);
-int nexthop_remove(NextHop *nexthop, Link *link, link_netlink_message_handler_t callback);
+NextHop *nexthop_free(NextHop *nexthop);
 
-int nexthop_get(Link *link, NextHop *in, NextHop **ret);
-int nexthop_add(Link *link, NextHop *in, NextHop **ret);
-int nexthop_add_foreign(Link *link, NextHop *in, NextHop **ret);
-bool nexthop_equal(NextHop *r1, NextHop *r2);
+void network_drop_invalid_nexthops(Network *network);
 
-int nexthop_section_verify(NextHop *nexthop);
+int link_set_nexthop(Link *link);
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(NextHop, nexthop_free);
+int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_id);
 CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_gateway);
index 003a50b68b06ff2c727c0772d73b754a22a6e9ee..1167f2865f61d44202cae9499fa5f00daa2997b1 100644 (file)
@@ -7,35 +7,32 @@
 #include <arpa/inet.h>
 
 #include "dns-domain.h"
-#include "networkd-address.h"
+#include "networkd-link.h"
 #include "networkd-manager.h"
+#include "networkd-network.h"
 #include "networkd-radv.h"
 #include "parse-util.h"
-#include "sd-radv.h"
 #include "string-util.h"
 #include "string-table.h"
 #include "strv.h"
 
-void prefix_free(Prefix *prefix) {
+Prefix *prefix_free(Prefix *prefix) {
         if (!prefix)
-                return;
+                return NULL;
 
         if (prefix->network) {
-                LIST_REMOVE(prefixes, prefix->network->static_prefixes, prefix);
-                assert(prefix->network->n_static_prefixes > 0);
-                prefix->network->n_static_prefixes--;
-
-                if (prefix->section)
-                        hashmap_remove(prefix->network->prefixes_by_section,
-                                       prefix->section);
+                assert(prefix->section);
+                hashmap_remove(prefix->network->prefixes_by_section, prefix->section);
         }
 
         network_config_section_free(prefix->section);
         sd_radv_prefix_unref(prefix->radv_prefix);
 
-        free(prefix);
+        return mfree(prefix);
 }
 
+DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, prefix_free);
+
 static int prefix_new(Prefix **ret) {
         _cleanup_(prefix_freep) Prefix *prefix = NULL;
 
@@ -51,29 +48,24 @@ static int prefix_new(Prefix **ret) {
         return 0;
 }
 
-static int prefix_new_static(Network *network, const char *filename,
-                             unsigned section_line, Prefix **ret) {
+static int prefix_new_static(Network *network, const char *filename, unsigned section_line, Prefix **ret) {
         _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
         _cleanup_(prefix_freep) Prefix *prefix = NULL;
         int r;
 
         assert(network);
         assert(ret);
-        assert(!!filename == (section_line > 0));
-
-        if (filename) {
-                r = network_config_section_new(filename, section_line, &n);
-                if (r < 0)
-                        return r;
+        assert(filename);
+        assert(section_line > 0);
 
-                if (section_line) {
-                        prefix = hashmap_get(network->prefixes_by_section, n);
-                        if (prefix) {
-                                *ret = TAKE_PTR(prefix);
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
 
-                                return 0;
-                        }
-                }
+        prefix = hashmap_get(network->prefixes_by_section, n);
+        if (prefix) {
+                *ret = TAKE_PTR(prefix);
+                return 0;
         }
 
         r = prefix_new(&prefix);
@@ -81,26 +73,38 @@ static int prefix_new_static(Network *network, const char *filename,
                 return r;
 
         prefix->network = network;
-        LIST_APPEND(prefixes, network->static_prefixes, prefix);
-        network->n_static_prefixes++;
+        prefix->section = TAKE_PTR(n);
 
-        if (filename) {
-                prefix->section = TAKE_PTR(n);
-
-                r = hashmap_ensure_allocated(&network->prefixes_by_section, &network_config_hash_ops);
-                if (r < 0)
-                        return r;
+        r = hashmap_ensure_allocated(&network->prefixes_by_section, &network_config_hash_ops);
+        if (r < 0)
+                return r;
 
-                r = hashmap_put(network->prefixes_by_section, prefix->section, prefix);
-                if (r < 0)
-                        return r;
-        }
+        r = hashmap_put(network->prefixes_by_section, prefix->section, prefix);
+        if (r < 0)
+                return r;
 
         *ret = TAKE_PTR(prefix);
 
         return 0;
 }
 
+RoutePrefix *route_prefix_free(RoutePrefix *prefix) {
+        if (!prefix)
+                return NULL;
+
+        if (prefix->network) {
+                assert(prefix->section);
+                hashmap_remove(prefix->network->route_prefixes_by_section, prefix->section);
+        }
+
+        network_config_section_free(prefix->section);
+        sd_radv_route_prefix_unref(prefix->radv_route_prefix);
+
+        return mfree(prefix);
+}
+
+DEFINE_NETWORK_SECTION_FUNCTIONS(RoutePrefix, route_prefix_free);
+
 static int route_prefix_new(RoutePrefix **ret) {
         _cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL;
 
@@ -116,49 +120,24 @@ static int route_prefix_new(RoutePrefix **ret) {
         return 0;
 }
 
-void route_prefix_free(RoutePrefix *prefix) {
-        if (!prefix)
-                return;
-
-        if (prefix->network) {
-                LIST_REMOVE(route_prefixes, prefix->network->static_route_prefixes, prefix);
-                assert(prefix->network->n_static_route_prefixes > 0);
-                prefix->network->n_static_route_prefixes--;
-
-                if (prefix->section)
-                        hashmap_remove(prefix->network->route_prefixes_by_section,
-                                       prefix->section);
-        }
-
-        network_config_section_free(prefix->section);
-        sd_radv_route_prefix_unref(prefix->radv_route_prefix);
-
-        free(prefix);
-}
-
-static int route_prefix_new_static(Network *network, const char *filename,
-                                   unsigned section_line, RoutePrefix **ret) {
+static int route_prefix_new_static(Network *network, const char *filename, unsigned section_line, RoutePrefix **ret) {
         _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
         _cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL;
         int r;
 
         assert(network);
         assert(ret);
-        assert(!!filename == (section_line > 0));
-
-        if (filename) {
-                r = network_config_section_new(filename, section_line, &n);
-                if (r < 0)
-                        return r;
+        assert(filename);
+        assert(section_line > 0);
 
-                if (section_line) {
-                        prefix = hashmap_get(network->route_prefixes_by_section, n);
-                        if (prefix) {
-                                *ret = TAKE_PTR(prefix);
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
 
-                                return 0;
-                        }
-                }
+        prefix = hashmap_get(network->route_prefixes_by_section, n);
+        if (prefix) {
+                *ret = TAKE_PTR(prefix);
+                return 0;
         }
 
         r = route_prefix_new(&prefix);
@@ -166,36 +145,52 @@ static int route_prefix_new_static(Network *network, const char *filename,
                 return r;
 
         prefix->network = network;
-        LIST_APPEND(route_prefixes, network->static_route_prefixes, prefix);
-        network->n_static_route_prefixes++;
-
-        if (filename) {
-                prefix->section = TAKE_PTR(n);
+        prefix->section = TAKE_PTR(n);
 
-                r = hashmap_ensure_allocated(&network->route_prefixes_by_section, &network_config_hash_ops);
-                if (r < 0)
-                        return r;
+        r = hashmap_ensure_allocated(&network->route_prefixes_by_section, &network_config_hash_ops);
+        if (r < 0)
+                return r;
 
-                r = hashmap_put(network->route_prefixes_by_section, prefix->section, prefix);
-                if (r < 0)
-                        return r;
-        }
+        r = hashmap_put(network->route_prefixes_by_section, prefix->section, prefix);
+        if (r < 0)
+                return r;
 
         *ret = TAKE_PTR(prefix);
 
         return 0;
 }
 
-int config_parse_prefix(const char *unit,
-                        const char *filename,
-                        unsigned line,
-                        const char *section,
-                        unsigned section_line,
-                        const char *lvalue,
-                        int ltype,
-                        const char *rvalue,
-                        void *data,
-                        void *userdata) {
+void network_drop_invalid_prefixes(Network *network) {
+        Prefix *prefix;
+
+        assert(network);
+
+        HASHMAP_FOREACH(prefix, network->prefixes_by_section)
+                if (section_is_invalid(prefix->section))
+                        prefix_free(prefix);
+}
+
+void network_drop_invalid_route_prefixes(Network *network) {
+        RoutePrefix *prefix;
+
+        assert(network);
+
+        HASHMAP_FOREACH(prefix, network->route_prefixes_by_section)
+                if (section_is_invalid(prefix->section))
+                        route_prefix_free(prefix);
+}
+
+int config_parse_prefix(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         Network *network = userdata;
         _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
@@ -230,16 +225,18 @@ int config_parse_prefix(const char *unit,
         return 0;
 }
 
-int config_parse_prefix_flags(const char *unit,
-                              const char *filename,
-                              unsigned line,
-                              const char *section,
-                              unsigned section_line,
-                              const char *lvalue,
-                              int ltype,
-                              const char *rvalue,
-                              void *data,
-                              void *userdata) {
+int config_parse_prefix_flags(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
         Network *network = userdata;
         _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
         int r;
@@ -274,16 +271,18 @@ int config_parse_prefix_flags(const char *unit,
         return 0;
 }
 
-int config_parse_prefix_lifetime(const char *unit,
-                                 const char *filename,
-                                 unsigned line,
-                                 const char *section,
-                                 unsigned section_line,
-                                 const char *lvalue,
-                                 int ltype,
-                                 const char *rvalue,
-                                 void *data,
-                                 void *userdata) {
+int config_parse_prefix_lifetime(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
         Network *network = userdata;
         _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
         usec_t usec;
@@ -362,16 +361,17 @@ int config_parse_prefix_assign(
         return 0;
 }
 
-int config_parse_route_prefix(const char *unit,
-                              const char *filename,
-                              unsigned line,
-                              const char *section,
-                              unsigned section_line,
-                              const char *lvalue,
-                              int ltype,
-                              const char *rvalue,
-                              void *data,
-                              void *userdata) {
+int config_parse_route_prefix(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         Network *network = userdata;
         _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
@@ -406,16 +406,18 @@ int config_parse_route_prefix(const char *unit,
         return 0;
 }
 
-int config_parse_route_prefix_lifetime(const char *unit,
-                                       const char *filename,
-                                       unsigned line,
-                                       const char *section,
-                                       unsigned section_line,
-                                       const char *lvalue,
-                                       int ltype,
-                                       const char *rvalue,
-                                       void *data,
-                                       void *userdata) {
+int config_parse_route_prefix_lifetime(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
         Network *network = userdata;
         _cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
         usec_t usec;
@@ -451,16 +453,15 @@ int config_parse_route_prefix_lifetime(const char *unit,
         return 0;
 }
 
-static int radv_get_ip6dns(Network *network, struct in6_addr **dns,
-                           size_t *n_dns) {
+static int network_get_ipv6_dns(Network *network, struct in6_addr **ret_addresses, size_t *ret_size) {
         _cleanup_free_ struct in6_addr *addresses = NULL;
-        size_t i, n_addresses = 0, n_allocated = 0;
+        size_t n_addresses = 0, n_allocated = 0;
 
         assert(network);
-        assert(dns);
-        assert(n_dns);
+        assert(ret_addresses);
+        assert(ret_size);
 
-        for (i = 0; i < network->n_dns; i++) {
+        for (size_t i = 0; i < network->n_dns; i++) {
                 union in_addr_union *addr;
 
                 if (network->dns[i]->family != AF_INET6)
@@ -479,11 +480,8 @@ static int radv_get_ip6dns(Network *network, struct in6_addr **dns,
                 addresses[n_addresses++] = addr->in6;
         }
 
-        if (addresses) {
-                *dns = TAKE_PTR(addresses);
-
-                *n_dns = n_addresses;
-        }
+        *ret_addresses = TAKE_PTR(addresses);
+        *ret_size = n_addresses;
 
         return n_addresses;
 }
@@ -520,7 +518,7 @@ static int radv_set_dns(Link *link, Link *uplink) {
 
         lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
 
-        r = radv_get_ip6dns(link->network, &dns, &n_dns);
+        r = network_get_ipv6_dns(link->network, &dns, &n_dns);
         if (r > 0)
                 goto set_dns;
 
@@ -530,7 +528,7 @@ static int radv_set_dns(Link *link, Link *uplink) {
                         return 0;
                 }
 
-                r = radv_get_ip6dns(uplink->network, &dns, &n_dns);
+                r = network_get_ipv6_dns(uplink->network, &dns, &n_dns);
                 if (r > 0)
                         goto set_dns;
         }
@@ -604,19 +602,29 @@ int radv_emit_dns(Link *link) {
         return 0;
 }
 
+static bool link_radv_enabled(Link *link) {
+        assert(link);
+
+        if (!link_ipv6ll_enabled(link))
+                return false;
+
+        return link->network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE;
+}
+
 int radv_configure(Link *link) {
-        RoutePrefix *q;
-        Prefix *p;
         int r;
 
         assert(link);
         assert(link->network);
 
+        if (!link_radv_enabled(link))
+                return 0;
+
         r = sd_radv_new(&link->radv);
         if (r < 0)
                 return r;
 
-        r = sd_radv_attach_event(link->radv, NULL, 0);
+        r = sd_radv_attach_event(link->radv, link->manager->event, 0);
         if (r < 0)
                 return r;
 
@@ -651,7 +659,10 @@ int radv_configure(Link *link) {
         }
 
         if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC) {
-                LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
+                RoutePrefix *q;
+                Prefix *p;
+
+                HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
                         r = sd_radv_add_prefix(link->radv, p->radv_prefix, false);
                         if (r == -EEXIST)
                                 continue;
@@ -663,7 +674,7 @@ int radv_configure(Link *link) {
                                 return r;
                 }
 
-                LIST_FOREACH(route_prefixes, q, link->network->static_route_prefixes) {
+                HASHMAP_FOREACH(q, link->network->route_prefixes_by_section) {
                         r = sd_radv_add_route_prefix(link->radv, q->radv_route_prefix, false);
                         if (r == -EEXIST)
                                 continue;
@@ -675,8 +686,43 @@ int radv_configure(Link *link) {
         return 0;
 }
 
-int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len,
-                    uint32_t lifetime_preferred, uint32_t lifetime_valid) {
+int radv_update_mac(Link *link) {
+        bool restart;
+        int r;
+
+        assert(link);
+
+        if (!link->radv)
+                return 0;
+
+        restart = sd_radv_is_running(link->radv);
+
+        if (restart) {
+                r = sd_radv_stop(link->radv);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_radv_set_mac(link->radv, &link->mac);
+        if (r < 0)
+                return r;
+
+        if (restart) {
+                r = sd_radv_start(link->radv);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int radv_add_prefix(
+                Link *link,
+                const struct in6_addr *prefix,
+                uint8_t prefix_len,
+                uint32_t lifetime_preferred,
+                uint32_t lifetime_valid) {
+
         _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
         int r;
 
@@ -822,10 +868,10 @@ int config_parse_radv_search_domains(
 }
 
 static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = {
-        [RADV_PREFIX_DELEGATION_NONE] = "no",
+        [RADV_PREFIX_DELEGATION_NONE]   = "no",
         [RADV_PREFIX_DELEGATION_STATIC] = "static",
-        [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6",
-        [RADV_PREFIX_DELEGATION_BOTH] = "yes",
+        [RADV_PREFIX_DELEGATION_DHCP6]  = "dhcpv6",
+        [RADV_PREFIX_DELEGATION_BOTH]   = "yes",
 };
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(
@@ -833,21 +879,24 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(
                 RADVPrefixDelegation,
                 RADV_PREFIX_DELEGATION_BOTH);
 
-DEFINE_CONFIG_PARSE_ENUM(config_parse_router_prefix_delegation,
-                         radv_prefix_delegation,
-                         RADVPrefixDelegation,
-                         "Invalid router prefix delegation");
-
-int config_parse_router_preference(const char *unit,
-                                   const char *filename,
-                                   unsigned line,
-                                   const char *section,
-                                   unsigned section_line,
-                                   const char *lvalue,
-                                   int ltype,
-                                   const char *rvalue,
-                                   void *data,
-                                   void *userdata) {
+DEFINE_CONFIG_PARSE_ENUM(
+                config_parse_router_prefix_delegation,
+                radv_prefix_delegation,
+                RADVPrefixDelegation,
+                "Invalid router prefix delegation");
+
+int config_parse_router_preference(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
         Network *network = userdata;
 
         assert(filename);
index 496ef97adcf6cca844e2a0b821138d39e78ae243..fbb93a9accccc1b76894f639a97b546f05b06003 100644 (file)
@@ -5,13 +5,17 @@
   Copyright © 2017 Intel Corporation. All rights reserved.
 ***/
 
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "sd-radv.h"
+
+#include "in-addr-util.h"
 #include "conf-parser.h"
-#include "networkd-address.h"
-#include "networkd-link.h"
 #include "networkd-util.h"
 
-typedef struct Prefix Prefix;
-typedef struct RoutePrefix RoutePrefix;
+typedef struct Network Network;
+typedef struct Link Link;
 
 typedef enum RADVPrefixDelegation {
         RADV_PREFIX_DELEGATION_NONE   = 0,
@@ -22,36 +26,31 @@ typedef enum RADVPrefixDelegation {
         _RADV_PREFIX_DELEGATION_INVALID = -1,
 } RADVPrefixDelegation;
 
-struct Prefix {
+typedef struct Prefix {
         Network *network;
         NetworkConfigSection *section;
 
         sd_radv_prefix *radv_prefix;
 
         bool assign;
+} Prefix;
 
-        LIST_FIELDS(Prefix, prefixes);
-};
-
-struct RoutePrefix {
+typedef struct RoutePrefix {
         Network *network;
         NetworkConfigSection *section;
 
         sd_radv_route_prefix *radv_route_prefix;
+} RoutePrefix;
 
-        LIST_FIELDS(RoutePrefix, route_prefixes);
-};
-
-void prefix_free(Prefix *prefix);
-
-DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, prefix_free);
-
-void route_prefix_free(RoutePrefix *prefix);
+Prefix *prefix_free(Prefix *prefix);
+RoutePrefix *route_prefix_free(RoutePrefix *prefix);
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(RoutePrefix, route_prefix_free);
+void network_drop_invalid_prefixes(Network *network);
+void network_drop_invalid_route_prefixes(Network *network);
 
 int radv_emit_dns(Link *link);
 int radv_configure(Link *link);
+int radv_update_mac(Link *link);
 int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len,
                     uint32_t lifetime_preferred, uint32_t lifetime_valid);
 
index 2610b24c82a787d4f62389dc2a52fa3110b904d6..659fecbf2e5a4edcb61c7a3098eb5e01365ad0fd 100644 (file)
@@ -3,16 +3,14 @@
 #include <linux/icmpv6.h>
 
 #include "alloc-util.h"
-#include "conf-parser.h"
-#include "in-addr-util.h"
-#include "missing_network.h"
 #include "netlink-util.h"
 #include "networkd-ipv4ll.h"
 #include "networkd-manager.h"
-#include "networkd-ndisc.h"
+#include "networkd-network.h"
+#include "networkd-nexthop.h"
 #include "networkd-route.h"
+#include "networkd-routing-policy-rule.h"
 #include "parse-util.h"
-#include "set.h"
 #include "socket-netlink.h"
 #include "string-table.h"
 #include "string-util.h"
 
 #define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
 
+static uint32_t link_get_vrf_table(Link *link) {
+        return link->network->vrf ? VRF(link->network->vrf)->table : RT_TABLE_MAIN;
+}
+
+uint32_t link_get_dhcp_route_table(Link *link) {
+        /* When the interface is part of an VRF use the VRFs routing table, unless
+         * another table is explicitly specified. */
+        if (link->network->dhcp_route_table_set)
+                return link->network->dhcp_route_table;
+        return link_get_vrf_table(link);
+}
+
+uint32_t link_get_ipv6_accept_ra_route_table(Link *link) {
+        if (link->network->ipv6_accept_ra_route_table_set)
+                return link->network->ipv6_accept_ra_route_table;
+        return link_get_vrf_table(link);
+}
+
+static const char * const route_type_table[__RTN_MAX] = {
+        [RTN_UNICAST]     = "unicast",
+        [RTN_LOCAL]       = "local",
+        [RTN_BROADCAST]   = "broadcast",
+        [RTN_ANYCAST]     = "anycast",
+        [RTN_MULTICAST]   = "multicast",
+        [RTN_BLACKHOLE]   = "blackhole",
+        [RTN_UNREACHABLE] = "unreachable",
+        [RTN_PROHIBIT]    = "prohibit",
+        [RTN_THROW]       = "throw",
+        [RTN_NAT]         = "nat",
+        [RTN_XRESOLVE]    = "xresolve",
+};
+
+assert_cc(__RTN_MAX <= UCHAR_MAX);
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_type, int);
+
+static const char * const route_scope_table[] = {
+        [RT_SCOPE_UNIVERSE] = "global",
+        [RT_SCOPE_SITE]     = "site",
+        [RT_SCOPE_LINK]     = "link",
+        [RT_SCOPE_HOST]     = "host",
+        [RT_SCOPE_NOWHERE]  = "nowhere",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_scope, int);
+
+#define ROUTE_SCOPE_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("nowhere") + 1)
+static const char *format_route_scope(int scope, char *buf, size_t size) {
+        const char *s;
+        char *p = buf;
+
+        s = route_scope_to_string(scope);
+        if (s)
+                strpcpy(&p, size, s);
+        else
+                strpcpyf(&p, size, "%d", scope);
+
+        return buf;
+}
+
+static const char * const route_table_table[] = {
+        [RT_TABLE_DEFAULT] = "default",
+        [RT_TABLE_MAIN]    = "main",
+        [RT_TABLE_LOCAL]   = "local",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table, int);
+
+#define ROUTE_TABLE_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("default") + 1)
+static const char *format_route_table(int table, char *buf, size_t size) {
+        const char *s;
+        char *p = buf;
+
+        s = route_table_to_string(table);
+        if (s)
+                strpcpy(&p, size, s);
+        else
+                strpcpyf(&p, size, "%d", table);
+
+        return buf;
+}
+
+static const char * const route_protocol_table[] = {
+        [RTPROT_KERNEL] = "kernel",
+        [RTPROT_BOOT]   = "boot",
+        [RTPROT_STATIC] = "static",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(route_protocol, int);
+
+static const char * const route_protocol_full_table[] = {
+        [RTPROT_REDIRECT] = "redirect",
+        [RTPROT_KERNEL]   = "kernel",
+        [RTPROT_BOOT]     = "boot",
+        [RTPROT_STATIC]   = "static",
+        [RTPROT_GATED]    = "gated",
+        [RTPROT_RA]       = "ra",
+        [RTPROT_MRT]      = "mrt",
+        [RTPROT_ZEBRA]    = "zebra",
+        [RTPROT_BIRD]     = "bird",
+        [RTPROT_DNROUTED] = "dnrouted",
+        [RTPROT_XORP]     = "xorp",
+        [RTPROT_NTK]      = "ntk",
+        [RTPROT_DHCP]     = "dhcp",
+        [RTPROT_MROUTED]  = "mrouted",
+        [RTPROT_BABEL]    = "babel",
+        [RTPROT_BGP]      = "bgp",
+        [RTPROT_ISIS]     = "isis",
+        [RTPROT_OSPF]     = "ospf",
+        [RTPROT_RIP]      = "rip",
+        [RTPROT_EIGRP]    = "eigrp",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(route_protocol_full, int);
+
+#define ROUTE_PROTOCOL_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("redirect") + 1)
+static const char *format_route_protocol(int protocol, char *buf, size_t size) {
+        const char *s;
+        char *p = buf;
+
+        s = route_protocol_full_to_string(protocol);
+        if (s)
+                strpcpy(&p, size, s);
+        else
+                strpcpyf(&p, size, "%d", protocol);
+
+        return buf;
+}
+
 static unsigned routes_max(void) {
         static thread_local unsigned cached = 0;
 
@@ -82,22 +208,20 @@ static int route_new_static(Network *network, const char *filename, unsigned sec
 
         assert(network);
         assert(ret);
-        assert(!!filename == (section_line > 0));
-
-        if (filename) {
-                r = network_config_section_new(filename, section_line, &n);
-                if (r < 0)
-                        return r;
+        assert(filename);
+        assert(section_line > 0);
 
-                route = hashmap_get(network->routes_by_section, n);
-                if (route) {
-                        *ret = TAKE_PTR(route);
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
 
-                        return 0;
-                }
+        route = hashmap_get(network->routes_by_section, n);
+        if (route) {
+                *ret = TAKE_PTR(route);
+                return 0;
         }
 
-        if (network->n_static_routes >= routes_max())
+        if (hashmap_size(network->routes_by_section) >= routes_max())
                 return -E2BIG;
 
         r = route_new(&route);
@@ -106,38 +230,27 @@ static int route_new_static(Network *network, const char *filename, unsigned sec
 
         route->protocol = RTPROT_STATIC;
         route->network = network;
-        LIST_PREPEND(routes, network->static_routes, route);
-        network->n_static_routes++;
-
-        if (filename) {
-                route->section = TAKE_PTR(n);
+        route->section = TAKE_PTR(n);
 
-                r = hashmap_ensure_allocated(&network->routes_by_section, &network_config_hash_ops);
-                if (r < 0)
-                        return r;
+        r = hashmap_ensure_allocated(&network->routes_by_section, &network_config_hash_ops);
+        if (r < 0)
+                return r;
 
-                r = hashmap_put(network->routes_by_section, route->section, route);
-                if (r < 0)
-                        return r;
-        }
+        r = hashmap_put(network->routes_by_section, route->section, route);
+        if (r < 0)
+                return r;
 
         *ret = TAKE_PTR(route);
-
         return 0;
 }
 
-void route_free(Route *route) {
+Route *route_free(Route *route) {
         if (!route)
-                return;
+                return NULL;
 
         if (route->network) {
-                LIST_REMOVE(routes, route->network->static_routes, route);
-
-                assert(route->network->n_static_routes > 0);
-                route->network->n_static_routes--;
-
-                if (route->section)
-                        hashmap_remove(route->network->routes_by_section, route->section);
+                assert(route->section);
+                hashmap_remove(route->network->routes_by_section, route->section);
         }
 
         network_config_section_free(route->section);
@@ -162,7 +275,7 @@ void route_free(Route *route) {
 
         sd_event_source_unref(route->expire);
 
-        free(route);
+        return mfree(route);
 }
 
 void route_hash_func(const Route *route, struct siphash *state) {
@@ -280,7 +393,7 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
                 route_compare_func,
                 route_free);
 
-bool route_equal(Route *r1, Route *r2) {
+static bool route_equal(Route *r1, Route *r2) {
         if (r1 == r2)
                 return true;
 
@@ -290,7 +403,7 @@ bool route_equal(Route *r1, Route *r2) {
         return route_compare_func(r1, r2) == 0;
 }
 
-int route_get(Link *link, Route *in, Route **ret) {
+static int route_get(Link *link, Route *in, Route **ret) {
 
         Route *existing;
 
@@ -360,11 +473,11 @@ static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret)
         return 0;
 }
 
-int route_add_foreign(Link *link, Route *in, Route **ret) {
+static int route_add_foreign(Link *link, Route *in, Route **ret) {
         return route_add_internal(link, &link->routes_foreign, in, ret);
 }
 
-int route_add(Link *link, Route *in, Route **ret) {
+static int route_add(Link *link, Route *in, Route **ret) {
 
         Route *route;
         int r;
@@ -509,7 +622,81 @@ int route_remove(Route *route, Link *link,
         return 0;
 }
 
-int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
+static bool link_is_static_route_configured(Link *link, Route *route) {
+        Route *net_route;
+
+        assert(link);
+        assert(route);
+
+        if (!link->network)
+                return false;
+
+        HASHMAP_FOREACH(net_route, link->network->routes_by_section)
+                if (route_equal(net_route, route))
+                        return true;
+
+        return false;
+}
+
+int link_drop_foreign_routes(Link *link) {
+        Route *route;
+        int k, r = 0;
+
+        assert(link);
+
+        SET_FOREACH(route, link->routes_foreign) {
+                /* do not touch routes managed by the kernel */
+                if (route->protocol == RTPROT_KERNEL)
+                        continue;
+
+                /* do not touch multicast route added by kernel */
+                /* FIXME: Why the kernel adds this route with protocol RTPROT_BOOT??? We need to investigate that.
+                 * https://tools.ietf.org/html/rfc4862#section-5.4 may explain why. */
+                if (route->protocol == RTPROT_BOOT &&
+                    route->family == AF_INET6 &&
+                    route->dst_prefixlen == 8 &&
+                    in_addr_equal(AF_INET6, &route->dst, &(union in_addr_union) { .in6 = {{{ 0xff,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }}} }))
+                        continue;
+
+                if (route->protocol == RTPROT_STATIC && link->network &&
+                    FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
+                        continue;
+
+                if (route->protocol == RTPROT_DHCP && link->network &&
+                    FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
+                        continue;
+
+                if (link_is_static_route_configured(link, route))
+                        k = route_add(link, route, NULL);
+                else
+                        k = route_remove(route, link, NULL);
+                if (k < 0 && r >= 0)
+                        r = k;
+        }
+
+        return r;
+}
+
+int link_drop_routes(Link *link) {
+        Route *route;
+        int k, r = 0;
+
+        assert(link);
+
+        SET_FOREACH(route, link->routes) {
+                /* do not touch routes managed by the kernel */
+                if (route->protocol == RTPROT_KERNEL)
+                        continue;
+
+                k = route_remove(route, link, NULL);
+                if (k < 0 && r >= 0)
+                        r = k;
+        }
+
+        return r;
+}
+
+static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
         Route *route = userdata;
         int r;
 
@@ -821,8 +1008,437 @@ int route_configure(
         return 1;
 }
 
+static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(link);
+        assert(link->route_messages > 0);
+        assert(IN_SET(link->state, LINK_STATE_CONFIGURING,
+                      LINK_STATE_FAILED, LINK_STATE_LINGER));
+
+        link->route_messages--;
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EEXIST) {
+                log_link_message_warning_errno(link, m, r, "Could not set route");
+                link_enter_failed(link);
+                return 1;
+        }
+
+        if (link->route_messages == 0) {
+                log_link_debug(link, "Routes set");
+                link->static_routes_configured = true;
+                link_set_nexthop(link);
+        }
+
+        return 1;
+}
+
+int link_set_routes(Link *link) {
+        enum {
+                PHASE_NON_GATEWAY, /* First phase: Routes without a gateway */
+                PHASE_GATEWAY,     /* Second phase: Routes with a gateway */
+                _PHASE_MAX
+        } phase;
+        Route *rt;
+        int r;
+
+        assert(link);
+        assert(link->network);
+        assert(link->state != _LINK_STATE_INVALID);
+
+        link->static_routes_configured = false;
+
+        if (!link->addresses_ready)
+                return 0;
+
+        if (!link_has_carrier(link) && !link->network->configure_without_carrier)
+                /* During configuring addresses, the link lost its carrier. As networkd is dropping
+                 * the addresses now, let's not configure the routes either. */
+                return 0;
+
+        r = link_set_routing_policy_rules(link);
+        if (r < 0)
+                return r;
+
+        /* First add the routes that enable us to talk to gateways, then add in the others that need a gateway. */
+        for (phase = 0; phase < _PHASE_MAX; phase++)
+                HASHMAP_FOREACH(rt, link->network->routes_by_section) {
+                        if (rt->gateway_from_dhcp)
+                                continue;
+
+                        if ((in_addr_is_null(rt->family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY))
+                                continue;
+
+                        r = route_configure(rt, link, route_handler, NULL);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not set routes: %m");
+                        if (r > 0)
+                                link->route_messages++;
+                }
+
+        if (link->route_messages == 0) {
+                link->static_routes_configured = true;
+                link_set_nexthop(link);
+        } else {
+                log_link_debug(link, "Setting routes");
+                link_set_state(link, LINK_STATE_CONFIGURING);
+        }
+
+        return 0;
+}
+
+int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
+        _cleanup_(route_freep) Route *tmp = NULL;
+        Route *route = NULL;
+        Link *link = NULL;
+        uint32_t ifindex;
+        uint16_t type;
+        unsigned char table;
+        int r;
+
+        assert(rtnl);
+        assert(message);
+        assert(m);
+
+        if (sd_netlink_message_is_error(message)) {
+                r = sd_netlink_message_get_errno(message);
+                if (r < 0)
+                        log_message_warning_errno(message, r, "rtnl: failed to receive route message, ignoring");
+
+                return 0;
+        }
+
+        r = sd_netlink_message_get_type(message, &type);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
+                return 0;
+        } else if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) {
+                log_warning("rtnl: received unexpected message type %u when processing route, ignoring.", type);
+                return 0;
+        }
+
+        r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
+        if (r == -ENODATA) {
+                log_debug("rtnl: received route message without ifindex, ignoring");
+                return 0;
+        } else if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m");
+                return 0;
+        } else if (ifindex <= 0) {
+                log_warning("rtnl: received route message with invalid ifindex %d, ignoring.", ifindex);
+                return 0;
+        }
+
+        r = link_get(m, ifindex, &link);
+        if (r < 0 || !link) {
+                /* when enumerating we might be out of sync, but we will
+                 * get the route again, so just ignore it */
+                if (!m->enumerating)
+                        log_warning("rtnl: received route message for link (%d) we do not know about, ignoring", ifindex);
+                return 0;
+        }
+
+        r = route_new(&tmp);
+        if (r < 0)
+                return log_oom();
+
+        r = sd_rtnl_message_route_get_family(message, &tmp->family);
+        if (r < 0) {
+                log_link_warning(link, "rtnl: received route message without family, ignoring");
+                return 0;
+        } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
+                log_link_debug(link, "rtnl: received route message with invalid family '%i', ignoring", tmp->family);
+                return 0;
+        }
+
+        r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: received route message without route protocol: %m");
+                return 0;
+        }
+
+        switch (tmp->family) {
+        case AF_INET:
+                r = sd_netlink_message_read_in_addr(message, RTA_DST, &tmp->dst.in);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m");
+                        return 0;
+                }
+
+                r = sd_netlink_message_read_in_addr(message, RTA_GATEWAY, &tmp->gw.in);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
+                        return 0;
+                }
+
+                r = sd_netlink_message_read_in_addr(message, RTA_SRC, &tmp->src.in);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m");
+                        return 0;
+                }
+
+                r = sd_netlink_message_read_in_addr(message, RTA_PREFSRC, &tmp->prefsrc.in);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m");
+                        return 0;
+                }
+
+                break;
+
+        case AF_INET6:
+                r = sd_netlink_message_read_in6_addr(message, RTA_DST, &tmp->dst.in6);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m");
+                        return 0;
+                }
+
+                r = sd_netlink_message_read_in6_addr(message, RTA_GATEWAY, &tmp->gw.in6);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
+                        return 0;
+                }
+
+                r = sd_netlink_message_read_in6_addr(message, RTA_SRC, &tmp->src.in6);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m");
+                        return 0;
+                }
+
+                r = sd_netlink_message_read_in6_addr(message, RTA_PREFSRC, &tmp->prefsrc.in6);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m");
+                        return 0;
+                }
+
+                break;
+
+        default:
+                assert_not_reached("Received route message with unsupported address family");
+                return 0;
+        }
+
+        r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid source prefixlen, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_rtnl_message_route_get_scope(message, &tmp->scope);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid scope, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_rtnl_message_route_get_tos(message, &tmp->tos);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid tos, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_rtnl_message_route_get_type(message, &tmp->type);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid type, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_rtnl_message_route_get_table(message, &table);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid table, ignoring: %m");
+                return 0;
+        }
+        tmp->table = table;
+
+        r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority);
+        if (r < 0 && r != -ENODATA) {
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid priority, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_netlink_message_enter_container(message, RTA_METRICS);
+        if (r < 0 && r != -ENODATA) {
+                log_link_error_errno(link, r, "rtnl: Could not enter RTA_METRICS container: %m");
+                return 0;
+        }
+        if (r >= 0) {
+                r = sd_netlink_message_read_u32(message, RTAX_INITCWND, &tmp->initcwnd);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route message with invalid initcwnd, ignoring: %m");
+                        return 0;
+                }
+
+                r = sd_netlink_message_read_u32(message, RTAX_INITRWND, &tmp->initrwnd);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route message with invalid initrwnd, ignoring: %m");
+                        return 0;
+                }
+
+                r = sd_netlink_message_exit_container(message);
+                if (r < 0) {
+                        log_link_error_errno(link, r, "rtnl: Could not exit from RTA_METRICS container: %m");
+                        return 0;
+                }
+        }
+
+        (void) route_get(link, tmp, &route);
+
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *buf_dst = NULL, *buf_dst_prefixlen = NULL,
+                        *buf_src = NULL, *buf_gw = NULL, *buf_prefsrc = NULL;
+                char buf_scope[ROUTE_SCOPE_STR_MAX], buf_table[ROUTE_TABLE_STR_MAX],
+                        buf_protocol[ROUTE_PROTOCOL_STR_MAX];
+
+                if (!in_addr_is_null(tmp->family, &tmp->dst)) {
+                        (void) in_addr_to_string(tmp->family, &tmp->dst, &buf_dst);
+                        (void) asprintf(&buf_dst_prefixlen, "/%u", tmp->dst_prefixlen);
+                }
+                if (!in_addr_is_null(tmp->family, &tmp->src))
+                        (void) in_addr_to_string(tmp->family, &tmp->src, &buf_src);
+                if (!in_addr_is_null(tmp->family, &tmp->gw))
+                        (void) in_addr_to_string(tmp->family, &tmp->gw, &buf_gw);
+                if (!in_addr_is_null(tmp->family, &tmp->prefsrc))
+                        (void) in_addr_to_string(tmp->family, &tmp->prefsrc, &buf_prefsrc);
+
+                log_link_debug(link,
+                               "%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
+                               (!route && !link->manager->manage_foreign_routes) ? "Ignoring received foreign" :
+                               type == RTM_DELROUTE ? "Forgetting" :
+                               route ? "Received remembered" : "Remembering",
+                               strna(buf_dst), strempty(buf_dst_prefixlen),
+                               strna(buf_src), strna(buf_gw), strna(buf_prefsrc),
+                               format_route_scope(tmp->scope, buf_scope, sizeof buf_scope),
+                               format_route_table(tmp->table, buf_table, sizeof buf_table),
+                               format_route_protocol(tmp->protocol, buf_protocol, sizeof buf_protocol),
+                               strna(route_type_to_string(tmp->type)));
+        }
+
+        switch (type) {
+        case RTM_NEWROUTE:
+                if (!route && link->manager->manage_foreign_routes) {
+                        /* A route appeared that we did not request */
+                        r = route_add_foreign(link, tmp, &route);
+                        if (r < 0) {
+                                log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
+                                return 0;
+                        }
+                }
+
+                break;
+
+        case RTM_DELROUTE:
+                route_free(route);
+                break;
+
+        default:
+                assert_not_reached("Received route message with invalid RTNL message type");
+        }
+
+        return 1;
+}
+
+int link_serialize_routes(Link *link, FILE *f) {
+        bool space = false;
+        Route *route;
+
+        assert(link);
+        assert(link->network);
+        assert(f);
+
+        fputs("ROUTES=", f);
+        SET_FOREACH(route, link->routes) {
+                _cleanup_free_ char *route_str = NULL;
+
+                if (in_addr_to_string(route->family, &route->dst, &route_str) < 0)
+                        continue;
+
+                fprintf(f, "%s%s/%hhu/%hhu/%"PRIu32"/%"PRIu32"/"USEC_FMT,
+                        space ? " " : "", route_str,
+                        route->dst_prefixlen, route->tos, route->priority, route->table, route->lifetime);
+                space = true;
+        }
+        fputc('\n', f);
+
+        return 0;
+}
+
+int link_deserialize_routes(Link *link, const char *routes) {
+        int r;
+
+        assert(link);
+
+        for (const char *p = routes;; ) {
+                _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
+                _cleanup_(route_freep) Route *tmp = NULL;
+                _cleanup_free_ char *route_str = NULL;
+                char *prefixlen_str;
+                Route *route;
+
+                r = extract_first_word(&p, &route_str, NULL, 0);
+                if (r < 0)
+                        return log_link_debug_errno(link, r, "Failed to parse ROUTES=: %m");
+                if (r == 0)
+                        return 0;
+
+                prefixlen_str = strchr(route_str, '/');
+                if (!prefixlen_str) {
+                        log_link_debug(link, "Failed to parse route, ignoring: %s", route_str);
+                        continue;
+                }
+                *prefixlen_str++ = '\0';
+
+                r = route_new(&tmp);
+                if (r < 0)
+                        return log_oom();
+
+                r = sscanf(prefixlen_str,
+                           "%hhu/%hhu/%"SCNu32"/%"PRIu32"/"USEC_FMT,
+                           &tmp->dst_prefixlen,
+                           &tmp->tos,
+                           &tmp->priority,
+                           &tmp->table,
+                           &tmp->lifetime);
+                if (r != 5) {
+                        log_link_debug(link,
+                                       "Failed to parse destination prefix length, tos, priority, table or expiration: %s",
+                                       prefixlen_str);
+                        continue;
+                }
+
+                r = in_addr_from_string_auto(route_str, &tmp->family, &tmp->dst);
+                if (r < 0) {
+                        log_link_debug_errno(link, r, "Failed to parse route destination %s: %m", route_str);
+                        continue;
+                }
+
+                r = route_add(link, tmp, &route);
+                if (r < 0)
+                        return log_link_debug_errno(link, r, "Failed to add route: %m");
+
+                if (route->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
+                        r = sd_event_add_time(link->manager->event, &expire,
+                                              clock_boottime_or_monotonic(),
+                                              route->lifetime, 0, route_expire_handler, route);
+                        if (r < 0)
+                                log_link_debug_errno(link, r, "Could not arm route expiration handler: %m");
+                }
+
+                sd_event_source_unref(route->expire);
+                route->expire = TAKE_PTR(expire);
+        }
+}
+
 int network_add_ipv4ll_route(Network *network) {
         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+        unsigned section_line;
         int r;
 
         assert(network);
@@ -830,8 +1446,10 @@ int network_add_ipv4ll_route(Network *network) {
         if (!network->ipv4ll_route)
                 return 0;
 
+        section_line = hashmap_find_free_section_line(network->routes_by_section);
+
         /* IPv4LLRoute= is in [Network] section. */
-        r = route_new_static(network, NULL, 0, &n);
+        r = route_new_static(network, network->filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -853,6 +1471,7 @@ int network_add_ipv4ll_route(Network *network) {
 
 int network_add_default_route_on_device(Network *network) {
         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
+        unsigned section_line;
         int r;
 
         assert(network);
@@ -860,8 +1479,10 @@ int network_add_default_route_on_device(Network *network) {
         if (!network->default_route_on_device)
                 return 0;
 
+        section_line = hashmap_find_free_section_line(network->routes_by_section);
+
         /* DefaultRouteOnDevice= is in [Network] section. */
-        r = route_new_static(network, NULL, 0, &n);
+        r = route_new_static(network, network->filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -874,113 +1495,6 @@ int network_add_default_route_on_device(Network *network) {
         return 0;
 }
 
-static const char * const route_type_table[__RTN_MAX] = {
-        [RTN_UNICAST]     = "unicast",
-        [RTN_LOCAL]       = "local",
-        [RTN_BROADCAST]   = "broadcast",
-        [RTN_ANYCAST]     = "anycast",
-        [RTN_MULTICAST]   = "multicast",
-        [RTN_BLACKHOLE]   = "blackhole",
-        [RTN_UNREACHABLE] = "unreachable",
-        [RTN_PROHIBIT]    = "prohibit",
-        [RTN_THROW]       = "throw",
-        [RTN_NAT]         = "nat",
-        [RTN_XRESOLVE]    = "xresolve",
-};
-
-assert_cc(__RTN_MAX <= UCHAR_MAX);
-DEFINE_STRING_TABLE_LOOKUP(route_type, int);
-
-static const char * const route_scope_table[] = {
-        [RT_SCOPE_UNIVERSE] = "global",
-        [RT_SCOPE_SITE]     = "site",
-        [RT_SCOPE_LINK]     = "link",
-        [RT_SCOPE_HOST]     = "host",
-        [RT_SCOPE_NOWHERE]  = "nowhere",
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_scope, int);
-
-const char *format_route_scope(int scope, char *buf, size_t size) {
-        const char *s;
-        char *p = buf;
-
-        s = route_scope_to_string(scope);
-        if (s)
-                strpcpy(&p, size, s);
-        else
-                strpcpyf(&p, size, "%d", scope);
-
-        return buf;
-}
-
-static const char * const route_table_table[] = {
-        [RT_TABLE_DEFAULT] = "default",
-        [RT_TABLE_MAIN]    = "main",
-        [RT_TABLE_LOCAL]   = "local",
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table, int);
-
-const char *format_route_table(int table, char *buf, size_t size) {
-        const char *s;
-        char *p = buf;
-
-        s = route_table_to_string(table);
-        if (s)
-                strpcpy(&p, size, s);
-        else
-                strpcpyf(&p, size, "%d", table);
-
-        return buf;
-}
-
-static const char * const route_protocol_table[] = {
-        [RTPROT_KERNEL] = "kernel",
-        [RTPROT_BOOT]   = "boot",
-        [RTPROT_STATIC] = "static",
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(route_protocol, int);
-
-static const char * const route_protocol_full_table[] = {
-        [RTPROT_REDIRECT] = "redirect",
-        [RTPROT_KERNEL]   = "kernel",
-        [RTPROT_BOOT]     = "boot",
-        [RTPROT_STATIC]   = "static",
-        [RTPROT_GATED]    = "gated",
-        [RTPROT_RA]       = "ra",
-        [RTPROT_MRT]      = "mrt",
-        [RTPROT_ZEBRA]    = "zebra",
-        [RTPROT_BIRD]     = "bird",
-        [RTPROT_DNROUTED] = "dnrouted",
-        [RTPROT_XORP]     = "xorp",
-        [RTPROT_NTK]      = "ntk",
-        [RTPROT_DHCP]     = "dhcp",
-        [RTPROT_MROUTED]  = "mrouted",
-        [RTPROT_BABEL]    = "babel",
-        [RTPROT_BGP]      = "bgp",
-        [RTPROT_ISIS]     = "isis",
-        [RTPROT_OSPF]     = "ospf",
-        [RTPROT_RIP]      = "rip",
-        [RTPROT_EIGRP]    = "eigrp",
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(route_protocol_full, int);
-
-const char *format_route_protocol(int protocol, char *buf, size_t size) {
-        const char *s;
-        char *p = buf;
-
-        s = route_protocol_full_to_string(protocol);
-        if (s)
-                strpcpy(&p, size, s);
-        else
-                strpcpyf(&p, size, "%d", protocol);
-
-        return buf;
-}
-
 int config_parse_gateway(
                 const char *unit,
                 const char *filename,
@@ -1004,9 +1518,8 @@ int config_parse_gateway(
         assert(data);
 
         if (streq(section, "Network")) {
-                /* we are not in an Route section, so treat
-                 * this as the special '0' section */
-                r = route_new_static(network, NULL, 0, &n);
+                /* we are not in an Route section, so use line number instead */
+                r = route_new_static(network, filename, line, &n);
                 if (r == -ENOMEM)
                         return log_oom();
                 if (r < 0) {
@@ -1658,7 +2171,7 @@ int config_parse_multipath_route(
         return 0;
 }
 
-int route_section_verify(Route *route, Network *network) {
+static int route_section_verify(Route *route, Network *network) {
         if (section_is_invalid(route->section))
                 return -EINVAL;
 
@@ -1687,7 +2200,7 @@ int route_section_verify(Route *route, Network *network) {
                         route->scope = RT_SCOPE_LINK;
         }
 
-        if (network->n_static_addresses == 0 &&
+        if (ordered_hashmap_isempty(network->addresses_by_section) &&
             in_addr_is_null(route->family, &route->gw) == 0 &&
             route->gateway_onlink < 0) {
                 log_warning("%s: Gateway= without static address configured. "
@@ -1698,3 +2211,13 @@ int route_section_verify(Route *route, Network *network) {
 
         return 0;
 }
+
+void network_drop_invalid_routes(Network *network) {
+        Route *route;
+
+        assert(network);
+
+        HASHMAP_FOREACH(route, network->routes_by_section)
+                if (route_section_verify(route, network) < 0)
+                        route_free(route);
+}
index 75651fa51228d19376e85d838657122856059d67..3347c7c57b4a00f12d092d7038cff2dd0b38d069 100644 (file)
@@ -1,15 +1,20 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
-#include "conf-parser.h"
-#include "macro.h"
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
 
-typedef struct Route Route;
-typedef struct NetworkConfigSection NetworkConfigSection;
+#include "sd-netlink.h"
 
-#include "networkd-network.h"
+#include "conf-parser.h"
+#include "in-addr-util.h"
+#include "networkd-link.h"
 #include "networkd-util.h"
 
+typedef struct Manager Manager;
+typedef struct Network Network;
+
 typedef struct MultipathRouteVia {
         uint16_t family;
         union in_addr_union address;
@@ -21,7 +26,7 @@ typedef struct MultipathRoute {
         uint32_t weight;
 } MultipathRoute;
 
-struct Route {
+typedef struct Route {
         Network *network;
         NetworkConfigSection *section;
 
@@ -58,43 +63,33 @@ struct Route {
 
         usec_t lifetime;
         sd_event_source *expire;
-
-        LIST_FIELDS(Route, routes);
-};
+} Route;
 
 void route_hash_func(const Route *route, struct siphash *state);
 int route_compare_func(const Route *a, const Route *b);
 extern const struct hash_ops route_hash_ops;
 
 int route_new(Route **ret);
-void route_free(Route *route);
+Route *route_free(Route *route);
+DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free);
+
 int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback, Route **ret);
 int route_remove(Route *route, Link *link, link_netlink_message_handler_t callback);
 
-int route_get(Link *link, Route *in, Route **ret);
-int route_add(Link *link, Route *in, Route **ret);
-int route_add_foreign(Link *link, Route *in, Route **ret);
-bool route_equal(Route *r1, Route *r2);
+int link_set_routes(Link *link);
+int link_drop_routes(Link *link);
+int link_drop_foreign_routes(Link *link);
+int link_serialize_routes(Link *link, FILE *f);
+int link_deserialize_routes(Link *link, const char *routes);
 
-int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata);
-int route_section_verify(Route *route, Network *network);
+uint32_t link_get_dhcp_route_table(Link *link);
+uint32_t link_get_ipv6_accept_ra_route_table(Link *link);
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free);
+int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
 
 int network_add_ipv4ll_route(Network *network);
 int network_add_default_route_on_device(Network *network);
-
-const char* route_type_to_string(int t) _const_;
-int route_type_from_string(const char *s) _pure_;
-
-#define ROUTE_SCOPE_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("nowhere") + 1)
-const char *format_route_scope(int scope, char *buf, size_t size);
-
-#define ROUTE_TABLE_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("default") + 1)
-const char *format_route_table(int table, char *buf, size_t size);
-
-#define ROUTE_PROTOCOL_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("redirect") + 1)
-const char *format_route_protocol(int protocol, char *buf, size_t size);
+void network_drop_invalid_routes(Network *network);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_gateway);
 CONFIG_PARSER_PROTOTYPE(config_parse_preferred_src);
index 3b95ea76b09f813e187683f3ddb5d7a4a32fa212..de60bd9555cdf9b2ea5af810904cc4eaa081146f 100644 (file)
@@ -9,9 +9,9 @@
 #include "fileio.h"
 #include "format-util.h"
 #include "ip-protocol-list.h"
-#include "networkd-routing-policy-rule.h"
 #include "netlink-util.h"
 #include "networkd-manager.h"
+#include "networkd-routing-policy-rule.h"
 #include "networkd-util.h"
 #include "parse-util.h"
 #include "socket-util.h"
 #include "strv.h"
 #include "user-util.h"
 
-int routing_policy_rule_new(RoutingPolicyRule **ret) {
+RoutingPolicyRule *routing_policy_rule_free(RoutingPolicyRule *rule) {
+        if (!rule)
+                return NULL;
+
+        if (rule->network) {
+                assert(rule->section);
+                hashmap_remove(rule->network->rules_by_section, rule->section);
+        }
+
+        if (rule->manager) {
+                if (set_get(rule->manager->rules, rule) == rule)
+                        set_remove(rule->manager->rules, rule);
+                if (set_get(rule->manager->rules_foreign, rule) == rule)
+                        set_remove(rule->manager->rules_foreign, rule);
+        }
+
+        network_config_section_free(rule->section);
+        free(rule->iif);
+        free(rule->oif);
+
+        return mfree(rule);
+}
+
+DEFINE_NETWORK_SECTION_FUNCTIONS(RoutingPolicyRule, routing_policy_rule_free);
+
+static int routing_policy_rule_new(RoutingPolicyRule **ret) {
         RoutingPolicyRule *rule;
 
         rule = new(RoutingPolicyRule, 1);
@@ -37,31 +62,43 @@ int routing_policy_rule_new(RoutingPolicyRule **ret) {
         return 0;
 }
 
-void routing_policy_rule_free(RoutingPolicyRule *rule) {
+static int routing_policy_rule_new_static(Network *network, const char *filename, unsigned section_line, RoutingPolicyRule **ret) {
+        _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
+        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        int r;
 
-        if (!rule)
-                return;
+        assert(network);
+        assert(ret);
+        assert(filename);
+        assert(section_line > 0);
 
-        if (rule->network) {
-                LIST_REMOVE(rules, rule->network->rules, rule);
-                assert(rule->network->n_rules > 0);
-                rule->network->n_rules--;
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
 
-                if (rule->section)
-                        hashmap_remove(rule->network->rules_by_section, rule->section);
+        rule = hashmap_get(network->rules_by_section, n);
+        if (rule) {
+                *ret = TAKE_PTR(rule);
+                return 0;
         }
 
-        if (rule->manager) {
-                if (set_get(rule->manager->rules, rule) == rule)
-                        set_remove(rule->manager->rules, rule);
-                if (set_get(rule->manager->rules_foreign, rule) == rule)
-                        set_remove(rule->manager->rules_foreign, rule);
-        }
+        r = routing_policy_rule_new(&rule);
+        if (r < 0)
+                return r;
 
-        network_config_section_free(rule->section);
-        free(rule->iif);
-        free(rule->oif);
-        free(rule);
+        rule->network = network;
+        rule->section = TAKE_PTR(n);
+
+        r = hashmap_ensure_allocated(&network->rules_by_section, &network_config_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = hashmap_put(network->rules_by_section, rule->section, rule);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(rule);
+        return 0;
 }
 
 static int routing_policy_rule_copy(RoutingPolicyRule *dest, RoutingPolicyRule *src) {
@@ -234,7 +271,7 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
                 routing_policy_rule_compare_func,
                 routing_policy_rule_free);
 
-int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) {
+static int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) {
 
         RoutingPolicyRule *existing;
 
@@ -257,31 +294,15 @@ int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRu
         return -ENOENT;
 }
 
-int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule) {
-        int r;
-
-        assert(m);
-
-        if (set_contains(m->rules_foreign, rule)) {
-                set_remove(m->rules_foreign, rule);
-
-                r = set_ensure_put(&m->rules, &routing_policy_rule_hash_ops, rule);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        routing_policy_rule_free(rule);
-        }
-
-        return -ENOENT;
-}
-
-static int routing_policy_rule_add_internal(Manager *m, Set **rules, RoutingPolicyRule *in, RoutingPolicyRule **ret) {
+static int routing_policy_rule_add_internal(Manager *m, Set **rules, RoutingPolicyRule *in, int family, RoutingPolicyRule **ret) {
         _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
         int r;
 
         assert(m);
         assert(rules);
         assert(in);
+        assert(IN_SET(family, AF_INET, AF_INET6));
+        assert(in->family == AF_UNSPEC || in->family == family);
 
         r = routing_policy_rule_new(&rule);
         if (r < 0)
@@ -293,6 +314,8 @@ static int routing_policy_rule_add_internal(Manager *m, Set **rules, RoutingPoli
         if (r < 0)
                 return r;
 
+        rule->family = family;
+
         r = set_ensure_put(rules, &routing_policy_rule_hash_ops, rule);
         if (r < 0)
                 return r;
@@ -306,12 +329,122 @@ static int routing_policy_rule_add_internal(Manager *m, Set **rules, RoutingPoli
         return 0;
 }
 
-static int routing_policy_rule_add(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) {
-        return routing_policy_rule_add_internal(m, &m->rules, rule, ret);
+static int routing_policy_rule_add(Manager *m, RoutingPolicyRule *rule, int family, RoutingPolicyRule **ret) {
+        return routing_policy_rule_add_internal(m, &m->rules, rule, family, ret);
 }
 
-int routing_policy_rule_add_foreign(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) {
-        return routing_policy_rule_add_internal(m, &m->rules_foreign, rule, ret);
+static int routing_policy_rule_add_foreign(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) {
+        return routing_policy_rule_add_internal(m, &m->rules_foreign, rule, rule->family, ret);
+}
+
+static int routing_policy_rule_set_netlink_message(RoutingPolicyRule *rule, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(rule);
+        assert(m);
+        assert(link);
+
+        if (in_addr_is_null(rule->family, &rule->from) == 0) {
+                r = netlink_message_append_in_addr_union(m, FRA_SRC, rule->family, &rule->from);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FRA_SRC attribute: %m");
+
+                r = sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m, rule->from_prefixlen);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not set source prefix length: %m");
+        }
+
+        if (in_addr_is_null(rule->family, &rule->to) == 0) {
+                r = netlink_message_append_in_addr_union(m, FRA_DST, rule->family, &rule->to);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FRA_DST attribute: %m");
+
+                r = sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m, rule->to_prefixlen);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
+        }
+
+        r = sd_netlink_message_append_u32(m, FRA_PRIORITY, rule->priority);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append FRA_PRIORITY attribute: %m");
+
+        if (rule->tos > 0) {
+                r = sd_rtnl_message_routing_policy_rule_set_tos(m, rule->tos);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not set IP rule TOS: %m");
+        }
+
+        if (rule->table < 256) {
+                r = sd_rtnl_message_routing_policy_rule_set_table(m, rule->table);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not set IP rule table: %m");
+        } else {
+                r = sd_rtnl_message_routing_policy_rule_set_table(m, RT_TABLE_UNSPEC);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not set IP rule table: %m");
+
+                r = sd_netlink_message_append_u32(m, FRA_TABLE, rule->table);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FRA_TABLE attribute: %m");
+        }
+
+        if (rule->fwmark > 0) {
+                r = sd_netlink_message_append_u32(m, FRA_FWMARK, rule->fwmark);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FRA_FWMARK attribute: %m");
+
+                r = sd_netlink_message_append_u32(m, FRA_FWMASK, rule->fwmask);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FRA_FWMASK attribute: %m");
+        }
+
+        if (rule->iif) {
+                r = sd_netlink_message_append_string(m, FRA_IIFNAME, rule->iif);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FRA_IIFNAME attribute: %m");
+        }
+
+        if (rule->oif) {
+                r = sd_netlink_message_append_string(m, FRA_OIFNAME, rule->oif);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FRA_OIFNAME attribute: %m");
+        }
+
+        r = sd_netlink_message_append_u8(m, FRA_IP_PROTO, rule->protocol);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append FRA_IP_PROTO attribute: %m");
+
+        if (rule->sport.start != 0 || rule->sport.end != 0) {
+                r = sd_netlink_message_append_data(m, FRA_SPORT_RANGE, &rule->sport, sizeof(rule->sport));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FRA_SPORT_RANGE attribute: %m");
+        }
+
+        if (rule->dport.start != 0 || rule->dport.end != 0) {
+                r = sd_netlink_message_append_data(m, FRA_DPORT_RANGE, &rule->dport, sizeof(rule->dport));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FRA_DPORT_RANGE attribute: %m");
+        }
+
+        if (rule->uid_range.start != UID_INVALID && rule->uid_range.end != UID_INVALID) {
+                r = sd_netlink_message_append_data(m, FRA_UID_RANGE, &rule->uid_range, sizeof(rule->uid_range));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FRA_UID_RANGE attribute: %m");
+        }
+
+        if (rule->invert_rule) {
+                r = sd_rtnl_message_routing_policy_rule_set_flags(m, FIB_RULE_INVERT);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FIB_RULE_INVERT attribute: %m");
+        }
+
+        if (rule->suppress_prefixlen >= 0) {
+                r = sd_netlink_message_append_u32(m, FRA_SUPPRESS_PREFIXLEN, (uint32_t) rule->suppress_prefixlen);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FRA_SUPPRESS_PREFIXLEN attribute: %m");
+        }
+
+        return 0;
 }
 
 static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
@@ -333,7 +466,7 @@ static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_messa
         return 1;
 }
 
-int routing_policy_rule_remove(RoutingPolicyRule *rule, Link *link, link_netlink_message_handler_t callback) {
+static int routing_policy_rule_remove(RoutingPolicyRule *rule, Link *link) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
@@ -359,28 +492,12 @@ int routing_policy_rule_remove(RoutingPolicyRule *rule, Link *link, link_netlink
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not allocate RTM_DELRULE message: %m");
 
-        if (in_addr_is_null(rule->family, &rule->from) == 0) {
-                r = netlink_message_append_in_addr_union(m, FRA_SRC, rule->family, &rule->from);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append FRA_SRC attribute: %m");
-
-                r = sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m, rule->from_prefixlen);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set source prefix length: %m");
-        }
-
-        if (in_addr_is_null(rule->family, &rule->to) == 0) {
-                r = netlink_message_append_in_addr_union(m, FRA_DST, rule->family, &rule->to);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append FRA_DST attribute: %m");
-
-                r = sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m, rule->to_prefixlen);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
-        }
+        r = routing_policy_rule_set_netlink_message(rule, m, link);
+        if (r < 0)
+                return r;
 
         r = netlink_call_async(link->manager->rtnl, NULL, m,
-                               callback ?: routing_policy_rule_remove_handler,
+                               routing_policy_rule_remove_handler,
                                link_netlink_destroy_callback, link);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
@@ -390,53 +507,6 @@ int routing_policy_rule_remove(RoutingPolicyRule *rule, Link *link, link_netlink
         return 0;
 }
 
-static int routing_policy_rule_new_static(Network *network, const char *filename, unsigned section_line, RoutingPolicyRule **ret) {
-        _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
-        int r;
-
-        assert(network);
-        assert(ret);
-        assert(!!filename == (section_line > 0));
-
-        if (filename) {
-                r = network_config_section_new(filename, section_line, &n);
-                if (r < 0)
-                        return r;
-
-                rule = hashmap_get(network->rules_by_section, n);
-                if (rule) {
-                        *ret = TAKE_PTR(rule);
-
-                        return 0;
-                }
-        }
-
-        r = routing_policy_rule_new(&rule);
-        if (r < 0)
-                return r;
-
-        rule->network = network;
-        LIST_APPEND(rules, network->rules, rule);
-        network->n_rules++;
-
-        if (filename) {
-                rule->section = TAKE_PTR(n);
-
-                r = hashmap_ensure_allocated(&network->rules_by_section, &network_config_hash_ops);
-                if (r < 0)
-                        return r;
-
-                r = hashmap_put(network->rules_by_section, rule->section, rule);
-                if (r < 0)
-                        return r;
-        }
-
-        *ret = TAKE_PTR(rule);
-
-        return 0;
-}
-
 static int routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
@@ -467,7 +537,7 @@ static int routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m,
         return 1;
 }
 
-int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netlink_message_handler_t callback) {
+static int routing_policy_rule_configure_internal(RoutingPolicyRule *rule, int family, Link *link) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
@@ -480,177 +550,380 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netl
         if (DEBUG_LOGGING) {
                 _cleanup_free_ char *from = NULL, *to = NULL;
 
-                (void) in_addr_to_string(rule->family, &rule->from, &from);
-                (void) in_addr_to_string(rule->family, &rule->to, &to);
+                (void) in_addr_to_string(family, &rule->from, &from);
+                (void) in_addr_to_string(family, &rule->to, &to);
 
                 log_link_debug(link,
                                "Configuring routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32,
                                rule->priority, strna(from), rule->from_prefixlen, strna(to), rule->to_prefixlen, strna(rule->iif), strna(rule->oif), rule->table);
         }
 
-        r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, rule->family);
+        r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, family);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not allocate RTM_NEWRULE message: %m");
 
-        if (in_addr_is_null(rule->family, &rule->from) == 0) {
-                r = netlink_message_append_in_addr_union(m, FRA_SRC, rule->family, &rule->from);
+        r = routing_policy_rule_set_netlink_message(rule, m, link);
+        if (r < 0)
+                return r;
+
+        r = netlink_call_async(link->manager->rtnl, NULL, m,
+                               routing_policy_rule_handler,
+                               link_netlink_destroy_callback, link);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+        link_ref(link);
+        link->routing_policy_rule_messages++;
+
+        r = routing_policy_rule_add(link->manager, rule, family, NULL);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not add rule: %m");
+
+        return 1;
+}
+
+static int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link) {
+        int r;
+
+        if (IN_SET(rule->family, AF_INET, AF_INET6))
+                return routing_policy_rule_configure_internal(rule, rule->family, link);
+
+        if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4)) {
+                r = routing_policy_rule_configure_internal(rule, AF_INET, link);
                 if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append FRA_SRC attribute: %m");
+                        return r;
+        }
+
+        if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) {
+                r = routing_policy_rule_configure_internal(rule, AF_INET6, link);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static bool manager_links_have_routing_policy_rule(Manager *m, RoutingPolicyRule *rule) {
+        Link *link;
+
+        assert(m);
+        assert(rule);
+
+        HASHMAP_FOREACH(link, m->links) {
+                RoutingPolicyRule *link_rule;
+
+                if (!link->network)
+                        continue;
+
+                HASHMAP_FOREACH(link_rule, link->network->rules_by_section)
+                        if (routing_policy_rule_compare_func(link_rule, rule) == 0)
+                                return true;
+        }
+
+        return false;
+}
+
+static void routing_policy_rule_purge(Manager *m, Link *link) {
+        RoutingPolicyRule *rule;
+        int r;
+
+        assert(m);
+        assert(link);
+
+        SET_FOREACH(rule, m->rules_saved) {
+                RoutingPolicyRule *existing;
+
+                existing = set_get(m->rules_foreign, rule);
+                if (!existing)
+                        continue; /* Saved rule does not exist anymore. */
+
+                if (manager_links_have_routing_policy_rule(m, existing))
+                        continue; /* Existing links have the saved rule. */
+
+                /* Existing links do not have the saved rule. Let's drop the rule now, and re-configure it
+                 * later when it is requested. */
+
+                r = routing_policy_rule_remove(existing, link);
+                if (r < 0) {
+                        log_warning_errno(r, "Could not remove routing policy rules: %m");
+                        continue;
+                }
+
+                link->routing_policy_rule_remove_messages++;
+
+                assert_se(set_remove(m->rules_foreign, existing) == existing);
+                routing_policy_rule_free(existing);
+        }
+}
+
+int link_set_routing_policy_rules(Link *link) {
+        RoutingPolicyRule *rule;
+        int r;
+
+        assert(link);
+        assert(link->network);
+
+        link->routing_policy_rules_configured = false;
+
+        HASHMAP_FOREACH(rule, link->network->rules_by_section) {
+                RoutingPolicyRule *existing;
+
+                r = routing_policy_rule_get(link->manager, rule, &existing);
+                if (r > 0)
+                        continue;
+                if (r == 0) {
+                        r = set_ensure_put(&link->manager->rules, &routing_policy_rule_hash_ops, existing);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not store existing routing policy rule: %m");
+
+                        set_remove(link->manager->rules_foreign, existing);
+                        continue;
+                }
 
-                r = sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m, rule->from_prefixlen);
+                r = routing_policy_rule_configure(rule, link);
                 if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set source prefix length: %m");
+                        return log_link_warning_errno(link, r, "Could not set routing policy rule: %m");
         }
 
-        if (in_addr_is_null(rule->family, &rule->to) == 0) {
-                r = netlink_message_append_in_addr_union(m, FRA_DST, rule->family, &rule->to);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append FRA_DST attribute: %m");
-
-                r = sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m, rule->to_prefixlen);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
+        routing_policy_rule_purge(link->manager, link);
+        if (link->routing_policy_rule_messages == 0)
+                link->routing_policy_rules_configured = true;
+        else {
+                log_link_debug(link, "Setting routing policy rules");
+                link_set_state(link, LINK_STATE_CONFIGURING);
         }
 
-        r = sd_netlink_message_append_u32(m, FRA_PRIORITY, rule->priority);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not append FRA_PRIORITY attribute: %m");
+        return 0;
+}
 
-        if (rule->tos > 0) {
-                r = sd_rtnl_message_routing_policy_rule_set_tos(m, rule->tos);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set IP rule TOS: %m");
-        }
+int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
+        _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL;
+        _cleanup_free_ char *from = NULL, *to = NULL;
+        RoutingPolicyRule *rule = NULL;
+        const char *iif = NULL, *oif = NULL;
+        uint32_t suppress_prefixlen;
+        unsigned flags;
+        uint16_t type;
+        int r;
 
-        if (rule->table < 256) {
-                r = sd_rtnl_message_routing_policy_rule_set_table(m, rule->table);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set IP rule table: %m");
-        } else {
-                r = sd_rtnl_message_routing_policy_rule_set_table(m, RT_TABLE_UNSPEC);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set IP rule table: %m");
+        assert(rtnl);
+        assert(message);
 
-                r = sd_netlink_message_append_u32(m, FRA_TABLE, rule->table);
+        if (sd_netlink_message_is_error(message)) {
+                r = sd_netlink_message_get_errno(message);
                 if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append FRA_TABLE attribute: %m");
-        }
+                        log_message_warning_errno(message, r, "rtnl: failed to receive rule message, ignoring");
 
-        if (rule->fwmark > 0) {
-                r = sd_netlink_message_append_u32(m, FRA_FWMARK, rule->fwmark);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append FRA_FWMARK attribute: %m");
+                return 0;
+        }
 
-                r = sd_netlink_message_append_u32(m, FRA_FWMASK, rule->fwmask);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append FRA_FWMASK attribute: %m");
+        r = sd_netlink_message_get_type(message, &type);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
+                return 0;
+        } else if (!IN_SET(type, RTM_NEWRULE, RTM_DELRULE)) {
+                log_warning("rtnl: received unexpected message type %u when processing rule, ignoring.", type);
+                return 0;
         }
 
-        if (rule->iif) {
-                r = sd_netlink_message_append_string(m, FRA_IIFNAME, rule->iif);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append FRA_IIFNAME attribute: %m");
+        r = routing_policy_rule_new(&tmp);
+        if (r < 0) {
+                log_oom();
+                return 0;
         }
 
-        if (rule->oif) {
-                r = sd_netlink_message_append_string(m, FRA_OIFNAME, rule->oif);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append FRA_OIFNAME attribute: %m");
+        r = sd_rtnl_message_get_family(message, &tmp->family);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get rule family, ignoring: %m");
+                return 0;
+        } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
+                log_debug("rtnl: received rule message with invalid family %d, ignoring.", tmp->family);
+                return 0;
         }
 
-        r = sd_netlink_message_append_u8(m, FRA_IP_PROTO, rule->protocol);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not append FRA_IP_PROTO attribute: %m");
+        switch (tmp->family) {
+        case AF_INET:
+                r = sd_netlink_message_read_in_addr(message, FRA_SRC, &tmp->from.in);
+                if (r < 0 && r != -ENODATA) {
+                        log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m");
+                        return 0;
+                } else if (r >= 0) {
+                        r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &tmp->from_prefixlen);
+                        if (r < 0) {
+                                log_warning_errno(r, "rtnl: received rule message without valid source prefix length, ignoring: %m");
+                                return 0;
+                        }
+                }
 
-        if (rule->sport.start != 0 || rule->sport.end != 0) {
-                r = sd_netlink_message_append_data(m, FRA_SPORT_RANGE, &rule->sport, sizeof(rule->sport));
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append FRA_SPORT_RANGE attribute: %m");
-        }
+                r = sd_netlink_message_read_in_addr(message, FRA_DST, &tmp->to.in);
+                if (r < 0 && r != -ENODATA) {
+                        log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m");
+                        return 0;
+                } else if (r >= 0) {
+                        r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &tmp->to_prefixlen);
+                        if (r < 0) {
+                                log_warning_errno(r, "rtnl: received rule message without valid destination prefix length, ignoring: %m");
+                                return 0;
+                        }
+                }
 
-        if (rule->dport.start != 0 || rule->dport.end != 0) {
-                r = sd_netlink_message_append_data(m, FRA_DPORT_RANGE, &rule->dport, sizeof(rule->dport));
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append FRA_DPORT_RANGE attribute: %m");
+                break;
+
+        case AF_INET6:
+                r = sd_netlink_message_read_in6_addr(message, FRA_SRC, &tmp->from.in6);
+                if (r < 0 && r != -ENODATA) {
+                        log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m");
+                        return 0;
+                } else if (r >= 0) {
+                        r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &tmp->from_prefixlen);
+                        if (r < 0) {
+                                log_warning_errno(r, "rtnl: received rule message without valid source prefix length, ignoring: %m");
+                                return 0;
+                        }
+                }
+
+                r = sd_netlink_message_read_in6_addr(message, FRA_DST, &tmp->to.in6);
+                if (r < 0 && r != -ENODATA) {
+                        log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m");
+                        return 0;
+                } else if (r >= 0) {
+                        r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &tmp->to_prefixlen);
+                        if (r < 0) {
+                                log_warning_errno(r, "rtnl: received rule message without valid destination prefix length, ignoring: %m");
+                                return 0;
+                        }
+                }
+
+                break;
+
+        default:
+                assert_not_reached("Received rule message with unsupported address family");
         }
 
-        if (rule->uid_range.start != UID_INVALID && rule->uid_range.end != UID_INVALID) {
-                r = sd_netlink_message_append_data(m, FRA_UID_RANGE, &rule->uid_range, sizeof(rule->uid_range));
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append FRA_UID_RANGE attribute: %m");
+        r = sd_rtnl_message_routing_policy_rule_get_flags(message, &flags);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: received rule message without valid flag, ignoring: %m");
+                return 0;
         }
+        tmp->invert_rule = flags & FIB_RULE_INVERT;
 
-        if (rule->invert_rule) {
-                r = sd_rtnl_message_routing_policy_rule_set_flags(m, FIB_RULE_INVERT);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append FIB_RULE_INVERT attribute: %m");
+        r = sd_netlink_message_read_u32(message, FRA_FWMARK, &tmp->fwmark);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_FWMARK attribute, ignoring: %m");
+                return 0;
         }
 
-        if (rule->suppress_prefixlen >= 0) {
-                r = sd_netlink_message_append_u32(m, FRA_SUPPRESS_PREFIXLEN, (uint32_t) rule->suppress_prefixlen);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append FRA_SUPPRESS_PREFIXLEN attribute: %m");
+        r = sd_netlink_message_read_u32(message, FRA_FWMASK, &tmp->fwmask);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_FWMASK attribute, ignoring: %m");
+                return 0;
         }
 
-        rule->link = link;
+        r = sd_netlink_message_read_u32(message, FRA_PRIORITY, &tmp->priority);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_PRIORITY attribute, ignoring: %m");
+                return 0;
+        }
 
-        r = netlink_call_async(link->manager->rtnl, NULL, m,
-                               callback ?: routing_policy_rule_handler,
-                               link_netlink_destroy_callback, link);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+        r = sd_netlink_message_read_u32(message, FRA_TABLE, &tmp->table);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_TABLE attribute, ignoring: %m");
+                return 0;
+        }
 
-        link_ref(link);
+        r = sd_rtnl_message_routing_policy_rule_get_tos(message, &tmp->tos);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get ip rule TOS, ignoring: %m");
+                return 0;
+        }
 
-        r = routing_policy_rule_add(link->manager, rule, NULL);
+        r = sd_netlink_message_read_string(message, FRA_IIFNAME, &iif);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_IIFNAME attribute, ignoring: %m");
+                return 0;
+        }
+        r = free_and_strdup(&tmp->iif, iif);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not add rule: %m");
-
-        return 1;
-}
-
-int routing_policy_rule_section_verify(RoutingPolicyRule *rule) {
-        int r;
-
-        if (section_is_invalid(rule->section))
-                return -EINVAL;
+                return log_oom();
 
-        if ((rule->family == AF_INET && FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) ||
-            (rule->family == AF_INET6 && FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4)))
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                "%s: address family specified by Family= conflicts with the address "
-                                "specified by To= or From=. Ignoring [RoutingPolicyRule] section from line %u.",
-                                rule->section->filename, rule->section->line);
+        r = sd_netlink_message_read_string(message, FRA_OIFNAME, &oif);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_OIFNAME attribute, ignoring: %m");
+                return 0;
+        }
+        r = free_and_strdup(&tmp->oif, oif);
+        if (r < 0)
+                return log_oom();
 
-        if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_IPV6)) {
-                _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule6 = NULL;
+        r = sd_netlink_message_read_u8(message, FRA_IP_PROTO, &tmp->protocol);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_IP_PROTO attribute, ignoring: %m");
+                return 0;
+        }
 
-                assert(rule->family == AF_UNSPEC);
+        r = sd_netlink_message_read(message, FRA_SPORT_RANGE, sizeof(tmp->sport), &tmp->sport);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_SPORT_RANGE attribute, ignoring: %m");
+                return 0;
+        }
 
-                /* When Family=both, we need to copy the section, AF_INET and AF_INET6. */
+        r = sd_netlink_message_read(message, FRA_DPORT_RANGE, sizeof(tmp->dport), &tmp->dport);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_DPORT_RANGE attribute, ignoring: %m");
+                return 0;
+        }
 
-                r = routing_policy_rule_new_static(rule->network, NULL, 0, &rule6);
-                if (r < 0)
-                        return r;
+        r = sd_netlink_message_read(message, FRA_UID_RANGE, sizeof(tmp->uid_range), &tmp->uid_range);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_UID_RANGE attribute, ignoring: %m");
+                return 0;
+        }
 
-                r = routing_policy_rule_copy(rule6, rule);
-                if (r < 0)
-                        return r;
+        r = sd_netlink_message_read_u32(message, FRA_SUPPRESS_PREFIXLEN, &suppress_prefixlen);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_SUPPRESS_PREFIXLEN attribute, ignoring: %m");
+                return 0;
+        }
+        if (r >= 0)
+                tmp->suppress_prefixlen = (int) suppress_prefixlen;
 
-                rule->family = AF_INET;
-                rule6->family = AF_INET6;
+        (void) routing_policy_rule_get(m, tmp, &rule);
 
-                TAKE_PTR(rule6);
+        if (DEBUG_LOGGING) {
+                (void) in_addr_to_string(tmp->family, &tmp->from, &from);
+                (void) in_addr_to_string(tmp->family, &tmp->to, &to);
         }
 
-        if (rule->family == AF_UNSPEC) {
-                if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6))
-                        rule->family = AF_INET6;
-                else
-                        rule->family = AF_INET;
+        switch (type) {
+        case RTM_NEWRULE:
+                if (rule)
+                        log_debug("Received remembered routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32,
+                                  tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table);
+                else {
+                        log_debug("Remembering foreign routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32,
+                                  tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table);
+                        r = routing_policy_rule_add_foreign(m, tmp, &rule);
+                        if (r < 0) {
+                                log_warning_errno(r, "Could not remember foreign rule, ignoring: %m");
+                                return 0;
+                        }
+                }
+                break;
+        case RTM_DELRULE:
+                if (rule) {
+                        log_debug("Forgetting routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32,
+                                  tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table);
+                        routing_policy_rule_free(rule);
+                } else
+                        log_debug("Kernel removed a routing policy rule we don't remember: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32", ignoring.",
+                                  tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table);
+                break;
+
+        default:
+                assert_not_reached("Received invalid RTNL message type");
         }
 
-        return 0;
+        return 1;
 }
 
 static int parse_fwmark_fwmask(const char *s, uint32_t *ret_fwmark, uint32_t *ret_fwmask) {
@@ -1183,14 +1456,40 @@ int config_parse_routing_policy_rule_suppress_prefixlen(
         return 0;
 }
 
+static int routing_policy_rule_section_verify(RoutingPolicyRule *rule) {
+        if (section_is_invalid(rule->section))
+                return -EINVAL;
+
+        if ((rule->family == AF_INET && FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) ||
+            (rule->family == AF_INET6 && FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4)))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                "%s: address family specified by Family= conflicts with the address "
+                                "specified by To= or From=. Ignoring [RoutingPolicyRule] section from line %u.",
+                                rule->section->filename, rule->section->line);
+
+        if (rule->family == AF_UNSPEC && rule->address_family == ADDRESS_FAMILY_NO)
+                rule->family = AF_INET;
+
+        return 0;
+}
+
+void network_drop_invalid_routing_policy_rules(Network *network) {
+        RoutingPolicyRule *rule;
+
+        assert(network);
+
+        HASHMAP_FOREACH(rule, network->rules_by_section)
+                if (routing_policy_rule_section_verify(rule) < 0)
+                        routing_policy_rule_free(rule);
+}
+
 int routing_policy_serialize_rules(Set *rules, FILE *f) {
-        RoutingPolicyRule *rule = NULL;
+        RoutingPolicyRule *rule;
         int r;
 
         assert(f);
 
         SET_FOREACH(rule, rules) {
-                _cleanup_free_ char *from_str = NULL, *to_str = NULL;
                 const char *family_str;
                 bool space = false;
 
@@ -1204,24 +1503,28 @@ int routing_policy_serialize_rules(Set *rules, FILE *f) {
                 }
 
                 if (!in_addr_is_null(rule->family, &rule->from)) {
-                        r = in_addr_to_string(rule->family, &rule->from, &from_str);
+                        _cleanup_free_ char *str = NULL;
+
+                        r = in_addr_to_string(rule->family, &rule->from, &str);
                         if (r < 0)
                                 return r;
 
                         fprintf(f, "%sfrom=%s/%hhu",
                                 space ? " " : "",
-                                from_str, rule->from_prefixlen);
+                                str, rule->from_prefixlen);
                         space = true;
                 }
 
                 if (!in_addr_is_null(rule->family, &rule->to)) {
-                        r = in_addr_to_string(rule->family, &rule->to, &to_str);
+                        _cleanup_free_ char *str = NULL;
+
+                        r = in_addr_to_string(rule->family, &rule->to, &str);
                         if (r < 0)
                                 return r;
 
                         fprintf(f, "%sto=%s/%hhu",
                                 space ? " " : "",
-                                to_str, rule->to_prefixlen);
+                                str, rule->to_prefixlen);
                         space = true;
                 }
 
@@ -1307,31 +1610,31 @@ int routing_policy_serialize_rules(Set *rules, FILE *f) {
         return 0;
 }
 
-static int routing_policy_rule_read_full_file(const char *state_file, char **ret) {
+static int routing_policy_rule_read_full_file(const char *state_file, char ***ret) {
+        _cleanup_strv_free_ char **lines = NULL;
         _cleanup_free_ char *s = NULL;
-        size_t size;
         int r;
 
         assert(state_file);
 
-        r = read_full_file(state_file, &s, &size);
-        if (r == -ENOENT)
-                return -ENODATA;
+        r = read_full_file(state_file, &s, NULL);
+        if (r == -ENOENT) {
+                *ret = NULL;
+                return 0;
+        }
         if (r < 0)
                 return r;
-        if (size <= 0)
-                return -ENODATA;
 
-        *ret = TAKE_PTR(s);
+        lines = strv_split_newlines(s);
+        if (!lines)
+                return -ENOMEM;
 
-        return size;
+        *ret = TAKE_PTR(lines);
+        return 0;
 }
 
 int routing_policy_load_rules(const char *state_file, Set **rules) {
-        _cleanup_strv_free_ char **l = NULL;
-        _cleanup_free_ char *data = NULL;
-        uint16_t low = 0, high = 0;
-        const char *p;
+        _cleanup_strv_free_ char **data = NULL;
         char **i;
         int r;
 
@@ -1339,15 +1642,12 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
         assert(rules);
 
         r = routing_policy_rule_read_full_file(state_file, &data);
-        if (r <= 0)
-                return r;
-
-        l = strv_split_newlines(data);
-        if (!l)
-                return -ENOMEM;
+        if (r < 0)
+                return log_warning_errno(r, "Failed to read %s, ignoring: %m", state_file);
 
-        STRV_FOREACH(i, l) {
+        STRV_FOREACH(i, data) {
                 _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
+                const char *p;
 
                 p = startswith(*i, "RULE=");
                 if (!p)
@@ -1355,7 +1655,7 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
 
                 r = routing_policy_rule_new(&rule);
                 if (r < 0)
-                        return r;
+                        return log_oom();
 
                 for (;;) {
                         _cleanup_free_ char *a = NULL;
@@ -1363,7 +1663,7 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
 
                         r = extract_first_word(&p, &a, NULL, 0);
                         if (r < 0)
-                                return r;
+                                return log_oom();
                         if (r == 0)
                                 break;
 
@@ -1385,7 +1685,7 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
                                         continue;
                                 }
                                 rule->family = r;
-                        } if (STR_IN_SET(a, "from", "to")) {
+                        } else if (STR_IN_SET(a, "from", "to")) {
                                 union in_addr_union *buffer;
                                 uint8_t *prefixlen;
 
@@ -1444,6 +1744,8 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
                                         continue;
                                 }
                         } else if (streq(a, "sourceport")) {
+                                uint16_t low, high;
+
                                 r = parse_ip_port_range(b, &low, &high);
                                 if (r < 0) {
                                         log_warning_errno(r, "Invalid routing policy rule source port range, ignoring assignment: '%s'", b);
@@ -1453,6 +1755,8 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
                                 rule->sport.start = low;
                                 rule->sport.end = high;
                         } else if (streq(a, "destinationport")) {
+                                uint16_t low, high;
+
                                 r = parse_ip_port_range(b, &low, &high);
                                 if (r < 0) {
                                         log_warning_errno(r, "Invalid routing policy rule destination port range, ignoring assignment: '%s'", b);
@@ -1495,7 +1799,7 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
 
                 r = set_ensure_put(rules, &routing_policy_rule_hash_ops, rule);
                 if (r < 0) {
-                        log_warning_errno(r, "Failed to add RPDB rule to saved DB, ignoring: %s", p);
+                        log_warning_errno(r, "Failed to add RPDB rule to saved DB, ignoring: %s", *i);
                         continue;
                 }
                 if (r > 0)
@@ -1504,53 +1808,3 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
 
         return 0;
 }
-
-static bool manager_links_have_routing_policy_rule(Manager *m, RoutingPolicyRule *rule) {
-        RoutingPolicyRule *link_rule;
-        Link *link;
-
-        assert(m);
-        assert(rule);
-
-        HASHMAP_FOREACH(link, m->links) {
-                if (!link->network)
-                        continue;
-
-                LIST_FOREACH(rules, link_rule, link->network->rules)
-                        if (routing_policy_rule_compare_func(link_rule, rule) == 0)
-                                return true;
-        }
-
-        return false;
-}
-
-void routing_policy_rule_purge(Manager *m, Link *link) {
-        RoutingPolicyRule *rule, *existing;
-        int r;
-
-        assert(m);
-        assert(link);
-
-        SET_FOREACH(rule, m->rules_saved) {
-                existing = set_get(m->rules_foreign, rule);
-                if (!existing)
-                        continue; /* Saved rule does not exist anymore. */
-
-                if (manager_links_have_routing_policy_rule(m, existing))
-                        continue; /* Existing links have the saved rule. */
-
-                /* Existing links do not have the saved rule. Let's drop the rule now, and re-configure it
-                 * later when it is requested. */
-
-                r = routing_policy_rule_remove(existing, link, NULL);
-                if (r < 0) {
-                        log_warning_errno(r, "Could not remove routing policy rules: %m");
-                        continue;
-                }
-
-                link->routing_policy_rule_remove_messages++;
-
-                assert_se(set_remove(m->rules_foreign, existing) == existing);
-                routing_policy_rule_free(existing);
-        }
-}
index af954e8fb5d3bc94627021045f0fc75ef172a90e..bc1193a111171a8c2e7478209eb1b7fadcbe9cf0 100644 (file)
@@ -2,28 +2,22 @@
 #pragma once
 
 #include <inttypes.h>
-#include <netinet/in.h>
 #include <linux/fib_rules.h>
 #include <stdbool.h>
+#include <stdio.h>
 
-#include "in-addr-util.h"
 #include "conf-parser.h"
-
-typedef struct RoutingPolicyRule RoutingPolicyRule;
-
-#include "networkd-link.h"
-#include "networkd-network.h"
+#include "in-addr-util.h"
 #include "networkd-util.h"
+#include "set.h"
 
 typedef struct Network Network;
 typedef struct Link Link;
-typedef struct NetworkConfigSection NetworkConfigSection;
 typedef struct Manager Manager;
 
-struct RoutingPolicyRule {
+typedef struct RoutingPolicyRule {
         Manager *manager;
         Network *network;
-        Link *link;
         NetworkConfigSection *section;
 
         bool invert_rule;
@@ -52,25 +46,18 @@ struct RoutingPolicyRule {
         struct fib_rule_uid_range uid_range;
 
         int suppress_prefixlen;
+} RoutingPolicyRule;
 
-        LIST_FIELDS(RoutingPolicyRule, rules);
-};
+RoutingPolicyRule *routing_policy_rule_free(RoutingPolicyRule *rule);
 
-int routing_policy_rule_new(RoutingPolicyRule **ret);
-void routing_policy_rule_free(RoutingPolicyRule *rule);
+void network_drop_invalid_routing_policy_rules(Network *network);
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(RoutingPolicyRule, routing_policy_rule_free);
-int routing_policy_rule_section_verify(RoutingPolicyRule *rule);
+int link_set_routing_policy_rules(Link *link);
 
-int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netlink_message_handler_t callback);
-int routing_policy_rule_remove(RoutingPolicyRule *rule, Link *link, link_netlink_message_handler_t callback);
+int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
 
-int routing_policy_rule_add_foreign(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret);
-int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret);
-int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule);
 int routing_policy_serialize_rules(Set *rules, FILE *f);
 int routing_policy_load_rules(const char *state_file, Set **rules);
-void routing_policy_rule_purge(Manager *m, Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_tos);
 CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_table);
index 7d99707343400fbcdc8371e46c0f3f5eff367cd2..61382a89937cc199409110d1c17be39e9b1ff662 100644 (file)
@@ -108,7 +108,7 @@ static int sr_iov_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         return 1;
 }
 
-int sr_iov_configure(Link *link, SRIOV *sr_iov) {
+static int sr_iov_configure(Link *link, SRIOV *sr_iov) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         int r;
 
@@ -226,7 +226,28 @@ int sr_iov_configure(Link *link, SRIOV *sr_iov) {
         return 0;
 }
 
-int sr_iov_section_verify(SRIOV *sr_iov) {
+int link_configure_sr_iov(Link *link) {
+        SRIOV *sr_iov;
+        int r;
+
+        link->sr_iov_configured = false;
+        link->sr_iov_messages = 0;
+
+        ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section) {
+                r = sr_iov_configure(link, sr_iov);
+                if (r < 0)
+                        return r;
+        }
+
+        if (link->sr_iov_messages == 0)
+                link->sr_iov_configured = true;
+        else
+                log_link_debug(link, "Configuring SR-IOV");
+
+        return 0;
+}
+
+static int sr_iov_section_verify(SRIOV *sr_iov) {
         assert(sr_iov);
 
         if (section_is_invalid(sr_iov->section))
@@ -241,6 +262,16 @@ int sr_iov_section_verify(SRIOV *sr_iov) {
         return 0;
 }
 
+void network_drop_invalid_sr_iov(Network *network) {
+        SRIOV *sr_iov;
+
+        assert(network);
+
+        ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section)
+                if (sr_iov_section_verify(sr_iov) < 0)
+                        sr_iov_free(sr_iov);
+}
+
 int config_parse_sr_iov_uint32(
                 const char *unit,
                 const char *filename,
index a545d1292ae0e5aec540964609a5ead866bf48c6..8a48545d64b8019c78ad89930ad6814476f07325 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/if_link.h>
 
 #include "conf-parser.h"
+#include "ether-addr-util.h"
 #include "networkd-link.h"
 #include "networkd-network.h"
 #include "networkd-util.h"
@@ -33,9 +34,8 @@ typedef struct SRIOV {
 } SRIOV;
 
 SRIOV *sr_iov_free(SRIOV *sr_iov);
-
-int sr_iov_configure(Link *link, SRIOV *sr_iov);
-int sr_iov_section_verify(SRIOV *sr_iov);
+int link_configure_sr_iov(Link *link);
+void network_drop_invalid_sr_iov(Network *network);
 
 DEFINE_NETWORK_SECTION_FUNCTIONS(SRIOV, sr_iov_free);
 
diff --git a/src/network/networkd-sysctl.c b/src/network/networkd-sysctl.c
new file mode 100644 (file)
index 0000000..c8facbf
--- /dev/null
@@ -0,0 +1,284 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <netinet/in.h>
+#include <linux/if.h>
+
+#include "missing_network.h"
+#include "networkd-link.h"
+#include "networkd-network.h"
+#include "networkd-sysctl.h"
+#include "socket-util.h"
+#include "string-table.h"
+#include "sysctl-util.h"
+
+static int link_update_ipv6_sysctl(Link *link) {
+        assert(link);
+
+        if (link->flags & IFF_LOOPBACK)
+                return 0;
+
+        if (!link_ipv6_enabled(link))
+                return 0;
+
+        return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", false);
+}
+
+static int link_set_proxy_arp(Link *link) {
+        assert(link);
+
+        if (link->flags & IFF_LOOPBACK)
+                return 0;
+
+        if (!link->network)
+                return 0;
+
+        if (link->network->proxy_arp < 0)
+                return 0;
+
+        return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0);
+}
+
+bool link_ip_forward_enabled(Link *link, int family) {
+        assert(link);
+        assert(IN_SET(family, AF_INET, AF_INET6));
+
+        if (family == AF_INET6 && !socket_ipv6_is_supported())
+                return false;
+
+        if (link->flags & IFF_LOOPBACK)
+                return false;
+
+        if (!link->network)
+                return false;
+
+        return link->network->ip_forward & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6);
+}
+
+static int link_set_ipv4_forward(Link *link) {
+        assert(link);
+
+        if (!link_ip_forward_enabled(link, AF_INET))
+                return 0;
+
+        /* We propagate the forwarding flag from one interface to the
+         * global setting one way. This means: as long as at least one
+         * interface was configured at any time that had IP forwarding
+         * enabled the setting will stay on for good. We do this
+         * primarily to keep IPv4 and IPv6 packet forwarding behaviour
+         * somewhat in sync (see below). */
+
+        return sysctl_write_ip_property(AF_INET, NULL, "ip_forward", "1");
+}
+
+static int link_set_ipv6_forward(Link *link) {
+        assert(link);
+
+        if (!link_ip_forward_enabled(link, AF_INET6))
+                return 0;
+
+        /* On Linux, the IPv6 stack does not know a per-interface
+         * packet forwarding setting: either packet forwarding is on
+         * for all, or off for all. We hence don't bother with a
+         * per-interface setting, but simply propagate the interface
+         * flag, if it is set, to the global flag, one-way. Note that
+         * while IPv4 would allow a per-interface flag, we expose the
+         * same behaviour there and also propagate the setting from
+         * one to all, to keep things simple (see above). */
+
+        return sysctl_write_ip_property(AF_INET6, "all", "forwarding", "1");
+}
+
+static int link_set_ipv6_privacy_extensions(Link *link) {
+        assert(link);
+
+        if (!socket_ipv6_is_supported())
+                return 0;
+
+        if (link->flags & IFF_LOOPBACK)
+                return 0;
+
+        if (!link->network)
+                return 0;
+
+        return sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) link->network->ipv6_privacy_extensions);
+}
+
+static int link_set_ipv6_accept_ra(Link *link) {
+        assert(link);
+
+        /* Make this a NOP if IPv6 is not available */
+        if (!socket_ipv6_is_supported())
+                return 0;
+
+        if (link->flags & IFF_LOOPBACK)
+                return 0;
+
+        if (!link->network)
+                return 0;
+
+        return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0");
+}
+
+static int link_set_ipv6_dad_transmits(Link *link) {
+        assert(link);
+
+        /* Make this a NOP if IPv6 is not available */
+        if (!socket_ipv6_is_supported())
+                return 0;
+
+        if (link->flags & IFF_LOOPBACK)
+                return 0;
+
+        if (!link->network)
+                return 0;
+
+        if (link->network->ipv6_dad_transmits < 0)
+                return 0;
+
+        return sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits);
+}
+
+static int link_set_ipv6_hop_limit(Link *link) {
+        assert(link);
+
+        /* Make this a NOP if IPv6 is not available */
+        if (!socket_ipv6_is_supported())
+                return 0;
+
+        if (link->flags & IFF_LOOPBACK)
+                return 0;
+
+        if (!link->network)
+                return 0;
+
+        if (link->network->ipv6_hop_limit < 0)
+                return 0;
+
+        return sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit);
+}
+
+static int link_set_ipv4_accept_local(Link *link) {
+        assert(link);
+
+        if (link->flags & IFF_LOOPBACK)
+                return 0;
+
+        if (link->network->ipv4_accept_local < 0)
+                return 0;
+
+        return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "accept_local", link->network->ipv4_accept_local > 0);
+}
+
+int link_set_sysctl(Link *link) {
+        int r;
+
+        assert(link);
+
+        /* If IPv6 configured that is static IPv6 address and IPv6LL autoconfiguration is enabled
+         * for this interface, then enable IPv6 */
+        r = link_update_ipv6_sysctl(link);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Cannot enable IPv6, ignoring: %m");
+
+        r = link_set_proxy_arp(link);
+        if (r < 0)
+               log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface, ignoring: %m");
+
+        r = link_set_ipv4_forward(link);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m");
+
+        r = link_set_ipv6_forward(link);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m");;
+
+        r = link_set_ipv6_privacy_extensions(link);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extension for interface, ignorign: %m");
+
+        r = link_set_ipv6_accept_ra(link);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface, ignoring: %m");
+
+        r = link_set_ipv6_dad_transmits(link);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface, ignoring: %m");
+
+        r = link_set_ipv6_hop_limit(link);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface, ignoring: %m");
+
+        r = link_set_ipv4_accept_local(link);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Cannot set IPv4 accept_local flag for interface, ignoring: %m");
+
+        return 0;
+}
+
+int link_set_ipv6_mtu(Link *link) {
+        int r;
+
+        assert(link);
+
+        /* Make this a NOP if IPv6 is not available */
+        if (!socket_ipv6_is_supported())
+                return 0;
+
+        if (link->flags & IFF_LOOPBACK)
+                return 0;
+
+        if (link->network->ipv6_mtu == 0)
+                return 0;
+
+        r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", link->network->ipv6_mtu);
+        if (r < 0)
+                return r;
+
+        link->ipv6_mtu_set = true;
+
+        return 0;
+}
+
+static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
+        [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
+        [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
+        [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExtensions,
+                                        IPV6_PRIVACY_EXTENSIONS_YES);
+
+int config_parse_ipv6_privacy_extensions(
+                const char* unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        IPv6PrivacyExtensions s, *ipv6_privacy_extensions = data;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(ipv6_privacy_extensions);
+
+        s = ipv6_privacy_extensions_from_string(rvalue);
+        if (s < 0) {
+                if (streq(rvalue, "kernel"))
+                        s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
+                else {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
+                        return 0;
+                }
+        }
+
+        *ipv6_privacy_extensions = s;
+
+        return 0;
+}
diff --git a/src/network/networkd-sysctl.h b/src/network/networkd-sysctl.h
new file mode 100644 (file)
index 0000000..a409d8f
--- /dev/null
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdbool.h>
+
+#include "conf-parser.h"
+
+typedef struct Link Link;
+
+typedef enum IPv6PrivacyExtensions {
+        /* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */
+        IPV6_PRIVACY_EXTENSIONS_NO,
+        IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC,
+        IPV6_PRIVACY_EXTENSIONS_YES, /* aka prefer-temporary */
+        _IPV6_PRIVACY_EXTENSIONS_MAX,
+        _IPV6_PRIVACY_EXTENSIONS_INVALID = -1,
+} IPv6PrivacyExtensions;
+
+bool link_ip_forward_enabled(Link *link, int family);
+int link_set_sysctl(Link *link);
+int link_set_ipv6_mtu(Link *link);
+
+const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_;
+IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_privacy_extensions);
index ce9319d942a3aa450fe438484d7e5cbd1d2cbde8..bae4ee5cbacf1ac2174eeb44d9880054c34d3883 100644 (file)
@@ -151,3 +151,15 @@ int network_config_section_new(const char *filename, unsigned line, NetworkConfi
 void network_config_section_free(NetworkConfigSection *cs) {
         free(cs);
 }
+
+unsigned hashmap_find_free_section_line(Hashmap *hashmap) {
+        NetworkConfigSection *cs;
+        unsigned n = 0;
+        void *entry;
+
+        HASHMAP_FOREACH_KEY(entry, cs, hashmap)
+                if (n < cs->line)
+                        n = cs->line;
+
+        return n + 1;
+}
index 0433f883a32856417fea5b130e0fad9a59545714..ce169fa731b4b0d50973d6589847df137fc3d69e 100644 (file)
@@ -2,10 +2,13 @@
 #pragma once
 
 #include "sd-dhcp-lease.h"
+#include "sd-netlink.h"
 
 #include "conf-parser.h"
-#include "hash-funcs.h"
+#include "hashmap.h"
+#include "log.h"
 #include "macro.h"
+#include "string-util.h"
 
 typedef enum AddressFamily {
         /* This is a bitmask, though it usually doesn't feel that way! */
@@ -49,6 +52,7 @@ int network_config_section_new(const char *filename, unsigned line, NetworkConfi
 void network_config_section_free(NetworkConfigSection *network);
 DEFINE_TRIVIAL_CLEANUP_FUNC(NetworkConfigSection*, network_config_section_free);
 extern const struct hash_ops network_config_hash_ops;
+unsigned hashmap_find_free_section_line(Hashmap *hashmap);
 
 static inline bool section_is_invalid(NetworkConfigSection *section) {
         /* If this returns false, then it does _not_ mean the section is valid. */
@@ -70,3 +74,10 @@ static inline bool section_is_invalid(NetworkConfigSection *section) {
         }                                                               \
         DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func);                  \
         DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid);
+
+static inline int log_message_warning_errno(sd_netlink_message *m, int err, const char *msg) {
+        const char *err_msg = NULL;
+
+        (void) sd_netlink_message_read_string(m, NLMSGERR_ATTR_MSG, &err_msg);
+        return log_warning_errno(err, "%s: %s%s%m", msg, strempty(err_msg), err_msg ? " " : "");
+}
index 14a8687458d5b8aebfed5cb7e947845bd18e1418..53b65286b37b42f767ea1d9207817237cbdc14ce 100644 (file)
@@ -6,6 +6,7 @@
 #include "sd-bus.h"
 
 #include "bus-util.h"
+#include "ether-addr-util.h"
 #include "netlink-internal.h"
 #include "netlink-util.h"
 #include "networkd-link.h"
index 445aee16ad0435b4f7361cb6098dac5d859837dc..f78ff9db547024b9071960d4d1a6a4bd39e812f8 100644 (file)
@@ -88,29 +88,9 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_error_errno(r, "Could not load configuration files: %m");
 
-        r = manager_rtnl_enumerate_links(m);
+        r = manager_enumerate(m);
         if (r < 0)
-                return log_error_errno(r, "Could not enumerate links: %m");
-
-        r = manager_rtnl_enumerate_addresses(m);
-        if (r < 0)
-                return log_error_errno(r, "Could not enumerate addresses: %m");
-
-        r = manager_rtnl_enumerate_neighbors(m);
-        if (r < 0)
-                return log_error_errno(r, "Could not enumerate neighbors: %m");
-
-        r = manager_rtnl_enumerate_routes(m);
-        if (r < 0)
-                return log_error_errno(r, "Could not enumerate routes: %m");
-
-        r = manager_rtnl_enumerate_rules(m);
-        if (r < 0)
-                return log_error_errno(r, "Could not enumerate rules: %m");
-
-        r = manager_rtnl_enumerate_nexthop(m);
-        if (r < 0)
-                return log_error_errno(r, "Could not enumerate nexthop: %m");
+                return r;
 
         r = manager_start(m);
         if (r < 0)
index 30a00133d6579a9dbfe54956ef04ccf8bdfbd463..974bb8c8220d53cdcc0c7c5395276a47946f8aed 100644 (file)
@@ -21,7 +21,7 @@ void traffic_control_free(TrafficControl *tc) {
         }
 }
 
-int traffic_control_configure(Link *link, TrafficControl *tc) {
+static int traffic_control_configure(Link *link, TrafficControl *tc) {
         assert(link);
         assert(tc);
 
@@ -35,7 +35,28 @@ int traffic_control_configure(Link *link, TrafficControl *tc) {
         }
 }
 
-int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact) {
+int link_configure_traffic_control(Link *link) {
+        TrafficControl *tc;
+        int r;
+
+        link->tc_configured = false;
+        link->tc_messages = 0;
+
+        ORDERED_HASHMAP_FOREACH(tc, link->network->tc_by_section) {
+                r = traffic_control_configure(link, tc);
+                if (r < 0)
+                        return r;
+        }
+
+        if (link->tc_messages == 0)
+                link->tc_configured = true;
+        else
+                log_link_debug(link, "Configuring traffic control");
+
+        return 0;
+}
+
+static int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact) {
         assert(tc);
 
         switch(tc->kind) {
@@ -47,3 +68,14 @@ int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, boo
                 assert_not_reached("Invalid traffic control type");
         }
 }
+
+void network_drop_invalid_traffic_control(Network *network) {
+        bool has_root = false, has_clsact = false;
+        TrafficControl *tc;
+
+        assert(network);
+
+        ORDERED_HASHMAP_FOREACH(tc, network->tc_by_section)
+                if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0)
+                        traffic_control_free(tc);
+}
index defa0b774a5786a52064ab06910a3b514dfe1d50..916ad3300aafffa944d7208f444ff3bae4ffaee3 100644 (file)
@@ -28,5 +28,5 @@ typedef struct TrafficControl {
 #define TC(tc) (&(tc)->meta)
 
 void traffic_control_free(TrafficControl *tc);
-int traffic_control_configure(Link *link, TrafficControl *tc);
-int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact);
+int link_configure_traffic_control(Link *link);
+void network_drop_invalid_traffic_control(Network *network);
index 2ac47e72f523b4d4f1fb6fc4c06a6ee67c54d64a..e93e8c0cc094089cd7ba6ce6695fa17be53f3f2e 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "alloc-util.h"
 #include "dhcp-lease-internal.h"
+#include "ether-addr-util.h"
 #include "hostname-util.h"
 #include "network-internal.h"
 #include "networkd-manager.h"
@@ -252,6 +253,6 @@ int main(void) {
 
         test_network_get(manager, loopback);
 
-        assert_se(manager_rtnl_enumerate_links(manager) >= 0);
+        assert_se(manager_enumerate(manager) >= 0);
         return 0;
 }
index 5d338e6f1a3a90f92f9bb77568d80d4e1432cfe1..030e50688a4e9ecd0a94f6340dd0a970a8b3687c 100644 (file)
@@ -176,14 +176,16 @@ static void test_config_parse_address_one(const char *rvalue, int family, unsign
         assert_se(network->filename = strdup("hogehoge.network"));
         assert_se(config_parse_match_ifnames("network", "filename", 1, "section", 1, "Name", 0, "*", &network->match_name, network) == 0);
         assert_se(config_parse_address("network", "filename", 1, "section", 1, "Address", 0, rvalue, network, network) == 0);
-        assert_se(network->n_static_addresses == 1);
+        assert_se(ordered_hashmap_size(network->addresses_by_section) == 1);
         assert_se(network_verify(network) >= 0);
-        assert_se(network->n_static_addresses == n_addresses);
+        assert_se(ordered_hashmap_size(network->addresses_by_section) == n_addresses);
         if (n_addresses > 0) {
-                assert_se(network->static_addresses);
-                assert_se(network->static_addresses->prefixlen == prefixlen);
-                assert_se(network->static_addresses->family == family);
-                assert_se(in_addr_equal(family, &network->static_addresses->in_addr, u));
+                Address *a;
+
+                assert_se(a = ordered_hashmap_first(network->addresses_by_section));
+                assert_se(a->prefixlen == prefixlen);
+                assert_se(a->family == family);
+                assert_se(in_addr_equal(family, &a->in_addr, u));
                 /* TODO: check Address.in_addr and Address.broadcast */
         }
 }
index 78755927c77f3667b305b4ea0052e52a3a9d78b5..40341d607380a43554ed69433f85c4cd9a7b66fd 100644 (file)
@@ -1,13 +1,8 @@
-/***
-  SPDX-License-Identifier: LGPL-2.1+
-***/
+/* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include "fd-util.h"
 #include "fileio.h"
-#include "log.h"
-#include "macro.h"
-#include "network-internal.h"
-#include "networkd-manager.h"
+#include "networkd-routing-policy-rule.h"
 #include "string-util.h"
 #include "tests.h"
 #include "tmpfile-util.h"
index fa2002d5785adfd949930f6dd71d75c528c1bfa5..62b949f58cd2250ae16c4a4fe002fe94a3ad93e6 100644 (file)
@@ -63,16 +63,19 @@ int change_uid_gid_raw(
                 uid_t uid,
                 gid_t gid,
                 const gid_t *supplementary_gids,
-                size_t n_supplementary_gids) {
+                size_t n_supplementary_gids,
+                bool chown_stdio) {
 
         if (!uid_is_valid(uid))
                 uid = 0;
         if (!gid_is_valid(gid))
                 gid = 0;
 
-        (void) fchown(STDIN_FILENO, uid, gid);
-        (void) fchown(STDOUT_FILENO, uid, gid);
-        (void) fchown(STDERR_FILENO, uid, gid);
+        if (chown_stdio) {
+                (void) fchown(STDIN_FILENO, uid, gid);
+                (void) fchown(STDOUT_FILENO, uid, gid);
+                (void) fchown(STDERR_FILENO, uid, gid);
+        }
 
         if (setgroups(n_supplementary_gids, supplementary_gids) < 0)
                 return log_error_errno(errno, "Failed to set auxiliary groups: %m");
@@ -86,7 +89,7 @@ int change_uid_gid_raw(
         return 0;
 }
 
-int change_uid_gid(const char *user, char **_home) {
+int change_uid_gid(const char *user, bool chown_stdio, char **ret_home) {
         char *x, *u, *g, *h;
         _cleanup_free_ gid_t *gids = NULL;
         _cleanup_free_ char *home = NULL, *line = NULL;
@@ -99,7 +102,7 @@ int change_uid_gid(const char *user, char **_home) {
         pid_t pid;
         int r;
 
-        assert(_home);
+        assert(ret_home);
 
         if (!user || STR_IN_SET(user, "root", "0")) {
                 /* Reset everything fully to 0, just in case */
@@ -108,7 +111,7 @@ int change_uid_gid(const char *user, char **_home) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to become root: %m");
 
-                *_home = NULL;
+                *ret_home = NULL;
                 return 0;
         }
 
@@ -232,12 +235,12 @@ int change_uid_gid(const char *user, char **_home) {
         if (r < 0 && !IN_SET(r, -EEXIST, -ENOTDIR))
                 return log_error_errno(r, "Failed to make home directory: %m");
 
-        r = change_uid_gid_raw(uid, gid, gids, n_gids);
+        r = change_uid_gid_raw(uid, gid, gids, n_gids, chown_stdio);
         if (r < 0)
                 return r;
 
-        if (_home)
-                *_home = TAKE_PTR(home);
+        if (ret_home)
+                *ret_home = TAKE_PTR(home);
 
         return 0;
 }
index 9a2b05ebbb15457e430427faf6e85251f7aec109..c82d50bdf131ebe805a8a2ad699a0781c7597b30 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
-int change_uid_gid_raw(uid_t uid, gid_t gid, const gid_t *supplementary_gids, size_t n_supplementary_gids);
-int change_uid_gid(const char *user, char **ret_home);
+int change_uid_gid_raw(uid_t uid, gid_t gid, const gid_t *supplementary_gids, size_t n_supplementary_gids, bool chown_stdio);
+int change_uid_gid(const char *user, bool chown_stdio, char **ret_home);
index 36a26046641195fb8574f964e2eccb5ca626110d..12a04b0baeda12cc83d8e0188f727d8bd00cc25b 100644 (file)
@@ -3321,9 +3321,9 @@ static int inner_child(
                 return log_error_errno(errno, "Failed to set PR_SET_KEEPCAPS: %m");
 
         if (uid_is_valid(arg_uid) || gid_is_valid(arg_gid))
-                r = change_uid_gid_raw(arg_uid, arg_gid, arg_supplementary_gids, arg_n_supplementary_gids);
+                r = change_uid_gid_raw(arg_uid, arg_gid, arg_supplementary_gids, arg_n_supplementary_gids, arg_console_mode != CONSOLE_PIPE);
         else
-                r = change_uid_gid(arg_user, &home);
+                r = change_uid_gid(arg_user, arg_console_mode != CONSOLE_PIPE, &home);
         if (r < 0)
                 return r;
 
index db6a103c42a74ed382359a1e32fba5795efcb63c..bcc51f973c9054d5a3019fab07b11e527486d296 100644 (file)
@@ -363,7 +363,7 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) {
 
         if (c->utc)
                 fputs(" UTC", f);
-        else if (c->timezone != NULL) {
+        else if (c->timezone) {
                 fputc(' ', f);
                 fputs(c->timezone, f);
         } else if (IN_SET(c->dst, 0, 1)) {
index 636c0e2a7fdbf9a1ecb0d86639dba669e533cd84..cb75d9a11bdf453bd06d5360837626040043fd03 100644 (file)
@@ -124,7 +124,7 @@ int path_chown_recursive(
         if (fd < 0)
                 return -errno;
 
-        if (!uid_is_valid(uid) && !gid_is_valid(gid) && (mask & 07777) == 07777)
+        if (!uid_is_valid(uid) && !gid_is_valid(gid) && FLAGS_SET(mask, 07777))
                 return 0; /* nothing to do */
 
         if (fstat(fd, &st) < 0)
@@ -160,7 +160,7 @@ int fd_chown_recursive(
         if (!S_ISDIR(st.st_mode))
                 return -ENOTDIR;
 
-        if (!uid_is_valid(uid) && !gid_is_valid(gid) && (mask & 07777) == 07777)
+        if (!uid_is_valid(uid) && !gid_is_valid(gid) && FLAGS_SET(mask, 07777))
                 return 0; /* nothing to do */
 
         /* Shortcut, as above */
index bb372d40010cf48aa334278b5cb9433f6a5d91a3..305fd8a0f18561d2651a1a555e69859fba9485e7 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <errno.h>
+#include <fcntl.h>
 #include <limits.h>
 #include <signal.h>
 #include <stddef.h>
@@ -27,6 +28,8 @@
 struct PTYForward {
         sd_event *event;
 
+        int input_fd;
+        int output_fd;
         int master;
 
         PTYForwardFlags flags;
@@ -40,6 +43,9 @@ struct PTYForward {
         struct termios saved_stdin_attr;
         struct termios saved_stdout_attr;
 
+        bool close_input_fd:1;
+        bool close_output_fd:1;
+
         bool saved_stdin:1;
         bool saved_stdout:1;
 
@@ -73,25 +79,36 @@ struct PTYForward {
 
 static void pty_forward_disconnect(PTYForward *f) {
 
-        if (f) {
-                f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
-                f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
+        if (!f)
+                return;
+
+        f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
+        f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
 
-                f->master_event_source = sd_event_source_unref(f->master_event_source);
-                f->sigwinch_event_source = sd_event_source_unref(f->sigwinch_event_source);
-                f->event = sd_event_unref(f->event);
+        f->master_event_source = sd_event_source_unref(f->master_event_source);
+        f->sigwinch_event_source = sd_event_source_unref(f->sigwinch_event_source);
+        f->event = sd_event_unref(f->event);
 
+        if (f->output_fd >= 0) {
                 if (f->saved_stdout)
-                        tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr);
+                        (void) tcsetattr(f->output_fd, TCSANOW, &f->saved_stdout_attr);
+
+                /* STDIN/STDOUT should not be non-blocking normally, so let's reset it */
+                (void) fd_nonblock(f->output_fd, false);
+                if (f->close_output_fd)
+                        f->output_fd = safe_close(f->output_fd);
+        }
+
+        if (f->input_fd >= 0) {
                 if (f->saved_stdin)
-                        tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr);
+                        (void) tcsetattr(f->input_fd, TCSANOW, &f->saved_stdin_attr);
 
-                f->saved_stdout = f->saved_stdin = false;
+                (void) fd_nonblock(f->input_fd, false);
+                if (f->close_input_fd)
+                        f->input_fd = safe_close(f->input_fd);
         }
 
-        /* STDIN/STDOUT should not be nonblocking normally, so let's unconditionally reset it */
-        (void) fd_nonblock(STDIN_FILENO, false);
-        (void) fd_nonblock(STDOUT_FILENO, false);
+        f->saved_stdout = f->saved_stdin = false;
 }
 
 static int pty_forward_done(PTYForward *f, int rcode) {
@@ -191,7 +208,7 @@ static int shovel(PTYForward *f) {
 
                 if (f->stdin_readable && f->in_buffer_full < LINE_MAX) {
 
-                        k = read(STDIN_FILENO, f->in_buffer + f->in_buffer_full, LINE_MAX - f->in_buffer_full);
+                        k = read(f->input_fd, f->in_buffer + f->in_buffer_full, LINE_MAX - f->in_buffer_full);
                         if (k < 0) {
 
                                 if (errno == EAGAIN)
@@ -275,7 +292,7 @@ static int shovel(PTYForward *f) {
 
                 if (f->stdout_writable && f->out_buffer_full > 0) {
 
-                        k = write(STDOUT_FILENO, f->out_buffer, f->out_buffer_full);
+                        k = write(f->output_fd, f->out_buffer, f->out_buffer_full);
                         if (k < 0) {
 
                                 if (errno == EAGAIN)
@@ -345,7 +362,7 @@ static int on_stdin_event(sd_event_source *e, int fd, uint32_t revents, void *us
         assert(e);
         assert(e == f->stdin_event_source);
         assert(fd >= 0);
-        assert(fd == STDIN_FILENO);
+        assert(fd == f->input_fd);
 
         if (revents & (EPOLLIN|EPOLLHUP))
                 f->stdin_readable = true;
@@ -360,7 +377,7 @@ static int on_stdout_event(sd_event_source *e, int fd, uint32_t revents, void *u
         assert(e);
         assert(e == f->stdout_event_source);
         assert(fd >= 0);
-        assert(fd == STDOUT_FILENO);
+        assert(fd == f->output_fd);
 
         if (revents & (EPOLLOUT|EPOLLHUP))
                 f->stdout_writable = true;
@@ -377,7 +394,7 @@ static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo *
         assert(e == f->sigwinch_event_source);
 
         /* The window size changed, let's forward that. */
-        if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
+        if (ioctl(f->output_fd, TIOCGWINSZ, &ws) >= 0)
                 (void) ioctl(f->master, TIOCSWINSZ, &ws);
 
         return 0;
@@ -400,6 +417,8 @@ int pty_forward_new(
         *f = (struct PTYForward) {
                 .flags = flags,
                 .master = -1,
+                .input_fd = -1,
+                .output_fd = -1,
         };
 
         if (event)
@@ -410,14 +429,42 @@ int pty_forward_new(
                         return r;
         }
 
-        if (!(flags & PTY_FORWARD_READ_ONLY)) {
-                r = fd_nonblock(STDIN_FILENO, true);
-                if (r < 0)
-                        return r;
-
-                r = fd_nonblock(STDOUT_FILENO, true);
-                if (r < 0)
-                        return r;
+        if (FLAGS_SET(flags, PTY_FORWARD_READ_ONLY))
+                f->output_fd = STDOUT_FILENO;
+        else {
+                /* If we shall be invoked in interactive mode, let's switch on non-blocking mode, so that we
+                 * never end up staving one direction while we block on the other. However, let's be careful
+                 * here and not turn on O_NONBLOCK for stdin/stdout directly, but of re-opened copies of
+                 * them. This has two advantages: when we are killed abruptly the stdin/stdout fds won't be
+                 * left in O_NONBLOCK state for the next process using them. In addition, if some process
+                 * running in the background wants to continue writing to our stdout it can do so without
+                 * being confused by O_NONBLOCK. */
+
+                f->input_fd = fd_reopen(STDIN_FILENO, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+                if (f->input_fd < 0) {
+                        /* Handle failures gracefully, after all certain fd types cannot be reopened
+                         * (sockets, …) */
+                        log_debug_errno(f->input_fd, "Failed to reopen stdin, using original fd: %m");
+
+                        r = fd_nonblock(STDIN_FILENO, true);
+                        if (r < 0)
+                                return r;
+
+                        f->input_fd = STDIN_FILENO;
+                } else
+                        f->close_input_fd = true;
+
+                f->output_fd = fd_reopen(STDOUT_FILENO, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+                if (f->output_fd < 0) {
+                        log_debug_errno(f->output_fd, "Failed to reopen stdout, using original fd: %m");
+
+                        r = fd_nonblock(STDOUT_FILENO, true);
+                        if (r < 0)
+                                return r;
+
+                        f->output_fd = STDOUT_FILENO;
+                } else
+                        f->close_output_fd = true;
         }
 
         r = fd_nonblock(master, true);
@@ -426,7 +473,7 @@ int pty_forward_new(
 
         f->master = master;
 
-        if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) {
+        if (ioctl(f->output_fd, TIOCGWINSZ, &ws) < 0) {
                 /* If we can't get the resolution from the output fd, then use our internal, regular width/height,
                  * i.e. something derived from $COLUMNS and $LINES if set. */
 
@@ -439,7 +486,9 @@ int pty_forward_new(
         (void) ioctl(master, TIOCSWINSZ, &ws);
 
         if (!(flags & PTY_FORWARD_READ_ONLY)) {
-                if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) {
+                assert(f->input_fd >= 0);
+
+                if (tcgetattr(f->input_fd, &f->saved_stdin_attr) >= 0) {
                         struct termios raw_stdin_attr;
 
                         f->saved_stdin = true;
@@ -447,10 +496,10 @@ int pty_forward_new(
                         raw_stdin_attr = f->saved_stdin_attr;
                         cfmakeraw(&raw_stdin_attr);
                         raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag;
-                        tcsetattr(STDIN_FILENO, TCSANOW, &raw_stdin_attr);
+                        tcsetattr(f->input_fd, TCSANOW, &raw_stdin_attr);
                 }
 
-                if (tcgetattr(STDOUT_FILENO, &f->saved_stdout_attr) >= 0) {
+                if (tcgetattr(f->output_fd, &f->saved_stdout_attr) >= 0) {
                         struct termios raw_stdout_attr;
 
                         f->saved_stdout = true;
@@ -459,10 +508,10 @@ int pty_forward_new(
                         cfmakeraw(&raw_stdout_attr);
                         raw_stdout_attr.c_iflag = f->saved_stdout_attr.c_iflag;
                         raw_stdout_attr.c_lflag = f->saved_stdout_attr.c_lflag;
-                        tcsetattr(STDOUT_FILENO, TCSANOW, &raw_stdout_attr);
+                        tcsetattr(f->output_fd, TCSANOW, &raw_stdout_attr);
                 }
 
-                r = sd_event_add_io(f->event, &f->stdin_event_source, STDIN_FILENO, EPOLLIN|EPOLLET, on_stdin_event, f);
+                r = sd_event_add_io(f->event, &f->stdin_event_source, f->input_fd, EPOLLIN|EPOLLET, on_stdin_event, f);
                 if (r < 0 && r != -EPERM)
                         return r;
 
@@ -470,7 +519,7 @@ int pty_forward_new(
                         (void) sd_event_source_set_description(f->stdin_event_source, "ptyfwd-stdin");
         }
 
-        r = sd_event_add_io(f->event, &f->stdout_event_source, STDOUT_FILENO, EPOLLOUT|EPOLLET, on_stdout_event, f);
+        r = sd_event_add_io(f->event, &f->stdout_event_source, f->output_fd, EPOLLOUT|EPOLLET, on_stdout_event, f);
         if (r == -EPERM)
                 /* stdout without epoll support. Likely redirected to regular file. */
                 f->stdout_writable = true;
index b22ef7cec1db7940e8a41ce91cddc41ac3cd63cd..9e3555d986782dbbc955304d3e67008c7e379979 100644 (file)
@@ -691,7 +691,7 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = {
         },
         [SYSCALL_FILTER_SET_PROCESS] = {
                 .name = "@process",
-                .help = "Process control, execution, namespaceing operations",
+                .help = "Process control, execution, namespacing operations",
                 .value =
                 "arch_prctl\0"
                 "capget\0"      /* Able to query arbitrary processes */
index 397220732985fa89736181124509407cbe5e5609..7571e609a26fe8236a4c691881b00a9a6c91a9ca 100644 (file)
@@ -46,7 +46,7 @@ static void test_tmpdir(const char *id, const char *A, const char *B) {
                 c = strjoina(a, "/tmp");
                 assert_se(stat(c, &x) >= 0);
                 assert_se(S_ISDIR(x.st_mode));
-                assert_se((x.st_mode & 01777) == 01777);
+                assert_se(FLAGS_SET(x.st_mode, 01777));
                 assert_se(rmdir(c) >= 0);
                 assert_se(rmdir(a) >= 0);
         }
@@ -57,7 +57,7 @@ static void test_tmpdir(const char *id, const char *A, const char *B) {
                 d = strjoina(b, "/tmp");
                 assert_se(stat(d, &y) >= 0);
                 assert_se(S_ISDIR(y.st_mode));
-                assert_se((y.st_mode & 01777) == 01777);
+                assert_se(FLAGS_SET(y.st_mode, 01777));
                 assert_se(rmdir(d) >= 0);
                 assert_se(rmdir(b) >= 0);
         }
index bb08da28b52273fbe1ddcc80a51c3f5e8a4bd3cd..9c33a560a8ccbfc0dc3f1399f36be8fd5e1d5501 100644 (file)
@@ -236,7 +236,7 @@ static int get_file_options(const char *vendor, const char *model,
                         retval = -1;
                         break;
                 }
-                if (vendor == NULL) {
+                if (!vendor) {
                         if (!vendor_in)
                                 break;
                 } else if (vendor_in &&
index 0da4ea787b0d4554e31894f268cc10353619b931..a588d18fd117b1637af09f7729adf66b902072f5 100644 (file)
@@ -45,12 +45,12 @@ static int exec_list(sd_device_enumerator *e, const char *action, Set **settle_s
 
                 r = write_string_file(filename, action, WRITE_STRING_FILE_DISABLE_BUFFER);
                 if (r < 0) {
-                        bool ignore = r == -ENOENT;
+                        bool ignore = IN_SET(r, -ENOENT, -ENODEV);
 
                         log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, r,
                                        "Failed to write '%s' to '%s'%s: %m",
                                        action, filename, ignore ? ", ignoring" : "");
-                        if (IN_SET(r, -EACCES, -ENODEV, -EROFS))
+                        if (IN_SET(r, -EACCES, -EROFS))
                                 /* Inovoked by unpriviledged user, or read only filesystem. Return earlier. */
                                 return r;
                         if (ret == 0 && !ignore)
index f8119d13ee789e67f47f93eb77f537e041b8f625..506cdd2264db11653f6f8680a067f2b2c6fad0c1 100644 (file)
@@ -155,7 +155,6 @@ Address=10.3.3.98/16
 Address=10.3.3.99/16
 Address=10.3.3.100/16
 Address=10.3.3.101/16
-Address=10.3.3.101/16
 Address=10.3.3.102/16
 Address=10.3.3.103/16
 Address=10.3.3.104/16
index c962c3d8458c2df163e91990b712294bf28b7ac1..099331db5053c23acd393871e203722df8f59612 100644 (file)
@@ -3,6 +3,7 @@ Name=eni99np1
 
 [Network]
 Address=192.168.100.100/24
+IPv6AcceptRA=no
 
 [SR-IOV]
 VirtualFunction=0
index 3e57b335ceeef1b25bddb1da68447c9f50cdfb5a..8058122e46e406fd290eb48c843b17469fe705aa 100755 (executable)
@@ -382,6 +382,9 @@ def remove_routing_policy_rule_tables(tables):
         rc = 0
         while rc == 0:
             rc = call('ip rule del table', table, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+        rc = 0
+        while rc == 0:
+            rc = call('ip -6 rule del table', table, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
 
 def remove_routes(routes):
     for route_type, addr in routes:
@@ -745,6 +748,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
     links = [
         '6rdtun99',
         'bareudp99',
+        'bond98',
         'bond99',
         'bridge99',
         'dropin-test',
@@ -870,6 +874,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '25-sit-tunnel.netdev',
         '25-tap.netdev',
         '25-tun.netdev',
+        '25-tunnel-any-any.network',
         '25-tunnel-local-any.network',
         '25-tunnel-remote-any.network',
         '25-tunnel.network',