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