chiark / gitweb /
sd-rtnl: modernize a bit
[elogind.git] / src / network / networkd-manager.c
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
22 #include <resolv.h>
23
24 #include "path-util.h"
25 #include "networkd.h"
26 #include "libudev-private.h"
27 #include "udev-util.h"
28 #include "rtnl-util.h"
29 #include "mkdir.h"
30 #include "virt.h"
31
32 const char* const network_dirs[] = {
33         "/etc/systemd/network",
34         "/run/systemd/network",
35         "/usr/lib/systemd/network",
36 #ifdef HAVE_SPLIT_USR
37         "/lib/systemd/network",
38 #endif
39         NULL};
40
41 static int dispatch_sigterm(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) {
42         Manager *m = userdata;
43
44         assert(m);
45
46         log_received_signal(LOG_INFO, si);
47
48         sd_event_exit(m->event, 0);
49         return 0;
50 }
51
52 static int setup_signals(Manager *m) {
53         sigset_t mask;
54         int r;
55
56         assert(m);
57
58         assert_se(sigemptyset(&mask) == 0);
59         sigset_add_many(&mask, SIGINT, SIGTERM, -1);
60         assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
61
62         r = sd_event_add_signal(m->event, &m->sigterm_event_source, SIGTERM, dispatch_sigterm, m);
63         if (r < 0)
64                 return r;
65
66         r = sd_event_add_signal(m->event, &m->sigint_event_source, SIGINT, dispatch_sigterm, m);
67         if (r < 0)
68                 return r;
69
70         return 0;
71 }
72
73 int manager_new(Manager **ret) {
74         _cleanup_manager_free_ Manager *m = NULL;
75         int r;
76
77         m = new0(Manager, 1);
78         if (!m)
79                 return -ENOMEM;
80
81         r = sd_event_default(&m->event);
82         if (r < 0)
83                 return r;
84
85         sd_event_set_watchdog(m->event, true);
86
87         r = sd_rtnl_open(&m->rtnl, RTMGRP_LINK | RTMGRP_IPV4_IFADDR);
88         if (r < 0)
89                 return r;
90
91         r = sd_bus_default_system(&m->bus);
92         if (r < 0 && r != -ENOENT) /* TODO: drop when we can rely on kdbus */
93                 return r;
94
95         r = setup_signals(m);
96         if (r < 0)
97                 return r;
98
99         m->udev = udev_new();
100         if (!m->udev)
101                 return -ENOMEM;
102
103         /* udev does not initialize devices inside containers,
104          * so we rely on them being already initialized before
105          * entering the container */
106         if (detect_container(NULL) > 0) {
107                 m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "kernel");
108                 if (!m->udev_monitor)
109                         return -ENOMEM;
110         } else {
111                 m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
112                 if (!m->udev_monitor)
113                         return -ENOMEM;
114         }
115
116         m->links = hashmap_new(uint64_hash_func, uint64_compare_func);
117         if (!m->links)
118                 return -ENOMEM;
119
120         m->netdevs = hashmap_new(string_hash_func, string_compare_func);
121         if (!m->netdevs)
122                 return -ENOMEM;
123
124         LIST_HEAD_INIT(m->networks);
125
126         *ret = m;
127         m = NULL;
128
129         return 0;
130 }
131
132 void manager_free(Manager *m) {
133         Network *network;
134         NetDev *netdev;
135         Link *link;
136
137         if (!m)
138                 return;
139
140         udev_monitor_unref(m->udev_monitor);
141         udev_unref(m->udev);
142         sd_bus_unref(m->bus);
143         sd_event_source_unref(m->udev_event_source);
144         sd_event_source_unref(m->sigterm_event_source);
145         sd_event_source_unref(m->sigint_event_source);
146         sd_event_unref(m->event);
147
148         while ((link = hashmap_first(m->links)))
149                 link_free(link);
150         hashmap_free(m->links);
151
152         while ((network = m->networks))
153                 network_free(network);
154
155         while ((netdev = hashmap_first(m->netdevs)))
156                 netdev_free(netdev);
157         hashmap_free(m->netdevs);
158
159         sd_rtnl_unref(m->rtnl);
160
161         free(m);
162 }
163
164 int manager_load_config(Manager *m) {
165         int r;
166
167         /* update timestamp */
168         paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, true);
169
170         r = netdev_load(m);
171         if (r < 0)
172                 return r;
173
174         r = network_load(m);
175         if (r < 0)
176                 return r;
177
178         return 0;
179 }
180
181 bool manager_should_reload(Manager *m) {
182         return paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, false);
183 }
184
185 static int manager_process_link(Manager *m, struct udev_device *device) {
186         Link *link = NULL;
187         int r;
188
189         assert(m);
190         assert(device);
191
192         link_get(m, udev_device_get_ifindex(device), &link);
193
194         if (streq_ptr(udev_device_get_action(device), "remove")) {
195                 log_debug("%s: link removed", udev_device_get_sysname(device));
196
197                 if (link)
198                         link_free(link);
199         } else {
200                 if (link) {
201                         log_debug("%s: link already exists, ignoring",
202                                   link->ifname);
203                         return 0;
204                 }
205
206                 r = link_add(m, device, &link);
207                 if (r < 0) {
208                         log_error("%s: could not handle link: %s",
209                                   udev_device_get_sysname(device),
210                                   strerror(-r));
211                 } else
212                         log_debug("%s: link (with ifindex %" PRIu64") added",
213                                   link->ifname, link->ifindex);
214         }
215
216         return 0;
217 }
218
219 int manager_udev_enumerate_links(Manager *m) {
220         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
221         struct udev_list_entry *item = NULL, *first = NULL;
222         int r;
223
224         assert(m);
225
226         e = udev_enumerate_new(m->udev);
227         if (!e)
228                 return -ENOMEM;
229
230         r = udev_enumerate_add_match_subsystem(e, "net");
231         if (r < 0)
232                 return r;
233
234         /* udev does not initialize devices inside containers,
235          * so we rely on them being already initialized before
236          * entering the container */
237         if (detect_container(NULL) <= 0) {
238                 r = udev_enumerate_add_match_is_initialized(e);
239                 if (r < 0)
240                         return r;
241         }
242
243         r = udev_enumerate_scan_devices(e);
244         if (r < 0)
245                 return r;
246
247         first = udev_enumerate_get_list_entry(e);
248         udev_list_entry_foreach(item, first) {
249                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
250                 int k;
251
252                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
253                 if (!d)
254                         return -ENOMEM;
255
256                 k = manager_process_link(m, d);
257                 if (k < 0)
258                         r = k;
259         }
260
261         return r;
262 }
263
264 static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
265         Manager *m = userdata;
266         struct udev_monitor *monitor = m->udev_monitor;
267         _cleanup_udev_device_unref_ struct udev_device *device = NULL;
268
269         device = udev_monitor_receive_device(monitor);
270         if (!device)
271                 return -ENOMEM;
272
273         manager_process_link(m, device);
274         return 0;
275 }
276
277 int manager_udev_listen(Manager *m) {
278         int r;
279
280         r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL);
281         if (r < 0) {
282                 log_error("Could not add udev monitor filter: %s", strerror(-r));
283                 return r;
284         }
285
286         r = udev_monitor_enable_receiving(m->udev_monitor);
287         if (r < 0) {
288                 log_error("Could not enable udev monitor");
289                 return r;
290         }
291
292         r = sd_event_add_io(m->event,
293                         &m->udev_event_source,
294                         udev_monitor_get_fd(m->udev_monitor),
295                         EPOLLIN, manager_dispatch_link_udev,
296                         m);
297         if (r < 0)
298                 return r;
299
300         return 0;
301 }
302
303 static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, void *userdata) {
304         Manager *m = userdata;
305         Link *link;
306         char *name;
307         int r, ifindex;
308
309         assert(rtnl);
310         assert(message);
311         assert(m);
312
313         r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
314         if (r < 0 || ifindex <= 0) {
315                 log_warning("received RTM_NEWLINK message without valid ifindex");
316                 return 0;
317         }
318
319         r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &name);
320         if (r < 0)
321                 log_warning("received RTM_NEWLINK message without valid ifname");
322         else {
323                 NetDev *netdev;
324
325                 r = netdev_get(m, name, &netdev);
326                 if (r >= 0)
327                         netdev_set_ifindex(netdev, message);
328         }
329
330         r = link_get(m, ifindex, &link);
331         if (r < 0) {
332                 log_debug("received RTM_NEWLINK message for untracked ifindex %d", ifindex);
333                 return 0;
334         }
335
336         /* only track the status of links we want to manage */
337         if (link->network) {
338                 r = link_update(link, message);
339                 if (r < 0)
340                         return 0;
341         } else
342                 log_debug("%s: received RTM_NEWLINK message for unmanaged link", link->ifname);
343
344         return 1;
345 }
346
347 int manager_rtnl_listen(Manager *m) {
348         int r;
349
350         r = sd_rtnl_attach_event(m->rtnl, m->event, 0);
351         if (r < 0)
352                 return r;
353
354         r = sd_rtnl_add_match(m->rtnl, RTM_NEWLINK, &manager_rtnl_process_link, m);
355         if (r < 0)
356                 return r;
357
358         return 0;
359 }
360
361 int manager_bus_listen(Manager *m) {
362         int r;
363
364         assert(m->event);
365
366         if (!m->bus) /* TODO: drop when we can rely on kdbus */
367                 return 0;
368
369         r = sd_bus_attach_event(m->bus, m->event, 0);
370         if (r < 0)
371                 return r;
372
373         return 0;
374 }
375
376 static void append_dns(FILE *f, struct in_addr *dns, unsigned char family, unsigned *count) {
377         char buf[INET6_ADDRSTRLEN];
378         const char *address;
379
380         address = inet_ntop(family, dns, buf, INET6_ADDRSTRLEN);
381         if (!address) {
382                 log_warning("Invalid DNS address. Ignoring.");
383                 return;
384         }
385
386         if (*count == MAXNS)
387                 fputs("# Too many DNS servers configured, the following entries "
388                       "will be ignored\n", f);
389
390         fprintf(f, "nameserver %s\n", address);
391
392         (*count) ++;
393 }
394
395 int manager_update_resolv_conf(Manager *m) {
396         _cleanup_free_ char *temp_path = NULL;
397         _cleanup_fclose_ FILE *f = NULL;
398         Link *link;
399         Iterator i;
400         unsigned count = 0;
401         const char *domainname = NULL;
402         int r;
403
404         assert(m);
405
406         r = fopen_temporary("/run/systemd/network/resolv.conf", &f, &temp_path);
407         if (r < 0)
408                 return r;
409
410         fchmod(fileno(f), 0644);
411
412         fputs("# This file is managed by systemd-networkd(8). Do not edit.\n#\n"
413               "# Third party programs must not access this file directly, but\n"
414               "# only through the symlink at /etc/resolv.conf. To manage\n"
415               "# resolv.conf(5) in a different way, replace the symlink by a\n"
416               "# static file or a different symlink.\n\n", f);
417
418         HASHMAP_FOREACH(link, m->links, i) {
419                 if (link->dhcp_lease) {
420                         struct in_addr *nameservers;
421                         size_t nameservers_size;
422
423                         if (link->network->dhcp_dns) {
424                                 r = sd_dhcp_lease_get_dns(link->dhcp_lease, &nameservers, &nameservers_size);
425                                 if (r >= 0) {
426                                         unsigned j;
427
428                                         for (j = 0; j < nameservers_size; j++)
429                                                 append_dns(f, &nameservers[j], AF_INET, &count);
430                                 }
431                         }
432
433                         if (link->network->dhcp_domainname && !domainname) {
434                                 r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
435                                 if (r >= 0)
436                                        fprintf(f, "domain %s\n", domainname);
437                         }
438                 }
439         }
440
441         HASHMAP_FOREACH(link, m->links, i) {
442                 if (link->network && link->network->dns) {
443                         Address *address;
444                         Iterator j;
445
446                         SET_FOREACH(address, link->network->dns, j) {
447                                 append_dns(f, &address->in_addr.in,
448                                            address->family, &count);
449                         }
450                 }
451         }
452
453         fflush(f);
454
455         if (ferror(f) || rename(temp_path, "/run/systemd/network/resolv.conf") < 0) {
456                 r = -errno;
457                 unlink("/run/systemd/network/resolv.conf");
458                 unlink(temp_path);
459                 return r;
460         }
461
462         return 0;
463 }