chiark / gitweb /
7b0c5d76537b527f76af726c255850dd46ba6ed7
[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 "path-util.h"
23 #include "networkd.h"
24 #include "libudev-private.h"
25 #include "udev-util.h"
26
27 int manager_new(Manager **ret) {
28         _cleanup_manager_free_ Manager *m = NULL;
29         int r;
30
31         m = new0(Manager, 1);
32         if (!m)
33                 return -ENOMEM;
34
35         r = sd_event_default(&m->event);
36         if (r < 0)
37                 return r;
38
39         sd_event_set_watchdog(m->event, true);
40
41         r = sd_rtnl_open(RTMGRP_LINK | RTMGRP_IPV4_IFADDR, &m->rtnl);
42         if (r < 0)
43                 return r;
44
45         m->udev = udev_new();
46         if (!m->udev)
47                 return -ENOMEM;
48
49         m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
50         if (!m->udev_monitor)
51                 return -ENOMEM;
52
53         m->links = hashmap_new(uint64_hash_func, uint64_compare_func);
54         if (!m->links)
55                 return -ENOMEM;
56
57         m->bridges = hashmap_new(string_hash_func, string_compare_func);
58         if (!m->bridges)
59                 return -ENOMEM;
60
61         LIST_HEAD_INIT(m->networks);
62
63         m->network_dirs = strv_new("/etc/systemd/network/",
64                         "/run/systemd/network/",
65                         "/usr/lib/systemd/network",
66 #ifdef HAVE_SPLIT_USER
67                         "/lib/systemd/network",
68 #endif
69                         NULL);
70         if (!m->network_dirs)
71                 return -ENOMEM;
72
73         if (!path_strv_canonicalize_uniq(m->network_dirs))
74                 return -ENOMEM;
75
76         *ret = m;
77         m = NULL;
78
79         return 0;
80 }
81
82 void manager_free(Manager *m) {
83         Network *network;
84         Bridge *bridge;
85         Link *link;
86
87         udev_monitor_unref(m->udev_monitor);
88         udev_unref(m->udev);
89         sd_event_source_unref(m->udev_event_source);
90         sd_event_unref(m->event);
91
92         while ((network = m->networks))
93                 network_free(network);
94
95         while ((link = hashmap_first(m->links)))
96                 link_free(link);
97         hashmap_free(m->links);
98
99         while ((bridge = hashmap_first(m->bridges)))
100                 bridge_free(bridge);
101         hashmap_free(m->bridges);
102
103         strv_free(m->network_dirs);
104         sd_rtnl_unref(m->rtnl);
105
106         free(m);
107 }
108
109 int manager_load_config(Manager *m) {
110         int r;
111
112         /* update timestamp */
113         paths_check_timestamp(m->network_dirs, &m->network_dirs_ts_usec, true);
114
115         r = bridge_load(m);
116         if (r < 0)
117                 return r;
118
119         r = network_load(m);
120         if (r < 0)
121                 return r;
122
123         return 0;
124 }
125
126 bool manager_should_reload(Manager *m) {
127         return paths_check_timestamp(m->network_dirs, &m->network_dirs_ts_usec, false);
128 }
129
130 static int manager_process_link(Manager *m, struct udev_device *device) {
131         Link *link;
132         int r;
133
134         if (streq_ptr(udev_device_get_action(device), "remove")) {
135                 uint64_t ifindex;
136
137                 log_debug("%s: link removed", udev_device_get_sysname(device));
138
139                 ifindex = udev_device_get_ifindex(device);
140                 link = hashmap_get(m->links, &ifindex);
141                 if (!link)
142                         return 0;
143
144                 link_free(link);
145         } else {
146                 r = link_add(m, device, &link);
147                 if (r < 0) {
148                         if (r == -EEXIST)
149                                 log_debug("%s: link already exists, ignoring",
150                                           link->ifname);
151                         else
152                                 log_error("%s: could not handle link: %s",
153                                           udev_device_get_sysname(device),
154                                           strerror(-r));
155                 } else
156                         log_debug("%s: link (with ifindex %" PRIu64") added",
157                                   link->ifname, link->ifindex);
158         }
159
160         return 0;
161 }
162
163 int manager_udev_enumerate_links(Manager *m) {
164         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
165         struct udev_list_entry *item = NULL, *first = NULL;
166         int r;
167
168         assert(m);
169
170         e = udev_enumerate_new(m->udev);
171         if (!e)
172                 return -ENOMEM;
173
174         r = udev_enumerate_add_match_subsystem(e, "net");
175         if (r < 0)
176                 return r;
177
178         r = udev_enumerate_add_match_is_initialized(e);
179         if (r < 0)
180                 return r;
181
182         r = udev_enumerate_scan_devices(e);
183         if (r < 0)
184                 return r;
185
186         first = udev_enumerate_get_list_entry(e);
187         udev_list_entry_foreach(item, first) {
188                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
189                 int k;
190
191                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
192                 if (!d)
193                         return -ENOMEM;
194
195                 k = manager_process_link(m, d);
196                 if (k < 0)
197                         r = k;
198         }
199
200         return r;
201 }
202
203 static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
204         Manager *m = userdata;
205         struct udev_monitor *monitor = m->udev_monitor;
206         _cleanup_udev_device_unref_ struct udev_device *device = NULL;
207
208         device = udev_monitor_receive_device(monitor);
209         if (!device)
210                 return -ENOMEM;
211
212         manager_process_link(m, device);
213         return 0;
214 }
215
216 int manager_udev_listen(Manager *m) {
217         int r;
218
219         r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL);
220         if (r < 0) {
221                 log_error("Could not add udev monitor filter: %s", strerror(-r));
222                 return r;
223         }
224
225         r = udev_monitor_enable_receiving(m->udev_monitor);
226         if (r < 0) {
227                 log_error("Could not enable udev monitor");
228                 return r;
229         }
230
231         r = sd_event_add_io(m->event,
232                         udev_monitor_get_fd(m->udev_monitor),
233                         EPOLLIN, manager_dispatch_link_udev,
234                         m, &m->udev_event_source);
235         if (r < 0)
236                 return r;
237
238         return 0;
239 }
240
241 static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, void *userdata) {
242         Manager *m = userdata;
243         Link *link;
244         int r, ifindex;
245
246         r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
247         if (r < 0) {
248                 log_debug("received RTM_NEWLINK message without valid ifindex");
249                 return 0;
250         }
251
252         link = hashmap_get(m->links, &ifindex);
253         if (!link) {
254                 log_debug("received RTM_NEWLINK message for ifindex we are not tracking (%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("received RTM_NEWLINK message for link we are not managing (%d)", ifindex);
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 }