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