chiark / gitweb /
change type for address family to "int"
[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_dhcp_dns_servers(Link *l) {
140         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
141         const struct in_addr *nameservers = NULL;
142         DnsServer *s, *nx;
143         int r, n, i;
144
145         assert(l);
146
147         r = sd_network_dhcp_use_dns(l->ifindex);
148         if (r <= 0)
149                 goto clear;
150
151         r = sd_network_get_dhcp_lease(l->ifindex, &lease);
152         if (r < 0)
153                 goto clear;
154
155         LIST_FOREACH(servers, s, l->dhcp_dns_servers)
156                 s->marked = true;
157
158         n = sd_dhcp_lease_get_dns(lease, &nameservers);
159         if (n < 0) {
160                 r = n;
161                 goto clear;
162         }
163
164         for (i = 0; i < n; i++) {
165                 union in_addr_union a = { .in = nameservers[i] };
166
167                 s = link_find_dns_server(l, DNS_SERVER_DHCP, AF_INET, &a);
168                 if (s)
169                         s->marked = false;
170                 else {
171                         r = dns_server_new(l->manager, NULL, DNS_SERVER_DHCP, l, AF_INET, &a);
172                         if (r < 0)
173                                 goto clear;
174                 }
175         }
176
177         LIST_FOREACH_SAFE(servers, s, nx, l->dhcp_dns_servers)
178                 if (s->marked)
179                         dns_server_free(s);
180
181         return 0;
182
183 clear:
184         while (l->dhcp_dns_servers)
185                 dns_server_free(l->dhcp_dns_servers);
186
187         return r;
188 }
189
190 static int link_update_link_dns_servers(Link *l) {
191         _cleanup_free_ struct in_addr *nameservers = NULL;
192         _cleanup_free_ struct in6_addr *nameservers6 = NULL;
193         DnsServer *s, *nx;
194         int r, n, i;
195
196         assert(l);
197
198         LIST_FOREACH(servers, s, l->link_dns_servers)
199                 s->marked = true;
200
201         n = sd_network_get_dns(l->ifindex, &nameservers);
202         if (n < 0) {
203                 r = n;
204                 goto clear;
205         }
206
207         for (i = 0; i < n; i++) {
208                 union in_addr_union a = { .in = nameservers[i] };
209
210                 s = link_find_dns_server(l, DNS_SERVER_LINK, AF_INET, &a);
211                 if (s)
212                         s->marked = false;
213                 else {
214                         r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, AF_INET, &a);
215                         if (r < 0)
216                                 goto clear;
217                 }
218         }
219
220         n = sd_network_get_dns6(l->ifindex, &nameservers6);
221         if (n < 0) {
222                 r = n;
223                 goto clear;
224         }
225
226         for (i = 0; i < n; i++) {
227                 union in_addr_union a = { .in6 = nameservers6[i] };
228
229                 s = link_find_dns_server(l, DNS_SERVER_LINK, AF_INET6, &a);
230                 if (s)
231                         s->marked = false;
232                 else {
233                         r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, AF_INET6, &a);
234                         if (r < 0)
235                                 goto clear;
236                 }
237         }
238
239         LIST_FOREACH_SAFE(servers, s, nx, l->link_dns_servers)
240                 if (s->marked)
241                         dns_server_free(s);
242
243         return 0;
244
245 clear:
246         while (l->link_dns_servers)
247                 dns_server_free(l->link_dns_servers);
248
249         return r;
250 }
251
252 int link_update_monitor(Link *l) {
253         assert(l);
254
255         link_update_dhcp_dns_servers(l);
256         link_update_link_dns_servers(l);
257         link_allocate_scopes(l);
258
259         return 0;
260 }
261
262 bool link_relevant(Link *l, int family) {
263         _cleanup_free_ char *state = NULL;
264         LinkAddress *a;
265
266         assert(l);
267
268         /* A link is relevant if it isn't a loopback device and has at
269          * least one relevant IP address */
270
271         if (l->flags & IFF_LOOPBACK)
272                 return false;
273
274         sd_network_get_link_operational_state(l->ifindex, &state);
275         if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
276                 return false;
277
278         LIST_FOREACH(addresses, a, l->addresses)
279                 if (a->family == family && link_address_relevant(a))
280                         return true;
281
282         return false;
283 }
284
285 LinkAddress *link_find_address(Link *l, int family, union in_addr_union *in_addr) {
286         LinkAddress *a;
287
288         assert(l);
289
290         LIST_FOREACH(addresses, a, l->addresses)
291                 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
292                         return a;
293
294         return NULL;
295 }
296
297 DnsServer* link_find_dns_server(Link *l, DnsServerSource source, int family, union in_addr_union *in_addr) {
298         DnsServer *first, *s;
299
300         assert(l);
301
302         first = source == DNS_SERVER_DHCP ? l->dhcp_dns_servers : l->link_dns_servers;
303
304         LIST_FOREACH(servers, s, first)
305                 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
306                         return s;
307
308         return NULL;
309 }
310
311 DnsServer *link_get_dns_server(Link *l) {
312         assert(l);
313
314         if (!l->current_dns_server)
315                 l->current_dns_server = l->link_dns_servers;
316         if (!l->current_dns_server)
317                 l->current_dns_server = l->dhcp_dns_servers;
318
319         return l->current_dns_server;
320 }
321
322 void link_next_dns_server(Link *l) {
323         assert(l);
324
325         /* Switch to the next DNS server */
326
327         if (!l->current_dns_server) {
328                 l->current_dns_server = l->link_dns_servers;
329                 if (l->current_dns_server)
330                         return;
331         }
332
333         if (!l->current_dns_server) {
334                 l->current_dns_server = l->dhcp_dns_servers;
335                 if (l->current_dns_server)
336                         return;
337         }
338
339         if (!l->current_dns_server)
340                 return;
341
342         if (l->current_dns_server->servers_next) {
343                 l->current_dns_server = l->current_dns_server->servers_next;
344                 return;
345         }
346
347         if (l->current_dns_server->source == DNS_SERVER_LINK)
348                 l->current_dns_server = l->dhcp_dns_servers;
349         else {
350                 assert(l->current_dns_server->source == DNS_SERVER_DHCP);
351                 l->current_dns_server = l->link_dns_servers;
352         }
353 }
354
355 int link_address_new(Link *l, LinkAddress **ret, int family, union in_addr_union *in_addr) {
356         LinkAddress *a;
357
358         assert(l);
359         assert(in_addr);
360
361         a = new0(LinkAddress, 1);
362         if (!a)
363                 return -ENOMEM;
364
365         a->family = family;
366         a->in_addr = *in_addr;
367
368         a->link = l;
369         LIST_PREPEND(addresses, l->addresses, a);
370
371         if (ret)
372                 *ret = a;
373
374         return 0;
375 }
376
377 LinkAddress *link_address_free(LinkAddress *a) {
378         if (!a)
379                 return NULL;
380
381         if (a->link)
382                 LIST_REMOVE(addresses, a->link->addresses, a);
383
384         free(a);
385         return NULL;
386 }
387
388 int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
389         int r;
390         assert(a);
391         assert(m);
392
393         r = sd_rtnl_message_addr_get_flags(m, &a->flags);
394         if (r < 0)
395                 return r;
396
397         sd_rtnl_message_addr_get_scope(m, &a->scope);
398
399         link_allocate_scopes(a->link);
400         return 0;
401 }
402
403 bool link_address_relevant(LinkAddress *a) {
404         assert(a);
405
406         if (a->flags & IFA_F_DEPRECATED)
407                 return false;
408
409         if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
410                 return false;
411
412         return true;
413 }