Compare commits
19 Commits
349176ae6c
...
7d94934b9d
Author | SHA1 | Date |
---|---|---|
Yu Watanabe | 7d94934b9d | |
Yu Watanabe | 3adf85b72f | |
Jan Synacek | 4801d8afe2 | |
Yu Watanabe | 086bcf5dca | |
Yu Watanabe | 4abcdaa08c | |
Susant Sahani | c16c780804 | |
Yu Watanabe | 55d3fdcf5e | |
Yu Watanabe | c94f7809e3 | |
Yu Watanabe | 0486a7d15b | |
Yu Watanabe | 13f697b7b1 | |
Yu Watanabe | f4f6f2c740 | |
David Tardon | 9e385314b0 | |
David Tardon | 9b2aee4170 | |
Zbigniew Jędrzejewski-Szmek | 2cea199ec1 | |
Zbigniew Jędrzejewski-Szmek | 27e946a5b3 | |
Zbigniew Jędrzejewski-Szmek | 75193d4128 | |
Zbigniew Jędrzejewski-Szmek | c3784a7d78 | |
Zbigniew Jędrzejewski-Szmek | e0cfed4c59 | |
Zbigniew Jędrzejewski-Szmek | c362077087 |
|
@ -1150,6 +1150,29 @@
|
|||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</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>
|
||||
|
|
26
man/udev.xml
26
man/udev.xml
|
@ -236,6 +236,32 @@
|
|||
</listitem>
|
||||
</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>
|
||||
<term><varname>TAG</varname></term>
|
||||
<listitem>
|
||||
|
|
|
@ -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
|
|
@ -157,6 +157,13 @@ enum {
|
|||
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,
|
||||
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
|
||||
};
|
||||
|
@ -165,7 +172,7 @@ enum {
|
|||
#define RTM_NR_FAMILIES (RTM_NR_MSGTYPES >> 2)
|
||||
#define RTM_FAM(cmd) (((cmd) - RTM_BASE) >> 2)
|
||||
|
||||
/*
|
||||
/*
|
||||
Generic structure for encapsulation of optional route information.
|
||||
It is reminiscent of sockaddr, but with sa_family replaced
|
||||
with attribute type.
|
||||
|
@ -205,7 +212,7 @@ struct rtmsg {
|
|||
|
||||
unsigned char rtm_table; /* Routing table id */
|
||||
unsigned char rtm_protocol; /* Routing protocol; see below */
|
||||
unsigned char rtm_scope; /* See below */
|
||||
unsigned char rtm_scope; /* See below */
|
||||
unsigned char rtm_type; /* See below */
|
||||
|
||||
unsigned rtm_flags;
|
||||
|
@ -342,6 +349,7 @@ enum rtattr_type_t {
|
|||
RTA_IP_PROTO,
|
||||
RTA_SPORT,
|
||||
RTA_DPORT,
|
||||
RTA_NH_ID,
|
||||
__RTA_MAX
|
||||
};
|
||||
|
||||
|
@ -515,7 +523,7 @@ struct ifinfomsg {
|
|||
};
|
||||
|
||||
/********************************************************************
|
||||
* prefix information
|
||||
* prefix information
|
||||
****/
|
||||
|
||||
struct prefixmsg {
|
||||
|
@ -529,7 +537,7 @@ struct prefixmsg {
|
|||
unsigned char prefix_pad3;
|
||||
};
|
||||
|
||||
enum
|
||||
enum
|
||||
{
|
||||
PREFIX_UNSPEC,
|
||||
PREFIX_ADDRESS,
|
||||
|
@ -704,6 +712,8 @@ enum rtnetlink_groups {
|
|||
#define RTNLGRP_IPV4_MROUTE_R RTNLGRP_IPV4_MROUTE_R
|
||||
RTNLGRP_IPV6_MROUTE_R,
|
||||
#define RTNLGRP_IPV6_MROUTE_R RTNLGRP_IPV6_MROUTE_R
|
||||
RTNLGRP_NEXTHOP,
|
||||
#define RTNLGRP_NEXTHOP RTNLGRP_NEXTHOP
|
||||
__RTNLGRP_MAX
|
||||
};
|
||||
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
|
||||
|
|
|
@ -164,9 +164,7 @@ static int automount_verify(Automount *a) {
|
|||
int r;
|
||||
|
||||
assert(a);
|
||||
|
||||
if (UNIT(a)->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
assert(UNIT(a)->load_state == UNIT_LOADED);
|
||||
|
||||
if (path_equal(a->where, "/")) {
|
||||
log_unit_error(UNIT(a), "Cannot have an automount unit for the root directory. Refusing.");
|
||||
|
@ -201,6 +199,24 @@ static int automount_set_where(Automount *a) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int automount_add_extras(Automount *a) {
|
||||
int r;
|
||||
|
||||
r = automount_set_where(a);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = automount_add_trigger_dependencies(a);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = automount_add_mount_dependencies(a);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return automount_add_default_dependencies(a);
|
||||
}
|
||||
|
||||
static int automount_load(Unit *u) {
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
int r;
|
||||
|
@ -209,27 +225,16 @@ static int automount_load(Unit *u) {
|
|||
assert(u->load_state == UNIT_STUB);
|
||||
|
||||
/* Load a .automount file */
|
||||
r = unit_load_fragment_and_dropin(u);
|
||||
r = unit_load_fragment_and_dropin(u, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u->load_state == UNIT_LOADED) {
|
||||
r = automount_set_where(a);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (u->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
r = automount_add_trigger_dependencies(a);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = automount_add_mount_dependencies(a);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = automount_add_default_dependencies(a);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
r = automount_add_extras(a);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return automount_verify(a);
|
||||
}
|
||||
|
|
|
@ -970,10 +970,10 @@ static uint64_t cgroup_cpu_weight_to_shares(uint64_t weight) {
|
|||
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;
|
||||
|
||||
buf = cpu_set_to_range_string(&cpus);
|
||||
buf = cpu_set_to_range_string(cpus);
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
|
@ -1221,8 +1221,8 @@ static void cgroup_context_apply(
|
|||
}
|
||||
|
||||
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_mems, "cpuset.mems");
|
||||
cgroup_apply_unified_cpuset(u, &c->cpuset_cpus, "cpuset.cpus");
|
||||
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
|
||||
|
|
|
@ -116,7 +116,7 @@ static void device_done(Unit *u) {
|
|||
static int device_load(Unit *u) {
|
||||
int r;
|
||||
|
||||
r = unit_load_fragment_and_dropin_optional(u);
|
||||
r = unit_load_fragment_and_dropin(u, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
|
@ -513,9 +513,7 @@ static int mount_verify(Mount *m) {
|
|||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (UNIT(m)->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
assert(UNIT(m)->load_state == UNIT_LOADED);
|
||||
|
||||
if (!m->from_fragment && !m->from_proc_self_mountinfo && !UNIT(m)->perpetual)
|
||||
return -ENOENT;
|
||||
|
@ -606,11 +604,11 @@ static int mount_add_extras(Mount *m) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mount_load_root_mount(Unit *u) {
|
||||
static void mount_load_root_mount(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
if (!unit_has_name(u, SPECIAL_ROOT_MOUNT))
|
||||
return 0;
|
||||
return;
|
||||
|
||||
u->perpetual = true;
|
||||
u->default_dependencies = false;
|
||||
|
@ -621,39 +619,33 @@ static int mount_load_root_mount(Unit *u) {
|
|||
|
||||
if (!u->description)
|
||||
u->description = strdup("Root Mount");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mount_load(Unit *u) {
|
||||
Mount *m = MOUNT(u);
|
||||
int r, q, w;
|
||||
int r, q = 0;
|
||||
|
||||
assert(u);
|
||||
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)
|
||||
q = unit_load_fragment_and_dropin_optional(u);
|
||||
else
|
||||
q = unit_load_fragment_and_dropin(u);
|
||||
bool fragment_optional = m->from_proc_self_mountinfo || u->perpetual;
|
||||
r = unit_load_fragment_and_dropin(u, !fragment_optional);
|
||||
|
||||
/* 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
|
||||
* 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. */
|
||||
if (u->load_state == UNIT_LOADED || m->from_proc_self_mountinfo || u->perpetual)
|
||||
w = mount_add_extras(m);
|
||||
else
|
||||
w = 0;
|
||||
q = mount_add_extras(m);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (q < 0)
|
||||
return q;
|
||||
if (w < 0)
|
||||
return w;
|
||||
if (u->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
return mount_verify(m);
|
||||
}
|
||||
|
|
|
@ -284,9 +284,7 @@ static int path_add_mount_dependencies(Path *p) {
|
|||
|
||||
static int path_verify(Path *p) {
|
||||
assert(p);
|
||||
|
||||
if (UNIT(p)->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
assert(UNIT(p)->load_state == UNIT_LOADED);
|
||||
|
||||
if (!p->specs) {
|
||||
log_unit_error(UNIT(p), "Path unit lacks path setting. Refusing.");
|
||||
|
@ -333,6 +331,20 @@ static int path_add_trigger_dependencies(Path *p) {
|
|||
return unit_add_two_dependencies(UNIT(p), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT);
|
||||
}
|
||||
|
||||
static int path_add_extras(Path *p) {
|
||||
int r;
|
||||
|
||||
r = path_add_trigger_dependencies(p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = path_add_mount_dependencies(p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return path_add_default_dependencies(p);
|
||||
}
|
||||
|
||||
static int path_load(Unit *u) {
|
||||
Path *p = PATH(u);
|
||||
int r;
|
||||
|
@ -340,24 +352,16 @@ static int path_load(Unit *u) {
|
|||
assert(u);
|
||||
assert(u->load_state == UNIT_STUB);
|
||||
|
||||
r = unit_load_fragment_and_dropin(u);
|
||||
r = unit_load_fragment_and_dropin(u, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u->load_state == UNIT_LOADED) {
|
||||
if (u->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
r = path_add_trigger_dependencies(p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = path_add_mount_dependencies(p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = path_add_default_dependencies(p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
r = path_add_extras(p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return path_verify(p);
|
||||
}
|
||||
|
|
|
@ -125,9 +125,7 @@ static int scope_add_default_dependencies(Scope *s) {
|
|||
|
||||
static int scope_verify(Scope *s) {
|
||||
assert(s);
|
||||
|
||||
if (UNIT(s)->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
assert(UNIT(s)->load_state == UNIT_LOADED);
|
||||
|
||||
if (set_isempty(UNIT(s)->pids) &&
|
||||
!MANAGER_IS_RELOADING(UNIT(s)->manager) &&
|
||||
|
@ -162,6 +160,20 @@ static int scope_load_init_scope(Unit *u) {
|
|||
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) {
|
||||
Scope *s = SCOPE(u);
|
||||
int r;
|
||||
|
@ -176,23 +188,17 @@ static int scope_load(Unit *u) {
|
|||
r = scope_load_init_scope(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = unit_load_fragment_and_dropin_optional(u);
|
||||
|
||||
r = unit_load_fragment_and_dropin(u, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u->load_state == UNIT_LOADED) {
|
||||
r = unit_patch_contexts(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (u->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
r = unit_set_default_slice(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = scope_add_default_dependencies(s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
r = scope_add_extras(s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return scope_verify(s);
|
||||
}
|
||||
|
|
|
@ -548,9 +548,7 @@ static int service_arm_timer(Service *s, usec_t usec) {
|
|||
|
||||
static int service_verify(Service *s) {
|
||||
assert(s);
|
||||
|
||||
if (UNIT(s)->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
assert(UNIT(s)->load_state == UNIT_LOADED);
|
||||
|
||||
if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]
|
||||
&& UNIT(s)->success_action == EMERGENCY_ACTION_NONE) {
|
||||
|
@ -760,32 +758,17 @@ static int service_load(Unit *u) {
|
|||
Service *s = SERVICE(u);
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
/* Load a .service file */
|
||||
r = unit_load_fragment(u);
|
||||
r = unit_load_fragment_and_dropin(u, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Still nothing found? Then let's give up */
|
||||
if (u->load_state == UNIT_STUB)
|
||||
return -ENOENT;
|
||||
if (u->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
/* 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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
r = service_add_extras(s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return service_verify(s);
|
||||
}
|
||||
|
|
|
@ -91,9 +91,7 @@ static int slice_verify(Slice *s) {
|
|||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (UNIT(s)->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
assert(UNIT(s)->load_state == UNIT_LOADED);
|
||||
|
||||
if (!slice_name_is_valid(UNIT(s)->id)) {
|
||||
log_unit_error(UNIT(s), "Slice name %s is not valid. Refusing.", UNIT(s)->id);
|
||||
|
@ -170,25 +168,25 @@ static int slice_load(Unit *u) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_load_fragment_and_dropin_optional(u);
|
||||
r = unit_load_fragment_and_dropin(u, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
/* This is a new unit? Then let's add in some extras */
|
||||
if (u->load_state == UNIT_LOADED) {
|
||||
r = unit_patch_contexts(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_patch_contexts(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = slice_add_parent_slice(s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = slice_add_parent_slice(s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = slice_add_default_dependencies(s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
r = slice_add_default_dependencies(s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return slice_verify(s);
|
||||
}
|
||||
|
|
|
@ -433,9 +433,7 @@ static const char *socket_find_symlink_target(Socket *s) {
|
|||
|
||||
static int socket_verify(Socket *s) {
|
||||
assert(s);
|
||||
|
||||
if (UNIT(s)->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
assert(UNIT(s)->load_state == UNIT_LOADED);
|
||||
|
||||
if (!s->ports) {
|
||||
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)
|
||||
return r;
|
||||
|
||||
r = unit_load_fragment_and_dropin(u);
|
||||
r = unit_load_fragment_and_dropin(u, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u->load_state == UNIT_LOADED) {
|
||||
/* This is a new unit? Then let's add in some extras */
|
||||
r = socket_add_extras(s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
if (u->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
/* This is a new unit? Then let's add in some extras */
|
||||
r = socket_add_extras(s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return socket_verify(s);
|
||||
}
|
||||
|
|
|
@ -232,8 +232,7 @@ static int swap_verify(Swap *s) {
|
|||
_cleanup_free_ char *e = NULL;
|
||||
int r;
|
||||
|
||||
if (UNIT(s)->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
assert(UNIT(s)->load_state == UNIT_LOADED);
|
||||
|
||||
r = unit_name_from_path(s->what, ".swap", &e);
|
||||
if (r < 0)
|
||||
|
@ -340,28 +339,26 @@ static int swap_add_extras(Swap *s) {
|
|||
|
||||
static int swap_load(Unit *u) {
|
||||
Swap *s = SWAP(u);
|
||||
int r, q;
|
||||
int r, q = 0;
|
||||
|
||||
assert(s);
|
||||
assert(u->load_state == UNIT_STUB);
|
||||
|
||||
/* Load a .swap file */
|
||||
if (SWAP(u)->from_proc_swaps)
|
||||
r = unit_load_fragment_and_dropin_optional(u);
|
||||
else
|
||||
r = unit_load_fragment_and_dropin(u);
|
||||
bool fragment_optional = s->from_proc_swaps;
|
||||
r = unit_load_fragment_and_dropin(u, !fragment_optional);
|
||||
|
||||
/* Add in some extras, and do so either when we successfully loaded something or when /proc/swaps is already
|
||||
* active. */
|
||||
/* Add in some extras, and do so either when we successfully loaded something or when /proc/swaps is
|
||||
* already active. */
|
||||
if (u->load_state == UNIT_LOADED || s->from_proc_swaps)
|
||||
q = swap_add_extras(s);
|
||||
else
|
||||
q = 0;
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (q < 0)
|
||||
return q;
|
||||
if (u->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
return swap_verify(s);
|
||||
}
|
||||
|
|
|
@ -80,18 +80,15 @@ static int target_load(Unit *u) {
|
|||
|
||||
assert(t);
|
||||
|
||||
r = unit_load_fragment_and_dropin(u);
|
||||
r = unit_load_fragment_and_dropin(u, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* This is a new unit? Then let's add in some extras */
|
||||
if (u->load_state == UNIT_LOADED) {
|
||||
r = target_add_default_dependencies(t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
if (u->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
/* This is a new unit? Then let's add in some extras */
|
||||
return target_add_default_dependencies(t);
|
||||
}
|
||||
|
||||
static int target_coldplug(Unit *u) {
|
||||
|
|
|
@ -73,9 +73,7 @@ static void timer_done(Unit *u) {
|
|||
|
||||
static int timer_verify(Timer *t) {
|
||||
assert(t);
|
||||
|
||||
if (UNIT(t)->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
assert(UNIT(t)->load_state == UNIT_LOADED);
|
||||
|
||||
if (!t->values && !t->on_clock_change && !t->on_timezone_change) {
|
||||
log_unit_error(UNIT(t), "Timer unit lacks value setting. Refusing.");
|
||||
|
@ -178,24 +176,25 @@ static int timer_load(Unit *u) {
|
|||
assert(u);
|
||||
assert(u->load_state == UNIT_STUB);
|
||||
|
||||
r = unit_load_fragment_and_dropin(u);
|
||||
r = unit_load_fragment_and_dropin(u, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u->load_state == UNIT_LOADED) {
|
||||
if (u->load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
r = timer_add_trigger_dependencies(t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
/* This is a new unit? Then let's add in some extras */
|
||||
r = timer_add_trigger_dependencies(t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = timer_setup_persistent(t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = timer_setup_persistent(t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = timer_add_default_dependencies(t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
r = timer_add_default_dependencies(t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return timer_verify(t);
|
||||
}
|
||||
|
|
|
@ -1361,7 +1361,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
|||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
assert(u);
|
||||
|
@ -1371,8 +1371,12 @@ int unit_load_fragment_and_dropin(Unit *u) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u->load_state == UNIT_STUB)
|
||||
return -ENOENT;
|
||||
if (u->load_state == UNIT_STUB) {
|
||||
if (fragment_required)
|
||||
return -ENOENT;
|
||||
|
||||
u->load_state = UNIT_LOADED;
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
@ -1381,27 +1385,6 @@ int unit_load_fragment_and_dropin(Unit *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) {
|
||||
Manager *m = u->manager;
|
||||
|
||||
|
@ -1559,16 +1542,11 @@ int unit_load(Unit *u) {
|
|||
u->fragment_mtime = now(CLOCK_REALTIME);
|
||||
}
|
||||
|
||||
if (UNIT_VTABLE(u)->load) {
|
||||
r = UNIT_VTABLE(u)->load(u);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (u->load_state == UNIT_STUB) {
|
||||
r = -ENOENT;
|
||||
r = UNIT_VTABLE(u)->load(u);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
assert(u->load_state != UNIT_STUB);
|
||||
|
||||
if (u->load_state == UNIT_LOADED) {
|
||||
unit_add_to_target_deps_queue(u);
|
||||
|
|
|
@ -670,8 +670,7 @@ int unit_merge_by_name(Unit *u, const char *other);
|
|||
|
||||
Unit *unit_follow_merge(Unit *u) _pure_;
|
||||
|
||||
int unit_load_fragment_and_dropin(Unit *u);
|
||||
int unit_load_fragment_and_dropin_optional(Unit *u);
|
||||
int unit_load_fragment_and_dropin(Unit *u, bool fragment_required);
|
||||
int unit_load(Unit *unit);
|
||||
|
||||
int unit_set_slice(Unit *u, Unit *slice);
|
||||
|
|
|
@ -88,7 +88,8 @@ int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) {
|
|||
assert_return(m, -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);
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/if_link.h>
|
||||
#include <linux/if_macsec.h>
|
||||
#include <linux/if_tunnel.h>
|
||||
#include <linux/nexthop.h>
|
||||
#include <linux/l2tp.h>
|
||||
#include <linux/veth.h>
|
||||
#include <linux/wireguard.h>
|
||||
|
@ -716,6 +717,17 @@ static const NLTypeSystem rtnl_routing_policy_rule_type_system = {
|
|||
.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[] = {
|
||||
[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) },
|
||||
|
@ -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_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_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 = {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include "sd-netlink.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);
|
||||
}
|
||||
|
||||
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) {
|
||||
return IN_SET(type, RTM_NEWLINK, RTM_SETLINK, RTM_GETLINK, RTM_DELLINK);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <netinet/in.h>
|
||||
#include <linux/if_addrlabel.h>
|
||||
#include <linux/nexthop.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
@ -285,6 +286,70 @@ int sd_rtnl_message_new_route(sd_netlink *rtnl, sd_netlink_message **ret,
|
|||
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) {
|
||||
struct ndmsg *ndm;
|
||||
|
||||
|
@ -713,6 +778,14 @@ int sd_rtnl_message_get_family(sd_netlink_message *m, int *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;
|
||||
}
|
||||
|
||||
|
|
|
@ -896,6 +896,13 @@ int sd_netlink_add_match(
|
|||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
case RTM_NEWNEXTHOP:
|
||||
case RTM_DELNEXTHOP:
|
||||
r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEXTHOP);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
|
|
@ -93,6 +93,8 @@ sources = files('''
|
|||
networkd-network-bus.h
|
||||
networkd-network.c
|
||||
networkd-network.h
|
||||
networkd-nexthop.c
|
||||
networkd-nexthop.h
|
||||
networkd-route.c
|
||||
networkd-route.h
|
||||
networkd-routing-policy-rule.c
|
||||
|
|
|
@ -672,6 +672,9 @@ static Link *link_free(Link *link) {
|
|||
link->routes = set_free_with_destructor(link->routes, 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_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;
|
||||
}
|
||||
|
||||
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) {
|
||||
int r;
|
||||
|
||||
|
@ -948,6 +1003,7 @@ int link_request_set_routes(Link *link) {
|
|||
assert(link->state != _LINK_STATE_INVALID);
|
||||
|
||||
link->static_routes_configured = false;
|
||||
link->static_routes_ready = false;
|
||||
|
||||
if (!link_has_carrier(link) && !link->network->configure_without_carrier)
|
||||
/* 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)
|
||||
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)
|
||||
return;
|
||||
|
||||
|
@ -1134,6 +1201,8 @@ static int link_request_set_addresses(Link *link) {
|
|||
link->addresses_ready = false;
|
||||
link->neighbors_configured = false;
|
||||
link->static_routes_configured = false;
|
||||
link->static_routes_ready = false;
|
||||
link->static_nexthops_configured = false;
|
||||
link->routing_policy_rules_configured = false;
|
||||
|
||||
r = link_set_bridge_fdb(link);
|
||||
|
|
|
@ -69,6 +69,7 @@ typedef struct Link {
|
|||
unsigned address_label_messages;
|
||||
unsigned neighbor_messages;
|
||||
unsigned route_messages;
|
||||
unsigned nexthop_messages;
|
||||
unsigned routing_policy_rule_messages;
|
||||
unsigned routing_policy_rule_remove_messages;
|
||||
unsigned enslaving;
|
||||
|
@ -79,9 +80,8 @@ typedef struct Link {
|
|||
Set *neighbors_foreign;
|
||||
Set *routes;
|
||||
Set *routes_foreign;
|
||||
|
||||
bool addresses_configured;
|
||||
bool addresses_ready;
|
||||
Set *nexthops;
|
||||
Set *nexthops_foreign;
|
||||
|
||||
sd_dhcp_client *dhcp_client;
|
||||
sd_dhcp_lease *dhcp_lease, *dhcp_lease_old;
|
||||
|
@ -100,8 +100,12 @@ typedef struct Link {
|
|||
sd_ipv4ll *ipv4ll;
|
||||
bool ipv4ll_address:1;
|
||||
|
||||
bool addresses_configured:1;
|
||||
bool addresses_ready:1;
|
||||
bool neighbors_configured:1;
|
||||
bool static_routes_configured:1;
|
||||
bool static_routes_ready:1;
|
||||
bool static_nexthops_configured:1;
|
||||
bool routing_policy_rules_configured: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_ipv6_accept_ra_route_table(Link *link);
|
||||
int link_request_set_routes(Link *link);
|
||||
int link_request_set_nexthop(Link *link);
|
||||
|
||||
#define ADDRESS_FMT_VAL(address) \
|
||||
be32toh((address).s_addr) >> 24, \
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <unistd.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/fib_rules.h>
|
||||
#include <linux/nexthop.h>
|
||||
|
||||
#include "sd-daemon.h"
|
||||
#include "sd-netlink.h"
|
||||
|
@ -1153,6 +1154,118 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
|
|||
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) {
|
||||
int n, fd, rtnl_fd = -EINVAL;
|
||||
|
||||
|
@ -1253,6 +1366,14 @@ static int manager_connect_rtnl(Manager *m) {
|
|||
if (r < 0)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1931,6 +2052,47 @@ int manager_rtnl_enumerate_rules(Manager *m) {
|
|||
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) {
|
||||
AddressPool *p;
|
||||
int r;
|
||||
|
|
|
@ -83,11 +83,13 @@ int manager_rtnl_enumerate_addresses(Manager *m);
|
|||
int manager_rtnl_enumerate_neighbors(Manager *m);
|
||||
int manager_rtnl_enumerate_routes(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_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_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);
|
||||
|
||||
|
|
|
@ -546,6 +546,7 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
|
|||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
assert(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) {
|
||||
|
||||
case SD_NDISC_OPTION_PREFIX_INFORMATION: {
|
||||
union in_addr_union a;
|
||||
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);
|
||||
if (r < 0)
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
uint64_t flags;
|
||||
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_options(link, rt);
|
||||
}
|
||||
(void) ndisc_router_process_default(link, rt);
|
||||
(void) ndisc_router_process_options(link, rt);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -141,6 +141,8 @@ Route.InitialAdvertisedReceiveWindow, config_parse_tcp_window,
|
|||
Route.QuickAck, config_parse_quickack, 0, 0
|
||||
Route.FastOpenNoCookie, config_parse_fast_open_no_cookie, 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.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
|
||||
DHCPv4.RoutesToDNS, config_parse_bool, 0, offsetof(Network, dhcp_routes_to_dns)
|
||||
|
|
|
@ -151,6 +151,7 @@ int network_verify(Network *network) {
|
|||
AddressLabel *label, *label_next;
|
||||
Prefix *prefix, *prefix_next;
|
||||
RoutingPolicyRule *rule, *rule_next;
|
||||
NextHop *nexthop, *nextnop_next;
|
||||
|
||||
assert(network);
|
||||
assert(network->filename);
|
||||
|
@ -282,6 +283,10 @@ int network_verify(Network *network) {
|
|||
if (route_section_verify(route, network) < 0)
|
||||
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)
|
||||
if (section_is_invalid(fdb->section))
|
||||
fdb_entry_free(fdb);
|
||||
|
@ -453,8 +458,9 @@ int network_load_one(Manager *manager, const char *filename) {
|
|||
"IPv6AddressLabel\0"
|
||||
"RoutingPolicyRule\0"
|
||||
"Route\0"
|
||||
"DHCP\0"
|
||||
"DHCPv4\0" /* compat */
|
||||
"NextHop\0"
|
||||
"DHCP\0" /* compat */
|
||||
"DHCPv4\0"
|
||||
"DHCPv6\0"
|
||||
"DHCPServer\0"
|
||||
"IPv6AcceptRA\0"
|
||||
|
@ -525,8 +531,9 @@ static Network *network_free(Network *network) {
|
|||
FdbEntry *fdb_entry;
|
||||
Neighbor *neighbor;
|
||||
AddressLabel *label;
|
||||
Prefix *prefix;
|
||||
Address *address;
|
||||
NextHop *nexthop;
|
||||
Prefix *prefix;
|
||||
Route *route;
|
||||
|
||||
if (!network)
|
||||
|
@ -573,6 +580,9 @@ static Network *network_free(Network *network) {
|
|||
while ((route = network->static_routes))
|
||||
route_free(route);
|
||||
|
||||
while ((nexthop = network->static_nexthops))
|
||||
nexthop_free(nexthop);
|
||||
|
||||
while ((address = network->static_addresses))
|
||||
address_free(address);
|
||||
|
||||
|
@ -596,6 +606,7 @@ static Network *network_free(Network *network) {
|
|||
|
||||
hashmap_free(network->addresses_by_section);
|
||||
hashmap_free(network->routes_by_section);
|
||||
hashmap_free(network->nexthops_by_section);
|
||||
hashmap_free(network->fdb_entries_by_section);
|
||||
hashmap_free(network->neighbors_by_section);
|
||||
hashmap_free(network->address_labels_by_section);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "networkd-lldp-rx.h"
|
||||
#include "networkd-lldp-tx.h"
|
||||
#include "networkd-neighbor.h"
|
||||
#include "networkd-nexthop.h"
|
||||
#include "networkd-radv.h"
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-routing-policy-rule.h"
|
||||
|
@ -228,6 +229,7 @@ struct Network {
|
|||
|
||||
LIST_HEAD(Address, static_addresses);
|
||||
LIST_HEAD(Route, static_routes);
|
||||
LIST_HEAD(NextHop, static_nexthops);
|
||||
LIST_HEAD(FdbEntry, static_fdb_entries);
|
||||
LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
|
||||
LIST_HEAD(Neighbor, neighbors);
|
||||
|
@ -238,6 +240,7 @@ struct Network {
|
|||
|
||||
unsigned n_static_addresses;
|
||||
unsigned n_static_routes;
|
||||
unsigned n_static_nexthops;
|
||||
unsigned n_static_fdb_entries;
|
||||
unsigned n_ipv6_proxy_ndp_addresses;
|
||||
unsigned n_neighbors;
|
||||
|
@ -248,6 +251,7 @@ struct Network {
|
|||
|
||||
Hashmap *addresses_by_section;
|
||||
Hashmap *routes_by_section;
|
||||
Hashmap *nexthops_by_section;
|
||||
Hashmap *fdb_entries_by_section;
|
||||
Hashmap *neighbors_by_section;
|
||||
Hashmap *address_labels_by_section;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -107,6 +107,10 @@ static int run(int argc, char *argv[]) {
|
|||
if (r < 0)
|
||||
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);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not start manager: %m");
|
||||
|
|
|
@ -419,21 +419,17 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
|
|||
int r;
|
||||
|
||||
if (STR_IN_SET(field, "DevicePolicy", "Slice"))
|
||||
|
||||
return bus_append_string(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field,
|
||||
"CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
|
||||
"TasksAccounting", "IPAccounting"))
|
||||
|
||||
return bus_append_parse_boolean(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight"))
|
||||
|
||||
return bus_append_cg_weight_parse(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "CPUShares", "StartupCPUShares"))
|
||||
|
||||
return bus_append_cg_cpu_shares_parse(m, field, eq);
|
||||
|
||||
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"))
|
||||
|
||||
return bus_append_cg_blkio_weight_parse(m, field, eq);
|
||||
|
||||
if (streq(field, "DisableControllers"))
|
||||
|
||||
return bus_append_strv(m, "DisableControllers", eq, EXTRACT_UNQUOTE);
|
||||
|
||||
if (streq(field, "Delegate")) {
|
||||
|
||||
r = parse_boolean(eq);
|
||||
if (r < 0)
|
||||
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;
|
||||
}
|
||||
|
||||
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")) {
|
||||
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 (isempty(eq))
|
||||
r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
|
||||
else {
|
||||
|
@ -540,7 +540,6 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
|
|||
}
|
||||
|
||||
if (streq(field, "DeviceAllow")) {
|
||||
|
||||
if (isempty(eq))
|
||||
r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
|
||||
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 (isempty(eq))
|
||||
r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
|
||||
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 (isempty(eq))
|
||||
r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
|
||||
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) {
|
||||
|
||||
if (streq(field, "Where"))
|
||||
|
||||
return bus_append_string(m, field, eq);
|
||||
|
||||
if (streq(field, "DirectoryMode"))
|
||||
|
||||
return bus_append_parse_mode(m, field, eq);
|
||||
|
||||
if (streq(field, "TimeoutIdleSec"))
|
||||
|
||||
return bus_append_parse_sec_rename(m, field, eq);
|
||||
|
||||
return 0;
|
||||
|
@ -818,7 +811,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
|
|||
"WorkingDirectory", "RootDirectory", "SyslogIdentifier",
|
||||
"ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage",
|
||||
"RuntimeDirectoryPreserve", "Personality", "KeyringMode", "NetworkNamespacePath"))
|
||||
|
||||
return bus_append_string(m, field, eq);
|
||||
|
||||
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",
|
||||
"ProtectControlGroups", "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality",
|
||||
"ProtectHostname", "RestrictSUIDSGID"))
|
||||
|
||||
return bus_append_parse_boolean(m, field, eq);
|
||||
|
||||
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",
|
||||
"RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory",
|
||||
"SupplementaryGroups", "SystemCallArchitectures"))
|
||||
|
||||
return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
|
||||
|
||||
if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax"))
|
||||
|
||||
return bus_append_log_level_from_string(m, field, eq);
|
||||
|
||||
if (streq(field, "SyslogFacility"))
|
||||
|
||||
return bus_append_log_facility_unshifted_from_string(m, field, eq);
|
||||
|
||||
if (streq(field, "SecureBits"))
|
||||
|
||||
return bus_append_secure_bits_from_string(m, field, eq);
|
||||
|
||||
if (streq(field, "CPUSchedulingPolicy"))
|
||||
|
||||
return bus_append_sched_policy_from_string(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "CPUSchedulingPriority", "OOMScoreAdjust"))
|
||||
|
||||
return bus_append_safe_atoi(m, field, eq);
|
||||
|
||||
if (streq(field, "Nice"))
|
||||
|
||||
return bus_append_parse_nice(m, field, eq);
|
||||
|
||||
if (streq(field, "SystemCallErrorNumber"))
|
||||
|
||||
return bus_append_parse_errno(m, field, eq);
|
||||
|
||||
if (streq(field, "IOSchedulingClass"))
|
||||
|
||||
return bus_append_ioprio_class_from_string(m, field, eq);
|
||||
|
||||
if (streq(field, "IOSchedulingPriority"))
|
||||
|
||||
return bus_append_ioprio_parse_priority(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field,
|
||||
"RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode",
|
||||
"LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask"))
|
||||
|
||||
return bus_append_parse_mode(m, field, eq);
|
||||
|
||||
if (streq(field, "TimerSlackNSec"))
|
||||
|
||||
return bus_append_parse_nsec(m, field, eq);
|
||||
|
||||
if (streq(field, "LogRateLimitIntervalSec"))
|
||||
|
||||
return bus_append_parse_sec_rename(m, field, eq);
|
||||
|
||||
if (streq(field, "LogRateLimitBurst"))
|
||||
|
||||
return bus_append_safe_atou(m, field, eq);
|
||||
|
||||
if (streq(field, "MountFlags"))
|
||||
|
||||
return bus_append_mount_propagation_flags_from_string(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment"))
|
||||
|
||||
return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
|
||||
|
||||
if (streq(field, "EnvironmentFile")) {
|
||||
|
||||
if (isempty(eq))
|
||||
r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0);
|
||||
else
|
||||
|
@ -916,7 +890,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
|
|||
}
|
||||
|
||||
if (streq(field, "LogExtraFields")) {
|
||||
|
||||
r = sd_bus_message_open_container(m, 'r', "sv");
|
||||
if (r < 0)
|
||||
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) {
|
||||
|
||||
if (streq(field, "KillMode"))
|
||||
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) {
|
||||
|
||||
if (STR_IN_SET(field, "What", "Where", "Options", "Type"))
|
||||
|
||||
return bus_append_string(m, field, eq);
|
||||
|
||||
if (streq(field, "TimeoutSec"))
|
||||
|
||||
return bus_append_parse_sec_rename(m, field, eq);
|
||||
|
||||
if (streq(field, "DirectoryMode"))
|
||||
|
||||
return bus_append_parse_mode(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "SloppyOptions", "LazyUnmount", "ForceUnmount"))
|
||||
|
||||
return bus_append_parse_boolean(m, field, eq);
|
||||
|
||||
return 0;
|
||||
|
@ -1392,17 +1360,14 @@ static int bus_append_path_property(sd_bus_message *m, const char *field, const
|
|||
int r;
|
||||
|
||||
if (streq(field, "MakeDirectory"))
|
||||
|
||||
return bus_append_parse_boolean(m, field, eq);
|
||||
|
||||
if (streq(field, "DirectoryMode"))
|
||||
|
||||
return bus_append_parse_mode(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field,
|
||||
"PathExists", "PathExistsGlob", "PathChanged",
|
||||
"PathModified", "DirectoryNotEmpty")) {
|
||||
|
||||
if (isempty(eq))
|
||||
r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0);
|
||||
else
|
||||
|
@ -1422,19 +1387,15 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
|
|||
if (STR_IN_SET(field,
|
||||
"PIDFile", "Type", "Restart", "BusName", "NotifyAccess",
|
||||
"USBFunctionDescriptors", "USBFunctionStrings", "OOMPolicy"))
|
||||
|
||||
return bus_append_string(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "PermissionsStartOnly", "RootDirectoryStartOnly", "RemainAfterExit", "GuessMainPID"))
|
||||
|
||||
return bus_append_parse_boolean(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "RestartSec", "TimeoutStartSec", "TimeoutStopSec", "RuntimeMaxSec", "WatchdogSec"))
|
||||
|
||||
return bus_append_parse_sec_rename(m, field, eq);
|
||||
|
||||
if (streq(field, "TimeoutSec")) {
|
||||
|
||||
r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -1443,7 +1404,6 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
|
|||
}
|
||||
|
||||
if (streq(field, "FileDescriptorStoreMax"))
|
||||
|
||||
return bus_append_safe_atou(m, field, eq);
|
||||
|
||||
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,
|
||||
"Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
|
||||
"PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet"))
|
||||
|
||||
return bus_append_parse_boolean(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "Priority", "IPTTL", "Mark"))
|
||||
|
||||
return bus_append_safe_atoi(m, field, eq);
|
||||
|
||||
if (streq(field, "IPTOS"))
|
||||
|
||||
return bus_append_ip_tos_from_string(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst"))
|
||||
|
||||
return bus_append_safe_atou(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "SocketMode", "DirectoryMode"))
|
||||
|
||||
return bus_append_parse_mode(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "MessageQueueMaxMessages", "MessageQueueMessageSize"))
|
||||
|
||||
return bus_append_safe_atoi64(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec"))
|
||||
|
||||
return bus_append_parse_sec_rename(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "ReceiveBuffer", "SendBuffer", "PipeSize"))
|
||||
|
||||
return bus_append_parse_size(m, field, eq, 1024);
|
||||
|
||||
if (STR_IN_SET(field, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost"))
|
||||
|
||||
return bus_append_exec_command(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field,
|
||||
"SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion",
|
||||
"BindToDevice", "BindIPv6Only", "FileDescriptorName",
|
||||
"SocketUser", "SocketGroup"))
|
||||
|
||||
return bus_append_string(m, field, eq);
|
||||
|
||||
if (streq(field, "Symlinks"))
|
||||
|
||||
return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
|
||||
|
||||
if (streq(field, "SocketProtocol"))
|
||||
|
||||
return bus_append_parse_ip_protocol(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field,
|
||||
"ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
|
||||
"ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) {
|
||||
|
||||
if (isempty(eq))
|
||||
r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
|
||||
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",
|
||||
"OnTimezoneChange", "OnClockChange"))
|
||||
|
||||
return bus_append_parse_boolean(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec"))
|
||||
|
||||
return bus_append_parse_sec_rename(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field,
|
||||
"OnActiveSec", "OnBootSec", "OnStartupSec",
|
||||
"OnUnitActiveSec","OnUnitInactiveSec")) {
|
||||
|
||||
if (isempty(eq))
|
||||
r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0);
|
||||
else {
|
||||
|
@ -1642,7 +1586,6 @@ static int bus_append_timer_property(sd_bus_message *m, const char *field, const
|
|||
}
|
||||
|
||||
if (streq(field, "OnCalendar")) {
|
||||
|
||||
if (isempty(eq))
|
||||
r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
|
||||
else
|
||||
|
@ -1666,25 +1609,20 @@ static int bus_append_unit_property(sd_bus_message *m, const char *field, const
|
|||
"JobTimeoutAction", "JobTimeoutRebootArgument",
|
||||
"StartLimitAction", "FailureAction", "SuccessAction",
|
||||
"RebootArgument", "CollectMode"))
|
||||
|
||||
return bus_append_string(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field,
|
||||
"StopWhenUnneeded", "RefuseManualStart", "RefuseManualStop",
|
||||
"AllowIsolate", "IgnoreOnIsolate", "DefaultDependencies"))
|
||||
|
||||
return bus_append_parse_boolean(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "JobTimeoutSec", "JobRunningTimeoutSec", "StartLimitIntervalSec"))
|
||||
|
||||
return bus_append_parse_sec_rename(m, field, eq);
|
||||
|
||||
if (streq(field, "StartLimitBurst"))
|
||||
|
||||
return bus_append_safe_atou(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "SuccessActionExitStatus", "FailureActionExitStatus")) {
|
||||
|
||||
if (isempty(eq))
|
||||
r = sd_bus_message_append(m, "(sv)", field, "i", -1);
|
||||
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 ||
|
||||
STR_IN_SET(field, "Documentation", "RequiresMountsFor"))
|
||||
|
||||
return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
|
||||
|
||||
t = condition_type_from_string(field);
|
||||
|
|
|
@ -7977,7 +7977,7 @@ static void help_states(void) {
|
|||
|
||||
static int help_boot_loader_entry(void) {
|
||||
_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;
|
||||
char **i;
|
||||
int r;
|
||||
|
|
|
@ -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_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_state(sd_netlink_message *m, uint16_t state);
|
||||
int sd_rtnl_message_neigh_get_family(sd_netlink_message *m, int *family);
|
||||
|
|
|
@ -425,9 +425,10 @@ int main(int argc, char *argv[]) {
|
|||
export = 1;
|
||||
break;
|
||||
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"
|
||||
" -h,--help print this help text\n\n");
|
||||
" -h,--help print this help text\n\n",
|
||||
program_invocation_short_name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -849,12 +849,13 @@ int main(int argc, char *argv[]) {
|
|||
log_open();
|
||||
break;
|
||||
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"
|
||||
" -u,--unlock-media unlock the media\n"
|
||||
" -e,--eject-media eject the media\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;
|
||||
default:
|
||||
rc = 1;
|
||||
|
|
|
@ -44,7 +44,7 @@ static int run(int argc, char **argv) {
|
|||
log_open();
|
||||
|
||||
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) {
|
||||
r = device_new_from_strv(&device, environ);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <ctype.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "architecture.h"
|
||||
#include "conf-files.h"
|
||||
#include "def.h"
|
||||
#include "device-util.h"
|
||||
|
@ -28,6 +29,7 @@
|
|||
#include "udev-event.h"
|
||||
#include "udev-rules.h"
|
||||
#include "user-util.h"
|
||||
#include "virt.h"
|
||||
|
||||
#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_NAME, /* string, name of network interface */
|
||||
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_SUBSYSTEM, /* string, sd_device_get_subsystem() */
|
||||
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);
|
||||
} else
|
||||
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")) {
|
||||
if (attr)
|
||||
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);
|
||||
|
||||
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_PARENTS_TAG:
|
||||
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) {
|
||||
UdevRuleToken *token;
|
||||
_cleanup_free_ char **tags = NULL;
|
||||
_cleanup_strv_free_ char **tags = NULL;
|
||||
uid_t uid = UID_INVALID;
|
||||
gid_t gid = GID_INVALID;
|
||||
mode_t mode = MODE_INVALID;
|
||||
|
|
|
@ -255,3 +255,6 @@ MaxLeaseTimeSec=
|
|||
DefaultLeaseTimeSec=
|
||||
EmitTimezone=
|
||||
DNS=
|
||||
[NextHop]
|
||||
Id=
|
||||
Gateway=
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,7 @@
|
|||
[Match]
|
||||
Name=veth-peer
|
||||
|
||||
[Network]
|
||||
IPv6AcceptRA=no
|
||||
Address=2600::1/0
|
||||
Address=192.168.5.1/24
|
|
@ -113,6 +113,16 @@ def expectedFailureIfLinkFileFieldIsNotSet():
|
|||
|
||||
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():
|
||||
global running_units
|
||||
|
||||
|
@ -1402,7 +1412,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
|||
'dummy99',
|
||||
'gretun97',
|
||||
'ip6gretun97',
|
||||
'test1'
|
||||
'test1',
|
||||
'veth99',
|
||||
]
|
||||
|
||||
units = [
|
||||
|
@ -1426,6 +1437,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
|||
'25-neighbor-ipv6.network',
|
||||
'25-neighbor-ip-dummy.network',
|
||||
'25-neighbor-ip.network',
|
||||
'25-nexthop.network',
|
||||
'25-link-local-addressing-no.network',
|
||||
'25-link-local-addressing-yes.network',
|
||||
'25-link-section-unmanaged.network',
|
||||
|
@ -1435,6 +1447,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
|||
'25-gateway-next-static.network',
|
||||
'25-sysctl-disable-ipv6.network',
|
||||
'25-sysctl.network',
|
||||
'25-veth-peer.network',
|
||||
'25-veth.netdev',
|
||||
'26-link-local-addressing-ipv6.network',
|
||||
'configure-without-carrier.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.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):
|
||||
links = [
|
||||
'dummy98',
|
||||
|
|
Loading…
Reference in New Issue