chiark / gitweb /
Revert "socket: add support for TCP fast Open"
[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         l->llmnr_support = SUPPORT_YES;
46
47         r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
48         if (r < 0)
49                 return r;
50
51         l->manager = m;
52
53         if (ret)
54                 *ret = l;
55         l = NULL;
56
57         return 0;
58 }
59
60 Link *link_free(Link *l) {
61
62         if (!l)
63                 return NULL;
64
65         while (l->addresses)
66                 link_address_free(l->addresses);
67
68         if (l->manager)
69                 hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
70
71         dns_scope_free(l->unicast_scope);
72         dns_scope_free(l->llmnr_ipv4_scope);
73         dns_scope_free(l->llmnr_ipv6_scope);
74
75         while (l->dns_servers)
76                 dns_server_free(l->dns_servers);
77
78         free(l);
79         return NULL;
80 }
81
82 static void link_allocate_scopes(Link *l) {
83         int r;
84
85         assert(l);
86
87         if (l->dns_servers) {
88                 if (!l->unicast_scope) {
89                         r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
90                         if (r < 0)
91                                 log_warning("Failed to allocate DNS scope: %s", strerror(-r));
92                 }
93         } else
94                 l->unicast_scope = dns_scope_free(l->unicast_scope);
95
96         if (link_relevant(l, AF_INET) &&
97             l->llmnr_support != SUPPORT_NO &&
98             l->manager->llmnr_support != SUPPORT_NO) {
99                 if (!l->llmnr_ipv4_scope) {
100                         r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
101                         if (r < 0)
102                                 log_warning("Failed to allocate LLMNR IPv4 scope: %s", strerror(-r));
103                 }
104         } else
105                 l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
106
107         if (link_relevant(l, AF_INET6) &&
108             l->llmnr_support != SUPPORT_NO &&
109             l->manager->llmnr_support != SUPPORT_NO &&
110             socket_ipv6_is_supported()) {
111                 if (!l->llmnr_ipv6_scope) {
112                         r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
113                         if (r < 0)
114                                 log_warning("Failed to allocate LLMNR IPv6 scope: %s", strerror(-r));
115                 }
116         } else
117                 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
118 }
119
120 void link_add_rrs(Link *l, bool force_remove) {
121         LinkAddress *a;
122
123         LIST_FOREACH(addresses, a, l->addresses)
124                 link_address_add_rrs(a, force_remove);
125 }
126
127 int link_update_rtnl(Link *l, sd_rtnl_message *m) {
128         const char *n = NULL;
129         int r;
130
131         assert(l);
132         assert(m);
133
134         r = sd_rtnl_message_link_get_flags(m, &l->flags);
135         if (r < 0)
136                 return r;
137
138         sd_rtnl_message_read_u32(m, IFLA_MTU, &l->mtu);
139
140         if (sd_rtnl_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
141                 strncpy(l->name, n, sizeof(l->name)-1);
142                 char_array_0(l->name);
143         }
144
145         link_allocate_scopes(l);
146         link_add_rrs(l, false);
147
148         return 0;
149 }
150
151 static int link_update_dns_servers(Link *l) {
152         _cleanup_strv_free_ char **nameservers = NULL;
153         char **nameserver;
154         DnsServer *s, *nx;
155         int r;
156
157         assert(l);
158
159         r = sd_network_link_get_dns(l->ifindex, &nameservers);
160         if (r < 0)
161                 goto clear;
162
163         LIST_FOREACH(servers, s, l->dns_servers)
164                 s->marked = true;
165
166         STRV_FOREACH(nameserver, nameservers) {
167                 union in_addr_union a;
168                 int family;
169
170                 r = in_addr_from_string_auto(*nameserver, &family, &a);
171                 if (r < 0)
172                         goto clear;
173
174                 s = link_find_dns_server(l, family, &a);
175                 if (s)
176                         s->marked = false;
177                 else {
178                         r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a);
179                         if (r < 0)
180                                 goto clear;
181                 }
182         }
183
184         LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
185                 if (s->marked)
186                         dns_server_free(s);
187
188         return 0;
189
190 clear:
191         while (l->dns_servers)
192                 dns_server_free(l->dns_servers);
193
194         return r;
195 }
196
197 static int link_update_llmnr_support(Link *l) {
198         _cleanup_free_ char *b = NULL;
199         int r;
200
201         assert(l);
202
203         r = sd_network_link_get_llmnr(l->ifindex, &b);
204         if (r < 0)
205                 goto clear;
206
207         r = parse_boolean(b);
208         if (r < 0) {
209                 if (streq(b, "resolve"))
210                         l->llmnr_support = SUPPORT_RESOLVE;
211                 else
212                         goto clear;
213
214         } else if (r > 0)
215                 l->llmnr_support = SUPPORT_YES;
216         else
217                 l->llmnr_support = SUPPORT_NO;
218
219         return 0;
220
221 clear:
222         l->llmnr_support = SUPPORT_YES;
223         return r;
224 }
225
226 int link_update_monitor(Link *l) {
227         assert(l);
228
229         link_update_dns_servers(l);
230         link_update_llmnr_support(l);
231         link_allocate_scopes(l);
232         link_add_rrs(l, false);
233
234         return 0;
235 }
236
237 bool link_relevant(Link *l, int family) {
238         _cleanup_free_ char *state = NULL;
239         LinkAddress *a;
240
241         assert(l);
242
243         /* A link is relevant if it isn't a loopback or pointopoint
244          * device, has a link beat, can do multicast and has at least
245          * one relevant IP address */
246
247         if (l->flags & (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_DORMANT))
248                 return false;
249
250         if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST))
251                 return false;
252
253         sd_network_link_get_operational_state(l->ifindex, &state);
254         if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
255                 return false;
256
257         LIST_FOREACH(addresses, a, l->addresses)
258                 if (a->family == family && link_address_relevant(a))
259                         return true;
260
261         return false;
262 }
263
264 LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
265         LinkAddress *a;
266
267         assert(l);
268
269         LIST_FOREACH(addresses, a, l->addresses)
270                 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
271                         return a;
272
273         return NULL;
274 }
275
276 DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
277         DnsServer *s;
278
279         assert(l);
280
281         LIST_FOREACH(servers, s, l->dns_servers)
282                 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
283                         return s;
284         return NULL;
285 }
286
287 DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
288         assert(l);
289
290         if (l->current_dns_server == s)
291                 return s;
292
293         if (s) {
294                 _cleanup_free_ char *ip = NULL;
295
296                 in_addr_to_string(s->family, &s->address, &ip);
297                 log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name);
298         }
299
300         l->current_dns_server = s;
301
302         if (l->unicast_scope)
303                 dns_cache_flush(&l->unicast_scope->cache);
304
305         return s;
306 }
307
308 DnsServer *link_get_dns_server(Link *l) {
309         assert(l);
310
311         if (!l->current_dns_server)
312                 link_set_dns_server(l, l->dns_servers);
313
314         return l->current_dns_server;
315 }
316
317 void link_next_dns_server(Link *l) {
318         assert(l);
319
320         if (!l->current_dns_server)
321                 return;
322
323         if (l->current_dns_server->servers_next) {
324                 link_set_dns_server(l, l->current_dns_server->servers_next);
325                 return;
326         }
327
328         link_set_dns_server(l, l->dns_servers);
329 }
330
331 int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
332         LinkAddress *a;
333
334         assert(l);
335         assert(in_addr);
336
337         a = new0(LinkAddress, 1);
338         if (!a)
339                 return -ENOMEM;
340
341         a->family = family;
342         a->in_addr = *in_addr;
343
344         a->link = l;
345         LIST_PREPEND(addresses, l->addresses, a);
346
347         if (ret)
348                 *ret = a;
349
350         return 0;
351 }
352
353 LinkAddress *link_address_free(LinkAddress *a) {
354         if (!a)
355                 return NULL;
356
357         if (a->link) {
358                 LIST_REMOVE(addresses, a->link->addresses, a);
359
360                 if (a->llmnr_address_rr) {
361                         if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
362                                 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
363                         else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
364                                 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
365                 }
366
367                 if (a->llmnr_ptr_rr) {
368                         if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
369                                 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
370                         else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
371                                 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
372                 }
373         }
374
375         dns_resource_record_unref(a->llmnr_address_rr);
376         dns_resource_record_unref(a->llmnr_ptr_rr);
377
378         free(a);
379         return NULL;
380 }
381
382 void link_address_add_rrs(LinkAddress *a, bool force_remove) {
383         int r;
384
385         assert(a);
386
387         if (a->family == AF_INET) {
388
389                 if (!force_remove &&
390                     link_address_relevant(a) &&
391                     a->link->llmnr_ipv4_scope &&
392                     a->link->llmnr_support == SUPPORT_YES &&
393                     a->link->manager->llmnr_support == SUPPORT_YES) {
394
395                         if (!a->link->manager->host_ipv4_key) {
396                                 a->link->manager->host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->hostname);
397                                 if (!a->link->manager->host_ipv4_key) {
398                                         r = -ENOMEM;
399                                         goto fail;
400                                 }
401                         }
402
403                         if (!a->llmnr_address_rr) {
404                                 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv4_key);
405                                 if (!a->llmnr_address_rr) {
406                                         r = -ENOMEM;
407                                         goto fail;
408                                 }
409
410                                 a->llmnr_address_rr->a.in_addr = a->in_addr.in;
411                                 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
412                         }
413
414                         if (!a->llmnr_ptr_rr) {
415                                 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
416                                 if (r < 0)
417                                         goto fail;
418
419                                 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
420                         }
421
422                         r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
423                         if (r < 0)
424                                 log_warning("Failed to add A record to LLMNR zone: %s", strerror(-r));
425
426                         r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
427                         if (r < 0)
428                                 log_warning("Failed to add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
429                 } else {
430                         if (a->llmnr_address_rr) {
431                                 if (a->link->llmnr_ipv4_scope)
432                                         dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
433                                 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
434                         }
435
436                         if (a->llmnr_ptr_rr) {
437                                 if (a->link->llmnr_ipv4_scope)
438                                         dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
439                                 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
440                         }
441                 }
442         }
443
444         if (a->family == AF_INET6) {
445
446                 if (!force_remove &&
447                     link_address_relevant(a) &&
448                     a->link->llmnr_ipv6_scope &&
449                     a->link->llmnr_support == SUPPORT_YES &&
450                     a->link->manager->llmnr_support == SUPPORT_YES) {
451
452                         if (!a->link->manager->host_ipv6_key) {
453                                 a->link->manager->host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->hostname);
454                                 if (!a->link->manager->host_ipv6_key) {
455                                         r = -ENOMEM;
456                                         goto fail;
457                                 }
458                         }
459
460                         if (!a->llmnr_address_rr) {
461                                 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv6_key);
462                                 if (!a->llmnr_address_rr) {
463                                         r = -ENOMEM;
464                                         goto fail;
465                                 }
466
467                                 a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
468                                 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
469                         }
470
471                         if (!a->llmnr_ptr_rr) {
472                                 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
473                                 if (r < 0)
474                                         goto fail;
475
476                                 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
477                         }
478
479                         r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
480                         if (r < 0)
481                                 log_warning("Failed to add AAAA record to LLMNR zone: %s", strerror(-r));
482
483                         r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
484                         if (r < 0)
485                                 log_warning("Failed to add IPv6 PTR record to LLMNR zone: %s", strerror(-r));
486                 } else {
487                         if (a->llmnr_address_rr) {
488                                 if (a->link->llmnr_ipv6_scope)
489                                         dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
490                                 a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
491                         }
492
493                         if (a->llmnr_ptr_rr) {
494                                 if (a->link->llmnr_ipv6_scope)
495                                         dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
496                                 a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
497                         }
498                 }
499         }
500
501         return;
502
503 fail:
504         log_debug("Failed to update address RRs: %s", strerror(-r));
505 }
506
507 int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
508         int r;
509         assert(a);
510         assert(m);
511
512         r = sd_rtnl_message_addr_get_flags(m, &a->flags);
513         if (r < 0)
514                 return r;
515
516         sd_rtnl_message_addr_get_scope(m, &a->scope);
517
518         link_allocate_scopes(a->link);
519         link_add_rrs(a->link, false);
520
521         return 0;
522 }
523
524 bool link_address_relevant(LinkAddress *a) {
525         assert(a);
526
527         if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE))
528                 return false;
529
530         if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
531                 return false;
532
533         return true;
534 }