release: bump version to 1.47.1 (development)

This commit is contained in:
Stanislas FAYE 2024-02-26 15:38:16 +01:00
commit 9920a4b576
152 changed files with 16355 additions and 11930 deletions

1
.gitignore vendored
View file

@ -183,6 +183,7 @@ test-*.trs
/src/nm-online/nm-online
/m4/build-to-host.m4
/m4/codeset.m4
/m4/gettext.m4
/m4/glibc2.m4

View file

@ -58,9 +58,9 @@ variables:
# This is done by running `ci-fairy generate-template` and possibly bumping
# ".default_tag".
ALPINE_TAG: 'tag-7da44bbacc09'
CENTOS_TAG: 'tag-c8090b8a9a6b'
CENTOS_TAG: 'tag-df11907da86a'
DEBIAN_TAG: 'tag-86a16c2d74d8'
FEDORA_TAG: 'tag-c8090b8a9a6b'
FEDORA_TAG: 'tag-df11907da86a'
UBUNTU_TAG: 'tag-86a16c2d74d8'
ALPINE_EXEC: 'bash .gitlab-ci/alpine-install.sh'

View file

@ -2353,7 +2353,6 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/sd-adapt-shared/mkdir.h \
src/libnm-systemd-shared/sd-adapt-shared/mountpoint-util.h \
src/libnm-systemd-shared/sd-adapt-shared/namespace-util.h \
src/libnm-systemd-shared/sd-adapt-shared/netif-util.h \
src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h \
src/libnm-systemd-shared/sd-adapt-shared/nulstr-util.h \
src/libnm-systemd-shared/sd-adapt-shared/os-util.h \
@ -2368,7 +2367,9 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/sd-adapt-shared/virt.h \
src/libnm-systemd-shared/src/basic/alloc-util.c \
src/libnm-systemd-shared/src/basic/alloc-util.h \
src/libnm-systemd-shared/src/basic/async.h \
src/libnm-systemd-shared/src/basic/arphrd-util.h \
src/libnm-systemd-shared/src/basic/btrfs.c \
src/libnm-systemd-shared/src/basic/btrfs.h \
src/libnm-systemd-shared/src/basic/cgroup-util.h \
src/libnm-systemd-shared/src/basic/constants.h \
src/libnm-systemd-shared/src/basic/dns-def.h \
@ -2407,6 +2408,7 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/src/basic/inotify-util.h \
src/libnm-systemd-shared/src/basic/io-util.c \
src/libnm-systemd-shared/src/basic/io-util.h \
src/libnm-systemd-shared/src/basic/iovec-util.h \
src/libnm-systemd-shared/src/basic/label.c \
src/libnm-systemd-shared/src/basic/label.h \
src/libnm-systemd-shared/src/basic/list.h \
@ -2426,6 +2428,7 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/src/basic/missing_syscall.h \
src/libnm-systemd-shared/src/basic/missing_threads.h \
src/libnm-systemd-shared/src/basic/missing_type.h \
src/libnm-systemd-shared/src/basic/namespace-util.h \
src/libnm-systemd-shared/src/basic/ordered-set.c \
src/libnm-systemd-shared/src/basic/ordered-set.h \
src/libnm-systemd-shared/src/basic/origin-id.h \
@ -2433,6 +2436,7 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/src/basic/parse-util.h \
src/libnm-systemd-shared/src/basic/path-util.c \
src/libnm-systemd-shared/src/basic/path-util.h \
src/libnm-systemd-shared/src/basic/pidref.h \
src/libnm-systemd-shared/src/basic/prioq.c \
src/libnm-systemd-shared/src/basic/prioq.h \
src/libnm-systemd-shared/src/basic/process-util.c \
@ -2512,14 +2516,17 @@ src_libnm_systemd_core_libnm_systemd_core_la_SOURCES = \
src/libnm-systemd-core/sd-adapt-core/condition.h \
src/libnm-systemd-core/sd-adapt-core/conf-parser.h \
src/libnm-systemd-core/sd-adapt-core/khash.h \
src/libnm-systemd-core/sd-adapt-core/netif-util.c \
src/libnm-systemd-core/sd-adapt-core/netif-util.h \
src/libnm-systemd-core/sd-adapt-core/network-util.h \
src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c \
src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h \
src/libnm-systemd-core/sd-adapt-core/sd-daemon.h \
src/libnm-systemd-core/sd-adapt-core/sd-messages.h \
src/libnm-systemd-core/sd-adapt-core/udev-util.h \
src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c \
src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h \
src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-duid.c \
src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h \
src/libnm-systemd-core/src/libsystemd-network/dhcp6-client-internal.h \
src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h \
src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h \
src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c \
@ -2541,9 +2548,11 @@ src_libnm_systemd_core_libnm_systemd_core_la_SOURCES = \
src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c \
src/libnm-systemd-core/src/systemd/_sd-common.h \
src/libnm-systemd-core/src/systemd/sd-device.h \
src/libnm-systemd-core/src/systemd/sd-dhcp-duid.h \
src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h \
src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h \
src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h \
src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h \
src/libnm-systemd-core/src/systemd/sd-event.h \
src/libnm-systemd-core/src/systemd/sd-id128.h \
src/libnm-systemd-core/src/systemd/sd-ndisc.h \

10
NEWS
View file

@ -1,3 +1,13 @@
=============================================
NetworkManager-1.48
Overview of changes since NetworkManager-1.46
=============================================
This is a snapshot of NetworkManager development. The API is
subject to change and not guaranteed to be compatible with
the later release.
USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
=============================================
NetworkManager-1.46
Overview of changes since NetworkManager-1.44

View file

