1
0
mirror of https://github.com/systemd/systemd synced 2026-03-09 22:54:56 +01:00

Compare commits

...

19 Commits

Author SHA1 Message Date
Yu Watanabe
c83e44110c
Merge pull request #18718 from yuwata/network-nexthop-blackhole
network: introduce Blackhole= setting in [NextHop] section
2021-02-23 04:44:12 +09:00
Yu Watanabe
e82a0ef1cf
Merge pull request #18734 from poettering/cryptsetup-description-escape
cryptsetup: unescape ID_PART_ENTRY_NAME udev field
2021-02-23 04:43:22 +09:00
Luca Boccassi
79e20ceb59 MountImages: fix exec_context_dump printf
Use strempty as options might not be set, and add the separator
for each option tuple
2021-02-23 04:42:39 +09:00
Yu Watanabe
518339d89f
Merge pull request #18731 from yuwata/backlight-trivial-cleanups
backlight: trivial cleanups
2021-02-23 04:42:09 +09:00
Zbigniew Jędrzejewski-Szmek
e6c31188dc
Merge pull request #18735 from poettering/some-doc-fixes
three documentation fixes
2021-02-22 19:45:01 +01:00
Yu Watanabe
69a91c704e test-network: add tests for Blackhole= setting in [NextHop] section 2021-02-23 02:21:35 +09:00
Yu Watanabe
484555f3f2 network: route: shorten code a bit 2021-02-23 02:21:31 +09:00
Yu Watanabe
30f1083709 network: nexthop: add Blackhole= setting in [NextHop] section
As similar to unreachable type routes, blackhole nexthops do not have
NHA_OID attribute, so they are managed by Manager.
2021-02-23 02:21:17 +09:00
Lennart Poettering
0a6aa7a238 man: try to improve documentation of conditions/asserts
Fixes: #18725
2021-02-22 18:12:34 +01:00
Lennart Poettering
f553b772e9 docs: , → . 2021-02-22 18:00:14 +01:00
Lennart Poettering
c6e33c293e man: correct documentaiton of StandardInput='s defaults in regards to "data"
Fixes: #18710
2021-02-22 17:52:20 +01:00
Lennart Poettering
fadd34dd5a cryptsetup: unescape ID_PART_ENTRY_NAME udev property before using it
Fixes: #18729
2021-02-22 17:39:50 +01:00
Lennart Poettering
930aa88f04 tree-wide: use sd_device_new_from_stat_rdev() whereever appropriate 2021-02-22 17:38:57 +01:00
Yu Watanabe
b77c929934 backlight: reindent comments 2021-02-22 23:45:55 +09:00
Yu Watanabe
d17d66f0ab backlight: reduce indentation a bit 2021-02-22 23:25:00 +09:00
Zbigniew Jędrzejewski-Szmek
5735ab0979 fuzz-systemctl-parse-argv: add "telinit" to the argv[0] list 2021-02-22 14:22:07 +00:00
Yu Watanabe
ba6c9b7994 backlight: same_device() may return negative errno 2021-02-22 23:11:28 +09:00
Yu Watanabe
7a9737bc7d backlight: exit earlier when unknown verb is specified 2021-02-22 23:00:46 +09:00
Yu Watanabe
84d32bf59b test-network: test wireguard peer in drop-in config
Closes #18241.
2021-02-22 21:33:36 +09:00
27 changed files with 382 additions and 187 deletions

View File

@ -226,7 +226,7 @@ appliance-like installations.
### What partitioning tools will create a DPS-compliant partition table? ### What partitioning tools will create a DPS-compliant partition table?
As of util-linux 2.25.2, the `fdisk` tool provides type codes to create the As of util-linux 2.25.2, the `fdisk` tool provides type codes to create the
root, home, and swap partitions that the DPS expects, By default, `fdisk` will root, home, and swap partitions that the DPS expects. By default, `fdisk` will
create an old-style MBR, not a GPT, so typing `l` to list partition types will create an old-style MBR, not a GPT, so typing `l` to list partition types will
not show the choices to let you set the correct UUID. Make sure to first create not show the choices to let you set the correct UUID. Make sure to first create
an empty GPT, then type `l` in order for the DPS-compliant type codes to be an empty GPT, then type `l` in order for the DPS-compliant type codes to be

View File

@ -2408,7 +2408,9 @@ SystemCallErrorNumber=EPERM</programlisting>
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more
details about named file descriptors and their ordering.</para> details about named file descriptors and their ordering.</para>
<para>This setting defaults to <option>null</option>.</para></listitem> <para>This setting defaults to <option>null</option>, unless
<varname>StandardInputText=</varname>/<varname>StandardInputData=</varname> are set, in which case it
defaults to <option>data</option>.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
@ -2522,9 +2524,11 @@ SystemCallErrorNumber=EPERM</programlisting>
<term><varname>StandardInputText=</varname></term> <term><varname>StandardInputText=</varname></term>
<term><varname>StandardInputData=</varname></term> <term><varname>StandardInputData=</varname></term>
<listitem><para>Configures arbitrary textual or binary data to pass via file descriptor 0 (STDIN) to the <listitem><para>Configures arbitrary textual or binary data to pass via file descriptor 0 (STDIN) to
executed processes. These settings have no effect unless <varname>StandardInput=</varname> is set to the executed processes. These settings have no effect unless <varname>StandardInput=</varname> is set
<option>data</option>. Use this option to embed process input data directly in the unit file.</para> to <option>data</option> (which is the default if <varname>StandardInput=</varname> is not set
otherwise, but <varname>StandardInputText=</varname>/<varname>StandardInputData=</varname> is). Use
this option to embed process input data directly in the unit file.</para>
<para><varname>StandardInputText=</varname> accepts arbitrary textual data. C-style escapes for special <para><varname>StandardInputText=</varname> accepts arbitrary textual data. C-style escapes for special
characters as well as the usual <literal>%</literal>-specifiers are resolved. Each time this setting is used characters as well as the usual <literal>%</literal>-specifiers are resolved. Each time this setting is used

View File

@ -1349,6 +1349,14 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
<literal>no</literal>.</para> <literal>no</literal>.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>Blackhole=</varname></term>
<listitem>
<para>Takes a boolean. If enabled, packets to the corresponding routes are discarded
silently, and <varname>Gateway=</varname> cannot be specified. Defaults to
<literal>no</literal>.</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>

View File

