chiark / gitweb /
3c6c757722a0d5203e8485a36918fc93b590d625
[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_strv_free_ char **nameservers = NULL;
137         char **nameserver;
138         DnsServer *s, *nx;
139         int r;
140
141         assert(l);
142
143         LIST_FOREACH(servers, s, l->dns_servers)
144                 s->marked = true;
145
146         r = sd_network_get_dns(l->ifindex, &nameservers);
147         if (r < 0)
148                 goto clear;
149
150         STRV_FOREACH(nameserver, nameservers) {
151                 union in_addr_union a;
152                 int family;
153
154                 r = in_addr_from_string_auto(*nameserver, &family, &a);
155                 if (r < 0)
156                         goto clear;
157
158                 s = link_find_dns_server(l, family, &a);
159                 if (s)
160                         s->marked = false;
161                 else {
162                         r = dns_server_new(l->manager, NULL, l, family, &a);
163                         if (r < 0)
164                                 goto clear;
165                 }
166         }
167
168         LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
169                 if (s->marked)
170                         dns_server_free(s);
171
172         return 0;
173
174 clear:
175         while (l->dns_servers)
176                 dns_server_free(l->dns_servers);
177
178         return r;
179 }
180
181 int link_update_monitor(Link *l) {
182         assert(l);
183
184         link_update_dns_servers(l);
185         link_allocate_scopes(l);
186
187         return 0;
188 }
189
190 bool link_relevant(Link *l, int family) {
191         _cleanup_free_ char *state = NULL;
192         LinkAddress *a;
193
194         assert(l);
195
196         /* A link is relevant if it isn't a loopback device and has at
197          * least one relevant IP address */
198
199         if (l->flags & IFF_LOOPBACK)
200                 return false;
201
202         sd_network_get_link_operational_state(l->ifindex, &state);
203         if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
204                 return false;
205
206         LIST_FOREACH(addresses, a, l->addresses)
207                 if (a->family == family && link_address_relevant(a))
208                         return true;
209
210         return false;
211 }
212
213 LinkAddress *link_find_address(Link *l, int family, union in_addr_union *in_addr) {
214         LinkAddress *a;
215
216         assert(l);
217
218         LIST_FOREACH(addresses, a, l->addresses)
219                 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
220                         return a;
221
222         return NULL;
223 }
224
225 DnsServer* link_find_dns_server(Link *l, int family, union in_addr_union *in_addr) {
226         DnsServer *s;
227
228         assert(l);
229
230         LIST_FOREACH(servers, s, l->dns_servers)
231                 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
232                         return s;
233
234         return NULL;
235 }
236
237 DnsServer *link_get_dns_server(Link *l) {
238         assert(l);
239
240         if (!l->current_dns_server)
241                 l->current_dns_server = l->dns_servers;
242
243         return l->current_dns_server;
244 }
245
246 void link_next_dns_server(Link *l) {
247         assert(l);
248
249         /* Switch to the next DNS server */
250
251         if (!l->current_dns_server) {
252                 l->current_dns_server = l->dns_servers;
253                 if (l->current_dns_server)
254                         return;
255         }
256
257         if (!l->current_dns_server)
258                 return;
259
260         if (l->current_dns_server->servers_next) {
261                 l->current_dns_server = l->current_dns_server->servers_next;
262                 return;
263         }
264
265         l->current_dns_server = l->dns_servers;
266 }
267
268 int link_address_new(Link *l, LinkAddress **ret, int family, union in_addr_union *in_addr) {
269         LinkAddress *a;
270
271         assert(l);
272         assert(in_addr);
273
274         a = new0(LinkAddress, 1);
275         if (!a)
276                 return -ENOMEM;
277
278         a->family = family;
279         a->in_addr = *in_addr;
280
281         a->link = l;
282         LIST_PREPEND(addresses, l->addresses, a);
283
284         if (ret)
285                 *ret = a;
286
287         return 0;
288 }
289
290 LinkAddress *link_address_free(LinkAddress *a) {
291         if (!a)
292                 return NULL;
293
294         if (a->link)
295                 LIST_REMOVE(addresses, a->link->addresses, a);
296
297         free(a);
298         return NULL;
299 }
300
301 int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
302         int r;
303         assert(a);
304         assert(m);
305
306         r = sd_rtnl_message_addr_get_flags(m, &a->flags);
307         if (r < 0)
308                 return r;
309
310         sd_rtnl_message_addr_get_scope(m, &a->scope);
311
312         link_allocate_scopes(a->link);
313         return 0;
314 }
315
316 bool link_address_relevant(LinkAddress *a) {
317         assert(a);
318
319         if (a->flags & IFA_F_DEPRECATED)
320                 return false;
321
322         if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
323                 return false;
324
325         return true;
326 }