]> git.proxmox.com Git - systemd.git/blame - src/resolve/resolved-link.c
Imported Upstream version 218
[systemd.git] / src / resolve / resolved-link.c
CommitLineData
5eef597e
MP
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
29int 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
60Link *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
82static 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)
f47781d8 91 log_warning_errno(r, "Failed to allocate DNS scope: %m");
5eef597e
MP
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)
f47781d8 102 log_warning_errno(r, "Failed to allocate LLMNR IPv4 scope: %m");
5eef597e
MP
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)
f47781d8 114 log_warning_errno(r, "Failed to allocate LLMNR IPv6 scope: %m");
5eef597e
MP
115 }
116 } else
117 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
118}
119
120void 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
127int 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
151static 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
190clear:
191 while (l->dns_servers)
192 dns_server_free(l->dns_servers);
193
194 return r;
195}
196
197static 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
221clear:
222 l->llmnr_support = SUPPORT_YES;
223 return r;
224}
225
226static 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
243int 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
255bool 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
282LinkAddress *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
294DnsServer* 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
305DnsServer* 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
326DnsServer *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
335void 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
349int 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
371LinkAddress *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
400void 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)
f47781d8 442 log_warning_errno(r, "Failed to add A record to LLMNR zone: %m");
5eef597e
MP
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)
f47781d8 446 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
5eef597e
MP
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)
f47781d8 499 log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m");
5eef597e
MP
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)
f47781d8 503 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
5eef597e
MP
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
521fail:
f47781d8 522 log_debug_errno(r, "Failed to update address RRs: %m");
5eef597e
MP
523}
524
525int 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
542bool 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}