1
0
mirror of https://github.com/systemd/systemd synced 2026-04-21 06:24:49 +02:00

Compare commits

..

76 Commits

Author SHA1 Message Date
Max Gautier
f55fe53ffc docs: Correct WantedBy= regarding template units 2022-02-15 16:54:11 +01:00
Daan De Meyer
0765763e51 test: Stop using TextTestRunner in systemd-networkd-tests.py
If we assign our own test runner, passing arguments stops working
as unittest won't instantiate its own test runner after it parses
the arguments from sys.argv.

Consequence is that the tests will write to stderr now instead of
stdout since it doesn't seem possible to configure the stream that
unittest.main() will instantiate its test runner with so it'll
default to sys.stderr.
2022-02-15 23:32:06 +09:00
Lennart Poettering
6783f31c9c
Merge pull request #22516 from keszybz/bit-and-pieces
Post-review fixups and other tiny improvements
2022-02-15 15:20:16 +01:00
Ludwig Nussel
4e076fc885 Revert "man: fix description of --force in halt(8) (#7392)"
This reverts commit 5d9adb5b60b815b477ba9e6b19ef0fd7e1854a38.
2022-02-15 14:58:03 +01:00
Lennart Poettering
ec00453b1c
Merge pull request #22412 from yuwata/sd-dhcp6-client-cleanups
sd-dhcp6-client: several fixes and cleanups
2022-02-15 14:18:55 +01:00
Zbigniew Jędrzejewski-Szmek
073c0e20ef homework: avoid runtime formatting of a fixed string
Follow-up for fe9bd5ad3670f6a34f9ea9b4e2c16bec6000ce11.
I checked that the formatted string has the same dashes as
before fe9bd5ad3670f6a34f9ea9b4e2c16bec6000ce11.
2022-02-15 10:50:43 +01:00
Zbigniew Jędrzejewski-Szmek
2d5c770d5c share/gpt: add stringified defines for GPT partition types 2022-02-15 10:49:41 +01:00
Zbigniew Jędrzejewski-Szmek
8adba77294 NEWS: minor grammar adjustment
Follow-up for e41dcb822167116c8924e56b39640536df894bbd.
2022-02-15 10:30:35 +01:00
Zbigniew Jędrzejewski-Szmek
71fae19e3f systemctl: expand error message 2022-02-15 10:26:58 +01:00
Zbigniew Jędrzejewski-Szmek
d33575eb4a docs: more internal links 2022-02-15 10:26:58 +01:00
Yu Watanabe
119c00c1fa test-dhcp6-client: add test for rapid commit 2022-02-14 15:09:37 +09:00
Yu Watanabe
1f28019113 test-dhcp6-client: cleanups
This does many cleanups. Most prominently,
- check the sents packet more strictly,
- make the packets used in the test more readable,
- unify callbacks.
2022-02-14 15:09:28 +09:00
Yu Watanabe
bccb8fc61a fuzz-dhcp6-client: add prefix hint and vendor option to sent message 2022-02-14 15:02:33 +09:00
Yu Watanabe
7b53d3ead3 fuzz-dhcp6-client: merge with fuzz-dhcp6-client-send 2022-02-14 15:02:33 +09:00
Yu Watanabe
013c6904fa fuzz-dhcp6-client: test multiple states
Then, now all three paths client_process_information(),
client_process_reply(), and client_process_advertise_or_rapid_commit_reply()
in client_receive_message() are fuzzed.
2022-02-14 15:02:33 +09:00
Yu Watanabe
bbe3f62a16 sd-dhcp6-client: append extra options before elapsed time option
The value of elapsed time option is determined in runtime and not
deterministic. It is hard to test the sent packets if it is located
in the intermidiate of the packet.
2022-02-14 15:02:33 +09:00
Yu Watanabe
e261d31557 sd-dhcp6-client: logs invalid NTP option
Returning negative errno triggers to produce debugging log in
dhcp6_lease_parse_message().
2022-02-14 15:02:33 +09:00
Yu Watanabe
3f09d563f4 sd-dhcp6-client: split dhcp6-internal.h into two
Also, this moves string tables to dhcp6-protocol.c.
2022-02-14 15:02:33 +09:00
Yu Watanabe
8b1cfab962 network: do not restart DHCPv6 client when it is already running in managed mode
Otherwise, if a network has two router and one provides M flag and the
other provides O flag, then the DHCPv6 client switches the running mode
repeatedly. That makes the IPv6 network configuration of the host
unstable.
2022-02-14 15:02:33 +09:00
Yu Watanabe
dd73db7850 sd-dhcp6-client: reset Information Refresh Time on stop 2022-02-14 15:02:30 +09:00
Yu Watanabe
e1774086a7 sd-dhcp6-client: add log about Information Refresh Time
And store IRT only when the client is running in information
requesting mode.
2022-02-14 15:01:02 +09:00
Yu Watanabe
9c9fee8002 sd-dhcp6-client: use memcmp_nn() at one more place 2022-02-14 15:01:02 +09:00
Yu Watanabe
ac680f766d dhcp-identifier: generate static and constant DUID-EN when the client is running in test mode
Follow-up for 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193.
2022-02-14 15:00:59 +09:00
Yu Watanabe
5e1618fafa dhcp-identifier: introduce dhcp_identifier_set_duid() 2022-02-14 14:58:49 +09:00
Yu Watanabe
f9971018eb dhcp-identifier: introduce duid_type_to_string() 2022-02-14 14:58:49 +09:00
Yu Watanabe
6f3fc86131 sd-dhcp6-client: do not expose set_transaction_id()
This is mostly for tests or fuzzers. Hence, this makes the function
requires that the client is running in the test mode.

