Compare commits
19 Commits
fc2ebc84d4
...
68f5b9ad37
Author | SHA1 | Date |
---|---|---|
Yu Watanabe | 68f5b9ad37 | |
Yu Watanabe | 5261c521e3 | |
Franck Bui | 514d9e1665 | |
Lennart Poettering | b480a4c15e | |
Lennart Poettering | af3baf174a | |
Ryan Wilson | d8091e1281 | |
Yu Watanabe | 2e49ae7e4f | |
Yu Watanabe | 083e98670f | |
Yu Watanabe | 45ee87df9f | |
Yu Watanabe | 4007421856 | |
Yu Watanabe | c970db1bf3 | |
Yu Watanabe | 80c3019982 | |
Yu Watanabe | ecb6dc08e3 | |
Yu Watanabe | 32fd218add | |
Yu Watanabe | 62a4858ea7 | |
Yu Watanabe | a4027e291e | |
Yu Watanabe | bd5f4b76c7 | |
Yu Watanabe | 407e9348c1 | |
Yu Watanabe | 2ca5445f32 |
25
TODO
25
TODO
|
@ -129,6 +129,10 @@ Deprecations and removals:
|
|||
|
||||
Features:
|
||||
|
||||
* format-table: introduce new cell type for strings with ansi sequences in
|
||||
them. display them in regular output mode (via strip_tab_ansi()), but
|
||||
suppress them in json mode.
|
||||
|
||||
* machined: when registering a machine, also take a relative cgroup path,
|
||||
relative to the machine's unit. This is useful when registering unpriv
|
||||
machines, as they might sit down the cgroup tree, below a cgroup delegation
|
||||
|
@ -217,12 +221,8 @@ Features:
|
|||
services where mount propagation from the root fs is off, an still have
|
||||
confext/sysext propagated in.
|
||||
|
||||
* support F_DUDFD_QUERY for comparing fds in same_fd (requires kernel 6.10)
|
||||
|
||||
* generic interface for varlink for setting log level and stuff that all our daemons can implement
|
||||
|
||||
* use pty ioctl to get peer wherever possible (TIOCGPTPEER)
|
||||
|
||||
* maybe teach repart.d/ dropins a new setting MakeMountNodes= or so, which is
|
||||
just like MakeDirectories=, but uses an access mode of 0000 and sets the +i
|
||||
chattr bit. This is useful as protection against early uses of /var/ or /tmp/
|
||||
|
@ -253,8 +253,6 @@ Features:
|
|||
* initrd: when transitioning from initrd to host, validate that
|
||||
/lib/modules/`uname -r` exists, refuse otherwise
|
||||
|
||||
* tmpfiles: add "owning" flag for lines that limits effect of --purge
|
||||
|
||||
* signed bpf loading: to address need for signature verification for bpf
|
||||
programs when they are loaded, and given the bpf folks don't think this is
|
||||
realistic in kernel space, maybe add small daemon that facilitates this
|
||||
|
@ -458,9 +456,6 @@ Features:
|
|||
* introduce mntid_t, and make it 64bit, as apparently the kernel switched to
|
||||
64bit mount ids
|
||||
|
||||
* use udev rule networkd ownership property to take ownership of network
|
||||
interfaces nspawn creates
|
||||
|
||||
* mountfsd/nsresourced
|
||||
- userdb: maybe allow callers to map one uid to their own uid
|
||||
- bpflsm: allow writes if resulting UID on disk would be userns' owner UID
|
||||
|
@ -647,6 +642,7 @@ Features:
|
|||
- openpt_allocate_in_namespace()
|
||||
- unit_attach_pid_to_cgroup_via_bus()
|
||||
- cg_attach() – requires new kernel feature
|
||||
- journald's process cache
|
||||
|
||||
* ddi must be listed as block device fstype
|
||||
|
||||
|
@ -1470,9 +1466,6 @@ Features:
|
|||
|
||||
* in sd-id128: also parse UUIDs in RFC4122 URN syntax (i.e. chop off urn:uuid: prefix)
|
||||
|
||||
* DynamicUser= + StateDirectory= → use uid mapping mounts, too, in order to
|
||||
make dirs appear under right UID.
|
||||
|
||||
* systemd-sysext: optionally, run it in initrd already, before transitioning
|
||||
into host, to open up possibility for services shipped like that.
|
||||
|
||||
|
@ -1644,14 +1637,6 @@ Features:
|
|||
|
||||
* maybe add kernel cmdline params: to force random seed crediting
|
||||
|
||||
* introduce a new per-process uuid, similar to the boot id, the machine id, the
|
||||
invocation id, that is derived from process creds, specifically a hashed
|
||||
combination of AT_RANDOM + getpid() + the starttime from
|
||||
/proc/self/status. Then add these ids implicitly when logging. Deriving this
|
||||
uuid from these three things has the benefit that it can be derived easily
|
||||
from /proc/$PID/ in a stable, and unique way that changes on both fork() and
|
||||
exec().
|
||||
|
||||
* let's not GC a unit while its ratelimits are still pending
|
||||
|
||||
* when killing due to service watchdog timeout maybe detect whether target
|
||||
|
|
|
@ -1286,21 +1286,21 @@ DuplicateAddressDetection=none</programlisting></para>
|
|||
<varlistentry>
|
||||
<term><varname>KeepConfiguration=</varname></term>
|
||||
<listitem>
|
||||
<para>Takes a boolean or one of <literal>static</literal>, <literal>dhcp-on-stop</literal>,
|
||||
<literal>dhcp</literal>. When <literal>static</literal>, <command>systemd-networkd</command>
|
||||
will not drop static addresses and routes on starting up process. When set to
|
||||
<literal>dhcp-on-stop</literal>, <command>systemd-networkd</command> will not drop addresses
|
||||
and routes on stopping the daemon. When <literal>dhcp</literal>,
|
||||
the addresses and routes provided by a DHCP server will never be dropped even if the DHCP
|
||||
lease expires. This is contrary to the DHCP specification, but may be the best choice if,
|
||||
e.g., the root filesystem relies on this connection. The setting <literal>dhcp</literal>
|
||||
implies <literal>dhcp-on-stop</literal>, and <literal>yes</literal> implies
|
||||
<literal>dhcp</literal> and <literal>static</literal>. Defaults to
|
||||
<literal>dhcp-on-stop</literal> when <command>systemd-networkd</command> is running in
|
||||
initrd, <literal>yes</literal> when the root filesystem is a network filesystem, and
|
||||
<literal>no</literal> otherwise.</para>
|
||||
<para>Takes a boolean or one of <literal>static</literal>, <literal>dynamic-on-stop</literal>, and
|
||||
<literal>dynamic</literal>. When <literal>static</literal>, <command>systemd-networkd</command>
|
||||
will not drop statically configured addresses and routes on starting up process. When
|
||||
<literal>dynamic-on-stop</literal>, the dynamically configurad addresses and routes, such as
|
||||
DHCPv4, DHCPv6, SLAAC, and IPv4 link-local address, will not be dropped when
|
||||
<command>systemd-networkd</command> is being stopped. When <literal>dynamic</literal>, the
|
||||
dynamically configured addresses and routes will never be dropped, and the lifetime of DHCPv4
|
||||
leases will be ignored. This is contrary to the DHCP specification, but may be the best choice if,
|
||||
e.g., the root filesystem relies on this connection. The setting <literal>dynamic</literal> implies
|
||||
<literal>dynamic-on-stop</literal>, and <literal>yes</literal> implies <literal>dynamic</literal>
|
||||
and <literal>static</literal>. Defaults to <literal>dynamic-on-stop</literal> when
|
||||
<command>systemd-networkd</command> is running in initrd, <literal>yes</literal> when the root
|
||||
filesystem is a network filesystem, and <literal>no</literal> otherwise.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v243"/>
|
||||
<xi:include href="version-info.xml" xpointer="v243"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
|
|
@ -1131,6 +1131,8 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
|
|||
* • If O_CREAT is used with XO_LABEL, any created file will be immediately relabelled.
|
||||
*
|
||||
* • If the path is specified NULL or empty, behaves like fd_reopen().
|
||||
*
|
||||
* • If XO_NOCOW is specified will turn on the NOCOW btrfs flag on the file, if available.
|
||||
*/
|
||||
|
||||
if (isempty(path)) {
|
||||
|
|
|
@ -76,6 +76,7 @@ sources = files(
|
|||
'networkd-route-nexthop.c',
|
||||
'networkd-route-util.c',
|
||||
'networkd-routing-policy-rule.c',
|
||||
'networkd-serialize.c',
|
||||
'networkd-setlink.c',
|
||||
'networkd-speed-meter.c',
|
||||
'networkd-sriov.c',
|
||||
|
|
|
@ -1274,8 +1274,8 @@ bool link_address_is_dynamic(const Link *link, const Address *address) {
|
|||
if (address->family != AF_INET)
|
||||
return false;
|
||||
|
||||
/* Even when the address is leased from a DHCP server, networkd assign the address
|
||||
* without lifetime when KeepConfiguration=dhcp. So, let's check that we have
|
||||
/* Even if an IPv4 address is leased from a DHCP server with a finite lifetime, networkd assign the
|
||||
* address without lifetime when KeepConfiguration=dynamic. So, let's check that we have
|
||||
* corresponding routes with RTPROT_DHCP. */
|
||||
SET_FOREACH(route, link->manager->routes) {
|
||||
if (route->source != NETWORK_CONFIG_SOURCE_FOREIGN)
|
||||
|
@ -1410,9 +1410,9 @@ int link_drop_unmanaged_addresses(Link *link) {
|
|||
continue;
|
||||
|
||||
/* link_address_is_dynamic() is slightly heavy. Let's call the function only when
|
||||
* KeepConfiguration=dhcp or static. */
|
||||
if (IN_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP, KEEP_CONFIGURATION_STATIC) &&
|
||||
link_address_is_dynamic(link, address) == (link->network->keep_configuration == KEEP_CONFIGURATION_DHCP))
|
||||
* KeepConfiguration=dynamic or static. */
|
||||
if (IN_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC, KEEP_CONFIGURATION_STATIC) &&
|
||||
link_address_is_dynamic(link, address) == (link->network->keep_configuration == KEEP_CONFIGURATION_DYNAMIC))
|
||||
continue;
|
||||
|
||||
} else if (address->source != NETWORK_CONFIG_SOURCE_STATIC)
|
||||
|
|
|
@ -613,8 +613,49 @@ static int dhcp_pd_finalize(Link *link) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void dhcp_pd_prefix_lost(Link *uplink) {
|
||||
static void dhcp_pd_mark_unreachable_route(Manager *manager, NetworkConfigSource source) {
|
||||
assert(manager);
|
||||
|
||||
Route *route;
|
||||
SET_FOREACH(route, manager->routes) {
|
||||
if (route->source != source)
|
||||
continue;
|
||||
if (route->family != AF_INET6)
|
||||
continue;
|
||||
if (route->nexthop.ifindex != 0) /* IPv6 unreachable has 0 ifindex. */
|
||||
continue;
|
||||
if (!route_type_is_reject(route->type))
|
||||
continue;
|
||||
|
||||
route_mark(route);
|
||||
}
|
||||
}
|
||||
|
||||
static int dhcp_pd_remove_unreachable_route(Manager *manager, NetworkConfigSource source, bool only_marked) {
|
||||
int ret = 0;
|
||||
|
||||
assert(manager);
|
||||
|
||||
Route *route;
|
||||
SET_FOREACH(route, manager->routes) {
|
||||
if (route->source != source)
|
||||
continue;
|
||||
if (route->family != AF_INET6)
|
||||
continue;
|
||||
if (route->nexthop.ifindex != 0) /* IPv6 unreachable has 0 ifindex. */
|
||||
continue;
|
||||
if (!route_type_is_reject(route->type))
|
||||
continue;
|
||||
if (only_marked && !route_is_marked(route))
|
||||
continue;
|
||||
|
||||
RET_GATHER(ret, route_remove_and_cancel(route, manager));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dhcp_pd_prefix_lost(Link *uplink, NetworkConfigSource source) {
|
||||
Link *link;
|
||||
int r;
|
||||
|
||||
|
@ -630,22 +671,7 @@ void dhcp_pd_prefix_lost(Link *uplink) {
|
|||
link_enter_failed(link);
|
||||
}
|
||||
|
||||
SET_FOREACH(route, uplink->manager->routes) {
|
||||
if (!IN_SET(route->source, NETWORK_CONFIG_SOURCE_DHCP4, NETWORK_CONFIG_SOURCE_DHCP6))
|
||||
continue;
|
||||
if (route->family != AF_INET6)
|
||||
continue;
|
||||
if (route->type != RTN_UNREACHABLE)
|
||||
continue;
|
||||
if (!set_contains(uplink->dhcp_pd_prefixes,
|
||||
&(struct in_addr_prefix) {
|
||||
.family = AF_INET6,
|
||||
.prefixlen = route->dst_prefixlen,
|
||||
.address = route->dst }))
|
||||
continue;
|
||||
|
||||
(void) route_remove_and_cancel(route, uplink->manager);
|
||||
}
|
||||
(void) dhcp_pd_remove_unreachable_route(uplink->manager, source, /* only_marked = */ false);
|
||||
|
||||
set_clear(uplink->dhcp_pd_prefixes);
|
||||
}
|
||||
|
@ -653,13 +679,20 @@ void dhcp_pd_prefix_lost(Link *uplink) {
|
|||
void dhcp4_pd_prefix_lost(Link *uplink) {
|
||||
Link *tunnel;
|
||||
|
||||
dhcp_pd_prefix_lost(uplink);
|
||||
assert(uplink);
|
||||
assert(uplink->manager);
|
||||
|
||||
dhcp_pd_prefix_lost(uplink, NETWORK_CONFIG_SOURCE_DHCP4);
|
||||
|
||||
if (uplink->dhcp4_6rd_tunnel_name &&
|
||||
link_get_by_name(uplink->manager, uplink->dhcp4_6rd_tunnel_name, &tunnel) >= 0)
|
||||
(void) link_remove(tunnel);
|
||||
}
|
||||
|
||||
void dhcp6_pd_prefix_lost(Link *uplink) {
|
||||
dhcp_pd_prefix_lost(uplink, NETWORK_CONFIG_SOURCE_DHCP6);
|
||||
}
|
||||
|
||||
static int dhcp4_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
|
||||
int r;
|
||||
|
||||
|
@ -1005,9 +1038,11 @@ int dhcp4_pd_prefix_acquired(Link *uplink) {
|
|||
return r;
|
||||
|
||||
/* Request unreachable route */
|
||||
dhcp_pd_mark_unreachable_route(uplink->manager, NETWORK_CONFIG_SOURCE_DHCP4);
|
||||
r = dhcp4_request_unreachable_route(uplink, &pd_prefix, pd_prefixlen, lifetime_usec, &server_address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
(void) dhcp_pd_remove_unreachable_route(uplink->manager, NETWORK_CONFIG_SOURCE_DHCP4, /* only_marked = */ true);
|
||||
|
||||
/* Create or update 6rd SIT tunnel device. */
|
||||
r = dhcp4_pd_create_6rd_tunnel(uplink, dhcp4_pd_6rd_tunnel_create_handler);
|
||||
|
@ -1085,11 +1120,14 @@ int dhcp6_pd_prefix_acquired(Link *uplink) {
|
|||
|
||||
assert(uplink);
|
||||
assert(uplink->dhcp6_lease);
|
||||
assert(uplink->manager);
|
||||
|
||||
r = sd_dhcp6_lease_get_server_address(uplink->dhcp6_lease, &server_address.in6);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(uplink, r, "Failed to get server address of DHCPv6 lease: %m");
|
||||
|
||||
dhcp_pd_mark_unreachable_route(uplink->manager, NETWORK_CONFIG_SOURCE_DHCP6);
|
||||
|
||||
/* First, logs acquired prefixes and request unreachable routes. */
|
||||
FOREACH_DHCP6_PD_PREFIX(uplink->dhcp6_lease) {
|
||||
usec_t lifetime_valid_usec;
|
||||
|
@ -1120,6 +1158,8 @@ int dhcp6_pd_prefix_acquired(Link *uplink) {
|
|||
return r;
|
||||
}
|
||||
|
||||
(void) dhcp_pd_remove_unreachable_route(uplink->manager, NETWORK_CONFIG_SOURCE_DHCP6, /* only_marked = */ true);
|
||||
|
||||
/* Then, assign subnet prefixes. */
|
||||
HASHMAP_FOREACH(link, uplink->manager->links_by_index) {
|
||||
if (!dhcp_pd_is_uplink(link, uplink, /* accept_auto = */ true))
|
||||
|
@ -1254,14 +1294,21 @@ int dhcp_request_prefix_delegation(Link *link) {
|
|||
|
||||
int link_drop_dhcp_pd_config(Link *link, Network *network) {
|
||||
assert(link);
|
||||
assert(network);
|
||||
assert(link->network);
|
||||
|
||||
if (!link_dhcp_pd_is_enabled(link))
|
||||
if (link->network == network)
|
||||
return 0; /* .network file is unchanged. It is not necessary to reconfigure the client. */
|
||||
|
||||
if (!link_dhcp_pd_is_enabled(link)) /* Disabled now, drop all configs. */
|
||||
return dhcp_pd_remove(link, /* only_marked = */ false);
|
||||
|
||||
/* If previously explicitly disabled, then there is nothing we need to drop.
|
||||
* If this is called on start up, we do not know the previous settings, assume nothing changed. */
|
||||
if (!network || !network->dhcp_pd)
|
||||
return 0;
|
||||
|
||||
/* If will be disabled or at least one config is changed, then drop all configurations. */
|
||||
if (!network->dhcp_pd ||
|
||||
link->network->dhcp_pd_assign != network->dhcp_pd_assign ||
|
||||
/* If at least one setting is changed, then drop all configurations. */
|
||||
if (link->network->dhcp_pd_assign != network->dhcp_pd_assign ||
|
||||
(link->network->dhcp_pd_assign &&
|
||||
(link->network->dhcp_pd_manage_temporary_address != network->dhcp_pd_manage_temporary_address ||
|
||||
!set_equal(link->network->dhcp_pd_tokens, network->dhcp_pd_tokens))) ||
|
||||
|
|
|
@ -20,8 +20,8 @@ int dhcp_request_prefix_delegation(Link *link);
|
|||
int link_drop_dhcp_pd_config(Link *link, Network *network);
|
||||
int dhcp4_pd_prefix_acquired(Link *uplink);
|
||||
int dhcp6_pd_prefix_acquired(Link *uplink);
|
||||
void dhcp_pd_prefix_lost(Link *uplink);
|
||||
void dhcp4_pd_prefix_lost(Link *uplink);
|
||||
void dhcp6_pd_prefix_lost(Link *uplink);
|
||||
int dhcp_pd_reconfigure_address(Address *address, Link *link);
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_pd_subnet_id);
|
||||
|
|
|
@ -247,7 +247,7 @@ static int dhcp4_remove_address_and_routes(Link *link, bool only_marked) {
|
|||
SET_FOREACH(route, link->manager->routes) {
|
||||
if (route->source != NETWORK_CONFIG_SOURCE_DHCP4)
|
||||
continue;
|
||||
if (route->nexthop.ifindex != 0 && route->nexthop.ifindex != link->ifindex)
|
||||
if (route->nexthop.ifindex != link->ifindex)
|
||||
continue;
|
||||
if (only_marked && !route_is_marked(route))
|
||||
continue;
|
||||
|
@ -915,7 +915,7 @@ static int dhcp4_request_address(Link *link, bool announce) {
|
|||
if (r < 0)
|
||||
return log_link_debug_errno(link, r, "DHCP error: failed to get DHCP server IP address: %m");
|
||||
|
||||
if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
|
||||
if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC)) {
|
||||
r = sd_dhcp_lease_get_lifetime_timestamp(link->dhcp_lease, CLOCK_BOOTTIME, &lifetime_usec);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "DHCP error: failed to get lifetime: %m");
|
||||
|
@ -1168,7 +1168,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
|
|||
|
||||
switch (event) {
|
||||
case SD_DHCP_CLIENT_EVENT_STOP:
|
||||
if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
|
||||
if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC)) {
|
||||
log_link_notice(link, "DHCPv4 connection considered critical, ignoring request to reconfigure it.");
|
||||
return 0;
|
||||
}
|
||||
|
@ -1199,7 +1199,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
|
|||
|
||||
break;
|
||||
case SD_DHCP_CLIENT_EVENT_EXPIRED:
|
||||
if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
|
||||
if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC)) {
|
||||
log_link_notice(link, "DHCPv4 connection considered critical, ignoring request to reconfigure it.");
|
||||
return 0;
|
||||
}
|
||||
|
@ -1214,7 +1214,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
|
|||
|
||||
break;
|
||||
case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
|
||||
if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
|
||||
if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC)) {
|
||||
log_link_notice(link, "DHCPv4 connection considered critical, ignoring request to reconfigure it.");
|
||||
return 0;
|
||||
}
|
||||
|
@ -1400,8 +1400,8 @@ static int dhcp4_set_request_address(Link *link) {
|
|||
return sd_dhcp_client_set_request_address(link->dhcp_client, &link->network->dhcp_request_address);
|
||||
}
|
||||
|
||||
/* 3. If KeepConfiguration=dhcp, use a foreign dynamic address. */
|
||||
if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
|
||||
/* 3. If KeepConfiguration=dynamic, use a foreign dynamic address. */
|
||||
if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC))
|
||||
return 0;
|
||||
|
||||
SET_FOREACH(a, link->addresses) {
|
||||
|
@ -1837,15 +1837,22 @@ int link_drop_dhcp4_config(Link *link, Network *network) {
|
|||
int ret = 0;
|
||||
|
||||
assert(link);
|
||||
assert(network);
|
||||
assert(link->network);
|
||||
|
||||
if (!link_dhcp4_enabled(link))
|
||||
return 0; /* Currently DHCPv4 client is not enabled, there is nothing we need to drop. */
|
||||
if (link->network == network)
|
||||
return 0; /* .network file is unchanged. It is not necessary to reconfigure the client. */
|
||||
|
||||
if (!FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4))
|
||||
/* Currently enabled but will be disabled. Stop the client and drop the lease. */
|
||||
if (!link_dhcp4_enabled(link)) {
|
||||
/* DHCP client is disabled. Stop the client if it is running and drop the lease. */
|
||||
ret = sd_dhcp_client_stop(link->dhcp_client);
|
||||
|
||||
/* Also explicitly drop DHCPv4 address and routes. Why? This is for the case when the DHCPv4
|
||||
* client was enabled on the previous invocation of networkd, but when it is restarted, a new
|
||||
* .network file may match to the interface, and DHCPv4 client may be disabled. In that case,
|
||||
* the DHCPv4 client is not running, hence sd_dhcp_client_stop() in the above does nothing. */
|
||||
RET_GATHER(ret, dhcp4_remove_address_and_routes(link, /* only_marked = */ false));
|
||||
}
|
||||
|
||||
/* Even if the client is currently enabled and also enabled in the new .network file, detailed
|
||||
* settings for the client may be different. Let's unref() the client. But do not unref() the lease.
|
||||
* it will be unref()ed later when a new lease is acquired. */
|
||||
|
|
|
@ -307,7 +307,6 @@ static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) {
|
|||
int r;
|
||||
|
||||
link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP6);
|
||||
manager_mark_routes(link->manager, NULL, NETWORK_CONFIG_SOURCE_DHCP6);
|
||||
|
||||
r = sd_dhcp6_client_get_lease(client, &lease);
|
||||
if (r < 0)
|
||||
|
@ -330,7 +329,7 @@ static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) {
|
|||
return r;
|
||||
} else if (sd_dhcp6_lease_has_pd_prefix(lease_old))
|
||||
/* When we had PD prefixes but not now, we need to remove them. */
|
||||
dhcp_pd_prefix_lost(link);
|
||||
dhcp6_pd_prefix_lost(link);
|
||||
|
||||
if (link->dhcp6_messages == 0) {
|
||||
link->dhcp6_configured = true;
|
||||
|
@ -377,7 +376,7 @@ static int dhcp6_lease_lost(Link *link) {
|
|||
log_link_info(link, "DHCPv6 lease lost");
|
||||
|
||||
if (sd_dhcp6_lease_has_pd_prefix(link->dhcp6_lease))
|
||||
dhcp_pd_prefix_lost(link);
|
||||
dhcp6_pd_prefix_lost(link);
|
||||
|
||||
link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease);
|
||||
|
||||
|
@ -845,15 +844,19 @@ int link_drop_dhcp6_config(Link *link, Network *network) {
|
|||
int ret = 0;
|
||||
|
||||
assert(link);
|
||||
assert(network);
|
||||
assert(link->network);
|
||||
|
||||
if (!link_dhcp6_enabled(link))
|
||||
return 0; /* Currently DHCPv6 client is not enabled, there is nothing we need to drop. */
|
||||
if (link->network == network)
|
||||
return 0; /* .network file is unchanged. It is not necessary to reconfigure the client. */
|
||||
|
||||
if (!FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV6))
|
||||
/* Currently enabled but will be disabled. Stop the client and drop the lease. */
|
||||
if (!link_dhcp6_enabled(link)) {
|
||||
/* DHCPv6 client is disabled. Stop the client if it is running and drop the lease. */
|
||||
ret = sd_dhcp6_client_stop(link->dhcp6_client);
|
||||
|
||||
/* Also explicitly drop DHCPv6 addresses and routes. See also link_drop_dhcp4_config(). */
|
||||
RET_GATHER(ret, dhcp6_remove(link, /* only_marked = */ false));
|
||||
}
|
||||
|
||||
/* Even if the client is currently enabled and also enabled in the new .network file, detailed
|
||||
* settings for the client may be different. Let's unref() the client. But do not unref() the lease.
|
||||
* it will be unref()ed later when a new lease is acquired. */
|
||||
|
|
|
@ -175,6 +175,8 @@ static int ipv4ll_check_mac(sd_ipv4ll *ll, const struct ether_addr *mac, void *u
|
|||
}
|
||||
|
||||
static int ipv4ll_set_address(Link *link) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
assert(link->ipv4ll);
|
||||
|
@ -193,6 +195,27 @@ static int ipv4ll_set_address(Link *link) {
|
|||
if (in4_addr_is_set(&link->network->ipv4ll_start_address))
|
||||
return sd_ipv4ll_set_address(link->ipv4ll, &link->network->ipv4ll_start_address);
|
||||
|
||||
/* 3. If KeepConfiguration=dynamic, use a foreign IPv4LL address. */
|
||||
if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC))
|
||||
return 0;
|
||||
|
||||
SET_FOREACH(a, link->addresses) {
|
||||
if (a->source != NETWORK_CONFIG_SOURCE_FOREIGN)
|
||||
continue;
|
||||
if (a->family != AF_INET)
|
||||
continue;
|
||||
if (!in4_addr_is_link_local_dynamic(&a->in_addr.in))
|
||||
continue;
|
||||
|
||||
r = sd_ipv4ll_set_address(link->ipv4ll, &a->in_addr.in);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Make the address not removed by link_drop_unmanaged_addresses(). */
|
||||
a->source = NETWORK_CONFIG_SOURCE_IPV4LL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -206,7 +229,7 @@ int ipv4ll_configure(Link *link) {
|
|||
return 0;
|
||||
|
||||
if (link->ipv4ll)
|
||||
return -EBUSY;
|
||||
return 0;
|
||||
|
||||
r = sd_ipv4ll_new(&link->ipv4ll);
|
||||
if (r < 0)
|
||||
|
@ -246,19 +269,27 @@ int link_drop_ipv4ll_config(Link *link, Network *network) {
|
|||
int ret = 0;
|
||||
|
||||
assert(link);
|
||||
assert(network);
|
||||
assert(link->network);
|
||||
|
||||
if (!link_ipv4ll_enabled(link))
|
||||
return 0;
|
||||
if (link->network == network)
|
||||
return 0; /* .network file is unchanged. It is not necessary to reconfigure the client. */
|
||||
|
||||
Network *saved = link->network;
|
||||
link->network = network;
|
||||
bool enabled = link_ipv4ll_enabled(link);
|
||||
link->network = saved;
|
||||
|
||||
if (!enabled)
|
||||
if (!link_ipv4ll_enabled(link)) {
|
||||
/* The client is disabled. Stop if it is running, and drop the address. */
|
||||
ret = sd_ipv4ll_stop(link->ipv4ll);
|
||||
|
||||
/* Also, explicitly drop the address for the case that this is called on start up.
|
||||
* See also comments in link_drop_dhcp4_config(). */
|
||||
Address *a;
|
||||
SET_FOREACH(a, link->addresses) {
|
||||
if (a->source != NETWORK_CONFIG_SOURCE_IPV4LL)
|
||||
continue;
|
||||
|
||||
assert(a->family == AF_INET);
|
||||
RET_GATHER(ret, address_remove_and_cancel(a, link));
|
||||
}
|
||||
}
|
||||
|
||||
link->ipv4ll = sd_ipv4ll_unref(link->ipv4ll);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -28,55 +28,79 @@
|
|||
#include "user-util.h"
|
||||
#include "wifi-util.h"
|
||||
|
||||
static int address_append_json(Address *address, sd_json_variant **array) {
|
||||
_cleanup_free_ char *scope = NULL, *flags = NULL, *state = NULL;
|
||||
static int address_append_json(Address *address, bool serializing, sd_json_variant **array) {
|
||||
_cleanup_free_ char *state = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
int r;
|
||||
|
||||
assert(address);
|
||||
assert(array);
|
||||
|
||||
r = route_scope_to_string_alloc(address->scope, &scope);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = address_flags_to_string_alloc(address->flags, address->family, &flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = network_config_state_to_string_alloc(address->state, &state);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_json_variant_append_arraybo(
|
||||
array,
|
||||
r = sd_json_buildo(
|
||||
&v,
|
||||
SD_JSON_BUILD_PAIR_INTEGER("Family", address->family),
|
||||
JSON_BUILD_PAIR_IN_ADDR("Address", &address->in_addr, address->family),
|
||||
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Peer", &address->in_addr_peer, address->family),
|
||||
JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Broadcast", &address->broadcast),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("PrefixLength", address->prefixlen),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("Scope", address->scope),
|
||||
SD_JSON_BUILD_PAIR_STRING("ScopeString", scope),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("Flags", address->flags),
|
||||
SD_JSON_BUILD_PAIR_STRING("FlagsString", flags),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("Label", address->label),
|
||||
JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUSec", address->lifetime_preferred_usec),
|
||||
JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUsec", address->lifetime_preferred_usec), /* for backward compat */
|
||||
JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUSec", address->lifetime_valid_usec),
|
||||
JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUsec", address->lifetime_valid_usec), /* for backward compat */
|
||||
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(address->source)),
|
||||
SD_JSON_BUILD_PAIR_STRING("ConfigState", state),
|
||||
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", &address->provider, address->family));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!serializing) {
|
||||
_cleanup_free_ char *scope = NULL, *flags = NULL;
|
||||
|
||||
r = route_scope_to_string_alloc(address->scope, &scope);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = address_flags_to_string_alloc(address->flags, address->family, &flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_merge_objectbo(
|
||||
&v,
|
||||
JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Broadcast", &address->broadcast),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("Scope", address->scope),
|
||||
SD_JSON_BUILD_PAIR_STRING("ScopeString", scope),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("Flags", address->flags),
|
||||
SD_JSON_BUILD_PAIR_STRING("FlagsString", flags),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("Label", address->label),
|
||||
JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUSec", address->lifetime_preferred_usec),
|
||||
JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUsec", address->lifetime_preferred_usec), /* for backward compat */
|
||||
JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUSec", address->lifetime_valid_usec),
|
||||
JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUsec", address->lifetime_valid_usec), /* for backward compat */
|
||||
SD_JSON_BUILD_PAIR_STRING("ConfigState", state));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_json_variant_append_array(array, v);
|
||||
}
|
||||
|
||||
static int addresses_append_json(Set *addresses, sd_json_variant **v) {
|
||||
int addresses_append_json(Link *link, bool serializing, sd_json_variant **v) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
|
||||
Address *address;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(v);
|
||||
|
||||
SET_FOREACH(address, addresses) {
|
||||
r = address_append_json(address, &array);
|
||||
SET_FOREACH(address, link->addresses) {
|
||||
if (serializing) {
|
||||
if (address->source == NETWORK_CONFIG_SOURCE_FOREIGN)
|
||||
continue;
|
||||
if (!address_is_ready(address))
|
||||
continue;
|
||||
|
||||
log_address_debug(address, "Serializing", link);
|
||||
}
|
||||
|
||||
r = address_append_json(address, serializing, &array);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -199,35 +223,20 @@ static int nexthops_append_json(Manager *manager, int ifindex, sd_json_variant *
|
|||
return json_variant_set_field_non_null(v, "NextHops", array);
|
||||
}
|
||||
|
||||
static int route_append_json(Route *route, sd_json_variant **array) {
|
||||
_cleanup_free_ char *scope = NULL, *protocol = NULL, *table = NULL, *flags = NULL, *state = NULL;
|
||||
static int route_append_json(Route *route, bool serializing, sd_json_variant **array) {
|
||||
_cleanup_free_ char *state = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
int r;
|
||||
|
||||
assert(route);
|
||||
assert(array);
|
||||
|
||||
r = route_scope_to_string_alloc(route->scope, &scope);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = route_protocol_to_string_alloc(route->protocol, &protocol);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_get_route_table_to_string(route->manager, route->table, /* append_num = */ false, &table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = route_flags_to_string_alloc(route->flags, &flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = network_config_state_to_string_alloc(route->state, &state);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_json_variant_append_arraybo(
|
||||
array,
|
||||
r = sd_json_buildo(
|
||||
&v,
|
||||
SD_JSON_BUILD_PAIR_INTEGER("Family", route->family),
|
||||
JSON_BUILD_PAIR_IN_ADDR("Destination", &route->dst, route->family),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("DestinationPrefixLength", route->dst_prefixlen),
|
||||
|
@ -238,26 +247,63 @@ static int route_append_json(Route *route, sd_json_variant **array) {
|
|||
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("PreferredSource", &route->prefsrc, route->family),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("TOS", route->tos),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("Scope", route->scope),
|
||||
SD_JSON_BUILD_PAIR_STRING("ScopeString", scope),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("Protocol", route->protocol),
|
||||
SD_JSON_BUILD_PAIR_STRING("ProtocolString", protocol),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("Type", route->type),
|
||||
SD_JSON_BUILD_PAIR_STRING("TypeString", route_type_to_string(route->type)),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("Priority", route->priority),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("Table", route->table),
|
||||
SD_JSON_BUILD_PAIR_STRING("TableString", table),
|
||||
JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("MTU", route_metric_get(&route->metric, RTAX_MTU)),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("Preference", route->pref),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("Flags", route->flags),
|
||||
SD_JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)),
|
||||
JSON_BUILD_PAIR_FINITE_USEC("LifetimeUSec", route->lifetime_usec),
|
||||
JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("NextHopID", route->nexthop_id),
|
||||
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(route->source)),
|
||||
SD_JSON_BUILD_PAIR_STRING("ConfigState", state),
|
||||
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", &route->provider, route->family));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (serializing) {
|
||||
r = sd_json_variant_merge_objectbo(
|
||||
&v,
|
||||
SD_JSON_BUILD_PAIR_INTEGER("InterfaceIndex", route->nexthop.ifindex),
|
||||
JSON_BUILD_PAIR_BYTE_ARRAY_NON_EMPTY("Metrics", route->metric.metrics, route->metric.n_metrics),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("TCPCongestionControlAlgorithm", route->metric.tcp_congestion_control_algo));
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
_cleanup_free_ char *scope = NULL, *protocol = NULL, *table = NULL, *flags = NULL;
|
||||
|
||||
r = route_scope_to_string_alloc(route->scope, &scope);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = route_protocol_to_string_alloc(route->protocol, &protocol);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_get_route_table_to_string(route->manager, route->table, /* append_num = */ false, &table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = route_flags_to_string_alloc(route->flags, &flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_merge_objectbo(
|
||||
&v,
|
||||
SD_JSON_BUILD_PAIR_STRING("ScopeString", scope),
|
||||
SD_JSON_BUILD_PAIR_STRING("ProtocolString", protocol),
|
||||
SD_JSON_BUILD_PAIR_STRING("TypeString", route_type_to_string(route->type)),
|
||||
SD_JSON_BUILD_PAIR_STRING("TableString", table),
|
||||
JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("MTU", route_metric_get(&route->metric, RTAX_MTU)),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("Preference", route->pref),
|
||||
SD_JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)),
|
||||
JSON_BUILD_PAIR_FINITE_USEC("LifetimeUSec", route->lifetime_usec),
|
||||
SD_JSON_BUILD_PAIR_STRING("ConfigState", state));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_json_variant_append_array(array, v);
|
||||
}
|
||||
|
||||
static int routes_append_json(Manager *manager, int ifindex, sd_json_variant **v) {
|
||||
int routes_append_json(Manager *manager, int ifindex, sd_json_variant **v) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
|
||||
Route *route;
|
||||
int r;
|
||||
|
@ -266,10 +312,21 @@ static int routes_append_json(Manager *manager, int ifindex, sd_json_variant **v
|
|||
assert(v);
|
||||
|
||||
SET_FOREACH(route, manager->routes) {
|
||||
if (route->nexthop.ifindex != ifindex)
|
||||
continue;
|
||||
if (ifindex >= 0) {
|
||||
if (route->nexthop.ifindex != ifindex)
|
||||
continue;
|
||||
} else {
|
||||
/* negative ifindex means we are serializing now. */
|
||||
|
||||
r = route_append_json(route, &array);
|
||||
if (route->source == NETWORK_CONFIG_SOURCE_FOREIGN)
|
||||
continue;
|
||||
if (!route_exists(route))
|
||||
continue;
|
||||
|
||||
log_route_debug(route, "Serializing", manager);
|
||||
}
|
||||
|
||||
r = route_append_json(route, /* serializing = */ ifindex < 0, &array);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -1413,7 +1470,7 @@ int link_build_json(Link *link, sd_json_variant **ret) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = addresses_append_json(link->addresses, &v);
|
||||
r = addresses_append_json(link, /* serializing = */ false, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
typedef struct Link Link;
|
||||
typedef struct Manager Manager;
|
||||
|
||||
int addresses_append_json(Link *link, bool serializing, sd_json_variant **v);
|
||||
int routes_append_json(Manager *manager, int ifindex, sd_json_variant **v);
|
||||
int link_build_json(Link *link, sd_json_variant **ret);
|
||||
int manager_build_json(Manager *manager, sd_json_variant **ret);
|
||||
|
|
|
@ -369,23 +369,41 @@ void link_set_state(Link *link, LinkState state) {
|
|||
link_dirty(link);
|
||||
}
|
||||
|
||||
int link_stop_engines(Link *link, bool may_keep_dhcp) {
|
||||
int link_stop_engines(Link *link, bool may_keep_dynamic) {
|
||||
int r, ret = 0;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
assert(link->manager->event);
|
||||
|
||||
bool keep_dhcp =
|
||||
may_keep_dhcp &&
|
||||
bool keep_dynamic =
|
||||
may_keep_dynamic &&
|
||||
link->network &&
|
||||
(link->manager->state == MANAGER_RESTARTING ||
|
||||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP));
|
||||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC_ON_STOP));
|
||||
|
||||
if (!keep_dhcp) {
|
||||
if (!keep_dynamic) {
|
||||
r = sd_dhcp_client_stop(link->dhcp_client);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCPv4 client: %m"));
|
||||
|
||||
r = sd_ipv4ll_stop(link->ipv4ll);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv4 link-local: %m"));
|
||||
|
||||
r = sd_dhcp6_client_stop(link->dhcp6_client);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m"));
|
||||
|
||||
r = dhcp_pd_remove(link, /* only_marked = */ false);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not remove DHCPv6 PD addresses and routes: %m"));
|
||||
|
||||
r = ndisc_stop(link);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery: %m"));
|
||||
|
||||
ndisc_flush(link);
|
||||
}
|
||||
|
||||
r = sd_dhcp_server_stop(link->dhcp_server);
|
||||
|
@ -400,28 +418,10 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
|
|||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop LLDP Tx: %m"));
|
||||
|
||||
r = sd_ipv4ll_stop(link->ipv4ll);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv4 link-local: %m"));
|
||||
|
||||
r = ipv4acd_stop(link);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv4 ACD client: %m"));
|
||||
|
||||
r = sd_dhcp6_client_stop(link->dhcp6_client);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m"));
|
||||
|
||||
r = dhcp_pd_remove(link, /* only_marked = */ false);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not remove DHCPv6 PD addresses and routes: %m"));
|
||||
|
||||
r = ndisc_stop(link);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery: %m"));
|
||||
|
||||
ndisc_flush(link);
|
||||
|
||||
r = sd_radv_stop(link->radv);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Advertisement: %m"));
|
||||
|
@ -449,7 +449,7 @@ void link_enter_failed(Link *link) {
|
|||
return;
|
||||
|
||||
stop:
|
||||
(void) link_stop_engines(link, /* may_keep_dhcp = */ false);
|
||||
(void) link_stop_engines(link, /* may_keep_dynamic = */ false);
|
||||
}
|
||||
|
||||
void link_check_ready(Link *link) {
|
||||
|
@ -1121,21 +1121,30 @@ static int link_drop_dynamic_config(Link *link, Network *network) {
|
|||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(network);
|
||||
assert(link->network);
|
||||
|
||||
/* Drop unnecessary dynamic configurations gracefully, e.g. drop DHCP lease in the case that
|
||||
* previously DHCP=yes and now DHCP=no, but keep DHCP lease when DHCP setting is unchanged. */
|
||||
|
||||
r = link_drop_ndisc_config(link, network);
|
||||
RET_GATHER(r, link_drop_radv_config(link, network));
|
||||
RET_GATHER(r, link_drop_radv_config(link, network)); /* Stop before dropping DHCP-PD prefixes. */
|
||||
RET_GATHER(r, link_drop_ipv4ll_config(link, network)); /* Stop before DHCPv4 client. */
|
||||
RET_GATHER(r, link_drop_dhcp4_config(link, network));
|
||||
RET_GATHER(r, link_drop_dhcp6_config(link, network));
|
||||
RET_GATHER(r, link_drop_dhcp_pd_config(link, network));
|
||||
RET_GATHER(r, link_drop_ipv4ll_config(link, network));
|
||||
link->dhcp_server = sd_dhcp_server_unref(link->dhcp_server);
|
||||
link->lldp_rx = sd_lldp_rx_unref(link->lldp_rx); /* TODO: keep the received neighbors. */
|
||||
link->lldp_tx = sd_lldp_tx_unref(link->lldp_tx);
|
||||
|
||||
/* Even if we do not release DHCP lease or so, reset 'configured' flags. Otherwise, e.g. if
|
||||
* previously UseDNS= was disabled but is now enabled, link will enter configured state before
|
||||
* expected DNS servers being acquired. */
|
||||
link->ipv4ll_address_configured = false;
|
||||
link->dhcp4_configured = false;
|
||||
link->dhcp6_configured = false;
|
||||
link->dhcp_pd_configured = false;
|
||||
link->ndisc_configured = false;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -1330,7 +1339,7 @@ static void link_enter_unmanaged(Link *link) {
|
|||
log_link_full(link, link->state == LINK_STATE_INITIALIZED ? LOG_DEBUG : LOG_INFO,
|
||||
"Unmanaging interface.");
|
||||
|
||||
(void) link_stop_engines(link, /* may_keep_dhcp = */ false);
|
||||
(void) link_stop_engines(link, /* may_keep_dynamic = */ false);
|
||||
(void) link_drop_requests(link);
|
||||
(void) link_drop_static_config(link);
|
||||
|
||||
|
@ -1385,29 +1394,7 @@ int link_reconfigure_impl(Link *link, LinkReconfigurationFlag flags) {
|
|||
joined,
|
||||
isempty(joined) ? "" : ")");
|
||||
|
||||
/* Dropping old .network file */
|
||||
|
||||
if (FLAGS_SET(flags, LINK_RECONFIGURE_CLEANLY)) {
|
||||
/* Remove all static configurations. Note, dynamic configurations are dropped by
|
||||
* link_stop_engines(), and foreign configurations will be removed later by
|
||||
* link_configure() -> link_drop_unmanaged_config(). */
|
||||
r = link_drop_static_config(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Stop DHCP client and friends, and drop dynamic configurations like DHCP address. */
|
||||
r = link_stop_engines(link, /* may_keep_dhcp = */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Free DHCP client and friends. */
|
||||
link_free_engines(link);
|
||||
} else {
|
||||
r = link_drop_dynamic_config(link, network);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Dropping configurations based on the old .network file. */
|
||||
r = link_drop_requests(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -1418,10 +1405,30 @@ int link_reconfigure_impl(Link *link, LinkReconfigurationFlag flags) {
|
|||
* map here, as it depends on .network files assigned to other links. */
|
||||
link_free_bound_to_list(link);
|
||||
|
||||
link->network = network_unref(link->network);
|
||||
_cleanup_(network_unrefp) Network *old_network = TAKE_PTR(link->network);
|
||||
|
||||
/* Then, apply new .network file */
|
||||
link->network = network_ref(network);
|
||||
|
||||
if (FLAGS_SET(network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC) ||
|
||||
!FLAGS_SET(flags, LINK_RECONFIGURE_CLEANLY)) {
|
||||
/* To make 'networkctl reconfigure INTERFACE' work safely for an interface whose new .network
|
||||
* file has KeepConfiguration=dynamic or yes, even if a clean reconfiguration is requested,
|
||||
* drop only unnecessary or possibly being changed dynamic configurations here. */
|
||||
r = link_drop_dynamic_config(link, old_network);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
/* Otherwise, stop DHCP client and friends unconditionally, and drop all dynamic
|
||||
* configurations like DHCP address and routes. */
|
||||
r = link_stop_engines(link, /* may_keep_dhcp = */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Free DHCP client and friends. */
|
||||
link_free_engines(link);
|
||||
}
|
||||
|
||||
link_update_operstate(link, true);
|
||||
link_dirty(link);
|
||||
|
||||
|
@ -1792,7 +1799,7 @@ static int link_carrier_lost_impl(Link *link) {
|
|||
if (!link->network)
|
||||
return ret;
|
||||
|
||||
RET_GATHER(ret, link_stop_engines(link, false));
|
||||
RET_GATHER(ret, link_stop_engines(link, /* may_keep_dynamic = */ false));
|
||||
RET_GATHER(ret, link_drop_static_config(link));
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -179,7 +179,7 @@ typedef struct Link {
|
|||
Set *ndisc_dnr;
|
||||
uint32_t ndisc_mtu;
|
||||
unsigned ndisc_messages;
|
||||
bool ndisc_configured:1;
|
||||
bool ndisc_configured;
|
||||
|
||||
sd_radv *radv;
|
||||
|
||||
|
@ -257,7 +257,7 @@ bool link_ipv6_enabled(Link *link);
|
|||
int link_ipv6ll_gained(Link *link);
|
||||
bool link_has_ipv6_connectivity(Link *link);
|
||||
|
||||
int link_stop_engines(Link *link, bool may_keep_dhcp);
|
||||
int link_stop_engines(Link *link, bool may_keep_dynamic);
|
||||
|
||||
const char* link_state_to_string(LinkState s) _const_;
|
||||
LinkState link_state_from_string(const char *s) _pure_;
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "networkd-queue.h"
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-routing-policy-rule.h"
|
||||
#include "networkd-serialize.h"
|
||||
#include "networkd-speed-meter.h"
|
||||
#include "networkd-state-file.h"
|
||||
#include "networkd-wifi.h"
|
||||
|
@ -245,6 +246,9 @@ static int manager_listen_fds(Manager *m, int *ret_rtnl_fd) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (manager_set_serialization_fd(m, fd, names[i]) >= 0)
|
||||
continue;
|
||||
|
||||
if (manager_add_tuntap_fd(m, fd, names[i]) >= 0)
|
||||
continue;
|
||||
|
||||
|
@ -442,6 +446,7 @@ static int manager_post_handler(sd_event_source *s, void *userdata) {
|
|||
fw_ctx_get_reply_callback_count(manager->fw_ctx) > 0)
|
||||
return 0; /* There are some message calls waiting for their replies. */
|
||||
|
||||
(void) manager_serialize(manager);
|
||||
manager->state = MANAGER_STOPPED;
|
||||
return sd_event_exit(sd_event_source_get_event(s), 0);
|
||||
|
||||
|
@ -476,7 +481,7 @@ static int manager_stop(Manager *manager, ManagerState state) {
|
|||
|
||||
Link *link;
|
||||
HASHMAP_FOREACH(link, manager->links_by_index)
|
||||
(void) link_stop_engines(link, /* may_keep_dhcp = */ true);
|
||||
(void) link_stop_engines(link, /* may_keep_dynamic = */ true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -503,8 +508,8 @@ static int manager_set_keep_configuration(Manager *m) {
|
|||
assert(m);
|
||||
|
||||
if (in_initrd()) {
|
||||
log_debug("Running in initrd, keep DHCPv4 addresses on stopping networkd by default.");
|
||||
m->keep_configuration = KEEP_CONFIGURATION_DHCP_ON_STOP;
|
||||
log_debug("Running in initrd, keep dynamically assigned configurations on stopping networkd by default.");
|
||||
m->keep_configuration = KEEP_CONFIGURATION_DYNAMIC_ON_STOP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -646,6 +651,7 @@ int manager_new(Manager **ret, bool test_mode) {
|
|||
.dhcp6_duid.type = DUID_TYPE_EN,
|
||||
.duid_product_uuid.type = DUID_TYPE_UUID,
|
||||
.dhcp_server_persist_leases = true,
|
||||
.serialization_fd = -EBADF,
|
||||
.ip_forwarding = { -1, -1, },
|
||||
#if HAVE_VMLINUX_H
|
||||
.cgroup_fd = -EBADF,
|
||||
|
@ -727,6 +733,8 @@ Manager* manager_free(Manager *m) {
|
|||
|
||||
m->fw_ctx = fw_ctx_free(m->fw_ctx);
|
||||
|
||||
m->serialization_fd = safe_close(m->serialization_fd);
|
||||
|
||||
return mfree(m);
|
||||
}
|
||||
|
||||
|
|
|
@ -129,6 +129,8 @@ struct Manager {
|
|||
|
||||
unsigned reloading;
|
||||
|
||||
int serialization_fd;
|
||||
|
||||
/* sysctl */
|
||||
int ip_forwarding[2];
|
||||
#if HAVE_VMLINUX_H
|
||||
|
|
|
@ -2672,28 +2672,39 @@ int link_drop_ndisc_config(Link *link, Network *network) {
|
|||
int r, ret = 0;
|
||||
|
||||
assert(link);
|
||||
assert(network);
|
||||
assert(link->network);
|
||||
|
||||
if (!link_ndisc_enabled(link))
|
||||
return 0; /* Currently DHCPv4 client is not enabled, there is nothing we need to drop. */
|
||||
if (link->network == network)
|
||||
return 0; /* .network file is unchanged. It is not necessary to reconfigure the client. */
|
||||
|
||||
Network *current = link->network;
|
||||
link->network = network;
|
||||
bool enabled = link_ndisc_enabled(link);
|
||||
link->network = current;
|
||||
|
||||
if (!enabled) {
|
||||
/* Currently enabled but will be disabled. Stop the client and flush configs. */
|
||||
if (!link_ndisc_enabled(link)) {
|
||||
/* NDisc is disabled. Stop the client if it is running and flush configs. */
|
||||
ret = ndisc_stop(link);
|
||||
ndisc_flush(link);
|
||||
link->ndisc = sd_ndisc_unref(link->ndisc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Even if the client is currently enabled and also enabled in the new .network file, detailed
|
||||
/* Even if the client was previously enabled and also enabled in the new .network file, detailed
|
||||
* settings for the client may be different. Let's unref() the client. */
|
||||
link->ndisc = sd_ndisc_unref(link->ndisc);
|
||||
|
||||
/* Get if NDisc was enabled or not. */
|
||||
Network *current = link->network;
|
||||
link->network = network;
|
||||
bool enabled = link_ndisc_enabled(link);
|
||||
link->network = current;
|
||||
|
||||
/* If previously explicitly disabled, there should be nothing to drop.
|
||||
* If we do not know the previous setting of the client, e.g. when networkd is restarted, in that
|
||||
* case we do not have the previous .network file assigned to the interface, then let's assume no
|
||||
* detailed configuration is changed. Hopefully, unmatching configurations will be dropped after
|
||||
* their lifetime. */
|
||||
if (!enabled)
|
||||
return 0;
|
||||
|
||||
assert(network);
|
||||
|
||||
/* Redirect messages will be ignored. Drop configurations based on the previously received redirect
|
||||
* messages. */
|
||||
if (!network->ndisc_use_redirect)
|
||||
|
|
|
@ -1093,15 +1093,53 @@ int config_parse_ignore_carrier_loss(
|
|||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_keep_configuration(
|
||||
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) {
|
||||
|
||||
KeepConfiguration t, *k = ASSERT_PTR(data);
|
||||
Network *network = ASSERT_PTR(userdata);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
*k = ASSERT_PTR(network->manager)->keep_configuration;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* backward compatibility */
|
||||
if (streq(rvalue, "dhcp")) {
|
||||
*k = KEEP_CONFIGURATION_DYNAMIC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (streq(rvalue, "dhcp-on-stop")) {
|
||||
*k = KEEP_CONFIGURATION_DYNAMIC_ON_STOP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
t = keep_configuration_from_string(rvalue);
|
||||
if (t < 0)
|
||||
return log_syntax_parse_error(unit, filename, line, t, lvalue, rvalue);
|
||||
|
||||
*k = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_required_family_for_online, link_required_address_family, AddressFamily);
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_keep_configuration, keep_configuration, KeepConfiguration);
|
||||
|
||||
static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = {
|
||||
[KEEP_CONFIGURATION_NO] = "no",
|
||||
[KEEP_CONFIGURATION_DHCP_ON_STOP] = "dhcp-on-stop",
|
||||
[KEEP_CONFIGURATION_DHCP] = "dhcp",
|
||||
[KEEP_CONFIGURATION_STATIC] = "static",
|
||||
[KEEP_CONFIGURATION_YES] = "yes",
|
||||
[KEEP_CONFIGURATION_NO] = "no",
|
||||
[KEEP_CONFIGURATION_DYNAMIC_ON_STOP] = "dynamic-on-stop",
|
||||
[KEEP_CONFIGURATION_DYNAMIC] = "dynamic",
|
||||
[KEEP_CONFIGURATION_STATIC] = "static",
|
||||
[KEEP_CONFIGURATION_YES] = "yes",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES);
|
||||
|
|
|
@ -32,14 +32,14 @@
|
|||
#include "socket-netlink.h"
|
||||
|
||||
typedef enum KeepConfiguration {
|
||||
KEEP_CONFIGURATION_NO = 0,
|
||||
KEEP_CONFIGURATION_DHCP_ON_START = 1 << 0,
|
||||
KEEP_CONFIGURATION_DHCP_ON_STOP = 1 << 1,
|
||||
KEEP_CONFIGURATION_DHCP = KEEP_CONFIGURATION_DHCP_ON_START | KEEP_CONFIGURATION_DHCP_ON_STOP,
|
||||
KEEP_CONFIGURATION_STATIC = 1 << 2,
|
||||
KEEP_CONFIGURATION_YES = KEEP_CONFIGURATION_DHCP | KEEP_CONFIGURATION_STATIC,
|
||||
KEEP_CONFIGURATION_NO = 0,
|
||||
KEEP_CONFIGURATION_DYNAMIC_ON_START = 1 << 0,
|
||||
KEEP_CONFIGURATION_DYNAMIC_ON_STOP = 1 << 1,
|
||||
KEEP_CONFIGURATION_DYNAMIC = KEEP_CONFIGURATION_DYNAMIC_ON_START | KEEP_CONFIGURATION_DYNAMIC_ON_STOP,
|
||||
KEEP_CONFIGURATION_STATIC = 1 << 2,
|
||||
KEEP_CONFIGURATION_YES = KEEP_CONFIGURATION_DYNAMIC | KEEP_CONFIGURATION_STATIC,
|
||||
_KEEP_CONFIGURATION_MAX,
|
||||
_KEEP_CONFIGURATION_INVALID = -EINVAL,
|
||||
_KEEP_CONFIGURATION_INVALID = -EINVAL,
|
||||
} KeepConfiguration;
|
||||
|
||||
typedef enum ActivationPolicy {
|
||||
|
|
|
@ -649,10 +649,10 @@ int link_drop_radv_config(Link *link, Network *network) {
|
|||
int ret = 0;
|
||||
|
||||
assert(link);
|
||||
assert(network);
|
||||
assert(link->network);
|
||||
|
||||
if (!link_radv_enabled(link))
|
||||
return 0;
|
||||
if (link->network == network)
|
||||
return 0; /* .network file is unchanged. It is not necessary to reconfigure the server. */
|
||||
|
||||
// FIXME: check detailed settings and do not stop if nothing changed.
|
||||
// FIXME: save dynamic prefixes acquired by DHCP-PD.
|
||||
|
|
|
@ -1471,7 +1471,7 @@ int link_drop_routes(Link *link, bool only_static) {
|
|||
continue;
|
||||
|
||||
if (IN_SET(route->protocol, RTPROT_DHCP, RTPROT_RA, RTPROT_REDIRECT) &&
|
||||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
|
||||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,398 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "af-list.h"
|
||||
#include "daemon-util.h"
|
||||
#include "data-fd-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "iovec-util.h"
|
||||
#include "json-util.h"
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-json.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-serialize.h"
|
||||
|
||||
int manager_serialize(Manager *manager) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *array = NULL;
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
|
||||
log_debug("Serializing...");
|
||||
|
||||
Link *link;
|
||||
HASHMAP_FOREACH(link, manager->links_by_index) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *e = NULL;
|
||||
|
||||
/* ignore unmanaged, failed, or removed interfaces. */
|
||||
if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
|
||||
continue;
|
||||
|
||||
r = sd_json_buildo(
|
||||
&e,
|
||||
SD_JSON_BUILD_PAIR_INTEGER("Index", link->ifindex));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = addresses_append_json(link, /* serializing = */ true, &e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_append_array(&array, e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = json_variant_set_field_non_null(&v, "Interfaces", array);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = routes_append_json(manager, /* ifindex = */ -1, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!v) {
|
||||
log_debug("There is nothing to serialize.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
_cleanup_free_ char *dump = NULL;
|
||||
r = sd_json_variant_format(v, /* flags = */ 0, &dump);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
fd = acquire_data_fd(dump);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
r = notify_push_fd(fd, "manager-serialization");
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to push serialization file descriptor: %m");
|
||||
|
||||
log_debug("Serialization completed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_set_serialization_fd(Manager *manager, int fd, const char *name) {
|
||||
assert(manager);
|
||||
assert(fd >= 0);
|
||||
assert(name);
|
||||
|
||||
if (!startswith(name, "manager-serialization"))
|
||||
return -EINVAL;
|
||||
|
||||
if (manager->serialization_fd >= 0)
|
||||
return -EEXIST;
|
||||
|
||||
manager->serialization_fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_network_config_source, NetworkConfigSource, network_config_source_from_string);
|
||||
|
||||
static int json_dispatch_address_family(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
int r, *i = ASSERT_PTR(userdata);
|
||||
int64_t i64;
|
||||
|
||||
assert_return(variant, -EINVAL);
|
||||
|
||||
if (FLAGS_SET(flags, SD_JSON_RELAX) && sd_json_variant_is_null(variant)) {
|
||||
*i = AF_UNSPEC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = sd_json_dispatch_int64(name, variant, flags, &i64);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!IN_SET(i64, AF_INET, AF_INET6) && !(FLAGS_SET(flags, SD_JSON_RELAX) && i64 == AF_UNSPEC))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds for an address family.", strna(name));
|
||||
|
||||
*i = (int) i64;
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct AddressParam {
|
||||
int family;
|
||||
struct iovec address;
|
||||
struct iovec peer;
|
||||
uint8_t prefixlen;
|
||||
NetworkConfigSource source;
|
||||
struct iovec provider;
|
||||
} AddressParam;
|
||||
|
||||
static void address_param_done(AddressParam *p) {
|
||||
assert(p);
|
||||
|
||||
iovec_done(&p->address);
|
||||
iovec_done(&p->peer);
|
||||
iovec_done(&p->provider);
|
||||
}
|
||||
|
||||
static int link_deserialize_address(Link *link, sd_json_variant *v) {
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "Family", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_address_family, offsetof(AddressParam, family), SD_JSON_MANDATORY },
|
||||
{ "Address", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(AddressParam, address), SD_JSON_MANDATORY },
|
||||
{ "Peer", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(AddressParam, peer), 0 },
|
||||
{ "PrefixLength", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(AddressParam, prefixlen), SD_JSON_MANDATORY },
|
||||
{ "ConfigSource", SD_JSON_VARIANT_STRING, json_dispatch_network_config_source, offsetof(AddressParam, source), SD_JSON_MANDATORY },
|
||||
{ "ConfigProvider", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(AddressParam, provider), 0 },
|
||||
{},
|
||||
};
|
||||
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(v);
|
||||
|
||||
_cleanup_(address_param_done) AddressParam p = {};
|
||||
r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
|
||||
if (r < 0)
|
||||
return log_link_debug_errno(link, r, "Failed to dispatch address from json variant: %m");
|
||||
|
||||
if (p.address.iov_len != FAMILY_ADDRESS_SIZE(p.family))
|
||||
return log_link_debug_errno(link, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Dispatched address size (%zu) is incompatible with the family (%s).",
|
||||
p.address.iov_len, af_to_ipv4_ipv6(p.family));
|
||||
|
||||
if (p.peer.iov_len != 0 && p.peer.iov_len != FAMILY_ADDRESS_SIZE(p.family))
|
||||
return log_link_debug_errno(link, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Dispatched peer address size (%zu) is incompatible with the family (%s).",
|
||||
p.peer.iov_len, af_to_ipv4_ipv6(p.family));
|
||||
|
||||
if (p.provider.iov_len != 0 && p.provider.iov_len != FAMILY_ADDRESS_SIZE(p.family))
|
||||
return log_link_debug_errno(link, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Dispatched provider address size (%zu) is incompatible with the family (%s).",
|
||||
p.provider.iov_len, af_to_ipv4_ipv6(p.family));
|
||||
|
||||
Address tmp = {
|
||||
.family = p.family,
|
||||
.prefixlen = p.prefixlen,
|
||||
};
|
||||
|
||||
memcpy_safe(&tmp.in_addr, p.address.iov_base, p.address.iov_len);
|
||||
memcpy_safe(&tmp.in_addr_peer, p.peer.iov_base, p.peer.iov_len);
|
||||
|
||||
Address *address;
|
||||
r = address_get(link, &tmp, &address);
|
||||
if (r < 0) {
|
||||
log_link_debug_errno(link, r, "Cannot find deserialized address %s: %m",
|
||||
IN_ADDR_PREFIX_TO_STRING(tmp.family, &tmp.in_addr, tmp.prefixlen));
|
||||
return 0; /* Already removed? */
|
||||
}
|
||||
|
||||
if (address->source != NETWORK_CONFIG_SOURCE_FOREIGN)
|
||||
return 0; /* Huh?? Already deserialized?? */
|
||||
|
||||
address->source = p.source;
|
||||
memcpy_safe(&address->provider, p.provider.iov_base, p.provider.iov_len);
|
||||
|
||||
log_address_debug(address, "Deserialized", link);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_deserialize_link(Manager *manager, sd_json_variant *v) {
|
||||
typedef struct LinkParam {
|
||||
int ifindex;
|
||||
sd_json_variant *addresses;
|
||||
} LinkParam;
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "Index", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(LinkParam, ifindex), SD_JSON_MANDATORY | SD_JSON_REFUSE_NULL },
|
||||
{ "Addresses", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant_noref, offsetof(LinkParam, addresses), 0 },
|
||||
{},
|
||||
};
|
||||
|
||||
int r, ret = 0;
|
||||
|
||||
assert(manager);
|
||||
assert(v);
|
||||
|
||||
LinkParam p = {};
|
||||
r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to dispatch interface from json variant: %m");
|
||||
|
||||
Link *link;
|
||||
r = link_get_by_index(manager, p.ifindex, &link);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "No interface with deserialized ifindex (%i) found: %m", p.ifindex);
|
||||
return 0; /* Already removed? */
|
||||
}
|
||||
|
||||
sd_json_variant *i;
|
||||
JSON_VARIANT_ARRAY_FOREACH(i, p.addresses)
|
||||
RET_GATHER(ret, link_deserialize_address(link, i));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct RouteParam {
|
||||
Route route;
|
||||
|
||||
struct iovec dst;
|
||||
struct iovec src;
|
||||
struct iovec prefsrc;
|
||||
struct iovec gw;
|
||||
struct iovec metrics;
|
||||
struct iovec provider;
|
||||
} RouteParam;
|
||||
|
||||
static void route_param_done(RouteParam *p) {
|
||||
assert(p);
|
||||
|
||||
free(p->route.metric.metrics);
|
||||
|
||||
iovec_done(&p->dst);
|
||||
iovec_done(&p->src);
|
||||
iovec_done(&p->prefsrc);
|
||||
iovec_done(&p->gw);
|
||||
iovec_done(&p->metrics);
|
||||
iovec_done(&p->provider);
|
||||
}
|
||||
|
||||
static int manager_deserialize_route(Manager *manager, sd_json_variant *v) {
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
/* rtmsg header */
|
||||
{ "Family", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_address_family, offsetof(RouteParam, route.family), SD_JSON_MANDATORY },
|
||||
{ "DestinationPrefixLength", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(RouteParam, route.dst_prefixlen), SD_JSON_MANDATORY },
|
||||
{ "SourcePrefixLength", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(RouteParam, route.src_prefixlen), 0 },
|
||||
{ "TOS", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(RouteParam, route.tos), SD_JSON_MANDATORY },
|
||||
{ "Protocol", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(RouteParam, route.protocol), SD_JSON_MANDATORY },
|
||||
{ "Scope", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(RouteParam, route.scope), SD_JSON_MANDATORY },
|
||||
{ "Type", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(RouteParam, route.type), SD_JSON_MANDATORY },
|
||||
{ "Flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, offsetof(RouteParam, route.flags), SD_JSON_MANDATORY },
|
||||
/* attributes */
|
||||
{ "Destination", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(RouteParam, dst), SD_JSON_MANDATORY },
|
||||
{ "Source", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(RouteParam, src), 0 },
|
||||
{ "Priority", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, offsetof(RouteParam, route.priority), SD_JSON_MANDATORY },
|
||||
{ "PreferredSource", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(RouteParam, prefsrc), 0 },
|
||||
{ "Table", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, offsetof(RouteParam, route.table), SD_JSON_MANDATORY },
|
||||
/* nexthops */
|
||||
{ "Gateway", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(RouteParam, gw), 0 },
|
||||
{ "InterfaceIndex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(RouteParam, route.nexthop.ifindex), SD_JSON_MANDATORY | SD_JSON_RELAX },
|
||||
{ "NextHopID", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, offsetof(RouteParam, route.nexthop_id), 0 },
|
||||
/* metrics */
|
||||
{ "Metrics", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(RouteParam, metrics), 0 },
|
||||
{ "TCPCongestionControlAlgorithm", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(RouteParam, route.metric.tcp_congestion_control_algo), 0 },
|
||||
/* config */
|
||||
{ "ConfigSource", SD_JSON_VARIANT_STRING, json_dispatch_network_config_source, offsetof(RouteParam, route.source), SD_JSON_MANDATORY },
|
||||
{ "ConfigProvider", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(RouteParam, provider), 0 },
|
||||
{},
|
||||
};
|
||||
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
assert(v);
|
||||
|
||||
_cleanup_(route_param_done) RouteParam p = {};
|
||||
r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to dispatch route from json variant: %m");
|
||||
|
||||
if (p.dst.iov_len != FAMILY_ADDRESS_SIZE(p.route.family))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Dispatched destination address size (%zu) is incompatible with the family (%s).",
|
||||
p.dst.iov_len, af_to_ipv4_ipv6(p.route.family));
|
||||
|
||||
if (p.src.iov_len != 0 && p.src.iov_len != FAMILY_ADDRESS_SIZE(p.route.family))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Dispatched source address size (%zu) is incompatible with the family (%s).",
|
||||
p.src.iov_len, af_to_ipv4_ipv6(p.route.family));
|
||||
|
||||
if (p.prefsrc.iov_len != 0 && p.prefsrc.iov_len != FAMILY_ADDRESS_SIZE(p.route.family))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Dispatched preferred source address size (%zu) is incompatible with the family (%s).",
|
||||
p.prefsrc.iov_len, af_to_ipv4_ipv6(p.route.family));
|
||||
|
||||
switch (p.gw.iov_len) {
|
||||
case 0:
|
||||
p.route.nexthop.family = AF_UNSPEC;
|
||||
break;
|
||||
case sizeof(struct in_addr):
|
||||
p.route.nexthop.family = AF_INET;
|
||||
break;
|
||||
case sizeof(struct in6_addr):
|
||||
p.route.nexthop.family = AF_INET6;
|
||||
break;
|
||||
default:
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Dispatched gateway address size (%zu) is invalid.",
|
||||
p.prefsrc.iov_len);
|
||||
}
|
||||
|
||||
if (p.metrics.iov_len % sizeof(uint32_t) != 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Dispatched route metric size (%zu) is invalid.",
|
||||
p.metrics.iov_len);
|
||||
|
||||
memcpy_safe(&p.route.dst, p.dst.iov_base, p.dst.iov_len);
|
||||
memcpy_safe(&p.route.src, p.src.iov_base, p.src.iov_len);
|
||||
memcpy_safe(&p.route.prefsrc, p.prefsrc.iov_base, p.prefsrc.iov_len);
|
||||
memcpy_safe(&p.route.nexthop.gw, p.gw.iov_base, p.gw.iov_len);
|
||||
|
||||
p.route.metric.n_metrics = p.metrics.iov_len / sizeof(uint32_t);
|
||||
p.route.metric.metrics = new(uint32_t, p.route.metric.n_metrics);
|
||||
if (!p.route.metric.metrics)
|
||||
return log_oom_debug();
|
||||
|
||||
memcpy_safe(p.route.metric.metrics, p.metrics.iov_base, p.metrics.iov_len);
|
||||
|
||||
Route *route;
|
||||
r = route_get(manager, &p.route, &route);
|
||||
if (r < 0) {
|
||||
log_route_debug(&p.route, "Cannot find deserialized", manager);
|
||||
return 0; /* Already removed? */
|
||||
}
|
||||
|
||||
if (route->source != NETWORK_CONFIG_SOURCE_FOREIGN)
|
||||
return 0; /* Huh?? Already deserialized?? */
|
||||
|
||||
route->source = p.route.source;
|
||||
memcpy_safe(&route->provider, p.provider.iov_base, p.provider.iov_len);
|
||||
|
||||
log_route_debug(route, "Deserialized", manager);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_deserialize(Manager *manager) {
|
||||
int r, ret = 0;
|
||||
|
||||
assert(manager);
|
||||
|
||||
_cleanup_close_ int fd = TAKE_FD(manager->serialization_fd);
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
|
||||
log_debug("Deserializing...");
|
||||
|
||||
_cleanup_fclose_ FILE *f = take_fdopen(&fd, "r");
|
||||
if (!f)
|
||||
return log_debug_errno(errno, "Failed to fdopen() serialization file descriptor: %m");
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
unsigned err_line, err_column;
|
||||
r = sd_json_parse_file(
|
||||
f,
|
||||
/* path = */ NULL,
|
||||
/* flags = */ 0,
|
||||
&v,
|
||||
&err_line,
|
||||
&err_column);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse json (line=%u, column=%u): %m", err_line, err_column);
|
||||
|
||||
sd_json_variant *i;
|
||||
JSON_VARIANT_ARRAY_FOREACH(i, sd_json_variant_by_key(v, "Interfaces"))
|
||||
RET_GATHER(ret, manager_deserialize_link(manager, i));
|
||||
|
||||
JSON_VARIANT_ARRAY_FOREACH(i, sd_json_variant_by_key(v, "Routes"))
|
||||
RET_GATHER(ret, manager_deserialize_route(manager, i));
|
||||
|
||||
log_debug("Deserialization completed.");
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
typedef struct Manager Manager;
|
||||
|
||||
int manager_serialize(Manager *manager);
|
||||
int manager_set_serialization_fd(Manager *manager, int fd, const char *name);
|
||||
int manager_deserialize(Manager *manager);
|
|
@ -16,6 +16,7 @@
|
|||
#include "networkd-conf.h"
|
||||
#include "networkd-manager-bus.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-serialize.h"
|
||||
#include "service-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "strv.h"
|
||||
|
@ -100,6 +101,10 @@ static int run(int argc, char *argv[]) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_deserialize(m);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to deserialize the prvious invocation, ignoring: %m");
|
||||
|
||||
r = manager_start(m);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not start manager: %m");
|
||||
|
|
|
@ -1808,63 +1808,81 @@ char* umount_and_unlink_and_free(char *p) {
|
|||
return mfree(p);
|
||||
}
|
||||
|
||||
static int path_get_mount_info(
|
||||
static int path_get_mount_info_at(
|
||||
int dir_fd,
|
||||
const char *path,
|
||||
char **ret_fstype,
|
||||
char **ret_options) {
|
||||
|
||||
_cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
|
||||
_cleanup_free_ char *fstype = NULL, *options = NULL;
|
||||
struct libmnt_fs *fs;
|
||||
int r;
|
||||
_cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
|
||||
int r, mnt_id;
|
||||
|
||||
assert(path);
|
||||
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||
|
||||
table = mnt_new_table();
|
||||
if (!table)
|
||||
return -ENOMEM;
|
||||
|
||||
r = mnt_table_parse_mtab(table, /* filename = */ NULL);
|
||||
r = path_get_mnt_id_at(dir_fd, path, &mnt_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_debug_errno(r, "Failed to get mount ID: %m");
|
||||
|
||||
fs = mnt_table_find_mountpoint(table, path, MNT_ITER_FORWARD);
|
||||
if (!fs)
|
||||
return -EINVAL;
|
||||
r = libmount_parse("/proc/self/mountinfo", NULL, &table, &iter);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse /proc/self/mountinfo: %m");
|
||||
|
||||
if (ret_fstype) {
|
||||
fstype = strdup(strempty(mnt_fs_get_fstype(fs)));
|
||||
if (!fstype)
|
||||
return -ENOMEM;
|
||||
for (;;) {
|
||||
struct libmnt_fs *fs;
|
||||
|
||||
r = mnt_table_next_fs(table, iter, &fs);
|
||||
if (r == 1)
|
||||
break; /* EOF */
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");
|
||||
|
||||
if (mnt_fs_get_id(fs) != mnt_id)
|
||||
continue;
|
||||
|
||||
_cleanup_free_ char *fstype = NULL, *options = NULL;
|
||||
|
||||
if (ret_fstype) {
|
||||
fstype = strdup(strempty(mnt_fs_get_fstype(fs)));
|
||||
if (!fstype)
|
||||
return log_oom_debug();
|
||||
}
|
||||
|
||||
if (ret_options) {
|
||||
options = strdup(strempty(mnt_fs_get_options(fs)));
|
||||
if (!options)
|
||||
return log_oom_debug();
|
||||
}
|
||||
|
||||
if (ret_fstype)
|
||||
*ret_fstype = TAKE_PTR(fstype);
|
||||
if (ret_options)
|
||||
*ret_options = TAKE_PTR(options);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ret_options) {
|
||||
options = strdup(strempty(mnt_fs_get_options(fs)));
|
||||
if (!options)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (ret_fstype)
|
||||
*ret_fstype = TAKE_PTR(fstype);
|
||||
if (ret_options)
|
||||
*ret_options = TAKE_PTR(options);
|
||||
|
||||
return 0;
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Cannot find mount ID %i from /proc/self/mountinfo.", mnt_id);
|
||||
}
|
||||
|
||||
int path_is_network_fs_harder(const char *path) {
|
||||
int path_is_network_fs_harder_at(int dir_fd, const char *path) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
int r;
|
||||
|
||||
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||
|
||||
fd = xopenat(dir_fd, path, O_PATH | O_CLOEXEC | O_NOFOLLOW);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
r = fd_is_network_fs(fd);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
_cleanup_free_ char *fstype = NULL, *options = NULL;
|
||||
int r, ret;
|
||||
|
||||
assert(path);
|
||||
|
||||
ret = path_is_network_fs(path);
|
||||
if (ret > 0)
|
||||
return true;
|
||||
|
||||
r = path_get_mount_info(path, &fstype, &options);
|
||||
r = path_get_mount_info_at(fd, /* path = */ NULL, &fstype, &options);
|
||||
if (r < 0)
|
||||
return RET_GATHER(ret, r);
|
||||
return r;
|
||||
|
||||
if (fstype_is_network(fstype))
|
||||
return true;
|
||||
|
|
|
@ -181,4 +181,7 @@ int mount_credentials_fs(const char *path, size_t size, bool ro);
|
|||
|
||||
int make_fsmount(int error_log_level, const char *what, const char *type, unsigned long flags, const char *options, int userns_fd);
|
||||
|
||||
int path_is_network_fs_harder(const char *path);
|
||||
int path_is_network_fs_harder_at(int dir_fd, const char *path);
|
||||
static inline int path_is_network_fs_harder(const char *path) {
|
||||
return path_is_network_fs_harder_at(AT_FDCWD, path);
|
||||
}
|
||||
|
|
|
@ -538,9 +538,53 @@ TEST(bind_mount_submounts) {
|
|||
}
|
||||
|
||||
TEST(path_is_network_fs_harder) {
|
||||
ASSERT_OK_ZERO(path_is_network_fs_harder("/dev"));
|
||||
ASSERT_OK_ZERO(path_is_network_fs_harder("/sys"));
|
||||
ASSERT_OK_ZERO(path_is_network_fs_harder("/run"));
|
||||
_cleanup_close_ int dir_fd = -EBADF;
|
||||
int r;
|
||||
|
||||
ASSERT_OK(dir_fd = open("/", O_PATH | O_CLOEXEC));
|
||||
FOREACH_STRING(s,
|
||||
"/", "/dev/", "/proc/", "/run/", "/sys/", "/tmp/", "/usr/", "/var/tmp/",
|
||||
"", ".", "../../../", "/this/path/should/not/exist/for/test-mount-util/") {
|
||||
|
||||
r = path_is_network_fs_harder(s);
|
||||
log_debug("path_is_network_fs_harder(%s) → %i: %s", s, r, r < 0 ? STRERROR(r) : yes_no(r));
|
||||
|
||||
const char *q = path_startswith(s, "/") ?: s;
|
||||
r = path_is_network_fs_harder_at(dir_fd, q);
|
||||
log_debug("path_is_network_fs_harder_at(root, %s) → %i: %s", q, r, r < 0 ? STRERROR(r) : yes_no(r));
|
||||
}
|
||||
|
||||
if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0) {
|
||||
(void) log_tests_skipped("not running privileged");
|
||||
return;
|
||||
}
|
||||
|
||||
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
|
||||
assert_se(mkdtemp_malloc("/tmp/test-mount-util.path_is_network_fs_harder.XXXXXXX", &t) >= 0);
|
||||
|
||||
r = safe_fork("(make_mount-point)",
|
||||
FORK_RESET_SIGNALS |
|
||||
FORK_CLOSE_ALL_FDS |
|
||||
FORK_DEATHSIG_SIGTERM |
|
||||
FORK_WAIT |
|
||||
FORK_REOPEN_LOG |
|
||||
FORK_LOG |
|
||||
FORK_NEW_MOUNTNS |
|
||||
FORK_MOUNTNS_SLAVE,
|
||||
NULL);
|
||||
ASSERT_OK(r);
|
||||
|
||||
if (r == 0) {
|
||||
ASSERT_OK(mount_nofollow_verbose(LOG_INFO, "tmpfs", t, "tmpfs", 0, NULL));
|
||||
ASSERT_OK_ZERO(path_is_network_fs_harder(t));
|
||||
ASSERT_OK_ERRNO(umount(t));
|
||||
|
||||
ASSERT_OK(mount_nofollow_verbose(LOG_INFO, "tmpfs", t, "tmpfs", 0, "x-systemd-growfs,x-systemd-automount"));
|
||||
ASSERT_OK_ZERO(path_is_network_fs_harder(t));
|
||||
ASSERT_OK_ERRNO(umount(t));
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
|
|
@ -142,11 +142,13 @@ endif
|
|||
############################################################
|
||||
|
||||
if install_tests
|
||||
foreach script : ['integration-test-setup.sh', 'run-unit-tests.py']
|
||||
install_data(script,
|
||||
install_mode : 'rwxr-xr-x',
|
||||
install_dir : testsdir)
|
||||
endforeach
|
||||
install_data('run-unit-tests.py',
|
||||
install_mode : 'rwxr-xr-x',
|
||||
install_dir : testsdir)
|
||||
|
||||
install_data('integration-test-setup.sh',
|
||||
install_mode : 'rwxr-xr-x',
|
||||
install_dir : testdata_dir)
|
||||
endif
|
||||
|
||||
############################################################
|
||||
|
|
|
@ -5,4 +5,4 @@ Name=veth99
|
|||
[Network]
|
||||
DHCP=ipv4
|
||||
IPv6AcceptRA=false
|
||||
KeepConfiguration=dhcp-on-stop
|
||||
KeepConfiguration=dynamic-on-stop
|
|
@ -5,4 +5,4 @@ Name=veth99
|
|||
[Network]
|
||||
DHCP=ipv4
|
||||
IPv6AcceptRA=false
|
||||
KeepConfiguration=dhcp
|
||||
KeepConfiguration=dynamic
|
|
@ -6705,7 +6705,6 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
|||
'--dhcp-option=option6:ntp-server,[2600::ff]')
|
||||
|
||||
networkctl_reload()
|
||||
networkctl_reconfigure('veth99') # Release previously acquired lease and start new DHCPv6 handshake.
|
||||
self.wait_online('veth99:routable', 'veth-peer:routable')
|
||||
|
||||
# checking address
|
||||
|
@ -7240,10 +7239,10 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
|||
self.assertNotIn('test-hostname', output)
|
||||
self.assertNotIn('26:mtu', output)
|
||||
|
||||
def test_dhcp_keep_configuration_dhcp(self):
|
||||
def test_dhcp_keep_configuration_dynamic(self):
|
||||
copy_network_unit('25-veth.netdev',
|
||||
'25-dhcp-server-veth-peer.network',
|
||||
'25-dhcp-client-keep-configuration-dhcp.network')
|
||||
'25-dhcp-client-keep-configuration-dynamic.network')
|
||||
start_networkd()
|
||||
self.wait_online('veth-peer:carrier')
|
||||
start_dnsmasq()
|
||||
|
@ -7275,7 +7274,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
|||
self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
|
||||
'valid_lft forever preferred_lft forever')
|
||||
|
||||
with open(os.path.join(network_unit_dir, '25-dhcp-client-keep-configuration-dhcp.network'), mode='a', encoding='utf-8') as f:
|
||||
with open(os.path.join(network_unit_dir, '25-dhcp-client-keep-configuration-dynamic.network'), mode='a', encoding='utf-8') as f:
|
||||
f.write('[Network]\nDHCP=no\n')
|
||||
|
||||
start_networkd()
|
||||
|
@ -7287,10 +7286,10 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
|||
self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
|
||||
'valid_lft forever preferred_lft forever')
|
||||
|
||||
def test_dhcp_keep_configuration_dhcp_on_stop(self):
|
||||
def test_dhcp_keep_configuration_dynamic_on_stop(self):
|
||||
copy_network_unit('25-veth.netdev',
|
||||
'25-dhcp-server-veth-peer.network',
|
||||
'25-dhcp-client-keep-configuration-dhcp-on-stop.network')
|
||||
'25-dhcp-client-keep-configuration-dynamic-on-stop.network')
|
||||
start_networkd()
|
||||
self.wait_online('veth-peer:carrier')
|
||||
start_dnsmasq()
|
||||
|
@ -7448,7 +7447,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
|||
self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
|
||||
|
||||
def test_dhcp_client_use_dns(self):
|
||||
def check(self, ipv4, ipv6):
|
||||
def check(self, ipv4, ipv6, needs_reconfigure=False):
|
||||
os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
|
||||
with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
|
||||
f.write('[DHCPv4]\nUseDNS=')
|
||||
|
@ -7458,6 +7457,8 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
|||
f.write('\n[IPv6AcceptRA]\nUseDNS=no')
|
||||
|
||||
networkctl_reload()
|
||||
if needs_reconfigure:
|
||||
networkctl_reconfigure('veth99')
|
||||
self.wait_online('veth99:routable')
|
||||
|
||||
# link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
|
||||
|
@ -7489,7 +7490,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
|||
|
||||
check(self, True, True)
|
||||
check(self, True, False)
|
||||
check(self, False, True)
|
||||
check(self, False, True, needs_reconfigure=True)
|
||||
check(self, False, False)
|
||||
|
||||
def test_dhcp_client_default_use_domains(self):
|
||||
|
@ -7541,7 +7542,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
|||
check(self, False, False, False)
|
||||
|
||||
def test_dhcp_client_use_dnr(self):
|
||||
def check(self, ipv4, ipv6):
|
||||
def check(self, ipv4, ipv6, needs_reconfigure=False):
|
||||
os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
|
||||
with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
|
||||
f.write('[DHCPv4]\nUseDNS=')
|
||||
|
@ -7551,6 +7552,8 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
|||
f.write('\n[IPv6AcceptRA]\nUseDNS=no')
|
||||
|
||||
networkctl_reload()
|
||||
if needs_reconfigure:
|
||||
networkctl_reconfigure('veth99')
|
||||
self.wait_online('veth99:routable')
|
||||
|
||||
# link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
|
||||
|
@ -7587,11 +7590,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
|||
|
||||
check(self, True, True)
|
||||
check(self, True, False)
|
||||
check(self, False, True)
|
||||
check(self, False, True, needs_reconfigure=True)
|
||||
check(self, False, False)
|
||||
|
||||
def test_dhcp_client_use_captive_portal(self):
|
||||
def check(self, ipv4, ipv6):
|
||||
def check(self, ipv4, ipv6, needs_reconfigure=False):
|
||||
os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
|
||||
with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
|
||||
f.write('[DHCPv4]\nUseCaptivePortal=')
|
||||
|
@ -7601,6 +7604,8 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
|||
f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
|
||||
|
||||
networkctl_reload()
|
||||
if needs_reconfigure:
|
||||
networkctl_reconfigure('veth99')
|
||||
self.wait_online('veth99:routable')
|
||||
|
||||
# link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
|
||||
|
@ -7625,7 +7630,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
|||
|
||||
check(self, True, True)
|
||||
check(self, True, False)
|
||||
check(self, False, True)
|
||||
check(self, False, True, needs_reconfigure=True)
|
||||
check(self, False, False)
|
||||
|
||||
def test_dhcp_client_reject_captive_portal(self):
|
||||
|
|
|
@ -7,9 +7,9 @@ Before=getty-pre.target
|
|||
|
||||
[Service]
|
||||
ExecStartPre=rm -f /failed /testok
|
||||
ExecStartPre=/usr/lib/systemd/tests/integration-test-setup.sh setup
|
||||
ExecStartPre=/usr/lib/systemd/tests/testdata/integration-test-setup.sh setup
|
||||
ExecStart=@command@
|
||||
ExecStopPost=/usr/lib/systemd/tests/integration-test-setup.sh finalize
|
||||
ExecStopPost=/usr/lib/systemd/tests/testdata/integration-test-setup.sh finalize
|
||||
Type=oneshot
|
||||
MemoryAccounting=@memory-accounting@
|
||||
StateDirectory=%N
|
||||
|
|
|
@ -132,10 +132,12 @@ testcase_unpriv() {
|
|||
return 0
|
||||
fi
|
||||
|
||||
# The kernel has a restriction for unprivileged user namespaces where they cannot mount a less restrictive
|
||||
# instance of /proc/. So if /proc/ is masked (e.g. /proc/kmsg is over-mounted with tmpfs as systemd-nspawn does),
|
||||
# then mounting a new /proc/ will fail and we will still see the host's /proc/. Thus, to allow tests to run in
|
||||
# a VM or nspawn, we mount a new proc on a temporary directory with no masking to bypass this kernel restriction.
|
||||
# IMPORTANT: For /proc/ to be remounted in pid namespace within an unprivileged user namespace, there needs to
|
||||
# be at least 1 unmasked procfs mount in ANY directory. Otherwise, if /proc/ is masked (e.g. /proc/scsi is
|
||||
# over-mounted with tmpfs), then mounting a new /proc/ will fail.
|
||||
#
|
||||
# Thus, to guarantee PrivatePIDs=yes tests for unprivileged users pass, we mount a new procfs on a temporary
|
||||
# directory with no masking. This will guarantee an unprivileged user can mount a new /proc/ successfully.
|
||||
mkdir -p /tmp/TEST-07-PID1-private-pids-proc
|
||||
mount -t proc proc /tmp/TEST-07-PID1-private-pids-proc
|
||||
|
||||
|
@ -146,7 +148,16 @@ testcase_unpriv() {
|
|||
umount /tmp/TEST-07-PID1-private-pids-proc
|
||||
rm -rf /tmp/TEST-07-PID1-private-pids-proc
|
||||
|
||||
# Now verify the behavior with masking - units should fail as PrivatePIDs=yes has no graceful fallback.
|
||||
# Now we will mask /proc/ by mounting tmpfs over /proc/scsi. This will guarantee that mounting /proc/ will fail
|
||||
# for unprivileged users when using PrivatePIDs=yes. Now units should fail as PrivatePIDs=yes has no graceful
|
||||
# fallback.
|
||||
#
|
||||
# Note some kernels do not have /proc/scsi so we verify the directory exists prior to running the test.
|
||||
if [ ! -d /proc/scsi ]; then
|
||||
echo "/proc/scsi does not exist, skipping unprivileged PrivatePIDs=yes test with masked /proc/"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$HAS_EXISTING_SCSI_MOUNT" == "no" ]]; then
|
||||
mount -t tmpfs tmpfs /proc/scsi
|
||||
fi
|
||||
|
|
Loading…
Reference in New Issue