chiark / gitweb /
a4c23bab06a69b2b7d4b9ac14a84ad38ebb33b6d
[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 "resolved-link.h"
27
28 static void link_address_add_rrs(LinkAddress *a);
29
30 int link_new(Manager *m, Link **ret, int ifindex) {
31         _cleanup_(link_freep) Link *l = NULL;
32         int r;
33
34         assert(m);
35         assert(ifindex > 0);
36
37         r = hashmap_ensure_allocated(&m->links, NULL, NULL);
38         if (r < 0)
39                 return r;
40
41         l = new0(Link, 1);
42         if (!l)
43                 return -ENOMEM;
44
45         l->ifindex = ifindex;
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) && (l->flags & IFF_MULTICAST) && l->manager->use_llmnr) {
97                 if (!l->llmnr_ipv4_scope) {
98                         r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
99                         if (r < 0)
100                                 log_warning("Failed to allocate LLMNR IPv4 scope: %s", strerror(-r));
101                 }
102         } else
103                 l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
104
105         if (link_relevant(l, AF_INET6) && (l->flags & IFF_MULTICAST) && l->manager->use_llmnr) {
106                 if (!l->llmnr_ipv6_scope) {
107                         r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
108                         if (r < 0)
109                                 log_warning("Failed to allocate LLMNR IPv6 scope: %s", strerror(-r));
110                 }
111         } else
112                 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
113 }
114
115 static void link_add_rrs(Link *l) {
116         LinkAddress *a;
117
118         LIST_FOREACH(addresses, a, l->addresses)
119                 link_address_add_rrs(a);
120 }
121
122 int link_update_rtnl(Link *l, sd_rtnl_message *m) {
123         const char *n = NULL;
124         int r;
125
126         assert(l);
127         assert(m);
128
129         r = sd_rtnl_message_link_get_flags(m, &l->flags);
130         if (r < 0)
131                 return r;
132
133         sd_rtnl_message_read_u32(m, IFLA_MTU, &l->mtu);
134
135         if (sd_rtnl_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
136                 strncpy(l->name, n, sizeof(l->name));
137                 char_array_0(l->name);
138         }
139
140         link_allocate_scopes(l);
141         link_add_rrs(l);
142
143         return 0;
144 }
145
146 static int link_update_dns_servers(Link *l) {
147         _cleanup_strv_free_ char **nameservers = NULL;
148         char **nameserver;
149         DnsServer *s, *nx;
150         int r;
151
152         assert(l);
153
154         LIST_FOREACH(servers, s, l->dns_servers)
155                 s->marked = true;
156
157         r = sd_network_get_dns(l->ifindex, &nameservers);
158         if (r < 0)
159                 goto clear;
160
161         STRV_FOREACH(nameserver, nameservers) {
162                 union in_addr_union a;
163                 int family;
164
165                 r = in_addr_from_string_auto(*nameserver, &family, &a);
166                 if (r < 0)
167                         goto clear;
168
169                 s = link_find_dns_server(l, family, &a);
170                 if (s)
171                         s->marked = false;
172                 else {
173                         r = dns_server_new(l->manager, NULL, l, family, &a);
174                         if (r < 0)
175                                 goto clear;
176                 }
177         }
178
179         LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
180                 if (s->marked)
181                         dns_server_free(s);
182
183         return 0;
184
185 clear:
186         while (l->dns_servers)
187                 dns_server_free(l->dns_servers);
188
189         return r;
190 }
191
192 int link_update_monitor(Link *l) {
193         assert(l);
194
195         link_update_dns_servers(l);
196         link_allocate_scopes(l);
197         link_add_rrs(l);
198
199         return 0;
200 }
201
202 bool link_relevant(Link *l, int family) {
203         _cleanup_free_ char *state = NULL;
204         LinkAddress *a;
205
206         assert(l);
207
208         /* A link is relevant if it isn't a loopback device and has at
209          * least one relevant IP address */
210
211         if (l->flags & IFF_LOOPBACK)
212                 return false;
213
214         sd_network_get_link_operational_state(l->ifindex, &state);
215         if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
216                 return false;
217
218         LIST_FOREACH(addresses, a, l->addresses)
219                 if (a->family == family && link_address_relevant(a))
220                         return true;
221
222         return false;
223 }
224
225 LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
226         LinkAddress *a;
227
228         assert(l);
229
230         LIST_FOREACH(addresses, a, l->addresses)
231                 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
232                         return a;
233
234         return NULL;
235 }
236
237 DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
238         DnsServer *s;
239
240         assert(l);
241
242         LIST_FOREACH(servers, s, l->dns_servers)
243                 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
244                         return s;
245         return NULL;
246 }
247
248 DnsServer *link_get_dns_server(Link *l) {
249         assert(l);
250
251         if (!l->current_dns_server)
252                 l->current_dns_server = l->dns_servers;
253
254         return l->current_dns_server;
255 }
256
257 void link_next_dns_server(Link *l) {
258         assert(l);
259
260         /* Switch to the next DNS server */
261
262         if (!l->current_dns_server) {
263                 l->current_dns_server = l->dns_servers;
264                 if (l->current_dns_server)
265                         return;
266         }
267
268         if (!l->current_dns_server)
269                 return;
270
271         if (l->current_dns_server->servers_next) {
272                 l->current_dns_server = l->current_dns_server->servers_next;
273                 return;
274         }
275
276         l->current_dns_server = l->dns_servers;
277 }
278
279 int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
280         LinkAddress *a;
281
282         assert(l);
283         assert(in_addr);
284
285         a = new0(LinkAddress, 1);
286         if (!a)
287                 return -ENOMEM;
288
289         a->family = family;
290         a->in_addr = *in_addr;
291
292         a->link = l;
293         LIST_PREPEND(addresses, l->addresses, a);
294
295         if (ret)
296                 *ret = a;
297
298         return 0;
299 }
300
301 LinkAddress *link_address_free(LinkAddress *a) {
302         if (!a)
303                 return NULL;
304
305         if (a->link) {
306                 LIST_REMOVE(addresses, a->link->addresses, a);
307
308                 if (a->llmnr_address_rr) {
309
310                         if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
311                                 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
312                         else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
313                                 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
314
315                         dns_resource_record_unref(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                         dns_resource_record_unref(a->llmnr_ptr_rr);
325                 }
326         }
327
328         free(a);
329         return NULL;
330 }
331
332 static void link_address_add_rrs(LinkAddress *a) {
333         int r;
334
335         assert(a);
336
337         if (a->family == AF_INET && a->link->llmnr_ipv4_scope) {
338
339                 if (!a->link->manager->host_ipv4_key) {
340                         a->link->manager->host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->hostname);
341                         if (!a->link->manager->host_ipv4_key) {
342                                 r = -ENOMEM;
343                                 goto fail;
344                         }
345                 }
346
347                 if (!a->llmnr_address_rr) {
348                         a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv4_key);
349                         if (!a->llmnr_address_rr) {
350                                 r = -ENOMEM;
351                                 goto fail;
352                         }
353
354                         a->llmnr_address_rr->a.in_addr = a->in_addr.in;
355                         a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
356                 }
357
358                 if (!a->llmnr_ptr_rr) {
359                         r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
360                         if (r < 0)
361                                 goto fail;
362
363                         a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
364                 }
365
366                 if (link_address_relevant(a)) {
367                         r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
368                         if (r < 0)
369                                 goto fail;
370
371                         r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
372                         if (r < 0)
373                                 goto fail;
374                 } else {
375                         dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
376                         dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
377                 }
378         }
379
380         if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope) {
381
382                 if (!a->link->manager->host_ipv6_key) {
383                         a->link->manager->host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->hostname);
384                         if (!a->link->manager->host_ipv6_key) {
385                                 r = -ENOMEM;
386                                 goto fail;
387                         }
388                 }
389
390                 if (!a->llmnr_address_rr) {
391                         a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv6_key);
392                         if (!a->llmnr_address_rr) {
393                                 r = -ENOMEM;
394                                 goto fail;
395                         }
396
397                         a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
398                         a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
399                 }
400
401                 if (!a->llmnr_ptr_rr) {
402                         r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
403                         if (r < 0)
404                                 goto fail;
405
406                         a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
407                 }
408
409                 if (link_address_relevant(a)) {
410                         r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
411                         if (r < 0)
412                                 goto fail;
413
414                         r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
415                         if (r < 0)
416                                 goto fail;
417                 } else {
418                         dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
419                         dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
420                 }
421         }
422
423         return;
424
425 fail:
426         log_debug("Failed to update address RRs: %s", strerror(-r));
427 }
428
429 int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
430         int r;
431         assert(a);
432         assert(m);
433
434         r = sd_rtnl_message_addr_get_flags(m, &a->flags);
435         if (r < 0)
436                 return r;
437
438         sd_rtnl_message_addr_get_scope(m, &a->scope);
439
440         link_allocate_scopes(a->link);
441         link_add_rrs(a->link);
442
443         return 0;
444 }
445
446 bool link_address_relevant(LinkAddress *a) {
447         assert(a);
448
449         if (a->flags & IFA_F_DEPRECATED)
450                 return false;
451
452         if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
453                 return false;
454
455         return true;
456 }