chiark / gitweb /
networkd: netdev - verify that newlink messages has the expected kind
[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         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;
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         netdev_set_ifindex(netdev, m);
178
179         return 1;
180 }
181
182 static int netdev_getlink(NetDev *netdev) {
183         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
184         int r;
185
186         assert(netdev->manager);
187         assert(netdev->manager->rtnl);
188         assert(netdev->name);
189
190         log_debug_netdev(netdev, "requesting netdev status");
191
192         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
193                                      RTM_GETLINK, 0);
194         if (r < 0) {
195                 log_error_netdev(netdev, "Could not allocate RTM_GETLINK message");
196                 return r;
197         }
198
199         r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
200         if (r < 0) {
201                 log_error_netdev(netdev, "Colud not append ifname to message: %s",
202                                  strerror(-r));
203                 return r;
204         }
205
206         r = sd_rtnl_call_async(netdev->manager->rtnl, req, netdev_getlink_handler,
207                                netdev, 0, NULL);
208         if (r < 0) {
209                 log_error_netdev(netdev,
210                                "Could not send rtnetlink message: %s", strerror(-r));
211                 return r;
212         }
213
214         return 0;
215 }
216
217 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
218         NetDev *netdev = userdata;
219         int r;
220
221         assert(netdev->state != _NETDEV_STATE_INVALID);
222
223         r = sd_rtnl_message_get_errno(m);
224         if (r == -EEXIST)
225                 r = netdev_getlink(netdev);
226
227         if (r < 0) {
228                 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
229                 netdev_enter_failed(netdev);
230
231                 return 1;
232         }
233
234         return 1;
235 }
236
237 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
238         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
239         const char *kind;
240         int r;
241
242         assert(netdev);
243         assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
244                (link && callback));
245         assert(netdev->name);
246         assert(netdev->manager);
247         assert(netdev->manager->rtnl);
248
249         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
250         if (r < 0) {
251                 log_error_netdev(netdev,
252                                  "Could not allocate RTM_NEWLINK message: %s",
253                                  strerror(-r));
254                 return r;
255         }
256
257         if (link) {
258                 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
259                 if (r < 0) {
260                         log_error_netdev(netdev,
261                                          "Could not append IFLA_LINK attribute: %s",
262                                          strerror(-r));
263                         return r;
264                 }
265         }
266
267         r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
268         if (r < 0) {
269                 log_error_netdev(netdev,
270                                  "Could not append IFLA_IFNAME attribute: %s",
271                                  strerror(-r));
272                 return r;
273         }
274
275         r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
276         if (r < 0) {
277                 log_error_netdev(netdev,
278                                  "Could not open IFLA_LINKINFO container: %s",
279                                  strerror(-r));
280                 return r;
281         }
282
283         kind = netdev_kind_to_string(netdev->kind);
284         if (!kind) {
285                 log_error_netdev(netdev, "Invalid kind");
286                 return -EINVAL;
287         }
288
289         r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, kind);
290         if (r < 0) {
291                 log_error_netdev(netdev,
292                                  "Could not append IFLA_INFO_KIND attribute: %s",
293                                  strerror(-r));
294                 return r;
295         }
296
297         if (netdev->vlanid <= VLANID_MAX || netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
298                 r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA);
299                 if (r < 0) {
300                         log_error_netdev(netdev,
301                                          "Could not open IFLA_INFO_DATA container: %s",
302                                          strerror(-r));
303                         return r;
304                 }
305
306                 if (netdev->vlanid <= VLANID_MAX) {
307                         r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
308                         if (r < 0) {
309                                 log_error_netdev(netdev,
310                                                  "Could not append IFLA_VLAN_ID attribute: %s",
311                                                  strerror(-r));
312                                 return r;
313                         }
314                 }
315
316                 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
317                         r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
318                         if (r < 0) {
319                                 log_error_netdev(netdev,
320                                                  "Could not append IFLA_MACVLAN_MODE attribute: %s",
321                                                  strerror(-r));
322                                 return r;
323                         }
324                 }
325
326                 r = sd_rtnl_message_close_container(req);
327                 if (r < 0) {
328                         log_error_netdev(netdev,
329                                          "Could not close IFLA_INFO_DATA container %s",
330                                          strerror(-r));
331                         return r;
332                 }
333         }
334
335         r = sd_rtnl_message_close_container(req);
336         if (r < 0) {
337                 log_error_netdev(netdev,
338                                  "Could not close IFLA_LINKINFO container %s",
339                                  strerror(-r));
340                 return r;
341         }
342
343         if (link)
344                 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
345         else
346                 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
347         if (r < 0) {
348                 log_error_netdev(netdev,
349                                  "Could not send rtnetlink message: %s", strerror(-r));
350                 return r;
351         }
352
353         log_debug_netdev(netdev, "creating netdev");
354
355         netdev->state = NETDEV_STATE_CREATING;
356
357         return 0;
358 }
359
360 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
361         if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
362                 return netdev_create(netdev, link, callback);
363
364         if (netdev->state == NETDEV_STATE_READY) {
365                 netdev_enslave_ready(netdev, link, callback);
366         } else {
367                 /* the netdev is not yet read, save this request for when it is*/
368                 netdev_enslave_callback *cb;
369
370                 cb = new0(netdev_enslave_callback, 1);
371                 if (!cb)
372                         return log_oom();
373
374                 cb->callback = callback;
375                 cb->link = link;
376
377                 LIST_PREPEND(callbacks, netdev->callbacks, cb);
378         }
379
380         return 0;
381 }
382
383 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
384         const char *kind;
385         char *received_kind;
386         int r, ifindex;
387
388         assert(netdev);
389         assert(ifindex > 0);
390
391         kind = netdev_kind_to_string(netdev->kind);
392         if (!kind)
393                 log_error_netdev(netdev, "Could not get kind");
394
395         r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
396         if (r < 0) {
397                 log_error_netdev(netdev, "Could not get LINKINFO");
398                 return r;
399         }
400
401         r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
402         if (r < 0) {
403                 log_error_netdev(netdev, "Could not get KIND");
404                 return r;
405         }
406
407         if (!streq(kind, received_kind)) {
408                 log_error_netdev(netdev, "Received newlink with wrong KIND");
409                 netdev_enter_failed(netdev);
410                 return r;
411         }
412
413         r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
414         if (r < 0) {
415                 log_struct_netdev(LOG_ERR, netdev,
416                                 "MESSAGE=%s: could not get ifindex: %s",
417                                 netdev->name, strerror(-r),
418                                 "ERRNO=%d", -r,
419                                 NULL);
420                 return r;
421         }
422
423         if (netdev->ifindex > 0) {
424                 if (netdev->ifindex == ifindex)
425                         return 0;
426                 else
427                         return -EEXIST;
428         }
429
430         netdev->ifindex = ifindex;
431
432         netdev_enter_ready(netdev);
433
434         return 0;
435 }
436
437 static int netdev_load_one(Manager *manager, const char *filename) {
438         _cleanup_netdev_free_ NetDev *netdev = NULL;
439         _cleanup_fclose_ FILE *file = NULL;
440         int r;
441
442         assert(manager);
443         assert(filename);
444
445         file = fopen(filename, "re");
446         if (!file) {
447                 if (errno == ENOENT)
448                         return 0;
449                 else
450                         return errno;
451         }
452
453         netdev = new0(NetDev, 1);
454         if (!netdev)
455                 return log_oom();
456
457         netdev->manager = manager;
458         netdev->state = _NETDEV_STATE_INVALID;
459         netdev->kind = _NETDEV_KIND_INVALID;
460         netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
461         netdev->vlanid = VLANID_MAX + 1;
462
463         r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
464                          config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
465                          false, false, netdev);
466         if (r < 0) {
467                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
468                 return r;
469         }
470
471         if (netdev->kind == _NETDEV_KIND_INVALID) {
472                 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
473                 return 0;
474         }
475
476         if (!netdev->name) {
477                 log_warning("NetDev without Name configured in %s. Ignoring", filename);
478                 return 0;
479         }
480
481         if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
482                 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
483                 return 0;
484         }
485
486         if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
487                 log_warning("VLAN Id configured for a %s in %s. Ignoring",
488                             netdev_kind_to_string(netdev->kind), filename);
489                 return 0;
490         }
491
492         if (netdev->kind != NETDEV_KIND_MACVLAN &&
493             netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
494                 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
495                             netdev_kind_to_string(netdev->kind), filename);
496                 return 0;
497         }
498
499         netdev->filename = strdup(filename);
500         if (!netdev->filename)
501                 return log_oom();
502
503         if (net_match_config(NULL, NULL, NULL, NULL, NULL,
504                              netdev->match_host, netdev->match_virt,
505                              netdev->match_kernel, netdev->match_arch,
506                              NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
507                 return 0;
508
509         r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
510         if (r < 0)
511                 return r;
512
513         LIST_HEAD_INIT(netdev->callbacks);
514
515         if (netdev->kind != NETDEV_KIND_VLAN &&
516             netdev->kind != NETDEV_KIND_MACVLAN) {
517                 r = netdev_create(netdev, NULL, NULL);
518                 if (r < 0)
519                         return r;
520         }
521
522         netdev = NULL;
523
524         return 0;
525 }
526
527 int netdev_load(Manager *manager) {
528         NetDev *netdev;
529         char **files, **f;
530         int r;
531
532         assert(manager);
533
534         while ((netdev = hashmap_first(manager->netdevs)))
535                 netdev_free(netdev);
536
537         r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
538         if (r < 0) {
539                 log_error("Failed to enumerate netdev files: %s", strerror(-r));
540                 return r;
541         }
542
543         STRV_FOREACH_BACKWARDS(f, files) {
544                 r = netdev_load_one(manager, *f);
545                 if (r < 0)
546                         return r;
547         }
548
549         strv_free(files);
550
551         return 0;
552 }