systemd: update code from upstream (2021-10-06)

This is a direct dump from systemd git.

======

SYSTEMD_DIR=../systemd
COMMIT=e5f093bf99ed742b74b81ac97e0e5e01cde02d95

(
  cd "$SYSTEMD_DIR"
  git checkout "$COMMIT"
  git reset --hard
  git clean -fdx
)

git ls-files -z :/src/libnm-systemd-core/src/ \
                :/src/libnm-systemd-shared/src/ \
                :/src/libnm-std-aux/unaligned.h | \
  xargs -0 rm -f

nm_copy_sd_shared() {
    mkdir -p "./src/libnm-systemd-shared/$(dirname "$1")"
    cp "$SYSTEMD_DIR/$1" "./src/libnm-systemd-shared/$1"
}

nm_copy_sd_core() {
    mkdir -p "./src/libnm-systemd-core/$(dirname "$1")"
    cp "$SYSTEMD_DIR/$1" "./src/libnm-systemd-core/$1"
}

nm_copy_sd_stdaux() {
    mkdir -p "./src/libnm-std-aux/"
    cp "$SYSTEMD_DIR/$1" "./src/libnm-std-aux/${1##*/}"
}

nm_copy_sd_core "src/libsystemd-network/arp-util.c"
nm_copy_sd_core "src/libsystemd-network/arp-util.h"
nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.c"
nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.h"
nm_copy_sd_core "src/libsystemd-network/dhcp-internal.h"
nm_copy_sd_core "src/libsystemd-network/dhcp-lease-internal.h"
nm_copy_sd_core "src/libsystemd-network/dhcp-network.c"
nm_copy_sd_core "src/libsystemd-network/dhcp-option.c"
nm_copy_sd_core "src/libsystemd-network/dhcp-packet.c"
nm_copy_sd_core "src/libsystemd-network/dhcp-protocol.h"
nm_copy_sd_core "src/libsystemd-network/dhcp6-internal.h"
nm_copy_sd_core "src/libsystemd-network/dhcp6-lease-internal.h"
nm_copy_sd_core "src/libsystemd-network/dhcp6-network.c"
nm_copy_sd_core "src/libsystemd-network/dhcp6-option.c"
nm_copy_sd_core "src/libsystemd-network/dhcp6-protocol.h"
nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.c"
nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.h"
nm_copy_sd_core "src/libsystemd-network/lldp-network.c"
nm_copy_sd_core "src/libsystemd-network/lldp-network.h"
nm_copy_sd_core "src/libsystemd-network/lldp-rx-internal.h"
nm_copy_sd_core "src/libsystemd-network/network-common.c"
nm_copy_sd_core "src/libsystemd-network/network-common.h"
nm_copy_sd_core "src/libsystemd-network/network-internal.c"
nm_copy_sd_core "src/libsystemd-network/network-internal.h"
nm_copy_sd_core "src/libsystemd-network/sd-dhcp-client.c"
nm_copy_sd_core "src/libsystemd-network/sd-dhcp-lease.c"
nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-client.c"
nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-lease.c"
nm_copy_sd_core "src/libsystemd-network/sd-ipv4acd.c"
nm_copy_sd_core "src/libsystemd-network/sd-ipv4ll.c"
nm_copy_sd_core "src/libsystemd-network/sd-lldp-rx.c"
nm_copy_sd_core "src/libsystemd/sd-event/event-source.h"
nm_copy_sd_core "src/libsystemd/sd-event/event-util.c"
nm_copy_sd_core "src/libsystemd/sd-event/event-util.h"
nm_copy_sd_core "src/libsystemd/sd-event/sd-event.c"
nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.c"
nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.h"
nm_copy_sd_core "src/libsystemd/sd-id128/sd-id128.c"
nm_copy_sd_core "src/systemd/_sd-common.h"
nm_copy_sd_core "src/systemd/sd-dhcp-client.h"
nm_copy_sd_core "src/systemd/sd-dhcp-lease.h"
nm_copy_sd_core "src/systemd/sd-dhcp-option.h"
nm_copy_sd_core "src/systemd/sd-dhcp6-client.h"
nm_copy_sd_core "src/systemd/sd-dhcp6-lease.h"
nm_copy_sd_core "src/systemd/sd-dhcp6-option.h"
nm_copy_sd_core "src/systemd/sd-event.h"
nm_copy_sd_core "src/systemd/sd-id128.h"
nm_copy_sd_core "src/systemd/sd-ipv4acd.h"
nm_copy_sd_core "src/systemd/sd-ipv4ll.h"
nm_copy_sd_core "src/systemd/sd-lldp-rx.h"
nm_copy_sd_core "src/systemd/sd-lldp.h"
nm_copy_sd_core "src/systemd/sd-ndisc.h"
nm_copy_sd_shared "src/basic/alloc-util.c"
nm_copy_sd_shared "src/basic/alloc-util.h"
nm_copy_sd_shared "src/basic/async.h"
nm_copy_sd_shared "src/basic/cgroup-util.h"
nm_copy_sd_shared "src/basic/dns-def.h"
nm_copy_sd_shared "src/basic/env-file.c"
nm_copy_sd_shared "src/basic/env-file.h"
nm_copy_sd_shared "src/basic/env-util.c"
nm_copy_sd_shared "src/basic/env-util.h"
nm_copy_sd_shared "src/basic/errno-util.h"
nm_copy_sd_shared "src/basic/escape.c"
nm_copy_sd_shared "src/basic/escape.h"
nm_copy_sd_shared "src/basic/ether-addr-util.c"
nm_copy_sd_shared "src/basic/ether-addr-util.h"
nm_copy_sd_shared "src/basic/extract-word.c"
nm_copy_sd_shared "src/basic/extract-word.h"
nm_copy_sd_shared "src/basic/fd-util.c"
nm_copy_sd_shared "src/basic/fd-util.h"
nm_copy_sd_shared "src/basic/fileio.c"
nm_copy_sd_shared "src/basic/fileio.h"
nm_copy_sd_shared "src/basic/format-util.c"
nm_copy_sd_shared "src/basic/format-util.h"
nm_copy_sd_shared "src/basic/fs-util.c"
nm_copy_sd_shared "src/basic/fs-util.h"
nm_copy_sd_shared "src/basic/hash-funcs.c"
nm_copy_sd_shared "src/basic/hash-funcs.h"
nm_copy_sd_shared "src/basic/hashmap.c"
nm_copy_sd_shared "src/basic/hashmap.h"
nm_copy_sd_shared "src/basic/hexdecoct.c"
nm_copy_sd_shared "src/basic/hexdecoct.h"
nm_copy_sd_shared "src/basic/hostname-util.c"
nm_copy_sd_shared "src/basic/hostname-util.h"
nm_copy_sd_shared "src/basic/in-addr-util.c"
nm_copy_sd_shared "src/basic/in-addr-util.h"
nm_copy_sd_shared "src/basic/inotify-util.c"
nm_copy_sd_shared "src/basic/inotify-util.h"
nm_copy_sd_shared "src/basic/io-util.c"
nm_copy_sd_shared "src/basic/io-util.h"
nm_copy_sd_shared "src/basic/list.h"
nm_copy_sd_shared "src/basic/log.h"
nm_copy_sd_shared "src/basic/macro.h"
nm_copy_sd_shared "src/basic/memory-util.c"
nm_copy_sd_shared "src/basic/memory-util.h"
nm_copy_sd_shared "src/basic/mempool.c"
nm_copy_sd_shared "src/basic/mempool.h"
nm_copy_sd_shared "src/basic/missing_fcntl.h"
nm_copy_sd_shared "src/basic/missing_random.h"
nm_copy_sd_shared "src/basic/missing_socket.h"
nm_copy_sd_shared "src/basic/missing_stat.h"
nm_copy_sd_shared "src/basic/missing_syscall.h"
nm_copy_sd_shared "src/basic/missing_type.h"
nm_copy_sd_shared "src/basic/ordered-set.c"
nm_copy_sd_shared "src/basic/ordered-set.h"
nm_copy_sd_shared "src/basic/parse-util.c"
nm_copy_sd_shared "src/basic/parse-util.h"
nm_copy_sd_shared "src/basic/path-util.c"
nm_copy_sd_shared "src/basic/path-util.h"
nm_copy_sd_shared "src/basic/prioq.c"
nm_copy_sd_shared "src/basic/prioq.h"
nm_copy_sd_shared "src/basic/process-util.c"
nm_copy_sd_shared "src/basic/process-util.h"
nm_copy_sd_shared "src/basic/random-util.c"
nm_copy_sd_shared "src/basic/random-util.h"
nm_copy_sd_shared "src/basic/ratelimit.c"
nm_copy_sd_shared "src/basic/ratelimit.h"
nm_copy_sd_shared "src/basic/set.h"
nm_copy_sd_shared "src/basic/signal-util.c"
nm_copy_sd_shared "src/basic/signal-util.h"
nm_copy_sd_shared "src/basic/siphash24.h"
nm_copy_sd_shared "src/basic/socket-util.c"
nm_copy_sd_shared "src/basic/socket-util.h"
nm_copy_sd_shared "src/basic/sort-util.h"
nm_copy_sd_shared "src/basic/sparse-endian.h"
nm_copy_sd_shared "src/basic/stat-util.c"
nm_copy_sd_shared "src/basic/stat-util.h"
nm_copy_sd_shared "src/basic/stdio-util.h"
nm_copy_sd_shared "src/basic/string-table.c"
nm_copy_sd_shared "src/basic/string-table.h"
nm_copy_sd_shared "src/basic/string-util.c"
nm_copy_sd_shared "src/basic/string-util.h"
nm_copy_sd_shared "src/basic/strv.c"
nm_copy_sd_shared "src/basic/strv.h"
nm_copy_sd_shared "src/basic/strxcpyx.c"
nm_copy_sd_shared "src/basic/strxcpyx.h"
nm_copy_sd_shared "src/basic/time-util.c"
nm_copy_sd_shared "src/basic/time-util.h"
nm_copy_sd_shared "src/basic/tmpfile-util.c"
nm_copy_sd_shared "src/basic/tmpfile-util.h"
nm_copy_sd_shared "src/basic/umask-util.h"
nm_copy_sd_shared "src/basic/user-util.h"
nm_copy_sd_shared "src/basic/utf8.c"
nm_copy_sd_shared "src/basic/utf8.h"
nm_copy_sd_shared "src/basic/util.c"
nm_copy_sd_shared "src/basic/util.h"
nm_copy_sd_shared "src/fundamental/macro-fundamental.h"
nm_copy_sd_shared "src/fundamental/string-util-fundamental.c"
nm_copy_sd_shared "src/fundamental/string-util-fundamental.h"
nm_copy_sd_shared "src/fundamental/type.h"
nm_copy_sd_shared "src/shared/dns-domain.c"
nm_copy_sd_shared "src/shared/dns-domain.h"
nm_copy_sd_shared "src/shared/log-link.h"
nm_copy_sd_shared "src/shared/web-util.c"
nm_copy_sd_shared "src/shared/web-util.h"
nm_copy_sd_stdaux "src/basic/unaligned.h"
This commit is contained in:
Thomas Haller 2021-07-29 12:46:06 +02:00
parent 08ee0a83a1
commit 51f93e00a2
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
80 changed files with 2208 additions and 2546 deletions

View file

@ -12,7 +12,7 @@
#include "sd-dhcp-client.h"
#include "dhcp-protocol.h"
#include "log-link.h"
#include "network-common.h"
#include "socket-util.h"
typedef struct sd_dhcp_option {
@ -75,10 +75,10 @@ void dhcp_client_set_test_mode(sd_dhcp_client *client, bool test_mode);
#define log_dhcp_client_errno(client, error, fmt, ...) \
log_interface_prefix_full_errno( \
"DHCPv4 client: ", \
sd_dhcp_client_get_ifname(client), \
sd_dhcp_client, client, \
error, fmt, ##__VA_ARGS__)
#define log_dhcp_client(client, fmt, ...) \
log_interface_prefix_full_errno_zerook( \
"DHCPv4 client: ", \
sd_dhcp_client_get_ifname(client), \
sd_dhcp_client, client, \
0, fmt, ##__VA_ARGS__)

View file

@ -13,8 +13,8 @@
#include "hashmap.h"
#include "list.h"
#include "log-link.h"
#include "macro.h"
#include "network-common.h"
#include "sparse-endian.h"
typedef struct sd_dhcp6_option {
@ -101,15 +101,29 @@ 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 * const *user_class);
int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *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(sd_dhcp6_client *client, 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);
int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen,
char ***str_arr);
int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str);
int dhcp6_option_parse(
const uint8_t *buf,
size_t buflen,
size_t *offset,
uint16_t *ret_option_code,
size_t *ret_option_data_len,
const uint8_t **ret_option_data);
int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message);
int dhcp6_option_parse_ia(
sd_dhcp6_client *client,
be32_t iaid,
uint16_t option_code,
size_t option_data_len,
const uint8_t *option_data,
DHCP6IA *ret);
int dhcp6_option_parse_addresses(
const uint8_t *optval,
size_t optlen,
struct in6_addr **addrs,
size_t *count);
int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret);
int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret);
int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
@ -125,10 +139,10 @@ void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode);
#define log_dhcp6_client_errno(client, error, fmt, ...) \
log_interface_prefix_full_errno( \
"DHCPv6 client: ", \
sd_dhcp6_client_get_ifname(client), \
sd_dhcp6_client, client, \
error, fmt, ##__VA_ARGS__)
#define log_dhcp6_client(client, fmt, ...) \
log_interface_prefix_full_errno_zerook( \
"DHCPv6 client: ", \
sd_dhcp6_client_get_ifname(client), \
sd_dhcp6_client, client, \
0, fmt, ##__VA_ARGS__)

View file

@ -28,11 +28,11 @@ struct sd_dhcp6_lease {
struct in6_addr *dns;
size_t dns_count;
char **domains;
size_t domains_count;
struct in6_addr *ntp;
size_t ntp_count;
char **ntp_fqdn;
size_t ntp_fqdn_count;
struct in6_addr *sntp;
size_t sntp_count;
char *fqdn;
};
@ -50,12 +50,10 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit);
int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
size_t optlen);
int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval,
size_t optlen) ;
int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) ;
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_new(sd_dhcp6_lease **ret);

View file

