resolved-link.c revision 6f4dedb250f2d607eceefaa491f338becbeee7c0
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier/***
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier This file is part of systemd.
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier Copyright 2014 Lennart Poettering
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier systemd is free software; you can redistribute it and/or modify it
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier under the terms of the GNU Lesser General Public License as published by
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier the Free Software Foundation; either version 2.1 of the License, or
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier (at your option) any later version.
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier systemd is distributed in the hope that it will be useful, but
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier WITHOUT ANY WARRANTY; without even the implied warranty of
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier Lesser General Public License for more details.
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier You should have received a copy of the GNU Lesser General Public License
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier along with systemd; If not, see <http://www.gnu.org/licenses/>.
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier***/
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier#include <net/if.h>
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier#include "sd-network.h"
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier#include "strv.h"
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier#include "resolved-link.h"
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalierint link_new(Manager *m, Link **ret, int ifindex) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier _cleanup_(link_freep) Link *l = NULL;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier int r;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier assert(m);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier assert(ifindex > 0);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier r = hashmap_ensure_allocated(&m->links, NULL, NULL);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (r < 0)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier return r;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier l = new0(Link, 1);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (!l)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier return -ENOMEM;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier l->ifindex = ifindex;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (r < 0)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier return r;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier l->manager = m;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering if (ret)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier *ret = l;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier l = NULL;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier return 0;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier}
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart PoetteringLink *link_free(Link *l) {
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering if (!l)
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering return NULL;
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering while (l->addresses)
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering link_address_free(l->addresses);
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering if (l->manager)
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering dns_scope_free(l->unicast_scope);
7430ec6ac08f2c0416d9f806964c46b30f3862b2Lennart Poettering dns_scope_free(l->llmnr_ipv4_scope);
7430ec6ac08f2c0416d9f806964c46b30f3862b2Lennart Poettering dns_scope_free(l->llmnr_ipv6_scope);
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering while (l->dns_servers)
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering dns_server_free(l->dns_servers);
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering free(l);
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering return NULL;
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering}
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poetteringstatic void link_allocate_scopes(Link *l) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier int r;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier assert(l);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (l->dns_servers) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (!l->unicast_scope) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (r < 0)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier log_warning("Failed to allocate DNS scope: %s", strerror(-r));
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier }
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier } else
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier l->unicast_scope = dns_scope_free(l->unicast_scope);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (link_relevant(l, AF_INET) && l->manager->use_llmnr) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (!l->llmnr_ipv4_scope) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (r < 0)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier log_warning("Failed to allocate LLMNR IPv4 scope: %s", strerror(-r));
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier }
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier } else
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (link_relevant(l, AF_INET6) && l->manager->use_llmnr) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (!l->llmnr_ipv6_scope) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (r < 0)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier log_warning("Failed to allocate LLMNR IPv6 scope: %s", strerror(-r));
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier }
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier } else
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier}
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalierint link_update_rtnl(Link *l, sd_rtnl_message *m) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier const char *n = NULL;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier int r;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier assert(l);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier assert(m);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier r = sd_rtnl_message_link_get_flags(m, &l->flags);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (r < 0)
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier return r;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier sd_rtnl_message_read_u32(m, IFLA_MTU, &l->mtu);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier if (sd_rtnl_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier strncpy(l->name, n, sizeof(l->name));
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier char_array_0(l->name);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier }
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier link_allocate_scopes(l);
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier return 0;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier}
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalierstatic int link_update_dns_servers(Link *l) {
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier _cleanup_strv_free_ char **nameservers = NULL;
cda134ab1eac84f874aacf8e885a07112a7fd5ceLennart Poettering char **nameserver;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier DnsServer *s, *nx;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier int r;
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier
641d1f99b8c4c5427a1fedcb4740586a130ac6cfRonny Chevalier assert(l);
LIST_FOREACH(servers, s, l->dns_servers)
s->marked = true;
r = sd_network_get_dns(l->ifindex, &nameservers);
if (r < 0)
goto clear;
STRV_FOREACH(nameserver, nameservers) {
union in_addr_union a;
int family;
r = in_addr_from_string_auto(*nameserver, &family, &a);
if (r < 0)
goto clear;
s = link_find_dns_server(l, family, &a);
if (s)
s->marked = false;
else {
r = dns_server_new(l->manager, NULL, l, family, &a);
if (r < 0)
goto clear;
}
}
LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
if (s->marked)
dns_server_free(s);
return 0;
clear:
while (l->dns_servers)
dns_server_free(l->dns_servers);
return r;
}
int link_update_monitor(Link *l) {
assert(l);
link_update_dns_servers(l);
link_allocate_scopes(l);
return 0;
}
bool link_relevant(Link *l, int family) {
_cleanup_free_ char *state = NULL;
LinkAddress *a;
assert(l);
/* A link is relevant if it isn't a loopback device and has at
* least one relevant IP address */
if (l->flags & IFF_LOOPBACK)
return false;
sd_network_get_link_operational_state(l->ifindex, &state);
if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
return false;
LIST_FOREACH(addresses, a, l->addresses)
if (a->family == family && link_address_relevant(a))
return true;
return false;
}
LinkAddress *link_find_address(Link *l, int family, union in_addr_union *in_addr) {
LinkAddress *a;
assert(l);
LIST_FOREACH(addresses, a, l->addresses)
if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
return a;
return NULL;
}
DnsServer* link_find_dns_server(Link *l, int family, union in_addr_union *in_addr) {
DnsServer *s;
assert(l);
LIST_FOREACH(servers, s, l->dns_servers)
if (s->family == family && in_addr_equal(family, &s->address, in_addr))
return s;
return NULL;
}
DnsServer *link_get_dns_server(Link *l) {
assert(l);
if (!l->current_dns_server)
l->current_dns_server = l->dns_servers;
return l->current_dns_server;
}
void link_next_dns_server(Link *l) {
assert(l);
/* Switch to the next DNS server */
if (!l->current_dns_server) {
l->current_dns_server = l->dns_servers;
if (l->current_dns_server)
return;
}
if (!l->current_dns_server)
return;
if (l->current_dns_server->servers_next) {
l->current_dns_server = l->current_dns_server->servers_next;
return;
}
l->current_dns_server = l->dns_servers;
}
int link_address_new(Link *l, LinkAddress **ret, int family, union in_addr_union *in_addr) {
LinkAddress *a;
assert(l);
assert(in_addr);
a = new0(LinkAddress, 1);
if (!a)
return -ENOMEM;
a->family = family;
a->in_addr = *in_addr;
a->link = l;
LIST_PREPEND(addresses, l->addresses, a);
if (ret)
*ret = a;
return 0;
}
LinkAddress *link_address_free(LinkAddress *a) {
if (!a)
return NULL;
if (a->link)
LIST_REMOVE(addresses, a->link->addresses, a);
free(a);
return NULL;
}
int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
int r;
assert(a);
assert(m);
r = sd_rtnl_message_addr_get_flags(m, &a->flags);
if (r < 0)
return r;
sd_rtnl_message_addr_get_scope(m, &a->scope);
link_allocate_scopes(a->link);
return 0;
}
bool link_address_relevant(LinkAddress *a) {
assert(a);
if (a->flags & IFA_F_DEPRECATED)
return false;
if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
return false;
return true;
}