]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-manager.c
networkd: add basic bonding support
[thirdparty/systemd.git] / src / network / networkd-manager.c
CommitLineData
f579559b
TG
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
3bef724f
TG
22#include <resolv.h>
23
f579559b
TG
24#include "path-util.h"
25#include "networkd.h"
26#include "libudev-private.h"
7b77ed8c 27#include "udev-util.h"
3bef724f 28#include "mkdir.h"
f579559b 29
2ad8416d
ZJS
30const 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
f579559b
TG
39int 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
afc6adb5 47 r = sd_event_default(&m->event);
f579559b
TG
48 if (r < 0)
49 return r;
50
cde93897
LP
51 sd_event_set_watchdog(m->event, true);
52
dd3efc09 53 r = sd_rtnl_open(RTMGRP_LINK | RTMGRP_IPV4_IFADDR, &m->rtnl);
f579559b
TG
54 if (r < 0)
55 return r;
56
1346b1f0 57 r = sd_bus_default_system(&m->bus);
bcbca829 58 if (r < 0 && r != -ENOENT) /* TODO: drop when we can rely on kdbus */
1346b1f0
TG
59 return r;
60
f579559b
TG
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
52433f6b
TG
73 m->netdevs = hashmap_new(string_hash_func, string_compare_func);
74 if (!m->netdevs)
02b59d57
TG
75 return -ENOMEM;
76
f579559b
TG
77 LIST_HEAD_INIT(m->networks);
78
f579559b
TG
79 *ret = m;
80 m = NULL;
81
82 return 0;
83}
84
85void manager_free(Manager *m) {
0617ffab 86 Network *network;
52433f6b 87 Netdev *netdev;
0617ffab
TG
88 Link *link;
89
f579559b
TG
90 udev_monitor_unref(m->udev_monitor);
91 udev_unref(m->udev);
1346b1f0 92 sd_bus_unref(m->bus);
f579559b
TG
93 sd_event_source_unref(m->udev_event_source);
94 sd_event_unref(m->event);
0617ffab
TG
95
96 while ((network = m->networks))
97 network_free(network);
98
99 while ((link = hashmap_first(m->links)))
100 link_free(link);
f579559b 101 hashmap_free(m->links);
0617ffab 102
52433f6b
TG
103 while ((netdev = hashmap_first(m->netdevs)))
104 netdev_free(netdev);
105 hashmap_free(m->netdevs);
02b59d57 106
f579559b
TG
107 sd_rtnl_unref(m->rtnl);
108
109 free(m);
110}
111
02b59d57
TG
112int manager_load_config(Manager *m) {
113 int r;
114
115 /* update timestamp */
2ad8416d 116 paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, true);
02b59d57 117
52433f6b 118 r = netdev_load(m);
02b59d57
TG
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
129bool manager_should_reload(Manager *m) {
2ad8416d 130 return paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, false);
02b59d57
TG
131}
132
f579559b
TG
133static 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
449f7554 140 log_debug("%s: link removed", udev_device_get_sysname(device));
002f5de9 141
f579559b
TG
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 {
aa3437a5 149 r = link_add(m, device, &link);
f579559b 150 if (r < 0) {
2672953b
TG
151 if (r == -EEXIST)
152 log_debug("%s: link already exists, ignoring",
aa3437a5 153 link->ifname);
2672953b
TG
154 else
155 log_error("%s: could not handle link: %s",
156 udev_device_get_sysname(device),
157 strerror(-r));
158 } else
aa3437a5
TG
159 log_debug("%s: link (with ifindex %" PRIu64") added",
160 link->ifname, link->ifindex);
f579559b
TG
161 }
162
163 return 0;
164}
165
166int manager_udev_enumerate_links(Manager *m) {
bf5332d2 167 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
f579559b 168 struct udev_list_entry *item = NULL, *first = NULL;
f579559b
TG
169 int r;
170
171 assert(m);
172
173 e = udev_enumerate_new(m->udev);
bf5332d2
LP
174 if (!e)
175 return -ENOMEM;
f579559b
TG
176
177 r = udev_enumerate_add_match_subsystem(e, "net");
178 if (r < 0)
bf5332d2 179 return r;
f579559b 180
e1202047
LP
181 r = udev_enumerate_add_match_is_initialized(e);
182 if (r < 0)
183 return r;
184
f579559b
TG
185 r = udev_enumerate_scan_devices(e);
186 if (r < 0)
bf5332d2 187 return r;
f579559b
TG
188
189 first = udev_enumerate_get_list_entry(e);
190 udev_list_entry_foreach(item, first) {
bf5332d2 191 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
f579559b
TG
192 int k;
193
194 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
bf5332d2
LP
195 if (!d)
196 return -ENOMEM;
f579559b 197
bf5332d2 198 k = manager_process_link(m, d);
f579559b
TG
199 if (k < 0)
200 r = k;
201 }
202
f579559b
TG
203 return r;
204}
205
206static 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;
7b77ed8c 209 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
f579559b
TG
210
211 device = udev_monitor_receive_device(monitor);
212 if (!device)
213 return -ENOMEM;
214
7b77ed8c 215 manager_process_link(m, device);
f579559b
TG
216 return 0;
217}
218
219int 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
f579559b
TG
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}
f882c247 243
dd3efc09
TG
244static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, void *userdata) {
245 Manager *m = userdata;
246 Link *link;
8b264404 247 uint64_t ifindex_64;
dd3efc09
TG
248 int r, ifindex;
249
250 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
d0d311d6
TG
251 if (r < 0) {
252 log_debug("received RTM_NEWLINK message without valid ifindex");
dd3efc09 253 return 0;
d0d311d6 254 }
dd3efc09 255
8b264404
TG
256 ifindex_64 = ifindex;
257 link = hashmap_get(m->links, &ifindex_64);
d0d311d6 258 if (!link) {
8b264404 259 log_debug("received RTM_NEWLINK message for untracked ifindex %d", ifindex);
dd3efc09 260 return 0;
d0d311d6 261 }
dd3efc09 262
06a6e593
TG
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;
d0d311d6 268 } else
8b264404 269 log_debug("%s: received RTM_NEWLINK message for unmanaged link", link->ifname);
dd3efc09
TG
270
271 return 1;
272}
273
f882c247
TG
274int 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
dd3efc09
TG
281 r = sd_rtnl_add_match(m->rtnl, RTM_NEWLINK, &manager_rtnl_process_link, m);
282 if (r < 0)
283 return r;
284
f882c247
TG
285 return 0;
286}
3bef724f 287
1346b1f0
TG
288int manager_bus_listen(Manager *m) {
289 int r;
290
bcbca829
TG
291 assert(m->event);
292
293 if (!m->bus) /* TODO: drop when we can rely on kdbus */
294 return 0;
295
1346b1f0
TG
296 r = sd_bus_attach_event(m->bus, m->event, 0);
297 if (r < 0)
298 return r;
299
300 return 0;
301}
302
3bef724f
TG
303static 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)
f3621dec
TG
314 fputs("# Too many DNS servers configured, the following entries "
315 "will be ignored\n", f);
3bef724f
TG
316
317 fprintf(f, "nameserver %s\n", address);
318
319 (*count) ++;
320}
321
322int 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;
039ebe6a 328 const char *domainname = NULL;
3bef724f
TG
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
b2ad8a16
TG
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);
3bef724f
TG
348
349 HASHMAP_FOREACH(link, m->links, i) {
350 if (link->dhcp) {
7ae4ef6d
TG
351 struct in_addr *nameservers;
352 size_t nameservers_size;
3bef724f 353
039ebe6a
TG
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;
3bef724f 358
039ebe6a
TG
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);
3bef724f
TG
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}