@ -7,8 +7,8 @@ dnl - add corresponding NM_VERSION_x_y_z macros in
dnl "shared/nm-version-macros.h.in"
dnl - update number in meson.build
m4_define([nm_major_version], [1])
m4_define([nm_minor_version], [46])
m4_define([nm_micro_version], [0])
m4_define([nm_minor_version], [47])
m4_define([nm_micro_version], [1])
m4_define([nm_version],
[nm_major_version.nm_minor_version.nm_micro_version])
@ -1157,7 +1157,12 @@ fi
NM_COMPILER_WARNINGS(AM_CFLAGS, ${more_warnings_default})
NM_COMPILER_WARNING_FLAG(LIBSYSTEMD_NM_CFLAGS, "-Wno-gnu-variable-sized-type-not-at-end")
for w in \
-Wno-nonnull-compare \
-Wno-calloc-transposed-args \
; do
NM_COMPILER_WARNING_FLAG(LIBSYSTEMD_NM_CFLAGS, "$w")
done
AC_SUBST(LIBSYSTEMD_NM_CFLAGS)
CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [\

View file

@ -63,6 +63,7 @@ install \
gtk-doc \
iptables \
jansson-devel \
jq \
libcurl-devel \
libndp-devel \
libselinux-devel \

View file

@ -19,8 +19,9 @@ usage() {
echo " -c|--clean: run \`git-clean -fdx :/\` before build"
echo " -S|--srpm: only build the SRPM"
echo " -g|--git: create tarball from current git HEAD (skips make dist)"
echo " -Q|--quick: only run \`make dist\` instead of \`make distcheck\`"
echo " -Q|--quick: only create the distribution tarball, without running checks"
echo " -N|--no-dist: skip creating the source tarball if you already did \`make dist\`"
echo " -m|--meson: use meson to create the source tarball"
echo " -w|--with \$OPTION: pass --with \$OPTION to rpmbuild. For example --with debug"
echo " -W|--without \$OPTION: pass --without \$OPTION to rpmbuild. For example --without debug"
echo " -s|--snapshot TEXT: use TEXT as the snapshot version for the new package (overwrites \$NM_BUILD_SNAPSHOT environment)"
@ -97,6 +98,10 @@ while [[ $# -gt 0 ]]; do
IGNORE_DIRTY=1
SOURCE_FROM_GIT=1
;;
-m|--meson)
USE_MESON=1
WITH_LIST=("${WITH_LIST[@]}" "--with" "meson")
;;
-Q|--quick)
QUICK=1
;;
@ -188,45 +193,87 @@ if [[ $IGNORE_DIRTY != 1 ]]; then
fi
fi
get_version_meson() {
meson introspect "$GITDIR/build" --projectinfo | jq -r .version
}
if [[ $NO_DIST != 1 ]]; then
./autogen.sh \
--with-runstatedir=/run \
--program-prefix= \
--prefix=/usr \
--exec-prefix=/usr \
--bindir=/usr/bin \
--sbindir=/usr/sbin \
--sysconfdir=/etc \
--datadir=/usr/share \
--includedir=/usr/include \
--libdir=/usr/lib \
--libexecdir=/usr/libexec \
--localstatedir=/var \
--sharedstatedir=/var/lib \
--mandir=/usr/share/man \
--infodir=/usr/share/info \
\
--disable-dependency-tracking \
--enable-gtk-doc \
--enable-introspection \
--enable-ifcfg-rh \
--enable-ifupdown \
--with-config-logging-backend-default=syslog \
--with-config-wifi-backend-default=wpa_supplicant \
--with-libaudit=yes-disabled-by-default \
--enable-polkit=yes \
--with-nm-cloud-setup=yes \
--with-config-dhcp-default=internal \
--with-config-dns-rc-manager-default=auto \
\
--with-iptables=/usr/sbin/iptables \
--with-nft=/usr/sbin/nft \
\
|| die "Error autogen.sh"
if [[ $QUICK == 1 ]]; then
make dist -j 7 || die "Error make dist"
if [[ $USE_MESON = 1 ]]; then
meson setup "$GITDIR/build" \
--prefix=/usr \
--bindir=/usr/bin \
--sbindir=/usr/sbin \
--sysconfdir=/etc \
--datadir=/usr/share \
--includedir=/usr/include \
--libdir=/usr/lib \
--libexecdir=/usr/libexec \
--localstatedir=/var \
--sharedstatedir=/var/lib \
--mandir=/usr/share/man \
--infodir=/usr/share/info \
-Ddocs=true \
-Dintrospection=true \
-Difcfg_rh=true \
-Difupdown=true \
-Dconfig_logging_backend_default=syslog \
-Dconfig_wifi_backend_default=wpa_supplicant \
-Dlibaudit=yes-disabled-by-default \
-Dpolkit=true \
-Dnm_cloud_setup=true \
-Dconfig_dhcp_default=internal \
-Dconfig_dns_rc_manager_default=auto \
-Diptables=/usr/sbin/iptables \
-Dnft=/usr/bin/nft \
|| die "Error meson setup"
VERSION="${VERSION:-$(get_version_meson || die "Could not read $VERSION")}"
if [[ $QUICK == 1 ]]; then
meson dist --allow-dirty -C "$GITDIR/build/" --no-tests || die "Error meson dist"
else
meson dist --allow-dirty -C "$GITDIR/build/" || die "Error meson dist with tests"
fi
export SOURCE="$(ls -1 "$GITDIR/build/meson-dist/NetworkManager-${VERSION}.tar.xz" 2>/dev/null | head -n1)"
else
make distcheck -j 7 || die "Error make distcheck"
./autogen.sh \
--with-runstatedir=/run \
--program-prefix= \
--prefix=/usr \
--exec-prefix=/usr \
--bindir=/usr/bin \
--sbindir=/usr/sbin \
--sysconfdir=/etc \
--datadir=/usr/share \
--includedir=/usr/include \
--libdir=/usr/lib \
--libexecdir=/usr/libexec \
--localstatedir=/var \
--sharedstatedir=/var/lib \
--mandir=/usr/share/man \
--infodir=/usr/share/info \
\
--disable-dependency-tracking \
--enable-gtk-doc \
--enable-introspection \
--enable-ifcfg-rh \
--enable-ifupdown \
--with-config-logging-backend-default=syslog \
--with-config-wifi-backend-default=wpa_supplicant \
--with-libaudit=yes-disabled-by-default \
--enable-polkit=yes \
--with-nm-cloud-setup=yes \
--with-config-dhcp-default=internal \
--with-config-dns-rc-manager-default=auto \
\
--with-iptables=/usr/sbin/iptables \
--with-nft=/usr/sbin/nft \
\
|| die "Error autogen.sh"
if [[ $QUICK == 1 ]]; then
make dist -j 7 || die "Error make dist"
else
make distcheck -j 7 || die "Error make distcheck"
fi
fi
fi

View file

@ -408,6 +408,10 @@ pushd "$DIRNAME"
git remote add origin "https://github.com/firewalld/firewalld.git"
elif [[ "$BUILD_TYPE" == "nftables" ]]; then
git remote add origin "git://git.netfilter.org/nftables"
elif [[ "$BUILD_TYPE" == "ulogd" ]]; then
git remote add origin "https://git.netfilter.org/ulogd2"
elif [[ "$BUILD_TYPE" == "libnetfilter_log" ]]; then
git remote add origin "https://git.netfilter.org/$BUILD_TYPE"
fi
LOCAL_MIRROR_URL="$(LANG=C git remote -v | sed -n 's/^origin\t*\([^\t].*\) (fetch)/\1/p')"
LOCAL_MIRROR="$(get_local_mirror "$LOCAL_MIRROR_URL")"
@ -526,6 +530,13 @@ cc304f05edab6c408a0f061eb1a104f9f06b8587 86ef789876b65c61751ce854835b91d4 init
# systemd
903dd65b5eb63257393955cb79777beb8c71afc1 SHA512 (systemd-253-rc1.tar.gz) = aaf0a6bf21bbc50a42015c9cb17f69d1aaf6cab6cabfba5140a94212fb864e38d638dace9a70447f62b4d2a817a0d3bd6f4ae8d9b3c2e741cdeb1cb332f70b65
# libnetfilter_log
97866a0a7482ca518bad39536c7c667bfb9604b2 2a4bb0654ae675a52d2e8d1c06090b94 libnetfilter_log-1.0.1.tar.bz2
b0e4be94c0b8f68d4e912402b93a130063c34e17 SHA512 (libnetfilter_log-1.0.2.tar.bz2) = 6b33718b1dd7f4504bceae14001da3a652cec46a6725a5dee83a7b55028cfa8e768cba917f968a5d5b60fd9ff04edf6040ef271a68e5fb65858bf73f4f9ccf23
# ulogd
79aa980f2df9dda0c097e8f883a62f414b9e5138 SHA512 (ulogd-2.0.8.tar.bz2) = 9f99f6f35bad5da4559d788dc3ba3dae17d4ae972737cae3313ecf68f08eaf5f55514fce6f30503437e4158fd30a06438b9249d5d20f6343964cbf690f87309d
EOF
)"
OLDIFS="$IFS"
@ -583,7 +594,7 @@ EOF
if [[ "$REVERT_COUNT" == "" || $REVERT_COUNT -gt 0 ]]; then
# parse the list of patches
IFS=$'\n' read -rd '' -a PATCH_LIST <<<"$(sed -n 's/^Patch\([0-9]\+\): \+\(.*\)$/\1 \2/p' ../"$SPEC" | sort -n)"
IFS=$'\n' read -rd '' -a PATCH_LIST <<<"$(sed -n 's/^Patch\([0-9]\+\):[ ]\+\(.*\)$/\1 \2/p' ../"$SPEC" | sort -n)"
if [[ "$BUILD_TYPE" == "NetworkManager" ]]; then
if containsElement idx "123 rh1085015-applet-translations.patch" "${PATCH_LIST[@]}"; then
@ -674,7 +685,7 @@ popd
if [[ $LOCAL != 0 ]]; then
rm -rf ./.makerepo.git/
mv "$DIRNAME/.git" ./.makerepo.git/
$FEDPKG $DIST local
$FEDPKG $DIST local -- --noclean
mv ./.makerepo.git/ "$DIRNAME/.git"
pushd "$DIRNAME"
git checkout -- .gitignore

View file

@ -823,6 +823,11 @@ domains=ALL
and these default values only matter if the per-profile values explicitly indicates
to use the default from <literal>NetworkManager.conf</literal>.
</para>
<para>
Note that while nmcli supports various aliases and convenience features for configuring
properties, the settings in this section do not. For example, enum values usually only
can be configured via their numeric magic number.
</para>
<para>
Example:
<programlisting>
@ -868,7 +873,13 @@ ipv6.ip6-privacy=0
</varlistentry>
<varlistentry>
<term><varname>connection.mdns</varname></term>
<listitem><para>If unspecified, the ultimate default values depends on the DNS plugin. With systemd-resolved the default currently is "no" (0) and for all other plugins also "no" (0).</para></listitem>
<listitem><para>
Currently only the systemd-resolve DNS plugin supports this setting.
If the setting is unspecified both in the profile and in the global
default here, then the default is determined by systemd-resolved.
See <literal>MulticastDNS=</literal> in
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>connection.mptcp-flags</varname></term>

View file

@ -6,7 +6,7 @@ project(
# - add corresponding NM_VERSION_x_y_z macros in
# "src/libnm-core-public/nm-version-macros.h.in"
# - update number in configure.ac
version: '1.46.0',
version: '1.47.1',
license: 'GPL2+',
default_options: [
'buildtype=debugoptimized',

11579
po/de.po

File diff suppressed because it is too large Load diff

6731
po/ru.po

File diff suppressed because it is too large Load diff

1817
po/sv.po

File diff suppressed because it is too large Load diff

View file

@ -67,6 +67,15 @@ G_DEFINE_TYPE(NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT)
/*****************************************************************************/
static guint32
lifetime_to_uint32(guint64 lft)
{
if (lft == G_MAXUINT64)
return G_MAXUINT32;
return lft / 1000000;
}
static NML3ConfigData *
lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GError **error)
{
@ -100,18 +109,19 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
if (!config->v6.info_only) {
gboolean has_any_addresses = FALSE;
uint32_t lft_pref;
uint32_t lft_valid;
uint64_t lft_pref;
uint64_t lft_valid;
sd_dhcp6_lease_reset_address_iter(lease);
sd_dhcp6_lease_address_iterator_reset(lease);
nm_gstring_prepare(&str);
while (sd_dhcp6_lease_get_address(lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) {
const NMPlatformIP6Address address = {
while (sd_dhcp6_lease_get_address(lease, &tmp_addr) >= 0
&& sd_dhcp6_lease_get_address_lifetime(lease, &lft_pref, &lft_valid) >= 0) {
NMPlatformIP6Address address = {
.plen = 128,
.address = tmp_addr,
.timestamp = ts,
.lifetime = lft_valid,
.preferred = lft_pref,
.lifetime = lifetime_to_uint32(lft_valid),
.preferred = lifetime_to_uint32(lft_pref),
.addr_source = NM_IP_CONFIG_SOURCE_DHCP,
};
@ -121,6 +131,7 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
g_string_append(nm_gstring_add_space_delimiter(str), addr_str);
has_any_addresses = TRUE;
sd_dhcp6_lease_address_iterator_next(lease);
}
if (str->len) {
@ -160,11 +171,12 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
uint8_t prefix_len;
nm_gstring_prepare(&str);
sd_dhcp6_lease_reset_pd_prefix_iter(lease);
while (!sd_dhcp6_lease_get_pd(lease, &prefix, &prefix_len, NULL, NULL)) {
sd_dhcp6_lease_pd_iterator_reset(lease);
while (!sd_dhcp6_lease_get_pd_prefix(lease, &prefix, &prefix_len)) {
nm_gstring_add_space_delimiter(str);
nm_inet6_ntop(&prefix, addr_str);
g_string_append_printf(str, "%s/%u", addr_str, prefix_len);
sd_dhcp6_lease_pd_iterator_next(lease);
}
if (str->len > 0) {
nm_dhcp_option_add_option(options,
@ -235,6 +247,8 @@ bound6_handle(NMDhcpSystemd *self)
gs_free_error GError *error = NULL;
NMPlatformIP6Address prefix = {0};
sd_dhcp6_lease *lease = NULL;
guint64 lft_valid;
guint64 lft_pref;
if (sd_dhcp6_client_get_lease(priv->client6, &lease) < 0 || !lease) {
_LOGW(" no lease!");
@ -254,14 +268,14 @@ bound6_handle(NMDhcpSystemd *self)
_nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_BOUND, l3cd);
sd_dhcp6_lease_reset_pd_prefix_iter(lease);
while (!sd_dhcp6_lease_get_pd(lease,
&prefix.address,
&prefix.plen,
&prefix.preferred,
&prefix.lifetime)) {
sd_dhcp6_lease_pd_iterator_reset(lease);
while (!sd_dhcp6_lease_get_pd_prefix(lease, &prefix.address, &prefix.plen)
&& !sd_dhcp6_lease_get_pd_lifetime(lease, &lft_pref, &lft_valid)) {
prefix.preferred = lifetime_to_uint32(lft_pref);
prefix.lifetime = lifetime_to_uint32(lft_valid);
prefix.timestamp = ts;
nm_dhcp_client_emit_ipv6_prefix_delegated(NM_DHCP_CLIENT(self), &prefix);
sd_dhcp6_lease_pd_iterator_next(lease);
}
}
@ -339,10 +353,10 @@ ip6_start(NMDhcpClient *client, const struct in6_addr *ll_addr, GError **error)
return FALSE;
}
r = sd_dhcp6_client_set_duid(sd_client,
unaligned_read_be16(&duid_arr[0]),
&duid_arr[2],
duid_len - 2);
r = sd_dhcp6_client_set_duid_raw(sd_client,
unaligned_read_be16(&duid_arr[0]),
&duid_arr[2],
duid_len - 2);
if (r < 0) {
nm_utils_error_set_errno(error, r, "failed to set DUID: %s");
return FALSE;

View file

@ -185,13 +185,16 @@ nm_audit_log(NMAuditManager *self,
priv = NM_AUDIT_MANAGER_GET_PRIVATE(self);
if (priv->auditd_fd >= 0) {
audit_log_user_message(priv->auditd_fd,
AUDIT_USYS_CONFIG,
build_message(&strbuf, BACKEND_AUDITD, fields),
NULL,
NULL,
NULL,
success);
int r;
r = audit_log_user_message(priv->auditd_fd,
AUDIT_USYS_CONFIG,
build_message(&strbuf, BACKEND_AUDITD, fields),
NULL,
NULL,
NULL,
success);
(void) r;
}
#endif

View file

@ -1076,7 +1076,7 @@ _set_values_intern_atomic_section_2_set(NMConfig *config,
g_key_file_set_value(keyfile,
NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN "with-whitespace",
"key2",
" b c\\, d ");
" b c\\\\, d ");
*out_expected_changes = NM_CONFIG_CHANGE_CAUSE_SET_VALUES | NM_CONFIG_CHANGE_VALUES
| NM_CONFIG_CHANGE_VALUES_INTERN;
}

View file

@ -2099,20 +2099,12 @@ _dbus_signal_ip_config_cb(NMVpnConnection *self, int addr_family, GVariant *dict
NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4))
nm_l3_config_data_add_route(l3cd, addr_family, route, NULL);
}
} else if (IS_IPv4 ? g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, "aau", &var_iter)
: g_variant_lookup(dict,
NM_VPN_PLUGIN_IP6_CONFIG_ROUTES,
"a(ayuayu)",
&var_iter)) {
_nm_unused nm_auto_free_variant_iter GVariantIter *var_iter_ref_owner = var_iter;
NMPlatformIPXRoute route = {};
guint32 plen;
GVariant *next_hop;
GVariant *dest;
guint32 prefix;
guint32 metric;
} else if (IS_IPv4) {
if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, "aau", &var_iter)) {
_nm_unused nm_auto_free_variant_iter GVariantIter *var_iter_ref_owner = var_iter;
NMPlatformIPXRoute route = {};
guint32 plen;
if (IS_IPv4) {
while (g_variant_iter_next(var_iter, "@au", &v)) {
_nm_unused gs_unref_variant GVariant *v_ref_owner = v;
@ -2151,42 +2143,84 @@ _dbus_signal_ip_config_cb(NMVpnConnection *self, int addr_family, GVariant *dict
break;
}
}
} else {
while (
g_variant_iter_next(var_iter, "(@ayu@ayu)", &dest, &prefix, &next_hop, &metric)) {
_nm_unused gs_unref_variant GVariant *next_hop_ref_owner = next_hop;
_nm_unused gs_unref_variant GVariant *dest_ref_owner = dest;
}
} else {
_nm_unused nm_auto_free_variant_iter GVariantIter *var_iter_ref_owner = NULL;
NMPlatformIPXRoute route = {};
guint32 prefix;
guint32 metric;
NMOptionBool new_signature = NM_OPTION_BOOL_DEFAULT;
if (prefix > 128)
continue;
/* IPv6 and no "preserve-routes" */
route.r6 = (NMPlatformIP6Route){
.plen = prefix,
.table_any = TRUE,
.metric_any = TRUE,
.rt_source = NM_IP_CONFIG_SOURCE_VPN,
};
if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, "a(ayuayu)", &var_iter))
new_signature = FALSE;
else if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, "a(ayuayuay)", &var_iter))
new_signature = TRUE;
else
var_iter = NULL;
if (!nm_ip_addr_set_from_variant(AF_INET6, &route.r6.network, dest, NULL))
continue;
var_iter_ref_owner = var_iter;
nm_ip_addr_set_from_variant(AF_INET6, &route.r6.gateway, next_hop, NULL);
while (TRUE) {
gs_unref_variant GVariant *next_hop = NULL;
gs_unref_variant GVariant *dest = NULL;
gs_unref_variant GVariant *pref_src = NULL;
nm_ip6_addr_clear_host_address(&route.r6.network, &route.r6.network, route.r6.plen);
if (!IN6_IS_ADDR_UNSPECIFIED(&priv->ip_data_6.gw_external.addr6)
&& IN6_ARE_ADDR_EQUAL(&route.r6.network, &priv->ip_data_6.gw_external.addr6)
&& route.r6.plen == 128) {
/* Ignore host routes to the VPN gateway since NM adds one itself.
* Since NM knows more about the routing situation than the VPN
* server, we want to use the NM created route instead of whatever
* the server provides.
*/
continue;
}
nm_l3_config_data_add_route_6(l3cd, &route.r6);
if (new_signature == NM_OPTION_BOOL_DEFAULT) {
break;
} else if (new_signature) {
if (!g_variant_iter_next(var_iter,
"(@ayu@ayu@ay)",
&dest,
&prefix,
&next_hop,
&metric,
&pref_src))
break;
} else {
if (!g_variant_iter_next(var_iter,
"(@ayu@ayu)",
&dest,
&prefix,
&next_hop,
&metric))
break;
}
if (prefix > 128)
continue;
route.r6 = (NMPlatformIP6Route){
.plen = prefix,
.table_any = TRUE,
.metric_any = TRUE,
.rt_source = NM_IP_CONFIG_SOURCE_VPN,
};
if (!nm_ip_addr_set_from_variant(AF_INET6, &route.r6.network, dest, NULL))
continue;
if (pref_src
&& !nm_ip_addr_set_from_variant(AF_INET6, &route.r6.pref_src, pref_src, NULL))
continue;
nm_ip_addr_set_from_variant(AF_INET6, &route.r6.gateway, next_hop, NULL);
nm_ip6_addr_clear_host_address(&route.r6.network, &route.r6.network, route.r6.plen);
if (!IN6_IS_ADDR_UNSPECIFIED(&priv->ip_data_6.gw_external.addr6)
&& IN6_ARE_ADDR_EQUAL(&route.r6.network, &priv->ip_data_6.gw_external.addr6)
&& route.r6.plen == 128) {
/* Ignore host routes to the VPN gateway since NM adds one itself.
* Since NM knows more about the routing situation than the VPN
* server, we want to use the NM created route instead of whatever
* the server provides.
*/
continue;
}
nm_l3_config_data_add_route_6(l3cd, &route.r6);
}
}

View file

@ -2852,11 +2852,12 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
* for the connection, "no" (0) disable mDNS for the interface, "resolve"
* (1) do not register hostname but allow resolving of mDNS host names
* and "default" (-1) to allow lookup of a global default in NetworkManager.conf.
* If unspecified, "default" ultimately depends on the DNS plugin (which
* for systemd-resolved currently means "no").
* If unspecified, "default" ultimately depends on the DNS plugin.
*
* This feature requires a plugin which supports mDNS. Otherwise, the
* setting has no effect. One such plugin is dns-systemd-resolved.
* setting has no effect. Currently the only supported DNS plugin is
* systemd-resolved. For systemd-resolved, the default is configurable via
* MulticastDNS= setting in resolved.conf.
*
* Since: 1.12
**/

View file

@ -46,24 +46,24 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingWireless,
PROP_AP_ISOLATION, );
typedef struct {
GBytes *ssid;
GArray *mac_address_blacklist;
GPtrArray *seen_bssids;
char *mode;
char *band;
char *bssid;
char *device_mac_address;
char *cloned_mac_address;
char *generate_mac_address_mask;
int ap_isolation;
guint32 mac_address_randomization;
guint32 channel;
guint32 rate;
guint32 tx_power;
guint32 mtu;
guint32 powersave;
guint32 wake_on_wlan;
bool hidden;
GBytes *ssid;
GPtrArray *seen_bssids;
char *mode;
char *band;
char *bssid;
char *device_mac_address;
char *cloned_mac_address;
char *generate_mac_address_mask;
NMValueStrv mac_address_blacklist;
int ap_isolation;
guint32 mac_address_randomization;
guint32 channel;
guint32 rate;
guint32 tx_power;
guint32 mtu;
guint32 powersave;
guint32 wake_on_wlan;
bool hidden;
} NMSettingWirelessPrivate;
/**
@ -470,12 +470,11 @@ nm_setting_wireless_get_generate_mac_address_mask(NMSettingWireless *setting)
const char *const *
nm_setting_wireless_get_mac_address_blacklist(NMSettingWireless *setting)
{
NMSettingWirelessPrivate *priv;
g_return_val_if_fail(NM_IS_SETTING_WIRELESS(setting), NULL);
priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
return nm_g_array_data(priv->mac_address_blacklist);
return nm_strvarray_get_strv_notnull(
NM_SETTING_WIRELESS_GET_PRIVATE(setting)->mac_address_blacklist.arr,
NULL);
}
/**
@ -489,7 +488,7 @@ nm_setting_wireless_get_num_mac_blacklist_items(NMSettingWireless *setting)
{
g_return_val_if_fail(NM_IS_SETTING_WIRELESS(setting), 0);
return NM_SETTING_WIRELESS_GET_PRIVATE(setting)->mac_address_blacklist->len;
return nm_g_array_len(NM_SETTING_WIRELESS_GET_PRIVATE(setting)->mac_address_blacklist.arr);
}
/**
@ -505,19 +504,11 @@ nm_setting_wireless_get_num_mac_blacklist_items(NMSettingWireless *setting)
const char *
nm_setting_wireless_get_mac_blacklist_item(NMSettingWireless *setting, guint32 idx)
{
NMSettingWirelessPrivate *priv;
g_return_val_if_fail(NM_IS_SETTING_WIRELESS(setting), NULL);
priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
if (idx == priv->mac_address_blacklist->len) {
return NULL;
}
g_return_val_if_fail(idx < priv->mac_address_blacklist->len, NULL);
return nm_g_array_index(priv->mac_address_blacklist, const char *, idx);
return nm_strvarray_get_idxnull_or_greturn(
NM_SETTING_WIRELESS_GET_PRIVATE(setting)->mac_address_blacklist.arr,
idx);
}
/**
@ -534,24 +525,28 @@ gboolean
nm_setting_wireless_add_mac_blacklist_item(NMSettingWireless *setting, const char *mac)
{
NMSettingWirelessPrivate *priv;
guint8 mac_bin[ETH_ALEN];
const char *candidate;
int i;
guint i;
guint len;
g_return_val_if_fail(NM_IS_SETTING_WIRELESS(setting), FALSE);
g_return_val_if_fail(mac != NULL, FALSE);
if (!nm_utils_hwaddr_valid(mac, ETH_ALEN))
if (!_nm_utils_hwaddr_aton_exact(mac, mac_bin, ETH_ALEN))
return FALSE;
priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
for (i = 0; i < priv->mac_address_blacklist->len; i++) {
candidate = nm_g_array_index(priv->mac_address_blacklist, char *, i);
if (nm_utils_hwaddr_matches(mac, -1, candidate, -1))
len = nm_g_array_len(priv->mac_address_blacklist.arr);
for (i = 0; i < len; i++) {
candidate = nm_g_array_index(priv->mac_address_blacklist.arr, char *, i);
if (nm_utils_hwaddr_matches(mac_bin, ETH_ALEN, candidate, -1))
return FALSE;
}
mac = nm_utils_hwaddr_canonical(mac, ETH_ALEN);
g_array_append_val(priv->mac_address_blacklist, mac);
nm_g_array_append_simple(nm_strvarray_ensure(&priv->mac_address_blacklist.arr),
nm_utils_hwaddr_ntoa(mac_bin, ETH_ALEN));
_notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
return TRUE;
}
@ -571,9 +566,13 @@ nm_setting_wireless_remove_mac_blacklist_item(NMSettingWireless *setting, guint3
g_return_if_fail(NM_IS_SETTING_WIRELESS(setting));
priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
g_return_if_fail(idx < priv->mac_address_blacklist->len);
if (!priv->mac_address_blacklist.arr) {
return;
}
g_array_remove_index(priv->mac_address_blacklist, idx);
g_return_if_fail(idx < priv->mac_address_blacklist.arr->len);
g_array_remove_index(priv->mac_address_blacklist.arr, idx);
_notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
}
@ -591,21 +590,29 @@ gboolean
nm_setting_wireless_remove_mac_blacklist_item_by_value(NMSettingWireless *setting, const char *mac)
{
NMSettingWirelessPrivate *priv;
guint8 mac_bin[ETH_ALEN];
const char *candidate;
int i;
guint i;
g_return_val_if_fail(NM_IS_SETTING_WIRELESS(setting), FALSE);
g_return_val_if_fail(mac != NULL, FALSE);
if (!_nm_utils_hwaddr_aton_exact(mac, mac_bin, ETH_ALEN))
return FALSE;
priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
for (i = 0; i < priv->mac_address_blacklist->len; i++) {
candidate = nm_g_array_index(priv->mac_address_blacklist, char *, i);
if (nm_utils_hwaddr_matches(mac, -1, candidate, -1)) {
g_array_remove_index(priv->mac_address_blacklist, i);
_notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
return TRUE;
if (priv->mac_address_blacklist.arr) {
for (i = 0; i < priv->mac_address_blacklist.arr->len; i++) {
candidate = nm_g_array_index(priv->mac_address_blacklist.arr, char *, i);
if (nm_utils_hwaddr_matches(mac_bin, ETH_ALEN, candidate, -1)) {
g_array_remove_index(priv->mac_address_blacklist.arr, i);
_notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
return TRUE;
}
}
}
return FALSE;
}
@ -620,8 +627,8 @@ nm_setting_wireless_clear_mac_blacklist_items(NMSettingWireless *setting)
{
g_return_if_fail(NM_IS_SETTING_WIRELESS(setting));
g_array_set_size(NM_SETTING_WIRELESS_GET_PRIVATE(setting)->mac_address_blacklist, 0);
_notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
if (nm_strvarray_clear(&NM_SETTING_WIRELESS_GET_PRIVATE(setting)->mac_address_blacklist.arr))
_notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
}
/**
@ -1041,20 +1048,22 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
return FALSE;
}
for (i = 0; i < priv->mac_address_blacklist->len; i++) {
const char *mac = nm_g_array_index(priv->mac_address_blacklist, const char *, i);
if (priv->mac_address_blacklist.arr) {
for (i = 0; i < priv->mac_address_blacklist.arr->len; i++) {
const char *mac = nm_g_array_index(priv->mac_address_blacklist.arr, const char *, i);
if (!nm_utils_hwaddr_valid(mac, ETH_ALEN)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("'%s' is not a valid MAC address"),
mac);
g_prefix_error(error,
"%s.%s: ",
NM_SETTING_WIRELESS_SETTING_NAME,
NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST);
return FALSE;
if (!nm_utils_hwaddr_valid(mac, ETH_ALEN)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("'%s' is not a valid MAC address"),
mac);
g_prefix_error(error,
"%s.%s: ",
NM_SETTING_WIRELESS_SETTING_NAME,
NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST);
return FALSE;
}
}
}
@ -1216,12 +1225,6 @@ nm_setting_wireless_get_wake_on_wlan(NMSettingWireless *setting)
return NM_SETTING_WIRELESS_GET_PRIVATE(setting)->wake_on_wlan;
}
static void
clear_blacklist_item(char **item_p)
{
g_free(*item_p);
}
/*****************************************************************************/
static void
@ -1234,9 +1237,6 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
case PROP_CLONED_MAC_ADDRESS:
g_value_set_string(value, nm_setting_wireless_get_cloned_mac_address(setting));
break;
case PROP_MAC_ADDRESS_BLACKLIST:
g_value_set_boxed(value, nm_g_array_data(priv->mac_address_blacklist));
break;
case PROP_SEEN_BSSIDS:
g_value_take_boxed(
value,
@ -1255,8 +1255,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
{
NMSettingWireless *self = NM_SETTING_WIRELESS(object);
NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE(self);
const char *const *blacklist;
const char *mac;
gboolean bool_val;
_PropertyEnums prop1 = PROP_0;
_PropertyEnums prop2 = PROP_0;
@ -1281,18 +1279,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
nm_gobject_notify_together(self, prop1, prop2);
break;
case PROP_MAC_ADDRESS_BLACKLIST:
blacklist = g_value_get_boxed(value);
g_array_set_size(priv->mac_address_blacklist, 0);
if (blacklist && blacklist[0]) {
gsize i;
for (i = 0; blacklist[i]; i++) {
mac = _nm_utils_hwaddr_canonical_or_invalid(blacklist[i], ETH_ALEN);
g_array_append_val(priv->mac_address_blacklist, mac);
}
}
break;
case PROP_SEEN_BSSIDS:
{
gs_unref_ptrarray GPtrArray *arr_old = NULL;
@ -1321,13 +1307,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
static void
nm_setting_wireless_init(NMSettingWireless *setting)
{
NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
/* We use GArray rather than GPtrArray so it will automatically be NULL-terminated */
priv->mac_address_blacklist = g_array_new(TRUE, FALSE, sizeof(char *));
g_array_set_clear_func(priv->mac_address_blacklist, (GDestroyNotify) clear_blacklist_item);
}
{}
/**
* nm_setting_wireless_new:
@ -1348,7 +1328,6 @@ finalize(GObject *object)
NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE(object);
g_free(priv->cloned_mac_address);
g_array_unref(priv->mac_address_blacklist);
nm_g_ptr_array_unref(priv->seen_bssids);
G_OBJECT_CLASS(nm_setting_wireless_parent_class)->finalize(object);
@ -1733,11 +1712,15 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
* is listed.
* ---end---
*/
_nm_setting_property_define_gprop_strv_oldstyle(properties_override,
obj_properties,
NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST,
PROP_MAC_ADDRESS_BLACKLIST,
NM_SETTING_PARAM_FUZZY_IGNORE);
_nm_setting_property_define_direct_strv(properties_override,
obj_properties,
NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST,
PROP_MAC_ADDRESS_BLACKLIST,
NM_SETTING_PARAM_FUZZY_IGNORE,
NMSettingWirelessPrivate,
mac_address_blacklist,
.direct_set_strv_normalize_hwaddr = TRUE,
.direct_strv_not_null = TRUE);
/**
* NMSettingWireless:seen-bssids:

View file

@ -8,6 +8,8 @@
#include "nm-setting.h"
#include <linux/if_ether.h>
#include "libnm-core-intern/nm-core-internal.h"
#include "libnm-glib-aux/nm-ref-string.h"
#include "libnm-glib-aux/nm-secret-utils.h"
@ -739,10 +741,29 @@ _property_direct_set_strv(const NMSettInfoSetting *sett_info,
if (!property_info->direct_strv_preserve_empty && strv && !strv[0])
strv = NULL;
if (nm_strvarray_equal_strv(p_val->arr, strv, -1))
return FALSE;
if (property_info->direct_set_strv_normalize_hwaddr) {
gs_unref_array GArray *arr = NULL;
if (strv) {
nm_strvarray_ensure(&arr);
for (; strv[0]; strv++) {
nm_strvarray_add_take(arr,
_nm_utils_hwaddr_canonical_or_invalid(strv[0], ETH_ALEN));
}
}
if (nm_strvarray_equal(p_val->arr, arr))
return FALSE;
NM_SWAP(&p_val->arr, &arr);
return TRUE;
}
if (nm_strvarray_equal_strv(p_val->arr, strv, -1)) {
return FALSE;
}
nm_strvarray_set_strv_full(&p_val->arr, strv, property_info->direct_strv_preserve_empty);
return TRUE;
}
@ -844,7 +865,7 @@ _nm_setting_property_get_property_direct(GObject *object,
value,
nm_strvarray_get_strv_full_dup(p_val->arr,
NULL,
FALSE,
property_info->direct_strv_not_null,
property_info->direct_strv_preserve_empty));
return;
}

View file

@ -821,6 +821,10 @@ struct _NMSettInfoProperty {
* normalize the string via g_ascii_strdown(). */
bool direct_set_string_ascii_strdown : 1;
/* If TRUE, this is a NM_VALUE_TYPE_STRV direct property holding MAC addresses,
* and the setter will normalize them via _nm_utils_hwaddr_canonical_or_invalid(). */
bool direct_set_strv_normalize_hwaddr : 1;
/* If TRUE, this is a NM_VALUE_TYPE_STRING direct property, and the setter will
* normalize the string via g_strstrip(). */
bool direct_set_string_strip : 1;
@ -870,6 +874,10 @@ struct _NMSettInfoProperty {
* an empty array. */
bool direct_strv_preserve_empty : 1;
/* This flag indicates that an empty strv array should be returned
* instead of NULL if it hadn't been created yet. */
bool direct_strv_not_null : 1;
/* Usually, properties that are set to the default value for the GParamSpec
* are not serialized to GVariant (and NULL is returned by to_dbus_data().
* Set this flag to force always converting the property even if the value

View file

@ -74,6 +74,7 @@
#define NM_VERSION_1_42 (NM_ENCODE_VERSION(1, 42, 0))
#define NM_VERSION_1_44 (NM_ENCODE_VERSION(1, 44, 0))
#define NM_VERSION_1_46 (NM_ENCODE_VERSION(1, 46, 0))
#define NM_VERSION_1_48 (NM_ENCODE_VERSION(1, 48, 0))
/* For releases, NM_API_VERSION is equal to NM_VERSION.
*

View file

@ -383,6 +383,20 @@
#define NM_AVAILABLE_IN_1_46
#endif
#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_1_48
#define NM_DEPRECATED_IN_1_48 G_DEPRECATED
#define NM_DEPRECATED_IN_1_48_FOR(f) G_DEPRECATED_FOR(f)
#else
#define NM_DEPRECATED_IN_1_48
#define NM_DEPRECATED_IN_1_48_FOR(f)
#endif
#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_48
#define NM_AVAILABLE_IN_1_48 G_UNAVAILABLE(1, 48)
#else
#define NM_AVAILABLE_IN_1_48
#endif
/*
* Synchronous API for calling D-Bus in libnm is deprecated. See
* https://networkmanager.dev/docs/libnm/latest/usage.html#sync-api

View file

@ -648,9 +648,10 @@ nm_lldp_neighbor_tlv_get_oui(NMLldpNeighbor *n, uint8_t oui[static 3], uint8_t *
int r;
g_return_val_if_fail(n, -EINVAL);
g_return_val_if_fail(oui, -EINVAL);
g_return_val_if_fail(subtype, -EINVAL);
nm_assert(oui);
r = nm_lldp_neighbor_tlv_is_type(n, NM_LLDP_TYPE_PRIVATE);
if (r < 0)
return r;

View file

@ -3,11 +3,11 @@
libnm_systemd_core = static_library(
'nm-systemd-core',
sources: files(
'src/libsystemd-network/dhcp-identifier.c',
'src/libsystemd-network/dhcp6-network.c',
'src/libsystemd-network/dhcp6-option.c',
'src/libsystemd-network/dhcp6-protocol.c',
'src/libsystemd-network/network-common.c',
'src/libsystemd-network/sd-dhcp-duid.c',
'src/libsystemd-network/sd-dhcp6-client.c',
'src/libsystemd-network/sd-dhcp6-lease.c',
'src/libsystemd/sd-event/event-util.c',
@ -15,6 +15,7 @@ libnm_systemd_core = static_library(
'src/libsystemd/sd-id128/id128-util.c',
'src/libsystemd/sd-id128/sd-id128.c',
'nm-sd.c',
'sd-adapt-core/netif-util.c',
'sd-adapt-core/nm-sd-adapt-core.c',
),
include_directories: [
@ -28,6 +29,7 @@ libnm_systemd_core = static_library(
top_inc,
src_inc,
],
c_args: libnm_systemd_common_cflags,
dependencies: [
libnm_systemd_shared_dep_inc,
glib_dep,

View file

@ -0,0 +1,219 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "nm-sd-adapt-core.h"
#include <linux/if.h>
#include <linux/if_arp.h>
#include "arphrd-util.h"
#include "device-util.h"
#include "log-link.h"
#include "memory-util.h"
#include "netif-util.h"
#include "siphash24.h"
#include "sparse-endian.h"
#include "strv.h"
#if 0 /* NM_IGNORED */
bool netif_has_carrier(uint8_t operstate, unsigned flags) {
/* see Documentation/networking/operstates.txt in the kernel sources */
if (operstate == IF_OPER_UP)
return true;
if (operstate != IF_OPER_UNKNOWN)
return false;
/* operstate may not be implemented, so fall back to flags */
return FLAGS_SET(flags, IFF_LOWER_UP | IFF_RUNNING) &&
!FLAGS_SET(flags, IFF_DORMANT);
}
int net_get_type_string(sd_device *device, uint16_t iftype, char **ret) {
const char *t;
char *p;
if (device &&
sd_device_get_devtype(device, &t) >= 0 &&
!isempty(t)) {
p = strdup(t);
if (!p)
return -ENOMEM;
*ret = p;
return 0;
}
t = arphrd_to_name(iftype);
if (!t)
return -ENOENT;
p = strdup(t);
if (!p)
return -ENOMEM;
*ret = ascii_strlower(p);
return 0;
}
#endif /* NM_IGNORED */
const char *
net_get_persistent_name(sd_device *device)
{
assert(device);
/* fetch some persistent data unique (on this machine) to this device */
FOREACH_STRING(field,
"ID_NET_NAME_ONBOARD",
"ID_NET_NAME_SLOT",
"ID_NET_NAME_PATH",
"ID_NET_NAME_MAC")
{
const char *name;
if (sd_device_get_property_value(device, field, &name) >= 0)
return name;
}
return NULL;
}
#if 0 /* NM_IGNORED */
/* Used when generating hardware address by udev, and IPv4LL seed by networkd. */
#define HASH_KEY SD_ID128_MAKE(d3, 1e, 48, fa, 90, fe, 4b, 4c, 9d, af, d5, d7, a1, b1, 2e, 8a)
int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret) {
const char *name;
assert(device);
assert(ret);
/* net_get_persistent_name() will return one of the device names based on stable information about
* the device. If this is not available, we fall back to using the actual device name. */
name = net_get_persistent_name(device);
if (!name && use_sysname)
(void) sd_device_get_sysname(device, &name);
if (!name)
return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA),
"No stable identifying information found");
log_device_debug(device, "Using \"%s\" as stable identifying information", name);
return net_get_unique_predictable_data_from_name(name, &HASH_KEY, ret);
}
int net_get_unique_predictable_data_from_name(
const char *name,
const sd_id128_t *key,
uint64_t *ret) {
size_t l, sz;
uint8_t *v;
int r;
assert(name);
assert(key);
assert(ret);
l = strlen(name);
sz = sizeof(sd_id128_t) + l;
v = newa(uint8_t, sz);
/* Fetch some persistent data unique to this machine */
r = sd_id128_get_machine((sd_id128_t*) v);
if (r < 0)
return r;
memcpy(v + sizeof(sd_id128_t), name, l);
/* Let's hash the machine ID plus the device name. We use
* a fixed, but originally randomly created hash key here. */
*ret = htole64(siphash24(v, sz, key->bytes));
return 0;
}
typedef struct Link {
const char *ifname;
} Link;
int net_verify_hardware_address(
const char *ifname,
bool is_static,
uint16_t iftype,
const struct hw_addr_data *ib_hw_addr, /* current or parent HW address */
struct hw_addr_data *new_hw_addr) {
Link link = { .ifname = ifname };
assert(new_hw_addr);
if (new_hw_addr->length == 0)
return 0;
if (new_hw_addr->length != arphrd_to_hw_addr_len(iftype)) {
if (is_static)
log_link_warning(&link,
"Specified MAC address with invalid length (%zu, expected %zu), refusing.",
new_hw_addr->length, arphrd_to_hw_addr_len(iftype));
return -EINVAL;
}
switch (iftype) {
case ARPHRD_ETHER:
/* see eth_random_addr() in the kernel */
if (ether_addr_is_null(&new_hw_addr->ether)) {
if (is_static)
log_link_warning(&link, "Specified MAC address is null, refusing.");
return -EINVAL;
}
if (ether_addr_is_broadcast(&new_hw_addr->ether)) {
if (is_static)
log_link_warning(&link, "Specified MAC address is broadcast, refusing.");
return -EINVAL;
}
if (ether_addr_is_multicast(&new_hw_addr->ether)) {
if (is_static)
log_link_warning(&link, "Specified MAC address has the multicast bit set, clearing the bit.");
new_hw_addr->bytes[0] &= 0xfe;
}
if (!is_static && !ether_addr_is_local(&new_hw_addr->ether))
/* Adjust local assignment bit when the MAC address is generated randomly. */
new_hw_addr->bytes[0] |= 0x02;
break;
case ARPHRD_INFINIBAND:
/* see ipoib_check_lladdr() in the kernel */
assert(ib_hw_addr);
assert(ib_hw_addr->length == INFINIBAND_ALEN);
if (is_static &&
(!memeqzero(new_hw_addr->bytes, INFINIBAND_ALEN - 8) ||
memcmp(new_hw_addr->bytes, ib_hw_addr->bytes, INFINIBAND_ALEN - 8) != 0))
log_link_warning(&link, "Only the last 8 bytes of the InifniBand MAC address can be changed, ignoring the first 12 bytes.");
if (memeqzero(new_hw_addr->bytes + INFINIBAND_ALEN - 8, 8)) {
if (is_static)
log_link_warning(&link, "The last 8 bytes of the InfiniBand MAC address cannot be null, refusing.");
return -EINVAL;
}
memcpy(new_hw_addr->bytes, ib_hw_addr->bytes, INFINIBAND_ALEN - 8);
break;
default:
if (is_static)
log_link_warning(&link, "Unsupported interface type %s%u to set MAC address, refusing.",
strna(arphrd_to_name(iftype)), iftype);
return -EINVAL;
}
return 0;
}
#endif /* NM_IGNORED */

View file

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <inttypes.h>
#include <stdbool.h>
#include "sd-device.h"
#include "sd-id128.h"
#include "ether-addr-util.h"
bool netif_has_carrier(uint8_t operstate, unsigned flags);
int net_get_type_string(sd_device *device, uint16_t iftype, char **ret);
const char *net_get_persistent_name(sd_device *device);
int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret);
int
net_get_unique_predictable_data_from_name(const char *name, const sd_id128_t *key, uint64_t *ret);
int net_verify_hardware_address(const char *ifname,
bool is_static,
uint16_t iftype,
const struct hw_addr_data *ib_hw_addr,
struct hw_addr_data *new_hw_addr);

View file

@ -12,15 +12,6 @@
/*****************************************************************************/
int
asynchronous_close(int fd)
{
safe_close(fd);
return -1;
}
/*****************************************************************************/
sd_device *
sd_device_ref(sd_device *self)
{

View file

@ -86,7 +86,6 @@ sd_notify(int unset_environment, const char *state)
#include "sd-id128.h"
#include "sparse-endian.h"
#include "async.h"
#endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */

View file

@ -0,0 +1,83 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-device.h"
#include "sd-dhcp-duid.h"
#include "sd-id128.h"
#include "ether-addr-util.h"
#include "macro.h"
#include "sparse-endian.h"
#define SYSTEMD_PEN 43793
typedef enum DUIDType {
DUID_TYPE_LLT = SD_DUID_TYPE_LLT,
DUID_TYPE_EN = SD_DUID_TYPE_EN,
DUID_TYPE_LL = SD_DUID_TYPE_LL,
DUID_TYPE_UUID = SD_DUID_TYPE_UUID,
_DUID_TYPE_MAX,
_DUID_TYPE_INVALID = -EINVAL,
} DUIDType;
/* RFC 8415 section 11.1:
* A DUID consists of a 2-octet type code represented in network byte order, followed by a variable number of
* octets that make up the actual identifier. The length of the DUID (not including the type code) is at
* least 1 octet and at most 128 octets. */
#define MIN_DUID_DATA_LEN 1
#define MAX_DUID_DATA_LEN 128
#define MIN_DUID_LEN (sizeof(be16_t) + MIN_DUID_DATA_LEN)
#define MAX_DUID_LEN (sizeof(be16_t) + MAX_DUID_DATA_LEN)
/* https://tools.ietf.org/html/rfc3315#section-9.1 */
struct duid {
be16_t type;
union {
struct {
/* DUID_TYPE_LLT */
be16_t htype;
be32_t time;
uint8_t haddr[];
} _packed_ llt;
struct {
/* DUID_TYPE_EN */
be32_t pen;
uint8_t id[];
} _packed_ en;
struct {
/* DUID_TYPE_LL */
be16_t htype;
uint8_t haddr[];
} _packed_ ll;
struct {
/* DUID_TYPE_UUID */
sd_id128_t uuid;
} _packed_ uuid;
uint8_t data[MAX_DUID_DATA_LEN];
};
} _packed_;
typedef struct sd_dhcp_duid {
size_t size;
union {
struct duid duid;
uint8_t raw[MAX_DUID_LEN];
};
} sd_dhcp_duid;
static inline bool duid_size_is_valid(size_t size) {
return size >= MIN_DUID_LEN && size <= MAX_DUID_LEN;
}
static inline bool duid_data_size_is_valid(size_t size) {
return size >= MIN_DUID_DATA_LEN && size <= MAX_DUID_DATA_LEN;
}
const char *duid_type_to_string(DUIDType t) _const_;
int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret);
int dhcp_identifier_set_iaid(
sd_device *dev,
const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
void *ret);

View file

@ -1,252 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "nm-sd-adapt-core.h"
#include <linux/if_infiniband.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include "dhcp-identifier.h"
#include "netif-util.h"
#include "siphash24.h"
#include "sparse-endian.h"
#include "string-table.h"
#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
static const char * const duid_type_table[_DUID_TYPE_MAX] = {
[DUID_TYPE_LLT] = "DUID-LLT",
[DUID_TYPE_EN] = "DUID-EN/Vendor",
[DUID_TYPE_LL] = "DUID-LL",
[DUID_TYPE_UUID] = "UUID",
};
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict) {
struct duid d;
assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
if (duid_len > MAX_DUID_LEN)
return -EINVAL;
if (!strict)
/* Strict validation is not requested. We only ensure that the
* DUID is not too long. */
return 0;
switch (duid_type) {
case DUID_TYPE_LLT:
if (duid_len <= sizeof(d.llt))
return -EINVAL;
break;
case DUID_TYPE_EN:
if (duid_len != sizeof(d.en))
return -EINVAL;
break;
case DUID_TYPE_LL:
if (duid_len <= sizeof(d.ll))
return -EINVAL;
break;
case DUID_TYPE_UUID:
if (duid_len != sizeof(d.uuid))
return -EINVAL;
break;
default:
/* accept unknown type in order to be forward compatible */
break;
}
return 0;
}
#if 0 /* NM_IGNORED */
static int dhcp_identifier_set_duid_llt(
const struct hw_addr_data *hw_addr,
uint16_t arp_type,
usec_t t,
struct duid *ret_duid,
size_t *ret_len) {
uint16_t time_from_2000y;
assert(hw_addr);
assert(ret_duid);
assert(ret_len);
if (hw_addr->length == 0)
return -EOPNOTSUPP;
if (arp_type == ARPHRD_ETHER)
assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
else
return -EOPNOTSUPP;
if (t < USEC_2000)
time_from_2000y = 0;
else
time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT);
unaligned_write_be16(&ret_duid->llt.htype, arp_type);
unaligned_write_be32(&ret_duid->llt.time, time_from_2000y);
memcpy(ret_duid->llt.haddr, hw_addr->bytes, hw_addr->length);
*ret_len = offsetof(struct duid, llt.haddr) + hw_addr->length;
return 0;
}
static int dhcp_identifier_set_duid_ll(
const struct hw_addr_data *hw_addr,
uint16_t arp_type,
struct duid *ret_duid,
size_t *ret_len) {
assert(hw_addr);
assert(ret_duid);
assert(ret_len);
if (hw_addr->length == 0)
return -EOPNOTSUPP;
if (arp_type == ARPHRD_ETHER)
assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
else
return -EOPNOTSUPP;
unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL);
unaligned_write_be16(&ret_duid->ll.htype, arp_type);
memcpy(ret_duid->ll.haddr, hw_addr->bytes, hw_addr->length);
*ret_len = offsetof(struct duid, ll.haddr) + hw_addr->length;
return 0;
}
#endif /* NM_IGNORED */
int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len) {
sd_id128_t machine_id;
uint64_t hash;
int r;
assert(ret_duid);
assert(ret_len);
if (!test_mode) {
r = sd_id128_get_machine(&machine_id);
if (r < 0)
return r;
} else
/* For tests, especially for fuzzers, reproducibility is important.
* Hence, use a static and constant machine ID.
* See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN);
unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN);
/* a bit of snake-oil perhaps, but no need to expose the machine-id
* directly; duid->en.id might not be aligned, so we need to copy */
hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
memcpy(ret_duid->en.id, &hash, sizeof(ret_duid->en.id));
*ret_len = offsetof(struct duid, en.id) + sizeof(ret_duid->en.id);
if (test_mode)
assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0);
return 0;
}
#if 0 /* NM_IGNORED */
static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) {
sd_id128_t machine_id;
int r;
assert(ret_duid);
assert(ret_len);
r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
if (r < 0)
return r;
unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID);
memcpy(&ret_duid->uuid.uuid, &machine_id, sizeof(machine_id));
*ret_len = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
return 0;
}
int dhcp_identifier_set_duid(
DUIDType duid_type,
const struct hw_addr_data *hw_addr,
uint16_t arp_type,
usec_t llt_time,
bool test_mode,
struct duid *ret_duid,
size_t *ret_len) {
switch (duid_type) {
case DUID_TYPE_LLT:
return dhcp_identifier_set_duid_llt(hw_addr, arp_type, llt_time, ret_duid, ret_len);
case DUID_TYPE_EN:
return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len);
case DUID_TYPE_LL:
return dhcp_identifier_set_duid_ll(hw_addr, arp_type, ret_duid, ret_len);
case DUID_TYPE_UUID:
return dhcp_identifier_set_duid_uuid(ret_duid, ret_len);
default:
return -EINVAL;
}
}
#endif /* NM_IGNORED */
int dhcp_identifier_set_iaid(
sd_device *dev,
const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
void *ret) {
#if 0 /* NM_IGNORED */
const char *name = NULL;
uint32_t id32;
uint64_t id;
assert(hw_addr);
assert(ret);
if (dev)
name = net_get_persistent_name(dev);
if (name)
id = siphash24(name, strlen(name), HASH_KEY.bytes);
else
/* fall back to MAC address if no predictable name available */
id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
id32 = (id & 0xffffffff) ^ (id >> 32);
if (legacy_unstable_byteorder)
/* for historical reasons (a bug), the bits were swapped and thus
* the result was endianness dependent. Preserve that behavior. */
id32 = bswap_32(id32);
else
/* the fixed behavior returns a stable byte order. Since LE is expected
* to be more common, swap the bytes on LE to give the same as legacy
* behavior. */
id32 = be32toh(id32);
unaligned_write_ne32(ret, id32);
return 0;
#else /* NM_IGNORED */
/* for NetworkManager, we don't use this function and we should never call here.
* This got replaced by nm_utils_create_dhcp_iaid(). */
g_return_val_if_reached (-EINVAL);
#endif /* NM_IGNORED */
}

View file

@ -1,75 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-device.h"
#include "sd-id128.h"
#include "ether-addr-util.h"
#include "macro.h"
#include "sparse-endian.h"
#include "time-util.h"
#include "unaligned.h"
#define SYSTEMD_PEN 43793
typedef enum DUIDType {
DUID_TYPE_LLT = 1,
DUID_TYPE_EN = 2,
DUID_TYPE_LL = 3,
DUID_TYPE_UUID = 4,
_DUID_TYPE_MAX,
_DUID_TYPE_INVALID = -EINVAL,
} DUIDType;
/* RFC 3315 section 9.1:
* A DUID can be no more than 128 octets long (not including the type code).
*/
#define MAX_DUID_LEN 128
/* https://tools.ietf.org/html/rfc3315#section-9.1 */
struct duid {
be16_t type;
union {
struct {
/* DUID_TYPE_LLT */
be16_t htype;
be32_t time;
uint8_t haddr[0];
} _packed_ llt;
struct {
/* DUID_TYPE_EN */
be32_t pen;
uint8_t id[8];
} _packed_ en;
struct {
/* DUID_TYPE_LL */
be16_t htype;
uint8_t haddr[0];
} _packed_ ll;
struct {
/* DUID_TYPE_UUID */
sd_id128_t uuid;
} _packed_ uuid;
struct {
uint8_t data[MAX_DUID_LEN];
} _packed_ raw;
};
} _packed_;
int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict);
int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len);
int dhcp_identifier_set_duid(
DUIDType duid_type,
const struct hw_addr_data *hw_addr,
uint16_t arp_type,
usec_t llt_time,
bool test_mode,
struct duid *ret_duid,
size_t *ret_len);
int dhcp_identifier_set_iaid(
sd_device *dev,
const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
void *ret);
const char *duid_type_to_string(DUIDType t) _const_;

View file

@ -0,0 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-dhcp6-client.h"
int dhcp6_client_set_state_callback(
sd_dhcp6_client *client,
sd_dhcp6_client_callback_t cb,
void *userdata);
int dhcp6_client_get_state(sd_dhcp6_client *client);

View file

@ -11,7 +11,8 @@
#include "sd-event.h"
#include "sd-dhcp6-client.h"
#include "dhcp-identifier.h"
#include "dhcp-duid-internal.h"
#include "dhcp6-client-internal.h"
#include "dhcp6-option.h"
#include "dhcp6-protocol.h"
#include "ether-addr-util.h"
@ -63,8 +64,7 @@ struct sd_dhcp6_client {
DHCP6IA ia_na;
DHCP6IA ia_pd;
DHCP6RequestIA request_ia;
struct duid duid;
size_t duid_len;
sd_dhcp_duid duid;
be16_t *req_opts;
size_t n_req_opts;
char *fqdn;
@ -79,10 +79,9 @@ struct sd_dhcp6_client {
sd_dhcp6_client_callback_t callback;
void *userdata;
sd_dhcp6_client_callback_t state_callback;
void *state_userdata;
bool send_release;
/* Ignore machine-ID when generating DUID. See dhcp_identifier_set_duid_en(). */
bool test_mode;
};
int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
@ -90,7 +89,6 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
const void *packet, size_t len);
int dhcp6_client_send_message(sd_dhcp6_client *client);
void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode);
int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
#define log_dhcp6_client_errno(client, error, fmt, ...) \

View file

