]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-link.c
hashmap: introduce hash_ops to make struct Hashmap smaller
[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
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 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)
102 log_warning("Failed to allocate LLMNR IPv4 scope: %s", strerror(-r));
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)
114 log_warning("Failed to allocate LLMNR IPv6 scope: %s", strerror(-r));
115 }
116 } else
117 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
118 }
119
120 void 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
127 int 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
151 static 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
190 clear:
191 while (l->dns_servers)
192 dns_server_free(l->dns_servers);
193
194 return r;
195 }
196
197 static 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
221 clear:
222 l->llmnr_support = SUPPORT_YES;
223 return r;
224 }
225
226 static 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
243 int 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
255 bool 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
282 LinkAddress *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
294 DnsServer* 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
305 DnsServer* 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
326 DnsServer *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
335 void 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
349 int 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
371 LinkAddress *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
400 void 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)
442 log_warning("Failed to add A record to LLMNR zone: %s", strerror(-r));
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)
446 log_warning("Failed to add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
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)
499 log_warning("Failed to add AAAA record to LLMNR zone: %s", strerror(-r));
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)
503 log_warning("Failed to add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
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
521 fail:
522 log_debug("Failed to update address RRs: %s", strerror(-r));
523 }
524
525 int 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
542 bool 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 }