@ -14,29 +14,12 @@
#include "dhcp6-lease-internal.h"
#include "dhcp6-protocol.h"
#include "dns-domain.h"
#include "escape.h"
#include "memory-util.h"
#include "sparse-endian.h"
#include "strv.h"
#include "unaligned.h"
typedef struct DHCP6StatusOption {
struct DHCP6Option option;
be16_t status;
char msg[];
} _packed_ DHCP6StatusOption;
typedef struct DHCP6AddressOption {
struct DHCP6Option option;
struct iaaddr iaaddr;
uint8_t options[];
} _packed_ DHCP6AddressOption;
typedef struct DHCP6PDPrefixOption {
struct DHCP6Option option;
struct iapdprefix iapdprefix;
uint8_t options[];
} _packed_ DHCP6PDPrefixOption;
#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na))
#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
@ -370,337 +353,410 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const
return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p);
}
static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
DHCP6Option *option = (DHCP6Option*) *buf;
uint16_t len;
int dhcp6_option_parse(
const uint8_t *buf,
size_t buflen,
size_t *offset,
uint16_t *ret_option_code,
size_t *ret_option_data_len,
const uint8_t **ret_option_data) {
assert_return(buf, -EINVAL);
assert_return(optcode, -EINVAL);
assert_return(optlen, -EINVAL);
const DHCP6Option *option;
size_t len;
if (*buflen < offsetof(DHCP6Option, data))
return -ENOMSG;
assert(buf);
assert(offset);
assert(ret_option_code);
assert(ret_option_data_len);
assert(ret_option_data);
if (buflen < offsetof(DHCP6Option, data))
return -EBADMSG;
if (*offset >= buflen - offsetof(DHCP6Option, data))
return -EBADMSG;
option = (const DHCP6Option*) (buf + *offset);
len = be16toh(option->len);
if (len > *buflen)
if (len > buflen - offsetof(DHCP6Option, data) - *offset)
return -EBADMSG;
*offset += offsetof(DHCP6Option, data) + len;
*ret_option_code = be16toh(option->code);
*ret_option_data_len = len;
*ret_option_data = option->data;
return 0;
}
int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message) {
assert(data);
if (data_len < sizeof(uint16_t))
return -EBADMSG;
if (ret_status_message) {
char *msg;
/* The status message MUST NOT be null-terminated. See section 21.13 of RFC8415.
* Let's escape unsafe characters for safety. */
msg = cescape_length((const char*) (data + sizeof(uint16_t)), data_len - sizeof(uint16_t));
if (!msg)
return -ENOMEM;
*ret_status_message = msg;
}
return unaligned_read_be16(data);
}
static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) {
int r;
assert(buf);
for(size_t offset = 0; offset < buflen;) {
const uint8_t *data;
size_t data_len;
uint16_t code;
r = dhcp6_option_parse(buf, buflen, &offset, &code, &data_len, &data);
if (r < 0)
return r;
switch(code) {
case SD_DHCP6_OPTION_STATUS_CODE: {
_cleanup_free_ char *msg = NULL;
r = dhcp6_option_parse_status(data, data_len, &msg);
if (r == -ENOMEM)
return r;
if (r < 0)
/* Let's log but ignore the invalid status option. */
log_dhcp6_client_errno(client, r,
"Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m");
else if (r > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA address or PD prefix option with non-zero status: %s%s%s",
strempty(msg), isempty(msg) ? "" : ": ",
dhcp6_message_status_to_string(r));
break;
}
default:
log_dhcp6_client(client, "Received an unknown sub option %u in IA address or PD prefix, ignoring.", code);
}
}
return 0;
}
static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) {
uint32_t lt_valid, lt_pref;
DHCP6Address *a;
int r;
assert(data);
assert(ret);
if (len < sizeof(struct iaaddr))
return -EBADMSG;
lt_valid = be32toh(((const struct iaaddr*) data)->lifetime_valid);
lt_pref = be32toh(((const struct iaaddr*) data)->lifetime_preferred);
if (lt_valid == 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA address with zero valid lifetime, ignoring.");
if (lt_pref > lt_valid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA address with preferred lifetime %"PRIu32
" larger than valid lifetime %"PRIu32", ignoring.",
lt_pref, lt_valid);
if (len > sizeof(struct iaaddr)) {
r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iaaddr), len - sizeof(struct iaaddr));
if (r < 0)
return r;
}
a = new(DHCP6Address, 1);
if (!a)
return -ENOMEM;
LIST_INIT(addresses, a);
memcpy(&a->iaaddr, data, sizeof(struct iaaddr));
*ret = a;
return 0;
}
static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) {
uint32_t lt_valid, lt_pref;
DHCP6Address *a;
int r;
if (len < sizeof(struct iapdprefix))
return -ENOMSG;
*optcode = be16toh(option->code);
*optlen = len;
lt_valid = be32toh(((const struct iapdprefix*) data)->lifetime_valid);
lt_pref = be32toh(((const struct iapdprefix*) data)->lifetime_preferred);
*buf += 4;
*buflen -= 4;
return 0;
}
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
size_t *optlen, uint8_t **optvalue) {
int r;
assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL);
r = option_parse_hdr(buf, buflen, optcode, optlen);
if (r < 0)
return r;
if (*optlen > *buflen)
return -ENOBUFS;
*optvalue = *buf;
*buflen -= *optlen;
*buf += *optlen;
return 0;
}
int dhcp6_option_parse_status(DHCP6Option *option, size_t len) {
DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option;
if (len < sizeof(DHCP6StatusOption) ||
be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(DHCP6StatusOption))
return -ENOBUFS;
return be16toh(statusopt->status);
}
static int dhcp6_option_parse_address(sd_dhcp6_client *client, DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) {
DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option;
DHCP6Address *addr;
uint32_t lt_valid, lt_pref;
int r;
if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*addr_option))
return -ENOBUFS;
lt_valid = be32toh(addr_option->iaaddr.lifetime_valid);
lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred);
if (lt_valid == 0 || lt_pref > lt_valid)
if (lt_valid == 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Valid lifetime of an IA address is zero or "
"preferred lifetime %"PRIu32" > valid lifetime %"PRIu32,
"Received a PD prefix with zero valid lifetime, ignoring.");
if (lt_pref > lt_valid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received a PD prefix with preferred lifetime %"PRIu32
" larger than valid lifetime %"PRIu32", ignoring.",
lt_pref, lt_valid);
if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) {
r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option));
if (len > sizeof(struct iapdprefix)) {
r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iapdprefix), len - sizeof(struct iapdprefix));
if (r < 0)
return r;
if (r > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Non-zero status code '%s' for address is received",
dhcp6_message_status_to_string(r));
}
addr = new0(DHCP6Address, 1);
if (!addr)
a = new(DHCP6Address, 1);
if (!a)
return -ENOMEM;
LIST_INIT(addresses, addr);
memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr));
LIST_PREPEND(addresses, ia->addresses, addr);
*ret_lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
LIST_INIT(addresses, a);
memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
*ret = a;
return 0;
}
static int dhcp6_option_parse_pdprefix(sd_dhcp6_client *client, DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) {
DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option;
DHCP6Address *prefix;
uint32_t lt_valid, lt_pref;
int dhcp6_option_parse_ia(
sd_dhcp6_client *client,
be32_t iaid,
uint16_t option_code,
size_t option_data_len,
const uint8_t *option_data,
DHCP6IA *ret) {
_cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
uint32_t lt_t1, lt_t2, lt_min = UINT32_MAX;
be32_t received_iaid;
size_t offset;
int r;
if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*pdprefix_option))
return -ENOBUFS;
assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD));
assert(option_data);
assert(ret);
lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid);
lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred);
/* This will return the following:
* -ENOMEM: memory allocation error,
* -ENOANO: unmatching IAID,
* -EINVAL: non-zero status code, or invalid lifetime,
* -EBADMSG: invalid message format,
* -ENODATA: no valid address or PD prefix,
* 0: success. */
if (lt_valid == 0 || lt_pref > lt_valid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Valid lifetieme of a PD prefix is zero or "
"preferred lifetime %"PRIu32" > valid lifetime %"PRIu32,
lt_pref, lt_valid);
if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*pdprefix_option)) {
r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*pdprefix_option));
if (r < 0)
return r;
if (r > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Non-zero status code '%s' for PD prefix is received",
dhcp6_message_status_to_string(r));
}
prefix = new0(DHCP6Address, 1);
if (!prefix)
return -ENOMEM;
LIST_INIT(addresses, prefix);
memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix));
LIST_PREPEND(addresses, ia->addresses, prefix);
*ret_lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
return 0;
}
int dhcp6_option_parse_ia(sd_dhcp6_client *client, 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);
iatype = be16toh(iaoption->code);
len = be16toh(iaoption->len);
switch (iatype) {
switch (option_code) {
case SD_DHCP6_OPTION_IA_NA:
if (len < DHCP6_OPTION_IA_NA_LEN)
return -ENOBUFS;
if (option_data_len < DHCP6_OPTION_IA_NA_LEN)
return -EBADMSG;
iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na));
lt_t1 = be32toh(ia->ia_na.lifetime_t1);
lt_t2 = be32toh(ia->ia_na.lifetime_t2);
if (lt_t1 > lt_t2)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"IA NA T1 %"PRIu32"sec > T2 %"PRIu32"sec",
lt_t1, lt_t2);
offset = DHCP6_OPTION_IA_NA_LEN;
received_iaid = ((const struct ia_na*) option_data)->id;
lt_t1 = be32toh(((const struct ia_na*) option_data)->lifetime_t1);
lt_t2 = be32toh(((const struct ia_na*) option_data)->lifetime_t2);
break;
case SD_DHCP6_OPTION_IA_PD:
if (len < sizeof(ia->ia_pd))
return -ENOBUFS;
if (option_data_len < DHCP6_OPTION_IA_PD_LEN)
return -EBADMSG;
iaaddr_offset = sizeof(ia->ia_pd);
memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd));
lt_t1 = be32toh(ia->ia_pd.lifetime_t1);
lt_t2 = be32toh(ia->ia_pd.lifetime_t2);
if (lt_t1 > lt_t2)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"IA PD T1 %"PRIu32"sec > T2 %"PRIu32"sec",
lt_t1, lt_t2);
offset = DHCP6_OPTION_IA_PD_LEN;
received_iaid = ((const struct ia_pd*) option_data)->id;
lt_t1 = be32toh(((const struct ia_pd*) option_data)->lifetime_t1);
lt_t2 = be32toh(((const struct ia_pd*) option_data)->lifetime_t2);
break;
case SD_DHCP6_OPTION_IA_TA:
if (len < DHCP6_OPTION_IA_TA_LEN)
return -ENOBUFS;
if (option_data_len < DHCP6_OPTION_IA_TA_LEN)
return -ENOMSG;
iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta));
offset = DHCP6_OPTION_IA_TA_LEN;
received_iaid = ((const struct ia_ta*) option_data)->id;
lt_t1 = lt_t2 = 0; /* No lifetime for IA_TA. */
break;
default:
return -ENOMSG;
assert_not_reached();
}
ia->type = iatype;
i = iaaddr_offset;
/* According to RFC8415, IAs which do not match the client's IAID should be ignored,
* but not necessary to ignore or refuse the whole message. */
if (received_iaid != iaid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
"Received an IA option with a different IAID "
"from the one chosen by the client, ignoring.");
while (i < len) {
DHCP6Option *option = (DHCP6Option *)&iaoption->data[i];
if (lt_t1 > lt_t2)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.",
lt_t1, lt_t2);
if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len))
return -ENOBUFS;
for (; offset < option_data_len;) {
const uint8_t *subdata;
size_t subdata_len;
uint16_t subopt;
opt = be16toh(option->code);
optlen = be16toh(option->len);
r = dhcp6_option_parse(option_data, option_data_len, &offset, &subopt, &subdata_len, &subdata);
if (r < 0)
return r;
switch (opt) {
case SD_DHCP6_OPTION_IAADDR:
switch (subopt) {
case SD_DHCP6_OPTION_IAADDR: {
DHCP6Address *a;
if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA))
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"IA Address option not in IA NA or TA option");
r = dhcp6_option_parse_address(client, option, ia, &lt_valid);
if (r < 0 && r != -EINVAL)
return r;
if (r >= 0 && lt_valid < lt_min)
lt_min = lt_valid;
break;
case SD_DHCP6_OPTION_IA_PD_PREFIX:
if (ia->type != SD_DHCP6_OPTION_IA_PD)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"IA PD Prefix option not in IA PD option");
r = dhcp6_option_parse_pdprefix(client, option, ia, &lt_valid);
if (r < 0 && r != -EINVAL)
return r;
if (r >= 0 && lt_valid < lt_min)
lt_min = lt_valid;
break;
case SD_DHCP6_OPTION_STATUS_CODE:
status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data));
if (status < 0)
return status;
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;
if (!IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) {
log_dhcp6_client(client, "Received an IA_PD option with an IA address, ignoring.");
continue;
}
break;
r = dhcp6_option_parse_ia_address(client, subdata, subdata_len, &a);
if (r == -ENOMEM)
return r;
if (r < 0)
/* Ignore the sub-option on non-critical errors. */
continue;
lt_min = MIN(lt_min, a->iaaddr.lifetime_valid);
LIST_PREPEND(addresses, ia.addresses, a);
break;
}
case SD_DHCP6_OPTION_IA_PD_PREFIX: {
DHCP6Address *a;
if (option_code != SD_DHCP6_OPTION_IA_PD) {
log_dhcp6_client(client, "Received an IA_NA or IA_TA option with an PD prefix, ignoring");
continue;
}
r = dhcp6_option_parse_ia_pdprefix(client, subdata, subdata_len, &a);
if (r == -ENOMEM)
return r;
if (r < 0)
/* Ignore the sub-option on non-critical errors. */
continue;
lt_min = MIN(lt_min, a->iapdprefix.lifetime_valid);
LIST_PREPEND(addresses, ia.addresses, a);
break;
}
case SD_DHCP6_OPTION_STATUS_CODE: {
_cleanup_free_ char *msg = NULL;
r = dhcp6_option_parse_status(subdata, subdata_len, &msg);
if (r == -ENOMEM)
return r;
if (r < 0)
log_dhcp6_client_errno(client, r,
"Received an IA option with an invalid status sub option, ignoring: %m");
else if (r > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA option with non-zero status: %s%s%s",
strempty(msg), isempty(msg) ? "" : ": ",
dhcp6_message_status_to_string(r));
break;
}
default:
log_dhcp6_client(client, "Unknown IA option %d", opt);
break;
log_dhcp6_client(client, "Received an IA option with an unknown sub-option %u, ignoring", subopt);
}
i += sizeof(*option) + optlen;
}
switch(iatype) {
if (!ia.addresses)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA),
"Received an IA option without valid IA addresses or PD prefixes, ignoring.");
if (IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_PD) &&
lt_t1 == 0 && lt_t2 == 0 && lt_min != UINT32_MAX) {
lt_t1 = lt_min / 2;
lt_t2 = lt_min / 10 * 8;
log_dhcp6_client(client, "Received an IA option with both T1 and T2 equal to zero. "
"Adjusting them based on the minimum valid lifetime of IA addresses or PD prefixes: "
"T1=%"PRIu32"sec, T2=%"PRIu32"sec", lt_t1, lt_t2);
}
switch(option_code) {
case SD_DHCP6_OPTION_IA_NA:
if (ia->ia_na.lifetime_t1 == 0 && ia->ia_na.lifetime_t2 == 0 && lt_min != UINT32_MAX) {
lt_t1 = lt_min / 2;
lt_t2 = lt_min / 10 * 8;
ia->ia_na.lifetime_t1 = htobe32(lt_t1);
ia->ia_na.lifetime_t2 = htobe32(lt_t2);
log_dhcp6_client(client, "Computed IA NA T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero",
lt_t1, lt_t2);
}
*ret = (DHCP6IA) {
.type = option_code,
.ia_na.id = iaid,
.ia_na.lifetime_t1 = htobe32(lt_t1),
.ia_na.lifetime_t2 = htobe32(lt_t2),
.addresses = TAKE_PTR(ia.addresses),
};
break;
case SD_DHCP6_OPTION_IA_TA:
*ret = (DHCP6IA) {
.type = option_code,
.ia_ta.id = iaid,
.addresses = TAKE_PTR(ia.addresses),
};
break;
case SD_DHCP6_OPTION_IA_PD:
if (ia->ia_pd.lifetime_t1 == 0 && ia->ia_pd.lifetime_t2 == 0 && lt_min != UINT32_MAX) {
lt_t1 = lt_min / 2;
lt_t2 = lt_min / 10 * 8;
ia->ia_pd.lifetime_t1 = htobe32(lt_t1);
ia->ia_pd.lifetime_t2 = htobe32(lt_t2);
log_dhcp6_client(client, "Computed IA PD T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero",
lt_t1, lt_t2);
}
*ret = (DHCP6IA) {
.type = option_code,
.ia_pd.id = iaid,
.ia_pd.lifetime_t1 = htobe32(lt_t1),
.ia_pd.lifetime_t2 = htobe32(lt_t2),
.addresses = TAKE_PTR(ia.addresses),
};
break;
default:
break;
assert_not_reached();
}
if (ret_status_code)
*ret_status_code = 0;
return 1;
return 0;
}
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
struct in6_addr **addrs, size_t count) {
int dhcp6_option_parse_addresses(
const uint8_t *optval,
size_t optlen,
struct in6_addr **addrs,
size_t *count) {
assert(optval);
assert(addrs);
assert(count);
if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0)
return -EINVAL;
return -EBADMSG;
if (!GREEDY_REALLOC(*addrs, count * sizeof(struct in6_addr) + optlen))
if (!GREEDY_REALLOC(*addrs, *count + optlen / sizeof(struct in6_addr)))
return -ENOMEM;
memcpy(*addrs + count, optval, optlen);
memcpy(*addrs + *count, optval, optlen);
*count += optlen / sizeof(struct in6_addr);
count += optlen / sizeof(struct in6_addr);
return count;
return 0;
}
static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) {
_cleanup_free_ char *ret = NULL;
const uint8_t *optval = *data;
uint16_t optlen = *len;
bool first = true;
size_t n = 0;
static int parse_domain(const uint8_t **data, size_t *len, char **ret) {
_cleanup_free_ char *domain = NULL;
const uint8_t *optval;
size_t optlen, n = 0;
int r;
assert(data);
assert(*data);
assert(len);
assert(ret);
optval = *data;
optlen = *len;
if (optlen <= 1)
return -ENODATA;
@ -724,42 +780,44 @@ static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain)
return -EMSGSIZE;
/* Literal label */
label = (const char *)optval;
label = (const char*) optval;
optval += c;
optlen -= c;
if (!GREEDY_REALLOC(ret, n + !first + DNS_LABEL_ESCAPED_MAX))
if (!GREEDY_REALLOC(domain, n + (n != 0) + DNS_LABEL_ESCAPED_MAX))
return -ENOMEM;
if (first)
first = false;
else
ret[n++] = '.';
if (n != 0)
domain[n++] = '.';
r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
r = dns_label_escape(label, c, domain + n, DNS_LABEL_ESCAPED_MAX);
if (r < 0)
return r;
n += r;
}
if (n) {
if (!GREEDY_REALLOC(ret, n + 1))
if (n > 0) {
if (!GREEDY_REALLOC(domain, n + 1))
return -ENOMEM;
ret[n] = 0;
domain[n] = '\0';
}
*out_domain = TAKE_PTR(ret);
*ret = TAKE_PTR(domain);
*data = optval;
*len = optlen;
return n;
}
int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str) {
int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret) {
_cleanup_free_ char *domain = NULL;
int r;
assert(optval);
assert(ret);
r = parse_domain(&optval, &optlen, &domain);
if (r < 0)
return r;
@ -768,39 +826,38 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
if (optlen != 0)
return -EINVAL;
*str = TAKE_PTR(domain);
*ret = TAKE_PTR(domain);
return 0;
}
int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
size_t idx = 0;
int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret) {
_cleanup_strv_free_ char **names = NULL;
int r;
assert(optval);
assert(ret);
if (optlen <= 1)
return -ENODATA;
if (optval[optlen - 1] != '\0')
return -EINVAL;
while (optlen > 0) {
_cleanup_free_ char *ret = NULL;
_cleanup_free_ char *name = NULL;
r = parse_domain(&optval, &optlen, &ret);
r = parse_domain(&optval, &optlen, &name);
if (r < 0)
return r;
if (r == 0)
continue;
r = strv_extend(&names, ret);
r = strv_consume(&names, TAKE_PTR(name));
if (r < 0)
return r;
idx++;
}
*str_arr = TAKE_PTR(names);
return idx;
*ret = TAKE_PTR(names);
return 0;
}
static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) {

View file

@ -5,13 +5,15 @@
#include "ether-addr-util.h"
#include "hexdecoct.h"
#include "in-addr-util.h"
#include "lldp-internal.h"
#include "lldp-neighbor.h"
#include "memory-util.h"
#include "missing_network.h"
#include "unaligned.h"
static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
assert(id);
assert(state);
siphash24_compress(id->chassis_id, id->chassis_id_size, state);
siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
siphash24_compress(id->port_id, id->port_id_size, state);
@ -19,16 +21,27 @@ static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash
}
int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
assert(x);
assert(y);
return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size)
?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size);
}
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func,
sd_lldp_neighbor, lldp_neighbor_unlink);
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
lldp_neighbor_hash_ops,
LLDPNeighborID,
lldp_neighbor_id_hash_func,
lldp_neighbor_id_compare_func,
sd_lldp_neighbor,
lldp_neighbor_unlink);
int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
const sd_lldp_neighbor *x = a, *y = b;
assert(x);
assert(y);
return CMP(x->until, y->until);
}
@ -36,14 +49,15 @@ _public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
if (!n)
return NULL;
assert(n->n_ref > 0 || n->lldp);
assert(n->n_ref > 0 || n->lldp_rx);
n->n_ref++;
return n;
}
static void lldp_neighbor_free(sd_lldp_neighbor *n) {
assert(n);
static sd_lldp_neighbor *lldp_neighbor_free(sd_lldp_neighbor *n) {
if (!n)
return NULL;
free(n->id.port_id);
free(n->id.chassis_id);
@ -53,7 +67,7 @@ static void lldp_neighbor_free(sd_lldp_neighbor *n) {
free(n->mud_url);
free(n->chassis_id_as_string);
free(n->port_id_as_string);
free(n);
return mfree(n);
}
_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
@ -67,7 +81,7 @@ _public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
assert(n->n_ref > 0);
n->n_ref--;
if (n->n_ref <= 0 && !n->lldp)
if (n->n_ref <= 0 && !n->lldp_rx)
lldp_neighbor_free(n);
return NULL;
@ -80,18 +94,18 @@ sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
if (!n)
return NULL;
if (!n->lldp)
if (!n->lldp_rx)
return NULL;
/* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
* because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
* ourselves from the hashtable and sometimes are called after we already are de-registered. */
(void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n);
(void) hashmap_remove_value(n->lldp_rx->neighbor_by_id, &n->id, n);
assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
assert_se(prioq_remove(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
n->lldp = NULL;
n->lldp_rx = NULL;
if (n->n_ref <= 0)
lldp_neighbor_free(n);
@ -112,7 +126,7 @@ sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) {
return n;
}
static int parse_string(sd_lldp *lldp, char **s, const void *q, size_t n) {
static int parse_string(sd_lldp_rx *lldp_rx, char **s, const void *q, size_t n) {
const char *p = q;
char *k;
@ -120,7 +134,7 @@ static int parse_string(sd_lldp *lldp, char **s, const void *q, size_t n) {
assert(p || n == 0);
if (*s) {
log_lldp(lldp, "Found duplicate string, ignoring field.");
log_lldp_rx(lldp_rx, "Found duplicate string, ignoring field.");
return 0;
}
@ -133,7 +147,7 @@ static int parse_string(sd_lldp *lldp, char **s, const void *q, size_t n) {
/* Look for inner NULs */
if (memchr(p, 0, n)) {
log_lldp(lldp, "Found inner NUL in string, ignoring field.");
log_lldp_rx(lldp_rx, "Found inner NUL in string, ignoring field.");
return 0;
}
@ -157,14 +171,14 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
assert(n);
if (n->raw_size < sizeof(struct ether_header))
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
"Received truncated packet, ignoring.");
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"Received truncated packet, ignoring.");
memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
if (h.ether_type != htobe16(ETHERTYPE_LLDP))
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
"Received packet with wrong type, ignoring.");
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"Received packet with wrong type, ignoring.");
if (h.ether_dhost[0] != 0x01 ||
h.ether_dhost[1] != 0x80 ||
@ -172,8 +186,8 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
h.ether_dhost[3] != 0x00 ||
h.ether_dhost[4] != 0x00 ||
!IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e))
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
"Received packet with wrong destination address, ignoring.");
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"Received packet with wrong destination address, ignoring.");
memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr));
memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr));
@ -186,7 +200,7 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
uint16_t length;
if (left < 2)
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"TLV lacks header, ignoring.");
type = p[0] >> 1;
@ -194,15 +208,15 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
p += 2, left -= 2;
if (left < length)
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
"TLV truncated, ignoring datagram.");
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"TLV truncated, ignoring datagram.");
switch (type) {
case SD_LLDP_TYPE_END:
if (length != 0)
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
"End marker TLV not zero-sized, ignoring datagram.");
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"End marker TLV not zero-sized, ignoring datagram.");
/* Note that after processing the SD_LLDP_TYPE_END left could still be > 0
* as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
@ -212,12 +226,12 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
case SD_LLDP_TYPE_CHASSIS_ID:
if (length < 2 || length > 256)
/* includes the chassis subtype, hence one extra byte */
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
"Chassis ID field size out of range, ignoring datagram.");
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"Chassis ID field size out of range, ignoring datagram.");
if (n->id.chassis_id)
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
"Duplicate chassis ID field, ignoring datagram.");
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"Duplicate chassis ID field, ignoring datagram.");
n->id.chassis_id = memdup(p, length);
if (!n->id.chassis_id)
@ -229,12 +243,12 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
case SD_LLDP_TYPE_PORT_ID:
if (length < 2 || length > 256)
/* includes the port subtype, hence one extra byte */
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
"Port ID field size out of range, ignoring datagram.");
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"Port ID field size out of range, ignoring datagram.");
if (n->id.port_id)
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
"Duplicate port ID field, ignoring datagram.");
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"Duplicate port ID field, ignoring datagram.");
n->id.port_id = memdup(p, length);
if (!n->id.port_id)
@ -245,39 +259,39 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
case SD_LLDP_TYPE_TTL:
if (length != 2)
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
"TTL field has wrong size, ignoring datagram.");
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"TTL field has wrong size, ignoring datagram.");
if (n->has_ttl)
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
"Duplicate TTL field, ignoring datagram.");
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"Duplicate TTL field, ignoring datagram.");
n->ttl = unaligned_read_be16(p);
n->has_ttl = true;
break;
case SD_LLDP_TYPE_PORT_DESCRIPTION:
r = parse_string(n->lldp, &n->port_description, p, length);
r = parse_string(n->lldp_rx, &n->port_description, p, length);
if (r < 0)
return r;
break;
case SD_LLDP_TYPE_SYSTEM_NAME:
r = parse_string(n->lldp, &n->system_name, p, length);
r = parse_string(n->lldp_rx, &n->system_name, p, length);
if (r < 0)
return r;
break;
case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
r = parse_string(n->lldp, &n->system_description, p, length);
r = parse_string(n->lldp_rx, &n->system_description, p, length);
if (r < 0)
return r;
break;
case SD_LLDP_TYPE_SYSTEM_CAPABILITIES:
if (length != 4)
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
"System capabilities field has wrong size.");
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"System capabilities field has wrong size.");
n->system_capabilities = unaligned_read_be16(p);
n->enabled_capabilities = unaligned_read_be16(p + 2);
@ -286,14 +300,13 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
case SD_LLDP_TYPE_PRIVATE:
if (length < 4)
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
"Found private TLV that is too short, ignoring.");
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"Found private TLV that is too short, ignoring.");
/* 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->lldp, &n->mud_url, p + sizeof(SD_LLDP_OUI_MUD) + 1,
length - 1 - sizeof(SD_LLDP_OUI_MUD));
if (memcmp(p, SD_LLDP_OUI_IANA_MUD, sizeof(SD_LLDP_OUI_IANA_MUD)) == 0) {
r = parse_string(n->lldp_rx, &n->mud_url, p + sizeof(SD_LLDP_OUI_IANA_MUD),
length - sizeof(SD_LLDP_OUI_IANA_MUD));
if (r < 0)
return r;
}
@ -305,8 +318,8 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
end_marker:
if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl)
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
"One or more mandatory TLV missing in datagram. Ignoring.");
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
"One or more mandatory TLV missing in datagram. Ignoring.");
n->rindex = sizeof(struct ether_header);
@ -328,8 +341,8 @@ void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
} else
n->until = 0;
if (n->lldp)
prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
if (n->lldp_rx)
prioq_reshuffle(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx);
}
bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {

View file

@ -5,10 +5,10 @@
#include <stdbool.h>
#include <sys/types.h>
#include "sd-lldp.h"
#include "sd-lldp-rx.h"
#include "hash-funcs.h"
#include "lldp-internal.h"
#include "lldp-rx-internal.h"
#include "time-util.h"
typedef struct LLDPNeighborID {
@ -21,8 +21,8 @@ typedef struct LLDPNeighborID {
} LLDPNeighborID;
struct sd_lldp_neighbor {
/* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */
sd_lldp *lldp;
/* Neighbor objects stay around as long as they are linked into an "sd_lldp_rx" object or n_ref > 0. */
sd_lldp_rx *lldp_rx;
unsigned n_ref;
triple_timestamp timestamp;

View file

