systemd: merge branch systemd into main

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1068
This commit is contained in:
Thomas Haller 2022-01-24 20:34:37 +01:00
commit 18cb8205b3
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
92 changed files with 2750 additions and 1350 deletions

View file

@ -2122,8 +2122,10 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/sd-adapt-shared/dhcp-server-internal.h \ src/libnm-systemd-shared/sd-adapt-shared/dhcp-server-internal.h \
src/libnm-systemd-shared/sd-adapt-shared/dirent-util.h \ src/libnm-systemd-shared/sd-adapt-shared/dirent-util.h \
src/libnm-systemd-shared/sd-adapt-shared/errno-list.h \ src/libnm-systemd-shared/sd-adapt-shared/errno-list.h \
src/libnm-systemd-shared/sd-adapt-shared/filesystems.h \
src/libnm-systemd-shared/sd-adapt-shared/glob-util.h \ src/libnm-systemd-shared/sd-adapt-shared/glob-util.h \
src/libnm-systemd-shared/sd-adapt-shared/gunicode.h \ src/libnm-systemd-shared/sd-adapt-shared/gunicode.h \
src/libnm-systemd-shared/sd-adapt-shared/hmac.h \
src/libnm-systemd-shared/sd-adapt-shared/idn-util.h \ src/libnm-systemd-shared/sd-adapt-shared/idn-util.h \
src/libnm-systemd-shared/sd-adapt-shared/ioprio.h \ src/libnm-systemd-shared/sd-adapt-shared/ioprio.h \
src/libnm-systemd-shared/sd-adapt-shared/locale-util.h \ src/libnm-systemd-shared/sd-adapt-shared/locale-util.h \
@ -2138,6 +2140,7 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/sd-adapt-shared/missing_timerfd.h \ src/libnm-systemd-shared/sd-adapt-shared/missing_timerfd.h \
src/libnm-systemd-shared/sd-adapt-shared/mkdir.h \ src/libnm-systemd-shared/sd-adapt-shared/mkdir.h \
src/libnm-systemd-shared/sd-adapt-shared/namespace-util.h \ src/libnm-systemd-shared/sd-adapt-shared/namespace-util.h \
src/libnm-systemd-shared/sd-adapt-shared/netif-util.h \
src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h \ src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h \
src/libnm-systemd-shared/sd-adapt-shared/nulstr-util.h \ src/libnm-systemd-shared/sd-adapt-shared/nulstr-util.h \
src/libnm-systemd-shared/sd-adapt-shared/os-util.h \ src/libnm-systemd-shared/sd-adapt-shared/os-util.h \
@ -2245,7 +2248,7 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/src/fundamental/macro-fundamental.h \ src/libnm-systemd-shared/src/fundamental/macro-fundamental.h \
src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c \ src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c \
src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h \ src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h \
src/libnm-systemd-shared/src/fundamental/type.h \ src/libnm-systemd-shared/src/fundamental/types-fundamental.h \
src/libnm-systemd-shared/src/shared/dns-domain.c \ src/libnm-systemd-shared/src/shared/dns-domain.c \
src/libnm-systemd-shared/src/shared/dns-domain.h \ src/libnm-systemd-shared/src/shared/dns-domain.h \
src/libnm-systemd-shared/src/shared/log-link.h \ src/libnm-systemd-shared/src/shared/log-link.h \

View file

@ -16,7 +16,7 @@
#include "unaligned.h" #include "unaligned.h"
#include "util.h" #include "util.h"
int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac) { int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *mac) {
struct sock_filter filter[] = { struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */ BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */
@ -38,30 +38,21 @@ int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
/* Sender Hardware Address must be different from our own */ /* Sender Hardware Address must be different from our own */
BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])),/* A <- 4 bytes of client's MAC */ BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be32(&mac->ether_addr_octet[0])), /* X <- 4 bytes of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 4), /* A == X ? */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */ BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be16(&mac->ether_addr_octet[4])), /* X <- remainder of client's MAC */
BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])),/* A <- remainder of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1), /* A == X ? */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
/* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */ /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */
BPF_STMT(BPF_LD + BPF_IMM, htobe32(a->s_addr)), /* A <- clients IP */ BPF_STMT(BPF_LDX + BPF_IMM, htobe32(a->s_addr)), /* X <- clients IP */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1), /* A == X ? */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
BPF_STMT(BPF_LD + BPF_IMM, htobe32(a->s_addr)), /* A <- clients IP */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1), /* A == 0 ? */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
}; };
struct sock_fprog fprog = { struct sock_fprog fprog = {
@ -77,7 +68,7 @@ int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *
return 0; return 0;
} }
int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac) { int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *mac) {
union sockaddr_union link = { union sockaddr_union link = {
.ll.sll_family = AF_PACKET, .ll.sll_family = AF_PACKET,
.ll.sll_protocol = htobe16(ETH_P_ARP), .ll.sll_protocol = htobe16(ETH_P_ARP),
@ -89,12 +80,13 @@ int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const stru
int r; int r;
assert(ifindex > 0); assert(ifindex > 0);
assert(mac);
s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
if (s < 0) if (s < 0)
return -errno; return -errno;
r = arp_update_filter(s, a, eth_mac); r = arp_update_filter(s, a, mac);
if (r < 0) if (r < 0)
return r; return r;

View file

@ -11,8 +11,8 @@
#include "socket-util.h" #include "socket-util.h"
#include "sparse-endian.h" #include "sparse-endian.h"
int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac); int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *mac);
int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac); int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *mac);
int arp_send_packet( int arp_send_packet(
int fd, int fd,

View file

@ -11,7 +11,7 @@
#include "dhcp-identifier.h" #include "dhcp-identifier.h"
#include "dhcp6-protocol.h" #include "dhcp6-protocol.h"
#include "network-util.h" #include "netif-util.h"
#include "siphash24.h" #include "siphash24.h"
#include "sparse-endian.h" #include "sparse-endian.h"
#include "stat-util.h" #include "stat-util.h"
@ -200,7 +200,7 @@ int dhcp_identifier_set_iaid(
/* device is under renaming */ /* device is under renaming */
return -EBUSY; return -EBUSY;
name = net_get_name_persistent(device); name = net_get_persistent_name(device);
} }
if (name) if (name)
@ -214,7 +214,7 @@ int dhcp_identifier_set_iaid(
if (legacy_unstable_byteorder) if (legacy_unstable_byteorder)
/* for historical reasons (a bug), the bits were swapped and thus /* for historical reasons (a bug), the bits were swapped and thus
* the result was endianness dependent. Preserve that behavior. */ * the result was endianness dependent. Preserve that behavior. */
id32 = __bswap_32(id32); id32 = bswap_32(id32);
else else
/* the fixed behavior returns a stable byte order. Since LE is expected /* the fixed behavior returns a stable byte order. Since LE is expected
* to be more common, swap the bytes on LE to give the same as legacy * to be more common, swap the bytes on LE to give the same as legacy

View file

@ -53,8 +53,8 @@ typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len,
int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message); int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message);
int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
uint8_t type, uint16_t arp_type, size_t optlen, uint8_t type, uint16_t arp_type, uint8_t hlen, const uint8_t *chaddr,
size_t *optoffset); size_t optlen, size_t *optoffset);
uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len); uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len);

View file

@ -70,6 +70,12 @@ struct sd_dhcp_lease {
char *timezone; char *timezone;
uint8_t sixrd_ipv4masklen;
uint8_t sixrd_prefixlen;
struct in6_addr sixrd_prefix;
struct in_addr *sixrd_br_addresses;
size_t sixrd_n_br_addresses;
LIST_HEAD(struct sd_dhcp_raw_option, private_options); LIST_HEAD(struct sd_dhcp_raw_option, private_options);
}; };

View file

@ -60,24 +60,20 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
/* We only support MAC address length to be either 0 or 6 (ETH_ALEN). Optionally /* We only support MAC address length to be either 0 or 6 (ETH_ALEN). Optionally
* compare chaddr for ETH_ALEN bytes. */ * compare chaddr for ETH_ALEN bytes. */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 12), /* A (the MAC address length) == ETH_ALEN ? */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 8), /* A (the MAC address length) == ETH_ALEN ? */
BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])), /* A <- 4 bytes of client's MAC */ BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])), /* X <- 4 bytes of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0), /* A == X ? */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])), /* A <- remainder of client's MAC */ BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])), /* X <- remainder of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0), /* A == X ? */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)), /* A <- DHCP magic cookie */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)), /* A <- DHCP magic cookie */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0), /* cookie == DHCP magic cookie ? */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0), /* cookie == DHCP magic cookie ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */
}; };
struct sock_fprog fprog = { struct sock_fprog fprog = {
.len = ELEMENTSOF(filter), .len = ELEMENTSOF(filter),
@ -117,10 +113,17 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
return TAKE_FD(s); return TAKE_FD(s);
} }
int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, uint32_t xid, int dhcp_network_bind_raw_socket(
const uint8_t *mac_addr, size_t mac_addr_len, int ifindex,
const uint8_t *bcast_addr, size_t bcast_addr_len, union sockaddr_union *link,
uint16_t arp_type, uint16_t port) { uint32_t xid,
const uint8_t *mac_addr,
size_t mac_addr_len,
const uint8_t *bcast_addr,
size_t bcast_addr_len,
uint16_t arp_type,
uint16_t port) {
static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
/* Default broadcast address for IPoIB */ /* Default broadcast address for IPoIB */
static const uint8_t ib_bcast[] = { static const uint8_t ib_bcast[] = {
@ -174,7 +177,6 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int
r = setsockopt_int(s, IPPROTO_IP, IP_TOS, ip_service_type); r = setsockopt_int(s, IPPROTO_IP, IP_TOS, ip_service_type);
else else
r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6); r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6);
if (r < 0) if (r < 0)
return r; return r;
@ -206,43 +208,49 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int
return r; return r;
} }
r = bind(s, &src.sa, sizeof(src.in)); if (bind(s, &src.sa, sizeof(src.in)) < 0)
if (r < 0)
return -errno; return -errno;
return TAKE_FD(s); return TAKE_FD(s);
} }
int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, int dhcp_network_send_raw_socket(
const void *packet, size_t len) { int s,
int r; const union sockaddr_union *link,
const void *packet,
size_t len) {
/* Do not add assert(s >= 0) here, as this is called in fuzz-dhcp-server, and in that case this
* function should fail with negative errno. */
assert(link); assert(link);
assert(packet); assert(packet);
assert(len); assert(len > 0);
r = sendto(s, packet, len, 0, &link->sa, SOCKADDR_LL_LEN(link->ll)); if (sendto(s, packet, len, 0, &link->sa, SOCKADDR_LL_LEN(link->ll)) < 0)
if (r < 0)
return -errno; return -errno;
return 0; return 0;
} }
int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, int dhcp_network_send_udp_socket(
const void *packet, size_t len) { int s,
be32_t address,
uint16_t port,
const void *packet,
size_t len) {
union sockaddr_union dest = { union sockaddr_union dest = {
.in.sin_family = AF_INET, .in.sin_family = AF_INET,
.in.sin_port = htobe16(port), .in.sin_port = htobe16(port),
.in.sin_addr.s_addr = address, .in.sin_addr.s_addr = address,
}; };
int r;
assert(s >= 0); assert(s >= 0);
assert(packet); assert(packet);
assert(len); assert(len > 0);
r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in)); if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in)) < 0)
if (r < 0)
return -errno; return -errno;
return 0; return 0;

View file

@ -12,21 +12,44 @@
#include "dhcp-internal.h" #include "dhcp-internal.h"
#include "dhcp-protocol.h" #include "dhcp-protocol.h"
#include "memory-util.h"
#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312 #define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, int dhcp_message_init(
uint8_t type, uint16_t arp_type, size_t optlen, DHCPMessage *message,
size_t *optoffset) { uint8_t op,
uint32_t xid,
uint8_t type,
uint16_t arp_type,
uint8_t hlen,
const uint8_t *chaddr,
size_t optlen,
size_t *optoffset) {
size_t offset = 0; size_t offset = 0;
int r; int r;
assert(IN_SET(op, BOOTREQUEST, BOOTREPLY)); assert(IN_SET(op, BOOTREQUEST, BOOTREPLY));
assert(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND)); assert(chaddr || hlen == 0);
message->op = op; message->op = op;
message->htype = arp_type; message->htype = arp_type;
message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0;
/* RFC2131 section 4.1.1:
The client MUST include its hardware address in the chaddr field, if
necessary for delivery of DHCP reply messages.
RFC 4390 section 2.1:
A DHCP client, when working over an IPoIB interface, MUST follow the
following rules:
"htype" (hardware address type) MUST be 32 [ARPPARAM].
"hlen" (hardware address length) MUST be 0.
"chaddr" (client hardware address) field MUST be zeroed.
*/
message->hlen = (arp_type == ARPHRD_INFINIBAND) ? 0 : hlen;
memcpy_safe(message->chaddr, chaddr, message->hlen);
message->xid = htobe32(xid); message->xid = htobe32(xid);
message->magic = htobe32(DHCP_MAGIC_COOKIE); message->magic = htobe32(DHCP_MAGIC_COOKIE);

View file

@ -11,6 +11,7 @@
#include "sd-event.h" #include "sd-event.h"
#include "sd-dhcp6-client.h" #include "sd-dhcp6-client.h"
#include "dhcp6-protocol.h"
#include "hashmap.h" #include "hashmap.h"
#include "list.h" #include "list.h"
#include "macro.h" #include "macro.h"
@ -93,6 +94,7 @@ typedef struct DHCP6IA {
typedef struct sd_dhcp6_client sd_dhcp6_client; typedef struct sd_dhcp6_client sd_dhcp6_client;
bool dhcp6_option_can_request(uint16_t option);
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
size_t optlen, const void *optval); size_t optlen, const void *optval);
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
@ -129,6 +131,12 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
const void *packet, size_t len); const void *packet, size_t len);
int client_parse_message(
sd_dhcp6_client *client,
DHCP6Message *message,
size_t len,
sd_dhcp6_lease *lease);
const char *dhcp6_message_type_to_string(int s) _const_; const char *dhcp6_message_type_to_string(int s) _const_;
int dhcp6_message_type_from_string(const char *s) _pure_; int dhcp6_message_type_from_string(const char *s) _pure_;
const char *dhcp6_message_status_to_string(int s) _const_; const char *dhcp6_message_status_to_string(int s) _const_;

View file

@ -14,10 +14,14 @@
struct sd_dhcp6_lease { struct sd_dhcp6_lease {
unsigned n_ref; unsigned n_ref;
uint8_t *clientid;
size_t clientid_len;
uint8_t *serverid; uint8_t *serverid;
size_t serverid_len; size_t serverid_len;
uint8_t preference; uint8_t preference;
bool rapid_commit; bool rapid_commit;
triple_timestamp timestamp;
struct in6_addr server_address;
DHCP6IA ia; DHCP6IA ia;
DHCP6IA pd; DHCP6IA pd;
@ -39,21 +43,19 @@ struct sd_dhcp6_lease {
int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire); int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire);
DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia); DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia);
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
size_t len); int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len);
int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len); int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len);
int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference); int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference);
int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference); int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference);
int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease); int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease);
int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); 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_add_dns(sd_dhcp6_lease *lease, const 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_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_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_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_new(sd_dhcp6_lease **ret); int dhcp6_lease_new(sd_dhcp6_lease **ret);

View file

@ -49,6 +49,10 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
if (r < 0) if (r < 0)
return r; return r;
r = setsockopt_int(s, SOL_SOCKET, SO_TIMESTAMP, true);
if (r < 0)
return r;
r = bind(s, &src.sa, sizeof(src.in6)); r = bind(s, &src.sa, sizeof(src.in6));
if (r < 0) if (r < 0)
return -errno; return -errno;

View file

@ -26,6 +26,193 @@
#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd)) #define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta)) #define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
bool dhcp6_option_can_request(uint16_t option) {
/* See Client ORO field in
* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
switch (option) {
case SD_DHCP6_OPTION_CLIENTID:
case SD_DHCP6_OPTION_SERVERID:
case SD_DHCP6_OPTION_IA_NA:
case SD_DHCP6_OPTION_IA_TA:
case SD_DHCP6_OPTION_IAADDR:
case SD_DHCP6_OPTION_ORO:
case SD_DHCP6_OPTION_PREFERENCE:
case SD_DHCP6_OPTION_ELAPSED_TIME:
case SD_DHCP6_OPTION_RELAY_MSG:
case SD_DHCP6_OPTION_AUTH:
case SD_DHCP6_OPTION_UNICAST:
case SD_DHCP6_OPTION_STATUS_CODE:
case SD_DHCP6_OPTION_RAPID_COMMIT:
case SD_DHCP6_OPTION_USER_CLASS:
case SD_DHCP6_OPTION_VENDOR_CLASS:
return false;
case SD_DHCP6_OPTION_VENDOR_OPTS:
return true;
case SD_DHCP6_OPTION_INTERFACE_ID:
case SD_DHCP6_OPTION_RECONF_MSG:
case SD_DHCP6_OPTION_RECONF_ACCEPT:
return false;
case SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME:
case SD_DHCP6_OPTION_SIP_SERVER_ADDRESS:
case SD_DHCP6_OPTION_DNS_SERVERS:
case SD_DHCP6_OPTION_DOMAIN_LIST:
return true;
case SD_DHCP6_OPTION_IA_PD:
case SD_DHCP6_OPTION_IA_PD_PREFIX:
return false;
case SD_DHCP6_OPTION_NIS_SERVERS:
case SD_DHCP6_OPTION_NISP_SERVERS:
case SD_DHCP6_OPTION_NIS_DOMAIN_NAME:
case SD_DHCP6_OPTION_NISP_DOMAIN_NAME:
case SD_DHCP6_OPTION_SNTP_SERVERS:
case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
case SD_DHCP6_OPTION_BCMCS_SERVER_D:
case SD_DHCP6_OPTION_BCMCS_SERVER_A:
case SD_DHCP6_OPTION_GEOCONF_CIVIC:
return true;
case SD_DHCP6_OPTION_REMOTE_ID:
case SD_DHCP6_OPTION_SUBSCRIBER_ID:
return false;
case SD_DHCP6_OPTION_CLIENT_FQDN:
case SD_DHCP6_OPTION_PANA_AGENT:
case SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE:
case SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE:
return true;
case SD_DHCP6_OPTION_ERO:
case SD_DHCP6_OPTION_LQ_QUERY:
case SD_DHCP6_OPTION_CLIENT_DATA:
case SD_DHCP6_OPTION_CLT_TIME:
case SD_DHCP6_OPTION_LQ_RELAY_DATA:
case SD_DHCP6_OPTION_LQ_CLIENT_LINK:
return false;
case SD_DHCP6_OPTION_MIP6_HNIDF:
case SD_DHCP6_OPTION_MIP6_VDINF:
case SD_DHCP6_OPTION_V6_LOST:
case SD_DHCP6_OPTION_CAPWAP_AC_V6:
return true;
case SD_DHCP6_OPTION_RELAY_ID:
return false;
case SD_DHCP6_OPTION_IPV6_ADDRESS_MOS:
case SD_DHCP6_OPTION_IPV6_FQDN_MOS:
case SD_DHCP6_OPTION_NTP_SERVER:
case SD_DHCP6_OPTION_V6_ACCESS_DOMAIN:
case SD_DHCP6_OPTION_SIP_UA_CS_LIST:
case SD_DHCP6_OPTION_BOOTFILE_URL:
case SD_DHCP6_OPTION_BOOTFILE_PARAM:
return true;
case SD_DHCP6_OPTION_CLIENT_ARCH_TYPE:
return false;
case SD_DHCP6_OPTION_NII:
case SD_DHCP6_OPTION_GEOLOCATION:
case SD_DHCP6_OPTION_AFTR_NAME:
case SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME:
return true;
case SD_DHCP6_OPTION_RSOO:
return false;
case SD_DHCP6_OPTION_PD_EXCLUDE:
return true;
case SD_DHCP6_OPTION_VSS:
return false;
case SD_DHCP6_OPTION_MIP6_IDINF:
case SD_DHCP6_OPTION_MIP6_UDINF:
case SD_DHCP6_OPTION_MIP6_HNP:
case SD_DHCP6_OPTION_MIP6_HAA:
case SD_DHCP6_OPTION_MIP6_HAF:
case SD_DHCP6_OPTION_RDNSS_SELECTION:
case SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME:
case SD_DHCP6_OPTION_KRB_REALM_NAME:
case SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME:
case SD_DHCP6_OPTION_KRB_KDC:
return true;
case SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR:
case SD_DHCP6_OPTION_LINK_ADDRESS:
case SD_DHCP6_OPTION_RADIUS:
return false;
case SD_DHCP6_OPTION_SOL_MAX_RT:
case SD_DHCP6_OPTION_INF_MAX_RT:
case SD_DHCP6_OPTION_ADDRSEL:
case SD_DHCP6_OPTION_ADDRSEL_TABLE:
case SD_DHCP6_OPTION_V6_PCP_SERVER:
return true;
case SD_DHCP6_OPTION_DHCPV4_MSG:
return false;
case SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER:
return true;
case SD_DHCP6_OPTION_S46_RULE:
return false;
case SD_DHCP6_OPTION_S46_BR:
return true;
case SD_DHCP6_OPTION_S46_DMR:
case SD_DHCP6_OPTION_S46_V4V6BIND:
case SD_DHCP6_OPTION_S46_PORTPARAMS:
return false;
case SD_DHCP6_OPTION_S46_CONT_MAPE:
case SD_DHCP6_OPTION_S46_CONT_MAPT:
case SD_DHCP6_OPTION_S46_CONT_LW:
case SD_DHCP6_OPTION_4RD:
case SD_DHCP6_OPTION_4RD_MAP_RULE:
case SD_DHCP6_OPTION_4RD_NON_MAP_RULE:
return true;
case SD_DHCP6_OPTION_LQ_BASE_TIME:
case SD_DHCP6_OPTION_LQ_START_TIME:
case SD_DHCP6_OPTION_LQ_END_TIME:
return false;
case SD_DHCP6_OPTION_CAPTIVE_PORTAL:
case SD_DHCP6_OPTION_MPL_PARAMETERS:
return true;
case SD_DHCP6_OPTION_ANI_ATT:
case SD_DHCP6_OPTION_ANI_NETWORK_NAME:
case SD_DHCP6_OPTION_ANI_AP_NAME:
case SD_DHCP6_OPTION_ANI_AP_BSSID:
case SD_DHCP6_OPTION_ANI_OPERATOR_ID:
case SD_DHCP6_OPTION_ANI_OPERATOR_REALM:
return false;
case SD_DHCP6_OPTION_S46_PRIORITY:
return true;
case SD_DHCP6_OPTION_MUD_URL_V6:
return false;
case SD_DHCP6_OPTION_V6_PREFIX64:
return true;
case SD_DHCP6_OPTION_F_BINDING_STATUS:
case SD_DHCP6_OPTION_F_CONNECT_FLAGS:
case SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO:
case SD_DHCP6_OPTION_F_DNS_HOST_NAME:
case SD_DHCP6_OPTION_F_DNS_ZONE_NAME:
case SD_DHCP6_OPTION_F_DNS_FLAGS:
case SD_DHCP6_OPTION_F_EXPIRATION_TIME:
case SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD:
case SD_DHCP6_OPTION_F_MCLT:
case SD_DHCP6_OPTION_F_PARTNER_LIFETIME:
case SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT:
case SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME:
case SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME:
case SD_DHCP6_OPTION_F_PROTOCOL_VERSION:
case SD_DHCP6_OPTION_F_KEEPALIVE_TIME:
case SD_DHCP6_OPTION_F_RECONFIGURE_DATA:
case SD_DHCP6_OPTION_F_RELATIONSHIP_NAME:
case SD_DHCP6_OPTION_F_SERVER_FLAGS:
case SD_DHCP6_OPTION_F_SERVER_STATE:
case SD_DHCP6_OPTION_F_START_TIME_OF_STATE:
case SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME:
case SD_DHCP6_OPTION_RELAY_PORT:
return false;
case SD_DHCP6_OPTION_V6_SZTP_REDIRECT:
case SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX:
return true;
case SD_DHCP6_OPTION_IA_LL:
case SD_DHCP6_OPTION_LLADDR:
case SD_DHCP6_OPTION_SLAP_QUAD:
return false;
case SD_DHCP6_OPTION_V6_DOTS_RI:
case SD_DHCP6_OPTION_V6_DOTS_ADDRESS:
case SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF:
return true;
default:
return false;
}
}
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) {
DHCP6Option *option; DHCP6Option *option;
@ -275,7 +462,7 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
if (dns_name_is_single_label(fqdn)) if (dns_name_is_single_label(fqdn))
r--; r--;
r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_FQDN, 1 + r, buffer); r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_CLIENT_FQDN, 1 + r, buffer);
return r; return r;
} }

View file

@ -5,6 +5,7 @@
Copyright © 2014 Intel Corporation. All rights reserved. Copyright © 2014 Intel Corporation. All rights reserved.
***/ ***/
#include <errno.h>
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet/udp.h> #include <netinet/udp.h>
@ -36,57 +37,83 @@ enum {
DHCP6_PORT_CLIENT = 546, DHCP6_PORT_CLIENT = 546,
}; };
#define DHCP6_INF_TIMEOUT 1 * USEC_PER_SEC #define DHCP6_INF_TIMEOUT (1 * USEC_PER_SEC)
#define DHCP6_INF_MAX_RT 120 * USEC_PER_SEC #define DHCP6_INF_MAX_RT (120 * USEC_PER_SEC)
#define DHCP6_SOL_MAX_DELAY 1 * USEC_PER_SEC #define DHCP6_SOL_MAX_DELAY (1 * USEC_PER_SEC)
#define DHCP6_SOL_TIMEOUT 1 * USEC_PER_SEC #define DHCP6_SOL_TIMEOUT (1 * USEC_PER_SEC)
#define DHCP6_SOL_MAX_RT 120 * USEC_PER_SEC #define DHCP6_SOL_MAX_RT (120 * USEC_PER_SEC)
#define DHCP6_REQ_TIMEOUT 1 * USEC_PER_SEC #define DHCP6_REQ_TIMEOUT (1 * USEC_PER_SEC)
#define DHCP6_REQ_MAX_RT 120 * USEC_PER_SEC #define DHCP6_REQ_MAX_RT (120 * USEC_PER_SEC)
#define DHCP6_REQ_MAX_RC 10 #define DHCP6_REQ_MAX_RC 10
#define DHCP6_REN_TIMEOUT 10 * USEC_PER_SEC #define DHCP6_REN_TIMEOUT (10 * USEC_PER_SEC)
#define DHCP6_REN_MAX_RT 600 * USEC_PER_SEC #define DHCP6_REN_MAX_RT (600 * USEC_PER_SEC)
#define DHCP6_REB_TIMEOUT 10 * USEC_PER_SEC #define DHCP6_REB_TIMEOUT (10 * USEC_PER_SEC)
#define DHCP6_REB_MAX_RT 600 * USEC_PER_SEC #define DHCP6_REB_MAX_RT (600 * USEC_PER_SEC)
enum DHCP6State { typedef enum DHCP6State {
DHCP6_STATE_STOPPED = 0, DHCP6_STATE_STOPPED,
DHCP6_STATE_INFORMATION_REQUEST = 1, DHCP6_STATE_INFORMATION_REQUEST,
DHCP6_STATE_SOLICITATION = 2, DHCP6_STATE_SOLICITATION,
DHCP6_STATE_REQUEST = 3, DHCP6_STATE_REQUEST,
DHCP6_STATE_BOUND = 4, DHCP6_STATE_BOUND,
DHCP6_STATE_RENEW = 5, DHCP6_STATE_RENEW,
DHCP6_STATE_REBIND = 6, DHCP6_STATE_REBIND,
}; _DHCP6_STATE_MAX,
_DHCP6_STATE_INVALID = -EINVAL,
} DHCP6State;
enum { /* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-1 */
DHCP6_SOLICIT = 1, typedef enum DHCP6MessageType {
DHCP6_ADVERTISE = 2, DHCP6_MESSAGE_SOLICIT = 1, /* RFC 8415 */
DHCP6_REQUEST = 3, DHCP6_MESSAGE_ADVERTISE = 2, /* RFC 8415 */
DHCP6_CONFIRM = 4, DHCP6_MESSAGE_REQUEST = 3, /* RFC 8415 */
DHCP6_RENEW = 5, DHCP6_MESSAGE_CONFIRM = 4, /* RFC 8415 */
DHCP6_REBIND = 6, DHCP6_MESSAGE_RENEW = 5, /* RFC 8415 */
DHCP6_REPLY = 7, DHCP6_MESSAGE_REBIND = 6, /* RFC 8415 */
DHCP6_RELEASE = 8, DHCP6_MESSAGE_REPLY = 7, /* RFC 8415 */
DHCP6_DECLINE = 9, DHCP6_MESSAGE_RELEASE = 8, /* RFC 8415 */
DHCP6_RECONFIGURE = 10, DHCP6_MESSAGE_DECLINE = 9, /* RFC 8415 */
DHCP6_INFORMATION_REQUEST = 11, DHCP6_MESSAGE_RECONFIGURE = 10, /* RFC 8415 */
DHCP6_RELAY_FORW = 12, DHCP6_MESSAGE_INFORMATION_REQUEST = 11, /* RFC 8415 */
DHCP6_RELAY_REPL = 13, DHCP6_MESSAGE_RELAY_FORWARD = 12, /* RFC 8415 */
_DHCP6_MESSAGE_MAX = 14, DHCP6_MESSAGE_RELAY_REPLY = 13, /* RFC 8415 */
}; DHCP6_MESSAGE_LEASE_QUERY = 14, /* RFC 5007 */
DHCP6_MESSAGE_LEASE_QUERY_REPLY = 15, /* RFC 5007 */
DHCP6_MESSAGE_LEASE_QUERY_DONE = 16, /* RFC 5460 */
DHCP6_MESSAGE_LEASE_QUERY_DATA = 17, /* RFC 5460 */
DHCP6_MESSAGE_RECONFIGURE_REQUEST = 18, /* RFC 6977 */
DHCP6_MESSAGE_RECONFIGURE_REPLY = 19, /* RFC 6977 */
DHCP6_MESSAGE_DHCPV4_QUERY = 20, /* RFC 7341 */
DHCP6_MESSAGE_DHCPV4_RESPONSE = 21, /* RFC 7341 */
DHCP6_MESSAGE_ACTIVE_LEASE_QUERY = 22, /* RFC 7653 */
DHCP6_MESSAGE_START_TLS = 23, /* RFC 7653 */
DHCP6_MESSAGE_BINDING_UPDATE = 24, /* RFC 8156 */
DHCP6_MESSAGE_BINDING_REPLY = 25, /* RFC 8156 */
DHCP6_MESSAGE_POOL_REQUEST = 26, /* RFC 8156 */
DHCP6_MESSAGE_POOL_RESPONSE = 27, /* RFC 8156 */
DHCP6_MESSAGE_UPDATE_REQUEST = 28, /* RFC 8156 */
DHCP6_MESSAGE_UPDATE_REQUEST_ALL = 29, /* RFC 8156 */
DHCP6_MESSAGE_UPDATE_DONE = 30, /* RFC 8156 */
DHCP6_MESSAGE_CONNECT = 31, /* RFC 8156 */
DHCP6_MESSAGE_CONNECT_REPLY = 32, /* RFC 8156 */
DHCP6_MESSAGE_DISCONNECT = 33, /* RFC 8156 */
DHCP6_MESSAGE_STATE = 34, /* RFC 8156 */
DHCP6_MESSAGE_CONTACT = 35, /* RFC 8156 */
_DHCP6_MESSAGE_TYPE_MAX,
_DHCP6_MESSAGE_TYPE_INVALID = -EINVAL,
} DHCP6MessageType;
enum { typedef enum DHCP6NTPSubOption {
DHCP6_NTP_SUBOPTION_SRV_ADDR = 1, DHCP6_NTP_SUBOPTION_SRV_ADDR = 1,
DHCP6_NTP_SUBOPTION_MC_ADDR = 2, DHCP6_NTP_SUBOPTION_MC_ADDR = 2,
DHCP6_NTP_SUBOPTION_SRV_FQDN = 3, DHCP6_NTP_SUBOPTION_SRV_FQDN = 3,
}; } DHCP6NTPSubOption;
/* /*
* RFC 8415, RFC 5007 and RFC 7653 status codes: * RFC 8415, RFC 5007 and RFC 7653 status codes:
* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-5 * https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-5
*/ */
enum { typedef enum DHCP6Status {
DHCP6_STATUS_SUCCESS = 0, DHCP6_STATUS_SUCCESS = 0,
DHCP6_STATUS_UNSPEC_FAIL = 1, DHCP6_STATUS_UNSPEC_FAIL = 1,
DHCP6_STATUS_NO_ADDRS_AVAIL = 2, DHCP6_STATUS_NO_ADDRS_AVAIL = 2,
@ -110,11 +137,12 @@ enum {
DHCP6_STATUS_SERVER_SHUTTING_DOWN = 20, DHCP6_STATUS_SERVER_SHUTTING_DOWN = 20,
DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED = 21, DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED = 21,
DHCP6_STATUS_EXCESSIVE_TIME_SKEW = 22, DHCP6_STATUS_EXCESSIVE_TIME_SKEW = 22,
_DHCP6_STATUS_MAX = 23, _DHCP6_STATUS_MAX,
}; _DHCP6_STATUS_INVALID = -EINVAL,
} DHCP6Status;
enum { typedef enum DHCP6FQDNFlag {
DHCP6_FQDN_FLAG_S = (1 << 0), DHCP6_FQDN_FLAG_S = 1 << 0,
DHCP6_FQDN_FLAG_O = (1 << 1), DHCP6_FQDN_FLAG_O = 1 << 1,
DHCP6_FQDN_FLAG_N = (1 << 2), DHCP6_FQDN_FLAG_N = 1 << 2,
}; } DHCP6FQDNFlag;

View file

@ -47,7 +47,7 @@ int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
return CMP(x->until, y->until); return CMP(x->until, y->until);
} }
_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) { sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
if (!n) if (!n)
return NULL; return NULL;
@ -72,7 +72,7 @@ static sd_lldp_neighbor *lldp_neighbor_free(sd_lldp_neighbor *n) {
return mfree(n); return mfree(n);
} }
_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) { sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
/* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
* the sd_lldp object. */ * the sd_lldp object. */
@ -360,7 +360,7 @@ bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0; return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
} }
_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) { int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert_return(address, -EINVAL); assert_return(address, -EINVAL);
@ -368,7 +368,7 @@ _public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct eth
return 0; return 0;
} }
_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) { int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert_return(address, -EINVAL); assert_return(address, -EINVAL);
@ -376,7 +376,7 @@ _public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struc
return 0; return 0;
} }
_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
assert_return(size, -EINVAL); assert_return(size, -EINVAL);
@ -387,7 +387,7 @@ _public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, siz
return 0; return 0;
} }
_public_ 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(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert_return(type, -EINVAL); assert_return(type, -EINVAL);
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
@ -440,7 +440,7 @@ static int format_network_address(const void *data, size_t sz, char **ret) {
return 1; return 1;
} }
_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) { int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
char *k; char *k;
int r; int r;
@ -496,7 +496,7 @@ done:
return 0; return 0;
} }
_public_ 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(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert_return(type, -EINVAL); assert_return(type, -EINVAL);
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
@ -511,7 +511,7 @@ _public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, co
return 0; return 0;
} }
_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) { int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
char *k; char *k;
int r; int r;
@ -566,7 +566,7 @@ done:
return 0; return 0;
} }
_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) { int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert_return(ret_sec, -EINVAL); assert_return(ret_sec, -EINVAL);
@ -574,7 +574,7 @@ _public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
return 0; return 0;
} }
_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) { int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
@ -585,7 +585,7 @@ _public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **
return 0; return 0;
} }
_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) { int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
@ -596,7 +596,7 @@ _public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const
return 0; return 0;
} }
_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) { int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
@ -607,7 +607,7 @@ _public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const ch
return 0; return 0;
} }
_public_ int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) { int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
@ -618,7 +618,7 @@ _public_ int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret)
return 0; return 0;
} }
_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
@ -629,7 +629,7 @@ _public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint1
return 0; return 0;
} }
_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
@ -640,7 +640,7 @@ _public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint
return 0; return 0;
} }
_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) { int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
int r; int r;
@ -661,7 +661,7 @@ _public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw,
return r; return r;
} }
_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) { int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert(n->raw_size >= sizeof(struct ether_header)); assert(n->raw_size >= sizeof(struct ether_header));
@ -670,7 +670,7 @@ _public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
return n->rindex < n->raw_size; return n->rindex < n->raw_size;
} }
_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) { int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
size_t length; size_t length;
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
@ -689,7 +689,7 @@ _public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
return n->rindex < n->raw_size; return n->rindex < n->raw_size;
} }
_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) { int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert_return(type, -EINVAL); assert_return(type, -EINVAL);
@ -703,7 +703,7 @@ _public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
return 0; return 0;
} }
_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) { int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
uint8_t k; uint8_t k;
int r; int r;
@ -716,7 +716,7 @@ _public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
return type == k; return type == k;
} }
_public_ 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_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) {
const uint8_t *d; const uint8_t *d;
size_t length; size_t length;
int r; int r;
@ -745,7 +745,7 @@ _public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_A
return 0; return 0;
} }
_public_ 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_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) {
uint8_t k[3], st; uint8_t k[3], st;
int r; int r;
@ -758,7 +758,7 @@ _public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[
return memcmp(k, oui, 3) == 0 && st == subtype; return memcmp(k, oui, 3) == 0 && st == subtype;
} }
_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
size_t length; size_t length;
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
@ -780,7 +780,7 @@ _public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret,
return 0; return 0;
} }
_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) { int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
assert_return(n, -EINVAL); assert_return(n, -EINVAL);
assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP); assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
assert_return(clock_supported(clock), -EOPNOTSUPP); assert_return(clock_supported(clock), -EOPNOTSUPP);