@ -1063,28 +1063,32 @@
<refsect2> <refsect2>
<title>Conditions and Asserts</title> <title>Conditions and Asserts</title>
<para>Unit files may also include a number of <varname index="false">Condition…=</varname> and <para>Unit files may also include a number of <varname index="false">Condition…=</varname> and <varname
<varname index="false">Assert…=</varname> settings. Before the unit is started, systemd will verify index="false">Assert…=</varname> settings. Before the unit is started, systemd will verify that the
that the specified conditions are true. If not, the starting of the unit will be (mostly silently) specified conditions and asserts are true. If not, the starting of the unit will be (mostly silently)
skipped. Failing conditions will not result in the unit being moved into the <literal>failed</literal> skipped (in case of conditions), or aborted with an error message (in case of asserts). Failing
state. The conditions are checked at the time the queued start job is to be executed. The ordering conditions or asserts will not result in the unit being moved into the <literal>failed</literal>
dependencies are still respected, so other units are still pulled in and ordered as if this unit was state. The conditions and asserts are checked at the time the queued start job is to be executed. The
successfully activated. Use condition expressions in order to skip units that do not apply to the local ordering dependencies are still respected, so other units are still pulled in and ordered as if this
system, for example because the kernel or runtime environment doesn't require their functionality. unit was successfully activated, and the conditions and asserts are executed the precise moment the
unit would normally start and thus can validate system state after the units ordered before completed
initialization. Use condition expressions for skipping units that do not apply to the local system, for
example because the kernel or runtime environment doesn't require their functionality.
</para> </para>
<para>If multiple conditions are specified, the unit will be executed if all of them apply (i.e. a <para>If multiple conditions are specified, the unit will be executed if all of them apply (i.e. a
logical AND is applied). Condition checks can use a pipe symbol (<literal>|</literal>) after the equals logical AND is applied). Condition checks can use a pipe symbol (<literal>|</literal>) after the equals
sign (<literal>Condition…=|…</literal>), which causes the condition becomes a triggering condition. If sign (<literal>Condition…=|…</literal>), which causes the condition to become a
at least one triggering condition is defined for a unit, then the unit will be executed if at least one <emphasis>triggering</emphasis> condition. If at least one triggering condition is defined for a unit,
of the triggering conditions apply and all of the non-triggering conditions. If you prefix an argument then the unit will be started if at least one of the triggering conditions of the unit applies and all
with the pipe symbol and an exclamation mark, the pipe symbol must be passed first, the exclamation of the regular (i.e. non-triggering) conditions apply. If you prefix an argument with the pipe symbol
second. If any of these options is assigned the empty string, the list of conditions is reset and an exclamation mark, the pipe symbol must be passed first, the exclamation second. If any of these
completely, all previous condition settings (of any kind) will have no effect.</para> options is assigned the empty string, the list of conditions is reset completely, all previous
condition settings (of any kind) will have no effect.</para>
<para>The <varname>AssertArchitecture=</varname>, <varname>AssertVirtualization=</varname>, … options <para>The <varname>AssertArchitecture=</varname>, <varname>AssertVirtualization=</varname>, … options
provide a similar mechanism that causes the job to fail (instead of being skipped). The failed check is are similar to conditions but cause the start job to fail (instead of being skipped). The failed check
logged. Units with failed conditions are considered to be in a clean state and will be garbage is logged. Units with failed conditions are considered to be in a clean state and will be garbage
collected if they are not referenced. This means that when queried, the condition failure may or may collected if they are not referenced. This means that when queried, the condition failure may or may
not show up in the state of the unit.</para> not show up in the state of the unit.</para>

View File

