]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-manager.c
sd-dhcp-client: split sd_dhcp_lease from sd_dhcp_client
[thirdparty/systemd.git] / src / network / networkd-manager.c
CommitLineData
f579559b
TG
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
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
3bef724f
TG
22#include <resolv.h>
23
f579559b
TG
24#include "path-util.h"
25#include "networkd.h"
26#include "libudev-private.h"
7b77ed8c 27#include "udev-util.h"
50add290 28#include "rtnl-util.h"
3bef724f 29#include "mkdir.h"
f579559b 30
2ad8416d
ZJS
31const char* const network_dirs[] = {
32 "/etc/systemd/network",
33 "/run/systemd/network",
34 "/usr/lib/systemd/network",
35#ifdef HAVE_SPLIT_USER
36 "/lib/systemd/network",
37#endif
38 NULL};
39
f579559b
TG
40int manager_new(Manager **ret) {
41 _cleanup_manager_free_ Manager *m = NULL;
42 int r;
43
44 m = new0(Manager, 1);
45 if (!m)
46 return -ENOMEM;
47
afc6adb5 48 r = sd_event_default(&m->event);
f579559b
TG
49 if (r < 0)
50 return r;
51
cde93897
LP
52 sd_event_set_watchdog(m->event, true);
53
dd3efc09 54 r = sd_rtnl_open(RTMGRP_LINK | RTMGRP_IPV4_IFADDR, &m->rtnl);
f579559b
TG
55 if (r < 0)
56 return r;
57
1346b1f0 58 r = sd_bus_default_system(&m->bus);
bcbca829 59 if (r < 0 && r != -ENOENT) /* TODO: drop when we can rely on kdbus */
1346b1f0
TG
60 return r;
61
f579559b
TG
62 m->udev = udev_new();
63 if (!m->udev)
64 return -ENOMEM;
65
66 m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
67 if (!m->udev_monitor)
68 return -ENOMEM;
69
70 m->links = hashmap_new(uint64_hash_func, uint64_compare_func);
71 if (!m->links)
72 return -ENOMEM;
73
52433f6b
TG
74 m->netdevs = hashmap_new(string_hash_func, string_compare_func);
75 if (!m->netdevs)
02b59d57
TG
76 return -ENOMEM;
77
f579559b
TG
78 LIST_HEAD_INIT(m->networks);
79
f579559b
TG
80 *ret = m;
81 m = NULL;
82
83 return 0;
84}
85
86void manager_free(Manager *m) {
0617ffab 87 Network *network;
52433f6b 88 Netdev *netdev;
0617ffab
TG
89 Link *link;
90
f579559b
TG
91 udev_monitor_unref(m->udev_monitor);
92 udev_unref(m->udev);
1346b1f0 93 sd_bus_unref(m->bus);
f579559b
TG
94 sd_event_source_unref(m->udev_event_source);
95 sd_event_unref(m->event);
0617ffab
TG
96
97 while ((network = m->networks))
98 network_free(network);
99
100 while ((link = hashmap_first(m->links)))
101 link_free(link);
f579559b 102 hashmap_free(m->links);
0617ffab 103
52433f6b
TG
104 while ((netdev = hashmap_first(m->netdevs)))
105 netdev_free(netdev);
106 hashmap_free(m->netdevs);
02b59d57 107
f579559b
TG
108 sd_rtnl_unref(m->rtnl);
109
110 free(m);
111}
112
02b59d57
TG
113int manager_load_config(Manager *m) {
114 int r;
115
116 /* update timestamp */
2ad8416d 117 paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, true);
02b59d57 118
52433f6b 119 r = netdev_load(m);
02b59d57
TG
120 if (r < 0)
121 return r;
122
123 r = network_load(m);
124 if (r < 0)
125 return r;
126
127 return 0;
128}
129
130bool manager_should_reload(Manager *m) {
2ad8416d 131 return paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, false);
02b59d57
TG
132}
133
f579559b
TG
134static int manager_process_link(Manager *m, struct udev_device *device) {
135 Link *link;
136 int r;
137
138 if (streq_ptr(udev_device_get_action(device), "remove")) {
139 uint64_t ifindex;
140
449f7554 141 log_debug("%s: link removed", udev_device_get_sysname(device));
002f5de9 142
f579559b
TG
143 ifindex = udev_device_get_ifindex(device);
144 link = hashmap_get(m->links, &ifindex);
145 if (!link)
146 return 0;
147
148 link_free(link);
149 } else {
aa3437a5 150 r = link_add(m, device, &link);
f579559b 151 if (r < 0) {
2672953b
TG
152 if (r == -EEXIST)
153 log_debug("%s: link already exists, ignoring",
aa3437a5 154 link->ifname);
2672953b
TG
155 else
156 log_error("%s: could not handle link: %s",
157 udev_device_get_sysname(device),
158 strerror(-r));
159 } else
aa3437a5
TG
160 log_debug("%s: link (with ifindex %" PRIu64") added",
161 link->ifname, link->ifindex);
f579559b
TG
162 }
163
164 return 0;
165}
166
167int manager_udev_enumerate_links(Manager *m) {
bf5332d2 168 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
f579559b 169 struct udev_list_entry *item = NULL, *first = NULL;
f579559b
TG
170 int r;
171
172 assert(m);
173
174 e = udev_enumerate_new(m->udev);
bf5332d2
LP
175 if (!e)
176 return -ENOMEM;
f579559b
TG
177
178 r = udev_enumerate_add_match_subsystem(e, "net");
179 if (r < 0)
bf5332d2 180 return r;
f579559b 181
e1202047
LP
182 r = udev_enumerate_add_match_is_initialized(e);
183 if (r < 0)
184 return r;
185
f579559b
TG
186 r = udev_enumerate_scan_devices(e);
187 if (r < 0)
bf5332d2 188 return r;
f579559b
TG
189
190 first = udev_enumerate_get_list_entry(e);
191 udev_list_entry_foreach(item, first) {
bf5332d2 192 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
f579559b
TG
193 int k;
194
195 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
bf5332d2
LP
196 if (!d)
197 return -ENOMEM;
f579559b 198
bf5332d2 199 k = manager_process_link(m, d);
f579559b
TG
200 if (k < 0)
201 r = k;
202 }
203
f579559b
TG
204 return r;
205}
206
207static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
208 Manager *m = userdata;
209 struct udev_monitor *monitor = m->udev_monitor;
7b77ed8c 210 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
f579559b
TG
211
212 device = udev_monitor_receive_device(monitor);
213 if (!device)
214 return -ENOMEM;
215
7b77ed8c 216 manager_process_link(m, device);
f579559b
TG
217 return 0;
218}
219
220int manager_udev_listen(Manager *m) {
221 int r;
222
223 r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL);
224 if (r < 0) {
225 log_error("Could not add udev monitor filter: %s", strerror(-r));
226 return r;
227 }
228
f579559b
TG
229 r = udev_monitor_enable_receiving(m->udev_monitor);
230 if (r < 0) {
231 log_error("Could not enable udev monitor");
232 return r;
233 }
234
235 r = sd_event_add_io(m->event,
236 udev_monitor_get_fd(m->udev_monitor),
237 EPOLLIN, manager_dispatch_link_udev,
238 m, &m->udev_event_source);
239 if (r < 0)
240 return r;
241
242 return 0;
243}
f882c247 244
dd3efc09
TG
245static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, void *userdata) {
246 Manager *m = userdata;
247 Link *link;
50add290 248 const char *name;
8b264404 249 uint64_t ifindex_64;
dd3efc09
TG
250 int r, ifindex;
251
252 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
50add290 253 if (r < 0 || ifindex <= 0) {
d0d311d6 254 log_debug("received RTM_NEWLINK message without valid ifindex");
dd3efc09 255 return 0;
d0d311d6 256 }
dd3efc09 257
50add290
TG
258 r = rtnl_message_link_get_ifname(message, &name);
259 if (r < 0)
260 log_debug("received RTM_NEWLINK message without valid IFLA_IFNAME");
261 else {
262 Netdev *netdev;
263
264 r = netdev_get(m, name, &netdev);
265 if (r >= 0) {
266 r = netdev_set_ifindex(netdev, ifindex);
267 if (r < 0)
268 log_debug("could not set ifindex of netdev '%s' to %d: %s",
269 name, ifindex, strerror(-r));
270 }
271 }
272
8b264404
TG
273 ifindex_64 = ifindex;
274 link = hashmap_get(m->links, &ifindex_64);
d0d311d6 275 if (!link) {
8b264404 276 log_debug("received RTM_NEWLINK message for untracked ifindex %d", ifindex);
dd3efc09 277 return 0;
d0d311d6 278 }
dd3efc09 279
06a6e593
TG
280 /* only track the status of links we want to manage */
281 if (link->network) {
282 r = link_update(link, message);
283 if (r < 0)
284 return 0;
d0d311d6 285 } else
8b264404 286 log_debug("%s: received RTM_NEWLINK message for unmanaged link", link->ifname);
dd3efc09
TG
287
288 return 1;
289}
290
f882c247
TG
291int manager_rtnl_listen(Manager *m) {
292 int r;
293
294 r = sd_rtnl_attach_event(m->rtnl, m->event, 0);
295 if (r < 0)
296 return r;
297
dd3efc09
TG
298 r = sd_rtnl_add_match(m->rtnl, RTM_NEWLINK, &manager_rtnl_process_link, m);
299 if (r < 0)
300 return r;
301
f882c247
TG
302 return 0;
303}
3bef724f 304
1346b1f0
TG
305int manager_bus_listen(Manager *m) {
306 int r;
307
bcbca829
TG
308 assert(m->event);
309
310 if (!m->bus) /* TODO: drop when we can rely on kdbus */
311 return 0;
312
1346b1f0
TG
313 r = sd_bus_attach_event(m->bus, m->event, 0);
314 if (r < 0)
315 return r;
316
317 return 0;
318}
319
3bef724f
TG
320static void append_dns(FILE *f, struct in_addr *dns, unsigned char family, unsigned *count) {
321 char buf[INET6_ADDRSTRLEN];
322 const char *address;
323
324 address = inet_ntop(family, dns, buf, INET6_ADDRSTRLEN);
325 if (!address) {
326 log_warning("Invalid DNS address. Ignoring.");
327 return;
328 }
329
330 if (*count == MAXNS)
f3621dec
TG
331 fputs("# Too many DNS servers configured, the following entries "
332 "will be ignored\n", f);
3bef724f
TG
333
334 fprintf(f, "nameserver %s\n", address);
335
336 (*count) ++;
337}
338
339int manager_update_resolv_conf(Manager *m) {
340 _cleanup_free_ char *temp_path = NULL;
341 _cleanup_fclose_ FILE *f = NULL;
342 Link *link;
343 Iterator i;
344 unsigned count = 0;
039ebe6a 345 const char *domainname = NULL;
3bef724f
TG
346 int r;
347
348 assert(m);
349
350 r = mkdir_safe_label("/run/systemd/network", 0755, 0, 0);
351 if (r < 0)
352 return r;
353
354 r = fopen_temporary("/run/systemd/network/resolv.conf", &f, &temp_path);
355 if (r < 0)
356 return r;
357
358 fchmod(fileno(f), 0644);
359
b2ad8a16
TG
360 fputs("# This file is managed by systemd-networkd(8). Do not edit.\n#\n"
361 "# Third party programs must not access this file directly, but\n"
362 "# only through the symlink at /etc/resolv.conf. To manage\n"
363 "# resolv.conf(5) in a different way, replace the symlink by a\n"
364 "# static file or a different symlink.\n\n", f);
3bef724f
TG
365
366 HASHMAP_FOREACH(link, m->links, i) {
a6cc569e 367 if (link->dhcp_lease) {
7ae4ef6d
TG
368 struct in_addr *nameservers;
369 size_t nameservers_size;
3bef724f 370
039ebe6a 371 if (link->network->dhcp_dns) {
a6cc569e 372 r = sd_dhcp_lease_get_dns(link->dhcp_lease, &nameservers, &nameservers_size);
039ebe6a
TG
373 if (r >= 0) {
374 unsigned j;
3bef724f 375
039ebe6a
TG
376 for (j = 0; j < nameservers_size; j++)
377 append_dns(f, &nameservers[j], AF_INET, &count);
378 }
379 }
380
381 if (link->network->dhcp_domainname && !domainname) {
a6cc569e 382 r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
039ebe6a
TG
383 if (r >= 0)
384 fprintf(f, "domain %s\n", domainname);
3bef724f
TG
385 }
386 }
387 }
388
389 HASHMAP_FOREACH(link, m->links, i)
390 if (link->network && link->network->dns)
391 append_dns(f, &link->network->dns->in_addr.in,
392 link->network->dns->family, &count);
393
394 fflush(f);
395
396 if (ferror(f) || rename(temp_path, "/run/systemd/network/resolv.conf") < 0) {
397 r = -errno;
398 unlink("/run/systemd/network/resolv.conf");
399 unlink(temp_path);
400 return r;
401 }
402
403 return 0;
404}