]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-link.c
resolved: implement LLMNR uniqueness verification
[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
ec2c5e43 95 if (link_relevant(l, AF_INET) && l->manager->use_llmnr) {
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
ec2c5e43 104 if (link_relevant(l, AF_INET6) && l->manager->use_llmnr) {
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
6073b6f2 153 LIST_FOREACH(servers, s, l->dns_servers)
74b2466e
LP
154 s->marked = true;
155
6f4dedb2
TG
156 r = sd_network_get_dns(l->ifindex, &nameservers);
157 if (r < 0)
74b2466e 158 goto clear;
74b2466e 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 {
6f4dedb2 172 r = dns_server_new(l->manager, NULL, 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
251DnsServer *link_get_dns_server(Link *l) {
252 assert(l);
253
254 if (!l->current_dns_server)
6073b6f2 255 l->current_dns_server = l->dns_servers;
74b2466e
LP
256
257 return l->current_dns_server;
258}
259
260void link_next_dns_server(Link *l) {
261 assert(l);
262
263 /* Switch to the next DNS server */
264
265 if (!l->current_dns_server) {
6073b6f2 266 l->current_dns_server = l->dns_servers;
74b2466e
LP
267 if (l->current_dns_server)
268 return;
269 }
270
271 if (!l->current_dns_server)
272 return;
273
274 if (l->current_dns_server->servers_next) {
275 l->current_dns_server = l->current_dns_server->servers_next;
276 return;
277 }
278
6073b6f2 279 l->current_dns_server = l->dns_servers;
74b2466e
LP
280}
281
623a4c97 282int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
74b2466e
LP
283 LinkAddress *a;
284
285 assert(l);
286 assert(in_addr);
287
288 a = new0(LinkAddress, 1);
289 if (!a)
290 return -ENOMEM;
291
292 a->family = family;
293 a->in_addr = *in_addr;
294
295 a->link = l;
296 LIST_PREPEND(addresses, l->addresses, a);
297
298 if (ret)
299 *ret = a;
300
301 return 0;
302}
303
304LinkAddress *link_address_free(LinkAddress *a) {
305 if (!a)
306 return NULL;
307
623a4c97 308 if (a->link) {
74b2466e
LP
309 LIST_REMOVE(addresses, a->link->addresses, a);
310
623a4c97 311 if (a->llmnr_address_rr) {
623a4c97
LP
312 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
313 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
314 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
315 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
623a4c97
LP
316 }
317
318 if (a->llmnr_ptr_rr) {
319 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
320 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
321 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
322 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
623a4c97
LP
323 }
324 }
325
ec2c5e43
LP
326 dns_resource_record_unref(a->llmnr_address_rr);
327 dns_resource_record_unref(a->llmnr_ptr_rr);
328
74b2466e
LP
329 free(a);
330 return NULL;
331}
332
ec2c5e43 333void link_address_add_rrs(LinkAddress *a, bool force_remove) {
623a4c97
LP
334 int r;
335
336 assert(a);
337
ec2c5e43 338 if (a->family == AF_INET) {
623a4c97 339
ec2c5e43 340 if (!force_remove && link_address_relevant(a) && a->link->llmnr_ipv4_scope) {
623a4c97 341 if (!a->link->manager->host_ipv4_key) {
ec2c5e43
LP
342 a->link->manager->host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->hostname);
343 if (!a->link->manager->host_ipv4_key) {
344 r = -ENOMEM;
345 goto fail;
346 }
623a4c97 347 }
623a4c97 348
623a4c97 349 if (!a->llmnr_address_rr) {
ec2c5e43
LP
350 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv4_key);
351 if (!a->llmnr_address_rr) {
352 r = -ENOMEM;
353 goto fail;
354 }
355
356 a->llmnr_address_rr->a.in_addr = a->in_addr.in;
357 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
623a4c97
LP
358 }
359
ec2c5e43
LP
360 if (!a->llmnr_ptr_rr) {
361 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
362 if (r < 0)
363 goto fail;
623a4c97 364
ec2c5e43
LP
365 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
366 }
623a4c97 367
ec2c5e43 368 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
623a4c97 369 if (r < 0)
ec2c5e43 370 log_warning("Failed tp add A record to LLMNR zone: %s", strerror(-r));
623a4c97 371
ec2c5e43 372 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
623a4c97 373 if (r < 0)
ec2c5e43 374 log_warning("Failed tp add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
623a4c97 375 } else {
ec2c5e43
LP
376 if (a->llmnr_address_rr) {
377 if (a->link->llmnr_ipv4_scope)
378 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
379 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
380 }
381
382 if (a->llmnr_ptr_rr) {
383 if (a->link->llmnr_ipv4_scope)
384 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
385 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
386 }
623a4c97
LP
387 }
388 }
389
ec2c5e43 390 if (a->family == AF_INET6) {
623a4c97 391
ec2c5e43 392 if (!force_remove && link_address_relevant(a) && a->link->llmnr_ipv6_scope) {
623a4c97 393 if (!a->link->manager->host_ipv6_key) {
ec2c5e43
LP
394 a->link->manager->host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->hostname);
395 if (!a->link->manager->host_ipv6_key) {
396 r = -ENOMEM;
397 goto fail;
398 }
623a4c97 399 }
623a4c97 400
623a4c97 401 if (!a->llmnr_address_rr) {
ec2c5e43
LP
402 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv6_key);
403 if (!a->llmnr_address_rr) {
404 r = -ENOMEM;
405 goto fail;
406 }
407
408 a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
409 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
623a4c97
LP
410 }
411
ec2c5e43
LP
412 if (!a->llmnr_ptr_rr) {
413 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
414 if (r < 0)
415 goto fail;
623a4c97 416
ec2c5e43
LP
417 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
418 }
623a4c97 419
ec2c5e43 420 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
623a4c97 421 if (r < 0)
ec2c5e43 422 log_warning("Failed to add AAAA record to LLMNR zone: %s", strerror(-r));
623a4c97 423
ec2c5e43 424 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
623a4c97 425 if (r < 0)
ec2c5e43 426 log_warning("Failed to add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
623a4c97 427 } else {
ec2c5e43
LP
428 if (a->llmnr_address_rr) {
429 if (a->link->llmnr_ipv6_scope)
430 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
431 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
432 }
433
434 if (a->llmnr_ptr_rr) {
435 if (a->link->llmnr_ipv6_scope)
436 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
437 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
438 }
623a4c97
LP
439 }
440 }
441
442 return;
443
444fail:
445 log_debug("Failed to update address RRs: %s", strerror(-r));
446}
447
74b2466e
LP
448int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
449 int r;
450 assert(a);
451 assert(m);
452
453 r = sd_rtnl_message_addr_get_flags(m, &a->flags);
454 if (r < 0)
455 return r;
456
1716f6dc 457 sd_rtnl_message_addr_get_scope(m, &a->scope);
74b2466e 458
1716f6dc 459 link_allocate_scopes(a->link);
ec2c5e43 460 link_add_rrs(a->link, false);
623a4c97 461
74b2466e
LP
462 return 0;
463}
464
465bool link_address_relevant(LinkAddress *a) {
466 assert(a);
467
468 if (a->flags & IFA_F_DEPRECATED)
469 return false;
470
471 if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
472 return false;
473
474 return true;
475}