@ -56,15 +56,18 @@ int lldp_network_bind_raw_socket(int ifindex) {
if (r < 0)
return -errno;
/* customer bridge */
r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
if (r < 0)
return -errno;
/* non TPMR bridge */
mreq.mr_address[ETH_ALEN - 1] = 0x03;
r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
if (r < 0)
return -errno;
/* nearest bridge */
mreq.mr_address[ETH_ALEN - 1] = 0x0E;
r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
if (r < 0)

View file

@ -2,13 +2,13 @@
#pragma once
#include "sd-event.h"
#include "sd-lldp.h"
#include "sd-lldp-rx.h"
#include "hashmap.h"
#include "log-link.h"
#include "network-common.h"
#include "prioq.h"
struct sd_lldp {
struct sd_lldp_rx {
unsigned n_ref;
int ifindex;
@ -25,7 +25,7 @@ struct sd_lldp {
uint64_t neighbors_max;
sd_lldp_callback_t callback;
sd_lldp_rx_callback_t callback;
void *userdata;
uint16_t capability_mask;
@ -33,16 +33,16 @@ struct sd_lldp {
struct ether_addr filter_address;
};
const char* lldp_event_to_string(sd_lldp_event_t e) _const_;
sd_lldp_event_t lldp_event_from_string(const char *s) _pure_;
const char* lldp_rx_event_to_string(sd_lldp_rx_event_t e) _const_;
sd_lldp_rx_event_t lldp_rx_event_from_string(const char *s) _pure_;
#define log_lldp_errno(lldp, error, fmt, ...) \
#define log_lldp_rx_errno(lldp_rx, error, fmt, ...) \
log_interface_prefix_full_errno( \
"LLDP: ", \
sd_lldp_get_ifname(lldp), \
"LLDP Rx: ", \
sd_lldp_rx, lldp_rx, \
error, fmt, ##__VA_ARGS__)
#define log_lldp(lldp, fmt, ...) \
#define log_lldp_rx(lldp_rx, fmt, ...) \
log_interface_prefix_full_errno_zerook( \
"LLDP: ", \
sd_lldp_get_ifname(lldp), \
"LLDP Rx: ", \
sd_lldp_rx, lldp_rx, \
0, fmt, ##__VA_ARGS__)

View file

@ -2,23 +2,14 @@
#include "format-util.h"
#include "network-common.h"
#include "string-util.h"
const char *get_ifname(int ifindex, char **ifname) {
char buf[IF_NAMESIZE + 1];
int get_ifname(int ifindex, char **ifname) {
assert(ifname);
/* This sets ifname only when it is not set yet. */
if (*ifname)
return *ifname;
return 0;
if (ifindex <= 0)
return NULL;
if (!format_ifname(ifindex, buf))
return NULL;
return *ifname = strdup(buf);
return format_ifname_alloc(ifindex, ifname);
}

View file

@ -1,4 +1,28 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
const char *get_ifname(int ifindex, char **ifname);
#include "log-link.h"
#define log_interface_prefix_full_errno_zerook(prefix, type, val, error, fmt, ...) \
({ \
int _e = (error); \
if (DEBUG_LOGGING) { \
const char *_n = NULL; \
\
(void) type##_get_ifname(val, &_n); \
log_interface_full_errno_zerook( \
_n, LOG_DEBUG, _e, prefix fmt, \
##__VA_ARGS__); \
} \
-ERRNO_VALUE(_e); \
})
#define log_interface_prefix_full_errno(prefix, type, val, error, fmt, ...) \
({ \
int _error = (error); \
ASSERT_NON_ZERO(_error); \
log_interface_prefix_full_errno_zerook( \
prefix, type, val, _error, fmt, ##__VA_ARGS__); \
})
int get_ifname(int ifindex, char **ifname);

View file

@ -192,7 +192,7 @@ int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) {
if (len != sizeof_field(sd_dhcp_client_id, eth))
return -EINVAL;
r = asprintf(&t, "%x:%x:%x:%x:%x:%x",
r = asprintf(&t, "%02x:%02x:%02x:%02x:%02x:%02x",
client_id->eth.haddr[0],
client_id->eth.haddr[1],
client_id->eth.haddr[2],
@ -297,11 +297,19 @@ int sd_dhcp_client_set_ifname(sd_dhcp_client *client, const char *ifname) {
return free_and_strdup(&client->ifname, ifname);
}
const char *sd_dhcp_client_get_ifname(sd_dhcp_client *client) {
if (!client)
return NULL;
int sd_dhcp_client_get_ifname(sd_dhcp_client *client, const char **ret) {
int r;
return get_ifname(client->ifindex, &client->ifname);
assert_return(client, -EINVAL);
r = get_ifname(client->ifindex, &client->ifname);
if (r < 0)
return r;
if (ret)
*ret = client->ifname;
return 0;
}
int sd_dhcp_client_set_mac(
@ -726,7 +734,7 @@ static int client_notify(sd_dhcp_client *client, int event) {
static int client_initialize(sd_dhcp_client *client) {
assert_return(client, -EINVAL);
client->receive_message = sd_event_source_unref(client->receive_message);
client->receive_message = sd_event_source_disable_unref(client->receive_message);
client->fd = safe_close(client->fd);
@ -1308,7 +1316,7 @@ static int client_timeout_resend(
goto error;
default:
assert_not_reached("Unhandled choice");
assert_not_reached();
}
r = event_reset_time(client->event, &client->timeout_resend,
@ -1492,7 +1500,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
assert(client);
client->receive_message = sd_event_source_unref(client->receive_message);
client->receive_message = sd_event_source_disable_unref(client->receive_message);
client->fd = safe_close(client->fd);
client->state = DHCP_STATE_REBINDING;
@ -1844,7 +1852,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
client->start_delay = 0;
(void) event_source_disable(client->timeout_resend);
client->receive_message = sd_event_source_unref(client->receive_message);
client->receive_message = sd_event_source_disable_unref(client->receive_message);
client->fd = safe_close(client->fd);
client->state = DHCP_STATE_BOUND;
@ -1896,7 +1904,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
r = -EINVAL;
goto error;
default:
assert_not_reached("invalid state");
assert_not_reached();
}
error:
@ -2226,17 +2234,15 @@ static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) {
log_dhcp_client(client, "FREE");
client_initialize(client);
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
client->timeout_expire = sd_event_source_unref(client->timeout_expire);
client_initialize(client);
sd_dhcp_client_detach_event(client);
sd_dhcp_lease_unref(client->lease);
set_free(client->req_opts);
free(client->hostname);
free(client->vendor_class_identifier);

View file

@ -179,11 +179,19 @@ int sd_dhcp6_client_set_ifname(sd_dhcp6_client *client, const char *ifname) {
return free_and_strdup(&client->ifname, ifname);
}
const char *sd_dhcp6_client_get_ifname(sd_dhcp6_client *client) {
if (!client)
return NULL;
int sd_dhcp6_client_get_ifname(sd_dhcp6_client *client, const char **ret) {
int r;
return get_ifname(client->ifindex, &client->ifname);
assert_return(client, -EINVAL);
r = get_ifname(client->ifindex, &client->ifname);
if (r < 0)
return r;
if (ret)
*ret = client->ifname;
return 0;
}
int sd_dhcp6_client_set_local_address(
@ -1101,11 +1109,9 @@ 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;
uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX;
usec_t irt = IRT_DEFAULT;
bool clientid = false;
size_t pos = 0;
int r;
assert(client);
@ -1114,50 +1120,35 @@ static int client_parse_message(
assert(lease);
len -= sizeof(DHCP6Message);
for (size_t offset = 0; offset < len;) {
uint16_t optcode;
size_t optlen;
const uint8_t *optval;
while (pos < len) {
DHCP6Option *option = (DHCP6Option *) &message->options[pos];
uint16_t optcode, optlen;
be32_t iaid_lease;
int status;
uint8_t *optval;
if (len < pos + offsetof(DHCP6Option, data))
return -ENOBUFS;
optcode = be16toh(option->code);
optlen = be16toh(option->len);
optval = option->data;
if (len < pos + offsetof(DHCP6Option, data) + optlen)
return -ENOBUFS;
r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
if (r < 0)
return r;
switch (optcode) {
case SD_DHCP6_OPTION_CLIENTID:
if (clientid) {
log_dhcp6_client(client, "%s contains multiple clientids",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}
if (clientid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple clientids",
dhcp6_message_type_to_string(message->type));
if (optlen != client->duid_len ||
memcmp(&client->duid, optval, optlen) != 0) {
log_dhcp6_client(client, "%s DUID does not match",
dhcp6_message_type_to_string(message->type));
memcmp(&client->duid, optval, optlen) != 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s DUID does not match",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}
clientid = true;
break;
case SD_DHCP6_OPTION_SERVERID:
r = dhcp6_lease_get_serverid(lease, NULL, NULL);
if (r >= 0) {
log_dhcp6_client(client, "%s contains multiple serverids",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}
if (r >= 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple serverids",
dhcp6_message_type_to_string(message->type));
r = dhcp6_lease_set_serverid(lease, optval, optlen);
if (r < 0)
@ -1175,87 +1166,75 @@ static int client_parse_message(
break;
case SD_DHCP6_OPTION_STATUS_CODE:
status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option));
if (status < 0)
return status;
case SD_DHCP6_OPTION_STATUS_CODE: {
_cleanup_free_ char *msg = NULL;
if (status > 0) {
log_dhcp6_client(client, "%s Status %s",
dhcp6_message_type_to_string(message->type),
dhcp6_message_status_to_string(status));
return -EINVAL;
}
break;
case SD_DHCP6_OPTION_IA_NA:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
log_dhcp6_client(client, "Information request ignoring IA NA option");
break;
}
r = dhcp6_option_parse_ia(client, 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);
r = dhcp6_option_parse_status(optval, optlen, &msg);
if (r < 0)
return r;
if (client->ia_na.ia_na.id != iaid_lease) {
log_dhcp6_client(client, "%s has wrong IAID for IA NA",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
if (r > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received %s message with non-zero status: %s%s%s",
dhcp6_message_type_to_string(message->type),
strempty(msg), isempty(msg) ? "" : ": ",
dhcp6_message_status_to_string(r));
break;
}
case SD_DHCP6_OPTION_IA_NA: {
_cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
break;
}
r = dhcp6_option_parse_ia(client, client->ia_pd.ia_na.id, optcode, optlen, optval, &ia);
if (r == -ENOMEM)
return r;
if (r < 0)
continue;
if (lease->ia.addresses) {
lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
}
break;
case SD_DHCP6_OPTION_IA_PD:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
log_dhcp6_client(client, "Information request ignoring IA PD option");
break;
}
r = dhcp6_option_parse_ia(client, 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;
log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
continue;
}
r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
if (r < 0)
return r;
lease->ia = ia;
ia = (DHCP6IA) {};
if (client->ia_pd.ia_pd.id != iaid_lease) {
log_dhcp6_client(client, "%s has wrong IAID for IA PD",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}
if (lease->pd.addresses) {
lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
}
lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2));
break;
}
case SD_DHCP6_OPTION_IA_PD: {
_cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
break;
}
r = dhcp6_option_parse_ia(client, client->ia_pd.ia_pd.id, optcode, optlen, optval, &ia);
if (r == -ENOMEM)
return r;
if (r < 0)
continue;
if (lease->pd.addresses) {
log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
continue;
}
lease->pd = ia;
ia = (DHCP6IA) {};
lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
break;
}
case SD_DHCP6_OPTION_RAPID_COMMIT:
r = dhcp6_lease_set_rapid_commit(lease);
if (r < 0)
@ -1264,28 +1243,28 @@ static int client_parse_message(
break;
case SD_DHCP6_OPTION_DNS_SERVERS:
r = dhcp6_lease_set_dns(lease, optval, optlen);
r = dhcp6_lease_add_dns(lease, optval, optlen);
if (r < 0)
return r;
break;
case SD_DHCP6_OPTION_DOMAIN_LIST:
r = dhcp6_lease_set_domains(lease, optval, optlen);
r = dhcp6_lease_add_domains(lease, optval, optlen);
if (r < 0)
return r;
break;
case SD_DHCP6_OPTION_NTP_SERVER:
r = dhcp6_lease_set_ntp(lease, optval, optlen);
r = dhcp6_lease_add_ntp(lease, optval, optlen);
if (r < 0)
return r;
break;
case SD_DHCP6_OPTION_SNTP_SERVERS:
r = dhcp6_lease_set_sntp(lease, optval, optlen);
r = dhcp6_lease_add_sntp(lease, optval, optlen);
if (r < 0)
return r;
@ -1305,30 +1284,21 @@ static int client_parse_message(
irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
break;
}
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));
return -EINVAL;
}
if (!clientid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has incomplete options",
dhcp6_message_type_to_string(message->type));
if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
r = dhcp6_lease_get_serverid(lease, NULL, NULL);
if (r < 0) {
log_dhcp6_client(client, "%s has no server id",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}
if (r < 0)
return log_dhcp6_client_errno(client, r, "%s has no server id",
dhcp6_message_type_to_string(message->type));
if (!lease->ia.addresses && !lease->pd.addresses)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring.");
} else {
if (lease->ia.addresses) {
lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
@ -1479,15 +1449,16 @@ static int client_receive_message(
return 0;
}
if (client->transaction_id != (message->transaction_id &
htobe32(0x00ffffff)))
if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
return 0;
switch (client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
r = client_receive_reply(client, message, len);
if (r < 0)
if (r < 0) {
log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
return 0;
}
client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
@ -1497,10 +1468,13 @@ static int client_receive_message(
case DHCP6_STATE_SOLICITATION:
r = client_receive_advertise(client, message, len);
if (r < 0) {
log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m");
return 0;
}
if (r == DHCP6_STATE_REQUEST) {
client_start(client, r);
break;
}
@ -1510,11 +1484,12 @@ static int client_receive_message(
case DHCP6_STATE_REBIND:
r = client_receive_reply(client, message, len);
if (r < 0)
if (r < 0) {
log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
return 0;
}
if (r == DHCP6_STATE_BOUND) {
r = client_start(client, DHCP6_STATE_BOUND);
if (r < 0) {
client_stop(client, r);

View file

@ -193,86 +193,69 @@ void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
lease->prefix_iter = lease->pd.addresses;
}
int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
int r;
int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL);
if (!optlen)
if (optlen == 0)
return 0;
r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns,
lease->dns_count);
if (r < 0)
return r;
lease->dns_count = r;
return 0;
return dhcp6_option_parse_addresses(optval, optlen, &lease->dns, &lease->dns_count);
}
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs) {
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
assert_return(lease, -EINVAL);
assert_return(addrs, -EINVAL);
assert_return(ret, -EINVAL);
if (lease->dns_count) {
*addrs = lease->dns;
return lease->dns_count;
}
if (!lease->dns)
return -ENOENT;
return -ENOENT;
*ret = lease->dns;
return lease->dns_count;
}
int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
size_t optlen) {
int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
_cleanup_strv_free_ char **domains = NULL;
int r;
char **domains;
assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL);
if (!optlen)
if (optlen == 0)
return 0;
r = dhcp6_option_parse_domainname_list(optval, optlen, &domains);
if (r < 0)
return 0;
return r;
strv_free_and_replace(lease->domains, domains);
lease->domains_count = r;
return r;
return strv_extend_strv(&lease->domains, domains, true);
}
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) {
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
assert_return(lease, -EINVAL);
assert_return(domains, -EINVAL);
assert_return(ret, -EINVAL);
if (lease->domains_count) {
*domains = lease->domains;
return lease->domains_count;
}
if (!lease->domains)
return -ENOENT;
return -ENOENT;
*ret = lease->domains;
return strv_length(lease->domains);
}
int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
int r;
uint16_t subopt;
size_t sublen;
uint8_t *subval;
assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL);
lease->ntp = mfree(lease->ntp);
lease->ntp_count = 0;
for (size_t offset = 0; offset < optlen;) {
const uint8_t *subval;
size_t sublen;
uint16_t subopt;
while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen,
&subval)) >= 0) {
int s;
char **servers;
r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
if (r < 0)
return r;
switch(subopt) {
case DHCP6_NTP_SUBOPTION_SRV_ADDR:
@ -280,86 +263,74 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
if (sublen != 16)
return 0;
s = dhcp6_option_parse_ip6addrs(subval, sublen,
&lease->ntp,
lease->ntp_count);
if (s < 0)
return s;
lease->ntp_count = s;
break;
case DHCP6_NTP_SUBOPTION_SRV_FQDN:
r = dhcp6_option_parse_domainname_list(subval, sublen,
&servers);
r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
if (r < 0)
return 0;
strv_free_and_replace(lease->ntp_fqdn, servers);
lease->ntp_fqdn_count = r;
return r;
break;
}
}
if (r != -ENOMSG)
return r;
case DHCP6_NTP_SUBOPTION_SRV_FQDN: {
_cleanup_free_ char *server = NULL;
r = dhcp6_option_parse_domainname(subval, sublen, &server);
if (r < 0)
return r;
if (strv_contains(lease->ntp_fqdn, server))
continue;
r = strv_consume(&lease->ntp_fqdn, TAKE_PTR(server));
if (r < 0)
return r;
break;
}}
}
return 0;
}
int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
int r;
int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL);
if (!optlen)
if (optlen == 0)
return 0;
if (lease->ntp || lease->ntp_fqdn)
return -EEXIST;
/* Using deprecated SNTP information */
r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp,
lease->ntp_count);
if (r < 0)
return r;
lease->ntp_count = r;
return 0;
/* SNTP option is defined in RFC4075, and deprecated by RFC5908. */
return dhcp6_option_parse_addresses(optval, optlen, &lease->sntp, &lease->sntp_count);
}
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease,
const struct in6_addr **addrs) {
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
assert_return(lease, -EINVAL);
assert_return(addrs, -EINVAL);
assert_return(ret, -EINVAL);
if (lease->ntp_count) {
*addrs = lease->ntp;
if (lease->ntp) {
*ret = lease->ntp;
return lease->ntp_count;
}
return -ENOENT;
}
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) {
assert_return(lease, -EINVAL);
assert_return(ntp_fqdn, -EINVAL);
if (lease->ntp_fqdn_count) {
*ntp_fqdn = lease->ntp_fqdn;
return lease->ntp_fqdn_count;
if (lease->sntp && !lease->ntp_fqdn) {
/* Fallback to the deprecated SNTP option. */
*ret = lease->sntp;
return lease->sntp_count;
}
return -ENOENT;
}
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval,
size_t optlen) {
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
assert_return(lease, -EINVAL);
assert_return(ret, -EINVAL);
if (!lease->ntp_fqdn)
return -ENOENT;
*ret = lease->ntp_fqdn;
return strv_length(lease->ntp_fqdn);
}
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
int r;
char *fqdn;
@ -378,33 +349,31 @@ int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval,
return free_and_replace(lease->fqdn, fqdn);
}
int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn) {
int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
assert_return(lease, -EINVAL);
assert_return(fqdn, -EINVAL);
assert_return(ret, -EINVAL);
if (lease->fqdn) {
*fqdn = lease->fqdn;
return 0;
}
if (!lease->fqdn)
return -ENOENT;
return -ENOENT;
*ret = lease->fqdn;
return 0;
}
static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
assert(lease);
if (!lease)
return NULL;
free(lease->serverid);
dhcp6_lease_free_ia(&lease->ia);
dhcp6_lease_free_ia(&lease->pd);
free(lease->dns);
free(lease->fqdn);
lease->domains = strv_free(lease->domains);
strv_free(lease->domains);
free(lease->ntp);
strv_free(lease->ntp_fqdn);
free(lease->sntp);
lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
return mfree(lease);
}

View file

@ -17,7 +17,6 @@
#include "event-util.h"
#include "fd-util.h"
#include "in-addr-util.h"
#include "log-link.h"
#include "memory-util.h"
#include "network-common.h"
#include "random-util.h"
@ -81,12 +80,12 @@ struct sd_ipv4acd {
#define log_ipv4acd_errno(acd, error, fmt, ...) \
log_interface_prefix_full_errno( \
"IPv4ACD: ", \
sd_ipv4acd_get_ifname(acd), \
sd_ipv4acd, acd, \
error, fmt, ##__VA_ARGS__)
#define log_ipv4acd(acd, fmt, ...) \
log_interface_prefix_full_errno_zerook( \
"IPv4ACD: ", \
sd_ipv4acd_get_ifname(acd), \
sd_ipv4acd, acd, \
0, fmt, ##__VA_ARGS__)
static const char * const ipv4acd_state_table[_IPV4ACD_STATE_MAX] = {
@ -120,7 +119,7 @@ static void ipv4acd_reset(sd_ipv4acd *acd) {
assert(acd);
(void) event_source_disable(acd->timer_event_source);
acd->receive_message_event_source = sd_event_source_unref(acd->receive_message_event_source);
acd->receive_message_event_source = sd_event_source_disable_unref(acd->receive_message_event_source);
acd->fd = safe_close(acd->fd);
@ -130,9 +129,8 @@ static void ipv4acd_reset(sd_ipv4acd *acd) {
static sd_ipv4acd *ipv4acd_free(sd_ipv4acd *acd) {
assert(acd);
acd->timer_event_source = sd_event_source_unref(acd->timer_event_source);
ipv4acd_reset(acd);
sd_event_source_unref(acd->timer_event_source);
sd_ipv4acd_detach_event(acd);
free(acd->ifname);
return mfree(acd);
@ -293,7 +291,7 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata)
break;
default:
assert_not_reached("Invalid state.");
assert_not_reached();
}
return 0;
@ -409,7 +407,7 @@ static int ipv4acd_on_packet(
break;
default:
assert_not_reached("Invalid state.");
assert_not_reached();
}
return 0;
@ -446,11 +444,19 @@ int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *ifname) {
return free_and_strdup(&acd->ifname, ifname);
}
const char *sd_ipv4acd_get_ifname(sd_ipv4acd *acd) {
if (!acd)
return NULL;
int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret) {
int r;
return get_ifname(acd->ifindex, &acd->ifname);
assert_return(acd, -EINVAL);
r = get_ifname(acd->ifindex, &acd->ifname);
if (r < 0)
return r;
if (ret)
*ret = acd->ifname;
return 0;
}
int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {

View file

@ -15,7 +15,7 @@
#include "alloc-util.h"
#include "ether-addr-util.h"
#include "in-addr-util.h"
#include "log-link.h"
#include "network-common.h"
#include "random-util.h"
#include "siphash24.h"
#include "sparse-endian.h"
@ -55,12 +55,12 @@ struct sd_ipv4ll {
#define log_ipv4ll_errno(ll, error, fmt, ...) \
log_interface_prefix_full_errno( \
"IPv4LL: ", \
sd_ipv4ll_get_ifname(ll), \
sd_ipv4ll, ll, \
error, fmt, ##__VA_ARGS__)
#define log_ipv4ll(ll, fmt, ...) \
log_interface_prefix_full_errno_zerook( \
"IPv4LL: ", \
sd_ipv4ll_get_ifname(ll), \
sd_ipv4ll, ll, \
0, fmt, ##__VA_ARGS__)
static void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata);
@ -133,11 +133,10 @@ int sd_ipv4ll_set_ifname(sd_ipv4ll *ll, const char *ifname) {
return sd_ipv4acd_set_ifname(ll->acd, ifname);
}
const char *sd_ipv4ll_get_ifname(sd_ipv4ll *ll) {
if (!ll)
return NULL;
int sd_ipv4ll_get_ifname(sd_ipv4ll *ll, const char **ret) {
assert_return(ll, -EINVAL);
return sd_ipv4acd_get_ifname(ll->acd);
return sd_ipv4acd_get_ifname(ll->acd, ret);
}
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
@ -360,7 +359,7 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
break;
default:
assert_not_reached("Invalid IPv4ACD event.");
assert_not_reached();
}
return;

View file

@ -0,0 +1,523 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <arpa/inet.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#include "sd-lldp-rx.h"
#include "alloc-util.h"
#include "ether-addr-util.h"
#include "event-util.h"
#include "fd-util.h"
#include "lldp-neighbor.h"
#include "lldp-network.h"
#include "lldp-rx-internal.h"
#include "memory-util.h"
#include "network-common.h"
#include "socket-util.h"
#include "sort-util.h"
#include "string-table.h"
#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
static const char * const lldp_rx_event_table[_SD_LLDP_RX_EVENT_MAX] = {
[SD_LLDP_RX_EVENT_ADDED] = "added",
[SD_LLDP_RX_EVENT_REMOVED] = "removed",
[SD_LLDP_RX_EVENT_UPDATED] = "updated",
[SD_LLDP_RX_EVENT_REFRESHED] = "refreshed",
};
DEFINE_STRING_TABLE_LOOKUP(lldp_rx_event, sd_lldp_rx_event_t);
static void lldp_rx_flush_neighbors(sd_lldp_rx *lldp_rx) {
assert(lldp_rx);
hashmap_clear(lldp_rx->neighbor_by_id);
}
static void lldp_rx_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n) {
assert(lldp_rx);
assert(event >= 0 && event < _SD_LLDP_RX_EVENT_MAX);
if (!lldp_rx->callback)
return (void) log_lldp_rx(lldp_rx, "Received '%s' event.", lldp_rx_event_to_string(event));
log_lldp_rx(lldp_rx, "Invoking callback for '%s' event.", lldp_rx_event_to_string(event));
lldp_rx->callback(lldp_rx, event, n, lldp_rx->userdata);
}
static int lldp_rx_make_space(sd_lldp_rx *lldp_rx, size_t extra) {
usec_t t = USEC_INFINITY;
bool changed = false;
assert(lldp_rx);
/* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
* are free. */
for (;;) {
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
n = prioq_peek(lldp_rx->neighbor_by_expiry);
if (!n)
break;
sd_lldp_neighbor_ref(n);
if (hashmap_size(lldp_rx->neighbor_by_id) > LESS_BY(lldp_rx->neighbors_max, extra))
goto remove_one;
if (t == USEC_INFINITY)
t = now(clock_boottime_or_monotonic());
if (n->until > t)
break;
remove_one:
lldp_neighbor_unlink(n);
lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, n);
changed = true;
}
return changed;
}
static bool lldp_rx_keep_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
assert(lldp_rx);
assert(n);
/* Don't keep data with a zero TTL */
if (n->ttl <= 0)
return false;
/* Filter out data from the filter address */
if (!ether_addr_is_null(&lldp_rx->filter_address) &&
ether_addr_equal(&lldp_rx->filter_address, &n->source_address))
return false;
/* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
* no caps field set. */
if (n->has_capabilities &&
(n->enabled_capabilities & lldp_rx->capability_mask) == 0)
return false;
/* Keep everything else */
return true;
}
static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor);
static int lldp_rx_add_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
bool keep;
int r;
assert(lldp_rx);
assert(n);
assert(!n->lldp_rx);
keep = lldp_rx_keep_neighbor(lldp_rx, n);
/* First retrieve the old entry for this MSAP */
old = hashmap_get(lldp_rx->neighbor_by_id, &n->id);
if (old) {
sd_lldp_neighbor_ref(old);
if (!keep) {
lldp_neighbor_unlink(old);
lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old);
return 0;
}
if (lldp_neighbor_equal(n, old)) {
/* Is this equal, then restart the TTL counter, but don't do anything else. */
old->timestamp = n->timestamp;
lldp_rx_start_timer(lldp_rx, old);
lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REFRESHED, old);
return 0;
}
/* Data changed, remove the old entry, and add a new one */
lldp_neighbor_unlink(old);
} else if (!keep)
return 0;
/* Then, make room for at least one new neighbor */
lldp_rx_make_space(lldp_rx, 1);
r = hashmap_ensure_put(&lldp_rx->neighbor_by_id, &lldp_neighbor_hash_ops, &n->id, n);
if (r < 0)
goto finish;
r = prioq_ensure_put(&lldp_rx->neighbor_by_expiry, lldp_neighbor_prioq_compare_func, n, &n->prioq_idx);
if (r < 0) {
assert_se(hashmap_remove(lldp_rx->neighbor_by_id, &n->id) == n);
goto finish;
}
n->lldp_rx = lldp_rx;
lldp_rx_start_timer(lldp_rx, n);
lldp_rx_callback(lldp_rx, old ? SD_LLDP_RX_EVENT_UPDATED : SD_LLDP_RX_EVENT_ADDED, n);
return 1;
finish:
if (old)
lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old);
return r;
}
static int lldp_rx_handle_datagram(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
int r;
assert(lldp_rx);
assert(n);
r = lldp_neighbor_parse(n);
if (r < 0)
return r;
r = lldp_rx_add_neighbor(lldp_rx, n);
if (r < 0)
return log_lldp_rx_errno(lldp_rx, r, "Failed to add datagram. Ignoring.");
log_lldp_rx(lldp_rx, "Successfully processed LLDP datagram.");
return 0;
}
static int lldp_rx_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
ssize_t space, length;
sd_lldp_rx *lldp_rx = userdata;
struct timespec ts;
assert(fd >= 0);
assert(lldp_rx);
space = next_datagram_size_fd(fd);
if (space < 0) {
log_lldp_rx_errno(lldp_rx, space, "Failed to determine datagram size to read, ignoring: %m");
return 0;
}
n = lldp_neighbor_new(space);
if (!n) {
log_oom_debug();
return 0;
}
length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
if (length < 0) {
if (IN_SET(errno, EAGAIN, EINTR))
return 0;
log_lldp_rx_errno(lldp_rx, errno, "Failed to read LLDP datagram, ignoring: %m");
return 0;
}
if ((size_t) length != n->raw_size) {
log_lldp_rx(lldp_rx, "Packet size mismatch, ignoring");
return 0;
}
/* Try to get the timestamp of this packet if it is known */
if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
else
triple_timestamp_get(&n->timestamp);
(void) lldp_rx_handle_datagram(lldp_rx, n);
return 0;
}
static void lldp_rx_reset(sd_lldp_rx *lldp_rx) {
assert(lldp_rx);
(void) event_source_disable(lldp_rx->timer_event_source);
lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source);
lldp_rx->fd = safe_close(lldp_rx->fd);
}
int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx) {
if (!lldp_rx)
return false;
return lldp_rx->fd >= 0;
}
_public_ int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) {
int r;
assert_return(lldp_rx, -EINVAL);
assert_return(lldp_rx->event, -EINVAL);
assert_return(lldp_rx->ifindex > 0, -EINVAL);
if (sd_lldp_rx_is_running(lldp_rx))
return 0;
assert(!lldp_rx->io_event_source);
lldp_rx->fd = lldp_network_bind_raw_socket(lldp_rx->ifindex);
if (lldp_rx->fd < 0)
return lldp_rx->fd;
r = sd_event_add_io(lldp_rx->event, &lldp_rx->io_event_source, lldp_rx->fd, EPOLLIN, lldp_rx_receive_datagram, lldp_rx);
if (r < 0)
goto fail;
r = sd_event_source_set_priority(lldp_rx->io_event_source, lldp_rx->event_priority);
if (r < 0)
goto fail;
(void) sd_event_source_set_description(lldp_rx->io_event_source, "lldp-rx-io");
log_lldp_rx(lldp_rx, "Started LLDP client");
return 1;
fail:
lldp_rx_reset(lldp_rx);
return r;
}
_public_ int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) {
if (!sd_lldp_rx_is_running(lldp_rx))
return 0;
log_lldp_rx(lldp_rx, "Stopping LLDP client");
lldp_rx_reset(lldp_rx);
lldp_rx_flush_neighbors(lldp_rx);
return 1;
}
_public_ int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) {
int r;
assert_return(lldp_rx, -EINVAL);
assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
assert_return(!lldp_rx->event, -EBUSY);
if (event)
lldp_rx->event = sd_event_ref(event);
else {
r = sd_event_default(&lldp_rx->event);
if (r < 0)
return r;
}
lldp_rx->event_priority = priority;
return 0;
}
_public_ int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) {
assert_return(lldp_rx, -EINVAL);
assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source);
lldp_rx->timer_event_source = sd_event_source_disable_unref(lldp_rx->timer_event_source);
lldp_rx->event = sd_event_unref(lldp_rx->event);
return 0;
}
_public_ sd_event* sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx) {
assert_return(lldp_rx, NULL);
return lldp_rx->event;
}
_public_ int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata) {
assert_return(lldp_rx, -EINVAL);
lldp_rx->callback = cb;
lldp_rx->userdata = userdata;
return 0;
}
_public_ int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex) {
assert_return(lldp_rx, -EINVAL);
assert_return(ifindex > 0, -EINVAL);
assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
lldp_rx->ifindex = ifindex;
return 0;
}
int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname) {
assert_return(lldp_rx, -EINVAL);
assert_return(ifname, -EINVAL);
if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
return -EINVAL;
return free_and_strdup(&lldp_rx->ifname, ifname);
}
int sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx, const char **ret) {
int r;
assert_return(lldp_rx, -EINVAL);
r = get_ifname(lldp_rx->ifindex, &lldp_rx->ifname);
if (r < 0)
return r;
if (ret)
*ret = lldp_rx->ifname;
return 0;
}
static sd_lldp_rx *lldp_rx_free(sd_lldp_rx *lldp_rx) {
if (!lldp_rx)
return NULL;
lldp_rx_reset(lldp_rx);
sd_lldp_rx_detach_event(lldp_rx);
lldp_rx_flush_neighbors(lldp_rx);
hashmap_free(lldp_rx->neighbor_by_id);
prioq_free(lldp_rx->neighbor_by_expiry);
free(lldp_rx->ifname);
return mfree(lldp_rx);
}
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_rx, sd_lldp_rx, lldp_rx_free);
_public_ int sd_lldp_rx_new(sd_lldp_rx **ret) {
_cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL;
assert_return(ret, -EINVAL);
lldp_rx = new(sd_lldp_rx, 1);
if (!lldp_rx)
return -ENOMEM;
*lldp_rx = (sd_lldp_rx) {
.n_ref = 1,
.fd = -1,
.neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
.capability_mask = UINT16_MAX,
};
*ret = TAKE_PTR(lldp_rx);
return 0;
}
static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
sd_lldp_rx *lldp_rx = userdata;
int r;
r = lldp_rx_make_space(lldp_rx, 0);
if (r < 0) {
log_lldp_rx_errno(lldp_rx, r, "Failed to make space, ignoring: %m");
return 0;
}
r = lldp_rx_start_timer(lldp_rx, NULL);
if (r < 0) {
log_lldp_rx_errno(lldp_rx, r, "Failed to restart timer, ignoring: %m");
return 0;
}
return 0;
}
static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor) {
sd_lldp_neighbor *n;
assert(lldp_rx);
assert(lldp_rx->event);
if (neighbor)
lldp_neighbor_start_ttl(neighbor);
n = prioq_peek(lldp_rx->neighbor_by_expiry);
if (!n)
return event_source_disable(lldp_rx->timer_event_source);
return event_reset_time(lldp_rx->event, &lldp_rx->timer_event_source,
clock_boottime_or_monotonic(),
n->until, 0,
on_timer_event, lldp_rx,
lldp_rx->event_priority, "lldp-rx-timer", true);
}
static inline int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
assert(a);
assert(b);
assert(*a);
assert(*b);
return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
}
_public_ int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***ret) {
_cleanup_free_ sd_lldp_neighbor **l = NULL;
sd_lldp_neighbor *n;
int k = 0;
assert_return(lldp_rx, -EINVAL);
assert_return(ret, -EINVAL);
if (hashmap_isempty(lldp_rx->neighbor_by_id)) { /* Special shortcut */
*ret = NULL;
return 0;
}
l = new0(sd_lldp_neighbor*, hashmap_size(lldp_rx->neighbor_by_id));
if (!l)
return -ENOMEM;
HASHMAP_FOREACH(n, lldp_rx->neighbor_by_id)
l[k++] = sd_lldp_neighbor_ref(n);
assert((size_t) k == hashmap_size(lldp_rx->neighbor_by_id));
/* Return things in a stable order */
typesafe_qsort(l, k, neighbor_compare_func);
*ret = TAKE_PTR(l);
return k;
}
_public_ int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) {
assert_return(lldp_rx, -EINVAL);
assert_return(m > 0, -EINVAL);
lldp_rx->neighbors_max = m;
lldp_rx_make_space(lldp_rx, 0);
return 0;
}
_public_ int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) {
assert_return(lldp_rx, -EINVAL);
assert_return(mask != 0, -EINVAL);
lldp_rx->capability_mask = mask;
return 0;
}
_public_ int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *addr) {
assert_return(lldp_rx, -EINVAL);
/* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
* that our own can be filtered out here. */
if (addr)
lldp_rx->filter_address = *addr;
else
zero(lldp_rx->filter_address);
return 0;
}