@ -10,7 +10,9 @@
#include "sd-dhcp6-lease.h"
#include "dhcp6-option.h"
#include "dhcp6-protocol.h"
#include "macro.h"
#include "set.h"
#include "time-util.h"
struct sd_dhcp6_lease {
@ -43,9 +45,11 @@ struct sd_dhcp6_lease {
struct in6_addr *sntp;
size_t sntp_count;
char *fqdn;
char *captive_portal;
struct sd_dhcp6_option **sorted_vendor_options;
Set *vendor_options;
};
int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid);
int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len);
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
@ -60,6 +64,7 @@ int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t
int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_set_captive_portal(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_new(sd_dhcp6_lease **ret);
int dhcp6_lease_new_from_message(
@ -69,3 +74,17 @@ int dhcp6_lease_new_from_message(
const triple_timestamp *timestamp,
const struct in6_addr *server_address,
sd_dhcp6_lease **ret);
#define _FOREACH_DHCP6_ADDRESS(lease, it) \
for (int it = sd_dhcp6_lease_address_iterator_reset(lease); \
it > 0; \
it = sd_dhcp6_lease_address_iterator_next(lease))
#define FOREACH_DHCP6_ADDRESS(lease) \
_FOREACH_DHCP6_ADDRESS(lease, UNIQ_T(i, UNIQ))
#define _FOREACH_DHCP6_PD_PREFIX(lease, it) \
for (int it = sd_dhcp6_lease_pd_iterator_reset(lease); \
it > 0; \
it = sd_dhcp6_lease_pd_iterator_next(lease))
#define FOREACH_DHCP6_PD_PREFIX(lease) \
_FOREACH_DHCP6_PD_PREFIX(lease, UNIQ_T(i, UNIQ))

View file

@ -17,6 +17,7 @@
#include "dns-domain.h"
#include "escape.h"
#include "memory-util.h"
#include "network-common.h"
#include "strv.h"
#include "unaligned.h"
@ -526,6 +527,26 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s
return status;
}
/* parse a string from dhcp option field. *ret must be initialized */
int dhcp6_option_parse_string(const uint8_t *data, size_t data_len, char **ret) {
_cleanup_free_ char *string = NULL;
int r;
assert(data || data_len == 0);
assert(ret);
if (data_len <= 0) {
*ret = mfree(*ret);
return 0;
}
r = make_cstring((const char *) data, data_len, MAKE_CSTRING_REFUSE_TRAILING_NUL, &string);
if (r < 0)
return r;
return free_and_replace(*ret, string);
}
static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) {
int r;
@ -567,7 +588,7 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t
static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) {
_cleanup_free_ DHCP6Address *a = NULL;
uint32_t lt_valid, lt_pref;
usec_t lt_valid, lt_pref;
int r;
assert(ia);
@ -586,17 +607,18 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, c
memcpy(&a->iaaddr, data, sizeof(struct iaaddr));
lt_valid = be32toh(a->iaaddr.lifetime_valid);
lt_pref = be32toh(a->iaaddr.lifetime_preferred);
lt_valid = be32_sec_to_usec(a->iaaddr.lifetime_valid, /* max_as_infinity = */ true);
lt_pref = be32_sec_to_usec(a->iaaddr.lifetime_preferred, /* max_as_infinity = */ true);
if (lt_valid == 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA address with zero valid lifetime, ignoring.");
if (lt_pref > lt_valid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA address with preferred lifetime %"PRIu32
" larger than valid lifetime %"PRIu32", ignoring.",
lt_pref, lt_valid);
"Received an IA address with preferred lifetime %s "
"larger than valid lifetime %s, ignoring.",
FORMAT_TIMESPAN(lt_pref, USEC_PER_SEC),
FORMAT_TIMESPAN(lt_valid, USEC_PER_SEC));
if (len > sizeof(struct iaaddr)) {
r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iaaddr), len - sizeof(struct iaaddr));
@ -610,7 +632,7 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, c
static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) {
_cleanup_free_ DHCP6Address *a = NULL;
uint32_t lt_valid, lt_pref;
usec_t lt_valid, lt_pref;
int r;
assert(ia);
@ -629,17 +651,18 @@ static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia,
memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
lt_valid = be32toh(a->iapdprefix.lifetime_valid);
lt_pref = be32toh(a->iapdprefix.lifetime_preferred);
lt_valid = be32_sec_to_usec(a->iapdprefix.lifetime_valid, /* max_as_infinity = */ true);
lt_pref = be32_sec_to_usec(a->iapdprefix.lifetime_preferred, /* max_as_infinity = */ true);
if (lt_valid == 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received a PD prefix with zero valid lifetime, ignoring.");
if (lt_pref > lt_valid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received a PD prefix with preferred lifetime %"PRIu32
" larger than valid lifetime %"PRIu32", ignoring.",
lt_pref, lt_valid);
"Received a PD prefix with preferred lifetime %s "
"larger than valid lifetime %s, ignoring.",
FORMAT_TIMESPAN(lt_pref, USEC_PER_SEC),
FORMAT_TIMESPAN(lt_valid, USEC_PER_SEC));
if (len > sizeof(struct iapdprefix)) {
r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iapdprefix), len - sizeof(struct iapdprefix));
@ -660,7 +683,7 @@ int dhcp6_option_parse_ia(
DHCP6IA **ret) {
_cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
uint32_t lt_t1, lt_t2;
usec_t lt_t1, lt_t2;
size_t header_len;
int r;
@ -710,17 +733,18 @@ int dhcp6_option_parse_ia(
"from the one chosen by the client, ignoring.");
/* It is not necessary to check if the lifetime_t2 is zero here, as in that case it will be updated later. */
lt_t1 = be32toh(ia->header.lifetime_t1);
lt_t2 = be32toh(ia->header.lifetime_t2);
lt_t1 = be32_sec_to_usec(ia->header.lifetime_t1, /* max_as_infinity = */ true);
lt_t2 = be32_sec_to_usec(ia->header.lifetime_t2, /* max_as_infinity = */ true);
if (lt_t1 > lt_t2)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.",
lt_t1, lt_t2);
"Received an IA option with T1 %s > T2 %s, ignoring.",
FORMAT_TIMESPAN(lt_t1, USEC_PER_SEC),
FORMAT_TIMESPAN(lt_t2, USEC_PER_SEC));
if (lt_t1 == 0 && lt_t2 > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA option with zero T1 and non-zero T2 (%"PRIu32"sec), ignoring.",
lt_t2);
"Received an IA option with zero T1 and non-zero T2 (%s), ignoring.",
FORMAT_TIMESPAN(lt_t2, USEC_PER_SEC));
for (size_t offset = header_len; offset < option_data_len;) {
const uint8_t *subdata;

View file

@ -88,6 +88,7 @@ int dhcp6_option_parse(
size_t *ret_option_data_len,
const uint8_t **ret_option_data);
int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message);
int dhcp6_option_parse_string(const uint8_t *data, size_t data_len, char **ret);
int dhcp6_option_parse_ia(
sd_dhcp6_client *client,
be32_t iaid,

View file

@ -2,8 +2,11 @@
#include "nm-sd-adapt-core.h"
#include "env-util.h"
#include "format-util.h"
#include "network-common.h"
#include "socket-util.h"
#include "unaligned.h"
int get_ifname(int ifindex, char **ifname) {
assert(ifname);
@ -15,3 +18,111 @@ int get_ifname(int ifindex, char **ifname) {
return format_ifname_alloc(ifindex, ifname);
}
usec_t unaligned_be32_sec_to_usec(const void *p, bool max_as_infinity) {
uint32_t s = unaligned_read_be32(ASSERT_PTR(p));
if (s == UINT32_MAX && max_as_infinity)
return USEC_INFINITY;
return s * USEC_PER_SEC;
}
usec_t be32_sec_to_usec(be32_t t, bool max_as_infinity) {
uint32_t s = be32toh(t);
if (s == UINT32_MAX && max_as_infinity)
return USEC_INFINITY;
return s * USEC_PER_SEC;
}
usec_t be32_msec_to_usec(be32_t t, bool max_as_infinity) {
uint32_t s = be32toh(t);
if (s == UINT32_MAX && max_as_infinity)
return USEC_INFINITY;
return s * USEC_PER_MSEC;
}
usec_t be16_sec_to_usec(be16_t t, bool max_as_infinity) {
uint16_t s = be16toh(t);
if (s == UINT16_MAX && max_as_infinity)
return USEC_INFINITY;
return s * USEC_PER_SEC;
}
be32_t usec_to_be32_sec(usec_t t) {
if (t == USEC_INFINITY)
/* Some settings, e.g. a lifetime of an address, UINT32_MAX is handled as infinity. so let's
* map USEC_INFINITY to UINT32_MAX. */
return htobe32(UINT32_MAX);
if (t >= (UINT32_MAX - 1) * USEC_PER_SEC)
/* Finite but too large. Let's use the largest (or off-by-one from the largest) finite value. */
return htobe32(UINT32_MAX - 1);
return htobe32((uint32_t) DIV_ROUND_UP(t, USEC_PER_SEC));
}
be32_t usec_to_be32_msec(usec_t t) {
if (t == USEC_INFINITY)
return htobe32(UINT32_MAX);
if (t >= (UINT32_MAX - 1) * USEC_PER_MSEC)
return htobe32(UINT32_MAX - 1);
return htobe32((uint32_t) DIV_ROUND_UP(t, USEC_PER_MSEC));
}
be16_t usec_to_be16_sec(usec_t t) {
if (t == USEC_INFINITY)
return htobe16(UINT16_MAX);
if (t >= (UINT16_MAX - 1) * USEC_PER_SEC)
return htobe16(UINT16_MAX - 1);
return htobe16((uint16_t) DIV_ROUND_UP(t, USEC_PER_SEC));
}
usec_t time_span_to_stamp(usec_t span, usec_t base) {
/* Typically, 0 lifetime (timespan) indicates the corresponding configuration (address or so) must be
* dropped. So, when the timespan is zero, here we return 0 rather than 'base'. This makes the caller
* easily understand that the configuration needs to be dropped immediately. */
if (span == 0)
return 0;
return usec_add(base, span);
}
bool network_test_mode_enabled(void) {
static int test_mode = -1;
int r;
if (test_mode < 0) {
r = getenv_bool("SYSTEMD_NETWORK_TEST_MODE");
if (r < 0) {
if (r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_NETWORK_TEST_MODE environment variable, ignoring: %m");
test_mode = false;
} else
test_mode = r;
}
return test_mode;
}
triple_timestamp* triple_timestamp_from_cmsg(triple_timestamp *t, struct msghdr *mh) {
assert(t);
assert(mh);
struct timeval *tv = CMSG_FIND_AND_COPY_DATA(mh, SOL_SOCKET, SCM_TIMESTAMP, struct timeval);
if (tv)
return triple_timestamp_from_realtime(t, timeval_load(tv));
return triple_timestamp_now(t);
}

View file

@ -1,7 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <sys/socket.h>
#include "log-link.h"
#include "sparse-endian.h"
#include "time-util.h"
#define log_interface_prefix_full_errno_zerook(prefix, type, val, error, fmt, ...) \
({ \
@ -28,3 +32,18 @@
})
int get_ifname(int ifindex, char **ifname);
usec_t unaligned_be32_sec_to_usec(const void *p, bool max_as_infinity);
usec_t be32_sec_to_usec(be32_t t, bool max_as_infinity);
usec_t be32_msec_to_usec(be32_t t, bool max_as_infinity);
usec_t be16_sec_to_usec(be16_t t, bool max_as_infinity);
be32_t usec_to_be32_sec(usec_t t);
be32_t usec_to_be32_msec(usec_t t);
be16_t usec_to_be16_sec(usec_t t);
usec_t time_span_to_stamp(usec_t span, usec_t base);
bool network_test_mode_enabled(void);
triple_timestamp* triple_timestamp_from_cmsg(triple_timestamp *t, struct msghdr *mh);
#define TRIPLE_TIMESTAMP_FROM_CMSG(mh) \
triple_timestamp_from_cmsg(&(triple_timestamp) {}, mh)

View file

@ -0,0 +1,290 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "nm-sd-adapt-core.h"
#include <linux/if_infiniband.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include "dhcp-duid-internal.h"
#include "hexdecoct.h"
#include "netif-util.h"
#include "network-common.h"
#include "siphash24.h"
#include "string-table.h"
#include "unaligned.h"
#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
static const char * const duid_type_table[_DUID_TYPE_MAX] = {
[DUID_TYPE_LLT] = "DUID-LLT",
[DUID_TYPE_EN] = "DUID-EN/Vendor",
[DUID_TYPE_LL] = "DUID-LL",
[DUID_TYPE_UUID] = "UUID",
};
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
int sd_dhcp_duid_clear(sd_dhcp_duid *duid) {
assert_return(duid, -EINVAL);
*duid = (sd_dhcp_duid) {};
return 0;
}
int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid) {
if (!duid)
return false;
return duid_size_is_valid(duid->size);
}
int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size) {
assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
assert_return(ret_type, -EINVAL);
assert_return(ret_data, -EINVAL);
assert_return(ret_size, -EINVAL);
*ret_type = be16toh(duid->duid.type);
*ret_data = duid->duid.data;
*ret_size = duid->size - offsetof(struct duid, data);
return 0;
}
int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size) {
assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
assert_return(ret_data, -EINVAL);
assert_return(ret_size, -EINVAL);
/* Unlike sd_dhcp_duid_get(), this returns whole DUID including its type. */
*ret_data = duid->raw;
*ret_size = duid->size;
return 0;
}
int sd_dhcp_duid_set(
sd_dhcp_duid *duid,
uint16_t duid_type,
const void *data,
size_t data_size) {
assert_return(duid, -EINVAL);
assert_return(data, -EINVAL);
if (!duid_data_size_is_valid(data_size))
return -EINVAL;
unaligned_write_be16(&duid->duid.type, duid_type);
memcpy(duid->duid.data, data, data_size);
duid->size = offsetof(struct duid, data) + data_size;
return 0;
}
int sd_dhcp_duid_set_raw(
sd_dhcp_duid *duid,
const void *data,
size_t data_size) {
assert_return(duid, -EINVAL);
assert_return(data, -EINVAL);
/* Unlike sd_dhcp_duid_set(), this takes whole DUID including its type. */
if (!duid_size_is_valid(data_size))
return -EINVAL;
memcpy(duid->raw, data, data_size);
duid->size = data_size;
return 0;
}
int sd_dhcp_duid_set_llt(
sd_dhcp_duid *duid,
const void *hw_addr,
size_t hw_addr_size,
uint16_t arp_type,
uint64_t usec) {
uint16_t time_from_2000y;
assert_return(duid, -EINVAL);
assert_return(hw_addr, -EINVAL);
if (arp_type == ARPHRD_ETHER)
assert_return(hw_addr_size == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL);
else
return -EOPNOTSUPP;
time_from_2000y = (uint16_t) ((usec_sub_unsigned(usec, USEC_2000) / USEC_PER_SEC) & 0xffffffff);
unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LLT);
unaligned_write_be16(&duid->duid.llt.htype, arp_type);
unaligned_write_be32(&duid->duid.llt.time, time_from_2000y);
memcpy(duid->duid.llt.haddr, hw_addr, hw_addr_size);
duid->size = offsetof(struct duid, llt.haddr) + hw_addr_size;
return 0;
}
int sd_dhcp_duid_set_ll(
sd_dhcp_duid *duid,
const void *hw_addr,
size_t hw_addr_size,
uint16_t arp_type) {
assert_return(duid, -EINVAL);
assert_return(hw_addr, -EINVAL);
if (arp_type == ARPHRD_ETHER)
assert_return(hw_addr_size == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL);
else
return -EOPNOTSUPP;
unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LL);
unaligned_write_be16(&duid->duid.ll.htype, arp_type);
memcpy(duid->duid.ll.haddr, hw_addr, hw_addr_size);
duid->size = offsetof(struct duid, ll.haddr) + hw_addr_size;
return 0;
}
int sd_dhcp_duid_set_en(sd_dhcp_duid *duid) {
sd_id128_t machine_id;
bool test_mode;
uint64_t hash;
int r;
assert_return(duid, -EINVAL);
test_mode = network_test_mode_enabled();
if (!test_mode) {
r = sd_id128_get_machine(&machine_id);
if (r < 0)
return r;
} else
/* For tests, especially for fuzzers, reproducibility is important.
* Hence, use a static and constant machine ID.
* See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_EN);
unaligned_write_be32(&duid->duid.en.pen, SYSTEMD_PEN);
/* a bit of snake-oil perhaps, but no need to expose the machine-id
* directly; duid->en.id might not be aligned, so we need to copy */
hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
memcpy(duid->duid.en.id, &hash, sizeof(hash));
duid->size = offsetof(struct duid, en.id) + sizeof(hash);
if (test_mode)
assert_se(memcmp(&duid->duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, duid->size) == 0);
return 0;
}
int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid) {
sd_id128_t machine_id;
int r;
assert_return(duid, -EINVAL);
r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
if (r < 0)
return r;
unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_UUID);
memcpy(&duid->duid.uuid.uuid, &machine_id, sizeof(machine_id));
duid->size = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
return 0;
}
int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret) {
_cleanup_free_ char *p = NULL, *x = NULL;
const char *t;
assert(data);
assert(ret);
if (!duid_data_size_is_valid(data_size))
return -EINVAL;
x = hexmem(data, data_size);
if (!x)
return -ENOMEM;
t = duid_type_to_string(type);
if (!t)
return asprintf(ret, "%04x:%s", htobe16(type), x);
p = strjoin(t, ":", x);
if (!p)
return -ENOMEM;
*ret = TAKE_PTR(p);
return 0;
}
int sd_dhcp_duid_to_string(const sd_dhcp_duid *duid, char **ret) {
uint16_t type;
const void *data;
size_t data_size;
int r;
assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
assert_return(ret, -EINVAL);
r = sd_dhcp_duid_get(duid, &type, &data, &data_size);
if (r < 0)
return r;
return dhcp_duid_to_string_internal(type, data, data_size, ret);
}
int dhcp_identifier_set_iaid(
sd_device *dev,
const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
void *ret) {
const char *name = NULL;
uint32_t id32;
uint64_t id;
assert(hw_addr);
assert(ret);
if (dev)
name = net_get_persistent_name(dev);
if (name)
id = siphash24(name, strlen(name), HASH_KEY.bytes);
else
/* fall back to MAC address if no predictable name available */
id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
id32 = (id & 0xffffffff) ^ (id >> 32);
if (legacy_unstable_byteorder)
/* for historical reasons (a bug), the bits were swapped and thus
* the result was endianness dependent. Preserve that behavior. */
id32 = bswap_32(id32);
else
/* the fixed behavior returns a stable byte order. Since LE is expected
* to be more common, swap the bytes on LE to give the same as legacy
* behavior. */
id32 = be32toh(id32);
unaligned_write_ne32(ret, id32);
return 0;
}

View file

@ -7,27 +7,22 @@
#include <errno.h>
#include <sys/ioctl.h>
#if 0 /* NM_IGNORED */
#include <linux/if_arp.h>
#else /* NM_IGNORED */
#include <net/if_arp.h>
#endif /* NM_IGNORED */
#include <linux/if_infiniband.h>
#include "sd-dhcp6-client.h"
#include "alloc-util.h"
#include "device-util.h"
#include "dhcp-identifier.h"
#include "dhcp-duid-internal.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#include "dns-domain.h"
#include "event-util.h"
#include "fd-util.h"
#include "hexdecoct.h"
#include "hostname-util.h"
#include "in-addr-util.h"
#include "io-util.h"
#include "iovec-util.h"
#include "random-util.h"
#include "socket-util.h"
#include "sort-util.h"
@ -52,6 +47,19 @@ int sd_dhcp6_client_set_callback(
return 0;
}
int dhcp6_client_set_state_callback(
sd_dhcp6_client *client,
sd_dhcp6_client_callback_t cb,
void *userdata) {
assert_return(client, -EINVAL);
client->state_callback = cb;
client->state_userdata = userdata;
return 0;
}
int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
@ -184,10 +192,10 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *
static int client_ensure_duid(sd_dhcp6_client *client) {
assert(client);
if (client->duid_len != 0)
if (sd_dhcp_duid_is_set(&client->duid))
return 0;
return dhcp_identifier_set_duid_en(client->test_mode, &client->duid, &client->duid_len);
return sd_dhcp6_client_set_duid_en(client);
}
/**
@ -195,101 +203,102 @@ static int client_ensure_duid(sd_dhcp6_client *client) {
* without further modification. Otherwise, if duid_type is supported, DUID
* is set based on that type. Otherwise, an error is returned.
*/
static int dhcp6_client_set_duid_internal(
sd_dhcp6_client *client,
DUIDType duid_type,
const void *duid,
size_t duid_len,
usec_t llt_time) {
int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time) {
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
assert_return(duid_len == 0 || duid, -EINVAL);
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);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
log_dhcp6_client(client, "Using DUID of type %i of incorrect length, proceeding.", duid_type);
}
client->duid.type = htobe16(duid_type);
memcpy(&client->duid.raw.data, duid, duid_len);
client->duid_len = sizeof(client->duid.type) + duid_len;
} else {
#if 0 /* NM_IGNORED */
r = dhcp_identifier_set_duid(duid_type, &client->hw_addr, client->arp_type, llt_time,
client->test_mode, &client->duid, &client->duid_len);
if (r == -EOPNOTSUPP)
return log_dhcp6_client_errno(client, r,
"Failed to set %s. MAC address is not set or "
"interface type is not supported.",
duid_type_to_string(duid_type));
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set %s: %m",
duid_type_to_string(duid_type));
#else /* NM_IGNORED */
g_return_val_if_reached (-EINVAL);
#endif /* NM_IGNORED */
}
r = sd_dhcp_duid_set_llt(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, llt_time);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m");
return 0;
}
int sd_dhcp6_client_set_duid(
sd_dhcp6_client *client,
uint16_t duid_type,
const void *duid,
size_t duid_len) {
return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0);
}
int sd_dhcp6_client_set_duid_llt(
sd_dhcp6_client *client,
usec_t llt_time) {
return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
}
int sd_dhcp6_client_duid_as_string(
sd_dhcp6_client *client,
char **duid) {
_cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
const char *v;
int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client) {
int r;
assert_return(client, -EINVAL);
assert_return(client->duid_len > 0, -ENODATA);
assert_return(duid, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
v = duid_type_to_string(be16toh(client->duid.type));
if (v) {
s = strdup(v);
if (!s)
return -ENOMEM;
} else {
r = asprintf(&s, "%0x", client->duid.type);
if (r < 0)
return -ENOMEM;
}
t = hexmem(&client->duid.raw.data, client->duid_len);
if (!t)
return -ENOMEM;
p = strjoin(s, ":", t);
if (!p)
return -ENOMEM;
*duid = TAKE_PTR(p);
r = sd_dhcp_duid_set_ll(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m");
return 0;
}
int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client) {
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
r = sd_dhcp_duid_set_en(&client->duid);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m");
return 0;
}
int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client) {
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
r = sd_dhcp_duid_set_uuid(&client->duid);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m");
return 0;
}
int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len) {
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
assert_return(duid || duid_len == 0, -EINVAL);
r = sd_dhcp_duid_set(&client->duid, duid_type, duid, duid_len);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID: %m");
return 0;
}
int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, const sd_dhcp_duid *duid) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
client->duid = *duid;
return 0;
}
int sd_dhcp6_client_get_duid(sd_dhcp6_client *client, const sd_dhcp_duid **ret) {
assert_return(client, -EINVAL);
assert_return(ret, -EINVAL);
if (!sd_dhcp_duid_is_set(&client->duid))
return -ENODATA;
*ret = &client->duid;
return 0;
}
int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret) {
assert_return(client, -EINVAL);
assert_return(ret, -EINVAL);
if (!sd_dhcp_duid_is_set(&client->duid))
return -ENODATA;
return sd_dhcp_duid_to_string(&client->duid, ret);
}
int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
@ -335,12 +344,6 @@ int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) {
return 0;
}
void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode) {
assert(client);
client->test_mode = test_mode;
}
int sd_dhcp6_client_set_fqdn(
sd_dhcp6_client *client,
const char *fqdn) {
@ -491,7 +494,7 @@ int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) {
assert(client);
assert(client->test_mode);
assert_se(network_test_mode_enabled());
/* This is for tests or fuzzers. */
@ -510,7 +513,6 @@ int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) {
int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
client->send_release = enable;
return 0;
@ -552,6 +554,15 @@ static void client_set_state(sd_dhcp6_client *client, DHCP6State state) {
dhcp6_state_to_string(client->state), dhcp6_state_to_string(state));
client->state = state;
if (client->state_callback)
client->state_callback(client, state, client->state_userdata);
}
int dhcp6_client_get_state(sd_dhcp6_client *client) {
assert_return(client, -EINVAL);
return client->state;
}
static void client_notify(sd_dhcp6_client *client, int event) {
@ -809,9 +820,9 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
if (r < 0)
return r;
assert(client->duid_len > 0);
assert(sd_dhcp_duid_is_set(&client->duid));
r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_CLIENTID,
client->duid_len, &client->duid);
client->duid.size, &client->duid.duid);
if (r < 0)
return r;
@ -824,7 +835,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
/* RFC 8415 Section 21.9.
* A client MUST include an Elapsed Time option in messages to indicate how long the client has
* been trying to complete a DHCP message exchange. */
elapsed_usec = NM_MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
elapsed_usec = MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
elapsed_time = htobe16(elapsed_usec);
r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time);
if (r < 0)
@ -1047,12 +1058,20 @@ static int client_enter_bound_state(sd_dhcp6_client *client) {
(void) event_source_disable(client->receive_message);
(void) event_source_disable(client->timeout_resend);
r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2, &lifetime_valid);
r = sd_dhcp6_lease_get_t1(client->lease, &lifetime_t1);
if (r < 0)
goto error;
r = sd_dhcp6_lease_get_t2(client->lease, &lifetime_t2);
if (r < 0)
goto error;
r = sd_dhcp6_lease_get_valid_lifetime(client->lease, &lifetime_valid);
if (r < 0)
goto error;
lifetime_t2 = client_timeout_compute_random(lifetime_t2);
lifetime_t1 = client_timeout_compute_random(NM_MIN(lifetime_t1, lifetime_t2));
lifetime_t1 = client_timeout_compute_random(MIN(lifetime_t1, lifetime_t2));
if (lifetime_t1 == USEC_INFINITY) {
log_dhcp6_client(client, "Infinite T1");
@ -1286,16 +1305,15 @@ static int client_receive_message(
.msg_control = &control,
.msg_controllen = sizeof(control),
};
triple_timestamp t = {};
triple_timestamp t;
_cleanup_free_ DHCP6Message *message = NULL;
struct in6_addr *server_address = NULL;
ssize_t buflen, len;
buflen = next_datagram_size_fd(fd);
if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen))
return 0;
if (buflen < 0) {
if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
return 0;
log_dhcp6_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
return 0;
}
@ -1307,10 +1325,9 @@ static int client_receive_message(
iov = IOVEC_MAKE(message, buflen);
len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
if (ERRNO_IS_NEG_TRANSIENT(len) || ERRNO_IS_NEG_DISCONNECT(len))
return 0;
if (len < 0) {
if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len))
return 0;
log_dhcp6_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m");
return 0;
}
@ -1329,9 +1346,7 @@ static int client_receive_message(
server_address = &sa.in6.sin6_addr;
}
struct timeval *tv = CMSG_FIND_AND_COPY_DATA(&msg, SOL_SOCKET, SCM_TIMESTAMP, struct timeval);
if (tv)
triple_timestamp_from_realtime(&t, timeval_load(tv));
triple_timestamp_from_cmsg(&t, &msg);
if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
return 0;

View file

