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