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