@ -10,7 +10,9 @@
#include "alloc-util.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#include "network-common.h"
#include "strv.h"
#include "unaligned.h"
#define IRT_DEFAULT (1 * USEC_PER_DAY)
#define IRT_MINIMUM (600 * USEC_PER_SEC)
@ -21,7 +23,7 @@ static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timest
if (timestamp && triple_timestamp_is_set(timestamp))
lease->timestamp = *timestamp;
else
triple_timestamp_get(&lease->timestamp);
triple_timestamp_now(&lease->timestamp);
}
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
@ -37,30 +39,26 @@ int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_
return 0;
}
static usec_t sec2usec(uint32_t sec) {
return sec == UINT32_MAX ? USEC_INFINITY : sec * USEC_PER_SEC;
}
static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX;
usec_t t1 = USEC_INFINITY, t2 = USEC_INFINITY, min_valid_lt = USEC_INFINITY;
assert(lease);
assert(lease->ia_na || lease->ia_pd);
if (lease->ia_na) {
t1 = MIN(t1, be32toh(lease->ia_na->header.lifetime_t1));
t2 = MIN(t2, be32toh(lease->ia_na->header.lifetime_t2));
t1 = MIN(t1, be32_sec_to_usec(lease->ia_na->header.lifetime_t1, /* max_as_infinity = */ true));
t2 = MIN(t2, be32_sec_to_usec(lease->ia_na->header.lifetime_t2, /* max_as_infinity = */ true));
LIST_FOREACH(addresses, a, lease->ia_na->addresses)
min_valid_lt = MIN(min_valid_lt, be32toh(a->iaaddr.lifetime_valid));
min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iaaddr.lifetime_valid, /* max_as_infinity = */ true));
}
if (lease->ia_pd) {
t1 = MIN(t1, be32toh(lease->ia_pd->header.lifetime_t1));
t2 = MIN(t2, be32toh(lease->ia_pd->header.lifetime_t2));
t1 = MIN(t1, be32_sec_to_usec(lease->ia_pd->header.lifetime_t1, /* max_as_infinity = */ true));
t2 = MIN(t2, be32_sec_to_usec(lease->ia_pd->header.lifetime_t2, /* max_as_infinity = */ true));
LIST_FOREACH(addresses, a, lease->ia_pd->addresses)
min_valid_lt = MIN(min_valid_lt, be32toh(a->iapdprefix.lifetime_valid));
min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iapdprefix.lifetime_valid, /* max_as_infinity = */ true));
}
if (t2 == 0 || t2 > min_valid_lt) {
@ -70,25 +68,52 @@ static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
t2 = min_valid_lt / 10 * 8;
}
lease->lifetime_valid = sec2usec(min_valid_lt);
lease->lifetime_t1 = sec2usec(t1);
lease->lifetime_t2 = sec2usec(t2);
lease->lifetime_valid = min_valid_lt;
lease->lifetime_t1 = t1;
lease->lifetime_t2 = t2;
}
int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid) {
assert(lease);
#define DEFINE_GET_TIME_FUNCTIONS(name, val) \
int sd_dhcp6_lease_get_##name( \
sd_dhcp6_lease *lease, \
uint64_t *ret) { \
\
assert_return(lease, -EINVAL); \
\
if (!lease->ia_na && !lease->ia_pd) \
return -ENODATA; \
\
if (ret) \
*ret = lease->val; \
return 0; \
} \
\
int sd_dhcp6_lease_get_##name##_timestamp( \
sd_dhcp6_lease *lease, \
clockid_t clock, \
uint64_t *ret) { \
\
usec_t s, t; \
int r; \
\
assert_return(lease, -EINVAL); \
\
r = sd_dhcp6_lease_get_##name(lease, &s); \
if (r < 0) \
return r; \
\
r = sd_dhcp6_lease_get_timestamp(lease, clock, &t); \
if (r < 0) \
return r; \
\
if (ret) \
*ret = time_span_to_stamp(s, t); \
return 0; \
}
if (!lease->ia_na && !lease->ia_pd)
return -ENODATA;
if (ret_t1)
*ret_t1 = lease->lifetime_t1;
if (ret_t2)
*ret_t2 = lease->lifetime_t2;
if (ret_valid)
*ret_valid = lease->lifetime_valid;
return 0;
}
DEFINE_GET_TIME_FUNCTIONS(t1, lifetime_t1);
DEFINE_GET_TIME_FUNCTIONS(t2, lifetime_t1);
DEFINE_GET_TIME_FUNCTIONS(valid_lifetime, lifetime_valid);
static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) {
assert(lease);
@ -218,61 +243,151 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) {
return 0;
}
int sd_dhcp6_lease_get_address(
int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
assert_return(lease, -EINVAL);
if (!lease->addr_iter)
return -ENODATA;
if (ret)
*ret = lease->addr_iter->iaaddr.address;
return 0;
}
int sd_dhcp6_lease_get_address_lifetime(
sd_dhcp6_lease *lease,
struct in6_addr *ret_addr,
uint32_t *ret_lifetime_preferred,
uint32_t *ret_lifetime_valid) {
usec_t *ret_lifetime_preferred,
usec_t *ret_lifetime_valid) {
const struct iaaddr *a;
assert_return(lease, -EINVAL);
if (!lease->addr_iter)
return -ENODATA;
if (ret_addr)
*ret_addr = lease->addr_iter->iaaddr.address;
if (ret_lifetime_preferred)
*ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred);
if (ret_lifetime_valid)
*ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid);
a = &lease->addr_iter->iaaddr;
lease->addr_iter = lease->addr_iter->addresses_next;
if (ret_lifetime_preferred)
*ret_lifetime_preferred = be32_sec_to_usec(a->lifetime_preferred, /* max_as_infinity = */ true);
if (ret_lifetime_valid)
*ret_lifetime_valid = be32_sec_to_usec(a->lifetime_valid, /* max_as_infinity = */ true);
return 0;
}
void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
if (lease)
lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
int sd_dhcp6_lease_address_iterator_reset(sd_dhcp6_lease *lease) {
if (!lease)
return false;
lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
return !!lease->addr_iter;
}
int sd_dhcp6_lease_get_pd(
int sd_dhcp6_lease_address_iterator_next(sd_dhcp6_lease *lease) {
if (!lease || !lease->addr_iter)
return false;
lease->addr_iter = lease->addr_iter->addresses_next;
return !!lease->addr_iter;
}
int sd_dhcp6_lease_has_address(sd_dhcp6_lease *lease) {
return lease && lease->ia_na;
}
int sd_dhcp6_lease_get_pd_prefix(
sd_dhcp6_lease *lease,
struct in6_addr *ret_prefix,
uint8_t *ret_prefix_len,
uint32_t *ret_lifetime_preferred,
uint32_t *ret_lifetime_valid) {
uint8_t *ret_prefix_len) {
const struct iapdprefix *a;
assert_return(lease, -EINVAL);
if (!lease->prefix_iter)
return -ENODATA;
if (ret_prefix)
*ret_prefix = lease->prefix_iter->iapdprefix.address;
if (ret_prefix_len)
*ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
if (ret_lifetime_preferred)
*ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
if (ret_lifetime_valid)
*ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
a = &lease->prefix_iter->iapdprefix;
lease->prefix_iter = lease->prefix_iter->addresses_next;
if (ret_prefix)
*ret_prefix = a->address;
if (ret_prefix_len)
*ret_prefix_len = a->prefixlen;
return 0;
}
void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
if (lease)
lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
int sd_dhcp6_lease_get_pd_lifetime(
sd_dhcp6_lease *lease,
uint64_t *ret_lifetime_preferred,
uint64_t *ret_lifetime_valid) {
const struct iapdprefix *a;
assert_return(lease, -EINVAL);
if (!lease->prefix_iter)
return -ENODATA;
a = &lease->prefix_iter->iapdprefix;
if (ret_lifetime_preferred)
*ret_lifetime_preferred = be32_sec_to_usec(a->lifetime_preferred, /* max_as_infinity = */ true);
if (ret_lifetime_valid)
*ret_lifetime_valid = be32_sec_to_usec(a->lifetime_valid, /* max_as_infinity = */ true);
return 0;
}
int sd_dhcp6_lease_pd_iterator_reset(sd_dhcp6_lease *lease) {
if (!lease)
return false;
lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
return !!lease->prefix_iter;
}
int sd_dhcp6_lease_pd_iterator_next(sd_dhcp6_lease *lease) {
if (!lease || !lease->prefix_iter)
return false;
lease->prefix_iter = lease->prefix_iter->addresses_next;
return !!lease->prefix_iter;
}
#define DEFINE_GET_TIMESTAMP2(name) \
int sd_dhcp6_lease_get_##name##_lifetime_timestamp( \
sd_dhcp6_lease *lease, \
clockid_t clock, \
uint64_t *ret_lifetime_preferred, \
uint64_t *ret_lifetime_valid) { \
\
usec_t t, p, v; \
int r; \
\
assert_return(lease, -EINVAL); \
\
r = sd_dhcp6_lease_get_##name##_lifetime( \
lease, \
ret_lifetime_preferred ? &p : NULL, \
ret_lifetime_valid ? &v : NULL); \
if (r < 0) \
return r; \
\
r = sd_dhcp6_lease_get_timestamp(lease, clock, &t); \
if (r < 0) \
return r; \
\
if (ret_lifetime_preferred) \
*ret_lifetime_preferred = time_span_to_stamp(p, t); \
if (ret_lifetime_valid) \
*ret_lifetime_valid = time_span_to_stamp(v, t); \
return 0; \
}
DEFINE_GET_TIMESTAMP2(address);
DEFINE_GET_TIMESTAMP2(pd);
int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) {
return lease && lease->ia_pd;
}
int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
@ -447,6 +562,111 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
return 0;
}
int dhcp6_lease_set_captive_portal(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
_cleanup_free_ char *uri = NULL;
int r;
assert(lease);
assert(optval || optlen == 0);
r = dhcp6_option_parse_string(optval, optlen, &uri);
if (r < 0)
return r;
if (uri && !in_charset(uri, URI_VALID))
return -EINVAL;
return free_and_replace(lease->captive_portal, uri);
}
int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret) {
assert_return(lease, -EINVAL);
assert_return(ret, -EINVAL);
if (!lease->captive_portal)
return -ENODATA;
*ret = lease->captive_portal;
return 0;
}
int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease *lease, sd_dhcp6_option ***ret) {
int r;
assert_return(lease, -EINVAL);
if (set_isempty(lease->vendor_options))
return -ENODATA;
if (ret) {
if (!lease->sorted_vendor_options) {
r = set_dump_sorted(lease->vendor_options, (void***) &lease->sorted_vendor_options, NULL);
if (r < 0)
return r;
}
*ret = lease->sorted_vendor_options;
}
return set_size(lease->vendor_options);
}
static int dhcp6_lease_insert_vendor_option(
sd_dhcp6_lease *lease,
uint16_t option_code,
const void *data,
size_t len,
uint32_t enterprise_id) {
_cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *option = NULL;
assert(lease);
option = new(sd_dhcp6_option, 1);
if (!option)
return -ENOMEM;
*option = (sd_dhcp6_option) {
.n_ref = 1,
.enterprise_identifier = enterprise_id,
.option = option_code,
.length = len,
};
option->data = memdup_suffix0(data, len);
if (!option->data)
return -ENOMEM;
return set_ensure_consume(&lease->vendor_options, &dhcp6_option_hash_ops, TAKE_PTR(option));
}
static int dhcp6_lease_add_vendor_option(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
int r;
uint32_t enterprise_id;
assert(lease);
assert(optval || optlen == 0);
if (optlen < sizeof(be32_t))
return -EBADMSG;
enterprise_id = unaligned_read_be32(optval);
for (size_t offset = 4; offset < optlen;) {
const uint8_t *subval;
size_t sublen;
uint16_t subopt;
r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
if (r < 0)
return r;
r = dhcp6_lease_insert_vendor_option(lease, subopt, subval, sublen, enterprise_id);
if (r < 0)
return r;
}
return 0;
}
static int dhcp6_lease_parse_message(
sd_dhcp6_client *client,
sd_dhcp6_lease *lease,
@ -467,6 +687,11 @@ static int dhcp6_lease_parse_message(
size_t optlen;
const uint8_t *optval;
if (len - offset < offsetof(DHCP6Option, data)) {
log_dhcp6_client(client, "Ignoring %zu invalid byte(s) at the end of the packet", len - offset);
break;
}
r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
if (r < 0)
return log_dhcp6_client_errno(client, r,
@ -607,6 +832,12 @@ static int dhcp6_lease_parse_message(
break;
case SD_DHCP6_OPTION_CAPTIVE_PORTAL:
r = dhcp6_lease_set_captive_portal(lease, optval, optlen);
if (r < 0)
log_dhcp6_client_errno(client, r, "Failed to parse captive portal option, ignoring: %m");
break;
case SD_DHCP6_OPTION_CLIENT_FQDN:
r = dhcp6_lease_set_fqdn(lease, optval, optlen);
if (r < 0)
@ -619,7 +850,14 @@ static int dhcp6_lease_parse_message(
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received information refresh time option with an invalid length (%zu).", optlen);
irt = unaligned_read_be32(optval) * USEC_PER_SEC;
irt = unaligned_be32_sec_to_usec(optval, /* max_as_infinity = */ false);
break;
case SD_DHCP6_OPTION_VENDOR_OPTS:
r = dhcp6_lease_add_vendor_option(lease, optval, optlen);
if (r < 0)
log_dhcp6_client_errno(client, r, "Failed to parse vendor option, ignoring: %m");
break;
}
}
@ -631,7 +869,7 @@ static int dhcp6_lease_parse_message(
"%s message does not contain client ID. Ignoring.",
dhcp6_message_type_to_string(message->type));
if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0)
if (memcmp_nn(clientid, clientid_len, &client->duid.duid, client->duid.size) != 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"The client ID in %s message does not match. Ignoring.",
dhcp6_message_type_to_string(message->type));
@ -661,12 +899,15 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
if (!lease)
return NULL;
set_free(lease->vendor_options);
free(lease->sorted_vendor_options);
free(lease->clientid);
free(lease->serverid);
dhcp6_ia_free(lease->ia_na);
dhcp6_ia_free(lease->ia_pd);
free(lease->dns);
free(lease->fqdn);
free(lease->captive_portal);
strv_free(lease->domains);
free(lease->ntp);
strv_free(lease->ntp_fqdn);

View file

@ -7,39 +7,41 @@
#include "sd-device.h"
#include "alloc-util.h"
#include "log.h"
#include "macro.h"
#include "strv.h"
#define device_unref_and_replace(a, b) \
unref_and_replace_full(a, b, sd_device_ref, sd_device_unref)
#define FOREACH_DEVICE_PROPERTY(device, key, value) \
for (key = sd_device_get_property_first(device, &(value)); \
key; \
key = sd_device_get_property_next(device, &(value)))
#define FOREACH_DEVICE_PROPERTY(device, key, value) \
for (const char *value, *key = sd_device_get_property_first(device, &value); \
key; \
key = sd_device_get_property_next(device, &value))
#define FOREACH_DEVICE_TAG(device, tag) \
for (tag = sd_device_get_tag_first(device); \
tag; \
#define FOREACH_DEVICE_TAG(device, tag) \
for (const char *tag = sd_device_get_tag_first(device); \
tag; \
tag = sd_device_get_tag_next(device))
#define FOREACH_DEVICE_CURRENT_TAG(device, tag) \
for (tag = sd_device_get_current_tag_first(device); \
tag; \
#define FOREACH_DEVICE_CURRENT_TAG(device, tag) \
for (const char *tag = sd_device_get_current_tag_first(device); \
tag; \
tag = sd_device_get_current_tag_next(device))
#define FOREACH_DEVICE_SYSATTR(device, attr) \
for (attr = sd_device_get_sysattr_first(device); \
attr; \
#define FOREACH_DEVICE_SYSATTR(device, attr) \
for (const char *attr = sd_device_get_sysattr_first(device); \
attr; \
attr = sd_device_get_sysattr_next(device))
#define FOREACH_DEVICE_DEVLINK(device, devlink) \
for (devlink = sd_device_get_devlink_first(device); \
devlink; \
#define FOREACH_DEVICE_DEVLINK(device, devlink) \
for (const char *devlink = sd_device_get_devlink_first(device); \
devlink; \
devlink = sd_device_get_devlink_next(device))
#define _FOREACH_DEVICE_CHILD(device, child, suffix_ptr) \
for (child = sd_device_get_child_first(device, suffix_ptr); \
for (sd_device *child = sd_device_get_child_first(device, suffix_ptr); \
child; \
child = sd_device_get_child_next(device, suffix_ptr))
@ -49,14 +51,14 @@
#define FOREACH_DEVICE_CHILD_WITH_SUFFIX(device, child, suffix) \
_FOREACH_DEVICE_CHILD(device, child, &suffix)
#define FOREACH_DEVICE(enumerator, device) \
for (device = sd_device_enumerator_get_device_first(enumerator); \
device; \
#define FOREACH_DEVICE(enumerator, device) \
for (sd_device *device = sd_device_enumerator_get_device_first(enumerator); \
device; \
device = sd_device_enumerator_get_device_next(enumerator))
#define FOREACH_SUBSYSTEM(enumerator, device) \
for (device = sd_device_enumerator_get_subsystem_first(enumerator); \
device; \
#define FOREACH_SUBSYSTEM(enumerator, device) \
for (sd_device *device = sd_device_enumerator_get_subsystem_first(enumerator); \
device; \
device = sd_device_enumerator_get_subsystem_next(enumerator))
#define log_device_full_errno_zerook(device, level, error, ...) \
@ -81,17 +83,17 @@
#define log_device_full(device, level, ...) (void) log_device_full_errno_zerook(device, level, 0, __VA_ARGS__)
#define log_device_debug(device, ...) log_device_full(device, LOG_DEBUG, __VA_ARGS__)
#define log_device_info(device, ...) log_device_full(device, LOG_INFO, __VA_ARGS__)
#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, __VA_ARGS__)
#define log_device_debug(device, ...) log_device_full(device, LOG_DEBUG, __VA_ARGS__)
#define log_device_info(device, ...) log_device_full(device, LOG_INFO, __VA_ARGS__)
#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, __VA_ARGS__)
#define log_device_warning(device, ...) log_device_full(device, LOG_WARNING, __VA_ARGS__)
#define log_device_error(device, ...) log_device_full(device, LOG_ERR, __VA_ARGS__)
#define log_device_error(device, ...) log_device_full(device, LOG_ERR, __VA_ARGS__)
#define log_device_debug_errno(device, error, ...) log_device_full_errno(device, LOG_DEBUG, error, __VA_ARGS__)
#define log_device_info_errno(device, error, ...) log_device_full_errno(device, LOG_INFO, error, __VA_ARGS__)
#define log_device_notice_errno(device, error, ...) log_device_full_errno(device, LOG_NOTICE, error, __VA_ARGS__)
#define log_device_debug_errno(device, error, ...) log_device_full_errno(device, LOG_DEBUG, error, __VA_ARGS__)
#define log_device_info_errno(device, error, ...) log_device_full_errno(device, LOG_INFO, error, __VA_ARGS__)
#define log_device_notice_errno(device, error, ...) log_device_full_errno(device, LOG_NOTICE, error, __VA_ARGS__)
#define log_device_warning_errno(device, error, ...) log_device_full_errno(device, LOG_WARNING, error, __VA_ARGS__)
#define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__)
#define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__)
int devname_from_devnum(mode_t mode, dev_t devnum, char **ret);
static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
@ -101,3 +103,13 @@ static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret);
char** device_make_log_fields(sd_device *device);
bool device_in_subsystem(sd_device *device, const char *subsystem);
bool device_is_devtype(sd_device *device, const char *devtype);
static inline bool device_property_can_set(const char *property) {
return property &&
!STR_IN_SET(property,
"ACTION", "DEVLINKS", "DEVNAME", "DEVPATH", "DEVTYPE", "DRIVER",
"IFINDEX", "MAJOR", "MINOR", "SEQNUM", "SUBSYSTEM", "TAGS");
}

View file

@ -99,16 +99,21 @@ int event_reset_time_relative(
const char *description,
bool force_reset) {
usec_t usec_now;
int r;
assert(e);
r = sd_event_now(e, clock, &usec_now);
if (r < 0)
return log_debug_errno(r, "sd-event: Failed to get the current time: %m");
if (usec > 0) {
usec_t usec_now;
return event_reset_time(e, s, clock, usec_add(usec_now, usec), accuracy, callback, userdata, priority, description, force_reset);
r = sd_event_now(e, clock, &usec_now);
if (r < 0)
return log_debug_errno(r, "sd-event: Failed to get the current time: %m");
usec = usec_add(usec_now, usec);
}
return event_reset_time(e, s, clock, usec, accuracy, callback, userdata, priority, description, force_reset);
}
#if 0 /* NM_IGNORED */
@ -149,4 +154,21 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
return 0;
}
int event_add_child_pidref(
sd_event *e,
sd_event_source **s,
const PidRef *pid,
int options,
sd_event_child_handler_t callback,
void *userdata) {
if (!pidref_is_set(pid))
return -ESRCH;
if (pid->fd >= 0)
return sd_event_add_child_pidfd(e, s, pid->fd, options, callback, userdata);
return sd_event_add_child(e, s, pid->pid, options, callback, userdata);
}
#endif /* NM_IGNORED */

View file

@ -5,6 +5,8 @@
#include "sd-event.h"
#include "pidref.h"
int event_reset_time(
sd_event *e,
sd_event_source **s,
@ -32,3 +34,7 @@ static inline int event_source_disable(sd_event_source *s) {
}
int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata);
#if 0 /* NM_IGNORED */
int event_add_child_pidref(sd_event *e, sd_event_source **s, const PidRef *pid, int options, sd_event_child_handler_t callback, void *userdata);
#endif /* NM_IGNORED */

View file

@ -1169,10 +1169,10 @@ static int source_set_pending(sd_event_source *s, bool b) {
assert(s->inotify.inode_data->inotify_data);
if (b)
s->inotify.inode_data->inotify_data->n_pending ++;
s->inotify.inode_data->inotify_data->n_pending++;
else {
assert(s->inotify.inode_data->inotify_data->n_pending > 0);
s->inotify.inode_data->inotify_data->n_pending --;
s->inotify.inode_data->inotify_data->n_pending--;
}
}
@ -1983,7 +1983,7 @@ _public_ int sd_event_add_memory_pressure(
env = secure_getenv("MEMORY_PRESSURE_WRITE");
if (env) {
r = unbase64mem(env, SIZE_MAX, &write_buffer, &write_buffer_size);
r = unbase64mem(env, &write_buffer, &write_buffer_size);
if (r < 0)
return r;
}
@ -2239,8 +2239,8 @@ static int inode_data_compare(const struct inode_data *x, const struct inode_dat
static void inode_data_hash_func(const struct inode_data *d, struct siphash *state) {
assert(d);
siphash24_compress(&d->dev, sizeof(d->dev), state);
siphash24_compress(&d->ino, sizeof(d->ino), state);
siphash24_compress_typesafe(d->dev, state);
siphash24_compress_typesafe(d->ino, state);
}
DEFINE_PRIVATE_HASH_OPS(inode_data_hash_ops, struct inode_data, inode_data_hash_func, inode_data_compare);
@ -4008,7 +4008,7 @@ static int process_inotify(sd_event *e) {
if (r < 0)
return r;
if (r > 0)
done ++;
done++;
}
return done;
@ -4620,7 +4620,7 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t
/* Set timestamp only when this is called first time. */
if (threshold == INT64_MAX)
triple_timestamp_get(&e->timestamp);
triple_timestamp_now(&e->timestamp);
for (size_t i = 0; i < m; i++) {
@ -5047,7 +5047,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) {
}
}
e->watchdog = !!b;
e->watchdog = b;
return e->watchdog;
fail:

View file

@ -11,9 +11,29 @@
#include "hexdecoct.h"
#include "id128-util.h"
#include "io-util.h"
#include "sha256.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "sync-util.h"
#include "virt.h"
int id128_from_string_nonzero(const char *s, sd_id128_t *ret) {
sd_id128_t t;
int r;
assert(ret);
r = sd_id128_from_string(ASSERT_PTR(s), &t);
if (r < 0)
return r;
if (sd_id128_is_null(t))
return -ENXIO;
*ret = t;
return 0;
}
#if 0 /* NM_IGNORED */
bool id128_is_valid(const char *s) {
@ -24,7 +44,7 @@ bool id128_is_valid(const char *s) {
l = strlen(s);
if (l == SD_ID128_STRING_MAX - 1)
/* Plain formatted 128bit hex string */
/* Plain formatted 128-bit hex string */
return in_charset(s, HEXDIGITS);
if (l == SD_ID128_UUID_STRING_MAX - 1) {
@ -53,7 +73,7 @@ int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret) {
assert(fd >= 0);
/* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
/* Reads an 128-bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
* optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they
* aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you
* accept".
@ -151,7 +171,7 @@ int id128_write_fd(int fd, Id128Flag f, sd_id128_t id) {
}
buffer[sz - 1] = '\n';
r = loop_write(fd, buffer, sz, false);
r = loop_write(fd, buffer, sz);
if (r < 0)
return r;
@ -178,11 +198,11 @@ int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) {
}
void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
siphash24_compress(p, sizeof(sd_id128_t), state);
siphash24_compress_typesafe(*p, state);
}
int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
return memcmp(a, b, 16);
return memcmp(a, b, sizeof(sd_id128_t));
}
sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
@ -210,9 +230,22 @@ int id128_get_product(sd_id128_t *ret) {
/* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is
* particularly relevant in VM environments, where VM managers typically place a VM uuid there. */
r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid);
if (r == -ENOENT)
r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid);
r = detect_container();
if (r < 0)
return r;
if (r > 0) /* Refuse returning this in containers, as this is not a property of our system then, but
* of the host */
return -ENOENT;
FOREACH_STRING(i,
"/sys/class/dmi/id/product_uuid", /* KVM */
"/proc/device-tree/vm,uuid", /* Device tree */
"/sys/hypervisor/uuid") { /* Xen */
r = id128_read(i, ID128_FORMAT_UUID, &uuid);
if (r != -ENOENT)
break;
}
if (r < 0)
return r;
@ -222,4 +255,22 @@ int id128_get_product(sd_id128_t *ret) {
*ret = uuid;
return 0;
}
sd_id128_t id128_digest(const void *data, size_t size) {
assert(data || size == 0);
/* Hashes a UUID from some arbitrary data */
if (size == SIZE_MAX)
size = strlen(data);
uint8_t h[SHA256_DIGEST_SIZE];
sd_id128_t id;
/* Take the first half of the SHA256 result */
assert_cc(sizeof(h) >= sizeof(id.bytes));
memcpy(id.bytes, sha256_direct(data, size, h), sizeof(id.bytes));
return id128_make_v4_uuid(id);
}
#endif /* NM_IGNORED */

View file

@ -6,6 +6,7 @@
#include "sd-id128.h"
#include "errno-util.h"
#include "hash-funcs.h"
#include "macro.h"
@ -20,6 +21,8 @@ typedef enum Id128Flag {
ID128_REFUSE_NULL = 1 << 3, /* Refuse all zero ID with -ENOMEDIUM. */
} Id128Flag;
int id128_from_string_nonzero(const char *s, sd_id128_t *ret);
int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret);
int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret);
static inline int id128_read(const char *path, Id128Flag f, sd_id128_t *ret) {
@ -44,9 +47,12 @@ sd_id128_t id128_make_v4_uuid(sd_id128_t id);
int id128_get_product(sd_id128_t *ret);
sd_id128_t id128_digest(const void *data, size_t size);
/* A helper to check for the three relevant cases of "machine ID not initialized" */
#define ERRNO_IS_MACHINE_ID_UNSET(r) \
IN_SET(abs(r), \
ENOENT, \
ENOMEDIUM, \
ENOPKG)
#define ERRNO_IS_NEG_MACHINE_ID_UNSET(r) \
IN_SET(r, \
-ENOENT, \
-ENOMEDIUM, \
-ENOPKG)
_DEFINE_ABS_WRAPPER(MACHINE_ID_UNSET);

View file

@ -345,18 +345,20 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
return 0;
}
static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
uint8_t hmac[SHA256_DIGEST_SIZE];
sd_id128_t result;
_public_ int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
assert_cc(sizeof(sd_id128_t) < SHA256_DIGEST_SIZE); /* Check that we don't need to pad with zeros. */
union {
uint8_t hmac[SHA256_DIGEST_SIZE];
sd_id128_t result;
} buf;
assert(ret);
assert_return(ret, -EINVAL);
assert_return(!sd_id128_is_null(app_id), -ENXIO);
hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), hmac);
hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), buf.hmac);
/* Take only the first half. */
memcpy(&result, hmac, MIN(sizeof(hmac), sizeof(result)));
*ret = id128_make_v4_uuid(result);
*ret = id128_make_v4_uuid(buf.result);
return 0;
}
@ -370,7 +372,7 @@ _public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *re
if (r < 0)
return r;
return get_app_specific(id, app_id, ret);
return sd_id128_get_app_specific(id, app_id, ret);
}
_public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
@ -383,6 +385,6 @@ _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret)
if (r < 0)
return r;
return get_app_specific(id, app_id, ret);
return sd_id128_get_app_specific(id, app_id, ret);
}
#endif /* NM_IGNORED */

View file

@ -99,7 +99,7 @@ typedef void (*_sd_destroy_t)(void *userdata);
} \
struct _sd_useless_struct_to_allow_trailing_semicolon_
/* The following macro should be used in all public enums, to force 64bit wideness on them, so that we can
/* The following macro should be used in all public enums, to force 64-bit wideness on them, so that we can
* freely extend them later on, without breaking compatibility. */
#define _SD_ENUM_FORCE_S64(id) \
_SD_##id##_INT64_MIN = INT64_MIN, \

View file

@ -129,6 +129,7 @@ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumera
int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match);
int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match);
int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value);
int sd_device_enumerator_add_match_property_required(sd_device_enumerator *enumerator, const char *property, const char *value);
int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname);
int sd_device_enumerator_add_nomatch_sysname(sd_device_enumerator *enumerator, const char *sysname);
int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag);

View file

@ -0,0 +1,71 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef foosddhcpduidhfoo
#define foosddhcpduidhfoo
/***
Copyright © 2013 Intel Corporation. All rights reserved.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <https://www.gnu.org/licenses/>.
***/
#include <inttypes.h>
#include <sys/types.h>
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
enum {
SD_DUID_TYPE_LLT = 1,
SD_DUID_TYPE_EN = 2,
SD_DUID_TYPE_LL = 3,
SD_DUID_TYPE_UUID = 4
};
typedef struct sd_dhcp_duid sd_dhcp_duid;
int sd_dhcp_duid_clear(sd_dhcp_duid *duid);
int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid);
int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size);
int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size);
int sd_dhcp_duid_set(
sd_dhcp_duid *duid,
uint16_t duid_type,
const void *data,
size_t data_size);
int sd_dhcp_duid_set_raw(
sd_dhcp_duid *duid,
const void *data,
size_t data_size);
int sd_dhcp_duid_set_llt(
sd_dhcp_duid *duid,
const void *hw_addr,
size_t hw_addr_size,
uint16_t arp_type,
uint64_t usec);
int sd_dhcp_duid_set_ll(
sd_dhcp_duid *duid,
const void *hw_addr,
size_t hw_addr_size,
uint16_t arp_type);
int sd_dhcp_duid_set_en(sd_dhcp_duid *duid);
int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid);
int sd_dhcp_duid_to_string(const sd_dhcp_duid *duid, char **ret);
_SD_END_DECLARATIONS;
#endif

View file

