1
0
mirror of https://github.com/systemd/systemd synced 2026-04-17 12:34:51 +02:00

Compare commits

...

36 Commits

Author SHA1 Message Date
Yu Watanabe
b0f83c2d82
Merge pull request #22254 from yuwata/dhcp-server-fix-segfault
sd-dhcp-server: remove lease with hashmap_remove_value()
2022-02-01 14:10:11 +09:00
Yu Watanabe
a8dfcd2c0f
Merge pull request #22319 from yuwata/network-use-reconfigure
network: also use link_reconfigure_impl() to initially assign .network file
2022-02-01 14:09:32 +09:00
James Hilliard
04660b10d3 meson: use full argument names for bpftool gen commands
This should be a purely cosmetic change.
2022-02-01 12:26:30 +09:00
Yu Watanabe
a46abf2e34 fuzz-dhcp-server: add static leases 2022-02-01 11:56:22 +09:00
Yu Watanabe
6796c5a9c4 test-dhcp-server: add tests for static lease 2022-02-01 11:56:22 +09:00
Yu Watanabe
7b5445e74e test-dhcp-server: add tests for setting static DHCP lease 2022-02-01 11:56:22 +09:00
Yu Watanabe
99e65b7df3 test-dhcp-server: add usual headers 2022-02-01 11:56:22 +09:00
Yu Watanabe
4f3cb2465a test-dhcp-server: run a test earlier which does not require privilege 2022-02-01 11:56:22 +09:00
Yu Watanabe
4e2319afe4 test-dhcp-server: move sd-event allocation 2022-02-01 11:56:22 +09:00
Yu Watanabe
7e0a8bf1ce test-dhcp-server: use log_tests_skipped_errno() 2022-02-01 11:56:22 +09:00
Yu Watanabe
8b572f7ab2 sd-dhcp-server: split out logic to ACK request 2022-02-01 11:56:22 +09:00
Yu Watanabe
bd1a3eb65b sd-dhcp-server: do not assign address reserved for static leases to non-matching clients
This fix the root cause of the issue #22253.
2022-02-01 11:56:22 +09:00
Yu Watanabe
7e98fe05a0 sd-dhcp-server: explicitly refuse when conflicting address is requested 2022-02-01 11:56:22 +09:00
Yu Watanabe
e2ba408084 sd-dhcp-server: do not assign an address from pool when a static lease for the client ID exists 2022-02-01 11:56:22 +09:00
Yu Watanabe
5cc8be890d sd-dhcp-server: rename get_pool_offset() -> address_is_in_pool()
As, the value of pool_offset is not used.
2022-02-01 11:56:22 +09:00
Yu Watanabe
eb5bff9c9d sd-dhcp-server: rename server_send_nak() -> server_send_nak_or_ignore()
And logs error in the function.
2022-02-01 11:56:01 +09:00
Yu Watanabe
8a7d048d1d sd-dhcp-server: set DHCPLease::server before hashmap_put()
Otherwise, if the second push is failed, then the first hashmap contains
dirty entry.

Also, this makes hashmap_remove_value() used when removing leases to
make not wrong lease is removed from the hashmap.

Note, this just hide the root cause of the issue #22253, which will be
fixed in later commit.

Fixes #22253.
2022-02-01 11:45:51 +09:00
Yu Watanabe
37e219800f sd-dhcp-server: fix indentation 2022-02-01 11:45:51 +09:00
Yu Watanabe
48be485b71 sd-dhcp-server: rename argument and add one missing assertion 2022-02-01 11:45:51 +09:00
Yu Watanabe
6277e48fa9 sd-dhcp-server: do not use implicit cast to boolean from integer 2022-02-01 11:45:51 +09:00
Yu Watanabe
a2a801926d sd-dhcp-server: refuse zero length client ID 2022-02-01 11:45:51 +09:00
Yu Watanabe
0a195d4186 sd-dhcp-server: do not log "STOPPED" when already stopped 2022-02-01 11:45:51 +09:00
Yu Watanabe
fb96111946 network: also use link_reconfigure_impl() to initially assign .network file 2022-02-01 11:38:01 +09:00
Yu Watanabe
bb193d2df2 network: drop tiny wrapper used only one place anymore 2022-02-01 11:37:45 +09:00
Yu Watanabe
3cf58ef316 network: do not free bound_by carrier map on reconfigure
Otherwise, if a link enters unmanaged state, then its carrier state does
not propagated to other interfaces.
2022-02-01 11:37:01 +09:00
Yu Watanabe
b69bfa4305 network: do not remove localhost address
Managing loopback interfaces by networkd is not recommended, but supporeted.
Even such spurious situation, do not drop the localhost addresses.
2022-02-01 11:30:37 +09:00
Yu Watanabe
259c65f36c network: move ndisc_flush() to link_stop_engines()
As it is not related to any static configs.
2022-02-01 11:30:37 +09:00
Yu Watanabe
a0e99a377a network: remove only managed configs on reconfigure or carrier lost
Otherwise, if the carrir of the non-managed interface is lost, the
configs such as addresses or routes on the interface will be removed by
networkd.
2022-02-01 11:30:37 +09:00
Luca Boccassi
9d67fb0e33
Merge pull request #22175 from keszybz/kernel-install-mkosi-initrd
kernel-install: add support for KERNEL_INSTALL_INITRD_GENERATOR and KERNEL_INSTALL_STAGING_AREA
2022-01-31 23:09:46 +00:00
Frantisek Sumsal
e3d1ffcc48 network: s/confiured/configured/
A quick typo fix I noticed whilst debugging.
2022-01-31 23:07:41 +00:00
Lennart Poettering
69339ae9f7 tree-wide: some additional checks to avoid CVE-2021-4034 style weaknesses 2022-01-31 23:07:19 +00:00
Zbigniew Jędrzejewski-Szmek
29f604131b kernel-install: add missing log line 2022-01-28 16:17:47 +01:00
Zbigniew Jędrzejewski-Szmek
367165a406 kernel-install: add "$KERNEL_INSTALL_STAGING_AREA" directory
The general approach of kernel-install was that each plugin would drop in some
files into the entry directory. But this doesn't scale well, because if we have
multiple initrd generators, or multiple initrds, each generator would need to
recreate the logic to put the generated files in the right place.

Also, effective cleanup is impossible if anything goes wrong on the way, so we
could end up with unused files in $BOOT.

So let's invert the process: plugins drop files into $KERNEL_INSTALL_STAGING_AREA,
and at the end 90-loaderentry.install DTRT with those files.

This allow new plugins like 50-mkosi-initrd.install to be significantly simpler.
2022-01-28 16:17:47 +01:00
Zbigniew Jędrzejewski-Szmek
680cec6b4d kernel-install: prefix errors with "Error:", exit immediately
kernel-install would continue after errors… We don't want this, as it
makes the results totally unpredicatable. If we didn't install the kernel
or didn't do some important part of the setup, let's just return an error
and let the user deal with it.

When looking at output, the error was often hard to distinguish, esp.
with -v. Add "Error:" everywhere to make the output easier to parse.
2022-01-28 16:17:47 +01:00
Zbigniew Jędrzejewski-Szmek
a520d5dddb kernel-install: k-i already creates $ENTRY_DIR_ABS, no need to do it again 2022-01-28 16:17:45 +01:00
Zbigniew Jędrzejewski-Szmek
5c1b257faf kernel-install: add new variable $KERNEL_INSTALL_INITRD_GENERATOR
The idea is that when not set, we do whatever we did in the past. But
with a new setting of initrd_generator=mkosi-initrd, mkosi-initrd will
generate an initrd.
2022-01-18 17:40:13 +01:00
25 changed files with 424 additions and 323 deletions

View File