View file

@ -1,522 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <arpa/inet.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#include "sd-lldp.h"
#include "alloc-util.h"
#include "ether-addr-util.h"
#include "event-util.h"
#include "fd-util.h"
#include "lldp-internal.h"
#include "lldp-neighbor.h"
#include "lldp-network.h"
#include "memory-util.h"
#include "network-common.h"
#include "socket-util.h"
#include "sort-util.h"
#include "string-table.h"
#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = {
[SD_LLDP_EVENT_ADDED] = "added",
[SD_LLDP_EVENT_REMOVED] = "removed",
[SD_LLDP_EVENT_UPDATED] = "updated",
[SD_LLDP_EVENT_REFRESHED] = "refreshed",
};
DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event_t);
static void lldp_flush_neighbors(sd_lldp *lldp) {
assert(lldp);
hashmap_clear(lldp->neighbor_by_id);
}
static void lldp_callback(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n) {
assert(lldp);
assert(event >= 0 && event < _SD_LLDP_EVENT_MAX);
if (!lldp->callback)
return (void) log_lldp(lldp, "Received '%s' event.", lldp_event_to_string(event));
log_lldp(lldp, "Invoking callback for '%s' event.", lldp_event_to_string(event));
lldp->callback(lldp, event, n, lldp->userdata);
}
static int lldp_make_space(sd_lldp *lldp, size_t extra) {
usec_t t = USEC_INFINITY;
bool changed = false;
assert(lldp);
/* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
* are free. */
for (;;) {
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
n = prioq_peek(lldp->neighbor_by_expiry);
if (!n)
break;
sd_lldp_neighbor_ref(n);
if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra))
goto remove_one;
if (t == USEC_INFINITY)
t = now(clock_boottime_or_monotonic());
if (n->until > t)
break;
remove_one:
lldp_neighbor_unlink(n);
lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n);
changed = true;
}
return changed;
}
static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
assert(lldp);
assert(n);
/* Don't keep data with a zero TTL */
if (n->ttl <= 0)
return false;
/* Filter out data from the filter address */
if (!ether_addr_is_null(&lldp->filter_address) &&
ether_addr_equal(&lldp->filter_address, &n->source_address))
return false;
/* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
* no caps field set. */
if (n->has_capabilities &&
(n->enabled_capabilities & lldp->capability_mask) == 0)
return false;
/* Keep everything else */
return true;
}
static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor);
static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
bool keep;
int r;
assert(lldp);
assert(n);
assert(!n->lldp);
keep = lldp_keep_neighbor(lldp, n);
/* First retrieve the old entry for this MSAP */
old = hashmap_get(lldp->neighbor_by_id, &n->id);
if (old) {
sd_lldp_neighbor_ref(old);
if (!keep) {
lldp_neighbor_unlink(old);
lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
return 0;
}
if (lldp_neighbor_equal(n, old)) {
/* Is this equal, then restart the TTL counter, but don't do anything else. */
old->timestamp = n->timestamp;
lldp_start_timer(lldp, old);
lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old);
return 0;
}
/* Data changed, remove the old entry, and add a new one */
lldp_neighbor_unlink(old);
} else if (!keep)
return 0;
/* Then, make room for at least one new neighbor */
lldp_make_space(lldp, 1);
r = hashmap_put(lldp->neighbor_by_id, &n->id, n);
if (r < 0)
goto finish;
r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx);
if (r < 0) {
assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n);
goto finish;
}
n->lldp = lldp;
lldp_start_timer(lldp, n);
lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n);
return 1;
finish:
if (old)
lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
return r;
}
static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) {
int r;
assert(lldp);
assert(n);
r = lldp_neighbor_parse(n);
if (r == -EBADMSG) /* Ignore bad messages */
return 0;
if (r < 0)
return r;
r = lldp_add_neighbor(lldp, n);
if (r < 0) {
log_lldp_errno(lldp, r, "Failed to add datagram. Ignoring.");
return 0;
}
log_lldp(lldp, "Successfully processed LLDP datagram.");
return 0;
}
static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
ssize_t space, length;
sd_lldp *lldp = userdata;
struct timespec ts;
assert(fd >= 0);
assert(lldp);
space = next_datagram_size_fd(fd);
if (space < 0) {
log_lldp_errno(lldp, space, "Failed to determine datagram size to read, ignoring: %m");
return 0;
}
n = lldp_neighbor_new(space);
if (!n)
return -ENOMEM;
length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
if (length < 0) {
if (IN_SET(errno, EAGAIN, EINTR))
return 0;
log_lldp_errno(lldp, errno, "Failed to read LLDP datagram, ignoring: %m");
return 0;
}
if ((size_t) length != n->raw_size) {
log_lldp(lldp, "Packet size mismatch, ignoring");
return 0;
}
/* Try to get the timestamp of this packet if it is known */
if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
else
triple_timestamp_get(&n->timestamp);
return lldp_handle_datagram(lldp, n);
}
static void lldp_reset(sd_lldp *lldp) {
assert(lldp);
(void) event_source_disable(lldp->timer_event_source);
lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
lldp->fd = safe_close(lldp->fd);
}
_public_ int sd_lldp_start(sd_lldp *lldp) {
int r;
assert_return(lldp, -EINVAL);
assert_return(lldp->event, -EINVAL);
assert_return(lldp->ifindex > 0, -EINVAL);
if (lldp->fd >= 0)
return 0;
assert(!lldp->io_event_source);
lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex);
if (lldp->fd < 0)
return lldp->fd;
r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
if (r < 0)
goto fail;
r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
if (r < 0)
goto fail;
(void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
log_lldp(lldp, "Started LLDP client");
return 1;
fail:
lldp_reset(lldp);
return r;
}
_public_ int sd_lldp_stop(sd_lldp *lldp) {
if (!lldp)
return 0;
if (lldp->fd < 0)
return 0;
log_lldp(lldp, "Stopping LLDP client");
lldp_reset(lldp);
lldp_flush_neighbors(lldp);
return 1;
}
_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) {
int r;
assert_return(lldp, -EINVAL);
assert_return(lldp->fd < 0, -EBUSY);
assert_return(!lldp->event, -EBUSY);
if (event)
lldp->event = sd_event_ref(event);
else {
r = sd_event_default(&lldp->event);
if (r < 0)
return r;
}
lldp->event_priority = priority;
return 0;
}
_public_ int sd_lldp_detach_event(sd_lldp *lldp) {
assert_return(lldp, -EINVAL);
assert_return(lldp->fd < 0, -EBUSY);
lldp->event = sd_event_unref(lldp->event);
return 0;
}
_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) {
assert_return(lldp, NULL);
return lldp->event;
}
_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) {
assert_return(lldp, -EINVAL);
lldp->callback = cb;
lldp->userdata = userdata;
return 0;
}
_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) {
assert_return(lldp, -EINVAL);
assert_return(ifindex > 0, -EINVAL);
assert_return(lldp->fd < 0, -EBUSY);
lldp->ifindex = ifindex;
return 0;
}
int sd_lldp_set_ifname(sd_lldp *lldp, const char *ifname) {
assert_return(lldp, -EINVAL);
assert_return(ifname, -EINVAL);
if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
return -EINVAL;
return free_and_strdup(&lldp->ifname, ifname);
}
const char *sd_lldp_get_ifname(sd_lldp *lldp) {
if (!lldp)
return NULL;
return get_ifname(lldp->ifindex, &lldp->ifname);
}
static sd_lldp* lldp_free(sd_lldp *lldp) {
assert(lldp);
lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
lldp_reset(lldp);
sd_lldp_detach_event(lldp);
lldp_flush_neighbors(lldp);
hashmap_free(lldp->neighbor_by_id);
prioq_free(lldp->neighbor_by_expiry);
free(lldp->ifname);
return mfree(lldp);
}
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp, sd_lldp, lldp_free);
_public_ int sd_lldp_new(sd_lldp **ret) {
_cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
int r;
assert_return(ret, -EINVAL);
lldp = new(sd_lldp, 1);
if (!lldp)
return -ENOMEM;
*lldp = (sd_lldp) {
.n_ref = 1,
.fd = -1,
.neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
.capability_mask = UINT16_MAX,
};
lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops);
if (!lldp->neighbor_by_id)
return -ENOMEM;
r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func);
if (r < 0)
return r;
*ret = TAKE_PTR(lldp);
return 0;
}
static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
}
static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
sd_lldp *lldp = userdata;
int r;
r = lldp_make_space(lldp, 0);
if (r < 0) {
log_lldp_errno(lldp, r, "Failed to make space, ignoring: %m");
return 0;
}
r = lldp_start_timer(lldp, NULL);
if (r < 0) {
log_lldp_errno(lldp, r, "Failed to restart timer, ignoring: %m");
return 0;
}
return 0;
}
static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) {
sd_lldp_neighbor *n;
assert(lldp);
if (neighbor)
lldp_neighbor_start_ttl(neighbor);
n = prioq_peek(lldp->neighbor_by_expiry);
if (!n)
return event_source_disable(lldp->timer_event_source);
if (!lldp->event)
return 0;
return event_reset_time(lldp->event, &lldp->timer_event_source,
clock_boottime_or_monotonic(),
n->until, 0,
on_timer_event, lldp,
lldp->event_priority, "lldp-timer", true);
}
_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
sd_lldp_neighbor **l = NULL, *n;
int k = 0, r;
assert_return(lldp, -EINVAL);
assert_return(ret, -EINVAL);
if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */
*ret = NULL;
return 0;
}
l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id));
if (!l)
return -ENOMEM;
r = lldp_start_timer(lldp, NULL);
if (r < 0) {
free(l);
return r;
}
HASHMAP_FOREACH(n, lldp->neighbor_by_id)
l[k++] = sd_lldp_neighbor_ref(n);
assert((size_t) k == hashmap_size(lldp->neighbor_by_id));
/* Return things in a stable order */
typesafe_qsort(l, k, neighbor_compare_func);
*ret = l;
return k;
}
_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) {
assert_return(lldp, -EINVAL);
assert_return(m > 0, -EINVAL);
lldp->neighbors_max = m;
lldp_make_space(lldp, 0);
return 0;
}
_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) {
assert_return(lldp, -EINVAL);
assert_return(mask != 0, -EINVAL);
lldp->capability_mask = mask;
return 0;
}
_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) {
assert_return(lldp, -EINVAL);
/* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
* that our own can be filtered out here. */
if (addr)
lldp->filter_address = *addr;
else
zero(lldp->filter_address);
return 0;
}

View file

@ -7,8 +7,8 @@
#include "sd-event.h"
#include "fs-util.h"
#include "hashmap.h"
#include "inotify-util.h"
#include "list.h"
#include "prioq.h"
#include "ratelimit.h"

View file

