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