]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-link.c
resolved: implement LLMNR uniqueness verification
[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->use_llmnr) {
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->use_llmnr) {
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, 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 DnsServer *link_get_dns_server(Link *l) {
252 assert(l);
253
254 if (!l->current_dns_server)
255 l->current_dns_server = l->dns_servers;
256
257 return l->current_dns_server;
258 }
259
260 void link_next_dns_server(Link *l) {
261 assert(l);
262
263 /* Switch to the next DNS server */
264
265 if (!l->current_dns_server) {
266 l->current_dns_server = l->dns_servers;
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
279 l->current_dns_server = l->dns_servers;
280 }
281
282 int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
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
304 LinkAddress *link_address_free(LinkAddress *a) {
305 if (!a)
306 return NULL;
307
308 if (a->link) {
309 LIST_REMOVE(addresses, a->link->addresses, a);
310
311 if (a->llmnr_address_rr) {
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);
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);
323 }
324 }
325
326 dns_resource_record_unref(a->llmnr_address_rr);
327 dns_resource_record_unref(a->llmnr_ptr_rr);
328
329 free(a);
330 return NULL;
331 }
332
333 void link_address_add_rrs(LinkAddress *a, bool force_remove) {
334 int r;
335
336 assert(a);
337
338 if (a->family == AF_INET) {
339
340 if (!force_remove && link_address_relevant(a) && a->link->llmnr_ipv4_scope) {
341 if (!a->link->manager->host_ipv4_key) {
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 }
347 }
348
349 if (!a->llmnr_address_rr) {
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;
358 }
359
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;
364
365 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
366 }
367
368 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
369 if (r < 0)
370 log_warning("Failed tp add A record to LLMNR zone: %s", strerror(-r));
371
372 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
373 if (r < 0)
374 log_warning("Failed tp add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
375 } else {
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 }
387 }
388 }
389
390 if (a->family == AF_INET6) {
391
392 if (!force_remove && link_address_relevant(a) && a->link->llmnr_ipv6_scope) {
393 if (!a->link->manager->host_ipv6_key) {
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 }
399 }
400
401 if (!a->llmnr_address_rr) {
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;
410 }
411
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;
416
417 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
418 }
419
420 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
421 if (r < 0)
422 log_warning("Failed to add AAAA record to LLMNR zone: %s", strerror(-r));
423
424 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
425 if (r < 0)
426 log_warning("Failed to add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
427 } else {
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 }
439 }
440 }
441
442 return;
443
444 fail:
445 log_debug("Failed to update address RRs: %s", strerror(-r));
446 }
447
448 int 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
457 sd_rtnl_message_addr_get_scope(m, &a->scope);
458
459 link_allocate_scopes(a->link);
460 link_add_rrs(a->link, false);
461
462 return 0;
463 }
464
465 bool 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 }