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