1
0
mirror of https://github.com/systemd/systemd synced 2026-03-17 02:24:48 +01:00

Compare commits

..

13 Commits

Author SHA1 Message Date
Yu Watanabe
38dd7673b3
Merge pull request #19703 from yuwata/network-ndisc-valid-time
network: ndisc: always honor valid time
2021-05-27 18:42:31 +09:00
Yu Watanabe
facaf083f0
Merge pull request #19743 from yuwata/dhcpv4-static-leases
network: dhcp-server:  support static lease
2021-05-27 18:41:22 +09:00
Yu Watanabe
a19b1ac7c8 network: ndisc: update log message 2021-05-27 16:33:36 +09:00
Yu Watanabe
25db3aeaf3 network: ndisc: always honor valid time
See draft-ietf-6man-slaac-renum-02, section 4.2.
https://datatracker.ietf.org/doc/html/draft-ietf-6man-slaac-renum#section-4.2

Replaces #15260.
2021-05-27 16:33:33 +09:00
borna-blazevic
ffaece68bc test-network: add a testcase for DHCP static lease 2021-05-27 15:57:33 +09:00
borna-blazevic
c517a49bf7 network: dhcp-server: introduce [DHCPServerStaticLease] section 2021-05-27 15:51:26 +09:00
borna-blazevic
65a0ef2341 sd-dhcp-server: support static address to DHCPv4 offer 2021-05-27 15:15:27 +09:00
Yu Watanabe
e954939b9e
Merge pull request #19736 from poettering/udev-trigger-uuid
sd-device: add support for triggering devices with UUID support
2021-05-27 13:05:00 +09:00
Christian Hesse
9fff026d60 man: fix tag type
This is an option, not a command.
2021-05-27 12:50:57 +09:00
Lennart Poettering
74bbc85ca6 hash-func: change value type of string_hash_ops_free_free to void*
The generic string_hash_ops_free_free hash operations vtable currently
assumes the data pointer is of type char*. There's really no reason to
assume that though, we regularly store non-string data as value in a
hashmap. Hence, to accomodate for that, use void* as pointer for the
value (and keep char* for the key, as that's what
string_hash_ops_free_free is for, after all).
2021-05-26 21:44:36 +02:00
Lennart Poettering
730b9c1e14 udevadm: make use of the new uuid-enabled triggering for "udevadm trigger"
This adds two things:

- A new switch --uuid is added to "udevadm trigger". If specified a
  random UUID is associated with the synthettic uevent and it is printed
  to stdout. It may then be used manually to match up uevents as they
  propagate through the system.

- The UUID logic is now implicitly enabled if "udevadm trigger --settle"
  is used, in order to wait for precisely the uevents we actually
  trigger. Fallback support is kept for pre-4.13 kernels (where the
  requests for trigger uevents with uuids results in EINVAL).
2021-05-26 21:44:36 +02:00
Lennart Poettering
b485fd932a sd-device: add API for triggering synthetic uevents with UUID
Since kernel 4.13 the kerne allows passing a UUID to generated uevents.
Optionally do so via a new sd_device_trigger_with_uuid() call, and add
sd_device_get_trigger_uuid() as helper to retrieve the UUID from a
uevent we receive.

This is useful for tracking uevents through the udev system, and waiting
for specific triggers.

(Note that the 4.13 patch allows passing arbitrary meta-info into the
uevent as well. This does not add an API for that, because I am not
convinced it makes sense — as it conflicts with our general rule that
events are "stateless" if you so will — and it complicates the interface
quite a bit).

This replaces #13881 in a way, which added a similar infra, but which
stalled, and whose synchronous settling APIs are somewhat problematic
and probably not material to merge.
2021-05-26 21:44:36 +02:00
Lennart Poettering
6f75309295 man: document that it is guaranteed that generated ID128 are never all-zero or all-one
This is the case because the ID128 we generate are all marked as v4 UUID
which requires that some bits are zero and others are one. Let's
document this so that people can rely on SD_ID128_NULL being a special
value for "uninitialized" that is always distinguishable from generated
UUIDs.
2021-05-26 21:44:15 +02:00
27 changed files with 748 additions and 111 deletions

View File