View file

@ -11,7 +11,6 @@
#include "socket-util.h" #include "socket-util.h"
int lldp_network_bind_raw_socket(int ifindex) { int lldp_network_bind_raw_socket(int ifindex) {
static const struct sock_filter filter[] = { static const struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */
@ -26,26 +25,21 @@ int lldp_network_bind_raw_socket(int ifindex) {
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept packet */ BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept packet */
}; };
static const struct sock_fprog fprog = { static const struct sock_fprog fprog = {
.len = ELEMENTSOF(filter), .len = ELEMENTSOF(filter),
.filter = (struct sock_filter*) filter, .filter = (struct sock_filter*) filter,
}; };
struct packet_mreq mreq = { struct packet_mreq mreq = {
.mr_ifindex = ifindex, .mr_ifindex = ifindex,
.mr_type = PACKET_MR_MULTICAST, .mr_type = PACKET_MR_MULTICAST,
.mr_alen = ETH_ALEN, .mr_alen = ETH_ALEN,
.mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 } .mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 }
}; };
union sockaddr_union saddrll = { union sockaddr_union saddrll = {
.ll.sll_family = AF_PACKET, .ll.sll_family = AF_PACKET,
.ll.sll_ifindex = ifindex, .ll.sll_ifindex = ifindex,
}; };
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
int r;
assert(ifindex > 0); assert(ifindex > 0);
@ -54,29 +48,24 @@ int lldp_network_bind_raw_socket(int ifindex) {
if (fd < 0) if (fd < 0)
return -errno; return -errno;
r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0)
if (r < 0)
return -errno; return -errno;
/* customer bridge */ /* customer bridge */
r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
if (r < 0)
return -errno; return -errno;
/* non TPMR bridge */ /* non TPMR bridge */
mreq.mr_address[ETH_ALEN - 1] = 0x03; mreq.mr_address[ETH_ALEN - 1] = 0x03;
r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
if (r < 0)
return -errno; return -errno;
/* nearest bridge */ /* nearest bridge */
mreq.mr_address[ETH_ALEN - 1] = 0x0E; mreq.mr_address[ETH_ALEN - 1] = 0x0E;
r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
if (r < 0)
return -errno; return -errno;
r = bind(fd, &saddrll.sa, sizeof(saddrll.ll)); if (bind(fd, &saddrll.sa, sizeof(saddrll.ll)) < 0)
if (r < 0)
return -errno; return -errno;
return TAKE_FD(fd); return TAKE_FD(fd);

View file

@ -8,8 +8,10 @@
int _e = (error); \ int _e = (error); \
if (DEBUG_LOGGING) { \ if (DEBUG_LOGGING) { \
const char *_n = NULL; \ const char *_n = NULL; \
type *_v = (val); \
\ \
(void) type##_get_ifname(val, &_n); \ if (_v) \
(void) type##_get_ifname(_v, &_n); \
log_interface_full_errno_zerook( \ log_interface_full_errno_zerook( \
_n, LOG_DEBUG, _e, prefix fmt, \ _n, LOG_DEBUG, _e, prefix fmt, \
##__VA_ARGS__); \ ##__VA_ARGS__); \

View file

@ -837,7 +837,8 @@ static int client_message_init(
return -ENOMEM; return -ENOMEM;
r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type, r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
client->arp_type, optlen, &optoffset); client->arp_type, client->mac_addr_len, client->mac_addr,
optlen, &optoffset);
if (r < 0) if (r < 0)
return r; return r;
@ -853,7 +854,7 @@ static int client_message_init(
secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1; secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
packet->dhcp.secs = htobe16(secs); packet->dhcp.secs = htobe16(secs);
/* RFC2132 section 4.1 /* RFC2131 section 4.1
A client that cannot receive unicast IP datagrams until its protocol A client that cannot receive unicast IP datagrams until its protocol
software has been configured with an IP address SHOULD set the software has been configured with an IP address SHOULD set the
BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
@ -867,15 +868,6 @@ static int client_message_init(
if (client->request_broadcast || client->arp_type != ARPHRD_ETHER) if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
packet->dhcp.flags = htobe16(0x8000); packet->dhcp.flags = htobe16(0x8000);
/* RFC2132 section 4.1.1:
The client MUST include its hardware address in the chaddr field, if
necessary for delivery of DHCP reply messages. Non-Ethernet
interfaces will leave 'chaddr' empty and use the client identifier
instead (eg, RFC 4390 section 2.1).
*/
if (client->arp_type == ARPHRD_ETHER)
memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
/* If no client identifier exists, construct an RFC 4361-compliant one */ /* If no client identifier exists, construct an RFC 4361-compliant one */
if (client->client_id_len == 0) { if (client->client_id_len == 0) {
size_t duid_len; size_t duid_len;
@ -1935,13 +1927,13 @@ static int client_receive_message_udp(
assert(client); assert(client);
buflen = next_datagram_size_fd(fd); buflen = next_datagram_size_fd(fd);
if (buflen == -ENETDOWN) if (buflen < 0) {
/* the link is down. Don't return an error or the I/O event if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
source will be disconnected and we won't be able to receive return 0;
packets again when the link comes back. */
log_dhcp_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
return 0; return 0;
if (buflen < 0) }
return buflen;
message = malloc0(buflen); message = malloc0(buflen);
if (!message) if (!message)
@ -1949,12 +1941,11 @@ static int client_receive_message_udp(
len = recv(fd, message, buflen, 0); len = recv(fd, message, buflen, 0);
if (len < 0) { if (len < 0) {
/* see comment above for why we shouldn't error out on ENETDOWN. */ if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
return 0; return 0;
return log_dhcp_client_errno(client, errno, log_dhcp_client_errno(client, errno, "Could not receive message from UDP socket, ignoring: %m");
"Could not receive message from UDP socket: %m"); return 0;
} }
if ((size_t) len < sizeof(DHCPMessage)) { if ((size_t) len < sizeof(DHCPMessage)) {
log_dhcp_client(client, "Too small to be a DHCP message: ignoring"); log_dhcp_client(client, "Too small to be a DHCP message: ignoring");
@ -2000,7 +1991,9 @@ static int client_receive_message_udp(
return 0; return 0;
} }
return client_handle_message(client, message, len); log_dhcp_client(client, "Received message from UDP socket, processing.");
(void) client_handle_message(client, message, len);
return 0;
} }
static int client_receive_message_raw( static int client_receive_message_raw(
@ -2028,10 +2021,13 @@ static int client_receive_message_raw(
assert(client); assert(client);
buflen = next_datagram_size_fd(fd); buflen = next_datagram_size_fd(fd);
if (buflen == -ENETDOWN) if (buflen < 0) {
if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
return 0;
log_dhcp_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
return 0; return 0;
if (buflen < 0) }
return buflen;
packet = malloc0(buflen); packet = malloc0(buflen);
if (!packet) if (!packet)
@ -2040,12 +2036,13 @@ static int client_receive_message_raw(
iov = IOVEC_MAKE(packet, buflen); iov = IOVEC_MAKE(packet, buflen);
len = recvmsg_safe(fd, &msg, 0); len = recvmsg_safe(fd, &msg, 0);
if (IN_SET(len, -EAGAIN, -EINTR, -ENETDOWN)) if (len < 0) {
return 0; if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len))
if (len < 0) return 0;
return log_dhcp_client_errno(client, len,
"Could not receive message from raw socket: %m");
log_dhcp_client_errno(client, len, "Could not receive message from raw socket, ignoring: %m");
return 0;
}
if ((size_t) len < sizeof(DHCPPacket)) if ((size_t) len < sizeof(DHCPPacket))
return 0; return 0;
@ -2061,7 +2058,9 @@ static int client_receive_message_raw(
len -= DHCP_IP_UDP_SIZE; len -= DHCP_IP_UDP_SIZE;
return client_handle_message(client, &packet->dhcp, len); log_dhcp_client(client, "Received message from RAW socket, processing.");
(void) client_handle_message(client, &packet->dhcp, len);
return 0;
} }
int sd_dhcp_client_send_renew(sd_dhcp_client *client) { int sd_dhcp_client_send_renew(sd_dhcp_client *client) {

View file

@ -107,12 +107,13 @@ int sd_dhcp_lease_get_servers(
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(what >= 0, -EINVAL); assert_return(what >= 0, -EINVAL);
assert_return(what < _SD_DHCP_LEASE_SERVER_TYPE_MAX, -EINVAL); assert_return(what < _SD_DHCP_LEASE_SERVER_TYPE_MAX, -EINVAL);
assert_return(addr, -EINVAL);
if (lease->servers[what].size <= 0) if (lease->servers[what].size <= 0)
return -ENODATA; return -ENODATA;
*addr = lease->servers[what].addr; if (addr)
*addr = lease->servers[what].addr;
return (int) lease->servers[what].size; return (int) lease->servers[what].size;
} }
@ -252,6 +253,33 @@ int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) {
return -ENODATA; return -ENODATA;
} }
int sd_dhcp_lease_get_6rd(
sd_dhcp_lease *lease,
uint8_t *ret_ipv4masklen,
uint8_t *ret_prefixlen,
struct in6_addr *ret_prefix,
const struct in_addr **ret_br_addresses,
size_t *ret_n_br_addresses) {
assert_return(lease, -EINVAL);
if (lease->sixrd_n_br_addresses <= 0)
return -ENODATA;
if (ret_ipv4masklen)
*ret_ipv4masklen = lease->sixrd_ipv4masklen;
if (ret_prefixlen)
*ret_prefixlen = lease->sixrd_prefixlen;
if (ret_prefix)
*ret_prefix = lease->sixrd_prefix;
if (ret_br_addresses)
*ret_br_addresses = lease->sixrd_br_addresses;
if (ret_n_br_addresses)
*ret_n_br_addresses = lease->sixrd_n_br_addresses;
return 0;
}
int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) { int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(data, -EINVAL); assert_return(data, -EINVAL);
@ -290,6 +318,7 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
free(lease->client_id); free(lease->client_id);
free(lease->vendor_specific); free(lease->vendor_specific);
strv_free(lease->search_domains); strv_free(lease->search_domains);
free(lease->sixrd_br_addresses);
return mfree(lease); return mfree(lease);
} }
@ -535,6 +564,61 @@ static int lease_parse_classless_routes(
return 0; return 0;
} }
static int lease_parse_6rd(sd_dhcp_lease *lease, const uint8_t *option, size_t len) {
uint8_t ipv4masklen, prefixlen;
struct in6_addr prefix;
_cleanup_free_ struct in_addr *br_addresses = NULL;
size_t n_br_addresses;
assert(lease);
assert(option);
/* See RFC 5969 Section 7.1.1 */
if (lease->sixrd_n_br_addresses > 0)
/* Multiple 6rd option?? */
return -EINVAL;
/* option-length: The length of the DHCP option in octets (22 octets with one BR IPv4 address). */
if (len < 2 + sizeof(struct in6_addr) + sizeof(struct in_addr) ||
(len - 2 - sizeof(struct in6_addr)) % sizeof(struct in_addr) != 0)
return -EINVAL;
/* IPv4MaskLen: The number of high-order bits that are identical across all CE IPv4 addresses
* within a given 6rd domain. This may be any value between 0 and 32. Any value
* greater than 32 is invalid. */
ipv4masklen = option[0];
if (ipv4masklen > 32)
return -EINVAL;
/* 6rdPrefixLen: The IPv6 prefix length of the SP's 6rd IPv6 prefix in number of bits. For the
* purpose of bounds checking by DHCP option processing, the sum of
* (32 - IPv4MaskLen) + 6rdPrefixLen MUST be less than or equal to 128. */
prefixlen = option[1];
if (32 - ipv4masklen + prefixlen > 128)
return -EINVAL;
/* 6rdPrefix: The service provider's 6rd IPv6 prefix represented as a 16-octet IPv6 address.
* The bits in the prefix after the 6rdPrefixlen number of bits are reserved and
* MUST be initialized to zero by the sender and ignored by the receiver. */
memcpy(&prefix, option + 2, sizeof(struct in6_addr));
(void) in6_addr_mask(&prefix, prefixlen);
/* 6rdBRIPv4Address: One or more IPv4 addresses of the 6rd Border Relay(s) for a given 6rd domain. */
n_br_addresses = (len - 2 - sizeof(struct in6_addr)) / sizeof(struct in_addr);
br_addresses = newdup(struct in_addr, option + 2 + sizeof(struct in6_addr), n_br_addresses);
if (!br_addresses)
return -ENOMEM;
lease->sixrd_ipv4masklen = ipv4masklen;
lease->sixrd_prefixlen = prefixlen;
lease->sixrd_prefix = prefix;
lease->sixrd_br_addresses = TAKE_PTR(br_addresses);
lease->sixrd_n_br_addresses = n_br_addresses;
return 0;
}
int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) { int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
sd_dhcp_lease *lease = userdata; sd_dhcp_lease *lease = userdata;
int r; int r;
@ -693,7 +777,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
} }
if (!timezone_is_valid(tz, LOG_DEBUG)) { if (!timezone_is_valid(tz, LOG_DEBUG)) {
log_debug_errno(r, "Timezone is not valid, ignoring: %m"); log_debug("Timezone is not valid, ignoring.");
return 0; return 0;
} }
@ -720,6 +804,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
lease->vendor_specific_len = len; lease->vendor_specific_len = len;
break; break;
case SD_DHCP_OPTION_6RD:
r = lease_parse_6rd(lease, option, len);
if (r < 0)
log_debug_errno(r, "Failed to parse 6rd option, ignoring: %m");
break;
case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST: case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST:
r = dhcp_lease_insert_private_option(lease, code, option, len); r = dhcp_lease_insert_private_option(lease, code, option, len);
if (r < 0) if (r < 0)

View file

@ -27,6 +27,7 @@
#include "hexdecoct.h" #include "hexdecoct.h"
#include "hostname-util.h" #include "hostname-util.h"
#include "in-addr-util.h" #include "in-addr-util.h"
#include "io-util.h"
#include "network-common.h" #include "network-common.h"
#include "random-util.h" #include "random-util.h"
#include "socket-util.h" #include "socket-util.h"
@ -41,16 +42,16 @@
#define IRT_MINIMUM (600 * USEC_PER_SEC) #define IRT_MINIMUM (600 * USEC_PER_SEC)
/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */ /* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
enum { typedef enum DHCP6RequestIA {
DHCP6_REQUEST_IA_NA = 1, DHCP6_REQUEST_IA_NA = 1 << 0,
DHCP6_REQUEST_IA_TA = 2, /* currently not used */ DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */
DHCP6_REQUEST_IA_PD = 4, DHCP6_REQUEST_IA_PD = 1 << 2,
}; } DHCP6RequestIA;
struct sd_dhcp6_client { struct sd_dhcp6_client {
unsigned n_ref; unsigned n_ref;
enum DHCP6State state; DHCP6State state;
sd_event *event; sd_event *event;
int event_priority; int event_priority;
int ifindex; int ifindex;
@ -64,7 +65,7 @@ struct sd_dhcp6_client {
DHCP6IA ia_pd; DHCP6IA ia_pd;
sd_event_source *timeout_t1; sd_event_source *timeout_t1;
sd_event_source *timeout_t2; sd_event_source *timeout_t2;
unsigned request; DHCP6RequestIA request_ia;
be32_t transaction_id; be32_t transaction_id;
usec_t transaction_start; usec_t transaction_start;
struct sd_dhcp6_lease *lease; struct sd_dhcp6_lease *lease;
@ -102,20 +103,42 @@ static const uint16_t default_req_opts[] = {
SD_DHCP6_OPTION_SNTP_SERVERS, SD_DHCP6_OPTION_SNTP_SERVERS,
}; };
const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = { const char * dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = {
[DHCP6_SOLICIT] = "SOLICIT", [DHCP6_MESSAGE_SOLICIT] = "Solicit",
[DHCP6_ADVERTISE] = "ADVERTISE", [DHCP6_MESSAGE_ADVERTISE] = "Advertise",
[DHCP6_REQUEST] = "REQUEST", [DHCP6_MESSAGE_REQUEST] = "Request",
[DHCP6_CONFIRM] = "CONFIRM", [DHCP6_MESSAGE_CONFIRM] = "Confirm",
[DHCP6_RENEW] = "RENEW", [DHCP6_MESSAGE_RENEW] = "Renew",
[DHCP6_REBIND] = "REBIND", [DHCP6_MESSAGE_REBIND] = "Rebind",
[DHCP6_REPLY] = "REPLY", [DHCP6_MESSAGE_REPLY] = "Reply",
[DHCP6_RELEASE] = "RELEASE", [DHCP6_MESSAGE_RELEASE] = "Release",
[DHCP6_DECLINE] = "DECLINE", [DHCP6_MESSAGE_DECLINE] = "Decline",
[DHCP6_RECONFIGURE] = "RECONFIGURE", [DHCP6_MESSAGE_RECONFIGURE] = "Reconfigure",
[DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST", [DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request",
[DHCP6_RELAY_FORW] = "RELAY-FORW", [DHCP6_MESSAGE_RELAY_FORWARD] = "Relay Forward",
[DHCP6_RELAY_REPL] = "RELAY-REPL", [DHCP6_MESSAGE_RELAY_REPLY] = "Relay Reply",
[DHCP6_MESSAGE_LEASE_QUERY] = "Lease Query",
[DHCP6_MESSAGE_LEASE_QUERY_REPLY] = "Lease Query Reply",
[DHCP6_MESSAGE_LEASE_QUERY_DONE] = "Lease Query Done",
[DHCP6_MESSAGE_LEASE_QUERY_DATA] = "Lease Query Data",
[DHCP6_MESSAGE_RECONFIGURE_REQUEST] = "Reconfigure Request",
[DHCP6_MESSAGE_RECONFIGURE_REPLY] = "Reconfigure Reply",
[DHCP6_MESSAGE_DHCPV4_QUERY] = "DHCPv4 Query",
[DHCP6_MESSAGE_DHCPV4_RESPONSE] = "DHCPv4 Response",
[DHCP6_MESSAGE_ACTIVE_LEASE_QUERY] = "Active Lease Query",
[DHCP6_MESSAGE_START_TLS] = "Start TLS",
[DHCP6_MESSAGE_BINDING_UPDATE] = "Binding Update",
[DHCP6_MESSAGE_BINDING_REPLY] = "Binding Reply",
[DHCP6_MESSAGE_POOL_REQUEST] = "Pool Request",
[DHCP6_MESSAGE_POOL_RESPONSE] = "Pool Response",
[DHCP6_MESSAGE_UPDATE_REQUEST] = "Update Request",
[DHCP6_MESSAGE_UPDATE_REQUEST_ALL] = "Update Request All",
[DHCP6_MESSAGE_UPDATE_DONE] = "Update Done",
[DHCP6_MESSAGE_CONNECT] = "Connect",
[DHCP6_MESSAGE_CONNECT_REPLY] = "Connect Reply",
[DHCP6_MESSAGE_DISCONNECT] = "Disconnect",
[DHCP6_MESSAGE_STATE] = "State",
[DHCP6_MESSAGE_CONTACT] = "Contact",
}; };
DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int); DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
@ -151,7 +174,7 @@ DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
#define DHCP6_CLIENT_DONT_DESTROY(client) \ #define DHCP6_CLIENT_DONT_DESTROY(client) \
_cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client) _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
static int client_start(sd_dhcp6_client *client, enum DHCP6State state); static int client_start(sd_dhcp6_client *client, DHCP6State state);
int sd_dhcp6_client_set_callback( int sd_dhcp6_client_set_callback(
sd_dhcp6_client *client, sd_dhcp6_client *client,
@ -234,10 +257,6 @@ int sd_dhcp6_client_set_mac(
return 0; return 0;
} }
if (client->mac_addr_len == addr_len &&
memcmp(&client->mac_addr, addr, addr_len) == 0)
return 0;
memcpy(&client->mac_addr, addr, addr_len); memcpy(&client->mac_addr, addr, addr_len);
client->mac_addr_len = addr_len; client->mac_addr_len = addr_len;
client->arp_type = arp_type; client->arp_type = arp_type;
@ -475,7 +494,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
assert_return(client, -EINVAL); assert_return(client, -EINVAL);
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
if (option <= 0 || option >= UINT8_MAX) if (!dhcp6_option_can_request(option))
return -EINVAL; return -EINVAL;
for (t = 0; t < client->req_opts_len; t++) for (t = 0; t < client->req_opts_len; t++)
@ -548,7 +567,7 @@ int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegati
assert_return(client, -EINVAL); assert_return(client, -EINVAL);
assert_return(delegation, -EINVAL); assert_return(delegation, -EINVAL);
*delegation = FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD); *delegation = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD);
return 0; return 0;
} }
@ -556,7 +575,7 @@ int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegati
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) { int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
assert_return(client, -EINVAL); assert_return(client, -EINVAL);
SET_FLAG(client->request, DHCP6_REQUEST_IA_PD, delegation); SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_PD, delegation);
return 0; return 0;
} }
@ -565,7 +584,7 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
assert_return(client, -EINVAL); assert_return(client, -EINVAL);
assert_return(request, -EINVAL); assert_return(request, -EINVAL);
*request = FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA); *request = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA);
return 0; return 0;
} }
@ -573,7 +592,7 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) { int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
assert_return(client, -EINVAL); assert_return(client, -EINVAL);
SET_FLAG(client->request, DHCP6_REQUEST_IA_NA, request); SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_NA, request);
return 0; return 0;
} }
@ -678,11 +697,11 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
switch(client->state) { switch(client->state) {
case DHCP6_STATE_INFORMATION_REQUEST: case DHCP6_STATE_INFORMATION_REQUEST:
message->type = DHCP6_INFORMATION_REQUEST; message->type = DHCP6_MESSAGE_INFORMATION_REQUEST;
if (client->mudurl) { if (client->mudurl) {
r = dhcp6_option_append(&opt, &optlen, r = dhcp6_option_append(&opt, &optlen,
SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl), SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
client->mudurl); client->mudurl);
if (r < 0) if (r < 0)
return r; return r;
@ -691,14 +710,14 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
break; break;
case DHCP6_STATE_SOLICITATION: case DHCP6_STATE_SOLICITATION:
message->type = DHCP6_SOLICIT; message->type = DHCP6_MESSAGE_SOLICIT;
r = dhcp6_option_append(&opt, &optlen, r = dhcp6_option_append(&opt, &optlen,
SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
if (r < 0) if (r < 0)
return r; return r;
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) { if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) {
r = dhcp6_option_append_ia(&opt, &optlen, r = dhcp6_option_append_ia(&opt, &optlen,
&client->ia_na); &client->ia_na);
if (r < 0) if (r < 0)
@ -713,7 +732,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (client->mudurl) { if (client->mudurl) {
r = dhcp6_option_append(&opt, &optlen, r = dhcp6_option_append(&opt, &optlen,
SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl), SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
client->mudurl); client->mudurl);
if (r < 0) if (r < 0)
return r; return r;
@ -738,7 +757,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r; return r;
} }
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(&opt, &optlen, &client->ia_pd, &client->hint_pd_prefix); r = dhcp6_option_append_pd(&opt, &optlen, &client->ia_pd, &client->hint_pd_prefix);
if (r < 0) if (r < 0)
return r; return r;
@ -750,9 +769,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
case DHCP6_STATE_RENEW: case DHCP6_STATE_RENEW:
if (client->state == DHCP6_STATE_REQUEST) if (client->state == DHCP6_STATE_REQUEST)
message->type = DHCP6_REQUEST; message->type = DHCP6_MESSAGE_REQUEST;
else else
message->type = DHCP6_RENEW; message->type = DHCP6_MESSAGE_RENEW;
r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID, r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
client->lease->serverid_len, client->lease->serverid_len,
@ -760,7 +779,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (r < 0) if (r < 0)
return r; return r;
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) { if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
r = dhcp6_option_append_ia(&opt, &optlen, r = dhcp6_option_append_ia(&opt, &optlen,
&client->lease->ia); &client->lease->ia);
if (r < 0) if (r < 0)
@ -775,7 +794,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (client->mudurl) { if (client->mudurl) {
r = dhcp6_option_append(&opt, &optlen, r = dhcp6_option_append(&opt, &optlen,
SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl), SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
client->mudurl); client->mudurl);
if (r < 0) if (r < 0)
return r; return r;
@ -799,7 +818,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r; return r;
} }
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL); r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL);
if (r < 0) if (r < 0)
return r; return r;
@ -808,9 +827,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
break; break;
case DHCP6_STATE_REBIND: case DHCP6_STATE_REBIND:
message->type = DHCP6_REBIND; message->type = DHCP6_MESSAGE_REBIND;
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) { if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) {
r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
if (r < 0) if (r < 0)
return r; return r;
@ -824,7 +843,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (client->mudurl) { if (client->mudurl) {
r = dhcp6_option_append(&opt, &optlen, r = dhcp6_option_append(&opt, &optlen,
SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl), SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
client->mudurl); client->mudurl);
if (r < 0) if (r < 0)
return r; return r;
@ -848,7 +867,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r; return r;
} }
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL); r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL);
if (r < 0) if (r < 0)
return r; return r;
@ -859,6 +878,8 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
case DHCP6_STATE_STOPPED: case DHCP6_STATE_STOPPED:
case DHCP6_STATE_BOUND: case DHCP6_STATE_BOUND:
return -EINVAL; return -EINVAL;
default:
assert_not_reached();
} }
r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO, r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
@ -873,14 +894,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (r < 0) if (r < 0)
return r; return r;
elapsed_usec = time_now - client->transaction_start; /* RFC 8415 Section 21.9.
if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10) * A client MUST include an Elapsed Time option in messages to indicate how long the client has
elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10); * been trying to complete a DHCP message exchange. */
else elapsed_usec = MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
elapsed_time = 0xffff; elapsed_time = htobe16(elapsed_usec);
r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time);
r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
sizeof(elapsed_time), &elapsed_time);
if (r < 0) if (r < 0)
return r; return r;
@ -936,7 +955,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) { static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
sd_dhcp6_client *client = userdata; sd_dhcp6_client *client = userdata;
DHCP6_CLIENT_DONT_DESTROY(client); DHCP6_CLIENT_DONT_DESTROY(client);
enum DHCP6State state; DHCP6State state;
assert(s); assert(s);
assert(client); assert(client);
@ -980,7 +999,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
case DHCP6_STATE_SOLICITATION: case DHCP6_STATE_SOLICITATION:
if (client->retransmit_count && client->lease) { if (client->retransmit_count > 0 && client->lease) {
client_start(client, DHCP6_STATE_REQUEST); client_start(client, DHCP6_STATE_REQUEST);
return 0; return 0;
} }
@ -1027,6 +1046,8 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
case DHCP6_STATE_STOPPED: case DHCP6_STATE_STOPPED:
case DHCP6_STATE_BOUND: case DHCP6_STATE_BOUND:
return 0; return 0;
default:
assert_not_reached();
} }
if (max_retransmit_count > 0 && if (max_retransmit_count > 0 &&
@ -1051,8 +1072,8 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
client->retransmit_time += init_retransmit_time / 10; client->retransmit_time += init_retransmit_time / 10;
} else { } else {
if (max_retransmit_time > 0 && assert(max_retransmit_time > 0);
client->retransmit_time > max_retransmit_time / 2) if (client->retransmit_time > max_retransmit_time / 2)
client->retransmit_time = client_timeout_compute_random(max_retransmit_time); client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
else else
client->retransmit_time += client_timeout_compute_random(client->retransmit_time); client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
@ -1113,7 +1134,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
return 0; return 0;
} }
static int client_parse_message( int client_parse_message(
sd_dhcp6_client *client, sd_dhcp6_client *client,
DHCP6Message *message, DHCP6Message *message,
size_t len, size_t len,
@ -1121,7 +1142,6 @@ static int client_parse_message(
uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX; uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX;
usec_t irt = IRT_DEFAULT; usec_t irt = IRT_DEFAULT;
bool clientid = false;
int r; int r;
assert(client); assert(client);
@ -1141,23 +1161,19 @@ static int client_parse_message(
switch (optcode) { switch (optcode) {
case SD_DHCP6_OPTION_CLIENTID: case SD_DHCP6_OPTION_CLIENTID:
if (clientid) if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple clientids", return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs",
dhcp6_message_type_to_string(message->type)); dhcp6_message_type_to_string(message->type));
if (optlen != client->duid_len || r = dhcp6_lease_set_clientid(lease, optval, optlen);
memcmp(&client->duid, optval, optlen) != 0) if (r < 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s DUID does not match", return r;
dhcp6_message_type_to_string(message->type));
clientid = true;
break; break;
case SD_DHCP6_OPTION_SERVERID: case SD_DHCP6_OPTION_SERVERID:
r = dhcp6_lease_get_serverid(lease, NULL, NULL); if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0)
if (r >= 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs",
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple serverids",
dhcp6_message_type_to_string(message->type)); dhcp6_message_type_to_string(message->type));
r = dhcp6_lease_set_serverid(lease, optval, optlen); r = dhcp6_lease_set_serverid(lease, optval, optlen);
@ -1255,35 +1271,35 @@ static int client_parse_message(
case SD_DHCP6_OPTION_DNS_SERVERS: case SD_DHCP6_OPTION_DNS_SERVERS:
r = dhcp6_lease_add_dns(lease, optval, optlen); r = dhcp6_lease_add_dns(lease, optval, optlen);
if (r < 0) if (r < 0)
return r; log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m");
break; break;
case SD_DHCP6_OPTION_DOMAIN_LIST: case SD_DHCP6_OPTION_DOMAIN_LIST:
r = dhcp6_lease_add_domains(lease, optval, optlen); r = dhcp6_lease_add_domains(lease, optval, optlen);
if (r < 0) if (r < 0)
return r; log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m");
break; break;
case SD_DHCP6_OPTION_NTP_SERVER: case SD_DHCP6_OPTION_NTP_SERVER:
r = dhcp6_lease_add_ntp(lease, optval, optlen); r = dhcp6_lease_add_ntp(lease, optval, optlen);
if (r < 0) if (r < 0)
return r; log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m");
break; break;
case SD_DHCP6_OPTION_SNTP_SERVERS: case SD_DHCP6_OPTION_SNTP_SERVERS:
r = dhcp6_lease_add_sntp(lease, optval, optlen); r = dhcp6_lease_add_sntp(lease, optval, optlen);
if (r < 0) if (r < 0)
return r; log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m");
break; break;
case SD_DHCP6_OPTION_FQDN: case SD_DHCP6_OPTION_CLIENT_FQDN:
r = dhcp6_lease_set_fqdn(lease, optval, optlen); r = dhcp6_lease_set_fqdn(lease, optval, optlen);
if (r < 0) if (r < 0)
return r; log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m");
break; break;
@ -1296,8 +1312,15 @@ static int client_parse_message(
} }
} }
if (!clientid) uint8_t *clientid;
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has incomplete options", size_t clientid_len;
if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s message does not contain client ID. Ignoring.",
dhcp6_message_type_to_string(message->type));
if (clientid_len != client->duid_len ||
memcmp(clientid, &client->duid, clientid_len) != 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "The client ID in %s message does not match. Ignoring.",
dhcp6_message_type_to_string(message->type)); dhcp6_message_type_to_string(message->type));
if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
@ -1325,21 +1348,32 @@ static int client_parse_message(
return 0; return 0;
} }
static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) { static int client_receive_reply(
sd_dhcp6_client *client,
DHCP6Message *reply,
size_t len,
const triple_timestamp *t,
const struct in6_addr *server_address) {
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
bool rapid_commit; bool rapid_commit;
int r; int r;
assert(client); assert(client);
assert(reply); assert(reply);
assert(t);
if (reply->type != DHCP6_REPLY) if (reply->type != DHCP6_MESSAGE_REPLY)
return 0; return 0;
r = dhcp6_lease_new(&lease); r = dhcp6_lease_new(&lease);
if (r < 0) if (r < 0)
return -ENOMEM; return -ENOMEM;
lease->timestamp = *t;
if (server_address)
lease->server_address = *server_address;
r = client_parse_message(client, reply, len, lease); r = client_parse_message(client, reply, len, lease);
if (r < 0) if (r < 0)
return r; return r;
@ -1359,18 +1393,32 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, si
return DHCP6_STATE_BOUND; return DHCP6_STATE_BOUND;
} }
static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) { static int client_receive_advertise(
sd_dhcp6_client *client,
DHCP6Message *advertise,
size_t len,
const triple_timestamp *t,
const struct in6_addr *server_address) {
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
uint8_t pref_advertise = 0, pref_lease = 0; uint8_t pref_advertise = 0, pref_lease = 0;
int r; int r;
if (advertise->type != DHCP6_ADVERTISE) assert(client);
assert(advertise);
assert(t);
if (advertise->type != DHCP6_MESSAGE_ADVERTISE)
return 0; return 0;
r = dhcp6_lease_new(&lease); r = dhcp6_lease_new(&lease);
if (r < 0) if (r < 0)
return r; return r;
lease->timestamp = *t;
if (server_address)
lease->server_address = *server_address;
r = client_parse_message(client, advertise, len, lease); r = client_parse_message(client, advertise, len, lease);
if (r < 0) if (r < 0)
return r; return r;
@ -1401,7 +1449,22 @@ static int client_receive_message(
sd_dhcp6_client *client = userdata; sd_dhcp6_client *client = userdata;
DHCP6_CLIENT_DONT_DESTROY(client); DHCP6_CLIENT_DONT_DESTROY(client);
/* This needs to be initialized with zero. See #20741. */
CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {};
struct iovec iov;
union sockaddr_union sa = {};
struct msghdr msg = {
.msg_name = &sa.sa,
.msg_namelen = sizeof(sa),
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
triple_timestamp t = {};
_cleanup_free_ DHCP6Message *message = NULL; _cleanup_free_ DHCP6Message *message = NULL;
struct in6_addr *server_address = NULL;
ssize_t buflen, len; ssize_t buflen, len;
int r = 0; int r = 0;
@ -1410,52 +1473,59 @@ static int client_receive_message(
assert(client->event); assert(client->event);
buflen = next_datagram_size_fd(fd); buflen = next_datagram_size_fd(fd);
if (buflen == -ENETDOWN) if (buflen < 0) {
/* the link is down. Don't return an error or the I/O event if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
source will be disconnected and we won't be able to receive return 0;
packets again when the link comes back. */
log_dhcp6_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
return 0; return 0;
if (buflen < 0) }
return buflen;
message = malloc(buflen); message = malloc(buflen);
if (!message) if (!message)
return -ENOMEM; return -ENOMEM;
len = recv(fd, message, buflen, 0); iov = IOVEC_MAKE(message, buflen);
len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
if (len < 0) { if (len < 0) {
/* see comment above for why we shouldn't error out on ENETDOWN. */ if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len))
if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
return 0; return 0;
return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m"); log_dhcp6_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m");
return 0;
} }
if ((size_t) len < sizeof(DHCP6Message)) { if ((size_t) len < sizeof(DHCP6Message)) {
log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring"); log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
return 0; return 0;
} }
switch(message->type) { /* msg_namelen == 0 happens when running the test-suite over a socketpair */
case DHCP6_SOLICIT: if (msg.msg_namelen > 0) {
case DHCP6_REQUEST: if (msg.msg_namelen != sizeof(struct sockaddr_in6) || sa.in6.sin6_family != AF_INET6) {
case DHCP6_CONFIRM: log_dhcp6_client(client, "Received message from invalid source, ignoring.");
case DHCP6_RENEW: return 0;
case DHCP6_REBIND: }
case DHCP6_RELEASE:
case DHCP6_DECLINE:
case DHCP6_INFORMATION_REQUEST:
case DHCP6_RELAY_FORW:
case DHCP6_RELAY_REPL:
return 0;
case DHCP6_ADVERTISE: server_address = &sa.in6.sin6_addr;
case DHCP6_REPLY: }
case DHCP6_RECONFIGURE:
break;
default: CMSG_FOREACH(cmsg, &msg) {
log_dhcp6_client(client, "Unknown message type %d", message->type); if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SO_TIMESTAMP &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
}
if (!triple_timestamp_is_set(&t))
triple_timestamp_get(&t);
if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY, DHCP6_MESSAGE_RECONFIGURE)) {
const char *type_str = dhcp6_message_type_to_string(message->type);
if (type_str)
log_dhcp6_client(client, "Received unexpected %s message, ignoring.", type_str);
else
log_dhcp6_client(client, "Received unsupported message type %u, ignoring.", message->type);
return 0; return 0;
} }
@ -1464,7 +1534,7 @@ static int client_receive_message(
switch (client->state) { switch (client->state) {
case DHCP6_STATE_INFORMATION_REQUEST: case DHCP6_STATE_INFORMATION_REQUEST:
r = client_receive_reply(client, message, len); r = client_receive_reply(client, message, len, &t, server_address);
if (r < 0) { if (r < 0) {
log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
return 0; return 0;
@ -1477,7 +1547,7 @@ static int client_receive_message(
break; break;
case DHCP6_STATE_SOLICITATION: case DHCP6_STATE_SOLICITATION:
r = client_receive_advertise(client, message, len); r = client_receive_advertise(client, message, len, &t, server_address);
if (r < 0) { if (r < 0) {
log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m"); log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m");
return 0; return 0;
@ -1493,7 +1563,7 @@ static int client_receive_message(
case DHCP6_STATE_RENEW: case DHCP6_STATE_RENEW:
case DHCP6_STATE_REBIND: case DHCP6_STATE_REBIND:
r = client_receive_reply(client, message, len); r = client_receive_reply(client, message, len, &t, server_address);
if (r < 0) { if (r < 0) {
log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
return 0; return 0;
@ -1517,6 +1587,8 @@ static int client_receive_message(
case DHCP6_STATE_STOPPED: case DHCP6_STATE_STOPPED:
return 0; return 0;
default:
assert_not_reached();
} }
log_dhcp6_client(client, "Recv %s", log_dhcp6_client(client, "Recv %s",
@ -1530,14 +1602,14 @@ static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
assert_return(client, -EINVAL); assert_return(client, -EINVAL);
assert_return(client->lease, -EINVAL); assert_return(client->lease, -EINVAL);
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) { if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
*lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1); *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
*lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2); *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
return 0; return 0;
} }
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
*lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1); *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
*lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2); *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
@ -1547,7 +1619,7 @@ static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
return -ENOMSG; return -ENOMSG;
} }
static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { static int client_start(sd_dhcp6_client *client, DHCP6State state) {
int r; int r;
usec_t timeout, time_now; usec_t timeout, time_now;
uint32_t lifetime_t1, lifetime_t2; uint32_t lifetime_t1, lifetime_t2;
@ -1647,6 +1719,8 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
client->state = state; client->state = state;
return 0; return 0;
default:
assert_not_reached();
} }
client->transaction_id = random_u32() & htobe32(0x00ffffff); client->transaction_id = random_u32() & htobe32(0x00ffffff);
@ -1685,7 +1759,7 @@ int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
} }
int sd_dhcp6_client_start(sd_dhcp6_client *client) { int sd_dhcp6_client_start(sd_dhcp6_client *client) {
enum DHCP6State state = DHCP6_STATE_SOLICITATION; DHCP6State state = DHCP6_STATE_SOLICITATION;
int r; int r;
assert_return(client, -EINVAL); assert_return(client, -EINVAL);
@ -1696,7 +1770,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
if (client->state != DHCP6_STATE_STOPPED) if (client->state != DHCP6_STATE_STOPPED)
return -EBUSY; return -EBUSY;
if (!client->information_request && !client->request) if (!client->information_request && client->request_ia == 0)
return -EINVAL; return -EINVAL;
r = client_reset(client); r = client_reset(client);
@ -1824,7 +1898,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
.ia_na.type = SD_DHCP6_OPTION_IA_NA, .ia_na.type = SD_DHCP6_OPTION_IA_NA,
.ia_pd.type = SD_DHCP6_OPTION_IA_PD, .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
.ifindex = -1, .ifindex = -1,
.request = DHCP6_REQUEST_IA_NA, .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD,
.fd = -1, .fd = -1,
.req_opts_len = ELEMENTSOF(default_req_opts), .req_opts_len = ELEMENTSOF(default_req_opts),
.hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1, .hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1,

View file

@ -13,6 +13,27 @@
#include "strv.h" #include "strv.h"
#include "util.h" #include "util.h"
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
assert_return(lease, -EINVAL);
assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
assert_return(clock_supported(clock), -EOPNOTSUPP);
assert_return(ret, -EINVAL);
if (!triple_timestamp_is_set(&lease->timestamp))
return -ENODATA;
*ret = triple_timestamp_by_clock(&lease->timestamp, clock);
return 0;
}
int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
assert_return(lease, -EINVAL);
assert_return(ret, -EINVAL);
*ret = lease->server_address;
return 0;
}
int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
DHCP6Address *addr; DHCP6Address *addr;
uint32_t valid = 0, t; uint32_t valid = 0, t;
@ -52,12 +73,43 @@ DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) {
return NULL; return NULL;
} }
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
size_t len) { uint8_t *clientid;
assert_return(lease, -EINVAL);
assert_return(id, -EINVAL);
assert_return(len > 0, -EINVAL);
clientid = memdup(id, len);
if (!clientid)
return -ENOMEM;
free_and_replace(lease->clientid, clientid);
lease->clientid_len = len;
return 0;
}
int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
assert_return(lease, -EINVAL);
if (!lease->clientid)
return -ENODATA;
if (ret_id)
*ret_id = lease->clientid;
if (ret_len)
*ret_len = lease->clientid_len;
return 0;
}
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
uint8_t *serverid; uint8_t *serverid;
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(id, -EINVAL); assert_return(id, -EINVAL);
assert_return(len > 0, -EINVAL);
serverid = memdup(id, len); serverid = memdup(id, len);
if (!serverid) if (!serverid)
@ -69,16 +121,16 @@ int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
return 0; return 0;
} }
int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len) { int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
if (!lease->serverid) if (!lease->serverid)
return -ENOMSG; return -ENODATA;
if (id) if (ret_id)
*id = lease->serverid; *ret_id = lease->serverid;
if (len) if (ret_len)
*len = lease->serverid_len; *ret_len = lease->serverid_len;
return 0; return 0;
} }
@ -119,24 +171,6 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit) {
return 0; return 0;
} }
int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
assert_return(lease, -EINVAL);
assert_return(iaid, -EINVAL);
*iaid = lease->ia.ia_na.id;
return 0;
}
int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
assert_return(lease, -EINVAL);
assert_return(iaid, -EINVAL);
*iaid = lease->pd.ia_pd.id;
return 0;
}
int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr, int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr,
uint32_t *lifetime_preferred, uint32_t *lifetime_preferred,
uint32_t *lifetime_valid) { uint32_t *lifetime_valid) {
@ -207,12 +241,13 @@ int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) { int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(ret, -EINVAL);
if (!lease->dns) if (!lease->dns)
return -ENOENT; return -ENOENT;
*ret = lease->dns; if (ret)
*ret = lease->dns;
return lease->dns_count; return lease->dns_count;
} }
@ -305,16 +340,17 @@ int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t op
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) { int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(ret, -EINVAL);
if (lease->ntp) { if (lease->ntp) {
*ret = lease->ntp; if (ret)
*ret = lease->ntp;
return lease->ntp_count; return lease->ntp_count;
} }
if (lease->sntp && !lease->ntp_fqdn) { if (lease->sntp && !lease->ntp_fqdn) {
/* Fallback to the deprecated SNTP option. */ /* Fallback to the deprecated SNTP option. */
*ret = lease->sntp; if (ret)
*ret = lease->sntp;
return lease->sntp_count; return lease->sntp_count;
} }
@ -323,12 +359,12 @@ int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) { int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(ret, -EINVAL);
if (!lease->ntp_fqdn) if (!lease->ntp_fqdn)
return -ENOENT; return -ENOENT;
*ret = lease->ntp_fqdn; if (ret)
*ret = lease->ntp_fqdn;
return strv_length(lease->ntp_fqdn); return strv_length(lease->ntp_fqdn);
} }
@ -366,6 +402,7 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
if (!lease) if (!lease)
return NULL; return NULL;
free(lease->clientid);
free(lease->serverid); free(lease->serverid);
dhcp6_lease_free_ia(&lease->ia); dhcp6_lease_free_ia(&lease->ia);
dhcp6_lease_free_ia(&lease->pd); dhcp6_lease_free_ia(&lease->pd);

