chiark / gitweb /
networkd: add basic bonding support
[elogind.git] / src / network / networkd-netdev.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 "networkd.h"
23 #include "net-util.h"
24 #include "path-util.h"
25 #include "conf-files.h"
26 #include "conf-parser.h"
27 #include "list.h"
28
29 static const char* const netdev_kind_table[] = {
30         [NETDEV_KIND_BRIDGE] = "bridge",
31         [NETDEV_KIND_BOND] = "bond"
32 };
33
34 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetdevKind);
35 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetdevKind, "Failed to parse netdev kind");
36
37 void netdev_free(Netdev *netdev) {
38         netdev_enslave_callback *callback;
39
40         if (!netdev)
41                 return;
42
43         while ((callback = netdev->callbacks)) {
44                 LIST_REMOVE(callbacks, netdev->callbacks, callback);
45                 free(callback);
46         }
47
48         if (netdev->name)
49                 hashmap_remove(netdev->manager->netdevs, netdev->name);
50
51         free(netdev->filename);
52
53         free(netdev->description);
54         free(netdev->name);
55
56         free(netdev);
57 }
58
59 int netdev_get(Manager *manager, const char *name, Netdev **ret) {
60         Netdev *netdev;
61
62         assert(manager);
63         assert(name);
64         assert(ret);
65
66         netdev = hashmap_get(manager->netdevs, name);
67         if (!netdev) {
68                 *ret = NULL;
69                 return -ENOENT;
70         }
71
72         *ret = netdev;
73
74         return 0;
75 }
76
77 static int netdev_enter_failed(Netdev *netdev) {
78         netdev->state = NETDEV_STATE_FAILED;
79
80         return 0;
81 }
82
83 static int netdev_enslave_ready(Netdev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
84         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
85         int r;
86
87         assert(netdev);
88         assert(netdev->state == NETDEV_STATE_READY);
89         assert(link);
90         assert(callback);
91
92         r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
93         if (r < 0) {
94                 log_error_netdev(netdev,
95                                  "Could not allocate RTM_SETLINK message: %s",
96                                  strerror(-r));
97                 return r;
98         }
99
100         r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->link->ifindex);
101         if (r < 0) {
102                 log_error_netdev(netdev,
103                                  "Could not append IFLA_MASTER attribute: %s",
104                                  strerror(-r));
105                 return r;
106         }
107
108         r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
109         if (r < 0) {
110                 log_error_netdev(netdev,
111                                  "Could not send rtnetlink message: %s",
112                                  strerror(-r));
113                 return r;
114         }
115
116         log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
117
118         return 0;
119 }
120
121 static int netdev_enter_ready(Netdev *netdev) {
122         netdev_enslave_callback *callback;
123
124         assert(netdev);
125         assert(netdev->name);
126
127         netdev->state = NETDEV_STATE_READY;
128
129         log_info_netdev(netdev, "netdev ready");
130
131         LIST_FOREACH(callbacks, callback, netdev->callbacks) {
132                 /* enslave the links that were attempted to be enslaved befor the
133                  * link was ready */
134                 netdev_enslave_ready(netdev, callback->link, callback->callback);
135         }
136
137         return 0;
138 }
139
140 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
141         Netdev *netdev = userdata;
142         int r;
143
144         assert(netdev->state != _NETDEV_STATE_INVALID);
145
146         r = sd_rtnl_message_get_errno(m);
147         if (r < 0) {
148                 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
149                 netdev_enter_failed(netdev);
150
151                 return 1;
152         }
153
154         return 1;
155 }
156
157 static int netdev_create(Netdev *netdev) {
158         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
159         const char *kind;
160         int r;
161
162         assert(netdev);
163         assert(netdev->state == _NETDEV_STATE_INVALID);
164         assert(netdev->name);
165         assert(netdev->manager);
166         assert(netdev->manager->rtnl);
167
168         r = sd_rtnl_message_link_new(RTM_NEWLINK, 0, &req);
169         if (r < 0) {
170                 log_error_netdev(netdev,
171                                  "Could not allocate RTM_NEWLINK message: %s",
172                                  strerror(-r));
173                 return r;
174         }
175
176         r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
177         if (r < 0) {
178                 log_error_netdev(netdev,
179                                  "Could not append IFLA_IFNAME attribute: %s",
180                                  strerror(-r));
181                 return r;
182         }
183
184         r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
185         if (r < 0) {
186                 log_error_netdev(netdev,
187                                  "Could not open IFLA_LINKINFO container: %s",
188                                  strerror(-r));
189                 return r;
190         }
191
192         kind = netdev_kind_to_string(netdev->kind);
193         if (!kind) {
194                 log_error_netdev(netdev, "Invalid kind");
195                 return -EINVAL;
196         }
197
198         r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, kind);
199         if (r < 0) {
200                 log_error_netdev(netdev,
201                                  "Could not append IFLA_INFO_KIND attribute: %s",
202                                  strerror(-r));
203                 return r;
204         }
205
206         r = sd_rtnl_message_close_container(req);
207         if (r < 0) {
208                 log_error_netdev(netdev,
209                                  "Could not close IFLA_LINKINFO container %s",
210                                  strerror(-r));
211                 return r;
212         }
213
214         r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
215         if (r < 0) {
216                 log_error_netdev(netdev,
217                                  "Could not send rtnetlink message: %s", strerror(-r));
218                 return r;
219         }
220
221         log_debug_netdev(netdev, "creating netdev");
222
223         netdev->state = NETDEV_STATE_CREATING;
224
225         return 0;
226 }
227
228 int netdev_enslave(Netdev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
229         if (netdev->state == NETDEV_STATE_READY) {
230                 netdev_enslave_ready(netdev, link, callback);
231         } else {
232                 /* the netdev is not yet read, save this request for when it is*/
233                 netdev_enslave_callback *cb;
234
235                 cb = new0(netdev_enslave_callback, 1);
236                 if (!cb)
237                         return log_oom();
238
239                 cb->callback = callback;
240                 cb->link = link;
241
242                 LIST_PREPEND(callbacks, netdev->callbacks, cb);
243         }
244
245         return 0;
246 }
247
248 int netdev_set_link(Manager *m, NetdevKind kind, Link *link) {
249         Netdev *netdev;
250         int r;
251
252         r = netdev_get(m, link->ifname, &netdev);
253         if (r < 0)
254                 return r;
255
256         if (netdev->link && netdev->link != link)
257                 return -EEXIST;
258
259         if (netdev->kind != kind)
260                 return -EINVAL;
261
262         netdev->link = link;
263
264         netdev_enter_ready(netdev);
265
266         return 0;
267 }
268
269 static int netdev_load_one(Manager *manager, const char *filename) {
270         _cleanup_netdev_free_ Netdev *netdev = NULL;
271         _cleanup_fclose_ FILE *file = NULL;
272         int r;
273
274         assert(manager);
275         assert(filename);
276
277         file = fopen(filename, "re");
278         if (!file) {
279                 if (errno == ENOENT)
280                         return 0;
281                 else
282                         return errno;
283         }
284
285         netdev = new0(Netdev, 1);
286         if (!netdev)
287                 return log_oom();
288
289         netdev->manager = manager;
290         netdev->state = _NETDEV_STATE_INVALID;
291         netdev->kind = _NETDEV_KIND_INVALID;
292
293         r = config_parse(NULL, filename, file, "Netdev\0", config_item_perf_lookup,
294                         (void*) network_gperf_lookup, false, false, netdev);
295         if (r < 0) {
296                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
297                 return r;
298         }
299
300         if (netdev->kind == _NETDEV_KIND_INVALID) {
301                 log_warning("Netdev without Kind configured in %s. Ignoring", filename);
302                 return 0;
303         }
304
305         if (!netdev->name) {
306                 log_warning("Netdev without Name configured in %s. Ignoring", filename);
307                 return 0;
308         }
309
310         netdev->filename = strdup(filename);
311         if (!netdev->filename)
312                 return log_oom();
313
314         r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
315         if (r < 0)
316                 return r;
317
318         LIST_HEAD_INIT(netdev->callbacks);
319
320         r = netdev_create(netdev);
321         if (r < 0)
322                 return r;
323
324         netdev = NULL;
325
326         return 0;
327 }
328
329 int netdev_load(Manager *manager) {
330         Netdev *netdev;
331         char **files, **f;
332         int r;
333
334         assert(manager);
335
336         while ((netdev = hashmap_first(manager->netdevs)))
337                 netdev_free(netdev);
338
339         r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
340         if (r < 0) {
341                 log_error("Failed to enumerate netdev files: %s", strerror(-r));
342                 return r;
343         }
344
345         STRV_FOREACH_BACKWARDS(f, files) {
346                 r = netdev_load_one(manager, *f);
347                 if (r < 0)
348                         return r;
349         }
350
351         strv_free(files);
352
353         return 0;
354 }