@ -915,7 +915,7 @@ static void source_disconnect(sd_event_source *s) {
}
default:
assert_not_reached("Wut? I shouldn't exist.");
assert_not_reached();
}
if (s->pending)
@ -2407,7 +2407,7 @@ static int event_source_offline(
break;
default:
assert_not_reached("Wut? I shouldn't exist.");
assert_not_reached();
}
/* Always reshuffle time prioq, as the ratelimited flag may be changed. */
@ -2495,7 +2495,7 @@ static int event_source_online(
break;
default:
assert_not_reached("Wut? I shouldn't exist.");
assert_not_reached();
}
s->enabled = enabled;
@ -3569,7 +3569,7 @@ static int source_dispatch(sd_event_source *s) {
case SOURCE_WATCHDOG:
case _SOURCE_EVENT_SOURCE_TYPE_MAX:
case _SOURCE_EVENT_SOURCE_TYPE_INVALID:
assert_not_reached("Wut? I shouldn't exist.");
assert_not_reached();
}
s->dispatching = false;
@ -3929,7 +3929,7 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t
break;
default:
assert_not_reached("Unexpected event source type");
assert_not_reached();
}
break;
@ -3953,7 +3953,7 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t
break;
default:
assert_not_reached("Invalid wake-up pointer");
assert_not_reached();
}
}
if (r < 0)
@ -4154,7 +4154,7 @@ _public_ int sd_event_loop(sd_event *e) {
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
_unused_ _cleanup_(sd_event_unrefp) sd_event *ref = NULL;
_unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
while (e->state != SD_EVENT_FINISHED) {
r = sd_event_run(e, UINT64_MAX);

View file

@ -5,12 +5,12 @@
#include <unistd.h>
#include "fd-util.h"
#include "fs-util.h"
#include "hexdecoct.h"
#include "id128-util.h"
#include "io-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "sync-util.h"
char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]) {
unsigned n, k = 0;
@ -167,10 +167,7 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
return r;
if (do_sync) {
if (fsync(fd) < 0)
return -errno;
r = fsync_directory_of_file(fd);
r = fsync_full(fd);
if (r < 0)
return r;
}

View file

@ -12,6 +12,8 @@
char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]);
#define ID128_TO_UUID_STRING(id) id128_to_uuid_string((id), (char[ID128_UUID_STRING_MAX]) {})
bool id128_is_valid(const char *s) _pure_;
typedef enum Id128Format {

View file

@ -135,7 +135,7 @@ int sd_dhcp_client_set_ifindex(
int sd_dhcp_client_set_ifname(
sd_dhcp_client *client,
const char *interface_name);
const char *sd_dhcp_client_get_ifname(sd_dhcp_client *client);
int sd_dhcp_client_get_ifname(sd_dhcp_client *client, const char **ret);
int sd_dhcp_client_set_mac(
sd_dhcp_client *client,
const uint8_t *addr,

View file

@ -94,7 +94,7 @@ int sd_dhcp6_client_set_ifindex(
int sd_dhcp6_client_set_ifname(
sd_dhcp6_client *client,
const char *interface_name);
const char * sd_dhcp6_client_get_ifname(sd_dhcp6_client *client);
int sd_dhcp6_client_get_ifname(sd_dhcp6_client *client, const char **ret);
int sd_dhcp6_client_set_local_address(
sd_dhcp6_client *client,
const struct in6_addr *local_address);

View file

@ -39,11 +39,11 @@ 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, 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, const struct in6_addr **addrs);
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn);
int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn);
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret);
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret);
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret);
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret);
int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret);
sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);

View file

@ -34,11 +34,13 @@ union sd_id128 {
uint64_t qwords[2];
};
#define SD_ID128_STRING_MAX 33
#define SD_ID128_STRING_MAX 33U
char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]);
int sd_id128_from_string(const char *s, sd_id128_t *ret);
#define SD_ID128_TO_STRING(id) sd_id128_to_string((id), (char[SD_ID128_STRING_MAX]) {})
int sd_id128_randomize(sd_id128_t *ret);
int sd_id128_get_machine(sd_id128_t *ret);

View file

@ -47,7 +47,7 @@ int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr);
int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int interface_index);
int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd);
int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *interface_name);
const char *sd_ipv4acd_get_ifname(sd_ipv4acd *acd);
int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret);
int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address);
int sd_ipv4acd_is_running(sd_ipv4acd *acd);
int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts);

View file

@ -47,7 +47,7 @@ int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int interface_index);
int sd_ipv4ll_get_ifindex(sd_ipv4ll *ll);
int sd_ipv4ll_set_ifname(sd_ipv4ll *ll, const char *interface_name);
const char *sd_ipv4ll_get_ifname(sd_ipv4ll *ll);
int sd_ipv4ll_get_ifname(sd_ipv4ll *ll, const char **ret);
int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address);
int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed);
int sd_ipv4ll_is_running(sd_ipv4ll *ll);

View file

@ -0,0 +1,109 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef foosdlldprxhfoo
#define foosdlldprxhfoo
/***
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 <errno.h>
#include <inttypes.h>
#include <net/ethernet.h>
#include <sys/types.h>
#include "sd-event.h"
#include "sd-lldp.h"
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
typedef struct sd_lldp_rx sd_lldp_rx;
typedef struct sd_lldp_neighbor sd_lldp_neighbor;
typedef enum sd_lldp_rx_event_t {
SD_LLDP_RX_EVENT_ADDED,
SD_LLDP_RX_EVENT_REMOVED,
SD_LLDP_RX_EVENT_UPDATED,
SD_LLDP_RX_EVENT_REFRESHED,
_SD_LLDP_RX_EVENT_MAX,
_SD_LLDP_RX_EVENT_INVALID = -EINVAL,
_SD_ENUM_FORCE_S64(LLDP_RX_EVENT),
} sd_lldp_rx_event_t;
typedef void (*sd_lldp_rx_callback_t)(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n, void *userdata);
int sd_lldp_rx_new(sd_lldp_rx **ret);
sd_lldp_rx *sd_lldp_rx_ref(sd_lldp_rx *lldp_rx);
sd_lldp_rx *sd_lldp_rx_unref(sd_lldp_rx *lldp_rx);
int sd_lldp_rx_start(sd_lldp_rx *lldp_rx);
int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx);
int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx);
int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority);
int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx);
sd_event *sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx);
int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata);
int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex);
int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname);
int sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx, const char **ret);
/* Controls how much and what to store in the neighbors database */
int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t n);
int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask);
int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *address);
int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***neighbors);
int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size);
sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n);
sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n);
/* Access to LLDP frame metadata */
int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address);
int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address);
int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret);
int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */
int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size);
int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret);
int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size);
int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret);
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);
/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs
* (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */
int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n);
int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n);
int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type);
int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type);
int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype);
int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype);
int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_rx, sd_lldp_rx_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref);
_SD_END_DECLARATIONS;
#endif

View file

@ -17,12 +17,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <inttypes.h>
#include <net/ethernet.h>
#include <sys/types.h>
#include "sd-event.h"
#include "_sd-common.h"
@ -85,20 +80,24 @@ enum {
#define SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \
((uint16_t) \
(SD_LLDP_SYSTEM_CAPABILITIES_REPEATER| \
SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE| \
SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP| \
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER| \
SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS| \
SD_LLDP_SYSTEM_CAPABILITIES_CVLAN| \
SD_LLDP_SYSTEM_CAPABILITIES_SVLAN| \
(SD_LLDP_SYSTEM_CAPABILITIES_REPEATER | \
SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE | \
SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP | \
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER | \
SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS | \
SD_LLDP_SYSTEM_CAPABILITIES_CVLAN | \
SD_LLDP_SYSTEM_CAPABILITIES_SVLAN | \
SD_LLDP_SYSTEM_CAPABILITIES_TPMR))
#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_802_1 (const uint8_t[]) { 0x00, 0x80, 0xc2 }
#define SD_LLDP_OUI_802_3 (const uint8_t[]) { 0x00, 0x12, 0x0f }
#define SD_LLDP_OUI_MUD (uint8_t[]) { 0x00, 0x00, 0x5E }
#define SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION 0x01
#define _SD_LLDP_OUI_IANA 0x00, 0x00, 0x5E
#define SD_LLDP_OUI_IANA (const uint8_t[]) { _SD_LLDP_OUI_IANA }
#define SD_LLDP_OUI_IANA_SUBTYPE_MUD 0x01
#define SD_LLDP_OUI_IANA_MUD \
(const uint8_t[]) { _SD_LLDP_OUI_IANA, SD_LLDP_OUI_IANA_SUBTYPE_MUD }
/* IEEE 802.1AB-2009 Annex E */
enum {
@ -119,80 +118,6 @@ enum {
SD_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE = 4,
};
typedef struct sd_lldp sd_lldp;
typedef struct sd_lldp_neighbor sd_lldp_neighbor;
typedef enum sd_lldp_event_t {
SD_LLDP_EVENT_ADDED,
SD_LLDP_EVENT_REMOVED,
SD_LLDP_EVENT_UPDATED,
SD_LLDP_EVENT_REFRESHED,
_SD_LLDP_EVENT_MAX,
_SD_LLDP_EVENT_INVALID = -EINVAL,
_SD_ENUM_FORCE_S64(LLDP_EVENT),
} sd_lldp_event_t;
typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n, void *userdata);
int sd_lldp_new(sd_lldp **ret);
sd_lldp* sd_lldp_ref(sd_lldp *lldp);
sd_lldp* sd_lldp_unref(sd_lldp *lldp);
int sd_lldp_start(sd_lldp *lldp);
int sd_lldp_stop(sd_lldp *lldp);
int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority);
int sd_lldp_detach_event(sd_lldp *lldp);
sd_event *sd_lldp_get_event(sd_lldp *lldp);
int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata);
int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex);
int sd_lldp_set_ifname(sd_lldp *lldp, const char *ifname);
const char *sd_lldp_get_ifname(sd_lldp *lldp);
/* Controls how much and what to store in the neighbors database */
int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t n);
int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask);
int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *address);
int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***neighbors);
int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size);
sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n);
sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n);
/* Access to LLDP frame metadata */
int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address);
int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address);
int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret);
int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */
int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size);
int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret);
int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size);
int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret);
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);
/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs
* (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */
int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n);
int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n);
int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type);
int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type);
int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype);
int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype);
int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp, sd_lldp_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref);
_SD_END_DECLARATIONS;
#endif

View file

@ -79,7 +79,7 @@ sd_event *sd_ndisc_get_event(sd_ndisc *nd);
int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t cb, void *userdata);
int sd_ndisc_set_ifindex(sd_ndisc *nd, int interface_index);
int sd_ndisc_set_ifname(sd_ndisc *nd, const char *interface_name);
const char *sd_ndisc_get_ifname(sd_ndisc *nd);
int sd_ndisc_get_ifname(sd_ndisc *nd, const char **ret);
int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *ret);

View file

@ -44,16 +44,19 @@ typedef void (*free_func_t)(void *p);
#define malloc0(n) (calloc(1, (n) ?: 1))
static inline void *mfree(void *memory) {
free(memory);
return NULL;
}
#define mfree(memory) \
({ \
free(memory); \
(typeof(memory)) NULL; \
})
#define free_and_replace(a, b) \
({ \
free(a); \
(a) = (b); \
(b) = NULL; \
typeof(a)* _a = &(a); \
typeof(b)* _b = &(b); \
free(*_a); \
(*_a) = (*_b); \
(*_b) = NULL; \
0; \
})

View file

@ -32,6 +32,7 @@ typedef enum CGroupController {
CGROUP_CONTROLLER_BPF_DEVICES,
CGROUP_CONTROLLER_BPF_FOREIGN,
CGROUP_CONTROLLER_BPF_SOCKET_BIND,
CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES,
_CGROUP_CONTROLLER_MAX,
_CGROUP_CONTROLLER_INVALID = -EINVAL,
@ -53,6 +54,7 @@ typedef enum CGroupMask {
CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES),
CGROUP_MASK_BPF_FOREIGN = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FOREIGN),
CGROUP_MASK_BPF_SOCKET_BIND = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_SOCKET_BIND),
CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES),
/* All real cgroup v1 controllers */
CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS,
@ -61,7 +63,7 @@ typedef enum CGroupMask {
CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
/* All cgroup v2 BPF pseudo-controllers */
CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND,
CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND|CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES,
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
} CGroupMask;
@ -172,6 +174,7 @@ typedef enum CGroupFlags {
typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata);
int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
int cg_kill_kernel_sigkill(const char *controller, const char *path);
int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
int cg_split_spec(const char *spec, char **ret_controller, char **ret_path);
@ -272,6 +275,7 @@ int cg_kernel_controllers(Set **controllers);
bool cg_ns_supported(void);
bool cg_freezer_supported(void);
bool cg_kill_supported(void);
int cg_all_unified(void);
int cg_hybrid_unified(void);

View file