View file

@ -202,6 +202,9 @@ static int lldp_rx_receive_datagram(sd_event_source *s, int fd, uint32_t revents
space = next_datagram_size_fd(fd); space = next_datagram_size_fd(fd);
if (space < 0) { if (space < 0) {
if (ERRNO_IS_TRANSIENT(space) || ERRNO_IS_DISCONNECT(space))
return 0;
log_lldp_rx_errno(lldp_rx, space, "Failed to determine datagram size to read, ignoring: %m"); log_lldp_rx_errno(lldp_rx, space, "Failed to determine datagram size to read, ignoring: %m");
return 0; return 0;
} }
@ -214,7 +217,7 @@ static int lldp_rx_receive_datagram(sd_event_source *s, int fd, uint32_t revents
length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT); length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
if (length < 0) { if (length < 0) {
if (IN_SET(errno, EAGAIN, EINTR)) if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
return 0; return 0;
log_lldp_rx_errno(lldp_rx, errno, "Failed to read LLDP datagram, ignoring: %m"); log_lldp_rx_errno(lldp_rx, errno, "Failed to read LLDP datagram, ignoring: %m");
@ -251,7 +254,7 @@ int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx) {
return lldp_rx->fd >= 0; return lldp_rx->fd >= 0;
} }
_public_ int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) { int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) {
int r; int r;
assert_return(lldp_rx, -EINVAL); assert_return(lldp_rx, -EINVAL);
@ -285,7 +288,7 @@ fail:
return r; return r;
} }
_public_ int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) { int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) {
if (!sd_lldp_rx_is_running(lldp_rx)) if (!sd_lldp_rx_is_running(lldp_rx))
return 0; return 0;
@ -297,7 +300,7 @@ _public_ int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) {
return 1; return 1;
} }
_public_ int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) { int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) {
int r; int r;
assert_return(lldp_rx, -EINVAL); assert_return(lldp_rx, -EINVAL);
@ -317,7 +320,7 @@ _public_ int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64
return 0; return 0;
} }
_public_ int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) { int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) {
assert_return(lldp_rx, -EINVAL); assert_return(lldp_rx, -EINVAL);
assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY); assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
@ -327,13 +330,13 @@ _public_ int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) {
return 0; return 0;
} }
_public_ sd_event* sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx) { sd_event* sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx) {
assert_return(lldp_rx, NULL); assert_return(lldp_rx, NULL);
return lldp_rx->event; return lldp_rx->event;
} }
_public_ int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata) { int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata) {
assert_return(lldp_rx, -EINVAL); assert_return(lldp_rx, -EINVAL);
lldp_rx->callback = cb; lldp_rx->callback = cb;
@ -342,7 +345,7 @@ _public_ int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t
return 0; return 0;
} }
_public_ int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex) { int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex) {
assert_return(lldp_rx, -EINVAL); assert_return(lldp_rx, -EINVAL);
assert_return(ifindex > 0, -EINVAL); assert_return(ifindex > 0, -EINVAL);
assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY); assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
@ -394,7 +397,7 @@ static sd_lldp_rx *lldp_rx_free(sd_lldp_rx *lldp_rx) {
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_rx, sd_lldp_rx, lldp_rx_free); 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) { int sd_lldp_rx_new(sd_lldp_rx **ret) {
_cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL; _cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL;
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
@ -462,7 +465,7 @@ static inline int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_nei
return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id); 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) { int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***ret) {
_cleanup_free_ sd_lldp_neighbor **l = NULL; _cleanup_free_ sd_lldp_neighbor **l = NULL;
sd_lldp_neighbor *n; sd_lldp_neighbor *n;
int k = 0; int k = 0;
@ -491,7 +494,7 @@ _public_ int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***r
return k; return k;
} }
_public_ int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) { int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) {
assert_return(lldp_rx, -EINVAL); assert_return(lldp_rx, -EINVAL);
assert_return(m > 0, -EINVAL); assert_return(m > 0, -EINVAL);
@ -501,7 +504,7 @@ _public_ int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) {
return 0; return 0;
} }
_public_ int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) { int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) {
assert_return(lldp_rx, -EINVAL); assert_return(lldp_rx, -EINVAL);
assert_return(mask != 0, -EINVAL); assert_return(mask != 0, -EINVAL);
@ -510,7 +513,7 @@ _public_ int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) {
return 0; return 0;
} }
_public_ int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *addr) { int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *addr) {
assert_return(lldp_rx, -EINVAL); 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 /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so

View file

@ -71,6 +71,7 @@ struct sd_event_source {
uint64_t prepare_iteration; uint64_t prepare_iteration;
sd_event_destroy_t destroy_callback; sd_event_destroy_t destroy_callback;
sd_event_handler_t ratelimit_expire_callback;
LIST_FIELDS(sd_event_source, sources); LIST_FIELDS(sd_event_source, sources);
@ -214,6 +215,11 @@ struct inotify_data {
* the events locally if they can't be coalesced). */ * the events locally if they can't be coalesced). */
unsigned n_pending; unsigned n_pending;
/* If this counter is non-zero, don't GC the inotify data object even if not used to watch any inode
* anymore. This is useful to pin the object for a bit longer, after the last event source needing it
* is gone. */
unsigned n_busy;
/* A linked list of all inotify objects with data already read, that still need processing. We keep this list /* A linked list of all inotify objects with data already read, that still need processing. We keep this list
* to make it efficient to figure out what inotify objects to process data on next. */ * to make it efficient to figure out what inotify objects to process data on next. */
LIST_FIELDS(struct inotify_data, buffered); LIST_FIELDS(struct inotify_data, buffered);

View file

@ -86,6 +86,30 @@ int event_reset_time(
return created; return created;
} }
int event_reset_time_relative(
sd_event *e,
sd_event_source **s,
clockid_t clock,
uint64_t usec,
uint64_t accuracy,
sd_event_time_handler_t callback,
void *userdata,
int64_t priority,
const char *description,
bool force_reset) {
usec_t usec_now;
int r;
assert(e);
r = sd_event_now(e, clock, &usec_now);
if (r < 0)
return log_debug_errno(r, "sd-event: Failed to get the current time: %m");
return event_reset_time(e, s, clock, usec_add(usec_now, usec), accuracy, callback, userdata, priority, description, force_reset);
}
int event_source_disable(sd_event_source *s) { int event_source_disable(sd_event_source *s) {
if (!s) if (!s)
return 0; return 0;

View file

@ -5,9 +5,27 @@
#include "sd-event.h" #include "sd-event.h"
int event_reset_time(sd_event *e, sd_event_source **s, int event_reset_time(
clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event *e,
sd_event_time_handler_t callback, void *userdata, sd_event_source **s,
int64_t priority, const char *description, bool force_reset); clockid_t clock,
uint64_t usec,
uint64_t accuracy,
sd_event_time_handler_t callback,
void *userdata,
int64_t priority,
const char *description,
bool force_reset);
int event_reset_time_relative(
sd_event *e,
sd_event_source **s,
clockid_t clock,
uint64_t usec,
uint64_t accuracy,
sd_event_time_handler_t callback,
void *userdata,
int64_t priority,
const char *description,
bool force_reset);
int event_source_disable(sd_event_source *s); int event_source_disable(sd_event_source *s);
int event_source_is_enabled(sd_event_source *s); int event_source_is_enabled(sd_event_source *s);

View file

@ -1824,6 +1824,29 @@ static void event_free_inode_data(
free(d); free(d);
} }
static void event_gc_inotify_data(
sd_event *e,
struct inotify_data *d) {
assert(e);
/* GCs the inotify data object if we don't need it anymore. That's the case if we don't want to watch
* any inode with it anymore, which in turn happens if no event source of this priority is interested
* in any inode any longer. That said, we maintain an extra busy counter: if non-zero we'll delay GC
* (under the expectation that the GC is called again once the counter is decremented). */
if (!d)
return;
if (!hashmap_isempty(d->inodes))
return;
if (d->n_busy > 0)
return;
event_free_inotify_data(e, d);
}
static void event_gc_inode_data( static void event_gc_inode_data(
sd_event *e, sd_event *e,
struct inode_data *d) { struct inode_data *d) {
@ -1841,8 +1864,7 @@ static void event_gc_inode_data(
inotify_data = d->inotify_data; inotify_data = d->inotify_data;
event_free_inode_data(e, d); event_free_inode_data(e, d);
if (inotify_data && hashmap_isempty(inotify_data->inodes)) event_gc_inotify_data(e, inotify_data);
event_free_inotify_data(e, inotify_data);
} }
static int event_make_inode_data( static int event_make_inode_data(
@ -1971,24 +1993,25 @@ static int inotify_exit_callback(sd_event_source *s, const struct inotify_event
return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata)); return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
} }
_public_ int sd_event_add_inotify( static int event_add_inotify_fd_internal(
sd_event *e, sd_event *e,
sd_event_source **ret, sd_event_source **ret,
const char *path, int fd,
bool donate,
uint32_t mask, uint32_t mask,
sd_event_inotify_handler_t callback, sd_event_inotify_handler_t callback,
void *userdata) { void *userdata) {
_cleanup_close_ int donated_fd = donate ? fd : -1;
_cleanup_(source_freep) sd_event_source *s = NULL;
struct inotify_data *inotify_data = NULL; struct inotify_data *inotify_data = NULL;
struct inode_data *inode_data = NULL; struct inode_data *inode_data = NULL;
_cleanup_close_ int fd = -1;
_cleanup_(source_freep) sd_event_source *s = NULL;
struct stat st; struct stat st;
int r; int r;
assert_return(e, -EINVAL); assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG); assert_return(e = event_resolve(e), -ENOPKG);
assert_return(path, -EINVAL); assert_return(fd >= 0, -EBADF);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD); assert_return(!event_pid_changed(e), -ECHILD);
@ -2001,12 +2024,6 @@ _public_ int sd_event_add_inotify(
if (mask & IN_MASK_ADD) if (mask & IN_MASK_ADD)
return -EINVAL; return -EINVAL;
fd = open(path, O_PATH|O_CLOEXEC|
(mask & IN_ONLYDIR ? O_DIRECTORY : 0)|
(mask & IN_DONT_FOLLOW ? O_NOFOLLOW : 0));
if (fd < 0)
return -errno;
if (fstat(fd, &st) < 0) if (fstat(fd, &st) < 0)
return -errno; return -errno;
@ -2026,14 +2043,24 @@ _public_ int sd_event_add_inotify(
r = event_make_inode_data(e, inotify_data, st.st_dev, st.st_ino, &inode_data); r = event_make_inode_data(e, inotify_data, st.st_dev, st.st_ino, &inode_data);
if (r < 0) { if (r < 0) {
event_free_inotify_data(e, inotify_data); event_gc_inotify_data(e, inotify_data);
return r; return r;
} }
/* Keep the O_PATH fd around until the first iteration of the loop, so that we can still change the priority of /* Keep the O_PATH fd around until the first iteration of the loop, so that we can still change the priority of
* the event source, until then, for which we need the original inode. */ * the event source, until then, for which we need the original inode. */
if (inode_data->fd < 0) { if (inode_data->fd < 0) {
inode_data->fd = TAKE_FD(fd); if (donated_fd >= 0)
inode_data->fd = TAKE_FD(donated_fd);
else {
inode_data->fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
if (inode_data->fd < 0) {
r = -errno;
event_gc_inode_data(e, inode_data);
return r;
}
}
LIST_PREPEND(to_close, e->inode_data_to_close, inode_data); LIST_PREPEND(to_close, e->inode_data_to_close, inode_data);
} }
@ -2046,8 +2073,6 @@ _public_ int sd_event_add_inotify(
if (r < 0) if (r < 0)
return r; return r;
(void) sd_event_source_set_description(s, path);
if (ret) if (ret)
*ret = s; *ret = s;
TAKE_PTR(s); TAKE_PTR(s);
@ -2055,6 +2080,48 @@ _public_ int sd_event_add_inotify(
return 0; return 0;
} }
_public_ int sd_event_add_inotify_fd(
sd_event *e,
sd_event_source **ret,
int fd,
uint32_t mask,
sd_event_inotify_handler_t callback,
void *userdata) {
return event_add_inotify_fd_internal(e, ret, fd, /* donate= */ false, mask, callback, userdata);
}
_public_ int sd_event_add_inotify(
sd_event *e,
sd_event_source **ret,
const char *path,
uint32_t mask,
sd_event_inotify_handler_t callback,
void *userdata) {
sd_event_source *s = NULL; /* avoid false maybe-uninitialized warning */
int fd, r;
assert_return(path, -EINVAL);
fd = open(path, O_PATH|O_CLOEXEC|
(mask & IN_ONLYDIR ? O_DIRECTORY : 0)|
(mask & IN_DONT_FOLLOW ? O_NOFOLLOW : 0));
if (fd < 0)
return -errno;
r = event_add_inotify_fd_internal(e, &s, fd, /* donate= */ true, mask, callback, userdata);
if (r < 0)
return r;
(void) sd_event_source_set_description(s, path);
if (ret)
*ret = s;
return r;
}
static sd_event_source* event_source_free(sd_event_source *s) { static sd_event_source* event_source_free(sd_event_source *s) {
if (!s) if (!s)
return NULL; return NULL;
@ -2845,7 +2912,7 @@ fail:
return r; return r;
} }
static int event_source_leave_ratelimit(sd_event_source *s) { static int event_source_leave_ratelimit(sd_event_source *s, bool run_callback) {
int r; int r;
assert(s); assert(s);
@ -2877,6 +2944,30 @@ static int event_source_leave_ratelimit(sd_event_source *s) {
ratelimit_reset(&s->rate_limit); ratelimit_reset(&s->rate_limit);
log_debug("Event source %p (%s) left rate limit state.", s, strna(s->description)); log_debug("Event source %p (%s) left rate limit state.", s, strna(s->description));
if (run_callback && s->ratelimit_expire_callback) {
s->dispatching = true;
r = s->ratelimit_expire_callback(s, s->userdata);
s->dispatching = false;
if (r < 0) {
log_debug_errno(r, "Ratelimit expiry callback of event source %s (type %s) returned error, %s: %m",
strna(s->description),
event_source_type_to_string(s->type),
s->exit_on_failure ? "exiting" : "disabling");
if (s->exit_on_failure)
(void) sd_event_exit(s->event, r);
}
if (s->n_ref == 0)
source_free(s);
else if (r < 0)
assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
return 1;
}
return 0; return 0;
fail: fail:
@ -3055,7 +3146,7 @@ static int flush_timer(sd_event *e, int fd, uint32_t events, usec_t *next) {
ss = read(fd, &x, sizeof(x)); ss = read(fd, &x, sizeof(x));
if (ss < 0) { if (ss < 0) {
if (IN_SET(errno, EAGAIN, EINTR)) if (ERRNO_IS_TRANSIENT(errno))
return 0; return 0;
return -errno; return -errno;
@ -3076,6 +3167,7 @@ static int process_timer(
struct clock_data *d) { struct clock_data *d) {
sd_event_source *s; sd_event_source *s;
bool callback_invoked = false;
int r; int r;
assert(e); assert(e);
@ -3093,9 +3185,11 @@ static int process_timer(
* again. */ * again. */
assert(s->ratelimited); assert(s->ratelimited);
r = event_source_leave_ratelimit(s); r = event_source_leave_ratelimit(s, /* run_callback */ true);
if (r < 0) if (r < 0)
return r; return r;
else if (r == 1)
callback_invoked = true;
continue; continue;
} }
@ -3110,7 +3204,7 @@ static int process_timer(
event_source_time_prioq_reshuffle(s); event_source_time_prioq_reshuffle(s);
} }
return 0; return callback_invoked;
} }
static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priority) { static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priority) {
@ -3258,7 +3352,7 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events, i
n = read(d->fd, &si, sizeof(si)); n = read(d->fd, &si, sizeof(si));
if (n < 0) { if (n < 0) {
if (IN_SET(errno, EAGAIN, EINTR)) if (ERRNO_IS_TRANSIENT(errno))
return 0; return 0;
return -errno; return -errno;
@ -3312,7 +3406,7 @@ static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t
n = read(d->fd, &d->buffer, sizeof(d->buffer)); n = read(d->fd, &d->buffer, sizeof(d->buffer));
if (n < 0) { if (n < 0) {
if (IN_SET(errno, EAGAIN, EINTR)) if (ERRNO_IS_TRANSIENT(errno))
return 0; return 0;
return -errno; return -errno;
@ -3560,13 +3654,23 @@ static int source_dispatch(sd_event_source *s) {
sz = offsetof(struct inotify_event, name) + d->buffer.ev.len; sz = offsetof(struct inotify_event, name) + d->buffer.ev.len;
assert(d->buffer_filled >= sz); assert(d->buffer_filled >= sz);
/* If the inotify callback destroys the event source then this likely means we don't need to
* watch the inode anymore, and thus also won't need the inotify object anymore. But if we'd
* free it immediately, then we couldn't drop the event from the inotify event queue without
* memory corruption anymore, as below. Hence, let's not free it immediately, but mark it
* "busy" with a counter (which will ensure it's not GC'ed away prematurely). Let's then
* explicitly GC it after we are done dropping the inotify event from the buffer. */
d->n_busy++;
r = s->inotify.callback(s, &d->buffer.ev, s->userdata); r = s->inotify.callback(s, &d->buffer.ev, s->userdata);
d->n_busy--;
/* When no event is pending anymore on this inotify object, then let's drop the event from the /* When no event is pending anymore on this inotify object, then let's drop the event from
* buffer. */ * the inotify event queue buffer. */
if (d->n_pending == 0) if (d->n_pending == 0)
event_inotify_data_drop(e, d, sz); event_inotify_data_drop(e, d, sz);
/* Now we don't want to access 'd' anymore, it's OK to GC now. */
event_gc_inotify_data(e, d);
break; break;
} }
@ -3591,7 +3695,7 @@ static int source_dispatch(sd_event_source *s) {
if (s->n_ref == 0) if (s->n_ref == 0)
source_free(s); source_free(s);
else if (r < 0) else if (r < 0)
sd_event_source_set_enabled(s, SD_EVENT_OFF); assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
return 1; return 1;
} }
@ -3632,7 +3736,7 @@ static int event_prepare(sd_event *e) {
if (s->n_ref == 0) if (s->n_ref == 0)
source_free(s); source_free(s);
else if (r < 0) else if (r < 0)
sd_event_source_set_enabled(s, SD_EVENT_OFF); assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
} }
return 0; return 0;
@ -3693,10 +3797,7 @@ static int arm_watchdog(sd_event *e) {
if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0) if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0)
its.it_value.tv_nsec = 1; its.it_value.tv_nsec = 1;
if (timerfd_settime(e->watchdog_fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) return RET_NERRNO(timerfd_settime(e->watchdog_fd, TFD_TIMER_ABSTIME, &its, NULL));
return -errno;
return 0;
} }
static int process_watchdog(sd_event *e) { static int process_watchdog(sd_event *e) {
@ -3806,7 +3907,7 @@ static int epoll_wait_usec(
int maxevents, int maxevents,
usec_t timeout) { usec_t timeout) {
int r, msec; int msec;
#if 0 #if 0
static bool epoll_pwait2_absent = false; static bool epoll_pwait2_absent = false;
@ -3846,14 +3947,7 @@ static int epoll_wait_usec(
msec = (int) k; msec = (int) k;
} }
r = epoll_wait(fd, return RET_NERRNO(epoll_wait(fd, events, maxevents, msec));
events,
maxevents,
msec);
if (r < 0)
return -errno;
return r;
} }
static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t *ret_min_priority) { static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t *ret_min_priority) {
@ -4024,6 +4118,10 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
if (r < 0) if (r < 0)
goto finish; goto finish;
r = process_inotify(e);
if (r < 0)
goto finish;
r = process_timer(e, e->timestamp.realtime, &e->realtime); r = process_timer(e, e->timestamp.realtime, &e->realtime);
if (r < 0) if (r < 0)
goto finish; goto finish;
@ -4032,10 +4130,6 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
if (r < 0) if (r < 0)
goto finish; goto finish;
r = process_timer(e, e->timestamp.monotonic, &e->monotonic);
if (r < 0)
goto finish;
r = process_timer(e, e->timestamp.realtime, &e->realtime_alarm); r = process_timer(e, e->timestamp.realtime, &e->realtime_alarm);
if (r < 0) if (r < 0)
goto finish; goto finish;
@ -4044,9 +4138,20 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
if (r < 0) if (r < 0)
goto finish; goto finish;
r = process_inotify(e); r = process_timer(e, e->timestamp.monotonic, &e->monotonic);
if (r < 0) if (r < 0)
goto finish; goto finish;
else if (r == 1) {
/* Ratelimit expiry callback was called. Let's postpone processing pending sources and
* put loop in the initial state in order to evaluate (in the next iteration) also sources
* there were potentially re-enabled by the callback.
*
* Wondering why we treat only this invocation of process_timer() differently? Once event
* source is ratelimited we essentially transform it into CLOCK_MONOTONIC timer hence
* ratelimit expiry callback is never called for any other timer type. */
r = 0;
goto finish;
}
if (event_next_pending(e)) { if (event_next_pending(e)) {
e->state = SD_EVENT_PENDING; e->state = SD_EVENT_PENDING;
@ -4117,7 +4222,7 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
this_run = now(CLOCK_MONOTONIC); this_run = now(CLOCK_MONOTONIC);
l = u64log2(this_run - e->last_run_usec); l = log2u64(this_run - e->last_run_usec);
assert(l < ELEMENTSOF(e->delays)); assert(l < ELEMENTSOF(e->delays));
e->delays[l]++; e->delays[l]++;
@ -4416,7 +4521,7 @@ _public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval
/* When ratelimiting is configured we'll always reset the rate limit state first and start fresh, /* When ratelimiting is configured we'll always reset the rate limit state first and start fresh,
* non-ratelimited. */ * non-ratelimited. */
r = event_source_leave_ratelimit(s); r = event_source_leave_ratelimit(s, /* run_callback */ false);
if (r < 0) if (r < 0)
return r; return r;
@ -4424,6 +4529,13 @@ _public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval
return 0; return 0;
} }
_public_ int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback) {
assert_return(s, -EINVAL);
s->ratelimit_expire_callback = callback;
return 0;
}
_public_ int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval, unsigned *ret_burst) { _public_ int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval, unsigned *ret_burst) {
assert_return(s, -EINVAL); assert_return(s, -EINVAL);

View file

@ -224,7 +224,7 @@ int id128_get_product(sd_id128_t *ret) {
r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid); r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
if (r == -ENOENT) if (r == -ENOENT)
r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid); r = id128_read("/proc/device-tree/vm,uuid", ID128_UUID, &uuid);
if (r < 0) if (r < 0)
return r; return r;

View file

@ -6,21 +6,14 @@
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#if HAVE_OPENSSL
#include <openssl/hmac.h>
#include <openssl/sha.h>
#endif
#include "sd-id128.h" #include "sd-id128.h"
#include "alloc-util.h" #include "alloc-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "hexdecoct.h" #include "hexdecoct.h"
#include "hmac.h"
#include "id128-util.h" #include "id128-util.h"
#include "io-util.h" #include "io-util.h"
#if !HAVE_OPENSSL
#include "khash.h"
#endif
#include "macro.h" #include "macro.h"
#include "missing_syscall.h" #include "missing_syscall.h"
#include "random-util.h" #include "random-util.h"
@ -283,43 +276,15 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
} }
static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) { static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
uint8_t hmac[SHA256_DIGEST_SIZE];
sd_id128_t result; sd_id128_t result;
assert(ret); assert(ret);
#if HAVE_OPENSSL hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), hmac);
/* We prefer doing this in-process, since we this means we are not dependent on kernel configuration,
* and this also works in locked down container environments. But some distros don't like OpenSSL's
* license and its (in-) compatibility with GPL2, hence also support khash */
uint8_t md[256/8];
if (!HMAC(EVP_sha256(),
&base, sizeof(base),
(const unsigned char*) &app_id, sizeof(app_id),
md, NULL))
return -ENOTRECOVERABLE;
/* Take only the first half. */ /* Take only the first half. */
memcpy(&result, md, MIN(sizeof(md), sizeof(result))); memcpy(&result, hmac, MIN(sizeof(hmac), sizeof(result)));
#else
_cleanup_(khash_unrefp) khash *h = NULL;
const void *p;
int r;
r = khash_new_with_key(&h, "hmac(sha256)", &base, sizeof(base));
if (r < 0)
return r;
r = khash_put(h, &app_id, sizeof(app_id));
if (r < 0)
return r;
r = khash_digest_data(h, &p);
if (r < 0)
return r;
/* We chop off the trailing 16 bytes */
memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
#endif
*ret = id128_make_v4_uuid(result); *ret = id128_make_v4_uuid(result);
return 0; return 0;

View file

@ -45,65 +45,66 @@ enum {
}; };
enum { enum {
SD_DHCP_OPTION_PAD = 0, SD_DHCP_OPTION_PAD = 0,
SD_DHCP_OPTION_SUBNET_MASK = 1, SD_DHCP_OPTION_SUBNET_MASK = 1,
SD_DHCP_OPTION_TIME_OFFSET = 2, SD_DHCP_OPTION_TIME_OFFSET = 2,
SD_DHCP_OPTION_ROUTER = 3, SD_DHCP_OPTION_ROUTER = 3,
SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6, SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6,
SD_DHCP_OPTION_LPR_SERVER = 9, SD_DHCP_OPTION_LPR_SERVER = 9,
SD_DHCP_OPTION_HOST_NAME = 12, SD_DHCP_OPTION_HOST_NAME = 12,
SD_DHCP_OPTION_BOOT_FILE_SIZE = 13, SD_DHCP_OPTION_BOOT_FILE_SIZE = 13,
SD_DHCP_OPTION_DOMAIN_NAME = 15, SD_DHCP_OPTION_DOMAIN_NAME = 15,
SD_DHCP_OPTION_ROOT_PATH = 17, SD_DHCP_OPTION_ROOT_PATH = 17,
SD_DHCP_OPTION_ENABLE_IP_FORWARDING = 19, SD_DHCP_OPTION_ENABLE_IP_FORWARDING = 19,
SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20, SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20,
SD_DHCP_OPTION_POLICY_FILTER = 21, SD_DHCP_OPTION_POLICY_FILTER = 21,
SD_DHCP_OPTION_INTERFACE_MDR = 22, SD_DHCP_OPTION_INTERFACE_MDR = 22,
SD_DHCP_OPTION_INTERFACE_TTL = 23, SD_DHCP_OPTION_INTERFACE_TTL = 23,
SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24, SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24,
SD_DHCP_OPTION_INTERFACE_MTU = 26, SD_DHCP_OPTION_INTERFACE_MTU = 26,
SD_DHCP_OPTION_BROADCAST = 28, SD_DHCP_OPTION_BROADCAST = 28,
/* Windows 10 option to send when Anonymize=true */ /* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_ROUTER_DISCOVER = 31, SD_DHCP_OPTION_ROUTER_DISCOVER = 31,
SD_DHCP_OPTION_STATIC_ROUTE = 33, SD_DHCP_OPTION_STATIC_ROUTE = 33,
SD_DHCP_OPTION_NTP_SERVER = 42, SD_DHCP_OPTION_NTP_SERVER = 42,
SD_DHCP_OPTION_VENDOR_SPECIFIC = 43, SD_DHCP_OPTION_VENDOR_SPECIFIC = 43,
/* Windows 10 option to send when Anonymize=true */ /* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_NETBIOS_NAMESERVER = 44, SD_DHCP_OPTION_NETBIOS_NAMESERVER = 44,
/* Windows 10 option to send when Anonymize=true */ /* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_NETBIOS_NODETYPE = 46, SD_DHCP_OPTION_NETBIOS_NODETYPE = 46,
/* Windows 10 option to send when Anonymize=true */ /* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_NETBIOS_SCOPE = 47, SD_DHCP_OPTION_NETBIOS_SCOPE = 47,
SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50, SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50,
SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51, SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51,
SD_DHCP_OPTION_OVERLOAD = 52, SD_DHCP_OPTION_OVERLOAD = 52,
SD_DHCP_OPTION_MESSAGE_TYPE = 53, SD_DHCP_OPTION_MESSAGE_TYPE = 53,
SD_DHCP_OPTION_SERVER_IDENTIFIER = 54, SD_DHCP_OPTION_SERVER_IDENTIFIER = 54,
SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55, SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55,
SD_DHCP_OPTION_ERROR_MESSAGE = 56, SD_DHCP_OPTION_ERROR_MESSAGE = 56,
SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57, SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57,
SD_DHCP_OPTION_RENEWAL_T1_TIME = 58, SD_DHCP_OPTION_RENEWAL_T1_TIME = 58,
SD_DHCP_OPTION_REBINDING_T2_TIME = 59, SD_DHCP_OPTION_REBINDING_T2_TIME = 59,
SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60,
SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61, SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61,
SD_DHCP_OPTION_SMTP_SERVER = 69, SD_DHCP_OPTION_SMTP_SERVER = 69,
SD_DHCP_OPTION_POP3_SERVER = 70, SD_DHCP_OPTION_POP3_SERVER = 70,
SD_DHCP_OPTION_USER_CLASS = 77, SD_DHCP_OPTION_USER_CLASS = 77,
SD_DHCP_OPTION_FQDN = 81, SD_DHCP_OPTION_FQDN = 81,
SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82, SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82,
SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100,
SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101,
SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119, SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119,
SD_DHCP_OPTION_SIP_SERVER = 120, SD_DHCP_OPTION_SIP_SERVER = 120,
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
SD_DHCP_OPTION_MUD_URL = 161, SD_DHCP_OPTION_MUD_URL = 161,
SD_DHCP_OPTION_PRIVATE_BASE = 224, SD_DHCP_OPTION_6RD = 212,
/* Windows 10 option to send when Anonymize=true */ SD_DHCP_OPTION_PRIVATE_BASE = 224,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249, SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249,
/* Windows 10 option to send when Anonymize=true */ /* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252, SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252,
SD_DHCP_OPTION_PRIVATE_LAST = 254, SD_DHCP_OPTION_PRIVATE_LAST = 254,
SD_DHCP_OPTION_END = 255, SD_DHCP_OPTION_END = 255,
}; };
/* Suboptions for SD_DHCP_OPTION_RELAY_AGENT_INFORMATION option */ /* Suboptions for SD_DHCP_OPTION_RELAY_AGENT_INFORMATION option */

View file

@ -71,6 +71,13 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes);
int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len); int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len); int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone); int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
int sd_dhcp_lease_get_6rd(
sd_dhcp_lease *lease,
uint8_t *ret_ipv4masklen,
uint8_t *ret_prefixlen,
struct in6_addr *ret_prefix,
const struct in_addr **ret_br_addresses,
size_t *ret_n_br_addresses);
int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination); int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination);
int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length); int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length);