@ -71,11 +71,9 @@ static int find_pci_or_platform_parent(sd_device *device, sd_device **ret) {
return -ENODATA; return -ENODATA;
c += strspn(c, DIGITS); c += strspn(c, DIGITS);
if (*c == '-') { if (*c == '-' && !STARTSWITH_SET(c, "-LVDS-", "-Embedded DisplayPort-"))
/* A connector DRM device, let's ignore all but LVDS and eDP! */ /* A connector DRM device, let's ignore all but LVDS and eDP! */
if (!STARTSWITH_SET(c, "-LVDS-", "-Embedded DisplayPort-"))
return -EOPNOTSUPP; return -EOPNOTSUPP;
}
} else if (streq(subsystem, "pci") && } else if (streq(subsystem, "pci") &&
sd_device_get_sysattr_value(parent, "class", &value) >= 0) { sd_device_get_sysattr_value(parent, "class", &value) >= 0) {
@ -137,19 +135,14 @@ static int validate_device(sd_device *device) {
assert(device); assert(device);
/* Verify whether we should actually care for a specific /* Verify whether we should actually care for a specific backlight device. For backlight devices
* backlight device. For backlight devices there might be * there might be multiple ways to access the same control: "firmware" (i.e. ACPI), "platform"
* multiple ways to access the same control: "firmware" * (i.e. via the machine's EC) and "raw" (via the graphics card). In general we should prefer
* (i.e. ACPI), "platform" (i.e. via the machine's EC) and * "firmware" (i.e. ACPI) or "platform" access over "raw" access, in order not to confuse the
* "raw" (via the graphics card). In general we should prefer * BIOS/EC, and compatibility with possible low-level hotkey handling of screen brightness. The
* "firmware" (i.e. ACPI) or "platform" access over "raw" * kernel will already make sure to expose only one of "firmware" and "platform" for the same
* access, in order not to confuse the BIOS/EC, and * device to userspace. However, we still need to make sure that we use "raw" only if no
* compatibility with possible low-level hotkey handling of * "firmware" or "platform" device for the same device exists. */
* screen brightness. The kernel will already make sure to
* expose only one of "firmware" and "platform" for the same
* device to userspace. However, we still need to make sure
* that we use "raw" only if no "firmware" or "platform"
* device for the same device exists. */
r = sd_device_get_subsystem(device, &subsystem); r = sd_device_get_subsystem(device, &subsystem);
if (r < 0) if (r < 0)
@ -194,13 +187,12 @@ static int validate_device(sd_device *device) {
!STR_IN_SET(v, "platform", "firmware")) !STR_IN_SET(v, "platform", "firmware"))
continue; continue;
/* OK, so there's another backlight device, and it's a /* OK, so there's another backlight device, and it's a platform or firmware device.
* platform or firmware device, so, let's see if we * Let's see if we can verify it belongs to the same device as ours. */
* can verify it belongs to the same device as ours. */
if (find_pci_or_platform_parent(other, &other_parent) < 0) if (find_pci_or_platform_parent(other, &other_parent) < 0)
continue; continue;
if (same_device(parent, other_parent)) { if (same_device(parent, other_parent) > 0) {
const char *device_sysname = NULL, *other_sysname = NULL; const char *device_sysname = NULL, *other_sysname = NULL;
/* Both have the same PCI parent, that means we are out. */ /* Both have the same PCI parent, that means we are out. */
@ -257,11 +249,6 @@ static int get_max_brightness(sd_device *device, unsigned *ret) {
return 0; return 0;
} }
/* Some systems turn the backlight all the way off at the lowest levels.
* clamp_brightness clamps the saved brightness to at least 1 or 5% of
* max_brightness in case of 'backlight' subsystem. This avoids preserving
* an unreadably dim screen, which would otherwise force the user to
* disable state restoration. */
static int clamp_brightness(sd_device *device, bool saved, unsigned max_brightness, unsigned *brightness) { static int clamp_brightness(sd_device *device, bool saved, unsigned max_brightness, unsigned *brightness) {
unsigned new_brightness, min_brightness; unsigned new_brightness, min_brightness;
const char *subsystem; const char *subsystem;
@ -270,6 +257,11 @@ static int clamp_brightness(sd_device *device, bool saved, unsigned max_brightne
assert(device); assert(device);
assert(brightness); assert(brightness);
/* Some systems turn the backlight all the way off at the lowest levels. This clamps the saved
* brightness to at least 1 or 5% of max_brightness in case of 'backlight' subsystem. This
* avoids preserving an unreadably dim screen, which would otherwise force the user to disable
* state restoration. */
r = sd_device_get_subsystem(device, &subsystem); r = sd_device_get_subsystem(device, &subsystem);
if (r < 0) if (r < 0)
return log_device_warning_errno(device, r, "Failed to get device subsystem: %m"); return log_device_warning_errno(device, r, "Failed to get device subsystem: %m");
@ -388,6 +380,9 @@ static int run(int argc, char *argv[]) {
if (argc != 3) if (argc != 3)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires two arguments."); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires two arguments.");
if (!STR_IN_SET(argv[1], "load", "save"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb %s.", argv[1]);
umask(0022); umask(0022);
r = mkdir_p("/var/lib/systemd/backlight", 0755); r = mkdir_p("/var/lib/systemd/backlight", 0755);
@ -409,9 +404,8 @@ static int run(int argc, char *argv[]) {
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to get backlight or LED device '%s:%s': %m", ss, sysname); return log_error_errno(r, "Failed to get backlight or LED device '%s:%s': %m", ss, sysname);
/* If max_brightness is 0, then there is no actual backlight /* If max_brightness is 0, then there is no actual backlight device. This happens on desktops
* device. This happens on desktops with Asus mainboards * with Asus mainboards that load the eeepc-wmi module. */
* that load the eeepc-wmi module. */
if (get_max_brightness(device, &max_brightness) < 0) if (get_max_brightness(device, &max_brightness) < 0)
return 0; return 0;
@ -432,14 +426,11 @@ static int run(int argc, char *argv[]) {
} else } else
saved = strjoina("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname); saved = strjoina("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname);
/* If there are multiple conflicting backlight devices, then /* If there are multiple conflicting backlight devices, then their probing at boot-time might
* their probing at boot-time might happen in any order. This * happen in any order. This means the validity checking of the device then is not reliable,
* means the validity checking of the device then is not * since it might not see other devices conflicting with a specific backlight. To deal with
* reliable, since it might not see other devices conflicting * this, we will actively delete backlight state files at shutdown (where device probing should
* with a specific backlight. To deal with this, we will * be complete), so that the validity check at boot time doesn't have to be reliable. */
* actively delete backlight state files at shutdown (where
* device probing should be complete), so that the validity
* check at boot time doesn't have to be reliable. */
if (streq(argv[1], "load")) { if (streq(argv[1], "load")) {
_cleanup_free_ char *value = NULL; _cleanup_free_ char *value = NULL;
@ -503,7 +494,7 @@ static int run(int argc, char *argv[]) {
return log_device_error_errno(device, r, "Failed to write %s: %m", saved); return log_device_error_errno(device, r, "Failed to write %s: %m", saved);
} else } else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb %s.", argv[1]); assert_not_reached("Unknown verb.");
return 0; return 0;
} }

View File

@ -5648,15 +5648,14 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
for (size_t i = 0; i < c->n_mount_images; i++) { for (size_t i = 0; i < c->n_mount_images; i++) {
MountOptions *o; MountOptions *o;
fprintf(f, "%sMountImages: %s%s:%s%s", prefix, fprintf(f, "%sMountImages: %s%s:%s", prefix,
c->mount_images[i].ignore_enoent ? "-": "", c->mount_images[i].ignore_enoent ? "-": "",
c->mount_images[i].source, c->mount_images[i].source,
c->mount_images[i].destination, c->mount_images[i].destination);
LIST_IS_EMPTY(c->mount_images[i].mount_options) ? "": ":");
LIST_FOREACH(mount_options, o, c->mount_images[i].mount_options) LIST_FOREACH(mount_options, o, c->mount_images[i].mount_options)
fprintf(f, "%s:%s", fprintf(f, ":%s:%s",
partition_designator_to_string(o->partition_designator), partition_designator_to_string(o->partition_designator),
o->options); strempty(o->options));
fprintf(f, "\n"); fprintf(f, "\n");
} }
} }

View File

@ -419,7 +419,6 @@ static int parse_options(const char *options) {
static char* disk_description(const char *path) { static char* disk_description(const char *path) {
static const char name_fields[] = static const char name_fields[] =
"ID_PART_ENTRY_NAME\0"
"DM_NAME\0" "DM_NAME\0"
"ID_MODEL_FROM_DATABASE\0" "ID_MODEL_FROM_DATABASE\0"
"ID_MODEL\0"; "ID_MODEL\0";
@ -427,6 +426,7 @@ static char* disk_description(const char *path) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL; _cleanup_(sd_device_unrefp) sd_device *device = NULL;
const char *i, *name; const char *i, *name;
struct stat st; struct stat st;
int r;
assert(path); assert(path);
@ -436,9 +436,27 @@ static char* disk_description(const char *path) {
if (!S_ISBLK(st.st_mode)) if (!S_ISBLK(st.st_mode))
return NULL; return NULL;
if (sd_device_new_from_devnum(&device, 'b', st.st_rdev) < 0) if (sd_device_new_from_stat_rdev(&device, &st) < 0)
return NULL; return NULL;
if (sd_device_get_property_value(device, "ID_PART_ENTRY_NAME", &name) >= 0) {
_cleanup_free_ char *unescaped = NULL;
/* ID_PART_ENTRY_NAME uses \x style escaping, using libblkid's blkid_encode_string(). Let's
* reverse this here to make the string more human friendly in case people embed spaces or
* other weird stuff. */
r = cunescape(name, UNESCAPE_RELAX, &unescaped);
if (r < 0) {
log_debug_errno(r, "Failed to unescape ID_PART_ENTRY_NAME, skipping device: %m");
return NULL;
}
if (!isempty(unescaped) && !string_has_cc(unescaped, NULL))
return TAKE_PTR(unescaped);
}
/* These need no unescaping. */
NULSTR_FOREACH(i, name_fields) NULSTR_FOREACH(i, name_fields)
if (sd_device_get_property_value(device, i, &name) >= 0 && if (sd_device_get_property_value(device, i, &name) >= 0 &&
!isempty(name)) !isempty(name))

View File

@ -287,7 +287,7 @@ static int run(int argc, char *argv[]) {
"%s is not a block device.", "%s is not a block device.",
device); device);
r = sd_device_new_from_devnum(&dev, 'b', st.st_rdev); r = sd_device_new_from_stat_rdev(&dev, &st);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to detect device %s: %m", device); return log_error_errno(r, "Failed to detect device %s: %m", device);

View File

@ -2711,7 +2711,7 @@ static int home_get_image_path_seat(Home *h, char **ret) {
if (!S_ISBLK(st.st_mode)) if (!S_ISBLK(st.st_mode))
return -ENOTBLK; return -ENOTBLK;
r = sd_device_new_from_devnum(&d, 'b', st.st_rdev); r = sd_device_new_from_stat_rdev(&d, &st);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -929,7 +929,7 @@ static int umount_by_device(sd_bus *bus, const char *what) {
return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK),
"Not a block device: %s", what); "Not a block device: %s", what);
r = sd_device_new_from_devnum(&d, 'b', st.st_rdev); r = sd_device_new_from_stat_rdev(&d, &st);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to get device from device number: %m"); return log_error_errno(r, "Failed to get device from device number: %m");
@ -1270,7 +1270,7 @@ static int discover_loop_backing_file(void) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid file type: %s", loop_dev); "Invalid file type: %s", loop_dev);
r = sd_device_new_from_devnum(&d, 'b', st.st_rdev); r = sd_device_new_from_stat_rdev(&d, &st);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to get device from device number: %m"); return log_error_errno(r, "Failed to get device from device number: %m");
@ -1314,7 +1314,7 @@ static int discover_device(void) {
"Invalid file type: %s", "Invalid file type: %s",
arg_mount_what); arg_mount_what);
r = sd_device_new_from_devnum(&d, 'b', st.st_rdev); r = sd_device_new_from_stat_rdev(&d, &st);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to get device from device number: %m"); return log_error_errno(r, "Failed to get device from device number: %m");

View File

@ -894,6 +894,8 @@ Manager* manager_free(Manager *m) {
m->routes = set_free(m->routes); m->routes = set_free(m->routes);
m->routes_foreign = set_free(m->routes_foreign); m->routes_foreign = set_free(m->routes_foreign);
m->nexthops = set_free(m->nexthops);
m->nexthops_foreign = set_free(m->nexthops_foreign);
m->nexthops_by_id = hashmap_free(m->nexthops_by_id); m->nexthops_by_id = hashmap_free(m->nexthops_by_id);
sd_event_source_unref(m->speed_meter_event_source); sd_event_source_unref(m->speed_meter_event_source);

View File

@ -64,6 +64,10 @@ struct Manager {
/* Manage nexthops by id. */ /* Manage nexthops by id. */
Hashmap *nexthops_by_id; Hashmap *nexthops_by_id;
/* Manager stores nexthops without RTA_OIF attribute. */
Set *nexthops;
Set *nexthops_foreign;
/* Manager stores routes without RTA_OIF attribute. */ /* Manager stores routes without RTA_OIF attribute. */
Set *routes; Set *routes;
Set *routes_foreign; Set *routes_foreign;

View File

@ -190,6 +190,7 @@ NextHop.Id, config_parse_nexthop_id,
NextHop.Gateway, config_parse_nexthop_gateway, 0, 0 NextHop.Gateway, config_parse_nexthop_gateway, 0, 0
NextHop.Family, config_parse_nexthop_family, 0, 0 NextHop.Family, config_parse_nexthop_family, 0, 0
NextHop.OnLink, config_parse_nexthop_onlink, 0, 0 NextHop.OnLink, config_parse_nexthop_onlink, 0, 0
NextHop.Blackhole, config_parse_nexthop_blackhole, 0, 0
DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
DHCPv4.UseDNS, config_parse_dhcp_use_dns, 0, 0 DHCPv4.UseDNS, config_parse_dhcp_use_dns, 0, 0
DHCPv4.RoutesToDNS, config_parse_bool, 0, offsetof(Network, dhcp_routes_to_dns) DHCPv4.RoutesToDNS, config_parse_bool, 0, offsetof(Network, dhcp_routes_to_dns)

View File

@ -33,6 +33,14 @@ NextHop *nexthop_free(NextHop *nexthop) {
hashmap_remove(nexthop->link->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id)); hashmap_remove(nexthop->link->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id));
} }
if (nexthop->manager) {
set_remove(nexthop->manager->nexthops, nexthop);
set_remove(nexthop->manager->nexthops_foreign, nexthop);
if (nexthop->id > 0)
hashmap_remove(nexthop->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id));
}
return mfree(nexthop); return mfree(nexthop);
} }
@ -95,6 +103,7 @@ static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) {
assert(nexthop); assert(nexthop);
siphash24_compress(&nexthop->id, sizeof(nexthop->id), state); siphash24_compress(&nexthop->id, sizeof(nexthop->id), state);
siphash24_compress(&nexthop->blackhole, sizeof(nexthop->blackhole), state);
siphash24_compress(&nexthop->family, sizeof(nexthop->family), state); siphash24_compress(&nexthop->family, sizeof(nexthop->family), state);
switch (nexthop->family) { switch (nexthop->family) {
@ -116,6 +125,10 @@ static int nexthop_compare_func(const NextHop *a, const NextHop *b) {
if (r != 0) if (r != 0)
return r; return r;
r = CMP(a->blackhole, b->blackhole);
if (r != 0)
return r;
r = CMP(a->family, b->family); r = CMP(a->family, b->family);
if (r != 0) if (r != 0)
return r; return r;
@ -133,6 +146,18 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
nexthop_compare_func, nexthop_compare_func,
nexthop_free); nexthop_free);
static void nexthop_copy(NextHop *dest, const NextHop *src) {
assert(dest);
assert(src);
/* This only copies entries used in the above hash and compare functions. */
dest->id = src->id;
dest->blackhole = src->blackhole;
dest->family = src->family;
dest->gw = src->gw;
}
int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) { int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) {
NextHop *nh; NextHop *nh;
@ -150,20 +175,20 @@ int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) {
return 0; return 0;
} }
static int nexthop_get(Link *link, const NextHop *in, NextHop **ret) { static int nexthop_get(Manager *manager, Link *link, const NextHop *in, NextHop **ret) {
NextHop *existing; NextHop *existing;
assert(link); assert(manager || link);
assert(in); assert(in);
existing = set_get(link->nexthops, in); existing = set_get(link ? link->nexthops : manager->nexthops, in);
if (existing) { if (existing) {
if (ret) if (ret)
*ret = existing; *ret = existing;
return 1; return 1;
} }
existing = set_get(link->nexthops_foreign, in); existing = set_get(link ? link->nexthops_foreign : manager->nexthops_foreign, in);
if (existing) { if (existing) {
if (ret) if (ret)
*ret = existing; *ret = existing;
@ -173,11 +198,11 @@ static int nexthop_get(Link *link, const NextHop *in, NextHop **ret) {
return -ENOENT; return -ENOENT;
} }
static int nexthop_add_internal(Link *link, Set **nexthops, const NextHop *in, NextHop **ret) { static int nexthop_add_internal(Manager *manager, Link *link, Set **nexthops, const NextHop *in, NextHop **ret) {
_cleanup_(nexthop_freep) NextHop *nexthop = NULL; _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
int r; int r;
assert(link); assert(manager || link);
assert(nexthops); assert(nexthops);
assert(in); assert(in);
@ -185,9 +210,7 @@ static int nexthop_add_internal(Link *link, Set **nexthops, const NextHop *in, N
if (r < 0) if (r < 0)
return r; return r;
nexthop->id = in->id; nexthop_copy(nexthop, in);
nexthop->family = in->family;
nexthop->gw = in->gw;
r = set_ensure_put(nexthops, &nexthop_hash_ops, nexthop); r = set_ensure_put(nexthops, &nexthop_hash_ops, nexthop);
if (r < 0) if (r < 0)
@ -196,6 +219,7 @@ static int nexthop_add_internal(Link *link, Set **nexthops, const NextHop *in, N
return -EEXIST; return -EEXIST;
nexthop->link = link; nexthop->link = link;
nexthop->manager = manager;
if (ret) if (ret)
*ret = nexthop; *ret = nexthop;
@ -204,8 +228,9 @@ static int nexthop_add_internal(Link *link, Set **nexthops, const NextHop *in, N
return 0; return 0;
} }
static int nexthop_add_foreign(Link *link, const NextHop *in, NextHop **ret) { static int nexthop_add_foreign(Manager *manager, Link *link, const NextHop *in, NextHop **ret) {
return nexthop_add_internal(link, &link->nexthops_foreign, in, ret); assert(manager || link);
return nexthop_add_internal(manager, link, link ? &link->nexthops_foreign : &manager->nexthops_foreign, in, ret);
} }
static int nexthop_add(Link *link, const NextHop *in, NextHop **ret) { static int nexthop_add(Link *link, const NextHop *in, NextHop **ret) {
@ -213,20 +238,30 @@ static int nexthop_add(Link *link, const NextHop *in, NextHop **ret) {
NextHop *nexthop; NextHop *nexthop;
int r; int r;
r = nexthop_get(link, in, &nexthop); assert(link);
assert(in);
if (in->blackhole)
r = nexthop_get(link->manager, NULL, in, &nexthop);
else
r = nexthop_get(NULL, link, in, &nexthop);
if (r == -ENOENT) { if (r == -ENOENT) {
/* NextHop does not exist, create a new one */ /* NextHop does not exist, create a new one */
r = nexthop_add_internal(link, &link->nexthops, in, &nexthop); r = nexthop_add_internal(link->manager,
in->blackhole ? NULL : link,
in->blackhole ? &link->manager->nexthops : &link->nexthops,
in, &nexthop);
if (r < 0) if (r < 0)
return r; return r;
is_new = true; is_new = true;
} else if (r == 0) { } else if (r == 0) {
/* Take over a foreign nexthop */ /* Take over a foreign nexthop */
r = set_ensure_put(&link->nexthops, &nexthop_hash_ops, nexthop); r = set_ensure_put(in->blackhole ? &link->manager->nexthops : &link->nexthops,
&nexthop_hash_ops, nexthop);
if (r < 0) if (r < 0)
return r; return r;
set_remove(link->nexthops_foreign, nexthop); set_remove(in->blackhole ? link->manager->nexthops_foreign : link->nexthops_foreign, nexthop);
} else if (r == 1) { } else if (r == 1) {
/* NextHop exists, do nothing */ /* NextHop exists, do nothing */
; ;
@ -238,11 +273,13 @@ static int nexthop_add(Link *link, const NextHop *in, NextHop **ret) {
return is_new; return is_new;
} }
static int nexthop_update(Link *link, NextHop *nexthop, const NextHop *in) { static int nexthop_update(Manager *manager, Link *link, NextHop *nexthop, const NextHop *in) {
Set *nexthops;
int r; int r;
assert(link); /* link may be NULL. */
assert(link->manager);
assert(manager);
assert(nexthop); assert(nexthop);
assert(in); assert(in);
assert(in->id > 0); assert(in->id > 0);
@ -255,19 +292,21 @@ static int nexthop_update(Link *link, NextHop *nexthop, const NextHop *in) {
return -EINVAL; return -EINVAL;
} }
nexthop = set_remove(link->nexthops, nexthop); nexthops = link ? link->nexthops : manager->nexthops;
nexthop = set_remove(nexthops, nexthop);
if (!nexthop) if (!nexthop)
return -ENOENT; return -ENOENT;
nexthop->id = in->id; nexthop->id = in->id;
r = set_put(link->nexthops, nexthop); r = set_put(nexthops, nexthop);
if (r <= 0) { if (r <= 0) {
int k; int k;
/* On failure, revert the change. */ /* On failure, revert the change. */
nexthop->id = 0; nexthop->id = 0;
k = set_put(link->nexthops, nexthop); k = set_put(nexthops, nexthop);
if (k <= 0) { if (k <= 0) {
nexthop_free(nexthop); nexthop_free(nexthop);
return k < 0 ? k : -EEXIST; return k < 0 ? k : -EEXIST;
@ -277,13 +316,14 @@ static int nexthop_update(Link *link, NextHop *nexthop, const NextHop *in) {
} }
set_manager: set_manager:
return hashmap_ensure_put(&link->manager->nexthops_by_id, NULL, UINT32_TO_PTR(nexthop->id), nexthop); return hashmap_ensure_put(&manager->nexthops_by_id, NULL, UINT32_TO_PTR(nexthop->id), nexthop);
} }
static void log_nexthop_debug(const NextHop *nexthop, uint32_t id, const char *str, const Link *link) { static void log_nexthop_debug(const NextHop *nexthop, uint32_t id, const char *str, const Link *link) {
assert(nexthop); assert(nexthop);
assert(str); assert(str);
assert(link);
/* link may be NULL. */
if (DEBUG_LOGGING) { if (DEBUG_LOGGING) {
_cleanup_free_ char *gw = NULL; _cleanup_free_ char *gw = NULL;
@ -291,11 +331,11 @@ static void log_nexthop_debug(const NextHop *nexthop, uint32_t id, const char *s
(void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw); (void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw);
if (nexthop->id == id) if (nexthop->id == id)
log_link_debug(link, "%s nexthop: id: %"PRIu32", gw: %s", log_link_debug(link, "%s nexthop: id: %"PRIu32", gw: %s, blackhole: %s",
str, nexthop->id, strna(gw)); str, nexthop->id, strna(gw), yes_no(nexthop->blackhole));
else else
log_link_debug(link, "%s nexthop: id: %"PRIu32"→%"PRIu32", gw: %s", log_link_debug(link, "%s nexthop: id: %"PRIu32"→%"PRIu32", gw: %s, blackhole: %s",
str, nexthop->id, id, strna(gw)); str, nexthop->id, id, strna(gw), yes_no(nexthop->blackhole));
} }
} }
@ -353,6 +393,11 @@ static int nexthop_configure(const NextHop *nexthop, Link *link) {
return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m"); return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m");
} }
if (nexthop->blackhole) {
r = sd_netlink_message_append_flag(req, NHA_BLACKHOLE);
if (r < 0)
return log_link_error_errno(link, r, "Could not append NHA_BLACKHOLE attribute: %m");
} else {
r = sd_netlink_message_append_u32(req, NHA_OIF, link->ifindex); r = sd_netlink_message_append_u32(req, NHA_OIF, link->ifindex);
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Could not append NHA_OIF attribute: %m"); return log_link_error_errno(link, r, "Could not append NHA_OIF attribute: %m");
@ -368,6 +413,7 @@ static int nexthop_configure(const NextHop *nexthop, Link *link) {
return log_link_error_errno(link, r, "Failed to set RTNH_F_ONLINK flag: %m"); return log_link_error_errno(link, r, "Failed to set RTNH_F_ONLINK flag: %m");
} }
} }
}
r = netlink_call_async(link->manager->rtnl, NULL, req, nexthop_handler, r = netlink_call_async(link->manager->rtnl, NULL, req, nexthop_handler,
link_netlink_destroy_callback, link); link_netlink_destroy_callback, link);
@ -431,7 +477,7 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
NextHop *nexthop = NULL; NextHop *nexthop = NULL;
uint32_t ifindex; uint32_t ifindex;
uint16_t type; uint16_t type;
Link *link; Link *link = NULL;
int r; int r;
assert(rtnl); assert(rtnl);
@ -456,13 +502,11 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
} }
r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex); r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex);
if (r == -ENODATA) { if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: received nexthop message without NHA_OIF attribute, ignoring: %m");
return 0;
} else if (r < 0) {
log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m"); log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
return 0; return 0;
} else if (ifindex <= 0) { } else if (r >= 0) {
if (ifindex <= 0) {
log_warning("rtnl: received nexthop message with invalid ifindex %"PRIu32", ignoring.", ifindex); log_warning("rtnl: received nexthop message with invalid ifindex %"PRIu32", ignoring.", ifindex);
return 0; return 0;
} }
@ -473,6 +517,7 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
log_warning("rtnl: received nexthop message for link (%"PRIu32") we do not know about, ignoring", ifindex); log_warning("rtnl: received nexthop message for link (%"PRIu32") we do not know about, ignoring", ifindex);
return 0; return 0;
} }
}
r = nexthop_new(&tmp); r = nexthop_new(&tmp);
if (r < 0) if (r < 0)
@ -491,6 +536,13 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
return 0; return 0;
} }
r = sd_netlink_message_has_flag(message, NHA_BLACKHOLE);
if (r < 0) {
log_link_warning_errno(link, r, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m");
return 0;
}
tmp->blackhole = r;
r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id); r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id);
if (r == -ENODATA) { if (r == -ENODATA) {
log_link_warning_errno(link, r, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m"); log_link_warning_errno(link, r, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m");
@ -503,7 +555,12 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
return 0; return 0;
} }
r = nexthop_get(link, tmp, &nexthop); /* All blackhole nexthops are managed by Manager. Note that the linux kernel does not set
* NHA_OID attribute when NHA_BLACKHOLE is set. Just for safety. */
if (tmp->blackhole)
link = NULL;
r = nexthop_get(m, link, tmp, &nexthop);
if (r < 0) { if (r < 0) {
uint32_t id; uint32_t id;
@ -512,7 +569,7 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
id = tmp->id; id = tmp->id;
tmp->id = 0; tmp->id = 0;
(void) nexthop_get(link, tmp, &nexthop); (void) nexthop_get(m, link, tmp, &nexthop);
tmp->id = id; tmp->id = id;
} }
@ -523,14 +580,14 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
log_nexthop_debug(nexthop, tmp->id, "Received remembered", link); log_nexthop_debug(nexthop, tmp->id, "Received remembered", link);
else { else {
log_nexthop_debug(tmp, tmp->id, "Remembering foreign", link); log_nexthop_debug(tmp, tmp->id, "Remembering foreign", link);
r = nexthop_add_foreign(link, tmp, &nexthop); r = nexthop_add_foreign(m, link, tmp, &nexthop);
if (r < 0) { if (r < 0) {
log_link_warning_errno(link, r, "Could not remember foreign nexthop, ignoring: %m"); log_link_warning_errno(link, r, "Could not remember foreign nexthop, ignoring: %m");
return 0; return 0;
} }
} }
r = nexthop_update(link, nexthop, tmp); r = nexthop_update(m, link, nexthop, tmp);
if (r < 0) { if (r < 0) {
log_link_warning_errno(link, r, "Could not update nexthop, ignoring: %m"); log_link_warning_errno(link, r, "Could not update nexthop, ignoring: %m");
return 0; return 0;
@ -556,6 +613,12 @@ static int nexthop_section_verify(NextHop *nh) {
/* When no Gateway= is specified, assume IPv4. */ /* When no Gateway= is specified, assume IPv4. */
nh->family = AF_INET; nh->family = AF_INET;
if (nh->blackhole && in_addr_is_set(nh->family, &nh->gw))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: blackhole nexthop cannot have gateway address. "
"Ignoring [NextHop] section from line %u.",
nh->section->filename, nh->section->line);
if (nh->onlink < 0 && in_addr_is_set(nh->family, &nh->gw) && if (nh->onlink < 0 && in_addr_is_set(nh->family, &nh->gw) &&
ordered_hashmap_isempty(nh->network->addresses_by_section)) { ordered_hashmap_isempty(nh->network->addresses_by_section)) {
/* If no address is configured, in most cases the gateway cannot be reachable. /* If no address is configured, in most cases the gateway cannot be reachable.
@ -784,3 +847,42 @@ int config_parse_nexthop_onlink(
TAKE_PTR(n); TAKE_PTR(n);
return 0; return 0;
} }
int config_parse_nexthop_blackhole(
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_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
Network *network = userdata;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
r = nexthop_new_static(network, filename, section_line, &n);
if (r < 0)
return log_oom();
r = parse_boolean(rvalue);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
return 0;
}
n->blackhole = r;
TAKE_PTR(n);
return 0;
}

View File

@ -20,11 +20,13 @@ typedef struct NextHop {
Network *network; Network *network;
NetworkConfigSection *section; NetworkConfigSection *section;
Manager *manager;
Link *link; Link *link;
unsigned char protocol; unsigned char protocol;
uint32_t id; uint32_t id;
bool blackhole;
int family; int family;
union in_addr_union gw; union in_addr_union gw;
int onlink; int onlink;
@ -43,3 +45,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_id);
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_gateway); CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_gateway);
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_family); CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_family);
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_onlink); CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_onlink);
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_blackhole);

View File

@ -451,35 +451,19 @@ static int route_get(const Manager *manager, const Link *link, const Route *in,
assert(manager || link); assert(manager || link);
assert(in); assert(in);
if (link) { existing = set_get(link ? link->routes : manager->routes, in);
existing = set_get(link->routes, in);
if (existing) { if (existing) {
if (ret) if (ret)
*ret = existing; *ret = existing;
return 1; return 1;
} }
existing = set_get(link->routes_foreign, in); existing = set_get(link ? link->routes_foreign : manager->routes_foreign, in);
if (existing) { if (existing) {
if (ret) if (ret)
*ret = existing; *ret = existing;
return 0; return 0;
} }
} else {
existing = set_get(manager->routes, in);
if (existing) {
if (ret)
*ret = existing;
return 1;
}
existing = set_get(manager->routes_foreign, in);
if (existing) {
if (ret)
*ret = existing;
return 0;
}
}
return -ENOENT; return -ENOENT;
} }
@ -488,6 +472,8 @@ static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, c
assert(dest); assert(dest);
assert(src); assert(src);
/* This only copies entries used by the above hash and compare functions. */
dest->family = src->family; dest->family = src->family;
dest->src = src->src; dest->src = src->src;
dest->src_prefixlen = src->src_prefixlen; dest->src_prefixlen = src->src_prefixlen;
@ -496,6 +482,9 @@ static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, c
dest->prefsrc = src->prefsrc; dest->prefsrc = src->prefsrc;
dest->scope = src->scope; dest->scope = src->scope;
dest->protocol = src->protocol; dest->protocol = src->protocol;
if (nh && nh->blackhole)
dest->type = RTN_BLACKHOLE;
else
dest->type = src->type; dest->type = src->type;
dest->tos = src->tos; dest->tos = src->tos;
dest->priority = src->priority; dest->priority = src->priority;
@ -593,19 +582,11 @@ static int route_add(Manager *manager, Link *link, const Route *in, const Multip
is_new = true; is_new = true;
} else if (r == 0) { } else if (r == 0) {
/* Take over a foreign route */ /* Take over a foreign route */
if (link) { r = set_ensure_put(link ? &link->routes : &manager->routes, &route_hash_ops, route);
r = set_ensure_put(&link->routes, &route_hash_ops, route);
if (r < 0) if (r < 0)
return r; return r;
set_remove(link->routes_foreign, route); set_remove(link ? link->routes_foreign : manager->routes_foreign, route);
} else {
r = set_ensure_put(&manager->routes, &route_hash_ops, route);
if (r < 0)
return r;
set_remove(manager->routes_foreign, route);
}
} else if (r == 1) { } else if (r == 1) {
/* Route exists, do nothing */ /* Route exists, do nothing */
; ;
@ -981,8 +962,8 @@ static int route_add_and_setup_timer(Link *link, const Route *route, const Multi
(void) manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh); (void) manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh);
if (route_type_is_reject(route)) if (route_type_is_reject(route) || (nh && nh->blackhole))
k = route_add(link->manager, NULL, route, NULL, NULL, &nr); k = route_add(link->manager, NULL, route, NULL, nh, &nr);
else if (!m || m->ifindex == 0 || m->ifindex == link->ifindex) else if (!m || m->ifindex == 0 || m->ifindex == link->ifindex)
k = route_add(NULL, link, route, m, nh, &nr); k = route_add(NULL, link, route, m, nh, &nr);
else { else {

View File

@ -532,7 +532,7 @@ int dissect_image(
if (!S_ISBLK(st.st_mode)) if (!S_ISBLK(st.st_mode))
return -ENOTBLK; return -ENOTBLK;
r = sd_device_new_from_devnum(&d, 'b', st.st_rdev); r = sd_device_new_from_stat_rdev(&d, &st);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -136,7 +136,7 @@ static int device_new_from_dev_path(const char *devlink, sd_device **ret_device)
return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK),
"%s does not point to a block device: %m", devlink); "%s does not point to a block device: %m", devlink);
r = sd_device_new_from_devnum(ret_device, 'b', st.st_rdev); r = sd_device_new_from_stat_rdev(ret_device, &st);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to initialize device from %s: %m", devlink); return log_error_errno(r, "Failed to initialize device from %s: %m", devlink);

View File

@ -353,6 +353,7 @@ Id=
Gateway= Gateway=
Family= Family=
OnLink= OnLink=
Blackhole=
[QDisc] [QDisc]
Parent= Parent=
Handle= Handle=

View File

@ -28,9 +28,27 @@ Id=5
Gateway=192.168.10.1 Gateway=192.168.10.1
OnLink=yes OnLink=yes
[NextHop]
Id=6
Family=ipv4
Blackhole=yes
[NextHop]
Id=7
Family=ipv6
Blackhole=yes
[NextHop] [NextHop]
Gateway=192.168.5.2 Gateway=192.168.5.2
[NextHop]
Family=ipv4
Blackhole=yes
[NextHop]
Family=ipv6
Blackhole=yes
[Route] [Route]
NextHop=1 NextHop=1
Destination=10.10.10.10 Destination=10.10.10.10
@ -46,3 +64,11 @@ Destination=2001:1234:5:8f62::1
[Route] [Route]
NextHop=5 NextHop=5
Destination=10.10.10.12 Destination=10.10.10.12
[Route]
NextHop=6
Destination=10.10.10.13
[Route]
NextHop=7
Destination=2001:1234:5:8f62::2

View File

@ -3,7 +3,6 @@ Name=wg99
Kind=wireguard Kind=wireguard
[WireGuard] [WireGuard]
PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
ListenPort=51820 ListenPort=51820
FwMark=1234 FwMark=1234

View File

@ -0,0 +1,5 @@
[WireGuardPeer]
PublicKey=TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=
PresharedKey=it7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=
AllowedIPs=192.168.124.2

View File

@ -0,0 +1,5 @@
[WireGuardPeer]
PublicKey=9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=
PresharedKey=6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=
AllowedIPs=192.168.124.3

View File

@ -0,0 +1,2 @@
[WireGuard]
PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=

View File

@ -1,2 +1,5 @@
[Match] [Match]
Name=wg99 Name=wg99
[Network]
Address=192.168.124.1/24

View File

@ -404,6 +404,13 @@ def remove_routes(routes):
for route_type, addr in routes: for route_type, addr in routes:
call('ip route del', route_type, addr, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) call('ip route del', route_type, addr, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
def remove_blackhole_nexthops():
ret = run('ip nexthop show dev lo', stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
if ret.returncode == 0:
for line in ret.stdout.rstrip().splitlines():
id = line.split()[1]
call(f'ip nexthop del id {id}')
def remove_l2tp_tunnels(tunnel_ids): def remove_l2tp_tunnels(tunnel_ids):
output = check_output('ip l2tp show tunnel') output = check_output('ip l2tp show tunnel')
for tid in tunnel_ids: for tid in tunnel_ids:
@ -1218,7 +1225,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt', '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
'25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network') '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
start_networkd() start_networkd()
self.wait_online(['wg99:carrier', 'wg98:routable', 'wg97:carrier']) self.wait_online(['wg99:routable', 'wg98:routable', 'wg97:carrier'])
output = check_output('ip -4 address show dev wg99')
print(output)
self.assertIn('inet 192.168.124.1/24 scope global wg99', output)
output = check_output('ip -4 address show dev wg98') output = check_output('ip -4 address show dev wg98')
print(output) print(output)
@ -1232,29 +1243,39 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
call('wg') call('wg')
output = check_output('wg show wg99 listen-port') output = check_output('wg show wg99 listen-port')
self.assertRegex(output, '51820') self.assertEqual(output, '51820')
output = check_output('wg show wg99 fwmark') output = check_output('wg show wg99 fwmark')
self.assertRegex(output, '0x4d2') self.assertEqual(output, '0x4d2')
output = check_output('wg show wg99 allowed-ips')
self.assertRegex(output, r'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48')
self.assertRegex(output, r'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128')
output = check_output('wg show wg99 persistent-keepalive')
self.assertRegex(output, r'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t20')
output = check_output('wg show wg99 endpoints')
self.assertRegex(output, r'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t192.168.27.3:51820')
output = check_output('wg show wg99 private-key') output = check_output('wg show wg99 private-key')
self.assertRegex(output, r'EEGlnEPYJV//kbvvIqxKkQwOiS\+UENyPncC4bF46ong=') self.assertEqual(output, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
output = check_output('wg show wg99 allowed-ips')
self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output)
self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output)
self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output)
self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output)
output = check_output('wg show wg99 persistent-keepalive')
self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output)
self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output)
self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output)
self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output)
output = check_output('wg show wg99 endpoints')
self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output)
self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output)
self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output)
self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output)
output = check_output('wg show wg99 preshared-keys') output = check_output('wg show wg99 preshared-keys')
self.assertRegex(output, r'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA= IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=') self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output)
self.assertRegex(output, r'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= cPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=') self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output)
self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output)
self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output)
output = check_output('wg show wg98 private-key') output = check_output('wg show wg98 private-key')
self.assertRegex(output, r'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr\+WHtZLZ90FU=') self.assertEqual(output, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
output = check_output('wg show wg97 listen-port') output = check_output('wg show wg97 listen-port')
self.assertRegex(output, '51821') self.assertEqual(output, '51821')
output = check_output('wg show wg97 fwmark') output = check_output('wg show wg97 fwmark')
self.assertRegex(output, '0x4d3') self.assertEqual(output, '0x4d3')
def test_geneve(self): def test_geneve(self):
copy_unit_to_networkd_unit_path('25-geneve.netdev', 'netdev-link-local-addressing-yes.network') copy_unit_to_networkd_unit_path('25-geneve.netdev', 'netdev-link-local-addressing-yes.network')
@ -1817,12 +1838,14 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
routes = [['blackhole', '202.54.1.2'], ['unreachable', '202.54.1.3'], ['prohibit', '202.54.1.4']] routes = [['blackhole', '202.54.1.2'], ['unreachable', '202.54.1.3'], ['prohibit', '202.54.1.4']]
def setUp(self): def setUp(self):
remove_blackhole_nexthops()
remove_routing_policy_rule_tables(self.routing_policy_rule_tables) remove_routing_policy_rule_tables(self.routing_policy_rule_tables)
remove_routes(self.routes) remove_routes(self.routes)
remove_links(self.links) remove_links(self.links)
stop_networkd(show_logs=False) stop_networkd(show_logs=False)
def tearDown(self): def tearDown(self):
remove_blackhole_nexthops()
remove_routing_policy_rule_tables(self.routing_policy_rule_tables) remove_routing_policy_rule_tables(self.routing_policy_rule_tables)
remove_routes(self.routes) remove_routes(self.routes)
remove_links(self.links) remove_links(self.links)
@ -2815,6 +2838,12 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink') self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99') self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99')
# kernel manages blackhole nexthops on lo
output = check_output('ip nexthop list dev lo')
print(output)
self.assertIn('id 6 blackhole', output)
self.assertIn('id 7 blackhole', output)
output = check_output('ip route show dev veth99 10.10.10.10') output = check_output('ip route show dev veth99 10.10.10.10')
print(output) print(output)
self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output) self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output)
@ -2831,6 +2860,14 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
print(output) print(output)
self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output) self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
output = check_output('ip route show 10.10.10.13')
print(output)
self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output)
output = check_output('ip -6 route show 2001:1234:5:8f62::2')
print(output)
self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output)
def test_qdisc(self): def test_qdisc(self):
copy_unit_to_networkd_unit_path('25-qdisc-clsact-and-htb.network', '12-dummy.netdev', copy_unit_to_networkd_unit_path('25-qdisc-clsact-and-htb.network', '12-dummy.netdev',
'25-qdisc-ingress-netem-compat.network', '11-dummy.netdev') '25-qdisc-ingress-netem-compat.network', '11-dummy.netdev')