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