View file

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

View file

@ -21,6 +21,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/types.h>
#include "_sd-common.h" #include "_sd-common.h"
@ -28,6 +29,9 @@ _SD_BEGIN_DECLARATIONS;
typedef struct sd_dhcp6_lease sd_dhcp6_lease; typedef struct sd_dhcp6_lease sd_dhcp6_lease;
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret);
void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease); void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
struct in6_addr *addr, struct in6_addr *addr,

View file

@ -93,6 +93,7 @@ int sd_event_add_signal(sd_event *e, sd_event_source **s, int sig, sd_event_sign
int sd_event_add_child(sd_event *e, sd_event_source **s, pid_t pid, int options, sd_event_child_handler_t callback, void *userdata); int sd_event_add_child(sd_event *e, sd_event_source **s, pid_t pid, int options, sd_event_child_handler_t callback, void *userdata);
int sd_event_add_child_pidfd(sd_event *e, sd_event_source **s, int pidfd, int options, sd_event_child_handler_t callback, void *userdata); int sd_event_add_child_pidfd(sd_event *e, sd_event_source **s, int pidfd, int options, sd_event_child_handler_t callback, void *userdata);
int sd_event_add_inotify(sd_event *e, sd_event_source **s, const char *path, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata); int sd_event_add_inotify(sd_event *e, sd_event_source **s, const char *path, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata);
int sd_event_add_inotify_fd(sd_event *e, sd_event_source **s, int fd, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata);
int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
@ -165,6 +166,7 @@ int sd_event_source_set_exit_on_failure(sd_event_source *s, int b);
int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval_usec, unsigned burst); int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval_usec, unsigned burst);
int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval_usec, unsigned *ret_burst); int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval_usec, unsigned *ret_burst);
int sd_event_source_is_ratelimited(sd_event_source *s); int sd_event_source_is_ratelimited(sd_event_source *s);
int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback);
/* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */ /* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref);

View file

@ -82,9 +82,6 @@ int sd_ndisc_set_ifname(sd_ndisc *nd, const char *interface_name);
int sd_ndisc_get_ifname(sd_ndisc *nd, const char **ret); 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_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *ret);
int sd_ndisc_get_hop_limit(sd_ndisc *nd, uint8_t *ret);
int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size); int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size);
sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt); sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt);
sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt); sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);

View file

@ -0,0 +1,3 @@
#pragma once
/* dummy header */

View file

@ -0,0 +1,3 @@
#pragma once
/* dummy header */

View file

@ -1,3 +1,5 @@
#pragma once #pragma once
/* dummy header */ /* dummy header */
#include <linux/magic.h>

View file

@ -0,0 +1,3 @@
#pragma once
/* dummy header */

View file

@ -22,20 +22,25 @@ typedef void (*free_func_t)(void *p);
#define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t))) #define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
#define alloca_safe(n) \
({ \
size_t _nn_ = n; \
assert(_nn_ <= ALLOCA_MAX); \
alloca(_nn_ == 0 ? 1 : _nn_); \
}) \
#define newa(t, n) \ #define newa(t, n) \
({ \ ({ \
size_t _n_ = n; \ size_t _n_ = n; \
assert(!size_multiply_overflow(sizeof(t), _n_)); \ assert(!size_multiply_overflow(sizeof(t), _n_)); \
assert(sizeof(t)*_n_ <= ALLOCA_MAX); \ (t*) alloca_safe(sizeof(t)*_n_); \
(t*) alloca((sizeof(t)*_n_) ?: 1); \
}) })
#define newa0(t, n) \ #define newa0(t, n) \
({ \ ({ \
size_t _n_ = n; \ size_t _n_ = n; \
assert(!size_multiply_overflow(sizeof(t), _n_)); \ assert(!size_multiply_overflow(sizeof(t), _n_)); \
assert(sizeof(t)*_n_ <= ALLOCA_MAX); \ (t*) alloca0((sizeof(t)*_n_)); \
(t*) alloca0((sizeof(t)*_n_) ?: 1); \
}) })
#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) #define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
@ -44,12 +49,6 @@ typedef void (*free_func_t)(void *p);
#define malloc0(n) (calloc(1, (n) ?: 1)) #define malloc0(n) (calloc(1, (n) ?: 1))
#define mfree(memory) \
({ \
free(memory); \
(typeof(memory)) NULL; \
})
#define free_and_replace(a, b) \ #define free_and_replace(a, b) \
({ \ ({ \
typeof(a)* _a = &(a); \ typeof(a)* _a = &(a); \
@ -67,8 +66,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s
({ \ ({ \
void *_q_; \ void *_q_; \
size_t _l_ = l; \ size_t _l_ = l; \
assert(_l_ <= ALLOCA_MAX); \ _q_ = alloca_safe(_l_); \
_q_ = alloca(_l_ ?: 1); \
memcpy_safe(_q_, p, _l_); \ memcpy_safe(_q_, p, _l_); \
}) })
@ -76,8 +74,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s
({ \ ({ \
void *_q_; \ void *_q_; \
size_t _l_ = l; \ size_t _l_ = l; \
assert(_l_ <= ALLOCA_MAX); \ _q_ = alloca_safe(_l_ + 1); \
_q_ = alloca(_l_ + 1); \
((uint8_t*) _q_)[_l_] = 0; \ ((uint8_t*) _q_)[_l_] = 0; \
memcpy_safe(_q_, p, _l_); \ memcpy_safe(_q_, p, _l_); \
}) })
@ -144,9 +141,8 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
({ \ ({ \
char *_new_; \ char *_new_; \
size_t _len_ = n; \ size_t _len_ = n; \
assert(_len_ <= ALLOCA_MAX); \ _new_ = alloca_safe(_len_); \
_new_ = alloca(_len_ ?: 1); \ memset(_new_, 0, _len_); \
(void *) memset(_new_, 0, _len_); \
}) })
/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */ /* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */
@ -155,8 +151,7 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
void *_ptr_; \ void *_ptr_; \
size_t _mask_ = (align) - 1; \ size_t _mask_ = (align) - 1; \
size_t _size_ = size; \ size_t _size_ = size; \
assert(_size_ <= ALLOCA_MAX); \ _ptr_ = alloca_safe(_size_ + _mask_); \
_ptr_ = alloca((_size_ + _mask_) ?: 1); \
(void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \ (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \
}) })
@ -165,7 +160,7 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
void *_new_; \ void *_new_; \
size_t _xsize_ = (size); \ size_t _xsize_ = (size); \
_new_ = alloca_align(_xsize_, (align)); \ _new_ = alloca_align(_xsize_, (align)); \
(void*)memset(_new_, 0, _xsize_); \ memset(_new_, 0, _xsize_); \
}) })
#if HAS_FEATURE_MEMORY_SANITIZER #if HAS_FEATURE_MEMORY_SANITIZER
@ -193,3 +188,19 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ __builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
MALLOC_SIZEOF_SAFE(x)/sizeof((x)[0]), \ MALLOC_SIZEOF_SAFE(x)/sizeof((x)[0]), \
VOID_0)) VOID_0))
/* These are like strdupa()/strndupa(), but honour ALLOCA_MAX */
#define strdupa_safe(s) \
({ \
const char *_t = (s); \
(char*) memdupa_suffix0(_t, strlen(_t)); \
})
#define strndupa_safe(s, n) \
({ \
const char *_t = (s); \
(char*) memdupa_suffix0(_t, strnlen(_t, (n))); \
})
#include "memory-util.h"

View file

@ -2,6 +2,7 @@
#pragma once #pragma once
#include <dirent.h> #include <dirent.h>
#include <fcntl.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
@ -33,6 +34,9 @@ typedef enum CGroupController {
CGROUP_CONTROLLER_BPF_FOREIGN, CGROUP_CONTROLLER_BPF_FOREIGN,
CGROUP_CONTROLLER_BPF_SOCKET_BIND, CGROUP_CONTROLLER_BPF_SOCKET_BIND,
CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES, CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES,
/* The BPF hook implementing RestrictFileSystems= is not defined here.
* It's applied as late as possible in exec_child() so we don't block
* our own unit setup code. */
_CGROUP_CONTROLLER_MAX, _CGROUP_CONTROLLER_MAX,
_CGROUP_CONTROLLER_INVALID = -EINVAL, _CGROUP_CONTROLLER_INVALID = -EINVAL,
@ -123,6 +127,20 @@ static inline bool CGROUP_CPU_SHARES_IS_OK(uint64_t x) {
(x >= CGROUP_CPU_SHARES_MIN && x <= CGROUP_CPU_SHARES_MAX); (x >= CGROUP_CPU_SHARES_MIN && x <= CGROUP_CPU_SHARES_MAX);
} }
/* Special values for the special {blkio,io}.bfq.weight attribute */
#define CGROUP_BFQ_WEIGHT_INVALID UINT64_MAX
#define CGROUP_BFQ_WEIGHT_MIN UINT64_C(1)
#define CGROUP_BFQ_WEIGHT_MAX UINT64_C(1000)
#define CGROUP_BFQ_WEIGHT_DEFAULT UINT64_C(100)
/* Convert the normal io.weight value to io.bfq.weight */
static inline uint64_t BFQ_WEIGHT(uint64_t io_weight) {
return
io_weight <= CGROUP_WEIGHT_DEFAULT ?
CGROUP_BFQ_WEIGHT_DEFAULT - (CGROUP_WEIGHT_DEFAULT - io_weight) * (CGROUP_BFQ_WEIGHT_DEFAULT - CGROUP_BFQ_WEIGHT_MIN) / (CGROUP_WEIGHT_DEFAULT - CGROUP_WEIGHT_MIN) :
CGROUP_BFQ_WEIGHT_DEFAULT + (io_weight - CGROUP_WEIGHT_DEFAULT) * (CGROUP_BFQ_WEIGHT_MAX - CGROUP_BFQ_WEIGHT_DEFAULT) / (CGROUP_WEIGHT_MAX - CGROUP_WEIGHT_DEFAULT);
}
/* Special values for the blkio.weight attribute */ /* Special values for the blkio.weight attribute */
#define CGROUP_BLKIO_WEIGHT_INVALID UINT64_MAX #define CGROUP_BLKIO_WEIGHT_INVALID UINT64_MAX
#define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10) #define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10)
@ -236,6 +254,7 @@ int cg_is_empty_recursive(const char *controller, const char *path);
int cg_get_root_path(char **path); int cg_get_root_path(char **path);
int cg_path_get_cgroupid(const char *path, uint64_t *ret);
int cg_path_get_session(const char *path, char **session); int cg_path_get_session(const char *path, char **session);
int cg_path_get_owner_uid(const char *path, uid_t *uid); int cg_path_get_owner_uid(const char *path, uid_t *uid);
int cg_path_get_unit(const char *path, char **unit); int cg_path_get_unit(const char *path, char **unit);
@ -311,3 +330,12 @@ typedef enum ManagedOOMPreference {
const char* managed_oom_preference_to_string(ManagedOOMPreference a) _const_; const char* managed_oom_preference_to_string(ManagedOOMPreference a) _const_;
ManagedOOMPreference managed_oom_preference_from_string(const char *s) _pure_; ManagedOOMPreference managed_oom_preference_from_string(const char *s) _pure_;
/* The structure to pass to name_to_handle_at() on cgroupfs2 */
typedef union {
struct file_handle file_handle;
uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)];
} cg_file_handle;
#define CG_FILE_HANDLE_INIT { .file_handle.handle_bytes = sizeof(uint64_t) }
#define CG_FILE_HANDLE_CGROUPID(fh) (*(uint64_t*) (fh).file_handle.f_handle)

View file

@ -10,6 +10,7 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "env-util.h" #include "env-util.h"
#include "errno-util.h"
#include "escape.h" #include "escape.h"
#include "extract-word.h" #include "extract-word.h"
#include "macro.h" #include "macro.h"
@ -397,7 +398,7 @@ int strv_env_replace_consume(char ***l, char *p) {
return -EINVAL; return -EINVAL;
} }
name = strndupa(p, t - p); name = strndupa_safe(p, t - p);
STRV_FOREACH(f, *l) STRV_FOREACH(f, *l)
if (env_entry_has_name(*f, name)) { if (env_entry_has_name(*f, name)) {
@ -484,7 +485,7 @@ char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
if (flags & REPLACE_ENV_USE_ENVIRONMENT) { if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
const char *t; const char *t;
t = strndupa(name, k); t = strndupa_safe(name, k);
return getenv(t); return getenv(t);
}; };
@ -791,15 +792,12 @@ int getenv_bool_secure(const char *p) {
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
int set_unset_env(const char *name, const char *value, bool overwrite) { int set_unset_env(const char *name, const char *value, bool overwrite) {
int r; assert(name);
if (value) if (value)
r = setenv(name, value, overwrite); return RET_NERRNO(setenv(name, value, overwrite));
else
r = unsetenv(name); return RET_NERRNO(unsetenv(name));
if (r < 0)
return -errno;
return 0;
} }
int putenv_dup(const char *assignment, bool override) { int putenv_dup(const char *assignment, bool override) {
@ -809,12 +807,10 @@ int putenv_dup(const char *assignment, bool override) {
if (!e) if (!e)
return -EINVAL; return -EINVAL;
n = strndupa(assignment, e - assignment); n = strndupa_safe(assignment, e - assignment);
/* This is like putenv(), but uses setenv() so that our memory doesn't become part of environ[]. */ /* This is like putenv(), but uses setenv() so that our memory doesn't become part of environ[]. */
if (setenv(n, e + 1, override) < 0) return RET_NERRNO(setenv(n, e + 1, override));
return -errno;
return 0;
} }
int setenv_systemd_exec_pid(bool update_only) { int setenv_systemd_exec_pid(bool update_only) {

View file

@ -13,7 +13,7 @@ static inline void _reset_errno_(int *saved_errno) {
errno = *saved_errno; errno = *saved_errno;
} }
#define PROTECT_ERRNO \ #define PROTECT_ERRNO \
_cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno _cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno
#define UNPROTECT_ERRNO \ #define UNPROTECT_ERRNO \
@ -31,6 +31,29 @@ static inline int negative_errno(void) {
return -errno; return -errno;
} }
static inline int RET_NERRNO(int ret) {
/* Helper to wrap system calls in to make them return negative errno errors. This brings system call
* error handling in sync with how we usually handle errors in our own code, i.e. with immediate
* returning of negative errno. Usage is like this:
*
*
* r = RET_NERRNO(unlink(t));
*
*
* or
*
*
* fd = RET_NERRNO(open("/etc/fstab", O_RDONLY|O_CLOEXEC));
*
*/
if (ret < 0)
return negative_errno();
return ret;
}
static inline const char *strerror_safe(int error) { static inline const char *strerror_safe(int error) {
/* 'safe' here does NOT mean thread safety. */ /* 'safe' here does NOT mean thread safety. */
return strerror(abs(error)); /* lgtm [cpp/potentially-dangerous-function] */ return strerror(abs(error)); /* lgtm [cpp/potentially-dangerous-function] */
@ -47,6 +70,13 @@ static inline int errno_or_else(int fallback) {
return -abs(fallback); return -abs(fallback);
} }
/* For send()/recv() or read()/write(). */
static inline bool ERRNO_IS_TRANSIENT(int r) {
return IN_SET(abs(r),
EAGAIN,
EINTR);
}
/* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5. /* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5.
* *
* Hint #2: The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases. See the * Hint #2: The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases. See the
@ -77,10 +107,8 @@ static inline bool ERRNO_IS_DISCONNECT(int r) {
* the accept(2) man page. */ * the accept(2) man page. */
static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) { static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) {
return ERRNO_IS_DISCONNECT(r) || return ERRNO_IS_DISCONNECT(r) ||
IN_SET(abs(r), ERRNO_IS_TRANSIENT(r) ||
EAGAIN, abs(r) == EOPNOTSUPP;
EINTR,
EOPNOTSUPP);
} }
/* Resource exhaustion, could be our fault or general system trouble */ /* Resource exhaustion, could be our fault or general system trouble */

View file

@ -547,7 +547,7 @@ char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) {
return str_realloc(buf); return str_realloc(buf);
} }
char* quote_command_line(char **argv) { char* quote_command_line(char **argv, ShellEscapeFlags flags) {
_cleanup_free_ char *result = NULL; _cleanup_free_ char *result = NULL;
assert(argv); assert(argv);
@ -556,7 +556,7 @@ char* quote_command_line(char **argv) {
STRV_FOREACH(a, argv) { STRV_FOREACH(a, argv) {
_cleanup_free_ char *t = NULL; _cleanup_free_ char *t = NULL;
t = shell_maybe_quote(*a, SHELL_ESCAPE_EMPTY); t = shell_maybe_quote(*a, flags);
if (!t) if (!t)
return NULL; return NULL;
@ -564,6 +564,6 @@ char* quote_command_line(char **argv) {
return NULL; return NULL;
} }
return TAKE_PTR(result); return str_realloc(TAKE_PTR(result));
} }
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */

View file

@ -69,4 +69,4 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
char* shell_escape(const char *s, const char *bad); char* shell_escape(const char *s, const char *bad);
char* shell_maybe_quote(const char *s, ShellEscapeFlags flags); char* shell_maybe_quote(const char *s, ShellEscapeFlags flags);
char* quote_command_line(char **argv); char* quote_command_line(char **argv, ShellEscapeFlags flags);

View file

@ -9,20 +9,29 @@
#include <sys/types.h> #include <sys/types.h>
#include "ether-addr-util.h" #include "ether-addr-util.h"
#include "hexdecoct.h"
#include "macro.h" #include "macro.h"
#include "string-util.h" #include "string-util.h"
char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]) { char *hw_addr_to_string_full(
const struct hw_addr_data *addr,
HardwareAddressToStringFlags flags,
char buffer[static HW_ADDR_TO_STRING_MAX]) {
assert(addr); assert(addr);
assert(buffer); assert(buffer);
assert(addr->length <= HW_ADDR_MAX_SIZE); assert(addr->length <= HW_ADDR_MAX_SIZE);
for (size_t i = 0; i < addr->length; i++) { for (size_t i = 0, j = 0; i < addr->length; i++) {
sprintf(&buffer[3*i], "%02"PRIx8, addr->bytes[i]); buffer[j++] = hexchar(addr->bytes[i] >> 4);
if (i < addr->length - 1) buffer[j++] = hexchar(addr->bytes[i] & 0x0f);
buffer[3*i + 2] = ':'; if (!FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON))
buffer[j++] = ':';
} }
buffer[addr->length == 0 || FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON) ?
addr->length * 2 :
addr->length * 3 - 1] = '\0';
return buffer; return buffer;
} }
@ -39,7 +48,7 @@ int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b)
return memcmp(a->bytes, b->bytes, a->length); return memcmp(a->bytes, b->bytes, a->length);
} }
static void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) { void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
assert(p); assert(p);
assert(state); assert(state);
@ -48,6 +57,7 @@ static void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *stat
} }
DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare); DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(hw_addr_hash_ops_free, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare, free);
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) { char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
assert(addr); assert(addr);
@ -93,75 +103,163 @@ static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *sta
} }
DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare); DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(ether_addr_hash_ops_free, struct ether_addr, ether_addr_hash_func, ether_addr_compare, free);
int ether_addr_from_string(const char *s, struct ether_addr *ret) { static int parse_hw_addr_one_field(const char **s, char sep, size_t len, uint8_t *buf) {
size_t pos = 0, n, field; const char *hex = HEXDIGITS, *p;
char sep = '\0'; uint16_t data = 0;
const char *hex = HEXDIGITS, *hexoff; bool cont;
size_t x;
bool touched;
#define parse_fields(v) \ assert(s);
for (field = 0; field < ELEMENTSOF(v); field++) { \ assert(*s);
touched = false; \ assert(IN_SET(len, 1, 2));
for (n = 0; n < (2 * sizeof(v[0])); n++) { \ assert(buf);
if (s[pos] == '\0') \
break; \ p = *s;
hexoff = strchr(hex, s[pos]); \
if (!hexoff) \ for (size_t i = 0; i < len * 2; i++) {
break; \ const char *hexoff;
assert(hexoff >= hex); \ size_t x;
x = hexoff - hex; \
if (x >= 16) \ if (*p == '\0' || *p == sep) {
x -= 6; /* A-F */ \ if (i == 0)
assert(x < 16); \ return -EINVAL;
touched = true; \ break;
v[field] <<= 4; \ }
v[field] += x; \
pos++; \ hexoff = strchr(hex, *p);
} \ if (!hexoff)
if (!touched) \ return -EINVAL;
return -EINVAL; \
if (field < (ELEMENTSOF(v)-1)) { \ assert(hexoff >= hex);
if (s[pos] != sep) \ x = hexoff - hex;
return -EINVAL; \ if (x >= 16)
else \ x -= 6; /* A-F */
pos++; \
} \ assert(x < 16);
data <<= 4;
data += x;
p++;
} }
if (*p != '\0' && *p != sep)
return -EINVAL;
switch (len) {
case 1:
buf[0] = data;
break;
case 2:
buf[0] = (data & 0xff00) >> 8;
buf[1] = data & 0xff;
break;
default:
assert_not_reached();
}
cont = *p == sep;
*s = p + cont;
return cont;
}
int parse_hw_addr_full(const char *s, size_t expected_len, struct hw_addr_data *ret) {
size_t field_size, max_len, len = 0;
uint8_t bytes[HW_ADDR_MAX_SIZE];
char sep;
int r;
assert(s);
assert(expected_len <= HW_ADDR_MAX_SIZE || expected_len == SIZE_MAX);
assert(ret);
/* This accepts the following formats:
*
* Dot separated 2 bytes format: xxyy.zzaa.bbcc
* Colon separated 1 bytes format: xx:yy:zz:aa:bb:cc
* Hyphen separated 1 bytes format: xx-yy-zz-aa-bb-cc
*
* Moreover, if expected_len == 0, 4, or 16, this also accepts:
*
* IPv4 format: used by IPv4 tunnel, e.g. ipgre
* IPv6 format: used by IPv6 tunnel, e.g. ip6gre
*
* The expected_len argument controls the length of acceptable addresses:
*
* 0: accepts 4 (AF_INET), 16 (AF_INET6), 6 (ETH_ALEN), or 20 (INFINIBAND_ALEN).
* SIZE_MAX: accepts arbitrary length, but at least one separator must be included.
* Otherwise: accepts addresses with matching length.
*/
if (IN_SET(expected_len, 0, sizeof(struct in_addr), sizeof(struct in6_addr))) {
union in_addr_union a;
int family;
if (expected_len == 0)
r = in_addr_from_string_auto(s, &family, &a);
else {
family = expected_len == sizeof(struct in_addr) ? AF_INET : AF_INET6;
r = in_addr_from_string(family, s, &a);
}
if (r >= 0) {
ret->length = FAMILY_ADDRESS_SIZE(family);
memcpy(ret->bytes, a.bytes, ret->length);
return 0;
}
}
max_len =
expected_len == 0 ? INFINIBAND_ALEN :
expected_len == SIZE_MAX ? HW_ADDR_MAX_SIZE : expected_len;
sep = s[strspn(s, HEXDIGITS)];
if (sep == '.')
field_size = 2;
else if (IN_SET(sep, ':', '-'))
field_size = 1;
else
return -EINVAL;
if (max_len % field_size != 0)
return -EINVAL;
for (size_t i = 0; i < max_len / field_size; i++) {
r = parse_hw_addr_one_field(&s, sep, field_size, bytes + i * field_size);
if (r < 0)
return r;
if (r == 0) {
len = (i + 1) * field_size;
break;
}
}
if (len == 0)
return -EINVAL;
if (expected_len == 0) {
if (!IN_SET(len, 4, 16, ETH_ALEN, INFINIBAND_ALEN))
return -EINVAL;
} else if (expected_len != SIZE_MAX) {
if (len != expected_len)
return -EINVAL;
}
ret->length = len;
memcpy(ret->bytes, bytes, ret->length);
return 0;
}
int parse_ether_addr(const char *s, struct ether_addr *ret) {
struct hw_addr_data a;
int r;
assert(s); assert(s);
assert(ret); assert(ret);
s += strspn(s, WHITESPACE); r = parse_hw_addr_full(s, ETH_ALEN, &a);
sep = s[strspn(s, hex)]; if (r < 0)
return r;
if (sep == '.') {
uint16_t shorts[3] = { 0 };
parse_fields(shorts);
if (s[pos] != '\0')
return -EINVAL;
for (n = 0; n < ELEMENTSOF(shorts); n++) {
ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8);
ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff);
}
} else if (IN_SET(sep, ':', '-')) {
struct ether_addr out = ETHER_ADDR_NULL;
parse_fields(out.ether_addr_octet);
if (s[pos] != '\0')
return -EINVAL;
for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++)
ret->ether_addr_octet[n] = out.ether_addr_octet[n];
} else
return -EINVAL;
*ret = a.ether;
return 0; return 0;
} }

