Compare commits

...

17 Commits

Author SHA1 Message Date
Vishal Chillara b5bdd37f31
Merge e9cfc2bd94 into 4b356c90dc 2024-11-23 13:57:34 +01:00
Ani Sinha 4b356c90dc measure: add 'dtbauto' option in help message
'dtbauto' command line was missing from the help string. Add it.
2024-11-23 12:43:34 +00:00
Léane GRASSER f28e16d14e po: Translated using Weblate (French)
Currently translated at 100.0% (257 of 257 strings)

Co-authored-by: Léane GRASSER <leane.grasser@proton.me>
Translate-URL: https://translate.fedoraproject.org/projects/systemd/main/fr/
Translation: systemd/main
2024-11-23 20:49:18 +09:00
Yu Watanabe 9e05e33871 networkd-test.py: fix interface state checker
After 259125d53d, network interfaces
declared by .netdev files are created after systemd-networkd sends READY
notification. So, even when networkd is started, the netdevs may not
be created yet, and 'ip' command may fail. Let's also check the return
code of the command.

This also
- drops never worked stdout checks,
- makes the test fail if the interface is not created within the timeout.
2024-11-23 17:33:43 +09:00
Lennart Poettering 95116bdfd5 nspawn: improve log message on bad incoming sd_notify() message
It's the PID that is wrong, not the UID/GID, be precise.
2024-11-23 17:33:17 +09:00
Lennart Poettering 2bd290ca02 nspawn: fix userns_mkdir() invocation
The wrong error code was logged.

But actually given that userns_mkdir() is fine with existing dirs, let's
drop the redundant conditionalization.

