chiark / gitweb /
f817d698c4cfec2ad0956886c82d8b70a0ee7ad6
[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 = 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                         udev_monitor_get_fd(m->udev_monitor),
256                         EPOLLIN, manager_dispatch_link_udev,
257                         m, &m->udev_event_source);
258         if (r < 0)
259                 return r;
260
261         return 0;
262 }
263
264 static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, void *userdata) {
265         Manager *m = userdata;
266         Link *link;
267         const char *name;
268         int r, ifindex;
269
270         assert(rtnl);
271         assert(message);
272         assert(m);
273
274         r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
275         if (r < 0 || ifindex <= 0) {
276                 log_debug("received RTM_NEWLINK message without valid ifindex");
277                 return 0;
278         }
279
280         r = rtnl_message_link_get_ifname(message, &name);
281         if (r < 0)
282                 log_debug("received RTM_NEWLINK message without valid IFLA_IFNAME");
283         else {
284                 NetDev *netdev;
285
286                 r = netdev_get(m, name, &netdev);
287                 if (r >= 0) {
288                         r = netdev_set_ifindex(netdev, ifindex);
289                         if (r < 0)
290                                 log_debug("could not set ifindex of netdev '%s' to %d: %s",
291                                           name, ifindex, strerror(-r));
292                 }
293         }
294
295         r = link_get(m, ifindex, &link);
296         if (r < 0) {
297                 log_debug("received RTM_NEWLINK message for untracked ifindex %d", ifindex);
298                 return 0;
299         }
300
301         /* only track the status of links we want to manage */
302         if (link->network) {
303                 r = link_update(link, message);
304                 if (r < 0)
305                         return 0;
306         } else
307                 log_debug("%s: received RTM_NEWLINK message for unmanaged link", link->ifname);
308
309         return 1;
310 }
311
312 int manager_rtnl_listen(Manager *m) {
313         int r;
314
315         r = sd_rtnl_attach_event(m->rtnl, m->event, 0);
316         if (r < 0)
317                 return r;
318
319         r = sd_rtnl_add_match(m->rtnl, RTM_NEWLINK, &manager_rtnl_process_link, m);
320         if (r < 0)
321                 return r;
322
323         return 0;
324 }
325
326 int manager_bus_listen(Manager *m) {
327         int r;
328
329         assert(m->event);
330
331         if (!m->bus) /* TODO: drop when we can rely on kdbus */
332                 return 0;
333
334         r = sd_bus_attach_event(m->bus, m->event, 0);
335         if (r < 0)
336                 return r;
337
338         return 0;
339 }
340
341 static void append_dns(FILE *f, struct in_addr *dns, unsigned char family, unsigned *count) {
342         char buf[INET6_ADDRSTRLEN];
343         const char *address;
344
345         address = inet_ntop(family, dns, buf, INET6_ADDRSTRLEN);
346         if (!address) {
347                 log_warning("Invalid DNS address. Ignoring.");
348                 return;
349         }
350
351         if (*count == MAXNS)
352                 fputs("# Too many DNS servers configured, the following entries "
353                       "will be ignored\n", f);
354
355         fprintf(f, "nameserver %s\n", address);
356
357         (*count) ++;
358 }
359
360 int manager_update_resolv_conf(Manager *m) {
361         _cleanup_free_ char *temp_path = NULL;
362         _cleanup_fclose_ FILE *f = NULL;
363         Link *link;
364         Iterator i;
365         unsigned count = 0;
366         const char *domainname = NULL;
367         int r;
368
369         assert(m);
370
371         r = mkdir_safe_label("/run/systemd/network", 0755, 0, 0);
372         if (r < 0)
373                 return r;
374
375         r = fopen_temporary("/run/systemd/network/resolv.conf", &f, &temp_path);
376         if (r < 0)
377                 return r;
378
379         fchmod(fileno(f), 0644);
380
381         fputs("# This file is managed by systemd-networkd(8). Do not edit.\n#\n"
382               "# Third party programs must not access this file directly, but\n"
383               "# only through the symlink at /etc/resolv.conf. To manage\n"
384               "# resolv.conf(5) in a different way, replace the symlink by a\n"
385               "# static file or a different symlink.\n\n", f);
386
387         HASHMAP_FOREACH(link, m->links, i) {
388                 if (link->dhcp_lease) {
389                         struct in_addr *nameservers;
390                         size_t nameservers_size;
391
392                         if (link->network->dhcp_dns) {
393                                 r = sd_dhcp_lease_get_dns(link->dhcp_lease, &nameservers, &nameservers_size);
394                                 if (r >= 0) {
395                                         unsigned j;
396
397                                         for (j = 0; j < nameservers_size; j++)
398                                                 append_dns(f, &nameservers[j], AF_INET, &count);
399                                 }
400                         }
401
402                         if (link->network->dhcp_domainname && !domainname) {
403                                 r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
404                                 if (r >= 0)
405                                        fprintf(f, "domain %s\n", domainname);
406                         }
407                 }
408         }
409
410         HASHMAP_FOREACH(link, m->links, i)
411                 if (link->network && link->network->dns)
412                         append_dns(f, &link->network->dns->in_addr.in,
413                                    link->network->dns->family, &count);
414
415         fflush(f);
416
417         if (ferror(f) || rename(temp_path, "/run/systemd/network/resolv.conf") < 0) {
418                 r = -errno;
419                 unlink("/run/systemd/network/resolv.conf");
420                 unlink(temp_path);
421                 return r;
422         }
423
424         return 0;
425 }