@ -171,11 +171,19 @@
<para><varname>KERNEL_INSTALL_BOOT_ROOT=</varname> is set for the plugins to the root directory (mount point, usually) of the hierarchy <para><varname>KERNEL_INSTALL_BOOT_ROOT=</varname> is set for the plugins to the root directory (mount point, usually) of the hierarchy
where boot-loader entries, kernel images, and associated resources should be placed. Can be overridden by setting <varname>BOOT_ROOT=</varname>.</para> where boot-loader entries, kernel images, and associated resources should be placed. Can be overridden by setting <varname>BOOT_ROOT=</varname>.</para>
<para><varname>KERNEL_INSTALL_LAYOUT=bls|other|...</varname> specifies the installation layout. <para><varname>KERNEL_INSTALL_LAYOUT=bls|other|...</varname> is set for the plugins to specify the installation layout.
Defaults to <option>bls</option> if <filename>$BOOT/<replaceable>MACHINE-ID</replaceable></filename> exists, or <option>other</option> otherwise. Defaults to <option>bls</option> if <filename>$BOOT/<replaceable>MACHINE-ID</replaceable></filename> exists, or <option>other</option> otherwise.
Additional layout names may be defined by convention. If a plugin uses a special layout, Additional layout names may be defined by convention. If a plugin uses a special layout,
it's encouraged to declare its own layout name and configure <varname>layout=</varname> in <filename>install.conf</filename> upon initial installation.</para> it's encouraged to declare its own layout name and configure <varname>layout=</varname> in <filename>install.conf</filename> upon initial installation.</para>
<para><varname>KERNEL_INSTALL_INITRD_GENERATOR=...</varname> is set for plugins to select the initrd generator.
This should be configured as <varname>initrd_generator=</varname> in <filename>install.conf</filename>.
</para>
<para><varname>KERNEL_INSTALL_STAGING_AREA=...</varname> is set for plugins to a path to a directory.
Plugins may drop files in that directory, and they will be installed as part of the loader entry, based
on the file name and extension.</para>
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term>bls</term> <term>bls</term>

View File

