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