chiark / gitweb /
treewide: use log_*_errno whenever %m is in the format string
[elogind.git] / src / resolve / resolved-link.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <net/if.h>
23
24 #include "sd-network.h"
25 #include "strv.h"
26 #include "missing.h"
27 #include "resolved-link.h"
28
29 int link_new(Manager *m, Link **ret, int ifindex) {
30         _cleanup_(link_freep) Link *l = NULL;
31         int r;
32
33         assert(m);
34         assert(ifindex > 0);
35
36         r = hashmap_ensure_allocated(&m->links, NULL);
37         if (r < 0)
38                 return r;
39
40         l = new0(Link, 1);
41         if (!l)
42                 return -ENOMEM;
43
44         l->ifindex = ifindex;
45         l->llmnr_support = SUPPORT_YES;
46
47         r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
48         if (r < 0)
49                 return r;
50
51         l->manager = m;
52
53         if (ret)
54                 *ret = l;
55         l = NULL;
56
57         return 0;
58 }
59
60 Link *link_free(Link *l) {
61
62         if (!l)
63                 return NULL;
64
65         while (l->addresses)
66                 link_address_free(l->addresses);
67
68         if (l->manager)
69                 hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
70
71         dns_scope_free(l->unicast_scope);
72         dns_scope_free(l->llmnr_ipv4_scope);
73         dns_scope_free(l->llmnr_ipv6_scope);
74
75         while (l->dns_servers)
76                 dns_server_free(l->dns_servers);
77
78         free(l);
79         return NULL;
80 }
81
82 static void link_allocate_scopes(Link *l) {
83         int r;
84
85         assert(l);
86
87         if (l->dns_servers) {
88                 if (!l->unicast_scope) {
89                         r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
90                         if (r < 0)
91                                 log_warning_errno(r, "Failed to allocate DNS scope: %m");
92                 }
93         } else
94                 l->unicast_scope = dns_scope_free(l->unicast_scope);
95
96         if (link_relevant(l, AF_INET) &&
97             l->llmnr_support != SUPPORT_NO &&
98             l->manager->llmnr_support != SUPPORT_NO) {
99                 if (!l->llmnr_ipv4_scope) {
100                         r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
101                         if (r < 0)
102                                 log_warning_errno(r, "Failed to allocate LLMNR IPv4 scope: %m");
103                 }
104         } else
105                 l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
106
107         if (link_relevant(l, AF_INET6) &&
108             l->llmnr_support != SUPPORT_NO &&
109             l->manager->llmnr_support != SUPPORT_NO &&
110             socket_ipv6_is_supported()) {
111                 if (!l->llmnr_ipv6_scope) {
112                         r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
113                         if (r < 0)
114                                 log_warning_errno(r, "Failed to allocate LLMNR IPv6 scope: %m");
115                 }
116         } else
117                 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
118 }
119
120 void link_add_rrs(Link *l, bool force_remove) {
121         LinkAddress *a;
122
123         LIST_FOREACH(addresses, a, l->addresses)
124                 link_address_add_rrs(a, force_remove);
125 }
126
127 int link_update_rtnl(Link *l, sd_rtnl_message *m) {
128         const char *n = NULL;
129         int r;
130
131         assert(l);
132         assert(m);
133
134         r = sd_rtnl_message_link_get_flags(m, &l->flags);
135         if (r < 0)
136                 return r;
137
138         sd_rtnl_message_read_u32(m, IFLA_MTU, &l->mtu);
139
140         if (sd_rtnl_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
141                 strncpy(l->name, n, sizeof(l->name)-1);
142                 char_array_0(l->name);
143         }
144
145         link_allocate_scopes(l);
146         link_add_rrs(l, false);
147
148         return 0;
149 }
150
151 static int link_update_dns_servers(Link *l) {
152         _cleanup_strv_free_ char **nameservers = NULL;
153         char **nameserver;
154         DnsServer *s, *nx;
155         int r;
156
157         assert(l);
158
159         r = sd_network_link_get_dns(l->ifindex, &nameservers);
160         if (r < 0)
161                 goto clear;
162
163         LIST_FOREACH(servers, s, l->dns_servers)
164                 s->marked = true;
165
166         STRV_FOREACH(nameserver, nameservers) {
167                 union in_addr_union a;
168                 int family;
169
170                 r = in_addr_from_string_auto(*nameserver, &family, &a);
171                 if (r < 0)
172                         goto clear;
173
174                 s = link_find_dns_server(l, family, &a);
175                 if (s)
176                         s->marked = false;
177                 else {
178                         r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a);
179                         if (r < 0)
180                                 goto clear;
181                 }
182         }
183
184         LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
185                 if (s->marked)
186                         dns_server_free(s);
187
188         return 0;
189
190 clear:
191         while (l->dns_servers)
192                 dns_server_free(l->dns_servers);
193
194         return r;
195 }
196
197 static int link_update_llmnr_support(Link *l) {
198         _cleanup_free_ char *b = NULL;
199         int r;
200
201         assert(l);
202
203         r = sd_network_link_get_llmnr(l->ifindex, &b);
204         if (r < 0)
205                 goto clear;
206
207         r = parse_boolean(b);
208         if (r < 0) {
209                 if (streq(b, "resolve"))
210                         l->llmnr_support = SUPPORT_RESOLVE;
211                 else
212                         goto clear;
213
214         } else if (r > 0)
215                 l->llmnr_support = SUPPORT_YES;
216         else
217                 l->llmnr_support = SUPPORT_NO;
218
219         return 0;
220
221 clear:
222         l->llmnr_support = SUPPORT_YES;
223         return r;
224 }
225
226 static int link_update_domains(Link *l) {
227         int r;
228
229         if (!l->unicast_scope)
230                 return 0;
231
232         strv_free(l->unicast_scope->domains);
233         l->unicast_scope->domains = NULL;
234
235         r = sd_network_link_get_domains(l->ifindex,
236                                         &l->unicast_scope->domains);
237         if (r < 0)
238                 return r;
239
240         return 0;
241 }
242
243 int link_update_monitor(Link *l) {
244         assert(l);
245
246         link_update_dns_servers(l);
247         link_update_llmnr_support(l);
248         link_allocate_scopes(l);
249         link_update_domains(l);
250         link_add_rrs(l, false);
251
252         return 0;
253 }
254
255 bool link_relevant(Link *l, int family) {
256         _cleanup_free_ char *state = NULL;
257         LinkAddress *a;
258
259         assert(l);
260
261         /* A link is relevant if it isn't a loopback or pointopoint
262          * device, has a link beat, can do multicast and has at least
263          * one relevant IP address */
264
265         if (l->flags & (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_DORMANT))
266                 return false;
267
268         if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST))
269                 return false;
270
271         sd_network_link_get_operational_state(l->ifindex, &state);
272         if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
273                 return false;
274
275         LIST_FOREACH(addresses, a, l->addresses)
276                 if (a->family == family && link_address_relevant(a))
277                         return true;
278
279         return false;
280 }
281
282 LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
283         LinkAddress *a;
284
285         assert(l);
286
287         LIST_FOREACH(addresses, a, l->addresses)
288                 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
289                         return a;
290
291         return NULL;
292 }
293
294 DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
295         DnsServer *s;
296
297         assert(l);
298
299         LIST_FOREACH(servers, s, l->dns_servers)
300                 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
301                         return s;
302         return NULL;
303 }
304
305 DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
306         assert(l);
307
308         if (l->current_dns_server == s)
309                 return s;
310
311         if (s) {
312                 _cleanup_free_ char *ip = NULL;
313
314                 in_addr_to_string(s->family, &s->address, &ip);
315                 log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name);
316         }
317
318         l->current_dns_server = s;
319
320         if (l->unicast_scope)
321                 dns_cache_flush(&l->unicast_scope->cache);
322
323         return s;
324 }
325
326 DnsServer *link_get_dns_server(Link *l) {
327         assert(l);
328
329         if (!l->current_dns_server)
330                 link_set_dns_server(l, l->dns_servers);
331
332         return l->current_dns_server;
333 }
334
335 void link_next_dns_server(Link *l) {
336         assert(l);
337
338         if (!l->current_dns_server)
339                 return;
340
341         if (l->current_dns_server->servers_next) {
342                 link_set_dns_server(l, l->current_dns_server->servers_next);
343                 return;
344         }
345
346         link_set_dns_server(l, l->dns_servers);
347 }
348
349 int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
350         LinkAddress *a;
351
352         assert(l);
353         assert(in_addr);
354
355         a = new0(LinkAddress, 1);
356         if (!a)
357                 return -ENOMEM;
358
359         a->family = family;
360         a->in_addr = *in_addr;
361
362         a->link = l;
363         LIST_PREPEND(addresses, l->addresses, a);
364
365         if (ret)
366                 *ret = a;
367
368         return 0;
369 }
370
371 LinkAddress *link_address_free(LinkAddress *a) {
372         if (!a)
373                 return NULL;
374
375         if (a->link) {
376                 LIST_REMOVE(addresses, a->link->addresses, a);
377
378                 if (a->llmnr_address_rr) {
379                         if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
380                                 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
381                         else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
382                                 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
383                 }
384
385                 if (a->llmnr_ptr_rr) {
386                         if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
387                                 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
388                         else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
389                                 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
390                 }
391         }
392
393         dns_resource_record_unref(a->llmnr_address_rr);
394         dns_resource_record_unref(a->llmnr_ptr_rr);
395
396         free(a);
397         return NULL;
398 }
399
400 void link_address_add_rrs(LinkAddress *a, bool force_remove) {
401         int r;
402
403         assert(a);
404
405         if (a->family == AF_INET) {
406
407                 if (!force_remove &&
408                     link_address_relevant(a) &&
409                     a->link->llmnr_ipv4_scope &&
410                     a->link->llmnr_support == SUPPORT_YES &&
411                     a->link->manager->llmnr_support == SUPPORT_YES) {
412
413                         if (!a->link->manager->host_ipv4_key) {
414                                 a->link->manager->host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->hostname);
415                                 if (!a->link->manager->host_ipv4_key) {
416                                         r = -ENOMEM;
417                                         goto fail;
418                                 }
419                         }
420
421                         if (!a->llmnr_address_rr) {
422                                 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv4_key);
423                                 if (!a->llmnr_address_rr) {
424                                         r = -ENOMEM;
425                                         goto fail;
426                                 }
427
428                                 a->llmnr_address_rr->a.in_addr = a->in_addr.in;
429                                 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
430                         }
431
432                         if (!a->llmnr_ptr_rr) {
433                                 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
434                                 if (r < 0)
435                                         goto fail;
436
437                                 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
438                         }
439
440                         r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
441                         if (r < 0)
442                                 log_warning_errno(r, "Failed to add A record to LLMNR zone: %m");
443
444                         r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
445                         if (r < 0)
446                                 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
447                 } else {
448                         if (a->llmnr_address_rr) {
449                                 if (a->link->llmnr_ipv4_scope)
450                                         dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
451                                 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
452                         }
453
454                         if (a->llmnr_ptr_rr) {
455                                 if (a->link->llmnr_ipv4_scope)
456                                         dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
457                                 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
458                         }
459                 }
460         }
461
462         if (a->family == AF_INET6) {
463
464                 if (!force_remove &&
465                     link_address_relevant(a) &&
466                     a->link->llmnr_ipv6_scope &&
467                     a->link->llmnr_support == SUPPORT_YES &&
468                     a->link->manager->llmnr_support == SUPPORT_YES) {
469
470                         if (!a->link->manager->host_ipv6_key) {
471                                 a->link->manager->host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->hostname);
472                                 if (!a->link->manager->host_ipv6_key) {
473                                         r = -ENOMEM;
474                                         goto fail;
475                                 }
476                         }
477
478                         if (!a->llmnr_address_rr) {
479                                 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv6_key);
480                                 if (!a->llmnr_address_rr) {
481                                         r = -ENOMEM;
482                                         goto fail;
483                                 }
484
485                                 a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
486                                 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
487                         }
488
489                         if (!a->llmnr_ptr_rr) {
490                                 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
491                                 if (r < 0)
492                                         goto fail;
493
494                                 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
495                         }
496
497                         r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
498                         if (r < 0)
499                                 log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m");
500
501                         r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
502                         if (r < 0)
503                                 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
504                 } else {
505                         if (a->llmnr_address_rr) {
506                                 if (a->link->llmnr_ipv6_scope)
507                                         dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
508                                 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
509                         }
510
511                         if (a->llmnr_ptr_rr) {
512                                 if (a->link->llmnr_ipv6_scope)
513                                         dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
514                                 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
515                         }
516                 }
517         }
518
519         return;
520
521 fail:
522         log_debug_errno(r, "Failed to update address RRs: %m");
523 }
524
525 int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
526         int r;
527         assert(a);
528         assert(m);
529
530         r = sd_rtnl_message_addr_get_flags(m, &a->flags);
531         if (r < 0)
532                 return r;
533
534         sd_rtnl_message_addr_get_scope(m, &a->scope);
535
536         link_allocate_scopes(a->link);
537         link_add_rrs(a->link, false);
538
539         return 0;
540 }
541
542 bool link_address_relevant(LinkAddress *a) {
543         assert(a);
544
545         if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE))
546                 return false;
547
548         if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
549                 return false;
550
551         return true;
552 }