]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-link.c
hwdb: Update database of Bluetooth company identifiers
[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 strv_free(l->unicast_scope->domains);
242 l->unicast_scope->domains = NULL;
243
244 r = sd_network_link_get_domains(l->ifindex,
245 &l->unicast_scope->domains);
246 if (r < 0)
247 return r;
248
249 return 0;
250 }
251
252 int link_update_monitor(Link *l) {
253 assert(l);
254
255 link_update_dns_servers(l);
256 link_update_llmnr_support(l);
257 link_allocate_scopes(l);
258 link_update_domains(l);
259 link_add_rrs(l, false);
260
261 return 0;
262 }
263
264 bool link_relevant(Link *l, int family) {
265 _cleanup_free_ char *state = NULL;
266 LinkAddress *a;
267
268 assert(l);
269
270 /* A link is relevant if it isn't a loopback or pointopoint
271 * device, has a link beat, can do multicast and has at least
272 * one relevant IP address */
273
274 if (l->flags & (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_DORMANT))
275 return false;
276
277 if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST))
278 return false;
279
280 sd_network_link_get_operational_state(l->ifindex, &state);
281 if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
282 return false;
283
284 LIST_FOREACH(addresses, a, l->addresses)
285 if (a->family == family && link_address_relevant(a))
286 return true;
287
288 return false;
289 }
290
291 LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
292 LinkAddress *a;
293
294 assert(l);
295
296 LIST_FOREACH(addresses, a, l->addresses)
297 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
298 return a;
299
300 return NULL;
301 }
302
303 DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
304 DnsServer *s;
305
306 assert(l);
307
308 LIST_FOREACH(servers, s, l->dns_servers)
309 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
310 return s;
311 return NULL;
312 }
313
314 DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
315 assert(l);
316
317 if (l->current_dns_server == s)
318 return s;
319
320 if (s) {
321 _cleanup_free_ char *ip = NULL;
322
323 in_addr_to_string(s->family, &s->address, &ip);
324 log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name);
325 }
326
327 l->current_dns_server = s;
328
329 if (l->unicast_scope)
330 dns_cache_flush(&l->unicast_scope->cache);
331
332 return s;
333 }
334
335 DnsServer *link_get_dns_server(Link *l) {
336 assert(l);
337
338 if (!l->current_dns_server)
339 link_set_dns_server(l, l->dns_servers);
340
341 return l->current_dns_server;
342 }
343
344 void link_next_dns_server(Link *l) {
345 assert(l);
346
347 if (!l->current_dns_server)
348 return;
349
350 if (l->current_dns_server->servers_next) {
351 link_set_dns_server(l, l->current_dns_server->servers_next);
352 return;
353 }
354
355 link_set_dns_server(l, l->dns_servers);
356 }
357
358 int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
359 LinkAddress *a;
360
361 assert(l);
362 assert(in_addr);
363
364 a = new0(LinkAddress, 1);
365 if (!a)
366 return -ENOMEM;
367
368 a->family = family;
369 a->in_addr = *in_addr;
370
371 a->link = l;
372 LIST_PREPEND(addresses, l->addresses, a);
373
374 if (ret)
375 *ret = a;
376
377 return 0;
378 }
379
380 LinkAddress *link_address_free(LinkAddress *a) {
381 if (!a)
382 return NULL;
383
384 if (a->link) {
385 LIST_REMOVE(addresses, a->link->addresses, a);
386
387 if (a->llmnr_address_rr) {
388 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
389 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
390 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
391 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
392 }
393
394 if (a->llmnr_ptr_rr) {
395 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
396 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
397 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
398 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
399 }
400 }
401
402 dns_resource_record_unref(a->llmnr_address_rr);
403 dns_resource_record_unref(a->llmnr_ptr_rr);
404
405 free(a);
406 return NULL;
407 }
408
409 void link_address_add_rrs(LinkAddress *a, bool force_remove) {
410 int r;
411
412 assert(a);
413
414 if (a->family == AF_INET) {
415
416 if (!force_remove &&
417 link_address_relevant(a) &&
418 a->link->llmnr_ipv4_scope &&
419 a->link->llmnr_support == SUPPORT_YES &&
420 a->link->manager->llmnr_support == SUPPORT_YES) {
421
422 if (!a->link->manager->llmnr_host_ipv4_key) {
423 a->link->manager->llmnr_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->llmnr_hostname);
424 if (!a->link->manager->llmnr_host_ipv4_key) {
425 r = -ENOMEM;
426 goto fail;
427 }
428 }
429
430 if (!a->llmnr_address_rr) {
431 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv4_key);
432 if (!a->llmnr_address_rr) {
433 r = -ENOMEM;
434 goto fail;
435 }
436
437 a->llmnr_address_rr->a.in_addr = a->in_addr.in;
438 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
439 }
440
441 if (!a->llmnr_ptr_rr) {
442 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
443 if (r < 0)
444 goto fail;
445
446 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
447 }
448
449 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
450 if (r < 0)
451 log_warning_errno(r, "Failed to add A record to LLMNR zone: %m");
452
453 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
454 if (r < 0)
455 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
456 } else {
457 if (a->llmnr_address_rr) {
458 if (a->link->llmnr_ipv4_scope)
459 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
460 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
461 }
462
463 if (a->llmnr_ptr_rr) {
464 if (a->link->llmnr_ipv4_scope)
465 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
466 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
467 }
468 }
469 }
470
471 if (a->family == AF_INET6) {
472
473 if (!force_remove &&
474 link_address_relevant(a) &&
475 a->link->llmnr_ipv6_scope &&
476 a->link->llmnr_support == SUPPORT_YES &&
477 a->link->manager->llmnr_support == SUPPORT_YES) {
478
479 if (!a->link->manager->llmnr_host_ipv6_key) {
480 a->link->manager->llmnr_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->llmnr_hostname);
481 if (!a->link->manager->llmnr_host_ipv6_key) {
482 r = -ENOMEM;
483 goto fail;
484 }
485 }
486
487 if (!a->llmnr_address_rr) {
488 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv6_key);
489 if (!a->llmnr_address_rr) {
490 r = -ENOMEM;
491 goto fail;
492 }
493
494 a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
495 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
496 }
497
498 if (!a->llmnr_ptr_rr) {
499 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
500 if (r < 0)
501 goto fail;
502
503 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
504 }
505
506 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
507 if (r < 0)
508 log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m");
509
510 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
511 if (r < 0)
512 log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
513 } else {
514 if (a->llmnr_address_rr) {
515 if (a->link->llmnr_ipv6_scope)
516 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
517 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
518 }
519
520 if (a->llmnr_ptr_rr) {
521 if (a->link->llmnr_ipv6_scope)
522 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
523 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
524 }
525 }
526 }
527
528 return;
529
530 fail:
531 log_debug_errno(r, "Failed to update address RRs: %m");
532 }
533
534 int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
535 int r;
536 assert(a);
537 assert(m);
538
539 r = sd_rtnl_message_addr_get_flags(m, &a->flags);
540 if (r < 0)
541 return r;
542
543 sd_rtnl_message_addr_get_scope(m, &a->scope);
544
545 link_allocate_scopes(a->link);
546 link_add_rrs(a->link, false);
547
548 return 0;
549 }
550
551 bool link_address_relevant(LinkAddress *a) {
552 assert(a);
553
554 if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE))
555 return false;
556
557 if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
558 return false;
559
560 return true;
561 }