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