chiark / gitweb /
resolved: don't override zone item state after starting the probe
[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, 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("Failed to allocate DNS scope: %s", strerror(-r));
92                 }
93         } else
94                 l->unicast_scope = dns_scope_free(l->unicast_scope);
95
96         if (link_relevant(l, AF_INET) && l->llmnr_support != SUPPORT_NO && l->manager->llmnr_support != SUPPORT_NO) {
97                 if (!l->llmnr_ipv4_scope) {
98                         r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
99                         if (r < 0)
100                                 log_warning("Failed to allocate LLMNR IPv4 scope: %s", strerror(-r));
101                 }
102         } else
103                 l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
104
105         if (link_relevant(l, AF_INET6) && l->llmnr_support != SUPPORT_NO && l->manager->llmnr_support != SUPPORT_NO) {
106                 if (!l->llmnr_ipv6_scope) {
107                         r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
108                         if (r < 0)
109                                 log_warning("Failed to allocate LLMNR IPv6 scope: %s", strerror(-r));
110                 }
111         } else
112                 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
113 }
114
115 void link_add_rrs(Link *l, bool force_remove) {
116         LinkAddress *a;
117
118         LIST_FOREACH(addresses, a, l->addresses)
119                 link_address_add_rrs(a, force_remove);
120 }
121
122 int link_update_rtnl(Link *l, sd_rtnl_message *m) {
123         const char *n = NULL;
124         int r;
125
126         assert(l);
127         assert(m);
128
129         r = sd_rtnl_message_link_get_flags(m, &l->flags);
130         if (r < 0)
131                 return r;
132
133         sd_rtnl_message_read_u32(m, IFLA_MTU, &l->mtu);
134
135         if (sd_rtnl_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
136                 strncpy(l->name, n, sizeof(l->name));
137                 char_array_0(l->name);
138         }
139
140         link_allocate_scopes(l);
141         link_add_rrs(l, false);
142
143         return 0;
144 }
145
146 static int link_update_dns_servers(Link *l) {
147         _cleanup_strv_free_ char **nameservers = NULL;
148         char **nameserver;
149         DnsServer *s, *nx;
150         int r;
151
152         assert(l);
153
154         r = sd_network_get_dns(l->ifindex, &nameservers);
155         if (r < 0)
156                 goto clear;
157
158         LIST_FOREACH(servers, s, l->dns_servers)
159                 s->marked = true;
160
161         STRV_FOREACH(nameserver, nameservers) {
162                 union in_addr_union a;
163                 int family;
164
165                 r = in_addr_from_string_auto(*nameserver, &family, &a);
166                 if (r < 0)
167                         goto clear;
168
169                 s = link_find_dns_server(l, family, &a);
170                 if (s)
171                         s->marked = false;
172                 else {
173                         r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a);
174                         if (r < 0)
175                                 goto clear;
176                 }
177         }
178
179         LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
180                 if (s->marked)
181                         dns_server_free(s);
182
183         return 0;
184
185 clear:
186         while (l->dns_servers)
187                 dns_server_free(l->dns_servers);
188
189         return r;
190 }
191
192 static int link_update_llmnr_support(Link *l) {
193         _cleanup_free_ char *b = NULL;
194         int r;
195
196         assert(l);
197
198         r = sd_network_get_llmnr(l->ifindex, &b);
199         if (r < 0)
200                 goto clear;
201
202         r = parse_boolean(b);
203         if (r < 0) {
204                 if (streq(b, "resolve"))
205                         l->llmnr_support = SUPPORT_RESOLVE;
206                 else
207                         goto clear;
208
209         } else if (r > 0)
210                 l->llmnr_support = SUPPORT_YES;
211         else
212                 l->llmnr_support = SUPPORT_NO;
213
214         return 0;
215
216 clear:
217         l->llmnr_support = SUPPORT_YES;
218         return r;
219 }
220
221 int link_update_monitor(Link *l) {
222         assert(l);
223
224         link_update_dns_servers(l);
225         link_update_llmnr_support(l);
226         link_allocate_scopes(l);
227         link_add_rrs(l, false);
228
229         return 0;
230 }
231
232 bool link_relevant(Link *l, int family) {
233         _cleanup_free_ char *state = NULL;
234         LinkAddress *a;
235
236         assert(l);
237
238         /* A link is relevant if it isn't a loopback or pointopoint
239          * device, has a link beat, can do multicast and has at least
240          * one relevant IP address */
241
242         if (l->flags & (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_DORMANT))
243                 return false;
244
245         if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST))
246                 return false;
247
248         sd_network_get_link_operational_state(l->ifindex, &state);
249         if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
250                 return false;
251
252         LIST_FOREACH(addresses, a, l->addresses)
253                 if (a->family == family && link_address_relevant(a))
254                         return true;
255
256         return false;
257 }
258
259 LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
260         LinkAddress *a;
261
262         assert(l);
263
264         LIST_FOREACH(addresses, a, l->addresses)
265                 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
266                         return a;
267
268         return NULL;
269 }
270
271 DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
272         DnsServer *s;
273
274         assert(l);
275
276         LIST_FOREACH(servers, s, l->dns_servers)
277                 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
278                         return s;
279         return NULL;
280 }
281
282 DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
283         assert(l);
284
285         if (l->current_dns_server == s)
286                 return s;
287
288         if (s) {
289                 _cleanup_free_ char *ip = NULL;
290
291                 in_addr_to_string(s->family, &s->address, &ip);
292                 log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name);
293         }
294
295         l->current_dns_server = s;
296
297         if (l->unicast_scope)
298                 dns_cache_flush(&l->unicast_scope->cache);
299
300         return s;
301 }
302
303 DnsServer *link_get_dns_server(Link *l) {
304         assert(l);
305
306         if (!l->current_dns_server)
307                 link_set_dns_server(l, l->dns_servers);
308
309         return l->current_dns_server;
310 }
311
312 void link_next_dns_server(Link *l) {
313         assert(l);
314
315         if (!l->current_dns_server)
316                 return;
317
318         if (l->current_dns_server->servers_next) {
319                 link_set_dns_server(l, l->current_dns_server->servers_next);
320                 return;
321         }
322
323         link_set_dns_server(l, l->dns_servers);
324 }
325
326 int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
327         LinkAddress *a;
328
329         assert(l);
330         assert(in_addr);
331
332         a = new0(LinkAddress, 1);
333         if (!a)
334                 return -ENOMEM;
335
336         a->family = family;
337         a->in_addr = *in_addr;
338
339         a->link = l;
340         LIST_PREPEND(addresses, l->addresses, a);
341
342         if (ret)
343                 *ret = a;
344
345         return 0;
346 }
347
348 LinkAddress *link_address_free(LinkAddress *a) {
349         if (!a)
350                 return NULL;
351
352         if (a->link) {
353                 LIST_REMOVE(addresses, a->link->addresses, a);
354
355                 if (a->llmnr_address_rr) {
356                         if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
357                                 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
358                         else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
359                                 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
360                 }
361
362                 if (a->llmnr_ptr_rr) {
363                         if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
364                                 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
365                         else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
366                                 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
367                 }
368         }
369
370         dns_resource_record_unref(a->llmnr_address_rr);
371         dns_resource_record_unref(a->llmnr_ptr_rr);
372
373         free(a);
374         return NULL;
375 }
376
377 void link_address_add_rrs(LinkAddress *a, bool force_remove) {
378         int r;
379
380         assert(a);
381
382         if (a->family == AF_INET) {
383
384                 if (!force_remove &&
385                     link_address_relevant(a) &&
386                     a->link->llmnr_ipv4_scope &&
387                     a->link->llmnr_support == SUPPORT_YES &&
388                     a->link->manager->llmnr_support == SUPPORT_YES) {
389
390                         if (!a->link->manager->host_ipv4_key) {
391                                 a->link->manager->host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->hostname);
392                                 if (!a->link->manager->host_ipv4_key) {
393                                         r = -ENOMEM;
394                                         goto fail;
395                                 }
396                         }
397
398                         if (!a->llmnr_address_rr) {
399                                 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv4_key);
400                                 if (!a->llmnr_address_rr) {
401                                         r = -ENOMEM;
402                                         goto fail;
403                                 }
404
405                                 a->llmnr_address_rr->a.in_addr = a->in_addr.in;
406                                 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
407                         }
408
409                         if (!a->llmnr_ptr_rr) {
410                                 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
411                                 if (r < 0)
412                                         goto fail;
413
414                                 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
415                         }
416
417                         r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
418                         if (r < 0)
419                                 log_warning("Failed tp add A record to LLMNR zone: %s", strerror(-r));
420
421                         r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
422                         if (r < 0)
423                                 log_warning("Failed tp add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
424                 } else {
425                         if (a->llmnr_address_rr) {
426                                 if (a->link->llmnr_ipv4_scope)
427                                         dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
428                                 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
429                         }
430
431                         if (a->llmnr_ptr_rr) {
432                                 if (a->link->llmnr_ipv4_scope)
433                                         dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
434                                 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
435                         }
436                 }
437         }
438
439         if (a->family == AF_INET6) {
440
441                 if (!force_remove &&
442                     link_address_relevant(a) &&
443                     a->link->llmnr_ipv6_scope &&
444                     a->link->llmnr_support == SUPPORT_YES &&
445                     a->link->manager->llmnr_support == SUPPORT_YES) {
446
447                         if (!a->link->manager->host_ipv6_key) {
448                                 a->link->manager->host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->hostname);
449                                 if (!a->link->manager->host_ipv6_key) {
450                                         r = -ENOMEM;
451                                         goto fail;
452                                 }
453                         }
454
455                         if (!a->llmnr_address_rr) {
456                                 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv6_key);
457                                 if (!a->llmnr_address_rr) {
458                                         r = -ENOMEM;
459                                         goto fail;
460                                 }
461
462                                 a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
463                                 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
464                         }
465
466                         if (!a->llmnr_ptr_rr) {
467                                 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
468                                 if (r < 0)
469                                         goto fail;
470
471                                 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
472                         }
473
474                         r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
475                         if (r < 0)
476                                 log_warning("Failed to add AAAA record to LLMNR zone: %s", strerror(-r));
477
478                         r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
479                         if (r < 0)
480                                 log_warning("Failed to add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
481                 } else {
482                         if (a->llmnr_address_rr) {
483                                 if (a->link->llmnr_ipv6_scope)
484                                         dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
485                                 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
486                         }
487
488                         if (a->llmnr_ptr_rr) {
489                                 if (a->link->llmnr_ipv6_scope)
490                                         dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
491                                 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
492                         }
493                 }
494         }
495
496         return;
497
498 fail:
499         log_debug("Failed to update address RRs: %s", strerror(-r));
500 }
501
502 int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
503         int r;
504         assert(a);
505         assert(m);
506
507         r = sd_rtnl_message_addr_get_flags(m, &a->flags);
508         if (r < 0)
509                 return r;
510
511         sd_rtnl_message_addr_get_scope(m, &a->scope);
512
513         link_allocate_scopes(a->link);
514         link_add_rrs(a->link, false);
515
516         return 0;
517 }
518
519 bool link_address_relevant(LinkAddress *a) {
520         assert(a);
521
522         if (a->flags & IFA_F_DEPRECATED)
523                 return false;
524
525         if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
526                 return false;
527
528         return true;
529 }