@ -24,6 +24,7 @@
#include <sys/types.h>
#include "sd-device.h"
#include "sd-dhcp-duid.h"
#include "sd-dhcp6-lease.h"
#include "sd-dhcp6-option.h"
#include "sd-event.h"
@ -40,154 +41,6 @@ enum {
SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST = 13
};
/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
enum {
SD_DHCP6_OPTION_CLIENTID = 1, /* RFC 8415 */
SD_DHCP6_OPTION_SERVERID = 2, /* RFC 8415 */
SD_DHCP6_OPTION_IA_NA = 3, /* RFC 8415 */
SD_DHCP6_OPTION_IA_TA = 4, /* RFC 8415 */
SD_DHCP6_OPTION_IAADDR = 5, /* RFC 8415 */
SD_DHCP6_OPTION_ORO = 6, /* RFC 8415 */
SD_DHCP6_OPTION_PREFERENCE = 7, /* RFC 8415 */
SD_DHCP6_OPTION_ELAPSED_TIME = 8, /* RFC 8415 */
SD_DHCP6_OPTION_RELAY_MSG = 9, /* RFC 8415 */
/* option code 10 is unassigned */
SD_DHCP6_OPTION_AUTH = 11, /* RFC 8415 */
SD_DHCP6_OPTION_UNICAST = 12, /* RFC 8415 */
SD_DHCP6_OPTION_STATUS_CODE = 13, /* RFC 8415 */
SD_DHCP6_OPTION_RAPID_COMMIT = 14, /* RFC 8415 */
SD_DHCP6_OPTION_USER_CLASS = 15, /* RFC 8415 */
SD_DHCP6_OPTION_VENDOR_CLASS = 16, /* RFC 8415 */
SD_DHCP6_OPTION_VENDOR_OPTS = 17, /* RFC 8415 */
SD_DHCP6_OPTION_INTERFACE_ID = 18, /* RFC 8415 */
SD_DHCP6_OPTION_RECONF_MSG = 19, /* RFC 8415 */
SD_DHCP6_OPTION_RECONF_ACCEPT = 20, /* RFC 8415 */
SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME = 21, /* RFC 3319 */
SD_DHCP6_OPTION_SIP_SERVER_ADDRESS = 22, /* RFC 3319 */
SD_DHCP6_OPTION_DNS_SERVER = 23, /* RFC 3646 */
SD_DHCP6_OPTION_DOMAIN = 24, /* RFC 3646 */
SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, RFC 8415 */
SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, RFC 8415 */
SD_DHCP6_OPTION_NIS_SERVER = 27, /* RFC 3898 */
SD_DHCP6_OPTION_NISP_SERVER = 28, /* RFC 3898 */
SD_DHCP6_OPTION_NIS_DOMAIN_NAME = 29, /* RFC 3898 */
SD_DHCP6_OPTION_NISP_DOMAIN_NAME = 30, /* RFC 3898 */
SD_DHCP6_OPTION_SNTP_SERVER = 31, /* RFC 4075, deprecated */
SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME = 32, /* RFC 4242, 8415, sec. 21.23 */
SD_DHCP6_OPTION_BCMCS_SERVER_D = 33, /* RFC 4280 */
SD_DHCP6_OPTION_BCMCS_SERVER_A = 34, /* RFC 4280 */
/* option code 35 is unassigned */
SD_DHCP6_OPTION_GEOCONF_CIVIC = 36, /* RFC 4776 */
SD_DHCP6_OPTION_REMOTE_ID = 37, /* RFC 4649 */
SD_DHCP6_OPTION_SUBSCRIBER_ID = 38, /* RFC 4580 */
SD_DHCP6_OPTION_CLIENT_FQDN = 39, /* RFC 4704 */
SD_DHCP6_OPTION_PANA_AGENT = 40, /* RFC 5192 */
SD_DHCP6_OPTION_POSIX_TIMEZONE = 41, /* RFC 4833 */
SD_DHCP6_OPTION_TZDB_TIMEZONE = 42, /* RFC 4833 */
SD_DHCP6_OPTION_ERO = 43, /* RFC 4994 */
SD_DHCP6_OPTION_LQ_QUERY = 44, /* RFC 5007 */
SD_DHCP6_OPTION_CLIENT_DATA = 45, /* RFC 5007 */
SD_DHCP6_OPTION_CLT_TIME = 46, /* RFC 5007 */
SD_DHCP6_OPTION_LQ_RELAY_DATA = 47, /* RFC 5007 */
SD_DHCP6_OPTION_LQ_CLIENT_LINK = 48, /* RFC 5007 */
SD_DHCP6_OPTION_MIP6_HNIDF = 49, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_VDINF = 50, /* RFC 6610 */
SD_DHCP6_OPTION_V6_LOST = 51, /* RFC 5223 */
SD_DHCP6_OPTION_CAPWAP_AC_V6 = 52, /* RFC 5417 */
SD_DHCP6_OPTION_RELAY_ID = 53, /* RFC 5460 */
SD_DHCP6_OPTION_IPV6_ADDRESS_MOS = 54, /* RFC 5678 */
SD_DHCP6_OPTION_IPV6_FQDN_MOS = 55, /* RFC 5678 */
SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */
SD_DHCP6_OPTION_V6_ACCESS_DOMAIN = 57, /* RFC 5986 */
SD_DHCP6_OPTION_SIP_UA_CS_LIST = 58, /* RFC 6011 */
SD_DHCP6_OPTION_BOOTFILE_URL = 59, /* RFC 5970 */
SD_DHCP6_OPTION_BOOTFILE_PARAM = 60, /* RFC 5970 */
SD_DHCP6_OPTION_CLIENT_ARCH_TYPE = 61, /* RFC 5970 */
SD_DHCP6_OPTION_NII = 62, /* RFC 5970 */
SD_DHCP6_OPTION_GEOLOCATION = 63, /* RFC 6225 */
SD_DHCP6_OPTION_AFTR_NAME = 64, /* RFC 6334 */
SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME = 65, /* RFC 6440 */
SD_DHCP6_OPTION_RSOO = 66, /* RFC 6422 */
SD_DHCP6_OPTION_PD_EXCLUDE = 67, /* RFC 6603 */
SD_DHCP6_OPTION_VSS = 68, /* RFC 6607 */
SD_DHCP6_OPTION_MIP6_IDINF = 69, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_UDINF = 70, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_HNP = 71, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_HAA = 72, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_HAF = 73, /* RFC 6610 */
SD_DHCP6_OPTION_RDNSS_SELECTION = 74, /* RFC 6731 */
SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME = 75, /* RFC 6784 */
SD_DHCP6_OPTION_KRB_REALM_NAME = 76, /* RFC 6784 */
SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME = 77, /* RFC 6784 */
SD_DHCP6_OPTION_KRB_KDC = 78, /* RFC 6784 */
SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR = 79, /* RFC 6939 */
SD_DHCP6_OPTION_LINK_ADDRESS = 80, /* RFC 6977 */
SD_DHCP6_OPTION_RADIUS = 81, /* RFC 7037 */
SD_DHCP6_OPTION_SOL_MAX_RT = 82, /* RFC 7083, RFC 8415 */
SD_DHCP6_OPTION_INF_MAX_RT = 83, /* RFC 7083, RFC 8415 */
SD_DHCP6_OPTION_ADDRSEL = 84, /* RFC 7078 */
SD_DHCP6_OPTION_ADDRSEL_TABLE = 85, /* RFC 7078 */
SD_DHCP6_OPTION_V6_PCP_SERVER = 86, /* RFC 7291 */
SD_DHCP6_OPTION_DHCPV4_MSG = 87, /* RFC 7341 */
SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER = 88, /* RFC 7341 */
SD_DHCP6_OPTION_S46_RULE = 89, /* RFC 7598 */
SD_DHCP6_OPTION_S46_BR = 90, /* RFC 7598, RFC 8539 */
SD_DHCP6_OPTION_S46_DMR = 91, /* RFC 7598 */
SD_DHCP6_OPTION_S46_V4V6BIND = 92, /* RFC 7598 */
SD_DHCP6_OPTION_S46_PORTPARAMS = 93, /* RFC 7598 */
SD_DHCP6_OPTION_S46_CONT_MAPE = 94, /* RFC 7598 */
SD_DHCP6_OPTION_S46_CONT_MAPT = 95, /* RFC 7598 */
SD_DHCP6_OPTION_S46_CONT_LW = 96, /* RFC 7598 */
SD_DHCP6_OPTION_4RD = 97, /* RFC 7600 */
SD_DHCP6_OPTION_4RD_MAP_RULE = 98, /* RFC 7600 */
SD_DHCP6_OPTION_4RD_NON_MAP_RULE = 99, /* RFC 7600 */
SD_DHCP6_OPTION_LQ_BASE_TIME = 100, /* RFC 7653 */
SD_DHCP6_OPTION_LQ_START_TIME = 101, /* RFC 7653 */
SD_DHCP6_OPTION_LQ_END_TIME = 102, /* RFC 7653 */
SD_DHCP6_OPTION_CAPTIVE_PORTAL = 103, /* RFC 8910 */
SD_DHCP6_OPTION_MPL_PARAMETERS = 104, /* RFC 7774 */
SD_DHCP6_OPTION_ANI_ATT = 105, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_NETWORK_NAME = 106, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_AP_NAME = 107, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_AP_BSSID = 108, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_OPERATOR_ID = 109, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_OPERATOR_REALM = 110, /* RFC 7839 */
SD_DHCP6_OPTION_S46_PRIORITY = 111, /* RFC 8026 */
SD_DHCP6_OPTION_MUD_URL_V6 = 112, /* RFC 8520 */
SD_DHCP6_OPTION_V6_PREFIX64 = 113, /* RFC 8115 */
SD_DHCP6_OPTION_F_BINDING_STATUS = 114, /* RFC 8156 */
SD_DHCP6_OPTION_F_CONNECT_FLAGS = 115, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO = 116, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_HOST_NAME = 117, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_ZONE_NAME = 118, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_FLAGS = 119, /* RFC 8156 */
SD_DHCP6_OPTION_F_EXPIRATION_TIME = 120, /* RFC 8156 */
SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD = 121, /* RFC 8156 */
SD_DHCP6_OPTION_F_MCLT = 122, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_LIFETIME = 123, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT = 124, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME = 125, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME = 126, /* RFC 8156 */
SD_DHCP6_OPTION_F_PROTOCOL_VERSION = 127, /* RFC 8156 */
SD_DHCP6_OPTION_F_KEEPALIVE_TIME = 128, /* RFC 8156 */
SD_DHCP6_OPTION_F_RECONFIGURE_DATA = 129, /* RFC 8156 */
SD_DHCP6_OPTION_F_RELATIONSHIP_NAME = 130, /* RFC 8156 */
SD_DHCP6_OPTION_F_SERVER_FLAGS = 131, /* RFC 8156 */
SD_DHCP6_OPTION_F_SERVER_STATE = 132, /* RFC 8156 */
SD_DHCP6_OPTION_F_START_TIME_OF_STATE = 133, /* RFC 8156 */
SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME = 134, /* RFC 8156 */
SD_DHCP6_OPTION_RELAY_PORT = 135, /* RFC 8357 */
SD_DHCP6_OPTION_V6_SZTP_REDIRECT = 136, /* RFC 8572 */
SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX = 137, /* RFC 8539 */
SD_DHCP6_OPTION_IA_LL = 138, /* RFC 8947 */
SD_DHCP6_OPTION_LLADDR = 139, /* RFC 8947 */
SD_DHCP6_OPTION_SLAP_QUAD = 140, /* RFC 8948 */
SD_DHCP6_OPTION_V6_DOTS_RI = 141, /* RFC 8973 */
SD_DHCP6_OPTION_V6_DOTS_ADDRESS = 142, /* RFC 8973 */
SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143 /* RFC 6153 */
/* option codes 144-65535 are unassigned */
};
typedef struct sd_dhcp6_client sd_dhcp6_client;
typedef void (*sd_dhcp6_client_callback_t)(sd_dhcp6_client *client, int event, void *userdata);
@ -211,23 +64,20 @@ int sd_dhcp6_client_set_mac(
const uint8_t *addr,
size_t addr_len,
uint16_t arp_type);
int sd_dhcp6_client_set_duid(
sd_dhcp6_client *client,
uint16_t duid_type,
const void *duid,
size_t duid_len);
int sd_dhcp6_client_set_duid_llt(
sd_dhcp6_client *client,
uint64_t llt_time);
int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time);
int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client);
int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client);
int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client);
int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len);
int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, const sd_dhcp_duid *duid);
int sd_dhcp6_client_get_duid(sd_dhcp6_client *client, const sd_dhcp_duid **ret);
int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret);
int sd_dhcp6_client_set_iaid(
sd_dhcp6_client *client,
uint32_t iaid);
int sd_dhcp6_client_get_iaid(
sd_dhcp6_client *client,
uint32_t *iaid);
int sd_dhcp6_client_duid_as_string(
sd_dhcp6_client *client,
char **duid);
int sd_dhcp6_client_set_fqdn(
sd_dhcp6_client *client,
const char *fqdn);

View file

@ -23,6 +23,8 @@
#include <netinet/in.h>
#include <sys/types.h>
#include "sd-dhcp6-option.h"
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
@ -30,24 +32,54 @@ _SD_BEGIN_DECLARATIONS;
typedef struct sd_dhcp6_lease sd_dhcp6_lease;
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
int sd_dhcp6_lease_get_t1(sd_dhcp6_lease *lease, uint64_t *ret);
int sd_dhcp6_lease_get_t1_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
int sd_dhcp6_lease_get_t2(sd_dhcp6_lease *lease, uint64_t *ret);
int sd_dhcp6_lease_get_t2_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
int sd_dhcp6_lease_get_valid_lifetime(sd_dhcp6_lease *lease, uint64_t *ret);
int sd_dhcp6_lease_get_valid_lifetime_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret);
void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
struct in6_addr *addr,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid);
void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
uint8_t *prefix_len,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid);
int sd_dhcp6_lease_address_iterator_reset(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_address_iterator_next(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_get_address(
sd_dhcp6_lease *lease,
struct in6_addr *ret);
int sd_dhcp6_lease_get_address_lifetime(
sd_dhcp6_lease *lease,
uint64_t *ret_lifetime_preferred,
uint64_t *ret_lifetime_valid);
int sd_dhcp6_lease_get_address_lifetime_timestamp(
sd_dhcp6_lease *lease,
clockid_t clock,
uint64_t *ret_lifetime_preferred,
uint64_t *ret_lifetime_valid);
int sd_dhcp6_lease_has_address(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_pd_iterator_reset(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_pd_iterator_next(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_get_pd_prefix(
sd_dhcp6_lease *lease,
struct in6_addr *ret_prefix,
uint8_t *ret_prefix_length);
int sd_dhcp6_lease_get_pd_lifetime(
sd_dhcp6_lease *lease,
uint64_t *ret_lifetime_preferred,
uint64_t *ret_lifetime_valid);
int sd_dhcp6_lease_get_pd_lifetime_timestamp(
sd_dhcp6_lease *lease,
clockid_t clock,
uint64_t *ret_lifetime_preferred,
uint64_t *ret_lifetime_valid);
int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret);
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret);
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret);
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret);
int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret);
int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret);
int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease *lease, sd_dhcp6_option ***ret);
sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);

View file

@ -20,6 +20,8 @@
#include <inttypes.h>
#include <sys/types.h>
#include "sd-dhcp6-protocol.h"
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;

View file

@ -0,0 +1,174 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef foosddhcp6protocolhfoo
#define foosddhcp6protocolhfoo
/***
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <https://www.gnu.org/licenses/>.
***/
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
enum {
SD_DHCP6_OPTION_CLIENTID = 1, /* RFC 8415 */
SD_DHCP6_OPTION_SERVERID = 2, /* RFC 8415 */
SD_DHCP6_OPTION_IA_NA = 3, /* RFC 8415 */
SD_DHCP6_OPTION_IA_TA = 4, /* RFC 8415 */
SD_DHCP6_OPTION_IAADDR = 5, /* RFC 8415 */
SD_DHCP6_OPTION_ORO = 6, /* RFC 8415 */
SD_DHCP6_OPTION_PREFERENCE = 7, /* RFC 8415 */
SD_DHCP6_OPTION_ELAPSED_TIME = 8, /* RFC 8415 */
SD_DHCP6_OPTION_RELAY_MSG = 9, /* RFC 8415 */
/* option code 10 is unassigned */
SD_DHCP6_OPTION_AUTH = 11, /* RFC 8415 */
SD_DHCP6_OPTION_UNICAST = 12, /* RFC 8415 */
SD_DHCP6_OPTION_STATUS_CODE = 13, /* RFC 8415 */
SD_DHCP6_OPTION_RAPID_COMMIT = 14, /* RFC 8415 */
SD_DHCP6_OPTION_USER_CLASS = 15, /* RFC 8415 */
SD_DHCP6_OPTION_VENDOR_CLASS = 16, /* RFC 8415 */
SD_DHCP6_OPTION_VENDOR_OPTS = 17, /* RFC 8415 */
SD_DHCP6_OPTION_INTERFACE_ID = 18, /* RFC 8415 */
SD_DHCP6_OPTION_RECONF_MSG = 19, /* RFC 8415 */
SD_DHCP6_OPTION_RECONF_ACCEPT = 20, /* RFC 8415 */
SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME = 21, /* RFC 3319 */
SD_DHCP6_OPTION_SIP_SERVER_ADDRESS = 22, /* RFC 3319 */
SD_DHCP6_OPTION_DNS_SERVER = 23, /* RFC 3646 */
SD_DHCP6_OPTION_DOMAIN = 24, /* RFC 3646 */
SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, RFC 8415 */
SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, RFC 8415 */
SD_DHCP6_OPTION_NIS_SERVER = 27, /* RFC 3898 */
SD_DHCP6_OPTION_NISP_SERVER = 28, /* RFC 3898 */
SD_DHCP6_OPTION_NIS_DOMAIN_NAME = 29, /* RFC 3898 */
SD_DHCP6_OPTION_NISP_DOMAIN_NAME = 30, /* RFC 3898 */
SD_DHCP6_OPTION_SNTP_SERVER = 31, /* RFC 4075, deprecated */
SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME = 32, /* RFC 4242, 8415, sec. 21.23 */
SD_DHCP6_OPTION_BCMCS_SERVER_D = 33, /* RFC 4280 */
SD_DHCP6_OPTION_BCMCS_SERVER_A = 34, /* RFC 4280 */
/* option code 35 is unassigned */
SD_DHCP6_OPTION_GEOCONF_CIVIC = 36, /* RFC 4776 */
SD_DHCP6_OPTION_REMOTE_ID = 37, /* RFC 4649 */
SD_DHCP6_OPTION_SUBSCRIBER_ID = 38, /* RFC 4580 */
SD_DHCP6_OPTION_CLIENT_FQDN = 39, /* RFC 4704 */
SD_DHCP6_OPTION_PANA_AGENT = 40, /* RFC 5192 */
SD_DHCP6_OPTION_POSIX_TIMEZONE = 41, /* RFC 4833 */
SD_DHCP6_OPTION_TZDB_TIMEZONE = 42, /* RFC 4833 */
SD_DHCP6_OPTION_ERO = 43, /* RFC 4994 */
SD_DHCP6_OPTION_LQ_QUERY = 44, /* RFC 5007 */
SD_DHCP6_OPTION_CLIENT_DATA = 45, /* RFC 5007 */
SD_DHCP6_OPTION_CLT_TIME = 46, /* RFC 5007 */
SD_DHCP6_OPTION_LQ_RELAY_DATA = 47, /* RFC 5007 */
SD_DHCP6_OPTION_LQ_CLIENT_LINK = 48, /* RFC 5007 */
SD_DHCP6_OPTION_MIP6_HNIDF = 49, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_VDINF = 50, /* RFC 6610 */
SD_DHCP6_OPTION_V6_LOST = 51, /* RFC 5223 */
SD_DHCP6_OPTION_CAPWAP_AC_V6 = 52, /* RFC 5417 */
SD_DHCP6_OPTION_RELAY_ID = 53, /* RFC 5460 */
SD_DHCP6_OPTION_IPV6_ADDRESS_MOS = 54, /* RFC 5678 */
SD_DHCP6_OPTION_IPV6_FQDN_MOS = 55, /* RFC 5678 */
SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */
SD_DHCP6_OPTION_V6_ACCESS_DOMAIN = 57, /* RFC 5986 */
SD_DHCP6_OPTION_SIP_UA_CS_LIST = 58, /* RFC 6011 */
SD_DHCP6_OPTION_BOOTFILE_URL = 59, /* RFC 5970 */
SD_DHCP6_OPTION_BOOTFILE_PARAM = 60, /* RFC 5970 */
SD_DHCP6_OPTION_CLIENT_ARCH_TYPE = 61, /* RFC 5970 */
SD_DHCP6_OPTION_NII = 62, /* RFC 5970 */
SD_DHCP6_OPTION_GEOLOCATION = 63, /* RFC 6225 */
SD_DHCP6_OPTION_AFTR_NAME = 64, /* RFC 6334 */
SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME = 65, /* RFC 6440 */
SD_DHCP6_OPTION_RSOO = 66, /* RFC 6422 */
SD_DHCP6_OPTION_PD_EXCLUDE = 67, /* RFC 6603 */
SD_DHCP6_OPTION_VSS = 68, /* RFC 6607 */
SD_DHCP6_OPTION_MIP6_IDINF = 69, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_UDINF = 70, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_HNP = 71, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_HAA = 72, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_HAF = 73, /* RFC 6610 */
SD_DHCP6_OPTION_RDNSS_SELECTION = 74, /* RFC 6731 */
SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME = 75, /* RFC 6784 */
SD_DHCP6_OPTION_KRB_REALM_NAME = 76, /* RFC 6784 */
SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME = 77, /* RFC 6784 */
SD_DHCP6_OPTION_KRB_KDC = 78, /* RFC 6784 */
SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR = 79, /* RFC 6939 */
SD_DHCP6_OPTION_LINK_ADDRESS = 80, /* RFC 6977 */
SD_DHCP6_OPTION_RADIUS = 81, /* RFC 7037 */
SD_DHCP6_OPTION_SOL_MAX_RT = 82, /* RFC 7083, RFC 8415 */
SD_DHCP6_OPTION_INF_MAX_RT = 83, /* RFC 7083, RFC 8415 */
SD_DHCP6_OPTION_ADDRSEL = 84, /* RFC 7078 */
SD_DHCP6_OPTION_ADDRSEL_TABLE = 85, /* RFC 7078 */
SD_DHCP6_OPTION_V6_PCP_SERVER = 86, /* RFC 7291 */
SD_DHCP6_OPTION_DHCPV4_MSG = 87, /* RFC 7341 */
SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER = 88, /* RFC 7341 */
SD_DHCP6_OPTION_S46_RULE = 89, /* RFC 7598 */
SD_DHCP6_OPTION_S46_BR = 90, /* RFC 7598, RFC 8539 */
SD_DHCP6_OPTION_S46_DMR = 91, /* RFC 7598 */
SD_DHCP6_OPTION_S46_V4V6BIND = 92, /* RFC 7598 */
SD_DHCP6_OPTION_S46_PORTPARAMS = 93, /* RFC 7598 */
SD_DHCP6_OPTION_S46_CONT_MAPE = 94, /* RFC 7598 */
SD_DHCP6_OPTION_S46_CONT_MAPT = 95, /* RFC 7598 */
SD_DHCP6_OPTION_S46_CONT_LW = 96, /* RFC 7598 */
SD_DHCP6_OPTION_4RD = 97, /* RFC 7600 */
SD_DHCP6_OPTION_4RD_MAP_RULE = 98, /* RFC 7600 */
SD_DHCP6_OPTION_4RD_NON_MAP_RULE = 99, /* RFC 7600 */
SD_DHCP6_OPTION_LQ_BASE_TIME = 100, /* RFC 7653 */
SD_DHCP6_OPTION_LQ_START_TIME = 101, /* RFC 7653 */
SD_DHCP6_OPTION_LQ_END_TIME = 102, /* RFC 7653 */
SD_DHCP6_OPTION_CAPTIVE_PORTAL = 103, /* RFC 8910 */
SD_DHCP6_OPTION_MPL_PARAMETERS = 104, /* RFC 7774 */
SD_DHCP6_OPTION_ANI_ATT = 105, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_NETWORK_NAME = 106, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_AP_NAME = 107, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_AP_BSSID = 108, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_OPERATOR_ID = 109, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_OPERATOR_REALM = 110, /* RFC 7839 */
SD_DHCP6_OPTION_S46_PRIORITY = 111, /* RFC 8026 */
SD_DHCP6_OPTION_MUD_URL_V6 = 112, /* RFC 8520 */
SD_DHCP6_OPTION_V6_PREFIX64 = 113, /* RFC 8115 */
SD_DHCP6_OPTION_F_BINDING_STATUS = 114, /* RFC 8156 */
SD_DHCP6_OPTION_F_CONNECT_FLAGS = 115, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO = 116, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_HOST_NAME = 117, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_ZONE_NAME = 118, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_FLAGS = 119, /* RFC 8156 */
SD_DHCP6_OPTION_F_EXPIRATION_TIME = 120, /* RFC 8156 */
SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD = 121, /* RFC 8156 */
SD_DHCP6_OPTION_F_MCLT = 122, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_LIFETIME = 123, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT = 124, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME = 125, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME = 126, /* RFC 8156 */
SD_DHCP6_OPTION_F_PROTOCOL_VERSION = 127, /* RFC 8156 */
SD_DHCP6_OPTION_F_KEEPALIVE_TIME = 128, /* RFC 8156 */
SD_DHCP6_OPTION_F_RECONFIGURE_DATA = 129, /* RFC 8156 */
SD_DHCP6_OPTION_F_RELATIONSHIP_NAME = 130, /* RFC 8156 */
SD_DHCP6_OPTION_F_SERVER_FLAGS = 131, /* RFC 8156 */
SD_DHCP6_OPTION_F_SERVER_STATE = 132, /* RFC 8156 */
SD_DHCP6_OPTION_F_START_TIME_OF_STATE = 133, /* RFC 8156 */
SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME = 134, /* RFC 8156 */
SD_DHCP6_OPTION_RELAY_PORT = 135, /* RFC 8357 */
SD_DHCP6_OPTION_V6_SZTP_REDIRECT = 136, /* RFC 8572 */
SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX = 137, /* RFC 8539 */
SD_DHCP6_OPTION_IA_LL = 138, /* RFC 8947 */
SD_DHCP6_OPTION_LLADDR = 139, /* RFC 8947 */
SD_DHCP6_OPTION_SLAP_QUAD = 140, /* RFC 8948 */
SD_DHCP6_OPTION_V6_DOTS_RI = 141, /* RFC 8973 */
SD_DHCP6_OPTION_V6_DOTS_ADDRESS = 142, /* RFC 8973 */
SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143 /* RFC 6153 */
/* option codes 144-65535 are unassigned */
};
_SD_END_DECLARATIONS;
#endif

View file

@ -50,6 +50,7 @@ int sd_id128_get_machine(sd_id128_t *ret);
int sd_id128_get_boot(sd_id128_t *ret);
int sd_id128_get_invocation(sd_id128_t *ret);
int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);

View file

@ -42,7 +42,8 @@ enum {
SD_NDISC_OPTION_RDNSS = 25,
SD_NDISC_OPTION_FLAGS_EXTENSION = 26,
SD_NDISC_OPTION_DNSSL = 31,
SD_NDISC_OPTION_CAPTIVE_PORTAL = 37
SD_NDISC_OPTION_CAPTIVE_PORTAL = 37,
SD_NDISC_OPTION_PREF64 = 38
};
/* Route preference, RFC 4191, Section 2.1 */
@ -85,14 +86,17 @@ int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt);
sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);
int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret);
int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size);
int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size);
int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret);
int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags);
int sd_ndisc_router_get_icmp6_ratelimit(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret);
int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime);
int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_get_retransmission_time(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret);
/* Generic option access */
@ -100,28 +104,42 @@ int sd_ndisc_router_option_rewind(sd_ndisc_router *rt);
int sd_ndisc_router_option_next(sd_ndisc_router *rt);
int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret);
int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type);
int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size);
int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size);
/* Specific option access: SD_NDISC_OPTION_PREFIX_INFORMATION */
int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret);
int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret);
int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_prefix_get_valid_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret);
int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen);
int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret);
int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret);
/* Specific option access: SD_NDISC_OPTION_ROUTE_INFORMATION */
int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen);
int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_route_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret);
int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret);
int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret);
/* Specific option access: SD_NDISC_OPTION_RDNSS */
int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret);
int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_rdnss_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
/* Specific option access: SD_NDISC_OPTION_DNSSL */
int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret);
int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_dnssl_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
/* Specific option access: SD_NDISC_OPTION_CAPTIVE_PORTAL */
int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret, size_t *ret_size);
/* Specific option access: SD_NDISC_OPTION_PREF64 */
int sd_ndisc_router_prefix64_get_prefix(sd_ndisc_router *rt, struct in6_addr *ret);
int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router *rt, unsigned *ret);
int sd_ndisc_router_prefix64_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_prefix64_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref);

View file