@ -121,6 +121,19 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) {
return -EAFNOSUPPORT; return -EAFNOSUPPORT;
} }
int in_addr_is_localhost_one(int family, const union in_addr_union *u) {
assert(u);
if (family == AF_INET)
/* 127.0.0.1 */
return be32toh(u->in.s_addr) == UINT32_C(0x7F000001);
if (family == AF_INET6)
return IN6_IS_ADDR_LOOPBACK(&u->in6); /* lgtm [cpp/potentially-dangerous-function] */
return -EAFNOSUPPORT;
}
bool in6_addr_is_ipv4_mapped_address(const struct in6_addr *a) { bool in6_addr_is_ipv4_mapped_address(const struct in6_addr *a) {
return a->s6_addr32[0] == 0 && return a->s6_addr32[0] == 0 &&
a->s6_addr32[1] == 0 && a->s6_addr32[1] == 0 &&

View File

@ -49,6 +49,7 @@ bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a);
bool in4_addr_is_localhost(const struct in_addr *a); bool in4_addr_is_localhost(const struct in_addr *a);
int in_addr_is_localhost(int family, const union in_addr_union *u); int in_addr_is_localhost(int family, const union in_addr_union *u);
int in_addr_is_localhost_one(int family, const union in_addr_union *u);
bool in4_addr_is_local_multicast(const struct in_addr *a); bool in4_addr_is_local_multicast(const struct in_addr *a);
bool in4_addr_is_non_local(const struct in_addr *a); bool in4_addr_is_non_local(const struct in_addr *a);

View File

@ -68,8 +68,8 @@ bpf_o_unstripped_cmd += [
if bpftool_strip if bpftool_strip
bpf_o_cmd = [ bpf_o_cmd = [
bpftool, bpftool,
'g', 'gen',
'o', 'object',
'@OUTPUT@', '@OUTPUT@',
'@INPUT@' '@INPUT@'
] ]
@ -85,7 +85,7 @@ endif
skel_h_cmd = [ skel_h_cmd = [
bpftool, bpftool,
'g', 'gen',
's', 'skeleton',
'@INPUT@' '@INPUT@'
] ]

View File

@ -4058,6 +4058,10 @@ static int exec_child(
assert(params); assert(params);
assert(exit_status); assert(exit_status);
/* Explicitly test for CVE-2021-4034 inspired invocations */
assert(command->path);
assert(!strv_isempty(command->argv));
rename_process_from_path(command->path); rename_process_from_path(command->path);
/* We reset exactly these signals, since they are the only ones we set to SIG_IGN in the main /* We reset exactly these signals, since they are the only ones we set to SIG_IGN in the main

View File

@ -18,6 +18,8 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with systemd; If not, see <http://www.gnu.org/licenses/>. # along with systemd; If not, see <http://www.gnu.org/licenses/>.
shopt -s nullglob
COMMAND="$1" COMMAND="$1"
KERNEL_VERSION="$2" KERNEL_VERSION="$2"
ENTRY_DIR_ABS="$3" ENTRY_DIR_ABS="$3"
@ -38,6 +40,8 @@ fi
case "$COMMAND" in case "$COMMAND" in
remove) remove)
[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
echo "Removing $BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION*.conf"
exec rm -f \ exec rm -f \
"$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf" \ "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf" \
"$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION+"*".conf" "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION+"*".conf"
@ -78,36 +82,33 @@ else
fi fi
if ! [ -d "$ENTRY_DIR_ABS" ]; then if ! [ -d "$ENTRY_DIR_ABS" ]; then
if [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ]; then echo "Error: entry directory '$ENTRY_DIR_ABS' does not exist" >&2
echo "+mkdir -v -p $ENTRY_DIR_ABS" exit 1
mkdir -v -p "$ENTRY_DIR_ABS"
else
mkdir -p "$ENTRY_DIR_ABS"
fi
fi fi
install -g root -o root -m 0644 "$KERNEL_IMAGE" "$ENTRY_DIR_ABS/linux" || { install -g root -o root -m 0644 "$KERNEL_IMAGE" "$ENTRY_DIR_ABS/linux" || {
echo "Could not copy '$KERNEL_IMAGE' to '$ENTRY_DIR_ABS/linux'." >&2 echo "Error: could not copy '$KERNEL_IMAGE' to '$ENTRY_DIR_ABS/linux'." >&2
exit 1 exit 1
} }
shift "$INITRD_OPTIONS_SHIFT" shift "$INITRD_OPTIONS_SHIFT"
for initrd; do # All files listed as arguments, and staged files called "initrd*" are installed as initrds.
for initrd in "$@" "${KERNEL_INSTALL_STAGING_AREA}"/initrd*; do
[ -f "$initrd" ] || { [ -f "$initrd" ] || {
echo "Initrd '$initrd' not a file." >&2 echo "Error: initrd '$initrd' not a file." >&2
exit 1 exit 1
} }
initrd_basename="${initrd##*/}" initrd_basename="${initrd##*/}"
[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "Installing $ENTRY_DIR_ABS/$initrd_basename" [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "Installing $ENTRY_DIR_ABS/$initrd_basename"
install -g root -o root -m 0644 "$initrd" "$ENTRY_DIR_ABS/$initrd_basename" || { install -g root -o root -m 0644 "$initrd" "$ENTRY_DIR_ABS/$initrd_basename" || {
echo "Could not copy '$initrd' to '$ENTRY_DIR_ABS/$initrd_basename'." >&2 echo "Error: could not copy '$initrd' to '$ENTRY_DIR_ABS/$initrd_basename'." >&2
exit 1 exit 1
} }
done done
mkdir -p "${LOADER_ENTRY%/*}" || { mkdir -p "${LOADER_ENTRY%/*}" || {
echo "Could not create loader entry directory '${LOADER_ENTRY%/*}'." >&2 echo "Error: could not create loader entry directory '${LOADER_ENTRY%/*}'." >&2
exit 1 exit 1
} }
@ -118,14 +119,18 @@ mkdir -p "${LOADER_ENTRY%/*}" || {
echo "machine-id $MACHINE_ID" echo "machine-id $MACHINE_ID"
echo "options $BOOT_OPTIONS" echo "options $BOOT_OPTIONS"
echo "linux $ENTRY_DIR/linux" echo "linux $ENTRY_DIR/linux"
for initrd; do
have_initrd=
for initrd in "${@}" "${KERNEL_INSTALL_STAGING_AREA}"/initrd*; do
echo "initrd $ENTRY_DIR/${initrd##*/}" echo "initrd $ENTRY_DIR/${initrd##*/}"
have_initrd=yes
done done
# Try "initrd", generated by dracut in its kernel-install hook, if no initrds were supplied # Try "initrd", generated by dracut in its kernel-install hook, if no initrds were supplied
[ $# -eq 0 ] && [ -f "$ENTRY_DIR_ABS/initrd" ] && echo "initrd $ENTRY_DIR/initrd" [ -z "$have_initrd" ] && [ -f "$ENTRY_DIR_ABS/initrd" ] && echo "initrd $ENTRY_DIR/initrd"
: :
} >"$LOADER_ENTRY" || { } >"$LOADER_ENTRY" || {
echo "Could not create loader entry '$LOADER_ENTRY'." >&2 echo "Error: could not create loader entry '$LOADER_ENTRY'." >&2
exit 1 exit 1
} }
exit 0 exit 0

View File

@ -8,3 +8,4 @@
# See kernel-install(8) for details. # See kernel-install(8) for details.
#layout=bls|other|... #layout=bls|other|...
#initrd_generator=dracut|...

View File

@ -73,13 +73,16 @@ else
fi fi
if [ $# -lt 1 ]; then if [ $# -lt 1 ]; then
echo "Not enough arguments" >&2 echo "Error: not enough arguments" >&2
exit 1 exit 1
fi fi
KERNEL_VERSION="$1" KERNEL_VERSION="$1"
shift shift
layout=
initrd_generator=
if [ -r "/etc/kernel/install.conf" ]; then if [ -r "/etc/kernel/install.conf" ]; then
. /etc/kernel/install.conf . /etc/kernel/install.conf
elif [ -r "/usr/lib/kernel/install.conf" ]; then elif [ -r "/usr/lib/kernel/install.conf" ]; then
@ -123,12 +126,22 @@ if [ -z "$layout" ]; then
fi fi
fi fi
ENTRY_DIR_ABS="$BOOT_ROOT/$MACHINE_ID/$KERNEL_VERSION" ENTRY_DIR_ABS="$BOOT_ROOT/$MACHINE_ID/$KERNEL_VERSION"
# Provide a directory where to store generated initrds
cleanup() {
[ -n "$KERNEL_INSTALL_STAGING_AREA" ] && rm -rf "$KERNEL_INSTALL_STAGING_AREA"
}
trap cleanup EXIT
KERNEL_INSTALL_STAGING_AREA="$(mktemp -d -t -p /tmp kernel-install.staging.XXXXXXX)"
export KERNEL_INSTALL_MACHINE_ID="$MACHINE_ID" export KERNEL_INSTALL_MACHINE_ID="$MACHINE_ID"
export KERNEL_INSTALL_BOOT_ROOT="$BOOT_ROOT" export KERNEL_INSTALL_BOOT_ROOT="$BOOT_ROOT"
export KERNEL_INSTALL_LAYOUT="$layout" export KERNEL_INSTALL_LAYOUT="$layout"
export KERNEL_INSTALL_INITRD_GENERATOR="$initrd_generator"
export KERNEL_INSTALL_STAGING_AREA
[ "$layout" = "bls" ] [ "$layout" = "bls" ]
MAKE_ENTRY_DIR_ABS=$? MAKE_ENTRY_DIR_ABS=$?
@ -147,12 +160,12 @@ IFS="
case "$COMMAND" in case "$COMMAND" in
add) add)
if [ $# -lt 1 ]; then if [ $# -lt 1 ]; then
echo "Command 'add' requires a kernel image" >&2 echo "Error: command 'add' requires a kernel image" >&2
exit 1 exit 1
fi fi
if ! [ -f "$1" ]; then if ! [ -f "$1" ]; then
echo "Kernel image argument $1 not a file" >&2 echo "Error: kernel image argument $1 not a file" >&2
exit 1 exit 1
fi fi
@ -162,9 +175,9 @@ case "$COMMAND" in
# to serve as the indication to use or to not use the BLS # to serve as the indication to use or to not use the BLS
if [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ]; then if [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ]; then
echo "+mkdir -v -p $ENTRY_DIR_ABS" echo "+mkdir -v -p $ENTRY_DIR_ABS"
mkdir -v -p "$ENTRY_DIR_ABS" mkdir -v -p "$ENTRY_DIR_ABS" || exit 1
else else
mkdir -p "$ENTRY_DIR_ABS" mkdir -p "$ENTRY_DIR_ABS" || exit 1
fi fi
fi fi
@ -193,7 +206,7 @@ case "$COMMAND" in
;; ;;
*) *)
echo "Unknown command '$COMMAND'" >&2 echo "Error: unknown command '$COMMAND'" >&2
exit 1 exit 1
;; ;;
esac esac

View File

@ -17,13 +17,42 @@ ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) {
return 0; return 0;
} }
static void add_lease(sd_dhcp_server *server, const struct in_addr *server_address, uint8_t i) {
static const uint8_t chaddr[] = {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3};
DHCPLease *lease;
assert(server);
assert_se(lease = new0(DHCPLease, 1));
lease->client_id.length = 2;
assert_se(lease->client_id.data = malloc(2));
lease->client_id.data[0] = 2;
lease->client_id.data[1] = i;
lease->address = htobe32(UINT32_C(10) << 24 | i);
lease->gateway = server_address->s_addr;
lease->expiration = UINT64_MAX;
lease->htype = ARPHRD_ETHER;
lease->hlen = ETH_ALEN;
memcpy(lease->chaddr, chaddr, ETH_ALEN);
assert_se(hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease) >= 0);
assert_se(hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease) >= 0);
lease->server = server;
}
static void add_static_lease(sd_dhcp_server *server, uint8_t i) {
uint8_t id[2] = { 2, i };
assert(server);
assert_se(sd_dhcp_server_set_static_lease(server,
&(struct in_addr) { .s_addr = htobe32(UINT32_C(10) << 24 | i)},
id, ELEMENTSOF(id)) >= 0);
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
_cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
struct in_addr address = {.s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))}; struct in_addr address = { .s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))};
static const uint8_t chaddr[] = {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3};
_cleanup_free_ uint8_t *duped = NULL; _cleanup_free_ uint8_t *duped = NULL;
uint8_t *client_id;
DHCPLease *lease;
if (size < sizeof(DHCPMessage)) if (size < sizeof(DHCPMessage))
return 0; return 0;
@ -36,24 +65,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
assert_se(server->fd >= 0); assert_se(server->fd >= 0);
assert_se(sd_dhcp_server_configure_pool(server, &address, 24, 0, 0) >= 0); assert_se(sd_dhcp_server_configure_pool(server, &address, 24, 0, 0) >= 0);
/* add a lease to the pool to expose additional code paths */ /* add leases to the pool to expose additional code paths */
client_id = malloc(2); add_lease(server, &address, 2);
assert_se(client_id); add_lease(server, &address, 3);
client_id[0] = 2;
client_id[1] = 2; /* add static leases */
lease = new0(DHCPLease, 1); add_static_lease(server, 3);
assert_se(lease); add_static_lease(server, 4);
lease->client_id.length = 2;
lease->client_id.data = client_id;
lease->address = htobe32(UINT32_C(10) << 24 | UINT32_C(2));
lease->gateway = htobe32(UINT32_C(10) << 24 | UINT32_C(1));
lease->expiration = UINT64_MAX;
lease->htype = ARPHRD_ETHER;
lease->hlen = ETH_ALEN;
memcpy(lease->chaddr, chaddr, ETH_ALEN);
assert_se(hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease) >= 0);
assert_se(hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease) >= 0);
lease->server = server;
(void) dhcp_server_handle_message(server, (DHCPMessage*) duped, size); (void) dhcp_server_handle_message(server, (DHCPMessage*) duped, size);

View File

