Compare commits

..

19 Commits

Author SHA1 Message Date
Yu Watanabe 7d94934b9d
Merge pull request #13760 from keszybz/unit-loading-unification
Unit loading unification
2019-10-14 23:35:54 +09:00
Yu Watanabe 3adf85b72f
Merge pull request #13735 from ssahani/ip-nexthop
network: introduce ip nexthop routing
2019-10-14 23:25:23 +09:00
Jan Synacek 4801d8afe2 udev: introduce CONST key name
Currently, there is no way to match against system-wide constants, such
as architecture or virtualization type, without forking helper binaries.
That potentially results in a huge number of spawned processes which
output always the same answer.

This patch introduces a special CONST keyword which takes a hard-coded
string as its key and returns a value assigned to that key. Currently
implemented are CONST{arch} and CONST{virt}, which can be used to match
against the system's architecture and virtualization type.
2019-10-14 22:01:11 +09:00
Yu Watanabe 086bcf5dca test-network: add a test case for nexthop 2019-10-14 21:55:57 +09:00
Yu Watanabe 4abcdaa08c network: update comment as DHCP is deprecated now 2019-10-14 21:33:22 +09:00
Susant Sahani c16c780804 network: introduce ip nexthop routing
Used to manipulate entries in the kernel's nexthop tables.
Example:
```
[NextHop]
Id=3
Gateway=192.168.5.1
```
2019-10-14 21:32:48 +09:00
Yu Watanabe 55d3fdcf5e network: ndisc: do not drop all prefixes when a prefix matches a blacklist
Fixes #13767.
2019-10-14 20:54:43 +09:00
Yu Watanabe c94f7809e3
Merge pull request #13761 from dtardon/program-name
udev: do not hardcode program name
2019-10-14 20:17:28 +09:00
Yu Watanabe 0486a7d15b
Merge pull request #13765 from yuwata/udev-memleak-13764
udev,systemctl: fix memleak caused by wrong cleanup function
2019-10-14 20:16:58 +09:00
Yu Watanabe 13f697b7b1 systemctl: fix memleak caused by wrong cleanup func 2019-10-14 01:18:41 +09:00
Yu Watanabe f4f6f2c740 udev: fix memleak caused by wrong cleanup function
Fixes #13764.
2019-10-13 00:35:53 +09:00
David Tardon 9e385314b0 udev: do not hardcode program name 2019-10-11 14:45:02 +02:00
David Tardon 9b2aee4170 udev/fido_id: fix program name in usage output 2019-10-11 14:44:53 +02:00
Zbigniew Jędrzejewski-Szmek 2cea199ec1 core: pass around pointer, not struct
Since this is a static function, the compiler is likely to optimize it away
anyway, but let's do the normal thing here.
2019-10-11 13:46:05 +02:00
Zbigniew Jędrzejewski-Szmek 27e946a5b3 shared/bus-unit-util: remove some empty lines
We generally don't add empty lines after if. It looks especially strange
when the block has is just a single line without braces.
2019-10-11 13:46:05 +02:00
Zbigniew Jędrzejewski-Szmek 75193d4128 core: adjust load functions for other unit types to be more like service
No functional change, just adjusting code to follow the same pattern
everywhere. In particular, never call _verify() on an already loaded unit,
but return early from the caller instead. This makes the code a bit easier
to follow.
2019-10-11 13:46:05 +02:00
Zbigniew Jędrzejewski-Szmek c3784a7d78 core: simplify unit_load() a bit
Now all unit types define .load. But even if it wasn't defined, we'd need
to call unit_load_fragment_and_dropin() anyway, so this code would not have
worked correctly.

Also, unit_load_fragment_and_dropin() either returns -ENOENT or changes
UNIT_STUB to UNIT_LOADED, so we don't need to repeat this here.
2019-10-11 11:25:04 +02:00
Zbigniew Jędrzejewski-Szmek e0cfed4c59 core/service: use common implementation of unit_load_fragment_and_dropin()
There is a slight functional change when load_state == UNIT_MERGED. Before,
we would not call unit_load_dropin(), but now we do. I'm not sure if this
causes an actual difference in behaviour, but since all other unit types do
this, I think it's better to do the same thing here too.
2019-10-11 11:25:04 +02:00
Zbigniew Jędrzejewski-Szmek c362077087 core: turn unit_load_fragment_and_dropin_optional() into a flag
unit_load_fragment_and_dropin() and unit_load_fragment_and_dropin_optional()
are really the same, with one minor difference in behaviour. Let's drop
the second function.

"_optional" in the name suggests that it's the "dropin" part that is optional.
(Which it is, but in this case, we mean the fragment to be optional.)
I think the new version with a flag is easier to understand.
2019-10-11 10:45:33 +02:00
46 changed files with 1273 additions and 330 deletions

View File

@ -1152,6 +1152,29 @@
</variablelist> </variablelist>
</refsect1> </refsect1>
<refsect1>
<title>[NextHop] Section Options</title>
<para>The <literal>[NextHop]</literal> section accepts the
following keys. Specify several <literal>[NextHop]</literal>
sections to configure several nexthop. Nexthop is used to manipulate entries in the kernel's nexthop
tables.</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>Gateway=</varname></term>
<listitem>
<para>As in the <literal>[Network]</literal> section. This is mandatory.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Id=</varname></term>
<listitem>
<para>The id of the nexthop (an unsigned integer). If unspecified or '0' then automatically chosen by kernel.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1> <refsect1>
<title>[Route] Section Options</title> <title>[Route] Section Options</title>
<para>The <literal>[Route]</literal> section accepts the <para>The <literal>[Route]</literal> section accepts the

View File

@ -236,6 +236,32 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>CONST{<replaceable>key</replaceable>}</varname></term>
<listitem>
<para>Match against a system-wide constant. Supported keys are:</para>
<variablelist>
<varlistentry>
<term><literal>arch</literal></term>
<listitem>
<para>System's architecture. See <option>ConditionArchitecture=</option> in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for possible values.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>virt</literal></term>
<listitem>
<para>System's virtualization environment. See
<citerefentry><refentrytitle>systemd-detect-virt</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for possible values.</para>
</listitem>
</varlistentry>
</variablelist>
<para>Unknown keys will never match.</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>TAG</varname></term> <term><varname>TAG</varname></term>
<listitem> <listitem>

56
src/basic/linux/nexthop.h Normal file
View File

@ -0,0 +1,56 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _LINUX_NEXTHOP_H
#define _LINUX_NEXTHOP_H
#include <linux/types.h>
struct nhmsg {
unsigned char nh_family;
unsigned char nh_scope; /* return only */
unsigned char nh_protocol; /* Routing protocol that installed nh */
unsigned char resvd;
unsigned int nh_flags; /* RTNH_F flags */
};
/* entry in a nexthop group */
struct nexthop_grp {
__u32 id; /* nexthop id - must exist */
__u8 weight; /* weight of this nexthop */
__u8 resvd1;
__u16 resvd2;
};
enum {
NEXTHOP_GRP_TYPE_MPATH, /* default type if not specified */
__NEXTHOP_GRP_TYPE_MAX,
};
#define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1)
enum {
NHA_UNSPEC,
NHA_ID, /* u32; id for nexthop. id == 0 means auto-assign */
NHA_GROUP, /* array of nexthop_grp */
NHA_GROUP_TYPE, /* u16 one of NEXTHOP_GRP_TYPE */
/* if NHA_GROUP attribute is added, no other attributes can be set */
NHA_BLACKHOLE, /* flag; nexthop used to blackhole packets */
/* if NHA_BLACKHOLE is added, OIF, GATEWAY, ENCAP can not be set */
NHA_OIF, /* u32; nexthop device */
NHA_GATEWAY, /* be32 (IPv4) or in6_addr (IPv6) gw address */
NHA_ENCAP_TYPE, /* u16; lwt encap type */
NHA_ENCAP, /* lwt encap data */
/* NHA_OIF can be appended to dump request to return only
* nexthops using given device
*/
NHA_GROUPS, /* flag; only return nexthop groups in dump */
NHA_MASTER, /* u32; only return nexthops with given master dev */
__NHA_MAX,
};
#define NHA_MAX (__NHA_MAX - 1)
#endif

View File

@ -157,6 +157,13 @@ enum {
RTM_GETCHAIN, RTM_GETCHAIN,
#define RTM_GETCHAIN RTM_GETCHAIN #define RTM_GETCHAIN RTM_GETCHAIN
RTM_NEWNEXTHOP = 104,
#define RTM_NEWNEXTHOP RTM_NEWNEXTHOP
RTM_DELNEXTHOP,
#define RTM_DELNEXTHOP RTM_DELNEXTHOP
RTM_GETNEXTHOP,
#define RTM_GETNEXTHOP RTM_GETNEXTHOP
__RTM_MAX, __RTM_MAX,
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
}; };
@ -342,6 +349,7 @@ enum rtattr_type_t {
RTA_IP_PROTO, RTA_IP_PROTO,
RTA_SPORT, RTA_SPORT,
RTA_DPORT, RTA_DPORT,
RTA_NH_ID,
__RTA_MAX __RTA_MAX
}; };
@ -704,6 +712,8 @@ enum rtnetlink_groups {
#define RTNLGRP_IPV4_MROUTE_R RTNLGRP_IPV4_MROUTE_R #define RTNLGRP_IPV4_MROUTE_R RTNLGRP_IPV4_MROUTE_R
RTNLGRP_IPV6_MROUTE_R, RTNLGRP_IPV6_MROUTE_R,
#define RTNLGRP_IPV6_MROUTE_R RTNLGRP_IPV6_MROUTE_R #define RTNLGRP_IPV6_MROUTE_R RTNLGRP_IPV6_MROUTE_R
RTNLGRP_NEXTHOP,
#define RTNLGRP_NEXTHOP RTNLGRP_NEXTHOP
__RTNLGRP_MAX __RTNLGRP_MAX
}; };
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1) #define RTNLGRP_MAX (__RTNLGRP_MAX - 1)

View File

