diff options
author | Thomas Haller <thaller@redhat.com> | 2020-06-13 15:53:00 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2020-06-13 17:08:22 +0200 |
commit | ecf32c2c8f8c97d64f0794e0c64c80ef05293ffc (patch) | |
tree | 58de978b29d0ee9bc19cb5a27cddb3ae4f069e44 | |
parent | 707d7c9ef91826bf11a2d3fdcbc29c9c7c280eac (diff) | |
parent | bbf57b114e13bea52120d19e90662a274dad4536 (diff) | |
download | NetworkManager-ecf32c2c8f8c97d64f0794e0c64c80ef05293ffc.tar.gz |
systemd: merge branch systemd into master
68 files changed, 2007 insertions, 790 deletions
diff --git a/Makefile.am b/Makefile.am index 78144f6ca4..86226a2c38 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1824,6 +1824,7 @@ shared_systemd_libnm_systemd_shared_la_SOURCES = \ shared/systemd/nm-sd-utils-shared.h \ shared/systemd/sd-adapt-shared/architecture.h \ shared/systemd/sd-adapt-shared/arphrd-list.h \ + shared/systemd/sd-adapt-shared/blockdev-util.h \ shared/systemd/sd-adapt-shared/build.h \ shared/systemd/sd-adapt-shared/copy.h \ shared/systemd/sd-adapt-shared/def.h \ @@ -2026,12 +2027,14 @@ src_libnm_systemd_core_la_SOURCES = \ src/systemd/src/systemd/sd-dhcp-option.h \ src/systemd/src/systemd/sd-dhcp6-client.h \ src/systemd/src/systemd/sd-dhcp6-lease.h \ + src/systemd/src/systemd/sd-dhcp6-option.h \ src/systemd/src/systemd/sd-event.h \ src/systemd/src/systemd/sd-id128.h \ src/systemd/src/systemd/sd-ipv4acd.h \ src/systemd/src/systemd/sd-ipv4ll.h \ src/systemd/src/systemd/sd-lldp.h \ - src/systemd/src/systemd/sd-ndisc.h + src/systemd/src/systemd/sd-ndisc.h \ + $(NULL) src_libnm_systemd_core_la_CPPFLAGS = $(src_libnm_systemd_core_la_cppflags) src_libnm_systemd_core_la_LIBADD = \ diff --git a/shared/systemd/sd-adapt-shared/blockdev-util.h b/shared/systemd/sd-adapt-shared/blockdev-util.h new file mode 100644 index 0000000000..637892c2d6 --- /dev/null +++ b/shared/systemd/sd-adapt-shared/blockdev-util.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/shared/systemd/src/basic/cgroup-util.h b/shared/systemd/src/basic/cgroup-util.h index 237139fad0..2b88571bc1 100644 --- a/shared/systemd/src/basic/cgroup-util.h +++ b/shared/systemd/src/basic/cgroup-util.h @@ -180,9 +180,31 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path); int cg_rmdir(const char *controller, const char *path); +typedef enum { + CG_KEY_MODE_GRACEFUL = 1 << 0, +} CGroupKeyMode; + int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value); int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret); -int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, char **keys, char **values); +int cg_get_keyed_attribute_full(const char *controller, const char *path, const char *attribute, char **keys, char **values, CGroupKeyMode mode); + +static inline int cg_get_keyed_attribute( + const char *controller, + const char *path, + const char *attribute, + char **keys, + char **ret_values) { + return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, 0); +} + +static inline int cg_get_keyed_attribute_graceful( + const char *controller, + const char *path, + const char *attribute, + char **keys, + char **ret_values) { + return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, CG_KEY_MODE_GRACEFUL); +} int cg_get_attribute_as_uint64(const char *controller, const char *path, const char *attribute, uint64_t *ret); @@ -238,6 +260,7 @@ int cg_mask_to_string(CGroupMask mask, char **ret); int cg_kernel_controllers(Set **controllers); bool cg_ns_supported(void); +bool cg_freezer_supported(void); int cg_all_unified(void); int cg_hybrid_unified(void); diff --git a/shared/systemd/src/basic/errno-util.h b/shared/systemd/src/basic/errno-util.h index 65a6384eeb..0ca650f48f 100644 --- a/shared/systemd/src/basic/errno-util.h +++ b/shared/systemd/src/basic/errno-util.h @@ -87,12 +87,16 @@ static inline bool ERRNO_IS_RESOURCE(int r) { ENOMEM); } -/* Three different errors for "operation/system call/ioctl not supported" */ +/* 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); + ENOSYS, + EAFNOSUPPORT, + EPFNOSUPPORT, + EPROTONOSUPPORT, + ESOCKTNOSUPPORT); } /* Two different errors for access problems */ diff --git a/shared/systemd/src/basic/escape.c b/shared/systemd/src/basic/escape.c index 2cc5be186a..1ea0b6a64e 100644 --- a/shared/systemd/src/basic/escape.c +++ b/shared/systemd/src/basic/escape.c @@ -522,22 +522,28 @@ char* shell_maybe_quote(const char *s, EscapeStyle style) { return NULL; t = r; - if (style == ESCAPE_BACKSLASH) + switch (style) { + case ESCAPE_BACKSLASH: + case ESCAPE_BACKSLASH_ONELINE: *(t++) = '"'; - else if (style == ESCAPE_POSIX) { + break; + case ESCAPE_POSIX: *(t++) = '$'; *(t++) = '\''; - } else + break; + default: assert_not_reached("Bad EscapeStyle"); + } t = mempcpy(t, s, p - s); - if (style == ESCAPE_BACKSLASH) - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, false); + if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE)) + t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, + style == ESCAPE_BACKSLASH_ONELINE); else t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true); - if (style == ESCAPE_BACKSLASH) + if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE)) *(t++) = '"'; else *(t++) = '\''; diff --git a/shared/systemd/src/basic/escape.h b/shared/systemd/src/basic/escape.h index b8eb137c3d..0b00b116ed 100644 --- a/shared/systemd/src/basic/escape.h +++ b/shared/systemd/src/basic/escape.h @@ -34,8 +34,13 @@ typedef enum UnescapeFlags { } UnescapeFlags; typedef enum EscapeStyle { - ESCAPE_BACKSLASH = 1, - ESCAPE_POSIX = 2, + ESCAPE_BACKSLASH = 1, /* Add shell quotes ("") so the shell will consider this a single + argument, possibly multiline. Tabs and newlines are not escaped. */ + ESCAPE_BACKSLASH_ONELINE = 2, /* Similar to ESCAPE_BACKSLASH, but always produces a single-line + string instead. Shell escape sequences are produced for tabs and + newlines. */ + ESCAPE_POSIX = 3, /* Similar to ESCAPE_BACKSLASH_ONELINE, but uses POSIX shell escape + * syntax (a string enclosed in $'') instead of plain quotes. */ } EscapeStyle; char *cescape(const char *s); diff --git a/shared/systemd/src/basic/fd-util.c b/shared/systemd/src/basic/fd-util.c index ea2c161206..a401663e27 100644 --- a/shared/systemd/src/basic/fd-util.c +++ b/shared/systemd/src/basic/fd-util.c @@ -23,9 +23,10 @@ #include "path-util.h" #include "process-util.h" #include "socket-util.h" +#include "stat-util.h" #include "stdio-util.h" -#include "util.h" #include "tmpfile-util.h" +#include "util.h" /* The maximum number of iterations in the loop to close descriptors in the fallback case * when /proc/self/fd/ is inaccessible. */ @@ -104,13 +105,16 @@ int fclose_nointr(FILE *f) { /* Same as close_nointr(), but for fclose() */ + errno = 0; /* Extra safety: if the FILE* object is not encapsulating an fd, it might not set errno + * correctly. Let's hence initialize it to zero first, so that we aren't confused by any + * prior errno here */ if (fclose(f) == 0) return 0; if (errno == EINTR) return 0; - return -errno; + return errno_or_else(EIO); } FILE* safe_fclose(FILE *f) { @@ -146,11 +150,7 @@ int fd_nonblock(int fd, bool nonblock) { if (flags < 0) return -errno; - if (nonblock) - nflags = flags | O_NONBLOCK; - else - nflags = flags & ~O_NONBLOCK; - + nflags = UPDATE_FLAG(flags, O_NONBLOCK, nonblock); if (nflags == flags) return 0; @@ -169,11 +169,7 @@ int fd_cloexec(int fd, bool cloexec) { if (flags < 0) return -errno; - if (cloexec) - nflags = flags | FD_CLOEXEC; - else - nflags = flags & ~FD_CLOEXEC; - + nflags = UPDATE_FLAG(flags, FD_CLOEXEC, cloexec); if (nflags == flags) return 0; @@ -954,8 +950,15 @@ int fd_reopen(int fd, int flags) { xsprintf(procfs_path, "/proc/self/fd/%i", fd); new_fd = open(procfs_path, flags); - if (new_fd < 0) - return -errno; + if (new_fd < 0) { + if (errno != ENOENT) + return -errno; + + if (proc_mounted() == 0) + return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ + + return -ENOENT; + } return new_fd; } diff --git a/shared/systemd/src/basic/fileio.c b/shared/systemd/src/basic/fileio.c index 543dd4eb9b..309032638c 100644 --- a/shared/systemd/src/basic/fileio.c +++ b/shared/systemd/src/basic/fileio.c @@ -122,7 +122,7 @@ int write_string_stream_ts( struct timespec *ts) { bool needs_nl; - int r; + int r, fd; assert(f); assert(line); @@ -130,6 +130,14 @@ int write_string_stream_ts( if (ferror(f)) return -EIO; + if (ts) { + /* If we shall set the timestamp we need the fd. But fmemopen() streams generally don't have + * an fd. Let's fail early in that case. */ + fd = fileno(f); + if (fd < 0) + return -EBADF; + } + needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"); if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) { @@ -157,7 +165,7 @@ int write_string_stream_ts( if (ts) { struct timespec twice[2] = {*ts, *ts}; - if (futimens(fileno(f), twice) < 0) + if (futimens(fd, twice) < 0) return -errno; } @@ -197,6 +205,13 @@ static int write_string_file_atomic( goto fail; } + if (FLAGS_SET(flags, WRITE_STRING_FILE_SYNC)) { + /* Sync the rename, too */ + r = fsync_directory_of_file(fileno(f)); + if (r < 0) + return r; + } + return 0; fail: @@ -415,7 +430,7 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re break; } - if (errno != -EINTR) + if (errno != EINTR) return -errno; } @@ -895,7 +910,7 @@ int fflush_and_check(FILE *f) { #if 0 /* NM_IGNORED */ int fflush_sync_and_check(FILE *f) { - int r; + int r, fd; assert(f); @@ -903,10 +918,16 @@ int fflush_sync_and_check(FILE *f) { if (r < 0) return r; - if (fsync(fileno(f)) < 0) + /* Not all file streams have an fd associated (think: fmemopen()), let's handle this gracefully and + * assume that in that case we need no explicit syncing */ + fd = fileno(f); + if (fd < 0) + return 0; + + if (fsync(fd) < 0) return -errno; - r = fsync_directory_of_file(fileno(f)); + r = fsync_directory_of_file(fd); if (r < 0) return r; @@ -1006,7 +1027,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile); int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) { size_t n = 0, allocated = 0, count = 0; _cleanup_free_ char *buffer = NULL; - int r, tty = -1; + int r; assert(f); @@ -1081,13 +1102,23 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) { count++; if (eol != EOL_NONE) { - /* If we are on a tty, we can't wait for more input. But we expect only - * \n as the single EOL marker, so there is no need to wait. We check - * this condition last to avoid isatty() check if not necessary. */ - - if (tty < 0) - tty = isatty(fileno(f)); - if (tty > 0) + /* If we are on a tty, we can't shouldn't wait for more input, because that + * generally means waiting for the user, interactively. In the case of a TTY + * we expect only \n as the single EOL marker, so we are in the lucky + * position that there is no need to wait. We check this condition last, to + * avoid isatty() check if not necessary. */ + + if ((flags & (READ_LINE_IS_A_TTY|READ_LINE_NOT_A_TTY)) == 0) { + int fd; + + fd = fileno(f); + if (fd < 0) /* Maybe an fmemopen() stream? Handle this gracefully, + * 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; + } + if (FLAGS_SET(flags, READ_LINE_IS_A_TTY)) break; } @@ -1168,3 +1199,27 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c filename, st->st_mode & 07777); return 0; } + +#if 0 /* NM_IGNORED */ +int sync_rights(int from, int to) { + struct stat st; + + if (fstat(from, &st) < 0) + return -errno; + + return fchmod_and_chown(to, st.st_mode & 07777, st.st_uid, st.st_gid); +} + +int rename_and_apply_smack_floor_label(const char *from, const char *to) { + int r = 0; + if (rename(from, to) < 0) + return -errno; + +#ifdef SMACK_RUN_LABEL + r = mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL); + if (r < 0) + return r; +#endif + return r; +} +#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/fileio.h b/shared/systemd/src/basic/fileio.h index 58daabaa8f..e2830b7963 100644 --- a/shared/systemd/src/basic/fileio.h +++ b/shared/systemd/src/basic/fileio.h @@ -88,7 +88,9 @@ int read_timestamp_file(const char *fn, usec_t *ret); int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space); typedef enum ReadLineFlags { - READ_LINE_ONLY_NUL = 1 << 0, + READ_LINE_ONLY_NUL = 1 << 0, + READ_LINE_IS_A_TTY = 1 << 1, + READ_LINE_NOT_A_TTY = 1 << 2, } ReadLineFlags; int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret); @@ -104,3 +106,7 @@ static inline int read_nul_string(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); + +int sync_rights(int from, int to); + +int rename_and_apply_smack_floor_label(const char *temp_path, const char *dest_path); diff --git a/shared/systemd/src/basic/fs-util.c b/shared/systemd/src/basic/fs-util.c index 7996657108..75df913acf 100644 --- a/shared/systemd/src/basic/fs-util.c +++ b/shared/systemd/src/basic/fs-util.c @@ -10,8 +10,10 @@ #include <unistd.h> #include "alloc-util.h" +#include "blockdev-util.h" #include "dirent-util.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "locale-util.h" #include "log.h" @@ -23,6 +25,7 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" +#include "random-util.h" #include "stat-util.h" #include "stdio-util.h" #include "string-util.h" @@ -342,34 +345,51 @@ int fchmod_opath(int fd, mode_t m) { * fchownat() does. */ xsprintf(procfs_path, "/proc/self/fd/%i", fd); - if (chmod(procfs_path, m) < 0) - return -errno; + if (chmod(procfs_path, m) < 0) { + if (errno != ENOENT) + return -errno; + + if (proc_mounted() == 0) + return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ + + return -ENOENT; + } return 0; } -int fd_warn_permissions(const char *path, int fd) { - struct stat st; - - if (fstat(fd, &st) < 0) - return -errno; +int stat_warn_permissions(const char *path, const struct stat *st) { + assert(path); + assert(st); /* Don't complain if we are reading something that is not a file, for example /dev/null */ - if (!S_ISREG(st.st_mode)) + if (!S_ISREG(st->st_mode)) return 0; - if (st.st_mode & 0111) + if (st->st_mode & 0111) log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path); - if (st.st_mode & 0002) + if (st->st_mode & 0002) log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path); - if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044) + if (getpid_cached() == 1 && (st->st_mode & 0044) != 0044) log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path); return 0; } +int fd_warn_permissions(const char *path, int fd) { + struct stat st; + + assert(path); + assert(fd >= 0); + + if (fstat(fd, &st) < 0) + return -errno; + + return stat_warn_permissions(path, &st); +} + int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; _cleanup_close_ int fd = -1; @@ -702,29 +722,31 @@ int unlink_or_warn(const char *filename) { int inotify_add_watch_fd(int fd, int what, uint32_t mask) { char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; - int r; + int wd; /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ xsprintf(path, "/proc/self/fd/%i", what); - r = inotify_add_watch(fd, path, mask); - if (r < 0) + wd = inotify_add_watch(fd, path, mask); + if (wd < 0) return -errno; - return r; + return wd; } #if 0 /* NM_IGNORED */ int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) { + int wd; - if (inotify_add_watch(fd, pathname, mask) < 0) { + wd = inotify_add_watch(fd, pathname, mask); + if (wd < 0) { if (errno == ENOSPC) return log_error_errno(errno, "Failed to add a watch for %s: inotify watch limit reached", pathname); return log_error_errno(errno, "Failed to add a watch for %s: %m", pathname); } - return 0; + return wd; } static bool unsafe_transition(const struct stat *a, const struct stat *b) { @@ -1303,11 +1325,13 @@ void unlink_tempfilep(char (*p)[]) { (void) unlink_noerrno(*p); } -int unlinkat_deallocate(int fd, const char *name, int flags) { +int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) { _cleanup_close_ int truncate_fd = -1; struct stat st; off_t l, bs; + assert((flags & ~(UNLINK_REMOVEDIR|UNLINK_ERASE)) == 0); + /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other * link to it. This is useful to ensure that other processes that might have the file open for reading won't be * able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up @@ -1324,7 +1348,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) { * Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the * primary job – to delete the file – is accomplished. */ - if ((flags & AT_REMOVEDIR) == 0) { + if (!FLAGS_SET(flags, UNLINK_REMOVEDIR)) { truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK); if (truncate_fd < 0) { @@ -1340,7 +1364,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) { } } - if (unlinkat(fd, name, flags) < 0) + if (unlinkat(fd, name, FLAGS_SET(flags, UNLINK_REMOVEDIR) ? AT_REMOVEDIR : 0) < 0) return -errno; if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */ @@ -1351,7 +1375,45 @@ int unlinkat_deallocate(int fd, const char *name, int flags) { return 0; } - if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0) + if (!S_ISREG(st.st_mode)) + return 0; + + if (FLAGS_SET(flags, UNLINK_ERASE) && st.st_size > 0 && st.st_nlink == 0) { + uint64_t left = st.st_size; + char buffer[64 * 1024]; + + /* If erasing is requested, let's overwrite the file with random data once before deleting + * it. This isn't going to give you shred(1) semantics, but hopefully should be good enough + * for stuff backed by tmpfs at least. + * + * Note that we only erase like this if the link count of the file is zero. If it is higher it + * is still linked by someone else and we'll leave it to them to remove it securely + * eventually! */ + + random_bytes(buffer, sizeof(buffer)); + + while (left > 0) { + ssize_t n; + + n = write(truncate_fd, buffer, MIN(sizeof(buffer), left)); + if (n < 0) { + log_debug_errno(errno, "Failed to erase data in file '%s', ignoring.", name); + break; + } + + assert(left >= (size_t) n); + left -= n; + } + + /* Let's refresh metadata */ + if (fstat(truncate_fd, &st) < 0) { + log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name); + return 0; + } + } + + /* Don't dallocate if there's nothing to deallocate or if the file is linked elsewhere */ + if (st.st_blocks == 0 || st.st_nlink > 0) return 0; /* If this is a regular file, it actually took up space on disk and there are no other links it's time to @@ -1490,4 +1552,89 @@ int open_parent(const char *path, int flags, mode_t mode) { return fd; } + +static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) { + _cleanup_free_ char *p = NULL, *uuids = NULL; + _cleanup_closedir_ DIR *d = NULL; + int r, found_encrypted = false; + + assert(sysfs_path); + + if (depth_left == 0) + return -EINVAL; + + p = path_join(sysfs_path, "dm/uuid"); + if (!p) + return -ENOMEM; + + r = read_one_line_file(p, &uuids); + if (r != -ENOENT) { + if (r < 0) + return r; + + /* The DM device's uuid attribute is prefixed with "CRYPT-" if this is a dm-crypt device. */ + if (startswith(uuids, "CRYPT-")) + return true; + } + + /* Not a dm-crypt device itself. But maybe it is on top of one? Follow the links in the "slaves/" + * subdir. */ + + p = mfree(p); + p = path_join(sysfs_path, "slaves"); + if (!p) + return -ENOMEM; + + d = opendir(p); + if (!d) { + if (errno == ENOENT) /* Doesn't have slaves */ + return false; + + return -errno; + } + + for (;;) { + _cleanup_free_ char *q = NULL; + struct dirent *de; + + errno = 0; + de = readdir_no_dot(d); + if (!de) { + if (errno != 0) + return -errno; + + break; /* No more slaves */ + } + + q = path_join(p, de->d_name); + if (!q) + return -ENOMEM; + + r = blockdev_is_encrypted(q, depth_left - 1); + if (r < 0) + return r; + if (r == 0) /* we found one that is not encrypted? then propagate that immediately */ + return false; + + found_encrypted = true; + } + + return found_encrypted; +} + +int path_is_encrypted(const char *path) { + char p[SYS_BLOCK_PATH_MAX(NULL)]; + dev_t devt; + int r; + + r = get_block_device(path, &devt); + if (r < 0) + return r; + if (r == 0) /* doesn't have a block device */ + return false; + + xsprintf_sys_block_path(p, NULL, devt); + + return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */); +} #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/fs-util.h b/shared/systemd/src/basic/fs-util.h index 6b9ade2ec1..b184570f9f 100644 --- a/shared/systemd/src/basic/fs-util.h +++ b/shared/systemd/src/basic/fs-util.h @@ -40,6 +40,7 @@ int fchmod_umask(int fd, mode_t mode); int fchmod_opath(int fd, mode_t m); int fd_warn_permissions(const char *path, int fd); +int stat_warn_permissions(const char *path, const struct stat *st); #define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) @@ -82,7 +83,7 @@ enum { CHASE_SAFE = 1 << 3, /* Return EPERM if we ever traverse from unprivileged to privileged files or directories */ CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */ CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */ - CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most compontent. With ret_fd, when the path's + CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's * right-most component refers to symlink, return O_PATH fd of the symlink. */ CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered */ }; @@ -113,7 +114,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free); int access_fd(int fd, int mode); void unlink_tempfilep(char (*p)[]); -int unlinkat_deallocate(int fd, const char *name, int flags); + +typedef enum UnlinkDeallocateFlags { + UNLINK_REMOVEDIR = 1 << 0, + UNLINK_ERASE = 1 << 1, +} UnlinkDeallocateFlags; + +int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags); int fsync_directory_of_file(int fd); int fsync_full(int fd); @@ -122,3 +129,5 @@ int fsync_path_at(int at_fd, const char *path); int syncfs_path(int atfd, const char *path); int open_parent(const char *path, int flags, mode_t mode); + +int path_is_encrypted(const char *path); diff --git a/shared/systemd/src/basic/hash-funcs.c b/shared/systemd/src/basic/hash-funcs.c index 1b0d129237..b1c19c9572 100644 --- a/shared/systemd/src/basic/hash-funcs.c +++ b/shared/systemd/src/basic/hash-funcs.c @@ -11,12 +11,14 @@ void string_hash_func(const char *p, struct siphash *state) { siphash24_compress(p, strlen(p) + 1, state); } -#if 0 /* NM_IGNORED */ DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func); +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free, + char, string_hash_func, string_compare_func, free); DEFINE_HASH_OPS_FULL(string_hash_ops_free_free, char, string_hash_func, string_compare_func, free, char, free); +#if 0 /* NM_IGNORED */ void path_hash_func(const char *q, struct siphash *state) { size_t n; @@ -56,6 +58,8 @@ void path_hash_func(const char *q, struct siphash *state) { } DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare); +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free, + char, path_hash_func, path_compare, free); #endif /* NM_IGNORED */ void trivial_hash_func(const void *p, struct siphash *state) { diff --git a/shared/systemd/src/basic/hash-funcs.h b/shared/systemd/src/basic/hash-funcs.h index 7bb5d1cd02..005d1b21d2 100644 --- a/shared/systemd/src/basic/hash-funcs.h +++ b/shared/systemd/src/basic/hash-funcs.h @@ -76,10 +76,12 @@ struct hash_ops { void string_hash_func(const char *p, struct siphash *state); #define string_compare_func strcmp extern const struct hash_ops string_hash_ops; +extern const struct hash_ops string_hash_ops_free; extern const struct hash_ops string_hash_ops_free_free; void path_hash_func(const char *p, struct siphash *state); extern const struct hash_ops path_hash_ops; +extern const struct hash_ops path_hash_ops_free; /* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings * or suchlike. */ diff --git a/shared/systemd/src/basic/hashmap.c b/shared/systemd/src/basic/hashmap.c index 1aa009478a..c0296bde36 100644 --- a/shared/systemd/src/basic/hashmap.c +++ b/shared/systemd/src/basic/hashmap.c @@ -147,12 +147,7 @@ struct hashmap_debug_info { /* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */ static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list); static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER; - -#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug; - -#else /* !ENABLE_DEBUG_HASHMAP */ -#define HASHMAP_DEBUG_FIELDS -#endif /* ENABLE_DEBUG_HASHMAP */ +#endif enum HashmapType { HASHMAP_TYPE_PLAIN, @@ -214,7 +209,10 @@ struct HashmapBase { bool from_pool:1; /* whether was allocated from mempool */ bool dirty:1; /* whether dirtied since last iterated_cache_get() */ bool cached:1; /* whether this hashmap is being cached */ - HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */ + +#if ENABLE_DEBUG_HASHMAP + struct hashmap_debug_info debug; +#endif }; /* Specific hash types @@ -256,7 +254,7 @@ struct hashmap_type_info { unsigned n_direct_buckets; }; -static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { +static _used_ const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { [HASHMAP_TYPE_PLAIN] = { .head_size = sizeof(Hashmap), .entry_size = sizeof(struct plain_hashmap_entry), @@ -709,7 +707,7 @@ static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) { : hashmap_iterate_in_internal_order(h, i); } -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { +bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { struct hashmap_base_entry *e; void *data; unsigned idx; @@ -735,7 +733,7 @@ bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const v } bool set_iterate(const Set *s, Iterator *i, void **value) { - return internal_hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL); + return _hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL); } #define HASHMAP_FOREACH_IDX(idx, h, i) \ @@ -743,7 +741,7 @@ bool set_iterate(const Set *s, Iterator *i, void **value) { (idx != IDX_NIL); \ (idx) = hashmap_iterate_entry((h), &(i))) -IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h) { +IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h) { IteratedCache *cache; assert(h); @@ -811,15 +809,15 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu return h; } -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +Hashmap *_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); } -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); } -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); } @@ -837,18 +835,18 @@ static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops return -ENOMEM; *h = q; - return 0; + return 1; } -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); } -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); } -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); } @@ -870,16 +868,16 @@ static void hashmap_free_no_clear(HashmapBase *h) { free(h); } -HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { +HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { if (h) { - internal_hashmap_clear(h, default_free_key, default_free_value); + _hashmap_clear(h, default_free_key, default_free_value); hashmap_free_no_clear(h); } return NULL; } -void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { +void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { free_func_t free_key, free_value; if (!h) return; @@ -893,11 +891,11 @@ void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_f * hash table, and only then call the destructor functions. If these destructors then try to unregister * themselves from our hash table a second time, the entry is already gone. */ - while (internal_hashmap_size(h) > 0) { + while (_hashmap_size(h) > 0) { void *k = NULL; void *v; - v = internal_hashmap_first_key_and_value(h, true, &k); + v = _hashmap_first_key_and_value(h, true, &k); if (free_key) free_key(k); @@ -1303,7 +1301,7 @@ int hashmap_update(Hashmap *h, const void *key, void *value) { return 0; } -void *internal_hashmap_get(HashmapBase *h, const void *key) { +void *_hashmap_get(HashmapBase *h, const void *key) { struct hashmap_base_entry *e; unsigned hash, idx; @@ -1338,7 +1336,7 @@ void *hashmap_get2(Hashmap *h, const void *key, void **key2) { return e->value; } -bool internal_hashmap_contains(HashmapBase *h, const void *key) { +bool _hashmap_contains(HashmapBase *h, const void *key) { unsigned hash; if (!h) @@ -1348,7 +1346,7 @@ bool internal_hashmap_contains(HashmapBase *h, const void *key) { return bucket_scan(h, hash, key) != IDX_NIL; } -void *internal_hashmap_remove(HashmapBase *h, const void *key) { +void *_hashmap_remove(HashmapBase *h, const void *key) { struct hashmap_base_entry *e; unsigned hash, idx; void *data; @@ -1486,7 +1484,7 @@ int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_ return 0; } -void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value) { +void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value) { struct hashmap_base_entry *e; unsigned hash, idx; @@ -1516,7 +1514,7 @@ static unsigned find_first_entry(HashmapBase *h) { return hashmap_iterate_entry(h, &i); } -void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) { +void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) { struct hashmap_base_entry *e; void *key, *data; unsigned idx; @@ -1541,21 +1539,21 @@ void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **r return data; } -unsigned internal_hashmap_size(HashmapBase *h) { +unsigned _hashmap_size(HashmapBase *h) { if (!h) return 0; return n_entries(h); } -unsigned internal_hashmap_buckets(HashmapBase *h) { +unsigned _hashmap_buckets(HashmapBase *h) { if (!h) return 0; return n_buckets(h); } -int internal_hashmap_merge(Hashmap *h, Hashmap *other) { +int _hashmap_merge(Hashmap *h, Hashmap *other) { Iterator i; unsigned idx; @@ -1591,7 +1589,7 @@ int set_merge(Set *s, Set *other) { return 0; } -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { +int _hashmap_reserve(HashmapBase *h, unsigned entries_add) { int r; assert(h); @@ -1609,7 +1607,7 @@ int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { * Returns: 0 on success. * -ENOMEM on alloc failure, in which case no move has been done. */ -int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { +int _hashmap_move(HashmapBase *h, HashmapBase *other) { struct swap_entries swap; struct hashmap_base_entry *e, *n; Iterator i; @@ -1654,7 +1652,7 @@ int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { return 0; } -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { +int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { struct swap_entries swap; unsigned h_hash, other_hash, idx; struct hashmap_base_entry *e, *n; @@ -1691,7 +1689,7 @@ int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *ke return 0; } -HashmapBase *internal_hashmap_copy(HashmapBase *h) { +HashmapBase *_hashmap_copy(HashmapBase *h) { HashmapBase *copy; int r; @@ -1714,14 +1712,14 @@ HashmapBase *internal_hashmap_copy(HashmapBase *h) { } if (r < 0) { - internal_hashmap_free(copy, false, false); + _hashmap_free(copy, false, false); return NULL; } return copy; } -char **internal_hashmap_get_strv(HashmapBase *h) { +char **_hashmap_get_strv(HashmapBase *h) { char **sv; Iterator i; unsigned idx, n; @@ -1778,42 +1776,55 @@ int hashmap_put_strdup(Hashmap **h, const char *k, const char *v) { return r; _cleanup_free_ char *kdup = NULL, *vdup = NULL; + kdup = strdup(k); - vdup = strdup(v); - if (!kdup || !vdup) + if (!kdup) return -ENOMEM; + if (v) { + vdup = strdup(v); + if (!vdup) + return -ENOMEM; + } + r = hashmap_put(*h, kdup, vdup); if (r < 0) { - if (r == -EEXIST && streq(v, hashmap_get(*h, kdup))) + if (r == -EEXIST && streq_ptr(v, hashmap_get(*h, kdup))) return 0; return r; } - assert(r > 0); /* 0 would mean vdup is already in the hashmap, which cannot be */ - kdup = vdup = NULL; + /* 0 with non-null vdup would mean vdup is already in the hashmap, which cannot be */ + assert(vdup == NULL || r > 0); + if (r > 0) + kdup = vdup = NULL; - return 0; + return r; } #endif /* NM_IGNORED */ -int set_put_strdup(Set *s, const char *p) { +int set_put_strdup(Set **s, const char *p) { char *c; + int r; assert(s); assert(p); - if (set_contains(s, (char*) p)) + r = set_ensure_allocated(s, &string_hash_ops_free); + if (r < 0) + return r; + + if (set_contains(*s, (char*) p)) return 0; c = strdup(p); if (!c) return -ENOMEM; - return set_consume(s, c); + return set_consume(*s, c); } -int set_put_strdupv(Set *s, char **l) { +int set_put_strdupv(Set **s, char **l) { int n = 0, r; char **i; diff --git a/shared/systemd/src/basic/hashmap.h b/shared/systemd/src/basic/hashmap.h index 65adc92513..230d322213 100644 --- a/shared/systemd/src/basic/hashmap.h +++ b/shared/systemd/src/basic/hashmap.h @@ -14,7 +14,7 @@ * will be treated as empty hashmap for all read operations. That way it is not * necessary to instantiate an object for each Hashmap use. * - * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap), + * If ENABLE_DEBUG_HASHMAP is defined (by configuring with -Ddebug-extra=hashmap), * the implementation will: * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py) * - perform extra checks for invalid use of iterators @@ -24,10 +24,9 @@ typedef void* (*hashmap_destroy_t)(void *p); -/* The base type for all hashmap and set types. Many functions in the - * implementation take (HashmapBase*) parameters and are run-time polymorphic, - * though the API is not meant to be polymorphic (do not call functions - * internal_*() directly). */ +/* The base type for all hashmap and set types. Many functions in the implementation take (HashmapBase*) + * parameters and are run-time polymorphic, though the API is not meant to be polymorphic (do not call + * underscore-prefixed functions directly). */ typedef struct HashmapBase HashmapBase; /* Specific hashmap/set types */ @@ -84,62 +83,70 @@ typedef struct { # define HASHMAP_DEBUG_PASS_ARGS #endif -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) +Hashmap *_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define hashmap_new(ops) _hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) +#define ordered_hashmap_new(ops) _ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) + +#define hashmap_free_and_replace(a, b) \ + ({ \ + hashmap_free(a); \ + (a) = (b); \ + (b) = NULL; \ + 0; \ + }) -HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); +HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); static inline Hashmap *hashmap_free(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL); } static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL); } static inline Hashmap *hashmap_free_free(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free); } static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free); } static inline Hashmap *hashmap_free_free_key(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL); } static inline OrderedHashmap *ordered_hashmap_free_free_key(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL); } static inline Hashmap *hashmap_free_free_free(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, free); } static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, free); } IteratedCache *iterated_cache_free(IteratedCache *cache); int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries); -HashmapBase *internal_hashmap_copy(HashmapBase *h); +HashmapBase *_hashmap_copy(HashmapBase *h); static inline Hashmap *hashmap_copy(Hashmap *h) { - return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); + return (Hashmap*) _hashmap_copy(HASHMAP_BASE(h)); } static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { - return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); + return (OrderedHashmap*) _hashmap_copy(HASHMAP_BASE(h)); } -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define hashmap_ensure_allocated(h, ops) _hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +#define ordered_hashmap_ensure_allocated(h, ops) _ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) -IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h); +IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h); static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) { - return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h)); + return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h)); } static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) { - return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h)); + return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h)); } int hashmap_put(Hashmap *h, const void *key, void *value); @@ -159,12 +166,12 @@ static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, vo return hashmap_replace(PLAIN_HASHMAP(h), key, value); } -void *internal_hashmap_get(HashmapBase *h, const void *key); +void *_hashmap_get(HashmapBase *h, const void *key); static inline void *hashmap_get(Hashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); + return _hashmap_get(HASHMAP_BASE(h), key); } static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); + return _hashmap_get(HASHMAP_BASE(h), key); } void *hashmap_get2(Hashmap *h, const void *key, void **rkey); @@ -172,20 +179,20 @@ static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, voi return hashmap_get2(PLAIN_HASHMAP(h), key, rkey); } -bool internal_hashmap_contains(HashmapBase *h, const void *key); +bool _hashmap_contains(HashmapBase *h, const void *key); static inline bool hashmap_contains(Hashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); + return _hashmap_contains(HASHMAP_BASE(h), key); } static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); + return _hashmap_contains(HASHMAP_BASE(h), key); } -void *internal_hashmap_remove(HashmapBase *h, const void *key); +void *_hashmap_remove(HashmapBase *h, const void *key); static inline void *hashmap_remove(Hashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); + return _hashmap_remove(HASHMAP_BASE(h), key); } static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); + return _hashmap_remove(HASHMAP_BASE(h), key); } void *hashmap_remove2(Hashmap *h, const void *key, void **rkey); @@ -193,9 +200,9 @@ static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); } -void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value); +void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value); static inline void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { - return internal_hashmap_remove_value(HASHMAP_BASE(h), key, value); + return _hashmap_remove_value(HASHMAP_BASE(h), key, value); } static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { @@ -214,41 +221,41 @@ static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const vo /* Since merging data from a OrderedHashmap into a Hashmap or vice-versa * should just work, allow this by having looser type-checking here. */ -int internal_hashmap_merge(Hashmap *h, Hashmap *other); -#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) +int _hashmap_merge(Hashmap *h, Hashmap *other); +#define hashmap_merge(h, other) _hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) #define ordered_hashmap_merge(h, other) hashmap_merge(h, other) -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add); +int _hashmap_reserve(HashmapBase *h, unsigned entries_add); static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); + return _hashmap_reserve(HASHMAP_BASE(h), entries_add); } static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); + return _hashmap_reserve(HASHMAP_BASE(h), entries_add); } -int internal_hashmap_move(HashmapBase *h, HashmapBase *other); +int _hashmap_move(HashmapBase *h, HashmapBase *other); /* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */ static inline int hashmap_move(Hashmap *h, Hashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); + return _hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); } static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); + return _hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); } -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); +int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); + return _hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); } static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); + return _hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); } -unsigned internal_hashmap_size(HashmapBase *h) _pure_; +unsigned _hashmap_size(HashmapBase *h) _pure_; static inline unsigned hashmap_size(Hashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); + return _hashmap_size(HASHMAP_BASE(h)); } static inline unsigned ordered_hashmap_size(OrderedHashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); + return _hashmap_size(HASHMAP_BASE(h)); } static inline bool hashmap_isempty(Hashmap *h) { @@ -258,49 +265,49 @@ static inline bool ordered_hashmap_isempty(OrderedHashmap *h) { return ordered_hashmap_size(h) == 0; } -unsigned internal_hashmap_buckets(HashmapBase *h) _pure_; +unsigned _hashmap_buckets(HashmapBase *h) _pure_; static inline unsigned hashmap_buckets(Hashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); + return _hashmap_buckets(HASHMAP_BASE(h)); } static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); + return _hashmap_buckets(HASHMAP_BASE(h)); } -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); +bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); + return _hashmap_iterate(HASHMAP_BASE(h), i, value, key); } static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); + return _hashmap_iterate(HASHMAP_BASE(h), i, value, key); } -void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); +void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); static inline void hashmap_clear(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL); + _hashmap_clear(HASHMAP_BASE(h), NULL, NULL); } static inline void ordered_hashmap_clear(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL); + _hashmap_clear(HASHMAP_BASE(h), NULL, NULL); } static inline void hashmap_clear_free(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, free); + _hashmap_clear(HASHMAP_BASE(h), NULL, free); } static inline void ordered_hashmap_clear_free(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, free); + _hashmap_clear(HASHMAP_BASE(h), NULL, free); } static inline void hashmap_clear_free_key(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, NULL); + _hashmap_clear(HASHMAP_BASE(h), free, NULL); } static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, NULL); + _hashmap_clear(HASHMAP_BASE(h), free, NULL); } static inline void hashmap_clear_free_free(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, free); + _hashmap_clear(HASHMAP_BASE(h), free, free); } static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, free); + _hashmap_clear(HASHMAP_BASE(h), free, free); } /* @@ -314,50 +321,50 @@ static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { * the first entry is O(1). */ -void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key); +void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key); static inline void *hashmap_steal_first_key_and_value(Hashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); } static inline void *ordered_hashmap_steal_first_key_and_value(OrderedHashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); } static inline void *hashmap_first_key_and_value(Hashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); } static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); } static inline void *hashmap_steal_first(Hashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); } static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); } static inline void *hashmap_first(Hashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); } static inline void *ordered_hashmap_first(OrderedHashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); } -static inline void *internal_hashmap_first_key(HashmapBase *h, bool remove) { +static inline void *_hashmap_first_key(HashmapBase *h, bool remove) { void *key = NULL; - (void) internal_hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key); + (void) _hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key); return key; } static inline void *hashmap_steal_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), true); + return _hashmap_first_key(HASHMAP_BASE(h), true); } static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), true); + return _hashmap_first_key(HASHMAP_BASE(h), true); } static inline void *hashmap_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), false); + return _hashmap_first_key(HASHMAP_BASE(h), false); } static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), false); + return _hashmap_first_key(HASHMAP_BASE(h), false); } #define hashmap_clear_with_destructor(_s, _f) \ @@ -386,12 +393,12 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { /* no hashmap_next */ void *ordered_hashmap_next(OrderedHashmap *h, const void *key); -char **internal_hashmap_get_strv(HashmapBase *h); +char **_hashmap_get_strv(HashmapBase *h); static inline char **hashmap_get_strv(Hashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); + return _hashmap_get_strv(HASHMAP_BASE(h)); } static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); + return _hashmap_get_strv(HASHMAP_BASE(h)); } /* diff --git a/shared/systemd/src/basic/hexdecoct.c b/shared/systemd/src/basic/hexdecoct.c index 36174ec3bc..1fb52305cc 100644 --- a/shared/systemd/src/basic/hexdecoct.c +++ b/shared/systemd/src/basic/hexdecoct.c @@ -604,13 +604,13 @@ ssize_t base64mem(const void *p, size_t l, char **out) { static int base64_append_width( char **prefix, int plen, - const char *sep, int indent, + char sep, int indent, const void *p, size_t l, int width) { _cleanup_free_ char *x = NULL; char *t, *s; - ssize_t len, slen, avail, line, lines; + ssize_t len, avail, line, lines; len = base64mem(p, l, &x); if (len <= 0) @@ -618,21 +618,20 @@ static int base64_append_width( lines = DIV_ROUND_UP(len, width); - slen = strlen_ptr(sep); - if (plen >= SSIZE_MAX - 1 - slen || - lines > (SSIZE_MAX - plen - 1 - slen) / (indent + width + 1)) + if ((size_t) plen >= SSIZE_MAX - 1 - 1 || + lines > (SSIZE_MAX - plen - 1 - 1) / (indent + width + 1)) return -ENOMEM; - t = realloc(*prefix, (ssize_t) plen + 1 + slen + (indent + width + 1) * lines); + t = realloc(*prefix, (ssize_t) plen + 1 + 1 + (indent + width + 1) * lines); if (!t) return -ENOMEM; - memcpy_safe(t + plen, sep, slen); + t[plen] = sep; - for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) { + for (line = 0, s = t + plen + 1, avail = len; line < lines; line++) { int act = MIN(width, avail); - if (line > 0 || sep) { + if (line > 0 || sep == '\n') { memset(s, ' ', indent); s += indent; } @@ -655,10 +654,10 @@ int base64_append( if (plen > width / 2 || plen + indent > width) /* leave indent on the left, keep last column free */ - return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1); + return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent - 1); else /* leave plen on the left, keep last column free */ - return base64_append_width(prefix, plen, " ", plen, p, l, width - plen - 1); + return base64_append_width(prefix, plen, ' ', plen + 1, p, l, width - plen - 1); } #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/hostname-util.c b/shared/systemd/src/basic/hostname-util.c index 00a92cb79a..82fc56dbd6 100644 --- a/shared/systemd/src/basic/hostname-util.c +++ b/shared/systemd/src/basic/hostname-util.c @@ -14,6 +14,7 @@ #include "hostname-util.h" #include "macro.h" #include "string-util.h" +#include "strv.h" #if 0 /* NM_IGNORED */ bool hostname_is_set(void) { @@ -24,7 +25,7 @@ bool hostname_is_set(void) { if (isempty(u.nodename)) return false; - /* This is the built-in kernel default host name */ + /* This is the built-in kernel default hostname */ if (streq(u.nodename, "(none)")) return false; @@ -33,6 +34,7 @@ bool hostname_is_set(void) { char* gethostname_malloc(void) { struct utsname u; + const char *s; /* This call tries to return something useful, either the actual hostname * or it makes something up. The only reason it might fail is OOM. @@ -40,10 +42,28 @@ char* gethostname_malloc(void) { assert_se(uname(&u) >= 0); - if (isempty(u.nodename) || streq(u.nodename, "(none)")) - return strdup(FALLBACK_HOSTNAME); + s = u.nodename; + if (isempty(s) || streq(s, "(none)")) + s = FALLBACK_HOSTNAME; - return strdup(u.nodename); + return strdup(s); +} + +char* gethostname_short_malloc(void) { + struct utsname u; + const char *s; + + /* Like above, but kills the FQDN part if present. */ + + assert_se(uname(&u) >= 0); + + s = u.nodename; + if (isempty(s) || streq(s, "(none)") || s[0] == '.') { + s = FALLBACK_HOSTNAME; + assert(s[0] != '.'); + } + + return strndup(s, strcspn(s, ".")); } #endif /* NM_IGNORED */ @@ -81,7 +101,7 @@ bool valid_ldh_char(char c) { } /** - * Check if s looks like a valid host name or FQDN. This does not do + * Check if s looks like a valid hostname or FQDN. This does not do * full DNS validation, but only checks if the name is composed of * allowed characters and the length is not above the maximum allowed * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if @@ -184,14 +204,16 @@ bool is_localhost(const char *hostname) { /* This tries to identify local host and domain names * described in RFC6761 plus the redhatism of localdomain */ - return strcaseeq(hostname, "localhost") || - strcaseeq(hostname, "localhost.") || - strcaseeq(hostname, "localhost.localdomain") || - strcaseeq(hostname, "localhost.localdomain.") || - endswith_no_case(hostname, ".localhost") || - endswith_no_case(hostname, ".localhost.") || - endswith_no_case(hostname, ".localhost.localdomain") || - endswith_no_case(hostname, ".localhost.localdomain."); + return STRCASE_IN_SET( + hostname, + "localhost", + "localhost.", + "localhost.localdomain", + "localhost.localdomain.") || + endswith_no_case(hostname, ".localhost") || + endswith_no_case(hostname, ".localhost.") || + endswith_no_case(hostname, ".localhost.localdomain") || + endswith_no_case(hostname, ".localhost.localdomain."); } #if 0 /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/hostname-util.h b/shared/systemd/src/basic/hostname-util.h index 7ba386a0fd..cafd6f020b 100644 --- a/shared/systemd/src/basic/hostname-util.h +++ b/shared/systemd/src/basic/hostname-util.h @@ -9,6 +9,7 @@ bool hostname_is_set(void); char* gethostname_malloc(void); +char* gethostname_short_malloc(void); int gethostname_strict(char **ret); bool valid_ldh_char(char c) _const_; diff --git a/shared/systemd/src/basic/in-addr-util.c b/shared/systemd/src/basic/in-addr-util.c index 0e77062101..cddc269f4a 100644 --- a/shared/systemd/src/basic/in-addr-util.c +++ b/shared/systemd/src/basic/in-addr-util.c @@ -180,47 +180,89 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) assert(u); /* Increases the network part of an address by one. Returns - * positive it that succeeds, or 0 if this overflows. */ + * positive if that succeeds, or -ERANGE if this overflows. */ + + return in_addr_prefix_nth(family, u, prefixlen, 1); +} + +/* + * Calculates the nth prefix of size prefixlen starting from the address denoted by u. + * + * On success 1 will be returned and the calculated prefix will be available in + * u. In the case nth == 0 the input will be left unchanged and 1 will be returned. + * In case the calculation cannot be performed (invalid prefix length, + * overflows would occur) -ERANGE is returned. If the address family given isn't + * supported -EAFNOSUPPORT will be returned. + * + * + * Examples: + * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 2), returns 1, writes 192.168.2.0 to u + * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 0), returns 1, no data written + * - in_addr_prefix_nth(AF_INET, 255.255.255.0, 24, 1), returns -ERANGE, no data written + * - in_addr_prefix_nth(AF_INET, 255.255.255.0, 0, 1), returns -ERANGE, no data written + * - in_addr_prefix_nth(AF_INET6, 2001:db8, 64, 0xff00) returns 1, writes 2001:0db8:0000:ff00:: to u + */ +int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth) { + assert(u); if (prefixlen <= 0) - return 0; + return -ERANGE; - if (family == AF_INET) { - uint32_t c, n; + if (nth == 0) + return 1; + if (family == AF_INET) { + uint32_t c, n, t; if (prefixlen > 32) prefixlen = 32; c = be32toh(u->in.s_addr); - n = c + (1UL << (32 - prefixlen)); - if (n < c) - return 0; - n &= 0xFFFFFFFFUL << (32 - prefixlen); + t = nth << (32 - prefixlen); + + /* Check for wrap */ + if (c > UINT32_MAX - t) + return -ERANGE; + + n = c + t; + + n &= UINT32_C(0xFFFFFFFF) << (32 - prefixlen); u->in.s_addr = htobe32(n); return 1; } if (family == AF_INET6) { - struct in6_addr add = {}, result; + struct in6_addr result = {}; uint8_t overflow = 0; - unsigned i; + uint64_t delta; /* this assumes that we only ever have to up to 1<<64 subnets */ + unsigned start_byte = (prefixlen - 1) / 8; if (prefixlen > 128) prefixlen = 128; /* First calculate what we have to add */ - add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8); + delta = nth << ((128 - prefixlen) % 8); - for (i = 16; i > 0; i--) { + for (unsigned i = 16; i > 0; i--) { unsigned j = i - 1; + unsigned d = 0; - result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow; - overflow = (result.s6_addr[j] < u->in6.s6_addr[j]); + if (j <= start_byte) { + int16_t t; + + d = delta & 0xFF; + delta >>= 8; + + t = u->in6.s6_addr[j] + d + overflow; + overflow = t > UINT8_MAX ? t - UINT8_MAX : 0; + + result.s6_addr[j] = (uint8_t)t; + } else + result.s6_addr[j] = u->in6.s6_addr[j]; } - if (overflow) - return 0; + if (overflow || delta != 0) + return -ERANGE; u->in6 = result; return 1; diff --git a/shared/systemd/src/basic/in-addr-util.h b/shared/systemd/src/basic/in-addr-util.h index ae2dad0bb1..90d79a5ef5 100644 --- a/shared/systemd/src/basic/in-addr-util.h +++ b/shared/systemd/src/basic/in-addr-util.h @@ -36,6 +36,7 @@ bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b); int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); +int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth); int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen); int in_addr_to_string(int family, const union in_addr_union *u, char **ret); int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret); diff --git a/shared/systemd/src/basic/io-util.c b/shared/systemd/src/basic/io-util.c index 4f57f04477..f1df3fac90 100644 --- a/shared/systemd/src/basic/io-util.c +++ b/shared/systemd/src/basic/io-util.c @@ -14,10 +14,6 @@ #if 0 /* NM_IGNORED */ int flush_fd(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; int count = 0; /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything @@ -30,19 +26,18 @@ int flush_fd(int fd) { ssize_t l; int r; - r = poll(&pollfd, 1, 0); + r = fd_wait_for_event(fd, POLLIN, 0); if (r < 0) { - if (errno == EINTR) + if (r == -EINTR) continue; - return -errno; - - } else if (r == 0) + return r; + } + if (r == 0) return count; l = read(fd, buf, sizeof(buf)); if (l < 0) { - if (errno == EINTR) continue; @@ -160,21 +155,15 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { } int pipe_eof(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN|POLLHUP, - }; - int r; - r = poll(&pollfd, 1, 0); + r = fd_wait_for_event(fd, POLLIN, 0); if (r < 0) - return -errno; - + return r; if (r == 0) return 0; - return pollfd.revents & POLLHUP; + return !!(r & POLLHUP); } #endif /* NM_IGNORED */ @@ -194,6 +183,9 @@ int fd_wait_for_event(int fd, int event, usec_t t) { if (r == 0) return 0; + if (pollfd.revents & POLLNVAL) + return -EBADF; + return pollfd.revents; } diff --git a/shared/systemd/src/basic/macro.h b/shared/systemd/src/basic/macro.h index 6cd9c516fb..0c2eb894c0 100644 --- a/shared/systemd/src/basic/macro.h +++ b/shared/systemd/src/basic/macro.h @@ -84,6 +84,14 @@ #define _variable_no_sanitize_address_ #endif +/* Apparently there's no has_feature() call defined to check for ubsan, hence let's define this + * unconditionally on llvm */ +#if defined(__clang__) +#define _function_no_sanitize_float_cast_overflow_ __attribute__((no_sanitize("float-cast-overflow"))) +#else +#define _function_no_sanitize_float_cast_overflow_ +#endif + #if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__) /* Temporarily disable some warnings */ #define DISABLE_WARNING_FORMAT_NONLITERAL \ @@ -115,6 +123,14 @@ _Pragma("GCC diagnostic push") #endif +#define DISABLE_WARNING_FLOAT_EQUAL \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"") + +#define DISABLE_WARNING_TYPE_LIMITS \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") + #define REENABLE_WARNING \ _Pragma("GCC diagnostic pop") #else @@ -441,6 +457,8 @@ static inline int __coverity_check_and_return__(int condition) { #define char_array_0(x) x[sizeof(x)-1] = 0; +#define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member) + /* Returns the number of chars needed to format variables of the * specified type as a decimal string. Adds in extra space for a * negative '-' prefix (hence works correctly on signed @@ -460,8 +478,10 @@ static inline int __coverity_check_and_return__(int condition) { ans; \ }) +#define UPDATE_FLAG(orig, flag, b) \ + ((b) ? ((orig) | (flag)) : ((orig) & ~(flag))) #define SET_FLAG(v, flag, b) \ - (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag)) + (v) = UPDATE_FLAG(v, flag, b) #define FLAGS_SET(v, flags) \ ((~(v) & (flags)) == 0) @@ -592,4 +612,17 @@ static inline int __coverity_check_and_return__(int condition) { DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name); \ DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func); +/* A macro to force copying of a variable from memory. This is useful whenever we want to read something from + * memory and want to make sure the compiler won't optimize away the destination variable for us. It's not + * supposed to be a full CPU memory barrier, i.e. CPU is still allowed to reorder the reads, but it is not + * allowed to remove our local copies of the variables. We want this to work for unaligned memory, hence + * memcpy() is great for our purposes. */ +#define READ_NOW(x) \ + ({ \ + typeof(x) _copy; \ + memcpy(&_copy, &(x), sizeof(_copy)); \ + asm volatile ("" : : : "memory"); \ + _copy; \ + }) + #include "log.h" diff --git a/shared/systemd/src/basic/memory-util.h b/shared/systemd/src/basic/memory-util.h index b7e2e67e84..a6a2ccdbc2 100644 --- a/shared/systemd/src/basic/memory-util.h +++ b/shared/systemd/src/basic/memory-util.h @@ -12,7 +12,7 @@ size_t page_size(void) _pure_; #define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) -#define PAGE_ALIGN_DOWN(l) (l & ~(page_size() - 1)) +#define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1)) /* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */ static inline void memcpy_safe(void *dst, const void *src, size_t n) { diff --git a/shared/systemd/src/basic/missing_random.h b/shared/systemd/src/basic/missing_random.h index 2e76031b32..17af87a3ae 100644 --- a/shared/systemd/src/basic/missing_random.h +++ b/shared/systemd/src/basic/missing_random.h @@ -14,3 +14,7 @@ #ifndef GRND_RANDOM #define GRND_RANDOM 0x0002 #endif + +#ifndef GRND_INSECURE +#define GRND_INSECURE 0x0004 +#endif diff --git a/shared/systemd/src/basic/missing_socket.h b/shared/systemd/src/basic/missing_socket.h index 29828dbae7..647e56f1c4 100644 --- a/shared/systemd/src/basic/missing_socket.h +++ b/shared/systemd/src/basic/missing_socket.h @@ -64,3 +64,8 @@ struct sockaddr_vm { #ifndef IP_TRANSPARENT #define IP_TRANSPARENT 19 #endif + +/* linux/sockios.h */ +#ifndef SIOCGSKNS +#define SIOCGSKNS 0x894C +#endif diff --git a/shared/systemd/src/basic/parse-util.c b/shared/systemd/src/basic/parse-util.c index 475a06ccf8..0b343b2a8a 100644 --- a/shared/systemd/src/basic/parse-util.c +++ b/shared/systemd/src/basic/parse-util.c @@ -20,14 +20,28 @@ #include "process-util.h" #include "stat-util.h" #include "string-util.h" +#include "strv.h" int parse_boolean(const char *v) { if (!v) return -EINVAL; - if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on")) + if (STRCASE_IN_SET(v, + "1", + "yes", + "y", + "true", + "t", + "on")) return 1; - else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off")) + + if (STRCASE_IN_SET(v, + "0", + "no", + "n", + "false", + "f", + "off")) return 0; return -EINVAL; @@ -59,26 +73,24 @@ int parse_pid(const char *s, pid_t* ret_pid) { } int parse_mode(const char *s, mode_t *ret) { - char *x; - long l; + unsigned m; + int r; assert(s); - assert(ret); - s += strspn(s, WHITESPACE); - if (s[0] == '-') - return -ERANGE; - - errno = 0; - l = strtol(s, &x, 8); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (l < 0 || l > 07777) + r = safe_atou_full(s, 8 | + SAFE_ATO_REFUSE_PLUS_MINUS, /* Leading '+' or even '-' char? that's just weird, + * refuse. User might have wanted to add mode flags or + * so, but this parser doesn't allow that, so let's + * better be safe. */ + &m); + if (r < 0) + return r; + if (m > 07777) return -ERANGE; - *ret = (mode_t) l; + if (ret) + *ret = m; return 0; } @@ -344,30 +356,73 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) { } #endif /* NM_IGNORED */ +static const char *mangle_base(const char *s, unsigned *base) { + const char *k; + + assert(s); + assert(base); + + /* Base already explicitly specified, then don't do anything. */ + if (SAFE_ATO_MASK_FLAGS(*base) != 0) + return s; + + /* Support Python 3 style "0b" and 0x" prefixes, because they truly make sense, much more than C's "0" prefix for octal. */ + k = STARTSWITH_SET(s, "0b", "0B"); + if (k) { + *base = 2 | (*base & SAFE_ATO_ALL_FLAGS); + return k; + } + + k = STARTSWITH_SET(s, "0o", "0O"); + if (k) { + *base = 8 | (*base & SAFE_ATO_ALL_FLAGS); + return k; + } + + return s; +} + int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { char *x = NULL; unsigned long l; assert(s); - assert(base <= 16); + assert(SAFE_ATO_MASK_FLAGS(base) <= 16); - /* strtoul() is happy to parse negative values, and silently - * converts them to unsigned values without generating an - * error. We want a clean error, hence let's look for the "-" - * prefix on our own, and generate an error. But let's do so - * only after strtoul() validated that the string is clean - * otherwise, so that we return EINVAL preferably over - * ERANGE. */ + /* strtoul() is happy to parse negative values, and silently converts them to unsigned values without + * generating an error. We want a clean error, hence let's look for the "-" prefix on our own, and + * generate an error. But let's do so only after strtoul() validated that the string is clean + * otherwise, so that we return EINVAL preferably over ERANGE. */ + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && + strchr(WHITESPACE, s[0])) + return -EINVAL; s += strspn(s, WHITESPACE); + if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && + IN_SET(s[0], '+', '-')) + return -EINVAL; /* Note that we check the "-" prefix again a second time below, but return a + * different error. I.e. if the SAFE_ATO_REFUSE_PLUS_MINUS flag is set we + * blanket refuse +/- prefixed integers, while if it is missing we'll just + * return ERANGE, because the string actually parses correctly, but doesn't + * fit in the return type. */ + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && + s[0] == '0' && !streq(s, "0")) + return -EINVAL; /* This is particularly useful to avoid ambiguities between C's octal + * notation and assumed-to-be-decimal integers with a leading zero. */ + + s = mangle_base(s, &base); + errno = 0; - l = strtoul(s, &x, base); + l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base) /* Let's mask off the flags bits so that only the actual + * base is left */); if (errno > 0) return -errno; if (!x || x == s || *x != 0) return -EINVAL; - if (s[0] == '-') + if (l != 0 && s[0] == '-') return -ERANGE; if ((unsigned long) (unsigned) l != l) return -ERANGE; @@ -379,13 +434,17 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { } int safe_atoi(const char *s, int *ret_i) { + unsigned base = 0; char *x = NULL; long l; assert(s); + s += strspn(s, WHITESPACE); + s = mangle_base(s, &base); + errno = 0; - l = strtol(s, &x, 0); + l = strtol(s, &x, base); if (errno > 0) return -errno; if (!x || x == s || *x != 0) @@ -399,21 +458,36 @@ int safe_atoi(const char *s, int *ret_i) { return 0; } -int safe_atollu(const char *s, long long unsigned *ret_llu) { +int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) { char *x = NULL; unsigned long long l; assert(s); + assert(SAFE_ATO_MASK_FLAGS(base) <= 16); + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && + strchr(WHITESPACE, s[0])) + return -EINVAL; s += strspn(s, WHITESPACE); + if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && + IN_SET(s[0], '+', '-')) + return -EINVAL; + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && + s[0] == '0' && s[1] != 0) + return -EINVAL; + + s = mangle_base(s, &base); + errno = 0; - l = strtoull(s, &x, 0); + l = strtoull(s, &x, SAFE_ATO_MASK_FLAGS(base)); if (errno > 0) return -errno; if (!x || x == s || *x != 0) return -EINVAL; - if (*s == '-') + if (l != 0 && s[0] == '-') return -ERANGE; if (ret_llu) @@ -423,13 +497,17 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) { } int safe_atolli(const char *s, long long int *ret_lli) { + unsigned base = 0; char *x = NULL; long long l; assert(s); + s += strspn(s, WHITESPACE); + s = mangle_base(s, &base); + errno = 0; - l = strtoll(s, &x, 0); + l = strtoll(s, &x, base); if (errno > 0) return -errno; if (!x || x == s || *x != 0) @@ -442,20 +520,22 @@ int safe_atolli(const char *s, long long int *ret_lli) { } int safe_atou8(const char *s, uint8_t *ret) { - char *x = NULL; + unsigned base = 0; unsigned long l; + char *x = NULL; assert(s); s += strspn(s, WHITESPACE); + s = mangle_base(s, &base); errno = 0; - l = strtoul(s, &x, 0); + l = strtoul(s, &x, base); if (errno > 0) return -errno; if (!x || x == s || *x != 0) return -EINVAL; - if (s[0] == '-') + if (l != 0 && s[0] == '-') return -ERANGE; if ((unsigned long) (uint8_t) l != l) return -ERANGE; @@ -470,34 +550,53 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { unsigned long l; assert(s); - assert(ret); - assert(base <= 16); + assert(SAFE_ATO_MASK_FLAGS(base) <= 16); + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && + strchr(WHITESPACE, s[0])) + return -EINVAL; s += strspn(s, WHITESPACE); + if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && + IN_SET(s[0], '+', '-')) + return -EINVAL; + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && + s[0] == '0' && s[1] != 0) + return -EINVAL; + + s = mangle_base(s, &base); + errno = 0; - l = strtoul(s, &x, base); + l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base)); if (errno > 0) return -errno; if (!x || x == s || *x != 0) return -EINVAL; - if (s[0] == '-') + if (l != 0 && s[0] == '-') return -ERANGE; if ((unsigned long) (uint16_t) l != l) return -ERANGE; - *ret = (uint16_t) l; + if (ret) + *ret = (uint16_t) l; + return 0; } int safe_atoi16(const char *s, int16_t *ret) { + unsigned base = 0; char *x = NULL; long l; assert(s); + s += strspn(s, WHITESPACE); + s = mangle_base(s, &base); + errno = 0; - l = strtol(s, &x, 0); + l = strtol(s, &x, base); if (errno > 0) return -errno; if (!x || x == s || *x != 0) diff --git a/shared/systemd/src/basic/parse-util.h b/shared/systemd/src/basic/parse-util.h index 36d76ba576..9a516ce5f6 100644 --- a/shared/systemd/src/basic/parse-util.h +++ b/shared/systemd/src/basic/parse-util.h @@ -21,6 +21,12 @@ int parse_range(const char *t, unsigned *lower, unsigned *upper); int parse_errno(const char *t); int parse_syscall_and_errno(const char *in, char **name, int *error); +#define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30) +#define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29) +#define SAFE_ATO_REFUSE_LEADING_WHITESPACE (1U << 28) +#define SAFE_ATO_ALL_FLAGS (SAFE_ATO_REFUSE_PLUS_MINUS|SAFE_ATO_REFUSE_LEADING_ZERO|SAFE_ATO_REFUSE_LEADING_WHITESPACE) +#define SAFE_ATO_MASK_FLAGS(base) ((base) & ~SAFE_ATO_ALL_FLAGS) + int safe_atou_full(const char *s, unsigned base, unsigned *ret_u); static inline int safe_atou(const char *s, unsigned *ret_u) { @@ -28,7 +34,6 @@ static inline int safe_atou(const char *s, unsigned *ret_u) { } int safe_atoi(const char *s, int *ret_i); -int safe_atollu(const char *s, unsigned long long *ret_u); int safe_atolli(const char *s, long long int *ret_i); int safe_atou8(const char *s, uint8_t *ret); @@ -59,6 +64,12 @@ static inline int safe_atoi32(const char *s, int32_t *ret_i) { return safe_atoi(s, (int*) ret_i); } +int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu); + +static inline int safe_atollu(const char *s, long long unsigned *ret_llu) { + return safe_atollu_full(s, 0, ret_llu); +} + static inline int safe_atou64(const char *s, uint64_t *ret_u) { assert_cc(sizeof(uint64_t) == sizeof(unsigned long long)); return safe_atollu(s, (unsigned long long*) ret_u); @@ -69,6 +80,11 @@ static inline int safe_atoi64(const char *s, int64_t *ret_i) { return safe_atolli(s, (long long int*) ret_i); } +static inline int safe_atoux64(const char *s, uint64_t *ret) { + assert_cc(sizeof(int64_t) == sizeof(long long unsigned)); + return safe_atollu_full(s, 16, (long long unsigned*) ret); +} + #if LONG_MAX == INT_MAX static inline int safe_atolu(const char *s, unsigned long *ret_u) { assert_cc(sizeof(unsigned long) == sizeof(unsigned)); diff --git a/shared/systemd/src/basic/path-util.c b/shared/systemd/src/basic/path-util.c index 7baab4be50..45477bc245 100644 --- a/shared/systemd/src/basic/path-util.c +++ b/shared/systemd/src/basic/path-util.c @@ -1062,7 +1062,7 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version) if (!path) return -ENOMEM; - r = glob_extend(&names, path); + r = glob_extend(&names, path, 0); if (r == -ENOENT) continue; if (r < 0) diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c index 2de3541cb8..6dcb26fb6b 100644 --- a/shared/systemd/src/basic/process-util.c +++ b/shared/systemd/src/basic/process-util.c @@ -212,50 +212,12 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags return 0; } -int rename_process(const char name[]) { - static size_t mm_size = 0; - static char *mm = NULL; - bool truncated = false; - size_t l; - - /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's - * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in - * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded; - * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be - * truncated. - * - * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */ - - if (isempty(name)) - return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ - - if (!is_main_thread()) - return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we - * cache things without locking, and we make assumptions that PR_SET_NAME sets the - * process name that isn't correct on any other threads */ - - l = strlen(name); - - /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we - * can use PR_SET_NAME, which sets the thread name for the calling thread. */ - if (prctl(PR_SET_NAME, name) < 0) - log_debug_errno(errno, "PR_SET_NAME failed: %m"); - if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */ - truncated = true; +static int update_argv(const char name[], size_t l) { + static int can_do = -1; - /* Second step, change glibc's ID of the process name. */ - if (program_invocation_name) { - size_t k; - - k = strlen(program_invocation_name); - strncpy(program_invocation_name, name, k); - if (l > k) - truncated = true; - } - - /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but - * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at - * the end. This is the best option for changing /proc/self/cmdline. */ + if (can_do == 0) + return 0; + can_do = false; /* We'll set it to true only if the whole process works */ /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is @@ -263,22 +225,29 @@ int rename_process(const char name[]) { * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but * mmap() is not. */ if (geteuid() != 0) - log_debug("Skipping PR_SET_MM, as we don't have privileges."); - else if (mm_size < l+1) { + return log_debug_errno(SYNTHETIC_ERRNO(EPERM), + "Skipping PR_SET_MM, as we don't have privileges."); + + static size_t mm_size = 0; + static char *mm = NULL; + int r; + + if (mm_size < l+1) { size_t nn_size; char *nn; nn_size = PAGE_ALIGN(l+1); nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (nn == MAP_FAILED) { - log_debug_errno(errno, "mmap() failed: %m"); - goto use_saved_argv; - } + if (nn == MAP_FAILED) + return log_debug_errno(errno, "mmap() failed: %m"); strncpy(nn, name, nn_size); /* Now, let's tell the kernel about this new memory */ if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { + if (ERRNO_IS_PRIVILEGE(errno)) + return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m"); + /* HACK: prctl() API is kind of dumb on this point. The existing end address may already be * below the desired start address, in which case the kernel may have kicked this back due * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in @@ -290,15 +259,13 @@ int rename_process(const char name[]) { log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m"); if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) { - log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m"); + r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m"); (void) munmap(nn, nn_size); - goto use_saved_argv; + return r; } - if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { - log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m"); - goto use_saved_argv; - } + if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) + return log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m"); } else { /* And update the end pointer to the new end, too. If this fails, we don't really know what * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure, @@ -320,13 +287,56 @@ int rename_process(const char name[]) { log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); } -use_saved_argv: + can_do = true; + return 0; +} + +int rename_process(const char name[]) { + bool truncated = false; + + /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's + * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in + * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded; + * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be + * truncated. + * + * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */ + + if (isempty(name)) + return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ + + if (!is_main_thread()) + return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we + * cache things without locking, and we make assumptions that PR_SET_NAME sets the + * process name that isn't correct on any other threads */ + + size_t l = strlen(name); + + /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we + * can use PR_SET_NAME, which sets the thread name for the calling thread. */ + if (prctl(PR_SET_NAME, name) < 0) + log_debug_errno(errno, "PR_SET_NAME failed: %m"); + if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */ + truncated = true; + + /* Second step, change glibc's ID of the process name. */ + if (program_invocation_name) { + size_t k; + + k = strlen(program_invocation_name); + strncpy(program_invocation_name, name, k); + if (l > k) + truncated = true; + } + + /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but + * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at + * the end. This is the best option for changing /proc/self/cmdline. */ + (void) update_argv(name, l); + /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if * it still looks here */ - if (saved_argc > 0) { - int i; - if (saved_argv[0]) { size_t k; @@ -336,7 +346,7 @@ use_saved_argv: truncated = true; } - for (i = 1; i < saved_argc; i++) { + for (int i = 1; i < saved_argc; i++) { if (!saved_argv[i]) break; @@ -634,6 +644,23 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) { return 0; } +int get_process_umask(pid_t pid, mode_t *umask) { + _cleanup_free_ char *m = NULL; + const char *p; + int r; + + assert(umask); + assert(pid >= 0); + + p = procfs_file_alloca(pid, "status"); + + r = get_proc_field(p, "Umask", WHITESPACE, &m); + if (r == -ENOENT) + return -ESRCH; + + return parse_mode(m, umask); +} + int wait_for_terminate(pid_t pid, siginfo_t *status) { siginfo_t dummy; @@ -1284,8 +1311,8 @@ int safe_fork_full( r, "Failed to rename process, ignoring: %m"); } - if (flags & FORK_DEATHSIG) - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) { + if (flags & (FORK_DEATHSIG|FORK_DEATHSIG_SIGINT)) + if (prctl(PR_SET_PDEATHSIG, (flags & FORK_DEATHSIG_SIGINT) ? SIGINT : SIGTERM) < 0) { log_full_errno(prio, errno, "Failed to set death signal: %m"); _exit(EXIT_FAILURE); } diff --git a/shared/systemd/src/basic/process-util.h b/shared/systemd/src/basic/process-util.h index 7b70c9f300..0501237790 100644 --- a/shared/systemd/src/basic/process-util.h +++ b/shared/systemd/src/basic/process-util.h @@ -45,6 +45,7 @@ int get_process_cwd(pid_t pid, char **cwd); int get_process_root(pid_t pid, char **root); int get_process_environ(pid_t pid, char **environ); int get_process_ppid(pid_t pid, pid_t *ppid); +int get_process_umask(pid_t pid, mode_t *umask); int wait_for_terminate(pid_t pid, siginfo_t *status); @@ -151,15 +152,16 @@ int must_be_root(void); typedef enum ForkFlags { FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */ FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */ - FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child */ - FORK_NULL_STDIO = 1 << 3, /* Connect 0,1,2 to /dev/null */ - FORK_REOPEN_LOG = 1 << 4, /* Reopen log connection */ - FORK_LOG = 1 << 5, /* Log above LOG_DEBUG log level about failures */ - FORK_WAIT = 1 << 6, /* Wait until child exited */ - FORK_NEW_MOUNTNS = 1 << 7, /* Run child in its own mount namespace */ - FORK_MOUNTNS_SLAVE = 1 << 8, /* Make child's mount namespace MS_SLAVE */ - FORK_RLIMIT_NOFILE_SAFE = 1 << 9, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ - FORK_STDOUT_TO_STDERR = 1 << 10, /* Make stdout a copy of stderr */ + FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child to SIGTERM */ + FORK_DEATHSIG_SIGINT = 1 << 3, /* Set PR_DEATHSIG in the child to SIGINT */ + FORK_NULL_STDIO = 1 << 4, /* Connect 0,1,2 to /dev/null */ + FORK_REOPEN_LOG = 1 << 5, /* Reopen log connection */ + FORK_LOG = 1 << 6, /* Log above LOG_DEBUG log level about failures */ + FORK_WAIT = 1 << 7, /* Wait until child exited */ + FORK_NEW_MOUNTNS = 1 << 8, /* Run child in its own mount namespace */ + FORK_MOUNTNS_SLAVE = 1 << 9, /* Make child's mount namespace MS_SLAVE */ + FORK_RLIMIT_NOFILE_SAFE = 1 << 10, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ + FORK_STDOUT_TO_STDERR = 1 << 11, /* Make stdout a copy of stderr */ } ForkFlags; int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); diff --git a/shared/systemd/src/basic/random-util.c b/shared/systemd/src/basic/random-util.c index 1a29494466..512e7af9cb 100644 --- a/shared/systemd/src/basic/random-util.c +++ b/shared/systemd/src/basic/random-util.c @@ -21,6 +21,7 @@ #endif #include "alloc-util.h" +#include "errno-util.h" #include "fd-util.h" #include "fileio.h" #include "io-util.h" @@ -215,7 +216,9 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { r = -1; errno = ENOSYS; #else - r = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK); + r = getrandom(p, n, + (FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) | + (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0)); #endif if (r > 0) { have_syscall = true; @@ -246,7 +249,7 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { have_syscall = true; return -EIO; - } else if (errno == ENOSYS) { + } else if (ERRNO_IS_NOT_SUPPORTED(errno)) { /* We lack the syscall, continue with reading from /dev/urandom. */ have_syscall = false; break; @@ -272,6 +275,18 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { /* Use /dev/urandom instead */ break; + + } else if (errno == EINVAL) { + + /* Most likely: unknown flag. We know that GRND_INSECURE might cause this, + * hence try without. */ + + if (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE)) { + flags = flags &~ RANDOM_ALLOW_INSECURE; + continue; + } + + return -errno; } else return -errno; } @@ -334,9 +349,11 @@ void initialize_srand(void) { /* INT_MAX gives us only 31 bits, so use 24 out of that. */ #if RAND_MAX >= INT_MAX +assert_cc(RAND_MAX >= 16777215); # define RAND_STEP 3 #else -/* SHORT_INT_MAX or lower gives at most 15 bits, we just just 8 out of that. */ +/* SHORT_INT_MAX or lower gives at most 15 bits, we just use 8 out of that. */ +assert_cc(RAND_MAX >= 255); # define RAND_STEP 1 #endif @@ -401,7 +418,7 @@ void random_bytes(void *p, size_t n) { * This function is hence not useful for generating UUIDs or cryptographic key material. */ - if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND) >= 0) + if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND|RANDOM_ALLOW_INSECURE) >= 0) return; /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */ diff --git a/shared/systemd/src/basic/random-util.h b/shared/systemd/src/basic/random-util.h index facc11b976..d8e067d96e 100644 --- a/shared/systemd/src/basic/random-util.h +++ b/shared/systemd/src/basic/random-util.h @@ -10,6 +10,7 @@ typedef enum RandomFlags { RANDOM_BLOCK = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */ RANDOM_MAY_FAIL = 1 << 2, /* If we can't get any randomness at all, return early with -ENODATA */ RANDOM_ALLOW_RDRAND = 1 << 3, /* Allow usage of the CPU RNG */ + RANDOM_ALLOW_INSECURE = 1 << 4, /* Allow usage of GRND_INSECURE flag to kernel's getrandom() API */ } RandomFlags; int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled up with pseudo random, if not enough is available */ diff --git a/shared/systemd/src/basic/set.h b/shared/systemd/src/basic/set.h index 5f1956177e..621e83bf27 100644 --- a/shared/systemd/src/basic/set.h +++ b/shared/systemd/src/basic/set.h @@ -5,40 +5,48 @@ #include "hashmap.h" #include "macro.h" -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) +#define set_free_and_replace(a, b) \ + ({ \ + set_free(a); \ + (a) = (b); \ + (b) = NULL; \ + 0; \ + }) + +Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS) static inline Set *set_free(Set *s) { - return (Set*) internal_hashmap_free(HASHMAP_BASE(s), NULL, NULL); + return (Set*) _hashmap_free(HASHMAP_BASE(s), NULL, NULL); } static inline Set *set_free_free(Set *s) { - return (Set*) internal_hashmap_free(HASHMAP_BASE(s), free, NULL); + return (Set*) _hashmap_free(HASHMAP_BASE(s), free, NULL); } /* no set_free_free_free */ static inline Set *set_copy(Set *s) { - return (Set*) internal_hashmap_copy(HASHMAP_BASE(s)); + return (Set*) _hashmap_copy(HASHMAP_BASE(s)); } -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_ensure_allocated(h, ops) _set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) int set_put(Set *s, const void *key); /* no set_update */ /* no set_replace */ static inline void *set_get(const Set *s, void *key) { - return internal_hashmap_get(HASHMAP_BASE((Set *) s), key); + return _hashmap_get(HASHMAP_BASE((Set *) s), key); } /* no set_get2 */ static inline bool set_contains(const Set *s, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE((Set *) s), key); + return _hashmap_contains(HASHMAP_BASE((Set *) s), key); } static inline void *set_remove(Set *s, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(s), key); + return _hashmap_remove(HASHMAP_BASE(s), key); } /* no set_remove2 */ @@ -48,19 +56,19 @@ int set_remove_and_put(Set *s, const void *old_key, const void *new_key); int set_merge(Set *s, Set *other); static inline int set_reserve(Set *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); + return _hashmap_reserve(HASHMAP_BASE(h), entries_add); } static inline int set_move(Set *s, Set *other) { - return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); + return _hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); } static inline int set_move_one(Set *s, Set *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); + return _hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); } static inline unsigned set_size(const Set *s) { - return internal_hashmap_size(HASHMAP_BASE((Set *) s)); + return _hashmap_size(HASHMAP_BASE((Set *) s)); } static inline bool set_isempty(const Set *s) { @@ -68,23 +76,23 @@ static inline bool set_isempty(const Set *s) { } static inline unsigned set_buckets(const Set *s) { - return internal_hashmap_buckets(HASHMAP_BASE((Set *) s)); + return _hashmap_buckets(HASHMAP_BASE((Set *) s)); } bool set_iterate(const Set *s, Iterator *i, void **value); static inline void set_clear(Set *s) { - internal_hashmap_clear(HASHMAP_BASE(s), NULL, NULL); + _hashmap_clear(HASHMAP_BASE(s), NULL, NULL); } static inline void set_clear_free(Set *s) { - internal_hashmap_clear(HASHMAP_BASE(s), free, NULL); + _hashmap_clear(HASHMAP_BASE(s), free, NULL); } /* no set_clear_free_free */ static inline void *set_steal_first(Set *s) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); } #define set_clear_with_destructor(_s, _f) \ @@ -103,18 +111,18 @@ static inline void *set_steal_first(Set *s) { /* no set_first_key */ static inline void *set_first(const Set *s) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE((Set *) s), false, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE((Set *) s), false, NULL); } /* no set_next */ static inline char **set_get_strv(Set *s) { - return internal_hashmap_get_strv(HASHMAP_BASE(s)); + return _hashmap_get_strv(HASHMAP_BASE(s)); } int set_consume(Set *s, void *value); -int set_put_strdup(Set *s, const char *p); -int set_put_strdupv(Set *s, char **l); +int set_put_strdup(Set **s, const char *p); +int set_put_strdupv(Set **s, char **l); int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags); #define SET_FOREACH(e, s, i) \ diff --git a/shared/systemd/src/basic/socket-util.c b/shared/systemd/src/basic/socket-util.c index 452b9ced20..9ced3a1291 100644 --- a/shared/systemd/src/basic/socket-util.c +++ b/shared/systemd/src/basic/socket-util.c @@ -25,6 +25,7 @@ #include "fd-util.h" #include "fileio.h" #include "format-util.h" +#include "io-util.h" #include "log.h" #include "macro.h" #include "memory-util.h" @@ -823,10 +824,7 @@ ssize_t send_one_fd_iov_sa( const struct sockaddr *sa, socklen_t len, int flags) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control = {}; struct msghdr mh = { .msg_name = (struct sockaddr*) sa, .msg_namelen = len, @@ -855,8 +853,6 @@ ssize_t send_one_fd_iov_sa( cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); - - mh.msg_controllen = CMSG_SPACE(sizeof(int)); } k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags); if (k < 0) @@ -882,17 +878,14 @@ ssize_t receive_one_fd_iov( int flags, int *ret_fd) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control; struct msghdr mh = { .msg_control = &control, .msg_controllen = sizeof(control), .msg_iov = iov, .msg_iovlen = iovlen, }; - struct cmsghdr *cmsg, *found = NULL; + struct cmsghdr *found; ssize_t k; assert(transport_fd >= 0); @@ -906,26 +899,18 @@ ssize_t receive_one_fd_iov( * combination with send_one_fd(). */ - k = recvmsg(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags); + k = recvmsg_safe(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags); if (k < 0) - return (ssize_t) -errno; + return k; - CMSG_FOREACH(cmsg, &mh) { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS && - cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { - assert(!found); - found = cmsg; - break; - } - } - - if (!found) + found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int))); + if (!found) { cmsg_close_all(&mh); - /* If didn't receive an FD or any data, return an error. */ - if (k == 0 && !found) - return -EIO; + /* If didn't receive an FD or any data, return an error. */ + if (k == 0) + return -EIO; + } if (found) *ret_fd = *(int*) CMSG_DATA(found); @@ -991,10 +976,6 @@ fallback: int flush_accept(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; int r, b; socklen_t l = sizeof(b); @@ -1015,12 +996,12 @@ int flush_accept(int fd) { for (unsigned iteration = 0;; iteration++) { int cfd; - r = poll(&pollfd, 1, 0); + r = fd_wait_for_event(fd, POLLIN, 0); if (r < 0) { - if (errno == EINTR) + if (r == -EINTR) continue; - return -errno; + return r; } if (r == 0) return 0; @@ -1043,6 +1024,7 @@ int flush_accept(int fd) { safe_close(cfd); } } +#endif /* NM_IGNORED */ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) { struct cmsghdr *cmsg; @@ -1058,6 +1040,7 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng return NULL; } +#if 0 /* NM_IGNORED */ int socket_ioctl_fd(void) { int fd; @@ -1179,3 +1162,46 @@ int socket_bind_to_ifindex(int fd, int ifindex) { return socket_bind_to_ifname(fd, ifname); } + +ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) { + ssize_t n; + + /* A wrapper around recvmsg() that checks for MSG_CTRUNC, and turns it into an error, in a reasonably + * safe way, closing any SCM_RIGHTS fds in the error path. + * + * Note that unlike our usual coding style this might modify *msg on failure. */ + + n = recvmsg(sockfd, msg, flags); + if (n < 0) + return -errno; + + if (FLAGS_SET(msg->msg_flags, MSG_CTRUNC)) { + cmsg_close_all(msg); + return -EXFULL; /* a recognizable error code */ + } + + return n; +} + +int socket_pass_pktinfo(int fd, bool b) { + int af; + socklen_t sl = sizeof(af); + + if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &sl) < 0) + return -errno; + + switch (af) { + + case AF_INET: + return setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, b); + + case AF_INET6: + return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, b); + + case AF_NETLINK: + return setsockopt_int(fd, SOL_NETLINK, NETLINK_PKTINFO, b); + + default: + return -EAFNOSUPPORT; + } +} diff --git a/shared/systemd/src/basic/socket-util.h b/shared/systemd/src/basic/socket-util.h index 2596c540ca..5e5cf73182 100644 --- a/shared/systemd/src/basic/socket-util.h +++ b/shared/systemd/src/basic/socket-util.h @@ -160,6 +160,25 @@ int flush_accept(int fd); struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length); +/* Type-safe, dereferencing version of cmsg_find() */ +#define CMSG_FIND_DATA(mh, level, type, ctype) \ + ({ \ + struct cmsghdr *_found; \ + _found = cmsg_find(mh, level, type, CMSG_LEN(sizeof(ctype))); \ + (ctype*) (_found ? CMSG_DATA(_found) : NULL); \ + }) + +/* Resolves to a type that can carry cmsghdr structures. Make sure things are properly aligned, i.e. the type + * itself is placed properly in memory and the size is also aligned to what's appropriate for "cmsghdr" + * structures. */ +#define CMSG_BUFFER_TYPE(size) \ + union { \ + struct cmsghdr cmsghdr; \ + uint8_t buf[size]; \ + uint8_t align_check[(size) >= CMSG_SPACE(0) && \ + (size) == CMSG_ALIGN(size) ? 1 : -1]; \ + } + /* * Certain hardware address types (e.g Infiniband) do not fit into sll_addr * (8 bytes) and run over the structure. This macro returns the correct size that @@ -201,3 +220,7 @@ static inline int setsockopt_int(int fd, int level, int optname, int value) { int socket_bind_to_ifname(int fd, const char *ifname); int socket_bind_to_ifindex(int fd, int ifindex); + +ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags); + +int socket_pass_pktinfo(int fd, bool b); diff --git a/shared/systemd/src/basic/sort-util.h b/shared/systemd/src/basic/sort-util.h index e029f8646e..a8dc3bb6ed 100644 --- a/shared/systemd/src/basic/sort-util.h +++ b/shared/systemd/src/basic/sort-util.h @@ -39,7 +39,7 @@ static inline void* bsearch_safe(const void *key, const void *base, * Normal qsort requires base to be nonnull. Here were require * that only if nmemb > 0. */ -static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) { +static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) { if (nmemb <= 1) return; @@ -52,7 +52,7 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn #define typesafe_qsort(p, n, func) \ ({ \ int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ - qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ + _qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ }) static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) { diff --git a/shared/systemd/src/basic/stat-util.c b/shared/systemd/src/basic/stat-util.c index a895c1c34e..5c51e13a4b 100644 --- a/shared/systemd/src/basic/stat-util.c +++ b/shared/systemd/src/basic/stat-util.c @@ -99,10 +99,10 @@ bool null_or_empty(struct stat *st) { if (S_ISREG(st->st_mode) && st->st_size <= 0) return true; - /* We don't want to hardcode the major/minor of /dev/null, - * hence we do a simpler "is this a device node?" check. */ + /* We don't want to hardcode the major/minor of /dev/null, hence we do a simpler "is this a character + * device node?" check. */ - if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) + if (S_ISCHR(st->st_mode)) return true; return false; @@ -113,6 +113,10 @@ int null_or_empty_path(const char *fn) { assert(fn); + /* If we have the path, let's do an easy text comparison first. */ + if (path_equal(fn, "/dev/null")) + return true; + if (stat(fn, &st) < 0) return -errno; @@ -183,13 +187,12 @@ int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { } int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { - _cleanup_close_ int fd = -1; + struct statfs s; - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); - if (fd < 0) + if (statfs(path, &s) < 0) return -errno; - return fd_is_fs_type(fd, magic_value); + return is_fs_type(&s, magic_value); } bool is_temporary_fs(const struct statfs *s) { @@ -384,4 +387,37 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret return 0; } + +int proc_mounted(void) { + int r; + + /* A quick check of procfs is properly mounted */ + + r = path_is_fs_type("/proc/", PROC_SUPER_MAGIC); + if (r == -ENOENT) /* not mounted at all */ + return false; + + return r; +} + +bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { + + /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to + * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file + * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file + * size, backing device, inode type and if this refers to a device not the major/minor. + * + * Note that we don't care if file attributes such as ownership or access mode change, this here is + * about contents of the file. The purpose here is to detect file contents changes, and nothing + * else. */ + + return a && b && + (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ + ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ + a->st_mtime == b->st_mtime && + (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */ + a->st_dev == b->st_dev && + a->st_ino == b->st_ino && + (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */ +} #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/stat-util.h b/shared/systemd/src/basic/stat-util.h index 7824af3500..59aedcb7c4 100644 --- a/shared/systemd/src/basic/stat-util.h +++ b/shared/systemd/src/basic/stat-util.h @@ -87,3 +87,7 @@ int fd_verify_directory(int fd); int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret); int device_path_make_canonical(mode_t mode, dev_t devno, char **ret); int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno); + +int proc_mounted(void); + +bool stat_inode_unmodified(const struct stat *a, const struct stat *b); diff --git a/shared/systemd/src/basic/string-util.c b/shared/systemd/src/basic/string-util.c index 9f15cacae5..c30e88c590 100644 --- a/shared/systemd/src/basic/string-util.c +++ b/shared/systemd/src/basic/string-util.c @@ -21,18 +21,19 @@ #include "util.h" int strcmp_ptr(const char *a, const char *b) { - /* Like strcmp(), but tries to make sense of NULL pointers */ + if (a && b) return strcmp(a, b); + return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */ +} - if (!a && b) - return -1; - - if (a && !b) - return 1; +int strcasecmp_ptr(const char *a, const char *b) { + /* Like strcasecmp(), but tries to make sense of NULL pointers */ - return 0; + if (a && b) + return strcasecmp(a, b); + return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */ } char* endswith(const char *s, const char *postfix) { @@ -219,7 +220,6 @@ char *strnappend(const char *s, const char *suffix, size_t b) { return r; } -#if 0 /* NM_IGNORED */ char *strjoin_real(const char *x, ...) { va_list ap; size_t l; @@ -277,6 +277,7 @@ char *strjoin_real(const char *x, ...) { return r; } +#if 0 /* NM_IGNORED */ char *strstrip(char *s) { if (!s) return NULL; diff --git a/shared/systemd/src/basic/string-util.h b/shared/systemd/src/basic/string-util.h index 2a344b996f..09131455bf 100644 --- a/shared/systemd/src/basic/string-util.h +++ b/shared/systemd/src/basic/string-util.h @@ -27,6 +27,7 @@ #define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0) int strcmp_ptr(const char *a, const char *b) _pure_; +int strcasecmp_ptr(const char *a, const char *b) _pure_; static inline bool streq_ptr(const char *a, const char *b) { return strcmp_ptr(a, b) == 0; diff --git a/shared/systemd/src/basic/strv.c b/shared/systemd/src/basic/strv.c index be1c8325b6..6f81213933 100644 --- a/shared/systemd/src/basic/strv.c +++ b/shared/systemd/src/basic/strv.c @@ -30,6 +30,18 @@ char *strv_find(char * const *l, const char *name) { return NULL; } +char *strv_find_case(char * const *l, const char *name) { + char * const *i; + + assert(name); + + STRV_FOREACH(i, l) + if (strcaseeq(*i, name)) + return *i; + + return NULL; +} + char *strv_find_prefix(char * const *l, const char *name) { char * const *i; @@ -944,20 +956,20 @@ static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const c return 1; } -int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) { +int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) { int r; - r = hashmap_ensure_allocated(h, &string_strv_hash_ops); + r = _hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; return string_strv_hashmap_put_internal(*h, key, value); } -int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value) { +int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) { int r; - r = ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops); + r = _ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; diff --git a/shared/systemd/src/basic/strv.h b/shared/systemd/src/basic/strv.h index dd3323c223..2ad927bce5 100644 --- a/shared/systemd/src/basic/strv.h +++ b/shared/systemd/src/basic/strv.h @@ -14,9 +14,13 @@ #include "string-util.h" char *strv_find(char * const *l, const char *name) _pure_; +char *strv_find_case(char * const *l, const char *name) _pure_; char *strv_find_prefix(char * const *l, const char *name) _pure_; char *strv_find_startswith(char * const *l, const char *name) _pure_; +#define strv_contains(l, s) (!!strv_find((l), (s))) +#define strv_contains_case(l, s) (!!strv_find_case((l), (s))) + char **strv_free(char **l); DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free); #define _cleanup_strv_free_ _cleanup_(strv_freep) @@ -54,8 +58,6 @@ static inline bool strv_equal(char * const *a, char * const *b) { return strv_compare(a, b) == 0; } -#define strv_contains(l, s) (!!strv_find((l), (s))) - char **strv_new_internal(const char *x, ...) _sentinel_; char **strv_new_ap(const char *x, va_list ap); #define strv_new(...) strv_new_internal(__VA_ARGS__, NULL) @@ -104,14 +106,14 @@ bool strv_overlap(char * const *a, char * const *b) _pure_; #define STRV_FOREACH_BACKWARDS(s, l) \ for (s = ({ \ - char **_l = l; \ + typeof(l) _l = l; \ _l ? _l + strv_length(_l) - 1U : NULL; \ }); \ (l) && ((s) >= (l)); \ (s)--) #define STRV_FOREACH_PAIR(x, y, l) \ - for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) + for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) char **strv_sort(char **l); void strv_print(char * const *l); @@ -156,6 +158,13 @@ void strv_print(char * const *l); _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \ }) +#define STRCASE_IN_SET(x, ...) strv_contains_case(STRV_MAKE(__VA_ARGS__), x) +#define STRCASEPTR_IN_SET(x, ...) \ + ({ \ + const char* _x = (x); \ + _x && strv_contains_case(STRV_MAKE(__VA_ARGS__), _x); \ + }) + #define STARTSWITH_SET(p, ...) \ ({ \ const char *_p = (p); \ @@ -217,5 +226,7 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space); }) extern const struct hash_ops string_strv_hash_ops; -int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value); -int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value); +int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS); +int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS); +#define string_strv_hashmap_put(h, k, v) _string_strv_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS) +#define string_strv_ordered_hashmap_put(h, k, v) _string_strv_ordered_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS) diff --git a/shared/systemd/src/basic/user-util.h b/shared/systemd/src/basic/user-util.h index 562f2c5ce1..acb6e573c4 100644 --- a/shared/systemd/src/basic/user-util.h +++ b/shared/systemd/src/basic/user-util.h @@ -99,20 +99,13 @@ static inline bool userns_supported(void) { return access("/proc/self/uid_map", F_OK) >= 0; } -bool valid_user_group_name_full(const char *u, bool strict); -bool valid_user_group_name_or_id_full(const char *u, bool strict); -static inline bool valid_user_group_name(const char *u) { - return valid_user_group_name_full(u, true); -} -static inline bool valid_user_group_name_or_id(const char *u) { - return valid_user_group_name_or_id_full(u, true); -} -static inline bool valid_user_group_name_compat(const char *u) { - return valid_user_group_name_full(u, false); -} -static inline bool valid_user_group_name_or_id_compat(const char *u) { - return valid_user_group_name_or_id_full(u, false); -} +typedef enum ValidUserFlags { + VALID_USER_RELAX = 1 << 0, + VALID_USER_WARN = 1 << 1, + VALID_USER_ALLOW_NUMERIC = 1 << 2, +} ValidUserFlags; + +bool valid_user_group_name(const char *u, ValidUserFlags flags); bool valid_gecos(const char *d); bool valid_home(const char *p); diff --git a/shared/systemd/src/basic/utf8.c b/shared/systemd/src/basic/utf8.c index ba28e12968..f2e27b6834 100644 --- a/shared/systemd/src/basic/utf8.c +++ b/shared/systemd/src/basic/utf8.c @@ -50,7 +50,6 @@ bool unichar_is_valid(char32_t ch) { return true; } -#if 0 /* NM_IGNORED */ static bool unichar_is_control(char32_t ch) { /* @@ -62,7 +61,6 @@ static bool unichar_is_control(char32_t ch) { return (ch < ' ' && !IN_SET(ch, '\t', '\n')) || (0x7F <= ch && ch <= 0x9F); } -#endif /* NM_IGNORED */ /* count of characters used to encode one unicode char */ static size_t utf8_encoded_expected_len(uint8_t c) { @@ -127,7 +125,6 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { return 0; } -#if 0 /* NM_IGNORED */ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { const char *p; @@ -154,7 +151,6 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { return true; } -#endif /* NM_IGNORED */ char *utf8_is_valid(const char *str) { const char *p; diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c index 72f60ccbf0..f65937d8e0 100644 --- a/src/dhcp/nm-dhcp-systemd.c +++ b/src/dhcp/nm-dhcp-systemd.c @@ -735,7 +735,8 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, { gs_unref_object NMIP6Config *ip6_config = NULL; gs_unref_hashtable GHashTable *options = NULL; - struct in6_addr tmp_addr, *dns; + struct in6_addr tmp_addr; + const struct in6_addr *dns; uint32_t lft_pref, lft_valid; char addr_str[NM_UTILS_INET_ADDRSTRLEN]; char **domains; diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c index b28e0ba084..026015a717 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.c +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c @@ -17,7 +17,6 @@ #include "udev-util.h" #include "virt.h" -#define SYSTEMD_PEN 43793 #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 */ diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.h b/src/systemd/src/libsystemd-network/dhcp-identifier.h index b3115125d9..76abd6583e 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.h +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.h @@ -8,6 +8,8 @@ #include "time-util.h" #include "unaligned.h" +#define SYSTEMD_PEN 43793 + typedef enum DUIDType { DUID_TYPE_LLT = 1, DUID_TYPE_EN = 2, diff --git a/src/systemd/src/libsystemd-network/dhcp-internal.h b/src/systemd/src/libsystemd-network/dhcp-internal.h index 6a803d7b05..7e8149487a 100644 --- a/src/systemd/src/libsystemd-network/dhcp-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp-internal.h @@ -7,7 +7,6 @@ #include <linux/if_packet.h> #include <net/ethernet.h> -#include <net/if_arp.h> #include <stdint.h> #include "sd-dhcp-client.h" @@ -23,6 +22,11 @@ typedef struct sd_dhcp_option { size_t length; } sd_dhcp_option; +typedef struct DHCPServerData { + struct in_addr *addr; + size_t size; +} DHCPServerData; + extern const struct hash_ops dhcp_option_hash_ops; int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h index 5cbebb4a34..aed30d6118 100644 --- a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h @@ -5,11 +5,9 @@ Copyright © 2013 Intel Corporation. All rights reserved. ***/ -#include <stdint.h> -#include <linux/if_packet.h> - #include "sd-dhcp-client.h" +#include "dhcp-internal.h" #include "dhcp-protocol.h" #include "list.h" #include "util.h" @@ -52,20 +50,7 @@ struct sd_dhcp_lease { struct in_addr *router; size_t router_size; - struct in_addr *dns; - size_t dns_size; - - struct in_addr *ntp; - size_t ntp_size; - - struct in_addr *sip; - size_t sip_size; - - struct in_addr *pop3_server; - size_t pop3_server_size; - - struct in_addr *smtp_server; - size_t smtp_server_size; + DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX]; struct sd_dhcp_route *static_route; size_t static_route_size, static_route_allocated; diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h index 517e357d3d..b0d1216eed 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h @@ -11,9 +11,21 @@ #include "sd-event.h" #include "list.h" +#include "hashmap.h" #include "macro.h" #include "sparse-endian.h" +typedef struct sd_dhcp6_option { + unsigned n_ref; + + uint32_t enterprise_identifier; + uint16_t option; + void *data; + size_t length; +} sd_dhcp6_option; + +extern const struct hash_ops dhcp6_option_hash_ops; + /* Common option header */ typedef struct DHCP6Option { be16_t code; @@ -87,10 +99,13 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix); int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); +int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class); +int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **user_class); +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options); int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); int dhcp6_option_parse_status(DHCP6Option *option, size_t len); -int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia); +int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code); int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, struct in6_addr **addrs, size_t count, size_t *allocated); diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c index bb4c4d9130..d596752b3b 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-option.c +++ b/src/systemd/src/libsystemd-network/dhcp6-option.c @@ -11,6 +11,7 @@ #include "sd-dhcp6-client.h" #include "alloc-util.h" +#include "dhcp-identifier.h" #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" @@ -80,6 +81,39 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, return 0; } +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) { + sd_dhcp6_option *options; + Iterator i; + int r; + + assert(buf); + assert(*buf); + assert(buflen); + assert(vendor_options); + + ORDERED_HASHMAP_FOREACH(options, vendor_options, i) { + _cleanup_free_ uint8_t *p = NULL; + size_t total; + + total = 4 + 2 + 2 + options->length; + + p = malloc(total); + if (!p) + return -ENOMEM; + + unaligned_write_be32(p, options->enterprise_identifier); + unaligned_write_be16(p + 4, options->option); + unaligned_write_be16(p + 6, options->length); + memcpy(p + 8, options->data, options->length); + + r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_OPTS, total, p); + if (r < 0) + return r; + } + + return 0; +} + int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { uint16_t len; uint8_t *ia_hdr; @@ -169,6 +203,75 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { return r; } +int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class) { + _cleanup_free_ uint8_t *p = NULL; + size_t total = 0, offset = 0; + char **s; + + assert_return(buf && *buf && buflen && user_class, -EINVAL); + + STRV_FOREACH(s, user_class) { + size_t len = strlen(*s); + uint8_t *q; + + if (len > 0xffff) + return -ENAMETOOLONG; + q = realloc(p, total + len + 2); + if (!q) + return -ENOMEM; + + p = q; + + unaligned_write_be16(&p[offset], len); + memcpy(&p[offset + 2], *s, len); + + offset += 2 + len; + total += 2 + len; + } + + return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, total, p); +} + +int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **vendor_class) { + _cleanup_free_ uint8_t *p = NULL; + uint32_t enterprise_identifier; + size_t total, offset; + char **s; + + assert(buf); + assert(*buf); + assert(buflen); + assert(vendor_class); + + enterprise_identifier = htobe32(SYSTEMD_PEN); + + p = memdup(&enterprise_identifier, sizeof(enterprise_identifier)); + if (!p) + return -ENOMEM; + + total = sizeof(enterprise_identifier); + offset = total; + + STRV_FOREACH(s, vendor_class) { + size_t len = strlen(*s); + uint8_t *q; + + q = realloc(p, total + len + 2); + if (!q) + return -ENOMEM; + + p = q; + + unaligned_write_be16(&p[offset], len); + memcpy(&p[offset + 2], *s, len); + + offset += 2 + len; + total += 2 + len; + } + + return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p); +} + int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix) { DHCP6Option *option = (DHCP6Option *)buf; size_t i = sizeof(*option) + sizeof(pd->ia_pd); @@ -349,13 +452,13 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, return 0; } -int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { +int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code) { + uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX; uint16_t iatype, optlen; - size_t i, len; + size_t iaaddr_offset; int r = 0, status; + size_t i, len; uint16_t opt; - size_t iaaddr_offset; - uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX; assert_return(ia, -EINVAL); assert_return(!ia->addresses, -EINVAL); @@ -465,11 +568,15 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data)); if (status < 0) return status; + if (status > 0) { - log_dhcp6_client(client, "IA status %d", - status); + if (ret_status_code) + *ret_status_code = status; - return -EINVAL; + log_dhcp6_client(client, "IA status %s", + dhcp6_message_status_to_string(status)); + + return 0; } break; @@ -513,7 +620,10 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { break; } - return 0; + if (ret_status_code) + *ret_status_code = 0; + + return 1; } int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, @@ -599,3 +709,44 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * return idx; } + +static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) { + if (!i) + return NULL; + + free(i->data); + return mfree(i); +} + +int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret) { + assert_return(ret, -EINVAL); + assert_return(length == 0 || data, -EINVAL); + + _cleanup_free_ void *q = memdup(data, length); + if (!q) + return -ENOMEM; + + sd_dhcp6_option *p = new(sd_dhcp6_option, 1); + if (!p) + return -ENOMEM; + + *p = (sd_dhcp6_option) { + .n_ref = 1, + .option = option, + .enterprise_identifier = enterprise_identifier, + .length = length, + .data = TAKE_PTR(q), + }; + + *ret = p; + return 0; +} + +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_option, sd_dhcp6_option, dhcp6_option_free); +DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR( + dhcp6_option_hash_ops, + void, + trivial_hash_func, + trivial_compare_func, + sd_dhcp6_option, + sd_dhcp6_option_unref); diff --git a/src/systemd/src/libsystemd-network/dhcp6-protocol.h b/src/systemd/src/libsystemd-network/dhcp6-protocol.h index ffae4453ac..f7a2702860 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-protocol.h +++ b/src/systemd/src/libsystemd-network/dhcp6-protocol.h @@ -82,14 +82,35 @@ enum { DHCP6_NTP_SUBOPTION_SRV_FQDN = 3, }; +/* + * RFC 8415, RFC 5007 and RFC 7653 status codes: + * https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-5 + */ enum { - DHCP6_STATUS_SUCCESS = 0, - DHCP6_STATUS_UNSPEC_FAIL = 1, - DHCP6_STATUS_NO_ADDRS_AVAIL = 2, - DHCP6_STATUS_NO_BINDING = 3, - DHCP6_STATUS_NOT_ON_LINK = 4, - DHCP6_STATUS_USE_MULTICAST = 5, - _DHCP6_STATUS_MAX = 6, + DHCP6_STATUS_SUCCESS = 0, + DHCP6_STATUS_UNSPEC_FAIL = 1, + DHCP6_STATUS_NO_ADDRS_AVAIL = 2, + DHCP6_STATUS_NO_BINDING = 3, + DHCP6_STATUS_NOT_ON_LINK = 4, + DHCP6_STATUS_USE_MULTICAST = 5, + DHCP6_STATUS_NO_PREFIX_AVAIL = 6, + DHCP6_STATUS_UNKNOWN_QUERY_TYPE = 7, + DHCP6_STATUS_MALFORMED_QUERY = 8, + DHCP6_STATUS_NOT_CONFIGURED = 9, + DHCP6_STATUS_NOT_ALLOWED = 10, + DHCP6_STATUS_QUERY_TERMINATED = 11, + DHCP6_STATUS_DATA_MISSING = 12, + DHCP6_STATUS_CATCHUP_COMPLETE = 13, + DHCP6_STATUS_NOT_SUPPORTED = 14, + DHCP6_STATUS_TLS_CONNECTION_REFUSED = 15, + DHCP6_STATUS_ADDRESS_IN_USE = 16, + DHCP6_STATUS_CONFIGURATION_CONFLICT = 17, + DHCP6_STATUS_MISSING_BINDING_INFORMATION = 18, + DHCP6_STATUS_OUTDATED_BINDING_INFORMATION = 19, + DHCP6_STATUS_SERVER_SHUTTING_DOWN = 20, + DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED = 21, + DHCP6_STATUS_EXCESSIVE_TIME_SKEW = 22, + _DHCP6_STATUS_MAX = 23, }; enum { diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c index 238d718bfb..9baa8e8ca9 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.c +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c @@ -52,6 +52,7 @@ static void lldp_neighbor_free(sd_lldp_neighbor *n) { free(n->port_description); free(n->system_name); free(n->system_description); + free(n->mud_url); free(n->chassis_id_as_string); free(n->port_id_as_string); free(n); @@ -294,9 +295,20 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { break; - case SD_LLDP_TYPE_PRIVATE: + case SD_LLDP_TYPE_PRIVATE: { if (length < 4) log_lldp("Found private TLV that is too short, ignoring."); + else { + /* RFC 8520: MUD URL */ + if (memcmp(p, SD_LLDP_OUI_MUD, sizeof(SD_LLDP_OUI_MUD)) == 0 && + p[sizeof(SD_LLDP_OUI_MUD)] == SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION) { + r = parse_string(&n->mud_url, p + sizeof(SD_LLDP_OUI_MUD) + 1, + length - 1 - sizeof(SD_LLDP_OUI_MUD)); + if (r < 0) + return r; + } + } + } break; } @@ -595,6 +607,17 @@ _public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const ch return 0; } +_public_ int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (!n->mud_url) + return -ENODATA; + + *ret = n->mud_url; + return 0; +} + _public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { assert_return(n, -EINVAL); assert_return(ret, -EINVAL); diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.h b/src/systemd/src/libsystemd-network/lldp-neighbor.h index 62dbff42ca..74175edf54 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.h +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.h @@ -54,6 +54,7 @@ struct sd_lldp_neighbor { char *port_description; char *system_name; char *system_description; + char *mud_url; uint16_t port_vlan_id; diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c index 94a6236a57..6a7382ac07 100644 --- a/src/systemd/src/libsystemd-network/network-internal.c +++ b/src/systemd/src/libsystemd-network/network-internal.c @@ -195,30 +195,34 @@ bool net_match_config(Set *match_mac, Set *match_permanent_mac, char * const *match_paths, char * const *match_drivers, - char * const *match_types, + char * const *match_iftypes, char * const *match_names, char * const *match_property, char * const *match_wifi_iftype, char * const *match_ssid, Set *match_bssid, - unsigned short iftype, sd_device *device, const struct ether_addr *dev_mac, const struct ether_addr *dev_permanent_mac, + const char *dev_driver, + unsigned short dev_iftype, const char *dev_name, char * const *alternative_names, - enum nl80211_iftype wifi_iftype, - const char *ssid, - const struct ether_addr *bssid) { + enum nl80211_iftype dev_wifi_iftype, + const char *dev_ssid, + const struct ether_addr *dev_bssid) { - const char *dev_path = NULL, *dev_driver = NULL, *mac_str; - _cleanup_free_ char *dev_type; + _cleanup_free_ char *dev_iftype_str; + const char *dev_path = NULL; - dev_type = link_get_type_string(iftype, device); + dev_iftype_str = link_get_type_string(dev_iftype, device); if (device) { + const char *mac_str; + (void) sd_device_get_property_value(device, "ID_PATH", &dev_path); - (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver); + if (!dev_driver) + (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver); if (!dev_name) (void) sd_device_get_sysname(device, &dev_name); if (!dev_mac && @@ -241,7 +245,7 @@ bool net_match_config(Set *match_mac, if (!net_condition_test_strv(match_drivers, dev_driver)) return false; - if (!net_condition_test_strv(match_types, dev_type)) + if (!net_condition_test_strv(match_iftypes, dev_iftype_str)) return false; if (!net_condition_test_ifname(match_names, dev_name, alternative_names)) @@ -250,13 +254,13 @@ bool net_match_config(Set *match_mac, if (!net_condition_test_property(match_property, device)) return false; - if (!net_condition_test_strv(match_wifi_iftype, wifi_iftype_to_string(wifi_iftype))) + if (!net_condition_test_strv(match_wifi_iftype, wifi_iftype_to_string(dev_wifi_iftype))) return false; - if (!net_condition_test_strv(match_ssid, ssid)) + if (!net_condition_test_strv(match_ssid, dev_ssid)) return false; - if (match_bssid && (!bssid || !set_contains(match_bssid, bssid))) + if (match_bssid && (!dev_bssid || !set_contains(match_bssid, dev_bssid))) return false; return true; @@ -660,27 +664,27 @@ int config_parse_bridge_port_priority( size_t serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size, - bool with_leading_space, + bool *with_leading_space, bool (*predicate)(const struct in_addr *addr)) { - size_t count; - size_t i; - assert(f); assert(addresses); - count = 0; + size_t count = 0; + bool _space = false; + if (!with_leading_space) + with_leading_space = &_space; - for (i = 0; i < size; i++) { + for (size_t i = 0; i < size; i++) { char sbuf[INET_ADDRSTRLEN]; if (predicate && !predicate(&addresses[i])) continue; - if (with_leading_space) + + if (*with_leading_space) fputc(' ', f); - else - with_leading_space = true; fputs(inet_ntop(AF_INET, &addresses[i], sbuf, sizeof(sbuf)), f); count++; + *with_leading_space = true; } return count; @@ -722,20 +726,22 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) { return size; } -void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size) { - unsigned i; - +void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size, bool *with_leading_space) { assert(f); assert(addresses); assert(size); - for (i = 0; i < size; i++) { - char buffer[INET6_ADDRSTRLEN]; + bool _space = false; + if (!with_leading_space) + with_leading_space = &_space; - fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f); + for (size_t i = 0; i < size; i++) { + char buffer[INET6_ADDRSTRLEN]; - if (i < size - 1) + if (*with_leading_space) fputc(' ', f); + fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f); + *with_leading_space = true; } } @@ -776,8 +782,6 @@ int deserialize_in6_addrs(struct in6_addr **ret, const char *string) { } void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) { - unsigned i; - assert(f); assert(key); assert(routes); @@ -785,7 +789,7 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz fprintf(f, "%s=", key); - for (i = 0; i < size; i++) { + for (size_t i = 0; i < size; i++) { char sbuf[INET_ADDRSTRLEN]; struct in_addr dest, gw; uint8_t length; @@ -794,8 +798,8 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0); assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0); - fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof(sbuf)), length); - fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof(sbuf)), (i < (size - 1)) ? " ": ""); + fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof sbuf), length); + fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof sbuf), i < size - 1 ? " ": ""); } fputs("\n", f); diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h index 6724cbbf1f..8e6377e0b0 100644 --- a/src/systemd/src/libsystemd-network/network-internal.h +++ b/src/systemd/src/libsystemd-network/network-internal.h @@ -17,23 +17,24 @@ #if 0 /* NM_IGNORED */ bool net_match_config(Set *match_mac, Set *match_permanent_mac, - char * const *match_path, - char * const *match_driver, - char * const *match_type, - char * const *match_name, + char * const *match_paths, + char * const *match_drivers, + char * const *match_iftypes, + char * const *match_names, char * const *match_property, char * const *match_wifi_iftype, char * const *match_ssid, Set *match_bssid, - unsigned short iftype, sd_device *device, const struct ether_addr *dev_mac, const struct ether_addr *dev_permanent_mac, + const char *dev_driver, + unsigned short dev_iftype, const char *dev_name, char * const *alternative_names, - enum nl80211_iftype wifi_iftype, - const char *ssid, - const struct ether_addr *bssid); + enum nl80211_iftype dev_wifi_iftype, + const char *dev_ssid, + const struct ether_addr *dev_bssid); CONFIG_PARSER_PROTOTYPE(config_parse_net_condition); CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr); @@ -51,11 +52,12 @@ const char *net_get_name_persistent(sd_device *device); size_t serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size, - bool with_leading_space, + bool *with_leading_space, bool (*predicate)(const struct in_addr *addr)); int deserialize_in_addrs(struct in_addr **addresses, const char *string); void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, - size_t size); + size_t size, + bool *with_leading_space); int deserialize_in6_addrs(struct in6_addr **addresses, const char *string); /* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */ diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c index 63321cca64..70821ac279 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -29,6 +29,7 @@ #include "random-util.h" #include "string-util.h" #include "strv.h" +#include "utf8.h" #include "web-util.h" #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */ @@ -37,6 +38,32 @@ #define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC) #define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE) +typedef struct sd_dhcp_client_id { + uint8_t type; + union { + struct { + /* 0: Generic (non-LL) (RFC 2132) */ + uint8_t data[MAX_CLIENT_ID_LEN]; + } _packed_ gen; + struct { + /* 1: Ethernet Link-Layer (RFC 2132) */ + uint8_t haddr[ETH_ALEN]; + } _packed_ eth; + struct { + /* 2 - 254: ARP/Link-Layer (RFC 2132) */ + uint8_t haddr[0]; + } _packed_ ll; + struct { + /* 255: Node-specific (RFC 4361) */ + be32_t iaid; + struct duid duid; + } _packed_ ns; + struct { + uint8_t data[MAX_CLIENT_ID_LEN]; + } _packed_ raw; + }; +} _packed_ sd_dhcp_client_id; + struct sd_dhcp_client { unsigned n_ref; @@ -58,37 +85,14 @@ struct sd_dhcp_client { uint8_t mac_addr[MAX_MAC_ADDR_LEN]; size_t mac_addr_len; uint16_t arp_type; - struct { - uint8_t type; - union { - struct { - /* 0: Generic (non-LL) (RFC 2132) */ - uint8_t data[MAX_CLIENT_ID_LEN]; - } _packed_ gen; - struct { - /* 1: Ethernet Link-Layer (RFC 2132) */ - uint8_t haddr[ETH_ALEN]; - } _packed_ eth; - struct { - /* 2 - 254: ARP/Link-Layer (RFC 2132) */ - uint8_t haddr[0]; - } _packed_ ll; - struct { - /* 255: Node-specific (RFC 4361) */ - be32_t iaid; - struct duid duid; - } _packed_ ns; - struct { - uint8_t data[MAX_CLIENT_ID_LEN]; - } _packed_ raw; - }; - } _packed_ client_id; + sd_dhcp_client_id client_id; size_t client_id_len; char *hostname; char *vendor_class_identifier; char *mudurl; char **user_class; uint32_t mtu; + uint32_t fallback_lease_lifetime; uint32_t xid; usec_t start_time; uint64_t attempt; @@ -152,6 +156,60 @@ static int client_receive_message_udp( void *userdata); static void client_stop(sd_dhcp_client *client, int error); +int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) { + const sd_dhcp_client_id *client_id = data; + _cleanup_free_ char *t = NULL; + int r = 0; + + assert_return(data, -EINVAL); + assert_return(len >= 1, -EINVAL); + assert_return(ret, -EINVAL); + + len -= 1; + if (len > MAX_CLIENT_ID_LEN) + return -EINVAL; + + switch (client_id->type) { + case 0: + if (utf8_is_printable((char *) client_id->gen.data, len)) + r = asprintf(&t, "%.*s", (int) len, client_id->gen.data); + else + r = asprintf(&t, "DATA"); + break; + case 1: + if (len != sizeof_field(sd_dhcp_client_id, eth)) + return -EINVAL; + + r = asprintf(&t, "%x:%x:%x:%x:%x:%x", + client_id->eth.haddr[0], + client_id->eth.haddr[1], + client_id->eth.haddr[2], + client_id->eth.haddr[3], + client_id->eth.haddr[4], + client_id->eth.haddr[5]); + break; + case 2 ... 254: + r = asprintf(&t, "ARP/LL"); + break; + case 255: + if (len < 6) + return -EINVAL; + + uint32_t iaid = be32toh(client_id->ns.iaid); + uint16_t duid_type = be16toh(client_id->ns.duid.type); + if (dhcp_validate_duid_len(duid_type, len - 6, true) < 0) + return -EINVAL; + + r = asprintf(&t, "IAID:0x%x/DUID", iaid); + break; + } + + if (r < 0) + return -ENOMEM; + *ret = TAKE_PTR(t); + return 0; +} + int sd_dhcp_client_set_callback( sd_dhcp_client *client, sd_dhcp_client_callback_t cb, @@ -617,6 +675,15 @@ int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) { return 0; } +int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint32_t fallback_lease_lifetime) { + assert_return(client, -EINVAL); + assert_return(fallback_lease_lifetime > 0, -EINVAL); + + client->fallback_lease_lifetime = fallback_lease_lifetime; + + return 0; +} + static int client_notify(sd_dhcp_client *client, int event) { assert(client); @@ -855,36 +922,12 @@ static int dhcp_client_send_raw( packet, len); } -static int client_send_discover(sd_dhcp_client *client) { - _cleanup_free_ DHCPPacket *discover = NULL; - size_t optoffset, optlen; +static int client_append_common_discover_request_options(sd_dhcp_client *client, DHCPPacket *packet, size_t *optoffset, size_t optlen) { sd_dhcp_option *j; Iterator i; int r; assert(client); - assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING)); - - r = client_message_init(client, &discover, DHCP_DISCOVER, - &optlen, &optoffset); - if (r < 0) - return r; - - /* the client may suggest values for the network address - and lease time in the DHCPDISCOVER message. The client may include - the ’requested IP address’ option to suggest that a particular IP - address be assigned, and may include the ’IP address lease time’ - option to suggest the lease time it would like. - */ - /* RFC7844 section 3: - SHOULD NOT contain any other option. */ - if (!client->anonymize && client->last_addr != INADDR_ANY) { - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, - 4, &client->last_addr); - if (r < 0) - return r; - } if (client->hostname) { /* According to RFC 4702 "clients that send the Client FQDN option in @@ -895,18 +938,18 @@ static int client_send_discover(sd_dhcp_client *client) { /* it is unclear from RFC 2131 if client should send hostname in DHCPDISCOVER but dhclient does and so we do as well */ - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0, SD_DHCP_OPTION_HOST_NAME, strlen(client->hostname), client->hostname); } else - r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset, + r = client_append_fqdn_option(&packet->dhcp, optlen, optoffset, client->hostname); if (r < 0) return r; } if (client->vendor_class_identifier) { - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, strlen(client->vendor_class_identifier), client->vendor_class_identifier); @@ -915,7 +958,7 @@ static int client_send_discover(sd_dhcp_client *client) { } if (client->mudurl) { - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0, SD_DHCP_OPTION_MUD_URL, strlen(client->mudurl), client->mudurl); @@ -924,7 +967,7 @@ static int client_send_discover(sd_dhcp_client *client) { } if (client->user_class) { - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0, SD_DHCP_OPTION_USER_CLASS, strv_length(client->user_class), client->user_class); @@ -933,7 +976,7 @@ static int client_send_discover(sd_dhcp_client *client) { } ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) { - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0, j->option, j->length, j->data); if (r < 0) return r; @@ -941,13 +984,50 @@ static int client_send_discover(sd_dhcp_client *client) { if (!ordered_hashmap_isempty(client->vendor_options)) { r = dhcp_option_append( - &discover->dhcp, optlen, &optoffset, 0, + &packet->dhcp, optlen, optoffset, 0, SD_DHCP_OPTION_VENDOR_SPECIFIC, ordered_hashmap_size(client->vendor_options), client->vendor_options); if (r < 0) return r; } + + return 0; +} + +static int client_send_discover(sd_dhcp_client *client) { + _cleanup_free_ DHCPPacket *discover = NULL; + size_t optoffset, optlen; + int r; + + assert(client); + assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING)); + + r = client_message_init(client, &discover, DHCP_DISCOVER, + &optlen, &optoffset); + if (r < 0) + return r; + + /* the client may suggest values for the network address + and lease time in the DHCPDISCOVER message. The client may include + the ’requested IP address’ option to suggest that a particular IP + address be assigned, and may include the ’IP address lease time’ + option to suggest the lease time it would like. + */ + /* RFC7844 section 3: + SHOULD NOT contain any other option. */ + if (!client->anonymize && client->last_addr != INADDR_ANY) { + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, + 4, &client->last_addr); + if (r < 0) + return r; + } + + r = client_append_common_discover_request_options(client, discover, &optoffset, optlen); + if (r < 0) + return r; + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL); if (r < 0) @@ -1039,36 +1119,9 @@ static int client_send_request(sd_dhcp_client *client) { return -EINVAL; } - if (client->hostname) { - if (dns_name_is_single_label(client->hostname)) - r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_HOST_NAME, - strlen(client->hostname), client->hostname); - else - r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset, - client->hostname); - if (r < 0) - return r; - } - - if (client->vendor_class_identifier) { - r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, - strlen(client->vendor_class_identifier), - client->vendor_class_identifier); - if (r < 0) - return r; - } - - if (client->mudurl) { - r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_MUD_URL, - strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - + r = client_append_common_discover_request_options(client, request, &optoffset, optlen); + if (r < 0) + return r; r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL); @@ -1424,6 +1477,9 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_ lease->next_server = offer->siaddr; lease->address = offer->yiaddr; + if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0) + lease->lifetime = client->fallback_lease_lifetime; + if (lease->address == 0 || lease->server_address == 0 || lease->lifetime == 0) { @@ -1904,13 +1960,13 @@ static int client_receive_message_raw( sd_dhcp_client *client = userdata; _cleanup_free_ DHCPPacket *packet = NULL; - uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))]; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct tpacket_auxdata))) control; struct iovec iov = {}; struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, - .msg_control = cmsgbuf, - .msg_controllen = sizeof(cmsgbuf), + .msg_control = &control, + .msg_controllen = sizeof(control), }; struct cmsghdr *cmsg; bool checksum = true; @@ -1932,25 +1988,21 @@ static int client_receive_message_raw( iov = IOVEC_MAKE(packet, buflen); - len = recvmsg(fd, &msg, 0); - if (len < 0) { - if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN)) - return 0; - - return log_dhcp_client_errno(client, errno, - "Could not receive message from raw socket: %m"); - } else if ((size_t)len < sizeof(DHCPPacket)) + len = recvmsg_safe(fd, &msg, 0); + if (IN_SET(len, -EAGAIN, -EINTR, -ENETDOWN)) return 0; + if (len < 0) + return log_dhcp_client_errno(client, len, + "Could not receive message from raw socket: %m"); - CMSG_FOREACH(cmsg, &msg) - if (cmsg->cmsg_level == SOL_PACKET && - cmsg->cmsg_type == PACKET_AUXDATA && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) { - struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg); + if ((size_t) len < sizeof(DHCPPacket)) + return 0; - checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY); - break; - } + cmsg = cmsg_find(&msg, SOL_PACKET, PACKET_AUXDATA, CMSG_LEN(sizeof(struct tpacket_auxdata))); + if (cmsg) { + struct tpacket_auxdata *aux = (struct tpacket_auxdata*) CMSG_DATA(cmsg); + checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY); + } r = dhcp_packet_verify_headers(packet, len, checksum, client->port); if (r < 0) diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index 86f2129062..ae581f910c 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -98,59 +98,40 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) { return 0; } -int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) { +int sd_dhcp_lease_get_servers( + sd_dhcp_lease *lease, + sd_dhcp_lease_server_type what, + const struct in_addr **addr) { + assert_return(lease, -EINVAL); + assert_return(what >= 0, -EINVAL); + assert_return(what < _SD_DHCP_LEASE_SERVER_TYPE_MAX, -EINVAL); assert_return(addr, -EINVAL); - if (lease->dns_size <= 0) + if (lease->servers[what].size <= 0) return -ENODATA; - *addr = lease->dns; - return (int) lease->dns_size; + *addr = lease->servers[what].addr; + return (int) lease->servers[what].size; } +int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) { + return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_DNS, addr); +} int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - - if (lease->ntp_size <= 0) - return -ENODATA; - - *addr = lease->ntp; - return (int) lease->ntp_size; + return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_NTP, addr); } - int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - - if (lease->sip_size <= 0) - return -ENODATA; - - *addr = lease->sip; - return (int) lease->sip_size; + return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SIP, addr); } - -int sd_dhcp_lease_get_pop3_server(sd_dhcp_lease *lease, const struct in_addr **addr) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - - if (lease->pop3_server_size <= 0) - return -ENODATA; - - *addr = lease->pop3_server; - return (int) lease->pop3_server_size; +int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr) { + return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_POP3, addr); } - -int sd_dhcp_lease_get_smtp_server(sd_dhcp_lease *lease, const struct in_addr **addr) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - - if (lease->smtp_server_size <= 0) - return -ENODATA; - - *addr = lease->smtp_server; - return (int) lease->smtp_server_size; +int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr) { + return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SMTP, addr); +} +int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr) { + return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_LPR, addr); } int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) { @@ -300,11 +281,10 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) { free(lease->timezone); free(lease->hostname); free(lease->domainname); - free(lease->dns); - free(lease->ntp); - free(lease->sip); - free(lease->pop3_server); - free(lease->smtp_server); + + for (sd_dhcp_lease_server_type i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++) + free(lease->servers[i].addr); + free(lease->static_route); free(lease->client_id); free(lease->vendor_specific); @@ -410,7 +390,7 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) { } static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) { - assert(option); + assert(option || len == 0); assert(ret); assert(n_ret); @@ -439,33 +419,24 @@ static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_add } static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) { - assert(option); + assert(option || len == 0); assert(ret); assert(n_ret); - if (len <= 0) { - *ret = mfree(*ret); - *n_ret = 0; - } else { - size_t n_addresses; - struct in_addr *addresses; - int l = len - 1; - - if (l % 4 != 0) - return -EINVAL; - - n_addresses = l / 4; + if (len <= 0) + return -EINVAL; - addresses = newdup(struct in_addr, option + 1, n_addresses); - if (!addresses) - return -ENOMEM; + /* The SIP record is like the other, regular server records, but prefixed with a single "encoding" + * byte that is either 0 or 1. We only support it to be 1 for now. Let's drop it and parse it like + * the other fields */ - free(*ret); - *ret = addresses; - *n_ret = n_addresses; + if (option[0] != 1) { /* We only support IP address encoding for now */ + *ret = mfree(*ret); + *n_ret = 0; + return 0; } - return 0; + return lease_parse_in_addrs(option + 1, len - 1, ret, n_ret); } static int lease_parse_routes( @@ -610,35 +581,41 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; case SD_DHCP_OPTION_DOMAIN_NAME_SERVER: - r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size); + r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_DNS].addr, &lease->servers[SD_DHCP_LEASE_DNS].size); if (r < 0) log_debug_errno(r, "Failed to parse DNS server, ignoring: %m"); break; case SD_DHCP_OPTION_NTP_SERVER: - r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size); + r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_NTP].addr, &lease->servers[SD_DHCP_LEASE_NTP].size); if (r < 0) log_debug_errno(r, "Failed to parse NTP server, ignoring: %m"); break; case SD_DHCP_OPTION_SIP_SERVER: - r = lease_parse_sip_server(option, len, &lease->sip, &lease->sip_size); + r = lease_parse_sip_server(option, len, &lease->servers[SD_DHCP_LEASE_SIP].addr, &lease->servers[SD_DHCP_LEASE_SIP].size); if (r < 0) log_debug_errno(r, "Failed to parse SIP server, ignoring: %m"); break; case SD_DHCP_OPTION_POP3_SERVER: - r = lease_parse_in_addrs(option, len, &lease->pop3_server, &lease->pop3_server_size); + r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_POP3].addr, &lease->servers[SD_DHCP_LEASE_POP3].size); if (r < 0) log_debug_errno(r, "Failed to parse POP3 server, ignoring: %m"); break; case SD_DHCP_OPTION_SMTP_SERVER: - r = lease_parse_in_addrs(option, len, &lease->smtp_server, &lease->smtp_server_size); + r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_SMTP].addr, &lease->servers[SD_DHCP_LEASE_SMTP].size); if (r < 0) log_debug_errno(r, "Failed to parse SMTP server, ignoring: %m"); break; + case SD_DHCP_OPTION_LPR_SERVER: + r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_LPR].addr, &lease->servers[SD_DHCP_LEASE_LPR].size); + if (r < 0) + log_debug_errno(r, "Failed to parse LPR server, ignoring: %m"); + break; + case SD_DHCP_OPTION_STATIC_ROUTE: r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated); if (r < 0) @@ -674,7 +651,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void case SD_DHCP_OPTION_HOST_NAME: r = lease_parse_domain(option, len, &lease->hostname); if (r < 0) { - log_debug_errno(r, "Failed to parse host name, ignoring: %m"); + log_debug_errno(r, "Failed to parse hostname, ignoring: %m"); return 0; } @@ -1075,8 +1052,9 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { *dns = NULL, *ntp = NULL, *sip = NULL, - *pop3_server = NULL, - *smtp_server = NULL, + *pop3 = NULL, + *smtp = NULL, + *lpr = NULL, *mtu = NULL, *routes = NULL, *domains = NULL, @@ -1106,8 +1084,9 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { "DNS", &dns, "NTP", &ntp, "SIP", &sip, - "POP3_SERVERS", &pop3_server, - "SMTP_SERVERS", &smtp_server, + "POP3", &pop3, + "SMTP", &smtp, + "LPR", &lpr, "MTU", &mtu, "DOMAINNAME", &lease->domainname, "HOSTNAME", &lease->hostname, @@ -1197,43 +1176,51 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { } if (dns) { - r = deserialize_in_addrs(&lease->dns, dns); + r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_DNS].addr, dns); if (r < 0) log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns); else - lease->dns_size = r; + lease->servers[SD_DHCP_LEASE_DNS].size = r; } if (ntp) { - r = deserialize_in_addrs(&lease->ntp, ntp); + r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_NTP].addr, ntp); if (r < 0) log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp); else - lease->ntp_size = r; + lease->servers[SD_DHCP_LEASE_NTP].size = r; } if (sip) { - r = deserialize_in_addrs(&lease->sip, sip); + r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_SIP].addr, sip); if (r < 0) log_debug_errno(r, "Failed to deserialize SIP servers %s, ignoring: %m", sip); else - lease->sip_size = r; + lease->servers[SD_DHCP_LEASE_SIP].size = r; + } + + if (pop3) { + r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_POP3].addr, pop3); + if (r < 0) + log_debug_errno(r, "Failed to deserialize POP3 server %s, ignoring: %m", pop3); + else + lease->servers[SD_DHCP_LEASE_POP3].size = r; } - if (pop3_server) { - r = deserialize_in_addrs(&lease->pop3_server, pop3_server); + if (smtp) { + r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_SMTP].addr, smtp); if (r < 0) - log_debug_errno(r, "Failed to deserialize POP3 server %s, ignoring: %m", pop3_server); + log_debug_errno(r, "Failed to deserialize SMTP server %s, ignoring: %m", smtp); else - lease->pop3_server_size = r; + lease->servers[SD_DHCP_LEASE_SMTP].size = r; } - if (smtp_server) { - r = deserialize_in_addrs(&lease->smtp_server, smtp_server); + if (lpr) { + r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_LPR].addr, lpr); if (r < 0) - log_debug_errno(r, "Failed to deserialize SMTP server %s, ignoring: %m", smtp_server); + log_debug_errno(r, "Failed to deserialize LPR server %s, ignoring: %m", lpr); else - lease->smtp_server_size = r; + lease->servers[SD_DHCP_LEASE_LPR].size = r; } if (mtu) { diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c index 02ebb0c201..d653b2571c 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c @@ -20,6 +20,7 @@ #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 "network-internal.h" @@ -69,6 +70,8 @@ struct sd_dhcp6_client { size_t req_opts_len; char *fqdn; char *mudurl; + char **user_class; + char **vendor_class; sd_event_source *receive_message; usec_t retransmit_time; uint8_t retransmit_count; @@ -80,6 +83,8 @@ struct sd_dhcp6_client { size_t duid_len; usec_t information_request_time_usec; usec_t information_refresh_time_usec; + OrderedHashmap *extra_options; + OrderedHashmap *vendor_options; }; static const uint16_t default_req_opts[] = { @@ -108,12 +113,29 @@ const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = { DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int); const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { - [DHCP6_STATUS_SUCCESS] = "Success", - [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure", - [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available", - [DHCP6_STATUS_NO_BINDING] = "Binding unavailable", - [DHCP6_STATUS_NOT_ON_LINK] = "Not on link", - [DHCP6_STATUS_USE_MULTICAST] = "Use multicast", + [DHCP6_STATUS_SUCCESS] = "Success", + [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure", + [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available", + [DHCP6_STATUS_NO_BINDING] = "Binding unavailable", + [DHCP6_STATUS_NOT_ON_LINK] = "Not on link", + [DHCP6_STATUS_USE_MULTICAST] = "Use multicast", + [DHCP6_STATUS_NO_PREFIX_AVAIL] = "No prefix available", + [DHCP6_STATUS_UNKNOWN_QUERY_TYPE] = "Unknown query type", + [DHCP6_STATUS_MALFORMED_QUERY] = "Malformed query", + [DHCP6_STATUS_NOT_CONFIGURED] = "Not configured", + [DHCP6_STATUS_NOT_ALLOWED] = "Not allowed", + [DHCP6_STATUS_QUERY_TERMINATED] = "Query terminated", + [DHCP6_STATUS_DATA_MISSING] = "Data missing", + [DHCP6_STATUS_CATCHUP_COMPLETE] = "Catch up complete", + [DHCP6_STATUS_NOT_SUPPORTED] = "Not supported", + [DHCP6_STATUS_TLS_CONNECTION_REFUSED] = "TLS connection refused", + [DHCP6_STATUS_ADDRESS_IN_USE] = "Address in use", + [DHCP6_STATUS_CONFIGURATION_CONFLICT] = "Configuration conflict", + [DHCP6_STATUS_MISSING_BINDING_INFORMATION] = "Missing binding information", + [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information", + [DHCP6_STATUS_SERVER_SHUTTING_DOWN] = "Server shutting down", + [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED] = "DNS update not supported", + [DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew", }; DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int); @@ -207,6 +229,25 @@ int sd_dhcp6_client_set_prefix_delegation_hint( return 0; } +int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) { + int r; + + assert_return(client, -EINVAL); + assert_return(v, -EINVAL); + + r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp6_option_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_put(client->vendor_options, v, v); + if (r < 0) + return r; + + sd_dhcp6_option_ref(v); + + return 1; +} + static int client_ensure_duid(sd_dhcp6_client *client) { if (client->duid_len != 0) return 0; @@ -237,7 +278,8 @@ static int dhcp6_client_set_duid_internal( 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, "Setting DUID of type %u with unexpected content", duid_type); + + log_dhcp6_client(client, "Using DUID of type %u of incorrect length, proceeding.", duid_type); } client->duid.type = htobe16(duid_type); @@ -296,6 +338,48 @@ int sd_dhcp6_client_set_duid_llt( return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time); } +static const char* const dhcp6_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_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_duid_type, DUIDType); + +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 r; + + assert_return(client, -EINVAL); + assert_return(client->duid_len > 0, -ENODATA); + + v = dhcp6_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); + + return 0; +} + int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { assert_return(client, -EINVAL); assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); @@ -307,6 +391,18 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { return 0; } +int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) { + assert_return(client, -EINVAL); + assert_return(iaid, -EINVAL); + + if (!client->iaid_set) + return -ENODATA; + + *iaid = be32toh(client->ia_na.ia_na.id); + + return 0; +} + int sd_dhcp6_client_set_fqdn( sd_dhcp6_client *client, const char *fqdn) { @@ -345,18 +441,8 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) assert_return(client, -EINVAL); assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); - switch(option) { - - case SD_DHCP6_OPTION_DNS_SERVERS: - case SD_DHCP6_OPTION_DOMAIN_LIST: - case SD_DHCP6_OPTION_SNTP_SERVERS: - case SD_DHCP6_OPTION_NTP_SERVER: - case SD_DHCP6_OPTION_RAPID_COMMIT: - break; - - default: + if (option <= 0 || option >= UINT8_MAX) return -EINVAL; - } for (t = 0; t < client->req_opts_len; t++) if (client->req_opts[t] == htobe16(option)) @@ -376,12 +462,55 @@ int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mud assert_return(client, -EINVAL); assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); assert_return(mudurl, -EINVAL); - assert_return(strlen(mudurl) <= 255, -EINVAL); + assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL); assert_return(http_url_is_valid(mudurl), -EINVAL); return free_and_strdup(&client->mudurl, mudurl); } +int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char **user_class) { + _cleanup_strv_free_ char **s = NULL; + char **p; + + assert_return(client, -EINVAL); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + + assert_return(user_class, -EINVAL); + + STRV_FOREACH(p, user_class) + if (strlen(*p) > UINT16_MAX) + return -ENAMETOOLONG; + + s = strv_copy(user_class); + if (!s) + return -ENOMEM; + + client->user_class = TAKE_PTR(s); + + return 0; +} + +int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char **vendor_class) { + _cleanup_strv_free_ char **s = NULL; + char **p; + + assert_return(client, -EINVAL); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(vendor_class, -EINVAL); + + STRV_FOREACH(p, vendor_class) + if (strlen(*p) > UINT8_MAX) + return -ENAMETOOLONG; + + s = strv_copy(vendor_class); + if (!s) + return -ENOMEM; + + client->vendor_class = TAKE_PTR(s); + + return 0; +} + int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) { assert_return(client, -EINVAL); assert_return(delegation, -EINVAL); @@ -436,6 +565,24 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { return 0; } +int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) { + int r; + + assert_return(client, -EINVAL); + assert_return(v, -EINVAL); + + r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp6_option_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v); + if (r < 0) + return r; + + sd_dhcp6_option_ref(v); + return 0; +} + static void client_notify(sd_dhcp6_client *client, int event) { assert(client); @@ -481,7 +628,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { _cleanup_free_ DHCP6Message *message = NULL; struct in6_addr all_servers = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; + struct sd_dhcp6_option *j; size_t len, optlen = 512; + Iterator i; uint8_t *opt; int r; usec_t elapsed_usec; @@ -542,6 +691,25 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->user_class) { + r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); + if (r < 0) + return r; + } + + if (client->vendor_class) { + r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); + if (r < 0) + return r; + } + + if (!ordered_hashmap_isempty(client->vendor_options)) { + r = dhcp6_option_append_vendor_option(&opt, &optlen, + client->vendor_options); + if (r < 0) + return r; + } + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix); if (r < 0) @@ -588,6 +756,24 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->user_class) { + r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); + if (r < 0) + return r; + } + + if (client->vendor_class) { + r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); + if (r < 0) + return r; + } + + if (!ordered_hashmap_isempty(client->vendor_options)) { + r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options); + if (r < 0) + return r; + } + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL); if (r < 0) @@ -622,6 +808,24 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->user_class) { + r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); + if (r < 0) + return r; + } + + if (client->vendor_class) { + r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); + if (r < 0) + return r; + } + + if (!ordered_hashmap_isempty(client->vendor_options)) { + r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options); + if (r < 0) + return r; + } + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL); if (r < 0) @@ -661,6 +865,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; + ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) { + r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data); + if (r < 0) + return r; + } + r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message, len - optlen); if (r < 0) @@ -888,10 +1098,11 @@ static int client_parse_message( size_t len, sd_dhcp6_lease *lease) { + uint16_t ia_na_status = 0, ia_pd_status = 0; uint32_t lt_t1 = ~0, lt_t2 = ~0; + usec_t irt = IRT_DEFAULT; bool clientid = false; size_t pos = 0; - usec_t irt = IRT_DEFAULT; int r; assert(client); @@ -905,8 +1116,8 @@ static int client_parse_message( DHCP6Option *option = (DHCP6Option *) &message->options[pos]; uint16_t optcode, optlen; be32_t iaid_lease; + int status; uint8_t *optval; - int status; if (len < pos + offsetof(DHCP6Option, data)) return -ENOBUFS; @@ -983,10 +1194,15 @@ static int client_parse_message( break; } - r = dhcp6_option_parse_ia(option, &lease->ia); + r = dhcp6_option_parse_ia(option, &lease->ia, &ia_na_status); if (r < 0 && r != -ENOMSG) return r; + if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) { + pos += offsetof(DHCP6Option, data) + optlen; + continue; + } + r = dhcp6_lease_get_iaid(lease, &iaid_lease); if (r < 0) return r; @@ -1011,10 +1227,15 @@ static int client_parse_message( break; } - r = dhcp6_option_parse_ia(option, &lease->pd); + r = dhcp6_option_parse_ia(option, &lease->pd, &ia_pd_status); if (r < 0 && r != -ENOMSG) return r; + if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) { + pos += offsetof(DHCP6Option, data) + optlen; + continue; + } + r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease); if (r < 0) return r; @@ -1078,6 +1299,11 @@ static int client_parse_message( pos += offsetof(DHCP6Option, data) + optlen; } + if (ia_na_status > 0 && ia_pd_status > 0) { + log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring."); + return -EINVAL; + } + if (!clientid) { log_dhcp6_client(client, "%s has incomplete options", dhcp6_message_type_to_string(message->type)); @@ -1573,6 +1799,11 @@ static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { free(client->req_opts); free(client->fqdn); free(client->mudurl); + + ordered_hashmap_free(client->extra_options); + strv_free(client->user_class); + strv_free(client->vendor_class); + return mfree(client); } diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c index 35caec7a23..b6dc027915 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c @@ -215,7 +215,7 @@ int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { return 0; } -int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs) { +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs) { assert_return(lease, -EINVAL); assert_return(addrs, -EINVAL); @@ -343,7 +343,7 @@ int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) } int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, - struct in6_addr **addrs) { + const struct in6_addr **addrs) { assert_return(lease, -EINVAL); assert_return(addrs, -EINVAL); diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c index a610546c4f..0bfaf265a2 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c @@ -254,12 +254,14 @@ static int ipv4ll_start_internal(sd_ipv4ll *ll, bool reset_generation) { return r; } - return 0; + return 1; } int sd_ipv4ll_start(sd_ipv4ll *ll) { assert_return(ll, -EINVAL); - assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); + + if (sd_ipv4ll_is_running(ll)) + return 0; return ipv4ll_start_internal(ll, true); } diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h index da2aa6c73b..ac3b5b369c 100644 --- a/src/systemd/src/systemd/sd-dhcp-client.h +++ b/src/systemd/src/systemd/sd-dhcp-client.h @@ -48,6 +48,7 @@ enum { SD_DHCP_OPTION_TIME_OFFSET = 2, SD_DHCP_OPTION_ROUTER = 3, SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6, + SD_DHCP_OPTION_LPR_SERVER = 9, SD_DHCP_OPTION_HOST_NAME = 12, SD_DHCP_OPTION_BOOT_FILE_SIZE = 13, SD_DHCP_OPTION_DOMAIN_NAME = 15, @@ -184,6 +185,9 @@ int sd_dhcp_client_get_lease( int sd_dhcp_client_set_service_type( sd_dhcp_client *client, int type); +int sd_dhcp_client_set_fallback_lease_lifetime( + sd_dhcp_client *client, + uint32_t fallback_lease_lifetime); int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v); int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v); @@ -201,6 +205,8 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client); * options when using RFC7844 Anonymity Profiles */ int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize); +int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret); + int sd_dhcp_client_attach_event( sd_dhcp_client *client, sd_event *event, diff --git a/src/systemd/src/systemd/sd-dhcp-lease.h b/src/systemd/src/systemd/sd-dhcp-lease.h index 1ed5bf27a3..17bd491819 100644 --- a/src/systemd/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/src/systemd/sd-dhcp-lease.h @@ -33,6 +33,17 @@ typedef struct sd_dhcp_route sd_dhcp_route; sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease); sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease); +typedef enum sd_dhcp_lease_server_type { + SD_DHCP_LEASE_DNS, + SD_DHCP_LEASE_NTP, + SD_DHCP_LEASE_SIP, + SD_DHCP_LEASE_POP3, + SD_DHCP_LEASE_SMTP, + SD_DHCP_LEASE_LPR, + _SD_DHCP_LEASE_SERVER_TYPE_MAX, + _SD_DHCP_LEASE_SERVER_TYPE_INVALID = -1, +} sd_dhcp_lease_server_type; + int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime); int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1); @@ -42,11 +53,13 @@ int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_servers(sd_dhcp_lease *lease, sd_dhcp_lease_server_type what, const struct in_addr **addr); int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr); -int sd_dhcp_lease_get_pop3_server(sd_dhcp_lease *lease, const struct in_addr **addr); -int sd_dhcp_lease_get_smtp_server(sd_dhcp_lease *lease, const struct in_addr **addr); +int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr); +int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr); +int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu); int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname); int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains); diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h index 091f8287ec..2b0d63a527 100644 --- a/src/systemd/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/src/systemd/sd-dhcp6-client.h @@ -24,6 +24,7 @@ #include <sys/types.h> #include "sd-dhcp6-lease.h" +#include "sd-dhcp6-option.h" #include "sd-event.h" #include "_sd-common.h" @@ -109,6 +110,12 @@ int sd_dhcp6_client_set_duid_llt( 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); @@ -124,6 +131,12 @@ int sd_dhcp6_client_set_request_option( int sd_dhcp6_client_set_request_mud_url( sd_dhcp6_client *client, const char *mudurl); +int sd_dhcp6_client_set_request_user_class( + sd_dhcp6_client *client, + char** user_class); +int sd_dhcp6_client_set_request_vendor_class( + sd_dhcp6_client *client, + char** vendor_class); int sd_dhcp6_client_set_prefix_delegation_hint( sd_dhcp6_client *client, uint8_t prefixlen, @@ -136,12 +149,17 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request); int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request); -int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id); +int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, + uint32_t transaction_id); +int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, + sd_dhcp6_option *v); int sd_dhcp6_client_get_lease( sd_dhcp6_client *client, sd_dhcp6_lease **ret); +int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v); + int sd_dhcp6_client_stop(sd_dhcp6_client *client); int sd_dhcp6_client_start(sd_dhcp6_client *client); int sd_dhcp6_client_is_running(sd_dhcp6_client *client); diff --git a/src/systemd/src/systemd/sd-dhcp6-lease.h b/src/systemd/src/systemd/sd-dhcp6-lease.h index 33a32a6dc5..4301c6db87 100644 --- a/src/systemd/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/src/systemd/sd-dhcp6-lease.h @@ -39,10 +39,9 @@ int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, uint32_t *lifetime_preferred, uint32_t *lifetime_valid); -int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs); +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs); int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains); -int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, - struct in6_addr **addrs); +int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **addrs); int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn); sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease); diff --git a/src/systemd/src/systemd/sd-dhcp6-option.h b/src/systemd/src/systemd/sd-dhcp6-option.h new file mode 100644 index 0000000000..88a4986315 --- /dev/null +++ b/src/systemd/src/systemd/sd-dhcp6-option.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#ifndef foosddhcp6optionhfoo +#define foosddhcp6optionhfoo + +/*** + 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 <http://www.gnu.org/licenses/>. +***/ + +#include <inttypes.h> +#include <sys/types.h> + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_dhcp6_option sd_dhcp6_option; + +int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret); +sd_dhcp6_option *sd_dhcp6_option_ref(sd_dhcp6_option *ra); +sd_dhcp6_option *sd_dhcp6_option_unref(sd_dhcp6_option *ra); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp6_option, sd_dhcp6_option_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/systemd/src/systemd/sd-lldp.h b/src/systemd/src/systemd/sd-lldp.h index bf3afadcec..2dc9f63246 100644 --- a/src/systemd/src/systemd/sd-lldp.h +++ b/src/systemd/src/systemd/sd-lldp.h @@ -96,6 +96,9 @@ enum { #define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 } #define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f } +#define SD_LLDP_OUI_MUD (uint8_t[]) { 0x00, 0x00, 0x5E } +#define SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION 0x01 + /* IEEE 802.1AB-2009 Annex E */ enum { SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1, @@ -169,6 +172,7 @@ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec); int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret); int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret); int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret); int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret); int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret); diff --git a/src/systemd/src/systemd/sd-ndisc.h b/src/systemd/src/systemd/sd-ndisc.h index d1bee343a2..3ddfc8cb6d 100644 --- a/src/systemd/src/systemd/sd-ndisc.h +++ b/src/systemd/src/systemd/sd-ndisc.h @@ -30,7 +30,7 @@ _SD_BEGIN_DECLARATIONS; -/* Neightbor Discovery Options, RFC 4861, Section 4.6 and +/* Neighbor Discovery Options, RFC 4861, Section 4.6 and * https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5 */ enum { SD_NDISC_OPTION_SOURCE_LL_ADDRESS = 1, |