Follow-up for: a1fcaa1549
2024-11-23 17:33:06 +09:00
Yu Watanabe 1e9fb1d456 shutdown: propagate one more error from sync_making_progress()
No functional change, just refactoring, as anyway all errors will be
ignored by the caller.
2024-11-23 17:32:51 +09:00
Yu Watanabe 56c761f8c6
namespace-util: handle -ENOSPC by userns_acquire() gracefully in is_idmapping_supported() (#35313)
Follow-up for edae62120f.
Fixes #35311.
2024-11-23 17:32:23 +09:00
Yu Watanabe b76730f3fe shutdown: close DM block device before issuing DM_DEV_REMOVE ioctl
Otherwise, the ioctl() may fail with EBUSY.

Follow-up for b4b66b2662.
Hopefully fixes #35243.
2024-11-23 17:31:36 +09:00
Yu Watanabe 3dda236c5c basic/linux: update kernel headers from v6.12 2024-11-23 17:31:12 +09:00
Zbigniew Jędrzejewski-Szmek 5598454a3f Undeprecate commandline params forcequotacheck, fastboot, and forcefsck
Those are historical names, but there is nothing wrong with them. The files on
/ (/fastboot, /forcefsck, and /forcequotacheck) are problematic because they
require a modification of the root file system. But the commandline params work
fine. They have the obvious advantage compared to our "modern" option that they
are much easier to type without looking up the spelling in the docs. Undeprecate
them to avoid unnecessary churn.
2024-11-23 17:30:56 +09:00
Yu Watanabe 2994ca354b namespace-util: update log messages 2024-11-23 06:52:48 +09:00
Yu Watanabe eb14b993bb namespace-util: handle -ENOSPC by userns_acquire() gracefully in is_idmapping_supported()
Follow-up for edae62120f.
Fixes #35311.
2024-11-23 06:52:38 +09:00
Christian Hesse c946b13575 link README.logs from tmpfiles.d/legacy.conf only if available
The file README.logs is installed only if SysVInit support is enabled.
Thus the link should depend on it as well.
2024-11-22 18:33:20 +00:00
Frantisek Sumsal e9cfc2bd94 varlinkctl: flush stdout after each record in --more mode
So things work correctly even if varlinkctl's output is redirected to a
file.
2024-10-29 22:44:13 +05:30
Vishal Chillara Srinivas 08860aa147 test: resolve: add testing for mDNS browse
Co-authored-by: Frantisek Sumsal <frantisek@sumsal.cz>
Co-authored-by: Vishwanath Chandapur <vishwanath.chandapur@philips.com>
2024-10-29 22:43:02 +05:30
Vishal Chillara Srinivas 2e5bf2774e resolved: Implement continuous mDNS querying as per RFC6762 5.2
Allow for mDNS service/domain/types browsing.
A client can connect to the backend via varlink and receive updates as the
requested service becomes available.
The interval between the first two queries MUST be at least one second,
the intervals between successive queries MUST increase by at least a factor of two.
When the interval between queries reaches or exceeds 60 minutes, a querier MAY cap
the interval to a maximum of 60 minutes, and perform subsequent queries at a
steady-state rate of one query per hour.
Delete expired cache entries one second after goodbye packet received
as per RFC6762 Section 10.1

Cache maintenance:
The querier should plan to issue a query at 80% of the record lifetime, and
then if no answer is received, at 85%, 90%, and 95%.
If an answer is received, then the remaining TTL is reset to the value given
in the answer, and this process repeats for as long as the Multicast DNS querier
has an ongoing interest in the record.
If no answer is received after four queries, the record is deleted when it
reaches 100% of its lifetime.

Co-authored-by: Vishwanath Chandapur <vishwanath.chandapur@philips.com>
2024-10-29 22:38:48 +05:30
43 changed files with 1399 additions and 141 deletions

View File

@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-06 14:42+0000\n"
"PO-Revision-Date: 2024-11-20 19:13+0000\n"
"PO-Revision-Date: 2024-11-23 10:38+0000\n"
"Last-Translator: Léane GRASSER <leane.grasser@proton.me>\n"
"Language-Team: French <https://translate.fedoraproject.org/projects/systemd/"
"main/fr/>\n"
@ -1258,7 +1258,7 @@ msgstr ""
#: src/sysupdate/org.freedesktop.sysupdate1.policy:75
msgid "Manage optional features"
msgstr "Gérer les fonctionnalités en option"
msgstr "Gérer les fonctionnalités facultatives"
#: src/sysupdate/org.freedesktop.sysupdate1.policy:76
msgid "Authentication is required to manage optional features"

View File

@ -21,7 +21,7 @@
#define AUTOFS_MIN_PROTO_VERSION 3
#define AUTOFS_MAX_PROTO_VERSION 5
#define AUTOFS_PROTO_SUBVERSION 5
#define AUTOFS_PROTO_SUBVERSION 6
/*
* The wait_queue_token (autofs_wqt_t) is part of a structure which is passed

View File

@ -1121,6 +1121,9 @@ enum bpf_attach_type {
#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
/* Add BPF_LINK_TYPE(type, name) in bpf_types.h to keep bpf_link_type_strs[]
* in sync with the definitions below.
*/
enum bpf_link_type {
BPF_LINK_TYPE_UNSPEC = 0,
BPF_LINK_TYPE_RAW_TRACEPOINT = 1,
@ -2851,7 +2854,7 @@ union bpf_attr {
* **TCP_SYNCNT**, **TCP_USER_TIMEOUT**, **TCP_NOTSENT_LOWAT**,
* **TCP_NODELAY**, **TCP_MAXSEG**, **TCP_WINDOW_CLAMP**,
* **TCP_THIN_LINEAR_TIMEOUTS**, **TCP_BPF_DELACK_MAX**,
* **TCP_BPF_RTO_MIN**.
* **TCP_BPF_RTO_MIN**, **TCP_BPF_SOCK_OPS_CB_FLAGS**.
* * **IPPROTO_IP**, which supports *optname* **IP_TOS**.
* * **IPPROTO_IPV6**, which supports the following *optname*\ s:
* **IPV6_TCLASS**, **IPV6_AUTOFLOWLABEL**.
@ -5519,11 +5522,12 @@ union bpf_attr {
* **-EOPNOTSUPP** if the hash calculation failed or **-EINVAL** if
* invalid arguments are passed.
*
* void *bpf_kptr_xchg(void *map_value, void *ptr)
* void *bpf_kptr_xchg(void *dst, void *ptr)
* Description
* Exchange kptr at pointer *map_value* with *ptr*, and return the
* old value. *ptr* can be NULL, otherwise it must be a referenced
* pointer which will be released when this helper is called.
* Exchange kptr at pointer *dst* with *ptr*, and return the old value.
* *dst* can be map value or local kptr. *ptr* can be NULL, otherwise
* it must be a referenced pointer which will be released when this helper
* is called.
* Return
* The old value of kptr (which can be NULL). The returned pointer
* if not NULL, is a reference which must be released using its
@ -6046,11 +6050,6 @@ enum {
BPF_F_MARK_ENFORCE = (1ULL << 6),
};
/* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */
enum {
BPF_F_INGRESS = (1ULL << 0),
};
/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */
enum {
BPF_F_TUNINFO_IPV6 = (1ULL << 0),
@ -6197,10 +6196,12 @@ enum {
BPF_F_BPRM_SECUREEXEC = (1ULL << 0),
};
/* Flags for bpf_redirect_map helper */
/* Flags for bpf_redirect and bpf_redirect_map helpers */
enum {
BPF_F_BROADCAST = (1ULL << 3),
BPF_F_EXCLUDE_INGRESS = (1ULL << 4),
BPF_F_INGRESS = (1ULL << 0), /* used for skb path */
BPF_F_BROADCAST = (1ULL << 3), /* used for XDP path */
BPF_F_EXCLUDE_INGRESS = (1ULL << 4), /* used for XDP path */
#define BPF_F_REDIRECT_FLAGS (BPF_F_INGRESS | BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS)
};
#define __bpf_md_ptr(type, name) \
@ -7080,6 +7081,7 @@ enum {
TCP_BPF_SYN = 1005, /* Copy the TCP header */
TCP_BPF_SYN_IP = 1006, /* Copy the IP[46] and TCP header */
TCP_BPF_SYN_MAC = 1007, /* Copy the MAC, IP[46], and TCP header */
TCP_BPF_SOCK_OPS_CB_FLAGS = 1008, /* Get or Set TCP sock ops flags */
};
enum {
@ -7512,4 +7514,13 @@ struct bpf_iter_num {
__u64 __opaque[1];
} __attribute__((aligned(8)));
/*
* Flags to control BPF kfunc behaviour.
* - BPF_F_PAD_ZEROS: Pad destination buffer with zeros. (See the respective
* helper documentation for details.)
*/
enum bpf_kfunc_flags {
BPF_F_PAD_ZEROS = (1ULL << 0),
};
#endif /* __LINUX_BPF_H__ */

View File

@ -28,6 +28,23 @@
#define _BITUL(x) (_UL(1) << (x))
#define _BITULL(x) (_ULL(1) << (x))
#if !defined(__ASSEMBLY__)
/*
* Missing __asm__ support
*
* __BIT128() would not work in the __asm__ code, as it shifts an
* 'unsigned __init128' data type as direct representation of
* 128 bit constants is not supported in the gcc compiler, as
* they get silently truncated.
*
* TODO: Please revisit this implementation when gcc compiler
* starts representing 128 bit constants directly like long
* and unsigned long etc. Subsequently drop the comment for
* GENMASK_U128() which would then start supporting __asm__ code.
*/
#define _BIT128(x) ((unsigned __int128)(1) << (x))
#endif
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (__typeof__(x))(a) - 1)
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))

View File

@ -2531,4 +2531,20 @@ struct ethtool_link_settings {
* __u32 map_lp_advertising[link_mode_masks_nwords];
*/
};
/**
* enum phy_upstream - Represents the upstream component a given PHY device
* is connected to, as in what is on the other end of the MII bus. Most PHYs
* will be attached to an Ethernet MAC controller, but in some cases, there's
* an intermediate PHY used as a media-converter, which will driver another
* MII interface as its output.
* @PHY_UPSTREAM_MAC: Upstream component is a MAC (a switch port,
* or ethernet controller)
* @PHY_UPSTREAM_PHY: Upstream component is a PHY (likely a media converter)
*/
enum phy_upstream {
PHY_UPSTREAM_MAC,
PHY_UPSTREAM_PHY,
};
#endif /* _LINUX_ETHTOOL_H */

View File

@ -67,6 +67,7 @@ enum {
FRA_IP_PROTO, /* ip proto */
FRA_SPORT_RANGE, /* sport */
FRA_DPORT_RANGE, /* dport */
FRA_DSCP, /* dscp */
__FRA_MAX
};

View File

@ -230,8 +230,8 @@ struct tpacket_hdr_v1 {
* ts_first_pkt:
* Is always the time-stamp when the block was opened.
* Case a) ZERO packets
* No packets to deal with but atleast you know the
* time-interval of this block.
* No packets to deal with but at least you know
* the time-interval of this block.
* Case b) Non-zero packets
* Use the ts of the first packet in the block.
*
@ -265,7 +265,8 @@ enum tpacket_versions {
- struct tpacket_hdr
- pad to TPACKET_ALIGNMENT=16
- struct sockaddr_ll
- Gap, chosen so that packet data (Start+tp_net) alignes to TPACKET_ALIGNMENT=16
- Gap, chosen so that packet data (Start+tp_net) aligns to
TPACKET_ALIGNMENT=16
- Start+tp_mac: [ Optional MAC header ]
- Start+tp_net: Packet data, aligned to TPACKET_ALIGNMENT=16.
- Pad to align to TPACKET_ALIGNMENT=16

View File

@ -141,7 +141,7 @@ struct in_addr {
*/
#define IP_PMTUDISC_INTERFACE 4
/* weaker version of IP_PMTUDISC_INTERFACE, which allows packets to get
* fragmented if they exeed the interface mtu
* fragmented if they exceed the interface mtu
*/
#define IP_PMTUDISC_OMIT 5

View File

@ -140,25 +140,6 @@
#endif /* _NETINET_IN_H */
/* Coordinate with glibc netipx/ipx.h header. */
#if defined(__NETIPX_IPX_H)
#define __UAPI_DEF_SOCKADDR_IPX 0
#define __UAPI_DEF_IPX_ROUTE_DEFINITION 0
#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 0
#define __UAPI_DEF_IPX_CONFIG_DATA 0
#define __UAPI_DEF_IPX_ROUTE_DEF 0
#else /* defined(__NETIPX_IPX_H) */
#define __UAPI_DEF_SOCKADDR_IPX 1
#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1
#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1
#define __UAPI_DEF_IPX_CONFIG_DATA 1
#define __UAPI_DEF_IPX_ROUTE_DEF 1
#endif /* defined(__NETIPX_IPX_H) */
/* Definitions for xattr.h */
#if defined(_SYS_XATTR_H)
#define __UAPI_DEF_XATTR 0
@ -240,23 +221,6 @@
#define __UAPI_DEF_IP6_MTUINFO 1
#endif
/* Definitions for ipx.h */
#ifndef __UAPI_DEF_SOCKADDR_IPX
#define __UAPI_DEF_SOCKADDR_IPX 1
#endif
#ifndef __UAPI_DEF_IPX_ROUTE_DEFINITION
#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1
#endif
#ifndef __UAPI_DEF_IPX_INTERFACE_DEFINITION
#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1
#endif
#ifndef __UAPI_DEF_IPX_CONFIG_DATA
#define __UAPI_DEF_IPX_CONFIG_DATA 1
#endif
#ifndef __UAPI_DEF_IPX_ROUTE_DEF
#define __UAPI_DEF_IPX_ROUTE_DEF 1
#endif
/* Definitions for xattr.h */
#ifndef __UAPI_DEF_XATTR
#define __UAPI_DEF_XATTR 1

View File

@ -436,7 +436,7 @@ enum nft_set_elem_flags {
* @NFTA_SET_ELEM_KEY: key value (NLA_NESTED: nft_data)
* @NFTA_SET_ELEM_DATA: data value of mapping (NLA_NESTED: nft_data_attributes)
* @NFTA_SET_ELEM_FLAGS: bitmask of nft_set_elem_flags (NLA_U32)
* @NFTA_SET_ELEM_TIMEOUT: timeout value (NLA_U64)
* @NFTA_SET_ELEM_TIMEOUT: timeout value, zero means never times out (NLA_U64)
* @NFTA_SET_ELEM_EXPIRATION: expiration time (NLA_U64)
* @NFTA_SET_ELEM_USERDATA: user data (NLA_BINARY)
* @NFTA_SET_ELEM_EXPR: expression (NLA_NESTED: nft_expr_attributes)
@ -1694,7 +1694,7 @@ enum nft_flowtable_flags {
*
* @NFTA_FLOWTABLE_TABLE: name of the table containing the expression (NLA_STRING)
* @NFTA_FLOWTABLE_NAME: name of this flow table (NLA_STRING)
* @NFTA_FLOWTABLE_HOOK: netfilter hook configuration(NLA_U32)
* @NFTA_FLOWTABLE_HOOK: netfilter hook configuration (NLA_NESTED)
* @NFTA_FLOWTABLE_USE: number of references to this flow table (NLA_U32)
* @NFTA_FLOWTABLE_HANDLE: object handle (NLA_U64)
* @NFTA_FLOWTABLE_FLAGS: flags (NLA_U32)

View File

@ -16,10 +16,15 @@ struct nhmsg {
struct nexthop_grp {
__u32 id; /* nexthop id - must exist */
__u8 weight; /* weight of this nexthop */
__u8 resvd1;
__u8 weight_high; /* high order bits of weight */
__u16 resvd2;
};
static __inline__ __u16 nexthop_grp_weight(const struct nexthop_grp *entry)
{
return ((entry->weight_high << 8) | entry->weight) + 1;
}
enum {
NEXTHOP_GRP_TYPE_MPATH, /* hash-threshold nexthop group
* default type if not specified
@ -33,6 +38,9 @@ enum {
#define NHA_OP_FLAG_DUMP_STATS BIT(0)
#define NHA_OP_FLAG_DUMP_HW_STATS BIT(1)
/* Response OP_FLAGS. */
#define NHA_OP_FLAG_RESP_GRP_RESVD_0 BIT(31) /* Dump clears resvd fields. */
enum {
NHA_UNSPEC,
NHA_ID, /* u32; id for nexthop. id == 0 means auto-assign */

View File

@ -531,20 +531,24 @@ int is_idmapping_supported(const char *path) {
userns_fd = userns_acquire(uid_map, gid_map);
if (ERRNO_IS_NEG_NOT_SUPPORTED(userns_fd) || ERRNO_IS_NEG_PRIVILEGE(userns_fd))
return false;
if (userns_fd == -ENOSPC) {
log_debug_errno(userns_fd, "Failed to acquire new user namespace, user.max_user_namespaces seems to be exhausted or maybe even zero, assuming ID-mapping is not supported: %m");
return false;
}
if (userns_fd < 0)
return log_debug_errno(userns_fd, "ID-mapping supported namespace acquire failed for '%s' : %m", path);
return log_debug_errno(userns_fd, "Failed to acquire new user namespace for checking if '%s' supports ID-mapping: %m", path);
dir_fd = RET_NERRNO(open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
if (ERRNO_IS_NEG_NOT_SUPPORTED(dir_fd))
return false;
if (dir_fd < 0)
return log_debug_errno(dir_fd, "ID-mapping supported open failed for '%s' : %m", path);
return log_debug_errno(dir_fd, "Failed to open '%s', cannot determine if ID-mapping is supported: %m", path);
mount_fd = RET_NERRNO(open_tree(dir_fd, "", AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC));
if (ERRNO_IS_NEG_NOT_SUPPORTED(mount_fd) || ERRNO_IS_NEG_PRIVILEGE(mount_fd) || mount_fd == -EINVAL)
return false;
if (mount_fd < 0)
return log_debug_errno(mount_fd, "ID-mapping supported open_tree failed for '%s' : %m", path);
return log_debug_errno(mount_fd, "Failed to open mount tree '%s', cannot determine if ID-mapping is supported: %m", path);
r = RET_NERRNO(mount_setattr(mount_fd, "", AT_EMPTY_PATH,
&(struct mount_attr) {
@ -554,7 +558,7 @@ int is_idmapping_supported(const char *path) {
if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL)
return false;
if (r < 0)
return log_debug_errno(r, "ID-mapping supported setattr failed for '%s' : %m", path);
return log_debug_errno(r, "Failed to set mount attribute to '%s', cannot determine if ID-mapping is supported: %m", path);
return true;
}

View File

@ -98,16 +98,11 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
}
}
#if HAVE_SYSV_COMPAT
else if (streq(key, "fastboot") && !value) {
log_warning("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
else if (streq(key, "fastboot") && !value)
arg_skip = true;
} else if (streq(key, "forcefsck") && !value) {
log_warning("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
else if (streq(key, "forcefsck") && !value)
arg_force = true;
}
#endif
return 0;
}

View File

@ -101,18 +101,19 @@ static int help(int argc, char *argv[], void *userdata) {
" -j Same as --json=pretty on tty, --json=short otherwise\n"
" --append=PATH Load specified JSON signature, and append new signature to it\n"
"\n%3$sUKI PE Section Options:%4$s %3$sUKI PE Section%4$s\n"
" --linux=PATH Path to Linux kernel image file %7$s .linux\n"
" --osrel=PATH Path to os-release file %7$s .osrel\n"
" --cmdline=PATH Path to file with kernel command line %7$s .cmdline\n"
" --initrd=PATH Path to initrd image file %7$s .initrd\n"
" --ucode=PATH Path to microcode image file %7$s .ucode\n"
" --splash=PATH Path to splash bitmap file %7$s .splash\n"
" --dtb=PATH Path to DeviceTree file %7$s .dtb\n"
" --uname=PATH Path to 'uname -r' file %7$s .uname\n"
" --sbat=PATH Path to SBAT file %7$s .sbat\n"
" --pcrpkey=PATH Path to public key for PCR signatures %7$s .pcrpkey\n"
" --profile=PATH Path to profile file %7$s .profile\n"
" --hwids=PATH Path to HWIDs file %7$s .hwids\n"
" --linux=PATH Path to Linux kernel image file %7$s .linux\n"
" --osrel=PATH Path to os-release file %7$s .osrel\n"
" --cmdline=PATH Path to file with kernel command line %7$s .cmdline\n"
" --initrd=PATH Path to initrd image file %7$s .initrd\n"
" --ucode=PATH Path to microcode image file %7$s .ucode\n"
" --splash=PATH Path to splash bitmap file %7$s .splash\n"
" --dtb=PATH Path to DeviceTree file %7$s .dtb\n"
" --dtbauto=PATH Path to DeviceTree file for auto selection %7$s .dtbauto\n"
" --uname=PATH Path to 'uname -r' file %7$s .uname\n"
" --sbat=PATH Path to SBAT file %7$s .sbat\n"
" --pcrpkey=PATH Path to public key for PCR signatures %7$s .pcrpkey\n"
" --profile=PATH Path to profile file %7$s .profile\n"
" --hwids=PATH Path to HWIDs file %7$s .hwids\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,

View File

@ -2280,10 +2280,9 @@ static int copy_devnode_one(const char *dest, const char *node, bool ignore_mkno
r = path_extract_directory(from, &parent);
if (r < 0)
return log_error_errno(r, "Failed to extract directory from %s: %m", from);
if (!path_equal(parent, "/dev/")) {
if (userns_mkdir(dest, parent, 0755, 0, 0) < 0)
return log_error_errno(r, "Failed to create directory %s: %m", parent);
}
r = userns_mkdir(dest, parent, 0755, 0, 0);
if (r < 0)
return log_error_errno(r, "Failed to create directory %s: %m", parent);
if (mknod(to, st.st_mode, st.st_rdev) < 0) {
r = -errno; /* Save the original error code. */
@ -4654,7 +4653,7 @@ static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t r
ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
if (!ucred || ucred->pid != inner_child_pid) {
log_debug("Received notify message without valid credentials. Ignoring.");
log_debug("Received notify message from process that is not the payload's PID 1. Ignoring.");
return 0;
}

View File

@ -36,14 +36,9 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
arg_skip = true;
else
log_warning("Invalid quotacheck.mode= value, ignoring: %s", value);
}
#if HAVE_SYSV_COMPAT
else if (streq(key, "forcequotacheck") && !value) {
log_warning("Please use 'quotacheck.mode=force' rather than 'forcequotacheck' on the kernel command line. Proceeding anyway.");
} else if (streq(key, "forcequotacheck") && !value)
arg_force = true;
}
#endif
return 0;
}

View File

@ -8,6 +8,7 @@ basic_dns_sources = files(
'resolved-dns-rr.c',
'resolved-dns-answer.c',
'resolved-dns-question.c',
'resolved-dns-browse-services.c',
'resolved-util.c',
'dns-type.c',
)

View File

@ -0,0 +1,687 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "af-list.h"
#include "event-util.h"
#include "json-util.h"
#include "random-util.h"
#include "resolved-dns-browse-services.h"
#include "resolved-dns-cache.h"
#include "resolved-varlink.h"
/* RFC 6762 section 5.2 - The querier should plan to issue a query at 80% of
* the record lifetime, and then if no answer is received, at 85%, 90%, and 95%. */
static usec_t mdns_maintenance_next_time(usec_t until, uint32_t ttl, int ttl_state) {
return usec_sub_unsigned(until, (20 - ttl_state * 5) * ttl * USEC_PER_SEC / 100);
}
/* RFC 6762 section 5.2 - A random variation of 2% of the record TTL should
* be added to maintenance queries. */
static usec_t mdns_maintenance_jitter(uint32_t ttl) {
return random_u64_range(100) * 2 * ttl * USEC_PER_SEC / 10000;
}
#define MDNS_80_PERCENT 80
#define MDNS_5_PERCENT 5
static void mdns_find_service_from_query(DnsService **service, DnsServiceBrowser *sb, DnsQuery *q) {
assert(sb);
/* Find the service that owns the query. */
LIST_FOREACH(dns_services, s, sb->dns_services) {
if (s->query == q) {
*service = s;
return;
}
}
*service = NULL;
}
static void mdns_maintenance_query_complete(DnsQuery *q) {
_cleanup_(dns_service_browser_unrefp) DnsServiceBrowser *sb = NULL;
_cleanup_(dns_query_freep) DnsQuery *query = q;
DnsService *service = NULL;
int r;
assert(query);
assert(query->manager);
sb = dns_service_browser_ref(hashmap_get(query->manager->dns_service_browsers, query->varlink_request));
if (!sb)
return;
if (query->state != DNS_TRANSACTION_SUCCESS) {
r = 0;
goto finish;
}
r = dns_answer_match_key(query->answer, sb->key, NULL);
if (r <= 0)
goto finish;
r = mdns_browser_lookup_cache(sb, query->answer_family);
finish:
if (r < 0)
log_error_errno(r, "mDNS maintenance query complete failed: %m");
mdns_find_service_from_query(&service, sb, query);
if (service)
service->query = NULL;
}
static int mdns_maintenance_query(sd_event_source *s, uint64_t usec, void *userdata) {
DnsService *service = NULL;
_cleanup_(dns_query_freep) DnsQuery *q = NULL;
int r;
assert(userdata);
service = userdata;
if (service->rr_ttl_state++ == MDNS_TTL_100_PERCENT)
return mdns_browser_lookup_cache(service->sb, service->family);
r = dns_query_new(service->sb->m, &q, service->sb->question_utf8, service->sb->question_idna, NULL, service->sb->ifindex, service->sb->flags);
if (r < 0)
goto finish;
q->complete = mdns_maintenance_query_complete;
q->varlink_request = sd_varlink_ref(service->sb->link);
service->query = TAKE_PTR(q);
usec_t next_time = mdns_maintenance_next_time(service->until, service->rr->ttl, service->rr_ttl_state);
/* Schedule next maintenance query for service */
r = event_reset_time(
service->sb->m->event, &service->schedule_event,
CLOCK_BOOTTIME, next_time, 0, mdns_maintenance_query,
service, 0, "mdns-next-query-schedule", true);
if (r < 0)
goto finish;
r = dns_query_go(service->query);
if (r < 0)
goto finish;
return 0;
finish:
dns_query_free(service->query);
return log_error_errno(r, "Failed mdns maintenance query: %m");
}
int dns_add_new_service(DnsServiceBrowser *sb, DnsResourceRecord *rr, int owner_family) {
_cleanup_(dns_service_unrefp) DnsService *s = NULL;
int r;
assert(sb);
assert(rr);
s = new(DnsService, 1);
if (!s)
return log_oom();
usec_t usec = now(CLOCK_BOOTTIME);
*s = (DnsService) {
.n_ref = 1,
.sb = dns_service_browser_ref(sb),
.rr = dns_resource_record_copy(rr),
.family = owner_family,
.until = rr->until,
.query = NULL,
.rr_ttl_state = MDNS_TTL_80_PERCENT,
};
LIST_PREPEND(dns_services, sb->dns_services, s);
/* Schedule the first cache maintenance query at 80% of the record's TTL.
* Subsequent queries issued at 5% increments until 100% of the TTL. RFC 6762 section 5.2.
* If service is being added after 80% of the TTL has already elapsed,
* schedule the next query at the next 5% increment. */
usec_t next_time = 0;
while (s->rr_ttl_state <= MDNS_TTL_100_PERCENT) {
next_time = mdns_maintenance_next_time(rr->until, rr->ttl, s->rr_ttl_state);
if (next_time >= usec)
break;
s->rr_ttl_state++;
}
if (next_time < usec) {
/* If next_time is still in the past, the service is being added after it has already expired.
* Just schedule a 100% maintenance query */
next_time = usec + USEC_PER_SEC;
s->rr_ttl_state = MDNS_TTL_100_PERCENT;
}
usec_t jitter = mdns_maintenance_jitter(rr->ttl);
r = sd_event_add_time(
sb->m->event,
&s->schedule_event,
CLOCK_BOOTTIME,
usec_add(next_time, jitter),
0,
mdns_maintenance_query,
s);
if (r < 0)
return r;
TAKE_PTR(s);
return 0;
}
void dns_remove_service(DnsServiceBrowser *sb, DnsService *service) {
assert(sb);
assert(service);
LIST_REMOVE(dns_services, sb->dns_services, service);
dns_service_free(service);
}
DnsService *dns_service_free(DnsService *service) {
if (!service)
return NULL;
sd_event_source_disable_unref(service->schedule_event);
if (service->query && DNS_TRANSACTION_IS_LIVE(service->query->state))
dns_query_complete(service->query, DNS_TRANSACTION_ABORTED);
dns_service_browser_unref(service->sb);
dns_resource_record_unref(service->rr);
return mfree(service);
}
DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsService, dns_service, dns_service_free);
int mdns_service_update(DnsService *service, DnsResourceRecord *rr, usec_t t) {
service->until = rr->until;
service->rr->ttl = rr->ttl;
/* Update the 80% TTL maintenance event based on new record received from the network.
* RFC 6762 section 5.2 */
usec_t next_time = mdns_maintenance_next_time(service->until, service->rr->ttl, MDNS_TTL_80_PERCENT);
usec_t jitter = mdns_maintenance_jitter(service->rr->ttl);
if (service->schedule_event)
return sd_event_source_set_time(service->schedule_event, usec_add(next_time, jitter));
return 0;
}
bool dns_service_contains(DnsService *services, DnsResourceRecord *rr, int owner_family) {
usec_t t = now(CLOCK_BOOTTIME);
LIST_FOREACH(dns_services, service, services)
if (dns_resource_record_equal(rr, service->rr) > 0 && service->family == owner_family) {
if (rr->ttl <= 1)
return true;
if (rr->until > service->until)
mdns_service_update(service, rr, t);
return true;
}
return false;
}
void dns_browse_services_purge(Manager *m, int family) {
int r = 0;
/* Called after caches are flused.
* Clear local service records and notify varlink client. */
if (!(m && m->dns_service_browsers))
return;
DnsServiceBrowser *sb;
HASHMAP_FOREACH(sb, m->dns_service_browsers) {
r = sd_event_source_set_enabled(sb->schedule_event, SD_EVENT_OFF);
if (r < 0)
goto finish;
if (family == AF_UNSPEC) {
r = mdns_browser_lookup_cache(sb, AF_INET);
if (r < 0)
goto finish;
r = mdns_browser_lookup_cache(sb, AF_INET6);
if (r < 0)
goto finish;
return;
}
r = mdns_browser_lookup_cache(sb, family);
if (r < 0)
goto finish;
}
finish:
if (r < 0)
log_error_errno(r, "mdns browse services purge failed: %m");
return;
}
int mdns_manage_services_answer(DnsServiceBrowser *sb, DnsAnswer *answer, int owner_family) {
DnsResourceRecord *i;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
int r;
assert(sb);
/* Check for new service added */
DNS_ANSWER_FOREACH(i, answer) {
_cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *entry = NULL;
if (dns_service_contains(sb->dns_services, i, owner_family))
continue;
r = dns_service_split(i->ptr.name, &name, &type, &domain);
if (r < 0)
goto finish;
if (!name) {
type = mfree(type);
domain = mfree(domain);
r = dns_service_split(dns_resource_key_name(i->key), &name, &type, &domain);
if (r < 0)
goto finish;
}
if (!type)
continue;
r = dns_add_new_service(sb, i, owner_family);
if (r < 0)
goto finish;
log_debug("Add into the list %s, %s, %s, %s, %d",
strna(name),
strna(type),
strna(domain),
strna(af_to_ipv4_ipv6(owner_family)),
sb->ifindex);
r = sd_json_buildo(&entry,
SD_JSON_BUILD_PAIR("add_flag", SD_JSON_BUILD_BOOLEAN(true)),
SD_JSON_BUILD_PAIR("family", SD_JSON_BUILD_INTEGER(owner_family)),
SD_JSON_BUILD_PAIR("name", SD_JSON_BUILD_STRING(name?: "")),
SD_JSON_BUILD_PAIR("type", SD_JSON_BUILD_STRING(type?: "")),
SD_JSON_BUILD_PAIR("domain", SD_JSON_BUILD_STRING(domain?: "")),
SD_JSON_BUILD_PAIR("interface", SD_JSON_BUILD_INTEGER(sb->ifindex)));
if (r < 0)
goto finish;
r = sd_json_variant_append_array(&array, entry);
if (r < 0)
goto finish;
}
/* Check for services removed */
LIST_FOREACH(dns_services, service, sb->dns_services) {
_cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *entry = NULL;
if (service->family != owner_family)
continue;
if (dns_answer_contains(answer, service->rr))
continue;
r = dns_service_split(service->rr->ptr.name, &name, &type, &domain);
if (r < 0)
goto finish;
if (!name) {
type = mfree(type);
domain = mfree(domain);
r = dns_service_split(dns_resource_key_name(service->rr->key), &name, &type, &domain);
if (r < 0)
goto finish;
}
dns_remove_service(sb, service);
log_debug("Remove from the list %s, %s, %s, %s, %d",
strna(name),
strna(type),
strna(domain),
strna(af_to_ipv4_ipv6(owner_family)),
sb->ifindex);
r = sd_json_buildo(&entry,
SD_JSON_BUILD_PAIR("add_flag", SD_JSON_BUILD_BOOLEAN(false)),
SD_JSON_BUILD_PAIR("family", SD_JSON_BUILD_INTEGER(owner_family)),
SD_JSON_BUILD_PAIR("name", SD_JSON_BUILD_STRING(name?: "")),
SD_JSON_BUILD_PAIR("type", SD_JSON_BUILD_STRING(type?: "")),
SD_JSON_BUILD_PAIR("domain", SD_JSON_BUILD_STRING(domain?: "")),
SD_JSON_BUILD_PAIR("interface", SD_JSON_BUILD_INTEGER(sb->ifindex)));
if (r < 0)
goto finish;
r = sd_json_variant_append_array(&array, entry);
if (r < 0)
goto finish;
}
if (!sd_json_variant_is_blank_array(array)) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *vm = NULL;
r = sd_json_buildo(&vm,
SD_JSON_BUILD_PAIR("browser_service_data", SD_JSON_BUILD_VARIANT(array)));
if (r < 0)
goto finish;
r = sd_varlink_notify(sb->link, vm);
if (r < 0)
goto finish;
}
return 0;
finish:
log_error_errno(r, "Failed to process received services: %m");
return sd_varlink_error_errno(sb->link, r);
}
int mdns_browser_lookup_cache(DnsServiceBrowser *sb, int owner_family) {
_cleanup_(dns_answer_unrefp) DnsAnswer *lookup_ret_answer = NULL;
DnsScope *scope;
int r;
assert(sb);
assert(sb->m);
scope = manager_find_scope_from_protocol(sb->m, sb->ifindex, DNS_PROTOCOL_MDNS, owner_family);
if (!scope)
return 0;
dns_cache_prune(&scope->cache);
r = dns_cache_lookup(
&scope->cache,
sb->key,
sb->flags,
NULL,
&lookup_ret_answer,
NULL,
NULL,
NULL);
if (r < 0)
return r;
return mdns_manage_services_answer(sb, lookup_ret_answer, owner_family);
}
int mdns_notify_browsers_goodbye(DnsScope *scope) {
DnsServiceBrowser *sb = NULL;
int r;
if (!scope)
return 0;
HASHMAP_FOREACH(sb, scope->manager->dns_service_browsers) {
r = mdns_browser_lookup_cache(sb, scope->family);
if (r < 0)
goto finish;
}
return 0;
finish:
return r;
}
int mdns_notify_browsers_unsolicited_updates(Manager *m, DnsAnswer *answer, int owner_family) {
DnsServiceBrowser *sb = NULL;
int r;
assert(m);
if (!answer)
return 0;
if (!m->dns_service_browsers)
return 0;
HASHMAP_FOREACH(sb, m->dns_service_browsers) {
r = dns_answer_match_key(answer, sb->key, NULL);
if (r < 0)
goto finish;
else if (r == 0)
continue;
r = mdns_browser_lookup_cache(sb, owner_family);
if (r < 0)
goto finish;
}
return 0;
finish:
return log_error_errno(r, "Failed to notify mDNS service subscribers, %m");
}
static void mdns_browse_service_query_complete(DnsQuery *q) {
_cleanup_(dns_service_browser_unrefp) DnsServiceBrowser *sb = NULL;
_cleanup_(dns_query_freep) DnsQuery *query = q;
int r;
assert(query);
assert(query->manager);
if (query->state != DNS_TRANSACTION_SUCCESS)
return;
sb = dns_service_browser_ref(hashmap_get(query->manager->dns_service_browsers, query->varlink_request));
if (!sb)
return;
r = dns_answer_match_key(query->answer, sb->key, NULL);
if (r < 0)
goto finish;
else if (r == 0)
return;
r = mdns_browser_lookup_cache(sb, query->answer_family);
if (r < 0)
goto finish;
/* When the query is answered from cache, we only get answers for one answer_family
* i.e. either ipv4 or ipv6.
* We need to perform another cache lookup for the other answer_family */
if (query->answer_query_flags == SD_RESOLVED_FROM_CACHE) {
r = mdns_browser_lookup_cache(sb, query->answer_family == AF_INET? AF_INET6 : AF_INET);
if (r < 0)
goto finish;
}
return;
finish:
log_error_errno(r, "mDNS browse query complete failed, %m");
}
static int mdns_next_query_schedule(sd_event_source *s, uint64_t usec, void *userdata) {
_cleanup_(dns_service_browser_unrefp) DnsServiceBrowser *sb = NULL;
_cleanup_(dns_query_freep) DnsQuery *q = NULL;
int r;
assert(userdata);
sb = dns_service_browser_ref(userdata);
r = dns_query_new(sb->m, &q, sb->question_utf8, sb->question_idna, NULL, sb->ifindex, sb->flags);
if (r < 0)
goto finish;
q->complete = mdns_browse_service_query_complete;
q->varlink_request = sd_varlink_ref(sb->link);
sd_varlink_set_userdata(sb->link, q);
r = dns_query_go(q);
if (r < 0)
goto finish;
/* RFC6762 5.2
* The intervals between successive queries MUST increase by at least a factor of two.
* When the interval between queries reaches or exceeds 60 minutes,perform
* subsequent queries at a steady-state rate of one query per hour */
if (sb->delay == 0) {
sb->delay++;
/* First query is sent wihtout SD_RESOLVED_NO_CACHE to fetch answers already in cache.
* Set SD_RESOLVED_NO_CACHE to make all subsequent queries go to the network. */
sb->flags |= SD_RESOLVED_NO_CACHE;
}
else
sb->delay = sb->delay < 2048 ? sb->delay * 2 : 3600;
r = event_reset_time_relative(
sb->m->event, &sb->schedule_event,
CLOCK_BOOTTIME, (sb->delay * USEC_PER_SEC),
0, mdns_next_query_schedule,
sb, 0, "mdns-next-query-schedule", true);
if (r < 0)
goto finish;
TAKE_PTR(q);
return 0;
finish:
return log_error_errno(r, "Failed to schedule mDNS query, %m");
}
void dns_service_browser_reset(Manager *m) {
int r;
if (!(m && m->dns_service_browsers))
return;
DnsServiceBrowser *sb;
HASHMAP_FOREACH(sb, m->dns_service_browsers) {
sb->delay = 0;
r = event_reset_time_relative(
sb->m->event, &sb->schedule_event,
CLOCK_BOOTTIME, (sb->delay * USEC_PER_SEC),
0, mdns_next_query_schedule,
sb, 0, "mdns-next-query-schedule", true);
if (r < 0)
log_error_errno(r, "Failed to reset mdns service subscriber, %m");
}
return;
}
int dns_subscribe_browse_service(
Manager *m,
sd_varlink *link,
const char *domain,
const char *name,
const char *type,
int ifindex,
uint64_t flags) {
_cleanup_(dns_service_browser_unrefp) DnsServiceBrowser *sb = NULL;
_cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL;
int r;
assert(m);
assert(link);
if (ifindex <= 0)
return sd_varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("ifindex"));
if (isempty(name))
name = NULL;
else if (!dns_service_name_is_valid(name))
return sd_varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("name"));
if (isempty(type))
type = NULL;
else if (!dnssd_srv_type_is_valid(type))
return sd_varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("type"));
r = dns_name_is_valid(domain);
if (r < 0)
return r;
if (r == 0)
return sd_varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("domain"));
r = dns_question_new_service_type(&question_utf8, name, type, domain, false, DNS_TYPE_PTR);
if (r < 0)
return r;
r = dns_question_new_service_type(&question_idna, name, type, domain, true, DNS_TYPE_PTR);
if (r < 0)
return r;
sb = new(DnsServiceBrowser, 1);
if (!sb)
return log_oom();
*sb = (DnsServiceBrowser) {
.n_ref = 1,
.m = m,
.link = sd_varlink_ref(link),
.question_utf8 = dns_question_ref(question_utf8),
.question_idna = dns_question_ref(question_idna),
.key = dns_question_first_key(question_utf8),
.ifindex = ifindex,
.flags = flags,
};
/* Only mDNS continuous querying is currently supported. See RFC 6762 */
switch (flags & SD_RESOLVED_PROTOCOLS_ALL) {
case SD_RESOLVED_MDNS:
r = sd_event_add_time(m->event,
&sb->schedule_event,
CLOCK_BOOTTIME,
usec_add(now(CLOCK_BOOTTIME), (sb->delay * USEC_PER_SEC)),
0,
mdns_next_query_schedule,
sb);
if (r < 0)
return r;
break;
default:
return -EINVAL;
}
r = hashmap_ensure_put(&m->dns_service_browsers, NULL, link, sb);
if (r < 0)
return r;
TAKE_PTR(sb);
return 0;
}
DnsServiceBrowser *dns_service_browser_free(DnsServiceBrowser *sb) {
DnsQuery *q;
if (!sb)
return NULL;
LIST_FOREACH(dns_services, service, sb->dns_services)
dns_remove_service(sb, service);
sd_event_source_disable_unref(sb->schedule_event);
q = sd_varlink_get_userdata(sb->link);
if (q && DNS_TRANSACTION_IS_LIVE(q->state))
dns_query_complete(q, DNS_TRANSACTION_ABORTED);
dns_question_unref(sb->question_idna);
dns_question_unref(sb->question_utf8);
sd_varlink_unref(sb->link);
return mfree(sb);
}
DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsServiceBrowser, dns_service_browser, dns_service_browser_free);

View File

@ -0,0 +1,79 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
typedef struct DnsServiceBrowser DnsServiceBrowser;
#include "resolved-dns-query.h"
#include "resolved-manager.h"
#include "sd-varlink.h"
typedef struct DnsService DnsService;
typedef enum DnsRecordTTLState DnsRecordTTLState;
enum DnsRecordTTLState {
MDNS_TTL_80_PERCENT,
MDNS_TTL_85_PERCENT,
MDNS_TTL_90_PERCENT,
MDNS_TTL_95_PERCENT,
MDNS_TTL_100_PERCENT
};
struct DnsService {
unsigned n_ref;
DnsServiceBrowser *sb;
sd_event_source *schedule_event;
DnsResourceRecord *rr;
int family;
usec_t until;
DnsRecordTTLState rr_ttl_state;
DnsQuery *query;
LIST_FIELDS(DnsService, dns_services);
};
struct DnsServiceBrowser {
unsigned n_ref;
Manager *m;
sd_varlink *link;
DnsQuestion *question_idna;
DnsQuestion *question_utf8;
uint64_t flags;
sd_event_source *schedule_event;
usec_t delay;
DnsResourceKey *key;
int ifindex;
uint64_t token;
LIST_HEAD(DnsService, dns_services);
};
DnsServiceBrowser *dns_service_browser_free(DnsServiceBrowser *sb);
void dns_remove_service(DnsServiceBrowser *sb, DnsService *service);
DnsService *dns_service_free(DnsService *service);
DnsServiceBrowser* dns_service_browser_ref(DnsServiceBrowser *sb);
DnsServiceBrowser* dns_service_browser_unref(DnsServiceBrowser *sb);
DnsService* dns_service_ref(DnsService *service);
DnsService* dns_service_unref(DnsService *service);
void dns_browse_services_purge(Manager *m, int family);
void dns_service_browser_reset(Manager *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServiceBrowser*, dns_service_browser_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsService*, dns_service_unref);
bool dns_service_contains(DnsService *services, DnsResourceRecord *rr, int owner_family);
int mdns_manage_services_answer(DnsServiceBrowser *sb, DnsAnswer *answer, int owner_family);
int dns_add_new_service(DnsServiceBrowser *sb, DnsResourceRecord *rr, int owner_family);
int mdns_service_update(DnsService *service, DnsResourceRecord *rr, usec_t t);
int mdns_browser_lookup_cache(DnsServiceBrowser *sb, int owner_family);
int dns_subscribe_browse_service(Manager *m,
sd_varlink *link,
const char *domain,
const char * name,
const char * type,
int ifindex,
uint64_t flags);
int mdns_notify_browsers_unsolicited_updates(Manager *m, DnsAnswer *answer, int owner_family);
int mdns_notify_browsers_goodbye(DnsScope *scope);

View File

@ -1011,6 +1011,7 @@ static int answer_add_clamp_ttl(
}
}
rr->until = until;
r = dns_answer_add_extend(answer, rr, ifindex, answer_flags, rrsig);
if (r < 0)
return r;

View File

@ -416,6 +416,66 @@ int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_
return 0;
}
int dns_question_new_service_type(
DnsQuestion **ret,
const char *service,
const char *type,
const char *domain,
bool convert_idna,
uint16_t record_type) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
_cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
_cleanup_free_ char *buf = NULL, *joined = NULL;
const char *name;
int r;
assert(ret);
if (record_type == DNS_TYPE_SRV)
return -EINVAL;
if (!domain)
return -EINVAL;
if (type) {
if (convert_idna) {
r = dns_name_apply_idna(domain, &buf);
if (r < 0)
return r;
if (r > 0)
domain = buf;
}
r = dns_service_join(service, type, domain, &joined);
if (r < 0)
return r;
name = joined;
} else {
if (service)
return -EINVAL;
name = domain;
}
q = dns_question_new(1);
if (!q)
return -ENOMEM;
key = dns_resource_key_new(DNS_CLASS_IN, record_type, name);
if (!key)
return -ENOMEM;
r = dns_question_add(q, key, 0);
if (r < 0)
return r;
*ret = TAKE_PTR(q);
return 0;
}
int dns_question_new_service(
DnsQuestion **ret,
const char *service,

View File

@ -31,6 +31,7 @@ DnsQuestion *dns_question_unref(DnsQuestion *q);
int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna);
int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a);
int dns_question_new_service(DnsQuestion **ret, const char *service, const char *type, const char *domain, bool with_txt, bool convert_idna);
int dns_question_new_service_type(DnsQuestion **ret, const char *service, const char *type, const char *domain, bool convert_idna, uint16_t record_type);
int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags);
int dns_question_add(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags);

