mirror of
https://github.com/systemd/systemd.git
synced 2025-01-09 01:18:19 +03:00
network: reconfigure interface more gracefully (#35035)
split-out of #34989.
This commit is contained in:
commit
df69f29728
@ -1384,7 +1384,7 @@ int link_drop_ipv6ll_addresses(Link *link) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int link_drop_foreign_addresses(Link *link) {
|
||||
int link_drop_unmanaged_addresses(Link *link) {
|
||||
Address *address;
|
||||
int r = 0;
|
||||
|
||||
@ -1401,18 +1401,22 @@ int link_drop_foreign_addresses(Link *link) {
|
||||
if (link->flags & IFF_LOOPBACK && in_addr_is_localhost_one(address->family, &address->in_addr) > 0)
|
||||
continue;
|
||||
|
||||
/* Ignore addresses we configured. */
|
||||
if (address->source != NETWORK_CONFIG_SOURCE_FOREIGN)
|
||||
continue;
|
||||
|
||||
/* Ignore addresses not assigned yet or already removing. */
|
||||
if (!address_exists(address))
|
||||
continue;
|
||||
|
||||
/* link_address_is_dynamic() is slightly heavy. Let's call the function only when KeepConfiguration= is set. */
|
||||
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))
|
||||
continue;
|
||||
if (address->source == NETWORK_CONFIG_SOURCE_FOREIGN) {
|
||||
if (link->network->keep_configuration == KEEP_CONFIGURATION_YES)
|
||||
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))
|
||||
continue;
|
||||
|
||||
} else if (address->source != NETWORK_CONFIG_SOURCE_STATIC)
|
||||
continue; /* Ignore dynamically configurad addresses. */
|
||||
|
||||
address_mark(address);
|
||||
}
|
||||
@ -1464,15 +1468,6 @@ int link_drop_static_addresses(Link *link) {
|
||||
return r;
|
||||
}
|
||||
|
||||
void link_foreignize_addresses(Link *link) {
|
||||
Address *address;
|
||||
|
||||
assert(link);
|
||||
|
||||
SET_FOREACH(address, link->addresses)
|
||||
address->source = NETWORK_CONFIG_SOURCE_FOREIGN;
|
||||
}
|
||||
|
||||
int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) {
|
||||
int r;
|
||||
|
||||
|
@ -113,9 +113,8 @@ bool link_check_addresses_ready(Link *link, NetworkConfigSource source);
|
||||
DEFINE_SECTION_CLEANUP_FUNCTIONS(Address, address_unref);
|
||||
|
||||
int link_drop_static_addresses(Link *link);
|
||||
int link_drop_foreign_addresses(Link *link);
|
||||
int link_drop_unmanaged_addresses(Link *link);
|
||||
int link_drop_ipv6ll_addresses(Link *link);
|
||||
void link_foreignize_addresses(Link *link);
|
||||
bool link_address_is_dynamic(const Link *link, const Address *address);
|
||||
|
||||
int link_get_address_full(
|
||||
|
@ -1252,6 +1252,28 @@ int dhcp_request_prefix_delegation(Link *link) {
|
||||
dhcp6_pd_assign_subnet_prefixes(link, uplink);
|
||||
}
|
||||
|
||||
int link_drop_dhcp_pd_config(Link *link, Network *network) {
|
||||
assert(link);
|
||||
assert(network);
|
||||
|
||||
if (!link_dhcp_pd_is_enabled(link))
|
||||
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 ||
|
||||
(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))) ||
|
||||
link->network->dhcp_pd_subnet_id != network->dhcp_pd_subnet_id ||
|
||||
link->network->dhcp_pd_route_metric != network->dhcp_pd_route_metric ||
|
||||
link->network->dhcp_pd_uplink_index != network->dhcp_pd_uplink_index ||
|
||||
!streq_ptr(link->network->dhcp_pd_uplink_name, network->dhcp_pd_uplink_name))
|
||||
return dhcp_pd_remove(link, /* only_marked = */ false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_dhcp_pd_subnet_id(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
|
@ -10,12 +10,14 @@
|
||||
|
||||
typedef struct Address Address;
|
||||
typedef struct Link Link;
|
||||
typedef struct Network Network;
|
||||
|
||||
bool link_dhcp_pd_is_enabled(Link *link);
|
||||
bool dhcp_pd_is_uplink(Link *link, Link *target, bool accept_auto);
|
||||
int dhcp_pd_find_uplink(Link *link, Link **ret);
|
||||
int dhcp_pd_remove(Link *link, bool only_marked);
|
||||
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);
|
||||
|
@ -1375,49 +1375,49 @@ static int dhcp4_set_client_identifier(Link *link) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp4_find_dynamic_address(Link *link, struct in_addr *ret) {
|
||||
Address *a;
|
||||
|
||||
static int dhcp4_set_request_address(Link *link) {
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
assert(ret);
|
||||
assert(link->dhcp_client);
|
||||
|
||||
/* 1. Use already assigned address. */
|
||||
Address *a;
|
||||
SET_FOREACH(a, link->addresses) {
|
||||
if (a->source != NETWORK_CONFIG_SOURCE_DHCP4)
|
||||
continue;
|
||||
|
||||
assert(a->family == AF_INET);
|
||||
|
||||
log_link_debug(link, "DHCPv4 CLIENT: requesting previously acquired address %s.",
|
||||
IN4_ADDR_TO_STRING(&a->in_addr.in));
|
||||
return sd_dhcp_client_set_request_address(link->dhcp_client, &a->in_addr.in);
|
||||
}
|
||||
|
||||
/* 2. If no address is assigned yet, use explicitly configured address. */
|
||||
if (in4_addr_is_set(&link->network->dhcp_request_address)) {
|
||||
log_link_debug(link, "DHCPv4 CLIENT: requesting address %s specified by RequestAddress=.",
|
||||
IN4_ADDR_TO_STRING(&link->network->dhcp_request_address));
|
||||
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))
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
SET_FOREACH(a, link->addresses) {
|
||||
if (a->source != NETWORK_CONFIG_SOURCE_FOREIGN)
|
||||
continue;
|
||||
if (a->family != AF_INET)
|
||||
continue;
|
||||
if (link_address_is_dynamic(link, a))
|
||||
break;
|
||||
if (!link_address_is_dynamic(link, a))
|
||||
continue;
|
||||
|
||||
log_link_debug(link, "DHCPv4 CLIENT: requesting foreign dynamic address %s.",
|
||||
IN4_ADDR_TO_STRING(&a->in_addr.in));
|
||||
return sd_dhcp_client_set_request_address(link->dhcp_client, &a->in_addr.in);
|
||||
}
|
||||
|
||||
if (!a)
|
||||
return false;
|
||||
|
||||
*ret = a->in_addr.in;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int dhcp4_set_request_address(Link *link) {
|
||||
struct in_addr a;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
assert(link->dhcp_client);
|
||||
|
||||
a = link->network->dhcp_request_address;
|
||||
|
||||
if (in4_addr_is_null(&a))
|
||||
(void) dhcp4_find_dynamic_address(link, &a);
|
||||
|
||||
if (in4_addr_is_null(&a))
|
||||
return 0;
|
||||
|
||||
log_link_debug(link, "DHCPv4 CLIENT: requesting %s.", IN4_ADDR_TO_STRING(&a));
|
||||
return sd_dhcp_client_set_request_address(link->dhcp_client, &a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool link_needs_dhcp_broadcast(Link *link) {
|
||||
@ -1833,6 +1833,26 @@ int link_request_dhcp4_client(Link *link) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int link_drop_dhcp4_config(Link *link, Network *network) {
|
||||
int ret = 0;
|
||||
|
||||
assert(link);
|
||||
assert(network);
|
||||
|
||||
if (!link_dhcp4_enabled(link))
|
||||
return 0; /* Currently DHCPv4 client is not enabled, there is nothing we need to drop. */
|
||||
|
||||
if (!FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4))
|
||||
/* Currently enabled but will be disabled. Stop the client and drop the lease. */
|
||||
ret = sd_dhcp_client_stop(link->dhcp_client);
|
||||
|
||||
/* 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. */
|
||||
link->dhcp_client = sd_dhcp_client_unref(link->dhcp_client);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int config_parse_dhcp_max_attempts(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
|
@ -25,6 +25,7 @@ int dhcp4_lease_lost(Link *link);
|
||||
int dhcp4_check_ready(Link *link);
|
||||
|
||||
int link_request_dhcp4_client(Link *link);
|
||||
int link_drop_dhcp4_config(Link *link, Network *network);
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts);
|
||||
|
@ -841,6 +841,26 @@ int link_request_dhcp6_client(Link *link) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int link_drop_dhcp6_config(Link *link, Network *network) {
|
||||
int ret = 0;
|
||||
|
||||
assert(link);
|
||||
assert(network);
|
||||
|
||||
if (!link_dhcp6_enabled(link))
|
||||
return 0; /* Currently DHCPv6 client is not enabled, there is nothing we need to drop. */
|
||||
|
||||
if (!FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV6))
|
||||
/* Currently enabled but will be disabled. Stop the client and drop the lease. */
|
||||
ret = sd_dhcp6_client_stop(link->dhcp6_client);
|
||||
|
||||
/* 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. */
|
||||
link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int link_serialize_dhcp6_client(Link *link, FILE *f) {
|
||||
_cleanup_free_ char *duid = NULL;
|
||||
uint32_t iaid;
|
||||
|
@ -13,6 +13,7 @@ typedef enum DHCP6ClientStartMode {
|
||||
} DHCP6ClientStartMode;
|
||||
|
||||
typedef struct Link Link;
|
||||
typedef struct Network Network;
|
||||
|
||||
bool link_dhcp6_with_address_enabled(Link *link);
|
||||
int dhcp6_check_ready(Link *link);
|
||||
@ -21,6 +22,7 @@ int dhcp6_start(Link *link);
|
||||
int dhcp6_start_on_ra(Link *link, bool information_request);
|
||||
|
||||
int link_request_dhcp6_client(Link *link);
|
||||
int link_drop_dhcp6_config(Link *link, Network *network);
|
||||
|
||||
int link_serialize_dhcp6_client(Link *link, FILE *f);
|
||||
|
||||
|
@ -179,10 +179,21 @@ static int ipv4ll_set_address(Link *link) {
|
||||
assert(link->network);
|
||||
assert(link->ipv4ll);
|
||||
|
||||
if (!in4_addr_is_set(&link->network->ipv4ll_start_address))
|
||||
return 0;
|
||||
/* 1. Use already assigned address. */
|
||||
Address *a;
|
||||
SET_FOREACH(a, link->addresses) {
|
||||
if (a->source != NETWORK_CONFIG_SOURCE_IPV4LL)
|
||||
continue;
|
||||
|
||||
return sd_ipv4ll_set_address(link->ipv4ll, &link->network->ipv4ll_start_address);
|
||||
assert(a->family == AF_INET);
|
||||
return sd_ipv4ll_set_address(link->ipv4ll, &a->in_addr.in);
|
||||
}
|
||||
|
||||
/* 2. If no address is assigned yet, use explicitly configured address. */
|
||||
if (in4_addr_is_set(&link->network->ipv4ll_start_address))
|
||||
return sd_ipv4ll_set_address(link->ipv4ll, &link->network->ipv4ll_start_address);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipv4ll_configure(Link *link) {
|
||||
@ -231,6 +242,27 @@ int ipv4ll_configure(Link *link) {
|
||||
return sd_ipv4ll_set_check_mac_callback(link->ipv4ll, ipv4ll_check_mac, link->manager);
|
||||
}
|
||||
|
||||
int link_drop_ipv4ll_config(Link *link, Network *network) {
|
||||
int ret = 0;
|
||||
|
||||
assert(link);
|
||||
assert(network);
|
||||
|
||||
if (!link_ipv4ll_enabled(link))
|
||||
return 0;
|
||||
|
||||
Network *saved = link->network;
|
||||
link->network = network;
|
||||
bool enabled = link_ipv4ll_enabled(link);
|
||||
link->network = saved;
|
||||
|
||||
if (!enabled)
|
||||
ret = sd_ipv4ll_stop(link->ipv4ll);
|
||||
|
||||
link->ipv4ll = sd_ipv4ll_unref(link->ipv4ll);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipv4ll_update_mac(Link *link) {
|
||||
assert(link);
|
||||
|
||||
|
@ -6,10 +6,12 @@
|
||||
#define IPV4LL_ROUTE_METRIC 2048
|
||||
|
||||
typedef struct Link Link;
|
||||
typedef struct Network Network;
|
||||
|
||||
bool link_ipv4ll_enabled(Link *link);
|
||||
|
||||
int ipv4ll_configure(Link *link);
|
||||
int link_drop_ipv4ll_config(Link *link, Network *network);
|
||||
int ipv4ll_update_mac(Link *link);
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll);
|
||||
|
@ -665,11 +665,9 @@ int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_
|
||||
if (r == 0)
|
||||
return 1; /* Polkit will call us back */
|
||||
|
||||
r = link_reconfigure(l, /* force = */ true);
|
||||
if (r > 0)
|
||||
r = link_save_and_clean_full(l, /* also_save_manager = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = link_reconfigure_full(l, LINK_RECONFIGURE_UNCONDITIONALLY | LINK_RECONFIGURE_CLEANLY, message, /* counter = */ NULL);
|
||||
if (r != 0)
|
||||
return r; /* Will reply later when r > 0. */
|
||||
|
||||
return sd_bus_reply_method_return(message, NULL);
|
||||
}
|
||||
|
@ -445,7 +445,7 @@ void link_enter_failed(Link *link) {
|
||||
}
|
||||
|
||||
log_link_info(link, "Trying to reconfigure the interface.");
|
||||
if (link_reconfigure(link, /* force = */ true) > 0)
|
||||
if (link_reconfigure(link, LINK_RECONFIGURE_UNCONDITIONALLY) > 0)
|
||||
return;
|
||||
|
||||
stop:
|
||||
@ -1008,7 +1008,7 @@ static int link_drop_requests(Link *link) {
|
||||
|
||||
/* If the request is already called, but its reply is not received, then we need to
|
||||
* drop the configuration (e.g. address) here. Note, if the configuration is known,
|
||||
* it will be handled later by link_drop_foreign_addresses() or so. */
|
||||
* it will be handled later by link_drop_unmanaged_addresses() or so. */
|
||||
if (req->waiting_reply && link->state != LINK_STATE_LINGER)
|
||||
switch (req->type) {
|
||||
case REQUEST_TYPE_ADDRESS: {
|
||||
@ -1086,33 +1086,23 @@ static Link *link_drop(Link *link) {
|
||||
return link_unref(link);
|
||||
}
|
||||
|
||||
static int link_drop_foreign_config(Link *link) {
|
||||
static int link_drop_unmanaged_config(Link *link) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->state == LINK_STATE_CONFIGURING);
|
||||
assert(link->manager);
|
||||
|
||||
/* Drop foreign config, but ignore unmanaged, loopback, or critical interfaces. We do not want
|
||||
* to remove loopback address or addresses used for root NFS. */
|
||||
|
||||
if (IN_SET(link->state, LINK_STATE_UNMANAGED, LINK_STATE_PENDING, LINK_STATE_INITIALIZED))
|
||||
return 0;
|
||||
if (FLAGS_SET(link->flags, IFF_LOOPBACK))
|
||||
return 0;
|
||||
if (link->network->keep_configuration == KEEP_CONFIGURATION_YES)
|
||||
return 0;
|
||||
|
||||
r = link_drop_foreign_routes(link);
|
||||
|
||||
RET_GATHER(r, link_drop_foreign_nexthops(link));
|
||||
RET_GATHER(r, link_drop_foreign_addresses(link));
|
||||
RET_GATHER(r, link_drop_foreign_neighbors(link));
|
||||
RET_GATHER(r, manager_drop_foreign_routing_policy_rules(link->manager));
|
||||
r = link_drop_unmanaged_routes(link);
|
||||
RET_GATHER(r, link_drop_unmanaged_nexthops(link));
|
||||
RET_GATHER(r, link_drop_unmanaged_addresses(link));
|
||||
RET_GATHER(r, link_drop_unmanaged_neighbors(link));
|
||||
RET_GATHER(r, link_drop_unmanaged_routing_policy_rules(link));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int link_drop_managed_config(Link *link) {
|
||||
static int link_drop_static_config(Link *link) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
@ -1127,15 +1117,26 @@ static int link_drop_managed_config(Link *link) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static void link_foreignize_config(Link *link) {
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
static int link_drop_dynamic_config(Link *link, Network *network) {
|
||||
int r;
|
||||
|
||||
link_foreignize_routes(link);
|
||||
link_foreignize_nexthops(link);
|
||||
link_foreignize_addresses(link);
|
||||
link_foreignize_neighbors(link);
|
||||
link_foreignize_routing_policy_rules(link);
|
||||
assert(link);
|
||||
assert(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_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);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int link_configure(Link *link) {
|
||||
@ -1252,7 +1253,7 @@ static int link_configure(Link *link) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = link_drop_foreign_config(link);
|
||||
r = link_drop_unmanaged_config(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1320,7 +1321,31 @@ static int link_get_network(Link *link, Network **ret) {
|
||||
return log_link_debug_errno(link, SYNTHETIC_ERRNO(ENOENT), "No matching .network found.");
|
||||
}
|
||||
|
||||
int link_reconfigure_impl(Link *link, bool force) {
|
||||
static void link_enter_unmanaged(Link *link) {
|
||||
assert(link);
|
||||
|
||||
if (link->state == LINK_STATE_UNMANAGED)
|
||||
return;
|
||||
|
||||
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_drop_requests(link);
|
||||
(void) link_drop_static_config(link);
|
||||
|
||||
/* The bound_to map depends on .network file, hence it needs to be freed. But, do not free the
|
||||
* bound_by map. Otherwise, if a link enters unmanaged state below, then its carrier state will
|
||||
* not propagated to other interfaces anymore. Moreover, it is not necessary to recreate the
|
||||
* map here, as it depends on .network files assigned to other links. */
|
||||
link_free_bound_to_list(link);
|
||||
link_free_engines(link);
|
||||
|
||||
link->network = network_unref(link->network);
|
||||
link_set_state(link, LINK_STATE_UNMANAGED);
|
||||
}
|
||||
|
||||
int link_reconfigure_impl(Link *link, LinkReconfigurationFlag flags) {
|
||||
Network *network = NULL;
|
||||
int r;
|
||||
|
||||
@ -1336,74 +1361,65 @@ int link_reconfigure_impl(Link *link, bool force) {
|
||||
return 0;
|
||||
|
||||
r = link_get_network(link, &network);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return r;
|
||||
|
||||
if (link->state != LINK_STATE_UNMANAGED && !network)
|
||||
/* If link is in initialized state, then link->network is also NULL. */
|
||||
force = true;
|
||||
|
||||
if (link->network == network && !force)
|
||||
if (r == -ENOENT) {
|
||||
link_enter_unmanaged(link);
|
||||
return 0;
|
||||
|
||||
if (network) {
|
||||
_cleanup_free_ char *joined = strv_join(network->dropins, ", ");
|
||||
|
||||
if (link->state == LINK_STATE_INITIALIZED)
|
||||
log_link_info(link, "Configuring with %s%s%s%s.",
|
||||
network->filename,
|
||||
isempty(joined) ? "" : " (dropins: ",
|
||||
joined,
|
||||
isempty(joined) ? "" : ")");
|
||||
else
|
||||
log_link_info(link, "Reconfiguring with %s%s%s%s.",
|
||||
network->filename,
|
||||
isempty(joined) ? "" : " (dropins: ",
|
||||
joined,
|
||||
isempty(joined) ? "" : ")");
|
||||
} else
|
||||
log_link_full(link, link->state == LINK_STATE_INITIALIZED ? LOG_DEBUG : LOG_INFO,
|
||||
"Unmanaging interface.");
|
||||
|
||||
/* Dropping old .network file */
|
||||
r = link_stop_engines(link, false);
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (link->network == network && !FLAGS_SET(flags, LINK_RECONFIGURE_UNCONDITIONALLY))
|
||||
return 0;
|
||||
|
||||
_cleanup_free_ char *joined = strv_join(network->dropins, ", ");
|
||||
if (link->network)
|
||||
log_link_info(link, "Reconfiguring with %s%s%s%s.",
|
||||
network->filename,
|
||||
isempty(joined) ? "" : " (dropins: ",
|
||||
joined,
|
||||
isempty(joined) ? "" : ")");
|
||||
else
|
||||
log_link_info(link, "Configuring with %s%s%s%s.",
|
||||
network->filename,
|
||||
isempty(joined) ? "" : " (dropins: ",
|
||||
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;
|
||||
}
|
||||
|
||||
r = link_drop_requests(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (network && !force && network->keep_configuration != KEEP_CONFIGURATION_YES)
|
||||
/* When a new/updated .network file is assigned, first make all configs (addresses,
|
||||
* routes, and so on) foreign, and then drop unnecessary configs later by
|
||||
* link_drop_foreign_config() in link_configure().
|
||||
* Note, when KeepConfiguration=yes, link_drop_foreign_config() does nothing. Hence,
|
||||
* here we need to drop the configs such as addresses, routes, and so on configured by
|
||||
* the previously assigned .network file. */
|
||||
link_foreignize_config(link);
|
||||
else {
|
||||
/* Remove all managed configs. Note, foreign configs are removed in later by
|
||||
* link_configure() -> link_drop_foreign_config() if the link is managed by us. */
|
||||
r = link_drop_managed_config(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* The bound_to map depends on .network file, hence it needs to be freed. But, do not free the
|
||||
* bound_by map. Otherwise, if a link enters unmanaged state below, then its carrier state will
|
||||
* not propagated to other interfaces anymore. Moreover, it is not necessary to recreate the
|
||||
* map here, as it depends on .network files assigned to other links. */
|
||||
link_free_bound_to_list(link);
|
||||
|
||||
link_free_engines(link);
|
||||
link->network = network_unref(link->network);
|
||||
|
||||
if (!network) {
|
||||
link_set_state(link, LINK_STATE_UNMANAGED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Then, apply new .network file */
|
||||
link->network = network_ref(network);
|
||||
link_update_operstate(link, true);
|
||||
@ -1419,63 +1435,14 @@ int link_reconfigure_impl(Link *link, bool force) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool force) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
r = link_getlink_handler_internal(rtnl, m, link, "Failed to update link state");
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = link_reconfigure_impl(link, force);
|
||||
if (r < 0) {
|
||||
link_enter_failed(link);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int link_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
||||
return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false);
|
||||
}
|
||||
|
||||
static int link_force_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
||||
return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ true);
|
||||
}
|
||||
|
||||
int link_reconfigure(Link *link, bool force) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
/* When link in pending or initialized state, then link_configure() will be called. To prevent
|
||||
* the function from being called multiple times simultaneously, refuse to reconfigure the
|
||||
* interface in these cases. */
|
||||
if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED, LINK_STATE_LINGER))
|
||||
return 0; /* 0 means no-op. */
|
||||
|
||||
r = link_call_getlink(link, force ? link_force_reconfigure_handler : link_reconfigure_handler);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
|
||||
link_enter_failed(link);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (force || link->state == LINK_STATE_FAILED)
|
||||
link_set_state(link, LINK_STATE_INITIALIZED);
|
||||
|
||||
return 1; /* 1 means the interface will be reconfigured. */
|
||||
}
|
||||
|
||||
typedef struct ReconfigureData {
|
||||
typedef struct LinkReconfigurationData {
|
||||
Link *link;
|
||||
Manager *manager;
|
||||
LinkReconfigurationFlag flags;
|
||||
sd_bus_message *message;
|
||||
} ReconfigureData;
|
||||
unsigned *counter;
|
||||
} LinkReconfigurationData;
|
||||
|
||||
static ReconfigureData* reconfigure_data_free(ReconfigureData *data) {
|
||||
static LinkReconfigurationData* link_reconfiguration_data_free(LinkReconfigurationData *data) {
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
@ -1485,56 +1452,72 @@ static ReconfigureData* reconfigure_data_free(ReconfigureData *data) {
|
||||
return mfree(data);
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(ReconfigureData*, reconfigure_data_free);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(LinkReconfigurationData*, link_reconfiguration_data_free);
|
||||
|
||||
static void reconfigure_data_destroy_callback(ReconfigureData *data) {
|
||||
static void link_reconfiguration_data_destroy_callback(LinkReconfigurationData *data) {
|
||||
int r;
|
||||
|
||||
assert(data);
|
||||
assert(data->manager);
|
||||
assert(data->manager->reloading > 0);
|
||||
assert(data->message);
|
||||
|
||||
data->manager->reloading--;
|
||||
if (data->manager->reloading <= 0) {
|
||||
r = sd_bus_reply_method_return(data->message, NULL);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to send reply for 'Reload' DBus method, ignoring: %m");
|
||||
if (data->message) {
|
||||
if (data->counter) {
|
||||
assert(*data->counter > 0);
|
||||
(*data->counter)--;
|
||||
}
|
||||
|
||||
if (!data->counter || *data->counter <= 0) {
|
||||
r = sd_bus_reply_method_return(data->message, NULL);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to reply for DBus method, ignoring: %m");
|
||||
}
|
||||
}
|
||||
|
||||
reconfigure_data_free(data);
|
||||
link_reconfiguration_data_free(data);
|
||||
}
|
||||
|
||||
static int reconfigure_handler_on_bus_method_reload(sd_netlink *rtnl, sd_netlink_message *m, ReconfigureData *data) {
|
||||
assert(data);
|
||||
assert(data->link);
|
||||
return link_reconfigure_handler_internal(rtnl, m, data->link, /* force = */ false);
|
||||
static int link_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, LinkReconfigurationData *data) {
|
||||
Link *link = ASSERT_PTR(ASSERT_PTR(data)->link);
|
||||
int r;
|
||||
|
||||
r = link_getlink_handler_internal(rtnl, m, link, "Failed to update link state");
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = link_reconfigure_impl(link, data->flags);
|
||||
if (r < 0) {
|
||||
link_enter_failed(link);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int link_reconfigure_on_bus_method_reload(Link *link, sd_bus_message *message) {
|
||||
int link_reconfigure_full(Link *link, LinkReconfigurationFlag flags, sd_bus_message *message, unsigned *counter) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
|
||||
_cleanup_(reconfigure_data_freep) ReconfigureData *data = NULL;
|
||||
_cleanup_(link_reconfiguration_data_freep) LinkReconfigurationData *data = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
assert(link->manager->rtnl);
|
||||
assert(message);
|
||||
|
||||
/* See comments in link_reconfigure() above. */
|
||||
/* When the link is in the pending or initialized state, link_reconfigure_impl() will be called later
|
||||
* by link_initialized() or link_initialized_and_synced(). To prevent the function from being called
|
||||
* multiple times simultaneously, let's refuse to reconfigure the interface here in such cases. */
|
||||
if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED, LINK_STATE_LINGER))
|
||||
return 0;
|
||||
return 0; /* 0 means no-op. */
|
||||
|
||||
data = new(ReconfigureData, 1);
|
||||
data = new(LinkReconfigurationData, 1);
|
||||
if (!data) {
|
||||
r = -ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
*data = (ReconfigureData) {
|
||||
*data = (LinkReconfigurationData) {
|
||||
.link = link_ref(link),
|
||||
.manager = link->manager,
|
||||
.message = sd_bus_message_ref(message),
|
||||
.flags = flags,
|
||||
.message = sd_bus_message_ref(message), /* message may be NULL, but _ref() works fine. */
|
||||
.counter = counter,
|
||||
};
|
||||
|
||||
r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_GETLINK, link->ifindex);
|
||||
@ -1542,18 +1525,19 @@ int link_reconfigure_on_bus_method_reload(Link *link, sd_bus_message *message) {
|
||||
goto failed;
|
||||
|
||||
r = netlink_call_async(link->manager->rtnl, NULL, req,
|
||||
reconfigure_handler_on_bus_method_reload,
|
||||
reconfigure_data_destroy_callback, data);
|
||||
link_reconfigure_handler,
|
||||
link_reconfiguration_data_destroy_callback, data);
|
||||
if (r < 0)
|
||||
goto failed;
|
||||
|
||||
TAKE_PTR(data);
|
||||
link->manager->reloading++;
|
||||
if (counter)
|
||||
(*counter)++;
|
||||
|
||||
if (link->state == LINK_STATE_FAILED)
|
||||
link_set_state(link, LINK_STATE_INITIALIZED);
|
||||
|
||||
return 0;
|
||||
return 1; /* 1 means the interface will be reconfigured, and bus method will be replied later. */
|
||||
|
||||
failed:
|
||||
log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
|
||||
@ -1580,7 +1564,7 @@ static int link_initialized_and_synced(Link *link) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return link_reconfigure_impl(link, /* force = */ false);
|
||||
return link_reconfigure_impl(link, /* flags = */ 0);
|
||||
}
|
||||
|
||||
static int link_initialized_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
||||
@ -1624,7 +1608,7 @@ static int link_initialized(Link *link, sd_device *device) {
|
||||
log_link_warning_errno(link, r, "Failed to manage SR-IOV PF and VF ports, ignoring: %m");
|
||||
|
||||
if (link->state != LINK_STATE_PENDING)
|
||||
return link_reconfigure(link, /* force = */ false);
|
||||
return link_reconfigure(link, /* flags = */ 0);
|
||||
|
||||
log_link_debug(link, "udev initialized link");
|
||||
|
||||
@ -1726,7 +1710,7 @@ int manager_udev_process_link(Manager *m, sd_device *device, sd_device_action_t
|
||||
}
|
||||
|
||||
static int link_carrier_gained(Link *link) {
|
||||
bool force_reconfigure;
|
||||
LinkReconfigurationFlag flags = 0;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
@ -1752,11 +1736,12 @@ static int link_carrier_gained(Link *link) {
|
||||
* already assigned to the interface (in that case, the .network file does not have the SSID=
|
||||
* setting in the [Match] section), and the interface is already being configured. Of course,
|
||||
* there may exist another .network file with higher priority and a matching SSID= setting. But
|
||||
* in that case, link_reconfigure_impl() can handle that without the force_reconfigure flag.
|
||||
* in that case, link_reconfigure_impl() can handle that without any flags.
|
||||
*
|
||||
* For non-wireless interfaces, we have no way to detect the connected network change. So,
|
||||
* setting force_reconfigure = false. Note, both ssid and previous_ssid are NULL in that case. */
|
||||
force_reconfigure = link->previous_ssid && !streq_ptr(link->previous_ssid, link->ssid);
|
||||
* we do not set any flags here. Note, both ssid and previous_ssid are NULL in that case. */
|
||||
if (link->previous_ssid && !streq_ptr(link->previous_ssid, link->ssid))
|
||||
flags |= LINK_RECONFIGURE_UNCONDITIONALLY | LINK_RECONFIGURE_CLEANLY;
|
||||
link->previous_ssid = mfree(link->previous_ssid);
|
||||
|
||||
/* AP and P2P-GO interfaces may have a new SSID - update the link properties in case a new .network
|
||||
@ -1771,7 +1756,7 @@ static int link_carrier_gained(Link *link) {
|
||||
/* At this stage, both wlan and link information should be up-to-date. Hence, it is not necessary to
|
||||
* call RTM_GETLINK, NL80211_CMD_GET_INTERFACE, or NL80211_CMD_GET_STATION commands, and simply call
|
||||
* link_reconfigure_impl(). Note, link_reconfigure_impl() returns 1 when the link is reconfigured. */
|
||||
r = link_reconfigure_impl(link, force_reconfigure);
|
||||
r = link_reconfigure_impl(link, flags);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
@ -1808,7 +1793,7 @@ static int link_carrier_lost_impl(Link *link) {
|
||||
return ret;
|
||||
|
||||
RET_GATHER(ret, link_stop_engines(link, false));
|
||||
RET_GATHER(ret, link_drop_managed_config(link));
|
||||
RET_GATHER(ret, link_drop_static_config(link));
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -2866,7 +2851,7 @@ int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Man
|
||||
if (manager->enumerating)
|
||||
return 0;
|
||||
|
||||
r = link_reconfigure_impl(link, /* force = */ false);
|
||||
r = link_reconfigure_impl(link, /* flags = */ 0);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
|
||||
link_enter_failed(link);
|
||||
|
@ -42,6 +42,11 @@ typedef enum LinkState {
|
||||
_LINK_STATE_INVALID = -EINVAL,
|
||||
} LinkState;
|
||||
|
||||
typedef enum LinkReconfigurationFlag {
|
||||
LINK_RECONFIGURE_UNCONDITIONALLY = 1 << 0, /* Reconfigure an interface even if .network file is unchanged. */
|
||||
LINK_RECONFIGURE_CLEANLY = 1 << 1, /* Drop all existing configs before reconfiguring. Otherwise, reuse existing configs as possible as we can. */
|
||||
} LinkReconfigurationFlag;
|
||||
|
||||
typedef struct Manager Manager;
|
||||
typedef struct Network Network;
|
||||
typedef struct NetDev NetDev;
|
||||
@ -259,9 +264,11 @@ LinkState link_state_from_string(const char *s) _pure_;
|
||||
|
||||
int link_request_stacked_netdevs(Link *link, NetDevLocalAddressType type);
|
||||
|
||||
int link_reconfigure_impl(Link *link, bool force);
|
||||
int link_reconfigure(Link *link, bool force);
|
||||
int link_reconfigure_on_bus_method_reload(Link *link, sd_bus_message *message);
|
||||
int link_reconfigure_impl(Link *link, LinkReconfigurationFlag flags);
|
||||
int link_reconfigure_full(Link *link, LinkReconfigurationFlag flags, sd_bus_message *message, unsigned *counter);
|
||||
static inline int link_reconfigure(Link *link, LinkReconfigurationFlag flags) {
|
||||
return link_reconfigure_full(link, flags, NULL, NULL);
|
||||
}
|
||||
|
||||
int link_check_initialized(Link *link);
|
||||
|
||||
|
@ -89,7 +89,7 @@ static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_b
|
||||
log_debug("Coming back from suspend, reconfiguring all connections...");
|
||||
|
||||
HASHMAP_FOREACH(link, m->links_by_index)
|
||||
(void) link_reconfigure(link, /* force = */ true);
|
||||
(void) link_reconfigure(link, LINK_RECONFIGURE_UNCONDITIONALLY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1221,12 +1221,9 @@ int manager_reload(Manager *m, sd_bus_message *message) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH(link, m->links_by_index) {
|
||||
if (message)
|
||||
(void) link_reconfigure_on_bus_method_reload(link, message);
|
||||
else
|
||||
(void) link_reconfigure(link, /* force = */ false);
|
||||
}
|
||||
HASHMAP_FOREACH(link, m->links_by_index)
|
||||
(void) link_reconfigure_full(link, /* flags = */ 0, message,
|
||||
/* counter = */ message ? &m->reloading : NULL);
|
||||
|
||||
log_debug("Reloaded.");
|
||||
r = 0;
|
||||
|
@ -2668,6 +2668,90 @@ int link_request_ndisc(Link *link) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int link_drop_ndisc_config(Link *link, Network *network) {
|
||||
int r, ret = 0;
|
||||
|
||||
assert(link);
|
||||
assert(network);
|
||||
|
||||
if (!link_ndisc_enabled(link))
|
||||
return 0; /* Currently DHCPv4 client is not enabled, there is nothing we need to drop. */
|
||||
|
||||
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. */
|
||||
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
|
||||
* settings for the client may be different. Let's unref() the client. */
|
||||
link->ndisc = sd_ndisc_unref(link->ndisc);
|
||||
|
||||
/* Redirect messages will be ignored. Drop configurations based on the previously received redirect
|
||||
* messages. */
|
||||
if (!network->ndisc_use_redirect)
|
||||
(void) ndisc_drop_redirect(link, /* router = */ NULL);
|
||||
|
||||
/* If one of the route setting is changed, drop all routes. */
|
||||
if (link->network->ndisc_use_gateway != network->ndisc_use_gateway ||
|
||||
link->network->ndisc_use_route_prefix != network->ndisc_use_route_prefix ||
|
||||
link->network->ndisc_use_onlink_prefix != network->ndisc_use_onlink_prefix ||
|
||||
link->network->ndisc_quickack != network->ndisc_quickack ||
|
||||
link->network->ndisc_route_metric_high != network->ndisc_route_metric_high ||
|
||||
link->network->ndisc_route_metric_medium != network->ndisc_route_metric_medium ||
|
||||
link->network->ndisc_route_metric_low != network->ndisc_route_metric_low ||
|
||||
!set_equal(link->network->ndisc_deny_listed_router, network->ndisc_deny_listed_router) ||
|
||||
!set_equal(link->network->ndisc_allow_listed_router, network->ndisc_allow_listed_router) ||
|
||||
!set_equal(link->network->ndisc_deny_listed_prefix, network->ndisc_deny_listed_prefix) ||
|
||||
!set_equal(link->network->ndisc_allow_listed_prefix, network->ndisc_allow_listed_prefix) ||
|
||||
!set_equal(link->network->ndisc_deny_listed_route_prefix, network->ndisc_deny_listed_route_prefix) ||
|
||||
!set_equal(link->network->ndisc_allow_listed_route_prefix, network->ndisc_allow_listed_route_prefix)) {
|
||||
Route *route;
|
||||
SET_FOREACH(route, link->manager->routes) {
|
||||
if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
|
||||
continue;
|
||||
|
||||
if (route->nexthop.ifindex != link->ifindex)
|
||||
continue;
|
||||
|
||||
if (route->protocol == RTPROT_REDIRECT)
|
||||
continue; /* redirect route is handled by ndisc_drop_redirect(). */
|
||||
|
||||
r = route_remove_and_cancel(route, link->manager);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove SLAAC route, ignoring: %m"));
|
||||
}
|
||||
}
|
||||
|
||||
/* If SLAAC address is disabled, drop all addresses. */
|
||||
if (!network->ndisc_use_autonomous_prefix ||
|
||||
!set_equal(link->network->ndisc_tokens, network->ndisc_tokens) ||
|
||||
!set_equal(link->network->ndisc_deny_listed_prefix, network->ndisc_deny_listed_prefix) ||
|
||||
!set_equal(link->network->ndisc_allow_listed_prefix, network->ndisc_allow_listed_prefix)) {
|
||||
Address *address;
|
||||
SET_FOREACH(address, link->addresses) {
|
||||
if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
|
||||
continue;
|
||||
|
||||
r = address_remove_and_cancel(address, link);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove SLAAC address, ignoring: %m"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!network->ndisc_use_mtu)
|
||||
link->ndisc_mtu = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ndisc_stop(Link *link) {
|
||||
assert(link);
|
||||
|
||||
|
@ -69,6 +69,7 @@ int ndisc_stop(Link *link);
|
||||
void ndisc_flush(Link *link);
|
||||
|
||||
int link_request_ndisc(Link *link);
|
||||
int link_drop_ndisc_config(Link *link, Network *network);
|
||||
int ndisc_reconfigure_address(Address *address, Link *link);
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_start_dhcp6_client);
|
||||
|
@ -467,7 +467,7 @@ int neighbor_remove(Neighbor *neighbor, Link *link) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int link_drop_foreign_neighbors(Link *link) {
|
||||
int link_drop_unmanaged_neighbors(Link *link) {
|
||||
Neighbor *neighbor;
|
||||
int r = 0;
|
||||
|
||||
@ -476,14 +476,15 @@ int link_drop_foreign_neighbors(Link *link) {
|
||||
|
||||
/* First, mark all neighbors. */
|
||||
SET_FOREACH(neighbor, link->neighbors) {
|
||||
/* Do not remove neighbors we configured. */
|
||||
if (neighbor->source != NETWORK_CONFIG_SOURCE_FOREIGN)
|
||||
continue;
|
||||
|
||||
/* Ignore neighbors not assigned yet or already removing. */
|
||||
if (!neighbor_exists(neighbor))
|
||||
continue;
|
||||
|
||||
/* Ignore foreign neighbors when KeepConfiguration=yes or static. */
|
||||
if (neighbor->source == NETWORK_CONFIG_SOURCE_FOREIGN &&
|
||||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
|
||||
continue;
|
||||
|
||||
neighbor_mark(neighbor);
|
||||
}
|
||||
|
||||
@ -495,6 +496,7 @@ int link_drop_foreign_neighbors(Link *link) {
|
||||
neighbor_unmark(existing);
|
||||
}
|
||||
|
||||
/* Finally, remove all marked neighbors. */
|
||||
SET_FOREACH(neighbor, link->neighbors) {
|
||||
if (!neighbor_is_marked(neighbor))
|
||||
continue;
|
||||
@ -526,15 +528,6 @@ int link_drop_static_neighbors(Link *link) {
|
||||
return r;
|
||||
}
|
||||
|
||||
void link_foreignize_neighbors(Link *link) {
|
||||
Neighbor *neighbor;
|
||||
|
||||
assert(link);
|
||||
|
||||
SET_FOREACH(neighbor, link->neighbors)
|
||||
neighbor->source = NETWORK_CONFIG_SOURCE_FOREIGN;
|
||||
}
|
||||
|
||||
int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
|
||||
_cleanup_(neighbor_unrefp) Neighbor *tmp = NULL;
|
||||
Neighbor *neighbor = NULL;
|
||||
|
@ -36,8 +36,7 @@ int neighbor_remove(Neighbor *neighbor, Link *link);
|
||||
int network_drop_invalid_neighbors(Network *network);
|
||||
|
||||
int link_drop_static_neighbors(Link *link);
|
||||
int link_drop_foreign_neighbors(Link *link);
|
||||
void link_foreignize_neighbors(Link *link);
|
||||
int link_drop_unmanaged_neighbors(Link *link);
|
||||
|
||||
int link_request_static_neighbors(Link *link);
|
||||
|
||||
|
@ -856,9 +856,10 @@ static bool nexthop_can_update(const NextHop *assigned_nexthop, const NextHop *r
|
||||
return true;
|
||||
}
|
||||
|
||||
static void link_mark_nexthops(Link *link, bool foreign) {
|
||||
int link_drop_nexthops(Link *link, bool only_static) {
|
||||
NextHop *nexthop;
|
||||
Link *other;
|
||||
int r = 0;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
@ -869,14 +870,21 @@ static void link_mark_nexthops(Link *link, bool foreign) {
|
||||
if (nexthop->protocol == RTPROT_KERNEL)
|
||||
continue;
|
||||
|
||||
/* When 'foreign' is true, mark only foreign nexthops, and vice versa. */
|
||||
if (nexthop->source != (foreign ? NETWORK_CONFIG_SOURCE_FOREIGN : NETWORK_CONFIG_SOURCE_STATIC))
|
||||
continue;
|
||||
|
||||
/* Ignore nexthops not assigned yet or already removed. */
|
||||
if (!nexthop_exists(nexthop))
|
||||
continue;
|
||||
|
||||
if (only_static) {
|
||||
if (nexthop->source != NETWORK_CONFIG_SOURCE_STATIC)
|
||||
continue;
|
||||
} else {
|
||||
/* Do not mark foreign nexthop when KeepConfiguration= is enabled. */
|
||||
if (nexthop->source == NETWORK_CONFIG_SOURCE_FOREIGN &&
|
||||
link->network &&
|
||||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Ignore nexthops bound to other links. */
|
||||
if (nexthop->ifindex > 0 && nexthop->ifindex != link->ifindex)
|
||||
continue;
|
||||
@ -886,7 +894,7 @@ static void link_mark_nexthops(Link *link, bool foreign) {
|
||||
|
||||
/* Then, unmark all nexthops requested by active links. */
|
||||
HASHMAP_FOREACH(other, link->manager->links_by_index) {
|
||||
if (!foreign && other == link)
|
||||
if (only_static && other == link)
|
||||
continue;
|
||||
|
||||
if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
|
||||
@ -905,17 +913,8 @@ static void link_mark_nexthops(Link *link, bool foreign) {
|
||||
nexthop_unmark(existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int link_drop_nexthops(Link *link, bool foreign) {
|
||||
NextHop *nexthop;
|
||||
int r = 0;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
|
||||
link_mark_nexthops(link, foreign);
|
||||
|
||||
/* Finally, remove all marked nexthops. */
|
||||
HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
|
||||
if (!nexthop_is_marked(nexthop))
|
||||
continue;
|
||||
@ -926,22 +925,6 @@ int link_drop_nexthops(Link *link, bool foreign) {
|
||||
return r;
|
||||
}
|
||||
|
||||
void link_foreignize_nexthops(Link *link) {
|
||||
NextHop *nexthop;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
|
||||
link_mark_nexthops(link, /* foreign = */ false);
|
||||
|
||||
HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
|
||||
if (!nexthop_is_marked(nexthop))
|
||||
continue;
|
||||
|
||||
nexthop->source = NETWORK_CONFIG_SOURCE_FOREIGN;
|
||||
}
|
||||
}
|
||||
|
||||
static int nexthop_update_group(NextHop *nexthop, sd_netlink_message *message) {
|
||||
_cleanup_hashmap_free_free_ Hashmap *h = NULL;
|
||||
_cleanup_free_ struct nexthop_grp *group = NULL;
|
||||
|
@ -53,14 +53,13 @@ int nexthop_remove(NextHop *nexthop, Manager *manager);
|
||||
|
||||
int network_drop_invalid_nexthops(Network *network);
|
||||
|
||||
int link_drop_nexthops(Link *link, bool foreign);
|
||||
static inline int link_drop_foreign_nexthops(Link *link) {
|
||||
return link_drop_nexthops(link, /* foreign = */ true);
|
||||
int link_drop_nexthops(Link *link, bool only_static);
|
||||
static inline int link_drop_unmanaged_nexthops(Link *link) {
|
||||
return link_drop_nexthops(link, /* only_static = */ false);
|
||||
}
|
||||
static inline int link_drop_static_nexthops(Link *link) {
|
||||
return link_drop_nexthops(link, /* foreign = */ false);
|
||||
return link_drop_nexthops(link, /* only_static = */ true);
|
||||
}
|
||||
void link_foreignize_nexthops(Link *link);
|
||||
|
||||
int link_request_static_nexthops(Link *link, bool only_ipv4);
|
||||
|
||||
|
@ -645,6 +645,22 @@ int link_request_radv(Link *link) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int link_drop_radv_config(Link *link, Network *network) {
|
||||
int ret = 0;
|
||||
|
||||
assert(link);
|
||||
assert(network);
|
||||
|
||||
if (!link_radv_enabled(link))
|
||||
return 0;
|
||||
|
||||
// FIXME: check detailed settings and do not stop if nothing changed.
|
||||
// FIXME: save dynamic prefixes acquired by DHCP-PD.
|
||||
ret = sd_radv_stop(link->radv);
|
||||
link->radv = sd_radv_unref(link->radv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int radv_start(Link *link) {
|
||||
int r;
|
||||
|
||||
|
@ -68,6 +68,7 @@ int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_le
|
||||
usec_t lifetime_preferred_usec, usec_t lifetime_valid_usec);
|
||||
|
||||
int link_request_radv(Link *link);
|
||||
int link_drop_radv_config(Link *link, Network *network);
|
||||
|
||||
const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_;
|
||||
RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_;
|
||||
|
@ -1439,10 +1439,10 @@ static int link_unmark_route(Link *link, const Route *route, const RouteNextHop
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int link_mark_routes(Link *link, bool foreign) {
|
||||
int link_drop_routes(Link *link, bool only_static) {
|
||||
Route *route;
|
||||
Link *other;
|
||||
int r;
|
||||
int r = 0;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
@ -1453,31 +1453,35 @@ static int link_mark_routes(Link *link, bool foreign) {
|
||||
if (route_by_kernel(route))
|
||||
continue;
|
||||
|
||||
/* When 'foreign' is true, mark only foreign routes, and vice versa.
|
||||
* Note, do not touch dynamic routes. They will removed by when e.g. lease is lost. */
|
||||
if (route->source != (foreign ? NETWORK_CONFIG_SOURCE_FOREIGN : NETWORK_CONFIG_SOURCE_STATIC))
|
||||
continue;
|
||||
|
||||
/* Ignore routes not assigned yet or already removed. */
|
||||
if (!route_exists(route))
|
||||
continue;
|
||||
|
||||
if (link->network) {
|
||||
if (route->protocol == RTPROT_STATIC &&
|
||||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
|
||||
if (only_static) {
|
||||
if (route->source != NETWORK_CONFIG_SOURCE_STATIC)
|
||||
continue;
|
||||
} else {
|
||||
/* Ignore dynamically assigned routes. */
|
||||
if (!IN_SET(route->source, NETWORK_CONFIG_SOURCE_FOREIGN, NETWORK_CONFIG_SOURCE_STATIC))
|
||||
continue;
|
||||
|
||||
if (route->protocol == RTPROT_DHCP &&
|
||||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
|
||||
continue;
|
||||
if (route->source == NETWORK_CONFIG_SOURCE_FOREIGN && link->network) {
|
||||
if (route->protocol == RTPROT_STATIC &&
|
||||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
|
||||
continue;
|
||||
|
||||
if (IN_SET(route->protocol, RTPROT_DHCP, RTPROT_RA, RTPROT_REDIRECT) &&
|
||||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* When we mark foreign routes, do not mark routes assigned to other interfaces.
|
||||
/* When we also mark foreign routes, do not mark routes assigned to other interfaces.
|
||||
* Otherwise, routes assigned to unmanaged interfaces will be dropped.
|
||||
* Note, route_get_link() does not provide assigned link for routes with an unreachable type
|
||||
* or IPv4 multipath routes. So, the current implementation does not support managing such
|
||||
* routes by other daemon or so, unless ManageForeignRoutes=no. */
|
||||
if (foreign) {
|
||||
if (!only_static) {
|
||||
Link *route_link;
|
||||
|
||||
if (route_get_link(link->manager, route, &route_link) >= 0 && route_link != link)
|
||||
@ -1489,7 +1493,7 @@ static int link_mark_routes(Link *link, bool foreign) {
|
||||
|
||||
/* Then, unmark all routes requested by active links. */
|
||||
HASHMAP_FOREACH(other, link->manager->links_by_index) {
|
||||
if (!foreign && other == link)
|
||||
if (only_static && other == link)
|
||||
continue;
|
||||
|
||||
if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
|
||||
@ -1523,20 +1527,7 @@ static int link_mark_routes(Link *link, bool foreign) {
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int link_drop_routes(Link *link, bool foreign) {
|
||||
Route *route;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
|
||||
r = link_mark_routes(link, foreign);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Finally, remove all marked routes. */
|
||||
SET_FOREACH(route, link->manager->routes) {
|
||||
if (!route_is_marked(route))
|
||||
continue;
|
||||
@ -1547,27 +1538,6 @@ int link_drop_routes(Link *link, bool foreign) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int link_foreignize_routes(Link *link) {
|
||||
Route *route;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
|
||||
r = link_mark_routes(link, /* foreign = */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
SET_FOREACH(route, link->manager->routes) {
|
||||
if (!route_is_marked(route))
|
||||
continue;
|
||||
|
||||
route->source = NETWORK_CONFIG_SOURCE_FOREIGN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int network_add_ipv4ll_route(Network *network) {
|
||||
_cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
|
||||
unsigned section_line;
|
||||
|
@ -102,14 +102,13 @@ int route_get_request(Manager *manager, const Route *route, Request **ret);
|
||||
|
||||
bool route_can_update(const Route *existing, const Route *requesting);
|
||||
|
||||
int link_drop_routes(Link *link, bool foreign);
|
||||
int link_drop_routes(Link *link, bool only_static);
|
||||
static inline int link_drop_static_routes(Link *link) {
|
||||
return link_drop_routes(link, false);
|
||||
}
|
||||
static inline int link_drop_foreign_routes(Link *link) {
|
||||
return link_drop_routes(link, true);
|
||||
}
|
||||
int link_foreignize_routes(Link *link);
|
||||
static inline int link_drop_unmanaged_routes(Link *link) {
|
||||
return link_drop_routes(link, false);
|
||||
}
|
||||
|
||||
int link_request_route(
|
||||
Link *link,
|
||||
|
@ -800,21 +800,30 @@ static void manager_unmark_routing_policy_rule(Manager *m, const RoutingPolicyRu
|
||||
routing_policy_rule_unmark(existing);
|
||||
}
|
||||
|
||||
static void manager_mark_routing_policy_rules(Manager *m, bool foreign, const Link *except) {
|
||||
int link_drop_routing_policy_rules(Link *link, bool only_static) {
|
||||
RoutingPolicyRule *rule;
|
||||
Link *link;
|
||||
int r = 0;
|
||||
|
||||
assert(m);
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
|
||||
/* First, mark all existing rules. */
|
||||
SET_FOREACH(rule, m->rules) {
|
||||
SET_FOREACH(rule, link->manager->rules) {
|
||||
/* Do not touch rules managed by kernel. */
|
||||
if (rule->protocol == RTPROT_KERNEL)
|
||||
continue;
|
||||
|
||||
/* When 'foreign' is true, mark only foreign rules, and vice versa. */
|
||||
if (rule->source != (foreign ? NETWORK_CONFIG_SOURCE_FOREIGN : NETWORK_CONFIG_SOURCE_STATIC))
|
||||
continue;
|
||||
if (only_static) {
|
||||
/* When 'only_static' is true, mark only static rules. */
|
||||
if (rule->source != NETWORK_CONFIG_SOURCE_STATIC)
|
||||
continue;
|
||||
} else {
|
||||
/* Do not mark foreign rules when KeepConfiguration= is enabled. */
|
||||
if (rule->source == NETWORK_CONFIG_SOURCE_FOREIGN &&
|
||||
link->network &&
|
||||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Ignore rules not assigned yet or already removing. */
|
||||
if (!routing_policy_rule_exists(rule))
|
||||
@ -824,57 +833,34 @@ static void manager_mark_routing_policy_rules(Manager *m, bool foreign, const Li
|
||||
}
|
||||
|
||||
/* Then, unmark all rules requested by active links. */
|
||||
HASHMAP_FOREACH(link, m->links_by_index) {
|
||||
if (link == except)
|
||||
Link *other;
|
||||
HASHMAP_FOREACH(other, link->manager->links_by_index) {
|
||||
if (only_static && other == link)
|
||||
continue;
|
||||
|
||||
if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
|
||||
if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
|
||||
continue;
|
||||
|
||||
HASHMAP_FOREACH(rule, link->network->rules_by_section) {
|
||||
HASHMAP_FOREACH(rule, other->network->rules_by_section) {
|
||||
if (IN_SET(rule->family, AF_INET, AF_INET6))
|
||||
manager_unmark_routing_policy_rule(m, rule, rule->family);
|
||||
manager_unmark_routing_policy_rule(link->manager, rule, rule->family);
|
||||
else {
|
||||
assert(rule->address_family == ADDRESS_FAMILY_YES);
|
||||
manager_unmark_routing_policy_rule(m, rule, AF_INET);
|
||||
manager_unmark_routing_policy_rule(m, rule, AF_INET6);
|
||||
manager_unmark_routing_policy_rule(link->manager, rule, AF_INET);
|
||||
manager_unmark_routing_policy_rule(link->manager, rule, AF_INET6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const Link *except) {
|
||||
RoutingPolicyRule *rule;
|
||||
int r = 0;
|
||||
|
||||
assert(m);
|
||||
|
||||
manager_mark_routing_policy_rules(m, foreign, except);
|
||||
|
||||
SET_FOREACH(rule, m->rules) {
|
||||
if (!routing_policy_rule_is_marked(rule))
|
||||
continue;
|
||||
|
||||
RET_GATHER(r, routing_policy_rule_remove(rule, m));
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void link_foreignize_routing_policy_rules(Link *link) {
|
||||
RoutingPolicyRule *rule;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
|
||||
manager_mark_routing_policy_rules(link->manager, /* foreign = */ false, link);
|
||||
|
||||
/* Finally, remove all marked rules. */
|
||||
SET_FOREACH(rule, link->manager->rules) {
|
||||
if (!routing_policy_rule_is_marked(rule))
|
||||
continue;
|
||||
|
||||
rule->source = NETWORK_CONFIG_SOURCE_FOREIGN;
|
||||
RET_GATHER(r, routing_policy_rule_remove(rule, link->manager));
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int routing_policy_rule_process_request(Request *req, Link *link, RoutingPolicyRule *rule) {
|
||||
|
@ -63,15 +63,14 @@ void network_drop_invalid_routing_policy_rules(Network *network);
|
||||
int link_request_static_routing_policy_rules(Link *link);
|
||||
|
||||
int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
|
||||
int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const Link *except);
|
||||
static inline int manager_drop_foreign_routing_policy_rules(Manager *m) {
|
||||
return manager_drop_routing_policy_rules_internal(m, true, NULL);
|
||||
|
||||
int link_drop_routing_policy_rules(Link *link, bool only_static);
|
||||
static inline int link_drop_unmanaged_routing_policy_rules(Link *link) {
|
||||
return link_drop_routing_policy_rules(link, false);
|
||||
}
|
||||
static inline int link_drop_static_routing_policy_rules(Link *link) {
|
||||
assert(link);
|
||||
return manager_drop_routing_policy_rules_internal(link->manager, false, link);
|
||||
return link_drop_routing_policy_rules(link, true);
|
||||
}
|
||||
void link_foreignize_routing_policy_rules(Link *link);
|
||||
|
||||
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(RoutingPolicyRule, routing_policy_rule);
|
||||
|
||||
|
@ -287,7 +287,7 @@ int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *mess
|
||||
* To make SSID= or other WiFi related settings in [Match] section work, let's try to
|
||||
* reconfigure the interface. */
|
||||
if (link->ssid && link_has_carrier(link)) {
|
||||
r = link_reconfigure_impl(link, /* force = */ false);
|
||||
r = link_reconfigure_impl(link, /* flags = */ 0);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
|
||||
link_enter_failed(link);
|
||||
@ -326,7 +326,7 @@ int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *mess
|
||||
}
|
||||
|
||||
/* If necessary, reconfigure based on those new properties */
|
||||
r = link_reconfigure_impl(link, /* force = */ false);
|
||||
r = link_reconfigure_impl(link, /* flags = */ 0);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
|
||||
link_enter_failed(link);
|
||||
|
@ -6704,6 +6704,7 @@ 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
|
||||
|
Loading…
Reference in New Issue
Block a user