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