]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-link.c
resolved: SPF records
[thirdparty/systemd.git] / src / resolve / resolved-link.c
CommitLineData
74b2466e
LP
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"
74b2466e
LP
25#include "strv.h"
26#include "resolved-link.h"
27
623a4c97
LP
28static void link_address_add_rrs(LinkAddress *a);
29
74b2466e
LP
30int link_new(Manager *m, Link **ret, int ifindex) {
31 _cleanup_(link_freep) Link *l = NULL;
32 int r;
33
34 assert(m);
35 assert(ifindex > 0);
36
37 r = hashmap_ensure_allocated(&m->links, NULL, NULL);
38 if (r < 0)
39 return r;
40
41 l = new0(Link, 1);
42 if (!l)
43 return -ENOMEM;
44
45 l->ifindex = ifindex;
46
47 r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
48 if (r < 0)
49 return r;
50
51 l->manager = m;
52
53 if (ret)
54 *ret = l;
55 l = NULL;
56
57 return 0;
58}
59
60Link *link_free(Link *l) {
61
62 if (!l)
63 return NULL;
64
65 while (l->addresses)
66 link_address_free(l->addresses);
67
68 if (l->manager)
69 hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
70
71 dns_scope_free(l->unicast_scope);
1716f6dc
LP
72 dns_scope_free(l->llmnr_ipv4_scope);
73 dns_scope_free(l->llmnr_ipv6_scope);
74b2466e 74
6073b6f2
TG
75 while (l->dns_servers)
76 dns_server_free(l->dns_servers);
74b2466e
LP
77
78 free(l);
79 return NULL;
1716f6dc
LP
80}
81
82static void link_allocate_scopes(Link *l) {
83 int r;
84
85 assert(l);
86
6073b6f2 87 if (l->dns_servers) {
1716f6dc
LP
88 if (!l->unicast_scope) {
89 r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
90 if (r < 0)
91 log_warning("Failed to allocate DNS scope: %s", strerror(-r));
92 }
93 } else
94 l->unicast_scope = dns_scope_free(l->unicast_scope);
95
8bea3d6f 96 if (link_relevant(l, AF_INET) && (l->flags & IFF_MULTICAST) && l->manager->use_llmnr) {
1716f6dc
LP
97 if (!l->llmnr_ipv4_scope) {
98 r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
99 if (r < 0)
100 log_warning("Failed to allocate LLMNR IPv4 scope: %s", strerror(-r));
101 }
102 } else
103 l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
104
8bea3d6f 105 if (link_relevant(l, AF_INET6) && (l->flags & IFF_MULTICAST) && l->manager->use_llmnr) {
1716f6dc
LP
106 if (!l->llmnr_ipv6_scope) {
107 r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
108 if (r < 0)
109 log_warning("Failed to allocate LLMNR IPv6 scope: %s", strerror(-r));
110 }
111 } else
112 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
113}
74b2466e 114
623a4c97
LP
115static void link_add_rrs(Link *l) {
116 LinkAddress *a;
117
118 LIST_FOREACH(addresses, a, l->addresses)
119 link_address_add_rrs(a);
120}
121
74b2466e 122int link_update_rtnl(Link *l, sd_rtnl_message *m) {
1716f6dc 123 const char *n = NULL;
74b2466e
LP
124 int r;
125
126 assert(l);
127 assert(m);
128
129 r = sd_rtnl_message_link_get_flags(m, &l->flags);
130 if (r < 0)
131 return r;
132
c5ed9316 133 sd_rtnl_message_read_u32(m, IFLA_MTU, &l->mtu);
1716f6dc
LP
134
135 if (sd_rtnl_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
136 strncpy(l->name, n, sizeof(l->name));
137 char_array_0(l->name);
138 }
139
140 link_allocate_scopes(l);
623a4c97
LP
141 link_add_rrs(l);
142
74b2466e
LP
143 return 0;
144}
145
6073b6f2 146static int link_update_dns_servers(Link *l) {
6f4dedb2
TG
147 _cleanup_strv_free_ char **nameservers = NULL;
148 char **nameserver;
74b2466e 149 DnsServer *s, *nx;
6f4dedb2 150 int r;
74b2466e
LP
151
152 assert(l);
153
6073b6f2 154 LIST_FOREACH(servers, s, l->dns_servers)
74b2466e
LP
155 s->marked = true;
156
6f4dedb2
TG
157 r = sd_network_get_dns(l->ifindex, &nameservers);
158 if (r < 0)
74b2466e 159 goto clear;
74b2466e 160
6f4dedb2
TG
161 STRV_FOREACH(nameserver, nameservers) {
162 union in_addr_union a;
163 int family;
74b2466e 164
6f4dedb2
TG
165 r = in_addr_from_string_auto(*nameserver, &family, &a);
166 if (r < 0)
167 goto clear;
74b2466e 168
6f4dedb2 169 s = link_find_dns_server(l, family, &a);
74b2466e
LP
170 if (s)
171 s->marked = false;
172 else {
6f4dedb2 173 r = dns_server_new(l->manager, NULL, l, family, &a);
74b2466e
LP
174 if (r < 0)
175 goto clear;
176 }
177 }
178
6073b6f2 179 LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
74b2466e
LP
180 if (s->marked)
181 dns_server_free(s);
182
183 return 0;
184
185clear:
6073b6f2
TG
186 while (l->dns_servers)
187 dns_server_free(l->dns_servers);
74b2466e
LP
188
189 return r;
190}
191
192int link_update_monitor(Link *l) {
193 assert(l);
194
6073b6f2 195 link_update_dns_servers(l);
1716f6dc 196 link_allocate_scopes(l);
623a4c97 197 link_add_rrs(l);
74b2466e
LP
198
199 return 0;
200}
201
0dd25fb9 202bool link_relevant(Link *l, int family) {
1716f6dc 203 _cleanup_free_ char *state = NULL;
74b2466e
LP
204 LinkAddress *a;
205
206 assert(l);
207
208 /* A link is relevant if it isn't a loopback device and has at
209 * least one relevant IP address */
210
211 if (l->flags & IFF_LOOPBACK)
212 return false;
213
1716f6dc
LP
214 sd_network_get_link_operational_state(l->ifindex, &state);
215 if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
74b2466e
LP
216 return false;
217
218 LIST_FOREACH(addresses, a, l->addresses)
1716f6dc 219 if (a->family == family && link_address_relevant(a))
74b2466e
LP
220 return true;
221
222 return false;
223}
224
623a4c97 225LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
74b2466e
LP
226 LinkAddress *a;
227
228 assert(l);
229
1716f6dc
LP
230 LIST_FOREACH(addresses, a, l->addresses)
231 if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
74b2466e 232 return a;
74b2466e
LP
233
234 return NULL;
235}
236
623a4c97 237DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
6073b6f2 238 DnsServer *s;
74b2466e
LP
239
240 assert(l);
241
6073b6f2 242 LIST_FOREACH(servers, s, l->dns_servers)
1716f6dc 243 if (s->family == family && in_addr_equal(family, &s->address, in_addr))
74b2466e 244 return s;
74b2466e
LP
245 return NULL;
246}
247
248DnsServer *link_get_dns_server(Link *l) {
249 assert(l);
250
251 if (!l->current_dns_server)
6073b6f2 252 l->current_dns_server = l->dns_servers;
74b2466e
LP
253
254 return l->current_dns_server;
255}
256
257void link_next_dns_server(Link *l) {
258 assert(l);
259
260 /* Switch to the next DNS server */
261
262 if (!l->current_dns_server) {
6073b6f2 263 l->current_dns_server = l->dns_servers;
74b2466e
LP
264 if (l->current_dns_server)
265 return;
266 }
267
268 if (!l->current_dns_server)
269 return;
270
271 if (l->current_dns_server->servers_next) {
272 l->current_dns_server = l->current_dns_server->servers_next;
273 return;
274 }
275
6073b6f2 276 l->current_dns_server = l->dns_servers;
74b2466e
LP
277}
278
623a4c97 279int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
74b2466e
LP
280 LinkAddress *a;
281
282 assert(l);
283 assert(in_addr);
284
285 a = new0(LinkAddress, 1);
286 if (!a)
287 return -ENOMEM;
288
289 a->family = family;
290 a->in_addr = *in_addr;
291
292 a->link = l;
293 LIST_PREPEND(addresses, l->addresses, a);
294
295 if (ret)
296 *ret = a;
297
298 return 0;
299}
300
301LinkAddress *link_address_free(LinkAddress *a) {
302 if (!a)
303 return NULL;
304
623a4c97 305 if (a->link) {
74b2466e
LP
306 LIST_REMOVE(addresses, a->link->addresses, a);
307
623a4c97
LP
308 if (a->llmnr_address_rr) {
309
310 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
311 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
312 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
313 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
314
315 dns_resource_record_unref(a->llmnr_address_rr);
316 }
317
318 if (a->llmnr_ptr_rr) {
319 if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
320 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
321 else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
322 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
323
324 dns_resource_record_unref(a->llmnr_ptr_rr);
325 }
326 }
327
74b2466e
LP
328 free(a);
329 return NULL;
330}
331
623a4c97
LP
332static void link_address_add_rrs(LinkAddress *a) {
333 int r;
334
335 assert(a);
336
337 if (a->family == AF_INET && a->link->llmnr_ipv4_scope) {
338
339 if (!a->link->manager->host_ipv4_key) {
340 a->link->manager->host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->hostname);
341 if (!a->link->manager->host_ipv4_key) {
342 r = -ENOMEM;
343 goto fail;
344 }
345 }
346
347 if (!a->llmnr_address_rr) {
348 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv4_key);
349 if (!a->llmnr_address_rr) {
350 r = -ENOMEM;
351 goto fail;
352 }
353
354 a->llmnr_address_rr->a.in_addr = a->in_addr.in;
0c903ae7 355 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
623a4c97
LP
356 }
357
358 if (!a->llmnr_ptr_rr) {
359 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
360 if (r < 0)
361 goto fail;
362
0c903ae7 363 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
623a4c97
LP
364 }
365
366 if (link_address_relevant(a)) {
367 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
368 if (r < 0)
369 goto fail;
370
371 r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
372 if (r < 0)
373 goto fail;
374 } else {
375 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
376 dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
377 }
378 }
379
380 if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope) {
381
382 if (!a->link->manager->host_ipv6_key) {
383 a->link->manager->host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->hostname);
384 if (!a->link->manager->host_ipv6_key) {
385 r = -ENOMEM;
386 goto fail;
387 }
388 }
389
390 if (!a->llmnr_address_rr) {
391 a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv6_key);
392 if (!a->llmnr_address_rr) {
393 r = -ENOMEM;
394 goto fail;
395 }
396
397 a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
0c903ae7 398 a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
623a4c97
LP
399 }
400
401 if (!a->llmnr_ptr_rr) {
402 r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
403 if (r < 0)
404 goto fail;
405
0c903ae7 406 a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
623a4c97
LP
407 }
408
409 if (link_address_relevant(a)) {
410 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
411 if (r < 0)
412 goto fail;
413
414 r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
415 if (r < 0)
416 goto fail;
417 } else {
418 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
419 dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
420 }
421 }
422
423 return;
424
425fail:
426 log_debug("Failed to update address RRs: %s", strerror(-r));
427}
428
74b2466e
LP
429int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) {
430 int r;
431 assert(a);
432 assert(m);
433
434 r = sd_rtnl_message_addr_get_flags(m, &a->flags);
435 if (r < 0)
436 return r;
437
1716f6dc 438 sd_rtnl_message_addr_get_scope(m, &a->scope);
74b2466e 439
1716f6dc 440 link_allocate_scopes(a->link);
623a4c97
LP
441 link_add_rrs(a->link);
442
74b2466e
LP
443 return 0;
444}
445
446bool link_address_relevant(LinkAddress *a) {
447 assert(a);
448
449 if (a->flags & IFA_F_DEPRECATED)
450 return false;
451
452 if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
453 return false;
454
455 return true;
456}