chiark / gitweb /
resolved: implement LLMNR uniqueness verification
[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->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 }