]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-link.c
resolved: read the system /etc/resolv.conf unless we wrote it ourselves
[thirdparty/systemd.git] / src / resolve / resolved-link.c
CommitLineData
74b2466e
LP
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"
74b2466e 25#include "strv.h"
ec2c5e43 26#include "missing.h"
74b2466e
LP
27#include "resolved-link.h"
28
29int 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
59Link *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);
1716f6dc
LP
71 dns_scope_free(l->llmnr_ipv4_scope);
72 dns_scope_free(l->llmnr_ipv6_scope);
74b2466e 73
6073b6f2
TG
74 while (l->dns_servers)
75 dns_server_free(l->dns_servers);
74b2466e
LP
76
77 free(l);
78 return NULL;
1716f6dc
LP
79}
80
81static void link_allocate_scopes(Link *l) {
82 int r;
83
84 assert(l);
85
6073b6f2 86 if (l->dns_servers) {
1716f6dc
LP
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
4e945a6f 95 if (link_relevant(l, AF_INET) && l->manager->llmnr_support != SUPPORT_NO) {
1716f6dc
LP
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
4e945a6f 104 if (link_relevant(l, AF_INET6) && l->manager->llmnr_support != SUPPORT_NO) {
1716f6dc
LP
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}
74b2466e 113
ec2c5e43 114void link_add_rrs(Link *l, bool force_remove) {
623a4c97
LP
115 LinkAddress *a;
116
117 LIST_FOREACH(addresses, a, l->addresses)
ec2c5e43 118 link_address_add_rrs(a, force_remove);
623a4c97
LP
119}
120
74b2466e 121int link_update_rtnl(Link *l, sd_rtnl_message *m) {
1716f6dc 122 const char *n = NULL;
74b2466e
LP
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
c5ed9316 132 sd_rtnl_message_read_u32(m, IFLA_MTU, &l->mtu);
1716f6dc
LP
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);
ec2c5e43 140 link_add_rrs(l, false);
623a4c97 141
74b2466e
LP
142 return 0;
143}
144
6073b6f2 145static int link_update_dns_servers(Link *l) {
6f4dedb2
TG
146 _cleanup_strv_free_ char **nameservers = NULL;
147 char **nameserver;
74b2466e 148 DnsServer *s, *nx;
6f4dedb2 149 int r;
74b2466e
LP
150
151 assert(l);
152
6f4dedb2
TG
153 r = sd_network_get_dns(l->ifindex, &nameservers);
154 if (r < 0)
74b2466e 155 goto clear;
74b2466e 156
5cb36f41
LP
157 LIST_FOREACH(servers, s, l->dns_servers)
158 s->marked = true;
159
6f4dedb2
TG
160 STRV_FOREACH(nameserver, nameservers) {
161 union in_addr_union a;
162 int family;
74b2466e 163
6f4dedb2
TG
164 r = in_addr_from_string_auto(*nameserver, &family, &a);
165 if (r < 0)
166 goto clear;
74b2466e 167
6f4dedb2 168 s = link_find_dns_server(l, family, &a);
74b2466e
LP
169 if (s)
170 s->marked = false;
171 else {
4e945a6f 172 r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a);
74b2466e
LP
173 if (r < 0)
174 goto clear;
175 }
176 }
177
6073b6f2 178 LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
74b2466e
LP
179 if (s->marked)
180 dns_server_free(s);
181
182 return 0;
183
184clear:
6073b6f2
TG
185 while (l->dns_servers)
186 dns_server_free(l->dns_servers);
74b2466e
LP
187
188 return r;
189}
190
191int link_update_monitor(Link *l) {
192 assert(l);
193
6073b6f2 194 link_update_dns_servers(l);
1716f6dc 195 link_allocate_scopes(l);
ec2c5e43 196 link_add_rrs(l, false);
74b2466e
LP
197
198 return 0;
199}
200
0dd25fb9 201bool link_relevant(Link *l, int family) {
1716f6dc 202 _cleanup_free_ char *state = NULL;
74b2466e
LP
203 LinkAddress *a;
204
205 assert(l);
206
ec2c5e43
LP
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;
74b2466e 213
ec2c5e43 214 if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST))
74b2466e
LP
215 return false;
216
1716f6dc
LP
217 sd_network_get_link_operational_state(l->ifindex, &state);
218 if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
74b2466e
LP
219 return false;
220
221 LIST_FOREACH(addresses, a, l->addresses)
1716f6dc 222 if (a->family == family && link_address_relevant(a))
74b2466e
LP
223 return true;
224
225 return false;
226}
227
623a4c97 228LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
74b2466e
LP
229 LinkAddress *a;
230
231 assert(l);
232
1716f6dc
LP
233 LIST_FOREACH(addresses, a, l->addresses)
234 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
74b2466e 235 return a;
74b2466e
LP
236
237 return NULL;
238}
239
623a4c97 240DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
6073b6f2 241 DnsServer *s;
74b2466e
LP
242
243 assert(l);
244
6073b6f2 245 LIST_FOREACH(servers, s, l->dns_servers)
1716f6dc 246 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
74b2466e 247 return s;
74b2466e
LP
248 return NULL;
249}
250
4e945a6f
LP
251static 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
74b2466e
LP
269DnsServer *link_get_dns_server(Link *l) {
270 assert(l);
271
272 if (!l->current_dns_server)
4e945a6f 273 link_set_dns_server(l, l->dns_servers);
74b2466e
LP
274
275 return l->current_dns_server;
276}
277
278void link_next_dns_server(Link *l) {
279 assert(l);
280
74b2466e
LP
281 if (!l->current_dns_server)
282 return;
283
284 if (l->current_dns_server->servers_next) {
4e945a6f 285 link_set_dns_server(l, l->current_dns_server->servers_next);
74b2466e
LP
286 return;
287 }
288
4e945a6f 289 link_set_dns_server(l, l->dns_servers);
74b2466e
LP
290}
291
623a4c97 292int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
74b2466e
LP
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
314LinkAddress *link_address_free(LinkAddress *a) {
315 if (!a)
316 return NULL;
317
623a4c97 318 if (a->link) {
74b2466e
LP
319 LIST_REMOVE(addresses, a->link->addresses, a);
320
623a4c97 321 if (a->llmnr_address_rr) {
623a4c97
LP
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);
623a4c97
LP
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);
623a4c97
LP
333 }
334 }
335
ec2c5e43
LP
336 dns_resource_record_unref(a->llmnr_address_rr);
337 dns_resource_record_unref(a->llmnr_ptr_rr);
338
74b2466e
LP
339 free(a);
340 return NULL;
341}
342
ec2c5e43 343void link_address_add_rrs(LinkAddress *a, bool force_remove) {
623a4c97
LP
344 int r;
345
346 assert(a);
347
ec2c5e43 348 if (a->family == AF_INET) {
623a4c97 349
4e945a6f
LP
350 if (!force_remove &&
351 link_address_relevant(a) &&
352 a->link->llmnr_ipv4_scope &&
353 a->link->manager->llmnr_support == SUPPORT_YES) {
354
623a4c97 355 if (!a->link->manager->host_ipv4_key) {
ec2c5e43
LP
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 }
623a4c97 361 }
623a4c97 362
623a4c97 363 if (!a->llmnr_address_rr) {
ec2c5e43
LP
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;
623a4c97
LP
372 }
373
ec2c5e43
LP
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;
623a4c97 378
ec2c5e43
LP
379 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
380 }
623a4c97 381
ec2c5e43 382 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
623a4c97 383 if (r < 0)
ec2c5e43 384 log_warning("Failed tp add A record to LLMNR zone: %s", strerror(-r));
623a4c97 385
ec2c5e43 386 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
623a4c97 387 if (r < 0)
ec2c5e43 388 log_warning("Failed tp add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
623a4c97 389 } else {
ec2c5e43
LP
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 }
623a4c97
LP
401 }
402 }
403
ec2c5e43 404 if (a->family == AF_INET6) {
623a4c97 405
4e945a6f
LP
406 if (!force_remove &&
407 link_address_relevant(a) &&
408 a->link->llmnr_ipv6_scope &&
409 a->link->manager->llmnr_support == SUPPORT_YES) {
410
623a4c97 411 if (!a->link->manager->host_ipv6_key) {
ec2c5e43
LP
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 }
623a4c97 417 }
623a4c97 418
623a4c97 419 if (!a->llmnr_address_rr) {
ec2c5e43
LP
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;
623a4c97
LP
428 }
429
ec2c5e43
LP
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;
623a4c97 434
ec2c5e43
LP
435 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
436 }
623a4c97 437
ec2c5e43 438 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
623a4c97 439 if (r < 0)
ec2c5e43 440 log_warning("Failed to add AAAA record to LLMNR zone: %s", strerror(-r));
623a4c97 441
ec2c5e43 442 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
623a4c97 443 if (r < 0)
ec2c5e43 444 log_warning("Failed to add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
623a4c97 445 } else {
ec2c5e43
LP
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 }
623a4c97
LP
457 }
458 }
459
460 return;
461
462fail:
463 log_debug("Failed to update address RRs: %s", strerror(-r));
464}
465
74b2466e
LP
466int 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
1716f6dc 475 sd_rtnl_message_addr_get_scope(m, &a->scope);
74b2466e 476
1716f6dc 477 link_allocate_scopes(a->link);
ec2c5e43 478 link_add_rrs(a->link, false);
623a4c97 479
74b2466e
LP
480 return 0;
481}
482
483bool 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}