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