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