1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include "sd-network.h"
27 #include "resolved-link.h"
29 int link_new(Manager *m, Link **ret, int ifindex) {
30 _cleanup_(link_freep) Link *l = NULL;
36 r = hashmap_ensure_allocated(&m->links, NULL, NULL);
46 r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
59 Link *link_free(Link *l) {
65 link_address_free(l->addresses);
68 hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
70 dns_scope_free(l->unicast_scope);
71 dns_scope_free(l->llmnr_ipv4_scope);
72 dns_scope_free(l->llmnr_ipv6_scope);
74 while (l->dns_servers)
75 dns_server_free(l->dns_servers);
81 static void link_allocate_scopes(Link *l) {
87 if (!l->unicast_scope) {
88 r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
90 log_warning("Failed to allocate DNS scope: %s", strerror(-r));
93 l->unicast_scope = dns_scope_free(l->unicast_scope);
95 if (link_relevant(l, AF_INET) && l->manager->use_llmnr) {
96 if (!l->llmnr_ipv4_scope) {
97 r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
99 log_warning("Failed to allocate LLMNR IPv4 scope: %s", strerror(-r));
102 l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
104 if (link_relevant(l, AF_INET6) && l->manager->use_llmnr) {
105 if (!l->llmnr_ipv6_scope) {
106 r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
108 log_warning("Failed to allocate LLMNR IPv6 scope: %s", strerror(-r));
111 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
114 void link_add_rrs(Link *l, bool force_remove) {
117 LIST_FOREACH(addresses, a, l->addresses)
118 link_address_add_rrs(a, force_remove);
121 int link_update_rtnl(Link *l, sd_rtnl_message *m) {
122 const char *n = NULL;
128 r = sd_rtnl_message_link_get_flags(m, &l->flags);
132 sd_rtnl_message_read_u32(m, IFLA_MTU, &l->mtu);
134 if (sd_rtnl_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
135 strncpy(l->name, n, sizeof(l->name));
136 char_array_0(l->name);
139 link_allocate_scopes(l);
140 link_add_rrs(l, false);
145 static int link_update_dns_servers(Link *l) {
146 _cleanup_strv_free_ char **nameservers = NULL;
153 LIST_FOREACH(servers, s, l->dns_servers)
156 r = sd_network_get_dns(l->ifindex, &nameservers);
160 STRV_FOREACH(nameserver, nameservers) {
161 union in_addr_union a;
164 r = in_addr_from_string_auto(*nameserver, &family, &a);
168 s = link_find_dns_server(l, family, &a);
172 r = dns_server_new(l->manager, NULL, l, family, &a);
178 LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
185 while (l->dns_servers)
186 dns_server_free(l->dns_servers);
191 int link_update_monitor(Link *l) {
194 link_update_dns_servers(l);
195 link_allocate_scopes(l);
196 link_add_rrs(l, false);
201 bool link_relevant(Link *l, int family) {
202 _cleanup_free_ char *state = NULL;
207 /* A link is relevant if it isn't a loopback or pointopoint
208 * device, has a link beat, can do multicast and has at least
209 * one relevant IP address */
211 if (l->flags & (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_DORMANT))
214 if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST))
217 sd_network_get_link_operational_state(l->ifindex, &state);
218 if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
221 LIST_FOREACH(addresses, a, l->addresses)
222 if (a->family == family && link_address_relevant(a))
228 LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
233 LIST_FOREACH(addresses, a, l->addresses)
234 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
240 DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
245 LIST_FOREACH(servers, s, l->dns_servers)
246 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
251 DnsServer *link_get_dns_server(Link *l) {
254 if (!l->current_dns_server)
255 l->current_dns_server = l->dns_servers;
257 return l->current_dns_server;
260 void link_next_dns_server(Link *l) {
263 /* Switch to the next DNS server */
265 if (!l->current_dns_server) {
266 l->current_dns_server = l->dns_servers;
267 if (l->current_dns_server)
271 if (!l->current_dns_server)
274 if (l->current_dns_server->servers_next) {
275 l->current_dns_server = l->current_dns_server->servers_next;
279 l->current_dns_server = l->dns_servers;
282 int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
288 a = new0(LinkAddress, 1);
293 a->in_addr = *in_addr;
296 LIST_PREPEND(addresses, l->addresses, a);
304 LinkAddress *link_address_free(LinkAddress *a) {
309 LIST_REMOVE(addresses, a->link->addresses, a);
311 if (a->llmnr_address_rr) {
312 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
313 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
314 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
315 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
318 if (a->llmnr_ptr_rr) {
319 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
320 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
321 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
322 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
326 dns_resource_record_unref(a->llmnr_address_rr);
327 dns_resource_record_unref(a->llmnr_ptr_rr);
333 void link_address_add_rrs(LinkAddress *a, bool force_remove) {
338 if (a->family == AF_INET) {
340 if (!force_remove && link_address_relevant(a) && a->link->llmnr_ipv4_scope) {
341 if (!a->link->manager->host_ipv4_key) {
342 a->link->manager->host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->hostname);
343 if (!a->link->manager->host_ipv4_key) {
349 if (!a->llmnr_address_rr) {
350 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv4_key);
351 if (!a->llmnr_address_rr) {
356 a->llmnr_address_rr->a.in_addr = a->in_addr.in;
357 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
360 if (!a->llmnr_ptr_rr) {
361 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
365 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
368 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
370 log_warning("Failed tp add A record to LLMNR zone: %s", strerror(-r));
372 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
374 log_warning("Failed tp add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
376 if (a->llmnr_address_rr) {
377 if (a->link->llmnr_ipv4_scope)
378 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
379 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
382 if (a->llmnr_ptr_rr) {
383 if (a->link->llmnr_ipv4_scope)
384 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
385 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
390 if (a->family == AF_INET6) {
392 if (!force_remove && link_address_relevant(a) && a->link->llmnr_ipv6_scope) {
393 if (!a->link->manager->host_ipv6_key) {
394 a->link->manager->host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->hostname);
395 if (!a->link->manager->host_ipv6_key) {
401 if (!a->llmnr_address_rr) {
402 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv6_key);
403 if (!a->llmnr_address_rr) {
408 a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
409 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
412 if (!a->llmnr_ptr_rr) {
413 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
417 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
420 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
422 log_warning("Failed to add AAAA record to LLMNR zone: %s", strerror(-r));
424 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
426 log_warning("Failed to add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
428 if (a->llmnr_address_rr) {
429 if (a->link->llmnr_ipv6_scope)
430 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
431 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
434 if (a->llmnr_ptr_rr) {
435 if (a->link->llmnr_ipv6_scope)
436 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
437 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
445 log_debug("Failed to update address RRs: %s", strerror(-r));
448 int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
453 r = sd_rtnl_message_addr_get_flags(m, &a->flags);
457 sd_rtnl_message_addr_get_scope(m, &a->scope);
459 link_allocate_scopes(a->link);
460 link_add_rrs(a->link, false);
465 bool link_address_relevant(LinkAddress *a) {
468 if (a->flags & IFA_F_DEPRECATED)
471 if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))