@ -30,19 +30,10 @@ static DHCPLease *dhcp_lease_free(DHCPLease *lease) {
return NULL; return NULL;
if (lease->server) { if (lease->server) {
DHCPLease *e; hashmap_remove_value(lease->server->bound_leases_by_address, UINT32_TO_PTR(lease->address), lease);
hashmap_remove_value(lease->server->bound_leases_by_client_id, &lease->client_id, lease);
e = hashmap_get(lease->server->bound_leases_by_client_id, &lease->client_id); hashmap_remove_value(lease->server->static_leases_by_address, UINT32_TO_PTR(lease->address), lease);
if (e == lease) { hashmap_remove_value(lease->server->static_leases_by_client_id, &lease->client_id, lease);
hashmap_remove(lease->server->bound_leases_by_address, UINT32_TO_PTR(lease->address));
hashmap_remove(lease->server->bound_leases_by_client_id, &lease->client_id);
}
e = hashmap_get(lease->server->static_leases_by_client_id, &lease->client_id);
if (e == lease) {
hashmap_remove(lease->server->static_leases_by_address, UINT32_TO_PTR(lease->address));
hashmap_remove(lease->server->static_leases_by_client_id, &lease->client_id);
}
} }
free(lease->client_id.data); free(lease->client_id.data);
@ -134,7 +125,7 @@ int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server) {
void client_id_hash_func(const DHCPClientId *id, struct siphash *state) { void client_id_hash_func(const DHCPClientId *id, struct siphash *state) {
assert(id); assert(id);
assert(id->length); assert(id->length > 0);
assert(id->data); assert(id->data);
siphash24_compress(&id->length, sizeof(id->length), state); siphash24_compress(&id->length, sizeof(id->length), state);
@ -144,8 +135,10 @@ void client_id_hash_func(const DHCPClientId *id, struct siphash *state) {
int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b) { int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b) {
int r; int r;
assert(!a->length || a->data); assert(a->length > 0);
assert(!b->length || b->data); assert(a->data);
assert(b->length > 0);
assert(b->data);
r = CMP(a->length, b->length); r = CMP(a->length, b->length);
if (r != 0) if (r != 0)
@ -165,8 +158,6 @@ DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) { static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
assert(server); assert(server);
log_dhcp_server(server, "UNREF");
sd_dhcp_server_stop(server); sd_dhcp_server_stop(server);
sd_event_unref(server->event); sd_event_unref(server->event);
@ -280,9 +271,13 @@ sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
} }
int sd_dhcp_server_stop(sd_dhcp_server *server) { int sd_dhcp_server_stop(sd_dhcp_server *server) {
bool running;
if (!server) if (!server)
return 0; return 0;
running = sd_dhcp_server_is_running(server);
server->receive_message = sd_event_source_disable_unref(server->receive_message); server->receive_message = sd_event_source_disable_unref(server->receive_message);
server->receive_broadcast = sd_event_source_disable_unref(server->receive_broadcast); server->receive_broadcast = sd_event_source_disable_unref(server->receive_broadcast);
@ -290,6 +285,7 @@ int sd_dhcp_server_stop(sd_dhcp_server *server) {
server->fd = safe_close(server->fd); server->fd = safe_close(server->fd);
server->fd_broadcast = safe_close(server->fd_broadcast); server->fd_broadcast = safe_close(server->fd_broadcast);
if (running)
log_dhcp_server(server, "STOPPED"); log_dhcp_server(server, "STOPPED");
return 0; return 0;
@ -311,7 +307,7 @@ static int dhcp_server_send_unicast_raw(
assert(server); assert(server);
assert(server->ifindex > 0); assert(server->ifindex > 0);
assert(server->address); assert(server->address != 0);
assert(hlen > 0); assert(hlen > 0);
assert(chaddr); assert(chaddr);
assert(packet); assert(packet);
@ -424,7 +420,7 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
assert(server); assert(server);
assert(req); assert(req);
assert(req->max_optlen); assert(req->max_optlen > 0);
assert(req->message); assert(req->message);
assert(optoffset <= req->max_optlen); assert(optoffset <= req->max_optlen);
assert(packet); assert(packet);
@ -472,12 +468,12 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
client, because the client may not have a correct network address client, because the client may not have a correct network address
or subnet mask, and the client may not be answering ARP requests. or subnet mask, and the client may not be answering ARP requests.
*/ */
if (req->message->giaddr) { if (req->message->giaddr != 0) {
destination = req->message->giaddr; destination = req->message->giaddr;
destination_port = DHCP_PORT_SERVER; destination_port = DHCP_PORT_SERVER;
if (type == DHCP_NAK) if (type == DHCP_NAK)
packet->dhcp.flags = htobe16(0x8000); packet->dhcp.flags = htobe16(0x8000);
} else if (req->message->ciaddr && type != DHCP_NAK) } else if (req->message->ciaddr != 0 && type != DHCP_NAK)
destination = req->message->ciaddr; destination = req->message->ciaddr;
bool l2_broadcast = requested_broadcast(req->message) || type == DHCP_NAK; bool l2_broadcast = requested_broadcast(req->message) || type == DHCP_NAK;
@ -485,17 +481,22 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
destination, destination_port, packet, optoffset, l2_broadcast); destination, destination_port, packet, optoffset, l2_broadcast);
} }
static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret, static int server_message_init(
uint8_t type, size_t *_optoffset, sd_dhcp_server *server,
DHCPPacket **ret,
uint8_t type,
size_t *ret_optoffset,
DHCPRequest *req) { DHCPRequest *req) {
_cleanup_free_ DHCPPacket *packet = NULL; _cleanup_free_ DHCPPacket *packet = NULL;
size_t optoffset = 0; size_t optoffset = 0;
int r; int r;
assert(server); assert(server);
assert(ret); assert(ret);
assert(_optoffset); assert(ret_optoffset);
assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK)); assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
assert(req);
packet = malloc0(sizeof(DHCPPacket) + req->max_optlen); packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
if (!packet) if (!packet)
@ -511,7 +512,7 @@ static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
packet->dhcp.flags = req->message->flags; packet->dhcp.flags = req->message->flags;
packet->dhcp.giaddr = req->message->giaddr; packet->dhcp.giaddr = req->message->giaddr;
*_optoffset = optoffset; *ret_optoffset = optoffset;
*ret = TAKE_PTR(packet); *ret = TAKE_PTR(packet);
return 0; return 0;
@ -614,16 +615,28 @@ static int server_send_offer_or_ack(
return dhcp_server_send_packet(server, req, packet, type, offset); return dhcp_server_send_packet(server, req, packet, type, offset);
} }
static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) { static int server_send_nak_or_ignore(sd_dhcp_server *server, bool init_reboot, DHCPRequest *req) {
_cleanup_free_ DHCPPacket *packet = NULL; _cleanup_free_ DHCPPacket *packet = NULL;
size_t offset; size_t offset;
int r; int r;
/* When a request is refused, RFC 2131, section 4.3.2 mentioned we should send NAK when the
* client is in INITREBOOT. If the client is in other state, there is nothing mentioned in the
* RFC whether we should send NAK or not. Hence, let's silently ignore the request. */
if (!init_reboot)
return 0;
r = server_message_init(server, &packet, DHCP_NAK, &offset, req); r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
if (r < 0) if (r < 0)
return r; return log_dhcp_server_errno(server, r, "Failed to create NAK message: %m");
return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset); r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
if (r < 0)
return log_dhcp_server_errno(server, r, "Could not send NAK message: %m");
log_dhcp_server(server, "NAK (0x%x)", be32toh(req->message->xid));
return DHCP_NAK;
} }
static int server_send_forcerenew( static int server_send_forcerenew(
@ -778,17 +791,20 @@ static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMes
return 0; return 0;
} }
static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) { static bool address_is_in_pool(sd_dhcp_server *server, be32_t address) {
assert(server); assert(server);
if (!server->pool_size) if (server->pool_size == 0)
return -EINVAL; return false;
if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) || if (be32toh(address) < (be32toh(server->subnet) | server->pool_offset) ||
be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size))) be32toh(address) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size)))
return -ERANGE; return false;
return be32toh(requested_ip & ~server->netmask) - server->pool_offset; if (hashmap_contains(server->static_leases_by_address, UINT32_TO_PTR(address)))
return false;
return true;
} }
static int append_agent_information_option(sd_dhcp_server *server, DHCPMessage *message, size_t opt_length, size_t size) { static int append_agent_information_option(sd_dhcp_server *server, DHCPMessage *message, size_t opt_length, size_t size) {
@ -903,6 +919,59 @@ static int prepare_new_lease(
return 0; return 0;
} }
static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLease *existing_lease, be32_t address) {
usec_t time_now, expiration;
int r;
assert(server);
assert(req);
assert(address != 0);
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) {
assert(existing_lease->server);
assert(existing_lease->address == address);
existing_lease->expiration = expiration;
} else {
_cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
r = prepare_new_lease(&lease, address, &req->client_id,
req->message->htype, req->message->hlen,
req->message->chaddr, req->message->giaddr, expiration);
if (r < 0)
return log_dhcp_server_errno(server, r, "Failed to create new lease: %m");
lease->server = server; /* This must be set just before hashmap_put(). */
r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
if (r < 0)
return log_dhcp_server_errno(server, r, "Could not save lease: %m");
r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
if (r < 0)
return log_dhcp_server_errno(server, r, "Could not save lease: %m");
TAKE_PTR(lease);
}
r = server_send_offer_or_ack(server, req, address, DHCP_ACK);
if (r < 0)
return log_dhcp_server_errno(server, r, "Could not send ACK: %m");
log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid));
if (server->callback)
server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
return DHCP_ACK;
}
static int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server) { static int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server) {
DHCPLease *lease; DHCPLease *lease;
usec_t time_now; usec_t time_now;
@ -974,7 +1043,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
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) if (server->pool_size == 0)
/* no pool allocated */ /* no pool allocated */
return 0; return 0;
@ -1026,14 +1095,12 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
return 1; return 1;
case DHCP_REQUEST: { case DHCP_REQUEST: {
DHCPLease *existing_lease_by_address;
be32_t address; be32_t address;
bool init_reboot = false; bool init_reboot = false;
int pool_offset;
/* see RFC 2131, section 4.3.2 */ /* see RFC 2131, section 4.3.2 */
if (req->server_id) { if (req->server_id != 0) {
log_dhcp_server(server, "REQUEST (selecting) (0x%x)", log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
be32toh(req->message->xid)); be32toh(req->message->xid));
@ -1042,22 +1109,22 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
/* client did not pick us */ /* client did not pick us */
return 0; return 0;
if (req->message->ciaddr) if (req->message->ciaddr != 0)
/* this MUST be zero */ /* this MUST be zero */
return 0; return 0;
if (!req->requested_ip) if (req->requested_ip == 0)
/* this must be filled in with the yiaddr /* this must be filled in with the yiaddr
from the chosen OFFER */ from the chosen OFFER */
return 0; return 0;
address = req->requested_ip; address = req->requested_ip;
} else if (req->requested_ip) { } else if (req->requested_ip != 0) {
log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)", log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
be32toh(req->message->xid)); be32toh(req->message->xid));
/* INIT-REBOOT */ /* INIT-REBOOT */
if (req->message->ciaddr) if (req->message->ciaddr != 0)
/* this MUST be zero */ /* this MUST be zero */
return 0; return 0;
@ -1069,7 +1136,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
be32toh(req->message->xid)); be32toh(req->message->xid));
/* REBINDING / RENEWING */ /* REBINDING / RENEWING */
if (!req->message->ciaddr) if (req->message->ciaddr == 0)
/* this MUST be filled in with clients IP address */ /* this MUST be filled in with clients IP address */
return 0; return 0;
@ -1080,110 +1147,27 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
if (address == server->address) if (address == server->address)
return 0; return 0;
pool_offset = get_pool_offset(server, address); if (static_lease) {
existing_lease_by_address = hashmap_get(server->bound_leases_by_address, UINT32_TO_PTR(address)); /* Found a static lease for the client ID. */
/* verify that the requested address is from the pool, and either if (static_lease->address != address)
owned by the current client or free */ /* The client requested an address which is different from the static lease. Refuse. */
if (static_lease && static_lease->address == address) { return server_send_nak_or_ignore(server, init_reboot, req);
_cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
usec_t time_now, expiration;
r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now); return server_ack_request(server, req, existing_lease, address);
if (r < 0)
return r;
expiration = usec_add(req->lifetime * USEC_PER_SEC, time_now);
r = prepare_new_lease(&lease, static_lease->address, &req->client_id,
req->message->htype, req->message->hlen,
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)
/* 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));
dhcp_lease_free(hashmap_remove(server->bound_leases_by_client_id, &lease->client_id));
r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
if (r < 0)
return log_dhcp_server_errno(server, r, "Could not save lease: %m");
r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
if (r < 0)
return log_dhcp_server_errno(server, r, "Could not save lease: %m");
lease->server = server;
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 && existing_lease_by_address == existing_lease) {
_cleanup_(dhcp_lease_freep) DHCPLease *new_lease = NULL;
usec_t time_now, expiration;
DHCPLease *lease;
/* Note that in the above condition we accept the case that both leases are NULL. */
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->htype, req->message->hlen,
req->message->chaddr, req->message->giaddr, expiration);
if (r < 0)
return r;
lease = new_lease;
} else {
existing_lease->expiration = expiration;
lease = existing_lease;
} }
r = server_send_offer_or_ack(server, req, address, DHCP_ACK); if (address_is_in_pool(server, address)) {
if (r < 0) /* The requested address is in the pool. */
/* 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)); if (existing_lease && existing_lease->address != address)
/* We previously assigned an address, but the client requested another one. Refuse. */
return server_send_nak_or_ignore(server, init_reboot, req);
r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease); return server_ack_request(server, req, existing_lease, address);
if (r < 0)
return log_dhcp_server_errno(server, r, "Could not save lease: %m");
r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
if (r < 0)
return log_dhcp_server_errno(server, r, "Could not save lease: %m");
lease->server = server;
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)
/* this only fails on critical errors */
return log_dhcp_server_errno(server, r, "Could not send nak: %m");
log_dhcp_server(server, "NAK (0x%x)", be32toh(req->message->xid));
return DHCP_NAK;
} }
break; return server_send_nak_or_ignore(server, init_reboot, req);
} }
case DHCP_RELEASE: { case DHCP_RELEASE: {
@ -1576,6 +1560,7 @@ int sd_dhcp_server_set_static_lease(
assert_return(server, -EINVAL); assert_return(server, -EINVAL);
assert_return(client_id, -EINVAL); assert_return(client_id, -EINVAL);
assert_return(client_id_size > 0, -EINVAL);
assert_return(!sd_dhcp_server_is_running(server), -EBUSY); assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
/* Static lease with an empty or omitted address is a valid entry, /* Static lease with an empty or omitted address is a valid entry,
@ -1588,13 +1573,10 @@ int sd_dhcp_server_set_static_lease(
.data = client_id, .data = client_id,
}; };
dhcp_lease_free(hashmap_remove(server->static_leases_by_client_id, &c)); dhcp_lease_free(hashmap_get(server->static_leases_by_client_id, &c));
return 0; return 0;
} }
if (hashmap_contains(server->static_leases_by_address, UINT32_TO_PTR(address->s_addr)))
return -EEXIST;
lease = new(DHCPLease, 1); lease = new(DHCPLease, 1);
if (!lease) if (!lease)
return -ENOMEM; return -ENOMEM;
@ -1602,13 +1584,13 @@ int sd_dhcp_server_set_static_lease(
*lease = (DHCPLease) { *lease = (DHCPLease) {
.address = address->s_addr, .address = address->s_addr,
.client_id.length = client_id_size, .client_id.length = client_id_size,
.gateway = 0,
.expiration = 0,
}; };
lease->client_id.data = memdup(client_id, client_id_size); lease->client_id.data = memdup(client_id, client_id_size);
if (!lease->client_id.data) if (!lease->client_id.data)
return -ENOMEM; return -ENOMEM;
lease->server = server; /* This must be set just before hashmap_put(). */
r = hashmap_ensure_put(&server->static_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease); r = hashmap_ensure_put(&server->static_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
if (r < 0) if (r < 0)
return r; return r;
@ -1616,7 +1598,6 @@ int sd_dhcp_server_set_static_lease(
if (r < 0) if (r < 0)
return r; return r;
lease->server = server;
TAKE_PTR(lease); TAKE_PTR(lease);
return 0; return 0;
} }

View File

@ -20,8 +20,9 @@ static void test_pool(struct in_addr *address, unsigned size, int ret) {
assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret); assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret);
} }
static int test_basic(sd_event *event, bool bind_to_interface) { static int test_basic(bool bind_to_interface) {
_cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
struct in_addr address_lo = { struct in_addr address_lo = {
.s_addr = htobe32(INADDR_LOOPBACK), .s_addr = htobe32(INADDR_LOOPBACK),
}; };
@ -30,6 +31,10 @@ static int test_basic(sd_event *event, bool bind_to_interface) {
}; };
int r; int r;
log_debug("/* %s(bind_to_interface=%s) */", __func__, yes_no(bind_to_interface));
assert_se(sd_event_new(&event) >= 0);
/* attach to loopback interface */ /* attach to loopback interface */
assert_se(sd_dhcp_server_new(&server, 1) >= 0); assert_se(sd_dhcp_server_new(&server, 1) >= 0);
assert_se(server); assert_se(server);
@ -58,7 +63,7 @@ static int test_basic(sd_event *event, bool bind_to_interface) {
r = sd_dhcp_server_start(server); r = sd_dhcp_server_start(server);
if (r == -EPERM) if (r == -EPERM)
return log_info_errno(r, "sd_dhcp_server_start failed: %m"); return r;
assert_se(r >= 0); assert_se(r >= 0);
assert_se(sd_dhcp_server_start(server) >= 0); assert_se(sd_dhcp_server_start(server) >= 0);
@ -108,9 +113,17 @@ static void test_message_handler(void) {
struct in_addr address_lo = { struct in_addr address_lo = {
.s_addr = htobe32(INADDR_LOOPBACK), .s_addr = htobe32(INADDR_LOOPBACK),
}; };
struct in_addr static_lease_address = {
.s_addr = htobe32(INADDR_LOOPBACK + 42),
};
static uint8_t static_lease_client_id[7] = {0x01, 'A', 'B', 'C', 'D', 'E', 'G' };
log_debug("/* %s */", __func__);
assert_se(sd_dhcp_server_new(&server, 1) >= 0); assert_se(sd_dhcp_server_new(&server, 1) >= 0);
assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0); assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0);
assert_se(sd_dhcp_server_set_static_lease(server, &static_lease_address, static_lease_client_id,
ELEMENTSOF(static_lease_client_id)) >= 0);
assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0); assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0);
assert_se(sd_dhcp_server_start(server) >= 0); assert_se(sd_dhcp_server_start(server) >= 0);
@ -180,6 +193,26 @@ static void test_message_handler(void) {
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 30); test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 30);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
/* request address reserved for static lease (unmatching client ID) */
test.option_client_id.id[6] = 'H';
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 42);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
/* request unmatching address */
test.option_client_id.id[6] = 'G';
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 41);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
/* request matching address */
test.option_client_id.id[6] = 'G';
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 42);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK);
/* try again */
test.option_client_id.id[6] = 'G';
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 42);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK);
} }
static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZE]) { static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZE]) {
@ -202,6 +235,8 @@ static void test_client_id_hash(void) {
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
}; };
log_debug("/* %s */", __func__);
a.data = (uint8_t*)strdup("abcd"); a.data = (uint8_t*)strdup("abcd");
b.data = (uint8_t*)strdup("abcd"); b.data = (uint8_t*)strdup("abcd");
@ -227,24 +262,61 @@ static void test_client_id_hash(void) {
free(b.data); free(b.data);
} }
static void test_static_lease(void) {
_cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
log_debug("/* %s */", __func__);
assert_se(sd_dhcp_server_new(&server, 1) >= 0);
assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020304 },
(uint8_t*) &(uint32_t) { 0x01020304 }, sizeof(uint32_t)) >= 0);
/* Duplicated entry. */
assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020304 },
(uint8_t*) &(uint32_t) { 0x01020304 }, sizeof(uint32_t)) == -EEXIST);
/* Address is conflicted. */
assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020304 },
(uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)) == -EEXIST);
/* Client ID is conflicted. */
assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020305 },
(uint8_t*) &(uint32_t) { 0x01020304 }, sizeof(uint32_t)) == -EEXIST);
assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020305 },
(uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)) >= 0);
/* Remove the previous entry. */
assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x00000000 },
(uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)) >= 0);
/* Then, set a different address. */
assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020306 },
(uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)) >= 0);
/* Remove again. */
assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x00000000 },
(uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)) >= 0);
/* Try to remove non-existent entry. */
assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x00000000 },
(uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)) >= 0);
/* Try to remove non-existent entry. */
assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x00000000 },
(uint8_t*) &(uint32_t) { 0x01020306 }, sizeof(uint32_t)) >= 0);
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
_cleanup_(sd_event_unrefp) sd_event *e;
int r; int r;
test_setup_logging(LOG_DEBUG); test_setup_logging(LOG_DEBUG);
assert_se(sd_event_new(&e) >= 0); test_client_id_hash();
test_static_lease();
r = test_basic(e, true); r = test_basic(true);
if (r != 0) if (r < 0)
return log_tests_skipped("cannot start dhcp server(bound to interface)"); return log_tests_skipped_errno(r, "cannot start dhcp server(bound to interface)");
r = test_basic(e, false); r = test_basic(false);
if (r != 0) if (r < 0)
return log_tests_skipped("cannot start dhcp server(non-bound to interface)"); return log_tests_skipped_errno(r, "cannot start dhcp server(non-bound to interface)");
test_message_handler(); test_message_handler();
test_client_id_hash();
return 0; return 0;
} }