@ -164,9 +164,7 @@ static int automount_verify(Automount *a) {
int r; int r;
assert(a); assert(a);
assert(UNIT(a)->load_state == UNIT_LOADED);
if (UNIT(a)->load_state != UNIT_LOADED)
return 0;
if (path_equal(a->where, "/")) { if (path_equal(a->where, "/")) {
log_unit_error(UNIT(a), "Cannot have an automount unit for the root directory. Refusing."); log_unit_error(UNIT(a), "Cannot have an automount unit for the root directory. Refusing.");
@ -201,19 +199,9 @@ static int automount_set_where(Automount *a) {
return 1; return 1;
} }
static int automount_load(Unit *u) { static int automount_add_extras(Automount *a) {
Automount *a = AUTOMOUNT(u);
int r; int r;
assert(u);
assert(u->load_state == UNIT_STUB);
/* Load a .automount file */
r = unit_load_fragment_and_dropin(u);
if (r < 0)
return r;
if (u->load_state == UNIT_LOADED) {
r = automount_set_where(a); r = automount_set_where(a);
if (r < 0) if (r < 0)
return r; return r;
@ -226,10 +214,27 @@ static int automount_load(Unit *u) {
if (r < 0) if (r < 0)
return r; return r;
r = automount_add_default_dependencies(a); return automount_add_default_dependencies(a);
}
static int automount_load(Unit *u) {
Automount *a = AUTOMOUNT(u);
int r;
assert(u);
assert(u->load_state == UNIT_STUB);
/* Load a .automount file */
r = unit_load_fragment_and_dropin(u, true);
if (r < 0)
return r;
if (u->load_state != UNIT_LOADED)
return 0;
r = automount_add_extras(a);
if (r < 0) if (r < 0)
return r; return r;
}
return automount_verify(a); return automount_verify(a);
} }

View File

@ -970,10 +970,10 @@ static uint64_t cgroup_cpu_weight_to_shares(uint64_t weight) {
CGROUP_CPU_SHARES_MIN, CGROUP_CPU_SHARES_MAX); CGROUP_CPU_SHARES_MIN, CGROUP_CPU_SHARES_MAX);
} }
static void cgroup_apply_unified_cpuset(Unit *u, CPUSet cpus, const char *name) { static void cgroup_apply_unified_cpuset(Unit *u, const CPUSet *cpus, const char *name) {
_cleanup_free_ char *buf = NULL; _cleanup_free_ char *buf = NULL;
buf = cpu_set_to_range_string(&cpus); buf = cpu_set_to_range_string(cpus);
if (!buf) if (!buf)
return; return;
@ -1221,8 +1221,8 @@ static void cgroup_context_apply(
} }
if ((apply_mask & CGROUP_MASK_CPUSET) && !is_local_root) { if ((apply_mask & CGROUP_MASK_CPUSET) && !is_local_root) {
cgroup_apply_unified_cpuset(u, c->cpuset_cpus, "cpuset.cpus"); cgroup_apply_unified_cpuset(u, &c->cpuset_cpus, "cpuset.cpus");
cgroup_apply_unified_cpuset(u, c->cpuset_mems, "cpuset.mems"); cgroup_apply_unified_cpuset(u, &c->cpuset_mems, "cpuset.mems");
} }
/* The 'io' controller attributes are not exported on the host's root cgroup (being a pure cgroup v2 /* The 'io' controller attributes are not exported on the host's root cgroup (being a pure cgroup v2

View File

@ -116,7 +116,7 @@ static void device_done(Unit *u) {
static int device_load(Unit *u) { static int device_load(Unit *u) {
int r; int r;
r = unit_load_fragment_and_dropin_optional(u); r = unit_load_fragment_and_dropin(u, false);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -513,9 +513,7 @@ static int mount_verify(Mount *m) {
int r; int r;
assert(m); assert(m);
assert(UNIT(m)->load_state == UNIT_LOADED);
if (UNIT(m)->load_state != UNIT_LOADED)
return 0;
if (!m->from_fragment && !m->from_proc_self_mountinfo && !UNIT(m)->perpetual) if (!m->from_fragment && !m->from_proc_self_mountinfo && !UNIT(m)->perpetual)
return -ENOENT; return -ENOENT;
@ -606,11 +604,11 @@ static int mount_add_extras(Mount *m) {
return 0; return 0;
} }
static int mount_load_root_mount(Unit *u) { static void mount_load_root_mount(Unit *u) {
assert(u); assert(u);
if (!unit_has_name(u, SPECIAL_ROOT_MOUNT)) if (!unit_has_name(u, SPECIAL_ROOT_MOUNT))
return 0; return;
u->perpetual = true; u->perpetual = true;
u->default_dependencies = false; u->default_dependencies = false;
@ -621,39 +619,33 @@ static int mount_load_root_mount(Unit *u) {
if (!u->description) if (!u->description)
u->description = strdup("Root Mount"); u->description = strdup("Root Mount");
return 1;
} }
static int mount_load(Unit *u) { static int mount_load(Unit *u) {
Mount *m = MOUNT(u); Mount *m = MOUNT(u);
int r, q, w; int r, q = 0;
assert(u); assert(u);
assert(u->load_state == UNIT_STUB); assert(u->load_state == UNIT_STUB);
r = mount_load_root_mount(u); mount_load_root_mount(u);
if (m->from_proc_self_mountinfo || u->perpetual) bool fragment_optional = m->from_proc_self_mountinfo || u->perpetual;
q = unit_load_fragment_and_dropin_optional(u); r = unit_load_fragment_and_dropin(u, !fragment_optional);
else
q = unit_load_fragment_and_dropin(u);
/* Add in some extras. Note we do this in all cases (even if we failed to load the unit) when announced by the /* Add in some extras. Note we do this in all cases (even if we failed to load the unit) when announced by the
* kernel, because we need some things to be set up no matter what when the kernel establishes a mount and thus * kernel, because we need some things to be set up no matter what when the kernel establishes a mount and thus
* we need to update the state in our unit to track it. After all, consider that we don't allow changing the * we need to update the state in our unit to track it. After all, consider that we don't allow changing the
* 'slice' field for a unit once it is active. */ * 'slice' field for a unit once it is active. */
if (u->load_state == UNIT_LOADED || m->from_proc_self_mountinfo || u->perpetual) if (u->load_state == UNIT_LOADED || m->from_proc_self_mountinfo || u->perpetual)
w = mount_add_extras(m); q = mount_add_extras(m);
else
w = 0;
if (r < 0) if (r < 0)
return r; return r;
if (q < 0) if (q < 0)
return q; return q;
if (w < 0) if (u->load_state != UNIT_LOADED)
return w; return 0;
return mount_verify(m); return mount_verify(m);
} }

View File

