#include <netinet/in.h>
#include <linux/if.h>
+#include <linux/if_arp.h>
#include <unistd.h>
#include "alloc-util.h"
#include "netlink-util.h"
#include "network-internal.h"
#include "networkd-can.h"
+#include "networkd-dhcp-server.h"
+#include "networkd-dhcp4.h"
+#include "networkd-dhcp6.h"
+#include "networkd-ipv4ll.h"
#include "networkd-ipv6-proxy-ndp.h"
+#include "networkd-link-bus.h"
+#include "networkd-link.h"
#include "networkd-lldp-tx.h"
#include "networkd-manager.h"
#include "networkd-ndisc.h"
return &link->manager->duid;
}
+int link_sysctl_ipv6_enabled(Link *link) {
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ assert(link);
+ assert(link->ifname);
+
+ if (link->sysctl_ipv6_enabled >= 0)
+ return link->sysctl_ipv6_enabled;
+
+ const char *ifname = link->ifname; /* work around bogus gcc warning */
+ r = sysctl_read_ip_property(AF_INET6, ifname, "disable_ipv6", &value);
+ if (r < 0)
+ return log_link_warning_errno(link, r,
+ "Failed to read net.ipv6.conf.%s.disable_ipv6 sysctl property: %m",
+ ifname);
+
+ link->sysctl_ipv6_enabled = value[0] == '0';
+ return link->sysctl_ipv6_enabled;
+}
+
static bool link_dhcp6_enabled(Link *link) {
assert(link);
if (link->network->bond)
return false;
- if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
+ if (link->iftype == ARPHRD_CAN)
return false;
- if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ if (link_sysctl_ipv6_enabled(link) == 0)
return false;
return link->network->dhcp & ADDRESS_FAMILY_IPV6;
if (link->network->bond)
return false;
- if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
+ if (link->iftype == ARPHRD_CAN)
return false;
return link->network->dhcp & ADDRESS_FAMILY_IPV4;
if (link->network->bond)
return false;
- if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
+ if (link->iftype == ARPHRD_CAN)
return false;
return link->network->dhcp_server;
}
-bool link_ipv4ll_enabled(Link *link, AddressFamilyBoolean mask) {
+bool link_ipv4ll_enabled(Link *link, AddressFamily mask) {
assert(link);
assert((mask & ~(ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)) == 0);
if (!link->network)
return false;
- if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "ip6gre", "ip6tnl", "sit", "vti", "vti6", "can", "vcan", "vxcan", "nlmon"))
+ if (link->iftype == ARPHRD_CAN)
return false;
- /* L3 or L3S mode do not support ARP. */
- if (IN_SET(link_get_ipvlan_mode(link), NETDEV_IPVLAN_MODE_L3, NETDEV_IPVLAN_MODE_L3S))
+ if (STRPTR_IN_SET(link->kind,
+ "vrf", "wireguard", "ipip", "gre", "ip6gre","ip6tnl", "sit", "vti",
+ "vti6", "nlmon", "xfrm"))
return false;
- if (link->network->bond)
+ /* L3 or L3S mode do not support ARP. */
+ if (IN_SET(link_get_ipvlan_mode(link), NETDEV_IPVLAN_MODE_L3, NETDEV_IPVLAN_MODE_L3S))
return false;
if (link->network->bond)
if (!link->network)
return false;
- if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "sit", "vti", "can", "vcan", "vxcan", "nlmon"))
+ if (link->iftype == ARPHRD_CAN)
+ return false;
+
+ if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "sit", "vti", "nlmon"))
return false;
if (link->network->bond)
return false;
- if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ if (link_sysctl_ipv6_enabled(link) == 0)
return false;
return link->network->link_local & ADDRESS_FAMILY_IPV6;
if (link->network->bond)
return false;
- if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ if (link_sysctl_ipv6_enabled(link) == 0)
return false;
- if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
+ if (link->iftype == ARPHRD_CAN)
return false;
/* DHCPv6 client will not be started if no IPv6 link-local address is configured. */
if (!link->network)
return false;
- if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID)
+ if (link->network->ip_forward == _ADDRESS_FAMILY_INVALID)
return false;
return link->network->ip_forward & ADDRESS_FAMILY_IPV4;
if (!link->network)
return false;
- if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID)
+ if (link->network->ip_forward == _ADDRESS_FAMILY_INVALID)
return false;
- if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ if (link_sysctl_ipv6_enabled(link) == 0)
return false;
return link->network->ip_forward & ADDRESS_FAMILY_IPV6;
void link_update_operstate(Link *link, bool also_update_master) {
LinkOperationalState operstate;
+ LinkCarrierState carrier_state;
+ LinkAddressState address_state;
+ _cleanup_strv_free_ char **p = NULL;
+ uint8_t scope = RT_SCOPE_NOWHERE;
+ bool changed = false;
+ Address *address;
Iterator i;
assert(link);
if (link->kernel_operstate == IF_OPER_DORMANT)
- operstate = LINK_OPERSTATE_DORMANT;
+ carrier_state = LINK_CARRIER_STATE_DORMANT;
else if (link_has_carrier(link)) {
- Address *address;
- uint8_t scope = RT_SCOPE_NOWHERE;
+ if (link_is_enslaved(link))
+ carrier_state = LINK_CARRIER_STATE_ENSLAVED;
+ else
+ carrier_state = LINK_CARRIER_STATE_CARRIER;
+ } else if (link->flags & IFF_UP)
+ carrier_state = LINK_CARRIER_STATE_NO_CARRIER;
+ else
+ carrier_state = LINK_CARRIER_STATE_OFF;
- /* if we have carrier, check what addresses we have */
- SET_FOREACH(address, link->addresses, i) {
- if (!address_is_ready(address))
- continue;
+ if (carrier_state >= LINK_CARRIER_STATE_CARRIER) {
+ Link *slave;
+
+ SET_FOREACH(slave, link->slaves, i) {
+ link_update_operstate(slave, false);
- if (address->scope < scope)
- scope = address->scope;
+ if (slave->carrier_state < LINK_CARRIER_STATE_CARRIER)
+ carrier_state = LINK_CARRIER_STATE_DEGRADED_CARRIER;
}
+ }
- /* for operstate we also take foreign addresses into account */
- SET_FOREACH(address, link->addresses_foreign, i) {
- if (!address_is_ready(address))
- continue;
+ SET_FOREACH(address, link->addresses, i) {
+ if (!address_is_ready(address))
+ continue;
- if (address->scope < scope)
- scope = address->scope;
- }
+ if (address->scope < scope)
+ scope = address->scope;
+ }
- if (scope < RT_SCOPE_SITE)
- /* universally accessible addresses found */
- operstate = LINK_OPERSTATE_ROUTABLE;
- else if (scope < RT_SCOPE_HOST)
- /* only link or site local addresses found */
- operstate = LINK_OPERSTATE_DEGRADED;
- else
- /* no useful addresses found */
- operstate = LINK_OPERSTATE_CARRIER;
- } else if (link->flags & IFF_UP)
- operstate = LINK_OPERSTATE_NO_CARRIER;
+ /* for operstate we also take foreign addresses into account */
+ SET_FOREACH(address, link->addresses_foreign, i) {
+ if (!address_is_ready(address))
+ continue;
+
+ if (address->scope < scope)
+ scope = address->scope;
+ }
+
+ if (scope < RT_SCOPE_SITE)
+ /* universally accessible addresses found */
+ address_state = LINK_ADDRESS_STATE_ROUTABLE;
+ else if (scope < RT_SCOPE_HOST)
+ /* only link or site local addresses found */
+ address_state = LINK_ADDRESS_STATE_DEGRADED;
else
- operstate = LINK_OPERSTATE_OFF;
+ /* no useful addresses found */
+ address_state = LINK_ADDRESS_STATE_OFF;
+
+ /* Mapping of address and carrier state vs operational state
+ * carrier state
+ * | off | no-carrier | dormant | degraded-carrier | carrier | enslaved
+ * ------------------------------------------------------------------------------
+ * off | off | no-carrier | dormant | degraded-carrier | carrier | enslaved
+ * address_state degraded | off | no-carrier | dormant | degraded-carrier | degraded | enslaved
+ * routable | off | no-carrier | dormant | degraded-carrier | routable | routable
+ */
- if (IN_SET(operstate, LINK_OPERSTATE_DEGRADED, LINK_OPERSTATE_CARRIER) &&
- link_is_enslaved(link))
+ if (carrier_state < LINK_CARRIER_STATE_CARRIER || address_state == LINK_ADDRESS_STATE_OFF)
+ operstate = (LinkOperationalState) carrier_state;
+ else if (address_state == LINK_ADDRESS_STATE_ROUTABLE)
+ operstate = LINK_OPERSTATE_ROUTABLE;
+ else if (carrier_state == LINK_CARRIER_STATE_CARRIER)
+ operstate = LINK_OPERSTATE_DEGRADED;
+ else
operstate = LINK_OPERSTATE_ENSLAVED;
- if (operstate >= LINK_OPERSTATE_CARRIER) {
- Link *slave;
-
- SET_FOREACH(slave, link->slaves, i) {
- link_update_operstate(slave, false);
+ if (link->carrier_state != carrier_state) {
+ link->carrier_state = carrier_state;
+ changed = true;
+ if (strv_extend(&p, "CarrierState") < 0)
+ log_oom();
+ }
- if (slave->operstate < LINK_OPERSTATE_CARRIER)
- operstate = LINK_OPERSTATE_DEGRADED_CARRIER;
- }
+ if (link->address_state != address_state) {
+ link->address_state = address_state;
+ changed = true;
+ if (strv_extend(&p, "AddressState") < 0)
+ log_oom();
}
if (link->operstate != operstate) {
link->operstate = operstate;
- link_send_changed(link, "OperationalState", NULL);
- link_dirty(link);
+ changed = true;
+ if (strv_extend(&p, "OperationalState") < 0)
+ log_oom();
}
+ if (p)
+ link_send_changed_strv(link, p);
+ if (changed)
+ link_dirty(link);
+
if (also_update_master && link->network) {
link_update_master_operstate(link, link->network->bond);
link_update_master_operstate(link, link->network->bridge);
.n_ref = 1,
.manager = manager,
.state = LINK_STATE_PENDING,
- .rtnl_extended_attrs = true,
.ifindex = ifindex,
.iftype = iftype,
+ .sysctl_ipv6_enabled = -1,
+
+ .n_dns = (unsigned) -1,
+ .dns_default_route = -1,
+ .llmnr = _RESOLVE_SUPPORT_INVALID,
+ .mdns = _RESOLVE_SUPPORT_INVALID,
+ .dnssec_mode = _DNSSEC_MODE_INVALID,
+ .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
};
link->ifname = strdup(ifname);
return 0;
}
+void link_ntp_settings_clear(Link *link) {
+ link->ntp = strv_free(link->ntp);
+}
+
+void link_dns_settings_clear(Link *link) {
+ link->dns = mfree(link->dns);
+ link->n_dns = (unsigned) -1;
+
+ link->search_domains = ordered_set_free_free(link->search_domains);
+ link->route_domains = ordered_set_free_free(link->route_domains);
+
+ link->dns_default_route = -1;
+ link->llmnr = _RESOLVE_SUPPORT_INVALID;
+ link->mdns = _RESOLVE_SUPPORT_INVALID;
+ link->dnssec_mode = _DNSSEC_MODE_INVALID;
+ link->dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID;
+
+ link->dnssec_negative_trust_anchors = set_free_free(link->dnssec_negative_trust_anchors);
+}
+
static Link *link_free(Link *link) {
Address *address;
assert(link);
+ link_ntp_settings_clear(link);
+ link_dns_settings_clear(link);
+
link->routes = set_free_with_destructor(link->routes, route_free);
link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free);
+ link->neighbors = set_free_with_destructor(link->neighbors, neighbor_free);
+ link->neighbors_foreign = set_free_with_destructor(link->neighbors_foreign, neighbor_free);
+
link->addresses = set_free_with_destructor(link->addresses, address_free);
link->addresses_foreign = set_free_with_destructor(link->addresses_foreign, address_free);
sd_dhcp_server_unref(link->dhcp_server);
sd_dhcp_client_unref(link->dhcp_client);
sd_dhcp_lease_unref(link->dhcp_lease);
+ set_free(link->dhcp_routes);
link_lldp_emit_stop(link);
assert(link->manager);
assert(link->manager->event);
+ dhcp4_release_old_lease(link);
+
if (link->dhcp_client && (!may_keep_dhcp || !link->network ||
!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP))) {
k = sd_dhcp_client_stop(link->dhcp_client);
link_dirty(link);
}
-static Address* link_find_dhcp_server_address(Link *link) {
- Address *address;
-
- assert(link);
- assert(link->network);
-
- /* The first statically configured address if there is any */
- LIST_FOREACH(addresses, address, link->network->static_addresses) {
-
- if (address->family != AF_INET)
- continue;
-
- if (in_addr_is_null(address->family, &address->in_addr))
- continue;
-
- return address;
- }
-
- /* If that didn't work, find a suitable address we got from the pool */
- LIST_FOREACH(addresses, address, link->pool_addresses) {
- if (address->family != AF_INET)
- continue;
-
- return address;
- }
-
- return NULL;
-}
-
static int link_join_netdevs_after_configured(Link *link) {
NetDev *netdev;
Iterator i;
assert(link);
assert(link->network);
- link_set_state(link, LINK_STATE_CONFIGURING);
link->routing_policy_rules_configured = false;
LIST_FOREACH(rules, rule, link->network->rules) {
- r = routing_policy_rule_get(link->manager, rule->family, &rule->from, rule->from_prefixlen, &rule->to,
- rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif,
- rule->protocol, &rule->sport, &rule->dport, &rrule);
- if (r == 0) {
- (void) routing_policy_rule_make_local(link->manager, rrule);
+ r = routing_policy_rule_get(link->manager, rule, &rrule);
+ if (r >= 0) {
+ if (r == 0)
+ (void) routing_policy_rule_make_local(link->manager, rrule);
continue;
}
r = routing_policy_rule_configure(rule, link, NULL);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not set routing policy rules: %m");
- link_enter_failed(link);
- return r;
- }
-
- link->routing_policy_rule_messages++;
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not set routing policy rules: %m");
+ if (r > 0)
+ link->routing_policy_rule_messages++;
}
routing_policy_rule_purge(link->manager, link);
if (link->routing_policy_rule_messages == 0) {
link->routing_policy_rules_configured = true;
link_check_ready(link);
- } else
+ } else {
log_link_debug(link, "Setting routing policy rules");
+ link_set_state(link, LINK_STATE_CONFIGURING);
+ }
return 0;
}
return 1;
r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST)
+ if (r < 0 && r != -EEXIST) {
log_link_warning_errno(link, r, "Could not set route: %m");
+ link_enter_failed(link);
+ return 1;
+ }
if (link->route_messages == 0) {
log_link_debug(link, "Routes set");
assert(link->address_messages == 0);
assert(link->state != _LINK_STATE_INVALID);
- link_set_state(link, LINK_STATE_CONFIGURING);
link->static_routes_configured = false;
+ if (!link_has_carrier(link) && !link->network->configure_without_carrier)
+ /* During configuring addresses, the link lost its carrier. As networkd is dropping
+ * the addresses now, let's not configure the routes either. */
+ return 0;
+
r = link_request_set_routing_policy_rule(link);
if (r < 0)
return r;
continue;
r = route_configure(rt, link, route_handler);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not set routes: %m");
- link_enter_failed(link);
- return r;
- }
-
- link->route_messages++;
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not set routes: %m");
+ if (r > 0)
+ link->route_messages++;
}
if (link->route_messages == 0) {
link->static_routes_configured = true;
link_check_ready(link);
- } else
+ } else {
log_link_debug(link, "Setting routes");
+ link_set_state(link, LINK_STATE_CONFIGURING);
+ }
return 0;
}
void link_check_ready(Link *link) {
Address *a;
Iterator i;
+ int r;
assert(link);
if (!link->addresses_ready) {
link->addresses_ready = true;
- link_request_set_routes(link);
+ r = link_request_set_routes(link);
+ if (r < 0)
+ link_enter_failed(link);
return;
}
if (!link->routing_policy_rules_configured)
return;
- if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !(link->ipv4ll_address && link->ipv4ll_route))
- return;
+ if (link_has_carrier(link) || !link->network->configure_without_carrier) {
- if (link_ipv6ll_enabled(link) &&
- in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address))
- return;
+ if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !(link->ipv4ll_address && link->ipv4ll_route))
+ return;
- if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) &&
- !link->dhcp4_configured &&
- !link->dhcp6_configured &&
- !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address && link->ipv4ll_route))
- /* When DHCP is enabled, at least one protocol must provide an address, or
- * an IPv4ll fallback address must be configured. */
- return;
+ if (link_ipv6ll_enabled(link) &&
+ in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address))
+ return;
- if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured)
- return;
+ if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) &&
+ !link->dhcp4_configured &&
+ !link->dhcp6_configured &&
+ !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address && link->ipv4ll_route))
+ /* When DHCP is enabled, at least one protocol must provide an address, or
+ * an IPv4ll fallback address must be configured. */
+ return;
+
+ if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured)
+ return;
+ }
if (link->state != LINK_STATE_CONFIGURED)
link_enter_configured(link);
assert(link->network);
assert(link->state != _LINK_STATE_INVALID);
- link_set_state(link, LINK_STATE_CONFIGURING);
link->neighbors_configured = false;
LIST_FOREACH(neighbors, neighbor, link->network->neighbors) {
r = neighbor_configure(neighbor, link, NULL);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not set neighbor: %m");
- link_enter_failed(link);
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not set neighbor: %m");
}
if (link->neighbor_messages == 0) {
link->neighbors_configured = true;
link_check_ready(link);
- } else
+ } else {
log_link_debug(link, "Setting neighbors");
+ link_set_state(link, LINK_STATE_CONFIGURING);
+ }
return 0;
}
return 1;
r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST)
+ if (r < 0 && r != -EEXIST) {
log_link_warning_errno(link, r, "could not set address: %m");
- else if (r >= 0)
- manager_rtnl_process_address(rtnl, m, link->manager);
+ link_enter_failed(link);
+ return 1;
+ } else if (r >= 0)
+ (void) manager_rtnl_process_address(rtnl, m, link->manager);
if (link->address_messages == 0) {
log_link_debug(link, "Addresses set");
return 1;
}
-static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
- _cleanup_free_ struct in_addr *addresses = NULL;
- size_t n_addresses = 0, n_allocated = 0;
- unsigned i;
-
- log_debug("Copying DNS server information from %s", link->ifname);
-
- if (!link->network)
- return 0;
-
- for (i = 0; i < link->network->n_dns; i++) {
- struct in_addr ia;
-
- /* Only look for IPv4 addresses */
- if (link->network->dns[i].family != AF_INET)
- continue;
-
- ia = link->network->dns[i].address.in;
-
- /* Never propagate obviously borked data */
- if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia))
- continue;
-
- if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
- return log_oom();
-
- addresses[n_addresses++] = ia;
- }
-
- if (link->network->dhcp_use_dns && link->dhcp_lease) {
- const struct in_addr *da = NULL;
- int j, n;
-
- n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da);
- if (n > 0) {
-
- if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
- return log_oom();
-
- for (j = 0; j < n; j++)
- if (in4_addr_is_non_local(&da[j]))
- addresses[n_addresses++] = da[j];
- }
- }
-
- if (n_addresses <= 0)
- return 0;
-
- return sd_dhcp_server_set_dns(s, addresses, n_addresses);
-}
-
-static int link_push_uplink_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) {
- _cleanup_free_ struct in_addr *addresses = NULL;
- size_t n_addresses = 0, n_allocated = 0;
- char **a;
-
- if (!link->network)
- return 0;
-
- log_debug("Copying NTP server information from %s", link->ifname);
-
- STRV_FOREACH(a, link->network->ntp) {
- union in_addr_union ia;
-
- /* Only look for IPv4 addresses */
- if (in_addr_from_string(AF_INET, *a, &ia) <= 0)
- continue;
-
- /* Never propagate obviously borked data */
- if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
- continue;
-
- if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
- return log_oom();
-
- addresses[n_addresses++] = ia.in;
- }
-
- if (link->network->dhcp_use_ntp && link->dhcp_lease) {
- const struct in_addr *da = NULL;
- int j, n;
-
- n = sd_dhcp_lease_get_ntp(link->dhcp_lease, &da);
- if (n > 0) {
-
- if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
- return log_oom();
-
- for (j = 0; j < n; j++)
- if (in4_addr_is_non_local(&da[j]))
- addresses[n_addresses++] = da[j];
- }
- }
-
- if (n_addresses <= 0)
- return 0;
-
- return sd_dhcp_server_set_ntp(s, addresses, n_addresses);
-}
-
static int link_set_bridge_fdb(Link *link) {
FdbEntry *fdb_entry;
int r;
assert(link->network);
assert(link->state != _LINK_STATE_INVALID);
- link_set_state(link, LINK_STATE_CONFIGURING);
-
/* Reset all *_configured flags we are configuring. */
link->addresses_configured = false;
link->addresses_ready = false;
update = address_get(link, ad->family, &ad->in_addr, ad->prefixlen, NULL) > 0;
r = address_configure(ad, link, address_handler, update);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not set addresses: %m");
- link_enter_failed(link);
- return r;
- }
-
- link->address_messages++;
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not set addresses: %m");
+ if (r > 0)
+ link->address_messages++;
}
LIST_FOREACH(labels, label, link->network->address_labels) {
r = address_label_configure(label, link, NULL, false);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not set address label: %m");
- link_enter_failed(link);
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not set address label: %m");
link->address_label_messages++;
}
/* now that we can figure out a default address for the dhcp server,
start it */
if (link_dhcp4_server_enabled(link) && (link->flags & IFF_UP)) {
- Address *address;
- Link *uplink = NULL;
- bool acquired_uplink = false;
-
- address = link_find_dhcp_server_address(link);
- if (!address) {
- log_link_warning(link, "Failed to find suitable address for DHCPv4 server instance.");
- link_enter_failed(link);
- return 0;
- }
-
- /* use the server address' subnet as the pool */
- r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen,
- link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size);
+ r = dhcp4_server_configure(link);
if (r < 0)
return r;
-
- /* TODO:
- r = sd_dhcp_server_set_router(link->dhcp_server,
- &main_address->in_addr.in);
- if (r < 0)
- return r;
- */
-
- if (link->network->dhcp_server_max_lease_time_usec > 0) {
- r = sd_dhcp_server_set_max_lease_time(
- link->dhcp_server,
- DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC));
- if (r < 0)
- return r;
- }
-
- if (link->network->dhcp_server_default_lease_time_usec > 0) {
- r = sd_dhcp_server_set_default_lease_time(
- link->dhcp_server,
- DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC));
- if (r < 0)
- return r;
- }
-
- if (link->network->dhcp_server_emit_dns) {
-
- if (link->network->n_dhcp_server_dns > 0)
- r = sd_dhcp_server_set_dns(link->dhcp_server, link->network->dhcp_server_dns, link->network->n_dhcp_server_dns);
- else {
- uplink = manager_find_uplink(link->manager, link);
- acquired_uplink = true;
-
- if (!uplink) {
- log_link_debug(link, "Not emitting DNS server information on link, couldn't find suitable uplink.");
- r = 0;
- } else
- r = link_push_uplink_dns_to_dhcp_server(uplink, link->dhcp_server);
- }
- if (r < 0)
- log_link_warning_errno(link, r, "Failed to set DNS server for DHCP server, ignoring: %m");
- }
-
- if (link->network->dhcp_server_emit_ntp) {
-
- if (link->network->n_dhcp_server_ntp > 0)
- r = sd_dhcp_server_set_ntp(link->dhcp_server, link->network->dhcp_server_ntp, link->network->n_dhcp_server_ntp);
- else {
- if (!acquired_uplink)
- uplink = manager_find_uplink(link->manager, link);
-
- if (!uplink) {
- log_link_debug(link, "Not emitting NTP server information on link, couldn't find suitable uplink.");
- r = 0;
- } else
- r = link_push_uplink_ntp_to_dhcp_server(uplink, link->dhcp_server);
-
- }
- if (r < 0)
- log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m");
- }
-
- r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to set router emission for DHCP server: %m");
-
- if (link->network->dhcp_server_emit_timezone) {
- _cleanup_free_ char *buffer = NULL;
- const char *tz = NULL;
-
- if (link->network->dhcp_server_timezone)
- tz = link->network->dhcp_server_timezone;
- else {
- r = get_timezone(&buffer);
- if (r < 0)
- log_warning_errno(r, "Failed to determine timezone: %m");
- else
- tz = buffer;
- }
-
- if (tz) {
- r = sd_dhcp_server_set_timezone(link->dhcp_server, tz);
- if (r < 0)
- return r;
- }
- }
- if (!sd_dhcp_server_is_running(link->dhcp_server)) {
- r = sd_dhcp_server_start(link->dhcp_server);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not start DHCPv4 server instance: %m");
-
- link_enter_failed(link);
-
- return 0;
- }
- }
-
log_link_debug(link, "Offering DHCPv4 leases");
}
if (link->address_messages == 0) {
link->addresses_configured = true;
link_check_ready(link);
- } else
+ } else {
log_link_debug(link, "Setting addresses");
+ link_set_state(link, LINK_STATE_CONFIGURING);
+ }
return 0;
}
return 1;
r = sd_netlink_message_get_errno(m);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not set MTU: %m");
- return 1;
- }
-
- log_link_debug(link, "Setting MTU done.");
+ if (r < 0)
+ log_link_warning_errno(link, r, "Could not set MTU, ignoring: %m");
+ else
+ log_link_debug(link, "Setting MTU done.");
- if (link->state == LINK_STATE_INITIALIZED)
- (void) link_configure_after_setting_mtu(link);
+ if (link->state == LINK_STATE_INITIALIZED) {
+ r = link_configure_after_setting_mtu(link);
+ if (r < 0)
+ link_enter_failed(link);
+ }
return 1;
}
r = sd_netlink_message_get_errno(m);
if (r < 0)
- log_link_warning_errno(link, r, "Could not set link flags: %m");
+ log_link_warning_errno(link, r, "Could not set link flags, ignoring: %m");
return 1;
}
r = sd_netlink_message_get_errno(m);
if (r < 0)
- log_link_warning_errno(link, r, "Could not set address genmode for interface: %m");
+ log_link_warning_errno(link, r, "Could not set address genmode for interface, ignoring: %m");
return 1;
}
r = set_put(master->slaves, link);
if (r < 0)
return r;
+ if (r == 0)
+ return 0;
link_ref(link);
return 0;
if (!link_has_carrier(link) && !link->network->configure_without_carrier)
return 0;
+ link_set_state(link, LINK_STATE_CONFIGURING);
return link_request_set_addresses(link);
}
log_link_error_errno(link, r, "Could not join netdev: %m");
link_enter_failed(link);
return 1;
- } else
- log_link_debug(link, "Joined netdev");
+ }
+
+ log_link_debug(link, "Joined netdev");
if (link->enslaving == 0) {
- link_joined(link);
+ r = link_joined(link);
+ if (r < 0)
+ link_enter_failed(link);
}
return 1;
return false;
}
+static bool link_is_neighbor_configured(Link *link, Neighbor *neighbor) {
+ Neighbor *net_neighbor;
+
+ assert(link);
+ assert(neighbor);
+
+ if (!link->network)
+ return false;
+
+ LIST_FOREACH(neighbors, net_neighbor, link->network->neighbors)
+ if (neighbor_equal(net_neighbor, neighbor))
+ return true;
+
+ return false;
+}
+
static bool link_is_static_route_configured(Link *link, Route *route) {
Route *net_route;
static int link_drop_foreign_config(Link *link) {
Address *address;
+ Neighbor *neighbor;
Route *route;
Iterator i;
int r;
}
}
+ SET_FOREACH(neighbor, link->neighbors_foreign, i) {
+ if (link_is_neighbor_configured(link, neighbor)) {
+ r = neighbor_add(link, neighbor->family, &neighbor->in_addr, &neighbor->lladdr, neighbor->lladdr_size, NULL);
+ if (r < 0)
+ return r;
+ } else {
+ r = neighbor_remove(neighbor, link, NULL);
+ if (r < 0)
+ return r;
+ }
+ }
+
SET_FOREACH(route, link->routes_foreign, i) {
/* do not touch routes managed by the kernel */
if (route->protocol == RTPROT_KERNEL)
continue;
+ /* do not touch multicast route added by kernel */
+ /* FIXME: Why the kernel adds this route with protocol RTPROT_BOOT??? We need to investigate that.
+ * https://tools.ietf.org/html/rfc4862#section-5.4 may explain why. */
+ if (route->protocol == RTPROT_BOOT &&
+ route->family == AF_INET6 &&
+ route->dst_prefixlen == 8 &&
+ in_addr_equal(AF_INET6, &route->dst, &(union in_addr_union) { .in6 = {{{ 0xff,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }}} }))
+ continue;
+
if (route->protocol == RTPROT_STATIC &&
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
continue;
continue;
if (link_is_static_route_configured(link, route)) {
- r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, NULL);
+ r = route_add(link, route->family, &route->dst, route->dst_prefixlen, &route->gw, route->tos, route->priority, route->table, NULL);
if (r < 0)
return r;
} else {
static int link_drop_config(Link *link) {
Address *address, *pool_address;
+ Neighbor *neighbor;
Route *route;
Iterator i;
int r;
}
}
+ SET_FOREACH(neighbor, link->neighbors, i) {
+ r = neighbor_remove(neighbor, link, NULL);
+ if (r < 0)
+ return r;
+ }
+
SET_FOREACH(route, link->routes, i) {
/* do not touch routes managed by the kernel */
if (route->protocol == RTPROT_KERNEL)
assert(link->network);
assert(link->state == LINK_STATE_INITIALIZED);
- if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
+ if (link->iftype == ARPHRD_CAN)
return link_configure_can(link);
/* Drop foreign config, but ignore loopback or critical devices.
configure:
while ((link = set_steal_first(manager->links_requesting_uuid))) {
+ link_unref(link);
+
r = link_configure(link);
if (r < 0)
- log_link_error_errno(link, r, "Failed to configure link: %m");
+ link_enter_failed(link);
}
manager->links_requesting_uuid = set_free(manager->links_requesting_uuid);
r = set_put(m->duids_requesting_uuid, duid);
if (r < 0)
return log_oom();
+
+ link_ref(link);
}
return 0;
}
static int link_initialized_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- (void) link_initialized_and_synced(link);
+ int r;
+
+ r = link_initialized_and_synced(link);
+ if (r < 0)
+ link_enter_failed(link);
return 1;
}
continue;
}
- r = route_add(link, family, &route_dst, prefixlen, tos, priority, table, &route);
+ r = route_add(link, family, &route_dst, prefixlen, NULL, tos, priority, table, &route);
if (r < 0)
return log_link_error_errno(link, r, "Failed to add route: %m");
return r;
}
+ link_set_state(link, LINK_STATE_CONFIGURING);
r = link_request_set_addresses(link);
if (r < 0)
return r;
fputc('\n', f);
}
+static void link_save_dns(FILE *f, struct in_addr_data *dns, unsigned n_dns, bool *space) {
+ unsigned j;
+ int r;
+
+ for (j = 0; j < n_dns; j++) {
+ _cleanup_free_ char *b = NULL;
+
+ r = in_addr_to_string(dns[j].family, &dns[j].address, &b);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to format address, ignoring: %m");
+ continue;
+ }
+
+ if (*space)
+ fputc(' ', f);
+ fputs(b, f);
+ *space = true;
+ }
+}
+
int link_save(Link *link) {
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
- const char *admin_state, *oper_state;
+ const char *admin_state, *oper_state, *carrier_state, *address_state;
Address *a;
Route *route;
Iterator i;
oper_state = link_operstate_to_string(link->operstate);
assert(oper_state);
+ carrier_state = link_carrier_state_to_string(link->carrier_state);
+ assert(carrier_state);
+
+ address_state = link_address_state_to_string(link->address_state);
+ assert(address_state);
+
r = fopen_temporary(link->state_file, &f, &temp_path);
if (r < 0)
goto fail;
fprintf(f,
"# This is private data. Do not parse.\n"
"ADMIN_STATE=%s\n"
- "OPER_STATE=%s\n",
- admin_state, oper_state);
+ "OPER_STATE=%s\n"
+ "CARRIER_STATE=%s\n"
+ "ADDRESS_STATE=%s\n",
+ admin_state, oper_state, carrier_state, address_state);
if (link->network) {
char **dhcp6_domains = NULL, **dhcp_domains = NULL;
const char *dhcp_domainname = NULL, *p;
sd_dhcp6_lease *dhcp6_lease = NULL;
- unsigned j;
bool space;
fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
fputs("DNS=", f);
space = false;
- for (j = 0; j < link->network->n_dns; j++) {
- _cleanup_free_ char *b = NULL;
-
- r = in_addr_to_string(link->network->dns[j].family,
- &link->network->dns[j].address, &b);
- if (r < 0) {
- log_debug_errno(r, "Failed to format address, ignoring: %m");
- continue;
- }
-
- if (space)
- fputc(' ', f);
- fputs(b, f);
- space = true;
- }
+ if (link->n_dns != (unsigned) -1)
+ link_save_dns(f, link->dns, link->n_dns, &space);
+ else
+ link_save_dns(f, link->network->dns, link->network->n_dns, &space);
if (link->network->dhcp_use_dns &&
link->dhcp_lease) {
space = true;
}
- if (link->network->dhcp_use_dns && dhcp6_lease) {
+ if (link->network->dhcp6_use_dns && dhcp6_lease) {
struct in6_addr *in6_addrs;
r = sd_dhcp6_lease_get_dns(dhcp6_lease, &in6_addrs);
fputs("NTP=", f);
space = false;
- fputstrv(f, link->network->ntp, NULL, &space);
+ fputstrv(f, link->ntp ?: link->network->ntp, NULL, &space);
if (link->network->dhcp_use_ntp &&
link->dhcp_lease) {
space = true;
}
- if (link->network->dhcp_use_ntp && dhcp6_lease) {
+ if (link->network->dhcp6_use_ntp && dhcp6_lease) {
struct in6_addr *in6_addrs;
char **hosts;
fputs("DOMAINS=", f);
space = false;
- ORDERED_SET_FOREACH(p, link->network->search_domains, i)
+ ORDERED_SET_FOREACH(p, link->search_domains ?: link->network->search_domains, i)
fputs_with_space(f, p, NULL, &space);
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) {
- NDiscDNSSL *dd;
-
if (dhcp_domainname)
fputs_with_space(f, dhcp_domainname, NULL, &space);
if (dhcp_domains)
fputstrv(f, dhcp_domains, NULL, &space);
if (dhcp6_domains)
fputstrv(f, dhcp6_domains, NULL, &space);
+ }
+
+ if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_YES) {
+ NDiscDNSSL *dd;
SET_FOREACH(dd, link->ndisc_dnssl, i)
fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
fputs("ROUTE_DOMAINS=", f);
space = false;
- ORDERED_SET_FOREACH(p, link->network->route_domains, i)
+ ORDERED_SET_FOREACH(p, link->route_domains ?: link->network->route_domains, i)
fputs_with_space(f, p, NULL, &space);
if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) {
- NDiscDNSSL *dd;
-
if (dhcp_domainname)
fputs_with_space(f, dhcp_domainname, NULL, &space);
if (dhcp_domains)
fputstrv(f, dhcp_domains, NULL, &space);
if (dhcp6_domains)
fputstrv(f, dhcp6_domains, NULL, &space);
+ }
+
+ if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_ROUTE) {
+ NDiscDNSSL *dd;
SET_FOREACH(dd, link->ndisc_dnssl, i)
fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
fputc('\n', f);
fprintf(f, "LLMNR=%s\n",
- resolve_support_to_string(link->network->llmnr));
+ resolve_support_to_string(link->llmnr >= 0 ? link->llmnr : link->network->llmnr));
fprintf(f, "MDNS=%s\n",
- resolve_support_to_string(link->network->mdns));
- if (link->network->dns_default_route >= 0)
+ resolve_support_to_string(link->mdns >= 0 ? link->mdns : link->network->mdns));
+ if (link->dns_default_route >= 0)
+ fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(link->dns_default_route));
+ else if (link->network->dns_default_route >= 0)
fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(link->network->dns_default_route));
- if (link->network->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
+ if (link->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
+ fprintf(f, "DNS_OVER_TLS=%s\n",
+ dns_over_tls_mode_to_string(link->dns_over_tls_mode));
+ else if (link->network->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
fprintf(f, "DNS_OVER_TLS=%s\n",
dns_over_tls_mode_to_string(link->network->dns_over_tls_mode));
- if (link->network->dnssec_mode != _DNSSEC_MODE_INVALID)
+ if (link->dnssec_mode != _DNSSEC_MODE_INVALID)
+ fprintf(f, "DNSSEC=%s\n",
+ dnssec_mode_to_string(link->dnssec_mode));
+ else if (link->network->dnssec_mode != _DNSSEC_MODE_INVALID)
fprintf(f, "DNSSEC=%s\n",
dnssec_mode_to_string(link->network->dnssec_mode));
- if (!set_isempty(link->network->dnssec_negative_trust_anchors)) {
+ if (!set_isempty(link->dnssec_negative_trust_anchors)) {
+ const char *n;
+
+ fputs("DNSSEC_NTA=", f);
+ space = false;
+ SET_FOREACH(n, link->dnssec_negative_trust_anchors, i)
+ fputs_with_space(f, n, NULL, &space);
+ fputc('\n', f);
+ } else if (!set_isempty(link->network->dnssec_negative_trust_anchors)) {
const char *n;
fputs("DNSSEC_NTA=", f);