View File

@ -854,6 +854,10 @@ int link_drop_foreign_addresses(Link *link) {
if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6)) if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6))
continue; continue;
/* Do not remove localhost address (127.0.0.1 and ::1) */
if (link->flags & IFF_LOOPBACK && in_addr_is_localhost_one(address->family, &address->in_addr) > 0)
continue;
/* Ignore addresses we configured. */ /* Ignore addresses we configured. */
if (address->source != NETWORK_CONFIG_SOURCE_FOREIGN) if (address->source != NETWORK_CONFIG_SOURCE_FOREIGN)
continue; continue;
@ -891,22 +895,19 @@ int link_drop_foreign_addresses(Link *link) {
return r; return r;
} }
int link_drop_addresses(Link *link) { int link_drop_managed_addresses(Link *link) {
Address *address; Address *address;
int k, r = 0; int k, r = 0;
assert(link); assert(link);
SET_FOREACH(address, link->addresses) { SET_FOREACH(address, link->addresses) {
/* Ignore addresses not assigned yet or already removing. */ /* Do not touch addresses managed by kernel or other tools. */
if (!address_exists(address)) if (address->source == NETWORK_CONFIG_SOURCE_FOREIGN)
continue; continue;
/* Do not drop IPv6LL addresses assigned by the kernel here. They will be dropped in /* Ignore addresses not assigned yet or already removing. */
* link_drop_ipv6ll_addresses() if IPv6LL addressing is disabled. */ if (!address_exists(address))
if (address->source == NETWORK_CONFIG_SOURCE_FOREIGN &&
address->family == AF_INET6 &&
in6_addr_is_link_local(&address->in_addr.in6))
continue; continue;
k = address_remove(address); k = address_remove(address);

View File

@ -74,7 +74,7 @@ void address_set_broadcast(Address *a);
DEFINE_SECTION_CLEANUP_FUNCTIONS(Address, address_free); DEFINE_SECTION_CLEANUP_FUNCTIONS(Address, address_free);
int link_drop_addresses(Link *link); int link_drop_managed_addresses(Link *link);
int link_drop_foreign_addresses(Link *link); int link_drop_foreign_addresses(Link *link);
int link_drop_ipv6ll_addresses(Link *link); int link_drop_ipv6ll_addresses(Link *link);
void link_foreignize_addresses(Link *link); void link_foreignize_addresses(Link *link);

View File

@ -430,6 +430,8 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
if (k < 0) if (k < 0)
r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m"); r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m");
ndisc_flush(link);
k = sd_radv_stop(link->radv); k = sd_radv_stop(link->radv);
if (k < 0) if (k < 0)
r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Advertisement: %m"); r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Advertisement: %m");
@ -892,24 +894,6 @@ static int link_new_bound_to_list(Link *link) {
return 0; return 0;
} }
static int link_new_carrier_maps(Link *link) {
int r;
r = link_new_bound_by_list(link);
if (r < 0)
return r;
r = link_handle_bound_by_list(link);
if (r < 0)
return r;
r = link_new_bound_to_list(link);
if (r < 0)
return r;
return link_handle_bound_to_list(link);
}
static void link_free_bound_to_list(Link *link) { static void link_free_bound_to_list(Link *link) {
bool updated = false; bool updated = false;
Link *bound_to; Link *bound_to;
@ -946,13 +930,6 @@ static void link_free_bound_by_list(Link *link) {
link_dirty(link); link_dirty(link);
} }
static void link_free_carrier_maps(Link *link) {
assert(link);
link_free_bound_to_list(link);
link_free_bound_by_list(link);
}
static int link_append_to_master(Link *link) { static int link_append_to_master(Link *link) {
Link *master; Link *master;
int r; int r;
@ -1012,7 +989,8 @@ static Link *link_drop(Link *link) {
link_drop_requests(link); link_drop_requests(link);
link_free_carrier_maps(link); link_free_bound_to_list(link);
link_free_bound_by_list(link);
link_drop_from_master(link); link_drop_from_master(link);
@ -1070,32 +1048,30 @@ static int link_drop_foreign_config(Link *link) {
return r; return r;
} }
static int link_drop_config(Link *link) { static int link_drop_managed_config(Link *link) {
int k, r; int k, r;
assert(link); assert(link);
assert(link->manager); assert(link->manager);
r = link_drop_routes(link); r = link_drop_managed_routes(link);
k = link_drop_nexthops(link); k = link_drop_managed_nexthops(link);
if (k < 0 && r >= 0) if (k < 0 && r >= 0)
r = k; r = k;
k = link_drop_addresses(link); k = link_drop_managed_addresses(link);
if (k < 0 && r >= 0) if (k < 0 && r >= 0)
r = k; r = k;
k = link_drop_neighbors(link); k = link_drop_managed_neighbors(link);
if (k < 0 && r >= 0) if (k < 0 && r >= 0)
r = k; r = k;
k = link_drop_routing_policy_rules(link); k = link_drop_managed_routing_policy_rules(link);
if (k < 0 && r >= 0) if (k < 0 && r >= 0)
r = k; r = k;
ndisc_flush(link);
return r; return r;
} }
@ -1119,6 +1095,10 @@ static int link_configure(Link *link) {
link_set_state(link, LINK_STATE_CONFIGURING); link_set_state(link, LINK_STATE_CONFIGURING);
r = link_new_bound_to_list(link);
if (r < 0)
return r;
r = link_configure_traffic_control(link); r = link_configure_traffic_control(link);
if (r < 0) if (r < 0)
return r; return r;
@ -1293,17 +1273,28 @@ static int link_reconfigure_impl(Link *link, bool force) {
assert(link); assert(link);
if (!IN_SET(link->state, LINK_STATE_INITIALIZED, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED, LINK_STATE_UNMANAGED))
return 0;
r = link_get_network(link, &network); r = link_get_network(link, &network);
if (r < 0 && r != -ENOENT) if (r < 0 && r != -ENOENT)
return r; return r;
if (link->state != LINK_STATE_UNMANAGED && !network)
/* If link is in initialized state, then link->network is also NULL. */
force = true;
if (link->network == network && !force) if (link->network == network && !force)
return 0; return 0;
if (network) if (network) {
log_link_info(link, "Reconfiguring with %s.", network->filename); if (link->state == LINK_STATE_INITIALIZED)
log_link_info(link, "Configuring with %s.", network->filename);
else else
log_link_info(link, "Unmanaging interface."); log_link_info(link, "Reconfiguring with %s.", network->filename);
} else
log_link_full(link, link->state == LINK_STATE_INITIALIZED ? LOG_DEBUG : LOG_INFO,
"Unmanaging interface.");
/* Dropping old .network file */ /* Dropping old .network file */
r = link_stop_engines(link, false); r = link_stop_engines(link, false);
@ -1318,12 +1309,19 @@ static int link_reconfigure_impl(Link *link, bool force) {
* link_drop_foreign_config() in link_configure(). */ * link_drop_foreign_config() in link_configure(). */
link_foreignize_config(link); link_foreignize_config(link);
else { else {
r = link_drop_config(link); /* Remove all managed configs. Note, foreign configs are removed in later by
* link_configure() -> link_drop_foreign_config() if the link is managed by us. */
r = link_drop_managed_config(link);
if (r < 0) if (r < 0)
return r; return r;
} }
link_free_carrier_maps(link); /* The bound_to map depends on .network file, hence it needs to be freed. But, do not free the
* bound_by map. Otherwise, if a link enters unmanaged state below, then its carrier state will
* not propagated to other interfaces anymore. Moreover, it is not necessary to recreate the
* map here, as it depends on .network files assigned to other links. */
link_free_bound_to_list(link);
link_free_engines(link); link_free_engines(link);
link->network = network_unref(link->network); link->network = network_unref(link->network);
@ -1337,10 +1335,6 @@ static int link_reconfigure_impl(Link *link, bool force) {
link_update_operstate(link, true); link_update_operstate(link, true);
link_dirty(link); link_dirty(link);
r = link_new_carrier_maps(link);
if (r < 0)
return r;
link_set_state(link, LINK_STATE_INITIALIZED); link_set_state(link, LINK_STATE_INITIALIZED);
link->activated = false; link->activated = false;
@ -1440,11 +1434,9 @@ int link_reconfigure_after_sleep(Link *link) {
} }
static int link_initialized_and_synced(Link *link) { static int link_initialized_and_synced(Link *link) {
Network *network;
int r; int r;
assert(link); assert(link);
assert(link->ifname);
assert(link->manager); assert(link->manager);
if (link->manager->test_mode) { if (link->manager->test_mode) {
@ -1453,7 +1445,7 @@ static int link_initialized_and_synced(Link *link) {
return 0; return 0;
} }
/* We may get called either from the asynchronous netlink callback, /* This may get called either from the asynchronous netlink callback,
* or directly from link_check_initialized() if running in a container. */ * or directly from link_check_initialized() if running in a container. */
if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED)) if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED))
return 0; return 0;
@ -1469,36 +1461,7 @@ static int link_initialized_and_synced(Link *link) {
if (r < 0) if (r < 0)
return r; return r;
if (!link->network) { return link_reconfigure_impl(link, /* force = */ false);
r = link_get_network(link, &network);
if (r == -ENOENT) {
link_set_state(link, LINK_STATE_UNMANAGED);
return 0;
}
if (r < 0)
return r;
if (link->flags & IFF_LOOPBACK) {
if (network->link_local != ADDRESS_FAMILY_NO)
log_link_debug(link, "Ignoring link-local autoconfiguration for loopback link");
if (network->dhcp != ADDRESS_FAMILY_NO)
log_link_debug(link, "Ignoring DHCP clients for loopback link");
if (network->dhcp_server)
log_link_debug(link, "Ignoring DHCP server for loopback link");
}
link->network = network_ref(network);
link_update_operstate(link, false);
link_dirty(link);
}
r = link_new_bound_to_list(link);
if (r < 0)
return r;
return link_configure(link);
} }
static int link_initialized_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { static int link_initialized_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
@ -1706,7 +1669,7 @@ static int link_carrier_lost_impl(Link *link) {
if (r < 0) if (r < 0)
ret = r; ret = r;
r = link_drop_config(link); r = link_drop_managed_config(link);
if (r < 0 && ret >= 0) if (r < 0 && ret >= 0)
ret = r; ret = r;

View File

@ -416,13 +416,17 @@ int link_drop_foreign_neighbors(Link *link) {
return r; return r;
} }
int link_drop_neighbors(Link *link) { int link_drop_managed_neighbors(Link *link) {
Neighbor *neighbor; Neighbor *neighbor;
int k, r = 0; int k, r = 0;
assert(link); assert(link);
SET_FOREACH(neighbor, link->neighbors) { SET_FOREACH(neighbor, link->neighbors) {
/* Do not touch nexthops managed by kernel or other tools. */
if (neighbor->source == NETWORK_CONFIG_SOURCE_FOREIGN)
continue;
/* Ignore neighbors not assigned yet or already removing. */ /* Ignore neighbors not assigned yet or already removing. */
if (!neighbor_exists(neighbor)) if (!neighbor_exists(neighbor))
continue; continue;

View File

@ -34,7 +34,7 @@ int neighbor_compare_func(const Neighbor *a, const Neighbor *b);
void network_drop_invalid_neighbors(Network *network); void network_drop_invalid_neighbors(Network *network);
int link_drop_neighbors(Link *link); int link_drop_managed_neighbors(Link *link);
int link_drop_foreign_neighbors(Link *link); int link_drop_foreign_neighbors(Link *link);
void link_foreignize_neighbors(Link *link); void link_foreignize_neighbors(Link *link);

View File

@ -613,8 +613,8 @@ static void manager_mark_nexthops(Manager *manager, bool foreign, const Link *ex
if (nexthop->protocol == RTPROT_KERNEL) if (nexthop->protocol == RTPROT_KERNEL)
continue; continue;
/* When 'foreign' is true, do not remove nexthops we configured. */ /* When 'foreign' is true, mark only foreign nexthops, and vice versa. */
if (foreign && nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN) if (foreign != (nexthop->source == NETWORK_CONFIG_SOURCE_FOREIGN))
continue; continue;
/* Ignore nexthops not assigned yet or already removed. */ /* Ignore nexthops not assigned yet or already removed. */
@ -641,7 +641,7 @@ static void manager_mark_nexthops(Manager *manager, bool foreign, const Link *ex
} }
} }
static int manager_drop_nexthops(Manager *manager) { static int manager_drop_marked_nexthops(Manager *manager) {
NextHop *nexthop; NextHop *nexthop;
int k, r = 0; int k, r = 0;
@ -704,14 +704,14 @@ int link_drop_foreign_nexthops(Link *link) {
manager_mark_nexthops(link->manager, /* foreign = */ true, NULL); manager_mark_nexthops(link->manager, /* foreign = */ true, NULL);
k = manager_drop_nexthops(link->manager); k = manager_drop_marked_nexthops(link->manager);
if (k < 0 && r >= 0) if (k < 0 && r >= 0)
r = k; r = k;
return r; return r;
} }
int link_drop_nexthops(Link *link) { int link_drop_managed_nexthops(Link *link) {
NextHop *nexthop; NextHop *nexthop;
int k, r = 0; int k, r = 0;
@ -723,6 +723,10 @@ int link_drop_nexthops(Link *link) {
if (nexthop->protocol == RTPROT_KERNEL) if (nexthop->protocol == RTPROT_KERNEL)
continue; continue;
/* Do not touch addresses managed by kernel or other tools. */
if (nexthop->source == NETWORK_CONFIG_SOURCE_FOREIGN)
continue;
/* Ignore nexthops not assigned yet or already removing. */ /* Ignore nexthops not assigned yet or already removing. */
if (!nexthop_exists(nexthop)) if (!nexthop_exists(nexthop))
continue; continue;
@ -734,7 +738,7 @@ int link_drop_nexthops(Link *link) {
manager_mark_nexthops(link->manager, /* foreign = */ false, link); manager_mark_nexthops(link->manager, /* foreign = */ false, link);
k = manager_drop_nexthops(link->manager); k = manager_drop_marked_nexthops(link->manager);
if (k < 0 && r >= 0) if (k < 0 && r >= 0)
r = k; r = k;

View File

@ -44,7 +44,7 @@ int nexthop_compare_func(const NextHop *a, const NextHop *b);
void network_drop_invalid_nexthops(Network *network); void network_drop_invalid_nexthops(Network *network);
int link_drop_nexthops(Link *link); int link_drop_managed_nexthops(Link *link);
int link_drop_foreign_nexthops(Link *link); int link_drop_foreign_nexthops(Link *link);
void link_foreignize_nexthops(Link *link); void link_foreignize_nexthops(Link *link);

View File

@ -788,8 +788,8 @@ static void manager_mark_routes(Manager *manager, bool foreign, const Link *exce
if (route->protocol == RTPROT_KERNEL) if (route->protocol == RTPROT_KERNEL)
continue; continue;
/* When 'foreign' is true, do not remove routes we configured. */ /* When 'foreign' is true, mark only foreign routes, and vice versa. */
if (foreign && route->source != NETWORK_CONFIG_SOURCE_FOREIGN) if (foreign != (route->source == NETWORK_CONFIG_SOURCE_FOREIGN))
continue; continue;
/* Do not touch dynamic routes. They will removed by dhcp_pd_prefix_lost() */ /* Do not touch dynamic routes. They will removed by dhcp_pd_prefix_lost() */
@ -834,7 +834,7 @@ static void manager_mark_routes(Manager *manager, bool foreign, const Link *exce
} }
} }
static int manager_drop_routes(Manager *manager) { static int manager_drop_marked_routes(Manager *manager) {
Route *route; Route *route;
int k, r = 0; int k, r = 0;
@ -955,14 +955,14 @@ int link_drop_foreign_routes(Link *link) {
manager_mark_routes(link->manager, /* foreign = */ true, NULL); manager_mark_routes(link->manager, /* foreign = */ true, NULL);
k = manager_drop_routes(link->manager); k = manager_drop_marked_routes(link->manager);
if (k < 0 && r >= 0) if (k < 0 && r >= 0)
r = k; r = k;
return r; return r;
} }
int link_drop_routes(Link *link) { int link_drop_managed_routes(Link *link) {
Route *route; Route *route;
int k, r = 0; int k, r = 0;
@ -973,6 +973,10 @@ int link_drop_routes(Link *link) {
if (route_by_kernel(route)) if (route_by_kernel(route))
continue; continue;
/* Do not touch routes managed by kernel or other tools. */
if (route->source == NETWORK_CONFIG_SOURCE_FOREIGN)
continue;
if (!route_exists(route)) if (!route_exists(route))
continue; continue;
@ -983,7 +987,7 @@ int link_drop_routes(Link *link) {
manager_mark_routes(link->manager, /* foreign = */ false, link); manager_mark_routes(link->manager, /* foreign = */ false, link);
k = manager_drop_routes(link->manager); k = manager_drop_marked_routes(link->manager);
if (k < 0 && r >= 0) if (k < 0 && r >= 0)
r = k; r = k;

View File

@ -82,7 +82,7 @@ int route_remove(Route *route);
int route_get(Manager *manager, Link *link, const Route *in, Route **ret); int route_get(Manager *manager, Link *link, const Route *in, Route **ret);
int link_drop_routes(Link *link); int link_drop_managed_routes(Link *link);
int link_drop_foreign_routes(Link *link); int link_drop_foreign_routes(Link *link);
void link_foreignize_routes(Link *link); void link_foreignize_routes(Link *link);

View File

@ -653,8 +653,8 @@ static void manager_mark_routing_policy_rules(Manager *m, bool foreign, const Li
if (rule->protocol == RTPROT_KERNEL) if (rule->protocol == RTPROT_KERNEL)
continue; continue;
/* When 'foreign' is true, do not remove rules we configured. */ /* When 'foreign' is true, mark only foreign rules, and vice versa. */
if (foreign && rule->source != NETWORK_CONFIG_SOURCE_FOREIGN) if (foreign != (rule->source == NETWORK_CONFIG_SOURCE_FOREIGN))
continue; continue;
/* Ignore rules not assigned yet or already removing. */ /* Ignore rules not assigned yet or already removing. */

View File

@ -71,7 +71,7 @@ int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const L
static inline int manager_drop_foreign_routing_policy_rules(Manager *m) { static inline int manager_drop_foreign_routing_policy_rules(Manager *m) {
return manager_drop_routing_policy_rules_internal(m, true, NULL); return manager_drop_routing_policy_rules_internal(m, true, NULL);
} }
static inline int link_drop_routing_policy_rules(Link *link) { static inline int link_drop_managed_routing_policy_rules(Link *link) {
assert(link); assert(link);
return manager_drop_routing_policy_rules_internal(link->manager, false, link); return manager_drop_routing_policy_rules_internal(link->manager, false, link);
} }

View File

@ -110,7 +110,7 @@ static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange
} }
} }
log_link_debug(l, "link is confiured by networkd and online."); log_link_debug(l, "link is configured by networkd and online.");
return 1; return 1;
} }

View File

@ -449,7 +449,16 @@ ExecCommandFlags exec_command_flags_from_string(const char *s) {
} }
int fexecve_or_execve(int executable_fd, const char *executable, char *const argv[], char *const envp[]) { int fexecve_or_execve(int executable_fd, const char *executable, char *const argv[], char *const envp[]) {
/* Refuse invalid fds, regardless if fexecve() use is enabled or not */
if (executable_fd < 0)
return -EBADF;
/* Block any attempts on exploiting Linux' liberal argv[] handling, i.e. CVE-2021-4034 and suchlike */
if (isempty(executable) || strv_isempty(argv))
return -EINVAL;
#if ENABLE_FEXECVE #if ENABLE_FEXECVE
execveat(executable_fd, "", argv, envp, AT_EMPTY_PATH); execveat(executable_fd, "", argv, envp, AT_EMPTY_PATH);
if (IN_SET(errno, ENOSYS, ENOENT) || ERRNO_IS_PRIVILEGE(errno)) if (IN_SET(errno, ENOSYS, ENOENT) || ERRNO_IS_PRIVILEGE(errno))

View File

@ -3893,7 +3893,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
print(output) print(output)
self.assertRegex(output, 'NO-CARRIER') self.assertRegex(output, 'NO-CARRIER')
self.assertNotRegex(output, '192.168.0.15/24') self.assertNotRegex(output, '192.168.0.15/24')
self.assertNotRegex(output, '192.168.0.16/24') self.assertRegex(output, '192.168.0.16/24') # foreign address is kept
print('### ip -6 route list table all dev bridge99') print('### ip -6 route list table all dev bridge99')
output = check_output('ip -6 route list table all dev bridge99') output = check_output('ip -6 route list table all dev bridge99')