@ -183,39 +183,51 @@ static int env_append(char **r, char ***k, char **a) {
return 0;
}
char **strv_env_merge(size_t n_lists, ...) {
_cleanup_strv_free_ char **ret = NULL;
size_t n = 0;
char **l, **k;
char** _strv_env_merge(char **first, ...) {
_cleanup_strv_free_ char **merged = NULL;
char **k;
va_list ap;
/* Merges an arbitrary number of environment sets */
va_start(ap, n_lists);
for (size_t i = 0; i < n_lists; i++) {
size_t n = strv_length(first);
va_start(ap, first);
for (;;) {
char **l;
l = va_arg(ap, char**);
if (l == POINTER_MAX)
break;
n += strv_length(l);
}
va_end(ap);
ret = new(char*, n+1);
if (!ret)
k = merged = new(char*, n + 1);
if (!merged)
return NULL;
merged[0] = NULL;
if (env_append(merged, &k, first) < 0)
return NULL;
*ret = NULL;
k = ret;
va_start(ap, first);
for (;;) {
char **l;
va_start(ap, n_lists);
for (size_t i = 0; i < n_lists; i++) {
l = va_arg(ap, char**);
if (env_append(ret, &k, l) < 0) {
if (l == POINTER_MAX)
break;
if (env_append(merged, &k, l) < 0) {
va_end(ap);
return NULL;
}
}
va_end(ap);
return TAKE_PTR(ret);
return TAKE_PTR(merged);
}
static bool env_match(const char *t, const char *pattern) {
@ -409,6 +421,32 @@ int strv_env_replace_strdup(char ***l, const char *assignment) {
return strv_env_replace_consume(l, p);
}
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment) {
/* Like strv_env_replace_strdup(), but pulls the variable from the environment of
* the calling program, if a variable name without value is specified.
*/
char *p;
if (strchr(assignment, '=')) {
if (!env_assignment_is_valid(assignment))
return -EINVAL;
p = strdup(assignment);
} else {
if (!env_name_is_valid(assignment))
return -EINVAL;
/* If we can't find the variable in our environment, we will use
* the empty string. This way "passthrough" is equivalent to passing
* --setenv=FOO=$FOO in the shell. */
p = strjoin(assignment, "=", secure_getenv(assignment));
}
if (!p)
return -ENOMEM;
return strv_env_replace_consume(l, p);
}
int strv_env_assign(char ***l, const char *key, const char *value) {
if (!env_name_is_valid(key))
return -EINVAL;
@ -577,6 +615,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
word = e+1;
state = WORD;
nest--;
} else if (*e == ':') {
if (flags & REPLACE_ENV_ALLOW_EXTENDED) {
len = e - word - 2;
@ -832,3 +871,20 @@ int getenv_path_list(const char *name, char ***ret_paths) {
*ret_paths = TAKE_PTR(l);
return 1;
}
int unsetenv_erase(const char *name) {
char *p;
assert(name);
p = getenv(name);
if (!p)
return 0;
string_erase(p);
if (unsetenv(name) < 0)
return -errno;
return 1;
}

View file

@ -39,13 +39,15 @@ char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const cha
bool strv_env_name_is_valid(char **l);
bool strv_env_name_or_assignment_is_valid(char **l);
char **strv_env_merge(size_t n_lists, ...);
char** _strv_env_merge(char **first, ...);
#define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX)
char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
char **strv_env_unset(char **l, const char *p); /* In place ... */
char **strv_env_unset_many(char **l, ...) _sentinel_;
int strv_env_replace_consume(char ***l, char *p); /* In place ... */
int strv_env_replace_strdup(char ***l, const char *assignment);
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment);
int strv_env_assign(char ***l, const char *key, const char *value);
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
@ -66,3 +68,5 @@ int setenv_systemd_exec_pid(bool update_only);
/* Parses and does sanity checks on an environment variable containing
* PATH-like colon-separated absolute paths */
int getenv_path_list(const char *name, char ***ret_paths);
int unsetenv_erase(const char *name);

View file

@ -433,11 +433,9 @@ bool fdname_is_valid(const char *s) {
}
int fd_get_path(int fd, char **ret) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
int r;
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
r = readlink_malloc(procfs_path, ret);
r = readlink_malloc(FORMAT_PROC_FD_PATH(fd), ret);
if (r == -ENOENT) {
/* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make
* things debuggable and distinguish the two. */
@ -647,7 +645,6 @@ finish:
}
int fd_reopen(int fd, int flags) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
int new_fd;
/* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
@ -657,8 +654,7 @@ int fd_reopen(int fd, int flags) {
*
* This implicitly resets the file read index to 0. */
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
new_fd = open(procfs_path, flags);
new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
if (new_fd < 0) {
if (errno != ENOENT)
return -errno;

View file

@ -7,6 +7,7 @@
#include <sys/socket.h>
#include "macro.h"
#include "stdio-util.h"
/* maximum length of fdname */
#define FDNAME_MAX 255
@ -104,7 +105,20 @@ static inline int make_null_stdio(void) {
0; \
})
int fd_reopen(int fd, int flags);
int read_nr_open(void);
int btrfs_defrag_fd(int fd);
/* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
#define PROC_FD_PATH_MAX \
(STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int))
static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int fd) {
assert(buf);
assert(fd >= 0);
assert_se(snprintf_ok(buf, PROC_FD_PATH_MAX, "/proc/self/fd/%i", fd));
return buf;
}
#define FORMAT_PROC_FD_PATH(fd) \
format_proc_fd_path((char[PROC_FD_PATH_MAX]) {}, (fd))

View file

@ -13,6 +13,7 @@
#include <unistd.h>
#include "alloc-util.h"
#include "chase-symlinks.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
@ -25,19 +26,22 @@
#include "socket-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "sync-util.h"
#include "tmpfile-util.h"
/* The maximum size of the file we'll read in one go in read_full_file() (64M). */
#define READ_FULL_BYTES_MAX (64U*1024U*1024U - 1U)
/* The maximum size of virtual files we'll read in one go in read_virtual_file() (4M). Note that this limit
* is different (and much lower) than the READ_FULL_BYTES_MAX limit. This reflects the fact that we use
* different strategies for reading virtual and regular files: virtual files are generally size constrained:
* there we allocate the full buffer size in advance. Regular files OTOH can be much larger, and here we grow
* the allocations exponentially in a loop. In glibc large allocations are immediately backed by mmap()
* making them relatively slow (measurably so). Thus, when allocating the full buffer in advance the large
* limit is a problem. When allocating piecemeal it's not. Hence pick two distinct limits. */
#define READ_VIRTUAL_BYTES_MAX (4U*1024U*1024U - 1U)
/* The maximum size of virtual files (i.e. procfs, sysfs, and other virtual "API" files) we'll read in one go
* in read_virtual_file(). Note that this limit is different (and much lower) than the READ_FULL_BYTES_MAX
* limit. This reflects the fact that we use different strategies for reading virtual and regular files:
* virtual files we generally have to read in a single read() syscall since the kernel doesn't support
* continuation read()s for them. Thankfully they are somewhat size constrained. Thus we can allocate the
* full potential buffer in advance. Regular files OTOH can be much larger, and there we grow the allocations
* exponentially in a loop. We use a size limit of 4M-2 because 4M-1 is the maximum buffer that /proc/sys/
* allows us to read() (larger reads will fail with ENOMEM), and we want to read one extra byte so that we
* can detect EOFs. */
#define READ_VIRTUAL_BYTES_MAX (4U*1024U*1024U - 2U)
int fopen_unlocked(const char *path, const char *options, FILE **ret) {
assert(ret);
@ -146,6 +150,30 @@ int write_string_stream_ts(
return -EBADF;
}
if (flags & WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) {
_cleanup_free_ char *t = NULL;
/* If value to be written is same as that of the existing value, then suppress the write. */
if (fd < 0) {
fd = fileno(f);
if (fd < 0)
return -EBADF;
}
/* Read an additional byte to detect cases where the prefix matches but the rest
* doesn't. Also, 0 returned by read_virtual_file_fd() means the read was truncated and
* it won't be equal to the new value. */
if (read_virtual_file_fd(fd, strlen(line)+1, &t, NULL) > 0 &&
streq_skip_trailing_chars(line, t, NEWLINE)) {
log_debug("No change in value '%s', supressing write", line);
return 0;
}
if (lseek(fd, 0, SEEK_SET) < 0)
return -errno;
}
needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
@ -261,10 +289,11 @@ int write_string_file_ts(
assert(!ts);
/* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY |
fd = open(fn, O_CLOEXEC|O_NOCTTY |
(FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0),
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
(FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
if (fd < 0) {
r = -errno;
@ -373,9 +402,8 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
return 1;
}
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size) {
_cleanup_free_ char *buf = NULL;
_cleanup_close_ int fd = -1;
size_t n, size;
int n_retries;
bool truncated = false;
@ -393,10 +421,7 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
* contents* may be returned. (Though the read is still done using one syscall.) Returns 0 on
* partial success, 1 if untruncated contents were read. */
fd = open(filename, O_RDONLY|O_CLOEXEC);
if (fd < 0)
return -errno;
assert(fd >= 0);
assert(max_size <= READ_VIRTUAL_BYTES_MAX || max_size == SIZE_MAX);
/* Limit the number of attempts to read the number of bytes returned by fstat(). */
@ -431,6 +456,11 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
}
n_retries--;
} else if (n_retries > 1) {
/* Files in /proc are generally smaller than the page size so let's start with
* a page size buffer from malloc and only use the max buffer on the final try. */
size = MIN3(page_size() - 1, READ_VIRTUAL_BYTES_MAX, max_size);
n_retries = 1;
} else {
size = MIN(READ_VIRTUAL_BYTES_MAX, max_size);
n_retries = 0;
@ -463,9 +493,14 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
if (n <= size)
break;
/* If a maximum size is specified and we already read as much, no need to try again */
if (max_size != SIZE_MAX && n >= max_size) {
n = max_size;
/* If a maximum size is specified and we already read more we know the file is larger, and
* can handle this as truncation case. Note that if the size of what we read equals the
* maximum size then this doesn't mean truncation, the file might or might not end on that
* byte. We need to rerun the loop in that case, with a larger buffer size, so that we read
* at least one more byte to be able to distinguish EOF from truncation. */
if (max_size != SIZE_MAX && n > max_size) {
n = size; /* Make sure we never use more than what we sized the buffer for (so that
* we have one free byte in it for the trailing NUL we add below).*/
truncated = true;
break;
}
@ -512,6 +547,18 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
return !truncated;
}
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
_cleanup_close_ int fd = -1;
assert(filename);
fd = open(filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
if (fd < 0)
return -errno;
return read_virtual_file_fd(fd, max_size, ret_contents, ret_size);
}
int read_full_stream_full(
FILE *f,
const char *filename,
@ -721,8 +768,6 @@ int read_full_file_full(
if (dir_fd == AT_FDCWD)
r = sockaddr_un_set_path(&sa.un, filename);
else {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
/* If we shall operate relative to some directory, then let's use O_PATH first to
* open the socket inode, and then connect to it via /proc/self/fd/. We have to do
* this since there's not connectat() that takes a directory fd as first arg. */
@ -731,8 +776,7 @@ int read_full_file_full(
if (dfd < 0)
return -errno;
xsprintf(procfs_path, "/proc/self/fd/%i", dfd);
r = sockaddr_un_set_path(&sa.un, procfs_path);
r = sockaddr_un_set_path(&sa.un, FORMAT_PROC_FD_PATH(dfd));
}
if (r < 0)
return r;
@ -897,10 +941,12 @@ DIR *xopendirat(int fd, const char *name, int flags) {
return d;
}
static int mode_to_flags(const char *mode) {
int fopen_mode_to_flags(const char *mode) {
const char *p;
int flags;
assert(mode);
if ((p = startswith(mode, "r+")))
flags = O_RDWR;
else if ((p = startswith(mode, "r")))
@ -953,7 +999,7 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r
} else {
int fd, mode_flags;
mode_flags = mode_to_flags(mode);
mode_flags = fopen_mode_to_flags(mode);
if (mode_flags < 0)
return mode_flags;
@ -1093,39 +1139,6 @@ int search_and_fopen_nulstr(
return search_and_fopen_internal(filename, mode, root, s, ret, ret_path);
}
int chase_symlinks_and_fopen_unlocked(
const char *path,
const char *root,
unsigned chase_flags,
const char *open_flags,
FILE **ret_file,
char **ret_path) {
_cleanup_close_ int fd = -1;
_cleanup_free_ char *final_path = NULL;
int mode_flags, r;
assert(path);
assert(open_flags);
assert(ret_file);
mode_flags = mode_to_flags(open_flags);
if (mode_flags < 0)
return mode_flags;
fd = chase_symlinks_and_open(path, root, chase_flags, mode_flags, ret_path ? &final_path : NULL);
if (fd < 0)
return fd;
r = take_fdopen_unlocked(&fd, open_flags, ret_file);
if (r < 0)
return r;
if (ret_path)
*ret_path = TAKE_PTR(final_path);
return 0;
}
int fflush_and_check(FILE *f) {
assert(f);
@ -1153,10 +1166,7 @@ int fflush_sync_and_check(FILE *f) {
if (fd < 0)
return 0;
if (fsync(fd) < 0)
return -errno;
r = fsync_directory_of_file(fd);
r = fsync_full(fd);
if (r < 0)
return r;
@ -1425,16 +1435,3 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c
filename, st->st_mode & 07777);
return 0;
}
int rename_and_apply_smack_floor_label(const char *from, const char *to) {
int r = 0;
if (rename(from, to) < 0)
return -errno;
#if HAVE_SMACK_RUN_LABEL
r = mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL);
if (r < 0)
return r;
#endif
return r;
}

View file

@ -2,11 +2,11 @@
#pragma once
#include <dirent.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include "macro.h"
@ -15,17 +15,18 @@
#define LONG_LINE_MAX (1U*1024U*1024U)
typedef enum {
WRITE_STRING_FILE_CREATE = 1 << 0,
WRITE_STRING_FILE_TRUNCATE = 1 << 1,
WRITE_STRING_FILE_ATOMIC = 1 << 2,
WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3,
WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4,
WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE = 1 << 5,
WRITE_STRING_FILE_SYNC = 1 << 6,
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 7,
WRITE_STRING_FILE_NOFOLLOW = 1 << 8,
WRITE_STRING_FILE_MKDIR_0755 = 1 << 9,
WRITE_STRING_FILE_MODE_0600 = 1 << 10,
WRITE_STRING_FILE_CREATE = 1 << 0,
WRITE_STRING_FILE_TRUNCATE = 1 << 1,
WRITE_STRING_FILE_ATOMIC = 1 << 2,
WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3,
WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4,
WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE = 1 << 5,
WRITE_STRING_FILE_SYNC = 1 << 6,
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 7,
WRITE_STRING_FILE_NOFOLLOW = 1 << 8,
WRITE_STRING_FILE_MKDIR_0755 = 1 << 9,
WRITE_STRING_FILE_MODE_0600 = 1 << 10,
WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 11,
/* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
@ -67,6 +68,7 @@ static inline int read_full_file(const char *filename, char **ret_contents, size
return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size);
}
int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size);
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
static inline int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) {
return read_virtual_file(filename, SIZE_MAX, ret_contents, ret_size);
@ -89,14 +91,6 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret, char **ret_path);
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret, char **ret_path);
int chase_symlinks_and_fopen_unlocked(
const char *path,
const char *root,
unsigned chase_flags,
const char *open_flags,
FILE **ret_file,
char **ret_path);
int fflush_and_check(FILE *f);
int fflush_sync_and_check(FILE *f);
@ -125,4 +119,4 @@ 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 rename_and_apply_smack_floor_label(const char *temp_path, const char *dest_path);
int fopen_mode_to_flags(const char *mode);

View file

@ -3,23 +3,43 @@
#include "format-util.h"
#include "memory-util.h"
#include "stdio-util.h"
#include "strxcpyx.h"
assert_cc(STRLEN("%") + DECIMAL_STR_MAX(int) <= IF_NAMESIZE);
int format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]) {
if (ifindex <= 0)
return -EINVAL;
assert_cc(DECIMAL_STR_MAX(int) + 1 <= IF_NAMESIZE + 1);
char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag) {
/* Buffer is always cleared */
memzero(buf, IF_NAMESIZE + 1);
if (if_indextoname(ifindex, buf))
return buf;
return 0;
if (!FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX))
return NULL;
return -errno;
if (FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX_WITH_PERCENT))
snprintf(buf, IF_NAMESIZE + 1, "%%%d", ifindex);
assert(snprintf_ok(buf, IF_NAMESIZE, "%%%d", ifindex));
else
snprintf(buf, IF_NAMESIZE + 1, "%d", ifindex);
assert(snprintf_ok(buf, IF_NAMESIZE, "%d", ifindex));
return buf;
return 0;
}
int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret) {
char buf[IF_NAMESIZE], *copy;
int r;
assert(ret);
r = format_ifname_full(ifindex, flag, buf);
if (r < 0)
return r;
copy = strdup(buf);
if (!copy)
return -ENOMEM;
*ret = copy;
return 0;
}
char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
@ -56,23 +76,23 @@ char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
for (size_t i = 0; i < n; i++)
if (t >= table[i].factor) {
if (flag & FORMAT_BYTES_BELOW_POINT) {
snprintf(buf, l,
"%" PRIu64 ".%" PRIu64 "%s",
t / table[i].factor,
i != n - 1 ?
(t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
(t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
table[i].suffix);
(void) snprintf(buf, l,
"%" PRIu64 ".%" PRIu64 "%s",
t / table[i].factor,
i != n - 1 ?
(t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
(t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
table[i].suffix);
} else
snprintf(buf, l,
"%" PRIu64 "%s",
t / table[i].factor,
table[i].suffix);
(void) snprintf(buf, l,
"%" PRIu64 "%s",
t / table[i].factor,
table[i].suffix);
goto finish;
}
snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : "");
(void) snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : "");
finish:
buf[l-1] = 0;

View file

@ -61,10 +61,23 @@ typedef enum {
FORMAT_IFNAME_IFINDEX_WITH_PERCENT = (1 << 1) | FORMAT_IFNAME_IFINDEX,
} FormatIfnameFlag;
char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag);
static inline char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) {
return format_ifname_full(ifindex, buf, 0);
int format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]);
int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret);
static inline int format_ifname(int ifindex, char buf[static IF_NAMESIZE]) {
return format_ifname_full(ifindex, 0, buf);
}
static inline int format_ifname_alloc(int ifindex, char **ret) {
return format_ifname_full_alloc(ifindex, 0, ret);
}
static inline char *_format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]) {
(void) format_ifname_full(ifindex, flag, buf);
return buf;
}
#define FORMAT_IFNAME_FULL(index, flag) _format_ifname_full(index, flag, (char[IF_NAMESIZE]){})
#define FORMAT_IFNAME(index) _format_ifname_full(index, 0, (char[IF_NAMESIZE]){})
typedef enum {
FORMAT_BYTES_USE_IEC = 1 << 0,

View file

@ -12,7 +12,6 @@
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "locale-util.h"
#include "log.h"
#include "macro.h"
#include "missing_fcntl.h"
@ -45,50 +44,39 @@ int unlink_noerrno(const char *path) {
}
int rmdir_parents(const char *path, const char *stop) {
size_t l;
int r = 0;
char *p;
int r;
assert(path);
assert(stop);
l = strlen(path);
if (!path_is_safe(path))
return -EINVAL;
/* Skip trailing slashes */
while (l > 0 && path[l-1] == '/')
l--;
if (!path_is_safe(stop))
return -EINVAL;
while (l > 0) {
char *t;
p = strdupa(path);
/* Skip last component */
while (l > 0 && path[l-1] != '/')
l--;
for (;;) {
char *slash = NULL;
/* Skip trailing slashes */
while (l > 0 && path[l-1] == '/')
l--;
if (l <= 0)
break;
t = strndup(path, l);
if (!t)
return -ENOMEM;
if (path_startswith(stop, t)) {
free(t);
/* skip the last component. */
r = path_find_last_component(p, /* accept_dot_dot= */ false, (const char **) &slash, NULL);
if (r <= 0)
return r;
if (slash == p)
return 0;
}
r = rmdir(t);
free(t);
assert(*slash == '/');
*slash = '\0';
if (r < 0)
if (errno != ENOENT)
return -errno;
if (path_startswith_full(stop, p, /* accept_dot_dot= */ false))
return 0;
if (rmdir(p) < 0 && errno != ENOENT)
return -errno;
}
return 0;
}
int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
@ -308,14 +296,11 @@ int fchmod_umask(int fd, mode_t m) {
}
int fchmod_opath(int fd, mode_t m) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
/* This function operates also on fd that might have been opened with
* O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like
* fchownat() does. */
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
if (chmod(procfs_path, m) < 0) {
if (chmod(FORMAT_PROC_FD_PATH(fd), m) < 0) {
if (errno != ENOENT)
return -errno;
@ -329,12 +314,9 @@ int fchmod_opath(int fd, mode_t m) {
}
int futimens_opath(int fd, const struct timespec ts[2]) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
/* Similar to fchmod_path() but for futimens() */
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
if (utimensat(AT_FDCWD, procfs_path, ts, 0) < 0) {
if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, 0) < 0) {
if (errno != ENOENT)
return -errno;
@ -380,9 +362,8 @@ int fd_warn_permissions(const char *path, int fd) {
}
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_close_ int fd = -1;
int r, ret = 0;
int r, ret;
assert(path);
@ -412,8 +393,6 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
/* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
* ownership and time of the file node in all cases, even if the fd refers to an O_PATH object which is
* something fchown(), fchmod(), futimensat() don't allow. */
xsprintf(fdpath, "/proc/self/fd/%i", fd);
ret = fchmod_and_chown(fd, mode, uid, gid);
if (stamp != USEC_INFINITY) {
@ -421,11 +400,11 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
timespec_store(&ts[0], stamp);
ts[1] = ts[0];
r = utimensat(AT_FDCWD, fdpath, ts, 0);
r = futimens_opath(fd, ts);
} else
r = utimensat(AT_FDCWD, fdpath, NULL, 0);
r = futimens_opath(fd, NULL);
if (r < 0 && ret >= 0)
return -errno;
return r;
return ret;
}
@ -702,547 +681,10 @@ int unlink_or_warn(const char *filename) {
return 0;
}
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
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);
wd = inotify_add_watch(fd, path, mask);
if (wd < 0)
return -errno;
return wd;
}
int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) {
int wd;
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 wd;
}
bool unsafe_transition(const struct stat *a, const struct stat *b) {
/* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
* privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
* making us believe we read something safe even though it isn't safe in the specific context we open it in. */
if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
return false;
return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */
}
static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) {
_cleanup_free_ char *n1 = NULL, *n2 = NULL, *user_a = NULL, *user_b = NULL;
struct stat st;
if (!FLAGS_SET(flags, CHASE_WARN))
return -ENOLINK;
(void) fd_get_path(a, &n1);
(void) fd_get_path(b, &n2);
if (fstat(a, &st) == 0)
user_a = uid_to_name(st.st_uid);
if (fstat(b, &st) == 0)
user_b = uid_to_name(st.st_uid);
return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK),
"Detected unsafe path transition %s (owned by %s) %s %s (owned by %s) during canonicalization of %s.",
strna(n1), strna(user_a), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), strna(user_b), path);
}
static int log_autofs_mount_point(int fd, const char *path, unsigned flags) {
_cleanup_free_ char *n1 = NULL;
if (!FLAGS_SET(flags, CHASE_WARN))
return -EREMOTE;
(void) fd_get_path(fd, &n1);
return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE),
"Detected autofs mount point %s during canonicalization of %s.",
strna(n1), path);
}
int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret_path, int *ret_fd) {
_cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
_cleanup_close_ int fd = -1;
unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */
bool exists = true, append_trail_slash = false;
struct stat previous_stat;
const char *todo;
int r;
assert(path);
/* Either the file may be missing, or we return an fd to the final object, but both make no sense */
if ((flags & CHASE_NONEXISTENT) && ret_fd)
return -EINVAL;
if ((flags & CHASE_STEP) && ret_fd)
return -EINVAL;
if (isempty(path))
return -EINVAL;
/* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
* symlinks relative to a root directory, instead of the root of the host.
*
* Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following
* relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is
* assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first
* prefixed accordingly.
*
* Algorithmically this operates on two path buffers: "done" are the components of the path we already
* processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to
* process. On each iteration, we move one component from "todo" to "done", processing it's special meaning
* each time. The "todo" path always starts with at least one slash, the "done" path always ends in no
* slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races
* to a minimum.
*
* Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got
* as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this
* function what to do when encountering a symlink with an absolute path as directory: prefix it by the
* specified path.
*
* There are five ways to invoke this function:
*
* 1. Without CHASE_STEP or ret_fd: in this case the path is resolved and the normalized path is
* returned in `ret_path`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set, 0
* is returned if the file doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set, >= 0 is
* returned if the destination was found, -ENOENT if it wasn't.
*
* 2. With ret_fd: in this case the destination is opened after chasing it as O_PATH and this file
* descriptor is returned as return value. This is useful to open files relative to some root
* directory. Note that the returned O_PATH file descriptors must be converted into a regular one (using
* fd_reopen() or such) before it can be used for reading/writing. ret_fd may not be combined with
* CHASE_NONEXISTENT.
*
* 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first
* symlink or ".." component of the path is resolved, and the resulting path is returned. This is useful if
* a caller wants to trace the path through the file system verbosely. Returns < 0 on error, > 0 if the
* path is fully normalized, and == 0 for each normalization step. This may be combined with
* CHASE_NONEXISTENT, in which case 1 is returned when a component is not found.
*
* 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from
* unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If
* CHASE_WARN is also set, a warning describing the unsafe transition is emitted.
*
* 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, path normalization
* is aborted and -EREMOTE is returned. If CHASE_WARN is also set, a warning showing the path of
* the mount point is emitted.
*/
/* A root directory of "/" or "" is identical to none */
if (empty_or_root(original_root))
original_root = NULL;
if (!original_root && !ret_path && !(flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_STEP)) && ret_fd) {
/* Shortcut the ret_fd case if the caller isn't interested in the actual path and has no root set
* and doesn't care about any of the other special features we provide either. */
r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0));
if (r < 0)
return -errno;
*ret_fd = r;
return 0;
}
if (original_root) {
r = path_make_absolute_cwd(original_root, &root);
if (r < 0)
return r;
/* Simplify the root directory, so that it has no duplicate slashes and nothing at the
* end. While we won't resolve the root path we still simplify it. Note that dropping the
* trailing slash should not change behaviour, since when opening it we specify O_DIRECTORY
* anyway. Moreover at the end of this function after processing everything we'll always turn
* the empty string back to "/". */
delete_trailing_chars(root, "/");
path_simplify(root);
if (flags & CHASE_PREFIX_ROOT) {
/* We don't support relative paths in combination with a root directory */
if (!path_is_absolute(path))
return -EINVAL;
path = prefix_roota(root, path);
}
}
r = path_make_absolute_cwd(path, &buffer);
if (r < 0)
return r;
fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH);
if (fd < 0)
return -errno;
if (flags & CHASE_SAFE)
if (fstat(fd, &previous_stat) < 0)
return -errno;
if (flags & CHASE_TRAIL_SLASH)
append_trail_slash = endswith(buffer, "/") || endswith(buffer, "/.");
if (root) {
/* If we are operating on a root directory, let's take the root directory as it is. */
todo = path_startswith(buffer, root);
if (!todo)
return log_full_errno(flags & CHASE_WARN ? LOG_WARNING : LOG_DEBUG,
SYNTHETIC_ERRNO(ECHRNG),
"Specified path '%s' is outside of specified root directory '%s', refusing to resolve.",
path, root);
done = strdup(root);
} else {
todo = buffer;
done = strdup("/");
}
for (;;) {
_cleanup_free_ char *first = NULL;
_cleanup_close_ int child = -1;
struct stat st;
const char *e;
r = path_find_first_component(&todo, true, &e);
if (r < 0)
return r;
if (r == 0) { /* We reached the end. */
if (append_trail_slash)
if (!strextend(&done, "/"))
return -ENOMEM;
break;
}
first = strndup(e, r);
if (!first)
return -ENOMEM;
/* Two dots? Then chop off the last bit of what we already found out. */
if (path_equal(first, "..")) {
_cleanup_free_ char *parent = NULL;
_cleanup_close_ int fd_parent = -1;
/* If we already are at the top, then going up will not change anything. This is in-line with
* how the kernel handles this. */
if (empty_or_root(done))
continue;
parent = dirname_malloc(done);
if (!parent)
return -ENOMEM;
/* Don't allow this to leave the root dir. */
if (root &&
path_startswith(done, root) &&
!path_startswith(parent, root))
continue;
free_and_replace(done, parent);
if (flags & CHASE_STEP)
goto chased_one;
fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH);
if (fd_parent < 0)
return -errno;
if (flags & CHASE_SAFE) {
if (fstat(fd_parent, &st) < 0)
return -errno;
if (unsafe_transition(&previous_stat, &st))
return log_unsafe_transition(fd, fd_parent, path, flags);
previous_stat = st;
}
safe_close(fd);
fd = TAKE_FD(fd_parent);
continue;
}
/* Otherwise let's see what this is. */
child = openat(fd, first, O_CLOEXEC|O_NOFOLLOW|O_PATH);
if (child < 0) {
if (errno == ENOENT &&
(flags & CHASE_NONEXISTENT) &&
(isempty(todo) || path_is_safe(todo))) {
/* If CHASE_NONEXISTENT is set, and the path does not exist, then
* that's OK, return what we got so far. But don't allow this if the
* remaining path contains "../" or something else weird. */
if (!path_extend(&done, first, todo))
return -ENOMEM;
exists = false;
break;
}
return -errno;
}
if (fstat(child, &st) < 0)
return -errno;
if ((flags & CHASE_SAFE) &&
unsafe_transition(&previous_stat, &st))
return log_unsafe_transition(fd, child, path, flags);
previous_stat = st;
if ((flags & CHASE_NO_AUTOFS) &&
fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
return log_autofs_mount_point(child, path, flags);
if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
_cleanup_free_ char *destination = NULL;
/* This is a symlink, in this case read the destination. But let's make sure we
* don't follow symlinks without bounds. */
if (--max_follow <= 0)
return -ELOOP;
r = readlinkat_malloc(fd, first, &destination);
if (r < 0)
return r;
if (isempty(destination))
return -EINVAL;
if (path_is_absolute(destination)) {
/* An absolute destination. Start the loop from the beginning, but use the root
* directory as base. */
safe_close(fd);
fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH);
if (fd < 0)
return -errno;
if (flags & CHASE_SAFE) {
if (fstat(fd, &st) < 0)
return -errno;
if (unsafe_transition(&previous_stat, &st))
return log_unsafe_transition(child, fd, path, flags);
previous_stat = st;
}
/* Note that we do not revalidate the root, we take it as is. */
r = free_and_strdup(&done, empty_to_root(root));
if (r < 0)
return r;
}
/* Prefix what's left to do with what we just read, and start the loop again, but
* remain in the current directory. */
if (!path_extend(&destination, todo))
return -ENOMEM;
free_and_replace(buffer, destination);
todo = buffer;
if (flags & CHASE_STEP)
goto chased_one;
continue;
}
/* If this is not a symlink, then let's just add the name we read to what we already verified. */
if (!path_extend(&done, first))
return -ENOMEM;
/* And iterate again, but go one directory further down. */
safe_close(fd);
fd = TAKE_FD(child);
}
if (ret_path)
*ret_path = TAKE_PTR(done);
if (ret_fd) {
/* Return the O_PATH fd we currently are looking to the caller. It can translate it to a
* proper fd by opening /proc/self/fd/xyz. */
assert(fd >= 0);
*ret_fd = TAKE_FD(fd);
}
if (flags & CHASE_STEP)
return 1;
return exists;
chased_one:
if (ret_path) {
const char *e;
/* todo may contain slashes at the beginning. */
r = path_find_first_component(&todo, true, &e);
if (r < 0)
return r;
if (r == 0)
*ret_path = TAKE_PTR(done);
else {
char *c;
c = path_join(done, e);
if (!c)
return -ENOMEM;
*ret_path = c;
}
}
return 0;
}
int chase_symlinks_and_open(
const char *path,
const char *root,
unsigned chase_flags,
int open_flags,
char **ret_path) {
_cleanup_close_ int path_fd = -1;
_cleanup_free_ char *p = NULL;
int r;
if (chase_flags & CHASE_NONEXISTENT)
return -EINVAL;
if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
/* Shortcut this call if none of the special features of this call are requested */
r = open(path, open_flags);
if (r < 0)
return -errno;
return r;
}
r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
if (r < 0)
return r;
assert(path_fd >= 0);
r = fd_reopen(path_fd, open_flags);
if (r < 0)
return r;
if (ret_path)
*ret_path = TAKE_PTR(p);
return r;
}
int chase_symlinks_and_opendir(
const char *path,
const char *root,
unsigned chase_flags,
char **ret_path,
DIR **ret_dir) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_close_ int path_fd = -1;
_cleanup_free_ char *p = NULL;
DIR *d;
int r;
if (!ret_dir)
return -EINVAL;
if (chase_flags & CHASE_NONEXISTENT)
return -EINVAL;
if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
/* Shortcut this call if none of the special features of this call are requested */
d = opendir(path);
if (!d)
return -errno;
*ret_dir = d;
return 0;
}
r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
if (r < 0)
return r;
assert(path_fd >= 0);
xsprintf(procfs_path, "/proc/self/fd/%i", path_fd);
d = opendir(procfs_path);
if (!d)
return -errno;
if (ret_path)
*ret_path = TAKE_PTR(p);
*ret_dir = d;
return 0;
}
int chase_symlinks_and_stat(
const char *path,
const char *root,
unsigned chase_flags,
char **ret_path,
struct stat *ret_stat,
int *ret_fd) {
_cleanup_close_ int path_fd = -1;
_cleanup_free_ char *p = NULL;
int r;
assert(path);
assert(ret_stat);
if (chase_flags & CHASE_NONEXISTENT)
return -EINVAL;
if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
/* Shortcut this call if none of the special features of this call are requested */
if (stat(path, ret_stat) < 0)
return -errno;
return 1;
}
r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
if (r < 0)
return r;
assert(path_fd >= 0);
if (fstat(path_fd, ret_stat) < 0)
return -errno;
if (ret_path)
*ret_path = TAKE_PTR(p);
if (ret_fd)
*ret_fd = TAKE_FD(path_fd);
return 1;
}
int access_fd(int fd, int mode) {
char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
/* Like access() but operates on an already open fd */
xsprintf(p, "/proc/self/fd/%i", fd);
if (access(p, mode) < 0) {
if (access(FORMAT_PROC_FD_PATH(fd), mode) < 0) {
if (errno != ENOENT)
return -errno;
@ -1377,172 +819,6 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
return 0;
}
int fsync_directory_of_file(int fd) {
_cleanup_close_ int dfd = -1;
struct stat st;
int r;
assert(fd >= 0);
/* We only reasonably can do this for regular files and directories, or for O_PATH fds, hence check
* for the inode type first */
if (fstat(fd, &st) < 0)
return -errno;
if (S_ISDIR(st.st_mode)) {
dfd = openat(fd, "..", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0);
if (dfd < 0)
return -errno;
} else if (!S_ISREG(st.st_mode)) { /* Regular files are OK regardless if O_PATH or not, for all other
* types check O_PATH flag */
int flags;
flags = fcntl(fd, F_GETFL);
if (flags < 0)
return -errno;
if (!FLAGS_SET(flags, O_PATH)) /* If O_PATH this refers to the inode in the fs, in which case
* we can sensibly do what is requested. Otherwise this refers
* to a socket, fifo or device node, where the concept of a
* containing directory doesn't make too much sense. */
return -ENOTTY;
}
if (dfd < 0) {
_cleanup_free_ char *path = NULL;
r = fd_get_path(fd, &path);
if (r < 0) {
log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m",
fd,
r == -ENOSYS ? ", ignoring" : "");
if (r == -ENOSYS)
/* If /proc is not available, we're most likely running in some
* chroot environment, and syncing the directory is not very
* important in that case. Let's just silently do nothing. */
return 0;
return r;
}
if (!path_is_absolute(path))
return -EINVAL;
dfd = open_parent(path, O_CLOEXEC|O_NOFOLLOW, 0);
if (dfd < 0)
return dfd;
}
if (fsync(dfd) < 0)
return -errno;
return 0;
}
int fsync_full(int fd) {
int r, q;
/* Sync both the file and the directory */
r = fsync(fd) < 0 ? -errno : 0;
q = fsync_directory_of_file(fd);
if (r < 0) /* Return earlier error */
return r;
if (q == -ENOTTY) /* Ignore if the 'fd' refers to a block device or so which doesn't really have a
* parent dir */
return 0;
return q;
}
int fsync_path_at(int at_fd, const char *path) {
_cleanup_close_ int opened_fd = -1;
int fd;
if (isempty(path)) {
if (at_fd == AT_FDCWD) {
opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (opened_fd < 0)
return -errno;
fd = opened_fd;
} else
fd = at_fd;
} else {
opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (opened_fd < 0)
return -errno;
fd = opened_fd;
}
if (fsync(fd) < 0)
return -errno;
return 0;
}
int fsync_parent_at(int at_fd, const char *path) {
_cleanup_close_ int opened_fd = -1;
if (isempty(path)) {
if (at_fd != AT_FDCWD)
return fsync_directory_of_file(at_fd);
opened_fd = open("..", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (opened_fd < 0)
return -errno;
if (fsync(opened_fd) < 0)
return -errno;
return 0;
}
opened_fd = openat(at_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
if (opened_fd < 0)
return -errno;
return fsync_directory_of_file(opened_fd);
}
int fsync_path_and_parent_at(int at_fd, const char *path) {
_cleanup_close_ int opened_fd = -1;
if (isempty(path)) {
if (at_fd != AT_FDCWD)
return fsync_full(at_fd);
opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
} else
opened_fd = openat(at_fd, path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC);
if (opened_fd < 0)
return -errno;
return fsync_full(opened_fd);
}
int syncfs_path(int atfd, const char *path) {
_cleanup_close_ int fd = -1;
if (isempty(path)) {
if (atfd != AT_FDCWD)
return syncfs(atfd) < 0 ? -errno : 0;
fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
} else
fd = openat(atfd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0)
return -errno;
if (syncfs(fd) < 0)
return -errno;
return 0;
}
int open_parent(const char *path, int flags, mode_t mode) {
_cleanup_free_ char *parent = NULL;
int fd, r;

View file

@ -6,7 +6,6 @@
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/inotify.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@ -67,44 +66,6 @@ int var_tmp_dir(const char **ret);
int unlink_or_warn(const char *filename);
#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
for ((e) = &buffer.ev; \
(uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
(e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
union inotify_event_buffer {
struct inotify_event ev;
uint8_t raw[INOTIFY_EVENT_MAX];
};
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask);
enum {
CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */
CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */
CHASE_NO_AUTOFS = 1 << 2, /* Return -EREMOTE if autofs mount point found */
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 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 */
};
bool unsafe_transition(const struct stat *a, const struct stat *b);
/* How many iterations to execute before returning -ELOOP */
#define CHASE_SYMLINKS_MAX 32
int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret_path, int *ret_fd);
int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path);
int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir);
int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd);
/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
static inline char *rmdir_and_free(char *p) {
PROTECT_ERRNO;
@ -137,14 +98,6 @@ typedef enum UnlinkDeallocateFlags {
int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags);
int fsync_directory_of_file(int fd);
int fsync_full(int fd);
int fsync_path_at(int at_fd, const char *path);
int fsync_parent_at(int at_fd, const char *path);
int fsync_path_and_parent_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 conservative_renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);

View file

@ -401,7 +401,7 @@ static struct hashmap_base_entry* bucket_at_virtual(HashmapBase *h, struct swap_
if (idx < _IDX_SWAP_END)
return &bucket_at_swap(swap, idx)->p.b;
assert_not_reached("Invalid index");
assert_not_reached();
}
static dib_raw_t* dib_raw_ptr(HashmapBase *h) {
@ -513,7 +513,7 @@ static void* entry_value(HashmapBase *h, struct hashmap_base_entry *e) {
return (void*) e->key;
default:
assert_not_reached("Unknown hashmap type");
assert_not_reached();
}
}
@ -1747,7 +1747,7 @@ HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS) {
r = set_merge((Set*)copy, (Set*)h);
break;
default:
assert_not_reached("Unknown hashmap type");
assert_not_reached();
}
if (r < 0)

View file

@ -36,66 +36,39 @@ char* get_default_hostname(void) {
return strdup(FALLBACK_HOSTNAME);
}
char* gethostname_malloc(void) {
int gethostname_full(GetHostnameFlags flags, char **ret) {
_cleanup_free_ char *buf = NULL, *fallback = NULL;
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.
* It might even return "localhost" if that's set. */
assert(ret);
assert_se(uname(&u) >= 0);
s = u.nodename;
if (isempty(s) || streq(s, "(none)"))
return get_default_hostname();
if (isempty(s) ||
(!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_NONE) && streq(s, "(none)")) ||
(!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_LOCALHOST) && is_localhost(s)) ||
(FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')) {
if (!FLAGS_SET(flags, GET_HOSTNAME_FALLBACK_DEFAULT))
return -ENXIO;
return strdup(s);
}
char* gethostname_short_malloc(void) {
struct utsname u;
const char *s;
_cleanup_free_ char *f = NULL;
/* 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 = f = get_default_hostname();
s = fallback = get_default_hostname();
if (!s)
return NULL;
return -ENOMEM;
assert(s[0] != '.');
if (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')
return -ENXIO;
}
return strndup(s, strcspn(s, "."));
}
int gethostname_strict(char **ret) {
struct utsname u;
char *k;
/* This call will rather fail than make up a name. It will not return "localhost" either. */
assert_se(uname(&u) >= 0);
if (isempty(u.nodename))
return -ENXIO;
if (streq(u.nodename, "(none)"))
return -ENXIO;
if (is_localhost(u.nodename))
return -ENXIO;
k = strdup(u.nodename);
if (!k)
if (FLAGS_SET(flags, GET_HOSTNAME_SHORT))
buf = strndup(s, strcspn(s, "."));
else
buf = strdup(s);
if (!buf)
return -ENOMEM;
*ret = k;
*ret = TAKE_PTR(buf);
return 0;
}

View file

@ -4,13 +4,41 @@
#include <stdbool.h>
#include <stdio.h>
#include "env-file.h"
#include "macro.h"
#include "strv.h"
typedef enum GetHostnameFlags {
GET_HOSTNAME_ALLOW_NONE = 1 << 0, /* accepts "(none)". */
GET_HOSTNAME_ALLOW_LOCALHOST = 1 << 1, /* accepts "localhost" or friends. */
GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 2, /* use default hostname if no hostname is set. */
GET_HOSTNAME_SHORT = 1 << 3, /* kills the FQDN part if present. */
} GetHostnameFlags;
int gethostname_full(GetHostnameFlags flags, char **ret);
static inline int gethostname_strict(char **ret) {
return gethostname_full(0, ret);
}
static inline char* gethostname_malloc(void) {
char *s;
if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &s) < 0)
return NULL;
return s;
}
static inline char* gethostname_short_malloc(void) {
char *s;
if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT | GET_HOSTNAME_SHORT, &s) < 0)
return NULL;
return s;
}
char* get_default_hostname(void);
char* gethostname_malloc(void);
char* gethostname_short_malloc(void);
int gethostname_strict(char **ret);
bool valid_ldh_char(char c) _const_;
@ -33,3 +61,7 @@ static inline bool is_outbound_hostname(const char *hostname) {
/* This tries to identify the valid syntaxes for the our synthetic "outbound" host. */
return STRCASE_IN_SET(hostname, "_outbound", "_outbound.");
}
static inline int get_pretty_hostname(char **ret) {
return parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", ret);
}