@ -5,6 +5,7 @@ libnm_systemd_shared = static_library(
sources: files(
'nm-sd-utils-shared.c',
'src/basic/alloc-util.c',
'src/basic/btrfs.c',
'src/basic/env-file.c',
'src/basic/env-util.c',
'src/basic/escape.c',
@ -58,6 +59,7 @@ libnm_systemd_shared = static_library(
top_inc,
src_inc,
],
c_args: libnm_systemd_common_cflags,
dependencies: glib_dep,
)

View file

@ -105,6 +105,33 @@ void* greedy_realloc0(
return q;
}
void* greedy_realloc_append(
void **p,
size_t *n_p,
const void *from,
size_t n_from,
size_t size) {
uint8_t *q;
assert(p);
assert(n_p);
assert(from || n_from == 0);
if (n_from > SIZE_MAX - *n_p)
return NULL;
q = greedy_realloc(p, *n_p + n_from, size);
if (!q)
return NULL;
memcpy_safe(q + *n_p * size, from, n_from * size);
*n_p += n_from;
return q;
}
void *expand_to_usable(void *ptr, size_t newsize _unused_) {
return ptr;
}

View file

@ -15,13 +15,12 @@
typedef void (*free_func_t)(void *p);
typedef void* (*mfree_func_t)(void *p);
typedef void (*free_array_func_t)(void *p, size_t n);
/* If for some reason more than 4M are allocated on the stack, let's abort immediately. It's better than
* proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */
#define ALLOCA_MAX (4U*1024U*1024U)
#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
#define new(t, n) ((t*) malloc_multiply(n, sizeof(t)))
#define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
@ -46,9 +45,9 @@ typedef void (*free_array_func_t)(void *p, size_t n);
(t*) alloca0((sizeof(t)*_n_)); \
})
#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
#define newdup(t, p, n) ((t*) memdup_multiply(p, n, sizeof(t)))
#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n)))
#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, n, sizeof(t)))
#define malloc0(n) (calloc(1, (n) ?: 1))
@ -113,7 +112,7 @@ static inline bool size_multiply_overflow(size_t size, size_t need) {
return _unlikely_(need != 0 && size > (SIZE_MAX / need));
}
_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) {
_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t need, size_t size) {
if (size_multiply_overflow(size, need))
return NULL;
@ -129,7 +128,7 @@ _alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size
}
#endif
_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) {
_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t need, size_t size) {
if (size_multiply_overflow(size, need))
return NULL;
@ -138,7 +137,7 @@ _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, si
/* Note that we can't decorate this function with _alloc_() since the returned memory area is one byte larger
* than the product of its parameters. */
static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t need) {
static inline void *memdup_suffix0_multiply(const void *p, size_t need, size_t size) {
if (size_multiply_overflow(size, need))
return NULL;
@ -147,6 +146,7 @@ static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t n
void* greedy_realloc(void **p, size_t need, size_t size);
void* greedy_realloc0(void **p, size_t need, size_t size);
void* greedy_realloc_append(void **p, size_t *n_p, const void *from, size_t n_from, size_t size);
#define GREEDY_REALLOC(array, need) \
greedy_realloc((void**) &(array), (need), sizeof((array)[0]))
@ -154,6 +154,9 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
#define GREEDY_REALLOC0(array, need) \
greedy_realloc0((void**) &(array), (need), sizeof((array)[0]))
#define GREEDY_REALLOC_APPEND(array, n_array, from, n_from) \
greedy_realloc_append((void**) &(array), (size_t*) &(n_array), (from), (n_from), sizeof((array)[0]))
#define alloca0(n) \
({ \
char *_new_; \
@ -224,7 +227,6 @@ static inline size_t malloc_sizeof_safe(void **xp) {
MALLOC_SIZEOF_SAFE(x)/sizeof((x)[0]), \
VOID_0))
/* These are like strdupa()/strndupa(), but honour ALLOCA_MAX */
#define strdupa_safe(s) \
({ \
@ -235,7 +237,40 @@ static inline size_t malloc_sizeof_safe(void **xp) {
#define strndupa_safe(s, n) \
({ \
const char *_t = (s); \
(char*) memdupa_suffix0(_t, strnlen(_t, (n))); \
(char*) memdupa_suffix0(_t, strnlen(_t, n)); \
})
/* Free every element of the array. */
static inline void free_many(void **p, size_t n) {
assert(p || n == 0);
FOREACH_ARRAY(i, p, n)
*i = mfree(*i);
}
/* Typesafe wrapper for char** rather than void**. Unfortunately C won't implicitly cast this. */
static inline void free_many_charp(char **c, size_t n) {
free_many((void**) c, n);
}
_alloc_(2) static inline void *realloc0(void *p, size_t new_size) {
size_t old_size;
void *q;
/* Like realloc(), but initializes anything appended to zero */
old_size = MALLOC_SIZEOF_SAFE(p);
q = realloc(p, new_size);
if (!q)
return NULL;
new_size = MALLOC_SIZEOF_SAFE(q); /* Update with actually allocated space */
if (new_size > old_size)
memset((uint8_t*) q + old_size, 0, new_size - old_size);
return q;
}
#include "memory-util.h"

View file

@ -0,0 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <inttypes.h>
#include <stddef.h>
const char *arphrd_to_name(int id);
int arphrd_from_name(const char *name);
size_t arphrd_to_hw_addr_len(uint16_t arphrd);

View file

@ -1,13 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <sys/types.h>
#include "macro.h"
int asynchronous_job(void* (*func)(void *p), void *arg);
int asynchronous_sync(pid_t *ret_pid);
int asynchronous_close(int fd);
DEFINE_TRIVIAL_CLEANUP_FUNC(int, asynchronous_close);

View file

@ -0,0 +1,100 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "nm-sd-adapt-shared.h"
#include <linux/btrfs.h>
#include <sys/ioctl.h>
#include "btrfs.h"
#include "fd-util.h"
#include "fs-util.h"
#include "path-util.h"
int btrfs_validate_subvolume_name(const char *name) {
if (!filename_is_valid(name))
return -EINVAL;
if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
return -E2BIG;
return 0;
}
static int extract_subvolume_name(const char *path, char **ret) {
_cleanup_free_ char *fn = NULL;
int r;
assert(path);
assert(ret);
r = path_extract_filename(path, &fn);
if (r < 0)
return r;
r = btrfs_validate_subvolume_name(fn);
if (r < 0)
return r;
*ret = TAKE_PTR(fn);
return 0;
}
int btrfs_subvol_make(int dir_fd, const char *path) {
struct btrfs_ioctl_vol_args args = {};
_cleanup_free_ char *subvolume = NULL, *parent = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(!isempty(path));
r = extract_subvolume_name(path, &subvolume);
if (r < 0)
return r;
r = path_extract_directory(path, &parent);
if (r < 0) {
if (r != -EDESTADDRREQ) /* Propagate error, unless only a filename was specified, which is OK */
return r;
dir_fd = fd_reopen_condition(dir_fd, O_CLOEXEC, O_PATH, &fd); /* drop O_PATH if it is set */
if (dir_fd < 0)
return dir_fd;
} else {
fd = openat(dir_fd, parent, O_DIRECTORY|O_RDONLY|O_CLOEXEC, 0);
if (fd < 0)
return -errno;
dir_fd = fd;
}
strncpy(args.name, subvolume, sizeof(args.name)-1);
return RET_NERRNO(ioctl(dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args));
}
int btrfs_subvol_make_fallback(int dir_fd, const char *path, mode_t mode) {
mode_t old, combined;
int r;
assert(path);
/* Let's work like mkdir(), i.e. take the specified mode, and mask it with the current umask. */
old = umask(~mode);
combined = old | ~mode;
if (combined != ~mode)
umask(combined);
r = btrfs_subvol_make(dir_fd, path);
umask(old);
if (r >= 0)
return 1; /* subvol worked */
if (!ERRNO_IS_NOT_SUPPORTED(r))
return r;
if (mkdirat(dir_fd, path, mode) < 0)
return -errno;
return 0; /* plain directory */
}

View file

@ -0,0 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
int btrfs_validate_subvolume_name(const char *name);
int btrfs_subvol_make(int dir_fd, const char *path);
int btrfs_subvol_make_fallback(int dir_fd, const char *path, mode_t mode);

View file

@ -10,6 +10,7 @@
#include <sys/types.h>
#include "constants.h"
#include "pidref.h"
#include "set.h"
#define SYSTEMD_CGROUP_CONTROLLER_LEGACY "name=systemd"
@ -35,7 +36,7 @@ typedef enum CGroupController {
CGROUP_CONTROLLER_BPF_SOCKET_BIND,
CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES,
/* The BPF hook implementing RestrictFileSystems= is not defined here.
* It's applied as late as possible in exec_child() so we don't block
* It's applied as late as possible in exec_invoke() so we don't block
* our own unit setup code. */
_CGROUP_CONTROLLER_MAX,
@ -66,10 +67,13 @@ typedef enum CGroupMask {
/* All real cgroup v2 controllers */
CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
/* All controllers we want to delegate in case of Delegate=yes. Which are pretty much the v2 controllers only, as delegation on v1 is not safe, and bpf stuff isn't a real controller */
CGROUP_MASK_DELEGATE = CGROUP_MASK_V2,
/* All cgroup v2 BPF pseudo-controllers */
CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND|CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES,
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1,
} CGroupMask;
static inline CGroupMask CGROUP_MASK_EXTEND_JOINED(CGroupMask mask) {
@ -176,13 +180,13 @@ typedef enum CGroupUnified {
* generate paths with multiple adjacent / removed.
*/
int cg_enumerate_processes(const char *controller, const char *path, FILE **_f);
int cg_read_pid(FILE *f, pid_t *_pid);
int cg_read_event(const char *controller, const char *path, const char *event,
char **val);
int cg_enumerate_processes(const char *controller, const char *path, FILE **ret);
int cg_read_pid(FILE *f, pid_t *ret);
int cg_read_pidref(FILE *f, PidRef *ret);
int cg_read_event(const char *controller, const char *path, const char *event, char **ret);
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d);
int cg_read_subgroup(DIR *d, char **fn);
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **ret);
int cg_read_subgroup(DIR *d, char **ret);
typedef enum CGroupFlags {
CGROUP_SIGCONT = 1 << 0,
@ -190,25 +194,31 @@ typedef enum CGroupFlags {
CGROUP_REMOVE = 1 << 2,
} CGroupFlags;
typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata);
typedef int (*cg_kill_log_func_t)(const PidRef *pid, int sig, void *userdata);
int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
int cg_kill_kernel_sigkill(const char *controller, const char *path);
int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
int cg_kill(const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
int cg_kill_kernel_sigkill(const char *path);
int cg_kill_recursive(const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
int cg_split_spec(const char *spec, char **ret_controller, char **ret_path);
int cg_mangle_path(const char *path, char **result);
int cg_mangle_path(const char *path, char **ret);
int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs);
int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs);
int cg_get_path(const char *controller, const char *path, const char *suffix, char **ret);
int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **ret);
int cg_pid_get_path(const char *controller, pid_t pid, char **path);
int cg_pid_get_path(const char *controller, pid_t pid, char **ret);
int cg_pidref_get_path(const char *controller, const PidRef *pidref, char **ret);
int cg_rmdir(const char *controller, const char *path);
int cg_is_threaded(const char *controller, const char *path);
int cg_is_threaded(const char *path);
typedef enum {
int cg_is_delegated(const char *path);
int cg_is_delegated_fd(int fd);
int cg_has_coredump_receive(const char *path);
typedef enum {
CG_KEY_MODE_GRACEFUL = 1 << 0,
} CGroupKeyMode;
@ -239,14 +249,14 @@ int cg_get_attribute_as_uint64(const char *controller, const char *path, const c
/* Does a parse_boolean() on the attribute contents and sets ret accordingly */
int cg_get_attribute_as_bool(const char *controller, const char *path, const char *attribute, bool *ret);
int cg_get_owner(const char *controller, const char *path, uid_t *ret_uid);
int cg_get_owner(const char *path, uid_t *ret_uid);
int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags);
int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size);
int cg_get_xattr_malloc(const char *controller, const char *path, const char *name, char **ret);
int cg_set_xattr(const char *path, const char *name, const void *value, size_t size, int flags);
int cg_get_xattr(const char *path, const char *name, void *value, size_t size);
int cg_get_xattr_malloc(const char *path, const char *name, char **ret);
/* Returns negative on error, and 0 or 1 on success for the bool value */
int cg_get_xattr_bool(const char *controller, const char *path, const char *name);
int cg_remove_xattr(const char *controller, const char *path, const char *name);
int cg_get_xattr_bool(const char *path, const char *name);
int cg_remove_xattr(const char *path, const char *name);
int cg_install_release_agent(const char *controller, const char *agent);
int cg_uninstall_release_agent(const char *controller);
@ -257,27 +267,28 @@ int cg_is_empty_recursive(const char *controller, const char *path);
int cg_get_root_path(char **path);
int cg_path_get_cgroupid(const char *path, uint64_t *ret);
int cg_path_get_session(const char *path, char **session);
int cg_path_get_owner_uid(const char *path, uid_t *uid);
int cg_path_get_unit(const char *path, char **unit);
int cg_path_get_unit_path(const char *path, char **unit);
int cg_path_get_user_unit(const char *path, char **unit);
int cg_path_get_machine_name(const char *path, char **machine);
int cg_path_get_slice(const char *path, char **slice);
int cg_path_get_user_slice(const char *path, char **slice);
int cg_path_get_session(const char *path, char **ret_session);
int cg_path_get_owner_uid(const char *path, uid_t *ret_uid);
int cg_path_get_unit(const char *path, char **ret_unit);
int cg_path_get_unit_path(const char *path, char **ret_unit);
int cg_path_get_user_unit(const char *path, char **ret_unit);
int cg_path_get_machine_name(const char *path, char **ret_machine);
int cg_path_get_slice(const char *path, char **ret_slice);
int cg_path_get_user_slice(const char *path, char **ret_slice);
int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted);
int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup);
int cg_shift_path(const char *cgroup, const char *cached_root, const char **ret_shifted);
int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **ret_cgroup);
int cg_pid_get_session(pid_t pid, char **session);
int cg_pid_get_owner_uid(pid_t pid, uid_t *uid);
int cg_pid_get_unit(pid_t pid, char **unit);
int cg_pid_get_user_unit(pid_t pid, char **unit);
int cg_pid_get_machine_name(pid_t pid, char **machine);
int cg_pid_get_slice(pid_t pid, char **slice);
int cg_pid_get_user_slice(pid_t pid, char **slice);
int cg_pid_get_session(pid_t pid, char **ret_session);
int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid);
int cg_pid_get_unit(pid_t pid, char **ret_unit);
int cg_pidref_get_unit(const PidRef *pidref, char **ret);
int cg_pid_get_user_unit(pid_t pid, char **ret_unit);
int cg_pid_get_machine_name(pid_t pid, char **ret_machine);
int cg_pid_get_slice(pid_t pid, char **ret_slice);
int cg_pid_get_user_slice(pid_t pid, char **ret_slice);
int cg_path_decode_unit(const char *cgroup, char **unit);
int cg_path_decode_unit(const char *cgroup, char **ret_unit);
bool cg_needs_escape(const char *p);
int cg_escape(const char *p, char **ret);

View file

@ -59,22 +59,13 @@
#define NOTIFY_FD_MAX 768
#define NOTIFY_BUFFER_MAX PIPE_BUF
#if HAVE_SPLIT_USR
# define _CONF_PATHS_SPLIT_USR_NULSTR(n) "/lib/" n "\0"
# define _CONF_PATHS_SPLIT_USR(n) , "/lib/" n
#else
# define _CONF_PATHS_SPLIT_USR_NULSTR(n)
# define _CONF_PATHS_SPLIT_USR(n)
#endif
/* Return a nulstr for a standard cascade of configuration paths, suitable to pass to
* conf_files_list_nulstr() to implement drop-in directories for extending configuration files. */
#define CONF_PATHS_NULSTR(n) \
"/etc/" n "\0" \
"/run/" n "\0" \
"/usr/local/lib/" n "\0" \
"/usr/lib/" n "\0" \
_CONF_PATHS_SPLIT_USR_NULSTR(n)
"/usr/lib/" n "\0"
#define CONF_PATHS_USR(n) \
"/etc/" n, \
@ -83,8 +74,7 @@
"/usr/lib/" n
#define CONF_PATHS(n) \
CONF_PATHS_USR(n) \
_CONF_PATHS_SPLIT_USR(n)
CONF_PATHS_USR(n)
#define CONF_PATHS_USR_STRV(n) \
STRV_MAKE(CONF_PATHS_USR(n))
@ -99,14 +89,9 @@
* in containers so that our children inherit that. */
#define DEFAULT_RLIMIT_MEMLOCK (1024ULL*1024ULL*8ULL)
#define PLYMOUTH_SOCKET { \
.un.sun_family = AF_UNIX, \
.un.sun_path = "\0/org/freedesktop/plymouthd", \
}
/* Path where PID1 listens for varlink subscriptions from systemd-oomd to notify of changes in ManagedOOM settings. */
#define VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM "/run/systemd/io.system.ManagedOOM"
#define VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM "/run/systemd/io.systemd.ManagedOOM"
/* Path where systemd-oomd listens for varlink connections from user managers to report changes in ManagedOOM settings. */
#define VARLINK_ADDR_PATH_MANAGED_OOM_USER "/run/systemd/oom/io.system.ManagedOOM"
#define VARLINK_ADDR_PATH_MANAGED_OOM_USER "/run/systemd/oom/io.systemd.ManagedOOM"
#define KERNEL_BASELINE_VERSION "4.15"

View file

@ -127,7 +127,7 @@ static int parse_env_file_internal(
state = VALUE;
if (!GREEDY_REALLOC(value, n_value+2))
return -ENOMEM;
return -ENOMEM;
value[n_value++] = c;
}
@ -245,7 +245,13 @@ static int parse_env_file_internal(
break;
case COMMENT_ESCAPE:
state = COMMENT;
log_debug("The line which doesn't begin with \";\" or \"#\", but follows a comment" \
" line trailing with escape is now treated as a non comment line since v254.");
if (strchr(NEWLINE, c)) {
state = PRE_KEY;
line++;
} else
state = COMMENT;
break;
}
}
@ -522,6 +528,7 @@ static int merge_env_file_push(
char ***env = ASSERT_PTR(userdata);
char *expanded_value;
int r;
assert(key);
@ -536,12 +543,12 @@ static int merge_env_file_push(
return 0;
}
expanded_value = replace_env(value, *env,
REPLACE_ENV_USE_ENVIRONMENT|
REPLACE_ENV_ALLOW_BRACELESS|
REPLACE_ENV_ALLOW_EXTENDED);
if (!expanded_value)
return -ENOMEM;
r = replace_env(value,
*env,
REPLACE_ENV_USE_ENVIRONMENT|REPLACE_ENV_ALLOW_BRACELESS|REPLACE_ENV_ALLOW_EXTENDED,
&expanded_value);
if (r < 0)
return log_error_errno(r, "%s:%u: Failed to expand variable '%s': %m", strna(filename), line, value);
free_and_replace(value, expanded_value);
@ -599,7 +606,7 @@ static void write_env_var(FILE *f, const char *v) {
fputc_unlocked('\n', f);
}
int write_env_file_at(int dir_fd, const char *fname, char **l) {
int write_env_file(int dir_fd, const char *fname, char **headers, char **l) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
int r;
@ -613,6 +620,12 @@ int write_env_file_at(int dir_fd, const char *fname, char **l) {
(void) fchmod_umask(fileno(f), 0644);
STRV_FOREACH(i, headers) {
assert(isempty(*i) || startswith(*i, "#"));
fputs_unlocked(*i, f);
fputc_unlocked('\n', f);
}
STRV_FOREACH(i, l)
write_env_var(f, *i);
@ -627,4 +640,12 @@ int write_env_file_at(int dir_fd, const char *fname, char **l) {
(void) unlinkat(dir_fd, p, 0);
return r;
}
int write_vconsole_conf(int dir_fd, const char *fname, char **l) {
char **headers = STRV_MAKE(
"# Written by systemd-localed(8) or systemd-firstboot(1), read by systemd-localed",
"# and systemd-vconsole-setup(8). Use localectl(1) to update this file.");
return write_env_file(dir_fd, fname, headers, l);
}
#endif /* NM_IGNORED */

View file

@ -19,7 +19,6 @@ int load_env_file_pairs_fd(int fd, const char *fname, char ***ret);
int merge_env_file(char ***env, FILE *f, const char *fname);
int write_env_file_at(int dir_fd, const char *fname, char **l);
static inline int write_env_file(const char *fname, char **l) {
return write_env_file_at(AT_FDCWD, fname, l);
}
int write_env_file(int dir_fd, const char *fname, char **headers, char **l);
int write_vconsole_conf(int dir_fd, const char *fname, char **l);

View file

@ -29,20 +29,21 @@
"_"
static bool env_name_is_valid_n(const char *e, size_t n) {
if (!e)
return false;
if (n == SIZE_MAX)
n = strlen_ptr(e);
if (n <= 0)
return false;
assert(e);
if (ascii_isdigit(e[0]))
return false;
/* POSIX says the overall size of the environment block cannot
* be > ARG_MAX, an individual assignment hence cannot be
* either. Discounting the equal sign and trailing NUL this
* hence leaves ARG_MAX-2 as longest possible variable
* name. */
/* POSIX says the overall size of the environment block cannot be > ARG_MAX, an individual assignment
* hence cannot be either. Discounting the equal sign and trailing NUL this hence leaves ARG_MAX-2 as
* longest possible variable name. */
if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
return false;
@ -246,9 +247,9 @@ static bool env_match(const char *t, const char *pattern) {
return true;
if (!strchr(pattern, '=')) {
size_t l = strlen(pattern);
t = startswith(t, pattern);
return strneq(t, pattern, l) && t[l] == '=';
return t && *t == '=';
}
return false;
@ -460,6 +461,35 @@ int strv_env_assign(char ***l, const char *key, const char *value) {
return strv_env_replace_consume(l, p);
}
int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) {
int r;
assert(l);
assert(key);
if (!env_name_is_valid(key))
return -EINVAL;
if (!valuef) {
strv_env_unset(*l, key);
return 0;
}
_cleanup_free_ char *value = NULL;
va_list ap;
va_start(ap, valuef);
r = vasprintf(&value, valuef, ap);
va_end(ap);
if (r < 0)
return -ENOMEM;
char *p = strjoin(key, "=", value);
if (!p)
return -ENOMEM;
return strv_env_replace_consume(l, p);
}
int _strv_env_assign_many(char ***l, ...) {
va_list ap;
int r;
@ -502,32 +532,31 @@ int _strv_env_assign_many(char ***l, ...) {
return 0;
}
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags) {
assert(name);
if (k == SIZE_MAX)
k = strlen(name);
if (k <= 0)
return NULL;
STRV_FOREACH_BACKWARDS(i, l)
if (strneq(*i, name, k) &&
(*i)[k] == '=')
return *i + k + 1;
if (strneq(*i, name, k) && (*i)[k] == '=')
return (char*) *i + k + 1;
if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
const char *t;
/* Safety check that the name is not overly long, before we do a stack allocation */
if (k > (size_t) sysconf(_SC_ARG_MAX) - 2)
return NULL;
t = strndupa_safe(name, k);
return getenv(t);
};
return NULL;
}
char *strv_env_get(char **l, const char *name) {
assert(name);
return strv_env_get_n(l, name, strlen(name), 0);
}
#endif /* NM_IGNORED */
char *strv_env_pairs_get(char **l, const char *name) {
@ -578,7 +607,61 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
return e;
}
char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
static int strv_extend_with_length(char ***l, const char *s, size_t n) {
char *c;
c = strndup(s, n);
if (!c)
return -ENOMEM;
return strv_consume(l, c);
}
static int strv_env_get_n_validated(
char **env,
const char *name,
size_t l,
ReplaceEnvFlags flags,
char **ret, /* points into the env block! do not free! */
char ***unset_variables, /* updated in place */
char ***bad_variables) { /* ditto */
char *e;
int r;
assert(l == 0 || name);
assert(ret);
if (env_name_is_valid_n(name, l)) {
e = strv_env_get_n(env, name, l, flags);
if (!e && unset_variables) {
r = strv_extend_with_length(unset_variables, name, l);
if (r < 0)
return r;
}
} else {
e = NULL; /* Resolve invalid variable names the same way as unset ones */
if (bad_variables) {
r = strv_extend_with_length(bad_variables, name, l);
if (r < 0)
return r;
}
}
*ret = e;
return !!e;
}
int replace_env_full(
const char *format,
size_t n,
char **env,
ReplaceEnvFlags flags,
char **ret,
char ***ret_unset_variables,
char ***ret_bad_variables) {
enum {
WORD,
CURLY,
@ -589,15 +672,22 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
ALTERNATE_VALUE,
} state = WORD;
_cleanup_strv_free_ char **unset_variables = NULL, **bad_variables = NULL;
const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */
char *k;
_cleanup_free_ char *s = NULL;
char ***pu, ***pb, *k;
size_t i, len = 0; /* len is initialized to appease gcc */
int nest = 0;
int nest = 0, r;
assert(format);
for (e = format, i = 0; *e && i < n; e ++, i ++)
if (n == SIZE_MAX)
n = strlen(format);
pu = ret_unset_variables ? &unset_variables : NULL;
pb = ret_bad_variables ? &bad_variables : NULL;
for (e = format, i = 0; *e && i < n; e++, i++)
switch (state) {
case WORD:
@ -609,27 +699,28 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
if (*e == '{') {
k = strnappend(s, word, e-word-1);
if (!k)
return NULL;
return -ENOMEM;
free_and_replace(s, k);
word = e-1;
state = VARIABLE;
nest++;
} else if (*e == '$') {
k = strnappend(s, word, e-word);
if (!k)
return NULL;
return -ENOMEM;
free_and_replace(s, k);
word = e+1;
state = WORD;
} else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
} else if (FLAGS_SET(flags, REPLACE_ENV_ALLOW_BRACELESS) && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
k = strnappend(s, word, e-word-1);
if (!k)
return NULL;
return -ENOMEM;
free_and_replace(s, k);
@ -642,12 +733,14 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
case VARIABLE:
if (*e == '}') {
const char *t;
char *t;
t = strv_env_get_n(env, word+2, e-word-2, flags);
r = strv_env_get_n_validated(env, word+2, e-word-2, flags, &t, pu, pb);
if (r < 0)
return r;
if (!strextend(&s, t))
return NULL;
return -ENOMEM;
word = e+1;
state = WORD;
@ -689,18 +782,37 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
nest--;
if (nest == 0) {
const char *t;
_cleanup_strv_free_ char **u = NULL, **b = NULL;
_cleanup_free_ char *v = NULL;
char *t = NULL;
t = strv_env_get_n(env, word+2, len, flags);
r = strv_env_get_n_validated(env, word+2, len, flags, &t, pu, pb);
if (r < 0)
return r;
if (t && state == ALTERNATE_VALUE)
t = v = replace_env_n(test_value, e-test_value, env, flags);
else if (!t && state == DEFAULT_VALUE)
t = v = replace_env_n(test_value, e-test_value, env, flags);
if (t && state == ALTERNATE_VALUE) {
r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
if (r < 0)
return r;
t = v;
} else if (!t && state == DEFAULT_VALUE) {
r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
if (r < 0)
return r;
t = v;
}
r = strv_extend_strv(&unset_variables, u, /* filter_duplicates= */ true);
if (r < 0)
return r;
r = strv_extend_strv(&bad_variables, b, /* filter_duplicates= */ true);
if (r < 0)
return r;
if (!strextend(&s, t))
return NULL;
return -ENOMEM;
word = e+1;
state = WORD;
@ -711,12 +823,14 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
if (!strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
const char *t;
char *t = NULL;
t = strv_env_get_n(env, word+1, e-word-1, flags);
r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
if (r < 0)
return r;
if (!strextend(&s, t))
return NULL;
return -ENOMEM;
word = e--;
i--;
@ -726,58 +840,83 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
}
if (state == VARIABLE_RAW) {
const char *t;
char *t;
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
t = strv_env_get_n(env, word+1, e-word-1, flags);
return strjoin(s, t);
} else
return strnappend(s, word, e-word);
r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
if (r < 0)
return r;
if (!strextend(&s, t))
return -ENOMEM;
} else if (!strextendn(&s, word, e-word))
return -ENOMEM;
if (ret_unset_variables)
*ret_unset_variables = TAKE_PTR(unset_variables);
if (ret_bad_variables)
*ret_bad_variables = TAKE_PTR(bad_variables);
if (ret)
*ret = TAKE_PTR(s);
return 0;
}
char **replace_env_argv(char **argv, char **env) {
_cleanup_strv_free_ char **ret = NULL;
int replace_env_argv(
char **argv,
char **env,
char ***ret,
char ***ret_unset_variables,
char ***ret_bad_variables) {
_cleanup_strv_free_ char **n = NULL, **unset_variables = NULL, **bad_variables = NULL;
size_t k = 0, l = 0;
int r;
l = strv_length(argv);
ret = new(char*, l+1);
if (!ret)
return NULL;
n = new(char*, l+1);
if (!n)
return -ENOMEM;
STRV_FOREACH(i, argv) {
const char *word = *i;
/* If $FOO appears as single word, replace it by the split up variable */
if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
char *e;
char **w;
if (word[0] == '$' && !IN_SET(word[1], '{', '$')) {
_cleanup_strv_free_ char **m = NULL;
const char *name = word + 1;
char *e, **w;
size_t q;
e = strv_env_get(env, *i+1);
if (e) {
int r;
r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
if (r < 0) {
ret[k] = NULL;
return NULL;
}
}
if (env_name_is_valid(name)) {
e = strv_env_get(env, name);
if (e)
r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
else if (ret_unset_variables)
r = strv_extend(&unset_variables, name);
else
r = 0;
} else if (ret_bad_variables)
r = strv_extend(&bad_variables, name);
else
r = 0;
if (r < 0)
return r;
q = strv_length(m);
l = l + q - 1;
w = reallocarray(ret, l + 1, sizeof(char *));
if (!w) {
ret[k] = NULL;
return NULL;
}
w = reallocarray(n, l + 1, sizeof(char*));
if (!w)
return -ENOMEM;
ret = w;
n = w;
if (m) {
memcpy(ret + k, m, q * sizeof(char*));
memcpy(n + k, m, (q + 1) * sizeof(char*));
m = mfree(m);
}
@ -785,15 +924,41 @@ char **replace_env_argv(char **argv, char **env) {
continue;
}
_cleanup_strv_free_ char **u = NULL, **b = NULL;
/* If ${FOO} appears as part of a word, replace it by the variable as-is */
ret[k] = replace_env(*i, env, 0);
if (!ret[k])
return NULL;
k++;
r = replace_env_full(
word,
/* length= */ SIZE_MAX,
env,
/* flags= */ 0,
n + k,
ret_unset_variables ? &u : NULL,
ret_bad_variables ? &b : NULL);
if (r < 0)
return r;
n[++k] = NULL;
r = strv_extend_strv(&unset_variables, u, /* filter_duplicates= */ true);
if (r < 0)
return r;
r = strv_extend_strv(&bad_variables, b, /*filter_duplicates= */ true);
if (r < 0)
return r;
}
ret[k] = NULL;
return TAKE_PTR(ret);
if (ret_unset_variables) {
strv_uniq(strv_sort(unset_variables));
*ret_unset_variables = TAKE_PTR(unset_variables);
}
if (ret_bad_variables) {
strv_uniq(strv_sort(bad_variables));
*ret_bad_variables = TAKE_PTR(bad_variables);
}
*ret = TAKE_PTR(n);
return 0;
}
#endif /* NM_IGNORED */
@ -853,8 +1018,8 @@ int putenv_dup(const char *assignment, bool override) {
}
int setenv_systemd_exec_pid(bool update_only) {
char str[DECIMAL_STR_MAX(pid_t)];
const char *e;
int r;
/* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */
@ -865,10 +1030,9 @@ int setenv_systemd_exec_pid(bool update_only) {
if (streq_ptr(e, "*"))
return 0;
xsprintf(str, PID_FMT, getpid_cached());
if (setenv("SYSTEMD_EXEC_PID", str, 1) < 0)
return -errno;
r = setenvf("SYSTEMD_EXEC_PID", /* overwrite= */ 1, PID_FMT, getpid_cached());
if (r < 0)
return r;
return 1;
}
@ -944,4 +1108,45 @@ int getenv_steal_erase(const char *name, char **ret) {
return 1;
}
int set_full_environment(char **env) {
int r;
clearenv();
STRV_FOREACH(e, env) {
_cleanup_free_ char *k = NULL, *v = NULL;
r = split_pair(*e, "=", &k, &v);
if (r < 0)
return r;
if (setenv(k, v, /* overwrite= */ true) < 0)
return -errno;
}
return 0;
}
int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
_cleanup_free_ char *value = NULL;
va_list ap;
int r;
assert(name);
if (!valuef)
return RET_NERRNO(unsetenv(name));
va_start(ap, valuef);
DISABLE_WARNING_FORMAT_NONLITERAL;
r = vasprintf(&value, valuef, ap);
REENABLE_WARNING;
va_end(ap);
if (r < 0)
return -ENOMEM;
return RET_NERRNO(setenv(name, value, overwrite));
}
#endif /* NM_IGNORED */

View file

@ -19,19 +19,19 @@ bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
bool env_assignment_is_valid(const char *e);
enum {
typedef enum ReplaceEnvFlags {
REPLACE_ENV_USE_ENVIRONMENT = 1 << 0,
REPLACE_ENV_ALLOW_BRACELESS = 1 << 1,
REPLACE_ENV_ALLOW_EXTENDED = 1 << 2,
};
} ReplaceEnvFlags;
char *replace_env_n(const char *format, size_t n, char **env, unsigned flags);
char **replace_env_argv(char **argv, char **env);
static inline char *replace_env(const char *format, char **env, unsigned flags) {
return replace_env_n(format, strlen(format), env, flags);
int replace_env_full(const char *format, size_t n, char **env, ReplaceEnvFlags flags, char **ret, char ***ret_unset_variables, char ***ret_bad_variables);
static inline int replace_env(const char *format, char **env, ReplaceEnvFlags flags, char **ret) {
return replace_env_full(format, SIZE_MAX, env, flags, ret, NULL, NULL);
}
int replace_env_argv(char **argv, char **env, char ***ret, char ***ret_unset_variables, char ***ret_bad_variables);
bool strv_env_is_valid(char **e);
#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
@ -49,11 +49,15 @@ int strv_env_replace_consume(char ***l, char *p); /* In place ... */
int strv_env_replace_strdup(char ***l, const char *assignment);
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment);
int strv_env_assign(char ***l, const char *key, const char *value);
int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) _printf_(3, 4);
int _strv_env_assign_many(char ***l, ...) _sentinel_;
#define strv_env_assign_many(l, ...) _strv_env_assign_many(l, __VA_ARGS__, NULL)
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
char *strv_env_get(char **x, const char *n) _pure_;
char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags);
static inline char* strv_env_get(char * const *x, const char *n) {
return strv_env_get_n(x, n, SIZE_MAX, 0);
}
char *strv_env_pairs_get(char **l, const char *name) _pure_;
int getenv_bool(const char *p);
@ -74,3 +78,7 @@ int setenv_systemd_exec_pid(bool update_only);
int getenv_path_list(const char *name, char ***ret_paths);
int getenv_steal_erase(const char *name, char **ret);
int set_full_environment(char **env);
int setenvf(const char *name, bool overwrite, const char *valuef, ...) _printf_(3,4);

View file

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
@ -73,6 +74,16 @@ static inline int RET_NERRNO(int ret) {
return ret;
}
/* Collect possible errors in <acc>, so that the first error can be returned.
* Returns (possibly updated) <acc>. */
#define RET_GATHER(acc, err) \
({ \
int *__a = &(acc), __e = (err); \
if (*__a >= 0 && __e < 0) \
*__a = __e; \
*__a; \
})
static inline int errno_or_else(int fallback) {
/* To be used when invoking library calls where errno handling is not defined clearly: we return
* errno if it is set, and the specified error otherwise. The idea is that the caller initializes
@ -84,12 +95,23 @@ static inline int errno_or_else(int fallback) {
return -abs(fallback);
}
/* abs(3) says: Trying to take the absolute value of the most negative integer is not defined. */
#define _DEFINE_ABS_WRAPPER(name) \
static inline bool ERRNO_IS_##name(intmax_t r) { \
if (r == INTMAX_MIN) \
return false; \
return ERRNO_IS_NEG_##name(-imaxabs(r)); \
}
assert_cc(INT_MAX <= INTMAX_MAX);
/* For send()/recv() or read()/write(). */
static inline bool ERRNO_IS_TRANSIENT(int r) {
return IN_SET(abs(r),
EAGAIN,
EINTR);
static inline bool ERRNO_IS_NEG_TRANSIENT(intmax_t r) {
return IN_SET(r,
-EAGAIN,
-EINTR);
}
_DEFINE_ABS_WRAPPER(TRANSIENT);
/* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5.
*
@ -98,79 +120,87 @@ static inline bool ERRNO_IS_TRANSIENT(int r) {
*
* Hint #3: When asynchronous connect() on TCP fails because the host never acknowledges a single packet,
* kernel tells us that with ETIMEDOUT, see tcp(7). */
static inline bool ERRNO_IS_DISCONNECT(int r) {
return IN_SET(abs(r),
ECONNABORTED,
ECONNREFUSED,
ECONNRESET,
EHOSTDOWN,
EHOSTUNREACH,
ENETDOWN,
ENETRESET,
ENETUNREACH,
ENONET,
ENOPROTOOPT,
ENOTCONN,
EPIPE,
EPROTO,
ESHUTDOWN,
ETIMEDOUT);
static inline bool ERRNO_IS_NEG_DISCONNECT(intmax_t r) {
return IN_SET(r,
-ECONNABORTED,
-ECONNREFUSED,
-ECONNRESET,
-EHOSTDOWN,
-EHOSTUNREACH,
-ENETDOWN,
-ENETRESET,
-ENETUNREACH,
-ENONET,
-ENOPROTOOPT,
-ENOTCONN,
-EPIPE,
-EPROTO,
-ESHUTDOWN,
-ETIMEDOUT);
}
_DEFINE_ABS_WRAPPER(DISCONNECT);
/* Transient errors we might get on accept() that we should ignore. As per error handling comment in
* the accept(2) man page. */
static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) {
return ERRNO_IS_DISCONNECT(r) ||
ERRNO_IS_TRANSIENT(r) ||
abs(r) == EOPNOTSUPP;
static inline bool ERRNO_IS_NEG_ACCEPT_AGAIN(intmax_t r) {
return ERRNO_IS_NEG_DISCONNECT(r) ||
ERRNO_IS_NEG_TRANSIENT(r) ||
r == -EOPNOTSUPP;
}
_DEFINE_ABS_WRAPPER(ACCEPT_AGAIN);
/* Resource exhaustion, could be our fault or general system trouble */
static inline bool ERRNO_IS_RESOURCE(int r) {
return IN_SET(abs(r),
EMFILE,
ENFILE,
ENOMEM);
static inline bool ERRNO_IS_NEG_RESOURCE(intmax_t r) {
return IN_SET(r,
-EMFILE,
-ENFILE,
-ENOMEM);
}
_DEFINE_ABS_WRAPPER(RESOURCE);
/* Seven different errors for "operation/system call/ioctl/socket feature not supported" */
static inline bool ERRNO_IS_NOT_SUPPORTED(int r) {
return IN_SET(abs(r),
EOPNOTSUPP,
ENOTTY,
ENOSYS,
EAFNOSUPPORT,
EPFNOSUPPORT,
EPROTONOSUPPORT,
ESOCKTNOSUPPORT);
static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
return IN_SET(r,
-EOPNOTSUPP,
-ENOTTY,
-ENOSYS,
-EAFNOSUPPORT,
-EPFNOSUPPORT,
-EPROTONOSUPPORT,
-ESOCKTNOSUPPORT);
}
_DEFINE_ABS_WRAPPER(NOT_SUPPORTED);
/* Two different errors for access problems */
static inline bool ERRNO_IS_PRIVILEGE(int r) {
return IN_SET(abs(r),
EACCES,
EPERM);
static inline bool ERRNO_IS_NEG_PRIVILEGE(intmax_t r) {
return IN_SET(r,
-EACCES,
-EPERM);
}
_DEFINE_ABS_WRAPPER(PRIVILEGE);
/* Three different errors for "not enough disk space" */
static inline bool ERRNO_IS_DISK_SPACE(int r) {
return IN_SET(abs(r),
ENOSPC,
EDQUOT,
EFBIG);
static inline bool ERRNO_IS_NEG_DISK_SPACE(intmax_t r) {
return IN_SET(r,
-ENOSPC,
-EDQUOT,
-EFBIG);
}
_DEFINE_ABS_WRAPPER(DISK_SPACE);
/* Three different errors for "this device does not quite exist" */
static inline bool ERRNO_IS_DEVICE_ABSENT(int r) {
return IN_SET(abs(r),
ENODEV,
ENXIO,
ENOENT);
static inline bool ERRNO_IS_NEG_DEVICE_ABSENT(intmax_t r) {
return IN_SET(r,
-ENODEV,
-ENXIO,
-ENOENT);
}
_DEFINE_ABS_WRAPPER(DEVICE_ABSENT);
/* Quite often we want to handle cases where the backing FS doesn't support extended attributes at all and
* where it simply doesn't have the requested xattr the same way */
static inline bool ERRNO_IS_XATTR_ABSENT(int r) {
return abs(r) == ENODATA ||
ERRNO_IS_NOT_SUPPORTED(r);
static inline bool ERRNO_IS_NEG_XATTR_ABSENT(intmax_t r) {
return r == -ENODATA ||
ERRNO_IS_NEG_NOT_SUPPORTED(r);
}
_DEFINE_ABS_WRAPPER(XATTR_ABSENT);

View file

@ -184,7 +184,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
}
case 'u': {
/* C++11 style 16bit unicode */
/* C++11 style 16-bit unicode */
int a[4];
size_t i;
@ -211,7 +211,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
}
case 'U': {
/* C++11 style 32bit unicode */
/* C++11 style 32-bit unicode */
int a[8];
size_t i;
@ -474,6 +474,33 @@ char* octescape(const char *s, size_t len) {
return buf;
}
char* decescape(const char *s, const char *bad, size_t len) {
char *buf, *t;
/* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */
assert(s || len == 0);
t = buf = new(char, len * 4 + 1);
if (!buf)
return NULL;
for (size_t i = 0; i < len; i++) {
uint8_t u = (uint8_t) s[i];
if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"') || strchr(bad, u)) {
*(t++) = '\\';
*(t++) = '0' + (u / 100);
*(t++) = '0' + ((u / 10) % 10);
*(t++) = '0' + (u % 10);
} else
*(t++) = u;
}
*t = 0;
return buf;
}
static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
assert(bad);
assert(t);

View file

@ -65,6 +65,7 @@ static inline char* xescape(const char *s, const char *bad) {
return xescape_full(s, bad, SIZE_MAX, 0);
}
char* octescape(const char *s, size_t len);
char* decescape(const char *s, const char *bad, size_t len);
char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags);
char* shell_escape(const char *s, const char *bad);

View file

@ -61,8 +61,8 @@ void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
assert(p);
assert(state);
siphash24_compress(&p->length, sizeof(p->length), state);
siphash24_compress(p->bytes, p->length, state);
siphash24_compress_typesafe(p->length, state);
siphash24_compress_safe(p->bytes, p->length, state);
}
DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
@ -108,7 +108,7 @@ int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
}
static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) {
siphash24_compress(p, sizeof(struct ether_addr), state);
siphash24_compress_typesafe(*p, state);
}
DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
@ -272,3 +272,11 @@ int parse_ether_addr(const char *s, struct ether_addr *ret) {
*ret = a.ether;
return 0;
}
void ether_addr_mark_random(struct ether_addr *addr) {
assert(addr);
/* see eth_random_addr in the kernel */
addr->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
addr->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
}

View file

@ -113,3 +113,5 @@ static inline bool ether_addr_is_global(const struct ether_addr *addr) {
extern const struct hash_ops ether_addr_hash_ops;
extern const struct hash_ops ether_addr_hash_ops_free;
void ether_addr_mark_random(struct ether_addr *addr);

View file

@ -275,11 +275,7 @@ int extract_many_words(const char **p, const char *separators, unsigned flags, .
r = extract_first_word(p, &l[c], separators, flags);
if (r < 0) {
int j;
for (j = 0; j < c; j++)
free(l[j]);
free_many_charp(l, c);
return r;
}

View file

@ -94,11 +94,25 @@ void safe_close_pair(int p[static 2]) {
p[1] = safe_close(p[1]);
}
void close_many(const int fds[], size_t n_fd) {
assert(fds || n_fd <= 0);
void close_many(const int fds[], size_t n_fds) {
assert(fds || n_fds == 0);
for (size_t i = 0; i < n_fd; i++)
safe_close(fds[i]);
FOREACH_ARRAY(fd, fds, n_fds)
safe_close(*fd);
}
void close_many_unset(int fds[], size_t n_fds) {
assert(fds || n_fds == 0);
FOREACH_ARRAY(fd, fds, n_fds)
*fd = safe_close(*fd);
}
void close_many_and_free(int *fds, size_t n_fds) {
assert(fds || n_fds == 0);
close_many(fds, n_fds);
free(fds);
}
int fclose_nointr(FILE *f) {
@ -158,6 +172,19 @@ int fd_nonblock(int fd, bool nonblock) {
return RET_NERRNO(fcntl(fd, F_SETFL, nflags));
}
int stdio_disable_nonblock(void) {
int ret = 0;
/* stdin/stdout/stderr really should have O_NONBLOCK, which would confuse apps if left on, as
* write()s might unexpectedly fail with EAGAIN. */
RET_GATHER(ret, fd_nonblock(STDIN_FILENO, false));
RET_GATHER(ret, fd_nonblock(STDOUT_FILENO, false));
RET_GATHER(ret, fd_nonblock(STDERR_FILENO, false));
return ret;
}
int fd_cloexec(int fd, bool cloexec) {
int flags, nflags;
@ -176,32 +203,32 @@ int fd_cloexec(int fd, bool cloexec) {
#if 0 /* NM_IGNORED */
int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec) {
int ret = 0, r;
int r = 0;
assert(n_fds == 0 || fds);
assert(fds || n_fds == 0);
for (size_t i = 0; i < n_fds; i++) {
if (fds[i] < 0) /* Skip gracefully over already invalidated fds */
FOREACH_ARRAY(fd, fds, n_fds) {
if (*fd < 0) /* Skip gracefully over already invalidated fds */
continue;
r = fd_cloexec(fds[i], cloexec);
if (r < 0 && ret >= 0) /* Continue going, but return first error */
ret = r;
else
ret = 1; /* report if we did anything */
RET_GATHER(r, fd_cloexec(*fd, cloexec));
if (r >= 0)
r = 1; /* report if we did anything */
}
return ret;
return r;
}
_pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
assert(n_fdset == 0 || fdset);
static bool fd_in_set(int fd, const int fds[], size_t n_fds) {
assert(fd >= 0);
assert(fds || n_fds == 0);
for (size_t i = 0; i < n_fdset; i++) {
if (fdset[i] < 0)
FOREACH_ARRAY(i, fds, n_fds) {
if (*i < 0)
continue;
if (fdset[i] == fd)
if (*i == fd)
return true;
}
@ -232,7 +259,7 @@ int get_max_fd(void) {
static int close_all_fds_frugal(const int except[], size_t n_except) {
int max_fd, r = 0;
assert(n_except == 0 || except);
assert(except || n_except == 0);
/* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so
* and hence is safe to be called in signal handler context. Most users should call close_all_fds(),
@ -247,8 +274,7 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
* spin the CPU for a long time. */
if (max_fd > MAX_FD_LOOP_LIMIT)
return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
"Refusing to loop over %d potential fds.",
max_fd);
"Refusing to loop over %d potential fds.", max_fd);
for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -EBADF) {
int q;
@ -257,8 +283,8 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
continue;
q = close_nointr(fd);
if (q < 0 && q != -EBADF && r >= 0)
r = q;
if (q != -EBADF)
RET_GATHER(r, q);
}
return r;
@ -589,7 +615,7 @@ int move_fd(int from, int to, int cloexec) {
if (fl < 0)
return -errno;
cloexec = !!(fl & FD_CLOEXEC);
cloexec = FLAGS_SET(fl, FD_CLOEXEC);
}
r = dup3(from, to, cloexec ? O_CLOEXEC : 0);
@ -647,7 +673,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
original_output_fd,
original_error_fd },
null_fd = -EBADF, /* If we open /dev/null, we store the fd to it here */
copy_fd[3] = { -EBADF, -EBADF, -EBADF }, /* This contains all fds we duplicate here
copy_fd[3] = EBADF_TRIPLET, /* This contains all fds we duplicate here
* temporarily, and hence need to close at the end. */
r;
bool null_readable, null_writable;
@ -745,8 +771,7 @@ finish:
safe_close_above_stdio(original_error_fd);
/* Close the copies we moved > 2 */
for (int i = 0; i < 3; i++)
safe_close(copy_fd[i]);
close_many(copy_fd, 3);
/* Close our null fd, if it's > 2 */
safe_close_above_stdio(null_fd);
@ -756,9 +781,10 @@ finish:
#endif /* NM_IGNORED */
int fd_reopen(int fd, int flags) {
int new_fd, r;
int r;
assert(fd >= 0 || fd == AT_FDCWD);
assert(!FLAGS_SET(flags, O_CREAT));
/* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
* turn O_RDWR fds into O_RDONLY fds.
@ -782,19 +808,12 @@ int fd_reopen(int fd, int flags) {
* the same way as the non-O_DIRECTORY case. */
return -ELOOP;
if (FLAGS_SET(flags, O_DIRECTORY) || fd == AT_FDCWD) {
if (FLAGS_SET(flags, O_DIRECTORY) || fd == AT_FDCWD)
/* If we shall reopen the fd as directory we can just go via "." and thus bypass the whole
* magic /proc/ directory, and make ourselves independent of that being mounted. */
new_fd = openat(fd, ".", flags | O_DIRECTORY);
if (new_fd < 0)
return -errno;
return RET_NERRNO(openat(fd, ".", flags | O_DIRECTORY));
return new_fd;
}
assert(fd >= 0);
new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
int new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
if (new_fd < 0) {
if (errno != ENOENT)
return -errno;
@ -811,7 +830,6 @@ int fd_reopen(int fd, int flags) {
return new_fd;
}
#if 0 /* NM_IGNORED */
int fd_reopen_condition(
int fd,
int flags,
@ -821,6 +839,7 @@ int fd_reopen_condition(
int r, new_fd;
assert(fd >= 0);
assert(!FLAGS_SET(flags, O_CREAT));
/* Invokes fd_reopen(fd, flags), but only if the existing F_GETFL flags don't match the specified
* flags (masked by the specified mask). This is useful for converting O_PATH fds into real fds if
@ -843,6 +862,7 @@ int fd_reopen_condition(
return new_fd;
}
#if 0 /* NM_IGNORED */
int fd_is_opath(int fd) {
int r;
@ -901,34 +921,21 @@ int fd_get_diskseq(int fd, uint64_t *ret) {
}
int path_is_root_at(int dir_fd, const char *path) {
STRUCT_NEW_STATX_DEFINE(st);
STRUCT_NEW_STATX_DEFINE(pst);
_cleanup_close_ int fd = -EBADF;
int r;
_cleanup_close_ int fd = -EBADF, pfd = -EBADF;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
if (!isempty(path)) {
fd = openat(dir_fd, path, O_PATH|O_CLOEXEC);
fd = openat(dir_fd, path, O_PATH|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
return -errno;
return errno == ENOTDIR ? false : -errno;
dir_fd = fd;
}
r = statx_fallback(dir_fd, ".", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st.sx);
if (r == -ENOTDIR)
return false;
if (r < 0)
return r;
r = statx_fallback(dir_fd, "..", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &pst.sx);
if (r < 0)
return r;
/* First, compare inode. If these are different, the fd does not point to the root directory "/". */
if (!statx_inode_same(&st.sx, &pst.sx))
return false;
pfd = openat(dir_fd, "..", O_PATH|O_DIRECTORY|O_CLOEXEC);
if (pfd < 0)
return errno == ENOTDIR ? false : -errno;
/* Even if the parent directory has the same inode, the fd may not point to the root directory "/",
* and we also need to check that the mount ids are the same. Otherwise, a construct like the
@ -936,40 +943,64 @@ int path_is_root_at(int dir_fd, const char *path) {
*
* $ mkdir /tmp/x /tmp/x/y
* $ mount --bind /tmp/x /tmp/x/y
*
* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old
* kernel is used without /proc mounted. In that case, let's assume that we do not have such spurious
* mount points in an early boot stage, and silently skip the following check. */
*/
if (!FLAGS_SET(st.nsx.stx_mask, STATX_MNT_ID)) {
return fds_are_same_mount(dir_fd, pfd);
}
int fds_are_same_mount(int fd1, int fd2) {
STRUCT_NEW_STATX_DEFINE(st1);
STRUCT_NEW_STATX_DEFINE(st2);
int r;
assert(fd1 >= 0);
assert(fd2 >= 0);
r = statx_fallback(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st1.sx);
if (r < 0)
return r;
r = statx_fallback(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st2.sx);
if (r < 0)
return r;
/* First, compare inode. If these are different, the fd does not point to the root directory "/". */
if (!statx_inode_same(&st1.sx, &st2.sx))
return false;
/* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old
* kernel is used. In that case, let's assume that we do not have such spurious mount points in an
* early boot stage, and silently skip the following check. */
if (!FLAGS_SET(st1.nsx.stx_mask, STATX_MNT_ID)) {
int mntid;
r = path_get_mnt_id_at(dir_fd, "", &mntid);
if (r == -ENOSYS)
r = path_get_mnt_id_at_fallback(fd1, "", &mntid);
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return true; /* skip the mount ID check */
if (r < 0)
return r;
assert(mntid >= 0);
st.nsx.stx_mnt_id = mntid;
st.nsx.stx_mask |= STATX_MNT_ID;
st1.nsx.stx_mnt_id = mntid;
st1.nsx.stx_mask |= STATX_MNT_ID;
}
if (!FLAGS_SET(pst.nsx.stx_mask, STATX_MNT_ID)) {
if (!FLAGS_SET(st2.nsx.stx_mask, STATX_MNT_ID)) {
int mntid;
r = path_get_mnt_id_at(dir_fd, "..", &mntid);
if (r == -ENOSYS)
r = path_get_mnt_id_at_fallback(fd2, "", &mntid);
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return true; /* skip the mount ID check */
if (r < 0)
return r;
assert(mntid >= 0);
pst.nsx.stx_mnt_id = mntid;
pst.nsx.stx_mask |= STATX_MNT_ID;
st2.nsx.stx_mnt_id = mntid;
st2.nsx.stx_mask |= STATX_MNT_ID;
}
return statx_mount_same(&st.nsx, &pst.nsx);
return statx_mount_same(&st1.nsx, &st2.nsx);
}
const char *accmode_to_string(int flags) {
@ -984,4 +1015,12 @@ const char *accmode_to_string(int flags) {
return NULL;
}
}
char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, int fd) {
assert(buf);
assert(fd >= 0);
assert(pid >= 0);
assert_se(snprintf_ok(buf, PROC_PID_FD_PATH_MAX, "/proc/" PID_FMT "/fd/%i", pid == 0 ? getpid_cached() : pid, fd));
return buf;
}
#endif /* NM_IGNORED */

View file

@ -16,7 +16,10 @@
/* Make sure we can distinguish fd 0 and NULL */
#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
#define PIPE_EBADF { -EBADF, -EBADF }
/* Useful helpers for initializing pipe(), socketpair() or stdio fd arrays */
#define EBADF_PAIR { -EBADF, -EBADF }
#define EBADF_TRIPLET { -EBADF, -EBADF, -EBADF }
int close_nointr(int fd);
int safe_close(int fd);
@ -29,7 +32,9 @@ static inline int safe_close_above_stdio(int fd) {
return safe_close(fd);
}
void close_many(const int fds[], size_t n_fd);
void close_many(const int fds[], size_t n_fds);
void close_many_unset(int fds[], size_t n_fds);
void close_many_and_free(int *fds, size_t n_fds);
int fclose_nointr(FILE *f);
FILE* safe_fclose(FILE *f);
@ -47,6 +52,11 @@ static inline void fclosep(FILE **f) {
safe_fclose(*f);
}
static inline void* close_fd_ptr(void *p) {
safe_close(PTR_TO_FD(p));
return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, pclose, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
@ -57,6 +67,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
#define _cleanup_close_pair_ _cleanup_(close_pairp)
int fd_nonblock(int fd, bool nonblock);
int stdio_disable_nonblock(void);
int fd_cloexec(int fd, bool cloexec);
int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec);
@ -102,6 +114,9 @@ int read_nr_open(void);
int fd_get_diskseq(int fd, uint64_t *ret);
int path_is_root_at(int dir_fd, const char *path);
static inline int path_is_root(const char *path) {
return path_is_root_at(AT_FDCWD, path);
}
static inline int dir_fd_is_root(int dir_fd) {
return path_is_root_at(dir_fd, NULL);
}
@ -109,6 +124,8 @@ static inline int dir_fd_is_root_or_cwd(int dir_fd) {
return dir_fd == AT_FDCWD ? true : path_is_root_at(dir_fd, NULL);
}
int fds_are_same_mount(int fd1, int fd2);
/* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
#define PROC_FD_PATH_MAX \
(STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int))
@ -123,6 +140,16 @@ static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int f
#define FORMAT_PROC_FD_PATH(fd) \
format_proc_fd_path((char[PROC_FD_PATH_MAX]) {}, (fd))
/* The maximum length a buffer for a /proc/<pid>/fd/<fd> path needs */
#define PROC_PID_FD_PATH_MAX \
(STRLEN("/proc//fd/") + DECIMAL_STR_MAX(pid_t) + DECIMAL_STR_MAX(int))
char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, int fd);
/* Kinda the same as FORMAT_PROC_FD_PATH(), but goes by PID rather than "self" symlink */
#define FORMAT_PROC_PID_FD_PATH(pid, fd) \
format_proc_pid_fd_path((char[PROC_PID_FD_PATH_MAX]) {}, (pid), (fd))
const char *accmode_to_string(int flags);
/* Like ASSERT_PTR, but for fds */

View file

@ -30,10 +30,13 @@
#include "stdio-util.h"
#include "string-util.h"
#include "sync-util.h"
#include "terminal-util.h"
#include "tmpfile-util.h"
/* The maximum size of the file we'll read in one go in read_full_file() (64M). */
#define READ_FULL_BYTES_MAX (64U*1024U*1024U - 1U)
/* Used when a size is specified for read_full_file() with READ_FULL_FILE_UNBASE64 or _UNHEX */
#define READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY 3
/* The maximum size of virtual files (i.e. procfs, sysfs, and other virtual "API" files) we'll read in one go
* in read_virtual_file(). Note that this limit is different (and much lower) than the READ_FULL_BYTES_MAX
@ -200,6 +203,19 @@ int write_string_stream_ts(
return 0;
}
static mode_t write_string_file_flags_to_mode(WriteStringFileFlags flags) {
/* We support three different modes, that are the ones that really make sense for text files like this:
*
* 0600 (i.e. root-only)
* 0444 (i.e. read-only)
* 0644 (i.e. writable for root, readable for everyone else)
*/
return FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 :
FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0444) ? 0444 : 0644;
}
static int write_string_file_atomic_at(
int dir_fd,
const char *fn,
@ -225,7 +241,7 @@ static int write_string_file_atomic_at(
if (r < 0)
goto fail;
r = fchmod_umask(fileno(f), FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0644);
r = fchmod_umask(fileno(f), write_string_file_flags_to_mode(flags));
if (r < 0)
goto fail;
@ -288,7 +304,7 @@ int write_string_file_ts_at(
(FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
(FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
write_string_file_flags_to_mode(flags));
if (fd < 0) {
r = -errno;
goto fail;
@ -576,7 +592,7 @@ int read_full_stream_full(
size_t *ret_size) {
_cleanup_free_ char *buf = NULL;
size_t n, n_next = 0, l;
size_t n, n_next = 0, l, expected_decoded_size = size;
int fd, r;
assert(f);
@ -587,6 +603,13 @@ int read_full_stream_full(
if (offset != UINT64_MAX && offset > LONG_MAX) /* fseek() can only deal with "long" offsets */
return -ERANGE;
if ((flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) != 0) {
if (size <= SIZE_MAX / READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY)
size *= READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY;
else
size = SIZE_MAX;
}
fd = fileno(f);
if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see
* fmemopen()), let's optimize our buffering */
@ -711,6 +734,11 @@ int read_full_stream_full(
explicit_bzero_safe(buf, n);
free_and_replace(buf, decoded);
n = l = decoded_size;
if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && l > expected_decoded_size) {
r = -E2BIG;
goto finalize;
}
}
if (!ret_size) {
@ -1057,7 +1085,9 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) {
if (mode_flags < 0)
return mode_flags;
copy_fd = fd_reopen(fd, mode_flags);
/* Flags returned by fopen_mode_to_flags might contain O_CREAT, but it doesn't make sense for fd_reopen
* since we're working on an existing fd anyway. Let's drop it here to avoid triggering assertion. */
copy_fd = fd_reopen(fd, mode_flags & ~O_CREAT);
if (copy_fd < 0)
return copy_fd;
@ -1069,123 +1099,171 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) {
return 0;
}
static int search_and_fopen_internal(
static int search_and_open_internal(
const char *path,
const char *mode,
int mode, /* if ret_fd is NULL this is an [FRWX]_OK mode for access(), otherwise an open mode for open() */
const char *root,
char **search,
FILE **ret,
int *ret_fd,
char **ret_path) {
int r;
assert(!ret_fd || !FLAGS_SET(mode, O_CREAT)); /* We don't support O_CREAT for this */
assert(path);
assert(mode);
assert(ret);
if (path_is_absolute(path)) {
_cleanup_close_ int fd = -EBADF;
if (ret_fd)
/* We only specify 0777 here to appease static analyzers, it's never used since we
* don't support O_CREAT here */
r = fd = RET_NERRNO(open(path, mode, 0777));
else
r = RET_NERRNO(access(path, mode));
if (r < 0)
return r;
if (ret_path) {
r = path_simplify_alloc(path, ret_path);
if (r < 0)
return r;
}
if (ret_fd)
*ret_fd = TAKE_FD(fd);
return 0;
}
if (!path_strv_resolve_uniq(search, root))
return -ENOMEM;
STRV_FOREACH(i, search) {
_cleanup_close_ int fd = -EBADF;
_cleanup_free_ char *p = NULL;
FILE *f;
p = path_join(root, *i, path);
if (!p)
return -ENOMEM;
f = fopen(p, mode);
if (f) {
if (ret_fd)
/* as above, 0777 is static analyzer appeasement */
r = fd = RET_NERRNO(open(p, mode, 0777));
else
r = RET_NERRNO(access(p, F_OK));
if (r >= 0) {
if (ret_path)
*ret_path = path_simplify(TAKE_PTR(p));
*ret = f;
if (ret_fd)
*ret_fd = TAKE_FD(fd);
return 0;
}
if (errno != ENOENT)
return -errno;
if (r != -ENOENT)
return r;
}
return -ENOENT;
}
int search_and_fopen(
const char *filename,
const char *mode,
int search_and_open(
const char *path,
int mode,
const char *root,
const char **search,
FILE **ret,
char **search,
int *ret_fd,
char **ret_path) {
_cleanup_strv_free_ char **copy = NULL;
assert(filename);
assert(mode);
assert(ret);
if (path_is_absolute(filename)) {
_cleanup_fclose_ FILE *f = NULL;
f = fopen(filename, mode);
if (!f)
return -errno;
if (ret_path) {
char *p;
p = strdup(filename);
if (!p)
return -ENOMEM;
*ret_path = path_simplify(p);
}
*ret = TAKE_PTR(f);
return 0;
}
assert(path);
copy = strv_copy((char**) search);
if (!copy)
return -ENOMEM;
return search_and_fopen_internal(filename, mode, root, copy, ret, ret_path);
return search_and_open_internal(path, mode, root, copy, ret_fd, ret_path);
}
int search_and_fopen_nulstr(
const char *filename,
static int search_and_fopen_internal(
const char *path,
const char *mode,
const char *root,
const char *search,
FILE **ret,
char **search,
FILE **ret_file,
char **ret_path) {
_cleanup_strv_free_ char **s = NULL;
_cleanup_free_ char *found_path = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
if (path_is_absolute(filename)) {
_cleanup_fclose_ FILE *f = NULL;
assert(path);
assert(mode || !ret_file);
f = fopen(filename, mode);
r = search_and_open(
path,
mode ? fopen_mode_to_flags(mode) : 0,
root,
search,
ret_file ? &fd : NULL,
ret_path ? &found_path : NULL);
if (r < 0)
return r;
if (ret_file) {
FILE *f = take_fdopen(&fd, mode);
if (!f)
return -errno;
if (ret_path) {
char *p;
p = strdup(filename);
if (!p)
return -ENOMEM;
*ret_path = path_simplify(p);
}
*ret = TAKE_PTR(f);
return 0;
*ret_file = f;
}
s = strv_split_nulstr(search);
if (!s)
if (ret_path)
*ret_path = TAKE_PTR(found_path);
return 0;
}
int search_and_fopen(
const char *path,
const char *mode,
const char *root,
const char **search,
FILE **ret_file,
char **ret_path) {
_cleanup_strv_free_ char **copy = NULL;
assert(path);
assert(mode || !ret_file);
copy = strv_copy((char**) search);
if (!copy)
return -ENOMEM;
return search_and_fopen_internal(filename, mode, root, s, ret, ret_path);
return search_and_fopen_internal(path, mode, root, copy, ret_file, ret_path);
}
int search_and_fopen_nulstr(
const char *path,
const char *mode,
const char *root,
const char *search,
FILE **ret_file,
char **ret_path) {
_cleanup_strv_free_ char **l = NULL;
assert(path);
assert(mode || !ret_file);
l = strv_split_nulstr(search);
if (!l)
return -ENOMEM;
return search_and_fopen_internal(path, mode, root, l, ret_file, ret_path);
}
#endif /* NM_IGNORED */
@ -1259,33 +1337,31 @@ int read_timestamp_file(const char *fn, usec_t *ret) {
}
#endif /* NM_IGNORED */
int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) {
int r;
int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *space) {
assert(s);
assert(space);
/* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter
* when specified shall initially point to a boolean variable initialized to false. It is set to true after the
* first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each
* element, but not before the first one. */
/* Outputs the specified string with fputs(), but optionally prefixes it with a separator.
* The *space parameter when specified shall initially point to a boolean variable initialized
* to false. It is set to true after the first invocation. This call is supposed to be use in loops,
* where a separator shall be inserted between each element, but not before the first one. */
if (!f)
f = stdout;
if (space) {
if (!separator)
separator = " ";
if (!separator)
separator = " ";
if (*space) {
r = fputs(separator, f);
if (r < 0)
return r;
}
if (*space)
if (fputs(separator, f) < 0)
return -EIO;
*space = true;
}
*space = true;
return fputs(s, f);
if (fputs(s, f) < 0)
return -EIO;
return 0;
}
#if 0 /* NM_IGNORED */
@ -1406,7 +1482,7 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
* and don't call isatty() on an invalid fd */
flags |= READ_LINE_NOT_A_TTY;
else
flags |= isatty(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
flags |= isatty_safe(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
}
if (FLAGS_SET(flags, READ_LINE_IS_A_TTY))
break;
@ -1437,6 +1513,36 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
return (int) count;
}
int read_stripped_line(FILE *f, size_t limit, char **ret) {
_cleanup_free_ char *s = NULL;
int r;
assert(f);
r = read_line(f, limit, ret ? &s : NULL);
if (r < 0)
return r;
if (ret) {
const char *p;
p = strstrip(s);
if (p == s)
*ret = TAKE_PTR(s);
else {
char *copy;
copy = strdup(p);
if (!copy)
return -ENOMEM;
*ret = copy;
}
}
return r;
}
int safe_fgetc(FILE *f, char *ret) {
int k;

View file

@ -26,7 +26,8 @@ typedef enum {
WRITE_STRING_FILE_NOFOLLOW = 1 << 8,
WRITE_STRING_FILE_MKDIR_0755 = 1 << 9,
WRITE_STRING_FILE_MODE_0600 = 1 << 10,
WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 11,
WRITE_STRING_FILE_MODE_0444 = 1 << 11,
WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 12,
/* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
@ -129,8 +130,12 @@ static inline int fopen_unlocked(const char *path, const char *mode, FILE **ret)
int fdopen_independent(int fd, const char *mode, FILE **ret);
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret, char **ret_path);
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret, char **ret_path);
int search_and_open(const char *path, int mode, const char *root, char **search, int *ret_fd, char **ret_path);
static inline int search_and_access(const char *path, int mode, const char *root, char**search, char **ret_path) {
return search_and_open(path, mode, root, search, NULL, ret_path);
}
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret_file, char **ret_path);
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret_file, char **ret_path);
int fflush_and_check(FILE *f);
int fflush_sync_and_check(FILE *f);
@ -138,7 +143,7 @@ int fflush_sync_and_check(FILE *f);
int write_timestamp_file_atomic(const char *fn, usec_t n);
int read_timestamp_file(const char *fn, usec_t *ret);
int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *space);
typedef enum ReadLineFlags {
READ_LINE_ONLY_NUL = 1 << 0,
@ -162,6 +167,8 @@ static inline int read_nul_string(FILE *f, size_t limit, char **ret) {
return read_line_full(f, limit, READ_LINE_ONLY_NUL, ret);
}
int read_stripped_line(FILE *f, size_t limit, char **ret);
int safe_fgetc(FILE *f, char *ret);
int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line);

View file

@ -11,6 +11,7 @@
#include <unistd.h>
#include "alloc-util.h"
#include "btrfs.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
@ -292,8 +293,22 @@ int fchmod_umask(int fd, mode_t m) {
int fchmod_opath(int fd, mode_t m) {
/* This function operates also on fd that might have been opened with
* O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like
* fchownat() does. */
* O_PATH. The tool set we have is non-intuitive:
* - fchmod(2) only operates on open files (i. e., fds with an open file description);
* - fchmodat(2) does not have a flag arg like fchownat(2) does, so no way to pass AT_EMPTY_PATH;
* + it should not be confused with the libc fchmodat(3) interface, which adds 4th flag argument,
* but does not support AT_EMPTY_PATH (only supports AT_SYMLINK_NOFOLLOW);
* - fchmodat2(2) supports all the AT_* flags, but is still very recent.
*
* We try to use fchmodat2(), and, if it is not supported, resort
* to the /proc/self/fd dance. */
assert(fd >= 0);
if (fchmodat2(fd, "", m, AT_EMPTY_PATH) >= 0)
return 0;
if (!IN_SET(errno, ENOSYS, EPERM)) /* Some container managers block unknown syscalls with EPERM */
return -errno;
if (chmod(FORMAT_PROC_FD_PATH(fd), m) < 0) {
if (errno != ENOENT)
@ -1108,6 +1123,16 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
/* This is like openat(), but has a few tricks up its sleeves, extending behaviour:
*
* O_DIRECTORY|O_CREAT is supported, which causes a directory to be created, and immediately
* opened. When used with the XO_SUBVOLUME flag this will even create a btrfs subvolume.
*
* If O_CREAT is used with XO_LABEL, any created file will be immediately relabelled.
*
* If the path is specified NULL or empty, behaves like fd_reopen().
*/
if (isempty(path)) {
assert(!FLAGS_SET(open_flags, O_CREAT|O_EXCL));
return fd_reopen(dir_fd, open_flags & ~O_NOFOLLOW);
@ -1120,7 +1145,10 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags
}
if (FLAGS_SET(open_flags, O_DIRECTORY|O_CREAT)) {
r = RET_NERRNO(mkdirat(dir_fd, path, mode));
if (FLAGS_SET(xopen_flags, XO_SUBVOLUME))
r = btrfs_subvol_make_fallback(dir_fd, path, mode);
else
r = RET_NERRNO(mkdirat(dir_fd, path, mode));
if (r == -EEXIST) {
if (FLAGS_SET(open_flags, O_EXCL))
return -EEXIST;
@ -1183,12 +1211,11 @@ int xopenat_lock(
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(path);
assert(IN_SET(operation & ~LOCK_NB, LOCK_EX, LOCK_SH));
/* POSIX/UNPOSIX locks don't work on directories (errno is set to -EBADF so let's return early with
* the same error here). */
if (FLAGS_SET(open_flags, O_DIRECTORY) && locktype != LOCK_BSD)
if (FLAGS_SET(open_flags, O_DIRECTORY) && !IN_SET(locktype, LOCK_BSD, LOCK_NONE))
return -EBADF;
for (;;) {

View file

@ -133,7 +133,8 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode);
int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created);
typedef enum XOpenFlags {
XO_LABEL = 1 << 0,
XO_LABEL = 1 << 0,
XO_SUBVOLUME = 1 << 1,
} XOpenFlags;
int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode);

View file

@ -25,7 +25,7 @@ bool emoji_enabled(void) {
return cached_emoji_enabled;
}
const char *special_glyph(SpecialGlyph code) {
const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
/* A list of a number of interesting unicode glyphs we can use to decorate our output. It's probably wise to be
* conservative here, and primarily stick to the glyphs defined in the eurlatgr font, so that display still
@ -54,11 +54,12 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_CROSS_MARK] = "-",
[SPECIAL_GLYPH_LIGHT_SHADE] = "-",
[SPECIAL_GLYPH_DARK_SHADE] = "X",
[SPECIAL_GLYPH_FULL_BLOCK] = "#",
[SPECIAL_GLYPH_SIGMA] = "S",
[SPECIAL_GLYPH_ARROW_LEFT] = "<-",
[SPECIAL_GLYPH_ARROW_RIGHT] = "->",
[SPECIAL_GLYPH_ARROW_UP] = "^",
[SPECIAL_GLYPH_ARROW_DOWN] = "v",
[SPECIAL_GLYPH_ARROW_LEFT] = "<-",
[SPECIAL_GLYPH_ARROW_RIGHT] = "->",
[SPECIAL_GLYPH_ELLIPSIS] = "...",
[SPECIAL_GLYPH_EXTERNAL_LINK] = "[LNK]",
[SPECIAL_GLYPH_ECSTATIC_SMILEY] = ":-]",
@ -73,6 +74,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_RECYCLING] = "~",
[SPECIAL_GLYPH_DOWNLOAD] = "\\",
[SPECIAL_GLYPH_SPARKLES] = "*",
[SPECIAL_GLYPH_LOW_BATTERY] = "!",
[SPECIAL_GLYPH_WARNING_SIGN] = "!",
},
@ -98,6 +100,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_CROSS_MARK] = u8"", /* actually called: BALLOT X */
[SPECIAL_GLYPH_LIGHT_SHADE] = u8"",
[SPECIAL_GLYPH_DARK_SHADE] = u8"",
[SPECIAL_GLYPH_FULL_BLOCK] = u8"",
[SPECIAL_GLYPH_SIGMA] = u8"Σ",
[SPECIAL_GLYPH_ARROW_UP] = u8"", /* actually called: UPWARDS ARROW */
[SPECIAL_GLYPH_ARROW_DOWN] = u8"", /* actually called: DOWNWARDS ARROW */
@ -131,7 +134,10 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_RECYCLING] = u8"♻️", /* actually called: UNIVERSAL RECYCLNG SYMBOL */
[SPECIAL_GLYPH_DOWNLOAD] = u8"⤵️", /* actually called: RIGHT ARROW CURVING DOWN */
[SPECIAL_GLYPH_SPARKLES] = u8"",
[SPECIAL_GLYPH_LOW_BATTERY] = u8"🪫",
[SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️",
[SPECIAL_GLYPH_COMPUTER_DISK] = u8"💽",
[SPECIAL_GLYPH_WORLD] = u8"🌍",
},
};
@ -139,5 +145,5 @@ const char *special_glyph(SpecialGlyph code) {
return NULL;
assert(code < _SPECIAL_GLYPH_MAX);
return draw_table[code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8()][code];
return draw_table[force_utf || (code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8())][code];
}

View file

@ -22,14 +22,15 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_MU,
SPECIAL_GLYPH_CHECK_MARK,
SPECIAL_GLYPH_CROSS_MARK,
SPECIAL_GLYPH_ARROW_LEFT,
SPECIAL_GLYPH_ARROW_RIGHT,
SPECIAL_GLYPH_ARROW_UP,
SPECIAL_GLYPH_ARROW_DOWN,
SPECIAL_GLYPH_ELLIPSIS,
SPECIAL_GLYPH_LIGHT_SHADE,
SPECIAL_GLYPH_DARK_SHADE,
SPECIAL_GLYPH_FULL_BLOCK,
SPECIAL_GLYPH_SIGMA,
SPECIAL_GLYPH_ARROW_UP,
SPECIAL_GLYPH_ARROW_DOWN,
SPECIAL_GLYPH_ARROW_LEFT,
SPECIAL_GLYPH_ARROW_RIGHT,
SPECIAL_GLYPH_ELLIPSIS,
SPECIAL_GLYPH_EXTERNAL_LINK,
_SPECIAL_GLYPH_FIRST_EMOJI,
SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_EMOJI,
@ -44,15 +45,22 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_RECYCLING,
SPECIAL_GLYPH_DOWNLOAD,
SPECIAL_GLYPH_SPARKLES,
SPECIAL_GLYPH_LOW_BATTERY,
SPECIAL_GLYPH_WARNING_SIGN,
SPECIAL_GLYPH_COMPUTER_DISK,
SPECIAL_GLYPH_WORLD,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;
const char *special_glyph(SpecialGlyph code) _const_;
bool emoji_enabled(void);
const char *special_glyph_full(SpecialGlyph code, bool force_utf) _const_;
static inline const char *special_glyph(SpecialGlyph code) {
return special_glyph_full(code, false);
}
static inline const char *special_glyph_check_mark(bool b) {
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK);
}

View file

@ -36,7 +36,7 @@ void path_hash_func(const char *q, struct siphash *state) {
/* if path is absolute, add one "/" to the hash. */
if (path_is_absolute(q))
siphash24_compress("/", 1, state);
siphash24_compress_byte('/', state);
for (;;) {
const char *e;
@ -71,7 +71,7 @@ DEFINE_HASH_OPS_FULL(path_hash_ops_free_free,
#endif /* NM_IGNORED */
void trivial_hash_func(const void *p, struct siphash *state) {
siphash24_compress(&p, sizeof(p), state);
siphash24_compress_typesafe(p, state);
}
int trivial_compare_func(const void *a, const void *b) {
@ -97,7 +97,7 @@ const struct hash_ops trivial_hash_ops_free_free = {
};
void uint64_hash_func(const uint64_t *p, struct siphash *state) {
siphash24_compress(p, sizeof(uint64_t), state);
siphash24_compress_typesafe(*p, state);
}
int uint64_compare_func(const uint64_t *a, const uint64_t *b) {
@ -109,7 +109,7 @@ DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func
#if 0 /* NM_IGNORED */
#if SIZEOF_DEV_T != 8
void devt_hash_func(const dev_t *p, struct siphash *state) {
siphash24_compress(p, sizeof(dev_t), state);
siphash24_compress_typesafe(*p, state);
}
#endif

View file

@ -93,14 +93,14 @@ extern const struct hash_ops trivial_hash_ops;
extern const struct hash_ops trivial_hash_ops_free;
extern const struct hash_ops trivial_hash_ops_free_free;
/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit
/* 32-bit values we can always just embed in the pointer itself, but in order to support 32-bit archs we need store 64-bit
* values indirectly, since they don't fit in a pointer. */
void uint64_hash_func(const uint64_t *p, struct siphash *state);
int uint64_compare_func(const uint64_t *a, const uint64_t *b) _pure_;
extern const struct hash_ops uint64_hash_ops;
/* On some archs dev_t is 32bit, and on others 64bit. And sometimes it's 64bit on 32bit archs, and sometimes 32bit on
* 64bit archs. Yuck! */
/* On some archs dev_t is 32-bit, and on others 64-bit. And sometimes it's 64-bit on 32-bit archs, and sometimes 32-bit on
* 64-bit archs. Yuck! */
#if SIZEOF_DEV_T != 8
void devt_hash_func(const dev_t *p, struct siphash *state);
#else

View file

@ -23,6 +23,7 @@
#include "random-util.h"
#include "set.h"
#include "siphash24.h"
#include "sort-util.h"
#include "string-util.h"
#include "strv.h"
@ -176,9 +177,9 @@ struct _packed_ indirect_storage {
};
struct direct_storage {
/* This gives us 39 bytes on 64bit, or 35 bytes on 32bit.
* That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit,
* or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */
/* This gives us 39 bytes on 64-bit, or 35 bytes on 32-bit.
* That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64-bit,
* or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32-bit. */
uint8_t storage[sizeof(struct indirect_storage)];
};
@ -2112,3 +2113,54 @@ bool set_fnmatch(Set *include_patterns, Set *exclude_patterns, const char *needl
return set_fnmatch_one(include_patterns, needle);
}
static int hashmap_entry_compare(
struct hashmap_base_entry * const *a,
struct hashmap_base_entry * const *b,
compare_func_t compare) {
assert(a && *a);
assert(b && *b);
assert(compare);
return compare((*a)->key, (*b)->key);
}
int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
_cleanup_free_ struct hashmap_base_entry **entries = NULL;
Iterator iter;
unsigned idx;
size_t n = 0;
assert(ret);
if (_hashmap_size(h) == 0) {
*ret = NULL;
if (ret_n)
*ret_n = 0;
return 0;
}
/* We append one more element than needed so that the resulting array can be used as a strv. We
* don't count this entry in the returned size. */
entries = new(struct hashmap_base_entry*, _hashmap_size(h) + 1);
if (!entries)
return -ENOMEM;
HASHMAP_FOREACH_IDX(idx, h, iter)
entries[n++] = bucket_at(h, idx);
assert(n == _hashmap_size(h));
entries[n] = NULL;
typesafe_qsort_r(entries, n, hashmap_entry_compare, h->hash_ops->compare);
/* Reuse the array. */
FOREACH_ARRAY(e, entries, n)
*e = entry_value(h, *e);
*ret = (void**) TAKE_PTR(entries);
if (ret_n)
*ret_n = n;
return 0;
}

View file

@ -398,12 +398,28 @@ static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) {
return _hashmap_get_strv(HASHMAP_BASE(h));
}
int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n);
static inline int hashmap_dump_sorted(Hashmap *h, void ***ret, size_t *ret_n) {
return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
}
static inline int ordered_hashmap_dump_sorted(OrderedHashmap *h, void ***ret, size_t *ret_n) {
return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
}
static inline int set_dump_sorted(Set *h, void ***ret, size_t *ret_n) {
return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
}
/*
* Hashmaps are iterated in unpredictable order.
* OrderedHashmaps are an exception to this. They are iterated in the order
* the entries were inserted.
* It is safe to remove the current entry.
*/
#define _HASHMAP_BASE_FOREACH(e, h, i) \
for (Iterator i = ITERATOR_FIRST; _hashmap_iterate((h), &i, (void**)&(e), NULL); )
#define HASHMAP_BASE_FOREACH(e, h) \
_HASHMAP_BASE_FOREACH(e, h, UNIQ_T(i, UNIQ))
#define _HASHMAP_FOREACH(e, h, i) \
for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), NULL); )
#define HASHMAP_FOREACH(e, h) \
@ -414,6 +430,11 @@ static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) {
#define ORDERED_HASHMAP_FOREACH(e, h) \
_ORDERED_HASHMAP_FOREACH(e, h, UNIQ_T(i, UNIQ))
#define _HASHMAP_BASE_FOREACH_KEY(e, k, h, i) \
for (Iterator i = ITERATOR_FIRST; _hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); )
#define HASHMAP_BASE_FOREACH_KEY(e, k, h) \
_HASHMAP_BASE_FOREACH_KEY(e, k, h, UNIQ_T(i, UNIQ))
#define _HASHMAP_FOREACH_KEY(e, k, h, i) \
for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); )
#define HASHMAP_FOREACH_KEY(e, k, h) \

View file

@ -116,7 +116,7 @@ int unhexmem_full(
const char *p,
size_t l,
bool secure,
void **ret,
void **ret_data,
size_t *ret_len) {
_cleanup_free_ uint8_t *buf = NULL;
@ -157,8 +157,8 @@ int unhexmem_full(
if (ret_len)
*ret_len = (size_t) (z - buf);
if (ret)
*ret = TAKE_PTR(buf);
if (ret_data)
*ret_data = TAKE_PTR(buf);
return 0;
}
@ -557,12 +557,12 @@ int unbase64char(char c) {
offset += '9' - '0' + 1;
if (c == '+')
if (IN_SET(c, '+', '-')) /* Support both the regular and the URL safe character set (see above) */
return offset;
offset++;
if (c == '/')
if (IN_SET(c, '/', '_')) /* ditto */
return offset;
return -EINVAL;
@ -772,7 +772,7 @@ int unbase64mem_full(
const char *p,
size_t l,
bool secure,
void **ret,
void **ret_data,
size_t *ret_size) {
_cleanup_free_ uint8_t *buf = NULL;
@ -860,8 +860,8 @@ int unbase64mem_full(
if (ret_size)
*ret_size = (size_t) (z - buf);
if (ret)
*ret = TAKE_PTR(buf);
if (ret_data)
*ret_data = TAKE_PTR(buf);
return 0;
}

View file

@ -18,9 +18,9 @@ char hexchar(int x) _const_;
int unhexchar(char c) _const_;
char *hexmem(const void *p, size_t l);
int unhexmem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
static inline int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
return unhexmem_full(p, l, false, mem, len);
int unhexmem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
static inline int unhexmem(const char *p, void **ret_data, size_t *ret_size) {
return unhexmem_full(p, SIZE_MAX, false, ret_data, ret_size);
}
char base32hexchar(int x) _const_;
@ -45,9 +45,9 @@ ssize_t base64_append(
size_t l,
size_t margin,
size_t width);
int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) {
return unbase64mem_full(p, l, false, mem, len);
int unbase64mem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
static inline int unbase64mem(const char *p, void **ret_data, size_t *ret_size) {
return unbase64mem_full(p, SIZE_MAX, false, ret_data, ret_size);
}
void hexdump(FILE *f, const void *p, size_t s);

View file

@ -736,10 +736,11 @@ int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen)
}
}
int in4_addr_prefix_covers(
int in4_addr_prefix_covers_full(
const struct in_addr *prefix,
unsigned char prefixlen,
const struct in_addr *address) {
const struct in_addr *address,
unsigned char address_prefixlen) {
struct in_addr masked_prefix, masked_address;
int r;
@ -747,6 +748,9 @@ int in4_addr_prefix_covers(
assert(prefix);
assert(address);
if (prefixlen > address_prefixlen)
return false;
masked_prefix = *prefix;
r = in4_addr_mask(&masked_prefix, prefixlen);
if (r < 0)
@ -760,10 +764,11 @@ int in4_addr_prefix_covers(
return in4_addr_equal(&masked_prefix, &masked_address);
}
int in6_addr_prefix_covers(
int in6_addr_prefix_covers_full(
const struct in6_addr *prefix,
unsigned char prefixlen,
const struct in6_addr *address) {
const struct in6_addr *address,
unsigned char address_prefixlen) {
struct in6_addr masked_prefix, masked_address;
int r;
@ -771,6 +776,9 @@ int in6_addr_prefix_covers(
assert(prefix);
assert(address);
if (prefixlen > address_prefixlen)
return false;
masked_prefix = *prefix;
r = in6_addr_mask(&masked_prefix, prefixlen);
if (r < 0)
@ -784,20 +792,21 @@ int in6_addr_prefix_covers(
return in6_addr_equal(&masked_prefix, &masked_address);
}
int in_addr_prefix_covers(
int in_addr_prefix_covers_full(
int family,
const union in_addr_union *prefix,
unsigned char prefixlen,
const union in_addr_union *address) {
const union in_addr_union *address,
unsigned char address_prefixlen) {
assert(prefix);
assert(address);
switch (family) {
case AF_INET:
return in4_addr_prefix_covers(&prefix->in, prefixlen, &address->in);
return in4_addr_prefix_covers_full(&prefix->in, prefixlen, &address->in, address_prefixlen);
case AF_INET6:
return in6_addr_prefix_covers(&prefix->in6, prefixlen, &address->in6);
return in6_addr_prefix_covers_full(&prefix->in6, prefixlen, &address->in6, address_prefixlen);
default:
return -EAFNOSUPPORT;
}
@ -922,12 +931,19 @@ int in_addr_prefix_from_string_auto_internal(
}
void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state) {
assert(u);
assert(state);
siphash24_compress(u->bytes, FAMILY_ADDRESS_SIZE(family), state);
}
void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
assert(a);
assert(state);
siphash24_compress(&a->family, sizeof(a->family), state);
siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
siphash24_compress_typesafe(a->family, state);
in_addr_hash_func(&a->address, a->family, state);
}
int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
@ -960,7 +976,7 @@ void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
assert(addr);
assert(state);
siphash24_compress(addr, sizeof(*addr), state);
siphash24_compress_typesafe(*addr, state);
}
int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {

View file

@ -144,9 +144,18 @@ int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mas
int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen);
int in6_addr_mask(struct in6_addr *addr, unsigned char prefixlen);
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address);
int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address);
int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address);
int in4_addr_prefix_covers_full(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address, unsigned char address_prefixlen);
int in6_addr_prefix_covers_full(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address, unsigned char address_prefixlen);
int in_addr_prefix_covers_full(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address, unsigned char address_prefixlen);
static inline int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address) {
return in4_addr_prefix_covers_full(prefix, prefixlen, address, 32);
}
static inline int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address) {
return in6_addr_prefix_covers_full(prefix, prefixlen, address, 128);
}
static inline int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address) {
return in_addr_prefix_covers_full(family, prefix, prefixlen, address, family == AF_INET ? 32 : family == AF_INET6 ? 128 : 0);
}
int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret);
int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
@ -176,6 +185,7 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
* See also oss-fuzz#11344. */
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state);
void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state);
int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y);
void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
@ -186,6 +196,16 @@ extern const struct hash_ops in_addr_data_hash_ops_free;
extern const struct hash_ops in6_addr_hash_ops;
extern const struct hash_ops in6_addr_hash_ops_free;
static inline void PTR_TO_IN4_ADDR(const void *p, struct in_addr *ret) {
assert(ret);
ret->s_addr = (uint32_t) ((uintptr_t) p);
}
static inline void* IN4_ADDR_TO_PTR(const struct in_addr *a) {
assert(a);
return (void*) ((uintptr_t) a->s_addr);
}
#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u"
#define IPV4_ADDRESS_FMT_VAL(address) \
be32toh((address).s_addr) >> 24, \

View file

@ -6,6 +6,43 @@
#include "inotify-util.h"
#include "stat-util.h"
bool inotify_event_next(
union inotify_event_buffer *buffer,
size_t size,
struct inotify_event **iterator,
int log_level) {
struct inotify_event *e;
size_t offset = 0;
assert(buffer);
assert(iterator);
if (*iterator) {
assert((uint8_t*) *iterator >= buffer->raw);
offset = (uint8_t*) *iterator - buffer->raw;
offset += offsetof(struct inotify_event, name) + (*iterator)->len;
}
if (size == offset)
return false; /* reached end of list */
if (size < offset ||
size - offset < offsetof(struct inotify_event, name)) {
log_full(log_level, "Received invalid inotify event, ignoring.");
return false;
}
e = CAST_ALIGN_PTR(struct inotify_event, buffer->raw + offset);
if (size - offset - offsetof(struct inotify_event, name) < e->len) {
log_full(log_level, "Received invalid inotify event, ignoring.");
return false;
}
*iterator = e;
return true;
}
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
int wd, r;

View file

@ -10,29 +10,27 @@
#define INOTIFY_EVENT_MAX (offsetof(struct inotify_event, name) + NAME_MAX + 1)
#define _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, start, end) \
for (struct inotify_event \
*start = &((buffer).ev), \
*end = (struct inotify_event*) ((uint8_t*) start + (sz)), \
*e = start; \
(size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) && \
((size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) + e->len || \
(log_full(log_level, "Received invalid inotify event, ignoring."), false)); \
e = (struct inotify_event*) ((uint8_t*) e + sizeof(struct inotify_event) + e->len))
#define _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, log_level) \
_FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, UNIQ_T(start, UNIQ), UNIQ_T(end, UNIQ))
/* This evaluates arguments multiple times */
#define FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, log_level) \
for (struct inotify_event *e = NULL; \
inotify_event_next(&buffer, sz, &e, log_level); )
#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
_FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_DEBUG)
FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_DEBUG)
#define FOREACH_INOTIFY_EVENT_WARN(e, buffer, sz) \
_FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_WARNING)
FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_WARNING)
union inotify_event_buffer {
struct inotify_event ev;
uint8_t raw[INOTIFY_EVENT_MAX];
};
bool inotify_event_next(
union inotify_event_buffer *buffer,
size_t size,
struct inotify_event **iterator,
int log_level);
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask);

View file

@ -7,7 +7,9 @@
#include <stdio.h>
#include <unistd.h>
#include "errno-util.h"
#include "io-util.h"
#include "iovec-util.h"
#include "string-util.h"
#include "time-util.h"
@ -58,8 +60,7 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
assert(fd >= 0);
/* If called with nbytes == 0, let's call read() at least
* once, to validate the operation */
/* If called with nbytes == 0, let's call read() at least once, to validate the operation */
if (nbytes > (size_t) SSIZE_MAX)
return -EINVAL;
@ -111,13 +112,29 @@ int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
}
#if 0 /* NM_IGNORED */
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
const uint8_t *p = ASSERT_PTR(buf);
int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout) {
const uint8_t *p;
usec_t end;
int r;
assert(fd >= 0);
assert(buf || nbytes == 0);
if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
return -EINVAL;
if (nbytes == 0) {
static const dummy_t dummy[0];
assert_cc(sizeof(dummy) == 0);
p = (const void*) dummy; /* Some valid pointer, in case NULL was specified */
} else {
if (nbytes == SIZE_MAX)
nbytes = strlen(buf);
else if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
return -EINVAL;
p = buf;
}
/* When timeout is 0 or USEC_INFINITY this is not used. But we initialize it to a sensible value. */
end = timestamp_is_set(timeout) ? usec_add(now(CLOCK_MONOTONIC), timeout) : USEC_INFINITY;
do {
ssize_t k;
@ -127,16 +144,31 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
if (errno == EINTR)
continue;
if (errno == EAGAIN && do_poll) {
/* We knowingly ignore any return value here,
* and expect that any error/EOF is reported
* via write() */
if (errno != EAGAIN || timeout == 0)
return -errno;
(void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
continue;
usec_t wait_for;
if (timeout == USEC_INFINITY)
wait_for = USEC_INFINITY;
else {
usec_t t = now(CLOCK_MONOTONIC);
if (t >= end)
return -ETIME;
wait_for = usec_sub_unsigned(end, t);
}
return -errno;
r = fd_wait_for_event(fd, POLLOUT, wait_for);
if (timeout == USEC_INFINITY || ERRNO_IS_NEG_TRANSIENT(r))
/* If timeout == USEC_INFINITY we knowingly ignore any return value
* here, and expect that any error/EOF is reported via write() */
continue;
if (r < 0)
return r;
if (r == 0)
return -ETIME;
continue;
}
if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */
@ -260,7 +292,7 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
return -EIO;
}
if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
if (lseek(fd, n, SEEK_CUR) < 0)
return -errno;
q += n;
@ -281,102 +313,4 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
return q - (const uint8_t*) p;
}
char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value) {
char *x;
x = strjoin(field, value);
if (x)
iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x);
return x;
}
char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value) {
char *x;
x = set_iovec_string_field(iovec, n_iovec, field, value);
free(value);
return x;
}
struct iovec_wrapper *iovw_new(void) {
return malloc0(sizeof(struct iovec_wrapper));
}
void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors) {
if (free_vectors)
for (size_t i = 0; i < iovw->count; i++)
free(iovw->iovec[i].iov_base);
iovw->iovec = mfree(iovw->iovec);
iovw->count = 0;
}
struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw) {
iovw_free_contents(iovw, true);
return mfree(iovw);
}
struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw) {
iovw_free_contents(iovw, false);
return mfree(iovw);
}
int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) {
if (iovw->count >= IOV_MAX)
return -E2BIG;
if (!GREEDY_REALLOC(iovw->iovec, iovw->count + 1))
return -ENOMEM;
iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len);
return 0;
}
int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value) {
_cleanup_free_ char *x = NULL;
int r;
x = strjoin(field, value);
if (!x)
return -ENOMEM;
r = iovw_put(iovw, x, strlen(x));
if (r >= 0)
TAKE_PTR(x);
return r;
}
int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value) {
_cleanup_free_ _unused_ char *free_ptr = value;
return iovw_put_string_field(iovw, field, value);
}
void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) {
for (size_t i = 0; i < iovw->count; i++)
iovw->iovec[i].iov_base = (char *)iovw->iovec[i].iov_base - old + new;
}
size_t iovw_size(struct iovec_wrapper *iovw) {
size_t n = 0;
for (size_t i = 0; i < iovw->count; i++)
n += iovw->iovec[i].iov_len;
return n;
}
void iovec_array_free(struct iovec *iov, size_t n) {
if (!iov)
return;
for (size_t i = 0; i < n; i++)
free(iov[i].iov_base);
free(iov);
}
#endif /* NM_IGNORED */

View file

@ -6,7 +6,6 @@
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/uio.h>
#include "macro.h"
#include "time-util.h"
@ -15,7 +14,11 @@ int flush_fd(int fd);
ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll);
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout);
static inline int loop_write(int fd, const void *buf, size_t nbytes) {
return loop_write_full(fd, buf, nbytes, 0);
}
int pipe_eof(int fd);
@ -24,38 +27,6 @@ int fd_wait_for_event(int fd, int event, usec_t timeout);
ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, size_t n) {
size_t r = 0;
for (size_t j = 0; j < n; j++)
r += i[j].iov_len;
return r;
}
static inline bool IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) {
/* Returns true if there is nothing else to send (bytes written cover all of the iovec),
* false if there's still work to do. */
for (size_t j = 0; j < n; j++) {
size_t sub;
if (i[j].iov_len == 0)
continue;
if (k == 0)
return false;
sub = MIN(i[j].iov_len, k);
i[j].iov_len -= sub;
i[j].iov_base = (uint8_t*) i[j].iov_base + sub;
k -= sub;
}
assert(k == 0); /* Anything else would mean that we wrote more bytes than available,
* or the kernel reported writing more bytes than sent. */
return true;
}
static inline bool FILE_SIZE_VALID(uint64_t l) {
/* ftruncate() and friends take an unsigned file size, but actually cannot deal with file sizes larger than
* 2^63 since the kernel internally handles it as signed value. This call allows checking for this early. */
@ -73,40 +44,3 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
return FILE_SIZE_VALID(l);
}
#define IOVEC_NULL (struct iovec) {}
#define IOVEC_MAKE(base, len) (struct iovec) { .iov_base = (base), .iov_len = (len) }
#define IOVEC_MAKE_STRING(string) \
({ \
char *_s = (char*) (string); \
IOVEC_MAKE(_s, strlen(_s)); \
})
char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
struct iovec_wrapper {
struct iovec *iovec;
size_t count;
};
struct iovec_wrapper *iovw_new(void);
struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw);
struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw);
void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors);
int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len);
static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) {
/* Move data into iovw or free on error */
int r = iovw_put(iovw, data, len);
if (r < 0)
free(data);
return r;
}
int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value);
int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value);
void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new);
size_t iovw_size(struct iovec_wrapper *iovw);
void iovec_array_free(struct iovec *iov, size_t n);

