chiark / gitweb /
resolved: don't read DHCP leases
[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 int link_new(Manager *m, Link **ret, int ifindex) {
29         _cleanup_(link_freep) Link *l = NULL;
30         int r;
31
32         assert(m);
33         assert(ifindex > 0);
34
35         r = hashmap_ensure_allocated(&m->links, NULL, NULL);
36         if (r < 0)
37                 return r;
38
39         l = new0(Link, 1);
40         if (!l)
41                 return -ENOMEM;
42
43         l->ifindex = ifindex;
44
45         r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
46         if (r < 0)
47                 return r;
48
49         l->manager = m;
50
51         if (ret)
52                 *ret = l;
53         l = NULL;
54
55         return 0;
56 }
57
58 Link *link_free(Link *l) {
59
60         if (!l)
61                 return NULL;
62
63         while (l->addresses)
64                 link_address_free(l->addresses);
65
66         if (l->manager)
67                 hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
68
69         dns_scope_free(l->unicast_scope);
70         dns_scope_free(l->llmnr_ipv4_scope);
71         dns_scope_free(l->llmnr_ipv6_scope);
72
73         while (l->dns_servers)
74                 dns_server_free(l->dns_servers);
75
76         free(l);
77         return NULL;
78 }
79
80 static void link_allocate_scopes(Link *l) {
81         int r;
82
83         assert(l);
84
85         if (l->dns_servers) {
86                 if (!l->unicast_scope) {
87                         r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
88                         if (r < 0)
89                                 log_warning("Failed to allocate DNS scope: %s", strerror(-r));
90                 }
91         } else
92                 l->unicast_scope = dns_scope_free(l->unicast_scope);
93
94         if (link_relevant(l, AF_INET) && l->manager->use_llmnr) {
95                 if (!l->llmnr_ipv4_scope) {
96                         r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
97                         if (r < 0)
98                                 log_warning("Failed to allocate LLMNR IPv4 scope: %s", strerror(-r));
99                 }
100         } else
101                 l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
102
103         if (link_relevant(l, AF_INET6) && l->manager->use_llmnr) {
104                 if (!l->llmnr_ipv6_scope) {
105                         r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
106                         if (r < 0)
107                                 log_warning("Failed to allocate LLMNR IPv6 scope: %s", strerror(-r));
108                 }
109         } else
110                 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
111 }
112
113 int link_update_rtnl(Link *l, sd_rtnl_message *m) {
114         const char *n = NULL;
115         int r;
116
117         assert(l);
118         assert(m);
119
120         r = sd_rtnl_message_link_get_flags(m, &l->flags);
121         if (r < 0)
122                 return r;
123
124         sd_rtnl_message_read_u32(m, IFLA_MTU, &l->mtu);
125
126         if (sd_rtnl_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
127                 strncpy(l->name, n, sizeof(l->name));
128                 char_array_0(l->name);
129         }
130
131         link_allocate_scopes(l);
132         return 0;
133 }
134
135 static int link_update_dns_servers(Link *l) {
136         _cleanup_free_ struct in_addr *nameservers = NULL;
137         _cleanup_free_ struct in6_addr *nameservers6 = NULL;
138         DnsServer *s, *nx;
139         int r, n, i;
140
141         assert(l);
142
143         LIST_FOREACH(servers, s, l->dns_servers)
144                 s->marked = true;
145
146         n = sd_network_get_dns(l->ifindex, &nameservers);
147         if (n < 0) {
148                 r = n;
149                 goto clear;
150         }
151
152         for (i = 0; i < n; i++) {
153                 union in_addr_union a = { .in = nameservers[i] };
154
155                 s = link_find_dns_server(l, AF_INET, &a);
156                 if (s)
157                         s->marked = false;
158                 else {
159                         r = dns_server_new(l->manager, NULL, l, AF_INET, &a);
160                         if (r < 0)
161                                 goto clear;
162                 }
163         }
164
165         n = sd_network_get_dns6(l->ifindex, &nameservers6);
166         if (n < 0) {
167                 r = n;
168                 goto clear;
169         }
170
171         for (i = 0; i < n; i++) {
172                 union in_addr_union a = { .in6 = nameservers6[i] };
173
174                 s = link_find_dns_server(l, AF_INET6, &a);
175                 if (s)
176                         s->marked = false;
177                 else {
178                         r = dns_server_new(l->manager, NULL, l, AF_INET6, &a);
179                         if (r < 0)
180                                 goto clear;
181                 }
182         }
183
184         LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
185                 if (s->marked)
186                         dns_server_free(s);
187
188         return 0;
189
190 clear:
191         while (l->dns_servers)
192                 dns_server_free(l->dns_servers);
193
194         return r;
195 }
196
197 int link_update_monitor(Link *l) {
198         assert(l);
199
200         link_update_dns_servers(l);
201         link_allocate_scopes(l);
202
203         return 0;
204 }
205
206 bool link_relevant(Link *l, int family) {
207         _cleanup_free_ char *state = NULL;
208         LinkAddress *a;
209
210         assert(l);
211
212         /* A link is relevant if it isn't a loopback device and has at
213          * least one relevant IP address */
214
215         if (l->flags & IFF_LOOPBACK)
216                 return false;
217
218         sd_network_get_link_operational_state(l->ifindex, &state);
219         if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
220                 return false;
221
222         LIST_FOREACH(addresses, a, l->addresses)
223                 if (a->family == family && link_address_relevant(a))
224                         return true;
225
226         return false;
227 }
228
229 LinkAddress *link_find_address(Link *l, int family, union in_addr_union *in_addr) {
230         LinkAddress *a;
231
232         assert(l);
233
234         LIST_FOREACH(addresses, a, l->addresses)
235                 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
236                         return a;
237
238         return NULL;
239 }
240
241 DnsServer* link_find_dns_server(Link *l, int family, union in_addr_union *in_addr) {
242         DnsServer *s;
243
244         assert(l);
245
246         LIST_FOREACH(servers, s, l->dns_servers)
247                 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
248                         return s;
249
250         return NULL;
251 }
252
253 DnsServer *link_get_dns_server(Link *l) {
254         assert(l);
255
256         if (!l->current_dns_server)
257                 l->current_dns_server = l->dns_servers;
258
259         return l->current_dns_server;
260 }
261
262 void link_next_dns_server(Link *l) {
263         assert(l);
264
265         /* Switch to the next DNS server */
266
267         if (!l->current_dns_server) {
268                 l->current_dns_server = l->dns_servers;
269                 if (l->current_dns_server)
270                         return;
271         }
272
273         if (!l->current_dns_server)
274                 return;
275
276         if (l->current_dns_server->servers_next) {
277                 l->current_dns_server = l->current_dns_server->servers_next;
278                 return;
279         }
280
281         l->current_dns_server = l->dns_servers;
282 }
283
284 int link_address_new(Link *l, LinkAddress **ret, int family, union in_addr_union *in_addr) {
285         LinkAddress *a;
286
287         assert(l);
288         assert(in_addr);
289
290         a = new0(LinkAddress, 1);
291         if (!a)
292                 return -ENOMEM;
293
294         a->family = family;
295         a->in_addr = *in_addr;
296
297         a->link = l;
298         LIST_PREPEND(addresses, l->addresses, a);
299
300         if (ret)
301                 *ret = a;
302
303         return 0;
304 }
305
306 LinkAddress *link_address_free(LinkAddress *a) {
307         if (!a)
308                 return NULL;
309
310         if (a->link)
311                 LIST_REMOVE(addresses, a->link->addresses, a);
312
313         free(a);
314         return NULL;
315 }
316
317 int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
318         int r;
319         assert(a);
320         assert(m);
321
322         r = sd_rtnl_message_addr_get_flags(m, &a->flags);
323         if (r < 0)
324                 return r;
325
326         sd_rtnl_message_addr_get_scope(m, &a->scope);
327
328         link_allocate_scopes(a->link);
329         return 0;
330 }
331
332 bool link_address_relevant(LinkAddress *a) {
333         assert(a);
334
335         if (a->flags & IFA_F_DEPRECATED)
336                 return false;
337
338         if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
339                 return false;
340
341         return true;
342 }