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