mirror of
https://github.com/systemd/systemd
synced 2026-04-21 14:34:51 +02:00
Compare commits
No commits in common. "f55fe53ffce351bb55b6190cf36511c37f99d766" and "b6fc52408afa91f2fb7650e6a7d42c65396e7815" have entirely different histories.
f55fe53ffc
...
b6fc52408a
4
NEWS
4
NEWS
@ -383,8 +383,8 @@ CHANGES WITH 250:
|
|||||||
whether to use the relevant fields from the IPv6 Router Advertisement
|
whether to use the relevant fields from the IPv6 Router Advertisement
|
||||||
records.
|
records.
|
||||||
|
|
||||||
* The ForceDHCPv6PDOtherInformation= setting in the [DHCPv6] section
|
* The ForceDHCPv6PDOtherInformation= setting in the [DHCPv6] section is
|
||||||
has been removed. Please use the WithoutRA= and UseDelegatedPrefix=
|
now removed. Please use the WithoutRA= and UseDelegatedPrefix=
|
||||||
settings in the [DHCPv6] section and the DHCPv6Client= setting in the
|
settings in the [DHCPv6] section and the DHCPv6Client= setting in the
|
||||||
[IPv6AcceptRA] section to control when the DHCPv6 client is started
|
[IPv6AcceptRA] section to control when the DHCPv6 client is started
|
||||||
and how the delegated prefixes are handled by the DHCPv6 client.
|
and how the delegated prefixes are handled by the DHCPv6 client.
|
||||||
|
|||||||
@ -407,11 +407,3 @@ available.
|
|||||||
|
|
||||||
The `gdisk` tool (from version 1.0.5 onward) and its variants (`sgdisk`,
|
The `gdisk` tool (from version 1.0.5 onward) and its variants (`sgdisk`,
|
||||||
`cgdisk`) also support creation of partitions with a matching type code.
|
`cgdisk`) also support creation of partitions with a matching type code.
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
[Boot Loader Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION)<br>
|
|
||||||
[Boot Loader Interface](https://systemd.io/BOOT_LOADER_INTERFACE)<br>
|
|
||||||
[systemd-boot(7)](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)<br>
|
|
||||||
[bootctl(1)](https://www.freedesktop.org/software/systemd/man/bootctl.html)<br>
|
|
||||||
[systemd-gpt-auto-generator(8)](https://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html)
|
|
||||||
|
|||||||
@ -84,8 +84,13 @@
|
|||||||
<term><option>-f</option></term>
|
<term><option>-f</option></term>
|
||||||
<term><option>--force</option></term>
|
<term><option>--force</option></term>
|
||||||
|
|
||||||
<listitem><para>Force immediate halt, power-off, reboot. Do
|
<listitem><para>Force immediate halt, power-off, or reboot. When
|
||||||
not contact the init system.</para></listitem>
|
specified once, this results in an immediate but clean shutdown
|
||||||
|
by the system manager. When specified twice, this results in an
|
||||||
|
immediate shutdown without contacting the system manager. See the
|
||||||
|
description of <option>--force</option> in
|
||||||
|
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||||
|
for more details.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
|||||||
@ -1894,9 +1894,9 @@
|
|||||||
<para><command>WantedBy=foo.service</command> in a service
|
<para><command>WantedBy=foo.service</command> in a service
|
||||||
<filename>bar.service</filename> is mostly equivalent to
|
<filename>bar.service</filename> is mostly equivalent to
|
||||||
<command>Alias=foo.service.wants/bar.service</command> in the
|
<command>Alias=foo.service.wants/bar.service</command> in the
|
||||||
same file. In case of template units listing non template units,
|
same file. In case of template units, <command>systemctl
|
||||||
<command>systemctl enable</command> must be called with an
|
enable</command> must be called with an instance name, and
|
||||||
instance name, and this instance will be added to the
|
this instance will be added to the
|
||||||
<filename>.wants/</filename> or
|
<filename>.wants/</filename> or
|
||||||
<filename>.requires/</filename> list of the listed unit. E.g.
|
<filename>.requires/</filename> list of the listed unit. E.g.
|
||||||
<command>WantedBy=getty.target</command> in a service
|
<command>WantedBy=getty.target</command> in a service
|
||||||
|
|||||||
@ -18,14 +18,6 @@ int _ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops HA
|
|||||||
int _ordered_set_ensure_put(OrderedSet **s, const struct hash_ops *ops, void *p HASHMAP_DEBUG_PARAMS);
|
int _ordered_set_ensure_put(OrderedSet **s, const struct hash_ops *ops, void *p HASHMAP_DEBUG_PARAMS);
|
||||||
#define ordered_set_ensure_put(s, hash_ops, key) _ordered_set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS)
|
#define ordered_set_ensure_put(s, hash_ops, key) _ordered_set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS)
|
||||||
|
|
||||||
static inline void ordered_set_clear(OrderedSet *s) {
|
|
||||||
return ordered_hashmap_clear((OrderedHashmap*) s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void ordered_set_clear_free(OrderedSet *s) {
|
|
||||||
return ordered_hashmap_clear_free((OrderedHashmap*) s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline OrderedSet* ordered_set_free(OrderedSet *s) {
|
static inline OrderedSet* ordered_set_free(OrderedSet *s) {
|
||||||
return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s);
|
return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1859,7 +1859,7 @@ static int make_partition_table(
|
|||||||
if (!t)
|
if (!t)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
r = fdisk_parttype_set_typestr(t, GPT_USER_HOME_STR);
|
r = fdisk_parttype_set_typestr(t, SD_ID128_TO_UUID_STRING(GPT_USER_HOME));
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to initialize partition type: %m");
|
return log_error_errno(r, "Failed to initialize partition type: %m");
|
||||||
|
|
||||||
|
|||||||
@ -8,27 +8,20 @@
|
|||||||
#include "sd-id128.h"
|
#include "sd-id128.h"
|
||||||
|
|
||||||
#include "dhcp-identifier.h"
|
#include "dhcp-identifier.h"
|
||||||
|
#include "dhcp6-protocol.h"
|
||||||
#include "netif-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"
|
||||||
#include "string-table.h"
|
#include "stdio-util.h"
|
||||||
#include "udev-util.h"
|
#include "udev-util.h"
|
||||||
|
#include "virt.h"
|
||||||
|
|
||||||
#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
|
#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
|
||||||
#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
|
#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
|
||||||
#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
|
#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
|
||||||
|
|
||||||
static const char * const duid_type_table[_DUID_TYPE_MAX] = {
|
int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) {
|
||||||
[DUID_TYPE_LLT] = "DUID-LLT",
|
|
||||||
[DUID_TYPE_EN] = "DUID-EN/Vendor",
|
|
||||||
[DUID_TYPE_LL] = "DUID-LL",
|
|
||||||
[DUID_TYPE_UUID] = "UUID",
|
|
||||||
};
|
|
||||||
|
|
||||||
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
|
|
||||||
|
|
||||||
int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict) {
|
|
||||||
struct duid d;
|
struct duid d;
|
||||||
|
|
||||||
assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
|
assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
|
||||||
@ -64,146 +57,110 @@ int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dhcp_identifier_set_duid_llt(const uint8_t *addr, size_t addr_len, uint16_t arp_type, usec_t t, struct duid *ret_duid, size_t *ret_len) {
|
int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) {
|
||||||
uint16_t time_from_2000y;
|
uint16_t time_from_2000y;
|
||||||
|
|
||||||
|
assert(duid);
|
||||||
|
assert(len);
|
||||||
assert(addr);
|
assert(addr);
|
||||||
assert(ret_duid);
|
|
||||||
assert(ret_len);
|
|
||||||
|
|
||||||
if (addr_len == 0)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
if (arp_type == ARPHRD_ETHER)
|
if (arp_type == ARPHRD_ETHER)
|
||||||
assert_return(addr_len == ETH_ALEN, -EINVAL);
|
assert_return(addr_len == ETH_ALEN, -EINVAL);
|
||||||
else if (arp_type == ARPHRD_INFINIBAND)
|
else if (arp_type == ARPHRD_INFINIBAND)
|
||||||
assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
|
assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
|
||||||
else
|
else
|
||||||
return -EOPNOTSUPP;
|
return -EINVAL;
|
||||||
|
|
||||||
if (t < USEC_2000)
|
if (t < USEC_2000)
|
||||||
time_from_2000y = 0;
|
time_from_2000y = 0;
|
||||||
else
|
else
|
||||||
time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
|
time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
|
||||||
|
|
||||||
unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT);
|
unaligned_write_be16(&duid->type, DUID_TYPE_LLT);
|
||||||
unaligned_write_be16(&ret_duid->llt.htype, arp_type);
|
unaligned_write_be16(&duid->llt.htype, arp_type);
|
||||||
unaligned_write_be32(&ret_duid->llt.time, time_from_2000y);
|
unaligned_write_be32(&duid->llt.time, time_from_2000y);
|
||||||
memcpy(ret_duid->llt.haddr, addr, addr_len);
|
memcpy(duid->llt.haddr, addr, addr_len);
|
||||||
|
|
||||||
*ret_len = sizeof(ret_duid->type) + sizeof(ret_duid->llt.htype) + sizeof(ret_duid->llt.time) + addr_len;
|
*len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dhcp_identifier_set_duid_ll(const uint8_t *addr, size_t addr_len, uint16_t arp_type, struct duid *ret_duid, size_t *ret_len) {
|
int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) {
|
||||||
|
assert(duid);
|
||||||
|
assert(len);
|
||||||
assert(addr);
|
assert(addr);
|
||||||
assert(ret_duid);
|
|
||||||
assert(ret_len);
|
|
||||||
|
|
||||||
if (addr_len == 0)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
if (arp_type == ARPHRD_ETHER)
|
if (arp_type == ARPHRD_ETHER)
|
||||||
assert_return(addr_len == ETH_ALEN, -EINVAL);
|
assert_return(addr_len == ETH_ALEN, -EINVAL);
|
||||||
else if (arp_type == ARPHRD_INFINIBAND)
|
else if (arp_type == ARPHRD_INFINIBAND)
|
||||||
assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
|
assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
|
||||||
else
|
else
|
||||||
return -EOPNOTSUPP;
|
return -EINVAL;
|
||||||
|
|
||||||
unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL);
|
unaligned_write_be16(&duid->type, DUID_TYPE_LL);
|
||||||
unaligned_write_be16(&ret_duid->ll.htype, arp_type);
|
unaligned_write_be16(&duid->ll.htype, arp_type);
|
||||||
memcpy(ret_duid->ll.haddr, addr, addr_len);
|
memcpy(duid->ll.haddr, addr, addr_len);
|
||||||
|
|
||||||
*ret_len = sizeof(ret_duid->type) + sizeof(ret_duid->ll.htype) + addr_len;
|
*len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len) {
|
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
|
||||||
sd_id128_t machine_id;
|
sd_id128_t machine_id;
|
||||||
uint64_t hash;
|
uint64_t hash;
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(ret_duid);
|
assert(duid);
|
||||||
assert(ret_len);
|
assert(len);
|
||||||
|
|
||||||
if (!test_mode) {
|
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||||
r = sd_id128_get_machine(&machine_id);
|
int r = sd_id128_get_machine(&machine_id);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
} else
|
#else
|
||||||
/* For tests, especially for fuzzers, reproducibility is important.
|
machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
|
||||||
* Hence, use a static and constant machine ID.
|
#endif
|
||||||
* See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
|
|
||||||
machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
|
|
||||||
|
|
||||||
unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN);
|
unaligned_write_be16(&duid->type, DUID_TYPE_EN);
|
||||||
unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN);
|
unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
|
||||||
|
|
||||||
|
*len = sizeof(duid->type) + sizeof(duid->en);
|
||||||
|
|
||||||
/* a bit of snake-oil perhaps, but no need to expose the machine-id
|
/* a bit of snake-oil perhaps, but no need to expose the machine-id
|
||||||
* directly; duid->en.id might not be aligned, so we need to copy */
|
* directly; duid->en.id might not be aligned, so we need to copy */
|
||||||
hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
|
hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
|
||||||
memcpy(ret_duid->en.id, &hash, sizeof(ret_duid->en.id));
|
memcpy(duid->en.id, &hash, sizeof(duid->en.id));
|
||||||
|
|
||||||
*ret_len = sizeof(ret_duid->type) + sizeof(ret_duid->en);
|
|
||||||
|
|
||||||
if (test_mode)
|
|
||||||
assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) {
|
int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) {
|
||||||
sd_id128_t machine_id;
|
sd_id128_t machine_id;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(ret_duid);
|
assert(duid);
|
||||||
assert(ret_len);
|
assert(len);
|
||||||
|
|
||||||
r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
|
r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID);
|
unaligned_write_be16(&duid->type, DUID_TYPE_UUID);
|
||||||
memcpy(ret_duid->raw.data, &machine_id, sizeof(machine_id));
|
memcpy(&duid->raw.data, &machine_id, sizeof(machine_id));
|
||||||
|
|
||||||
*ret_len = sizeof(ret_duid->type) + sizeof(machine_id);
|
*len = sizeof(duid->type) + sizeof(machine_id);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dhcp_identifier_set_duid(
|
|
||||||
DUIDType duid_type,
|
|
||||||
const uint8_t *addr,
|
|
||||||
size_t addr_len,
|
|
||||||
uint16_t arp_type,
|
|
||||||
usec_t llt_time,
|
|
||||||
bool test_mode,
|
|
||||||
struct duid *ret_duid,
|
|
||||||
size_t *ret_len) {
|
|
||||||
|
|
||||||
switch (duid_type) {
|
|
||||||
case DUID_TYPE_LLT:
|
|
||||||
return dhcp_identifier_set_duid_llt(addr, addr_len, arp_type, llt_time, ret_duid, ret_len);
|
|
||||||
case DUID_TYPE_EN:
|
|
||||||
return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len);
|
|
||||||
case DUID_TYPE_LL:
|
|
||||||
return dhcp_identifier_set_duid_ll(addr, addr_len, arp_type, ret_duid, ret_len);
|
|
||||||
case DUID_TYPE_UUID:
|
|
||||||
return dhcp_identifier_set_duid_uuid(ret_duid, ret_len);
|
|
||||||
default:
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int dhcp_identifier_set_iaid(
|
int dhcp_identifier_set_iaid(
|
||||||
int ifindex,
|
int ifindex,
|
||||||
const uint8_t *mac,
|
const uint8_t *mac,
|
||||||
size_t mac_len,
|
size_t mac_len,
|
||||||
bool legacy_unstable_byteorder,
|
bool legacy_unstable_byteorder,
|
||||||
bool use_mac,
|
bool use_mac,
|
||||||
void *ret) {
|
void *_id) {
|
||||||
|
|
||||||
/* name is a pointer to memory in the sd_device struct, so must
|
/* name is a pointer to memory in the sd_device struct, so must
|
||||||
* have the same scope */
|
* have the same scope */
|
||||||
@ -255,6 +212,6 @@ int dhcp_identifier_set_iaid(
|
|||||||
* behavior. */
|
* behavior. */
|
||||||
id32 = be32toh(id32);
|
id32 = be32toh(id32);
|
||||||
|
|
||||||
unaligned_write_ne32(ret, id32);
|
unaligned_write_ne32(_id, id32);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,23 +54,9 @@ struct duid {
|
|||||||
};
|
};
|
||||||
} _packed_;
|
} _packed_;
|
||||||
|
|
||||||
int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict);
|
int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict);
|
||||||
int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len);
|
int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len);
|
||||||
int dhcp_identifier_set_duid(
|
int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len);
|
||||||
DUIDType duid_type,
|
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len);
|
||||||
const uint8_t *addr,
|
int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len);
|
||||||
size_t addr_len,
|
int dhcp_identifier_set_iaid(int ifindex, const uint8_t *mac, size_t mac_len, bool legacy_unstable_byteorder, bool use_mac, void *_id);
|
||||||
uint16_t arp_type,
|
|
||||||
usec_t llt_time,
|
|
||||||
bool test_mode,
|
|
||||||
struct duid *ret_duid,
|
|
||||||
size_t *ret_len);
|
|
||||||
int dhcp_identifier_set_iaid(
|
|
||||||
int ifindex,
|
|
||||||
const uint8_t *mac,
|
|
||||||
size_t mac_len,
|
|
||||||
bool legacy_unstable_byteorder,
|
|
||||||
bool use_mac,
|
|
||||||
void *ret);
|
|
||||||
|
|
||||||
const char *duid_type_to_string(DUIDType t) _const_;
|
|
||||||
|
|||||||
@ -11,83 +11,138 @@
|
|||||||
#include "sd-event.h"
|
#include "sd-event.h"
|
||||||
#include "sd-dhcp6-client.h"
|
#include "sd-dhcp6-client.h"
|
||||||
|
|
||||||
#include "dhcp-identifier.h"
|
|
||||||
#include "dhcp6-option.h"
|
|
||||||
#include "dhcp6-protocol.h"
|
#include "dhcp6-protocol.h"
|
||||||
#include "ether-addr-util.h"
|
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
|
#include "list.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
#include "network-common.h"
|
#include "network-common.h"
|
||||||
#include "ordered-set.h"
|
|
||||||
#include "sparse-endian.h"
|
#include "sparse-endian.h"
|
||||||
#include "time-util.h"
|
|
||||||
|
|
||||||
/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
|
typedef struct sd_dhcp6_option {
|
||||||
typedef enum DHCP6RequestIA {
|
|
||||||
DHCP6_REQUEST_IA_NA = 1 << 0,
|
|
||||||
DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */
|
|
||||||
DHCP6_REQUEST_IA_PD = 1 << 2,
|
|
||||||
} DHCP6RequestIA;
|
|
||||||
|
|
||||||
struct sd_dhcp6_client {
|
|
||||||
unsigned n_ref;
|
unsigned n_ref;
|
||||||
|
|
||||||
int ifindex;
|
uint32_t enterprise_identifier;
|
||||||
char *ifname;
|
uint16_t option;
|
||||||
|
void *data;
|
||||||
|
size_t length;
|
||||||
|
} sd_dhcp6_option;
|
||||||
|
|
||||||
struct in6_addr local_address;
|
extern const struct hash_ops dhcp6_option_hash_ops;
|
||||||
struct hw_addr_data hw_addr;
|
|
||||||
uint16_t arp_type;
|
|
||||||
|
|
||||||
sd_event *event;
|
/* Common option header */
|
||||||
sd_event_source *receive_message;
|
typedef struct DHCP6Option {
|
||||||
sd_event_source *timeout_resend;
|
be16_t code;
|
||||||
sd_event_source *timeout_expire;
|
be16_t len;
|
||||||
sd_event_source *timeout_t1;
|
uint8_t data[];
|
||||||
sd_event_source *timeout_t2;
|
} _packed_ DHCP6Option;
|
||||||
int event_priority;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
DHCP6State state;
|
/* Address option */
|
||||||
bool information_request;
|
struct iaaddr {
|
||||||
usec_t information_request_time_usec;
|
struct in6_addr address;
|
||||||
usec_t information_refresh_time_usec;
|
be32_t lifetime_preferred;
|
||||||
be32_t transaction_id;
|
be32_t lifetime_valid;
|
||||||
usec_t transaction_start;
|
} _packed_;
|
||||||
usec_t retransmit_time;
|
|
||||||
uint8_t retransmit_count;
|
|
||||||
|
|
||||||
bool iaid_set;
|
/* Prefix Delegation Prefix option */
|
||||||
DHCP6IA ia_na;
|
struct iapdprefix {
|
||||||
DHCP6IA ia_pd;
|
be32_t lifetime_preferred;
|
||||||
DHCP6RequestIA request_ia;
|
be32_t lifetime_valid;
|
||||||
struct duid duid;
|
uint8_t prefixlen;
|
||||||
size_t duid_len;
|
struct in6_addr address;
|
||||||
be16_t *req_opts;
|
} _packed_;
|
||||||
size_t req_opts_len;
|
|
||||||
char *fqdn;
|
|
||||||
char *mudurl;
|
|
||||||
char **user_class;
|
|
||||||
char **vendor_class;
|
|
||||||
OrderedHashmap *extra_options;
|
|
||||||
OrderedSet *vendor_options;
|
|
||||||
|
|
||||||
struct sd_dhcp6_lease *lease;
|
typedef struct DHCP6Address DHCP6Address;
|
||||||
|
|
||||||
sd_dhcp6_client_callback_t callback;
|
struct DHCP6Address {
|
||||||
void *userdata;
|
LIST_FIELDS(DHCP6Address, addresses);
|
||||||
|
|
||||||
/* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */
|
union {
|
||||||
bool test_mode;
|
struct iaaddr iaaddr;
|
||||||
|
struct iapdprefix iapdprefix;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Non-temporary Address option */
|
||||||
|
struct ia_na {
|
||||||
|
be32_t id;
|
||||||
|
be32_t lifetime_t1;
|
||||||
|
be32_t lifetime_t2;
|
||||||
|
} _packed_;
|
||||||
|
|
||||||
|
/* Prefix Delegation option */
|
||||||
|
struct ia_pd {
|
||||||
|
be32_t id;
|
||||||
|
be32_t lifetime_t1;
|
||||||
|
be32_t lifetime_t2;
|
||||||
|
} _packed_;
|
||||||
|
|
||||||
|
/* Temporary Address option */
|
||||||
|
struct ia_ta {
|
||||||
|
be32_t id;
|
||||||
|
} _packed_;
|
||||||
|
|
||||||
|
typedef struct DHCP6IA {
|
||||||
|
uint16_t type;
|
||||||
|
union {
|
||||||
|
struct ia_na ia_na;
|
||||||
|
struct ia_pd ia_pd;
|
||||||
|
struct ia_ta ia_ta;
|
||||||
|
};
|
||||||
|
|
||||||
|
LIST_HEAD(DHCP6Address, addresses);
|
||||||
|
} DHCP6IA;
|
||||||
|
|
||||||
|
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,
|
||||||
|
size_t optlen, const void *optval);
|
||||||
|
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
|
||||||
|
int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix);
|
||||||
|
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
|
||||||
|
int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class);
|
||||||
|
int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class);
|
||||||
|
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options);
|
||||||
|
|
||||||
|
int dhcp6_option_parse(
|
||||||
|
const uint8_t *buf,
|
||||||
|
size_t buflen,
|
||||||
|
size_t *offset,
|
||||||
|
uint16_t *ret_option_code,
|
||||||
|
size_t *ret_option_data_len,
|
||||||
|
const uint8_t **ret_option_data);
|
||||||
|
int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message);
|
||||||
|
int dhcp6_option_parse_ia(
|
||||||
|
sd_dhcp6_client *client,
|
||||||
|
be32_t iaid,
|
||||||
|
uint16_t option_code,
|
||||||
|
size_t option_data_len,
|
||||||
|
const uint8_t *option_data,
|
||||||
|
DHCP6IA *ret);
|
||||||
|
int dhcp6_option_parse_addresses(
|
||||||
|
const uint8_t *optval,
|
||||||
|
size_t optlen,
|
||||||
|
struct in6_addr **addrs,
|
||||||
|
size_t *count);
|
||||||
|
int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret);
|
||||||
|
int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret);
|
||||||
|
|
||||||
int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
|
int dhcp6_network_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 dhcp6_client_send_message(sd_dhcp6_client *client);
|
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_;
|
||||||
|
int dhcp6_message_type_from_string(const char *s) _pure_;
|
||||||
|
const char *dhcp6_message_status_to_string(int s) _const_;
|
||||||
|
int dhcp6_message_status_from_string(const char *s) _pure_;
|
||||||
|
|
||||||
void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode);
|
void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode);
|
||||||
int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
|
|
||||||
|
|
||||||
#define log_dhcp6_client_errno(client, error, fmt, ...) \
|
#define log_dhcp6_client_errno(client, error, fmt, ...) \
|
||||||
log_interface_prefix_full_errno( \
|
log_interface_prefix_full_errno( \
|
||||||
|
|||||||
@ -5,13 +5,11 @@
|
|||||||
Copyright © 2014-2015 Intel Corporation. All rights reserved.
|
Copyright © 2014-2015 Intel Corporation. All rights reserved.
|
||||||
***/
|
***/
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "sd-dhcp6-lease.h"
|
#include "sd-dhcp6-lease.h"
|
||||||
|
|
||||||
#include "dhcp6-option.h"
|
#include "dhcp6-internal.h"
|
||||||
#include "macro.h"
|
|
||||||
#include "time-util.h"
|
|
||||||
|
|
||||||
struct sd_dhcp6_lease {
|
struct sd_dhcp6_lease {
|
||||||
unsigned n_ref;
|
unsigned n_ref;
|
||||||
@ -23,13 +21,10 @@ struct sd_dhcp6_lease {
|
|||||||
uint8_t preference;
|
uint8_t preference;
|
||||||
bool rapid_commit;
|
bool rapid_commit;
|
||||||
triple_timestamp timestamp;
|
triple_timestamp timestamp;
|
||||||
usec_t lifetime_t1;
|
|
||||||
usec_t lifetime_t2;
|
|
||||||
usec_t lifetime_valid;
|
|
||||||
struct in6_addr server_address;
|
struct in6_addr server_address;
|
||||||
|
|
||||||
DHCP6IA *ia_na;
|
DHCP6IA ia;
|
||||||
DHCP6IA *ia_pd;
|
DHCP6IA pd;
|
||||||
|
|
||||||
DHCP6Address *addr_iter;
|
DHCP6Address *addr_iter;
|
||||||
DHCP6Address *prefix_iter;
|
DHCP6Address *prefix_iter;
|
||||||
@ -45,15 +40,17 @@ struct sd_dhcp6_lease {
|
|||||||
char *fqdn;
|
char *fqdn;
|
||||||
};
|
};
|
||||||
|
|
||||||
int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid);
|
int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire);
|
||||||
|
DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia);
|
||||||
|
|
||||||
int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
|
int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
|
||||||
int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len);
|
int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len);
|
||||||
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const 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_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 *ret);
|
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 *ret);
|
int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit);
|
||||||
|
|
||||||
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);
|
||||||
@ -62,10 +59,3 @@ int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t op
|
|||||||
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);
|
||||||
int dhcp6_lease_new_from_message(
|
|
||||||
sd_dhcp6_client *client,
|
|
||||||
const DHCP6Message *message,
|
|
||||||
size_t len,
|
|
||||||
const triple_timestamp *timestamp,
|
|
||||||
const struct in6_addr *server_address,
|
|
||||||
sd_dhcp6_lease **ret);
|
|
||||||
|
|||||||
@ -9,12 +9,14 @@
|
|||||||
#include "sd-dhcp6-client.h"
|
#include "sd-dhcp6-client.h"
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
|
#include "dhcp-identifier.h"
|
||||||
#include "dhcp6-internal.h"
|
#include "dhcp6-internal.h"
|
||||||
#include "dhcp6-option.h"
|
#include "dhcp6-lease-internal.h"
|
||||||
#include "dhcp6-protocol.h"
|
#include "dhcp6-protocol.h"
|
||||||
#include "dns-domain.h"
|
#include "dns-domain.h"
|
||||||
#include "escape.h"
|
#include "escape.h"
|
||||||
#include "memory-util.h"
|
#include "memory-util.h"
|
||||||
|
#include "sparse-endian.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "unaligned.h"
|
#include "unaligned.h"
|
||||||
|
|
||||||
@ -210,15 +212,19 @@ bool dhcp6_option_can_request(uint16_t option) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
assert_return(buf, -EINVAL);
|
assert_return(buf, -EINVAL);
|
||||||
assert_return(*buf, -EINVAL);
|
assert_return(*buf, -EINVAL);
|
||||||
assert_return(buflen, -EINVAL);
|
assert_return(buflen, -EINVAL);
|
||||||
|
|
||||||
|
option = (DHCP6Option*) *buf;
|
||||||
|
|
||||||
if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data))
|
if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data))
|
||||||
return -ENOBUFS;
|
return -ENOBUFS;
|
||||||
|
|
||||||
unaligned_write_be16(*buf + offsetof(DHCP6Option, code), optcode);
|
option->code = htobe16(optcode);
|
||||||
unaligned_write_be16(*buf + offsetof(DHCP6Option, len), optlen);
|
option->len = htobe16(optlen);
|
||||||
|
|
||||||
*buf += offsetof(DHCP6Option, data);
|
*buf += offsetof(DHCP6Option, data);
|
||||||
*buflen -= offsetof(DHCP6Option, data);
|
*buflen -= offsetof(DHCP6Option, data);
|
||||||
@ -244,7 +250,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options) {
|
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) {
|
||||||
sd_dhcp6_option *options;
|
sd_dhcp6_option *options;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -253,7 +259,7 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet
|
|||||||
assert(buflen);
|
assert(buflen);
|
||||||
assert(vendor_options);
|
assert(vendor_options);
|
||||||
|
|
||||||
ORDERED_SET_FOREACH(options, vendor_options) {
|
ORDERED_HASHMAP_FOREACH(options, vendor_options) {
|
||||||
_cleanup_free_ uint8_t *p = NULL;
|
_cleanup_free_ uint8_t *p = NULL;
|
||||||
size_t total;
|
size_t total;
|
||||||
|
|
||||||
@ -276,33 +282,79 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int option_append_ia_address(uint8_t **buf, size_t *buflen, const struct iaaddr *address) {
|
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
|
||||||
struct iaaddr a;
|
size_t ia_buflen, ia_addrlen = 0;
|
||||||
|
struct ia_na ia_na;
|
||||||
|
struct ia_ta ia_ta;
|
||||||
|
DHCP6Address *addr;
|
||||||
|
uint8_t *ia_hdr;
|
||||||
|
uint16_t len;
|
||||||
|
void *p;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(buf);
|
assert_return(buf, -EINVAL);
|
||||||
assert(*buf);
|
assert_return(*buf, -EINVAL);
|
||||||
assert(buflen);
|
assert_return(buflen, -EINVAL);
|
||||||
assert(address);
|
assert_return(ia, -EINVAL);
|
||||||
|
|
||||||
/* Do not append T1 and T2. */
|
/* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */
|
||||||
a = (struct iaaddr) {
|
|
||||||
.address = address->address,
|
|
||||||
};
|
|
||||||
|
|
||||||
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr));
|
switch (ia->type) {
|
||||||
if (r < 0)
|
case SD_DHCP6_OPTION_IA_NA:
|
||||||
return r;
|
len = DHCP6_OPTION_IA_NA_LEN;
|
||||||
|
ia_na = (struct ia_na) {
|
||||||
|
.id = ia->ia_na.id,
|
||||||
|
};
|
||||||
|
p = &ia_na;
|
||||||
|
break;
|
||||||
|
|
||||||
memcpy(*buf, &a, sizeof(struct iaaddr));
|
case SD_DHCP6_OPTION_IA_TA:
|
||||||
|
len = DHCP6_OPTION_IA_TA_LEN;
|
||||||
|
ia_ta = (struct ia_ta) {
|
||||||
|
.id = ia->ia_ta.id,
|
||||||
|
};
|
||||||
|
p = &ia_ta;
|
||||||
|
break;
|
||||||
|
|
||||||
*buf += sizeof(struct iaaddr);
|
default:
|
||||||
*buflen -= sizeof(struct iaaddr);
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
return offsetof(DHCP6Option, data) + sizeof(struct iaaddr);
|
if (*buflen < offsetof(DHCP6Option, data) + len)
|
||||||
|
return -ENOBUFS;
|
||||||
|
|
||||||
|
ia_hdr = *buf;
|
||||||
|
ia_buflen = *buflen;
|
||||||
|
|
||||||
|
*buf += offsetof(DHCP6Option, data);
|
||||||
|
*buflen -= offsetof(DHCP6Option, data);
|
||||||
|
|
||||||
|
memcpy(*buf, p, len);
|
||||||
|
|
||||||
|
*buf += len;
|
||||||
|
*buflen -= len;
|
||||||
|
|
||||||
|
LIST_FOREACH(addresses, addr, ia->addresses) {
|
||||||
|
struct iaaddr a = {
|
||||||
|
.address = addr->iaaddr.address,
|
||||||
|
};
|
||||||
|
|
||||||
|
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr));
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
memcpy(*buf, &a, sizeof(struct iaaddr));
|
||||||
|
|
||||||
|
*buf += sizeof(struct iaaddr);
|
||||||
|
*buflen -= sizeof(struct iaaddr);
|
||||||
|
|
||||||
|
ia_addrlen += offsetof(DHCP6Option, data) + sizeof(struct iaaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct iapdprefix *prefix) {
|
static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Address *prefix) {
|
||||||
struct iapdprefix p;
|
struct iapdprefix p;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -311,13 +363,14 @@ static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct i
|
|||||||
assert(buflen);
|
assert(buflen);
|
||||||
assert(prefix);
|
assert(prefix);
|
||||||
|
|
||||||
if (prefix->prefixlen == 0)
|
if (prefix->iapdprefix.prefixlen == 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* Do not append T1 and T2. */
|
/* Do not append T1 and T2. */
|
||||||
|
|
||||||
p = (struct iapdprefix) {
|
p = (struct iapdprefix) {
|
||||||
.prefixlen = prefix->prefixlen,
|
.prefixlen = prefix->iapdprefix.prefixlen,
|
||||||
.address = prefix->address,
|
.address = prefix->iapdprefix.address,
|
||||||
};
|
};
|
||||||
|
|
||||||
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix));
|
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix));
|
||||||
@ -332,67 +385,57 @@ static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct i
|
|||||||
return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix);
|
return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
|
int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix) {
|
||||||
struct ia_header header;
|
struct ia_pd ia_pd;
|
||||||
const DHCP6Address *addr;
|
size_t len, pd_buflen;
|
||||||
size_t ia_buflen;
|
uint8_t *pd_hdr;
|
||||||
uint8_t *ia_hdr;
|
|
||||||
uint16_t len;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert_return(buf, -EINVAL);
|
assert_return(buf, -EINVAL);
|
||||||
assert_return(*buf, -EINVAL);
|
assert_return(*buf, -EINVAL);
|
||||||
assert_return(buflen, -EINVAL);
|
assert_return(buflen, -EINVAL);
|
||||||
assert_return(ia, -EINVAL);
|
assert_return(pd, -EINVAL);
|
||||||
|
assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
|
||||||
|
|
||||||
/* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */
|
/* Do not set T1 and T2. */
|
||||||
|
ia_pd = (struct ia_pd) {
|
||||||
switch (ia->type) {
|
.id = pd->ia_pd.id,
|
||||||
case SD_DHCP6_OPTION_IA_NA:
|
};
|
||||||
case SD_DHCP6_OPTION_IA_PD:
|
len = sizeof(struct ia_pd);
|
||||||
len = sizeof(struct ia_header);
|
|
||||||
header = (struct ia_header) {
|
|
||||||
.id = ia->header.id,
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_DHCP6_OPTION_IA_TA:
|
|
||||||
len = sizeof(be32_t); /* IA_TA does not have lifetime. */
|
|
||||||
header = (struct ia_header) {
|
|
||||||
.id = ia->header.id,
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert_not_reached();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*buflen < offsetof(DHCP6Option, data) + len)
|
if (*buflen < offsetof(DHCP6Option, data) + len)
|
||||||
return -ENOBUFS;
|
return -ENOBUFS;
|
||||||
|
|
||||||
ia_hdr = *buf;
|
pd_hdr = *buf;
|
||||||
ia_buflen = *buflen;
|
pd_buflen = *buflen;
|
||||||
|
|
||||||
/* The header will be written at the end of this function. */
|
/* The header will be written at the end of this function. */
|
||||||
*buf += offsetof(DHCP6Option, data);
|
*buf += offsetof(DHCP6Option, data);
|
||||||
*buflen -= offsetof(DHCP6Option, data);
|
*buflen -= offsetof(DHCP6Option, data);
|
||||||
|
|
||||||
memcpy(*buf, &header, len);
|
memcpy(*buf, &ia_pd, len);
|
||||||
*buf += len;
|
|
||||||
*buflen -= len;
|
|
||||||
|
|
||||||
LIST_FOREACH(addresses, addr, ia->addresses) {
|
*buf += sizeof(struct ia_pd);
|
||||||
if (ia->type == SD_DHCP6_OPTION_IA_PD)
|
*buflen -= sizeof(struct ia_pd);
|
||||||
r = option_append_pd_prefix(buf, buflen, &addr->iapdprefix);
|
|
||||||
else
|
DHCP6Address *prefix;
|
||||||
r = option_append_ia_address(buf, buflen, &addr->iaaddr);
|
LIST_FOREACH(addresses, prefix, pd->addresses) {
|
||||||
|
r = option_append_pd_prefix(buf, buflen, prefix);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
len += r;
|
len += r;
|
||||||
}
|
}
|
||||||
|
|
||||||
return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len);
|
if (hint_pd_prefix && hint_pd_prefix->iapdprefix.prefixlen > 0) {
|
||||||
|
r = option_append_pd_prefix(buf, buflen, hint_pd_prefix);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
len += r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return option_append_hdr(&pd_hdr, &pd_buflen, pd->type, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
|
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
|
||||||
@ -505,6 +548,7 @@ int dhcp6_option_parse(
|
|||||||
size_t *ret_option_data_len,
|
size_t *ret_option_data_len,
|
||||||
const uint8_t **ret_option_data) {
|
const uint8_t **ret_option_data) {
|
||||||
|
|
||||||
|
const DHCP6Option *option;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
assert(buf);
|
assert(buf);
|
||||||
@ -519,15 +563,16 @@ int dhcp6_option_parse(
|
|||||||
if (*offset >= buflen - offsetof(DHCP6Option, data))
|
if (*offset >= buflen - offsetof(DHCP6Option, data))
|
||||||
return -EBADMSG;
|
return -EBADMSG;
|
||||||
|
|
||||||
len = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, len));
|
option = (const DHCP6Option*) (buf + *offset);
|
||||||
|
len = be16toh(option->len);
|
||||||
|
|
||||||
if (len > buflen - offsetof(DHCP6Option, data) - *offset)
|
if (len > buflen - offsetof(DHCP6Option, data) - *offset)
|
||||||
return -EBADMSG;
|
return -EBADMSG;
|
||||||
|
|
||||||
*ret_option_code = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, code));
|
|
||||||
*ret_option_data_len = len;
|
|
||||||
*ret_option_data = buf + *offset + offsetof(DHCP6Option, data);
|
|
||||||
*offset += offsetof(DHCP6Option, data) + len;
|
*offset += offsetof(DHCP6Option, data) + len;
|
||||||
|
*ret_option_code = be16toh(option->code);
|
||||||
|
*ret_option_data_len = len;
|
||||||
|
*ret_option_data = option->data;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -556,7 +601,7 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s
|
|||||||
static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) {
|
static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(buf || buflen == 0);
|
assert(buf);
|
||||||
|
|
||||||
for(size_t offset = 0; offset < buflen;) {
|
for(size_t offset = 0; offset < buflen;) {
|
||||||
const uint8_t *data;
|
const uint8_t *data;
|
||||||
@ -574,15 +619,15 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t
|
|||||||
r = dhcp6_option_parse_status(data, data_len, &msg);
|
r = dhcp6_option_parse_status(data, data_len, &msg);
|
||||||
if (r == -ENOMEM)
|
if (r == -ENOMEM)
|
||||||
return r;
|
return r;
|
||||||
if (r > 0)
|
|
||||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
|
||||||
"Received an IA address or PD prefix option with non-zero status: %s%s%s",
|
|
||||||
strempty(msg), isempty(msg) ? "" : ": ",
|
|
||||||
dhcp6_message_status_to_string(r));
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
/* Let's log but ignore the invalid status option. */
|
/* Let's log but ignore the invalid status option. */
|
||||||
log_dhcp6_client_errno(client, r,
|
log_dhcp6_client_errno(client, r,
|
||||||
"Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m");
|
"Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m");
|
||||||
|
else if (r > 0)
|
||||||
|
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"Received an IA address or PD prefix option with non-zero status: %s%s%s",
|
||||||
|
strempty(msg), isempty(msg) ? "" : ": ",
|
||||||
|
dhcp6_message_status_to_string(r));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -593,29 +638,19 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) {
|
static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) {
|
||||||
_cleanup_free_ DHCP6Address *a = NULL;
|
|
||||||
uint32_t lt_valid, lt_pref;
|
uint32_t lt_valid, lt_pref;
|
||||||
|
DHCP6Address *a;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(ia);
|
assert(data);
|
||||||
assert(data || len == 0);
|
assert(ret);
|
||||||
|
|
||||||
if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA))
|
|
||||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
|
||||||
"Received an IA address sub-option in an invalid option, ignoring.");
|
|
||||||
|
|
||||||
if (len < sizeof(struct iaaddr))
|
if (len < sizeof(struct iaaddr))
|
||||||
return -EBADMSG;
|
return -EBADMSG;
|
||||||
|
|
||||||
a = new(DHCP6Address, 1);
|
lt_valid = be32toh(((const struct iaaddr*) data)->lifetime_valid);
|
||||||
if (!a)
|
lt_pref = be32toh(((const struct iaaddr*) data)->lifetime_preferred);
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
memcpy(&a->iaaddr, data, sizeof(struct iaaddr));
|
|
||||||
|
|
||||||
lt_valid = be32toh(a->iaaddr.lifetime_valid);
|
|
||||||
lt_pref = be32toh(a->iaaddr.lifetime_preferred);
|
|
||||||
|
|
||||||
if (lt_valid == 0)
|
if (lt_valid == 0)
|
||||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||||
@ -632,33 +667,27 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, c
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) {
|
|
||||||
_cleanup_free_ DHCP6Address *a = NULL;
|
|
||||||
uint32_t lt_valid, lt_pref;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(ia);
|
|
||||||
assert(data || len == 0);
|
|
||||||
|
|
||||||
if (ia->type != SD_DHCP6_OPTION_IA_PD)
|
|
||||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
|
||||||
"Received an PD prefix sub-option in an invalid option, ignoring");
|
|
||||||
|
|
||||||
if (len < sizeof(struct iapdprefix))
|
|
||||||
return -EBADMSG;
|
|
||||||
|
|
||||||
a = new(DHCP6Address, 1);
|
a = new(DHCP6Address, 1);
|
||||||
if (!a)
|
if (!a)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
|
LIST_INIT(addresses, a);
|
||||||
|
memcpy(&a->iaaddr, data, sizeof(struct iaaddr));
|
||||||
|
|
||||||
lt_valid = be32toh(a->iapdprefix.lifetime_valid);
|
*ret = a;
|
||||||
lt_pref = be32toh(a->iapdprefix.lifetime_preferred);
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) {
|
||||||
|
uint32_t lt_valid, lt_pref;
|
||||||
|
DHCP6Address *a;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (len < sizeof(struct iapdprefix))
|
||||||
|
return -ENOMSG;
|
||||||
|
|
||||||
|
lt_valid = be32toh(((const struct iapdprefix*) data)->lifetime_valid);
|
||||||
|
lt_pref = be32toh(((const struct iapdprefix*) data)->lifetime_preferred);
|
||||||
|
|
||||||
if (lt_valid == 0)
|
if (lt_valid == 0)
|
||||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||||
@ -675,7 +704,14 @@ static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia,
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a));
|
a = new(DHCP6Address, 1);
|
||||||
|
if (!a)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
LIST_INIT(addresses, a);
|
||||||
|
memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
|
||||||
|
|
||||||
|
*ret = a;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -685,15 +721,16 @@ int dhcp6_option_parse_ia(
|
|||||||
uint16_t option_code,
|
uint16_t option_code,
|
||||||
size_t option_data_len,
|
size_t option_data_len,
|
||||||
const uint8_t *option_data,
|
const uint8_t *option_data,
|
||||||
DHCP6IA **ret) {
|
DHCP6IA *ret) {
|
||||||
|
|
||||||
_cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
|
_cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
|
||||||
uint32_t lt_t1, lt_t2;
|
uint32_t lt_t1, lt_t2, lt_min = UINT32_MAX;
|
||||||
size_t header_len;
|
be32_t received_iaid;
|
||||||
|
size_t offset;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD));
|
assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD));
|
||||||
assert(option_data || option_data_len == 0);
|
assert(option_data);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
/* This will return the following:
|
/* This will return the following:
|
||||||
@ -706,51 +743,56 @@ int dhcp6_option_parse_ia(
|
|||||||
|
|
||||||
switch (option_code) {
|
switch (option_code) {
|
||||||
case SD_DHCP6_OPTION_IA_NA:
|
case SD_DHCP6_OPTION_IA_NA:
|
||||||
|
|
||||||
|
if (option_data_len < DHCP6_OPTION_IA_NA_LEN)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
offset = DHCP6_OPTION_IA_NA_LEN;
|
||||||
|
|
||||||
|
received_iaid = ((const struct ia_na*) option_data)->id;
|
||||||
|
lt_t1 = be32toh(((const struct ia_na*) option_data)->lifetime_t1);
|
||||||
|
lt_t2 = be32toh(((const struct ia_na*) option_data)->lifetime_t2);
|
||||||
|
break;
|
||||||
|
|
||||||
case SD_DHCP6_OPTION_IA_PD:
|
case SD_DHCP6_OPTION_IA_PD:
|
||||||
header_len = sizeof(struct ia_header);
|
|
||||||
|
if (option_data_len < DHCP6_OPTION_IA_PD_LEN)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
offset = DHCP6_OPTION_IA_PD_LEN;
|
||||||
|
|
||||||
|
received_iaid = ((const struct ia_pd*) option_data)->id;
|
||||||
|
lt_t1 = be32toh(((const struct ia_pd*) option_data)->lifetime_t1);
|
||||||
|
lt_t2 = be32toh(((const struct ia_pd*) option_data)->lifetime_t2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SD_DHCP6_OPTION_IA_TA:
|
case SD_DHCP6_OPTION_IA_TA:
|
||||||
header_len = sizeof(be32_t); /* IA_TA does not have lifetime. */
|
if (option_data_len < DHCP6_OPTION_IA_TA_LEN)
|
||||||
|
return -ENOMSG;
|
||||||
|
|
||||||
|
offset = DHCP6_OPTION_IA_TA_LEN;
|
||||||
|
|
||||||
|
received_iaid = ((const struct ia_ta*) option_data)->id;
|
||||||
|
lt_t1 = lt_t2 = 0; /* No lifetime for IA_TA. */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert_not_reached();
|
assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (option_data_len < header_len)
|
|
||||||
return -EBADMSG;
|
|
||||||
|
|
||||||
ia = new(DHCP6IA, 1);
|
|
||||||
if (!ia)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*ia = (DHCP6IA) {
|
|
||||||
.type = option_code,
|
|
||||||
};
|
|
||||||
memcpy(&ia->header, option_data, header_len);
|
|
||||||
|
|
||||||
/* According to RFC8415, IAs which do not match the client's IAID should be ignored,
|
/* According to RFC8415, IAs which do not match the client's IAID should be ignored,
|
||||||
* but not necessary to ignore or refuse the whole message. */
|
* but not necessary to ignore or refuse the whole message. */
|
||||||
if (ia->header.id != iaid)
|
if (received_iaid != iaid)
|
||||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
|
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
|
||||||
"Received an IA option with a different IAID "
|
"Received an IA option with a different IAID "
|
||||||
"from the one chosen by the client, ignoring.");
|
"from the one chosen by the client, ignoring.");
|
||||||
|
|
||||||
/* It is not necessary to check if the lifetime_t2 is zero here, as in that case it will be updated later. */
|
|
||||||
lt_t1 = be32toh(ia->header.lifetime_t1);
|
|
||||||
lt_t2 = be32toh(ia->header.lifetime_t2);
|
|
||||||
|
|
||||||
if (lt_t1 > lt_t2)
|
if (lt_t1 > lt_t2)
|
||||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||||
"Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.",
|
"Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.",
|
||||||
lt_t1, lt_t2);
|
lt_t1, lt_t2);
|
||||||
if (lt_t1 == 0 && lt_t2 > 0)
|
|
||||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
|
||||||
"Received an IA option with zero T1 and non-zero T2 (%"PRIu32"sec), ignoring.",
|
|
||||||
lt_t2);
|
|
||||||
|
|
||||||
for (size_t offset = header_len; offset < option_data_len;) {
|
for (; offset < option_data_len;) {
|
||||||
const uint8_t *subdata;
|
const uint8_t *subdata;
|
||||||
size_t subdata_len;
|
size_t subdata_len;
|
||||||
uint16_t subopt;
|
uint16_t subopt;
|
||||||
@ -761,19 +803,41 @@ int dhcp6_option_parse_ia(
|
|||||||
|
|
||||||
switch (subopt) {
|
switch (subopt) {
|
||||||
case SD_DHCP6_OPTION_IAADDR: {
|
case SD_DHCP6_OPTION_IAADDR: {
|
||||||
r = dhcp6_option_parse_ia_address(client, ia, subdata, subdata_len);
|
DHCP6Address *a;
|
||||||
|
|
||||||
|
if (!IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) {
|
||||||
|
log_dhcp6_client(client, "Received an IA_PD option with an IA address, ignoring.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dhcp6_option_parse_ia_address(client, subdata, subdata_len, &a);
|
||||||
if (r == -ENOMEM)
|
if (r == -ENOMEM)
|
||||||
return r;
|
return r;
|
||||||
|
if (r < 0)
|
||||||
|
/* Ignore the sub-option on non-critical errors. */
|
||||||
|
continue;
|
||||||
|
|
||||||
/* Ignore non-critical errors in the sub-option. */
|
lt_min = MIN(lt_min, be32toh(a->iaaddr.lifetime_valid));
|
||||||
|
LIST_PREPEND(addresses, ia.addresses, a);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SD_DHCP6_OPTION_IA_PD_PREFIX: {
|
case SD_DHCP6_OPTION_IA_PD_PREFIX: {
|
||||||
r = dhcp6_option_parse_ia_pdprefix(client, ia, subdata, subdata_len);
|
DHCP6Address *a;
|
||||||
|
|
||||||
|
if (option_code != SD_DHCP6_OPTION_IA_PD) {
|
||||||
|
log_dhcp6_client(client, "Received an IA_NA or IA_TA option with an PD prefix, ignoring");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dhcp6_option_parse_ia_pdprefix(client, subdata, subdata_len, &a);
|
||||||
if (r == -ENOMEM)
|
if (r == -ENOMEM)
|
||||||
return r;
|
return r;
|
||||||
|
if (r < 0)
|
||||||
|
/* Ignore the sub-option on non-critical errors. */
|
||||||
|
continue;
|
||||||
|
|
||||||
/* Ignore non-critical errors in the sub-option. */
|
lt_min = MIN(lt_min, be32toh(a->iapdprefix.lifetime_valid));
|
||||||
|
LIST_PREPEND(addresses, ia.addresses, a);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SD_DHCP6_OPTION_STATUS_CODE: {
|
case SD_DHCP6_OPTION_STATUS_CODE: {
|
||||||
@ -782,14 +846,14 @@ int dhcp6_option_parse_ia(
|
|||||||
r = dhcp6_option_parse_status(subdata, subdata_len, &msg);
|
r = dhcp6_option_parse_status(subdata, subdata_len, &msg);
|
||||||
if (r == -ENOMEM)
|
if (r == -ENOMEM)
|
||||||
return r;
|
return r;
|
||||||
if (r > 0)
|
if (r < 0)
|
||||||
|
log_dhcp6_client_errno(client, r,
|
||||||
|
"Received an IA option with an invalid status sub option, ignoring: %m");
|
||||||
|
else if (r > 0)
|
||||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||||
"Received an IA option with non-zero status: %s%s%s",
|
"Received an IA option with non-zero status: %s%s%s",
|
||||||
strempty(msg), isempty(msg) ? "" : ": ",
|
strempty(msg), isempty(msg) ? "" : ": ",
|
||||||
dhcp6_message_status_to_string(r));
|
dhcp6_message_status_to_string(r));
|
||||||
if (r < 0)
|
|
||||||
log_dhcp6_client_errno(client, r,
|
|
||||||
"Received an IA option with an invalid status sub option, ignoring: %m");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -797,11 +861,50 @@ int dhcp6_option_parse_ia(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ia->addresses)
|
if (!ia.addresses)
|
||||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA),
|
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA),
|
||||||
"Received an IA option without valid IA addresses or PD prefixes, ignoring.");
|
"Received an IA option without valid IA addresses or PD prefixes, ignoring.");
|
||||||
|
|
||||||
*ret = TAKE_PTR(ia);
|
if (IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_PD) &&
|
||||||
|
lt_t1 == 0 && lt_t2 == 0 && lt_min != UINT32_MAX) {
|
||||||
|
lt_t1 = lt_min / 2;
|
||||||
|
lt_t2 = lt_min / 10 * 8;
|
||||||
|
|
||||||
|
log_dhcp6_client(client, "Received an IA option with both T1 and T2 equal to zero. "
|
||||||
|
"Adjusting them based on the minimum valid lifetime of IA addresses or PD prefixes: "
|
||||||
|
"T1=%"PRIu32"sec, T2=%"PRIu32"sec", lt_t1, lt_t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(option_code) {
|
||||||
|
case SD_DHCP6_OPTION_IA_NA:
|
||||||
|
*ret = (DHCP6IA) {
|
||||||
|
.type = option_code,
|
||||||
|
.ia_na.id = iaid,
|
||||||
|
.ia_na.lifetime_t1 = htobe32(lt_t1),
|
||||||
|
.ia_na.lifetime_t2 = htobe32(lt_t2),
|
||||||
|
.addresses = TAKE_PTR(ia.addresses),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case SD_DHCP6_OPTION_IA_TA:
|
||||||
|
*ret = (DHCP6IA) {
|
||||||
|
.type = option_code,
|
||||||
|
.ia_ta.id = iaid,
|
||||||
|
.addresses = TAKE_PTR(ia.addresses),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case SD_DHCP6_OPTION_IA_PD:
|
||||||
|
*ret = (DHCP6IA) {
|
||||||
|
.type = option_code,
|
||||||
|
.ia_pd.id = iaid,
|
||||||
|
.ia_pd.lifetime_t1 = htobe32(lt_t1),
|
||||||
|
.ia_pd.lifetime_t2 = htobe32(lt_t2),
|
||||||
|
.addresses = TAKE_PTR(ia.addresses),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert_not_reached();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,104 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "sd-dhcp6-client.h"
|
|
||||||
|
|
||||||
#include "hash-funcs.h"
|
|
||||||
#include "list.h"
|
|
||||||
#include "macro.h"
|
|
||||||
#include "ordered-set.h"
|
|
||||||
#include "sparse-endian.h"
|
|
||||||
|
|
||||||
typedef struct sd_dhcp6_option {
|
|
||||||
unsigned n_ref;
|
|
||||||
|
|
||||||
uint32_t enterprise_identifier;
|
|
||||||
uint16_t option;
|
|
||||||
void *data;
|
|
||||||
size_t length;
|
|
||||||
} sd_dhcp6_option;
|
|
||||||
|
|
||||||
extern const struct hash_ops dhcp6_option_hash_ops;
|
|
||||||
|
|
||||||
/* Common option header */
|
|
||||||
typedef struct DHCP6Option {
|
|
||||||
be16_t code;
|
|
||||||
be16_t len;
|
|
||||||
uint8_t data[];
|
|
||||||
} _packed_ DHCP6Option;
|
|
||||||
|
|
||||||
/* Address option */
|
|
||||||
struct iaaddr {
|
|
||||||
struct in6_addr address;
|
|
||||||
be32_t lifetime_preferred;
|
|
||||||
be32_t lifetime_valid;
|
|
||||||
} _packed_;
|
|
||||||
|
|
||||||
/* Prefix Delegation Prefix option */
|
|
||||||
struct iapdprefix {
|
|
||||||
be32_t lifetime_preferred;
|
|
||||||
be32_t lifetime_valid;
|
|
||||||
uint8_t prefixlen;
|
|
||||||
struct in6_addr address;
|
|
||||||
} _packed_;
|
|
||||||
|
|
||||||
typedef struct DHCP6Address DHCP6Address;
|
|
||||||
|
|
||||||
struct DHCP6Address {
|
|
||||||
LIST_FIELDS(DHCP6Address, addresses);
|
|
||||||
|
|
||||||
union {
|
|
||||||
struct iaaddr iaaddr;
|
|
||||||
struct iapdprefix iapdprefix;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ia_header {
|
|
||||||
be32_t id;
|
|
||||||
be32_t lifetime_t1;
|
|
||||||
be32_t lifetime_t2;
|
|
||||||
} _packed_;
|
|
||||||
|
|
||||||
typedef struct DHCP6IA {
|
|
||||||
uint16_t type;
|
|
||||||
struct ia_header header;
|
|
||||||
|
|
||||||
LIST_HEAD(DHCP6Address, addresses);
|
|
||||||
} DHCP6IA;
|
|
||||||
|
|
||||||
void dhcp6_ia_clear_addresses(DHCP6IA *ia);
|
|
||||||
DHCP6IA *dhcp6_ia_free(DHCP6IA *ia);
|
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6IA*, dhcp6_ia_free);
|
|
||||||
|
|
||||||
bool dhcp6_option_can_request(uint16_t option);
|
|
||||||
|
|
||||||
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
|
|
||||||
size_t optlen, const void *optval);
|
|
||||||
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
|
|
||||||
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
|
|
||||||
int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class);
|
|
||||||
int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class);
|
|
||||||
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options);
|
|
||||||
|
|
||||||
int dhcp6_option_parse(
|
|
||||||
const uint8_t *buf,
|
|
||||||
size_t buflen,
|
|
||||||
size_t *offset,
|
|
||||||
uint16_t *ret_option_code,
|
|
||||||
size_t *ret_option_data_len,
|
|
||||||
const uint8_t **ret_option_data);
|
|
||||||
int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message);
|
|
||||||
int dhcp6_option_parse_ia(
|
|
||||||
sd_dhcp6_client *client,
|
|
||||||
be32_t iaid,
|
|
||||||
uint16_t option_code,
|
|
||||||
size_t option_data_len,
|
|
||||||
const uint8_t *option_data,
|
|
||||||
DHCP6IA **ret);
|
|
||||||
int dhcp6_option_parse_addresses(
|
|
||||||
const uint8_t *optval,
|
|
||||||
size_t optlen,
|
|
||||||
struct in6_addr **addrs,
|
|
||||||
size_t *count);
|
|
||||||
int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret);
|
|
||||||
int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret);
|
|
||||||
@ -1,84 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
||||||
|
|
||||||
#include "dhcp6-protocol.h"
|
|
||||||
#include "string-table.h"
|
|
||||||
|
|
||||||
static const char * const dhcp6_state_table[_DHCP6_STATE_MAX] = {
|
|
||||||
[DHCP6_STATE_STOPPED] = "stopped",
|
|
||||||
[DHCP6_STATE_INFORMATION_REQUEST] = "information-request",
|
|
||||||
[DHCP6_STATE_SOLICITATION] = "solicitation",
|
|
||||||
[DHCP6_STATE_REQUEST] = "request",
|
|
||||||
[DHCP6_STATE_BOUND] = "bound",
|
|
||||||
[DHCP6_STATE_RENEW] = "renew",
|
|
||||||
[DHCP6_STATE_REBIND] = "rebind",
|
|
||||||
};
|
|
||||||
|
|
||||||
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_state, DHCP6State);
|
|
||||||
|
|
||||||
static const char * const dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = {
|
|
||||||
[DHCP6_MESSAGE_SOLICIT] = "Solicit",
|
|
||||||
[DHCP6_MESSAGE_ADVERTISE] = "Advertise",
|
|
||||||
[DHCP6_MESSAGE_REQUEST] = "Request",
|
|
||||||
[DHCP6_MESSAGE_CONFIRM] = "Confirm",
|
|
||||||
[DHCP6_MESSAGE_RENEW] = "Renew",
|
|
||||||
[DHCP6_MESSAGE_REBIND] = "Rebind",
|
|
||||||
[DHCP6_MESSAGE_REPLY] = "Reply",
|
|
||||||
[DHCP6_MESSAGE_RELEASE] = "Release",
|
|
||||||
[DHCP6_MESSAGE_DECLINE] = "Decline",
|
|
||||||
[DHCP6_MESSAGE_RECONFIGURE] = "Reconfigure",
|
|
||||||
[DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request",
|
|
||||||
[DHCP6_MESSAGE_RELAY_FORWARD] = "Relay Forward",
|
|
||||||
[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, DHCP6MessageType);
|
|
||||||
|
|
||||||
static const char * const dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
|
|
||||||
[DHCP6_STATUS_SUCCESS] = "Success",
|
|
||||||
[DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
|
|
||||||
[DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
|
|
||||||
[DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
|
|
||||||
[DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
|
|
||||||
[DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
|
|
||||||
[DHCP6_STATUS_NO_PREFIX_AVAIL] = "No prefix available",
|
|
||||||
[DHCP6_STATUS_UNKNOWN_QUERY_TYPE] = "Unknown query type",
|
|
||||||
[DHCP6_STATUS_MALFORMED_QUERY] = "Malformed query",
|
|
||||||
[DHCP6_STATUS_NOT_CONFIGURED] = "Not configured",
|
|
||||||
[DHCP6_STATUS_NOT_ALLOWED] = "Not allowed",
|
|
||||||
[DHCP6_STATUS_QUERY_TERMINATED] = "Query terminated",
|
|
||||||
[DHCP6_STATUS_DATA_MISSING] = "Data missing",
|
|
||||||
[DHCP6_STATUS_CATCHUP_COMPLETE] = "Catch up complete",
|
|
||||||
[DHCP6_STATUS_NOT_SUPPORTED] = "Not supported",
|
|
||||||
[DHCP6_STATUS_TLS_CONNECTION_REFUSED] = "TLS connection refused",
|
|
||||||
[DHCP6_STATUS_ADDRESS_IN_USE] = "Address in use",
|
|
||||||
[DHCP6_STATUS_CONFIGURATION_CONFLICT] = "Configuration conflict",
|
|
||||||
[DHCP6_STATUS_MISSING_BINDING_INFORMATION] = "Missing binding information",
|
|
||||||
[DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information",
|
|
||||||
[DHCP6_STATUS_SERVER_SHUTTING_DOWN] = "Server shutting down",
|
|
||||||
[DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED] = "DNS update not supported",
|
|
||||||
[DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew",
|
|
||||||
};
|
|
||||||
|
|
||||||
DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, DHCP6Status);
|
|
||||||
@ -28,8 +28,8 @@ typedef struct DHCP6Message DHCP6Message;
|
|||||||
#define DHCP6_MIN_OPTIONS_SIZE \
|
#define DHCP6_MIN_OPTIONS_SIZE \
|
||||||
1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
|
1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
|
||||||
|
|
||||||
#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \
|
#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \
|
||||||
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } }
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } }
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -100,15 +100,13 @@ typedef enum DHCP6MessageType {
|
|||||||
DHCP6_MESSAGE_STATE = 34, /* RFC 8156 */
|
DHCP6_MESSAGE_STATE = 34, /* RFC 8156 */
|
||||||
DHCP6_MESSAGE_CONTACT = 35, /* RFC 8156 */
|
DHCP6_MESSAGE_CONTACT = 35, /* RFC 8156 */
|
||||||
_DHCP6_MESSAGE_TYPE_MAX,
|
_DHCP6_MESSAGE_TYPE_MAX,
|
||||||
_DHCP6_MESSAGE_TYPE_INVALID = -EINVAL,
|
_DHCP6_MESSAGE_TYPE_INVALID = -EINVAL,
|
||||||
} DHCP6MessageType;
|
} DHCP6MessageType;
|
||||||
|
|
||||||
typedef enum DHCP6NTPSubOption {
|
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,
|
||||||
_DHCP6_NTP_SUBOPTION_MAX,
|
|
||||||
_DHCP6_NTP_SUBOPTION_INVALID = -EINVAL,
|
|
||||||
} DHCP6NTPSubOption;
|
} DHCP6NTPSubOption;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -140,7 +138,7 @@ typedef enum DHCP6Status {
|
|||||||
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,
|
_DHCP6_STATUS_MAX,
|
||||||
_DHCP6_STATUS_INVALID = -EINVAL,
|
_DHCP6_STATUS_INVALID = -EINVAL,
|
||||||
} DHCP6Status;
|
} DHCP6Status;
|
||||||
|
|
||||||
typedef enum DHCP6FQDNFlag {
|
typedef enum DHCP6FQDNFlag {
|
||||||
@ -148,9 +146,3 @@ typedef enum DHCP6FQDNFlag {
|
|||||||
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;
|
} DHCP6FQDNFlag;
|
||||||
|
|
||||||
const char *dhcp6_state_to_string(DHCP6State s) _const_;
|
|
||||||
const char *dhcp6_message_type_to_string(DHCP6MessageType s) _const_;
|
|
||||||
DHCP6MessageType dhcp6_message_type_from_string(const char *s) _pure_;
|
|
||||||
const char *dhcp6_message_status_to_string(DHCP6Status s) _const_;
|
|
||||||
DHCP6Status dhcp6_message_status_from_string(const char *s) _pure_;
|
|
||||||
|
|||||||
61
src/libsystemd-network/fuzz-dhcp6-client-send.c
Normal file
61
src/libsystemd-network/fuzz-dhcp6-client-send.c
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "fuzz.h"
|
||||||
|
|
||||||
|
#include "sd-dhcp6-client.c"
|
||||||
|
|
||||||
|
int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
|
||||||
|
const void *packet, size_t len) {
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
|
||||||
|
assert_se(fd >= 0);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||||
|
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
|
||||||
|
_cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
|
||||||
|
struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
|
||||||
|
triple_timestamp t = {};
|
||||||
|
usec_t time_now;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (size < sizeof(DHCP6Message))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
assert_se(sd_event_new(&e) >= 0);
|
||||||
|
assert_se(sd_dhcp6_client_new(&client) >= 0);
|
||||||
|
assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
|
||||||
|
assert_se(sd_dhcp6_client_set_ifindex(client, 42) == 0);
|
||||||
|
assert_se(sd_dhcp6_client_set_fqdn(client, "example.com") == 1);
|
||||||
|
assert_se(sd_dhcp6_client_set_request_mud_url(client, "https://www.example.com/mudfile.json") >= 0);
|
||||||
|
assert_se(sd_dhcp6_client_set_request_user_class(client, STRV_MAKE("u1", "u2", "u3")) >= 0);
|
||||||
|
assert_se(sd_dhcp6_client_set_request_vendor_class(client, STRV_MAKE("v1", "v2", "v3")) >= 0);
|
||||||
|
assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
|
||||||
|
assert_se(sd_dhcp6_client_set_information_request(client, false) == 0);
|
||||||
|
dhcp6_client_set_test_mode(client, true);
|
||||||
|
assert_se(sd_dhcp6_client_start(client) >= 0);
|
||||||
|
assert_se(sd_dhcp6_client_set_transaction_id(client, htobe32(0x00ffffff) & ((const DHCP6Message *) data)->transaction_id) == 0);
|
||||||
|
|
||||||
|
triple_timestamp_get(&t);
|
||||||
|
r = client_receive_advertise(client, (DHCP6Message *) data, size, &t, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
|
||||||
|
if (r < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (r == DHCP6_STATE_REQUEST)
|
||||||
|
client->state = DHCP6_STATE_REQUEST;
|
||||||
|
(void) client_send_message(client, time_now);
|
||||||
|
cleanup:
|
||||||
|
assert_se(sd_dhcp6_client_stop(client) >= 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@ -6,59 +6,43 @@
|
|||||||
#include "sd-event.h"
|
#include "sd-event.h"
|
||||||
|
|
||||||
#include "dhcp6-internal.h"
|
#include "dhcp6-internal.h"
|
||||||
#include "event-util.h"
|
#include "dhcp6-protocol.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fuzz.h"
|
#include "fuzz.h"
|
||||||
|
|
||||||
static int test_dhcp_fd[2] = { -1, -1 };
|
static int test_dhcp_fd[2] = { -1, -1 };
|
||||||
|
|
||||||
int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, const void *packet, size_t len) {
|
int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
|
||||||
|
const void *packet, size_t len) {
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
|
int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
|
||||||
assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) >= 0);
|
assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) >= 0);
|
||||||
return TAKE_FD(test_dhcp_fd[0]);
|
return test_dhcp_fd[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fuzz_client(sd_dhcp6_client *client, const uint8_t *data, size_t size, DHCP6State state) {
|
static void fuzz_client(const uint8_t *data, size_t size, bool is_information_request_enabled) {
|
||||||
assert_se(sd_dhcp6_client_set_information_request(client, state == DHCP6_STATE_INFORMATION_REQUEST) >= 0);
|
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
|
||||||
|
_cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
|
||||||
|
struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
|
||||||
|
|
||||||
|
assert_se(sd_event_new(&e) >= 0);
|
||||||
|
assert_se(sd_dhcp6_client_new(&client) >= 0);
|
||||||
|
assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
|
||||||
|
assert_se(sd_dhcp6_client_set_ifindex(client, 42) == 0);
|
||||||
|
assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
|
||||||
|
assert_se(sd_dhcp6_client_set_information_request(client, is_information_request_enabled) == 0);
|
||||||
|
dhcp6_client_set_test_mode(client, true);
|
||||||
|
|
||||||
assert_se(sd_dhcp6_client_start(client) >= 0);
|
assert_se(sd_dhcp6_client_start(client) >= 0);
|
||||||
|
|
||||||
client->state = state;
|
|
||||||
|
|
||||||
if (size >= sizeof(DHCP6Message))
|
if (size >= sizeof(DHCP6Message))
|
||||||
assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message *) data)->transaction_id) == 0);
|
assert_se(sd_dhcp6_client_set_transaction_id(client, htobe32(0x00ffffff) & ((const DHCP6Message *) data)->transaction_id) == 0);
|
||||||
|
|
||||||
/* These states does not require lease to send message. */
|
|
||||||
if (IN_SET(client->state, DHCP6_STATE_INFORMATION_REQUEST, DHCP6_STATE_SOLICITATION))
|
|
||||||
assert_se(dhcp6_client_send_message(client) >= 0);
|
|
||||||
|
|
||||||
assert_se(write(test_dhcp_fd[1], data, size) == (ssize_t) size);
|
assert_se(write(test_dhcp_fd[1], data, size) == (ssize_t) size);
|
||||||
|
|
||||||
assert_se(sd_event_run(sd_dhcp6_client_get_event(client), UINT64_MAX) > 0);
|
sd_event_run(e, UINT64_MAX);
|
||||||
|
|
||||||
/* Check the state transition. */
|
|
||||||
if (client->state != state)
|
|
||||||
switch (state) {
|
|
||||||
case DHCP6_STATE_INFORMATION_REQUEST:
|
|
||||||
assert_se(client->state == DHCP6_STATE_STOPPED);
|
|
||||||
break;
|
|
||||||
case DHCP6_STATE_SOLICITATION:
|
|
||||||
assert_se(IN_SET(client->state, DHCP6_STATE_REQUEST, DHCP6_STATE_BOUND));
|
|
||||||
break;
|
|
||||||
case DHCP6_STATE_REQUEST:
|
|
||||||
assert_se(client->state == DHCP6_STATE_BOUND);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert_not_reached();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send message if the client has a lease. */
|
|
||||||
if (state != DHCP6_STATE_INFORMATION_REQUEST && sd_dhcp6_client_get_lease(client, NULL) >= 0) {
|
|
||||||
client->state = DHCP6_STATE_REQUEST;
|
|
||||||
dhcp6_client_send_message(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_se(sd_dhcp6_client_stop(client) >= 0);
|
assert_se(sd_dhcp6_client_stop(client) >= 0);
|
||||||
|
|
||||||
@ -66,43 +50,14 @@ static void fuzz_client(sd_dhcp6_client *client, const uint8_t *data, size_t siz
|
|||||||
}
|
}
|
||||||
|
|
||||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||||
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
|
|
||||||
_cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
|
|
||||||
_cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *v1 = NULL, *v2 = NULL;
|
|
||||||
struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
|
|
||||||
struct in6_addr hint = { { { 0x3f, 0xfe, 0x05, 0x01, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } };
|
|
||||||
static const char *v1_data = "hogehoge", *v2_data = "foobar";
|
|
||||||
|
|
||||||
if (size > 65536)
|
if (size > 65536)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
assert_se(sd_event_new(&e) >= 0);
|
/* This triggers client_receive_advertise */
|
||||||
assert_se(sd_dhcp6_client_new(&client) >= 0);
|
fuzz_client(data, size, false);
|
||||||
assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
|
|
||||||
assert_se(sd_dhcp6_client_set_ifindex(client, 42) >= 0);
|
|
||||||
assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
|
|
||||||
dhcp6_client_set_test_mode(client, true);
|
|
||||||
|
|
||||||
/* Used when sending message. */
|
/* This triggers client_receive_reply */
|
||||||
assert_se(sd_dhcp6_client_set_fqdn(client, "example.com") == 1);
|
fuzz_client(data, size, true);
|
||||||
assert_se(sd_dhcp6_client_set_request_mud_url(client, "https://www.example.com/mudfile.json") >= 0);
|
|
||||||
assert_se(sd_dhcp6_client_set_request_user_class(client, STRV_MAKE("u1", "u2", "u3")) >= 0);
|
|
||||||
assert_se(sd_dhcp6_client_set_request_vendor_class(client, STRV_MAKE("v1", "v2", "v3")) >= 0);
|
|
||||||
assert_se(sd_dhcp6_client_set_prefix_delegation_hint(client, 48, &hint) >= 0);
|
|
||||||
assert_se(sd_dhcp6_option_new(123, v1_data, strlen(v1_data), 12345, &v1) >= 0);
|
|
||||||
assert_se(sd_dhcp6_option_new(456, v2_data, strlen(v2_data), 45678, &v2) >= 0);
|
|
||||||
assert_se(sd_dhcp6_client_add_vendor_option(client, v1) >= 0);
|
|
||||||
assert_se(sd_dhcp6_client_add_vendor_option(client, v2) >= 0);
|
|
||||||
|
|
||||||
fuzz_client(client, data, size, DHCP6_STATE_INFORMATION_REQUEST);
|
|
||||||
fuzz_client(client, data, size, DHCP6_STATE_SOLICITATION);
|
|
||||||
|
|
||||||
/* If size is zero, then the resend timer will be triggered at first,
|
|
||||||
* but in the REQUEST state the client must have a lease. */
|
|
||||||
if (size == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
fuzz_client(client, data, size, DHCP6_STATE_REQUEST);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,8 +17,6 @@ sources = files('''
|
|||||||
dhcp6-lease-internal.h
|
dhcp6-lease-internal.h
|
||||||
dhcp6-network.c
|
dhcp6-network.c
|
||||||
dhcp6-option.c
|
dhcp6-option.c
|
||||||
dhcp6-option.h
|
|
||||||
dhcp6-protocol.c
|
|
||||||
dhcp6-protocol.h
|
dhcp6-protocol.h
|
||||||
icmp6-util.c
|
icmp6-util.c
|
||||||
icmp6-util.h
|
icmp6-util.h
|
||||||
@ -115,6 +113,10 @@ fuzzers += [
|
|||||||
[libshared,
|
[libshared,
|
||||||
libsystemd_network]],
|
libsystemd_network]],
|
||||||
|
|
||||||
|
[files('fuzz-dhcp6-client-send.c'),
|
||||||
|
[libshared,
|
||||||
|
libsystemd_network]],
|
||||||
|
|
||||||
[files('fuzz-dhcp-server.c'),
|
[files('fuzz-dhcp-server.c'),
|
||||||
[libsystemd_network,
|
[libsystemd_network,
|
||||||
libshared]],
|
libshared]],
|
||||||
|
|||||||
@ -450,7 +450,7 @@ static int dhcp_client_set_iaid_duid_internal(
|
|||||||
bool iaid_append,
|
bool iaid_append,
|
||||||
bool iaid_set,
|
bool iaid_set,
|
||||||
uint32_t iaid,
|
uint32_t iaid,
|
||||||
DUIDType duid_type,
|
uint16_t duid_type,
|
||||||
const void *duid,
|
const void *duid,
|
||||||
size_t duid_len,
|
size_t duid_len,
|
||||||
usec_t llt_time) {
|
usec_t llt_time) {
|
||||||
@ -489,20 +489,37 @@ static int dhcp_client_set_iaid_duid_internal(
|
|||||||
client->client_id.ns.duid.type = htobe16(duid_type);
|
client->client_id.ns.duid.type = htobe16(duid_type);
|
||||||
memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len);
|
memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len);
|
||||||
len = sizeof(client->client_id.ns.duid.type) + duid_len;
|
len = sizeof(client->client_id.ns.duid.type) + duid_len;
|
||||||
|
} else
|
||||||
|
switch (duid_type) {
|
||||||
|
case DUID_TYPE_LLT:
|
||||||
|
if (client->mac_addr_len == 0)
|
||||||
|
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set.");
|
||||||
|
|
||||||
} else {
|
r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
|
||||||
r = dhcp_identifier_set_duid(duid_type, client->mac_addr, client->mac_addr_len,
|
if (r < 0)
|
||||||
client->arp_type, llt_time, client->test_mode,
|
return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m");
|
||||||
&client->client_id.ns.duid, &len);
|
break;
|
||||||
if (r == -EOPNOTSUPP)
|
case DUID_TYPE_EN:
|
||||||
return log_dhcp_client_errno(client, r,
|
r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
|
||||||
"Failed to set %s. MAC address is not set or "
|
if (r < 0)
|
||||||
"interface type is not supported.",
|
return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m");
|
||||||
duid_type_to_string(duid_type));
|
break;
|
||||||
if (r < 0)
|
case DUID_TYPE_LL:
|
||||||
return log_dhcp_client_errno(client, r, "Failed to set %s: %m",
|
if (client->mac_addr_len == 0)
|
||||||
duid_type_to_string(duid_type));
|
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set.");
|
||||||
}
|
|
||||||
|
r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
|
||||||
|
if (r < 0)
|
||||||
|
return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m");
|
||||||
|
break;
|
||||||
|
case DUID_TYPE_UUID:
|
||||||
|
r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len);
|
||||||
|
if (r < 0)
|
||||||
|
return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type");
|
||||||
|
}
|
||||||
|
|
||||||
client->client_id_len = sizeof(client->client_id.type) + len +
|
client->client_id_len = sizeof(client->client_id.type) + len +
|
||||||
(iaid_append ? sizeof(client->client_id.ns.iaid) : 0);
|
(iaid_append ? sizeof(client->client_id.ns.iaid) : 0);
|
||||||
@ -859,7 +876,7 @@ static int client_message_init(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = dhcp_identifier_set_duid_en(client->test_mode, &client->client_id.ns.duid, &duid_len);
|
r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -6,21 +6,10 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "dhcp6-internal.h"
|
|
||||||
#include "dhcp6-lease-internal.h"
|
#include "dhcp6-lease-internal.h"
|
||||||
|
#include "dhcp6-protocol.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
|
#include "util.h"
|
||||||
#define IRT_DEFAULT (1 * USEC_PER_DAY)
|
|
||||||
#define IRT_MINIMUM (600 * USEC_PER_SEC)
|
|
||||||
|
|
||||||
static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timestamp *timestamp) {
|
|
||||||
assert(lease);
|
|
||||||
|
|
||||||
if (timestamp && triple_timestamp_is_set(timestamp))
|
|
||||||
lease->timestamp = *timestamp;
|
|
||||||
else
|
|
||||||
triple_timestamp_get(&lease->timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
|
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
|
||||||
assert_return(lease, -EINVAL);
|
assert_return(lease, -EINVAL);
|
||||||
@ -35,69 +24,6 @@ int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static usec_t sec2usec(uint32_t sec) {
|
|
||||||
return sec == UINT32_MAX ? USEC_INFINITY : sec * USEC_PER_SEC;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
|
|
||||||
uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX;
|
|
||||||
DHCP6Address *a;
|
|
||||||
|
|
||||||
assert(lease);
|
|
||||||
assert(lease->ia_na || lease->ia_pd);
|
|
||||||
|
|
||||||
if (lease->ia_na) {
|
|
||||||
t1 = MIN(t1, be32toh(lease->ia_na->header.lifetime_t1));
|
|
||||||
t2 = MIN(t2, be32toh(lease->ia_na->header.lifetime_t2));
|
|
||||||
|
|
||||||
LIST_FOREACH(addresses, a, lease->ia_na->addresses)
|
|
||||||
min_valid_lt = MIN(min_valid_lt, be32toh(a->iaaddr.lifetime_valid));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lease->ia_pd) {
|
|
||||||
t1 = MIN(t1, be32toh(lease->ia_pd->header.lifetime_t1));
|
|
||||||
t2 = MIN(t2, be32toh(lease->ia_pd->header.lifetime_t2));
|
|
||||||
|
|
||||||
LIST_FOREACH(addresses, a, lease->ia_pd->addresses)
|
|
||||||
min_valid_lt = MIN(min_valid_lt, be32toh(a->iapdprefix.lifetime_valid));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t2 == 0 || t2 > min_valid_lt) {
|
|
||||||
/* If T2 is zero or longer than the minimum valid lifetime of the addresses or prefixes,
|
|
||||||
* then adjust lifetime with it. */
|
|
||||||
t1 = min_valid_lt / 2;
|
|
||||||
t2 = min_valid_lt / 10 * 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
lease->lifetime_valid = sec2usec(min_valid_lt);
|
|
||||||
lease->lifetime_t1 = sec2usec(t1);
|
|
||||||
lease->lifetime_t2 = sec2usec(t2);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid) {
|
|
||||||
assert(lease);
|
|
||||||
|
|
||||||
if (!lease->ia_na && !lease->ia_pd)
|
|
||||||
return -ENODATA;
|
|
||||||
|
|
||||||
if (ret_t1)
|
|
||||||
*ret_t1 = lease->lifetime_t1;
|
|
||||||
if (ret_t2)
|
|
||||||
*ret_t2 = lease->lifetime_t2;
|
|
||||||
if (ret_valid)
|
|
||||||
*ret_valid = lease->lifetime_valid;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) {
|
|
||||||
assert(lease);
|
|
||||||
|
|
||||||
if (server_address)
|
|
||||||
lease->server_address = *server_address;
|
|
||||||
else
|
|
||||||
lease->server_address = (struct in6_addr) {};
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
|
int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
|
||||||
assert_return(lease, -EINVAL);
|
assert_return(lease, -EINVAL);
|
||||||
assert_return(ret, -EINVAL);
|
assert_return(ret, -EINVAL);
|
||||||
@ -106,37 +32,55 @@ int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *re
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dhcp6_ia_clear_addresses(DHCP6IA *ia) {
|
int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
|
||||||
DHCP6Address *a, *n;
|
DHCP6Address *addr;
|
||||||
|
uint32_t valid = 0, t;
|
||||||
|
|
||||||
assert(ia);
|
assert_return(ia, -EINVAL);
|
||||||
|
assert_return(expire, -EINVAL);
|
||||||
|
|
||||||
LIST_FOREACH_SAFE(addresses, a, n, ia->addresses)
|
LIST_FOREACH(addresses, addr, ia->addresses) {
|
||||||
free(a);
|
t = be32toh(addr->iaaddr.lifetime_valid);
|
||||||
|
if (valid < t)
|
||||||
|
valid = t;
|
||||||
|
}
|
||||||
|
|
||||||
ia->addresses = NULL;
|
t = be32toh(ia->ia_na.lifetime_t2);
|
||||||
|
if (t > valid)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
*expire = valid - t;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DHCP6IA *dhcp6_ia_free(DHCP6IA *ia) {
|
DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) {
|
||||||
|
DHCP6Address *address;
|
||||||
|
|
||||||
if (!ia)
|
if (!ia)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
dhcp6_ia_clear_addresses(ia);
|
while (ia->addresses) {
|
||||||
|
address = ia->addresses;
|
||||||
|
|
||||||
return mfree(ia);
|
LIST_REMOVE(addresses, ia->addresses, address);
|
||||||
|
|
||||||
|
free(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
|
int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
|
||||||
uint8_t *clientid = NULL;
|
uint8_t *clientid;
|
||||||
|
|
||||||
assert(lease);
|
assert_return(lease, -EINVAL);
|
||||||
assert(id || len == 0);
|
assert_return(id, -EINVAL);
|
||||||
|
assert_return(len > 0, -EINVAL);
|
||||||
|
|
||||||
if (len > 0) {
|
clientid = memdup(id, len);
|
||||||
clientid = memdup(id, len);
|
if (!clientid)
|
||||||
if (!clientid)
|
return -ENOMEM;
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
free_and_replace(lease->clientid, clientid);
|
free_and_replace(lease->clientid, clientid);
|
||||||
lease->clientid_len = len;
|
lease->clientid_len = len;
|
||||||
@ -145,7 +89,7 @@ int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t le
|
|||||||
}
|
}
|
||||||
|
|
||||||
int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
|
int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
|
||||||
assert(lease);
|
assert_return(lease, -EINVAL);
|
||||||
|
|
||||||
if (!lease->clientid)
|
if (!lease->clientid)
|
||||||
return -ENODATA;
|
return -ENODATA;
|
||||||
@ -159,16 +103,15 @@ int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re
|
|||||||
}
|
}
|
||||||
|
|
||||||
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
|
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
|
||||||
uint8_t *serverid = NULL;
|
uint8_t *serverid;
|
||||||
|
|
||||||
assert(lease);
|
assert_return(lease, -EINVAL);
|
||||||
assert(id || len == 0);
|
assert_return(id, -EINVAL);
|
||||||
|
assert_return(len > 0, -EINVAL);
|
||||||
|
|
||||||
if (len > 0) {
|
serverid = memdup(id, len);
|
||||||
serverid = memdup(id, len);
|
if (!serverid)
|
||||||
if (!serverid)
|
return -ENOMEM;
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
free_and_replace(lease->serverid, serverid);
|
free_and_replace(lease->serverid, serverid);
|
||||||
lease->serverid_len = len;
|
lease->serverid_len = len;
|
||||||
@ -177,7 +120,7 @@ int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t le
|
|||||||
}
|
}
|
||||||
|
|
||||||
int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
|
int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
|
||||||
assert(lease);
|
assert_return(lease, -EINVAL);
|
||||||
|
|
||||||
if (!lease->serverid)
|
if (!lease->serverid)
|
||||||
return -ENODATA;
|
return -ENODATA;
|
||||||
@ -186,99 +129,107 @@ int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re
|
|||||||
*ret_id = lease->serverid;
|
*ret_id = lease->serverid;
|
||||||
if (ret_len)
|
if (ret_len)
|
||||||
*ret_len = lease->serverid_len;
|
*ret_len = lease->serverid_len;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) {
|
int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) {
|
||||||
assert(lease);
|
assert_return(lease, -EINVAL);
|
||||||
|
|
||||||
lease->preference = preference;
|
lease->preference = preference;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret) {
|
int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) {
|
||||||
assert(lease);
|
assert_return(preference, -EINVAL);
|
||||||
assert(ret);
|
|
||||||
|
if (!lease)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
*preference = lease->preference;
|
||||||
|
|
||||||
*ret = lease->preference;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) {
|
int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) {
|
||||||
assert(lease);
|
|
||||||
|
|
||||||
lease->rapid_commit = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) {
|
|
||||||
assert(lease);
|
|
||||||
assert(ret);
|
|
||||||
|
|
||||||
*ret = lease->rapid_commit;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sd_dhcp6_lease_get_address(
|
|
||||||
sd_dhcp6_lease *lease,
|
|
||||||
struct in6_addr *ret_addr,
|
|
||||||
uint32_t *ret_lifetime_preferred,
|
|
||||||
uint32_t *ret_lifetime_valid) {
|
|
||||||
|
|
||||||
assert_return(lease, -EINVAL);
|
assert_return(lease, -EINVAL);
|
||||||
|
|
||||||
if (!lease->addr_iter)
|
lease->rapid_commit = true;
|
||||||
return -ENODATA;
|
|
||||||
|
|
||||||
if (ret_addr)
|
return 0;
|
||||||
*ret_addr = lease->addr_iter->iaaddr.address;
|
}
|
||||||
if (ret_lifetime_preferred)
|
|
||||||
*ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred);
|
int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit) {
|
||||||
if (ret_lifetime_valid)
|
assert_return(lease, -EINVAL);
|
||||||
*ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid);
|
assert_return(rapid_commit, -EINVAL);
|
||||||
|
|
||||||
|
*rapid_commit = lease->rapid_commit;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr,
|
||||||
|
uint32_t *lifetime_preferred,
|
||||||
|
uint32_t *lifetime_valid) {
|
||||||
|
assert_return(lease, -EINVAL);
|
||||||
|
assert_return(addr, -EINVAL);
|
||||||
|
assert_return(lifetime_preferred, -EINVAL);
|
||||||
|
assert_return(lifetime_valid, -EINVAL);
|
||||||
|
|
||||||
|
if (!lease->addr_iter)
|
||||||
|
return -ENOMSG;
|
||||||
|
|
||||||
|
memcpy(addr, &lease->addr_iter->iaaddr.address,
|
||||||
|
sizeof(struct in6_addr));
|
||||||
|
*lifetime_preferred =
|
||||||
|
be32toh(lease->addr_iter->iaaddr.lifetime_preferred);
|
||||||
|
*lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid);
|
||||||
|
|
||||||
lease->addr_iter = lease->addr_iter->addresses_next;
|
lease->addr_iter = lease->addr_iter->addresses_next;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
|
void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
|
||||||
if (lease)
|
if (lease)
|
||||||
lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
|
lease->addr_iter = lease->ia.addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_dhcp6_lease_get_pd(
|
int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
|
||||||
sd_dhcp6_lease *lease,
|
uint8_t *prefix_len,
|
||||||
struct in6_addr *ret_prefix,
|
uint32_t *lifetime_preferred,
|
||||||
uint8_t *ret_prefix_len,
|
uint32_t *lifetime_valid) {
|
||||||
uint32_t *ret_lifetime_preferred,
|
|
||||||
uint32_t *ret_lifetime_valid) {
|
|
||||||
|
|
||||||
assert_return(lease, -EINVAL);
|
assert_return(lease, -EINVAL);
|
||||||
|
assert_return(prefix, -EINVAL);
|
||||||
|
assert_return(prefix_len, -EINVAL);
|
||||||
|
assert_return(lifetime_preferred, -EINVAL);
|
||||||
|
assert_return(lifetime_valid, -EINVAL);
|
||||||
|
|
||||||
if (!lease->prefix_iter)
|
if (!lease->prefix_iter)
|
||||||
return -ENODATA;
|
return -ENOMSG;
|
||||||
|
|
||||||
if (ret_prefix)
|
memcpy(prefix, &lease->prefix_iter->iapdprefix.address,
|
||||||
*ret_prefix = lease->prefix_iter->iapdprefix.address;
|
sizeof(struct in6_addr));
|
||||||
if (ret_prefix_len)
|
*prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
|
||||||
*ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
|
*lifetime_preferred =
|
||||||
if (ret_lifetime_preferred)
|
be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
|
||||||
*ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
|
*lifetime_valid =
|
||||||
if (ret_lifetime_valid)
|
be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
|
||||||
*ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
|
|
||||||
|
|
||||||
lease->prefix_iter = lease->prefix_iter->addresses_next;
|
lease->prefix_iter = lease->prefix_iter->addresses_next;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
|
void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
|
||||||
if (lease)
|
if (lease)
|
||||||
lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
|
lease->prefix_iter = lease->pd.addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
assert(lease);
|
assert_return(lease, -EINVAL);
|
||||||
assert(optval || optlen == 0);
|
assert_return(optval, -EINVAL);
|
||||||
|
|
||||||
if (optlen == 0)
|
if (optlen == 0)
|
||||||
return 0;
|
return 0;
|
||||||
@ -290,7 +241,7 @@ int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
|
|||||||
assert_return(lease, -EINVAL);
|
assert_return(lease, -EINVAL);
|
||||||
|
|
||||||
if (!lease->dns)
|
if (!lease->dns)
|
||||||
return -ENODATA;
|
return -ENOENT;
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
*ret = lease->dns;
|
*ret = lease->dns;
|
||||||
@ -302,8 +253,8 @@ int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t
|
|||||||
_cleanup_strv_free_ char **domains = NULL;
|
_cleanup_strv_free_ char **domains = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(lease);
|
assert_return(lease, -EINVAL);
|
||||||
assert(optval || optlen == 0);
|
assert_return(optval, -EINVAL);
|
||||||
|
|
||||||
if (optlen == 0)
|
if (optlen == 0)
|
||||||
return 0;
|
return 0;
|
||||||
@ -320,7 +271,7 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
|
|||||||
assert_return(ret, -EINVAL);
|
assert_return(ret, -EINVAL);
|
||||||
|
|
||||||
if (!lease->domains)
|
if (!lease->domains)
|
||||||
return -ENODATA;
|
return -ENOENT;
|
||||||
|
|
||||||
*ret = lease->domains;
|
*ret = lease->domains;
|
||||||
return strv_length(lease->domains);
|
return strv_length(lease->domains);
|
||||||
@ -329,8 +280,8 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
|
|||||||
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 r;
|
int r;
|
||||||
|
|
||||||
assert(lease);
|
assert_return(lease, -EINVAL);
|
||||||
assert(optval || optlen == 0);
|
assert_return(optval, -EINVAL);
|
||||||
|
|
||||||
for (size_t offset = 0; offset < optlen;) {
|
for (size_t offset = 0; offset < optlen;) {
|
||||||
const uint8_t *subval;
|
const uint8_t *subval;
|
||||||
@ -345,7 +296,7 @@ int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt
|
|||||||
case DHCP6_NTP_SUBOPTION_SRV_ADDR:
|
case DHCP6_NTP_SUBOPTION_SRV_ADDR:
|
||||||
case DHCP6_NTP_SUBOPTION_MC_ADDR:
|
case DHCP6_NTP_SUBOPTION_MC_ADDR:
|
||||||
if (sublen != 16)
|
if (sublen != 16)
|
||||||
return -EINVAL;
|
return 0;
|
||||||
|
|
||||||
r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
|
r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -375,8 +326,8 @@ int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
assert(lease);
|
assert_return(lease, -EINVAL);
|
||||||
assert(optval || optlen == 0);
|
assert_return(optval, -EINVAL);
|
||||||
|
|
||||||
if (optlen == 0)
|
if (optlen == 0)
|
||||||
return 0;
|
return 0;
|
||||||
@ -401,14 +352,14 @@ int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **
|
|||||||
return lease->sntp_count;
|
return lease->sntp_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -ENODATA;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
if (!lease->ntp_fqdn)
|
if (!lease->ntp_fqdn)
|
||||||
return -ENODATA;
|
return -ENOENT;
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
*ret = lease->ntp_fqdn;
|
*ret = lease->ntp_fqdn;
|
||||||
@ -416,14 +367,11 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
char *fqdn;
|
|
||||||
int r;
|
int r;
|
||||||
|
char *fqdn;
|
||||||
|
|
||||||
assert(lease);
|
assert_return(lease, -EINVAL);
|
||||||
assert(optval || optlen == 0);
|
assert_return(optval, -EINVAL);
|
||||||
|
|
||||||
if (optlen == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (optlen < 2)
|
if (optlen < 2)
|
||||||
return -ENODATA;
|
return -ENODATA;
|
||||||
@ -442,222 +390,20 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
|
|||||||
assert_return(ret, -EINVAL);
|
assert_return(ret, -EINVAL);
|
||||||
|
|
||||||
if (!lease->fqdn)
|
if (!lease->fqdn)
|
||||||
return -ENODATA;
|
return -ENOENT;
|
||||||
|
|
||||||
*ret = lease->fqdn;
|
*ret = lease->fqdn;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dhcp6_lease_parse_message(
|
|
||||||
sd_dhcp6_client *client,
|
|
||||||
sd_dhcp6_lease *lease,
|
|
||||||
const DHCP6Message *message,
|
|
||||||
size_t len) {
|
|
||||||
|
|
||||||
usec_t irt = IRT_DEFAULT;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(client);
|
|
||||||
assert(lease);
|
|
||||||
assert(message);
|
|
||||||
assert(len >= sizeof(DHCP6Message));
|
|
||||||
|
|
||||||
len -= sizeof(DHCP6Message);
|
|
||||||
for (size_t offset = 0; offset < len;) {
|
|
||||||
uint16_t optcode;
|
|
||||||
size_t optlen;
|
|
||||||
const uint8_t *optval;
|
|
||||||
|
|
||||||
r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
switch (optcode) {
|
|
||||||
case SD_DHCP6_OPTION_CLIENTID:
|
|
||||||
if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0)
|
|
||||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs",
|
|
||||||
dhcp6_message_type_to_string(message->type));
|
|
||||||
|
|
||||||
r = dhcp6_lease_set_clientid(lease, optval, optlen);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_DHCP6_OPTION_SERVERID:
|
|
||||||
if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0)
|
|
||||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs",
|
|
||||||
dhcp6_message_type_to_string(message->type));
|
|
||||||
|
|
||||||
r = dhcp6_lease_set_serverid(lease, optval, optlen);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_DHCP6_OPTION_PREFERENCE:
|
|
||||||
if (optlen != 1)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
r = dhcp6_lease_set_preference(lease, optval[0]);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_DHCP6_OPTION_STATUS_CODE: {
|
|
||||||
_cleanup_free_ char *msg = NULL;
|
|
||||||
|
|
||||||
r = dhcp6_option_parse_status(optval, optlen, &msg);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (r > 0)
|
|
||||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
|
||||||
"Received %s message with non-zero status: %s%s%s",
|
|
||||||
dhcp6_message_type_to_string(message->type),
|
|
||||||
strempty(msg), isempty(msg) ? "" : ": ",
|
|
||||||
dhcp6_message_status_to_string(r));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SD_DHCP6_OPTION_IA_NA: {
|
|
||||||
_cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
|
|
||||||
|
|
||||||
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
|
||||||
log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia);
|
|
||||||
if (r == -ENOMEM)
|
|
||||||
return r;
|
|
||||||
if (r < 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (lease->ia_na) {
|
|
||||||
log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
dhcp6_ia_free(lease->ia_na);
|
|
||||||
lease->ia_na = TAKE_PTR(ia);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SD_DHCP6_OPTION_IA_PD: {
|
|
||||||
_cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
|
|
||||||
|
|
||||||
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
|
||||||
log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia);
|
|
||||||
if (r == -ENOMEM)
|
|
||||||
return r;
|
|
||||||
if (r < 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (lease->ia_pd) {
|
|
||||||
log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
dhcp6_ia_free(lease->ia_pd);
|
|
||||||
lease->ia_pd = TAKE_PTR(ia);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SD_DHCP6_OPTION_RAPID_COMMIT:
|
|
||||||
r = dhcp6_lease_set_rapid_commit(lease);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_DHCP6_OPTION_DNS_SERVERS:
|
|
||||||
r = dhcp6_lease_add_dns(lease, optval, optlen);
|
|
||||||
if (r < 0)
|
|
||||||
log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m");
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_DHCP6_OPTION_DOMAIN_LIST:
|
|
||||||
r = dhcp6_lease_add_domains(lease, optval, optlen);
|
|
||||||
if (r < 0)
|
|
||||||
log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m");
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_DHCP6_OPTION_NTP_SERVER:
|
|
||||||
r = dhcp6_lease_add_ntp(lease, optval, optlen);
|
|
||||||
if (r < 0)
|
|
||||||
log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m");
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_DHCP6_OPTION_SNTP_SERVERS:
|
|
||||||
r = dhcp6_lease_add_sntp(lease, optval, optlen);
|
|
||||||
if (r < 0)
|
|
||||||
log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m");
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_DHCP6_OPTION_CLIENT_FQDN:
|
|
||||||
r = dhcp6_lease_set_fqdn(lease, optval, optlen);
|
|
||||||
if (r < 0)
|
|
||||||
log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m");
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
|
|
||||||
if (optlen != 4)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *clientid;
|
|
||||||
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 (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_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));
|
|
||||||
|
|
||||||
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
|
||||||
client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
|
|
||||||
log_dhcp6_client(client, "New information request will be refused in %s.",
|
|
||||||
FORMAT_TIMESPAN(client->information_refresh_time_usec, USEC_PER_SEC));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
r = dhcp6_lease_get_serverid(lease, NULL, NULL);
|
|
||||||
if (r < 0)
|
|
||||||
return log_dhcp6_client_errno(client, r, "%s has no server id",
|
|
||||||
dhcp6_message_type_to_string(message->type));
|
|
||||||
|
|
||||||
if (!lease->ia_na && !lease->ia_pd)
|
|
||||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
|
||||||
"No IA_PD prefix or IA_NA address received. Ignoring.");
|
|
||||||
|
|
||||||
dhcp6_lease_set_lifetime(lease);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
|
static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
|
||||||
if (!lease)
|
if (!lease)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
free(lease->clientid);
|
free(lease->clientid);
|
||||||
free(lease->serverid);
|
free(lease->serverid);
|
||||||
dhcp6_ia_free(lease->ia_na);
|
dhcp6_lease_free_ia(&lease->ia);
|
||||||
dhcp6_ia_free(lease->ia_pd);
|
dhcp6_lease_free_ia(&lease->pd);
|
||||||
free(lease->dns);
|
free(lease->dns);
|
||||||
free(lease->fqdn);
|
free(lease->fqdn);
|
||||||
strv_free(lease->domains);
|
strv_free(lease->domains);
|
||||||
@ -673,47 +419,14 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free);
|
|||||||
int dhcp6_lease_new(sd_dhcp6_lease **ret) {
|
int dhcp6_lease_new(sd_dhcp6_lease **ret) {
|
||||||
sd_dhcp6_lease *lease;
|
sd_dhcp6_lease *lease;
|
||||||
|
|
||||||
assert(ret);
|
lease = new0(sd_dhcp6_lease, 1);
|
||||||
|
|
||||||
lease = new(sd_dhcp6_lease, 1);
|
|
||||||
if (!lease)
|
if (!lease)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
*lease = (sd_dhcp6_lease) {
|
lease->n_ref = 1;
|
||||||
.n_ref = 1,
|
|
||||||
};
|
LIST_HEAD_INIT(lease->ia.addresses);
|
||||||
|
|
||||||
*ret = lease;
|
*ret = lease;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dhcp6_lease_new_from_message(
|
|
||||||
sd_dhcp6_client *client,
|
|
||||||
const DHCP6Message *message,
|
|
||||||
size_t len,
|
|
||||||
const triple_timestamp *timestamp,
|
|
||||||
const struct in6_addr *server_address,
|
|
||||||
sd_dhcp6_lease **ret) {
|
|
||||||
|
|
||||||
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(client);
|
|
||||||
assert(message);
|
|
||||||
assert(len >= sizeof(DHCP6Message));
|
|
||||||
assert(ret);
|
|
||||||
|
|
||||||
r = dhcp6_lease_new(&lease);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
dhcp6_lease_set_timestamp(lease, timestamp);
|
|
||||||
dhcp6_lease_set_server_address(lease, server_address);
|
|
||||||
|
|
||||||
r = dhcp6_lease_parse_message(client, lease, message, len);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
*ret = TAKE_PTR(lease);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -170,7 +170,7 @@ static int check_options(uint8_t code, uint8_t len, const void *option, void *us
|
|||||||
struct duid duid;
|
struct duid duid;
|
||||||
size_t duid_len;
|
size_t duid_len;
|
||||||
|
|
||||||
assert_se(dhcp_identifier_set_duid_en(/* test_mode = */ true, &duid, &duid_len) >= 0);
|
assert_se(dhcp_identifier_set_duid_en(&duid, &duid_len) >= 0);
|
||||||
assert_se(dhcp_identifier_set_iaid(42, mac_addr, ETH_ALEN, true, /* use_mac = */ true, &iaid) >= 0);
|
assert_se(dhcp_identifier_set_iaid(42, mac_addr, ETH_ALEN, true, /* use_mac = */ true, &iaid) >= 0);
|
||||||
|
|
||||||
assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len);
|
assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -432,20 +432,8 @@ int dhcp6_start_on_ra(Link *link, bool information_request) {
|
|||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (inf_req == information_request)
|
if (inf_req == information_request)
|
||||||
/* The client is already running in the requested mode. */
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!inf_req) {
|
|
||||||
log_link_debug(link,
|
|
||||||
"The DHCPv6 client is already running in the managed mode, "
|
|
||||||
"refusing to start the client in the information requesting mode.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_link_debug(link,
|
|
||||||
"The DHCPv6 client is running in the information requesting mode. "
|
|
||||||
"Restarting the client in the managed mode.");
|
|
||||||
|
|
||||||
r = sd_dhcp6_client_stop(link->dhcp6_client);
|
r = sd_dhcp6_client_stop(link->dhcp6_client);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
@ -122,24 +122,15 @@
|
|||||||
#define GPT_USR_X86_64_VERITY_SIG SD_ID128_MAKE(e7,bb,33,fb,06,cf,4e,81,82,73,e5,43,b4,13,e2,e2)
|
#define GPT_USR_X86_64_VERITY_SIG SD_ID128_MAKE(e7,bb,33,fb,06,cf,4e,81,82,73,e5,43,b4,13,e2,e2)
|
||||||
#define GPT_USR_X86_VERITY_SIG SD_ID128_MAKE(97,4a,71,c0,de,41,43,c3,be,5d,5c,5c,cd,1a,d2,c0)
|
#define GPT_USR_X86_VERITY_SIG SD_ID128_MAKE(97,4a,71,c0,de,41,43,c3,be,5d,5c,5c,cd,1a,d2,c0)
|
||||||
|
|
||||||
#define GPT_ESP SD_ID128_MAKE(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b)
|
#define GPT_ESP SD_ID128_MAKE(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b)
|
||||||
#define GPT_ESP_STR SD_ID128_MAKE_UUID_STR(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b)
|
#define GPT_XBOOTLDR SD_ID128_MAKE(bc,13,c2,ff,59,e6,42,62,a3,52,b2,75,fd,6f,71,72)
|
||||||
#define GPT_XBOOTLDR SD_ID128_MAKE(bc,13,c2,ff,59,e6,42,62,a3,52,b2,75,fd,6f,71,72)
|
#define GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
|
||||||
#define GPT_XBOOTLDR_STR SD_ID128_MAKE_UUID_STR(bc,13,c2,ff,59,e6,42,62,a3,52,b2,75,fd,6f,71,72)
|
#define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
|
||||||
#define GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
|
#define GPT_SRV SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8)
|
||||||
#define GPT_SWAP_STR SD_ID128_MAKE_UUID_STR(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
|
#define GPT_VAR SD_ID128_MAKE(4d,21,b0,16,b5,34,45,c2,a9,fb,5c,16,e0,91,fd,2d)
|
||||||
#define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
|
#define GPT_TMP SD_ID128_MAKE(7e,c6,f5,57,3b,c5,4a,ca,b2,93,16,ef,5d,f6,39,d1)
|
||||||
#define GPT_HOME_STR SD_ID128_MAKE_UUID_STR(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
|
#define GPT_USER_HOME SD_ID128_MAKE(77,3f,91,ef,66,d4,49,b5,bd,83,d6,83,bf,40,ad,16)
|
||||||
#define GPT_SRV SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8)
|
#define GPT_LINUX_GENERIC SD_ID128_MAKE(0f,c6,3d,af,84,83,47,72,8e,79,3d,69,d8,47,7d,e4)
|
||||||
#define GPT_SRV_STR SD_ID128_MAKE_UUID_STR(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8)
|
|
||||||
#define GPT_VAR SD_ID128_MAKE(4d,21,b0,16,b5,34,45,c2,a9,fb,5c,16,e0,91,fd,2d)
|
|
||||||
#define GPT_VAR_STR SD_ID128_MAKE_UUID_STR(4d,21,b0,16,b5,34,45,c2,a9,fb,5c,16,e0,91,fd,2d)
|
|
||||||
#define GPT_TMP SD_ID128_MAKE(7e,c6,f5,57,3b,c5,4a,ca,b2,93,16,ef,5d,f6,39,d1)
|
|
||||||
#define GPT_TMP_STR SD_ID128_MAKE_UUID_STR(7e,c6,f5,57,3b,c5,4a,ca,b2,93,16,ef,5d,f6,39,d1)
|
|
||||||
#define GPT_USER_HOME SD_ID128_MAKE(77,3f,91,ef,66,d4,49,b5,bd,83,d6,83,bf,40,ad,16)
|
|
||||||
#define GPT_USER_HOME_STR SD_ID128_MAKE_UUID_STR(77,3f,91,ef,66,d4,49,b5,bd,83,d6,83,bf,40,ad,16)
|
|
||||||
#define GPT_LINUX_GENERIC SD_ID128_MAKE(0f,c6,3d,af,84,83,47,72,8e,79,3d,69,d8,47,7d,e4)
|
|
||||||
#define GPT_LINUX_GENERIC_STR SD_ID128_MAKE_UUID_STR(0f,c6,3d,af,84,83,47,72,8e,79,3d,69,d8,47,7d,e4)
|
|
||||||
|
|
||||||
/* Maintain same order as above */
|
/* Maintain same order as above */
|
||||||
#if defined(__alpha__)
|
#if defined(__alpha__)
|
||||||
|
|||||||
@ -2142,7 +2142,7 @@ int show(int argc, char *argv[], void *userdata) {
|
|||||||
|
|
||||||
show_mode = systemctl_show_mode_from_string(argv[0]);
|
show_mode = systemctl_show_mode_from_string(argv[0]);
|
||||||
if (show_mode < 0)
|
if (show_mode < 0)
|
||||||
return log_error_errno(show_mode, "Invalid argument '%s'.", argv[0]);
|
return log_error_errno(show_mode, "Invalid argument.");
|
||||||
|
|
||||||
if (show_mode == SYSTEMCTL_SHOW_HELP && argc <= 1)
|
if (show_mode == SYSTEMCTL_SHOW_HELP && argc <= 1)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
|||||||
@ -251,7 +251,7 @@ int sd_dhcp6_client_set_request_vendor_class(
|
|||||||
int sd_dhcp6_client_set_prefix_delegation_hint(
|
int sd_dhcp6_client_set_prefix_delegation_hint(
|
||||||
sd_dhcp6_client *client,
|
sd_dhcp6_client *client,
|
||||||
uint8_t prefixlen,
|
uint8_t prefixlen,
|
||||||
const struct in6_addr *pd_prefix);
|
const struct in6_addr *pd_address);
|
||||||
int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client,
|
int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client,
|
||||||
int *delegation);
|
int *delegation);
|
||||||
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client,
|
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client,
|
||||||
@ -260,6 +260,8 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client,
|
|||||||
int *request);
|
int *request);
|
||||||
int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
|
int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
|
||||||
int request);
|
int request);
|
||||||
|
int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client,
|
||||||
|
uint32_t transaction_id);
|
||||||
int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
|
int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
|
||||||
sd_dhcp6_option *v);
|
sd_dhcp6_option *v);
|
||||||
|
|
||||||
|
|||||||
@ -5933,4 +5933,5 @@ if __name__ == '__main__':
|
|||||||
env.update({ 'UBSAN_OPTIONS' : ubsan_options })
|
env.update({ 'UBSAN_OPTIONS' : ubsan_options })
|
||||||
|
|
||||||
sys.argv[1:] = unknown_args
|
sys.argv[1:] = unknown_args
|
||||||
unittest.main(verbosity=3)
|
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,
|
||||||
|
verbosity=3))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user