diff options
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 Binary files differnew file mode 100644 index 0000000000..0753966ea4 --- /dev/null +++ b/test/fuzz/fuzz-dhcp6-client/crash-13578 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') |