View file

@ -794,7 +794,7 @@ int in_addr_prefix_from_string_auto_internal(
k = 0;
break;
default:
assert_not_reached("Invalid prefixlen mode");
assert_not_reached();
}
if (ret_family)
@ -831,35 +831,6 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct
DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
static void in_addr_prefix_hash_func(const struct in_addr_prefix *a, struct siphash *state) {
assert(a);
assert(state);
siphash24_compress(&a->family, sizeof(a->family), state);
siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
}
static int in_addr_prefix_compare_func(const struct in_addr_prefix *x, const struct in_addr_prefix *y) {
int r;
assert(x);
assert(y);
r = CMP(x->family, y->family);
if (r != 0)
return r;
r = CMP(x->prefixlen, y->prefixlen);
if (r != 0)
return r;
return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
}
DEFINE_HASH_OPS(in_addr_prefix_hash_ops, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func);
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(in_addr_prefix_hash_ops_free, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func, free);
void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
assert(addr);
assert(state);

View file

@ -20,12 +20,6 @@ struct in_addr_data {
union in_addr_union address;
};
struct in_addr_prefix {
int family;
uint8_t prefixlen;
union in_addr_union address;
};
bool in4_addr_is_null(const struct in_addr *a);
static inline bool in4_addr_is_set(const struct in_addr *a) {
return !in4_addr_is_null(a);
@ -124,8 +118,6 @@ void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b);
extern const struct hash_ops in_addr_data_hash_ops;
extern const struct hash_ops in_addr_prefix_hash_ops;
extern const struct hash_ops in_addr_prefix_hash_ops_free;
extern const struct hash_ops in6_addr_hash_ops;
#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u"

View file

@ -0,0 +1,29 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "fd-util.h"
#include "inotify-util.h"
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
int wd;
/* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask);
if (wd < 0)
return -errno;
return wd;
}
int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) {
int wd;
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 wd;
}

View file

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <sys/inotify.h>
#define INOTIFY_EVENT_MAX (offsetof(struct inotify_event, name) + NAME_MAX + 1)
#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
for ((e) = &buffer.ev; \
(uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
(e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
union inotify_event_buffer {
struct inotify_event ev;
uint8_t raw[INOTIFY_EVENT_MAX];
};
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask);

View file

@ -142,11 +142,8 @@
#define LIST_FOREACH_SAFE(name,i,n,head) \
for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
#define LIST_FOREACH_BEFORE(name,i,p) \
for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev)
#define LIST_FOREACH_AFTER(name,i,p) \
for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
#define LIST_FOREACH_BACKWARDS(name,i,p) \
for ((i) = (p); (i); (i) = (i)->name##_prev)
/* Iterate through all the members of the list p is included in, but skip over p */
#define LIST_FOREACH_OTHERS(name,i,p) \

View file

@ -32,6 +32,15 @@ typedef enum LogTarget{
#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1)
#define ERRNO_VALUE(val) (abs(val) & 255)
/* The callback function to be invoked when syntax warnings are seen
* in the unit files. */
typedef void (*log_syntax_callback_t)(const char *unit, int level, void *userdata);
void set_log_syntax_callback(log_syntax_callback_t cb, void *userdata);
static inline void clear_log_syntax_callback(dummy_t *dummy) {
set_log_syntax_callback(/* cb= */ NULL, /* userdata= */ NULL);
}
const char *log_target_to_string(LogTarget target) _const_;
LogTarget log_target_from_string(const char *s) _pure_;
void log_set_target(LogTarget target);
@ -174,7 +183,6 @@ _noreturn_ void log_assert_failed(
const char *func);
_noreturn_ void log_assert_failed_unreachable(
const char *text,
const char *file,
int line,
const char *func);

View file

@ -20,31 +20,14 @@
#define _sentinel_ __attribute__((__sentinel__))
#define _destructor_ __attribute__((__destructor__))
#define _deprecated_ __attribute__((__deprecated__))
#define _packed_ __attribute__((__packed__))
#define _malloc_ __attribute__((__malloc__))
#define _weak_ __attribute__((__weak__))
#define _likely_(x) (__builtin_expect(!!(x), 1))
#define _unlikely_(x) (__builtin_expect(!!(x), 0))
#define _public_ __attribute__((__visibility__("default")))
#define _hidden_ __attribute__((__visibility__("hidden")))
#define _weakref_(x) __attribute__((__weakref__(#x)))
#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
#define _warn_unused_result_ __attribute__((__warn_unused_result__))
#if __GNUC__ >= 7
#define _fallthrough_ __attribute__((__fallthrough__))
#else
#define _fallthrough_
#endif
/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
* compiler versions */
#ifndef _noreturn_
#if __STDC_VERSION__ >= 201112L
#define _noreturn_ _Noreturn
#else
#define _noreturn_ __attribute__((__noreturn__))
#endif
#endif
#if !defined(HAS_FEATURE_MEMORY_SANITIZER)
# if defined(__has_feature)
@ -163,6 +146,20 @@
#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p)))
static inline size_t ALIGN_TO(size_t l, size_t ali) {
/* Check that alignment is exponent of 2 */
#if SIZE_MAX == UINT_MAX
assert(__builtin_popcount(ali) == 1);
#elif SIZE_MAX == ULONG_MAX
assert(__builtin_popcountl(ali) == 1);
#elif SIZE_MAX == ULONGLONG_MAX
assert(__builtin_popcountll(ali) == 1);
#else
#error "Unexpected size_t"
#endif
if (l > SIZE_MAX - (ali - 1))
return SIZE_MAX; /* indicate overflow */
return ((l + ali - 1) & ~(ali - 1));
}
@ -209,13 +206,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
return m;
}
/*
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
* Contrary to strlen(), this is a constant expression.
* @x: a string literal.
*/
#define STRLEN(x) ((unsigned) sizeof(""x"") - 1)
/*
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
@ -282,8 +272,8 @@ static inline int __coverity_check_and_return__(int condition) {
#define assert(expr) assert_message_se(expr, #expr)
#endif
#define assert_not_reached(t) \
log_assert_failed_unreachable(t, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__)
#define assert_not_reached() \
log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__)
#define assert_return(expr, r) \
do { \
@ -345,12 +335,12 @@ static inline int __coverity_check_and_return__(int condition) {
(2U+(sizeof(type) <= 1 ? 3U : \
sizeof(type) <= 2 ? 5U : \
sizeof(type) <= 4 ? 10U : \
sizeof(type) <= 8 ? 20U : (unsigned) sizeof(int[-2*(sizeof(type) > 8)])))
sizeof(type) <= 8 ? 20U : sizeof(int[-2*(sizeof(type) > 8)])))
#define DECIMAL_STR_WIDTH(x) \
({ \
typeof(x) _x_ = (x); \
unsigned ans = 1; \
size_t ans = 1; \
while ((_x_ /= 10) != 0) \
ans++; \
ans; \
@ -483,4 +473,10 @@ static inline size_t size_add(size_t x, size_t y) {
return y >= SIZE_MAX - x ? SIZE_MAX : x + y;
}
typedef struct {
int _empty[0];
} dummy_t;
assert_cc(sizeof(dummy_t) == 0);
#include "log.h"

View file

@ -18,26 +18,25 @@ size_t page_size(void) {
return pgsz;
}
bool memeqzero(const void *data, size_t length) {
/* Does the buffer consist entirely of NULs?
bool memeqbyte(uint8_t byte, const void *data, size_t length) {
/* Does the buffer consist entirely of the same specific byte value?
* Copied from https://github.com/systemd/casync/, copied in turn from
* https://github.com/rustyrussell/ccan/blob/master/ccan/mem/mem.c#L92,
* which is licensed CC-0.
*/
const uint8_t *p = data;
size_t i;
/* Check first 16 bytes manually */
for (i = 0; i < 16; i++, length--) {
for (size_t i = 0; i < 16; i++, length--) {
if (length == 0)
return true;
if (p[i])
if (p[i] != byte)
return false;
}
/* Now we know first 16 bytes are NUL, memcmp with self. */
return memcmp(data, p + i, length) == 0;
/* Now we know first 16 bytes match, memcmp() with self. */
return memcmp(data, p + 16, length) == 0;
}
#if !HAVE_EXPLICIT_BZERO

View file

