mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-10-15 12:34:55 +00:00
systemd: merge branch systemd into master
This commit is contained in:
commit
ecf32c2c8f
|
@ -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 = \
|
||||
|
|
3
shared/systemd/sd-adapt-shared/blockdev-util.h
Normal file
3
shared/systemd/sd-adapt-shared/blockdev-util.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
/* dummy header */
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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++) = '\'';
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 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 (tty < 0)
|
||||
tty = isatty(fileno(f));
|
||||
if (tty > 0)
|
||||
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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,8 +345,35 @@ 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 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))
|
||||
return 0;
|
||||
|
||||
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)
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
@ -351,23 +381,13 @@ int fchmod_opath(int fd, mode_t m) {
|
|||
int fd_warn_permissions(const char *path, int fd) {
|
||||
struct stat st;
|
||||
|
||||
assert(path);
|
||||
assert(fd >= 0);
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
/* Don't complain if we are reading something that is not a file, for example /dev/null */
|
||||
if (!S_ISREG(st.st_mode))
|
||||
return 0;
|
||||
|
||||
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)
|
||||
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)
|
||||
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;
|
||||
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) {
|
||||
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
|
||||
#define hashmap_free_and_replace(a, b) \
|
||||
({ \
|
||||
hashmap_free(a); \
|
||||
(a) = (b); \
|
||||
(b) = NULL; \
|
||||
0; \
|
||||
})
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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 (nth == 0)
|
||||
return 1;
|
||||
|
||||
if (family == AF_INET) {
|
||||
uint32_t c, n;
|
||||
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -14,3 +14,7 @@
|
|||
#ifndef GRND_RANDOM
|
||||
#define GRND_RANDOM 0x0002
|
||||
#endif
|
||||
|
||||
#ifndef GRND_INSECURE
|
||||
#define GRND_INSECURE 0x0004
|
||||
#endif
|
||||
|
|
|
@ -64,3 +64,8 @@ struct sockaddr_vm {
|
|||
#ifndef IP_TRANSPARENT
|
||||
#define IP_TRANSPARENT 19
|
||||
#endif
|
||||
|
||||
/* linux/sockios.h */
|
||||
#ifndef SIOCGSKNS
|
||||
#define SIOCGSKNS 0x894C
|
||||
#endif
|
||||
|
|
|
@ -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] == '-')
|
||||
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;
|
||||
|
||||
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)
|
||||
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)
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
static int update_argv(const char name[], size_t l) {
|
||||
static int can_do = -1;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
int strcasecmp_ptr(const char *a, const char *b) {
|
||||
/* Like strcasecmp(), but tries to make sense of NULL pointers */
|
||||
|
||||
if (a && !b)
|
||||
return 1;
|
||||
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
uint16_t iatype, optlen;
|
||||
size_t i, len;
|
||||
int r = 0, status;
|
||||
uint16_t opt;
|
||||
size_t iaaddr_offset;
|
||||
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 iaaddr_offset;
|
||||
int r = 0, status;
|
||||
size_t i, len;
|
||||
uint16_t opt;
|
||||
|
||||
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);
|
||||
|
||||
return -EINVAL;
|
||||
if (status > 0) {
|
||||
if (ret_status_code)
|
||||
*ret_status_code = status;
|
||||
|
||||
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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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++) {
|
||||
bool _space = false;
|
||||
if (!with_leading_space)
|
||||
with_leading_space = &_space;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
char buffer[INET6_ADDRSTRLEN];
|
||||
|
||||
fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f);
|
||||
|
||||
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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,11 +922,82 @@ static int dhcp_client_send_raw(
|
|||
packet, len);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (client->hostname) {
|
||||
/* According to RFC 4702 "clients that send the Client FQDN option in
|
||||
their messages MUST NOT also send the Host Name option". Just send
|
||||
one of the two depending on the hostname type.
|
||||
*/
|
||||
if (dns_name_is_single_label(client->hostname)) {
|
||||
/* 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(&packet->dhcp, optlen, optoffset, 0,
|
||||
SD_DHCP_OPTION_HOST_NAME,
|
||||
strlen(client->hostname), client->hostname);
|
||||
} else
|
||||
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(&packet->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(&packet->dhcp, optlen, optoffset, 0,
|
||||
SD_DHCP_OPTION_MUD_URL,
|
||||
strlen(client->mudurl),
|
||||
client->mudurl);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (client->user_class) {
|
||||
r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
|
||||
SD_DHCP_OPTION_USER_CLASS,
|
||||
strv_length(client->user_class),
|
||||
client->user_class);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) {
|
||||
r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
|
||||
j->option, j->length, j->data);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!ordered_hashmap_isempty(client->vendor_options)) {
|
||||
r = dhcp_option_append(
|
||||
&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;
|
||||
sd_dhcp_option *j;
|
||||
Iterator i;
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
|
@ -886,67 +1024,9 @@ static int client_send_discover(sd_dhcp_client *client) {
|
|||
return r;
|
||||
}
|
||||
|
||||
if (client->hostname) {
|
||||
/* According to RFC 4702 "clients that send the Client FQDN option in
|
||||
their messages MUST NOT also send the Host Name option". Just send
|
||||
one of the two depending on the hostname type.
|
||||
*/
|
||||
if (dns_name_is_single_label(client->hostname)) {
|
||||
/* 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,
|
||||
SD_DHCP_OPTION_HOST_NAME,
|
||||
strlen(client->hostname), client->hostname);
|
||||
} else
|
||||
r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset,
|
||||
client->hostname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (client->vendor_class_identifier) {
|
||||
r = dhcp_option_append(&discover->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(&discover->dhcp, optlen, &optoffset, 0,
|
||||
SD_DHCP_OPTION_MUD_URL,
|
||||
strlen(client->mudurl),
|
||||
client->mudurl);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (client->user_class) {
|
||||
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
|
||||
SD_DHCP_OPTION_USER_CLASS,
|
||||
strv_length(client->user_class),
|
||||
client->user_class);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) {
|
||||
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
|
||||
j->option, j->length, j->data);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!ordered_hashmap_isempty(client->vendor_options)) {
|
||||
r = dhcp_option_append(
|
||||
&discover->dhcp, optlen, &optoffset, 0,
|
||||
SD_DHCP_OPTION_VENDOR_SPECIFIC,
|
||||
ordered_hashmap_size(client->vendor_options), client->vendor_options);
|
||||
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);
|
||||
|
@ -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,
|
||||
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");
|
||||
} else if ((size_t)len < sizeof(DHCPPacket))
|
||||
|
||||
if ((size_t) len < sizeof(DHCPPacket))
|
||||
return 0;
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
|
|
|
@ -98,59 +98,40 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
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->servers[what].size <= 0)
|
||||
return -ENODATA;
|
||||
|
||||
*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) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(addr, -EINVAL);
|
||||
|
||||
if (lease->dns_size <= 0)
|
||||
return -ENODATA;
|
||||
|
||||
*addr = lease->dns;
|
||||
return (int) lease->dns_size;
|
||||
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) {
|
||||
if (len <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* 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 */
|
||||
|
||||
if (option[0] != 1) { /* We only support IP address encoding for now */
|
||||
*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;
|
||||
|
||||
addresses = newdup(struct in_addr, option + 1, n_addresses);
|
||||
if (!addresses)
|
||||
return -ENOMEM;
|
||||
|
||||
free(*ret);
|
||||
*ret = addresses;
|
||||
*n_ret = n_addresses;
|
||||
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_server) {
|
||||
r = deserialize_in_addrs(&lease->pop3_server, pop3_server);
|
||||
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_server);
|
||||
log_debug_errno(r, "Failed to deserialize POP3 server %s, ignoring: %m", pop3);
|
||||
else
|
||||
lease->pop3_server_size = r;
|
||||
lease->servers[SD_DHCP_LEASE_POP3].size = r;
|
||||
}
|
||||
|
||||
if (smtp_server) {
|
||||
r = deserialize_in_addrs(&lease->smtp_server, smtp_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 SMTP server %s, ignoring: %m", smtp_server);
|
||||
log_debug_errno(r, "Failed to deserialize SMTP server %s, ignoring: %m", smtp);
|
||||
else
|
||||
lease->smtp_server_size = r;
|
||||
lease->servers[SD_DHCP_LEASE_SMTP].size = r;
|
||||
}
|
||||
|
||||
if (lpr) {
|
||||
r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_LPR].addr, lpr);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to deserialize LPR server %s, ignoring: %m", lpr);
|
||||
else
|
||||
lease->servers[SD_DHCP_LEASE_LPR].size = r;
|
||||
}
|
||||
|
||||
if (mtu) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
37
src/systemd/src/systemd/sd-dhcp6-option.h
Normal file
37
src/systemd/src/systemd/sd-dhcp6-option.h
Normal file
|
@ -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
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue