chiark / gitweb /
resolved: when there's already somebody listening on the LLMNR ports, simple disable...
[elogind.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         r = sd_network_get_dns(l->ifindex, &nameservers);
154         if (r < 0)
155                 goto clear;
156
157         LIST_FOREACH(servers, s, l->dns_servers)
158                 s->marked = true;
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 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         }
263
264         l->current_dns_server = s;
265
266         if (l->unicast_scope)
267                 dns_cache_flush(&l->unicast_scope->cache);
268
269         return s;
270 }
271
272 DnsServer *link_get_dns_server(Link *l) {
273         assert(l);
274
275         if (!l->current_dns_server)
276                 link_set_dns_server(l, l->dns_servers);
277
278         return l->current_dns_server;
279 }
280
281 void link_next_dns_server(Link *l) {
282         assert(l);
283
284         if (!l->current_dns_server)
285                 return;
286
287         if (l->current_dns_server->servers_next) {
288                 link_set_dns_server(l, l->current_dns_server->servers_next);
289                 return;
290         }
291
292         link_set_dns_server(l, l->dns_servers);
293 }
294
295 int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
296         LinkAddress *a;
297
298         assert(l);
299         assert(in_addr);
300
301         a = new0(LinkAddress, 1);
302         if (!a)
303                 return -ENOMEM;
304
305         a->family = family;
306         a->in_addr = *in_addr;
307
308         a->link = l;
309         LIST_PREPEND(addresses, l->addresses, a);
310
311         if (ret)
312                 *ret = a;
313
314         return 0;
315 }
316
317 LinkAddress *link_address_free(LinkAddress *a) {
318         if (!a)
319                 return NULL;
320
321         if (a->link) {
322                 LIST_REMOVE(addresses, a->link->addresses, a);
323
324                 if (a->llmnr_address_rr) {
325                         if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
326                                 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
327                         else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
328                                 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
329                 }
330
331                 if (a->llmnr_ptr_rr) {
332                         if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
333                                 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
334                         else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
335                                 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
336                 }
337         }
338
339         dns_resource_record_unref(a->llmnr_address_rr);
340         dns_resource_record_unref(a->llmnr_ptr_rr);
341
342         free(a);
343         return NULL;
344 }
345
346 void link_address_add_rrs(LinkAddress *a, bool force_remove) {
347         int r;
348
349         assert(a);
350
351         if (a->family == AF_INET) {
352
353                 if (!force_remove &&
354                     link_address_relevant(a) &&
355                     a->link->llmnr_ipv4_scope &&
356                     a->link->manager->llmnr_support == SUPPORT_YES) {
357
358                         if (!a->link->manager->host_ipv4_key) {
359                                 a->link->manager->host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->hostname);
360                                 if (!a->link->manager->host_ipv4_key) {
361                                         r = -ENOMEM;
362                                         goto fail;
363                                 }
364                         }
365
366                         if (!a->llmnr_address_rr) {
367                                 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv4_key);
368                                 if (!a->llmnr_address_rr) {
369                                         r = -ENOMEM;
370                                         goto fail;
371                                 }
372
373                                 a->llmnr_address_rr->a.in_addr = a->in_addr.in;
374                                 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
375                         }
376
377                         if (!a->llmnr_ptr_rr) {
378                                 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
379                                 if (r < 0)
380                                         goto fail;
381
382                                 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
383                         }
384
385                         r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
386                         if (r < 0)
387                                 log_warning("Failed tp add A record to LLMNR zone: %s", strerror(-r));
388
389                         r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
390                         if (r < 0)
391                                 log_warning("Failed tp add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
392                 } else {
393                         if (a->llmnr_address_rr) {
394                                 if (a->link->llmnr_ipv4_scope)
395                                         dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
396                                 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
397                         }
398
399                         if (a->llmnr_ptr_rr) {
400                                 if (a->link->llmnr_ipv4_scope)
401                                         dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
402                                 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
403                         }
404                 }
405         }
406
407         if (a->family == AF_INET6) {
408
409                 if (!force_remove &&
410                     link_address_relevant(a) &&
411                     a->link->llmnr_ipv6_scope &&
412                     a->link->manager->llmnr_support == SUPPORT_YES) {
413
414                         if (!a->link->manager->host_ipv6_key) {
415                                 a->link->manager->host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->hostname);
416                                 if (!a->link->manager->host_ipv6_key) {
417                                         r = -ENOMEM;
418                                         goto fail;
419                                 }
420                         }
421
422                         if (!a->llmnr_address_rr) {
423                                 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv6_key);
424                                 if (!a->llmnr_address_rr) {
425                                         r = -ENOMEM;
426                                         goto fail;
427                                 }
428
429                                 a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
430                                 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
431                         }
432
433                         if (!a->llmnr_ptr_rr) {
434                                 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
435                                 if (r < 0)
436                                         goto fail;
437
438                                 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
439                         }
440
441                         r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
442                         if (r < 0)
443                                 log_warning("Failed to add AAAA record to LLMNR zone: %s", strerror(-r));
444
445                         r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
446                         if (r < 0)
447                                 log_warning("Failed to add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
448                 } else {
449                         if (a->llmnr_address_rr) {
450                                 if (a->link->llmnr_ipv6_scope)
451                                         dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
452                                 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
453                         }
454
455                         if (a->llmnr_ptr_rr) {
456                                 if (a->link->llmnr_ipv6_scope)
457                                         dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
458                                 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
459                         }
460                 }
461         }
462
463         return;
464
465 fail:
466         log_debug("Failed to update address RRs: %s", strerror(-r));
467 }
468
469 int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
470         int r;
471         assert(a);
472         assert(m);
473
474         r = sd_rtnl_message_addr_get_flags(m, &a->flags);
475         if (r < 0)
476                 return r;
477
478         sd_rtnl_message_addr_get_scope(m, &a->scope);
479
480         link_allocate_scopes(a->link);
481         link_add_rrs(a->link, false);
482
483         return 0;
484 }
485
486 bool link_address_relevant(LinkAddress *a) {
487         assert(a);
488
489         if (a->flags & IFA_F_DEPRECATED)
490                 return false;
491
492         if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
493                 return false;
494
495         return true;
496 }