@ -47,7 +47,9 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2
#define zero(x) (memzero(&(x), sizeof(x)))
bool memeqzero(const void *data, size_t length);
bool memeqbyte(uint8_t byte, const void *data, size_t length);
#define memeqzero(data, length) memeqbyte(0x00, data, length)
#define eqzero(x) memeqzero(x, sizeof(x))
@ -71,6 +73,16 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const
return memmem(haystack, haystacklen, needle, needlelen);
}
static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
const uint8_t *p;
p = memmem_safe(haystack, haystacklen, needle, needlelen);
if (!p)
return NULL;
return (uint8_t*) p + needlelen;
}
#if HAVE_EXPLICIT_BZERO
static inline void* explicit_bzero_safe(void *p, size_t l) {
if (l > 0)

View file

@ -41,6 +41,26 @@ static inline int missing_pivot_root(const char *new_root, const char *put_old)
/* ======================================================================= */
#if !HAVE_IOPRIO_GET
static inline int missing_ioprio_get(int which, int who) {
return syscall(__NR_ioprio_get, which, who);
}
# define ioprio_get missing_ioprio_get
#endif
/* ======================================================================= */
#if !HAVE_IOPRIO_SET
static inline int missing_ioprio_set(int which, int who, int ioprio) {
return syscall(__NR_ioprio_set, which, who, ioprio);
}
# define ioprio_set missing_ioprio_set
#endif
/* ======================================================================= */
#if !HAVE_MEMFD_CREATE
static inline int missing_memfd_create(const char *name, unsigned int flags) {
# ifdef __NR_memfd_create

View file

@ -740,13 +740,13 @@ int parse_oom_score_adjust(const char *s, int *ret) {
int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret) {
assert(ret);
if (i >= (~0UL << FSHIFT))
if (i >= (~0UL << LOADAVG_PRECISION_BITS))
return -ERANGE;
i = i << FSHIFT;
f = DIV_ROUND_UP((f << FSHIFT), 100);
i = i << LOADAVG_PRECISION_BITS;
f = DIV_ROUND_UP((f << LOADAVG_PRECISION_BITS), 100);
if (f >= FIXED_1)
if (f >= LOADAVG_FIXED_POINT_1_0)
return -ERANGE;
*ret = i | f;

View file

@ -3,7 +3,6 @@
#include <inttypes.h>
#include <limits.h>
#include <linux/loadavg.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
@ -136,6 +135,14 @@ int parse_ip_prefix_length(const char *s, int *ret);
int parse_oom_score_adjust(const char *s, int *ret);
/* Implement floating point using fixed integers, to improve performance when
* calculating load averages. These macros can be used to extract the integer
* and decimal parts of a value. */
#define LOADAVG_PRECISION_BITS 11
#define LOADAVG_FIXED_POINT_1_0 (1 << LOADAVG_PRECISION_BITS)
#define LOADAVG_INT_SIDE(x) ((x) >> LOADAVG_PRECISION_BITS)
#define LOADAVG_DECIMAL_SIDE(x) LOADAVG_INT_SIDE(((x) & (LOADAVG_FIXED_POINT_1_0 - 1)) * 100)
/* Given a Linux load average (e.g. decimal number 34.89 where 34 is passed as i and 89 is passed as f), convert it
* to a loadavg_t. */
int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret);

View file

@ -13,6 +13,7 @@
#undef basename
#include "alloc-util.h"
#include "chase-symlinks.h"
#include "extract-word.h"
#include "fd-util.h"
#include "fs-util.h"
@ -630,7 +631,11 @@ static int check_x_access(const char *path, int *ret_fd) {
return r;
r = access_fd(fd, X_OK);
if (r < 0)
if (r == -ENOSYS) {
/* /proc is not mounted. Fallback to access(). */
if (access(path, X_OK) < 0)
return -errno;
} else if (r < 0)
return r;
if (ret_fd)
@ -639,30 +644,53 @@ static int check_x_access(const char *path, int *ret_fd) {
return 0;
}
int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd) {
int last_error, r;
static int find_executable_impl(const char *name, const char *root, char **ret_filename, int *ret_fd) {
_cleanup_close_ int fd = -1;
_cleanup_free_ char *path_name = NULL;
int r;
assert(name);
/* Function chase_symlinks() is invoked only when root is not NULL, as using it regardless of
* root value would alter the behavior of existing callers for example: /bin/sleep would become
* /usr/bin/sleep when find_executables is called. Hence, this function should be invoked when
* needed to avoid unforeseen regression or other complicated changes. */
if (root) {
r = chase_symlinks(name,
root,
CHASE_PREFIX_ROOT,
&path_name,
/* ret_fd= */ NULL); /* prefix root to name in case full paths are not specified */
if (r < 0)
return r;
name = path_name;
}
r = check_x_access(name, ret_fd ? &fd : NULL);
if (r < 0)
return r;
if (ret_filename) {
r = path_make_absolute_cwd(name, ret_filename);
if (r < 0)
return r;
}
if (ret_fd)
*ret_fd = TAKE_FD(fd);
return 0;
}
int find_executable_full(const char *name, const char *root, char **exec_search_path, bool use_path_envvar, char **ret_filename, int *ret_fd) {
int last_error = -ENOENT, r = 0;
const char *p = NULL;
assert(name);
if (is_path(name)) {
_cleanup_close_ int fd = -1;
r = check_x_access(name, ret_fd ? &fd : NULL);
if (r < 0)
return r;
if (ret_filename) {
r = path_make_absolute_cwd(name, ret_filename);
if (r < 0)
return r;
}
if (ret_fd)
*ret_fd = TAKE_FD(fd);
return 0;
}
if (is_path(name))
return find_executable_impl(name, root, ret_filename, ret_fd);
if (use_path_envvar)
/* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the
@ -671,12 +699,31 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
if (!p)
p = DEFAULT_PATH;
last_error = -ENOENT;
if (exec_search_path) {
char **element;
STRV_FOREACH(element, exec_search_path) {
_cleanup_free_ char *full_path = NULL;
if (!path_is_absolute(*element))
continue;
full_path = path_join(*element, name);
if (!full_path)
return -ENOMEM;
r = find_executable_impl(full_path, root, ret_filename, ret_fd);
if (r < 0) {
if (r != -EACCES)
last_error = r;
continue;
}
return 0;
}
return last_error;
}
/* Resolve a single-component name to a full path */
for (;;) {
_cleanup_free_ char *element = NULL;
_cleanup_close_ int fd = -1;
r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
@ -690,7 +737,7 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
if (!path_extend(&element, name))
return -ENOMEM;
r = check_x_access(element, ret_fd ? &fd : NULL);
r = find_executable_impl(element, root, ret_filename, ret_fd);
if (r < 0) {
/* PATH entries which we don't have access to are ignored, as per tradition. */
if (r != -EACCES)
@ -699,11 +746,6 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
}
/* Found it! */
if (ret_filename)
*ret_filename = path_simplify(TAKE_PTR(element));
if (ret_fd)
*ret_fd = TAKE_FD(fd);
return 0;
}

View file

@ -99,9 +99,9 @@ int path_strv_make_absolute_cwd(char **l);
char** path_strv_resolve(char **l, const char *root);
char** path_strv_resolve_uniq(char **l, const char *root);
int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd);
int find_executable_full(const char *name, const char *root, char **exec_search_path, bool use_path_envvar, char **ret_filename, int *ret_fd);
static inline int find_executable(const char *name, char **ret_filename) {
return find_executable_full(name, true, ret_filename, NULL);
return find_executable_full(name, /* root= */ NULL, NULL, true, ret_filename, NULL);
}
bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);

View file

@ -173,6 +173,16 @@ int prioq_put(Prioq *q, void *data, unsigned *idx) {
return 0;
}
int prioq_ensure_put(Prioq **q, compare_func_t compare_func, void *data, unsigned *idx) {
int r;
r = prioq_ensure_allocated(q, compare_func);
if (r < 0)
return r;
return prioq_put(*q, data, idx);
}
static void remove_item(Prioq *q, struct prioq_item *i) {
struct prioq_item *l;

View file

@ -16,6 +16,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Prioq*, prioq_free);
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
int prioq_put(Prioq *q, void *data, unsigned *idx);
int prioq_ensure_put(Prioq **q, compare_func_t compare_func, void *data, unsigned *idx);
int prioq_remove(Prioq *q, void *data, unsigned *idx);
int prioq_reshuffle(Prioq *q, void *data, unsigned *idx);

View file

@ -27,7 +27,6 @@
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "ioprio.h"
#include "locale-util.h"
#include "log.h"
#include "macro.h"
@ -1254,7 +1253,7 @@ int safe_fork_full(
pid_t original_pid, pid;
sigset_t saved_ss, ss;
_cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
_unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
bool block_signals = false, block_all = false;
int prio, r;
@ -1503,6 +1502,24 @@ int set_oom_score_adjust(int value) {
WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
}
int get_oom_score_adjust(int *ret) {
_cleanup_free_ char *t;
int r, a;
r = read_virtual_file("/proc/self/oom_score_adj", SIZE_MAX, &t, NULL);
if (r < 0)
return r;
delete_trailing_chars(t, WHITESPACE);
assert_se(safe_atoi(t, &a) >= 0);
assert_se(oom_score_adjust_is_valid(a));
if (ret)
*ret = a;
return 0;
}
int pidfd_get_pid(int fd, pid_t *ret) {
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *fdinfo = NULL;

View file

@ -13,8 +13,8 @@
#include "alloc-util.h"
#include "format-util.h"
#include "ioprio.h"
#include "macro.h"
#include "missing_ioprio.h"
#include "time-util.h"
#define procfs_file_alloca(pid, field) \
@ -175,6 +175,7 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
int namespace_fork(const char *outer_name, const char *inner_name, int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
int set_oom_score_adjust(int value);
int get_oom_score_adjust(int *ret);
/* The highest possibly (theoretic) pid_t value on this architecture. */
#define PID_T_MAX ((pid_t) INT32_MAX)

View file

@ -26,7 +26,7 @@ static inline Set* set_free_free(Set *s) {
/* no set_free_free_free */
#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS))
#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(s) 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)

View file

@ -265,7 +265,7 @@ int pop_pending_signal_internal(int sig, ...) {
if (sigemptyset(&ss) < 0)
return -errno;
/* Add first signal (if the signal is zero, we'll silently skip it, to make it easiert to build
/* Add first signal (if the signal is zero, we'll silently skip it, to make it easier to build
* parameter lists where some element are sometimes off, similar to how sigset_add_many_ap() handles
* this.) */
if (sig > 0 && sigaddset(&ss, sig) < 0)

View file

@ -455,23 +455,23 @@ int sockaddr_pretty(
if (r < 0)
return -ENOMEM;
} else {
char a[INET6_ADDRSTRLEN], ifname[IF_NAMESIZE + 1];
char a[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
if (sa->in6.sin6_scope_id != 0)
format_ifname_full(sa->in6.sin6_scope_id, ifname, FORMAT_IFNAME_IFINDEX);
if (include_port) {
r = asprintf(&p,
if (asprintf(&p,
"[%s]:%u%s%s",
a,
be16toh(sa->in6.sin6_port),
sa->in6.sin6_scope_id != 0 ? "%" : "",
sa->in6.sin6_scope_id != 0 ? ifname : "");
if (r < 0)
FORMAT_IFNAME_FULL(sa->in6.sin6_scope_id, FORMAT_IFNAME_IFINDEX)) < 0)
return -ENOMEM;
} else {
p = sa->in6.sin6_scope_id != 0 ? strjoin(a, "%", ifname) : strdup(a);
if (sa->in6.sin6_scope_id != 0)
p = strjoin(a, "%", FORMAT_IFNAME_FULL(sa->in6.sin6_scope_id, FORMAT_IFNAME_IFINDEX));
else
p = strdup(a);
if (!p)
return -ENOMEM;
}
@ -793,7 +793,7 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) {
/* Let's refuse "all" and "default" as interface name, to avoid collisions with the special sysctl
* directories /proc/sys/net/{ipv4,ipv6}/conf/{all,default} */
if (STR_IN_SET(p, "all", "default"))
if (!FLAGS_SET(flags, IFNAME_VALID_SPECIAL) && STR_IN_SET(p, "all", "default"))
return false;
for (const char *t = p; *t; t++) {
@ -921,7 +921,7 @@ int getpeergroups(int fd, gid_t **ret) {
ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
struct iovec *iov, size_t iovlen,
const struct iovec *iov, size_t iovlen,
const struct sockaddr *sa, socklen_t len,
int flags) {
@ -929,7 +929,7 @@ ssize_t send_one_fd_iov_sa(
struct msghdr mh = {
.msg_name = (struct sockaddr*) sa,
.msg_namelen = len,
.msg_iov = iov,
.msg_iov = (struct iovec *)iov,
.msg_iovlen = iovlen,
};
ssize_t k;
@ -1233,7 +1233,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
}
int socket_bind_to_ifindex(int fd, int ifindex) {
char ifname[IF_NAMESIZE + 1];
char ifname[IF_NAMESIZE];
int r;
assert(fd >= 0);
@ -1251,8 +1251,9 @@ int socket_bind_to_ifindex(int fd, int ifindex) {
return r;
/* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */
if (!format_ifname(ifindex, ifname))
return -errno;
r = format_ifname(ifindex, ifname);
if (r < 0)
return r;
return socket_bind_to_ifname(fd, ifname);
}

View file

@ -135,9 +135,10 @@ int ip_tos_to_string_alloc(int i, char **s);
int ip_tos_from_string(const char *s);
typedef enum {
IFNAME_VALID_ALTERNATIVE = 1 << 0,
IFNAME_VALID_NUMERIC = 1 << 1,
_IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC,
IFNAME_VALID_ALTERNATIVE = 1 << 0, /* Allow "altnames" too */
IFNAME_VALID_NUMERIC = 1 << 1, /* Allow decimal formatted ifindexes too */
IFNAME_VALID_SPECIAL = 1 << 2, /* Allow the special names "all" and "default" */
_IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC | IFNAME_VALID_SPECIAL,
} IfnameValidFlags;
bool ifname_valid_char(char a);
bool ifname_valid_full(const char *p, IfnameValidFlags flags);
@ -153,7 +154,7 @@ int getpeergroups(int fd, gid_t **ret);
ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
struct iovec *iov, size_t iovlen,
const struct iovec *iov, size_t iovlen,
const struct sockaddr *sa, socklen_t len,
int flags);
int send_one_fd_sa(int transport_fd,
@ -245,7 +246,7 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
_len = sizeof(struct sockaddr_vm); \
break; \
default: \
assert_not_reached("invalid socket family"); \
assert_not_reached(); \
} \
_len; \
})
@ -277,6 +278,28 @@ static inline int getsockopt_int(int fd, int level, int optname, int *ret) {
int socket_bind_to_ifname(int fd, const char *ifname);
int socket_bind_to_ifindex(int fd, int ifindex);
/* Define a 64bit version of timeval/timespec in any case, even on 32bit userspace. */
struct timeval_large {
uint64_t tvl_sec, tvl_usec;
};
struct timespec_large {
uint64_t tvl_sec, tvl_nsec;
};
/* glibc duplicates timespec/timeval on certain 32bit archs, once in 32bit and once in 64bit.
* See __convert_scm_timestamps() in glibc source code. Hence, we need additional buffer space for them
* to prevent from recvmsg_safe() returning -EXFULL. */
#define CMSG_SPACE_TIMEVAL \
((sizeof(struct timeval) == sizeof(struct timeval_large)) ? \
CMSG_SPACE(sizeof(struct timeval)) : \
CMSG_SPACE(sizeof(struct timeval)) + \
CMSG_SPACE(sizeof(struct timeval_large)))
#define CMSG_SPACE_TIMESPEC \
((sizeof(struct timespec) == sizeof(struct timespec_large)) ? \
CMSG_SPACE(sizeof(struct timespec)) : \
CMSG_SPACE(sizeof(struct timespec)) + \
CMSG_SPACE(sizeof(struct timespec_large)))
ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags);
int socket_get_family(int fd, int *ret);

View file

@ -8,10 +8,11 @@
#include <unistd.h>
#include "alloc-util.h"
#include "chase-symlinks.h"
#include "dirent-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "macro.h"
#include "missing_fs.h"
#include "missing_magic.h"

View file

@ -9,8 +9,13 @@
#include "macro.h"
#include "memory-util.h"
#define snprintf_ok(buf, len, fmt, ...) \
((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len))
#define snprintf_ok(buf, len, fmt, ...) \
({ \
char *_buf = (buf); \
size_t _len = (len); \
int _snpf = snprintf(_buf, _len, (fmt), __VA_ARGS__); \
_snpf >= 0 && (size_t) _snpf < _len ? _buf : NULL; \
})
#define xsprintf(buf, fmt, ...) \
assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough")
@ -58,7 +63,7 @@ do { \
(void) va_arg(ap, long double); \
break; \
default: \
assert_not_reached("Unknown format string argument."); \
assert_not_reached(); \
} \
} \
} while (false)

View file

@ -1146,3 +1146,19 @@ int string_contains_word_strv(const char *string, const char *separators, char *
*ret_word = found;
return !!found;
}
bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
if (!s1 && !s2)
return true;
if (!s1 || !s2)
return false;
if (!ok)
ok = WHITESPACE;
for (; *s1 && *s2; s1++, s2++)
if (*s1 != *s2)
break;
return in_charset(s1, ok) && in_charset(s2, ok);
}

View file

@ -189,22 +189,6 @@ static inline void strncpy_exact(char *buf, const char *src, size_t buf_len) {
}
REENABLE_WARNING;
/* Like startswith(), but operates on arbitrary memory blocks */
static inline void *memory_startswith(const void *p, size_t sz, const char *token) {
assert(token);
size_t n = strlen(token);
if (sz < n)
return NULL;
assert(p);
if (memcmp(p, token, n) != 0)
return NULL;
return (uint8_t*) p + n;
}
/* Like startswith_no_case(), but operates on arbitrary memory blocks.
* It works only for ASCII strings.
*/
@ -242,3 +226,5 @@ int string_contains_word_strv(const char *string, const char *separators, char *
static inline int string_contains_word(const char *string, const char *separators, const char *word) {
return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL);
}
bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok);

View file

@ -300,6 +300,24 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla
return (int) n;
}
int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags) {
_cleanup_strv_free_ char **l = NULL;
int r;
assert(t);
assert(s);
r = strv_split_full(&l, s, separators, flags);
if (r < 0)
return r;
r = strv_extend_strv(t, l, filter_duplicates);
if (r < 0)
return r;
return (int) strv_length(*t);
}
int strv_split_colon_pairs(char ***t, const char *s) {
_cleanup_strv_free_ char **l = NULL;
size_t n = 0;

View file

@ -83,6 +83,9 @@ static inline char **strv_split(const char *s, const char *separators) {
return ret;
}
int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags);
#define strv_split_and_extend(t, s, sep, dup) strv_split_and_extend_full(t, s, sep, dup, 0)
int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags);
static inline char **strv_split_newlines(const char *s) {
char **ret;
@ -233,9 +236,11 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space);
#define strv_free_and_replace(a, b) \
({ \
strv_free(a); \
(a) = (b); \
(b) = NULL; \
char ***_a = &(a); \
char ***_b = &(b); \
strv_free(*_a); \
(*_a) = (*_b); \
(*_b) = NULL; \
0; \
})

View file

@ -433,62 +433,62 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
usec_t years = d / USEC_PER_YEAR;
usec_t months = (d % USEC_PER_YEAR) / USEC_PER_MONTH;
snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
years,
years == 1 ? "year" : "years",
months,
months == 1 ? "month" : "months",
s);
(void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
years,
years == 1 ? "year" : "years",
months,
months == 1 ? "month" : "months",
s);
} else if (d >= USEC_PER_MONTH) {
usec_t months = d / USEC_PER_MONTH;
usec_t days = (d % USEC_PER_MONTH) / USEC_PER_DAY;
snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
months,
months == 1 ? "month" : "months",
days,
days == 1 ? "day" : "days",
s);
(void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
months,
months == 1 ? "month" : "months",
days,
days == 1 ? "day" : "days",
s);
} else if (d >= USEC_PER_WEEK) {
usec_t weeks = d / USEC_PER_WEEK;
usec_t days = (d % USEC_PER_WEEK) / USEC_PER_DAY;
snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
weeks,
weeks == 1 ? "week" : "weeks",
days,
days == 1 ? "day" : "days",
s);
(void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
weeks,
weeks == 1 ? "week" : "weeks",
days,
days == 1 ? "day" : "days",
s);
} else if (d >= 2*USEC_PER_DAY)
snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
(void) snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
else if (d >= 25*USEC_PER_HOUR)
snprintf(buf, l, "1 day " USEC_FMT "h %s",
(d - USEC_PER_DAY) / USEC_PER_HOUR, s);
(void) snprintf(buf, l, "1 day " USEC_FMT "h %s",
(d - USEC_PER_DAY) / USEC_PER_HOUR, s);
else if (d >= 6*USEC_PER_HOUR)
snprintf(buf, l, USEC_FMT "h %s",
d / USEC_PER_HOUR, s);
(void) snprintf(buf, l, USEC_FMT "h %s",
d / USEC_PER_HOUR, s);
else if (d >= USEC_PER_HOUR)
snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
d / USEC_PER_HOUR,
(d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
(void) snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
d / USEC_PER_HOUR,
(d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
else if (d >= 5*USEC_PER_MINUTE)
snprintf(buf, l, USEC_FMT "min %s",
d / USEC_PER_MINUTE, s);
(void) snprintf(buf, l, USEC_FMT "min %s",
d / USEC_PER_MINUTE, s);
else if (d >= USEC_PER_MINUTE)
snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
d / USEC_PER_MINUTE,
(d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
(void) snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
d / USEC_PER_MINUTE,
(d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
else if (d >= USEC_PER_SEC)
snprintf(buf, l, USEC_FMT "s %s",
d / USEC_PER_SEC, s);
(void) snprintf(buf, l, USEC_FMT "s %s",
d / USEC_PER_SEC, s);
else if (d >= USEC_PER_MSEC)
snprintf(buf, l, USEC_FMT "ms %s",
d / USEC_PER_MSEC, s);
(void) snprintf(buf, l, USEC_FMT "ms %s",
d / USEC_PER_MSEC, s);
else if (d > 0)
snprintf(buf, l, USEC_FMT"us %s",
d, s);
(void) snprintf(buf, l, USEC_FMT"us %s",
d, s);
else
snprintf(buf, l, "now");
(void) snprintf(buf, l, "now");
buf[l-1] = 0;
return buf;

View file

@ -300,11 +300,7 @@ int link_tmpfile(int fd, const char *path, const char *target) {
if (r < 0)
return r;
} else {
char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
if (linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
return -errno;
}

View file

@ -11,9 +11,26 @@
#define _const_ __attribute__((__const__))
#define _pure_ __attribute__((__pure__))
#define _section_(x) __attribute__((__section__(x)))
#define _packed_ __attribute__((__packed__))
#define _used_ __attribute__((__used__))
#define _unused_ __attribute__((__unused__))
#define _cleanup_(x) __attribute__((__cleanup__(x)))
#define _likely_(x) (__builtin_expect(!!(x), 1))
#define _unlikely_(x) (__builtin_expect(!!(x), 0))
#if __GNUC__ >= 7
#define _fallthrough_ __attribute__((__fallthrough__))
#else
#define _fallthrough_
#endif
/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
* compiler versions */
#ifndef _noreturn_
#if __STDC_VERSION__ >= 201112L
#define _noreturn_ _Noreturn
#else
#define _noreturn_ __attribute__((__noreturn__))
#endif
#endif
#define XSTRINGIFY(x) #x
#define STRINGIFY(x) XSTRINGIFY(x)
@ -34,7 +51,14 @@
#define CONCATENATE(x, y) XCONCATENATE(x, y)
#ifdef SD_BOOT
#define assert(expr) do {} while (false)
#ifdef NDEBUG
#define assert(expr)
#define assert_not_reached() __builtin_unreachable()
#else
void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_;
#define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
#define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__)
#endif
#endif
#if defined(static_assert)
@ -70,12 +94,29 @@
UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
})
/* evaluates to (void) if _A or _B are not constant or of different types */
#define IS_UNSIGNED_INTEGER_TYPE(type) \
(__builtin_types_compatible_p(typeof(type), unsigned char) || \
__builtin_types_compatible_p(typeof(type), unsigned short) || \
__builtin_types_compatible_p(typeof(type), unsigned) || \
__builtin_types_compatible_p(typeof(type), unsigned long) || \
__builtin_types_compatible_p(typeof(type), unsigned long long))
#define IS_SIGNED_INTEGER_TYPE(type) \
(__builtin_types_compatible_p(typeof(type), signed char) || \
__builtin_types_compatible_p(typeof(type), signed short) || \
__builtin_types_compatible_p(typeof(type), signed) || \
__builtin_types_compatible_p(typeof(type), signed long) || \
__builtin_types_compatible_p(typeof(type), signed long long))
/* Evaluates to (void) if _A or _B are not constant or of different types (being integers of different sizes
* is also OK as long as the signedness matches) */
#define CONST_MAX(_A, _B) \
(__builtin_choose_expr( \
__builtin_constant_p(_A) && \
__builtin_constant_p(_B) && \
__builtin_types_compatible_p(typeof(_A), typeof(_B)), \
(__builtin_types_compatible_p(typeof(_A), typeof(_B)) || \
(IS_UNSIGNED_INTEGER_TYPE(_A) && IS_UNSIGNED_INTEGER_TYPE(_B)) || \
(IS_SIGNED_INTEGER_TYPE(_A) && IS_SIGNED_INTEGER_TYPE(_B))), \
((_A) > (_B)) ? (_A) : (_B), \
VOID_0))
@ -216,3 +257,10 @@
(ptr) = NULL; \
_ptr_; \
})
/*
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
* Contrary to strlen(), this is a constant expression.
* @x: a string literal.
*/
#define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0])))

View file

@ -16,6 +16,7 @@
#define strncmp(a, b, n) StrnCmp((a), (b), (n))
#define strcasecmp(a, b) StriCmp((a), (b))
#define STR_C(str) (L ## str)
#define memcmp(a, b, n) CompareMem(a, b, n)
#else
#define STR_C(str) (str)
#endif
@ -65,3 +66,19 @@ static inline const sd_char *yes_no(sd_bool b) {
}
sd_int strverscmp_improved(const sd_char *a, const sd_char *b);
/* Like startswith(), but operates on arbitrary memory blocks */
static inline void *memory_startswith(const void *p, sd_size_t sz, const sd_char *token) {
assert(token);
sd_size_t n = strlen(token) * sizeof(sd_char);
if (sz < n)
return NULL;
assert(p);
if (memcmp(p, token, n) != 0)
return NULL;
return (uint8_t*) p + n;
}

View file

@ -17,24 +17,6 @@
log_interface_full_errno_zerook(ifname, level, _error, __VA_ARGS__); \
})
#define log_interface_prefix_full_errno_zerook(prefix, ifname_expr, error, fmt, ...) \
({ \
int _e = (error); \
if (DEBUG_LOGGING) \
log_interface_full_errno_zerook( \
ifname_expr, \
LOG_DEBUG, _e, prefix fmt, \
##__VA_ARGS__); \
-ERRNO_VALUE(_e); \
})
#define log_interface_prefix_full_errno(prefix, ifname_expr, error, fmt, ...) \
({ \
int _error = (error); \
ASSERT_NON_ZERO(_error); \
log_interface_prefix_full_errno_zerook(prefix, ifname_expr, _error, fmt, ##__VA_ARGS__); \
})
/*
* The following macros append INTERFACE= to the message.
* The macros require a struct named 'Link' which contains 'char *ifname':

View file

@ -36,16 +36,29 @@ bool http_url_is_valid(const char *url) {
return ascii_is_valid(p);
}
bool file_url_is_valid(const char *url) {
const char *p;
if (isempty(url))
return false;
p = startswith(url, "file:/");
if (isempty(p))
return false;
return ascii_is_valid(p);
}
bool documentation_url_is_valid(const char *url) {
const char *p;
if (isempty(url))
return false;
if (http_url_is_valid(url))
if (http_url_is_valid(url) || file_url_is_valid(url))
return true;
p = STARTSWITH_SET(url, "file:/", "info:", "man:");
p = STARTSWITH_SET(url, "info:", "man:");
if (isempty(p))
return false;

View file

@ -6,6 +6,7 @@
#include "macro.h"
bool http_url_is_valid(const char *url) _pure_;
bool file_url_is_valid(const char *url) _pure_;
bool documentation_url_is_valid(const char *url) _pure_;