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