chiark / gitweb /
networkd: netdev - support joining already existing netdevs
[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[_NETDEV_KIND_MAX] = {
32         [NETDEV_KIND_BRIDGE] = "bridge",
33         [NETDEV_KIND_BOND] = "bond",
34         [NETDEV_KIND_VLAN] = "vlan",
35         [NETDEV_KIND_MACVLAN] = "macvlan",
36 };
37
38 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
39 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
40
41 static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
42         [NETDEV_MACVLAN_MODE_PRIVATE] = "private",
43         [NETDEV_MACVLAN_MODE_VEPA] = "vepa",
44         [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge",
45         [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru",
46 };
47
48 DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
49 DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
50
51 void netdev_free(NetDev *netdev) {
52         netdev_enslave_callback *callback;
53
54         if (!netdev)
55                 return;
56
57         while ((callback = netdev->callbacks)) {
58                 LIST_REMOVE(callbacks, netdev->callbacks, callback);
59                 free(callback);
60         }
61
62         if (netdev->name)
63                 hashmap_remove(netdev->manager->netdevs, netdev->name);
64
65         free(netdev->filename);
66
67         free(netdev->description);
68         free(netdev->name);
69
70         free(netdev);
71 }
72
73 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
74         NetDev *netdev;
75
76         assert(manager);
77         assert(name);
78         assert(ret);
79
80         netdev = hashmap_get(manager->netdevs, name);
81         if (!netdev) {
82                 *ret = NULL;
83                 return -ENOENT;
84         }
85
86         *ret = netdev;
87
88         return 0;
89 }
90
91 static int netdev_enter_failed(NetDev *netdev) {
92         netdev->state = NETDEV_STATE_FAILED;
93
94         return 0;
95 }
96
97 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
98         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
99         int r;
100
101         assert(netdev);
102         assert(netdev->state == NETDEV_STATE_READY);
103         assert(netdev->manager);
104         assert(netdev->manager->rtnl);
105         assert(link);
106         assert(callback);
107
108         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
109                                      RTM_SETLINK, link->ifindex);
110         if (r < 0) {
111                 log_error_netdev(netdev,
112                                  "Could not allocate RTM_SETLINK message: %s",
113                                  strerror(-r));
114                 return r;
115         }
116
117         r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
118         if (r < 0) {
119                 log_error_netdev(netdev,
120                                  "Could not append IFLA_MASTER attribute: %s",
121                                  strerror(-r));
122                 return r;
123         }
124
125         r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
126         if (r < 0) {
127                 log_error_netdev(netdev,
128                                  "Could not send rtnetlink message: %s",
129                                  strerror(-r));
130                 return r;
131         }
132
133         log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
134
135         return 0;
136 }
137
138 static int netdev_enter_ready(NetDev *netdev) {
139         netdev_enslave_callback *callback;
140
141         assert(netdev);
142         assert(netdev->name);
143
144         netdev->state = NETDEV_STATE_READY;
145
146         log_info_netdev(netdev, "netdev ready");
147
148         LIST_FOREACH(callbacks, callback, netdev->callbacks) {
149                 /* enslave the links that were attempted to be enslaved befor the
150                  * link was ready */
151                 netdev_enslave_ready(netdev, callback->link, callback->callback);
152         }
153
154         return 0;
155 }
156
157 static int netdev_getlink_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
158                                 void *userdata) {
159         NetDev *netdev = userdata;
160         int r, ifindex;
161
162         assert(netdev);
163
164         if (netdev->state == NETDEV_STATE_FAILED)
165                 return 1;
166
167         r = sd_rtnl_message_get_errno(m);
168         if (r < 0) {
169                 log_struct_netdev(LOG_ERR, netdev,
170                                 "MESSAGE=%s: could not get link: %s",
171                                 netdev->name, strerror(-r),
172                                 "ERRNO=%d", -r,
173                                 NULL);
174                 return 1;
175         }
176
177         r = sd_rtnl_message_link_get_ifindex(m, &ifindex);
178         if (r < 0) {
179                 log_struct_netdev(LOG_ERR, netdev,
180                                 "MESSAGE=%s: could not get ifindex: %s",
181                                 netdev->name, strerror(-r),
182                                 "ERRNO=%d", -r,
183                                 NULL);
184                 return 1;
185         }
186
187         r = netdev_set_ifindex(netdev, ifindex);
188         if (r < 0)
189                 log_warning_netdev(netdev, "could not set ifindex to %d", ifindex);
190
191         return 1;
192 }
193
194 static int netdev_getlink(NetDev *netdev) {
195         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
196         int r;
197
198         assert(netdev->manager);
199         assert(netdev->manager->rtnl);
200         assert(netdev->name);
201
202         log_debug_netdev(netdev, "requesting netdev status");
203
204         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
205                                      RTM_GETLINK, 0);
206         if (r < 0) {
207                 log_error_netdev(netdev, "Could not allocate RTM_GETLINK message");
208                 return r;
209         }
210
211         r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
212         if (r < 0) {
213                 log_error_netdev(netdev, "Colud not append ifname to message: %s",
214                                  strerror(-r));
215                 return r;
216         }
217
218         r = sd_rtnl_call_async(netdev->manager->rtnl, req, netdev_getlink_handler,
219                                netdev, 0, NULL);
220         if (r < 0) {
221                 log_error_netdev(netdev,
222                                "Could not send rtnetlink message: %s", strerror(-r));
223                 return r;
224         }
225
226         return 0;
227 }
228
229 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
230         NetDev *netdev = userdata;
231         int r;
232
233         assert(netdev->state != _NETDEV_STATE_INVALID);
234
235         r = sd_rtnl_message_get_errno(m);
236         if (r == -EEXIST)
237                 r = netdev_getlink(netdev);
238
239         if (r < 0) {
240                 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
241                 netdev_enter_failed(netdev);
242
243                 return 1;
244         }
245
246         return 1;
247 }
248
249 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
250         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
251         const char *kind;
252         int r;
253
254         assert(netdev);
255         assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
256                (link && callback));
257         assert(netdev->name);
258         assert(netdev->manager);
259         assert(netdev->manager->rtnl);
260
261         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
262         if (r < 0) {
263                 log_error_netdev(netdev,
264                                  "Could not allocate RTM_NEWLINK message: %s",
265                                  strerror(-r));
266                 return r;
267         }
268
269         if (link) {
270                 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
271                 if (r < 0) {
272                         log_error_netdev(netdev,
273                                          "Could not append IFLA_LINK attribute: %s",
274                                          strerror(-r));
275                         return r;
276                 }
277         }
278
279         r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
280         if (r < 0) {
281                 log_error_netdev(netdev,
282                                  "Could not append IFLA_IFNAME attribute: %s",
283                                  strerror(-r));
284                 return r;
285         }
286
287         r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
288         if (r < 0) {
289                 log_error_netdev(netdev,
290                                  "Could not open IFLA_LINKINFO container: %s",
291                                  strerror(-r));
292                 return r;
293         }
294
295         kind = netdev_kind_to_string(netdev->kind);
296         if (!kind) {
297                 log_error_netdev(netdev, "Invalid kind");
298                 return -EINVAL;
299         }
300
301         r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, kind);
302         if (r < 0) {
303                 log_error_netdev(netdev,
304                                  "Could not append IFLA_INFO_KIND attribute: %s",
305                                  strerror(-r));
306                 return r;
307         }
308
309         if (netdev->vlanid <= VLANID_MAX || netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
310                 r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA);
311                 if (r < 0) {
312                         log_error_netdev(netdev,
313                                          "Could not open IFLA_INFO_DATA container: %s",
314                                          strerror(-r));
315                         return r;
316                 }
317
318                 if (netdev->vlanid <= VLANID_MAX) {
319                         r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
320                         if (r < 0) {
321                                 log_error_netdev(netdev,
322                                                  "Could not append IFLA_VLAN_ID attribute: %s",
323                                                  strerror(-r));
324                                 return r;
325                         }
326                 }
327
328                 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
329                         r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
330                         if (r < 0) {
331                                 log_error_netdev(netdev,
332                                                  "Could not append IFLA_MACVLAN_MODE attribute: %s",
333                                                  strerror(-r));
334                                 return r;
335                         }
336                 }
337
338                 r = sd_rtnl_message_close_container(req);
339                 if (r < 0) {
340                         log_error_netdev(netdev,
341                                          "Could not close IFLA_INFO_DATA container %s",
342                                          strerror(-r));
343                         return r;
344                 }
345         }
346
347         r = sd_rtnl_message_close_container(req);
348         if (r < 0) {
349                 log_error_netdev(netdev,
350                                  "Could not close IFLA_LINKINFO container %s",
351                                  strerror(-r));
352                 return r;
353         }
354
355         if (link)
356                 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
357         else
358                 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
359         if (r < 0) {
360                 log_error_netdev(netdev,
361                                  "Could not send rtnetlink message: %s", strerror(-r));
362                 return r;
363         }
364
365         log_debug_netdev(netdev, "creating netdev");
366
367         netdev->state = NETDEV_STATE_CREATING;
368
369         return 0;
370 }
371
372 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
373         if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
374                 return netdev_create(netdev, link, callback);
375
376         if (netdev->state == NETDEV_STATE_READY) {
377                 netdev_enslave_ready(netdev, link, callback);
378         } else {
379                 /* the netdev is not yet read, save this request for when it is*/
380                 netdev_enslave_callback *cb;
381
382                 cb = new0(netdev_enslave_callback, 1);
383                 if (!cb)
384                         return log_oom();
385
386                 cb->callback = callback;
387                 cb->link = link;
388
389                 LIST_PREPEND(callbacks, netdev->callbacks, cb);
390         }
391
392         return 0;
393 }
394
395 int netdev_set_ifindex(NetDev *netdev, int ifindex) {
396         assert(netdev);
397         assert(ifindex > 0);
398
399         if (netdev->ifindex > 0) {
400                 if (netdev->ifindex == ifindex)
401                         return 0;
402                 else
403                         return -EEXIST;
404         }
405
406         netdev->ifindex = ifindex;
407
408         netdev_enter_ready(netdev);
409
410         return 0;
411 }
412
413 static int netdev_load_one(Manager *manager, const char *filename) {
414         _cleanup_netdev_free_ NetDev *netdev = NULL;
415         _cleanup_fclose_ FILE *file = NULL;
416         int r;
417
418         assert(manager);
419         assert(filename);
420
421         file = fopen(filename, "re");
422         if (!file) {
423                 if (errno == ENOENT)
424                         return 0;
425                 else
426                         return errno;
427         }
428
429         netdev = new0(NetDev, 1);
430         if (!netdev)
431                 return log_oom();
432
433         netdev->manager = manager;
434         netdev->state = _NETDEV_STATE_INVALID;
435         netdev->kind = _NETDEV_KIND_INVALID;
436         netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
437         netdev->vlanid = VLANID_MAX + 1;
438
439         r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
440                          config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
441                          false, false, netdev);
442         if (r < 0) {
443                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
444                 return r;
445         }
446
447         if (netdev->kind == _NETDEV_KIND_INVALID) {
448                 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
449                 return 0;
450         }
451
452         if (!netdev->name) {
453                 log_warning("NetDev without Name configured in %s. Ignoring", filename);
454                 return 0;
455         }
456
457         if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
458                 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
459                 return 0;
460         }
461
462         if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
463                 log_warning("VLAN Id configured for a %s in %s. Ignoring",
464                             netdev_kind_to_string(netdev->kind), filename);
465                 return 0;
466         }
467
468         if (netdev->kind != NETDEV_KIND_MACVLAN &&
469             netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
470                 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
471                             netdev_kind_to_string(netdev->kind), filename);
472                 return 0;
473         }
474
475         netdev->filename = strdup(filename);
476         if (!netdev->filename)
477                 return log_oom();
478
479         if (net_match_config(NULL, NULL, NULL, NULL, NULL,
480                              netdev->match_host, netdev->match_virt,
481                              netdev->match_kernel, netdev->match_arch,
482                              NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
483                 return 0;
484
485         r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
486         if (r < 0)
487                 return r;
488
489         LIST_HEAD_INIT(netdev->callbacks);
490
491         if (netdev->kind != NETDEV_KIND_VLAN &&
492             netdev->kind != NETDEV_KIND_MACVLAN) {
493                 r = netdev_create(netdev, NULL, NULL);
494                 if (r < 0)
495                         return r;
496         }
497
498         netdev = NULL;
499
500         return 0;
501 }
502
503 int netdev_load(Manager *manager) {
504         NetDev *netdev;
505         char **files, **f;
506         int r;
507
508         assert(manager);
509
510         while ((netdev = hashmap_first(manager->netdevs)))
511                 netdev_free(netdev);
512
513         r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
514         if (r < 0) {
515                 log_error("Failed to enumerate netdev files: %s", strerror(-r));
516                 return r;
517         }
518
519         STRV_FOREACH_BACKWARDS(f, files) {
520                 r = netdev_load_one(manager, *f);
521                 if (r < 0)
522                         return r;
523         }
524
525         strv_free(files);
526
527         return 0;
528 }