View file

@ -6,6 +6,9 @@
#include <stdbool.h> #include <stdbool.h>
#include "hash-funcs.h" #include "hash-funcs.h"
#include "in-addr-util.h"
#include "macro.h"
#include "memory-util.h"
/* This is MAX_ADDR_LEN as defined in linux/netdevice.h, but net/if_arp.h /* This is MAX_ADDR_LEN as defined in linux/netdevice.h, but net/if_arp.h
* defines a macro of the same name with a much lower size. */ * defines a macro of the same name with a much lower size. */
@ -16,29 +19,51 @@ struct hw_addr_data {
union { union {
struct ether_addr ether; struct ether_addr ether;
uint8_t infiniband[INFINIBAND_ALEN]; uint8_t infiniband[INFINIBAND_ALEN];
struct in_addr in;
struct in6_addr in6;
uint8_t bytes[HW_ADDR_MAX_SIZE]; uint8_t bytes[HW_ADDR_MAX_SIZE];
}; };
}; };
int parse_hw_addr_full(const char *s, size_t expected_len, struct hw_addr_data *ret);
static inline int parse_hw_addr(const char *s, struct hw_addr_data *ret) {
return parse_hw_addr_full(s, 0, ret);
}
int parse_ether_addr(const char *s, struct ether_addr *ret);
typedef enum HardwareAddressToStringFlags {
HW_ADDR_TO_STRING_NO_COLON = 1 << 0,
} HardwareAddressToStringFlags;
#define HW_ADDR_TO_STRING_MAX (3*HW_ADDR_MAX_SIZE) #define HW_ADDR_TO_STRING_MAX (3*HW_ADDR_MAX_SIZE)
char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]); char *hw_addr_to_string_full(
const struct hw_addr_data *addr,
HardwareAddressToStringFlags flags,
char buffer[static HW_ADDR_TO_STRING_MAX]);
static inline char *hw_addr_to_string(const struct hw_addr_data *addr, char buffer[static HW_ADDR_TO_STRING_MAX]) {
return hw_addr_to_string_full(addr, 0, buffer);
}
/* Note: the lifetime of the compound literal is the immediately surrounding block, /* Note: the lifetime of the compound literal is the immediately surrounding block,
* see C11 §6.5.2.5, and * see C11 §6.5.2.5, and
* https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */ * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
#define HW_ADDR_TO_STR(hw_addr) hw_addr_to_string((hw_addr), (char[HW_ADDR_TO_STRING_MAX]){}) #define HW_ADDR_TO_STR_FULL(hw_addr, flags) hw_addr_to_string_full((hw_addr), flags, (char[HW_ADDR_TO_STRING_MAX]){})
#define HW_ADDR_TO_STR(hw_addr) HW_ADDR_TO_STR_FULL(hw_addr, 0)
#define HW_ADDR_NULL ((const struct hw_addr_data){}) #define HW_ADDR_NULL ((const struct hw_addr_data){})
void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state);
int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b); int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b);
static inline bool hw_addr_equal(const struct hw_addr_data *a, const struct hw_addr_data *b) { static inline bool hw_addr_equal(const struct hw_addr_data *a, const struct hw_addr_data *b) {
return hw_addr_compare(a, b) == 0; return hw_addr_compare(a, b) == 0;
} }
static inline bool hw_addr_is_null(const struct hw_addr_data *addr) { static inline bool hw_addr_is_null(const struct hw_addr_data *addr) {
return hw_addr_equal(addr, &HW_ADDR_NULL); assert(addr);
return addr->length == 0 || memeqzero(addr->bytes, addr->length);
} }
extern const struct hash_ops hw_addr_hash_ops; extern const struct hash_ops hw_addr_hash_ops;
extern const struct hash_ops hw_addr_hash_ops_free;
#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X" #define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X"
#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5] #define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
@ -60,6 +85,29 @@ static inline bool ether_addr_is_null(const struct ether_addr *addr) {
return ether_addr_equal(addr, &ETHER_ADDR_NULL); return ether_addr_equal(addr, &ETHER_ADDR_NULL);
} }
int ether_addr_from_string(const char *s, struct ether_addr *ret); static inline bool ether_addr_is_broadcast(const struct ether_addr *addr) {
assert(addr);
return memeqbyte(0xff, addr->ether_addr_octet, ETH_ALEN);
}
static inline bool ether_addr_is_multicast(const struct ether_addr *addr) {
assert(addr);
return FLAGS_SET(addr->ether_addr_octet[0], 0x01);
}
static inline bool ether_addr_is_unicast(const struct ether_addr *addr) {
return !ether_addr_is_multicast(addr);
}
static inline bool ether_addr_is_local(const struct ether_addr *addr) {
/* Determine if the Ethernet address is locally-assigned one (IEEE 802) */
assert(addr);
return FLAGS_SET(addr->ether_addr_octet[0], 0x02);
}
static inline bool ether_addr_is_global(const struct ether_addr *addr) {
return !ether_addr_is_local(addr);
}
extern const struct hash_ops ether_addr_hash_ops; extern const struct hash_ops ether_addr_hash_ops;
extern const struct hash_ops ether_addr_hash_ops_free;

View file

@ -154,10 +154,7 @@ int fd_nonblock(int fd, bool nonblock) {
if (nflags == flags) if (nflags == flags)
return 0; return 0;
if (fcntl(fd, F_SETFL, nflags) < 0) return RET_NERRNO(fcntl(fd, F_SETFL, nflags));
return -errno;
return 0;
} }
int fd_cloexec(int fd, bool cloexec) { int fd_cloexec(int fd, bool cloexec) {
@ -173,10 +170,7 @@ int fd_cloexec(int fd, bool cloexec) {
if (nflags == flags) if (nflags == flags)
return 0; return 0;
if (fcntl(fd, F_SETFD, nflags) < 0) return RET_NERRNO(fcntl(fd, F_SETFD, nflags));
return -errno;
return 0;
} }
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
@ -190,7 +184,7 @@ _pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
return false; return false;
} }
static int get_max_fd(void) { int get_max_fd(void) {
struct rlimit rl; struct rlimit rl;
rlim_t m; rlim_t m;
@ -211,104 +205,17 @@ static int get_max_fd(void) {
return (int) (m - 1); return (int) (m - 1);
} }
int close_all_fds_full(int except[], size_t n_except, bool allow_alloc) { static int close_all_fds_frugal(const int except[], size_t n_except) {
static bool have_close_range = true; /* Assume we live in the future */ int max_fd, r = 0;
_cleanup_closedir_ DIR *d = NULL;
int r = 0;
assert(n_except == 0 || except); assert(n_except == 0 || except);
if (have_close_range) { /* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so
/* In the best case we have close_range() to close all fds between a start and an end fd, * and hence is safe to be called in signal handler context. Most users should call close_all_fds(),
* which we can use on the "inverted" exception array, i.e. all intervals between all * but when we assume we are called from signal handler context, then use this simpler call
* adjacent pairs from the sorted exception array. This changes loop complexity from O(n) * instead. */
* where n is number of open fds to O(mlog(m)) where m is the number of fds to keep
* open. Given that we assume n m that's preferable to us. */
if (n_except == 0) { max_fd = get_max_fd();
/* Close everything. Yay! */
if (close_range(3, -1, 0) >= 0)
return 0;
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
have_close_range = false;
else
return -errno;
} else {
typesafe_qsort(except, n_except, cmp_int);
for (size_t i = 0; i < n_except; i++) {
int start = i == 0 ? 2 : MAX(except[i-1], 2); /* The first three fds shall always remain open */
int end = MAX(except[i], 2);
assert(end >= start);
if (end - start <= 1)
continue;
/* Close everything between the start and end fds (both of which shall stay open) */
if (close_range(start + 1, end - 1, 0) < 0) {
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
have_close_range = false;
else
return -errno;
goto opendir_fallback;
}
}
/* The loop succeeded. Let's now close everything beyond the end */
if (except[n_except-1] >= INT_MAX) /* Don't let the addition below overflow */
return 0;
int start = MAX(except[n_except-1], 2);
if (close_range(start + 1, -1, 0) >= 0)
return 0;
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
have_close_range = false;
else
return -errno;
}
}
/* Fallback for when close_range() is not supported */
opendir_fallback:
d = allow_alloc ? opendir("/proc/self/fd") : NULL;
if (d) {
struct dirent *de;
FOREACH_DIRENT(de, d, return -errno) {
int fd = -1, q;
if (safe_atoi(de->d_name, &fd) < 0)
/* Let's better ignore this, just in case */
continue;
if (fd < 3)
continue;
if (fd == dirfd(d))
continue;
if (fd_in_set(fd, except, n_except))
continue;
q = close_nointr(fd);
if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
r = q;
}
return r;
}
/* Fallback for when /proc isn't available (for example in chroots) or when we cannot allocate by
* brute-forcing through the file descriptor table. */
int max_fd = get_max_fd();
if (max_fd < 0) if (max_fd < 0)
return max_fd; return max_fd;
@ -316,7 +223,7 @@ int close_all_fds_full(int except[], size_t n_except, bool allow_alloc) {
* spin the CPU for a long time. */ * spin the CPU for a long time. */
if (max_fd > MAX_FD_LOOP_LIMIT) if (max_fd > MAX_FD_LOOP_LIMIT)
return log_debug_errno(SYNTHETIC_ERRNO(EPERM), return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
"/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.", "Refusing to loop over %d potential fds.",
max_fd); max_fd);
for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
@ -333,6 +240,178 @@ int close_all_fds_full(int except[], size_t n_except, bool allow_alloc) {
return r; return r;
} }
static bool have_close_range = true; /* Assume we live in the future */
static int close_all_fds_special_case(const int except[], size_t n_except) {
assert(n_except == 0 || except);
/* Handles a few common special cases separately, since they are common and can be optimized really
* nicely, since we won't need sorting for them. Returns > 0 if the special casing worked, 0
* otherwise. */
if (!have_close_range)
return 0;
switch (n_except) {
case 0:
/* Close everything. Yay! */
if (close_range(3, -1, 0) >= 0)
return 1;
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) {
have_close_range = false;
return 0;
}
return -errno;
case 1:
/* Close all but exactly one, then we don't need no sorting. This is a pretty common
* case, hence let's handle it specially. */
if ((except[0] <= 3 || close_range(3, except[0]-1, 0) >= 0) &&
(except[0] >= INT_MAX || close_range(MAX(3, except[0]+1), -1, 0) >= 0))
return 1;
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) {
have_close_range = false;
return 0;
}
return -errno;
default:
return 0;
}
}
int close_all_fds_without_malloc(const int except[], size_t n_except) {
int r;
assert(n_except == 0 || except);
r = close_all_fds_special_case(except, n_except);
if (r < 0)
return r;
if (r > 0) /* special case worked! */
return 0;
return close_all_fds_frugal(except, n_except);
}
int close_all_fds(const int except[], size_t n_except) {
_cleanup_closedir_ DIR *d = NULL;
int r = 0;
assert(n_except == 0 || except);
r = close_all_fds_special_case(except, n_except);
if (r < 0)
return r;
if (r > 0) /* special case worked! */
return 0;
if (have_close_range) {
_cleanup_free_ int *sorted_malloc = NULL;
size_t n_sorted;
int *sorted;
/* In the best case we have close_range() to close all fds between a start and an end fd,
* which we can use on the "inverted" exception array, i.e. all intervals between all
* adjacent pairs from the sorted exception array. This changes loop complexity from O(n)
* where n is number of open fds to O(mlog(m)) where m is the number of fds to keep
* open. Given that we assume n m that's preferable to us. */
assert(n_except < SIZE_MAX);
n_sorted = n_except + 1;
if (n_sorted > 64) /* Use heap for large numbers of fds, stack otherwise */
sorted = sorted_malloc = new(int, n_sorted);
else
sorted = newa(int, n_sorted);
if (sorted) {
memcpy(sorted, except, n_except * sizeof(int));
/* Let's add fd 2 to the list of fds, to simplify the loop below, as this
* allows us to cover the head of the array the same way as the body */
sorted[n_sorted-1] = 2;
typesafe_qsort(sorted, n_sorted, cmp_int);
for (size_t i = 0; i < n_sorted-1; i++) {
int start, end;
start = MAX(sorted[i], 2); /* The first three fds shall always remain open */
end = MAX(sorted[i+1], 2);
assert(end >= start);
if (end - start <= 1)
continue;
/* Close everything between the start and end fds (both of which shall stay open) */
if (close_range(start + 1, end - 1, 0) < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
have_close_range = false;
break;
}
}
if (have_close_range) {
/* The loop succeeded. Let's now close everything beyond the end */
if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */
return 0;
if (close_range(sorted[n_sorted-1] + 1, -1, 0) >= 0)
return 0;
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
have_close_range = false;
}
}
/* Fallback on OOM or if close_range() is not supported */
}
d = opendir("/proc/self/fd");
if (!d)
return close_all_fds_frugal(except, n_except); /* ultimate fallback if /proc/ is not available */
FOREACH_DIRENT(de, d, return -errno) {
int fd = -1, q;
if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
continue;
if (safe_atoi(de->d_name, &fd) < 0)
/* Let's better ignore this, just in case */
continue;
if (fd < 3)
continue;
if (fd == dirfd(d))
continue;
if (fd_in_set(fd, except, n_except))
continue;
q = close_nointr(fd);
if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
r = q;
}
return r;
}
int same_fd(int a, int b) { int same_fd(int a, int b) {
struct stat sta, stb; struct stat sta, stb;
pid_t pid; pid_t pid;
@ -652,7 +731,7 @@ finish:
} }
int fd_reopen(int fd, int flags) { int fd_reopen(int fd, int flags) {
int new_fd; int new_fd, r;
/* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
* turn O_RDWR fds into O_RDONLY fds. * turn O_RDWR fds into O_RDONLY fds.
@ -661,15 +740,28 @@ int fd_reopen(int fd, int flags) {
* *
* This implicitly resets the file read index to 0. */ * This implicitly resets the file read index to 0. */
if (FLAGS_SET(flags, O_DIRECTORY)) {
/* If we shall reopen the fd as directory we can just go via "." and thus bypass the whole
* magic /proc/ directory, and make ourselves independent of that being mounted. */
new_fd = openat(fd, ".", flags);
if (new_fd < 0)
return -errno;
return new_fd;
}
new_fd = open(FORMAT_PROC_FD_PATH(fd), flags); new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
if (new_fd < 0) { if (new_fd < 0) {
if (errno != ENOENT) if (errno != ENOENT)
return -errno; return -errno;
if (proc_mounted() == 0) r = proc_mounted();
if (r == 0)
return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
return -ENOENT; return r > 0 ? -EBADF : -ENOENT; /* If /proc/ is definitely around then this means the fd is
* not valid, otherwise let's propagate the original
* error */
} }
return new_fd; return new_fd;
@ -710,9 +802,6 @@ int btrfs_defrag_fd(int fd) {
if (r < 0) if (r < 0)
return r; return r;
if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0) return RET_NERRNO(ioctl(fd, BTRFS_IOC_DEFRAG, NULL));
return -errno;
return 0;
} }
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */

View file

@ -57,10 +57,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
int fd_nonblock(int fd, bool nonblock); int fd_nonblock(int fd, bool nonblock);
int fd_cloexec(int fd, bool cloexec); int fd_cloexec(int fd, bool cloexec);
int close_all_fds_full(int except[], size_t n_except, bool allow_alloc); int get_max_fd(void);
static inline int close_all_fds(int except[], size_t n_except) {
return close_all_fds_full(except, n_except, true); int close_all_fds(const int except[], size_t n_except);
} int close_all_fds_without_malloc(const int except[], size_t n_except);
int same_fd(int a, int b); int same_fd(int a, int b);
@ -91,9 +91,10 @@ static inline int make_null_stdio(void) {
/* Like TAKE_PTR() but for file descriptors, resetting them to -1 */ /* Like TAKE_PTR() but for file descriptors, resetting them to -1 */
#define TAKE_FD(fd) \ #define TAKE_FD(fd) \
({ \ ({ \
int _fd_ = (fd); \ int *_fd_ = &(fd); \
(fd) = -1; \ int _ret_ = *_fd_; \
_fd_; \ *_fd_ = -1; \
_ret_; \
}) })
/* Like free_and_replace(), but for file descriptors */ /* Like free_and_replace(), but for file descriptors */

View file

@ -169,7 +169,7 @@ int write_string_stream_ts(
* it won't be equal to the new value. */ * it won't be equal to the new value. */
if (read_virtual_file_fd(fd, strlen(line)+1, &t, NULL) > 0 && if (read_virtual_file_fd(fd, strlen(line)+1, &t, NULL) > 0 &&
streq_skip_trailing_chars(line, t, NEWLINE)) { streq_skip_trailing_chars(line, t, NEWLINE)) {
log_debug("No change in value '%s', supressing write", line); log_debug("No change in value '%s', suppressing write", line);
return 0; return 0;
} }
@ -551,12 +551,25 @@ int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *r
return !truncated; return !truncated;
} }
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) { int read_virtual_file_at(
int dir_fd,
const char *filename,
size_t max_size,
char **ret_contents,
size_t *ret_size) {
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
assert(filename); assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
fd = open(filename, O_RDONLY | O_NOCTTY | O_CLOEXEC); if (!filename) {
if (dir_fd == AT_FDCWD)
return -EBADF;
return read_virtual_file_fd(dir_fd, max_size, ret_contents, ret_size);
}
fd = openat(dir_fd, filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
if (fd < 0) if (fd < 0)
return -errno; return -errno;
@ -934,6 +947,9 @@ DIR *xopendirat(int fd, const char *name, int flags) {
assert(!(flags & O_CREAT)); assert(!(flags & O_CREAT));
if (fd == AT_FDCWD && flags == 0)
return opendir(name);
nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0); nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
if (nfd < 0) if (nfd < 0)
return NULL; return NULL;

View file

@ -69,7 +69,10 @@ static inline int read_full_file(const char *filename, char **ret_contents, size
} }
int read_virtual_file_fd(int fd, 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);
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size); int read_virtual_file_at(int dir_fd, const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
static inline int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
return read_virtual_file_at(AT_FDCWD, filename, max_size, ret_contents, ret_size);
}
static inline int read_full_virtual_file(const char *filename, 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); return read_virtual_file(filename, SIZE_MAX, ret_contents, ret_size);
} }

View file

@ -26,11 +26,15 @@ assert_cc(sizeof(gid_t) == sizeof(uint32_t));
# error Unknown time_t size # error Unknown time_t size
#endif #endif
#if defined __x86_64__ && defined __ILP32__ #if 0 /* NM_IGNORED */
#if SIZEOF_TIMEX_MEMBER == 8
# define PRI_TIMEX PRIi64 # define PRI_TIMEX PRIi64
#else #elif SIZEOF_TIMEX_MEMBER == 4
# define PRI_TIMEX "li" # define PRI_TIMEX "li"
#else
# error Unknown timex member size
#endif #endif
#endif /* NM_IGNORED */
#if SIZEOF_RLIM_T == 8 #if SIZEOF_RLIM_T == 8
# define RLIM_FMT "%" PRIu64 # define RLIM_FMT "%" PRIu64

View file

@ -14,6 +14,7 @@
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
#include "fs-util.h" #include "fs-util.h"
#include "hostname-util.h"
#include "log.h" #include "log.h"
#include "macro.h" #include "macro.h"
#include "missing_fcntl.h" #include "missing_fcntl.h"
@ -31,18 +32,13 @@
#include "strv.h" #include "strv.h"
#include "time-util.h" #include "time-util.h"
#include "tmpfile-util.h" #include "tmpfile-util.h"
#include "umask-util.h"
#include "user-util.h" #include "user-util.h"
#include "util.h" #include "util.h"
int unlink_noerrno(const char *path) { int unlink_noerrno(const char *path) {
PROTECT_ERRNO; PROTECT_ERRNO;
int r; return RET_NERRNO(unlink(path));
r = unlink(path);
if (r < 0)
return -errno;
return 0;
} }
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
@ -59,7 +55,7 @@ int rmdir_parents(const char *path, const char *stop) {
if (!path_is_safe(stop)) if (!path_is_safe(stop))
return -EINVAL; return -EINVAL;
p = strdupa(path); p = strdupa_safe(path);
for (;;) { for (;;) {
char *slash = NULL; char *slash = NULL;
@ -99,8 +95,8 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
* want though not atomic (i.e. for a short period both the new and the old filename will exist). */ * want though not atomic (i.e. for a short period both the new and the old filename will exist). */
if (linkat(olddirfd, oldpath, newdirfd, newpath, 0) >= 0) { if (linkat(olddirfd, oldpath, newdirfd, newpath, 0) >= 0) {
if (unlinkat(olddirfd, oldpath, 0) < 0) { r = RET_NERRNO(unlinkat(olddirfd, oldpath, 0));
r = -errno; /* Backup errno before the following unlinkat() alters it */ if (r < 0) {
(void) unlinkat(newdirfd, newpath, 0); (void) unlinkat(newdirfd, newpath, 0);
return r; return r;
} }
@ -119,10 +115,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
if (errno != ENOENT) if (errno != ENOENT)
return -errno; return -errno;
if (renameat(olddirfd, oldpath, newdirfd, newpath) < 0) return RET_NERRNO(renameat(olddirfd, oldpath, newdirfd, newpath));
return -errno;
return 0;
} }
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
@ -290,14 +283,9 @@ int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t
} }
int fchmod_umask(int fd, mode_t m) { int fchmod_umask(int fd, mode_t m) {
mode_t u; _cleanup_umask_ mode_t u = umask(0777);
int r;
u = umask(0777); return RET_NERRNO(fchmod(fd, m & (~u)));
r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
umask(u);
return r;
} }
int fchmod_opath(int fd, mode_t m) { int fchmod_opath(int fd, mode_t m) {
@ -550,7 +538,6 @@ int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) {
int get_files_in_directory(const char *path, char ***list) { int get_files_in_directory(const char *path, char ***list) {
_cleanup_strv_free_ char **l = NULL; _cleanup_strv_free_ char **l = NULL;
_cleanup_closedir_ DIR *d = NULL; _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
size_t n = 0; size_t n = 0;
assert(path); assert(path);
@ -830,7 +817,7 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
int open_parent(const char *path, int flags, mode_t mode) { int open_parent(const char *path, int flags, mode_t mode) {
_cleanup_free_ char *parent = NULL; _cleanup_free_ char *parent = NULL;
int fd, r; int r;
r = path_extract_directory(path, &parent); r = path_extract_directory(path, &parent);
if (r < 0) if (r < 0)
@ -844,11 +831,7 @@ int open_parent(const char *path, int flags, mode_t mode) {
else if (!FLAGS_SET(flags, O_TMPFILE)) else if (!FLAGS_SET(flags, O_TMPFILE))
flags |= O_DIRECTORY|O_RDONLY; flags |= O_DIRECTORY|O_RDONLY;
fd = open(parent, flags, mode); return RET_NERRNO(open(parent, flags, mode));
if (fd < 0)
return -errno;
return fd;
} }
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
@ -962,3 +945,152 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size) {
return -EINTR; return -EINTR;
} }
int parse_cifs_service(
const char *s,
char **ret_host,
char **ret_service,
char **ret_path) {
_cleanup_free_ char *h = NULL, *ss = NULL, *x = NULL;
const char *p, *e, *d;
char delimiter;
/* Parses a CIFS service in form of //host/service/path… and splitting it in three parts. The last
* part is optional, in which case NULL is returned there. To maximize compatibility syntax with
* backslashes instead of slashes is accepted too. */
if (!s)
return -EINVAL;
p = startswith(s, "//");
if (!p) {
p = startswith(s, "\\\\");
if (!p)
return -EINVAL;
}
delimiter = s[0];
e = strchr(p, delimiter);
if (!e)
return -EINVAL;
h = strndup(p, e - p);
if (!h)
return -ENOMEM;
if (!hostname_is_valid(h, 0))
return -EINVAL;
e++;
d = strchrnul(e, delimiter);
ss = strndup(e, d - e);
if (!ss)
return -ENOMEM;
if (!filename_is_valid(ss))
return -EINVAL;
if (!isempty(d)) {
x = strdup(skip_leading_chars(d, CHAR_TO_STR(delimiter)));
if (!x)
return -EINVAL;
/* Make sure to convert Windows-style "\" → Unix-style / */
for (char *i = x; *i; i++)
if (*i == delimiter)
*i = '/';
if (!path_is_valid(x))
return -EINVAL;
path_simplify(x);
if (!path_is_normalized(x))
return -EINVAL;
}
if (ret_host)
*ret_host = TAKE_PTR(h);
if (ret_service)
*ret_service = TAKE_PTR(ss);
if (ret_path)
*ret_path = TAKE_PTR(x);
return 0;
}
int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
_cleanup_close_ int fd = -1, parent_fd = -1;
_cleanup_free_ char *fname = NULL;
bool made;
int r;
/* Creates a directory with mkdirat() and then opens it, in the "most atomic" fashion we can
* do. Guarantees that the returned fd refers to a directory. If O_EXCL is specified will fail if the
* dir already exists. Otherwise will open an existing dir, but only if it is one. */
if (flags & ~(O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_EXCL|O_NOATIME|O_NOFOLLOW|O_PATH))
return -EINVAL;
if ((flags & O_ACCMODE) != O_RDONLY)
return -EINVAL;
/* Note that O_DIRECTORY|O_NOFOLLOW is implied, but we allow specifying it anyway. The following
* flags actually make sense to specify: O_CLOEXEC, O_EXCL, O_NOATIME, O_PATH */
if (isempty(path))
return -EINVAL;
if (!filename_is_valid(path)) {
_cleanup_free_ char *parent = NULL;
/* If this is not a valid filename, it's a path. Let's open the parent directory then, so
* that we can pin it, and operate below it. */
r = path_extract_directory(path, &parent);
if (r < 0)
return r;
r = path_extract_filename(path, &fname);
if (r < 0)
return r;
parent_fd = openat(dirfd, parent, O_PATH|O_DIRECTORY|O_CLOEXEC);
if (parent_fd < 0)
return -errno;
dirfd = parent_fd;
path = fname;
}
r = RET_NERRNO(mkdirat(dirfd, path, mode));
if (r == -EEXIST) {
if (FLAGS_SET(flags, O_EXCL))
return -EEXIST;
made = false;
} else if (r < 0)
return r;
else
made = true;
fd = RET_NERRNO(openat(dirfd, path, (flags & ~O_EXCL)|O_DIRECTORY|O_NOFOLLOW));
if (fd < 0) {
if (fd == -ENOENT) /* We got ENOENT? then someone else immediately removed it after we
* created it. In that case let's return immediately without unlinking
* anything, because there simply isn't anything to unlink anymore. */
return -ENOENT;
if (fd == -ELOOP) /* is a symlink? exists already → created by someone else, don't unlink */
return -EEXIST;
if (fd == -ENOTDIR) /* not a directory? exists already → created by someone else, don't unlink */
return -EEXIST;
if (made)
(void) unlinkat(dirfd, path, AT_REMOVEDIR);
return fd;
}
return TAKE_FD(fd);
}

View file

@ -47,7 +47,7 @@ int fd_warn_permissions(const char *path, int fd);
int stat_warn_permissions(const char *path, const struct stat *st); int stat_warn_permissions(const char *path, const struct stat *st);
#define laccess(path, mode) \ #define laccess(path, mode) \
(faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) < 0 ? -errno : 0) RET_NERRNO(faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW))
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode); int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
int touch(const char *path); int touch(const char *path);
@ -106,3 +106,7 @@ static inline int conservative_rename(const char *oldpath, const char *newpath)
} }
int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size); int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size);
int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char **ret_path);
int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode);

