#include <netinet/in.h>
#include <linux/if.h>
+#include <linux/if_arp.h>
#include <unistd.h>
#include "alloc-util.h"
if (link->network->bond)
return false;
- if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
+ if (link->iftype == ARPHRD_CAN)
return false;
if (link_sysctl_ipv6_enabled(link) == 0)
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 (link->iftype == ARPHRD_CAN)
+ return false;
+
if (STRPTR_IN_SET(link->kind,
"vrf", "wireguard", "ipip", "gre", "ip6gre","ip6tnl", "sit", "vti",
- "vti6", "can", "vcan", "vxcan", "nlmon", "xfrm"))
+ "vti6", "nlmon", "xfrm"))
return false;
/* L3 or L3S mode do not support ARP. */
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)
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 (link_sysctl_ipv6_enabled(link) == 0)
.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);
r = set_put(master->slaves, link);
if (r < 0)
return r;
+ if (r == 0)
+ return 0;
link_ref(link);
return 0;
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)
link_enter_failed(link);
r = set_put(m->duids_requesting_uuid, duid);
if (r < 0)
return log_oom();
+
+ link_ref(link);
}
return 0;
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");
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;
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) {
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) {
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) {
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) {
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);