Also, now the function mask the value for message type.
2022-02-14 14:58:49 +09:00
Yu Watanabe
9e4dee4cec sd-dhcp6-client: use OrderedSet for vendor option
This also fixes memleak.
2022-02-14 14:58:49 +09:00
Yu Watanabe
885ea2ab1c ordered-set: introduce ordered_set_clear() 2022-02-14 14:58:49 +09:00
Yu Watanabe
1978efb9a5 sd-dhcp6-client: use struct hw_addr_data 2022-02-14 14:58:49 +09:00
Yu Watanabe
6f8ff34218 sd-dhcp6-client: stop IO event source when client entered bound state
Also when the client receives a reply when running in information
requesting mode.
2022-02-14 14:58:49 +09:00
Yu Watanabe
6f4490bb0c sd-dhcp6-client: adjust assertions 2022-02-14 14:58:46 +09:00
Yu Watanabe
65b85f2340 sd-dhcp6-client: log state transition 2022-02-14 14:57:35 +09:00
Yu Watanabe
e5d69be216 sd-dhcp6-client: rename client_set_state() -> client_start_transaction()
Previously, it obeys state transition, but now it is only used when
(re-)starting transaction. Let's rename again in more explanative name.
2022-02-14 14:57:35 +09:00
Yu Watanabe
fa78d165bb sd-dhcp6-client: use proper type for string table lookup funcs 2022-02-14 14:57:35 +09:00
Yu Watanabe
a8d1012f30 sd-dhcp6-client: voidify client_set_state() 2022-02-14 14:57:34 +09:00
Yu Watanabe
cfcc85bbca sd-dhcp6-client: log message is processed before state is changed 2022-02-14 14:57:34 +09:00
Yu Watanabe
c50c9e509f sd-dhcp6-client: move client_timeout_resend() and client_set_state()
These are deeply related to client_message_send(). Hence, move them near
the function.
2022-02-14 14:57:34 +09:00
Yu Watanabe
0eca25ba00 sd-dhcp6-client: move client_ensure_iaid()
No functional change.
2022-02-14 14:57:34 +09:00
Yu Watanabe
af2b484141 sd-dhcp6-client: merge client_start() and client_reset() 2022-02-14 14:56:54 +09:00
Yu Watanabe
ec7baf998d sd-dhcp6-client: use event_reset_time_relative() at one more place 2022-02-14 14:54:08 +09:00
Yu Watanabe
22ad018b5e sd-dhcp6-client: max_retransmit_count is only used when client is in DHCP6_STATE_REQUEST 2022-02-14 14:54:08 +09:00
Yu Watanabe
0f3b8b809c sd-dhcp6-client: drop unnecessary event_source_disable()
The event source will be soon re-enabled in the same function.

The function client_timeout_resend() may return earlier without
re-enabling the timer souce. However,
- the timer event source is one shot by default. Hence, it is not
  necessary to disable in the callback function,
- when it returns early, then client_set_state() or client_stop() is
  called before return, and they re-ernable or disable the timer.
2022-02-14 14:51:19 +09:00
Yu Watanabe
3bb18e707c sd-dhcp6-client: set lease expiration timer in client_enter_bound_state()
The timer must be active until the client get re-enter bound state,
and the timeout must be determined by the lease acquired when entering
the bound state.
2022-02-14 14:45:14 +09:00
Yu Watanabe
30b31359b0 sd-dhcp6-client: drop unnecessary assignment 2022-02-14 14:45:14 +09:00
Yu Watanabe
8ef959cd09 sd-dhcp6-client: initialize IO event source in sd_dhcp6_client_start() 2022-02-14 14:45:14 +09:00
Yu Watanabe
f814cd80de sd-dhcp6-client: make state transition stricter 2022-02-14 14:45:14 +09:00
Yu Watanabe
c2c878d806 sd-dhcp6-client: prohibit spurious state transition
Previously, `client_set_state(client, DHCP6_STATE_STOPPED)` might make
the client enter the SOLICIT state.
2022-02-14 14:45:14 +09:00
Yu Watanabe
220a88ca75 sd-dhcp6-client: disable T1 timer on T2
Otherwise, the client may be enter RENEW state after REBIND state.
2022-02-14 14:45:14 +09:00
Yu Watanabe
c41bdb17fc sd-dhcp6-client: call client_notify() in client_enter_bound_state() 2022-02-14 14:45:14 +09:00
Yu Watanabe
cdf3d8c584 sd-dhcp6-client: fix lifetime handling
This fixes the following issues:
- if T1 is finite and T2 is infinite, then the timer for T1 was not
  enabled,
- after T1 and T2 are randomized, T1 may be longer than T2.
2022-02-14 14:45:07 +09:00
Yu Watanabe
1d6cb4ce2a sd-dhcp6-client: use event_reset_time_relative() 2022-02-14 14:43:45 +09:00
Yu Watanabe
02354ee738 sd-dhcp6-client: introduce client_enter_bound_state()
To make client_set_state() smaller. This should not change any behavior.
2022-02-14 14:43:45 +09:00
Yu Watanabe
cc5184823f sd-dhcp6-client: fix overflow in calculating timeout value 2022-02-14 14:43:45 +09:00
Yu Watanabe
d0875a07dd sd-dhcp6-client: disable event source when client is stopped or freed 2022-02-14 14:43:45 +09:00
Yu Watanabe
7771233127 sd-dhcp6-client: use in6_addr_to_string() 2022-02-14 14:43:45 +09:00
Yu Watanabe
4db6334c9a sd-dhcp6-client: stop client on error in client state transition 2022-02-14 14:43:45 +09:00
Yu Watanabe
bfa1139af4 sd-dhcp6-client: add missing one more error handling 2022-02-14 14:43:45 +09:00
Yu Watanabe
06d5e856f8 sd-dhcp6-client: rename client_start() -> client_set_state() 2022-02-14 14:43:45 +09:00
Yu Watanabe
07a3b3408b sd-dhcp6-client: introduce client_process_*()
This is mostly for shortening `client_receive_message()`.

This also fixes the following:
- do not trigger SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST notification
  when non-reply message,
- add several missing error handlings caused in client_start().
2022-02-14 14:43:45 +09:00
Yu Watanabe
0123b2dbac sd-dhcp6-client: fix possible unaligned read or write 2022-02-14 14:43:45 +09:00
Yu Watanabe
16347c15d8 sd-dhcp6-client: voidify client_reset() 2022-02-14 14:43:45 +09:00
Yu Watanabe
65ece4c85b sd-dhcp6-client: introduce dhcp6_lease_new_from_message() 2022-02-14 14:43:45 +09:00
Yu Watanabe
c930925841 sd-dhcp6-client: use structured initializer 2022-02-14 14:43:45 +09:00
Yu Watanabe
f4fbea7a0f sd-dhcp6-lease: unify lease lifetime calculation 2022-02-14 14:43:45 +09:00
Yu Watanabe
126277aceb sd-dhcp6-client: always use ENODATA when a lease does not have requested data 2022-02-14 14:43:45 +09:00
Yu Watanabe
29858a0f9e sd-dhcp6-client: unify dhcp6_option_append_{ia,pd}() 2022-02-14 14:43:45 +09:00
Yu Watanabe
877bfc78fd sd-dhcp6-client: store PD prefix hint in ia_pd
And allows to specify multiple hints.
2022-02-14 14:43:45 +09:00
Yu Watanabe
e5b0b87f51 sd-dhcp6-client: introduce dhcp6_ia_free() 2022-02-14 14:43:45 +09:00
Yu Watanabe
4b0f27173c sd-dhcp6-client: unify IA option header
This also fixes possible unaligned read of message.
2022-02-14 14:43:45 +09:00
Yu Watanabe
93bd7c4192 sd-dhcp6-lease: reset client or server ID when length is zero
Addresses https://github.com/systemd/systemd/pull/22406#issuecomment-1029391091.
2022-02-14 14:43:45 +09:00
Yu Watanabe
049fddfa7d sd-dhcp6-lease: convert assert_return() -> assert() in non-public functions 2022-02-14 14:43:45 +09:00
Yu Watanabe
7c999d38fa sd-dhcp6-client: rename ia -> ia_na, pd -> ia_pd in sd_dhcp6_lease
The previous naming is quite misleading.
2022-02-14 14:43:45 +09:00
Yu Watanabe
43b7a412b1 sd-dhcp6-client: fix typo ia_pd -> ia_na
Fortunately, currently both ia_pd and ia_na in sd_dhcp6_client are
equivalent now.
2022-02-14 14:43:45 +09:00
Yu Watanabe
5e4d135c60 sd-dhcp6-client: introduce two helpers to create message 2022-02-14 14:43:45 +09:00
Yu Watanabe
6fcf356b9a sd-dhcp6-client: trigger assertion whn invalid IA type is provided
If the condition hits, then it is an error in coding, instead of a user
misconfiguration or invalid receivd message.
2022-02-14 14:40:57 +09:00
Yu Watanabe
8448321571 sd-dhcp6-client: add missing address existence check
This adds similar check as 58da18251f468de9de4cc7b36804c924e2fd4421.
2022-02-10 21:50:02 +09:00
30 changed files with 2281 additions and 2358 deletions