@ -241,7 +241,7 @@
<term><option>--make-machine-id-directory=yes|no|auto</option></term>
<listitem><para>Control creation and deletion of the top-level machine ID directory on the file
system containing boot loader entries (i.e. beneath the file system returned by
<command>--print-boot-path</command> above) during <option>install</option> and
<option>--print-boot-path</option> above) during <option>install</option> and
<option>remove</option>, respectively. <literal>auto</literal> is equivalent to
<literal>yes</literal> if <filename>/etc/machine-id</filename> resides on a filesystem other than
tmpfs and <literal>no</literal> otherwise (in the latter case the machine ID is likely transient and

View File

@ -99,12 +99,16 @@
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details. The
ID is cached internally. In future a different mechanism to determine the invocation ID may be added.</para>
<para>Note that <function>sd_id128_get_machine_app_specific()</function>, <function>sd_id128_get_boot()</function>,
<function>sd_id128_get_boot_app_specific()</function>, and <function>sd_id128_get_invocation()</function> always
return UUID v4 compatible IDs. <function>sd_id128_get_machine()</function> will also return a UUID v4-compatible
ID on new installations but might not on older. It is possible to convert the machine ID into a UUID v4-compatible
one. For more information, see
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
<para>Note that <function>sd_id128_get_machine_app_specific()</function>,
<function>sd_id128_get_boot()</function>, <function>sd_id128_get_boot_app_specific()</function>, and
<function>sd_id128_get_invocation()</function> always return UUID v4 compatible IDs.
<function>sd_id128_get_machine()</function> will also return a UUID v4-compatible ID on new installations
but might not on older. It is possible to convert the machine ID into a UUID v4-compatible one. For more
information, see
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>. It is
hence guaranteed that thes functions will never return the ID consisting of all zero or all one bits
(<constant>SD_ID128_NULL</constant>, <constant>SD_ID128_ALLF</constant>) — with the possible exception of
<function>sd_id128_get_machine()</function>, as mentioned.</para>
<para>For more information about the <literal>sd_id128_t</literal>
type see

View File

@ -42,8 +42,9 @@
<filename>/dev/urandom</filename> kernel random number
generator.</para>
<para>Note that <function>sd_id128_randomize()</function> always
returns a UUID v4-compatible ID.</para>
<para>Note that <function>sd_id128_randomize()</function> always returns a UUID v4-compatible ID. It is
hence guaranteed that this function will never return the ID consisting of all zero or all one bits
(<constant>SD_ID128_NULL</constant>, <constant>SD_ID128_ALLF</constant>).</para>
<para>For more information about the <literal>sd_id128_t</literal>
type, see

View File

@ -2535,6 +2535,29 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
</variablelist>
</refsect1>
<refsect1>
<title>[DHCPServerStaticLease] Section Options</title>
<para>The <literal>[DHCPServerStaticLease]</literal> section configures a static DHCP lease to
assign a pre-set IPv4 address to a specific device based on its MAC address. This section can be
specified multiple times.</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>MACAddress=</varname></term>
<listitem><para>The hardware address of a device which should be assigned IPv4 address
specified in <varname>Address=</varname>. This key is mandatory.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Address=</varname></term>
<listitem><para>IPv4 address that should be assigned to a device with a hardware address
specified in <varname>MACAddress=</varname>. This key is mandatory.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[IPv6SendRA] Section Options</title>
<para>The [IPv6SendRA] section contains settings for sending IPv6 Router Advertisements and whether

View File

@ -375,6 +375,15 @@
the same command to finish.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--uuid</option></term>
<listitem>
<para>Trigger the synthetic device events, and associate a randomized UUID with each. These UUIDs
are printed to standard output, one line for each event. These UUIDs are included in the uevent
environment block (in the <literal>SYNTH_UUID=</literal> property) and may be used to track
delivery of the generated events.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--wait-daemon[=<replaceable>SECONDS</replaceable>]</option></term>
<listitem>

View File

@ -14,7 +14,7 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free,
char, string_hash_func, string_compare_func, free);
DEFINE_HASH_OPS_FULL(string_hash_ops_free_free,
char, string_hash_func, string_compare_func, free,
char, free);
void, free);
void path_hash_func(const char *q, struct siphash *state) {
size_t n;
@ -57,6 +57,9 @@ void path_hash_func(const char *q, struct siphash *state) {
DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare);
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free,
char, path_hash_func, path_compare, free);
DEFINE_HASH_OPS_FULL(path_hash_ops_free_free,
char, path_hash_func, path_compare, free,
void, free);
void trivial_hash_func(const void *p, struct siphash *state) {
siphash24_compress(&p, sizeof(p), state);

View File

@ -82,6 +82,7 @@ extern const struct hash_ops string_hash_ops_free_free;
void path_hash_func(const char *p, struct siphash *state);
extern const struct hash_ops path_hash_ops;
extern const struct hash_ops path_hash_ops_free;
extern const struct hash_ops path_hash_ops_free_free;
/* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings
* or suchlike. */

View File

@ -68,6 +68,7 @@ struct sd_dhcp_server {
bool emit_router;
Hashmap *leases_by_client_id;
Hashmap *static_leases_by_client_id;
DHCPLease **bound_leases;
DHCPLease invalid_lease;

View File

@ -32,6 +32,8 @@ static DHCPLease *dhcp_lease_free(DHCPLease *lease) {
return mfree(lease);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPLease*, dhcp_lease_free);
/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
* the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
* moreover, the server's own address may be in the pool, and is in that case reserved in order not to
@ -160,6 +162,7 @@ static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
free(server->servers[i].addr);
hashmap_free(server->leases_by_client_id);
hashmap_free(server->static_leases_by_client_id);
ordered_set_free(server->extra_options);
ordered_set_free(server->vendor_options);
@ -201,6 +204,9 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
server->leases_by_client_id = hashmap_new(&dhcp_lease_hash_ops);
if (!server->leases_by_client_id)
return -ENOMEM;
server->static_leases_by_client_id = hashmap_new(&dhcp_lease_hash_ops);
if (!server->static_leases_by_client_id)
return -ENOMEM;
*ret = TAKE_PTR(server);
@ -802,13 +808,55 @@ static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *messag
return -EBADMSG;
}
static int prepare_new_lease(
DHCPLease **ret_lease,
be32_t address,
const DHCPClientId *client_id,
const uint8_t chaddr[static ETH_ALEN],
be32_t gateway,
usec_t expiration) {
_cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
lease = new(DHCPLease, 1);
if (!lease)
return -ENOMEM;
*lease = (DHCPLease) {
.address = address,
.client_id.length = client_id->length,
.gateway = gateway,
.expiration = expiration,
};
lease->client_id.data = memdup(client_id->data, client_id->length);
if (!lease->client_id.data)
return -ENOMEM;
memcpy(&lease->chaddr, chaddr, ETH_ALEN);
*ret_lease = TAKE_PTR(lease);
return 0;
}
static bool static_leases_have_address(sd_dhcp_server *server, be32_t address) {
DHCPLease *s;
assert(server);
HASHMAP_FOREACH(s, server->static_leases_by_client_id)
if (s->address == address)
return true;
return false;
}
#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
size_t length) {
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length) {
_cleanup_(dhcp_request_freep) DHCPRequest *req = NULL;
_cleanup_free_ char *error_message = NULL;
DHCPLease *existing_lease;
DHCPLease *existing_lease, *static_lease;
int type, r;
assert(server);
@ -832,8 +880,8 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
/* this only fails on critical errors */
return r;
existing_lease = hashmap_get(server->leases_by_client_id,
&req->client_id);
existing_lease = hashmap_get(server->leases_by_client_id, &req->client_id);
static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id);
switch(type) {
@ -841,15 +889,16 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
be32_t address = INADDR_ANY;
unsigned i;
log_dhcp_server(server, "DISCOVER (0x%x)",
be32toh(req->message->xid));
log_dhcp_server(server, "DISCOVER (0x%x)", be32toh(req->message->xid));
if (!server->pool_size)
/* no pool allocated */
return 0;
/* for now pick a random free address from the pool */
if (existing_lease)
if (static_lease)
address = static_lease->address;
else if (existing_lease)
address = existing_lease->address;
else {
struct siphash state;
@ -867,8 +916,11 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
for (i = 0; i < server->pool_size; i++) {
if (!server->bound_leases[next_offer]) {
address = server->subnet | htobe32(server->pool_offset + next_offer);
break;
be32_t tmp = server->subnet | htobe32(server->pool_offset + next_offer);
if (!static_leases_have_address(server, tmp)) {
address = tmp;
break;
}
}
next_offer = (next_offer + 1) % server->pool_size;
@ -948,63 +1000,82 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
/* verify that the requested address is from the pool, and either
owned by the current client or free */
if (pool_offset >= 0 &&
server->bound_leases[pool_offset] == existing_lease) {
DHCPLease *lease;
usec_t time_now = 0;
if (pool_offset >= 0 && static_lease) {
_cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL, *old_lease = NULL;
usec_t time_now, expiration;
if (!existing_lease) {
lease = new0(DHCPLease, 1);
if (!lease)
return -ENOMEM;
lease->address = address;
lease->client_id.data = memdup(req->client_id.data,
req->client_id.length);
if (!lease->client_id.data) {
free(lease);
return -ENOMEM;
}
lease->client_id.length = req->client_id.length;
memcpy(&lease->chaddr, &req->message->chaddr,
ETH_ALEN);
lease->gateway = req->message->giaddr;
} else
lease = existing_lease;
r = sd_event_now(server->event,
clock_boottime_or_monotonic(),
&time_now);
if (r < 0) {
if (!existing_lease)
dhcp_lease_free(lease);
r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
return r;
}
lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
expiration = usec_add(req->lifetime * USEC_PER_SEC, time_now);
r = prepare_new_lease(&lease, static_lease->address, &req->client_id,
req->message->chaddr, req->message->giaddr, expiration);
if (r < 0)
return r;
r = server_send_offer_or_ack(server, req, address, DHCP_ACK);
if (r < 0) {
if (r < 0)
/* this only fails on critical errors */
log_dhcp_server_errno(server, r, "Could not send ack: %m");
return log_dhcp_server_errno(server, r, "Could not send ack: %m");
if (!existing_lease)
dhcp_lease_free(lease);
log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid));
server->bound_leases[pool_offset] = lease;
old_lease = hashmap_remove(server->leases_by_client_id, &lease->client_id);
r = hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
if (r < 0)
return log_dhcp_server_errno(server, r, "Could not save lease: %m");
TAKE_PTR(lease);
if (server->callback)
server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
return DHCP_ACK;
} else if (pool_offset >= 0 && server->bound_leases[pool_offset] == existing_lease) {
_cleanup_(dhcp_lease_freep) DHCPLease *new_lease = NULL;
usec_t time_now, expiration;
DHCPLease *lease;
r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
return r;
expiration = usec_add(req->lifetime * USEC_PER_SEC, time_now);
if (!existing_lease) {
r = prepare_new_lease(&new_lease, address, &req->client_id,
req->message->chaddr, req->message->giaddr, expiration);
if (r < 0)
return r;
lease = new_lease;
} else {
log_dhcp_server(server, "ACK (0x%x)",
be32toh(req->message->xid));
server->bound_leases[pool_offset] = lease;
hashmap_put(server->leases_by_client_id,
&lease->client_id, lease);
if (server->callback)
server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
return DHCP_ACK;
existing_lease->expiration = expiration;
lease = existing_lease;
}
r = server_send_offer_or_ack(server, req, address, DHCP_ACK);
if (r < 0)
/* this only fails on critical errors */
return log_dhcp_server_errno(server, r, "Could not send ack: %m");
log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid));
server->bound_leases[pool_offset] = lease;
r = hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
if (r < 0)
return log_dhcp_server_errno(server, r, "Could not save lease: %m");
TAKE_PTR(new_lease);
if (server->callback)
server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
return DHCP_ACK;
} else if (init_reboot) {
r = server_send_nak(server, req);
if (r < 0)
@ -1397,3 +1468,61 @@ int sd_dhcp_server_set_relay_agent_information(
free_and_replace(server->agent_remote_id, remote_id_dup);
return 0;
}
int sd_dhcp_server_set_static_lease(
sd_dhcp_server *server,
const struct in_addr *address,
uint8_t *client_id,
size_t client_id_size) {
_cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL, *old = NULL;
DHCPClientId c;
int r;
assert_return(server, -EINVAL);
assert_return(client_id, -EINVAL);
assert_return(client_id_size == ETH_ALEN + 1, -EINVAL);
assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
/* Static lease with an empty or omitted address is a valid entry,
* the server removes any static lease with the specified mac address. */
if (!address || address->s_addr == 0) {
_cleanup_free_ void *data = NULL;
data = memdup(client_id, client_id_size);
if (!data)
return -ENOMEM;
c = (DHCPClientId) {
.length = client_id_size,
.data = data,
};
old = hashmap_remove(server->static_leases_by_client_id, &c);
return 0;
}
if (static_leases_have_address(server, address->s_addr))
return -EEXIST;
lease = new(DHCPLease, 1);
if (!lease)
return -ENOMEM;
*lease = (DHCPLease) {
.address = address->s_addr,
.client_id.length = client_id_size,
.gateway = 0,
.expiration = 0,
};
lease->client_id.data = memdup(client_id, client_id_size);
if (!lease->client_id.data)
return -ENOMEM;
r = hashmap_ensure_put(&server->static_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
if (r < 0)
return r;
TAKE_PTR(lease);
return 0;
}

View File

@ -757,4 +757,6 @@ global:
sd_device_monitor_filter_add_match_sysattr;
sd_device_monitor_filter_add_match_parent;
sd_device_get_usec_initialized;
sd_device_trigger_with_uuid;
sd_device_get_trigger_uuid;
} LIBSYSTEMD_248;

View File

@ -16,6 +16,7 @@
#include "fileio.h"
#include "fs-util.h"
#include "hashmap.h"
#include "id128-util.h"
#include "macro.h"
#include "parse-util.h"
#include "path-util.h"
@ -1869,6 +1870,34 @@ _public_ int sd_device_get_property_value(sd_device *device, const char *key, co
return 0;
}
_public_ int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret) {
const char *s;
sd_id128_t id;
int r;
assert_return(device, -EINVAL);
/* Retrieves the UUID attached to a uevent when triggering it from userspace via
* sd_device_trigger_with_uuid() or an equivalent interface. Returns -ENOENT if the record is not
* caused by a synthetic event and -ENODATA if it was but no UUID was specified */
r = sd_device_get_property_value(device, "SYNTH_UUID", &s);
if (r < 0)
return r;
if (streq(s, "0")) /* SYNTH_UUID=0 is set whenever a device is triggered by userspace without specifying a UUID */
return -ENODATA;
r = sd_id128_from_string(s, &id);
if (r < 0)
return r;
if (ret)
*ret = id;
return 0;
}
static int device_cache_sysattr_value(sd_device *device, const char *key, char *value) {
_cleanup_free_ char *new_key = NULL, *old_value = NULL;
int r;
@ -2096,5 +2125,41 @@ _public_ int sd_device_trigger(sd_device *device, sd_device_action_t action) {
if (!s)
return -EINVAL;
/* This uses the simple no-UUID interface of kernel < 4.13 */
return sd_device_set_sysattr_value(device, "uevent", s);
}
_public_ int sd_device_trigger_with_uuid(
sd_device *device,
sd_device_action_t action,
sd_id128_t *ret_uuid) {
char buf[ID128_UUID_STRING_MAX];
const char *s, *j;
sd_id128_t u;
int r;
assert_return(device, -EINVAL);
/* If noone wants to know the UUID, use the simple interface from pre-4.13 times */
if (!ret_uuid)
return sd_device_trigger(device, action);
s = device_action_to_string(action);
if (!s)
return -EINVAL;
r = sd_id128_randomize(&u);
if (r < 0)
return r;
id128_to_uuid_string(u, buf);
j = strjoina(s, " ", buf);
r = sd_device_set_sysattr_value(device, "uevent", j);
if (r < 0)
return r;
*ret_uuid = u;
return 0;
}

View File

@ -69,6 +69,8 @@ sources = files('''
networkd-dhcp-common.h
networkd-dhcp-server-bus.c
networkd-dhcp-server-bus.h
networkd-dhcp-server-static-lease.c
networkd-dhcp-server-static-lease.h
networkd-dhcp-server.c
networkd-dhcp-server.h
networkd-dhcp4.c

View File

@ -0,0 +1,212 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
#include "ether-addr-util.h"
#include "hashmap.h"
#include "networkd-dhcp-server-static-lease.h"
#include "networkd-network.h"
#include "networkd-util.h"
DEFINE_NETWORK_SECTION_FUNCTIONS(DHCPStaticLease, dhcp_static_lease_free);
DHCPStaticLease *dhcp_static_lease_free(DHCPStaticLease *static_lease) {
if (!static_lease)
return NULL;
if (static_lease->network && static_lease->section)
hashmap_remove(static_lease->network->dhcp_static_leases_by_section, static_lease->section);
network_config_section_free(static_lease->section);
free(static_lease->client_id);
return mfree(static_lease);
}
static int dhcp_static_lease_new(DHCPStaticLease **ret) {
DHCPStaticLease *p;
assert(ret);
p = new0(DHCPStaticLease, 1);
if (!p)
return -ENOMEM;
*ret = TAKE_PTR(p);
return 0;
}
static int lease_new_static(Network *network, const char *filename, unsigned section_line, DHCPStaticLease **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(dhcp_static_lease_freep) DHCPStaticLease *static_lease = NULL;
int r;
assert(network);
assert(filename);
assert(section_line > 0);
assert(ret);
r = network_config_section_new(filename, section_line, &n);
if (r < 0)
return r;
static_lease = hashmap_get(network->dhcp_static_leases_by_section, n);
if (static_lease) {
*ret = TAKE_PTR(static_lease);
return 0;
}
r = dhcp_static_lease_new(&static_lease);
if (r < 0)
return r;
static_lease->network = network;
static_lease->section = TAKE_PTR(n);
r = hashmap_ensure_put(&network->dhcp_static_leases_by_section, &network_config_hash_ops, static_lease->section, static_lease);
if (r < 0)
return r;
*ret = TAKE_PTR(static_lease);
return 0;
}
static int static_lease_verify(DHCPStaticLease *static_lease) {
if (section_is_invalid(static_lease->section))
return -EINVAL;
if (in4_addr_is_null(&static_lease->address))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: DHCP static lease without Address= field configured. "
"Ignoring [DHCPServerStaticLease] section from line %u.",
static_lease->section->filename, static_lease->section->line);
/* TODO: check that the address is in the pool. */
if (static_lease->client_id_size == 0 || !static_lease->client_id)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: DHCP static lease without MACAddress= field configured. "
"Ignoring [DHCPServerStaticLease] section from line %u.",
static_lease->section->filename, static_lease->section->line);
assert(static_lease->client_id_size == ETH_ALEN + 1);
return 0;
}
void network_drop_invalid_static_leases(Network *network) {
DHCPStaticLease *static_lease;
assert(network);
HASHMAP_FOREACH(static_lease, network->dhcp_static_leases_by_section)
if (static_lease_verify(static_lease) < 0)
dhcp_static_lease_free(static_lease);
}
int config_parse_dhcp_static_lease_address(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(dhcp_static_lease_free_or_set_invalidp) DHCPStaticLease *lease = NULL;
Network *network = userdata;
union in_addr_union addr;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(network);
r = lease_new_static(network, filename, section_line, &lease);
if (r < 0)
return log_oom();
if (isempty(rvalue)) {
lease->address.s_addr = 0;
TAKE_PTR(lease);
return 0;
}
r = in_addr_from_string(AF_INET, rvalue, &addr);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse IPv4 address for DHCPv4 static lease, ignoring assignment: %s", rvalue);
return 0;
}
if (in4_addr_is_null(&addr.in)) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
"IPv4 address for DHCPv4 static lease cannot be the ANY address, ignoring assignment: %s", rvalue);
return 0;
}
lease->address = addr.in;
TAKE_PTR(lease);
return 0;
}
int config_parse_dhcp_static_lease_hwaddr(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(dhcp_static_lease_free_or_set_invalidp) DHCPStaticLease *lease = NULL;
Network *network = userdata;
struct ether_addr hwaddr;
uint8_t *c;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(network);
r = lease_new_static(network, filename, section_line, &lease);
if (r < 0)
return log_oom();
if (isempty(rvalue)) {
lease->client_id = mfree(lease->client_id);
lease->client_id_size = 0;
return 0;
}
r = ether_addr_from_string(rvalue, &hwaddr);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse MAC address for DHCPv4 static lease, ignoring assignment: %s", rvalue);
return 0;
}
if (ether_addr_is_null(&hwaddr) || (hwaddr.ether_addr_octet[0] & 0x01)) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
"MAC address for DHCPv4 static lease cannot be null or multicast, ignoring assignment: %s", rvalue);
return 0;
}
c = new(uint8_t, ETH_ALEN + 1);
if (!c)
return log_oom();
/* set client id type to 1: Ethernet Link-Layer (RFC 2132) */
c[0] = 0x01;
memcpy(c + 1, &hwaddr, ETH_ALEN);
free_and_replace(lease->client_id, c);
lease->client_id_size = ETH_ALEN + 1;
TAKE_PTR(lease);
return 0;
}

View File

@ -0,0 +1,26 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <inttypes.h>
#include "conf-parser.h"
#include "in-addr-util.h"
typedef struct Network Network;
typedef struct NetworkConfigSection NetworkConfigSection;
typedef struct DHCPStaticLease {
Network *network;
NetworkConfigSection *section;
struct in_addr address;
uint8_t *client_id;
size_t client_id_size;
} DHCPStaticLease;
DHCPStaticLease *dhcp_static_lease_free(DHCPStaticLease *lease);
void network_drop_invalid_static_leases(Network *network);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_static_lease_address);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_static_lease_hwaddr);

View File

@ -9,8 +9,9 @@
#include "fd-util.h"
#include "fileio.h"
#include "networkd-address.h"
#include "networkd-dhcp-server.h"
#include "networkd-dhcp-server-bus.h"
#include "networkd-dhcp-server-static-lease.h"
#include "networkd-dhcp-server.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-network.h"
@ -291,6 +292,7 @@ static int dhcp4_server_set_dns_from_resolve_conf(Link *link) {
int dhcp4_server_configure(Link *link) {
bool acquired_uplink = false;
sd_dhcp_option *p;
DHCPStaticLease *static_lease;
Link *uplink = NULL;
Address *address;
bool bind_to_interface;
@ -439,6 +441,12 @@ int dhcp4_server_configure(Link *link) {
return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m");
}
HASHMAP_FOREACH(static_lease, link->network->dhcp_static_leases_by_section) {
r = sd_dhcp_server_set_static_lease(link->dhcp_server, &static_lease->address, static_lease->client_id, static_lease->client_id_size);
if (r < 0)
return log_link_error_errno(link, r, "Failed to set DHCPv4 static lease for DHCP server: %m");
}
if (!sd_dhcp_server_is_running(link->dhcp_server)) {
r = sd_dhcp_server_start(link->dhcp_server);
if (r < 0)

View File

@ -23,7 +23,6 @@
#define NDISC_DNSSL_MAX 64U
#define NDISC_RDNSS_MAX 64U
#define NDISC_PREFIX_LFT_MIN 7200U
#define DAD_CONFLICTS_IDGEN_RETRIES_RFC7217 3
@ -755,7 +754,7 @@ static int ndisc_router_generate_addresses(Link *link, struct in6_addr *address,
}
static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
uint32_t lifetime_valid, lifetime_preferred, lifetime_remaining;
uint32_t lifetime_valid, lifetime_preferred;
_cleanup_set_free_free_ Set *addresses = NULL;
struct in6_addr addr, *a;
unsigned prefixlen;
@ -777,6 +776,11 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
if (r < 0)
return log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m");
if (lifetime_valid == 0) {
log_link_debug(link, "Ignoring prefix as its valid lifetime is zero.");
return 0;
}
r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m");
@ -795,35 +799,29 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
SET_FOREACH(a, addresses) {
_cleanup_(address_freep) Address *address = NULL;
Address *existing_address;
Address *e;
r = address_new(&address);
if (r < 0)
return log_oom();
address->family = AF_INET6;
address->in_addr.in6 = *a;
address->prefixlen = prefixlen;
address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
address->cinfo.ifa_valid = lifetime_valid;
address->cinfo.ifa_prefered = lifetime_preferred;
address->in_addr.in6 = *a;
/* see RFC4862 section 5.5.3.e */
r = address_get(link, address, &existing_address);
/* See RFC4862, section 5.5.3.e. But the following logic is deviated from RFC4862 by
* honoring all valid lifetimes to improve the reaction of SLAAC to renumbering events.
* See draft-ietf-6man-slaac-renum-02, section 4.2. */
r = address_get(link, address, &e);
if (r > 0) {
lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC;
if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining)
address->cinfo.ifa_valid = lifetime_valid;
else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN)
address->cinfo.ifa_valid = lifetime_remaining;
else
address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
} else if (lifetime_valid > 0)
address->cinfo.ifa_valid = lifetime_valid;
else
continue; /* see RFC4862 section 5.5.3.d */
if (address->cinfo.ifa_valid == 0)
continue;
/* If the address is already assigned, but not valid anymore, then refuse to
* update the address. */
if (e->cinfo.tstamp / 100 + e->cinfo.ifa_valid < time_now / USEC_PER_SEC)
continue;
}
r = ndisc_request_address(TAKE_PTR(address), link, rt);
if (r < 0)
@ -890,14 +888,14 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
r = sd_ndisc_router_route_get_lifetime(rt, &lifetime);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get gateway lifetime from RA: %m");
return log_link_error_errno(link, r, "Failed to get route lifetime from RA: %m");
if (lifetime == 0)
return 0;
r = sd_ndisc_router_route_get_address(rt, &dst);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get route address: %m");
return log_link_error_errno(link, r, "Failed to get route destination address: %m");
if ((!set_isempty(link->network->ndisc_allow_listed_route_prefix) &&
!set_contains(link->network->ndisc_allow_listed_route_prefix, &dst)) ||

View File

@ -12,6 +12,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
#include "networkd-bridge-fdb.h"
#include "networkd-can.h"
#include "networkd-dhcp-common.h"
#include "networkd-dhcp-server-static-lease.h"
#include "networkd-dhcp-server.h"
#include "networkd-dhcp4.h"
#include "networkd-dhcp6.h"
@ -288,6 +289,8 @@ DHCPServer.PoolSize, config_parse_uint32,
DHCPServer.SendVendorOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_server_send_vendor_options)
DHCPServer.SendOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_server_send_options)
DHCPServer.BindToInterface, config_parse_bool, 0, offsetof(Network, dhcp_server_bind_to_interface)
DHCPServerStaticLease.Address, config_parse_dhcp_static_lease_address, 0, 0
DHCPServerStaticLease.MACAddress, config_parse_dhcp_static_lease_hwaddr, 0, 0
Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost)
Bridge.UseBPDU, config_parse_tristate, 0, offsetof(Network, use_bpdu)
Bridge.HairPin, config_parse_tristate, 0, offsetof(Network, hairpin)

View File

@ -17,6 +17,7 @@
#include "networkd-address.h"
#include "networkd-bridge-fdb.h"
#include "networkd-dhcp-common.h"
#include "networkd-dhcp-server-static-lease.h"
#include "networkd-dhcp-server.h"
#include "networkd-manager.h"
#include "networkd-mdb.h"
@ -241,6 +242,7 @@ int network_verify(Network *network) {
network_drop_invalid_routing_policy_rules(network);
network_drop_invalid_traffic_control(network);
network_drop_invalid_sr_iov(network);
network_drop_invalid_static_leases(network);
network_adjust_dhcp_server(network);
@ -414,6 +416,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
"DHCPv6\0"
"DHCPv6PrefixDelegation\0"
"DHCPServer\0"
"DHCPServerStaticLease\0"
"IPv6AcceptRA\0"
"IPv6NDPProxyAddress\0"
"Bridge\0"
@ -607,6 +610,7 @@ static Network *network_free(Network *network) {
hashmap_free_with_destructor(network->prefixes_by_section, prefix_free);
hashmap_free_with_destructor(network->route_prefixes_by_section, route_prefix_free);
hashmap_free_with_destructor(network->rules_by_section, routing_policy_rule_free);
hashmap_free_with_destructor(network->dhcp_static_leases_by_section, dhcp_static_lease_free);
ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);

View File

@ -315,6 +315,7 @@ struct Network {
Hashmap *prefixes_by_section;
Hashmap *route_prefixes_by_section;
Hashmap *rules_by_section;
Hashmap *dhcp_static_leases_by_section;
OrderedHashmap *tc_by_section;
OrderedHashmap *sr_iov_by_section;

View File

@ -24,6 +24,7 @@
#include <sys/types.h>
#include "sd-event.h"
#include "sd-id128.h"
#include "_sd-common.h"
@ -96,11 +97,13 @@ const char *sd_device_get_sysattr_next(sd_device *device);
int sd_device_has_tag(sd_device *device, const char *tag);
int sd_device_has_current_tag(sd_device *device, const char *tag);
int sd_device_get_property_value(sd_device *device, const char *key, const char **value);
int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret);
int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value);
int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *value);
int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr, const char *format, ...) _sd_printf_(3, 4);
int sd_device_trigger(sd_device *device, sd_device_action_t action);
int sd_device_trigger_with_uuid(sd_device *device, sd_device_action_t action, sd_id128_t *ret_uuid);
/* device enumerator */

View File

@ -77,6 +77,7 @@ int sd_dhcp_server_set_smtp(sd_dhcp_server *server, const struct in_addr smtp[],
int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v);
int sd_dhcp_server_add_vendor_option(sd_dhcp_server *server, sd_dhcp_option *v);
int sd_dhcp_server_set_static_lease(sd_dhcp_server *server, const struct in_addr *address, uint8_t *client_id, size_t client_id_size);
int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t);
int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t);

View File

@ -24,8 +24,14 @@
static bool arg_verbose = false;
static bool arg_dry_run = false;
static bool arg_quiet = false;
static bool arg_uuid = false;
static int exec_list(sd_device_enumerator *e, sd_device_action_t action, Set **settle_set) {
static int exec_list(
sd_device_enumerator *e,
sd_device_action_t action,
Hashmap *settle_hashmap) {
bool skip_uuid_logic = false;
const char *action_str;
sd_device *d;
int r, ret = 0;
@ -33,18 +39,33 @@ static int exec_list(sd_device_enumerator *e, sd_device_action_t action, Set **s
action_str = device_action_to_string(action);
FOREACH_DEVICE_AND_SUBSYSTEM(e, d) {
sd_id128_t id = SD_ID128_NULL;
const char *syspath;
if (sd_device_get_syspath(d, &syspath) < 0)
r = sd_device_get_syspath(d, &syspath);
if (r < 0) {
log_debug_errno(r, "Failed to get syspath of enumerated devices, ignoring: %m");
continue;
}
if (arg_verbose)
printf("%s\n", strna(syspath));
printf("%s\n", syspath);
if (arg_dry_run)
continue;
r = sd_device_trigger(d, action);
/* Use the UUID mode if the user explicitly asked for it, or if --settle has been specified,
* so that we can recognize our own uevent. */
r = sd_device_trigger_with_uuid(d, action, (arg_uuid || settle_hashmap) && !skip_uuid_logic ? &id : NULL);
if (r == -EINVAL && !arg_uuid && settle_hashmap && !skip_uuid_logic) {
/* If we specified a UUID because of the settling logic, and we got EINVAL this might
* be caused by an old kernel which doesn't know the UUID logic (pre-4.13). Let's try
* if it works without the UUID logic then. */
r = sd_device_trigger(d, action);
if (r != -EINVAL)
skip_uuid_logic = true; /* dropping the uuid stuff changed the return code,
* hence don't bother next time */
}
if (r < 0) {
/* ENOENT may be returned when a device does not have /uevent or is already
* removed. Hence, this is logged at debug level and ignored.
@ -86,10 +107,28 @@ static int exec_list(sd_device_enumerator *e, sd_device_action_t action, Set **s
continue;
}
if (settle_set) {
r = set_put_strdup(settle_set, syspath);
/* If the user asked for it, write event UUID to stdout */
if (arg_uuid)
printf(SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id));
if (settle_hashmap) {
_cleanup_free_ sd_id128_t *mid = NULL;
_cleanup_free_ char *sp = NULL;
sp = strdup(syspath);
if (!sp)
return log_oom();
mid = newdup(sd_id128_t, &id, 1);
if (!d)
return log_oom();
r = hashmap_put(settle_hashmap, sp, mid);
if (r < 0)
return log_oom();
TAKE_PTR(sp);
TAKE_PTR(mid);
}
}
@ -97,24 +136,51 @@ static int exec_list(sd_device_enumerator *e, sd_device_action_t action, Set **s
}
static int device_monitor_handler(sd_device_monitor *m, sd_device *dev, void *userdata) {
_cleanup_free_ char *val = NULL;
Set *settle_set = userdata;
Hashmap *settle_hashmap = userdata;
sd_id128_t *settle_id;
const char *syspath;
char *k;
int r;
assert(dev);
assert(settle_set);
assert(settle_hashmap);
if (sd_device_get_syspath(dev, &syspath) < 0)
r = sd_device_get_syspath(dev, &syspath);
if (r < 0) {
log_debug_errno(r, "Failed to get syspath of device event, ignoring: %m");
return 0;
}
settle_id = hashmap_get2(settle_hashmap, syspath, (void**) &k);
if (!settle_id) {
log_debug("Got uevent for unexpected device '%s', ignoring.", syspath);
return 0;
}
if (!sd_id128_is_null(*settle_id)) { /* If this is SD_ID128_NULL then we are on pre-4.13 and have no UUID to check, hence don't */
sd_id128_t event_id;
r = sd_device_get_trigger_uuid(dev, &event_id);
if (r < 0) {
log_debug_errno(r, "Got uevent without synthetic UUID for device '%s', ignoring: %m", syspath);
return 0;
}
if (!sd_id128_equal(event_id, *settle_id)) {
log_debug("Got uevent not matching expected UUID for device '%s', ignoring.", syspath);
return 0;
}
}
if (arg_verbose)
printf("settle %s\n", syspath);
val = set_remove(settle_set, syspath);
if (!val)
log_debug("Got epoll event on syspath %s not present in syspath set", syspath);
if (arg_uuid)
printf("settle " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(*settle_id));
if (set_isempty(settle_set))
free(hashmap_remove(settle_hashmap, syspath));
free(k);
if (hashmap_isempty(settle_hashmap))
return sd_event_exit(sd_device_monitor_get_event(m), 0);
return 0;
@ -162,7 +228,8 @@ static int help(void) {
" -b --parent-match=NAME Trigger devices with that parent device\n"
" -w --settle Wait for the triggered events to complete\n"
" --wait-daemon[=SECONDS] Wait for udevd daemon to be initialized\n"
" before triggering uevents\n",
" before triggering uevents\n"
" --uuid Print synthetic uevent UUID\n",
program_invocation_short_name);
return 0;
@ -172,6 +239,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
enum {
ARG_NAME = 0x100,
ARG_PING,
ARG_UUID,
};
static const struct option options[] = {
@ -193,6 +261,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
{ "wait-daemon", optional_argument, NULL, ARG_PING },
{ "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{ "uuid", no_argument, NULL, ARG_UUID },
{}
};
enum {
@ -203,7 +272,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_set_free_ Set *settle_set = NULL;
_cleanup_hashmap_free_ Hashmap *settle_hashmap = NULL;
usec_t ping_timeout_usec = 5 * USEC_PER_SEC;
bool settle = false, ping = false;
int c, r;
@ -327,7 +396,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
break;
}
case ARG_PING: {
case ARG_PING:
ping = true;
if (optarg) {
r = parse_sec(optarg, &ping_timeout_usec);
@ -335,7 +404,10 @@ int trigger_main(int argc, char *argv[], void *userdata) {
log_error_errno(r, "Failed to parse timeout value '%s', ignoring: %m", optarg);
}
break;
}
case ARG_UUID:
arg_uuid = true;
break;
case 'V':
return print_version();
@ -377,8 +449,8 @@ int trigger_main(int argc, char *argv[], void *userdata) {
}
if (settle) {
settle_set = set_new(&string_hash_ops_free);
if (!settle_set)
settle_hashmap = hashmap_new(&path_hash_ops_free_free);
if (!settle_hashmap)
return log_oom();
r = sd_event_default(&event);
@ -393,7 +465,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to attach event to device monitor: %m");
r = sd_device_monitor_start(m, device_monitor_handler, settle_set);
r = sd_device_monitor_start(m, device_monitor_handler, settle_hashmap);
if (r < 0)
return log_error_errno(r, "Failed to start device monitor: %m");
}
@ -413,11 +485,11 @@ int trigger_main(int argc, char *argv[], void *userdata) {
assert_not_reached("Unknown device type");
}
r = exec_list(e, action, settle ? &settle_set : NULL);
r = exec_list(e, action, settle_hashmap);
if (r < 0)
return r;
if (event && !set_isempty(settle_set)) {
if (event && !hashmap_isempty(settle_hashmap)) {
r = sd_event_loop(event);
if (r < 0)
return log_error_errno(r, "Event loop failed: %m");

View File

@ -0,0 +1,29 @@
[Match]
Name=veth-peer
[Network]
Address=10.1.1.1/24
DHCPServer=true
IPMasquerade=true
IPForward=true
[DHCPServer]
PoolOffset=0
PoolSize=20
EmitDNS=yes
DNS=9.9.9.9
[DHCPServerStaticLease]
MACAddress=12:34:56:78:9a:bc
Address=10.1.1.2
[DHCPServerStaticLease]
MACAddress=12:34:56:78:9a:bc
Address=10.1.1.3
[DHCPServerStaticLease]
Address=10.1.1.4
[DHCPServerStaticLease]
MACAddress=12:34:56:78:9a:bf
Address=10.1.1.5

View File

@ -366,6 +366,9 @@ RelayTarget=
RelayAgentCircuitId=
RelayAgentRemoteId=
ServerAddress=
[DHCPServerStaticLease]
MACAddress=
Address=
[NextHop]
Id=
Gateway=

View File

@ -0,0 +1,9 @@
[Match]
Name=veth99
[Network]
IPv6AcceptRA=no
DHCP=ipv4
[DHCPv4]
ClientIdentifier=mac

View File

@ -0,0 +1,17 @@
[Match]
Name=veth-peer
[Network]
IPv6AcceptRA=no
Address=10.1.1.1/24
DHCPServer=yes
[DHCPServer]
PoolOffset=0
PoolSize=20
EmitDNS=yes
DNS=9.9.9.9
[DHCPServerStaticLease]
MACAddress=12:34:56:78:9a:bc
Address=10.1.1.3

View File

@ -3673,8 +3673,10 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
units = [
'25-veth.netdev',
'dhcp-client.network',
'dhcp-client-static-lease.network',
'dhcp-client-timezone-router.network',
'dhcp-server.network',
'dhcp-server-static-lease.network',
'dhcp-server-timezone-router.network']
def setUp(self):
@ -3709,6 +3711,15 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
self.assertRegex(output, '192.168.5.*')
self.assertRegex(output, 'Europe/Berlin')
def test_dhcp_server_static_lease(self):
copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client-static-lease.network', 'dhcp-server-static-lease.network')
start_networkd()
self.wait_online(['veth99:routable', 'veth-peer:routable'])
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
print(output)
self.assertIn('10.1.1.3 (DHCP4 via 10.1.1.1)', output)
class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
links = [
'client',