chiark / gitweb /
43f60d8bb6b5cff67a037315eb89909c9df05236
[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 "dhcp-lease-internal.h"
26 #include "strv.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->dhcp_dns_servers)
75                 dns_server_free(l->dhcp_dns_servers);
76
77         while (l->link_dns_servers)
78                 dns_server_free(l->link_dns_servers);
79
80         free(l);
81         return NULL;
82 }
83
84 static void link_allocate_scopes(Link *l) {
85         int r;
86
87         assert(l);
88
89         if (l->link_dns_servers || l->dhcp_dns_servers) {
90                 if (!l->unicast_scope) {
91                         r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
92                         if (r < 0)
93                                 log_warning("Failed to allocate DNS scope: %s", strerror(-r));
94                 }
95         } else
96                 l->unicast_scope = dns_scope_free(l->unicast_scope);
97
98         if (link_relevant(l, AF_INET) && l->manager->use_llmnr) {
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) && l->manager->use_llmnr) {
108                 if (!l->llmnr_ipv6_scope) {
109                         r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
110                         if (r < 0)
111                                 log_warning("Failed to allocate LLMNR IPv6 scope: %s", strerror(-r));
112                 }
113         } else
114                 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
115 }
116
117 int link_update_rtnl(Link *l, sd_rtnl_message *m) {
118         const char *n = NULL;
119         int r;
120
121         assert(l);
122         assert(m);
123
124         r = sd_rtnl_message_link_get_flags(m, &l->flags);
125         if (r < 0)
126                 return r;
127
128         sd_rtnl_message_read_u32(m, IFLA_MTU, &l->mtu);
129
130         if (sd_rtnl_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
131                 strncpy(l->name, n, sizeof(l->name));
132                 char_array_0(l->name);
133         }
134
135         link_allocate_scopes(l);
136         return 0;
137 }
138
139 static int link_update_link_dns_servers(Link *l) {
140         _cleanup_free_ struct in_addr *nameservers = NULL;
141         _cleanup_free_ struct in6_addr *nameservers6 = NULL;
142         DnsServer *s, *nx;
143         int r, n, i;
144
145         assert(l);
146
147         LIST_FOREACH(servers, s, l->link_dns_servers)
148                 s->marked = true;
149
150         n = sd_network_get_dns(l->ifindex, &nameservers);
151         if (n < 0) {
152                 r = n;
153                 goto clear;
154         }
155
156         for (i = 0; i < n; i++) {
157                 union in_addr_union a = { .in = nameservers[i] };
158
159                 s = link_find_dns_server(l, DNS_SERVER_LINK, AF_INET, &a);
160                 if (s)
161                         s->marked = false;
162                 else {
163                         r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, AF_INET, &a);
164                         if (r < 0)
165                                 goto clear;
166                 }
167         }
168
169         n = sd_network_get_dns6(l->ifindex, &nameservers6);
170         if (n < 0) {
171                 r = n;
172                 goto clear;
173         }
174
175         for (i = 0; i < n; i++) {
176                 union in_addr_union a = { .in6 = nameservers6[i] };
177
178                 s = link_find_dns_server(l, DNS_SERVER_LINK, AF_INET6, &a);
179                 if (s)
180                         s->marked = false;
181                 else {
182                         r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, AF_INET6, &a);
183                         if (r < 0)
184                                 goto clear;
185                 }
186         }
187
188         LIST_FOREACH_SAFE(servers, s, nx, l->link_dns_servers)
189                 if (s->marked)
190                         dns_server_free(s);
191
192         return 0;
193
194 clear:
195         while (l->link_dns_servers)
196                 dns_server_free(l->link_dns_servers);
197
198         return r;
199 }
200
201 int link_update_monitor(Link *l) {
202         assert(l);
203
204         link_update_link_dns_servers(l);
205         link_allocate_scopes(l);
206
207         return 0;
208 }
209
210 bool link_relevant(Link *l, int family) {
211         _cleanup_free_ char *state = NULL;
212         LinkAddress *a;
213
214         assert(l);
215
216         /* A link is relevant if it isn't a loopback device and has at
217          * least one relevant IP address */
218
219         if (l->flags & IFF_LOOPBACK)
220                 return false;
221
222         sd_network_get_link_operational_state(l->ifindex, &state);
223         if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
224                 return false;
225
226         LIST_FOREACH(addresses, a, l->addresses)
227                 if (a->family == family && link_address_relevant(a))
228                         return true;
229
230         return false;
231 }
232
233 LinkAddress *link_find_address(Link *l, int family, union in_addr_union *in_addr) {
234         LinkAddress *a;
235
236         assert(l);
237
238         LIST_FOREACH(addresses, a, l->addresses)
239                 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
240                         return a;
241
242         return NULL;
243 }
244
245 DnsServer* link_find_dns_server(Link *l, DnsServerSource source, int family, union in_addr_union *in_addr) {
246         DnsServer *first, *s;
247
248         assert(l);
249
250         first = source == DNS_SERVER_DHCP ? l->dhcp_dns_servers : l->link_dns_servers;
251
252         LIST_FOREACH(servers, s, first)
253                 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
254                         return s;
255
256         return NULL;
257 }
258
259 DnsServer *link_get_dns_server(Link *l) {
260         assert(l);
261
262         if (!l->current_dns_server)
263                 l->current_dns_server = l->link_dns_servers;
264         if (!l->current_dns_server)
265                 l->current_dns_server = l->dhcp_dns_servers;
266
267         return l->current_dns_server;
268 }
269
270 void link_next_dns_server(Link *l) {
271         assert(l);
272
273         /* Switch to the next DNS server */
274
275         if (!l->current_dns_server) {
276                 l->current_dns_server = l->link_dns_servers;
277                 if (l->current_dns_server)
278                         return;
279         }
280
281         if (!l->current_dns_server) {
282                 l->current_dns_server = l->dhcp_dns_servers;
283                 if (l->current_dns_server)
284                         return;
285         }
286
287         if (!l->current_dns_server)
288                 return;
289
290         if (l->current_dns_server->servers_next) {
291                 l->current_dns_server = l->current_dns_server->servers_next;
292                 return;
293         }
294
295         if (l->current_dns_server->source == DNS_SERVER_LINK)
296                 l->current_dns_server = l->dhcp_dns_servers;
297         else {
298                 assert(l->current_dns_server->source == DNS_SERVER_DHCP);
299                 l->current_dns_server = l->link_dns_servers;
300         }
301 }
302
303 int link_address_new(Link *l, LinkAddress **ret, int family, union in_addr_union *in_addr) {
304         LinkAddress *a;
305
306         assert(l);
307         assert(in_addr);
308
309         a = new0(LinkAddress, 1);
310         if (!a)
311                 return -ENOMEM;
312
313         a->family = family;
314         a->in_addr = *in_addr;
315
316         a->link = l;
317         LIST_PREPEND(addresses, l->addresses, a);
318
319         if (ret)
320                 *ret = a;
321
322         return 0;
323 }
324
325 LinkAddress *link_address_free(LinkAddress *a) {
326         if (!a)
327                 return NULL;
328
329         if (a->link)
330                 LIST_REMOVE(addresses, a->link->addresses, a);
331
332         free(a);
333         return NULL;
334 }
335
336 int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
337         int r;
338         assert(a);
339         assert(m);
340
341         r = sd_rtnl_message_addr_get_flags(m, &a->flags);
342         if (r < 0)
343                 return r;
344
345         sd_rtnl_message_addr_get_scope(m, &a->scope);
346
347         link_allocate_scopes(a->link);
348         return 0;
349 }
350
351 bool link_address_relevant(LinkAddress *a) {
352         assert(a);
353
354         if (a->flags & IFA_F_DEPRECATED)
355                 return false;
356
357         if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
358                 return false;
359
360         return true;
361 }