View File

@ -398,6 +398,7 @@ DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
.n_ref = 1,
.key = dns_resource_key_ref(key),
.expiry = USEC_INFINITY,
.until = USEC_INFINITY,
.n_skip_labels_signer = UINT8_MAX,
.n_skip_labels_source = UINT8_MAX,
};
@ -1704,6 +1705,7 @@ DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr) {
copy->ttl = rr->ttl;
copy->expiry = rr->expiry;
copy->until = rr->until;
copy->n_skip_labels_signer = rr->n_skip_labels_signer;
copy->n_skip_labels_source = rr->n_skip_labels_source;
copy->unparsable = rr->unparsable;

View File

@ -107,6 +107,8 @@ struct DnsResourceRecord {
unsigned n_ref;
uint32_t ttl;
usec_t expiry; /* RRSIG signature expiry */
usec_t until; /* Used to pass until of a record when doing a dns_cache_lookup().
* Needed to schedule cache maintenance queries when browsing for services. */
DnsResourceKey *key;

View File

@ -10,6 +10,7 @@
#include "hostname-util.h"
#include "missing_network.h"
#include "random-util.h"
#include "resolved-dns-browse-services.h"
#include "resolved-dnssd.h"
#include "resolved-dns-scope.h"
#include "resolved-dns-synthesize.h"
@ -121,6 +122,9 @@ DnsScope* dns_scope_free(DnsScope *s) {
dns_cache_flush(&s->cache);
dns_zone_flush(&s->zone);
/* Clear records of mDNS service browse subscriber, since cache bas been flushed */
dns_browse_services_purge(s->manager, s->family);
LIST_REMOVE(scopes, s->manager->dns_scopes, s);
return mfree(s);
}

View File

@ -14,6 +14,7 @@
#include "mkdir.h"
#include "netif-util.h"
#include "parse-util.h"
#include "resolved-dns-browse-services.h"
#include "resolved-link.h"
#include "resolved-llmnr.h"
#include "resolved-mdns.h"
@ -166,6 +167,7 @@ void link_allocate_scopes(Link *l) {
r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET);
if (r < 0)
log_link_warning_errno(l, r, "Failed to allocate mDNS IPv4 scope, ignoring: %m");
dns_service_browser_reset(l->manager);
}
} else
l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope);
@ -176,6 +178,7 @@ void link_allocate_scopes(Link *l) {
r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6);
if (r < 0)
log_link_warning_errno(l, r, "Failed to allocate mDNS IPv6 scope, ignoring: %m");
dns_service_browser_reset(l->manager);
}
} else
l->mdns_ipv6_scope = dns_scope_free(l->mdns_ipv6_scope);

