sensor:modalias:acpi:KIOX0009*:dmi:*:svnAcer:pnOneS1003:*
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
+sensor:modalias:acpi:BOSC0200*:dmi:*:svnAcer*:pnSwitchSW312-31:*
+ ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
#########################################
# Archos
</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>
</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>
--- /dev/null
+/* 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
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)
};
#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.
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;
RTA_IP_PROTO,
RTA_SPORT,
RTA_DPORT,
+ RTA_NH_ID,
__RTA_MAX
};
};
/********************************************************************
- * prefix information
+ * prefix information
****/
struct prefixmsg {
unsigned char prefix_pad3;
};
-enum
+enum
{
PREFIX_UNSPEC,
PREFIX_ADDRESS,
#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)
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.");
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;
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;
-
- r = automount_add_trigger_dependencies(a);
- if (r < 0)
- return r;
-
- r = automount_add_mount_dependencies(a);
- if (r < 0)
- return r;
+ if (u->load_state != UNIT_LOADED)
+ return 0;
- 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);
}
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;
}
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
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;
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;
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;
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);
}
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.");
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;
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) {
-
- r = path_add_trigger_dependencies(p);
- if (r < 0)
- return r;
-
- r = path_add_mount_dependencies(p);
- if (r < 0)
- return r;
+ if (u->load_state != UNIT_LOADED)
+ return 0;
- 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);
}
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) &&
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;
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;
-
- r = unit_set_default_slice(u);
- if (r < 0)
- return r;
+ if (u->load_state != UNIT_LOADED)
+ return 0;
- 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);
}
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) {
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);
}
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);
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;
- /* 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;
- r = unit_patch_contexts(u);
- if (r < 0)
- return r;
+ /* This is a new unit? Then let's add in some extras */
+ 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);
}
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.");
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);
}
_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)
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);
}
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) {
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.");
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);
}
}
/* 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);
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
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;
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);
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);
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);
#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>
.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) },
[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 = {
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <linux/rtnetlink.h>
+
#include "sd-netlink.h"
#include "in-addr-util.h"
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);
}
#include <netinet/in.h>
#include <linux/if_addrlabel.h>
+#include <linux/nexthop.h>
#include <stdbool.h>
#include <unistd.h>
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;
*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;
}
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;
}
return s->vtfd;
}
-int session_prepare_vt(Session *s) {
+static int session_prepare_vt(Session *s) {
int vt, r;
struct vt_mode mode = {};
const char* tty_validity_to_string(TTYValidity t) _const_;
TTYValidity tty_validity_from_string(const char *s) _pure_;
-int session_prepare_vt(Session *s);
void session_leave_vt(Session *s);
bool session_is_controller(Session *s, const char *sender);
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
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);
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;
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
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;
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);
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;
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;
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;
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, \
#include <unistd.h>
#include <linux/if.h>
#include <linux/fib_rules.h>
+#include <linux/nexthop.h>
#include "sd-daemon.h"
#include "sd-netlink.h"
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;
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;
}
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;
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);
int r;
assert(link);
+ assert(link->network);
assert(rt);
r = sd_ndisc_router_option_rewind(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");
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;
}
}
- 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;
}
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)
AddressLabel *label, *label_next;
Prefix *prefix, *prefix_next;
RoutingPolicyRule *rule, *rule_next;
+ NextHop *nexthop, *nextnop_next;
assert(network);
assert(network->filename);
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);
"IPv6AddressLabel\0"
"RoutingPolicyRule\0"
"Route\0"
- "DHCP\0"
- "DHCPv4\0" /* compat */
+ "NextHop\0"
+ "DHCP\0" /* compat */
+ "DHCPv4\0"
"DHCPv6\0"
"DHCPServer\0"
"IPv6AcceptRA\0"
FdbEntry *fdb_entry;
Neighbor *neighbor;
AddressLabel *label;
- Prefix *prefix;
Address *address;
+ NextHop *nexthop;
+ Prefix *prefix;
Route *route;
if (!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);
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);
#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"
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);
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;
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;
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
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");
r = unit_file_lookup_state(UNIT_FILE_SYSTEM, &paths, de->d_name, &state);
if (r < 0)
return log_debug_errno(r, "Failed to determine unit file state of '%s': %m", de->d_name);
- if (!IN_SET(state, UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_LINKED, UNIT_FILE_RUNTIME))
+ if (!IN_SET(state, UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_LINKED, UNIT_FILE_RUNTIME, UNIT_FILE_LINKED_RUNTIME))
return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit file '%s' is in state '%s', can't detach.", de->d_name, unit_file_state_to_string(state));
r = unit_file_is_active(bus, de->d_name, error);
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")) {
}
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);
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);
}
if (streq(field, "CPUQuota")) {
-
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
else {
}
if (streq(field, "DeviceAllow")) {
-
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
else {
}
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 {
}
if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) {
-
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
else {
}
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;
"WorkingDirectory", "RootDirectory", "SyslogIdentifier",
"ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage",
"RuntimeDirectoryPreserve", "Personality", "KeyringMode", "NetworkNamespacePath"))
-
return bus_append_string(m, field, eq);
if (STR_IN_SET(field,
"DynamicUser", "RemoveIPC", "ProtectKernelTunables", "ProtectKernelModules",
"ProtectControlGroups", "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality",
"ProtectHostname", "RestrictSUIDSGID"))
-
return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field,
"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
}
if (streq(field, "LogExtraFields")) {
-
r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0)
return bus_log_create_error(r);
}
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);
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;
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
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;
}
if (streq(field, "FileDescriptorStoreMax"))
-
return bus_append_safe_atou(m, field, eq);
if (STR_IN_SET(field,
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
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 {
}
if (streq(field, "OnCalendar")) {
-
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
else
"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 {
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);
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;
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);
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;
}
}
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;
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);
#include <ctype.h>
#include "alloc-util.h"
+#include "architecture.h"
#include "conf-files.h"
#include "def.h"
#include "device-util.h"
#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")
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() */
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);
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)
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;
DefaultLeaseTimeSec=
EmitTimezone=
DNS=
+[NextHop]
+Id=
+Gateway=
--- /dev/null
+[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
--- /dev/null
+[Match]
+Name=veth-peer
+
+[Network]
+IPv6AcceptRA=no
+Address=2600::1/0
+Address=192.168.5.1/24
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
'dummy99',
'gretun97',
'ip6gretun97',
- 'test1'
+ 'test1',
+ 'veth99',
]
units = [
'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',
'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',
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',