View file

@ -49,8 +49,7 @@ int gethostname_full(GetHostnameFlags flags, char **ret) {
assert_se(uname(&u) >= 0); assert_se(uname(&u) >= 0);
s = u.nodename; s = u.nodename;
if (isempty(s) || if (isempty(s) || streq(s, "(none)") ||
(!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_ALLOW_LOCALHOST) && is_localhost(s)) ||
(FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')) { (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')) {
if (!FLAGS_SET(flags, GET_HOSTNAME_FALLBACK_DEFAULT)) if (!FLAGS_SET(flags, GET_HOSTNAME_FALLBACK_DEFAULT))

View file

@ -9,10 +9,9 @@
#include "strv.h" #include "strv.h"
typedef enum GetHostnameFlags { typedef enum GetHostnameFlags {
GET_HOSTNAME_ALLOW_NONE = 1 << 0, /* accepts "(none)". */ GET_HOSTNAME_ALLOW_LOCALHOST = 1 << 0, /* accepts "localhost" or friends. */
GET_HOSTNAME_ALLOW_LOCALHOST = 1 << 1, /* accepts "localhost" or friends. */ GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 1, /* use default hostname if no hostname is set. */
GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 2, /* use default hostname if no hostname is set. */ GET_HOSTNAME_SHORT = 1 << 2, /* kills the FQDN part if present. */
GET_HOSTNAME_SHORT = 1 << 3, /* kills the FQDN part if present. */
} GetHostnameFlags; } GetHostnameFlags;
int gethostname_full(GetHostnameFlags flags, char **ret); int gethostname_full(GetHostnameFlags flags, char **ret);

View file

@ -629,65 +629,120 @@ int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mas
return 0; return 0;
} }
int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen) {
struct in_addr mask;
assert(addr);
if (!in4_addr_prefixlen_to_netmask(&mask, prefixlen))
return -EINVAL;
addr->s_addr &= mask.s_addr;
return 0;
}
int in6_addr_mask(struct in6_addr *addr, unsigned char prefixlen) {
unsigned i;
for (i = 0; i < 16; i++) {
uint8_t mask;
if (prefixlen >= 8) {
mask = 0xFF;
prefixlen -= 8;
} else if (prefixlen > 0) {
mask = 0xFF << (8 - prefixlen);
prefixlen = 0;
} else {
assert(prefixlen == 0);
mask = 0;
}
addr->s6_addr[i] &= mask;
}
return 0;
}
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) { int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) {
assert(addr); assert(addr);
if (family == AF_INET) { switch (family) {
struct in_addr mask; case AF_INET:
return in4_addr_mask(&addr->in, prefixlen);
if (!in4_addr_prefixlen_to_netmask(&mask, prefixlen)) case AF_INET6:
return -EINVAL; return in6_addr_mask(&addr->in6, prefixlen);
default:
addr->in.s_addr &= mask.s_addr; return -EAFNOSUPPORT;
return 0;
} }
if (family == AF_INET6) {
unsigned i;
for (i = 0; i < 16; i++) {
uint8_t mask;
if (prefixlen >= 8) {
mask = 0xFF;
prefixlen -= 8;
} else {
mask = 0xFF << (8 - prefixlen);
prefixlen = 0;
}
addr->in6.s6_addr[i] &= mask;
}
return 0;
}
return -EAFNOSUPPORT;
} }
int in_addr_prefix_covers(int family, int in4_addr_prefix_covers(
const union in_addr_union *prefix, const struct in_addr *prefix,
unsigned char prefixlen, unsigned char prefixlen,
const union in_addr_union *address) { const struct in_addr *address) {
union in_addr_union masked_prefix, masked_address; struct in_addr masked_prefix, masked_address;
int r; int r;
assert(prefix); assert(prefix);
assert(address); assert(address);
masked_prefix = *prefix; masked_prefix = *prefix;
r = in_addr_mask(family, &masked_prefix, prefixlen); r = in4_addr_mask(&masked_prefix, prefixlen);
if (r < 0) if (r < 0)
return r; return r;
masked_address = *address; masked_address = *address;
r = in_addr_mask(family, &masked_address, prefixlen); r = in4_addr_mask(&masked_address, prefixlen);
if (r < 0) if (r < 0)
return r; return r;
return in_addr_equal(family, &masked_prefix, &masked_address); return in4_addr_equal(&masked_prefix, &masked_address);
}
int in6_addr_prefix_covers(
const struct in6_addr *prefix,
unsigned char prefixlen,
const struct in6_addr *address) {
struct in6_addr masked_prefix, masked_address;
int r;
assert(prefix);
assert(address);
masked_prefix = *prefix;
r = in6_addr_mask(&masked_prefix, prefixlen);
if (r < 0)
return r;
masked_address = *address;
r = in6_addr_mask(&masked_address, prefixlen);
if (r < 0)
return r;
return in6_addr_equal(&masked_prefix, &masked_address);
}
int in_addr_prefix_covers(
int family,
const union in_addr_union *prefix,
unsigned char prefixlen,
const union in_addr_union *address) {
assert(prefix);
assert(address);
switch (family) {
case AF_INET:
return in4_addr_prefix_covers(&prefix->in, prefixlen, &address->in);
case AF_INET6:
return in6_addr_prefix_covers(&prefix->in6, prefixlen, &address->in6);
default:
return -EAFNOSUPPORT;
}
} }
int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) { int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) {
@ -855,4 +910,10 @@ int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
} }
DEFINE_HASH_OPS(in6_addr_hash_ops, struct in6_addr, in6_addr_hash_func, in6_addr_compare_func); DEFINE_HASH_OPS(in6_addr_hash_ops, struct in6_addr, in6_addr_hash_func, in6_addr_compare_func);
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
in6_addr_hash_ops_free,
struct in6_addr,
in6_addr_hash_func,
in6_addr_compare_func,
free);
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */

View file

@ -89,7 +89,11 @@ unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr);
struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen);
int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen);
int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask);
int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen);
int in6_addr_mask(struct in6_addr *addr, unsigned char prefixlen);
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen); int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address);
int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address);
int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address); int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address);
int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret); int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret);
int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
@ -110,6 +114,13 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
return family == AF_INET6 ? 16 : 4; return family == AF_INET6 ? 16 : 4;
} }
#define FAMILY_ADDRESS_SIZE_SAFE(f) \
({ \
int _f = (f); \
_f == AF_INET ? sizeof(struct in_addr) : \
_f == AF_INET6 ? sizeof(struct in6_addr) : 0; \
})
/* Workaround for clang, explicitly specify the maximum-size element here. /* Workaround for clang, explicitly specify the maximum-size element here.
* See also oss-fuzz#11344. */ * See also oss-fuzz#11344. */
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} }) #define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
@ -119,6 +130,7 @@ 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_data_hash_ops;
extern const struct hash_ops in6_addr_hash_ops; extern const struct hash_ops in6_addr_hash_ops;
extern const struct hash_ops in6_addr_hash_ops_free;
#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u" #define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u"
#define IPV4_ADDRESS_FMT_VAL(address) \ #define IPV4_ADDRESS_FMT_VAL(address) \

View file

@ -4,14 +4,26 @@
#include "fd-util.h" #include "fd-util.h"
#include "inotify-util.h" #include "inotify-util.h"
#include "stat-util.h"
int inotify_add_watch_fd(int fd, int what, uint32_t mask) { int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
int wd; int wd, r;
/* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ /* 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); wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask);
if (wd < 0) if (wd < 0) {
return -errno; if (errno != ENOENT)
return -errno;
/* Didn't work with ENOENT? If so, then either /proc/ isn't mounted, or the fd is bad */
r = proc_mounted();
if (r == 0)
return -ENOSYS;
if (r > 0)
return -EBADF;
return -ENOENT; /* OK, no clue, let's propagate the original error */
}
return wd; return wd;
} }

View file

@ -134,7 +134,7 @@
} while (false) } while (false)
#define LIST_JUST_US(name,item) \ #define LIST_JUST_US(name,item) \
(!(item)->name##_prev && !(item)->name##_next) \ (!(item)->name##_prev && !(item)->name##_next)
#define LIST_FOREACH(name,i,head) \ #define LIST_FOREACH(name,i,head) \
for ((i) = (head); (i); (i) = (i)->name##_next) for ((i) = (head); (i); (i) = (i)->name##_next)
@ -181,3 +181,12 @@
} \ } \
(b) = NULL; \ (b) = NULL; \
} while (false) } while (false)
#define LIST_POP(name, a) \
({ \
typeof(a)* _a = &(a); \
typeof(a) _p = *_a; \
if (_p) \
LIST_REMOVE(name, *_a, _p); \
_p; \
})

View file

@ -27,10 +27,14 @@ typedef enum LogTarget{
_LOG_TARGET_INVALID = -EINVAL, _LOG_TARGET_INVALID = -EINVAL,
} LogTarget; } LogTarget;
/* Note to readers: << and >> have lower precedence than & and | */ /* This log level disables logging completely. It can only be passed to log_set_max_level() and cannot be
* used a regular log level. */
#define LOG_NULL (LOG_EMERG - 1)
/* Note to readers: << and >> have lower precedence (are evaluated earlier) than & and | */
#define SYNTHETIC_ERRNO(num) (1 << 30 | (num)) #define SYNTHETIC_ERRNO(num) (1 << 30 | (num))
#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1) #define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1)
#define ERRNO_VALUE(val) (abs(val) & 255) #define ERRNO_VALUE(val) (abs(val) & ~(1 << 30))
/* The callback function to be invoked when syntax warnings are seen /* The callback function to be invoked when syntax warnings are seen
* in the unit files. */ * in the unit files. */
@ -90,6 +94,7 @@ int log_open(void);
void log_close(void); void log_close(void);
void log_forget_fds(void); void log_forget_fds(void);
void log_parse_environment_variables(void);
void log_parse_environment(void); void log_parse_environment(void);
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */

View file

@ -25,7 +25,7 @@
#define _public_ __attribute__((__visibility__("default"))) #define _public_ __attribute__((__visibility__("default")))
#define _hidden_ __attribute__((__visibility__("hidden"))) #define _hidden_ __attribute__((__visibility__("hidden")))
#define _weakref_(x) __attribute__((__weakref__(#x))) #define _weakref_(x) __attribute__((__weakref__(#x)))
#define _alignas_(x) __attribute__((__aligned__(__alignof(x)))) #define _alignas_(x) __attribute__((__aligned__(__alignof__(x))))
#define _alignptr_ __attribute__((__aligned__(sizeof(void*)))) #define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
#define _warn_unused_result_ __attribute__((__warn_unused_result__)) #define _warn_unused_result_ __attribute__((__warn_unused_result__))
@ -154,24 +154,6 @@
#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p))) #define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p)))
#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p))) #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 == ULLONG_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));
}
#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali))) #define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali)))
/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
@ -355,13 +337,6 @@ static inline int __coverity_check_and_return__(int condition) {
ans; \ ans; \
}) })
#define UPDATE_FLAG(orig, flag, b) \
((b) ? ((orig) | (flag)) : ((orig) & ~(flag)))
#define SET_FLAG(v, flag, b) \
(v) = UPDATE_FLAG(v, flag, b)
#define FLAGS_SET(v, flags) \
((~(v) & (flags)) == 0)
#define SWAP_TWO(x, y) do { \ #define SWAP_TWO(x, y) do { \
typeof(x) _t = (x); \ typeof(x) _t = (x); \
(x) = (y); \ (x) = (y); \

View file

