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