4
NEWS
View File

@ -383,8 +383,8 @@ CHANGES WITH 250:
whether to use the relevant fields from the IPv6 Router Advertisement
records.
* The ForceDHCPv6PDOtherInformation= setting in the [DHCPv6] section is
now removed. Please use the WithoutRA= and UseDelegatedPrefix=
* The ForceDHCPv6PDOtherInformation= setting in the [DHCPv6] section
has been removed. Please use the WithoutRA= and UseDelegatedPrefix=
settings in the [DHCPv6] section and the DHCPv6Client= setting in the
[IPv6AcceptRA] section to control when the DHCPv6 client is started
and how the delegated prefixes are handled by the DHCPv6 client.

View File

@ -407,3 +407,11 @@ available.
The `gdisk` tool (from version 1.0.5 onward) and its variants (`sgdisk`,
`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)

View File

@ -84,13 +84,8 @@
<term><option>-f</option></term>
<term><option>--force</option></term>
<listitem><para>Force immediate halt, power-off, or reboot. When
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>
<listitem><para>Force immediate halt, power-off, reboot. Do
not contact the init system.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -1894,9 +1894,9 @@
<para><command>WantedBy=foo.service</command> in a service
<filename>bar.service</filename> is mostly equivalent to
<command>Alias=foo.service.wants/bar.service</command> in the
same file. In case of template units, <command>systemctl
enable</command> must be called with an instance name, and
this instance will be added to the
same file. In case of template units listing non template units,
<command>systemctl enable</command> must be called with an
instance name, and this instance will be added to the
<filename>.wants/</filename> or
<filename>.requires/</filename> list of the listed unit. E.g.
<command>WantedBy=getty.target</command> in a service

View File

@ -18,6 +18,14 @@ 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);
#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) {
return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s);
}

View File

@ -1859,7 +1859,7 @@ static int make_partition_table(
if (!t)
return log_oom();
r = fdisk_parttype_set_typestr(t, SD_ID128_TO_UUID_STRING(GPT_USER_HOME));
r = fdisk_parttype_set_typestr(t, GPT_USER_HOME_STR);
if (r < 0)
return log_error_errno(r, "Failed to initialize partition type: %m");

View File

@ -8,20 +8,27 @@
#include "sd-id128.h"
#include "dhcp-identifier.h"
#include "dhcp6-protocol.h"
#include "netif-util.h"
#include "siphash24.h"
#include "sparse-endian.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.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 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 */
int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) {
static const char * const duid_type_table[_DUID_TYPE_MAX] = {
[DUID_TYPE_LLT] = "DUID-LLT",
[DUID_TYPE_EN] = "DUID-EN/Vendor",
[DUID_TYPE_LL] = "DUID-LL",
[DUID_TYPE_UUID] = "UUID",
};
DEFINE_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;
assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
@ -57,110 +64,146 @@ int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) {
return 0;
}
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) {
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) {
uint16_t time_from_2000y;
assert(duid);
assert(len);
assert(addr);
assert(ret_duid);
assert(ret_len);
if (addr_len == 0)
return -EOPNOTSUPP;
if (arp_type == ARPHRD_ETHER)
assert_return(addr_len == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
else
return -EINVAL;
return -EOPNOTSUPP;
if (t < USEC_2000)
time_from_2000y = 0;
else
time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
unaligned_write_be16(&duid->type, DUID_TYPE_LLT);
unaligned_write_be16(&duid->llt.htype, arp_type);
unaligned_write_be32(&duid->llt.time, time_from_2000y);
memcpy(duid->llt.haddr, addr, addr_len);
unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT);
unaligned_write_be16(&ret_duid->llt.htype, arp_type);
unaligned_write_be32(&ret_duid->llt.time, time_from_2000y);
memcpy(ret_duid->llt.haddr, addr, addr_len);
*len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len;
*ret_len = sizeof(ret_duid->type) + sizeof(ret_duid->llt.htype) + sizeof(ret_duid->llt.time) + addr_len;
return 0;
}
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);
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) {
assert(addr);
assert(ret_duid);
assert(ret_len);
if (addr_len == 0)
return -EOPNOTSUPP;
if (arp_type == ARPHRD_ETHER)
assert_return(addr_len == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
else
return -EINVAL;
return -EOPNOTSUPP;
unaligned_write_be16(&duid->type, DUID_TYPE_LL);
unaligned_write_be16(&duid->ll.htype, arp_type);
memcpy(duid->ll.haddr, addr, addr_len);
unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL);
unaligned_write_be16(&ret_duid->ll.htype, arp_type);
memcpy(ret_duid->ll.haddr, addr, addr_len);
*len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len;
*ret_len = sizeof(ret_duid->type) + sizeof(ret_duid->ll.htype) + addr_len;
return 0;
}
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len) {
sd_id128_t machine_id;
uint64_t hash;
int r;
assert(duid);
assert(len);
assert(ret_duid);
assert(ret_len);
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
int r = sd_id128_get_machine(&machine_id);
if (r < 0)
return r;
#else
machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
#endif
if (!test_mode) {
r = sd_id128_get_machine(&machine_id);
if (r < 0)
return r;
} else
/* For tests, especially for fuzzers, reproducibility is important.
* Hence, use a static and constant machine ID.
* 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(&duid->type, DUID_TYPE_EN);
unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
*len = sizeof(duid->type) + sizeof(duid->en);
unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN);
unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN);
/* 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 */
hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
memcpy(duid->en.id, &hash, sizeof(duid->en.id));
memcpy(ret_duid->en.id, &hash, sizeof(ret_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;
}
int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) {
static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) {
sd_id128_t machine_id;
int r;
assert(duid);
assert(len);
assert(ret_duid);
assert(ret_len);
r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
if (r < 0)
return r;
unaligned_write_be16(&duid->type, DUID_TYPE_UUID);
memcpy(&duid->raw.data, &machine_id, sizeof(machine_id));
unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID);
memcpy(ret_duid->raw.data, &machine_id, sizeof(machine_id));
*len = sizeof(duid->type) + sizeof(machine_id);
*ret_len = sizeof(ret_duid->type) + sizeof(machine_id);
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 ifindex,
const uint8_t *mac,
size_t mac_len,
bool legacy_unstable_byteorder,
bool use_mac,
void *_id) {
void *ret) {
/* name is a pointer to memory in the sd_device struct, so must
* have the same scope */
@ -212,6 +255,6 @@ int dhcp_identifier_set_iaid(
* behavior. */
id32 = be32toh(id32);
unaligned_write_ne32(_id, id32);
unaligned_write_ne32(ret, id32);
return 0;
}

View File

@ -54,9 +54,23 @@ struct duid {
};
} _packed_;
int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict);
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_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len);
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len);
int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *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);
int dhcp_validate_duid_len(DUIDType 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(
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);
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_;

View File

@ -11,138 +11,83 @@
#include "sd-event.h"
#include "sd-dhcp6-client.h"
#include "dhcp-identifier.h"
#include "dhcp6-option.h"
#include "dhcp6-protocol.h"
#include "ether-addr-util.h"
#include "hashmap.h"
#include "list.h"
#include "macro.h"
#include "network-common.h"
#include "ordered-set.h"
#include "sparse-endian.h"
#include "time-util.h"
typedef struct sd_dhcp6_option {
/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
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;
uint32_t enterprise_identifier;
uint16_t option;
void *data;
size_t length;
} sd_dhcp6_option;
int ifindex;
char *ifname;
extern const struct hash_ops dhcp6_option_hash_ops;
struct in6_addr local_address;
struct hw_addr_data hw_addr;
uint16_t arp_type;
/* Common option header */
typedef struct DHCP6Option {
be16_t code;
be16_t len;
uint8_t data[];
} _packed_ DHCP6Option;
sd_event *event;
sd_event_source *receive_message;
sd_event_source *timeout_resend;
sd_event_source *timeout_expire;
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
int event_priority;
int fd;
/* Address option */
struct iaaddr {
struct in6_addr address;
be32_t lifetime_preferred;
be32_t lifetime_valid;
} _packed_;
DHCP6State state;
bool information_request;
usec_t information_request_time_usec;
usec_t information_refresh_time_usec;
be32_t transaction_id;
usec_t transaction_start;
usec_t retransmit_time;
uint8_t retransmit_count;
/* Prefix Delegation Prefix option */
struct iapdprefix {
be32_t lifetime_preferred;
be32_t lifetime_valid;
uint8_t prefixlen;
struct in6_addr address;
} _packed_;
bool iaid_set;
DHCP6IA ia_na;
DHCP6IA ia_pd;
DHCP6RequestIA request_ia;
struct duid duid;
size_t duid_len;
be16_t *req_opts;
size_t req_opts_len;
char *fqdn;
char *mudurl;
char **user_class;
char **vendor_class;
OrderedHashmap *extra_options;
OrderedSet *vendor_options;
typedef struct DHCP6Address DHCP6Address;
struct sd_dhcp6_lease *lease;
struct DHCP6Address {
LIST_FIELDS(DHCP6Address, addresses);
sd_dhcp6_client_callback_t callback;
void *userdata;
union {
struct iaaddr iaaddr;
struct iapdprefix iapdprefix;
};
/* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */
bool test_mode;
};
/* 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_send_udp_socket(int s, struct in6_addr *address,
const void *packet, size_t len);
int client_parse_message(
sd_dhcp6_client *client,
DHCP6Message *message,
size_t len,
sd_dhcp6_lease *lease);
const char *dhcp6_message_type_to_string(int s) _const_;
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_;
int dhcp6_client_send_message(sd_dhcp6_client *client);
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, ...) \
log_interface_prefix_full_errno( \

View File

@ -5,11 +5,13 @@
Copyright © 2014-2015 Intel Corporation. All rights reserved.
***/
#include <stdint.h>
#include <inttypes.h>
#include "sd-dhcp6-lease.h"
#include "dhcp6-internal.h"
#include "dhcp6-option.h"
#include "macro.h"
#include "time-util.h"
struct sd_dhcp6_lease {
unsigned n_ref;
@ -21,10 +23,13 @@ struct sd_dhcp6_lease {
uint8_t preference;
bool rapid_commit;
triple_timestamp timestamp;
usec_t lifetime_t1;
usec_t lifetime_t2;
usec_t lifetime_valid;
struct in6_addr server_address;
DHCP6IA ia;
DHCP6IA pd;
DHCP6IA *ia_na;
DHCP6IA *ia_pd;
DHCP6Address *addr_iter;
DHCP6Address *prefix_iter;
@ -40,17 +45,15 @@ struct sd_dhcp6_lease {
char *fqdn;
};
int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire);
DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia);
int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid);
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_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len);
int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference);
int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference);
int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret);
int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease);
int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit);
int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret);
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);
@ -59,3 +62,10 @@ 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_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);

View File

@ -9,14 +9,12 @@
#include "sd-dhcp6-client.h"
#include "alloc-util.h"
#include "dhcp-identifier.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#include "dhcp6-option.h"
#include "dhcp6-protocol.h"
#include "dns-domain.h"
#include "escape.h"
#include "memory-util.h"
#include "sparse-endian.h"
#include "strv.h"
#include "unaligned.h"
@ -212,19 +210,15 @@ 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) {
DHCP6Option *option;
assert_return(buf, -EINVAL);
assert_return(*buf, -EINVAL);
assert_return(buflen, -EINVAL);
option = (DHCP6Option*) *buf;
if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data))
return -ENOBUFS;
option->code = htobe16(optcode);
option->len = htobe16(optlen);
unaligned_write_be16(*buf + offsetof(DHCP6Option, code), optcode);
unaligned_write_be16(*buf + offsetof(DHCP6Option, len), optlen);
*buf += offsetof(DHCP6Option, data);
*buflen -= offsetof(DHCP6Option, data);
@ -250,7 +244,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
return 0;
}
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) {
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options) {
sd_dhcp6_option *options;
int r;
@ -259,7 +253,7 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash
assert(buflen);
assert(vendor_options);
ORDERED_HASHMAP_FOREACH(options, vendor_options) {
ORDERED_SET_FOREACH(options, vendor_options) {
_cleanup_free_ uint8_t *p = NULL;
size_t total;
@ -282,79 +276,33 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash
return 0;
}
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
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;
static int option_append_ia_address(uint8_t **buf, size_t *buflen, const struct iaaddr *address) {
struct iaaddr a;
int r;
assert_return(buf, -EINVAL);
assert_return(*buf, -EINVAL);
assert_return(buflen, -EINVAL);
assert_return(ia, -EINVAL);
assert(buf);
assert(*buf);
assert(buflen);
assert(address);
/* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */
/* Do not append T1 and T2. */
a = (struct iaaddr) {
.address = address->address,
};
switch (ia->type) {
case SD_DHCP6_OPTION_IA_NA:
len = DHCP6_OPTION_IA_NA_LEN;
ia_na = (struct ia_na) {
.id = ia->ia_na.id,
};
p = &ia_na;
break;
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr));
if (r < 0)
return r;
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;
memcpy(*buf, &a, sizeof(struct iaaddr));
default:
return -EINVAL;
}
*buf += sizeof(struct iaaddr);
*buflen -= 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);
return offsetof(DHCP6Option, data) + sizeof(struct iaaddr);
}
static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Address *prefix) {
static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct iapdprefix *prefix) {
struct iapdprefix p;
int r;
@ -363,14 +311,13 @@ static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Add
assert(buflen);
assert(prefix);
if (prefix->iapdprefix.prefixlen == 0)
if (prefix->prefixlen == 0)
return -EINVAL;
/* Do not append T1 and T2. */
p = (struct iapdprefix) {
.prefixlen = prefix->iapdprefix.prefixlen,
.address = prefix->iapdprefix.address,
.prefixlen = prefix->prefixlen,
.address = prefix->address,
};
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix));
@ -385,57 +332,67 @@ static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Add
return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix);
}
int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix) {
struct ia_pd ia_pd;
size_t len, pd_buflen;
uint8_t *pd_hdr;
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
struct ia_header header;
const DHCP6Address *addr;
size_t ia_buflen;
uint8_t *ia_hdr;
uint16_t len;
int r;
assert_return(buf, -EINVAL);
assert_return(*buf, -EINVAL);
assert_return(buflen, -EINVAL);
assert_return(pd, -EINVAL);
assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
assert_return(ia, -EINVAL);
/* Do not set T1 and T2. */
ia_pd = (struct ia_pd) {
.id = pd->ia_pd.id,
};
len = sizeof(struct ia_pd);
/* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */
switch (ia->type) {
case SD_DHCP6_OPTION_IA_NA:
case SD_DHCP6_OPTION_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)
return -ENOBUFS;
pd_hdr = *buf;
pd_buflen = *buflen;
ia_hdr = *buf;
ia_buflen = *buflen;
/* The header will be written at the end of this function. */
*buf += offsetof(DHCP6Option, data);
*buflen -= offsetof(DHCP6Option, data);
memcpy(*buf, &ia_pd, len);
memcpy(*buf, &header, len);
*buf += len;
*buflen -= len;
*buf += sizeof(struct ia_pd);
*buflen -= sizeof(struct ia_pd);
DHCP6Address *prefix;
LIST_FOREACH(addresses, prefix, pd->addresses) {
r = option_append_pd_prefix(buf, buflen, prefix);
LIST_FOREACH(addresses, addr, ia->addresses) {
if (ia->type == SD_DHCP6_OPTION_IA_PD)
r = option_append_pd_prefix(buf, buflen, &addr->iapdprefix);
else
r = option_append_ia_address(buf, buflen, &addr->iaaddr);
if (r < 0)
return r;
len += r;
}
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);
return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len);
}
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
@ -548,7 +505,6 @@ int dhcp6_option_parse(
size_t *ret_option_data_len,
const uint8_t **ret_option_data) {
const DHCP6Option *option;
size_t len;
assert(buf);
@ -563,16 +519,15 @@ int dhcp6_option_parse(
if (*offset >= buflen - offsetof(DHCP6Option, data))
return -EBADMSG;
option = (const DHCP6Option*) (buf + *offset);
len = be16toh(option->len);
len = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, len));
if (len > buflen - offsetof(DHCP6Option, data) - *offset)
return -EBADMSG;
*offset += offsetof(DHCP6Option, data) + len;
*ret_option_code = be16toh(option->code);
*ret_option_code = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, code));
*ret_option_data_len = len;
*ret_option_data = option->data;
*ret_option_data = buf + *offset + offsetof(DHCP6Option, data);
*offset += offsetof(DHCP6Option, data) + len;
return 0;
}
@ -601,7 +556,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) {
int r;
assert(buf);
assert(buf || buflen == 0);
for(size_t offset = 0; offset < buflen;) {
const uint8_t *data;
@ -619,15 +574,15 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t
r = dhcp6_option_parse_status(data, data_len, &msg);
if (r == -ENOMEM)
return r;
if (r < 0)
/* Let's log but ignore the invalid status option. */
log_dhcp6_client_errno(client, r,
"Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m");
else if (r > 0)
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)
/* Let's log but ignore the invalid status option. */
log_dhcp6_client_errno(client, r,
"Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m");
break;
}
default:
@ -638,19 +593,29 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t
return 0;
}
static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) {
static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) {
_cleanup_free_ DHCP6Address *a = NULL;
uint32_t lt_valid, lt_pref;
DHCP6Address *a;
int r;
assert(data);
assert(ret);
assert(ia);
assert(data || len == 0);
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))
return -EBADMSG;
lt_valid = be32toh(((const struct iaaddr*) data)->lifetime_valid);
lt_pref = be32toh(((const struct iaaddr*) data)->lifetime_preferred);
a = new(DHCP6Address, 1);
if (!a)
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)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
@ -667,27 +632,33 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t
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);
if (!a)
return -ENOMEM;
LIST_INIT(addresses, a);
memcpy(&a->iaaddr, data, sizeof(struct iaaddr));
memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
*ret = a;
return 0;
}
static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) {
uint32_t lt_valid, lt_pref;
DHCP6Address *a;
int r;
if (len < sizeof(struct iapdprefix))
return -ENOMSG;
lt_valid = be32toh(((const struct iapdprefix*) data)->lifetime_valid);
lt_pref = be32toh(((const struct iapdprefix*) data)->lifetime_preferred);
lt_valid = be32toh(a->iapdprefix.lifetime_valid);
lt_pref = be32toh(a->iapdprefix.lifetime_preferred);
if (lt_valid == 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
@ -704,14 +675,7 @@ static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t
return r;
}
a = new(DHCP6Address, 1);
if (!a)
return -ENOMEM;
LIST_INIT(addresses, a);
memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
*ret = a;
LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a));
return 0;
}
@ -721,16 +685,15 @@ int dhcp6_option_parse_ia(
uint16_t option_code,
size_t option_data_len,
const uint8_t *option_data,
DHCP6IA *ret) {
DHCP6IA **ret) {
_cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
uint32_t lt_t1, lt_t2, lt_min = UINT32_MAX;
be32_t received_iaid;
size_t offset;
_cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
uint32_t lt_t1, lt_t2;
size_t header_len;
int r;
assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD));
assert(option_data);
assert(option_data || option_data_len == 0);
assert(ret);
/* This will return the following:
@ -743,56 +706,51 @@ int dhcp6_option_parse_ia(
switch (option_code) {
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:
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);
header_len = sizeof(struct ia_header);
break;
case SD_DHCP6_OPTION_IA_TA:
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. */
header_len = sizeof(be32_t); /* IA_TA does not have lifetime. */
break;
default:
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,
* but not necessary to ignore or refuse the whole message. */
if (received_iaid != iaid)
if (ia->header.id != iaid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
"Received an IA option with a different IAID "
"from the one chosen by the client, ignoring.");
/* 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)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.",
lt_t1, lt_t2);
if (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 (; offset < option_data_len;) {
for (size_t offset = header_len; offset < option_data_len;) {
const uint8_t *subdata;
size_t subdata_len;
uint16_t subopt;
@ -803,41 +761,19 @@ int dhcp6_option_parse_ia(
switch (subopt) {
case SD_DHCP6_OPTION_IAADDR: {
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);
r = dhcp6_option_parse_ia_address(client, ia, subdata, subdata_len);
if (r == -ENOMEM)
return r;
if (r < 0)
/* Ignore the sub-option on non-critical errors. */
continue;
lt_min = MIN(lt_min, be32toh(a->iaaddr.lifetime_valid));
LIST_PREPEND(addresses, ia.addresses, a);
/* Ignore non-critical errors in the sub-option. */
break;
}
case SD_DHCP6_OPTION_IA_PD_PREFIX: {
DHCP6Address *a;
if (option_code != SD_DHCP6_OPTION_IA_PD) {
log_dhcp6_client(client, "Received an IA_NA or IA_TA option with an PD prefix, ignoring");
continue;
}
r = dhcp6_option_parse_ia_pdprefix(client, subdata, subdata_len, &a);
r = dhcp6_option_parse_ia_pdprefix(client, ia, subdata, subdata_len);
if (r == -ENOMEM)
return r;
if (r < 0)
/* Ignore the sub-option on non-critical errors. */
continue;
lt_min = MIN(lt_min, be32toh(a->iapdprefix.lifetime_valid));
LIST_PREPEND(addresses, ia.addresses, a);
/* Ignore non-critical errors in the sub-option. */
break;
}
case SD_DHCP6_OPTION_STATUS_CODE: {
@ -846,14 +782,14 @@ int dhcp6_option_parse_ia(
r = dhcp6_option_parse_status(subdata, subdata_len, &msg);
if (r == -ENOMEM)
return r;
if (r < 0)
log_dhcp6_client_errno(client, r,
"Received an IA option with an invalid status sub option, ignoring: %m");
else if (r > 0)
if (r > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA option with non-zero status: %s%s%s",
strempty(msg), isempty(msg) ? "" : ": ",
dhcp6_message_status_to_string(r));
if (r < 0)
log_dhcp6_client_errno(client, r,
"Received an IA option with an invalid status sub option, ignoring: %m");
break;
}
default:
@ -861,50 +797,11 @@ int dhcp6_option_parse_ia(
}
}
if (!ia.addresses)
if (!ia->addresses)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA),
"Received an IA option without valid IA addresses or PD prefixes, ignoring.");
if (IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_PD) &&
lt_t1 == 0 && lt_t2 == 0 && lt_min != UINT32_MAX) {
lt_t1 = lt_min / 2;
lt_t2 = lt_min / 10 * 8;
log_dhcp6_client(client, "Received an IA option with both T1 and T2 equal to zero. "
"Adjusting them based on the minimum valid lifetime of IA addresses or PD prefixes: "
"T1=%"PRIu32"sec, T2=%"PRIu32"sec", lt_t1, lt_t2);
}
switch(option_code) {
case SD_DHCP6_OPTION_IA_NA:
*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();
}
*ret = TAKE_PTR(ia);
return 0;
}

View File

@ -0,0 +1,104 @@
/* 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);

View File

@ -0,0 +1,84 @@
/* 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);

View File

@ -28,8 +28,8 @@ typedef struct DHCP6Message DHCP6Message;
#define DHCP6_MIN_OPTIONS_SIZE \
1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } }
enum {
@ -100,13 +100,15 @@ typedef enum DHCP6MessageType {
DHCP6_MESSAGE_STATE = 34, /* RFC 8156 */
DHCP6_MESSAGE_CONTACT = 35, /* RFC 8156 */
_DHCP6_MESSAGE_TYPE_MAX,
_DHCP6_MESSAGE_TYPE_INVALID = -EINVAL,
_DHCP6_MESSAGE_TYPE_INVALID = -EINVAL,
} DHCP6MessageType;
typedef enum DHCP6NTPSubOption {
DHCP6_NTP_SUBOPTION_SRV_ADDR = 1,
DHCP6_NTP_SUBOPTION_MC_ADDR = 2,
DHCP6_NTP_SUBOPTION_SRV_FQDN = 3,
_DHCP6_NTP_SUBOPTION_MAX,
_DHCP6_NTP_SUBOPTION_INVALID = -EINVAL,
} DHCP6NTPSubOption;
/*
@ -138,7 +140,7 @@ typedef enum DHCP6Status {
DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED = 21,
DHCP6_STATUS_EXCESSIVE_TIME_SKEW = 22,
_DHCP6_STATUS_MAX,
_DHCP6_STATUS_INVALID = -EINVAL,
_DHCP6_STATUS_INVALID = -EINVAL,
} DHCP6Status;
typedef enum DHCP6FQDNFlag {
@ -146,3 +148,9 @@ typedef enum DHCP6FQDNFlag {
DHCP6_FQDN_FLAG_O = 1 << 1,
DHCP6_FQDN_FLAG_N = 1 << 2,
} 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_;

View File

@ -1,61 +0,0 @@
/* 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;
}

View File

@ -6,43 +6,59 @@
#include "sd-event.h"
#include "dhcp6-internal.h"
#include "dhcp6-protocol.h"
#include "event-util.h"
#include "fd-util.h"
#include "fuzz.h"
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;
}
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);
return test_dhcp_fd[0];
return TAKE_FD(test_dhcp_fd[0]);
}
static void fuzz_client(const uint8_t *data, size_t size, bool is_information_request_enabled) {
_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);
static void fuzz_client(sd_dhcp6_client *client, const uint8_t *data, size_t size, DHCP6State state) {
assert_se(sd_dhcp6_client_set_information_request(client, state == DHCP6_STATE_INFORMATION_REQUEST) >= 0);
assert_se(sd_dhcp6_client_start(client) >= 0);
client->state = state;
if (size >= sizeof(DHCP6Message))
assert_se(sd_dhcp6_client_set_transaction_id(client, htobe32(0x00ffffff) & ((const DHCP6Message *) data)->transaction_id) == 0);
assert_se(dhcp6_client_set_transaction_id(client, ((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);
sd_event_run(e, UINT64_MAX);
assert_se(sd_event_run(sd_dhcp6_client_get_event(client), UINT64_MAX) > 0);
/* 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);
@ -50,14 +66,43 @@ static void fuzz_client(const uint8_t *data, size_t size, bool is_information_re
}
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)
return 0;
/* This triggers client_receive_advertise */
fuzz_client(data, size, false);
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);
dhcp6_client_set_test_mode(client, true);
/* This triggers client_receive_reply */
fuzz_client(data, size, true);
/* Used when sending message. */
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_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;
}

View File

@ -17,6 +17,8 @@ sources = files('''
dhcp6-lease-internal.h
dhcp6-network.c
dhcp6-option.c
dhcp6-option.h
dhcp6-protocol.c
dhcp6-protocol.h
icmp6-util.c
icmp6-util.h
@ -113,10 +115,6 @@ fuzzers += [
[libshared,
libsystemd_network]],
[files('fuzz-dhcp6-client-send.c'),
[libshared,
libsystemd_network]],
[files('fuzz-dhcp-server.c'),
[libsystemd_network,
libshared]],

View File

@ -450,7 +450,7 @@ static int dhcp_client_set_iaid_duid_internal(
bool iaid_append,
bool iaid_set,
uint32_t iaid,
uint16_t duid_type,
DUIDType duid_type,
const void *duid,
size_t duid_len,
usec_t llt_time) {
@ -489,37 +489,20 @@ static int dhcp_client_set_iaid_duid_internal(
client->client_id.ns.duid.type = htobe16(duid_type);
memcpy(&client->client_id.ns.duid.raw.data, duid, 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.");
r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, 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-LLT: %m");
break;
case DUID_TYPE_EN:
r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
if (r < 0)
return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m");
break;
case DUID_TYPE_LL:
if (client->mac_addr_len == 0)
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");
}
} else {
r = dhcp_identifier_set_duid(duid_type, client->mac_addr, client->mac_addr_len,
client->arp_type, llt_time, client->test_mode,
&client->client_id.ns.duid, &len);
if (r == -EOPNOTSUPP)
return log_dhcp_client_errno(client, r,
"Failed to set %s. MAC address is not set or "
"interface type is not supported.",
duid_type_to_string(duid_type));
if (r < 0)
return log_dhcp_client_errno(client, r, "Failed to set %s: %m",
duid_type_to_string(duid_type));
}
client->client_id_len = sizeof(client->client_id.type) + len +
(iaid_append ? sizeof(client->client_id.ns.iaid) : 0);
@ -876,7 +859,7 @@ static int client_message_init(
if (r < 0)
return r;
r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len);
r = dhcp_identifier_set_duid_en(client->test_mode, &client->client_id.ns.duid, &duid_len);
if (r < 0)
return r;

File diff suppressed because it is too large Load Diff

View File

@ -6,10 +6,21 @@
#include <errno.h>
#include "alloc-util.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#include "dhcp6-protocol.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) {
assert_return(lease, -EINVAL);
@ -24,6 +35,69 @@ int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_
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) {
assert_return(lease, -EINVAL);
assert_return(ret, -EINVAL);
@ -32,55 +106,37 @@ int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *re
return 0;
}
int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
DHCP6Address *addr;
uint32_t valid = 0, t;
void dhcp6_ia_clear_addresses(DHCP6IA *ia) {
DHCP6Address *a, *n;
assert_return(ia, -EINVAL);
assert_return(expire, -EINVAL);
assert(ia);
LIST_FOREACH(addresses, addr, ia->addresses) {
t = be32toh(addr->iaaddr.lifetime_valid);
if (valid < t)
valid = t;
}
LIST_FOREACH_SAFE(addresses, a, n, ia->addresses)
free(a);
t = be32toh(ia->ia_na.lifetime_t2);
if (t > valid)
return -EINVAL;
*expire = valid - t;
return 0;
ia->addresses = NULL;
}
DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) {
DHCP6Address *address;
DHCP6IA *dhcp6_ia_free(DHCP6IA *ia) {
if (!ia)
return NULL;
while (ia->addresses) {
address = ia->addresses;
dhcp6_ia_clear_addresses(ia);
LIST_REMOVE(addresses, ia->addresses, address);
free(address);
}
return NULL;
return mfree(ia);
}
int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
uint8_t *clientid;
uint8_t *clientid = NULL;
assert_return(lease, -EINVAL);
assert_return(id, -EINVAL);
assert_return(len > 0, -EINVAL);
assert(lease);
assert(id || len == 0);
clientid = memdup(id, len);
if (!clientid)
return -ENOMEM;
if (len > 0) {
clientid = memdup(id, len);
if (!clientid)
return -ENOMEM;
}
free_and_replace(lease->clientid, clientid);
lease->clientid_len = len;
@ -89,7 +145,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) {
assert_return(lease, -EINVAL);
assert(lease);
if (!lease->clientid)
return -ENODATA;
@ -103,15 +159,16 @@ 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) {
uint8_t *serverid;
uint8_t *serverid = NULL;
assert_return(lease, -EINVAL);
assert_return(id, -EINVAL);
assert_return(len > 0, -EINVAL);
assert(lease);
assert(id || len == 0);
serverid = memdup(id, len);
if (!serverid)
return -ENOMEM;
if (len > 0) {
serverid = memdup(id, len);
if (!serverid)
return -ENOMEM;
}
free_and_replace(lease->serverid, serverid);
lease->serverid_len = len;
@ -120,7 +177,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) {
assert_return(lease, -EINVAL);
assert(lease);
if (!lease->serverid)
return -ENODATA;
@ -129,107 +186,99 @@ int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re
*ret_id = lease->serverid;
if (ret_len)
*ret_len = lease->serverid_len;
return 0;
}
int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) {
assert_return(lease, -EINVAL);
assert(lease);
lease->preference = preference;
return 0;
}
int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) {
assert_return(preference, -EINVAL);
if (!lease)
return -EINVAL;
*preference = lease->preference;
int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret) {
assert(lease);
assert(ret);
*ret = lease->preference;
return 0;
}
int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) {
assert_return(lease, -EINVAL);
assert(lease);
lease->rapid_commit = true;
return 0;
}
int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit) {
assert_return(lease, -EINVAL);
assert_return(rapid_commit, -EINVAL);
*rapid_commit = lease->rapid_commit;
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 *addr,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid) {
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(addr, -EINVAL);
assert_return(lifetime_preferred, -EINVAL);
assert_return(lifetime_valid, -EINVAL);
if (!lease->addr_iter)
return -ENOMSG;
return -ENODATA;
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);
if (ret_addr)
*ret_addr = lease->addr_iter->iaaddr.address;
if (ret_lifetime_preferred)
*ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred);
if (ret_lifetime_valid)
*ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid);
lease->addr_iter = lease->addr_iter->addresses_next;
return 0;
}
void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
if (lease)
lease->addr_iter = lease->ia.addresses;
lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
}
int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
uint8_t *prefix_len,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid) {
int sd_dhcp6_lease_get_pd(
sd_dhcp6_lease *lease,
struct in6_addr *ret_prefix,
uint8_t *ret_prefix_len,
uint32_t *ret_lifetime_preferred,
uint32_t *ret_lifetime_valid) {
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)
return -ENOMSG;
return -ENODATA;
memcpy(prefix, &lease->prefix_iter->iapdprefix.address,
sizeof(struct in6_addr));
*prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
*lifetime_preferred =
be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
*lifetime_valid =
be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
if (ret_prefix)
*ret_prefix = lease->prefix_iter->iapdprefix.address;
if (ret_prefix_len)
*ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
if (ret_lifetime_preferred)
*ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
if (ret_lifetime_valid)
*ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
lease->prefix_iter = lease->prefix_iter->addresses_next;
return 0;
}
void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
if (lease)
lease->prefix_iter = lease->pd.addresses;
lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
}
int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL);
assert(lease);
assert(optval || optlen == 0);
if (optlen == 0)
return 0;
@ -241,7 +290,7 @@ int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
assert_return(lease, -EINVAL);
if (!lease->dns)
return -ENOENT;
return -ENODATA;
if (ret)
*ret = lease->dns;
@ -253,8 +302,8 @@ int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t
_cleanup_strv_free_ char **domains = NULL;
int r;
assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL);
assert(lease);
assert(optval || optlen == 0);
if (optlen == 0)
return 0;
@ -271,7 +320,7 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
assert_return(ret, -EINVAL);
if (!lease->domains)
return -ENOENT;
return -ENODATA;
*ret = lease->domains;
return strv_length(lease->domains);
@ -280,8 +329,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 r;
assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL);
assert(lease);
assert(optval || optlen == 0);
for (size_t offset = 0; offset < optlen;) {
const uint8_t *subval;
@ -296,7 +345,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_MC_ADDR:
if (sublen != 16)
return 0;
return -EINVAL;
r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
if (r < 0)
@ -326,8 +375,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) {
assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL);
assert(lease);
assert(optval || optlen == 0);
if (optlen == 0)
return 0;
@ -352,14 +401,14 @@ int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **
return lease->sntp_count;
}
return -ENOENT;
return -ENODATA;
}
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
assert_return(lease, -EINVAL);
if (!lease->ntp_fqdn)
return -ENOENT;
return -ENODATA;
if (ret)
*ret = lease->ntp_fqdn;
@ -367,11 +416,14 @@ 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 r;
char *fqdn;
int r;
assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL);
assert(lease);
assert(optval || optlen == 0);
if (optlen == 0)
return 0;
if (optlen < 2)
return -ENODATA;
@ -390,20 +442,222 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
assert_return(ret, -EINVAL);
if (!lease->fqdn)
return -ENOENT;
return -ENODATA;
*ret = lease->fqdn;
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) {
if (!lease)
return NULL;
free(lease->clientid);
free(lease->serverid);
dhcp6_lease_free_ia(&lease->ia);
dhcp6_lease_free_ia(&lease->pd);
dhcp6_ia_free(lease->ia_na);
dhcp6_ia_free(lease->ia_pd);
free(lease->dns);
free(lease->fqdn);
strv_free(lease->domains);
@ -419,14 +673,47 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free);
int dhcp6_lease_new(sd_dhcp6_lease **ret) {
sd_dhcp6_lease *lease;
lease = new0(sd_dhcp6_lease, 1);
assert(ret);
lease = new(sd_dhcp6_lease, 1);
if (!lease)
return -ENOMEM;
lease->n_ref = 1;
LIST_HEAD_INIT(lease->ia.addresses);
*lease = (sd_dhcp6_lease) {
.n_ref = 1,
};
*ret = lease;
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;
}

View File

@ -170,7 +170,7 @@ static int check_options(uint8_t code, uint8_t len, const void *option, void *us
struct duid duid;
size_t duid_len;
assert_se(dhcp_identifier_set_duid_en(&duid, &duid_len) >= 0);
assert_se(dhcp_identifier_set_duid_en(/* test_mode = */ true, &duid, &duid_len) >= 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);

File diff suppressed because it is too large Load Diff

View File

@ -432,8 +432,20 @@ int dhcp6_start_on_ra(Link *link, bool information_request) {
return r;
if (inf_req == information_request)
/* The client is already running in the requested mode. */
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);
if (r < 0)
return r;

View File

@ -122,15 +122,24 @@
#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_ESP SD_ID128_MAKE(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_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
#define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
#define GPT_SRV SD_ID128_MAKE(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_TMP SD_ID128_MAKE(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_LINUX_GENERIC SD_ID128_MAKE(0f,c6,3d,af,84,83,47,72,8e,79,3d,69,d8,47,7d,e4)
#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_STR SD_ID128_MAKE_UUID_STR(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_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_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
#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_SRV SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8)
#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 */
#if defined(__alpha__)

View File

@ -2142,7 +2142,7 @@ int show(int argc, char *argv[], void *userdata) {
show_mode = systemctl_show_mode_from_string(argv[0]);
if (show_mode < 0)
return log_error_errno(show_mode, "Invalid argument.");
return log_error_errno(show_mode, "Invalid argument '%s'.", argv[0]);
if (show_mode == SYSTEMCTL_SHOW_HELP && argc <= 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),

View File

@ -251,7 +251,7 @@ int sd_dhcp6_client_set_request_vendor_class(
int sd_dhcp6_client_set_prefix_delegation_hint(
sd_dhcp6_client *client,
uint8_t prefixlen,
const struct in6_addr *pd_address);
const struct in6_addr *pd_prefix);
int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client,
int *delegation);
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client,
@ -260,8 +260,6 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client,
int *request);
int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
int request);
int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client,
uint32_t transaction_id);
int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
sd_dhcp6_option *v);

View File

@ -5933,5 +5933,4 @@ if __name__ == '__main__':
env.update({ 'UBSAN_OPTIONS' : ubsan_options })
sys.argv[1:] = unknown_args
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,
verbosity=3))
unittest.main(verbosity=3)