@ -284,9 +284,7 @@ static int path_add_mount_dependencies(Path *p) {
static int path_verify(Path *p) { static int path_verify(Path *p) {
assert(p); assert(p);
assert(UNIT(p)->load_state == UNIT_LOADED);
if (UNIT(p)->load_state != UNIT_LOADED)
return 0;
if (!p->specs) { if (!p->specs) {
log_unit_error(UNIT(p), "Path unit lacks path setting. Refusing."); log_unit_error(UNIT(p), "Path unit lacks path setting. Refusing.");
@ -333,19 +331,9 @@ static int path_add_trigger_dependencies(Path *p) {
return unit_add_two_dependencies(UNIT(p), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT); return unit_add_two_dependencies(UNIT(p), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT);
} }
static int path_load(Unit *u) { static int path_add_extras(Path *p) {
Path *p = PATH(u);
int r; int r;
assert(u);
assert(u->load_state == UNIT_STUB);
r = unit_load_fragment_and_dropin(u);
if (r < 0)
return r;
if (u->load_state == UNIT_LOADED) {
r = path_add_trigger_dependencies(p); r = path_add_trigger_dependencies(p);
if (r < 0) if (r < 0)
return r; return r;
@ -354,10 +342,26 @@ static int path_load(Unit *u) {
if (r < 0) if (r < 0)
return r; return r;
r = path_add_default_dependencies(p); return path_add_default_dependencies(p);
}
static int path_load(Unit *u) {
Path *p = PATH(u);
int r;
assert(u);
assert(u->load_state == UNIT_STUB);
r = unit_load_fragment_and_dropin(u, true);
if (r < 0)
return r;
if (u->load_state != UNIT_LOADED)
return 0;
r = path_add_extras(p);
if (r < 0) if (r < 0)
return r; return r;
}
return path_verify(p); return path_verify(p);
} }

View File

@ -125,9 +125,7 @@ static int scope_add_default_dependencies(Scope *s) {
static int scope_verify(Scope *s) { static int scope_verify(Scope *s) {
assert(s); assert(s);
assert(UNIT(s)->load_state == UNIT_LOADED);
if (UNIT(s)->load_state != UNIT_LOADED)
return 0;
if (set_isempty(UNIT(s)->pids) && if (set_isempty(UNIT(s)->pids) &&
!MANAGER_IS_RELOADING(UNIT(s)->manager) && !MANAGER_IS_RELOADING(UNIT(s)->manager) &&
@ -162,6 +160,20 @@ static int scope_load_init_scope(Unit *u) {
return 1; return 1;
} }
static int scope_add_extras(Scope *s) {
int r;
r = unit_patch_contexts(UNIT(s));
if (r < 0)
return r;
r = unit_set_default_slice(UNIT(s));
if (r < 0)
return r;
return scope_add_default_dependencies(s);
}
static int scope_load(Unit *u) { static int scope_load(Unit *u) {
Scope *s = SCOPE(u); Scope *s = SCOPE(u);
int r; int r;
@ -176,24 +188,18 @@ static int scope_load(Unit *u) {
r = scope_load_init_scope(u); r = scope_load_init_scope(u);
if (r < 0) if (r < 0)
return r; return r;
r = unit_load_fragment_and_dropin_optional(u);
r = unit_load_fragment_and_dropin(u, false);
if (r < 0) if (r < 0)
return r; return r;
if (u->load_state == UNIT_LOADED) { if (u->load_state != UNIT_LOADED)
r = unit_patch_contexts(u); return 0;
if (r < 0)
return r;
r = unit_set_default_slice(u); r = scope_add_extras(s);
if (r < 0) if (r < 0)
return r; return r;
r = scope_add_default_dependencies(s);
if (r < 0)
return r;
}
return scope_verify(s); return scope_verify(s);
} }

View File

@ -548,9 +548,7 @@ static int service_arm_timer(Service *s, usec_t usec) {
static int service_verify(Service *s) { static int service_verify(Service *s) {
assert(s); assert(s);
assert(UNIT(s)->load_state == UNIT_LOADED);
if (UNIT(s)->load_state != UNIT_LOADED)
return 0;
if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP] if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]
&& UNIT(s)->success_action == EMERGENCY_ACTION_NONE) { && UNIT(s)->success_action == EMERGENCY_ACTION_NONE) {
@ -760,32 +758,17 @@ static int service_load(Unit *u) {
Service *s = SERVICE(u); Service *s = SERVICE(u);
int r; int r;
assert(s); r = unit_load_fragment_and_dropin(u, true);
/* Load a .service file */
r = unit_load_fragment(u);
if (r < 0) if (r < 0)
return r; return r;
/* Still nothing found? Then let's give up */ if (u->load_state != UNIT_LOADED)
if (u->load_state == UNIT_STUB) return 0;
return -ENOENT;
/* This is a new unit? Then let's add in some extras */ /* This is a new unit? Then let's add in some extras */
if (u->load_state == UNIT_LOADED) {
/* We were able to load something, then let's add in
* the dropin directories. */
r = unit_load_dropin(u);
if (r < 0)
return r;
/* This is a new unit? Then let's add in some
* extras */
r = service_add_extras(s); r = service_add_extras(s);
if (r < 0) if (r < 0)
return r; return r;
}
return service_verify(s); return service_verify(s);
} }

View File

@ -91,9 +91,7 @@ static int slice_verify(Slice *s) {
int r; int r;
assert(s); assert(s);
assert(UNIT(s)->load_state == UNIT_LOADED);
if (UNIT(s)->load_state != UNIT_LOADED)
return 0;
if (!slice_name_is_valid(UNIT(s)->id)) { if (!slice_name_is_valid(UNIT(s)->id)) {
log_unit_error(UNIT(s), "Slice name %s is not valid. Refusing.", UNIT(s)->id); log_unit_error(UNIT(s), "Slice name %s is not valid. Refusing.", UNIT(s)->id);
@ -170,13 +168,14 @@ static int slice_load(Unit *u) {
if (r < 0) if (r < 0)
return r; return r;
r = unit_load_fragment_and_dropin_optional(u); r = unit_load_fragment_and_dropin(u, false);
if (r < 0) if (r < 0)
return r; return r;
/* This is a new unit? Then let's add in some extras */ if (u->load_state != UNIT_LOADED)
if (u->load_state == UNIT_LOADED) { return 0;
/* This is a new unit? Then let's add in some extras */
r = unit_patch_contexts(u); r = unit_patch_contexts(u);
if (r < 0) if (r < 0)
return r; return r;
@ -188,7 +187,6 @@ static int slice_load(Unit *u) {
r = slice_add_default_dependencies(s); r = slice_add_default_dependencies(s);
if (r < 0) if (r < 0)
return r; return r;
}
return slice_verify(s); return slice_verify(s);
} }

View File

@ -433,9 +433,7 @@ static const char *socket_find_symlink_target(Socket *s) {
static int socket_verify(Socket *s) { static int socket_verify(Socket *s) {
assert(s); assert(s);
assert(UNIT(s)->load_state == UNIT_LOADED);
if (UNIT(s)->load_state != UNIT_LOADED)
return 0;
if (!s->ports) { if (!s->ports) {
log_unit_error(UNIT(s), "Unit has no Listen setting (ListenStream=, ListenDatagram=, ListenFIFO=, ...). Refusing."); log_unit_error(UNIT(s), "Unit has no Listen setting (ListenStream=, ListenDatagram=, ListenFIFO=, ...). Refusing.");
@ -514,16 +512,17 @@ static int socket_load(Unit *u) {
if (r < 0) if (r < 0)
return r; return r;
r = unit_load_fragment_and_dropin(u); r = unit_load_fragment_and_dropin(u, true);
if (r < 0) if (r < 0)
return r; return r;
if (u->load_state == UNIT_LOADED) { if (u->load_state != UNIT_LOADED)
return 0;
/* This is a new unit? Then let's add in some extras */ /* This is a new unit? Then let's add in some extras */
r = socket_add_extras(s); r = socket_add_extras(s);
if (r < 0) if (r < 0)
return r; return r;
}
return socket_verify(s); return socket_verify(s);
} }

View File

@ -232,8 +232,7 @@ static int swap_verify(Swap *s) {
_cleanup_free_ char *e = NULL; _cleanup_free_ char *e = NULL;
int r; int r;
if (UNIT(s)->load_state != UNIT_LOADED) assert(UNIT(s)->load_state == UNIT_LOADED);
return 0;
r = unit_name_from_path(s->what, ".swap", &e); r = unit_name_from_path(s->what, ".swap", &e);
if (r < 0) if (r < 0)
@ -340,28 +339,26 @@ static int swap_add_extras(Swap *s) {
static int swap_load(Unit *u) { static int swap_load(Unit *u) {
Swap *s = SWAP(u); Swap *s = SWAP(u);
int r, q; int r, q = 0;
assert(s); assert(s);
assert(u->load_state == UNIT_STUB); assert(u->load_state == UNIT_STUB);
/* Load a .swap file */ /* Load a .swap file */
if (SWAP(u)->from_proc_swaps) bool fragment_optional = s->from_proc_swaps;
r = unit_load_fragment_and_dropin_optional(u); r = unit_load_fragment_and_dropin(u, !fragment_optional);
else
r = unit_load_fragment_and_dropin(u);
/* Add in some extras, and do so either when we successfully loaded something or when /proc/swaps is already /* Add in some extras, and do so either when we successfully loaded something or when /proc/swaps is
* active. */ * already active. */
if (u->load_state == UNIT_LOADED || s->from_proc_swaps) if (u->load_state == UNIT_LOADED || s->from_proc_swaps)
q = swap_add_extras(s); q = swap_add_extras(s);
else
q = 0;
if (r < 0) if (r < 0)
return r; return r;
if (q < 0) if (q < 0)
return q; return q;
if (u->load_state != UNIT_LOADED)
return 0;
return swap_verify(s); return swap_verify(s);
} }

View File

@ -80,18 +80,15 @@ static int target_load(Unit *u) {
assert(t); assert(t);
r = unit_load_fragment_and_dropin(u); r = unit_load_fragment_and_dropin(u, true);
if (r < 0) if (r < 0)
return r; return r;
if (u->load_state != UNIT_LOADED)
return 0;
/* This is a new unit? Then let's add in some extras */ /* This is a new unit? Then let's add in some extras */
if (u->load_state == UNIT_LOADED) { return target_add_default_dependencies(t);
r = target_add_default_dependencies(t);
if (r < 0)
return r;
}
return 0;
} }
static int target_coldplug(Unit *u) { static int target_coldplug(Unit *u) {

View File

@ -73,9 +73,7 @@ static void timer_done(Unit *u) {
static int timer_verify(Timer *t) { static int timer_verify(Timer *t) {
assert(t); assert(t);
assert(UNIT(t)->load_state == UNIT_LOADED);
if (UNIT(t)->load_state != UNIT_LOADED)
return 0;
if (!t->values && !t->on_clock_change && !t->on_timezone_change) { if (!t->values && !t->on_clock_change && !t->on_timezone_change) {
log_unit_error(UNIT(t), "Timer unit lacks value setting. Refusing."); log_unit_error(UNIT(t), "Timer unit lacks value setting. Refusing.");
@ -178,12 +176,14 @@ static int timer_load(Unit *u) {
assert(u); assert(u);
assert(u->load_state == UNIT_STUB); assert(u->load_state == UNIT_STUB);
r = unit_load_fragment_and_dropin(u); r = unit_load_fragment_and_dropin(u, true);
if (r < 0) if (r < 0)
return r; return r;
if (u->load_state == UNIT_LOADED) { if (u->load_state != UNIT_LOADED)
return 0;
/* This is a new unit? Then let's add in some extras */
r = timer_add_trigger_dependencies(t); r = timer_add_trigger_dependencies(t);
if (r < 0) if (r < 0)
return r; return r;
@ -195,7 +195,6 @@ static int timer_load(Unit *u) {
r = timer_add_default_dependencies(t); r = timer_add_default_dependencies(t);
if (r < 0) if (r < 0)
return r; return r;
}
return timer_verify(t); return timer_verify(t);
} }

View File

@ -1361,7 +1361,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
} }
/* Common implementation for multiple backends */ /* Common implementation for multiple backends */
int unit_load_fragment_and_dropin(Unit *u) { int unit_load_fragment_and_dropin(Unit *u, bool fragment_required) {
int r; int r;
assert(u); assert(u);
@ -1371,9 +1371,13 @@ int unit_load_fragment_and_dropin(Unit *u) {
if (r < 0) if (r < 0)
return r; return r;
if (u->load_state == UNIT_STUB) if (u->load_state == UNIT_STUB) {
if (fragment_required)
return -ENOENT; return -ENOENT;
u->load_state = UNIT_LOADED;
}
/* Load drop-in directory data. If u is an alias, we might be reloading the /* Load drop-in directory data. If u is an alias, we might be reloading the
* target unit needlessly. But we cannot be sure which drops-ins have already * target unit needlessly. But we cannot be sure which drops-ins have already
* been loaded and which not, at least without doing complicated book-keeping, * been loaded and which not, at least without doing complicated book-keeping,
@ -1381,27 +1385,6 @@ int unit_load_fragment_and_dropin(Unit *u) {
return unit_load_dropin(unit_follow_merge(u)); return unit_load_dropin(unit_follow_merge(u));
} }
/* Common implementation for multiple backends */
int unit_load_fragment_and_dropin_optional(Unit *u) {
int r;
assert(u);
/* Same as unit_load_fragment_and_dropin(), but whether
* something can be loaded or not doesn't matter. */
/* Load a .service/.socket/.slice/… file */
r = unit_load_fragment(u);
if (r < 0)
return r;
if (u->load_state == UNIT_STUB)
u->load_state = UNIT_LOADED;
/* Load drop-in directory data */
return unit_load_dropin(unit_follow_merge(u));
}
void unit_add_to_target_deps_queue(Unit *u) { void unit_add_to_target_deps_queue(Unit *u) {
Manager *m = u->manager; Manager *m = u->manager;
@ -1559,16 +1542,11 @@ int unit_load(Unit *u) {
u->fragment_mtime = now(CLOCK_REALTIME); u->fragment_mtime = now(CLOCK_REALTIME);
} }
if (UNIT_VTABLE(u)->load) {
r = UNIT_VTABLE(u)->load(u); r = UNIT_VTABLE(u)->load(u);
if (r < 0) if (r < 0)
goto fail; goto fail;
}
if (u->load_state == UNIT_STUB) { assert(u->load_state != UNIT_STUB);
r = -ENOENT;
goto fail;
}
if (u->load_state == UNIT_LOADED) { if (u->load_state == UNIT_LOADED) {
unit_add_to_target_deps_queue(u); unit_add_to_target_deps_queue(u);

View File

@ -670,8 +670,7 @@ int unit_merge_by_name(Unit *u, const char *other);
Unit *unit_follow_merge(Unit *u) _pure_; Unit *unit_follow_merge(Unit *u) _pure_;
int unit_load_fragment_and_dropin(Unit *u); int unit_load_fragment_and_dropin(Unit *u, bool fragment_required);
int unit_load_fragment_and_dropin_optional(Unit *u);
int unit_load(Unit *unit); int unit_load(Unit *unit);
int unit_set_slice(Unit *u, Unit *slice); int unit_set_slice(Unit *u, Unit *slice);

View File

@ -88,7 +88,8 @@ int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) {
assert_return(m, -EINVAL); assert_return(m, -EINVAL);
assert_return(m->hdr, -EINVAL); assert_return(m->hdr, -EINVAL);
assert_return(IN_SET(m->hdr->nlmsg_type, RTM_GETLINK, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH, RTM_GETRULE, RTM_GETADDRLABEL), -EINVAL); assert_return(IN_SET(m->hdr->nlmsg_type, RTM_GETLINK, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH,
RTM_GETRULE, RTM_GETADDRLABEL, RTM_GETNEXTHOP), -EINVAL);
SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump); SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump);

View File

@ -18,6 +18,7 @@
#include <linux/if_link.h> #include <linux/if_link.h>
#include <linux/if_macsec.h> #include <linux/if_macsec.h>
#include <linux/if_tunnel.h> #include <linux/if_tunnel.h>
#include <linux/nexthop.h>
#include <linux/l2tp.h> #include <linux/l2tp.h>
#include <linux/veth.h> #include <linux/veth.h>
#include <linux/wireguard.h> #include <linux/wireguard.h>
@ -716,6 +717,17 @@ static const NLTypeSystem rtnl_routing_policy_rule_type_system = {
.types = rtnl_routing_policy_rule_types, .types = rtnl_routing_policy_rule_types,
}; };
static const NLType rtnl_nexthop_types[] = {
[NHA_ID] = { .type = NETLINK_TYPE_U32 },
[NHA_OIF] = { .type = NETLINK_TYPE_U32 },
[NHA_GATEWAY] = { .type = NETLINK_TYPE_IN_ADDR },
};
static const NLTypeSystem rtnl_nexthop_type_system = {
.count = ELEMENTSOF(rtnl_nexthop_types),
.types = rtnl_nexthop_types,
};
static const NLType rtnl_types[] = { static const NLType rtnl_types[] = {
[NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 }, [NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 },
[NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) }, [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) },
@ -738,6 +750,9 @@ static const NLType rtnl_types[] = {
[RTM_NEWRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) }, [RTM_NEWRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
[RTM_DELRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) }, [RTM_DELRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
[RTM_GETRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) }, [RTM_GETRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
[RTM_NEWNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
[RTM_DELNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
[RTM_GETNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
}; };
const NLTypeSystem rtnl_type_system_root = { const NLTypeSystem rtnl_type_system_root = {

View File

@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+ */ /* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once #pragma once
#include <linux/rtnetlink.h>
#include "sd-netlink.h" #include "sd-netlink.h"
#include "in-addr-util.h" #include "in-addr-util.h"
@ -19,6 +21,10 @@ static inline bool rtnl_message_type_is_route(uint16_t type) {
return IN_SET(type, RTM_NEWROUTE, RTM_GETROUTE, RTM_DELROUTE); return IN_SET(type, RTM_NEWROUTE, RTM_GETROUTE, RTM_DELROUTE);
} }
static inline bool rtnl_message_type_is_nexthop(uint16_t type) {
return IN_SET(type, RTM_NEWNEXTHOP, RTM_GETNEXTHOP, RTM_DELNEXTHOP);
}
static inline bool rtnl_message_type_is_link(uint16_t type) { static inline bool rtnl_message_type_is_link(uint16_t type) {
return IN_SET(type, RTM_NEWLINK, RTM_SETLINK, RTM_GETLINK, RTM_DELLINK); return IN_SET(type, RTM_NEWLINK, RTM_SETLINK, RTM_GETLINK, RTM_DELLINK);
} }

View File

@ -2,6 +2,7 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <linux/if_addrlabel.h> #include <linux/if_addrlabel.h>
#include <linux/nexthop.h>
#include <stdbool.h> #include <stdbool.h>
#include <unistd.h> #include <unistd.h>
@ -285,6 +286,70 @@ int sd_rtnl_message_new_route(sd_netlink *rtnl, sd_netlink_message **ret,
return 0; return 0;
} }
int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret,
uint16_t nhmsg_type, int nh_family,
unsigned char nh_protocol) {
struct nhmsg *nhm;
int r;
assert_return(rtnl_message_type_is_nexthop(nhmsg_type), -EINVAL);
assert_return((nhmsg_type == RTM_GETNEXTHOP && nh_family == AF_UNSPEC) ||
IN_SET(nh_family, AF_INET, AF_INET6), -EINVAL);
assert_return(ret, -EINVAL);
r = message_new(rtnl, ret, nhmsg_type);
if (r < 0)
return r;
if (nhmsg_type == RTM_NEWNEXTHOP)
(*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND;
nhm = NLMSG_DATA((*ret)->hdr);
nhm->nh_family = nh_family;
nhm->nh_scope = RT_SCOPE_UNIVERSE;
nhm->nh_protocol = nh_protocol;
return 0;
}
int sd_rtnl_message_nexthop_set_flags(sd_netlink_message *m, uint8_t flags) {
struct nhmsg *nhm;
assert_return(m, -EINVAL);
assert_return(m->hdr, -EINVAL);
assert_return(rtnl_message_type_is_nexthop(m->hdr->nlmsg_type), -EINVAL);
nhm = NLMSG_DATA(m->hdr);
nhm->nh_flags |= flags;
return 0;
}
int sd_rtnl_message_nexthop_set_family(sd_netlink_message *m, uint8_t family) {
struct nhmsg *nhm;
assert_return(m, -EINVAL);
assert_return(m->hdr, -EINVAL);
nhm = NLMSG_DATA(m->hdr);
nhm->nh_family = family;
return 0;
}
int sd_rtnl_message_nexthop_get_family(sd_netlink_message *m, uint8_t *family) {
struct nhmsg *nhm;
assert_return(m, -EINVAL);
assert_return(m->hdr, -EINVAL);
nhm = NLMSG_DATA(m->hdr);
*family = nhm->nh_family ;
return 0;
}
int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags) { int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags) {
struct ndmsg *ndm; struct ndmsg *ndm;
@ -713,6 +778,14 @@ int sd_rtnl_message_get_family(sd_netlink_message *m, int *family) {
*family = rtm->rtm_family; *family = rtm->rtm_family;
return 0;
} else if (rtnl_message_type_is_nexthop(m->hdr->nlmsg_type)) {
struct nhmsg *nhm;
nhm = NLMSG_DATA(m->hdr);
*family = nhm->nh_family;
return 0; return 0;
} }

View File

@ -896,6 +896,13 @@ int sd_netlink_add_match(
if (r < 0) if (r < 0)
return r; return r;
break; break;
case RTM_NEWNEXTHOP:
case RTM_DELNEXTHOP:
r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEXTHOP);
if (r < 0)
return r;
break;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }

View File

@ -93,6 +93,8 @@ sources = files('''
networkd-network-bus.h networkd-network-bus.h
networkd-network.c networkd-network.c
networkd-network.h networkd-network.h
networkd-nexthop.c
networkd-nexthop.h
networkd-route.c networkd-route.c
networkd-route.h networkd-route.h
networkd-routing-policy-rule.c networkd-routing-policy-rule.c

View File

@ -672,6 +672,9 @@ static Link *link_free(Link *link) {
link->routes = set_free_with_destructor(link->routes, route_free); link->routes = set_free_with_destructor(link->routes, route_free);
link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free); link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free);
link->nexthops = set_free_with_destructor(link->nexthops, nexthop_free);
link->nexthops_foreign = set_free_with_destructor(link->nexthops_foreign, nexthop_free);
link->neighbors = set_free_with_destructor(link->neighbors, neighbor_free); link->neighbors = set_free_with_destructor(link->neighbors, neighbor_free);
link->neighbors_foreign = set_free_with_destructor(link->neighbors_foreign, neighbor_free); link->neighbors_foreign = set_free_with_destructor(link->neighbors_foreign, neighbor_free);
@ -903,6 +906,58 @@ static int link_request_set_routing_policy_rule(Link *link) {
return 0; return 0;
} }
static int nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
assert(link->nexthop_messages > 0);
assert(IN_SET(link->state, LINK_STATE_CONFIGURING,
LINK_STATE_FAILED, LINK_STATE_LINGER));
link->nexthop_messages--;
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
log_link_warning_errno(link, r, "Could not set nexthop: %m");
link_enter_failed(link);
return 1;
}
if (link->nexthop_messages == 0) {
log_link_debug(link, "Nexthop set");
link->static_nexthops_configured = true;
link_check_ready(link);
}
return 1;
}
int link_request_set_nexthop(Link *link) {
NextHop *nh;
int r;
LIST_FOREACH(nexthops, nh, link->network->static_nexthops) {
r = nexthop_configure(nh, link, nexthop_handler);
if (r < 0)
return log_link_warning_errno(link, r, "Could not set nexthop: %m");
if (r > 0)
link->nexthop_messages++;
}
if (link->nexthop_messages == 0) {
link->static_nexthops_configured = true;
link_check_ready(link);
} else {
log_link_debug(link, "Setting nexthop");
link_set_state(link, LINK_STATE_CONFIGURING);
}
return 1;
}
static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r; int r;
@ -948,6 +1003,7 @@ int link_request_set_routes(Link *link) {
assert(link->state != _LINK_STATE_INVALID); assert(link->state != _LINK_STATE_INVALID);
link->static_routes_configured = false; link->static_routes_configured = false;
link->static_routes_ready = false;
if (!link_has_carrier(link) && !link->network->configure_without_carrier) if (!link_has_carrier(link) && !link->network->configure_without_carrier)
/* During configuring addresses, the link lost its carrier. As networkd is dropping /* During configuring addresses, the link lost its carrier. As networkd is dropping
@ -1017,6 +1073,17 @@ void link_check_ready(Link *link) {
if (!link->static_routes_configured) if (!link->static_routes_configured)
return; return;
if (!link->static_routes_ready) {
link->static_routes_ready = true;
r = link_request_set_nexthop(link);
if (r < 0)
link_enter_failed(link);
return;
}
if (!link->static_nexthops_configured)
return;
if (!link->routing_policy_rules_configured) if (!link->routing_policy_rules_configured)
return; return;
@ -1134,6 +1201,8 @@ static int link_request_set_addresses(Link *link) {
link->addresses_ready = false; link->addresses_ready = false;
link->neighbors_configured = false; link->neighbors_configured = false;
link->static_routes_configured = false; link->static_routes_configured = false;
link->static_routes_ready = false;
link->static_nexthops_configured = false;
link->routing_policy_rules_configured = false; link->routing_policy_rules_configured = false;
r = link_set_bridge_fdb(link); r = link_set_bridge_fdb(link);

View File

@ -69,6 +69,7 @@ typedef struct Link {
unsigned address_label_messages; unsigned address_label_messages;
unsigned neighbor_messages; unsigned neighbor_messages;
unsigned route_messages; unsigned route_messages;
unsigned nexthop_messages;
unsigned routing_policy_rule_messages; unsigned routing_policy_rule_messages;
unsigned routing_policy_rule_remove_messages; unsigned routing_policy_rule_remove_messages;
unsigned enslaving; unsigned enslaving;
@ -79,9 +80,8 @@ typedef struct Link {
Set *neighbors_foreign; Set *neighbors_foreign;
Set *routes; Set *routes;
Set *routes_foreign; Set *routes_foreign;
Set *nexthops;
bool addresses_configured; Set *nexthops_foreign;
bool addresses_ready;
sd_dhcp_client *dhcp_client; sd_dhcp_client *dhcp_client;
sd_dhcp_lease *dhcp_lease, *dhcp_lease_old; sd_dhcp_lease *dhcp_lease, *dhcp_lease_old;
@ -100,8 +100,12 @@ typedef struct Link {
sd_ipv4ll *ipv4ll; sd_ipv4ll *ipv4ll;
bool ipv4ll_address:1; bool ipv4ll_address:1;
bool addresses_configured:1;
bool addresses_ready:1;
bool neighbors_configured:1; bool neighbors_configured:1;
bool static_routes_configured:1; bool static_routes_configured:1;
bool static_routes_ready:1;
bool static_nexthops_configured:1;
bool routing_policy_rules_configured:1; bool routing_policy_rules_configured:1;
bool setting_mtu:1; bool setting_mtu:1;
@ -198,6 +202,7 @@ uint32_t link_get_vrf_table(Link *link);
uint32_t link_get_dhcp_route_table(Link *link); uint32_t link_get_dhcp_route_table(Link *link);
uint32_t link_get_ipv6_accept_ra_route_table(Link *link); uint32_t link_get_ipv6_accept_ra_route_table(Link *link);
int link_request_set_routes(Link *link); int link_request_set_routes(Link *link);
int link_request_set_nexthop(Link *link);
#define ADDRESS_FMT_VAL(address) \ #define ADDRESS_FMT_VAL(address) \
be32toh((address).s_addr) >> 24, \ be32toh((address).s_addr) >> 24, \

View File

@ -5,6 +5,7 @@
#include <unistd.h> #include <unistd.h>
#include <linux/if.h> #include <linux/if.h>
#include <linux/fib_rules.h> #include <linux/fib_rules.h>
#include <linux/nexthop.h>
#include "sd-daemon.h" #include "sd-daemon.h"
#include "sd-netlink.h" #include "sd-netlink.h"
@ -1153,6 +1154,118 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
return 1; return 1;
} }
int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
_cleanup_(nexthop_freep) NextHop *tmp = NULL;
_cleanup_free_ char *gateway = NULL;
NextHop *nexthop = NULL;
Manager *m = userdata;
Link *link = NULL;
uint16_t type;
int r;
assert(rtnl);
assert(message);
assert(m);
if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message);
if (r < 0)
log_warning_errno(r, "rtnl: failed to receive rule message, ignoring: %m");
return 0;
}
r = sd_netlink_message_get_type(message, &type);
if (r < 0) {
log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
return 0;
} else if (!IN_SET(type, RTM_NEWNEXTHOP, RTM_DELNEXTHOP)) {
log_warning("rtnl: received unexpected message type %u when processing nexthop, ignoring.", type);
return 0;
}
r = nexthop_new(&tmp);
if (r < 0)
return log_oom();
r = sd_rtnl_message_get_family(message, &tmp->family);
if (r < 0) {
log_warning_errno(r, "rtnl: could not get nexthop family, ignoring: %m");
return 0;
} else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
log_debug("rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family);
return 0;
}
switch (tmp->family) {
case AF_INET:
r = sd_netlink_message_read_in_addr(message, NHA_GATEWAY, &tmp->gw.in);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
return 0;
}
break;
case AF_INET6:
r = sd_netlink_message_read_in6_addr(message, NHA_GATEWAY, &tmp->gw.in6);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
return 0;
}
break;
default:
assert_not_reached("Received rule message with unsupported address family");
}
r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get NHA_ID attribute, ignoring: %m");
return 0;
}
r = sd_netlink_message_read_u32(message, NHA_OIF, &tmp->oif);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
return 0;
}
r = link_get(m, tmp->oif, &link);
if (r < 0 || !link) {
if (!m->enumerating)
log_warning("rtnl: received nexthop message for link (%d) we do not know about, ignoring", tmp->oif);
return 0;
}
(void) nexthop_get(link, tmp, &nexthop);
if (DEBUG_LOGGING)
(void) in_addr_to_string(tmp->family, &tmp->gw, &gateway);
switch (type) {
case RTM_NEWNEXTHOP:
if (!nexthop) {
log_debug("Remembering foreign nexthop: %s, oif: %d, id: %d", gateway, tmp->oif, tmp->id);
r = nexthop_add_foreign(link, tmp, &nexthop);
if (r < 0) {
log_warning_errno(r, "Could not remember foreign nexthop, ignoring: %m");
return 0;
}
}
break;
case RTM_DELNEXTHOP:
log_debug("Forgetting foreign nexthop: %s, oif: %d, id: %d", gateway, tmp->oif, tmp->id);
nexthop_free(nexthop);
break;
default:
assert_not_reached("Received invalid RTNL message type");
}
return 1;
}
static int systemd_netlink_fd(void) { static int systemd_netlink_fd(void) {
int n, fd, rtnl_fd = -EINVAL; int n, fd, rtnl_fd = -EINVAL;
@ -1253,6 +1366,14 @@ static int manager_connect_rtnl(Manager *m) {
if (r < 0) if (r < 0)
return r; return r;
r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWNEXTHOP, &manager_rtnl_process_nexthop, NULL, m, "network-rtnl_process_nexthop");
if (r < 0)
return r;
r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELNEXTHOP, &manager_rtnl_process_nexthop, NULL, m, "network-rtnl_process_nexthop");
if (r < 0)
return r;
return 0; return 0;
} }
@ -1931,6 +2052,47 @@ int manager_rtnl_enumerate_rules(Manager *m) {
return r; return r;
} }
int manager_rtnl_enumerate_nexthop(Manager *m) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
sd_netlink_message *nexthop;
int r;
assert(m);
assert(m->rtnl);
r = sd_rtnl_message_new_nexthop(m->rtnl, &req, RTM_GETNEXTHOP, 0, 0);
if (r < 0)
return r;
r = sd_netlink_message_request_dump(req, true);
if (r < 0)
return r;
r = sd_netlink_call(m->rtnl, req, 0, &reply);
if (r < 0) {
if (r == -EOPNOTSUPP) {
log_debug("Nexthop are not supported by the kernel. Ignoring.");
return 0;
}
return r;
}
for (nexthop = reply; nexthop; nexthop = sd_netlink_message_next(nexthop)) {
int k;
m->enumerating = true;
k = manager_rtnl_process_nexthop(m->rtnl, nexthop, m);
if (k < 0)
r = k;
m->enumerating = false;
}
return r;
}
int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) { int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
AddressPool *p; AddressPool *p;
int r; int r;

View File

@ -83,11 +83,13 @@ int manager_rtnl_enumerate_addresses(Manager *m);
int manager_rtnl_enumerate_neighbors(Manager *m); int manager_rtnl_enumerate_neighbors(Manager *m);
int manager_rtnl_enumerate_routes(Manager *m); int manager_rtnl_enumerate_routes(Manager *m);
int manager_rtnl_enumerate_rules(Manager *m); int manager_rtnl_enumerate_rules(Manager *m);
int manager_rtnl_enumerate_nexthop(Manager *m);
int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, void *userdata); int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, void *userdata);
int manager_rtnl_process_neighbor(sd_netlink *nl, sd_netlink_message *message, void *userdata); int manager_rtnl_process_neighbor(sd_netlink *nl, sd_netlink_message *message, void *userdata);
int manager_rtnl_process_route(sd_netlink *nl, sd_netlink_message *message, void *userdata); int manager_rtnl_process_route(sd_netlink *nl, sd_netlink_message *message, void *userdata);
int manager_rtnl_process_rule(sd_netlink *nl, sd_netlink_message *message, void *userdata); int manager_rtnl_process_rule(sd_netlink *nl, sd_netlink_message *message, void *userdata);
int manager_rtnl_process_nexthop(sd_netlink *nl, sd_netlink_message *message, void *userdata);
void manager_dirty(Manager *m); void manager_dirty(Manager *m);

View File

@ -546,6 +546,7 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
int r; int r;
assert(link); assert(link);
assert(link->network);
assert(rt); assert(rt);
r = sd_ndisc_router_option_rewind(rt); r = sd_ndisc_router_option_rewind(rt);
@ -564,8 +565,24 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
switch (type) { switch (type) {
case SD_NDISC_OPTION_PREFIX_INFORMATION: { case SD_NDISC_OPTION_PREFIX_INFORMATION: {
union in_addr_union a;
uint8_t flags; uint8_t flags;
r = sd_ndisc_router_prefix_get_address(rt, &a.in6);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get prefix address: %m");
if (set_contains(link->network->ndisc_black_listed_prefix, &a.in6)) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *b = NULL;
(void) in_addr_to_string(AF_INET6, &a, &b);
log_link_debug(link, "Prefix '%s' is black listed, ignoring", strna(b));
}
break;
}
r = sd_ndisc_router_prefix_get_flags(rt, &flags); r = sd_ndisc_router_prefix_get_flags(rt, &flags);
if (r < 0) if (r < 0)
return log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m"); return log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m");
@ -602,46 +619,6 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
return 0; return 0;
} }
static int ndisc_prefix_is_black_listed(Link *link, sd_ndisc_router *rt) {
int r;
assert(link);
assert(link->network);
assert(rt);
for (r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) {
union in_addr_union a;
uint8_t type;
if (r < 0)
return log_link_warning_errno(link, r, "Failed to iterate through options: %m");
if (r == 0) /* EOF */
return false;
r = sd_ndisc_router_option_get_type(rt, &type);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to get RA option type: %m");
if (type != SD_NDISC_OPTION_PREFIX_INFORMATION)
continue;
r = sd_ndisc_router_prefix_get_address(rt, &a.in6);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get prefix address: %m");
if (set_contains(link->network->ndisc_black_listed_prefix, &a.in6)) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *b = NULL;
(void) in_addr_to_string(AF_INET6, &a, &b);
log_link_debug(link, "Prefix '%s' is black listed, ignoring", strna(b));
}
return true;
}
}
}
static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
uint64_t flags; uint64_t flags;
int r; int r;
@ -666,10 +643,8 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
} }
} }
if (ndisc_prefix_is_black_listed(link, rt) == 0) {
(void) ndisc_router_process_default(link, rt); (void) ndisc_router_process_default(link, rt);
(void) ndisc_router_process_options(link, rt); (void) ndisc_router_process_options(link, rt);
}
return r; return r;
} }

View File

@ -141,6 +141,8 @@ Route.InitialAdvertisedReceiveWindow, config_parse_tcp_window,
Route.QuickAck, config_parse_quickack, 0, 0 Route.QuickAck, config_parse_quickack, 0, 0
Route.FastOpenNoCookie, config_parse_fast_open_no_cookie, 0, 0 Route.FastOpenNoCookie, config_parse_fast_open_no_cookie, 0, 0
Route.TTLPropagate, config_parse_route_ttl_propagate, 0, 0 Route.TTLPropagate, config_parse_route_ttl_propagate, 0, 0
NextHop.Id, config_parse_nexthop_id, 0, 0
NextHop.Gateway, config_parse_nexthop_gateway, 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_bool, 0, offsetof(Network, dhcp_use_dns) DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
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

@ -151,6 +151,7 @@ int network_verify(Network *network) {
AddressLabel *label, *label_next; AddressLabel *label, *label_next;
Prefix *prefix, *prefix_next; Prefix *prefix, *prefix_next;
RoutingPolicyRule *rule, *rule_next; RoutingPolicyRule *rule, *rule_next;
NextHop *nexthop, *nextnop_next;
assert(network); assert(network);
assert(network->filename); assert(network->filename);
@ -282,6 +283,10 @@ int network_verify(Network *network) {
if (route_section_verify(route, network) < 0) if (route_section_verify(route, network) < 0)
route_free(route); route_free(route);
LIST_FOREACH_SAFE(nexthops, nexthop, nextnop_next, network->static_nexthops)
if (nexthop_section_verify(nexthop) < 0)
nexthop_free(nexthop);
LIST_FOREACH_SAFE(static_fdb_entries, fdb, fdb_next, network->static_fdb_entries) LIST_FOREACH_SAFE(static_fdb_entries, fdb, fdb_next, network->static_fdb_entries)
if (section_is_invalid(fdb->section)) if (section_is_invalid(fdb->section))
fdb_entry_free(fdb); fdb_entry_free(fdb);
@ -453,8 +458,9 @@ int network_load_one(Manager *manager, const char *filename) {
"IPv6AddressLabel\0" "IPv6AddressLabel\0"
"RoutingPolicyRule\0" "RoutingPolicyRule\0"
"Route\0" "Route\0"
"DHCP\0" "NextHop\0"
"DHCPv4\0" /* compat */ "DHCP\0" /* compat */
"DHCPv4\0"
"DHCPv6\0" "DHCPv6\0"
"DHCPServer\0" "DHCPServer\0"
"IPv6AcceptRA\0" "IPv6AcceptRA\0"
@ -525,8 +531,9 @@ static Network *network_free(Network *network) {
FdbEntry *fdb_entry; FdbEntry *fdb_entry;
Neighbor *neighbor; Neighbor *neighbor;
AddressLabel *label; AddressLabel *label;
Prefix *prefix;
Address *address; Address *address;
NextHop *nexthop;
Prefix *prefix;
Route *route; Route *route;
if (!network) if (!network)
@ -573,6 +580,9 @@ static Network *network_free(Network *network) {
while ((route = network->static_routes)) while ((route = network->static_routes))
route_free(route); route_free(route);
while ((nexthop = network->static_nexthops))
nexthop_free(nexthop);
while ((address = network->static_addresses)) while ((address = network->static_addresses))
address_free(address); address_free(address);
@ -596,6 +606,7 @@ static Network *network_free(Network *network) {
hashmap_free(network->addresses_by_section); hashmap_free(network->addresses_by_section);
hashmap_free(network->routes_by_section); hashmap_free(network->routes_by_section);
hashmap_free(network->nexthops_by_section);
hashmap_free(network->fdb_entries_by_section); hashmap_free(network->fdb_entries_by_section);
hashmap_free(network->neighbors_by_section); hashmap_free(network->neighbors_by_section);
hashmap_free(network->address_labels_by_section); hashmap_free(network->address_labels_by_section);

View File

@ -19,6 +19,7 @@
#include "networkd-lldp-rx.h" #include "networkd-lldp-rx.h"
#include "networkd-lldp-tx.h" #include "networkd-lldp-tx.h"
#include "networkd-neighbor.h" #include "networkd-neighbor.h"
#include "networkd-nexthop.h"
#include "networkd-radv.h" #include "networkd-radv.h"
#include "networkd-route.h" #include "networkd-route.h"
#include "networkd-routing-policy-rule.h" #include "networkd-routing-policy-rule.h"
@ -228,6 +229,7 @@ struct Network {
LIST_HEAD(Address, static_addresses); LIST_HEAD(Address, static_addresses);
LIST_HEAD(Route, static_routes); LIST_HEAD(Route, static_routes);
LIST_HEAD(NextHop, static_nexthops);
LIST_HEAD(FdbEntry, static_fdb_entries); LIST_HEAD(FdbEntry, static_fdb_entries);
LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses); LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
LIST_HEAD(Neighbor, neighbors); LIST_HEAD(Neighbor, neighbors);
@ -238,6 +240,7 @@ struct Network {
unsigned n_static_addresses; unsigned n_static_addresses;
unsigned n_static_routes; unsigned n_static_routes;
unsigned n_static_nexthops;
unsigned n_static_fdb_entries; unsigned n_static_fdb_entries;
unsigned n_ipv6_proxy_ndp_addresses; unsigned n_ipv6_proxy_ndp_addresses;
unsigned n_neighbors; unsigned n_neighbors;
@ -248,6 +251,7 @@ struct Network {
Hashmap *addresses_by_section; Hashmap *addresses_by_section;
Hashmap *routes_by_section; Hashmap *routes_by_section;
Hashmap *nexthops_by_section;
Hashmap *fdb_entries_by_section; Hashmap *fdb_entries_by_section;
Hashmap *neighbors_by_section; Hashmap *neighbors_by_section;
Hashmap *address_labels_by_section; Hashmap *address_labels_by_section;

View File

@ -0,0 +1,473 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2019 VMware, Inc.
*/
#include <linux/nexthop.h>
#include "alloc-util.h"
#include "conf-parser.h"
#include "in-addr-util.h"
#include "netlink-util.h"
#include "networkd-manager.h"
#include "networkd-nexthop.h"
#include "parse-util.h"
#include "set.h"
#include "string-util.h"
#include "util.h"
int nexthop_new(NextHop **ret) {
_cleanup_(nexthop_freep) NextHop *nexthop = NULL;
nexthop = new(NextHop, 1);
if (!nexthop)
return -ENOMEM;
*nexthop = (NextHop) {
.family = AF_UNSPEC,
};
*ret = TAKE_PTR(nexthop);
return 0;
}
static int nexthop_new_static(Network *network, const char *filename, unsigned section_line, NextHop **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(nexthop_freep) NextHop *nexthop = NULL;
int r;
assert(network);
assert(ret);
assert(!!filename == (section_line > 0));
if (filename) {
r = network_config_section_new(filename, section_line, &n);
if (r < 0)
return r;
nexthop = hashmap_get(network->nexthops_by_section, n);
if (nexthop) {
*ret = TAKE_PTR(nexthop);
return 0;
}
}
r = nexthop_new(&nexthop);
if (r < 0)
return r;
nexthop->protocol = RTPROT_STATIC;
nexthop->network = network;
LIST_PREPEND(nexthops, network->static_nexthops, nexthop);
network->n_static_nexthops++;
if (filename) {
nexthop->section = TAKE_PTR(n);
r = hashmap_ensure_allocated(&network->nexthops_by_section, &network_config_hash_ops);
if (r < 0)
return r;
r = hashmap_put(network->nexthops_by_section, nexthop->section, nexthop);
if (r < 0)
return r;
}
*ret = TAKE_PTR(nexthop);
return 0;
}
void nexthop_free(NextHop *nexthop) {
if (!nexthop)
return;
if (nexthop->network) {
LIST_REMOVE(nexthops, nexthop->network->static_nexthops, nexthop);
assert(nexthop->network->n_static_nexthops > 0);
nexthop->network->n_static_nexthops--;
if (nexthop->section)
hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section);
}
network_config_section_free(nexthop->section);
if (nexthop->link) {
set_remove(nexthop->link->nexthops, nexthop);
set_remove(nexthop->link->nexthops_foreign, nexthop);
}
free(nexthop);
}
static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) {
assert(nexthop);
siphash24_compress(&nexthop->id, sizeof(nexthop->id), state);
siphash24_compress(&nexthop->oif, sizeof(nexthop->oif), state);
siphash24_compress(&nexthop->family, sizeof(nexthop->family), state);
switch (nexthop->family) {
case AF_INET:
case AF_INET6:
siphash24_compress(&nexthop->gw, FAMILY_ADDRESS_SIZE(nexthop->family), state);
break;
default:
/* treat any other address family as AF_UNSPEC */
break;
}
}
static int nexthop_compare_func(const NextHop *a, const NextHop *b) {
int r;
r = CMP(a->id, b->id);
if (r != 0)
return r;
r = CMP(a->oif, b->oif);
if (r != 0)
return r;
r = CMP(a->family, b->family);
if (r != 0)
return r;
switch (a->family) {
case AF_INET:
case AF_INET6:
r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
if (r != 0)
return r;
return 0;
default:
/* treat any other address family as AF_UNSPEC */
return 0;
}
}
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
nexthop_hash_ops,
NextHop,
nexthop_hash_func,
nexthop_compare_func,
nexthop_free);
bool nexthop_equal(NextHop *r1, NextHop *r2) {
if (r1 == r2)
return true;
if (!r1 || !r2)
return false;
return nexthop_compare_func(r1, r2) == 0;
}
int nexthop_get(Link *link, NextHop *in, NextHop **ret) {
NextHop *existing;
assert(link);
assert(in);
existing = set_get(link->nexthops, in);
if (existing) {
if (ret)
*ret = existing;
return 1;
}
existing = set_get(link->nexthops_foreign, in);
if (existing) {
if (ret)
*ret = existing;
return 0;
}
return -ENOENT;
}
static int nexthop_add_internal(Link *link, Set **nexthops, NextHop *in, NextHop **ret) {
_cleanup_(nexthop_freep) NextHop *nexthop = NULL;
int r;
assert(link);
assert(nexthops);
assert(in);
r = nexthop_new(&nexthop);
if (r < 0)
return r;
nexthop->id = in->id;
nexthop->oif = in->oif;
nexthop->family = in->family;
nexthop->gw = in->gw;
r = set_ensure_allocated(nexthops, &nexthop_hash_ops);
if (r < 0)
return r;
r = set_put(*nexthops, nexthop);
if (r < 0)
return r;
if (r == 0)
return -EEXIST;
nexthop->link = link;
if (ret)
*ret = nexthop;
nexthop = NULL;
return 0;
}
int nexthop_add_foreign(Link *link, NextHop *in, NextHop **ret) {
return nexthop_add_internal(link, &link->nexthops_foreign, in, ret);
}
int nexthop_add(Link *link, NextHop *in, NextHop **ret) {
NextHop *nexthop;
int r;
r = nexthop_get(link, in, &nexthop);
if (r == -ENOENT) {
/* NextHop does not exist, create a new one */
r = nexthop_add_internal(link, &link->nexthops, in, &nexthop);
if (r < 0)
return r;
} else if (r == 0) {
/* Take over a foreign nexthop */
r = set_ensure_allocated(&link->nexthops, &nexthop_hash_ops);
if (r < 0)
return r;
r = set_put(link->nexthops, nexthop);
if (r < 0)
return r;
set_remove(link->nexthops_foreign, nexthop);
} else if (r == 1) {
/* NextHop exists, do nothing */
;
} else
return r;
if (ret)
*ret = nexthop;
return 0;
}
static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(m);
assert(link);
assert(link->ifname);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -ESRCH)
log_link_warning_errno(link, r, "Could not drop nexthop: %m");
return 1;
}
int nexthop_remove(NextHop *nexthop, Link *link,
link_netlink_message_handler_t callback) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(link);
assert(link->manager);
assert(link->manager->rtnl);
assert(link->ifindex > 0);
assert(IN_SET(nexthop->family, AF_INET, AF_INET6));
r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &req,
RTM_DELNEXTHOP, nexthop->family,
nexthop->protocol);
if (r < 0)
return log_link_error_errno(link, r, "Could not create RTM_DELNEXTHOP message: %m");
if (DEBUG_LOGGING) {
_cleanup_free_ char *gw = NULL;
if (!in_addr_is_null(nexthop->family, &nexthop->gw))
(void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw);
log_link_debug(link, "Removing nexthop: gw: %s", strna(gw));
}
if (in_addr_is_null(nexthop->family, &nexthop->gw) == 0) {
r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, nexthop->family, &nexthop->gw);
if (r < 0)
return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
}
r = netlink_call_async(link->manager->rtnl, NULL, req,
callback ?: nexthop_remove_handler,
link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
return 0;
}
int nexthop_configure(
NextHop *nexthop,
Link *link,
link_netlink_message_handler_t callback) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(link);
assert(link->manager);
assert(link->manager->rtnl);
assert(link->ifindex > 0);
assert(IN_SET(nexthop->family, AF_INET, AF_INET6));
assert(callback);
if (DEBUG_LOGGING) {
_cleanup_free_ char *gw = NULL;
if (!in_addr_is_null(nexthop->family, &nexthop->gw))
(void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw);
log_link_debug(link, "Configuring nexthop: gw: %s", strna(gw));
}
r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &req,
RTM_NEWNEXTHOP, nexthop->family,
nexthop->protocol);
if (r < 0)
return log_link_error_errno(link, r, "Could not create RTM_NEWNEXTHOP message: %m");
r = sd_netlink_message_append_u32(req, NHA_ID, nexthop->id);
if (r < 0)
return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m");
r = sd_netlink_message_append_u32(req, NHA_OIF, link->ifindex);
if (r < 0)
return log_link_error_errno(link, r, "Could not append NHA_OIF attribute: %m");
if (in_addr_is_null(nexthop->family, &nexthop->gw) == 0) {
r = netlink_message_append_in_addr_union(req, NHA_GATEWAY, nexthop->family, &nexthop->gw);
if (r < 0)
return log_link_error_errno(link, r, "Could not append NHA_GATEWAY attribute: %m");
r = sd_rtnl_message_nexthop_set_family(req, nexthop->family);
if (r < 0)
return log_link_error_errno(link, r, "Could not set nexthop family: %m");
}
r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
r = nexthop_add(link, nexthop, &nexthop);
if (r < 0)
return log_link_error_errno(link, r, "Could not add nexthop: %m");
return 1;
}
int nexthop_section_verify(NextHop *nh) {
if (section_is_invalid(nh->section))
return -EINVAL;
if (in_addr_is_null(nh->family, &nh->gw) < 0)
return -EINVAL;
return 0;
}
int config_parse_nexthop_id(
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 r;
r = safe_atou32(rvalue, &n->id);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue);
return 0;
}
TAKE_PTR(n);
return 0;
}
int config_parse_nexthop_gateway(
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 r;
r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
return 0;
}
TAKE_PTR(n);
return 0;
}

View File

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2019 VMware, Inc.
*/
#pragma once
#include "conf-parser.h"
#include "macro.h"
typedef struct NextHop NextHop;
typedef struct NetworkConfigSection NetworkConfigSection;
#include "networkd-network.h"
#include "networkd-util.h"
struct NextHop {
Network *network;
NetworkConfigSection *section;
Link *link;
unsigned char protocol;
int family;
uint32_t oif;
uint32_t id;
union in_addr_union gw;
LIST_FIELDS(NextHop, nexthops);
};
extern const struct hash_ops nexthop_hash_ops;
int nexthop_new(NextHop **ret);
void nexthop_free(NextHop *nexthop);
int nexthop_configure(NextHop *nexthop, Link *link, link_netlink_message_handler_t callback);
int nexthop_remove(NextHop *nexthop, Link *link, link_netlink_message_handler_t callback);
int nexthop_get(Link *link, NextHop *in, NextHop **ret);
int nexthop_add(Link *link, NextHop *in, NextHop **ret);
int nexthop_add_foreign(Link *link, NextHop *in, NextHop **ret);
bool nexthop_equal(NextHop *r1, NextHop *r2);
int nexthop_section_verify(NextHop *nexthop);
DEFINE_NETWORK_SECTION_FUNCTIONS(NextHop, nexthop_free);
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_id);
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_gateway);

View File

@ -107,6 +107,10 @@ static int run(int argc, char *argv[]) {
if (r < 0) if (r < 0)
return log_error_errno(r, "Could not enumerate rules: %m"); return log_error_errno(r, "Could not enumerate rules: %m");
r = manager_rtnl_enumerate_nexthop(m);
if (r < 0)
return log_error_errno(r, "Could not enumerate nexthop: %m");
r = manager_start(m); r = manager_start(m);
if (r < 0) if (r < 0)
return log_error_errno(r, "Could not start manager: %m"); return log_error_errno(r, "Could not start manager: %m");

View File

@ -419,21 +419,17 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
int r; int r;
if (STR_IN_SET(field, "DevicePolicy", "Slice")) if (STR_IN_SET(field, "DevicePolicy", "Slice"))
return bus_append_string(m, field, eq); return bus_append_string(m, field, eq);
if (STR_IN_SET(field, if (STR_IN_SET(field,
"CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting", "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
"TasksAccounting", "IPAccounting")) "TasksAccounting", "IPAccounting"))
return bus_append_parse_boolean(m, field, eq); return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight")) if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight"))
return bus_append_cg_weight_parse(m, field, eq); return bus_append_cg_weight_parse(m, field, eq);
if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) if (STR_IN_SET(field, "CPUShares", "StartupCPUShares"))
return bus_append_cg_cpu_shares_parse(m, field, eq); return bus_append_cg_cpu_shares_parse(m, field, eq);
if (STR_IN_SET(field, "AllowedCPUs", "AllowedMemoryNodes")) { if (STR_IN_SET(field, "AllowedCPUs", "AllowedMemoryNodes")) {
@ -453,15 +449,12 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
} }
if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight"))
return bus_append_cg_blkio_weight_parse(m, field, eq); return bus_append_cg_blkio_weight_parse(m, field, eq);
if (streq(field, "DisableControllers")) if (streq(field, "DisableControllers"))
return bus_append_strv(m, "DisableControllers", eq, EXTRACT_UNQUOTE); return bus_append_strv(m, "DisableControllers", eq, EXTRACT_UNQUOTE);
if (streq(field, "Delegate")) { if (streq(field, "Delegate")) {
r = parse_boolean(eq); r = parse_boolean(eq);
if (r < 0) if (r < 0)
return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_UNQUOTE); return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_UNQUOTE);
@ -473,7 +466,15 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
return 1; return 1;
} }
if (STR_IN_SET(field, "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) { if (STR_IN_SET(field, "MemoryMin",
"DefaultMemoryLow",
"DefaultMemoryMin",
"MemoryLow",
"MemoryHigh",
"MemoryMax",
"MemorySwapMax",
"MemoryLimit",
"TasksMax")) {
if (isempty(eq) || streq(eq, "infinity")) { if (isempty(eq) || streq(eq, "infinity")) {
r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX); r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX);
@ -505,7 +506,6 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
} }
if (streq(field, "CPUQuota")) { if (streq(field, "CPUQuota")) {
if (isempty(eq)) if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
else { else {
@ -540,7 +540,6 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
} }
if (streq(field, "DeviceAllow")) { if (streq(field, "DeviceAllow")) {
if (isempty(eq)) if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0); r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
else { else {
@ -562,7 +561,6 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
} }
if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
if (isempty(eq)) if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0); r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
else { else {
@ -596,7 +594,6 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
} }
if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) { if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) {
if (isempty(eq)) if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0); r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
else { else {
@ -792,17 +789,13 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
} }
static int bus_append_automount_property(sd_bus_message *m, const char *field, const char *eq) { static int bus_append_automount_property(sd_bus_message *m, const char *field, const char *eq) {
if (streq(field, "Where")) if (streq(field, "Where"))
return bus_append_string(m, field, eq); return bus_append_string(m, field, eq);
if (streq(field, "DirectoryMode")) if (streq(field, "DirectoryMode"))
return bus_append_parse_mode(m, field, eq); return bus_append_parse_mode(m, field, eq);
if (streq(field, "TimeoutIdleSec")) if (streq(field, "TimeoutIdleSec"))
return bus_append_parse_sec_rename(m, field, eq); return bus_append_parse_sec_rename(m, field, eq);
return 0; return 0;
@ -818,7 +811,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
"WorkingDirectory", "RootDirectory", "SyslogIdentifier", "WorkingDirectory", "RootDirectory", "SyslogIdentifier",
"ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage", "ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage",
"RuntimeDirectoryPreserve", "Personality", "KeyringMode", "NetworkNamespacePath")) "RuntimeDirectoryPreserve", "Personality", "KeyringMode", "NetworkNamespacePath"))
return bus_append_string(m, field, eq); return bus_append_string(m, field, eq);
if (STR_IN_SET(field, if (STR_IN_SET(field,
@ -828,7 +820,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
"DynamicUser", "RemoveIPC", "ProtectKernelTunables", "ProtectKernelModules", "DynamicUser", "RemoveIPC", "ProtectKernelTunables", "ProtectKernelModules",
"ProtectControlGroups", "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality", "ProtectControlGroups", "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality",
"ProtectHostname", "RestrictSUIDSGID")) "ProtectHostname", "RestrictSUIDSGID"))
return bus_append_parse_boolean(m, field, eq); return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, if (STR_IN_SET(field,
@ -836,73 +827,56 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
"ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths", "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths",
"RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory", "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory",
"SupplementaryGroups", "SystemCallArchitectures")) "SupplementaryGroups", "SystemCallArchitectures"))
return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE); return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax")) if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax"))
return bus_append_log_level_from_string(m, field, eq); return bus_append_log_level_from_string(m, field, eq);
if (streq(field, "SyslogFacility")) if (streq(field, "SyslogFacility"))
return bus_append_log_facility_unshifted_from_string(m, field, eq); return bus_append_log_facility_unshifted_from_string(m, field, eq);
if (streq(field, "SecureBits")) if (streq(field, "SecureBits"))
return bus_append_secure_bits_from_string(m, field, eq); return bus_append_secure_bits_from_string(m, field, eq);
if (streq(field, "CPUSchedulingPolicy")) if (streq(field, "CPUSchedulingPolicy"))
return bus_append_sched_policy_from_string(m, field, eq); return bus_append_sched_policy_from_string(m, field, eq);
if (STR_IN_SET(field, "CPUSchedulingPriority", "OOMScoreAdjust")) if (STR_IN_SET(field, "CPUSchedulingPriority", "OOMScoreAdjust"))
return bus_append_safe_atoi(m, field, eq); return bus_append_safe_atoi(m, field, eq);
if (streq(field, "Nice")) if (streq(field, "Nice"))
return bus_append_parse_nice(m, field, eq); return bus_append_parse_nice(m, field, eq);
if (streq(field, "SystemCallErrorNumber")) if (streq(field, "SystemCallErrorNumber"))
return bus_append_parse_errno(m, field, eq); return bus_append_parse_errno(m, field, eq);
if (streq(field, "IOSchedulingClass")) if (streq(field, "IOSchedulingClass"))
return bus_append_ioprio_class_from_string(m, field, eq); return bus_append_ioprio_class_from_string(m, field, eq);
if (streq(field, "IOSchedulingPriority")) if (streq(field, "IOSchedulingPriority"))
return bus_append_ioprio_parse_priority(m, field, eq); return bus_append_ioprio_parse_priority(m, field, eq);
if (STR_IN_SET(field, if (STR_IN_SET(field,
"RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode",
"LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask"))
return bus_append_parse_mode(m, field, eq); return bus_append_parse_mode(m, field, eq);
if (streq(field, "TimerSlackNSec")) if (streq(field, "TimerSlackNSec"))
return bus_append_parse_nsec(m, field, eq); return bus_append_parse_nsec(m, field, eq);
if (streq(field, "LogRateLimitIntervalSec")) if (streq(field, "LogRateLimitIntervalSec"))
return bus_append_parse_sec_rename(m, field, eq); return bus_append_parse_sec_rename(m, field, eq);
if (streq(field, "LogRateLimitBurst")) if (streq(field, "LogRateLimitBurst"))
return bus_append_safe_atou(m, field, eq); return bus_append_safe_atou(m, field, eq);
if (streq(field, "MountFlags")) if (streq(field, "MountFlags"))
return bus_append_mount_propagation_flags_from_string(m, field, eq); return bus_append_mount_propagation_flags_from_string(m, field, eq);
if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment")) if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment"))
return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE); return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
if (streq(field, "EnvironmentFile")) { if (streq(field, "EnvironmentFile")) {
if (isempty(eq)) if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0); r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0);
else else
@ -916,7 +890,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
} }
if (streq(field, "LogExtraFields")) { if (streq(field, "LogExtraFields")) {
r = sd_bus_message_open_container(m, 'r', "sv"); r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
@ -1354,7 +1327,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
} }
static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) { static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
if (streq(field, "KillMode")) if (streq(field, "KillMode"))
return bus_append_string(m, field, eq); return bus_append_string(m, field, eq);
@ -1370,19 +1342,15 @@ static int bus_append_kill_property(sd_bus_message *m, const char *field, const
static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) { static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) {
if (STR_IN_SET(field, "What", "Where", "Options", "Type")) if (STR_IN_SET(field, "What", "Where", "Options", "Type"))
return bus_append_string(m, field, eq); return bus_append_string(m, field, eq);
if (streq(field, "TimeoutSec")) if (streq(field, "TimeoutSec"))
return bus_append_parse_sec_rename(m, field, eq); return bus_append_parse_sec_rename(m, field, eq);
if (streq(field, "DirectoryMode")) if (streq(field, "DirectoryMode"))
return bus_append_parse_mode(m, field, eq); return bus_append_parse_mode(m, field, eq);
if (STR_IN_SET(field, "SloppyOptions", "LazyUnmount", "ForceUnmount")) if (STR_IN_SET(field, "SloppyOptions", "LazyUnmount", "ForceUnmount"))
return bus_append_parse_boolean(m, field, eq); return bus_append_parse_boolean(m, field, eq);
return 0; return 0;
@ -1392,17 +1360,14 @@ static int bus_append_path_property(sd_bus_message *m, const char *field, const
int r; int r;
if (streq(field, "MakeDirectory")) if (streq(field, "MakeDirectory"))
return bus_append_parse_boolean(m, field, eq); return bus_append_parse_boolean(m, field, eq);
if (streq(field, "DirectoryMode")) if (streq(field, "DirectoryMode"))
return bus_append_parse_mode(m, field, eq); return bus_append_parse_mode(m, field, eq);
if (STR_IN_SET(field, if (STR_IN_SET(field,
"PathExists", "PathExistsGlob", "PathChanged", "PathExists", "PathExistsGlob", "PathChanged",
"PathModified", "DirectoryNotEmpty")) { "PathModified", "DirectoryNotEmpty")) {
if (isempty(eq)) if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0); r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0);
else else
@ -1422,19 +1387,15 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
if (STR_IN_SET(field, if (STR_IN_SET(field,
"PIDFile", "Type", "Restart", "BusName", "NotifyAccess", "PIDFile", "Type", "Restart", "BusName", "NotifyAccess",
"USBFunctionDescriptors", "USBFunctionStrings", "OOMPolicy")) "USBFunctionDescriptors", "USBFunctionStrings", "OOMPolicy"))
return bus_append_string(m, field, eq); return bus_append_string(m, field, eq);
if (STR_IN_SET(field, "PermissionsStartOnly", "RootDirectoryStartOnly", "RemainAfterExit", "GuessMainPID")) if (STR_IN_SET(field, "PermissionsStartOnly", "RootDirectoryStartOnly", "RemainAfterExit", "GuessMainPID"))
return bus_append_parse_boolean(m, field, eq); return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "RestartSec", "TimeoutStartSec", "TimeoutStopSec", "RuntimeMaxSec", "WatchdogSec")) if (STR_IN_SET(field, "RestartSec", "TimeoutStartSec", "TimeoutStopSec", "RuntimeMaxSec", "WatchdogSec"))
return bus_append_parse_sec_rename(m, field, eq); return bus_append_parse_sec_rename(m, field, eq);
if (streq(field, "TimeoutSec")) { if (streq(field, "TimeoutSec")) {
r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq); r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq);
if (r < 0) if (r < 0)
return r; return r;
@ -1443,7 +1404,6 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
} }
if (streq(field, "FileDescriptorStoreMax")) if (streq(field, "FileDescriptorStoreMax"))
return bus_append_safe_atou(m, field, eq); return bus_append_safe_atou(m, field, eq);
if (STR_IN_SET(field, if (STR_IN_SET(field,
@ -1543,60 +1503,47 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons
if (STR_IN_SET(field, if (STR_IN_SET(field,
"Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast", "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
"PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet")) "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet"))
return bus_append_parse_boolean(m, field, eq); return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "Priority", "IPTTL", "Mark")) if (STR_IN_SET(field, "Priority", "IPTTL", "Mark"))
return bus_append_safe_atoi(m, field, eq); return bus_append_safe_atoi(m, field, eq);
if (streq(field, "IPTOS")) if (streq(field, "IPTOS"))
return bus_append_ip_tos_from_string(m, field, eq); return bus_append_ip_tos_from_string(m, field, eq);
if (STR_IN_SET(field, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst")) if (STR_IN_SET(field, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst"))
return bus_append_safe_atou(m, field, eq); return bus_append_safe_atou(m, field, eq);
if (STR_IN_SET(field, "SocketMode", "DirectoryMode")) if (STR_IN_SET(field, "SocketMode", "DirectoryMode"))
return bus_append_parse_mode(m, field, eq); return bus_append_parse_mode(m, field, eq);
if (STR_IN_SET(field, "MessageQueueMaxMessages", "MessageQueueMessageSize")) if (STR_IN_SET(field, "MessageQueueMaxMessages", "MessageQueueMessageSize"))
return bus_append_safe_atoi64(m, field, eq); return bus_append_safe_atoi64(m, field, eq);
if (STR_IN_SET(field, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec")) if (STR_IN_SET(field, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec"))
return bus_append_parse_sec_rename(m, field, eq); return bus_append_parse_sec_rename(m, field, eq);
if (STR_IN_SET(field, "ReceiveBuffer", "SendBuffer", "PipeSize")) if (STR_IN_SET(field, "ReceiveBuffer", "SendBuffer", "PipeSize"))
return bus_append_parse_size(m, field, eq, 1024); return bus_append_parse_size(m, field, eq, 1024);
if (STR_IN_SET(field, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost")) if (STR_IN_SET(field, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost"))
return bus_append_exec_command(m, field, eq); return bus_append_exec_command(m, field, eq);
if (STR_IN_SET(field, if (STR_IN_SET(field,
"SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion", "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion",
"BindToDevice", "BindIPv6Only", "FileDescriptorName", "BindToDevice", "BindIPv6Only", "FileDescriptorName",
"SocketUser", "SocketGroup")) "SocketUser", "SocketGroup"))
return bus_append_string(m, field, eq); return bus_append_string(m, field, eq);
if (streq(field, "Symlinks")) if (streq(field, "Symlinks"))
return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE); return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
if (streq(field, "SocketProtocol")) if (streq(field, "SocketProtocol"))
return bus_append_parse_ip_protocol(m, field, eq); return bus_append_parse_ip_protocol(m, field, eq);
if (STR_IN_SET(field, if (STR_IN_SET(field,
"ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink", "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
"ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) { "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) {
if (isempty(eq)) if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0); r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
else else
@ -1614,17 +1561,14 @@ static int bus_append_timer_property(sd_bus_message *m, const char *field, const
if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent", if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent",
"OnTimezoneChange", "OnClockChange")) "OnTimezoneChange", "OnClockChange"))
return bus_append_parse_boolean(m, field, eq); return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec")) if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec"))
return bus_append_parse_sec_rename(m, field, eq); return bus_append_parse_sec_rename(m, field, eq);
if (STR_IN_SET(field, if (STR_IN_SET(field,
"OnActiveSec", "OnBootSec", "OnStartupSec", "OnActiveSec", "OnBootSec", "OnStartupSec",
"OnUnitActiveSec","OnUnitInactiveSec")) { "OnUnitActiveSec","OnUnitInactiveSec")) {
if (isempty(eq)) if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0); r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0);
else { else {
@ -1642,7 +1586,6 @@ static int bus_append_timer_property(sd_bus_message *m, const char *field, const
} }
if (streq(field, "OnCalendar")) { if (streq(field, "OnCalendar")) {
if (isempty(eq)) if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0); r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
else else
@ -1666,25 +1609,20 @@ static int bus_append_unit_property(sd_bus_message *m, const char *field, const
"JobTimeoutAction", "JobTimeoutRebootArgument", "JobTimeoutAction", "JobTimeoutRebootArgument",
"StartLimitAction", "FailureAction", "SuccessAction", "StartLimitAction", "FailureAction", "SuccessAction",
"RebootArgument", "CollectMode")) "RebootArgument", "CollectMode"))
return bus_append_string(m, field, eq); return bus_append_string(m, field, eq);
if (STR_IN_SET(field, if (STR_IN_SET(field,
"StopWhenUnneeded", "RefuseManualStart", "RefuseManualStop", "StopWhenUnneeded", "RefuseManualStart", "RefuseManualStop",
"AllowIsolate", "IgnoreOnIsolate", "DefaultDependencies")) "AllowIsolate", "IgnoreOnIsolate", "DefaultDependencies"))
return bus_append_parse_boolean(m, field, eq); return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "JobTimeoutSec", "JobRunningTimeoutSec", "StartLimitIntervalSec")) if (STR_IN_SET(field, "JobTimeoutSec", "JobRunningTimeoutSec", "StartLimitIntervalSec"))
return bus_append_parse_sec_rename(m, field, eq); return bus_append_parse_sec_rename(m, field, eq);
if (streq(field, "StartLimitBurst")) if (streq(field, "StartLimitBurst"))
return bus_append_safe_atou(m, field, eq); return bus_append_safe_atou(m, field, eq);
if (STR_IN_SET(field, "SuccessActionExitStatus", "FailureActionExitStatus")) { if (STR_IN_SET(field, "SuccessActionExitStatus", "FailureActionExitStatus")) {
if (isempty(eq)) if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", field, "i", -1); r = sd_bus_message_append(m, "(sv)", field, "i", -1);
else { else {
@ -1704,7 +1642,6 @@ static int bus_append_unit_property(sd_bus_message *m, const char *field, const
if (unit_dependency_from_string(field) >= 0 || if (unit_dependency_from_string(field) >= 0 ||
STR_IN_SET(field, "Documentation", "RequiresMountsFor")) STR_IN_SET(field, "Documentation", "RequiresMountsFor"))
return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE); return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
t = condition_type_from_string(field); t = condition_type_from_string(field);

View File

@ -7977,7 +7977,7 @@ static void help_states(void) {
static int help_boot_loader_entry(void) { static int help_boot_loader_entry(void) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char **l = NULL; _cleanup_strv_free_ char **l = NULL;
sd_bus *bus; sd_bus *bus;
char **i; char **i;
int r; int r;

View File

@ -167,6 +167,12 @@ int sd_rtnl_message_route_get_dst_prefixlen(sd_netlink_message *m, unsigned char
int sd_rtnl_message_route_get_src_prefixlen(sd_netlink_message *m, unsigned char *src_len); int sd_rtnl_message_route_get_src_prefixlen(sd_netlink_message *m, unsigned char *src_len);
int sd_rtnl_message_route_get_type(sd_netlink_message *m, unsigned char *type); int sd_rtnl_message_route_get_type(sd_netlink_message *m, unsigned char *type);
int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nhmsg_type, int nh_family, unsigned char nh_protocol);
int sd_rtnl_message_nexthop_set_flags(sd_netlink_message *m, uint8_t flags);
int sd_rtnl_message_nexthop_set_family(sd_netlink_message *m, uint8_t family);
int sd_rtnl_message_nexthop_get_family(sd_netlink_message *m, uint8_t *family);
int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags); int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags);
int sd_rtnl_message_neigh_set_state(sd_netlink_message *m, uint16_t state); int sd_rtnl_message_neigh_set_state(sd_netlink_message *m, uint16_t state);
int sd_rtnl_message_neigh_get_family(sd_netlink_message *m, int *family); int sd_rtnl_message_neigh_get_family(sd_netlink_message *m, int *family);

View File

@ -425,9 +425,10 @@ int main(int argc, char *argv[]) {
export = 1; export = 1;
break; break;
case 'h': case 'h':
printf("Usage: ata_id [--export] [--help] <device>\n" printf("Usage: %s [--export] [--help] <device>\n"
" -x,--export print values as environment keys\n" " -x,--export print values as environment keys\n"
" -h,--help print this help text\n\n"); " -h,--help print this help text\n\n",
program_invocation_short_name);
return 0; return 0;
} }
} }

View File

@ -849,12 +849,13 @@ int main(int argc, char *argv[]) {
log_open(); log_open();
break; break;
case 'h': case 'h':
printf("Usage: cdrom_id [options] <device>\n" printf("Usage: %s [options] <device>\n"
" -l,--lock-media lock the media (to enable eject request events)\n" " -l,--lock-media lock the media (to enable eject request events)\n"
" -u,--unlock-media unlock the media\n" " -u,--unlock-media unlock the media\n"
" -e,--eject-media eject the media\n" " -e,--eject-media eject the media\n"
" -d,--debug debug to stderr\n" " -d,--debug debug to stderr\n"
" -h,--help print this help text\n\n"); " -h,--help print this help text\n\n",
program_invocation_short_name);
goto exit; goto exit;
default: default:
rc = 1; rc = 1;

View File

@ -44,7 +44,7 @@ static int run(int argc, char **argv) {
log_open(); log_open();
if (argc > 2) if (argc > 2)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Usage: hidraw_id [SYSFS_PATH]"); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Usage: %s [SYSFS_PATH]", program_invocation_short_name);
if (argc == 1) { if (argc == 1) {
r = device_new_from_strv(&device, environ); r = device_new_from_strv(&device, environ);

View File

@ -3,6 +3,7 @@
#include <ctype.h> #include <ctype.h>
#include "alloc-util.h" #include "alloc-util.h"
#include "architecture.h"
#include "conf-files.h" #include "conf-files.h"
#include "def.h" #include "def.h"
#include "device-util.h" #include "device-util.h"
@ -28,6 +29,7 @@
#include "udev-event.h" #include "udev-event.h"
#include "udev-rules.h" #include "udev-rules.h"
#include "user-util.h" #include "user-util.h"
#include "virt.h"
#define RULES_DIRS (const char* const*) CONF_PATHS_STRV("udev/rules.d") #define RULES_DIRS (const char* const*) CONF_PATHS_STRV("udev/rules.d")
@ -69,6 +71,7 @@ typedef enum {
TK_M_DEVLINK, /* strv, sd_device_get_devlink_first(), sd_device_get_devlink_next() */ TK_M_DEVLINK, /* strv, sd_device_get_devlink_first(), sd_device_get_devlink_next() */
TK_M_NAME, /* string, name of network interface */ TK_M_NAME, /* string, name of network interface */
TK_M_ENV, /* string, device property, takes key through attribute */ TK_M_ENV, /* string, device property, takes key through attribute */
TK_M_CONST, /* string, system-specific hard-coded constant */
TK_M_TAG, /* strv, sd_device_get_tag_first(), sd_device_get_tag_next() */ TK_M_TAG, /* strv, sd_device_get_tag_first(), sd_device_get_tag_next() */
TK_M_SUBSYSTEM, /* string, sd_device_get_subsystem() */ TK_M_SUBSYSTEM, /* string, sd_device_get_subsystem() */
TK_M_DRIVER, /* string, sd_device_get_driver() */ TK_M_DRIVER, /* string, sd_device_get_driver() */
@ -618,6 +621,12 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
r = rule_line_add_token(rule_line, TK_A_ENV, op, value, attr); r = rule_line_add_token(rule_line, TK_A_ENV, op, value, attr);
} else } else
r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr); r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr);
} else if (streq(key, "CONST")) {
if (isempty(attr) || !STR_IN_SET(attr, "arch", "virt"))
return log_token_invalid_attr(rules, key);
if (!is_match)
return log_token_invalid_op(rules, key);
r = rule_line_add_token(rule_line, TK_M_CONST, op, value, attr);
} else if (streq(key, "TAG")) { } else if (streq(key, "TAG")) {
if (attr) if (attr)
return log_token_invalid_attr(rules, key); return log_token_invalid_attr(rules, key);
@ -1574,6 +1583,17 @@ static int udev_rule_apply_token_to_event(
val = hashmap_get(properties_list, token->data); val = hashmap_get(properties_list, token->data);
return token_match_string(token, val); return token_match_string(token, val);
case TK_M_CONST: {
const char *k = token->data;
if (streq(k, "arch"))
val = architecture_to_string(uname_architecture());
else if (streq(k, "virt"))
val = virtualization_to_string(detect_virtualization());
else
assert_not_reached("Invalid CONST key");
return token_match_string(token, val);
}
case TK_M_TAG: case TK_M_TAG:
case TK_M_PARENTS_TAG: case TK_M_PARENTS_TAG:
FOREACH_DEVICE_TAG(dev, val) FOREACH_DEVICE_TAG(dev, val)
@ -2325,7 +2345,7 @@ static int apply_static_dev_perms(const char *devnode, uid_t uid, gid_t gid, mod
static int udev_rule_line_apply_static_dev_perms(UdevRuleLine *rule_line) { static int udev_rule_line_apply_static_dev_perms(UdevRuleLine *rule_line) {
UdevRuleToken *token; UdevRuleToken *token;
_cleanup_free_ char **tags = NULL; _cleanup_strv_free_ char **tags = NULL;
uid_t uid = UID_INVALID; uid_t uid = UID_INVALID;
gid_t gid = GID_INVALID; gid_t gid = GID_INVALID;
mode_t mode = MODE_INVALID; mode_t mode = MODE_INVALID;

View File

@ -255,3 +255,6 @@ MaxLeaseTimeSec=
DefaultLeaseTimeSec= DefaultLeaseTimeSec=
EmitTimezone= EmitTimezone=
DNS= DNS=
[NextHop]
Id=
Gateway=

View File

@ -0,0 +1,11 @@
[Match]
Name=veth99
[Network]
IPv6AcceptRA=no
Address=192.168.5.10/24
Gateway=192.168.5.1
[NextHop]
Id=1
Gateway=192.168.5.1

View File

@ -0,0 +1,7 @@
[Match]
Name=veth-peer
[Network]
IPv6AcceptRA=no
Address=2600::1/0
Address=192.168.5.1/24

View File

@ -113,6 +113,16 @@ def expectedFailureIfLinkFileFieldIsNotSet():
return f return f
def expectedFailureIfNexthopIsNotAvailable():
def f(func):
rc = call('ip nexthop list')
if rc == 0:
return func
else:
return unittest.expectedFailure(func)
return f
def setUpModule(): def setUpModule():
global running_units global running_units
@ -1402,7 +1412,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'dummy99', 'dummy99',
'gretun97', 'gretun97',
'ip6gretun97', 'ip6gretun97',
'test1' 'test1',
'veth99',
] ]
units = [ units = [
@ -1426,6 +1437,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'25-neighbor-ipv6.network', '25-neighbor-ipv6.network',
'25-neighbor-ip-dummy.network', '25-neighbor-ip-dummy.network',
'25-neighbor-ip.network', '25-neighbor-ip.network',
'25-nexthop.network',
'25-link-local-addressing-no.network', '25-link-local-addressing-no.network',
'25-link-local-addressing-yes.network', '25-link-local-addressing-yes.network',
'25-link-section-unmanaged.network', '25-link-section-unmanaged.network',
@ -1435,6 +1447,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'25-gateway-next-static.network', '25-gateway-next-static.network',
'25-sysctl-disable-ipv6.network', '25-sysctl-disable-ipv6.network',
'25-sysctl.network', '25-sysctl.network',
'25-veth-peer.network',
'25-veth.netdev',
'26-link-local-addressing-ipv6.network', '26-link-local-addressing-ipv6.network',
'configure-without-carrier.network', 'configure-without-carrier.network',
'routing-policy-rule-dummy98.network', 'routing-policy-rule-dummy98.network',
@ -1974,6 +1988,16 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98') self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98') self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
@expectedFailureIfNexthopIsNotAvailable()
def test_nexthop(self):
copy_unit_to_networkd_unit_path('25-nexthop.network', '25-veth.netdev', '25-veth-peer.network')
start_networkd()
self.wait_online(['veth99:routable', 'veth-peer:routable'])
output = check_output('ip nexthop list dev veth99')
print(output)
self.assertRegex(output, '192.168.5.1')
class NetworkdStateFileTests(unittest.TestCase, Utilities): class NetworkdStateFileTests(unittest.TestCase, Utilities):
links = [ links = [
'dummy98', 'dummy98',