@ -20,6 +20,7 @@
#include <asm/sgidefs.h> #include <asm/sgidefs.h>
#endif #endif
#include "macro.h"
#include "missing_keyctl.h" #include "missing_keyctl.h"
#include "missing_stat.h" #include "missing_stat.h"
#include "missing_syscall_def.h" #include "missing_syscall_def.h"
@ -79,7 +80,8 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) {
/* ======================================================================= */ /* ======================================================================= */
#if !HAVE_GETRANDOM #if !HAVE_GETRANDOM
static inline int missing_getrandom(void *buffer, size_t count, unsigned flags) { /* glibc says getrandom() returns ssize_t */
static inline ssize_t missing_getrandom(void *buffer, size_t count, unsigned flags) {
# ifdef __NR_getrandom # ifdef __NR_getrandom
return syscall(__NR_getrandom, buffer, count, flags); return syscall(__NR_getrandom, buffer, count, flags);
# else # else
@ -466,8 +468,52 @@ struct mount_attr {
struct mount_attr; struct mount_attr;
#endif #endif
#ifndef MOUNT_ATTR_RDONLY
#define MOUNT_ATTR_RDONLY 0x00000001 /* Mount read-only */
#endif
#ifndef MOUNT_ATTR_NOSUID
#define MOUNT_ATTR_NOSUID 0x00000002 /* Ignore suid and sgid bits */
#endif
#ifndef MOUNT_ATTR_NODEV
#define MOUNT_ATTR_NODEV 0x00000004 /* Disallow access to device special files */
#endif
#ifndef MOUNT_ATTR_NOEXEC
#define MOUNT_ATTR_NOEXEC 0x00000008 /* Disallow program execution */
#endif
#ifndef MOUNT_ATTR__ATIME
#define MOUNT_ATTR__ATIME 0x00000070 /* Setting on how atime should be updated */
#endif
#ifndef MOUNT_ATTR_RELATIME
#define MOUNT_ATTR_RELATIME 0x00000000 /* - Update atime relative to mtime/ctime. */
#endif
#ifndef MOUNT_ATTR_NOATIME
#define MOUNT_ATTR_NOATIME 0x00000010 /* - Do not update access times. */
#endif
#ifndef MOUNT_ATTR_STRICTATIME
#define MOUNT_ATTR_STRICTATIME 0x00000020 /* - Always perform atime updates */
#endif
#ifndef MOUNT_ATTR_NODIRATIME
#define MOUNT_ATTR_NODIRATIME 0x00000080 /* Do not update directory access times */
#endif
#ifndef MOUNT_ATTR_IDMAP #ifndef MOUNT_ATTR_IDMAP
#define MOUNT_ATTR_IDMAP 0x00100000 #define MOUNT_ATTR_IDMAP 0x00100000 /* Idmap mount to @userns_fd in struct mount_attr. */
#endif
#ifndef MOUNT_ATTR_NOSYMFOLLOW
#define MOUNT_ATTR_NOSYMFOLLOW 0x00200000 /* Do not follow symlinks */
#endif
#ifndef MOUNT_ATTR_SIZE_VER0
#define MOUNT_ATTR_SIZE_VER0 32 /* sizeof first published struct */
#endif #endif
#ifndef AT_RECURSIVE #ifndef AT_RECURSIVE
@ -545,4 +591,20 @@ static inline int missing_move_mount(
# define move_mount missing_move_mount # define move_mount missing_move_mount
#endif #endif
/* ======================================================================= */
#if !HAVE_GETDENTS64
static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) {
# if defined __NR_getdents64 && __NR_getdents64 >= 0
return syscall(__NR_getdents64, fd, buffer, length);
# else
errno = ENOSYS;
return -1;
# endif
}
# define getdents64 missing_getdents64
#endif
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */

View file

@ -651,7 +651,7 @@ int parse_ip_port(const char *s, uint16_t *ret) {
uint16_t l; uint16_t l;
int r; int r;
r = safe_atou16(s, &l); r = safe_atou16_full(s, SAFE_ATO_REFUSE_LEADING_WHITESPACE, &l);
if (r < 0) if (r < 0)
return r; return r;
@ -711,7 +711,7 @@ int parse_dev(const char *s, dev_t *ret) {
if (s[n] != ':') if (s[n] != ':')
return -EINVAL; return -EINVAL;
major = strndupa(s, n); major = strndupa_safe(s, n);
r = safe_atou(major, &x); r = safe_atou(major, &x);
if (r < 0) if (r < 0)
return r; return r;
@ -772,7 +772,7 @@ int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) {
if (!d) if (!d)
return -EINVAL; return -EINVAL;
i_str = strndupa(s, d - s); i_str = strndupa_safe(s, d - s);
f_str = d + 1; f_str = d + 1;
r = safe_atolu_full(i_str, 10, &i); r = safe_atolu_full(i_str, 10, &i);

View file

@ -1246,8 +1246,6 @@ char *file_in_same_dir(const char *path, const char *filename) {
} }
bool hidden_or_backup_file(const char *filename) { bool hidden_or_backup_file(const char *filename) {
const char *p;
assert(filename); assert(filename);
if (filename[0] == '.' || if (filename[0] == '.' ||
@ -1257,24 +1255,25 @@ bool hidden_or_backup_file(const char *filename) {
endswith(filename, "~")) endswith(filename, "~"))
return true; return true;
p = strrchr(filename, '.'); const char *dot = strrchr(filename, '.');
if (!p) if (!dot)
return false; return false;
/* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up /* Please, let's not add more entries to the list below. If external projects think it's a good idea
* with always new suffixes and that everybody else should just adjust to that, then it really should be on * to come up with always new suffixes and that everybody else should just adjust to that, then it
* them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt * really should be on them. Hence, in future, let's not add any more entries. Instead, let's ask
* one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional * those packages to instead adopt one of the generic suffixes/prefixes for hidden files or backups,
* string. Specifically: there's now: * possibly augmented with an additional string. Specifically: there's now:
* *
* The generic suffixes "~" and ".bak" for backup files * The generic suffixes "~" and ".bak" for backup files
* The generic prefix "." for hidden files * The generic prefix "." for hidden files
* *
* Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist" * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old",
* or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead. * ".foopkg-dist" or so registered, let's refuse that and ask them to use ".foopkg.new",
* ".foopkg.old" or ".foopkg~" instead.
*/ */
return STR_IN_SET(p + 1, return STR_IN_SET(dot + 1,
"rpmnew", "rpmnew",
"rpmsave", "rpmsave",
"rpmorig", "rpmorig",
@ -1296,15 +1295,16 @@ bool hidden_or_backup_file(const char *filename) {
bool is_device_path(const char *path) { bool is_device_path(const char *path) {
/* Returns true on paths that likely refer to a device, either by path in sysfs or to something in /dev */ /* Returns true for paths that likely refer to a device, either by path in sysfs or to something in
* /dev. */
return PATH_STARTSWITH_SET(path, "/dev/", "/sys/"); return PATH_STARTSWITH_SET(path, "/dev/", "/sys/");
} }
bool valid_device_node_path(const char *path) { bool valid_device_node_path(const char *path) {
/* Some superficial checks whether the specified path is a valid device node path, all without looking at the /* Some superficial checks whether the specified path is a valid device node path, all without
* actual device node. */ * looking at the actual device node. */
if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/")) if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/"))
return false; return false;
@ -1318,8 +1318,8 @@ bool valid_device_node_path(const char *path) {
bool valid_device_allow_pattern(const char *path) { bool valid_device_allow_pattern(const char *path) {
assert(path); assert(path);
/* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny= /* Like valid_device_node_path(), but also allows full-subsystem expressions like those accepted by
* accept it */ * DeviceAllow= and DeviceDeny=. */
if (STARTSWITH_SET(path, "block-", "char-")) if (STARTSWITH_SET(path, "block-", "char-"))
return true; return true;
@ -1412,8 +1412,8 @@ bool dot_or_dot_dot(const char *path) {
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
bool empty_or_root(const char *path) { bool empty_or_root(const char *path) {
/* For operations relative to some root directory, returns true if the specified root directory is redundant, /* For operations relative to some root directory, returns true if the specified root directory is
* i.e. either / or NULL or the empty string or any equivalent. */ * redundant, i.e. either / or NULL or the empty string or any equivalent. */
if (isempty(path)) if (isempty(path))
return true; return true;

View file

@ -187,13 +187,13 @@ static int get_process_cmdline_nulstr(
return r; return r;
} }
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) { int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
_cleanup_free_ char *t = NULL; _cleanup_free_ char *t = NULL;
size_t k; size_t k;
char *ans; char *ans;
assert(line);
assert(pid >= 0); assert(pid >= 0);
assert(ret);
/* Retrieve and format a commandline. See above for discussion of retrieval options. /* Retrieve and format a commandline. See above for discussion of retrieval options.
* *
@ -220,25 +220,23 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
assert(!(flags & PROCESS_CMDLINE_USE_LOCALE)); assert(!(flags & PROCESS_CMDLINE_USE_LOCALE));
_cleanup_strv_free_ char **args = NULL; _cleanup_strv_free_ char **args = NULL;
char **p;
args = strv_parse_nulstr(t, k); args = strv_parse_nulstr(t, k);
if (!args) if (!args)
return -ENOMEM; return -ENOMEM;
for (size_t i = 0; args[i]; i++) { /* Drop trailing empty strings. See issue #21186. */
char *e; STRV_FOREACH_BACKWARDS(p, args) {
if (!isempty(*p))
break;
e = shell_maybe_quote(args[i], shflags); *p = mfree(*p);
if (!e)
return -ENOMEM;
free_and_replace(args[i], e);
} }
ans = strv_join(args, " "); ans = quote_command_line(args, shflags);
if (!ans) if (!ans)
return -ENOMEM; return -ENOMEM;
} else { } else {
/* Arguments are separated by NULs. Let's replace those with spaces. */ /* Arguments are separated by NULs. Let's replace those with spaces. */
for (size_t i = 0; i < k - 1; i++) for (size_t i = 0; i < k - 1; i++)
@ -257,7 +255,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
ans = str_realloc(ans); ans = str_realloc(ans);
} }
*line = ans; *ret = ans;
return 0; return 0;
} }
@ -464,29 +462,29 @@ int is_kernel_thread(pid_t pid) {
return !!(flags & PF_KTHREAD); return !!(flags & PF_KTHREAD);
} }
int get_process_capeff(pid_t pid, char **capeff) { int get_process_capeff(pid_t pid, char **ret) {
const char *p; const char *p;
int r; int r;
assert(capeff);
assert(pid >= 0); assert(pid >= 0);
assert(ret);
p = procfs_file_alloca(pid, "status"); p = procfs_file_alloca(pid, "status");
r = get_proc_field(p, "CapEff", WHITESPACE, capeff); r = get_proc_field(p, "CapEff", WHITESPACE, ret);
if (r == -ENOENT) if (r == -ENOENT)
return -ESRCH; return -ESRCH;
return r; return r;
} }
static int get_process_link_contents(const char *proc_file, char **name) { static int get_process_link_contents(const char *proc_file, char **ret) {
int r; int r;
assert(proc_file); assert(proc_file);
assert(name); assert(ret);
r = readlink_malloc(proc_file, name); r = readlink_malloc(proc_file, ret);
if (r == -ENOENT) if (r == -ENOENT)
return -ESRCH; return -ESRCH;
if (r < 0) if (r < 0)
@ -495,32 +493,33 @@ static int get_process_link_contents(const char *proc_file, char **name) {
return 0; return 0;
} }
int get_process_exe(pid_t pid, char **name) { int get_process_exe(pid_t pid, char **ret) {
const char *p; const char *p;
char *d; char *d;
int r; int r;
assert(pid >= 0); assert(pid >= 0);
assert(ret);
p = procfs_file_alloca(pid, "exe"); p = procfs_file_alloca(pid, "exe");
r = get_process_link_contents(p, name); r = get_process_link_contents(p, ret);
if (r < 0) if (r < 0)
return r; return r;
d = endswith(*name, " (deleted)"); d = endswith(*ret, " (deleted)");
if (d) if (d)
*d = '\0'; *d = '\0';
return 0; return 0;
} }
static int get_process_id(pid_t pid, const char *field, uid_t *uid) { static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
_cleanup_fclose_ FILE *f = NULL; _cleanup_fclose_ FILE *f = NULL;
const char *p; const char *p;
int r; int r;
assert(field); assert(field);
assert(uid); assert(ret);
if (pid < 0) if (pid < 0)
return -EINVAL; return -EINVAL;
@ -550,60 +549,62 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
l[strcspn(l, WHITESPACE)] = 0; l[strcspn(l, WHITESPACE)] = 0;
return parse_uid(l, uid); return parse_uid(l, ret);
} }
} }
return -EIO; return -EIO;
} }
int get_process_uid(pid_t pid, uid_t *uid) { int get_process_uid(pid_t pid, uid_t *ret) {
if (pid == 0 || pid == getpid_cached()) { if (pid == 0 || pid == getpid_cached()) {
*uid = getuid(); *ret = getuid();
return 0; return 0;
} }
return get_process_id(pid, "Uid:", uid); return get_process_id(pid, "Uid:", ret);
} }
int get_process_gid(pid_t pid, gid_t *gid) { int get_process_gid(pid_t pid, gid_t *ret) {
if (pid == 0 || pid == getpid_cached()) { if (pid == 0 || pid == getpid_cached()) {
*gid = getgid(); *ret = getgid();
return 0; return 0;
} }
assert_cc(sizeof(uid_t) == sizeof(gid_t)); assert_cc(sizeof(uid_t) == sizeof(gid_t));
return get_process_id(pid, "Gid:", gid); return get_process_id(pid, "Gid:", ret);
} }
int get_process_cwd(pid_t pid, char **cwd) { int get_process_cwd(pid_t pid, char **ret) {
const char *p; const char *p;
assert(pid >= 0); assert(pid >= 0);
assert(ret);
if (pid == 0 || pid == getpid_cached()) if (pid == 0 || pid == getpid_cached())
return safe_getcwd(cwd); return safe_getcwd(ret);
p = procfs_file_alloca(pid, "cwd"); p = procfs_file_alloca(pid, "cwd");
return get_process_link_contents(p, cwd); return get_process_link_contents(p, ret);
} }
int get_process_root(pid_t pid, char **root) { int get_process_root(pid_t pid, char **ret) {
const char *p; const char *p;
assert(pid >= 0); assert(pid >= 0);
assert(ret);
p = procfs_file_alloca(pid, "root"); p = procfs_file_alloca(pid, "root");
return get_process_link_contents(p, root); return get_process_link_contents(p, ret);
} }
#define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U) #define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U)
int get_process_environ(pid_t pid, char **env) { int get_process_environ(pid_t pid, char **ret) {
_cleanup_fclose_ FILE *f = NULL; _cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *outcome = NULL; _cleanup_free_ char *outcome = NULL;
size_t sz = 0; size_t sz = 0;
@ -611,7 +612,7 @@ int get_process_environ(pid_t pid, char **env) {
int r; int r;
assert(pid >= 0); assert(pid >= 0);
assert(env); assert(ret);
p = procfs_file_alloca(pid, "environ"); p = procfs_file_alloca(pid, "environ");
@ -643,7 +644,7 @@ int get_process_environ(pid_t pid, char **env) {
} }
outcome[sz] = '\0'; outcome[sz] = '\0';
*env = TAKE_PTR(outcome); *ret = TAKE_PTR(outcome);
return 0; return 0;
} }
@ -702,13 +703,13 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
return 0; return 0;
} }
int get_process_umask(pid_t pid, mode_t *umask) { int get_process_umask(pid_t pid, mode_t *ret) {
_cleanup_free_ char *m = NULL; _cleanup_free_ char *m = NULL;
const char *p; const char *p;
int r; int r;
assert(umask);
assert(pid >= 0); assert(pid >= 0);
assert(ret);
p = procfs_file_alloca(pid, "status"); p = procfs_file_alloca(pid, "status");
@ -716,7 +717,7 @@ int get_process_umask(pid_t pid, mode_t *umask) {
if (r == -ENOENT) if (r == -ENOENT)
return -ESRCH; return -ESRCH;
return parse_mode(m, umask); return parse_mode(m, ret);
} }
int wait_for_terminate(pid_t pid, siginfo_t *status) { int wait_for_terminate(pid_t pid, siginfo_t *status) {
@ -830,7 +831,7 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
if (n >= until) if (n >= until)
break; break;
r = sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)) < 0 ? -errno : 0; r = RET_NERRNO(sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)));
/* Assuming we woke due to the child exiting. */ /* Assuming we woke due to the child exiting. */
if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) { if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) {
if (status.si_pid == pid) { if (status.si_pid == pid) {
@ -863,8 +864,8 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
void sigkill_wait(pid_t pid) { void sigkill_wait(pid_t pid) {
assert(pid > 1); assert(pid > 1);
if (kill(pid, SIGKILL) >= 0) (void) kill(pid, SIGKILL);
(void) wait_for_terminate(pid, NULL); (void) wait_for_terminate(pid, NULL);
} }
void sigkill_waitp(pid_t *pid) { void sigkill_waitp(pid_t *pid) {
@ -881,14 +882,14 @@ void sigkill_waitp(pid_t *pid) {
void sigterm_wait(pid_t pid) { void sigterm_wait(pid_t pid) {
assert(pid > 1); assert(pid > 1);
if (kill_and_sigcont(pid, SIGTERM) >= 0) (void) kill_and_sigcont(pid, SIGTERM);
(void) wait_for_terminate(pid, NULL); (void) wait_for_terminate(pid, NULL);
} }
int kill_and_sigcont(pid_t pid, int sig) { int kill_and_sigcont(pid_t pid, int sig) {
int r; int r;
r = kill(pid, sig) < 0 ? -errno : 0; r = RET_NERRNO(kill(pid, sig));
/* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't /* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't
* affected by a process being suspended anyway. */ * affected by a process being suspended anyway. */
@ -1064,8 +1065,8 @@ unsigned long personality_from_string(const char *p) {
if (architecture == native_architecture()) if (architecture == native_architecture())
return PER_LINUX; return PER_LINUX;
#ifdef SECONDARY_ARCHITECTURE #ifdef ARCHITECTURE_SECONDARY
if (architecture == SECONDARY_ARCHITECTURE) if (architecture == ARCHITECTURE_SECONDARY)
return PER_LINUX32; return PER_LINUX32;
#endif #endif
@ -1077,9 +1078,9 @@ const char* personality_to_string(unsigned long p) {
if (p == PER_LINUX) if (p == PER_LINUX)
architecture = native_architecture(); architecture = native_architecture();
#ifdef SECONDARY_ARCHITECTURE #ifdef ARCHITECTURE_SECONDARY
else if (p == PER_LINUX32) else if (p == PER_LINUX32)
architecture = SECONDARY_ARCHITECTURE; architecture = ARCHITECTURE_SECONDARY;
#endif #endif
if (architecture < 0) if (architecture < 0)
@ -1150,23 +1151,6 @@ int pid_compare_func(const pid_t *a, const pid_t *b) {
/* Suitable for usage in qsort() */ /* Suitable for usage in qsort() */
return CMP(*a, *b); return CMP(*a, *b);
} }
int ioprio_parse_priority(const char *s, int *ret) {
int i, r;
assert(s);
assert(ret);
r = safe_atoi(s, &i);
if (r < 0)
return r;
if (!ioprio_priority_is_valid(i))
return -EINVAL;
*ret = i;
return 0;
}
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
/* The cached PID, possible values: /* The cached PID, possible values:
@ -1256,7 +1240,7 @@ static void restore_sigsetp(sigset_t **ssp) {
int safe_fork_full( int safe_fork_full(
const char *name, const char *name,
int except_fds[], const int except_fds[],
size_t n_except_fds, size_t n_except_fds,
ForkFlags flags, ForkFlags flags,
pid_t *ret_pid) { pid_t *ret_pid) {
@ -1451,7 +1435,7 @@ int safe_fork_full(
int namespace_fork( int namespace_fork(
const char *outer_name, const char *outer_name,
const char *inner_name, const char *inner_name,
int except_fds[], const int except_fds[],
size_t n_except_fds, size_t n_except_fds,
ForkFlags flags, ForkFlags flags,
int pidns_fd, int pidns_fd,
@ -1467,8 +1451,7 @@ int namespace_fork(
* process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that * process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that
* /proc/self/fd works correctly. */ * /proc/self/fd works correctly. */
r = safe_fork_full(outer_name, except_fds, n_except_fds, r = safe_fork_full(outer_name, except_fds, n_except_fds, (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
(flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
if (r < 0) if (r < 0)
return r; return r;
if (r == 0) { if (r == 0) {
@ -1513,7 +1496,7 @@ int set_oom_score_adjust(int value) {
} }
int get_oom_score_adjust(int *ret) { int get_oom_score_adjust(int *ret) {
_cleanup_free_ char *t; _cleanup_free_ char *t = NULL;
int r, a; int r, a;
r = read_virtual_file("/proc/self/oom_score_adj", SIZE_MAX, &t, NULL); r = read_virtual_file("/proc/self/oom_score_adj", SIZE_MAX, &t, NULL);
@ -1628,14 +1611,27 @@ bool invoked_as(char *argv[], const char *token) {
return strstr(last_path_component(argv[0]), token); return strstr(last_path_component(argv[0]), token);
} }
static const char *const ioprio_class_table[] = { _noreturn_ void freeze(void) {
[IOPRIO_CLASS_NONE] = "none", log_close();
[IOPRIO_CLASS_RT] = "realtime",
[IOPRIO_CLASS_BE] = "best-effort", /* Make sure nobody waits for us (i.e. on one of our sockets) anymore. Note that we use
[IOPRIO_CLASS_IDLE] = "idle", * close_all_fds_without_malloc() instead of plain close_all_fds() here, since we want this function
}; * to be compatible with being called from signal handlers. */
(void) close_all_fds_without_malloc(NULL, 0);
/* Let's not freeze right away, but keep reaping zombies. */
for (;;) {
siginfo_t si = {};
if (waitid(P_ALL, 0, &si, WEXITED) < 0 && errno != EINTR)
break;
}
/* waitid() failed with an unexpected error, things are really borked. Freeze now! */
for (;;)
pause();
}
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES);
static const char *const sigchld_code_table[] = { static const char *const sigchld_code_table[] = {
[CLD_EXITED] = "exited", [CLD_EXITED] = "exited",

View file

@ -14,7 +14,6 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "format-util.h" #include "format-util.h"
#include "macro.h" #include "macro.h"
#include "missing_ioprio.h"
#include "time-util.h" #include "time-util.h"
#define procfs_file_alloca(pid, field) \ #define procfs_file_alloca(pid, field) \
@ -39,17 +38,17 @@ typedef enum ProcessCmdlineFlags {
PROCESS_CMDLINE_QUOTE_POSIX = 1 << 3, PROCESS_CMDLINE_QUOTE_POSIX = 1 << 3,
} ProcessCmdlineFlags; } ProcessCmdlineFlags;
int get_process_comm(pid_t pid, char **name); int get_process_comm(pid_t pid, char **ret);
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line); int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
int get_process_exe(pid_t pid, char **name); int get_process_exe(pid_t pid, char **ret);
int get_process_uid(pid_t pid, uid_t *uid); int get_process_uid(pid_t pid, uid_t *ret);
int get_process_gid(pid_t pid, gid_t *gid); int get_process_gid(pid_t pid, gid_t *ret);
int get_process_capeff(pid_t pid, char **capeff); int get_process_capeff(pid_t pid, char **ret);
int get_process_cwd(pid_t pid, char **cwd); int get_process_cwd(pid_t pid, char **ret);
int get_process_root(pid_t pid, char **root); int get_process_root(pid_t pid, char **ret);
int get_process_environ(pid_t pid, char **environ); int get_process_environ(pid_t pid, char **ret);
int get_process_ppid(pid_t pid, pid_t *ppid); int get_process_ppid(pid_t pid, pid_t *ret);
int get_process_umask(pid_t pid, mode_t *umask); int get_process_umask(pid_t pid, mode_t *ret);
int wait_for_terminate(pid_t pid, siginfo_t *status); int wait_for_terminate(pid_t pid, siginfo_t *status);
@ -97,9 +96,6 @@ const char *personality_to_string(unsigned long);
int safe_personality(unsigned long p); int safe_personality(unsigned long p);
int opinionated_personality(unsigned long *ret); int opinionated_personality(unsigned long *ret);
int ioprio_class_to_string_alloc(int i, char **s);
int ioprio_class_from_string(const char *s);
const char *sigchld_code_to_string(int i) _const_; const char *sigchld_code_to_string(int i) _const_;
int sigchld_code_from_string(const char *s) _pure_; int sigchld_code_from_string(const char *s) _pure_;
@ -131,21 +127,11 @@ static inline bool sched_priority_is_valid(int i) {
return i >= 0 && i <= sched_get_priority_max(SCHED_RR); return i >= 0 && i <= sched_get_priority_max(SCHED_RR);
} }
static inline bool ioprio_class_is_valid(int i) {
return IN_SET(i, IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE);
}
static inline bool ioprio_priority_is_valid(int i) {
return i >= 0 && i < IOPRIO_BE_NR;
}
static inline bool pid_is_valid(pid_t p) { static inline bool pid_is_valid(pid_t p) {
return p > 0; return p > 0;
} }
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
int ioprio_parse_priority(const char *s, int *ret);
pid_t getpid_cached(void); pid_t getpid_cached(void);
void reset_cached_pid(void); void reset_cached_pid(void);
@ -168,13 +154,13 @@ typedef enum ForkFlags {
FORK_NEW_USERNS = 1 << 13, /* Run child in its own user namespace */ FORK_NEW_USERNS = 1 << 13, /* Run child in its own user namespace */
} ForkFlags; } ForkFlags;
int safe_fork_full(const char *name, int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) { static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
return safe_fork_full(name, NULL, 0, flags, ret_pid); return safe_fork_full(name, NULL, 0, flags, 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 namespace_fork(const char *outer_name, const char *inner_name, const 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 set_oom_score_adjust(int value);
int get_oom_score_adjust(int *ret); int get_oom_score_adjust(int *ret);
@ -194,8 +180,9 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX);
/* Like TAKE_PTR() but for child PIDs, resetting them to 0 */ /* Like TAKE_PTR() but for child PIDs, resetting them to 0 */
#define TAKE_PID(pid) \ #define TAKE_PID(pid) \
({ \ ({ \
pid_t _pid_ = (pid); \ pid_t *_ppid_ = &(pid); \
(pid) = 0; \ pid_t _pid_ = *_ppid_; \
*_ppid_ = 0; \
_pid_; \ _pid_; \
}) })
@ -204,3 +191,5 @@ int pidfd_get_pid(int fd, pid_t *ret);
int setpriority_closest(int priority); int setpriority_closest(int priority);
bool invoked_as(char *argv[], const char *token); bool invoked_as(char *argv[], const char *token);
_noreturn_ void freeze(void);

View file

@ -163,7 +163,6 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
static int have_syscall = -1; static int have_syscall = -1;
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
bool got_some = false; bool got_some = false;
int r;
/* Gathers some high-quality randomness from the kernel (or potentially mid-quality randomness from /* Gathers some high-quality randomness from the kernel (or potentially mid-quality randomness from
* the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't block, unless the RANDOM_BLOCK * the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't block, unless the RANDOM_BLOCK
@ -222,25 +221,26 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) { if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) {
for (;;) { for (;;) {
ssize_t l;
#if !HAVE_GETRANDOM #if !HAVE_GETRANDOM
/* NetworkManager Note: systemd calls the syscall directly in this case. Don't add that workaround. /* NetworkManager Note: systemd calls the syscall directly in this case. Don't add that workaround.
* If you don't compile against a libc that provides getrandom(), you don't get it. */ * If you don't compile against a libc that provides getrandom(), you don't get it. */
r = -1; l = -1;
errno = ENOSYS; errno = ENOSYS;
#else #else
r = getrandom(p, n, l = getrandom(p, n,
(FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) | (FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) |
(FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0)); (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0));
#endif #endif
if (r > 0) { if (l > 0) {
have_syscall = true; have_syscall = true;
if ((size_t) r == n) if ((size_t) l == n)
return 0; /* Yay, success! */ return 0; /* Yay, success! */
assert((size_t) r < n); assert((size_t) l < n);
p = (uint8_t*) p + r; p = (uint8_t*) p + l;
n -= r; n -= l;
if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
/* Fill in the remaining bytes using pseudo-random values */ /* Fill in the remaining bytes using pseudo-random values */
@ -257,7 +257,7 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
/* Fill in the rest with /dev/urandom */ /* Fill in the rest with /dev/urandom */
break; break;
} else if (r == 0) { } else if (l == 0) {
have_syscall = true; have_syscall = true;
return -EIO; return -EIO;

View file

@ -5,6 +5,7 @@
#include <errno.h> #include <errno.h>
#include <stdarg.h> #include <stdarg.h>
#include "errno-util.h"
#include "macro.h" #include "macro.h"
#include "parse-util.h" #include "parse-util.h"
#include "signal-util.h" #include "signal-util.h"
@ -42,10 +43,7 @@ int reset_signal_mask(void) {
if (sigemptyset(&ss) < 0) if (sigemptyset(&ss) < 0)
return -errno; return -errno;
if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0) return RET_NERRNO(sigprocmask(SIG_SETMASK, &ss, NULL));
return -errno;
return 0;
} }
int sigaction_many_internal(const struct sigaction *sa, ...) { int sigaction_many_internal(const struct sigaction *sa, ...) {
@ -122,7 +120,7 @@ int sigprocmask_many(int how, sigset_t *old, ...) {
return 0; return 0;
} }
static const char *const __signal_table[] = { static const char *const static_signal_table[] = {
[SIGHUP] = "HUP", [SIGHUP] = "HUP",
[SIGINT] = "INT", [SIGINT] = "INT",
[SIGQUIT] = "QUIT", [SIGQUIT] = "QUIT",
@ -158,13 +156,13 @@ static const char *const __signal_table[] = {
[SIGSYS] = "SYS" [SIGSYS] = "SYS"
}; };
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int); DEFINE_PRIVATE_STRING_TABLE_LOOKUP(static_signal, int);
const char *signal_to_string(int signo) { const char *signal_to_string(int signo) {
static thread_local char buf[STRLEN("RTMIN+") + DECIMAL_STR_MAX(int)]; static thread_local char buf[STRLEN("RTMIN+") + DECIMAL_STR_MAX(int)];
const char *name; const char *name;
name = __signal_to_string(signo); name = static_signal_to_string(signo);
if (name) if (name)
return name; return name;
@ -193,7 +191,7 @@ int signal_from_string(const char *s) {
s += 3; s += 3;
/* Check that the input is a signal name. */ /* Check that the input is a signal name. */
signo = __signal_from_string(s); signo = static_signal_from_string(s);
if (signo > 0) if (signo > 0)
return signo; return signo;
@ -250,11 +248,7 @@ int signal_is_blocked(int sig) {
if (r != 0) if (r != 0)
return -r; return -r;
r = sigismember(&ss, sig); return RET_NERRNO(sigismember(&ss, sig));
if (r < 0)
return -errno;
return r;
} }
int pop_pending_signal_internal(int sig, ...) { int pop_pending_signal_internal(int sig, ...) {

View file

@ -405,6 +405,41 @@ const union in_addr_union *sockaddr_in_addr(const struct sockaddr *_sa) {
} }
} }
int sockaddr_set_in_addr(
union sockaddr_union *u,
int family,
const union in_addr_union *a,
uint16_t port) {
assert(u);
assert(a);
switch (family) {
case AF_INET:
u->in = (struct sockaddr_in) {
.sin_family = AF_INET,
.sin_addr = a->in,
.sin_port = htobe16(port),
};
return 0;
case AF_INET6:
u->in6 = (struct sockaddr_in6) {
.sin6_family = AF_INET6,
.sin6_addr = a->in6,
.sin6_port = htobe16(port),
};
return 0;
default:
return -EAFNOSUPPORT;
}
}
int sockaddr_pretty( int sockaddr_pretty(
const struct sockaddr *_sa, const struct sockaddr *_sa,
socklen_t salen, socklen_t salen,
@ -556,7 +591,7 @@ int getpeername_pretty(int fd, bool include_port, char **ret) {
return -errno; return -errno;
if (sa.sa.sa_family == AF_UNIX) { if (sa.sa.sa_family == AF_UNIX) {
struct ucred ucred = {}; struct ucred ucred = UCRED_INVALID;
/* UNIX connection sockets are anonymous, so let's use /* UNIX connection sockets are anonymous, so let's use
* PID/UID as pretty credentials instead */ * PID/UID as pretty credentials instead */
@ -1238,10 +1273,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
/* Call with NULL to drop binding */ /* Call with NULL to drop binding */
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen_ptr(ifname)) < 0) return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen_ptr(ifname)));
return -errno;
return 0;
} }
int socket_bind_to_ifindex(int fd, int ifindex) { int socket_bind_to_ifindex(int fd, int ifindex) {
@ -1250,13 +1282,9 @@ int socket_bind_to_ifindex(int fd, int ifindex) {
assert(fd >= 0); assert(fd >= 0);
if (ifindex <= 0) { if (ifindex <= 0)
/* Drop binding */ /* Drop binding */
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0) < 0) return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0));
return -errno;
return 0;
}
r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex); r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
if (r != -ENOPROTOOPT) if (r != -ENOPROTOOPT)
@ -1345,16 +1373,10 @@ int socket_set_unicast_if(int fd, int af, int ifi) {
switch (af) { switch (af) {
case AF_INET: case AF_INET:
if (setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0) return RET_NERRNO(setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)));
return -errno;
return 0;
case AF_INET6: case AF_INET6:
if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0) return RET_NERRNO(setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)));
return -errno;
return 0;
default: default:
return -EAFNOSUPPORT; return -EAFNOSUPPORT;

View file

@ -15,6 +15,7 @@
#include <sys/un.h> #include <sys/un.h>
#include "errno-util.h" #include "errno-util.h"
#include "in-addr-util.h"
#include "macro.h" #include "macro.h"
#include "missing_network.h" #include "missing_network.h"
#include "missing_socket.h" #include "missing_socket.h"
@ -108,6 +109,7 @@ bool socket_ipv6_is_enabled(void);
int sockaddr_port(const struct sockaddr *_sa, unsigned *port); int sockaddr_port(const struct sockaddr *_sa, unsigned *port);
const union in_addr_union *sockaddr_in_addr(const struct sockaddr *sa); const union in_addr_union *sockaddr_in_addr(const struct sockaddr *sa);
int sockaddr_set_in_addr(union sockaddr_union *u, int family, const union in_addr_union *a, uint16_t port);
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret); int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret);
int getpeername_pretty(int fd, bool include_port, char **ret); int getpeername_pretty(int fd, bool include_port, char **ret);
@ -331,3 +333,6 @@ static inline int socket_set_recvfragsize(int fd, int af, bool b) {
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
int socket_get_mtu(int fd, int af, size_t *ret); int socket_get_mtu(int fd, int af, size_t *ret);
/* an initializer for struct ucred that initialized all fields to the invalid value appropriate for each */
#define UCRED_INVALID { .pid = 0, .uid = UID_INVALID, .gid = GID_INVALID }

View file

@ -5,14 +5,20 @@
#include "macro.h" #include "macro.h"
/* This is the same as glibc's internal __compar_d_fn_t type. glibc exports a public comparison_fn_t, for the
* external type __compar_fn_t, but doesn't do anything similar for __compar_d_fn_t. Let's hence do that
* ourselves, picking a name that is obvious, but likely enough to not clash with glibc's choice of naming if
* they should ever add one. */
typedef int (*comparison_userdata_fn_t)(const void *, const void *, void *);
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
__compar_d_fn_t compar, void *arg); comparison_userdata_fn_t compar, void *arg);
#define typesafe_bsearch_r(k, b, n, func, userdata) \ #define typesafe_bsearch_r(k, b, n, func, userdata) \
({ \ ({ \
const typeof(b[0]) *_k = k; \ const typeof(b[0]) *_k = k; \
int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \ int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \
xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_d_fn_t) _func_, userdata); \ xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \
}) })
/** /**
@ -20,7 +26,7 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
* that only if nmemb > 0. * that only if nmemb > 0.
*/ */
static inline void* bsearch_safe(const void *key, const void *base, static inline void* bsearch_safe(const void *key, const void *base,
size_t nmemb, size_t size, __compar_fn_t compar) { size_t nmemb, size_t size, comparison_fn_t compar) {
if (nmemb <= 0) if (nmemb <= 0)
return NULL; return NULL;
@ -32,14 +38,14 @@ static inline void* bsearch_safe(const void *key, const void *base,
({ \ ({ \
const typeof(b[0]) *_k = k; \ const typeof(b[0]) *_k = k; \
int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \ int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \
bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_fn_t) _func_); \ bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \
}) })
/** /**
* Normal qsort requires base to be nonnull. Here were require * Normal qsort requires base to be nonnull. Here were require
* that only if nmemb > 0. * that only if nmemb > 0.
*/ */
static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) { static inline void _qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
if (nmemb <= 1) if (nmemb <= 1)
return; return;
@ -52,11 +58,11 @@ static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_f
#define typesafe_qsort(p, n, func) \ #define typesafe_qsort(p, n, func) \
({ \ ({ \
int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \
_qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ _qsort_safe((p), (n), sizeof((p)[0]), (comparison_fn_t) _func_); \
}) })
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) { static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, comparison_userdata_fn_t compar, void *userdata) {
if (nmemb <= 1) if (nmemb <= 1)
return; return;
@ -67,7 +73,7 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_
#define typesafe_qsort_r(p, n, func, userdata) \ #define typesafe_qsort_r(p, n, func, userdata) \
({ \ ({ \
int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \ int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \
qsort_r_safe((p), (n), sizeof((p)[0]), (__compar_d_fn_t) _func_, userdata); \ qsort_r_safe((p), (n), sizeof((p)[0]), (comparison_userdata_fn_t) _func_, userdata); \
}) })
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */

View file

@ -55,9 +55,9 @@ typedef uint64_t __sd_bitwise be64_t;
#undef le64toh #undef le64toh
#if __BYTE_ORDER == __LITTLE_ENDIAN #if __BYTE_ORDER == __LITTLE_ENDIAN
#define bswap_16_on_le(x) __bswap_16(x) #define bswap_16_on_le(x) bswap_16(x)
#define bswap_32_on_le(x) __bswap_32(x) #define bswap_32_on_le(x) bswap_32(x)
#define bswap_64_on_le(x) __bswap_64(x) #define bswap_64_on_le(x) bswap_64(x)
#define bswap_16_on_be(x) (x) #define bswap_16_on_be(x) (x)
#define bswap_32_on_be(x) (x) #define bswap_32_on_be(x) (x)
#define bswap_64_on_be(x) (x) #define bswap_64_on_be(x) (x)
@ -65,9 +65,9 @@ typedef uint64_t __sd_bitwise be64_t;
#define bswap_16_on_le(x) (x) #define bswap_16_on_le(x) (x)
#define bswap_32_on_le(x) (x) #define bswap_32_on_le(x) (x)
#define bswap_64_on_le(x) (x) #define bswap_64_on_le(x) (x)
#define bswap_16_on_be(x) __bswap_16(x) #define bswap_16_on_be(x) bswap_16(x)
#define bswap_32_on_be(x) __bswap_32(x) #define bswap_32_on_be(x) bswap_32(x)
#define bswap_64_on_be(x) __bswap_64(x) #define bswap_64_on_be(x) bswap_64(x)
#endif #endif
static inline le16_t htole16(uint16_t value) { return (le16_t __sd_force) bswap_16_on_be(value); } static inline le16_t htole16(uint16_t value) { return (le16_t __sd_force) bswap_16_on_be(value); }

View file

@ -15,10 +15,13 @@
#include "errno-util.h" #include "errno-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
#include "filesystems.h"
#include "fs-util.h"
#include "macro.h" #include "macro.h"
#include "missing_fs.h" #include "missing_fs.h"
#include "missing_magic.h" #include "missing_magic.h"
#include "missing_syscall.h" #include "missing_syscall.h"
#include "nulstr-util.h"
#include "parse-util.h" #include "parse-util.h"
#include "stat-util.h" #include "stat-util.h"
#include "string-util.h" #include "string-util.h"
@ -75,27 +78,41 @@ int is_device_node(const char *path) {
int dir_is_empty_at(int dir_fd, const char *path) { int dir_is_empty_at(int dir_fd, const char *path) {
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
_cleanup_closedir_ DIR *d = NULL; /* Allocate space for at least 3 full dirents, since every dir has at least two entries ("." +
* ".."), and only once we have seen if there's a third we know whether the dir is empty or not. */
DEFINE_DIRENT_BUFFER(buffer, 3);
struct dirent *de; struct dirent *de;
ssize_t n;
if (path) { if (path) {
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC); fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (fd < 0) if (fd < 0)
return -errno; return -errno;
} else if (dir_fd == AT_FDCWD) {
fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
return -errno;
} else { } else {
/* Note that DUPing is not enough, as the internal pointer /* Note that DUPing is not enough, as the internal pointer would still be shared and moved
* would still be shared and moved by FOREACH_DIRENT. */ * getedents64(). */
fd = fd_reopen(dir_fd, O_CLOEXEC); assert(dir_fd >= 0);
fd = fd_reopen(dir_fd, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (fd < 0) if (fd < 0)
return fd; return fd;
} }
d = take_fdopendir(&fd); n = getdents64(fd, &buffer, sizeof(buffer));
if (!d) if (n < 0)
return -errno; return -errno;
FOREACH_DIRENT(de, d, return -errno) msan_unpoison(&buffer, n);
return 0;
FOREACH_DIRENT_IN_BUFFER(de, &buffer.de, n)
if (!dot_or_dot_dot(de->d_name))
return 0;
return 1; return 1;
} }
@ -178,7 +195,6 @@ int files_same(const char *filea, const char *fileb, int flags) {
a.st_ino == b.st_ino; a.st_ino == b.st_ino;
} }
#if 0 /* NM_IGNORED */
bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
assert(s); assert(s);
assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type)); assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
@ -186,6 +202,7 @@ bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
return F_TYPE_EQUAL(s->f_type, magic_value); return F_TYPE_EQUAL(s->f_type, magic_value);
} }
#if 0 /* NM_IGNORED */
int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
struct statfs s; struct statfs s;
@ -194,6 +211,7 @@ int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
return is_fs_type(&s, magic_value); return is_fs_type(&s, magic_value);
} }
#endif /* NM_IGNORE */
int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
struct statfs s; struct statfs s;
@ -204,20 +222,13 @@ int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
return is_fs_type(&s, magic_value); return is_fs_type(&s, magic_value);
} }
#if 0 /* NM_IGNORE */
bool is_temporary_fs(const struct statfs *s) { bool is_temporary_fs(const struct statfs *s) {
return is_fs_type(s, TMPFS_MAGIC) || return fs_in_group(s, FILESYSTEM_SET_TEMPORARY);
is_fs_type(s, RAMFS_MAGIC);
} }
bool is_network_fs(const struct statfs *s) { bool is_network_fs(const struct statfs *s) {
return is_fs_type(s, CIFS_MAGIC_NUMBER) || return fs_in_group(s, FILESYSTEM_SET_NETWORK);
is_fs_type(s, CODA_SUPER_MAGIC) ||
is_fs_type(s, NCP_SUPER_MAGIC) ||
is_fs_type(s, NFS_SUPER_MAGIC) ||
is_fs_type(s, SMB_SUPER_MAGIC) ||
is_fs_type(s, V9FS_MAGIC) ||
is_fs_type(s, AFS_SUPER_MAGIC) ||
is_fs_type(s, OCFS2_SUPER_MAGIC);
} }
int fd_is_temporary_fs(int fd) { int fd_is_temporary_fs(int fd) {
@ -395,6 +406,7 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret
return 0; return 0;
} }
#endif /* NM_IGNORED */
int proc_mounted(void) { int proc_mounted(void) {
int r; int r;
@ -408,6 +420,7 @@ int proc_mounted(void) {
return r; return r;
} }
#if 0 /* NM_IGNORED */
bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
/* Returns if the specified stat structures reference the same, unmodified inode. This check tries to /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to

View file

@ -60,6 +60,10 @@ static inline const char *empty_to_null(const char *p) {
return isempty(p) ? NULL : p; return isempty(p) ? NULL : p;
} }
static inline const char *empty_to_na(const char *p) {
return isempty(p) ? "n/a" : p;
}
static inline const char *empty_to_dash(const char *str) { static inline const char *empty_to_dash(const char *str) {
return isempty(str) ? "-" : str; return isempty(str) ? "-" : str;
} }

View file

@ -18,7 +18,7 @@
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
char *strv_find(char * const *l, const char *name) { char* strv_find(char * const *l, const char *name) {
char * const *i; char * const *i;
assert(name); assert(name);
@ -30,7 +30,7 @@ char *strv_find(char * const *l, const char *name) {
return NULL; return NULL;
} }
char *strv_find_case(char * const *l, const char *name) { char* strv_find_case(char * const *l, const char *name) {
char * const *i; char * const *i;
assert(name); assert(name);
@ -42,7 +42,7 @@ char *strv_find_case(char * const *l, const char *name) {
return NULL; return NULL;
} }
char *strv_find_prefix(char * const *l, const char *name) { char* strv_find_prefix(char * const *l, const char *name) {
char * const *i; char * const *i;
assert(name); assert(name);
@ -54,7 +54,7 @@ char *strv_find_prefix(char * const *l, const char *name) {
return NULL; return NULL;
} }
char *strv_find_startswith(char * const *l, const char *name) { char* strv_find_startswith(char * const *l, const char *name) {
char * const *i, *e; char * const *i, *e;
assert(name); assert(name);
@ -71,19 +71,17 @@ char *strv_find_startswith(char * const *l, const char *name) {
return NULL; return NULL;
} }
char **strv_free(char **l) { char** strv_free(char **l) {
char **k;
if (!l) if (!l)
return NULL; return NULL;
for (k = l; *k; k++) for (char **k = l; *k; k++)
free(*k); free(*k);
return mfree(l); return mfree(l);
} }
char **strv_free_erase(char **l) { char** strv_free_erase(char **l) {
char **i; char **i;
STRV_FOREACH(i, l) STRV_FOREACH(i, l)
@ -92,7 +90,7 @@ char **strv_free_erase(char **l) {
return mfree(l); return mfree(l);
} }
char **strv_copy(char * const *l) { char** strv_copy(char * const *l) {
char **r, **k; char **r, **k;
k = r = new(char*, strv_length(l) + 1); k = r = new(char*, strv_length(l) + 1);
@ -124,7 +122,7 @@ size_t strv_length(char * const *l) {
return n; return n;
} }
char **strv_new_ap(const char *x, va_list ap) { char** strv_new_ap(const char *x, va_list ap) {
_cleanup_strv_free_ char **a = NULL; _cleanup_strv_free_ char **a = NULL;
size_t n = 0, i = 0; size_t n = 0, i = 0;
va_list aq; va_list aq;
@ -163,7 +161,7 @@ char **strv_new_ap(const char *x, va_list ap) {
return TAKE_PTR(a); return TAKE_PTR(a);
} }
char **strv_new_internal(const char *x, ...) { char** strv_new_internal(const char *x, ...) {
char **r; char **r;
va_list ap; va_list ap;
@ -176,7 +174,7 @@ char **strv_new_internal(const char *x, ...) {
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) { int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
char * const *s, **t; char * const *s, **t;
size_t p, q, i = 0, j; size_t p, q, i = 0;
assert(a); assert(a);
@ -197,7 +195,6 @@ int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
*a = t; *a = t;
STRV_FOREACH(s, b) { STRV_FOREACH(s, b) {
if (filter_duplicates && strv_contains(t, *s)) if (filter_duplicates && strv_contains(t, *s))
continue; continue;
@ -214,7 +211,7 @@ int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
return (int) i; return (int) i;
rollback: rollback:
for (j = 0; j < i; j++) for (size_t j = 0; j < i; j++)
free(t[p + j]); free(t[p + j]);
t[p] = NULL; t[p] = NULL;
@ -289,7 +286,6 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla
return -ENOMEM; return -ENOMEM;
l[n++] = TAKE_PTR(word); l[n++] = TAKE_PTR(word);
l[n] = NULL; l[n] = NULL;
} }
@ -376,7 +372,7 @@ int strv_split_colon_pairs(char ***t, const char *s) {
} }
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) { char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) {
char * const *s; char * const *s;
char *r, *e; char *r, *e;
size_t n, k, m; size_t n, k, m;
@ -483,7 +479,7 @@ int strv_push_pair(char ***l, char *a, char *b) {
int strv_insert(char ***l, size_t position, char *value) { int strv_insert(char ***l, size_t position, char *value) {
char **c; char **c;
size_t n, m, i; size_t n, m;
if (!value) if (!value)
return 0; return 0;
@ -500,18 +496,14 @@ int strv_insert(char ***l, size_t position, char *value) {
if (!c) if (!c)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < position; i++) for (size_t i = 0; i < position; i++)
c[i] = (*l)[i]; c[i] = (*l)[i];
c[position] = value; c[position] = value;
for (i = position; i < n; i++) for (size_t i = position; i < n; i++)
c[i+1] = (*l)[i]; c[i+1] = (*l)[i];
c[n+1] = NULL; c[n+1] = NULL;
free(*l); return free_and_replace(*l, c);
*l = c;
return 0;
} }
int strv_consume(char ***l, char *value) { int strv_consume(char ***l, char *value) {
@ -608,7 +600,7 @@ int strv_extend_front(char ***l, const char *value) {
return 0; return 0;
} }
char **strv_uniq(char **l) { char** strv_uniq(char **l) {
char **i; char **i;
/* Drops duplicate entries. The first identical string will be /* Drops duplicate entries. The first identical string will be
@ -630,7 +622,7 @@ bool strv_is_uniq(char * const *l) {
return true; return true;
} }
char **strv_remove(char **l, const char *s) { char** strv_remove(char **l, const char *s) {
char **f, **t; char **f, **t;
if (!l) if (!l)
@ -651,7 +643,7 @@ char **strv_remove(char **l, const char *s) {
return l; return l;
} }
char **strv_parse_nulstr(const char *s, size_t l) { char** strv_parse_nulstr(const char *s, size_t l) {
/* l is the length of the input data, which will be split at NULs into /* l is the length of the input data, which will be split at NULs into
* elements of the resulting strv. Hence, the number of items in the resulting strv * elements of the resulting strv. Hence, the number of items in the resulting strv
* will be equal to one plus the number of NUL bytes in the l bytes starting at s, * will be equal to one plus the number of NUL bytes in the l bytes starting at s,
@ -663,7 +655,6 @@ char **strv_parse_nulstr(const char *s, size_t l) {
* empty strings in s. * empty strings in s.
*/ */
const char *p;
size_t c = 0, i = 0; size_t c = 0, i = 0;
char **v; char **v;
@ -672,7 +663,7 @@ char **strv_parse_nulstr(const char *s, size_t l) {
if (l <= 0) if (l <= 0)
return new0(char*, 1); return new0(char*, 1);
for (p = s; p < s + l; p++) for (const char *p = s; p < s + l; p++)
if (*p == 0) if (*p == 0)
c++; c++;
@ -683,8 +674,7 @@ char **strv_parse_nulstr(const char *s, size_t l) {
if (!v) if (!v)
return NULL; return NULL;
p = s; for (const char *p = s; p < s + l; ) {
while (p < s + l) {
const char *e; const char *e;
e = memchr(p, 0, s + l - p); e = memchr(p, 0, s + l - p);
@ -709,7 +699,7 @@ char **strv_parse_nulstr(const char *s, size_t l) {
} }
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
char **strv_split_nulstr(const char *s) { char** strv_split_nulstr(const char *s) {
const char *i; const char *i;
char **r = NULL; char **r = NULL;
@ -786,7 +776,7 @@ static int str_compare(char * const *a, char * const *b) {
return strcmp(*a, *b); return strcmp(*a, *b);
} }
char **strv_sort(char **l) { char** strv_sort(char **l) {
typesafe_qsort(l, strv_length(l), str_compare); typesafe_qsort(l, strv_length(l), str_compare);
return l; return l;
} }
@ -836,21 +826,21 @@ int strv_extendf(char ***l, const char *format, ...) {
return strv_consume(l, x); return strv_consume(l, x);
} }
char **strv_reverse(char **l) { char** strv_reverse(char **l) {
size_t n, i; size_t n;
n = strv_length(l); n = strv_length(l);
if (n <= 1) if (n <= 1)
return l; return l;
for (i = 0; i < n / 2; i++) for (size_t i = 0; i < n / 2; i++)
SWAP_TWO(l[i], l[n-1-i]); SWAP_TWO(l[i], l[n-1-i]);
return l; return l;
} }
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
char **strv_shell_escape(char **l, const char *bad) { char** strv_shell_escape(char **l, const char *bad) {
char **s; char **s;
/* Escapes every character in every string in l that is in bad, /* Escapes every character in every string in l that is in bad,
@ -882,19 +872,7 @@ bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *
return false; return false;
} }
char ***strv_free_free(char ***l) { char** strv_skip(char **l, size_t n) {
char ***i;
if (!l)
return NULL;
for (i = l; *i; i++)
strv_free(*i);
return mfree(l);
}
char **strv_skip(char **l, size_t n) {
while (n > 0) { while (n > 0) {
if (strv_isempty(l)) if (strv_isempty(l))
@ -907,7 +885,7 @@ char **strv_skip(char **l, size_t n) {
} }
int strv_extend_n(char ***l, const char *value, size_t n) { int strv_extend_n(char ***l, const char *value, size_t n) {
size_t i, j, k; size_t i, k;
char **nl; char **nl;
assert(l); assert(l);
@ -934,15 +912,15 @@ int strv_extend_n(char ***l, const char *value, size_t n) {
if (!nl[i]) if (!nl[i])
goto rollback; goto rollback;
} }
nl[i] = NULL; nl[i] = NULL;
return 0; return 0;
rollback: rollback:
for (j = k; j < i; j++) for (size_t j = k; j < i; j++)
free(nl[j]); free(nl[j]);
nl[k] = NULL; nl[k] = NULL;
return -ENOMEM; return -ENOMEM;
} }

View file

@ -13,23 +13,23 @@
#include "macro.h" #include "macro.h"
#include "string-util.h" #include "string-util.h"
char *strv_find(char * const *l, const char *name) _pure_; char* strv_find(char * const *l, const char *name) _pure_;
char *strv_find_case(char * const *l, const char *name) _pure_; char* strv_find_case(char * const *l, const char *name) _pure_;
char *strv_find_prefix(char * const *l, const char *name) _pure_; char* strv_find_prefix(char * const *l, const char *name) _pure_;
char *strv_find_startswith(char * const *l, const char *name) _pure_; char* strv_find_startswith(char * const *l, const char *name) _pure_;
#define strv_contains(l, s) (!!strv_find((l), (s))) #define strv_contains(l, s) (!!strv_find((l), (s)))
#define strv_contains_case(l, s) (!!strv_find_case((l), (s))) #define strv_contains_case(l, s) (!!strv_find_case((l), (s)))
char **strv_free(char **l); char** strv_free(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free); DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
#define _cleanup_strv_free_ _cleanup_(strv_freep) #define _cleanup_strv_free_ _cleanup_(strv_freep)
char **strv_free_erase(char **l); char** strv_free_erase(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase); DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase);
#define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep) #define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep)
char **strv_copy(char * const *l); char** strv_copy(char * const *l);
size_t strv_length(char * const *l) _pure_; size_t strv_length(char * const *l) _pure_;
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates); int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates);
@ -50,8 +50,8 @@ int strv_consume(char ***l, char *value);
int strv_consume_pair(char ***l, char *a, char *b); int strv_consume_pair(char ***l, char *a, char *b);
int strv_consume_prepend(char ***l, char *value); int strv_consume_prepend(char ***l, char *value);
char **strv_remove(char **l, const char *s); char** strv_remove(char **l, const char *s);
char **strv_uniq(char **l); char** strv_uniq(char **l);
bool strv_is_uniq(char * const *l); bool strv_is_uniq(char * const *l);
int strv_compare(char * const *a, char * const *b); int strv_compare(char * const *a, char * const *b);
@ -59,8 +59,8 @@ static inline bool strv_equal(char * const *a, char * const *b) {
return strv_compare(a, b) == 0; return strv_compare(a, b) == 0;
} }
char **strv_new_internal(const char *x, ...) _sentinel_; char** strv_new_internal(const char *x, ...) _sentinel_;
char **strv_new_ap(const char *x, va_list ap); char** strv_new_ap(const char *x, va_list ap);
#define strv_new(...) strv_new_internal(__VA_ARGS__, NULL) #define strv_new(...) strv_new_internal(__VA_ARGS__, NULL)
#define STRV_IGNORE ((const char *) POINTER_MAX) #define STRV_IGNORE ((const char *) POINTER_MAX)
@ -74,7 +74,7 @@ static inline bool strv_isempty(char * const *l) {
} }
int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags); int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags);
static inline char **strv_split(const char *s, const char *separators) { static inline char** strv_split(const char *s, const char *separators) {
char **ret; char **ret;
if (strv_split_full(&ret, s, separators, 0) < 0) if (strv_split_full(&ret, s, separators, 0) < 0)
@ -87,7 +87,7 @@ int strv_split_and_extend_full(char ***t, const char *s, const char *separators,
#define strv_split_and_extend(t, s, sep, dup) strv_split_and_extend_full(t, s, sep, dup, 0) #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); int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags);
static inline char **strv_split_newlines(const char *s) { static inline char** strv_split_newlines(const char *s) {
char **ret; char **ret;
if (strv_split_newlines_full(&ret, s, 0) < 0) if (strv_split_newlines_full(&ret, s, 0) < 0)
@ -101,13 +101,13 @@ static inline char **strv_split_newlines(const char *s) {
* string in the vector is an empty string. */ * string in the vector is an empty string. */
int strv_split_colon_pairs(char ***t, const char *s); int strv_split_colon_pairs(char ***t, const char *s);
char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separtor); char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separtor);
static inline char *strv_join(char * const *l, const char *separator) { static inline char *strv_join(char * const *l, const char *separator) {
return strv_join_full(l, separator, NULL, false); return strv_join_full(l, separator, NULL, false);
} }
char **strv_parse_nulstr(const char *s, size_t l); char** strv_parse_nulstr(const char *s, size_t l);
char **strv_split_nulstr(const char *s); char** strv_split_nulstr(const char *s);
int strv_make_nulstr(char * const *l, char **p, size_t *n); int strv_make_nulstr(char * const *l, char **p, size_t *n);
static inline int strv_from_nulstr(char ***a, const char *nulstr) { static inline int strv_from_nulstr(char ***a, const char *nulstr) {
@ -136,7 +136,7 @@ bool strv_overlap(char * const *a, char * const *b) _pure_;
#define STRV_FOREACH_PAIR(x, y, l) \ #define STRV_FOREACH_PAIR(x, y, l) \
for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
char **strv_sort(char **l); char** strv_sort(char **l);
void strv_print(char * const *l); void strv_print(char * const *l);
#define strv_from_stdarg_alloca(first) \ #define strv_from_stdarg_alloca(first) \
@ -206,13 +206,16 @@ void strv_print(char * const *l);
_found; \ _found; \
}) })
#define FOREACH_STRING(x, y, ...) \ #define _FOREACH_STRING(uniq, x, y, ...) \
for (char **_l = STRV_MAKE(({ x = y; }), ##__VA_ARGS__); \ for (char **UNIQ_T(l, uniq) = STRV_MAKE(({ x = y; }), ##__VA_ARGS__); \
x; \ x; \
x = *(++_l)) x = *(++UNIQ_T(l, uniq)))
char **strv_reverse(char **l); #define FOREACH_STRING(x, y, ...) \
char **strv_shell_escape(char **l, const char *bad); _FOREACH_STRING(UNIQ, x, y, ##__VA_ARGS__)
char** strv_reverse(char **l);
char** strv_shell_escape(char **l, const char *bad);
bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos); bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos);
static inline bool strv_fnmatch(char* const* patterns, const char *s) { static inline bool strv_fnmatch(char* const* patterns, const char *s) {
@ -225,10 +228,7 @@ static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, i
strv_fnmatch_full(patterns, s, flags, NULL); strv_fnmatch_full(patterns, s, flags, NULL);
} }
char ***strv_free_free(char ***l); char** strv_skip(char **l, size_t n);
DEFINE_TRIVIAL_CLEANUP_FUNC(char***, strv_free_free);
char **strv_skip(char **l, size_t n);
int strv_extend_n(char ***l, const char *value, size_t n); int strv_extend_n(char ***l, const char *value, size_t n);

View file

@ -17,57 +17,73 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "string-util.h"
#include "strxcpyx.h" #include "strxcpyx.h"
size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) { size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated) {
bool truncated = false;
assert(dest); assert(dest);
assert(src); assert(src);
if (size == 0) if (size == 0) {
if (ret_truncated)
*ret_truncated = len > 0;
return 0; return 0;
}
if (len >= size) { if (len >= size) {
if (size > 1) if (size > 1)
*dest = mempcpy(*dest, src, size-1); *dest = mempcpy(*dest, src, size-1);
size = 0; size = 0;
truncated = true;
} else if (len > 0) { } else if (len > 0) {
*dest = mempcpy(*dest, src, len); *dest = mempcpy(*dest, src, len);
size -= len; size -= len;
} }
if (ret_truncated)
*ret_truncated = truncated;
*dest[0] = '\0'; *dest[0] = '\0';
return size; return size;
} }
size_t strpcpy(char **dest, size_t size, const char *src) { size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated) {
assert(dest); assert(dest);
assert(src); assert(src);
return strnpcpy(dest, size, src, strlen(src)); return strnpcpy_full(dest, size, src, strlen(src), ret_truncated);
} }
size_t strpcpyf(char **dest, size_t size, const char *src, ...) { size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) {
bool truncated = false;
va_list va; va_list va;
int i; int i;
assert(dest); assert(dest);
assert(src); assert(src);
if (size == 0)
return 0;
va_start(va, src); va_start(va, src);
i = vsnprintf(*dest, size, src, va); i = vsnprintf(*dest, size, src, va);
if (i < (int)size) { va_end(va);
if (i < (int) size) {
*dest += i; *dest += i;
size -= i; size -= i;
} else } else {
size = 0; size = 0;
va_end(va); truncated = i > 0;
}
if (ret_truncated)
*ret_truncated = truncated;
return size; return size;
} }
size_t strpcpyl(char **dest, size_t size, const char *src, ...) { size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) {
bool truncated = false;
va_list va; va_list va;
assert(dest); assert(dest);
@ -75,31 +91,38 @@ size_t strpcpyl(char **dest, size_t size, const char *src, ...) {
va_start(va, src); va_start(va, src);
do { do {
size = strpcpy(dest, size, src); bool t;
size = strpcpy_full(dest, size, src, &t);
truncated = truncated || t;
src = va_arg(va, char *); src = va_arg(va, char *);
} while (src); } while (src);
va_end(va); va_end(va);
if (ret_truncated)
*ret_truncated = truncated;
return size; return size;
} }
size_t strnscpy(char *dest, size_t size, const char *src, size_t len) { size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated) {
char *s; char *s;
assert(dest); assert(dest);
assert(src); assert(src);
s = dest; s = dest;
return strnpcpy(&s, size, src, len); return strnpcpy_full(&s, size, src, len, ret_truncated);
} }
size_t strscpy(char *dest, size_t size, const char *src) { size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated) {
assert(dest); assert(dest);
assert(src); assert(src);
return strnscpy(dest, size, src, strlen(src)); return strnscpy_full(dest, size, src, strlen(src), ret_truncated);
} }
size_t strscpyl(char *dest, size_t size, const char *src, ...) { size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) {
bool truncated = false;
va_list va; va_list va;
char *s; char *s;
@ -109,10 +132,16 @@ size_t strscpyl(char *dest, size_t size, const char *src, ...) {
va_start(va, src); va_start(va, src);
s = dest; s = dest;
do { do {
size = strpcpy(&s, size, src); bool t;
size = strpcpy_full(&s, size, src, &t);
truncated = truncated || t;
src = va_arg(va, char *); src = va_arg(va, char *);
} while (src); } while (src);
va_end(va); va_end(va);
if (ret_truncated)
*ret_truncated = truncated;
return size; return size;
} }

