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