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"
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
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
• 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).
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
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:
[![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)
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
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
@@
-/* 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;
@@
(
+/* 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)
|
+/* 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)
|
)
top="$(git rev-parse --show-toplevel)"
-iso_defs="$top/coccinelle/systemd-definitions.iso"
args=
# Create an array from files tracked by git...
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
@@
-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);
+)
+++ /dev/null
-/* 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
@@
+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);
<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
<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>
<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
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>
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>
</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>
</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>
</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>
<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>
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
<literal>vmware</literal>,
<literal>microsoft</literal>,
<literal>oracle</literal>,
+ <literal>powervm</literal>,
<literal>xen</literal>,
<literal>bochs</literal>,
<literal>uml</literal>,
<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
['systemd-cgtop', ''],
['systemd-delta', ''],
['systemd-detect-virt', ''],
+ ['systemd-id128', ''],
['systemd-nspawn', ''],
['systemd-path', ''],
['systemd-run', ''],
[bash_systemctl, ''],
['bootctl', 'ENABLE_EFI'],
['coredumpctl', 'ENABLE_COREDUMP'],
+ ['homectl', 'ENABLE_HOMED'],
['hostnamectl', 'ENABLE_HOSTNAMED'],
['localectl', 'ENABLE_LOCALED'],
['loginctl', 'ENABLE_LOGIND'],
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;
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)
_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) {
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.
[VIRTUALIZATION_BHYVE] = "bhyve",
[VIRTUALIZATION_QNX] = "qnx",
[VIRTUALIZATION_ACRN] = "acrn",
+ [VIRTUALIZATION_POWERVM] = "powervm",
[VIRTUALIZATION_VM_OTHER] = "vm-other",
[VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
VIRTUALIZATION_BHYVE,
VIRTUALIZATION_QNX,
VIRTUALIZATION_ACRN,
+ VIRTUALIZATION_POWERVM,
VIRTUALIZATION_VM_OTHER,
VIRTUALIZATION_VM_LAST = VIRTUALIZATION_VM_OTHER,
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;
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);
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;
return r;
n += r;
- } else if ((c & 0xc0) == 0xc0) {
+ } else if (FLAGS_SET(c, 0xc0)) {
/* Pointer */
uint8_t d;
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);
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)
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) {
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);
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
#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"
#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"
#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;
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);
*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;
}
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;
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");
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,
#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);
#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);
.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) {
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(
}
/* 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;
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;
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;
assert(prefixlen > 0);
assert(found);
+ if (p->family != family)
+ return 0;
+
if (p->prefixlen >= prefixlen)
return 0;
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;
+}
/* 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);
#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
.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);
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);
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);
network_config_section_free(address->section);
free(address->label);
- free(address);
+ return mfree(address);
}
static uint32_t address_prefix(const Address *a) {
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;
}
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;
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,
return 0;
}
-int address_drop(Address *address) {
+static int address_drop(Address *address) {
Link *link;
bool ready;
int r;
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");
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;
/* 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)
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,
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);
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;
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");
}
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");
}
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;
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,
return 0;
}
-int config_parse_address(const char *unit,
+int config_parse_address(
+ const char *unit,
const char *filename,
unsigned line,
const char *section,
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();
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;
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;
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;
const char *rvalue,
void *data,
void *userdata) {
+
Network *network = userdata;
_cleanup_(address_free_or_set_invalidp) Address *n = NULL;
AddressFamily 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;
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);
+}
#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;
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;
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");
/* 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");
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");
Copyright © 2016 BISDN GmbH. All rights reserved.
***/
-#include <stdint.h>
-
#include "conf-parser.h"
#define BRIDGE_VLAN_BITMAP_MAX 4096
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);
/* 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,
#define DHCP_ROUTE_METRIC 1024
+typedef struct Link Link;
+typedef struct Manager Manager;
+
typedef enum DHCPUseDomains {
DHCP_USE_DOMAINS_NO,
DHCP_USE_DOMAINS_YES,
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_;
/* 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;
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;
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),
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;
#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"
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;
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;
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;
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;
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 */
* 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
return 0;
}
-int dhcp4_set_client_identifier(Link *link) {
+static int dhcp4_set_client_identifier(Link *link) {
int r;
assert(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;
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,
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,
} 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);
#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;
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;
return 1;
}
- r = link_request_set_routes(link);
+ r = link_set_routes(link);
if (r < 0) {
link_enter_failed(link);
return 1;
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;
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;
}
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);
return 1;
}
- r = link_request_set_routes(link);
+ r = link_set_routes(link);
if (r < 0) {
link_enter_failed(link);
return 1;
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;
}
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;
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);
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,
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);
#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(
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. */
/* 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);
}
/* 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;
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. */
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,
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,
_NEIGHBOR_CACHE_ENTRY_FLAGS_INVALID = -1,
} NeighborCacheEntryFlags;
-struct FdbEntry {
+typedef struct FdbEntry {
Network *network;
NetworkConfigSection *section;
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);
}
}
+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) {
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,
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);
#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);
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) {
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(
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;
}
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;
}
#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);
#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);
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())
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())
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. */
}
static Link *link_free(Link *link) {
- Address *address;
-
assert(link);
link_ntp_settings_clear(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);
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);
int link_stop_clients(Link *link, bool may_keep_dhcp) {
int r = 0, k;
- Address *ad;
assert(link);
assert(link->manager);
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);
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);
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)) {
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;
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;
}
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);
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);
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);
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;
}
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,
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) {
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);
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;
if (r < 0)
return r;
- r = link_configure_ipv4_dad(link);
- if (r < 0)
- return r;
-
return link_configure_continue(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);
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;
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);
*routes = NULL,
*dhcp4_address = NULL,
*ipv4ll_address = NULL;
- union in_addr_union address;
int r;
assert(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;
}
}
link_set_state(link, LINK_STATE_CONFIGURING);
- r = link_request_set_addresses(link);
+ r = link_set_static_configs(link);
if (r < 0)
return r;
}
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);
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;
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);
/************************************************************/
- 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);
} 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)
#include "sd-radv.h"
#include "sd-netlink.h"
-#include "list.h"
#include "log-link.h"
#include "network-util.h"
#include "networkd-util.h"
Set *addresses;
Set *addresses_foreign;
+ Set *pool_addresses;
Set *static_addresses;
Set *neighbors;
Set *neighbors_foreign;
bool ipv6_mtu_set:1;
bool bridge_mdb_configured:1;
- LIST_HEAD(Address, pool_addresses);
-
sd_dhcp_server *dhcp_server;
sd_ndisc *ndisc;
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);
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);
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);
#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"
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)
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)
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;
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-#include <stdbool.h>
-
#include "conf-parser.h"
typedef struct Link Link;
_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);
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;
}
#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;
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);
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;
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;
if (r < 0)
return r;
- r = setup_default_address_pool(m);
+ r = address_pool_setup_default(m);
if (r < 0)
return r;
}
void manager_free(Manager *m) {
- AddressPool *pool;
Link *link;
if (!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. */
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;
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);
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);
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);
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);
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;
}
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;
-}
#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;
OrderedHashmap *networks;
Hashmap *dhcp6_prefixes;
Set *dhcp6_pd_prefixes;
- LIST_HEAD(AddressPool, address_pools);
+ OrderedSet *address_pools;
usec_t network_dirs_ts_usec;
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);
#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,
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. */
/* 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;
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))
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");
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,
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;
-}
/* 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);
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) {
return 1;
}
- r = link_request_set_routes(link);
+ r = link_set_routes(link);
if (r < 0) {
link_enter_failed(link);
return 1;
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;
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;
}
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)
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);
/* 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);
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;
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);
*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;
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;
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");
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");
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;
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;
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");
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");
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;
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,
/* 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;
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);
#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"
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
#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"
}
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);
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)
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;
}
}
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;
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);
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;
}
}
-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,
#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,
int ipv6_dad_transmits;
int ipv6_hop_limit;
int ipv6_proxy_ndp;
+ Set *ipv6_proxy_ndp_addresses;
int proxy_arp;
uint32_t ipv6_mtu;
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;
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);
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_;
#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);
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);
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) {
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(
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);
return r;
nexthop->id = in->id;
- nexthop->oif = in->oif;
nexthop->family = in->family;
nexthop->gw = in->gw;
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;
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;
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;
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");
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;
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,
#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;
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);
#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;
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);
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;
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);
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;
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;
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;
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;
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;
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)
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;
}
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;
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;
}
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;
}
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;
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;
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;
}
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(
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);
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,
_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);
#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;
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);
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);
sd_event_source_unref(route->expire);
- free(route);
+ return mfree(route);
}
void route_hash_func(const Route *route, struct siphash *state) {
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;
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;
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;
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;
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);
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;
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);
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;
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,
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) {
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;
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. "
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);
+}
/* 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;
uint32_t weight;
} MultipathRoute;
-struct Route {
+typedef struct Route {
Network *network;
NetworkConfigSection *section;
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);
#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);
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) {
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;
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)
if (r < 0)
return r;
+ rule->family = family;
+
r = set_ensure_put(rules, &routing_policy_rule_hash_ops, rule);
if (r < 0)
return r;
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) {
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;
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");
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;
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;
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) {
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;
}
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;
}
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;
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)
r = routing_policy_rule_new(&rule);
if (r < 0)
- return r;
+ return log_oom();
for (;;) {
_cleanup_free_ char *a = NULL;
r = extract_first_word(&p, &a, NULL, 0);
if (r < 0)
- return r;
+ return log_oom();
if (r == 0)
break;
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;
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);
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);
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)
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);
- }
-}
#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;
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);
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;
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))
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,
#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"
} 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);
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
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;
+}
#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! */
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. */
} \
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 ? " " : "");
+}
#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"
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)
}
}
-int traffic_control_configure(Link *link, TrafficControl *tc) {
+static int traffic_control_configure(Link *link, TrafficControl *tc) {
assert(link);
assert(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) {
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);
+}
#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);
#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"
test_network_get(manager, loopback);
- assert_se(manager_rtnl_enumerate_links(manager) >= 0);
+ assert_se(manager_enumerate(manager) >= 0);
return 0;
}
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 */
}
}
-/***
- 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"
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");
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;
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 */
if (r < 0)
return log_error_errno(r, "Failed to become root: %m");
- *_home = NULL;
+ *ret_home = NULL;
return 0;
}
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;
}
/* 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);
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;
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)) {
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)
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 */
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <errno.h>
+#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stddef.h>
struct PTYForward {
sd_event *event;
+ int input_fd;
+ int output_fd;
int master;
PTYForwardFlags flags;
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;
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) {
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)
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)
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;
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;
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;
*f = (struct PTYForward) {
.flags = flags,
.master = -1,
+ .input_fd = -1,
+ .output_fd = -1,
};
if (event)
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);
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. */
(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;
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;
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;
(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;
},
[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 */
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);
}
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);
}
retval = -1;
break;
}
- if (vendor == NULL) {
+ if (!vendor) {
if (!vendor_in)
break;
} else if (vendor_in &&
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)
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
[Network]
Address=192.168.100.100/24
+IPv6AcceptRA=no
[SR-IOV]
VirtualFunction=0
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:
links = [
'6rdtun99',
'bareudp99',
+ 'bond98',
'bond99',
'bridge99',
'dropin-test',
'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',