summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/systemd.network.xml33
-rw-r--r--src/core/dbus-service.c4
-rw-r--r--src/core/service.c4
-rw-r--r--src/core/unit.c6
-rw-r--r--src/libsystemd-network/radv-internal.h26
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c7
-rw-r--r--src/libsystemd-network/sd-radv.c130
-rw-r--r--src/libsystemd/sd-netlink/netlink-message.c4
-rw-r--r--src/login/logind-core.c71
-rw-r--r--src/login/logind.h16
-rw-r--r--src/network/networkd-address.c51
-rw-r--r--src/network/networkd-dhcp4.c2
-rw-r--r--src/network/networkd-dhcp6.c56
-rw-r--r--src/network/networkd-link.c39
-rw-r--r--src/network/networkd-manager.c110
-rw-r--r--src/network/networkd-neighbor.c4
-rw-r--r--src/network/networkd-network-gperf.gperf2
-rw-r--r--src/network/networkd-network.c1
-rw-r--r--src/network/networkd-network.h3
-rw-r--r--src/network/networkd-radv.c195
-rw-r--r--src/network/networkd-radv.h9
-rw-r--r--src/network/networkd-route.c220
-rw-r--r--src/network/networkd-route.h9
-rw-r--r--src/network/networkd-routing-policy-rule.c24
-rw-r--r--src/shared/bus-unit-util.c5
-rw-r--r--src/systemctl/systemctl.c5
-rw-r--r--src/systemd/sd-radv.h10
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c106
l---------test/TEST-39-EXECRELOAD/Makefile (renamed from test/TEST-37-EXECRELOAD/Makefile)0
-rwxr-xr-xtest/TEST-39-EXECRELOAD/test.sh (renamed from test/TEST-37-EXECRELOAD/test.sh)0
-rw-r--r--test/TEST-39-EXECRELOAD/testsuite.sh (renamed from test/TEST-37-EXECRELOAD/testsuite.sh)0
-rw-r--r--test/TEST-40-EXEC-COMMAND-EX/Makefile9
-rwxr-xr-xtest/TEST-40-EXEC-COMMAND-EX/test.sh43
-rwxr-xr-xtest/TEST-40-EXEC-COMMAND-EX/testsuite.sh46
-rw-r--r--test/fuzz/fuzz-dhcp6-client/crash-13578bin0 -> 62 bytes
-rw-r--r--test/fuzz/fuzz-network-parser/directives.network3
-rw-r--r--test/test-network/conf/ipv6ra-prefix-client.network6
-rw-r--r--test/test-network/conf/ipv6ra-prefix.network14
-rwxr-xr-xtest/test-network/systemd-networkd-tests.py31
39 files changed, 876 insertions, 428 deletions
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 155c0868b2..8ecc39ce5e 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -1886,7 +1886,7 @@
</variablelist>
</refsect1>
- <refsect1>
+ <refsect1>
<title>[IPv6Prefix] Section Options</title>
<para>One or more <literal>[IPv6Prefix]</literal> sections contain the IPv6
prefixes that are announced via Router Advertisements. See
@@ -1932,6 +1932,37 @@
</refsect1>
<refsect1>
+ <title>[IPv6RoutePrefix] Section Options</title>
+ <para>One or more <literal>[IPv6RoutePrefix]</literal> sections contain the IPv6
+ prefix routes that are announced via Router Advertisements. See
+ <ulink url="https://tools.ietf.org/html/rfc4191">RFC 4191</ulink>
+ for further details.</para>
+
+ <variablelist class='network-directives'>
+
+ <varlistentry>
+ <term><varname>Route=</varname></term>
+
+ <listitem><para>The IPv6 route that is to be distributed to hosts.
+ Similarly to configuring static IPv6 routes, the setting is
+ configured as an IPv6 prefix routes and its prefix route length,
+ separated by a<literal>/</literal> character. Use multiple
+ <literal>[IPv6PrefixRoutes]</literal> sections to configure multiple IPv6
+ prefix routes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LifetimeSec=</varname></term>
+
+ <listitem><para>Lifetime for the route prefix measured in
+ seconds. <varname>LifetimeSec=</varname> defaults to 604800 seconds (one week).
+ </para></listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
<title>[Bridge] Section Options</title>
<para>The <literal>[Bridge]</literal> section accepts the
following keys.</para>
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index ad7471c729..c401b90aaf 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -130,6 +130,7 @@ const sd_bus_vtable bus_service_vtable[] = {
BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecCondition", offsetof(Service, exec_command[SERVICE_EXEC_CONDITION]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+ BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecConditionEx", offsetof(Service, exec_command[SERVICE_EXEC_CONDITION]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecStartPreEx", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
@@ -137,8 +138,11 @@ const sd_bus_vtable bus_service_vtable[] = {
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecStartPostEx", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+ BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecReloadEx", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+ BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecStopEx", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+ BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecStopPostEx", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
/* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */
SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
diff --git a/src/core/service.c b/src/core/service.c
index 894b8af661..71befcddc8 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -4329,9 +4329,13 @@ static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] =
DEFINE_STRING_TABLE_LOOKUP(service_exec_command, ServiceExecCommand);
static const char* const service_exec_ex_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
+ [SERVICE_EXEC_CONDITION] = "ExecConditionEx",
[SERVICE_EXEC_START_PRE] = "ExecStartPreEx",
[SERVICE_EXEC_START] = "ExecStartEx",
[SERVICE_EXEC_START_POST] = "ExecStartPostEx",
+ [SERVICE_EXEC_RELOAD] = "ExecReloadEx",
+ [SERVICE_EXEC_STOP] = "ExecStopEx",
+ [SERVICE_EXEC_STOP_POST] = "ExecStopPostEx",
};
DEFINE_STRING_TABLE_LOOKUP(service_exec_ex_command, ServiceExecCommand);
diff --git a/src/core/unit.c b/src/core/unit.c
index 52a1acafab..87a5976dcc 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -3901,6 +3901,7 @@ int unit_add_node_dependency(Unit *u, const char *what, bool wants, UnitDependen
int unit_coldplug(Unit *u) {
int r = 0, q;
char **i;
+ Job *uj;
assert(u);
@@ -3923,8 +3924,9 @@ int unit_coldplug(Unit *u) {
r = q;
}
- if (u->job) {
- q = job_coldplug(u->job);
+ uj = u->job ?: u->nop_job;
+ if (uj) {
+ q = job_coldplug(uj);
if (q < 0 && r >= 0)
r = q;
}
diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h
index 7b09c7a66c..fb6617bedd 100644
--- a/src/libsystemd-network/radv-internal.h
+++ b/src/libsystemd-network/radv-internal.h
@@ -19,6 +19,7 @@ assert_cc(SD_RADV_DEFAULT_MIN_TIMEOUT_USEC <= SD_RADV_DEFAULT_MAX_TIMEOUT_USEC);
#define SD_RADV_MIN_DELAY_BETWEEN_RAS 3
#define SD_RADV_MAX_RA_DELAY_TIME_USEC (500*USEC_PER_MSEC)
+#define SD_RADV_OPT_ROUTE_INFORMATION 24
#define SD_RADV_OPT_RDNSS 25
#define SD_RADV_OPT_DNSSL 31
@@ -58,6 +59,9 @@ struct sd_radv {
unsigned n_prefixes;
LIST_HEAD(sd_radv_prefix, prefixes);
+ unsigned n_route_prefixes;
+ LIST_HEAD(sd_radv_route_prefix, route_prefixes);
+
size_t n_rdnss;
struct sd_radv_opt_dns *rdnss;
struct sd_radv_opt_dns *dnssl;
@@ -98,6 +102,28 @@ struct sd_radv_prefix {
usec_t preferred_until;
};
+#define radv_route_prefix_opt__contents { \
+ uint8_t type; \
+ uint8_t length; \
+ uint8_t prefixlen; \
+ uint8_t flags_reserved; \
+ be32_t lifetime; \
+ struct in6_addr in6_addr; \
+}
+
+struct radv_route_prefix_opt radv_route_prefix_opt__contents;
+
+struct radv_route_prefix_opt__packed radv_route_prefix_opt__contents _packed_;
+assert_cc(sizeof(struct radv_route_prefix_opt) == sizeof(struct radv_route_prefix_opt__packed));
+
+struct sd_radv_route_prefix {
+ unsigned n_ref;
+
+ struct radv_route_prefix_opt opt;
+
+ LIST_FIELDS(struct sd_radv_route_prefix, prefix);
+};
+
#define log_radv_full(level, error, fmt, ...) log_internal(level, error, PROJECT_FILE, __LINE__, __func__, "RADV: " fmt, ##__VA_ARGS__)
#define log_radv_errno(error, fmt, ...) log_radv_full(LOG_DEBUG, error, fmt, ##__VA_ARGS__)
#define log_radv(fmt, ...) log_radv_errno(0, fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 7dab776b72..5a3b0a6353 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -29,8 +29,8 @@
#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
-#define IRT_DEFAULT 1 * USEC_PER_DAY
-#define IRT_MINIMUM 600 * USEC_PER_SEC
+#define IRT_DEFAULT (1 * USEC_PER_DAY)
+#define IRT_MINIMUM (600 * USEC_PER_SEC)
/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
enum {
@@ -1002,6 +1002,9 @@ static int client_parse_message(
break;
case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
+ if (optlen != 4)
+ return -EINVAL;
+
irt = be32toh(*(be32_t *) optval) * USEC_PER_SEC;
break;
}
diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c
index 185b55e1c5..d531f52326 100644
--- a/src/libsystemd-network/sd-radv.c
+++ b/src/libsystemd-network/sd-radv.c
@@ -116,6 +116,7 @@ static sd_radv *radv_free(sd_radv *ra) {
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv, sd_radv, radv_free);
static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_lifetime) {
+ sd_radv_route_prefix *rt;
sd_radv_prefix *p;
struct sockaddr_in6 dst_addr = {
.sin6_family = AF_INET6,
@@ -136,9 +137,9 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_li
.nd_opt_mtu_type = ND_OPT_MTU,
.nd_opt_mtu_len = 1,
};
- /* Reserve iov space for RA header, linkaddr, MTU, N prefixes, RDNSS
+ /* Reserve iov space for RA header, linkaddr, MTU, N prefixes, N routes, RDNSS
and DNSSL */
- struct iovec iov[5 + ra->n_prefixes];
+ struct iovec iov[5 + ra->n_prefixes + ra->n_route_prefixes];
struct msghdr msg = {
.msg_name = &dst_addr,
.msg_namelen = sizeof(dst_addr),
@@ -190,6 +191,9 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_li
iov[msg.msg_iovlen++] = IOVEC_MAKE(&p->opt, sizeof(p->opt));
}
+ LIST_FOREACH(prefix, rt, ra->route_prefixes)
+ iov[msg.msg_iovlen++] = IOVEC_MAKE(&rt->opt, sizeof(rt->opt));
+
if (ra->rdnss)
iov[msg.msg_iovlen++] = IOVEC_MAKE(ra->rdnss, ra->rdnss->length * 8);
@@ -606,6 +610,77 @@ _public_ sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra,
return cur;
}
+_public_ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p, int dynamic) {
+ char time_string_valid[FORMAT_TIMESPAN_MAX];
+ usec_t time_now, valid, valid_until;
+ _cleanup_free_ char *pretty = NULL;
+ sd_radv_route_prefix *cur;
+ int r;
+
+ assert_return(ra, -EINVAL);
+
+ if (!p)
+ return -EINVAL;
+
+ (void) in_addr_to_string(AF_INET6,
+ (union in_addr_union*) &p->opt.in6_addr,
+ &pretty);
+
+ LIST_FOREACH(prefix, cur, ra->route_prefixes) {
+ _cleanup_free_ char *addr = NULL;
+
+ r = in_addr_prefix_intersect(AF_INET6,
+ (union in_addr_union*) &cur->opt.in6_addr,
+ cur->opt.prefixlen,
+ (union in_addr_union*) &p->opt.in6_addr,
+ p->opt.prefixlen);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ if (dynamic && cur->opt.prefixlen == p->opt.prefixlen)
+ goto update;
+
+ (void) in_addr_to_string(AF_INET6,
+ (union in_addr_union*) &cur->opt.in6_addr,
+ &addr);
+ log_radv("IPv6 route prefix %s/%u already configured, ignoring %s/%u",
+ strempty(addr), cur->opt.prefixlen,
+ strempty(pretty), p->opt.prefixlen);
+
+ return -EEXIST;
+ }
+
+ p = sd_radv_route_prefix_ref(p);
+
+ LIST_APPEND(prefix, ra->route_prefixes, p);
+ ra->n_route_prefixes++;
+
+ cur = p;
+ if (!dynamic) {
+ log_radv("Added prefix %s/%u", strempty(pretty), p->opt.prefixlen);
+ return 0;
+ }
+
+ update:
+ r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
+ if (r < 0)
+ return r;
+
+ valid = be32toh(p->opt.lifetime) * USEC_PER_SEC;
+ valid_until = usec_add(valid, time_now);
+ if (valid_until == USEC_INFINITY)
+ return -EOVERFLOW;
+
+ log_radv("%s route prefix %s/%u valid %s",
+ cur? "Updated": "Added",
+ strempty(pretty), p->opt.prefixlen,
+ format_timespan(time_string_valid, FORMAT_TIMESPAN_MAX, valid, USEC_PER_SEC));
+
+ return 0;
+}
+
_public_ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime,
const struct in6_addr *dns, size_t n_dns) {
_cleanup_free_ struct sd_radv_opt_dns *opt_rdnss = NULL;
@@ -770,3 +845,54 @@ _public_ int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p,
return 0;
}
+
+_public_ int sd_radv_route_prefix_new(sd_radv_route_prefix **ret) {
+ sd_radv_route_prefix *p;
+
+ assert_return(ret, -EINVAL);
+
+ p = new(sd_radv_route_prefix, 1);
+ if (!p)
+ return -ENOMEM;
+
+ *p = (sd_radv_route_prefix) {
+ .n_ref = 1,
+
+ .opt.type = SD_RADV_OPT_ROUTE_INFORMATION,
+ .opt.length = DIV_ROUND_UP(sizeof(p->opt), 8),
+ .opt.prefixlen = 64,
+
+ .opt.lifetime = htobe32(604800),
+ };
+
+ *ret = p;
+ return 0;
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_route_prefix, sd_radv_route_prefix, mfree);
+
+_public_ int sd_radv_prefix_set_route_prefix(sd_radv_route_prefix *p, const struct in6_addr *in6_addr,
+ unsigned char prefixlen) {
+ assert_return(p, -EINVAL);
+ assert_return(in6_addr, -EINVAL);
+
+ if (prefixlen > 128)
+ return -EINVAL;
+
+ if (prefixlen > 64)
+ /* unusual but allowed, log it */
+ log_radv("Unusual prefix length %u greater than 64", prefixlen);
+
+ p->opt.in6_addr = *in6_addr;
+ p->opt.prefixlen = prefixlen;
+
+ return 0;
+}
+
+_public_ int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint32_t valid_lifetime) {
+ assert_return(p, -EINVAL);
+
+ p->opt.lifetime = htobe32(valid_lifetime);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c
index ddfb3ae96c..16964712c9 100644
--- a/src/libsystemd/sd-netlink/netlink-message.c
+++ b/src/libsystemd/sd-netlink/netlink-message.c
@@ -577,7 +577,9 @@ static int netlink_message_read_internal(sd_netlink_message *m, unsigned short t
assert(m->n_containers < RTNL_CONTAINER_DEPTH);
assert(m->containers[m->n_containers].attributes);
- assert(type < m->containers[m->n_containers].n_attributes);
+
+ if (type >= m->containers[m->n_containers].n_attributes)
+ return -ENODATA;
attribute = &m->containers[m->n_containers].attributes[type];
diff --git a/src/login/logind-core.c b/src/login/logind-core.c
index 1d21e90a2e..b981544e12 100644
--- a/src/login/logind-core.c
+++ b/src/login/logind-core.c
@@ -75,7 +75,7 @@ int manager_parse_config_file(Manager *m) {
CONFIG_PARSE_WARN, m);
}
-int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) {
+int manager_add_device(Manager *m, const char *sysfs, bool master, Device **ret_device) {
Device *d;
assert(m);
@@ -91,13 +91,13 @@ int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_dev
return -ENOMEM;
}
- if (_device)
- *_device = d;
+ if (ret_device)
+ *ret_device = d;
return 0;
}
-int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
+int manager_add_seat(Manager *m, const char *id, Seat **ret_seat) {
Seat *s;
int r;
@@ -111,13 +111,13 @@ int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
return r;
}
- if (_seat)
- *_seat = s;
+ if (ret_seat)
+ *ret_seat = s;
return 0;
}
-int manager_add_session(Manager *m, const char *id, Session **_session) {
+int manager_add_session(Manager *m, const char *id, Session **ret_session) {
Session *s;
int r;
@@ -131,8 +131,8 @@ int manager_add_session(Manager *m, const char *id, Session **_session) {
return r;
}
- if (_session)
- *_session = s;
+ if (ret_session)
+ *ret_session = s;
return 0;
}
@@ -143,7 +143,7 @@ int manager_add_user(
gid_t gid,
const char *name,
const char *home,
- User **_user) {
+ User **ret_user) {
User *u;
int r;
@@ -158,8 +158,8 @@ int manager_add_user(
return r;
}
- if (_user)
- *_user = u;
+ if (ret_user)
+ *ret_user = u;
return 0;
}
@@ -167,7 +167,7 @@ int manager_add_user(
int manager_add_user_by_name(
Manager *m,
const char *name,
- User **_user) {
+ User **ret_user) {
const char *home = NULL;
uid_t uid;
@@ -181,10 +181,10 @@ int manager_add_user_by_name(
if (r < 0)
return r;
- return manager_add_user(m, uid, gid, name, home, _user);
+ return manager_add_user(m, uid, gid, name, home, ret_user);
}
-int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
+int manager_add_user_by_uid(Manager *m, uid_t uid, User **ret_user) {
struct passwd *p;
assert(m);
@@ -194,7 +194,7 @@ int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
if (!p)
return errno_or_else(ENOENT);
- return manager_add_user(m, uid, p->pw_gid, p->pw_name, p->pw_dir, _user);
+ return manager_add_user(m, uid, p->pw_gid, p->pw_name, p->pw_dir, ret_user);
}
int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **ret) {
@@ -217,7 +217,7 @@ int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **ret) {
return 0;
}
-int manager_add_button(Manager *m, const char *name, Button **_button) {
+int manager_add_button(Manager *m, const char *name, Button **ret_button) {
Button *b;
assert(m);
@@ -230,8 +230,8 @@ int manager_add_button(Manager *m, const char *name, Button **_button) {
return -ENOMEM;
}
- if (_button)
- *_button = b;
+ if (ret_button)
+ *ret_button = b;
return 0;
}
@@ -354,28 +354,19 @@ int manager_get_session_by_pid(Manager *m, pid_t pid, Session **ret) {
s = hashmap_get(m->sessions_by_leader, PID_TO_PTR(pid));
if (!s) {
r = cg_pid_get_unit(pid, &unit);
- if (r < 0)
- goto not_found;
-
- s = hashmap_get(m->session_units, unit);
- if (!s)
- goto not_found;
+ if (r >= 0)
+ s = hashmap_get(m->session_units, unit);
}
if (ret)
*ret = s;
- return 1;
-
-not_found:
- if (ret)
- *ret = NULL;
- return 0;
+ return !!s;
}
int manager_get_user_by_pid(Manager *m, pid_t pid, User **ret) {
_cleanup_free_ char *unit = NULL;
- User *u;
+ User *u = NULL;
int r;
assert(m);
@@ -384,23 +375,13 @@ int manager_get_user_by_pid(Manager *m, pid_t pid, User **ret) {
return -EINVAL;
r = cg_pid_get_slice(pid, &unit);
- if (r < 0)
- goto not_found;
-
- u = hashmap_get(m->user_units, unit);
- if (!u)
- goto not_found;
+ if (r >= 0)
+ u = hashmap_get(m->user_units, unit);
if (ret)
*ret = u;
- return 1;
-
-not_found:
- if (ret)
- *ret = NULL;
-
- return 0;
+ return !!u;
}
int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
diff --git a/src/login/logind.h b/src/login/logind.h
index f260f2dc96..5e2dc8cb77 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -127,14 +127,14 @@ struct Manager {
void manager_reset_config(Manager *m);
int manager_parse_config_file(Manager *m);
-int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device);
-int manager_add_button(Manager *m, const char *name, Button **_button);
-int manager_add_seat(Manager *m, const char *id, Seat **_seat);
-int manager_add_session(Manager *m, const char *id, Session **_session);
-int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, const char *home, User **_user);
-int manager_add_user_by_name(Manager *m, const char *name, User **_user);
-int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user);
-int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor);
+int manager_add_device(Manager *m, const char *sysfs, bool master, Device **ret_device);
+int manager_add_button(Manager *m, const char *name, Button **ret_button);
+int manager_add_seat(Manager *m, const char *id, Seat **ret_seat);
+int manager_add_session(Manager *m, const char *id, Session **ret_session);
+int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, const char *home, User **ret_user);
+int manager_add_user_by_name(Manager *m, const char *name, User **ret_user);
+int manager_add_user_by_uid(Manager *m, uid_t uid, User **ret_user);
+int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **ret_inhibitor);
int manager_process_seat_device(Manager *m, sd_device *d);
int manager_process_button_device(Manager *m, sd_device *d);
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 4fe5571039..23d40ccc41 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -115,6 +115,20 @@ void address_free(Address *address) {
free(address);
}
+static uint32_t address_prefix(const Address *a) {
+ assert(a);
+
+ /* make sure we don't try to shift by 32.
+ * See ISO/IEC 9899:TC3 § 6.5.7.3. */
+ if (a->prefixlen == 0)
+ return 0;
+
+ if (a->in_addr_peer.in.s_addr != 0)
+ return be32toh(a->in_addr_peer.in.s_addr) >> (32 - a->prefixlen);
+ else
+ return be32toh(a->in_addr.in.s_addr) >> (32 - a->prefixlen);
+}
+
static void address_hash_func(const Address *a, struct siphash *state) {
assert(a);
@@ -125,16 +139,8 @@ static void address_hash_func(const Address *a, struct siphash *state) {
siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
/* peer prefix */
- if (a->prefixlen != 0) {
- uint32_t prefix;
-
- if (a->in_addr_peer.in.s_addr != 0)
- prefix = be32toh(a->in_addr_peer.in.s_addr) >> (32 - a->prefixlen);
- else
- prefix = be32toh(a->in_addr.in.s_addr) >> (32 - a->prefixlen);
-
- siphash24_compress(&prefix, sizeof(prefix), state);
- }
+ uint32_t prefix = address_prefix(a);
+ siphash24_compress(&prefix, sizeof(prefix), state);
_fallthrough_;
case AF_INET6:
@@ -162,26 +168,11 @@ static int address_compare_func(const Address *a1, const Address *a2) {
if (r != 0)
return r;
- /* compare the peer prefixes */
- if (a1->prefixlen != 0) {
- /* make sure we don't try to shift by 32.
- * See ISO/IEC 9899:TC3 § 6.5.7.3. */
- uint32_t b1, b2;
-
- if (a1->in_addr_peer.in.s_addr != 0)
- b1 = be32toh(a1->in_addr_peer.in.s_addr) >> (32 - a1->prefixlen);
- else
- b1 = be32toh(a1->in_addr.in.s_addr) >> (32 - a1->prefixlen);
-
- if (a2->in_addr_peer.in.s_addr != 0)
- b2 = be32toh(a2->in_addr_peer.in.s_addr) >> (32 - a1->prefixlen);
- else
- b2 = be32toh(a2->in_addr.in.s_addr) >> (32 - a1->prefixlen);
-
- r = CMP(b1, b2);
- if (r != 0)
- return r;
- }
+ uint32_t prefix1 = address_prefix(a1);
+ uint32_t prefix2 = address_prefix(a2);
+ r = CMP(prefix1, prefix2);
+ if (r != 0)
+ return r;
_fallthrough_;
case AF_INET6:
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index c5644f844b..d9951abe24 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -181,7 +181,7 @@ static int link_set_dhcp_routes(Link *link) {
* the addresses now, let's not configure the routes either. */
return 0;
- r = set_ensure_allocated(&link->dhcp_routes, &route_full_hash_ops);
+ r = set_ensure_allocated(&link->dhcp_routes, &route_hash_ops);
if (r < 0)
return log_oom();
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index 5e9535a1e7..e61eeda7d0 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -134,22 +134,21 @@ int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) {
&lifetime_preferred,
&lifetime_valid) >= 0) {
_cleanup_free_ char *buf = NULL;
- Route *route;
+ _cleanup_(route_freep) Route *route = NULL;
if (pd_prefix_len >= 64)
continue;
(void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
- r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, NULL, 0, 0, 0, &route);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to add unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m",
- strnull(buf),
- pd_prefix_len);
- continue;
- }
+ r = route_new(&route);
+ if (r < 0)
+ return r;
- route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE);
+ route->family = AF_INET6;
+ route->dst = pd_prefix;
+ route->dst_prefixlen = pd_prefix_len;
+ route->type = RTN_UNREACHABLE;
r = route_remove(route, link, dhcp6_route_remove_handler);
if (r < 0) {
@@ -290,20 +289,17 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
strnull(buf), pd_prefix_len);
if (pd_prefix_len < 64) {
- uint32_t table;
- Route *route;
-
- table = link_get_dhcp_route_table(link);
+ _cleanup_(route_freep) Route *route = NULL;
- r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, NULL, 0, 0, table, &route);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to add unreachable route for DHCPv6 delegated subnet %s/%u: %m",
- strnull(buf),
- pd_prefix_len);
- continue;
- }
+ r = route_new(&route);
+ if (r < 0)
+ return r;
- route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE);
+ route->family = AF_INET6;
+ route->dst = pd_prefix;
+ route->dst_prefixlen = pd_prefix_len;
+ route->table = link_get_dhcp_route_table(link);
+ route->type = RTN_UNREACHABLE;
r = route_configure(route, link, dhcp6_route_handler);
if (r < 0) {
@@ -721,20 +717,23 @@ static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, Link *
}
static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
+ _cleanup_(route_freep) Route *route = NULL;
_cleanup_free_ struct in6_addr *a = NULL;
_cleanup_free_ char *buf = NULL;
Link *assigned_link;
- Route *route;
int r;
assert_return(m, -EINVAL);
assert_return(addr, -EINVAL);
- r = route_add(link, AF_INET6, (union in_addr_union *) addr, 64,
- NULL, 0, 0, 0, &route);
+ r = route_new(&route);
if (r < 0)
return r;
+ route->family = AF_INET6;
+ route->dst.in6 = *addr;
+ route->dst_prefixlen = 64;
+
r = route_configure(route, link, dhcp6_route_add_handler);
if (r < 0)
return r;
@@ -786,8 +785,8 @@ static int dhcp6_prefix_remove_handler(sd_netlink *nl, sd_netlink_message *m, Li
int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
_cleanup_free_ struct in6_addr *a = NULL;
_cleanup_(link_unrefp) Link *l = NULL;
+ _cleanup_(route_freep) Route *route = NULL;
_cleanup_free_ char *buf = NULL;
- Route *route;
int r;
assert_return(m, -EINVAL);
@@ -798,10 +797,15 @@ int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
return -EINVAL;
(void) sd_radv_remove_prefix(l->radv, addr, 64);
- r = route_get(l, AF_INET6, (union in_addr_union *) addr, 64, NULL, 0, 0, 0, &route);
+
+ r = route_new(&route);
if (r < 0)
return r;
+ route->family = AF_INET6;
+ route->dst.in6 = *addr;
+ route->dst_prefixlen = 64;
+
r = route_remove(route, l, dhcp6_prefix_remove_handler);
if (r < 0)
return r;
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index f1aeb7287e..be7e311e9f 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -2393,9 +2393,9 @@ static int link_drop_foreign_config(Link *link) {
continue;
if (link_address_is_dynamic(link, address)) {
- if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
+ if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
continue;
- } else if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
+ } else if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
continue;
if (link_is_static_address_configured(link, address)) {
@@ -2435,16 +2435,16 @@ static int link_drop_foreign_config(Link *link) {
in_addr_equal(AF_INET6, &route->dst, &(union in_addr_union) { .in6 = {{{ 0xff,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }}} }))
continue;
- if (route->protocol == RTPROT_STATIC &&
+ if (route->protocol == RTPROT_STATIC && link->network &&
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
continue;
- if (route->protocol == RTPROT_DHCP &&
+ if (route->protocol == RTPROT_DHCP && link->network &&
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
continue;
if (link_is_static_route_configured(link, route)) {
- r = route_add(link, route->family, &route->dst, route->dst_prefixlen, &route->gw, route->tos, route->priority, route->table, NULL);
+ r = route_add(link, route, NULL);
if (r < 0)
return r;
} else {
@@ -2906,7 +2906,6 @@ static int link_load(Link *link) {
*dhcp4_address = NULL,
*ipv4ll_address = NULL;
union in_addr_union address;
- union in_addr_union route_dst;
const char *p;
int r;
@@ -2993,14 +2992,11 @@ network_file_fail:
p = routes;
for (;;) {
- Route *route;
- _cleanup_free_ char *route_str = NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
- usec_t lifetime;
+ _cleanup_(route_freep) Route *tmp = NULL;
+ _cleanup_free_ char *route_str = NULL;
char *prefixlen_str;
- int family;
- unsigned char prefixlen, tos, table;
- uint32_t priority;
+ Route *route;
r = extract_first_word(&p, &route_str, NULL, 0);
if (r < 0) {
@@ -3018,7 +3014,11 @@ network_file_fail:
*prefixlen_str++ = '\0';
- r = sscanf(prefixlen_str, "%hhu/%hhu/%"SCNu32"/%hhu/"USEC_FMT, &prefixlen, &tos, &priority, &table, &lifetime);
+ r = route_new(&tmp);
+ if (r < 0)
+ return log_oom();
+
+ r = sscanf(prefixlen_str, "%hhu/%hhu/%"SCNu32"/%"PRIu32"/"USEC_FMT, &tmp->dst_prefixlen, &tmp->tos, &tmp->priority, &tmp->table, &tmp->lifetime);
if (r != 5) {
log_link_debug(link,
"Failed to parse destination prefix length, tos, priority, table or expiration %s",
@@ -3026,24 +3026,23 @@ network_file_fail:
continue;
}
- r = in_addr_from_string_auto(route_str, &family, &route_dst);
+ r = in_addr_from_string_auto(route_str, &tmp->family, &tmp->dst);
if (r < 0) {
log_link_debug_errno(link, r, "Failed to parse route destination %s: %m", route_str);
continue;
}
- r = route_add(link, family, &route_dst, prefixlen, NULL, tos, priority, table, &route);
+ r = route_add(link, tmp, &route);
if (r < 0)
return log_link_error_errno(link, r, "Failed to add route: %m");
- if (lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
- r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(), lifetime,
+ if (route->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
+ r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(), route->lifetime,
0, route_expire_handler, route);
if (r < 0)
log_link_warning_errno(link, r, "Could not arm route expiration handler: %m");
}
- route->lifetime = lifetime;
sd_event_source_unref(route->expire);
route->expire = TAKE_PTR(expire);
}
@@ -3116,8 +3115,8 @@ int link_add(Manager *m, sd_netlink_message *message, Link **ret) {
sprintf(ifindex_str, "n%d", link->ifindex);
r = sd_device_new_from_device_id(&device, ifindex_str);
if (r < 0) {
- log_link_warning_errno(link, r, "Could not find device: %m");
- goto failed;
+ log_link_warning_errno(link, r, "Could not find device, waiting for device initialization: %m");
+ return 0;
}
r = sd_device_get_is_initialized(device);
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index a164d6163e..ea962e50db 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -265,15 +265,13 @@ static int manager_connect_udev(Manager *m) {
}
int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
+ _cleanup_(route_freep) Route *tmp = NULL;
+ Route *route = NULL;
Manager *m = userdata;
Link *link = NULL;
+ uint32_t ifindex;
uint16_t type;
- uint32_t ifindex, priority = 0;
- unsigned char protocol, scope, tos, table, rt_type;
- int family;
- unsigned char dst_prefixlen, src_prefixlen;
- union in_addr_union dst = IN_ADDR_NULL, gw = IN_ADDR_NULL, src = IN_ADDR_NULL, prefsrc = IN_ADDR_NULL;
- Route *route = NULL;
+ unsigned char table;
int r;
assert(rtnl);
@@ -318,39 +316,43 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
return 0;
}
- r = sd_rtnl_message_route_get_family(message, &family);
- if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) {
+ r = route_new(&tmp);
+ if (r < 0)
+ return log_oom();
+
+ r = sd_rtnl_message_route_get_family(message, &tmp->family);
+ if (r < 0 || !IN_SET(tmp->family, AF_INET, AF_INET6)) {
log_link_warning(link, "rtnl: received route message with invalid family, ignoring");
return 0;
}
- r = sd_rtnl_message_route_get_protocol(message, &protocol);
+ r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol);
if (r < 0) {
log_warning_errno(r, "rtnl: received route message with invalid route protocol: %m");
return 0;
}
- switch (family) {
+ switch (tmp->family) {
case AF_INET:
- r = sd_netlink_message_read_in_addr(message, RTA_DST, &dst.in);
+ r = sd_netlink_message_read_in_addr(message, RTA_DST, &tmp->dst.in);
if (r < 0 && r != -ENODATA) {
log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m");
return 0;
}
- r = sd_netlink_message_read_in_addr(message, RTA_GATEWAY, &gw.in);
+ r = sd_netlink_message_read_in_addr(message, RTA_GATEWAY, &tmp->gw.in);
if (r < 0 && r != -ENODATA) {
log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
return 0;
}
- r = sd_netlink_message_read_in_addr(message, RTA_SRC, &src.in);
+ r = sd_netlink_message_read_in_addr(message, RTA_SRC, &tmp->src.in);
if (r < 0 && r != -ENODATA) {
log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m");
return 0;
}
- r = sd_netlink_message_read_in_addr(message, RTA_PREFSRC, &prefsrc.in);
+ r = sd_netlink_message_read_in_addr(message, RTA_PREFSRC, &tmp->prefsrc.in);
if (r < 0 && r != -ENODATA) {
log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m");
return 0;
@@ -359,25 +361,25 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
break;
case AF_INET6:
- r = sd_netlink_message_read_in6_addr(message, RTA_DST, &dst.in6);
+ r = sd_netlink_message_read_in6_addr(message, RTA_DST, &tmp->dst.in6);
if (r < 0 && r != -ENODATA) {
log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m");
return 0;
}
- r = sd_netlink_message_read_in6_addr(message, RTA_GATEWAY, &gw.in6);
+ r = sd_netlink_message_read_in6_addr(message, RTA_GATEWAY, &tmp->gw.in6);
if (r < 0 && r != -ENODATA) {
log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
return 0;
}
- r = sd_netlink_message_read_in6_addr(message, RTA_SRC, &src.in6);
+ r = sd_netlink_message_read_in6_addr(message, RTA_SRC, &tmp->src.in6);
if (r < 0 && r != -ENODATA) {
log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m");
return 0;
}
- r = sd_netlink_message_read_in6_addr(message, RTA_PREFSRC, &prefsrc.in6);
+ r = sd_netlink_message_read_in6_addr(message, RTA_PREFSRC, &tmp->prefsrc.in6);
if (r < 0 && r != -ENODATA) {
log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m");
return 0;
@@ -390,31 +392,31 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
return 0;
}
- r = sd_rtnl_message_route_get_dst_prefixlen(message, &dst_prefixlen);
+ r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen);
if (r < 0) {
log_link_warning_errno(link, r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m");
return 0;
}
- r = sd_rtnl_message_route_get_src_prefixlen(message, &src_prefixlen);
+ r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen);
if (r < 0) {
log_link_warning_errno(link, r, "rtnl: received route message with invalid source prefixlen, ignoring: %m");
return 0;
}
- r = sd_rtnl_message_route_get_scope(message, &scope);
+ r = sd_rtnl_message_route_get_scope(message, &tmp->scope);
if (r < 0) {
log_link_warning_errno(link, r, "rtnl: received route message with invalid scope, ignoring: %m");
return 0;
}
- r = sd_rtnl_message_route_get_tos(message, &tos);
+ r = sd_rtnl_message_route_get_tos(message, &tmp->tos);
if (r < 0) {
log_link_warning_errno(link, r, "rtnl: received route message with invalid tos, ignoring: %m");
return 0;
}
- r = sd_rtnl_message_route_get_type(message, &rt_type);
+ r = sd_rtnl_message_route_get_type(message, &tmp->type);
if (r < 0) {
log_link_warning_errno(link, r, "rtnl: received route message with invalid type, ignoring: %m");
return 0;
@@ -425,14 +427,40 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
log_link_warning_errno(link, r, "rtnl: received route message with invalid table, ignoring: %m");
return 0;
}
+ tmp->table = table;
- r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &priority);
+ r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority);
if (r < 0 && r != -ENODATA) {
log_link_warning_errno(link, r, "rtnl: received route message with invalid priority, ignoring: %m");
return 0;
}
- (void) route_get(link, family, &dst, dst_prefixlen, &gw, tos, priority, table, &route);
+ r = sd_netlink_message_enter_container(message, RTA_METRICS);
+ if (r < 0 && r != -ENODATA) {
+ log_link_error_errno(link, r, "rtnl: Could not enter RTA_METRICS container: %m");
+ return 0;
+ }
+ if (r >= 0) {
+ r = sd_netlink_message_read_u32(message, RTAX_INITCWND, &tmp->initcwnd);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route message with invalid initcwnd, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_u32(message, RTAX_INITRWND, &tmp->initrwnd);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: received route message with invalid initrwnd, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_exit_container(message);
+ if (r < 0) {
+ log_link_error_errno(link, r, "rtnl: Could not exit from RTA_METRICS container: %m");
+ return 0;
+ }
+ }
+
+ (void) route_get(link, tmp, &route);
if (DEBUG_LOGGING) {
_cleanup_free_ char *buf_dst = NULL, *buf_dst_prefixlen = NULL,
@@ -440,41 +468,39 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
char buf_scope[ROUTE_SCOPE_STR_MAX], buf_table[ROUTE_TABLE_STR_MAX],
buf_protocol[ROUTE_PROTOCOL_STR_MAX];
- if (!in_addr_is_null(family, &dst)) {
- (void) in_addr_to_string(family, &dst, &buf_dst);
- (void) asprintf(&buf_dst_prefixlen, "/%u", dst_prefixlen);
+ if (!in_addr_is_null(tmp->family, &tmp->dst)) {
+ (void) in_addr_to_string(tmp->family, &tmp->dst, &buf_dst);
+ (void) asprintf(&buf_dst_prefixlen, "/%u", tmp->dst_prefixlen);
}
- if (!in_addr_is_null(family, &src))
- (void) in_addr_to_string(family, &src, &buf_src);
- if (!in_addr_is_null(family, &gw))
- (void) in_addr_to_string(family, &gw, &buf_gw);
- if (!in_addr_is_null(family, &prefsrc))
- (void) in_addr_to_string(family, &prefsrc, &buf_prefsrc);
+ if (!in_addr_is_null(tmp->family, &tmp->src))
+ (void) in_addr_to_string(tmp->family, &tmp->src, &buf_src);
+ if (!in_addr_is_null(tmp->family, &tmp->gw))
+ (void) in_addr_to_string(tmp->family, &tmp->gw, &buf_gw);
+ if (!in_addr_is_null(tmp->family, &tmp->prefsrc))
+ (void) in_addr_to_string(tmp->family, &tmp->prefsrc, &buf_prefsrc);
log_link_debug(link,
"%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
- type == RTM_DELROUTE ? "Forgetting" : route ? "Updating remembered" : "Remembering",
+ type == RTM_DELROUTE ? "Forgetting" : route ? "Received remembered" : "Remembering",
strna(buf_dst), strempty(buf_dst_prefixlen),
strna(buf_src), strna(buf_gw), strna(buf_prefsrc),
- format_route_scope(scope, buf_scope, sizeof buf_scope),
- format_route_table(table, buf_table, sizeof buf_table),
- format_route_protocol(protocol, buf_protocol, sizeof buf_protocol),
- strna(route_type_to_string(rt_type)));
+ format_route_scope(tmp->scope, buf_scope, sizeof buf_scope),
+ format_route_table(tmp->table, buf_table, sizeof buf_table),
+ format_route_protocol(tmp->protocol, buf_protocol, sizeof buf_protocol),
+ strna(route_type_to_string(tmp->type)));
}
switch (type) {
case RTM_NEWROUTE:
if (!route) {
/* A route appeared that we did not request */
- r = route_add_foreign(link, family, &dst, dst_prefixlen, &gw, tos, priority, table, &route);
+ r = route_add_foreign(link, tmp, &route);
if (r < 0) {
log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
return 0;
}
}
- route_update(route, &src, src_prefixlen, &gw, &prefsrc, scope, protocol, rt_type);
-
break;
case RTM_DELROUTE:
diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c
index 537f6be9e1..fd61ebd5d3 100644
--- a/src/network/networkd-neighbor.c
+++ b/src/network/networkd-neighbor.c
@@ -209,18 +209,20 @@ static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state)
assert(neighbor);
siphash24_compress(&neighbor->family, sizeof(neighbor->family), state);
+ siphash24_compress(&neighbor->lladdr_size, sizeof(neighbor->lladdr_size), state);
switch (neighbor->family) {
case AF_INET:
case AF_INET6:
/* Equality of neighbors are given by the pair (addr,lladdr) */
siphash24_compress(&neighbor->in_addr, FAMILY_ADDRESS_SIZE(neighbor->family), state);
- siphash24_compress(&neighbor->lladdr, neighbor->lladdr_size, state);
break;
default:
/* treat any other address family as AF_UNSPEC */
break;
}
+
+ siphash24_compress(&neighbor->lladdr, neighbor->lladdr_size, state);
}
static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index d4d108ad25..43163a31ec 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -223,6 +223,8 @@ IPv6Prefix.OnLink, config_parse_prefix_flags,
IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0
IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0
IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0
+IPv6RoutePrefix.Route, config_parse_route_prefix, 0, 0
+IPv6RoutePrefix.LifetimeSec, config_parse_route_prefix_lifetime, 0, 0
CAN.BitRate, config_parse_si_size, 0, offsetof(Network, can_bitrate)
CAN.SamplePoint, config_parse_permille, 0, offsetof(Network, can_sample_point)
CAN.RestartSec, config_parse_sec, 0, offsetof(Network, can_restart_us)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 70dbd31f50..0608219429 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -458,6 +458,7 @@ int network_load_one(Manager *manager, const char *filename) {
"BridgeVLAN\0"
"IPv6PrefixDelegation\0"
"IPv6Prefix\0"
+ "IPv6RoutePrefix\0"
"CAN\0",
config_item_perf_lookup, network_network_gperf_lookup,
CONFIG_PARSE_WARN, network);
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index bc760744e5..486b8c31a5 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -221,6 +221,7 @@ struct Network {
LIST_HEAD(Neighbor, neighbors);
LIST_HEAD(AddressLabel, address_labels);
LIST_HEAD(Prefix, static_prefixes);
+ LIST_HEAD(Prefix, static_route_prefixes);
LIST_HEAD(RoutingPolicyRule, rules);
unsigned n_static_addresses;
@@ -230,6 +231,7 @@ struct Network {
unsigned n_neighbors;
unsigned n_address_labels;
unsigned n_static_prefixes;
+ unsigned n_static_route_prefixes;
unsigned n_rules;
Hashmap *addresses_by_section;
@@ -238,6 +240,7 @@ struct Network {
Hashmap *neighbors_by_section;
Hashmap *address_labels_by_section;
Hashmap *prefixes_by_section;
+ Hashmap *route_prefixes_by_section;
Hashmap *rules_by_section;
/* All kinds of DNS configuration */
diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c
index 25321aefed..8972c661ae 100644
--- a/src/network/networkd-radv.c
+++ b/src/network/networkd-radv.c
@@ -101,16 +101,100 @@ static int prefix_new_static(Network *network, const char *filename,
return 0;
}
+int route_prefix_new(Prefix **ret) {
+ _cleanup_(prefix_freep) Prefix *prefix = NULL;
+
+ prefix = new0(Prefix, 1);
+ if (!prefix)
+ return -ENOMEM;
+
+ if (sd_radv_route_prefix_new(&prefix->radv_route_prefix) < 0)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(prefix);
+
+ return 0;
+}
+
+void route_prefix_free(Prefix *prefix) {
+ if (!prefix)
+ return;
+
+ if (prefix->network) {
+ LIST_REMOVE(prefixes, prefix->network->static_route_prefixes, prefix);
+ assert(prefix->network->n_static_route_prefixes > 0);
+ prefix->network->n_static_route_prefixes--;
+
+ if (prefix->section)
+ hashmap_remove(prefix->network->route_prefixes_by_section,
+ prefix->section);
+ }
+
+ network_config_section_free(prefix->section);
+
+ free(prefix);
+}
+
+static int route_prefix_new_static(Network *network, const char *filename,
+ unsigned section_line, Prefix **ret) {
+ _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(prefix_freep) Prefix *prefix = 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;
+
+ if (section_line) {
+ prefix = hashmap_get(network->route_prefixes_by_section, n);
+ if (prefix) {
+ *ret = TAKE_PTR(prefix);
+
+ return 0;
+ }
+ }
+ }
+
+ r = route_prefix_new(&prefix);
+ if (r < 0)
+ return r;
+
+ prefix->network = network;
+ LIST_APPEND(prefixes, network->static_route_prefixes, prefix);
+ network->n_static_route_prefixes++;
+
+ if (filename) {
+ prefix->section = TAKE_PTR(n);
+
+ r = hashmap_ensure_allocated(&network->route_prefixes_by_section, &network_config_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(network->route_prefixes_by_section, prefix->section, prefix);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(prefix);
+
+ return 0;
+}
+
int config_parse_prefix(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) {
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Network *network = userdata;
_cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
@@ -234,6 +318,90 @@ int config_parse_prefix_lifetime(const char *unit,
return 0;
}
+int config_parse_route_prefix(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) {
+
+ Network *network = userdata;
+ _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
+ uint8_t prefixlen = 64;
+ union in_addr_union in6addr;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = route_prefix_new_static(network, filename, section_line, &p);
+ if (r < 0)
+ return r;
+
+ r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Route prefix is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ if (sd_radv_prefix_set_route_prefix(p->radv_route_prefix, &in6addr.in6, prefixlen) < 0)
+ return -EADDRNOTAVAIL;
+
+ log_syntax(unit, LOG_INFO, filename, line, r, "Found route prefix %s", rvalue);
+
+ p = NULL;
+
+ return 0;
+}
+
+int config_parse_route_prefix_lifetime(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) {
+ Network *network = userdata;
+ _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
+ usec_t usec;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = route_prefix_new_static(network, filename, section_line, &p);
+ if (r < 0)
+ return r;
+
+ r = parse_sec(rvalue, &usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Roure lifetime is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ /* a value of 0xffffffff represents infinity */
+ r = sd_radv_route_prefix_set_lifetime(p->radv_route_prefix, DIV_ROUND_UP(usec, USEC_PER_SEC));
+ if (r < 0)
+ return r;
+
+ p = NULL;
+
+ return 0;
+}
+
static int radv_get_ip6dns(Network *network, struct in6_addr **dns,
size_t *n_dns) {
_cleanup_free_ struct in6_addr *addresses = NULL;
@@ -438,6 +606,15 @@ int radv_configure(Link *link) {
if (r < 0)
return r;
}
+
+ LIST_FOREACH(prefixes, p, link->network->static_route_prefixes) {
+ r = sd_radv_add_route_prefix(link->radv, p->radv_route_prefix, false);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
+
}
return radv_emit_dns(link);
diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h
index 45be083bfe..2f60b285ae 100644
--- a/src/network/networkd-radv.h
+++ b/src/network/networkd-radv.h
@@ -26,8 +26,10 @@ struct Prefix {
NetworkConfigSection *section;
sd_radv_prefix *radv_prefix;
+ sd_radv_route_prefix *radv_route_prefix;
LIST_FIELDS(Prefix, prefixes);
+ LIST_FIELDS(Prefix, route_prefixes);
};
int prefix_new(Prefix **ret);
@@ -35,6 +37,11 @@ void prefix_free(Prefix *prefix);
DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, prefix_free);
+int route_prefix_new(Prefix **ret);
+void route_prefix_free(Prefix *prefix);
+
+DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, route_prefix_free);
+
int radv_emit_dns(Link *link);
int radv_configure(Link *link);
@@ -48,3 +55,5 @@ CONFIG_PARSER_PROTOTYPE(config_parse_prefix_flags);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_lifetime);
CONFIG_PARSER_PROTOTYPE(config_parse_radv_dns);
CONFIG_PARSER_PROTOTYPE(config_parse_radv_search_domains);
+CONFIG_PARSER_PROTOTYPE(config_parse_route_prefix);
+CONFIG_PARSER_PROTOTYPE(config_parse_route_prefix_lifetime);
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index dd1469d0df..85df5d9395 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -157,13 +157,25 @@ static void route_hash_func(const Route *route, struct siphash *state) {
switch (route->family) {
case AF_INET:
case AF_INET6:
- /* Equality of routes are given by the 4-touple
- (dst_prefix,dst_prefixlen,tos,priority,table) */
- siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
+ siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
+
+ siphash24_compress(&route->src_prefixlen, sizeof(route->src_prefixlen), state);
+ siphash24_compress(&route->src, FAMILY_ADDRESS_SIZE(route->family), state);
+
+ siphash24_compress(&route->gw, FAMILY_ADDRESS_SIZE(route->family), state);
+
+ siphash24_compress(&route->prefsrc, FAMILY_ADDRESS_SIZE(route->family), state);
+
siphash24_compress(&route->tos, sizeof(route->tos), state);
siphash24_compress(&route->priority, sizeof(route->priority), state);
siphash24_compress(&route->table, sizeof(route->table), state);
+ siphash24_compress(&route->protocol, sizeof(route->protocol), state);
+ siphash24_compress(&route->scope, sizeof(route->scope), state);
+ siphash24_compress(&route->type, sizeof(route->type), state);
+
+ siphash24_compress(&route->initcwnd, sizeof(route->initcwnd), state);
+ siphash24_compress(&route->initrwnd, sizeof(route->initrwnd), state);
break;
default:
@@ -186,75 +198,23 @@ static int route_compare_func(const Route *a, const Route *b) {
if (r != 0)
return r;
- r = CMP(a->tos, b->tos);
- if (r != 0)
- return r;
-
- r = CMP(a->priority, b->priority);
+ r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
if (r != 0)
return r;
- r = CMP(a->table, b->table);
+ r = CMP(a->src_prefixlen, b->src_prefixlen);
if (r != 0)
return r;
- r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
+ r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family));
if (r != 0)
return r;
- return memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
- default:
- /* treat any other address family as AF_UNSPEC */
- return 0;
- }
-}
-
-DEFINE_PRIVATE_HASH_OPS(route_hash_ops, Route, route_hash_func, route_compare_func);
-
-static void route_full_hash_func(const Route *route, struct siphash *state) {
- assert(route);
-
- siphash24_compress(&route->family, sizeof(route->family), state);
-
- switch (route->family) {
- case AF_INET:
- case AF_INET6:
- siphash24_compress(&route->gw, FAMILY_ADDRESS_SIZE(route->family), state);
- siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
- siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
- siphash24_compress(&route->src, FAMILY_ADDRESS_SIZE(route->family), state);
- siphash24_compress(&route->src_prefixlen, sizeof(route->src_prefixlen), state);
- siphash24_compress(&route->prefsrc, FAMILY_ADDRESS_SIZE(route->family), state);
-
- siphash24_compress(&route->tos, sizeof(route->tos), state);
- siphash24_compress(&route->priority, sizeof(route->priority), state);
- siphash24_compress(&route->table, sizeof(route->table), state);
- siphash24_compress(&route->protocol, sizeof(route->protocol), state);
- siphash24_compress(&route->scope, sizeof(route->scope), state);
- siphash24_compress(&route->type, sizeof(route->type), state);
-
- break;
- default:
- /* treat any other address family as AF_UNSPEC */
- break;
- }
-}
-
-static int route_full_compare_func(const Route *a, const Route *b) {
- int r;
-
- r = CMP(a->family, b->family);
- if (r != 0)
- return r;
-
- switch (a->family) {
- case AF_INET:
- case AF_INET6:
- r = CMP(a->dst_prefixlen, b->dst_prefixlen);
+ r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
if (r != 0)
return r;
- r = CMP(a->src_prefixlen, b->src_prefixlen);
+ r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
if (r != 0)
return r;
@@ -282,19 +242,15 @@ static int route_full_compare_func(const Route *a, const Route *b) {
if (r != 0)
return r;
- r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
+ r = CMP(a->initcwnd, b->initcwnd);
if (r != 0)
return r;
- r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
- if (r != 0)
- return r;
-
- r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family));
+ r = CMP(a->initrwnd, b->initrwnd);
if (r != 0)
return r;
- return memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
+ return 0;
default:
/* treat any other address family as AF_UNSPEC */
return 0;
@@ -302,10 +258,10 @@ static int route_full_compare_func(const Route *a, const Route *b) {
}
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
- route_full_hash_ops,
+ route_hash_ops,
Route,
- route_full_hash_func,
- route_full_compare_func,
+ route_hash_func,
+ route_compare_func,
route_free);
bool route_equal(Route *r1, Route *r2) {
@@ -318,39 +274,21 @@ bool route_equal(Route *r1, Route *r2) {
return route_compare_func(r1, r2) == 0;
}
-int route_get(Link *link,
- int family,
- const union in_addr_union *dst,
- unsigned char dst_prefixlen,
- const union in_addr_union *gw,
- unsigned char tos,
- uint32_t priority,
- uint32_t table,
- Route **ret) {
+int route_get(Link *link, Route *in, Route **ret) {
- Route route, *existing;
+ Route *existing;
assert(link);
- assert(dst);
-
- route = (Route) {
- .family = family,
- .dst = *dst,
- .dst_prefixlen = dst_prefixlen,
- .gw = gw ? *gw : IN_ADDR_NULL,
- .tos = tos,
- .priority = priority,
- .table = table,
- };
+ assert(in);
- existing = set_get(link->routes, &route);
+ existing = set_get(link->routes, in);
if (existing) {
if (ret)
*ret = existing;
return 1;
}
- existing = set_get(link->routes_foreign, &route);
+ existing = set_get(link->routes_foreign, in);
if (existing) {
if (ret)
*ret = existing;
@@ -360,37 +298,35 @@ int route_get(Link *link,
return -ENOENT;
}
-static int route_add_internal(
- Link *link,
- Set **routes,
- int family,
- const union in_addr_union *dst,
- unsigned char dst_prefixlen,
- const union in_addr_union *gw,
- unsigned char tos,
- uint32_t priority,
- uint32_t table,
- Route **ret) {
+static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret) {
_cleanup_(route_freep) Route *route = NULL;
int r;
assert(link);
assert(routes);
- assert(dst);
+ assert(in);
r = route_new(&route);
if (r < 0)
return r;
- route->family = family;
- route->dst = *dst;
- route->dst_prefixlen = dst_prefixlen;
- route->dst = *dst;
- route->gw = gw ? *gw : IN_ADDR_NULL;
- route->tos = tos;
- route->priority = priority;
- route->table = table;
+ route->family = in->family;
+ route->src = in->src;
+ route->src_prefixlen = in->src_prefixlen;
+ route->dst = in->dst;
+ route->dst_prefixlen = in->dst_prefixlen;
+ route->gw = in->gw;
+ route->prefsrc = in->prefsrc;
+ route->scope = in->scope;
+ route->protocol = in->protocol;
+ route->type = in->type;
+ route->tos = in->tos;
+ route->priority = in->priority;
+ route->table = in->table;
+ route->initcwnd = in->initcwnd;
+ route->initrwnd = in->initrwnd;
+ route->lifetime = in->lifetime;
r = set_ensure_allocated(routes, &route_hash_ops);
if (r < 0)
@@ -412,37 +348,19 @@ static int route_add_internal(
return 0;
}
-int route_add_foreign(
- Link *link,
- int family,
- const union in_addr_union *dst,
- unsigned char dst_prefixlen,
- const union in_addr_union *gw,
- unsigned char tos,
- uint32_t priority,
- uint32_t table,
- Route **ret) {
-
- return route_add_internal(link, &link->routes_foreign, family, dst, dst_prefixlen, gw, tos, priority, table, ret);
+int route_add_foreign(Link *link, Route *in, Route **ret) {
+ return route_add_internal(link, &link->routes_foreign, in, ret);
}
-int route_add(Link *link,
- int family,
- const union in_addr_union *dst,
- unsigned char dst_prefixlen,
- const union in_addr_union *gw,
- unsigned char tos,
- uint32_t priority,
- uint32_t table,
- Route **ret) {
+int route_add(Link *link, Route *in, Route **ret) {
Route *route;
int r;
- r = route_get(link, family, dst, dst_prefixlen, gw, tos, priority, table, &route);
+ r = route_get(link, in, &route);
if (r == -ENOENT) {
/* Route does not exist, create a new one */
- r = route_add_internal(link, &link->routes, family, dst, dst_prefixlen, gw, tos, priority, table, &route);
+ r = route_add_internal(link, &link->routes, in, &route);
if (r < 0)
return r;
} else if (r == 0) {
@@ -468,27 +386,6 @@ int route_add(Link *link,
return 0;
}
-void route_update(Route *route,
- const union in_addr_union *src,
- unsigned char src_prefixlen,
- const union in_addr_union *gw,
- const union in_addr_union *prefsrc,
- unsigned char scope,
- unsigned char protocol,
- unsigned char type) {
-
- assert(route);
- assert(src || src_prefixlen == 0);
-
- route->src = src ? *src : IN_ADDR_NULL;
- route->src_prefixlen = src_prefixlen;
- route->gw = gw ? *gw : IN_ADDR_NULL;
- route->prefsrc = prefsrc ? *prefsrc : IN_ADDR_NULL;
- route->scope = scope;
- route->protocol = protocol;
- route->type = type;
-}
-
static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
@@ -626,7 +523,6 @@ int route_configure(
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
- usec_t lifetime;
int r;
assert(link);
@@ -636,7 +532,7 @@ int route_configure(
assert(IN_SET(route->family, AF_INET, AF_INET6));
assert(callback);
- if (route_get(link, route->family, &route->dst, route->dst_prefixlen, &route->gw, route->tos, route->priority, route->table, NULL) <= 0 &&
+ if (route_get(link, route, NULL) <= 0 &&
set_size(link->routes) >= routes_max())
return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
"Too many routes are configured, refusing: %m");
@@ -810,15 +706,11 @@ int route_configure(
link_ref(link);
- lifetime = route->lifetime;
-
- r = route_add(link, route->family, &route->dst, route->dst_prefixlen, &route->gw, route->tos, route->priority, route->table, &route);
+ r = route_add(link, route, &route);
if (r < 0)
return log_link_error_errno(link, r, "Could not add route: %m");
/* TODO: drop expiration handling once it can be pushed into the kernel */
- route->lifetime = lifetime;
-
if (route->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
route->lifetime, 0, route_expire_handler, route);
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
index 9bd4991520..89d54020db 100644
--- a/src/network/networkd-route.h
+++ b/src/network/networkd-route.h
@@ -49,17 +49,16 @@ struct Route {
LIST_FIELDS(Route, routes);
};
-extern const struct hash_ops route_full_hash_ops;
+extern const struct hash_ops route_hash_ops;
int route_new(Route **ret);
void route_free(Route *route);
int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback);
int route_remove(Route *route, Link *link, link_netlink_message_handler_t callback);
-int route_get(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, const union in_addr_union *gw, unsigned char tos, uint32_t priority, uint32_t table, Route **ret);
-int route_add(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, const union in_addr_union *gw, unsigned char tos, uint32_t priority, uint32_t table, Route **ret);
-int route_add_foreign(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, const union in_addr_union *gw, unsigned char tos, uint32_t priority, uint32_t table, Route **ret);
-void route_update(Route *route, const union in_addr_union *src, unsigned char src_prefixlen, const union in_addr_union *gw, const union in_addr_union *prefsrc, unsigned char scope, unsigned char protocol, unsigned char type);
+int route_get(Link *link, Route *in, Route **ret);
+int route_add(Link *link, Route *in, Route **ret);
+int route_add_foreign(Link *link, Route *in, Route **ret);
bool route_equal(Route *r1, Route *r2);
int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata);
diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c
index 9443db02fd..8203f87c9f 100644
--- a/src/network/networkd-routing-policy-rule.c
+++ b/src/network/networkd-routing-policy-rule.c
@@ -105,7 +105,6 @@ static void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct
switch (rule->family) {
case AF_INET:
case AF_INET6:
-
siphash24_compress(&rule->from, FAMILY_ADDRESS_SIZE(rule->family), state);
siphash24_compress(&rule->from_prefixlen, sizeof(rule->from_prefixlen), state);
@@ -151,10 +150,18 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro
if (r != 0)
return r;
+ r = memcmp(&a->from, &b->from, FAMILY_ADDRESS_SIZE(a->family));
+ if (r != 0)
+ return r;
+
r = CMP(a->to_prefixlen, b->to_prefixlen);
if (r != 0)
return r;
+ r = memcmp(&a->to, &b->to, FAMILY_ADDRESS_SIZE(a->family));
+ if (r != 0)
+ return r;
+
r = CMP(a->invert_rule, b->invert_rule);
if (r != 0)
return r;
@@ -179,14 +186,6 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro
if (r != 0)
return r;
- r = strcmp_ptr(a->iif, b->iif);
- if (!r)
- return r;
-
- r = strcmp_ptr(a->oif, b->oif);
- if (!r)
- return r;
-
r = CMP(a->protocol, b->protocol);
if (r != 0)
return r;
@@ -199,12 +198,15 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro
if (r != 0)
return r;
- r = memcmp(&a->from, &b->from, FAMILY_ADDRESS_SIZE(a->family));
+ r = strcmp_ptr(a->iif, b->iif);
if (r != 0)
return r;
- return memcmp(&a->to, &b->to, FAMILY_ADDRESS_SIZE(a->family));
+ r = strcmp_ptr(a->oif, b->oif);
+ if (r != 0)
+ return r;
+ return 0;
default:
/* treat any other address family as AF_UNSPEC */
return 0;
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index e53b9d5ea2..612ae84fff 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -1434,8 +1434,9 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
if (STR_IN_SET(field,
"ExecCondition", "ExecStartPre", "ExecStart", "ExecStartPost",
- "ExecStartPreEx", "ExecStartEx", "ExecStartPostEx",
- "ExecReload", "ExecStop", "ExecStopPost"))
+ "ExecConditionEx", "ExecStartPreEx", "ExecStartEx", "ExecStartPostEx",
+ "ExecReload", "ExecStop", "ExecStopPost",
+ "ExecReloadEx", "ExecStopEx", "ExecStopPostEx"))
return bus_append_exec_command(m, field, eq);
if (STR_IN_SET(field, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) {
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index dcf76bed7c..7917cbe39f 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -5534,6 +5534,8 @@ static int show_one(
{ "IPEgressBytes", "t", NULL, offsetof(UnitStatusInfo, ip_egress_bytes) },
{ "IOReadBytes", "t", NULL, offsetof(UnitStatusInfo, io_read_bytes) },
{ "IOWriteBytes", "t", NULL, offsetof(UnitStatusInfo, io_write_bytes) },
+ { "ExecCondition", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecConditionEx", "a(sasasttttuii)", map_exec, 0 },
{ "ExecStartPre", "a(sasbttttuii)", map_exec, 0 },
{ "ExecStartPreEx", "a(sasasttttuii)", map_exec, 0 },
{ "ExecStart", "a(sasbttttuii)", map_exec, 0 },
@@ -5541,9 +5543,12 @@ static int show_one(
{ "ExecStartPost", "a(sasbttttuii)", map_exec, 0 },
{ "ExecStartPostEx", "a(sasasttttuii)", map_exec, 0 },
{ "ExecReload", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecReloadEx", "a(sasasttttuii)", map_exec, 0 },
{ "ExecStopPre", "a(sasbttttuii)", map_exec, 0 },
{ "ExecStop", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStopEx", "a(sasasttttuii)", map_exec, 0 },
{ "ExecStopPost", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStopPostEx", "a(sasasttttuii)", map_exec, 0 },
{}
};
diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h
index 93861b9d24..f085231934 100644
--- a/src/systemd/sd-radv.h
+++ b/src/systemd/sd-radv.h
@@ -37,6 +37,7 @@ _SD_BEGIN_DECLARATIONS;
typedef struct sd_radv sd_radv;
typedef struct sd_radv_prefix sd_radv_prefix;
+typedef struct sd_radv_route_prefix sd_radv_route_prefix;
/* Router Advertisement */
int sd_radv_new(sd_radv **ret);
@@ -59,6 +60,7 @@ int sd_radv_set_managed_information(sd_radv *ra, int managed);
int sd_radv_set_other_information(sd_radv *ra, int other);
int sd_radv_set_preference(sd_radv *ra, unsigned preference);
int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, int dynamic);
+int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p, int dynamic);
sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra, const struct in6_addr *prefix,
unsigned char prefixlen);
int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime,
@@ -80,8 +82,16 @@ int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p,
int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p,
uint32_t preferred_lifetime);
+int sd_radv_route_prefix_new(sd_radv_route_prefix **ret);
+sd_radv_route_prefix *sd_radv_route_prefix_ref(sd_radv_route_prefix *ra);
+sd_radv_route_prefix *sd_radv_route_prefix_unref(sd_radv_route_prefix *ra);
+
+int sd_radv_prefix_set_route_prefix(sd_radv_route_prefix *p, const struct in6_addr *in6_addr, unsigned char prefixlen);
+int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint32_t valid_lifetime);
+
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv, sd_radv_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_prefix, sd_radv_prefix_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_route_prefix, sd_radv_route_prefix_unref);
_SD_END_DECLARATIONS;
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index 5f5245e48a..3032f15898 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -277,7 +277,48 @@ static int send_passwords(const char *socket_name, char **passwords) {
return (int) n;
}
-static int parse_password(const char *filename, char **wall) {
+static bool wall_tty_match(const char *path, void *userdata) {
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -1;
+ struct stat st;
+
+ if (!path_is_absolute(path))
+ path = strjoina("/dev/", path);
+
+ if (lstat(path, &st) < 0) {
+ log_debug_errno(errno, "Failed to stat %s: %m", path);
+ return true;
+ }
+
+ if (!S_ISCHR(st.st_mode)) {
+ log_debug("%s is not a character device.", path);
+ return true;
+ }
+
+ /* We use named pipes to ensure that wall messages suggesting
+ * password entry are not printed over password prompts
+ * already shown. We use the fact here that opening a pipe in
+ * non-blocking mode for write-only will succeed only if
+ * there's some writer behind it. Using pipes has the
+ * advantage that the block will automatically go away if the
+ * process dies. */
+
+ if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) {
+ log_oom();
+ return true;
+ }
+
+ fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+ if (fd < 0) {
+ log_debug_errno(errno, "Failed to open the wall pipe: %m");
+ return 1;
+ }
+
+ /* What, we managed to open the pipe? Then this tty is filtered. */
+ return 0;
+}
+
+static int parse_password(const char *filename) {
_cleanup_free_ char *socket_name = NULL, *message = NULL;
bool accept_cached = false, echo = false;
uint64_t not_after = 0;
@@ -318,19 +359,16 @@ static int parse_password(const char *filename, char **wall) {
printf("'%s' (PID %u)\n", message, pid);
else if (arg_action == ACTION_WALL) {
- char *_wall;
+ _cleanup_free_ char *wall = NULL;
- if (asprintf(&_wall,
- "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
- "Please enter password with the systemd-tty-ask-password-agent tool:",
- strempty(*wall),
- *wall ? "\r\n\r\n" : "",
+ if (asprintf(&wall,
+ "Password entry required for \'%s\' (PID %u).\r\n"
+ "Please enter password with the systemd-tty-ask-password-agent tool.",
message,
pid) < 0)
return log_oom();
- free(*wall);
- *wall = _wall;
+ (void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
} else {
_cleanup_strv_free_erase_ char **passwords = NULL;
@@ -411,47 +449,6 @@ static int wall_tty_block(void) {
return fd;
}
-static bool wall_tty_match(const char *path, void *userdata) {
- _cleanup_free_ char *p = NULL;
- _cleanup_close_ int fd = -1;
- struct stat st;
-
- if (!path_is_absolute(path))
- path = strjoina("/dev/", path);
-
- if (lstat(path, &st) < 0) {
- log_debug_errno(errno, "Failed to stat %s: %m", path);
- return true;
- }
-
- if (!S_ISCHR(st.st_mode)) {
- log_debug("%s is not a character device.", path);
- return true;
- }
-
- /* We use named pipes to ensure that wall messages suggesting
- * password entry are not printed over password prompts
- * already shown. We use the fact here that opening a pipe in
- * non-blocking mode for write-only will succeed only if
- * there's some writer behind it. Using pipes has the
- * advantage that the block will automatically go away if the
- * process dies. */
-
- if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) {
- log_oom();
- return true;
- }
-
- fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
- if (fd < 0) {
- log_debug_errno(errno, "Failed to open the wall pipe: %m");
- return 1;
- }
-
- /* What, we managed to open the pipe? Then this tty is filtered. */
- return 0;
-}
-
static int show_passwords(void) {
_cleanup_closedir_ DIR *d;
struct dirent *de;
@@ -466,10 +463,10 @@ static int show_passwords(void) {
}
FOREACH_DIRENT_ALL(de, d, return log_error_errno(errno, "Failed to read directory: %m")) {
- _cleanup_free_ char *p = NULL, *wall = NULL;
+ _cleanup_free_ char *p = NULL;
int q;
- /* We only support /dev on tmpfs, hence we can rely on
+ /* We only support /run on tmpfs, hence we can rely on
* d_type to be reliable */
if (de->d_type != DT_REG)
@@ -485,12 +482,9 @@ static int show_passwords(void) {
if (!p)
return log_oom();
- q = parse_password(p, &wall);
+ q = parse_password(p);
if (q < 0 && r == 0)
r = q;
-
- if (wall)
- (void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
}
return r;
diff --git a/test/TEST-37-EXECRELOAD/Makefile b/test/TEST-39-EXECRELOAD/Makefile
index e9f93b1104..e9f93b1104 120000
--- a/test/TEST-37-EXECRELOAD/Makefile
+++ b/test/TEST-39-EXECRELOAD/Makefile
diff --git a/test/TEST-37-EXECRELOAD/test.sh b/test/TEST-39-EXECRELOAD/test.sh
index 7eb9db415a..7eb9db415a 100755
--- a/test/TEST-37-EXECRELOAD/test.sh
+++ b/test/TEST-39-EXECRELOAD/test.sh
diff --git a/test/TEST-37-EXECRELOAD/testsuite.sh b/test/TEST-39-EXECRELOAD/testsuite.sh
index a51f6fd5cc..a51f6fd5cc 100644
--- a/test/TEST-37-EXECRELOAD/testsuite.sh
+++ b/test/TEST-39-EXECRELOAD/testsuite.sh
diff --git a/test/TEST-40-EXEC-COMMAND-EX/Makefile b/test/TEST-40-EXEC-COMMAND-EX/Makefile
new file mode 100644
index 0000000000..45e9bfc67c
--- /dev/null
+++ b/test/TEST-40-EXEC-COMMAND-EX/Makefile
@@ -0,0 +1,9 @@
+BUILD_DIR=$(shell ../../tools/find-build-dir.sh)
+
+all setup run:
+ @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@
+
+clean clean-again:
+ @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --clean
+
+.PHONY: all setup run clean clean-again
diff --git a/test/TEST-40-EXEC-COMMAND-EX/test.sh b/test/TEST-40-EXEC-COMMAND-EX/test.sh
new file mode 100755
index 0000000000..723c9ccd4b
--- /dev/null
+++ b/test/TEST-40-EXEC-COMMAND-EX/test.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+set -e
+TEST_DESCRIPTION="test ExecXYZEx= service unit dbus hookups"
+
+. $TEST_BASE_DIR/test-functions
+
+test_setup() {
+ create_empty_image_rootdir
+
+ (
+ LOG_LEVEL=5
+ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
+
+ setup_basic_environment
+
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
+ # setup the testsuite service
+ cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+
+[Service]
+ExecStart=/testsuite.sh
+Type=oneshot
+StandardOutput=tty
+StandardError=tty
+NotifyAccess=all
+EOF
+ cp testsuite.sh $initdir/
+
+ setup_testsuite
+ )
+ setup_nspawn_root
+}
+
+do_test "$@"
diff --git a/test/TEST-40-EXEC-COMMAND-EX/testsuite.sh b/test/TEST-40-EXEC-COMMAND-EX/testsuite.sh
new file mode 100755
index 0000000000..e0580ae75a
--- /dev/null
+++ b/test/TEST-40-EXEC-COMMAND-EX/testsuite.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+declare -A property
+
+property[1_one]=ExecCondition
+property[2_two]=ExecStartPre
+property[3_three]=ExecStart
+property[4_four]=ExecStartPost
+property[5_five]=ExecReload
+property[6_six]=ExecStop
+property[7_seven]=ExecStopPost
+
+# These should all get upgraded to the corresponding Ex property as the non-Ex variant
+# does not support the ":" prefix (no-env-expand).
+for c in "${!property[@]}"; do
+ systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property[$c]}=:/bin/echo \${$c}" /bin/true
+ systemctl show -p "${property[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no"
+ systemctl show -p "${property[$c]}Ex" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand"
+done
+
+declare -A property_ex
+
+property_ex[1_one_ex]=ExecConditionEx
+property_ex[2_two_ex]=ExecStartPreEx
+property_ex[3_three_ex]=ExecStartEx
+property_ex[4_four_ex]=ExecStartPostEx
+property_ex[5_five_ex]=ExecReloadEx
+property_ex[6_six_ex]=ExecStopEx
+property_ex[7_seven_ex]=ExecStopPostEx
+
+for c in "${!property_ex[@]}"; do
+ systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property_ex[$c]}=:/bin/echo \${$c}" /bin/true
+ systemctl show -p "${property_ex[$c]%??}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no"
+ systemctl show -p "${property_ex[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand"
+done
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/fuzz/fuzz-dhcp6-client/crash-13578 b/test/fuzz/fuzz-dhcp6-client/crash-13578
new file mode 100644
index 0000000000..0753966ea4
--- /dev/null
+++ b/test/fuzz/fuzz-dhcp6-client/crash-13578
Binary files differ
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index 848d4bd187..b688d37d08 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -174,6 +174,9 @@ OnLink=
PreferredLifetimeSec=
AddressAutoconfiguration=
ValidLifetimeSec=
+[IPv6RoutePrefix]
+Route=
+LifetimeSec=
[BridgeVLAN]
EgressUntagged=
VLAN=
diff --git a/test/test-network/conf/ipv6ra-prefix-client.network b/test/test-network/conf/ipv6ra-prefix-client.network
new file mode 100644
index 0000000000..bc40b123c7
--- /dev/null
+++ b/test/test-network/conf/ipv6ra-prefix-client.network
@@ -0,0 +1,6 @@
+[Match]
+Name=veth-peer
+
+[Network]
+DHCP=no
+IPv6AcceptRA=yes
diff --git a/test/test-network/conf/ipv6ra-prefix.network b/test/test-network/conf/ipv6ra-prefix.network
new file mode 100644
index 0000000000..7bb6661362
--- /dev/null
+++ b/test/test-network/conf/ipv6ra-prefix.network
@@ -0,0 +1,14 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=no
+IPv6PrefixDelegation=yes
+Address=2001:db8:0:1::1/64
+
+[IPv6Prefix]
+Prefix=2001:db8:0:1::4/64
+
+[IPv6RoutePrefix]
+Route=2001:db0:fff::/64
+LifetimeSec=1000
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index f1a58158e1..17ecc8b438 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -3131,6 +3131,37 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
print(output)
self.assertRegex(output, 'example.com')
+class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
+ links = ['veth99']
+
+ units = [
+ '25-veth.netdev',
+ 'ipv6ra-prefix-client.network',
+ 'ipv6ra-prefix.network'
+ ]
+
+ def setUp(self):
+ remove_links(self.links)
+ stop_networkd(show_logs=False)
+
+ def tearDown(self):
+ remove_log_file()
+ remove_links(self.links)
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
+
+ def test_ipv6_route_prefix(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client.network', 'ipv6ra-prefix.network')
+
+ start_networkd()
+ self.wait_online(['veth-peer:carrier'])
+ start_dnsmasq()
+ self.wait_online(['veth99:routable', 'veth-peer:routable'])
+
+ output = check_output('ip', '-6', 'route', 'show', 'dev', 'veth-peer')
+ print(output)
+ self.assertRegex(output, '2001:db8:0:1::/64 proto ra')
+
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')