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,
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);
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);
-
- if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
- return false;
+ assert(link->dhcp_client);
+ /* 1. Use already assigned address. */
+ Address *a;
SET_FOREACH(a, link->addresses) {
- if (a->source != NETWORK_CONFIG_SOURCE_FOREIGN)
- continue;
- if (a->family != AF_INET)
+ if (a->source != NETWORK_CONFIG_SOURCE_DHCP4)
continue;
- if (link_address_is_dynamic(link, a))
- break;
- }
-
- if (!a)
- return false;
- *ret = a->in_addr.in;
- return true;
-}
+ assert(a->family == AF_INET);
-static int dhcp4_set_request_address(Link *link) {
- struct in_addr a;
+ 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);
+ }
- assert(link);
- assert(link->network);
- assert(link->dhcp_client);
+ /* 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);
+ }
- a = 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 0;
- if (in4_addr_is_null(&a))
- (void) dhcp4_find_dynamic_address(link, &a);
+ 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))
+ continue;
- if (in4_addr_is_null(&a))
- return 0;
+ 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);
+ }
- 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) {
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,
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);
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;
} DHCP6ClientStartMode;
typedef struct Link Link;
+typedef struct Network Network;
bool link_dhcp6_with_address_enabled(Link *link);
int dhcp6_check_ready(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);
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;
+
+ assert(a->family == AF_INET);
+ return sd_ipv4ll_set_address(link->ipv4ll, &a->in_addr.in);
+ }
- return sd_ipv4ll_set_address(link->ipv4ll, &link->network->ipv4ll_start_address);
+ /* 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) {
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);
#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);
return r;
}
+static int link_drop_dynamic_config(Link *link, Network *network) {
+ int r;
+
+ 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) {
int r;
isempty(joined) ? "" : ")");
/* Dropping old .network file */
- r = link_stop_engines(link, false);
- if (r < 0)
- return r;
-
- r = link_drop_requests(link);
- if (r < 0)
- return r;
if (FLAGS_SET(flags, LINK_RECONFIGURE_UNCONDITIONALLY)) {
/* Remove all static configurations. Note, dynamic configurations are dropped by
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;
+
/* 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);
/* Then, apply new .network file */
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);
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);
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;
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_;
'--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