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