View File

@ -769,6 +769,7 @@ int manager_start(Manager *m) {
Manager *manager_free(Manager *m) {
Link *l;
DnssdService *s;
DnsServiceBrowser *sb;
if (!m)
return NULL;
@ -840,6 +841,10 @@ Manager *manager_free(Manager *m) {
dns_trust_anchor_flush(&m->trust_anchor);
manager_etc_hosts_flush(m);
while ((sb = hashmap_first(m->dns_service_browsers)))
dns_service_browser_free(sb);
hashmap_free(m->dns_service_browsers);
return mfree(m);
}
@ -1474,29 +1479,28 @@ bool manager_packet_from_our_transaction(Manager *m, DnsPacket *p) {
return t->sent && dns_packet_equal(t->sent, p);
}
DnsScope* manager_find_scope(Manager *m, DnsPacket *p) {
DnsScope* manager_find_scope_from_protocol(Manager *m, int ifindex, DnsProtocol protocol, int family) {
Link *l;
assert(m);
assert(p);
l = hashmap_get(m->links, INT_TO_PTR(p->ifindex));
l = hashmap_get(m->links, INT_TO_PTR(ifindex));
if (!l)
return NULL;
switch (p->protocol) {
switch (protocol) {
case DNS_PROTOCOL_LLMNR:
if (p->family == AF_INET)
if (family == AF_INET)
return l->llmnr_ipv4_scope;
else if (p->family == AF_INET6)
else if (family == AF_INET6)
return l->llmnr_ipv6_scope;
break;
case DNS_PROTOCOL_MDNS:
if (p->family == AF_INET)
if (family == AF_INET)
return l->mdns_ipv4_scope;
else if (p->family == AF_INET6)
else if (family == AF_INET6)
return l->mdns_ipv6_scope;
break;
@ -1706,6 +1710,9 @@ void manager_flush_caches(Manager *m, int log_level) {
LIST_FOREACH(scopes, scope, m->dns_scopes)
dns_cache_flush(&scope->cache);
dns_browse_services_purge(m, AF_UNSPEC); /* Clear records of DNS service browse subscriber, since caches are flushed */
dns_service_browser_reset(m);
log_full(log_level, "Flushed all caches.");
}

View File

@ -16,6 +16,7 @@
typedef struct Manager Manager;
#include "resolved-dns-browse-services.h"
#include "resolved-dns-query.h"
#include "resolved-dns-search-domain.h"
#include "resolved-dns-stream.h"
@ -161,6 +162,9 @@ struct Manager {
size_t n_socket_graveyard;
struct sigrtmin18_info sigrtmin18_info;
/* Map varlink links to DnsServiceBrowser instances. */
Hashmap *dns_service_browsers;
};
/* Manager */
@ -188,7 +192,13 @@ int manager_next_hostname(Manager *m);
bool manager_packet_from_local_address(Manager *m, DnsPacket *p);
bool manager_packet_from_our_transaction(Manager *m, DnsPacket *p);
DnsScope* manager_find_scope(Manager *m, DnsPacket *p);
DnsScope* manager_find_scope_from_protocol(Manager *m, int ifindex, DnsProtocol protocol, int family);
static inline DnsScope* manager_find_scope(Manager *m, DnsPacket *p) {
assert(m);
assert(p);
return manager_find_scope_from_protocol(m, p->ifindex, p->protocol, p->family);
}
void manager_verify_all(Manager *m);

View File

@ -359,6 +359,10 @@ static int mdns_goodbye_callback(sd_event_source *s, uint64_t usec, void *userda
dns_cache_prune(&scope->cache);
r = mdns_notify_browsers_goodbye(scope);
if (r < 0)
log_error_errno(r, "mDNS: Failed to notify service subscribers of goodbyes, %m");
if (dns_cache_expiry_in_one_second(&scope->cache, usec)) {
r = sd_event_add_time_relative(
scope->manager->event,
@ -380,6 +384,7 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
Manager *m = userdata;
DnsScope *scope;
int r;
bool unsolicited_packet = true;
r = manager_recv(m, fd, DNS_PROTOCOL_MDNS, &p);
if (r <= 0)
@ -455,30 +460,6 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
}
}
for (bool match = true; match;) {
match = false;
LIST_FOREACH(transactions_by_scope, t, scope->transactions) {
if (t->state != DNS_TRANSACTION_PENDING)
continue;
r = dns_answer_match_key(p->answer, dns_transaction_key(t), NULL);
if (r <= 0) {
if (r < 0)
log_debug_errno(r, "Failed to match resource key, ignoring: %m");
continue;
}
/* This packet matches the transaction, let's pass it on as reply */
dns_transaction_process_reply(t, p, false);
/* The dns_transaction_process_reply() -> dns_transaction_complete() ->
* dns_query_candidate_stop() may free multiple transactions. Hence, restart
* the loop. */
match = true;
break;
}
}
dns_cache_put(
&scope->cache,
scope->manager->enable_cache,
@ -494,6 +475,35 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
&p->sender,
scope->manager->stale_retention_usec);
for (bool match = true; match;) {
match = false;
LIST_FOREACH(transactions_by_scope, t, scope->transactions) {
if (t->state != DNS_TRANSACTION_PENDING)
continue;
r = dns_answer_match_key(p->answer, dns_transaction_key(t), NULL);
if (r <= 0) {
if (r < 0)
log_debug_errno(r, "Failed to match resource key, ignoring: %m");
continue;
}
unsolicited_packet = false;
/* This packet matches the transaction, let's pass it on as reply */
dns_transaction_process_reply(t, p, false);
/* The dns_transaction_process_reply() -> dns_transaction_complete() ->
* dns_query_candidate_stop() may free multiple transactions. Hence, restart
* the loop. */
match = true;
break;
}
}
/* Check incoming packet key matches with active clients if yes update the same */
if (unsolicited_packet)
mdns_notify_browsers_unsolicited_updates(m, p->answer, p->family);
} else if (dns_packet_validate_query(p) > 0) {
log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));

View File

@ -4,6 +4,7 @@
#include "glyph-util.h"
#include "in-addr-util.h"
#include "json-util.h"
#include "resolved-dns-browse-services.h"
#include "resolved-dns-synthesize.h"
#include "resolved-varlink.h"
#include "socket-netlink.h"
@ -30,11 +31,26 @@ typedef struct LookupParametersResolveService {
uint64_t flags;
} LookupParametersResolveService;
typedef struct LookupParametersMdnsBrowse {
char *domainName;
char *name;
char *type;
int ifindex;
uint64_t flags;
} LookupParametersMdnsBrowse;
static void lookup_parameters_destroy(LookupParameters *p) {
assert(p);
free(p->name);
}
static void lookup_parameters_mdns_destroy(LookupParametersMdnsBrowse *p) {
assert(p);
free(p->domainName);
free(p->name);
free(p->type);
}
static int reply_query_state(DnsQuery *q) {
assert(q);
@ -108,10 +124,18 @@ static int reply_query_state(DnsQuery *q) {
static void vl_on_disconnect(sd_varlink_server *s, sd_varlink *link, void *userdata) {
DnsQuery *q;
Manager *m;
assert(s);
assert(link);
m = sd_varlink_server_get_userdata(s);
if (!m)
return;
DnsServiceBrowser *sb = hashmap_remove(m->dns_service_browsers, link);
dns_service_browser_free(sb);
q = sd_varlink_get_userdata(link);
if (!q)
return;
@ -1209,6 +1233,43 @@ static int verify_polkit(sd_varlink *link, sd_json_variant *parameters, const ch
&m->polkit_registry);
}
static int vl_method_start_browse(sd_varlink* link, sd_json_variant* parameters, sd_varlink_method_flags_t flags, void* userdata) {
static const sd_json_dispatch_field dispatch_table[] = {
{ "domainName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(LookupParametersMdnsBrowse, domainName), SD_JSON_MANDATORY },
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(LookupParametersMdnsBrowse, name), 0 },
{ "type", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(LookupParametersMdnsBrowse, type), 0 },
{ "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(LookupParametersMdnsBrowse, ifindex), SD_JSON_MANDATORY },
{ "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LookupParametersMdnsBrowse, flags), SD_JSON_MANDATORY },
{}
};
_cleanup_(lookup_parameters_mdns_destroy) LookupParametersMdnsBrowse p = {};
Manager *m;
int r = 0;
assert(link);
/* if the client didn't set the more flag, it is using us incorrectly */
if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
m = sd_varlink_server_get_userdata(sd_varlink_get_server(link));
assert(m);
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
if (r < 0)
return log_error_errno(r, "vl_method_start_browse json_dispatch fail: %m");
if (!validate_and_mangle_flags(NULL, &p.flags, 0))
return sd_varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("flags"));
r = dns_subscribe_browse_service(m, link, p.domainName, p.name, p.type, p.ifindex, p.flags);
if (r < 0)
return sd_varlink_error_errno(link, r);
return 1;
}
static int vl_method_subscribe_query_results(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
Manager *m = ASSERT_PTR(sd_varlink_get_userdata(ASSERT_PTR(link)));
int r;
@ -1422,7 +1483,8 @@ static int varlink_main_server_init(Manager *m) {
"io.systemd.Resolve.ResolveHostname", vl_method_resolve_hostname,
"io.systemd.Resolve.ResolveAddress", vl_method_resolve_address,
"io.systemd.Resolve.ResolveService", vl_method_resolve_service,
"io.systemd.Resolve.ResolveRecord", vl_method_resolve_record);
"io.systemd.Resolve.ResolveRecord", vl_method_resolve_record,
"io.systemd.Resolve.StartBrowse", vl_method_start_browse);
if (r < 0)
return log_error_errno(r, "Failed to register varlink methods: %m");

View File

@ -102,6 +102,15 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_DEFINE_FIELD(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_FIELD(name, SD_VARLINK_STRING, 0));
static SD_VARLINK_DEFINE_STRUCT_TYPE(
ServiceData,
SD_VARLINK_DEFINE_FIELD(add_flag, SD_VARLINK_BOOL, 0),
SD_VARLINK_DEFINE_FIELD(family, SD_VARLINK_INT, 0),
SD_VARLINK_DEFINE_FIELD(name, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_FIELD(type, SD_VARLINK_STRING, 0),
SD_VARLINK_DEFINE_FIELD(domain, SD_VARLINK_STRING, 0),
SD_VARLINK_DEFINE_FIELD(interface, SD_VARLINK_INT, 0));
static SD_VARLINK_DEFINE_METHOD(
ResolveAddress,
SD_VARLINK_FIELD_COMMENT("The Linux interface index for the network interface to search on. Typically left unspecified, in order to search on all interfaces."),
@ -159,6 +168,15 @@ static SD_VARLINK_DEFINE_METHOD(
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(rrs, ResolvedRecord, SD_VARLINK_ARRAY),
SD_VARLINK_DEFINE_OUTPUT(flags, SD_VARLINK_INT, 0));
static SD_VARLINK_DEFINE_METHOD(
StartBrowse,
SD_VARLINK_DEFINE_INPUT(domainName, SD_VARLINK_STRING, 0),
SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(type, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(ifindex, SD_VARLINK_INT, 0),
SD_VARLINK_DEFINE_INPUT(flags, SD_VARLINK_INT, 0),
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(browser_service_data, ServiceData, SD_VARLINK_ARRAY));
static SD_VARLINK_DEFINE_ERROR(NoNameServers);
static SD_VARLINK_DEFINE_ERROR(NoSuchResourceRecord);
static SD_VARLINK_DEFINE_ERROR(QueryTimedOut);
@ -198,6 +216,8 @@ SD_VARLINK_DEFINE_INTERFACE(
&vl_method_ResolveService,
SD_VARLINK_SYMBOL_COMMENT("Resolves a domain name to one or more DNS resource records."),
&vl_method_ResolveRecord,
SD_VARLINK_SYMBOL_COMMENT("Starts browsing for mDNS services of specified type."),
&vl_method_StartBrowse,
SD_VARLINK_SYMBOL_COMMENT("Encapsulates a resolved address."),
&vl_type_ResolvedAddress,
SD_VARLINK_SYMBOL_COMMENT("Encapsulates a resolved host name."),
@ -212,6 +232,7 @@ SD_VARLINK_DEFINE_INTERFACE(
&vl_type_ResourceRecord,
SD_VARLINK_SYMBOL_COMMENT("Encapsulates information about a resolved DNS resource record "),
&vl_type_ResolvedRecord,
&vl_type_ServiceData,
&vl_error_NoNameServers,
&vl_error_NoSuchResourceRecord,
&vl_error_QueryTimedOut,

View File

@ -98,15 +98,17 @@ static int delete_dm(DeviceMapper *m) {
assert(major(m->devnum) != 0);
assert(m->path);
fd = open(m->path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0)
log_debug_errno(errno, "Failed to open DM block device %s for syncing, ignoring: %m", m->path);
else {
(void) sync_with_progress(fd);
fd = safe_close(fd);
}
fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
if (fd < 0)
return -errno;
_cleanup_close_ int block_fd = open(m->path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (block_fd < 0)
log_debug_errno(errno, "Failed to open DM block device %s for syncing, ignoring: %m", m->path);
else
(void) sync_with_progress(block_fd);
return log_debug_errno(errno, "Failed to open /dev/mapper/control: %m");
return RET_NERRNO(ioctl(fd, DM_DEV_REMOVE, &(struct dm_ioctl) {
.version = {

View File

@ -211,10 +211,8 @@ static int sync_making_progress(unsigned long long *prev_dirty) {
continue;
errno = 0;
if (sscanf(line, "%*s %llu %*s", &ull) != 1) {
log_warning_errno(errno_or_else(EIO), "Failed to parse /proc/meminfo field, ignoring: %m");
return false;
}
if (sscanf(line, "%*s %llu %*s", &ull) != 1)
return log_warning_errno(errno_or_else(EIO), "Failed to parse /proc/meminfo field: %m");
val += ull;
}

View File

@ -522,6 +522,7 @@ static int reply_callback(
if (!arg_quiet)
sd_json_variant_dump(parameters, arg_json_format_flags, stdout, NULL);
fflush(stdout);
return r;
}

View File

@ -0,0 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
all setup run clean clean-again:
@TEST_BASE_DIR=../ ./test.sh --$@
.PHONY: all setup run clean clean-again

View File

@ -0,0 +1,8 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
integration_tests += [
integration_test_template + {
'name' : fs.name(meson.current_source_dir()),
'vm' : true,
},
]

View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Test for systemd-resolved's mDNS functionality"
IMAGE_NAME="resolved-mdns"
TEST_NO_NSPAWN=1
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@"

View File

@ -379,6 +379,7 @@ foreach dirname : [
'TEST-84-STORAGETM',
'TEST-85-NETWORK',
'TEST-86-MULTI-PROFILE-UKI',
'TEST-87-RESOLVED-MDNS',
]
subdir(dirname)
endforeach

View File

@ -960,10 +960,13 @@ exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ {{ s/^.*=/
# wait until devices got created
for _ in range(50):
out = subprocess.check_output(['ip', 'a', 'show', 'dev', self.if_router])
if b'state UP' in out and b'scope global' in out:
if subprocess.run(['ip', 'link', 'show', 'dev', self.if_router],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0:
break
time.sleep(0.1)
else:
subprocess.call(['ip', 'link', 'show', 'dev', self.if_router])
self.fail('Timed out waiting for {ifr} created.'.format(ifr=self.if_router))
def shutdown_iface(self):
'''Remove test interface and stop DHCP server'''

View File

@ -80,6 +80,7 @@ show_journal = True # When true, show journal on stopping networkd.
active_units = []
protected_links = {
'br0',
'erspan0',
'gre0',
'gretap0',

View File

@ -0,0 +1,8 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-87-RESOLVED-MDNS
[Service]
ExecStartPre=rm -f /failed /testok
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
Type=oneshot

View File

@ -0,0 +1,255 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
# shellcheck source=test/units/test-control.sh
. "$(dirname "$0")"/test-control.sh
SERVICE_TYPE_COUNT=10
SERVICE_COUNT=20
CONTAINER_ZONE="test-$RANDOM"
CONTAINER_1="test-mdns-1"
CONTAINER_2="test-mdns-2"
# Prepare containers
create_container() {
local container="${1:?}"
local stype sid svc
# Prepare container's /etc
#
# Since we also need the various test suite related dropins from the host's /etc,
# we'll overlay our customizations on top of that
mkdir -p "/var/lib/machines/$container/etc/systemd/dnssd"
# Create 20 test services for each service type (_testServiceX._udp) and number them sequentially,
# i.e. create services 0-19 for _testService0._udp, services 20-39 for _testService1._udp, and so on
for stype in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
for sid in $(seq 0 $((SERVICE_COUNT - 1))); do
svc=$((stype * SERVICE_COUNT + sid))
cat >"/var/lib/machines/$container/etc/systemd/dnssd/test-service-$container-$svc.dnssd" <<EOF
[Service]
Name=Test Service $svc on %H
Type=_testService$stype._udp
Port=24002
TxtText=DC=Monitor PN=867313 SN=XZ051Z0051
EOF
done
done
# To make things fast, spawn the container with a transient version of what's currently the host's
# rootfs, sans a couple of tweaks to make the container unique enough
mkdir -p "/run/systemd/system/systemd-nspawn@$container.service.d"
cat >"/run/systemd/system/systemd-nspawn@$container.service.d/override.conf" <<EOF
[Service]
ExecStart=
ExecStart=systemd-nspawn --quiet --link-journal=try-guest --keep-unit --machine=%i --boot \
--volatile=yes --directory=/ \
--inaccessible=/etc/machine-id \
--inaccessible=/etc/hostname \
--resolv-conf=replace-stub \
--network-zone=$CONTAINER_ZONE \
--overlay=/etc:/var/lib/machines/$container/etc::/etc \
--hostname=$container
EOF
}
check_both() {
local service_id="${1:?}"
local result_file="${2:?}"
# We should get 20 services per container, 40 total
if [[ "$(wc -l <"$result_file")" -ge 40 ]]; then
# Check if the services we got are the correct ones
for i in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
svc=$((service_id * SERVICE_COUNT + i))
if ! grep "Test Service $svc on $CONTAINER_1" "$result_file" || \
! grep "Test Service $svc on $CONTAINER_2" "$result_file"; then
return 1
fi
done
# We got all records and all of them are what we expect
return 0
fi
return 1
}
check_first() {
local service_id="${1:?}"
local result_file="${2:?}"
# We should get 20 services per container
if [[ "$(wc -l <"$result_file")" -ge 20 ]]; then
# Check if the services we got are the correct ones
for i in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
svc=$((service_id * SERVICE_COUNT + i))
if ! grep "Test Service $svc on $CONTAINER_1" "$result_file"; then
return 1
fi
# This check assumes the second container is unreachable, so this shouldn't happen
if grep "Test Service $svc on $CONTAINER_2" "$result_file"; then
echo >&2 "Found a record from an unreachable container"
cat "$result_file"
exit 1
fi
done
# We got all records and all of them are what we expect
return 0
fi
return 1
}
run_and_check_services() {
local service_id="${1:?}"
local check_func="${2:?}"
local unit_name="varlinkctl-$service_id-$SRANDOM.service"
local i out_file parameters service_type svc tmp_file
out_file="$(mktemp)"
error_file="$(mktemp)"
tmp_file="$(mktemp)"
service_type="_testService$service_id._udp"
parameters="{ \"domainName\": \"$service_type.local\", \"name\": \"\", \"type\": \"\", \"ifindex\": ${BRIDGE_INDEX:?}, \"flags\": 16785432 }"
systemd-run --unit="$unit_name" --service-type=exec -p StandardOutput="file:$out_file" -p StandardError="file:$error_file" \
varlinkctl call --more /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.StartBrowse "$parameters"
# shellcheck disable=SC2064
# Note: unregister the trap once it's fired, otherwise it'll get propagated to functions that call this
# one, *sigh*
trap "trap - RETURN; systemctl stop $unit_name" RETURN
for _ in {0..14}; do
# The response format, for reference (it's JSON-SEQ):
#
# {
# "browser_service_data": [
# {
# "add_flag": true,
# "family": 10,
# "name": "Test Service 13 on test-mdns-1",
# "type": "_testService0._udp",
# "domain": "local",
# "interface": 3
# },
# ...
# ]
# }
if [[ -s "$out_file" ]]; then
# Extract the service name from each valid record...
# jq --slurp --raw-output \
# ".[].browser_service_data[] | select(.add_flag == true and .type == \"$service_type\" and .family == 10).name" "$out_file" | sort | tee "$tmp_file"
grep -o '"name":"[^"]*"' "$out_file" | sed 's/"name":"//;s/"//g' | sort | tee "$tmp_file"
# ...and compare them with what we expect
if "$check_func" "$service_id" "$tmp_file"; then
return 0
fi
fi
sleep 2
done
cat "$out_file"
cat "$error_file"
return 1
}
testcase_all_sequential() {
: "Test each service type (sequentially)"
resolvectl flush-caches
for id in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
run_and_check_services "$id" check_both
done
echo testcase_end
}
testcase_all_parallel() {
: "Test each service type (in parallel)"
resolvectl flush-caches
for id in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
run_and_check_services "$id" check_both &
done
wait
}
testcase_single_service_multiple_times() {
: "Test one service type multiple times"
resolvectl flush-caches
for _ in {0..4}; do
run_and_check_services 4 check_both
done
}
testcase_second_unreachable() {
: "Test each service type while the second container is unreachable"
resolvectl flush-caches
systemd-run -M "$CONTAINER_2" --wait --pipe -- networkctl down host0
for id in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
run_and_check_services "$id" check_first
done
: "Test each service type after bringing the second container back up again"
systemd-run -M "$CONTAINER_2" --wait --pipe -- networkctl up host0
systemd-run -M "$CONTAINER_2" --wait --pipe -- \
/usr/lib/systemd/systemd-networkd-wait-online --ipv4 --ipv6 --interface=host0 --operational-state=degraded --timeout=30
for id in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
run_and_check_services "$id" check_both
done
}
: "Setup host & containers"
# Note: create the drop-in intentionally under /run/ and copy it manually into the containers
mkdir -p /run/systemd/resolved.conf.d/
cat >/run/systemd/resolved.conf.d/99-mdns-llmnr.conf <<EOF
[Resolve]
MulticastDNS=yes
LLMNR=yes
EOF
systemctl unmask systemd-resolved.service systemd-networkd.{service,socket} systemd-machined.service
systemctl enable --now systemd-resolved.service systemd-networkd.{socket,service} systemd-machined.service
ln -svrf /run/systemd/resolved.conf.d/99-mdns-llmnr.conf /etc/resolv.conf
systemctl reload systemd-resolved.service systemd-networkd.service
for container in "$CONTAINER_1" "$CONTAINER_2"; do
create_container "$container"
mkdir -p "/var/lib/machines/$container/etc/systemd/resolved.conf.d/"
cp /run/systemd/resolved.conf.d/99-mdns-llmnr.conf "/var/lib/machines/$container/etc/systemd/resolved.conf.d/"
touch "/var/lib/machines/$container/etc/hostname"
systemctl daemon-reload
machinectl start "$container"
# Wait for the system bus to start...
timeout 30s bash -xec "while ! systemd-run -M '$container' --wait --pipe true; do sleep 1; done"
# ...and from there wait for the machine bootup to finish. We don't really care if the container
# boots up in a degraded state, hence the `:`
timeout 30s systemd-run -M "$container" --wait --pipe -- systemctl --wait is-system-running || :
# Wait until the veth interface is configured and turn on mDNS and LLMNR
systemd-run -M "$container" --wait --pipe -- \
/usr/lib/systemd/systemd-networkd-wait-online --ipv4 --ipv6 --interface=host0 --operational-state=degraded --timeout=30
systemd-run -M "$container" --wait --pipe -- resolvectl mdns host0 yes
systemd-run -M "$container" --wait --pipe -- resolvectl llmnr host0 yes
systemd-run -M "$container" --wait --pipe -- networkctl status --no-pager
systemd-run -M "$container" --wait --pipe -- resolvectl status --no-pager
[[ "$(systemd-run -M "$container" --wait --pipe -- resolvectl mdns host0)" =~ :\ yes$ ]]
[[ "$(systemd-run -M "$container" --wait --pipe -- resolvectl llmnr host0)" =~ :\ yes$ ]]
done
BRIDGE_INDEX="$(<"/sys/class/net/vz-$CONTAINER_ZONE/ifindex")"
machinectl list
resolvectl mdns "vz-$CONTAINER_ZONE" on
resolvectl llmnr "vz-$CONTAINER_ZONE" on
networkctl status
resolvectl status
# Run the actual test cases (functions prefixed by testcase_)
run_testcases
touch /testok

View File

@ -13,11 +13,12 @@
d /run/lock 0755 root root -
L /var/lock - - - - ../run/lock
{% if HAVE_SYSV_COMPAT %}
{% if CREATE_LOG_DIRS %}
L$ /var/log/README - - - - ../..{{DOC_DIR}}/README.logs
{% endif %}
{% if HAVE_SYSV_COMPAT %}
# /run/lock/subsys is used for serializing SysV service execution, and
# hence without use on SysV-less systems.
d /run/lock/subsys 0755 root root -