View file

@ -1,14 +1,33 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once #pragma once
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include "macro.h" #include "macro.h"
size_t strnpcpy(char **dest, size_t size, const char *src, size_t len); size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated);
size_t strpcpy(char **dest, size_t size, const char *src); static inline size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) {
size_t strpcpyf(char **dest, size_t size, const char *src, ...) _printf_(3, 4); return strnpcpy_full(dest, size, src, len, NULL);
size_t strpcpyl(char **dest, size_t size, const char *src, ...) _sentinel_; }
size_t strnscpy(char *dest, size_t size, const char *src, size_t len); size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated);
size_t strscpy(char *dest, size_t size, const char *src); static inline size_t strpcpy(char **dest, size_t size, const char *src) {
size_t strscpyl(char *dest, size_t size, const char *src, ...) _sentinel_; return strpcpy_full(dest, size, src, NULL);
}
size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) _printf_(4, 5);
#define strpcpyf(dest, size, src, ...) \
strpcpyf_full((dest), (size), NULL, (src), ##__VA_ARGS__)
size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) _sentinel_;
#define strpcpyl(dest, size, src, ...) \
strpcpyl_full((dest), (size), NULL, (src), ##__VA_ARGS__)
size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated);
static inline size_t strnscpy(char *dest, size_t size, const char *src, size_t len) {
return strnscpy_full(dest, size, src, len, NULL);
}
size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated);
static inline size_t strscpy(char *dest, size_t size, const char *src) {
return strscpy_full(dest, size, src, NULL);
}
size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) _sentinel_;
#define strscpyl(dest, size, src, ...) \
strscpyl_full(dest, size, NULL, src, ##__VA_ARGS__)

View file

@ -271,7 +271,6 @@ struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n) {
return ts; return ts;
} }
#if 0 /* NM_IGNORED */
usec_t timeval_load(const struct timeval *tv) { usec_t timeval_load(const struct timeval *tv) {
assert(tv); assert(tv);
@ -286,6 +285,7 @@ usec_t timeval_load(const struct timeval *tv) {
(usec_t) tv->tv_usec; (usec_t) tv->tv_usec;
} }
#if 0 /* NM_IGNORED */
struct timeval *timeval_store(struct timeval *tv, usec_t u) { struct timeval *timeval_store(struct timeval *tv, usec_t u) {
assert(tv); assert(tv);
@ -676,7 +676,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
goto finish; goto finish;
} else if ((k = endswith(t, " ago"))) { } else if ((k = endswith(t, " ago"))) {
t = strndupa(t, k - t); t = strndupa_safe(t, k - t);
r = parse_sec(t, &minus); r = parse_sec(t, &minus);
if (r < 0) if (r < 0)
@ -685,7 +685,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
goto finish; goto finish;
} else if ((k = endswith(t, " left"))) { } else if ((k = endswith(t, " left"))) {
t = strndupa(t, k - t); t = strndupa_safe(t, k - t);
r = parse_sec(t, &plus); r = parse_sec(t, &plus);
if (r < 0) if (r < 0)
@ -697,7 +697,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
/* See if the timestamp is suffixed with UTC */ /* See if the timestamp is suffixed with UTC */
utc = endswith_no_case(t, " UTC"); utc = endswith_no_case(t, " UTC");
if (utc) if (utc)
t = strndupa(t, utc - t); t = strndupa_safe(t, utc - t);
else { else {
const char *e = NULL; const char *e = NULL;
int j; int j;
@ -728,7 +728,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
if (IN_SET(j, 0, 1)) { if (IN_SET(j, 0, 1)) {
/* Found one of the two timezones specified. */ /* Found one of the two timezones specified. */
t = strndupa(t, e - t - 1); t = strndupa_safe(t, e - t - 1);
dst = j; dst = j;
tzn = tzname[j]; tzn = tzname[j];
} }
@ -929,7 +929,7 @@ int parse_timestamp(const char *t, usec_t *usec) {
/* Cut off the timezone if we don't need it. */ /* Cut off the timezone if we don't need it. */
if (with_tz) if (with_tz)
t = strndupa(t, last_space - t); t = strndupa_safe(t, last_space - t);
shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz); shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz);

View file

@ -67,16 +67,9 @@ int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) {
/* This is much like mkostemp() but is subject to umask(). */ /* This is much like mkostemp() but is subject to umask(). */
int mkostemp_safe(char *pattern) { int mkostemp_safe(char *pattern) {
int fd = -1; /* avoid false maybe-uninitialized warning */
assert(pattern); assert(pattern);
BLOCK_WITH_UMASK(0077);
RUN_WITH_UMASK(0077) return RET_NERRNO(mkostemp(pattern, O_CLOEXEC));
fd = mkostemp(pattern, O_CLOEXEC);
if (fd < 0)
return -errno;
return fd;
} }
#if 0 /* NM_IGNORED */ #if 0 /* NM_IGNORED */
@ -288,8 +281,6 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
} }
int link_tmpfile(int fd, const char *path, const char *target) { int link_tmpfile(int fd, const char *path, const char *target) {
int r;
assert(fd >= 0); assert(fd >= 0);
assert(target); assert(target);
@ -300,16 +291,10 @@ int link_tmpfile(int fd, const char *path, const char *target) {
* Note that in both cases we will not replace existing files. This is because linkat() does not support this * Note that in both cases we will not replace existing files. This is because linkat() does not support this
* operation currently (renameat2() does), and there is no nice way to emulate this. */ * operation currently (renameat2() does), and there is no nice way to emulate this. */
if (path) { if (path)
r = rename_noreplace(AT_FDCWD, path, AT_FDCWD, target); return rename_noreplace(AT_FDCWD, path, AT_FDCWD, target);
if (r < 0)
return r;
} else {
if (linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
return -errno;
}
return 0; return RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW));
} }
int mkdtemp_malloc(const char *template, char **ret) { int mkdtemp_malloc(const char *template, char **ret) {

View file

@ -24,3 +24,6 @@ assert_cc((S_IFMT & 0777) == 0);
for (_cleanup_umask_ mode_t _saved_umask_ = umask(mask) | S_IFMT; \ for (_cleanup_umask_ mode_t _saved_umask_ = umask(mask) | S_IFMT; \
FLAGS_SET(_saved_umask_, S_IFMT); \ FLAGS_SET(_saved_umask_, S_IFMT); \
_saved_umask_ &= 0777) _saved_umask_ &= 0777)
#define BLOCK_WITH_UMASK(mask) \
_unused_ _cleanup_umask_ mode_t _saved_umask_ = umask(mask);

View file

@ -112,6 +112,12 @@ bool is_nologin_shell(const char *shell);
int is_this_me(const char *username); int is_this_me(const char *username);
const char *get_home_root(void);
static inline bool hashed_password_is_locked_or_invalid(const char *password) {
return password && password[0] != '$';
}
/* A locked *and* invalid password for "struct spwd"'s .sp_pwdp and "struct passwd"'s .pw_passwd field */ /* A locked *and* invalid password for "struct spwd"'s .sp_pwdp and "struct passwd"'s .pw_passwd field */
#define PASSWORD_LOCKED_AND_INVALID "!*" #define PASSWORD_LOCKED_AND_INVALID "!*"

View file

@ -126,11 +126,9 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
} }
bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) { bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) {
const char *p;
assert(str); assert(str);
for (p = str; length > 0;) { for (const char *p = str; length > 0;) {
int encoded_len, r; int encoded_len, r;
char32_t val; char32_t val;
@ -293,14 +291,12 @@ char *utf8_escape_non_printable_full(const char *str, size_t console_width, bool
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
char *ascii_is_valid(const char *str) { char *ascii_is_valid(const char *str) {
const char *p;
/* Check whether the string consists of valid ASCII bytes, /* Check whether the string consists of valid ASCII bytes,
* i.e values between 0 and 127, inclusive. */ * i.e values between 0 and 127, inclusive. */
assert(str); assert(str);
for (p = str; *p; p++) for (const char *p = str; *p; p++)
if ((unsigned char) *p >= 128) if ((unsigned char) *p >= 128)
return NULL; return NULL;
@ -322,6 +318,37 @@ char *ascii_is_valid_n(const char *str, size_t len) {
} }
#endif /* NM_IGNORED */ #endif /* NM_IGNORED */
int utf8_to_ascii(const char *str, char replacement_char, char **ret) {
/* Convert to a string that has only ASCII chars, replacing anything that is not ASCII
* by replacement_char. */
_cleanup_free_ char *ans = new(char, strlen(str) + 1);
if (!ans)
return -ENOMEM;
char *q = ans;
for (const char *p = str; *p; q++) {
int l;
l = utf8_encoded_valid_unichar(p, SIZE_MAX);
if (l < 0) /* Non-UTF-8, let's not even try to propagate the garbage */
return l;
if (l == 1)
*q = *p;
else
/* non-ASCII, we need to replace it */
*q = replacement_char;
p += l;
}
*q = '\0';
*ret = TAKE_PTR(ans);
return 0;
}
/** /**
* utf8_encode_unichar() - Encode single UCS-4 character as UTF-8 * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8
* @out_utf8: output buffer of at least 4 bytes or NULL * @out_utf8: output buffer of at least 4 bytes or NULL

View file

@ -21,6 +21,8 @@ static inline char *utf8_is_valid(const char *s) {
char *ascii_is_valid(const char *s) _pure_; char *ascii_is_valid(const char *s) _pure_;
char *ascii_is_valid_n(const char *str, size_t len); char *ascii_is_valid_n(const char *str, size_t len);
int utf8_to_ascii(const char *str, char replacement_char, char **ret);
bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) _pure_; bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) _pure_;
#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true) #define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)

View file

@ -8,7 +8,6 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "build.h" #include "build.h"
#include "dirent-util.h"
#include "env-file.h" #include "env-file.h"
#include "env-util.h" #include "env-util.h"
#include "fd-util.h" #include "fd-util.h"
@ -118,71 +117,6 @@ void in_initrd_force(bool value) {
saved_in_initrd = value; saved_in_initrd = value;
} }
int on_ac_power(void) {
bool found_offline = false, found_online = false;
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
d = opendir("/sys/class/power_supply");
if (!d)
return errno == ENOENT ? true : -errno;
FOREACH_DIRENT(de, d, return -errno) {
_cleanup_close_ int fd = -1, device = -1;
char contents[6];
ssize_t n;
device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (device < 0) {
if (IN_SET(errno, ENOENT, ENOTDIR))
continue;
return -errno;
}
fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
if (errno == ENOENT)
continue;
return -errno;
}
n = read(fd, contents, sizeof(contents));
if (n < 0)
return -errno;
if (n != 6 || memcmp(contents, "Mains\n", 6))
continue;
safe_close(fd);
fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
if (errno == ENOENT)
continue;
return -errno;
}
n = read(fd, contents, sizeof(contents));
if (n < 0)
return -errno;
if (n != 2 || contents[1] != '\n')
return -EIO;
if (contents[0] == '1') {
found_online = true;
break;
} else if (contents[0] == '0')
found_offline = true;
else
return -EIO;
}
return found_online || !found_offline;
}
int container_get_leader(const char *machine, pid_t *pid) { int container_get_leader(const char *machine, pid_t *pid) {
_cleanup_free_ char *s = NULL, *class = NULL; _cleanup_free_ char *s = NULL, *class = NULL;
const char *p; const char *p;

View file

@ -20,13 +20,20 @@ int prot_from_flags(int flags) _const_;
bool in_initrd(void); bool in_initrd(void);
void in_initrd_force(bool value); void in_initrd_force(bool value);
int on_ac_power(void); /* Note: log2(0) == log2(1) == 0 here and below. */
static inline unsigned u64log2(uint64_t n) { #define CONST_LOG2ULL(x) ((x) > 1 ? (unsigned) __builtin_clzll(x) ^ 63U : 0)
#define NONCONST_LOG2ULL(x) ({ \
unsigned long long _x = (x); \
_x > 1 ? (unsigned) __builtin_clzll(_x) ^ 63U : 0; \
})
#define LOG2ULL(x) __builtin_choose_expr(__builtin_constant_p(x), CONST_LOG2ULL(x), NONCONST_LOG2ULL(x))
static inline unsigned log2u64(uint64_t x) {
#if __SIZEOF_LONG_LONG__ == 8 #if __SIZEOF_LONG_LONG__ == 8
return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0; return LOG2ULL(x);
#else #else
#error "Wut?" # error "Wut?"
#endif #endif
} }
@ -34,26 +41,27 @@ static inline unsigned u32ctz(uint32_t n) {
#if __SIZEOF_INT__ == 4 #if __SIZEOF_INT__ == 4
return n != 0 ? __builtin_ctz(n) : 32; return n != 0 ? __builtin_ctz(n) : 32;
#else #else
#error "Wut?" # error "Wut?"
#endif #endif
} }
static inline unsigned log2i(int x) { #define CONST_LOG2U(x) ((x) > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1 : 0)
assert(x > 0); #define NONCONST_LOG2U(x) ({ \
unsigned _x = (x); \
_x > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(_x) - 1 : 0; \
})
#define LOG2U(x) __builtin_choose_expr(__builtin_constant_p(x), CONST_LOG2U(x), NONCONST_LOG2U(x))
return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1; static inline unsigned log2i(int x) {
return LOG2U(x);
} }
static inline unsigned log2u(unsigned x) { static inline unsigned log2u(unsigned x) {
assert(x > 0); return LOG2U(x);
return sizeof(unsigned) * 8 - __builtin_clz(x) - 1;
} }
static inline unsigned log2u_round_up(unsigned x) { static inline unsigned log2u_round_up(unsigned x) {
assert(x > 0); if (x <= 1)
if (x == 1)
return 0; return 0;
return log2u(x - 1) + 1; return log2u(x - 1) + 1;

View file

@ -5,13 +5,15 @@
#include <assert.h> #include <assert.h>
#endif #endif
#include "type.h" #include <limits.h>
#include "types-fundamental.h"
#define _align_(x) __attribute__((__aligned__(x))) #define _align_(x) __attribute__((__aligned__(x)))
#define _const_ __attribute__((__const__)) #define _const_ __attribute__((__const__))
#define _pure_ __attribute__((__pure__)) #define _pure_ __attribute__((__pure__))
#define _section_(x) __attribute__((__section__(x))) #define _section_(x) __attribute__((__section__(x)))
#define _packed_ __attribute__((__packed__)) #define _packed_ __attribute__((__packed__))
#define _retain_ __attribute__((__retain__))
#define _used_ __attribute__((__used__)) #define _used_ __attribute__((__used__))
#define _unused_ __attribute__((__unused__)) #define _unused_ __attribute__((__unused__))
#define _cleanup_(x) __attribute__((__cleanup__(x))) #define _cleanup_(x) __attribute__((__cleanup__(x)))
@ -59,8 +61,19 @@
#define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); }) #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__) #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__)
#endif #endif
#define memcpy(a, b, c) CopyMem((a), (b), (c))
#define free(a) FreePool(a)
#endif #endif
/* This passes the argument through after (if asserts are enabled) checking that it is not null. */
#define ASSERT_PTR(expr) \
({ \
typeof(expr) _expr_ = (expr); \
assert(_expr_); \
_expr_; \
})
#if defined(static_assert) #if defined(static_assert)
#define assert_cc(expr) \ #define assert_cc(expr) \
static_assert(expr, #expr) static_assert(expr, #expr)
@ -81,8 +94,8 @@
#define ONCE __ONCE(UNIQ_T(_once_, UNIQ)) #define ONCE __ONCE(UNIQ_T(_once_, UNIQ))
#define __ONCE(o) \ #define __ONCE(o) \
({ \ ({ \
static bool (o) = false; \ static sd_bool (o) = sd_false; \
__sync_bool_compare_and_swap(&(o), false, true); \ __sync_bool_compare_and_swap(&(o), sd_false, sd_true); \
}) })
#undef MAX #undef MAX
@ -232,7 +245,7 @@
#define IN_SET(x, ...) \ #define IN_SET(x, ...) \
({ \ ({ \
sd_bool _found = false; \ sd_bool _found = sd_false; \
/* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \ /* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \
* type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \ * type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \
* the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \ * the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \
@ -241,7 +254,7 @@
assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \ assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \
switch(x) { \ switch(x) { \
FOR_EACH_MAKE_CASE(__VA_ARGS__) \ FOR_EACH_MAKE_CASE(__VA_ARGS__) \
_found = true; \ _found = sd_true; \
break; \ break; \
default: \ default: \
break; \ break; \
@ -253,8 +266,9 @@
* resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */ * resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */
#define TAKE_PTR(ptr) \ #define TAKE_PTR(ptr) \
({ \ ({ \
typeof(ptr) _ptr_ = (ptr); \ typeof(ptr) *_pptr_ = &(ptr); \
(ptr) = NULL; \ typeof(ptr) _ptr_ = *_pptr_; \
*_pptr_ = NULL; \
_ptr_; \ _ptr_; \
}) })
@ -264,3 +278,47 @@
* @x: a string literal. * @x: a string literal.
*/ */
#define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0]))) #define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0])))
#define mfree(memory) \
({ \
free(memory); \
(typeof(memory)) NULL; \
})
static inline size_t ALIGN_TO(size_t l, size_t ali) {
/* sd-boot uses UINTN for size_t, let's make sure SIZE_MAX is correct. */
assert_cc(SIZE_MAX == ~(size_t)0);
/* 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 == ULLONG_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));
}
/* Same as ALIGN_TO but callable in constant contexts. */
#define CONST_ALIGN_TO(l, ali) \
__builtin_choose_expr( \
__builtin_constant_p(l) && \
__builtin_constant_p(ali) && \
__builtin_popcountll(ali) == 1 && /* is power of 2? */ \
(l <= SIZE_MAX - (ali - 1)), /* overflow? */ \
((l) + (ali) - 1) & ~((ali) - 1), \
VOID_0)
#define UPDATE_FLAG(orig, flag, b) \
((b) ? ((orig) | (flag)) : ((orig) & ~(flag)))
#define SET_FLAG(v, flag, b) \
(v) = UPDATE_FLAG(v, flag, b)
#define FLAGS_SET(v, flags) \
((~(v) & (flags)) == 0)

View file

@ -10,7 +10,7 @@
#include "string-util-fundamental.h" #include "string-util-fundamental.h"
sd_char *startswith(const sd_char *s, const sd_char *prefix) { sd_char *startswith(const sd_char *s, const sd_char *prefix) {
sd_size_t l; size_t l;
assert(s); assert(s);
assert(prefix); assert(prefix);
@ -24,7 +24,7 @@ sd_char *startswith(const sd_char *s, const sd_char *prefix) {
#ifndef SD_BOOT #ifndef SD_BOOT
sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) { sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
sd_size_t l; size_t l;
assert(s); assert(s);
assert(prefix); assert(prefix);
@ -38,7 +38,7 @@ sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
#endif #endif
sd_char* endswith(const sd_char *s, const sd_char *postfix) { sd_char* endswith(const sd_char *s, const sd_char *postfix) {
sd_size_t sl, pl; size_t sl, pl;
assert(s); assert(s);
assert(postfix); assert(postfix);
@ -59,7 +59,7 @@ sd_char* endswith(const sd_char *s, const sd_char *postfix) {
} }
sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) { sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
sd_size_t sl, pl; size_t sl, pl;
assert(s); assert(s);
assert(postfix); assert(postfix);

View file

@ -68,10 +68,10 @@ static inline const sd_char *yes_no(sd_bool b) {
sd_int strverscmp_improved(const sd_char *a, const sd_char *b); sd_int strverscmp_improved(const sd_char *a, const sd_char *b);
/* Like startswith(), but operates on arbitrary memory blocks */ /* Like startswith(), but operates on arbitrary memory blocks */
static inline void *memory_startswith(const void *p, sd_size_t sz, const sd_char *token) { static inline void *memory_startswith(const void *p, size_t sz, const sd_char *token) {
assert(token); assert(token);
sd_size_t n = strlen(token) * sizeof(sd_char); size_t n = strlen(token) * sizeof(sd_char);
if (sz < n) if (sz < n)
return NULL; return NULL;

View file

@ -1,22 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#ifdef SD_BOOT
#include <efi.h>
typedef BOOLEAN sd_bool;
typedef CHAR16 sd_char;
typedef INTN sd_int;
typedef UINTN sd_size_t;
#define true TRUE
#define false FALSE
#else
#include <stdbool.h>
#include <stdint.h>
typedef bool sd_bool;
typedef char sd_char;
typedef int sd_int;
typedef size_t sd_size_t;
#endif

View file

@ -0,0 +1,39 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
/* This defines a number of basic types that are one thing in userspace and another in the UEFI environment,
* but mostly the same in concept and behaviour.
*
* Note: if the definition of these types/values has slightly different semantics in userspace and in the
* UEFI environment then please prefix its name with "sd_" to make clear these types have special semantics,
* and *we* defined them. Otherwise, if the types are effectively 100% identical in behaviour in userspace
* and UEFI environment you can omit the prefix. (Examples: sd_char is 8 bit in userspace and 16 bit in UEFI
* space hence it should have the sd_ prefix; but size_t in userspace and UINTN in UEFI environment are 100%
* defined the same way ultimately, hence it's OK to just define size_t as alias to UINTN in UEFI
* environment, so that size_t can be used everywhere, without any "sd_" prefix.)
*
* Note: we generally prefer the userspace names of types and concepts. i.e. if in doubt please name types
* after the userspace vocabulary, and let's keep UEFI vocabulary specific to the UEFI build environment. */
#ifdef SD_BOOT
#include <efi.h>
typedef BOOLEAN sd_bool;
typedef CHAR16 sd_char;
typedef INTN sd_int;
typedef UINTN size_t;
#define sd_true TRUE
#define sd_false FALSE
#else
#include <stdbool.h>
#include <stdint.h>
typedef bool sd_bool;
typedef char sd_char;
typedef int sd_int;
#define sd_true true
#define sd_false false
#endif

View file

@ -687,7 +687,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
} }
/* Found it! Now generate the new name */ /* Found it! Now generate the new name */
prefix = strndupa(name, saved_before - name); prefix = strndupa_safe(name, saved_before - name);
r = dns_name_concat(prefix, new_suffix, 0, ret); r = dns_name_concat(prefix, new_suffix, 0, ret);
if (r < 0) if (r < 0)
@ -1037,7 +1037,7 @@ static bool dns_service_name_label_is_valid(const char *label, size_t n) {
if (memchr(label, 0, n)) if (memchr(label, 0, n))
return false; return false;
s = strndupa(label, n); s = strndupa_safe(label, n);
return dns_service_name_is_valid(s); return dns_service_name_is_valid(s);
} }

View file

@ -62,6 +62,10 @@ static inline int dns_name_is_valid_ldh(const char *s) {
return 1; return 1;
} }
static inline bool dns_name_is_empty(const char *s) {
return isempty(s) || streq(s, ".");
}
void dns_name_hash_func(const char *s, struct siphash *state); void dns_name_hash_func(const char *s, struct siphash *state);
int dns_name_compare_func(const char *a, const char *b); int dns_name_compare_func(const char *a, const char *b);
extern const struct hash_ops dns_name_hash_ops; extern const struct hash_ops dns_name_hash_ops;