View file

@ -0,0 +1,99 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdbool.h>
#include <sys/types.h>
#include <sys/uio.h>
#include "alloc-util.h"
#include "macro.h"
/* An iovec pointing to a single NUL byte */
#define IOVEC_NUL_BYTE (const struct iovec) { \
.iov_base = (void*) (const uint8_t[1]) { 0 }, \
.iov_len = 1, \
}
size_t iovec_total_size(const struct iovec *iovec, size_t n);
bool iovec_increment(struct iovec *iovec, size_t n, size_t k);
/* This accepts both const and non-const pointers */
#define IOVEC_MAKE(base, len) \
(struct iovec) { \
.iov_base = (void*) (base), \
.iov_len = (len), \
}
static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) {
assert(iovec);
/* We don't use strlen_ptr() here, because we don't want to include string-util.h for now */
*iovec = IOVEC_MAKE(s, s ? strlen(s) : 0);
return iovec;
}
#define IOVEC_MAKE_STRING(s) \
*iovec_make_string(&(struct iovec) {}, s)
#define CONST_IOVEC_MAKE_STRING(s) \
(const struct iovec) { \
.iov_base = (char*) s, \
.iov_len = STRLEN(s), \
}
static inline void iovec_done(struct iovec *iovec) {
/* A _cleanup_() helper that frees the iov_base in the iovec */
assert(iovec);
iovec->iov_base = mfree(iovec->iov_base);
iovec->iov_len = 0;
}
static inline void iovec_done_erase(struct iovec *iovec) {
assert(iovec);
iovec->iov_base = erase_and_free(iovec->iov_base);
iovec->iov_len = 0;
}
static inline bool iovec_is_set(const struct iovec *iovec) {
/* Checks if the iovec points to a non-empty chunk of memory */
return iovec && iovec->iov_len > 0 && iovec->iov_base;
}
static inline bool iovec_is_valid(const struct iovec *iovec) {
/* Checks if the iovec is either NULL, empty or points to a valid bit of memory */
return !iovec || (iovec->iov_base || iovec->iov_len == 0);
}
char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
void iovec_array_free(struct iovec *iovec, size_t n_iovec);
static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) {
if (a == b)
return 0;
return memcmp_nn(a ? a->iov_base : NULL,
a ? a->iov_len : 0,
b ? b->iov_base : NULL,
b ? b->iov_len : 0);
}
static inline struct iovec *iovec_memdup(const struct iovec *source, struct iovec *ret) {
assert(ret);
if (!iovec_is_set(source))
*ret = (struct iovec) {};
else {
void *p = memdup(source->iov_base, source->iov_len);
if (!p)
return NULL;
*ret = IOVEC_MAKE(p, source->iov_len);
}
return ret;
}

View file

@ -192,6 +192,18 @@
_p; \
})
#define LIST_CLEAR(name, head, free_func) \
_LIST_CLEAR(name, head, free_func, UNIQ_T(elem, UNIQ))
/* Clear the list, destroying each element with free_func */
#define _LIST_CLEAR(name, head, free_func, elem) \
({ \
typeof(head) elem; \
while ((elem = LIST_POP(name, head))) \
free_func(elem); \
head; \
})
/* Now include "macro.h", because we want our definition of assert() which the macros above use. We include
* it down here instead of up top, since macro.h pulls in log.h which in turn needs our own definitions. */
#include "macro.h"

Some files were not shown because too many files have changed in this diff Show more